diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2012-01-05 01:18:42 -0500 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2012-01-05 01:19:55 -0500 |
commit | c899afedf168b6735911997d8366b7f23e7e59bc (patch) | |
tree | c8160d82bc0b70e9faa661fa5749fe64ca10e821 /drivers/input/touchscreen | |
parent | 75072323a2968c1bd1b74a48ebf5a5d7e5e10183 (diff) |
Input: ucb1400_ts - convert to threaded IRQ
Instead of manually create and handler kernel thread switch to threaded
IRQ and let kernel IRQ core manage thread for us.
Acked-by: Marek Vasut <marek.vasut@gmail.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r-- | drivers/input/touchscreen/ucb1400_ts.c | 235 |
1 files changed, 113 insertions, 122 deletions
diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index 36ff1549434b..5162f4e34252 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c | |||
@@ -20,24 +20,24 @@ | |||
20 | 20 | ||
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | #include <linux/init.h> | 22 | #include <linux/init.h> |
23 | #include <linux/completion.h> | ||
24 | #include <linux/delay.h> | 23 | #include <linux/delay.h> |
24 | #include <linux/sched.h> | ||
25 | #include <linux/wait.h> | ||
25 | #include <linux/input.h> | 26 | #include <linux/input.h> |
26 | #include <linux/device.h> | 27 | #include <linux/device.h> |
27 | #include <linux/interrupt.h> | 28 | #include <linux/interrupt.h> |
28 | #include <linux/suspend.h> | ||
29 | #include <linux/kthread.h> | ||
30 | #include <linux/freezer.h> | ||
31 | #include <linux/ucb1400.h> | 29 | #include <linux/ucb1400.h> |
32 | 30 | ||
31 | #define UCB1400_TS_POLL_PERIOD 10 /* ms */ | ||
32 | |||
33 | static int adcsync; | 33 | static int adcsync; |
34 | static int ts_delay = 55; /* us */ | 34 | static int ts_delay = 55; /* us */ |
35 | static int ts_delay_pressure; /* us */ | 35 | static int ts_delay_pressure; /* us */ |
36 | 36 | ||
37 | /* Switch to interrupt mode. */ | 37 | /* Switch to interrupt mode. */ |
38 | static void ucb1400_ts_mode_int(struct snd_ac97 *ac97) | 38 | static void ucb1400_ts_mode_int(struct ucb1400_ts *ucb) |
39 | { | 39 | { |
40 | ucb1400_reg_write(ac97, UCB_TS_CR, | 40 | ucb1400_reg_write(ucb->ac97, UCB_TS_CR, |
41 | UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | | 41 | UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | |
42 | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | | 42 | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | |
43 | UCB_TS_CR_MODE_INT); | 43 | UCB_TS_CR_MODE_INT); |
@@ -53,7 +53,9 @@ static unsigned int ucb1400_ts_read_pressure(struct ucb1400_ts *ucb) | |||
53 | UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | | 53 | UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | |
54 | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | | 54 | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | |
55 | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); | 55 | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); |
56 | |||
56 | udelay(ts_delay_pressure); | 57 | udelay(ts_delay_pressure); |
58 | |||
57 | return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync); | 59 | return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync); |
58 | } | 60 | } |
59 | 61 | ||
@@ -127,26 +129,26 @@ static unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb) | |||
127 | return ucb1400_adc_read(ucb->ac97, 0, adcsync); | 129 | return ucb1400_adc_read(ucb->ac97, 0, adcsync); |
128 | } | 130 | } |
129 | 131 | ||
130 | static int ucb1400_ts_pen_up(struct snd_ac97 *ac97) | 132 | static int ucb1400_ts_pen_up(struct ucb1400_ts *ucb) |
131 | { | 133 | { |
132 | unsigned short val = ucb1400_reg_read(ac97, UCB_TS_CR); | 134 | unsigned short val = ucb1400_reg_read(ucb->ac97, UCB_TS_CR); |
133 | 135 | ||
134 | return val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW); | 136 | return val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW); |
135 | } | 137 | } |
136 | 138 | ||
137 | static void ucb1400_ts_irq_enable(struct snd_ac97 *ac97) | 139 | static void ucb1400_ts_irq_enable(struct ucb1400_ts *ucb) |
138 | { | 140 | { |
139 | ucb1400_reg_write(ac97, UCB_IE_CLEAR, UCB_IE_TSPX); | 141 | ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, UCB_IE_TSPX); |
140 | ucb1400_reg_write(ac97, UCB_IE_CLEAR, 0); | 142 | ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); |
141 | ucb1400_reg_write(ac97, UCB_IE_FAL, UCB_IE_TSPX); | 143 | ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, UCB_IE_TSPX); |
142 | } | 144 | } |
143 | 145 | ||
144 | static void ucb1400_ts_irq_disable(struct snd_ac97 *ac97) | 146 | static void ucb1400_ts_irq_disable(struct ucb1400_ts *ucb) |
145 | { | 147 | { |
146 | ucb1400_reg_write(ac97, UCB_IE_FAL, 0); | 148 | ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, 0); |
147 | } | 149 | } |
148 | 150 | ||
149 | static void ucb1400_ts_evt_add(struct input_dev *idev, u16 pressure, u16 x, u16 y) | 151 | static void ucb1400_ts_report_event(struct input_dev *idev, u16 pressure, u16 x, u16 y) |
150 | { | 152 | { |
151 | input_report_abs(idev, ABS_X, x); | 153 | input_report_abs(idev, ABS_X, x); |
152 | input_report_abs(idev, ABS_Y, y); | 154 | input_report_abs(idev, ABS_Y, y); |
@@ -162,7 +164,7 @@ static void ucb1400_ts_event_release(struct input_dev *idev) | |||
162 | input_sync(idev); | 164 | input_sync(idev); |
163 | } | 165 | } |
164 | 166 | ||
165 | static void ucb1400_handle_pending_irq(struct ucb1400_ts *ucb) | 167 | static void ucb1400_clear_pending_irq(struct ucb1400_ts *ucb) |
166 | { | 168 | { |
167 | unsigned int isr; | 169 | unsigned int isr; |
168 | 170 | ||
@@ -171,32 +173,34 @@ static void ucb1400_handle_pending_irq(struct ucb1400_ts *ucb) | |||
171 | ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); | 173 | ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); |
172 | 174 | ||
173 | if (isr & UCB_IE_TSPX) | 175 | if (isr & UCB_IE_TSPX) |
174 | ucb1400_ts_irq_disable(ucb->ac97); | 176 | ucb1400_ts_irq_disable(ucb); |
175 | else | 177 | else |
176 | dev_dbg(&ucb->ts_idev->dev, "ucb1400: unexpected IE_STATUS = %#x\n", isr); | 178 | dev_dbg(&ucb->ts_idev->dev, |
177 | enable_irq(ucb->irq); | 179 | "ucb1400: unexpected IE_STATUS = %#x\n", isr); |
178 | } | 180 | } |
179 | 181 | ||
180 | static int ucb1400_ts_thread(void *_ucb) | 182 | /* |
183 | * A restriction with interrupts exists when using the ucb1400, as | ||
184 | * the codec read/write routines may sleep while waiting for codec | ||
185 | * access completion and uses semaphores for access control to the | ||
186 | * AC97 bus. Therefore the driver is forced to use threaded interrupt | ||
187 | * handler. | ||
188 | */ | ||
189 | static irqreturn_t ucb1400_irq(int irqnr, void *devid) | ||
181 | { | 190 | { |
182 | struct ucb1400_ts *ucb = _ucb; | 191 | struct ucb1400_ts *ucb = devid; |
183 | struct task_struct *tsk = current; | 192 | unsigned int x, y, p; |
184 | int valid = 0; | 193 | bool penup; |
185 | struct sched_param param = { .sched_priority = 1 }; | ||
186 | 194 | ||
187 | sched_setscheduler(tsk, SCHED_FIFO, ¶m); | 195 | if (unlikely(irqnr != ucb->irq)) |
196 | return IRQ_NONE; | ||
188 | 197 | ||
189 | set_freezable(); | 198 | ucb1400_clear_pending_irq(ucb); |
190 | while (!kthread_should_stop()) { | ||
191 | unsigned int x, y, p; | ||
192 | long timeout; | ||
193 | 199 | ||
194 | ucb->ts_restart = 0; | 200 | /* Start with a small delay before checking pendown state */ |
201 | msleep(UCB1400_TS_POLL_PERIOD); | ||
195 | 202 | ||
196 | if (ucb->irq_pending) { | 203 | while (!ucb->stopped && !(penup = ucb1400_ts_pen_up(ucb))) { |
197 | ucb->irq_pending = 0; | ||
198 | ucb1400_handle_pending_irq(ucb); | ||
199 | } | ||
200 | 204 | ||
201 | ucb1400_adc_enable(ucb->ac97); | 205 | ucb1400_adc_enable(ucb->ac97); |
202 | x = ucb1400_ts_read_xpos(ucb); | 206 | x = ucb1400_ts_read_xpos(ucb); |
@@ -204,91 +208,62 @@ static int ucb1400_ts_thread(void *_ucb) | |||
204 | p = ucb1400_ts_read_pressure(ucb); | 208 | p = ucb1400_ts_read_pressure(ucb); |
205 | ucb1400_adc_disable(ucb->ac97); | 209 | ucb1400_adc_disable(ucb->ac97); |
206 | 210 | ||
207 | /* Switch back to interrupt mode. */ | 211 | ucb1400_ts_report_event(ucb->ts_idev, p, x, y); |
208 | ucb1400_ts_mode_int(ucb->ac97); | ||
209 | |||
210 | msleep(10); | ||
211 | |||
212 | if (ucb1400_ts_pen_up(ucb->ac97)) { | ||
213 | ucb1400_ts_irq_enable(ucb->ac97); | ||
214 | |||
215 | /* | ||
216 | * If we spat out a valid sample set last time, | ||
217 | * spit out a "pen off" sample here. | ||
218 | */ | ||
219 | if (valid) { | ||
220 | ucb1400_ts_event_release(ucb->ts_idev); | ||
221 | valid = 0; | ||
222 | } | ||
223 | |||
224 | timeout = MAX_SCHEDULE_TIMEOUT; | ||
225 | } else { | ||
226 | valid = 1; | ||
227 | ucb1400_ts_evt_add(ucb->ts_idev, p, x, y); | ||
228 | timeout = msecs_to_jiffies(10); | ||
229 | } | ||
230 | 212 | ||
231 | wait_event_freezable_timeout(ucb->ts_wait, | 213 | wait_event_timeout(ucb->ts_wait, ucb->stopped, |
232 | ucb->irq_pending || ucb->ts_restart || | 214 | msecs_to_jiffies(UCB1400_TS_POLL_PERIOD)); |
233 | kthread_should_stop(), timeout); | ||
234 | } | 215 | } |
235 | 216 | ||
236 | /* Send the "pen off" if we are stopping with the pen still active */ | 217 | ucb1400_ts_event_release(ucb->ts_idev); |
237 | if (valid) | ||
238 | ucb1400_ts_event_release(ucb->ts_idev); | ||
239 | 218 | ||
240 | ucb->ts_task = NULL; | 219 | if (!ucb->stopped) { |
241 | return 0; | 220 | /* Switch back to interrupt mode. */ |
221 | ucb1400_ts_mode_int(ucb); | ||
222 | ucb1400_ts_irq_enable(ucb); | ||
223 | } | ||
224 | |||
225 | return IRQ_HANDLED; | ||
242 | } | 226 | } |
243 | 227 | ||
244 | /* | 228 | static void ucb1400_ts_stop(struct ucb1400_ts *ucb) |
245 | * A restriction with interrupts exists when using the ucb1400, as | ||
246 | * the codec read/write routines may sleep while waiting for codec | ||
247 | * access completion and uses semaphores for access control to the | ||
248 | * AC97 bus. A complete codec read cycle could take anywhere from | ||
249 | * 60 to 100uSec so we *definitely* don't want to spin inside the | ||
250 | * interrupt handler waiting for codec access. So, we handle the | ||
251 | * interrupt by scheduling a RT kernel thread to run in process | ||
252 | * context instead of interrupt context. | ||
253 | */ | ||
254 | static irqreturn_t ucb1400_hard_irq(int irqnr, void *devid) | ||
255 | { | 229 | { |
256 | struct ucb1400_ts *ucb = devid; | 230 | /* Signal IRQ thread to stop polling and disable the handler. */ |
231 | ucb->stopped = true; | ||
232 | mb(); | ||
233 | wake_up(&ucb->ts_wait); | ||
234 | disable_irq(ucb->irq); | ||
257 | 235 | ||
258 | if (irqnr == ucb->irq) { | 236 | ucb1400_ts_irq_disable(ucb); |
259 | disable_irq_nosync(ucb->irq); | 237 | ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 0); |
260 | ucb->irq_pending = 1; | 238 | } |
261 | wake_up(&ucb->ts_wait); | 239 | |
262 | return IRQ_HANDLED; | 240 | /* Must be called with ts->lock held */ |
263 | } | 241 | static void ucb1400_ts_start(struct ucb1400_ts *ucb) |
264 | return IRQ_NONE; | 242 | { |
243 | /* Tell IRQ thread that it may poll the device. */ | ||
244 | ucb->stopped = false; | ||
245 | mb(); | ||
246 | |||
247 | ucb1400_ts_mode_int(ucb); | ||
248 | ucb1400_ts_irq_enable(ucb); | ||
249 | |||
250 | enable_irq(ucb->irq); | ||
265 | } | 251 | } |
266 | 252 | ||
267 | static int ucb1400_ts_open(struct input_dev *idev) | 253 | static int ucb1400_ts_open(struct input_dev *idev) |
268 | { | 254 | { |
269 | struct ucb1400_ts *ucb = input_get_drvdata(idev); | 255 | struct ucb1400_ts *ucb = input_get_drvdata(idev); |
270 | int ret = 0; | ||
271 | |||
272 | BUG_ON(ucb->ts_task); | ||
273 | 256 | ||
274 | ucb->ts_task = kthread_run(ucb1400_ts_thread, ucb, "UCB1400_ts"); | 257 | ucb1400_ts_start(ucb); |
275 | if (IS_ERR(ucb->ts_task)) { | ||
276 | ret = PTR_ERR(ucb->ts_task); | ||
277 | ucb->ts_task = NULL; | ||
278 | } | ||
279 | 258 | ||
280 | return ret; | 259 | return 0; |
281 | } | 260 | } |
282 | 261 | ||
283 | static void ucb1400_ts_close(struct input_dev *idev) | 262 | static void ucb1400_ts_close(struct input_dev *idev) |
284 | { | 263 | { |
285 | struct ucb1400_ts *ucb = input_get_drvdata(idev); | 264 | struct ucb1400_ts *ucb = input_get_drvdata(idev); |
286 | 265 | ||
287 | if (ucb->ts_task) | 266 | ucb1400_ts_stop(ucb); |
288 | kthread_stop(ucb->ts_task); | ||
289 | |||
290 | ucb1400_ts_irq_disable(ucb->ac97); | ||
291 | ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 0); | ||
292 | } | 267 | } |
293 | 268 | ||
294 | #ifndef NO_IRQ | 269 | #ifndef NO_IRQ |
@@ -342,11 +317,11 @@ static int __devinit ucb1400_ts_detect_irq(struct ucb1400_ts *ucb) | |||
342 | return 0; | 317 | return 0; |
343 | } | 318 | } |
344 | 319 | ||
345 | static int __devinit ucb1400_ts_probe(struct platform_device *dev) | 320 | static int __devinit ucb1400_ts_probe(struct platform_device *pdev) |
346 | { | 321 | { |
322 | struct ucb1400_ts *ucb = pdev->dev.platform_data; | ||
347 | int error, x_res, y_res; | 323 | int error, x_res, y_res; |
348 | u16 fcsr; | 324 | u16 fcsr; |
349 | struct ucb1400_ts *ucb = dev->dev.platform_data; | ||
350 | 325 | ||
351 | ucb->ts_idev = input_allocate_device(); | 326 | ucb->ts_idev = input_allocate_device(); |
352 | if (!ucb->ts_idev) { | 327 | if (!ucb->ts_idev) { |
@@ -362,21 +337,13 @@ static int __devinit ucb1400_ts_probe(struct platform_device *dev) | |||
362 | goto err_free_devs; | 337 | goto err_free_devs; |
363 | } | 338 | } |
364 | } | 339 | } |
340 | printk(KERN_DEBUG "UCB1400: found IRQ %d\n", ucb->irq); | ||
365 | 341 | ||
366 | init_waitqueue_head(&ucb->ts_wait); | 342 | init_waitqueue_head(&ucb->ts_wait); |
367 | 343 | ||
368 | error = request_irq(ucb->irq, ucb1400_hard_irq, IRQF_TRIGGER_RISING, | ||
369 | "UCB1400", ucb); | ||
370 | if (error) { | ||
371 | printk(KERN_ERR "ucb1400: unable to grab irq%d: %d\n", | ||
372 | ucb->irq, error); | ||
373 | goto err_free_devs; | ||
374 | } | ||
375 | printk(KERN_DEBUG "UCB1400: found IRQ %d\n", ucb->irq); | ||
376 | |||
377 | input_set_drvdata(ucb->ts_idev, ucb); | 344 | input_set_drvdata(ucb->ts_idev, ucb); |
378 | 345 | ||
379 | ucb->ts_idev->dev.parent = &dev->dev; | 346 | ucb->ts_idev->dev.parent = &pdev->dev; |
380 | ucb->ts_idev->name = "UCB1400 touchscreen interface"; | 347 | ucb->ts_idev->name = "UCB1400 touchscreen interface"; |
381 | ucb->ts_idev->id.vendor = ucb1400_reg_read(ucb->ac97, | 348 | ucb->ts_idev->id.vendor = ucb1400_reg_read(ucb->ac97, |
382 | AC97_VENDOR_ID1); | 349 | AC97_VENDOR_ID1); |
@@ -404,6 +371,17 @@ static int __devinit ucb1400_ts_probe(struct platform_device *dev) | |||
404 | input_set_abs_params(ucb->ts_idev, ABS_Y, 0, y_res, 0, 0); | 371 | input_set_abs_params(ucb->ts_idev, ABS_Y, 0, y_res, 0, 0); |
405 | input_set_abs_params(ucb->ts_idev, ABS_PRESSURE, 0, 0, 0, 0); | 372 | input_set_abs_params(ucb->ts_idev, ABS_PRESSURE, 0, 0, 0, 0); |
406 | 373 | ||
374 | ucb1400_ts_stop(ucb); | ||
375 | |||
376 | error = request_threaded_irq(ucb->irq, NULL, ucb1400_irq, | ||
377 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | ||
378 | "UCB1400", ucb); | ||
379 | if (error) { | ||
380 | printk(KERN_ERR "ucb1400: unable to grab irq%d: %d\n", | ||
381 | ucb->irq, error); | ||
382 | goto err_free_devs; | ||
383 | } | ||
384 | |||
407 | error = input_register_device(ucb->ts_idev); | 385 | error = input_register_device(ucb->ts_idev); |
408 | if (error) | 386 | if (error) |
409 | goto err_free_irq; | 387 | goto err_free_irq; |
@@ -418,9 +396,9 @@ err: | |||
418 | return error; | 396 | return error; |
419 | } | 397 | } |
420 | 398 | ||
421 | static int __devexit ucb1400_ts_remove(struct platform_device *dev) | 399 | static int __devexit ucb1400_ts_remove(struct platform_device *pdev) |
422 | { | 400 | { |
423 | struct ucb1400_ts *ucb = dev->dev.platform_data; | 401 | struct ucb1400_ts *ucb = pdev->dev.platform_data; |
424 | 402 | ||
425 | free_irq(ucb->irq, ucb); | 403 | free_irq(ucb->irq, ucb); |
426 | input_unregister_device(ucb->ts_idev); | 404 | input_unregister_device(ucb->ts_idev); |
@@ -429,24 +407,37 @@ static int __devexit ucb1400_ts_remove(struct platform_device *dev) | |||
429 | } | 407 | } |
430 | 408 | ||
431 | #ifdef CONFIG_PM_SLEEP | 409 | #ifdef CONFIG_PM_SLEEP |
410 | static int ucb1400_ts_suspend(struct device *dev) | ||
411 | { | ||
412 | struct ucb1400_ts *ucb = dev->platform_data; | ||
413 | struct input_dev *idev = ucb->ts_idev; | ||
414 | |||
415 | mutex_lock(&idev->mutex); | ||
416 | |||
417 | if (idev->users) | ||
418 | ucb1400_ts_start(ucb); | ||
419 | |||
420 | mutex_unlock(&idev->mutex); | ||
421 | return 0; | ||
422 | } | ||
423 | |||
432 | static int ucb1400_ts_resume(struct device *dev) | 424 | static int ucb1400_ts_resume(struct device *dev) |
433 | { | 425 | { |
434 | struct ucb1400_ts *ucb = dev->platform_data; | 426 | struct ucb1400_ts *ucb = dev->platform_data; |
427 | struct input_dev *idev = ucb->ts_idev; | ||
435 | 428 | ||
436 | if (ucb->ts_task) { | 429 | mutex_lock(&idev->mutex); |
437 | /* | 430 | |
438 | * Restart the TS thread to ensure the | 431 | if (idev->users) |
439 | * TS interrupt mode is set up again | 432 | ucb1400_ts_stop(ucb); |
440 | * after sleep. | 433 | |
441 | */ | 434 | mutex_unlock(&idev->mutex); |
442 | ucb->ts_restart = 1; | ||
443 | wake_up(&ucb->ts_wait); | ||
444 | } | ||
445 | return 0; | 435 | return 0; |
446 | } | 436 | } |
447 | #endif | 437 | #endif |
448 | 438 | ||
449 | static SIMPLE_DEV_PM_OPS(ucb1400_ts_pm_ops, NULL, ucb1400_ts_resume); | 439 | static SIMPLE_DEV_PM_OPS(ucb1400_ts_pm_ops, |
440 | ucb1400_ts_suspend, ucb1400_ts_resume); | ||
450 | 441 | ||
451 | static struct platform_driver ucb1400_ts_driver = { | 442 | static struct platform_driver ucb1400_ts_driver = { |
452 | .probe = ucb1400_ts_probe, | 443 | .probe = ucb1400_ts_probe, |