diff options
author | Imre Deak <imre.deak@nokia.com> | 2006-04-11 23:43:55 -0400 |
---|---|---|
committer | Dmitry Torokhov <dtor_core@ameritech.net> | 2006-04-11 23:43:55 -0400 |
commit | 7de90a8cb9c51145d7f60d8db17ce0fa07d1b281 (patch) | |
tree | 81e2c0a54974c0a552474f5c5cc66d6082a8bfdd | |
parent | c4febb94dae915da4423b81c487eabed9cef5cba (diff) |
Input: ads7846 - miscellaneous fixes
- Add disable attribute to support device locking mode where
unintentional touch event shouldn't wake up the system;
- Update comments;
- Add missing spin_lock_init;
- Do device resume with the lock held;
- Do cleanup calls / free memory in the reverse order of initialization.
Signed-off-by: Imre Deak <imre.deak@nokia.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r-- | drivers/input/touchscreen/ads7846.c | 144 |
1 files changed, 114 insertions, 30 deletions
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index bdec112e89c4..fec3b9b22309 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c | |||
@@ -2,6 +2,8 @@ | |||
2 | * ADS7846 based touchscreen and sensor driver | 2 | * ADS7846 based touchscreen and sensor driver |
3 | * | 3 | * |
4 | * Copyright (c) 2005 David Brownell | 4 | * Copyright (c) 2005 David Brownell |
5 | * Copyright (c) 2006 Nokia Corporation | ||
6 | * Various changes: Imre Deak <imre.deak@nokia.com> | ||
5 | * | 7 | * |
6 | * Using code from: | 8 | * Using code from: |
7 | * - corgi_ts.c | 9 | * - corgi_ts.c |
@@ -34,17 +36,25 @@ | |||
34 | 36 | ||
35 | 37 | ||
36 | /* | 38 | /* |
37 | * This code has been lightly tested on an ads7846. | 39 | * This code has been tested on an ads7846 / N770 device. |
38 | * Support for ads7843 and ads7845 has only been stubbed in. | 40 | * Support for ads7843 and ads7845 has only been stubbed in. |
39 | * | 41 | * |
40 | * Not yet done: investigate the values reported. Are x/y/pressure | 42 | * Not yet done: How accurate are the temperature and voltage |
41 | * event values sane enough for X11? How accurate are the temperature | 43 | * readings? (System-specific calibration should support |
42 | * and voltage readings? (System-specific calibration should support | ||
43 | * accuracy of 0.3 degrees C; otherwise it's 2.0 degrees.) | 44 | * accuracy of 0.3 degrees C; otherwise it's 2.0 degrees.) |
44 | * | 45 | * |
46 | * IRQ handling needs a workaround because of a shortcoming in handling | ||
47 | * edge triggered IRQs on some platforms like the OMAP1/2. These | ||
48 | * platforms don't handle the ARM lazy IRQ disabling properly, thus we | ||
49 | * have to maintain our own SW IRQ disabled status. This should be | ||
50 | * removed as soon as the affected platform's IRQ handling is fixed. | ||
51 | * | ||
45 | * app note sbaa036 talks in more detail about accurate sampling... | 52 | * app note sbaa036 talks in more detail about accurate sampling... |
46 | * that ought to help in situations like LCDs inducing noise (which | 53 | * that ought to help in situations like LCDs inducing noise (which |
47 | * can also be helped by using synch signals) and more generally. | 54 | * can also be helped by using synch signals) and more generally. |
55 | * This driver tries to utilize the measures described in the app | ||
56 | * note. The strength of filtering can be set in the board-* specific | ||
57 | * files. | ||
48 | */ | 58 | */ |
49 | 59 | ||
50 | #define TS_POLL_PERIOD msecs_to_jiffies(10) | 60 | #define TS_POLL_PERIOD msecs_to_jiffies(10) |
@@ -91,6 +101,7 @@ struct ads7846 { | |||
91 | unsigned pending:1; /* P: lock */ | 101 | unsigned pending:1; /* P: lock */ |
92 | // FIXME remove "irq_disabled" | 102 | // FIXME remove "irq_disabled" |
93 | unsigned irq_disabled:1; /* P: lock */ | 103 | unsigned irq_disabled:1; /* P: lock */ |
104 | unsigned disabled:1; | ||
94 | }; | 105 | }; |
95 | 106 | ||
96 | /* leave chip selected when we're done, for quicker re-select? */ | 107 | /* leave chip selected when we're done, for quicker re-select? */ |
@@ -161,6 +172,9 @@ struct ser_req { | |||
161 | struct spi_transfer xfer[6]; | 172 | struct spi_transfer xfer[6]; |
162 | }; | 173 | }; |
163 | 174 | ||
175 | static void ads7846_enable(struct ads7846 *ts); | ||
176 | static void ads7846_disable(struct ads7846 *ts); | ||
177 | |||
164 | static int ads7846_read12_ser(struct device *dev, unsigned command) | 178 | static int ads7846_read12_ser(struct device *dev, unsigned command) |
165 | { | 179 | { |
166 | struct spi_device *spi = to_spi_device(dev); | 180 | struct spi_device *spi = to_spi_device(dev); |
@@ -257,6 +271,37 @@ static ssize_t ads7846_pen_down_show(struct device *dev, | |||
257 | 271 | ||
258 | static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL); | 272 | static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL); |
259 | 273 | ||
274 | static ssize_t ads7846_disable_show(struct device *dev, | ||
275 | struct device_attribute *attr, char *buf) | ||
276 | { | ||
277 | struct ads7846 *ts = dev_get_drvdata(dev); | ||
278 | |||
279 | return sprintf(buf, "%u\n", ts->disabled); | ||
280 | } | ||
281 | |||
282 | static ssize_t ads7846_disable_store(struct device *dev, | ||
283 | struct device_attribute *attr, | ||
284 | const char *buf, size_t count) | ||
285 | { | ||
286 | struct ads7846 *ts = dev_get_drvdata(dev); | ||
287 | char *endp; | ||
288 | int i; | ||
289 | |||
290 | i = simple_strtoul(buf, &endp, 10); | ||
291 | spin_lock_irq(&ts->lock); | ||
292 | |||
293 | if (i) | ||
294 | ads7846_disable(ts); | ||
295 | else | ||
296 | ads7846_enable(ts); | ||
297 | |||
298 | spin_unlock_irq(&ts->lock); | ||
299 | |||
300 | return count; | ||
301 | } | ||
302 | |||
303 | static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store); | ||
304 | |||
260 | /*--------------------------------------------------------------------------*/ | 305 | /*--------------------------------------------------------------------------*/ |
261 | 306 | ||
262 | /* | 307 | /* |
@@ -396,12 +441,9 @@ static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs) | |||
396 | { | 441 | { |
397 | struct ads7846 *ts = handle; | 442 | struct ads7846 *ts = handle; |
398 | unsigned long flags; | 443 | unsigned long flags; |
399 | int r = IRQ_HANDLED; | ||
400 | 444 | ||
401 | spin_lock_irqsave(&ts->lock, flags); | 445 | spin_lock_irqsave(&ts->lock, flags); |
402 | if (ts->irq_disabled) | 446 | if (likely(!ts->irq_disabled && !ts->disabled)) { |
403 | r = IRQ_HANDLED; | ||
404 | else { | ||
405 | if (!ts->irq_disabled) { | 447 | if (!ts->irq_disabled) { |
406 | /* REVISIT irq logic for many ARM chips has cloned a | 448 | /* REVISIT irq logic for many ARM chips has cloned a |
407 | * bug wherein disabling an irq in its handler won't | 449 | * bug wherein disabling an irq in its handler won't |
@@ -419,20 +461,17 @@ static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs) | |||
419 | } | 461 | } |
420 | } | 462 | } |
421 | spin_unlock_irqrestore(&ts->lock, flags); | 463 | spin_unlock_irqrestore(&ts->lock, flags); |
422 | return r; | 464 | |
465 | return IRQ_HANDLED; | ||
423 | } | 466 | } |
424 | 467 | ||
425 | /*--------------------------------------------------------------------------*/ | 468 | /*--------------------------------------------------------------------------*/ |
426 | 469 | ||
427 | static int | 470 | /* Must be called with ts->lock held */ |
428 | ads7846_suspend(struct spi_device *spi, pm_message_t message) | 471 | static void ads7846_disable(struct ads7846 *ts) |
429 | { | 472 | { |
430 | struct ads7846 *ts = dev_get_drvdata(&spi->dev); | 473 | if (ts->disabled) |
431 | unsigned long flags; | 474 | return; |
432 | |||
433 | spin_lock_irqsave(&ts->lock, flags); | ||
434 | |||
435 | spi->dev.power.power_state = message; | ||
436 | 475 | ||
437 | /* are we waiting for IRQ, or polling? */ | 476 | /* are we waiting for IRQ, or polling? */ |
438 | if (!ts->pendown) { | 477 | if (!ts->pendown) { |
@@ -448,9 +487,9 @@ ads7846_suspend(struct spi_device *spi, pm_message_t message) | |||
448 | mod_timer(&ts->timer, jiffies); | 487 | mod_timer(&ts->timer, jiffies); |
449 | 488 | ||
450 | while (ts->pendown || ts->pending) { | 489 | while (ts->pendown || ts->pending) { |
451 | spin_unlock_irqrestore(&ts->lock, flags); | 490 | spin_unlock_irq(&ts->lock); |
452 | msleep(1); | 491 | msleep(1); |
453 | spin_lock_irqsave(&ts->lock, flags); | 492 | spin_lock_irq(&ts->lock); |
454 | } | 493 | } |
455 | } | 494 | } |
456 | 495 | ||
@@ -458,17 +497,46 @@ ads7846_suspend(struct spi_device *spi, pm_message_t message) | |||
458 | * leave it that way after every request | 497 | * leave it that way after every request |
459 | */ | 498 | */ |
460 | 499 | ||
461 | spin_unlock_irqrestore(&ts->lock, flags); | 500 | ts->disabled = 1; |
501 | } | ||
502 | |||
503 | /* Must be called with ts->lock held */ | ||
504 | static void ads7846_enable(struct ads7846 *ts) | ||
505 | { | ||
506 | if (!ts->disabled) | ||
507 | return; | ||
508 | |||
509 | ts->disabled = 0; | ||
510 | ts->irq_disabled = 0; | ||
511 | enable_irq(ts->spi->irq); | ||
512 | } | ||
513 | |||
514 | static int ads7846_suspend(struct spi_device *spi, pm_message_t message) | ||
515 | { | ||
516 | struct ads7846 *ts = dev_get_drvdata(&spi->dev); | ||
517 | |||
518 | spin_lock_irq(&ts->lock); | ||
519 | |||
520 | spi->dev.power.power_state = message; | ||
521 | ads7846_disable(ts); | ||
522 | |||
523 | spin_unlock_irq(&ts->lock); | ||
524 | |||
462 | return 0; | 525 | return 0; |
526 | |||
463 | } | 527 | } |
464 | 528 | ||
465 | static int ads7846_resume(struct spi_device *spi) | 529 | static int ads7846_resume(struct spi_device *spi) |
466 | { | 530 | { |
467 | struct ads7846 *ts = dev_get_drvdata(&spi->dev); | 531 | struct ads7846 *ts = dev_get_drvdata(&spi->dev); |
468 | 532 | ||
469 | ts->irq_disabled = 0; | 533 | spin_lock_irq(&ts->lock); |
470 | enable_irq(ts->spi->irq); | 534 | |
471 | spi->dev.power.power_state = PMSG_ON; | 535 | spi->dev.power.power_state = PMSG_ON; |
536 | ads7846_enable(ts); | ||
537 | |||
538 | spin_unlock_irq(&ts->lock); | ||
539 | |||
472 | return 0; | 540 | return 0; |
473 | } | 541 | } |
474 | 542 | ||
@@ -521,6 +589,8 @@ static int __devinit ads7846_probe(struct spi_device *spi) | |||
521 | ts->timer.data = (unsigned long) ts; | 589 | ts->timer.data = (unsigned long) ts; |
522 | ts->timer.function = ads7846_timer; | 590 | ts->timer.function = ads7846_timer; |
523 | 591 | ||
592 | spin_lock_init(&ts->lock); | ||
593 | |||
524 | ts->model = pdata->model ? : 7846; | 594 | ts->model = pdata->model ? : 7846; |
525 | ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; | 595 | ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; |
526 | ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; | 596 | ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; |
@@ -671,13 +741,25 @@ static int __devinit ads7846_probe(struct spi_device *spi) | |||
671 | 741 | ||
672 | device_create_file(&spi->dev, &dev_attr_pen_down); | 742 | device_create_file(&spi->dev, &dev_attr_pen_down); |
673 | 743 | ||
744 | device_create_file(&spi->dev, &dev_attr_disable); | ||
745 | |||
674 | err = input_register_device(input_dev); | 746 | err = input_register_device(input_dev); |
675 | if (err) | 747 | if (err) |
676 | goto err_free_irq; | 748 | goto err_remove_attr; |
677 | 749 | ||
678 | return 0; | 750 | return 0; |
679 | 751 | ||
680 | err_free_irq: | 752 | err_remove_attr: |
753 | device_remove_file(&spi->dev, &dev_attr_disable); | ||
754 | device_remove_file(&spi->dev, &dev_attr_pen_down); | ||
755 | if (ts->model == 7846) { | ||
756 | device_remove_file(&spi->dev, &dev_attr_temp1); | ||
757 | device_remove_file(&spi->dev, &dev_attr_temp0); | ||
758 | } | ||
759 | if (ts->model != 7845) | ||
760 | device_remove_file(&spi->dev, &dev_attr_vbatt); | ||
761 | device_remove_file(&spi->dev, &dev_attr_vaux); | ||
762 | |||
681 | free_irq(spi->irq, ts); | 763 | free_irq(spi->irq, ts); |
682 | err_free_mem: | 764 | err_free_mem: |
683 | input_free_device(input_dev); | 765 | input_free_device(input_dev); |
@@ -689,22 +771,24 @@ static int __devexit ads7846_remove(struct spi_device *spi) | |||
689 | { | 771 | { |
690 | struct ads7846 *ts = dev_get_drvdata(&spi->dev); | 772 | struct ads7846 *ts = dev_get_drvdata(&spi->dev); |
691 | 773 | ||
774 | input_unregister_device(ts->input); | ||
775 | |||
692 | ads7846_suspend(spi, PMSG_SUSPEND); | 776 | ads7846_suspend(spi, PMSG_SUSPEND); |
693 | free_irq(ts->spi->irq, ts); | ||
694 | if (ts->irq_disabled) | ||
695 | enable_irq(ts->spi->irq); | ||
696 | 777 | ||
778 | device_remove_file(&spi->dev, &dev_attr_disable); | ||
697 | device_remove_file(&spi->dev, &dev_attr_pen_down); | 779 | device_remove_file(&spi->dev, &dev_attr_pen_down); |
698 | |||
699 | if (ts->model == 7846) { | 780 | if (ts->model == 7846) { |
700 | device_remove_file(&spi->dev, &dev_attr_temp0); | ||
701 | device_remove_file(&spi->dev, &dev_attr_temp1); | 781 | device_remove_file(&spi->dev, &dev_attr_temp1); |
782 | device_remove_file(&spi->dev, &dev_attr_temp0); | ||
702 | } | 783 | } |
703 | if (ts->model != 7845) | 784 | if (ts->model != 7845) |
704 | device_remove_file(&spi->dev, &dev_attr_vbatt); | 785 | device_remove_file(&spi->dev, &dev_attr_vbatt); |
705 | device_remove_file(&spi->dev, &dev_attr_vaux); | 786 | device_remove_file(&spi->dev, &dev_attr_vaux); |
706 | 787 | ||
707 | input_unregister_device(ts->input); | 788 | free_irq(ts->spi->irq, ts); |
789 | if (ts->irq_disabled) | ||
790 | enable_irq(ts->spi->irq); | ||
791 | |||
708 | kfree(ts); | 792 | kfree(ts); |
709 | 793 | ||
710 | dev_dbg(&spi->dev, "unregistered touchscreen\n"); | 794 | dev_dbg(&spi->dev, "unregistered touchscreen\n"); |