From d3339ebf194efc5a6c9f53686cc03ce7cc7f0def Mon Sep 17 00:00:00 2001 From: Theodore A. Roth Date: Fri, 15 May 2009 11:07:06 -0700 Subject: [PATCH] Add touchscreen support for ts7350/ts7370/ts7390 boards. * This was pulled directly from the 2.6.21 kernel from Technologic Systems. Minor modifications were made to port to 2.6.29.1 kernel. --- drivers/input/touchscreen/Kconfig | 37 +++++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/ts_lcd.c | 301 ++++++++++++++++++++++++++++++++++++ 3 files changed, 339 insertions(+), 0 deletions(-) create mode 100644 drivers/input/touchscreen/ts_lcd.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index bb6486a..f8a034c 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -151,6 +151,43 @@ config TOUCHSCREEN_MK712 To compile this driver as a module, choose M here: the module will be called mk712. +config TOUCHSCREEN_TSLCD + tristate "TS-LCD" + help + Say Y here if you have the TS-LCD. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tslcd. + +config TOUCHSCREEN_TSLCD_SWAPXY + bool "Swap X/Y axes" + depends on TOUCHSCREEN_TSLCD + help + Select this if the X and Y axes are reversed from the + expected polarity. This option is provided for compiling + the TSLCD driver into the kernel; when loading as a module + you can use the swapxy parameter to override this default. + +config TOUCHSCREEN_TSLCD_NEGX + bool "Reverse X" + depends on TOUCHSCREEN_TSLCD + help + Select this if the X axis is backwards. This option is provided + for compiling the TSLCD driver into the kernel; when loading + as a module you can use the swapxy parameter to override this + default. + +config TOUCHSCREEN_TSLCD_NEGY + bool "Reverse Y" + depends on TOUCHSCREEN_TSLCD + help + Select this if the Y axis is backwards. This option is provided + for compiling the TSLCD driver into the kernel; when loading + as a module you can use the swapxy parameter to override this + default. + config TOUCHSCREEN_HP600 tristate "HP Jornada 6xx touchscreen" depends on SH_HP6XX && SH_ADC diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index d3375af..fe65855 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -34,3 +34,4 @@ wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o 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_TSLCD) += ts_lcd.o diff --git a/drivers/input/touchscreen/ts_lcd.c b/drivers/input/touchscreen/ts_lcd.c new file mode 100644 index 0000000..82e98f4 --- /dev/null +++ b/drivers/input/touchscreen/ts_lcd.c @@ -0,0 +1,301 @@ +/* + * TS-LCD touchscreen controller driver + * + * adapted from mk712.c + * Copyright (c) 1999-2002 Transmeta Corporation + * Copyright (c) 2005 Rick Koch + * Copyright (c) 2005 Vojtech Pavlik + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +/* + * This driver supports the TS-LCD + */ + +/* + * 1999-12-18: original version, Daniel Quinlan + * 1999-12-19: added anti-jitter code, report pen-up events, fixed mk712_poll + * to use queue_empty, Nathan Laredo + * 1999-12-20: improved random point rejection, Nathan Laredo + * 2000-01-05: checked in new anti-jitter code, changed mouse protocol, fixed + * queue code, added module options, other fixes, Daniel Quinlan + * 2002-03-15: Clean up for kernel merge + * Fixed multi open race, fixed memory checks, fixed resource + * allocation, fixed close/powerdown bug, switched to new init + * 2005-01-18: Ported to 2.6 from 2.4.28, Rick Koch + * 2005-02-05: Rewritten for the input layer, Vojtech Pavlik + * 2008-02-05: adapted from mk712.c for the TS-LCD, Michael Schmidt + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Daniel Quinlan , Vojtech Pavlik "); +MODULE_DESCRIPTION("TS-LCD"); +MODULE_LICENSE("GPL"); + +static struct input_dev *tslcd_dev; +static DEFINE_SPINLOCK(tslcd_lock); +static struct timer_list timer; +static int timer_started = 0; + +static int swapxy = +#ifdef TOUCHSCREEN_TSLCD_SWAPXY + 1; +#else + 0; +#endif +static int negx = +#ifdef TOUCHSCREEN_TSLCD_NEGX + 1; +#else + 0; +#endif +static int negy = +#ifdef TOUCHSCREEN_TSLCD_NEGY + 1; +#else + 0; +#endif + + +#define TSLCD_NOTOUCH 0xFFFF + +static volatile unsigned short *tX, *tY; +static unsigned short calib_x0 = 87, calib_dx = 3867; // 3954-87; +static unsigned short calib_y0 = 129, calib_dy = 3848; //3977-129; + +static inline unsigned short tslcd_readX(void) { + short x = swapxy ? *tY : *tX; + + if (x & 1) { + x = ((~x) >> 4) & 0xfff; + x = (x-calib_x0) * 4096 / calib_dx; + if (x < 0) x = 0; else if (x > 4095) x = 4095; + if (negx) x = 4095 - x; + return x; + } else { + return TSLCD_NOTOUCH; + } +} + +static inline unsigned short tslcd_readY(void) { + short y = swapxy ? *tX : *tY; + + y = ((~y) >> 4) & 0xfff; + y = (y-calib_y0) * 4096 / calib_dy; + if (y < 0) y = 0; else if (y > 4095) y = 4095; + if (negy) y = 4095 - y; + return y; +} + +#define DEBOUNCE_MS 0 +static void tslcd_sample(unsigned long data) +{ + static unsigned short last_x = TSLCD_NOTOUCH; + static unsigned short last_y = 0; + static unsigned short x,y; + static int bounce=DEBOUNCE_MS; + + spin_lock(&tslcd_lock); + x = tslcd_readX(); + y = tslcd_readY(); + if (x == last_x && y == last_y) { + goto sample_done; + } + if (x == TSLCD_NOTOUCH) { + bounce = DEBOUNCE_MS; + //printk("sample none\n"); + input_report_key(tslcd_dev, BTN_TOUCH, 0); + } else { + if (bounce > 0) { + bounce--; + } else { + //printk("x=%d,y=%d\n",x,y); + input_report_key(tslcd_dev, BTN_TOUCH, 1); + input_report_abs(tslcd_dev, ABS_X, x); + input_report_abs(tslcd_dev, ABS_Y, y); + } + } + last_x = x; + last_y = y; + + input_sync(tslcd_dev); + + sample_done: + timer.expires = jiffies + 1; + add_timer(&timer); + spin_unlock(&tslcd_lock); +} + +static int tslcd_open(struct input_dev *dev) +{ + unsigned long flags; + + //printk("tslcd_open\n"); + spin_lock_irqsave(&tslcd_lock, flags); + if (!timer_started) { + timer_started++; + timer.function = tslcd_sample; + timer.data = 0; + timer.expires = jiffies + 1; + init_timer(&timer); + add_timer(&timer); + } + spin_unlock_irqrestore(&tslcd_lock, flags); + + return 0; +} + +static void tslcd_close(struct input_dev *dev) +{ + unsigned long flags; + + //printk("tslcd_close\n"); + spin_lock_irqsave(&tslcd_lock, flags); + if (!--timer_started) { + del_timer(&timer); + } + spin_unlock_irqrestore(&tslcd_lock, flags); + +} + +static void sspi_cmd(volatile unsigned short *gpio,unsigned int cmd) { + unsigned int i; + + // pulse CS# + *gpio = (*gpio & 0xFFF0) | 0x2; + *gpio = (*gpio & 0xFFF0) | 0x0; + + for (i = 0; i < 32; i++, cmd <<= 1) { + if (cmd & 0x80) { + *gpio = (*gpio & 0xFFF0) | 0x4; + *gpio = (*gpio & 0xFFF0) | 0xc; + } else { + *gpio = (*gpio & 0xFFF0) | 0x0; + *gpio = (*gpio & 0xFFF0) | 0x8; + } + } +} + +#define CALIB1_OFFSET 7 +#define CALIB2_OFFSET 8 +static void load_calibration(volatile unsigned short *gpio, + unsigned short *x,unsigned short *dx, + unsigned short *y,unsigned short *dy) { + int i,j,ret,n[20],_x,_y,_dx,_dy; + + sspi_cmd(gpio,0xac); // X_PROGRAM_EN + sspi_cmd(gpio,0x4e); // READ_TAG + + for (j = 0; j < 20; j++) { + for (ret = 0x0, i = 0; i < 32; i++) { + *gpio = (*gpio & 0xFFF0) | 0x0; + *gpio = (*gpio & 0xFFF0) | 0x8; + ret = ret << 1 | (*gpio & 0x1); + } + n[j] = ret; + } + + sspi_cmd(gpio,0x78); // PROGRAM_DIS + + *gpio = (*gpio & 0xFFF0) | 0x2; + _x = n[CALIB1_OFFSET] >> 16; + _dx = n[CALIB1_OFFSET] & 0xFFFF; + _y = n[CALIB2_OFFSET] >> 16; + _dy = n[CALIB2_OFFSET] & 0xFFFF; + if ((_dx < 1 || _dx > 32768) || (_dy < 1 || _dy > 32768)) { + return; // use defaults + } + if ((_x < 1 || _x > 32768) || (_y < 1 || _y > 32768)) { + return; // use defaults + } + if (_dx == 0) _dx = 4096; + if (_dy == 0) _dy = 4096; + + *x = _x; + *y = _y; + *dx = _dx; + *dy = _dy; +} + +static int __init tslcd_init(void) +{ + int err; + volatile unsigned short *vreg; + + vreg = ioremap(0x600ff000,4096); + if (vreg == 0) { + printk("tslcd: could not get touchscreen regs\n"); + err = -ENOMEM; + goto fail1; + } + tX = vreg + 0x20; + tY = vreg + 0x21; + load_calibration(vreg+(0x86 / sizeof(unsigned short)), + &calib_x0,&calib_dx,&calib_y0,&calib_dy); + printk("x calibration: offset=%d, width=%d\n",calib_x0,calib_dx); + printk("y calibration: offset=%d, width=%d\n",calib_y0,calib_dy); + + tslcd_dev = input_allocate_device(); + if (!tslcd_dev) { + printk(KERN_ERR "tslcd: not enough memory\n"); + err = -ENOMEM; + goto fail1; + } + + tslcd_dev->name = "TS-LCD"; + tslcd_dev->phys = "tslcd/input0"; + //tslcd_dev->id.bustype = BUS_ISA; + //tslcd_dev->id.vendor = 0x0005; + //tslcd_dev->id.product = 0x0001; + //tslcd_dev->id.version = 0x0100; + + tslcd_dev->open = tslcd_open; + tslcd_dev->close = tslcd_close; + + tslcd_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + tslcd_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(tslcd_dev, ABS_X, 0, 4096, 0, 0); + input_set_abs_params(tslcd_dev, ABS_Y, 0, 4096, 0, 0); + + err = input_register_device(tslcd_dev); + if (err) + goto fail2; + + return 0; + + fail1: + input_free_device(tslcd_dev); + fail2: + return err; +} + +static void __exit tslcd_exit(void) +{ + input_unregister_device(tslcd_dev); +} + +module_init(tslcd_init); +module_exit(tslcd_exit); +module_param(swapxy,bool, 0644); +module_param(negx,bool, 0644); +module_param(negy,bool, 0644); +MODULE_PARM_DESC(swapxy, "Swap X/Y axes"); +MODULE_PARM_DESC(negx, "Reverse X axis"); +MODULE_PARM_DESC(negy, "Reverse Y axis"); -- 1.5.4.3