aboutsummaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/touchscreen/Kconfig9
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/s3c2410_ts.c45
-rw-r--r--drivers/input/touchscreen/ts_filter_mean.c18
-rw-r--r--drivers/input/touchscreen/ts_filter_median.c21
-rw-r--r--drivers/input/touchscreen/ts_filter_variance.c205
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,
+};
+