aboutsummaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen/ts_filter_mean.c
blob: 2322432fc3fe3c21cae591d4d0ff7bd6fc8d1104 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/*
 * 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;

	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,
};