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 /drivers/input | |
| 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>
Diffstat (limited to 'drivers/input')
| -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"); |
