[PATCH 2/2] iio: exynos-adc: add experimental touchscreen support
Dmitry Torokhov
dmitry.torokhov at gmail.com
Sun Jul 20 13:28:42 PDT 2014
On Sun, Jul 20, 2014 at 02:51:37PM +0100, Jonathan Cameron wrote:
> On 20/07/14 14:49, Jonathan Cameron wrote:
> >On 18/07/14 20:29, Arnd Bergmann wrote:
> >>This adds support for the touchscreen on Samsung s3c64xx.
> >>The driver is completely untested but shows roughly how
> >>it could be done, following the example of the at91 driver.
> >>
> >Hi Arnd,
> Cc'd linux-input and Dmitry.
> >
> >>Open questions include:
> >>
> >>- compared to the old plat-samsung/adc driver, there is
> >> no support for prioritizing ts over other clients, nor
> >> for oversampling. From my reading of the code, the
> >> priorities didn't actually have any effect at all, but
> >> the oversampling might be needed. Maybe the original
> >> authors have some insight.
> >>
> >>- I simply register the input device from the adc driver
> >> itself, as the at91 code does. The driver also supports
> >> sub-nodes, but I don't understand how they are meant
> >> to be used, so using those might be better.
> >So, the alternative you are (I think) referring to is to use
> >the buffered in kernel client interface. That way a separate
> >touch screen driver can use the output channels provided by IIO
> >in much the same way you might use a regulator or similar.
> >Note that whilst this is similar to the simple polled interface
> >used for things like the iio_hwmon driver, the data flow is
> >quite different (clearly the polled interfce would be
> >inappropriate here).
> >
> >Whilst we've discussed it in the past for touch screen drivers
> >like this, usually the hardware isn't generic enough to be
> >of any real use if not being used as a touch screen. As such
> >it's often simpler to just have the support directly in the
> >driver (as you've observed the at91 driver does this).
> >
> >Whilst the interface has been there a while, it's not really had
> >all that much use. The original target was the simpler case
> >of 3D accelerometer where we have a generic iio to input
> >bridge driver. Time constraints meant that I haven't yet actually
> >formally submitted the input side of this. Whilst there are lots
> >of other things that can use this interface, right now nothing
> >actually does so.
> >
> >>
> >>- The new exynos_read_s3c64xx_ts() function is intentionally
> >> very similar to the existing exynos_read_raw() functions.
> >> It should probably be changed, either by merging the two
> >> into one, or by simplifying the exynos_read_s3c64xx_ts()
> >> function. This depends a bit on the answers to the questions
> >> above.
> >I'd be tempted to not bother keeping them that similar. It's
> >not a generic IIO channel so simplify it where possible.
> >>
> >>- We probably need to add support for platform_data as well,
> >> I've skipped this so far.
> >>
> >>- Is anybody able to debug this driver on real hardware?
> >> While it's possible that it actually works, it's more
> >> likely that I made a few subtle mistakes.
> >>
> >>Signed-off-by: Arnd Bergmann <arnd at arndb.de>
> >Looks pretty good to me. A few symantic bits and pieces and
> >one bug spotted. Short and sweet.
> >>
> >>diff --git a/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt b/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt
> >>index e1b74828f413..4329bf3c3326 100644
> >>--- a/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt
> >>+++ b/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt
> >>@@ -41,6 +41,10 @@ Required properties:
> >> and compatible ADC block)
> >> - vdd-supply VDD input supply.
> >>
> >>+Optional properties:
> >>+- has-touchscreen: If present, indicates that a touchscreen is
> >>+ connected an usable.
> >>+
> >> Note: child nodes can be added for auto probing from device tree.
> >>
> >> Example: adding device info in dtsi file
> >>diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
> >>index 5f95638513d2..cf1d9f3e2492 100644
> >>--- a/drivers/iio/adc/exynos_adc.c
> >>+++ b/drivers/iio/adc/exynos_adc.c
> >>@@ -34,6 +34,7 @@
> >> #include <linux/regulator/consumer.h>
> >> #include <linux/of_platform.h>
> >> #include <linux/err.h>
> >>+#include <linux/input.h>
> >Might want to make the input side optional at compile time...
> >I supose the existing parts are unlikely to be used much in headless
> >devices, but you never know. Maybe we just leave this until someone
> >shouts they want to be able to avoid compiling it in.
> >>
> >> #include <linux/iio/iio.h>
> >> #include <linux/iio/machine.h>
> >>@@ -103,6 +104,7 @@
> >>
> >> /* Bit definitions common for ADC_V1 and ADC_V2 */
> >> #define ADC_CON_EN_START (1u << 0)
> >>+#define ADC_DATX_PRESSED (1u << 15)
> >> #define ADC_DATX_MASK 0xFFF
> >>
> >> #define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(100))
> >>@@ -110,16 +112,20 @@
> >> struct exynos_adc {
> >> struct exynos_adc_data *data;
> >> struct device *dev;
> >>+ struct input_dev *input;
> >> void __iomem *regs;
> >> void __iomem *enable_reg;
> >> struct clk *clk;
> >> struct clk *sclk;
> >> unsigned int irq;
> >>+ unsigned int tsirq;
> >> struct regulator *vdd;
> >>
> >> struct completion completion;
> >>
> >>+ bool read_ts;
> >> u32 value;
> >>+ u32 value2;
> >As I state further down, I'd rather keep a little
> >clear of the naming used in IIO for bits that aren't
> >going through IIO (less confusing!). Maybe just
> >have
> > u32 x, y;
> >> unsigned int version;
> >> };
> >>
> >>@@ -390,12 +396,61 @@ static int exynos_read_raw(struct iio_dev *indio_dev,
> >> return ret;
> >> }
> >>
> >>+static int exynos_read_s3c64xx_ts(struct iio_dev *indio_dev,
> >>+ struct iio_chan_spec const *chan,
> >>+ int *val,
> >>+ int *val2,
> >>+ long mask)
> >>+{
> >>+ struct exynos_adc *info = iio_priv(indio_dev);
> >>+ unsigned long timeout;
> >>+ int ret;
> >>+
> >>+ if (mask != IIO_CHAN_INFO_RAW)
> >>+ return -EINVAL;
> >>+
> >>+ mutex_lock(&indio_dev->mlock);
> >>+ info->read_ts = 1;
> >>+
> >>+ reinit_completion(&info->completion);
> >>+
> >>+ writel(ADC_S3C2410_TSC_PULL_UP_DISABLE | ADC_TSC_AUTOPST,
> >>+ ADC_V1_TSC(info->regs));
> >>+
> >>+ /* Select the ts channel to be used and Trigger conversion */
> >>+ info->data->start_conv(info, 0);
> >0 is a rather magic value. A define perhaps?
> >>+
> >>+ timeout = wait_for_completion_timeout
> >>+ (&info->completion, EXYNOS_ADC_TIMEOUT);
> >>+ if (timeout == 0) {
> >>+ dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n");
> >>+ if (info->data->init_hw)
> >>+ info->data->init_hw(info);
> >>+ ret = -ETIMEDOUT;
> >>+ } else {
> >>+ *val = info->value;
> >>+ *val2 = info->value2;
> >This is definitely abuse as those two values are not intended for
> >different values. If you want to do this please use different naming
> >and don't try to fiddle it into the IIO read raw framework.
> >As you've suggested above, better to simplify this code and drop the
> >bits cloned from the other handler.
> >>+ ret = IIO_VAL_INT;
> >>+ }
> >>+
> >>+ info->read_ts = 0;
> >>+ mutex_unlock(&indio_dev->mlock);
> >>+
> >>+ return ret;
> >>+}
> >>+
> >> static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
> >> {
> >> struct exynos_adc *info = (struct exynos_adc *)dev_id;
> >>
> >> /* Read value */
> >>- info->value = readl(ADC_V1_DATX(info->regs)) & ADC_DATX_MASK;
> >>+ if (info->read_ts) {
> >>+ info->value = readl(ADC_V1_DATX(info->regs)) & ADC_DATX_MASK;
> >>+ info->value2 = readl(ADC_V1_DATY(info->regs)) & ADC_DATX_MASK;
> >ADC_DATY_MASK would be more obviously correct.
> >
> >>+ writel(ADC_TSC_WAIT4INT | ADC_S3C2443_TSC_UD_SEN, ADC_V1_TSC(info->regs));
> >Perhaps the above is cryptic enough to warrant a comment?
> >>+ } else {
> >>+ info->value = readl(ADC_V1_DATX(info->regs)) & ADC_DATX_MASK;
> >>+ }
> >>
> >> /* clear irq */
> >> if (info->data->clear_irq)
> >>@@ -406,6 +461,46 @@ static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
> >> return IRQ_HANDLED;
> >> }
> >>
> >>+/*
> >>+ * Here we (ab)use a threaded interrupt handler to stay running
> >>+ * for as long as the touchscreen remains pressed, we report
> >>+ * a new event with the latest data and then sleep until the
> >>+ * next timer tick. This mirrors the behavior of the old
> >>+ * driver, with much less code.
> >>+ */
> >>+static irqreturn_t exynos_ts_isr(int irq, void *dev_id)
> >>+{
> >>+ struct exynos_adc *info = dev_id;
> >>+ struct iio_dev *dev = dev_get_drvdata(info->dev);
> >>+ u32 x, y;
> >>+ bool pressed;
> >>+ int ret;
> >>+
> >>+ do {
> >>+ ret =exynos_read_s3c64xx_ts(dev, NULL, &x, &y, IIO_CHAN_INFO_RAW);
> >= exynos
> >>+ if (ret == -ETIMEDOUT)
> >>+ break;
> >>+
> >>+ pressed = x & y & ADC_DATX_PRESSED;
> >>+ if (!pressed)
> >>+ break;
> >>+
> >>+ input_report_abs(info->input, ABS_X, x & ADC_DATX_MASK);
> >>+ input_report_abs(info->input, ABS_Y, y & ADC_DATX_MASK);
> >>+ input_report_key(info->input, BTN_TOUCH, 1);
> >>+ input_sync(info->input);
> >>+
> >>+ msleep(1);
> >>+ } while (1);
It would be nice to actually close the device even if someone is
touching screen. Please implement open/close methods and have them set a
flag that you would check here.
> >>+
> >>+ input_report_key(info->input, BTN_TOUCH, 0);
> >>+ input_sync(info->input);
> >>+
> >>+ writel(0, ADC_V1_CLRINTPNDNUP(info->regs));
> >>+
> >>+ return IRQ_HANDLED;
> >>+}
> >>+
> >> static int exynos_adc_reg_access(struct iio_dev *indio_dev,
> >> unsigned reg, unsigned writeval,
> >> unsigned *readval)
> >>@@ -457,12 +552,57 @@ static int exynos_adc_remove_devices(struct device *dev, void *c)
> >> return 0;
> >> }
> >>
> >>+static int exynos_adc_ts_init(struct exynos_adc *info)
> >>+{
> >>+ int ret;
> >>+
> >>+ info->input = input_allocate_device();
> >>+ if (!info->input)
> >>+ return -ENOMEM;
> >>+
> >>+ info->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> >>+ info->input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
> >>+
> >>+ input_set_abs_params(info->input, ABS_X, 0, 0x3FF, 0, 0);
> >>+ input_set_abs_params(info->input, ABS_Y, 0, 0x3FF, 0, 0);
> >>+
> >>+ /* data from s3c2410_ts driver */
> >>+ info->input->name = "S3C24xx TouchScreen";
> >>+ info->input->id.bustype = BUS_HOST;
> >>+ info->input->id.vendor = 0xDEAD;
> >>+ info->input->id.product = 0xBEEF;
You do not need to fill these entries with fake data.
> >>+ info->input->id.version = 0x0200;
> >>+
> >>+ ret = input_register_device(info->input);
> >>+ if (ret) {
> >>+ input_free_device(info->input);
> >>+ goto err;
> >>+ }
> >>+
> >>+ if (info->tsirq > 0)
> >>+ ret = request_threaded_irq(info->irq, NULL, exynos_ts_isr,
> >>+ 0, "touchscreen", info);
> >info->tsirq
> >(that had me really confused for a moment ;)
> >Also, perhaps a more specific name. touchscreen_updown or similar as the
> >main interrupt is also used during touchscreen operation.
> >>+ if (ret < 0) {
> >>+ dev_err(info->dev, "failed requesting touchsccreen irq, irq = %d\n",
> >>+ info->irq);
> >>+ goto err_input;
> >>+ }
> >>+
> >>+ return 0;
> >>+
> >>+err_input:
> >>+ input_unregister_device(info->input);
> >>+err:
> >>+ return ret;
> >>+}
> >>+
> >> static int exynos_adc_probe(struct platform_device *pdev)
> >> {
> >> struct exynos_adc *info = NULL;
> >> struct device_node *np = pdev->dev.of_node;
> >> struct iio_dev *indio_dev = NULL;
> >> struct resource *mem;
> >>+ bool has_ts;
> >> int ret = -ENODEV;
> >> int irq;
> >>
> >>@@ -498,8 +638,14 @@ static int exynos_adc_probe(struct platform_device *pdev)
> >> dev_err(&pdev->dev, "no irq resource?\n");
> >> return irq;
> >> }
> >>-
> >> info->irq = irq;
> >>+
> >>+ irq = platform_get_irq(pdev, 1);
> >>+ if (irq == -EPROBE_DEFER)
> >>+ return irq;
> >>+
> >>+ info->tsirq = irq;
> >>+
> >> info->dev = &pdev->dev;
> >>
> >> init_completion(&info->completion);
> >>@@ -565,6 +711,12 @@ static int exynos_adc_probe(struct platform_device *pdev)
> >> if (info->data->init_hw)
> >> info->data->init_hw(info);
> >>
> >>+ has_ts = of_property_read_bool(pdev->dev.of_node, "has-touchscreen");
> >>+ if (has_ts)
> >>+ ret = exynos_adc_ts_init(info);
> >>+ if (ret)
> >>+ goto err_iio;
> >>+
> >> ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev);
> >> if (ret < 0) {
> >> dev_err(&pdev->dev, "failed adding child nodes\n");
> >>@@ -576,6 +728,11 @@ static int exynos_adc_probe(struct platform_device *pdev)
> >> err_of_populate:
> >> device_for_each_child(&indio_dev->dev, NULL,
> >> exynos_adc_remove_devices);
> >>+ if (has_ts) {
> >>+ input_unregister_device(info->input);
> >>+ free_irq(info->tsirq, info);
Are we sure that device is quiesced here and interrupt won't arrive
between input_unregister_device() and free_irq()? I guess if you
properly implement open/close it won't matter.
> >>+ }
> >>+err_iio:
> >> iio_device_unregister(indio_dev);
> >> err_irq:
> >> free_irq(info->irq, info);
> >>@@ -595,9 +752,12 @@ static int exynos_adc_remove(struct platform_device *pdev)
> >> struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> >> struct exynos_adc *info = iio_priv(indio_dev);
> >>
> >>+ input_free_device(info->input);
Should be unregister, not free.
> >> device_for_each_child(&indio_dev->dev, NULL,
> >> exynos_adc_remove_devices);
> >> iio_device_unregister(indio_dev);
> >>+ if (info->tsirq > 0)
> >>+ free_irq(info->tsirq, info);
> >> free_irq(info->irq, info);
> >> if (info->data->exit_hw)
> >> info->data->exit_hw(info);
> >>
Thanks.
--
Dmitry
More information about the linux-arm-kernel
mailing list