18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Toradex Colibri VF50 Touchscreen driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2015 Toradex AG 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Originally authored by Stefan Agner for 3.0 kernel 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 138c2ecf20Sopenharmony_ci#include <linux/iio/consumer.h> 148c2ecf20Sopenharmony_ci#include <linux/iio/types.h> 158c2ecf20Sopenharmony_ci#include <linux/input.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/of.h> 208c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/types.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define DRIVER_NAME "colibri-vf50-ts" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define VF_ADC_MAX ((1 << 12) - 1) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define COLI_TOUCH_MIN_DELAY_US 1000 308c2ecf20Sopenharmony_ci#define COLI_TOUCH_MAX_DELAY_US 2000 318c2ecf20Sopenharmony_ci#define COLI_PULLUP_MIN_DELAY_US 10000 328c2ecf20Sopenharmony_ci#define COLI_PULLUP_MAX_DELAY_US 11000 338c2ecf20Sopenharmony_ci#define COLI_TOUCH_NO_OF_AVGS 5 348c2ecf20Sopenharmony_ci#define COLI_TOUCH_REQ_ADC_CHAN 4 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct vf50_touch_device { 378c2ecf20Sopenharmony_ci struct platform_device *pdev; 388c2ecf20Sopenharmony_ci struct input_dev *ts_input; 398c2ecf20Sopenharmony_ci struct iio_channel *channels; 408c2ecf20Sopenharmony_ci struct gpio_desc *gpio_xp; 418c2ecf20Sopenharmony_ci struct gpio_desc *gpio_xm; 428c2ecf20Sopenharmony_ci struct gpio_desc *gpio_yp; 438c2ecf20Sopenharmony_ci struct gpio_desc *gpio_ym; 448c2ecf20Sopenharmony_ci int pen_irq; 458c2ecf20Sopenharmony_ci int min_pressure; 468c2ecf20Sopenharmony_ci bool stop_touchscreen; 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* 508c2ecf20Sopenharmony_ci * Enables given plates and measures touch parameters using ADC 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_cistatic int adc_ts_measure(struct iio_channel *channel, 538c2ecf20Sopenharmony_ci struct gpio_desc *plate_p, struct gpio_desc *plate_m) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci int i, value = 0, val = 0; 568c2ecf20Sopenharmony_ci int error; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci gpiod_set_value(plate_p, 1); 598c2ecf20Sopenharmony_ci gpiod_set_value(plate_m, 1); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci usleep_range(COLI_TOUCH_MIN_DELAY_US, COLI_TOUCH_MAX_DELAY_US); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci for (i = 0; i < COLI_TOUCH_NO_OF_AVGS; i++) { 648c2ecf20Sopenharmony_ci error = iio_read_channel_raw(channel, &val); 658c2ecf20Sopenharmony_ci if (error < 0) { 668c2ecf20Sopenharmony_ci value = error; 678c2ecf20Sopenharmony_ci goto error_iio_read; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci value += val; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci value /= COLI_TOUCH_NO_OF_AVGS; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cierror_iio_read: 768c2ecf20Sopenharmony_ci gpiod_set_value(plate_p, 0); 778c2ecf20Sopenharmony_ci gpiod_set_value(plate_m, 0); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return value; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* 838c2ecf20Sopenharmony_ci * Enable touch detection using falling edge detection on XM 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_cistatic void vf50_ts_enable_touch_detection(struct vf50_touch_device *vf50_ts) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci /* Enable plate YM (needs to be strong GND, high active) */ 888c2ecf20Sopenharmony_ci gpiod_set_value(vf50_ts->gpio_ym, 1); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* 918c2ecf20Sopenharmony_ci * Let the platform mux to idle state in order to enable 928c2ecf20Sopenharmony_ci * Pull-Up on GPIO 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci pinctrl_pm_select_idle_state(&vf50_ts->pdev->dev); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* Wait for the pull-up to be stable on high */ 978c2ecf20Sopenharmony_ci usleep_range(COLI_PULLUP_MIN_DELAY_US, COLI_PULLUP_MAX_DELAY_US); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* 1018c2ecf20Sopenharmony_ci * ADC touch screen sampling bottom half irq handler 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_cistatic irqreturn_t vf50_ts_irq_bh(int irq, void *private) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct vf50_touch_device *vf50_ts = private; 1068c2ecf20Sopenharmony_ci struct device *dev = &vf50_ts->pdev->dev; 1078c2ecf20Sopenharmony_ci int val_x, val_y, val_z1, val_z2, val_p = 0; 1088c2ecf20Sopenharmony_ci bool discard_val_on_start = true; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* Disable the touch detection plates */ 1118c2ecf20Sopenharmony_ci gpiod_set_value(vf50_ts->gpio_ym, 0); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* Let the platform mux to default state in order to mux as ADC */ 1148c2ecf20Sopenharmony_ci pinctrl_pm_select_default_state(dev); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci while (!vf50_ts->stop_touchscreen) { 1178c2ecf20Sopenharmony_ci /* X-Direction */ 1188c2ecf20Sopenharmony_ci val_x = adc_ts_measure(&vf50_ts->channels[0], 1198c2ecf20Sopenharmony_ci vf50_ts->gpio_xp, vf50_ts->gpio_xm); 1208c2ecf20Sopenharmony_ci if (val_x < 0) 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Y-Direction */ 1248c2ecf20Sopenharmony_ci val_y = adc_ts_measure(&vf50_ts->channels[1], 1258c2ecf20Sopenharmony_ci vf50_ts->gpio_yp, vf50_ts->gpio_ym); 1268c2ecf20Sopenharmony_ci if (val_y < 0) 1278c2ecf20Sopenharmony_ci break; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* 1308c2ecf20Sopenharmony_ci * Touch pressure 1318c2ecf20Sopenharmony_ci * Measure on XP/YM 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci val_z1 = adc_ts_measure(&vf50_ts->channels[2], 1348c2ecf20Sopenharmony_ci vf50_ts->gpio_yp, vf50_ts->gpio_xm); 1358c2ecf20Sopenharmony_ci if (val_z1 < 0) 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci val_z2 = adc_ts_measure(&vf50_ts->channels[3], 1388c2ecf20Sopenharmony_ci vf50_ts->gpio_yp, vf50_ts->gpio_xm); 1398c2ecf20Sopenharmony_ci if (val_z2 < 0) 1408c2ecf20Sopenharmony_ci break; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Validate signal (avoid calculation using noise) */ 1438c2ecf20Sopenharmony_ci if (val_z1 > 64 && val_x > 64) { 1448c2ecf20Sopenharmony_ci /* 1458c2ecf20Sopenharmony_ci * Calculate resistance between the plates 1468c2ecf20Sopenharmony_ci * lower resistance means higher pressure 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci int r_x = (1000 * val_x) / VF_ADC_MAX; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci val_p = (r_x * val_z2) / val_z1 - r_x; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci } else { 1538c2ecf20Sopenharmony_ci val_p = 2000; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci val_p = 2000 - val_p; 1578c2ecf20Sopenharmony_ci dev_dbg(dev, 1588c2ecf20Sopenharmony_ci "Measured values: x: %d, y: %d, z1: %d, z2: %d, p: %d\n", 1598c2ecf20Sopenharmony_ci val_x, val_y, val_z1, val_z2, val_p); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* 1628c2ecf20Sopenharmony_ci * If touch pressure is too low, stop measuring and reenable 1638c2ecf20Sopenharmony_ci * touch detection 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci if (val_p < vf50_ts->min_pressure || val_p > 2000) 1668c2ecf20Sopenharmony_ci break; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* 1698c2ecf20Sopenharmony_ci * The pressure may not be enough for the first x and the 1708c2ecf20Sopenharmony_ci * second y measurement, but, the pressure is ok when the 1718c2ecf20Sopenharmony_ci * driver is doing the third and fourth measurement. To 1728c2ecf20Sopenharmony_ci * take care of this, we drop the first measurement always. 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci if (discard_val_on_start) { 1758c2ecf20Sopenharmony_ci discard_val_on_start = false; 1768c2ecf20Sopenharmony_ci } else { 1778c2ecf20Sopenharmony_ci /* 1788c2ecf20Sopenharmony_ci * Report touch position and sleep for 1798c2ecf20Sopenharmony_ci * the next measurement. 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci input_report_abs(vf50_ts->ts_input, 1828c2ecf20Sopenharmony_ci ABS_X, VF_ADC_MAX - val_x); 1838c2ecf20Sopenharmony_ci input_report_abs(vf50_ts->ts_input, 1848c2ecf20Sopenharmony_ci ABS_Y, VF_ADC_MAX - val_y); 1858c2ecf20Sopenharmony_ci input_report_abs(vf50_ts->ts_input, 1868c2ecf20Sopenharmony_ci ABS_PRESSURE, val_p); 1878c2ecf20Sopenharmony_ci input_report_key(vf50_ts->ts_input, BTN_TOUCH, 1); 1888c2ecf20Sopenharmony_ci input_sync(vf50_ts->ts_input); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci usleep_range(COLI_PULLUP_MIN_DELAY_US, 1928c2ecf20Sopenharmony_ci COLI_PULLUP_MAX_DELAY_US); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* Report no more touch, re-enable touch detection */ 1968c2ecf20Sopenharmony_ci input_report_abs(vf50_ts->ts_input, ABS_PRESSURE, 0); 1978c2ecf20Sopenharmony_ci input_report_key(vf50_ts->ts_input, BTN_TOUCH, 0); 1988c2ecf20Sopenharmony_ci input_sync(vf50_ts->ts_input); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci vf50_ts_enable_touch_detection(vf50_ts); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int vf50_ts_open(struct input_dev *dev_input) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct vf50_touch_device *touchdev = input_get_drvdata(dev_input); 2088c2ecf20Sopenharmony_ci struct device *dev = &touchdev->pdev->dev; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci dev_dbg(dev, "Input device %s opened, starting touch detection\n", 2118c2ecf20Sopenharmony_ci dev_input->name); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci touchdev->stop_touchscreen = false; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* Mux detection before request IRQ, wait for pull-up to settle */ 2168c2ecf20Sopenharmony_ci vf50_ts_enable_touch_detection(touchdev); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic void vf50_ts_close(struct input_dev *dev_input) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct vf50_touch_device *touchdev = input_get_drvdata(dev_input); 2248c2ecf20Sopenharmony_ci struct device *dev = &touchdev->pdev->dev; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci touchdev->stop_touchscreen = true; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* Make sure IRQ is not running past close */ 2298c2ecf20Sopenharmony_ci mb(); 2308c2ecf20Sopenharmony_ci synchronize_irq(touchdev->pen_irq); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci gpiod_set_value(touchdev->gpio_ym, 0); 2338c2ecf20Sopenharmony_ci pinctrl_pm_select_default_state(dev); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci dev_dbg(dev, "Input device %s closed, disable touch detection\n", 2368c2ecf20Sopenharmony_ci dev_input->name); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic int vf50_ts_get_gpiod(struct device *dev, struct gpio_desc **gpio_d, 2408c2ecf20Sopenharmony_ci const char *con_id, enum gpiod_flags flags) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci int error; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci *gpio_d = devm_gpiod_get(dev, con_id, flags); 2458c2ecf20Sopenharmony_ci if (IS_ERR(*gpio_d)) { 2468c2ecf20Sopenharmony_ci error = PTR_ERR(*gpio_d); 2478c2ecf20Sopenharmony_ci dev_err(dev, "Could not get gpio_%s %d\n", con_id, error); 2488c2ecf20Sopenharmony_ci return error; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic void vf50_ts_channel_release(void *data) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct iio_channel *channels = data; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci iio_channel_release_all(channels); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int vf50_ts_probe(struct platform_device *pdev) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct input_dev *input; 2648c2ecf20Sopenharmony_ci struct iio_channel *channels; 2658c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2668c2ecf20Sopenharmony_ci struct vf50_touch_device *touchdev; 2678c2ecf20Sopenharmony_ci int num_adc_channels; 2688c2ecf20Sopenharmony_ci int error; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci channels = iio_channel_get_all(dev); 2718c2ecf20Sopenharmony_ci if (IS_ERR(channels)) 2728c2ecf20Sopenharmony_ci return PTR_ERR(channels); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci error = devm_add_action(dev, vf50_ts_channel_release, channels); 2758c2ecf20Sopenharmony_ci if (error) { 2768c2ecf20Sopenharmony_ci iio_channel_release_all(channels); 2778c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register iio channel release action"); 2788c2ecf20Sopenharmony_ci return error; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci num_adc_channels = 0; 2828c2ecf20Sopenharmony_ci while (channels[num_adc_channels].indio_dev) 2838c2ecf20Sopenharmony_ci num_adc_channels++; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (num_adc_channels != COLI_TOUCH_REQ_ADC_CHAN) { 2868c2ecf20Sopenharmony_ci dev_err(dev, "Inadequate ADC channels specified\n"); 2878c2ecf20Sopenharmony_ci return -EINVAL; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci touchdev = devm_kzalloc(dev, sizeof(*touchdev), GFP_KERNEL); 2918c2ecf20Sopenharmony_ci if (!touchdev) 2928c2ecf20Sopenharmony_ci return -ENOMEM; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci touchdev->pdev = pdev; 2958c2ecf20Sopenharmony_ci touchdev->channels = channels; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci error = of_property_read_u32(dev->of_node, "vf50-ts-min-pressure", 2988c2ecf20Sopenharmony_ci &touchdev->min_pressure); 2998c2ecf20Sopenharmony_ci if (error) 3008c2ecf20Sopenharmony_ci return error; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci input = devm_input_allocate_device(dev); 3038c2ecf20Sopenharmony_ci if (!input) { 3048c2ecf20Sopenharmony_ci dev_err(dev, "Failed to allocate TS input device\n"); 3058c2ecf20Sopenharmony_ci return -ENOMEM; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci input->name = DRIVER_NAME; 3098c2ecf20Sopenharmony_ci input->id.bustype = BUS_HOST; 3108c2ecf20Sopenharmony_ci input->dev.parent = dev; 3118c2ecf20Sopenharmony_ci input->open = vf50_ts_open; 3128c2ecf20Sopenharmony_ci input->close = vf50_ts_close; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci input_set_capability(input, EV_KEY, BTN_TOUCH); 3158c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_X, 0, VF_ADC_MAX, 0, 0); 3168c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_Y, 0, VF_ADC_MAX, 0, 0); 3178c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_PRESSURE, 0, VF_ADC_MAX, 0, 0); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci touchdev->ts_input = input; 3208c2ecf20Sopenharmony_ci input_set_drvdata(input, touchdev); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci error = input_register_device(input); 3238c2ecf20Sopenharmony_ci if (error) { 3248c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register input device\n"); 3258c2ecf20Sopenharmony_ci return error; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci error = vf50_ts_get_gpiod(dev, &touchdev->gpio_xp, "xp", GPIOD_OUT_LOW); 3298c2ecf20Sopenharmony_ci if (error) 3308c2ecf20Sopenharmony_ci return error; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci error = vf50_ts_get_gpiod(dev, &touchdev->gpio_xm, 3338c2ecf20Sopenharmony_ci "xm", GPIOD_OUT_LOW); 3348c2ecf20Sopenharmony_ci if (error) 3358c2ecf20Sopenharmony_ci return error; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci error = vf50_ts_get_gpiod(dev, &touchdev->gpio_yp, "yp", GPIOD_OUT_LOW); 3388c2ecf20Sopenharmony_ci if (error) 3398c2ecf20Sopenharmony_ci return error; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci error = vf50_ts_get_gpiod(dev, &touchdev->gpio_ym, "ym", GPIOD_OUT_LOW); 3428c2ecf20Sopenharmony_ci if (error) 3438c2ecf20Sopenharmony_ci return error; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci touchdev->pen_irq = platform_get_irq(pdev, 0); 3468c2ecf20Sopenharmony_ci if (touchdev->pen_irq < 0) 3478c2ecf20Sopenharmony_ci return touchdev->pen_irq; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci error = devm_request_threaded_irq(dev, touchdev->pen_irq, 3508c2ecf20Sopenharmony_ci NULL, vf50_ts_irq_bh, IRQF_ONESHOT, 3518c2ecf20Sopenharmony_ci "vf50 touch", touchdev); 3528c2ecf20Sopenharmony_ci if (error) { 3538c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request IRQ %d: %d\n", 3548c2ecf20Sopenharmony_ci touchdev->pen_irq, error); 3558c2ecf20Sopenharmony_ci return error; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic const struct of_device_id vf50_touch_of_match[] = { 3628c2ecf20Sopenharmony_ci { .compatible = "toradex,vf50-touchscreen", }, 3638c2ecf20Sopenharmony_ci { } 3648c2ecf20Sopenharmony_ci}; 3658c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, vf50_touch_of_match); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic struct platform_driver vf50_touch_driver = { 3688c2ecf20Sopenharmony_ci .driver = { 3698c2ecf20Sopenharmony_ci .name = "toradex,vf50_touchctrl", 3708c2ecf20Sopenharmony_ci .of_match_table = vf50_touch_of_match, 3718c2ecf20Sopenharmony_ci }, 3728c2ecf20Sopenharmony_ci .probe = vf50_ts_probe, 3738c2ecf20Sopenharmony_ci}; 3748c2ecf20Sopenharmony_cimodule_platform_driver(vf50_touch_driver); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sanchayan Maity"); 3778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Colibri VF50 Touchscreen driver"); 3788c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 379