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