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