Index: include/linux/ac97_codec.h
===================================================================
--- include/linux/ac97_codec.h	(.../vendor/arm/linux)	(revision 33)
+++ linux/include/linux/ac97_codec.h	(.../branches/development/linux)	(revision 33)
@@ -214,6 +214,9 @@
                                     (CODEC)->supported_mixers & (1<<FOO) )
 
 struct ac97_codec {
+	/* Linked list of codecs */
+	struct list_head list;
+		
 	/* AC97 controller connected with */
 	void *private_data;
 
@@ -221,24 +224,37 @@
 	int id;
 	int dev_mixer; 
 	int type;
+	u32 model;
+	int modem:1;
 #ifdef CONFIG_PM
 	struct pm_dev       *pmdev;
 #endif
 
 	struct ac97_ops *codec_ops;
 
-	/* controller specific lower leverl ac97 accessing routines */
+	/* controller specific lower level ac97 accessing routines */
 	u16  (*codec_read)  (struct ac97_codec *codec, u8 reg);
 	void (*codec_write) (struct ac97_codec *codec, u8 reg, u16 val);
 
 	/* Wait for codec-ready.  Ok to sleep here.  */
 	void  (*codec_wait)  (struct ac97_codec *codec);
+		
+		/* callback used by helper drivers for interesting ac97 setups */
+	void  (*codec_unregister) (struct ac97_codec *codec);
+	
+	struct ac97_driver *driver;
+	void *driver_private;	/* Private data for the driver */
+	
+	spinlock_t lock;
 
 	/* OSS mixer masks */
 	int modcnt;
 	int supported_mixers;
 	int stereo_mixers;
 	int record_sources;
+	
+	/* Property flags */
+	int flags;
 
 	int bit_resolution;
 
@@ -267,7 +283,15 @@
 	/* Amplifier control */
 	int (*amplifier)(struct ac97_codec *codec, int on);
 	/* Digital mode control */
-	int (*digital)(struct ac97_codec *codec, int format);
+	int (*digital)(struct ac97_codec *codec, int slots, int rate, int mode);
+#define AUDIO_DIGITAL		0x8000
+#define AUDIO_PRO		0x4000
+#define AUDIO_DRS		0x2000
+#define AUDIO_CCMASK		0x003F
+	
+#define AC97_DELUDED_MODEM	1	/* Audio codec reports its a modem */
+#define AC97_NO_PCM_VOLUME	2	/* Volume control is missing 	   */
+#define AC97_DEFAULT_POWER_OFF 4 /* Needs warm reset to power up */
 };
 
 extern int ac97_read_proc (char *page_out, char **start, off_t off,
@@ -276,6 +300,21 @@
 extern unsigned int ac97_set_adc_rate(struct ac97_codec *codec, unsigned int rate);
 extern unsigned int ac97_set_dac_rate(struct ac97_codec *codec, unsigned int rate);
 extern int ac97_save_state(struct ac97_codec *codec);
-extern int ac97_restore_state(struct ac97_codec *codec);
+extern int ac97_restore_state(struct ac97_codec *codec);	
+extern struct ac97_codec *ac97_alloc_codec(void);
+extern void ac97_release_codec(struct ac97_codec *codec);
 
+struct ac97_driver {
+	struct list_head list;
+	char *name;
+	u32 codec_id;
+	u32 codec_mask;
+	int (*probe) (struct ac97_codec *codec, struct ac97_driver *driver);
+	void (*remove) (struct ac97_codec *codec, struct ac97_driver *driver);
+};
+
+extern int ac97_register_driver(struct ac97_driver *driver);
+extern void ac97_unregister_driver(struct ac97_driver *driver);
+
+
 #endif /* _AC97_CODEC_H_ */
Index: drivers/sound/ac97_plugin_wm97xx.c
===================================================================
--- drivers/sound/ac97_plugin_wm97xx.c	(revision 0)
+++ linux/drivers/sound/ac97_plugin_wm97xx.c	(revision 33)
@@ -0,0 +1,1174 @@
+/*
+ * ac97_plugin_wm97xx.c  --  Touch screen driver for Wolfson WM9705 and WM9712
+ *                           AC97 Codecs.
+ *
+ * Copyright 2003 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *         parts (c) Ian Molton <spyro@f2s.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
+ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+ *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+ *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the  GNU General Public License along
+ *  with this program; if not, write  to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Notes:
+ *
+ *  Features:
+ *       - supports WM9705, WM9712
+ *       - polling mode
+ *       - coordinate polling
+ *       - continuous mode
+ *       - adjustable rpu/dpp settings
+ *       - adjustable pressure current
+ *       - adjustable sample settle delay
+ *       - 4 and 5 wire touchscreens (5 wire is WM9712 only)
+ *       - pen down detection
+ *       - power management
+ *
+ *  TODO:
+ *       - adjustable sample rate
+ *       - AUX adc in coordinate / continous modes
+ *       - Codec GPIO
+ *       - battery monitor
+ *       - sample AUX adc's
+ *
+ *  Revision history
+ *    7th May 2003   Initial version.
+ *    6th June 2003  Added non module support and AC97 registration.
+ *   18th June 2003  Added AUX adc sampling. 
+ *   23rd June 2003  Did some minimal reformatting, fixed a couple of
+ *     locking bugs and noted a race to fix.
+ *   24th June 2003  Added power management and fixed race condition.
+ *   10th July 2003  Changed to a misc device.
+ *   31st July 2003  Moved TS_EVENT and TS_CAL to wm97xx.h
+ *    8th Aug  2003  Added option for read() calling wm97xx_sample_touch()
+ *                   because some ac97_read/ac_97_write call schedule()
+ *    7th Nov  2003  Added Input touch event interface, stanley.cai@intel.com
+ *   13th Nov  2003  Removed h3600 touch interface, added interrupt based
+ *                   pen down notification and implemented continous mode
+ *                   on XScale arch.
+ *    4th Dec  2003  Removed ADC src bits from sample data.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/pm.h>
+#include <asm/uaccess.h>        /* get_user,copy_to_user */
+#include <asm/io.h>
+
+#include "wm97xx.h"
+
+#define TS_NAME "wm97xx"
+#define TS_MINOR 16
+#define WM_TS_VERSION "0.10"
+#define AC97_NUM_REG 64
+
+/*
+ * Machine specific set up.
+ * 
+ * This is for targets that can support a PEN down interrupt and/or 
+ * streaming back touch data in an AC97 slot (not slot 1). The 
+ * streaming touch data is read back via the targets AC97 FIFO's 
+ */
+#if defined(CONFIG_ARCH_WMMX)
+#include <asm/arch/wmmx-regs.h>
+#include <asm/arch/irqs.h>
+#define WM97XX_IRQ 	IRQ_AC97
+#define WM97XX_FIFO_HAS_DATA MISR & (1 << 2)
+#define WM97XX_READ_FIFO MODR & (0xffff)
+#endif
+
+#ifndef WM97XX_IRQ
+#define WM97XX_IRQ 0
+#define WM97XX_FIFO_HAS_DATA 0
+#define WM97XX_READ_FIFO 0
+#endif
+
+/*
+ * Debug
+ */
+#define PFX TS_NAME
+#define WM97XX_TS_DEBUG 0
+
+#ifdef WM97XX_TS_DEBUG
+#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg)
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set the codec sample mode.
+ *
+ * The WM9712 can sample touchscreen data in 3 different operating
+ * modes. i.e. polling, coordinate and continous.
+ *
+ * Polling:-     The driver polls the codec and issues 3 seperate commands
+ *               over the AC97 link to read X,Y and pressure.
+ * 
+ * Coordinate: - The driver polls the codec and only issues 1 command over
+ *               the AC97 link to read X,Y and pressure. This mode has
+ *               strict timing requirements and may drop samples if 
+ *               interrupted. However, it is less demanding on the AC97
+ *               link. Note: this mode requires a larger delay than polling
+ *               mode.
+ *
+ * Continuous:-  The codec automatically samples X,Y and pressure and then
+ *               sends the data over the AC97 link in slots. This is the
+ *               same method used by the codec when recording audio.
+ *
+ * Set mode = 0 for polling, 1 for coordinate and 2 for continuous.
+ *            
+ */
+MODULE_PARM(mode,"i");
+MODULE_PARM_DESC(mode, "Set WM97XX operation mode");
+static int mode = 0;
+
+/*
+ * WM9712 - Set internal pull up for pen detect. 
+ * 
+ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive)
+ * i.e. pull up resistance = 64k Ohms / rpu.
+ * 
+ * Adjust this value if you are having problems with pen detect not 
+ * detecting any down events.
+ */
+MODULE_PARM(rpu,"i");
+MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
+static int rpu = 0;
+
+/*
+ * WM9705 - Pen detect comparator threshold. 
+ * 
+ * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold
+ * i.e. 1 =  Vmid/15 threshold
+ *      15 =  Vmid/1 threshold
+ * 
+ * Adjust this value if you are having problems with pen detect not 
+ * detecting any down events.
+ */
+MODULE_PARM(pdd,"i");
+MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold");
+static int pdd = 0;
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA 
+ *     pil = 1 to use 200uA and
+ *     pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure. 
+ */
+MODULE_PARM(pil,"i");
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+static int pil = 0;
+
+/*
+ * WM9712 - Set five_wire = 1 to use a 5 wire touchscreen.
+ * 
+ * NOTE: Five wire mode does not allow for readback of pressure.
+ */
+MODULE_PARM(five_wire,"i");
+MODULE_PARM_DESC(five_wire, "Set 5 wire touchscreen.");
+static int five_wire = 0;
+
+/*
+ * Set adc sample delay.
+ * 
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+MODULE_PARM(delay,"i");
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+static int delay = 4;
+
+typedef struct {
+	int is_wm9712:1;                  /* are we a WM9705/12 */ 
+	int is_registered:1;              /* Is the driver AC97 registered */
+	int adc_errs;                     /* sample read back errors */
+	spinlock_t lock;
+	struct ac97_codec *codec;
+#if defined(CONFIG_PROC_FS)	
+	struct proc_dir_entry *wm97xx_ts_ps;
+#endif
+#if defined(CONFIG_PM)
+	struct pm_dev * pm;
+	int line_pgal:5;
+	int line_pgar:5;
+	int phone_pga:5;
+	int mic_pgal:5;
+	int mic_pgar:5;
+#endif
+	struct input_dev *idev;
+	struct completion thread_init;
+	struct completion thread_exit;
+	struct task_struct *rtask;
+	struct semaphore  sem;
+	int use_count;
+	int restart;
+} wm97xx_ts_t;
+
+static inline void poll_delay (void);
+static int __init wm97xx_ts_init_module(void);
+static inline int pendown (wm97xx_ts_t *ts);
+static int wm97xx_poll_read_adc (wm97xx_ts_t* ts, u16 adcsel, u16* sample);
+static void init_wm97xx_phy(void);
+static int wm97xx_probe(struct ac97_codec *codec, struct ac97_driver *driver);
+static void wm97xx_remove(struct ac97_codec *codec,  struct ac97_driver *driver);
+static void wm97xx_ts_cleanup_module(void);
+static int wm97xx_ts_evt_add(wm97xx_ts_t* ts, u16 pressure, u16 x, u16 y);
+static int wm97xx_ts_evt_release(wm97xx_ts_t* ts);
+
+#if defined(CONFIG_PM)
+static int wm97xx_pm_event(struct pm_dev *dev, pm_request_t rqst, void *data);
+static void wm97xx_suspend(void);
+static void wm97xx_resume(void);
+static void wm9712_pga_save(wm97xx_ts_t* ts);
+static void wm9712_pga_restore(wm97xx_ts_t* ts);
+#endif
+
+/* we only support a single touchscreen */
+static wm97xx_ts_t wm97xx_ts;
+static struct input_dev wm97xx_input;
+	
+/* AC97 registration info */
+static struct ac97_driver wm9705_driver = {
+	codec_id: 0x574D4C05,
+	codec_mask: 0xFFFFFFFF,
+	name: "Wolfson WM9705 Touchscreen",
+	probe:	wm97xx_probe,
+	remove: __devexit_p(wm97xx_remove),
+};
+
+static struct ac97_driver wm9712_driver = {
+	codec_id: 0x574D4C12,
+	codec_mask: 0xFFFFFFFF,
+	name: "Wolfson WM9712 Touchscreen",
+	probe:	wm97xx_probe,
+	remove: __devexit_p(wm97xx_remove),
+};
+
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[16] = {
+	21,// 1 AC97 Link frames
+	42,// 2
+	84,// 4
+	167,// 8
+	333,// 16
+	667,// 32
+	1000,// 48
+	1333,// 64
+	2000,// 96
+	2667,// 128
+	3333,// 160
+	4000,// 192
+	4667,// 224
+	5333,// 256
+	6000,// 288
+	0 // No delay, switch matrix always on
+};
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(void)
+{ 
+	int pdelay = 3 * AC97_LINK_FRAME + delay_table[delay];
+	udelay (pdelay);
+}
+
+
+/*
+ * Read a sample from the adc in polling mode.
+ */
+static int wm97xx_poll_read_adc (wm97xx_ts_t* ts, u16 adcsel, u16* sample)
+{
+	u16 dig1;
+	int timeout = 5 * delay;
+
+	/* set up digitiser */
+	dig1 = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1); 
+	dig1&=0x0fff;
+	ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER1, dig1 | adcsel |
+			       WM97XX_POLL); 
+
+	/* wait 3 AC97 time slots + delay for conversion */
+	poll_delay();
+
+	/* wait for POLL to go low */
+	while ((ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { 
+		udelay(AC97_LINK_FRAME);
+		timeout--;
+	}
+	if (timeout > 0)
+		*sample = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD);
+	else {
+		ts->adc_errs++;
+		err ("adc sample timeout");
+		return 0;
+	}
+  
+	/* check we have correct sample */
+	if ((*sample & 0x7000) != adcsel ) { 
+		err ("adc wrong sample, read %x got %x", adcsel, *sample & 0x7000);
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * Read a sample from the adc in coordinate mode.
+ */
+static int wm97xx_coord_read_adc(wm97xx_ts_t* ts, u16* x, u16* y, u16* pressure)
+{
+	u16 dig1;
+	int timeout = 5 * delay;
+
+	/* set up digitiser */
+	dig1 = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1); 
+	dig1&=0x0fff;
+	ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER1, dig1 | WM97XX_ADCSEL_PRES |
+		WM97XX_POLL); 
+
+	/* wait 3 AC97 time slots + delay for conversion */
+	poll_delay();
+	
+	/* read X then wait for 1 AC97 link frame + settling delay */
+	*x = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD);
+	udelay (AC97_LINK_FRAME + delay_table[delay]);
+
+	/* read Y */
+	*y = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD);
+	
+	/* wait for POLL to go low and then read pressure */
+	while ((ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)&& timeout) {
+			udelay(AC97_LINK_FRAME);
+			timeout--;
+	}
+	if (timeout > 0)		
+		*pressure = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD);
+	else {
+		ts->adc_errs++;
+		err ("adc sample timeout");
+		return 0;
+	}
+	
+	/* check we have correct samples */
+	if (((*x & 0x7000) == 0x1000) && ((*y & 0x7000) == 0x2000) && 
+		((*pressure & 0x7000) == 0x3000)) { 
+		return 1;
+	} else {
+		ts->adc_errs++;
+		err ("adc got wrong samples, got x 0x%x y 0x%x pressure 0x%x", *x, *y, *pressure);
+		return 0;
+	}
+}
+
+/*
+ * Sample the touchscreen in polling mode
+ */
+int wm97xx_poll_touch(wm97xx_ts_t *ts)
+{
+	u16 x, y, p;
+	
+	if (!wm97xx_poll_read_adc(ts, WM97XX_ADCSEL_X, &x)) {
+		info("x %d\n", x);
+		return -EIO;
+	}
+	if (!wm97xx_poll_read_adc(ts, WM97XX_ADCSEL_Y, &y)) {
+		info("y %d\n", y);
+		return -EIO;
+	}
+	if (pil && !five_wire) { 
+		if (!wm97xx_poll_read_adc(ts, WM97XX_ADCSEL_PRES, &p)) {
+			info("p %d\n", p);
+			return -EIO;
+		}
+	} else {
+		p = 0xffff;
+	}
+	
+	wm97xx_ts_evt_add(ts, p, x, y);
+	return 1;
+}
+
+/*
+ * Sample the touchscreen in polling coordinate mode
+ */
+int wm97xx_poll_coord_touch(wm97xx_ts_t *ts)
+{
+	u16 x, y, p;
+	
+	if (wm97xx_coord_read_adc(ts, &x, &y, &p)) {
+		wm97xx_ts_evt_add(ts, p, x, y);
+		return 1;
+	} else
+		return -EIO;
+}
+
+/*
+ * Sample the touchscreen in continous mode
+ */
+int wm97xx_cont_touch(wm97xx_ts_t *ts)
+{
+	u16 x, y;
+	int count = 0;
+	
+	/* currently assuming x is followed by y from the FIFO */
+	while (WM97XX_FIFO_HAS_DATA) {
+		x = WM97XX_READ_FIFO;
+		y = WM97XX_READ_FIFO;
+		wm97xx_ts_evt_add(ts, 0xffff, x, y);
+		count++;
+	}
+	return count;
+}
+
+/*
+ * Is the pen down ?
+ */
+static inline int pendown (wm97xx_ts_t *ts)
+{
+	return ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD) 
+		           & WM97XX_PEN_DOWN;
+}
+
+#if defined(CONFIG_PROC_FS)
+static int wm97xx_read_proc (char *page, char **start, off_t off,
+			     int count, int *eof, void *data)
+{
+	int len = 0, prpu;
+	u16 dig1, dig2, digrd, adcsel, adcsrc, slt, prp, rev;
+	unsigned long flags;
+	char srev = ' ';
+	wm97xx_ts_t* ts;
+
+	if ((ts = data) == NULL)
+		return -ENODEV;
+      
+	spin_lock_irqsave(&ts->lock, flags);
+	if (!ts->is_registered) {
+		spin_unlock_irqrestore(&ts->lock, flags);
+		len += sprintf (page+len, "No device registered\n");
+		return len;
+	}
+
+	dig1 = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1);
+	dig2 = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER2);
+
+	ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER1, dig1 | WM97XX_POLL);
+	poll_delay();
+
+	digrd = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD);
+	rev = (ts->codec->codec_read(ts->codec, AC97_WM9712_REV) & 0x000c) >> 2;
+
+	spin_unlock_irqrestore(&ts->lock, flags);
+      
+	adcsel = dig1 & 0x7000;
+	adcsrc = digrd & 0x7000;
+	slt = (dig1 & 0x7) + 5;
+	prp = dig2 & 0xc000;
+	prpu = dig2 & 0x003f;
+
+	len += sprintf (page+len, "Wolfson WM97xx Version %s\n", WM_TS_VERSION);
+      
+	len += sprintf (page+len, "Using %s", ts->is_wm9712 ? "WM9712" : "WM9705");
+	if (ts->is_wm9712) {
+		switch (rev) {
+		case 0x0:
+			srev = 'A';
+			break;
+		case 0x1:
+			srev = 'B';
+			break;
+		case 0x2:
+			srev = 'D';
+			break;
+		case 0x3:
+			srev = 'E';
+			break;
+		}
+		len += sprintf (page+len, " silicon rev %c\n",srev);
+	} else
+		len += sprintf (page+len, "\n");
+      
+	len += sprintf (page+len, "Settings     :\n%s%s%s%s",
+			dig1 & WM97XX_POLL ? " -sampling adc data(poll)\n" : "",
+			adcsel ==  WM97XX_ADCSEL_X ? " -adc set to X coordinate\n" : "",
+			adcsel ==  WM97XX_ADCSEL_Y ? " -adc set to Y coordinate\n" : "",
+			adcsel ==  WM97XX_ADCSEL_PRES ? " -adc set to pressure\n" : "");
+	if (ts->is_wm9712) {
+		len += sprintf (page+len, "%s%s%s%s", 
+				adcsel ==  WM9712_ADCSEL_COMP1 ? " -adc set to COMP1/AUX1\n" : "",
+				adcsel ==  WM9712_ADCSEL_COMP2 ? " -adc set to COMP2/AUX2\n" : "",
+				adcsel ==  WM9712_ADCSEL_BMON ? " -adc set to BMON\n" : "",
+				adcsel ==  WM9712_ADCSEL_WIPER ? " -adc set to WIPER\n" : "");
+	} else {
+		len += sprintf (page+len, "%s%s%s%s",
+				adcsel ==  WM9705_ADCSEL_PCBEEP ? " -adc set to PCBEEP\n" : "",
+				adcsel ==  WM9705_ADCSEL_PHONE ? " -adc set to PHONE\n" : "",
+				adcsel ==  WM9705_ADCSEL_BMON ? " -adc set to BMON\n" : "",
+				adcsel ==  WM9705_ADCSEL_AUX ? " -adc set to AUX\n" : "");
+	}
+      
+	len += sprintf (page+len, "%s%s%s%s%s%s",
+			dig1 & WM97XX_COO ? " -coordinate sampling\n" : " -individual sampling\n",
+			dig1 & WM97XX_CTC ? " -continuous mode\n" : " -polling mode\n",
+			prp == WM97XX_PRP_DET ? " -pen detect enabled, no wake up\n" : "",
+			prp == WM97XX_PRP_DETW ? " -pen detect enabled, wake up\n" : "",
+			prp == WM97XX_PRP_DET_DIG ? " -pen digitiser and pen detect enabled\n" : "",
+			dig1 & WM97XX_SLEN ? " -read back using slot " : " -read back using AC97\n");
+      
+	if ((dig1 & WM97XX_SLEN) && slt !=12)
+		len += sprintf(page+len, "%d\n", slt);
+	len += sprintf (page+len, " -adc sample delay %d uSecs\n", delay_table[(dig1 & 0x00f0) >> 4]);
+      
+	if (ts->is_wm9712) {
+		if (prpu)
+			len += sprintf (page+len, " -rpu %d Ohms\n", 64000/ prpu);
+		len += sprintf (page+len, " -pressure current %s uA\n", dig2 & WM9712_PIL ? "400" : "200");
+		len += sprintf (page+len, " -using %s wire touchscreen mode", dig2 & WM9712_45W ? "5" : "4");
+	} else {
+		len += sprintf (page+len, " -pressure current %s uA\n", dig2 & WM9705_PIL ? "400" : "200");
+		len += sprintf (page+len, " -%s impedance for PHONE and PCBEEP\n", dig2 & WM9705_PHIZ ? "high" : "low");
+	}
+      
+	len += sprintf(page+len, "\nADC data:\n%s%d\n%s%s\n",
+		       " -adc value (decimal) : ", digrd & 0x0fff,
+		       " -pen ", digrd & 0x8000 ? "Down" : "Up");
+	if (ts->is_wm9712) {
+		len += sprintf (page+len, "%s%s%s%s", 
+				adcsrc ==  WM9712_ADCSEL_COMP1 ? " -adc value is COMP1/AUX1\n" : "",
+				adcsrc ==  WM9712_ADCSEL_COMP2 ? " -adc value is COMP2/AUX2\n" : "",
+				adcsrc ==  WM9712_ADCSEL_BMON ? " -adc value is BMON\n" : "",
+				adcsrc ==  WM9712_ADCSEL_WIPER ? " -adc value is WIPER\n" : "");
+	} else {
+		len += sprintf (page+len, "%s%s%s%s",
+				adcsrc ==  WM9705_ADCSEL_PCBEEP ? " -adc value is PCBEEP\n" : "",
+				adcsrc ==  WM9705_ADCSEL_PHONE ? " -adc value is PHONE\n" : "",
+				adcsrc ==  WM9705_ADCSEL_BMON ? " -adc value is BMON\n" : "",
+				adcsrc ==  WM9705_ADCSEL_AUX ? " -adc value is AUX\n" : "");
+	}
+      
+	len += sprintf(page+len, "\nRegisters:\n%s%x\n%s%x\n%s%x\n",
+		       " -digitiser 1    (0x76) : 0x", dig1,
+		       " -digitiser 2    (0x78) : 0x", dig2,
+		       " -digitiser read (0x7a) : 0x", digrd);
+      
+	len += sprintf(page+len, "\nErrors:\n%s%d\n",
+		       " -coordinate errors ", ts->adc_errs);
+
+	return len;
+}
+
+#endif
+
+#if defined(CONFIG_PM)
+/* WM97xx Power Management
+ * The WM9712 has extra powerdown states that are controlled in 
+ * seperate registers from the AC97 power management.
+ * We will only power down into the extra WM9712 states and leave 
+ * the AC97 power management to the sound driver.
+ */
+static int wm97xx_pm_event(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+	switch(rqst) {
+		case PM_SUSPEND:
+			wm97xx_suspend();
+			break;
+		case PM_RESUME:
+			wm97xx_resume();
+			break;
+	}
+	return 0;
+}
+
+/*
+ * Power down the codec
+ */
+static void wm97xx_suspend(void)
+{
+	wm97xx_ts_t* ts = &wm97xx_ts;
+	u16 reg;
+	unsigned long flags;
+      
+	spin_lock_irqsave(&ts->lock, flags);
+	if (!ts->is_registered) {
+		spin_unlock_irqrestore(&ts->lock, flags);
+		return;
+	}
+      
+	/* wm9705 does not have extra PM */
+	if (!ts->is_wm9712) {
+		spin_unlock_irqrestore(&ts->lock, flags);
+		return;
+	}
+      
+	/* save and mute the PGA's */
+	wm9712_pga_save(ts);
+      
+	reg = ts->codec->codec_read(ts->codec, AC97_PHONE_VOL);
+	ts->codec->codec_write(ts->codec, AC97_PHONE_VOL, reg | 0x001f);
+      
+	reg = ts->codec->codec_read(ts->codec, AC97_MIC_VOL);
+	ts->codec->codec_write(ts->codec, AC97_MIC_VOL, reg | 0x1f1f);
+      
+	reg = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL);
+	ts->codec->codec_write(ts->codec, AC97_LINEIN_VOL, reg | 0x1f1f);
+      
+	/* power down, dont disable the AC link */
+	ts->codec->codec_write(ts->codec, AC97_WM9712_POWER, WM9712_PD(14) | 
+			       WM9712_PD(13) | WM9712_PD(12) | WM9712_PD(11) | 
+			       WM9712_PD(10) | WM9712_PD(9) | WM9712_PD(8) | 
+			       WM9712_PD(7) | WM9712_PD(6) | WM9712_PD(5) | 
+			       WM9712_PD(4) | WM9712_PD(3) | WM9712_PD(2) | 
+			       WM9712_PD(1) | WM9712_PD(0));
+      
+	spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+/*
+ * Power up the Codec
+ */
+static void wm97xx_resume(void)
+{
+	wm97xx_ts_t* ts = &wm97xx_ts;
+	unsigned long flags;
+      
+	/* are we registered */
+	spin_lock_irqsave(&ts->lock, flags);
+	if (!ts->is_registered) {
+		spin_unlock_irqrestore(&ts->lock, flags);
+		return;
+	}
+      
+	/* wm9705 does not have extra PM */
+	if (!ts->is_wm9712) {
+		spin_unlock_irqrestore(&ts->lock, flags);
+		return;
+	}
+
+	/* power up */
+	ts->codec->codec_write(ts->codec, AC97_WM9712_POWER, 0x0);
+      
+	/* restore PGA state */
+	wm9712_pga_restore(ts);
+      
+	spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+/* save state of wm9712 PGA's */
+static void wm9712_pga_save(wm97xx_ts_t* ts)
+{
+	ts->phone_pga = ts->codec->codec_read(ts->codec, AC97_PHONE_VOL) & 0x001f;
+	ts->line_pgal = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL) & 0x1f00;
+	ts->line_pgar = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL) & 0x001f;
+	ts->mic_pgal = ts->codec->codec_read(ts->codec, AC97_MIC_VOL) & 0x1f00;
+	ts->mic_pgar = ts->codec->codec_read(ts->codec, AC97_MIC_VOL) & 0x001f;
+}
+
+/* restore state of wm9712 PGA's */
+static void wm9712_pga_restore(wm97xx_ts_t* ts)
+{
+	u16 reg;
+      
+	reg = ts->codec->codec_read(ts->codec, AC97_PHONE_VOL);
+	ts->codec->codec_write(ts->codec, AC97_PHONE_VOL, reg | ts->phone_pga);
+      
+	reg = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL);
+	ts->codec->codec_write(ts->codec, AC97_LINEIN_VOL, reg | ts->line_pgar | (ts->line_pgal << 8));
+
+	reg = ts->codec->codec_read(ts->codec, AC97_MIC_VOL);
+	ts->codec->codec_write(ts->codec, AC97_MIC_VOL, reg | ts->mic_pgar | (ts->mic_pgal << 8));
+}
+
+#endif
+
+/*
+ * set up the physical settings of the device 
+ */
+static void init_wm97xx_phy(void)
+{
+	u16 dig1, dig2, aux, vid;
+	wm97xx_ts_t *ts = &wm97xx_ts;
+
+	/* default values */
+	dig1 = WM97XX_DELAY(4) | WM97XX_SLT(5);
+	if (ts->is_wm9712)
+		dig2 = WM9712_RPU(1);
+	else {
+		dig2 = 0x0;
+	
+		/* 
+		 * mute VIDEO and AUX as they share X and Y touchscreen 
+		 * inputs on the WM9705 
+		 */
+		aux = ts->codec->codec_read(ts->codec, AC97_AUX_VOL);
+		if (!(aux & 0x8000)) {
+			info("muting AUX mixer as it shares X touchscreen coordinate");
+			ts->codec->codec_write(ts->codec, AC97_AUX_VOL, 0x8000 | aux);
+		}
+	
+		vid = ts->codec->codec_read(ts->codec, AC97_VIDEO_VOL);
+		if (!(vid & 0x8000)) {
+			info("muting VIDEO mixer as it shares Y touchscreen coordinate");
+			ts->codec->codec_write(ts->codec, AC97_VIDEO_VOL, 0x8000 | vid);
+		}
+	}
+      
+	/* WM9712 rpu */
+	if (ts->is_wm9712 && rpu) {
+		dig2 &= 0xffc0;
+		dig2 |= WM9712_RPU(rpu);
+		info("setting pen detect pull-up to %d Ohms",64000 / rpu);
+	}
+      
+	/* touchpanel pressure */
+	if  (pil == 2) {
+		if (ts->is_wm9712)
+			dig2 |= WM9712_PIL;
+		else
+			dig2 |= WM9705_PIL;
+		info("setting pressure measurement current to 400uA.");
+	} else if (pil) 
+		info ("setting pressure measurement current to 200uA.");
+      
+	/* WM9712 five wire */
+	if (ts->is_wm9712 && five_wire) {
+		dig2 |= WM9712_45W;
+		info("setting 5-wire touchscreen mode.");
+	}
+      
+	/* sample settling delay */
+	if (delay!=4) {
+		if (delay < 0 || delay > 15) {
+			info ("supplied delay out of range.");
+			delay = 4;
+		}
+		dig1 &= 0xff0f;
+		dig1 |= WM97XX_DELAY(delay);
+		info("setting adc sample delay to %d u Secs.", delay_table[delay]);
+	}
+      
+	/* coordinate mode */
+	if (mode == 1) {
+		dig1 |= WM97XX_COO;
+		info("using coordinate mode");
+	}
+	
+	/* continous mode */
+	if (mode == 2) {
+		dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_CM_RATE_375 | WM97XX_SLEN | WM97XX_SLT(5);
+		info("using continous mode");
+		if (ts->is_wm9712)
+			dig2 |= WM9712_PDEN;
+		else
+			dig2 |= WM9705_PDEN;
+	}
+      
+	/* WM9705 pdd */
+	if (pdd && !ts->is_wm9712) {
+		dig2 |= (pdd & 0x000f);
+		info("setting pdd to Vmid/%d", 1 - (pdd & 0x000f));
+	}
+      
+	ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER1, dig1);
+	ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER2, dig2); 
+}
+
+
+/*
+ * Called by the audio codec initialisation to register
+ * the touchscreen driver.
+ */
+
+static int wm97xx_probe(struct ac97_codec *codec, struct ac97_driver *driver)
+{
+	unsigned long flags;
+	u16 id1, id2;
+	wm97xx_ts_t *ts = &wm97xx_ts;
+  
+	spin_lock_irqsave(&ts->lock, flags);
+      
+	/* we only support 1 touchscreen at the moment */
+	if (ts->is_registered) {
+		spin_unlock_irqrestore(&ts->lock, flags);
+		return -1;
+	}
+      
+	/* 
+	 * We can only use a WM9705 or WM9712 that has been *first* initialised
+	 * by the AC97 audio driver. This is because we have to use the audio 
+	 * drivers codec read() and write() functions to sample the touchscreen
+	 *
+	 * If an initialsed WM97xx is found then get the codec read and write 
+	 * functions. 
+	 */
+      
+	/* test for a WM9712 or a WM9705 */
+	id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
+	id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
+	if (id1 == WM97XX_ID1 && id2 == WM9712_ID2) {
+		ts->is_wm9712 = 1;
+		info("registered a WM9712");
+	} else if (id1 == WM97XX_ID1 && id2 == WM9705_ID2) {
+		ts->is_wm9712 = 0;
+		info("registered a WM9705");
+	} else {
+		err("could not find a WM97xx codec. Found a 0x%4x:0x%4x instead",
+		    id1, id2);
+		spin_unlock_irqrestore(&ts->lock, flags);
+		return -1;
+	}
+      
+	/* set up AC97 codec interface */
+	ts->codec = codec;
+	codec->driver_private = (void*)&ts;
+      
+	/* set up physical characteristics */
+	init_wm97xx_phy();
+      
+	ts->is_registered = 1;
+	spin_unlock_irqrestore(&ts->lock, flags);
+	return 0;
+}
+
+/*
+ * Called by ac97_codec when it is unloaded. 
+ */
+static void wm97xx_remove(struct ac97_codec *codec, struct ac97_driver *driver)
+{
+	unsigned long flags;
+	u16 dig1, dig2;
+	wm97xx_ts_t *ts = codec->driver_private;
+	
+    dbg("Unloading codec\n"); 
+	spin_lock_irqsave(&ts->lock, flags);
+      
+	/* check that are registered */
+	if (!ts->is_registered) {
+		err("double unregister");
+		spin_unlock_irqrestore(&ts->lock, flags);
+		return;
+	}
+      
+	ts->is_registered = 0;
+      
+	/* restore default digitiser values */
+	dig1 = WM97XX_DELAY(4) | WM97XX_SLT(6);
+	if (ts->is_wm9712)
+		dig2 = WM9712_RPU(1);
+	else 
+		dig2 = 0x0;
+      
+	codec->codec_write(codec, AC97_WM97XX_DIGITISER1, dig1);
+	codec->codec_write(codec, AC97_WM97XX_DIGITISER2, dig2); 
+	ts->codec = NULL;
+      
+	spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+/*
+ * add a touch event
+ */
+static int wm97xx_ts_evt_add(wm97xx_ts_t* ts, u16 pressure, u16 x, u16 y)
+{
+	/* add event and remove adc src bits */
+	//printk("x %d y %d p %d\n", x,y,pressure);
+	input_report_abs(ts->idev, ABS_X, x & 0xfff);
+	input_report_abs(ts->idev, ABS_Y, y & 0xfff);
+	input_report_abs(ts->idev, ABS_PRESSURE, pressure & 0xfff);
+	return 0;
+}
+
+/*
+ * add a pen up event
+ */
+static int wm97xx_ts_evt_release(wm97xx_ts_t* ts)
+{
+//	dbg("release the stylus.\n");
+	input_report_abs(ts->idev, ABS_PRESSURE, 0);
+	return 0;
+}
+
+/*
+ * The touchscreen sample reader thread
+ */
+static int wm97xx_thread(void *_ts)
+{
+	wm97xx_ts_t *ts = (wm97xx_ts_t *)_ts;
+	struct task_struct *tsk = current;
+	int valid = 0;
+
+	ts->rtask = tsk;
+
+	/* set up thread context */
+	daemonize();
+	reparent_to_init();
+	strcpy(tsk->comm, "ktsd");
+	tsk->tty = NULL;
+
+	/* we will die when we receive SIGKILL */
+	spin_lock_irq(&tsk->sigmask_lock);
+	siginitsetinv(&tsk->blocked, sigmask(SIGKILL));
+	recalc_sigpending(tsk);
+	spin_unlock_irq(&tsk->sigmask_lock);
+
+	/* init is complete */
+	complete(&ts->thread_init);
+
+	/* touch reader loop */
+	for (;;) {
+		ts->restart = 0;
+		if(signal_pending(tsk))
+			break;
+
+		if(pendown(ts)) {
+			switch (mode) {
+				case 0:
+					wm97xx_poll_touch(ts);
+					valid = 1;
+					break;
+				case 1:
+					wm97xx_poll_coord_touch(ts);
+					valid = 1;
+					break;
+				case 2:
+					wm97xx_cont_touch(ts);
+					valid = 1;
+					break;
+			}
+		} else {
+			if (valid) {
+				valid = 0;
+				wm97xx_ts_evt_release(ts);
+			}
+		}
+
+		set_task_state(tsk, TASK_INTERRUPTIBLE);
+		if (HZ >= 100)
+			schedule_timeout(HZ/100);
+		else
+			schedule_timeout(1);
+	}
+	ts->rtask = NULL;
+	complete_and_exit(&ts->thread_exit, 0);
+
+	return 0;
+}
+
+/*
+ * Start the touchscreen thread and 
+ * the touch digitiser.
+ */
+static int wm97xx_ts_input_open(struct input_dev *idev)
+{
+	wm97xx_ts_t *ts = (wm97xx_ts_t *) &wm97xx_ts;
+	u32 flags;
+	int ret, val;
+
+	spin_lock_irqsave( &ts->lock, flags );
+	if ( ts->use_count++ == 0 ) {
+
+		/* start touchscreen thread */
+		ts->idev = idev;
+		init_completion(&ts->thread_init);
+		ret = kernel_thread(wm97xx_thread, ts, 0);
+		if (ret >= 0)
+			wait_for_completion(&ts->thread_init); 
+	
+		/* start digitiser */
+		val = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER2);
+		ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER2,
+								   val | WM97XX_PRP_DET_DIG);
+	}
+	spin_unlock_irqrestore( &ts->lock, flags );
+	return 0;
+}
+
+/*
+ * Kill the touchscreen thread and stop
+ * the touch digitiser.
+ */
+static void wm97xx_ts_input_close(struct input_dev *idev)
+{
+	wm97xx_ts_t *ts = (wm97xx_ts_t *) &wm97xx_ts;
+	u32 flags;
+	int val;
+
+	spin_lock_irqsave(&ts->lock, flags);
+	if (--ts->use_count == 0) {
+		/* kill thread */
+		init_completion(&ts->thread_exit);
+		if (ts->rtask) {
+			send_sig(SIGKILL, ts->rtask, 1);
+			wait_for_completion(&ts->thread_exit);
+		}
+
+		/* stop digitiser */
+		val = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER2);
+		ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER2,
+				       val & ~WM97XX_PRP_DET_DIG);
+	}
+	spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+static int __init wm97xx_ts_init_module(void)
+{
+	wm97xx_ts_t* ts = &wm97xx_ts;
+	char proc_str[64];
+      
+	info("Wolfson WM9705/WM9712 Touchscreen Controller");
+	info("Version %s  liam.girdwood@wolfsonmicro.com", WM_TS_VERSION);
+      
+	memset(ts, 0, sizeof(wm97xx_ts_t));
+
+	/* tell input system what we events we accept and register */
+	wm97xx_input.name = "wm97xx touchscreen";
+	wm97xx_input.open = wm97xx_ts_input_open;
+	wm97xx_input.close = wm97xx_ts_input_close;
+	__set_bit(EV_ABS, wm97xx_input.evbit);
+	__set_bit(ABS_X, wm97xx_input.absbit);
+	__set_bit(ABS_Y, wm97xx_input.absbit);
+	__set_bit(ABS_PRESSURE, wm97xx_input.absbit);
+	input_register_device(&wm97xx_input);
+   
+	spin_lock_init(&ts->lock);
+	init_MUTEX(&ts->sem);
+	
+	/* register with the AC97 layer */
+	ac97_register_driver(&wm9705_driver);
+	ac97_register_driver(&wm9712_driver);
+      
+#if defined(CONFIG_PROC_FS)
+	sprintf(proc_str, "driver/%s", TS_NAME);
+	if ((ts->wm97xx_ts_ps = create_proc_read_entry (proc_str, 0, NULL,
+							wm97xx_read_proc, ts)) == 0)
+		err("could not register proc interface /proc/%s", proc_str);
+#endif
+#if defined(CONFIG_PM)
+	if ((ts->pm = pm_register(PM_UNKNOWN_DEV, PM_SYS_UNKNOWN, wm97xx_pm_event)) == 0)
+		err("could not register with power management");
+#endif
+	
+	return 0;
+}
+
+static void wm97xx_ts_cleanup_module(void)
+{
+#if defined(CONFIG_PM)
+	pm_unregister (wm97xx_ts.pm);
+#endif
+	input_unregister_device(&wm97xx_input);
+}
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("WM9705/WM9712 Touch Screen / BMON Driver");
+MODULE_LICENSE("GPL");
+
+module_init(wm97xx_ts_init_module);
+module_exit(wm97xx_ts_cleanup_module);
+
+#ifndef MODULE
+
+static int __init wm97xx_ts_setup(char *options)
+{
+	char *this_opt = options;
+
+	if (!options || !*options)
+		return 0;
+
+	/* parse the options and check for out of range values */
+	for(this_opt=strsep(options, ",");
+	    this_opt; this_opt=strsep(NULL, ",")) {
+		if (!strncmp(this_opt, "pil:", 4)) {
+			this_opt+=4;
+			pil = simple_strtol(this_opt, NULL, 0);
+			if (pil < 0 || pil > 2)
+				pil = 0;
+			continue;
+		}
+		if (!strncmp(this_opt, "rpu:", 4)) {
+			this_opt+=4;
+			rpu = simple_strtol(this_opt, NULL, 0);
+			if (rpu < 0 || rpu > 31)
+				rpu = 0;
+			continue;
+		}
+		if (!strncmp(this_opt, "pdd:", 4)) {
+			this_opt+=4;
+			pdd = simple_strtol(this_opt, NULL, 0);
+			if (pdd < 0 || pdd > 15)
+				pdd = 0;
+			continue;
+		}
+		if (!strncmp(this_opt, "delay:", 6)) {
+			this_opt+=6;
+			delay = simple_strtol(this_opt, NULL, 0);
+			if (delay < 0 || delay > 15)
+				delay = 4;
+			continue;
+		}
+		if (!strncmp(this_opt, "five_wire:", 10)) {
+			this_opt+=10;
+			five_wire = simple_strtol(this_opt, NULL, 0);
+			if (five_wire < 0 || five_wire > 1)
+				five_wire = 0;
+			continue;
+		}
+		if (!strncmp(this_opt, "mode:", 5)) {
+			this_opt+=5;
+			mode = simple_strtol(this_opt, NULL, 0);
+			if (mode < 0 || mode > 2)
+				mode = 0;
+			continue;
+		}
+	}
+	return 1;
+}
+
+__setup("wm97xx_ts=", wm97xx_ts_setup);
+
+#endif /* MODULE */
Index: drivers/sound/Config.in
===================================================================
--- drivers/sound/Config.in	(.../vendor/arm/linux)	(revision 33)
+++ linux/drivers/sound/Config.in	(.../branches/development/linux)	(revision 33)
@@ -227,9 +227,15 @@
    fi
    dep_tristate '    Netwinder WaveArtist' CONFIG_SOUND_WAVEARTIST $CONFIG_SOUND_OSS $CONFIG_ARCH_NETWINDER
    dep_tristate '  Intel PXA250/210 AC97 audio' CONFIG_SOUND_PXA_AC97 $CONFIG_ARCH_PXA $CONFIG_SOUND
+      dep_tristate '  Wolfson WM8753 audio' CONFIG_SOUND_PXA_WM8753 $CONFIG_ARCH_PXA $CONFIG_SOUND
 fi
 
 dep_tristate '  TV card (bt848) mixer support' CONFIG_SOUND_TVMIXER $CONFIG_SOUND $CONFIG_I2C
+dep_tristate '  Wolfson Touchscreen/BMON plugin' CONFIG_SOUND_WM97XX $CONFIG_SOUND 
+dep_tristate '  Wolfson WM9713 Touchscreen/DAC plugin' CONFIG_SOUND_WM9713 $CONFIG_SOUND
+if ["$CONFIG_SOUND_WM9713" = "y" -o "$CONFIG_SOUND_WM9713" = "m"]; then
+	define_bool CONFIG_SOUND_AC97_WARM y
+fi
 
 # A cross directory dependence. The sound modules will need gameport.o compiled in,
 # but it resides in the drivers/char/joystick directory. This define_tristate takes
Index: drivers/sound/ac97_codec.c
===================================================================
--- drivers/sound/ac97_codec.c	(.../vendor/arm/linux)	(revision 33)
+++ linux/drivers/sound/ac97_codec.c	(.../branches/development/linux)	(revision 33)
@@ -1,4 +1,3 @@
-
 /*
  * ac97_codec.c: Generic AC97 mixer/modem module
  *
@@ -31,6 +30,13 @@
  **************************************************************************
  *
  * History
+ * Feb 25, 2004 Liam Girdwood
+ *  Added support for codecs that require a warm reset to power up.
+ *  Support for WM9713
+ * May 02, 2003 Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ *	Removed non existant WM9700
+ *	Added support for WM9705, WM9708, WM9709, WM9710, WM9711
+ *	WM9712 and WM9717
  * Mar 28, 2002 Randolph Bentson <bentson@holmsjoen.com>
  *	corrections to support WM9707 in ViewPad 1000
  * v0.4 Mar 15 2000 Ollie Lho
@@ -45,6 +51,7 @@
 #include <linux/module.h>
 #include <linux/version.h>
 #include <linux/kernel.h>
+#include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/bitops.h>
@@ -52,6 +59,8 @@
 #include <linux/ac97_codec.h>
 #include <asm/uaccess.h>
 
+#define CODEC_ID_BUFSZ 14
+
 static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel);
 static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel, 
 			     unsigned int left, unsigned int right);
@@ -61,9 +70,12 @@
 
 static int ac97_init_mixer(struct ac97_codec *codec);
 
-static int wolfson_init00(struct ac97_codec * codec);
 static int wolfson_init03(struct ac97_codec * codec);
 static int wolfson_init04(struct ac97_codec * codec);
+static int wolfson_init05(struct ac97_codec * codec);
+static int wolfson_init11(struct ac97_codec * codec);
+static int wolfson_init13(struct ac97_codec * codec);
+static int phillips_ucb1400_init(struct ac97_codec * codec);
 static int tritech_init(struct ac97_codec * codec);
 static int tritech_maestro_init(struct ac97_codec * codec);
 static int sigmatel_9708_init(struct ac97_codec *codec);
@@ -71,7 +83,10 @@
 static int sigmatel_9744_init(struct ac97_codec *codec);
 static int ad1886_init(struct ac97_codec *codec);
 static int eapd_control(struct ac97_codec *codec, int);
-static int crystal_digital_control(struct ac97_codec *codec, int mode);
+static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
+static int cmedia_init(struct ac97_codec * codec);
+static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
+static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
 
 
 /*
@@ -92,9 +107,13 @@
  
 static struct ac97_ops null_ops = { NULL, NULL, NULL };
 static struct ac97_ops default_ops = { NULL, eapd_control, NULL };
-static struct ac97_ops wolfson_ops00 = { wolfson_init00, NULL, NULL };
+static struct ac97_ops default_digital_ops = { NULL, eapd_control, generic_digital_control};
 static struct ac97_ops wolfson_ops03 = { wolfson_init03, NULL, NULL };
 static struct ac97_ops wolfson_ops04 = { wolfson_init04, NULL, NULL };
+static struct ac97_ops wolfson_ops05 = { wolfson_init05, NULL, NULL };
+static struct ac97_ops wolfson_ops11 = { wolfson_init11, NULL, NULL };
+static struct ac97_ops wolfson_ops13 = { wolfson_init13, NULL, NULL };
+static struct ac97_ops phillips_ucb1400_ops = { phillips_ucb1400_init, NULL, NULL };
 static struct ac97_ops tritech_ops = { tritech_init, NULL, NULL };
 static struct ac97_ops tritech_m_ops = { tritech_maestro_init, NULL, NULL };
 static struct ac97_ops sigmatel_9708_ops = { sigmatel_9708_init, NULL, NULL };
@@ -102,12 +121,15 @@
 static struct ac97_ops sigmatel_9744_ops = { sigmatel_9744_init, NULL, NULL };
 static struct ac97_ops crystal_digital_ops = { NULL, eapd_control, crystal_digital_control };
 static struct ac97_ops ad1886_ops = { ad1886_init, eapd_control, NULL };
+static struct ac97_ops cmedia_ops = { NULL, eapd_control, NULL};
+static struct ac97_ops cmedia_digital_ops = { cmedia_init, eapd_control, cmedia_digital_control};
 
 /* sorted by vendor/device id */
 static const struct {
 	u32 id;
 	char *name;
 	struct ac97_ops *ops;
+	int flags;
 } ac97_codec_ids[] = {
 	{0x41445303, "Analog Devices AD1819",	&null_ops},
 	{0x41445340, "Analog Devices AD1881",	&null_ops},
@@ -119,8 +141,12 @@
 	{0x414B4D00, "Asahi Kasei AK4540",	&null_ops},
 	{0x414B4D01, "Asahi Kasei AK4542",	&null_ops},
 	{0x414B4D02, "Asahi Kasei AK4543",	&null_ops},
+	{0x414C4326, "ALC100P",			&null_ops},
 	{0x414C4710, "ALC200/200P",		&null_ops},
-	{0x414C4720, "ALC650",			&null_ops},
+	{0x414C4720, "ALC650",			&default_digital_ops},
+	{0x434D4941, "CMedia",			&cmedia_ops,		AC97_NO_PCM_VOLUME },
+	{0x434D4942, "CMedia",			&cmedia_ops,		AC97_NO_PCM_VOLUME },
+	{0x434D4961, "CMedia",			&cmedia_digital_ops,	AC97_NO_PCM_VOLUME },
 	{0x43525900, "Cirrus Logic CS4297",	&default_ops},
 	{0x43525903, "Cirrus Logic CS4297",	&default_ops},
 	{0x43525913, "Cirrus Logic CS4297A rev A", &default_ops},
@@ -131,11 +157,12 @@
 	{0x43525931, "Cirrus Logic CS4299 rev A", &crystal_digital_ops},
 	{0x43525933, "Cirrus Logic CS4299 rev C", &crystal_digital_ops},
 	{0x43525934, "Cirrus Logic CS4299 rev D", &crystal_digital_ops},
-	{0x4352594d, "Cirrus Logic CS4201"	, &null_ops},
+	{0x43585442, "CXT66",			&default_ops,		AC97_DELUDED_MODEM },
+	{0x44543031, "Diamond Technology DT0893", &default_ops},
 	{0x45838308, "ESS Allegro ES1988",	&null_ops},
 	{0x49434511, "ICE1232",			&null_ops}, /* I hope --jk */
 	{0x4e534331, "National Semiconductor LM4549", &null_ops},
-	{0x50534304, "Philips UCB1400",		&default_ops},
+	{0x50534304, "Philips UCB1400",		&phillips_ucb1400_ops},
 	{0x53494c22, "Silicon Laboratory Si3036", &null_ops},
 	{0x53494c23, "Silicon Laboratory Si3038", &null_ops},
 	{0x545200FF, "TriTech TR?????",		&tritech_m_ops},
@@ -144,15 +171,19 @@
 	{0x54524106, "TriTech TR28026",		&null_ops},
 	{0x54524108, "TriTech TR28028",		&tritech_ops},
 	{0x54524123, "TriTech TR A5",		&null_ops},
-	{0x574D4C00, "Wolfson WM9700A",		&wolfson_ops00},
-	{0x574D4C03, "Wolfson WM9703/WM9707",	&wolfson_ops03},
+	{0x574D4C03, "Wolfson WM9703/07/08/17",	&wolfson_ops03},
 	{0x574D4C04, "Wolfson WM9704M/WM9704Q",	&wolfson_ops04},
+	{0x574D4C05, "Wolfson WM9705/WM9710",   &wolfson_ops05},
+	{0x574D4C09, "Wolfson WM9709",		&null_ops},
+	{0x574D4C12, "Wolfson WM9711/9712",	&wolfson_ops11},
+	{0x574D4C13, "Wolfson WM9713",	&wolfson_ops13, AC97_DEFAULT_POWER_OFF},
 	{0x83847600, "SigmaTel STAC????",	&null_ops},
 	{0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops},
 	{0x83847605, "SigmaTel STAC9704",	&null_ops},
 	{0x83847608, "SigmaTel STAC9708",	&sigmatel_9708_ops},
 	{0x83847609, "SigmaTel STAC9721/23",	&sigmatel_9721_ops},
 	{0x83847644, "SigmaTel STAC9744/45",	&sigmatel_9744_ops},
+	{0x83847652, "SigmaTel STAC9752/53",	&default_ops},
 	{0x83847656, "SigmaTel STAC9756/57",	&sigmatel_9744_ops},
 	{0x83847666, "SigmaTel STAC9750T",	&sigmatel_9744_ops},
 	{0x83847684, "SigmaTel STAC9783/84?",	&null_ops},
@@ -272,6 +303,10 @@
 	[SOUND_MIXER_PHONEIN] 	= AC97_REC_PHONE
 };
 
+static LIST_HEAD(codecs);
+static LIST_HEAD(codec_drivers);
+static DECLARE_MUTEX(codec_sem);
+
 /* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows
    about that given mixer, and should be holding a spinlock for the card */
 static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel) 
@@ -656,6 +691,98 @@
 }
 
 /**
+ *	codec_id	-  Turn id1/id2 into a PnP string
+ *	@id1: Vendor ID1
+ *	@id2: Vendor ID2
+ *	@buf: CODEC_ID_BUFSZ byte buffer
+ *
+ *	Fills buf with a zero terminated PnP ident string for the id1/id2
+ *	pair. For convenience the return is the passed in buffer pointer.
+ */
+ 
+static char *codec_id(u16 id1, u16 id2, char *buf)
+{
+	if(id1&0x8080) {
+		snprintf(buf, CODEC_ID_BUFSZ, "0x%04x:0x%04x", id1, id2);
+	} else {
+		buf[0] = (id1 >> 8);
+		buf[1] = (id1 & 0xFF);
+		buf[2] = (id2 >> 8);
+		snprintf(buf+3, CODEC_ID_BUFSZ - 3, "%d", id2&0xFF);
+	}
+	return buf;
+}
+ 
+/**
+ *	ac97_check_modem - Check if the Codec is a modem
+ *	@codec: codec to check
+ *
+ *	Return true if the device is an AC97 1.0 or AC97 2.0 modem
+ */
+ 
+static int ac97_check_modem(struct ac97_codec *codec)
+{
+	/* Check for an AC97 1.0 soft modem (ID1) */
+	if(codec->codec_read(codec, AC97_RESET) & 2)
+		return 1;
+	/* Check for an AC97 2.x soft modem */
+	codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L);
+	if(codec->codec_read(codec, AC97_EXTENDED_MODEM_ID) & 1)
+		return 1;
+	return 0;
+}
+
+
+/**
+ *	ac97_alloc_codec - Allocate an AC97 codec
+ *
+ *	Returns a new AC97 codec structure. AC97 codecs may become
+ *	refcounted soon so this interface is needed. Returns with
+ *	one reference taken.
+ */
+ 
+struct ac97_codec *ac97_alloc_codec(void)
+{
+	struct ac97_codec *codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL);
+	if(!codec)
+		return NULL;
+
+	memset(codec, 0, sizeof(*codec));
+	spin_lock_init(&codec->lock);
+	INIT_LIST_HEAD(&codec->list);
+	return codec;
+}
+
+EXPORT_SYMBOL(ac97_alloc_codec);
+
+/**
+ *	ac97_release_codec -	Release an AC97 codec
+ *	@codec: codec to release
+ *
+ *	Release an allocated AC97 codec. This will be refcounted in
+ *	time but for the moment is trivial. Calls the unregister
+ *	handler if the codec is now defunct.
+ */
+ 
+void ac97_release_codec(struct ac97_codec *codec)
+{
+	/* Remove from the list first, we don't want to be
+	   "rediscovered" */
+	down(&codec_sem);
+	list_del(&codec->list);
+	up(&codec_sem);
+	/*
+	 *	The driver needs to deal with internal
+	 *	locking to avoid accidents here. 
+	 */
+	if(codec->driver)
+		codec->driver->remove(codec, codec->driver);
+	kfree(codec);
+}
+
+EXPORT_SYMBOL(ac97_release_codec);
+
+/**
  *	ac97_probe_codec - Initialize and setup AC97-compatible codec
  *	@codec: (in/out) Kernel info for a single AC97 codec
  *
@@ -675,59 +802,107 @@
  *	Currently codec_wait is used to wait for AC97 codec
  *	reset to complete. 
  *
+ *  Some codecs will power down when a register reset is
+ *  performed. We now check for such codecs.
+ *
  *	Returns 1 (true) on success, or 0 (false) on failure.
  */
  
 int ac97_probe_codec(struct ac97_codec *codec)
 {
 	u16 id1, id2;
-	u16 audio, modem;
+	u16 audio;
 	int i;
-
+	char cidbuf[CODEC_ID_BUFSZ];
+	u16 f;
+	struct list_head *l;
+	struct ac97_driver *d;
+	
 	/* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should 
 	 * be read zero.
 	 *
 	 * FIXME: is the following comment outdated?  -jgarzik 
 	 * Probing of AC97 in this way is not reliable, it is not even SAFE !!
 	 */
-	codec->codec_write(codec, AC97_RESET, 0L);
 
-	/* also according to spec, we wait for codec-ready state */	
+	/* also according to spec, we wait for codec-ready state */
 	if (codec->codec_wait)
 		codec->codec_wait(codec);
 	else
 		udelay(10);
-
-	if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) {
-		printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n",
-		       codec->id ? "Secondary" : "Primary");
-		return 0;
-	}
-
-	/* probe for Modem Codec */
-	codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L);
-	modem = codec->codec_read(codec, AC97_EXTENDED_MODEM_ID);
-
+	
+	/* will the codec power down if register reset ? */
+	id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
+	id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
 	codec->name = NULL;
 	codec->codec_ops = &null_ops;
-
-	id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
-	id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
 	for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) {
 		if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) {
 			codec->type = ac97_codec_ids[i].id;
 			codec->name = ac97_codec_ids[i].name;
 			codec->codec_ops = ac97_codec_ids[i].ops;
+			codec->flags = ac97_codec_ids[i].flags;
 			break;
 		}
 	}
+	codec->model = (id1 << 16) | id2;
+	if (codec->flags != AC97_DEFAULT_POWER_OFF) {
+		/* reset codec and wait for the ready bit before we continue */
+		codec->codec_write(codec, AC97_RESET, 0L);
+		if (codec->codec_wait)
+			codec->codec_wait(codec);
+		else
+			udelay(10);
+	}
+
+	if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) {
+		printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n",
+		       (codec->id & 0x2) ? (codec->id&1 ? "4th" : "Tertiary") 
+		       : (codec->id&1 ? "Secondary":  "Primary"));
+		return 0;
+	}
+
+	/* probe for Modem Codec */
+	codec->modem = ac97_check_modem(codec);
+
+	f = codec->codec_read(codec, AC97_EXTENDED_STATUS);
+	if(f & 4)
+		codec->codec_ops = &default_digital_ops;
+	
+	/* A device which thinks its a modem but isnt */
+	if(codec->flags & AC97_DELUDED_MODEM)
+		codec->modem = 0;
+		
 	if (codec->name == NULL)
 		codec->name = "Unknown";
-	printk(KERN_INFO "ac97_codec: AC97 %s codec, id: 0x%04x:"
-	       "0x%04x (%s)\n", audio ? "Audio" : (modem ? "Modem" : ""),
-	       id1, id2, codec->name);
+	printk(KERN_INFO "ac97_codec: AC97 %s codec, id: %s (%s)\n", 
+		codec->modem ? "Modem" : (audio ? "Audio" : ""),
+	       codec_id(id1, id2, cidbuf), codec->name);
 
-	return ac97_init_mixer(codec);
+	if(!ac97_init_mixer(codec))
+		return 0;
+		
+	/* 
+	 *	Attach last so the caller can override the mixer
+	 *	callbacks.
+	 */
+	 
+	down(&codec_sem);
+	list_add(&codec->list, &codecs);
+
+	list_for_each(l, &codec_drivers) {
+		d = list_entry(l, struct ac97_driver, list);
+		if ((codec->model ^ d->codec_id) & d->codec_mask)
+			continue;
+		if(d->probe(codec, d) == 0)
+		{
+			codec->driver = d;
+			break;
+		}
+	}
+
+	up(&codec_sem);
+	return 1;
 }
 
 static int ac97_init_mixer(struct ac97_codec *codec)
@@ -746,6 +921,7 @@
 	if (!(cap & 0x10))
 		codec->supported_mixers &= ~SOUND_MASK_ALTPCM;
 
+
 	/* detect bit resolution */
 	codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0x2020);
 	if(codec->codec_read(codec, AC97_MASTER_VOL_STEREO) == 0x2020)
@@ -759,11 +935,6 @@
 	codec->recmask_io = ac97_recmask_io;
 	codec->mixer_ioctl = ac97_mixer_ioctl;
 
-	/* codec specific initialization for 4-6 channel output or secondary codec stuff */
-	if (codec->codec_ops->init != NULL) {
-		codec->codec_ops->init(codec);
-	}
-
 	/* initialize mixer channel volumes */
 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
 		struct mixer_defaults *md = &mixer_defaults[i];
@@ -774,6 +945,20 @@
 		ac97_set_mixer(codec, md->mixer, md->value);
 	}
 
+	/* codec specific initialization for 4-6 channel output or secondary codec stuff */
+	if (codec->codec_ops->init != NULL) {
+		codec->codec_ops->init(codec);
+	}
+	
+	/*
+	 *	Volume is MUTE only on this device. We have to initialise
+	 *	it but its useless beyond that.
+	 */
+	if(codec->flags & AC97_NO_PCM_VOLUME)
+	{
+		codec->supported_mixers &= ~SOUND_MASK_PCM;
+		printk(KERN_WARNING "AC97 codec does not have proper volume support.\n");
+	}
 	return 1;
 }
 
@@ -846,55 +1031,100 @@
 	return 0;
 }
 
+static int cmedia_init(struct ac97_codec *codec)
+{
+	/* Initialise the CMedia 9739 */
+	/*
+		We could set various options here
+		Register 0x20 bit 0x100 sets mic as center bass
+		Also do multi_channel_ctrl &=~0x3000 |=0x1000
+		
+		For now we set up the GPIO and PC beep 
+	*/
+	
+	u16 v;
+	
+	/* MIC */
+	codec->codec_write(codec, 0x64, 0x3000);
+	v = codec->codec_read(codec, 0x64);
+	v &= ~0x8000;
+	codec->codec_write(codec, 0x64, v);
+	codec->codec_write(codec, 0x70, 0x0100);
+	codec->codec_write(codec, 0x72, 0x0020);
+	return 0;
+}
+	
+#define AC97_WM97XX_FMIXER_VOL 0x72
+#define AC97_WM97XX_RMIXER_VOL 0x74
+#define AC97_WM97XX_TEST 0x5a
+#define AC97_WM9704_RPCM_VOL 0x70
+#define AC97_WM9711_OUT3VOL 0x16
 
-static int wolfson_init00(struct ac97_codec * codec)
+static int wolfson_init03(struct ac97_codec * codec)
 {
-	/* This initialization is suspect, but not known to be wrong.
-	   It was copied from the initialization for the WM9704Q, but
-	   that same sequence is known to fail for the WM9707.  Thus
-	   this warning may help someone with hardware to test
-	   this code. */
-	codec->codec_write(codec, 0x72, 0x0808);
-	codec->codec_write(codec, 0x74, 0x0808);
+	/* this is known to work for the ViewSonic ViewPad 1000 */
+	codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
+	codec->codec_write(codec, AC97_GENERAL_PURPOSE, 0x8000);
+	return 0;
+}
 
+static int wolfson_init04(struct ac97_codec * codec)
+{
+	codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
+	codec->codec_write(codec, AC97_WM97XX_RMIXER_VOL, 0x0808);
+
 	// patch for DVD noise
-	codec->codec_write(codec, 0x5a, 0x0200);
+	codec->codec_write(codec, AC97_WM97XX_TEST, 0x0200);
 
 	// init vol as PCM vol
-	codec->codec_write(codec, 0x70,
+	codec->codec_write(codec, AC97_WM9704_RPCM_VOL,
 		codec->codec_read(codec, AC97_PCMOUT_VOL));
 
+	/* set rear surround volume */
 	codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
 	return 0;
 }
 
-
-static int wolfson_init03(struct ac97_codec * codec)
+/* WM9705, WM9710 */
+static int wolfson_init05(struct ac97_codec * codec)
 {
-	/* this is known to work for the ViewSonic ViewPad 1000 */
-	codec->codec_write(codec, 0x72, 0x0808);
-	codec->codec_write(codec, 0x20, 0x8000);
+	/* set front mixer volume */
+	codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
 	return 0;
 }
 
-
-static int wolfson_init04(struct ac97_codec * codec)
+/* WM9711, WM9712 */
+static int wolfson_init11(struct ac97_codec * codec)
 {
-	codec->codec_write(codec, 0x72, 0x0808);
-	codec->codec_write(codec, 0x74, 0x0808);
+	/* stop pop's during suspend/resume */
+	codec->codec_write(codec, AC97_WM97XX_TEST, codec->codec_read(codec, AC97_WM97XX_TEST) & 0xffbf);
 
-	// patch for DVD noise
-	codec->codec_write(codec, 0x5a, 0x0200);
+	/* set out3 volume */
+	codec->codec_write(codec, AC97_WM9711_OUT3VOL, 0x0808);
+	return 0;
+}
 
-	// init vol as PCM vol
-	codec->codec_write(codec, 0x70,
-		codec->codec_read(codec, AC97_PCMOUT_VOL));
+/* WM9713 */
+static int wolfson_init13(struct ac97_codec * codec)
+{
+	codec->codec_write(codec, AC97_RECORD_GAIN, 0x00a0);	
+	codec->codec_write(codec, AC97_POWER_CONTROL, 0x0000);
+	codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0xDA00);
+	codec->codec_write(codec, AC97_EXTEND_MODEM_STAT, 0x3810);
+	codec->codec_write(codec, AC97_PHONE_VOL, 0x0808);
+	codec->codec_write(codec, AC97_PCBEEP_VOL, 0x0808);
+	return 0;
+}
 
-	codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
+/* UCB1400 */
+static int phillips_ucb1400_init(struct ac97_codec * codec)
+{
+	codec->codec_write(codec,AC97_EXTENDED_STATUS,1);
+	codec->codec_write(codec, 0x6a, 0x0050);
+	codec->codec_write(codec, 0x6c, 0x0030);
 	return 0;
 }
 
-
 static int tritech_init(struct ac97_codec * codec)
 {
 	codec->codec_write(codec, 0x26, 0x0300);
@@ -954,26 +1184,115 @@
 	return 0;
 }
 
+static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
+{
+	u16 reg;
+	
+	reg = codec->codec_read(codec, AC97_SPDIF_CONTROL);
+	
+	switch(rate)
+	{
+		/* Off by default */
+		default:
+		case 0:
+			reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
+			codec->codec_write(codec, AC97_EXTENDED_STATUS, (reg & ~AC97_EA_SPDIF));
+			if(rate == 0)
+				return 0;
+			return -EINVAL;
+		case 1:
+			reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K;
+			break;
+		case 2:
+			reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K;
+			break;
+		case 3:
+			reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K;
+			break;
+	}
+	
+	reg &= ~AC97_SC_CC_MASK;
+	reg |= (mode & AUDIO_CCMASK) << 6;
+	
+	if(mode & AUDIO_DIGITAL)
+		reg |= 2;
+	if(mode & AUDIO_PRO)
+		reg |= 1;
+	if(mode & AUDIO_DRS)
+		reg |= 0x4000;
+
+	codec->codec_write(codec, AC97_SPDIF_CONTROL, reg);
+
+	reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
+	reg &= (AC97_EA_SLOT_MASK);
+	reg |= AC97_EA_VRA | AC97_EA_SPDIF | slots;
+	codec->codec_write(codec, AC97_EXTENDED_STATUS, reg);
+	
+	reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
+	if(!(reg & 0x0400))
+	{
+		codec->codec_write(codec, AC97_EXTENDED_STATUS, reg & ~ AC97_EA_SPDIF);
+		return -EINVAL;
+	}
+	return 0;
+}
+
 /*
- *	Crystal digital audio control (CS4299
+ *	Crystal digital audio control (CS4299)
  */
  
-static int crystal_digital_control(struct ac97_codec *codec, int mode)
+static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
 {
 	u16 cv;
 
-	switch(mode)
+	if(mode & AUDIO_DIGITAL)
+		return -EINVAL;
+		
+	switch(rate)
 	{
 		case 0: cv = 0x0; break;	/* SPEN off */
-		case 1: cv = 0x8004; break;	/* 48KHz digital */
-		case 2: cv = 0x8104; break;	/* 44.1KHz digital */
+		case 48000: cv = 0x8004; break;	/* 48KHz digital */
+		case 44100: cv = 0x8104; break;	/* 44.1KHz digital */
+		case 32768: 			/* 32Khz */
 		default:
-			return -1;		/* Not supported yet(eg AC3) */
+			return -EINVAL;
 	}
 	codec->codec_write(codec, 0x68, cv);
 	return 0;
 }
 
+/*
+ *	CMedia digital audio control
+ *	Needs more work.
+ */
+ 
+static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
+{
+	u16 cv;
+
+	if(mode & AUDIO_DIGITAL)
+		return -EINVAL;
+		
+	switch(rate)
+	{
+		case 0:		cv = 0x0001; break;	/* SPEN off */
+		case 48000:	cv = 0x0009; break;	/* 48KHz digital */
+		default:
+			return -EINVAL;
+	}
+	codec->codec_write(codec, 0x2A, 0x05c4);
+	codec->codec_write(codec, 0x6C, cv);
+	
+	/* Switch on mix to surround */
+	cv = codec->codec_read(codec, 0x64);
+	cv &= ~0x0200;
+	if(mode)
+		cv |= 0x0200;
+	codec->codec_write(codec, 0x64, cv);
+	return 0;
+}
+
+
 /* copied from drivers/sound/maestro.c */
 #if 0  /* there has been 1 person on the planet with a pt101 that we
         know of.  If they care, they can put this back in :) */
@@ -1111,4 +1430,67 @@
 
 EXPORT_SYMBOL(ac97_restore_state);
 
+/**
+ *	ac97_register_driver	-	register a codec helper
+ *	@driver: Driver handler
+ *
+ *	Register a handler for codecs matching the codec id. The handler
+ *	attach function is called for all present codecs and will be 
+ *	called when new codecs are discovered.
+ */
+ 
+int ac97_register_driver(struct ac97_driver *driver)
+{
+	struct list_head *l;
+	struct ac97_codec *c;
+	
+	down(&codec_sem);
+	INIT_LIST_HEAD(&driver->list);
+	list_add(&driver->list, &codec_drivers);
+	
+	list_for_each(l, &codecs)
+	{
+		c = list_entry(l, struct ac97_codec, list);
+		if(c->driver != NULL || ((c->model ^ driver->codec_id) & driver->codec_mask))
+			continue;
+		if(driver->probe(c, driver))
+			continue;
+		c->driver = driver;
+	}
+	up(&codec_sem);
+	return 0;
+}
+
+EXPORT_SYMBOL(ac97_register_driver);
+
+/**
+ *	ac97_unregister_driver	-	unregister a codec helper
+ *	@driver: Driver handler
+ *
+ *	Register a handler for codecs matching the codec id. The handler
+ *	attach function is called for all present codecs and will be 
+ *	called when new codecs are discovered.
+ */
+ 
+void ac97_unregister_driver(struct ac97_driver *driver)
+{
+	struct list_head *l;
+	struct ac97_codec *c;
+	
+	down(&codec_sem);
+	list_del_init(&driver->list);
+	
+	list_for_each(l, &codecs)
+	{
+		c = list_entry(l, struct ac97_codec, list);
+		if(c->driver == driver)
+			driver->remove(c, driver);
+		c->driver = NULL;
+	}
+	
+	up(&codec_sem);
+}
+
+EXPORT_SYMBOL(ac97_unregister_driver);
+	
 MODULE_LICENSE("GPL");
Index: drivers/sound/wm97xx.h
===================================================================
--- drivers/sound/wm97xx.h	(revision 0)
+++ linux/drivers/sound/wm97xx.h	(revision 33)
@@ -0,0 +1,111 @@
+  
+/*
+ * Register bits for Wolfson WM97xx series of codecs
+ */
+ 
+#ifndef _WM97XX_H_
+#define _WM97XX_H_
+
+#include <linux/ac97_codec.h>	/* AC97 control layer */
+
+/*
+ * WM97xx AC97 Touchscreen registers
+ */
+#define AC97_WM97XX_DIGITISER1		0x76
+#define AC97_WM97XX_DIGITISER2		0x78
+#define AC97_WM97XX_DIGITISER_RD	0x7a
+
+#define AC97_WM9713_DIG1			0x74
+#define AC97_WM9713_DIG2			AC97_WM97XX_DIGITISER1
+#define AC97_WM9713_DIG3			AC97_WM97XX_DIGITISER2
+#define AC97_WM9713_DIG_RD			AC97_WM97XX_DIGITISER_RD
+
+/*
+ * WM97xx register bits
+ */
+#define WM97XX_POLL			0x8000		/* initiate a polling measurement */
+#define WM97XX_ADCSEL_X		0x1000		/* x coord measurement */
+#define WM97XX_ADCSEL_Y		0x2000		/* y coord measurement */
+#define WM97XX_ADCSEL_PRES	0x3000		/* pressure measurement */
+#define WM97XX_COO			0x0800		/* enable coordinate mode */
+#define WM97XX_CTC			0x0400		/* enable continuous mode */
+#define WM97XX_CM_RATE_93	0x0000		/* 93.75Hz continuous rate */
+#define WM97XX_CM_RATE_187	0x0100		/* 187.5Hz continuous rate */
+#define WM97XX_CM_RATE_375	0x0200		/* 375Hz continuous rate */
+#define WM97XX_CM_RATE_750	0x0300		/* 750Hz continuous rate */
+#define WM97XX_CM_RATE_8K	0x00f0		/* 8kHz continuous rate */
+#define WM97XX_CM_RATE_12K	0x01f0		/* 12kHz continuous rate */
+#define WM97XX_CM_RATE_24K	0x02f0		/* 24kHz continuous rate */
+#define WM97XX_CM_RATE_48K	0x03f0		/* 48kHz continuous rate */
+#define WM97XX_DELAY(i)		((i << 4) & 0x00f0)	/* sample delay times */
+#define WM97XX_SLEN			0x0008		/* slot read back enable */
+#define WM97XX_SLT(i)		((i - 5) & 0x7)	/* touchpanel slot selection (5-11) */
+#define WM97XX_PRP_DETW		0x4000		/* pen detect on, digitiser off, wake up */
+#define WM97XX_PRP_DET		0x8000		/* pen detect on, digitiser off, no wake up */
+#define WM97XX_PRP_DET_DIG	0xc000		/* pen detect on, digitiser on */
+#define WM97XX_RPR			0x2000		/* wake up on pen down */
+#define WM97XX_PEN_DOWN     0x8000     /* pen is down */
+
+/* WM9712 Bits */
+#define WM9712_45W			0x1000		/* set for 5-wire touchscreen */
+#define WM9712_PDEN			0x0800		/* measure only when pen down */
+#define WM9712_WAIT			0x0200		/* wait until adc is read before next sample */
+#define WM9712_PIL			0x0100		/* current used for pressure measurement. set 400uA else 200uA */ 
+#define WM9712_MASK_HI		0x0040		/* hi on mask pin (47) stops conversions */
+#define WM9712_MASK_EDGE	0x0080		/* rising/falling edge on pin delays sample */
+#define	WM9712_MASK_SYNC	0x00c0		/* rising/falling edge on mask initiates sample */
+#define WM9712_RPU(i)		(i&0x3f)	/* internal pull up on pen detect (64k / rpu) */
+#define WM9712_ADCSEL_COMP1	0x4000		/* COMP1/AUX1 measurement (pin29) */
+#define WM9712_ADCSEL_COMP2	0x5000		/* COMP2/AUX2 measurement (pin30) */
+#define WM9712_ADCSEL_BMON	0x6000		/* BMON/AUX3 measurement (pin31) */
+#define WM9712_ADCSEL_WIPER	0x7000		/* WIPER/AUX4 measurement (pin12) */
+#define WM9712_PD(i)		(0x1 << i)  /* power management */ 
+
+/* WM9712 Registers */
+#define AC97_WM9712_POWER			0x24
+#define AC97_WM9712_REV				0x58
+
+/* WM9705 Bits */
+#define WM9705_PDEN			0x1000		/* measure only when pen is down */
+#define WM9705_PINV			0x0800		/* inverts sense of pen down output */
+#define WM9705_BSEN			0x0400		/* BUSY flag enable, pin47 is 1 when busy */
+#define WM9705_BINV			0x0200		/* invert BUSY (pin47) output */
+#define WM9705_WAIT			0x0100		/* wait until adc is read before next sample */
+#define WM9705_PIL			0x0080		/* current used for pressure measurement. set 400uA else 200uA */ 
+#define WM9705_PHIZ			0x0040		/* set PHONE and PCBEEP inputs to high impedance */
+#define WM9705_MASK_HI		0x0010		/* hi on mask stops conversions */
+#define WM9705_MASK_EDGE	0x0020		/* rising/falling edge on pin delays sample */
+#define	WM9705_MASK_SYNC	0x0030		/* rising/falling edge on mask initiates sample */
+#define WM9705_PDD(i)		(i & 0x000f) /* pen detect comparator threshold */
+#define WM9705_ADCSEL_BMON	0x4000		/* BMON measurement */
+#define WM9705_ADCSEL_AUX	0x5000		/* AUX measurement */
+#define WM9705_ADCSEL_PHONE	0x6000		/* PHONE measurement */
+#define WM9705_ADCSEL_PCBEEP 0x7000		/* PCBEEP measurement */
+
+/* WM9713 Bits */
+#define WM9713_PDPOL		0x0400		/* Pen down polarity */
+#define WM9713_POLL			0x0200		/* initiate a polling measurement */
+#define WM9713_CTC			0x0100		/* enable continuous mode */
+#define WM9713_ADCSEL_X		0x0002		/* X measurement */
+#define WM9713_ADCSEL_Y		0x0004		/* Y measurement */
+#define WM9713_ADCSEL_PRES	0x0008		/* Pressure measurement */
+#define WM9713_COO			0x0001		/* enable coordinate mode */
+
+/* AUX ADC ID's */
+#define TS_COMP1			0x0
+#define TS_COMP2			0x1
+#define TS_BMON				0x2
+#define TS_WIPER			0x3
+
+/* ID numbers */
+#define WM97XX_ID1			0x574d
+#define WM9712_ID2			0x4c12
+#define WM9705_ID2			0x4c05
+#define WM9713_ID2			0x4c13
+
+#define AC97_LINK_FRAME		21			/* time in uS for AC97 link frame */
+
+void register_touchscreen_codec(struct ac97_codec *codec);
+void unregister_touchscreen_codec(struct ac97_codec *codec);
+
+#endif
Index: drivers/sound/Makefile
===================================================================
--- drivers/sound/Makefile	(.../vendor/arm/linux)	(revision 33)
+++ linux/drivers/sound/Makefile	(.../branches/development/linux)	(revision 33)
@@ -86,6 +86,9 @@
 obj-$(CONFIG_SOUND_RME96XX)     += rme96xx.o
 obj-$(CONFIG_SOUND_BT878)	+= btaudio.o
 obj-$(CONFIG_SOUND_IT8172)	+= ite8172.o ac97_codec.o
+obj-$(CONFIG_SOUND_WM97XX)	+= ac97_plugin_wm97xx.o
+obj-$(CONFIG_SOUND_WM9713)	+= ac97_plugin_wm9713.o
+obj-$(CONFIG_SOUND_PXA_WM8753) += pxa-wm8753.o pxa-audio.o
 
 ifeq ($(CONFIG_MIDI_EMU10K1),y)
   obj-$(CONFIG_SOUND_EMU10K1)	+= sound.o
Index: drivers/sound/ac97_plugin_wm9713.c
===================================================================
--- drivers/sound/ac97_plugin_wm9713.c	(revision 0)
+++ linux/drivers/sound/ac97_plugin_wm9713.c	(revision 33)
@@ -0,0 +1,1080 @@
+/*
+ * ac97_plugin_wm9713.c  --  Touch screen driver for Wolfson WM9705 and WM9712
+ *                           AC97 Codecs.
+ *
+ * Copyright 2003 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *         parts (c) Ian Molton <spyro@f2s.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
+ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+ *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+ *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the  GNU General Public License along
+ *  with this program; if not, write  to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Notes:
+ *
+ *  Features:
+ *       - supports WM9713
+ *       - polling mode
+ *       - coordinate polling
+ *       - continuous mode
+ *       - adjustable rpu/dpp settings
+ *       - adjustable pressure current
+ *       - adjustable sample settle delay
+ *       - 4 and 5 wire touchscreens
+ *       - pen down detection
+ *       - power management
+ *
+ *  TODO:
+ *       - adjustable sample rate
+ *
+ *  Revision history
+ *    4th Feb 2004   Initial version.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/pm.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <asm/uaccess.h>        /* get_user,copy_to_user */
+#include <asm/io.h>
+
+#include "wm97xx.h"
+
+#define TS_NAME "wm9713"
+#define TS_MINOR 16
+#define WM_TS_VERSION "0.4"
+#define AC97_NUM_REG 64
+
+/*
+ * Debug
+ */
+#define PFX TS_NAME
+#define WM97XX_TS_DEBUG 0
+
+#ifdef WM97XX_TS_DEBUG
+#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg)
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set the codec sample mode.
+ *
+ * The WM9713 can sample touchscreen data in 3 different operating
+ * modes. i.e. polling, coordinate and continous.
+ *
+ * Polling:-     The driver polls the codec and issues 3 seperate commands
+ *               over the AC97 link to read X,Y and pressure.
+ * 
+ * Coordinate: - The driver polls the codec and only issues 1 command over
+ *               the AC97 link to read X,Y and pressure. This mode has
+ *               strict timing requirements and may drop samples if 
+ *               interrupted. However, it is less demanding on the AC97
+ *               link. Note: this mode requires a larger delay than polling
+ *               mode.
+ *
+ * Continuous:-  The codec automatically samples X,Y and pressure and then
+ *               sends the data over the AC97 link in slots. This is the
+ *               same method used by the codec when recording audio.
+ *
+ * Set mode = 0 for polling, 1 for coordinate and 2 for continuous.
+ *            
+ */
+MODULE_PARM(mode,"i");
+MODULE_PARM_DESC(mode, "Set WM9713 operation mode");
+static int mode = 0;
+
+/*
+ * - Set internal pull up for pen detect. 
+ * 
+ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive)
+ * i.e. pull up resistance = 64k Ohms / rpu.
+ * 
+ * Adjust this value if you are having problems with pen detect not 
+ * detecting any down events.
+ */
+MODULE_PARM(rpu,"i");
+MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
+static int rpu = 0;
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA 
+ *     pil = 1 to use 200uA and
+ *     pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure. 
+ */
+MODULE_PARM(pil,"i");
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+static int pil = 0;
+
+/*
+ * - Set five_wire = 1 to use a 5 wire touchscreen.
+ * 
+ * NOTE: Five wire mode does not allow for readback of pressure.
+ */
+MODULE_PARM(five_wire,"i");
+MODULE_PARM_DESC(five_wire, "Set 5 wire touchscreen.");
+static int five_wire = 0;
+
+/*
+ * Set adc sample delay.
+ * 
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+MODULE_PARM(delay,"i");
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+static int delay = 4;
+
+struct wm9713_pcm_device {
+	void * pcm_audio;	/* machine specific private audio data */
+	struct file_operations * pcm_fops;
+};
+
+typedef struct {
+	int is_registered:1;              /* Is the driver AC97 registered */
+	int adc_errs;                     /* sample read back errors */
+	spinlock_t lock;
+	struct ac97_codec *codec;
+#if defined(CONFIG_PROC_FS)	
+	struct proc_dir_entry *wm9713_ts_ps;
+#endif
+#if defined(CONFIG_PM)
+	struct pm_dev * pm;
+	int line_pgal:5;
+	int line_pgar:5;
+	int phone_pga:5;
+	int mic_pgal:5;
+	int mic_pgar:5;
+#endif
+	struct input_dev *idev;
+	struct completion thread_init;
+	struct completion thread_exit;
+	struct task_struct *rtask;
+	struct semaphore  sem;
+	struct wm9713_pcm_device *pcm;
+	int (*cont_mode)(void * ts);
+	int use_count;
+	int restart;
+} wm9713_ts_t;
+
+static inline void poll_delay (void);
+static int __init wm9713_ts_init_module(void);
+static inline int pendown (wm9713_ts_t *ts);
+static int wm9713_poll_read_adc (wm9713_ts_t* ts, u16 adcsel, u16* sample);
+static void init_wm9713_phy(void);
+static int wm9713_probe(struct ac97_codec *codec, struct ac97_driver *driver);
+static void wm9713_remove(struct ac97_codec *codec,  struct ac97_driver *driver);
+static void wm9713_ts_cleanup_module(void);
+static int wm9713_ts_evt_add(wm9713_ts_t* ts, u16 pressure, u16 x, u16 y);
+static int wm9713_ts_evt_release(wm9713_ts_t* ts);
+static int wm9713_machine_init(wm9713_ts_t * ts);
+
+#if defined(CONFIG_PM)
+static int wm9713_pm_event(struct pm_dev *dev, pm_request_t rqst, void *data);
+static void wm9713_suspend(void);
+static void wm9713_resume(void);
+static void wm9713_pga_save(wm9713_ts_t* ts);
+static void wm9713_pga_restore(wm9713_ts_t* ts);
+#endif
+
+/* we only support a single touchscreen */
+static struct input_dev wm9713_input;
+static wm9713_ts_t wm9713_ts;
+
+/*
+ * Machine specific set up.
+ * 
+ * This is for targets that can support the PEN down interrupt, 
+ * streaming back touch data in an AC97 slot (not slot 1) or the 
+ * WM9713 PCM codec. The streaming touch data is read back via the targets
+ * AC97 FIFO's 
+ */
+#if defined(CONFIG_ARCH_1WMMX)
+#include <asm/arch/wmmx-regs.h>
+#include <asm/arch/irqs.h>
+#include "wmmx-audio.h"
+
+#define WM9713_IRQ 	IRQ_AC97
+
+static audio_stream_t wm9713_pcm_audio_out = {
+	name:			"WM9713 PCM audio out",
+	dcmd:			(DCMD_INCSRCADDR|DCMD_FLOWTRG|DCMD_BURST16|DCMD_WIDTH2),
+	drcmr:			&DRCMRTXSS2DR,
+	dev_addr:		__PREG(SSDR_(SSP_PORT2)),
+};
+
+static audio_stream_t wm9713_pcm_audio_in = {
+	name:			"WM9713 PCM audio in",
+	dcmd:			(DCMD_INCTRGADDR|DCMD_FLOWSRC|DCMD_BURST16|DCMD_WIDTH2),
+	drcmr:			&DRCMRRXSS2DR,
+	dev_addr:		__PREG(SSDR_(SSP_PORT2)),
+};
+
+static audio_state_t wm9713_pcm = {
+	output_stream:		&wm9713_pcm_audio_out,
+	input_stream:		&wm9713_pcm_audio_in,
+	client_ioctl:		wm9713_pcm_ioctl,
+	sem:			__MUTEX_INITIALIZER(wm9713_pcm.sem),
+};
+
+/*
+ * Sample the touchscreen in continous mode for Bulverde
+ */
+static int wmmx_cont_touch(wm9713_ts_t *ts)
+{
+	u16 x, y;
+	int count = 0;
+	
+	/* currently assuming x is followed by y from the FIFO */
+	while (MISR & (1 << 2)) {
+		x = MODR & (0xffff);
+		y = MODR & (0xffff);
+		wm9713_ts_evt_add(ts, 0xffff, x, y);
+		count++;
+	}
+	return count;
+}
+
+static int wmmx_init(wm9713_ts_t * ts)
+{
+	ts->cont_mode = wmmx_cont_touch;
+	return 0;
+}
+
+#endif
+	
+/* AC97 registration info */
+static struct ac97_driver wm9713_driver = {
+	codec_id: 0x574D4C13,
+	codec_mask: 0xFFFFFFFF,
+	name: "Wolfson WM9713 Touchscreen and VDAC",
+	probe:	wm9713_probe,
+	remove: __devexit_p(wm9713_remove),
+};
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[16] = {
+	21,// 1 AC97 Link frames
+	42,// 2
+	84,// 4
+	167,// 8
+	333,// 16
+	667,// 32
+	1000,// 48
+	1333,// 64
+	2000,// 96
+	2667,// 128
+	3333,// 160
+	4000,// 192
+	4667,// 224
+	5333,// 256
+	6000,// 288
+	0 // No delay, switch matrix always on
+};
+
+static int wm9713_machine_init(wm9713_ts_t * ts)
+{
+	int ret = 0;
+#if defined(CONFIG_ARCH_1WMMX)
+	ret = wmmx_init(ts);
+#endif	
+	return ret;
+}
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(void)
+{ 
+	int pdelay = 3 * AC97_LINK_FRAME + delay_table[delay];
+	udelay (pdelay);
+}
+
+/*
+ * Wait for POLL to go low
+ */
+static inline int poll_wait(wm9713_ts_t* ts)
+{ 
+	int timeout = 5 * delay;
+	
+	while ((ts->codec->codec_read(ts->codec, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) { 
+		udelay(AC97_LINK_FRAME);
+		timeout--;
+	}
+
+	return timeout;
+}
+
+/*
+ * Read a sample from the adc in polling mode.
+ */
+static int wm9713_poll_read_adc (wm9713_ts_t* ts, u16 adcsel, u16* sample)
+{
+	u16 dig1;
+
+	/* set up digitiser */
+	dig1 = ts->codec->codec_read(ts->codec, AC97_WM9713_DIG1); 
+	ts->codec->codec_write(ts->codec, AC97_WM9713_DIG1, dig1 | adcsel |
+			       WM9713_POLL); 
+	
+	/* wait 3 AC97 time slots + delay for conversion */
+	poll_delay();
+
+	if (poll_wait(ts) > 0)
+		*sample = ts->codec->codec_read(ts->codec, AC97_WM9713_DIG_RD);
+	else {
+		ts->adc_errs++;
+		printk ("adc sample timeout\n");
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * Read a sample from the adc in coordinate mode.
+ */
+static int wm9713_coord_read_adc(wm9713_ts_t* ts, u16* x, u16* y, u16* pressure)
+{
+	u16 dig1;
+	int timeout = 5 * delay;
+
+	/* set up digitiser */
+	dig1 = ts->codec->codec_read(ts->codec, AC97_WM9713_DIG1); 
+	ts->codec->codec_write(ts->codec, AC97_WM9713_DIG1, dig1 | WM9713_ADCSEL_PRES |
+		WM9713_POLL | WM9713_COO); 
+
+	/* wait 3 AC97 time slots + delay for conversion */
+	poll_delay();
+	
+	/* read X then wait for 1 AC97 link frame + settling delay */
+	*x = ts->codec->codec_read(ts->codec, AC97_WM9713_DIG_RD);
+	udelay (AC97_LINK_FRAME + delay_table[delay]);
+
+	/* read Y */
+	*y = ts->codec->codec_read(ts->codec, AC97_WM9713_DIG_RD);
+	
+	/* wait for POLL to go low and then read pressure */
+	while ((ts->codec->codec_read(ts->codec, AC97_WM9713_DIG1) & WM9713_POLL)&& timeout) {
+			udelay(AC97_LINK_FRAME);
+			timeout--;
+	}
+	if (timeout > 0)		
+		*pressure = ts->codec->codec_read(ts->codec, AC97_WM9713_DIG_RD);
+	else {
+		ts->adc_errs++;
+		err ("adc sample timeout");
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * Sample the touchscreen in polling mode
+ */
+int wm9713_poll_touch(wm9713_ts_t *ts)
+{
+	u16 x, y, p;
+	
+	if (!wm9713_poll_read_adc(ts, WM9713_ADCSEL_X, &x))
+		return -EIO;
+	if (!wm9713_poll_read_adc(ts, WM9713_ADCSEL_Y, &y))
+		return -EIO;
+	if (pil && !five_wire) {
+		if (!wm9713_poll_read_adc(ts, WM9713_ADCSEL_PRES, &p))
+			return -EIO;
+	} else {
+		p = 0xffff;
+	}
+	
+	wm9713_ts_evt_add(ts, p, x, y);
+	return 1;
+}
+
+/*
+ * Sample the touchscreen in polling coordinate mode
+ */
+int wm9713_poll_coord_touch(wm9713_ts_t *ts)
+{
+	u16 x, y, p;
+	
+	if (wm9713_coord_read_adc(ts, &x, &y, &p)) {
+		wm9713_ts_evt_add(ts, p, x, y);
+		return 1;
+	} else
+		return -EIO;
+}
+
+/*
+ * Is the pen down ?
+ */
+static inline int pendown (wm9713_ts_t *ts)
+{
+	return ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD) 
+		           & WM97XX_PEN_DOWN;
+}
+
+#if defined(CONFIG_PROC_FS)
+static int wm9713_read_proc (char *page, char **start, off_t off,
+			     int count, int *eof, void *data)
+{
+	int len = 0, prpu, i;
+	u16 dig1, dig2, dig3, digrd, adcsel, adcsrc, slt, prp, rev;
+	unsigned long flags;
+	wm9713_ts_t* ts;
+
+	if ((ts = data) == NULL)
+		return -ENODEV;
+      
+	spin_lock_irqsave(&ts->lock, flags);
+	if (!ts->is_registered) {
+		spin_unlock_irqrestore(&ts->lock, flags);
+		len += sprintf (page+len, "No device registered\n");
+		return len;
+	}
+	
+	for (i=0; i< 0x42; i+=2)
+		len += sprintf(page+len, "%x : %x\n", i, ts->codec->codec_read(ts->codec, i));
+	
+	dig1 = ts->codec->codec_read(ts->codec, AC97_WM9713_DIG1);
+	dig2 = ts->codec->codec_read(ts->codec, AC97_WM9713_DIG2);
+	dig3 = ts->codec->codec_read(ts->codec, AC97_WM9713_DIG3);
+
+	ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER1, dig1 | WM97XX_POLL);
+	poll_delay();
+
+	digrd = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD);
+	rev = (ts->codec->codec_read(ts->codec, AC97_WM9712_REV) & 0x000c) >> 2;
+
+	spin_unlock_irqrestore(&ts->lock, flags);
+      
+	adcsel = (dig1 & 0x00fe) >> 1;
+	adcsrc = digrd & 0x7000;
+	slt = (dig2 & 0x0007);
+	prp = dig3 & 0xc000;
+	prpu = dig3 & 0x003f;
+
+	len += sprintf (page+len, "Wolfson WM9713 Version %s\n", WM_TS_VERSION);
+        
+	len += sprintf (page+len, "Settings     :\n%s%s%s%s",
+			dig1 & WM97XX_POLL ? " -sampling adc data(poll)\n" : "",
+			adcsel ==  WM9713_ADCSEL_X ? " -adc set to X coordinate\n" : "",
+			adcsel ==  WM9713_ADCSEL_Y ? " -adc set to Y coordinate\n" : "",
+			adcsel ==  WM9713_ADCSEL_PRES ? " -adc set to pressure\n" : "");
+	
+	len += sprintf (page+len, "%s%s%s%s", 
+			adcsel ==  WM9712_ADCSEL_COMP1 ? " -adc set to COMP1/AUX1\n" : "",
+			adcsel ==  WM9712_ADCSEL_COMP2 ? " -adc set to COMP2/AUX2\n" : "",
+			adcsel ==  WM9712_ADCSEL_BMON ? " -adc set to BMON\n" : "",
+			adcsel ==  WM9712_ADCSEL_WIPER ? " -adc set to WIPER\n" : "");
+      
+	len += sprintf (page+len, "%s%s%s%s%s%s",
+			dig1 & WM9713_COO ? " -coordinate sampling\n" : " -individual sampling\n",
+			dig1 & WM9713_CTC ? " -continuous mode\n" : " -polling mode\n",
+			prp == WM97XX_PRP_DET ? " -pen detect enabled, no wake up\n" : "",
+			prp == WM97XX_PRP_DETW ? " -pen detect enabled, wake up\n" : "",
+			prp == WM97XX_PRP_DET_DIG ? " -pen digitiser and pen detect enabled\n" : "",
+			dig2 & WM97XX_SLEN ? " -read back using slot " : " -read back using AC97\n");
+      
+	if ((dig2 & WM97XX_SLEN) && slt !=12)
+		len += sprintf(page+len, "%d\n", slt);
+	len += sprintf (page+len, " -adc sample delay %d uSecs\n", delay_table[(dig1 & 0x00f0) >> 4]);
+      
+	
+	if (prpu)
+		len += sprintf (page+len, " -rpu %d Ohms\n", 64000/ prpu);
+	len += sprintf (page+len, " -pressure current %s uA\n", dig2 & WM9712_PIL ? "400" : "200");
+	len += sprintf (page+len, " -using %s wire touchscreen mode", dig2 & WM9712_45W ? "5" : "4");
+      
+	len += sprintf(page+len, "\nADC data:\n%s%d\n%s%s\n",
+		       " -adc value (decimal) : ", digrd & 0x0fff,
+		       " -pen ", digrd & 0x8000 ? "Down" : "Up");
+	
+	len += sprintf (page+len, "%s%s%s%s", 
+			adcsrc ==  WM9712_ADCSEL_COMP1 ? " -adc value is COMP1/AUX1\n" : "",
+			adcsrc ==  WM9712_ADCSEL_COMP2 ? " -adc value is COMP2/AUX2\n" : "",
+			adcsrc ==  WM9712_ADCSEL_BMON ? " -adc value is BMON\n" : "",
+			adcsrc ==  WM9712_ADCSEL_WIPER ? " -adc value is WIPER\n" : "");
+     
+	len += sprintf(page+len, "\nRegisters:\n%s%x\n%s%x\n%s%x\n",
+		       " -digitiser 1    (0x76) : 0x", dig1,
+		       " -digitiser 2    (0x78) : 0x", dig2,
+		       " -digitiser read (0x7a) : 0x", digrd);
+      
+	len += sprintf(page+len, "\nErrors:\n%s%d\n",
+		       " -coordinate errors ", ts->adc_errs);
+
+	return len;
+}
+
+#endif
+
+#if defined(CONFIG_PM)
+/* WM97xx Power Management
+ * The WM9712 has extra powerdown states that are controlled in 
+ * seperate registers from the AC97 power management.
+ * We will only power down into the extra WM9712 states and leave 
+ * the AC97 power management to the sound driver.
+ */
+static int wm9713_pm_event(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+	switch(rqst) {
+		case PM_SUSPEND:
+			wm9713_suspend();
+			break;
+		case PM_RESUME:
+			wm9713_resume();
+			break;
+	}
+	return 0;
+}
+
+/*
+ * Power down the codec
+ */
+static void wm9713_suspend(void)
+{
+	wm9713_ts_t* ts = &wm9713_ts;
+	u16 reg;
+	unsigned long flags;
+      
+	spin_lock_irqsave(&ts->lock, flags);
+	if (!ts->is_registered) {
+		spin_unlock_irqrestore(&ts->lock, flags);
+		return;
+	}
+      
+	/* save and mute the PGA's */
+	wm9713_pga_save(ts);
+      
+	reg = ts->codec->codec_read(ts->codec, AC97_PHONE_VOL);
+	ts->codec->codec_write(ts->codec, AC97_PHONE_VOL, reg | 0x001f);
+      
+	reg = ts->codec->codec_read(ts->codec, AC97_MIC_VOL);
+	ts->codec->codec_write(ts->codec, AC97_MIC_VOL, reg | 0x1f1f);
+      
+	reg = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL);
+	ts->codec->codec_write(ts->codec, AC97_LINEIN_VOL, reg | 0x1f1f);
+      
+	/* LIAM need 13 spec stuff here power down, dont disable the AC link */
+	ts->codec->codec_write(ts->codec, AC97_WM9712_POWER, WM9712_PD(14) | 
+			       WM9712_PD(13) | WM9712_PD(12) | WM9712_PD(11) | 
+			       WM9712_PD(10) | WM9712_PD(9) | WM9712_PD(8) | 
+			       WM9712_PD(7) | WM9712_PD(6) | WM9712_PD(5) | 
+			       WM9712_PD(4) | WM9712_PD(3) | WM9712_PD(2) | 
+			       WM9712_PD(1) | WM9712_PD(0));
+      
+	spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+/*
+ * Power up the Codec
+ */
+static void wm9713_resume(void)
+{
+	wm9713_ts_t* ts = &wm9713_ts;
+	unsigned long flags;
+      
+	/* are we registered */
+	spin_lock_irqsave(&ts->lock, flags);
+	if (!ts->is_registered) {
+		spin_unlock_irqrestore(&ts->lock, flags);
+		return;
+	}
+
+	/* power up */
+	ts->codec->codec_write(ts->codec, AC97_WM9712_POWER, 0x0);
+      
+	/* restore PGA state */
+	wm9713_pga_restore(ts);
+      
+	spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+/* save state of wm9713 PGA's */
+static void wm9713_pga_save(wm9713_ts_t* ts)
+{
+	ts->phone_pga = ts->codec->codec_read(ts->codec, AC97_PHONE_VOL) & 0x001f;
+	ts->line_pgal = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL) & 0x1f00;
+	ts->line_pgar = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL) & 0x001f;
+	ts->mic_pgal = ts->codec->codec_read(ts->codec, AC97_MIC_VOL) & 0x1f00;
+	ts->mic_pgar = ts->codec->codec_read(ts->codec, AC97_MIC_VOL) & 0x001f;
+}
+
+/* restore state of wm9713 PGA's */
+static void wm9713_pga_restore(wm9713_ts_t* ts)
+{
+	u16 reg;
+      
+	reg = ts->codec->codec_read(ts->codec, AC97_PHONE_VOL);
+	ts->codec->codec_write(ts->codec, AC97_PHONE_VOL, reg | ts->phone_pga);
+      
+	reg = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL);
+	ts->codec->codec_write(ts->codec, AC97_LINEIN_VOL, reg | ts->line_pgar | (ts->line_pgal << 8));
+
+	reg = ts->codec->codec_read(ts->codec, AC97_MIC_VOL);
+	ts->codec->codec_write(ts->codec, AC97_MIC_VOL, reg | ts->mic_pgar | (ts->mic_pgal << 8));
+}
+
+#endif
+
+/*
+ * set up the physical settings of the device 
+ */
+static void init_wm9713_phy(void)
+{
+	u16 dig1 = 0, dig2, dig3;
+	wm9713_ts_t *ts = &wm9713_ts;
+
+	/* default values */
+	dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5);
+	dig3= WM9712_RPU(1);
+      
+	/* rpu */
+	if (rpu) {
+		dig3 &= 0xffc0;
+		dig3 |= WM9712_RPU(rpu);
+		info("setting pen detect pull-up to %d Ohms",64000 / rpu);
+	}
+      
+	/* touchpanel pressure */
+	if  (pil == 2) {
+		dig3 |= WM9712_PIL;
+		info("setting pressure measurement current to 400uA.");
+	} else if (pil) 
+		info ("setting pressure measurement current to 200uA.");
+      
+	/* five wire */
+	if (five_wire) {
+		dig3 |= WM9712_45W;
+		info("setting 5-wire touchscreen mode.");
+	}
+      
+	/* sample settling delay */
+	if (delay!=4) {
+		if (delay < 0 || delay > 15) {
+			info ("supplied delay out of range.");
+			delay = 4;
+		}
+		dig2 &= 0xff0f;
+		dig2 |= WM97XX_DELAY(delay);
+		info("setting adc sample delay to %d u Secs.", delay_table[delay]);
+	}
+      
+	/* coordinate mode */
+	if (mode == 1) {
+		dig1 |= WM9713_CTC;
+		info("using coordinate mode");
+	}
+	
+	/* continous mode */
+	if (mode == 2) {
+		if (ts->cont_mode) {
+			dig2 |= WM97XX_CM_RATE_375 | WM97XX_SLEN | WM97XX_SLT(5);
+			dig1 |= WM9713_CTC | WM9713_COO;			
+			info("using continous mode");
+			dig3 |= WM9712_PDEN;
+		} else {
+			mode = 0;
+			err("continous mode not supported on this machine");
+		}
+	}
+
+	ts->codec->codec_write(ts->codec, AC97_WM9713_DIG1, dig1);
+	ts->codec->codec_write(ts->codec, AC97_WM9713_DIG2, dig2);
+	ts->codec->codec_write(ts->codec, AC97_WM9713_DIG3, dig3);
+}
+
+
+/*
+ * Called by the audio codec initialisation to register
+ * the touchscreen driver.
+ */
+
+static int wm9713_probe(struct ac97_codec *codec, struct ac97_driver *driver)
+{
+	unsigned long flags;
+	u16 id1, id2;
+	wm9713_ts_t *ts = &wm9713_ts;
+printk("here 1\n");
+	spin_lock_irqsave(&ts->lock, flags);
+      
+	/* we only support 1 touchscreen at the moment */
+	if (ts->is_registered) {
+		spin_unlock_irqrestore(&ts->lock, flags);
+		printk("here 2\n");
+		return -1;
+	}
+      
+	/* 
+	 * We can only use a WM9713 that has been *first* initialised
+	 * by the AC97 audio driver. This is because we have to use the audio 
+	 * drivers codec read() and write() functions to sample the touchscreen
+	 *
+	 * If an initialsed WM9713 is found then get the codec read and write 
+	 * functions. 
+	 */
+      
+	/* test for a WM9713 */
+	id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
+	id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
+	if (id1 == WM97XX_ID1 && id2 == WM9713_ID2) {
+		info("registered a WM9713");
+	} else {
+		err("could not find a WM97xx codec. Found a 0x%4x:0x%4x instead",
+		    id1, id2);
+		spin_unlock_irqrestore(&ts->lock, flags);
+		return -1;
+	}
+      
+	/* set up AC97 codec interface */
+	ts->codec = codec;
+	codec->driver_private = (void*)&ts;
+      
+	/* set up physical characteristics */
+	init_wm9713_phy();
+      
+	ts->is_registered = 1;
+	spin_unlock_irqrestore(&ts->lock, flags);
+	return 0;
+}
+
+/*
+ * Called by ac97_codec when it is unloaded. 
+ */
+static void wm9713_remove(struct ac97_codec *codec, struct ac97_driver *driver)
+{
+	unsigned long flags;
+	u16 dig2, dig3;
+	wm9713_ts_t *ts = codec->driver_private;
+	
+    dbg("Unloading codec\n"); 
+	spin_lock_irqsave(&ts->lock, flags);
+      
+	/* check that are registered */
+	if (!ts->is_registered) {
+		err("double unregister");
+		spin_unlock_irqrestore(&ts->lock, flags);
+		return;
+	}
+      
+	ts->is_registered = 0;
+      
+	/* restore default digitiser values */
+	dig2 = WM97XX_DELAY(4) | WM97XX_SLT(6);
+	dig3 = WM9712_RPU(1);
+      
+	codec->codec_write(codec, AC97_WM9713_DIG2, dig2);
+	codec->codec_write(codec, AC97_WM9713_DIG3, dig3); 
+	ts->codec = NULL;
+      
+	spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+/*
+ * add a touch event
+ */
+static int wm9713_ts_evt_add(wm9713_ts_t* ts, u16 pressure, u16 x, u16 y)
+{
+	/* add event and remove adc src bits */
+	printk("x %d y %d p %d\n", x&0xfff,y&0xfff,pressure);
+	input_report_abs(ts->idev, ABS_X, x & 0xfff);
+	input_report_abs(ts->idev, ABS_Y, y & 0xfff);
+	input_report_abs(ts->idev, ABS_PRESSURE, pressure & 0xfff);
+	return 0;
+}
+
+/*
+ * add a pen up event
+ */
+static int wm9713_ts_evt_release(wm9713_ts_t* ts)
+{
+//	dbg("release the stylus.\n");
+	input_report_abs(ts->idev, ABS_PRESSURE, 0);
+	return 0;
+}
+
+/*
+ * The touchscreen sample reader thread
+ */
+static int wm9713_thread(void *_ts)
+{
+	wm9713_ts_t *ts = (wm9713_ts_t *)_ts;
+	struct task_struct *tsk = current;
+	int valid = 0;
+
+	ts->rtask = tsk;
+
+	/* set up thread context */
+	daemonize();
+	reparent_to_init();
+	strcpy(tsk->comm, "ktsd");
+	tsk->tty = NULL;
+
+	/* we will die when we receive SIGKILL */
+	spin_lock_irq(&tsk->sigmask_lock);
+	siginitsetinv(&tsk->blocked, sigmask(SIGKILL));
+	recalc_sigpending(tsk);
+	spin_unlock_irq(&tsk->sigmask_lock);
+
+	/* init is complete */
+	complete(&ts->thread_init);
+
+	/* touch reader loop */
+	for (;;) {
+		ts->restart = 0;
+		if(signal_pending(tsk))
+			break;
+
+		if(pendown(ts)) {
+			switch (mode) {
+				case 0:
+					wm9713_poll_touch(ts);
+					valid = 1;
+					break;
+				case 1:
+					wm9713_poll_coord_touch(ts);
+					valid = 1;
+					break;
+				case 2:
+					ts->cont_mode(ts);
+					valid = 1;
+					break;
+			}
+		} else {
+			if (valid) {
+				valid = 0;
+				wm9713_ts_evt_release(ts);
+			}
+		}
+
+		set_task_state(tsk, TASK_INTERRUPTIBLE);
+		if (HZ >= 100)
+			schedule_timeout(HZ/100);
+		else
+			schedule_timeout(1);
+	}
+	ts->rtask = NULL;
+	complete_and_exit(&ts->thread_exit, 0);
+
+	return 0;
+}
+
+/*
+ * Start the touchscreen thread and 
+ * the touch digitiser.
+ */
+static int wm9713_ts_input_open(struct input_dev *idev)
+{
+	wm9713_ts_t *ts = (wm9713_ts_t *) &wm9713_ts;
+	u32 flags;
+	int ret, val;
+
+	spin_lock_irqsave( &ts->lock, flags );
+	if ( ts->use_count++ == 0 ) {
+
+		/* start touchscreen thread */
+		ts->idev = idev;
+		init_completion(&ts->thread_init);
+		ret = kernel_thread(wm9713_thread, ts, 0);
+		if (ret >= 0)
+			wait_for_completion(&ts->thread_init); 
+	
+		/* start digitiser */
+		val = ts->codec->codec_read(ts->codec, AC97_EXTENDED_MODEM_ID);
+		ts->codec->codec_write(ts->codec, AC97_EXTENDED_MODEM_ID,
+								   val & 0x7fff);
+		val = ts->codec->codec_read(ts->codec, AC97_WM9713_DIG3);
+		ts->codec->codec_write(ts->codec, AC97_WM9713_DIG3,
+								   val | WM97XX_PRP_DET_DIG);
+	}
+	spin_unlock_irqrestore( &ts->lock, flags );
+	return 0;
+}
+
+/*
+ * Kill the touchscreen thread and stop
+ * the touch digitiser.
+ */
+static void wm9713_ts_input_close(struct input_dev *idev)
+{
+	wm9713_ts_t *ts = (wm9713_ts_t *) &wm9713_ts;
+	u32 flags;
+	int val;
+
+	spin_lock_irqsave(&ts->lock, flags);
+	if (--ts->use_count == 0) {
+		/* kill thread */
+		init_completion(&ts->thread_exit);
+		if (ts->rtask) {
+			send_sig(SIGKILL, ts->rtask, 1);
+			wait_for_completion(&ts->thread_exit);
+		}
+
+		/* stop digitiser */
+		val = ts->codec->codec_read(ts->codec, AC97_WM9713_DIG3);
+		ts->codec->codec_write(ts->codec, AC97_WM9713_DIG3,
+				       val & ~WM97XX_PRP_DET_DIG);
+		val = ts->codec->codec_read(ts->codec, AC97_EXTENDED_MODEM_ID);
+		ts->codec->codec_write(ts->codec, AC97_EXTENDED_MODEM_ID,
+								   val | 0x8000);
+	}
+	spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+static int __init wm9713_ts_init_module(void)
+{
+	wm9713_ts_t* ts = &wm9713_ts;
+	char proc_str[64];
+      
+	info("Wolfson WM9713 Touchscreen and PCM DAC Controller");
+	info("Version %s  liam.girdwood@wolfsonmicro.com", WM_TS_VERSION);
+      
+	memset(ts, 0, sizeof(wm9713_ts_t));
+	wm9713_machine_init(&wm9713_ts);
+	
+	/* tell input system what we events we accept and register */
+	wm9713_input.name = "wm9713 touchscreen";
+	wm9713_input.open = wm9713_ts_input_open;
+	wm9713_input.close = wm9713_ts_input_close;
+	__set_bit(EV_ABS, wm9713_input.evbit);
+	__set_bit(ABS_X, wm9713_input.absbit);
+	__set_bit(ABS_Y, wm9713_input.absbit);
+	__set_bit(ABS_PRESSURE, wm9713_input.absbit);
+	input_register_device(&wm9713_input);
+   
+	spin_lock_init(&ts->lock);
+	init_MUTEX(&ts->sem);
+	
+	/* register with the AC97 layer */
+	ac97_register_driver(&wm9713_driver);
+      
+#if defined(CONFIG_PROC_FS)
+	sprintf(proc_str, "driver/%s", TS_NAME);
+	if ((ts->wm9713_ts_ps = create_proc_read_entry (proc_str, 0, NULL,
+							wm9713_read_proc, ts)) == 0)
+		err("could not register proc interface /proc/%s", proc_str);
+#endif
+#if defined(CONFIG_PM)
+	if ((ts->pm = pm_register(PM_UNKNOWN_DEV, PM_SYS_UNKNOWN, wm9713_pm_event)) == 0)
+		err("could not register with power management");
+#endif
+	
+	return 0;
+}
+
+static void wm9713_ts_cleanup_module(void)
+{
+#if defined(CONFIG_PM)
+	pm_unregister (wm9713_ts.pm);
+#endif
+	input_unregister_device(&wm9713_input);
+}
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("WM9713 Touch Screen / PCM DAC Driver");
+MODULE_LICENSE("GPL");
+
+module_init(wm9713_ts_init_module);
+module_exit(wm9713_ts_cleanup_module);
+
+#ifndef MODULE
+
+static int __init wm9713_ts_setup(char *options)
+{
+	char *this_opt = options;
+
+	if (!options || !*options)
+		return 0;
+
+	/* parse the options and check for out of range values */
+	for(this_opt=strsep(options, ",");
+	    this_opt; this_opt=strsep(NULL, ",")) {
+		if (!strncmp(this_opt, "pil:", 4)) {
+			this_opt+=4;
+			pil = simple_strtol(this_opt, NULL, 0);
+			if (pil < 0 || pil > 2)
+				pil = 0;
+			continue;
+		}
+		if (!strncmp(this_opt, "rpu:", 4)) {
+			this_opt+=4;
+			rpu = simple_strtol(this_opt, NULL, 0);
+			if (rpu < 0 || rpu > 31)
+				rpu = 0;
+			continue;
+		}
+		if (!strncmp(this_opt, "delay:", 6)) {
+			this_opt+=6;
+			delay = simple_strtol(this_opt, NULL, 0);
+			if (delay < 0 || delay > 15)
+				delay = 4;
+			continue;
+		}
+		if (!strncmp(this_opt, "five_wire:", 10)) {
+			this_opt+=10;
+			five_wire = simple_strtol(this_opt, NULL, 0);
+			if (five_wire < 0 || five_wire > 1)
+				five_wire = 0;
+			continue;
+		}
+		if (!strncmp(this_opt, "mode:", 5)) {
+			this_opt+=5;
+			mode = simple_strtol(this_opt, NULL, 0);
+			if (mode < 0 || mode > 2)
+				mode = 0;
+			continue;
+		}
+	}
+	return 1;
+}
+
+__setup("wm9713_ts=", wm9713_ts_setup);
+
+#endif /* MODULE */
Index: drivers/sound/pxa-ac97.c
===================================================================
--- drivers/sound/pxa-ac97.c	(.../vendor/arm/linux)	(revision 33)
+++ linux/drivers/sound/pxa-ac97.c	(.../branches/development/linux)	(revision 33)
@@ -8,6 +8,13 @@
  *  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.
+ *
+ *  AC97 GPIO Changes:-
+ *   In order to read/write codec GPIO bits using AC97 link slot 12,
+ *   all IO to AC97_GPIO_STATUS must be via the Xscale modem codec
+ *   address space. This is currently broken in PXA270 A,B and C0 
+ *   silicon, so we use the slower slot 1/2 method. 
+ *     Liam Girdwood <liam.girdwood@wolfsonmicro.com>
  */
 
 #include <linux/init.h>
@@ -72,13 +79,7 @@
        ret = ac97_probe_codec(codec);
        if (ret != 1)
            printk(KERN_INFO "After resume AC97codec fail.\n");
-       /* little hack for UCB1400 (should be moved elsewhere) */
-       pxa_ac97_write(codec,AC97_EXTENDED_STATUS,1);
 
-       /* pxa_ac97_write(&pxa_ac97_codec, 0x6a, 0x1ff7); */
-       pxa_ac97_write(codec, 0x6a, 0x0050);
-       pxa_ac97_write(codec, 0x6c, 0x0030);
-
    }
     if (rqst == PM_SUSPEND) {
    /*
@@ -103,7 +104,20 @@
 
 	down(&CAR_mutex);
 	if (!(CAR & CAR_CAIP)) {
-		volatile u32 *reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1);
+		volatile u32 *reg_addr; 
+		
+#if 0		
+		/* if we are reading the GPIO status then this is cached 
+		 * in hardware so we don't need to read over the link.
+		 * NOTE: This is broken in PXA27x A,B and C0 silicon
+		 */ 		
+		if (reg == AC97_GPIO_STATUS) {
+			reg_addr = (u32 *)&PMC_REG_BASE + (reg >> 1);
+			return *reg_addr;
+		} 	
+#endif
+		
+		reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1);
 
 		waitingForMask=GSR_SDONE;
 
@@ -132,8 +146,22 @@
 {
 	down(&CAR_mutex);
 	if (!(CAR & CAR_CAIP)) {
-		volatile u32 *reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1);
+		volatile u32 *reg_addr;
 
+#if 0
+		/* if we are writing to the codec GPIO using slot 12 
+		 * then we have to write to the modem register space
+		 * NOTE: This is broken in PXA27x A,B and C0 silicon
+		 */ 		
+		if (reg == AC97_GPIO_STATUS) {
+			reg_addr = (u32 *)&PMC_REG_BASE + (reg >> 1);
+			*reg_addr = val;
+			return;
+		}
+#endif
+		
+		reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1);
+
 		waitingForMask=GSR_CDONE;
 		init_completion(&CAR_completion);
 		*reg_addr = val;
@@ -174,6 +202,13 @@
 			return ret;
 
 		CKEN |= CKEN2_AC97; 
+#if defined(CONFIG_ARCH_MAINSTONE)
+		/* enable MSII daughter card clock */
+		GPDR(1) |= (1 << 13);
+		GAFR1_L &= 0xf3ffffff;
+		GAFR1_L |= (0x01 << 26);
+#endif		
+		
 		set_GPIO_mode(GPIO31_SYNC_AC97_MD);
 		set_GPIO_mode(GPIO30_SDATA_OUT_AC97_MD);
 		set_GPIO_mode(GPIO28_BITCLK_AC97_MD);
@@ -188,9 +223,20 @@
 		MST_MSCWR2 &= ~MSCWR2_LINE1;
 #endif
 
+#if defined(CONFIG_SOUND_AC97_WARM)
+#if defined(CONFIG_CPU_BULVERDE)
+		/* warm reset broken on Bulverde */
+		GAFR3_U &= ~0xC;
+		GPSR3 = 0x20000 | GPLR3;
+#endif			
+		GCR |= GCR_COLD_RST|GCR_CDONE_IE|GCR_SDONE_IE;
+		udelay(10);
+		GCR |= GCR_WARM_RST|GCR_CDONE_IE|GCR_SDONE_IE;
+#else
 		GCR = 0;
 		udelay(10);
 		GCR = GCR_COLD_RST|GCR_CDONE_IE|GCR_SDONE_IE;
+#endif
 		while (!(GSR & GSR_PCR)) {
 			schedule();
 		}
@@ -202,12 +248,6 @@
 			CKEN &= ~CKEN2_AC97; 
 			return ret;
 		}
-
-		// need little hack for UCB1400 (should be moved elsewhere)
-		pxa_ac97_write(&pxa_ac97_codec,AC97_EXTENDED_STATUS,1);
-		//pxa_ac97_write(&pxa_ac97_codec, 0x6a, 0x1ff7);
-		pxa_ac97_write(&pxa_ac97_codec, 0x6a, 0x0050);
-		pxa_ac97_write(&pxa_ac97_codec, 0x6c, 0x0030);
 	}
 
 	pxa_ac97_refcount++;
@@ -425,4 +465,3 @@
 
 module_init(pxa_ac97_init);
 module_exit(pxa_ac97_exit);
-
