xref: /linux/drivers/input/touchscreen/resistive-adc-touch.c (revision 08729298c4eea5a9bbc0598d92ff47638a97a1ae)
1aa132ffbSEugen Hristev // SPDX-License-Identifier: GPL-2.0
2aa132ffbSEugen Hristev /*
3aa132ffbSEugen Hristev  * ADC generic resistive touchscreen (GRTS)
4aa132ffbSEugen Hristev  * This is a generic input driver that connects to an ADC
5aa132ffbSEugen Hristev  * given the channels in device tree, and reports events to the input
6aa132ffbSEugen Hristev  * subsystem.
7aa132ffbSEugen Hristev  *
8aa132ffbSEugen Hristev  * Copyright (C) 2017,2018 Microchip Technology,
9aa132ffbSEugen Hristev  * Author: Eugen Hristev <eugen.hristev@microchip.com>
10aa132ffbSEugen Hristev  *
11aa132ffbSEugen Hristev  */
12aa132ffbSEugen Hristev #include <linux/input.h>
13aa132ffbSEugen Hristev #include <linux/input/touchscreen.h>
14aa132ffbSEugen Hristev #include <linux/iio/consumer.h>
15aa132ffbSEugen Hristev #include <linux/iio/iio.h>
16*08729298SAndy Shevchenko #include <linux/mod_devicetable.h>
17aa132ffbSEugen Hristev #include <linux/module.h>
18aa132ffbSEugen Hristev #include <linux/platform_device.h>
19*08729298SAndy Shevchenko #include <linux/property.h>
20aa132ffbSEugen Hristev 
21aa132ffbSEugen Hristev #define DRIVER_NAME					"resistive-adc-touch"
22aa132ffbSEugen Hristev #define GRTS_DEFAULT_PRESSURE_MIN			50000
23fb082cd5SOleksij Rempel #define GRTS_DEFAULT_PRESSURE_MAX			65535
24aa132ffbSEugen Hristev #define GRTS_MAX_POS_MASK				GENMASK(11, 0)
25fb082cd5SOleksij Rempel #define GRTS_MAX_CHANNELS				4
26fb082cd5SOleksij Rempel 
27fb082cd5SOleksij Rempel enum grts_ch_type {
28fb082cd5SOleksij Rempel 	GRTS_CH_NONE = 0,
29fb082cd5SOleksij Rempel 	GRTS_CH_X,
30fb082cd5SOleksij Rempel 	GRTS_CH_Y,
31fb082cd5SOleksij Rempel 	GRTS_CH_PRESSURE,
32fb082cd5SOleksij Rempel 	GRTS_CH_Z1,
33fb082cd5SOleksij Rempel 	GRTS_CH_Z2,
34fb082cd5SOleksij Rempel };
35aa132ffbSEugen Hristev 
36aa132ffbSEugen Hristev /**
37136feb4cSLee Jones  * struct grts_state - generic resistive touch screen information struct
3802e28cf7SAndy Shevchenko  * @x_plate_ohms:	resistance of the X plate
39aa132ffbSEugen Hristev  * @pressure_min:	number representing the minimum for the pressure
40aa132ffbSEugen Hristev  * @pressure:		are we getting pressure info or not
41aa132ffbSEugen Hristev  * @iio_chans:		list of channels acquired
42aa132ffbSEugen Hristev  * @iio_cb:		iio_callback buffer for the data
43aa132ffbSEugen Hristev  * @input:		the input device structure that we register
44aa132ffbSEugen Hristev  * @prop:		touchscreen properties struct
4502e28cf7SAndy Shevchenko  * @ch:			channels that are defined for the touchscreen
46aa132ffbSEugen Hristev  */
47aa132ffbSEugen Hristev struct grts_state {
48fb082cd5SOleksij Rempel 	u32				x_plate_ohms;
4902e28cf7SAndy Shevchenko 	u32				pressure_min;
50aa132ffbSEugen Hristev 	bool				pressure;
51aa132ffbSEugen Hristev 	struct iio_channel		*iio_chans;
52aa132ffbSEugen Hristev 	struct iio_cb_buffer		*iio_cb;
53aa132ffbSEugen Hristev 	struct input_dev		*input;
54aa132ffbSEugen Hristev 	struct touchscreen_properties	prop;
55fb082cd5SOleksij Rempel 	u8				ch[GRTS_MAX_CHANNELS];
56aa132ffbSEugen Hristev };
57aa132ffbSEugen Hristev 
58aa132ffbSEugen Hristev static int grts_cb(const void *data, void *private)
59aa132ffbSEugen Hristev {
60aa132ffbSEugen Hristev 	const u16 *touch_info = data;
61aa132ffbSEugen Hristev 	struct grts_state *st = private;
62fb082cd5SOleksij Rempel 	unsigned int x, y, press = 0, z1 = 0, z2;
63fb082cd5SOleksij Rempel 	unsigned int Rt, i;
64aa132ffbSEugen Hristev 
65fb082cd5SOleksij Rempel 	for (i = 0; i < ARRAY_SIZE(st->ch) && st->ch[i] != GRTS_CH_NONE; i++) {
66fb082cd5SOleksij Rempel 		switch (st->ch[i]) {
67fb082cd5SOleksij Rempel 		case GRTS_CH_X:
68fb082cd5SOleksij Rempel 			x = touch_info[i];
69fb082cd5SOleksij Rempel 			break;
70fb082cd5SOleksij Rempel 		case GRTS_CH_Y:
71fb082cd5SOleksij Rempel 			y = touch_info[i];
72fb082cd5SOleksij Rempel 			break;
73fb082cd5SOleksij Rempel 		case GRTS_CH_PRESSURE:
74fb082cd5SOleksij Rempel 			press = touch_info[i];
75fb082cd5SOleksij Rempel 			break;
76fb082cd5SOleksij Rempel 		case GRTS_CH_Z1:
77fb082cd5SOleksij Rempel 			z1 = touch_info[i];
78fb082cd5SOleksij Rempel 			break;
79fb082cd5SOleksij Rempel 		case GRTS_CH_Z2:
80fb082cd5SOleksij Rempel 			z2 = touch_info[i];
81fb082cd5SOleksij Rempel 			break;
82fb082cd5SOleksij Rempel 		}
83fb082cd5SOleksij Rempel 	}
84fb082cd5SOleksij Rempel 
85fb082cd5SOleksij Rempel 	if (z1) {
86fb082cd5SOleksij Rempel 		Rt = z2;
87fb082cd5SOleksij Rempel 		Rt -= z1;
88fb082cd5SOleksij Rempel 		Rt *= st->x_plate_ohms;
89fb082cd5SOleksij Rempel 		Rt = DIV_ROUND_CLOSEST(Rt, 16);
90fb082cd5SOleksij Rempel 		Rt *= x;
91fb082cd5SOleksij Rempel 		Rt /= z1;
92fb082cd5SOleksij Rempel 		Rt = DIV_ROUND_CLOSEST(Rt, 256);
93fb082cd5SOleksij Rempel 		/*
94fb082cd5SOleksij Rempel 		 * On increased pressure the resistance (Rt) is decreasing
95fb082cd5SOleksij Rempel 		 * so, convert values to make it looks as real pressure.
96fb082cd5SOleksij Rempel 		 */
97fb082cd5SOleksij Rempel 		if (Rt < GRTS_DEFAULT_PRESSURE_MAX)
98fb082cd5SOleksij Rempel 			press = GRTS_DEFAULT_PRESSURE_MAX - Rt;
99fb082cd5SOleksij Rempel 		else
100fb082cd5SOleksij Rempel 			press = 0;
101fb082cd5SOleksij Rempel 	}
102aa132ffbSEugen Hristev 
103aa132ffbSEugen Hristev 	if ((!x && !y) || (st->pressure && (press < st->pressure_min))) {
104aa132ffbSEugen Hristev 		/* report end of touch */
105aa132ffbSEugen Hristev 		input_report_key(st->input, BTN_TOUCH, 0);
106aa132ffbSEugen Hristev 		input_sync(st->input);
107aa132ffbSEugen Hristev 		return 0;
108aa132ffbSEugen Hristev 	}
109aa132ffbSEugen Hristev 
110aa132ffbSEugen Hristev 	/* report proper touch to subsystem*/
111aa132ffbSEugen Hristev 	touchscreen_report_pos(st->input, &st->prop, x, y, false);
112aa132ffbSEugen Hristev 	if (st->pressure)
113aa132ffbSEugen Hristev 		input_report_abs(st->input, ABS_PRESSURE, press);
114aa132ffbSEugen Hristev 	input_report_key(st->input, BTN_TOUCH, 1);
115aa132ffbSEugen Hristev 	input_sync(st->input);
116aa132ffbSEugen Hristev 
117aa132ffbSEugen Hristev 	return 0;
118aa132ffbSEugen Hristev }
119aa132ffbSEugen Hristev 
120aa132ffbSEugen Hristev static int grts_open(struct input_dev *dev)
121aa132ffbSEugen Hristev {
122aa132ffbSEugen Hristev 	int error;
123aa132ffbSEugen Hristev 	struct grts_state *st = input_get_drvdata(dev);
124aa132ffbSEugen Hristev 
125aa132ffbSEugen Hristev 	error = iio_channel_start_all_cb(st->iio_cb);
126aa132ffbSEugen Hristev 	if (error) {
127aa132ffbSEugen Hristev 		dev_err(dev->dev.parent, "failed to start callback buffer.\n");
128aa132ffbSEugen Hristev 		return error;
129aa132ffbSEugen Hristev 	}
130aa132ffbSEugen Hristev 	return 0;
131aa132ffbSEugen Hristev }
132aa132ffbSEugen Hristev 
133aa132ffbSEugen Hristev static void grts_close(struct input_dev *dev)
134aa132ffbSEugen Hristev {
135aa132ffbSEugen Hristev 	struct grts_state *st = input_get_drvdata(dev);
136aa132ffbSEugen Hristev 
137aa132ffbSEugen Hristev 	iio_channel_stop_all_cb(st->iio_cb);
138aa132ffbSEugen Hristev }
139aa132ffbSEugen Hristev 
140aa132ffbSEugen Hristev static void grts_disable(void *data)
141aa132ffbSEugen Hristev {
142aa132ffbSEugen Hristev 	iio_channel_release_all_cb(data);
143aa132ffbSEugen Hristev }
144aa132ffbSEugen Hristev 
145fb082cd5SOleksij Rempel static int grts_get_properties(struct grts_state *st, struct device *dev)
146fb082cd5SOleksij Rempel {
147fb082cd5SOleksij Rempel 	int idx, error;
148fb082cd5SOleksij Rempel 
149fb082cd5SOleksij Rempel 	idx = device_property_match_string(dev, "io-channel-names", "x");
150fb082cd5SOleksij Rempel 	if (idx < 0)
151fb082cd5SOleksij Rempel 		return idx;
152fb082cd5SOleksij Rempel 
153fb082cd5SOleksij Rempel 	if (idx >= ARRAY_SIZE(st->ch))
154fb082cd5SOleksij Rempel 		return -EOVERFLOW;
155fb082cd5SOleksij Rempel 
156fb082cd5SOleksij Rempel 	st->ch[idx] = GRTS_CH_X;
157fb082cd5SOleksij Rempel 
158fb082cd5SOleksij Rempel 	idx = device_property_match_string(dev, "io-channel-names", "y");
159fb082cd5SOleksij Rempel 	if (idx < 0)
160fb082cd5SOleksij Rempel 		return idx;
161fb082cd5SOleksij Rempel 
162fb082cd5SOleksij Rempel 	if (idx >= ARRAY_SIZE(st->ch))
163fb082cd5SOleksij Rempel 		return -EOVERFLOW;
164fb082cd5SOleksij Rempel 
165fb082cd5SOleksij Rempel 	st->ch[idx] = GRTS_CH_Y;
166fb082cd5SOleksij Rempel 
167fb082cd5SOleksij Rempel 	/* pressure is optional */
168fb082cd5SOleksij Rempel 	idx = device_property_match_string(dev, "io-channel-names", "pressure");
169fb082cd5SOleksij Rempel 	if (idx >= 0) {
170fb082cd5SOleksij Rempel 		if (idx >= ARRAY_SIZE(st->ch))
171fb082cd5SOleksij Rempel 			return -EOVERFLOW;
172fb082cd5SOleksij Rempel 
173fb082cd5SOleksij Rempel 		st->ch[idx] = GRTS_CH_PRESSURE;
174fb082cd5SOleksij Rempel 		st->pressure = true;
175fb082cd5SOleksij Rempel 
176fb082cd5SOleksij Rempel 		return 0;
177fb082cd5SOleksij Rempel 	}
178fb082cd5SOleksij Rempel 
179fb082cd5SOleksij Rempel 	/* if no pressure is defined, try optional z1 + z2 */
180fb082cd5SOleksij Rempel 	idx = device_property_match_string(dev, "io-channel-names", "z1");
181fb082cd5SOleksij Rempel 	if (idx < 0)
182fb082cd5SOleksij Rempel 		return 0;
183fb082cd5SOleksij Rempel 
184fb082cd5SOleksij Rempel 	if (idx >= ARRAY_SIZE(st->ch))
185fb082cd5SOleksij Rempel 		return -EOVERFLOW;
186fb082cd5SOleksij Rempel 
187fb082cd5SOleksij Rempel 	st->ch[idx] = GRTS_CH_Z1;
188fb082cd5SOleksij Rempel 
189fb082cd5SOleksij Rempel 	/* if z1 is provided z2 is not optional */
190fb082cd5SOleksij Rempel 	idx = device_property_match_string(dev, "io-channel-names", "z2");
191fb082cd5SOleksij Rempel 	if (idx < 0)
192fb082cd5SOleksij Rempel 		return idx;
193fb082cd5SOleksij Rempel 
194fb082cd5SOleksij Rempel 	if (idx >= ARRAY_SIZE(st->ch))
195fb082cd5SOleksij Rempel 		return -EOVERFLOW;
196fb082cd5SOleksij Rempel 
197fb082cd5SOleksij Rempel 	st->ch[idx] = GRTS_CH_Z2;
198fb082cd5SOleksij Rempel 	st->pressure = true;
199fb082cd5SOleksij Rempel 
200fb082cd5SOleksij Rempel 	error = device_property_read_u32(dev,
201fb082cd5SOleksij Rempel 					 "touchscreen-x-plate-ohms",
202fb082cd5SOleksij Rempel 					 &st->x_plate_ohms);
203fb082cd5SOleksij Rempel 	if (error) {
204fb082cd5SOleksij Rempel 		dev_err(dev, "can't get touchscreen-x-plate-ohms property\n");
205fb082cd5SOleksij Rempel 		return error;
206fb082cd5SOleksij Rempel 	}
207fb082cd5SOleksij Rempel 
208fb082cd5SOleksij Rempel 	return 0;
209fb082cd5SOleksij Rempel }
210fb082cd5SOleksij Rempel 
211aa132ffbSEugen Hristev static int grts_probe(struct platform_device *pdev)
212aa132ffbSEugen Hristev {
213aa132ffbSEugen Hristev 	struct grts_state *st;
214aa132ffbSEugen Hristev 	struct input_dev *input;
215aa132ffbSEugen Hristev 	struct device *dev = &pdev->dev;
216aa132ffbSEugen Hristev 	int error;
217aa132ffbSEugen Hristev 
218aa132ffbSEugen Hristev 	st = devm_kzalloc(dev, sizeof(struct grts_state), GFP_KERNEL);
219aa132ffbSEugen Hristev 	if (!st)
220aa132ffbSEugen Hristev 		return -ENOMEM;
221aa132ffbSEugen Hristev 
222aa132ffbSEugen Hristev 	/* get the channels from IIO device */
223aa132ffbSEugen Hristev 	st->iio_chans = devm_iio_channel_get_all(dev);
224aa132ffbSEugen Hristev 	if (IS_ERR(st->iio_chans)) {
225aa132ffbSEugen Hristev 		error = PTR_ERR(st->iio_chans);
226aa132ffbSEugen Hristev 		if (error != -EPROBE_DEFER)
227aa132ffbSEugen Hristev 			dev_err(dev, "can't get iio channels.\n");
228aa132ffbSEugen Hristev 		return error;
229aa132ffbSEugen Hristev 	}
230aa132ffbSEugen Hristev 
231fb082cd5SOleksij Rempel 	if (!device_property_present(dev, "io-channel-names"))
232fb082cd5SOleksij Rempel 		return -ENODEV;
233fb082cd5SOleksij Rempel 
234fb082cd5SOleksij Rempel 	error = grts_get_properties(st, dev);
235fb082cd5SOleksij Rempel 	if (error) {
236fb082cd5SOleksij Rempel 		dev_err(dev, "Failed to parse properties\n");
237fb082cd5SOleksij Rempel 		return error;
238aa132ffbSEugen Hristev 	}
239aa132ffbSEugen Hristev 
240aa132ffbSEugen Hristev 	if (st->pressure) {
241aa132ffbSEugen Hristev 		error = device_property_read_u32(dev,
242aa132ffbSEugen Hristev 						 "touchscreen-min-pressure",
243aa132ffbSEugen Hristev 						 &st->pressure_min);
244aa132ffbSEugen Hristev 		if (error) {
245aa132ffbSEugen Hristev 			dev_dbg(dev, "can't get touchscreen-min-pressure property.\n");
246aa132ffbSEugen Hristev 			st->pressure_min = GRTS_DEFAULT_PRESSURE_MIN;
247aa132ffbSEugen Hristev 		}
248aa132ffbSEugen Hristev 	}
249aa132ffbSEugen Hristev 
250aa132ffbSEugen Hristev 	input = devm_input_allocate_device(dev);
251aa132ffbSEugen Hristev 	if (!input) {
252aa132ffbSEugen Hristev 		dev_err(dev, "failed to allocate input device.\n");
253aa132ffbSEugen Hristev 		return -ENOMEM;
254aa132ffbSEugen Hristev 	}
255aa132ffbSEugen Hristev 
256aa132ffbSEugen Hristev 	input->name = DRIVER_NAME;
257aa132ffbSEugen Hristev 	input->id.bustype = BUS_HOST;
258aa132ffbSEugen Hristev 	input->open = grts_open;
259aa132ffbSEugen Hristev 	input->close = grts_close;
260aa132ffbSEugen Hristev 
261aa132ffbSEugen Hristev 	input_set_abs_params(input, ABS_X, 0, GRTS_MAX_POS_MASK - 1, 0, 0);
262aa132ffbSEugen Hristev 	input_set_abs_params(input, ABS_Y, 0, GRTS_MAX_POS_MASK - 1, 0, 0);
263aa132ffbSEugen Hristev 	if (st->pressure)
264aa132ffbSEugen Hristev 		input_set_abs_params(input, ABS_PRESSURE, st->pressure_min,
265fb082cd5SOleksij Rempel 				     GRTS_DEFAULT_PRESSURE_MAX, 0, 0);
266aa132ffbSEugen Hristev 
267aa132ffbSEugen Hristev 	input_set_capability(input, EV_KEY, BTN_TOUCH);
268aa132ffbSEugen Hristev 
269aa132ffbSEugen Hristev 	/* parse optional device tree properties */
270aa132ffbSEugen Hristev 	touchscreen_parse_properties(input, false, &st->prop);
271aa132ffbSEugen Hristev 
272aa132ffbSEugen Hristev 	st->input = input;
273aa132ffbSEugen Hristev 	input_set_drvdata(input, st);
274aa132ffbSEugen Hristev 
275aa132ffbSEugen Hristev 	error = input_register_device(input);
276aa132ffbSEugen Hristev 	if (error) {
277aa132ffbSEugen Hristev 		dev_err(dev, "failed to register input device.");
278aa132ffbSEugen Hristev 		return error;
279aa132ffbSEugen Hristev 	}
280aa132ffbSEugen Hristev 
281aa132ffbSEugen Hristev 	st->iio_cb = iio_channel_get_all_cb(dev, grts_cb, st);
282aa132ffbSEugen Hristev 	if (IS_ERR(st->iio_cb)) {
283aa132ffbSEugen Hristev 		dev_err(dev, "failed to allocate callback buffer.\n");
284aa132ffbSEugen Hristev 		return PTR_ERR(st->iio_cb);
285aa132ffbSEugen Hristev 	}
286aa132ffbSEugen Hristev 
287aa132ffbSEugen Hristev 	error = devm_add_action_or_reset(dev, grts_disable, st->iio_cb);
288aa132ffbSEugen Hristev 	if (error) {
289aa132ffbSEugen Hristev 		dev_err(dev, "failed to add disable action.\n");
290aa132ffbSEugen Hristev 		return error;
291aa132ffbSEugen Hristev 	}
292aa132ffbSEugen Hristev 
293aa132ffbSEugen Hristev 	return 0;
294aa132ffbSEugen Hristev }
295aa132ffbSEugen Hristev 
296aa132ffbSEugen Hristev static const struct of_device_id grts_of_match[] = {
297aa132ffbSEugen Hristev 	{
298aa132ffbSEugen Hristev 		.compatible = "resistive-adc-touch",
299aa132ffbSEugen Hristev 	}, {
300aa132ffbSEugen Hristev 		/* sentinel */
301aa132ffbSEugen Hristev 	},
302aa132ffbSEugen Hristev };
303aa132ffbSEugen Hristev 
304aa132ffbSEugen Hristev MODULE_DEVICE_TABLE(of, grts_of_match);
305aa132ffbSEugen Hristev 
306aa132ffbSEugen Hristev static struct platform_driver grts_driver = {
307aa132ffbSEugen Hristev 	.probe = grts_probe,
308aa132ffbSEugen Hristev 	.driver = {
309aa132ffbSEugen Hristev 		.name = DRIVER_NAME,
310*08729298SAndy Shevchenko 		.of_match_table = grts_of_match,
311aa132ffbSEugen Hristev 	},
312aa132ffbSEugen Hristev };
313aa132ffbSEugen Hristev 
314aa132ffbSEugen Hristev module_platform_driver(grts_driver);
315aa132ffbSEugen Hristev 
316aa132ffbSEugen Hristev MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
317aa132ffbSEugen Hristev MODULE_DESCRIPTION("Generic ADC Resistive Touch Driver");
318aa132ffbSEugen Hristev MODULE_LICENSE("GPL v2");
319