diff options
author | Thomas Kunze <thommycheck@gmx.de> | 2009-02-10 20:59:05 +0100 |
---|---|---|
committer | Thomas Kunze <thommycheck@gmx.de> | 2009-02-10 20:59:05 +0100 |
commit | 4be205e17f2f4497ff936255c5bba7553bc39dc9 (patch) | |
tree | f3b472118758d0f1b352834810b7bd294a4454d0 /packages/linux/linux-2.6.28/collie/0012-move-ucb1200-ts-driver.patch | |
parent | 0345d624647954e3a49e05b8c221f8281e0897eb (diff) |
linux_2.6.28: add collie patches
Diffstat (limited to 'packages/linux/linux-2.6.28/collie/0012-move-ucb1200-ts-driver.patch')
-rw-r--r-- | packages/linux/linux-2.6.28/collie/0012-move-ucb1200-ts-driver.patch | 981 |
1 files changed, 981 insertions, 0 deletions
diff --git a/packages/linux/linux-2.6.28/collie/0012-move-ucb1200-ts-driver.patch b/packages/linux/linux-2.6.28/collie/0012-move-ucb1200-ts-driver.patch new file mode 100644 index 0000000000..c1aafef5e2 --- /dev/null +++ b/packages/linux/linux-2.6.28/collie/0012-move-ucb1200-ts-driver.patch @@ -0,0 +1,981 @@ +From 9e0d71c4a6247d88d3b772f6b05bcaa39711a937 Mon Sep 17 00:00:00 2001 +From: Thomas Kunze <thommycheck@gmx.de> +Date: Tue, 10 Feb 2009 19:31:25 +0100 +Subject: [PATCH 12/23] move ucb1200-ts driver + +Move the touchscreen driver to drivers/input/touchscreen +where touchscreen drivers belong. + +Conflicts: + + drivers/input/touchscreen/Makefile + drivers/mfd/Kconfig + drivers/mfd/Makefile + +Conflicts: + + drivers/mfd/Kconfig + drivers/mfd/Makefile +--- + drivers/input/touchscreen/Kconfig | 7 + + drivers/input/touchscreen/Makefile | 1 + + drivers/input/touchscreen/ucb1x00-ts.c | 438 ++++++++++++++++++++++++++++++++ + drivers/mfd/Kconfig | 3 - + drivers/mfd/Makefile | 3 +- + drivers/mfd/ucb1x00-ts.c | 438 -------------------------------- + 6 files changed, 447 insertions(+), 443 deletions(-) + create mode 100644 drivers/input/touchscreen/ucb1x00-ts.c + delete mode 100644 drivers/mfd/ucb1x00-ts.c + +diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig +index 3d1ab8f..3ac8cd6 100644 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -221,6 +221,13 @@ config TOUCHSCREEN_ATMEL_TSADCC + To compile this driver as a module, choose M here: the + module will be called atmel_tsadcc. + ++config TOUCHSCREEN_UCB1200_TS ++ tristate "Philips UCB1200 touchscreen" ++ depends on MCP_UCB1200 ++ help ++ This enabled support for the Pilips UCB1200 touchscreen interface ++ and compatible. ++ + config TOUCHSCREEN_UCB1400 + tristate "Philips UCB1400 touchscreen" + depends on AC97_BUS +diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile +index 15cf290..77ba930 100644 +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -25,6 +25,7 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o + obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o + obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o + obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o ++obj-$(CONFIG_TOUCHSCREEN_UCB1200_TS) += ucb1x00-ts.o + obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o + obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o + wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o +diff --git a/drivers/input/touchscreen/ucb1x00-ts.c b/drivers/input/touchscreen/ucb1x00-ts.c +new file mode 100644 +index 0000000..b5feae9 +--- /dev/null ++++ b/drivers/input/touchscreen/ucb1x00-ts.c +@@ -0,0 +1,438 @@ ++/* ++ * Touchscreen driver for UCB1x00-based touchscreens ++ * ++ * Copyright (C) 2001 Russell King, All Rights Reserved. ++ * Copyright (C) 2005 Pavel Machek ++ * ++ * 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. ++ * ++ * 21-Jan-2002 <jco@ict.es> : ++ * ++ * Added support for synchronous A/D mode. This mode is useful to ++ * avoid noise induced in the touchpanel by the LCD, provided that ++ * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin. ++ * It is important to note that the signal connected to the ADCSYNC ++ * pin should provide pulses even when the LCD is blanked, otherwise ++ * a pen touch needed to unblank the LCD will never be read. ++ */ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/smp.h> ++#include <linux/sched.h> ++#include <linux/completion.h> ++#include <linux/delay.h> ++#include <linux/string.h> ++#include <linux/input.h> ++#include <linux/device.h> ++#include <linux/freezer.h> ++#include <linux/slab.h> ++#include <linux/kthread.h> ++#include <linux/mfd/ucb1x00.h> ++ ++#include <asm/dma.h> ++#include <mach/collie.h> ++#include <asm/mach-types.h> ++ ++ ++ ++struct ucb1x00_ts { ++ struct input_dev *idev; ++ struct ucb1x00 *ucb; ++ ++ wait_queue_head_t irq_wait; ++ struct task_struct *rtask; ++ u16 x_res; ++ u16 y_res; ++ ++ unsigned int restart:1; ++ unsigned int adcsync:1; ++}; ++ ++static int adcsync; ++ ++static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) ++{ ++ struct input_dev *idev = ts->idev; ++ ++ input_report_abs(idev, ABS_X, x); ++ input_report_abs(idev, ABS_Y, y); ++ input_report_abs(idev, ABS_PRESSURE, pressure); ++ input_sync(idev); ++} ++ ++static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) ++{ ++ struct input_dev *idev = ts->idev; ++ ++ input_report_abs(idev, ABS_PRESSURE, 0); ++ input_sync(idev); ++} ++ ++/* ++ * Switch to interrupt mode. ++ */ ++static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts) ++{ ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | ++ UCB_TS_CR_MODE_INT); ++} ++ ++/* ++ * Switch to pressure mode, and read pressure. We don't need to wait ++ * here, since both plates are being driven. ++ */ ++static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts) ++{ ++ if (machine_is_collie()) { ++ ucb1x00_io_write(ts->ucb, COLLIE_TC35143_GPIO_TBL_CHK, 0); ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_POW | ++ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); ++ ++ udelay(55); ++ ++ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_AD2, ts->adcsync); ++ } else { ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ ++ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); ++ } ++} ++ ++/* ++ * Switch to X position mode and measure Y plate. We switch the plate ++ * configuration in pressure mode, then switch to position mode. This ++ * gives a faster response time. Even so, we need to wait about 55us ++ * for things to stabilise. ++ */ ++static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts) ++{ ++ if (machine_is_collie()) ++ ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK); ++ else { ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ } ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); ++ ++ udelay(55); ++ ++ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); ++} ++ ++/* ++ * Switch to Y position mode and measure X plate. We switch the plate ++ * configuration in pressure mode, then switch to position mode. This ++ * gives a faster response time. Even so, we need to wait about 55us ++ * for things to stabilise. ++ */ ++static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts) ++{ ++ if (machine_is_collie()) ++ ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK); ++ else { ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ } ++ ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | ++ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); ++ ++ udelay(55); ++ ++ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); ++} ++ ++/* ++ * Switch to X plate resistance mode. Set MX to ground, PX to ++ * supply. Measure current. ++ */ ++static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts) ++{ ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); ++} ++ ++/* ++ * Switch to Y plate resistance mode. Set MY to ground, PY to ++ * supply. Measure current. ++ */ ++static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts) ++{ ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); ++} ++ ++static inline int ucb1x00_ts_pen_down(struct ucb1x00_ts *ts) ++{ ++ unsigned int val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR); ++ ++ if (machine_is_collie()) ++ return (!(val & (UCB_TS_CR_TSPX_LOW))); ++ else ++ return (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)); ++} ++ ++/* ++ * This is a RT kernel thread that handles the ADC accesses ++ * (mainly so we can use semaphores in the UCB1200 core code ++ * to serialise accesses to the ADC). ++ */ ++static int ucb1x00_thread(void *_ts) ++{ ++ struct ucb1x00_ts *ts = _ts; ++ DECLARE_WAITQUEUE(wait, current); ++ int valid = 0; ++ ++ set_freezable(); ++ add_wait_queue(&ts->irq_wait, &wait); ++ while (!kthread_should_stop()) { ++ unsigned int x, y, p; ++ signed long timeout; ++ ++ ts->restart = 0; ++ ++ ucb1x00_adc_enable(ts->ucb); ++ ++ x = ucb1x00_ts_read_xpos(ts); ++ y = ucb1x00_ts_read_ypos(ts); ++ p = ucb1x00_ts_read_pressure(ts); ++ ++ /* ++ * Switch back to interrupt mode. ++ */ ++ ucb1x00_ts_mode_int(ts); ++ ucb1x00_adc_disable(ts->ucb); ++ ++ msleep(10); ++ ++ ucb1x00_enable(ts->ucb); ++ ++ ++ if (ucb1x00_ts_pen_down(ts)) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING); ++ ucb1x00_disable(ts->ucb); ++ ++ /* ++ * If we spat out a valid sample set last time, ++ * spit out a "pen off" sample here. ++ */ ++ if (valid) { ++ ucb1x00_ts_event_release(ts); ++ valid = 0; ++ } ++ ++ timeout = MAX_SCHEDULE_TIMEOUT; ++ } else { ++ ucb1x00_disable(ts->ucb); ++ ++ /* ++ * Filtering is policy. Policy belongs in user ++ * space. We therefore leave it to user space ++ * to do any filtering they please. ++ */ ++ if (!ts->restart) { ++ ucb1x00_ts_evt_add(ts, p, x, y); ++ valid = 1; ++ } ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ timeout = HZ / 100; ++ } ++ ++ try_to_freeze(); ++ ++ schedule_timeout(timeout); ++ } ++ ++ remove_wait_queue(&ts->irq_wait, &wait); ++ ++ ts->rtask = NULL; ++ return 0; ++} ++ ++/* ++ * We only detect touch screen _touches_ with this interrupt ++ * handler, and even then we just schedule our task. ++ */ ++static void ucb1x00_ts_irq(int idx, void *id) ++{ ++ struct ucb1x00_ts *ts = id; ++ ++ ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); ++ wake_up(&ts->irq_wait); ++} ++ ++static int ucb1x00_ts_open(struct input_dev *idev) ++{ ++ struct ucb1x00_ts *ts = input_get_drvdata(idev); ++ int ret = 0; ++ ++ BUG_ON(ts->rtask); ++ ++ init_waitqueue_head(&ts->irq_wait); ++ ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts); ++ if (ret < 0) ++ goto out; ++ ++ /* ++ * If we do this at all, we should allow the user to ++ * measure and read the X and Y resistance at any time. ++ */ ++ ucb1x00_adc_enable(ts->ucb); ++ ts->x_res = ucb1x00_ts_read_xres(ts); ++ ts->y_res = ucb1x00_ts_read_yres(ts); ++ ucb1x00_adc_disable(ts->ucb); ++ ++ ts->rtask = kthread_run(ucb1x00_thread, ts, "ktsd"); ++ if (!IS_ERR(ts->rtask)) { ++ ret = 0; ++ } else { ++ ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); ++ ts->rtask = NULL; ++ ret = -EFAULT; ++ } ++ ++ out: ++ return ret; ++} ++ ++/* ++ * Release touchscreen resources. Disable IRQs. ++ */ ++static void ucb1x00_ts_close(struct input_dev *idev) ++{ ++ struct ucb1x00_ts *ts = input_get_drvdata(idev); ++ ++ if (ts->rtask) ++ kthread_stop(ts->rtask); ++ ++ ucb1x00_enable(ts->ucb); ++ ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); ++ ucb1x00_disable(ts->ucb); ++} ++ ++#ifdef CONFIG_PM ++static int ucb1x00_ts_resume(struct ucb1x00_dev *dev) ++{ ++ struct ucb1x00_ts *ts = dev->priv; ++ ++ if (ts->rtask != NULL) { ++ /* ++ * Restart the TS thread to ensure the ++ * TS interrupt mode is set up again ++ * after sleep. ++ */ ++ ts->restart = 1; ++ wake_up(&ts->irq_wait); ++ } ++ return 0; ++} ++#else ++#define ucb1x00_ts_resume NULL ++#endif ++ ++ ++/* ++ * Initialisation. ++ */ ++static int ucb1x00_ts_add(struct ucb1x00_dev *dev) ++{ ++ struct ucb1x00_ts *ts; ++ struct input_dev *idev; ++ int err; ++ ++ ts = kzalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL); ++ idev = input_allocate_device(); ++ if (!ts || !idev) { ++ err = -ENOMEM; ++ goto fail; ++ } ++ ++ ts->ucb = dev->ucb; ++ ts->idev = idev; ++ ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC; ++ ++ idev->name = "Touchscreen panel"; ++ idev->id.product = ts->ucb->id; ++ idev->open = ucb1x00_ts_open; ++ idev->close = ucb1x00_ts_close; ++ ++ __set_bit(EV_ABS, idev->evbit); ++ __set_bit(ABS_X, idev->absbit); ++ __set_bit(ABS_Y, idev->absbit); ++ __set_bit(ABS_PRESSURE, idev->absbit); ++ ++ input_set_drvdata(idev, ts); ++ ++ err = input_register_device(idev); ++ if (err) ++ goto fail; ++ ++ dev->priv = ts; ++ ++ return 0; ++ ++ fail: ++ input_free_device(idev); ++ kfree(ts); ++ return err; ++} ++ ++static void ucb1x00_ts_remove(struct ucb1x00_dev *dev) ++{ ++ struct ucb1x00_ts *ts = dev->priv; ++ ++ input_unregister_device(ts->idev); ++ kfree(ts); ++} ++ ++static struct ucb1x00_driver ucb1x00_ts_driver = { ++ .add = ucb1x00_ts_add, ++ .remove = ucb1x00_ts_remove, ++ .resume = ucb1x00_ts_resume, ++}; ++ ++static int __init ucb1x00_ts_init(void) ++{ ++ return ucb1x00_register_driver(&ucb1x00_ts_driver); ++} ++ ++static void __exit ucb1x00_ts_exit(void) ++{ ++ ucb1x00_unregister_driver(&ucb1x00_ts_driver); ++} ++ ++module_param(adcsync, int, 0444); ++module_init(ucb1x00_ts_init); ++module_exit(ucb1x00_ts_exit); ++ ++MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); ++MODULE_DESCRIPTION("UCB1x00 touchscreen driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index 2572773..bbc137d 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -172,8 +172,5 @@ config MCP_UCB1200 + tristate "Support for UCB1200 / UCB1300" + depends on MCP + +-config MCP_UCB1200_TS +- tristate "Touchscreen interface support" +- depends on MCP_UCB1200 && INPUT + + endmenu +diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile +index 9a5ad8a..4981aff 100644 +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -24,11 +24,10 @@ obj-$(CONFIG_MFD_CORE) += mfd-core.o + obj-$(CONFIG_MCP) += mcp-core.o + obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o + obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o +-obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o + + ifeq ($(CONFIG_SA1100_ASSABET),y) + obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o + endif + obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o + +-obj-$(CONFIG_PMIC_DA903X) += da903x.o +\ No newline at end of file ++obj-$(CONFIG_PMIC_DA903X) += da903x.o +diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c +deleted file mode 100644 +index b5feae9..0000000 +--- a/drivers/mfd/ucb1x00-ts.c ++++ /dev/null +@@ -1,438 +0,0 @@ +-/* +- * Touchscreen driver for UCB1x00-based touchscreens +- * +- * Copyright (C) 2001 Russell King, All Rights Reserved. +- * Copyright (C) 2005 Pavel Machek +- * +- * 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. +- * +- * 21-Jan-2002 <jco@ict.es> : +- * +- * Added support for synchronous A/D mode. This mode is useful to +- * avoid noise induced in the touchpanel by the LCD, provided that +- * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin. +- * It is important to note that the signal connected to the ADCSYNC +- * pin should provide pulses even when the LCD is blanked, otherwise +- * a pen touch needed to unblank the LCD will never be read. +- */ +-#include <linux/module.h> +-#include <linux/moduleparam.h> +-#include <linux/init.h> +-#include <linux/smp.h> +-#include <linux/sched.h> +-#include <linux/completion.h> +-#include <linux/delay.h> +-#include <linux/string.h> +-#include <linux/input.h> +-#include <linux/device.h> +-#include <linux/freezer.h> +-#include <linux/slab.h> +-#include <linux/kthread.h> +-#include <linux/mfd/ucb1x00.h> +- +-#include <asm/dma.h> +-#include <mach/collie.h> +-#include <asm/mach-types.h> +- +- +- +-struct ucb1x00_ts { +- struct input_dev *idev; +- struct ucb1x00 *ucb; +- +- wait_queue_head_t irq_wait; +- struct task_struct *rtask; +- u16 x_res; +- u16 y_res; +- +- unsigned int restart:1; +- unsigned int adcsync:1; +-}; +- +-static int adcsync; +- +-static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) +-{ +- struct input_dev *idev = ts->idev; +- +- input_report_abs(idev, ABS_X, x); +- input_report_abs(idev, ABS_Y, y); +- input_report_abs(idev, ABS_PRESSURE, pressure); +- input_sync(idev); +-} +- +-static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) +-{ +- struct input_dev *idev = ts->idev; +- +- input_report_abs(idev, ABS_PRESSURE, 0); +- input_sync(idev); +-} +- +-/* +- * Switch to interrupt mode. +- */ +-static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts) +-{ +- ucb1x00_reg_write(ts->ucb, UCB_TS_CR, +- UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | +- UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | +- UCB_TS_CR_MODE_INT); +-} +- +-/* +- * Switch to pressure mode, and read pressure. We don't need to wait +- * here, since both plates are being driven. +- */ +-static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts) +-{ +- if (machine_is_collie()) { +- ucb1x00_io_write(ts->ucb, COLLIE_TC35143_GPIO_TBL_CHK, 0); +- ucb1x00_reg_write(ts->ucb, UCB_TS_CR, +- UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_POW | +- UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); +- +- udelay(55); +- +- return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_AD2, ts->adcsync); +- } else { +- ucb1x00_reg_write(ts->ucb, UCB_TS_CR, +- UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | +- UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | +- UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); +- +- return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); +- } +-} +- +-/* +- * Switch to X position mode and measure Y plate. We switch the plate +- * configuration in pressure mode, then switch to position mode. This +- * gives a faster response time. Even so, we need to wait about 55us +- * for things to stabilise. +- */ +-static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts) +-{ +- if (machine_is_collie()) +- ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK); +- else { +- ucb1x00_reg_write(ts->ucb, UCB_TS_CR, +- UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | +- UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); +- ucb1x00_reg_write(ts->ucb, UCB_TS_CR, +- UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | +- UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); +- } +- ucb1x00_reg_write(ts->ucb, UCB_TS_CR, +- UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | +- UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); +- +- udelay(55); +- +- return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); +-} +- +-/* +- * Switch to Y position mode and measure X plate. We switch the plate +- * configuration in pressure mode, then switch to position mode. This +- * gives a faster response time. Even so, we need to wait about 55us +- * for things to stabilise. +- */ +-static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts) +-{ +- if (machine_is_collie()) +- ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK); +- else { +- ucb1x00_reg_write(ts->ucb, UCB_TS_CR, +- UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | +- UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); +- ucb1x00_reg_write(ts->ucb, UCB_TS_CR, +- UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | +- UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); +- } +- +- ucb1x00_reg_write(ts->ucb, UCB_TS_CR, +- UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | +- UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); +- +- udelay(55); +- +- return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); +-} +- +-/* +- * Switch to X plate resistance mode. Set MX to ground, PX to +- * supply. Measure current. +- */ +-static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts) +-{ +- ucb1x00_reg_write(ts->ucb, UCB_TS_CR, +- UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | +- UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); +- return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); +-} +- +-/* +- * Switch to Y plate resistance mode. Set MY to ground, PY to +- * supply. Measure current. +- */ +-static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts) +-{ +- ucb1x00_reg_write(ts->ucb, UCB_TS_CR, +- UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | +- UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); +- return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); +-} +- +-static inline int ucb1x00_ts_pen_down(struct ucb1x00_ts *ts) +-{ +- unsigned int val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR); +- +- if (machine_is_collie()) +- return (!(val & (UCB_TS_CR_TSPX_LOW))); +- else +- return (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)); +-} +- +-/* +- * This is a RT kernel thread that handles the ADC accesses +- * (mainly so we can use semaphores in the UCB1200 core code +- * to serialise accesses to the ADC). +- */ +-static int ucb1x00_thread(void *_ts) +-{ +- struct ucb1x00_ts *ts = _ts; +- DECLARE_WAITQUEUE(wait, current); +- int valid = 0; +- +- set_freezable(); +- add_wait_queue(&ts->irq_wait, &wait); +- while (!kthread_should_stop()) { +- unsigned int x, y, p; +- signed long timeout; +- +- ts->restart = 0; +- +- ucb1x00_adc_enable(ts->ucb); +- +- x = ucb1x00_ts_read_xpos(ts); +- y = ucb1x00_ts_read_ypos(ts); +- p = ucb1x00_ts_read_pressure(ts); +- +- /* +- * Switch back to interrupt mode. +- */ +- ucb1x00_ts_mode_int(ts); +- ucb1x00_adc_disable(ts->ucb); +- +- msleep(10); +- +- ucb1x00_enable(ts->ucb); +- +- +- if (ucb1x00_ts_pen_down(ts)) { +- set_current_state(TASK_INTERRUPTIBLE); +- +- ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING); +- ucb1x00_disable(ts->ucb); +- +- /* +- * If we spat out a valid sample set last time, +- * spit out a "pen off" sample here. +- */ +- if (valid) { +- ucb1x00_ts_event_release(ts); +- valid = 0; +- } +- +- timeout = MAX_SCHEDULE_TIMEOUT; +- } else { +- ucb1x00_disable(ts->ucb); +- +- /* +- * Filtering is policy. Policy belongs in user +- * space. We therefore leave it to user space +- * to do any filtering they please. +- */ +- if (!ts->restart) { +- ucb1x00_ts_evt_add(ts, p, x, y); +- valid = 1; +- } +- +- set_current_state(TASK_INTERRUPTIBLE); +- timeout = HZ / 100; +- } +- +- try_to_freeze(); +- +- schedule_timeout(timeout); +- } +- +- remove_wait_queue(&ts->irq_wait, &wait); +- +- ts->rtask = NULL; +- return 0; +-} +- +-/* +- * We only detect touch screen _touches_ with this interrupt +- * handler, and even then we just schedule our task. +- */ +-static void ucb1x00_ts_irq(int idx, void *id) +-{ +- struct ucb1x00_ts *ts = id; +- +- ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); +- wake_up(&ts->irq_wait); +-} +- +-static int ucb1x00_ts_open(struct input_dev *idev) +-{ +- struct ucb1x00_ts *ts = input_get_drvdata(idev); +- int ret = 0; +- +- BUG_ON(ts->rtask); +- +- init_waitqueue_head(&ts->irq_wait); +- ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts); +- if (ret < 0) +- goto out; +- +- /* +- * If we do this at all, we should allow the user to +- * measure and read the X and Y resistance at any time. +- */ +- ucb1x00_adc_enable(ts->ucb); +- ts->x_res = ucb1x00_ts_read_xres(ts); +- ts->y_res = ucb1x00_ts_read_yres(ts); +- ucb1x00_adc_disable(ts->ucb); +- +- ts->rtask = kthread_run(ucb1x00_thread, ts, "ktsd"); +- if (!IS_ERR(ts->rtask)) { +- ret = 0; +- } else { +- ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); +- ts->rtask = NULL; +- ret = -EFAULT; +- } +- +- out: +- return ret; +-} +- +-/* +- * Release touchscreen resources. Disable IRQs. +- */ +-static void ucb1x00_ts_close(struct input_dev *idev) +-{ +- struct ucb1x00_ts *ts = input_get_drvdata(idev); +- +- if (ts->rtask) +- kthread_stop(ts->rtask); +- +- ucb1x00_enable(ts->ucb); +- ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); +- ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); +- ucb1x00_disable(ts->ucb); +-} +- +-#ifdef CONFIG_PM +-static int ucb1x00_ts_resume(struct ucb1x00_dev *dev) +-{ +- struct ucb1x00_ts *ts = dev->priv; +- +- if (ts->rtask != NULL) { +- /* +- * Restart the TS thread to ensure the +- * TS interrupt mode is set up again +- * after sleep. +- */ +- ts->restart = 1; +- wake_up(&ts->irq_wait); +- } +- return 0; +-} +-#else +-#define ucb1x00_ts_resume NULL +-#endif +- +- +-/* +- * Initialisation. +- */ +-static int ucb1x00_ts_add(struct ucb1x00_dev *dev) +-{ +- struct ucb1x00_ts *ts; +- struct input_dev *idev; +- int err; +- +- ts = kzalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL); +- idev = input_allocate_device(); +- if (!ts || !idev) { +- err = -ENOMEM; +- goto fail; +- } +- +- ts->ucb = dev->ucb; +- ts->idev = idev; +- ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC; +- +- idev->name = "Touchscreen panel"; +- idev->id.product = ts->ucb->id; +- idev->open = ucb1x00_ts_open; +- idev->close = ucb1x00_ts_close; +- +- __set_bit(EV_ABS, idev->evbit); +- __set_bit(ABS_X, idev->absbit); +- __set_bit(ABS_Y, idev->absbit); +- __set_bit(ABS_PRESSURE, idev->absbit); +- +- input_set_drvdata(idev, ts); +- +- err = input_register_device(idev); +- if (err) +- goto fail; +- +- dev->priv = ts; +- +- return 0; +- +- fail: +- input_free_device(idev); +- kfree(ts); +- return err; +-} +- +-static void ucb1x00_ts_remove(struct ucb1x00_dev *dev) +-{ +- struct ucb1x00_ts *ts = dev->priv; +- +- input_unregister_device(ts->idev); +- kfree(ts); +-} +- +-static struct ucb1x00_driver ucb1x00_ts_driver = { +- .add = ucb1x00_ts_add, +- .remove = ucb1x00_ts_remove, +- .resume = ucb1x00_ts_resume, +-}; +- +-static int __init ucb1x00_ts_init(void) +-{ +- return ucb1x00_register_driver(&ucb1x00_ts_driver); +-} +- +-static void __exit ucb1x00_ts_exit(void) +-{ +- ucb1x00_unregister_driver(&ucb1x00_ts_driver); +-} +- +-module_param(adcsync, int, 0444); +-module_init(ucb1x00_ts_init); +-module_exit(ucb1x00_ts_exit); +- +-MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); +-MODULE_DESCRIPTION("UCB1x00 touchscreen driver"); +-MODULE_LICENSE("GPL"); +-- +1.5.6.5 + |