test-touchscreen-pressure.patch From: Andy Green Signed-off-by: Andy Green --- arch/arm/mach-s3c2440/mach-gta02.c | 20 ++- defconfig-gta01 | 6 + defconfig-gta02 | 7 + drivers/input/touchscreen/s3c2410_ts.c | 217 +++++++++++++++++++++----------- drivers/input/touchscreen/ts_filter.c | 86 +++++++++++++ include/asm-arm/arch-s3c2410/ts.h | 4 + include/linux/ts_filter.h | 27 ++++ 7 files changed, 283 insertions(+), 84 deletions(-) diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c index 8c5d1cb..b033490 100644 --- a/arch/arm/mach-s3c2440/mach-gta02.c +++ b/arch/arm/mach-s3c2440/mach-gta02.c @@ -902,10 +902,10 @@ 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, + .extent = 31, /* total memory of median filter */ + .decimation_threshold = 6 * 3, /* delta that decides fast or slow */ + .decimation_below = 28, /* slow -- accumilate lots of inp before out */ + .decimation_above = 8, /* fast -- less inp before output so we track */ }; static struct ts_filter_mean_configuration gta02_ts_mean_config = { @@ -915,14 +915,20 @@ static struct ts_filter_mean_configuration gta02_ts_mean_config = { static struct s3c2410_ts_mach_info gta02_ts_cfg = { .delay = 10000, - .presc = 0xff, /* slow as we can go */ + /* fast as we can go to reduce time with ts plates powered */ + .presc = 50000000 / 2500000, /* 50MHz PCLK / 2.5MHz clock needed */ + /* only do additional area measurements every third time */ + .area_test_ratio = 3, + .area_minimum = 20, + .area_maximum = 75, + .filter_sequence = { [0] = &ts_filter_median_api, - [1] = &ts_filter_mean_api, +// [1] = &ts_filter_mean_api, }, .filter_config = { [0] = >a02_ts_median_config, - [1] = >a02_ts_mean_config, +// [1] = >a02_ts_mean_config, }, }; diff --git a/defconfig-gta01 b/defconfig-gta01 index 57a7d51..08fce0c 100644 --- a/defconfig-gta01 +++ b/defconfig-gta01 @@ -159,7 +159,7 @@ CONFIG_CPU_LLSERIAL_S3C2440=y # # Power management # -# CONFIG_S3C2410_PM_DEBUG is not set +CONFIG_S3C2410_PM_DEBUG=y # CONFIG_S3C2410_PM_CHECK is not set CONFIG_S3C_LOWLEVEL_UART_PORT=0 @@ -1696,7 +1696,9 @@ CONFIG_FORCED_INLINING=y # CONFIG_SAMPLES is not set # CONFIG_DEBUG_USER is not set CONFIG_DEBUG_ERRORS=y -# CONFIG_DEBUG_LL is not set +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_ICEDCC is not set +CONFIG_DEBUG_S3C_PORT=y CONFIG_DEBUG_S3C_UART=0 # diff --git a/defconfig-gta02 b/defconfig-gta02 index 8150615..8ca3f9c 100644 --- a/defconfig-gta02 +++ b/defconfig-gta02 @@ -159,6 +159,7 @@ CONFIG_CPU_LLSERIAL_S3C2440=y # # Power management # +CONFIG_S3C2410_PM_DEBUG=y # CONFIG_S3C2410_PM_CHECK is not set CONFIG_S3C_LOWLEVEL_UART_PORT=2 @@ -1020,7 +1021,7 @@ CONFIG_SPI_S3C24XX_GPIO=y CONFIG_POWER_SUPPLY=y # CONFIG_POWER_SUPPLY_DEBUG is not set # CONFIG_PDA_POWER is not set -CONFIG_APM_POWER=y +# CONFIG_APM_POWER is not set # CONFIG_BATTERY_DS2760 is not set CONFIG_BATTERY_BQ27000_HDQ=y CONFIG_GTA02_HDQ=y @@ -1695,7 +1696,9 @@ CONFIG_FORCED_INLINING=y # CONFIG_SAMPLES is not set # CONFIG_DEBUG_USER is not set CONFIG_DEBUG_ERRORS=y -# CONFIG_DEBUG_LL is not set +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_ICEDCC is not set +CONFIG_DEBUG_S3C_PORT=y CONFIG_DEBUG_S3C_UART=2 # diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index 3b063bb..77d3f63 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -79,6 +79,18 @@ S3C2410_ADCTSC_AUTO_PST | \ S3C2410_ADCTSC_XY_PST(0)) +#define READZ1 (S3C2410_ADCTSC_XM_SEN | /* X- on */ \ + S3C2410_ADCTSC_YP_SEN | /* Y+ off -- sample this*/ \ + S3C2410_ADCTSC_XY_PST(0)) /* Y- off, X+ on */ + +#define READZ2 (S3C2410_ADCTSC_XM_SEN | /* X- on */ \ + S3C2410_ADCTSC_XP_SEN | /* X+ off -- z1*/ \ + S3C2410_ADCTSC_XY_PST(0)) /* Y- off -- z2, Y+ on */ + +#define NODRIVE (S3C2410_ADCTSC_YP_SEN | /* Y+ off */ \ + S3C2410_ADCTSC_XP_SEN | /* X+ off */ \ + S3C2410_ADCTSC_XY_PST(0)) /* Y- off, X- off */ + #define DEBUG_LVL KERN_DEBUG MODULE_AUTHOR("Arnaud Patard "); @@ -99,7 +111,11 @@ static char *s3c2410ts_name = "s3c2410 TouchScreen"; struct s3c2410ts { struct input_dev *dev; struct ts_filter *tsf[MAX_TS_FILTER_CHAIN]; - int coords[2]; /* just X and Y for us */ + struct s3c2410_ts_mach_info info; + struct ts_filter_4node_area area; + + int coords[4]; /* X, Y, Z1, Z2 */ + int running; }; static struct s3c2410ts ts; @@ -108,7 +124,7 @@ static void __iomem *base_addr; -static inline void s3c2410_ts_connect(void) +static void s3c2410_ts_connect(void) { s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON); s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON); @@ -118,53 +134,62 @@ static inline void s3c2410_ts_connect(void) static void touch_timer_fire(unsigned long data) { - unsigned long data0; - unsigned long data1; - int updown; + int area = 0; - data0 = readl(base_addr + S3C2410_ADCDAT0); - data1 = readl(base_addr + S3C2410_ADCDAT1); + /* has he lifted the stylus? */ - updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && - (!(data1 & S3C2410_ADCDAT0_UPDOWN)); + if ((readl(base_addr + S3C2410_ADCDAT0) & + readl(base_addr + S3C2410_ADCDAT1)) & S3C2410_ADCDAT0_UPDOWN) { - if (updown) { + /* he's stopped touching */ if (ts.tsf[0]) - (ts.tsf[0]->api->scale)(ts.tsf[0], &ts.coords[0]); + (ts.tsf[0]->api->clear)(ts.tsf[0]); -#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG - { - struct timeval tv; + input_report_key(ts.dev, BTN_TOUCH, 0); + input_report_abs(ts.dev, ABS_PRESSURE, 0); + input_sync(ts.dev); - do_gettimeofday(&tv); - printk(DEBUG_LVL "T:%06d, X:%03ld, Y:%03ld\n", - (int)tv.tv_usec, ts.coords[0], ts.coords[1]); - } -#endif + printk(KERN_INFO "up\n"); - input_report_abs(ts.dev, ABS_X, ts.coords[0]); - input_report_abs(ts.dev, ABS_Y, ts.coords[1]); + ts.area.last_area = 0; + ts.running = 0; - input_report_key(ts.dev, BTN_TOUCH, 1); - input_report_abs(ts.dev, ABS_PRESSURE, 1); - input_sync(ts.dev); + writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC); - 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 { + return; + } - if (ts.tsf[0]) - (ts.tsf[0]->api->clear)(ts.tsf[0]); + /* no, we are still "down" */ - input_report_key(ts.dev, BTN_TOUCH, 0); - input_report_abs(ts.dev, ABS_PRESSURE, 0); - input_sync(ts.dev); + if (ts.tsf[0]) + area = ts_filter_4node_compute(ts.tsf[0], &ts.area, + &ts.coords[0]); + printk(KERN_INFO "%d\n", area); + input_report_abs(ts.dev, ABS_PRESSURE, area); - writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC); +#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.coords[0], ts.coords[1]); } +#endif + + input_report_key(ts.dev, BTN_TOUCH, 1); + input_report_abs(ts.dev, ABS_X, ts.coords[0]); + input_report_abs(ts.dev, ABS_Y, ts.coords[1]); + + input_sync(ts.dev); + + /* start another ADC action */ + + 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 struct timer_list touch_timer = @@ -172,52 +197,88 @@ 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 (!(((readl(base_addr + S3C2410_ADCDAT0) & + readl(base_addr + S3C2410_ADCDAT1)) & S3C2410_ADCDAT0_UPDOWN)) && + !ts.running) { + ts.running = 1; + 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); + } return IRQ_HANDLED; } static irqreturn_t stylus_action(int irq, void *dev_id) { - /* grab the ADC results */ - ts.coords[0] = readl(base_addr + S3C2410_ADCDAT0) & + + switch(ts_filter_4node_area_sequencer(&ts.area, &ts.coords[0])) { + + case TSF4NS_XMXP_GETYP: /* grab the X Y normal ADC results */ + ts.coords[0] = readl(base_addr + S3C2410_ADCDAT0) & S3C2410_ADCDAT0_XPDATA_MASK; - ts.coords[1] = readl(base_addr + S3C2410_ADCDAT1) & + ts.coords[1] = readl(base_addr + S3C2410_ADCDAT1) & S3C2410_ADCDAT1_YPDATA_MASK; + /* because we get X and Y in one step, advance sequencer + * past the "get the Y step" it will give us next time + */ + ts_filter_4node_area_sequencer(&ts.area, &ts.coords[0]); + + /* if not doing area measurement next, let's go with X Y */ + if (ts.area.step == TSF4NS_XMXP_GETYP) + break; + + /* set up to get the first area reading + * We force power across X- and Y+, then read Y- + */ + writel(S3C2410_ADCTSC_PULL_UP_DISABLE | READZ2, + base_addr + S3C2410_ADCTSC); + writel((readl(base_addr + S3C2410_ADCCON) & ~(7 << 3)) | + S3C2410_ADCCON_ENABLE_START | (4 << 3), /* Y- */ + base_addr + S3C2410_ADCCON); + return IRQ_HANDLED; + + case TSF4NS_XMYP_GETYM: + ts.coords[2] = readl(base_addr + S3C2410_ADCDAT0) & + S3C2410_ADCDAT0_XPDATA_MASK; + /* set up to get the second area reading + * We force power across X- and Y+, then read X+ + */ + writel(S3C2410_ADCTSC_PULL_UP_DISABLE | READZ2, + base_addr + S3C2410_ADCTSC); + writel((readl(base_addr + S3C2410_ADCCON) & ~(7 << 3)) | + S3C2410_ADCCON_ENABLE_START | (7 << 3), /* X+ */ + base_addr + S3C2410_ADCCON); + return IRQ_HANDLED; + + case TSF4NS_XMYP_GETXP: + default: + ts.coords[3] = readl(base_addr + S3C2410_ADCDAT0) & + S3C2410_ADCDAT0_XPDATA_MASK; - if (!ts.tsf[0]) /* filtering is disabled then use raw directly */ - goto real_sample; + ts.area.area_coords_stash[0] = ts.coords[2]; + ts.area.area_coords_stash[1] = ts.coords[3]; - /* send it to the chain of filters */ - if ((ts.tsf[0]->api->process)(ts.tsf[0], &ts.coords[0])) - goto real_sample; + /* kill power across plates */ + writel(S3C2410_ADCTSC_PULL_UP_DISABLE | NODRIVE, + base_addr + S3C2410_ADCTSC); + break; + } - /* - * no real sample came out of processing yet, - * get another raw result to feed it - */ - writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, + if (ts.tsf[0] && !(ts.tsf[0]->api->process)(ts.tsf[0], &ts.coords[0])) { + /* + * no real sample came out of processing yet, + * get another raw result to feed it + */ + 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); - return IRQ_HANDLED; + 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); @@ -236,13 +297,13 @@ static int __init s3c2410ts_probe(struct platform_device *pdev) struct s3c2410_ts_mach_info *info; struct input_dev *input_dev; int ret = 0; + int used_coord_dimensions = ARRAY_SIZE(ts.coords); dev_info(&pdev->dev, "Starting\n"); info = (struct s3c2410_ts_mach_info *)pdev->dev.platform_data; - if (!info) - { + if (!info) { dev_err(&pdev->dev, "Hm... too bad: no platform data for ts\n"); return -EINVAL; } @@ -303,7 +364,10 @@ static int __init s3c2410ts_probe(struct platform_device *pdev) ts.dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0); input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0); - input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0); + if (info->area_maximum) /* if machine data set it up... */ + input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 100, 0, 0); + else + used_coord_dimensions = 2; /* no area, just x and y */ ts.dev->private = &ts; ts.dev->name = s3c2410ts_name; @@ -312,10 +376,17 @@ static int __init s3c2410ts_probe(struct platform_device *pdev) ts.dev->id.product = 0xBEEF; ts.dev->id.version = S3C2410TSVERSION; - /* create the filter chain set up for the 2 coordinates we produce */ + /* copy the info struct wholesale */ + ts.info = *info; + + ts.area.test_ratio = info->area_test_ratio; + ts.area.min_clip = info->area_minimum; + ts.area.max_clip = info->area_maximum; + + /* create the filter chain set up for the 3 coordinates we produce */ ret = ts_filter_create_chain( (struct ts_filter_api **)&info->filter_sequence, - &info->filter_config, ts.tsf, ARRAY_SIZE(ts.coords)); + info->filter_config, ts.tsf, used_coord_dimensions); if (ret) dev_info(&pdev->dev, "%d filter(s) initialized\n", ret); else /* this is OK, just means there won't be any filtering */ diff --git a/drivers/input/touchscreen/ts_filter.c b/drivers/input/touchscreen/ts_filter.c index f8b2b2f..9817766 100644 --- a/drivers/input/touchscreen/ts_filter.c +++ b/drivers/input/touchscreen/ts_filter.c @@ -55,3 +55,89 @@ void ts_filter_destroy_chain(struct ts_filter **list) } } EXPORT_SYMBOL_GPL(ts_filter_destroy_chain); + +enum ts_filter_4node_step +ts_filter_4node_area_sequencer(struct ts_filter_4node_area *area, int * coords) +{ + enum ts_filter_4node_step step = area->step; + + switch (step) { + case TSF4NS_XMXP_GETYP: /* normal X coord */ + area->step = TSF4NS_YMYP_GETXP; + break; + case TSF4NS_YMYP_GETXP: /* normal Y coord */ + area->step = TSF4NS_XMXP_GETYP; /* default to go back for X */ + if (!area->max_clip) /* not used */ + break; + if (!area->test_counter--) { + area->test_counter = area->test_ratio; + area->step = TSF4NS_XMYP_GETYM; /* do z1 / z2 seq */ + break; + } + coords[2] = area->area_coords_stash[0]; + coords[3] = area->area_coords_stash[1]; + break; + case TSF4NS_XMYP_GETYM: /* special z1 coord for area */ + area->step = TSF4NS_XMYP_GETXP; + break; + case TSF4NS_XMYP_GETXP: /* special z2 coord for area */ + default: + area->step = TSF4NS_XMXP_GETYP; /* restart after this one */ + break; + } + + return step; +} +EXPORT_SYMBOL_GPL(ts_filter_4node_area_sequencer); + +int ts_filter_4node_compute(struct ts_filter *tsf, + struct ts_filter_4node_area *area, int * coords) +{ + int n; + + if (!tsf || !area) + return 0; + + /* get filters to scale their results just before use */ + (tsf->api->scale)(tsf, coords); + + if (!area->max_clip) + return 0; + + /* we didn't get fresh area coords this time, use last computed */ + if (area->test_counter != area->test_ratio) + return area->last_area; + + /* avoid divide by zero */ + if (!coords[2]) { + area->last_area = 100; + + return area->last_area; + } + + n = coords[0]; + if (!n) + n = 1; + + /* compute area depressed */ +// area->last_area = ((950 * coords[0]) / ((((1024 * coords[3]) / coords[2]) - 1024))); + area->last_area = ((100 * coords[3]) / coords[2]); + + printk(KERN_INFO "coords[0]=%d, coords[2]=%d, coords[3]=%d, area = %d\n", + coords[0], coords[2], coords[3], area->last_area); + + /* clip the area */ + if (area->last_area < area->min_clip) + area->last_area = area->min_clip; + if (area->last_area > area->max_clip) + area->last_area = area->max_clip; + + /* scale it up to 1 - 100 */ + area->last_area = 100 - ((100 * (area->last_area - area->min_clip)) / + (area->max_clip - area->min_clip)); + if (area->last_area < 1) + area->last_area = 1; + + return area->last_area; +} +EXPORT_SYMBOL_GPL(ts_filter_4node_compute); diff --git a/include/asm-arm/arch-s3c2410/ts.h b/include/asm-arm/arch-s3c2410/ts.h index ffd33d2..09633f7 100644 --- a/include/asm-arm/arch-s3c2410/ts.h +++ b/include/asm-arm/arch-s3c2410/ts.h @@ -21,6 +21,10 @@ struct s3c2410_ts_mach_info { int delay; int presc; + int area_test_ratio; + int area_minimum; + int area_maximum; + /* array of pointers to filter APIs we want to use, in order * ends on first NULL, all NULL is OK */ diff --git a/include/linux/ts_filter.h b/include/linux/ts_filter.h index 7262bba..b4a5831 100644 --- a/include/linux/ts_filter.h +++ b/include/linux/ts_filter.h @@ -38,6 +38,25 @@ struct ts_filter { int coords[MAX_TS_FILTER_COORDS]; }; +enum ts_filter_4node_step { + TSF4NS_XMXP_GETYP, + TSF4NS_YMYP_GETXP, + TSF4NS_XMYP_GETYM, + TSF4NS_XMYP_GETXP, +}; + +struct ts_filter_4node_area { + int test_ratio; + + int test_counter; + int min_clip; + int max_clip; + int area_coords_stash[2]; + + enum ts_filter_4node_step step; + int last_area; +}; + /* * helper to create a filter chain from array of API pointers and * array of config ints... leaves pointers to created filters in list @@ -51,3 +70,11 @@ extern int ts_filter_create_chain(struct ts_filter_api **api, void **config, extern void ts_filter_destroy_chain(struct ts_filter **list); #endif + +/* helpers to compute pressure from 4-pin touchscreen */ + +extern enum ts_filter_4node_step +ts_filter_4node_area_sequencer(struct ts_filter_4node_area *area, int * coords); + +extern int ts_filter_4node_compute(struct ts_filter *tsf, + struct ts_filter_4node_area *area, int * coords);