diff options
Diffstat (limited to 'drivers/input/touchscreen/s3c2410_ts.c')
-rw-r--r-- | drivers/input/touchscreen/s3c2410_ts.c | 316 |
1 files changed, 137 insertions, 179 deletions
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index 23d7e28e460..bcfdd00fa27 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -19,7 +19,7 @@ * ChangeLog * * 2004-09-05: Herbert Pƶtzl <herbert@13thfloor.at> - * - added clock (de-)allocation code + * - Added clock (de-)allocation code * * 2005-03-06: Arnaud Patard <arnaud.patard@rtp-net.org> * - h1940_ -> s3c2410 (this driver is now also used on the n30 @@ -37,12 +37,16 @@ * 2007-05-23: Harald Welte <laforge@openmoko.org> * - Add proper support for S32440 * - * 2008-06-23: Andy Green <andy@openmoko.com> - * - removed averaging system - * - added generic Touchscreen filter stuff + * 2008-06-23: Andy Green <andy@warmcat.com> + * - Removed averaging system + * - Added generic Touchscreen filter stuff * * 2008-11-27: Nelson Castillo <arhuaco@freaks-unidos.net> - * - improve interrupt handling + * - Improve interrupt handling + * + * 2009-04-09: Nelson Castillo <arhuaco@freaks-unidos.net> + * - Use s3c-adc API (Vasily Khoruzhick <anarsoul@gmail.com> provided + * a working example for a simpler version of this driver). */ #include <linux/errno.h> @@ -57,34 +61,34 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/clk.h> -#include <asm/io.h> +#include <linux/io.h> #include <asm/irq.h> #include <mach/regs-gpio.h> #include <mach/ts.h> #include <mach/hardware.h> #include <plat/regs-adc.h> +#include <plat/adc.h> #include "ts_filter_chain.h" /* 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 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 TSPRINTK(fmt, args...) \ + printk(KERN_DEBUG "%s: " fmt, __func__ , ## args) +#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG +# define DPRINTK TSPRINTK +#else +# define DPRINTK(fmt, args...) +#endif -#define DEBUG_LVL KERN_DEBUG MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>"); MODULE_DESCRIPTION("s3c2410 touchscreen driver"); @@ -97,23 +101,23 @@ MODULE_LICENSE("GPL"); static char *s3c2410ts_name = "s3c2410 TouchScreen"; #define TS_RELEASE_TIMEOUT (HZ >> 7 ? HZ >> 7 : 1) /* 8ms (5ms if HZ is 200) */ -#define TS_EVENT_FIFO_SIZE (2 << 6) /* must be a power of 2 */ - -#define TS_STATE_STANDBY 0 /* initial state */ -#define TS_STATE_PRESSED 1 -#define TS_STATE_RELEASE_PENDING 2 -#define TS_STATE_RELEASE 3 +#define TS_EVENT_FIFO_SIZE (2 << 6) /* Must be a power of 2. */ /* * Per-touchscreen data. */ +enum ts_state {TS_STATE_STANDBY, TS_STATE_PRESSED, TS_STATE_RELEASE_PENDING, + TS_STATE_RELEASE}; + struct s3c2410ts { struct input_dev *dev; struct ts_filter_chain *chain; + enum ts_state state; int is_down; - int state; struct kfifo *event_fifo; + struct s3c_adc_client *adc_client; + unsigned adc_selected; }; static struct s3c2410ts ts; @@ -132,12 +136,31 @@ static inline void s3c2410_ts_connect(void) s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON); } +/* + * Code that starts ADC conversions. + */ + +static void ts_adc_timer_f(unsigned long data); +static struct timer_list ts_adc_timer = TIMER_INITIALIZER(ts_adc_timer_f, 0, 0); + +static void ts_adc_timer_f(unsigned long data) +{ + if (s3c_adc_start(ts.adc_client, 0, 1)) + mod_timer(&ts_adc_timer, jiffies + 1); +} + static void s3c2410_ts_start_adc_conversion(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); + if (ts.adc_selected) + mod_timer(&ts_adc_timer, jiffies + 1); + else + ts_adc_timer_f(0); +} + +/* Callback for the s3c-adc API. */ +void adc_selected_f(unsigned selected) +{ + ts.adc_selected = selected; } /* @@ -161,18 +184,14 @@ static void ts_input_report(int event, int coords[]) input_report_key(ts.dev, BTN_TOUCH, 1); input_report_abs(ts.dev, ABS_PRESSURE, 1); -#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG - printk(DEBUG_LVL "T:%06d %6s (X:%03d, Y:%03d)\n", - (int)tv.tv_usec, s[event], coords[0], coords[1]); -#endif + DPRINTK("T:%06d %6s (X:%03d, Y:%03d)\n", + (int)tv.tv_usec, s[event], coords[0], coords[1]); } else { input_report_key(ts.dev, BTN_TOUCH, 0); input_report_abs(ts.dev, ABS_PRESSURE, 0); -#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG - printk(DEBUG_LVL "T:%06d %6s\n", - (int)tv.tv_usec, s[event]); -#endif + DPRINTK("T:%06d %6s\n", + (int)tv.tv_usec, s[event]); } input_sync(ts.dev); @@ -199,7 +218,7 @@ static void event_send_timer_f(unsigned long data) switch (event_type) { case 'D': if (ts.state == TS_STATE_RELEASE_PENDING) - /* Ignore short UP event */ + /* Ignore short UP event. */ ts.state = TS_STATE_PRESSED; break; @@ -208,21 +227,21 @@ static void event_send_timer_f(unsigned long data) break; case 'P': - if (ts.is_down) /* stylus_action needs a conversion */ + if (ts.is_down) /* Stylus_action needs a conversion. */ s3c2410_ts_start_adc_conversion(); if (unlikely(__kfifo_get(ts.event_fifo, (unsigned char *)buf, sizeof(int) * 2) - != sizeof(int) * 2)) - goto ts_exit_error; + != sizeof(int) * 2)) { + /* This will only happen if we have a bug. */ + TSPRINTK("Invalid packet\n"); + return; + } ts_input_report(IE_DOWN, buf); ts.state = TS_STATE_PRESSED; break; - - default: - goto ts_exit_error; } noop_counter = 0; @@ -243,11 +262,6 @@ static void event_send_timer_f(unsigned long data) } else { mod_timer(&event_send_timer, jiffies + TS_RELEASE_TIMEOUT); } - - return; - -ts_exit_error: /* should not happen unless we have a bug */ - printk(KERN_ERR __FILE__ ": event_send_timer_f failed\n"); } /* @@ -260,70 +274,72 @@ static irqreturn_t stylus_updown(int irq, void *dev_id) unsigned long data1; int event_type; - data0 = readl(base_addr+S3C2410_ADCDAT0); - data1 = readl(base_addr+S3C2410_ADCDAT1); + data0 = readl(base_addr + S3C2410_ADCDAT0); + data1 = readl(base_addr + S3C2410_ADCDAT1); - ts.is_down = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && - (!(data1 & S3C2410_ADCDAT0_UPDOWN)); + ts.is_down = !(data0 & S3C2410_ADCDAT0_UPDOWN) && + !(data1 & S3C2410_ADCDAT0_UPDOWN); event_type = ts.is_down ? 'D' : 'U'; if (unlikely(__kfifo_put(ts.event_fifo, (unsigned char *)&event_type, - sizeof(int)) != sizeof(int))) /* should not happen */ - printk(KERN_ERR __FILE__": stylus_updown lost event!\n"); + sizeof(int)) != sizeof(int))) + /* Only happens if we have a bug. */ + TSPRINTK("FIFO full\n"); if (ts.is_down) s3c2410_ts_start_adc_conversion(); else - writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC); + writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC); mod_timer(&event_send_timer, jiffies + 1); return IRQ_HANDLED; } -static irqreturn_t stylus_action(int irq, void *dev_id) +static void stylus_adc_action(unsigned p0, unsigned p1, unsigned *conv_left) { int buf[3]; - /* Grab the ADC results. */ - buf[1] = readl(base_addr + S3C2410_ADCDAT0) & - S3C2410_ADCDAT0_XPDATA_MASK; - buf[2] = readl(base_addr + S3C2410_ADCDAT1) & - S3C2410_ADCDAT1_YPDATA_MASK; + /* TODO: Do we really need this? */ + if (p0 & S3C2410_ADCDAT0_AUTO_PST || + p1 & S3C2410_ADCDAT1_AUTO_PST) { + *conv_left = 1; + return; + } + + buf[1] = p0; + buf[2] = p1; switch (ts_filter_chain_feed(ts.chain, &buf[1])) { case 0: /* The filter wants more points. */ - s3c2410_ts_start_adc_conversion(); - return IRQ_HANDLED; + *conv_left = 1; + return; case 1: /* We have a point from the filters or no filtering enabled. */ buf[0] = 'P'; break; default: - printk(KERN_ERR __FILE__ - ":%d Invalid ts_filter_chain_feed return value.\n", - __LINE__); + TSPRINTK("invalid return value\n"); case -1: - /* Error. Ignore the event. */ + /* Too much noise. Ignore the event. */ ts_filter_chain_clear(ts.chain); writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC); - return IRQ_HANDLED; + return; }; if (unlikely(__kfifo_put(ts.event_fifo, (unsigned char *)buf, sizeof(int) * 3) != sizeof(int) * 3)) - printk(KERN_ERR __FILE__":stylus_action bug.\n"); + /* This will only happen if we have a bug. */ + TSPRINTK("FIFO full\n"); writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC); mod_timer(&event_send_timer, jiffies + 1); - return IRQ_HANDLED; + return; } -static struct clk *adc_clock; - /* * The functions for inserting/removing us as a module. */ @@ -339,56 +355,38 @@ static int __init s3c2410ts_probe(struct platform_device *pdev) info = (struct s3c2410_ts_mach_info *)pdev->dev.platform_data; - if (!info) - { - dev_err(&pdev->dev, "Hm... too bad: no platform data for ts\n"); + if (!info) { + dev_err(&pdev->dev, "No platform data\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) { - dev_err(&pdev->dev, "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); + base_addr = ioremap(S3C2410_PA_ADC, 0x20); if (base_addr == NULL) { dev_err(&pdev->dev, "Failed to remap register block\n"); ret = -ENOMEM; goto bail0; } - /* 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(); + ts.adc_client = + s3c_adc_register(pdev, adc_selected_f, stylus_adc_action, 1); + if (!ts.adc_client) { + dev_err(&pdev->dev, + "Unable to register s3c2410_ts as s3_adc client\n"); + iounmap(base_addr); + ret = -EIO; + goto bail0; + } + + input_dev = input_allocate_device(); if (!input_dev) { dev_err(&pdev->dev, "Unable to allocate the input device\n"); ret = -ENOMEM; @@ -415,7 +413,7 @@ static int __init s3c2410ts_probe(struct platform_device *pdev) goto bail2; } - /* create the filter chain set up for the 2 coordinates we produce */ + /* Create the filter chain set up for the 2 coordinates we produce. */ ts.chain = ts_filter_chain_create(pdev, info->filter_config, 2); if (IS_ERR(ts.chain)) @@ -423,42 +421,31 @@ static int __init s3c2410ts_probe(struct platform_device *pdev) ts_filter_chain_clear(ts.chain); - /* Get irqs */ - if (request_irq(IRQ_ADC, stylus_action, IRQF_SAMPLE_RANDOM, - "s3c2410_action", ts.dev)) { - dev_err(&pdev->dev, "Could not allocate ts IRQ_ADC !\n"); - iounmap(base_addr); - ret = -EIO; - goto bail3; - } + /* Get IRQ. */ if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM, "s3c2410_action", ts.dev)) { dev_err(&pdev->dev, "Could not allocate ts IRQ_TC !\n"); - free_irq(IRQ_ADC, ts.dev); iounmap(base_addr); ret = -EIO; - goto bail4; + goto bail3; } dev_info(&pdev->dev, "Successfully loaded\n"); - /* All went ok, so register to the input system */ + /* All went ok. Register to the input system. */ rc = input_register_device(ts.dev); if (rc) { + dev_info(&pdev->dev, "Could not register input device\n"); ret = -EIO; - goto bail5; + goto bail4; } return 0; -bail5: +bail4: free_irq(IRQ_TC, ts.dev); - free_irq(IRQ_ADC, ts.dev); - clk_disable(adc_clock); iounmap(base_addr); disable_irq(IRQ_TC); -bail4: - disable_irq(IRQ_ADC); bail3: ts_filter_chain_destroy(ts.chain); kfifo_free(ts.event_fifo); @@ -473,17 +460,10 @@ bail0: 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; - } + free_irq(IRQ_TC, ts.dev); + s3c_adc_release(ts.adc_client); input_unregister_device(ts.dev); iounmap(base_addr); @@ -495,45 +475,25 @@ static int s3c2410ts_remove(struct platform_device *pdev) } #ifdef CONFIG_PM + +#define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | \ + S3C2410_ADCTSC_XY_PST(0)) + 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); + writel(TSC_SLEEP, base_addr + S3C2410_ADCTSC); + writel(readl(base_addr + S3C2410_ADCCON) | S3C2410_ADCCON_STDBM, + base_addr + S3C2410_ADCCON); 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); - mdelay(1); - ts_filter_chain_clear(ts.chain); - - 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); + writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC); return 0; } @@ -544,27 +504,25 @@ static int s3c2410ts_resume(struct platform_device *pdev) #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, - + .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, - + .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) |