1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Touchscreen driver for WM831x PMICs |
4 | * |
5 | * Copyright 2011 Wolfson Microelectronics plc. |
6 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/moduleparam.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/string.h> |
13 | #include <linux/pm.h> |
14 | #include <linux/input.h> |
15 | #include <linux/interrupt.h> |
16 | #include <linux/io.h> |
17 | #include <linux/mfd/wm831x/core.h> |
18 | #include <linux/mfd/wm831x/irq.h> |
19 | #include <linux/mfd/wm831x/pdata.h> |
20 | #include <linux/platform_device.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/types.h> |
23 | |
24 | /* |
25 | * R16424 (0x4028) - Touch Control 1 |
26 | */ |
27 | #define WM831X_TCH_ENA 0x8000 /* TCH_ENA */ |
28 | #define WM831X_TCH_CVT_ENA 0x4000 /* TCH_CVT_ENA */ |
29 | #define WM831X_TCH_SLPENA 0x1000 /* TCH_SLPENA */ |
30 | #define WM831X_TCH_Z_ENA 0x0400 /* TCH_Z_ENA */ |
31 | #define WM831X_TCH_Y_ENA 0x0200 /* TCH_Y_ENA */ |
32 | #define WM831X_TCH_X_ENA 0x0100 /* TCH_X_ENA */ |
33 | #define WM831X_TCH_DELAY_MASK 0x00E0 /* TCH_DELAY - [7:5] */ |
34 | #define WM831X_TCH_DELAY_SHIFT 5 /* TCH_DELAY - [7:5] */ |
35 | #define WM831X_TCH_DELAY_WIDTH 3 /* TCH_DELAY - [7:5] */ |
36 | #define WM831X_TCH_RATE_MASK 0x001F /* TCH_RATE - [4:0] */ |
37 | #define WM831X_TCH_RATE_SHIFT 0 /* TCH_RATE - [4:0] */ |
38 | #define WM831X_TCH_RATE_WIDTH 5 /* TCH_RATE - [4:0] */ |
39 | |
40 | /* |
41 | * R16425 (0x4029) - Touch Control 2 |
42 | */ |
43 | #define WM831X_TCH_PD_WK 0x2000 /* TCH_PD_WK */ |
44 | #define WM831X_TCH_5WIRE 0x1000 /* TCH_5WIRE */ |
45 | #define WM831X_TCH_PDONLY 0x0800 /* TCH_PDONLY */ |
46 | #define WM831X_TCH_ISEL 0x0100 /* TCH_ISEL */ |
47 | #define WM831X_TCH_RPU_MASK 0x000F /* TCH_RPU - [3:0] */ |
48 | #define WM831X_TCH_RPU_SHIFT 0 /* TCH_RPU - [3:0] */ |
49 | #define WM831X_TCH_RPU_WIDTH 4 /* TCH_RPU - [3:0] */ |
50 | |
51 | /* |
52 | * R16426-8 (0x402A-C) - Touch Data X/Y/X |
53 | */ |
54 | #define WM831X_TCH_PD 0x8000 /* TCH_PD1 */ |
55 | #define WM831X_TCH_DATA_MASK 0x0FFF /* TCH_DATA - [11:0] */ |
56 | #define WM831X_TCH_DATA_SHIFT 0 /* TCH_DATA - [11:0] */ |
57 | #define WM831X_TCH_DATA_WIDTH 12 /* TCH_DATA - [11:0] */ |
58 | |
59 | struct wm831x_ts { |
60 | struct input_dev *input_dev; |
61 | struct wm831x *wm831x; |
62 | unsigned int data_irq; |
63 | unsigned int pd_irq; |
64 | bool pressure; |
65 | bool pen_down; |
66 | struct work_struct pd_data_work; |
67 | }; |
68 | |
69 | static void wm831x_pd_data_work(struct work_struct *work) |
70 | { |
71 | struct wm831x_ts *wm831x_ts = |
72 | container_of(work, struct wm831x_ts, pd_data_work); |
73 | |
74 | if (wm831x_ts->pen_down) { |
75 | enable_irq(irq: wm831x_ts->data_irq); |
76 | dev_dbg(wm831x_ts->wm831x->dev, "IRQ PD->DATA done\n" ); |
77 | } else { |
78 | enable_irq(irq: wm831x_ts->pd_irq); |
79 | dev_dbg(wm831x_ts->wm831x->dev, "IRQ DATA->PD done\n" ); |
80 | } |
81 | } |
82 | |
83 | static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data) |
84 | { |
85 | struct wm831x_ts *wm831x_ts = irq_data; |
86 | struct wm831x *wm831x = wm831x_ts->wm831x; |
87 | static int data_types[] = { ABS_X, ABS_Y, ABS_PRESSURE }; |
88 | u16 data[3]; |
89 | int count; |
90 | int i, ret; |
91 | |
92 | if (wm831x_ts->pressure) |
93 | count = 3; |
94 | else |
95 | count = 2; |
96 | |
97 | wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, |
98 | WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT); |
99 | |
100 | ret = wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, |
101 | buf: data); |
102 | if (ret != 0) { |
103 | dev_err(wm831x->dev, "Failed to read touch data: %d\n" , |
104 | ret); |
105 | return IRQ_NONE; |
106 | } |
107 | |
108 | /* |
109 | * We get a pen down reading on every reading, report pen up if any |
110 | * individual reading does so. |
111 | */ |
112 | wm831x_ts->pen_down = true; |
113 | for (i = 0; i < count; i++) { |
114 | if (!(data[i] & WM831X_TCH_PD)) { |
115 | wm831x_ts->pen_down = false; |
116 | continue; |
117 | } |
118 | input_report_abs(dev: wm831x_ts->input_dev, code: data_types[i], |
119 | value: data[i] & WM831X_TCH_DATA_MASK); |
120 | } |
121 | |
122 | if (!wm831x_ts->pen_down) { |
123 | /* Switch from data to pen down */ |
124 | dev_dbg(wm831x->dev, "IRQ DATA->PD\n" ); |
125 | |
126 | disable_irq_nosync(irq: wm831x_ts->data_irq); |
127 | |
128 | /* Don't need data any more */ |
129 | wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, |
130 | WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | |
131 | WM831X_TCH_Z_ENA, val: 0); |
132 | |
133 | /* Flush any final samples that arrived while reading */ |
134 | wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, |
135 | WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT); |
136 | |
137 | wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, buf: data); |
138 | |
139 | if (wm831x_ts->pressure) |
140 | input_report_abs(dev: wm831x_ts->input_dev, |
141 | ABS_PRESSURE, value: 0); |
142 | |
143 | input_report_key(dev: wm831x_ts->input_dev, BTN_TOUCH, value: 0); |
144 | |
145 | schedule_work(work: &wm831x_ts->pd_data_work); |
146 | } else { |
147 | input_report_key(dev: wm831x_ts->input_dev, BTN_TOUCH, value: 1); |
148 | } |
149 | |
150 | input_sync(dev: wm831x_ts->input_dev); |
151 | |
152 | return IRQ_HANDLED; |
153 | } |
154 | |
155 | static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data) |
156 | { |
157 | struct wm831x_ts *wm831x_ts = irq_data; |
158 | struct wm831x *wm831x = wm831x_ts->wm831x; |
159 | int ena = 0; |
160 | |
161 | if (wm831x_ts->pen_down) |
162 | return IRQ_HANDLED; |
163 | |
164 | disable_irq_nosync(irq: wm831x_ts->pd_irq); |
165 | |
166 | /* Start collecting data */ |
167 | if (wm831x_ts->pressure) |
168 | ena |= WM831X_TCH_Z_ENA; |
169 | |
170 | wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, |
171 | WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, |
172 | WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena); |
173 | |
174 | wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, |
175 | WM831X_TCHPD_EINT, WM831X_TCHPD_EINT); |
176 | |
177 | wm831x_ts->pen_down = true; |
178 | |
179 | /* Switch from pen down to data */ |
180 | dev_dbg(wm831x->dev, "IRQ PD->DATA\n" ); |
181 | schedule_work(work: &wm831x_ts->pd_data_work); |
182 | |
183 | return IRQ_HANDLED; |
184 | } |
185 | |
186 | static int wm831x_ts_input_open(struct input_dev *idev) |
187 | { |
188 | struct wm831x_ts *wm831x_ts = input_get_drvdata(dev: idev); |
189 | struct wm831x *wm831x = wm831x_ts->wm831x; |
190 | |
191 | wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, |
192 | WM831X_TCH_ENA | WM831X_TCH_CVT_ENA | |
193 | WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | |
194 | WM831X_TCH_Z_ENA, WM831X_TCH_ENA); |
195 | |
196 | wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, |
197 | WM831X_TCH_CVT_ENA, WM831X_TCH_CVT_ENA); |
198 | |
199 | return 0; |
200 | } |
201 | |
202 | static void wm831x_ts_input_close(struct input_dev *idev) |
203 | { |
204 | struct wm831x_ts *wm831x_ts = input_get_drvdata(dev: idev); |
205 | struct wm831x *wm831x = wm831x_ts->wm831x; |
206 | |
207 | /* Shut the controller down, disabling all other functionality too */ |
208 | wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, |
209 | WM831X_TCH_ENA | WM831X_TCH_X_ENA | |
210 | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, val: 0); |
211 | |
212 | /* Make sure any pending IRQs are done, the above will prevent |
213 | * new ones firing. |
214 | */ |
215 | synchronize_irq(irq: wm831x_ts->data_irq); |
216 | synchronize_irq(irq: wm831x_ts->pd_irq); |
217 | |
218 | /* Make sure the IRQ completion work is quiesced */ |
219 | flush_work(work: &wm831x_ts->pd_data_work); |
220 | |
221 | /* If we ended up with the pen down then make sure we revert back |
222 | * to pen detection state for the next time we start up. |
223 | */ |
224 | if (wm831x_ts->pen_down) { |
225 | disable_irq(irq: wm831x_ts->data_irq); |
226 | enable_irq(irq: wm831x_ts->pd_irq); |
227 | wm831x_ts->pen_down = false; |
228 | } |
229 | } |
230 | |
231 | static int wm831x_ts_probe(struct platform_device *pdev) |
232 | { |
233 | struct wm831x_ts *wm831x_ts; |
234 | struct wm831x *wm831x = dev_get_drvdata(dev: pdev->dev.parent); |
235 | struct wm831x_pdata *core_pdata = dev_get_platdata(dev: pdev->dev.parent); |
236 | struct wm831x_touch_pdata *pdata = NULL; |
237 | struct input_dev *input_dev; |
238 | int error, irqf; |
239 | |
240 | if (core_pdata) |
241 | pdata = core_pdata->touch; |
242 | |
243 | wm831x_ts = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct wm831x_ts), |
244 | GFP_KERNEL); |
245 | input_dev = devm_input_allocate_device(&pdev->dev); |
246 | if (!wm831x_ts || !input_dev) { |
247 | error = -ENOMEM; |
248 | goto err_alloc; |
249 | } |
250 | |
251 | wm831x_ts->wm831x = wm831x; |
252 | wm831x_ts->input_dev = input_dev; |
253 | INIT_WORK(&wm831x_ts->pd_data_work, wm831x_pd_data_work); |
254 | |
255 | /* |
256 | * If we have a direct IRQ use it, otherwise use the interrupt |
257 | * from the WM831x IRQ controller. |
258 | */ |
259 | wm831x_ts->data_irq = wm831x_irq(wm831x, |
260 | irq: platform_get_irq_byname(pdev, |
261 | "TCHDATA" )); |
262 | if (pdata && pdata->data_irq) |
263 | wm831x_ts->data_irq = pdata->data_irq; |
264 | |
265 | wm831x_ts->pd_irq = wm831x_irq(wm831x, |
266 | irq: platform_get_irq_byname(pdev, "TCHPD" )); |
267 | if (pdata && pdata->pd_irq) |
268 | wm831x_ts->pd_irq = pdata->pd_irq; |
269 | |
270 | if (pdata) |
271 | wm831x_ts->pressure = pdata->pressure; |
272 | else |
273 | wm831x_ts->pressure = true; |
274 | |
275 | /* Five wire touchscreens can't report pressure */ |
276 | if (pdata && pdata->fivewire) { |
277 | wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, |
278 | WM831X_TCH_5WIRE, WM831X_TCH_5WIRE); |
279 | |
280 | /* Pressure measurements are not possible for five wire mode */ |
281 | WARN_ON(pdata->pressure && pdata->fivewire); |
282 | wm831x_ts->pressure = false; |
283 | } else { |
284 | wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, |
285 | WM831X_TCH_5WIRE, val: 0); |
286 | } |
287 | |
288 | if (pdata) { |
289 | switch (pdata->isel) { |
290 | default: |
291 | dev_err(&pdev->dev, "Unsupported ISEL setting: %d\n" , |
292 | pdata->isel); |
293 | fallthrough; |
294 | case 200: |
295 | case 0: |
296 | wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, |
297 | WM831X_TCH_ISEL, val: 0); |
298 | break; |
299 | case 400: |
300 | wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, |
301 | WM831X_TCH_ISEL, WM831X_TCH_ISEL); |
302 | break; |
303 | } |
304 | } |
305 | |
306 | wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, |
307 | WM831X_TCH_PDONLY, val: 0); |
308 | |
309 | /* Default to 96 samples/sec */ |
310 | wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, |
311 | WM831X_TCH_RATE_MASK, val: 6); |
312 | |
313 | if (pdata && pdata->data_irqf) |
314 | irqf = pdata->data_irqf; |
315 | else |
316 | irqf = IRQF_TRIGGER_HIGH; |
317 | |
318 | error = request_threaded_irq(irq: wm831x_ts->data_irq, |
319 | NULL, thread_fn: wm831x_ts_data_irq, |
320 | flags: irqf | IRQF_ONESHOT | IRQF_NO_AUTOEN, |
321 | name: "Touchscreen data" , dev: wm831x_ts); |
322 | if (error) { |
323 | dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n" , |
324 | wm831x_ts->data_irq, error); |
325 | goto err_alloc; |
326 | } |
327 | |
328 | if (pdata && pdata->pd_irqf) |
329 | irqf = pdata->pd_irqf; |
330 | else |
331 | irqf = IRQF_TRIGGER_HIGH; |
332 | |
333 | error = request_threaded_irq(irq: wm831x_ts->pd_irq, |
334 | NULL, thread_fn: wm831x_ts_pen_down_irq, |
335 | flags: irqf | IRQF_ONESHOT, |
336 | name: "Touchscreen pen down" , dev: wm831x_ts); |
337 | if (error) { |
338 | dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n" , |
339 | wm831x_ts->pd_irq, error); |
340 | goto err_data_irq; |
341 | } |
342 | |
343 | /* set up touch configuration */ |
344 | input_dev->name = "WM831x touchscreen" ; |
345 | input_dev->phys = "wm831x" ; |
346 | input_dev->open = wm831x_ts_input_open; |
347 | input_dev->close = wm831x_ts_input_close; |
348 | |
349 | __set_bit(EV_ABS, input_dev->evbit); |
350 | __set_bit(EV_KEY, input_dev->evbit); |
351 | __set_bit(BTN_TOUCH, input_dev->keybit); |
352 | |
353 | input_set_abs_params(dev: input_dev, ABS_X, min: 0, max: 4095, fuzz: 5, flat: 0); |
354 | input_set_abs_params(dev: input_dev, ABS_Y, min: 0, max: 4095, fuzz: 5, flat: 0); |
355 | if (wm831x_ts->pressure) |
356 | input_set_abs_params(dev: input_dev, ABS_PRESSURE, min: 0, max: 4095, fuzz: 5, flat: 0); |
357 | |
358 | input_set_drvdata(dev: input_dev, data: wm831x_ts); |
359 | input_dev->dev.parent = &pdev->dev; |
360 | |
361 | error = input_register_device(input_dev); |
362 | if (error) |
363 | goto err_pd_irq; |
364 | |
365 | platform_set_drvdata(pdev, data: wm831x_ts); |
366 | return 0; |
367 | |
368 | err_pd_irq: |
369 | free_irq(wm831x_ts->pd_irq, wm831x_ts); |
370 | err_data_irq: |
371 | free_irq(wm831x_ts->data_irq, wm831x_ts); |
372 | err_alloc: |
373 | |
374 | return error; |
375 | } |
376 | |
377 | static int wm831x_ts_remove(struct platform_device *pdev) |
378 | { |
379 | struct wm831x_ts *wm831x_ts = platform_get_drvdata(pdev); |
380 | |
381 | free_irq(wm831x_ts->pd_irq, wm831x_ts); |
382 | free_irq(wm831x_ts->data_irq, wm831x_ts); |
383 | |
384 | return 0; |
385 | } |
386 | |
387 | static struct platform_driver wm831x_ts_driver = { |
388 | .driver = { |
389 | .name = "wm831x-touch" , |
390 | }, |
391 | .probe = wm831x_ts_probe, |
392 | .remove = wm831x_ts_remove, |
393 | }; |
394 | module_platform_driver(wm831x_ts_driver); |
395 | |
396 | /* Module information */ |
397 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>" ); |
398 | MODULE_DESCRIPTION("WM831x PMIC touchscreen driver" ); |
399 | MODULE_LICENSE("GPL" ); |
400 | MODULE_ALIAS("platform:wm831x-touch" ); |
401 | |