#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];
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;
u8 disable_depth;
int hw_flags;
-
- s16 dav_gpio;
int irq;
};
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)
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;
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) {
} 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)
enable_irq(ts->irq);
tsc2301_ts_start_scan(tsc);
- /* Same workaround as above. */
- tsc2301_kp_restart(tsc);
}
#ifdef CONFIG_PM
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);
{
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);
}
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);
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;
}
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>");