]> err.no Git - linux-2.6/commitdiff
Input: ads7846 - debouncing and rudimentary sample filtering
authorImre Deak <imre.deak@nokia.com>
Wed, 12 Apr 2006 03:42:03 +0000 (23:42 -0400)
committerDmitry Torokhov <dtor_core@ameritech.net>
Wed, 12 Apr 2006 03:42:03 +0000 (23:42 -0400)
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>
drivers/input/touchscreen/ads7846.c
include/linux/spi/ads7846.h

index 54d4334778602df6b5ad4c1d36b3b1e17c696ee5..8670cd13bd5d6fa373d71d60f0e46288aa5b4618 100644 (file)
@@ -77,7 +77,13 @@ struct ads7846 {
        struct ts_event         tc;
 
        struct spi_transfer     xfer[10];
-       struct spi_message      msg;
+       struct spi_message      msg[5];
+       int                     msg_idx;
+       int                     read_cnt;
+       int                     last_read;
+
+       u16                     debounce_max;
+       u16                     debounce_tol;
 
        spinlock_t              lock;
        struct timer_list       timer;          /* P: lock */
@@ -167,7 +173,7 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
        if (!req)
                return -ENOMEM;
 
-       INIT_LIST_HEAD(&req->msg.transfers);
+       spi_message_init(&req->msg);
 
        /* activate reference, so it has time to settle; */
        req->ref_on = REF_ON;
@@ -344,31 +350,76 @@ static void ads7846_rx(void *ads)
        spin_unlock_irqrestore(&ts->lock, flags);
 }
 
+static void ads7846_debounce(void *ads)
+{
+       struct ads7846          *ts = ads;
+       struct spi_message      *m;
+       struct spi_transfer     *t;
+       u16                     val;
+       int                     status;
+
+       m = &ts->msg[ts->msg_idx];
+       t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
+       val = (*(u16 *)t->rx_buf) >> 3;
+
+       if (!ts->read_cnt || (abs(ts->last_read - val) > ts->debounce_tol
+                               && ts->read_cnt < ts->debounce_max)) {
+               /* Repeat it, if this was the first read or the read wasn't
+                * consistent enough
+                */
+               ts->read_cnt++;
+               ts->last_read = val;
+       } else {
+               /* Go for the next read */
+               ts->msg_idx++;
+               ts->read_cnt = 0;
+               m++;
+       }
+       status = spi_async(ts->spi, m);
+       if (status)
+               dev_err(&ts->spi->dev, "spi_async --> %d\n",
+                               status);
+}
+
 static void ads7846_timer(unsigned long handle)
 {
        struct ads7846  *ts = (void *)handle;
        int             status = 0;
-       unsigned long   flags;
+
+       ts->msg_idx = 0;
+       status = spi_async(ts->spi, &ts->msg[0]);
+       if (status)
+               dev_err(&ts->spi->dev, "spi_async --> %d\n", status);
+}
+
+static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs)
+{
+       struct ads7846 *ts = handle;
+       unsigned long flags;
+       int r = IRQ_HANDLED;
 
        spin_lock_irqsave(&ts->lock, flags);
-       if (!ts->pending) {
-               ts->pending = 1;
+       if (ts->irq_disabled)
+               r = IRQ_HANDLED;
+       else {
                if (!ts->irq_disabled) {
+                       /* REVISIT irq logic for many ARM chips has cloned a
+                        * bug wherein disabling an irq in its handler won't
+                        * work;(it's disabled lazily, and too late to work.
+                        * until all their irq logic is fixed, we must shadow
+                        * that state here.
+                        */
                        ts->irq_disabled = 1;
+
                        disable_irq(ts->spi->irq);
                }
-               status = spi_async(ts->spi, &ts->msg);
-               if (status)
-                       dev_err(&ts->spi->dev, "spi_async --> %d\n",
-                                       status);
+               if (!ts->pending) {
+                       ts->pending = 1;
+                       mod_timer(&ts->timer, jiffies);
+               }
        }
        spin_unlock_irqrestore(&ts->lock, flags);
-}
-
-static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs)
-{
-       ads7846_timer((unsigned long) handle);
-       return IRQ_HANDLED;
+       return r;
 }
 
 /*--------------------------------------------------------------------------*/
@@ -426,6 +477,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
        struct ads7846                  *ts;
        struct input_dev                *input_dev;
        struct ads7846_platform_data    *pdata = spi->dev.platform_data;
+       struct spi_message              *m;
        struct spi_transfer             *x;
        int                             err;
 
@@ -472,6 +524,8 @@ static int __devinit ads7846_probe(struct spi_device *spi)
        ts->model = pdata->model ? : 7846;
        ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;
        ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
+       ts->debounce_max = pdata->debounce_max ? : 1;
+       ts->debounce_tol = pdata->debounce_tol ? : 10;
 
        snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id);
 
@@ -495,72 +549,98 @@ static int __devinit ads7846_probe(struct spi_device *spi)
        /* set up the transfers to read touchscreen state; this assumes we
         * use formula #2 for pressure, not #3.
         */
-       INIT_LIST_HEAD(&ts->msg.transfers);
+       m = &ts->msg[0];
        x = ts->xfer;
 
+       spi_message_init(m);
+
        /* y- still on; turn on only y+ (and ADC) */
        ts->read_y = READ_Y;
        x->tx_buf = &ts->read_y;
        x->len = 1;
-       spi_message_add_tail(x, &ts->msg);
+       spi_message_add_tail(x, m);
 
        x++;
        x->rx_buf = &ts->tc.y;
        x->len = 2;
-       spi_message_add_tail(x, &ts->msg);
+       spi_message_add_tail(x, m);
+
+       m->complete = ads7846_debounce;
+       m->context = ts;
+
+       m++;
+       spi_message_init(m);
+
+       /* turn y- off, x+ on, then leave in lowpower */
+       x++;
+       ts->read_x = READ_X;
+       x->tx_buf = &ts->read_x;
+       x->len = 1;
+       spi_message_add_tail(x, m);
+
+       x++;
+       x->rx_buf = &ts->tc.x;
+       x->len = 2;
+       spi_message_add_tail(x, m);
+
+       m->complete = ads7846_debounce;
+       m->context = ts;
 
        /* turn y+ off, x- on; we'll use formula #2 */
        if (ts->model == 7846) {
+               m++;
+               spi_message_init(m);
+
                x++;
                ts->read_z1 = READ_Z1;
                x->tx_buf = &ts->read_z1;
                x->len = 1;
-               spi_message_add_tail(x, &ts->msg);
+               spi_message_add_tail(x, m);
 
                x++;
                x->rx_buf = &ts->tc.z1;
                x->len = 2;
-               spi_message_add_tail(x, &ts->msg);
+               spi_message_add_tail(x, m);
+
+               m->complete = ads7846_debounce;
+               m->context = ts;
+
+               m++;
+               spi_message_init(m);
 
                x++;
                ts->read_z2 = READ_Z2;
                x->tx_buf = &ts->read_z2;
                x->len = 1;
-               spi_message_add_tail(x, &ts->msg);
+               spi_message_add_tail(x, m);
 
                x++;
                x->rx_buf = &ts->tc.z2;
                x->len = 2;
-               spi_message_add_tail(x, &ts->msg);
-       }
+               spi_message_add_tail(x, m);
 
-       /* turn y- off, x+ on, then leave in lowpower */
-       x++;
-       ts->read_x = READ_X;
-       x->tx_buf = &ts->read_x;
-       x->len = 1;
-       spi_message_add_tail(x, &ts->msg);
-
-       x++;
-       x->rx_buf = &ts->tc.x;
-       x->len = 2;
-       spi_message_add_tail(x, &ts->msg);
+               m->complete = ads7846_debounce;
+               m->context = ts;
+       }
 
        /* power down */
+       m++;
+       spi_message_init(m);
+
        x++;
        ts->pwrdown = PWRDOWN;
        x->tx_buf = &ts->pwrdown;
        x->len = 1;
-       spi_message_add_tail(x, &ts->msg);
+       spi_message_add_tail(x, m);
 
        x++;
        x->rx_buf = &ts->dummy;
        x->len = 2;
        CS_CHANGE(*x);
-       spi_message_add_tail(x, &ts->msg);
+       spi_message_add_tail(x, m);
 
-       ts->msg.complete = ads7846_rx;
-       ts->msg.context = ts;
+       m->complete = ads7846_rx;
+       m->context = ts;
 
        if (request_irq(spi->irq, ads7846_irq,
                        SA_SAMPLE_RANDOM | SA_TRIGGER_FALLING,
index 72261e0f2ac19f29932ef36a154ae273a680eca2..3f76649512564de13bafbef74a3b3ea5df53c54e 100644 (file)
@@ -14,5 +14,8 @@ struct ads7846_platform_data {
        u16     x_min, x_max;
        u16     y_min, y_max;
        u16     pressure_min, pressure_max;
+
+       u16     debounce_max;           /* max number of readings per sample */
+       u16     debounce_tol;           /* tolerance used for filtering */
 };