From fc5d5366335469828d78898e2e74f8f80aa1d076 Mon Sep 17 00:00:00 2001 From: mokopatches Date: Wed, 16 Jul 2008 14:44:49 +0100 Subject: [PATCH] s3c2410_touchscreen.patch --- arch/arm/mach-s3c2410/mach-h1940.c | 8 + arch/arm/plat-s3c24xx/devs.c | 18 ++ arch/arm/plat-s3c24xx/s3c244x.c | 1 + drivers/input/touchscreen/Kconfig | 18 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/s3c2410_ts.c | 437 ++++++++++++++++++++++++++++++++ include/asm-arm/arch-s3c2410/ts.h | 28 ++ include/asm-arm/plat-s3c24xx/devs.h | 1 + 8 files changed, 512 insertions(+), 0 deletions(-) create mode 100644 drivers/input/touchscreen/s3c2410_ts.c create mode 100644 include/asm-arm/arch-s3c2410/ts.h diff --git a/arch/arm/mach-s3c2410/mach-h1940.c b/arch/arm/mach-s3c2410/mach-h1940.c index 7c1145e..93cd8c8 100644 --- a/arch/arm/mach-s3c2410/mach-h1940.c +++ b/arch/arm/mach-s3c2410/mach-h1940.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -129,6 +130,11 @@ static struct s3c2410_udc_mach_info h1940_udc_cfg __initdata = { .vbus_pin_inverted = 1, }; +static struct s3c2410_ts_mach_info h1940_ts_cfg __initdata = { + .delay = 10000, + .presc = 49, + .oversampling_shift = 2, +}; /** * Set lcd on or off @@ -186,6 +192,7 @@ static struct platform_device *h1940_devices[] __initdata = { &s3c_device_i2c, &s3c_device_iis, &s3c_device_usbgadget, + &s3c_device_ts, &s3c_device_leds, &s3c_device_bluetooth, }; @@ -214,6 +221,7 @@ static void __init h1940_init(void) u32 tmp; s3c24xx_fb_set_platdata(&h1940_fb_info); + set_s3c2410ts_info(&h1940_ts_cfg); s3c24xx_udc_set_platdata(&h1940_udc_cfg); /* Turn off suspend on both USB ports, and switch the diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c index e546e93..c1fbe2d 100644 --- a/arch/arm/plat-s3c24xx/devs.c +++ b/arch/arm/plat-s3c24xx/devs.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -207,6 +208,23 @@ struct platform_device s3c_device_nand = { EXPORT_SYMBOL(s3c_device_nand); +/* Touchscreen */ +struct platform_device s3c_device_ts = { + .name = "s3c2410-ts", + .id = -1, +}; + +EXPORT_SYMBOL(s3c_device_ts); + +static struct s3c2410_ts_mach_info s3c2410ts_info; + +void set_s3c2410ts_info(struct s3c2410_ts_mach_info *hard_s3c2410ts_info) +{ + memcpy(&s3c2410ts_info,hard_s3c2410ts_info,sizeof(struct s3c2410_ts_mach_info)); + s3c_device_ts.dev.platform_data = &s3c2410ts_info; +} +EXPORT_SYMBOL(set_s3c2410ts_info); + /* USB Device (Gadget)*/ static struct resource s3c_usbgadget_resource[] = { diff --git a/arch/arm/plat-s3c24xx/s3c244x.c b/arch/arm/plat-s3c24xx/s3c244x.c index 2f01af5..7a1a12d 100644 --- a/arch/arm/plat-s3c24xx/s3c244x.c +++ b/arch/arm/plat-s3c24xx/s3c244x.c @@ -68,6 +68,7 @@ void __init s3c244x_map_io(struct map_desc *mach_desc, int size) s3c_device_sdi.name = "s3c2440-sdi"; s3c_device_i2c.name = "s3c2440-i2c"; s3c_device_nand.name = "s3c2440-nand"; + s3c_device_ts.name = "s3c2440-ts"; s3c_device_usbgadget.name = "s3c2440-usbgadget"; } diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 565ec71..52de2b0 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -67,6 +67,24 @@ config TOUCHSCREEN_FUJITSU To compile this driver as a module, choose M here: the module will be called fujitsu-ts. +config TOUCHSCREEN_S3C2410 + tristate "Samsung S3C2410 touchscreen input driver" + depends on ARCH_S3C2410 && INPUT && INPUT_TOUCHSCREEN + select SERIO + help + Say Y here if you have the s3c2410 touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called s3c2410_ts. + +config TOUCHSCREEN_S3C2410_DEBUG + boolean "Samsung S3C2410 touchscreen debug messages" + depends on TOUCHSCREEN_S3C2410 + help + Select this if you want debug messages + config TOUCHSCREEN_GUNZE tristate "Gunze AHL-51S touchscreen" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 3c096d7..7c3bd1c 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -26,3 +26,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_S3C2410) += s3c2410_ts.o diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c new file mode 100644 index 0000000..68071c2 --- /dev/null +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -0,0 +1,437 @@ +/* + * 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) 2004 Arnaud Patard + * iPAQ H1940 touchscreen support + * + * ChangeLog + * + * 2004-09-05: Herbert Pƶtzl + * - added clock (de-)allocation code + * + * 2005-03-06: Arnaud Patard + * - h1940_ -> s3c2410 (this driver is now also used on the n30 + * machines :P) + * - Debug messages are now enabled with the config option + * TOUCHSCREEN_S3C2410_DEBUG + * - Changed the way the value are read + * - Input subsystem should now work + * - Use ioremap and readl/writel + * + * 2005-03-23: Arnaud Patard + * - Make use of some undocumented features of the touchscreen + * controller + * + * 2007-05-23: Harald Welte + * - Add proper support for S32440 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* For ts.dev.id.version */ +#define S3C2410TSVERSION 0x0101 + +#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_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 DEBUG_LVL KERN_DEBUG + +MODULE_AUTHOR("Arnaud Patard "); +MODULE_DESCRIPTION("s3c2410 touchscreen driver"); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + + +static char *s3c2410ts_name = "s3c2410 TouchScreen"; + +/* + * Per-touchscreen data. + */ + +struct s3c2410ts { + struct input_dev *dev; + long xp; + long yp; + int count; + int shift; +}; + +static struct s3c2410ts ts; +static void __iomem *base_addr; + +static inline void s3c2410_ts_connect(void) +{ + s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON); + s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON); + s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON); + s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON); +} + +static void touch_timer_fire(unsigned long data) +{ + 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)); + + 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); + } +#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.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; + + 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); + } +} + +static struct timer_list touch_timer = + TIMER_INITIALIZER(touch_timer_fire, 0, 0); + +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); + + return IRQ_HANDLED; +} + + +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); + + ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK; + ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK; + ts.count++; + + if (ts.count < (1<dev.platform_data; + + if (!info) + { + printk(KERN_ERR "Hm... too bad : no platform data for ts\n"); + return -EINVAL; + } + +#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG + printk(DEBUG_LVL "Entering s3c2410ts_init\n"); +#endif + + adc_clock = clk_get(NULL, "adc"); + if (!adc_clock) { + printk(KERN_ERR "failed to get adc clock source\n"); + return -ENOENT; + } + clk_enable(adc_clock); + +#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG + printk(DEBUG_LVL "got and enabled clock\n"); +#endif + + base_addr = ioremap(S3C2410_PA_ADC,0x20); + if (base_addr == NULL) { + printk(KERN_ERR "Failed to remap register block\n"); + return -ENOMEM; + } + + + /* If we acutally are a S3C2410: Configure GPIOs */ + 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); + else + writel(0,base_addr+S3C2410_ADCCON); + + + /* Initialise registers */ + if ((info->delay&0xffff) > 0) + writel(info->delay & 0xffff, base_addr+S3C2410_ADCDLY); + + 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"); + return -ENOMEM; + } + + ts.dev = input_dev; + ts.dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | + BIT_MASK(EV_ABS); + 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); + + ts.dev->private = &ts; + ts.dev->name = s3c2410ts_name; + ts.dev->id.bustype = BUS_RS232; + ts.dev->id.vendor = 0xDEAD; + ts.dev->id.product = 0xBEEF; + ts.dev->id.version = S3C2410TSVERSION; + + ts.shift = info->oversampling_shift; + + /* 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"); + 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"); + free_irq(IRQ_ADC, ts.dev); + iounmap(base_addr); + return -EIO; + } + + printk(KERN_INFO "%s successfully loaded\n", s3c2410ts_name); + + /* All went ok, so register to the input system */ + rc = input_register_device(ts.dev); + if (rc) { + free_irq(IRQ_TC, ts.dev); + free_irq(IRQ_ADC, ts.dev); + clk_disable(adc_clock); + iounmap(base_addr); + return -EIO; + } + + return 0; +} + +static int s3c2410ts_remove(struct platform_device *pdev) +{ + disable_irq(IRQ_ADC); + disable_irq(IRQ_TC); + free_irq(IRQ_TC,ts.dev); + free_irq(IRQ_ADC,ts.dev); + + if (adc_clock) { + clk_disable(adc_clock); + clk_put(adc_clock); + adc_clock = NULL; + } + + input_unregister_device(ts.dev); + iounmap(base_addr); + + return 0; +} + +#ifdef CONFIG_PM +static int s3c2410ts_suspend(struct platform_device *pdev, pm_message_t state) +{ + writel(TSC_SLEEP, base_addr+S3C2410_ADCTSC); + writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_STDBM, + base_addr+S3C2410_ADCCON); + + disable_irq(IRQ_ADC); + disable_irq(IRQ_TC); + + clk_disable(adc_clock); + + return 0; +} + +static int s3c2410ts_resume(struct platform_device *pdev) +{ + struct s3c2410_ts_mach_info *info = + ( struct s3c2410_ts_mach_info *)pdev->dev.platform_data; + + clk_enable(adc_clock); + msleep(1); + + 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); + else + writel(0,base_addr+S3C2410_ADCCON); + + /* Initialise registers */ + if ((info->delay&0xffff) > 0) + writel(info->delay & 0xffff, base_addr+S3C2410_ADCDLY); + + writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC); + + return 0; +} + +#else +#define s3c2410ts_suspend NULL +#define s3c2410ts_resume NULL +#endif + +static struct platform_driver s3c2410ts_driver = { + .driver = { + .name = "s3c2410-ts", + .owner = THIS_MODULE, + }, + .probe = s3c2410ts_probe, + .remove = s3c2410ts_remove, + .suspend = s3c2410ts_suspend, + .resume = s3c2410ts_resume, + +}; + +static struct platform_driver s3c2440ts_driver = { + .driver = { + .name = "s3c2440-ts", + .owner = THIS_MODULE, + }, + .probe = s3c2410ts_probe, + .remove = s3c2410ts_remove, + .suspend = s3c2410ts_suspend, + .resume = s3c2410ts_resume, + +}; + +static int __init s3c2410ts_init(void) +{ + int rc; + + rc = platform_driver_register(&s3c2410ts_driver); + if (rc < 0) + return rc; + + rc = platform_driver_register(&s3c2440ts_driver); + if (rc < 0) + platform_driver_unregister(&s3c2410ts_driver); + + return rc; +} + +static void __exit s3c2410ts_exit(void) +{ + platform_driver_unregister(&s3c2440ts_driver); + platform_driver_unregister(&s3c2410ts_driver); +} + +module_init(s3c2410ts_init); +module_exit(s3c2410ts_exit); + +/* + Local variables: + compile-command: "make ARCH=arm CROSS_COMPILE=/usr/local/arm/3.3.2/bin/arm-linux- -k -C ../../.." + c-basic-offset: 8 + End: +*/ diff --git a/include/asm-arm/arch-s3c2410/ts.h b/include/asm-arm/arch-s3c2410/ts.h new file mode 100644 index 0000000..593632a --- /dev/null +++ b/include/asm-arm/arch-s3c2410/ts.h @@ -0,0 +1,28 @@ +/* linux/include/asm/arch-s3c2410/ts.h + * + * Copyright (c) 2005 Arnaud Patard + * + * + * 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. + * + * + * Changelog: + * 24-Mar-2005 RTP Created file + * 03-Aug-2005 RTP Renamed to ts.h + */ + +#ifndef __ASM_ARM_TS_H +#define __ASM_ARM_TS_H + +struct s3c2410_ts_mach_info { + int delay; + int presc; + int oversampling_shift; +}; + +void set_s3c2410ts_info(struct s3c2410_ts_mach_info *hard_s3c2410ts_info); + +#endif /* __ASM_ARM_TS_H */ + diff --git a/include/asm-arm/plat-s3c24xx/devs.h b/include/asm-arm/plat-s3c24xx/devs.h index f9d6f03..43de9cc 100644 --- a/include/asm-arm/plat-s3c24xx/devs.h +++ b/include/asm-arm/plat-s3c24xx/devs.h @@ -42,6 +42,7 @@ extern struct platform_device s3c_device_timer2; extern struct platform_device s3c_device_timer3; extern struct platform_device s3c_device_usbgadget; +extern struct platform_device s3c_device_ts; /* s3c2440 specific devices */ -- 1.5.6.3