Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Toradex Colibri VF50 Touchscreen driver
0004  *
0005  * Copyright 2015 Toradex AG
0006  *
0007  * Originally authored by Stefan Agner for 3.0 kernel
0008  */
0009 
0010 #include <linux/delay.h>
0011 #include <linux/err.h>
0012 #include <linux/gpio/consumer.h>
0013 #include <linux/iio/consumer.h>
0014 #include <linux/iio/types.h>
0015 #include <linux/input.h>
0016 #include <linux/interrupt.h>
0017 #include <linux/kernel.h>
0018 #include <linux/module.h>
0019 #include <linux/of.h>
0020 #include <linux/pinctrl/consumer.h>
0021 #include <linux/platform_device.h>
0022 #include <linux/slab.h>
0023 #include <linux/types.h>
0024 
0025 #define DRIVER_NAME         "colibri-vf50-ts"
0026 
0027 #define VF_ADC_MAX          ((1 << 12) - 1)
0028 
0029 #define COLI_TOUCH_MIN_DELAY_US     1000
0030 #define COLI_TOUCH_MAX_DELAY_US     2000
0031 #define COLI_PULLUP_MIN_DELAY_US    10000
0032 #define COLI_PULLUP_MAX_DELAY_US    11000
0033 #define COLI_TOUCH_NO_OF_AVGS       5
0034 #define COLI_TOUCH_REQ_ADC_CHAN     4
0035 
0036 struct vf50_touch_device {
0037     struct platform_device *pdev;
0038     struct input_dev *ts_input;
0039     struct iio_channel *channels;
0040     struct gpio_desc *gpio_xp;
0041     struct gpio_desc *gpio_xm;
0042     struct gpio_desc *gpio_yp;
0043     struct gpio_desc *gpio_ym;
0044     int pen_irq;
0045     int min_pressure;
0046     bool stop_touchscreen;
0047 };
0048 
0049 /*
0050  * Enables given plates and measures touch parameters using ADC
0051  */
0052 static int adc_ts_measure(struct iio_channel *channel,
0053               struct gpio_desc *plate_p, struct gpio_desc *plate_m)
0054 {
0055     int i, value = 0, val = 0;
0056     int error;
0057 
0058     gpiod_set_value(plate_p, 1);
0059     gpiod_set_value(plate_m, 1);
0060 
0061     usleep_range(COLI_TOUCH_MIN_DELAY_US, COLI_TOUCH_MAX_DELAY_US);
0062 
0063     for (i = 0; i < COLI_TOUCH_NO_OF_AVGS; i++) {
0064         error = iio_read_channel_raw(channel, &val);
0065         if (error < 0) {
0066             value = error;
0067             goto error_iio_read;
0068         }
0069 
0070         value += val;
0071     }
0072 
0073     value /= COLI_TOUCH_NO_OF_AVGS;
0074 
0075 error_iio_read:
0076     gpiod_set_value(plate_p, 0);
0077     gpiod_set_value(plate_m, 0);
0078 
0079     return value;
0080 }
0081 
0082 /*
0083  * Enable touch detection using falling edge detection on XM
0084  */
0085 static void vf50_ts_enable_touch_detection(struct vf50_touch_device *vf50_ts)
0086 {
0087     /* Enable plate YM (needs to be strong GND, high active) */
0088     gpiod_set_value(vf50_ts->gpio_ym, 1);
0089 
0090     /*
0091      * Let the platform mux to idle state in order to enable
0092      * Pull-Up on GPIO
0093      */
0094     pinctrl_pm_select_idle_state(&vf50_ts->pdev->dev);
0095 
0096     /* Wait for the pull-up to be stable on high */
0097     usleep_range(COLI_PULLUP_MIN_DELAY_US, COLI_PULLUP_MAX_DELAY_US);
0098 }
0099 
0100 /*
0101  * ADC touch screen sampling bottom half irq handler
0102  */
0103 static irqreturn_t vf50_ts_irq_bh(int irq, void *private)
0104 {
0105     struct vf50_touch_device *vf50_ts = private;
0106     struct device *dev = &vf50_ts->pdev->dev;
0107     int val_x, val_y, val_z1, val_z2, val_p = 0;
0108     bool discard_val_on_start = true;
0109 
0110     /* Disable the touch detection plates */
0111     gpiod_set_value(vf50_ts->gpio_ym, 0);
0112 
0113     /* Let the platform mux to default state in order to mux as ADC */
0114     pinctrl_pm_select_default_state(dev);
0115 
0116     while (!vf50_ts->stop_touchscreen) {
0117         /* X-Direction */
0118         val_x = adc_ts_measure(&vf50_ts->channels[0],
0119                 vf50_ts->gpio_xp, vf50_ts->gpio_xm);
0120         if (val_x < 0)
0121             break;
0122 
0123         /* Y-Direction */
0124         val_y = adc_ts_measure(&vf50_ts->channels[1],
0125                 vf50_ts->gpio_yp, vf50_ts->gpio_ym);
0126         if (val_y < 0)
0127             break;
0128 
0129         /*
0130          * Touch pressure
0131          * Measure on XP/YM
0132          */
0133         val_z1 = adc_ts_measure(&vf50_ts->channels[2],
0134                 vf50_ts->gpio_yp, vf50_ts->gpio_xm);
0135         if (val_z1 < 0)
0136             break;
0137         val_z2 = adc_ts_measure(&vf50_ts->channels[3],
0138                 vf50_ts->gpio_yp, vf50_ts->gpio_xm);
0139         if (val_z2 < 0)
0140             break;
0141 
0142         /* Validate signal (avoid calculation using noise) */
0143         if (val_z1 > 64 && val_x > 64) {
0144             /*
0145              * Calculate resistance between the plates
0146              * lower resistance means higher pressure
0147              */
0148             int r_x = (1000 * val_x) / VF_ADC_MAX;
0149 
0150             val_p = (r_x * val_z2) / val_z1 - r_x;
0151 
0152         } else {
0153             val_p = 2000;
0154         }
0155 
0156         val_p = 2000 - val_p;
0157         dev_dbg(dev,
0158             "Measured values: x: %d, y: %d, z1: %d, z2: %d, p: %d\n",
0159             val_x, val_y, val_z1, val_z2, val_p);
0160 
0161         /*
0162          * If touch pressure is too low, stop measuring and reenable
0163          * touch detection
0164          */
0165         if (val_p < vf50_ts->min_pressure || val_p > 2000)
0166             break;
0167 
0168         /*
0169          * The pressure may not be enough for the first x and the
0170          * second y measurement, but, the pressure is ok when the
0171          * driver is doing the third and fourth measurement. To
0172          * take care of this, we drop the first measurement always.
0173          */
0174         if (discard_val_on_start) {
0175             discard_val_on_start = false;
0176         } else {
0177             /*
0178              * Report touch position and sleep for
0179              * the next measurement.
0180              */
0181             input_report_abs(vf50_ts->ts_input,
0182                     ABS_X, VF_ADC_MAX - val_x);
0183             input_report_abs(vf50_ts->ts_input,
0184                     ABS_Y, VF_ADC_MAX - val_y);
0185             input_report_abs(vf50_ts->ts_input,
0186                     ABS_PRESSURE, val_p);
0187             input_report_key(vf50_ts->ts_input, BTN_TOUCH, 1);
0188             input_sync(vf50_ts->ts_input);
0189         }
0190 
0191         usleep_range(COLI_PULLUP_MIN_DELAY_US,
0192                  COLI_PULLUP_MAX_DELAY_US);
0193     }
0194 
0195     /* Report no more touch, re-enable touch detection */
0196     input_report_abs(vf50_ts->ts_input, ABS_PRESSURE, 0);
0197     input_report_key(vf50_ts->ts_input, BTN_TOUCH, 0);
0198     input_sync(vf50_ts->ts_input);
0199 
0200     vf50_ts_enable_touch_detection(vf50_ts);
0201 
0202     return IRQ_HANDLED;
0203 }
0204 
0205 static int vf50_ts_open(struct input_dev *dev_input)
0206 {
0207     struct vf50_touch_device *touchdev = input_get_drvdata(dev_input);
0208     struct device *dev = &touchdev->pdev->dev;
0209 
0210     dev_dbg(dev, "Input device %s opened, starting touch detection\n",
0211         dev_input->name);
0212 
0213     touchdev->stop_touchscreen = false;
0214 
0215     /* Mux detection before request IRQ, wait for pull-up to settle */
0216     vf50_ts_enable_touch_detection(touchdev);
0217 
0218     return 0;
0219 }
0220 
0221 static void vf50_ts_close(struct input_dev *dev_input)
0222 {
0223     struct vf50_touch_device *touchdev = input_get_drvdata(dev_input);
0224     struct device *dev = &touchdev->pdev->dev;
0225 
0226     touchdev->stop_touchscreen = true;
0227 
0228     /* Make sure IRQ is not running past close */
0229     mb();
0230     synchronize_irq(touchdev->pen_irq);
0231 
0232     gpiod_set_value(touchdev->gpio_ym, 0);
0233     pinctrl_pm_select_default_state(dev);
0234 
0235     dev_dbg(dev, "Input device %s closed, disable touch detection\n",
0236         dev_input->name);
0237 }
0238 
0239 static int vf50_ts_get_gpiod(struct device *dev, struct gpio_desc **gpio_d,
0240                  const char *con_id, enum gpiod_flags flags)
0241 {
0242     int error;
0243 
0244     *gpio_d = devm_gpiod_get(dev, con_id, flags);
0245     if (IS_ERR(*gpio_d)) {
0246         error = PTR_ERR(*gpio_d);
0247         dev_err(dev, "Could not get gpio_%s %d\n", con_id, error);
0248         return error;
0249     }
0250 
0251     return 0;
0252 }
0253 
0254 static void vf50_ts_channel_release(void *data)
0255 {
0256     struct iio_channel *channels = data;
0257 
0258     iio_channel_release_all(channels);
0259 }
0260 
0261 static int vf50_ts_probe(struct platform_device *pdev)
0262 {
0263     struct input_dev *input;
0264     struct iio_channel *channels;
0265     struct device *dev = &pdev->dev;
0266     struct vf50_touch_device *touchdev;
0267     int num_adc_channels;
0268     int error;
0269 
0270     channels = iio_channel_get_all(dev);
0271     if (IS_ERR(channels))
0272         return PTR_ERR(channels);
0273 
0274     error = devm_add_action(dev, vf50_ts_channel_release, channels);
0275     if (error) {
0276         iio_channel_release_all(channels);
0277         dev_err(dev, "Failed to register iio channel release action");
0278         return error;
0279     }
0280 
0281     num_adc_channels = 0;
0282     while (channels[num_adc_channels].indio_dev)
0283         num_adc_channels++;
0284 
0285     if (num_adc_channels != COLI_TOUCH_REQ_ADC_CHAN) {
0286         dev_err(dev, "Inadequate ADC channels specified\n");
0287         return -EINVAL;
0288     }
0289 
0290     touchdev = devm_kzalloc(dev, sizeof(*touchdev), GFP_KERNEL);
0291     if (!touchdev)
0292         return -ENOMEM;
0293 
0294     touchdev->pdev = pdev;
0295     touchdev->channels = channels;
0296 
0297     error = of_property_read_u32(dev->of_node, "vf50-ts-min-pressure",
0298                  &touchdev->min_pressure);
0299     if (error)
0300         return error;
0301 
0302     input = devm_input_allocate_device(dev);
0303     if (!input) {
0304         dev_err(dev, "Failed to allocate TS input device\n");
0305         return -ENOMEM;
0306     }
0307 
0308     input->name = DRIVER_NAME;
0309     input->id.bustype = BUS_HOST;
0310     input->dev.parent = dev;
0311     input->open = vf50_ts_open;
0312     input->close = vf50_ts_close;
0313 
0314     input_set_capability(input, EV_KEY, BTN_TOUCH);
0315     input_set_abs_params(input, ABS_X, 0, VF_ADC_MAX, 0, 0);
0316     input_set_abs_params(input, ABS_Y, 0, VF_ADC_MAX, 0, 0);
0317     input_set_abs_params(input, ABS_PRESSURE, 0, VF_ADC_MAX, 0, 0);
0318 
0319     touchdev->ts_input = input;
0320     input_set_drvdata(input, touchdev);
0321 
0322     error = input_register_device(input);
0323     if (error) {
0324         dev_err(dev, "Failed to register input device\n");
0325         return error;
0326     }
0327 
0328     error = vf50_ts_get_gpiod(dev, &touchdev->gpio_xp, "xp", GPIOD_OUT_LOW);
0329     if (error)
0330         return error;
0331 
0332     error = vf50_ts_get_gpiod(dev, &touchdev->gpio_xm,
0333                 "xm", GPIOD_OUT_LOW);
0334     if (error)
0335         return error;
0336 
0337     error = vf50_ts_get_gpiod(dev, &touchdev->gpio_yp, "yp", GPIOD_OUT_LOW);
0338     if (error)
0339         return error;
0340 
0341     error = vf50_ts_get_gpiod(dev, &touchdev->gpio_ym, "ym", GPIOD_OUT_LOW);
0342     if (error)
0343         return error;
0344 
0345     touchdev->pen_irq = platform_get_irq(pdev, 0);
0346     if (touchdev->pen_irq < 0)
0347         return touchdev->pen_irq;
0348 
0349     error = devm_request_threaded_irq(dev, touchdev->pen_irq,
0350                       NULL, vf50_ts_irq_bh, IRQF_ONESHOT,
0351                       "vf50 touch", touchdev);
0352     if (error) {
0353         dev_err(dev, "Failed to request IRQ %d: %d\n",
0354             touchdev->pen_irq, error);
0355         return error;
0356     }
0357 
0358     return 0;
0359 }
0360 
0361 static const struct of_device_id vf50_touch_of_match[] = {
0362     { .compatible = "toradex,vf50-touchscreen", },
0363     { }
0364 };
0365 MODULE_DEVICE_TABLE(of, vf50_touch_of_match);
0366 
0367 static struct platform_driver vf50_touch_driver = {
0368     .driver = {
0369         .name = "toradex,vf50_touchctrl",
0370         .of_match_table = vf50_touch_of_match,
0371     },
0372     .probe = vf50_ts_probe,
0373 };
0374 module_platform_driver(vf50_touch_driver);
0375 
0376 MODULE_AUTHOR("Sanchayan Maity");
0377 MODULE_DESCRIPTION("Colibri VF50 Touchscreen driver");
0378 MODULE_LICENSE("GPL");