Here's a patch that makes my freerunner's touchscreen produce more
consistent, better data. Andy is working on a different patch to do
this. With my patch, I can type on the matchbox-keyboard with my
fingertips. Currently the issues are that some taps are too soft to be
picked up, and the cursor is slugging when dragging it quickly. Another
issue is the lack of click debouncing. Without this, many soft taps and
soft drags generate spurious, quick "stylus up" events. If a stylus is
lifted for less than x milliseconds, we should ignore that lift event.
The main functional changes in my patch are
1. A lifted stylus always causes big jumps in the data, so when we see
those, we check for a lifted stylus.
2. If we do detect a lifted stylus, we do not use the erratic points in
the averaging
3. reject_threshold_vs_avg_x was previously looking for jumps larger
than 5 pixels and throwing those away. Our hardware is much less
consistent than this. I now look for jumps 70 pixels in the long
direction and 20 pixels in the short direction, and throwing those
away.
4. If I see a click event with less than 2 points in its data, I don't
send it.
I've done all my testing with no user-side filtering (variance
and dejitter disabled in /etc/ts.conf). User-side fitlering may make it
better or worse. Let me know what you think.
Dima
diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c
index c15f072..f47973e 100644
--- a/arch/arm/mach-s3c2440/mach-gta02.c
+++ b/arch/arm/mach-s3c2440/mach-gta02.c
@@ -929,14 +929,16 @@ static struct s3c2410_udc_mach_info gta02_udc_cfg = {
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,
+ /* averaging filter length, 2^n */
+ .oversampling_shift = 4,
+
/* flagged for beauty contest on next sample if differs from
* average more than this
*/
- .reject_threshold_vs_avg = 2,
+ .reject_threshold_vs_avg_x = 20,
+ .reject_threshold_vs_avg_y = 70,
+
+ .min_number_of_points_to_use = 2
};
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
index fc1c500..0093125 100644
--- a/drivers/input/touchscreen/s3c2410_ts.c
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -39,6 +39,9 @@
*
* 2008-06-18: Andy Green <[EMAIL PROTECTED]>
* - Outlier removal
+ *
+ * 2008-09-14: Dima Kogan <[EMAIL PROTECTED]>
+ * - Cleanup, reworked filtering
*/
#include <linux/errno.h>
@@ -100,8 +103,6 @@ struct s3c2410ts_sample {
struct s3c2410ts {
struct input_dev *dev;
- long xp;
- long yp;
int count;
int shift;
int extent; /* 1 << shift */
@@ -111,14 +112,14 @@ struct s3c2410ts {
* 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 reject_threshold_vs_avg_x;
+ int reject_threshold_vs_avg_y;
int flag_previous_exceeded_threshold;
- int flag_first_touch_sent;
+ int min_number_of_points_to_use;
};
static struct s3c2410ts ts;
@@ -131,7 +132,7 @@ static void clear_raw_fifo(void)
ts.raw_running_avg.x = 0;
ts.raw_running_avg.y = 0;
ts.flag_previous_exceeded_threshold = 0;
- ts.flag_first_touch_sent = 0;
+ ts.count = 0;
}
@@ -143,71 +144,85 @@ static inline void s3c2410_ts_connect(void)
s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);
}
+static int is_stylus_down(void)
+{
+ unsigned long data0 = readl(base_addr+S3C2410_ADCDAT0);
+ unsigned long data1 = readl(base_addr+S3C2410_ADCDAT1);
+
+ return (!(data0 & S3C2410_ADCDAT0_UPDOWN)) &&
+ (!(data1 & S3C2410_ADCDAT0_UPDOWN));
+}
+
+static void wait_for_datapoint(void)
+{
+ 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);
+}
+
static void touch_timer_fire(unsigned long data)
{
unsigned long data0;
unsigned long data1;
int updown;
+ int length = (ts.head_raw_fifo - ts.tail_raw_fifo) & (ts.extent - 1);
data0 = readl(base_addr + S3C2410_ADCDAT0);
data1 = readl(base_addr + S3C2410_ADCDAT1);
- updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) &&
- (!(data1 & S3C2410_ADCDAT0_UPDOWN));
+ updown = is_stylus_down();
+ if (updown && ts.count < ts.extent)
+ {
+ // if we need to gather more data and the stylus is
+ // still down, get more data
+ wait_for_datapoint();
+ return;
+ }
- // if we need to send an untouch event, but we haven't yet sent the
- // touch event (this happens if the touchscreen was tapped lightly),
- // send the touch event first
- if (!updown && !ts.flag_first_touch_sent && ts.count != 0) {
- input_report_abs(ts.dev, ABS_X, ts.xp >> ts.shift);
- input_report_abs(ts.dev, ABS_Y, ts.yp >> ts.shift);
+ // if we are still touching the screen, output the touch event
+ // if we have lifted the stylus, output the touch event if we have
+ // enough data points for our measurement
+ if ( updown || (!updown && length >= ts.min_number_of_points_to_use) )
+ {
+ int x,y;
+ // if the previous point was a jump, ignore it
+ if(ts.flag_previous_exceeded_threshold)
+ {
+ x = (ts.raw_running_avg.x - ts.raw_sample_fifo[(ts.head_raw_fifo - 1) &
+ (ts.extent - 1)].x) / (length - 1);
+ y = (ts.raw_running_avg.y - ts.raw_sample_fifo[(ts.head_raw_fifo - 1) &
+ (ts.extent - 1)].y) / (length - 1);
+ }
+ else
+ {
+ x = ts.raw_running_avg.x / length;
+ y = ts.raw_running_avg.y / length;
+ }
+
+ input_report_abs(ts.dev, ABS_X, x);
+ input_report_abs(ts.dev, ABS_Y, y);
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;
- }
-
- 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);
- }
+ printk(DEBUG_LVL "d: 0 0 %d %d\n", x, y);
#endif
+ }
- 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);
- ts.flag_first_touch_sent = 1;
- }
-
- ts.xp = 0;
- ts.yp = 0;
+ if (updown) {
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);
+ wait_for_datapoint();
} else {
- ts.count = 0;
-
input_report_key(ts.dev, BTN_TOUCH, 0);
input_report_abs(ts.dev, ABS_PRESSURE, 0);
input_sync(ts.dev);
- ts.flag_first_touch_sent = 0;
+ clear_raw_fifo();
+#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
+ printk(DEBUG_LVL "d: 0 0 0 0\n");
+#endif
writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
}
}
@@ -217,23 +232,12 @@ static struct timer_list touch_timer =
static irqreturn_t stylus_updown(int irq, void *dev_id)
{
- unsigned long data0;
- unsigned long data1;
- int updown;
-
- data0 = readl(base_addr+S3C2410_ADCDAT0);
- data1 = readl(base_addr+S3C2410_ADCDAT1);
-
- 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
* timer isn't running anyways. */
- if (updown)
- touch_timer_fire(0);
-
+ if (is_stylus_down())
+ wait_for_datapoint();
return IRQ_HANDLED;
}
@@ -249,6 +253,10 @@ static irqreturn_t stylus_action(int irq, void *dev_id)
x = readl(base_addr + S3C2410_ADCDAT0) & S3C2410_ADCDAT0_XPDATA_MASK;
y = readl(base_addr + S3C2410_ADCDAT1) & S3C2410_ADCDAT1_YPDATA_MASK;
+#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
+ printk(DEBUG_LVL "d: %ld %ld 0 0\n", x, y);
+#endif
+
if (!length)
goto store_sample;
@@ -266,74 +274,72 @@ static irqreturn_t stylus_action(int irq, void *dev_id)
* 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
+ /* 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 (ts.flag_previous_exceeded_threshold &&
+ ((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
*/
- 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;
+ ts.raw_running_avg.x += x - ts.raw_sample_fifo[(ts.head_raw_fifo - 1) &
+ (ts.extent - 1)].x;
+ ts.raw_running_avg.y += 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 */
+ }
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);
+ {
+ if ((x >= scaled_avg_x - ts.reject_threshold_vs_avg_x) &&
+ (x <= scaled_avg_x + ts.reject_threshold_vs_avg_x) &&
+ (y >= scaled_avg_y - ts.reject_threshold_vs_avg_y) &&
+ (y <= scaled_avg_y + ts.reject_threshold_vs_avg_y))
+ ts.flag_previous_exceeded_threshold = 0;
+ else
+ ts.flag_previous_exceeded_threshold = 1;
+
+ store_sample:
+ 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);
}
- /* 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)) {
+
+ // If we've got enough points, run the timer to output the data.
+ // If we're reading jumpy data, the stylus may have been lifted. Wait a
+ // jiffy and check for interrupts, to check for this. Otherwise, the
+ // stylus is probably still down, so just get another point.
+ if(ts.flag_previous_exceeded_threshold || ts.count >= ts.extent)
+ {
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:
+ else
+ wait_for_datapoint();
return IRQ_HANDLED;
}
@@ -423,11 +429,12 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
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.reject_threshold_vs_avg_x = info->reject_threshold_vs_avg_x;
+ ts.reject_threshold_vs_avg_y = info->reject_threshold_vs_avg_y;
+ ts.min_number_of_points_to_use = info->min_number_of_points_to_use;
ts.raw_sample_fifo = kmalloc(sizeof(struct s3c2410ts_sample) *
- ts.excursion_filter_len, GFP_KERNEL);
+ ts.extent, GFP_KERNEL);
clear_raw_fifo();
/* Get irqs */
diff --git a/include/asm-arm/arch-s3c2410/ts.h b/include/asm-arm/arch-s3c2410/ts.h
index 44c1e4b..21c734a 100644
--- a/include/asm-arm/arch-s3c2410/ts.h
+++ b/include/asm-arm/arch-s3c2410/ts.h
@@ -21,7 +21,14 @@ struct s3c2410_ts_mach_info {
int presc;
int oversampling_shift;
int excursion_filter_len_bits;
+
+ // use either the separately specified _avg_x and _avg_y thresholds, or the
+ // common _avg threshold, but not both
int reject_threshold_vs_avg;
+ int reject_threshold_vs_avg_x;
+ int reject_threshold_vs_avg_y;
+
+ int min_number_of_points_to_use;
};
void set_s3c2410ts_info(struct s3c2410_ts_mach_info *hard_s3c2410ts_info);