summaryrefslogtreecommitdiff
path: root/packages/linux/logicpd-pxa270-2.6.19.2/ucb1400-touchscreen.patch
diff options
context:
space:
mode:
authorCliff Brake <cbrake@bec-systems.com>2007-01-31 13:41:09 +0000
committerCliff Brake <cbrake@bec-systems.com>2007-01-31 13:41:09 +0000
commita3990a712303e30e9d131f4c146f1cfde44255ed (patch)
treeced2d3edc8c7fe37b3d9f9840dddbff71fcc54e2 /packages/linux/logicpd-pxa270-2.6.19.2/ucb1400-touchscreen.patch
parent7a2c81bf198b4bcd8f344a1ef72734d1d605bba9 (diff)
logicpd-pxa270 2.6.19.2: update kernel, contributed by Shane Volpe
Diffstat (limited to 'packages/linux/logicpd-pxa270-2.6.19.2/ucb1400-touchscreen.patch')
-rw-r--r--packages/linux/logicpd-pxa270-2.6.19.2/ucb1400-touchscreen.patch747
1 files changed, 747 insertions, 0 deletions
diff --git a/packages/linux/logicpd-pxa270-2.6.19.2/ucb1400-touchscreen.patch b/packages/linux/logicpd-pxa270-2.6.19.2/ucb1400-touchscreen.patch
new file mode 100644
index 0000000000..3986a2c10a
--- /dev/null
+++ b/packages/linux/logicpd-pxa270-2.6.19.2/ucb1400-touchscreen.patch
@@ -0,0 +1,747 @@
+This patch is slightly adjusted from
+
+http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=3073/1
+http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=3074/2
+http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=3075/2
+
+in order to get it to apply cleanly to the released 2.6.15 codebase
+and to put the Kconfig stuff in a more reasonable place in the tree.
+Actually, I think Kconfig should probably separate the notion of the
+touchscreen driver and the AC97-MCP layer thing; but that problem is
+basically in the underlying mcp-based ucb1x00 driver layout in the
+first place.
+Index: linux-2.6.15gum/drivers/mfd/Makefile
+===================================================================
+--- linux-2.6.15gum.orig/drivers/mfd/Makefile
++++ linux-2.6.15gum/drivers/mfd/Makefile
+@@ -10,3 +10,6 @@
+ ifeq ($(CONFIG_SA1100_ASSABET),y)
+ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o
+ endif
++
++obj-$(CONFIG_TOUCHSCREEN_UCB1400) += mcp-ac97.o ucb1x00-core.o ucb1x00-ts.o
++
+Index: linux-2.6.15gum/drivers/mfd/mcp-core.c
+===================================================================
+--- linux-2.6.15gum.orig/drivers/mfd/mcp-core.c
++++ linux-2.6.15gum/drivers/mfd/mcp-core.c
+@@ -18,7 +18,6 @@
+ #include <linux/slab.h>
+ #include <linux/string.h>
+
+-#include <asm/dma.h>
+ #include <asm/system.h>
+
+ #include "mcp.h"
+@@ -206,6 +205,7 @@ struct mcp *mcp_host_alloc(struct device
+ mcp->attached_device.bus = &mcp_bus_type;
+ mcp->attached_device.dma_mask = parent->dma_mask;
+ mcp->attached_device.release = mcp_release;
++ mcp->dev = &mcp->attached_device;
+ }
+ return mcp;
+ }
+Index: linux-2.6.15gum/drivers/mfd/mcp-sa11x0.c
+===================================================================
+--- linux-2.6.15gum.orig/drivers/mfd/mcp-sa11x0.c
++++ linux-2.6.15gum/drivers/mfd/mcp-sa11x0.c
+@@ -31,8 +31,12 @@
+ #include "mcp.h"
+
+ struct mcp_sa11x0 {
+- u32 mccr0;
+- u32 mccr1;
++ u32 mccr0;
++ u32 mccr1;
++ dma_device_t dma_audio_rd;
++ dma_device_t dma_audio_wr;
++ dma_device_t dma_telco_rd;
++ dma_device_t dma_telco_wr;
+ };
+
+ #define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp))
+@@ -159,10 +163,10 @@ static int mcp_sa11x0_probe(struct platf
+ mcp->owner = THIS_MODULE;
+ mcp->ops = &mcp_sa11x0;
+ mcp->sclk_rate = data->sclk_rate;
+- mcp->dma_audio_rd = DMA_Ser4MCP0Rd;
+- mcp->dma_audio_wr = DMA_Ser4MCP0Wr;
+- mcp->dma_telco_rd = DMA_Ser4MCP1Rd;
+- mcp->dma_telco_wr = DMA_Ser4MCP1Wr;
++ priv(mcp)->dma_audio_rd = DMA_Ser4MCP0Rd;
++ priv(mcp)->dma_audio_wr = DMA_Ser4MCP0Wr;
++ priv(mcp)->dma_telco_rd = DMA_Ser4MCP1Rd;
++ priv(mcp)->dma_telco_wr = DMA_Ser4MCP1Wr;
+
+ platform_set_drvdata(pdev, mcp);
+
+Index: linux-2.6.15gum/drivers/mfd/mcp.h
+===================================================================
+--- linux-2.6.15gum.orig/drivers/mfd/mcp.h
++++ linux-2.6.15gum/drivers/mfd/mcp.h
+@@ -19,11 +19,8 @@ struct mcp {
+ int use_count;
+ unsigned int sclk_rate;
+ unsigned int rw_timeout;
+- dma_device_t dma_audio_rd;
+- dma_device_t dma_audio_wr;
+- dma_device_t dma_telco_rd;
+- dma_device_t dma_telco_wr;
+ struct device attached_device;
++ struct device *dev;
+ };
+
+ struct mcp_ops {
+Index: linux-2.6.15gum/drivers/mfd/ucb1x00-assabet.c
+===================================================================
+--- linux-2.6.15gum.orig/drivers/mfd/ucb1x00-assabet.c
++++ linux-2.6.15gum/drivers/mfd/ucb1x00-assabet.c
+@@ -15,8 +15,6 @@
+ #include <linux/proc_fs.h>
+ #include <linux/device.h>
+
+-#include <asm/dma.h>
+-
+ #include "ucb1x00.h"
+
+ #define UCB1X00_ATTR(name,input)\
+Index: linux-2.6.15gum/drivers/mfd/ucb1x00-core.c
+===================================================================
+--- a/drivers/mfd/ucb1x00-core~org.c 2006-11-29 16:57:37.000000000 -0500
++++ a/drivers/mfd/ucb1x00-core.c 2006-12-29 13:42:58.000000000 -0500
+@@ -22,6 +22,7 @@
+ #include <linux/init.h>
+ #include <linux/errno.h>
+ #include <linux/interrupt.h>
++#include <linux/kthread.h>
+ #include <linux/device.h>
+ #include <linux/mutex.h>
+
+@@ -30,6 +31,14 @@
+
+ #include "ucb1x00.h"
+
++#if (defined CONFIG_UCB1400) || (defined CONFIG_UCB1400_MODULE)
++#define UCB_IS_1400(id) ((id) == UCB_ID_1400)
++#define UCB_X_CSR1 0xe /* this fake entry will be translated by mcp */
++#define UCB_X_CSR2 0xf /* this fake entry will be translated by mcp */
++#else
++#define UCB_IS_1400(id) (0)
++#endif
++
+ static DEFINE_MUTEX(ucb1x00_mutex);
+ static LIST_HEAD(ucb1x00_drivers);
+ static LIST_HEAD(ucb1x00_devices);
+@@ -57,9 +66,9 @@
+ spin_lock_irqsave(&ucb->io_lock, flags);
+ ucb->io_dir |= out;
+ ucb->io_dir &= ~in;
++ spin_unlock_irqrestore(&ucb->io_lock, flags);
+
+ ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
+- spin_unlock_irqrestore(&ucb->io_lock, flags);
+ }
+
+ /**
+@@ -85,9 +94,9 @@
+ spin_lock_irqsave(&ucb->io_lock, flags);
+ ucb->io_out |= set;
+ ucb->io_out &= ~clear;
++ spin_unlock_irqrestore(&ucb->io_lock, flags);
+
+ ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
+- spin_unlock_irqrestore(&ucb->io_lock, flags);
+ }
+
+ /**
+@@ -177,7 +186,7 @@
+ schedule_timeout(1);
+ }
+
+- return UCB_ADC_DAT(val);
++ return UCB_IS_1400(ucb->id) ? (val & 0x3ff) : ((val & 0x7fe0) >> 5);
+ }
+
+ /**
+@@ -222,6 +231,47 @@
+ return IRQ_HANDLED;
+ }
+
++/*
++ * A restriction with interrupts exists when using the ucb1400, as
++ * the codec read/write routines may sleep while waiting for codec
++ * access completion and uses semaphores for access control to the
++ * AC97 bus. A complete codec read cycle could take anywhere from
++ * 60 to 100uSec so we *definitely* don't want to spin inside the
++ * interrupt handler waiting for codec access. So, we handle the
++ * interrupt by scheduling a RT kernel thread to run in process
++ * context instead of interrupt context.
++ */
++static int ucb1x00_thread(void *_ucb)
++{
++ struct task_struct *tsk = current;
++ struct ucb1x00 *ucb = _ucb;
++
++ tsk->policy = SCHED_FIFO;
++ tsk->rt_priority = 1;
++
++ while (!kthread_should_stop()) {
++ wait_for_completion_interruptible(&ucb->irq_wait);
++ if (try_to_freeze())
++ continue;
++ ucb1x00_irq(ucb->irq, ucb);
++ enable_irq(ucb->irq);
++ }
++
++ ucb->irq_task = NULL;
++ return 0;
++}
++
++static irqreturn_t ucb1x00_threaded_irq(int irqnr, void *devid, struct pt_regs *regs)
++{
++ struct ucb1x00 *ucb = devid;
++ if (irqnr == ucb->irq) {
++ disable_irq(ucb->irq);
++ complete(&ucb->irq_wait);
++ return IRQ_HANDLED;
++ }
++ return IRQ_NONE;
++}
++
+ /**
+ * ucb1x00_hook_irq - hook a UCB1x00 interrupt
+ * @ucb: UCB1x00 structure describing chip
+@@ -276,17 +326,27 @@
+ if (idx < 16) {
+ spin_lock_irqsave(&ucb->lock, flags);
+
+- ucb1x00_enable(ucb);
+- if (edges & UCB_RISING) {
++ if (edges & UCB_RISING)
+ ucb->irq_ris_enbl |= 1 << idx;
+- ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
+- }
+- if (edges & UCB_FALLING) {
++ if (edges & UCB_FALLING)
+ ucb->irq_fal_enbl |= 1 << idx;
+- ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
+- }
+- ucb1x00_disable(ucb);
++
+ spin_unlock_irqrestore(&ucb->lock, flags);
++
++ ucb1x00_enable(ucb);
++ ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
++ ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
++ ucb1x00_disable(ucb);
++
++ /* This prevents spurious interrupts on the UCB1400 */
++ ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 1 << idx);
++ ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
++
++ ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
++ ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
++
++ ucb1x00_disable(ucb);
++
+ }
+ }
+
+@@ -306,16 +366,20 @@
+ spin_lock_irqsave(&ucb->lock, flags);
+
+ ucb1x00_enable(ucb);
+- if (edges & UCB_RISING) {
++ if (edges & UCB_RISING)
+ ucb->irq_ris_enbl &= ~(1 << idx);
+- ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
+- }
+- if (edges & UCB_FALLING) {
++
++ if (edges & UCB_FALLING)
+ ucb->irq_fal_enbl &= ~(1 << idx);
+- ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
+- }
+- ucb1x00_disable(ucb);
++
+ spin_unlock_irqrestore(&ucb->lock, flags);
++
++ ucb1x00_enable(ucb);
++ ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
++ ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
++ ucb1x00_disable(ucb);
++
++
+ }
+ }
+
+@@ -348,16 +412,17 @@
+ ucb->irq_ris_enbl &= ~(1 << idx);
+ ucb->irq_fal_enbl &= ~(1 << idx);
+
+- ucb1x00_enable(ucb);
+- ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
+- ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
+- ucb1x00_disable(ucb);
+-
+ irq->fn = NULL;
+ irq->devid = NULL;
+ ret = 0;
+ }
+ spin_unlock_irq(&ucb->lock);
++
++ ucb1x00_enable(ucb);
++ ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
++ ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
++ ucb1x00_disable(ucb);
++
+ return ret;
+
+ bad:
+@@ -479,7 +544,7 @@
+ mcp_enable(mcp);
+ id = mcp_reg_read(mcp, UCB_ID);
+
+- if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_TC35143) {
++ if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_TC35143 && !UCB_IS_1400(id)) {
+ printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id);
+ goto err_disable;
+ }
+@@ -492,29 +557,41 @@
+ memset(ucb, 0, sizeof(struct ucb1x00));
+
+ ucb->cdev.class = &ucb1x00_class;
+- ucb->cdev.dev = &mcp->attached_device;
++ ucb->cdev.dev = mcp->dev;
+ strlcpy(ucb->cdev.class_id, "ucb1x00", sizeof(ucb->cdev.class_id));
+
+ spin_lock_init(&ucb->lock);
+ spin_lock_init(&ucb->io_lock);
+ sema_init(&ucb->adc_sem, 1);
++ init_completion(&ucb->irq_wait);
+
+ ucb->id = id;
+ ucb->mcp = mcp;
+ ucb->irq = ucb1x00_detect_irq(ucb);
+ if (ucb->irq == NO_IRQ) {
+- printk(KERN_ERR "UCB1x00: IRQ probe failed\n");
+- ret = -ENODEV;
+- goto err_free;
++ printk(KERN_ERR "UCB1x00: IRQ probe auto-detect failed.. hardcoding IRQ..\n");
++ ucb->irq = 164;
++ //ret = -ENODEV;
++ //goto err_free;
+ }
+
+- ret = request_irq(ucb->irq, ucb1x00_irq, IRQF_TRIGGER_RISING,
+- "UCB1x00", ucb);
++ ret = request_irq(ucb->irq,
++ UCB_IS_1400(id) ? ucb1x00_threaded_irq : ucb1x00_irq,
++ 0, "UCB1x00", ucb);
++
+ if (ret) {
+ printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n",
+ ucb->irq, ret);
+ goto err_free;
+ }
++ if (UCB_IS_1400(id)) {
++ ucb->irq_task = kthread_run(ucb1x00_thread, ucb, "kUCB1x00d");
++ if (IS_ERR(ucb->irq_task)) {
++ ret = PTR_ERR(ucb->irq_task);
++ ucb->irq_task = NULL;
++ goto err_irq;
++ }
++ }
+
+ mcp_set_drvdata(mcp, ucb);
+
+@@ -532,6 +609,8 @@
+ goto out;
+
+ err_irq:
++ if (UCB_IS_1400(id) && ucb->irq_task)
++ kthread_stop(ucb->irq_task);
+ free_irq(ucb->irq, ucb);
+ err_free:
+ kfree(ucb);
+@@ -554,6 +633,8 @@
+ }
+ mutex_unlock(&ucb1x00_mutex);
+
++ if (UCB_IS_1400(ucb->id) && ucb->irq_task)
++ kthread_stop(ucb->irq_task);
+ free_irq(ucb->irq, ucb);
+ class_device_unregister(&ucb->cdev);
+ }
+
+Index: linux-2.6.15gum/drivers/mfd/ucb1x00-ts.c
+===================================================================
+--- linux-2.6.15gum.orig/drivers/mfd/ucb1x00-ts.c
++++ linux-2.6.15gum/drivers/mfd/ucb1x00-ts.c
+@@ -33,10 +33,8 @@
+ #include <linux/slab.h>
+ #include <linux/kthread.h>
+
+-#include <asm/dma.h>
+-#include <asm/semaphore.h>
+-#include <asm/arch/collie.h>
+ #include <asm/mach-types.h>
++#include <asm/arch-sa1100/collie.h>
+
+ #include "ucb1x00.h"
+
+@@ -45,13 +43,14 @@
+ struct input_dev *idev;
+ struct ucb1x00 *ucb;
+
+- wait_queue_head_t irq_wait;
++ struct completion irq_wait;
+ struct task_struct *rtask;
+ u16 x_res;
+ u16 y_res;
+
+ unsigned int restart:1;
+ unsigned int adcsync:1;
++ unsigned int go_thread;
+ };
+
+ static int adcsync;
+@@ -205,7 +204,6 @@
+ {
+ struct ucb1x00_ts *ts = _ts;
+ struct task_struct *tsk = current;
+- DECLARE_WAITQUEUE(wait, tsk);
+ int valid;
+
+ /*
+@@ -217,10 +215,8 @@
+
+ valid = 0;
+
+- add_wait_queue(&ts->irq_wait, &wait);
+- while (!kthread_should_stop()) {
++ while (!kthread_should_stop() && ts->go_thread) {
+ unsigned int x, y, p;
+- signed long timeout;
+
+ ts->restart = 0;
+
+@@ -242,8 +238,6 @@
+
+
+ if (ucb1x00_ts_pen_down(ts)) {
+- set_task_state(tsk, TASK_INTERRUPTIBLE);
+-
+ ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING);
+ ucb1x00_disable(ts->ucb);
+
+@@ -256,7 +250,16 @@
+ valid = 0;
+ }
+
+- timeout = MAX_SCHEDULE_TIMEOUT;
++ /*
++ * Since ucb1x00_enable_irq() might sleep due
++ * to the way the UCB1400 regs are accessed, we
++ * can't use set_task_state() before that call,
++ * and not changing state before enabling the
++ * interrupt is racy. A completion handler avoids
++ * the issue.
++ */
++ wait_for_completion_interruptible(&ts->irq_wait);
++
+ } else {
+ ucb1x00_disable(ts->ucb);
+
+@@ -271,16 +274,12 @@
+ }
+
+ set_task_state(tsk, TASK_INTERRUPTIBLE);
+- timeout = HZ / 100;
++ schedule_timeout(HZ/100);
+ }
+
+ try_to_freeze();
+-
+- schedule_timeout(timeout);
+ }
+
+- remove_wait_queue(&ts->irq_wait, &wait);
+-
+ ts->rtask = NULL;
+ return 0;
+ }
+@@ -293,7 +292,7 @@
+ {
+ struct ucb1x00_ts *ts = id;
+ ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
+- wake_up(&ts->irq_wait);
++ complete(&ts->irq_wait);
+ }
+
+ static int ucb1x00_ts_open(struct input_dev *idev)
+@@ -303,7 +302,7 @@
+
+ BUG_ON(ts->rtask);
+
+- init_waitqueue_head(&ts->irq_wait);
++ init_completion(&ts->irq_wait);
+ ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts);
+ if (ret < 0)
+ goto out;
+@@ -318,6 +317,8 @@
+ ucb1x00_adc_disable(ts->ucb);
+
+ ts->rtask = kthread_run(ucb1x00_thread, ts, "ktsd");
++ ts->go_thread = 1; //start thread!
++
+ if (!IS_ERR(ts->rtask)) {
+ ret = 0;
+ } else {
+@@ -337,9 +338,14 @@
+ {
+ struct ucb1x00_ts *ts = idev->private;
+
++
++ ts->go_thread = 0; //Stop thread!
++ complete(&ts->irq_wait);
++
+ 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);
+@@ -358,7 +364,7 @@
+ * after sleep.
+ */
+ ts->restart = 1;
+- wake_up(&ts->irq_wait);
++ complete(&ts->irq_wait);
+ }
+ return 0;
+ }
+
+Index: linux-2.6.15gum/drivers/mfd/ucb1x00.h
+===================================================================
+--- a/drivers/mfd/ucb1x00.h~org 2006-12-29 13:43:38.000000000 -0500
++++ a/drivers/mfd/ucb1x00.h 2006-12-29 13:44:48.000000000 -0500
+@@ -94,6 +94,7 @@
+ #define UCB_ID 0x0c
+ #define UCB_ID_1200 0x1004
+ #define UCB_ID_1300 0x1005
++#define UCB_ID_1400 0x4304
+ #define UCB_ID_TC35143 0x9712
+
+ #define UCB_MODE 0x0d
+@@ -111,6 +112,8 @@
+ spinlock_t lock;
+ struct mcp *mcp;
+ unsigned int irq;
++ struct task_struct *irq_task;
++ struct completion irq_wait;
+ struct semaphore adc_sem;
+ spinlock_t io_lock;
+ u16 id;
+Index: linux-2.6.15gum/drivers/mfd/mcp-ac97.c
+===================================================================
+--- /dev/null
++++ linux-2.6.15gum/drivers/mfd/mcp-ac97.c
+@@ -0,0 +1,153 @@
++/*
++ * linux/drivers/misc/mcp-ac97.c
++ *
++ * Author: Nicolas Pitre
++ * Created: Jan 14, 2005
++ * Copyright: (C) MontaVista Software Inc.
++ *
++ * 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.
++ *
++ * This module provides the minimum replacement for mcp-core.c allowing for
++ * the UCB1400 chip to be driven by the ucb1x00 driver over an AC97 link.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/device.h>
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/ac97_codec.h>
++
++#include "mcp.h"
++
++/* ucb1x00 SIB register to ucb1400 AC-link register mapping */
++
++static const unsigned char regmap[] = {
++ 0x5a, /* UCB_IO_DATA */
++ 0X5C, /* UCB_IO_DIR */
++ 0X5E, /* UCB_IE_RIS */
++ 0x60, /* UCB_IE_FAL */
++ 0x62, /* UCB_IE_STATUS */
++ 0, /* UCB_TC_A */
++ 0, /* UCB_TC_B */
++ 0, /* UCB_AC_A */
++ 0, /* UCB_AC_B */
++ 0x64, /* UCB_TS_CR */
++ 0x66, /* UCB_ADC_CR */
++ 0x68, /* UCB_ADC_DATA */
++ 0x7e, /* UCB_ID */
++ 0, /* UCB_MODE */
++};
++
++unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg)
++{
++ ac97_t *ac97 = to_ac97_t(mcp->dev);
++ if (reg < ARRAY_SIZE(regmap)) {
++ reg = regmap[reg];
++ if (reg)
++ return ac97->bus->ops->read(ac97, reg);
++ }
++ return -1;
++}
++EXPORT_SYMBOL(mcp_reg_read);
++
++void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val)
++{
++ ac97_t *ac97 = to_ac97_t(mcp->dev);
++ if (reg < ARRAY_SIZE(regmap)) {
++ reg = regmap[reg];
++ if (reg)
++ ac97->bus->ops->write(ac97, reg, val);
++ }
++}
++EXPORT_SYMBOL(mcp_reg_write);
++
++void mcp_enable(struct mcp *mcp)
++{
++}
++EXPORT_SYMBOL(mcp_enable);
++
++void mcp_disable(struct mcp *mcp)
++{
++}
++EXPORT_SYMBOL(mcp_disable);
++
++#define to_mcp_driver(d) container_of(d, struct mcp_driver, drv)
++
++static int mcp_probe(struct device *dev)
++{
++ struct mcp_driver *drv = to_mcp_driver(dev->driver);
++ struct mcp *mcp;
++ int ret;
++
++ ret = -ENOMEM;
++ mcp = kmalloc(sizeof(*mcp), GFP_KERNEL);
++ if (mcp) {
++ memset(mcp, 0, sizeof(*mcp));
++ mcp->owner = THIS_MODULE;
++ mcp->dev = dev;
++ ret = drv->probe(mcp);
++ if (ret)
++ kfree(mcp);
++ }
++ if (!ret)
++ dev_set_drvdata(dev, mcp);
++ return ret;
++}
++
++static int mcp_remove(struct device *dev)
++{
++ struct mcp_driver *drv = to_mcp_driver(dev->driver);
++ struct mcp *mcp = dev_get_drvdata(dev);
++
++ drv->remove(mcp);
++ dev_set_drvdata(dev, NULL);
++ kfree(mcp);
++ return 0;
++}
++
++static int mcp_suspend(struct device *dev, pm_message_t state)
++{
++ struct mcp_driver *drv = to_mcp_driver(dev->driver);
++ struct mcp *mcp = dev_get_drvdata(dev);
++ int ret = 0;
++
++ if (drv->suspend)
++ ret = drv->suspend(mcp, state);
++ return ret;
++}
++
++static int mcp_resume(struct device *dev)
++{
++ struct mcp_driver *drv = to_mcp_driver(dev->driver);
++ struct mcp *mcp = dev_get_drvdata(dev);
++ int ret = 0;
++
++ if (drv->resume)
++ ret = drv->resume(mcp);
++ return ret;
++}
++
++int mcp_driver_register(struct mcp_driver *mcpdrv)
++{
++ mcpdrv->drv.owner = THIS_MODULE;
++ mcpdrv->drv.bus = &ac97_bus_type;
++ mcpdrv->drv.probe = mcp_probe;
++ mcpdrv->drv.remove = mcp_remove;
++ mcpdrv->drv.suspend = mcp_suspend;
++ mcpdrv->drv.resume = mcp_resume;
++ return driver_register(&mcpdrv->drv);
++}
++EXPORT_SYMBOL(mcp_driver_register);
++
++void mcp_driver_unregister(struct mcp_driver *mcpdrv)
++{
++ driver_unregister(&mcpdrv->drv);
++}
++EXPORT_SYMBOL(mcp_driver_unregister);
++
++MODULE_LICENSE("GPL");
+Index: linux-2.6.15gum/drivers/input/touchscreen/Kconfig
+===================================================================
+--- linux-2.6.15gum.orig/drivers/input/touchscreen/Kconfig
++++ linux-2.6.15gum/drivers/input/touchscreen/Kconfig
+@@ -11,6 +11,25 @@ menuconfig INPUT_TOUCHSCREEN
+
+ if INPUT_TOUCHSCREEN
+
++config UCB1400
++ bool
++
++config TOUCHSCREEN_UCB1400
++ tristate "UCB1400 Touchscreen support"
++ depends on ARCH_LUBBOCK || MACH_MAINSTONE || ARCH_PXA
++ select SND_AC97_BUS
++ select UCB1400
++ help
++ Say Y here if you have a touchscreen connected to a UCB1400 ADC chip
++ on the AC97 bus of a PXA255/PXA270 host.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called ucb1x00-ts. It will also build the modules
++ ucb1x00-core and mcp-ac97 which provide the compatibility layers
++ down to the AC97 bus.
++
+ config TOUCHSCREEN_BITSY
+ tristate "Compaq iPAQ H3600 (Bitsy) touchscreen"
+ depends on SA1100_BITSY
+Index: linux-2.6.15gum/drivers/input/Kconfig
+===================================================================
+--- linux-2.6.15gum.orig/drivers/input/Kconfig
++++ linux-2.6.15gum/drivers/input/Kconfig
+@@ -87,7 +87,7 @@ config INPUT_JOYDEV
+ module will be called joydev.
+
+ config INPUT_TSDEV
+- tristate "Touchscreen interface"
++ tristate "Compaq touchscreen interface"
+ ---help---
+ Say Y here if you have an application that only can understand the
+ Compaq touchscreen protocol for absolute pointer data. This is