xref: /openbmc/linux/drivers/input/touchscreen/colibri-vf50-ts.c (revision c95baf12f5077419db01313ab61c2aac007d40cd)
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  
36  struct 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   */
adc_ts_measure(struct iio_channel * channel,struct gpio_desc * plate_p,struct gpio_desc * plate_m)52  static 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  
75  error_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   */
vf50_ts_enable_touch_detection(struct vf50_touch_device * vf50_ts)85  static 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   */
vf50_ts_irq_bh(int irq,void * private)103  static 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  
vf50_ts_open(struct input_dev * dev_input)205  static 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  
vf50_ts_close(struct input_dev * dev_input)221  static 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  
vf50_ts_get_gpiod(struct device * dev,struct gpio_desc ** gpio_d,const char * con_id,enum gpiod_flags flags)239  static 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  
vf50_ts_channel_release(void * data)254  static void vf50_ts_channel_release(void *data)
255  {
256  	struct iio_channel *channels = data;
257  
258  	iio_channel_release_all(channels);
259  }
260  
vf50_ts_probe(struct platform_device * pdev)261  static 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  
361  static const struct of_device_id vf50_touch_of_match[] = {
362  	{ .compatible = "toradex,vf50-touchscreen", },
363  	{ }
364  };
365  MODULE_DEVICE_TABLE(of, vf50_touch_of_match);
366  
367  static 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  };
374  module_platform_driver(vf50_touch_driver);
375  
376  MODULE_AUTHOR("Sanchayan Maity");
377  MODULE_DESCRIPTION("Colibri VF50 Touchscreen driver");
378  MODULE_LICENSE("GPL");
379