aboutsummaryrefslogtreecommitdiffstats
path: root/recipes/linux/linux-2.6.28/collie/0012-move-ucb1200-ts-driver.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes/linux/linux-2.6.28/collie/0012-move-ucb1200-ts-driver.patch')
-rw-r--r--recipes/linux/linux-2.6.28/collie/0012-move-ucb1200-ts-driver.patch981
1 files changed, 981 insertions, 0 deletions
diff --git a/recipes/linux/linux-2.6.28/collie/0012-move-ucb1200-ts-driver.patch b/recipes/linux/linux-2.6.28/collie/0012-move-ucb1200-ts-driver.patch
new file mode 100644
index 0000000000..c1aafef5e2
--- /dev/null
+++ b/recipes/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
+