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.17-rc5/drivers/mfd/Makefile
===================================================================
--- linux-2.6.17-rc5.orig/drivers/mfd/Makefile
+++ linux-2.6.17-rc5/drivers/mfd/Makefile
@@ -10,3 +10,6 @@ obj-$(CONFIG_MCP_UCB1200_TS)	+= ucb1x00-
 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.17-rc5/drivers/mfd/mcp-core.c
===================================================================
--- linux-2.6.17-rc5.orig/drivers/mfd/mcp-core.c
+++ linux-2.6.17-rc5/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"
@@ -208,6 +207,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.17-rc5/drivers/mfd/mcp-sa11x0.c
===================================================================
--- linux-2.6.17-rc5.orig/drivers/mfd/mcp-sa11x0.c
+++ linux-2.6.17-rc5/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.17-rc5/drivers/mfd/mcp.h
===================================================================
--- linux-2.6.17-rc5.orig/drivers/mfd/mcp.h
+++ linux-2.6.17-rc5/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.17-rc5/drivers/mfd/ucb1x00-assabet.c
===================================================================
--- linux-2.6.17-rc5.orig/drivers/mfd/ucb1x00-assabet.c
+++ linux-2.6.17-rc5/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.17-rc5/drivers/mfd/ucb1x00-core.c
===================================================================
--- linux-2.6.17-rc5.orig/drivers/mfd/ucb1x00-core.c
+++ linux-2.6.17-rc5/drivers/mfd/ucb1x00-core.c
@@ -23,6 +23,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>
 
@@ -31,6 +32,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);
@@ -58,9 +67,9 @@ void ucb1x00_io_set_dir(struct ucb1x00 *
 	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);
 }
 
 /**
@@ -86,9 +95,9 @@ void ucb1x00_io_write(struct ucb1x00 *uc
 	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);
 }
 
 /**
@@ -178,7 +187,7 @@ unsigned int ucb1x00_adc_read(struct ucb
 		schedule_timeout(1);
 	}
 
-	return UCB_ADC_DAT(val);
+	return UCB_IS_1400(ucb->id) ? (val & 0x3ff) : ((val & 0x7fe0) >> 5);
 }
 
 /**
@@ -223,6 +232,47 @@ static irqreturn_t ucb1x00_irq(int irqnr
 	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, NULL);
+		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,18 +326,22 @@ void ucb1x00_enable_irq(struct ucb1x00 *
 
 	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);
+
+		/* 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);
 	}
 }
 
@@ -305,18 +359,16 @@ void ucb1x00_disable_irq(struct ucb1x00 
 
 	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);
 	}
 }
 
@@ -349,16 +401,17 @@ int ucb1x00_free_irq(struct ucb1x00 *ucb
 		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:
@@ -478,7 +531,7 @@ static int ucb1x00_probe(struct mcp *mcp
 	mcp_enable(mcp);
 	id = mcp_reg_read(mcp, UCB_ID);
 
-	if (id != UCB_ID_1200 && id != UCB_ID_1300) {
+	if (id != UCB_ID_1200 && id != UCB_ID_1300 && !UCB_IS_1400(id)) {
 		printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id);
 		goto err_disable;
 	}
@@ -491,12 +544,13 @@ static int ucb1x00_probe(struct mcp *mcp
 	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;
@@ -507,13 +561,22 @@ static int ucb1x00_probe(struct mcp *mcp
 		goto err_free;
 	}
 
-	ret = request_irq(ucb->irq, ucb1x00_irq, SA_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);
 
@@ -531,6 +594,8 @@ static int ucb1x00_probe(struct mcp *mcp
 	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);
@@ -553,6 +618,8 @@ static void ucb1x00_remove(struct mcp *m
 	}
 	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.17-rc5/drivers/mfd/ucb1x00-ts.c
===================================================================
--- linux-2.6.17-rc5.orig/drivers/mfd/ucb1x00-ts.c
+++ linux-2.6.17-rc5/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 ucb1x00_ts {
 	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 @@ static int ucb1x00_thread(void *_ts)
 {
 	struct ucb1x00_ts *ts = _ts;
 	struct task_struct *tsk = current;
-	DECLARE_WAITQUEUE(wait, tsk);
 	int valid;
 
 	/*
@@ -217,10 +215,8 @@ static int ucb1x00_thread(void *_ts)
 
 	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 @@ static int ucb1x00_thread(void *_ts)
 
 
 		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 @@ static int ucb1x00_thread(void *_ts)
 				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 @@ static int ucb1x00_thread(void *_ts)
 			}
 
 			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 @@ static void ucb1x00_ts_irq(int idx, void
 {
 	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 @@ static int ucb1x00_ts_open(struct input_
 
 	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 @@ static int ucb1x00_ts_open(struct input_
 	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 @@ static void ucb1x00_ts_close(struct inpu
 {
 	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 @@ static int ucb1x00_ts_resume(struct ucb1
 		 * after sleep.
 		 */
 		ts->restart = 1;
-		wake_up(&ts->irq_wait);
+		complete(&ts->irq_wait);
 	}
 	return 0;
 }
Index: linux-2.6.17-rc5/drivers/mfd/ucb1x00.h
===================================================================
--- linux-2.6.17-rc5.orig/drivers/mfd/ucb1x00.h
+++ linux-2.6.17-rc5/drivers/mfd/ucb1x00.h
@@ -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_MODE	0x0d
 #define UCB_MODE_DYN_VFLAG_ENA	(1 << 12)
@@ -110,6 +111,8 @@ struct ucb1x00 {
 	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;
@@ -122,6 +125,7 @@ struct ucb1x00 {
 	struct class_device	cdev;
 	struct list_head	node;
 	struct list_head	devs;
+
 };
 
 struct ucb1x00_driver;
Index: linux-2.6.17-rc5/drivers/mfd/mcp-ac97.c
===================================================================
--- /dev/null
+++ linux-2.6.17-rc5/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.17-rc5/drivers/input/touchscreen/Kconfig
===================================================================
--- linux-2.6.17-rc5.orig/drivers/input/touchscreen/Kconfig
+++ linux-2.6.17-rc5/drivers/input/touchscreen/Kconfig
@@ -24,6 +24,25 @@ config TOUCHSCREEN_ADS7846
 	  To compile this driver as a module, choose M here: the
 	  module will be called ads7846.
 
+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.17-rc5/drivers/input/Kconfig
===================================================================
--- linux-2.6.17-rc5.orig/drivers/input/Kconfig
+++ linux-2.6.17-rc5/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