aboutsummaryrefslogtreecommitdiff
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
parent4c1c1b07611390857b93f515a284dae2de3115d7 (diff)
test-touchscreen-median.patch
Signed-off-by: Andy Green <andy@openmoko.com>
-rw-r--r--arch/arm/mach-s3c2410/include/mach/ts.h15
-rw-r--r--arch/arm/mach-s3c2410/mach-gta01.c34
-rw-r--r--arch/arm/mach-s3c2440/mach-gta02.c37
-rw-r--r--drivers/input/touchscreen/Kconfig30
-rw-r--r--drivers/input/touchscreen/Makefile3
-rw-r--r--drivers/input/touchscreen/s3c2410_ts.c254
-rw-r--r--drivers/input/touchscreen/ts_filter.c57
-rw-r--r--drivers/input/touchscreen/ts_filter_mean.c170
-rw-r--r--drivers/input/touchscreen/ts_filter_median.c210
-rw-r--r--include/linux/ts_filter.h53
-rw-r--r--include/linux/ts_filter_mean.h34
-rw-r--r--include/linux/ts_filter_median.h36
12 files changed, 745 insertions, 188 deletions
diff --git a/arch/arm/mach-s3c2410/include/mach/ts.h b/arch/arm/mach-s3c2410/include/mach/ts.h
index 6fa75f6c0ab..ab4433f10bd 100644
--- a/arch/arm/mach-s3c2410/include/mach/ts.h
+++ b/arch/arm/mach-s3c2410/include/mach/ts.h
@@ -16,12 +16,17 @@
#ifndef __ASM_ARM_TS_H
#define __ASM_ARM_TS_H
+#include <linux/ts_filter.h>
+
struct s3c2410_ts_mach_info {
- int delay;
- int presc;
- int oversampling_shift;
- int excursion_filter_len_bits;
- int reject_threshold_vs_avg;
+ int delay;
+ int presc;
+ /* array of pointers to filter APIs we want to use, in order
+ * ends on first NULL, all NULL is OK
+ */
+ struct ts_filter_api *filter_sequence[MAX_TS_FILTER_CHAIN];
+ /* array of configuration ints, one for each filter above */
+ void *filter_config[MAX_TS_FILTER_CHAIN];
};
void set_s3c2410ts_info(struct s3c2410_ts_mach_info *hard_s3c2410ts_info);
diff --git a/arch/arm/mach-s3c2410/mach-gta01.c b/arch/arm/mach-s3c2410/mach-gta01.c
index 47377bd3da0..e718a810f42 100644
--- a/arch/arm/mach-s3c2410/mach-gta01.c
+++ b/arch/arm/mach-s3c2410/mach-gta01.c
@@ -505,19 +505,35 @@ static struct s3c2410_udc_mach_info gta01_udc_cfg = {
.vbus_draw = gta01_udc_vbus_draw,
};
+
+/* touchscreen configuration */
+
+static struct ts_filter_median_configuration gta01_ts_median_config = {
+ .extent = 31,
+ .decimation_below = 24,
+ .decimation_threshold = 8 * 3,
+ .decimation_above = 12,
+};
+
+static struct ts_filter_mean_configuration gta01_ts_mean_config = {
+ .bits_filter_length = 5,
+ .averaging_threshold = 12
+};
+
static struct s3c2410_ts_mach_info gta01_ts_cfg = {
.delay = 10000,
- .presc = 50000000 / 1000000, /* 50 MHz PCLK / 1MHz */
- /* simple averaging, 2^n samples */
- .oversampling_shift = 5,
- /* averaging filter length, 2^n */
- .excursion_filter_len_bits = 5,
- /* flagged for beauty contest on next sample if differs from
- * average more than this
- */
- .reject_threshold_vs_avg = 2,
+ .presc = 0xff, /* slow as we can go */
+ .filter_sequence = {
+ [0] = &ts_filter_median_api,
+ [1] = &ts_filter_mean_api,
+ },
+ .filter_config = {
+ [0] = &gta01_ts_median_config,
+ [1] = &gta01_ts_mean_config,
+ },
};
+
/* SPI */
static void gta01_jbt6k74_reset(int devidx, int level)
diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c
index 0ccb7fba850..6b44f1731b6 100644
--- a/arch/arm/mach-s3c2440/mach-gta02.c
+++ b/arch/arm/mach-s3c2440/mach-gta02.c
@@ -92,6 +92,9 @@
#include "../plat-s3c24xx/neo1973_pm_gps.h"
+#include <linux/ts_filter_mean.h>
+#include <linux/ts_filter_median.h>
+
/* arbitrates which sensor IRQ owns the shared SPI bus */
static spinlock_t motion_irq_lock;
@@ -939,20 +942,34 @@ static struct s3c2410_udc_mach_info gta02_udc_cfg = {
};
+
+/* touchscreen configuration */
+
+static struct ts_filter_median_configuration gta02_ts_median_config = {
+ .extent = 31,
+ .decimation_below = 28,
+ .decimation_threshold = 8 * 3,
+ .decimation_above = 12,
+};
+
+static struct ts_filter_mean_configuration gta02_ts_mean_config = {
+ .bits_filter_length = 3,
+ .averaging_threshold = 6 * 3,
+};
+
static struct s3c2410_ts_mach_info gta02_ts_cfg = {
.delay = 10000,
- .presc = 50000000 / 1000000, /* 50 MHz PCLK / 1MHz */
- /* simple averaging, 2^n samples */
- .oversampling_shift = 5,
- /* averaging filter length, 2^n */
- .excursion_filter_len_bits = 5,
- /* flagged for beauty contest on next sample if differs from
- * average more than this
- */
- .reject_threshold_vs_avg = 2,
+ .presc = 0xff, /* slow as we can go */
+ .filter_sequence = {
+ [0] = &ts_filter_median_api,
+ [1] = &ts_filter_mean_api,
+ },
+ .filter_config = {
+ [0] = &gta02_ts_median_config,
+ [1] = &gta02_ts_mean_config,
+ },
};
-
/* SPI: LCM control interface attached to Glamo3362 */
static void gta02_jbt6k74_reset(int devidx, int level)
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 26dac07e38e..ec28494584b 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -11,6 +11,34 @@ menuconfig INPUT_TOUCHSCREEN
if INPUT_TOUCHSCREEN
+menuconfig TOUCHSCREEN_FILTER
+ boolean "Touchscreen Filtering"
+ depends on INPUT_TOUCHSCREEN
+ help
+ Select this to include kernel touchscreen filter support. The filters
+ can be combined in any order in your machine init and the parameters
+ for them can also be set there.
+
+if TOUCHSCREEN_FILTER
+
+config TOUCHSCREEN_FILTER_MEDIAN
+ bool "Median Average Touchscreen Filter"
+ depends on INPUT_TOUCHSCREEN && TOUCHSCREEN_FILTER
+ default Y
+ help
+ Say Y here if you want to use the Median touchscreen filter, it's
+ highly effective if you data is noisy with occasional excursions.
+
+config TOUCHSCREEN_FILTER_MEAN
+ bool "Mean Average Touchscreen Filter"
+ depends on INPUT_TOUCHSCREEN && TOUCHSCREEN_FILTER
+ default Y
+ help
+ Say Y here if you want to use the Mean touchscreen filter, it
+ can further improve decent quality data by removing jitter
+
+endif
+
config TOUCHSCREEN_ADS7846
tristate "ADS7846/TSC2046 and ADS7843 based touchscreens"
depends on SPI_MASTER
@@ -75,6 +103,7 @@ config TOUCHSCREEN_S3C2410
tristate "Samsung S3C2410 touchscreen input driver"
depends on ARCH_S3C2410 && INPUT && INPUT_TOUCHSCREEN
select SERIO
+ select TOUCHSCREEN_FILTER
help
Say Y here if you have the s3c2410 touchscreen.
@@ -395,3 +424,4 @@ config TOUCHSCREEN_TOUCHIT213
module will be called touchit213.
endif
+
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 53e5a52f225..1bbe8f5812f 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -32,3 +32,6 @@ wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
+obj-$(CONFIG_TOUCHSCREEN_FILTER) += ts_filter.o
+obj-$(CONFIG_TOUCHSCREEN_FILTER_MEDIAN) += ts_filter_median.o
+obj-$(CONFIG_TOUCHSCREEN_FILTER_MEAN) += ts_filter_mean.o
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
index ceca0886a80..774b71ba1dd 100644
--- a/drivers/input/touchscreen/s3c2410_ts.c
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -37,8 +37,9 @@
* 2007-05-23: Harald Welte <laforge@openmoko.org>
* - Add proper support for S32440
*
- * 2008-06-18: Andy Green <andy@openmoko.com>
- * - Outlier removal
+ * 2008-06-23: Andy Green <andy@openmoko.com>
+ * - removed averaging system
+ * - added generic Touchscreen filter stuff
*/
#include <linux/errno.h>
@@ -59,6 +60,8 @@
#include <plat/regs-adc.h>
+#include <linux/ts_filter.h>
+
/* For ts.dev.id.version */
#define S3C2410TSVERSION 0x0101
@@ -93,46 +96,16 @@ static char *s3c2410ts_name = "s3c2410 TouchScreen";
* Per-touchscreen data.
*/
-struct s3c2410ts_sample {
- int x;
- int y;
-};
-
struct s3c2410ts {
struct input_dev *dev;
- long xp;
- long yp;
- int count;
- int shift;
- int extent; /* 1 << shift */
-
- /* the raw sample fifo is a lightweight way to track a running average
- * of all taken samples. "running average" here means that it gives
- * correct average for each sample, not only at the end of block of
- * samples
- */
- int excursion_filter_len;
- struct s3c2410ts_sample *raw_sample_fifo;
- int head_raw_fifo;
- int tail_raw_fifo;
- struct s3c2410ts_sample raw_running_avg;
- int reject_threshold_vs_avg;
- int flag_previous_exceeded_threshold;
int flag_first_touch_sent;
+ struct ts_filter *tsf[MAX_TS_FILTER_CHAIN];
+ int coords[2]; /* just X and Y for us */
};
static struct s3c2410ts ts;
-static void __iomem *base_addr;
-static void clear_raw_fifo(void)
-{
- ts.head_raw_fifo = 0;
- ts.tail_raw_fifo = 0;
- ts.raw_running_avg.x = 0;
- ts.raw_running_avg.y = 0;
- ts.flag_previous_exceeded_threshold = 0;
- ts.flag_first_touch_sent = 0;
-}
+static void __iomem *base_addr;
static inline void s3c2410_ts_connect(void)
@@ -169,39 +142,35 @@ static void touch_timer_fire(unsigned long data)
}
if (updown) {
- if (ts.count != 0) {
- ts.xp >>= ts.shift;
- ts.yp >>= ts.shift;
+
+ if (ts.tsf[0])
+ (ts.tsf[0]->api->scale)(ts.tsf[0], &ts.coords[0]);
#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
- {
- struct timeval tv;
+ {
+ struct timeval tv;
- do_gettimeofday(&tv);
- printk(DEBUG_LVL "T:%06d, X:%03ld, Y:%03ld\n",
- (int)tv.tv_usec, ts.xp, ts.yp);
- }
+ do_gettimeofday(&tv);
+ printk(DEBUG_LVL "T:%06d, X:%03ld, Y:%03ld\n",
+ (int)tv.tv_usec, ts.coords[0], ts.coords[1]);
+ }
#endif
- input_report_abs(ts.dev, ABS_X, ts.xp);
- input_report_abs(ts.dev, ABS_Y, ts.yp);
+ input_report_abs(ts.dev, ABS_X, ts.coords[0]);
+ input_report_abs(ts.dev, ABS_Y, ts.coords[1]);
- input_report_key(ts.dev, BTN_TOUCH, 1);
- input_report_abs(ts.dev, ABS_PRESSURE, 1);
- input_sync(ts.dev);
- ts.flag_first_touch_sent = 1;
- }
-
- ts.xp = 0;
- ts.yp = 0;
- ts.count = 0;
+ input_report_key(ts.dev, BTN_TOUCH, 1);
+ input_report_abs(ts.dev, ABS_PRESSURE, 1);
+ input_sync(ts.dev);
writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
base_addr+S3C2410_ADCTSC);
writel(readl(base_addr+S3C2410_ADCCON) |
S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
} else {
- ts.count = 0;
+
+ if (ts.tsf[0])
+ (ts.tsf[0]->api->clear)(ts.tsf[0]);
input_report_key(ts.dev, BTN_TOUCH, 0);
input_report_abs(ts.dev, ABS_PRESSURE, 0);
@@ -237,103 +206,35 @@ static irqreturn_t stylus_updown(int irq, void *dev_id)
return IRQ_HANDLED;
}
-
static irqreturn_t stylus_action(int irq, void *dev_id)
{
- unsigned long x;
- unsigned long y;
- int length = (ts.head_raw_fifo - ts.tail_raw_fifo) & (ts.extent - 1);
- int scaled_avg_x;
- int scaled_avg_y;
-
- x = readl(base_addr + S3C2410_ADCDAT0) & S3C2410_ADCDAT0_XPDATA_MASK;
- y = readl(base_addr + S3C2410_ADCDAT1) & S3C2410_ADCDAT1_YPDATA_MASK;
-
- if (!length)
- goto store_sample;
-
- scaled_avg_x = ts.raw_running_avg.x / length;
- scaled_avg_y = ts.raw_running_avg.y / length;
-
- /* we appear to accept every sample into both the running average FIFO
- * and the summing average. BUT, if the last sample crossed a
- * machine-set threshold, each time we do a beauty contest
- * on the new sample comparing if it is closer to the running
- * average and the previous sample. If it is closer to the previous
- * suspicious sample, we assume the change is real and accept both
- * if the new sample has returned to being closer to the average than
- * the previous sample, we take the previous sample as an excursion
- * and overwrite it in both the running average and summing average.
+ /* grab the ADC results */
+ ts.coords[0] = readl(base_addr + S3C2410_ADCDAT0) &
+ S3C2410_ADCDAT0_XPDATA_MASK;
+ ts.coords[1] = readl(base_addr + S3C2410_ADCDAT1) &
+ S3C2410_ADCDAT1_YPDATA_MASK;
+
+ if (!ts.tsf[0]) /* filtering is disabled then use raw directly */
+ goto real_sample;
+
+ /* send it to the chain of filters */
+ if ((ts.tsf[0]->api->process)(ts.tsf[0], &ts.coords[0]))
+ goto real_sample;
+
+ /*
+ * no real sample came out of processing yet,
+ * get another raw result to feed it
*/
-
- if (ts.flag_previous_exceeded_threshold)
- /* new one closer to "nonconformist" previous, or average?
- * Pythagoras? Who? Don't need it because large excursion
- * will be accounted for correctly this way
- */
- if ((abs(x - scaled_avg_x) + abs(y - scaled_avg_y)) <
- (abs(x - ts.raw_sample_fifo[(ts.head_raw_fifo - 1) &
- (ts.extent - 1)].x) +
- abs(y - ts.raw_sample_fifo[(ts.head_raw_fifo - 1) &
- (ts.extent - 1)].y))) {
- /* it's closer to average, reject previous as a one-
- * shot excursion, by overwriting it
- */
- ts.xp += x - ts.raw_sample_fifo[(ts.head_raw_fifo - 1) &
- (ts.extent - 1)].x;
- ts.yp += y - ts.raw_sample_fifo[(ts.head_raw_fifo - 1) &
- (ts.extent - 1)].y;
- ts.raw_sample_fifo[(ts.head_raw_fifo - 1) &
- (ts.extent - 1)].x = x;
- ts.raw_sample_fifo[(ts.head_raw_fifo - 1) &
- (ts.extent - 1)].y = y;
- /* no new sample: replaced previous, so we are done */
- goto completed;
- }
- /* else it was closer to nonconformist previous: it's likely
- * a genuine consistent move then.
- * Keep previous and add new guy.
- */
-
- if ((x >= scaled_avg_x - ts.reject_threshold_vs_avg) &&
- (x <= scaled_avg_x + ts.reject_threshold_vs_avg) &&
- (y >= scaled_avg_y - ts.reject_threshold_vs_avg) &&
- (y <= scaled_avg_y + ts.reject_threshold_vs_avg))
- ts.flag_previous_exceeded_threshold = 0;
- else
- ts.flag_previous_exceeded_threshold = 1;
-
-store_sample:
- ts.xp += x;
- ts.yp += y;
- ts.count++;
-
- /* remove oldest sample from avg when we have full pipeline */
- if (((ts.head_raw_fifo + 1) & (ts.extent - 1)) == ts.tail_raw_fifo) {
- ts.raw_running_avg.x -= ts.raw_sample_fifo[ts.tail_raw_fifo].x;
- ts.raw_running_avg.y -= ts.raw_sample_fifo[ts.tail_raw_fifo].y;
- ts.tail_raw_fifo = (ts.tail_raw_fifo + 1) & (ts.extent - 1);
- }
- /* always add current sample to fifo and average */
- ts.raw_sample_fifo[ts.head_raw_fifo].x = x;
- ts.raw_sample_fifo[ts.head_raw_fifo].y = y;
- ts.raw_running_avg.x += x;
- ts.raw_running_avg.y += y;
- ts.head_raw_fifo = (ts.head_raw_fifo + 1) & (ts.extent - 1);
-
-completed:
- if (ts.count >= (1 << ts.shift)) {
- mod_timer(&touch_timer, jiffies + 1);
- writel(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
- goto bail;
- }
-
writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
- base_addr+S3C2410_ADCTSC);
- writel(readl(base_addr+S3C2410_ADCCON) |
- S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
+ base_addr + S3C2410_ADCTSC);
+ writel(readl(base_addr + S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START,
+ base_addr + S3C2410_ADCCON);
+ return IRQ_HANDLED;
+
+real_sample:
+ mod_timer(&touch_timer, jiffies + 1);
+ writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC);
-bail:
return IRQ_HANDLED;
}
@@ -348,6 +249,9 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
int rc;
struct s3c2410_ts_mach_info *info;
struct input_dev *input_dev;
+ int ret = 0;
+
+ dev_info(&pdev->dev, "Starting\n");
info = (struct s3c2410_ts_mach_info *)pdev->dev.platform_data;
@@ -375,7 +279,8 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
base_addr = ioremap(S3C2410_PA_ADC,0x20);
if (base_addr == NULL) {
dev_err(&pdev->dev, "Failed to remap register block\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto bail0;
}
@@ -390,7 +295,6 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
else
writel(0, base_addr+S3C2410_ADCCON);
-
/* Initialise registers */
if ((info->delay & 0xffff) > 0)
writel(info->delay & 0xffff, base_addr + S3C2410_ADCDLY);
@@ -403,7 +307,8 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
if (!input_dev) {
dev_err(&pdev->dev, "Unable to allocate the input device\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto bail1;
}
ts.dev = input_dev;
@@ -420,28 +325,33 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
ts.dev->id.product = 0xBEEF;
ts.dev->id.version = S3C2410TSVERSION;
- ts.shift = info->oversampling_shift;
- ts.extent = 1 << info->oversampling_shift;
- ts.reject_threshold_vs_avg = info->reject_threshold_vs_avg;
- ts.excursion_filter_len = 1 << info->excursion_filter_len_bits;
+ /* create the filter chain set up for the 2 coordinates we produce */
+ ret = ts_filter_create_chain(
+ (struct ts_filter_api **)&info->filter_sequence,
+ &info->filter_config, ts.tsf, ARRAY_SIZE(ts.coords));
+ if (ret)
+ dev_info(&pdev->dev, "%d filter(s) initialized\n", ret);
+ else /* this is OK, just means there won't be any filtering */
+ dev_info(&pdev->dev, "Unfiltered output selected\n");
- ts.raw_sample_fifo = kmalloc(sizeof(struct s3c2410ts_sample) *
- ts.excursion_filter_len, GFP_KERNEL);
- clear_raw_fifo();
+ if (!ts.tsf[0])
+ dev_info(&pdev->dev, "No filtering\n");
/* Get irqs */
if (request_irq(IRQ_ADC, stylus_action, IRQF_SAMPLE_RANDOM,
"s3c2410_action", ts.dev)) {
dev_err(&pdev->dev, "Could not allocate ts IRQ_ADC !\n");
iounmap(base_addr);
- return -EIO;
+ ret = -EIO;
+ goto bail3;
}
if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,
"s3c2410_action", ts.dev)) {
dev_err(&pdev->dev, "Could not allocate ts IRQ_TC !\n");
free_irq(IRQ_ADC, ts.dev);
iounmap(base_addr);
- return -EIO;
+ ret = -EIO;
+ goto bail4;
}
dev_info(&pdev->dev, "successfully loaded\n");
@@ -453,10 +363,25 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
free_irq(IRQ_ADC, ts.dev);
clk_disable(adc_clock);
iounmap(base_addr);
- return -EIO;
+ ret = -EIO;
+ goto bail5;
}
return 0;
+
+bail5:
+ disable_irq(IRQ_TC);
+bail4:
+ disable_irq(IRQ_ADC);
+bail3:
+ ts_filter_destroy_chain(ts.tsf);
+
+ input_unregister_device(ts.dev);
+bail1:
+ iounmap(base_addr);
+bail0:
+
+ return ret;
}
static int s3c2410ts_remove(struct platform_device *pdev)
@@ -472,11 +397,11 @@ static int s3c2410ts_remove(struct platform_device *pdev)
adc_clock = NULL;
}
- kfree(ts.raw_sample_fifo);
-
input_unregister_device(ts.dev);
iounmap(base_addr);
+ ts_filter_destroy_chain(ts.tsf);
+
return 0;
}
@@ -503,7 +428,8 @@ static int s3c2410ts_resume(struct platform_device *pdev)
clk_enable(adc_clock);
mdelay(1);
- clear_raw_fifo();
+ if (ts.tsf[0])
+ (ts.tsf[0]->api->clear)(ts.tsf[0]);
enable_irq(IRQ_ADC);
enable_irq(IRQ_TC);
diff --git a/drivers/input/touchscreen/ts_filter.c b/drivers/input/touchscreen/ts_filter.c
new file mode 100644
index 00000000000..f8b2b2f5846
--- /dev/null
+++ b/drivers/input/touchscreen/ts_filter.c
@@ -0,0 +1,57 @@
+/*
+ * 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>
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/ts_filter.h>
+
+int ts_filter_create_chain(struct ts_filter_api **api, void **config,
+ struct ts_filter **list, int count_coords)
+{
+ int count = 0;
+ struct ts_filter *last = NULL;
+
+ if (!api)
+ return 0;
+
+ while (*api && (count < MAX_TS_FILTER_CHAIN)) {
+ *list = ((*api)->create)(*config++, count_coords);
+ if (!*list) {
+ printk(KERN_ERR "Filter %d failed init\n", count);
+ return count;
+ }
+ (*list)->api = (struct ts_filter_api *)*api++;
+ if (last)
+ last->next = *list;
+ last = *list;
+ list++;
+ count++;
+ }
+
+ return count;
+}
+EXPORT_SYMBOL_GPL(ts_filter_create_chain);
+
+void ts_filter_destroy_chain(struct ts_filter **list)
+{
+ while (*list) {
+ ((*list)->api->destroy)(*list);
+ list++;
+ }
+}
+EXPORT_SYMBOL_GPL(ts_filter_destroy_chain);
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,
+};
diff --git a/drivers/input/touchscreen/ts_filter_median.c b/drivers/input/touchscreen/ts_filter_median.c
new file mode 100644
index 00000000000..fe1b35f110b
--- /dev/null
+++ b/drivers/input/touchscreen/ts_filter_median.c
@@ -0,0 +1,210 @@
+/*
+ * 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>
+ *
+ *
+ * Median averaging stuff. We sort incoming raw samples into an array of
+ * MEDIAN_SIZE length, discarding the oldest sample each time once we are full.
+ * We then return the sum of the middle three samples for X and Y. It means
+ * the final result must be divided by (3 * scaling factor) to correct for
+ * avoiding the repeated /3.
+ *
+ * This strongly rejects brief excursions away from a central point that is
+ * sticky in time compared to the excursion duration.
+ *
+ * Thanks to Dale Schumacher (who wrote some example code) and Carl-Daniel
+ * Halifinger who pointed out this would be a good method.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/ts_filter_median.h>
+
+static void ts_filter_median_insert(int *p, int sample, int count)
+{
+ int n;
+
+ /* search through what we got so far to find where to put sample */
+ for (n = 0; n < count; n++)
+ /* we met somebody bigger than us? */
+ if (sample < p[n]) {
+ /* starting from the end, push bigger guys down one */
+ for (count--; count >= n; count--)
+ p[count + 1] = p[count];
+ p[n] = sample; /* and put us in place of first bigger */
+ return;
+ }
+
+ p[count] = sample; /* nobody was bigger than us, add us on the end */
+}
+
+static void ts_filter_median_del(int *p, int value, int count)
+{
+ int index;
+
+ for (index = 0; index < count; index++)
+ if (p[index] == value) {
+ for (; index < count; index++)
+ p[index] = p[index + 1];
+ return;
+ }
+}
+
+
+static void ts_filter_median_clear(struct ts_filter *tsf)
+{
+ struct ts_filter_median *tsfm = (struct ts_filter_median *)tsf;
+
+ tsfm->pos = 0;
+ tsfm->valid = 0;
+
+ if (tsf->next) /* chain */
+ (tsf->next->api->clear)(tsf->next);
+}
+
+static struct ts_filter *ts_filter_median_create(void * conf, int count_coords)
+{
+ int *p;
+ int n;
+ struct ts_filter_median *tsfm = kzalloc(sizeof(struct ts_filter_median),
+ GFP_KERNEL);
+
+ if (!tsfm)
+ return NULL;
+
+ tsfm->config = (struct ts_filter_median_configuration *)conf;
+ BUG_ON((count_coords < 1) || (count_coords > MAX_TS_FILTER_COORDS));
+ tsfm->tsf.count_coords = count_coords;
+
+ tsfm->config->midpoint = (tsfm->config->extent >> 1) + 1;
+
+ printk(KERN_INFO" Creating Median ts filter len %d depth %d dec %d\n",
+ tsfm->config->extent, count_coords,
+ tsfm->config->decimation_threshold);
+
+ p = kmalloc(2 * count_coords * sizeof(int) * (tsfm->config->extent + 1),
+ GFP_KERNEL);
+ if (!p) {
+ kfree(tsfm);
+ return NULL;
+ }
+
+ for (n = 0; n < count_coords; n++) {
+ tsfm->sort[n] = p;
+ p += tsfm->config->extent + 1;
+ tsfm->fifo[n] = p;
+ p += tsfm->config->extent + 1;
+ }
+
+ ts_filter_median_clear(&tsfm->tsf);
+
+ return &tsfm->tsf;
+}
+
+static void ts_filter_median_destroy(struct ts_filter *tsf)
+{
+ struct ts_filter_median *tsfm = (struct ts_filter_median *)tsf;
+
+ if (tsf->next) /* chain */
+ (tsf->next->api->destroy)(tsf->next);
+
+ kfree(tsfm->sort[0]); /* first guy has pointer from kmalloc */
+ kfree(tsf);
+}
+
+static void ts_filter_median_scale(struct ts_filter *tsf, int *coords)
+{
+ int n;
+
+ for (n = 0; n < tsf->count_coords; n++)
+ coords[n] = (coords[n] + 2) / 3;
+
+ if (tsf->next) /* chain */
+ (tsf->next->api->scale)(tsf->next, coords);
+}
+
+/* give us the raw sample data coords, and if we return 1 then you can
+ * get a filtered coordinate from coords: if we return 0 you didn't
+ * fill all the filters with samples yet.
+ */
+
+static int ts_filter_median_process(struct ts_filter *tsf, int *coords)
+{
+ struct ts_filter_median *tsfm = (struct ts_filter_median *)tsf;
+ int n;
+ int movement = 1;
+
+ for (n = 0; n < tsf->count_coords; n++) {
+ /* grab copy in insertion order to remove when oldest */
+ tsfm->fifo[n][tsfm->pos] = coords[n];
+ /* insert these samples in sorted order in the median arrays */
+ ts_filter_median_insert(tsfm->sort[n], coords[n], tsfm->valid);
+ }
+ /* move us on in the fifo */
+ if (++tsfm->pos == (tsfm->config->extent + 1))
+ tsfm->pos = 0;
+
+ /* we have finished a median sampling? */
+ if (++tsfm->valid != tsfm->config->extent)
+ return 0; /* no valid sample to use */
+
+ /* discard the oldest sample in median sorted array */
+ tsfm->valid--;
+
+ /* sum the middle 3 in the median sorted arrays. We don't divide back
+ * down which increases the sum resolution by a factor of 3 until the
+ * scale API is called
+ */
+ for (n = 0; n < tsfm->tsf.count_coords; n++)
+ /* perform the deletion of the oldest sample */
+ ts_filter_median_del(tsfm->sort[n], tsfm->fifo[n][tsfm->pos],
+ tsfm->valid);
+
+ tsfm->decimation_count--;
+ if (tsfm->decimation_count >= 0)
+ return 0;
+
+ for (n = 0; n < tsfm->tsf.count_coords; n++) {
+ /* give the coordinate result from summing median 3 */
+ coords[n] = tsfm->sort[n][tsfm->config->midpoint - 1] +
+ tsfm->sort[n][tsfm->config->midpoint] +
+ tsfm->sort[n][tsfm->config->midpoint + 1]
+ ;
+
+ movement += abs(tsfm->last_issued[n] - coords[n]);
+ }
+
+ if (movement > tsfm->config->decimation_threshold) /* fast */
+ tsfm->decimation_count = tsfm->config->decimation_above;
+ else
+ tsfm->decimation_count = tsfm->config->decimation_below;
+
+ memcpy(&tsfm->last_issued, coords, tsfm->tsf.count_coords);
+
+ if (tsf->next) /* chain */
+ return (tsf->next->api->process)(tsf->next, coords);
+ else
+ return 1;
+}
+
+struct ts_filter_api ts_filter_median_api = {
+ .create = ts_filter_median_create,
+ .destroy = ts_filter_median_destroy,
+ .clear = ts_filter_median_clear,
+ .process = ts_filter_median_process,
+ .scale = ts_filter_median_scale,
+};
diff --git a/include/linux/ts_filter.h b/include/linux/ts_filter.h
new file mode 100644
index 00000000000..7262bbaf3dd
--- /dev/null
+++ b/include/linux/ts_filter.h
@@ -0,0 +1,53 @@
+#ifndef __TS_FILTER_H__
+#define __TS_FILTER_H__
+
+/*
+ * touchscreen filter
+ *
+ * median
+ *
+ * (c) 2008 Andy Green <andy@openmoko.com>
+ */
+
+/* max filters you can chain up */
+#define MAX_TS_FILTER_CHAIN 4
+#define MAX_TS_FILTER_COORDS 6
+
+struct ts_filter;
+
+/* operations that a filter can perform
+ */
+struct ts_filter_api {
+ struct ts_filter * (*create)(void *config, int count_coords);
+ void (*destroy)(struct ts_filter *filter);
+ void (*clear)(struct ts_filter *filter);
+ int (*process)(struct ts_filter *filter, int *coords);
+ void (*scale)(struct ts_filter *filter, int *coords);
+};
+
+/* this is the common part of all filters, the result
+ * we use this type as an otherwise opaque handle on to
+ * the actual filter. Therefore you need one of these
+ * at the start of your actual filter struct
+ */
+
+struct ts_filter {
+ struct ts_filter *next; /* next in chain */
+ struct ts_filter_api *api; /* operations to use for this object */
+ int count_coords;
+ int coords[MAX_TS_FILTER_COORDS];
+};
+
+/*
+ * helper to create a filter chain from array of API pointers and
+ * array of config ints... leaves pointers to created filters in list
+ * array and fills in ->next pointers to create the chain
+ */
+
+extern int ts_filter_create_chain(struct ts_filter_api **api, void **config,
+ struct ts_filter **list, int count_coords);
+
+/* helper to destroy a whole chain from the list of filter pointers */
+
+extern void ts_filter_destroy_chain(struct ts_filter **list);
+#endif
diff --git a/include/linux/ts_filter_mean.h b/include/linux/ts_filter_mean.h
new file mode 100644
index 00000000000..46ff01a4c82
--- /dev/null
+++ b/include/linux/ts_filter_mean.h
@@ -0,0 +1,34 @@
+#ifndef __TS_FILTER_MEAN_H__
+#define __TS_FILTER_MEAN_H__
+
+#include <linux/ts_filter.h>
+
+/*
+ * touchscreen filter
+ *
+ * mean
+ *
+ * (c) 2008 Andy Green <andy@openmoko.com>
+ */
+
+struct ts_filter_mean_configuration {
+ int bits_filter_length;
+ int averaging_threshold;
+
+ int extent;
+};
+
+struct ts_filter_mean {
+ struct ts_filter tsf;
+ struct ts_filter_mean_configuration *config;
+
+ int reported[MAX_TS_FILTER_COORDS];
+ int lowpass[MAX_TS_FILTER_COORDS];
+ int *fifo[MAX_TS_FILTER_COORDS];
+ int fhead[MAX_TS_FILTER_COORDS];
+ int ftail[MAX_TS_FILTER_COORDS];
+};
+
+extern struct ts_filter_api ts_filter_mean_api;
+
+#endif
diff --git a/include/linux/ts_filter_median.h b/include/linux/ts_filter_median.h
new file mode 100644
index 00000000000..d81642883f7
--- /dev/null
+++ b/include/linux/ts_filter_median.h
@@ -0,0 +1,36 @@
+#ifndef __TS_FILTER_MEDIAN_H__
+#define __TS_FILTER_MEDIAN_H__
+
+#include <linux/ts_filter.h>
+
+/*
+ * touchscreen filter
+ *
+ * median
+ *
+ * (c) 2008 Andy Green <andy@openmoko.com>
+ */
+
+struct ts_filter_median_configuration {
+ int extent;
+ int midpoint;
+ int decimation_threshold;
+ int decimation_above;
+ int decimation_below;
+};
+
+struct ts_filter_median {
+ struct ts_filter tsf;
+ struct ts_filter_median_configuration *config;
+
+ int decimation_count;
+ int last_issued[MAX_TS_FILTER_COORDS];
+ int valid; /* how many samples in the sort buffer are valid */
+ int *sort[MAX_TS_FILTER_COORDS]; /* samples taken for median */
+ int *fifo[MAX_TS_FILTER_COORDS]; /* samples taken for median */
+ int pos; /* where we are in the fifo sample memory */
+};
+
+extern struct ts_filter_api ts_filter_median_api;
+
+#endif