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