diff options
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 9 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/s3c2410_ts.c | 45 | ||||
-rw-r--r-- | drivers/input/touchscreen/ts_filter_mean.c | 18 | ||||
-rw-r--r-- | drivers/input/touchscreen/ts_filter_median.c | 21 | ||||
-rw-r--r-- | drivers/input/touchscreen/ts_filter_variance.c | 205 |
6 files changed, 263 insertions, 36 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index ec28494584b..98566fd3342 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -21,6 +21,15 @@ menuconfig TOUCHSCREEN_FILTER if TOUCHSCREEN_FILTER +config TOUCHSCREEN_FILTER_VARIANCE + bool "Variance Touchscreen Filter" + depends on INPUT_TOUCHSCREEN && TOUCHSCREEN_FILTER + default Y + help + Say Y here if you want to use the Variance touchscreen filter, it + helps discarding a click if we get too much noise. + + config TOUCHSCREEN_FILTER_MEDIAN bool "Median Average Touchscreen Filter" depends on INPUT_TOUCHSCREEN && TOUCHSCREEN_FILTER diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 1bbe8f5812f..ee6bd283668 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -33,5 +33,6 @@ wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o obj-$(CONFIG_TOUCHSCREEN_FILTER) += ts_filter.o +obj-$(CONFIG_TOUCHSCREEN_FILTER_VARIANCE) += ts_filter_variance.o obj-$(CONFIG_TOUCHSCREEN_FILTER_MEDIAN) += ts_filter_median.o obj-$(CONFIG_TOUCHSCREEN_FILTER_MEAN) += ts_filter_mean.o diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index a880163059a..ea0f58aaf32 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -103,7 +103,7 @@ static char *s3c2410ts_name = "s3c2410 TouchScreen"; #define TS_STATE_RELEASE 4 #define SKIP_NHEAD 2 -#define SKIP_NTAIL 2 +#define SKIP_NTAIL 1 /* * Per-touchscreen data. @@ -392,27 +392,28 @@ static irqreturn_t stylus_action(int irq, void *dev_id) ts.coords[1] = readl(base_addr + S3C2410_ADCDAT1) & S3C2410_ADCDAT1_YPDATA_MASK; - if (!ts.tsf[0]) /* filtering is disabled then use raw directly */ - goto real_sample; - - /* send it to the chain of filters */ - if ((ts.tsf[0]->api->process)(ts.tsf[0], &ts.coords[0])) - goto real_sample; - - /* - * no real sample came out of processing yet, - * get another raw result to feed it - */ - - s3c2410_ts_start_adc_conversion(); - - return IRQ_HANDLED; - -real_sample: - - if (ts.tsf[0]) - (ts.tsf[0]->api->scale)(ts.tsf[0], &ts.coords[0]); + if (ts.tsf[0]) { /* filtering is enabled, don't use raw directly */ + switch ((ts.tsf[0]->api->process)(ts.tsf[0], &ts.coords[0])) { + case 0: /* + * no real sample came out of processing yet, + * get another raw result to feed it + */ + s3c2410_ts_start_adc_conversion(); + return IRQ_HANDLED; + case 1: /* filters are ready to deliver a sample */ + (ts.tsf[0]->api->scale)(ts.tsf[0], &ts.coords[0]); + break; + case -1: + /* error in filters, ignore the event */ + (ts.tsf[0]->api->clear)(ts.tsf[0]); + writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC); + return IRQ_HANDLED; + default: + printk(KERN_ERR":stylus_action error\n"); + } + } + /* We use a buffer because want an atomic operation */ buf[0] = 'P'; buf[1] = ts.coords[0]; buf[2] = ts.coords[1]; @@ -420,7 +421,7 @@ real_sample: if (unlikely(__kfifo_put(ts.event_fifo, (unsigned char *)buf, sizeof(int) * 3) != sizeof(int) * 3)) /* should not happen */ - printk(KERN_ERR __FILE__": stylus_action lost event!\n"); + printk(KERN_ERR":stylus_action error\n"); writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC); mod_timer(&event_send_timer, jiffies + 1); diff --git a/drivers/input/touchscreen/ts_filter_mean.c b/drivers/input/touchscreen/ts_filter_mean.c index 2322432fc3f..a2f1748a686 100644 --- a/drivers/input/touchscreen/ts_filter_mean.c +++ b/drivers/input/touchscreen/ts_filter_mean.c @@ -36,7 +36,7 @@ #include <linux/slab.h> #include <linux/ts_filter_mean.h> -static void ts_filter_mean_clear(struct ts_filter *tsf) +static void ts_filter_mean_clear_internal(struct ts_filter *tsf) { struct ts_filter_mean *tsfs = (struct ts_filter_mean *)tsf; int n; @@ -46,6 +46,11 @@ static void ts_filter_mean_clear(struct ts_filter *tsf) tsfs->ftail[n] = 0; tsfs->lowpass[n] = 0; } +} + +static void ts_filter_mean_clear(struct ts_filter *tsf) +{ + ts_filter_mean_clear_internal(tsf); if (tsf->next) /* chain */ (tsf->next->api->clear)(tsf->next); @@ -82,10 +87,11 @@ static struct ts_filter *ts_filter_mean_create(void *config, int count_coords) if (!tsfs->config->averaging_threshold) tsfs->config->averaging_threshold = 0xffff; /* always active */ - ts_filter_mean_clear(&tsfs->tsf); + ts_filter_mean_clear_internal(&tsfs->tsf); - printk(KERN_INFO " Created Mean ts filter len %d thresh %d\n", - tsfs->config->extent, tsfs->config->averaging_threshold); + printk(KERN_INFO" Created Mean ts filter len %d depth %d thresh %d\n", + tsfs->config->extent, count_coords, + tsfs->config->averaging_threshold); return &tsfs->tsf; } @@ -151,8 +157,8 @@ static int ts_filter_mean_process(struct ts_filter *tsf, int *coords) if (tsf->next) /* chain */ return (tsf->next->api->process)(tsf->next, coords); -// else - return 1; + + return 1; } struct ts_filter_api ts_filter_mean_api = { diff --git a/drivers/input/touchscreen/ts_filter_median.c b/drivers/input/touchscreen/ts_filter_median.c index d60c4314b87..67dd3febd55 100644 --- a/drivers/input/touchscreen/ts_filter_median.c +++ b/drivers/input/touchscreen/ts_filter_median.c @@ -65,13 +65,18 @@ static void ts_filter_median_del(int *p, int value, int count) } -static void ts_filter_median_clear(struct ts_filter *tsf) +static void ts_filter_median_clear_internal(struct ts_filter *tsf) { struct ts_filter_median *tsfm = (struct ts_filter_median *)tsf; tsfm->pos = 0; tsfm->valid = 0; +} +static void ts_filter_median_clear(struct ts_filter *tsf) +{ + ts_filter_median_clear_internal(tsf); + if (tsf->next) /* chain */ (tsf->next->api->clear)(tsf->next); } @@ -92,10 +97,6 @@ static struct ts_filter *ts_filter_median_create(void * conf, int count_coords) tsfm->config->midpoint = (tsfm->config->extent >> 1) + 1; - printk(KERN_INFO" Creating Median ts filter len %d depth %d dec %d\n", - tsfm->config->extent, count_coords, - tsfm->config->decimation_threshold); - p = kmalloc(2 * count_coords * sizeof(int) * (tsfm->config->extent + 1), GFP_KERNEL); if (!p) { @@ -110,7 +111,11 @@ static struct ts_filter *ts_filter_median_create(void * conf, int count_coords) p += tsfm->config->extent + 1; } - ts_filter_median_clear(&tsfm->tsf); + ts_filter_median_clear_internal(&tsfm->tsf); + + printk(KERN_INFO" Created Median ts filter len %d depth %d dec %d\n", + tsfm->config->extent, count_coords, + tsfm->config->decimation_threshold); return &tsfm->tsf; } @@ -195,8 +200,8 @@ static int ts_filter_median_process(struct ts_filter *tsf, int *coords) if (tsf->next) /* chain */ return (tsf->next->api->process)(tsf->next, coords); - else - return 1; + + return 1; } struct ts_filter_api ts_filter_median_api = { diff --git a/drivers/input/touchscreen/ts_filter_variance.c b/drivers/input/touchscreen/ts_filter_variance.c new file mode 100644 index 00000000000..c3352105c9f --- /dev/null +++ b/drivers/input/touchscreen/ts_filter_variance.c @@ -0,0 +1,205 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) 2008 by Openmoko, Inc. + * Author: Nelson Castillo <arhuaco@freaks-unidos.net> + * All rights reserved. + * + * This filter is useful to reject clicks that are not reliable. We + * only care about what happens when we receive DOWN events for the fist time. + * If this filter does not reject the first samples then it will change + * its internal state to "passed" and the remaining samples + * will be passed to the next filter in the chain. + * + * First we collect N samples, then then we sort them. We discard the borders + * (with a window) and then compute the variance of the remaining set. + * If the computed variance is bigger than a threshold, we reject the click. + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sort.h> +#include <linux/ts_filter_variance.h> + +static void ts_filter_variance_clear_internal(struct ts_filter_variance *tsfv, + int attempts) +{ + tsfv->N = 0; + tsfv->passed = 0; + tsfv->tries_left = attempts; +} + +static void ts_filter_variance_clear(struct ts_filter *tsf) +{ + struct ts_filter_variance *tsfv = (struct ts_filter_variance *)tsf; + + ts_filter_variance_clear_internal(tsfv, tsfv->config->attempts); + + if (tsf->next) /* chain */ + (tsf->next->api->clear)(tsf->next); +} + +static struct ts_filter *ts_filter_variance_create(void *conf, int count_coords) +{ + struct ts_filter_variance *tsfv; + int i; + + BUG_ON((count_coords < 1) || (count_coords > MAX_TS_FILTER_COORDS)); + + tsfv = kzalloc(sizeof(struct ts_filter_variance), GFP_KERNEL); + if (!tsfv) + return NULL; + + tsfv->config = (struct ts_filter_variance_configuration *)conf; + tsfv->tsf.count_coords = count_coords; + + BUG_ON(tsfv->config->attempts <= 0); + + tsfv->samples[0] = kmalloc(count_coords * sizeof(int) * + tsfv->config->extent, GFP_KERNEL); + if (!tsfv->samples[0]) { + kfree(tsfv); + return NULL; + } + for (i = 1; i < count_coords; ++i) + tsfv->samples[i] = tsfv->samples[0] + i * tsfv->config->extent; + + ts_filter_variance_clear_internal(tsfv, tsfv->config->attempts); + + printk(KERN_INFO" Created Variance ts filter len %d depth %d window" + " %d thresh %d\n", tsfv->config->extent, + count_coords, tsfv->config->window, + tsfv->config->threshold); + + /* scale the threshold to avoid divisions later */ + tsfv->config->threshold *= tsfv->config->extent - + (tsfv->config->window << 1); + + return &tsfv->tsf; +} + +static void ts_filter_variance_destroy(struct ts_filter *tsf) +{ + struct ts_filter_variance *tsfv = (struct ts_filter_variance *)tsf; + + kfree(tsfv->samples[0]); /* first guy has pointer from kmalloc */ + kfree(tsf); +} + +static void ts_filter_variance_scale(struct ts_filter *tsf, int *coords) +{ + struct ts_filter_variance *tsfv = (struct ts_filter_variance *)tsf; + + if (!tsfv->passed) + return; + + if (tsf->next) { + (tsf->next->api->scale)(tsf->next, coords); + } else { + int n; + for (n = 0; n < tsf->count_coords; n++) { + int c = tsfv->samples[n][tsfv->N / 2] + + tsfv->samples[n][tsfv->N / 2 + 1] + + tsfv->samples[n][tsfv->N / 2 - 1]; + coords[n] = (c + 2) / 3; + } + } +} + +static int int_cmp(const void *_a, const void *_b) +{ + const int *a = _a; + const int *b = _b; + + if (*a > *b) + return 1; + if (*a < *b) + return -1; + return 0; +} + +/* give us the raw sample data coords, and if we return 1 then you can + * get a filtered coordinate from coords: if we return 0 you didn't + * fill all the filters with samples yet. + */ + +static int ts_filter_variance_process(struct ts_filter *tsf, int *coords) +{ + struct ts_filter_variance *tsfv = (struct ts_filter_variance *)tsf; + int n; + + if (tsfv->passed) { /* chain */ + if (tsf->next) + return (tsf->next->api->process)(tsf->next, coords); + return 1; + } + + for (n = 0; n < tsf->count_coords; n++) + tsfv->samples[n][tsfv->N] = coords[n]; + + if (++tsfv->N < tsfv->config->extent) + return 0; /* we meed more samples */ + + tsfv->passed = 1; + + for (n = 0; n < tsfv->tsf.count_coords; n++) { + int i; + int avg = 0; + int variance = 0; + + sort(tsfv->samples[n], tsfv->config->extent, sizeof(int), + int_cmp, NULL); + + for (i = tsfv->config->window; i < tsfv->config->extent - + tsfv->config->window; ++i) + avg += tsfv->samples[n][i]; + + avg /= tsfv->config->extent - (tsfv->config->window << 1); + + for (i = tsfv->config->window; i < tsfv->config->extent - + tsfv->config->window; ++i) { + int s = tsfv->samples[n][i] - avg; + variance += s * s; + } + + if (variance > tsfv->config->threshold) { + tsfv->passed = 0; + break; + } + } + + if (tsfv->passed) /* Let's reuse the last sample */ + return ts_filter_variance_process(tsf, coords); + + if (--tsfv->tries_left) { + ts_filter_variance_clear_internal(tsfv, tsfv->tries_left); + return 0; /* ask for more samples */ + } + + /* avoid overflow if we are called again without clearing the filter */ + ts_filter_variance_clear_internal(tsfv, tsfv->config->attempts); + + return -1; +} + +struct ts_filter_api ts_filter_variance_api = { + .create = ts_filter_variance_create, + .destroy = ts_filter_variance_destroy, + .clear = ts_filter_variance_clear, + .process = ts_filter_variance_process, + .scale = ts_filter_variance_scale, +}; + |