]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/input/touchscreen/tsc2301_ts.c
Merge current mainline tree into linux-omap tree
[linux-2.6-omap-h63xx.git] / drivers / input / touchscreen / tsc2301_ts.c
index 148d8be02bb9b27ad7a41323fc08f3bc3bc5518a..a157b48dd0b8638a9187ed950be8403a12242d84 100644 (file)
 #include <linux/delay.h>
 #include <linux/spi/spi.h>
 
-#ifdef CONFIG_ARCH_OMAP
-#include <asm/arch/gpio.h>
-#endif
-
 #include <linux/spi/tsc2301.h>
 
 /**
 
 #define MAX_12BIT                                      ((1 << 12) - 1)
 
+#define TS_RECT_SIZE                                   8
+#define TSF_MIN_Z1                                     100
+#define TSF_MAX_Z2                                     4000
+
+#define TSF_SAMPLES                                    4
+
+struct ts_filter {
+       int                     sample_cnt;
+
+       int                     avg_x;
+       int                     avg_y;
+       int                     avg_z1;
+       int                     avg_z2;
+};
+
+struct ts_coords {
+       u16                     x;
+       u16                     y;
+       u16                     z1;
+       u16                     z2;
+};
+
 struct tsc2301_ts {
        struct input_dev        *idev;
        char                    phys[32];
@@ -108,13 +126,14 @@ struct tsc2301_ts {
 
        struct spi_transfer     read_xfer[2];
        struct spi_message      read_msg;
-       u16                     data[4];
+       struct ts_coords        *coords;
+
+       struct ts_filter        filter;
 
        int                     hw_avg_max;
        u16                     x;
        u16                     y;
        u16                     p;
-       int                     sample_cnt;
 
        u16                     x_plate_ohm;
        int                     stab_time;
@@ -127,8 +146,6 @@ struct tsc2301_ts {
        u8                      disable_depth;
 
        int                     hw_flags;
-
-       s16                     dav_gpio;
        int                     irq;
 };
 
@@ -259,11 +276,13 @@ static int tsc2301_ts_configure(struct tsc2301 *tsc, int flags)
 static void tsc2301_ts_start_scan(struct tsc2301 *tsc)
 {
        tsc2301_ts_configure(tsc, tsc->ts->hw_flags);
+       tsc2301_kp_restart(tsc);
 }
 
 static void tsc2301_ts_stop_scan(struct tsc2301 *tsc)
 {
        tsc2301_write_reg(tsc, TSC2301_REG_ADC, TSC2301_ADCREG_STOP_CONVERSION);
+       tsc2301_kp_restart(tsc);
 }
 
 static void update_pen_state(struct tsc2301_ts *ts, int x, int y, int pressure)
@@ -291,25 +310,58 @@ static void update_pen_state(struct tsc2301_ts *ts, int x, int y, int pressure)
 
 static int filter(struct tsc2301_ts *ts, int x, int y, int z1, int z2)
 {
-       int pressure, pressure_limit;
-
-       if (z1) {
-               pressure = ts->x_plate_ohm * x;
-               pressure /= 4096;
-               pressure *= z2 - z1;
-               pressure /= z1;
-       } else
-               pressure = 0;
-
-       /* If pressure value is above a preset limit (pen is barely
-        * touching the screen) we can't trust the coordinate values.
-        */
+       int inside_rect, pressure_limit, Rt;
+       struct ts_filter *tsf = &ts->filter;
+
+       /* validate pressure and position */
+       if (x > MAX_12BIT || y > MAX_12BIT)
+               return 0;
+
+       /* skip coords if the pressure-components are out of range */
+       if (z1 < TSF_MIN_Z1 || z2 > TSF_MAX_Z2)
+               return 0;
+
+       /* Use the x,y,z1,z2 directly on the first "pen down" event */
+       if (ts->event_sent) {
+               tsf->avg_x  += x;
+               tsf->avg_y  += y;
+               tsf->avg_z1 += z1;
+               tsf->avg_z2 += z2;
+
+               if (++tsf->sample_cnt < TSF_SAMPLES)
+                       return 0;
+               x = tsf->avg_x / TSF_SAMPLES;
+               y = tsf->avg_y / TSF_SAMPLES;
+               z1 = tsf->avg_z1 / TSF_SAMPLES;
+               z2 = tsf->avg_z2 / TSF_SAMPLES;
+       }
+       tsf->sample_cnt = 0;
+       tsf->avg_x  = 0;
+       tsf->avg_y  = 0;
+       tsf->avg_z1 = 0;
+       tsf->avg_z2 = 0;
+
        pressure_limit = ts->event_sent? ts->max_pressure: ts->touch_pressure;
 
-       if (pressure < pressure_limit && x < MAX_12BIT && y < MAX_12BIT) {
+       /* z1 is always at least 100: */
+       Rt = x * (z2 - z1) / z1;
+       Rt = Rt * ts->x_plate_ohm / 4096;
+       if (Rt > pressure_limit)
+               return 0;
+
+       /* discard the event if it still is within the previous rect - unless
+        * if the pressure is harder, but then use previous x,y position */
+       inside_rect = (
+           x > (int)ts->x - TS_RECT_SIZE && x < (int)ts->x + TS_RECT_SIZE &&
+           y > (int)ts->y - TS_RECT_SIZE && y < (int)ts->y + TS_RECT_SIZE);
+
+       if (!ts->event_sent || !inside_rect) {
                ts->x = x;
                ts->y = y;
-               ts->p = pressure;
+               ts->p = Rt;
+               return 1;
+       } else if (Rt < ts->p) {
+               ts->p = Rt;
                return 1;
        }
        return 0;
@@ -326,10 +378,10 @@ static void tsc2301_ts_rx(void *arg)
        int send_event;
        int x, y, z1, z2;
 
-       x  = ts->data[0];
-       y  = ts->data[1];
-       z1 = ts->data[2];
-       z2 = ts->data[3];
+       x  = ts->coords->x;
+       y  = ts->coords->y;
+       z1 = ts->coords->z1;
+       z2 = ts->coords->z2;
 
        send_event = filter(ts, x, y, z1, z2);
        if (send_event) {
@@ -390,10 +442,6 @@ static void tsc2301_ts_disable(struct tsc2301 *tsc)
        } while (ts->event_sent);
 
        tsc2301_ts_stop_scan(tsc);
-       /* Workaround a bug where turning on / off touchscreen scanner
-        * can get the keypad scanner stuck.
-        */
-       tsc2301_kp_restart(tsc);
 }
 
 static void tsc2301_ts_enable(struct tsc2301 *tsc)
@@ -406,8 +454,6 @@ static void tsc2301_ts_enable(struct tsc2301 *tsc)
        enable_irq(ts->irq);
 
        tsc2301_ts_start_scan(tsc);
-       /* Same workaround as above. */
-       tsc2301_kp_restart(tsc);
 }
 
 #ifdef CONFIG_PM
@@ -445,7 +491,7 @@ static void tsc2301_ts_setup_spi_xfer(struct tsc2301 *tsc)
        spi_message_add_tail(x, m);
 
        x++;
-       x->rx_buf = &ts->data;
+       x->rx_buf = ts->coords;
        x->len = 8;
        spi_message_add_tail(x, m);
 
@@ -505,31 +551,28 @@ int __devinit tsc2301_ts_init(struct tsc2301 *tsc,
 {
        struct tsc2301_ts *ts;
        struct input_dev *idev;
-       int dav_gpio, r;
+       int r;
        int x_max, y_max;
        int x_fudge, y_fudge, p_fudge;
 
-       if (pdata->dav_gpio < 0) {
-               dev_err(&tsc->spi->dev, "need DAV GPIO");
+       if (pdata->dav_int <= 0) {
+               dev_err(&tsc->spi->dev, "need DAV IRQ");
                return -EINVAL;
        }
-       dav_gpio = pdata->dav_gpio;
 
        ts = kzalloc(sizeof(*ts), GFP_KERNEL);
        if (ts == NULL)
                return -ENOMEM;
        tsc->ts = ts;
 
-       ts->dav_gpio = dav_gpio;
-#ifdef CONFIG_ARCH_OMAP
-       r = omap_request_gpio(dav_gpio);
-       if (r < 0) {
-               dev_err(&tsc->spi->dev, "unable to get DAV GPIO");
-               goto err1;
+       ts->coords = kzalloc(sizeof(*ts->coords), GFP_KERNEL);
+       if (ts->coords == NULL) {
+               kfree(ts);
+               return -ENOMEM;
        }
-       omap_set_gpio_direction(dav_gpio, 1);
-       ts->irq = OMAP_GPIO_IRQ(dav_gpio);
-#endif
+
+       ts->irq = pdata->dav_int;
+
        init_timer(&ts->penup_timer);
        setup_timer(&ts->penup_timer, tsc2301_ts_timer_handler,
                        (unsigned long)tsc);
@@ -560,8 +603,9 @@ int __devinit tsc2301_ts_init(struct tsc2301 *tsc,
        }
        idev->name = "TSC2301 touchscreen";
        snprintf(ts->phys, sizeof(ts->phys),
-                "%s/input-ts", tsc->spi->dev.bus_id);
+                "%s/input-ts", dev_name(&tsc->spi->dev));
        idev->phys = ts->phys;
+       idev->dev.parent = &tsc->spi->dev;
 
        idev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
        idev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
@@ -608,10 +652,7 @@ err3:
        tsc2301_ts_stop_scan(tsc);
        input_free_device(idev);
 err2:
-#ifdef CONFIG_ARCH_OMAP
-       omap_free_gpio(dav_gpio);
-#endif
-err1:
+       kfree(ts->coords);
        kfree(ts);
        return r;
 }
@@ -628,9 +669,7 @@ void __devexit tsc2301_ts_exit(struct tsc2301 *tsc)
        free_irq(ts->irq, tsc);
        input_unregister_device(ts->idev);
 
-#ifdef CONFIG_ARCH_OMAP
-       omap_free_gpio(ts->dav_gpio);
-#endif
+       kfree(ts->coords);
        kfree(ts);
 }
 MODULE_AUTHOR("Jarkko Oikarinen <jarkko.oikarinen@nokia.com>");