diff options
author | Imre Deak <imre.deak@nokia.com> | 2006-04-11 23:42:03 -0400 |
---|---|---|
committer | Dmitry Torokhov <dtor_core@ameritech.net> | 2006-04-11 23:42:03 -0400 |
commit | 0b7018aae7e1798f55f736b9a77c201708aa0e33 (patch) | |
tree | b561dbcdb07540ffc76616894dddfa8f787f9202 | |
parent | 53a0ef89e95c725f3faab98573770aeb7429c1a3 (diff) |
Input: ads7846 - debouncing and rudimentary sample filtering
Some touchscreens seem to oscillate heavily for a while after touching
the screen. Implement support for sampling the screen until we get two
consecutive values that are close enough.
Signed-off-by: Imre Deak <imre.deak@nokia.com>
Signed-off-by: Juha Yrjola <juha.yrjola@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 | 156 | ||||
-rw-r--r-- | include/linux/spi/ads7846.h | 3 |
2 files changed, 121 insertions, 38 deletions
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 54d433477860..8670cd13bd5d 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c | |||
@@ -77,7 +77,13 @@ struct ads7846 { | |||
77 | struct ts_event tc; | 77 | struct ts_event tc; |
78 | 78 | ||
79 | struct spi_transfer xfer[10]; | 79 | struct spi_transfer xfer[10]; |
80 | struct spi_message msg; | 80 | struct spi_message msg[5]; |
81 | int msg_idx; | ||
82 | int read_cnt; | ||
83 | int last_read; | ||
84 | |||
85 | u16 debounce_max; | ||
86 | u16 debounce_tol; | ||
81 | 87 | ||
82 | spinlock_t lock; | 88 | spinlock_t lock; |
83 | struct timer_list timer; /* P: lock */ | 89 | struct timer_list timer; /* P: lock */ |
@@ -167,7 +173,7 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) | |||
167 | if (!req) | 173 | if (!req) |
168 | return -ENOMEM; | 174 | return -ENOMEM; |
169 | 175 | ||
170 | INIT_LIST_HEAD(&req->msg.transfers); | 176 | spi_message_init(&req->msg); |
171 | 177 | ||
172 | /* activate reference, so it has time to settle; */ | 178 | /* activate reference, so it has time to settle; */ |
173 | req->ref_on = REF_ON; | 179 | req->ref_on = REF_ON; |
@@ -344,31 +350,76 @@ static void ads7846_rx(void *ads) | |||
344 | spin_unlock_irqrestore(&ts->lock, flags); | 350 | spin_unlock_irqrestore(&ts->lock, flags); |
345 | } | 351 | } |
346 | 352 | ||
353 | static void ads7846_debounce(void *ads) | ||
354 | { | ||
355 | struct ads7846 *ts = ads; | ||
356 | struct spi_message *m; | ||
357 | struct spi_transfer *t; | ||
358 | u16 val; | ||
359 | int status; | ||
360 | |||
361 | m = &ts->msg[ts->msg_idx]; | ||
362 | t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); | ||
363 | val = (*(u16 *)t->rx_buf) >> 3; | ||
364 | |||
365 | if (!ts->read_cnt || (abs(ts->last_read - val) > ts->debounce_tol | ||
366 | && ts->read_cnt < ts->debounce_max)) { | ||
367 | /* Repeat it, if this was the first read or the read wasn't | ||
368 | * consistent enough | ||
369 | */ | ||
370 | ts->read_cnt++; | ||
371 | ts->last_read = val; | ||
372 | } else { | ||
373 | /* Go for the next read */ | ||
374 | ts->msg_idx++; | ||
375 | ts->read_cnt = 0; | ||
376 | m++; | ||
377 | } | ||
378 | status = spi_async(ts->spi, m); | ||
379 | if (status) | ||
380 | dev_err(&ts->spi->dev, "spi_async --> %d\n", | ||
381 | status); | ||
382 | } | ||
383 | |||
347 | static void ads7846_timer(unsigned long handle) | 384 | static void ads7846_timer(unsigned long handle) |
348 | { | 385 | { |
349 | struct ads7846 *ts = (void *)handle; | 386 | struct ads7846 *ts = (void *)handle; |
350 | int status = 0; | 387 | int status = 0; |
351 | unsigned long flags; | 388 | |
389 | ts->msg_idx = 0; | ||
390 | status = spi_async(ts->spi, &ts->msg[0]); | ||
391 | if (status) | ||
392 | dev_err(&ts->spi->dev, "spi_async --> %d\n", status); | ||
393 | } | ||
394 | |||
395 | static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs) | ||
396 | { | ||
397 | struct ads7846 *ts = handle; | ||
398 | unsigned long flags; | ||
399 | int r = IRQ_HANDLED; | ||
352 | 400 | ||
353 | spin_lock_irqsave(&ts->lock, flags); | 401 | spin_lock_irqsave(&ts->lock, flags); |
354 | if (!ts->pending) { | 402 | if (ts->irq_disabled) |
355 | ts->pending = 1; | 403 | r = IRQ_HANDLED; |
404 | else { | ||
356 | if (!ts->irq_disabled) { | 405 | if (!ts->irq_disabled) { |
406 | /* REVISIT irq logic for many ARM chips has cloned a | ||
407 | * bug wherein disabling an irq in its handler won't | ||
408 | * work;(it's disabled lazily, and too late to work. | ||
409 | * until all their irq logic is fixed, we must shadow | ||
410 | * that state here. | ||
411 | */ | ||
357 | ts->irq_disabled = 1; | 412 | ts->irq_disabled = 1; |
413 | |||
358 | disable_irq(ts->spi->irq); | 414 | disable_irq(ts->spi->irq); |
359 | } | 415 | } |
360 | status = spi_async(ts->spi, &ts->msg); | 416 | if (!ts->pending) { |
361 | if (status) | 417 | ts->pending = 1; |
362 | dev_err(&ts->spi->dev, "spi_async --> %d\n", | 418 | mod_timer(&ts->timer, jiffies); |
363 | status); | 419 | } |
364 | } | 420 | } |
365 | spin_unlock_irqrestore(&ts->lock, flags); | 421 | spin_unlock_irqrestore(&ts->lock, flags); |
366 | } | 422 | return r; |
367 | |||
368 | static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs) | ||
369 | { | ||
370 | ads7846_timer((unsigned long) handle); | ||
371 | return IRQ_HANDLED; | ||
372 | } | 423 | } |
373 | 424 | ||
374 | /*--------------------------------------------------------------------------*/ | 425 | /*--------------------------------------------------------------------------*/ |
@@ -426,6 +477,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) | |||
426 | struct ads7846 *ts; | 477 | struct ads7846 *ts; |
427 | struct input_dev *input_dev; | 478 | struct input_dev *input_dev; |
428 | struct ads7846_platform_data *pdata = spi->dev.platform_data; | 479 | struct ads7846_platform_data *pdata = spi->dev.platform_data; |
480 | struct spi_message *m; | ||
429 | struct spi_transfer *x; | 481 | struct spi_transfer *x; |
430 | int err; | 482 | int err; |
431 | 483 | ||
@@ -472,6 +524,8 @@ static int __devinit ads7846_probe(struct spi_device *spi) | |||
472 | ts->model = pdata->model ? : 7846; | 524 | ts->model = pdata->model ? : 7846; |
473 | ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; | 525 | ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; |
474 | ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; | 526 | ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; |
527 | ts->debounce_max = pdata->debounce_max ? : 1; | ||
528 | ts->debounce_tol = pdata->debounce_tol ? : 10; | ||
475 | 529 | ||
476 | snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id); | 530 | snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id); |
477 | 531 | ||
@@ -495,72 +549,98 @@ static int __devinit ads7846_probe(struct spi_device *spi) | |||
495 | /* set up the transfers to read touchscreen state; this assumes we | 549 | /* set up the transfers to read touchscreen state; this assumes we |
496 | * use formula #2 for pressure, not #3. | 550 | * use formula #2 for pressure, not #3. |
497 | */ | 551 | */ |
498 | INIT_LIST_HEAD(&ts->msg.transfers); | 552 | m = &ts->msg[0]; |
499 | x = ts->xfer; | 553 | x = ts->xfer; |
500 | 554 | ||
555 | spi_message_init(m); | ||
556 | |||
501 | /* y- still on; turn on only y+ (and ADC) */ | 557 | /* y- still on; turn on only y+ (and ADC) */ |
502 | ts->read_y = READ_Y; | 558 | ts->read_y = READ_Y; |
503 | x->tx_buf = &ts->read_y; | 559 | x->tx_buf = &ts->read_y; |
504 | x->len = 1; | 560 | x->len = 1; |
505 | spi_message_add_tail(x, &ts->msg); | 561 | spi_message_add_tail(x, m); |
506 | 562 | ||
507 | x++; | 563 | x++; |
508 | x->rx_buf = &ts->tc.y; | 564 | x->rx_buf = &ts->tc.y; |
509 | x->len = 2; | 565 | x->len = 2; |
510 | spi_message_add_tail(x, &ts->msg); | 566 | spi_message_add_tail(x, m); |
567 | |||
568 | m->complete = ads7846_debounce; | ||
569 | m->context = ts; | ||
570 | |||
571 | m++; | ||
572 | spi_message_init(m); | ||
573 | |||
574 | /* turn y- off, x+ on, then leave in lowpower */ | ||
575 | x++; | ||
576 | ts->read_x = READ_X; | ||
577 | x->tx_buf = &ts->read_x; | ||
578 | x->len = 1; | ||
579 | spi_message_add_tail(x, m); | ||
580 | |||
581 | x++; | ||
582 | x->rx_buf = &ts->tc.x; | ||
583 | x->len = 2; | ||
584 | spi_message_add_tail(x, m); | ||
585 | |||
586 | m->complete = ads7846_debounce; | ||
587 | m->context = ts; | ||
511 | 588 | ||
512 | /* turn y+ off, x- on; we'll use formula #2 */ | 589 | /* turn y+ off, x- on; we'll use formula #2 */ |
513 | if (ts->model == 7846) { | 590 | if (ts->model == 7846) { |
591 | m++; | ||
592 | spi_message_init(m); | ||
593 | |||
514 | x++; | 594 | x++; |
515 | ts->read_z1 = READ_Z1; | 595 | ts->read_z1 = READ_Z1; |
516 | x->tx_buf = &ts->read_z1; | 596 | x->tx_buf = &ts->read_z1; |
517 | x->len = 1; | 597 | x->len = 1; |
518 | spi_message_add_tail(x, &ts->msg); | 598 | spi_message_add_tail(x, m); |
519 | 599 | ||
520 | x++; | 600 | x++; |
521 | x->rx_buf = &ts->tc.z1; | 601 | x->rx_buf = &ts->tc.z1; |
522 | x->len = 2; | 602 | x->len = 2; |
523 | spi_message_add_tail(x, &ts->msg); | 603 | spi_message_add_tail(x, m); |
604 | |||
605 | m->complete = ads7846_debounce; | ||
606 | m->context = ts; | ||
607 | |||
608 | m++; | ||
609 | spi_message_init(m); | ||
524 | 610 | ||
525 | x++; | 611 | x++; |
526 | ts->read_z2 = READ_Z2; | 612 | ts->read_z2 = READ_Z2; |
527 | x->tx_buf = &ts->read_z2; | 613 | x->tx_buf = &ts->read_z2; |
528 | x->len = 1; | 614 | x->len = 1; |
529 | spi_message_add_tail(x, &ts->msg); | 615 | spi_message_add_tail(x, m); |
530 | 616 | ||
531 | x++; | 617 | x++; |
532 | x->rx_buf = &ts->tc.z2; | 618 | x->rx_buf = &ts->tc.z2; |
533 | x->len = 2; | 619 | x->len = 2; |
534 | spi_message_add_tail(x, &ts->msg); | 620 | spi_message_add_tail(x, m); |
535 | } | ||
536 | 621 | ||
537 | /* turn y- off, x+ on, then leave in lowpower */ | 622 | m->complete = ads7846_debounce; |
538 | x++; | 623 | m->context = ts; |
539 | ts->read_x = READ_X; | 624 | } |
540 | x->tx_buf = &ts->read_x; | ||
541 | x->len = 1; | ||
542 | spi_message_add_tail(x, &ts->msg); | ||
543 | |||
544 | x++; | ||
545 | x->rx_buf = &ts->tc.x; | ||
546 | x->len = 2; | ||
547 | spi_message_add_tail(x, &ts->msg); | ||
548 | 625 | ||
549 | /* power down */ | 626 | /* power down */ |
627 | m++; | ||
628 | spi_message_init(m); | ||
629 | |||
550 | x++; | 630 | x++; |
551 | ts->pwrdown = PWRDOWN; | 631 | ts->pwrdown = PWRDOWN; |
552 | x->tx_buf = &ts->pwrdown; | 632 | x->tx_buf = &ts->pwrdown; |
553 | x->len = 1; | 633 | x->len = 1; |
554 | spi_message_add_tail(x, &ts->msg); | 634 | spi_message_add_tail(x, m); |
555 | 635 | ||
556 | x++; | 636 | x++; |
557 | x->rx_buf = &ts->dummy; | 637 | x->rx_buf = &ts->dummy; |
558 | x->len = 2; | 638 | x->len = 2; |
559 | CS_CHANGE(*x); | 639 | CS_CHANGE(*x); |
560 | spi_message_add_tail(x, &ts->msg); | 640 | spi_message_add_tail(x, m); |
561 | 641 | ||
562 | ts->msg.complete = ads7846_rx; | 642 | m->complete = ads7846_rx; |
563 | ts->msg.context = ts; | 643 | m->context = ts; |
564 | 644 | ||
565 | if (request_irq(spi->irq, ads7846_irq, | 645 | if (request_irq(spi->irq, ads7846_irq, |
566 | SA_SAMPLE_RANDOM | SA_TRIGGER_FALLING, | 646 | SA_SAMPLE_RANDOM | SA_TRIGGER_FALLING, |
diff --git a/include/linux/spi/ads7846.h b/include/linux/spi/ads7846.h index 72261e0f2ac1..3f7664951256 100644 --- a/include/linux/spi/ads7846.h +++ b/include/linux/spi/ads7846.h | |||
@@ -14,5 +14,8 @@ struct ads7846_platform_data { | |||
14 | u16 x_min, x_max; | 14 | u16 x_min, x_max; |
15 | u16 y_min, y_max; | 15 | u16 y_min, y_max; |
16 | u16 pressure_min, pressure_max; | 16 | u16 pressure_min, pressure_max; |
17 | |||
18 | u16 debounce_max; /* max number of readings per sample */ | ||
19 | u16 debounce_tol; /* tolerance used for filtering */ | ||
17 | }; | 20 | }; |
18 | 21 | ||