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