diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/input/touchscreen/s3c2410_ts.c | 114 |
1 files changed, 97 insertions, 17 deletions
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index 5a31c4f250c..a880163059a 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -102,6 +102,9 @@ static char *s3c2410ts_name = "s3c2410 TouchScreen"; #define TS_STATE_RELEASE_PENDING 3 #define TS_STATE_RELEASE 4 +#define SKIP_NHEAD 2 +#define SKIP_NTAIL 2 + /* * Per-touchscreen data. */ @@ -119,6 +122,9 @@ static struct s3c2410ts ts; static void __iomem *base_addr; +/* + * A few low level functions. + */ static inline void s3c2410_ts_connect(void) { @@ -140,18 +146,18 @@ static void s3c2410_ts_start_adc_conversion(void) * Just send the input events. */ -enum ts_input_event {IE_DOWN = 0, IE_UP, IE_UPDATE}; +enum ts_input_event {IE_DOWN = 0, IE_UP}; static void ts_input_report(int event, int coords[]) { #ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG - static char *s[] = {"down", "up", "update"}; + static char *s[] = {"down", "up"}; struct timeval tv; do_gettimeofday(&tv); #endif - if (event == IE_DOWN || event == IE_UPDATE) { + if (event == IE_DOWN) { input_report_abs(ts.dev, ABS_X, coords[0]); input_report_abs(ts.dev, ABS_Y, coords[1]); input_report_key(ts.dev, BTN_TOUCH, 1); @@ -174,10 +180,93 @@ static void ts_input_report(int event, int coords[]) input_sync(ts.dev); } + /* - * Manage state of the touchscreen and send events. + * Skip filter for touchscreen values. + * + * Problem: The first and the last sample might be unreliable. We provide + * this filter as a separate function in order to keep the event_send_timer_f + * function simple. This filter: + * + * - Skips NHEAD points after IE_DOWN + * - Skips NTAIL points before IE_UP + * - Ignores a click if we have less than (NHEAD + NTAILl + 1) points */ +struct skip_filter_event { + int coords[2]; +}; + +struct skip_filter { + unsigned N; + unsigned M; + int sent; + struct skip_filter_event buf[SKIP_NTAIL]; +}; + +struct skip_filter ts_skip; + +static void ts_skip_filter_reset(void) +{ + ts_skip.N = 0; + ts_skip.M = 0; + ts_skip.sent = 0; +} + +static void ts_skip_filter(int event, int coords[]) +{ + /* skip the first N samples */ + if (ts_skip.N < SKIP_NHEAD) { + if (IE_UP == event) + ts_skip_filter_reset(); + else + ts_skip.N++; + return; + } + + /* We didn't send DOWN -- Ignore UP */ + if (IE_UP == event && !ts_skip.sent) { + ts_skip_filter_reset(); + return; + } + + /* Just accept the event if NTAIL == 0 */ + if (!SKIP_NTAIL) { + ts_input_report(event, coords); + if (IE_UP == event) + ts_skip_filter_reset(); + else + ts_skip.sent = 1; + return; + } + + /* NTAIL > 0, Queue current point if we need to */ + if (!ts_skip.sent && ts_skip.M < SKIP_NTAIL) { + memcpy(&ts_skip.buf[ts_skip.M++].coords[0], &coords[0], + sizeof(int) * 2); + return; + } + + /* queue full: accept one, queue one */ + + if (ts_skip.M >= SKIP_NTAIL) + ts_skip.M = 0; + + ts_input_report(event, ts_skip.buf[ts_skip.M].coords); + + if (event == IE_UP) { + ts_skip_filter_reset(); + } else { + memcpy(&ts_skip.buf[ts_skip.M++].coords[0], &coords[0], + sizeof(int) * 2); + ts_skip.sent = 1; + } +} + + +/* + * Manage the state of the touchscreen. Send events to the skip filter. + */ static void event_send_timer_f(unsigned long data); @@ -189,7 +278,6 @@ static void event_send_timer_f(unsigned long data) static unsigned long running; static int noop_counter; int event_type; - static unsigned n_points; if (unlikely(test_and_set_bit(0, &running))) { mod_timer(&event_send_timer, @@ -225,14 +313,7 @@ static void event_send_timer_f(unsigned long data) != sizeof(int) * 2)) goto ts_exit_error; - if (n_points++ < 2) - break; - - if (ts.state == TS_STATE_PRESSED_PENDING) - ts_input_report(IE_DOWN, buf); - else - ts_input_report(IE_UPDATE, buf); - + ts_skip_filter(IE_DOWN, buf); ts.state = TS_STATE_PRESSED; break; @@ -251,11 +332,8 @@ static void event_send_timer_f(unsigned long data) * while to avoid jitter. If we get a DOWN * event we do not send it. */ - if (n_points > 2) - ts_input_report(IE_UP, NULL); - + ts_skip_filter(IE_UP, NULL); ts.state = TS_STATE_STANDBY; - n_points = 0; if (ts.tsf[0]) (ts.tsf[0]->api->clear)(ts.tsf[0]); @@ -438,6 +516,8 @@ static int __init s3c2410ts_probe(struct platform_device *pdev) ts.dev->id.version = S3C2410TSVERSION; ts.state = TS_STATE_STANDBY; + ts_skip_filter_reset(); + /* create the filter chain set up for the 2 coordinates we produce */ ret = ts_filter_create_chain( (struct ts_filter_api **)&info->filter_sequence, |