diff options
21 files changed, 298 insertions, 283 deletions
diff --git a/arch/arm/configs/gta01_moredrivers_defconfig b/arch/arm/configs/gta01_moredrivers_defconfig index 36d2b170b52..411acab046f 100644 --- a/arch/arm/configs/gta01_moredrivers_defconfig +++ b/arch/arm/configs/gta01_moredrivers_defconfig @@ -180,7 +180,7 @@ CONFIG_S3C2410_CLOCK=y CONFIG_S3C24XX_GPIO_EXTRA=0 CONFIG_S3C2410_DMA=y # CONFIG_S3C2410_DMA_DEBUG is not set -# CONFIG_S3C24XX_ADC is not set +CONFIG_S3C24XX_ADC=y CONFIG_MACH_NEO1973=y CONFIG_PLAT_S3C=y CONFIG_CPU_LLSERIAL_S3C2410_ONLY=y @@ -1192,11 +1192,7 @@ CONFIG_SSB_POSSIBLE=y CONFIG_MFD_PCF50606=y CONFIG_PCF50606_ADC=y CONFIG_PCF50606_GPO=y -CONFIG_MFD_GLAMO=y -CONFIG_MFD_GLAMO_FB=y -CONFIG_MFD_GLAMO_SPI_GPIO=y -CONFIG_MFD_GLAMO_SPI_FB=y -CONFIG_MFD_GLAMO_MCI=y +# CONFIG_MFD_GLAMO is not set # # Multimedia devices diff --git a/arch/arm/configs/gta02_micro_defconfig b/arch/arm/configs/gta02_micro_defconfig index ae30c4b1ddb..6a6ff77ea2c 100644 --- a/arch/arm/configs/gta02_micro_defconfig +++ b/arch/arm/configs/gta02_micro_defconfig @@ -158,7 +158,7 @@ CONFIG_S3C24XX_PWM=y CONFIG_S3C24XX_GPIO_EXTRA=0 CONFIG_S3C2410_DMA=y # CONFIG_S3C2410_DMA_DEBUG is not set -# CONFIG_S3C24XX_ADC is not set +CONFIG_S3C24XX_ADC=y CONFIG_MACH_NEO1973=y CONFIG_PLAT_S3C=y CONFIG_CPU_LLSERIAL_S3C2440_ONLY=y diff --git a/arch/arm/configs/gta02_moredrivers_defconfig b/arch/arm/configs/gta02_moredrivers_defconfig index bd66869f74a..13b41215faa 100644 --- a/arch/arm/configs/gta02_moredrivers_defconfig +++ b/arch/arm/configs/gta02_moredrivers_defconfig @@ -180,7 +180,7 @@ CONFIG_S3C24XX_PWM=y CONFIG_S3C24XX_GPIO_EXTRA=0 CONFIG_S3C2410_DMA=y # CONFIG_S3C2410_DMA_DEBUG is not set -# CONFIG_S3C24XX_ADC is not set +CONFIG_S3C24XX_ADC=y CONFIG_MACH_SMDK=y CONFIG_MACH_NEO1973=y CONFIG_PLAT_S3C=y diff --git a/arch/arm/configs/gta02_packaging_defconfig b/arch/arm/configs/gta02_packaging_defconfig index 15defd28841..03c72d5652b 100644 --- a/arch/arm/configs/gta02_packaging_defconfig +++ b/arch/arm/configs/gta02_packaging_defconfig @@ -180,7 +180,7 @@ CONFIG_S3C24XX_PWM=y CONFIG_S3C24XX_GPIO_EXTRA=0 CONFIG_S3C2410_DMA=y # CONFIG_S3C2410_DMA_DEBUG is not set -# CONFIG_S3C24XX_ADC is not set +CONFIG_S3C24XX_ADC=y CONFIG_MACH_SMDK=y CONFIG_MACH_NEO1973=y CONFIG_PLAT_S3C=y diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig index f4f7af180aa..02f406bea08 100644 --- a/arch/arm/mach-s3c2410/Kconfig +++ b/arch/arm/mach-s3c2410/Kconfig @@ -155,6 +155,7 @@ config MACH_NEO1973_GTA01 select PCF50606_WATCHDOG select POWER_SUPPLY select BATTERY_GTA01 + select S3C24XX_ADC help Say Y here if you are using the FIC Neo1973 GSM Phone diff --git a/arch/arm/mach-s3c2410/mach-gta01.c b/arch/arm/mach-s3c2410/mach-gta01.c index 8a13a4adcfd..21726956ff6 100644 --- a/arch/arm/mach-s3c2410/mach-gta01.c +++ b/arch/arm/mach-s3c2410/mach-gta01.c @@ -560,6 +560,7 @@ static struct platform_device *gta01_devices[] __initdata = { &s3c_device_sdi, &s3c_device_usbgadget, &s3c_device_nand, + &s3c_device_adc, &s3c_device_ts, }; diff --git a/arch/arm/mach-s3c2442/Kconfig b/arch/arm/mach-s3c2442/Kconfig index aa184ba7586..13fb5e190b4 100644 --- a/arch/arm/mach-s3c2442/Kconfig +++ b/arch/arm/mach-s3c2442/Kconfig @@ -36,6 +36,7 @@ config MACH_NEO1973_GTA02 select S3C_PWM select FIQ select S3C_DEV_USB_HOST + select S3C24XX_ADC help Say Y here if you are using the FIC Neo1973 GSM Phone diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c index 0a93749ef8c..42b8cea9ff1 100644 --- a/arch/arm/mach-s3c2442/mach-gta02.c +++ b/arch/arm/mach-s3c2442/mach-gta02.c @@ -1562,6 +1562,7 @@ static struct platform_device *gta02_devices[] __initdata = { &s3c_device_usbgadget, &s3c_device_nand, >a02_nor_flash, + &s3c_device_adc, &s3c24xx_pwm_device, >a02_led_dev, diff --git a/arch/arm/plat-s3c/include/plat/adc.h b/arch/arm/plat-s3c/include/plat/adc.h index 43df2a404b0..d847bd476b6 100644 --- a/arch/arm/plat-s3c/include/plat/adc.h +++ b/arch/arm/plat-s3c/include/plat/adc.h @@ -19,10 +19,12 @@ struct s3c_adc_client; extern int s3c_adc_start(struct s3c_adc_client *client, unsigned int channel, unsigned int nr_samples); -extern struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev, - void (*select)(unsigned selected), - void (*conv)(unsigned d0, unsigned d1), - unsigned int is_ts); +extern struct s3c_adc_client * + s3c_adc_register(struct platform_device *pdev, + void (*select)(unsigned selected), + void (*conv)(unsigned d0, unsigned d1, + unsigned *samples_left), + unsigned int is_ts); extern void s3c_adc_release(struct s3c_adc_client *client); diff --git a/arch/arm/plat-s3c24xx/adc.c b/arch/arm/plat-s3c24xx/adc.c index 9a5c767e0a4..9056bcc6c17 100644 --- a/arch/arm/plat-s3c24xx/adc.c +++ b/arch/arm/plat-s3c24xx/adc.c @@ -45,7 +45,8 @@ struct s3c_adc_client { unsigned char channel; void (*select_cb)(unsigned selected); - void (*convert_cb)(unsigned val1, unsigned val2); + void (*convert_cb)(unsigned val1, unsigned val2, + unsigned *samples_left); }; struct adc_device { @@ -57,6 +58,7 @@ struct adc_device { void __iomem *regs; unsigned int prescale; + unsigned int delay; int irq; }; @@ -67,10 +69,19 @@ static LIST_HEAD(adc_pending); #define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg) +#define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | \ + S3C2410_ADCTSC_XP_SEN | S3C2410_ADCTSC_AUTO_PST | \ + S3C2410_ADCTSC_XY_PST(0)) + + static inline void s3c_adc_convert(struct adc_device *adc) { unsigned con = readl(adc->regs + S3C2410_ADCCON); + if (adc->cur->is_ts) + writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, + adc->regs + S3C2410_ADCTSC); + con |= S3C2410_ADCCON_ENABLE_START; writel(con, adc->regs + S3C2410_ADCCON); } @@ -158,7 +169,8 @@ static void s3c_adc_default_select(unsigned select) struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev, void (*select)(unsigned int selected), - void (*conv)(unsigned d0, unsigned d1), + void (*conv)(unsigned d0, unsigned d1, + unsigned *samples_left), unsigned int is_ts) { struct s3c_adc_client *client; @@ -210,9 +222,10 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw) data1 = readl(adc->regs + S3C2410_ADCDAT1); adc_dbg(adc, "read %d: 0x%04x, 0x%04x\n", client->nr_samples, data0, data1); - (client->convert_cb)(data0 & 0x3ff, data1 & 0x3ff); + client->nr_samples--; + (client->convert_cb)(data0 & 0x3ff, data1 & 0x3ff, &client->nr_samples); - if (--client->nr_samples > 0) { + if (client->nr_samples > 0) { /* fire another conversion for this */ client->select_cb(1); @@ -244,6 +257,7 @@ static int s3c_adc_probe(struct platform_device *pdev) adc->pdev = pdev; adc->prescale = S3C2410_ADCCON_PRSCVL(49); + adc->delay = 0x2710; adc->irq = platform_get_irq(pdev, 1); if (adc->irq <= 0) { @@ -283,6 +297,7 @@ static int s3c_adc_probe(struct platform_device *pdev) writel(adc->prescale | S3C2410_ADCCON_PRSCEN, adc->regs + S3C2410_ADCCON); + writel(adc->delay, adc->regs + S3C2410_ADCDLY); dev_info(dev, "attached adc driver\n"); @@ -338,6 +353,7 @@ static int s3c_adc_resume(struct platform_device *pdev) writel(adc->prescale | S3C2410_ADCCON_PRSCEN, adc->regs + S3C2410_ADCCON); + writel(adc->delay, adc->regs + S3C2410_ADCDLY); return 0; } diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c index 0fdaa54ada9..eeb95f863ce 100644 --- a/arch/arm/plat-s3c24xx/devs.c +++ b/arch/arm/plat-s3c24xx/devs.c @@ -205,6 +205,7 @@ EXPORT_SYMBOL(s3c_device_nand); struct platform_device s3c_device_ts = { .name = "s3c2410-ts", .id = -1, + .dev.parent = &s3c_device_adc.dev, }; EXPORT_SYMBOL(s3c_device_ts); diff --git a/drivers/ar6000/ar6000/ar6000_drv.c b/drivers/ar6000/ar6000/ar6000_drv.c index 21504f221dd..2941d5ed8c2 100644 --- a/drivers/ar6000/ar6000/ar6000_drv.c +++ b/drivers/ar6000/ar6000/ar6000_drv.c @@ -221,6 +221,15 @@ static void ar6000_detect_error(unsigned long ptr); static struct net_device_stats *ar6000_get_stats(struct net_device *dev); static struct iw_statistics *ar6000_get_iwstats(struct net_device * dev); +static struct net_device_ops ar6000_netdev_ops = { + .ndo_open = ar6000_open, + .ndo_stop = ar6000_close, + .ndo_start_xmit = ar6000_data_tx, + .ndo_validate_addr = eth_validate_addr, + .ndo_do_ioctl = ar6000_ioctl, + .ndo_get_stats = &ar6000_get_stats, +}; + /* * HTC service connection handlers */ @@ -858,20 +867,15 @@ ar6000_avail_ev(HTC_HANDLE HTCHandle) spin_lock_init(&ar->arLock); /* Don't install the init function if BMI is requested */ - if(!bmienable) - { - dev->init = ar6000_init; + if (!bmienable) { + ar6000_netdev_ops.ndo_init = ar6000_init; } else { AR_DEBUG_PRINTF(" BMI enabled \n"); } - dev->open = &ar6000_open; - dev->stop = &ar6000_close; - dev->hard_start_xmit = &ar6000_data_tx; - dev->get_stats = &ar6000_get_stats; + dev->netdev_ops = &ar6000_netdev_ops; /* dev->tx_timeout = ar6000_tx_timeout; */ - dev->do_ioctl = &ar6000_ioctl; dev->watchdog_timeo = AR6000_TX_TIMEOUT; ar6000_ioctl_iwsetup(&ath_iw_handler_def); dev->wireless_handlers = &ath_iw_handler_def; 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) diff --git a/drivers/input/touchscreen/ts_filter.h b/drivers/input/touchscreen/ts_filter.h index aac1e028d0b..632e5fb3f4e 100644 --- a/drivers/input/touchscreen/ts_filter.h +++ b/drivers/input/touchscreen/ts_filter.h @@ -4,7 +4,7 @@ /* * Touchscreen filter. * - * (c) 2008,2009 Andy Green <andy@openmoko.com> + * (c) 2008,2009 Andy Green <andy@warmcat.com> */ #include <linux/platform_device.h> diff --git a/drivers/input/touchscreen/ts_filter_chain.c b/drivers/input/touchscreen/ts_filter_chain.c index a6346bdba3e..17793ace144 100644 --- a/drivers/input/touchscreen/ts_filter_chain.c +++ b/drivers/input/touchscreen/ts_filter_chain.c @@ -13,7 +13,7 @@ * 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) 2008,2009 Andy Green <andy@openmoko.com> + * Copyright (c) 2008,2009 Andy Green <andy@warmcat.com> */ #include <linux/kernel.h> diff --git a/drivers/input/touchscreen/ts_filter_chain.h b/drivers/input/touchscreen/ts_filter_chain.h index 806bffe6c99..065a6a026d2 100644 --- a/drivers/input/touchscreen/ts_filter_chain.h +++ b/drivers/input/touchscreen/ts_filter_chain.h @@ -4,7 +4,7 @@ /* * Touchscreen filter chains. * - * (c) 2008,2009 Andy Green <andy@openmoko.com> + * (c) 2008,2009 Andy Green <andy@warmcat.com> */ #include "ts_filter.h" diff --git a/drivers/input/touchscreen/ts_filter_group.c b/drivers/input/touchscreen/ts_filter_group.c index b7c3f3ddcb9..722e8dfbdae 100644 --- a/drivers/input/touchscreen/ts_filter_group.c +++ b/drivers/input/touchscreen/ts_filter_group.c @@ -20,29 +20,39 @@ * * This filter is useful to reject samples that are not reliable. We consider * that a sample is not reliable if it deviates form the Majority. + * This filter mixes information from all the available dimensions. It means + * that for two dimensions we draw a rectangle where the thought-to-be good + * points can be found. * - * 1) We collect S samples. + * The implementation would be more efficient with a double-linked list but + * let's keep it simple for now. * - * 2) For each dimension: - * - * - We sort the points. + * 1) We collect S samples and keep it in sorted sets. * - Points that are "close enough" are considered to be in the same set. - * - We choose the set with more elements. If more than "threshold" - * points are in this set we use the first and the last point of the set - * to define the valid range for this dimension [min, max], otherwise we - * discard all the points and go to step 1. + * We don't actually keep the sets but ranges of points. + * + * 2) For each dimension: + * - We choose the range with more elements. If more than "threshold" + * points are in this range we use the minimum and the maximum point + * of the range to define the valid range for this dimension [min, max], + * otherwise we discard all the points and the ranges and go to step 1. * * 3) We consider the unsorted S samples and try to feed them to the next * filter in the chain. If one of the points of each sample - * is not in the allowed range for its dimension, we discard the sample. + * is not in the allowed range for its dimension we discard the sample. * */ #include <linux/kernel.h> #include <linux/slab.h> -#include <linux/sort.h> #include "ts_filter_group.h" +struct coord_range { + int min; /* Minimum value of the range. */ + int max; /* Maximum value of the range */ + int N; /* Number of points in the range. */ +}; + struct ts_filter_group { /* Private filter configuration. */ struct ts_filter_group_configuration *config; @@ -52,13 +62,15 @@ struct ts_filter_group { int N; /* How many samples we have. */ int *samples[MAX_TS_FILTER_COORDS]; /* The samples: our input. */ - int *group_size; /* Used for temporal computations. */ - int *sorted_samples; /* Used for temporal computations. */ + /* Temporal values that help us compute range_min and range_max. */ + struct coord_range *ranges[MAX_TS_FILTER_COORDS]; /* Ranges. */ + int n_ranges[MAX_TS_FILTER_COORDS]; /* Number of ranges */ + /* Computed ranges that help us filter the points. */ int range_max[MAX_TS_FILTER_COORDS]; /* Max. computed ranges. */ int range_min[MAX_TS_FILTER_COORDS]; /* Min. computed ranges. */ - int tries_left; /* We finish if we don't get enough samples. */ + int tries_left; /* We finish if we can't get enough samples. */ int ready; /* If we are ready to deliver samples. */ int result; /* Index of the point being returned. */ }; @@ -70,10 +82,13 @@ struct ts_filter_group { static void ts_filter_group_clear_internal(struct ts_filter_group *tsfg, int attempts) { + int n; tsfg->N = 0; tsfg->tries_left = attempts; tsfg->ready = 0; tsfg->result = 0; + for (n = 0; n < tsfg->tsf.count_coords; n++) + tsfg->n_ranges[n] = 0; } static void ts_filter_group_clear(struct ts_filter *tsf) @@ -101,8 +116,9 @@ static struct ts_filter *ts_filter_group_create( tsfg->tsf.count_coords = count_coords; BUG_ON(tsfg->config->attempts <= 0); + BUG_ON(tsfg->config->length < tsfg->config->threshold); - tsfg->samples[0] = kmalloc((2 + count_coords) * sizeof(int) * + tsfg->samples[0] = kmalloc(count_coords * sizeof(int) * tsfg->config->length, GFP_KERNEL); if (!tsfg->samples[0]) { kfree(tsfg); @@ -110,10 +126,16 @@ static struct ts_filter *ts_filter_group_create( } for (i = 1; i < count_coords; ++i) tsfg->samples[i] = tsfg->samples[0] + i * tsfg->config->length; - tsfg->sorted_samples = tsfg->samples[0] + count_coords * - tsfg->config->length; - tsfg->group_size = tsfg->samples[0] + (1 + count_coords) * - tsfg->config->length; + + tsfg->ranges[0] = kmalloc(count_coords * sizeof(struct coord_range) * + tsfg->config->length, GFP_KERNEL); + if (!tsfg->ranges[0]) { + kfree(tsfg->samples[0]); + kfree(tsfg); + return NULL; + } + for (i = 1; i < count_coords; ++i) + tsfg->ranges[i] = tsfg->ranges[0] + i * tsfg->config->length; ts_filter_group_clear_internal(tsfg, tsfg->config->attempts); @@ -128,77 +150,90 @@ static void ts_filter_group_destroy(struct ts_filter *tsf) { struct ts_filter_group *tsfg = ts_filter_to_filter_group(tsf); - kfree(tsfg->samples[0]); /* first guy has pointer from kmalloc */ + kfree(tsfg->samples[0]); + kfree(tsfg->ranges[0]); kfree(tsf); } -static int int_cmp(const void *_a, const void *_b) -{ - const int *a = _a; - const int *b = _b; +static void ts_filter_group_prepare_next(struct ts_filter *tsf); - if (*a > *b) - return 1; - if (*a < *b) - return -1; - return 0; -} +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define IN_RANGE(c, r) ((c) >= (r).min - tsfg->config->close_enough && \ + (c) <= (r).max + tsfg->config->close_enough) -static void ts_filter_group_prepare_next(struct ts_filter *tsf); +static void delete_spot(struct coord_range *v, int n, int size) +{ + int i; + for (i = n; i < size - 1; ++i) + v[i] = v[i + 1]; +} static int ts_filter_group_process(struct ts_filter *tsf, int *coords) { struct ts_filter_group *tsfg = ts_filter_to_filter_group(tsf); int n; - int i; + int j; BUG_ON(tsfg->N >= tsfg->config->length); BUG_ON(tsfg->ready); - for (n = 0; n < tsf->count_coords; n++) - tsfg->samples[n][tsfg->N] = coords[n]; + for (n = 0; n < tsfg->tsf.count_coords; n++) { + int i; + struct coord_range *range = tsfg->ranges[n]; + int *n_ranges = &tsfg->n_ranges[n]; + int found = 0; - if (++tsfg->N < tsfg->config->length) - return 0; /* We need more samples. */ + tsfg->samples[n][tsfg->N] = coords[n]; - for (n = 0; n < tsfg->tsf.count_coords; n++) { - int *v = tsfg->sorted_samples; - int ngroups = 0; - int best_size; - int best_idx = 0; - int idx = 0; - - memcpy(v, tsfg->samples[n], tsfg->N * sizeof(int)); - /* - * FIXME: Remove this sort call. We already have the - * algorithm for this modification. The filter will - * need less points (about half) if there is not a - * lot of noise. Right now we are doing a constant - * amount of work no matter how much noise we are - * dealing with. - */ - sort(v, tsfg->N, sizeof(int), int_cmp, NULL); - - tsfg->group_size[0] = 1; - for (i = 1; i < tsfg->N; ++i) { - if (v[i] - v[i - 1] <= tsfg->config->close_enough) - tsfg->group_size[ngroups]++; - else - tsfg->group_size[++ngroups] = 1; + for (i = 0; i < *n_ranges; ++i) { + if (IN_RANGE(coords[n], range[i])) { + range[i].min = MIN(range[i].min, coords[n]); + range[i].max = MAX(range[i].max, coords[n]); + range[i].N++; + found = 1; + break; + } else if (coords[n] <= range[i].min) + break; /* We need to insert a range. */ } - ngroups++; - - best_size = tsfg->group_size[0]; - for (i = 1; i < ngroups; i++) { - idx += tsfg->group_size[i - 1]; - if (best_size < tsfg->group_size[i]) { - best_size = tsfg->group_size[i]; - best_idx = idx; + if (found) { /* We might need to melt ranges. */ + if (i && range[i - 1].max + tsfg->config->close_enough + >= range[i].min) { + BUG_ON(range[i - 1].max >= range[i].max); + range[i - 1].max = range[i].max; + range[i - 1].N += range[i].N; + delete_spot(range, i, *n_ranges); + (*n_ranges)--; + i--; + } + if (i < *n_ranges - 1 && range[i + 1].min - + tsfg->config->close_enough <= range[i].max) { + range[i].max = range[i + 1].max; + range[i].N += range[i + 1].N; + delete_spot(range, i + 1, *n_ranges); + (*n_ranges)--; } + } else { + BUG_ON((*n_ranges) >= tsfg->config->length); + (*n_ranges)++; + for (j = *n_ranges - 1; j > i; --j) + range[j] = range[j - 1]; + range[i].N = 1; + range[i].min = coords[n]; + range[i].max = coords[n]; } + } - if (best_size < tsfg->config->threshold) { - /* This set is not good enough for us. */ + if (++tsfg->N < tsfg->config->length) + return 0; + + for (n = 0; n < tsfg->tsf.count_coords; ++n) { + int best = 0; + for (j = 1; j < tsfg->n_ranges[n]; ++j) + if (tsfg->ranges[n][best].N < tsfg->ranges[n][j].N) + best = j; + if (tsfg->ranges[n][best].N < tsfg->config->threshold) { + /* This set of points is not good enough for us. */ if (--tsfg->tries_left) { ts_filter_group_clear_internal (tsfg, tsfg->tries_left); @@ -207,9 +242,8 @@ static int ts_filter_group_process(struct ts_filter *tsf, int *coords) } return 1; /* We give up: error. */ } - - tsfg->range_min[n] = v[best_idx]; - tsfg->range_max[n] = v[best_idx + best_size - 1]; + tsfg->range_min[n] = tsfg->ranges[n][best].min; + tsfg->range_max[n] = tsfg->ranges[n][best].max; } ts_filter_group_prepare_next(tsf); diff --git a/drivers/input/touchscreen/ts_filter_mean.c b/drivers/input/touchscreen/ts_filter_mean.c index 7621ded3ec8..0c604321713 100644 --- a/drivers/input/touchscreen/ts_filter_mean.c +++ b/drivers/input/touchscreen/ts_filter_mean.c @@ -14,7 +14,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright (c) 2008,2009 - * Andy Green <andy@openmoko.com> + * Andy Green <andy@warmcat.com> * Nelson Castillo <arhuaco@freaks-unidos.net> * * Simple mean filter. diff --git a/drivers/input/touchscreen/ts_filter_mean.h b/drivers/input/touchscreen/ts_filter_mean.h index f5b5e4bbe10..80f9b215dff 100644 --- a/drivers/input/touchscreen/ts_filter_mean.h +++ b/drivers/input/touchscreen/ts_filter_mean.h @@ -9,7 +9,7 @@ * mean * * (c) 2008,2009 - * Andy Green <andy@openmoko.com> + * Andy Green <andy@warmcat.com> * Nelson Castillo <arhuaco@freaks-unidos.net> */ diff --git a/drivers/input/touchscreen/ts_filter_median.c b/drivers/input/touchscreen/ts_filter_median.c index b8a6206d1b7..6f8aae5941d 100644 --- a/drivers/input/touchscreen/ts_filter_median.c +++ b/drivers/input/touchscreen/ts_filter_median.c @@ -13,7 +13,7 @@ * 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) 2008 Andy Green <andy@openmoko.com> + * Copyright (c) 2008 Andy Green <andy@warmcat.com> * * * Median averaging stuff. We sort incoming raw samples into an array of diff --git a/drivers/input/touchscreen/ts_filter_median.h b/drivers/input/touchscreen/ts_filter_median.h index 1c19472e375..b13f361bb6a 100644 --- a/drivers/input/touchscreen/ts_filter_median.h +++ b/drivers/input/touchscreen/ts_filter_median.h @@ -8,7 +8,7 @@ * * median * - * (c) 2008 Andy Green <andy@openmoko.com> + * (c) 2008 Andy Green <andy@warmcat.com> */ struct ts_filter_median_configuration { |