linux/drivers/input/touchscreen/colibri-vf50-ts.c
<<
>>
Prefs
   1/*
   2 * Toradex Colibri VF50 Touchscreen driver
   3 *
   4 * Copyright 2015 Toradex AG
   5 *
   6 * Originally authored by Stefan Agner for 3.0 kernel
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 */
  13
  14#include <linux/delay.h>
  15#include <linux/err.h>
  16#include <linux/gpio.h>
  17#include <linux/gpio/consumer.h>
  18#include <linux/iio/consumer.h>
  19#include <linux/iio/types.h>
  20#include <linux/input.h>
  21#include <linux/interrupt.h>
  22#include <linux/kernel.h>
  23#include <linux/module.h>
  24#include <linux/of.h>
  25#include <linux/pinctrl/consumer.h>
  26#include <linux/platform_device.h>
  27#include <linux/slab.h>
  28#include <linux/types.h>
  29
  30#define DRIVER_NAME                     "colibri-vf50-ts"
  31#define DRV_VERSION                     "1.0"
  32
  33#define VF_ADC_MAX                      ((1 << 12) - 1)
  34
  35#define COLI_TOUCH_MIN_DELAY_US         1000
  36#define COLI_TOUCH_MAX_DELAY_US         2000
  37#define COLI_PULLUP_MIN_DELAY_US        10000
  38#define COLI_PULLUP_MAX_DELAY_US        11000
  39#define COLI_TOUCH_NO_OF_AVGS           5
  40#define COLI_TOUCH_REQ_ADC_CHAN         4
  41
  42struct vf50_touch_device {
  43        struct platform_device *pdev;
  44        struct input_dev *ts_input;
  45        struct iio_channel *channels;
  46        struct gpio_desc *gpio_xp;
  47        struct gpio_desc *gpio_xm;
  48        struct gpio_desc *gpio_yp;
  49        struct gpio_desc *gpio_ym;
  50        int pen_irq;
  51        int min_pressure;
  52        bool stop_touchscreen;
  53};
  54
  55/*
  56 * Enables given plates and measures touch parameters using ADC
  57 */
  58static int adc_ts_measure(struct iio_channel *channel,
  59                          struct gpio_desc *plate_p, struct gpio_desc *plate_m)
  60{
  61        int i, value = 0, val = 0;
  62        int error;
  63
  64        gpiod_set_value(plate_p, 1);
  65        gpiod_set_value(plate_m, 1);
  66
  67        usleep_range(COLI_TOUCH_MIN_DELAY_US, COLI_TOUCH_MAX_DELAY_US);
  68
  69        for (i = 0; i < COLI_TOUCH_NO_OF_AVGS; i++) {
  70                error = iio_read_channel_raw(channel, &val);
  71                if (error < 0) {
  72                        value = error;
  73                        goto error_iio_read;
  74                }
  75
  76                value += val;
  77        }
  78
  79        value /= COLI_TOUCH_NO_OF_AVGS;
  80
  81error_iio_read:
  82        gpiod_set_value(plate_p, 0);
  83        gpiod_set_value(plate_m, 0);
  84
  85        return value;
  86}
  87
  88/*
  89 * Enable touch detection using falling edge detection on XM
  90 */
  91static void vf50_ts_enable_touch_detection(struct vf50_touch_device *vf50_ts)
  92{
  93        /* Enable plate YM (needs to be strong GND, high active) */
  94        gpiod_set_value(vf50_ts->gpio_ym, 1);
  95
  96        /*
  97         * Let the platform mux to idle state in order to enable
  98         * Pull-Up on GPIO
  99         */
 100        pinctrl_pm_select_idle_state(&vf50_ts->pdev->dev);
 101
 102        /* Wait for the pull-up to be stable on high */
 103        usleep_range(COLI_PULLUP_MIN_DELAY_US, COLI_PULLUP_MAX_DELAY_US);
 104}
 105
 106/*
 107 * ADC touch screen sampling bottom half irq handler
 108 */
 109static irqreturn_t vf50_ts_irq_bh(int irq, void *private)
 110{
 111        struct vf50_touch_device *vf50_ts = private;
 112        struct device *dev = &vf50_ts->pdev->dev;
 113        int val_x, val_y, val_z1, val_z2, val_p = 0;
 114        bool discard_val_on_start = true;
 115
 116        /* Disable the touch detection plates */
 117        gpiod_set_value(vf50_ts->gpio_ym, 0);
 118
 119        /* Let the platform mux to default state in order to mux as ADC */
 120        pinctrl_pm_select_default_state(dev);
 121
 122        while (!vf50_ts->stop_touchscreen) {
 123                /* X-Direction */
 124                val_x = adc_ts_measure(&vf50_ts->channels[0],
 125                                vf50_ts->gpio_xp, vf50_ts->gpio_xm);
 126                if (val_x < 0)
 127                        break;
 128
 129                /* Y-Direction */
 130                val_y = adc_ts_measure(&vf50_ts->channels[1],
 131                                vf50_ts->gpio_yp, vf50_ts->gpio_ym);
 132                if (val_y < 0)
 133                        break;
 134
 135                /*
 136                 * Touch pressure
 137                 * Measure on XP/YM
 138                 */
 139                val_z1 = adc_ts_measure(&vf50_ts->channels[2],
 140                                vf50_ts->gpio_yp, vf50_ts->gpio_xm);
 141                if (val_z1 < 0)
 142                        break;
 143                val_z2 = adc_ts_measure(&vf50_ts->channels[3],
 144                                vf50_ts->gpio_yp, vf50_ts->gpio_xm);
 145                if (val_z2 < 0)
 146                        break;
 147
 148                /* Validate signal (avoid calculation using noise) */
 149                if (val_z1 > 64 && val_x > 64) {
 150                        /*
 151                         * Calculate resistance between the plates
 152                         * lower resistance means higher pressure
 153                         */
 154                        int r_x = (1000 * val_x) / VF_ADC_MAX;
 155
 156                        val_p = (r_x * val_z2) / val_z1 - r_x;
 157
 158                } else {
 159                        val_p = 2000;
 160                }
 161
 162                val_p = 2000 - val_p;
 163                dev_dbg(dev,
 164                        "Measured values: x: %d, y: %d, z1: %d, z2: %d, p: %d\n",
 165                        val_x, val_y, val_z1, val_z2, val_p);
 166
 167                /*
 168                 * If touch pressure is too low, stop measuring and reenable
 169                 * touch detection
 170                 */
 171                if (val_p < vf50_ts->min_pressure || val_p > 2000)
 172                        break;
 173
 174                /*
 175                 * The pressure may not be enough for the first x and the
 176                 * second y measurement, but, the pressure is ok when the
 177                 * driver is doing the third and fourth measurement. To
 178                 * take care of this, we drop the first measurement always.
 179                 */
 180                if (discard_val_on_start) {
 181                        discard_val_on_start = false;
 182                } else {
 183                        /*
 184                         * Report touch position and sleep for
 185                         * the next measurement.
 186                         */
 187                        input_report_abs(vf50_ts->ts_input,
 188                                        ABS_X, VF_ADC_MAX - val_x);
 189                        input_report_abs(vf50_ts->ts_input,
 190                                        ABS_Y, VF_ADC_MAX - val_y);
 191                        input_report_abs(vf50_ts->ts_input,
 192                                        ABS_PRESSURE, val_p);
 193                        input_report_key(vf50_ts->ts_input, BTN_TOUCH, 1);
 194                        input_sync(vf50_ts->ts_input);
 195                }
 196
 197                usleep_range(COLI_PULLUP_MIN_DELAY_US,
 198                             COLI_PULLUP_MAX_DELAY_US);
 199        }
 200
 201        /* Report no more touch, re-enable touch detection */
 202        input_report_abs(vf50_ts->ts_input, ABS_PRESSURE, 0);
 203        input_report_key(vf50_ts->ts_input, BTN_TOUCH, 0);
 204        input_sync(vf50_ts->ts_input);
 205
 206        vf50_ts_enable_touch_detection(vf50_ts);
 207
 208        return IRQ_HANDLED;
 209}
 210
 211static int vf50_ts_open(struct input_dev *dev_input)
 212{
 213        struct vf50_touch_device *touchdev = input_get_drvdata(dev_input);
 214        struct device *dev = &touchdev->pdev->dev;
 215
 216        dev_dbg(dev, "Input device %s opened, starting touch detection\n",
 217                dev_input->name);
 218
 219        touchdev->stop_touchscreen = false;
 220
 221        /* Mux detection before request IRQ, wait for pull-up to settle */
 222        vf50_ts_enable_touch_detection(touchdev);
 223
 224        return 0;
 225}
 226
 227static void vf50_ts_close(struct input_dev *dev_input)
 228{
 229        struct vf50_touch_device *touchdev = input_get_drvdata(dev_input);
 230        struct device *dev = &touchdev->pdev->dev;
 231
 232        touchdev->stop_touchscreen = true;
 233
 234        /* Make sure IRQ is not running past close */
 235        mb();
 236        synchronize_irq(touchdev->pen_irq);
 237
 238        gpiod_set_value(touchdev->gpio_ym, 0);
 239        pinctrl_pm_select_default_state(dev);
 240
 241        dev_dbg(dev, "Input device %s closed, disable touch detection\n",
 242                dev_input->name);
 243}
 244
 245static int vf50_ts_get_gpiod(struct device *dev, struct gpio_desc **gpio_d,
 246                             const char *con_id, enum gpiod_flags flags)
 247{
 248        int error;
 249
 250        *gpio_d = devm_gpiod_get(dev, con_id, flags);
 251        if (IS_ERR(*gpio_d)) {
 252                error = PTR_ERR(*gpio_d);
 253                dev_err(dev, "Could not get gpio_%s %d\n", con_id, error);
 254                return error;
 255        }
 256
 257        return 0;
 258}
 259
 260static void vf50_ts_channel_release(void *data)
 261{
 262        struct iio_channel *channels = data;
 263
 264        iio_channel_release_all(channels);
 265}
 266
 267static int vf50_ts_probe(struct platform_device *pdev)
 268{
 269        struct input_dev *input;
 270        struct iio_channel *channels;
 271        struct device *dev = &pdev->dev;
 272        struct vf50_touch_device *touchdev;
 273        int num_adc_channels;
 274        int error;
 275
 276        channels = iio_channel_get_all(dev);
 277        if (IS_ERR(channels))
 278                return PTR_ERR(channels);
 279
 280        error = devm_add_action(dev, vf50_ts_channel_release, channels);
 281        if (error) {
 282                iio_channel_release_all(channels);
 283                dev_err(dev, "Failed to register iio channel release action");
 284                return error;
 285        }
 286
 287        num_adc_channels = 0;
 288        while (channels[num_adc_channels].indio_dev)
 289                num_adc_channels++;
 290
 291        if (num_adc_channels != COLI_TOUCH_REQ_ADC_CHAN) {
 292                dev_err(dev, "Inadequate ADC channels specified\n");
 293                return -EINVAL;
 294        }
 295
 296        touchdev = devm_kzalloc(dev, sizeof(*touchdev), GFP_KERNEL);
 297        if (!touchdev)
 298                return -ENOMEM;
 299
 300        touchdev->pdev = pdev;
 301        touchdev->channels = channels;
 302
 303        error = of_property_read_u32(dev->of_node, "vf50-ts-min-pressure",
 304                                 &touchdev->min_pressure);
 305        if (error)
 306                return error;
 307
 308        input = devm_input_allocate_device(dev);
 309        if (!input) {
 310                dev_err(dev, "Failed to allocate TS input device\n");
 311                return -ENOMEM;
 312        }
 313
 314        input->name = DRIVER_NAME;
 315        input->id.bustype = BUS_HOST;
 316        input->dev.parent = dev;
 317        input->open = vf50_ts_open;
 318        input->close = vf50_ts_close;
 319
 320        input_set_capability(input, EV_KEY, BTN_TOUCH);
 321        input_set_abs_params(input, ABS_X, 0, VF_ADC_MAX, 0, 0);
 322        input_set_abs_params(input, ABS_Y, 0, VF_ADC_MAX, 0, 0);
 323        input_set_abs_params(input, ABS_PRESSURE, 0, VF_ADC_MAX, 0, 0);
 324
 325        touchdev->ts_input = input;
 326        input_set_drvdata(input, touchdev);
 327
 328        error = input_register_device(input);
 329        if (error) {
 330                dev_err(dev, "Failed to register input device\n");
 331                return error;
 332        }
 333
 334        error = vf50_ts_get_gpiod(dev, &touchdev->gpio_xp, "xp", GPIOD_OUT_LOW);
 335        if (error)
 336                return error;
 337
 338        error = vf50_ts_get_gpiod(dev, &touchdev->gpio_xm,
 339                                "xm", GPIOD_OUT_LOW);
 340        if (error)
 341                return error;
 342
 343        error = vf50_ts_get_gpiod(dev, &touchdev->gpio_yp, "yp", GPIOD_OUT_LOW);
 344        if (error)
 345                return error;
 346
 347        error = vf50_ts_get_gpiod(dev, &touchdev->gpio_ym, "ym", GPIOD_OUT_LOW);
 348        if (error)
 349                return error;
 350
 351        touchdev->pen_irq = platform_get_irq(pdev, 0);
 352        if (touchdev->pen_irq < 0)
 353                return touchdev->pen_irq;
 354
 355        error = devm_request_threaded_irq(dev, touchdev->pen_irq,
 356                                          NULL, vf50_ts_irq_bh, IRQF_ONESHOT,
 357                                          "vf50 touch", touchdev);
 358        if (error) {
 359                dev_err(dev, "Failed to request IRQ %d: %d\n",
 360                        touchdev->pen_irq, error);
 361                return error;
 362        }
 363
 364        return 0;
 365}
 366
 367static const struct of_device_id vf50_touch_of_match[] = {
 368        { .compatible = "toradex,vf50-touchscreen", },
 369        { }
 370};
 371MODULE_DEVICE_TABLE(of, vf50_touch_of_match);
 372
 373static struct platform_driver vf50_touch_driver = {
 374        .driver = {
 375                .name = "toradex,vf50_touchctrl",
 376                .of_match_table = vf50_touch_of_match,
 377        },
 378        .probe = vf50_ts_probe,
 379};
 380module_platform_driver(vf50_touch_driver);
 381
 382MODULE_AUTHOR("Sanchayan Maity");
 383MODULE_DESCRIPTION("Colibri VF50 Touchscreen driver");
 384MODULE_LICENSE("GPL");
 385MODULE_VERSION(DRV_VERSION);
 386