]>
Commit | Line | Data |
---|---|---|
acb45439 | 1 | /* |
5437775e | 2 | * Touchscreen driver for UCB1x00-based touchscreens |
acb45439 RK |
3 | * |
4 | * Copyright (C) 2001 Russell King, All Rights Reserved. | |
5437775e | 5 | * Copyright (C) 2005 Pavel Machek |
acb45439 RK |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * 21-Jan-2002 <jco@ict.es> : | |
12 | * | |
13 | * Added support for synchronous A/D mode. This mode is useful to | |
14 | * avoid noise induced in the touchpanel by the LCD, provided that | |
15 | * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin. | |
16 | * It is important to note that the signal connected to the ADCSYNC | |
17 | * pin should provide pulses even when the LCD is blanked, otherwise | |
18 | * a pen touch needed to unblank the LCD will never be read. | |
19 | */ | |
20 | #include <linux/config.h> | |
21 | #include <linux/module.h> | |
22 | #include <linux/moduleparam.h> | |
23 | #include <linux/init.h> | |
24 | #include <linux/smp.h> | |
25 | #include <linux/smp_lock.h> | |
26 | #include <linux/sched.h> | |
27 | #include <linux/completion.h> | |
28 | #include <linux/delay.h> | |
29 | #include <linux/string.h> | |
30 | #include <linux/input.h> | |
31 | #include <linux/device.h> | |
32 | #include <linux/suspend.h> | |
33 | #include <linux/slab.h> | |
5437775e | 34 | #include <linux/kthread.h> |
acb45439 RK |
35 | |
36 | #include <asm/dma.h> | |
37 | #include <asm/semaphore.h> | |
38 | ||
39 | #include "ucb1x00.h" | |
40 | ||
41 | ||
42 | struct ucb1x00_ts { | |
43 | struct input_dev idev; | |
44 | struct ucb1x00 *ucb; | |
45 | ||
46 | wait_queue_head_t irq_wait; | |
acb45439 | 47 | struct task_struct *rtask; |
acb45439 RK |
48 | u16 x_res; |
49 | u16 y_res; | |
50 | ||
6b9ea421 RK |
51 | unsigned int restart:1; |
52 | unsigned int adcsync:1; | |
acb45439 RK |
53 | }; |
54 | ||
55 | static int adcsync; | |
56 | ||
57 | static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) | |
58 | { | |
59 | input_report_abs(&ts->idev, ABS_X, x); | |
60 | input_report_abs(&ts->idev, ABS_Y, y); | |
61 | input_report_abs(&ts->idev, ABS_PRESSURE, pressure); | |
62 | input_sync(&ts->idev); | |
63 | } | |
64 | ||
65 | static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) | |
66 | { | |
67 | input_report_abs(&ts->idev, ABS_PRESSURE, 0); | |
68 | input_sync(&ts->idev); | |
69 | } | |
70 | ||
71 | /* | |
72 | * Switch to interrupt mode. | |
73 | */ | |
74 | static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts) | |
75 | { | |
76 | ucb1x00_reg_write(ts->ucb, UCB_TS_CR, | |
77 | UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | | |
78 | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | | |
79 | UCB_TS_CR_MODE_INT); | |
80 | } | |
81 | ||
82 | /* | |
83 | * Switch to pressure mode, and read pressure. We don't need to wait | |
84 | * here, since both plates are being driven. | |
85 | */ | |
86 | static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts) | |
87 | { | |
88 | ucb1x00_reg_write(ts->ucb, UCB_TS_CR, | |
89 | UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | | |
90 | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | | |
91 | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); | |
92 | ||
93 | return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); | |
94 | } | |
95 | ||
96 | /* | |
97 | * Switch to X position mode and measure Y plate. We switch the plate | |
98 | * configuration in pressure mode, then switch to position mode. This | |
99 | * gives a faster response time. Even so, we need to wait about 55us | |
100 | * for things to stabilise. | |
101 | */ | |
102 | static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts) | |
103 | { | |
104 | ucb1x00_reg_write(ts->ucb, UCB_TS_CR, | |
105 | UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | | |
106 | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); | |
107 | ucb1x00_reg_write(ts->ucb, UCB_TS_CR, | |
108 | UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | | |
109 | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); | |
110 | ucb1x00_reg_write(ts->ucb, UCB_TS_CR, | |
111 | UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | | |
112 | UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); | |
113 | ||
114 | udelay(55); | |
115 | ||
116 | return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); | |
117 | } | |
118 | ||
119 | /* | |
120 | * Switch to Y position mode and measure X plate. We switch the plate | |
121 | * configuration in pressure mode, then switch to position mode. This | |
122 | * gives a faster response time. Even so, we need to wait about 55us | |
123 | * for things to stabilise. | |
124 | */ | |
125 | static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts) | |
126 | { | |
127 | ucb1x00_reg_write(ts->ucb, UCB_TS_CR, | |
128 | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | | |
129 | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); | |
130 | ucb1x00_reg_write(ts->ucb, UCB_TS_CR, | |
131 | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | | |
132 | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); | |
133 | ucb1x00_reg_write(ts->ucb, UCB_TS_CR, | |
134 | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | | |
135 | UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); | |
136 | ||
137 | udelay(55); | |
138 | ||
139 | return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); | |
140 | } | |
141 | ||
142 | /* | |
143 | * Switch to X plate resistance mode. Set MX to ground, PX to | |
144 | * supply. Measure current. | |
145 | */ | |
146 | static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts) | |
147 | { | |
148 | ucb1x00_reg_write(ts->ucb, UCB_TS_CR, | |
149 | UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | | |
150 | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); | |
151 | return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); | |
152 | } | |
153 | ||
154 | /* | |
155 | * Switch to Y plate resistance mode. Set MY to ground, PY to | |
156 | * supply. Measure current. | |
157 | */ | |
158 | static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts) | |
159 | { | |
160 | ucb1x00_reg_write(ts->ucb, UCB_TS_CR, | |
161 | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | | |
162 | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); | |
163 | return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); | |
164 | } | |
165 | ||
166 | /* | |
167 | * This is a RT kernel thread that handles the ADC accesses | |
168 | * (mainly so we can use semaphores in the UCB1200 core code | |
169 | * to serialise accesses to the ADC). | |
170 | */ | |
171 | static int ucb1x00_thread(void *_ts) | |
172 | { | |
173 | struct ucb1x00_ts *ts = _ts; | |
174 | struct task_struct *tsk = current; | |
175 | DECLARE_WAITQUEUE(wait, tsk); | |
176 | int valid; | |
177 | ||
acb45439 RK |
178 | /* |
179 | * We could run as a real-time thread. However, thus far | |
180 | * this doesn't seem to be necessary. | |
181 | */ | |
182 | // tsk->policy = SCHED_FIFO; | |
183 | // tsk->rt_priority = 1; | |
184 | ||
acb45439 RK |
185 | valid = 0; |
186 | ||
187 | add_wait_queue(&ts->irq_wait, &wait); | |
5437775e | 188 | while (!kthread_should_stop()) { |
acb45439 RK |
189 | unsigned int x, y, p, val; |
190 | signed long timeout; | |
191 | ||
192 | ts->restart = 0; | |
193 | ||
194 | ucb1x00_adc_enable(ts->ucb); | |
195 | ||
196 | x = ucb1x00_ts_read_xpos(ts); | |
197 | y = ucb1x00_ts_read_ypos(ts); | |
198 | p = ucb1x00_ts_read_pressure(ts); | |
199 | ||
200 | /* | |
201 | * Switch back to interrupt mode. | |
202 | */ | |
203 | ucb1x00_ts_mode_int(ts); | |
204 | ucb1x00_adc_disable(ts->ucb); | |
205 | ||
5437775e | 206 | msleep(10); |
acb45439 RK |
207 | |
208 | ucb1x00_enable(ts->ucb); | |
209 | val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR); | |
210 | ||
211 | if (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)) { | |
212 | set_task_state(tsk, TASK_INTERRUPTIBLE); | |
213 | ||
214 | ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); | |
215 | ucb1x00_disable(ts->ucb); | |
216 | ||
217 | /* | |
218 | * If we spat out a valid sample set last time, | |
219 | * spit out a "pen off" sample here. | |
220 | */ | |
221 | if (valid) { | |
222 | ucb1x00_ts_event_release(ts); | |
223 | valid = 0; | |
224 | } | |
225 | ||
226 | timeout = MAX_SCHEDULE_TIMEOUT; | |
227 | } else { | |
228 | ucb1x00_disable(ts->ucb); | |
229 | ||
230 | /* | |
231 | * Filtering is policy. Policy belongs in user | |
232 | * space. We therefore leave it to user space | |
233 | * to do any filtering they please. | |
234 | */ | |
235 | if (!ts->restart) { | |
236 | ucb1x00_ts_evt_add(ts, p, x, y); | |
237 | valid = 1; | |
238 | } | |
239 | ||
240 | set_task_state(tsk, TASK_INTERRUPTIBLE); | |
241 | timeout = HZ / 100; | |
242 | } | |
243 | ||
244 | try_to_freeze(); | |
245 | ||
246 | schedule_timeout(timeout); | |
acb45439 RK |
247 | } |
248 | ||
249 | remove_wait_queue(&ts->irq_wait, &wait); | |
250 | ||
251 | ts->rtask = NULL; | |
5437775e | 252 | return 0; |
acb45439 RK |
253 | } |
254 | ||
255 | /* | |
256 | * We only detect touch screen _touches_ with this interrupt | |
257 | * handler, and even then we just schedule our task. | |
258 | */ | |
259 | static void ucb1x00_ts_irq(int idx, void *id) | |
260 | { | |
261 | struct ucb1x00_ts *ts = id; | |
262 | ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); | |
263 | wake_up(&ts->irq_wait); | |
264 | } | |
265 | ||
266 | static int ucb1x00_ts_open(struct input_dev *idev) | |
267 | { | |
268 | struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev; | |
269 | int ret = 0; | |
270 | ||
5437775e | 271 | BUG_ON(ts->rtask); |
acb45439 RK |
272 | |
273 | init_waitqueue_head(&ts->irq_wait); | |
274 | ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts); | |
275 | if (ret < 0) | |
276 | goto out; | |
277 | ||
278 | /* | |
279 | * If we do this at all, we should allow the user to | |
280 | * measure and read the X and Y resistance at any time. | |
281 | */ | |
282 | ucb1x00_adc_enable(ts->ucb); | |
283 | ts->x_res = ucb1x00_ts_read_xres(ts); | |
284 | ts->y_res = ucb1x00_ts_read_yres(ts); | |
285 | ucb1x00_adc_disable(ts->ucb); | |
286 | ||
5437775e PM |
287 | ts->rtask = kthread_run(ucb1x00_thread, ts, "ktsd"); |
288 | if (!IS_ERR(ts->rtask)) { | |
acb45439 RK |
289 | ret = 0; |
290 | } else { | |
291 | ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); | |
5437775e PM |
292 | ts->rtask = NULL; |
293 | ret = -EFAULT; | |
acb45439 RK |
294 | } |
295 | ||
296 | out: | |
acb45439 RK |
297 | return ret; |
298 | } | |
299 | ||
300 | /* | |
301 | * Release touchscreen resources. Disable IRQs. | |
302 | */ | |
303 | static void ucb1x00_ts_close(struct input_dev *idev) | |
304 | { | |
305 | struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev; | |
306 | ||
5437775e PM |
307 | if (ts->rtask) |
308 | kthread_stop(ts->rtask); | |
acb45439 | 309 | |
5437775e PM |
310 | ucb1x00_enable(ts->ucb); |
311 | ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); | |
312 | ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); | |
313 | ucb1x00_disable(ts->ucb); | |
acb45439 RK |
314 | } |
315 | ||
316 | #ifdef CONFIG_PM | |
317 | static int ucb1x00_ts_resume(struct ucb1x00_dev *dev) | |
318 | { | |
319 | struct ucb1x00_ts *ts = dev->priv; | |
320 | ||
321 | if (ts->rtask != NULL) { | |
322 | /* | |
323 | * Restart the TS thread to ensure the | |
324 | * TS interrupt mode is set up again | |
325 | * after sleep. | |
326 | */ | |
327 | ts->restart = 1; | |
328 | wake_up(&ts->irq_wait); | |
329 | } | |
330 | return 0; | |
331 | } | |
332 | #else | |
333 | #define ucb1x00_ts_resume NULL | |
334 | #endif | |
335 | ||
336 | ||
337 | /* | |
338 | * Initialisation. | |
339 | */ | |
340 | static int ucb1x00_ts_add(struct ucb1x00_dev *dev) | |
341 | { | |
342 | struct ucb1x00_ts *ts; | |
343 | ||
344 | ts = kmalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL); | |
345 | if (!ts) | |
346 | return -ENOMEM; | |
347 | ||
348 | memset(ts, 0, sizeof(struct ucb1x00_ts)); | |
349 | ||
350 | ts->ucb = dev->ucb; | |
351 | ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC; | |
acb45439 RK |
352 | |
353 | ts->idev.name = "Touchscreen panel"; | |
354 | ts->idev.id.product = ts->ucb->id; | |
355 | ts->idev.open = ucb1x00_ts_open; | |
356 | ts->idev.close = ucb1x00_ts_close; | |
357 | ||
358 | __set_bit(EV_ABS, ts->idev.evbit); | |
359 | __set_bit(ABS_X, ts->idev.absbit); | |
360 | __set_bit(ABS_Y, ts->idev.absbit); | |
361 | __set_bit(ABS_PRESSURE, ts->idev.absbit); | |
362 | ||
363 | input_register_device(&ts->idev); | |
364 | ||
365 | dev->priv = ts; | |
366 | ||
367 | return 0; | |
368 | } | |
369 | ||
370 | static void ucb1x00_ts_remove(struct ucb1x00_dev *dev) | |
371 | { | |
372 | struct ucb1x00_ts *ts = dev->priv; | |
373 | input_unregister_device(&ts->idev); | |
374 | kfree(ts); | |
375 | } | |
376 | ||
377 | static struct ucb1x00_driver ucb1x00_ts_driver = { | |
378 | .add = ucb1x00_ts_add, | |
379 | .remove = ucb1x00_ts_remove, | |
380 | .resume = ucb1x00_ts_resume, | |
381 | }; | |
382 | ||
383 | static int __init ucb1x00_ts_init(void) | |
384 | { | |
385 | return ucb1x00_register_driver(&ucb1x00_ts_driver); | |
386 | } | |
387 | ||
388 | static void __exit ucb1x00_ts_exit(void) | |
389 | { | |
390 | ucb1x00_unregister_driver(&ucb1x00_ts_driver); | |
391 | } | |
392 | ||
393 | module_param(adcsync, int, 0444); | |
394 | module_init(ucb1x00_ts_init); | |
395 | module_exit(ucb1x00_ts_exit); | |
396 | ||
397 | MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); | |
398 | MODULE_DESCRIPTION("UCB1x00 touchscreen driver"); | |
399 | MODULE_LICENSE("GPL"); |