aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/input/touchscreen/ts_filter_group.c178
1 files changed, 106 insertions, 72 deletions
diff --git a/drivers/input/touchscreen/ts_filter_group.c b/drivers/input/touchscreen/ts_filter_group.c
index b7c3f3ddcb9..93f8fc690e4 100644
--- a/drivers/input/touchscreen/ts_filter_group.c
+++ b/drivers/input/touchscreen/ts_filter_group.c
@@ -20,29 +20,39 @@
*
* 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.
+ * This filter mixes information from all the available dimensions. It means
+ * that for two dimensions we draw a rectangle where the though-to-be good
+ * points can be found.
*
- * 1) We collect S samples.
+ * The implementation would be more efficient with a double-linked list but
+ * let's keep it simple for now.
*
- * 2) For each dimension:
- *
- * - We sort the points.
+ * 1) We collect S samples and keep it in sorted sets.
* - 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.
+ * We don't actually keep the sets but ranges of points.
+ *
+ * 2) For each dimension:
+ * - We choose the range with more elements. If more than "threshold"
+ * points are in this range we use the minimum and the maximum point
+ * of the range to define the valid range for this dimension [min, max],
+ * otherwise we discard all the points and the ranges 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.
+ * 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 "ts_filter_group.h"
+struct coord_range {
+ int min; /* Minimum value of the range. */
+ int max; /* Maximum value of the range */
+ int N; /* Number of points in the range. */
+};
+
struct ts_filter_group {
/* Private filter configuration. */
struct ts_filter_group_configuration *config;
@@ -52,13 +62,15 @@ struct ts_filter_group {
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. */
+ /* Temporal values that help us compute range_min and range_max. */
+ struct coord_range *ranges[MAX_TS_FILTER_COORDS]; /* Ranges. */
+ int n_ranges[MAX_TS_FILTER_COORDS]; /* Number of ranges */
+ /* Computed ranges that help us filter the points. */
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 tries_left; /* We finish if we can't get enough samples. */
int ready; /* If we are ready to deliver samples. */
int result; /* Index of the point being returned. */
};
@@ -70,10 +82,13 @@ struct ts_filter_group {
static void ts_filter_group_clear_internal(struct ts_filter_group *tsfg,
int attempts)
{
+ int n;
tsfg->N = 0;
tsfg->tries_left = attempts;
tsfg->ready = 0;
tsfg->result = 0;
+ for (n = 0; n < tsfg->tsf.count_coords; n++)
+ tsfg->n_ranges[n] = 0;
}
static void ts_filter_group_clear(struct ts_filter *tsf)
@@ -101,8 +116,9 @@ static struct ts_filter *ts_filter_group_create(
tsfg->tsf.count_coords = count_coords;
BUG_ON(tsfg->config->attempts <= 0);
+ BUG_ON(tsfg->config->length < tsfg->config->threshold);
- tsfg->samples[0] = kmalloc((2 + count_coords) * sizeof(int) *
+ tsfg->samples[0] = kmalloc(count_coords * sizeof(int) *
tsfg->config->length, GFP_KERNEL);
if (!tsfg->samples[0]) {
kfree(tsfg);
@@ -110,10 +126,16 @@ static struct ts_filter *ts_filter_group_create(
}
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;
+
+ tsfg->ranges[0] = kmalloc(count_coords * sizeof(struct coord_range) *
+ tsfg->config->length, GFP_KERNEL);
+ if (!tsfg->ranges[0]) {
+ kfree(tsfg->samples[0]);
+ kfree(tsfg);
+ return NULL;
+ }
+ for (i = 1; i < count_coords; ++i)
+ tsfg->ranges[i] = tsfg->ranges[0] + i * tsfg->config->length;
ts_filter_group_clear_internal(tsfg, tsfg->config->attempts);
@@ -128,77 +150,90 @@ 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(tsfg->samples[0]);
+ kfree(tsfg->ranges[0]);
kfree(tsf);
}
-static int int_cmp(const void *_a, const void *_b)
-{
- const int *a = _a;
- const int *b = _b;
+static void ts_filter_group_prepare_next(struct ts_filter *tsf);
- if (*a > *b)
- return 1;
- if (*a < *b)
- return -1;
- return 0;
-}
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define IN_RANGE(c, r) ((c) >= (r).min - tsfg->config->close_enough && \
+ (c) <= (r).max + tsfg->config->close_enough)
-static void ts_filter_group_prepare_next(struct ts_filter *tsf);
+static void delete_spot(struct coord_range *v, int n, int size)
+{
+ int i;
+ for (i = n; i < size - 1; ++i)
+ v[i] = v[i + 1];
+}
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;
+ int j;
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];
+ for (n = 0; n < tsfg->tsf.count_coords; n++) {
+ int i;
+ struct coord_range *range = tsfg->ranges[n];
+ int *n_ranges = &tsfg->n_ranges[n];
+ int found = 0;
- if (++tsfg->N < tsfg->config->length)
- return 0; /* We need more samples. */
+ tsfg->samples[n][tsfg->N] = coords[n];
- 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;
+ for (i = 0; i < *n_ranges; ++i) {
+ if (IN_RANGE(coords[n], range[i])) {
+ range[i].min = MIN(range[i].min, coords[n]);
+ range[i].max = MAX(range[i].max, coords[n]);
+ range[i].N++;
+ found = 1;
+ break;
+ } else if (coords[n] <= range[i].min)
+ break; /* We need to insert a range. */
}
- 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 (found) { /* We might need to melt ranges. */
+ if (i && range[i - 1].max + tsfg->config->close_enough
+ >= range[i].min) {
+ BUG_ON(range[i - 1].max >= range[i].max);
+ range[i - 1].max = range[i].max;
+ range[i - 1].N += range[i].N;
+ delete_spot(range, i, *n_ranges);
+ (*n_ranges)--;
+ i--;
+ }
+ if (i < *n_ranges - 1 && range[i + 1].min -
+ tsfg->config->close_enough <= range[i].max) {
+ range[i].max = range[i + 1].max;
+ range[i].N += range[i + 1].N;
+ delete_spot(range, i + 1, *n_ranges);
+ (*n_ranges)--;
}
+ } else {
+ BUG_ON((*n_ranges) >= tsfg->config->length);
+ (*n_ranges)++;
+ for (j = *n_ranges - 1; j > i; --j)
+ range[j] = range[j - 1];
+ range[i].N = 1;
+ range[i].min = coords[n];
+ range[i].max = coords[n];
}
+ }
- if (best_size < tsfg->config->threshold) {
- /* This set is not good enough for us. */
+ if (++tsfg->N < tsfg->config->length)
+ return 0;
+
+ for (n = 0; n < tsfg->tsf.count_coords; ++n) {
+ int best = 0;
+ for (j = 1; j < tsfg->n_ranges[n]; ++j)
+ if (tsfg->ranges[n][best].N < tsfg->ranges[n][j].N)
+ best = j;
+ if (tsfg->ranges[n][best].N < tsfg->config->threshold) {
+ /* This set of points is not good enough for us. */
if (--tsfg->tries_left) {
ts_filter_group_clear_internal
(tsfg, tsfg->tries_left);
@@ -207,9 +242,8 @@ static int ts_filter_group_process(struct ts_filter *tsf, int *coords)
}
return 1; /* We give up: error. */
}
-
- tsfg->range_min[n] = v[best_idx];
- tsfg->range_max[n] = v[best_idx + best_size - 1];
+ tsfg->range_min[n] = tsfg->ranges[n][best].min;
+ tsfg->range_max[n] = tsfg->ranges[n][best].max;
}
ts_filter_group_prepare_next(tsf);