summaryrefslogtreecommitdiff
path: root/target/linux/s3c24xx/patches/0188-touchscreen-meddling.patch.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/s3c24xx/patches/0188-touchscreen-meddling.patch.patch')
-rwxr-xr-xtarget/linux/s3c24xx/patches/0188-touchscreen-meddling.patch.patch505
1 files changed, 505 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches/0188-touchscreen-meddling.patch.patch b/target/linux/s3c24xx/patches/0188-touchscreen-meddling.patch.patch
new file mode 100755
index 0000000000..a5e2d84b88
--- /dev/null
+++ b/target/linux/s3c24xx/patches/0188-touchscreen-meddling.patch.patch
@@ -0,0 +1,505 @@
+From 76d6ab9228efca87d3e6db46e918c7bbf19f6540 Mon Sep 17 00:00:00 2001
+From: Andy Green <andy@openmoko.com>
+Date: Fri, 25 Jul 2008 23:06:16 +0100
+Subject: [PATCH] touchscreen-meddling.patch
+
+Touchscreen on GTA01-02 experiences noise on the channel that serves the
+"tall axis" of the LCM. The sample quality of the other axis is good.
+The bad samples have a characteristic of one shot excursions that can
+reach +/- 20% or more of the sample average.
+
+Previously, we had a simple averaging scheme going in the touchscreen
+driver that summed up 32 x and ys and then divided it by 32. This patch
+first tidies up the existing code for style, then adds a new "running
+average" concept with a FIFO. The running average is separate from the
+summing average mentioned above, and is accurate for the last n samples
+sample-by-sample, where n is set by 1 << excursion_filter_len_bits in the
+machine / platform stuff.
+
+The heuristic the patch implements for the filtering is to accept all
+samples, but tag the *previous* sample with a flag if it differed from
+the running average by more than reject_threshold_vs_avg in either
+axis. The next sample time, a beauty contest is held if the flag was
+set to decide if we think the previous sample was a one-shot excursion
+(detected by the new sample being closer to the average than to the
+flagged previous sample), or if we believe we are moving (detected by
+the new sample being closer to the flagged previous sample than the
+average. In the case that we believe the previous sample was an
+excursion, we simply overwrite it with the new data and adjust the
+summing average to use the new data instead of the excursion data.
+
+I only tested this by eyeballing the output of ts_print_raw, but it
+seemed to be quite a bit better. Gross movement appeared to be
+tracked fine too. If folks want to try different heuristics on top
+of this patch, be my guest; either way feedback on what it looks like
+with a graphical app would be good.
+
+Signed-off-by: Andy Green <andy@openmoko.com>
+---
+ arch/arm/mach-s3c2440/mach-gta02.c | 10 +-
+ drivers/input/touchscreen/s3c2410_ts.c | 256 ++++++++++++++++++++++++--------
+ include/asm-arm/arch-s3c2410/ts.h | 8 +-
+ 3 files changed, 205 insertions(+), 69 deletions(-)
+
+diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c
+index c32bb2a..afe8039 100644
+--- a/arch/arm/mach-s3c2440/mach-gta02.c
++++ b/arch/arm/mach-s3c2440/mach-gta02.c
+@@ -897,10 +897,18 @@ static struct s3c2410_udc_mach_info gta02_udc_cfg = {
+
+ static struct s3c2410_ts_mach_info gta02_ts_cfg = {
+ .delay = 10000,
+- .presc = 65,
++ .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,
+ };
+
++
+ /* SPI: LCM control interface attached to Glamo3362 */
+
+ static void gta02_jbt6k74_reset(int devidx, int level)
+diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
+index 1251454..b1ba73d 100644
+--- a/drivers/input/touchscreen/s3c2410_ts.c
++++ b/drivers/input/touchscreen/s3c2410_ts.c
+@@ -36,6 +36,9 @@
+ *
+ * 2007-05-23: Harald Welte <laforge@openmoko.org>
+ * - Add proper support for S32440
++ *
++ * 2008-06-18: Andy Green <andy@openmoko.com>
++ * - Outlier removal
+ */
+
+ #include <linux/errno.h>
+@@ -62,11 +65,16 @@
+ #define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0))
+
+ #define WAIT4INT(x) (((x)<<8) | \
+- S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
++ S3C2410_ADCTSC_YM_SEN | \
++ S3C2410_ADCTSC_YP_SEN | \
++ S3C2410_ADCTSC_XP_SEN | \
+ S3C2410_ADCTSC_XY_PST(3))
+
+-#define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
+- S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
++#define AUTOPST (S3C2410_ADCTSC_YM_SEN | \
++ S3C2410_ADCTSC_YP_SEN | \
++ S3C2410_ADCTSC_XP_SEN | \
++ S3C2410_ADCTSC_AUTO_PST | \
++ S3C2410_ADCTSC_XY_PST(0))
+
+ #define DEBUG_LVL KERN_DEBUG
+
+@@ -85,17 +93,46 @@ 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;
+ };
+
+ 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;
++}
++
++
+ static inline void s3c2410_ts_connect(void)
+ {
+ s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);
+@@ -110,47 +147,52 @@ static void touch_timer_fire(unsigned long data)
+ unsigned long data1;
+ int updown;
+
+- data0 = readl(base_addr+S3C2410_ADCDAT0);
+- data1 = readl(base_addr+S3C2410_ADCDAT1);
++ data0 = readl(base_addr + S3C2410_ADCDAT0);
++ data1 = readl(base_addr + S3C2410_ADCDAT1);
+
+- updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
++ updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) &&
++ (!(data1 & S3C2410_ADCDAT0_UPDOWN));
+
+- if (updown) {
+- if (ts.count != 0) {
+- ts.xp >>= ts.shift;
+- ts.yp >>= ts.shift;
++ if (updown) {
++ if (ts.count != 0) {
++ ts.xp >>= ts.shift;
++ ts.yp >>= ts.shift;
+
+ #ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
+- {
+- struct timeval tv;
+- do_gettimeofday(&tv);
+- printk(DEBUG_LVL "T: %06d, X: %03ld, Y: %03ld\n", (int)tv.tv_usec, ts.xp, ts.yp);
+- }
++ {
++ struct timeval tv;
++
++ do_gettimeofday(&tv);
++ printk(DEBUG_LVL "T:%06d, X:%03ld, Y:%03ld\n",
++ (int)tv.tv_usec, ts.xp, ts.yp);
++ }
+ #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.xp);
++ input_report_abs(ts.dev, ABS_Y, ts.yp);
+
+- input_report_key(ts.dev, BTN_TOUCH, 1);
+- input_report_abs(ts.dev, ABS_PRESSURE, 1);
+- input_sync(ts.dev);
+- }
++ input_report_key(ts.dev, BTN_TOUCH, 1);
++ input_report_abs(ts.dev, ABS_PRESSURE, 1);
++ input_sync(ts.dev);
++ }
+
+- ts.xp = 0;
+- ts.yp = 0;
+- ts.count = 0;
++ ts.xp = 0;
++ ts.yp = 0;
++ ts.count = 0;
+
+- 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;
++ 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;
+
+- input_report_key(ts.dev, BTN_TOUCH, 0);
+- input_report_abs(ts.dev, ABS_PRESSURE, 0);
+- input_sync(ts.dev);
++ input_report_key(ts.dev, BTN_TOUCH, 0);
++ input_report_abs(ts.dev, ABS_PRESSURE, 0);
++ input_sync(ts.dev);
+
+- writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
+- }
++ writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
++ }
+ }
+
+ static struct timer_list touch_timer =
+@@ -165,7 +207,8 @@ static irqreturn_t stylus_updown(int irq, void *dev_id)
+ data0 = readl(base_addr+S3C2410_ADCDAT0);
+ data1 = readl(base_addr+S3C2410_ADCDAT1);
+
+- updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
++ updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) &&
++ (!(data1 & S3C2410_ADCDAT0_UPDOWN));
+
+ /* TODO we should never get an interrupt with updown set while
+ * the timer is running, but maybe we ought to verify that the
+@@ -180,24 +223,94 @@ static irqreturn_t stylus_updown(int irq, void *dev_id)
+
+ static irqreturn_t stylus_action(int irq, void *dev_id)
+ {
+- unsigned long data0;
+- unsigned long data1;
+-
+- data0 = readl(base_addr+S3C2410_ADCDAT0);
+- data1 = readl(base_addr+S3C2410_ADCDAT1);
++ unsigned long x;
++ unsigned long y;
++ int length = (ts.head_raw_fifo - ts.tail_raw_fifo) & (ts.extent - 1);
++ int scaled_avg_x = ts.raw_running_avg.x / length;
++ int scaled_avg_y = ts.raw_running_avg.y / length;
++
++ x = readl(base_addr + S3C2410_ADCDAT0) & S3C2410_ADCDAT0_XPDATA_MASK;
++ y = readl(base_addr + S3C2410_ADCDAT1) & S3C2410_ADCDAT1_YPDATA_MASK;
++
++ /* 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.
++ */
++
++ 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;
+
+- ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
+- ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
++ /* accepted */
++ ts.xp += x;
++ ts.yp += y;
+ ts.count++;
+
+- if (ts.count < (1<<ts.shift)) {
+- 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 {
+- mod_timer(&touch_timer, jiffies+1);
++ /* 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);
++
++bail:
+ return IRQ_HANDLED;
+ }
+
+@@ -213,11 +326,11 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
+ struct s3c2410_ts_mach_info *info;
+ struct input_dev *input_dev;
+
+- info = ( struct s3c2410_ts_mach_info *)pdev->dev.platform_data;
++ info = (struct s3c2410_ts_mach_info *)pdev->dev.platform_data;
+
+ if (!info)
+ {
+- printk(KERN_ERR "Hm... too bad : no platform data for ts\n");
++ dev_err(&pdev->dev, "Hm... too bad: no platform data for ts\n");
+ return -EINVAL;
+ }
+
+@@ -227,7 +340,7 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
+
+ adc_clock = clk_get(NULL, "adc");
+ if (!adc_clock) {
+- printk(KERN_ERR "failed to get adc clock source\n");
++ dev_err(&pdev->dev, "failed to get adc clock source\n");
+ return -ENOENT;
+ }
+ clk_enable(adc_clock);
+@@ -238,7 +351,7 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
+
+ base_addr = ioremap(S3C2410_PA_ADC,0x20);
+ if (base_addr == NULL) {
+- printk(KERN_ERR "Failed to remap register block\n");
++ dev_err(&pdev->dev, "Failed to remap register block\n");
+ return -ENOMEM;
+ }
+
+@@ -247,25 +360,26 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
+ if (!strcmp(pdev->name, "s3c2410-ts"))
+ s3c2410_ts_connect();
+
+- if ((info->presc&0xff) > 0)
+- writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\
+- base_addr+S3C2410_ADCCON);
++ if ((info->presc & 0xff) > 0)
++ writel(S3C2410_ADCCON_PRSCEN |
++ S3C2410_ADCCON_PRSCVL(info->presc&0xFF),
++ base_addr + S3C2410_ADCCON);
+ else
+- writel(0,base_addr+S3C2410_ADCCON);
++ writel(0, base_addr+S3C2410_ADCCON);
+
+
+ /* Initialise registers */
+- if ((info->delay&0xffff) > 0)
+- writel(info->delay & 0xffff, base_addr+S3C2410_ADCDLY);
++ if ((info->delay & 0xffff) > 0)
++ writel(info->delay & 0xffff, base_addr + S3C2410_ADCDLY);
+
+- writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
++ writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC);
+
+ /* Initialise input stuff */
+ memset(&ts, 0, sizeof(struct s3c2410ts));
+ input_dev = input_allocate_device();
+
+ if (!input_dev) {
+- printk(KERN_ERR "Unable to allocate the input device !!\n");
++ dev_err(&pdev->dev, "Unable to allocate the input device\n");
+ return -ENOMEM;
+ }
+
+@@ -284,23 +398,30 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
+ 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;
++
++ ts.raw_sample_fifo = kmalloc(sizeof(struct s3c2410ts_sample) *
++ ts.excursion_filter_len, GFP_KERNEL);
++ clear_raw_fifo();
+
+ /* Get irqs */
+ if (request_irq(IRQ_ADC, stylus_action, IRQF_SAMPLE_RANDOM,
+- "s3c2410_action", ts.dev)) {
+- printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_ADC !\n");
++ "s3c2410_action", ts.dev)) {
++ dev_err(&pdev->dev, "Could not allocate ts IRQ_ADC !\n");
+ iounmap(base_addr);
+ return -EIO;
+ }
+ if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,
+ "s3c2410_action", ts.dev)) {
+- printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !\n");
++ dev_err(&pdev->dev, "Could not allocate ts IRQ_TC !\n");
+ free_irq(IRQ_ADC, ts.dev);
+ iounmap(base_addr);
+ return -EIO;
+ }
+
+- printk(KERN_INFO "%s successfully loaded\n", s3c2410ts_name);
++ dev_info(&pdev->dev, "successfully loaded\n");
+
+ /* All went ok, so register to the input system */
+ rc = input_register_device(ts.dev);
+@@ -328,6 +449,8 @@ static int s3c2410ts_remove(struct platform_device *pdev)
+ adc_clock = NULL;
+ }
+
++ kfree(ts.raw_sample_fifo);
++
+ input_unregister_device(ts.dev);
+ iounmap(base_addr);
+
+@@ -357,17 +480,20 @@ static int s3c2410ts_resume(struct platform_device *pdev)
+ clk_enable(adc_clock);
+ mdelay(1);
+
++ clear_raw_fifo();
++
+ enable_irq(IRQ_ADC);
+ enable_irq(IRQ_TC);
+
+ if ((info->presc&0xff) > 0)
+- writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\
+- base_addr+S3C2410_ADCCON);
++ writel(S3C2410_ADCCON_PRSCEN |
++ S3C2410_ADCCON_PRSCVL(info->presc&0xFF),
++ base_addr+S3C2410_ADCCON);
+ else
+ writel(0,base_addr+S3C2410_ADCCON);
+
+ /* Initialise registers */
+- if ((info->delay&0xffff) > 0)
++ if ((info->delay & 0xffff) > 0)
+ writel(info->delay & 0xffff, base_addr+S3C2410_ADCDLY);
+
+ writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
+diff --git a/include/asm-arm/arch-s3c2410/ts.h b/include/asm-arm/arch-s3c2410/ts.h
+index 593632a..44c1e4b 100644
+--- a/include/asm-arm/arch-s3c2410/ts.h
++++ b/include/asm-arm/arch-s3c2410/ts.h
+@@ -17,9 +17,11 @@
+ #define __ASM_ARM_TS_H
+
+ struct s3c2410_ts_mach_info {
+- int delay;
+- int presc;
+- int oversampling_shift;
++ int delay;
++ int presc;
++ int oversampling_shift;
++ int excursion_filter_len_bits;
++ int reject_threshold_vs_avg;
+ };
+
+ void set_s3c2410ts_info(struct s3c2410_ts_mach_info *hard_s3c2410ts_info);
+--
+1.5.6.3
+