aboutsummaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen/ts_filter_mean.c
diff options
context:
space:
mode:
authorAndy Green <andy@openmoko.com>2008-11-19 17:11:06 +0000
committerAndy Green <agreen@pads.home.warmcat.com>2008-11-19 17:11:06 +0000
commitd082ca932a9d03105fe26881ee860e7ff7025a9d (patch)
tree04253fc0920a760167d873d1c850bdbe0863bbb2 /drivers/input/touchscreen/ts_filter_mean.c
parent4c1c1b07611390857b93f515a284dae2de3115d7 (diff)
test-touchscreen-median.patch
Signed-off-by: Andy Green <andy@openmoko.com>
Diffstat (limited to 'drivers/input/touchscreen/ts_filter_mean.c')
-rw-r--r--drivers/input/touchscreen/ts_filter_mean.c170
1 files changed, 170 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/ts_filter_mean.c b/drivers/input/touchscreen/ts_filter_mean.c
new file mode 100644
index 00000000000..b589bee826f
--- /dev/null
+++ b/drivers/input/touchscreen/ts_filter_mean.c
@@ -0,0 +1,170 @@
+/*
+ * 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 Andy Green <andy@openmoko.com>
+ *
+ *
+ * Mean has no effect if the samples are changing by more that the
+ * threshold set by averaging_threshold in the configuration.
+ *
+ * However while samples come in that don't go outside this threshold from
+ * the last reported sample, Mean replaces the samples with a simple mean
+ * of a configurable number of samples (set by bits_filter_length in config,
+ * which is 2^n, so 5 there makes 32 sample averaging).
+ *
+ * Mean works well if the input data is already good quality, reducing + / - 1
+ * sample jitter when the stylus is still, or moving very slowly, without
+ * introducing abrupt transitions or reducing ability to follow larger
+ * movements. If you set the threshold higher than the dynamic range of the
+ * coordinates, you can just use it as a simple mean average.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/ts_filter_mean.h>
+
+static void ts_filter_mean_clear(struct ts_filter *tsf)
+{
+ struct ts_filter_mean *tsfs = (struct ts_filter_mean *)tsf;
+ int n;
+
+ for (n = 0; n < tsfs->tsf.count_coords; n++) {
+ tsfs->fhead[n] = 0;
+ tsfs->ftail[n] = 0;
+ tsfs->lowpass[n] = 0;
+ }
+
+ if (tsf->next) /* chain */
+ (tsf->next->api->clear)(tsf->next);
+}
+
+static struct ts_filter *ts_filter_mean_create(void *config, int count_coords)
+{
+ int *p;
+ int n;
+ struct ts_filter_mean *tsfs = kzalloc(
+ sizeof(struct ts_filter_mean), GFP_KERNEL);
+
+ if (!tsfs)
+ return NULL;
+
+ BUG_ON((count_coords < 1) || (count_coords > MAX_TS_FILTER_COORDS));
+ tsfs->tsf.count_coords = count_coords;
+
+ tsfs->config = (struct ts_filter_mean_configuration *)config;
+
+ tsfs->config->extent = 1 << tsfs->config->bits_filter_length;
+ BUG_ON((tsfs->config->extent > 256) || (!tsfs->config->extent));
+
+ p = kmalloc(tsfs->config->extent * sizeof(int) * count_coords,
+ GFP_KERNEL);
+ if (!p)
+ return NULL;
+
+ for (n = 0; n < count_coords; n++) {
+ tsfs->fifo[n] = p;
+ p += tsfs->config->extent;
+ }
+
+ if (!tsfs->config->averaging_threshold)
+ tsfs->config->averaging_threshold = 0xffff; /* always active */
+
+ ts_filter_mean_clear(&tsfs->tsf);
+
+ printk(KERN_INFO " Created Mean ts filter len %d thresh %d\n",
+ tsfs->config->extent, tsfs->config->averaging_threshold);
+
+ return &tsfs->tsf;
+}
+
+static void ts_filter_mean_destroy(struct ts_filter *tsf)
+{
+ struct ts_filter_mean *tsfs = (struct ts_filter_mean *)tsf;
+
+ if (!tsf)
+ return;
+
+ if (tsf->next) /* chain */
+ (tsf->next->api->destroy)(tsf->next);
+
+ kfree(tsfs->fifo[0]); /* first guy has pointer from kmalloc */
+ kfree(tsf);
+}
+
+static void ts_filter_mean_scale(struct ts_filter *tsf, int *coords)
+{
+ if (tsf->next) /* chain */
+ (tsf->next->api->scale)(tsf->next, coords);
+}
+
+/* give us the raw sample data in x and y, and if we return 1 then you can
+ * get a filtered coordinate from tsm->x and tsm->y: if we return 0 you didn't
+ * fill the filter with samples yet.
+ */
+
+static int ts_filter_mean_process(struct ts_filter *tsf, int *coords)
+{
+ struct ts_filter_mean *tsfs = (struct ts_filter_mean *)tsf;
+ int n;
+ int len;
+
+ for (n = 0; n < tsf->count_coords; n++) {
+
+ /* has he moved far enough away that we should abandon current
+ * low pass filtering state?
+ */
+ if ((coords[n] < (tsfs->reported[n] -
+ tsfs->config->averaging_threshold)) ||
+ (coords[n] > (tsfs->reported[n] +
+ tsfs->config->averaging_threshold))) {
+ tsfs->fhead[n] = 0;
+ tsfs->ftail[n] = 0;
+ tsfs->lowpass[n] = 0;
+ }
+
+ /* capture this sample into fifo and sum */
+ tsfs->fifo[n][tsfs->fhead[n]++] = coords[n];
+ if (tsfs->fhead[n] == tsfs->config->extent)
+ tsfs->fhead[n] = 0;
+ tsfs->lowpass[n] += coords[n];
+
+ /* adjust the sum into an average and use that*/
+ len = (tsfs->fhead[n] - tsfs->ftail[n]) &
+ (tsfs->config->extent - 1);
+ coords[n] = (tsfs->lowpass[n] + (len >> 1)) / len;
+ tsfs->reported[n] = coords[n];
+
+ /* remove oldest sample if we are full */
+ if (len == (tsfs->config->extent - 1)) {
+ tsfs->lowpass[n] -= tsfs->fifo[n][tsfs->ftail[n]++];
+ if (tsfs->ftail[n] == tsfs->config->extent)
+ tsfs->ftail[n] = 0;
+ }
+ }
+
+ if (tsf->next) /* chain */
+ return (tsf->next->api->process)(tsf->next, coords);
+// else
+ return 1;
+}
+
+struct ts_filter_api ts_filter_mean_api = {
+ .create = ts_filter_mean_create,
+ .destroy = ts_filter_mean_destroy,
+ .clear = ts_filter_mean_clear,
+ .process = ts_filter_mean_process,
+ .scale = ts_filter_mean_scale,
+};