[s3c24xx] bump to 2.6.30-rc6
[openwrt.git] / target / linux / s3c24xx / files-2.6.30 / drivers / input / touchscreen / ts_filter_group.c
diff --git a/target/linux/s3c24xx/files-2.6.30/drivers/input/touchscreen/ts_filter_group.c b/target/linux/s3c24xx/files-2.6.30/drivers/input/touchscreen/ts_filter_group.c
new file mode 100644 (file)
index 0000000..f53f16d
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * 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,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.
+ *
+ * 1) We collect S samples.
+ *
+ * 2) For each dimension:
+ *
+ *  - We sort the points.
+ *  - Points that are "close enough" are considered to be in the same set.
+ *  - We choose the set with more elements. If more than "threshold"
+ *    points are in this set we use the first and the last point of the set
+ *    to define the valid range for this dimension [min, max], otherwise we
+ *    discard all the points and go to step 1.
+ *
+ * 3) We consider the unsorted S samples and try to feed them to the next
+ *    filter in the chain. If one of the points of each sample
+ *    is not in the allowed range for its dimension, we discard the sample.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+#include <linux/touchscreen/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 = ts_filter_to_filter_group(tsf);
+
+       ts_filter_group_clear_internal(tsfg, tsfg->config->attempts);
+}
+
+static struct ts_filter *ts_filter_group_create(
+       struct platform_device *pdev,
+       const struct ts_filter_configuration *conf,
+       int count_coords)
+{
+       struct ts_filter_group *tsfg;
+       int i;
+
+       tsfg = kzalloc(sizeof(struct ts_filter_group), GFP_KERNEL);
+       if (!tsfg)
+               return NULL;
+
+       tsfg->config = container_of(conf,
+                                   struct ts_filter_group_configuration,
+                                   config);
+       tsfg->tsf.count_coords = count_coords;
+
+       BUG_ON(tsfg->config->attempts <= 0);
+
+       tsfg->samples[0] = kmalloc((2 + count_coords) * sizeof(int) *
+                                  tsfg->config->length, GFP_KERNEL);
+       if (!tsfg->samples[0]) {
+               kfree(tsfg);
+               return NULL;
+       }
+       for (i = 1; i < count_coords; ++i)
+               tsfg->samples[i] = tsfg->samples[0] + i * tsfg->config->length;
+       tsfg->sorted_samples = tsfg->samples[0] + count_coords *
+                              tsfg->config->length;
+       tsfg->group_size = tsfg->samples[0] + (1 + count_coords) *
+                              tsfg->config->length;
+
+       ts_filter_group_clear_internal(tsfg, tsfg->config->attempts);
+
+       dev_info(&pdev->dev, "Created Group filter len:%d coords:%d close:%d "
+                "thresh:%d\n", tsfg->config->length, count_coords,
+                tsfg->config->close_enough, tsfg->config->threshold);
+
+       return &tsfg->tsf;
+}
+
+static void ts_filter_group_destroy(struct ts_filter *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 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;
+}
+
+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 = ts_filter_to_filter_group(tsf);
+       int n;
+       int i;
+
+       BUG_ON(tsfg->N >= tsfg->config->length);
+       BUG_ON(tsfg->ready);
+
+       for (n = 0; n < tsf->count_coords; n++)
+               tsfg->samples[n][tsfg->N] = coords[n];
+
+       if (++tsfg->N < tsfg->config->length)
+               return 0;       /* We need more samples. */
+
+       for (n = 0; n < tsfg->tsf.count_coords; n++) {
+               int *v = tsfg->sorted_samples;
+               int ngroups = 0;
+               int best_size;
+               int best_idx = 0;
+               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;
+               for (i = 1; i < tsfg->N; ++i) {
+                       if (v[i] - v[i - 1] <= tsfg->config->close_enough)
+                               tsfg->group_size[ngroups]++;
+                       else
+                               tsfg->group_size[++ngroups] = 1;
+               }
+               ngroups++;
+
+               best_size = tsfg->group_size[0];
+               for (i = 1; i < ngroups; i++) {
+                       idx += tsfg->group_size[i - 1];
+                       if (best_size < tsfg->group_size[i]) {
+                               best_size = tsfg->group_size[i];
+                               best_idx = idx;
+                       }
+               }
+
+               if (best_size < tsfg->config->threshold) {
+                       /* This set is not good enough for us. */
+                       if (--tsfg->tries_left) {
+                               ts_filter_group_clear_internal
+                                       (tsfg, tsfg->tries_left);
+                               /* No errors but we need more samples. */
+                               return 0;
+                       }
+                       return 1; /* We give up: error. */
+               }
+
+               tsfg->range_min[n] = v[best_idx];
+               tsfg->range_max[n] = v[best_idx + best_size - 1];
+       }
+
+       ts_filter_group_prepare_next(tsf);
+
+       return 0;
+}
+
+/*
+ * 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;
+
+       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;
+               }
+
+               if (n == priv->tsf.count_coords) /* Sample is OK. */
+                       break;
+
+               priv->result++;
+       }
+
+       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);
+
+       ts_filter_group_clear_internal(priv, priv->config->attempts);
+}
+
+const 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,
+       .haspoint =     ts_filter_group_haspoint,
+       .getpoint =     ts_filter_group_getpoint,
+       .scale =        ts_filter_group_scale,
+};
+EXPORT_SYMBOL_GPL(ts_filter_group_api);
+