aboutsummaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen/ts_filter_group.c
diff options
context:
space:
mode:
authorNelson Castillo <arhuaco@freaks-unidos.net>2009-03-10 12:04:05 +0000
committerAndy Green <agreen@octopus.localdomain>2009-03-10 12:04:05 +0000
commitbd88fa696f505a8a71484341e4fcde581b2d12a6 (patch)
treebca483a0c57848ca6d122907c42a0c7c04fbeb4a /drivers/input/touchscreen/ts_filter_group.c
parent45aeeebf673b03329c423c2382a836ca878dd851 (diff)
Improve filter API and update filters
This patch turns upstream feedback into API modifications and code improvements. There will be more patches implementing upstream corrections but this one is the that will make most of the invasive changes and make the most important improvements to the API. Tested in a GTA02/rev06. The goals of this patch are: * Replace recursive calls with iteration. * General code improvements. * Make ts_filter_mean.c a reference for the rest of the filters. * Make the (almost)minimum number of changes to the other filters so that they compile and work, patches for cleaning these up will come next. * Filters should do what they were doing before. Some important changes: * Move "struct ts_filter tsf" in the private structures to force a crash (or break things) if we forget to remove an open-coded cast. * ts_filter.c/ts_filter.h ~ API modifications. * s3c2410_ts.c: ~ Use the new API. ~ Cleanups. * ts_filter_mean.c ~ Replace with a simple mean. ~ Use as a reference for the new API. ~ Move private structure from the .h to the .c. * ts_filter_group.c ~ Update to use the new API. * ts_filter_median.c ~ Update to use the new API. * ts_filter_linear.c ~ Remove functions that are no longer needed. Note: I might leave some TODOs and FIXMEs with this patch. Most of them will be removed shortly. Signed-off-by: Nelson Castillo <arhuaco@freaks-unidos.net>
Diffstat (limited to 'drivers/input/touchscreen/ts_filter_group.c')
-rw-r--r--drivers/input/touchscreen/ts_filter_group.c166
1 files changed, 118 insertions, 48 deletions
diff --git a/drivers/input/touchscreen/ts_filter_group.c b/drivers/input/touchscreen/ts_filter_group.c
index f2ecd92916e..9b02c70c4bb 100644
--- a/drivers/input/touchscreen/ts_filter_group.c
+++ b/drivers/input/touchscreen/ts_filter_group.c
@@ -13,10 +13,11 @@
* 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.
+ * Copyright (C) 2008,2009 by Openmoko, Inc.
* Author: Nelson Castillo <arhuaco@freaks-unidos.net>
* All rights reserved.
*
+ *
* This filter is useful to reject samples that are not reliable. We consider
* that a sample is not reliable if it deviates form the Majority.
*
@@ -42,21 +43,44 @@
#include <linux/sort.h>
#include "ts_filter_group.h"
+struct ts_filter_group {
+ /* Private filter configuration. */
+ struct ts_filter_group_configuration *config;
+ /* Filter API. */
+ struct ts_filter tsf;
+
+ int N; /* How many samples we have */
+ int *samples[MAX_TS_FILTER_COORDS]; /* The samples: our input. */
+
+ int *group_size; /* Used for temporal computations. */
+ int *sorted_samples; /* Used for temporal computations. */
+
+ int range_max[MAX_TS_FILTER_COORDS]; /* Max. computed ranges. */
+ int range_min[MAX_TS_FILTER_COORDS]; /* Min. computed ranges. */
+
+ int tries_left; /* We finish if we don't get enough samples. */
+ int ready; /* If we are ready to deliver samples. */
+ int result; /* Index of the point being returned. */
+};
+
+#define ts_filter_to_filter_group(f) \
+ container_of(f, struct ts_filter_group, tsf)
+
+
static void ts_filter_group_clear_internal(struct ts_filter_group *tsfg,
int attempts)
{
tsfg->N = 0;
tsfg->tries_left = attempts;
+ tsfg->ready = 0;
+ tsfg->result = 0;
}
static void ts_filter_group_clear(struct ts_filter *tsf)
{
- struct ts_filter_group *tsfg = (struct ts_filter_group *)tsf;
+ struct ts_filter_group *tsfg = ts_filter_to_filter_group(tsf);
ts_filter_group_clear_internal(tsfg, tsfg->config->attempts);
-
- if (tsf->next) /* chain */
- (tsf->next->api->clear)(tsf->next);
}
static struct ts_filter *ts_filter_group_create(struct platform_device *pdev,
@@ -65,8 +89,6 @@ static struct ts_filter *ts_filter_group_create(struct platform_device *pdev,
struct ts_filter_group *tsfg;
int i;
- BUG_ON((count_coords < 1) || (count_coords > MAX_TS_FILTER_COORDS));
-
tsfg = kzalloc(sizeof(struct ts_filter_group), GFP_KERNEL);
if (!tsfg)
return NULL;
@@ -91,28 +113,21 @@ static struct ts_filter *ts_filter_group_create(struct platform_device *pdev,
ts_filter_group_clear_internal(tsfg, tsfg->config->attempts);
- printk(KERN_INFO" Created group ts filter len %d depth %d close %d "
- "thresh %d\n", tsfg->config->extent, count_coords,
- tsfg->config->close_enough, tsfg->config->threshold);
+ dev_info(&pdev->dev, "Created Group filter len:%d coords:%d close:%d "
+ "thresh:%d\n", tsfg->config->extent, count_coords,
+ tsfg->config->close_enough, tsfg->config->threshold);
return &tsfg->tsf;
}
-static void ts_filter_group_destroy(struct platform_device *pdev,
- struct ts_filter *tsf)
+static void ts_filter_group_destroy(struct ts_filter *tsf)
{
- struct ts_filter_group *tsfg = (struct ts_filter_group *)tsf;
+ struct ts_filter_group *tsfg = ts_filter_to_filter_group(tsf);
kfree(tsfg->samples[0]); /* first guy has pointer from kmalloc */
kfree(tsf);
}
-static void ts_filter_group_scale(struct ts_filter *tsf, int *coords)
-{
- if (tsf->next)
- (tsf->next->api->scale)(tsf->next, coords);
-}
-
static int int_cmp(const void *_a, const void *_b)
{
const int *a = _a;
@@ -125,20 +140,22 @@ static int int_cmp(const void *_a, const void *_b)
return 0;
}
+static void ts_filter_group_prepare_next(struct ts_filter *tsf);
+
static int ts_filter_group_process(struct ts_filter *tsf, int *coords)
{
- struct ts_filter_group *tsfg = (struct ts_filter_group *)tsf;
+ struct ts_filter_group *tsfg = ts_filter_to_filter_group(tsf);
int n;
int i;
- int ret = 0; /* ask for more samples by default */
BUG_ON(tsfg->N >= tsfg->config->extent);
+ BUG_ON(tsfg->ready);
for (n = 0; n < tsf->count_coords; n++)
tsfg->samples[n][tsfg->N] = coords[n];
if (++tsfg->N < tsfg->config->extent)
- return 0; /* we meed more samples */
+ return 0; /* We need more samples. */
for (n = 0; n < tsfg->tsf.count_coords; n++) {
int *v = tsfg->sorted_samples;
@@ -148,6 +165,14 @@ static int ts_filter_group_process(struct ts_filter *tsf, int *coords)
int idx = 0;
memcpy(v, tsfg->samples[n], tsfg->N * sizeof(int));
+ /*
+ * FIXME: Remove this sort call. We already have the
+ * algorithm for this modification. The filter will
+ * need less points (about half) if there is not a
+ * lot of noise. Right now we are doing a constant
+ * amount of work no matter how much noise we are
+ * dealing with.
+ */
sort(v, tsfg->N, sizeof(int), int_cmp, NULL);
tsfg->group_size[0] = 1;
@@ -173,49 +198,94 @@ static int ts_filter_group_process(struct ts_filter *tsf, int *coords)
if (--tsfg->tries_left) {
ts_filter_group_clear_internal
(tsfg, tsfg->tries_left);
- return 0; /* ask for more samples */
+ /* No errors but we need more samples. */
+ return 0;
}
- return -1; /* we give up */
+ return 1; /* We give up: error. */
}
tsfg->range_min[n] = v[best_idx];
tsfg->range_max[n] = v[best_idx + best_size - 1];
}
- for (i = 0; i < tsfg->N; ++i) {
- int r;
+ ts_filter_group_prepare_next(tsf);
- for (n = 0; n < tsfg->tsf.count_coords; ++n) {
- coords[n] = tsfg->samples[n][i];
- if (coords[n] < tsfg->range_min[n] ||
- coords[n] > tsfg->range_max[n])
- break;
- }
+ return 0;
+}
- if (n != tsfg->tsf.count_coords) /* sample not OK */
- continue;
+/*
+ * This private function prepares a point that will be returned
+ * in ts_filter_group_getpoint if it is available. It updates
+ * the priv->ready state also.
+ */
+static void ts_filter_group_prepare_next(struct ts_filter *tsf)
+{
+ struct ts_filter_group *priv = ts_filter_to_filter_group(tsf);
+ int n;
- if (tsf->next) {
- r = (tsf->next->api->process)(tsf->next, coords);
- if (r) {
- ret = r;
+ while (priv->result < priv->N) {
+ for (n = 0; n < priv->tsf.count_coords; ++n) {
+ if (priv->samples[n][priv->result] <
+ priv->range_min[n] ||
+ priv->samples[n][priv->result] > priv->range_max[n])
break;
- }
- } else if (i == tsfg->N - 1) {
- ret = 1;
}
+
+ if (n == priv->tsf.count_coords) /* Sample is OK. */
+ break;
+
+ priv->result++;
}
- ts_filter_group_clear_internal(tsfg, tsfg->config->attempts);
+ if (unlikely(priv->result >= priv->N)) { /* No sample to deliver. */
+ ts_filter_group_clear_internal(priv, priv->config->attempts);
+ priv->ready = 0;
+ } else {
+ priv->ready = 1;
+ }
+}
+
+static int ts_filter_group_haspoint(struct ts_filter *tsf)
+{
+ struct ts_filter_group *priv = ts_filter_to_filter_group(tsf);
+
+ return priv->ready;
+}
+
+static void ts_filter_group_getpoint(struct ts_filter *tsf, int *point)
+{
+ struct ts_filter_group *priv = ts_filter_to_filter_group(tsf);
+ int n;
+
+ BUG_ON(!priv->ready);
+
+ for (n = 0; n < priv->tsf.count_coords; n++)
+ point[n] = priv->samples[n][priv->result];
+
+ priv->result++;
+
+ /* This call will update priv->ready. */
+ ts_filter_group_prepare_next(tsf);
+}
+
+/*
+ * Get ready to process the next batch of points, forget
+ * points we could have delivered.
+ */
+static void ts_filter_group_scale(struct ts_filter *tsf, int *coords)
+{
+ struct ts_filter_group *priv = ts_filter_to_filter_group(tsf);
- return ret;
+ ts_filter_group_clear_internal(priv, priv->config->attempts);
}
struct ts_filter_api ts_filter_group_api = {
- .create = ts_filter_group_create,
- .destroy = ts_filter_group_destroy,
- .clear = ts_filter_group_clear,
- .process = ts_filter_group_process,
- .scale = ts_filter_group_scale,
+ .create = ts_filter_group_create,
+ .destroy = ts_filter_group_destroy,
+ .clear = ts_filter_group_clear,
+ .process = ts_filter_group_process,
+ .haspoint = ts_filter_group_haspoint,
+ .getpoint = ts_filter_group_getpoint,
+ .scale = ts_filter_group_scale,
};