summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorMarcin Juszkiewicz <hrw@openembedded.org>2007-10-30 15:21:19 +0000
committerMarcin Juszkiewicz <hrw@openembedded.org>2007-10-30 15:21:19 +0000
commita43e2b53a23bbd6955209a01a8388508f9205857 (patch)
treed9c5818b83a56f33baf0cb5d5a3bb34c49365845 /packages
parent18f5c86801714259eaaa1e1a81565984f58ecd14 (diff)
linux-rp: update Tosa patchset in 2.6.22 and 2.6.23 - close #3233
USB Host patches are not updated
Diffstat (limited to 'packages')
-rw-r--r--packages/linux/linux-rp-2.6.22/defconfig-tosa28
-rw-r--r--packages/linux/linux-rp-2.6.22/sharpsl-pm-postresume-r1.patch30
-rw-r--r--packages/linux/linux-rp-2.6.22/tmio-fb-r6-fix-r0.patch45
-rw-r--r--packages/linux/linux-rp-2.6.22/tmio-nand-r8.patch594
-rw-r--r--packages/linux/linux-rp-2.6.22/tmio-ohci-r6.patch929
-rw-r--r--packages/linux/linux-rp-2.6.22/tmio-tc6393-r8.patch800
-rw-r--r--packages/linux/linux-rp-2.6.22/tosa-keyboard-r19.patch514
-rw-r--r--packages/linux/linux-rp-2.6.22/tosa-lcdnoise-r1-fix-r0.patch135
-rw-r--r--packages/linux/linux-rp-2.6.22/tosa-lcdnoise-r1.patch158
-rw-r--r--packages/linux/linux-rp-2.6.22/tosa-power-r18-fix-r0.patch59
-rw-r--r--packages/linux/linux-rp-2.6.22/tosa-pxaac97-r6-fix-r0.patch29
-rw-r--r--packages/linux/linux-rp-2.6.22/tosa-tmio-lcd-r10-fix-r0.patch35
-rw-r--r--packages/linux/linux-rp-2.6.22/tosa-tmio-lcd-r10.patch472
-rw-r--r--packages/linux/linux-rp-2.6.22/usb-ohci-hooks-r2.patch180
-rw-r--r--packages/linux/linux-rp-2.6.22/wm9712-reset-loop-r2.patch44
-rw-r--r--packages/linux/linux-rp-2.6.22/wm9712-suspend-cold-res-r2.patch16
-rw-r--r--packages/linux/linux-rp-2.6.22/wm97xx-lg13-r0-fix-r0.patch128
-rw-r--r--packages/linux/linux-rp-2.6.22/wm97xx-lg13-r0.patch2899
-rw-r--r--packages/linux/linux-rp-2.6.23/defconfig-tosa50
-rw-r--r--packages/linux/linux-rp-2.6.23/sharpsl-pm-postresume-r1.patch60
-rw-r--r--packages/linux/linux-rp-2.6.23/tmio-fb-r6-fix-r0.patch90
-rw-r--r--packages/linux/linux-rp-2.6.23/tmio-nand-r8.patch1188
-rw-r--r--packages/linux/linux-rp-2.6.23/tmio-tc6393-r8.patch1600
-rw-r--r--packages/linux/linux-rp-2.6.23/tosa-bluetooth-r8.patch388
-rw-r--r--packages/linux/linux-rp-2.6.23/tosa-keyboard-r19.patch1028
-rw-r--r--packages/linux/linux-rp-2.6.23/tosa-lcdnoise-r1-fix-r0.patch270
-rw-r--r--packages/linux/linux-rp-2.6.23/tosa-lcdnoise-r1.patch316
-rw-r--r--packages/linux/linux-rp-2.6.23/tosa-power-r18-fix-r0.patch118
-rw-r--r--packages/linux/linux-rp-2.6.23/tosa-power-r18.patch1382
-rw-r--r--packages/linux/linux-rp-2.6.23/tosa-pxaac97-r6-fix-r0.patch58
-rw-r--r--packages/linux/linux-rp-2.6.23/tosa-tmio-lcd-r10-fix-r0.patch70
-rw-r--r--packages/linux/linux-rp-2.6.23/tosa-tmio-lcd-r10.patch928
-rw-r--r--packages/linux/linux-rp-2.6.23/wm9712-reset-loop-r2.patch88
-rw-r--r--packages/linux/linux-rp-2.6.23/wm9712-suspend-cold-res-r2.patch32
-rw-r--r--packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0-fix-r0.patch256
-rw-r--r--packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0.patch5798
-rw-r--r--packages/linux/linux-rp_2.6.22.bb24
-rw-r--r--packages/linux/linux-rp_2.6.23.bb28
38 files changed, 20815 insertions, 52 deletions
diff --git a/packages/linux/linux-rp-2.6.22/defconfig-tosa b/packages/linux/linux-rp-2.6.22/defconfig-tosa
index 74fc076608..ef44385067 100644
--- a/packages/linux/linux-rp-2.6.22/defconfig-tosa
+++ b/packages/linux/linux-rp-2.6.22/defconfig-tosa
@@ -262,6 +262,9 @@ CONFIG_SYN_COOKIES=y
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
# CONFIG_INET_TUNNEL is not set
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
CONFIG_INET_DIAG=m
CONFIG_INET_TCP_DIAG=m
# CONFIG_TCP_CONG_ADVANCED is not set
@@ -792,9 +795,11 @@ CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_WM97XX=y
# CONFIG_TOUCHSCREEN_WM9705 is not set
CONFIG_TOUCHSCREEN_WM9712=y
+CONFIG_TOUCHSCREEN_TOSA=y
# CONFIG_TOUCHSCREEN_WM9713 is not set
# CONFIG_TOUCHSCREEN_WM97XX_PXA is not set
-# CONFIG_INPUT_MISC is not set
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=m
#
# Hardware I/O ports
@@ -1040,7 +1045,7 @@ CONFIG_SND_SUPPORT_OLD_API=y
#
# Generic devices
#
-CONFIG_SND_AC97_BUS=y
+CONFIG_AC97_BUS=y
CONFIG_SND_DUMMY=m
# CONFIG_SND_MTPAV is not set
# CONFIG_SND_SERIAL_U16550 is not set
@@ -1072,15 +1077,15 @@ CONFIG_SND_SOC=y
#
# SoC Audio for the Intel PXA2xx
#
-CONFIG_SND_PXA2xx_SOC=y
-CONFIG_SND_PXA2xx_SOC_AC97=y
-# CONFIG_SND_PXA2xx_SOC_MAINSTONE is not set
-# CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM8753 is not set
-# CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM9713 is not set
-# CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM9712 is not set
-# CONFIG_SND_PXA2xx_SOC_CORGI is not set
-# CONFIG_SND_PXA2xx_SOC_SPITZ is not set
-CONFIG_SND_PXA2xx_SOC_TOSA=y
+CONFIG_SND_PXA2XX_SOC=y
+CONFIG_SND_PXA2XX_SOC_AC97=y
+# CONFIG_SND_PXA2XX_SOC_MAINSTONE is not set
+# CONFIG_SND_PXA2XX_SOC_MAINSTONE_WM8753 is not set
+# CONFIG_SND_PXA2XX_SOC_MAINSTONE_WM9713 is not set
+# CONFIG_SND_PXA2XX_SOC_MAINSTONE_WM9712 is not set
+# CONFIG_SND_PXA2XX_SOC_CORGI is not set
+# CONFIG_SND_PXA2XX_SOC_SPITZ is not set
+CONFIG_SND_PXA2XX_SOC_TOSA=y
#
# Soc Codecs
@@ -1572,6 +1577,7 @@ CONFIG_DEBUG_ERRORS=y
# Cryptographic options
#
CONFIG_CRYPTO=y
+CONFIG_CRYPTO_ALGAPI=y
CONFIG_CRYPTO_HMAC=y
CONFIG_CRYPTO_NULL=m
CONFIG_CRYPTO_MD4=m
diff --git a/packages/linux/linux-rp-2.6.22/sharpsl-pm-postresume-r1.patch b/packages/linux/linux-rp-2.6.22/sharpsl-pm-postresume-r1.patch
new file mode 100644
index 0000000000..409daf03e6
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/sharpsl-pm-postresume-r1.patch
@@ -0,0 +1,30 @@
+ arch/arm/common/sharpsl_pm.c | 3 +++
+ include/asm-arm/hardware/sharpsl_pm.h | 1 +
+ 2 files changed, 4 insertions(+)
+
+Index: git/include/asm-arm/hardware/sharpsl_pm.h
+===================================================================
+--- git.orig/include/asm-arm/hardware/sharpsl_pm.h 2006-10-31 16:09:33.000000000 +0000
++++ git/include/asm-arm/hardware/sharpsl_pm.h 2006-11-07 22:08:41.000000000 +0000
+@@ -26,6 +26,7 @@ struct sharpsl_charger_machinfo {
+ void (*presuspend)(void);
+ void (*postsuspend)(void);
+ void (*earlyresume)(void);
++ void (*postresume)(void);
+ unsigned long (*read_devdata)(int);
+ #define SHARPSL_BATT_VOLT 1
+ #define SHARPSL_BATT_TEMP 2
+Index: git/arch/arm/common/sharpsl_pm.c
+===================================================================
+--- git.orig/arch/arm/common/sharpsl_pm.c 2006-11-07 22:03:48.000000000 +0000
++++ git/arch/arm/common/sharpsl_pm.c 2006-11-07 22:04:20.000000000 +0000
+@@ -584,6 +584,9 @@ static int corgi_pxa_pm_enter(suspend_st
+ if (sharpsl_pm.machinfo->earlyresume)
+ sharpsl_pm.machinfo->earlyresume();
+
++ if (sharpsl_pm.machinfo->postresume)
++ sharpsl_pm.machinfo->postresume();
++
+ dev_dbg(sharpsl_pm.dev, "SharpSL resuming...\n");
+
+ return 0;
diff --git a/packages/linux/linux-rp-2.6.22/tmio-fb-r6-fix-r0.patch b/packages/linux/linux-rp-2.6.22/tmio-fb-r6-fix-r0.patch
new file mode 100644
index 0000000000..eab57c50e8
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/tmio-fb-r6-fix-r0.patch
@@ -0,0 +1,45 @@
+From 302745ce6f3bab7b1a97de32339405ae3fd8eacb Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 00:05:54 +0400
+Subject: [PATCH] tmio-fb-r6.patch fixes
+
+---
+ drivers/video/tmiofb.c | 8 ++++----
+ 1 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c
+index 10b0105..72eb76c 100644
+--- a/drivers/video/tmiofb.c
++++ b/drivers/video/tmiofb.c
+@@ -463,8 +463,8 @@ static int tmiofb_vblank (struct fb_info *fbi, struct fb_vblank *vblank)
+ #define FBIO_TMIO_ACC_WRITE 0x7C639300
+ #define FBIO_TMIO_ACC_SYNC 0x7C639301
+
+-static int tmiofb_ioctl (struct inode *inode, struct file *file,
+- unsigned int cmd, unsigned long arg, struct fb_info *fbi)
++static int tmiofb_ioctl (struct fb_info *fbi,
++ unsigned int cmd, unsigned long arg)
+ {
+ switch (cmd) {
+ case FBIOGET_VBLANK: {
+@@ -677,7 +677,7 @@ static struct fb_ops tmiofb_ops_acc = {
+ * 2000 0002 display start
+ * 2000 0004 line number match (0x1ff mask???)
+ */
+-static irqreturn_t tmiofb_irq (int irq, void *__fbi, struct pt_regs *r)
++static irqreturn_t tmiofb_irq (int irq, void *__fbi)
+ {
+ struct fb_info* fbi = __fbi;
+ struct tmiofb_par* par = fbi->par;
+@@ -762,7 +762,7 @@ static int __init tmiofb_probe (struct device *dev)
+ }
+ fbi->screen_base = par->sram;
+
+- retval = request_irq (irq->start, &tmiofb_irq, SA_INTERRUPT,
++ retval = request_irq (irq->start, &tmiofb_irq, IRQF_DISABLED,
+ TMIO_NAME_LCD, fbi);
+ if (retval)
+ goto err_request_irq;
+--
+1.4.4.4
+
diff --git a/packages/linux/linux-rp-2.6.22/tmio-nand-r8.patch b/packages/linux/linux-rp-2.6.22/tmio-nand-r8.patch
new file mode 100644
index 0000000000..a71fd114a8
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/tmio-nand-r8.patch
@@ -0,0 +1,594 @@
+ drivers/mtd/nand/Kconfig | 7 +
+ drivers/mtd/nand/Makefile | 1 +
+ drivers/mtd/nand/tmio.c | 554 +++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 562 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
+index f1d60b6..b9c8796 100644
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -69,6 +69,13 @@ config MTD_NAND_AMS_DELTA
+ help
+ Support for NAND flash on Amstrad E3 (Delta).
+
++config MTD_NAND_TMIO
++ tristate "NAND Flash device on Toshiba Mobile IO Controller"
++ depends on MTD_NAND && TOSHIBA_TC6393XB
++ help
++ Support for NAND flash connected to a Toshiba Mobile IO
++ Controller in some PDAs, including the Sharp SL6000x.
++
+ config MTD_NAND_TOTO
+ tristate "NAND Flash device on TOTO board"
+ depends on ARCH_OMAP && BROKEN
+diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
+index edba1db..64f24e1 100644
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -27,5 +27,6 @@ obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o
+ obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
+ obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
+ obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
++obj-$(CONFIG_MTD_NAND_TMIO) += tmio.o
+
+ nand-objs := nand_base.o nand_bbt.o
+diff --git a/drivers/mtd/nand/tmio.c b/drivers/mtd/nand/tmio.c
+new file mode 100644
+index 0000000..d196553
+--- /dev/null
++++ b/drivers/mtd/nand/tmio.c
+@@ -0,0 +1,554 @@
++/*
++ * A device driver for NAND flash connected to a Toshiba Mobile IO
++ * controller. This is known to work with the following variants:
++ * TC6393XB revision 3
++ *
++ * Maintainer: Chris Humbert <mahadri+mtd@drigon.com>
++ *
++ * Copyright (C) 2005 Chris Humbert
++ * Copyright (C) 2005 Dirk Opfer
++ * Copyright (C) 2004 SHARP
++ * Copyright (C) 2002 Lineo Japan, Inc.
++ * Copyright (C) Ian Molton and Sebastian Carlier
++ *
++ * Based on Sharp's NAND driver, sharp_sl_tc6393.c
++ *
++ * 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.
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/delay.h>
++#include <linux/wait.h>
++#include <linux/ioport.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++#include <asm/io.h>
++#include <asm/hardware/tmio.h>
++
++#include <linux/interrupt.h>
++
++#define mtd_printk(level, mtd, format, arg...) \
++ printk (level "%s: " format, mtd->name, ## arg)
++#define mtd_warn(mtd, format, arg...) \
++ mtd_printk (KERN_WARNING, mtd, format, ## arg)
++
++/*--------------------------------------------------------------------------*/
++
++/* tmio_nfcr.mode Register Command List */
++#define FCR_MODE_DATA 0x94 // Data Data_Mode
++#define FCR_MODE_COMMAND 0x95 // Data Command_Mode
++#define FCR_MODE_ADDRESS 0x96 // Data Address_Mode
++
++#define FCR_MODE_HWECC_CALC 0xB4 // HW-ECC Data
++#define FCR_MODE_HWECC_RESULT 0xD4 // HW-ECC Calculation Result Read_Mode
++#define FCR_MODE_HWECC_RESET 0xF4 // HW-ECC Reset
++
++#define FCR_MODE_POWER_ON 0x0C // Power Supply ON to SSFDC card
++#define FCR_MODE_POWER_OFF 0x08 // Power Supply OFF to SSFDC card
++
++#define FCR_MODE_LED_OFF 0x00 // LED OFF
++#define FCR_MODE_LED_ON 0x04 // LED ON
++
++#define FCR_MODE_EJECT_ON 0x68 // Ejection Demand from Penguin is Advanced
++#define FCR_MODE_EJECT_OFF 0x08 // Ejection Demand from Penguin is Not Advanced
++
++#define FCR_MODE_LOCK 0x6C // Operates By Lock_Mode. Ejection Switch is Invalid
++#define FCR_MODE_UNLOCK 0x0C // Operates By UnLock_Mode.Ejection Switch is Effective
++
++#define FCR_MODE_CONTROLLER_ID 0x40 // Controller ID Read
++#define FCR_MODE_STANDBY 0x00 // SSFDC card Changes Standby State
++
++#define FCR_MODE_WE 0x80
++#define FCR_MODE_ECC1 0x40
++#define FCR_MODE_ECC0 0x20
++#define FCR_MODE_CE 0x10
++#define FCR_MODE_PCNT1 0x08
++#define FCR_MODE_PCNT0 0x04
++#define FCR_MODE_ALE 0x02
++#define FCR_MODE_CLE 0x01
++
++#define FCR_STATUS_BUSY 0x80
++
++/*
++ * NAND Flash Host Controller Configuration Register
++ */
++struct tmio_nfhccr {
++ u8 x00[4];
++ u16 command; /* 0x04 Command */
++ u8 x01[0x0a];
++ u16 base[2]; /* 0x10 NAND Flash Control Reg Base Addr*/
++ u8 x02[0x29];
++ u8 intp; /* 0x3d Interrupt Pin */
++ u8 x03[0x0a];
++ u8 inte; /* 0x48 Interrupt Enable */
++ u8 x04;
++ u8 ec; /* 0x4a Event Control */
++ u8 x05;
++ u8 icc; /* 0x4c Internal Clock Control */
++ u8 x06[0x0e];
++ u8 eccc; /* 0x5b ECC Control */
++ u8 x07[4];
++ u8 nftc; /* 0x60 NAND Flash Transaction Control */
++ u8 nfm; /* 0x61 NAND Flash Monitor */
++ u8 nfpsc; /* 0x62 NAND Flash Power Supply Control */
++ u8 nfdc; /* 0x63 NAND Flash Detect Control */
++ u8 x08[0x9c];
++} __attribute__ ((packed));
++
++/*
++ * NAND Flash Control Register
++ */
++struct tmio_nfcr {
++union {
++ u8 u8; /* 0x00 Data Register */
++ u16 u16;
++ u32 u32;
++} __attribute__ ((packed));
++ u8 mode; /* 0x04 Mode Register */
++ u8 status; /* 0x05 Status Register */
++ u8 isr; /* 0x06 Interrupt Status Register */
++ u8 imr; /* 0x07 Interrupt Mask Register */
++} __attribute__ ((packed));
++
++struct tmio_nand {
++ struct mtd_info mtd;
++ struct nand_chip chip;
++
++ struct tmio_nfhccr __iomem * ccr;
++ struct tmio_nfcr __iomem * fcr;
++
++ unsigned int irq;
++
++ /* for tmio_nand_read_byte */
++ u8 read;
++ unsigned read_good:1;
++};
++
++#define mtd_to_tmio(m) container_of(m, struct tmio_nand, mtd)
++
++/*--------------------------------------------------------------------------*/
++
++static void tmio_nand_hwcontrol(struct mtd_info *mtd, int cmd,
++ unsigned int ctrl)
++{
++ struct tmio_nand *tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem *fcr = tmio->fcr;
++ struct nand_chip *chip = mtd->priv;
++
++ if (ctrl & NAND_CTRL_CHANGE) {
++ u8 mode;
++
++ if (ctrl & NAND_NCE) {
++ mode = FCR_MODE_DATA;
++
++ if (ctrl & NAND_CLE)
++ mode |= FCR_MODE_CLE;
++ else
++ mode &= ~FCR_MODE_CLE;
++
++ if (ctrl & NAND_ALE)
++ mode |= FCR_MODE_ALE;
++ else
++ mode &= ~FCR_MODE_ALE;
++ } else {
++ mode = FCR_MODE_STANDBY;
++ }
++
++ iowrite8 (mode, &fcr->mode);
++ tmio->read_good = 0;
++ }
++
++ if (cmd != NAND_CMD_NONE)
++ writeb(cmd, chip->IO_ADDR_W);
++}
++
++static int tmio_nand_dev_ready (struct mtd_info* mtd)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ return !(ioread8 (&fcr->status) & FCR_STATUS_BUSY);
++}
++
++static irqreturn_t tmio_irq (int irq, void *__tmio)
++{
++ struct tmio_nand* tmio = __tmio;
++ struct nand_chip* this = &tmio->chip;
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ /* disable RDYREQ interrupt */
++ iowrite8 (0x00, &fcr->imr);
++
++ if (unlikely (!waitqueue_active (&this->controller->wq)))
++ printk (KERN_WARNING TMIO_NAME_NAND ": spurious interrupt\n");
++
++ wake_up (&this->controller->wq);
++ return IRQ_HANDLED;
++}
++
++/*
++ * The TMIO core has a RDYREQ interrupt on the posedge of #SMRB.
++ * This interrupt is normally disabled, but for long operations like
++ * erase and write, we enable it to wake us up. The irq handler
++ * disables the interrupt.
++ */
++static int
++tmio_nand_wait (struct mtd_info *mtd, struct nand_chip *this)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++ long timeout;
++
++ /* enable RDYREQ interrupt */
++ iowrite8 (0x0f, &fcr->isr);
++ iowrite8 (0x81, &fcr->imr);
++
++ timeout = wait_event_timeout (this->controller->wq, tmio_nand_dev_ready (mtd),
++ msecs_to_jiffies (this->state == FL_ERASING ? 400 : 20));
++
++ if (unlikely (!tmio_nand_dev_ready (mtd))) {
++ iowrite8 (0x00, &fcr->imr);
++ mtd_warn (mtd, "still busy with %s after %d ms\n",
++ this->state == FL_ERASING ? "erase" : "program",
++ this->state == FL_ERASING ? 400 : 20);
++
++ } else if (unlikely (!timeout)) {
++ iowrite8 (0x00, &fcr->imr);
++ mtd_warn (mtd, "timeout waiting for interrupt\n");
++ }
++
++ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
++ return this->read_byte (mtd);
++}
++
++/*
++ * The TMIO controller combines two 8-bit data bytes into one 16-bit
++ * word. This function separates them so nand_base.c works as expected,
++ * especially its NAND_CMD_READID routines.
++ *
++ * To prevent stale data from being read, tmio_nand_hwcontrol() clears
++ * tmio->read_good.
++ */
++static u_char tmio_nand_read_byte (struct mtd_info *mtd)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++ unsigned int data;
++
++ if (tmio->read_good--)
++ return tmio->read;
++
++ data = ioread16 (&fcr->u16);
++ tmio->read = data >> 8;
++ return data;
++}
++
++/*
++ * The TMIO controller converts an 8-bit NAND interface to a 16-bit
++ * bus interface, so all data reads and writes must be 16-bit wide.
++ * Thus, we implement 16-bit versions of the read, write, and verify
++ * buffer functions.
++ */
++static void
++tmio_nand_write_buf (struct mtd_info *mtd, const u_char *buf, int len)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ iowrite16_rep (&fcr->u16, buf, len >> 1);
++}
++
++static void tmio_nand_read_buf (struct mtd_info *mtd, u_char *buf, int len)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ ioread16_rep (&fcr->u16, buf, len >> 1);
++}
++
++static int
++tmio_nand_verify_buf (struct mtd_info *mtd, const u_char *buf, int len)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++ u16* p = (u16*) buf;
++
++ for (len >>= 1; len; len--)
++ if (*(p++) != ioread16 (&fcr->u16))
++ return -EFAULT;
++ return 0;
++}
++
++static void tmio_nand_enable_hwecc (struct mtd_info* mtd, int mode)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ iowrite8 (FCR_MODE_HWECC_RESET, &fcr->mode);
++ ioread8 (&fcr->u8); /* dummy read */
++ iowrite8 (FCR_MODE_HWECC_CALC, &fcr->mode);
++}
++
++static int tmio_nand_calculate_ecc (struct mtd_info* mtd, const u_char* dat,
++ u_char* ecc_code)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++ unsigned int ecc;
++
++ iowrite8 (FCR_MODE_HWECC_RESULT, &fcr->mode);
++
++ ecc = ioread16 (&fcr->u16);
++ ecc_code[1] = ecc; // 000-255 LP7-0
++ ecc_code[0] = ecc >> 8; // 000-255 LP15-8
++ ecc = ioread16 (&fcr->u16);
++ ecc_code[2] = ecc; // 000-255 CP5-0,11b
++ ecc_code[4] = ecc >> 8; // 256-511 LP7-0
++ ecc = ioread16 (&fcr->u16);
++ ecc_code[3] = ecc; // 256-511 LP15-8
++ ecc_code[5] = ecc >> 8; // 256-511 CP5-0,11b
++
++ iowrite8 (FCR_MODE_DATA, &fcr->mode);
++ return 0;
++}
++
++static void tmio_hw_init (struct device *dev, struct tmio_nand *tmio)
++{
++ struct resource* nfcr = tmio_resource_control (dev);
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ struct tmio_nfhccr __iomem * ccr = tmio->ccr;
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++ unsigned long base;
++
++ /* (89h) SMD Buffer ON By TC6393XB SystemConfig gpibfc1 */
++ tdev->ops->clock (dev, 1);
++ tdev->ops->function (dev, 1);
++
++ /* (4Ch) CLKRUN Enable 1st spcrunc */
++ iowrite8 (0x81, &ccr->icc);
++
++ /* (10h)BaseAddress 0x1000 spba.spba2 */
++ base = nfcr->start - tdev->iomem->start;
++ iowrite16 (base, ccr->base + 0);
++ iowrite16 (base >> 16, ccr->base + 1);
++
++ /* (04h)Command Register I/O spcmd */
++ iowrite8 (0x02, &ccr->command);
++
++ /* (62h) Power Supply Control ssmpwc */
++ /* HardPowerOFF - SuspendOFF - PowerSupplyWait_4MS */
++ iowrite8 (0x02, &ccr->nfpsc);
++
++ /* (63h) Detect Control ssmdtc */
++ iowrite8 (0x02, &ccr->nfdc);
++
++ /* Interrupt status register clear sintst */
++ iowrite8 (0x0f, &fcr->isr);
++
++ /* After power supply, Media are reset smode */
++ iowrite8 (FCR_MODE_POWER_ON, &fcr->mode);
++ iowrite8 (FCR_MODE_COMMAND, &fcr->mode);
++ iowrite8 (NAND_CMD_RESET, &fcr->u8);
++
++ /* Standby Mode smode */
++ iowrite8 (FCR_MODE_STANDBY, &fcr->mode);
++
++ mdelay (5);
++}
++
++static void tmio_hw_stop (struct device *dev, struct tmio_nand *tmio)
++{
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ iowrite8 (FCR_MODE_POWER_OFF, &fcr->mode);
++ tdev->ops->function (dev, 0);
++ tdev->ops->clock (dev, 0);
++}
++
++/*--------------------------------------------------------------------------*/
++
++#ifdef CONFIG_MTD_PARTITIONS
++static const char *part_probes[] = { "cmdlinepart", NULL };
++#endif
++
++static int tmio_probe (struct device *dev)
++{
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ struct tmio_nand_platform_data* tnpd = dev->platform_data;
++ struct resource* ccr = tmio_resource_config (dev);
++ struct resource* fcr = tmio_resource_control (dev);
++ struct resource* irq = tmio_resource_irq (dev);
++ struct tmio_nand* tmio;
++ struct mtd_info* mtd;
++ struct nand_chip* this;
++ struct mtd_partition* parts;
++ int nbparts = 0;
++ int retval;
++
++ if (!tnpd)
++ return -EINVAL;
++
++ retval = request_resource (tdev->iomem, ccr);
++ if (retval)
++ goto err_request_ccr;
++
++ retval = request_resource (tdev->iomem, fcr);
++ if (retval)
++ goto err_request_fcr;
++
++ tmio = kzalloc (sizeof *tmio, GFP_KERNEL);
++ if (!tmio) {
++ retval = -ENOMEM;
++ goto err_kzalloc;
++ }
++
++ dev_set_drvdata (dev, tmio);
++ mtd = &tmio->mtd;
++ this = &tmio->chip;
++ mtd->priv = this;
++ mtd->name = TMIO_NAME_NAND;
++
++ tmio->ccr = ioremap (ccr->start, ccr->end - ccr->start + 1);
++ if (!tmio->ccr) {
++ retval = -EIO;
++ goto err_iomap_ccr;
++ }
++
++ tmio->fcr = ioremap (fcr->start, fcr->end - fcr->start + 1);
++ if (!tmio->fcr) {
++ retval = -EIO;
++ goto err_iomap_fcr;
++ }
++
++ tmio_hw_init (dev, tmio);
++
++ /* Set address of NAND IO lines */
++ this->IO_ADDR_R = tmio->fcr;
++ this->IO_ADDR_W = tmio->fcr;
++
++ /* Set address of hardware control function */
++ this->cmd_ctrl = tmio_nand_hwcontrol;
++ this->dev_ready = tmio_nand_dev_ready;
++ this->read_byte = tmio_nand_read_byte;
++ this->write_buf = tmio_nand_write_buf;
++ this->read_buf = tmio_nand_read_buf;
++ this->verify_buf = tmio_nand_verify_buf;
++
++ /* set eccmode using hardware ECC */
++ this->ecc.mode = NAND_ECC_HW;
++ this->ecc.size = 512;
++ this->ecc.bytes = 6;
++ this->ecc.hwctl = tmio_nand_enable_hwecc;
++ this->ecc.calculate = tmio_nand_calculate_ecc;
++ this->ecc.correct = nand_correct_data;
++ this->badblock_pattern = tnpd->badblock_pattern;
++
++ /* 15 us command delay time */
++ this->chip_delay = 15;
++
++ if (irq->start) {
++ retval = request_irq (irq->start, &tmio_irq,
++ IRQF_DISABLED, irq->name, tmio);
++ if (!retval) {
++ tmio->irq = irq->start;
++ this->waitfunc = tmio_nand_wait;
++ } else
++ mtd_warn (mtd, "request_irq error %d\n", retval);
++ }
++
++ /* Scan to find existence of the device */
++ if (nand_scan (mtd, 1)) {
++ retval = -ENODEV;
++ goto err_scan;
++ }
++
++ /* Register the partitions */
++#ifdef CONFIG_MTD_PARTITIONS
++ nbparts = parse_mtd_partitions (mtd, part_probes, &parts, 0);
++#endif
++ if (nbparts <= 0) {
++ parts = tnpd->partition;
++ nbparts = tnpd->num_partitions;
++ }
++
++ add_mtd_partitions (mtd, parts, nbparts);
++ return 0;
++
++err_scan:
++ if (tmio->irq)
++ free_irq (tmio->irq, tmio);
++ tmio_hw_stop (dev, tmio);
++ iounmap (tmio->fcr);
++err_iomap_fcr:
++ iounmap (tmio->ccr);
++err_iomap_ccr:
++ kfree (tmio);
++err_kzalloc:
++ release_resource (fcr);
++err_request_fcr:
++ release_resource (ccr);
++err_request_ccr:
++ return retval;
++}
++
++static int tmio_remove (struct device *dev)
++{
++ struct tmio_nand* tmio = dev_get_drvdata (dev);
++
++ nand_release (&tmio->mtd);
++ if (tmio->irq)
++ free_irq (tmio->irq, tmio);
++ tmio_hw_stop (dev, tmio);
++ iounmap (tmio->fcr);
++ iounmap (tmio->ccr);
++ kfree (tmio);
++ release_resource (tmio_resource_control (dev));
++ release_resource (tmio_resource_config (dev));
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int tmio_suspend (struct device *dev, pm_message_t state)
++{
++ tmio_hw_stop (dev, dev_get_drvdata (dev));
++ return 0;
++}
++
++static int tmio_resume (struct device *dev)
++{
++ tmio_hw_init (dev, dev_get_drvdata (dev));
++ return 0;
++}
++#endif
++
++static struct device_driver tmio_driver = {
++ .name = TMIO_NAME_NAND,
++ .bus = &tmio_bus_type,
++ .probe = tmio_probe,
++ .remove = tmio_remove,
++#ifdef CONFIG_PM
++ .suspend = tmio_suspend,
++ .resume = tmio_resume,
++#endif
++};
++
++static int __init tmio_init (void) {
++ return driver_register (&tmio_driver);
++}
++
++static void __exit tmio_exit (void) {
++ driver_unregister (&tmio_driver);
++}
++
++module_init (tmio_init);
++module_exit (tmio_exit);
++
++MODULE_LICENSE ("GPL");
++MODULE_AUTHOR ("Dirk Opfer, Chris Humbert");
++MODULE_DESCRIPTION ("NAND flash driver on Toshiba Mobile IO controller");
diff --git a/packages/linux/linux-rp-2.6.22/tmio-ohci-r6.patch b/packages/linux/linux-rp-2.6.22/tmio-ohci-r6.patch
new file mode 100644
index 0000000000..9fdd2962c9
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/tmio-ohci-r6.patch
@@ -0,0 +1,929 @@
+
+ drivers/usb/host/Kconfig | 1
+ drivers/usb/host/ohci-hcd.c | 1
+ drivers/usb/host/ohci-tmio.c | 894 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 896 insertions(+)
+
+Index: git/drivers/usb/host/ohci-tmio.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ git/drivers/usb/host/ohci-tmio.c 2006-11-07 21:48:33.000000000 +0000
+@@ -0,0 +1,894 @@
++/*
++ * OHCI HCD (Host Controller Driver) for USB.
++ *
++ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
++ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
++ * (C) Copyright 2002 Hewlett-Packard Company
++ *
++ * Bus glue for Toshiba Mobile IO (TMIO) Controller's OHCI core
++ * (C) Copyright 2005 Chris Humbert <mahadri-usb@drigon.com>
++ *
++ * This is known to work with the following variants:
++ * TC6393XB revision 3 (32kB SRAM)
++ *
++ * The TMIO's OHCI core DMAs through a small internal buffer that
++ * is directly addressable by the CPU. dma_declare_coherent_memory
++ * and DMA bounce buffers allow the higher-level OHCI host driver to
++ * work. However, the dma API doesn't handle dma mapping failures
++ * well (dma_sg_map() is a prime example), so it is unusable.
++ *
++ * This HC pretends be a PIO-ish controller and uses the kernel's
++ * generic allocator for the entire SRAM. Using the USB core's
++ * usb_operations, we provide hcd_buffer_alloc/free. Using the OHCI's
++ * ohci_ops, we provide memory management for OHCI's TDs and EDs. We
++ * internally queue a URB's TDs until enough dma memory is available
++ * to enqueue them with the HC.
++ *
++ * Written from sparse documentation from Toshiba and Sharp's driver
++ * for the 2.4 kernel,
++ * usb-ohci-tc6393.c (C) Copyright 2004 Lineo Solutions, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/fs.h>
++#include <linux/mount.h>
++#include <linux/pagemap.h>
++#include <linux/init.h>
++#include <linux/namei.h>
++#include <linux/sched.h>
++
++#include <linux/genalloc.h>
++#include <asm/dma-mapping.h> /* for consistent_sync() */
++#include <asm/hardware/tmio.h>
++
++/*-------------------------------------------------------------------------*/
++
++/*
++ * USB Host Controller Configuration Register
++ */
++struct tmio_uhccr {
++ u8 x00[8];
++ u8 revid; /* 0x08 Revision ID */
++ u8 x01[7];
++ u16 basel; /* 0x10 USB Control Register Base Address Low */
++ u16 baseh; /* 0x12 USB Control Register Base Address High */
++ u8 x02[0x2c];
++ u8 ilme; /* 0x40 Internal Local Memory Enable */
++ u8 x03[0x0b];
++ u16 pm; /* 0x4c Power Management */
++ u8 x04[2];
++ u8 intc; /* 0x50 INT Control */
++ u8 x05[3];
++ u16 lmw1l; /* 0x54 Local Memory Window 1 LMADRS Low */
++ u16 lmw1h; /* 0x56 Local Memory Window 1 LMADRS High */
++ u16 lmw1bl; /* 0x58 Local Memory Window 1 Base Address Low */
++ u16 lmw1bh; /* 0x5A Local Memory Window 1 Base Address High */
++ u16 lmw2l; /* 0x5C Local Memory Window 2 LMADRS Low */
++ u16 lmw2h; /* 0x5E Local Memory Window 2 LMADRS High */
++ u16 lmw2bl; /* 0x60 Local Memory Window 2 Base Address Low */
++ u16 lmw2bh; /* 0x62 Local Memory Window 2 Base Address High */
++ u8 x06[0x98];
++ u8 misc; /* 0xFC MISC */
++ u8 x07[3];
++} __attribute__ ((packed));
++
++union tmio_uhccr_pm {
++ u16 raw;
++struct {
++ unsigned gcken:1; /* D0 */
++ unsigned ckrnen:1; /* D1 */
++ unsigned uspw1:1; /* D2 USB Port 1 Power Disable */
++ unsigned uspw2:1; /* D3 USB Port 2 Power Disable */
++ unsigned x00:4;
++ unsigned pmee:1; /* D8 */
++ unsigned x01:6;
++ unsigned pmes:1; /* D15 */
++} __attribute__ ((packed));
++} __attribute__ ((packed));
++
++/*-------------------------------------------------------------------------*/
++
++struct tmio_dma_pool {
++ struct device* dev;
++ unsigned int size;
++};
++
++struct tmio_hcd {
++ struct gen_pool* poolp;
++ struct usb_operations ops;
++ struct tmio_dma_pool td_pool;
++ struct tmio_dma_pool ed_pool;
++
++ struct tmio_uhccr __iomem *ccr;
++ void __iomem * sram;
++ size_t sram_len;
++};
++
++#define hcd_to_tmio(hcd) ((struct tmio_hcd*)(hcd_to_ohci (hcd) + 1))
++
++struct tmio_td {
++ void* data; /* td's data buffer */
++ void __iomem * bounce; /* dma bounce buffer */
++ dma_addr_t dma; /* bounce buffer dma address */
++ size_t len; /* bounce buffer length */
++ u32 info; /* parameter for td_fill */
++};
++
++struct tmio_urb {
++ int td_add; /* next index to be added */
++ int td_queue; /* next index to be HC enqueued */
++
++ struct tmio_td td [0]; /* private td data */
++};
++
++static inline struct tmio_urb *urb_to_turb (struct urb *urb)
++{
++ urb_priv_t* urb_priv = urb->hcpriv;
++ return (struct tmio_urb*)(urb_priv->td + urb_priv->length);
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* gen_pool_alloc page allocator callback */
++static unsigned long tmio_pool_callback(struct gen_pool *poolp)
++{
++ return 0;
++}
++
++static inline void tmio_pool_destroy(struct tmio_hcd *tmio)
++{
++ struct gen_pool *poolp = tmio->poolp;
++
++ if (!poolp)
++ return;
++ if (poolp->h)
++ kfree(poolp->h);
++ kfree(poolp);
++ tmio->poolp = NULL;
++}
++
++/*-------------------------------------------------------------------------*/
++
++#define BOUNDED_XYL(x,y,ylen) (((y) <= (x)) && ((x) < ((y)+(ylen))))
++#define BOUNDED_XYY(x,y1,y2) (((y1) <= (x)) && ((x) < (y2)))
++
++static inline dma_addr_t tmio_virt_to_dma (struct usb_hcd *hcd, void *vaddr)
++{
++ struct resource* sram = tmio_resource_mem (hcd->self.controller);
++ struct tmio_hcd* tmio = hcd_to_tmio (hcd);
++
++ return BOUNDED_XYL (vaddr, tmio->sram, tmio->sram_len)
++ ? sram->start + (vaddr - tmio->sram)
++ : ~0;
++}
++
++static inline void* tmio_dma_to_virt (struct usb_hcd *hcd, dma_addr_t handle)
++{
++ struct resource* sram = tmio_resource_mem (hcd->self.controller);
++ struct tmio_hcd* tmio = hcd_to_tmio (hcd);
++
++ return BOUNDED_XYY (handle, sram->start, sram->end + 1)
++ ? tmio->sram + handle - sram->start
++ : NULL;
++}
++
++/*
++ * allocate dma-able memory in the device's internal sram
++ *
++ * The generic pool allocator's minimum chunk size is 32 bytes,
++ * which is the cache line size on the PXA255, so we don't need
++ * to do anything special for smaller requests.
++ */
++static inline void *tmio_dma_alloc (struct device *dev, size_t size,
++ dma_addr_t *handle, gfp_t mem_flags)
++{
++ struct usb_hcd* hcd = dev_get_drvdata (dev);
++ struct tmio_hcd* tmio = hcd_to_tmio (hcd);
++ void* virt = (void*) gen_pool_alloc (tmio->poolp, size);
++
++ return (*handle = tmio_virt_to_dma (hcd, virt)) == ~0 ? NULL : virt;
++}
++
++static inline void tmio_dma_free (struct device *dev, size_t size,
++ void *cpu_addr, dma_addr_t handle)
++{
++ struct usb_hcd* hcd = dev_get_drvdata (dev);
++ struct tmio_hcd* tmio = hcd_to_tmio (hcd);
++ dma_addr_t dma = tmio_virt_to_dma (hcd, cpu_addr);
++
++ if (unlikely (dma == ~0)) {
++ dev_err (dev, "trying to free bad address 0x%p\n", cpu_addr);
++ return;
++ }
++
++ if (unlikely (handle != dma))
++ dev_err (dev, "dma address mismatch for 0x%p: %08x != %08x\n",
++ cpu_addr, handle, dma);
++
++ gen_pool_free (tmio->poolp, (unsigned long) cpu_addr, size);
++}
++
++/*-------------------------------------------------------------------------*/
++
++static void *tmio_dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags,
++ dma_addr_t *handle)
++{
++ struct tmio_dma_pool *tdp = (struct tmio_dma_pool*) pool;
++ return tmio_dma_alloc (tdp->dev, tdp->size, handle, mem_flags);
++}
++
++static void
++tmio_dma_pool_free (struct dma_pool *pool, void *vaddr, dma_addr_t addr)
++{
++ struct tmio_dma_pool *tdp = (struct tmio_dma_pool*) pool;
++ return tmio_dma_free (tdp->dev, tdp->size, vaddr, addr);
++}
++
++static void *tmio_buffer_alloc (struct usb_bus *bus, size_t size,
++ gfp_t mem_flags, dma_addr_t *dma)
++{
++ return tmio_dma_alloc (bus->controller, size, dma, mem_flags);
++}
++
++static void tmio_buffer_free (struct usb_bus *bus, size_t size,
++ void *addr, dma_addr_t dma)
++{
++ tmio_dma_free (bus->controller, size, addr, dma);
++}
++
++/*-------------------------------------------------------------------------*/
++
++static void tmio_hc_stop (struct usb_hcd *hcd)
++{
++ struct device* dev = hcd->self.controller;
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ struct tmio_hcd* tmio = hcd_to_tmio (hcd);
++ struct tmio_uhccr __iomem* ccr = tmio->ccr;
++ union tmio_uhccr_pm pm = {0};
++
++ pm.gcken = 1;
++ pm.ckrnen = 1;
++ pm.uspw1 = 1;
++ pm.uspw2 = 1;
++
++ iowrite8 (0, &ccr->intc);
++ iowrite8 (0, &ccr->ilme);
++ iowrite16(0, &ccr->basel);
++ iowrite16(0, &ccr->baseh);
++ iowrite16(pm.raw, &ccr->pm);
++
++ tdev->ops->function (dev, 0);
++ tdev->ops->clock (dev, 0);
++}
++
++static void tmio_hc_start (struct usb_hcd *hcd)
++{
++ struct device* dev = hcd->self.controller;
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ struct tmio_hcd* tmio = hcd_to_tmio (hcd);
++ struct tmio_uhccr __iomem* ccr = tmio->ccr;
++ union tmio_uhccr_pm pm = {0};
++
++ pm.pmes = 1;
++ pm.pmee = 1;
++ pm.ckrnen = 1;
++ pm.gcken = 1;
++
++ tdev->ops->clock (dev, 1);
++ tdev->ops->function (dev, 1);
++
++ iowrite16(pm.raw, &ccr->pm);
++ iowrite16(hcd->rsrc_start, &ccr->basel);
++ iowrite16(hcd->rsrc_start >> 16, &ccr->baseh);
++ iowrite8 (1, &ccr->ilme);
++ iowrite8 (2, &ccr->intc);
++
++ consistent_sync (tmio->sram, tmio->sram_len, DMA_BIDIRECTIONAL);
++
++ dev_info (dev, "revision %d @ 0x%08llx, irq %d\n",
++ ioread8 (&ccr->revid), hcd->rsrc_start, hcd->irq);
++}
++
++static void tmio_stop (struct usb_hcd *hcd)
++{
++ struct ohci_hcd* ohci = hcd_to_ohci (hcd);
++
++ /* NULL these so ohci_stop() doesn't try to free them */
++ ohci->hcca = NULL;
++ ohci->td_cache = NULL;
++ ohci->ed_cache = NULL;
++
++ ohci_stop (hcd);
++ tmio_hc_stop (hcd);
++ tmio_pool_destroy (hcd_to_tmio (hcd));
++
++ /* We don't free the hcca because tmio_hc_stop() turns off
++ * the sram and the memory allocation data is destroyed. */
++}
++
++static int tmio_start (struct usb_hcd *hcd)
++{
++ struct device* dev = hcd->self.controller;
++ struct ohci_hcd* ohci = hcd_to_ohci (hcd);
++ struct tmio_hcd* tmio = hcd_to_tmio (hcd);
++ int retval;
++
++ tmio_hc_start (hcd);
++
++ tmio->poolp = gen_pool_create(0, fls(tmio->sram_len) - 1,
++ tmio_pool_callback, 0);
++ if (!tmio->poolp) {
++ retval = -ENOMEM;
++ goto err_gen_pool_create;
++ }
++
++ gen_pool_free (tmio->poolp, (unsigned long)(tmio->sram),
++ tmio->sram_len);
++
++ ohci->hcca = tmio_dma_alloc (dev, sizeof *ohci->hcca,
++ &ohci->hcca_dma, GFP_KERNEL);
++ if (!ohci->hcca) {
++ retval = -ENOMEM;
++ goto err_hcca_alloc;
++ }
++
++ /* for our dma_pool_alloc/free hooks */
++ ohci->td_cache = (struct dma_pool*) &tmio->td_pool;
++ ohci->ed_cache = (struct dma_pool*) &tmio->ed_pool;
++
++ if ((retval = ohci_init (ohci)) < 0)
++ goto err_ohci_init;
++
++ if ((retval = ohci_run (ohci)) < 0)
++ goto err_ohci_run;
++
++ return 0;
++
++err_ohci_run:
++ err ("can't start %s", hcd->self.bus_name);
++err_ohci_init:
++err_hcca_alloc:
++err_gen_pool_create:
++ tmio_stop (hcd);
++ return retval;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static inline void *tmio_urb_dma_to_virt(struct urb *urb, dma_addr_t dma)
++{
++ if (BOUNDED_XYL(dma, urb->transfer_dma, urb->transfer_buffer_length))
++ return urb->transfer_buffer + dma - urb->transfer_dma;
++
++ if (BOUNDED_XYL(dma, urb->setup_dma, sizeof (struct usb_ctrlrequest)))
++ return urb->setup_packet + dma - urb->setup_dma;
++
++ return NULL;
++}
++
++static struct tmio_td* tmio_td_find (struct td *td, int *index)
++{
++ struct urb* urb = td->urb;
++ urb_priv_t* urb_priv = urb->hcpriv;
++ struct tmio_urb* turb = urb_to_turb (urb);
++ int i;
++
++ for (i=0; i < urb_priv->length; i++)
++ if (urb_priv->td[i] == td) {
++ *index = i;
++ return turb->td + i;
++ }
++
++ return NULL;
++}
++
++/*
++ * map the td's data to dma-able memory
++ *
++ * if this td transfers data,
++ * sets tmtd->data to the urb's data buffer
++ * sets tmtd->dma to dma-able memory
++ * sets tmtd->bounce to non-NULL if a bounce buffer is allocated
++ * copies the urb's data buffer to the bounce buffer if necessary
++ */
++static int tmio_td_dma_map (struct ohci_hcd *ohci, struct urb *urb,
++ struct tmio_td *tmtd, int idx)
++{
++ struct usb_hcd* hcd = ohci_to_hcd (ohci);
++ struct device* dev = hcd->self.controller;
++ dma_addr_t dma;
++
++ if (!tmtd->len)
++ return 0;
++
++ if (tmio_dma_to_virt (hcd, tmtd->dma))
++ return 0;
++
++ tmtd->data = tmio_urb_dma_to_virt (urb, tmtd->dma);
++ if (unlikely (!tmtd->data)) {
++ dev_err (dev, "TD has bad dma address 0x%08x\n", tmtd->dma);
++ return 0;
++ }
++
++ tmtd->bounce = tmio_dma_alloc (dev, tmtd->len, &dma, GFP_ATOMIC);
++ if (!tmtd->bounce)
++ return -ENOMEM;
++
++ if ((usb_pipecontrol (urb->pipe) && !idx) || usb_pipeout (urb->pipe)) {
++ consistent_sync (tmtd->bounce, tmtd->len, DMA_TO_DEVICE);
++ memcpy (tmtd->bounce, tmtd->data, tmtd->len);
++ } else
++ consistent_sync (tmtd->bounce, tmtd->len, DMA_FROM_DEVICE);
++
++ tmtd->dma = dma;
++ return 0;
++}
++
++/*
++ * unmaps the td's data from dma-able memory
++ *
++ * if a bounce buffer has been allocated,
++ * copy the bounce buffer to the urb's data buffer if necessary
++ * free the bounce buffer
++ */
++static void tmio_td_dma_unmap (struct ohci_hcd *ohci, struct td *td)
++{
++ struct device* dev = (ohci_to_hcd (ohci))->self.controller;
++ struct urb* urb = td->urb;
++ int idx;
++ struct tmio_td* tmtd = tmio_td_find (td, &idx);
++
++ if (!tmtd->bounce)
++ return;
++
++ if (usb_pipein (urb->pipe) && (usb_pipecontrol (urb->pipe) ? idx : 1)) {
++ memcpy (tmtd->data, tmtd->bounce, tmtd->len);
++ consistent_sync (tmtd->data, tmtd->len, DMA_TO_DEVICE);
++ }
++
++ tmio_dma_free (dev, tmtd->len, tmtd->bounce, tmtd->dma);
++}
++
++static int tmio_urb_runqueue (struct ohci_hcd *ohci, struct urb *urb)
++{
++ struct tmio_urb* turb = urb_to_turb (urb);
++ urb_priv_t* urb_priv= urb->hcpriv;
++ int start = turb->td_queue;
++ int retval = 0;
++ int i;
++
++ for (i = start; i < turb->td_add; i = ++turb->td_queue) {
++ struct tmio_td *tmtd = turb->td + i;
++
++ if ((retval = tmio_td_dma_map (ohci, urb, tmtd, i)))
++ break;
++
++ td_fill (ohci, tmtd->info, tmtd->dma, tmtd->len, urb, i);
++ }
++
++ if (i <= start)
++ return retval;
++
++ /* kickstart the appropriate list */
++ wmb ();
++ switch (urb_priv->ed->type) {
++ case PIPE_BULK:
++ ohci_writel (ohci, OHCI_BLF, &ohci->regs->cmdstatus);
++ break;
++ case PIPE_CONTROL:
++ ohci_writel (ohci, OHCI_CLF, &ohci->regs->cmdstatus);
++ break;
++ }
++
++ return retval;
++}
++
++/*
++ * This needs to be called with ohci->lock held so the pending urb list
++ * isn't modified.
++ */
++static int tmio_ohci_runqueue (struct ohci_hcd *ohci)
++{
++ urb_priv_t* priv;
++ int retval = 0;
++
++ list_for_each_entry_reverse (priv, &ohci->pending, pending)
++ if ((retval = tmio_urb_runqueue (ohci, priv->td[0]->urb)))
++ return retval;
++
++ return retval;
++}
++
++static void tmio_td_fill (struct ohci_hcd *ohci, u32 info,
++ dma_addr_t data, int len, struct urb *urb, int index)
++{
++ struct tmio_urb* turb = urb_to_turb (urb);
++ struct tmio_td* tmtd = turb->td + index;
++
++ tmtd->data = NULL;
++ tmtd->bounce = NULL;
++ tmtd->dma = data;
++ tmtd->len = len;
++ tmtd->info = info;
++ turb->td_add = index + 1;
++}
++
++static void
++tmio_td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td)
++{
++ tmio_td_dma_unmap (ohci, td);
++ td_done (ohci, urb, td);
++}
++
++const static struct ohci_ops tmio_ops = {
++ .dma_pool_alloc = tmio_dma_pool_alloc,
++ .dma_pool_free = tmio_dma_pool_free,
++ .td_fill = tmio_td_fill,
++ .td_done = tmio_td_done,
++};
++
++/*-------------------------------------------------------------------------*/
++
++static irqreturn_t tmio_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
++{
++ irqreturn_t retval = ohci_irq (hcd, ptregs);
++
++ if (retval == IRQ_HANDLED) {
++ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
++ unsigned long flags;
++
++ spin_lock_irqsave(&ohci->lock, flags);
++ tmio_ohci_runqueue (ohci);
++ spin_unlock_irqrestore (&ohci->lock, flags);
++ }
++
++ return retval;
++}
++
++/*
++ * This is ohci_urb_enqueue with:
++ * dma address sanitization for tmio_urb_dma_to_virt()
++ * allocate extra space in urb_priv for our private data
++ * initialize urb_priv->td[0]->urb for tmio_ohci_runqueue()
++ * call tmio_ohci_runqueue() after submitting TDs
++ */
++static int tmio_urb_enqueue (
++ struct usb_hcd *hcd,
++ struct usb_host_endpoint *ep,
++ struct urb *urb,
++ gfp_t mem_flags
++) {
++ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
++ struct ed *ed;
++ urb_priv_t *urb_priv;
++ unsigned int pipe = urb->pipe;
++ int i, size = 0;
++ unsigned long flags;
++ int retval = 0;
++
++#ifdef OHCI_VERBOSE_DEBUG
++ urb_print (urb, "SUB", usb_pipein (pipe));
++#endif
++
++ /* make sure we can convert dma offsets back to virtual addresses */
++ if (!tmio_dma_to_virt (hcd, urb->setup_dma))
++ urb->setup_dma = 0;
++
++ if (!tmio_dma_to_virt (hcd, urb->transfer_dma))
++ urb->transfer_dma = sizeof (struct usb_ctrlrequest);
++
++ /* every endpoint has a ed, locate and maybe (re)initialize it */
++ if (! (ed = ed_get (ohci, ep, urb->dev, pipe, urb->interval)))
++ return -ENOMEM;
++
++ /* for the private part of the URB we need the number of TDs (size) */
++ switch (ed->type) {
++ case PIPE_CONTROL:
++ /* td_submit_urb() doesn't yet handle these */
++ if (urb->transfer_buffer_length > 4096)
++ return -EMSGSIZE;
++
++ /* 1 TD for setup, 1 for ACK, plus ... */
++ size = 2;
++ /* FALLTHROUGH */
++ // case PIPE_INTERRUPT:
++ // case PIPE_BULK:
++ default:
++ /* one TD for every 4096 Bytes (can be upto 8K) */
++ size += urb->transfer_buffer_length / 4096;
++ /* ... and for any remaining bytes ... */
++ if ((urb->transfer_buffer_length % 4096) != 0)
++ size++;
++ /* ... and maybe a zero length packet to wrap it up */
++ if (size == 0)
++ size++;
++ else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0
++ && (urb->transfer_buffer_length
++ % usb_maxpacket (urb->dev, pipe,
++ usb_pipeout (pipe))) == 0)
++ size++;
++ break;
++ case PIPE_ISOCHRONOUS: /* number of packets from URB */
++ size = urb->number_of_packets;
++ break;
++ }
++
++ /* allocate the private part of the URB */
++ urb_priv = kzalloc (sizeof (urb_priv_t)
++ + size * sizeof (struct td*)
++ + sizeof (struct tmio_urb)
++ + size * sizeof (struct tmio_td),
++ mem_flags);
++ if (!urb_priv)
++ return -ENOMEM;
++ INIT_LIST_HEAD (&urb_priv->pending);
++ urb_priv->length = size;
++ urb_priv->ed = ed;
++
++ /* allocate the TDs (deferring hash chain updates) */
++ for (i = 0; i < size; i++) {
++ urb_priv->td [i] = td_alloc (ohci, mem_flags);
++ if (!urb_priv->td [i]) {
++ urb_priv->length = i;
++ urb_free_priv (ohci, urb_priv);
++ return -ENOMEM;
++ }
++ urb_priv->td [i]->urb = urb;
++ }
++
++ spin_lock_irqsave (&ohci->lock, flags);
++
++ /* don't submit to a dead HC */
++ if (!HC_IS_RUNNING(hcd->state)) {
++ retval = -ENODEV;
++ goto fail;
++ }
++
++ /* in case of unlink-during-submit */
++ spin_lock (&urb->lock);
++ if (urb->status != -EINPROGRESS) {
++ spin_unlock (&urb->lock);
++ urb->hcpriv = urb_priv;
++ finish_urb (ohci, urb, NULL);
++ retval = 0;
++ goto fail;
++ }
++
++ /* schedule the ed if needed */
++ if (ed->state == ED_IDLE) {
++ retval = ed_schedule (ohci, ed);
++ if (retval < 0)
++ goto fail0;
++ if (ed->type == PIPE_ISOCHRONOUS) {
++ u16 frame = ohci_frame_no(ohci);
++
++ /* delay a few frames before the first TD */
++ frame += max_t (u16, 8, ed->interval);
++ frame &= ~(ed->interval - 1);
++ frame |= ed->branch;
++ urb->start_frame = frame;
++
++ /* yes, only URB_ISO_ASAP is supported, and
++ * urb->start_frame is never used as input.
++ */
++ }
++ } else if (ed->type == PIPE_ISOCHRONOUS)
++ urb->start_frame = ed->last_iso + ed->interval;
++
++ /* fill the TDs and link them to the ed; and
++ * enable that part of the schedule, if needed
++ * and update count of queued periodic urbs
++ */
++ urb->hcpriv = urb_priv;
++ td_submit_urb (ohci, urb);
++ tmio_ohci_runqueue (ohci);
++
++fail0:
++ spin_unlock (&urb->lock);
++fail:
++ if (retval)
++ urb_free_priv (ohci, urb_priv);
++ spin_unlock_irqrestore (&ohci->lock, flags);
++ return retval;
++}
++
++static const struct hc_driver tmio_hc_driver = {
++ .description = hcd_name,
++ .product_desc = "TMIO OHCI USB Host Controller",
++ .hcd_priv_size = sizeof (struct ohci_hcd)
++ + sizeof (struct tmio_hcd),
++
++ /* generic hardware linkage */
++ .irq = tmio_irq,
++ .flags = HCD_USB11 | HCD_MEMORY,
++
++ /* basic lifecycle operations */
++ .start = tmio_start,
++ .stop = tmio_stop,
++
++ /* managing i/o requests and associated device resources */
++ .urb_enqueue = tmio_urb_enqueue,
++ .urb_dequeue = ohci_urb_dequeue,
++ .endpoint_disable = ohci_endpoint_disable,
++
++ /* scheduling support */
++ .get_frame_number = ohci_get_frame,
++
++ /* root hub support */
++ .hub_status_data = ohci_hub_status_data,
++ .hub_control = ohci_hub_control,
++#ifdef CONFIG_PM
++ .bus_suspend = ohci_bus_suspend,
++ .bus_resume = ohci_bus_resume,
++#endif
++ .start_port_reset = ohci_start_port_reset,
++};
++
++/*-------------------------------------------------------------------------*/
++
++/* configure so an HC device and id are always provided */
++/* always called with process context; sleeping is OK */
++
++/**
++ * tmio_probe - initialize TMIO-based HCDs
++ * Context: !in_interrupt()
++ *
++ * Allocates basic resources for this USB host controller, and
++ * then invokes the start() method for the HCD associated with it
++ * through the hotplug entry's driver_data.
++ */
++static int tmio_probe (struct device *dev)
++{
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ struct resource* config = tmio_resource_config (dev);
++ struct resource* regs = tmio_resource_control (dev);
++ struct resource* sram = tmio_resource_mem (dev);
++ struct resource* irq = tmio_resource_irq (dev);
++ struct usb_operations* ops;
++ struct tmio_hcd* tmio;
++ struct ohci_hcd* ohci;
++ struct usb_hcd* hcd;
++ int retval;
++
++ if (usb_disabled ())
++ return -ENODEV;
++
++ if (dev->dma_mask || dev->coherent_dma_mask) {
++ dev_err (dev, "DMA not supported\n");
++ return -ENODEV;
++ }
++
++ hcd = usb_create_hcd (&tmio_hc_driver, dev, dev->bus_id);
++ if (!hcd) {
++ retval = -ENOMEM;
++ goto err_create_hcd;
++ }
++
++ tmio = hcd_to_tmio (hcd);
++ tmio->td_pool.dev = dev;
++ tmio->ed_pool.dev = dev;
++ tmio->td_pool.size = sizeof (struct td);
++ tmio->ed_pool.size = sizeof (struct ed);
++ ohci = hcd_to_ohci (hcd);
++ ohci_hcd_init (ohci);
++ ohci->ops = &tmio_ops;
++
++ retval = request_resource (tdev->iomem, config);
++ if (retval)
++ goto err_request_config_resource;
++
++ retval = request_resource (tdev->iomem, regs);
++ if (retval)
++ goto err_request_regs_resource;
++
++ retval = request_resource (tdev->iomem, sram);
++ if (retval)
++ goto err_request_sram_resource;
++
++ hcd->rsrc_start = regs->start;
++ hcd->rsrc_len = regs->end - regs->start + 1;
++ tmio->sram_len = sram->end - sram->start + 1;
++
++ tmio->ccr = ioremap (config->start, config->end - config->start + 1);
++ if (!tmio->ccr) {
++ retval = -ENOMEM;
++ goto err_ioremap_ccr;
++ }
++
++ hcd->regs = ioremap (hcd->rsrc_start, hcd->rsrc_len);
++ if (!hcd->regs) {
++ retval = -ENOMEM;
++ goto err_ioremap_regs;
++ }
++
++ tmio->sram = ioremap (sram->start, tmio->sram_len);
++ if (!tmio->sram) {
++ retval = -ENOMEM;
++ goto err_ioremap_sram;
++ }
++
++ /* drivers should use our coherent buffer allocator */
++ ops = &tmio->ops;
++ memcpy (ops, hcd->self.op, sizeof *ops);
++ ops->buffer_alloc = tmio_buffer_alloc;
++ ops->buffer_free = tmio_buffer_free;
++ hcd->self.op = ops;
++
++ retval = usb_add_hcd (hcd, irq->start, SA_INTERRUPT);
++ if (retval)
++ goto err_usb_add_hcd;
++
++ return 0;
++
++err_usb_add_hcd:
++ iounmap (tmio->sram);
++err_ioremap_sram:
++ iounmap (hcd->regs);
++err_ioremap_regs:
++ iounmap (tmio->ccr);
++err_ioremap_ccr:
++ release_resource (sram);
++err_request_sram_resource:
++ release_resource (regs);
++err_request_regs_resource:
++ release_resource (config);
++err_request_config_resource:
++ usb_put_hcd (hcd);
++err_create_hcd:
++ return retval;
++}
++
++/* may be called without controller electrically present */
++/* may be called with controller, bus, and devices active */
++
++/**
++ * tmio_remove - shutdown processing for TMIO-based HCDs
++ * @dev: USB Host Controller being removed
++ * Context: !in_interrupt()
++ *
++ * Reverses the effect of tmio_probe(), first invoking
++ * the HCD's stop() method. It is always called from a thread
++ * context, normally "rmmod", "apmd", or something similar.
++ */
++static int tmio_remove (struct device *dev)
++{
++ struct usb_hcd* hcd = dev_get_drvdata (dev);
++ struct tmio_hcd* tmio = hcd_to_tmio (hcd);
++
++ usb_remove_hcd (hcd);
++ iounmap (tmio->sram);
++ iounmap (hcd->regs);
++ iounmap (tmio->ccr);
++ release_resource (tmio_resource_mem (dev));
++ release_resource (tmio_resource_control (dev));
++ release_resource (tmio_resource_config (dev));
++ usb_put_hcd (hcd);
++ return 0;
++}
++
++static struct device_driver tmio_ohci = {
++ .name = TMIO_NAME_OHCI,
++ .bus = &tmio_bus_type,
++ .probe = tmio_probe,
++ .remove = tmio_remove,
++};
++
++static int __init tmio_init (void)
++{
++ dbg (DRIVER_INFO " (%s)", TMIO_SOC_NAME);
++ dbg ("block sizes: ed %d td %d",
++ sizeof (struct ed), sizeof (struct td));
++
++ return driver_register (&tmio_ohci);
++}
++
++static void __exit tmio_exit (void)
++{
++ driver_unregister (&tmio_ohci);
++}
++
++module_init (tmio_init);
++module_exit (tmio_exit);
+Index: git/drivers/usb/host/Kconfig
+===================================================================
+--- git.orig/drivers/usb/host/Kconfig 2006-11-07 21:46:32.000000000 +0000
++++ git/drivers/usb/host/Kconfig 2006-11-07 21:48:38.000000000 +0000
+@@ -84,6 +84,7 @@ config USB_OHCI_HCD
+ depends on USB && USB_ARCH_HAS_OHCI
+ select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
+ select I2C if ARCH_PNX4008
++ select GENERIC_ALLOCATOR if TOSHIBA_TC6393XB
+ ---help---
+ The Open Host Controller Interface (OHCI) is a standard for accessing
+ USB 1.1 host controller hardware. It does more in hardware than Intel's
+Index: git/drivers/usb/host/ohci-hcd.c
+===================================================================
+--- git.orig/drivers/usb/host/ohci-hcd.c 2006-11-07 21:46:32.000000000 +0000
++++ git/drivers/usb/host/ohci-hcd.c 2006-11-07 21:48:33.000000000 +0000
+@@ -944,6 +944,7 @@ MODULE_LICENSE ("GPL");
+ || defined(CONFIG_ARCH_OMAP) \
+ || defined (CONFIG_ARCH_LH7A404) \
+ || defined (CONFIG_PXA27x) \
++ || defined (CONFIG_TOSHIBA_TC6393XB) \
+ || defined (CONFIG_ARCH_EP93XX) \
+ || defined (CONFIG_SOC_AU1X00) \
+ || defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \
diff --git a/packages/linux/linux-rp-2.6.22/tmio-tc6393-r8.patch b/packages/linux/linux-rp-2.6.22/tmio-tc6393-r8.patch
new file mode 100644
index 0000000000..1bfdc23630
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/tmio-tc6393-r8.patch
@@ -0,0 +1,800 @@
+ arch/arm/common/Kconfig | 3
+ arch/arm/common/Makefile | 1
+ arch/arm/common/tc6393xb.c | 668 ++++++++++++++++++++++++++++++++++++++++
+ arch/arm/mach-pxa/Kconfig | 1
+ include/asm-arm/arch-pxa/irqs.h | 10
+ include/asm-arm/hardware/tmio.h | 44 ++
+ 6 files changed, 727 insertions(+)
+
+Index: git/arch/arm/common/tc6393xb.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ git/arch/arm/common/tc6393xb.c 2006-11-07 22:14:49.000000000 +0000
+@@ -0,0 +1,668 @@
++/*
++ * Toshiba TC6393XB SoC support
++ *
++ * Maintainer: Chris Humbert <mahadri-kernel@drigon.com>
++ *
++ * Copyright (c) 2005-2006 Chris Humbert
++ * Copyright (c) 2005 Dirk Opfer
++ *
++ * Based on code written by Sharp/Lineo for 2.4 kernels
++ * Based on locomo.c
++ *
++ * 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.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/errno.h>
++#include <linux/ioport.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/fb.h>
++
++#include <asm/hardware.h>
++#include <asm/mach-types.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/mach/irq.h>
++#include <asm/arch/irqs.h>
++#include <asm/hardware/tmio.h>
++
++#ifndef TMIO_SOC_TC6393XB
++#error "TC6393XB SoC not configured"
++#endif
++
++/*--------------------------------------------------------------------------*/
++
++/* cell ids must be 0-based because they are used as array indexes. */
++#define TC6393_CELL_NAND 0
++#define TC6393_CELL_SD 1
++#define TC6393_CELL_OHCI 2
++#define TC6393_CELL_SERIAL 3
++#define TC6393_CELL_LCD 4
++#define TC6393_NUM_CELLS 5
++
++#define TC6393_RESOURCE(_name, _start, _end, _flags) \
++ { \
++ .name = _name, \
++ .start = _start, \
++ .end = _end, \
++ .flags = _flags, \
++ }
++
++#define TC6393_MEM(name, start, size) \
++ TC6393_RESOURCE(name, start, (start) + (size) - 1, IORESOURCE_MEM)
++
++#define TC6393_IRQ(name, irq) \
++ TC6393_RESOURCE(name, irq, irq, IORESOURCE_IRQ)
++
++const static struct resource tc6393_NAND_resource[] = {
++ TC6393_MEM (TMIO_NAME_NAND, 0x000100, 0x100),
++ TC6393_MEM (TMIO_NAME_NAND, 0x001000, 0x008),
++ TC6393_MEM (TMIO_NAME_NAND, 0, 0),
++ TC6393_IRQ (TMIO_NAME_NAND, IRQ_TC6393_NAND),
++};
++
++const static struct resource tc6393_SD_resource[] = {
++ TC6393_MEM (TMIO_NAME_SD, 0x000200, 0x100),
++ TC6393_MEM (TMIO_NAME_SD, 0x002000, 0x200),
++ TC6393_MEM (TMIO_NAME_SD, 0, 0),
++ TC6393_IRQ (TMIO_NAME_SD, IRQ_TC6393_SD),
++};
++
++const static struct resource tc6393_OHCI_resource[] = {
++ TC6393_MEM (TMIO_NAME_OHCI, 0x000300, 0x100),
++ TC6393_MEM (TMIO_NAME_OHCI, 0x003000, 0x100),
++ TC6393_MEM (TMIO_NAME_OHCI, 0x010000, 32 * 1024),
++ TC6393_IRQ (TMIO_NAME_OHCI, IRQ_TC6393_OHCI),
++};
++
++const static struct resource tc6393_SERIAL_resource[] = {
++ TC6393_MEM (TMIO_NAME_SERIAL, 0x000400, 0x100),
++ TC6393_MEM (TMIO_NAME_SERIAL, 0x004000, 0x100),
++ TC6393_MEM (TMIO_NAME_SERIAL, 0, 0),
++ TC6393_IRQ (TMIO_NAME_SERIAL, IRQ_TC6393_SERIAL),
++};
++
++const static struct resource tc6393_LCD_resource[] = {
++ TC6393_MEM (TMIO_NAME_LCD, 0x000500, 0x100),
++ TC6393_MEM (TMIO_NAME_LCD, 0x005000, 0x200),
++ TC6393_MEM (TMIO_NAME_LCD, 0x100000, 1024 * 1024),
++ TC6393_IRQ (TMIO_NAME_LCD, IRQ_TC6393_LCD),
++};
++
++#define TC6393_CELL(_NAME) \
++ [TC6393_CELL_##_NAME] = { \
++ .name = TMIO_NAME_##_NAME, \
++ .id = TC6393_CELL_##_NAME, \
++ .resource = tc6393_##_NAME##_resource, \
++ .num_resources = ARRAY_SIZE (tc6393_##_NAME##_resource), \
++ }
++
++struct tc6393_cell {
++ const char* name;
++ unsigned int id;
++ const struct resource* resource;
++ unsigned int num_resources;
++};
++
++const static struct tc6393_cell tc6393_cell [TC6393_NUM_CELLS] = {
++ TC6393_CELL (NAND ),
++ TC6393_CELL (SD ),
++ TC6393_CELL (OHCI ),
++ TC6393_CELL (SERIAL ),
++ TC6393_CELL (LCD ),
++};
++
++/*--------------------------------------------------------------------------*/
++
++/*
++ * TC6393 System Configuration Register
++ */
++struct tc6393_scr {
++ u8 x00[8];
++ u8 revid; /* 0x08 Revision ID */
++ u8 x01[0x47];
++ u8 isr; /* 0x50 Interrupt Status */
++ u8 x02;
++ u8 imr; /* 0x52 Interrupt Mask */
++ u8 x03;
++ u8 irr; /* 0x54 Interrupt Routing */
++ u8 x04[0x0b];
++ u16 gper; /* 0x60 GP Enable */
++ u8 x05[2];
++ u16 gpi_sr[2]; /* 0x64 GPI Status */
++ u16 gpi_imr[2]; /* 0x68 GPI INT Mask */
++ u16 gpi_eder[2]; /* 0x6c GPI Edge Detect Enable */
++ u16 gpi_lir[4]; /* 0x70 GPI Level Invert */
++ u16 gpo_dsr[2]; /* 0x78 GPO Data Set */
++ u16 gpo_doecr[2]; /* 0x7c GPO Data OE Control */
++ u16 gp_iarcr[2]; /* 0x80 GP Internal Active Reg Control */
++ u16 gp_iarlcr[2]; /* 0x84 GP Internal Active Reg Level Con*/
++ u8 gpi_bcr[4]; /* 0x88 GPI Buffer Control */
++ u16 gpa_iarcr; /* 0x8c GPa Internal Active Reg Control */
++ u8 x06[2];
++ u16 gpa_iarlcr; /* 0x90 GPa Internal Active Reg Level Co*/
++ u8 x07[2];
++ u16 gpa_bcr; /* 0x94 GPa Buffer Control */
++ u8 x08[2];
++ u16 ccr; /* 0x98 Clock Control */
++ u16 pll2cr; /* 0x9a PLL2 Control */
++ u16 pll1cr[2]; /* 0x9c PLL1 Control */
++ u8 diarcr; /* 0xa0 Device Internal Active Reg Contr*/
++ u8 dbocr; /* 0xa1 Device Buffer Off Control */
++ u8 x09[0x3e];
++ u8 fer; /* 0xe0 Function Enable */
++ u8 x10[3];
++ u16 mcr; /* 0xe4 Mode Control */
++ u8 x11[0x14];
++ u8 config; /* 0xfc Configuration Control */
++ u8 x12[2];
++ u8 debug; /* 0xff Debug */
++} __attribute__ ((packed));
++
++union tc6393_scr_fer {
++ u8 raw;
++struct {
++ unsigned usben:1; /* D0 USB enable */
++ unsigned lcdcven:1; /* D1 polysylicon TFT enable */
++ unsigned slcden:1; /* D2 SLCD enable */
++} __attribute__ ((packed));
++} __attribute__ ((packed));
++
++union tc6393_scr_ccr {
++ u16 raw;
++struct {
++ unsigned ck32ken:1; /* D0 SD host clock enable */
++ unsigned usbcken:1; /* D1 USB host clock enable */
++ unsigned x00:2;
++ unsigned sharp:1; /* D4 ??? set in Sharp's code */
++ unsigned x01:3;
++ enum { disable = 0,
++ m12MHz = 1,
++ m24MHz = 2,
++ m48MHz = 3,
++ } mclksel:3; /* D10-D8 LCD controller clock */
++ unsigned x02:1;
++ enum { h24MHz = 0,
++ h48MHz = 1,
++ } hclksel:2; /* D13-D12 host bus clock */
++ unsigned x03:2;
++} __attribute__ ((packed));
++} __attribute__ ((packed));
++
++/*--------------------------------------------------------------------------*/
++
++struct tc6393 {
++ spinlock_t lock; /* read-modify-write lock */
++ struct device* dev; /* TC6393 device */
++ struct tc6393_scr __iomem *scr; /* system configuration reg */
++
++ struct resource rscr; /* system config reg resource */
++ struct resource* iomem; /* entire TC6393 iomem resource */
++ unsigned int irq; /* hardware cascade irq */
++
++ struct tmio_device tdev [TC6393_NUM_CELLS];
++};
++
++/*--------------------------------------------------------------------------*/
++
++static u32 tc6393_ioread32 (const void __iomem *addr)
++{
++ return ((u32) ioread16 (addr)) | (((u32) ioread16 (addr + 2)) << 16);
++}
++
++static u32 tc6393_iowrite32 (u32 val, const void __iomem *addr)
++{
++ iowrite16 (val, addr);
++ iowrite16 (val >> 16, addr + 2);
++ return val;
++}
++
++u32 get_tc6393_gpio (struct device *dev)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++
++ return tc6393_ioread32 (scr->gpo_dsr);
++}
++EXPORT_SYMBOL (get_tc6393_gpio);
++
++u32 set_tc6393_gpio (struct device *dev, u32 bits)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ unsigned long flags;
++ u32 dsr;
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ dsr = tc6393_ioread32 (scr->gpo_dsr) | bits;
++ tc6393_iowrite32 (dsr, scr->gpo_dsr);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++
++ return dsr;
++}
++EXPORT_SYMBOL (set_tc6393_gpio);
++
++u32 reset_tc6393_gpio (struct device *dev, u32 bits)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ unsigned long flags;
++ u32 dsr;
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ dsr = tc6393_ioread32 (scr->gpo_dsr) & ~bits;
++ tc6393_iowrite32 (dsr, scr->gpo_dsr);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++
++ return dsr;
++}
++EXPORT_SYMBOL (reset_tc6393_gpio);
++
++/*--------------------------------------------------------------------------*/
++
++static void
++tc6393_irq (unsigned int irq, struct irq_desc *desc)
++{
++ struct tc6393* tc6393 = get_irq_chip_data (irq);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ unsigned int isr;
++ unsigned int bit;
++ unsigned int i;
++
++ desc->chip->ack (irq);
++
++ while ((isr = ioread8(&scr->isr) & ~ioread8(&scr->imr)))
++ for (bit = 1, i = IRQ_TC6393_START; i <= IRQ_TC6393_LCD;
++ bit <<= 1, i++)
++ if (isr & bit)
++ desc_handle_irq (i, irq_desc + i);
++}
++
++static void tc6393_irq_ack (unsigned int irq)
++{
++}
++
++static void tc6393_irq_mask (unsigned int irq)
++{
++ struct tc6393* tc6393 = get_irq_chip_data (irq);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ unsigned long flags;
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ iowrite8 (ioread8 (&scr->imr) | (1 << (irq - IRQ_TC6393_START)),
++ &scr->imr);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++}
++
++static void tc6393_irq_unmask (unsigned int irq)
++{
++ struct tc6393* tc6393 = get_irq_chip_data (irq);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ unsigned long flags;
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ iowrite8 (ioread8 (&scr->imr) & ~(1 << (irq - IRQ_TC6393_START)),
++ &scr->imr);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++}
++
++static struct irq_chip tc6393_chip = {
++ .ack = tc6393_irq_ack,
++ .mask = tc6393_irq_mask,
++ .unmask = tc6393_irq_unmask,
++};
++
++static void tc6393_attach_irq (struct tc6393 *tc6393)
++{
++ unsigned int irq;
++
++ for (irq = IRQ_TC6393_START; irq <= IRQ_TC6393_LCD; irq++) {
++ set_irq_chip (irq, &tc6393_chip);
++ set_irq_chip_data(irq, tc6393);
++ set_irq_handler (irq, handle_edge_irq);
++ set_irq_flags (irq, IRQF_VALID | IRQF_PROBE);
++ }
++
++ set_irq_type (tc6393->irq, IRQT_FALLING);
++ set_irq_chip_data (tc6393->irq, tc6393);
++ set_irq_chained_handler (tc6393->irq, tc6393_irq);
++}
++
++static void tc6393_detach_irq (struct tc6393 *tc6393)
++{
++ unsigned int irq;
++
++ set_irq_chained_handler (tc6393->irq, NULL);
++ set_irq_chip_data (tc6393->irq, NULL);
++
++ for (irq = IRQ_TC6393_START; irq <= IRQ_TC6393_LCD; irq++) {
++ set_irq_flags (irq, 0);
++ set_irq_chip (irq, NULL);
++ set_irq_chip_data(irq, NULL);
++ }
++}
++
++/*--------------------------------------------------------------------------*/
++
++static int tc6393_bus_match (struct device *dev, struct device_driver *drv)
++{
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ const struct tc6393_cell* cell = tdev->soc_data;
++
++ return !strcmp (cell->name, drv->name);
++}
++
++static int tc6393_bus_suspend (struct device *dev, pm_message_t state)
++{
++ struct device_driver* drv = dev->driver;
++ return drv && drv->suspend ? drv->suspend (dev, state) : 0;
++}
++
++static int tc6393_bus_resume (struct device *dev)
++{
++ struct device_driver* drv = dev->driver;
++ return drv && drv->resume ? drv->resume (dev) : 0;
++}
++
++struct bus_type tc6393_bus_type = {
++ .name = TMIO_NAME_BUS,
++ .match = tc6393_bus_match,
++ .suspend = tc6393_bus_suspend,
++ .resume = tc6393_bus_resume,
++};
++EXPORT_SYMBOL (tc6393_bus_type);
++
++/*--------------------------------------------------------------------------*/
++
++static void tc6393_cell_clock (struct device *dev, int enable)
++{
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ const struct tc6393_cell* cell = tdev->soc_data;
++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ union tc6393_scr_ccr ccr;
++ unsigned long flags;
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ ccr.raw = ioread16 (&scr->ccr);
++
++ switch (cell->id) {
++ case TC6393_CELL_SD: ccr.ck32ken = enable; break;
++ case TC6393_CELL_OHCI: ccr.usbcken = enable; break;
++ case TC6393_CELL_LCD:
++ ccr.mclksel = enable ? m48MHz : disable;
++ break;
++ }
++
++ printk (KERN_DEBUG TMIO_NAME_CORE ": scr->ccr = %04x\n", ccr.raw);
++
++ iowrite16(ccr.raw, &scr->ccr);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++}
++
++static void tc6393_cell_function (struct device *dev, int enable)
++{
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ const struct tc6393_cell* cell = tdev->soc_data;
++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ union tc6393_scr_fer fer;
++ unsigned long flags;
++
++ if (cell->id == TC6393_CELL_NAND) {
++ if (enable) {
++ /* SMD buffer on */
++ printk (KERN_DEBUG TMIO_NAME_CORE ": SMD buffer on\n");
++ iowrite8 (0xff, scr->gpi_bcr + 1);
++ }
++ return;
++ }
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ fer.raw = ioread16 (&scr->fer);
++
++ switch (cell->id) {
++ case TC6393_CELL_OHCI: fer.usben = enable; break;
++ case TC6393_CELL_LCD: fer.slcden = enable; break;
++ }
++
++ printk (KERN_DEBUG TMIO_NAME_CORE ": scr->fer = %02x\n", fer.raw);
++
++ iowrite8 (fer.raw, &scr->fer);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++}
++
++static void
++tc6393_lcd_mode (struct device *dev, const struct fb_videomode *mode)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++
++ iowrite16 (mode->pixclock, scr->pll1cr + 0);
++ iowrite16 (mode->pixclock >> 16, scr->pll1cr + 1);
++}
++
++static struct tmio_cell_ops tc6393_cell_ops = {
++ .clock = tc6393_cell_clock,
++ .function = tc6393_cell_function,
++ .lcd_mode = tc6393_lcd_mode,
++};
++
++static void tc6393_device_release (struct device *dev)
++{
++}
++
++static int
++tc6393_device_register (struct tc6393 *tc6393, struct tmio_cell *tcell)
++{
++ const struct tc6393_cell* cell;
++ struct tmio_device* tdev;
++ struct device* dev;
++ int i;
++
++ for (i = 0; strcmp (tcell->name, tc6393_cell [i].name); )
++ if (++i >= ARRAY_SIZE(tc6393_cell))
++ return -EINVAL;
++
++ cell = tc6393_cell + i;
++ tdev = tc6393->tdev + i;
++ dev = &tdev->dev;
++
++ tdev->ops = &tc6393_cell_ops;
++ tdev->iomem = tc6393->iomem;
++ tdev->soc_data = (void*) cell;
++
++ dev->parent = tc6393->dev;
++ strncpy (dev->bus_id, cell->name, sizeof dev->bus_id);
++ dev->bus = &tc6393_bus_type;
++ dev->dma_mask = tc6393->dev->dma_mask;
++ dev->coherent_dma_mask = tc6393->dev->coherent_dma_mask;
++ dev->release = tc6393_device_release;
++ dev->platform_data = tcell->platform_data;
++
++ for (i=0; i < cell->num_resources; i++) {
++ const struct resource* cr = cell->resource + i;
++ struct resource* dr = tdev->resource + i;
++
++ dr->name = cr->name;
++ dr->start = cr->start;
++ dr->end = cr->end;
++ dr->flags = cr->flags;
++
++ /* convert memory offsets to absolutes */
++ if (cr->flags & IORESOURCE_MEM) {
++ dr->start += tc6393->iomem->start;
++ dr->end += tc6393->iomem->start;
++ }
++ }
++
++ return device_register (dev);
++}
++
++/*--------------------------------------------------------------------------*/
++
++static void tc6393_hw_init (struct tc6393 *tc6393)
++{
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ struct tc6393_platform_data* tcpd = tc6393->dev->platform_data;
++
++ tcpd->enable (tc6393->dev);
++
++ iowrite8 (0, &scr->fer);
++ iowrite16(tcpd->scr_pll2cr, &scr->pll2cr);
++ iowrite16(tcpd->scr_ccr, &scr->ccr);
++ iowrite16(tcpd->scr_mcr, &scr->mcr);
++ iowrite16(tcpd->scr_gper, &scr->gper);
++ iowrite8 (0, &scr->irr);
++ iowrite8 (0xbf, &scr->imr);
++ iowrite16(tcpd->scr_gpo_dsr, scr->gpo_dsr + 0);
++ iowrite16(tcpd->scr_gpo_dsr >> 16, scr->gpo_dsr + 1);
++ iowrite16(tcpd->scr_gpo_doecr, scr->gpo_doecr + 0);
++ iowrite16(tcpd->scr_gpo_doecr >> 16, scr->gpo_doecr + 1);
++}
++
++static int tc6393_probe (struct device *dev)
++{
++ struct platform_device* pdev = to_platform_device (dev);
++ struct tc6393_platform_data* tcpd = dev->platform_data;
++ struct tc6393* tc6393;
++ struct resource* iomem;
++ struct resource* rscr;
++ int retval;
++ int i;
++
++ iomem = platform_get_resource (pdev, IORESOURCE_MEM, 0);
++ if (!iomem)
++ return -EINVAL;
++
++ tc6393 = kzalloc (sizeof *tc6393, GFP_KERNEL);
++ if (!tc6393) {
++ retval = -ENOMEM;
++ goto err_kzalloc;
++ }
++
++ dev_set_drvdata (dev, tc6393);
++ spin_lock_init (&tc6393->lock);
++ tc6393->dev = dev;
++ tc6393->iomem = iomem;
++ tc6393->irq = platform_get_irq (pdev, 0);
++
++ rscr = &tc6393->rscr;
++ rscr->name = TMIO_NAME_CORE;
++ rscr->start = iomem->start;
++ rscr->end = iomem->start + 0xff;
++ rscr->flags = IORESOURCE_MEM;
++
++ retval = request_resource (iomem, rscr);
++ if (retval)
++ goto err_request_scr;
++
++ tc6393->scr = ioremap (rscr->start, rscr->end - rscr->start + 1);
++ if (!tc6393->scr) {
++ retval = -ENOMEM;
++ goto err_ioremap;
++ }
++
++ tc6393_hw_init (tc6393);
++
++ printk (KERN_INFO "Toshiba %s revision %d at 0x%08lx, irq %d\n",
++ TMIO_SOC_NAME, ioread8 (&tc6393->scr->revid),
++ iomem->start, tc6393->irq);
++
++ if (tc6393->irq)
++ tc6393_attach_irq (tc6393);
++
++ for (i = 0; i < tcpd->num_cells; i++)
++ tc6393_device_register (tc6393, tcpd->cell + i);
++
++ return 0;
++
++err_ioremap:
++ release_resource (rscr);
++err_request_scr:
++ kfree(tc6393);
++err_kzalloc:
++ release_resource (iomem);
++ return retval;
++}
++
++static int tc6393_dev_remove (struct device *dev, void *data)
++{
++ device_unregister (dev);
++ return 0;
++}
++
++static int tc6393_remove (struct device *dev)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev);
++
++ device_for_each_child (dev, tc6393, tc6393_dev_remove);
++
++ if (tc6393->irq)
++ tc6393_detach_irq (tc6393);
++
++ iounmap (tc6393->scr);
++ release_resource (&tc6393->rscr);
++ release_resource (tc6393->iomem);
++ kfree (tc6393);
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int tc6393_suspend (struct device *dev, pm_message_t state)
++{
++ struct tc6393_platform_data* tcpd = dev->platform_data;
++ tcpd->disable (dev);
++ return 0;
++}
++
++static int tc6393_resume (struct device *dev)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev);
++ tc6393_hw_init (tc6393);
++ return 0;
++}
++#endif
++
++static struct device_driver tc6393_device_driver = {
++ .name = TMIO_SOC_NAME,
++ .bus = &platform_bus_type,
++ .probe = tc6393_probe,
++ .remove = tc6393_remove,
++#ifdef CONFIG_PM
++ .suspend = tc6393_suspend,
++ .resume = tc6393_resume,
++#endif
++};
++
++/*--------------------------------------------------------------------------*/
++
++static int __init tc6393_init (void)
++{
++ int retval = bus_register (&tc6393_bus_type);
++ if (retval)
++ return retval;
++
++ return driver_register (&tc6393_device_driver);
++}
++
++static void __exit tc6393_exit (void)
++{
++ driver_unregister (&tc6393_device_driver);
++ bus_unregister (&tc6393_bus_type);
++}
++
++module_init (tc6393_init);
++module_exit (tc6393_exit);
++
++MODULE_DESCRIPTION ("TC6393 SoC bus driver");
++MODULE_AUTHOR ("Chris Humbert, Dirk Opfer");
++MODULE_LICENSE ("GPL");
+Index: git/arch/arm/common/Kconfig
+===================================================================
+--- git.orig/arch/arm/common/Kconfig 2006-10-31 16:08:28.000000000 +0000
++++ git/arch/arm/common/Kconfig 2006-11-07 22:13:09.000000000 +0000
+@@ -31,3 +31,6 @@ config SHARPSL_PM
+
+ config SHARP_SCOOP
+ bool
++
++config TOSHIBA_TC6393XB
++ bool
+Index: git/arch/arm/mach-pxa/Kconfig
+===================================================================
+--- git.orig/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:06.000000000 +0000
++++ git/arch/arm/mach-pxa/ 2006-11-07 23:30:34.000000000 +0000
+@@ -128,6 +128,7 @@ config MACH_BORZOI
+ config MACH_TOSA
+ bool "Enable Sharp SL-6000x (Tosa) Support"
+ depends on PXA_SHARPSL_25x
++ select TOSHIBA_TC6393XB
+
+ config PXA25x
+ bool
+Index: git/arch/arm/common/Makefile
+===================================================================
+--- git.orig/arch/arm/common/Makefile 2006-10-31 16:08:28.000000000 +0000
++++ git/arch/arm/common/Makefile 2006-11-07 22:13:09.000000000 +0000
+@@ -17,3 +17,4 @@ obj-$(CONFIG_SHARPSL_PM) += sharpsl_pm.o
+ obj-$(CONFIG_SHARP_SCOOP) += scoop.o
+ obj-$(CONFIG_ARCH_IXP2000) += uengine.o
+ obj-$(CONFIG_ARCH_IXP23XX) += uengine.o
++obj-$(CONFIG_TOSHIBA_TC6393XB) += tc6393xb.o
+Index: git/include/asm-arm/hardware/tmio.h
+===================================================================
+--- git.orig/include/asm-arm/hardware/tmio.h 2006-11-07 22:13:09.000000000 +0000
++++ git/include/asm-arm/hardware/tmio.h 2006-11-07 22:13:09.000000000 +0000
+@@ -91,6 +91,50 @@ struct tmio_device {
+
+ /*--------------------------------------------------------------------------*/
+
++/*
++ * TC6393XB SoC
++ */
++#ifdef CONFIG_TOSHIBA_TC6393XB
++#define TMIO_SOC_TC6393XB
++#define TMIO_SOC_NAME "TC6393XB"
++#define TMIO_NAME_BUS "tc6393-bus"
++#define TMIO_NAME_CORE "tc6393-core"
++#define TMIO_NAME_NAND "tc6393-nand"
++#define TMIO_NAME_SD "tc6393-sd"
++#define TMIO_NAME_OHCI "tc6393-ohci"
++#define TMIO_NAME_SERIAL "tc6393-serial"
++#define TMIO_NAME_LCD "tc6393-lcd"
++#define tmio_bus_type tc6393_bus_type
++
++#define TC6393_GPIO(x) (1 << (x))
++
++extern struct bus_type tc6393_bus_type;
++
++struct tc6393_platform_data {
++ u16 scr_pll2cr; /* PLL2 Control */
++ u16 scr_ccr; /* Clock Control */
++ u16 scr_mcr; /* Mode Control */
++ u16 scr_gper; /* GP Enable */
++ u32 scr_gpo_doecr; /* GPO Data OE Control */
++ u32 scr_gpo_dsr; /* GPO Data Set */
++
++ /* cells to register as devices */
++ struct tmio_cell* cell;
++ unsigned int num_cells;
++
++ /* callbacks to enable and disable the TC6393XB's power and clock */
++ void (*enable) (struct device *dev);
++ void (*disable) (struct device *dev);
++};
++
++u32 get_tc6393_gpio (struct device *dev);
++u32 set_tc6393_gpio (struct device *dev, u32 bits);
++u32 reset_tc6393_gpio (struct device *dev, u32 bits);
++
++/*--------------------------------------------------------------------------*/
++
++#else
+ #error "no TMIO SoC configured"
++#endif
+
+ #endif
+Index: git/include/asm-arm/arch-pxa/irqs.h
+===================================================================
+--- git.orig/include/asm-arm/arch-pxa/irqs.h 2006-10-31 16:09:33.000000000 +0000
++++ git/include/asm-arm/arch-pxa/irqs.h 2006-11-07 22:13:09.000000000 +0000
+@@ -163,17 +163,27 @@
+ #define IRQ_LOCOMO_SPI_OVRN (IRQ_BOARD_END + 20)
+ #define IRQ_LOCOMO_SPI_TEND (IRQ_BOARD_END + 21)
+
++#define IRQ_TC6393_START (IRQ_BOARD_END)
++#define IRQ_TC6393_NAND (IRQ_BOARD_END + 0)
++#define IRQ_TC6393_SD (IRQ_BOARD_END + 1)
++#define IRQ_TC6393_OHCI (IRQ_BOARD_END + 2)
++#define IRQ_TC6393_SERIAL (IRQ_BOARD_END + 3)
++#define IRQ_TC6393_LCD (IRQ_BOARD_END + 4)
++
+ /*
+ * Figure out the MAX IRQ number.
+ *
+ * If we have an SA1111, the max IRQ is S1_BVD1_STSCHG+1.
+ * If we have an LoCoMo, the max IRQ is IRQ_LOCOMO_SPI_TEND+1
++ * If we have an TC6393XB, the max IRQ is IRQ_TC6393_LCD+1
+ * Otherwise, we have the standard IRQs only.
+ */
+ #ifdef CONFIG_SA1111
+ #define NR_IRQS (IRQ_S1_BVD1_STSCHG + 1)
+ #elif defined(CONFIG_SHARP_LOCOMO)
+ #define NR_IRQS (IRQ_LOCOMO_SPI_TEND + 1)
++#elif defined(CONFIG_TOSHIBA_TC6393XB)
++#define NR_IRQS (IRQ_TC6393_LCD + 1)
+ #elif defined(CONFIG_ARCH_LUBBOCK) || \
+ defined(CONFIG_MACH_LOGICPD_PXA270) || \
+ defined(CONFIG_MACH_MAINSTONE)
diff --git a/packages/linux/linux-rp-2.6.22/tosa-keyboard-r19.patch b/packages/linux/linux-rp-2.6.22/tosa-keyboard-r19.patch
new file mode 100644
index 0000000000..948c27fdce
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/tosa-keyboard-r19.patch
@@ -0,0 +1,514 @@
+ drivers/input/keyboard/Kconfig | 12 -
+ drivers/input/keyboard/Makefile | 1
+ drivers/input/keyboard/tosakbd.c | 467 +++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 479 insertions(+), 1 deletion(-)
+
+Index: git/drivers/input/keyboard/Kconfig
+===================================================================
+--- git.orig/drivers/input/keyboard/Kconfig 2006-10-31 16:08:57.000000000 +0000
++++ git/drivers/input/keyboard/Kconfig 2006-11-07 22:13:10.000000000 +0000
+@@ -148,12 +148,22 @@ config KEYBOARD_SPITZ
+ depends on PXA_SHARPSL
+ default y
+ help
+- Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000,
++ Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000,
+ SL-C3000 and Sl-C3100 series of PDAs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called spitzkbd.
+
++config KEYBOARD_TOSA
++ tristate "Tosa keyboard"
++ depends on PXA_SHARPSL
++ default y
++ help
++ Say Y here to enable the keyboard on the Sharp Zaurus SL-6000x (Tosa)
++
++ To compile this driver as a module, choose M here: the
++ module will be called tosakbd.
++
+ config KEYBOARD_AMIGA
+ tristate "Amiga keyboard"
+ depends on AMIGA
+Index: git/drivers/input/keyboard/Makefile
+===================================================================
+--- git.orig/drivers/input/keyboard/Makefile 2006-10-31 16:08:57.000000000 +0000
++++ git/drivers/input/keyboard/Makefile 2006-11-07 22:13:10.000000000 +0000
+@@ -17,3 +17,4 @@ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkb
+ obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
+ obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
+ obj-$(CONFIG_KEYBOARD_ASIC3) += asic3_keys.o
++obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o
+Index: git/drivers/input/keyboard/tosakbd.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ git/drivers/input/keyboard/tosakbd.c 2006-11-07 23:27:19.000000000 +0000
+@@ -0,0 +1,467 @@
++/*
++ * Keyboard driver for Sharp Tosa models (SL-6000x)
++ *
++ * Copyright (c) 2005 Dirk Opfer
++ *
++ * Based on xtkbd.c/locomkbd.c/corgikbd.c
++ *
++ * 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.
++ *
++ */
++
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/interrupt.h>
++#include <linux/jiffies.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++
++#include <asm/arch/tosa.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch/pxa-regs.h>
++
++
++#define TOSA_KEY_STROBE_NUM (11)
++#define TOSA_KEY_SENSE_NUM (7)
++
++#define KEYMASK_ON (0x1<<0)
++#define KEYMASK_REC (0x1<<1)
++#define KEYMASK_SYNC (0x1<<2)
++
++#define KB_ROWS 7
++#define KB_COLS 11
++#define KB_ROWMASK(r) (1 << (r))
++#define SCANCODE(r,c) ( ((r)<<4) + (c) + 1 )
++#define NR_SCANCODES (SCANCODE(KB_ROWS-1,KB_COLS)+1+1)
++
++#define SCAN_INTERVAL (HZ/10)
++#define HP_SCAN_INTERVAL (150) /* ms */
++#define HP_STABLE_COUNT 2
++
++#define TOSA_KEY_CALENDER KEY_F1
++#define TOSA_KEY_ADDRESS KEY_F2
++#define TOSA_KEY_FN KEY_F3
++#define TOSA_KEY_CANCEL KEY_F4
++#define TOSA_KEY_OFF KEY_SUSPEND
++#define TOSA_KEY_CENTER KEY_F5
++#define TOSA_KEY_REC KEY_F6
++#define TOSA_KEY_LIGHT KEY_F7
++#define TOSA_KEY_RECORD KEY_F8
++#define TOSA_KEY_HOME KEY_F9
++#define TOSA_KEY_MAIL KEY_F10
++#define TOSA_KEY_OK KEY_F11
++#define TOSA_KEY_MENU KEY_F12
++#define TOSA_KEY_SYNC KEY_F13
++
++#define GET_ROWS_STATUS(c) ((GPLR2 & TOSA_GPIO_ALL_SENSE_BIT) >> TOSA_GPIO_ALL_SENSE_RSHIFT)
++#define KB_DISCHARGE_DELAY 10
++#define KB_ACTIVATE_DELAY 10
++
++
++static unsigned char tosakbd_keycode[NR_SCANCODES] = {
++ 0, /* 0 */
++ 0, KEY_W, 0, 0, 0, KEY_K, KEY_BACKSPACE, KEY_P, 0, 0, 0, TOSA_KEY_OFF, 0, 0, 0, 0, /*1 - 16*/
++ KEY_Q, KEY_E, KEY_T, KEY_Y, 0, KEY_O, KEY_I, KEY_COMMA, 0, 0, 0, TOSA_KEY_RECORD, 0, 0, 0, 0, /*17 - 32*/
++ KEY_A, KEY_D, KEY_G, KEY_U, 0, KEY_L, KEY_ENTER, KEY_DOT, 0, 0, 0, TOSA_KEY_SYNC, 0, 0, 0, 0, /*33 - 48*/
++ KEY_Z, KEY_C, KEY_V, KEY_J, TOSA_KEY_ADDRESS, TOSA_KEY_CANCEL, TOSA_KEY_CENTER, TOSA_KEY_OK, KEY_LEFTSHIFT, 0 , 0,0 , 0, 0, 0, 0, /*49 - 64*/
++ KEY_S, KEY_R, KEY_B, KEY_N, TOSA_KEY_CALENDER, TOSA_KEY_HOME, TOSA_KEY_REC, TOSA_KEY_LIGHT, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /*65 - 80*/
++ KEY_TAB, KEY_SLASH, KEY_H, KEY_M, TOSA_KEY_MENU, 0, KEY_UP, 0, 0, 0, TOSA_KEY_FN, 0, 0, 0, 0, 0, /*81 - 96*/
++ KEY_X, KEY_F, KEY_SPACE, KEY_APOSTROPHE, TOSA_KEY_MAIL, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, /*97 - 109*/
++};
++
++struct tosakbd {
++ unsigned char keycode[ARRAY_SIZE(tosakbd_keycode)];
++ struct input_dev *input;
++
++ spinlock_t lock;
++ struct timer_list timer;
++ struct timer_list hptimer;
++
++ int hp_state;
++ int hp_count;
++
++ unsigned int suspended;
++ unsigned long suspend_jiffies;
++};
++
++/* Helper functions for reading the keyboard matrix
++ * Note: We should really be using pxa_gpio_mode to alter GPDR but it
++ * requires a function call per GPIO bit which is excessive
++ * when we need to access 12 bits at once, multiple times.
++ * These functions must be called within local_irq_save()/local_irq_restore()
++ * or similar.
++ */
++static inline void tosakbd_discharge_all(void)
++{
++ /* STROBE All HiZ */
++ GPCR1 = TOSA_GPIO_HIGH_STROBE_BIT;
++ GPDR1 &= ~TOSA_GPIO_HIGH_STROBE_BIT;
++ GPCR2 = TOSA_GPIO_LOW_STROBE_BIT;
++ GPDR2 &= ~TOSA_GPIO_LOW_STROBE_BIT;
++}
++
++static inline void tosakbd_activate_all(void)
++{
++ /* STROBE ALL -> High */
++ GPSR1 = TOSA_GPIO_HIGH_STROBE_BIT;
++ GPDR1 |= TOSA_GPIO_HIGH_STROBE_BIT;
++ GPSR2 = TOSA_GPIO_LOW_STROBE_BIT;
++ GPDR2 |= TOSA_GPIO_LOW_STROBE_BIT;
++
++ udelay(KB_DISCHARGE_DELAY);
++
++ /* STATE CLEAR */
++ GEDR2 |= TOSA_GPIO_ALL_SENSE_BIT;
++}
++
++static inline void tosakbd_activate_col(int col)
++{
++ if (col<=5) {
++ /* STROBE col -> High, not col -> HiZ */
++ GPSR1 = TOSA_GPIO_STROBE_BIT(col);
++ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
++ } else {
++ /* STROBE col -> High, not col -> HiZ */
++ GPSR2 = TOSA_GPIO_STROBE_BIT(col);
++ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
++ }
++}
++
++static inline void tosakbd_reset_col(int col)
++{
++ if (col<=5) {
++ /* STROBE col -> Low */
++ GPCR1 = TOSA_GPIO_STROBE_BIT(col);
++ /* STROBE col -> out, not col -> HiZ */
++ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
++ } else {
++ /* STROBE col -> Low */
++ GPCR2 = TOSA_GPIO_STROBE_BIT(col);
++ /* STROBE col -> out, not col -> HiZ */
++ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
++ }
++}
++
++/*
++ * Read the GPIOs for POWER, RECORD and SYNC
++ */
++static int read_port_key_status_raw(void)
++{
++ int val=0;
++
++ /* Power key */
++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_ON_KEY))==0)
++ val |= KEYMASK_ON;
++ /* Record key */
++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_RECORD_BTN))==0)
++ val |= KEYMASK_REC;
++ /* Sync key */
++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_SYNC))==0)
++ val |= KEYMASK_SYNC;
++ return val;
++}
++
++
++/*
++ * The tosa keyboard only generates interrupts when a key is pressed.
++ * So when a key is pressed, we enable a timer. This timer scans the
++ * keyboard, and this is how we detect when the key is released.
++ */
++
++/* Scan the hardware keyboard and push any changes up through the input layer */
++static void tosakbd_scankeyboard(struct tosakbd *tosakbd_data)
++{
++ unsigned int row, col, rowd;
++ unsigned long flags;
++ unsigned int num_pressed = 0;
++
++ if (tosakbd_data->suspended)
++ return;
++
++ spin_lock_irqsave(&tosakbd_data->lock, flags);
++
++ for (col = 0; col < KB_COLS; col++) {
++ /*
++ * Discharge the output driver capacitatance
++ * in the keyboard matrix. (Yes it is significant..)
++ */
++ tosakbd_discharge_all();
++ udelay(KB_DISCHARGE_DELAY);
++
++ tosakbd_activate_col( col);
++ udelay(KB_ACTIVATE_DELAY);
++
++ rowd = GET_ROWS_STATUS(col);
++
++ for (row = 0; row < KB_ROWS; row++) {
++ unsigned int scancode, pressed;
++ scancode = SCANCODE(row, col);
++ pressed = rowd & KB_ROWMASK(row);
++ input_report_key(tosakbd_data->input, tosakbd_data->keycode[scancode], pressed);
++ if (pressed)
++ num_pressed++;
++ }
++
++ tosakbd_reset_col(col);
++ }
++
++ tosakbd_activate_all();
++
++ rowd = read_port_key_status_raw();
++
++ for (row = 0; row < 3; row++ ) {
++ unsigned int scancode, pressed;
++ scancode = SCANCODE(row, KB_COLS);
++ pressed = rowd & KB_ROWMASK(row);
++ input_report_key(tosakbd_data->input, tosakbd_data->keycode[scancode], pressed);
++ if (pressed)
++ num_pressed++;
++
++ if (pressed && (tosakbd_data->keycode[scancode] == TOSA_KEY_OFF)
++ && time_after(jiffies, tosakbd_data->suspend_jiffies + msecs_to_jiffies(1000))) {
++ input_event(tosakbd_data->input, EV_PWR, TOSA_KEY_OFF, 1);
++ tosakbd_data->suspend_jiffies = jiffies;
++ }
++ }
++
++ input_sync(tosakbd_data->input);
++
++ /* if any keys are pressed, enable the timer */
++ if (num_pressed)
++ mod_timer(&tosakbd_data->timer, jiffies + SCAN_INTERVAL);
++
++ spin_unlock_irqrestore(&tosakbd_data->lock, flags);
++}
++
++/*
++ * tosa keyboard interrupt handler.
++ */
++static irqreturn_t tosakbd_interrupt(int irq, void *dev_id)
++{
++ struct tosakbd *tosakbd_data = dev_id;
++
++ if (!timer_pending(&tosakbd_data->timer))
++ {
++ /** wait chattering delay **/
++ udelay(20);
++ tosakbd_scankeyboard(tosakbd_data);
++ }
++
++ return IRQ_HANDLED;
++}
++
++/*
++ * tosa timer checking for released keys
++ */
++static void tosakbd_timer_callback(unsigned long data)
++{
++ struct tosakbd *tosakbd_data = (struct tosakbd *) data;
++ tosakbd_scankeyboard(tosakbd_data);
++}
++
++/*
++ * The headphone generates an interrupt.
++ * We debounce the switche and pass them to the input system.
++ */
++
++static irqreturn_t tosakbd_hp_isr(int irq, void *dev_id)
++{
++ struct tosakbd *tosakbd_data = dev_id;
++
++ if (!timer_pending(&tosakbd_data->hptimer))
++ mod_timer(&tosakbd_data->hptimer, jiffies + msecs_to_jiffies(HP_SCAN_INTERVAL));
++
++ return IRQ_HANDLED;
++}
++
++static void tosakbd_hp_timer(unsigned long data)
++{
++ struct tosakbd *tosakbd_data = (struct tosakbd *) data;
++ unsigned long state;
++ unsigned long flags;
++
++ state = (GPLR(TOSA_GPIO_EAR_IN) & GPIO_bit(TOSA_GPIO_EAR_IN));
++ if (state != tosakbd_data->hp_state) {
++ tosakbd_data->hp_count = 0;
++ tosakbd_data->hp_state = state;
++ } else if (tosakbd_data->hp_count < HP_STABLE_COUNT) {
++ tosakbd_data->hp_count++;
++ }
++
++ if (tosakbd_data->hp_count >= HP_STABLE_COUNT) {
++ spin_lock_irqsave(&tosakbd_data->lock, flags);
++
++ input_report_switch(tosakbd_data->input, SW_HEADPHONE_INSERT, ((GPLR(TOSA_GPIO_EAR_IN) & GPIO_bit(TOSA_GPIO_EAR_IN)) == 0));
++ input_sync(tosakbd_data->input);
++
++ spin_unlock_irqrestore(&tosakbd_data->lock, flags);
++ } else {
++ mod_timer(&tosakbd_data->hptimer, jiffies + msecs_to_jiffies(HP_SCAN_INTERVAL));
++ }
++}
++
++#ifdef CONFIG_PM
++static int tosakbd_suspend(struct platform_device *dev, pm_message_t state)
++{
++ struct tosakbd *tosakbd = platform_get_drvdata(dev);
++
++ tosakbd->suspended = 1;
++
++ return 0;
++}
++
++static int tosakbd_resume(struct platform_device *dev)
++{
++ struct tosakbd *tosakbd = platform_get_drvdata(dev);
++
++ /* Upon resume, ignore the suspend key for a short while */
++ tosakbd->suspend_jiffies = jiffies;
++ tosakbd->suspended = 0;
++
++ return 0;
++}
++#else
++#define tosakbd_suspend NULL
++#define tosakbd_resume NULL
++#endif
++
++static int __init tosakbd_probe(struct platform_device *pdev) {
++
++ int i;
++ struct tosakbd *tosakbd;
++ struct input_dev *input_dev;
++
++ tosakbd = kzalloc(sizeof(struct tosakbd), GFP_KERNEL);
++ if (!tosakbd)
++ return -ENOMEM;
++
++ input_dev = input_allocate_device();
++ if (!input_dev) {
++ kfree(tosakbd);
++ return -ENOMEM;
++ }
++
++ platform_set_drvdata(pdev,tosakbd);
++
++ spin_lock_init(&tosakbd->lock);
++
++ /* Init Keyboard rescan timer */
++ init_timer(&tosakbd->timer);
++ tosakbd->timer.function = tosakbd_timer_callback;
++ tosakbd->timer.data = (unsigned long) tosakbd;
++
++ /* Init Headphone Timer */
++ init_timer(&tosakbd->hptimer);
++ tosakbd->hptimer.function = tosakbd_hp_timer;
++ tosakbd->hptimer.data = (unsigned long) tosakbd;
++
++ tosakbd->suspend_jiffies = jiffies;
++
++ tosakbd->input = input_dev;
++
++ input_dev->private = tosakbd;
++ input_dev->name = "Tosa Keyboard";
++ input_dev->phys = "tosakbd/input0";
++ input_dev->cdev.dev = &pdev->dev;
++
++ input_dev->id.bustype = BUS_HOST;
++ input_dev->id.vendor = 0x0001;
++ input_dev->id.product = 0x0001;
++ input_dev->id.version = 0x0100;
++
++ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_PWR) | BIT(EV_SW);
++ input_dev->keycode = tosakbd->keycode;
++ input_dev->keycodesize = sizeof(unsigned char);
++ input_dev->keycodemax = ARRAY_SIZE(tosakbd_keycode);
++
++ memcpy(tosakbd->keycode, tosakbd_keycode, sizeof(tosakbd->keycode));
++ for (i = 0; i < ARRAY_SIZE(tosakbd_keycode); i++)
++ set_bit(tosakbd->keycode[i], input_dev->keybit);
++ clear_bit(0, input_dev->keybit);
++ set_bit(SW_HEADPHONE_INSERT, input_dev->swbit);
++
++ input_register_device(tosakbd->input);
++
++ /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
++ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) {
++ pxa_gpio_mode( TOSA_GPIO_KEY_SENSE(i) | GPIO_IN);
++ if (request_irq(TOSA_IRQ_GPIO_KEY_SENSE(i), tosakbd_interrupt,
++ IRQF_DISABLED | IRQF_TRIGGER_RISING, "tosakbd", tosakbd)) {
++ printk("tosakbd: Can't get IRQ: %d !\n", i);
++ }
++ }
++
++ /* Set Strobe lines as outputs - set high */
++ for (i = 0; i < TOSA_KEY_STROBE_NUM; i++) {
++ pxa_gpio_mode( TOSA_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
++ }
++
++ // Power&Rec Button
++ pxa_gpio_mode( TOSA_GPIO_ON_KEY | GPIO_IN);
++ pxa_gpio_mode( TOSA_GPIO_RECORD_BTN | GPIO_IN);
++ pxa_gpio_mode( TOSA_GPIO_SYNC | GPIO_IN);
++ pxa_gpio_mode( TOSA_GPIO_EAR_IN | GPIO_IN);
++
++ if (request_irq(TOSA_IRQ_GPIO_ON_KEY, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "On key", tosakbd) ||
++ request_irq(TOSA_IRQ_GPIO_RECORD_BTN, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "Record key", tosakbd) ||
++ request_irq(TOSA_IRQ_GPIO_SYNC, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "Sync key", tosakbd) ||
++ request_irq(TOSA_IRQ_GPIO_EAR_IN, tosakbd_hp_isr, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "HP in", tosakbd)) {
++ printk("Could not allocate KEYBD IRQ!\n");
++ }
++
++ printk(KERN_INFO "input: Tosa Keyboard Registered\n");
++
++ return 0;
++}
++
++static int tosakbd_remove(struct platform_device *dev) {
++
++ int i;
++ struct tosakbd *tosakbd = platform_get_drvdata(dev);
++
++ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++)
++ free_irq(TOSA_IRQ_GPIO_KEY_SENSE(i),tosakbd);
++
++ free_irq(TOSA_IRQ_GPIO_ON_KEY,tosakbd);
++ free_irq(TOSA_IRQ_GPIO_RECORD_BTN,tosakbd);
++ free_irq(TOSA_IRQ_GPIO_SYNC,tosakbd);
++
++ del_timer_sync(&tosakbd->timer);
++
++ input_unregister_device(tosakbd->input);
++
++ kfree(tosakbd);
++
++ return 0;
++}
++
++static struct platform_driver tosakbd_driver = {
++ .probe = tosakbd_probe,
++ .remove = tosakbd_remove,
++ .suspend = tosakbd_suspend,
++ .resume = tosakbd_resume,
++ .driver = {
++ .name = "tosa-keyboard",
++ },
++};
++
++static int __devinit tosakbd_init(void)
++{
++ return platform_driver_register(&tosakbd_driver);
++}
++
++static void __exit tosakbd_exit(void)
++{
++ platform_driver_unregister(&tosakbd_driver);
++}
++
++module_init(tosakbd_init);
++module_exit(tosakbd_exit);
++
++MODULE_AUTHOR("Dirk Opfer <Dirk@Opfer-Online.de>");
++MODULE_DESCRIPTION("Tosa Keyboard Driver");
++MODULE_LICENSE("GPLv2");
diff --git a/packages/linux/linux-rp-2.6.22/tosa-lcdnoise-r1-fix-r0.patch b/packages/linux/linux-rp-2.6.22/tosa-lcdnoise-r1-fix-r0.patch
new file mode 100644
index 0000000000..93a9c18720
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/tosa-lcdnoise-r1-fix-r0.patch
@@ -0,0 +1,135 @@
+From eada869814636157956641ba1503f0d6cc04e2b7 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 17:43:51 +0400
+Subject: [PATCH] tosa-lcdnoise-r1.patch fixes
+
+---
+ arch/arm/mach-pxa/tosa_lcd.c | 34 ++++++++++++++++++++++++++++++++++
+ drivers/input/touchscreen/tosa_ts.c | 9 +--------
+ include/asm-arm/arch-pxa/tosa.h | 5 +++++
+ 3 files changed, 40 insertions(+), 8 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa_lcd.c b/arch/arm/mach-pxa/tosa_lcd.c
+index d52f63f..447ca86 100644
+--- a/arch/arm/mach-pxa/tosa_lcd.c
++++ b/arch/arm/mach-pxa/tosa_lcd.c
+@@ -59,6 +59,8 @@ static int bl_intensity;
+ static struct ssp_dev tosa_nssp_dev;
+ static struct ssp_state tosa_nssp_state;
+ static spinlock_t tosa_nssp_lock;
++static int blanked;
++static unsigned long hsync_time;
+
+ static unsigned short normal_i2c[] = {
+ DAC_BASE,
+@@ -130,6 +132,17 @@ static void tosa_lcd_tg_init(struct device *dev)
+ pxa_nssp_output(TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */
+ }
+
++static unsigned long calc_hsync_time(const struct fb_videomode *mode) {
++ /* The 25 and 44 'magic numbers' are from Sharp's 2.4 patches */
++ if (mode->yres == 640) {
++ return 25;
++ }
++ if (mode->yres == 320) {
++ return 44;
++ }
++ return 0;
++}
++
+ static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode)
+ {
+ const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR;
+@@ -154,6 +167,8 @@ static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode)
+ /* set common voltage */
+ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj);
+
++ blanked = 0;
++ hsync_time = calc_hsync_time(mode);
+ }
+
+ static void tosa_lcd_tg_off(struct device *dev)
+@@ -172,6 +187,8 @@ static void tosa_lcd_tg_off(struct device *dev)
+
+ /* L3V Off */
+ reset_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON);
++
++ blanked = 1;
+ }
+
+ static int tosa_detect_client(struct i2c_adapter* adapter, int address, int kind) {
+@@ -238,6 +255,23 @@ static int tosa_detach_client(struct i2c_client* client) {
+ return 0;
+ }
+
++unsigned long tosa_lcd_get_hsync_time(void)
++{
++/* This method should eventually contain the correct algorithm for calculating
++ the hsync_time */
++ if (blanked)
++ return 0;
++ else
++ return hsync_time;
++}
++
++void tosa_lcd_wait_hsync(void)
++{
++ /* Waits for a rising edge on the VGA line */
++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0);
++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0);
++}
++
+ static struct i2c_driver tosa_driver={
+ .id = TOSA_LCD_I2C_DEVICEID,
+ .attach_adapter = tosa_attach_adapter,
+diff --git a/drivers/input/touchscreen/tosa_ts.c b/drivers/input/touchscreen/tosa_ts.c
+index bc733e9..134f8ce 100644
+--- a/drivers/input/touchscreen/tosa_ts.c
++++ b/drivers/input/touchscreen/tosa_ts.c
+@@ -25,13 +25,6 @@
+ #define CCNT_ON() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1))
+ #define CCNT_OFF() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1))
+
+-static inline void tosa_lcd_wait_hsync(void)
+-{
+- /* Waits for a rising edge on the VGA line */
+- while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0);
+- while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0);
+-}
+-
+ /* On the Sharp SL-6000 (Tosa), due to a noisy LCD, we need to perform a wait
+ * before sampling the Y axis of the touchscreen */
+ void tosa_lcd_sync_on(int adcsel) {
+@@ -54,7 +47,7 @@ void tosa_lcd_sync_on(int adcsel) {
+ }
+ }
+
+-void tosa_lcd_sync_off(void) {
++void tosa_lcd_sync_off(int adcsel) {
+ CCNT_OFF();
+ }
+
+diff --git a/include/asm-arm/arch-pxa/tosa.h b/include/asm-arm/arch-pxa/tosa.h
+index ce7322d..7f446fd 100644
+--- a/include/asm-arm/arch-pxa/tosa.h
++++ b/include/asm-arm/arch-pxa/tosa.h
+@@ -1,6 +1,7 @@
+ /*
+ * Hardware specific definitions for Sharp SL-C6000x series of PDAs
+ *
++ * Copyright (c) 2006 Wolfson Microelectronics PLC.
+ * Copyright (c) 2005 Dirk Opfer
+ *
+ * Based on Sharp's 2.4 kernel patches
+@@ -187,4 +188,8 @@
+ extern struct platform_device tosascoop_jc_device;
+ extern struct platform_device tosascoop_device;
+ extern struct platform_device tc6393_device;
++
++unsigned long tosa_lcd_get_hsync_time(void);
++void tosa_lcd_wait_hsync(void);
++
+ #endif /* _ASM_ARCH_TOSA_H_ */
+--
+1.4.4.4
+
diff --git a/packages/linux/linux-rp-2.6.22/tosa-lcdnoise-r1.patch b/packages/linux/linux-rp-2.6.22/tosa-lcdnoise-r1.patch
new file mode 100644
index 0000000000..21f3cf66b1
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/tosa-lcdnoise-r1.patch
@@ -0,0 +1,158 @@
+From 564b757ba44b517ac6d693b94a177708bb5d3887 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 17:30:30 +0400
+Subject: [PATCH] tosa-lcdnoise-r1.patch
+
+---
+ drivers/input/touchscreen/Kconfig | 13 +++++
+ drivers/input/touchscreen/Makefile | 1 +
+ drivers/input/touchscreen/tosa_ts.c | 102 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 116 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
+index 3ac01b4..6862e8f 100644
+--- a/drivers/input/touchscreen/Kconfig
++++ b/drivers/input/touchscreen/Kconfig
+@@ -219,6 +219,19 @@ config TOUCHSCREEN_USB_DMC_TSC10
+ bool "DMC TSC-10/25 device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
++config TOUCHSCREEN_TOSA
++ tristate "Sharp Tosa touchscreen driver"
++ depends on TOUCHSCREEN_WM97XX && MACH_TOSA
++ default n
++ help
++ Say Y here to enable the driver for the touchscreen on the
++ Sharp Tosa PDA.
++ depends on TOUCHSCREEN_WM97XX && MACH_TOSA
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called tosa_ts.
++
+ config TOUCHSCREEN_TSC2101
+ tristate "TI TSC2101 touchscreen input driver"
+ depends on MACH_HX2750 && INPUT && INPUT_TOUCHSCREEN
+diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
+index f64d1a5..4fc0e17 100644
+--- a/drivers/input/touchscreen/Makefile
++++ b/drivers/input/touchscreen/Makefile
+@@ -22,6 +22,7 @@ obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
+ obj-$(CONFIG_TOUCHSCREEN_TSC2101) += tsc2101_ts.o
+ obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
+ obj-$(CONFIG_TOUCHSCREEN_WM97XX_PXA) += pxa-wm97xx.o
++obj-$(CONFIG_TOUCHSCREEN_TOSA) += tosa_ts.o
+
+ ifeq ($(CONFIG_TOUCHSCREEN_WM9713),y)
+ wm97xx-ts-objs += wm9713.o
+diff --git a/drivers/input/touchscreen/tosa_ts.c b/drivers/input/touchscreen/tosa_ts.c
+new file mode 100644
+index 0000000..bc733e9
+--- /dev/null
++++ b/drivers/input/touchscreen/tosa_ts.c
+@@ -0,0 +1,102 @@
++/*
++ * tosa_ts.c -- Touchscreen driver for Sharp SL-6000 (Tosa).
++ *
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ * Author: Mike Arthur
++ * linux@wolfsonmicro.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.
++ *
++ * Revision history
++ * 1st Sep 2006 Initial version.
++ *
++ */
++
++#include <linux/wm97xx.h>
++#include <asm/arch/tosa.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch/pxa-regs.h>
++
++/* Taken from the Sharp 2.4 kernel code */
++#define CCNT(a) asm volatile ("mrc p14, 0, %0, C1, C1, 0" : "=r"(a))
++#define CCNT_ON() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1))
++#define CCNT_OFF() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1))
++
++static inline void tosa_lcd_wait_hsync(void)
++{
++ /* Waits for a rising edge on the VGA line */
++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0);
++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0);
++}
++
++/* On the Sharp SL-6000 (Tosa), due to a noisy LCD, we need to perform a wait
++ * before sampling the Y axis of the touchscreen */
++void tosa_lcd_sync_on(int adcsel) {
++ unsigned long timer1 = 0, timer2 = 0, wait_time = 0;
++ if (adcsel == WM97XX_ADCSEL_Y) {
++ wait_time = tosa_lcd_get_hsync_time();
++ CCNT_ON();
++
++ if (wait_time) {
++ /* wait for LCD rising edge */
++ tosa_lcd_wait_hsync();
++ /* get clock */
++ CCNT(timer1);
++ CCNT(timer2);
++
++ while ((timer2 - timer1) < wait_time) {
++ CCNT(timer2);
++ }
++ }
++ }
++}
++
++void tosa_lcd_sync_off(void) {
++ CCNT_OFF();
++}
++
++static struct wm97xx_mach_ops tosa_mach_ops = {
++ .pre_sample = tosa_lcd_sync_on,
++ .post_sample = tosa_lcd_sync_off,
++};
++
++int tosa_ts_probe(struct device *dev) {
++ struct wm97xx *wm = dev->driver_data;
++ return wm97xx_register_mach_ops (wm, &tosa_mach_ops);
++}
++
++
++int tosa_ts_remove(struct device *dev) {
++ struct wm97xx *wm = dev->driver_data;
++ wm97xx_unregister_mach_ops (wm);
++ return 0;
++}
++
++static struct device_driver tosa_ts_driver = {
++ .name = "wm97xx-touchscreen",
++ .bus = &wm97xx_bus_type,
++ .owner = THIS_MODULE,
++ .probe = tosa_ts_probe,
++ .remove = tosa_ts_remove,
++};
++
++static int __init tosa_ts_init(void)
++{
++ return driver_register(&tosa_ts_driver);
++}
++
++static void __exit tosa_ts_exit(void)
++{
++ driver_unregister(&tosa_ts_driver);
++}
++
++module_init(tosa_ts_init);
++module_exit(tosa_ts_exit);
++
++/* Module information */
++MODULE_AUTHOR("Mike Arthur, mike@mikearthur.co.uk, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("Sharp SL6000 Tosa Touch Screen Driver");
++MODULE_LICENSE("GPL");
+--
+1.4.4.4
+
diff --git a/packages/linux/linux-rp-2.6.22/tosa-power-r18-fix-r0.patch b/packages/linux/linux-rp-2.6.22/tosa-power-r18-fix-r0.patch
new file mode 100644
index 0000000000..8899ae270b
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/tosa-power-r18-fix-r0.patch
@@ -0,0 +1,59 @@
+From 24813da9b0aac0e92635d7307837d89a9f4a1ee7 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 16:47:15 +0400
+Subject: [PATCH] tosa-power-r18.patch fixes
+
+---
+ arch/arm/mach-pxa/tosa_pm.c | 9 +++++----
+ 1 files changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa_pm.c b/arch/arm/mach-pxa/tosa_pm.c
+index 1eab1af..2df75f0 100644
+--- a/arch/arm/mach-pxa/tosa_pm.c
++++ b/arch/arm/mach-pxa/tosa_pm.c
+@@ -17,9 +17,9 @@
+ #include <linux/interrupt.h>
+ #include <linux/platform_device.h>
+ #include <linux/pm.h>
++#include <linux/apm-emulation.h>
+ #include <linux/wm97xx.h>
+
+-#include <asm/apm.h>
+ #include <asm/irq.h>
+ #include <asm/mach-types.h>
+ #include <asm/hardware.h>
+@@ -144,7 +144,7 @@ static int tosa_ac97_init(void)
+ pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
+ pxa_gpio_mode(GPIO20_DREQ0_MD);
+
+- pxa_set_cken(CKEN2_AC97, 1);
++ pxa_set_cken(CKEN_AC97, 1);
+ /* AC97 power on sequense */
+ while ( 1 ) {
+ GCR = 0;
+@@ -184,11 +184,12 @@ static int tosa_ac97_init(void)
+ pxa_gpio_mode(GPIO32_SDATA_IN1_AC97_MD);
+ ad_polling = 1;
+ printk("tosa_ac97_init\n");
++ return 0;
+ }
+
+ void tosa_ac97_exit(void)
+ {
+- if (!(CKEN & CKEN2_AC97))
++ if (!(CKEN & CKEN_AC97))
+ return;
+
+ // power down the whole chip
+@@ -197,7 +198,7 @@ void tosa_ac97_exit(void)
+ // GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN);
+ // GSR = GSR;
+ // GCR = GCR_ACLINK_OFF;
+- pxa_set_cken(CKEN2_AC97, 0);
++ pxa_set_cken(CKEN_AC97, 0);
+ /* switch back to irq driver */
+ ad_polling = 0;
+ printk("tosa_ac97_exit\n");
+--
+1.4.4.4
+
diff --git a/packages/linux/linux-rp-2.6.22/tosa-pxaac97-r6-fix-r0.patch b/packages/linux/linux-rp-2.6.22/tosa-pxaac97-r6-fix-r0.patch
new file mode 100644
index 0000000000..9c18aae98d
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/tosa-pxaac97-r6-fix-r0.patch
@@ -0,0 +1,29 @@
+From 005693333f4b3e0495bb80cc3cfd812e3e6f0a30 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 00:48:42 +0400
+Subject: [PATCH] tosa-pxaac97-r6.patch fixes
+
+---
+ arch/arm/mach-pxa/tosa.c | 4 ++--
+ 1 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c
+index 059fa07..61536d4 100644
+--- a/arch/arm/mach-pxa/tosa.c
++++ b/arch/arm/mach-pxa/tosa.c
+@@ -310,10 +310,10 @@ static void __init tosa_init(void)
+ PMCR = 0x01;
+
+ // AC97 Disable all IRQ's
+- pxa_set_cken(CKEN2_AC97, 1);
++ pxa_set_cken(CKEN_AC97, 1);
+ GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN);
+ GSR = GSR;
+- pxa_set_cken(CKEN2_AC97, 0);
++ pxa_set_cken(CKEN_AC97, 0);
+
+ pxa_set_mci_info(&tosa_mci_platform_data);
+ pxa_set_udc_info(&udc_info);
+--
+1.4.4.4
+
diff --git a/packages/linux/linux-rp-2.6.22/tosa-tmio-lcd-r10-fix-r0.patch b/packages/linux/linux-rp-2.6.22/tosa-tmio-lcd-r10-fix-r0.patch
new file mode 100644
index 0000000000..a2e2bee151
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/tosa-tmio-lcd-r10-fix-r0.patch
@@ -0,0 +1,35 @@
+From bb3ed6577c592d86f0976a92978c9454bbdfbe59 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 02:01:23 +0400
+Subject: [PATCH] tosa-tmio-lcd-r10.patch fixes
+
+---
+ arch/arm/mach-pxa/tosa_lcd.c | 5 +++--
+ 1 files changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa_lcd.c b/arch/arm/mach-pxa/tosa_lcd.c
+index eeeee3e..d52f63f 100644
+--- a/arch/arm/mach-pxa/tosa_lcd.c
++++ b/arch/arm/mach-pxa/tosa_lcd.c
+@@ -66,7 +66,7 @@ static unsigned short normal_i2c[] = {
+ };
+ I2C_CLIENT_INSMOD;
+
+-static struct corgibl_machinfo tosa_bl_machinfo = {
++static struct generic_bl_info tosa_bl_machinfo = {
+ .max_intensity = 255,
+ .default_intensity = 68,
+ .limit_mask = 0x0b,
+@@ -80,7 +80,8 @@ int tosa_bl_intensity(void)
+
+ static void pxa_nssp_output(unsigned char reg, unsigned char data)
+ {
+- unsigned long flag, dummy;
++ unsigned long flag;
++ u32 dummy;
+ u32 dat = ( ((reg << 5) & 0xe0) | (data & 0x1f) );
+ spin_lock_irqsave(&tosa_nssp_lock, flag);
+
+--
+1.4.4.4
+
diff --git a/packages/linux/linux-rp-2.6.22/tosa-tmio-lcd-r10.patch b/packages/linux/linux-rp-2.6.22/tosa-tmio-lcd-r10.patch
new file mode 100644
index 0000000000..aef3a047c1
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/tosa-tmio-lcd-r10.patch
@@ -0,0 +1,472 @@
+ arch/arm/mach-pxa/Kconfig | 5
+ arch/arm/mach-pxa/Makefile | 2
+ arch/arm/mach-pxa/tosa.c | 49 +++++-
+ arch/arm/mach-pxa/tosa_lcd.c | 344 +++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 396 insertions(+), 4 deletions(-)
+
+Index: git/arch/arm/mach-pxa/Makefile
+===================================================================
+--- git.orig/arch/arm/mach-pxa/Makefile 2006-11-07 22:13:10.000000000 +0000
++++ git/arch/arm/mach-pxa/Makefile 2006-11-07 23:29:38.000000000 +0000
+@@ -17,7 +17,7 @@ obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o
+ obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o
+ obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o
+ obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o
+-obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o
++obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o tosa_lcd.o
+ obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o
+
+ # Support for blinky lights
+Index: git/arch/arm/mach-pxa/tosa_lcd.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ git/arch/arm/mach-pxa/tosa_lcd.c 2006-11-07 23:29:25.000000000 +0000
+@@ -0,0 +1,344 @@
++/*
++ * LCD / Backlight control code for Sharp SL-6000x (tosa)
++ *
++ * Copyright (c) 2005 Dirk Opfer
++ *
++ * 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.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/i2c.h>
++#include <linux/fb.h>
++
++#include <asm/mach/sharpsl_param.h>
++#include <asm/hardware.h>
++#include <asm/hardware/scoop.h>
++#include <asm/hardware/tmio.h>
++#include <asm/arch/ssp.h>
++#include <asm/arch/sharpsl.h>
++#include <asm/arch/tosa.h>
++#include <asm/arch/pxa-regs.h>
++
++#define DAC_BASE 0x4e
++#define DAC_CH1 0
++#define DAC_CH2 1
++
++#define TG_REG0_VQV 0x0001
++#define TG_REG0_COLOR 0x0002
++#define TG_REG0_UD 0x0004
++#define TG_REG0_LR 0x0008
++#define COMADJ_DEFAULT 97
++#define TOSA_LCD_I2C_DEVICEID 0x4711 // Fixme: new value
++
++static void tosa_lcd_tg_init(struct device *dev);
++static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode);
++static void tosa_lcd_tg_off(struct device *dev);
++static void tosa_set_backlight(int intensity);
++
++const static struct tmio_lcd_ops tosa_tc6393_lcd_ops = {
++ .init = tosa_lcd_tg_init,
++ .tg_on = tosa_lcd_tg_on,
++ .tg_off = tosa_lcd_tg_off,
++};
++
++static struct platform_device *tosabl_device;
++static struct i2c_driver tosa_driver;
++static struct i2c_client* tosa_i2c_dac;
++static int initialised;
++static int comadj;
++static int bl_intensity;
++static struct ssp_dev tosa_nssp_dev;
++static struct ssp_state tosa_nssp_state;
++static spinlock_t tosa_nssp_lock;
++
++static unsigned short normal_i2c[] = {
++ DAC_BASE,
++ I2C_CLIENT_END
++};
++I2C_CLIENT_INSMOD;
++
++static struct corgibl_machinfo tosa_bl_machinfo = {
++ .max_intensity = 255,
++ .default_intensity = 68,
++ .limit_mask = 0x0b,
++ .set_bl_intensity = tosa_set_backlight,
++};
++
++int tosa_bl_intensity(void)
++{
++ return bl_intensity;
++}
++
++static void pxa_nssp_output(unsigned char reg, unsigned char data)
++{
++ unsigned long flag, dummy;
++ u32 dat = ( ((reg << 5) & 0xe0) | (data & 0x1f) );
++ spin_lock_irqsave(&tosa_nssp_lock, flag);
++
++ ssp_config(&tosa_nssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), 0, 0, SSCR0_SerClkDiv(128));
++ ssp_enable(&tosa_nssp_dev);
++
++ ssp_write_word(&tosa_nssp_dev,dat);
++
++ /* Read null data back from device to prevent SSP overflow */
++ ssp_read_word(&tosa_nssp_dev, &dummy);
++ ssp_disable(&tosa_nssp_dev);
++ spin_unlock_irqrestore(&tosa_nssp_lock, flag);
++
++}
++
++static void tosa_set_backlight(int intensity)
++{
++ if (!tosa_i2c_dac)
++ return;
++
++ bl_intensity = intensity;
++ /* SetBacklightDuty */
++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH2, (unsigned char)intensity);
++
++ /* SetBacklightVR */
++ if (intensity)
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BL_C20MA);
++ else
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BL_C20MA);
++
++ /* bl_enable GP04=1 otherwise GP04=0*/
++ pxa_nssp_output(TG_GPODR2, intensity ? 0x01 : 0x00);
++}
++
++static void tosa_lcd_tg_init(struct device *dev)
++{
++ /* L3V On */
++ set_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON);
++ mdelay(60);
++
++ /* TG On */
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_TG_ON);
++ mdelay(60);
++
++ pxa_nssp_output(TG_TPOSCTL,0x00); /* delayed 0clk TCTL signal for VGA */
++ pxa_nssp_output(TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */
++}
++
++static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode)
++{
++ const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR;
++ pxa_nssp_output(TG_PNLCTL, value | (mode->yres == 320 ? 0 : TG_REG0_VQV));
++
++ /* TG LCD pannel power up */
++ pxa_nssp_output(TG_PINICTL,0x4);
++ mdelay(50);
++
++ /* TG LCD GVSS */
++ pxa_nssp_output(TG_PINICTL,0x0);
++
++ if (!initialised)
++ {
++ /* after the pannel is powered up the first time, we can access the i2c bus */
++ /* so probe for the DAC */
++ i2c_add_driver(&tosa_driver);
++ initialised = 1;
++ mdelay(50);
++ }
++ if (tosa_i2c_dac)
++ /* set common voltage */
++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj);
++
++}
++
++static void tosa_lcd_tg_off(struct device *dev)
++{
++ /* TG LCD VHSA off */
++ pxa_nssp_output(TG_PINICTL,0x4);
++ mdelay(50);
++
++ /* TG LCD signal off */
++ pxa_nssp_output(TG_PINICTL,0x6);
++ mdelay(50);
++
++ /* TG Off */
++ set_tc6393_gpio(&tc6393_device.dev, TOSA_TC6393_TG_ON);
++ mdelay(100);
++
++ /* L3V Off */
++ reset_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON);
++}
++
++static int tosa_detect_client(struct i2c_adapter* adapter, int address, int kind) {
++ int err = 0;
++
++ printk("Tosa-LCD: DAC detected address:0x%2.2x\n",address);
++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA ))
++ goto ERROR0;
++
++ if (!(tosa_i2c_dac = (struct i2c_client*)kzalloc(sizeof(*tosa_i2c_dac), GFP_KERNEL))) {
++ err = -ENOMEM;
++ goto ERROR0;
++ }
++
++ //i2c_set_clientdata(tosa_i2c_dac, data);
++ tosa_i2c_dac->addr = address;
++ tosa_i2c_dac->adapter = adapter;
++ tosa_i2c_dac->driver = &tosa_driver;
++ tosa_i2c_dac->dev.parent = &tc6393_device.dev;
++ strcpy(tosa_i2c_dac->name, "tosa lcd");
++ if ((err = i2c_attach_client(tosa_i2c_dac)))
++ goto ERROR3;
++
++ /* Now i2c is ready, allocate the backlight device*/
++ tosabl_device = platform_device_alloc("corgi-bl", -1);
++ if (!tosabl_device) {
++ err = -ENOMEM;
++ goto ERROR4;
++ }
++
++ /* set parent device */
++ tosabl_device->dev.parent = &tosa_i2c_dac->dev;
++ tosabl_device->dev.platform_data = &tosa_bl_machinfo;
++
++ err = platform_device_add(tosabl_device);
++
++ if (err)
++ platform_device_put(tosabl_device);
++
++ /* set common voltage */
++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj);
++
++ return 0;
++ERROR4:
++ i2c_detach_client(tosa_i2c_dac);
++ERROR3:
++ kfree(tosa_i2c_dac);
++ERROR0:
++ return err;
++}
++
++static int tosa_attach_adapter(struct i2c_adapter* adapter) {
++ return i2c_probe(adapter, &addr_data, &tosa_detect_client);
++}
++
++static int tosa_detach_client(struct i2c_client* client) {
++ int err;
++
++ if ((err = i2c_detach_client(client))) {
++ printk(KERN_ERR "tosa: Cannot deregister client\n");
++ return err;
++ }
++ kfree(client);
++ return 0;
++}
++
++static struct i2c_driver tosa_driver={
++ .id = TOSA_LCD_I2C_DEVICEID,
++ .attach_adapter = tosa_attach_adapter,
++ .detach_client = tosa_detach_client,
++};
++
++static int __init tosa_lcd_probe(struct platform_device *pdev)
++{
++ int ret;
++ spin_lock_init(&tosa_nssp_lock);
++
++ if (!pdev->dev.platform_data)
++ return -EINVAL;
++
++ /* Set Common Voltage */
++ comadj = sharpsl_param.comadj == -1 ? COMADJ_DEFAULT : sharpsl_param.comadj;
++
++ ret=ssp_init(&tosa_nssp_dev,2,0);
++
++ /* initialize SSP */
++ pxa_gpio_mode(GPIO83_NSSP_TX);
++ pxa_gpio_mode(GPIO81_NSSP_CLK_OUT);
++ pxa_gpio_mode(GPIO82_NSSP_FRM_OUT);
++
++ if (ret)
++ printk(KERN_ERR "Unable to register NSSP handler!\n");
++ else {
++ struct tmio_lcd_ops* *tmio_ops = pdev->dev.platform_data;
++ ssp_disable(&tosa_nssp_dev);
++ initialised = 0;
++
++ /* Set the lcd functions */
++ *tmio_ops = (struct tmio_lcd_ops*) &tosa_tc6393_lcd_ops;
++ }
++
++ return ret;
++}
++
++static int tosa_lcd_remove(struct platform_device *pdev)
++{
++ /* delete the lcd functions */
++ struct tmio_lcd_ops* *tmio_ops = pdev->dev.platform_data;
++ *tmio_ops = NULL;
++
++ ssp_exit(&tosa_nssp_dev);
++
++ if (tosa_i2c_dac) {
++ i2c_detach_client(tosa_i2c_dac);
++ kfree(tosa_i2c_dac);
++ }
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++
++static int tosa_lcd_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ ssp_flush(&tosa_nssp_dev);
++ ssp_save_state(&tosa_nssp_dev,&tosa_nssp_state);
++ return 0;
++}
++
++static int tosa_lcd_resume(struct platform_device *pdev)
++{
++ printk("tosa_lcd_resume\n");
++ ssp_restore_state(&tosa_nssp_dev,&tosa_nssp_state);
++ ssp_enable(&tosa_nssp_dev);
++ printk("tosa_lcd_resume ok\n");
++ return 0;
++}
++#else
++
++#define tosa_lcd_suspend NULL
++#define tosa_lcd_resume NULL
++
++#endif
++
++
++static struct platform_driver tosalcd_driver = {
++ .probe = tosa_lcd_probe,
++ .remove = tosa_lcd_remove,
++ .suspend = tosa_lcd_suspend,
++ .resume = tosa_lcd_resume,
++ .driver = {
++ .name = "tosa-lcd",
++ },
++};
++
++static int __init tosa_lcd_init(void)
++{
++ return platform_driver_register(&tosalcd_driver);
++}
++
++static void __exit tosa_lcd_cleanup (void)
++{
++ platform_driver_unregister (&tosalcd_driver);
++}
++
++device_initcall(tosa_lcd_init);
++module_exit (tosa_lcd_cleanup);
++
++MODULE_DESCRIPTION ("Tosa LCD device");
++MODULE_AUTHOR ("Dirk Opfer");
++MODULE_LICENSE ("GPL v2");
+Index: git/arch/arm/mach-pxa/tosa.c
+===================================================================
+--- git.orig/arch/arm/mach-pxa/tosa.c 2006-11-07 22:13:10.000000000 +0000
++++ git/arch/arm/mach-pxa/tosa.c 2006-11-07 23:29:38.000000000 +0000
+@@ -24,6 +24,7 @@
+ #include <linux/mtd/partitions.h>
+ #include <linux/pm.h>
+ #include <linux/delay.h>
++#include <linux/fb.h>
+
+ #include <asm/setup.h>
+ #include <asm/memory.h>
+@@ -48,7 +49,6 @@
+
+ #include "generic.h"
+
+-
+ /*
+ * SCOOP Device
+ */
+@@ -345,7 +345,38 @@ static struct tmio_nand_platform_data to
+ .badblock_pattern = &tosa_tc6393_nand_bbt,
+ };
+
+-extern struct tmio_lcd_platform_data tosa_tc6393_lcd_platform_data;
++static struct fb_videomode tosa_tc6393_lcd_mode[] = {
++ {
++ .xres = 480,
++ .yres = 640,
++ .pixclock = 0x002cdf00,/* PLL divisor */
++ .left_margin = 0x004c,
++ .right_margin = 0x005b,
++ .upper_margin = 0x0001,
++ .lower_margin = 0x000d,
++ .hsync_len = 0x0002,
++ .vsync_len = 0x0001,
++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
++ .vmode = FB_VMODE_NONINTERLACED,
++ },{
++ .xres = 240,
++ .yres = 320,
++ .pixclock = 0x00e7f203,/* PLL divisor */
++ .left_margin = 0x0024,
++ .right_margin = 0x002f,
++ .upper_margin = 0x0001,
++ .lower_margin = 0x000d,
++ .hsync_len = 0x0002,
++ .vsync_len = 0x0001,
++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
++ .vmode = FB_VMODE_NONINTERLACED,
++}};
++
++struct tmio_lcd_platform_data tosa_tc6393_lcd_platform_data = {
++ .ops = NULL,
++ .modelist = tosa_tc6393_lcd_mode,
++ .num_modes = ARRAY_SIZE(tosa_tc6393_lcd_mode),
++};
+
+ static struct tmio_cell tosa_tc6393_cells[] = {
+ {
+@@ -384,6 +415,19 @@ struct platform_device tc6393_device = {
+ .num_resources = ARRAY_SIZE(tc6393_resources),
+ .resource = tc6393_resources,
+ };
++EXPORT_SYMBOL (tc6393_device);
++
++/*
++ * Tosa LCD / Backlight stuff
++ */
++static struct platform_device tosalcd_device = {
++ .name = "tosa-lcd",
++ .id = -1,
++ .dev = {
++ .parent = &tc6393_device.dev,
++ .platform_data = &tosa_tc6393_lcd_platform_data.ops,
++ },
++};
+
+ static struct platform_device *devices[] __initdata = {
+ &tosascoop_device,
+@@ -391,6 +435,7 @@ static struct platform_device *devices[]
+ &tosakbd_device,
+ &tosaled_device,
+ &tc6393_device,
++ &tosalcd_device,
+ };
+
+ static void tosa_poweroff(void)
+Index: git/arch/arm/mach-pxa/Kconfig
+===================================================================
+--- git.orig/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:10.000000000 +0000
++++ git/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:10.000000000 +0000
+@@ -129,7 +129,10 @@ config MACH_TOSA
+ bool "Enable Sharp SL-6000x (Tosa) Support"
+ depends PXA_SHARPSL_25x
+ select TOSHIBA_TC6393XB
+- select SHARPSL_PM
++ select I2C
++ select I2C_PXA
++ select SHARPSL_PM
++ select PXA_SSP
+
+ config PXA25x
+ bool
diff --git a/packages/linux/linux-rp-2.6.22/usb-ohci-hooks-r2.patch b/packages/linux/linux-rp-2.6.22/usb-ohci-hooks-r2.patch
new file mode 100644
index 0000000000..3656e98f0c
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/usb-ohci-hooks-r2.patch
@@ -0,0 +1,180 @@
+Index: linux-2.6.15/drivers/usb/host/ohci.h
+===================================================================
+--- linux-2.6.15.orig/drivers/usb/host/ohci.h
++++ linux-2.6.15/drivers/usb/host/ohci.h
+@@ -336,6 +336,23 @@ typedef struct urb_priv {
+ // sizeof (struct td) ~= 64 == 2^6 ...
+ #define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 6)) % TD_HASH_SIZE)
+
++struct ohci_hcd;
++
++/*
++ * Hooks to support controllers that must resort to a PIO-ish
++ * implementation because they only dma with on-chip memory.
++ */
++struct ohci_ops {
++ void* (*dma_pool_alloc) (struct dma_pool *pool, gfp_t mem_flags,
++ dma_addr_t *handle);
++ void (*dma_pool_free) (struct dma_pool *pool, void *vaddr,
++ dma_addr_t addr);
++
++ void (*td_fill) (struct ohci_hcd *ohci, u32 info, dma_addr_t data,
++ int len, struct urb *urb, int index);
++ void (*td_done) (struct ohci_hcd *ohci, struct urb *urb,
++ struct td *td);
++};
+
+ /*
+ * This is the full ohci controller description
+@@ -346,6 +363,7 @@ typedef struct urb_priv {
+
+ struct ohci_hcd {
+ spinlock_t lock;
++ const struct ohci_ops *ops;
+
+ /*
+ * I/O memory used to communicate with the HC (dma-consistent)
+Index: linux-2.6.15/drivers/usb/host/ohci-mem.c
+===================================================================
+--- linux-2.6.15.orig/drivers/usb/host/ohci-mem.c
++++ linux-2.6.15/drivers/usb/host/ohci-mem.c
+@@ -21,10 +21,13 @@
+ * No memory seen by this driver is pagable.
+ */
+
++static const struct ohci_ops ohci_ops;
++
+ /*-------------------------------------------------------------------------*/
+
+ static void ohci_hcd_init (struct ohci_hcd *ohci)
+ {
++ ohci->ops = &ohci_ops;
+ ohci->next_statechange = jiffies;
+ spin_lock_init (&ohci->lock);
+ INIT_LIST_HEAD (&ohci->pending);
+@@ -88,7 +91,7 @@ td_alloc (struct ohci_hcd *hc, gfp_t mem
+ dma_addr_t dma;
+ struct td *td;
+
+- td = dma_pool_alloc (hc->td_cache, mem_flags, &dma);
++ td = hc->ops->dma_pool_alloc (hc->td_cache, mem_flags, &dma);
+ if (td) {
+ /* in case hc fetches it, make it look dead */
+ memset (td, 0, sizeof *td);
+@@ -110,7 +113,7 @@ td_free (struct ohci_hcd *hc, struct td
+ *prev = td->td_hash;
+ else if ((td->hwINFO & cpu_to_hc32(hc, TD_DONE)) != 0)
+ ohci_dbg (hc, "no hash for td %p\n", td);
+- dma_pool_free (hc->td_cache, td, td->td_dma);
++ hc->ops->dma_pool_free (hc->td_cache, td, td->td_dma);
+ }
+
+ /*-------------------------------------------------------------------------*/
+@@ -122,7 +125,7 @@ ed_alloc (struct ohci_hcd *hc, gfp_t mem
+ dma_addr_t dma;
+ struct ed *ed;
+
+- ed = dma_pool_alloc (hc->ed_cache, mem_flags, &dma);
++ ed = hc->ops->dma_pool_alloc (hc->ed_cache, mem_flags, &dma);
+ if (ed) {
+ memset (ed, 0, sizeof (*ed));
+ INIT_LIST_HEAD (&ed->td_list);
+@@ -134,6 +137,6 @@ ed_alloc (struct ohci_hcd *hc, gfp_t mem
+ static void
+ ed_free (struct ohci_hcd *hc, struct ed *ed)
+ {
+- dma_pool_free (hc->ed_cache, ed, ed->dma);
++ hc->ops->dma_pool_free (hc->ed_cache, ed, ed->dma);
+ }
+
+Index: linux-2.6.15/drivers/usb/host/ohci-q.c
+===================================================================
+--- linux-2.6.15.orig/drivers/usb/host/ohci-q.c
++++ linux-2.6.15/drivers/usb/host/ohci-q.c
+@@ -629,7 +629,7 @@ static void td_submit_urb (
+ : TD_T_TOGGLE | TD_CC | TD_DP_IN;
+ /* TDs _could_ transfer up to 8K each */
+ while (data_len > 4096) {
+- td_fill (ohci, info, data, 4096, urb, cnt);
++ ohci->ops->td_fill (ohci, info, data, 4096, urb, cnt);
+ data += 4096;
+ data_len -= 4096;
+ cnt++;
+@@ -637,11 +637,11 @@ static void td_submit_urb (
+ /* maybe avoid ED halt on final TD short read */
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
+ info |= TD_R;
+- td_fill (ohci, info, data, data_len, urb, cnt);
++ ohci->ops->td_fill (ohci, info, data, data_len, urb, cnt);
+ cnt++;
+ if ((urb->transfer_flags & URB_ZERO_PACKET)
+ && cnt < urb_priv->length) {
+- td_fill (ohci, info, 0, 0, urb, cnt);
++ ohci->ops->td_fill (ohci, info, 0, 0, urb, cnt);
+ cnt++;
+ }
+ /* maybe kickstart bulk list */
+@@ -656,17 +656,18 @@ static void td_submit_urb (
+ */
+ case PIPE_CONTROL:
+ info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
+- td_fill (ohci, info, urb->setup_dma, 8, urb, cnt++);
++ ohci->ops->td_fill (ohci, info, urb->setup_dma, 8, urb, cnt++);
+ if (data_len > 0) {
+ info = TD_CC | TD_R | TD_T_DATA1;
+ info |= is_out ? TD_DP_OUT : TD_DP_IN;
+ /* NOTE: mishandles transfers >8K, some >4K */
+- td_fill (ohci, info, data, data_len, urb, cnt++);
++ ohci->ops->td_fill (ohci, info, data, data_len,
++ urb, cnt++);
+ }
+ info = (is_out || data_len == 0)
+ ? TD_CC | TD_DP_IN | TD_T_DATA1
+ : TD_CC | TD_DP_OUT | TD_T_DATA1;
+- td_fill (ohci, info, data, 0, urb, cnt++);
++ ohci->ops->td_fill (ohci, info, data, 0, urb, cnt++);
+ /* maybe kickstart control list */
+ wmb ();
+ ohci_writel (ohci, OHCI_CLF, &ohci->regs->cmdstatus);
+@@ -685,7 +686,7 @@ static void td_submit_urb (
+ // a 2^16 iso range, vs other HCs max of 2^10)
+ frame += cnt * urb->interval;
+ frame &= 0xffff;
+- td_fill (ohci, TD_CC | TD_ISO | frame,
++ ohci->ops->td_fill (ohci, TD_CC | TD_ISO | frame,
+ data + urb->iso_frame_desc [cnt].offset,
+ urb->iso_frame_desc [cnt].length, urb, cnt);
+ }
+@@ -788,6 +789,14 @@ static void td_done (struct ohci_hcd *oh
+ }
+ }
+
++/* default operations for most HCs */
++static const struct ohci_ops ohci_ops = {
++ .dma_pool_alloc = dma_pool_alloc,
++ .dma_pool_free = dma_pool_free,
++ .td_fill = td_fill,
++ .td_done = td_done,
++};
++
+ /*-------------------------------------------------------------------------*/
+
+ static inline struct td *
+@@ -984,7 +993,7 @@ rescan_this:
+ *prev = td->hwNextTD | savebits;
+
+ /* HC may have partly processed this TD */
+- td_done (ohci, urb, td);
++ ohci->ops->td_done (ohci, urb, td);
+ urb_priv->td_cnt++;
+
+ /* if URB is done, clean up */
+@@ -1079,7 +1088,7 @@ dl_done_list (struct ohci_hcd *ohci, str
+ struct ed *ed = td->ed;
+
+ /* update URB's length and status from TD */
+- td_done (ohci, urb, td);
++ ohci->ops->td_done (ohci, urb, td);
+ urb_priv->td_cnt++;
+
+ /* If all this urb's TDs are done, call complete() */
diff --git a/packages/linux/linux-rp-2.6.22/wm9712-reset-loop-r2.patch b/packages/linux/linux-rp-2.6.22/wm9712-reset-loop-r2.patch
new file mode 100644
index 0000000000..78e81ea83a
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/wm9712-reset-loop-r2.patch
@@ -0,0 +1,44 @@
+ sound/soc/codecs/wm9712.c | 28 ++++++++++++++++++----------
+ 1 file changed, 18 insertions(+), 10 deletions(-)
+
+Index: git/sound/soc/codecs/wm9712.c
+===================================================================
+--- git.orig/sound/soc/codecs/wm9712.c 2006-11-07 22:10:01.000000000 +0000
++++ git/sound/soc/codecs/wm9712.c 2006-11-07 22:11:50.000000000 +0000
+@@ -618,18 +618,26 @@ static int wm9712_dapm_event(struct snd_
+
+ static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
+ {
+- if (try_warm && soc_ac97_ops.warm_reset) {
+- soc_ac97_ops.warm_reset(codec->ac97);
+- if (!(ac97_read(codec, 0) & 0x8000))
+- return 1;
+- }
++ int retry = 3;
+
+- soc_ac97_ops.reset(codec->ac97);
+- if (ac97_read(codec, 0) & 0x8000)
+- goto err;
+- return 0;
++ while (retry--)
++ {
++ if(try_warm && soc_ac97_ops.warm_reset) {
++ soc_ac97_ops.warm_reset(codec->ac97);
++ if(ac97_read(codec, 0) & 0x8000)
++ continue;
++ else
++ return 1;
++ }
++
++ soc_ac97_ops.reset(codec->ac97);
++ if(ac97_read(codec, 0) & 0x8000)
++ continue;
++ else
++ return 0;
++
++ }
+
+-err:
+ printk(KERN_ERR "WM9712 AC97 reset failed\n");
+ return -EIO;
+ }
diff --git a/packages/linux/linux-rp-2.6.22/wm9712-suspend-cold-res-r2.patch b/packages/linux/linux-rp-2.6.22/wm9712-suspend-cold-res-r2.patch
new file mode 100644
index 0000000000..5179b47cc4
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/wm9712-suspend-cold-res-r2.patch
@@ -0,0 +1,16 @@
+ sound/soc/codecs/wm9712.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+Index: git/sound/soc/codecs/wm9712.c
+===================================================================
+--- git.orig/sound/soc/codecs/wm9712.c 2006-11-07 21:57:34.000000000 +0000
++++ git/sound/soc/codecs/wm9712.c 2006-11-07 21:59:30.000000000 +0000
+@@ -651,7 +651,7 @@ static int wm9712_soc_resume(struct plat
+ int i, ret;
+ u16 *cache = codec->reg_cache;
+
+- ret = wm9712_reset(codec, 1);
++ ret = wm9712_reset(codec, 0);
+ if (ret < 0){
+ printk(KERN_ERR "could not reset AC97 codec\n");
+ return ret;
diff --git a/packages/linux/linux-rp-2.6.22/wm97xx-lg13-r0-fix-r0.patch b/packages/linux/linux-rp-2.6.22/wm97xx-lg13-r0-fix-r0.patch
new file mode 100644
index 0000000000..5ad0d8703d
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/wm97xx-lg13-r0-fix-r0.patch
@@ -0,0 +1,128 @@
+ drivers/input/power.c | 2 +-
+ drivers/input/touchscreen/Kconfig | 2 +-
+ drivers/input/touchscreen/wm97xx-core.c | 35 ++++++++++++++++---------------
+ include/linux/wm97xx.h | 2 +-
+ 4 files changed, 21 insertions(+), 20 deletions(-)
+
+diff --git a/drivers/input/power.c b/drivers/input/power.c
+index 4443e34..7aac875 100644
+--- a/drivers/input/power.c
++++ b/drivers/input/power.c
+@@ -156,7 +156,7 @@ static void power_event(struct input_handle *handle, unsigned int type,
+ }
+ }
+
+-static struct input_handle *power_connect(struct input_handler *handler,
++static int power_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+ {
+diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
+index 6862e8f..9b532e9 100644
+--- a/drivers/input/touchscreen/Kconfig
++++ b/drivers/input/touchscreen/Kconfig
+@@ -247,7 +247,7 @@ config TOUCHSCREEN_TSC2101
+
+ config TOUCHSCREEN_WM97XX
+ tristate "Support for WM97xx AC97 touchscreen controllers"
+- depends SND_AC97_BUS
++ depends AC97_BUS
+
+ choice
+ prompt "WM97xx codec type"
+diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
+index 9b2710e..d3ce3f3 100644
+--- a/drivers/input/touchscreen/wm97xx-core.c
++++ b/drivers/input/touchscreen/wm97xx-core.c
+@@ -84,6 +84,7 @@
+ #include <linux/bitops.h>
+ #include <linux/workqueue.h>
+ #include <linux/device.h>
++#include <linux/freezer.h>
+ #include <linux/wm97xx.h>
+ #include <asm/uaccess.h>
+ #include <asm/io.h>
+@@ -241,14 +242,15 @@ WM97XX_STATUS_ATTR(gpio);
+
+ static int wm97xx_sys_add(struct device *dev)
+ {
++ int err;
+ if (aux_sys) {
+- device_create_file(dev, &dev_attr_aux1);
+- device_create_file(dev, &dev_attr_aux2);
+- device_create_file(dev, &dev_attr_aux3);
+- device_create_file(dev, &dev_attr_aux4);
++ err = device_create_file(dev, &dev_attr_aux1);
++ err = device_create_file(dev, &dev_attr_aux2);
++ err = device_create_file(dev, &dev_attr_aux3);
++ err = device_create_file(dev, &dev_attr_aux4);
+ }
+ if (status_sys)
+- device_create_file(dev, &dev_attr_gpio);
++ err = device_create_file(dev, &dev_attr_gpio);
+ return 0;
+ }
+
+@@ -366,12 +368,12 @@ void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir,
+
+ /*
+ * Handle a pen down interrupt.
+- */
+-static void wm97xx_pen_irq_worker(void *ptr)
+-{
+- struct wm97xx *wm = (struct wm97xx *) ptr;
+-
+- /* do we need to enable the touch panel reader */
++ */
++static void wm97xx_pen_irq_worker(struct work_struct *work)
++{
++ struct wm97xx *wm = container_of(work, struct wm97xx, pen_event_work);
++
++ /* do we need to enable the touch panel reader */
+ if (wm->id == WM9705_ID2) {
+ if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN)
+ wm->pen_is_down = 1;
+@@ -411,9 +413,8 @@ static void wm97xx_pen_irq_worker(void *ptr)
+ * We have to disable the codec interrupt in the handler because it can
+ * take upto 1ms to clear the interrupt source. The interrupt is then enabled
+ * again in the slow handler when the source has been cleared.
+- */
+-static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id,
+- struct pt_regs *regs)
++ */
++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id)
+ {
+ struct wm97xx *wm = (struct wm97xx *) dev_id;
+ disable_irq(wm->pen_irq);
+@@ -428,15 +429,15 @@ static int wm97xx_init_pen_irq(struct wm97xx *wm)
+ {
+ u16 reg;
+
+- INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker, wm);
+- if ((wm->pen_irq_workq =
++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker);
++ if ((wm->pen_irq_workq =
+ create_singlethread_workqueue("kwm97pen")) == NULL) {
+ err("could not create pen irq work queue");
+ wm->pen_irq = 0;
+ return -EINVAL;
+ }
+
+- if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) {
++ if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, IRQF_SHARED, "wm97xx-pen", wm)) {
+ err("could not register codec pen down interrupt, will poll for pen down");
+ destroy_workqueue(wm->pen_irq_workq);
+ wm->pen_irq = 0;
+diff --git a/include/linux/wm97xx.h b/include/linux/wm97xx.h
+index b1c1740..a9bd57e 100644
+--- a/include/linux/wm97xx.h
++++ b/include/linux/wm97xx.h
+@@ -243,7 +243,7 @@ struct wm97xx {
+ u16 dig_save[3]; /* saved during aux reading */
+ struct wm97xx_codec_drv *codec; /* attached codec driver*/
+ struct input_dev* input_dev; /* touchscreen input device */
+- ac97_t *ac97; /* ALSA codec access */
++ struct snd_ac97 *ac97; /* ALSA codec access */
+ struct device *dev; /* ALSA device */
+ struct device *battery_dev;
+ struct device *touch_dev;
diff --git a/packages/linux/linux-rp-2.6.22/wm97xx-lg13-r0.patch b/packages/linux/linux-rp-2.6.22/wm97xx-lg13-r0.patch
new file mode 100644
index 0000000000..c918c5daff
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.22/wm97xx-lg13-r0.patch
@@ -0,0 +1,2899 @@
+Index: linux-2.6.17/drivers/input/touchscreen/Kconfig
+===================================================================
+--- linux-2.6.17.orig/drivers/input/touchscreen/Kconfig 2006-09-19 20:35:35.060495500 +0200
++++ linux-2.6.17/drivers/input/touchscreen/Kconfig 2006-09-19 20:36:47.965051750 +0200
+@@ -121,4 +121,57 @@ config TOUCHSCREEN_TSC2101
+ To compile this driver as a module, choose M here: the
+ module will be called ads7846_ts.
+
++config TOUCHSCREEN_WM97XX
++ tristate "Support for WM97xx AC97 touchscreen controllers"
++ depends SND_AC97_BUS
++
++choice
++ prompt "WM97xx codec type"
++
++config TOUCHSCREEN_WM9705
++ bool "WM9705 Touchscreen interface support"
++ depends on TOUCHSCREEN_WM97XX
++ help
++ Say Y here if you have the wm9705 touchscreen.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called wm9705.
++
++config TOUCHSCREEN_WM9712
++ bool "WM9712 Touchscreen interface support"
++ depends on TOUCHSCREEN_WM97XX
++ help
++ Say Y here if you have the wm9712 touchscreen.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called wm9712.
++
++config TOUCHSCREEN_WM9713
++ bool "WM9713 Touchscreen interface support"
++ depends on TOUCHSCREEN_WM97XX
++ help
++ Say Y here if you have the wm9713 touchscreen.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called wm9713.
++
++endchoice
++
++config TOUCHSCREEN_WM97XX_PXA
++ tristate "WM97xx PXA accelerated touch"
++ depends on TOUCHSCREEN_WM97XX && ARCH_PXA
++ help
++ Say Y here for continuous mode touch on the PXA
++
++ If unsure, say N
++
++ To compile this driver as a module, choose M here: the
++ module will be called pxa-wm97xx
++
+ endif
+Index: linux-2.6.17/drivers/input/touchscreen/Makefile
+===================================================================
+--- linux-2.6.17.orig/drivers/input/touchscreen/Makefile 2006-09-19 20:35:35.072496250 +0200
++++ linux-2.6.17/drivers/input/touchscreen/Makefile 2006-09-19 20:37:40.540337500 +0200
+@@ -4,6 +4,8 @@
+
+ # Each configuration option enables a list of files.
+
++wm97xx-ts-objs := wm97xx-core.o
++
+ obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
+ obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
+ obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o
+@@ -13,3 +15,16 @@ obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtou
+ obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
+ obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
+ obj-$(CONFIG_TOUCHSCREEN_TSC2101) += tsc2101_ts.o
++obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
++obj-$(CONFIG_TOUCHSCREEN_WM97XX_PXA) += pxa-wm97xx.o
++
++ifeq ($(CONFIG_TOUCHSCREEN_WM9713),y)
++wm97xx-ts-objs += wm9713.o
++endif
++
++ifeq ($(CONFIG_TOUCHSCREEN_WM9712),y)
++wm97xx-ts-objs += wm9712.o
++endif
++ifeq ($(CONFIG_TOUCHSCREEN_WM9705),y)
++wm97xx-ts-objs += wm9705.o
++endif
+Index: linux-2.6.17/drivers/input/touchscreen/pxa-wm97xx.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/drivers/input/touchscreen/pxa-wm97xx.c 2006-09-19 20:36:47.965051750 +0200
+@@ -0,0 +1,289 @@
++/*
++ * pxa-wm97xx.c -- pxa-wm97xx Continuous Touch screen driver for
++ * Wolfson WM97xx AC97 Codecs.
++ *
++ * Copyright 2004 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ *
++ * 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.
++ *
++ * Notes:
++ * This is a wm97xx extended touch driver to capture touch
++ * data in a continuous manner on the Intel XScale archictecture
++ *
++ * Features:
++ * - codecs supported:- WM9705, WM9712, WM9713
++ * - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x
++ *
++ * Revision history
++ * 18th Aug 2004 Initial version.
++ * 26th Jul 2005 Improved continous read back and added FIFO flushing.
++ * 06th Sep 2005 Mike Arthur <linux@wolfsonmicro.com>
++ * Moved to using the wm97xx bus
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/irq.h>
++#include <linux/wm97xx.h>
++#include <asm/io.h>
++#include <asm/arch/pxa-regs.h>
++
++#define VERSION "0.13"
++
++struct continuous {
++ u16 id; /* codec id */
++ u8 code; /* continuous code */
++ u8 reads; /* number of coord reads per read cycle */
++ u32 speed; /* number of coords per second */
++};
++
++#define WM_READS(sp) ((sp / HZ) + 1)
++
++static const struct continuous cinfo[] = {
++ {WM9705_ID2, 0, WM_READS(94), 94},
++ {WM9705_ID2, 1, WM_READS(188), 188},
++ {WM9705_ID2, 2, WM_READS(375), 375},
++ {WM9705_ID2, 3, WM_READS(750), 750},
++ {WM9712_ID2, 0, WM_READS(94), 94},
++ {WM9712_ID2, 1, WM_READS(188), 188},
++ {WM9712_ID2, 2, WM_READS(375), 375},
++ {WM9712_ID2, 3, WM_READS(750), 750},
++ {WM9713_ID2, 0, WM_READS(94), 94},
++ {WM9713_ID2, 1, WM_READS(120), 120},
++ {WM9713_ID2, 2, WM_READS(154), 154},
++ {WM9713_ID2, 3, WM_READS(188), 188},
++};
++
++/* continuous speed index */
++static int sp_idx = 0;
++static u16 last = 0, tries = 0;
++
++/*
++ * Pen sampling frequency (Hz) in continuous mode.
++ */
++static int cont_rate = 200;
++module_param(cont_rate, int, 0);
++MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)");
++
++/*
++ * Pen down detection.
++ *
++ * This driver can either poll or use an interrupt to indicate a pen down
++ * event. If the irq request fails then it will fall back to polling mode.
++ */
++static int pen_int = 1;
++module_param(pen_int, int, 0);
++MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)");
++
++/*
++ * Pressure readback.
++ *
++ * Set to 1 to read back pen down pressure
++ */
++static int pressure = 0;
++module_param(pressure, int, 0);
++MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)");
++
++/*
++ * AC97 touch data slot.
++ *
++ * Touch screen readback data ac97 slot
++ */
++static int ac97_touch_slot = 5;
++module_param(ac97_touch_slot, int, 0);
++MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number");
++
++
++/* flush AC97 slot 5 FIFO on pxa machines */
++#ifdef CONFIG_PXA27x
++void wm97xx_acc_pen_up (struct wm97xx* wm)
++{
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(1);
++
++ while (MISR & (1 << 2))
++ MODR;
++}
++#else
++void wm97xx_acc_pen_up (struct wm97xx* wm)
++{
++ int count = 16;
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(1);
++
++ while (count < 16) {
++ MODR;
++ count--;
++ }
++}
++#endif
++
++int wm97xx_acc_pen_down (struct wm97xx* wm)
++{
++ u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES;
++ int reads = 0;
++
++ /* data is never immediately available after pen down irq */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(1);
++
++ if (tries > 5){
++ tries = 0;
++ return RC_PENUP;
++ }
++
++ x = MODR;
++ if (x == last) {
++ tries++;
++ return RC_AGAIN;
++ }
++ last = x;
++ do {
++ if (reads)
++ x= MODR;
++ y= MODR;
++ if (pressure)
++ p = MODR;
++
++ /* are samples valid */
++ if ((x & 0x7000) != WM97XX_ADCSEL_X ||
++ (y & 0x7000) != WM97XX_ADCSEL_Y ||
++ (p & 0x7000) != WM97XX_ADCSEL_PRES)
++ goto up;
++
++ /* coordinate is good */
++ tries = 0;
++ //printk("x %x y %x p %x\n", x,y,p);
++ input_report_abs (wm->input_dev, ABS_X, x & 0xfff);
++ input_report_abs (wm->input_dev, ABS_Y, y & 0xfff);
++ input_report_abs (wm->input_dev, ABS_PRESSURE, p & 0xfff);
++ input_sync (wm->input_dev);
++ reads++;
++ } while (reads < cinfo[sp_idx].reads);
++up:
++ return RC_PENDOWN | RC_AGAIN;
++}
++
++int wm97xx_acc_startup(struct wm97xx* wm)
++{
++ int idx = 0;
++
++ /* check we have a codec */
++ if (wm->ac97 == NULL)
++ return -ENODEV;
++
++ /* Go you big red fire engine */
++ for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) {
++ if (wm->id != cinfo[idx].id)
++ continue;
++ sp_idx = idx;
++ if (cont_rate <= cinfo[idx].speed)
++ break;
++ }
++ wm->acc_rate = cinfo[sp_idx].code;
++ wm->acc_slot = ac97_touch_slot;
++ printk(KERN_INFO "pxa2xx accelerated touchscreen driver, %d samples (sec)\n",
++ cinfo[sp_idx].speed);
++
++ /* codec specific irq config */
++ if (pen_int) {
++ switch (wm->id) {
++ case WM9705_ID2:
++ wm->pen_irq = IRQ_GPIO(4);
++ set_irq_type(IRQ_GPIO(4), IRQT_BOTHEDGE);
++ break;
++ case WM9712_ID2:
++ case WM9713_ID2:
++ /* enable pen down interrupt */
++ /* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */
++ wm->pen_irq = MAINSTONE_AC97_IRQ;
++ wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
++ WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_STICKY, WM97XX_GPIO_WAKE);
++ wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT,
++ WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_NOTSTICKY, WM97XX_GPIO_NOWAKE);
++ break;
++ default:
++ printk(KERN_WARNING "pen down irq not supported on this device\n");
++ pen_int = 0;
++ break;
++ }
++ }
++
++ return 0;
++}
++
++void wm97xx_acc_shutdown(struct wm97xx* wm)
++{
++ /* codec specific deconfig */
++ if (pen_int) {
++ switch (wm->id & 0xffff) {
++ case WM9705_ID2:
++ wm->pen_irq = 0;
++ break;
++ case WM9712_ID2:
++ case WM9713_ID2:
++ /* disable interrupt */
++ wm->pen_irq = 0;
++ break;
++ }
++ }
++}
++
++static struct wm97xx_mach_ops pxa_mach_ops = {
++ .acc_enabled = 1,
++ .acc_pen_up = wm97xx_acc_pen_up,
++ .acc_pen_down = wm97xx_acc_pen_down,
++ .acc_startup = wm97xx_acc_startup,
++ .acc_shutdown = wm97xx_acc_shutdown,
++};
++
++int pxa_wm97xx_probe(struct device *dev)
++{
++ struct wm97xx *wm = dev->driver_data;
++ return wm97xx_register_mach_ops (wm, &pxa_mach_ops);
++}
++
++int pxa_wm97xx_remove(struct device *dev)
++{
++ struct wm97xx *wm = dev->driver_data;
++ wm97xx_unregister_mach_ops (wm);
++ return 0;
++}
++
++static struct device_driver pxa_wm97xx_driver = {
++ .name = "wm97xx-touchscreen",
++ .bus = &wm97xx_bus_type,
++ .owner = THIS_MODULE,
++ .probe = pxa_wm97xx_probe,
++ .remove = pxa_wm97xx_remove
++};
++
++static int __init pxa_wm97xx_init(void)
++{
++ return driver_register(&pxa_wm97xx_driver);
++}
++
++static void __exit pxa_wm97xx_exit(void)
++{
++ driver_unregister(&pxa_wm97xx_driver);
++}
++
++module_init(pxa_wm97xx_init);
++module_exit(pxa_wm97xx_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
++MODULE_DESCRIPTION("wm97xx continuous touch driver for pxa2xx");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17/drivers/input/touchscreen/wm9705.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/drivers/input/touchscreen/wm9705.c 2006-09-19 20:36:47.969052000 +0200
+@@ -0,0 +1,360 @@
++/*
++ * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec.
++ *
++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ * Russell King <rmk@arm.linux.org.uk>
++ *
++ * 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.
++ *
++ * Revision history
++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com>
++ * Added pre and post sample calls.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/input.h>
++#include <linux/delay.h>
++#include <linux/bitops.h>
++#include <linux/wm97xx.h>
++
++#define TS_NAME "wm97xx"
++#define WM9705_VERSION "0.62"
++#define DEFAULT_PRESSURE 0xb0c0
++
++/*
++ * Debug
++ */
++#if 0
++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...)
++#endif
++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
++
++/*
++ * Module parameters
++ */
++
++/*
++ * 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.
++ */
++static int pil = 0;
++module_param(pil, int, 0);
++MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
++
++/*
++ * Set threshold for pressure measurement.
++ *
++ * Pen down pressure below threshold is ignored.
++ */
++static int pressure = DEFAULT_PRESSURE & 0xfff;
++module_param(pressure, int, 0);
++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
++
++/*
++ * 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.
++ */
++static int delay = 4;
++module_param(delay, int, 0);
++MODULE_PARM_DESC(delay, "Set adc sample delay.");
++
++/*
++ * 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.
++ */
++static int pdd = 8;
++module_param(pdd, int, 0);
++MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold");
++
++/*
++ * Set adc mask function.
++ *
++ * Sources of glitch noise, such as signals driving an LCD display, may feed
++ * through to the touch screen plates and affect measurement accuracy. In
++ * order to minimise this, a signal may be applied to the MASK pin to delay or
++ * synchronise the sampling.
++ *
++ * 0 = No delay or sync
++ * 1 = High on pin stops conversions
++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
++ * 3 = Edge triggered, edge on pin starts conversion after delay param
++ */
++static int mask = 0;
++module_param(mask, int, 0);
++MODULE_PARM_DESC(mask, "Set adc mask function.");
++
++/*
++ * ADC sample delay times in uS
++ */
++static const int delay_table[] = {
++ 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(int d)
++{
++ udelay (3 * AC97_LINK_FRAME + delay_table [d]);
++}
++
++/*
++ * set up the physical settings of the WM9705
++ */
++static void init_wm9705_phy(struct wm97xx* wm)
++{
++ u16 dig1 = 0, dig2 = WM97XX_RPR;
++
++ /*
++ * mute VIDEO and AUX as they share X and Y touchscreen
++ * inputs on the WM9705
++ */
++ wm97xx_reg_write(wm, AC97_AUX, 0x8000);
++ wm97xx_reg_write(wm, AC97_VIDEO, 0x8000);
++
++ /* touchpanel pressure current*/
++ if (pil == 2) {
++ dig2 |= WM9705_PIL;
++ dbg("setting pressure measurement current to 400uA.");
++ } else if (pil)
++ dbg("setting pressure measurement current to 200uA.");
++ if(!pil)
++ pressure = 0;
++
++ /* polling mode sample settling delay */
++ if (delay!=4) {
++ if (delay < 0 || delay > 15) {
++ dbg("supplied delay out of range.");
++ delay = 4;
++ }
++ }
++ dig1 &= 0xff0f;
++ dig1 |= WM97XX_DELAY(delay);
++ dbg("setting adc sample delay to %d u Secs.", delay_table[delay]);
++
++ /* WM9705 pdd */
++ dig2 |= (pdd & 0x000f);
++ dbg("setting pdd to Vmid/%d", 1 - (pdd & 0x000f));
++
++ /* mask */
++ dig2 |= ((mask & 0x3) << 4);
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
++}
++
++static int wm9705_digitiser_ioctl(struct wm97xx* wm, int cmd)
++{
++ switch(cmd) {
++ case WM97XX_DIG_START:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] | WM97XX_PRP_DET_DIG);
++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
++ break;
++ case WM97XX_DIG_STOP:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] & ~WM97XX_PRP_DET_DIG);
++ break;
++ case WM97XX_AUX_PREPARE:
++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
++ break;
++ case WM97XX_DIG_RESTORE:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
++ break;
++ case WM97XX_PHY_INIT:
++ init_wm9705_phy(wm);
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static inline int is_pden (struct wm97xx* wm)
++{
++ return wm->dig[2] & WM9705_PDEN;
++}
++
++/*
++ * Read a sample from the WM9705 adc in polling mode.
++ */
++static int wm9705_poll_sample (struct wm97xx* wm, int adcsel, int *sample)
++{
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ if (adcsel & 0x8000)
++ adcsel = ((adcsel & 0x7fff) + 3) << 12;
++
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(adcsel);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
++
++ /* wait 3 AC97 time slots + delay for conversion */
++ poll_delay (delay);
++
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dbg ("adc sample timeout");
++ return RC_PENUP;
++ }
++
++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(adcsel);
++
++ /* check we have correct sample */
++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
++ dbg ("adc wrong sample, read %x got %x", adcsel,
++ *sample & WM97XX_ADCSEL_MASK);
++ return RC_PENUP;
++ }
++
++ if (!(*sample & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++
++ return RC_VALID;
++}
++
++/*
++ * Sample the WM9705 touchscreen in polling mode
++ */
++static int wm9705_poll_touch(struct wm97xx* wm, struct wm97xx_data *data)
++{
++ int rc;
++
++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID)
++ return rc;
++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID)
++ return rc;
++ if (pil) {
++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID)
++ return rc;
++ } else
++ data->p = DEFAULT_PRESSURE;
++
++ return RC_VALID;
++}
++
++/*
++ * Enable WM9705 continuous mode, i.e. touch data is streamed across an AC97 slot
++ */
++static int wm9705_acc_enable (struct wm97xx* wm, int enable)
++{
++ u16 dig1, dig2;
++ int ret = 0;
++
++ dig1 = wm->dig[1];
++ dig2 = wm->dig[2];
++
++ if (enable) {
++ /* continous mode */
++ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0)
++ return ret;
++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
++ WM97XX_DELAY (delay) |
++ WM97XX_SLT (wm->acc_slot) |
++ WM97XX_RATE (wm->acc_rate);
++ if (pil)
++ dig1 |= WM97XX_ADCSEL_PRES;
++ dig2 |= WM9705_PDEN;
++ } else {
++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
++ dig2 &= ~WM9705_PDEN;
++ if (wm->mach_ops->acc_shutdown)
++ wm->mach_ops->acc_shutdown(wm);
++ }
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
++ return ret;
++}
++
++struct wm97xx_codec_drv wm97xx_codec = {
++ .id = WM9705_ID2,
++ .name = "wm9705",
++ .poll_sample = wm9705_poll_sample,
++ .poll_touch = wm9705_poll_touch,
++ .acc_enable = wm9705_acc_enable,
++ .digitiser_ioctl = wm9705_digitiser_ioctl,
++};
++
++EXPORT_SYMBOL_GPL(wm97xx_codec);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("WM9705 Touch Screen Driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17/drivers/input/touchscreen/wm9712.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/drivers/input/touchscreen/wm9712.c 2006-09-19 20:36:47.969052000 +0200
+@@ -0,0 +1,464 @@
++/*
++ * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs.
++ *
++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ * Russell King <rmk@arm.linux.org.uk>
++ *
++ * 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.
++ *
++ * Revision history
++ * 4th Jul 2005 Initial version.
++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com>
++ * Added pre and post sample calls.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/input.h>
++#include <linux/delay.h>
++#include <linux/bitops.h>
++#include <linux/wm97xx.h>
++
++#define TS_NAME "wm97xx"
++#define WM9712_VERSION "0.61"
++#define DEFAULT_PRESSURE 0xb0c0
++
++/*
++ * Debug
++ */
++#if 0
++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...)
++#endif
++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
++
++/*
++ * Module parameters
++ */
++
++/*
++ * 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 event.
++ */
++static int rpu = 3;
++module_param(rpu, int, 0);
++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
++
++/*
++ * 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.
++ */
++static int pil = 0;
++module_param(pil, int, 0);
++MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
++
++/*
++ * Set threshold for pressure measurement.
++ *
++ * Pen down pressure below threshold is ignored.
++ */
++static int pressure = DEFAULT_PRESSURE & 0xfff;
++module_param(pressure, int, 0);
++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
++
++/*
++ * 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.
++ */
++static int delay = 3;
++module_param(delay, int, 0);
++MODULE_PARM_DESC(delay, "Set adc sample delay.");
++
++/*
++ * Set five_wire = 1 to use a 5 wire touchscreen.
++ *
++ * NOTE: Five wire mode does not allow for readback of pressure.
++ */
++static int five_wire;
++module_param(five_wire, int, 0);
++MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen.");
++
++/*
++ * Set adc mask function.
++ *
++ * Sources of glitch noise, such as signals driving an LCD display, may feed
++ * through to the touch screen plates and affect measurement accuracy. In
++ * order to minimise this, a signal may be applied to the MASK pin to delay or
++ * synchronise the sampling.
++ *
++ * 0 = No delay or sync
++ * 1 = High on pin stops conversions
++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
++ * 3 = Edge triggered, edge on pin starts conversion after delay param
++ */
++static int mask = 0;
++module_param(mask, int, 0);
++MODULE_PARM_DESC(mask, "Set adc mask function.");
++
++/*
++ * Coordinate Polling Enable.
++ *
++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
++ * for every poll.
++ */
++static int coord = 0;
++module_param(coord, int, 0);
++MODULE_PARM_DESC(coord, "Polling coordinate mode");
++
++/*
++ * ADC sample delay times in uS
++ */
++static const int delay_table[] = {
++ 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(int d)
++{
++ udelay (3 * AC97_LINK_FRAME + delay_table [d]);
++}
++
++/*
++ * set up the physical settings of the WM9712
++ */
++static void init_wm9712_phy(struct wm97xx* wm)
++{
++ u16 dig1 = 0;
++ u16 dig2 = WM97XX_RPR | WM9712_RPU(1);
++
++ /* WM9712 rpu */
++ if (rpu) {
++ dig2 &= 0xffc0;
++ dig2 |= WM9712_RPU(rpu);
++ dbg("setting pen detect pull-up to %d Ohms",64000 / rpu);
++ }
++
++ /* touchpanel pressure current*/
++ if (pil == 2) {
++ dig2 |= WM9712_PIL;
++ dbg("setting pressure measurement current to 400uA.");
++ } else if (pil)
++ dbg("setting pressure measurement current to 200uA.");
++ if(!pil)
++ pressure = 0;
++
++ /* WM9712 five wire */
++ if (five_wire) {
++ dig2 |= WM9712_45W;
++ dbg("setting 5-wire touchscreen mode.");
++ }
++
++ /* polling mode sample settling delay */
++ if (delay < 0 || delay > 15) {
++ dbg("supplied delay out of range.");
++ delay = 4;
++ }
++ dig1 &= 0xff0f;
++ dig1 |= WM97XX_DELAY(delay);
++ dbg("setting adc sample delay to %d u Secs.", delay_table[delay]);
++
++ /* mask */
++ dig2 |= ((mask & 0x3) << 6);
++ if (mask) {
++ u16 reg;
++ /* Set GPIO4 as Mask Pin*/
++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4);
++ }
++
++ /* wait - coord mode */
++ if(coord)
++ dig2 |= WM9712_WAIT;
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
++}
++
++static int wm9712_digitiser_ioctl(struct wm97xx* wm, int cmd)
++{
++ u16 dig2 = wm->dig[2];
++
++ switch(cmd) {
++ case WM97XX_DIG_START:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 | WM97XX_PRP_DET_DIG);
++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
++ break;
++ case WM97XX_DIG_STOP:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 & ~WM97XX_PRP_DET_DIG);
++ break;
++ case WM97XX_AUX_PREPARE:
++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
++ break;
++ case WM97XX_DIG_RESTORE:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
++ break;
++ case WM97XX_PHY_INIT:
++ init_wm9712_phy(wm);
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static inline int is_pden (struct wm97xx* wm)
++{
++ return wm->dig[2] & WM9712_PDEN;
++}
++
++/*
++ * Read a sample from the WM9712 adc in polling mode.
++ */
++static int wm9712_poll_sample (struct wm97xx* wm, int adcsel, int *sample)
++{
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ if (adcsel & 0x8000)
++ adcsel = ((adcsel & 0x7fff) + 3) << 12;
++
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(adcsel);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
++
++ /* wait 3 AC97 time slots + delay for conversion */
++ poll_delay (delay);
++
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dbg ("adc sample timeout");
++ return RC_PENUP;
++ }
++
++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(adcsel);
++
++ /* check we have correct sample */
++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
++ dbg ("adc wrong sample, read %x got %x", adcsel,
++ *sample & WM97XX_ADCSEL_MASK);
++ return RC_PENUP;
++ }
++
++ if (!(*sample & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++
++ return RC_VALID;
++}
++
++/*
++ * Read a coord from the WM9712 adc in polling mode.
++ */
++static int wm9712_poll_coord (struct wm97xx* wm, struct wm97xx_data *data)
++{
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
++ WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay));
++
++ /* wait 3 AC97 time slots + delay for conversion and read x */
++ poll_delay(delay);
++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dbg ("adc sample timeout");
++ return RC_PENUP;
++ }
++
++ /* read back y data */
++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (pil)
++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ else
++ data->p = DEFAULT_PRESSURE;
++
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
++
++ /* check we have correct sample */
++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
++ goto err;
++ if(pil && !(data->p & WM97XX_ADCSEL_PRES))
++ goto err;
++
++ if (!(data->x & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++ return RC_VALID;
++err:
++ return RC_PENUP;
++}
++
++/*
++ * Sample the WM9712 touchscreen in polling mode
++ */
++static int wm9712_poll_touch(struct wm97xx* wm, struct wm97xx_data *data)
++{
++ int rc;
++
++ if(coord) {
++ if((rc = wm9712_poll_coord(wm, data)) != RC_VALID)
++ return rc;
++ } else {
++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID)
++ return rc;
++
++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID)
++ return rc;
++
++ if (pil && !five_wire) {
++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID)
++ return rc;
++ } else
++ data->p = DEFAULT_PRESSURE;
++ }
++ return RC_VALID;
++}
++
++/*
++ * Enable WM9712 continuous mode, i.e. touch data is streamed across an AC97 slot
++ */
++static int wm9712_acc_enable (struct wm97xx* wm, int enable)
++{
++ u16 dig1, dig2;
++ int ret = 0;
++
++ dig1 = wm->dig[1];
++ dig2 = wm->dig[2];
++
++ if (enable) {
++ /* continous mode */
++ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0)
++ return ret;
++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
++ WM97XX_DELAY (delay) |
++ WM97XX_SLT (wm->acc_slot) |
++ WM97XX_RATE (wm->acc_rate);
++ if (pil)
++ dig1 |= WM97XX_ADCSEL_PRES;
++ dig2 |= WM9712_PDEN;
++ } else {
++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
++ dig2 &= ~WM9712_PDEN;
++ if (wm->mach_ops->acc_shutdown)
++ wm->mach_ops->acc_shutdown(wm);
++ }
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
++ return 0;
++}
++
++struct wm97xx_codec_drv wm97xx_codec = {
++ .id = WM9712_ID2,
++ .name = "wm9712",
++ .poll_sample = wm9712_poll_sample,
++ .poll_touch = wm9712_poll_touch,
++ .acc_enable = wm9712_acc_enable,
++ .digitiser_ioctl = wm9712_digitiser_ioctl,
++};
++
++EXPORT_SYMBOL_GPL(wm97xx_codec);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("WM9712 Touch Screen Driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17/drivers/input/touchscreen/wm9713.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/drivers/input/touchscreen/wm9713.c 2006-09-19 20:36:47.969052000 +0200
+@@ -0,0 +1,461 @@
++/*
++ * wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec.
++ *
++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ * Russell King <rmk@arm.linux.org.uk>
++ *
++ * 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.
++ *
++ * Revision history
++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com>
++ * Added pre and post sample calls.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/input.h>
++#include <linux/delay.h>
++#include <linux/bitops.h>
++#include <linux/wm97xx.h>
++
++#define TS_NAME "wm97xx"
++#define WM9713_VERSION "0.53"
++#define DEFAULT_PRESSURE 0xb0c0
++
++/*
++ * Debug
++ */
++#if 0
++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...)
++#endif
++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
++
++/*
++ * Module parameters
++ */
++
++/*
++ * 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 event.
++ */
++static int rpu = 1;
++module_param(rpu, int, 0);
++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
++
++/*
++ * 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.
++ */
++static int pil = 0;
++module_param(pil, int, 0);
++MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
++
++/*
++ * Set threshold for pressure measurement.
++ *
++ * Pen down pressure below threshold is ignored.
++ */
++static int pressure = DEFAULT_PRESSURE & 0xfff;
++module_param(pressure, int, 0);
++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
++
++/*
++ * 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.
++ */
++static int delay = 4;
++module_param(delay, int, 0);
++MODULE_PARM_DESC(delay, "Set adc sample delay.");
++
++/*
++ * Set adc mask function.
++ *
++ * Sources of glitch noise, such as signals driving an LCD display, may feed
++ * through to the touch screen plates and affect measurement accuracy. In
++ * order to minimise this, a signal may be applied to the MASK pin to delay or
++ * synchronise the sampling.
++ *
++ * 0 = No delay or sync
++ * 1 = High on pin stops conversions
++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
++ * 3 = Edge triggered, edge on pin starts conversion after delay param
++ */
++static int mask = 0;
++module_param(mask, int, 0);
++MODULE_PARM_DESC(mask, "Set adc mask function.");
++
++/*
++ * Coordinate Polling Enable.
++ *
++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
++ * for every poll.
++ */
++static int coord = 1;
++module_param(coord, int, 0);
++MODULE_PARM_DESC(coord, "Polling coordinate mode");
++
++/*
++ * ADC sample delay times in uS
++ */
++static const int delay_table[] = {
++ 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(int d)
++{
++ udelay (3 * AC97_LINK_FRAME + delay_table [d]);
++}
++
++/*
++ * set up the physical settings of the WM9713
++ */
++static void init_wm9713_phy(struct wm97xx* wm)
++{
++ u16 dig1 = 0, dig2, dig3;
++
++ /* 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.");
++ if(!pil)
++ pressure = 0;
++
++ /* sample settling delay */
++ if (delay < 0 || delay > 15) {
++ info ("supplied delay out of range.");
++ delay = 4;
++ info("setting adc sample delay to %d u Secs.", delay_table[delay]);
++ }
++ dig2 &= 0xff0f;
++ dig2 |= WM97XX_DELAY(delay);
++
++ /* mask */
++ dig3 |= ((mask & 0x3) << 4);
++ if(coord)
++ dig3 |= WM9713_WAIT;
++
++ wm->misc = wm97xx_reg_read(wm, 0x5a);
++
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0);
++}
++
++static int wm9713_digitiser_ioctl(struct wm97xx* wm, int cmd)
++{
++ u16 val = 0;
++
++ switch(cmd){
++ case WM97XX_DIG_START:
++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] | WM97XX_PRP_DET_DIG);
++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
++ break;
++ case WM97XX_DIG_STOP:
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] & ~WM97XX_PRP_DET_DIG);
++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000);
++ break;
++ case WM97XX_AUX_PREPARE:
++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG);
++ break;
++ case WM97XX_DIG_RESTORE:
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]);
++ break;
++ case WM97XX_PHY_INIT:
++ init_wm9713_phy(wm);
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static inline int is_pden (struct wm97xx* wm)
++{
++ return wm->dig[2] & WM9713_PDEN;
++}
++
++/*
++ * Read a sample from the WM9713 adc in polling mode.
++ */
++static int wm9713_poll_sample (struct wm97xx* wm, int adcsel, int *sample)
++{
++ u16 dig1;
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ if (adcsel & 0x8000)
++ adcsel = 1 << ((adcsel & 0x7fff) + 3);
++
++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
++ dig1 &= ~WM9713_ADCSEL_MASK;
++
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(adcsel);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | adcsel |WM9713_POLL);
++
++ /* wait 3 AC97 time slots + delay for conversion */
++ poll_delay(delay);
++
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dbg ("adc sample timeout");
++ return RC_PENUP;
++ }
++
++ *sample =wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(adcsel);
++
++ /* check we have correct sample */
++ if ((*sample & WM97XX_ADCSRC_MASK) != ffs(adcsel >> 1) << 12) {
++ dbg ("adc wrong sample, read %x got %x", adcsel,
++ *sample & WM97XX_ADCSRC_MASK);
++ return RC_PENUP;
++ }
++
++ if (!(*sample & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++
++ return RC_VALID;
++}
++
++/*
++ * Read a coordinate from the WM9713 adc in polling mode.
++ */
++static int wm9713_poll_coord (struct wm97xx* wm, struct wm97xx_data *data)
++{
++ u16 dig1;
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
++ dig1 &= ~WM9713_ADCSEL_MASK;
++ if(pil)
++ dig1 |= WM97XX_ADCSEL_PRES;
++
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | WM9713_POLL | WM9713_COO);
++
++ /* wait 3 AC97 time slots + delay for conversion */
++ poll_delay(delay);
++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dbg ("adc sample timeout");
++ return RC_PENUP;
++ }
++
++ /* read back data */
++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (pil)
++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ else
++ data->p = DEFAULT_PRESSURE;
++
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
++
++ /* check we have correct sample */
++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
++ goto err;
++ if(pil && !(data->p & WM97XX_ADCSEL_PRES))
++ goto err;
++
++ if (!(data->x & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++ return RC_VALID;
++err:
++ return RC_PENUP;
++}
++
++/*
++ * Sample the WM9713 touchscreen in polling mode
++ */
++static int wm9713_poll_touch(struct wm97xx* wm, struct wm97xx_data *data)
++{
++ int rc;
++
++ if(coord) {
++ if((rc = wm9713_poll_coord(wm, data)) != RC_VALID)
++ return rc;
++ } else {
++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x)) != RC_VALID)
++ return rc;
++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y)) != RC_VALID)
++ return rc;
++ if (pil) {
++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES, &data->p)) != RC_VALID)
++ return rc;
++ } else
++ data->p = DEFAULT_PRESSURE;
++ }
++ return RC_VALID;
++}
++
++/*
++ * Enable WM9713 continuous mode, i.e. touch data is streamed across an AC97 slot
++ */
++static int wm9713_acc_enable (struct wm97xx* wm, int enable)
++{
++ u16 dig1, dig2, dig3;
++ int ret = 0;
++
++ dig1 = wm->dig[0];
++ dig2 = wm->dig[1];
++ dig3 = wm->dig[2];
++
++ if (enable) {
++ /* continous mode */
++ if (wm->mach_ops->acc_startup &&
++ (ret = wm->mach_ops->acc_startup(wm)) < 0)
++ return ret;
++
++ dig1 &= ~WM9713_ADCSEL_MASK;
++ dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X | WM9713_ADCSEL_Y;
++ if (pil)
++ dig1 |= WM9713_ADCSEL_PRES;
++ dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK | WM97XX_CM_RATE_MASK);
++ dig2 |= WM97XX_SLEN | WM97XX_DELAY (delay) |
++ WM97XX_SLT (wm->acc_slot) | WM97XX_RATE (wm->acc_rate);
++ dig3 |= WM9713_PDEN;
++ } else {
++ dig1 &= ~(WM9713_CTC | WM9713_COO);
++ dig2 &= ~WM97XX_SLEN;
++ dig3 &= ~WM9713_PDEN;
++ if (wm->mach_ops->acc_shutdown)
++ wm->mach_ops->acc_shutdown(wm);
++ }
++
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
++ return ret;
++}
++
++struct wm97xx_codec_drv wm97xx_codec = {
++ .id = WM9713_ID2,
++ .name = "wm9713",
++ .poll_sample = wm9713_poll_sample,
++ .poll_touch = wm9713_poll_touch,
++ .acc_enable = wm9713_acc_enable,
++ .digitiser_ioctl = wm9713_digitiser_ioctl,
++};
++
++EXPORT_SYMBOL_GPL(wm97xx_codec);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("WM9713 Touch Screen Driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17/drivers/input/touchscreen/wm97xx-core.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/drivers/input/touchscreen/wm97xx-core.c 2006-09-19 20:36:47.969052000 +0200
+@@ -0,0 +1,912 @@
++/*
++ * wm97xx-core.c -- Touch screen driver core for Wolfson WM9705, WM9712
++ * and WM9713 AC97 Codecs.
++ *
++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ * Russell King <rmk@arm.linux.org.uk>
++ *
++ * 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.
++ *
++ * Notes:
++ *
++ * Features:
++ * - supports WM9705, WM9712, WM9713
++ * - polling mode
++ * - continuous mode (arch-dependent)
++ * - adjustable rpu/dpp settings
++ * - adjustable pressure current
++ * - adjustable sample settle delay
++ * - 4 and 5 wire touchscreens (5 wire is WM9712 only)
++ * - pen down detection
++ * - battery monitor
++ * - sample AUX adc's
++ * - power management
++ * - codec GPIO
++ * - codec event notification
++ * Todo
++ * - Support for async sampling control for noisy LCD'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
++ * codec_mutexing 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.
++ * 16th Nov 2003 Ian Molton <spyro@f2s.com>
++ * Modified so that it suits the new 2.6 driver model.
++ * 25th Jan 2004 Andrew Zabolotny <zap@homelink.ru>
++ * Implemented IRQ-driven pen down detection, implemented
++ * the private API meant to be exposed to platform-specific
++ * drivers, reorganized the driver so that it supports
++ * an arbitrary number of devices.
++ * 1st Feb 2004 Moved continuous mode handling to a separate
++ * architecture-dependent file. For now only PXA
++ * built-in AC97 controller is supported (pxa-ac97-wm97xx.c).
++ * 11th Feb 2004 Reduced CPU usage by keeping a cached copy of both
++ * digitizer registers instead of reading them every time.
++ * A reorganization of the whole code for better
++ * error handling.
++ * 17th Apr 2004 Added BMON support.
++ * 17th Nov 2004 Added codec GPIO, codec event handling (real and virtual
++ * GPIOs) and 2.6 power management.
++ * 29th Nov 2004 Added WM9713 support.
++ * 4th Jul 2005 Moved codec specific code out to seperate files.
++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com>
++ * Added bus interface.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/string.h>
++#include <linux/proc_fs.h>
++#include <linux/pm.h>
++#include <linux/interrupt.h>
++#include <linux/bitops.h>
++#include <linux/workqueue.h>
++#include <linux/device.h>
++#include <linux/wm97xx.h>
++#include <asm/uaccess.h>
++#include <asm/io.h>
++
++#define TS_NAME "wm97xx"
++#define WM_CORE_VERSION "0.63"
++#define DEFAULT_PRESSURE 0xb0c0
++
++/*
++ * WM97xx - enable/disable AUX ADC sysfs
++ */
++static int aux_sys = 1;
++module_param(aux_sys, int, 0);
++MODULE_PARM_DESC(aux_sys, "enable AUX ADC sysfs entries");
++
++/*
++ * WM97xx - enable/disable codec status sysfs
++ */
++static int status_sys = 1;
++module_param(status_sys, int, 0);
++MODULE_PARM_DESC(status_sys, "enable codec status sysfs entries");
++
++/*
++ * Touchscreen absolute values
++ *
++ * These parameters are used to help the input layer discard out of
++ * range readings and reduce jitter etc.
++ *
++ * o min, max:- indicate the min and max values your touch screen returns
++ * o fuzz:- use a higher number to reduce jitter
++ *
++ * The default values correspond to Mainstone II in QVGA mode
++ *
++ * Please read
++ * Documentation/input/input-programming.txt for more details.
++ */
++
++static int abs_x[3] = {350,3900,5};
++module_param_array(abs_x, int, NULL, 0);
++MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz");
++
++static int abs_y[3] = {320,3750,40};
++module_param_array(abs_y, int, NULL, 0);
++MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz");
++
++static int abs_p[3] = {0,150,4};
++module_param_array(abs_p, int, NULL, 0);
++MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz");
++
++/*
++ * Debug
++ */
++#if 0
++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...)
++#endif
++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
++
++/* codec AC97 IO access */
++int wm97xx_reg_read(struct wm97xx *wm, u16 reg)
++{
++ if (wm->ac97)
++ return wm->ac97->bus->ops->read(wm->ac97, reg);
++ else
++ return -1;
++}
++
++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val)
++{
++ /* cache digitiser registers */
++ if(reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3)
++ wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val;
++
++ /* cache gpio regs */
++ if(reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE)
++ wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val;
++
++ /* wm9713 irq reg */
++ if(reg == 0x5a)
++ wm->misc = val;
++
++ if (wm->ac97)
++ wm->ac97->bus->ops->write(wm->ac97, reg, val);
++}
++
++
++/**
++ * wm97xx_read_aux_adc - Read the aux adc.
++ * @wm: wm97xx device.
++ * @adcsel: codec ADC to be read
++ *
++ * Reads the selected AUX ADC.
++ */
++
++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
++{
++ int power_adc = 0, auxval;
++ u16 power = 0;
++
++ /* get codec */
++ mutex_lock(&wm->codec_mutex);
++
++ /* When the touchscreen is not in use, we may have to power up the AUX ADC
++ * before we can use sample the AUX inputs->
++ */
++ if (wm->id == WM9713_ID2 &&
++ (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) {
++ power_adc = 1;
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff);
++ }
++
++ /* Prepare the codec for AUX reading */
++ wm->codec->digitiser_ioctl(wm, WM97XX_AUX_PREPARE);
++
++ /* Turn polling mode on to read AUX ADC */
++ wm->pen_probably_down = 1;
++ wm->codec->poll_sample(wm, adcsel, &auxval);
++
++ if (power_adc)
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000);
++
++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_RESTORE);
++
++ wm->pen_probably_down = 0;
++
++ mutex_unlock(&wm->codec_mutex);
++ return auxval & 0xfff;
++}
++
++#define WM97XX_AUX_ATTR(name,input) \
++static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \
++{ \
++ struct wm97xx *wm = (struct wm97xx*)dev->driver_data; \
++ return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, input)); \
++} \
++static DEVICE_ATTR(name, 0444, name##_show, NULL)
++
++WM97XX_AUX_ATTR(aux1, WM97XX_AUX_ID1);
++WM97XX_AUX_ATTR(aux2, WM97XX_AUX_ID2);
++WM97XX_AUX_ATTR(aux3, WM97XX_AUX_ID3);
++WM97XX_AUX_ATTR(aux4, WM97XX_AUX_ID4);
++
++#define WM97XX_STATUS_ATTR(name) \
++static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \
++{ \
++ struct wm97xx *wm = (struct wm97xx*)dev->driver_data; \
++ return sprintf(buf, "%d\n", wm97xx_reg_read(wm, AC97_GPIO_STATUS)); \
++} \
++static DEVICE_ATTR(name, 0444, name##_show, NULL)
++
++WM97XX_STATUS_ATTR(gpio);
++
++static int wm97xx_sys_add(struct device *dev)
++{
++ if (aux_sys) {
++ device_create_file(dev, &dev_attr_aux1);
++ device_create_file(dev, &dev_attr_aux2);
++ device_create_file(dev, &dev_attr_aux3);
++ device_create_file(dev, &dev_attr_aux4);
++ }
++ if (status_sys)
++ device_create_file(dev, &dev_attr_gpio);
++ return 0;
++}
++
++static void wm97xx_sys_remove(struct device *dev)
++{
++ if (status_sys)
++ device_remove_file(dev, &dev_attr_gpio);
++ if (aux_sys) {
++ device_remove_file(dev, &dev_attr_aux1);
++ device_remove_file(dev, &dev_attr_aux2);
++ device_remove_file(dev, &dev_attr_aux3);
++ device_remove_file(dev, &dev_attr_aux4);
++ }
++}
++
++/**
++ * wm97xx_get_gpio - Get the status of a codec GPIO.
++ * @wm: wm97xx device.
++ * @gpio: gpio
++ *
++ * Get the status of a codec GPIO pin
++ */
++
++wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio)
++{
++ u16 status;
++ wm97xx_gpio_status_t ret;
++
++ mutex_lock(&wm->codec_mutex);
++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
++
++ if (status & gpio)
++ ret = WM97XX_GPIO_HIGH;
++ else
++ ret = WM97XX_GPIO_LOW;
++
++ mutex_unlock(&wm->codec_mutex);
++ return ret;
++}
++
++/**
++ * wm97xx_set_gpio - Set the status of a codec GPIO.
++ * @wm: wm97xx device.
++ * @gpio: gpio
++ *
++ *
++ * Set the status of a codec GPIO pin
++ */
++
++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
++ wm97xx_gpio_status_t status)
++{
++ u16 reg;
++
++ mutex_lock(&wm->codec_mutex);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
++
++ if (status & WM97XX_GPIO_HIGH)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ if (wm->id == WM9712_ID2)
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1);
++ else
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg);
++ mutex_unlock(&wm->codec_mutex);
++}
++
++/*
++ * Codec GPIO pin configuration, this set's pin direction, polarity,
++ * stickyness and wake up.
++ */
++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir,
++ wm97xx_gpio_pol_t pol, wm97xx_gpio_sticky_t sticky,
++ wm97xx_gpio_wake_t wake)
++{
++ u16 reg;
++
++ mutex_lock(&wm->codec_mutex);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
++
++ if (pol == WM97XX_GPIO_POL_HIGH)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
++
++ if (sticky == WM97XX_GPIO_STICKY)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
++
++ if (wake == WM97XX_GPIO_WAKE)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
++
++ if (dir == WM97XX_GPIO_IN)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg);
++ mutex_unlock(&wm->codec_mutex);
++}
++
++/*
++ * Handle a pen down interrupt.
++ */
++static void wm97xx_pen_irq_worker(void *ptr)
++{
++ struct wm97xx *wm = (struct wm97xx *) ptr;
++
++ /* do we need to enable the touch panel reader */
++ if (wm->id == WM9705_ID2) {
++ if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN)
++ wm->pen_is_down = 1;
++ else
++ wm->pen_is_down = 0;
++ wake_up_interruptible(&wm->pen_irq_wait);
++ } else {
++ u16 status, pol;
++ mutex_lock(&wm->codec_mutex);
++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
++ pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
++
++ if (WM97XX_GPIO_13 & pol & status) {
++ wm->pen_is_down = 1;
++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol & ~WM97XX_GPIO_13);
++ } else {
++ wm->pen_is_down = 0;
++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol | WM97XX_GPIO_13);
++ }
++
++ if (wm->id == WM9712_ID2)
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status & ~WM97XX_GPIO_13) << 1);
++ else
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, status & ~WM97XX_GPIO_13);
++ mutex_unlock(&wm->codec_mutex);
++ wake_up_interruptible(&wm->pen_irq_wait);
++ }
++
++ if (!wm->pen_is_down && wm->mach_ops && wm->mach_ops->acc_enabled)
++ wm->mach_ops->acc_pen_up(wm);
++ enable_irq(wm->pen_irq);
++}
++
++/*
++ * Codec PENDOWN irq handler
++ *
++ * We have to disable the codec interrupt in the handler because it can
++ * take upto 1ms to clear the interrupt source. The interrupt is then enabled
++ * again in the slow handler when the source has been cleared.
++ */
++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id,
++ struct pt_regs *regs)
++{
++ struct wm97xx *wm = (struct wm97xx *) dev_id;
++ disable_irq(wm->pen_irq);
++ queue_work(wm->pen_irq_workq, &wm->pen_event_work);
++ return IRQ_HANDLED;
++}
++
++/*
++ * initialise pen IRQ handler and workqueue
++ */
++static int wm97xx_init_pen_irq(struct wm97xx *wm)
++{
++ u16 reg;
++
++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker, wm);
++ if ((wm->pen_irq_workq =
++ create_singlethread_workqueue("kwm97pen")) == NULL) {
++ err("could not create pen irq work queue");
++ wm->pen_irq = 0;
++ return -EINVAL;
++ }
++
++ if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) {
++ err("could not register codec pen down interrupt, will poll for pen down");
++ destroy_workqueue(wm->pen_irq_workq);
++ wm->pen_irq = 0;
++ return -EINVAL;
++ }
++
++ /* enable PEN down on wm9712/13 */
++ if (wm->id != WM9705_ID2) {
++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg & 0xfffb);
++ reg = wm97xx_reg_read(wm, 0x5a);
++ wm97xx_reg_write(wm, 0x5a, reg & ~0x0001);
++ }
++
++ return 0;
++}
++
++/* Private struct for communication between struct wm97xx_tshread
++ * and wm97xx_read_samples */
++struct ts_state {
++ int sleep_time;
++ int min_sleep_time;
++};
++
++static int wm97xx_read_samples(struct wm97xx *wm, struct ts_state *state)
++{
++ struct wm97xx_data data;
++ int rc;
++
++ mutex_lock(&wm->codec_mutex);
++
++ if (wm->mach_ops && wm->mach_ops->acc_enabled)
++ rc = wm->mach_ops->acc_pen_down(wm);
++ else
++ rc = wm->codec->poll_touch(wm, &data);
++
++ if (rc & RC_PENUP) {
++ if (wm->pen_is_down) {
++ wm->pen_is_down = 0;
++ dbg("pen up");
++ input_report_abs(wm->input_dev, ABS_PRESSURE, 0);
++ input_sync(wm->input_dev);
++ } else if (!(rc & RC_AGAIN)) {
++ /* We need high frequency updates only while pen is down,
++ * the user never will be able to touch screen faster than
++ * a few times per second... On the other hand, when the
++ * user is actively working with the touchscreen we don't
++ * want to lose the quick response. So we will slowly
++ * increase sleep time after the pen is up and quicky
++ * restore it to ~one task switch when pen is down again.
++ */
++ if (state->sleep_time < HZ / 10)
++ state->sleep_time++;
++ }
++
++ } else if (rc & RC_VALID) {
++ dbg("pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n",
++ data.x >> 12, data.x & 0xfff, data.y >> 12,
++ data.y & 0xfff, data.p >> 12, data.p & 0xfff);
++ input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff);
++ input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff);
++ input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff);
++ input_sync(wm->input_dev);
++ wm->pen_is_down = 1;
++ state->sleep_time = state->min_sleep_time;
++ } else if (rc & RC_PENDOWN) {
++ dbg("pen down");
++ wm->pen_is_down = 1;
++ state->sleep_time = state->min_sleep_time;
++ }
++
++ mutex_unlock(&wm->codec_mutex);
++ return rc;
++}
++
++/*
++* The touchscreen sample reader thread.
++*/
++static int wm97xx_ts_read(void *data)
++{
++ int rc;
++ struct ts_state state;
++ struct wm97xx *wm = (struct wm97xx *) data;
++
++ /* set up thread context */
++ wm->ts_task = current;
++ daemonize("kwm97xxts");
++
++ if (wm->codec == NULL) {
++ wm->ts_task = NULL;
++ printk(KERN_ERR "codec is NULL, bailing\n");
++ }
++
++ complete(&wm->ts_init);
++ wm->pen_is_down = 0;
++ state.min_sleep_time = HZ >= 100 ? HZ / 100 : 1;
++ if (state.min_sleep_time < 1)
++ state.min_sleep_time = 1;
++ state.sleep_time = state.min_sleep_time;
++
++ /* touch reader loop */
++ while (wm->ts_task) {
++ do {
++ try_to_freeze();
++ rc = wm97xx_read_samples(wm, &state);
++ } while (rc & RC_AGAIN);
++ if (!wm->pen_is_down && wm->pen_irq) {
++ /* Nice, we don't have to poll for pen down event */
++ wait_event_interruptible(wm->pen_irq_wait, wm->pen_is_down);
++ } else {
++ set_task_state(current, TASK_INTERRUPTIBLE);
++ schedule_timeout(state.sleep_time);
++ }
++ }
++ complete_and_exit(&wm->ts_exit, 0);
++}
++
++/**
++ * wm97xx_ts_input_open - Open the touch screen input device.
++ * @idev: Input device to be opened.
++ *
++ * Called by the input sub system to open a wm97xx touchscreen device.
++ * Starts the touchscreen thread and touch digitiser.
++ */
++static int wm97xx_ts_input_open(struct input_dev *idev)
++{
++ int ret = 0;
++ struct wm97xx *wm = (struct wm97xx *) idev->private;
++
++ mutex_lock(&wm->codec_mutex);
++ /* first time opened ? */
++ if (wm->ts_use_count++ == 0) {
++ /* start touchscreen thread */
++ init_completion(&wm->ts_init);
++ init_completion(&wm->ts_exit);
++ ret = kernel_thread(wm97xx_ts_read, wm, CLONE_KERNEL);
++
++ if (ret >= 0) {
++ wait_for_completion(&wm->ts_init);
++ if (wm->ts_task == NULL)
++ ret = -EINVAL;
++ } else {
++ mutex_unlock(&wm->codec_mutex);
++ return ret;
++ }
++
++ /* start digitiser */
++ if (wm->mach_ops && wm->mach_ops->acc_enabled)
++ wm->codec->acc_enable(wm, 1);
++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_START);
++
++ /* init pen down/up irq handling */
++ if (wm->pen_irq) {
++ wm97xx_init_pen_irq(wm);
++
++ if (wm->pen_irq == 0) {
++ /* we failed to get an irq for pen down events,
++ * so we resort to polling. kickstart the reader */
++ wm->pen_is_down = 1;
++ wake_up_interruptible(&wm->pen_irq_wait);
++ }
++ }
++ }
++
++ mutex_unlock(&wm->codec_mutex);
++ return 0;
++}
++
++/**
++ * wm97xx_ts_input_close - Close the touch screen input device.
++ * @idev: Input device to be closed.
++ *
++ * Called by the input sub system to close a wm97xx touchscreen device.
++ * Kills the touchscreen thread and stops the touch digitiser.
++ */
++
++static void wm97xx_ts_input_close(struct input_dev *idev)
++{
++ struct wm97xx *wm = (struct wm97xx *) idev->private;
++
++ mutex_lock(&wm->codec_mutex);
++ if (--wm->ts_use_count == 0) {
++ /* destroy workqueues and free irqs */
++ if (wm->pen_irq) {
++ free_irq(wm->pen_irq, wm);
++ destroy_workqueue(wm->pen_irq_workq);
++ }
++
++ /* kill thread */
++ if (wm->ts_task) {
++ wm->ts_task = NULL;
++ wm->pen_is_down = 1;
++ wake_up_interruptible(&wm->pen_irq_wait);
++ wait_for_completion(&wm->ts_exit);
++ wm->pen_is_down = 0;
++ }
++
++ /* stop digitiser */
++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_STOP);
++ if (wm->mach_ops && wm->mach_ops->acc_enabled)
++ wm->codec->acc_enable(wm, 0);
++ }
++ mutex_unlock(&wm->codec_mutex);
++}
++
++static int wm97xx_bus_match(struct device *dev, struct device_driver *drv)
++{
++ return !(strcmp(dev->bus_id,drv->name));
++}
++
++/*
++ * The AC97 audio driver will do all the Codec suspend and resume
++ * tasks. This is just for anything machine specific or extra.
++ */
++static int wm97xx_bus_suspend(struct device *dev, pm_message_t state)
++{
++ int ret = 0;
++
++ if (dev->driver && dev->driver->suspend)
++ ret = dev->driver->suspend(dev, state);
++
++ return ret;
++}
++
++static int wm97xx_bus_resume(struct device *dev)
++{
++ int ret = 0;
++
++ if (dev->driver && dev->driver->resume)
++ ret = dev->driver->resume(dev);
++
++ return ret;
++}
++
++struct bus_type wm97xx_bus_type = {
++ .name = "wm97xx",
++ .match = wm97xx_bus_match,
++ .suspend = wm97xx_bus_suspend,
++ .resume = wm97xx_bus_resume,
++};
++
++static void wm97xx_release(struct device *dev)
++{
++ kfree(dev);
++}
++
++static int wm97xx_probe(struct device *dev)
++{
++ struct wm97xx* wm;
++ int ret = 0, id = 0;
++
++ if (!(wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL)))
++ return -ENOMEM;
++ mutex_init(&wm->codec_mutex);
++
++ init_waitqueue_head(&wm->pen_irq_wait);
++ wm->dev = dev;
++ dev->driver_data = wm;
++ wm->ac97 = to_ac97_t(dev);
++
++ /* check that we have a supported codec */
++ if ((id = wm97xx_reg_read(wm, AC97_VENDOR_ID1)) != WM97XX_ID1) {
++ err("could not find a wm97xx, found a %x instead\n", id);
++ kfree(wm);
++ return -ENODEV;
++ }
++
++ wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2);
++ if(wm->id != wm97xx_codec.id) {
++ err("could not find a the selected codec, please build for wm97%2x", wm->id & 0xff);
++ kfree(wm);
++ return -ENODEV;
++ }
++
++ if((wm->input_dev = input_allocate_device()) == NULL) {
++ kfree(wm);
++ return -ENOMEM;
++ }
++
++ /* set up touch configuration */
++ info("detected a wm97%2x codec", wm->id & 0xff);
++ wm->input_dev->name = "wm97xx touchscreen";
++ wm->input_dev->open = wm97xx_ts_input_open;
++ wm->input_dev->close = wm97xx_ts_input_close;
++ set_bit(EV_ABS, wm->input_dev->evbit);
++ set_bit(ABS_X, wm->input_dev->absbit);
++ set_bit(ABS_Y, wm->input_dev->absbit);
++ set_bit(ABS_PRESSURE, wm->input_dev->absbit);
++ wm->input_dev->absmax[ABS_X] = abs_x[1];
++ wm->input_dev->absmax[ABS_Y] = abs_y[1];
++ wm->input_dev->absmax[ABS_PRESSURE] = abs_p[1];
++ wm->input_dev->absmin[ABS_X] = abs_x[0];
++ wm->input_dev->absmin[ABS_Y] = abs_y[0];
++ wm->input_dev->absmin[ABS_PRESSURE] = abs_p[0];
++ wm->input_dev->absfuzz[ABS_X] = abs_x[2];
++ wm->input_dev->absfuzz[ABS_Y] = abs_y[2];
++ wm->input_dev->absfuzz[ABS_PRESSURE] = abs_p[2];
++ wm->input_dev->private = wm;
++ wm->codec = &wm97xx_codec;
++ if((ret = input_register_device(wm->input_dev)) < 0) {
++ kfree(wm);
++ return -ENOMEM;
++ }
++
++ if(aux_sys)
++ wm97xx_sys_add(dev);
++
++ /* set up physical characteristics */
++ wm->codec->digitiser_ioctl(wm, WM97XX_PHY_INIT);
++
++ /* load gpio cache */
++ wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG);
++ wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
++ wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
++ wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
++ wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
++ wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE);
++
++ /* register our battery device */
++ if (!(wm->battery_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) {
++ ret = -ENOMEM;
++ goto batt_err;
++ }
++ wm->battery_dev->bus = &wm97xx_bus_type;
++ strcpy(wm->battery_dev->bus_id,"wm97xx-battery");
++ wm->battery_dev->driver_data = wm;
++ wm->battery_dev->parent = dev;
++ wm->battery_dev->release = wm97xx_release;
++ if((ret = device_register(wm->battery_dev)) < 0)
++ goto batt_reg_err;
++
++ /* register our extended touch device (for machine specific extensions) */
++ if (!(wm->touch_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) {
++ ret = -ENOMEM;
++ goto touch_err;
++ }
++ wm->touch_dev->bus = &wm97xx_bus_type;
++ strcpy(wm->touch_dev->bus_id,"wm97xx-touchscreen");
++ wm->touch_dev->driver_data = wm;
++ wm->touch_dev->parent = dev;
++ wm->touch_dev->release = wm97xx_release;
++ if((ret = device_register(wm->touch_dev)) < 0)
++ goto touch_reg_err;
++
++ return ret;
++
++touch_reg_err:
++ kfree(wm->touch_dev);
++touch_err:
++ device_unregister(wm->battery_dev);
++batt_reg_err:
++ kfree(wm->battery_dev);
++batt_err:
++ input_unregister_device(wm->input_dev);
++ kfree(wm);
++ return ret;
++}
++
++static int wm97xx_remove(struct device *dev)
++{
++ struct wm97xx *wm = dev_get_drvdata(dev);
++
++ /* Stop touch reader thread */
++ if (wm->ts_task) {
++ wm->ts_task = NULL;
++ wm->pen_is_down = 1;
++ wake_up_interruptible(&wm->pen_irq_wait);
++ wait_for_completion(&wm->ts_exit);
++ }
++ device_unregister(wm->battery_dev);
++ device_unregister(wm->touch_dev);
++ input_unregister_device(wm->input_dev);
++
++ if(aux_sys)
++ wm97xx_sys_remove(dev);
++
++ kfree(wm);
++ return 0;
++}
++
++#ifdef CONFIG_PM
++int wm97xx_resume(struct device* dev)
++{
++ struct wm97xx *wm = dev_get_drvdata(dev);
++
++ /* restore digitiser and gpio's */
++ if(wm->id == WM9713_ID2) {
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]);
++ wm97xx_reg_write(wm, 0x5a, wm->misc);
++ if(wm->ts_use_count) {
++ u16 reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff;
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg);
++ }
++ }
++
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]);
++
++ wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]);
++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]);
++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]);
++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]);
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]);
++ wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]);
++
++ return 0;
++}
++
++#else
++#define wm97xx_resume NULL
++#endif
++
++int wm97xx_register_mach_ops(struct wm97xx *wm, struct wm97xx_mach_ops *mach_ops)
++{
++ mutex_lock(&wm->codec_mutex);
++ if(wm->mach_ops) {
++ mutex_unlock(&wm->codec_mutex);
++ return -EINVAL;
++ }
++ wm->mach_ops = mach_ops;
++ mutex_unlock(&wm->codec_mutex);
++ return 0;
++}
++
++void wm97xx_unregister_mach_ops(struct wm97xx *wm)
++{
++ mutex_lock(&wm->codec_mutex);
++ wm->mach_ops = NULL;
++ mutex_unlock(&wm->codec_mutex);
++}
++
++static struct device_driver wm97xx_driver = {
++ .name = "ac97",
++ .bus = &ac97_bus_type,
++ .owner = THIS_MODULE,
++ .probe = wm97xx_probe,
++ .remove = wm97xx_remove,
++ .resume = wm97xx_resume,
++};
++
++static int __init wm97xx_init(void)
++{
++ int ret;
++
++ info("version %s liam.girdwood@wolfsonmicro.com", WM_CORE_VERSION);
++ if((ret = bus_register(&wm97xx_bus_type)) < 0)
++ return ret;
++ return driver_register(&wm97xx_driver);
++}
++
++static void __exit wm97xx_exit(void)
++{
++ driver_unregister(&wm97xx_driver);
++ bus_unregister(&wm97xx_bus_type);
++}
++
++EXPORT_SYMBOL_GPL(wm97xx_get_gpio);
++EXPORT_SYMBOL_GPL(wm97xx_set_gpio);
++EXPORT_SYMBOL_GPL(wm97xx_config_gpio);
++EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc);
++EXPORT_SYMBOL_GPL(wm97xx_reg_read);
++EXPORT_SYMBOL_GPL(wm97xx_reg_write);
++EXPORT_SYMBOL_GPL(wm97xx_bus_type);
++EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops);
++EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops);
++
++module_init(wm97xx_init);
++module_exit(wm97xx_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17/include/linux/wm97xx.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/include/linux/wm97xx.h 2006-09-19 20:36:47.973052250 +0200
+@@ -0,0 +1,291 @@
++
++/*
++ * Register bits and API for Wolfson WM97xx series of codecs
++ */
++
++#ifndef _LINUX_WM97XX_H
++#define _LINUX_WM97XX_H
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/ac97_codec.h>
++#include <sound/initval.h>
++#include <linux/types.h>
++#include <linux/list.h>
++#include <linux/input.h> /* Input device 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
++
++/*
++ * 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_ADCSEL_MASK 0x7000
++#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_CM_RATE_MASK 0x03f0
++#define WM97XX_RATE(i) (((i & 3) << 8) | ((i & 4) ? 0xf0 : 0))
++#define WM97XX_DELAY(i) ((i << 4) & 0x00f0) /* sample delay times */
++#define WM97XX_DELAY_MASK 0x00f0
++#define WM97XX_SLEN 0x0008 /* slot read back enable */
++#define WM97XX_SLT(i) ((i - 5) & 0x7) /* touchpanel slot selection (5-11) */
++#define WM97XX_SLT_MASK 0x0007
++#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 */
++#define WM97XX_ADCSRC_MASK 0x7000 /* ADC source mask */
++
++#define WM97XX_AUX_ID1 0x8001
++#define WM97XX_AUX_ID2 0x8002
++#define WM97XX_AUX_ID3 0x8003
++#define WM97XX_AUX_ID4 0x8004
++
++
++/* 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_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 */
++
++
++/* 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 */
++#define WM9713_PDEN 0x0800 /* measure only when pen down */
++#define WM9713_ADCSEL_MASK 0x00fe /* ADC selection mask */
++#define WM9713_WAIT 0x0200 /* coordinate wait */
++
++/* 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
++
++/* Codec GPIO's */
++#define WM97XX_MAX_GPIO 16
++#define WM97XX_GPIO_1 (1 << 1)
++#define WM97XX_GPIO_2 (1 << 2)
++#define WM97XX_GPIO_3 (1 << 3)
++#define WM97XX_GPIO_4 (1 << 4)
++#define WM97XX_GPIO_5 (1 << 5)
++#define WM97XX_GPIO_6 (1 << 6)
++#define WM97XX_GPIO_7 (1 << 7)
++#define WM97XX_GPIO_8 (1 << 8)
++#define WM97XX_GPIO_9 (1 << 9)
++#define WM97XX_GPIO_10 (1 << 10)
++#define WM97XX_GPIO_11 (1 << 11)
++#define WM97XX_GPIO_12 (1 << 12)
++#define WM97XX_GPIO_13 (1 << 13)
++#define WM97XX_GPIO_14 (1 << 14)
++#define WM97XX_GPIO_15 (1 << 15)
++
++
++#define AC97_LINK_FRAME 21 /* time in uS for AC97 link frame */
++
++
++/*---------------- Return codes from sample reading functions ---------------*/
++
++/* More data is available; call the sample gathering function again */
++#define RC_AGAIN 0x00000001
++/* The returned sample is valid */
++#define RC_VALID 0x00000002
++/* The pen is up (the first RC_VALID without RC_PENUP means pen is down) */
++#define RC_PENUP 0x00000004
++/* The pen is down (RC_VALID implies RC_PENDOWN, but sometimes it is helpful
++ to tell the handler that the pen is down but we don't know yet his coords,
++ so the handler should not sleep or wait for pendown irq) */
++#define RC_PENDOWN 0x00000008
++
++/* The wm97xx driver provides a private API for writing platform-specific
++ * drivers.
++ */
++
++/* The structure used to return arch specific sampled data into */
++struct wm97xx_data {
++ int x;
++ int y;
++ int p;
++};
++
++/* Codec GPIO status
++ */
++typedef enum {
++ WM97XX_GPIO_HIGH,
++ WM97XX_GPIO_LOW
++} wm97xx_gpio_status_t;
++
++/* Codec GPIO direction
++ */
++typedef enum {
++ WM97XX_GPIO_IN,
++ WM97XX_GPIO_OUT
++} wm97xx_gpio_dir_t;
++
++/* Codec GPIO polarity
++ */
++typedef enum {
++ WM97XX_GPIO_POL_HIGH,
++ WM97XX_GPIO_POL_LOW
++} wm97xx_gpio_pol_t;
++
++/* Codec GPIO sticky
++ */
++typedef enum {
++ WM97XX_GPIO_STICKY,
++ WM97XX_GPIO_NOTSTICKY
++} wm97xx_gpio_sticky_t;
++
++/* Codec GPIO wake
++ */
++typedef enum {
++ WM97XX_GPIO_WAKE,
++ WM97XX_GPIO_NOWAKE
++} wm97xx_gpio_wake_t;
++
++
++/*
++ * Digitiser ioctl commands
++ */
++#define WM97XX_DIG_START 0x1
++#define WM97XX_DIG_STOP 0x2
++#define WM97XX_PHY_INIT 0x3
++#define WM97XX_AUX_PREPARE 0x4
++#define WM97XX_DIG_RESTORE 0x5
++
++struct wm97xx;
++extern struct wm97xx_codec_drv wm97xx_codec;
++
++/*
++ * Codec driver interface - allows mapping to WM9705/12/13 and newer codecs
++ */
++struct wm97xx_codec_drv {
++ u16 id;
++ char *name;
++ int (*poll_sample) (struct wm97xx *, int adcsel, int *sample); /* read 1 sample */
++ int (*poll_touch) (struct wm97xx *, struct wm97xx_data *); /* read X,Y,[P] in poll */
++ int (*digitiser_ioctl) (struct wm97xx *, int cmd);
++ int (*acc_enable) (struct wm97xx *, int enable);
++};
++
++
++/* Machine specific and accelerated touch operations */
++struct wm97xx_mach_ops {
++
++ /* accelerated touch readback - coords are transmited on AC97 link */
++ int acc_enabled;
++ void (*acc_pen_up) (struct wm97xx *);
++ int (*acc_pen_down) (struct wm97xx *);
++ int (*acc_startup) (struct wm97xx *);
++ void (*acc_shutdown) (struct wm97xx *);
++
++ /* pre and post sample - can be used to minimise any analog noise */
++ void (*pre_sample) (int); /* function to run before sampling */
++ void (*post_sample) (int); /* function to run after sampling */
++};
++
++struct wm97xx {
++ u16 dig[3], id, gpio[6], misc; /* Cached codec registers */
++ u16 dig_save[3]; /* saved during aux reading */
++ struct wm97xx_codec_drv *codec; /* attached codec driver*/
++ struct input_dev* input_dev; /* touchscreen input device */
++ ac97_t *ac97; /* ALSA codec access */
++ struct device *dev; /* ALSA device */
++ struct device *battery_dev;
++ struct device *touch_dev;
++ struct wm97xx_mach_ops *mach_ops;
++ struct mutex codec_mutex;
++ struct completion ts_init;
++ struct completion ts_exit;
++ struct task_struct *ts_task;
++ unsigned int pen_irq; /* Pen IRQ number in use */
++ wait_queue_head_t pen_irq_wait; /* Pen IRQ wait queue */
++ struct workqueue_struct *pen_irq_workq;
++ struct work_struct pen_event_work;
++ u16 acc_slot; /* AC97 slot used for acc touch data */
++ u16 acc_rate; /* acc touch data rate */
++ unsigned int ts_use_count;
++ unsigned pen_is_down:1; /* Pen is down */
++ unsigned aux_waiting:1; /* aux measurement waiting */
++ unsigned pen_probably_down:1; /* used in polling mode */
++};
++
++/* Codec GPIO access (not supported on WM9705)
++ * This can be used to set/get codec GPIO and Virtual GPIO status.
++ */
++wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio);
++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
++ wm97xx_gpio_status_t status);
++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio,
++ wm97xx_gpio_dir_t dir,
++ wm97xx_gpio_pol_t pol,
++ wm97xx_gpio_sticky_t sticky,
++ wm97xx_gpio_wake_t wake);
++
++/* codec AC97 IO access */
++int wm97xx_reg_read(struct wm97xx *wm, u16 reg);
++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val);
++
++/* aux adc readback */
++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel);
++
++/* machine ops */
++int wm97xx_register_mach_ops(struct wm97xx *, struct wm97xx_mach_ops *);
++void wm97xx_unregister_mach_ops(struct wm97xx *);
++
++extern struct bus_type wm97xx_bus_type;
++#endif
diff --git a/packages/linux/linux-rp-2.6.23/defconfig-tosa b/packages/linux/linux-rp-2.6.23/defconfig-tosa
index 74fc076608..08c2bbe87a 100644
--- a/packages/linux/linux-rp-2.6.23/defconfig-tosa
+++ b/packages/linux/linux-rp-2.6.23/defconfig-tosa
@@ -248,7 +248,7 @@ CONFIG_PACKET_MMAP=y
CONFIG_UNIX=y
CONFIG_XFRM=y
CONFIG_XFRM_USER=m
-# CONFIG_NET_KEY is not set
+CONFIG_NET_KEY=m
CONFIG_INET=y
# CONFIG_IP_MULTICAST is not set
# CONFIG_IP_ADVANCED_ROUTER is not set
@@ -262,6 +262,9 @@ CONFIG_SYN_COOKIES=y
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
# CONFIG_INET_TUNNEL is not set
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
CONFIG_INET_DIAG=m
CONFIG_INET_TCP_DIAG=m
# CONFIG_TCP_CONG_ADVANCED is not set
@@ -792,9 +795,11 @@ CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_WM97XX=y
# CONFIG_TOUCHSCREEN_WM9705 is not set
CONFIG_TOUCHSCREEN_WM9712=y
+CONFIG_TOUCHSCREEN_TOSA=y
# CONFIG_TOUCHSCREEN_WM9713 is not set
# CONFIG_TOUCHSCREEN_WM97XX_PXA is not set
-# CONFIG_INPUT_MISC is not set
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=m
#
# Hardware I/O ports
@@ -1004,10 +1009,10 @@ CONFIG_FONT_8x8=y
#
# Logo configuration
#
-CONFIG_LOGO=y
+# CONFIG_LOGO is not set
# CONFIG_LOGO_LINUX_MONO is not set
# CONFIG_LOGO_LINUX_VGA16 is not set
-CONFIG_LOGO_LINUX_CLUT224=y
+# CONFIG_LOGO_LINUX_CLUT224 is not set
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_BACKLIGHT_DEVICE=y
@@ -1033,14 +1038,14 @@ CONFIG_SND_OSSEMUL=y
CONFIG_SND_MIXER_OSS=m
CONFIG_SND_PCM_OSS=m
# CONFIG_SND_DYNAMIC_MINORS is not set
-CONFIG_SND_SUPPORT_OLD_API=y
+# CONFIG_SND_SUPPORT_OLD_API is not set
# CONFIG_SND_VERBOSE_PRINTK is not set
# CONFIG_SND_DEBUG is not set
#
# Generic devices
#
-CONFIG_SND_AC97_BUS=y
+CONFIG_AC97_BUS=y
CONFIG_SND_DUMMY=m
# CONFIG_SND_MTPAV is not set
# CONFIG_SND_SERIAL_U16550 is not set
@@ -1072,15 +1077,15 @@ CONFIG_SND_SOC=y
#
# SoC Audio for the Intel PXA2xx
#
-CONFIG_SND_PXA2xx_SOC=y
-CONFIG_SND_PXA2xx_SOC_AC97=y
-# CONFIG_SND_PXA2xx_SOC_MAINSTONE is not set
-# CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM8753 is not set
-# CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM9713 is not set
-# CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM9712 is not set
-# CONFIG_SND_PXA2xx_SOC_CORGI is not set
-# CONFIG_SND_PXA2xx_SOC_SPITZ is not set
-CONFIG_SND_PXA2xx_SOC_TOSA=y
+CONFIG_SND_PXA2XX_SOC=y
+CONFIG_SND_PXA2XX_SOC_AC97=y
+# CONFIG_SND_PXA2XX_SOC_MAINSTONE is not set
+# CONFIG_SND_PXA2XX_SOC_MAINSTONE_WM8753 is not set
+# CONFIG_SND_PXA2XX_SOC_MAINSTONE_WM9713 is not set
+# CONFIG_SND_PXA2XX_SOC_MAINSTONE_WM9712 is not set
+# CONFIG_SND_PXA2XX_SOC_CORGI is not set
+# CONFIG_SND_PXA2XX_SOC_SPITZ is not set
+CONFIG_SND_PXA2XX_SOC_TOSA=y
#
# Soc Codecs
@@ -1100,6 +1105,7 @@ CONFIG_SND_SOC_WM9712=y
# Open Sound System
#
# CONFIG_SOUND_PRIME is not set
+CONFIG_HID=m
#
# USB support
@@ -1301,12 +1307,12 @@ CONFIG_USB_IDMOUSE=m
#
# USB Gadget Support
#
-CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET=m
# CONFIG_USB_GADGET_DEBUG_FILES is not set
CONFIG_USB_GADGET_SELECTED=y
# CONFIG_USB_GADGET_NET2280 is not set
-CONFIG_USB_GADGET_PXA2XX=y
-CONFIG_USB_PXA2XX=y
+CONFIG_USB_GADGET_PXA2XX=m
+CONFIG_USB_PXA2XX=m
# CONFIG_USB_PXA2XX_SMALL is not set
# CONFIG_USB_GADGET_PXA27X is not set
# CONFIG_USB_GADGET_GOKU is not set
@@ -1379,7 +1385,7 @@ CONFIG_FUSE_FS=m
#
# CD-ROM/DVD Filesystems
#
-# CONFIG_ISO9660_FS is not set
+CONFIG_ISO9660_FS=m
# CONFIG_UDF_FS is not set
#
@@ -1399,7 +1405,7 @@ CONFIG_PROC_FS=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
# CONFIG_HUGETLB_PAGE is not set
-CONFIG_RAMFS=y
+# CONFIG_RAMFS is not set
# CONFIG_RELAYFS_FS is not set
# CONFIG_CONFIGFS_FS is not set
@@ -1543,6 +1549,7 @@ CONFIG_DEBUG_KERNEL=y
CONFIG_LOG_BUF_SHIFT=14
CONFIG_DETECT_SOFTLOCKUP=y
CONFIG_TIMER_STATS=y
+# CONFIG_SCHED_DEBUG is not set
# CONFIG_SCHEDSTATS is not set
# CONFIG_DEBUG_SLAB is not set
# CONFIG_DEBUG_PREEMPT is not set
@@ -1572,7 +1579,8 @@ CONFIG_DEBUG_ERRORS=y
# Cryptographic options
#
CONFIG_CRYPTO=y
-CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_ALGAPI=m
+CONFIG_CRYPTO_HMAC=m
CONFIG_CRYPTO_NULL=m
CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MD5=m
diff --git a/packages/linux/linux-rp-2.6.23/sharpsl-pm-postresume-r1.patch b/packages/linux/linux-rp-2.6.23/sharpsl-pm-postresume-r1.patch
new file mode 100644
index 0000000000..e1491d6d49
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/sharpsl-pm-postresume-r1.patch
@@ -0,0 +1,60 @@
+ arch/arm/common/sharpsl_pm.c | 3 +++
+ include/asm-arm/hardware/sharpsl_pm.h | 1 +
+ 2 files changed, 4 insertions(+)
+
+Index: git/include/asm-arm/hardware/sharpsl_pm.h
+===================================================================
+--- git.orig/include/asm-arm/hardware/sharpsl_pm.h 2006-10-31 16:09:33.000000000 +0000
++++ git/include/asm-arm/hardware/sharpsl_pm.h 2006-11-07 22:08:41.000000000 +0000
+@@ -26,6 +26,7 @@ struct sharpsl_charger_machinfo {
+ void (*presuspend)(void);
+ void (*postsuspend)(void);
+ void (*earlyresume)(void);
++ void (*postresume)(void);
+ unsigned long (*read_devdata)(int);
+ #define SHARPSL_BATT_VOLT 1
+ #define SHARPSL_BATT_TEMP 2
+Index: git/arch/arm/common/sharpsl_pm.c
+===================================================================
+--- git.orig/arch/arm/common/sharpsl_pm.c 2006-11-07 22:03:48.000000000 +0000
++++ git/arch/arm/common/sharpsl_pm.c 2006-11-07 22:04:20.000000000 +0000
+@@ -584,6 +584,9 @@ static int corgi_pxa_pm_enter(suspend_st
+ if (sharpsl_pm.machinfo->earlyresume)
+ sharpsl_pm.machinfo->earlyresume();
+
++ if (sharpsl_pm.machinfo->postresume)
++ sharpsl_pm.machinfo->postresume();
++
+ dev_dbg(sharpsl_pm.dev, "SharpSL resuming...\n");
+
+ return 0;
+ arch/arm/common/sharpsl_pm.c | 3 +++
+ include/asm-arm/hardware/sharpsl_pm.h | 1 +
+ 2 files changed, 4 insertions(+)
+
+Index: git/include/asm-arm/hardware/sharpsl_pm.h
+===================================================================
+--- git.orig/include/asm-arm/hardware/sharpsl_pm.h 2006-10-31 16:09:33.000000000 +0000
++++ git/include/asm-arm/hardware/sharpsl_pm.h 2006-11-07 22:08:41.000000000 +0000
+@@ -26,6 +26,7 @@ struct sharpsl_charger_machinfo {
+ void (*presuspend)(void);
+ void (*postsuspend)(void);
+ void (*earlyresume)(void);
++ void (*postresume)(void);
+ unsigned long (*read_devdata)(int);
+ #define SHARPSL_BATT_VOLT 1
+ #define SHARPSL_BATT_TEMP 2
+Index: git/arch/arm/common/sharpsl_pm.c
+===================================================================
+--- git.orig/arch/arm/common/sharpsl_pm.c 2006-11-07 22:03:48.000000000 +0000
++++ git/arch/arm/common/sharpsl_pm.c 2006-11-07 22:04:20.000000000 +0000
+@@ -584,6 +584,9 @@ static int corgi_pxa_pm_enter(suspend_st
+ if (sharpsl_pm.machinfo->earlyresume)
+ sharpsl_pm.machinfo->earlyresume();
+
++ if (sharpsl_pm.machinfo->postresume)
++ sharpsl_pm.machinfo->postresume();
++
+ dev_dbg(sharpsl_pm.dev, "SharpSL resuming...\n");
+
+ return 0;
diff --git a/packages/linux/linux-rp-2.6.23/tmio-fb-r6-fix-r0.patch b/packages/linux/linux-rp-2.6.23/tmio-fb-r6-fix-r0.patch
new file mode 100644
index 0000000000..523edec381
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/tmio-fb-r6-fix-r0.patch
@@ -0,0 +1,90 @@
+From 302745ce6f3bab7b1a97de32339405ae3fd8eacb Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 00:05:54 +0400
+Subject: [PATCH] tmio-fb-r6.patch fixes
+
+---
+ drivers/video/tmiofb.c | 8 ++++----
+ 1 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c
+index 10b0105..72eb76c 100644
+--- a/drivers/video/tmiofb.c
++++ b/drivers/video/tmiofb.c
+@@ -463,8 +463,8 @@ static int tmiofb_vblank (struct fb_info *fbi, struct fb_vblank *vblank)
+ #define FBIO_TMIO_ACC_WRITE 0x7C639300
+ #define FBIO_TMIO_ACC_SYNC 0x7C639301
+
+-static int tmiofb_ioctl (struct inode *inode, struct file *file,
+- unsigned int cmd, unsigned long arg, struct fb_info *fbi)
++static int tmiofb_ioctl (struct fb_info *fbi,
++ unsigned int cmd, unsigned long arg)
+ {
+ switch (cmd) {
+ case FBIOGET_VBLANK: {
+@@ -677,7 +677,7 @@ static struct fb_ops tmiofb_ops_acc = {
+ * 2000 0002 display start
+ * 2000 0004 line number match (0x1ff mask???)
+ */
+-static irqreturn_t tmiofb_irq (int irq, void *__fbi, struct pt_regs *r)
++static irqreturn_t tmiofb_irq (int irq, void *__fbi)
+ {
+ struct fb_info* fbi = __fbi;
+ struct tmiofb_par* par = fbi->par;
+@@ -762,7 +762,7 @@ static int __init tmiofb_probe (struct device *dev)
+ }
+ fbi->screen_base = par->sram;
+
+- retval = request_irq (irq->start, &tmiofb_irq, SA_INTERRUPT,
++ retval = request_irq (irq->start, &tmiofb_irq, IRQF_DISABLED,
+ TMIO_NAME_LCD, fbi);
+ if (retval)
+ goto err_request_irq;
+--
+1.4.4.4
+
+From 302745ce6f3bab7b1a97de32339405ae3fd8eacb Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 00:05:54 +0400
+Subject: [PATCH] tmio-fb-r6.patch fixes
+
+---
+ drivers/video/tmiofb.c | 8 ++++----
+ 1 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c
+index 10b0105..72eb76c 100644
+--- a/drivers/video/tmiofb.c
++++ b/drivers/video/tmiofb.c
+@@ -463,8 +463,8 @@ static int tmiofb_vblank (struct fb_info *fbi, struct fb_vblank *vblank)
+ #define FBIO_TMIO_ACC_WRITE 0x7C639300
+ #define FBIO_TMIO_ACC_SYNC 0x7C639301
+
+-static int tmiofb_ioctl (struct inode *inode, struct file *file,
+- unsigned int cmd, unsigned long arg, struct fb_info *fbi)
++static int tmiofb_ioctl (struct fb_info *fbi,
++ unsigned int cmd, unsigned long arg)
+ {
+ switch (cmd) {
+ case FBIOGET_VBLANK: {
+@@ -677,7 +677,7 @@ static struct fb_ops tmiofb_ops_acc = {
+ * 2000 0002 display start
+ * 2000 0004 line number match (0x1ff mask???)
+ */
+-static irqreturn_t tmiofb_irq (int irq, void *__fbi, struct pt_regs *r)
++static irqreturn_t tmiofb_irq (int irq, void *__fbi)
+ {
+ struct fb_info* fbi = __fbi;
+ struct tmiofb_par* par = fbi->par;
+@@ -762,7 +762,7 @@ static int __init tmiofb_probe (struct device *dev)
+ }
+ fbi->screen_base = par->sram;
+
+- retval = request_irq (irq->start, &tmiofb_irq, SA_INTERRUPT,
++ retval = request_irq (irq->start, &tmiofb_irq, IRQF_DISABLED,
+ TMIO_NAME_LCD, fbi);
+ if (retval)
+ goto err_request_irq;
+--
+1.4.4.4
+
diff --git a/packages/linux/linux-rp-2.6.23/tmio-nand-r8.patch b/packages/linux/linux-rp-2.6.23/tmio-nand-r8.patch
new file mode 100644
index 0000000000..3a30c2f34c
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/tmio-nand-r8.patch
@@ -0,0 +1,1188 @@
+ drivers/mtd/nand/Kconfig | 7 +
+ drivers/mtd/nand/Makefile | 1 +
+ drivers/mtd/nand/tmio.c | 554 +++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 562 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
+index f1d60b6..b9c8796 100644
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -69,6 +69,13 @@ config MTD_NAND_AMS_DELTA
+ help
+ Support for NAND flash on Amstrad E3 (Delta).
+
++config MTD_NAND_TMIO
++ tristate "NAND Flash device on Toshiba Mobile IO Controller"
++ depends on MTD_NAND && TOSHIBA_TC6393XB
++ help
++ Support for NAND flash connected to a Toshiba Mobile IO
++ Controller in some PDAs, including the Sharp SL6000x.
++
+ config MTD_NAND_TOTO
+ tristate "NAND Flash device on TOTO board"
+ depends on ARCH_OMAP && BROKEN
+diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
+index edba1db..64f24e1 100644
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -27,5 +27,6 @@ obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o
+ obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
+ obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
+ obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
++obj-$(CONFIG_MTD_NAND_TMIO) += tmio.o
+
+ nand-objs := nand_base.o nand_bbt.o
+diff --git a/drivers/mtd/nand/tmio.c b/drivers/mtd/nand/tmio.c
+new file mode 100644
+index 0000000..d196553
+--- /dev/null
++++ b/drivers/mtd/nand/tmio.c
+@@ -0,0 +1,554 @@
++/*
++ * A device driver for NAND flash connected to a Toshiba Mobile IO
++ * controller. This is known to work with the following variants:
++ * TC6393XB revision 3
++ *
++ * Maintainer: Chris Humbert <mahadri+mtd@drigon.com>
++ *
++ * Copyright (C) 2005 Chris Humbert
++ * Copyright (C) 2005 Dirk Opfer
++ * Copyright (C) 2004 SHARP
++ * Copyright (C) 2002 Lineo Japan, Inc.
++ * Copyright (C) Ian Molton and Sebastian Carlier
++ *
++ * Based on Sharp's NAND driver, sharp_sl_tc6393.c
++ *
++ * 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.
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/delay.h>
++#include <linux/wait.h>
++#include <linux/ioport.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++#include <asm/io.h>
++#include <asm/hardware/tmio.h>
++
++#include <linux/interrupt.h>
++
++#define mtd_printk(level, mtd, format, arg...) \
++ printk (level "%s: " format, mtd->name, ## arg)
++#define mtd_warn(mtd, format, arg...) \
++ mtd_printk (KERN_WARNING, mtd, format, ## arg)
++
++/*--------------------------------------------------------------------------*/
++
++/* tmio_nfcr.mode Register Command List */
++#define FCR_MODE_DATA 0x94 // Data Data_Mode
++#define FCR_MODE_COMMAND 0x95 // Data Command_Mode
++#define FCR_MODE_ADDRESS 0x96 // Data Address_Mode
++
++#define FCR_MODE_HWECC_CALC 0xB4 // HW-ECC Data
++#define FCR_MODE_HWECC_RESULT 0xD4 // HW-ECC Calculation Result Read_Mode
++#define FCR_MODE_HWECC_RESET 0xF4 // HW-ECC Reset
++
++#define FCR_MODE_POWER_ON 0x0C // Power Supply ON to SSFDC card
++#define FCR_MODE_POWER_OFF 0x08 // Power Supply OFF to SSFDC card
++
++#define FCR_MODE_LED_OFF 0x00 // LED OFF
++#define FCR_MODE_LED_ON 0x04 // LED ON
++
++#define FCR_MODE_EJECT_ON 0x68 // Ejection Demand from Penguin is Advanced
++#define FCR_MODE_EJECT_OFF 0x08 // Ejection Demand from Penguin is Not Advanced
++
++#define FCR_MODE_LOCK 0x6C // Operates By Lock_Mode. Ejection Switch is Invalid
++#define FCR_MODE_UNLOCK 0x0C // Operates By UnLock_Mode.Ejection Switch is Effective
++
++#define FCR_MODE_CONTROLLER_ID 0x40 // Controller ID Read
++#define FCR_MODE_STANDBY 0x00 // SSFDC card Changes Standby State
++
++#define FCR_MODE_WE 0x80
++#define FCR_MODE_ECC1 0x40
++#define FCR_MODE_ECC0 0x20
++#define FCR_MODE_CE 0x10
++#define FCR_MODE_PCNT1 0x08
++#define FCR_MODE_PCNT0 0x04
++#define FCR_MODE_ALE 0x02
++#define FCR_MODE_CLE 0x01
++
++#define FCR_STATUS_BUSY 0x80
++
++/*
++ * NAND Flash Host Controller Configuration Register
++ */
++struct tmio_nfhccr {
++ u8 x00[4];
++ u16 command; /* 0x04 Command */
++ u8 x01[0x0a];
++ u16 base[2]; /* 0x10 NAND Flash Control Reg Base Addr*/
++ u8 x02[0x29];
++ u8 intp; /* 0x3d Interrupt Pin */
++ u8 x03[0x0a];
++ u8 inte; /* 0x48 Interrupt Enable */
++ u8 x04;
++ u8 ec; /* 0x4a Event Control */
++ u8 x05;
++ u8 icc; /* 0x4c Internal Clock Control */
++ u8 x06[0x0e];
++ u8 eccc; /* 0x5b ECC Control */
++ u8 x07[4];
++ u8 nftc; /* 0x60 NAND Flash Transaction Control */
++ u8 nfm; /* 0x61 NAND Flash Monitor */
++ u8 nfpsc; /* 0x62 NAND Flash Power Supply Control */
++ u8 nfdc; /* 0x63 NAND Flash Detect Control */
++ u8 x08[0x9c];
++} __attribute__ ((packed));
++
++/*
++ * NAND Flash Control Register
++ */
++struct tmio_nfcr {
++union {
++ u8 u8; /* 0x00 Data Register */
++ u16 u16;
++ u32 u32;
++} __attribute__ ((packed));
++ u8 mode; /* 0x04 Mode Register */
++ u8 status; /* 0x05 Status Register */
++ u8 isr; /* 0x06 Interrupt Status Register */
++ u8 imr; /* 0x07 Interrupt Mask Register */
++} __attribute__ ((packed));
++
++struct tmio_nand {
++ struct mtd_info mtd;
++ struct nand_chip chip;
++
++ struct tmio_nfhccr __iomem * ccr;
++ struct tmio_nfcr __iomem * fcr;
++
++ unsigned int irq;
++
++ /* for tmio_nand_read_byte */
++ u8 read;
++ unsigned read_good:1;
++};
++
++#define mtd_to_tmio(m) container_of(m, struct tmio_nand, mtd)
++
++/*--------------------------------------------------------------------------*/
++
++static void tmio_nand_hwcontrol(struct mtd_info *mtd, int cmd,
++ unsigned int ctrl)
++{
++ struct tmio_nand *tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem *fcr = tmio->fcr;
++ struct nand_chip *chip = mtd->priv;
++
++ if (ctrl & NAND_CTRL_CHANGE) {
++ u8 mode;
++
++ if (ctrl & NAND_NCE) {
++ mode = FCR_MODE_DATA;
++
++ if (ctrl & NAND_CLE)
++ mode |= FCR_MODE_CLE;
++ else
++ mode &= ~FCR_MODE_CLE;
++
++ if (ctrl & NAND_ALE)
++ mode |= FCR_MODE_ALE;
++ else
++ mode &= ~FCR_MODE_ALE;
++ } else {
++ mode = FCR_MODE_STANDBY;
++ }
++
++ iowrite8 (mode, &fcr->mode);
++ tmio->read_good = 0;
++ }
++
++ if (cmd != NAND_CMD_NONE)
++ writeb(cmd, chip->IO_ADDR_W);
++}
++
++static int tmio_nand_dev_ready (struct mtd_info* mtd)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ return !(ioread8 (&fcr->status) & FCR_STATUS_BUSY);
++}
++
++static irqreturn_t tmio_irq (int irq, void *__tmio)
++{
++ struct tmio_nand* tmio = __tmio;
++ struct nand_chip* this = &tmio->chip;
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ /* disable RDYREQ interrupt */
++ iowrite8 (0x00, &fcr->imr);
++
++ if (unlikely (!waitqueue_active (&this->controller->wq)))
++ printk (KERN_WARNING TMIO_NAME_NAND ": spurious interrupt\n");
++
++ wake_up (&this->controller->wq);
++ return IRQ_HANDLED;
++}
++
++/*
++ * The TMIO core has a RDYREQ interrupt on the posedge of #SMRB.
++ * This interrupt is normally disabled, but for long operations like
++ * erase and write, we enable it to wake us up. The irq handler
++ * disables the interrupt.
++ */
++static int
++tmio_nand_wait (struct mtd_info *mtd, struct nand_chip *this)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++ long timeout;
++
++ /* enable RDYREQ interrupt */
++ iowrite8 (0x0f, &fcr->isr);
++ iowrite8 (0x81, &fcr->imr);
++
++ timeout = wait_event_timeout (this->controller->wq, tmio_nand_dev_ready (mtd),
++ msecs_to_jiffies (this->state == FL_ERASING ? 400 : 20));
++
++ if (unlikely (!tmio_nand_dev_ready (mtd))) {
++ iowrite8 (0x00, &fcr->imr);
++ mtd_warn (mtd, "still busy with %s after %d ms\n",
++ this->state == FL_ERASING ? "erase" : "program",
++ this->state == FL_ERASING ? 400 : 20);
++
++ } else if (unlikely (!timeout)) {
++ iowrite8 (0x00, &fcr->imr);
++ mtd_warn (mtd, "timeout waiting for interrupt\n");
++ }
++
++ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
++ return this->read_byte (mtd);
++}
++
++/*
++ * The TMIO controller combines two 8-bit data bytes into one 16-bit
++ * word. This function separates them so nand_base.c works as expected,
++ * especially its NAND_CMD_READID routines.
++ *
++ * To prevent stale data from being read, tmio_nand_hwcontrol() clears
++ * tmio->read_good.
++ */
++static u_char tmio_nand_read_byte (struct mtd_info *mtd)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++ unsigned int data;
++
++ if (tmio->read_good--)
++ return tmio->read;
++
++ data = ioread16 (&fcr->u16);
++ tmio->read = data >> 8;
++ return data;
++}
++
++/*
++ * The TMIO controller converts an 8-bit NAND interface to a 16-bit
++ * bus interface, so all data reads and writes must be 16-bit wide.
++ * Thus, we implement 16-bit versions of the read, write, and verify
++ * buffer functions.
++ */
++static void
++tmio_nand_write_buf (struct mtd_info *mtd, const u_char *buf, int len)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ iowrite16_rep (&fcr->u16, buf, len >> 1);
++}
++
++static void tmio_nand_read_buf (struct mtd_info *mtd, u_char *buf, int len)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ ioread16_rep (&fcr->u16, buf, len >> 1);
++}
++
++static int
++tmio_nand_verify_buf (struct mtd_info *mtd, const u_char *buf, int len)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++ u16* p = (u16*) buf;
++
++ for (len >>= 1; len; len--)
++ if (*(p++) != ioread16 (&fcr->u16))
++ return -EFAULT;
++ return 0;
++}
++
++static void tmio_nand_enable_hwecc (struct mtd_info* mtd, int mode)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ iowrite8 (FCR_MODE_HWECC_RESET, &fcr->mode);
++ ioread8 (&fcr->u8); /* dummy read */
++ iowrite8 (FCR_MODE_HWECC_CALC, &fcr->mode);
++}
++
++static int tmio_nand_calculate_ecc (struct mtd_info* mtd, const u_char* dat,
++ u_char* ecc_code)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++ unsigned int ecc;
++
++ iowrite8 (FCR_MODE_HWECC_RESULT, &fcr->mode);
++
++ ecc = ioread16 (&fcr->u16);
++ ecc_code[1] = ecc; // 000-255 LP7-0
++ ecc_code[0] = ecc >> 8; // 000-255 LP15-8
++ ecc = ioread16 (&fcr->u16);
++ ecc_code[2] = ecc; // 000-255 CP5-0,11b
++ ecc_code[4] = ecc >> 8; // 256-511 LP7-0
++ ecc = ioread16 (&fcr->u16);
++ ecc_code[3] = ecc; // 256-511 LP15-8
++ ecc_code[5] = ecc >> 8; // 256-511 CP5-0,11b
++
++ iowrite8 (FCR_MODE_DATA, &fcr->mode);
++ return 0;
++}
++
++static void tmio_hw_init (struct device *dev, struct tmio_nand *tmio)
++{
++ struct resource* nfcr = tmio_resource_control (dev);
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ struct tmio_nfhccr __iomem * ccr = tmio->ccr;
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++ unsigned long base;
++
++ /* (89h) SMD Buffer ON By TC6393XB SystemConfig gpibfc1 */
++ tdev->ops->clock (dev, 1);
++ tdev->ops->function (dev, 1);
++
++ /* (4Ch) CLKRUN Enable 1st spcrunc */
++ iowrite8 (0x81, &ccr->icc);
++
++ /* (10h)BaseAddress 0x1000 spba.spba2 */
++ base = nfcr->start - tdev->iomem->start;
++ iowrite16 (base, ccr->base + 0);
++ iowrite16 (base >> 16, ccr->base + 1);
++
++ /* (04h)Command Register I/O spcmd */
++ iowrite8 (0x02, &ccr->command);
++
++ /* (62h) Power Supply Control ssmpwc */
++ /* HardPowerOFF - SuspendOFF - PowerSupplyWait_4MS */
++ iowrite8 (0x02, &ccr->nfpsc);
++
++ /* (63h) Detect Control ssmdtc */
++ iowrite8 (0x02, &ccr->nfdc);
++
++ /* Interrupt status register clear sintst */
++ iowrite8 (0x0f, &fcr->isr);
++
++ /* After power supply, Media are reset smode */
++ iowrite8 (FCR_MODE_POWER_ON, &fcr->mode);
++ iowrite8 (FCR_MODE_COMMAND, &fcr->mode);
++ iowrite8 (NAND_CMD_RESET, &fcr->u8);
++
++ /* Standby Mode smode */
++ iowrite8 (FCR_MODE_STANDBY, &fcr->mode);
++
++ mdelay (5);
++}
++
++static void tmio_hw_stop (struct device *dev, struct tmio_nand *tmio)
++{
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ iowrite8 (FCR_MODE_POWER_OFF, &fcr->mode);
++ tdev->ops->function (dev, 0);
++ tdev->ops->clock (dev, 0);
++}
++
++/*--------------------------------------------------------------------------*/
++
++#ifdef CONFIG_MTD_PARTITIONS
++static const char *part_probes[] = { "cmdlinepart", NULL };
++#endif
++
++static int tmio_probe (struct device *dev)
++{
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ struct tmio_nand_platform_data* tnpd = dev->platform_data;
++ struct resource* ccr = tmio_resource_config (dev);
++ struct resource* fcr = tmio_resource_control (dev);
++ struct resource* irq = tmio_resource_irq (dev);
++ struct tmio_nand* tmio;
++ struct mtd_info* mtd;
++ struct nand_chip* this;
++ struct mtd_partition* parts;
++ int nbparts = 0;
++ int retval;
++
++ if (!tnpd)
++ return -EINVAL;
++
++ retval = request_resource (tdev->iomem, ccr);
++ if (retval)
++ goto err_request_ccr;
++
++ retval = request_resource (tdev->iomem, fcr);
++ if (retval)
++ goto err_request_fcr;
++
++ tmio = kzalloc (sizeof *tmio, GFP_KERNEL);
++ if (!tmio) {
++ retval = -ENOMEM;
++ goto err_kzalloc;
++ }
++
++ dev_set_drvdata (dev, tmio);
++ mtd = &tmio->mtd;
++ this = &tmio->chip;
++ mtd->priv = this;
++ mtd->name = TMIO_NAME_NAND;
++
++ tmio->ccr = ioremap (ccr->start, ccr->end - ccr->start + 1);
++ if (!tmio->ccr) {
++ retval = -EIO;
++ goto err_iomap_ccr;
++ }
++
++ tmio->fcr = ioremap (fcr->start, fcr->end - fcr->start + 1);
++ if (!tmio->fcr) {
++ retval = -EIO;
++ goto err_iomap_fcr;
++ }
++
++ tmio_hw_init (dev, tmio);
++
++ /* Set address of NAND IO lines */
++ this->IO_ADDR_R = tmio->fcr;
++ this->IO_ADDR_W = tmio->fcr;
++
++ /* Set address of hardware control function */
++ this->cmd_ctrl = tmio_nand_hwcontrol;
++ this->dev_ready = tmio_nand_dev_ready;
++ this->read_byte = tmio_nand_read_byte;
++ this->write_buf = tmio_nand_write_buf;
++ this->read_buf = tmio_nand_read_buf;
++ this->verify_buf = tmio_nand_verify_buf;
++
++ /* set eccmode using hardware ECC */
++ this->ecc.mode = NAND_ECC_HW;
++ this->ecc.size = 512;
++ this->ecc.bytes = 6;
++ this->ecc.hwctl = tmio_nand_enable_hwecc;
++ this->ecc.calculate = tmio_nand_calculate_ecc;
++ this->ecc.correct = nand_correct_data;
++ this->badblock_pattern = tnpd->badblock_pattern;
++
++ /* 15 us command delay time */
++ this->chip_delay = 15;
++
++ if (irq->start) {
++ retval = request_irq (irq->start, &tmio_irq,
++ IRQF_DISABLED, irq->name, tmio);
++ if (!retval) {
++ tmio->irq = irq->start;
++ this->waitfunc = tmio_nand_wait;
++ } else
++ mtd_warn (mtd, "request_irq error %d\n", retval);
++ }
++
++ /* Scan to find existence of the device */
++ if (nand_scan (mtd, 1)) {
++ retval = -ENODEV;
++ goto err_scan;
++ }
++
++ /* Register the partitions */
++#ifdef CONFIG_MTD_PARTITIONS
++ nbparts = parse_mtd_partitions (mtd, part_probes, &parts, 0);
++#endif
++ if (nbparts <= 0) {
++ parts = tnpd->partition;
++ nbparts = tnpd->num_partitions;
++ }
++
++ add_mtd_partitions (mtd, parts, nbparts);
++ return 0;
++
++err_scan:
++ if (tmio->irq)
++ free_irq (tmio->irq, tmio);
++ tmio_hw_stop (dev, tmio);
++ iounmap (tmio->fcr);
++err_iomap_fcr:
++ iounmap (tmio->ccr);
++err_iomap_ccr:
++ kfree (tmio);
++err_kzalloc:
++ release_resource (fcr);
++err_request_fcr:
++ release_resource (ccr);
++err_request_ccr:
++ return retval;
++}
++
++static int tmio_remove (struct device *dev)
++{
++ struct tmio_nand* tmio = dev_get_drvdata (dev);
++
++ nand_release (&tmio->mtd);
++ if (tmio->irq)
++ free_irq (tmio->irq, tmio);
++ tmio_hw_stop (dev, tmio);
++ iounmap (tmio->fcr);
++ iounmap (tmio->ccr);
++ kfree (tmio);
++ release_resource (tmio_resource_control (dev));
++ release_resource (tmio_resource_config (dev));
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int tmio_suspend (struct device *dev, pm_message_t state)
++{
++ tmio_hw_stop (dev, dev_get_drvdata (dev));
++ return 0;
++}
++
++static int tmio_resume (struct device *dev)
++{
++ tmio_hw_init (dev, dev_get_drvdata (dev));
++ return 0;
++}
++#endif
++
++static struct device_driver tmio_driver = {
++ .name = TMIO_NAME_NAND,
++ .bus = &tmio_bus_type,
++ .probe = tmio_probe,
++ .remove = tmio_remove,
++#ifdef CONFIG_PM
++ .suspend = tmio_suspend,
++ .resume = tmio_resume,
++#endif
++};
++
++static int __init tmio_init (void) {
++ return driver_register (&tmio_driver);
++}
++
++static void __exit tmio_exit (void) {
++ driver_unregister (&tmio_driver);
++}
++
++module_init (tmio_init);
++module_exit (tmio_exit);
++
++MODULE_LICENSE ("GPL");
++MODULE_AUTHOR ("Dirk Opfer, Chris Humbert");
++MODULE_DESCRIPTION ("NAND flash driver on Toshiba Mobile IO controller");
+ drivers/mtd/nand/Kconfig | 7 +
+ drivers/mtd/nand/Makefile | 1 +
+ drivers/mtd/nand/tmio.c | 554 +++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 562 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
+index f1d60b6..b9c8796 100644
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -69,6 +69,13 @@ config MTD_NAND_AMS_DELTA
+ help
+ Support for NAND flash on Amstrad E3 (Delta).
+
++config MTD_NAND_TMIO
++ tristate "NAND Flash device on Toshiba Mobile IO Controller"
++ depends on MTD_NAND && TOSHIBA_TC6393XB
++ help
++ Support for NAND flash connected to a Toshiba Mobile IO
++ Controller in some PDAs, including the Sharp SL6000x.
++
+ config MTD_NAND_TOTO
+ tristate "NAND Flash device on TOTO board"
+ depends on ARCH_OMAP && BROKEN
+diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
+index edba1db..64f24e1 100644
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -27,5 +27,6 @@ obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o
+ obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
+ obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
+ obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
++obj-$(CONFIG_MTD_NAND_TMIO) += tmio.o
+
+ nand-objs := nand_base.o nand_bbt.o
+diff --git a/drivers/mtd/nand/tmio.c b/drivers/mtd/nand/tmio.c
+new file mode 100644
+index 0000000..d196553
+--- /dev/null
++++ b/drivers/mtd/nand/tmio.c
+@@ -0,0 +1,554 @@
++/*
++ * A device driver for NAND flash connected to a Toshiba Mobile IO
++ * controller. This is known to work with the following variants:
++ * TC6393XB revision 3
++ *
++ * Maintainer: Chris Humbert <mahadri+mtd@drigon.com>
++ *
++ * Copyright (C) 2005 Chris Humbert
++ * Copyright (C) 2005 Dirk Opfer
++ * Copyright (C) 2004 SHARP
++ * Copyright (C) 2002 Lineo Japan, Inc.
++ * Copyright (C) Ian Molton and Sebastian Carlier
++ *
++ * Based on Sharp's NAND driver, sharp_sl_tc6393.c
++ *
++ * 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.
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/delay.h>
++#include <linux/wait.h>
++#include <linux/ioport.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++#include <asm/io.h>
++#include <asm/hardware/tmio.h>
++
++#include <linux/interrupt.h>
++
++#define mtd_printk(level, mtd, format, arg...) \
++ printk (level "%s: " format, mtd->name, ## arg)
++#define mtd_warn(mtd, format, arg...) \
++ mtd_printk (KERN_WARNING, mtd, format, ## arg)
++
++/*--------------------------------------------------------------------------*/
++
++/* tmio_nfcr.mode Register Command List */
++#define FCR_MODE_DATA 0x94 // Data Data_Mode
++#define FCR_MODE_COMMAND 0x95 // Data Command_Mode
++#define FCR_MODE_ADDRESS 0x96 // Data Address_Mode
++
++#define FCR_MODE_HWECC_CALC 0xB4 // HW-ECC Data
++#define FCR_MODE_HWECC_RESULT 0xD4 // HW-ECC Calculation Result Read_Mode
++#define FCR_MODE_HWECC_RESET 0xF4 // HW-ECC Reset
++
++#define FCR_MODE_POWER_ON 0x0C // Power Supply ON to SSFDC card
++#define FCR_MODE_POWER_OFF 0x08 // Power Supply OFF to SSFDC card
++
++#define FCR_MODE_LED_OFF 0x00 // LED OFF
++#define FCR_MODE_LED_ON 0x04 // LED ON
++
++#define FCR_MODE_EJECT_ON 0x68 // Ejection Demand from Penguin is Advanced
++#define FCR_MODE_EJECT_OFF 0x08 // Ejection Demand from Penguin is Not Advanced
++
++#define FCR_MODE_LOCK 0x6C // Operates By Lock_Mode. Ejection Switch is Invalid
++#define FCR_MODE_UNLOCK 0x0C // Operates By UnLock_Mode.Ejection Switch is Effective
++
++#define FCR_MODE_CONTROLLER_ID 0x40 // Controller ID Read
++#define FCR_MODE_STANDBY 0x00 // SSFDC card Changes Standby State
++
++#define FCR_MODE_WE 0x80
++#define FCR_MODE_ECC1 0x40
++#define FCR_MODE_ECC0 0x20
++#define FCR_MODE_CE 0x10
++#define FCR_MODE_PCNT1 0x08
++#define FCR_MODE_PCNT0 0x04
++#define FCR_MODE_ALE 0x02
++#define FCR_MODE_CLE 0x01
++
++#define FCR_STATUS_BUSY 0x80
++
++/*
++ * NAND Flash Host Controller Configuration Register
++ */
++struct tmio_nfhccr {
++ u8 x00[4];
++ u16 command; /* 0x04 Command */
++ u8 x01[0x0a];
++ u16 base[2]; /* 0x10 NAND Flash Control Reg Base Addr*/
++ u8 x02[0x29];
++ u8 intp; /* 0x3d Interrupt Pin */
++ u8 x03[0x0a];
++ u8 inte; /* 0x48 Interrupt Enable */
++ u8 x04;
++ u8 ec; /* 0x4a Event Control */
++ u8 x05;
++ u8 icc; /* 0x4c Internal Clock Control */
++ u8 x06[0x0e];
++ u8 eccc; /* 0x5b ECC Control */
++ u8 x07[4];
++ u8 nftc; /* 0x60 NAND Flash Transaction Control */
++ u8 nfm; /* 0x61 NAND Flash Monitor */
++ u8 nfpsc; /* 0x62 NAND Flash Power Supply Control */
++ u8 nfdc; /* 0x63 NAND Flash Detect Control */
++ u8 x08[0x9c];
++} __attribute__ ((packed));
++
++/*
++ * NAND Flash Control Register
++ */
++struct tmio_nfcr {
++union {
++ u8 u8; /* 0x00 Data Register */
++ u16 u16;
++ u32 u32;
++} __attribute__ ((packed));
++ u8 mode; /* 0x04 Mode Register */
++ u8 status; /* 0x05 Status Register */
++ u8 isr; /* 0x06 Interrupt Status Register */
++ u8 imr; /* 0x07 Interrupt Mask Register */
++} __attribute__ ((packed));
++
++struct tmio_nand {
++ struct mtd_info mtd;
++ struct nand_chip chip;
++
++ struct tmio_nfhccr __iomem * ccr;
++ struct tmio_nfcr __iomem * fcr;
++
++ unsigned int irq;
++
++ /* for tmio_nand_read_byte */
++ u8 read;
++ unsigned read_good:1;
++};
++
++#define mtd_to_tmio(m) container_of(m, struct tmio_nand, mtd)
++
++/*--------------------------------------------------------------------------*/
++
++static void tmio_nand_hwcontrol(struct mtd_info *mtd, int cmd,
++ unsigned int ctrl)
++{
++ struct tmio_nand *tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem *fcr = tmio->fcr;
++ struct nand_chip *chip = mtd->priv;
++
++ if (ctrl & NAND_CTRL_CHANGE) {
++ u8 mode;
++
++ if (ctrl & NAND_NCE) {
++ mode = FCR_MODE_DATA;
++
++ if (ctrl & NAND_CLE)
++ mode |= FCR_MODE_CLE;
++ else
++ mode &= ~FCR_MODE_CLE;
++
++ if (ctrl & NAND_ALE)
++ mode |= FCR_MODE_ALE;
++ else
++ mode &= ~FCR_MODE_ALE;
++ } else {
++ mode = FCR_MODE_STANDBY;
++ }
++
++ iowrite8 (mode, &fcr->mode);
++ tmio->read_good = 0;
++ }
++
++ if (cmd != NAND_CMD_NONE)
++ writeb(cmd, chip->IO_ADDR_W);
++}
++
++static int tmio_nand_dev_ready (struct mtd_info* mtd)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ return !(ioread8 (&fcr->status) & FCR_STATUS_BUSY);
++}
++
++static irqreturn_t tmio_irq (int irq, void *__tmio)
++{
++ struct tmio_nand* tmio = __tmio;
++ struct nand_chip* this = &tmio->chip;
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ /* disable RDYREQ interrupt */
++ iowrite8 (0x00, &fcr->imr);
++
++ if (unlikely (!waitqueue_active (&this->controller->wq)))
++ printk (KERN_WARNING TMIO_NAME_NAND ": spurious interrupt\n");
++
++ wake_up (&this->controller->wq);
++ return IRQ_HANDLED;
++}
++
++/*
++ * The TMIO core has a RDYREQ interrupt on the posedge of #SMRB.
++ * This interrupt is normally disabled, but for long operations like
++ * erase and write, we enable it to wake us up. The irq handler
++ * disables the interrupt.
++ */
++static int
++tmio_nand_wait (struct mtd_info *mtd, struct nand_chip *this)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++ long timeout;
++
++ /* enable RDYREQ interrupt */
++ iowrite8 (0x0f, &fcr->isr);
++ iowrite8 (0x81, &fcr->imr);
++
++ timeout = wait_event_timeout (this->controller->wq, tmio_nand_dev_ready (mtd),
++ msecs_to_jiffies (this->state == FL_ERASING ? 400 : 20));
++
++ if (unlikely (!tmio_nand_dev_ready (mtd))) {
++ iowrite8 (0x00, &fcr->imr);
++ mtd_warn (mtd, "still busy with %s after %d ms\n",
++ this->state == FL_ERASING ? "erase" : "program",
++ this->state == FL_ERASING ? 400 : 20);
++
++ } else if (unlikely (!timeout)) {
++ iowrite8 (0x00, &fcr->imr);
++ mtd_warn (mtd, "timeout waiting for interrupt\n");
++ }
++
++ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
++ return this->read_byte (mtd);
++}
++
++/*
++ * The TMIO controller combines two 8-bit data bytes into one 16-bit
++ * word. This function separates them so nand_base.c works as expected,
++ * especially its NAND_CMD_READID routines.
++ *
++ * To prevent stale data from being read, tmio_nand_hwcontrol() clears
++ * tmio->read_good.
++ */
++static u_char tmio_nand_read_byte (struct mtd_info *mtd)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++ unsigned int data;
++
++ if (tmio->read_good--)
++ return tmio->read;
++
++ data = ioread16 (&fcr->u16);
++ tmio->read = data >> 8;
++ return data;
++}
++
++/*
++ * The TMIO controller converts an 8-bit NAND interface to a 16-bit
++ * bus interface, so all data reads and writes must be 16-bit wide.
++ * Thus, we implement 16-bit versions of the read, write, and verify
++ * buffer functions.
++ */
++static void
++tmio_nand_write_buf (struct mtd_info *mtd, const u_char *buf, int len)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ iowrite16_rep (&fcr->u16, buf, len >> 1);
++}
++
++static void tmio_nand_read_buf (struct mtd_info *mtd, u_char *buf, int len)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ ioread16_rep (&fcr->u16, buf, len >> 1);
++}
++
++static int
++tmio_nand_verify_buf (struct mtd_info *mtd, const u_char *buf, int len)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++ u16* p = (u16*) buf;
++
++ for (len >>= 1; len; len--)
++ if (*(p++) != ioread16 (&fcr->u16))
++ return -EFAULT;
++ return 0;
++}
++
++static void tmio_nand_enable_hwecc (struct mtd_info* mtd, int mode)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ iowrite8 (FCR_MODE_HWECC_RESET, &fcr->mode);
++ ioread8 (&fcr->u8); /* dummy read */
++ iowrite8 (FCR_MODE_HWECC_CALC, &fcr->mode);
++}
++
++static int tmio_nand_calculate_ecc (struct mtd_info* mtd, const u_char* dat,
++ u_char* ecc_code)
++{
++ struct tmio_nand* tmio = mtd_to_tmio (mtd);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++ unsigned int ecc;
++
++ iowrite8 (FCR_MODE_HWECC_RESULT, &fcr->mode);
++
++ ecc = ioread16 (&fcr->u16);
++ ecc_code[1] = ecc; // 000-255 LP7-0
++ ecc_code[0] = ecc >> 8; // 000-255 LP15-8
++ ecc = ioread16 (&fcr->u16);
++ ecc_code[2] = ecc; // 000-255 CP5-0,11b
++ ecc_code[4] = ecc >> 8; // 256-511 LP7-0
++ ecc = ioread16 (&fcr->u16);
++ ecc_code[3] = ecc; // 256-511 LP15-8
++ ecc_code[5] = ecc >> 8; // 256-511 CP5-0,11b
++
++ iowrite8 (FCR_MODE_DATA, &fcr->mode);
++ return 0;
++}
++
++static void tmio_hw_init (struct device *dev, struct tmio_nand *tmio)
++{
++ struct resource* nfcr = tmio_resource_control (dev);
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ struct tmio_nfhccr __iomem * ccr = tmio->ccr;
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++ unsigned long base;
++
++ /* (89h) SMD Buffer ON By TC6393XB SystemConfig gpibfc1 */
++ tdev->ops->clock (dev, 1);
++ tdev->ops->function (dev, 1);
++
++ /* (4Ch) CLKRUN Enable 1st spcrunc */
++ iowrite8 (0x81, &ccr->icc);
++
++ /* (10h)BaseAddress 0x1000 spba.spba2 */
++ base = nfcr->start - tdev->iomem->start;
++ iowrite16 (base, ccr->base + 0);
++ iowrite16 (base >> 16, ccr->base + 1);
++
++ /* (04h)Command Register I/O spcmd */
++ iowrite8 (0x02, &ccr->command);
++
++ /* (62h) Power Supply Control ssmpwc */
++ /* HardPowerOFF - SuspendOFF - PowerSupplyWait_4MS */
++ iowrite8 (0x02, &ccr->nfpsc);
++
++ /* (63h) Detect Control ssmdtc */
++ iowrite8 (0x02, &ccr->nfdc);
++
++ /* Interrupt status register clear sintst */
++ iowrite8 (0x0f, &fcr->isr);
++
++ /* After power supply, Media are reset smode */
++ iowrite8 (FCR_MODE_POWER_ON, &fcr->mode);
++ iowrite8 (FCR_MODE_COMMAND, &fcr->mode);
++ iowrite8 (NAND_CMD_RESET, &fcr->u8);
++
++ /* Standby Mode smode */
++ iowrite8 (FCR_MODE_STANDBY, &fcr->mode);
++
++ mdelay (5);
++}
++
++static void tmio_hw_stop (struct device *dev, struct tmio_nand *tmio)
++{
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ struct tmio_nfcr __iomem * fcr = tmio->fcr;
++
++ iowrite8 (FCR_MODE_POWER_OFF, &fcr->mode);
++ tdev->ops->function (dev, 0);
++ tdev->ops->clock (dev, 0);
++}
++
++/*--------------------------------------------------------------------------*/
++
++#ifdef CONFIG_MTD_PARTITIONS
++static const char *part_probes[] = { "cmdlinepart", NULL };
++#endif
++
++static int tmio_probe (struct device *dev)
++{
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ struct tmio_nand_platform_data* tnpd = dev->platform_data;
++ struct resource* ccr = tmio_resource_config (dev);
++ struct resource* fcr = tmio_resource_control (dev);
++ struct resource* irq = tmio_resource_irq (dev);
++ struct tmio_nand* tmio;
++ struct mtd_info* mtd;
++ struct nand_chip* this;
++ struct mtd_partition* parts;
++ int nbparts = 0;
++ int retval;
++
++ if (!tnpd)
++ return -EINVAL;
++
++ retval = request_resource (tdev->iomem, ccr);
++ if (retval)
++ goto err_request_ccr;
++
++ retval = request_resource (tdev->iomem, fcr);
++ if (retval)
++ goto err_request_fcr;
++
++ tmio = kzalloc (sizeof *tmio, GFP_KERNEL);
++ if (!tmio) {
++ retval = -ENOMEM;
++ goto err_kzalloc;
++ }
++
++ dev_set_drvdata (dev, tmio);
++ mtd = &tmio->mtd;
++ this = &tmio->chip;
++ mtd->priv = this;
++ mtd->name = TMIO_NAME_NAND;
++
++ tmio->ccr = ioremap (ccr->start, ccr->end - ccr->start + 1);
++ if (!tmio->ccr) {
++ retval = -EIO;
++ goto err_iomap_ccr;
++ }
++
++ tmio->fcr = ioremap (fcr->start, fcr->end - fcr->start + 1);
++ if (!tmio->fcr) {
++ retval = -EIO;
++ goto err_iomap_fcr;
++ }
++
++ tmio_hw_init (dev, tmio);
++
++ /* Set address of NAND IO lines */
++ this->IO_ADDR_R = tmio->fcr;
++ this->IO_ADDR_W = tmio->fcr;
++
++ /* Set address of hardware control function */
++ this->cmd_ctrl = tmio_nand_hwcontrol;
++ this->dev_ready = tmio_nand_dev_ready;
++ this->read_byte = tmio_nand_read_byte;
++ this->write_buf = tmio_nand_write_buf;
++ this->read_buf = tmio_nand_read_buf;
++ this->verify_buf = tmio_nand_verify_buf;
++
++ /* set eccmode using hardware ECC */
++ this->ecc.mode = NAND_ECC_HW;
++ this->ecc.size = 512;
++ this->ecc.bytes = 6;
++ this->ecc.hwctl = tmio_nand_enable_hwecc;
++ this->ecc.calculate = tmio_nand_calculate_ecc;
++ this->ecc.correct = nand_correct_data;
++ this->badblock_pattern = tnpd->badblock_pattern;
++
++ /* 15 us command delay time */
++ this->chip_delay = 15;
++
++ if (irq->start) {
++ retval = request_irq (irq->start, &tmio_irq,
++ IRQF_DISABLED, irq->name, tmio);
++ if (!retval) {
++ tmio->irq = irq->start;
++ this->waitfunc = tmio_nand_wait;
++ } else
++ mtd_warn (mtd, "request_irq error %d\n", retval);
++ }
++
++ /* Scan to find existence of the device */
++ if (nand_scan (mtd, 1)) {
++ retval = -ENODEV;
++ goto err_scan;
++ }
++
++ /* Register the partitions */
++#ifdef CONFIG_MTD_PARTITIONS
++ nbparts = parse_mtd_partitions (mtd, part_probes, &parts, 0);
++#endif
++ if (nbparts <= 0) {
++ parts = tnpd->partition;
++ nbparts = tnpd->num_partitions;
++ }
++
++ add_mtd_partitions (mtd, parts, nbparts);
++ return 0;
++
++err_scan:
++ if (tmio->irq)
++ free_irq (tmio->irq, tmio);
++ tmio_hw_stop (dev, tmio);
++ iounmap (tmio->fcr);
++err_iomap_fcr:
++ iounmap (tmio->ccr);
++err_iomap_ccr:
++ kfree (tmio);
++err_kzalloc:
++ release_resource (fcr);
++err_request_fcr:
++ release_resource (ccr);
++err_request_ccr:
++ return retval;
++}
++
++static int tmio_remove (struct device *dev)
++{
++ struct tmio_nand* tmio = dev_get_drvdata (dev);
++
++ nand_release (&tmio->mtd);
++ if (tmio->irq)
++ free_irq (tmio->irq, tmio);
++ tmio_hw_stop (dev, tmio);
++ iounmap (tmio->fcr);
++ iounmap (tmio->ccr);
++ kfree (tmio);
++ release_resource (tmio_resource_control (dev));
++ release_resource (tmio_resource_config (dev));
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int tmio_suspend (struct device *dev, pm_message_t state)
++{
++ tmio_hw_stop (dev, dev_get_drvdata (dev));
++ return 0;
++}
++
++static int tmio_resume (struct device *dev)
++{
++ tmio_hw_init (dev, dev_get_drvdata (dev));
++ return 0;
++}
++#endif
++
++static struct device_driver tmio_driver = {
++ .name = TMIO_NAME_NAND,
++ .bus = &tmio_bus_type,
++ .probe = tmio_probe,
++ .remove = tmio_remove,
++#ifdef CONFIG_PM
++ .suspend = tmio_suspend,
++ .resume = tmio_resume,
++#endif
++};
++
++static int __init tmio_init (void) {
++ return driver_register (&tmio_driver);
++}
++
++static void __exit tmio_exit (void) {
++ driver_unregister (&tmio_driver);
++}
++
++module_init (tmio_init);
++module_exit (tmio_exit);
++
++MODULE_LICENSE ("GPL");
++MODULE_AUTHOR ("Dirk Opfer, Chris Humbert");
++MODULE_DESCRIPTION ("NAND flash driver on Toshiba Mobile IO controller");
diff --git a/packages/linux/linux-rp-2.6.23/tmio-tc6393-r8.patch b/packages/linux/linux-rp-2.6.23/tmio-tc6393-r8.patch
new file mode 100644
index 0000000000..500fa837ec
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/tmio-tc6393-r8.patch
@@ -0,0 +1,1600 @@
+ arch/arm/common/Kconfig | 3
+ arch/arm/common/Makefile | 1
+ arch/arm/common/tc6393xb.c | 668 ++++++++++++++++++++++++++++++++++++++++
+ arch/arm/mach-pxa/Kconfig | 1
+ include/asm-arm/arch-pxa/irqs.h | 10
+ include/asm-arm/hardware/tmio.h | 44 ++
+ 6 files changed, 727 insertions(+)
+
+Index: git/arch/arm/common/tc6393xb.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ git/arch/arm/common/tc6393xb.c 2006-11-07 22:14:49.000000000 +0000
+@@ -0,0 +1,668 @@
++/*
++ * Toshiba TC6393XB SoC support
++ *
++ * Maintainer: Chris Humbert <mahadri-kernel@drigon.com>
++ *
++ * Copyright (c) 2005-2006 Chris Humbert
++ * Copyright (c) 2005 Dirk Opfer
++ *
++ * Based on code written by Sharp/Lineo for 2.4 kernels
++ * Based on locomo.c
++ *
++ * 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.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/errno.h>
++#include <linux/ioport.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/fb.h>
++
++#include <asm/hardware.h>
++#include <asm/mach-types.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/mach/irq.h>
++#include <asm/arch/irqs.h>
++#include <asm/hardware/tmio.h>
++
++#ifndef TMIO_SOC_TC6393XB
++#error "TC6393XB SoC not configured"
++#endif
++
++/*--------------------------------------------------------------------------*/
++
++/* cell ids must be 0-based because they are used as array indexes. */
++#define TC6393_CELL_NAND 0
++#define TC6393_CELL_SD 1
++#define TC6393_CELL_OHCI 2
++#define TC6393_CELL_SERIAL 3
++#define TC6393_CELL_LCD 4
++#define TC6393_NUM_CELLS 5
++
++#define TC6393_RESOURCE(_name, _start, _end, _flags) \
++ { \
++ .name = _name, \
++ .start = _start, \
++ .end = _end, \
++ .flags = _flags, \
++ }
++
++#define TC6393_MEM(name, start, size) \
++ TC6393_RESOURCE(name, start, (start) + (size) - 1, IORESOURCE_MEM)
++
++#define TC6393_IRQ(name, irq) \
++ TC6393_RESOURCE(name, irq, irq, IORESOURCE_IRQ)
++
++const static struct resource tc6393_NAND_resource[] = {
++ TC6393_MEM (TMIO_NAME_NAND, 0x000100, 0x100),
++ TC6393_MEM (TMIO_NAME_NAND, 0x001000, 0x008),
++ TC6393_MEM (TMIO_NAME_NAND, 0, 0),
++ TC6393_IRQ (TMIO_NAME_NAND, IRQ_TC6393_NAND),
++};
++
++const static struct resource tc6393_SD_resource[] = {
++ TC6393_MEM (TMIO_NAME_SD, 0x000200, 0x100),
++ TC6393_MEM (TMIO_NAME_SD, 0x002000, 0x200),
++ TC6393_MEM (TMIO_NAME_SD, 0, 0),
++ TC6393_IRQ (TMIO_NAME_SD, IRQ_TC6393_SD),
++};
++
++const static struct resource tc6393_OHCI_resource[] = {
++ TC6393_MEM (TMIO_NAME_OHCI, 0x000300, 0x100),
++ TC6393_MEM (TMIO_NAME_OHCI, 0x003000, 0x100),
++ TC6393_MEM (TMIO_NAME_OHCI, 0x010000, 32 * 1024),
++ TC6393_IRQ (TMIO_NAME_OHCI, IRQ_TC6393_OHCI),
++};
++
++const static struct resource tc6393_SERIAL_resource[] = {
++ TC6393_MEM (TMIO_NAME_SERIAL, 0x000400, 0x100),
++ TC6393_MEM (TMIO_NAME_SERIAL, 0x004000, 0x100),
++ TC6393_MEM (TMIO_NAME_SERIAL, 0, 0),
++ TC6393_IRQ (TMIO_NAME_SERIAL, IRQ_TC6393_SERIAL),
++};
++
++const static struct resource tc6393_LCD_resource[] = {
++ TC6393_MEM (TMIO_NAME_LCD, 0x000500, 0x100),
++ TC6393_MEM (TMIO_NAME_LCD, 0x005000, 0x200),
++ TC6393_MEM (TMIO_NAME_LCD, 0x100000, 1024 * 1024),
++ TC6393_IRQ (TMIO_NAME_LCD, IRQ_TC6393_LCD),
++};
++
++#define TC6393_CELL(_NAME) \
++ [TC6393_CELL_##_NAME] = { \
++ .name = TMIO_NAME_##_NAME, \
++ .id = TC6393_CELL_##_NAME, \
++ .resource = tc6393_##_NAME##_resource, \
++ .num_resources = ARRAY_SIZE (tc6393_##_NAME##_resource), \
++ }
++
++struct tc6393_cell {
++ const char* name;
++ unsigned int id;
++ const struct resource* resource;
++ unsigned int num_resources;
++};
++
++const static struct tc6393_cell tc6393_cell [TC6393_NUM_CELLS] = {
++ TC6393_CELL (NAND ),
++ TC6393_CELL (SD ),
++ TC6393_CELL (OHCI ),
++ TC6393_CELL (SERIAL ),
++ TC6393_CELL (LCD ),
++};
++
++/*--------------------------------------------------------------------------*/
++
++/*
++ * TC6393 System Configuration Register
++ */
++struct tc6393_scr {
++ u8 x00[8];
++ u8 revid; /* 0x08 Revision ID */
++ u8 x01[0x47];
++ u8 isr; /* 0x50 Interrupt Status */
++ u8 x02;
++ u8 imr; /* 0x52 Interrupt Mask */
++ u8 x03;
++ u8 irr; /* 0x54 Interrupt Routing */
++ u8 x04[0x0b];
++ u16 gper; /* 0x60 GP Enable */
++ u8 x05[2];
++ u16 gpi_sr[2]; /* 0x64 GPI Status */
++ u16 gpi_imr[2]; /* 0x68 GPI INT Mask */
++ u16 gpi_eder[2]; /* 0x6c GPI Edge Detect Enable */
++ u16 gpi_lir[4]; /* 0x70 GPI Level Invert */
++ u16 gpo_dsr[2]; /* 0x78 GPO Data Set */
++ u16 gpo_doecr[2]; /* 0x7c GPO Data OE Control */
++ u16 gp_iarcr[2]; /* 0x80 GP Internal Active Reg Control */
++ u16 gp_iarlcr[2]; /* 0x84 GP Internal Active Reg Level Con*/
++ u8 gpi_bcr[4]; /* 0x88 GPI Buffer Control */
++ u16 gpa_iarcr; /* 0x8c GPa Internal Active Reg Control */
++ u8 x06[2];
++ u16 gpa_iarlcr; /* 0x90 GPa Internal Active Reg Level Co*/
++ u8 x07[2];
++ u16 gpa_bcr; /* 0x94 GPa Buffer Control */
++ u8 x08[2];
++ u16 ccr; /* 0x98 Clock Control */
++ u16 pll2cr; /* 0x9a PLL2 Control */
++ u16 pll1cr[2]; /* 0x9c PLL1 Control */
++ u8 diarcr; /* 0xa0 Device Internal Active Reg Contr*/
++ u8 dbocr; /* 0xa1 Device Buffer Off Control */
++ u8 x09[0x3e];
++ u8 fer; /* 0xe0 Function Enable */
++ u8 x10[3];
++ u16 mcr; /* 0xe4 Mode Control */
++ u8 x11[0x14];
++ u8 config; /* 0xfc Configuration Control */
++ u8 x12[2];
++ u8 debug; /* 0xff Debug */
++} __attribute__ ((packed));
++
++union tc6393_scr_fer {
++ u8 raw;
++struct {
++ unsigned usben:1; /* D0 USB enable */
++ unsigned lcdcven:1; /* D1 polysylicon TFT enable */
++ unsigned slcden:1; /* D2 SLCD enable */
++} __attribute__ ((packed));
++} __attribute__ ((packed));
++
++union tc6393_scr_ccr {
++ u16 raw;
++struct {
++ unsigned ck32ken:1; /* D0 SD host clock enable */
++ unsigned usbcken:1; /* D1 USB host clock enable */
++ unsigned x00:2;
++ unsigned sharp:1; /* D4 ??? set in Sharp's code */
++ unsigned x01:3;
++ enum { disable = 0,
++ m12MHz = 1,
++ m24MHz = 2,
++ m48MHz = 3,
++ } mclksel:3; /* D10-D8 LCD controller clock */
++ unsigned x02:1;
++ enum { h24MHz = 0,
++ h48MHz = 1,
++ } hclksel:2; /* D13-D12 host bus clock */
++ unsigned x03:2;
++} __attribute__ ((packed));
++} __attribute__ ((packed));
++
++/*--------------------------------------------------------------------------*/
++
++struct tc6393 {
++ spinlock_t lock; /* read-modify-write lock */
++ struct device* dev; /* TC6393 device */
++ struct tc6393_scr __iomem *scr; /* system configuration reg */
++
++ struct resource rscr; /* system config reg resource */
++ struct resource* iomem; /* entire TC6393 iomem resource */
++ unsigned int irq; /* hardware cascade irq */
++
++ struct tmio_device tdev [TC6393_NUM_CELLS];
++};
++
++/*--------------------------------------------------------------------------*/
++
++static u32 tc6393_ioread32 (const void __iomem *addr)
++{
++ return ((u32) ioread16 (addr)) | (((u32) ioread16 (addr + 2)) << 16);
++}
++
++static u32 tc6393_iowrite32 (u32 val, const void __iomem *addr)
++{
++ iowrite16 (val, addr);
++ iowrite16 (val >> 16, addr + 2);
++ return val;
++}
++
++u32 get_tc6393_gpio (struct device *dev)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++
++ return tc6393_ioread32 (scr->gpo_dsr);
++}
++EXPORT_SYMBOL (get_tc6393_gpio);
++
++u32 set_tc6393_gpio (struct device *dev, u32 bits)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ unsigned long flags;
++ u32 dsr;
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ dsr = tc6393_ioread32 (scr->gpo_dsr) | bits;
++ tc6393_iowrite32 (dsr, scr->gpo_dsr);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++
++ return dsr;
++}
++EXPORT_SYMBOL (set_tc6393_gpio);
++
++u32 reset_tc6393_gpio (struct device *dev, u32 bits)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ unsigned long flags;
++ u32 dsr;
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ dsr = tc6393_ioread32 (scr->gpo_dsr) & ~bits;
++ tc6393_iowrite32 (dsr, scr->gpo_dsr);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++
++ return dsr;
++}
++EXPORT_SYMBOL (reset_tc6393_gpio);
++
++/*--------------------------------------------------------------------------*/
++
++static void
++tc6393_irq (unsigned int irq, struct irq_desc *desc)
++{
++ struct tc6393* tc6393 = get_irq_chip_data (irq);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ unsigned int isr;
++ unsigned int bit;
++ unsigned int i;
++
++ desc->chip->ack (irq);
++
++ while ((isr = ioread8(&scr->isr) & ~ioread8(&scr->imr)))
++ for (bit = 1, i = IRQ_TC6393_START; i <= IRQ_TC6393_LCD;
++ bit <<= 1, i++)
++ if (isr & bit)
++ desc_handle_irq (i, irq_desc + i);
++}
++
++static void tc6393_irq_ack (unsigned int irq)
++{
++}
++
++static void tc6393_irq_mask (unsigned int irq)
++{
++ struct tc6393* tc6393 = get_irq_chip_data (irq);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ unsigned long flags;
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ iowrite8 (ioread8 (&scr->imr) | (1 << (irq - IRQ_TC6393_START)),
++ &scr->imr);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++}
++
++static void tc6393_irq_unmask (unsigned int irq)
++{
++ struct tc6393* tc6393 = get_irq_chip_data (irq);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ unsigned long flags;
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ iowrite8 (ioread8 (&scr->imr) & ~(1 << (irq - IRQ_TC6393_START)),
++ &scr->imr);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++}
++
++static struct irq_chip tc6393_chip = {
++ .ack = tc6393_irq_ack,
++ .mask = tc6393_irq_mask,
++ .unmask = tc6393_irq_unmask,
++};
++
++static void tc6393_attach_irq (struct tc6393 *tc6393)
++{
++ unsigned int irq;
++
++ for (irq = IRQ_TC6393_START; irq <= IRQ_TC6393_LCD; irq++) {
++ set_irq_chip (irq, &tc6393_chip);
++ set_irq_chip_data(irq, tc6393);
++ set_irq_handler (irq, handle_edge_irq);
++ set_irq_flags (irq, IRQF_VALID | IRQF_PROBE);
++ }
++
++ set_irq_type (tc6393->irq, IRQT_FALLING);
++ set_irq_chip_data (tc6393->irq, tc6393);
++ set_irq_chained_handler (tc6393->irq, tc6393_irq);
++}
++
++static void tc6393_detach_irq (struct tc6393 *tc6393)
++{
++ unsigned int irq;
++
++ set_irq_chained_handler (tc6393->irq, NULL);
++ set_irq_chip_data (tc6393->irq, NULL);
++
++ for (irq = IRQ_TC6393_START; irq <= IRQ_TC6393_LCD; irq++) {
++ set_irq_flags (irq, 0);
++ set_irq_chip (irq, NULL);
++ set_irq_chip_data(irq, NULL);
++ }
++}
++
++/*--------------------------------------------------------------------------*/
++
++static int tc6393_bus_match (struct device *dev, struct device_driver *drv)
++{
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ const struct tc6393_cell* cell = tdev->soc_data;
++
++ return !strcmp (cell->name, drv->name);
++}
++
++static int tc6393_bus_suspend (struct device *dev, pm_message_t state)
++{
++ struct device_driver* drv = dev->driver;
++ return drv && drv->suspend ? drv->suspend (dev, state) : 0;
++}
++
++static int tc6393_bus_resume (struct device *dev)
++{
++ struct device_driver* drv = dev->driver;
++ return drv && drv->resume ? drv->resume (dev) : 0;
++}
++
++struct bus_type tc6393_bus_type = {
++ .name = TMIO_NAME_BUS,
++ .match = tc6393_bus_match,
++ .suspend = tc6393_bus_suspend,
++ .resume = tc6393_bus_resume,
++};
++EXPORT_SYMBOL (tc6393_bus_type);
++
++/*--------------------------------------------------------------------------*/
++
++static void tc6393_cell_clock (struct device *dev, int enable)
++{
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ const struct tc6393_cell* cell = tdev->soc_data;
++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ union tc6393_scr_ccr ccr;
++ unsigned long flags;
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ ccr.raw = ioread16 (&scr->ccr);
++
++ switch (cell->id) {
++ case TC6393_CELL_SD: ccr.ck32ken = enable; break;
++ case TC6393_CELL_OHCI: ccr.usbcken = enable; break;
++ case TC6393_CELL_LCD:
++ ccr.mclksel = enable ? m48MHz : disable;
++ break;
++ }
++
++ printk (KERN_DEBUG TMIO_NAME_CORE ": scr->ccr = %04x\n", ccr.raw);
++
++ iowrite16(ccr.raw, &scr->ccr);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++}
++
++static void tc6393_cell_function (struct device *dev, int enable)
++{
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ const struct tc6393_cell* cell = tdev->soc_data;
++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ union tc6393_scr_fer fer;
++ unsigned long flags;
++
++ if (cell->id == TC6393_CELL_NAND) {
++ if (enable) {
++ /* SMD buffer on */
++ printk (KERN_DEBUG TMIO_NAME_CORE ": SMD buffer on\n");
++ iowrite8 (0xff, scr->gpi_bcr + 1);
++ }
++ return;
++ }
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ fer.raw = ioread16 (&scr->fer);
++
++ switch (cell->id) {
++ case TC6393_CELL_OHCI: fer.usben = enable; break;
++ case TC6393_CELL_LCD: fer.slcden = enable; break;
++ }
++
++ printk (KERN_DEBUG TMIO_NAME_CORE ": scr->fer = %02x\n", fer.raw);
++
++ iowrite8 (fer.raw, &scr->fer);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++}
++
++static void
++tc6393_lcd_mode (struct device *dev, const struct fb_videomode *mode)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++
++ iowrite16 (mode->pixclock, scr->pll1cr + 0);
++ iowrite16 (mode->pixclock >> 16, scr->pll1cr + 1);
++}
++
++static struct tmio_cell_ops tc6393_cell_ops = {
++ .clock = tc6393_cell_clock,
++ .function = tc6393_cell_function,
++ .lcd_mode = tc6393_lcd_mode,
++};
++
++static void tc6393_device_release (struct device *dev)
++{
++}
++
++static int
++tc6393_device_register (struct tc6393 *tc6393, struct tmio_cell *tcell)
++{
++ const struct tc6393_cell* cell;
++ struct tmio_device* tdev;
++ struct device* dev;
++ int i;
++
++ for (i = 0; strcmp (tcell->name, tc6393_cell [i].name); )
++ if (++i >= ARRAY_SIZE(tc6393_cell))
++ return -EINVAL;
++
++ cell = tc6393_cell + i;
++ tdev = tc6393->tdev + i;
++ dev = &tdev->dev;
++
++ tdev->ops = &tc6393_cell_ops;
++ tdev->iomem = tc6393->iomem;
++ tdev->soc_data = (void*) cell;
++
++ dev->parent = tc6393->dev;
++ strncpy (dev->bus_id, cell->name, sizeof dev->bus_id);
++ dev->bus = &tc6393_bus_type;
++ dev->dma_mask = tc6393->dev->dma_mask;
++ dev->coherent_dma_mask = tc6393->dev->coherent_dma_mask;
++ dev->release = tc6393_device_release;
++ dev->platform_data = tcell->platform_data;
++
++ for (i=0; i < cell->num_resources; i++) {
++ const struct resource* cr = cell->resource + i;
++ struct resource* dr = tdev->resource + i;
++
++ dr->name = cr->name;
++ dr->start = cr->start;
++ dr->end = cr->end;
++ dr->flags = cr->flags;
++
++ /* convert memory offsets to absolutes */
++ if (cr->flags & IORESOURCE_MEM) {
++ dr->start += tc6393->iomem->start;
++ dr->end += tc6393->iomem->start;
++ }
++ }
++
++ return device_register (dev);
++}
++
++/*--------------------------------------------------------------------------*/
++
++static void tc6393_hw_init (struct tc6393 *tc6393)
++{
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ struct tc6393_platform_data* tcpd = tc6393->dev->platform_data;
++
++ tcpd->enable (tc6393->dev);
++
++ iowrite8 (0, &scr->fer);
++ iowrite16(tcpd->scr_pll2cr, &scr->pll2cr);
++ iowrite16(tcpd->scr_ccr, &scr->ccr);
++ iowrite16(tcpd->scr_mcr, &scr->mcr);
++ iowrite16(tcpd->scr_gper, &scr->gper);
++ iowrite8 (0, &scr->irr);
++ iowrite8 (0xbf, &scr->imr);
++ iowrite16(tcpd->scr_gpo_dsr, scr->gpo_dsr + 0);
++ iowrite16(tcpd->scr_gpo_dsr >> 16, scr->gpo_dsr + 1);
++ iowrite16(tcpd->scr_gpo_doecr, scr->gpo_doecr + 0);
++ iowrite16(tcpd->scr_gpo_doecr >> 16, scr->gpo_doecr + 1);
++}
++
++static int tc6393_probe (struct device *dev)
++{
++ struct platform_device* pdev = to_platform_device (dev);
++ struct tc6393_platform_data* tcpd = dev->platform_data;
++ struct tc6393* tc6393;
++ struct resource* iomem;
++ struct resource* rscr;
++ int retval;
++ int i;
++
++ iomem = platform_get_resource (pdev, IORESOURCE_MEM, 0);
++ if (!iomem)
++ return -EINVAL;
++
++ tc6393 = kzalloc (sizeof *tc6393, GFP_KERNEL);
++ if (!tc6393) {
++ retval = -ENOMEM;
++ goto err_kzalloc;
++ }
++
++ dev_set_drvdata (dev, tc6393);
++ spin_lock_init (&tc6393->lock);
++ tc6393->dev = dev;
++ tc6393->iomem = iomem;
++ tc6393->irq = platform_get_irq (pdev, 0);
++
++ rscr = &tc6393->rscr;
++ rscr->name = TMIO_NAME_CORE;
++ rscr->start = iomem->start;
++ rscr->end = iomem->start + 0xff;
++ rscr->flags = IORESOURCE_MEM;
++
++ retval = request_resource (iomem, rscr);
++ if (retval)
++ goto err_request_scr;
++
++ tc6393->scr = ioremap (rscr->start, rscr->end - rscr->start + 1);
++ if (!tc6393->scr) {
++ retval = -ENOMEM;
++ goto err_ioremap;
++ }
++
++ tc6393_hw_init (tc6393);
++
++ printk (KERN_INFO "Toshiba %s revision %d at 0x%08lx, irq %d\n",
++ TMIO_SOC_NAME, ioread8 (&tc6393->scr->revid),
++ iomem->start, tc6393->irq);
++
++ if (tc6393->irq)
++ tc6393_attach_irq (tc6393);
++
++ for (i = 0; i < tcpd->num_cells; i++)
++ tc6393_device_register (tc6393, tcpd->cell + i);
++
++ return 0;
++
++err_ioremap:
++ release_resource (rscr);
++err_request_scr:
++ kfree(tc6393);
++err_kzalloc:
++ release_resource (iomem);
++ return retval;
++}
++
++static int tc6393_dev_remove (struct device *dev, void *data)
++{
++ device_unregister (dev);
++ return 0;
++}
++
++static int tc6393_remove (struct device *dev)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev);
++
++ device_for_each_child (dev, tc6393, tc6393_dev_remove);
++
++ if (tc6393->irq)
++ tc6393_detach_irq (tc6393);
++
++ iounmap (tc6393->scr);
++ release_resource (&tc6393->rscr);
++ release_resource (tc6393->iomem);
++ kfree (tc6393);
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int tc6393_suspend (struct device *dev, pm_message_t state)
++{
++ struct tc6393_platform_data* tcpd = dev->platform_data;
++ tcpd->disable (dev);
++ return 0;
++}
++
++static int tc6393_resume (struct device *dev)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev);
++ tc6393_hw_init (tc6393);
++ return 0;
++}
++#endif
++
++static struct device_driver tc6393_device_driver = {
++ .name = TMIO_SOC_NAME,
++ .bus = &platform_bus_type,
++ .probe = tc6393_probe,
++ .remove = tc6393_remove,
++#ifdef CONFIG_PM
++ .suspend = tc6393_suspend,
++ .resume = tc6393_resume,
++#endif
++};
++
++/*--------------------------------------------------------------------------*/
++
++static int __init tc6393_init (void)
++{
++ int retval = bus_register (&tc6393_bus_type);
++ if (retval)
++ return retval;
++
++ return driver_register (&tc6393_device_driver);
++}
++
++static void __exit tc6393_exit (void)
++{
++ driver_unregister (&tc6393_device_driver);
++ bus_unregister (&tc6393_bus_type);
++}
++
++module_init (tc6393_init);
++module_exit (tc6393_exit);
++
++MODULE_DESCRIPTION ("TC6393 SoC bus driver");
++MODULE_AUTHOR ("Chris Humbert, Dirk Opfer");
++MODULE_LICENSE ("GPL");
+Index: git/arch/arm/common/Kconfig
+===================================================================
+--- git.orig/arch/arm/common/Kconfig 2006-10-31 16:08:28.000000000 +0000
++++ git/arch/arm/common/Kconfig 2006-11-07 22:13:09.000000000 +0000
+@@ -31,3 +31,6 @@ config SHARPSL_PM
+
+ config SHARP_SCOOP
+ bool
++
++config TOSHIBA_TC6393XB
++ bool
+Index: git/arch/arm/mach-pxa/Kconfig
+===================================================================
+--- git.orig/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:06.000000000 +0000
++++ git/arch/arm/mach-pxa/ 2006-11-07 23:30:34.000000000 +0000
+@@ -128,6 +128,7 @@ config MACH_BORZOI
+ config MACH_TOSA
+ bool "Enable Sharp SL-6000x (Tosa) Support"
+ depends on PXA_SHARPSL_25x
++ select TOSHIBA_TC6393XB
+
+ config PXA25x
+ bool
+Index: git/arch/arm/common/Makefile
+===================================================================
+--- git.orig/arch/arm/common/Makefile 2006-10-31 16:08:28.000000000 +0000
++++ git/arch/arm/common/Makefile 2006-11-07 22:13:09.000000000 +0000
+@@ -17,3 +17,4 @@ obj-$(CONFIG_SHARPSL_PM) += sharpsl_pm.o
+ obj-$(CONFIG_SHARP_SCOOP) += scoop.o
+ obj-$(CONFIG_ARCH_IXP2000) += uengine.o
+ obj-$(CONFIG_ARCH_IXP23XX) += uengine.o
++obj-$(CONFIG_TOSHIBA_TC6393XB) += tc6393xb.o
+Index: git/include/asm-arm/hardware/tmio.h
+===================================================================
+--- git.orig/include/asm-arm/hardware/tmio.h 2006-11-07 22:13:09.000000000 +0000
++++ git/include/asm-arm/hardware/tmio.h 2006-11-07 22:13:09.000000000 +0000
+@@ -91,6 +91,50 @@ struct tmio_device {
+
+ /*--------------------------------------------------------------------------*/
+
++/*
++ * TC6393XB SoC
++ */
++#ifdef CONFIG_TOSHIBA_TC6393XB
++#define TMIO_SOC_TC6393XB
++#define TMIO_SOC_NAME "TC6393XB"
++#define TMIO_NAME_BUS "tc6393-bus"
++#define TMIO_NAME_CORE "tc6393-core"
++#define TMIO_NAME_NAND "tc6393-nand"
++#define TMIO_NAME_SD "tc6393-sd"
++#define TMIO_NAME_OHCI "tc6393-ohci"
++#define TMIO_NAME_SERIAL "tc6393-serial"
++#define TMIO_NAME_LCD "tc6393-lcd"
++#define tmio_bus_type tc6393_bus_type
++
++#define TC6393_GPIO(x) (1 << (x))
++
++extern struct bus_type tc6393_bus_type;
++
++struct tc6393_platform_data {
++ u16 scr_pll2cr; /* PLL2 Control */
++ u16 scr_ccr; /* Clock Control */
++ u16 scr_mcr; /* Mode Control */
++ u16 scr_gper; /* GP Enable */
++ u32 scr_gpo_doecr; /* GPO Data OE Control */
++ u32 scr_gpo_dsr; /* GPO Data Set */
++
++ /* cells to register as devices */
++ struct tmio_cell* cell;
++ unsigned int num_cells;
++
++ /* callbacks to enable and disable the TC6393XB's power and clock */
++ void (*enable) (struct device *dev);
++ void (*disable) (struct device *dev);
++};
++
++u32 get_tc6393_gpio (struct device *dev);
++u32 set_tc6393_gpio (struct device *dev, u32 bits);
++u32 reset_tc6393_gpio (struct device *dev, u32 bits);
++
++/*--------------------------------------------------------------------------*/
++
++#else
+ #error "no TMIO SoC configured"
++#endif
+
+ #endif
+Index: git/include/asm-arm/arch-pxa/irqs.h
+===================================================================
+--- git.orig/include/asm-arm/arch-pxa/irqs.h 2006-10-31 16:09:33.000000000 +0000
++++ git/include/asm-arm/arch-pxa/irqs.h 2006-11-07 22:13:09.000000000 +0000
+@@ -163,17 +163,27 @@
+ #define IRQ_LOCOMO_SPI_OVRN (IRQ_BOARD_END + 20)
+ #define IRQ_LOCOMO_SPI_TEND (IRQ_BOARD_END + 21)
+
++#define IRQ_TC6393_START (IRQ_BOARD_END)
++#define IRQ_TC6393_NAND (IRQ_BOARD_END + 0)
++#define IRQ_TC6393_SD (IRQ_BOARD_END + 1)
++#define IRQ_TC6393_OHCI (IRQ_BOARD_END + 2)
++#define IRQ_TC6393_SERIAL (IRQ_BOARD_END + 3)
++#define IRQ_TC6393_LCD (IRQ_BOARD_END + 4)
++
+ /*
+ * Figure out the MAX IRQ number.
+ *
+ * If we have an SA1111, the max IRQ is S1_BVD1_STSCHG+1.
+ * If we have an LoCoMo, the max IRQ is IRQ_LOCOMO_SPI_TEND+1
++ * If we have an TC6393XB, the max IRQ is IRQ_TC6393_LCD+1
+ * Otherwise, we have the standard IRQs only.
+ */
+ #ifdef CONFIG_SA1111
+ #define NR_IRQS (IRQ_S1_BVD1_STSCHG + 1)
+ #elif defined(CONFIG_SHARP_LOCOMO)
+ #define NR_IRQS (IRQ_LOCOMO_SPI_TEND + 1)
++#elif defined(CONFIG_TOSHIBA_TC6393XB)
++#define NR_IRQS (IRQ_TC6393_LCD + 1)
+ #elif defined(CONFIG_ARCH_LUBBOCK) || \
+ defined(CONFIG_MACH_LOGICPD_PXA270) || \
+ defined(CONFIG_MACH_MAINSTONE)
+ arch/arm/common/Kconfig | 3
+ arch/arm/common/Makefile | 1
+ arch/arm/common/tc6393xb.c | 668 ++++++++++++++++++++++++++++++++++++++++
+ arch/arm/mach-pxa/Kconfig | 1
+ include/asm-arm/arch-pxa/irqs.h | 10
+ include/asm-arm/hardware/tmio.h | 44 ++
+ 6 files changed, 727 insertions(+)
+
+Index: git/arch/arm/common/tc6393xb.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ git/arch/arm/common/tc6393xb.c 2006-11-07 22:14:49.000000000 +0000
+@@ -0,0 +1,668 @@
++/*
++ * Toshiba TC6393XB SoC support
++ *
++ * Maintainer: Chris Humbert <mahadri-kernel@drigon.com>
++ *
++ * Copyright (c) 2005-2006 Chris Humbert
++ * Copyright (c) 2005 Dirk Opfer
++ *
++ * Based on code written by Sharp/Lineo for 2.4 kernels
++ * Based on locomo.c
++ *
++ * 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.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/errno.h>
++#include <linux/ioport.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/fb.h>
++
++#include <asm/hardware.h>
++#include <asm/mach-types.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/mach/irq.h>
++#include <asm/arch/irqs.h>
++#include <asm/hardware/tmio.h>
++
++#ifndef TMIO_SOC_TC6393XB
++#error "TC6393XB SoC not configured"
++#endif
++
++/*--------------------------------------------------------------------------*/
++
++/* cell ids must be 0-based because they are used as array indexes. */
++#define TC6393_CELL_NAND 0
++#define TC6393_CELL_SD 1
++#define TC6393_CELL_OHCI 2
++#define TC6393_CELL_SERIAL 3
++#define TC6393_CELL_LCD 4
++#define TC6393_NUM_CELLS 5
++
++#define TC6393_RESOURCE(_name, _start, _end, _flags) \
++ { \
++ .name = _name, \
++ .start = _start, \
++ .end = _end, \
++ .flags = _flags, \
++ }
++
++#define TC6393_MEM(name, start, size) \
++ TC6393_RESOURCE(name, start, (start) + (size) - 1, IORESOURCE_MEM)
++
++#define TC6393_IRQ(name, irq) \
++ TC6393_RESOURCE(name, irq, irq, IORESOURCE_IRQ)
++
++const static struct resource tc6393_NAND_resource[] = {
++ TC6393_MEM (TMIO_NAME_NAND, 0x000100, 0x100),
++ TC6393_MEM (TMIO_NAME_NAND, 0x001000, 0x008),
++ TC6393_MEM (TMIO_NAME_NAND, 0, 0),
++ TC6393_IRQ (TMIO_NAME_NAND, IRQ_TC6393_NAND),
++};
++
++const static struct resource tc6393_SD_resource[] = {
++ TC6393_MEM (TMIO_NAME_SD, 0x000200, 0x100),
++ TC6393_MEM (TMIO_NAME_SD, 0x002000, 0x200),
++ TC6393_MEM (TMIO_NAME_SD, 0, 0),
++ TC6393_IRQ (TMIO_NAME_SD, IRQ_TC6393_SD),
++};
++
++const static struct resource tc6393_OHCI_resource[] = {
++ TC6393_MEM (TMIO_NAME_OHCI, 0x000300, 0x100),
++ TC6393_MEM (TMIO_NAME_OHCI, 0x003000, 0x100),
++ TC6393_MEM (TMIO_NAME_OHCI, 0x010000, 32 * 1024),
++ TC6393_IRQ (TMIO_NAME_OHCI, IRQ_TC6393_OHCI),
++};
++
++const static struct resource tc6393_SERIAL_resource[] = {
++ TC6393_MEM (TMIO_NAME_SERIAL, 0x000400, 0x100),
++ TC6393_MEM (TMIO_NAME_SERIAL, 0x004000, 0x100),
++ TC6393_MEM (TMIO_NAME_SERIAL, 0, 0),
++ TC6393_IRQ (TMIO_NAME_SERIAL, IRQ_TC6393_SERIAL),
++};
++
++const static struct resource tc6393_LCD_resource[] = {
++ TC6393_MEM (TMIO_NAME_LCD, 0x000500, 0x100),
++ TC6393_MEM (TMIO_NAME_LCD, 0x005000, 0x200),
++ TC6393_MEM (TMIO_NAME_LCD, 0x100000, 1024 * 1024),
++ TC6393_IRQ (TMIO_NAME_LCD, IRQ_TC6393_LCD),
++};
++
++#define TC6393_CELL(_NAME) \
++ [TC6393_CELL_##_NAME] = { \
++ .name = TMIO_NAME_##_NAME, \
++ .id = TC6393_CELL_##_NAME, \
++ .resource = tc6393_##_NAME##_resource, \
++ .num_resources = ARRAY_SIZE (tc6393_##_NAME##_resource), \
++ }
++
++struct tc6393_cell {
++ const char* name;
++ unsigned int id;
++ const struct resource* resource;
++ unsigned int num_resources;
++};
++
++const static struct tc6393_cell tc6393_cell [TC6393_NUM_CELLS] = {
++ TC6393_CELL (NAND ),
++ TC6393_CELL (SD ),
++ TC6393_CELL (OHCI ),
++ TC6393_CELL (SERIAL ),
++ TC6393_CELL (LCD ),
++};
++
++/*--------------------------------------------------------------------------*/
++
++/*
++ * TC6393 System Configuration Register
++ */
++struct tc6393_scr {
++ u8 x00[8];
++ u8 revid; /* 0x08 Revision ID */
++ u8 x01[0x47];
++ u8 isr; /* 0x50 Interrupt Status */
++ u8 x02;
++ u8 imr; /* 0x52 Interrupt Mask */
++ u8 x03;
++ u8 irr; /* 0x54 Interrupt Routing */
++ u8 x04[0x0b];
++ u16 gper; /* 0x60 GP Enable */
++ u8 x05[2];
++ u16 gpi_sr[2]; /* 0x64 GPI Status */
++ u16 gpi_imr[2]; /* 0x68 GPI INT Mask */
++ u16 gpi_eder[2]; /* 0x6c GPI Edge Detect Enable */
++ u16 gpi_lir[4]; /* 0x70 GPI Level Invert */
++ u16 gpo_dsr[2]; /* 0x78 GPO Data Set */
++ u16 gpo_doecr[2]; /* 0x7c GPO Data OE Control */
++ u16 gp_iarcr[2]; /* 0x80 GP Internal Active Reg Control */
++ u16 gp_iarlcr[2]; /* 0x84 GP Internal Active Reg Level Con*/
++ u8 gpi_bcr[4]; /* 0x88 GPI Buffer Control */
++ u16 gpa_iarcr; /* 0x8c GPa Internal Active Reg Control */
++ u8 x06[2];
++ u16 gpa_iarlcr; /* 0x90 GPa Internal Active Reg Level Co*/
++ u8 x07[2];
++ u16 gpa_bcr; /* 0x94 GPa Buffer Control */
++ u8 x08[2];
++ u16 ccr; /* 0x98 Clock Control */
++ u16 pll2cr; /* 0x9a PLL2 Control */
++ u16 pll1cr[2]; /* 0x9c PLL1 Control */
++ u8 diarcr; /* 0xa0 Device Internal Active Reg Contr*/
++ u8 dbocr; /* 0xa1 Device Buffer Off Control */
++ u8 x09[0x3e];
++ u8 fer; /* 0xe0 Function Enable */
++ u8 x10[3];
++ u16 mcr; /* 0xe4 Mode Control */
++ u8 x11[0x14];
++ u8 config; /* 0xfc Configuration Control */
++ u8 x12[2];
++ u8 debug; /* 0xff Debug */
++} __attribute__ ((packed));
++
++union tc6393_scr_fer {
++ u8 raw;
++struct {
++ unsigned usben:1; /* D0 USB enable */
++ unsigned lcdcven:1; /* D1 polysylicon TFT enable */
++ unsigned slcden:1; /* D2 SLCD enable */
++} __attribute__ ((packed));
++} __attribute__ ((packed));
++
++union tc6393_scr_ccr {
++ u16 raw;
++struct {
++ unsigned ck32ken:1; /* D0 SD host clock enable */
++ unsigned usbcken:1; /* D1 USB host clock enable */
++ unsigned x00:2;
++ unsigned sharp:1; /* D4 ??? set in Sharp's code */
++ unsigned x01:3;
++ enum { disable = 0,
++ m12MHz = 1,
++ m24MHz = 2,
++ m48MHz = 3,
++ } mclksel:3; /* D10-D8 LCD controller clock */
++ unsigned x02:1;
++ enum { h24MHz = 0,
++ h48MHz = 1,
++ } hclksel:2; /* D13-D12 host bus clock */
++ unsigned x03:2;
++} __attribute__ ((packed));
++} __attribute__ ((packed));
++
++/*--------------------------------------------------------------------------*/
++
++struct tc6393 {
++ spinlock_t lock; /* read-modify-write lock */
++ struct device* dev; /* TC6393 device */
++ struct tc6393_scr __iomem *scr; /* system configuration reg */
++
++ struct resource rscr; /* system config reg resource */
++ struct resource* iomem; /* entire TC6393 iomem resource */
++ unsigned int irq; /* hardware cascade irq */
++
++ struct tmio_device tdev [TC6393_NUM_CELLS];
++};
++
++/*--------------------------------------------------------------------------*/
++
++static u32 tc6393_ioread32 (const void __iomem *addr)
++{
++ return ((u32) ioread16 (addr)) | (((u32) ioread16 (addr + 2)) << 16);
++}
++
++static u32 tc6393_iowrite32 (u32 val, const void __iomem *addr)
++{
++ iowrite16 (val, addr);
++ iowrite16 (val >> 16, addr + 2);
++ return val;
++}
++
++u32 get_tc6393_gpio (struct device *dev)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++
++ return tc6393_ioread32 (scr->gpo_dsr);
++}
++EXPORT_SYMBOL (get_tc6393_gpio);
++
++u32 set_tc6393_gpio (struct device *dev, u32 bits)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ unsigned long flags;
++ u32 dsr;
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ dsr = tc6393_ioread32 (scr->gpo_dsr) | bits;
++ tc6393_iowrite32 (dsr, scr->gpo_dsr);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++
++ return dsr;
++}
++EXPORT_SYMBOL (set_tc6393_gpio);
++
++u32 reset_tc6393_gpio (struct device *dev, u32 bits)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ unsigned long flags;
++ u32 dsr;
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ dsr = tc6393_ioread32 (scr->gpo_dsr) & ~bits;
++ tc6393_iowrite32 (dsr, scr->gpo_dsr);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++
++ return dsr;
++}
++EXPORT_SYMBOL (reset_tc6393_gpio);
++
++/*--------------------------------------------------------------------------*/
++
++static void
++tc6393_irq (unsigned int irq, struct irq_desc *desc)
++{
++ struct tc6393* tc6393 = get_irq_chip_data (irq);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ unsigned int isr;
++ unsigned int bit;
++ unsigned int i;
++
++ desc->chip->ack (irq);
++
++ while ((isr = ioread8(&scr->isr) & ~ioread8(&scr->imr)))
++ for (bit = 1, i = IRQ_TC6393_START; i <= IRQ_TC6393_LCD;
++ bit <<= 1, i++)
++ if (isr & bit)
++ desc_handle_irq (i, irq_desc + i);
++}
++
++static void tc6393_irq_ack (unsigned int irq)
++{
++}
++
++static void tc6393_irq_mask (unsigned int irq)
++{
++ struct tc6393* tc6393 = get_irq_chip_data (irq);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ unsigned long flags;
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ iowrite8 (ioread8 (&scr->imr) | (1 << (irq - IRQ_TC6393_START)),
++ &scr->imr);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++}
++
++static void tc6393_irq_unmask (unsigned int irq)
++{
++ struct tc6393* tc6393 = get_irq_chip_data (irq);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ unsigned long flags;
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ iowrite8 (ioread8 (&scr->imr) & ~(1 << (irq - IRQ_TC6393_START)),
++ &scr->imr);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++}
++
++static struct irq_chip tc6393_chip = {
++ .ack = tc6393_irq_ack,
++ .mask = tc6393_irq_mask,
++ .unmask = tc6393_irq_unmask,
++};
++
++static void tc6393_attach_irq (struct tc6393 *tc6393)
++{
++ unsigned int irq;
++
++ for (irq = IRQ_TC6393_START; irq <= IRQ_TC6393_LCD; irq++) {
++ set_irq_chip (irq, &tc6393_chip);
++ set_irq_chip_data(irq, tc6393);
++ set_irq_handler (irq, handle_edge_irq);
++ set_irq_flags (irq, IRQF_VALID | IRQF_PROBE);
++ }
++
++ set_irq_type (tc6393->irq, IRQT_FALLING);
++ set_irq_chip_data (tc6393->irq, tc6393);
++ set_irq_chained_handler (tc6393->irq, tc6393_irq);
++}
++
++static void tc6393_detach_irq (struct tc6393 *tc6393)
++{
++ unsigned int irq;
++
++ set_irq_chained_handler (tc6393->irq, NULL);
++ set_irq_chip_data (tc6393->irq, NULL);
++
++ for (irq = IRQ_TC6393_START; irq <= IRQ_TC6393_LCD; irq++) {
++ set_irq_flags (irq, 0);
++ set_irq_chip (irq, NULL);
++ set_irq_chip_data(irq, NULL);
++ }
++}
++
++/*--------------------------------------------------------------------------*/
++
++static int tc6393_bus_match (struct device *dev, struct device_driver *drv)
++{
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ const struct tc6393_cell* cell = tdev->soc_data;
++
++ return !strcmp (cell->name, drv->name);
++}
++
++static int tc6393_bus_suspend (struct device *dev, pm_message_t state)
++{
++ struct device_driver* drv = dev->driver;
++ return drv && drv->suspend ? drv->suspend (dev, state) : 0;
++}
++
++static int tc6393_bus_resume (struct device *dev)
++{
++ struct device_driver* drv = dev->driver;
++ return drv && drv->resume ? drv->resume (dev) : 0;
++}
++
++struct bus_type tc6393_bus_type = {
++ .name = TMIO_NAME_BUS,
++ .match = tc6393_bus_match,
++ .suspend = tc6393_bus_suspend,
++ .resume = tc6393_bus_resume,
++};
++EXPORT_SYMBOL (tc6393_bus_type);
++
++/*--------------------------------------------------------------------------*/
++
++static void tc6393_cell_clock (struct device *dev, int enable)
++{
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ const struct tc6393_cell* cell = tdev->soc_data;
++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ union tc6393_scr_ccr ccr;
++ unsigned long flags;
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ ccr.raw = ioread16 (&scr->ccr);
++
++ switch (cell->id) {
++ case TC6393_CELL_SD: ccr.ck32ken = enable; break;
++ case TC6393_CELL_OHCI: ccr.usbcken = enable; break;
++ case TC6393_CELL_LCD:
++ ccr.mclksel = enable ? m48MHz : disable;
++ break;
++ }
++
++ printk (KERN_DEBUG TMIO_NAME_CORE ": scr->ccr = %04x\n", ccr.raw);
++
++ iowrite16(ccr.raw, &scr->ccr);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++}
++
++static void tc6393_cell_function (struct device *dev, int enable)
++{
++ struct tmio_device* tdev = dev_to_tdev (dev);
++ const struct tc6393_cell* cell = tdev->soc_data;
++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ union tc6393_scr_fer fer;
++ unsigned long flags;
++
++ if (cell->id == TC6393_CELL_NAND) {
++ if (enable) {
++ /* SMD buffer on */
++ printk (KERN_DEBUG TMIO_NAME_CORE ": SMD buffer on\n");
++ iowrite8 (0xff, scr->gpi_bcr + 1);
++ }
++ return;
++ }
++
++ spin_lock_irqsave (&tc6393->lock, flags);
++ fer.raw = ioread16 (&scr->fer);
++
++ switch (cell->id) {
++ case TC6393_CELL_OHCI: fer.usben = enable; break;
++ case TC6393_CELL_LCD: fer.slcden = enable; break;
++ }
++
++ printk (KERN_DEBUG TMIO_NAME_CORE ": scr->fer = %02x\n", fer.raw);
++
++ iowrite8 (fer.raw, &scr->fer);
++ spin_unlock_irqrestore (&tc6393->lock, flags);
++}
++
++static void
++tc6393_lcd_mode (struct device *dev, const struct fb_videomode *mode)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent);
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++
++ iowrite16 (mode->pixclock, scr->pll1cr + 0);
++ iowrite16 (mode->pixclock >> 16, scr->pll1cr + 1);
++}
++
++static struct tmio_cell_ops tc6393_cell_ops = {
++ .clock = tc6393_cell_clock,
++ .function = tc6393_cell_function,
++ .lcd_mode = tc6393_lcd_mode,
++};
++
++static void tc6393_device_release (struct device *dev)
++{
++}
++
++static int
++tc6393_device_register (struct tc6393 *tc6393, struct tmio_cell *tcell)
++{
++ const struct tc6393_cell* cell;
++ struct tmio_device* tdev;
++ struct device* dev;
++ int i;
++
++ for (i = 0; strcmp (tcell->name, tc6393_cell [i].name); )
++ if (++i >= ARRAY_SIZE(tc6393_cell))
++ return -EINVAL;
++
++ cell = tc6393_cell + i;
++ tdev = tc6393->tdev + i;
++ dev = &tdev->dev;
++
++ tdev->ops = &tc6393_cell_ops;
++ tdev->iomem = tc6393->iomem;
++ tdev->soc_data = (void*) cell;
++
++ dev->parent = tc6393->dev;
++ strncpy (dev->bus_id, cell->name, sizeof dev->bus_id);
++ dev->bus = &tc6393_bus_type;
++ dev->dma_mask = tc6393->dev->dma_mask;
++ dev->coherent_dma_mask = tc6393->dev->coherent_dma_mask;
++ dev->release = tc6393_device_release;
++ dev->platform_data = tcell->platform_data;
++
++ for (i=0; i < cell->num_resources; i++) {
++ const struct resource* cr = cell->resource + i;
++ struct resource* dr = tdev->resource + i;
++
++ dr->name = cr->name;
++ dr->start = cr->start;
++ dr->end = cr->end;
++ dr->flags = cr->flags;
++
++ /* convert memory offsets to absolutes */
++ if (cr->flags & IORESOURCE_MEM) {
++ dr->start += tc6393->iomem->start;
++ dr->end += tc6393->iomem->start;
++ }
++ }
++
++ return device_register (dev);
++}
++
++/*--------------------------------------------------------------------------*/
++
++static void tc6393_hw_init (struct tc6393 *tc6393)
++{
++ struct tc6393_scr __iomem * scr = tc6393->scr;
++ struct tc6393_platform_data* tcpd = tc6393->dev->platform_data;
++
++ tcpd->enable (tc6393->dev);
++
++ iowrite8 (0, &scr->fer);
++ iowrite16(tcpd->scr_pll2cr, &scr->pll2cr);
++ iowrite16(tcpd->scr_ccr, &scr->ccr);
++ iowrite16(tcpd->scr_mcr, &scr->mcr);
++ iowrite16(tcpd->scr_gper, &scr->gper);
++ iowrite8 (0, &scr->irr);
++ iowrite8 (0xbf, &scr->imr);
++ iowrite16(tcpd->scr_gpo_dsr, scr->gpo_dsr + 0);
++ iowrite16(tcpd->scr_gpo_dsr >> 16, scr->gpo_dsr + 1);
++ iowrite16(tcpd->scr_gpo_doecr, scr->gpo_doecr + 0);
++ iowrite16(tcpd->scr_gpo_doecr >> 16, scr->gpo_doecr + 1);
++}
++
++static int tc6393_probe (struct device *dev)
++{
++ struct platform_device* pdev = to_platform_device (dev);
++ struct tc6393_platform_data* tcpd = dev->platform_data;
++ struct tc6393* tc6393;
++ struct resource* iomem;
++ struct resource* rscr;
++ int retval;
++ int i;
++
++ iomem = platform_get_resource (pdev, IORESOURCE_MEM, 0);
++ if (!iomem)
++ return -EINVAL;
++
++ tc6393 = kzalloc (sizeof *tc6393, GFP_KERNEL);
++ if (!tc6393) {
++ retval = -ENOMEM;
++ goto err_kzalloc;
++ }
++
++ dev_set_drvdata (dev, tc6393);
++ spin_lock_init (&tc6393->lock);
++ tc6393->dev = dev;
++ tc6393->iomem = iomem;
++ tc6393->irq = platform_get_irq (pdev, 0);
++
++ rscr = &tc6393->rscr;
++ rscr->name = TMIO_NAME_CORE;
++ rscr->start = iomem->start;
++ rscr->end = iomem->start + 0xff;
++ rscr->flags = IORESOURCE_MEM;
++
++ retval = request_resource (iomem, rscr);
++ if (retval)
++ goto err_request_scr;
++
++ tc6393->scr = ioremap (rscr->start, rscr->end - rscr->start + 1);
++ if (!tc6393->scr) {
++ retval = -ENOMEM;
++ goto err_ioremap;
++ }
++
++ tc6393_hw_init (tc6393);
++
++ printk (KERN_INFO "Toshiba %s revision %d at 0x%08lx, irq %d\n",
++ TMIO_SOC_NAME, ioread8 (&tc6393->scr->revid),
++ iomem->start, tc6393->irq);
++
++ if (tc6393->irq)
++ tc6393_attach_irq (tc6393);
++
++ for (i = 0; i < tcpd->num_cells; i++)
++ tc6393_device_register (tc6393, tcpd->cell + i);
++
++ return 0;
++
++err_ioremap:
++ release_resource (rscr);
++err_request_scr:
++ kfree(tc6393);
++err_kzalloc:
++ release_resource (iomem);
++ return retval;
++}
++
++static int tc6393_dev_remove (struct device *dev, void *data)
++{
++ device_unregister (dev);
++ return 0;
++}
++
++static int tc6393_remove (struct device *dev)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev);
++
++ device_for_each_child (dev, tc6393, tc6393_dev_remove);
++
++ if (tc6393->irq)
++ tc6393_detach_irq (tc6393);
++
++ iounmap (tc6393->scr);
++ release_resource (&tc6393->rscr);
++ release_resource (tc6393->iomem);
++ kfree (tc6393);
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int tc6393_suspend (struct device *dev, pm_message_t state)
++{
++ struct tc6393_platform_data* tcpd = dev->platform_data;
++ tcpd->disable (dev);
++ return 0;
++}
++
++static int tc6393_resume (struct device *dev)
++{
++ struct tc6393* tc6393 = dev_get_drvdata (dev);
++ tc6393_hw_init (tc6393);
++ return 0;
++}
++#endif
++
++static struct device_driver tc6393_device_driver = {
++ .name = TMIO_SOC_NAME,
++ .bus = &platform_bus_type,
++ .probe = tc6393_probe,
++ .remove = tc6393_remove,
++#ifdef CONFIG_PM
++ .suspend = tc6393_suspend,
++ .resume = tc6393_resume,
++#endif
++};
++
++/*--------------------------------------------------------------------------*/
++
++static int __init tc6393_init (void)
++{
++ int retval = bus_register (&tc6393_bus_type);
++ if (retval)
++ return retval;
++
++ return driver_register (&tc6393_device_driver);
++}
++
++static void __exit tc6393_exit (void)
++{
++ driver_unregister (&tc6393_device_driver);
++ bus_unregister (&tc6393_bus_type);
++}
++
++module_init (tc6393_init);
++module_exit (tc6393_exit);
++
++MODULE_DESCRIPTION ("TC6393 SoC bus driver");
++MODULE_AUTHOR ("Chris Humbert, Dirk Opfer");
++MODULE_LICENSE ("GPL");
+Index: git/arch/arm/common/Kconfig
+===================================================================
+--- git.orig/arch/arm/common/Kconfig 2006-10-31 16:08:28.000000000 +0000
++++ git/arch/arm/common/Kconfig 2006-11-07 22:13:09.000000000 +0000
+@@ -31,3 +31,6 @@ config SHARPSL_PM
+
+ config SHARP_SCOOP
+ bool
++
++config TOSHIBA_TC6393XB
++ bool
+Index: git/arch/arm/mach-pxa/Kconfig
+===================================================================
+--- git.orig/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:06.000000000 +0000
++++ git/arch/arm/mach-pxa/ 2006-11-07 23:30:34.000000000 +0000
+@@ -128,6 +128,7 @@ config MACH_BORZOI
+ config MACH_TOSA
+ bool "Enable Sharp SL-6000x (Tosa) Support"
+ depends on PXA_SHARPSL_25x
++ select TOSHIBA_TC6393XB
+
+ config PXA25x
+ bool
+Index: git/arch/arm/common/Makefile
+===================================================================
+--- git.orig/arch/arm/common/Makefile 2006-10-31 16:08:28.000000000 +0000
++++ git/arch/arm/common/Makefile 2006-11-07 22:13:09.000000000 +0000
+@@ -17,3 +17,4 @@ obj-$(CONFIG_SHARPSL_PM) += sharpsl_pm.o
+ obj-$(CONFIG_SHARP_SCOOP) += scoop.o
+ obj-$(CONFIG_ARCH_IXP2000) += uengine.o
+ obj-$(CONFIG_ARCH_IXP23XX) += uengine.o
++obj-$(CONFIG_TOSHIBA_TC6393XB) += tc6393xb.o
+Index: git/include/asm-arm/hardware/tmio.h
+===================================================================
+--- git.orig/include/asm-arm/hardware/tmio.h 2006-11-07 22:13:09.000000000 +0000
++++ git/include/asm-arm/hardware/tmio.h 2006-11-07 22:13:09.000000000 +0000
+@@ -91,6 +91,50 @@ struct tmio_device {
+
+ /*--------------------------------------------------------------------------*/
+
++/*
++ * TC6393XB SoC
++ */
++#ifdef CONFIG_TOSHIBA_TC6393XB
++#define TMIO_SOC_TC6393XB
++#define TMIO_SOC_NAME "TC6393XB"
++#define TMIO_NAME_BUS "tc6393-bus"
++#define TMIO_NAME_CORE "tc6393-core"
++#define TMIO_NAME_NAND "tc6393-nand"
++#define TMIO_NAME_SD "tc6393-sd"
++#define TMIO_NAME_OHCI "tc6393-ohci"
++#define TMIO_NAME_SERIAL "tc6393-serial"
++#define TMIO_NAME_LCD "tc6393-lcd"
++#define tmio_bus_type tc6393_bus_type
++
++#define TC6393_GPIO(x) (1 << (x))
++
++extern struct bus_type tc6393_bus_type;
++
++struct tc6393_platform_data {
++ u16 scr_pll2cr; /* PLL2 Control */
++ u16 scr_ccr; /* Clock Control */
++ u16 scr_mcr; /* Mode Control */
++ u16 scr_gper; /* GP Enable */
++ u32 scr_gpo_doecr; /* GPO Data OE Control */
++ u32 scr_gpo_dsr; /* GPO Data Set */
++
++ /* cells to register as devices */
++ struct tmio_cell* cell;
++ unsigned int num_cells;
++
++ /* callbacks to enable and disable the TC6393XB's power and clock */
++ void (*enable) (struct device *dev);
++ void (*disable) (struct device *dev);
++};
++
++u32 get_tc6393_gpio (struct device *dev);
++u32 set_tc6393_gpio (struct device *dev, u32 bits);
++u32 reset_tc6393_gpio (struct device *dev, u32 bits);
++
++/*--------------------------------------------------------------------------*/
++
++#else
+ #error "no TMIO SoC configured"
++#endif
+
+ #endif
+Index: git/include/asm-arm/arch-pxa/irqs.h
+===================================================================
+--- git.orig/include/asm-arm/arch-pxa/irqs.h 2006-10-31 16:09:33.000000000 +0000
++++ git/include/asm-arm/arch-pxa/irqs.h 2006-11-07 22:13:09.000000000 +0000
+@@ -163,17 +163,27 @@
+ #define IRQ_LOCOMO_SPI_OVRN (IRQ_BOARD_END + 20)
+ #define IRQ_LOCOMO_SPI_TEND (IRQ_BOARD_END + 21)
+
++#define IRQ_TC6393_START (IRQ_BOARD_END)
++#define IRQ_TC6393_NAND (IRQ_BOARD_END + 0)
++#define IRQ_TC6393_SD (IRQ_BOARD_END + 1)
++#define IRQ_TC6393_OHCI (IRQ_BOARD_END + 2)
++#define IRQ_TC6393_SERIAL (IRQ_BOARD_END + 3)
++#define IRQ_TC6393_LCD (IRQ_BOARD_END + 4)
++
+ /*
+ * Figure out the MAX IRQ number.
+ *
+ * If we have an SA1111, the max IRQ is S1_BVD1_STSCHG+1.
+ * If we have an LoCoMo, the max IRQ is IRQ_LOCOMO_SPI_TEND+1
++ * If we have an TC6393XB, the max IRQ is IRQ_TC6393_LCD+1
+ * Otherwise, we have the standard IRQs only.
+ */
+ #ifdef CONFIG_SA1111
+ #define NR_IRQS (IRQ_S1_BVD1_STSCHG + 1)
+ #elif defined(CONFIG_SHARP_LOCOMO)
+ #define NR_IRQS (IRQ_LOCOMO_SPI_TEND + 1)
++#elif defined(CONFIG_TOSHIBA_TC6393XB)
++#define NR_IRQS (IRQ_TC6393_LCD + 1)
+ #elif defined(CONFIG_ARCH_LUBBOCK) || \
+ defined(CONFIG_MACH_LOGICPD_PXA270) || \
+ defined(CONFIG_MACH_MAINSTONE)
diff --git a/packages/linux/linux-rp-2.6.23/tosa-bluetooth-r8.patch b/packages/linux/linux-rp-2.6.23/tosa-bluetooth-r8.patch
new file mode 100644
index 0000000000..9976549956
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/tosa-bluetooth-r8.patch
@@ -0,0 +1,388 @@
+Index: linux-2.6.17/arch/arm/mach-pxa/Makefile
+===================================================================
+--- linux-2.6.17.orig/arch/arm/mach-pxa/Makefile 2006-06-20 11:45:51.252467944 +0200
++++ linux-2.6.17/arch/arm/mach-pxa/Makefile 2006-06-20 11:46:33.619027248 +0200
+@@ -16,7 +16,7 @@
+ obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o
+ obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o
+ obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o
+-obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o tosa_lcd.o
++obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o tosa_lcd.o tosa_bt.o
+ obj-$(CONFIG_MACH_EM_X270) += em-x270.o
+ obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o
+
+Index: linux-2.6.17/arch/arm/mach-pxa/tosa_bt.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/arch/arm/mach-pxa/tosa_bt.c 2006-06-20 11:46:08.107905528 +0200
+@@ -0,0 +1,128 @@
++/*
++ * Bluetooth control code for Sharp SL-6000x (tosa)
++ *
++ * Copyright (c) 2005 Dirk Opfer
++ *
++ * 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.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <asm/hardware.h>
++
++#include <asm/hardware/scoop.h>
++#include <asm/arch/tosa.h>
++#include <asm/arch/pxa-regs.h>
++
++
++static int tosa_bluetooth_power(int on)
++{
++
++ if (!on) { //off
++
++ set_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET);
++ pxa_gpio_mode(GPIO42_BTRXD|GPIO_IN);
++ pxa_gpio_mode(GPIO43_BTTXD|GPIO_IN);
++ pxa_gpio_mode(GPIO44_BTCTS|GPIO_IN);
++ pxa_gpio_mode(GPIO45_BTRTS|GPIO_IN);
++ mdelay(10); // wait 10ms
++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET);
++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_PWR_EN);
++ reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_BT_LED); // turn off BT LED
++
++ } else { // on
++
++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET);
++ set_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_PWR_EN);
++ pxa_gpio_mode(GPIO42_HWRXD_MD);
++ pxa_gpio_mode(GPIO43_HWTXD_MD);
++ pxa_gpio_mode(GPIO44_HWCTS_MD);
++ pxa_gpio_mode(GPIO45_HWRTS_MD);
++
++ set_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET);
++ mdelay(20); // wait 20ms
++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET);
++ set_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_BT_LED); // turn BT LED on
++ }
++ return 0;
++}
++
++/*
++ * Support Routines
++ */
++int __init tosa_bluetooth_probe(struct platform_device *dev)
++{
++ int ret = 0;
++ pxa_gpio_mode(GPIO42_BTRXD|GPIO_IN);
++ pxa_gpio_mode(GPIO43_BTTXD|GPIO_IN);
++ pxa_gpio_mode(GPIO44_BTCTS|GPIO_IN);
++ pxa_gpio_mode(GPIO45_BTRTS|GPIO_IN);
++ set_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_PWR_EN);
++ mdelay(5);
++
++ if ( (GPLR(GPIO42_BTRXD) & GPIO_bit(GPIO42_BTRXD))==0 &&
++ (GPLR(GPIO44_BTCTS) & GPIO_bit(GPIO44_BTCTS))==0) {
++ printk(KERN_INFO "No Bluetooth Device found!\n");
++ ret = ENODEV; // no bluetooth
++ } else {
++ printk(KERN_INFO "Tosa Bluetooth Device found on ttyS3!\n");
++ }
++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_PWR_EN);
++
++ tosa_bluetooth_power(1); // Power on
++ return ret;
++}
++
++static int tosa_bluetooth_remove(struct platform_device *dev)
++{
++ tosa_bluetooth_power(0); // Power off
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int tosa_bluetooth_suspend(struct platform_device *dev, pm_message_t state)
++{
++ tosa_bluetooth_power(0); // Power off
++ return 0;
++}
++
++static int tosa_bluetooth_resume(struct platform_device *dev)
++{
++ tosa_bluetooth_power(1); // Power on
++ return 0;
++}
++#else
++#define tosa_bluetooth_suspend NULL
++#define tosa_bluetooth_resume NULL
++#endif
++
++static struct platform_driver tosa_bluetooth_driver = {
++ .probe = tosa_bluetooth_probe,
++ .remove = tosa_bluetooth_remove,
++ .suspend = tosa_bluetooth_suspend,
++ .resume = tosa_bluetooth_resume,
++ .driver = {
++ .name = "tosa-bluetooth",
++ },
++};
++
++int __init tosa_bluetooth_init(void)
++{
++ return platform_driver_register(&tosa_bluetooth_driver);
++}
++
++void __exit tosa_bluetooth_cleanup(void)
++{
++ platform_driver_unregister(&tosa_bluetooth_driver);
++}
++
++module_init(tosa_bluetooth_init);
++module_exit(tosa_bluetooth_cleanup);
+Index: linux-2.6.17/arch/arm/mach-pxa/tosa.c
+===================================================================
+--- linux-2.6.17.orig/arch/arm/mach-pxa/tosa.c 2006-06-20 11:45:51.254467640 +0200
++++ linux-2.6.17/arch/arm/mach-pxa/tosa.c 2006-06-20 11:46:08.112904768 +0200
+@@ -288,7 +288,7 @@
+
+ static void tosa_tc6393_enable(struct device *dev)
+ {
+-
++ printk("!!tosa_tc6393_enable!!\n");
+ reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON);
+ reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC6393_SUSPEND);
+ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_TC6393_REST_IN); //#PCLR
+@@ -303,7 +303,7 @@
+
+ static void tosa_tc6393_disable(struct device *dev)
+ {
+-
++ printk("!!tosa_tc6393_disable!!\n");
+ reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON);
+ reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC6393_SUSPEND);
+ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_TC6393_REST_IN); //#PCLR
+@@ -428,6 +428,17 @@
+ },
+ };
+
++/*
++ * Tosa Blueooth
++ */
++static struct platform_device tosa_bluetooth_device = {
++ .name = "tosa-bluetooth",
++ .id = -1,
++ .dev = {
++ .parent = &tosascoop_jc_device.dev,
++ },
++};
++
+ static struct platform_device *devices[] __initdata = {
+ &tosascoop_device,
+ &tosascoop_jc_device,
+@@ -435,6 +446,7 @@
+ &tosaled_device,
+ &tc6393_device,
+ &tosalcd_device,
++ &tosa_bluetooth_device,
+ };
+
+ static void tosa_poweroff(void)
+Index: linux-2.6.17/arch/arm/mach-pxa/Makefile
+===================================================================
+--- linux-2.6.17.orig/arch/arm/mach-pxa/Makefile 2006-06-20 11:45:51.252467944 +0200
++++ linux-2.6.17/arch/arm/mach-pxa/Makefile 2006-06-20 11:46:33.619027248 +0200
+@@ -16,7 +16,7 @@
+ obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o
+ obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o
+ obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o
+-obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o tosa_lcd.o
++obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o tosa_lcd.o tosa_bt.o
+ obj-$(CONFIG_MACH_EM_X270) += em-x270.o
+ obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o
+
+Index: linux-2.6.17/arch/arm/mach-pxa/tosa_bt.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/arch/arm/mach-pxa/tosa_bt.c 2006-06-20 11:46:08.107905528 +0200
+@@ -0,0 +1,128 @@
++/*
++ * Bluetooth control code for Sharp SL-6000x (tosa)
++ *
++ * Copyright (c) 2005 Dirk Opfer
++ *
++ * 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.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <asm/hardware.h>
++
++#include <asm/hardware/scoop.h>
++#include <asm/arch/tosa.h>
++#include <asm/arch/pxa-regs.h>
++
++
++static int tosa_bluetooth_power(int on)
++{
++
++ if (!on) { //off
++
++ set_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET);
++ pxa_gpio_mode(GPIO42_BTRXD|GPIO_IN);
++ pxa_gpio_mode(GPIO43_BTTXD|GPIO_IN);
++ pxa_gpio_mode(GPIO44_BTCTS|GPIO_IN);
++ pxa_gpio_mode(GPIO45_BTRTS|GPIO_IN);
++ mdelay(10); // wait 10ms
++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET);
++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_PWR_EN);
++ reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_BT_LED); // turn off BT LED
++
++ } else { // on
++
++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET);
++ set_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_PWR_EN);
++ pxa_gpio_mode(GPIO42_HWRXD_MD);
++ pxa_gpio_mode(GPIO43_HWTXD_MD);
++ pxa_gpio_mode(GPIO44_HWCTS_MD);
++ pxa_gpio_mode(GPIO45_HWRTS_MD);
++
++ set_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET);
++ mdelay(20); // wait 20ms
++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET);
++ set_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_BT_LED); // turn BT LED on
++ }
++ return 0;
++}
++
++/*
++ * Support Routines
++ */
++int __init tosa_bluetooth_probe(struct platform_device *dev)
++{
++ int ret = 0;
++ pxa_gpio_mode(GPIO42_BTRXD|GPIO_IN);
++ pxa_gpio_mode(GPIO43_BTTXD|GPIO_IN);
++ pxa_gpio_mode(GPIO44_BTCTS|GPIO_IN);
++ pxa_gpio_mode(GPIO45_BTRTS|GPIO_IN);
++ set_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_PWR_EN);
++ mdelay(5);
++
++ if ( (GPLR(GPIO42_BTRXD) & GPIO_bit(GPIO42_BTRXD))==0 &&
++ (GPLR(GPIO44_BTCTS) & GPIO_bit(GPIO44_BTCTS))==0) {
++ printk(KERN_INFO "No Bluetooth Device found!\n");
++ ret = ENODEV; // no bluetooth
++ } else {
++ printk(KERN_INFO "Tosa Bluetooth Device found on ttyS3!\n");
++ }
++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_PWR_EN);
++
++ tosa_bluetooth_power(1); // Power on
++ return ret;
++}
++
++static int tosa_bluetooth_remove(struct platform_device *dev)
++{
++ tosa_bluetooth_power(0); // Power off
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int tosa_bluetooth_suspend(struct platform_device *dev, pm_message_t state)
++{
++ tosa_bluetooth_power(0); // Power off
++ return 0;
++}
++
++static int tosa_bluetooth_resume(struct platform_device *dev)
++{
++ tosa_bluetooth_power(1); // Power on
++ return 0;
++}
++#else
++#define tosa_bluetooth_suspend NULL
++#define tosa_bluetooth_resume NULL
++#endif
++
++static struct platform_driver tosa_bluetooth_driver = {
++ .probe = tosa_bluetooth_probe,
++ .remove = tosa_bluetooth_remove,
++ .suspend = tosa_bluetooth_suspend,
++ .resume = tosa_bluetooth_resume,
++ .driver = {
++ .name = "tosa-bluetooth",
++ },
++};
++
++int __init tosa_bluetooth_init(void)
++{
++ return platform_driver_register(&tosa_bluetooth_driver);
++}
++
++void __exit tosa_bluetooth_cleanup(void)
++{
++ platform_driver_unregister(&tosa_bluetooth_driver);
++}
++
++module_init(tosa_bluetooth_init);
++module_exit(tosa_bluetooth_cleanup);
+Index: linux-2.6.17/arch/arm/mach-pxa/tosa.c
+===================================================================
+--- linux-2.6.17.orig/arch/arm/mach-pxa/tosa.c 2006-06-20 11:45:51.254467640 +0200
++++ linux-2.6.17/arch/arm/mach-pxa/tosa.c 2006-06-20 11:46:08.112904768 +0200
+@@ -288,7 +288,7 @@
+
+ static void tosa_tc6393_enable(struct device *dev)
+ {
+-
++ printk("!!tosa_tc6393_enable!!\n");
+ reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON);
+ reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC6393_SUSPEND);
+ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_TC6393_REST_IN); //#PCLR
+@@ -303,7 +303,7 @@
+
+ static void tosa_tc6393_disable(struct device *dev)
+ {
+-
++ printk("!!tosa_tc6393_disable!!\n");
+ reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON);
+ reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC6393_SUSPEND);
+ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_TC6393_REST_IN); //#PCLR
+@@ -428,6 +428,17 @@
+ },
+ };
+
++/*
++ * Tosa Blueooth
++ */
++static struct platform_device tosa_bluetooth_device = {
++ .name = "tosa-bluetooth",
++ .id = -1,
++ .dev = {
++ .parent = &tosascoop_jc_device.dev,
++ },
++};
++
+ static struct platform_device *devices[] __initdata = {
+ &tosascoop_device,
+ &tosascoop_jc_device,
+@@ -435,6 +446,7 @@
+ &tosaled_device,
+ &tc6393_device,
+ &tosalcd_device,
++ &tosa_bluetooth_device,
+ };
+
+ static void tosa_poweroff(void)
diff --git a/packages/linux/linux-rp-2.6.23/tosa-keyboard-r19.patch b/packages/linux/linux-rp-2.6.23/tosa-keyboard-r19.patch
new file mode 100644
index 0000000000..5d94f25527
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/tosa-keyboard-r19.patch
@@ -0,0 +1,1028 @@
+ drivers/input/keyboard/Kconfig | 12 -
+ drivers/input/keyboard/Makefile | 1
+ drivers/input/keyboard/tosakbd.c | 467 +++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 479 insertions(+), 1 deletion(-)
+
+Index: git/drivers/input/keyboard/Kconfig
+===================================================================
+--- git.orig/drivers/input/keyboard/Kconfig 2006-10-31 16:08:57.000000000 +0000
++++ git/drivers/input/keyboard/Kconfig 2006-11-07 22:13:10.000000000 +0000
+@@ -148,12 +148,22 @@ config KEYBOARD_SPITZ
+ depends on PXA_SHARPSL
+ default y
+ help
+- Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000,
++ Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000,
+ SL-C3000 and Sl-C3100 series of PDAs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called spitzkbd.
+
++config KEYBOARD_TOSA
++ tristate "Tosa keyboard"
++ depends on PXA_SHARPSL
++ default y
++ help
++ Say Y here to enable the keyboard on the Sharp Zaurus SL-6000x (Tosa)
++
++ To compile this driver as a module, choose M here: the
++ module will be called tosakbd.
++
+ config KEYBOARD_AMIGA
+ tristate "Amiga keyboard"
+ depends on AMIGA
+Index: git/drivers/input/keyboard/Makefile
+===================================================================
+--- git.orig/drivers/input/keyboard/Makefile 2006-10-31 16:08:57.000000000 +0000
++++ git/drivers/input/keyboard/Makefile 2006-11-07 22:13:10.000000000 +0000
+@@ -17,3 +17,4 @@ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkb
+ obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
+ obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
+ obj-$(CONFIG_KEYBOARD_ASIC3) += asic3_keys.o
++obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o
+Index: git/drivers/input/keyboard/tosakbd.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ git/drivers/input/keyboard/tosakbd.c 2006-11-07 23:27:19.000000000 +0000
+@@ -0,0 +1,467 @@
++/*
++ * Keyboard driver for Sharp Tosa models (SL-6000x)
++ *
++ * Copyright (c) 2005 Dirk Opfer
++ *
++ * Based on xtkbd.c/locomkbd.c/corgikbd.c
++ *
++ * 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.
++ *
++ */
++
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/interrupt.h>
++#include <linux/jiffies.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++
++#include <asm/arch/tosa.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch/pxa-regs.h>
++
++
++#define TOSA_KEY_STROBE_NUM (11)
++#define TOSA_KEY_SENSE_NUM (7)
++
++#define KEYMASK_ON (0x1<<0)
++#define KEYMASK_REC (0x1<<1)
++#define KEYMASK_SYNC (0x1<<2)
++
++#define KB_ROWS 7
++#define KB_COLS 11
++#define KB_ROWMASK(r) (1 << (r))
++#define SCANCODE(r,c) ( ((r)<<4) + (c) + 1 )
++#define NR_SCANCODES (SCANCODE(KB_ROWS-1,KB_COLS)+1+1)
++
++#define SCAN_INTERVAL (HZ/10)
++#define HP_SCAN_INTERVAL (150) /* ms */
++#define HP_STABLE_COUNT 2
++
++#define TOSA_KEY_CALENDER KEY_F1
++#define TOSA_KEY_ADDRESS KEY_F2
++#define TOSA_KEY_FN KEY_F3
++#define TOSA_KEY_CANCEL KEY_F4
++#define TOSA_KEY_OFF KEY_SUSPEND
++#define TOSA_KEY_CENTER KEY_F5
++#define TOSA_KEY_REC KEY_F6
++#define TOSA_KEY_LIGHT KEY_F7
++#define TOSA_KEY_RECORD KEY_F8
++#define TOSA_KEY_HOME KEY_F9
++#define TOSA_KEY_MAIL KEY_F10
++#define TOSA_KEY_OK KEY_F11
++#define TOSA_KEY_MENU KEY_F12
++#define TOSA_KEY_SYNC KEY_F13
++
++#define GET_ROWS_STATUS(c) ((GPLR2 & TOSA_GPIO_ALL_SENSE_BIT) >> TOSA_GPIO_ALL_SENSE_RSHIFT)
++#define KB_DISCHARGE_DELAY 10
++#define KB_ACTIVATE_DELAY 10
++
++
++static unsigned char tosakbd_keycode[NR_SCANCODES] = {
++ 0, /* 0 */
++ 0, KEY_W, 0, 0, 0, KEY_K, KEY_BACKSPACE, KEY_P, 0, 0, 0, TOSA_KEY_OFF, 0, 0, 0, 0, /*1 - 16*/
++ KEY_Q, KEY_E, KEY_T, KEY_Y, 0, KEY_O, KEY_I, KEY_COMMA, 0, 0, 0, TOSA_KEY_RECORD, 0, 0, 0, 0, /*17 - 32*/
++ KEY_A, KEY_D, KEY_G, KEY_U, 0, KEY_L, KEY_ENTER, KEY_DOT, 0, 0, 0, TOSA_KEY_SYNC, 0, 0, 0, 0, /*33 - 48*/
++ KEY_Z, KEY_C, KEY_V, KEY_J, TOSA_KEY_ADDRESS, TOSA_KEY_CANCEL, TOSA_KEY_CENTER, TOSA_KEY_OK, KEY_LEFTSHIFT, 0 , 0,0 , 0, 0, 0, 0, /*49 - 64*/
++ KEY_S, KEY_R, KEY_B, KEY_N, TOSA_KEY_CALENDER, TOSA_KEY_HOME, TOSA_KEY_REC, TOSA_KEY_LIGHT, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /*65 - 80*/
++ KEY_TAB, KEY_SLASH, KEY_H, KEY_M, TOSA_KEY_MENU, 0, KEY_UP, 0, 0, 0, TOSA_KEY_FN, 0, 0, 0, 0, 0, /*81 - 96*/
++ KEY_X, KEY_F, KEY_SPACE, KEY_APOSTROPHE, TOSA_KEY_MAIL, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, /*97 - 109*/
++};
++
++struct tosakbd {
++ unsigned char keycode[ARRAY_SIZE(tosakbd_keycode)];
++ struct input_dev *input;
++
++ spinlock_t lock;
++ struct timer_list timer;
++ struct timer_list hptimer;
++
++ int hp_state;
++ int hp_count;
++
++ unsigned int suspended;
++ unsigned long suspend_jiffies;
++};
++
++/* Helper functions for reading the keyboard matrix
++ * Note: We should really be using pxa_gpio_mode to alter GPDR but it
++ * requires a function call per GPIO bit which is excessive
++ * when we need to access 12 bits at once, multiple times.
++ * These functions must be called within local_irq_save()/local_irq_restore()
++ * or similar.
++ */
++static inline void tosakbd_discharge_all(void)
++{
++ /* STROBE All HiZ */
++ GPCR1 = TOSA_GPIO_HIGH_STROBE_BIT;
++ GPDR1 &= ~TOSA_GPIO_HIGH_STROBE_BIT;
++ GPCR2 = TOSA_GPIO_LOW_STROBE_BIT;
++ GPDR2 &= ~TOSA_GPIO_LOW_STROBE_BIT;
++}
++
++static inline void tosakbd_activate_all(void)
++{
++ /* STROBE ALL -> High */
++ GPSR1 = TOSA_GPIO_HIGH_STROBE_BIT;
++ GPDR1 |= TOSA_GPIO_HIGH_STROBE_BIT;
++ GPSR2 = TOSA_GPIO_LOW_STROBE_BIT;
++ GPDR2 |= TOSA_GPIO_LOW_STROBE_BIT;
++
++ udelay(KB_DISCHARGE_DELAY);
++
++ /* STATE CLEAR */
++ GEDR2 |= TOSA_GPIO_ALL_SENSE_BIT;
++}
++
++static inline void tosakbd_activate_col(int col)
++{
++ if (col<=5) {
++ /* STROBE col -> High, not col -> HiZ */
++ GPSR1 = TOSA_GPIO_STROBE_BIT(col);
++ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
++ } else {
++ /* STROBE col -> High, not col -> HiZ */
++ GPSR2 = TOSA_GPIO_STROBE_BIT(col);
++ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
++ }
++}
++
++static inline void tosakbd_reset_col(int col)
++{
++ if (col<=5) {
++ /* STROBE col -> Low */
++ GPCR1 = TOSA_GPIO_STROBE_BIT(col);
++ /* STROBE col -> out, not col -> HiZ */
++ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
++ } else {
++ /* STROBE col -> Low */
++ GPCR2 = TOSA_GPIO_STROBE_BIT(col);
++ /* STROBE col -> out, not col -> HiZ */
++ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
++ }
++}
++
++/*
++ * Read the GPIOs for POWER, RECORD and SYNC
++ */
++static int read_port_key_status_raw(void)
++{
++ int val=0;
++
++ /* Power key */
++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_ON_KEY))==0)
++ val |= KEYMASK_ON;
++ /* Record key */
++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_RECORD_BTN))==0)
++ val |= KEYMASK_REC;
++ /* Sync key */
++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_SYNC))==0)
++ val |= KEYMASK_SYNC;
++ return val;
++}
++
++
++/*
++ * The tosa keyboard only generates interrupts when a key is pressed.
++ * So when a key is pressed, we enable a timer. This timer scans the
++ * keyboard, and this is how we detect when the key is released.
++ */
++
++/* Scan the hardware keyboard and push any changes up through the input layer */
++static void tosakbd_scankeyboard(struct tosakbd *tosakbd_data)
++{
++ unsigned int row, col, rowd;
++ unsigned long flags;
++ unsigned int num_pressed = 0;
++
++ if (tosakbd_data->suspended)
++ return;
++
++ spin_lock_irqsave(&tosakbd_data->lock, flags);
++
++ for (col = 0; col < KB_COLS; col++) {
++ /*
++ * Discharge the output driver capacitatance
++ * in the keyboard matrix. (Yes it is significant..)
++ */
++ tosakbd_discharge_all();
++ udelay(KB_DISCHARGE_DELAY);
++
++ tosakbd_activate_col( col);
++ udelay(KB_ACTIVATE_DELAY);
++
++ rowd = GET_ROWS_STATUS(col);
++
++ for (row = 0; row < KB_ROWS; row++) {
++ unsigned int scancode, pressed;
++ scancode = SCANCODE(row, col);
++ pressed = rowd & KB_ROWMASK(row);
++ input_report_key(tosakbd_data->input, tosakbd_data->keycode[scancode], pressed);
++ if (pressed)
++ num_pressed++;
++ }
++
++ tosakbd_reset_col(col);
++ }
++
++ tosakbd_activate_all();
++
++ rowd = read_port_key_status_raw();
++
++ for (row = 0; row < 3; row++ ) {
++ unsigned int scancode, pressed;
++ scancode = SCANCODE(row, KB_COLS);
++ pressed = rowd & KB_ROWMASK(row);
++ input_report_key(tosakbd_data->input, tosakbd_data->keycode[scancode], pressed);
++ if (pressed)
++ num_pressed++;
++
++ if (pressed && (tosakbd_data->keycode[scancode] == TOSA_KEY_OFF)
++ && time_after(jiffies, tosakbd_data->suspend_jiffies + msecs_to_jiffies(1000))) {
++ input_event(tosakbd_data->input, EV_PWR, TOSA_KEY_OFF, 1);
++ tosakbd_data->suspend_jiffies = jiffies;
++ }
++ }
++
++ input_sync(tosakbd_data->input);
++
++ /* if any keys are pressed, enable the timer */
++ if (num_pressed)
++ mod_timer(&tosakbd_data->timer, jiffies + SCAN_INTERVAL);
++
++ spin_unlock_irqrestore(&tosakbd_data->lock, flags);
++}
++
++/*
++ * tosa keyboard interrupt handler.
++ */
++static irqreturn_t tosakbd_interrupt(int irq, void *dev_id)
++{
++ struct tosakbd *tosakbd_data = dev_id;
++
++ if (!timer_pending(&tosakbd_data->timer))
++ {
++ /** wait chattering delay **/
++ udelay(20);
++ tosakbd_scankeyboard(tosakbd_data);
++ }
++
++ return IRQ_HANDLED;
++}
++
++/*
++ * tosa timer checking for released keys
++ */
++static void tosakbd_timer_callback(unsigned long data)
++{
++ struct tosakbd *tosakbd_data = (struct tosakbd *) data;
++ tosakbd_scankeyboard(tosakbd_data);
++}
++
++/*
++ * The headphone generates an interrupt.
++ * We debounce the switche and pass them to the input system.
++ */
++
++static irqreturn_t tosakbd_hp_isr(int irq, void *dev_id)
++{
++ struct tosakbd *tosakbd_data = dev_id;
++
++ if (!timer_pending(&tosakbd_data->hptimer))
++ mod_timer(&tosakbd_data->hptimer, jiffies + msecs_to_jiffies(HP_SCAN_INTERVAL));
++
++ return IRQ_HANDLED;
++}
++
++static void tosakbd_hp_timer(unsigned long data)
++{
++ struct tosakbd *tosakbd_data = (struct tosakbd *) data;
++ unsigned long state;
++ unsigned long flags;
++
++ state = (GPLR(TOSA_GPIO_EAR_IN) & GPIO_bit(TOSA_GPIO_EAR_IN));
++ if (state != tosakbd_data->hp_state) {
++ tosakbd_data->hp_count = 0;
++ tosakbd_data->hp_state = state;
++ } else if (tosakbd_data->hp_count < HP_STABLE_COUNT) {
++ tosakbd_data->hp_count++;
++ }
++
++ if (tosakbd_data->hp_count >= HP_STABLE_COUNT) {
++ spin_lock_irqsave(&tosakbd_data->lock, flags);
++
++ input_report_switch(tosakbd_data->input, SW_HEADPHONE_INSERT, ((GPLR(TOSA_GPIO_EAR_IN) & GPIO_bit(TOSA_GPIO_EAR_IN)) == 0));
++ input_sync(tosakbd_data->input);
++
++ spin_unlock_irqrestore(&tosakbd_data->lock, flags);
++ } else {
++ mod_timer(&tosakbd_data->hptimer, jiffies + msecs_to_jiffies(HP_SCAN_INTERVAL));
++ }
++}
++
++#ifdef CONFIG_PM
++static int tosakbd_suspend(struct platform_device *dev, pm_message_t state)
++{
++ struct tosakbd *tosakbd = platform_get_drvdata(dev);
++
++ tosakbd->suspended = 1;
++
++ return 0;
++}
++
++static int tosakbd_resume(struct platform_device *dev)
++{
++ struct tosakbd *tosakbd = platform_get_drvdata(dev);
++
++ /* Upon resume, ignore the suspend key for a short while */
++ tosakbd->suspend_jiffies = jiffies;
++ tosakbd->suspended = 0;
++
++ return 0;
++}
++#else
++#define tosakbd_suspend NULL
++#define tosakbd_resume NULL
++#endif
++
++static int __init tosakbd_probe(struct platform_device *pdev) {
++
++ int i;
++ struct tosakbd *tosakbd;
++ struct input_dev *input_dev;
++
++ tosakbd = kzalloc(sizeof(struct tosakbd), GFP_KERNEL);
++ if (!tosakbd)
++ return -ENOMEM;
++
++ input_dev = input_allocate_device();
++ if (!input_dev) {
++ kfree(tosakbd);
++ return -ENOMEM;
++ }
++
++ platform_set_drvdata(pdev,tosakbd);
++
++ spin_lock_init(&tosakbd->lock);
++
++ /* Init Keyboard rescan timer */
++ init_timer(&tosakbd->timer);
++ tosakbd->timer.function = tosakbd_timer_callback;
++ tosakbd->timer.data = (unsigned long) tosakbd;
++
++ /* Init Headphone Timer */
++ init_timer(&tosakbd->hptimer);
++ tosakbd->hptimer.function = tosakbd_hp_timer;
++ tosakbd->hptimer.data = (unsigned long) tosakbd;
++
++ tosakbd->suspend_jiffies = jiffies;
++
++ tosakbd->input = input_dev;
++
++ input_dev->private = tosakbd;
++ input_dev->name = "Tosa Keyboard";
++ input_dev->phys = "tosakbd/input0";
++ input_dev->cdev.dev = &pdev->dev;
++
++ input_dev->id.bustype = BUS_HOST;
++ input_dev->id.vendor = 0x0001;
++ input_dev->id.product = 0x0001;
++ input_dev->id.version = 0x0100;
++
++ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_PWR) | BIT(EV_SW);
++ input_dev->keycode = tosakbd->keycode;
++ input_dev->keycodesize = sizeof(unsigned char);
++ input_dev->keycodemax = ARRAY_SIZE(tosakbd_keycode);
++
++ memcpy(tosakbd->keycode, tosakbd_keycode, sizeof(tosakbd->keycode));
++ for (i = 0; i < ARRAY_SIZE(tosakbd_keycode); i++)
++ set_bit(tosakbd->keycode[i], input_dev->keybit);
++ clear_bit(0, input_dev->keybit);
++ set_bit(SW_HEADPHONE_INSERT, input_dev->swbit);
++
++ input_register_device(tosakbd->input);
++
++ /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
++ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) {
++ pxa_gpio_mode( TOSA_GPIO_KEY_SENSE(i) | GPIO_IN);
++ if (request_irq(TOSA_IRQ_GPIO_KEY_SENSE(i), tosakbd_interrupt,
++ IRQF_DISABLED | IRQF_TRIGGER_RISING, "tosakbd", tosakbd)) {
++ printk("tosakbd: Can't get IRQ: %d !\n", i);
++ }
++ }
++
++ /* Set Strobe lines as outputs - set high */
++ for (i = 0; i < TOSA_KEY_STROBE_NUM; i++) {
++ pxa_gpio_mode( TOSA_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
++ }
++
++ // Power&Rec Button
++ pxa_gpio_mode( TOSA_GPIO_ON_KEY | GPIO_IN);
++ pxa_gpio_mode( TOSA_GPIO_RECORD_BTN | GPIO_IN);
++ pxa_gpio_mode( TOSA_GPIO_SYNC | GPIO_IN);
++ pxa_gpio_mode( TOSA_GPIO_EAR_IN | GPIO_IN);
++
++ if (request_irq(TOSA_IRQ_GPIO_ON_KEY, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "On key", tosakbd) ||
++ request_irq(TOSA_IRQ_GPIO_RECORD_BTN, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "Record key", tosakbd) ||
++ request_irq(TOSA_IRQ_GPIO_SYNC, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "Sync key", tosakbd) ||
++ request_irq(TOSA_IRQ_GPIO_EAR_IN, tosakbd_hp_isr, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "HP in", tosakbd)) {
++ printk("Could not allocate KEYBD IRQ!\n");
++ }
++
++ printk(KERN_INFO "input: Tosa Keyboard Registered\n");
++
++ return 0;
++}
++
++static int tosakbd_remove(struct platform_device *dev) {
++
++ int i;
++ struct tosakbd *tosakbd = platform_get_drvdata(dev);
++
++ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++)
++ free_irq(TOSA_IRQ_GPIO_KEY_SENSE(i),tosakbd);
++
++ free_irq(TOSA_IRQ_GPIO_ON_KEY,tosakbd);
++ free_irq(TOSA_IRQ_GPIO_RECORD_BTN,tosakbd);
++ free_irq(TOSA_IRQ_GPIO_SYNC,tosakbd);
++
++ del_timer_sync(&tosakbd->timer);
++
++ input_unregister_device(tosakbd->input);
++
++ kfree(tosakbd);
++
++ return 0;
++}
++
++static struct platform_driver tosakbd_driver = {
++ .probe = tosakbd_probe,
++ .remove = tosakbd_remove,
++ .suspend = tosakbd_suspend,
++ .resume = tosakbd_resume,
++ .driver = {
++ .name = "tosa-keyboard",
++ },
++};
++
++static int __devinit tosakbd_init(void)
++{
++ return platform_driver_register(&tosakbd_driver);
++}
++
++static void __exit tosakbd_exit(void)
++{
++ platform_driver_unregister(&tosakbd_driver);
++}
++
++module_init(tosakbd_init);
++module_exit(tosakbd_exit);
++
++MODULE_AUTHOR("Dirk Opfer <Dirk@Opfer-Online.de>");
++MODULE_DESCRIPTION("Tosa Keyboard Driver");
++MODULE_LICENSE("GPLv2");
+ drivers/input/keyboard/Kconfig | 12 -
+ drivers/input/keyboard/Makefile | 1
+ drivers/input/keyboard/tosakbd.c | 467 +++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 479 insertions(+), 1 deletion(-)
+
+Index: git/drivers/input/keyboard/Kconfig
+===================================================================
+--- git.orig/drivers/input/keyboard/Kconfig 2006-10-31 16:08:57.000000000 +0000
++++ git/drivers/input/keyboard/Kconfig 2006-11-07 22:13:10.000000000 +0000
+@@ -148,12 +148,22 @@ config KEYBOARD_SPITZ
+ depends on PXA_SHARPSL
+ default y
+ help
+- Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000,
++ Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000,
+ SL-C3000 and Sl-C3100 series of PDAs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called spitzkbd.
+
++config KEYBOARD_TOSA
++ tristate "Tosa keyboard"
++ depends on PXA_SHARPSL
++ default y
++ help
++ Say Y here to enable the keyboard on the Sharp Zaurus SL-6000x (Tosa)
++
++ To compile this driver as a module, choose M here: the
++ module will be called tosakbd.
++
+ config KEYBOARD_AMIGA
+ tristate "Amiga keyboard"
+ depends on AMIGA
+Index: git/drivers/input/keyboard/Makefile
+===================================================================
+--- git.orig/drivers/input/keyboard/Makefile 2006-10-31 16:08:57.000000000 +0000
++++ git/drivers/input/keyboard/Makefile 2006-11-07 22:13:10.000000000 +0000
+@@ -17,3 +17,4 @@ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkb
+ obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
+ obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
+ obj-$(CONFIG_KEYBOARD_ASIC3) += asic3_keys.o
++obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o
+Index: git/drivers/input/keyboard/tosakbd.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ git/drivers/input/keyboard/tosakbd.c 2006-11-07 23:27:19.000000000 +0000
+@@ -0,0 +1,467 @@
++/*
++ * Keyboard driver for Sharp Tosa models (SL-6000x)
++ *
++ * Copyright (c) 2005 Dirk Opfer
++ *
++ * Based on xtkbd.c/locomkbd.c/corgikbd.c
++ *
++ * 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.
++ *
++ */
++
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/interrupt.h>
++#include <linux/jiffies.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++
++#include <asm/arch/tosa.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch/pxa-regs.h>
++
++
++#define TOSA_KEY_STROBE_NUM (11)
++#define TOSA_KEY_SENSE_NUM (7)
++
++#define KEYMASK_ON (0x1<<0)
++#define KEYMASK_REC (0x1<<1)
++#define KEYMASK_SYNC (0x1<<2)
++
++#define KB_ROWS 7
++#define KB_COLS 11
++#define KB_ROWMASK(r) (1 << (r))
++#define SCANCODE(r,c) ( ((r)<<4) + (c) + 1 )
++#define NR_SCANCODES (SCANCODE(KB_ROWS-1,KB_COLS)+1+1)
++
++#define SCAN_INTERVAL (HZ/10)
++#define HP_SCAN_INTERVAL (150) /* ms */
++#define HP_STABLE_COUNT 2
++
++#define TOSA_KEY_CALENDER KEY_F1
++#define TOSA_KEY_ADDRESS KEY_F2
++#define TOSA_KEY_FN KEY_F3
++#define TOSA_KEY_CANCEL KEY_F4
++#define TOSA_KEY_OFF KEY_SUSPEND
++#define TOSA_KEY_CENTER KEY_F5
++#define TOSA_KEY_REC KEY_F6
++#define TOSA_KEY_LIGHT KEY_F7
++#define TOSA_KEY_RECORD KEY_F8
++#define TOSA_KEY_HOME KEY_F9
++#define TOSA_KEY_MAIL KEY_F10
++#define TOSA_KEY_OK KEY_F11
++#define TOSA_KEY_MENU KEY_F12
++#define TOSA_KEY_SYNC KEY_F13
++
++#define GET_ROWS_STATUS(c) ((GPLR2 & TOSA_GPIO_ALL_SENSE_BIT) >> TOSA_GPIO_ALL_SENSE_RSHIFT)
++#define KB_DISCHARGE_DELAY 10
++#define KB_ACTIVATE_DELAY 10
++
++
++static unsigned char tosakbd_keycode[NR_SCANCODES] = {
++ 0, /* 0 */
++ 0, KEY_W, 0, 0, 0, KEY_K, KEY_BACKSPACE, KEY_P, 0, 0, 0, TOSA_KEY_OFF, 0, 0, 0, 0, /*1 - 16*/
++ KEY_Q, KEY_E, KEY_T, KEY_Y, 0, KEY_O, KEY_I, KEY_COMMA, 0, 0, 0, TOSA_KEY_RECORD, 0, 0, 0, 0, /*17 - 32*/
++ KEY_A, KEY_D, KEY_G, KEY_U, 0, KEY_L, KEY_ENTER, KEY_DOT, 0, 0, 0, TOSA_KEY_SYNC, 0, 0, 0, 0, /*33 - 48*/
++ KEY_Z, KEY_C, KEY_V, KEY_J, TOSA_KEY_ADDRESS, TOSA_KEY_CANCEL, TOSA_KEY_CENTER, TOSA_KEY_OK, KEY_LEFTSHIFT, 0 , 0,0 , 0, 0, 0, 0, /*49 - 64*/
++ KEY_S, KEY_R, KEY_B, KEY_N, TOSA_KEY_CALENDER, TOSA_KEY_HOME, TOSA_KEY_REC, TOSA_KEY_LIGHT, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /*65 - 80*/
++ KEY_TAB, KEY_SLASH, KEY_H, KEY_M, TOSA_KEY_MENU, 0, KEY_UP, 0, 0, 0, TOSA_KEY_FN, 0, 0, 0, 0, 0, /*81 - 96*/
++ KEY_X, KEY_F, KEY_SPACE, KEY_APOSTROPHE, TOSA_KEY_MAIL, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, /*97 - 109*/
++};
++
++struct tosakbd {
++ unsigned char keycode[ARRAY_SIZE(tosakbd_keycode)];
++ struct input_dev *input;
++
++ spinlock_t lock;
++ struct timer_list timer;
++ struct timer_list hptimer;
++
++ int hp_state;
++ int hp_count;
++
++ unsigned int suspended;
++ unsigned long suspend_jiffies;
++};
++
++/* Helper functions for reading the keyboard matrix
++ * Note: We should really be using pxa_gpio_mode to alter GPDR but it
++ * requires a function call per GPIO bit which is excessive
++ * when we need to access 12 bits at once, multiple times.
++ * These functions must be called within local_irq_save()/local_irq_restore()
++ * or similar.
++ */
++static inline void tosakbd_discharge_all(void)
++{
++ /* STROBE All HiZ */
++ GPCR1 = TOSA_GPIO_HIGH_STROBE_BIT;
++ GPDR1 &= ~TOSA_GPIO_HIGH_STROBE_BIT;
++ GPCR2 = TOSA_GPIO_LOW_STROBE_BIT;
++ GPDR2 &= ~TOSA_GPIO_LOW_STROBE_BIT;
++}
++
++static inline void tosakbd_activate_all(void)
++{
++ /* STROBE ALL -> High */
++ GPSR1 = TOSA_GPIO_HIGH_STROBE_BIT;
++ GPDR1 |= TOSA_GPIO_HIGH_STROBE_BIT;
++ GPSR2 = TOSA_GPIO_LOW_STROBE_BIT;
++ GPDR2 |= TOSA_GPIO_LOW_STROBE_BIT;
++
++ udelay(KB_DISCHARGE_DELAY);
++
++ /* STATE CLEAR */
++ GEDR2 |= TOSA_GPIO_ALL_SENSE_BIT;
++}
++
++static inline void tosakbd_activate_col(int col)
++{
++ if (col<=5) {
++ /* STROBE col -> High, not col -> HiZ */
++ GPSR1 = TOSA_GPIO_STROBE_BIT(col);
++ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
++ } else {
++ /* STROBE col -> High, not col -> HiZ */
++ GPSR2 = TOSA_GPIO_STROBE_BIT(col);
++ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
++ }
++}
++
++static inline void tosakbd_reset_col(int col)
++{
++ if (col<=5) {
++ /* STROBE col -> Low */
++ GPCR1 = TOSA_GPIO_STROBE_BIT(col);
++ /* STROBE col -> out, not col -> HiZ */
++ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
++ } else {
++ /* STROBE col -> Low */
++ GPCR2 = TOSA_GPIO_STROBE_BIT(col);
++ /* STROBE col -> out, not col -> HiZ */
++ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
++ }
++}
++
++/*
++ * Read the GPIOs for POWER, RECORD and SYNC
++ */
++static int read_port_key_status_raw(void)
++{
++ int val=0;
++
++ /* Power key */
++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_ON_KEY))==0)
++ val |= KEYMASK_ON;
++ /* Record key */
++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_RECORD_BTN))==0)
++ val |= KEYMASK_REC;
++ /* Sync key */
++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_SYNC))==0)
++ val |= KEYMASK_SYNC;
++ return val;
++}
++
++
++/*
++ * The tosa keyboard only generates interrupts when a key is pressed.
++ * So when a key is pressed, we enable a timer. This timer scans the
++ * keyboard, and this is how we detect when the key is released.
++ */
++
++/* Scan the hardware keyboard and push any changes up through the input layer */
++static void tosakbd_scankeyboard(struct tosakbd *tosakbd_data)
++{
++ unsigned int row, col, rowd;
++ unsigned long flags;
++ unsigned int num_pressed = 0;
++
++ if (tosakbd_data->suspended)
++ return;
++
++ spin_lock_irqsave(&tosakbd_data->lock, flags);
++
++ for (col = 0; col < KB_COLS; col++) {
++ /*
++ * Discharge the output driver capacitatance
++ * in the keyboard matrix. (Yes it is significant..)
++ */
++ tosakbd_discharge_all();
++ udelay(KB_DISCHARGE_DELAY);
++
++ tosakbd_activate_col( col);
++ udelay(KB_ACTIVATE_DELAY);
++
++ rowd = GET_ROWS_STATUS(col);
++
++ for (row = 0; row < KB_ROWS; row++) {
++ unsigned int scancode, pressed;
++ scancode = SCANCODE(row, col);
++ pressed = rowd & KB_ROWMASK(row);
++ input_report_key(tosakbd_data->input, tosakbd_data->keycode[scancode], pressed);
++ if (pressed)
++ num_pressed++;
++ }
++
++ tosakbd_reset_col(col);
++ }
++
++ tosakbd_activate_all();
++
++ rowd = read_port_key_status_raw();
++
++ for (row = 0; row < 3; row++ ) {
++ unsigned int scancode, pressed;
++ scancode = SCANCODE(row, KB_COLS);
++ pressed = rowd & KB_ROWMASK(row);
++ input_report_key(tosakbd_data->input, tosakbd_data->keycode[scancode], pressed);
++ if (pressed)
++ num_pressed++;
++
++ if (pressed && (tosakbd_data->keycode[scancode] == TOSA_KEY_OFF)
++ && time_after(jiffies, tosakbd_data->suspend_jiffies + msecs_to_jiffies(1000))) {
++ input_event(tosakbd_data->input, EV_PWR, TOSA_KEY_OFF, 1);
++ tosakbd_data->suspend_jiffies = jiffies;
++ }
++ }
++
++ input_sync(tosakbd_data->input);
++
++ /* if any keys are pressed, enable the timer */
++ if (num_pressed)
++ mod_timer(&tosakbd_data->timer, jiffies + SCAN_INTERVAL);
++
++ spin_unlock_irqrestore(&tosakbd_data->lock, flags);
++}
++
++/*
++ * tosa keyboard interrupt handler.
++ */
++static irqreturn_t tosakbd_interrupt(int irq, void *dev_id)
++{
++ struct tosakbd *tosakbd_data = dev_id;
++
++ if (!timer_pending(&tosakbd_data->timer))
++ {
++ /** wait chattering delay **/
++ udelay(20);
++ tosakbd_scankeyboard(tosakbd_data);
++ }
++
++ return IRQ_HANDLED;
++}
++
++/*
++ * tosa timer checking for released keys
++ */
++static void tosakbd_timer_callback(unsigned long data)
++{
++ struct tosakbd *tosakbd_data = (struct tosakbd *) data;
++ tosakbd_scankeyboard(tosakbd_data);
++}
++
++/*
++ * The headphone generates an interrupt.
++ * We debounce the switche and pass them to the input system.
++ */
++
++static irqreturn_t tosakbd_hp_isr(int irq, void *dev_id)
++{
++ struct tosakbd *tosakbd_data = dev_id;
++
++ if (!timer_pending(&tosakbd_data->hptimer))
++ mod_timer(&tosakbd_data->hptimer, jiffies + msecs_to_jiffies(HP_SCAN_INTERVAL));
++
++ return IRQ_HANDLED;
++}
++
++static void tosakbd_hp_timer(unsigned long data)
++{
++ struct tosakbd *tosakbd_data = (struct tosakbd *) data;
++ unsigned long state;
++ unsigned long flags;
++
++ state = (GPLR(TOSA_GPIO_EAR_IN) & GPIO_bit(TOSA_GPIO_EAR_IN));
++ if (state != tosakbd_data->hp_state) {
++ tosakbd_data->hp_count = 0;
++ tosakbd_data->hp_state = state;
++ } else if (tosakbd_data->hp_count < HP_STABLE_COUNT) {
++ tosakbd_data->hp_count++;
++ }
++
++ if (tosakbd_data->hp_count >= HP_STABLE_COUNT) {
++ spin_lock_irqsave(&tosakbd_data->lock, flags);
++
++ input_report_switch(tosakbd_data->input, SW_HEADPHONE_INSERT, ((GPLR(TOSA_GPIO_EAR_IN) & GPIO_bit(TOSA_GPIO_EAR_IN)) == 0));
++ input_sync(tosakbd_data->input);
++
++ spin_unlock_irqrestore(&tosakbd_data->lock, flags);
++ } else {
++ mod_timer(&tosakbd_data->hptimer, jiffies + msecs_to_jiffies(HP_SCAN_INTERVAL));
++ }
++}
++
++#ifdef CONFIG_PM
++static int tosakbd_suspend(struct platform_device *dev, pm_message_t state)
++{
++ struct tosakbd *tosakbd = platform_get_drvdata(dev);
++
++ tosakbd->suspended = 1;
++
++ return 0;
++}
++
++static int tosakbd_resume(struct platform_device *dev)
++{
++ struct tosakbd *tosakbd = platform_get_drvdata(dev);
++
++ /* Upon resume, ignore the suspend key for a short while */
++ tosakbd->suspend_jiffies = jiffies;
++ tosakbd->suspended = 0;
++
++ return 0;
++}
++#else
++#define tosakbd_suspend NULL
++#define tosakbd_resume NULL
++#endif
++
++static int __init tosakbd_probe(struct platform_device *pdev) {
++
++ int i;
++ struct tosakbd *tosakbd;
++ struct input_dev *input_dev;
++
++ tosakbd = kzalloc(sizeof(struct tosakbd), GFP_KERNEL);
++ if (!tosakbd)
++ return -ENOMEM;
++
++ input_dev = input_allocate_device();
++ if (!input_dev) {
++ kfree(tosakbd);
++ return -ENOMEM;
++ }
++
++ platform_set_drvdata(pdev,tosakbd);
++
++ spin_lock_init(&tosakbd->lock);
++
++ /* Init Keyboard rescan timer */
++ init_timer(&tosakbd->timer);
++ tosakbd->timer.function = tosakbd_timer_callback;
++ tosakbd->timer.data = (unsigned long) tosakbd;
++
++ /* Init Headphone Timer */
++ init_timer(&tosakbd->hptimer);
++ tosakbd->hptimer.function = tosakbd_hp_timer;
++ tosakbd->hptimer.data = (unsigned long) tosakbd;
++
++ tosakbd->suspend_jiffies = jiffies;
++
++ tosakbd->input = input_dev;
++
++ input_dev->private = tosakbd;
++ input_dev->name = "Tosa Keyboard";
++ input_dev->phys = "tosakbd/input0";
++ input_dev->cdev.dev = &pdev->dev;
++
++ input_dev->id.bustype = BUS_HOST;
++ input_dev->id.vendor = 0x0001;
++ input_dev->id.product = 0x0001;
++ input_dev->id.version = 0x0100;
++
++ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_PWR) | BIT(EV_SW);
++ input_dev->keycode = tosakbd->keycode;
++ input_dev->keycodesize = sizeof(unsigned char);
++ input_dev->keycodemax = ARRAY_SIZE(tosakbd_keycode);
++
++ memcpy(tosakbd->keycode, tosakbd_keycode, sizeof(tosakbd->keycode));
++ for (i = 0; i < ARRAY_SIZE(tosakbd_keycode); i++)
++ set_bit(tosakbd->keycode[i], input_dev->keybit);
++ clear_bit(0, input_dev->keybit);
++ set_bit(SW_HEADPHONE_INSERT, input_dev->swbit);
++
++ input_register_device(tosakbd->input);
++
++ /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
++ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) {
++ pxa_gpio_mode( TOSA_GPIO_KEY_SENSE(i) | GPIO_IN);
++ if (request_irq(TOSA_IRQ_GPIO_KEY_SENSE(i), tosakbd_interrupt,
++ IRQF_DISABLED | IRQF_TRIGGER_RISING, "tosakbd", tosakbd)) {
++ printk("tosakbd: Can't get IRQ: %d !\n", i);
++ }
++ }
++
++ /* Set Strobe lines as outputs - set high */
++ for (i = 0; i < TOSA_KEY_STROBE_NUM; i++) {
++ pxa_gpio_mode( TOSA_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
++ }
++
++ // Power&Rec Button
++ pxa_gpio_mode( TOSA_GPIO_ON_KEY | GPIO_IN);
++ pxa_gpio_mode( TOSA_GPIO_RECORD_BTN | GPIO_IN);
++ pxa_gpio_mode( TOSA_GPIO_SYNC | GPIO_IN);
++ pxa_gpio_mode( TOSA_GPIO_EAR_IN | GPIO_IN);
++
++ if (request_irq(TOSA_IRQ_GPIO_ON_KEY, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "On key", tosakbd) ||
++ request_irq(TOSA_IRQ_GPIO_RECORD_BTN, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "Record key", tosakbd) ||
++ request_irq(TOSA_IRQ_GPIO_SYNC, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "Sync key", tosakbd) ||
++ request_irq(TOSA_IRQ_GPIO_EAR_IN, tosakbd_hp_isr, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "HP in", tosakbd)) {
++ printk("Could not allocate KEYBD IRQ!\n");
++ }
++
++ printk(KERN_INFO "input: Tosa Keyboard Registered\n");
++
++ return 0;
++}
++
++static int tosakbd_remove(struct platform_device *dev) {
++
++ int i;
++ struct tosakbd *tosakbd = platform_get_drvdata(dev);
++
++ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++)
++ free_irq(TOSA_IRQ_GPIO_KEY_SENSE(i),tosakbd);
++
++ free_irq(TOSA_IRQ_GPIO_ON_KEY,tosakbd);
++ free_irq(TOSA_IRQ_GPIO_RECORD_BTN,tosakbd);
++ free_irq(TOSA_IRQ_GPIO_SYNC,tosakbd);
++
++ del_timer_sync(&tosakbd->timer);
++
++ input_unregister_device(tosakbd->input);
++
++ kfree(tosakbd);
++
++ return 0;
++}
++
++static struct platform_driver tosakbd_driver = {
++ .probe = tosakbd_probe,
++ .remove = tosakbd_remove,
++ .suspend = tosakbd_suspend,
++ .resume = tosakbd_resume,
++ .driver = {
++ .name = "tosa-keyboard",
++ },
++};
++
++static int __devinit tosakbd_init(void)
++{
++ return platform_driver_register(&tosakbd_driver);
++}
++
++static void __exit tosakbd_exit(void)
++{
++ platform_driver_unregister(&tosakbd_driver);
++}
++
++module_init(tosakbd_init);
++module_exit(tosakbd_exit);
++
++MODULE_AUTHOR("Dirk Opfer <Dirk@Opfer-Online.de>");
++MODULE_DESCRIPTION("Tosa Keyboard Driver");
++MODULE_LICENSE("GPLv2");
diff --git a/packages/linux/linux-rp-2.6.23/tosa-lcdnoise-r1-fix-r0.patch b/packages/linux/linux-rp-2.6.23/tosa-lcdnoise-r1-fix-r0.patch
new file mode 100644
index 0000000000..02dd6a74eb
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/tosa-lcdnoise-r1-fix-r0.patch
@@ -0,0 +1,270 @@
+From eada869814636157956641ba1503f0d6cc04e2b7 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 17:43:51 +0400
+Subject: [PATCH] tosa-lcdnoise-r1.patch fixes
+
+---
+ arch/arm/mach-pxa/tosa_lcd.c | 34 ++++++++++++++++++++++++++++++++++
+ drivers/input/touchscreen/tosa_ts.c | 9 +--------
+ include/asm-arm/arch-pxa/tosa.h | 5 +++++
+ 3 files changed, 40 insertions(+), 8 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa_lcd.c b/arch/arm/mach-pxa/tosa_lcd.c
+index d52f63f..447ca86 100644
+--- a/arch/arm/mach-pxa/tosa_lcd.c
++++ b/arch/arm/mach-pxa/tosa_lcd.c
+@@ -59,6 +59,8 @@ static int bl_intensity;
+ static struct ssp_dev tosa_nssp_dev;
+ static struct ssp_state tosa_nssp_state;
+ static spinlock_t tosa_nssp_lock;
++static int blanked;
++static unsigned long hsync_time;
+
+ static unsigned short normal_i2c[] = {
+ DAC_BASE,
+@@ -130,6 +132,17 @@ static void tosa_lcd_tg_init(struct device *dev)
+ pxa_nssp_output(TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */
+ }
+
++static unsigned long calc_hsync_time(const struct fb_videomode *mode) {
++ /* The 25 and 44 'magic numbers' are from Sharp's 2.4 patches */
++ if (mode->yres == 640) {
++ return 25;
++ }
++ if (mode->yres == 320) {
++ return 44;
++ }
++ return 0;
++}
++
+ static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode)
+ {
+ const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR;
+@@ -154,6 +167,8 @@ static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode)
+ /* set common voltage */
+ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj);
+
++ blanked = 0;
++ hsync_time = calc_hsync_time(mode);
+ }
+
+ static void tosa_lcd_tg_off(struct device *dev)
+@@ -172,6 +187,8 @@ static void tosa_lcd_tg_off(struct device *dev)
+
+ /* L3V Off */
+ reset_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON);
++
++ blanked = 1;
+ }
+
+ static int tosa_detect_client(struct i2c_adapter* adapter, int address, int kind) {
+@@ -238,6 +255,23 @@ static int tosa_detach_client(struct i2c_client* client) {
+ return 0;
+ }
+
++unsigned long tosa_lcd_get_hsync_time(void)
++{
++/* This method should eventually contain the correct algorithm for calculating
++ the hsync_time */
++ if (blanked)
++ return 0;
++ else
++ return hsync_time;
++}
++
++void tosa_lcd_wait_hsync(void)
++{
++ /* Waits for a rising edge on the VGA line */
++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0);
++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0);
++}
++
+ static struct i2c_driver tosa_driver={
+ .id = TOSA_LCD_I2C_DEVICEID,
+ .attach_adapter = tosa_attach_adapter,
+diff --git a/drivers/input/touchscreen/tosa_ts.c b/drivers/input/touchscreen/tosa_ts.c
+index bc733e9..134f8ce 100644
+--- a/drivers/input/touchscreen/tosa_ts.c
++++ b/drivers/input/touchscreen/tosa_ts.c
+@@ -25,13 +25,6 @@
+ #define CCNT_ON() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1))
+ #define CCNT_OFF() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1))
+
+-static inline void tosa_lcd_wait_hsync(void)
+-{
+- /* Waits for a rising edge on the VGA line */
+- while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0);
+- while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0);
+-}
+-
+ /* On the Sharp SL-6000 (Tosa), due to a noisy LCD, we need to perform a wait
+ * before sampling the Y axis of the touchscreen */
+ void tosa_lcd_sync_on(int adcsel) {
+@@ -54,7 +47,7 @@ void tosa_lcd_sync_on(int adcsel) {
+ }
+ }
+
+-void tosa_lcd_sync_off(void) {
++void tosa_lcd_sync_off(int adcsel) {
+ CCNT_OFF();
+ }
+
+diff --git a/include/asm-arm/arch-pxa/tosa.h b/include/asm-arm/arch-pxa/tosa.h
+index ce7322d..7f446fd 100644
+--- a/include/asm-arm/arch-pxa/tosa.h
++++ b/include/asm-arm/arch-pxa/tosa.h
+@@ -1,6 +1,7 @@
+ /*
+ * Hardware specific definitions for Sharp SL-C6000x series of PDAs
+ *
++ * Copyright (c) 2006 Wolfson Microelectronics PLC.
+ * Copyright (c) 2005 Dirk Opfer
+ *
+ * Based on Sharp's 2.4 kernel patches
+@@ -187,4 +188,8 @@
+ extern struct platform_device tosascoop_jc_device;
+ extern struct platform_device tosascoop_device;
+ extern struct platform_device tc6393_device;
++
++unsigned long tosa_lcd_get_hsync_time(void);
++void tosa_lcd_wait_hsync(void);
++
+ #endif /* _ASM_ARCH_TOSA_H_ */
+--
+1.4.4.4
+
+From eada869814636157956641ba1503f0d6cc04e2b7 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 17:43:51 +0400
+Subject: [PATCH] tosa-lcdnoise-r1.patch fixes
+
+---
+ arch/arm/mach-pxa/tosa_lcd.c | 34 ++++++++++++++++++++++++++++++++++
+ drivers/input/touchscreen/tosa_ts.c | 9 +--------
+ include/asm-arm/arch-pxa/tosa.h | 5 +++++
+ 3 files changed, 40 insertions(+), 8 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa_lcd.c b/arch/arm/mach-pxa/tosa_lcd.c
+index d52f63f..447ca86 100644
+--- a/arch/arm/mach-pxa/tosa_lcd.c
++++ b/arch/arm/mach-pxa/tosa_lcd.c
+@@ -59,6 +59,8 @@ static int bl_intensity;
+ static struct ssp_dev tosa_nssp_dev;
+ static struct ssp_state tosa_nssp_state;
+ static spinlock_t tosa_nssp_lock;
++static int blanked;
++static unsigned long hsync_time;
+
+ static unsigned short normal_i2c[] = {
+ DAC_BASE,
+@@ -130,6 +132,17 @@ static void tosa_lcd_tg_init(struct device *dev)
+ pxa_nssp_output(TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */
+ }
+
++static unsigned long calc_hsync_time(const struct fb_videomode *mode) {
++ /* The 25 and 44 'magic numbers' are from Sharp's 2.4 patches */
++ if (mode->yres == 640) {
++ return 25;
++ }
++ if (mode->yres == 320) {
++ return 44;
++ }
++ return 0;
++}
++
+ static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode)
+ {
+ const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR;
+@@ -154,6 +167,8 @@ static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode)
+ /* set common voltage */
+ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj);
+
++ blanked = 0;
++ hsync_time = calc_hsync_time(mode);
+ }
+
+ static void tosa_lcd_tg_off(struct device *dev)
+@@ -172,6 +187,8 @@ static void tosa_lcd_tg_off(struct device *dev)
+
+ /* L3V Off */
+ reset_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON);
++
++ blanked = 1;
+ }
+
+ static int tosa_detect_client(struct i2c_adapter* adapter, int address, int kind) {
+@@ -238,6 +255,23 @@ static int tosa_detach_client(struct i2c_client* client) {
+ return 0;
+ }
+
++unsigned long tosa_lcd_get_hsync_time(void)
++{
++/* This method should eventually contain the correct algorithm for calculating
++ the hsync_time */
++ if (blanked)
++ return 0;
++ else
++ return hsync_time;
++}
++
++void tosa_lcd_wait_hsync(void)
++{
++ /* Waits for a rising edge on the VGA line */
++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0);
++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0);
++}
++
+ static struct i2c_driver tosa_driver={
+ .id = TOSA_LCD_I2C_DEVICEID,
+ .attach_adapter = tosa_attach_adapter,
+diff --git a/drivers/input/touchscreen/tosa_ts.c b/drivers/input/touchscreen/tosa_ts.c
+index bc733e9..134f8ce 100644
+--- a/drivers/input/touchscreen/tosa_ts.c
++++ b/drivers/input/touchscreen/tosa_ts.c
+@@ -25,13 +25,6 @@
+ #define CCNT_ON() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1))
+ #define CCNT_OFF() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1))
+
+-static inline void tosa_lcd_wait_hsync(void)
+-{
+- /* Waits for a rising edge on the VGA line */
+- while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0);
+- while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0);
+-}
+-
+ /* On the Sharp SL-6000 (Tosa), due to a noisy LCD, we need to perform a wait
+ * before sampling the Y axis of the touchscreen */
+ void tosa_lcd_sync_on(int adcsel) {
+@@ -54,7 +47,7 @@ void tosa_lcd_sync_on(int adcsel) {
+ }
+ }
+
+-void tosa_lcd_sync_off(void) {
++void tosa_lcd_sync_off(int adcsel) {
+ CCNT_OFF();
+ }
+
+diff --git a/include/asm-arm/arch-pxa/tosa.h b/include/asm-arm/arch-pxa/tosa.h
+index ce7322d..7f446fd 100644
+--- a/include/asm-arm/arch-pxa/tosa.h
++++ b/include/asm-arm/arch-pxa/tosa.h
+@@ -1,6 +1,7 @@
+ /*
+ * Hardware specific definitions for Sharp SL-C6000x series of PDAs
+ *
++ * Copyright (c) 2006 Wolfson Microelectronics PLC.
+ * Copyright (c) 2005 Dirk Opfer
+ *
+ * Based on Sharp's 2.4 kernel patches
+@@ -187,4 +188,8 @@
+ extern struct platform_device tosascoop_jc_device;
+ extern struct platform_device tosascoop_device;
+ extern struct platform_device tc6393_device;
++
++unsigned long tosa_lcd_get_hsync_time(void);
++void tosa_lcd_wait_hsync(void);
++
+ #endif /* _ASM_ARCH_TOSA_H_ */
+--
+1.4.4.4
+
diff --git a/packages/linux/linux-rp-2.6.23/tosa-lcdnoise-r1.patch b/packages/linux/linux-rp-2.6.23/tosa-lcdnoise-r1.patch
new file mode 100644
index 0000000000..910b727b82
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/tosa-lcdnoise-r1.patch
@@ -0,0 +1,316 @@
+From 564b757ba44b517ac6d693b94a177708bb5d3887 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 17:30:30 +0400
+Subject: [PATCH] tosa-lcdnoise-r1.patch
+
+---
+ drivers/input/touchscreen/Kconfig | 13 +++++
+ drivers/input/touchscreen/Makefile | 1 +
+ drivers/input/touchscreen/tosa_ts.c | 102 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 116 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
+index 3ac01b4..6862e8f 100644
+--- a/drivers/input/touchscreen/Kconfig
++++ b/drivers/input/touchscreen/Kconfig
+@@ -219,6 +219,19 @@ config TOUCHSCREEN_USB_DMC_TSC10
+ bool "DMC TSC-10/25 device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
++config TOUCHSCREEN_TOSA
++ tristate "Sharp Tosa touchscreen driver"
++ depends on TOUCHSCREEN_WM97XX && MACH_TOSA
++ default n
++ help
++ Say Y here to enable the driver for the touchscreen on the
++ Sharp Tosa PDA.
++ depends on TOUCHSCREEN_WM97XX && MACH_TOSA
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called tosa_ts.
++
+ config TOUCHSCREEN_TSC2101
+ tristate "TI TSC2101 touchscreen input driver"
+ depends on MACH_HX2750 && INPUT && INPUT_TOUCHSCREEN
+diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
+index f64d1a5..4fc0e17 100644
+--- a/drivers/input/touchscreen/Makefile
++++ b/drivers/input/touchscreen/Makefile
+@@ -22,6 +22,7 @@ obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
+ obj-$(CONFIG_TOUCHSCREEN_TSC2101) += tsc2101_ts.o
+ obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
+ obj-$(CONFIG_TOUCHSCREEN_WM97XX_PXA) += pxa-wm97xx.o
++obj-$(CONFIG_TOUCHSCREEN_TOSA) += tosa_ts.o
+
+ ifeq ($(CONFIG_TOUCHSCREEN_WM9713),y)
+ wm97xx-ts-objs += wm9713.o
+diff --git a/drivers/input/touchscreen/tosa_ts.c b/drivers/input/touchscreen/tosa_ts.c
+new file mode 100644
+index 0000000..bc733e9
+--- /dev/null
++++ b/drivers/input/touchscreen/tosa_ts.c
+@@ -0,0 +1,102 @@
++/*
++ * tosa_ts.c -- Touchscreen driver for Sharp SL-6000 (Tosa).
++ *
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ * Author: Mike Arthur
++ * linux@wolfsonmicro.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.
++ *
++ * Revision history
++ * 1st Sep 2006 Initial version.
++ *
++ */
++
++#include <linux/wm97xx.h>
++#include <asm/arch/tosa.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch/pxa-regs.h>
++
++/* Taken from the Sharp 2.4 kernel code */
++#define CCNT(a) asm volatile ("mrc p14, 0, %0, C1, C1, 0" : "=r"(a))
++#define CCNT_ON() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1))
++#define CCNT_OFF() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1))
++
++static inline void tosa_lcd_wait_hsync(void)
++{
++ /* Waits for a rising edge on the VGA line */
++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0);
++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0);
++}
++
++/* On the Sharp SL-6000 (Tosa), due to a noisy LCD, we need to perform a wait
++ * before sampling the Y axis of the touchscreen */
++void tosa_lcd_sync_on(int adcsel) {
++ unsigned long timer1 = 0, timer2 = 0, wait_time = 0;
++ if (adcsel == WM97XX_ADCSEL_Y) {
++ wait_time = tosa_lcd_get_hsync_time();
++ CCNT_ON();
++
++ if (wait_time) {
++ /* wait for LCD rising edge */
++ tosa_lcd_wait_hsync();
++ /* get clock */
++ CCNT(timer1);
++ CCNT(timer2);
++
++ while ((timer2 - timer1) < wait_time) {
++ CCNT(timer2);
++ }
++ }
++ }
++}
++
++void tosa_lcd_sync_off(void) {
++ CCNT_OFF();
++}
++
++static struct wm97xx_mach_ops tosa_mach_ops = {
++ .pre_sample = tosa_lcd_sync_on,
++ .post_sample = tosa_lcd_sync_off,
++};
++
++int tosa_ts_probe(struct device *dev) {
++ struct wm97xx *wm = dev->driver_data;
++ return wm97xx_register_mach_ops (wm, &tosa_mach_ops);
++}
++
++
++int tosa_ts_remove(struct device *dev) {
++ struct wm97xx *wm = dev->driver_data;
++ wm97xx_unregister_mach_ops (wm);
++ return 0;
++}
++
++static struct device_driver tosa_ts_driver = {
++ .name = "wm97xx-touchscreen",
++ .bus = &wm97xx_bus_type,
++ .owner = THIS_MODULE,
++ .probe = tosa_ts_probe,
++ .remove = tosa_ts_remove,
++};
++
++static int __init tosa_ts_init(void)
++{
++ return driver_register(&tosa_ts_driver);
++}
++
++static void __exit tosa_ts_exit(void)
++{
++ driver_unregister(&tosa_ts_driver);
++}
++
++module_init(tosa_ts_init);
++module_exit(tosa_ts_exit);
++
++/* Module information */
++MODULE_AUTHOR("Mike Arthur, mike@mikearthur.co.uk, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("Sharp SL6000 Tosa Touch Screen Driver");
++MODULE_LICENSE("GPL");
+--
+1.4.4.4
+
+From 564b757ba44b517ac6d693b94a177708bb5d3887 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 17:30:30 +0400
+Subject: [PATCH] tosa-lcdnoise-r1.patch
+
+---
+ drivers/input/touchscreen/Kconfig | 13 +++++
+ drivers/input/touchscreen/Makefile | 1 +
+ drivers/input/touchscreen/tosa_ts.c | 102 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 116 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
+index 3ac01b4..6862e8f 100644
+--- a/drivers/input/touchscreen/Kconfig
++++ b/drivers/input/touchscreen/Kconfig
+@@ -219,6 +219,19 @@ config TOUCHSCREEN_USB_DMC_TSC10
+ bool "DMC TSC-10/25 device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
++config TOUCHSCREEN_TOSA
++ tristate "Sharp Tosa touchscreen driver"
++ depends on TOUCHSCREEN_WM97XX && MACH_TOSA
++ default n
++ help
++ Say Y here to enable the driver for the touchscreen on the
++ Sharp Tosa PDA.
++ depends on TOUCHSCREEN_WM97XX && MACH_TOSA
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called tosa_ts.
++
+ config TOUCHSCREEN_TSC2101
+ tristate "TI TSC2101 touchscreen input driver"
+ depends on MACH_HX2750 && INPUT && INPUT_TOUCHSCREEN
+diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
+index f64d1a5..4fc0e17 100644
+--- a/drivers/input/touchscreen/Makefile
++++ b/drivers/input/touchscreen/Makefile
+@@ -22,6 +22,7 @@ obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
+ obj-$(CONFIG_TOUCHSCREEN_TSC2101) += tsc2101_ts.o
+ obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
+ obj-$(CONFIG_TOUCHSCREEN_WM97XX_PXA) += pxa-wm97xx.o
++obj-$(CONFIG_TOUCHSCREEN_TOSA) += tosa_ts.o
+
+ ifeq ($(CONFIG_TOUCHSCREEN_WM9713),y)
+ wm97xx-ts-objs += wm9713.o
+diff --git a/drivers/input/touchscreen/tosa_ts.c b/drivers/input/touchscreen/tosa_ts.c
+new file mode 100644
+index 0000000..bc733e9
+--- /dev/null
++++ b/drivers/input/touchscreen/tosa_ts.c
+@@ -0,0 +1,102 @@
++/*
++ * tosa_ts.c -- Touchscreen driver for Sharp SL-6000 (Tosa).
++ *
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ * Author: Mike Arthur
++ * linux@wolfsonmicro.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.
++ *
++ * Revision history
++ * 1st Sep 2006 Initial version.
++ *
++ */
++
++#include <linux/wm97xx.h>
++#include <asm/arch/tosa.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch/pxa-regs.h>
++
++/* Taken from the Sharp 2.4 kernel code */
++#define CCNT(a) asm volatile ("mrc p14, 0, %0, C1, C1, 0" : "=r"(a))
++#define CCNT_ON() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1))
++#define CCNT_OFF() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1))
++
++static inline void tosa_lcd_wait_hsync(void)
++{
++ /* Waits for a rising edge on the VGA line */
++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0);
++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0);
++}
++
++/* On the Sharp SL-6000 (Tosa), due to a noisy LCD, we need to perform a wait
++ * before sampling the Y axis of the touchscreen */
++void tosa_lcd_sync_on(int adcsel) {
++ unsigned long timer1 = 0, timer2 = 0, wait_time = 0;
++ if (adcsel == WM97XX_ADCSEL_Y) {
++ wait_time = tosa_lcd_get_hsync_time();
++ CCNT_ON();
++
++ if (wait_time) {
++ /* wait for LCD rising edge */
++ tosa_lcd_wait_hsync();
++ /* get clock */
++ CCNT(timer1);
++ CCNT(timer2);
++
++ while ((timer2 - timer1) < wait_time) {
++ CCNT(timer2);
++ }
++ }
++ }
++}
++
++void tosa_lcd_sync_off(void) {
++ CCNT_OFF();
++}
++
++static struct wm97xx_mach_ops tosa_mach_ops = {
++ .pre_sample = tosa_lcd_sync_on,
++ .post_sample = tosa_lcd_sync_off,
++};
++
++int tosa_ts_probe(struct device *dev) {
++ struct wm97xx *wm = dev->driver_data;
++ return wm97xx_register_mach_ops (wm, &tosa_mach_ops);
++}
++
++
++int tosa_ts_remove(struct device *dev) {
++ struct wm97xx *wm = dev->driver_data;
++ wm97xx_unregister_mach_ops (wm);
++ return 0;
++}
++
++static struct device_driver tosa_ts_driver = {
++ .name = "wm97xx-touchscreen",
++ .bus = &wm97xx_bus_type,
++ .owner = THIS_MODULE,
++ .probe = tosa_ts_probe,
++ .remove = tosa_ts_remove,
++};
++
++static int __init tosa_ts_init(void)
++{
++ return driver_register(&tosa_ts_driver);
++}
++
++static void __exit tosa_ts_exit(void)
++{
++ driver_unregister(&tosa_ts_driver);
++}
++
++module_init(tosa_ts_init);
++module_exit(tosa_ts_exit);
++
++/* Module information */
++MODULE_AUTHOR("Mike Arthur, mike@mikearthur.co.uk, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("Sharp SL6000 Tosa Touch Screen Driver");
++MODULE_LICENSE("GPL");
+--
+1.4.4.4
+
diff --git a/packages/linux/linux-rp-2.6.23/tosa-power-r18-fix-r0.patch b/packages/linux/linux-rp-2.6.23/tosa-power-r18-fix-r0.patch
new file mode 100644
index 0000000000..fdfbea8ee0
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/tosa-power-r18-fix-r0.patch
@@ -0,0 +1,118 @@
+From 24813da9b0aac0e92635d7307837d89a9f4a1ee7 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 16:47:15 +0400
+Subject: [PATCH] tosa-power-r18.patch fixes
+
+---
+ arch/arm/mach-pxa/tosa_pm.c | 9 +++++----
+ 1 files changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa_pm.c b/arch/arm/mach-pxa/tosa_pm.c
+index 1eab1af..2df75f0 100644
+--- a/arch/arm/mach-pxa/tosa_pm.c
++++ b/arch/arm/mach-pxa/tosa_pm.c
+@@ -17,9 +17,9 @@
+ #include <linux/interrupt.h>
+ #include <linux/platform_device.h>
+ #include <linux/pm.h>
++#include <linux/apm-emulation.h>
+ #include <linux/wm97xx.h>
+
+-#include <asm/apm.h>
+ #include <asm/irq.h>
+ #include <asm/mach-types.h>
+ #include <asm/hardware.h>
+@@ -144,7 +144,7 @@ static int tosa_ac97_init(void)
+ pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
+ pxa_gpio_mode(GPIO20_DREQ0_MD);
+
+- pxa_set_cken(CKEN2_AC97, 1);
++ pxa_set_cken(CKEN_AC97, 1);
+ /* AC97 power on sequense */
+ while ( 1 ) {
+ GCR = 0;
+@@ -184,11 +184,12 @@ static int tosa_ac97_init(void)
+ pxa_gpio_mode(GPIO32_SDATA_IN1_AC97_MD);
+ ad_polling = 1;
+ printk("tosa_ac97_init\n");
++ return 0;
+ }
+
+ void tosa_ac97_exit(void)
+ {
+- if (!(CKEN & CKEN2_AC97))
++ if (!(CKEN & CKEN_AC97))
+ return;
+
+ // power down the whole chip
+@@ -197,7 +198,7 @@ void tosa_ac97_exit(void)
+ // GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN);
+ // GSR = GSR;
+ // GCR = GCR_ACLINK_OFF;
+- pxa_set_cken(CKEN2_AC97, 0);
++ pxa_set_cken(CKEN_AC97, 0);
+ /* switch back to irq driver */
+ ad_polling = 0;
+ printk("tosa_ac97_exit\n");
+--
+1.4.4.4
+
+From 24813da9b0aac0e92635d7307837d89a9f4a1ee7 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 16:47:15 +0400
+Subject: [PATCH] tosa-power-r18.patch fixes
+
+---
+ arch/arm/mach-pxa/tosa_pm.c | 9 +++++----
+ 1 files changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa_pm.c b/arch/arm/mach-pxa/tosa_pm.c
+index 1eab1af..2df75f0 100644
+--- a/arch/arm/mach-pxa/tosa_pm.c
++++ b/arch/arm/mach-pxa/tosa_pm.c
+@@ -17,9 +17,9 @@
+ #include <linux/interrupt.h>
+ #include <linux/platform_device.h>
+ #include <linux/pm.h>
++#include <linux/apm-emulation.h>
+ #include <linux/wm97xx.h>
+
+-#include <asm/apm.h>
+ #include <asm/irq.h>
+ #include <asm/mach-types.h>
+ #include <asm/hardware.h>
+@@ -144,7 +144,7 @@ static int tosa_ac97_init(void)
+ pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
+ pxa_gpio_mode(GPIO20_DREQ0_MD);
+
+- pxa_set_cken(CKEN2_AC97, 1);
++ pxa_set_cken(CKEN_AC97, 1);
+ /* AC97 power on sequense */
+ while ( 1 ) {
+ GCR = 0;
+@@ -184,11 +184,12 @@ static int tosa_ac97_init(void)
+ pxa_gpio_mode(GPIO32_SDATA_IN1_AC97_MD);
+ ad_polling = 1;
+ printk("tosa_ac97_init\n");
++ return 0;
+ }
+
+ void tosa_ac97_exit(void)
+ {
+- if (!(CKEN & CKEN2_AC97))
++ if (!(CKEN & CKEN_AC97))
+ return;
+
+ // power down the whole chip
+@@ -197,7 +198,7 @@ void tosa_ac97_exit(void)
+ // GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN);
+ // GSR = GSR;
+ // GCR = GCR_ACLINK_OFF;
+- pxa_set_cken(CKEN2_AC97, 0);
++ pxa_set_cken(CKEN_AC97, 0);
+ /* switch back to irq driver */
+ ad_polling = 0;
+ printk("tosa_ac97_exit\n");
+--
+1.4.4.4
+
diff --git a/packages/linux/linux-rp-2.6.23/tosa-power-r18.patch b/packages/linux/linux-rp-2.6.23/tosa-power-r18.patch
new file mode 100644
index 0000000000..a099717749
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/tosa-power-r18.patch
@@ -0,0 +1,1382 @@
+Index: linux-2.6.17/arch/arm/mach-pxa/Makefile
+===================================================================
+--- linux-2.6.17.orig/arch/arm/mach-pxa/Makefile 2006-09-19 20:51:33.984424500 +0200
++++ linux-2.6.17/arch/arm/mach-pxa/Makefile 2006-09-19 21:08:04.922354250 +0200
+@@ -16,7 +16,7 @@ obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o
+ obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o
+ obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o
+ obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o
+-obj-$(CONFIG_MACH_TOSA) += tosa.o
++obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o
+ obj-$(CONFIG_MACH_EM_X270) += em-x270.o
+ obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o
+
+Index: linux-2.6.17/arch/arm/mach-pxa/tosa_pm.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/arch/arm/mach-pxa/tosa_pm.c 2006-09-19 21:08:34.476201250 +0200
+@@ -0,0 +1,661 @@
++/*
++ * Battery and Power Management code for the Sharp SL-6000x
++ *
++ * Copyright (c) 2005 Dirk Opfer
++ *
++ * 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.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/stat.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/pm.h>
++#include <linux/wm97xx.h>
++
++#include <asm/apm.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++#include <asm/hardware.h>
++#include <asm/hardware/scoop.h>
++#include <asm/hardware/tmio.h>
++
++#include <asm/arch/sharpsl.h>
++#include <asm/arch/tosa.h>
++#include <asm/arch/pxa-regs.h>
++#include <sound/soc.h>
++#include <sound/ac97_codec.h>
++#include "sharpsl.h"
++
++extern int tosa_bl_intensity(void);
++volatile static int ad_polling;
++static int tosa_pm_driver_probe(struct device *dev);
++static int tosa_pm_driver_remove(struct device *dev);
++static struct wm97xx *wm9712;
++
++/************************************************************
++ * AC97 functions
++ ************************************************************/
++#define AC97_TIMEOUT_VAL 0x1000000
++
++#define AC97_MISC_MODEM_STAT 0x0056
++#define AC97_GPIO_CONFIG 0x004C
++
++static u16 tosa_ac97_read(unsigned short reg)
++{
++ volatile u32 *reg_addr;
++ volatile int timeout;
++ unsigned short data;
++
++ if (CAR & CAR_CAIP) {
++ printk(KERN_CRIT ": CAR_CAIP already set\n");
++ return 0;
++ }
++
++ if (reg == AC97_GPIO_STATUS)
++ reg_addr = &PMC_REG_BASE;
++ else
++ reg_addr = &PAC_REG_BASE;
++
++ reg_addr += (reg >> 1);
++
++ data=0;
++ GSR = GSR_CDONE | GSR_SDONE;
++
++ data = *reg_addr;
++ timeout = 0;
++
++ while(((GSR & GSR_SDONE)) == 0 && (timeout++ < AC97_TIMEOUT_VAL));
++
++ if ((timeout >= AC97_TIMEOUT_VAL)) {
++ GSR = GSR;
++ printk(KERN_CRIT ": AC97 is busy1.\n");
++ return data;
++ }
++
++ // actual read
++ GSR = GSR_CDONE | GSR_SDONE;
++ data = *reg_addr;
++
++ timeout = 0;
++ while(((GSR & GSR_SDONE) == 0) && (timeout++<AC97_TIMEOUT_VAL));
++ if ((timeout >= AC97_TIMEOUT_VAL)) {
++ GSR = GSR;
++ printk(KERN_CRIT ": AC97 is busy2.\n");
++ return data;
++ }
++
++ return data;
++}
++
++static void tosa_ac97_write(unsigned short reg, unsigned short val)
++{
++ volatile u32 *reg_addr;
++ volatile int timeout=0;
++
++ if (CAR & CAR_CAIP) {
++ printk(KERN_CRIT ": CAR_CAIP already set\n");
++ return;
++ }
++
++ GSR = GSR_CDONE | GSR_SDONE;
++ if (reg == AC97_GPIO_STATUS)
++ reg_addr = &PMC_REG_BASE;
++ else
++ reg_addr = &PAC_REG_BASE;
++
++ reg_addr += (reg >> 1);
++
++ *reg_addr = val;
++ while(((GSR & GSR_CDONE) == 0) && (timeout++ < AC97_TIMEOUT_VAL));
++ if (timeout >= AC97_TIMEOUT_VAL) {
++ printk(KERN_CRIT ": AC97 is busy.\n");
++ }
++}
++
++static void tosa_ac97_bit_clear(u8 reg, u16 val)
++{
++ unsigned short dat = tosa_ac97_read(reg);
++ dat &= ~val;
++ tosa_ac97_write(reg, dat);
++}
++
++static void tosa_ac97_bit_set(u8 reg, u16 val)
++{
++ unsigned short dat = tosa_ac97_read(reg);
++ dat |= val;
++ tosa_ac97_write(reg, dat);
++}
++
++
++static int tosa_ac97_init(void)
++{
++ int timeo;
++
++ pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
++ pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
++ pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
++ pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
++ pxa_gpio_mode(GPIO20_DREQ0_MD);
++
++ pxa_set_cken(CKEN2_AC97, 1);
++ /* AC97 power on sequense */
++ while ( 1 ) {
++ GCR = 0;
++ udelay(100);
++ GCR |= GCR_COLD_RST;
++ udelay(5);
++ GCR |= GCR_WARM_RST;
++ udelay(5);
++ for ( timeo = 0x10000; timeo > 0; timeo-- ) {
++ if ( GSR & GSR_PCR ) break;
++ mdelay(5);
++ }
++ if( timeo > 0 ) break;
++ printk(KERN_WARNING "AC97 power on retry!!\n");
++ }
++
++ tosa_ac97_write(AC97_EXTENDED_STATUS, 1);
++ /*
++ * Setting AC97 GPIO
++ * i/o function
++ * GPIO1: input EAR_IN signal
++ * GPIO2: output IRQ signal
++ * GPIO3: output PENDOWN signal
++ * GPIO4: input MASK signal
++ * GPIO5: input DETECT MIC signal
++ */
++ // AC97_GPIO_FUNC AC97_MISC_MODEM_STAT
++
++ tosa_ac97_bit_clear(AC97_MISC_MODEM_STAT,
++ ((1<<2)|(1<<3)|(1<<4)|(1<<5)));
++ tosa_ac97_bit_clear(AC97_GPIO_CONFIG,(1<<2)|(1<<3));
++ tosa_ac97_bit_set(AC97_GPIO_CONFIG, (1<<1)|(1<<4)|(1<<5));
++
++ tosa_ac97_write(AC97_WM97XX_DIGITISER2, 0xc009);
++ tosa_ac97_write(AC97_WM97XX_DIGITISER1, 0x0030 | WM97XX_DELAY(4));
++
++ pxa_gpio_mode(GPIO32_SDATA_IN1_AC97_MD);
++ ad_polling = 1;
++ printk("tosa_ac97_init\n");
++}
++
++void tosa_ac97_exit(void)
++{
++ if (!(CKEN & CKEN2_AC97))
++ return;
++
++ // power down the whole chip
++ tosa_ac97_write(AC97_POWERDOWN, 0x7fff);
++
++// GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN);
++// GSR = GSR;
++// GCR = GCR_ACLINK_OFF;
++ pxa_set_cken(CKEN2_AC97, 0);
++ /* switch back to irq driver */
++ ad_polling = 0;
++ printk("tosa_ac97_exit\n");
++}
++
++int ac97_ad_input(u16 adcsel)
++{
++ unsigned short val = 0;
++ unsigned long timeout;
++
++ // prepare
++ tosa_ac97_read(AC97_WM97XX_DIGITISER_RD);
++
++ if (adcsel & 0x8000)
++ adcsel = ((adcsel & 0x7fff) + 3) << 12;
++
++ /* Conversion start */
++ tosa_ac97_write(AC97_WM97XX_DIGITISER1, (adcsel | WM97XX_POLL | WM97XX_DELAY(4)));
++ timeout = 0x1000;
++ /* wait for POLL to go low */
++ while ((tosa_ac97_read(AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) {
++ udelay(100);
++ timeout--;
++ }
++
++ val = tosa_ac97_read(AC97_WM97XX_DIGITISER_RD);
++
++ val &= 0xFFF ;
++
++ return val;
++}
++
++
++int tosa_read_aux_adc(u16 adcsel)
++{
++ if (ad_polling)
++ return (ac97_ad_input(adcsel));
++ else
++ return (wm97xx_read_aux_adc(wm9712, adcsel));
++}
++
++static struct device_driver tosa_pm_driver = {
++ .name = "wm97xx-battery",
++ .bus = &wm97xx_bus_type,
++ .owner = THIS_MODULE,
++ .probe = tosa_pm_driver_probe,
++ .remove = tosa_pm_driver_remove,
++};
++
++#if 0
++#define TOSA_TEMP_READ_WAIT_TIME (5) // 5msec [Fix]
++int tosa_read_battery(struct wm97xx* wm, int channel)
++{
++ //return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, input));
++
++ int wm_aux,i;
++ int value = 0;
++ int clear_mux;
++
++ switch(channel) {
++
++ case 0: // Main
++
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON);
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON);
++ wm_aux = WM97XX_AUX_ID3;
++ break;
++
++ case 1: // Jacket
++
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_V_ON);
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT_SW_ON);
++ wm_aux = WM97XX_AUX_ID3;
++ break;
++
++ case 2: // BU
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BU_CHRG_ON);
++ wm_aux = WM97XX_AUX_ID4;
++ break;
++
++ case 3: // Main Temp
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON);
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON);
++ wm_aux = WM97XX_AUX_ID2;
++ break;
++
++ case 4: // Jacket Temp
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON);
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON);
++ wm_aux = WM97XX_AUX_ID2;
++ break;
++
++ default:
++ return -1;
++ }
++
++ mdelay(TOSA_TEMP_READ_WAIT_TIME);
++ for(i=0;i<4;i++)
++ {
++ if (wm9712)
++ value += wm97xx_read_aux_adc(wm, wm_aux);
++ else
++ value += ac97_ad_input(wm, wm_aux);
++ }
++
++ value>>=2;
++ // reset the multiplexer
++ clear_mux = TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON | TOSA_TC6393_BAT0_TH_ON | TOSA_TC6393_BAT1_TH_ON | TOSA_TC6393_BU_CHRG_ON;
++
++ return value;
++
++}
++#endif
++
++/* BL 5-3 */
++struct battery_thresh tosa_battery_levels_bl[] = {
++ { 1663, 100 },
++ { 1605, 75 },
++ { 1564, 50 },
++ { 1510, 25 },
++ { 1435, 5 },
++ { 0, 0 },
++};
++
++/* BL 2-0 */
++struct battery_thresh tosa_battery_levels[] = {
++ { 1679, 100 },
++ { 1617, 75 },
++ { 1576, 50 },
++ { 1530, 25 },
++ { 1448, 5 },
++ { 0, 0 },
++};
++
++struct pm_devices {
++ const char * name;
++ struct bus_type *bus;
++ struct device_driver *driver;
++ struct device * dev;
++ int (*resume)(struct device *dev, void * data); // int (*resume)(struct device *dev);
++ int (*suspend)(struct device *dev, void * data);// int (*suspend)(struct device *dev, pm_message_t state);
++};
++
++/* Ugly
++ We need the following devices to measure the battery and control the charger:
++ Also we need access to these before we sleep and immediatly after we resume so we can't
++ control their pm via the kernel device manager. To access their pm functions we will backup
++ the suspend and resume handler and clear these pointers.
++ After that we can suspend and resume these devices.
++
++ Don't change the order of this table!!!!!
++*/
++static struct pm_devices dev_table[] = {
++ [0] = {
++ .name = TMIO_SOC_NAME,
++ .bus = &platform_bus_type,
++ },
++};
++
++static int tosa_pm_driver_probe(struct device *dev)
++{
++ wm9712 = dev->driver_data;
++ ad_polling = 0;
++ return 0;
++}
++
++static int tosa_pm_driver_remove(struct device *dev)
++{
++ wm9712 = NULL;
++ return 0;
++}
++
++
++static void tosa_charger_init(void)
++{
++ int i;
++
++ /* If this driver doesn't register, bad things will happen, Tosa won't boot,
++ and the world will possibly explode */
++ i = driver_register(&tosa_pm_driver);
++ if (i < 0)
++ panic("Cannot register the tosa_pm driver on the wm97xx bus. Halting.");
++
++ for(i=0;i<ARRAY_SIZE(dev_table);i++)
++ {
++ dev_table[i].driver = driver_find(dev_table[i].name, dev_table[i].bus);
++ if (dev_table[i].driver)
++ {
++ dev_table[i].resume = dev_table[i].driver->resume;
++ dev_table[i].suspend = dev_table[i].driver->suspend;
++ dev_table[i].driver->resume = NULL;
++ dev_table[i].driver->suspend = NULL;
++ }
++ }
++
++ pxa_gpio_mode(TOSA_GPIO_AC_IN | GPIO_IN);
++ pxa_gpio_mode(TOSA_GPIO_BAT0_CRG | GPIO_IN);
++ pxa_gpio_mode(TOSA_GPIO_BAT1_CRG | GPIO_IN);
++ pxa_gpio_mode(TOSA_GPIO_BAT0_LOW | GPIO_IN);
++ pxa_gpio_mode(TOSA_GPIO_BAT1_LOW | GPIO_IN);
++
++ pxa_gpio_mode(TOSA_GPIO_JACKET_DETECT | GPIO_IN);
++ pxa_gpio_mode(TOSA_GPIO_POWERON | GPIO_IN);
++ sharpsl_pm_pxa_init();
++}
++
++
++static void tosa_measure_temp(int on)
++{
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON | TOSA_TC6393_BAT1_TH_ON);
++
++ if (on)
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON);
++
++}
++
++static void tosa_charge(int on)
++{
++ if(on)
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_CHARGE_OFF);
++ else
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_CHARGE_OFF);
++}
++
++static void tosa_discharge1(int on)
++{
++}
++
++static void tosa_discharge(int on)
++{
++}
++
++
++static void tosa_presuspend(void)
++{
++ int i;
++ unsigned long wakeup_mask;
++
++ // put remaining devices into sleep
++ for(i=0;i<ARRAY_SIZE(dev_table);i++)
++ {
++ if(dev_table[i].suspend)
++ driver_for_each_device(dev_table[i].driver, NULL,
++ (void*)&PMSG_SUSPEND, dev_table[i].suspend);
++ }
++
++ tosa_ac97_exit();
++
++ wakeup_mask = GPIO_bit(TOSA_GPIO_POWERON) | GPIO_bit(TOSA_GPIO_ON_KEY) | GPIO_bit(TOSA_GPIO_AC_IN);
++
++ wakeup_mask |= GPIO_bit(TOSA_GPIO_BAT0_LOW);
++ PWER = wakeup_mask | PWER_RTC;
++
++ PRER = wakeup_mask;
++ PFER = wakeup_mask;
++
++ for (i = 0; i <=15; i++) {
++ if (PRER & PFER & GPIO_bit(i)) {
++ if (GPLR0 & GPIO_bit(i) )
++ PRER &= ~GPIO_bit(i);
++ else
++ PFER &= ~GPIO_bit(i);
++ }
++ }
++
++ /* Clear reset status */
++ RCSR = RCSR_HWR | RCSR_WDR | RCSR_SMR | RCSR_GPR;
++
++ /* Stop 3.6MHz and drive HIGH to PCMCIA and CS */
++ PCFR = PCFR_OPDE;
++
++ /* Resume on keyboard power key */
++ PGSR1 = (PGSR1 & ~TOSA_GPIO_LOW_STROBE_BIT);
++ PGSR2 = (PGSR2 & ~TOSA_GPIO_HIGH_STROBE_BIT);
++
++ GPDR0 = 0xC3810940;
++ GPDR1 = 0xFCFFAB82;
++ GPDR2 = 0x000F501f;
++// write_scoop_reg(&tosascoop_device.dev,SCOOP_GPWR,0);
++// only debug
++reset_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_NOTE_LED);
++}
++
++void tosa_postsuspend(void)
++{
++ int i;
++// only debug
++set_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_NOTE_LED);
++
++ for(i=ARRAY_SIZE(dev_table);i;i--)
++ {
++ if(dev_table[i-1].resume)
++ driver_for_each_device(dev_table[i-1].driver, NULL,
++ NULL, dev_table[i-1].resume);
++ }
++ tosa_ac97_init();
++ PMCR = 0x01;
++}
++
++void tosa_postresume(void)
++{
++ tosa_ac97_exit();
++}
++
++/*
++ * Check what brought us out of the suspend.
++ * Return: 0 to sleep, otherwise wake
++ */
++static int tosa_should_wakeup(unsigned int resume_on_alarm)
++{
++ int is_resume = 0;
++
++ dev_dbg(sharpsl_pm.dev, "GPLR0 = %x,%x\n", GPLR0, PEDR);
++
++ if ((PEDR & GPIO_bit(TOSA_GPIO_AC_IN))) {
++ if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) {
++ /* charge on */
++ dev_dbg(sharpsl_pm.dev, "ac insert\n");
++ sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG;
++ } else {
++ /* charge off */
++ dev_dbg(sharpsl_pm.dev, "ac remove\n");
++ sharpsl_pm_led(SHARPSL_LED_OFF);
++ sharpsl_pm.machinfo->charge(0);
++ sharpsl_pm.charge_mode = CHRG_OFF;
++ }
++ }
++
++ if ((PEDR & GPIO_bit(GPIO_bit(TOSA_GPIO_BAT0_CRG))))
++ dev_dbg(sharpsl_pm.dev, "Charge full interrupt\n");
++
++ if (PEDR & GPIO_bit(TOSA_GPIO_POWERON))
++ is_resume |= GPIO_bit(TOSA_GPIO_POWERON);
++
++ if (PEDR & GPIO_bit(TOSA_GPIO_ON_KEY))
++ is_resume |= GPIO_bit(TOSA_GPIO_ON_KEY);
++
++ if (resume_on_alarm && (PEDR & PWER_RTC))
++ is_resume |= PWER_RTC;
++
++ return is_resume;
++}
++
++static unsigned long tosa_charger_wakeup(void)
++{
++// return ~GPLR0 & ( GPIO_bit(TOSA_GPIO_AC_IN) | GPIO_bit(TOSA_GPIO_POWERON) | GPIO_bit(TOSA_GPIO_ON_KEY) );
++ return (~GPLR0 & ( GPIO_bit(TOSA_GPIO_POWERON) | GPIO_bit(TOSA_GPIO_ON_KEY) )) | (GPLR0 & GPIO_bit(TOSA_GPIO_AC_IN));
++}
++
++unsigned long tosa_read_bat(void)
++{
++ unsigned long value;
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON);
++ mdelay(5);
++ value = tosa_read_aux_adc(WM97XX_AUX_ID3);
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON);
++ return value;
++}
++
++unsigned long tosapm_read_devdata(int type)
++{
++ switch(type) {
++ case SHARPSL_STATUS_ACIN:
++ return ((GPLR(TOSA_GPIO_AC_IN) & GPIO_bit(TOSA_GPIO_AC_IN)) == 0);
++ case SHARPSL_STATUS_LOCK:
++ return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batlock);
++ case SHARPSL_STATUS_CHRGFULL:
++ return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batfull);
++ case SHARPSL_STATUS_FATAL:
++ return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_fatal);
++ case SHARPSL_ACIN_VOLT:
++ return 1000; // not used on tosa
++ case SHARPSL_BATT_TEMP:
++ return tosa_read_aux_adc(WM97XX_AUX_ID2);
++ case SHARPSL_BATT_VOLT:
++ return tosa_read_bat();
++ default:
++ return tosa_read_bat();
++ }
++}
++
++static struct sharpsl_charger_machinfo tosa_pm_machinfo = {
++ .init = tosa_charger_init,
++ .exit = sharpsl_pm_pxa_remove,
++ .gpio_batlock = TOSA_GPIO_BAT_LOCKED,
++ .gpio_acin = TOSA_GPIO_AC_IN,
++ .gpio_batfull = TOSA_GPIO_BAT0_CRG,
++ .batfull_irq = 0,
++ .discharge = tosa_discharge,
++ .discharge1 = tosa_discharge1,
++ .charge = tosa_charge,
++ .measure_temp = tosa_measure_temp,
++ .presuspend = tosa_presuspend,
++ .postsuspend = tosa_postsuspend,
++ .postresume = tosa_postresume,
++ .read_devdata = tosapm_read_devdata,
++ .charger_wakeup = tosa_charger_wakeup,
++ .should_wakeup = tosa_should_wakeup,
++ .backlight_limit = corgibl_limit_intensity,
++ .backlight_get_status= tosa_bl_intensity,
++ .bat_levels = 6,
++ .bat_levels_noac = tosa_battery_levels,
++ .bat_levels_acin = tosa_battery_levels,
++ .bat_levels_noac_bl = tosa_battery_levels_bl,
++ .bat_levels_acin_bl = tosa_battery_levels_bl,
++ .charge_on_volt = 1200, // 2.9V TOSA_MAIN_BATTERY_ERR_THRESH voltage < 1200 -> error
++ .charge_on_temp = 3600, // -- TOSA_MAIN_BATTERY_EXIST_THRESH temp > 3600 -> error, no battery
++ .charge_acin_high = 1500, // not used default value
++ .charge_acin_low = 500, // not used default value
++ .fatal_acin_volt = 1572, // 3.8V
++ .fatal_noacin_volt= 1551, // 3.75V
++ .status_high_acin = 1564, // 3.78V
++ .status_low_acin = 1510, // 3.65V
++ .status_high_noac = 1564, // 3.78V
++ .status_low_noac = 1510, // 3.65V
++};
++
++static struct platform_device *tosapm_device;
++
++static int __devinit tosapm_init(void)
++{
++ int ret;
++
++ tosapm_device = platform_device_alloc("sharpsl-pm", -1);
++ if (!tosapm_device)
++ return -ENOMEM;
++
++ tosapm_device->dev.platform_data = &tosa_pm_machinfo;
++ ret = platform_device_add(tosapm_device);
++
++ if (ret)
++ platform_device_put(tosapm_device);
++
++ return ret;
++}
++
++static void tosapm_exit(void)
++{
++ int i;
++
++ // restore the resume / suspend handler
++ for(i=0;i<ARRAY_SIZE(dev_table);i++)
++ {
++ if (dev_table[i].driver)
++ {
++ dev_table[i].driver->resume = dev_table[i].resume;
++ dev_table[i].driver->suspend = dev_table[i].suspend;
++ dev_table[i].resume = NULL;
++ dev_table[i].suspend = NULL;
++ put_driver(dev_table[i].driver);
++ }
++ }
++
++ if (wm9712)
++ driver_unregister(&tosa_pm_driver);
++
++ platform_device_unregister(tosapm_device);
++}
++
++module_init(tosapm_init);
++module_exit(tosapm_exit);
+Index: linux-2.6.17/arch/arm/mach-pxa/Kconfig
+===================================================================
+--- linux-2.6.17.orig/arch/arm/mach-pxa/Kconfig 2006-09-19 20:51:40.160810500 +0200
++++ linux-2.6.17/arch/arm/mach-pxa/Kconfig 2006-09-19 21:08:04.926354500 +0200
+@@ -110,6 +110,7 @@ config MACH_TOSA
+ bool "Enable Sharp SL-6000x (Tosa) Support"
+ depends PXA_SHARPSL_25x
+ select TOSHIBA_TC6393XB
++ select SHARPSL_PM
+
+ config PXA25x
+ bool
+Index: linux-2.6.17/arch/arm/mach-pxa/Makefile
+===================================================================
+--- linux-2.6.17.orig/arch/arm/mach-pxa/Makefile 2006-09-19 20:51:33.984424500 +0200
++++ linux-2.6.17/arch/arm/mach-pxa/Makefile 2006-09-19 21:08:04.922354250 +0200
+@@ -16,7 +16,7 @@ obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o
+ obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o
+ obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o
+ obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o
+-obj-$(CONFIG_MACH_TOSA) += tosa.o
++obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o
+ obj-$(CONFIG_MACH_EM_X270) += em-x270.o
+ obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o
+
+Index: linux-2.6.17/arch/arm/mach-pxa/tosa_pm.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/arch/arm/mach-pxa/tosa_pm.c 2006-09-19 21:08:34.476201250 +0200
+@@ -0,0 +1,661 @@
++/*
++ * Battery and Power Management code for the Sharp SL-6000x
++ *
++ * Copyright (c) 2005 Dirk Opfer
++ *
++ * 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.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/stat.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/pm.h>
++#include <linux/wm97xx.h>
++
++#include <asm/apm.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++#include <asm/hardware.h>
++#include <asm/hardware/scoop.h>
++#include <asm/hardware/tmio.h>
++
++#include <asm/arch/sharpsl.h>
++#include <asm/arch/tosa.h>
++#include <asm/arch/pxa-regs.h>
++#include <sound/soc.h>
++#include <sound/ac97_codec.h>
++#include "sharpsl.h"
++
++extern int tosa_bl_intensity(void);
++volatile static int ad_polling;
++static int tosa_pm_driver_probe(struct device *dev);
++static int tosa_pm_driver_remove(struct device *dev);
++static struct wm97xx *wm9712;
++
++/************************************************************
++ * AC97 functions
++ ************************************************************/
++#define AC97_TIMEOUT_VAL 0x1000000
++
++#define AC97_MISC_MODEM_STAT 0x0056
++#define AC97_GPIO_CONFIG 0x004C
++
++static u16 tosa_ac97_read(unsigned short reg)
++{
++ volatile u32 *reg_addr;
++ volatile int timeout;
++ unsigned short data;
++
++ if (CAR & CAR_CAIP) {
++ printk(KERN_CRIT ": CAR_CAIP already set\n");
++ return 0;
++ }
++
++ if (reg == AC97_GPIO_STATUS)
++ reg_addr = &PMC_REG_BASE;
++ else
++ reg_addr = &PAC_REG_BASE;
++
++ reg_addr += (reg >> 1);
++
++ data=0;
++ GSR = GSR_CDONE | GSR_SDONE;
++
++ data = *reg_addr;
++ timeout = 0;
++
++ while(((GSR & GSR_SDONE)) == 0 && (timeout++ < AC97_TIMEOUT_VAL));
++
++ if ((timeout >= AC97_TIMEOUT_VAL)) {
++ GSR = GSR;
++ printk(KERN_CRIT ": AC97 is busy1.\n");
++ return data;
++ }
++
++ // actual read
++ GSR = GSR_CDONE | GSR_SDONE;
++ data = *reg_addr;
++
++ timeout = 0;
++ while(((GSR & GSR_SDONE) == 0) && (timeout++<AC97_TIMEOUT_VAL));
++ if ((timeout >= AC97_TIMEOUT_VAL)) {
++ GSR = GSR;
++ printk(KERN_CRIT ": AC97 is busy2.\n");
++ return data;
++ }
++
++ return data;
++}
++
++static void tosa_ac97_write(unsigned short reg, unsigned short val)
++{
++ volatile u32 *reg_addr;
++ volatile int timeout=0;
++
++ if (CAR & CAR_CAIP) {
++ printk(KERN_CRIT ": CAR_CAIP already set\n");
++ return;
++ }
++
++ GSR = GSR_CDONE | GSR_SDONE;
++ if (reg == AC97_GPIO_STATUS)
++ reg_addr = &PMC_REG_BASE;
++ else
++ reg_addr = &PAC_REG_BASE;
++
++ reg_addr += (reg >> 1);
++
++ *reg_addr = val;
++ while(((GSR & GSR_CDONE) == 0) && (timeout++ < AC97_TIMEOUT_VAL));
++ if (timeout >= AC97_TIMEOUT_VAL) {
++ printk(KERN_CRIT ": AC97 is busy.\n");
++ }
++}
++
++static void tosa_ac97_bit_clear(u8 reg, u16 val)
++{
++ unsigned short dat = tosa_ac97_read(reg);
++ dat &= ~val;
++ tosa_ac97_write(reg, dat);
++}
++
++static void tosa_ac97_bit_set(u8 reg, u16 val)
++{
++ unsigned short dat = tosa_ac97_read(reg);
++ dat |= val;
++ tosa_ac97_write(reg, dat);
++}
++
++
++static int tosa_ac97_init(void)
++{
++ int timeo;
++
++ pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
++ pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
++ pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
++ pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
++ pxa_gpio_mode(GPIO20_DREQ0_MD);
++
++ pxa_set_cken(CKEN2_AC97, 1);
++ /* AC97 power on sequense */
++ while ( 1 ) {
++ GCR = 0;
++ udelay(100);
++ GCR |= GCR_COLD_RST;
++ udelay(5);
++ GCR |= GCR_WARM_RST;
++ udelay(5);
++ for ( timeo = 0x10000; timeo > 0; timeo-- ) {
++ if ( GSR & GSR_PCR ) break;
++ mdelay(5);
++ }
++ if( timeo > 0 ) break;
++ printk(KERN_WARNING "AC97 power on retry!!\n");
++ }
++
++ tosa_ac97_write(AC97_EXTENDED_STATUS, 1);
++ /*
++ * Setting AC97 GPIO
++ * i/o function
++ * GPIO1: input EAR_IN signal
++ * GPIO2: output IRQ signal
++ * GPIO3: output PENDOWN signal
++ * GPIO4: input MASK signal
++ * GPIO5: input DETECT MIC signal
++ */
++ // AC97_GPIO_FUNC AC97_MISC_MODEM_STAT
++
++ tosa_ac97_bit_clear(AC97_MISC_MODEM_STAT,
++ ((1<<2)|(1<<3)|(1<<4)|(1<<5)));
++ tosa_ac97_bit_clear(AC97_GPIO_CONFIG,(1<<2)|(1<<3));
++ tosa_ac97_bit_set(AC97_GPIO_CONFIG, (1<<1)|(1<<4)|(1<<5));
++
++ tosa_ac97_write(AC97_WM97XX_DIGITISER2, 0xc009);
++ tosa_ac97_write(AC97_WM97XX_DIGITISER1, 0x0030 | WM97XX_DELAY(4));
++
++ pxa_gpio_mode(GPIO32_SDATA_IN1_AC97_MD);
++ ad_polling = 1;
++ printk("tosa_ac97_init\n");
++}
++
++void tosa_ac97_exit(void)
++{
++ if (!(CKEN & CKEN2_AC97))
++ return;
++
++ // power down the whole chip
++ tosa_ac97_write(AC97_POWERDOWN, 0x7fff);
++
++// GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN);
++// GSR = GSR;
++// GCR = GCR_ACLINK_OFF;
++ pxa_set_cken(CKEN2_AC97, 0);
++ /* switch back to irq driver */
++ ad_polling = 0;
++ printk("tosa_ac97_exit\n");
++}
++
++int ac97_ad_input(u16 adcsel)
++{
++ unsigned short val = 0;
++ unsigned long timeout;
++
++ // prepare
++ tosa_ac97_read(AC97_WM97XX_DIGITISER_RD);
++
++ if (adcsel & 0x8000)
++ adcsel = ((adcsel & 0x7fff) + 3) << 12;
++
++ /* Conversion start */
++ tosa_ac97_write(AC97_WM97XX_DIGITISER1, (adcsel | WM97XX_POLL | WM97XX_DELAY(4)));
++ timeout = 0x1000;
++ /* wait for POLL to go low */
++ while ((tosa_ac97_read(AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) {
++ udelay(100);
++ timeout--;
++ }
++
++ val = tosa_ac97_read(AC97_WM97XX_DIGITISER_RD);
++
++ val &= 0xFFF ;
++
++ return val;
++}
++
++
++int tosa_read_aux_adc(u16 adcsel)
++{
++ if (ad_polling)
++ return (ac97_ad_input(adcsel));
++ else
++ return (wm97xx_read_aux_adc(wm9712, adcsel));
++}
++
++static struct device_driver tosa_pm_driver = {
++ .name = "wm97xx-battery",
++ .bus = &wm97xx_bus_type,
++ .owner = THIS_MODULE,
++ .probe = tosa_pm_driver_probe,
++ .remove = tosa_pm_driver_remove,
++};
++
++#if 0
++#define TOSA_TEMP_READ_WAIT_TIME (5) // 5msec [Fix]
++int tosa_read_battery(struct wm97xx* wm, int channel)
++{
++ //return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, input));
++
++ int wm_aux,i;
++ int value = 0;
++ int clear_mux;
++
++ switch(channel) {
++
++ case 0: // Main
++
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON);
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON);
++ wm_aux = WM97XX_AUX_ID3;
++ break;
++
++ case 1: // Jacket
++
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_V_ON);
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT_SW_ON);
++ wm_aux = WM97XX_AUX_ID3;
++ break;
++
++ case 2: // BU
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BU_CHRG_ON);
++ wm_aux = WM97XX_AUX_ID4;
++ break;
++
++ case 3: // Main Temp
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON);
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON);
++ wm_aux = WM97XX_AUX_ID2;
++ break;
++
++ case 4: // Jacket Temp
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON);
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON);
++ wm_aux = WM97XX_AUX_ID2;
++ break;
++
++ default:
++ return -1;
++ }
++
++ mdelay(TOSA_TEMP_READ_WAIT_TIME);
++ for(i=0;i<4;i++)
++ {
++ if (wm9712)
++ value += wm97xx_read_aux_adc(wm, wm_aux);
++ else
++ value += ac97_ad_input(wm, wm_aux);
++ }
++
++ value>>=2;
++ // reset the multiplexer
++ clear_mux = TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON | TOSA_TC6393_BAT0_TH_ON | TOSA_TC6393_BAT1_TH_ON | TOSA_TC6393_BU_CHRG_ON;
++
++ return value;
++
++}
++#endif
++
++/* BL 5-3 */
++struct battery_thresh tosa_battery_levels_bl[] = {
++ { 1663, 100 },
++ { 1605, 75 },
++ { 1564, 50 },
++ { 1510, 25 },
++ { 1435, 5 },
++ { 0, 0 },
++};
++
++/* BL 2-0 */
++struct battery_thresh tosa_battery_levels[] = {
++ { 1679, 100 },
++ { 1617, 75 },
++ { 1576, 50 },
++ { 1530, 25 },
++ { 1448, 5 },
++ { 0, 0 },
++};
++
++struct pm_devices {
++ const char * name;
++ struct bus_type *bus;
++ struct device_driver *driver;
++ struct device * dev;
++ int (*resume)(struct device *dev, void * data); // int (*resume)(struct device *dev);
++ int (*suspend)(struct device *dev, void * data);// int (*suspend)(struct device *dev, pm_message_t state);
++};
++
++/* Ugly
++ We need the following devices to measure the battery and control the charger:
++ Also we need access to these before we sleep and immediatly after we resume so we can't
++ control their pm via the kernel device manager. To access their pm functions we will backup
++ the suspend and resume handler and clear these pointers.
++ After that we can suspend and resume these devices.
++
++ Don't change the order of this table!!!!!
++*/
++static struct pm_devices dev_table[] = {
++ [0] = {
++ .name = TMIO_SOC_NAME,
++ .bus = &platform_bus_type,
++ },
++};
++
++static int tosa_pm_driver_probe(struct device *dev)
++{
++ wm9712 = dev->driver_data;
++ ad_polling = 0;
++ return 0;
++}
++
++static int tosa_pm_driver_remove(struct device *dev)
++{
++ wm9712 = NULL;
++ return 0;
++}
++
++
++static void tosa_charger_init(void)
++{
++ int i;
++
++ /* If this driver doesn't register, bad things will happen, Tosa won't boot,
++ and the world will possibly explode */
++ i = driver_register(&tosa_pm_driver);
++ if (i < 0)
++ panic("Cannot register the tosa_pm driver on the wm97xx bus. Halting.");
++
++ for(i=0;i<ARRAY_SIZE(dev_table);i++)
++ {
++ dev_table[i].driver = driver_find(dev_table[i].name, dev_table[i].bus);
++ if (dev_table[i].driver)
++ {
++ dev_table[i].resume = dev_table[i].driver->resume;
++ dev_table[i].suspend = dev_table[i].driver->suspend;
++ dev_table[i].driver->resume = NULL;
++ dev_table[i].driver->suspend = NULL;
++ }
++ }
++
++ pxa_gpio_mode(TOSA_GPIO_AC_IN | GPIO_IN);
++ pxa_gpio_mode(TOSA_GPIO_BAT0_CRG | GPIO_IN);
++ pxa_gpio_mode(TOSA_GPIO_BAT1_CRG | GPIO_IN);
++ pxa_gpio_mode(TOSA_GPIO_BAT0_LOW | GPIO_IN);
++ pxa_gpio_mode(TOSA_GPIO_BAT1_LOW | GPIO_IN);
++
++ pxa_gpio_mode(TOSA_GPIO_JACKET_DETECT | GPIO_IN);
++ pxa_gpio_mode(TOSA_GPIO_POWERON | GPIO_IN);
++ sharpsl_pm_pxa_init();
++}
++
++
++static void tosa_measure_temp(int on)
++{
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON | TOSA_TC6393_BAT1_TH_ON);
++
++ if (on)
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON);
++
++}
++
++static void tosa_charge(int on)
++{
++ if(on)
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_CHARGE_OFF);
++ else
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_CHARGE_OFF);
++}
++
++static void tosa_discharge1(int on)
++{
++}
++
++static void tosa_discharge(int on)
++{
++}
++
++
++static void tosa_presuspend(void)
++{
++ int i;
++ unsigned long wakeup_mask;
++
++ // put remaining devices into sleep
++ for(i=0;i<ARRAY_SIZE(dev_table);i++)
++ {
++ if(dev_table[i].suspend)
++ driver_for_each_device(dev_table[i].driver, NULL,
++ (void*)&PMSG_SUSPEND, dev_table[i].suspend);
++ }
++
++ tosa_ac97_exit();
++
++ wakeup_mask = GPIO_bit(TOSA_GPIO_POWERON) | GPIO_bit(TOSA_GPIO_ON_KEY) | GPIO_bit(TOSA_GPIO_AC_IN);
++
++ wakeup_mask |= GPIO_bit(TOSA_GPIO_BAT0_LOW);
++ PWER = wakeup_mask | PWER_RTC;
++
++ PRER = wakeup_mask;
++ PFER = wakeup_mask;
++
++ for (i = 0; i <=15; i++) {
++ if (PRER & PFER & GPIO_bit(i)) {
++ if (GPLR0 & GPIO_bit(i) )
++ PRER &= ~GPIO_bit(i);
++ else
++ PFER &= ~GPIO_bit(i);
++ }
++ }
++
++ /* Clear reset status */
++ RCSR = RCSR_HWR | RCSR_WDR | RCSR_SMR | RCSR_GPR;
++
++ /* Stop 3.6MHz and drive HIGH to PCMCIA and CS */
++ PCFR = PCFR_OPDE;
++
++ /* Resume on keyboard power key */
++ PGSR1 = (PGSR1 & ~TOSA_GPIO_LOW_STROBE_BIT);
++ PGSR2 = (PGSR2 & ~TOSA_GPIO_HIGH_STROBE_BIT);
++
++ GPDR0 = 0xC3810940;
++ GPDR1 = 0xFCFFAB82;
++ GPDR2 = 0x000F501f;
++// write_scoop_reg(&tosascoop_device.dev,SCOOP_GPWR,0);
++// only debug
++reset_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_NOTE_LED);
++}
++
++void tosa_postsuspend(void)
++{
++ int i;
++// only debug
++set_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_NOTE_LED);
++
++ for(i=ARRAY_SIZE(dev_table);i;i--)
++ {
++ if(dev_table[i-1].resume)
++ driver_for_each_device(dev_table[i-1].driver, NULL,
++ NULL, dev_table[i-1].resume);
++ }
++ tosa_ac97_init();
++ PMCR = 0x01;
++}
++
++void tosa_postresume(void)
++{
++ tosa_ac97_exit();
++}
++
++/*
++ * Check what brought us out of the suspend.
++ * Return: 0 to sleep, otherwise wake
++ */
++static int tosa_should_wakeup(unsigned int resume_on_alarm)
++{
++ int is_resume = 0;
++
++ dev_dbg(sharpsl_pm.dev, "GPLR0 = %x,%x\n", GPLR0, PEDR);
++
++ if ((PEDR & GPIO_bit(TOSA_GPIO_AC_IN))) {
++ if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) {
++ /* charge on */
++ dev_dbg(sharpsl_pm.dev, "ac insert\n");
++ sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG;
++ } else {
++ /* charge off */
++ dev_dbg(sharpsl_pm.dev, "ac remove\n");
++ sharpsl_pm_led(SHARPSL_LED_OFF);
++ sharpsl_pm.machinfo->charge(0);
++ sharpsl_pm.charge_mode = CHRG_OFF;
++ }
++ }
++
++ if ((PEDR & GPIO_bit(GPIO_bit(TOSA_GPIO_BAT0_CRG))))
++ dev_dbg(sharpsl_pm.dev, "Charge full interrupt\n");
++
++ if (PEDR & GPIO_bit(TOSA_GPIO_POWERON))
++ is_resume |= GPIO_bit(TOSA_GPIO_POWERON);
++
++ if (PEDR & GPIO_bit(TOSA_GPIO_ON_KEY))
++ is_resume |= GPIO_bit(TOSA_GPIO_ON_KEY);
++
++ if (resume_on_alarm && (PEDR & PWER_RTC))
++ is_resume |= PWER_RTC;
++
++ return is_resume;
++}
++
++static unsigned long tosa_charger_wakeup(void)
++{
++// return ~GPLR0 & ( GPIO_bit(TOSA_GPIO_AC_IN) | GPIO_bit(TOSA_GPIO_POWERON) | GPIO_bit(TOSA_GPIO_ON_KEY) );
++ return (~GPLR0 & ( GPIO_bit(TOSA_GPIO_POWERON) | GPIO_bit(TOSA_GPIO_ON_KEY) )) | (GPLR0 & GPIO_bit(TOSA_GPIO_AC_IN));
++}
++
++unsigned long tosa_read_bat(void)
++{
++ unsigned long value;
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON);
++ mdelay(5);
++ value = tosa_read_aux_adc(WM97XX_AUX_ID3);
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON);
++ return value;
++}
++
++unsigned long tosapm_read_devdata(int type)
++{
++ switch(type) {
++ case SHARPSL_STATUS_ACIN:
++ return ((GPLR(TOSA_GPIO_AC_IN) & GPIO_bit(TOSA_GPIO_AC_IN)) == 0);
++ case SHARPSL_STATUS_LOCK:
++ return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batlock);
++ case SHARPSL_STATUS_CHRGFULL:
++ return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batfull);
++ case SHARPSL_STATUS_FATAL:
++ return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_fatal);
++ case SHARPSL_ACIN_VOLT:
++ return 1000; // not used on tosa
++ case SHARPSL_BATT_TEMP:
++ return tosa_read_aux_adc(WM97XX_AUX_ID2);
++ case SHARPSL_BATT_VOLT:
++ return tosa_read_bat();
++ default:
++ return tosa_read_bat();
++ }
++}
++
++static struct sharpsl_charger_machinfo tosa_pm_machinfo = {
++ .init = tosa_charger_init,
++ .exit = sharpsl_pm_pxa_remove,
++ .gpio_batlock = TOSA_GPIO_BAT_LOCKED,
++ .gpio_acin = TOSA_GPIO_AC_IN,
++ .gpio_batfull = TOSA_GPIO_BAT0_CRG,
++ .batfull_irq = 0,
++ .discharge = tosa_discharge,
++ .discharge1 = tosa_discharge1,
++ .charge = tosa_charge,
++ .measure_temp = tosa_measure_temp,
++ .presuspend = tosa_presuspend,
++ .postsuspend = tosa_postsuspend,
++ .postresume = tosa_postresume,
++ .read_devdata = tosapm_read_devdata,
++ .charger_wakeup = tosa_charger_wakeup,
++ .should_wakeup = tosa_should_wakeup,
++ .backlight_limit = corgibl_limit_intensity,
++ .backlight_get_status= tosa_bl_intensity,
++ .bat_levels = 6,
++ .bat_levels_noac = tosa_battery_levels,
++ .bat_levels_acin = tosa_battery_levels,
++ .bat_levels_noac_bl = tosa_battery_levels_bl,
++ .bat_levels_acin_bl = tosa_battery_levels_bl,
++ .charge_on_volt = 1200, // 2.9V TOSA_MAIN_BATTERY_ERR_THRESH voltage < 1200 -> error
++ .charge_on_temp = 3600, // -- TOSA_MAIN_BATTERY_EXIST_THRESH temp > 3600 -> error, no battery
++ .charge_acin_high = 1500, // not used default value
++ .charge_acin_low = 500, // not used default value
++ .fatal_acin_volt = 1572, // 3.8V
++ .fatal_noacin_volt= 1551, // 3.75V
++ .status_high_acin = 1564, // 3.78V
++ .status_low_acin = 1510, // 3.65V
++ .status_high_noac = 1564, // 3.78V
++ .status_low_noac = 1510, // 3.65V
++};
++
++static struct platform_device *tosapm_device;
++
++static int __devinit tosapm_init(void)
++{
++ int ret;
++
++ tosapm_device = platform_device_alloc("sharpsl-pm", -1);
++ if (!tosapm_device)
++ return -ENOMEM;
++
++ tosapm_device->dev.platform_data = &tosa_pm_machinfo;
++ ret = platform_device_add(tosapm_device);
++
++ if (ret)
++ platform_device_put(tosapm_device);
++
++ return ret;
++}
++
++static void tosapm_exit(void)
++{
++ int i;
++
++ // restore the resume / suspend handler
++ for(i=0;i<ARRAY_SIZE(dev_table);i++)
++ {
++ if (dev_table[i].driver)
++ {
++ dev_table[i].driver->resume = dev_table[i].resume;
++ dev_table[i].driver->suspend = dev_table[i].suspend;
++ dev_table[i].resume = NULL;
++ dev_table[i].suspend = NULL;
++ put_driver(dev_table[i].driver);
++ }
++ }
++
++ if (wm9712)
++ driver_unregister(&tosa_pm_driver);
++
++ platform_device_unregister(tosapm_device);
++}
++
++module_init(tosapm_init);
++module_exit(tosapm_exit);
+Index: linux-2.6.17/arch/arm/mach-pxa/Kconfig
+===================================================================
+--- linux-2.6.17.orig/arch/arm/mach-pxa/Kconfig 2006-09-19 20:51:40.160810500 +0200
++++ linux-2.6.17/arch/arm/mach-pxa/Kconfig 2006-09-19 21:08:04.926354500 +0200
+@@ -110,6 +110,7 @@ config MACH_TOSA
+ bool "Enable Sharp SL-6000x (Tosa) Support"
+ depends PXA_SHARPSL_25x
+ select TOSHIBA_TC6393XB
++ select SHARPSL_PM
+
+ config PXA25x
+ bool
diff --git a/packages/linux/linux-rp-2.6.23/tosa-pxaac97-r6-fix-r0.patch b/packages/linux/linux-rp-2.6.23/tosa-pxaac97-r6-fix-r0.patch
new file mode 100644
index 0000000000..6f6f540eff
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/tosa-pxaac97-r6-fix-r0.patch
@@ -0,0 +1,58 @@
+From 005693333f4b3e0495bb80cc3cfd812e3e6f0a30 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 00:48:42 +0400
+Subject: [PATCH] tosa-pxaac97-r6.patch fixes
+
+---
+ arch/arm/mach-pxa/tosa.c | 4 ++--
+ 1 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c
+index 059fa07..61536d4 100644
+--- a/arch/arm/mach-pxa/tosa.c
++++ b/arch/arm/mach-pxa/tosa.c
+@@ -310,10 +310,10 @@ static void __init tosa_init(void)
+ PMCR = 0x01;
+
+ // AC97 Disable all IRQ's
+- pxa_set_cken(CKEN2_AC97, 1);
++ pxa_set_cken(CKEN_AC97, 1);
+ GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN);
+ GSR = GSR;
+- pxa_set_cken(CKEN2_AC97, 0);
++ pxa_set_cken(CKEN_AC97, 0);
+
+ pxa_set_mci_info(&tosa_mci_platform_data);
+ pxa_set_udc_info(&udc_info);
+--
+1.4.4.4
+
+From 005693333f4b3e0495bb80cc3cfd812e3e6f0a30 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 00:48:42 +0400
+Subject: [PATCH] tosa-pxaac97-r6.patch fixes
+
+---
+ arch/arm/mach-pxa/tosa.c | 4 ++--
+ 1 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c
+index 059fa07..61536d4 100644
+--- a/arch/arm/mach-pxa/tosa.c
++++ b/arch/arm/mach-pxa/tosa.c
+@@ -310,10 +310,10 @@ static void __init tosa_init(void)
+ PMCR = 0x01;
+
+ // AC97 Disable all IRQ's
+- pxa_set_cken(CKEN2_AC97, 1);
++ pxa_set_cken(CKEN_AC97, 1);
+ GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN);
+ GSR = GSR;
+- pxa_set_cken(CKEN2_AC97, 0);
++ pxa_set_cken(CKEN_AC97, 0);
+
+ pxa_set_mci_info(&tosa_mci_platform_data);
+ pxa_set_udc_info(&udc_info);
+--
+1.4.4.4
+
diff --git a/packages/linux/linux-rp-2.6.23/tosa-tmio-lcd-r10-fix-r0.patch b/packages/linux/linux-rp-2.6.23/tosa-tmio-lcd-r10-fix-r0.patch
new file mode 100644
index 0000000000..73913bccc8
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/tosa-tmio-lcd-r10-fix-r0.patch
@@ -0,0 +1,70 @@
+From bb3ed6577c592d86f0976a92978c9454bbdfbe59 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 02:01:23 +0400
+Subject: [PATCH] tosa-tmio-lcd-r10.patch fixes
+
+---
+ arch/arm/mach-pxa/tosa_lcd.c | 5 +++--
+ 1 files changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa_lcd.c b/arch/arm/mach-pxa/tosa_lcd.c
+index eeeee3e..d52f63f 100644
+--- a/arch/arm/mach-pxa/tosa_lcd.c
++++ b/arch/arm/mach-pxa/tosa_lcd.c
+@@ -66,7 +66,7 @@ static unsigned short normal_i2c[] = {
+ };
+ I2C_CLIENT_INSMOD;
+
+-static struct corgibl_machinfo tosa_bl_machinfo = {
++static struct generic_bl_info tosa_bl_machinfo = {
+ .max_intensity = 255,
+ .default_intensity = 68,
+ .limit_mask = 0x0b,
+@@ -80,7 +80,8 @@ int tosa_bl_intensity(void)
+
+ static void pxa_nssp_output(unsigned char reg, unsigned char data)
+ {
+- unsigned long flag, dummy;
++ unsigned long flag;
++ u32 dummy;
+ u32 dat = ( ((reg << 5) & 0xe0) | (data & 0x1f) );
+ spin_lock_irqsave(&tosa_nssp_lock, flag);
+
+--
+1.4.4.4
+
+From bb3ed6577c592d86f0976a92978c9454bbdfbe59 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 19 Oct 2007 02:01:23 +0400
+Subject: [PATCH] tosa-tmio-lcd-r10.patch fixes
+
+---
+ arch/arm/mach-pxa/tosa_lcd.c | 5 +++--
+ 1 files changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa_lcd.c b/arch/arm/mach-pxa/tosa_lcd.c
+index eeeee3e..d52f63f 100644
+--- a/arch/arm/mach-pxa/tosa_lcd.c
++++ b/arch/arm/mach-pxa/tosa_lcd.c
+@@ -66,7 +66,7 @@ static unsigned short normal_i2c[] = {
+ };
+ I2C_CLIENT_INSMOD;
+
+-static struct corgibl_machinfo tosa_bl_machinfo = {
++static struct generic_bl_info tosa_bl_machinfo = {
+ .max_intensity = 255,
+ .default_intensity = 68,
+ .limit_mask = 0x0b,
+@@ -80,7 +80,8 @@ int tosa_bl_intensity(void)
+
+ static void pxa_nssp_output(unsigned char reg, unsigned char data)
+ {
+- unsigned long flag, dummy;
++ unsigned long flag;
++ u32 dummy;
+ u32 dat = ( ((reg << 5) & 0xe0) | (data & 0x1f) );
+ spin_lock_irqsave(&tosa_nssp_lock, flag);
+
+--
+1.4.4.4
+
diff --git a/packages/linux/linux-rp-2.6.23/tosa-tmio-lcd-r10.patch b/packages/linux/linux-rp-2.6.23/tosa-tmio-lcd-r10.patch
new file mode 100644
index 0000000000..cd24cc6cdc
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/tosa-tmio-lcd-r10.patch
@@ -0,0 +1,928 @@
+ arch/arm/mach-pxa/Kconfig | 5
+ arch/arm/mach-pxa/Makefile | 2
+ arch/arm/mach-pxa/tosa.c | 49 +++++-
+ arch/arm/mach-pxa/tosa_lcd.c | 344 +++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 396 insertions(+), 4 deletions(-)
+
+Index: git/arch/arm/mach-pxa/Makefile
+===================================================================
+--- git.orig/arch/arm/mach-pxa/Makefile 2006-11-07 22:13:10.000000000 +0000
++++ git/arch/arm/mach-pxa/Makefile 2006-11-07 23:29:38.000000000 +0000
+@@ -17,7 +17,7 @@ obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o
+ obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o
+ obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o
+ obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o
+-obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o
++obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o tosa_lcd.o
+ obj-$(CONFIG_MACH_EM_X270) += em-x270.o
+ obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o
+
+Index: git/arch/arm/mach-pxa/tosa_lcd.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ git/arch/arm/mach-pxa/tosa_lcd.c 2006-11-07 23:29:25.000000000 +0000
+@@ -0,0 +1,344 @@
++/*
++ * LCD / Backlight control code for Sharp SL-6000x (tosa)
++ *
++ * Copyright (c) 2005 Dirk Opfer
++ *
++ * 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.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/i2c.h>
++#include <linux/fb.h>
++
++#include <asm/mach/sharpsl_param.h>
++#include <asm/hardware.h>
++#include <asm/hardware/scoop.h>
++#include <asm/hardware/tmio.h>
++#include <asm/arch/ssp.h>
++#include <asm/arch/sharpsl.h>
++#include <asm/arch/tosa.h>
++#include <asm/arch/pxa-regs.h>
++
++#define DAC_BASE 0x4e
++#define DAC_CH1 0
++#define DAC_CH2 1
++
++#define TG_REG0_VQV 0x0001
++#define TG_REG0_COLOR 0x0002
++#define TG_REG0_UD 0x0004
++#define TG_REG0_LR 0x0008
++#define COMADJ_DEFAULT 97
++#define TOSA_LCD_I2C_DEVICEID 0x4711 // Fixme: new value
++
++static void tosa_lcd_tg_init(struct device *dev);
++static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode);
++static void tosa_lcd_tg_off(struct device *dev);
++static void tosa_set_backlight(int intensity);
++
++const static struct tmio_lcd_ops tosa_tc6393_lcd_ops = {
++ .init = tosa_lcd_tg_init,
++ .tg_on = tosa_lcd_tg_on,
++ .tg_off = tosa_lcd_tg_off,
++};
++
++static struct platform_device *tosabl_device;
++static struct i2c_driver tosa_driver;
++static struct i2c_client* tosa_i2c_dac;
++static int initialised;
++static int comadj;
++static int bl_intensity;
++static struct ssp_dev tosa_nssp_dev;
++static struct ssp_state tosa_nssp_state;
++static spinlock_t tosa_nssp_lock;
++
++static unsigned short normal_i2c[] = {
++ DAC_BASE,
++ I2C_CLIENT_END
++};
++I2C_CLIENT_INSMOD;
++
++static struct corgibl_machinfo tosa_bl_machinfo = {
++ .max_intensity = 255,
++ .default_intensity = 68,
++ .limit_mask = 0x0b,
++ .set_bl_intensity = tosa_set_backlight,
++};
++
++int tosa_bl_intensity(void)
++{
++ return bl_intensity;
++}
++
++static void pxa_nssp_output(unsigned char reg, unsigned char data)
++{
++ unsigned long flag, dummy;
++ u32 dat = ( ((reg << 5) & 0xe0) | (data & 0x1f) );
++ spin_lock_irqsave(&tosa_nssp_lock, flag);
++
++ ssp_config(&tosa_nssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), 0, 0, SSCR0_SerClkDiv(128));
++ ssp_enable(&tosa_nssp_dev);
++
++ ssp_write_word(&tosa_nssp_dev,dat);
++
++ /* Read null data back from device to prevent SSP overflow */
++ ssp_read_word(&tosa_nssp_dev, &dummy);
++ ssp_disable(&tosa_nssp_dev);
++ spin_unlock_irqrestore(&tosa_nssp_lock, flag);
++
++}
++
++static void tosa_set_backlight(int intensity)
++{
++ if (!tosa_i2c_dac)
++ return;
++
++ bl_intensity = intensity;
++ /* SetBacklightDuty */
++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH2, (unsigned char)intensity);
++
++ /* SetBacklightVR */
++ if (intensity)
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BL_C20MA);
++ else
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BL_C20MA);
++
++ /* bl_enable GP04=1 otherwise GP04=0*/
++ pxa_nssp_output(TG_GPODR2, intensity ? 0x01 : 0x00);
++}
++
++static void tosa_lcd_tg_init(struct device *dev)
++{
++ /* L3V On */
++ set_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON);
++ mdelay(60);
++
++ /* TG On */
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_TG_ON);
++ mdelay(60);
++
++ pxa_nssp_output(TG_TPOSCTL,0x00); /* delayed 0clk TCTL signal for VGA */
++ pxa_nssp_output(TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */
++}
++
++static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode)
++{
++ const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR;
++ pxa_nssp_output(TG_PNLCTL, value | (mode->yres == 320 ? 0 : TG_REG0_VQV));
++
++ /* TG LCD pannel power up */
++ pxa_nssp_output(TG_PINICTL,0x4);
++ mdelay(50);
++
++ /* TG LCD GVSS */
++ pxa_nssp_output(TG_PINICTL,0x0);
++
++ if (!initialised)
++ {
++ /* after the pannel is powered up the first time, we can access the i2c bus */
++ /* so probe for the DAC */
++ i2c_add_driver(&tosa_driver);
++ initialised = 1;
++ mdelay(50);
++ }
++ if (tosa_i2c_dac)
++ /* set common voltage */
++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj);
++
++}
++
++static void tosa_lcd_tg_off(struct device *dev)
++{
++ /* TG LCD VHSA off */
++ pxa_nssp_output(TG_PINICTL,0x4);
++ mdelay(50);
++
++ /* TG LCD signal off */
++ pxa_nssp_output(TG_PINICTL,0x6);
++ mdelay(50);
++
++ /* TG Off */
++ set_tc6393_gpio(&tc6393_device.dev, TOSA_TC6393_TG_ON);
++ mdelay(100);
++
++ /* L3V Off */
++ reset_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON);
++}
++
++static int tosa_detect_client(struct i2c_adapter* adapter, int address, int kind) {
++ int err = 0;
++
++ printk("Tosa-LCD: DAC detected address:0x%2.2x\n",address);
++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA ))
++ goto ERROR0;
++
++ if (!(tosa_i2c_dac = (struct i2c_client*)kzalloc(sizeof(*tosa_i2c_dac), GFP_KERNEL))) {
++ err = -ENOMEM;
++ goto ERROR0;
++ }
++
++ //i2c_set_clientdata(tosa_i2c_dac, data);
++ tosa_i2c_dac->addr = address;
++ tosa_i2c_dac->adapter = adapter;
++ tosa_i2c_dac->driver = &tosa_driver;
++ tosa_i2c_dac->dev.parent = &tc6393_device.dev;
++ strcpy(tosa_i2c_dac->name, "tosa lcd");
++ if ((err = i2c_attach_client(tosa_i2c_dac)))
++ goto ERROR3;
++
++ /* Now i2c is ready, allocate the backlight device*/
++ tosabl_device = platform_device_alloc("corgi-bl", -1);
++ if (!tosabl_device) {
++ err = -ENOMEM;
++ goto ERROR4;
++ }
++
++ /* set parent device */
++ tosabl_device->dev.parent = &tosa_i2c_dac->dev;
++ tosabl_device->dev.platform_data = &tosa_bl_machinfo;
++
++ err = platform_device_add(tosabl_device);
++
++ if (err)
++ platform_device_put(tosabl_device);
++
++ /* set common voltage */
++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj);
++
++ return 0;
++ERROR4:
++ i2c_detach_client(tosa_i2c_dac);
++ERROR3:
++ kfree(tosa_i2c_dac);
++ERROR0:
++ return err;
++}
++
++static int tosa_attach_adapter(struct i2c_adapter* adapter) {
++ return i2c_probe(adapter, &addr_data, &tosa_detect_client);
++}
++
++static int tosa_detach_client(struct i2c_client* client) {
++ int err;
++
++ if ((err = i2c_detach_client(client))) {
++ printk(KERN_ERR "tosa: Cannot deregister client\n");
++ return err;
++ }
++ kfree(client);
++ return 0;
++}
++
++static struct i2c_driver tosa_driver={
++ .id = TOSA_LCD_I2C_DEVICEID,
++ .attach_adapter = tosa_attach_adapter,
++ .detach_client = tosa_detach_client,
++};
++
++static int __init tosa_lcd_probe(struct platform_device *pdev)
++{
++ int ret;
++ spin_lock_init(&tosa_nssp_lock);
++
++ if (!pdev->dev.platform_data)
++ return -EINVAL;
++
++ /* Set Common Voltage */
++ comadj = sharpsl_param.comadj == -1 ? COMADJ_DEFAULT : sharpsl_param.comadj;
++
++ ret=ssp_init(&tosa_nssp_dev,2,0);
++
++ /* initialize SSP */
++ pxa_gpio_mode(GPIO83_NSSP_TX);
++ pxa_gpio_mode(GPIO81_NSSP_CLK_OUT);
++ pxa_gpio_mode(GPIO82_NSSP_FRM_OUT);
++
++ if (ret)
++ printk(KERN_ERR "Unable to register NSSP handler!\n");
++ else {
++ struct tmio_lcd_ops* *tmio_ops = pdev->dev.platform_data;
++ ssp_disable(&tosa_nssp_dev);
++ initialised = 0;
++
++ /* Set the lcd functions */
++ *tmio_ops = (struct tmio_lcd_ops*) &tosa_tc6393_lcd_ops;
++ }
++
++ return ret;
++}
++
++static int tosa_lcd_remove(struct platform_device *pdev)
++{
++ /* delete the lcd functions */
++ struct tmio_lcd_ops* *tmio_ops = pdev->dev.platform_data;
++ *tmio_ops = NULL;
++
++ ssp_exit(&tosa_nssp_dev);
++
++ if (tosa_i2c_dac) {
++ i2c_detach_client(tosa_i2c_dac);
++ kfree(tosa_i2c_dac);
++ }
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++
++static int tosa_lcd_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ ssp_flush(&tosa_nssp_dev);
++ ssp_save_state(&tosa_nssp_dev,&tosa_nssp_state);
++ return 0;
++}
++
++static int tosa_lcd_resume(struct platform_device *pdev)
++{
++ printk("tosa_lcd_resume\n");
++ ssp_restore_state(&tosa_nssp_dev,&tosa_nssp_state);
++ ssp_enable(&tosa_nssp_dev);
++ printk("tosa_lcd_resume ok\n");
++ return 0;
++}
++#else
++
++#define tosa_lcd_suspend NULL
++#define tosa_lcd_resume NULL
++
++#endif
++
++
++static struct platform_driver tosalcd_driver = {
++ .probe = tosa_lcd_probe,
++ .remove = tosa_lcd_remove,
++ .suspend = tosa_lcd_suspend,
++ .resume = tosa_lcd_resume,
++ .driver = {
++ .name = "tosa-lcd",
++ },
++};
++
++static int __init tosa_lcd_init(void)
++{
++ return platform_driver_register(&tosalcd_driver);
++}
++
++static void __exit tosa_lcd_cleanup (void)
++{
++ platform_driver_unregister (&tosalcd_driver);
++}
++
++device_initcall(tosa_lcd_init);
++module_exit (tosa_lcd_cleanup);
++
++MODULE_DESCRIPTION ("Tosa LCD device");
++MODULE_AUTHOR ("Dirk Opfer");
++MODULE_LICENSE ("GPL v2");
+Index: git/arch/arm/mach-pxa/tosa.c
+===================================================================
+--- git.orig/arch/arm/mach-pxa/tosa.c 2006-11-07 22:13:10.000000000 +0000
++++ git/arch/arm/mach-pxa/tosa.c 2006-11-07 23:29:38.000000000 +0000
+@@ -24,6 +24,7 @@
+ #include <linux/mtd/partitions.h>
+ #include <linux/pm.h>
+ #include <linux/delay.h>
++#include <linux/fb.h>
+
+ #include <asm/setup.h>
+ #include <asm/memory.h>
+@@ -345,7 +345,38 @@ static struct tmio_nand_platform_data to
+ .badblock_pattern = &tosa_tc6393_nand_bbt,
+ };
+
+-extern struct tmio_lcd_platform_data tosa_tc6393_lcd_platform_data;
++static struct fb_videomode tosa_tc6393_lcd_mode[] = {
++ {
++ .xres = 480,
++ .yres = 640,
++ .pixclock = 0x002cdf00,/* PLL divisor */
++ .left_margin = 0x004c,
++ .right_margin = 0x005b,
++ .upper_margin = 0x0001,
++ .lower_margin = 0x000d,
++ .hsync_len = 0x0002,
++ .vsync_len = 0x0001,
++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
++ .vmode = FB_VMODE_NONINTERLACED,
++ },{
++ .xres = 240,
++ .yres = 320,
++ .pixclock = 0x00e7f203,/* PLL divisor */
++ .left_margin = 0x0024,
++ .right_margin = 0x002f,
++ .upper_margin = 0x0001,
++ .lower_margin = 0x000d,
++ .hsync_len = 0x0002,
++ .vsync_len = 0x0001,
++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
++ .vmode = FB_VMODE_NONINTERLACED,
++}};
++
++struct tmio_lcd_platform_data tosa_tc6393_lcd_platform_data = {
++ .ops = NULL,
++ .modelist = tosa_tc6393_lcd_mode,
++ .num_modes = ARRAY_SIZE(tosa_tc6393_lcd_mode),
++};
+
+ static struct tmio_cell tosa_tc6393_cells[] = {
+ {
+@@ -384,6 +415,19 @@ struct platform_device tc6393_device = {
+ .num_resources = ARRAY_SIZE(tc6393_resources),
+ .resource = tc6393_resources,
+ };
++EXPORT_SYMBOL (tc6393_device);
++
++/*
++ * Tosa LCD / Backlight stuff
++ */
++static struct platform_device tosalcd_device = {
++ .name = "tosa-lcd",
++ .id = -1,
++ .dev = {
++ .parent = &tc6393_device.dev,
++ .platform_data = &tosa_tc6393_lcd_platform_data.ops,
++ },
++};
+
+ static struct platform_device *devices[] __initdata = {
+ &tosascoop_device,
+@@ -391,6 +435,7 @@ static struct platform_device *devices[]
+ &tosakbd_device,
+ &tosaled_device,
+ &tc6393_device,
++ &tosalcd_device,
+ };
+
+ static void tosa_poweroff(void)
+Index: git/arch/arm/mach-pxa/Kconfig
+===================================================================
+--- git.orig/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:10.000000000 +0000
++++ git/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:10.000000000 +0000
+@@ -129,7 +129,10 @@ config MACH_TOSA
+ bool "Enable Sharp SL-6000x (Tosa) Support"
+ depends PXA_SHARPSL_25x
+ select TOSHIBA_TC6393XB
+- select SHARPSL_PM
++ select I2C
++ select I2C_PXA
++ select SHARPSL_PM
++ select PXA_SSP
+
+ config PXA25x
+ bool
+ arch/arm/mach-pxa/Kconfig | 5
+ arch/arm/mach-pxa/Makefile | 2
+ arch/arm/mach-pxa/tosa.c | 49 +++++-
+ arch/arm/mach-pxa/tosa_lcd.c | 344 +++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 396 insertions(+), 4 deletions(-)
+
+Index: git/arch/arm/mach-pxa/Makefile
+===================================================================
+--- git.orig/arch/arm/mach-pxa/Makefile 2006-11-07 22:13:10.000000000 +0000
++++ git/arch/arm/mach-pxa/Makefile 2006-11-07 23:29:38.000000000 +0000
+@@ -17,7 +17,7 @@ obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o
+ obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o
+ obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o
+ obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o
+-obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o
++obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o tosa_lcd.o
+ obj-$(CONFIG_MACH_EM_X270) += em-x270.o
+ obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o
+
+Index: git/arch/arm/mach-pxa/tosa_lcd.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ git/arch/arm/mach-pxa/tosa_lcd.c 2006-11-07 23:29:25.000000000 +0000
+@@ -0,0 +1,344 @@
++/*
++ * LCD / Backlight control code for Sharp SL-6000x (tosa)
++ *
++ * Copyright (c) 2005 Dirk Opfer
++ *
++ * 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.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/i2c.h>
++#include <linux/fb.h>
++
++#include <asm/mach/sharpsl_param.h>
++#include <asm/hardware.h>
++#include <asm/hardware/scoop.h>
++#include <asm/hardware/tmio.h>
++#include <asm/arch/ssp.h>
++#include <asm/arch/sharpsl.h>
++#include <asm/arch/tosa.h>
++#include <asm/arch/pxa-regs.h>
++
++#define DAC_BASE 0x4e
++#define DAC_CH1 0
++#define DAC_CH2 1
++
++#define TG_REG0_VQV 0x0001
++#define TG_REG0_COLOR 0x0002
++#define TG_REG0_UD 0x0004
++#define TG_REG0_LR 0x0008
++#define COMADJ_DEFAULT 97
++#define TOSA_LCD_I2C_DEVICEID 0x4711 // Fixme: new value
++
++static void tosa_lcd_tg_init(struct device *dev);
++static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode);
++static void tosa_lcd_tg_off(struct device *dev);
++static void tosa_set_backlight(int intensity);
++
++const static struct tmio_lcd_ops tosa_tc6393_lcd_ops = {
++ .init = tosa_lcd_tg_init,
++ .tg_on = tosa_lcd_tg_on,
++ .tg_off = tosa_lcd_tg_off,
++};
++
++static struct platform_device *tosabl_device;
++static struct i2c_driver tosa_driver;
++static struct i2c_client* tosa_i2c_dac;
++static int initialised;
++static int comadj;
++static int bl_intensity;
++static struct ssp_dev tosa_nssp_dev;
++static struct ssp_state tosa_nssp_state;
++static spinlock_t tosa_nssp_lock;
++
++static unsigned short normal_i2c[] = {
++ DAC_BASE,
++ I2C_CLIENT_END
++};
++I2C_CLIENT_INSMOD;
++
++static struct corgibl_machinfo tosa_bl_machinfo = {
++ .max_intensity = 255,
++ .default_intensity = 68,
++ .limit_mask = 0x0b,
++ .set_bl_intensity = tosa_set_backlight,
++};
++
++int tosa_bl_intensity(void)
++{
++ return bl_intensity;
++}
++
++static void pxa_nssp_output(unsigned char reg, unsigned char data)
++{
++ unsigned long flag, dummy;
++ u32 dat = ( ((reg << 5) & 0xe0) | (data & 0x1f) );
++ spin_lock_irqsave(&tosa_nssp_lock, flag);
++
++ ssp_config(&tosa_nssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), 0, 0, SSCR0_SerClkDiv(128));
++ ssp_enable(&tosa_nssp_dev);
++
++ ssp_write_word(&tosa_nssp_dev,dat);
++
++ /* Read null data back from device to prevent SSP overflow */
++ ssp_read_word(&tosa_nssp_dev, &dummy);
++ ssp_disable(&tosa_nssp_dev);
++ spin_unlock_irqrestore(&tosa_nssp_lock, flag);
++
++}
++
++static void tosa_set_backlight(int intensity)
++{
++ if (!tosa_i2c_dac)
++ return;
++
++ bl_intensity = intensity;
++ /* SetBacklightDuty */
++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH2, (unsigned char)intensity);
++
++ /* SetBacklightVR */
++ if (intensity)
++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BL_C20MA);
++ else
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BL_C20MA);
++
++ /* bl_enable GP04=1 otherwise GP04=0*/
++ pxa_nssp_output(TG_GPODR2, intensity ? 0x01 : 0x00);
++}
++
++static void tosa_lcd_tg_init(struct device *dev)
++{
++ /* L3V On */
++ set_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON);
++ mdelay(60);
++
++ /* TG On */
++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_TG_ON);
++ mdelay(60);
++
++ pxa_nssp_output(TG_TPOSCTL,0x00); /* delayed 0clk TCTL signal for VGA */
++ pxa_nssp_output(TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */
++}
++
++static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode)
++{
++ const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR;
++ pxa_nssp_output(TG_PNLCTL, value | (mode->yres == 320 ? 0 : TG_REG0_VQV));
++
++ /* TG LCD pannel power up */
++ pxa_nssp_output(TG_PINICTL,0x4);
++ mdelay(50);
++
++ /* TG LCD GVSS */
++ pxa_nssp_output(TG_PINICTL,0x0);
++
++ if (!initialised)
++ {
++ /* after the pannel is powered up the first time, we can access the i2c bus */
++ /* so probe for the DAC */
++ i2c_add_driver(&tosa_driver);
++ initialised = 1;
++ mdelay(50);
++ }
++ if (tosa_i2c_dac)
++ /* set common voltage */
++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj);
++
++}
++
++static void tosa_lcd_tg_off(struct device *dev)
++{
++ /* TG LCD VHSA off */
++ pxa_nssp_output(TG_PINICTL,0x4);
++ mdelay(50);
++
++ /* TG LCD signal off */
++ pxa_nssp_output(TG_PINICTL,0x6);
++ mdelay(50);
++
++ /* TG Off */
++ set_tc6393_gpio(&tc6393_device.dev, TOSA_TC6393_TG_ON);
++ mdelay(100);
++
++ /* L3V Off */
++ reset_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON);
++}
++
++static int tosa_detect_client(struct i2c_adapter* adapter, int address, int kind) {
++ int err = 0;
++
++ printk("Tosa-LCD: DAC detected address:0x%2.2x\n",address);
++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA ))
++ goto ERROR0;
++
++ if (!(tosa_i2c_dac = (struct i2c_client*)kzalloc(sizeof(*tosa_i2c_dac), GFP_KERNEL))) {
++ err = -ENOMEM;
++ goto ERROR0;
++ }
++
++ //i2c_set_clientdata(tosa_i2c_dac, data);
++ tosa_i2c_dac->addr = address;
++ tosa_i2c_dac->adapter = adapter;
++ tosa_i2c_dac->driver = &tosa_driver;
++ tosa_i2c_dac->dev.parent = &tc6393_device.dev;
++ strcpy(tosa_i2c_dac->name, "tosa lcd");
++ if ((err = i2c_attach_client(tosa_i2c_dac)))
++ goto ERROR3;
++
++ /* Now i2c is ready, allocate the backlight device*/
++ tosabl_device = platform_device_alloc("corgi-bl", -1);
++ if (!tosabl_device) {
++ err = -ENOMEM;
++ goto ERROR4;
++ }
++
++ /* set parent device */
++ tosabl_device->dev.parent = &tosa_i2c_dac->dev;
++ tosabl_device->dev.platform_data = &tosa_bl_machinfo;
++
++ err = platform_device_add(tosabl_device);
++
++ if (err)
++ platform_device_put(tosabl_device);
++
++ /* set common voltage */
++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj);
++
++ return 0;
++ERROR4:
++ i2c_detach_client(tosa_i2c_dac);
++ERROR3:
++ kfree(tosa_i2c_dac);
++ERROR0:
++ return err;
++}
++
++static int tosa_attach_adapter(struct i2c_adapter* adapter) {
++ return i2c_probe(adapter, &addr_data, &tosa_detect_client);
++}
++
++static int tosa_detach_client(struct i2c_client* client) {
++ int err;
++
++ if ((err = i2c_detach_client(client))) {
++ printk(KERN_ERR "tosa: Cannot deregister client\n");
++ return err;
++ }
++ kfree(client);
++ return 0;
++}
++
++static struct i2c_driver tosa_driver={
++ .id = TOSA_LCD_I2C_DEVICEID,
++ .attach_adapter = tosa_attach_adapter,
++ .detach_client = tosa_detach_client,
++};
++
++static int __init tosa_lcd_probe(struct platform_device *pdev)
++{
++ int ret;
++ spin_lock_init(&tosa_nssp_lock);
++
++ if (!pdev->dev.platform_data)
++ return -EINVAL;
++
++ /* Set Common Voltage */
++ comadj = sharpsl_param.comadj == -1 ? COMADJ_DEFAULT : sharpsl_param.comadj;
++
++ ret=ssp_init(&tosa_nssp_dev,2,0);
++
++ /* initialize SSP */
++ pxa_gpio_mode(GPIO83_NSSP_TX);
++ pxa_gpio_mode(GPIO81_NSSP_CLK_OUT);
++ pxa_gpio_mode(GPIO82_NSSP_FRM_OUT);
++
++ if (ret)
++ printk(KERN_ERR "Unable to register NSSP handler!\n");
++ else {
++ struct tmio_lcd_ops* *tmio_ops = pdev->dev.platform_data;
++ ssp_disable(&tosa_nssp_dev);
++ initialised = 0;
++
++ /* Set the lcd functions */
++ *tmio_ops = (struct tmio_lcd_ops*) &tosa_tc6393_lcd_ops;
++ }
++
++ return ret;
++}
++
++static int tosa_lcd_remove(struct platform_device *pdev)
++{
++ /* delete the lcd functions */
++ struct tmio_lcd_ops* *tmio_ops = pdev->dev.platform_data;
++ *tmio_ops = NULL;
++
++ ssp_exit(&tosa_nssp_dev);
++
++ if (tosa_i2c_dac) {
++ i2c_detach_client(tosa_i2c_dac);
++ kfree(tosa_i2c_dac);
++ }
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++
++static int tosa_lcd_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ ssp_flush(&tosa_nssp_dev);
++ ssp_save_state(&tosa_nssp_dev,&tosa_nssp_state);
++ return 0;
++}
++
++static int tosa_lcd_resume(struct platform_device *pdev)
++{
++ printk("tosa_lcd_resume\n");
++ ssp_restore_state(&tosa_nssp_dev,&tosa_nssp_state);
++ ssp_enable(&tosa_nssp_dev);
++ printk("tosa_lcd_resume ok\n");
++ return 0;
++}
++#else
++
++#define tosa_lcd_suspend NULL
++#define tosa_lcd_resume NULL
++
++#endif
++
++
++static struct platform_driver tosalcd_driver = {
++ .probe = tosa_lcd_probe,
++ .remove = tosa_lcd_remove,
++ .suspend = tosa_lcd_suspend,
++ .resume = tosa_lcd_resume,
++ .driver = {
++ .name = "tosa-lcd",
++ },
++};
++
++static int __init tosa_lcd_init(void)
++{
++ return platform_driver_register(&tosalcd_driver);
++}
++
++static void __exit tosa_lcd_cleanup (void)
++{
++ platform_driver_unregister (&tosalcd_driver);
++}
++
++device_initcall(tosa_lcd_init);
++module_exit (tosa_lcd_cleanup);
++
++MODULE_DESCRIPTION ("Tosa LCD device");
++MODULE_AUTHOR ("Dirk Opfer");
++MODULE_LICENSE ("GPL v2");
+Index: git/arch/arm/mach-pxa/tosa.c
+===================================================================
+--- git.orig/arch/arm/mach-pxa/tosa.c 2006-11-07 22:13:10.000000000 +0000
++++ git/arch/arm/mach-pxa/tosa.c 2006-11-07 23:29:38.000000000 +0000
+@@ -24,6 +24,7 @@
+ #include <linux/mtd/partitions.h>
+ #include <linux/pm.h>
+ #include <linux/delay.h>
++#include <linux/fb.h>
+
+ #include <asm/setup.h>
+ #include <asm/memory.h>
+@@ -345,7 +345,38 @@ static struct tmio_nand_platform_data to
+ .badblock_pattern = &tosa_tc6393_nand_bbt,
+ };
+
+-extern struct tmio_lcd_platform_data tosa_tc6393_lcd_platform_data;
++static struct fb_videomode tosa_tc6393_lcd_mode[] = {
++ {
++ .xres = 480,
++ .yres = 640,
++ .pixclock = 0x002cdf00,/* PLL divisor */
++ .left_margin = 0x004c,
++ .right_margin = 0x005b,
++ .upper_margin = 0x0001,
++ .lower_margin = 0x000d,
++ .hsync_len = 0x0002,
++ .vsync_len = 0x0001,
++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
++ .vmode = FB_VMODE_NONINTERLACED,
++ },{
++ .xres = 240,
++ .yres = 320,
++ .pixclock = 0x00e7f203,/* PLL divisor */
++ .left_margin = 0x0024,
++ .right_margin = 0x002f,
++ .upper_margin = 0x0001,
++ .lower_margin = 0x000d,
++ .hsync_len = 0x0002,
++ .vsync_len = 0x0001,
++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
++ .vmode = FB_VMODE_NONINTERLACED,
++}};
++
++struct tmio_lcd_platform_data tosa_tc6393_lcd_platform_data = {
++ .ops = NULL,
++ .modelist = tosa_tc6393_lcd_mode,
++ .num_modes = ARRAY_SIZE(tosa_tc6393_lcd_mode),
++};
+
+ static struct tmio_cell tosa_tc6393_cells[] = {
+ {
+@@ -384,6 +415,19 @@ struct platform_device tc6393_device = {
+ .num_resources = ARRAY_SIZE(tc6393_resources),
+ .resource = tc6393_resources,
+ };
++EXPORT_SYMBOL (tc6393_device);
++
++/*
++ * Tosa LCD / Backlight stuff
++ */
++static struct platform_device tosalcd_device = {
++ .name = "tosa-lcd",
++ .id = -1,
++ .dev = {
++ .parent = &tc6393_device.dev,
++ .platform_data = &tosa_tc6393_lcd_platform_data.ops,
++ },
++};
+
+ static struct platform_device *devices[] __initdata = {
+ &tosascoop_device,
+@@ -391,6 +435,7 @@ static struct platform_device *devices[]
+ &tosakbd_device,
+ &tosaled_device,
+ &tc6393_device,
++ &tosalcd_device,
+ };
+
+ static void tosa_poweroff(void)
+Index: git/arch/arm/mach-pxa/Kconfig
+===================================================================
+--- git.orig/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:10.000000000 +0000
++++ git/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:10.000000000 +0000
+@@ -129,7 +129,10 @@ config MACH_TOSA
+ bool "Enable Sharp SL-6000x (Tosa) Support"
+ depends PXA_SHARPSL_25x
+ select TOSHIBA_TC6393XB
+- select SHARPSL_PM
++ select I2C
++ select I2C_PXA
++ select SHARPSL_PM
++ select PXA_SSP
+
+ config PXA25x
+ bool
diff --git a/packages/linux/linux-rp-2.6.23/wm9712-reset-loop-r2.patch b/packages/linux/linux-rp-2.6.23/wm9712-reset-loop-r2.patch
new file mode 100644
index 0000000000..0e32a62ea6
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/wm9712-reset-loop-r2.patch
@@ -0,0 +1,88 @@
+ sound/soc/codecs/wm9712.c | 28 ++++++++++++++++++----------
+ 1 file changed, 18 insertions(+), 10 deletions(-)
+
+Index: git/sound/soc/codecs/wm9712.c
+===================================================================
+--- git.orig/sound/soc/codecs/wm9712.c 2006-11-07 22:10:01.000000000 +0000
++++ git/sound/soc/codecs/wm9712.c 2006-11-07 22:11:50.000000000 +0000
+@@ -618,18 +618,26 @@ static int wm9712_dapm_event(struct snd_
+
+ static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
+ {
+- if (try_warm && soc_ac97_ops.warm_reset) {
+- soc_ac97_ops.warm_reset(codec->ac97);
+- if (!(ac97_read(codec, 0) & 0x8000))
+- return 1;
+- }
++ int retry = 3;
+
+- soc_ac97_ops.reset(codec->ac97);
+- if (ac97_read(codec, 0) & 0x8000)
+- goto err;
+- return 0;
++ while (retry--)
++ {
++ if(try_warm && soc_ac97_ops.warm_reset) {
++ soc_ac97_ops.warm_reset(codec->ac97);
++ if(ac97_read(codec, 0) & 0x8000)
++ continue;
++ else
++ return 1;
++ }
++
++ soc_ac97_ops.reset(codec->ac97);
++ if(ac97_read(codec, 0) & 0x8000)
++ continue;
++ else
++ return 0;
++
++ }
+
+-err:
+ printk(KERN_ERR "WM9712 AC97 reset failed\n");
+ return -EIO;
+ }
+ sound/soc/codecs/wm9712.c | 28 ++++++++++++++++++----------
+ 1 file changed, 18 insertions(+), 10 deletions(-)
+
+Index: git/sound/soc/codecs/wm9712.c
+===================================================================
+--- git.orig/sound/soc/codecs/wm9712.c 2006-11-07 22:10:01.000000000 +0000
++++ git/sound/soc/codecs/wm9712.c 2006-11-07 22:11:50.000000000 +0000
+@@ -618,18 +618,26 @@ static int wm9712_dapm_event(struct snd_
+
+ static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
+ {
+- if (try_warm && soc_ac97_ops.warm_reset) {
+- soc_ac97_ops.warm_reset(codec->ac97);
+- if (!(ac97_read(codec, 0) & 0x8000))
+- return 1;
+- }
++ int retry = 3;
+
+- soc_ac97_ops.reset(codec->ac97);
+- if (ac97_read(codec, 0) & 0x8000)
+- goto err;
+- return 0;
++ while (retry--)
++ {
++ if(try_warm && soc_ac97_ops.warm_reset) {
++ soc_ac97_ops.warm_reset(codec->ac97);
++ if(ac97_read(codec, 0) & 0x8000)
++ continue;
++ else
++ return 1;
++ }
++
++ soc_ac97_ops.reset(codec->ac97);
++ if(ac97_read(codec, 0) & 0x8000)
++ continue;
++ else
++ return 0;
++
++ }
+
+-err:
+ printk(KERN_ERR "WM9712 AC97 reset failed\n");
+ return -EIO;
+ }
diff --git a/packages/linux/linux-rp-2.6.23/wm9712-suspend-cold-res-r2.patch b/packages/linux/linux-rp-2.6.23/wm9712-suspend-cold-res-r2.patch
new file mode 100644
index 0000000000..cbf854d772
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/wm9712-suspend-cold-res-r2.patch
@@ -0,0 +1,32 @@
+ sound/soc/codecs/wm9712.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+Index: git/sound/soc/codecs/wm9712.c
+===================================================================
+--- git.orig/sound/soc/codecs/wm9712.c 2006-11-07 21:57:34.000000000 +0000
++++ git/sound/soc/codecs/wm9712.c 2006-11-07 21:59:30.000000000 +0000
+@@ -651,7 +651,7 @@ static int wm9712_soc_resume(struct plat
+ int i, ret;
+ u16 *cache = codec->reg_cache;
+
+- ret = wm9712_reset(codec, 1);
++ ret = wm9712_reset(codec, 0);
+ if (ret < 0){
+ printk(KERN_ERR "could not reset AC97 codec\n");
+ return ret;
+ sound/soc/codecs/wm9712.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+Index: git/sound/soc/codecs/wm9712.c
+===================================================================
+--- git.orig/sound/soc/codecs/wm9712.c 2006-11-07 21:57:34.000000000 +0000
++++ git/sound/soc/codecs/wm9712.c 2006-11-07 21:59:30.000000000 +0000
+@@ -651,7 +651,7 @@ static int wm9712_soc_resume(struct plat
+ int i, ret;
+ u16 *cache = codec->reg_cache;
+
+- ret = wm9712_reset(codec, 1);
++ ret = wm9712_reset(codec, 0);
+ if (ret < 0){
+ printk(KERN_ERR "could not reset AC97 codec\n");
+ return ret;
diff --git a/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0-fix-r0.patch b/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0-fix-r0.patch
new file mode 100644
index 0000000000..f246fbfed5
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0-fix-r0.patch
@@ -0,0 +1,256 @@
+ drivers/input/power.c | 2 +-
+ drivers/input/touchscreen/Kconfig | 2 +-
+ drivers/input/touchscreen/wm97xx-core.c | 35 ++++++++++++++++---------------
+ include/linux/wm97xx.h | 2 +-
+ 4 files changed, 21 insertions(+), 20 deletions(-)
+
+diff --git a/drivers/input/power.c b/drivers/input/power.c
+index 4443e34..7aac875 100644
+--- a/drivers/input/power.c
++++ b/drivers/input/power.c
+@@ -156,7 +156,7 @@ static void power_event(struct input_handle *handle, unsigned int type,
+ }
+ }
+
+-static struct input_handle *power_connect(struct input_handler *handler,
++static int power_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+ {
+diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
+index 6862e8f..9b532e9 100644
+--- a/drivers/input/touchscreen/Kconfig
++++ b/drivers/input/touchscreen/Kconfig
+@@ -247,7 +247,7 @@ config TOUCHSCREEN_TSC2101
+
+ config TOUCHSCREEN_WM97XX
+ tristate "Support for WM97xx AC97 touchscreen controllers"
+- depends SND_AC97_BUS
++ depends AC97_BUS
+
+ choice
+ prompt "WM97xx codec type"
+diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
+index 9b2710e..d3ce3f3 100644
+--- a/drivers/input/touchscreen/wm97xx-core.c
++++ b/drivers/input/touchscreen/wm97xx-core.c
+@@ -84,6 +84,7 @@
+ #include <linux/bitops.h>
+ #include <linux/workqueue.h>
+ #include <linux/device.h>
++#include <linux/freezer.h>
+ #include <linux/wm97xx.h>
+ #include <asm/uaccess.h>
+ #include <asm/io.h>
+@@ -241,14 +242,15 @@ WM97XX_STATUS_ATTR(gpio);
+
+ static int wm97xx_sys_add(struct device *dev)
+ {
++ int err;
+ if (aux_sys) {
+- device_create_file(dev, &dev_attr_aux1);
+- device_create_file(dev, &dev_attr_aux2);
+- device_create_file(dev, &dev_attr_aux3);
+- device_create_file(dev, &dev_attr_aux4);
++ err = device_create_file(dev, &dev_attr_aux1);
++ err = device_create_file(dev, &dev_attr_aux2);
++ err = device_create_file(dev, &dev_attr_aux3);
++ err = device_create_file(dev, &dev_attr_aux4);
+ }
+ if (status_sys)
+- device_create_file(dev, &dev_attr_gpio);
++ err = device_create_file(dev, &dev_attr_gpio);
+ return 0;
+ }
+
+@@ -366,12 +368,12 @@ void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir,
+
+ /*
+ * Handle a pen down interrupt.
+- */
+-static void wm97xx_pen_irq_worker(void *ptr)
+-{
+- struct wm97xx *wm = (struct wm97xx *) ptr;
+-
+- /* do we need to enable the touch panel reader */
++ */
++static void wm97xx_pen_irq_worker(struct work_struct *work)
++{
++ struct wm97xx *wm = container_of(work, struct wm97xx, pen_event_work);
++
++ /* do we need to enable the touch panel reader */
+ if (wm->id == WM9705_ID2) {
+ if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN)
+ wm->pen_is_down = 1;
+@@ -411,9 +413,8 @@ static void wm97xx_pen_irq_worker(void *ptr)
+ * We have to disable the codec interrupt in the handler because it can
+ * take upto 1ms to clear the interrupt source. The interrupt is then enabled
+ * again in the slow handler when the source has been cleared.
+- */
+-static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id,
+- struct pt_regs *regs)
++ */
++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id)
+ {
+ struct wm97xx *wm = (struct wm97xx *) dev_id;
+ disable_irq(wm->pen_irq);
+@@ -428,15 +429,15 @@ static int wm97xx_init_pen_irq(struct wm97xx *wm)
+ {
+ u16 reg;
+
+- INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker, wm);
+- if ((wm->pen_irq_workq =
++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker);
++ if ((wm->pen_irq_workq =
+ create_singlethread_workqueue("kwm97pen")) == NULL) {
+ err("could not create pen irq work queue");
+ wm->pen_irq = 0;
+ return -EINVAL;
+ }
+
+- if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) {
++ if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, IRQF_SHARED, "wm97xx-pen", wm)) {
+ err("could not register codec pen down interrupt, will poll for pen down");
+ destroy_workqueue(wm->pen_irq_workq);
+ wm->pen_irq = 0;
+diff --git a/include/linux/wm97xx.h b/include/linux/wm97xx.h
+index b1c1740..a9bd57e 100644
+--- a/include/linux/wm97xx.h
++++ b/include/linux/wm97xx.h
+@@ -243,7 +243,7 @@ struct wm97xx {
+ u16 dig_save[3]; /* saved during aux reading */
+ struct wm97xx_codec_drv *codec; /* attached codec driver*/
+ struct input_dev* input_dev; /* touchscreen input device */
+- ac97_t *ac97; /* ALSA codec access */
++ struct snd_ac97 *ac97; /* ALSA codec access */
+ struct device *dev; /* ALSA device */
+ struct device *battery_dev;
+ struct device *touch_dev;
+ drivers/input/power.c | 2 +-
+ drivers/input/touchscreen/Kconfig | 2 +-
+ drivers/input/touchscreen/wm97xx-core.c | 35 ++++++++++++++++---------------
+ include/linux/wm97xx.h | 2 +-
+ 4 files changed, 21 insertions(+), 20 deletions(-)
+
+diff --git a/drivers/input/power.c b/drivers/input/power.c
+index 4443e34..7aac875 100644
+--- a/drivers/input/power.c
++++ b/drivers/input/power.c
+@@ -156,7 +156,7 @@ static void power_event(struct input_handle *handle, unsigned int type,
+ }
+ }
+
+-static struct input_handle *power_connect(struct input_handler *handler,
++static int power_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+ {
+diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
+index 6862e8f..9b532e9 100644
+--- a/drivers/input/touchscreen/Kconfig
++++ b/drivers/input/touchscreen/Kconfig
+@@ -247,7 +247,7 @@ config TOUCHSCREEN_TSC2101
+
+ config TOUCHSCREEN_WM97XX
+ tristate "Support for WM97xx AC97 touchscreen controllers"
+- depends SND_AC97_BUS
++ depends AC97_BUS
+
+ choice
+ prompt "WM97xx codec type"
+diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
+index 9b2710e..d3ce3f3 100644
+--- a/drivers/input/touchscreen/wm97xx-core.c
++++ b/drivers/input/touchscreen/wm97xx-core.c
+@@ -84,6 +84,7 @@
+ #include <linux/bitops.h>
+ #include <linux/workqueue.h>
+ #include <linux/device.h>
++#include <linux/freezer.h>
+ #include <linux/wm97xx.h>
+ #include <asm/uaccess.h>
+ #include <asm/io.h>
+@@ -241,14 +242,15 @@ WM97XX_STATUS_ATTR(gpio);
+
+ static int wm97xx_sys_add(struct device *dev)
+ {
++ int err;
+ if (aux_sys) {
+- device_create_file(dev, &dev_attr_aux1);
+- device_create_file(dev, &dev_attr_aux2);
+- device_create_file(dev, &dev_attr_aux3);
+- device_create_file(dev, &dev_attr_aux4);
++ err = device_create_file(dev, &dev_attr_aux1);
++ err = device_create_file(dev, &dev_attr_aux2);
++ err = device_create_file(dev, &dev_attr_aux3);
++ err = device_create_file(dev, &dev_attr_aux4);
+ }
+ if (status_sys)
+- device_create_file(dev, &dev_attr_gpio);
++ err = device_create_file(dev, &dev_attr_gpio);
+ return 0;
+ }
+
+@@ -366,12 +368,12 @@ void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir,
+
+ /*
+ * Handle a pen down interrupt.
+- */
+-static void wm97xx_pen_irq_worker(void *ptr)
+-{
+- struct wm97xx *wm = (struct wm97xx *) ptr;
+-
+- /* do we need to enable the touch panel reader */
++ */
++static void wm97xx_pen_irq_worker(struct work_struct *work)
++{
++ struct wm97xx *wm = container_of(work, struct wm97xx, pen_event_work);
++
++ /* do we need to enable the touch panel reader */
+ if (wm->id == WM9705_ID2) {
+ if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN)
+ wm->pen_is_down = 1;
+@@ -411,9 +413,8 @@ static void wm97xx_pen_irq_worker(void *ptr)
+ * We have to disable the codec interrupt in the handler because it can
+ * take upto 1ms to clear the interrupt source. The interrupt is then enabled
+ * again in the slow handler when the source has been cleared.
+- */
+-static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id,
+- struct pt_regs *regs)
++ */
++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id)
+ {
+ struct wm97xx *wm = (struct wm97xx *) dev_id;
+ disable_irq(wm->pen_irq);
+@@ -428,15 +429,15 @@ static int wm97xx_init_pen_irq(struct wm97xx *wm)
+ {
+ u16 reg;
+
+- INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker, wm);
+- if ((wm->pen_irq_workq =
++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker);
++ if ((wm->pen_irq_workq =
+ create_singlethread_workqueue("kwm97pen")) == NULL) {
+ err("could not create pen irq work queue");
+ wm->pen_irq = 0;
+ return -EINVAL;
+ }
+
+- if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) {
++ if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, IRQF_SHARED, "wm97xx-pen", wm)) {
+ err("could not register codec pen down interrupt, will poll for pen down");
+ destroy_workqueue(wm->pen_irq_workq);
+ wm->pen_irq = 0;
+diff --git a/include/linux/wm97xx.h b/include/linux/wm97xx.h
+index b1c1740..a9bd57e 100644
+--- a/include/linux/wm97xx.h
++++ b/include/linux/wm97xx.h
+@@ -243,7 +243,7 @@ struct wm97xx {
+ u16 dig_save[3]; /* saved during aux reading */
+ struct wm97xx_codec_drv *codec; /* attached codec driver*/
+ struct input_dev* input_dev; /* touchscreen input device */
+- ac97_t *ac97; /* ALSA codec access */
++ struct snd_ac97 *ac97; /* ALSA codec access */
+ struct device *dev; /* ALSA device */
+ struct device *battery_dev;
+ struct device *touch_dev;
diff --git a/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0.patch b/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0.patch
new file mode 100644
index 0000000000..b029ccc066
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0.patch
@@ -0,0 +1,5798 @@
+Index: linux-2.6.17/drivers/input/touchscreen/Kconfig
+===================================================================
+--- linux-2.6.17.orig/drivers/input/touchscreen/Kconfig 2006-09-19 20:35:35.060495500 +0200
++++ linux-2.6.17/drivers/input/touchscreen/Kconfig 2006-09-19 20:36:47.965051750 +0200
+@@ -121,4 +121,57 @@ config TOUCHSCREEN_TSC2101
+ To compile this driver as a module, choose M here: the
+ module will be called ads7846_ts.
+
++config TOUCHSCREEN_WM97XX
++ tristate "Support for WM97xx AC97 touchscreen controllers"
++ depends SND_AC97_BUS
++
++choice
++ prompt "WM97xx codec type"
++
++config TOUCHSCREEN_WM9705
++ bool "WM9705 Touchscreen interface support"
++ depends on TOUCHSCREEN_WM97XX
++ help
++ Say Y here if you have the wm9705 touchscreen.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called wm9705.
++
++config TOUCHSCREEN_WM9712
++ bool "WM9712 Touchscreen interface support"
++ depends on TOUCHSCREEN_WM97XX
++ help
++ Say Y here if you have the wm9712 touchscreen.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called wm9712.
++
++config TOUCHSCREEN_WM9713
++ bool "WM9713 Touchscreen interface support"
++ depends on TOUCHSCREEN_WM97XX
++ help
++ Say Y here if you have the wm9713 touchscreen.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called wm9713.
++
++endchoice
++
++config TOUCHSCREEN_WM97XX_PXA
++ tristate "WM97xx PXA accelerated touch"
++ depends on TOUCHSCREEN_WM97XX && ARCH_PXA
++ help
++ Say Y here for continuous mode touch on the PXA
++
++ If unsure, say N
++
++ To compile this driver as a module, choose M here: the
++ module will be called pxa-wm97xx
++
+ endif
+Index: linux-2.6.17/drivers/input/touchscreen/Makefile
+===================================================================
+--- linux-2.6.17.orig/drivers/input/touchscreen/Makefile 2006-09-19 20:35:35.072496250 +0200
++++ linux-2.6.17/drivers/input/touchscreen/Makefile 2006-09-19 20:37:40.540337500 +0200
+@@ -4,6 +4,8 @@
+
+ # Each configuration option enables a list of files.
+
++wm97xx-ts-objs := wm97xx-core.o
++
+ obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
+ obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
+ obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o
+@@ -13,3 +15,16 @@ obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtou
+ obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
+ obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
+ obj-$(CONFIG_TOUCHSCREEN_TSC2101) += tsc2101_ts.o
++obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
++obj-$(CONFIG_TOUCHSCREEN_WM97XX_PXA) += pxa-wm97xx.o
++
++ifeq ($(CONFIG_TOUCHSCREEN_WM9713),y)
++wm97xx-ts-objs += wm9713.o
++endif
++
++ifeq ($(CONFIG_TOUCHSCREEN_WM9712),y)
++wm97xx-ts-objs += wm9712.o
++endif
++ifeq ($(CONFIG_TOUCHSCREEN_WM9705),y)
++wm97xx-ts-objs += wm9705.o
++endif
+Index: linux-2.6.17/drivers/input/touchscreen/pxa-wm97xx.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/drivers/input/touchscreen/pxa-wm97xx.c 2006-09-19 20:36:47.965051750 +0200
+@@ -0,0 +1,289 @@
++/*
++ * pxa-wm97xx.c -- pxa-wm97xx Continuous Touch screen driver for
++ * Wolfson WM97xx AC97 Codecs.
++ *
++ * Copyright 2004 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ *
++ * 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.
++ *
++ * Notes:
++ * This is a wm97xx extended touch driver to capture touch
++ * data in a continuous manner on the Intel XScale archictecture
++ *
++ * Features:
++ * - codecs supported:- WM9705, WM9712, WM9713
++ * - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x
++ *
++ * Revision history
++ * 18th Aug 2004 Initial version.
++ * 26th Jul 2005 Improved continous read back and added FIFO flushing.
++ * 06th Sep 2005 Mike Arthur <linux@wolfsonmicro.com>
++ * Moved to using the wm97xx bus
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/irq.h>
++#include <linux/wm97xx.h>
++#include <asm/io.h>
++#include <asm/arch/pxa-regs.h>
++
++#define VERSION "0.13"
++
++struct continuous {
++ u16 id; /* codec id */
++ u8 code; /* continuous code */
++ u8 reads; /* number of coord reads per read cycle */
++ u32 speed; /* number of coords per second */
++};
++
++#define WM_READS(sp) ((sp / HZ) + 1)
++
++static const struct continuous cinfo[] = {
++ {WM9705_ID2, 0, WM_READS(94), 94},
++ {WM9705_ID2, 1, WM_READS(188), 188},
++ {WM9705_ID2, 2, WM_READS(375), 375},
++ {WM9705_ID2, 3, WM_READS(750), 750},
++ {WM9712_ID2, 0, WM_READS(94), 94},
++ {WM9712_ID2, 1, WM_READS(188), 188},
++ {WM9712_ID2, 2, WM_READS(375), 375},
++ {WM9712_ID2, 3, WM_READS(750), 750},
++ {WM9713_ID2, 0, WM_READS(94), 94},
++ {WM9713_ID2, 1, WM_READS(120), 120},
++ {WM9713_ID2, 2, WM_READS(154), 154},
++ {WM9713_ID2, 3, WM_READS(188), 188},
++};
++
++/* continuous speed index */
++static int sp_idx = 0;
++static u16 last = 0, tries = 0;
++
++/*
++ * Pen sampling frequency (Hz) in continuous mode.
++ */
++static int cont_rate = 200;
++module_param(cont_rate, int, 0);
++MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)");
++
++/*
++ * Pen down detection.
++ *
++ * This driver can either poll or use an interrupt to indicate a pen down
++ * event. If the irq request fails then it will fall back to polling mode.
++ */
++static int pen_int = 1;
++module_param(pen_int, int, 0);
++MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)");
++
++/*
++ * Pressure readback.
++ *
++ * Set to 1 to read back pen down pressure
++ */
++static int pressure = 0;
++module_param(pressure, int, 0);
++MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)");
++
++/*
++ * AC97 touch data slot.
++ *
++ * Touch screen readback data ac97 slot
++ */
++static int ac97_touch_slot = 5;
++module_param(ac97_touch_slot, int, 0);
++MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number");
++
++
++/* flush AC97 slot 5 FIFO on pxa machines */
++#ifdef CONFIG_PXA27x
++void wm97xx_acc_pen_up (struct wm97xx* wm)
++{
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(1);
++
++ while (MISR & (1 << 2))
++ MODR;
++}
++#else
++void wm97xx_acc_pen_up (struct wm97xx* wm)
++{
++ int count = 16;
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(1);
++
++ while (count < 16) {
++ MODR;
++ count--;
++ }
++}
++#endif
++
++int wm97xx_acc_pen_down (struct wm97xx* wm)
++{
++ u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES;
++ int reads = 0;
++
++ /* data is never immediately available after pen down irq */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(1);
++
++ if (tries > 5){
++ tries = 0;
++ return RC_PENUP;
++ }
++
++ x = MODR;
++ if (x == last) {
++ tries++;
++ return RC_AGAIN;
++ }
++ last = x;
++ do {
++ if (reads)
++ x= MODR;
++ y= MODR;
++ if (pressure)
++ p = MODR;
++
++ /* are samples valid */
++ if ((x & 0x7000) != WM97XX_ADCSEL_X ||
++ (y & 0x7000) != WM97XX_ADCSEL_Y ||
++ (p & 0x7000) != WM97XX_ADCSEL_PRES)
++ goto up;
++
++ /* coordinate is good */
++ tries = 0;
++ //printk("x %x y %x p %x\n", x,y,p);
++ input_report_abs (wm->input_dev, ABS_X, x & 0xfff);
++ input_report_abs (wm->input_dev, ABS_Y, y & 0xfff);
++ input_report_abs (wm->input_dev, ABS_PRESSURE, p & 0xfff);
++ input_sync (wm->input_dev);
++ reads++;
++ } while (reads < cinfo[sp_idx].reads);
++up:
++ return RC_PENDOWN | RC_AGAIN;
++}
++
++int wm97xx_acc_startup(struct wm97xx* wm)
++{
++ int idx = 0;
++
++ /* check we have a codec */
++ if (wm->ac97 == NULL)
++ return -ENODEV;
++
++ /* Go you big red fire engine */
++ for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) {
++ if (wm->id != cinfo[idx].id)
++ continue;
++ sp_idx = idx;
++ if (cont_rate <= cinfo[idx].speed)
++ break;
++ }
++ wm->acc_rate = cinfo[sp_idx].code;
++ wm->acc_slot = ac97_touch_slot;
++ printk(KERN_INFO "pxa2xx accelerated touchscreen driver, %d samples (sec)\n",
++ cinfo[sp_idx].speed);
++
++ /* codec specific irq config */
++ if (pen_int) {
++ switch (wm->id) {
++ case WM9705_ID2:
++ wm->pen_irq = IRQ_GPIO(4);
++ set_irq_type(IRQ_GPIO(4), IRQT_BOTHEDGE);
++ break;
++ case WM9712_ID2:
++ case WM9713_ID2:
++ /* enable pen down interrupt */
++ /* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */
++ wm->pen_irq = MAINSTONE_AC97_IRQ;
++ wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
++ WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_STICKY, WM97XX_GPIO_WAKE);
++ wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT,
++ WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_NOTSTICKY, WM97XX_GPIO_NOWAKE);
++ break;
++ default:
++ printk(KERN_WARNING "pen down irq not supported on this device\n");
++ pen_int = 0;
++ break;
++ }
++ }
++
++ return 0;
++}
++
++void wm97xx_acc_shutdown(struct wm97xx* wm)
++{
++ /* codec specific deconfig */
++ if (pen_int) {
++ switch (wm->id & 0xffff) {
++ case WM9705_ID2:
++ wm->pen_irq = 0;
++ break;
++ case WM9712_ID2:
++ case WM9713_ID2:
++ /* disable interrupt */
++ wm->pen_irq = 0;
++ break;
++ }
++ }
++}
++
++static struct wm97xx_mach_ops pxa_mach_ops = {
++ .acc_enabled = 1,
++ .acc_pen_up = wm97xx_acc_pen_up,
++ .acc_pen_down = wm97xx_acc_pen_down,
++ .acc_startup = wm97xx_acc_startup,
++ .acc_shutdown = wm97xx_acc_shutdown,
++};
++
++int pxa_wm97xx_probe(struct device *dev)
++{
++ struct wm97xx *wm = dev->driver_data;
++ return wm97xx_register_mach_ops (wm, &pxa_mach_ops);
++}
++
++int pxa_wm97xx_remove(struct device *dev)
++{
++ struct wm97xx *wm = dev->driver_data;
++ wm97xx_unregister_mach_ops (wm);
++ return 0;
++}
++
++static struct device_driver pxa_wm97xx_driver = {
++ .name = "wm97xx-touchscreen",
++ .bus = &wm97xx_bus_type,
++ .owner = THIS_MODULE,
++ .probe = pxa_wm97xx_probe,
++ .remove = pxa_wm97xx_remove
++};
++
++static int __init pxa_wm97xx_init(void)
++{
++ return driver_register(&pxa_wm97xx_driver);
++}
++
++static void __exit pxa_wm97xx_exit(void)
++{
++ driver_unregister(&pxa_wm97xx_driver);
++}
++
++module_init(pxa_wm97xx_init);
++module_exit(pxa_wm97xx_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
++MODULE_DESCRIPTION("wm97xx continuous touch driver for pxa2xx");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17/drivers/input/touchscreen/wm9705.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/drivers/input/touchscreen/wm9705.c 2006-09-19 20:36:47.969052000 +0200
+@@ -0,0 +1,360 @@
++/*
++ * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec.
++ *
++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ * Russell King <rmk@arm.linux.org.uk>
++ *
++ * 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.
++ *
++ * Revision history
++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com>
++ * Added pre and post sample calls.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/input.h>
++#include <linux/delay.h>
++#include <linux/bitops.h>
++#include <linux/wm97xx.h>
++
++#define TS_NAME "wm97xx"
++#define WM9705_VERSION "0.62"
++#define DEFAULT_PRESSURE 0xb0c0
++
++/*
++ * Debug
++ */
++#if 0
++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...)
++#endif
++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
++
++/*
++ * Module parameters
++ */
++
++/*
++ * 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.
++ */
++static int pil = 0;
++module_param(pil, int, 0);
++MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
++
++/*
++ * Set threshold for pressure measurement.
++ *
++ * Pen down pressure below threshold is ignored.
++ */
++static int pressure = DEFAULT_PRESSURE & 0xfff;
++module_param(pressure, int, 0);
++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
++
++/*
++ * 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.
++ */
++static int delay = 4;
++module_param(delay, int, 0);
++MODULE_PARM_DESC(delay, "Set adc sample delay.");
++
++/*
++ * 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.
++ */
++static int pdd = 8;
++module_param(pdd, int, 0);
++MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold");
++
++/*
++ * Set adc mask function.
++ *
++ * Sources of glitch noise, such as signals driving an LCD display, may feed
++ * through to the touch screen plates and affect measurement accuracy. In
++ * order to minimise this, a signal may be applied to the MASK pin to delay or
++ * synchronise the sampling.
++ *
++ * 0 = No delay or sync
++ * 1 = High on pin stops conversions
++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
++ * 3 = Edge triggered, edge on pin starts conversion after delay param
++ */
++static int mask = 0;
++module_param(mask, int, 0);
++MODULE_PARM_DESC(mask, "Set adc mask function.");
++
++/*
++ * ADC sample delay times in uS
++ */
++static const int delay_table[] = {
++ 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(int d)
++{
++ udelay (3 * AC97_LINK_FRAME + delay_table [d]);
++}
++
++/*
++ * set up the physical settings of the WM9705
++ */
++static void init_wm9705_phy(struct wm97xx* wm)
++{
++ u16 dig1 = 0, dig2 = WM97XX_RPR;
++
++ /*
++ * mute VIDEO and AUX as they share X and Y touchscreen
++ * inputs on the WM9705
++ */
++ wm97xx_reg_write(wm, AC97_AUX, 0x8000);
++ wm97xx_reg_write(wm, AC97_VIDEO, 0x8000);
++
++ /* touchpanel pressure current*/
++ if (pil == 2) {
++ dig2 |= WM9705_PIL;
++ dbg("setting pressure measurement current to 400uA.");
++ } else if (pil)
++ dbg("setting pressure measurement current to 200uA.");
++ if(!pil)
++ pressure = 0;
++
++ /* polling mode sample settling delay */
++ if (delay!=4) {
++ if (delay < 0 || delay > 15) {
++ dbg("supplied delay out of range.");
++ delay = 4;
++ }
++ }
++ dig1 &= 0xff0f;
++ dig1 |= WM97XX_DELAY(delay);
++ dbg("setting adc sample delay to %d u Secs.", delay_table[delay]);
++
++ /* WM9705 pdd */
++ dig2 |= (pdd & 0x000f);
++ dbg("setting pdd to Vmid/%d", 1 - (pdd & 0x000f));
++
++ /* mask */
++ dig2 |= ((mask & 0x3) << 4);
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
++}
++
++static int wm9705_digitiser_ioctl(struct wm97xx* wm, int cmd)
++{
++ switch(cmd) {
++ case WM97XX_DIG_START:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] | WM97XX_PRP_DET_DIG);
++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
++ break;
++ case WM97XX_DIG_STOP:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] & ~WM97XX_PRP_DET_DIG);
++ break;
++ case WM97XX_AUX_PREPARE:
++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
++ break;
++ case WM97XX_DIG_RESTORE:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
++ break;
++ case WM97XX_PHY_INIT:
++ init_wm9705_phy(wm);
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static inline int is_pden (struct wm97xx* wm)
++{
++ return wm->dig[2] & WM9705_PDEN;
++}
++
++/*
++ * Read a sample from the WM9705 adc in polling mode.
++ */
++static int wm9705_poll_sample (struct wm97xx* wm, int adcsel, int *sample)
++{
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ if (adcsel & 0x8000)
++ adcsel = ((adcsel & 0x7fff) + 3) << 12;
++
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(adcsel);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
++
++ /* wait 3 AC97 time slots + delay for conversion */
++ poll_delay (delay);
++
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dbg ("adc sample timeout");
++ return RC_PENUP;
++ }
++
++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(adcsel);
++
++ /* check we have correct sample */
++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
++ dbg ("adc wrong sample, read %x got %x", adcsel,
++ *sample & WM97XX_ADCSEL_MASK);
++ return RC_PENUP;
++ }
++
++ if (!(*sample & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++
++ return RC_VALID;
++}
++
++/*
++ * Sample the WM9705 touchscreen in polling mode
++ */
++static int wm9705_poll_touch(struct wm97xx* wm, struct wm97xx_data *data)
++{
++ int rc;
++
++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID)
++ return rc;
++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID)
++ return rc;
++ if (pil) {
++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID)
++ return rc;
++ } else
++ data->p = DEFAULT_PRESSURE;
++
++ return RC_VALID;
++}
++
++/*
++ * Enable WM9705 continuous mode, i.e. touch data is streamed across an AC97 slot
++ */
++static int wm9705_acc_enable (struct wm97xx* wm, int enable)
++{
++ u16 dig1, dig2;
++ int ret = 0;
++
++ dig1 = wm->dig[1];
++ dig2 = wm->dig[2];
++
++ if (enable) {
++ /* continous mode */
++ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0)
++ return ret;
++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
++ WM97XX_DELAY (delay) |
++ WM97XX_SLT (wm->acc_slot) |
++ WM97XX_RATE (wm->acc_rate);
++ if (pil)
++ dig1 |= WM97XX_ADCSEL_PRES;
++ dig2 |= WM9705_PDEN;
++ } else {
++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
++ dig2 &= ~WM9705_PDEN;
++ if (wm->mach_ops->acc_shutdown)
++ wm->mach_ops->acc_shutdown(wm);
++ }
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
++ return ret;
++}
++
++struct wm97xx_codec_drv wm97xx_codec = {
++ .id = WM9705_ID2,
++ .name = "wm9705",
++ .poll_sample = wm9705_poll_sample,
++ .poll_touch = wm9705_poll_touch,
++ .acc_enable = wm9705_acc_enable,
++ .digitiser_ioctl = wm9705_digitiser_ioctl,
++};
++
++EXPORT_SYMBOL_GPL(wm97xx_codec);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("WM9705 Touch Screen Driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17/drivers/input/touchscreen/wm9712.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/drivers/input/touchscreen/wm9712.c 2006-09-19 20:36:47.969052000 +0200
+@@ -0,0 +1,464 @@
++/*
++ * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs.
++ *
++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ * Russell King <rmk@arm.linux.org.uk>
++ *
++ * 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.
++ *
++ * Revision history
++ * 4th Jul 2005 Initial version.
++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com>
++ * Added pre and post sample calls.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/input.h>
++#include <linux/delay.h>
++#include <linux/bitops.h>
++#include <linux/wm97xx.h>
++
++#define TS_NAME "wm97xx"
++#define WM9712_VERSION "0.61"
++#define DEFAULT_PRESSURE 0xb0c0
++
++/*
++ * Debug
++ */
++#if 0
++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...)
++#endif
++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
++
++/*
++ * Module parameters
++ */
++
++/*
++ * 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 event.
++ */
++static int rpu = 3;
++module_param(rpu, int, 0);
++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
++
++/*
++ * 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.
++ */
++static int pil = 0;
++module_param(pil, int, 0);
++MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
++
++/*
++ * Set threshold for pressure measurement.
++ *
++ * Pen down pressure below threshold is ignored.
++ */
++static int pressure = DEFAULT_PRESSURE & 0xfff;
++module_param(pressure, int, 0);
++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
++
++/*
++ * 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.
++ */
++static int delay = 3;
++module_param(delay, int, 0);
++MODULE_PARM_DESC(delay, "Set adc sample delay.");
++
++/*
++ * Set five_wire = 1 to use a 5 wire touchscreen.
++ *
++ * NOTE: Five wire mode does not allow for readback of pressure.
++ */
++static int five_wire;
++module_param(five_wire, int, 0);
++MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen.");
++
++/*
++ * Set adc mask function.
++ *
++ * Sources of glitch noise, such as signals driving an LCD display, may feed
++ * through to the touch screen plates and affect measurement accuracy. In
++ * order to minimise this, a signal may be applied to the MASK pin to delay or
++ * synchronise the sampling.
++ *
++ * 0 = No delay or sync
++ * 1 = High on pin stops conversions
++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
++ * 3 = Edge triggered, edge on pin starts conversion after delay param
++ */
++static int mask = 0;
++module_param(mask, int, 0);
++MODULE_PARM_DESC(mask, "Set adc mask function.");
++
++/*
++ * Coordinate Polling Enable.
++ *
++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
++ * for every poll.
++ */
++static int coord = 0;
++module_param(coord, int, 0);
++MODULE_PARM_DESC(coord, "Polling coordinate mode");
++
++/*
++ * ADC sample delay times in uS
++ */
++static const int delay_table[] = {
++ 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(int d)
++{
++ udelay (3 * AC97_LINK_FRAME + delay_table [d]);
++}
++
++/*
++ * set up the physical settings of the WM9712
++ */
++static void init_wm9712_phy(struct wm97xx* wm)
++{
++ u16 dig1 = 0;
++ u16 dig2 = WM97XX_RPR | WM9712_RPU(1);
++
++ /* WM9712 rpu */
++ if (rpu) {
++ dig2 &= 0xffc0;
++ dig2 |= WM9712_RPU(rpu);
++ dbg("setting pen detect pull-up to %d Ohms",64000 / rpu);
++ }
++
++ /* touchpanel pressure current*/
++ if (pil == 2) {
++ dig2 |= WM9712_PIL;
++ dbg("setting pressure measurement current to 400uA.");
++ } else if (pil)
++ dbg("setting pressure measurement current to 200uA.");
++ if(!pil)
++ pressure = 0;
++
++ /* WM9712 five wire */
++ if (five_wire) {
++ dig2 |= WM9712_45W;
++ dbg("setting 5-wire touchscreen mode.");
++ }
++
++ /* polling mode sample settling delay */
++ if (delay < 0 || delay > 15) {
++ dbg("supplied delay out of range.");
++ delay = 4;
++ }
++ dig1 &= 0xff0f;
++ dig1 |= WM97XX_DELAY(delay);
++ dbg("setting adc sample delay to %d u Secs.", delay_table[delay]);
++
++ /* mask */
++ dig2 |= ((mask & 0x3) << 6);
++ if (mask) {
++ u16 reg;
++ /* Set GPIO4 as Mask Pin*/
++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4);
++ }
++
++ /* wait - coord mode */
++ if(coord)
++ dig2 |= WM9712_WAIT;
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
++}
++
++static int wm9712_digitiser_ioctl(struct wm97xx* wm, int cmd)
++{
++ u16 dig2 = wm->dig[2];
++
++ switch(cmd) {
++ case WM97XX_DIG_START:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 | WM97XX_PRP_DET_DIG);
++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
++ break;
++ case WM97XX_DIG_STOP:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 & ~WM97XX_PRP_DET_DIG);
++ break;
++ case WM97XX_AUX_PREPARE:
++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
++ break;
++ case WM97XX_DIG_RESTORE:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
++ break;
++ case WM97XX_PHY_INIT:
++ init_wm9712_phy(wm);
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static inline int is_pden (struct wm97xx* wm)
++{
++ return wm->dig[2] & WM9712_PDEN;
++}
++
++/*
++ * Read a sample from the WM9712 adc in polling mode.
++ */
++static int wm9712_poll_sample (struct wm97xx* wm, int adcsel, int *sample)
++{
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ if (adcsel & 0x8000)
++ adcsel = ((adcsel & 0x7fff) + 3) << 12;
++
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(adcsel);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
++
++ /* wait 3 AC97 time slots + delay for conversion */
++ poll_delay (delay);
++
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dbg ("adc sample timeout");
++ return RC_PENUP;
++ }
++
++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(adcsel);
++
++ /* check we have correct sample */
++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
++ dbg ("adc wrong sample, read %x got %x", adcsel,
++ *sample & WM97XX_ADCSEL_MASK);
++ return RC_PENUP;
++ }
++
++ if (!(*sample & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++
++ return RC_VALID;
++}
++
++/*
++ * Read a coord from the WM9712 adc in polling mode.
++ */
++static int wm9712_poll_coord (struct wm97xx* wm, struct wm97xx_data *data)
++{
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
++ WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay));
++
++ /* wait 3 AC97 time slots + delay for conversion and read x */
++ poll_delay(delay);
++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dbg ("adc sample timeout");
++ return RC_PENUP;
++ }
++
++ /* read back y data */
++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (pil)
++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ else
++ data->p = DEFAULT_PRESSURE;
++
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
++
++ /* check we have correct sample */
++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
++ goto err;
++ if(pil && !(data->p & WM97XX_ADCSEL_PRES))
++ goto err;
++
++ if (!(data->x & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++ return RC_VALID;
++err:
++ return RC_PENUP;
++}
++
++/*
++ * Sample the WM9712 touchscreen in polling mode
++ */
++static int wm9712_poll_touch(struct wm97xx* wm, struct wm97xx_data *data)
++{
++ int rc;
++
++ if(coord) {
++ if((rc = wm9712_poll_coord(wm, data)) != RC_VALID)
++ return rc;
++ } else {
++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID)
++ return rc;
++
++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID)
++ return rc;
++
++ if (pil && !five_wire) {
++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID)
++ return rc;
++ } else
++ data->p = DEFAULT_PRESSURE;
++ }
++ return RC_VALID;
++}
++
++/*
++ * Enable WM9712 continuous mode, i.e. touch data is streamed across an AC97 slot
++ */
++static int wm9712_acc_enable (struct wm97xx* wm, int enable)
++{
++ u16 dig1, dig2;
++ int ret = 0;
++
++ dig1 = wm->dig[1];
++ dig2 = wm->dig[2];
++
++ if (enable) {
++ /* continous mode */
++ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0)
++ return ret;
++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
++ WM97XX_DELAY (delay) |
++ WM97XX_SLT (wm->acc_slot) |
++ WM97XX_RATE (wm->acc_rate);
++ if (pil)
++ dig1 |= WM97XX_ADCSEL_PRES;
++ dig2 |= WM9712_PDEN;
++ } else {
++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
++ dig2 &= ~WM9712_PDEN;
++ if (wm->mach_ops->acc_shutdown)
++ wm->mach_ops->acc_shutdown(wm);
++ }
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
++ return 0;
++}
++
++struct wm97xx_codec_drv wm97xx_codec = {
++ .id = WM9712_ID2,
++ .name = "wm9712",
++ .poll_sample = wm9712_poll_sample,
++ .poll_touch = wm9712_poll_touch,
++ .acc_enable = wm9712_acc_enable,
++ .digitiser_ioctl = wm9712_digitiser_ioctl,
++};
++
++EXPORT_SYMBOL_GPL(wm97xx_codec);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("WM9712 Touch Screen Driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17/drivers/input/touchscreen/wm9713.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/drivers/input/touchscreen/wm9713.c 2006-09-19 20:36:47.969052000 +0200
+@@ -0,0 +1,461 @@
++/*
++ * wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec.
++ *
++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ * Russell King <rmk@arm.linux.org.uk>
++ *
++ * 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.
++ *
++ * Revision history
++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com>
++ * Added pre and post sample calls.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/input.h>
++#include <linux/delay.h>
++#include <linux/bitops.h>
++#include <linux/wm97xx.h>
++
++#define TS_NAME "wm97xx"
++#define WM9713_VERSION "0.53"
++#define DEFAULT_PRESSURE 0xb0c0
++
++/*
++ * Debug
++ */
++#if 0
++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...)
++#endif
++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
++
++/*
++ * Module parameters
++ */
++
++/*
++ * 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 event.
++ */
++static int rpu = 1;
++module_param(rpu, int, 0);
++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
++
++/*
++ * 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.
++ */
++static int pil = 0;
++module_param(pil, int, 0);
++MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
++
++/*
++ * Set threshold for pressure measurement.
++ *
++ * Pen down pressure below threshold is ignored.
++ */
++static int pressure = DEFAULT_PRESSURE & 0xfff;
++module_param(pressure, int, 0);
++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
++
++/*
++ * 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.
++ */
++static int delay = 4;
++module_param(delay, int, 0);
++MODULE_PARM_DESC(delay, "Set adc sample delay.");
++
++/*
++ * Set adc mask function.
++ *
++ * Sources of glitch noise, such as signals driving an LCD display, may feed
++ * through to the touch screen plates and affect measurement accuracy. In
++ * order to minimise this, a signal may be applied to the MASK pin to delay or
++ * synchronise the sampling.
++ *
++ * 0 = No delay or sync
++ * 1 = High on pin stops conversions
++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
++ * 3 = Edge triggered, edge on pin starts conversion after delay param
++ */
++static int mask = 0;
++module_param(mask, int, 0);
++MODULE_PARM_DESC(mask, "Set adc mask function.");
++
++/*
++ * Coordinate Polling Enable.
++ *
++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
++ * for every poll.
++ */
++static int coord = 1;
++module_param(coord, int, 0);
++MODULE_PARM_DESC(coord, "Polling coordinate mode");
++
++/*
++ * ADC sample delay times in uS
++ */
++static const int delay_table[] = {
++ 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(int d)
++{
++ udelay (3 * AC97_LINK_FRAME + delay_table [d]);
++}
++
++/*
++ * set up the physical settings of the WM9713
++ */
++static void init_wm9713_phy(struct wm97xx* wm)
++{
++ u16 dig1 = 0, dig2, dig3;
++
++ /* 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.");
++ if(!pil)
++ pressure = 0;
++
++ /* sample settling delay */
++ if (delay < 0 || delay > 15) {
++ info ("supplied delay out of range.");
++ delay = 4;
++ info("setting adc sample delay to %d u Secs.", delay_table[delay]);
++ }
++ dig2 &= 0xff0f;
++ dig2 |= WM97XX_DELAY(delay);
++
++ /* mask */
++ dig3 |= ((mask & 0x3) << 4);
++ if(coord)
++ dig3 |= WM9713_WAIT;
++
++ wm->misc = wm97xx_reg_read(wm, 0x5a);
++
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0);
++}
++
++static int wm9713_digitiser_ioctl(struct wm97xx* wm, int cmd)
++{
++ u16 val = 0;
++
++ switch(cmd){
++ case WM97XX_DIG_START:
++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] | WM97XX_PRP_DET_DIG);
++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
++ break;
++ case WM97XX_DIG_STOP:
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] & ~WM97XX_PRP_DET_DIG);
++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000);
++ break;
++ case WM97XX_AUX_PREPARE:
++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG);
++ break;
++ case WM97XX_DIG_RESTORE:
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]);
++ break;
++ case WM97XX_PHY_INIT:
++ init_wm9713_phy(wm);
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static inline int is_pden (struct wm97xx* wm)
++{
++ return wm->dig[2] & WM9713_PDEN;
++}
++
++/*
++ * Read a sample from the WM9713 adc in polling mode.
++ */
++static int wm9713_poll_sample (struct wm97xx* wm, int adcsel, int *sample)
++{
++ u16 dig1;
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ if (adcsel & 0x8000)
++ adcsel = 1 << ((adcsel & 0x7fff) + 3);
++
++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
++ dig1 &= ~WM9713_ADCSEL_MASK;
++
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(adcsel);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | adcsel |WM9713_POLL);
++
++ /* wait 3 AC97 time slots + delay for conversion */
++ poll_delay(delay);
++
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dbg ("adc sample timeout");
++ return RC_PENUP;
++ }
++
++ *sample =wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(adcsel);
++
++ /* check we have correct sample */
++ if ((*sample & WM97XX_ADCSRC_MASK) != ffs(adcsel >> 1) << 12) {
++ dbg ("adc wrong sample, read %x got %x", adcsel,
++ *sample & WM97XX_ADCSRC_MASK);
++ return RC_PENUP;
++ }
++
++ if (!(*sample & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++
++ return RC_VALID;
++}
++
++/*
++ * Read a coordinate from the WM9713 adc in polling mode.
++ */
++static int wm9713_poll_coord (struct wm97xx* wm, struct wm97xx_data *data)
++{
++ u16 dig1;
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
++ dig1 &= ~WM9713_ADCSEL_MASK;
++ if(pil)
++ dig1 |= WM97XX_ADCSEL_PRES;
++
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | WM9713_POLL | WM9713_COO);
++
++ /* wait 3 AC97 time slots + delay for conversion */
++ poll_delay(delay);
++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dbg ("adc sample timeout");
++ return RC_PENUP;
++ }
++
++ /* read back data */
++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (pil)
++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ else
++ data->p = DEFAULT_PRESSURE;
++
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
++
++ /* check we have correct sample */
++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
++ goto err;
++ if(pil && !(data->p & WM97XX_ADCSEL_PRES))
++ goto err;
++
++ if (!(data->x & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++ return RC_VALID;
++err:
++ return RC_PENUP;
++}
++
++/*
++ * Sample the WM9713 touchscreen in polling mode
++ */
++static int wm9713_poll_touch(struct wm97xx* wm, struct wm97xx_data *data)
++{
++ int rc;
++
++ if(coord) {
++ if((rc = wm9713_poll_coord(wm, data)) != RC_VALID)
++ return rc;
++ } else {
++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x)) != RC_VALID)
++ return rc;
++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y)) != RC_VALID)
++ return rc;
++ if (pil) {
++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES, &data->p)) != RC_VALID)
++ return rc;
++ } else
++ data->p = DEFAULT_PRESSURE;
++ }
++ return RC_VALID;
++}
++
++/*
++ * Enable WM9713 continuous mode, i.e. touch data is streamed across an AC97 slot
++ */
++static int wm9713_acc_enable (struct wm97xx* wm, int enable)
++{
++ u16 dig1, dig2, dig3;
++ int ret = 0;
++
++ dig1 = wm->dig[0];
++ dig2 = wm->dig[1];
++ dig3 = wm->dig[2];
++
++ if (enable) {
++ /* continous mode */
++ if (wm->mach_ops->acc_startup &&
++ (ret = wm->mach_ops->acc_startup(wm)) < 0)
++ return ret;
++
++ dig1 &= ~WM9713_ADCSEL_MASK;
++ dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X | WM9713_ADCSEL_Y;
++ if (pil)
++ dig1 |= WM9713_ADCSEL_PRES;
++ dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK | WM97XX_CM_RATE_MASK);
++ dig2 |= WM97XX_SLEN | WM97XX_DELAY (delay) |
++ WM97XX_SLT (wm->acc_slot) | WM97XX_RATE (wm->acc_rate);
++ dig3 |= WM9713_PDEN;
++ } else {
++ dig1 &= ~(WM9713_CTC | WM9713_COO);
++ dig2 &= ~WM97XX_SLEN;
++ dig3 &= ~WM9713_PDEN;
++ if (wm->mach_ops->acc_shutdown)
++ wm->mach_ops->acc_shutdown(wm);
++ }
++
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
++ return ret;
++}
++
++struct wm97xx_codec_drv wm97xx_codec = {
++ .id = WM9713_ID2,
++ .name = "wm9713",
++ .poll_sample = wm9713_poll_sample,
++ .poll_touch = wm9713_poll_touch,
++ .acc_enable = wm9713_acc_enable,
++ .digitiser_ioctl = wm9713_digitiser_ioctl,
++};
++
++EXPORT_SYMBOL_GPL(wm97xx_codec);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("WM9713 Touch Screen Driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17/drivers/input/touchscreen/wm97xx-core.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/drivers/input/touchscreen/wm97xx-core.c 2006-09-19 20:36:47.969052000 +0200
+@@ -0,0 +1,912 @@
++/*
++ * wm97xx-core.c -- Touch screen driver core for Wolfson WM9705, WM9712
++ * and WM9713 AC97 Codecs.
++ *
++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ * Russell King <rmk@arm.linux.org.uk>
++ *
++ * 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.
++ *
++ * Notes:
++ *
++ * Features:
++ * - supports WM9705, WM9712, WM9713
++ * - polling mode
++ * - continuous mode (arch-dependent)
++ * - adjustable rpu/dpp settings
++ * - adjustable pressure current
++ * - adjustable sample settle delay
++ * - 4 and 5 wire touchscreens (5 wire is WM9712 only)
++ * - pen down detection
++ * - battery monitor
++ * - sample AUX adc's
++ * - power management
++ * - codec GPIO
++ * - codec event notification
++ * Todo
++ * - Support for async sampling control for noisy LCD'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
++ * codec_mutexing 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.
++ * 16th Nov 2003 Ian Molton <spyro@f2s.com>
++ * Modified so that it suits the new 2.6 driver model.
++ * 25th Jan 2004 Andrew Zabolotny <zap@homelink.ru>
++ * Implemented IRQ-driven pen down detection, implemented
++ * the private API meant to be exposed to platform-specific
++ * drivers, reorganized the driver so that it supports
++ * an arbitrary number of devices.
++ * 1st Feb 2004 Moved continuous mode handling to a separate
++ * architecture-dependent file. For now only PXA
++ * built-in AC97 controller is supported (pxa-ac97-wm97xx.c).
++ * 11th Feb 2004 Reduced CPU usage by keeping a cached copy of both
++ * digitizer registers instead of reading them every time.
++ * A reorganization of the whole code for better
++ * error handling.
++ * 17th Apr 2004 Added BMON support.
++ * 17th Nov 2004 Added codec GPIO, codec event handling (real and virtual
++ * GPIOs) and 2.6 power management.
++ * 29th Nov 2004 Added WM9713 support.
++ * 4th Jul 2005 Moved codec specific code out to seperate files.
++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com>
++ * Added bus interface.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/string.h>
++#include <linux/proc_fs.h>
++#include <linux/pm.h>
++#include <linux/interrupt.h>
++#include <linux/bitops.h>
++#include <linux/workqueue.h>
++#include <linux/device.h>
++#include <linux/wm97xx.h>
++#include <asm/uaccess.h>
++#include <asm/io.h>
++
++#define TS_NAME "wm97xx"
++#define WM_CORE_VERSION "0.63"
++#define DEFAULT_PRESSURE 0xb0c0
++
++/*
++ * WM97xx - enable/disable AUX ADC sysfs
++ */
++static int aux_sys = 1;
++module_param(aux_sys, int, 0);
++MODULE_PARM_DESC(aux_sys, "enable AUX ADC sysfs entries");
++
++/*
++ * WM97xx - enable/disable codec status sysfs
++ */
++static int status_sys = 1;
++module_param(status_sys, int, 0);
++MODULE_PARM_DESC(status_sys, "enable codec status sysfs entries");
++
++/*
++ * Touchscreen absolute values
++ *
++ * These parameters are used to help the input layer discard out of
++ * range readings and reduce jitter etc.
++ *
++ * o min, max:- indicate the min and max values your touch screen returns
++ * o fuzz:- use a higher number to reduce jitter
++ *
++ * The default values correspond to Mainstone II in QVGA mode
++ *
++ * Please read
++ * Documentation/input/input-programming.txt for more details.
++ */
++
++static int abs_x[3] = {350,3900,5};
++module_param_array(abs_x, int, NULL, 0);
++MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz");
++
++static int abs_y[3] = {320,3750,40};
++module_param_array(abs_y, int, NULL, 0);
++MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz");
++
++static int abs_p[3] = {0,150,4};
++module_param_array(abs_p, int, NULL, 0);
++MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz");
++
++/*
++ * Debug
++ */
++#if 0
++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...)
++#endif
++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
++
++/* codec AC97 IO access */
++int wm97xx_reg_read(struct wm97xx *wm, u16 reg)
++{
++ if (wm->ac97)
++ return wm->ac97->bus->ops->read(wm->ac97, reg);
++ else
++ return -1;
++}
++
++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val)
++{
++ /* cache digitiser registers */
++ if(reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3)
++ wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val;
++
++ /* cache gpio regs */
++ if(reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE)
++ wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val;
++
++ /* wm9713 irq reg */
++ if(reg == 0x5a)
++ wm->misc = val;
++
++ if (wm->ac97)
++ wm->ac97->bus->ops->write(wm->ac97, reg, val);
++}
++
++
++/**
++ * wm97xx_read_aux_adc - Read the aux adc.
++ * @wm: wm97xx device.
++ * @adcsel: codec ADC to be read
++ *
++ * Reads the selected AUX ADC.
++ */
++
++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
++{
++ int power_adc = 0, auxval;
++ u16 power = 0;
++
++ /* get codec */
++ mutex_lock(&wm->codec_mutex);
++
++ /* When the touchscreen is not in use, we may have to power up the AUX ADC
++ * before we can use sample the AUX inputs->
++ */
++ if (wm->id == WM9713_ID2 &&
++ (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) {
++ power_adc = 1;
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff);
++ }
++
++ /* Prepare the codec for AUX reading */
++ wm->codec->digitiser_ioctl(wm, WM97XX_AUX_PREPARE);
++
++ /* Turn polling mode on to read AUX ADC */
++ wm->pen_probably_down = 1;
++ wm->codec->poll_sample(wm, adcsel, &auxval);
++
++ if (power_adc)
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000);
++
++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_RESTORE);
++
++ wm->pen_probably_down = 0;
++
++ mutex_unlock(&wm->codec_mutex);
++ return auxval & 0xfff;
++}
++
++#define WM97XX_AUX_ATTR(name,input) \
++static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \
++{ \
++ struct wm97xx *wm = (struct wm97xx*)dev->driver_data; \
++ return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, input)); \
++} \
++static DEVICE_ATTR(name, 0444, name##_show, NULL)
++
++WM97XX_AUX_ATTR(aux1, WM97XX_AUX_ID1);
++WM97XX_AUX_ATTR(aux2, WM97XX_AUX_ID2);
++WM97XX_AUX_ATTR(aux3, WM97XX_AUX_ID3);
++WM97XX_AUX_ATTR(aux4, WM97XX_AUX_ID4);
++
++#define WM97XX_STATUS_ATTR(name) \
++static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \
++{ \
++ struct wm97xx *wm = (struct wm97xx*)dev->driver_data; \
++ return sprintf(buf, "%d\n", wm97xx_reg_read(wm, AC97_GPIO_STATUS)); \
++} \
++static DEVICE_ATTR(name, 0444, name##_show, NULL)
++
++WM97XX_STATUS_ATTR(gpio);
++
++static int wm97xx_sys_add(struct device *dev)
++{
++ if (aux_sys) {
++ device_create_file(dev, &dev_attr_aux1);
++ device_create_file(dev, &dev_attr_aux2);
++ device_create_file(dev, &dev_attr_aux3);
++ device_create_file(dev, &dev_attr_aux4);
++ }
++ if (status_sys)
++ device_create_file(dev, &dev_attr_gpio);
++ return 0;
++}
++
++static void wm97xx_sys_remove(struct device *dev)
++{
++ if (status_sys)
++ device_remove_file(dev, &dev_attr_gpio);
++ if (aux_sys) {
++ device_remove_file(dev, &dev_attr_aux1);
++ device_remove_file(dev, &dev_attr_aux2);
++ device_remove_file(dev, &dev_attr_aux3);
++ device_remove_file(dev, &dev_attr_aux4);
++ }
++}
++
++/**
++ * wm97xx_get_gpio - Get the status of a codec GPIO.
++ * @wm: wm97xx device.
++ * @gpio: gpio
++ *
++ * Get the status of a codec GPIO pin
++ */
++
++wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio)
++{
++ u16 status;
++ wm97xx_gpio_status_t ret;
++
++ mutex_lock(&wm->codec_mutex);
++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
++
++ if (status & gpio)
++ ret = WM97XX_GPIO_HIGH;
++ else
++ ret = WM97XX_GPIO_LOW;
++
++ mutex_unlock(&wm->codec_mutex);
++ return ret;
++}
++
++/**
++ * wm97xx_set_gpio - Set the status of a codec GPIO.
++ * @wm: wm97xx device.
++ * @gpio: gpio
++ *
++ *
++ * Set the status of a codec GPIO pin
++ */
++
++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
++ wm97xx_gpio_status_t status)
++{
++ u16 reg;
++
++ mutex_lock(&wm->codec_mutex);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
++
++ if (status & WM97XX_GPIO_HIGH)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ if (wm->id == WM9712_ID2)
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1);
++ else
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg);
++ mutex_unlock(&wm->codec_mutex);
++}
++
++/*
++ * Codec GPIO pin configuration, this set's pin direction, polarity,
++ * stickyness and wake up.
++ */
++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir,
++ wm97xx_gpio_pol_t pol, wm97xx_gpio_sticky_t sticky,
++ wm97xx_gpio_wake_t wake)
++{
++ u16 reg;
++
++ mutex_lock(&wm->codec_mutex);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
++
++ if (pol == WM97XX_GPIO_POL_HIGH)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
++
++ if (sticky == WM97XX_GPIO_STICKY)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
++
++ if (wake == WM97XX_GPIO_WAKE)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
++
++ if (dir == WM97XX_GPIO_IN)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg);
++ mutex_unlock(&wm->codec_mutex);
++}
++
++/*
++ * Handle a pen down interrupt.
++ */
++static void wm97xx_pen_irq_worker(void *ptr)
++{
++ struct wm97xx *wm = (struct wm97xx *) ptr;
++
++ /* do we need to enable the touch panel reader */
++ if (wm->id == WM9705_ID2) {
++ if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN)
++ wm->pen_is_down = 1;
++ else
++ wm->pen_is_down = 0;
++ wake_up_interruptible(&wm->pen_irq_wait);
++ } else {
++ u16 status, pol;
++ mutex_lock(&wm->codec_mutex);
++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
++ pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
++
++ if (WM97XX_GPIO_13 & pol & status) {
++ wm->pen_is_down = 1;
++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol & ~WM97XX_GPIO_13);
++ } else {
++ wm->pen_is_down = 0;
++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol | WM97XX_GPIO_13);
++ }
++
++ if (wm->id == WM9712_ID2)
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status & ~WM97XX_GPIO_13) << 1);
++ else
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, status & ~WM97XX_GPIO_13);
++ mutex_unlock(&wm->codec_mutex);
++ wake_up_interruptible(&wm->pen_irq_wait);
++ }
++
++ if (!wm->pen_is_down && wm->mach_ops && wm->mach_ops->acc_enabled)
++ wm->mach_ops->acc_pen_up(wm);
++ enable_irq(wm->pen_irq);
++}
++
++/*
++ * Codec PENDOWN irq handler
++ *
++ * We have to disable the codec interrupt in the handler because it can
++ * take upto 1ms to clear the interrupt source. The interrupt is then enabled
++ * again in the slow handler when the source has been cleared.
++ */
++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id,
++ struct pt_regs *regs)
++{
++ struct wm97xx *wm = (struct wm97xx *) dev_id;
++ disable_irq(wm->pen_irq);
++ queue_work(wm->pen_irq_workq, &wm->pen_event_work);
++ return IRQ_HANDLED;
++}
++
++/*
++ * initialise pen IRQ handler and workqueue
++ */
++static int wm97xx_init_pen_irq(struct wm97xx *wm)
++{
++ u16 reg;
++
++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker, wm);
++ if ((wm->pen_irq_workq =
++ create_singlethread_workqueue("kwm97pen")) == NULL) {
++ err("could not create pen irq work queue");
++ wm->pen_irq = 0;
++ return -EINVAL;
++ }
++
++ if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) {
++ err("could not register codec pen down interrupt, will poll for pen down");
++ destroy_workqueue(wm->pen_irq_workq);
++ wm->pen_irq = 0;
++ return -EINVAL;
++ }
++
++ /* enable PEN down on wm9712/13 */
++ if (wm->id != WM9705_ID2) {
++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg & 0xfffb);
++ reg = wm97xx_reg_read(wm, 0x5a);
++ wm97xx_reg_write(wm, 0x5a, reg & ~0x0001);
++ }
++
++ return 0;
++}
++
++/* Private struct for communication between struct wm97xx_tshread
++ * and wm97xx_read_samples */
++struct ts_state {
++ int sleep_time;
++ int min_sleep_time;
++};
++
++static int wm97xx_read_samples(struct wm97xx *wm, struct ts_state *state)
++{
++ struct wm97xx_data data;
++ int rc;
++
++ mutex_lock(&wm->codec_mutex);
++
++ if (wm->mach_ops && wm->mach_ops->acc_enabled)
++ rc = wm->mach_ops->acc_pen_down(wm);
++ else
++ rc = wm->codec->poll_touch(wm, &data);
++
++ if (rc & RC_PENUP) {
++ if (wm->pen_is_down) {
++ wm->pen_is_down = 0;
++ dbg("pen up");
++ input_report_abs(wm->input_dev, ABS_PRESSURE, 0);
++ input_sync(wm->input_dev);
++ } else if (!(rc & RC_AGAIN)) {
++ /* We need high frequency updates only while pen is down,
++ * the user never will be able to touch screen faster than
++ * a few times per second... On the other hand, when the
++ * user is actively working with the touchscreen we don't
++ * want to lose the quick response. So we will slowly
++ * increase sleep time after the pen is up and quicky
++ * restore it to ~one task switch when pen is down again.
++ */
++ if (state->sleep_time < HZ / 10)
++ state->sleep_time++;
++ }
++
++ } else if (rc & RC_VALID) {
++ dbg("pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n",
++ data.x >> 12, data.x & 0xfff, data.y >> 12,
++ data.y & 0xfff, data.p >> 12, data.p & 0xfff);
++ input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff);
++ input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff);
++ input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff);
++ input_sync(wm->input_dev);
++ wm->pen_is_down = 1;
++ state->sleep_time = state->min_sleep_time;
++ } else if (rc & RC_PENDOWN) {
++ dbg("pen down");
++ wm->pen_is_down = 1;
++ state->sleep_time = state->min_sleep_time;
++ }
++
++ mutex_unlock(&wm->codec_mutex);
++ return rc;
++}
++
++/*
++* The touchscreen sample reader thread.
++*/
++static int wm97xx_ts_read(void *data)
++{
++ int rc;
++ struct ts_state state;
++ struct wm97xx *wm = (struct wm97xx *) data;
++
++ /* set up thread context */
++ wm->ts_task = current;
++ daemonize("kwm97xxts");
++
++ if (wm->codec == NULL) {
++ wm->ts_task = NULL;
++ printk(KERN_ERR "codec is NULL, bailing\n");
++ }
++
++ complete(&wm->ts_init);
++ wm->pen_is_down = 0;
++ state.min_sleep_time = HZ >= 100 ? HZ / 100 : 1;
++ if (state.min_sleep_time < 1)
++ state.min_sleep_time = 1;
++ state.sleep_time = state.min_sleep_time;
++
++ /* touch reader loop */
++ while (wm->ts_task) {
++ do {
++ try_to_freeze();
++ rc = wm97xx_read_samples(wm, &state);
++ } while (rc & RC_AGAIN);
++ if (!wm->pen_is_down && wm->pen_irq) {
++ /* Nice, we don't have to poll for pen down event */
++ wait_event_interruptible(wm->pen_irq_wait, wm->pen_is_down);
++ } else {
++ set_task_state(current, TASK_INTERRUPTIBLE);
++ schedule_timeout(state.sleep_time);
++ }
++ }
++ complete_and_exit(&wm->ts_exit, 0);
++}
++
++/**
++ * wm97xx_ts_input_open - Open the touch screen input device.
++ * @idev: Input device to be opened.
++ *
++ * Called by the input sub system to open a wm97xx touchscreen device.
++ * Starts the touchscreen thread and touch digitiser.
++ */
++static int wm97xx_ts_input_open(struct input_dev *idev)
++{
++ int ret = 0;
++ struct wm97xx *wm = (struct wm97xx *) idev->private;
++
++ mutex_lock(&wm->codec_mutex);
++ /* first time opened ? */
++ if (wm->ts_use_count++ == 0) {
++ /* start touchscreen thread */
++ init_completion(&wm->ts_init);
++ init_completion(&wm->ts_exit);
++ ret = kernel_thread(wm97xx_ts_read, wm, CLONE_KERNEL);
++
++ if (ret >= 0) {
++ wait_for_completion(&wm->ts_init);
++ if (wm->ts_task == NULL)
++ ret = -EINVAL;
++ } else {
++ mutex_unlock(&wm->codec_mutex);
++ return ret;
++ }
++
++ /* start digitiser */
++ if (wm->mach_ops && wm->mach_ops->acc_enabled)
++ wm->codec->acc_enable(wm, 1);
++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_START);
++
++ /* init pen down/up irq handling */
++ if (wm->pen_irq) {
++ wm97xx_init_pen_irq(wm);
++
++ if (wm->pen_irq == 0) {
++ /* we failed to get an irq for pen down events,
++ * so we resort to polling. kickstart the reader */
++ wm->pen_is_down = 1;
++ wake_up_interruptible(&wm->pen_irq_wait);
++ }
++ }
++ }
++
++ mutex_unlock(&wm->codec_mutex);
++ return 0;
++}
++
++/**
++ * wm97xx_ts_input_close - Close the touch screen input device.
++ * @idev: Input device to be closed.
++ *
++ * Called by the input sub system to close a wm97xx touchscreen device.
++ * Kills the touchscreen thread and stops the touch digitiser.
++ */
++
++static void wm97xx_ts_input_close(struct input_dev *idev)
++{
++ struct wm97xx *wm = (struct wm97xx *) idev->private;
++
++ mutex_lock(&wm->codec_mutex);
++ if (--wm->ts_use_count == 0) {
++ /* destroy workqueues and free irqs */
++ if (wm->pen_irq) {
++ free_irq(wm->pen_irq, wm);
++ destroy_workqueue(wm->pen_irq_workq);
++ }
++
++ /* kill thread */
++ if (wm->ts_task) {
++ wm->ts_task = NULL;
++ wm->pen_is_down = 1;
++ wake_up_interruptible(&wm->pen_irq_wait);
++ wait_for_completion(&wm->ts_exit);
++ wm->pen_is_down = 0;
++ }
++
++ /* stop digitiser */
++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_STOP);
++ if (wm->mach_ops && wm->mach_ops->acc_enabled)
++ wm->codec->acc_enable(wm, 0);
++ }
++ mutex_unlock(&wm->codec_mutex);
++}
++
++static int wm97xx_bus_match(struct device *dev, struct device_driver *drv)
++{
++ return !(strcmp(dev->bus_id,drv->name));
++}
++
++/*
++ * The AC97 audio driver will do all the Codec suspend and resume
++ * tasks. This is just for anything machine specific or extra.
++ */
++static int wm97xx_bus_suspend(struct device *dev, pm_message_t state)
++{
++ int ret = 0;
++
++ if (dev->driver && dev->driver->suspend)
++ ret = dev->driver->suspend(dev, state);
++
++ return ret;
++}
++
++static int wm97xx_bus_resume(struct device *dev)
++{
++ int ret = 0;
++
++ if (dev->driver && dev->driver->resume)
++ ret = dev->driver->resume(dev);
++
++ return ret;
++}
++
++struct bus_type wm97xx_bus_type = {
++ .name = "wm97xx",
++ .match = wm97xx_bus_match,
++ .suspend = wm97xx_bus_suspend,
++ .resume = wm97xx_bus_resume,
++};
++
++static void wm97xx_release(struct device *dev)
++{
++ kfree(dev);
++}
++
++static int wm97xx_probe(struct device *dev)
++{
++ struct wm97xx* wm;
++ int ret = 0, id = 0;
++
++ if (!(wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL)))
++ return -ENOMEM;
++ mutex_init(&wm->codec_mutex);
++
++ init_waitqueue_head(&wm->pen_irq_wait);
++ wm->dev = dev;
++ dev->driver_data = wm;
++ wm->ac97 = to_ac97_t(dev);
++
++ /* check that we have a supported codec */
++ if ((id = wm97xx_reg_read(wm, AC97_VENDOR_ID1)) != WM97XX_ID1) {
++ err("could not find a wm97xx, found a %x instead\n", id);
++ kfree(wm);
++ return -ENODEV;
++ }
++
++ wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2);
++ if(wm->id != wm97xx_codec.id) {
++ err("could not find a the selected codec, please build for wm97%2x", wm->id & 0xff);
++ kfree(wm);
++ return -ENODEV;
++ }
++
++ if((wm->input_dev = input_allocate_device()) == NULL) {
++ kfree(wm);
++ return -ENOMEM;
++ }
++
++ /* set up touch configuration */
++ info("detected a wm97%2x codec", wm->id & 0xff);
++ wm->input_dev->name = "wm97xx touchscreen";
++ wm->input_dev->open = wm97xx_ts_input_open;
++ wm->input_dev->close = wm97xx_ts_input_close;
++ set_bit(EV_ABS, wm->input_dev->evbit);
++ set_bit(ABS_X, wm->input_dev->absbit);
++ set_bit(ABS_Y, wm->input_dev->absbit);
++ set_bit(ABS_PRESSURE, wm->input_dev->absbit);
++ wm->input_dev->absmax[ABS_X] = abs_x[1];
++ wm->input_dev->absmax[ABS_Y] = abs_y[1];
++ wm->input_dev->absmax[ABS_PRESSURE] = abs_p[1];
++ wm->input_dev->absmin[ABS_X] = abs_x[0];
++ wm->input_dev->absmin[ABS_Y] = abs_y[0];
++ wm->input_dev->absmin[ABS_PRESSURE] = abs_p[0];
++ wm->input_dev->absfuzz[ABS_X] = abs_x[2];
++ wm->input_dev->absfuzz[ABS_Y] = abs_y[2];
++ wm->input_dev->absfuzz[ABS_PRESSURE] = abs_p[2];
++ wm->input_dev->private = wm;
++ wm->codec = &wm97xx_codec;
++ if((ret = input_register_device(wm->input_dev)) < 0) {
++ kfree(wm);
++ return -ENOMEM;
++ }
++
++ if(aux_sys)
++ wm97xx_sys_add(dev);
++
++ /* set up physical characteristics */
++ wm->codec->digitiser_ioctl(wm, WM97XX_PHY_INIT);
++
++ /* load gpio cache */
++ wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG);
++ wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
++ wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
++ wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
++ wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
++ wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE);
++
++ /* register our battery device */
++ if (!(wm->battery_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) {
++ ret = -ENOMEM;
++ goto batt_err;
++ }
++ wm->battery_dev->bus = &wm97xx_bus_type;
++ strcpy(wm->battery_dev->bus_id,"wm97xx-battery");
++ wm->battery_dev->driver_data = wm;
++ wm->battery_dev->parent = dev;
++ wm->battery_dev->release = wm97xx_release;
++ if((ret = device_register(wm->battery_dev)) < 0)
++ goto batt_reg_err;
++
++ /* register our extended touch device (for machine specific extensions) */
++ if (!(wm->touch_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) {
++ ret = -ENOMEM;
++ goto touch_err;
++ }
++ wm->touch_dev->bus = &wm97xx_bus_type;
++ strcpy(wm->touch_dev->bus_id,"wm97xx-touchscreen");
++ wm->touch_dev->driver_data = wm;
++ wm->touch_dev->parent = dev;
++ wm->touch_dev->release = wm97xx_release;
++ if((ret = device_register(wm->touch_dev)) < 0)
++ goto touch_reg_err;
++
++ return ret;
++
++touch_reg_err:
++ kfree(wm->touch_dev);
++touch_err:
++ device_unregister(wm->battery_dev);
++batt_reg_err:
++ kfree(wm->battery_dev);
++batt_err:
++ input_unregister_device(wm->input_dev);
++ kfree(wm);
++ return ret;
++}
++
++static int wm97xx_remove(struct device *dev)
++{
++ struct wm97xx *wm = dev_get_drvdata(dev);
++
++ /* Stop touch reader thread */
++ if (wm->ts_task) {
++ wm->ts_task = NULL;
++ wm->pen_is_down = 1;
++ wake_up_interruptible(&wm->pen_irq_wait);
++ wait_for_completion(&wm->ts_exit);
++ }
++ device_unregister(wm->battery_dev);
++ device_unregister(wm->touch_dev);
++ input_unregister_device(wm->input_dev);
++
++ if(aux_sys)
++ wm97xx_sys_remove(dev);
++
++ kfree(wm);
++ return 0;
++}
++
++#ifdef CONFIG_PM
++int wm97xx_resume(struct device* dev)
++{
++ struct wm97xx *wm = dev_get_drvdata(dev);
++
++ /* restore digitiser and gpio's */
++ if(wm->id == WM9713_ID2) {
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]);
++ wm97xx_reg_write(wm, 0x5a, wm->misc);
++ if(wm->ts_use_count) {
++ u16 reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff;
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg);
++ }
++ }
++
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]);
++
++ wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]);
++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]);
++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]);
++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]);
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]);
++ wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]);
++
++ return 0;
++}
++
++#else
++#define wm97xx_resume NULL
++#endif
++
++int wm97xx_register_mach_ops(struct wm97xx *wm, struct wm97xx_mach_ops *mach_ops)
++{
++ mutex_lock(&wm->codec_mutex);
++ if(wm->mach_ops) {
++ mutex_unlock(&wm->codec_mutex);
++ return -EINVAL;
++ }
++ wm->mach_ops = mach_ops;
++ mutex_unlock(&wm->codec_mutex);
++ return 0;
++}
++
++void wm97xx_unregister_mach_ops(struct wm97xx *wm)
++{
++ mutex_lock(&wm->codec_mutex);
++ wm->mach_ops = NULL;
++ mutex_unlock(&wm->codec_mutex);
++}
++
++static struct device_driver wm97xx_driver = {
++ .name = "ac97",
++ .bus = &ac97_bus_type,
++ .owner = THIS_MODULE,
++ .probe = wm97xx_probe,
++ .remove = wm97xx_remove,
++ .resume = wm97xx_resume,
++};
++
++static int __init wm97xx_init(void)
++{
++ int ret;
++
++ info("version %s liam.girdwood@wolfsonmicro.com", WM_CORE_VERSION);
++ if((ret = bus_register(&wm97xx_bus_type)) < 0)
++ return ret;
++ return driver_register(&wm97xx_driver);
++}
++
++static void __exit wm97xx_exit(void)
++{
++ driver_unregister(&wm97xx_driver);
++ bus_unregister(&wm97xx_bus_type);
++}
++
++EXPORT_SYMBOL_GPL(wm97xx_get_gpio);
++EXPORT_SYMBOL_GPL(wm97xx_set_gpio);
++EXPORT_SYMBOL_GPL(wm97xx_config_gpio);
++EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc);
++EXPORT_SYMBOL_GPL(wm97xx_reg_read);
++EXPORT_SYMBOL_GPL(wm97xx_reg_write);
++EXPORT_SYMBOL_GPL(wm97xx_bus_type);
++EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops);
++EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops);
++
++module_init(wm97xx_init);
++module_exit(wm97xx_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17/include/linux/wm97xx.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/include/linux/wm97xx.h 2006-09-19 20:36:47.973052250 +0200
+@@ -0,0 +1,291 @@
++
++/*
++ * Register bits and API for Wolfson WM97xx series of codecs
++ */
++
++#ifndef _LINUX_WM97XX_H
++#define _LINUX_WM97XX_H
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/ac97_codec.h>
++#include <sound/initval.h>
++#include <linux/types.h>
++#include <linux/list.h>
++#include <linux/input.h> /* Input device 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
++
++/*
++ * 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_ADCSEL_MASK 0x7000
++#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_CM_RATE_MASK 0x03f0
++#define WM97XX_RATE(i) (((i & 3) << 8) | ((i & 4) ? 0xf0 : 0))
++#define WM97XX_DELAY(i) ((i << 4) & 0x00f0) /* sample delay times */
++#define WM97XX_DELAY_MASK 0x00f0
++#define WM97XX_SLEN 0x0008 /* slot read back enable */
++#define WM97XX_SLT(i) ((i - 5) & 0x7) /* touchpanel slot selection (5-11) */
++#define WM97XX_SLT_MASK 0x0007
++#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 */
++#define WM97XX_ADCSRC_MASK 0x7000 /* ADC source mask */
++
++#define WM97XX_AUX_ID1 0x8001
++#define WM97XX_AUX_ID2 0x8002
++#define WM97XX_AUX_ID3 0x8003
++#define WM97XX_AUX_ID4 0x8004
++
++
++/* 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_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 */
++
++
++/* 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 */
++#define WM9713_PDEN 0x0800 /* measure only when pen down */
++#define WM9713_ADCSEL_MASK 0x00fe /* ADC selection mask */
++#define WM9713_WAIT 0x0200 /* coordinate wait */
++
++/* 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
++
++/* Codec GPIO's */
++#define WM97XX_MAX_GPIO 16
++#define WM97XX_GPIO_1 (1 << 1)
++#define WM97XX_GPIO_2 (1 << 2)
++#define WM97XX_GPIO_3 (1 << 3)
++#define WM97XX_GPIO_4 (1 << 4)
++#define WM97XX_GPIO_5 (1 << 5)
++#define WM97XX_GPIO_6 (1 << 6)
++#define WM97XX_GPIO_7 (1 << 7)
++#define WM97XX_GPIO_8 (1 << 8)
++#define WM97XX_GPIO_9 (1 << 9)
++#define WM97XX_GPIO_10 (1 << 10)
++#define WM97XX_GPIO_11 (1 << 11)
++#define WM97XX_GPIO_12 (1 << 12)
++#define WM97XX_GPIO_13 (1 << 13)
++#define WM97XX_GPIO_14 (1 << 14)
++#define WM97XX_GPIO_15 (1 << 15)
++
++
++#define AC97_LINK_FRAME 21 /* time in uS for AC97 link frame */
++
++
++/*---------------- Return codes from sample reading functions ---------------*/
++
++/* More data is available; call the sample gathering function again */
++#define RC_AGAIN 0x00000001
++/* The returned sample is valid */
++#define RC_VALID 0x00000002
++/* The pen is up (the first RC_VALID without RC_PENUP means pen is down) */
++#define RC_PENUP 0x00000004
++/* The pen is down (RC_VALID implies RC_PENDOWN, but sometimes it is helpful
++ to tell the handler that the pen is down but we don't know yet his coords,
++ so the handler should not sleep or wait for pendown irq) */
++#define RC_PENDOWN 0x00000008
++
++/* The wm97xx driver provides a private API for writing platform-specific
++ * drivers.
++ */
++
++/* The structure used to return arch specific sampled data into */
++struct wm97xx_data {
++ int x;
++ int y;
++ int p;
++};
++
++/* Codec GPIO status
++ */
++typedef enum {
++ WM97XX_GPIO_HIGH,
++ WM97XX_GPIO_LOW
++} wm97xx_gpio_status_t;
++
++/* Codec GPIO direction
++ */
++typedef enum {
++ WM97XX_GPIO_IN,
++ WM97XX_GPIO_OUT
++} wm97xx_gpio_dir_t;
++
++/* Codec GPIO polarity
++ */
++typedef enum {
++ WM97XX_GPIO_POL_HIGH,
++ WM97XX_GPIO_POL_LOW
++} wm97xx_gpio_pol_t;
++
++/* Codec GPIO sticky
++ */
++typedef enum {
++ WM97XX_GPIO_STICKY,
++ WM97XX_GPIO_NOTSTICKY
++} wm97xx_gpio_sticky_t;
++
++/* Codec GPIO wake
++ */
++typedef enum {
++ WM97XX_GPIO_WAKE,
++ WM97XX_GPIO_NOWAKE
++} wm97xx_gpio_wake_t;
++
++
++/*
++ * Digitiser ioctl commands
++ */
++#define WM97XX_DIG_START 0x1
++#define WM97XX_DIG_STOP 0x2
++#define WM97XX_PHY_INIT 0x3
++#define WM97XX_AUX_PREPARE 0x4
++#define WM97XX_DIG_RESTORE 0x5
++
++struct wm97xx;
++extern struct wm97xx_codec_drv wm97xx_codec;
++
++/*
++ * Codec driver interface - allows mapping to WM9705/12/13 and newer codecs
++ */
++struct wm97xx_codec_drv {
++ u16 id;
++ char *name;
++ int (*poll_sample) (struct wm97xx *, int adcsel, int *sample); /* read 1 sample */
++ int (*poll_touch) (struct wm97xx *, struct wm97xx_data *); /* read X,Y,[P] in poll */
++ int (*digitiser_ioctl) (struct wm97xx *, int cmd);
++ int (*acc_enable) (struct wm97xx *, int enable);
++};
++
++
++/* Machine specific and accelerated touch operations */
++struct wm97xx_mach_ops {
++
++ /* accelerated touch readback - coords are transmited on AC97 link */
++ int acc_enabled;
++ void (*acc_pen_up) (struct wm97xx *);
++ int (*acc_pen_down) (struct wm97xx *);
++ int (*acc_startup) (struct wm97xx *);
++ void (*acc_shutdown) (struct wm97xx *);
++
++ /* pre and post sample - can be used to minimise any analog noise */
++ void (*pre_sample) (int); /* function to run before sampling */
++ void (*post_sample) (int); /* function to run after sampling */
++};
++
++struct wm97xx {
++ u16 dig[3], id, gpio[6], misc; /* Cached codec registers */
++ u16 dig_save[3]; /* saved during aux reading */
++ struct wm97xx_codec_drv *codec; /* attached codec driver*/
++ struct input_dev* input_dev; /* touchscreen input device */
++ ac97_t *ac97; /* ALSA codec access */
++ struct device *dev; /* ALSA device */
++ struct device *battery_dev;
++ struct device *touch_dev;
++ struct wm97xx_mach_ops *mach_ops;
++ struct mutex codec_mutex;
++ struct completion ts_init;
++ struct completion ts_exit;
++ struct task_struct *ts_task;
++ unsigned int pen_irq; /* Pen IRQ number in use */
++ wait_queue_head_t pen_irq_wait; /* Pen IRQ wait queue */
++ struct workqueue_struct *pen_irq_workq;
++ struct work_struct pen_event_work;
++ u16 acc_slot; /* AC97 slot used for acc touch data */
++ u16 acc_rate; /* acc touch data rate */
++ unsigned int ts_use_count;
++ unsigned pen_is_down:1; /* Pen is down */
++ unsigned aux_waiting:1; /* aux measurement waiting */
++ unsigned pen_probably_down:1; /* used in polling mode */
++};
++
++/* Codec GPIO access (not supported on WM9705)
++ * This can be used to set/get codec GPIO and Virtual GPIO status.
++ */
++wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio);
++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
++ wm97xx_gpio_status_t status);
++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio,
++ wm97xx_gpio_dir_t dir,
++ wm97xx_gpio_pol_t pol,
++ wm97xx_gpio_sticky_t sticky,
++ wm97xx_gpio_wake_t wake);
++
++/* codec AC97 IO access */
++int wm97xx_reg_read(struct wm97xx *wm, u16 reg);
++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val);
++
++/* aux adc readback */
++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel);
++
++/* machine ops */
++int wm97xx_register_mach_ops(struct wm97xx *, struct wm97xx_mach_ops *);
++void wm97xx_unregister_mach_ops(struct wm97xx *);
++
++extern struct bus_type wm97xx_bus_type;
++#endif
+Index: linux-2.6.17/drivers/input/touchscreen/Kconfig
+===================================================================
+--- linux-2.6.17.orig/drivers/input/touchscreen/Kconfig 2006-09-19 20:35:35.060495500 +0200
++++ linux-2.6.17/drivers/input/touchscreen/Kconfig 2006-09-19 20:36:47.965051750 +0200
+@@ -121,4 +121,57 @@ config TOUCHSCREEN_TSC2101
+ To compile this driver as a module, choose M here: the
+ module will be called ads7846_ts.
+
++config TOUCHSCREEN_WM97XX
++ tristate "Support for WM97xx AC97 touchscreen controllers"
++ depends SND_AC97_BUS
++
++choice
++ prompt "WM97xx codec type"
++
++config TOUCHSCREEN_WM9705
++ bool "WM9705 Touchscreen interface support"
++ depends on TOUCHSCREEN_WM97XX
++ help
++ Say Y here if you have the wm9705 touchscreen.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called wm9705.
++
++config TOUCHSCREEN_WM9712
++ bool "WM9712 Touchscreen interface support"
++ depends on TOUCHSCREEN_WM97XX
++ help
++ Say Y here if you have the wm9712 touchscreen.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called wm9712.
++
++config TOUCHSCREEN_WM9713
++ bool "WM9713 Touchscreen interface support"
++ depends on TOUCHSCREEN_WM97XX
++ help
++ Say Y here if you have the wm9713 touchscreen.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called wm9713.
++
++endchoice
++
++config TOUCHSCREEN_WM97XX_PXA
++ tristate "WM97xx PXA accelerated touch"
++ depends on TOUCHSCREEN_WM97XX && ARCH_PXA
++ help
++ Say Y here for continuous mode touch on the PXA
++
++ If unsure, say N
++
++ To compile this driver as a module, choose M here: the
++ module will be called pxa-wm97xx
++
+ endif
+Index: linux-2.6.17/drivers/input/touchscreen/Makefile
+===================================================================
+--- linux-2.6.17.orig/drivers/input/touchscreen/Makefile 2006-09-19 20:35:35.072496250 +0200
++++ linux-2.6.17/drivers/input/touchscreen/Makefile 2006-09-19 20:37:40.540337500 +0200
+@@ -4,6 +4,8 @@
+
+ # Each configuration option enables a list of files.
+
++wm97xx-ts-objs := wm97xx-core.o
++
+ obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
+ obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
+ obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o
+@@ -13,3 +15,16 @@ obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtou
+ obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
+ obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
+ obj-$(CONFIG_TOUCHSCREEN_TSC2101) += tsc2101_ts.o
++obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
++obj-$(CONFIG_TOUCHSCREEN_WM97XX_PXA) += pxa-wm97xx.o
++
++ifeq ($(CONFIG_TOUCHSCREEN_WM9713),y)
++wm97xx-ts-objs += wm9713.o
++endif
++
++ifeq ($(CONFIG_TOUCHSCREEN_WM9712),y)
++wm97xx-ts-objs += wm9712.o
++endif
++ifeq ($(CONFIG_TOUCHSCREEN_WM9705),y)
++wm97xx-ts-objs += wm9705.o
++endif
+Index: linux-2.6.17/drivers/input/touchscreen/pxa-wm97xx.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/drivers/input/touchscreen/pxa-wm97xx.c 2006-09-19 20:36:47.965051750 +0200
+@@ -0,0 +1,289 @@
++/*
++ * pxa-wm97xx.c -- pxa-wm97xx Continuous Touch screen driver for
++ * Wolfson WM97xx AC97 Codecs.
++ *
++ * Copyright 2004 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ *
++ * 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.
++ *
++ * Notes:
++ * This is a wm97xx extended touch driver to capture touch
++ * data in a continuous manner on the Intel XScale archictecture
++ *
++ * Features:
++ * - codecs supported:- WM9705, WM9712, WM9713
++ * - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x
++ *
++ * Revision history
++ * 18th Aug 2004 Initial version.
++ * 26th Jul 2005 Improved continous read back and added FIFO flushing.
++ * 06th Sep 2005 Mike Arthur <linux@wolfsonmicro.com>
++ * Moved to using the wm97xx bus
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/irq.h>
++#include <linux/wm97xx.h>
++#include <asm/io.h>
++#include <asm/arch/pxa-regs.h>
++
++#define VERSION "0.13"
++
++struct continuous {
++ u16 id; /* codec id */
++ u8 code; /* continuous code */
++ u8 reads; /* number of coord reads per read cycle */
++ u32 speed; /* number of coords per second */
++};
++
++#define WM_READS(sp) ((sp / HZ) + 1)
++
++static const struct continuous cinfo[] = {
++ {WM9705_ID2, 0, WM_READS(94), 94},
++ {WM9705_ID2, 1, WM_READS(188), 188},
++ {WM9705_ID2, 2, WM_READS(375), 375},
++ {WM9705_ID2, 3, WM_READS(750), 750},
++ {WM9712_ID2, 0, WM_READS(94), 94},
++ {WM9712_ID2, 1, WM_READS(188), 188},
++ {WM9712_ID2, 2, WM_READS(375), 375},
++ {WM9712_ID2, 3, WM_READS(750), 750},
++ {WM9713_ID2, 0, WM_READS(94), 94},
++ {WM9713_ID2, 1, WM_READS(120), 120},
++ {WM9713_ID2, 2, WM_READS(154), 154},
++ {WM9713_ID2, 3, WM_READS(188), 188},
++};
++
++/* continuous speed index */
++static int sp_idx = 0;
++static u16 last = 0, tries = 0;
++
++/*
++ * Pen sampling frequency (Hz) in continuous mode.
++ */
++static int cont_rate = 200;
++module_param(cont_rate, int, 0);
++MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)");
++
++/*
++ * Pen down detection.
++ *
++ * This driver can either poll or use an interrupt to indicate a pen down
++ * event. If the irq request fails then it will fall back to polling mode.
++ */
++static int pen_int = 1;
++module_param(pen_int, int, 0);
++MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)");
++
++/*
++ * Pressure readback.
++ *
++ * Set to 1 to read back pen down pressure
++ */
++static int pressure = 0;
++module_param(pressure, int, 0);
++MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)");
++
++/*
++ * AC97 touch data slot.
++ *
++ * Touch screen readback data ac97 slot
++ */
++static int ac97_touch_slot = 5;
++module_param(ac97_touch_slot, int, 0);
++MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number");
++
++
++/* flush AC97 slot 5 FIFO on pxa machines */
++#ifdef CONFIG_PXA27x
++void wm97xx_acc_pen_up (struct wm97xx* wm)
++{
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(1);
++
++ while (MISR & (1 << 2))
++ MODR;
++}
++#else
++void wm97xx_acc_pen_up (struct wm97xx* wm)
++{
++ int count = 16;
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(1);
++
++ while (count < 16) {
++ MODR;
++ count--;
++ }
++}
++#endif
++
++int wm97xx_acc_pen_down (struct wm97xx* wm)
++{
++ u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES;
++ int reads = 0;
++
++ /* data is never immediately available after pen down irq */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(1);
++
++ if (tries > 5){
++ tries = 0;
++ return RC_PENUP;
++ }
++
++ x = MODR;
++ if (x == last) {
++ tries++;
++ return RC_AGAIN;
++ }
++ last = x;
++ do {
++ if (reads)
++ x= MODR;
++ y= MODR;
++ if (pressure)
++ p = MODR;
++
++ /* are samples valid */
++ if ((x & 0x7000) != WM97XX_ADCSEL_X ||
++ (y & 0x7000) != WM97XX_ADCSEL_Y ||
++ (p & 0x7000) != WM97XX_ADCSEL_PRES)
++ goto up;
++
++ /* coordinate is good */
++ tries = 0;
++ //printk("x %x y %x p %x\n", x,y,p);
++ input_report_abs (wm->input_dev, ABS_X, x & 0xfff);
++ input_report_abs (wm->input_dev, ABS_Y, y & 0xfff);
++ input_report_abs (wm->input_dev, ABS_PRESSURE, p & 0xfff);
++ input_sync (wm->input_dev);
++ reads++;
++ } while (reads < cinfo[sp_idx].reads);
++up:
++ return RC_PENDOWN | RC_AGAIN;
++}
++
++int wm97xx_acc_startup(struct wm97xx* wm)
++{
++ int idx = 0;
++
++ /* check we have a codec */
++ if (wm->ac97 == NULL)
++ return -ENODEV;
++
++ /* Go you big red fire engine */
++ for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) {
++ if (wm->id != cinfo[idx].id)
++ continue;
++ sp_idx = idx;
++ if (cont_rate <= cinfo[idx].speed)
++ break;
++ }
++ wm->acc_rate = cinfo[sp_idx].code;
++ wm->acc_slot = ac97_touch_slot;
++ printk(KERN_INFO "pxa2xx accelerated touchscreen driver, %d samples (sec)\n",
++ cinfo[sp_idx].speed);
++
++ /* codec specific irq config */
++ if (pen_int) {
++ switch (wm->id) {
++ case WM9705_ID2:
++ wm->pen_irq = IRQ_GPIO(4);
++ set_irq_type(IRQ_GPIO(4), IRQT_BOTHEDGE);
++ break;
++ case WM9712_ID2:
++ case WM9713_ID2:
++ /* enable pen down interrupt */
++ /* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */
++ wm->pen_irq = MAINSTONE_AC97_IRQ;
++ wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
++ WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_STICKY, WM97XX_GPIO_WAKE);
++ wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT,
++ WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_NOTSTICKY, WM97XX_GPIO_NOWAKE);
++ break;
++ default:
++ printk(KERN_WARNING "pen down irq not supported on this device\n");
++ pen_int = 0;
++ break;
++ }
++ }
++
++ return 0;
++}
++
++void wm97xx_acc_shutdown(struct wm97xx* wm)
++{
++ /* codec specific deconfig */
++ if (pen_int) {
++ switch (wm->id & 0xffff) {
++ case WM9705_ID2:
++ wm->pen_irq = 0;
++ break;
++ case WM9712_ID2:
++ case WM9713_ID2:
++ /* disable interrupt */
++ wm->pen_irq = 0;
++ break;
++ }
++ }
++}
++
++static struct wm97xx_mach_ops pxa_mach_ops = {
++ .acc_enabled = 1,
++ .acc_pen_up = wm97xx_acc_pen_up,
++ .acc_pen_down = wm97xx_acc_pen_down,
++ .acc_startup = wm97xx_acc_startup,
++ .acc_shutdown = wm97xx_acc_shutdown,
++};
++
++int pxa_wm97xx_probe(struct device *dev)
++{
++ struct wm97xx *wm = dev->driver_data;
++ return wm97xx_register_mach_ops (wm, &pxa_mach_ops);
++}
++
++int pxa_wm97xx_remove(struct device *dev)
++{
++ struct wm97xx *wm = dev->driver_data;
++ wm97xx_unregister_mach_ops (wm);
++ return 0;
++}
++
++static struct device_driver pxa_wm97xx_driver = {
++ .name = "wm97xx-touchscreen",
++ .bus = &wm97xx_bus_type,
++ .owner = THIS_MODULE,
++ .probe = pxa_wm97xx_probe,
++ .remove = pxa_wm97xx_remove
++};
++
++static int __init pxa_wm97xx_init(void)
++{
++ return driver_register(&pxa_wm97xx_driver);
++}
++
++static void __exit pxa_wm97xx_exit(void)
++{
++ driver_unregister(&pxa_wm97xx_driver);
++}
++
++module_init(pxa_wm97xx_init);
++module_exit(pxa_wm97xx_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
++MODULE_DESCRIPTION("wm97xx continuous touch driver for pxa2xx");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17/drivers/input/touchscreen/wm9705.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/drivers/input/touchscreen/wm9705.c 2006-09-19 20:36:47.969052000 +0200
+@@ -0,0 +1,360 @@
++/*
++ * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec.
++ *
++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ * Russell King <rmk@arm.linux.org.uk>
++ *
++ * 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.
++ *
++ * Revision history
++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com>
++ * Added pre and post sample calls.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/input.h>
++#include <linux/delay.h>
++#include <linux/bitops.h>
++#include <linux/wm97xx.h>
++
++#define TS_NAME "wm97xx"
++#define WM9705_VERSION "0.62"
++#define DEFAULT_PRESSURE 0xb0c0
++
++/*
++ * Debug
++ */
++#if 0
++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...)
++#endif
++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
++
++/*
++ * Module parameters
++ */
++
++/*
++ * 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.
++ */
++static int pil = 0;
++module_param(pil, int, 0);
++MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
++
++/*
++ * Set threshold for pressure measurement.
++ *
++ * Pen down pressure below threshold is ignored.
++ */
++static int pressure = DEFAULT_PRESSURE & 0xfff;
++module_param(pressure, int, 0);
++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
++
++/*
++ * 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.
++ */
++static int delay = 4;
++module_param(delay, int, 0);
++MODULE_PARM_DESC(delay, "Set adc sample delay.");
++
++/*
++ * 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.
++ */
++static int pdd = 8;
++module_param(pdd, int, 0);
++MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold");
++
++/*
++ * Set adc mask function.
++ *
++ * Sources of glitch noise, such as signals driving an LCD display, may feed
++ * through to the touch screen plates and affect measurement accuracy. In
++ * order to minimise this, a signal may be applied to the MASK pin to delay or
++ * synchronise the sampling.
++ *
++ * 0 = No delay or sync
++ * 1 = High on pin stops conversions
++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
++ * 3 = Edge triggered, edge on pin starts conversion after delay param
++ */
++static int mask = 0;
++module_param(mask, int, 0);
++MODULE_PARM_DESC(mask, "Set adc mask function.");
++
++/*
++ * ADC sample delay times in uS
++ */
++static const int delay_table[] = {
++ 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(int d)
++{
++ udelay (3 * AC97_LINK_FRAME + delay_table [d]);
++}
++
++/*
++ * set up the physical settings of the WM9705
++ */
++static void init_wm9705_phy(struct wm97xx* wm)
++{
++ u16 dig1 = 0, dig2 = WM97XX_RPR;
++
++ /*
++ * mute VIDEO and AUX as they share X and Y touchscreen
++ * inputs on the WM9705
++ */
++ wm97xx_reg_write(wm, AC97_AUX, 0x8000);
++ wm97xx_reg_write(wm, AC97_VIDEO, 0x8000);
++
++ /* touchpanel pressure current*/
++ if (pil == 2) {
++ dig2 |= WM9705_PIL;
++ dbg("setting pressure measurement current to 400uA.");
++ } else if (pil)
++ dbg("setting pressure measurement current to 200uA.");
++ if(!pil)
++ pressure = 0;
++
++ /* polling mode sample settling delay */
++ if (delay!=4) {
++ if (delay < 0 || delay > 15) {
++ dbg("supplied delay out of range.");
++ delay = 4;
++ }
++ }
++ dig1 &= 0xff0f;
++ dig1 |= WM97XX_DELAY(delay);
++ dbg("setting adc sample delay to %d u Secs.", delay_table[delay]);
++
++ /* WM9705 pdd */
++ dig2 |= (pdd & 0x000f);
++ dbg("setting pdd to Vmid/%d", 1 - (pdd & 0x000f));
++
++ /* mask */
++ dig2 |= ((mask & 0x3) << 4);
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
++}
++
++static int wm9705_digitiser_ioctl(struct wm97xx* wm, int cmd)
++{
++ switch(cmd) {
++ case WM97XX_DIG_START:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] | WM97XX_PRP_DET_DIG);
++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
++ break;
++ case WM97XX_DIG_STOP:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] & ~WM97XX_PRP_DET_DIG);
++ break;
++ case WM97XX_AUX_PREPARE:
++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
++ break;
++ case WM97XX_DIG_RESTORE:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
++ break;
++ case WM97XX_PHY_INIT:
++ init_wm9705_phy(wm);
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static inline int is_pden (struct wm97xx* wm)
++{
++ return wm->dig[2] & WM9705_PDEN;
++}
++
++/*
++ * Read a sample from the WM9705 adc in polling mode.
++ */
++static int wm9705_poll_sample (struct wm97xx* wm, int adcsel, int *sample)
++{
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ if (adcsel & 0x8000)
++ adcsel = ((adcsel & 0x7fff) + 3) << 12;
++
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(adcsel);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
++
++ /* wait 3 AC97 time slots + delay for conversion */
++ poll_delay (delay);
++
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dbg ("adc sample timeout");
++ return RC_PENUP;
++ }
++
++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(adcsel);
++
++ /* check we have correct sample */
++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
++ dbg ("adc wrong sample, read %x got %x", adcsel,
++ *sample & WM97XX_ADCSEL_MASK);
++ return RC_PENUP;
++ }
++
++ if (!(*sample & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++
++ return RC_VALID;
++}
++
++/*
++ * Sample the WM9705 touchscreen in polling mode
++ */
++static int wm9705_poll_touch(struct wm97xx* wm, struct wm97xx_data *data)
++{
++ int rc;
++
++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID)
++ return rc;
++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID)
++ return rc;
++ if (pil) {
++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID)
++ return rc;
++ } else
++ data->p = DEFAULT_PRESSURE;
++
++ return RC_VALID;
++}
++
++/*
++ * Enable WM9705 continuous mode, i.e. touch data is streamed across an AC97 slot
++ */
++static int wm9705_acc_enable (struct wm97xx* wm, int enable)
++{
++ u16 dig1, dig2;
++ int ret = 0;
++
++ dig1 = wm->dig[1];
++ dig2 = wm->dig[2];
++
++ if (enable) {
++ /* continous mode */
++ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0)
++ return ret;
++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
++ WM97XX_DELAY (delay) |
++ WM97XX_SLT (wm->acc_slot) |
++ WM97XX_RATE (wm->acc_rate);
++ if (pil)
++ dig1 |= WM97XX_ADCSEL_PRES;
++ dig2 |= WM9705_PDEN;
++ } else {
++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
++ dig2 &= ~WM9705_PDEN;
++ if (wm->mach_ops->acc_shutdown)
++ wm->mach_ops->acc_shutdown(wm);
++ }
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
++ return ret;
++}
++
++struct wm97xx_codec_drv wm97xx_codec = {
++ .id = WM9705_ID2,
++ .name = "wm9705",
++ .poll_sample = wm9705_poll_sample,
++ .poll_touch = wm9705_poll_touch,
++ .acc_enable = wm9705_acc_enable,
++ .digitiser_ioctl = wm9705_digitiser_ioctl,
++};
++
++EXPORT_SYMBOL_GPL(wm97xx_codec);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("WM9705 Touch Screen Driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17/drivers/input/touchscreen/wm9712.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/drivers/input/touchscreen/wm9712.c 2006-09-19 20:36:47.969052000 +0200
+@@ -0,0 +1,464 @@
++/*
++ * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs.
++ *
++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ * Russell King <rmk@arm.linux.org.uk>
++ *
++ * 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.
++ *
++ * Revision history
++ * 4th Jul 2005 Initial version.
++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com>
++ * Added pre and post sample calls.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/input.h>
++#include <linux/delay.h>
++#include <linux/bitops.h>
++#include <linux/wm97xx.h>
++
++#define TS_NAME "wm97xx"
++#define WM9712_VERSION "0.61"
++#define DEFAULT_PRESSURE 0xb0c0
++
++/*
++ * Debug
++ */
++#if 0
++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...)
++#endif
++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
++
++/*
++ * Module parameters
++ */
++
++/*
++ * 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 event.
++ */
++static int rpu = 3;
++module_param(rpu, int, 0);
++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
++
++/*
++ * 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.
++ */
++static int pil = 0;
++module_param(pil, int, 0);
++MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
++
++/*
++ * Set threshold for pressure measurement.
++ *
++ * Pen down pressure below threshold is ignored.
++ */
++static int pressure = DEFAULT_PRESSURE & 0xfff;
++module_param(pressure, int, 0);
++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
++
++/*
++ * 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.
++ */
++static int delay = 3;
++module_param(delay, int, 0);
++MODULE_PARM_DESC(delay, "Set adc sample delay.");
++
++/*
++ * Set five_wire = 1 to use a 5 wire touchscreen.
++ *
++ * NOTE: Five wire mode does not allow for readback of pressure.
++ */
++static int five_wire;
++module_param(five_wire, int, 0);
++MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen.");
++
++/*
++ * Set adc mask function.
++ *
++ * Sources of glitch noise, such as signals driving an LCD display, may feed
++ * through to the touch screen plates and affect measurement accuracy. In
++ * order to minimise this, a signal may be applied to the MASK pin to delay or
++ * synchronise the sampling.
++ *
++ * 0 = No delay or sync
++ * 1 = High on pin stops conversions
++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
++ * 3 = Edge triggered, edge on pin starts conversion after delay param
++ */
++static int mask = 0;
++module_param(mask, int, 0);
++MODULE_PARM_DESC(mask, "Set adc mask function.");
++
++/*
++ * Coordinate Polling Enable.
++ *
++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
++ * for every poll.
++ */
++static int coord = 0;
++module_param(coord, int, 0);
++MODULE_PARM_DESC(coord, "Polling coordinate mode");
++
++/*
++ * ADC sample delay times in uS
++ */
++static const int delay_table[] = {
++ 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(int d)
++{
++ udelay (3 * AC97_LINK_FRAME + delay_table [d]);
++}
++
++/*
++ * set up the physical settings of the WM9712
++ */
++static void init_wm9712_phy(struct wm97xx* wm)
++{
++ u16 dig1 = 0;
++ u16 dig2 = WM97XX_RPR | WM9712_RPU(1);
++
++ /* WM9712 rpu */
++ if (rpu) {
++ dig2 &= 0xffc0;
++ dig2 |= WM9712_RPU(rpu);
++ dbg("setting pen detect pull-up to %d Ohms",64000 / rpu);
++ }
++
++ /* touchpanel pressure current*/
++ if (pil == 2) {
++ dig2 |= WM9712_PIL;
++ dbg("setting pressure measurement current to 400uA.");
++ } else if (pil)
++ dbg("setting pressure measurement current to 200uA.");
++ if(!pil)
++ pressure = 0;
++
++ /* WM9712 five wire */
++ if (five_wire) {
++ dig2 |= WM9712_45W;
++ dbg("setting 5-wire touchscreen mode.");
++ }
++
++ /* polling mode sample settling delay */
++ if (delay < 0 || delay > 15) {
++ dbg("supplied delay out of range.");
++ delay = 4;
++ }
++ dig1 &= 0xff0f;
++ dig1 |= WM97XX_DELAY(delay);
++ dbg("setting adc sample delay to %d u Secs.", delay_table[delay]);
++
++ /* mask */
++ dig2 |= ((mask & 0x3) << 6);
++ if (mask) {
++ u16 reg;
++ /* Set GPIO4 as Mask Pin*/
++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4);
++ }
++
++ /* wait - coord mode */
++ if(coord)
++ dig2 |= WM9712_WAIT;
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
++}
++
++static int wm9712_digitiser_ioctl(struct wm97xx* wm, int cmd)
++{
++ u16 dig2 = wm->dig[2];
++
++ switch(cmd) {
++ case WM97XX_DIG_START:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 | WM97XX_PRP_DET_DIG);
++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
++ break;
++ case WM97XX_DIG_STOP:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 & ~WM97XX_PRP_DET_DIG);
++ break;
++ case WM97XX_AUX_PREPARE:
++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
++ break;
++ case WM97XX_DIG_RESTORE:
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
++ break;
++ case WM97XX_PHY_INIT:
++ init_wm9712_phy(wm);
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static inline int is_pden (struct wm97xx* wm)
++{
++ return wm->dig[2] & WM9712_PDEN;
++}
++
++/*
++ * Read a sample from the WM9712 adc in polling mode.
++ */
++static int wm9712_poll_sample (struct wm97xx* wm, int adcsel, int *sample)
++{
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ if (adcsel & 0x8000)
++ adcsel = ((adcsel & 0x7fff) + 3) << 12;
++
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(adcsel);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
++
++ /* wait 3 AC97 time slots + delay for conversion */
++ poll_delay (delay);
++
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dbg ("adc sample timeout");
++ return RC_PENUP;
++ }
++
++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(adcsel);
++
++ /* check we have correct sample */
++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
++ dbg ("adc wrong sample, read %x got %x", adcsel,
++ *sample & WM97XX_ADCSEL_MASK);
++ return RC_PENUP;
++ }
++
++ if (!(*sample & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++
++ return RC_VALID;
++}
++
++/*
++ * Read a coord from the WM9712 adc in polling mode.
++ */
++static int wm9712_poll_coord (struct wm97xx* wm, struct wm97xx_data *data)
++{
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
++ WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay));
++
++ /* wait 3 AC97 time slots + delay for conversion and read x */
++ poll_delay(delay);
++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dbg ("adc sample timeout");
++ return RC_PENUP;
++ }
++
++ /* read back y data */
++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (pil)
++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ else
++ data->p = DEFAULT_PRESSURE;
++
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
++
++ /* check we have correct sample */
++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
++ goto err;
++ if(pil && !(data->p & WM97XX_ADCSEL_PRES))
++ goto err;
++
++ if (!(data->x & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++ return RC_VALID;
++err:
++ return RC_PENUP;
++}
++
++/*
++ * Sample the WM9712 touchscreen in polling mode
++ */
++static int wm9712_poll_touch(struct wm97xx* wm, struct wm97xx_data *data)
++{
++ int rc;
++
++ if(coord) {
++ if((rc = wm9712_poll_coord(wm, data)) != RC_VALID)
++ return rc;
++ } else {
++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID)
++ return rc;
++
++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID)
++ return rc;
++
++ if (pil && !five_wire) {
++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID)
++ return rc;
++ } else
++ data->p = DEFAULT_PRESSURE;
++ }
++ return RC_VALID;
++}
++
++/*
++ * Enable WM9712 continuous mode, i.e. touch data is streamed across an AC97 slot
++ */
++static int wm9712_acc_enable (struct wm97xx* wm, int enable)
++{
++ u16 dig1, dig2;
++ int ret = 0;
++
++ dig1 = wm->dig[1];
++ dig2 = wm->dig[2];
++
++ if (enable) {
++ /* continous mode */
++ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0)
++ return ret;
++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
++ WM97XX_DELAY (delay) |
++ WM97XX_SLT (wm->acc_slot) |
++ WM97XX_RATE (wm->acc_rate);
++ if (pil)
++ dig1 |= WM97XX_ADCSEL_PRES;
++ dig2 |= WM9712_PDEN;
++ } else {
++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
++ dig2 &= ~WM9712_PDEN;
++ if (wm->mach_ops->acc_shutdown)
++ wm->mach_ops->acc_shutdown(wm);
++ }
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
++ return 0;
++}
++
++struct wm97xx_codec_drv wm97xx_codec = {
++ .id = WM9712_ID2,
++ .name = "wm9712",
++ .poll_sample = wm9712_poll_sample,
++ .poll_touch = wm9712_poll_touch,
++ .acc_enable = wm9712_acc_enable,
++ .digitiser_ioctl = wm9712_digitiser_ioctl,
++};
++
++EXPORT_SYMBOL_GPL(wm97xx_codec);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("WM9712 Touch Screen Driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17/drivers/input/touchscreen/wm9713.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/drivers/input/touchscreen/wm9713.c 2006-09-19 20:36:47.969052000 +0200
+@@ -0,0 +1,461 @@
++/*
++ * wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec.
++ *
++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ * Russell King <rmk@arm.linux.org.uk>
++ *
++ * 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.
++ *
++ * Revision history
++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com>
++ * Added pre and post sample calls.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/input.h>
++#include <linux/delay.h>
++#include <linux/bitops.h>
++#include <linux/wm97xx.h>
++
++#define TS_NAME "wm97xx"
++#define WM9713_VERSION "0.53"
++#define DEFAULT_PRESSURE 0xb0c0
++
++/*
++ * Debug
++ */
++#if 0
++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...)
++#endif
++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
++
++/*
++ * Module parameters
++ */
++
++/*
++ * 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 event.
++ */
++static int rpu = 1;
++module_param(rpu, int, 0);
++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
++
++/*
++ * 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.
++ */
++static int pil = 0;
++module_param(pil, int, 0);
++MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
++
++/*
++ * Set threshold for pressure measurement.
++ *
++ * Pen down pressure below threshold is ignored.
++ */
++static int pressure = DEFAULT_PRESSURE & 0xfff;
++module_param(pressure, int, 0);
++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
++
++/*
++ * 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.
++ */
++static int delay = 4;
++module_param(delay, int, 0);
++MODULE_PARM_DESC(delay, "Set adc sample delay.");
++
++/*
++ * Set adc mask function.
++ *
++ * Sources of glitch noise, such as signals driving an LCD display, may feed
++ * through to the touch screen plates and affect measurement accuracy. In
++ * order to minimise this, a signal may be applied to the MASK pin to delay or
++ * synchronise the sampling.
++ *
++ * 0 = No delay or sync
++ * 1 = High on pin stops conversions
++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
++ * 3 = Edge triggered, edge on pin starts conversion after delay param
++ */
++static int mask = 0;
++module_param(mask, int, 0);
++MODULE_PARM_DESC(mask, "Set adc mask function.");
++
++/*
++ * Coordinate Polling Enable.
++ *
++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
++ * for every poll.
++ */
++static int coord = 1;
++module_param(coord, int, 0);
++MODULE_PARM_DESC(coord, "Polling coordinate mode");
++
++/*
++ * ADC sample delay times in uS
++ */
++static const int delay_table[] = {
++ 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(int d)
++{
++ udelay (3 * AC97_LINK_FRAME + delay_table [d]);
++}
++
++/*
++ * set up the physical settings of the WM9713
++ */
++static void init_wm9713_phy(struct wm97xx* wm)
++{
++ u16 dig1 = 0, dig2, dig3;
++
++ /* 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.");
++ if(!pil)
++ pressure = 0;
++
++ /* sample settling delay */
++ if (delay < 0 || delay > 15) {
++ info ("supplied delay out of range.");
++ delay = 4;
++ info("setting adc sample delay to %d u Secs.", delay_table[delay]);
++ }
++ dig2 &= 0xff0f;
++ dig2 |= WM97XX_DELAY(delay);
++
++ /* mask */
++ dig3 |= ((mask & 0x3) << 4);
++ if(coord)
++ dig3 |= WM9713_WAIT;
++
++ wm->misc = wm97xx_reg_read(wm, 0x5a);
++
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0);
++}
++
++static int wm9713_digitiser_ioctl(struct wm97xx* wm, int cmd)
++{
++ u16 val = 0;
++
++ switch(cmd){
++ case WM97XX_DIG_START:
++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] | WM97XX_PRP_DET_DIG);
++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
++ break;
++ case WM97XX_DIG_STOP:
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] & ~WM97XX_PRP_DET_DIG);
++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000);
++ break;
++ case WM97XX_AUX_PREPARE:
++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG);
++ break;
++ case WM97XX_DIG_RESTORE:
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]);
++ break;
++ case WM97XX_PHY_INIT:
++ init_wm9713_phy(wm);
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static inline int is_pden (struct wm97xx* wm)
++{
++ return wm->dig[2] & WM9713_PDEN;
++}
++
++/*
++ * Read a sample from the WM9713 adc in polling mode.
++ */
++static int wm9713_poll_sample (struct wm97xx* wm, int adcsel, int *sample)
++{
++ u16 dig1;
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ if (adcsel & 0x8000)
++ adcsel = 1 << ((adcsel & 0x7fff) + 3);
++
++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
++ dig1 &= ~WM9713_ADCSEL_MASK;
++
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(adcsel);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | adcsel |WM9713_POLL);
++
++ /* wait 3 AC97 time slots + delay for conversion */
++ poll_delay(delay);
++
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dbg ("adc sample timeout");
++ return RC_PENUP;
++ }
++
++ *sample =wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(adcsel);
++
++ /* check we have correct sample */
++ if ((*sample & WM97XX_ADCSRC_MASK) != ffs(adcsel >> 1) << 12) {
++ dbg ("adc wrong sample, read %x got %x", adcsel,
++ *sample & WM97XX_ADCSRC_MASK);
++ return RC_PENUP;
++ }
++
++ if (!(*sample & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++
++ return RC_VALID;
++}
++
++/*
++ * Read a coordinate from the WM9713 adc in polling mode.
++ */
++static int wm9713_poll_coord (struct wm97xx* wm, struct wm97xx_data *data)
++{
++ u16 dig1;
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
++ dig1 &= ~WM9713_ADCSEL_MASK;
++ if(pil)
++ dig1 |= WM97XX_ADCSEL_PRES;
++
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | WM9713_POLL | WM9713_COO);
++
++ /* wait 3 AC97 time slots + delay for conversion */
++ poll_delay(delay);
++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dbg ("adc sample timeout");
++ return RC_PENUP;
++ }
++
++ /* read back data */
++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (pil)
++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ else
++ data->p = DEFAULT_PRESSURE;
++
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
++
++ /* check we have correct sample */
++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
++ goto err;
++ if(pil && !(data->p & WM97XX_ADCSEL_PRES))
++ goto err;
++
++ if (!(data->x & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++ return RC_VALID;
++err:
++ return RC_PENUP;
++}
++
++/*
++ * Sample the WM9713 touchscreen in polling mode
++ */
++static int wm9713_poll_touch(struct wm97xx* wm, struct wm97xx_data *data)
++{
++ int rc;
++
++ if(coord) {
++ if((rc = wm9713_poll_coord(wm, data)) != RC_VALID)
++ return rc;
++ } else {
++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x)) != RC_VALID)
++ return rc;
++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y)) != RC_VALID)
++ return rc;
++ if (pil) {
++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES, &data->p)) != RC_VALID)
++ return rc;
++ } else
++ data->p = DEFAULT_PRESSURE;
++ }
++ return RC_VALID;
++}
++
++/*
++ * Enable WM9713 continuous mode, i.e. touch data is streamed across an AC97 slot
++ */
++static int wm9713_acc_enable (struct wm97xx* wm, int enable)
++{
++ u16 dig1, dig2, dig3;
++ int ret = 0;
++
++ dig1 = wm->dig[0];
++ dig2 = wm->dig[1];
++ dig3 = wm->dig[2];
++
++ if (enable) {
++ /* continous mode */
++ if (wm->mach_ops->acc_startup &&
++ (ret = wm->mach_ops->acc_startup(wm)) < 0)
++ return ret;
++
++ dig1 &= ~WM9713_ADCSEL_MASK;
++ dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X | WM9713_ADCSEL_Y;
++ if (pil)
++ dig1 |= WM9713_ADCSEL_PRES;
++ dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK | WM97XX_CM_RATE_MASK);
++ dig2 |= WM97XX_SLEN | WM97XX_DELAY (delay) |
++ WM97XX_SLT (wm->acc_slot) | WM97XX_RATE (wm->acc_rate);
++ dig3 |= WM9713_PDEN;
++ } else {
++ dig1 &= ~(WM9713_CTC | WM9713_COO);
++ dig2 &= ~WM97XX_SLEN;
++ dig3 &= ~WM9713_PDEN;
++ if (wm->mach_ops->acc_shutdown)
++ wm->mach_ops->acc_shutdown(wm);
++ }
++
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
++ return ret;
++}
++
++struct wm97xx_codec_drv wm97xx_codec = {
++ .id = WM9713_ID2,
++ .name = "wm9713",
++ .poll_sample = wm9713_poll_sample,
++ .poll_touch = wm9713_poll_touch,
++ .acc_enable = wm9713_acc_enable,
++ .digitiser_ioctl = wm9713_digitiser_ioctl,
++};
++
++EXPORT_SYMBOL_GPL(wm97xx_codec);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("WM9713 Touch Screen Driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17/drivers/input/touchscreen/wm97xx-core.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/drivers/input/touchscreen/wm97xx-core.c 2006-09-19 20:36:47.969052000 +0200
+@@ -0,0 +1,912 @@
++/*
++ * wm97xx-core.c -- Touch screen driver core for Wolfson WM9705, WM9712
++ * and WM9713 AC97 Codecs.
++ *
++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ * Russell King <rmk@arm.linux.org.uk>
++ *
++ * 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.
++ *
++ * Notes:
++ *
++ * Features:
++ * - supports WM9705, WM9712, WM9713
++ * - polling mode
++ * - continuous mode (arch-dependent)
++ * - adjustable rpu/dpp settings
++ * - adjustable pressure current
++ * - adjustable sample settle delay
++ * - 4 and 5 wire touchscreens (5 wire is WM9712 only)
++ * - pen down detection
++ * - battery monitor
++ * - sample AUX adc's
++ * - power management
++ * - codec GPIO
++ * - codec event notification
++ * Todo
++ * - Support for async sampling control for noisy LCD'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
++ * codec_mutexing 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.
++ * 16th Nov 2003 Ian Molton <spyro@f2s.com>
++ * Modified so that it suits the new 2.6 driver model.
++ * 25th Jan 2004 Andrew Zabolotny <zap@homelink.ru>
++ * Implemented IRQ-driven pen down detection, implemented
++ * the private API meant to be exposed to platform-specific
++ * drivers, reorganized the driver so that it supports
++ * an arbitrary number of devices.
++ * 1st Feb 2004 Moved continuous mode handling to a separate
++ * architecture-dependent file. For now only PXA
++ * built-in AC97 controller is supported (pxa-ac97-wm97xx.c).
++ * 11th Feb 2004 Reduced CPU usage by keeping a cached copy of both
++ * digitizer registers instead of reading them every time.
++ * A reorganization of the whole code for better
++ * error handling.
++ * 17th Apr 2004 Added BMON support.
++ * 17th Nov 2004 Added codec GPIO, codec event handling (real and virtual
++ * GPIOs) and 2.6 power management.
++ * 29th Nov 2004 Added WM9713 support.
++ * 4th Jul 2005 Moved codec specific code out to seperate files.
++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com>
++ * Added bus interface.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/string.h>
++#include <linux/proc_fs.h>
++#include <linux/pm.h>
++#include <linux/interrupt.h>
++#include <linux/bitops.h>
++#include <linux/workqueue.h>
++#include <linux/device.h>
++#include <linux/wm97xx.h>
++#include <asm/uaccess.h>
++#include <asm/io.h>
++
++#define TS_NAME "wm97xx"
++#define WM_CORE_VERSION "0.63"
++#define DEFAULT_PRESSURE 0xb0c0
++
++/*
++ * WM97xx - enable/disable AUX ADC sysfs
++ */
++static int aux_sys = 1;
++module_param(aux_sys, int, 0);
++MODULE_PARM_DESC(aux_sys, "enable AUX ADC sysfs entries");
++
++/*
++ * WM97xx - enable/disable codec status sysfs
++ */
++static int status_sys = 1;
++module_param(status_sys, int, 0);
++MODULE_PARM_DESC(status_sys, "enable codec status sysfs entries");
++
++/*
++ * Touchscreen absolute values
++ *
++ * These parameters are used to help the input layer discard out of
++ * range readings and reduce jitter etc.
++ *
++ * o min, max:- indicate the min and max values your touch screen returns
++ * o fuzz:- use a higher number to reduce jitter
++ *
++ * The default values correspond to Mainstone II in QVGA mode
++ *
++ * Please read
++ * Documentation/input/input-programming.txt for more details.
++ */
++
++static int abs_x[3] = {350,3900,5};
++module_param_array(abs_x, int, NULL, 0);
++MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz");
++
++static int abs_y[3] = {320,3750,40};
++module_param_array(abs_y, int, NULL, 0);
++MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz");
++
++static int abs_p[3] = {0,150,4};
++module_param_array(abs_p, int, NULL, 0);
++MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz");
++
++/*
++ * Debug
++ */
++#if 0
++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...)
++#endif
++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
++
++/* codec AC97 IO access */
++int wm97xx_reg_read(struct wm97xx *wm, u16 reg)
++{
++ if (wm->ac97)
++ return wm->ac97->bus->ops->read(wm->ac97, reg);
++ else
++ return -1;
++}
++
++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val)
++{
++ /* cache digitiser registers */
++ if(reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3)
++ wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val;
++
++ /* cache gpio regs */
++ if(reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE)
++ wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val;
++
++ /* wm9713 irq reg */
++ if(reg == 0x5a)
++ wm->misc = val;
++
++ if (wm->ac97)
++ wm->ac97->bus->ops->write(wm->ac97, reg, val);
++}
++
++
++/**
++ * wm97xx_read_aux_adc - Read the aux adc.
++ * @wm: wm97xx device.
++ * @adcsel: codec ADC to be read
++ *
++ * Reads the selected AUX ADC.
++ */
++
++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
++{
++ int power_adc = 0, auxval;
++ u16 power = 0;
++
++ /* get codec */
++ mutex_lock(&wm->codec_mutex);
++
++ /* When the touchscreen is not in use, we may have to power up the AUX ADC
++ * before we can use sample the AUX inputs->
++ */
++ if (wm->id == WM9713_ID2 &&
++ (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) {
++ power_adc = 1;
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff);
++ }
++
++ /* Prepare the codec for AUX reading */
++ wm->codec->digitiser_ioctl(wm, WM97XX_AUX_PREPARE);
++
++ /* Turn polling mode on to read AUX ADC */
++ wm->pen_probably_down = 1;
++ wm->codec->poll_sample(wm, adcsel, &auxval);
++
++ if (power_adc)
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000);
++
++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_RESTORE);
++
++ wm->pen_probably_down = 0;
++
++ mutex_unlock(&wm->codec_mutex);
++ return auxval & 0xfff;
++}
++
++#define WM97XX_AUX_ATTR(name,input) \
++static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \
++{ \
++ struct wm97xx *wm = (struct wm97xx*)dev->driver_data; \
++ return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, input)); \
++} \
++static DEVICE_ATTR(name, 0444, name##_show, NULL)
++
++WM97XX_AUX_ATTR(aux1, WM97XX_AUX_ID1);
++WM97XX_AUX_ATTR(aux2, WM97XX_AUX_ID2);
++WM97XX_AUX_ATTR(aux3, WM97XX_AUX_ID3);
++WM97XX_AUX_ATTR(aux4, WM97XX_AUX_ID4);
++
++#define WM97XX_STATUS_ATTR(name) \
++static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \
++{ \
++ struct wm97xx *wm = (struct wm97xx*)dev->driver_data; \
++ return sprintf(buf, "%d\n", wm97xx_reg_read(wm, AC97_GPIO_STATUS)); \
++} \
++static DEVICE_ATTR(name, 0444, name##_show, NULL)
++
++WM97XX_STATUS_ATTR(gpio);
++
++static int wm97xx_sys_add(struct device *dev)
++{
++ if (aux_sys) {
++ device_create_file(dev, &dev_attr_aux1);
++ device_create_file(dev, &dev_attr_aux2);
++ device_create_file(dev, &dev_attr_aux3);
++ device_create_file(dev, &dev_attr_aux4);
++ }
++ if (status_sys)
++ device_create_file(dev, &dev_attr_gpio);
++ return 0;
++}
++
++static void wm97xx_sys_remove(struct device *dev)
++{
++ if (status_sys)
++ device_remove_file(dev, &dev_attr_gpio);
++ if (aux_sys) {
++ device_remove_file(dev, &dev_attr_aux1);
++ device_remove_file(dev, &dev_attr_aux2);
++ device_remove_file(dev, &dev_attr_aux3);
++ device_remove_file(dev, &dev_attr_aux4);
++ }
++}
++
++/**
++ * wm97xx_get_gpio - Get the status of a codec GPIO.
++ * @wm: wm97xx device.
++ * @gpio: gpio
++ *
++ * Get the status of a codec GPIO pin
++ */
++
++wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio)
++{
++ u16 status;
++ wm97xx_gpio_status_t ret;
++
++ mutex_lock(&wm->codec_mutex);
++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
++
++ if (status & gpio)
++ ret = WM97XX_GPIO_HIGH;
++ else
++ ret = WM97XX_GPIO_LOW;
++
++ mutex_unlock(&wm->codec_mutex);
++ return ret;
++}
++
++/**
++ * wm97xx_set_gpio - Set the status of a codec GPIO.
++ * @wm: wm97xx device.
++ * @gpio: gpio
++ *
++ *
++ * Set the status of a codec GPIO pin
++ */
++
++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
++ wm97xx_gpio_status_t status)
++{
++ u16 reg;
++
++ mutex_lock(&wm->codec_mutex);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
++
++ if (status & WM97XX_GPIO_HIGH)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ if (wm->id == WM9712_ID2)
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1);
++ else
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg);
++ mutex_unlock(&wm->codec_mutex);
++}
++
++/*
++ * Codec GPIO pin configuration, this set's pin direction, polarity,
++ * stickyness and wake up.
++ */
++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir,
++ wm97xx_gpio_pol_t pol, wm97xx_gpio_sticky_t sticky,
++ wm97xx_gpio_wake_t wake)
++{
++ u16 reg;
++
++ mutex_lock(&wm->codec_mutex);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
++
++ if (pol == WM97XX_GPIO_POL_HIGH)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
++
++ if (sticky == WM97XX_GPIO_STICKY)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
++
++ if (wake == WM97XX_GPIO_WAKE)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
++
++ if (dir == WM97XX_GPIO_IN)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg);
++ mutex_unlock(&wm->codec_mutex);
++}
++
++/*
++ * Handle a pen down interrupt.
++ */
++static void wm97xx_pen_irq_worker(void *ptr)
++{
++ struct wm97xx *wm = (struct wm97xx *) ptr;
++
++ /* do we need to enable the touch panel reader */
++ if (wm->id == WM9705_ID2) {
++ if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN)
++ wm->pen_is_down = 1;
++ else
++ wm->pen_is_down = 0;
++ wake_up_interruptible(&wm->pen_irq_wait);
++ } else {
++ u16 status, pol;
++ mutex_lock(&wm->codec_mutex);
++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
++ pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
++
++ if (WM97XX_GPIO_13 & pol & status) {
++ wm->pen_is_down = 1;
++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol & ~WM97XX_GPIO_13);
++ } else {
++ wm->pen_is_down = 0;
++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol | WM97XX_GPIO_13);
++ }
++
++ if (wm->id == WM9712_ID2)
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status & ~WM97XX_GPIO_13) << 1);
++ else
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, status & ~WM97XX_GPIO_13);
++ mutex_unlock(&wm->codec_mutex);
++ wake_up_interruptible(&wm->pen_irq_wait);
++ }
++
++ if (!wm->pen_is_down && wm->mach_ops && wm->mach_ops->acc_enabled)
++ wm->mach_ops->acc_pen_up(wm);
++ enable_irq(wm->pen_irq);
++}
++
++/*
++ * Codec PENDOWN irq handler
++ *
++ * We have to disable the codec interrupt in the handler because it can
++ * take upto 1ms to clear the interrupt source. The interrupt is then enabled
++ * again in the slow handler when the source has been cleared.
++ */
++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id,
++ struct pt_regs *regs)
++{
++ struct wm97xx *wm = (struct wm97xx *) dev_id;
++ disable_irq(wm->pen_irq);
++ queue_work(wm->pen_irq_workq, &wm->pen_event_work);
++ return IRQ_HANDLED;
++}
++
++/*
++ * initialise pen IRQ handler and workqueue
++ */
++static int wm97xx_init_pen_irq(struct wm97xx *wm)
++{
++ u16 reg;
++
++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker, wm);
++ if ((wm->pen_irq_workq =
++ create_singlethread_workqueue("kwm97pen")) == NULL) {
++ err("could not create pen irq work queue");
++ wm->pen_irq = 0;
++ return -EINVAL;
++ }
++
++ if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) {
++ err("could not register codec pen down interrupt, will poll for pen down");
++ destroy_workqueue(wm->pen_irq_workq);
++ wm->pen_irq = 0;
++ return -EINVAL;
++ }
++
++ /* enable PEN down on wm9712/13 */
++ if (wm->id != WM9705_ID2) {
++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg & 0xfffb);
++ reg = wm97xx_reg_read(wm, 0x5a);
++ wm97xx_reg_write(wm, 0x5a, reg & ~0x0001);
++ }
++
++ return 0;
++}
++
++/* Private struct for communication between struct wm97xx_tshread
++ * and wm97xx_read_samples */
++struct ts_state {
++ int sleep_time;
++ int min_sleep_time;
++};
++
++static int wm97xx_read_samples(struct wm97xx *wm, struct ts_state *state)
++{
++ struct wm97xx_data data;
++ int rc;
++
++ mutex_lock(&wm->codec_mutex);
++
++ if (wm->mach_ops && wm->mach_ops->acc_enabled)
++ rc = wm->mach_ops->acc_pen_down(wm);
++ else
++ rc = wm->codec->poll_touch(wm, &data);
++
++ if (rc & RC_PENUP) {
++ if (wm->pen_is_down) {
++ wm->pen_is_down = 0;
++ dbg("pen up");
++ input_report_abs(wm->input_dev, ABS_PRESSURE, 0);
++ input_sync(wm->input_dev);
++ } else if (!(rc & RC_AGAIN)) {
++ /* We need high frequency updates only while pen is down,
++ * the user never will be able to touch screen faster than
++ * a few times per second... On the other hand, when the
++ * user is actively working with the touchscreen we don't
++ * want to lose the quick response. So we will slowly
++ * increase sleep time after the pen is up and quicky
++ * restore it to ~one task switch when pen is down again.
++ */
++ if (state->sleep_time < HZ / 10)
++ state->sleep_time++;
++ }
++
++ } else if (rc & RC_VALID) {
++ dbg("pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n",
++ data.x >> 12, data.x & 0xfff, data.y >> 12,
++ data.y & 0xfff, data.p >> 12, data.p & 0xfff);
++ input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff);
++ input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff);
++ input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff);
++ input_sync(wm->input_dev);
++ wm->pen_is_down = 1;
++ state->sleep_time = state->min_sleep_time;
++ } else if (rc & RC_PENDOWN) {
++ dbg("pen down");
++ wm->pen_is_down = 1;
++ state->sleep_time = state->min_sleep_time;
++ }
++
++ mutex_unlock(&wm->codec_mutex);
++ return rc;
++}
++
++/*
++* The touchscreen sample reader thread.
++*/
++static int wm97xx_ts_read(void *data)
++{
++ int rc;
++ struct ts_state state;
++ struct wm97xx *wm = (struct wm97xx *) data;
++
++ /* set up thread context */
++ wm->ts_task = current;
++ daemonize("kwm97xxts");
++
++ if (wm->codec == NULL) {
++ wm->ts_task = NULL;
++ printk(KERN_ERR "codec is NULL, bailing\n");
++ }
++
++ complete(&wm->ts_init);
++ wm->pen_is_down = 0;
++ state.min_sleep_time = HZ >= 100 ? HZ / 100 : 1;
++ if (state.min_sleep_time < 1)
++ state.min_sleep_time = 1;
++ state.sleep_time = state.min_sleep_time;
++
++ /* touch reader loop */
++ while (wm->ts_task) {
++ do {
++ try_to_freeze();
++ rc = wm97xx_read_samples(wm, &state);
++ } while (rc & RC_AGAIN);
++ if (!wm->pen_is_down && wm->pen_irq) {
++ /* Nice, we don't have to poll for pen down event */
++ wait_event_interruptible(wm->pen_irq_wait, wm->pen_is_down);
++ } else {
++ set_task_state(current, TASK_INTERRUPTIBLE);
++ schedule_timeout(state.sleep_time);
++ }
++ }
++ complete_and_exit(&wm->ts_exit, 0);
++}
++
++/**
++ * wm97xx_ts_input_open - Open the touch screen input device.
++ * @idev: Input device to be opened.
++ *
++ * Called by the input sub system to open a wm97xx touchscreen device.
++ * Starts the touchscreen thread and touch digitiser.
++ */
++static int wm97xx_ts_input_open(struct input_dev *idev)
++{
++ int ret = 0;
++ struct wm97xx *wm = (struct wm97xx *) idev->private;
++
++ mutex_lock(&wm->codec_mutex);
++ /* first time opened ? */
++ if (wm->ts_use_count++ == 0) {
++ /* start touchscreen thread */
++ init_completion(&wm->ts_init);
++ init_completion(&wm->ts_exit);
++ ret = kernel_thread(wm97xx_ts_read, wm, CLONE_KERNEL);
++
++ if (ret >= 0) {
++ wait_for_completion(&wm->ts_init);
++ if (wm->ts_task == NULL)
++ ret = -EINVAL;
++ } else {
++ mutex_unlock(&wm->codec_mutex);
++ return ret;
++ }
++
++ /* start digitiser */
++ if (wm->mach_ops && wm->mach_ops->acc_enabled)
++ wm->codec->acc_enable(wm, 1);
++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_START);
++
++ /* init pen down/up irq handling */
++ if (wm->pen_irq) {
++ wm97xx_init_pen_irq(wm);
++
++ if (wm->pen_irq == 0) {
++ /* we failed to get an irq for pen down events,
++ * so we resort to polling. kickstart the reader */
++ wm->pen_is_down = 1;
++ wake_up_interruptible(&wm->pen_irq_wait);
++ }
++ }
++ }
++
++ mutex_unlock(&wm->codec_mutex);
++ return 0;
++}
++
++/**
++ * wm97xx_ts_input_close - Close the touch screen input device.
++ * @idev: Input device to be closed.
++ *
++ * Called by the input sub system to close a wm97xx touchscreen device.
++ * Kills the touchscreen thread and stops the touch digitiser.
++ */
++
++static void wm97xx_ts_input_close(struct input_dev *idev)
++{
++ struct wm97xx *wm = (struct wm97xx *) idev->private;
++
++ mutex_lock(&wm->codec_mutex);
++ if (--wm->ts_use_count == 0) {
++ /* destroy workqueues and free irqs */
++ if (wm->pen_irq) {
++ free_irq(wm->pen_irq, wm);
++ destroy_workqueue(wm->pen_irq_workq);
++ }
++
++ /* kill thread */
++ if (wm->ts_task) {
++ wm->ts_task = NULL;
++ wm->pen_is_down = 1;
++ wake_up_interruptible(&wm->pen_irq_wait);
++ wait_for_completion(&wm->ts_exit);
++ wm->pen_is_down = 0;
++ }
++
++ /* stop digitiser */
++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_STOP);
++ if (wm->mach_ops && wm->mach_ops->acc_enabled)
++ wm->codec->acc_enable(wm, 0);
++ }
++ mutex_unlock(&wm->codec_mutex);
++}
++
++static int wm97xx_bus_match(struct device *dev, struct device_driver *drv)
++{
++ return !(strcmp(dev->bus_id,drv->name));
++}
++
++/*
++ * The AC97 audio driver will do all the Codec suspend and resume
++ * tasks. This is just for anything machine specific or extra.
++ */
++static int wm97xx_bus_suspend(struct device *dev, pm_message_t state)
++{
++ int ret = 0;
++
++ if (dev->driver && dev->driver->suspend)
++ ret = dev->driver->suspend(dev, state);
++
++ return ret;
++}
++
++static int wm97xx_bus_resume(struct device *dev)
++{
++ int ret = 0;
++
++ if (dev->driver && dev->driver->resume)
++ ret = dev->driver->resume(dev);
++
++ return ret;
++}
++
++struct bus_type wm97xx_bus_type = {
++ .name = "wm97xx",
++ .match = wm97xx_bus_match,
++ .suspend = wm97xx_bus_suspend,
++ .resume = wm97xx_bus_resume,
++};
++
++static void wm97xx_release(struct device *dev)
++{
++ kfree(dev);
++}
++
++static int wm97xx_probe(struct device *dev)
++{
++ struct wm97xx* wm;
++ int ret = 0, id = 0;
++
++ if (!(wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL)))
++ return -ENOMEM;
++ mutex_init(&wm->codec_mutex);
++
++ init_waitqueue_head(&wm->pen_irq_wait);
++ wm->dev = dev;
++ dev->driver_data = wm;
++ wm->ac97 = to_ac97_t(dev);
++
++ /* check that we have a supported codec */
++ if ((id = wm97xx_reg_read(wm, AC97_VENDOR_ID1)) != WM97XX_ID1) {
++ err("could not find a wm97xx, found a %x instead\n", id);
++ kfree(wm);
++ return -ENODEV;
++ }
++
++ wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2);
++ if(wm->id != wm97xx_codec.id) {
++ err("could not find a the selected codec, please build for wm97%2x", wm->id & 0xff);
++ kfree(wm);
++ return -ENODEV;
++ }
++
++ if((wm->input_dev = input_allocate_device()) == NULL) {
++ kfree(wm);
++ return -ENOMEM;
++ }
++
++ /* set up touch configuration */
++ info("detected a wm97%2x codec", wm->id & 0xff);
++ wm->input_dev->name = "wm97xx touchscreen";
++ wm->input_dev->open = wm97xx_ts_input_open;
++ wm->input_dev->close = wm97xx_ts_input_close;
++ set_bit(EV_ABS, wm->input_dev->evbit);
++ set_bit(ABS_X, wm->input_dev->absbit);
++ set_bit(ABS_Y, wm->input_dev->absbit);
++ set_bit(ABS_PRESSURE, wm->input_dev->absbit);
++ wm->input_dev->absmax[ABS_X] = abs_x[1];
++ wm->input_dev->absmax[ABS_Y] = abs_y[1];
++ wm->input_dev->absmax[ABS_PRESSURE] = abs_p[1];
++ wm->input_dev->absmin[ABS_X] = abs_x[0];
++ wm->input_dev->absmin[ABS_Y] = abs_y[0];
++ wm->input_dev->absmin[ABS_PRESSURE] = abs_p[0];
++ wm->input_dev->absfuzz[ABS_X] = abs_x[2];
++ wm->input_dev->absfuzz[ABS_Y] = abs_y[2];
++ wm->input_dev->absfuzz[ABS_PRESSURE] = abs_p[2];
++ wm->input_dev->private = wm;
++ wm->codec = &wm97xx_codec;
++ if((ret = input_register_device(wm->input_dev)) < 0) {
++ kfree(wm);
++ return -ENOMEM;
++ }
++
++ if(aux_sys)
++ wm97xx_sys_add(dev);
++
++ /* set up physical characteristics */
++ wm->codec->digitiser_ioctl(wm, WM97XX_PHY_INIT);
++
++ /* load gpio cache */
++ wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG);
++ wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
++ wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
++ wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
++ wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
++ wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE);
++
++ /* register our battery device */
++ if (!(wm->battery_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) {
++ ret = -ENOMEM;
++ goto batt_err;
++ }
++ wm->battery_dev->bus = &wm97xx_bus_type;
++ strcpy(wm->battery_dev->bus_id,"wm97xx-battery");
++ wm->battery_dev->driver_data = wm;
++ wm->battery_dev->parent = dev;
++ wm->battery_dev->release = wm97xx_release;
++ if((ret = device_register(wm->battery_dev)) < 0)
++ goto batt_reg_err;
++
++ /* register our extended touch device (for machine specific extensions) */
++ if (!(wm->touch_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) {
++ ret = -ENOMEM;
++ goto touch_err;
++ }
++ wm->touch_dev->bus = &wm97xx_bus_type;
++ strcpy(wm->touch_dev->bus_id,"wm97xx-touchscreen");
++ wm->touch_dev->driver_data = wm;
++ wm->touch_dev->parent = dev;
++ wm->touch_dev->release = wm97xx_release;
++ if((ret = device_register(wm->touch_dev)) < 0)
++ goto touch_reg_err;
++
++ return ret;
++
++touch_reg_err:
++ kfree(wm->touch_dev);
++touch_err:
++ device_unregister(wm->battery_dev);
++batt_reg_err:
++ kfree(wm->battery_dev);
++batt_err:
++ input_unregister_device(wm->input_dev);
++ kfree(wm);
++ return ret;
++}
++
++static int wm97xx_remove(struct device *dev)
++{
++ struct wm97xx *wm = dev_get_drvdata(dev);
++
++ /* Stop touch reader thread */
++ if (wm->ts_task) {
++ wm->ts_task = NULL;
++ wm->pen_is_down = 1;
++ wake_up_interruptible(&wm->pen_irq_wait);
++ wait_for_completion(&wm->ts_exit);
++ }
++ device_unregister(wm->battery_dev);
++ device_unregister(wm->touch_dev);
++ input_unregister_device(wm->input_dev);
++
++ if(aux_sys)
++ wm97xx_sys_remove(dev);
++
++ kfree(wm);
++ return 0;
++}
++
++#ifdef CONFIG_PM
++int wm97xx_resume(struct device* dev)
++{
++ struct wm97xx *wm = dev_get_drvdata(dev);
++
++ /* restore digitiser and gpio's */
++ if(wm->id == WM9713_ID2) {
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]);
++ wm97xx_reg_write(wm, 0x5a, wm->misc);
++ if(wm->ts_use_count) {
++ u16 reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff;
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg);
++ }
++ }
++
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]);
++
++ wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]);
++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]);
++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]);
++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]);
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]);
++ wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]);
++
++ return 0;
++}
++
++#else
++#define wm97xx_resume NULL
++#endif
++
++int wm97xx_register_mach_ops(struct wm97xx *wm, struct wm97xx_mach_ops *mach_ops)
++{
++ mutex_lock(&wm->codec_mutex);
++ if(wm->mach_ops) {
++ mutex_unlock(&wm->codec_mutex);
++ return -EINVAL;
++ }
++ wm->mach_ops = mach_ops;
++ mutex_unlock(&wm->codec_mutex);
++ return 0;
++}
++
++void wm97xx_unregister_mach_ops(struct wm97xx *wm)
++{
++ mutex_lock(&wm->codec_mutex);
++ wm->mach_ops = NULL;
++ mutex_unlock(&wm->codec_mutex);
++}
++
++static struct device_driver wm97xx_driver = {
++ .name = "ac97",
++ .bus = &ac97_bus_type,
++ .owner = THIS_MODULE,
++ .probe = wm97xx_probe,
++ .remove = wm97xx_remove,
++ .resume = wm97xx_resume,
++};
++
++static int __init wm97xx_init(void)
++{
++ int ret;
++
++ info("version %s liam.girdwood@wolfsonmicro.com", WM_CORE_VERSION);
++ if((ret = bus_register(&wm97xx_bus_type)) < 0)
++ return ret;
++ return driver_register(&wm97xx_driver);
++}
++
++static void __exit wm97xx_exit(void)
++{
++ driver_unregister(&wm97xx_driver);
++ bus_unregister(&wm97xx_bus_type);
++}
++
++EXPORT_SYMBOL_GPL(wm97xx_get_gpio);
++EXPORT_SYMBOL_GPL(wm97xx_set_gpio);
++EXPORT_SYMBOL_GPL(wm97xx_config_gpio);
++EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc);
++EXPORT_SYMBOL_GPL(wm97xx_reg_read);
++EXPORT_SYMBOL_GPL(wm97xx_reg_write);
++EXPORT_SYMBOL_GPL(wm97xx_bus_type);
++EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops);
++EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops);
++
++module_init(wm97xx_init);
++module_exit(wm97xx_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17/include/linux/wm97xx.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17/include/linux/wm97xx.h 2006-09-19 20:36:47.973052250 +0200
+@@ -0,0 +1,291 @@
++
++/*
++ * Register bits and API for Wolfson WM97xx series of codecs
++ */
++
++#ifndef _LINUX_WM97XX_H
++#define _LINUX_WM97XX_H
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/ac97_codec.h>
++#include <sound/initval.h>
++#include <linux/types.h>
++#include <linux/list.h>
++#include <linux/input.h> /* Input device 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
++
++/*
++ * 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_ADCSEL_MASK 0x7000
++#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_CM_RATE_MASK 0x03f0
++#define WM97XX_RATE(i) (((i & 3) << 8) | ((i & 4) ? 0xf0 : 0))
++#define WM97XX_DELAY(i) ((i << 4) & 0x00f0) /* sample delay times */
++#define WM97XX_DELAY_MASK 0x00f0
++#define WM97XX_SLEN 0x0008 /* slot read back enable */
++#define WM97XX_SLT(i) ((i - 5) & 0x7) /* touchpanel slot selection (5-11) */
++#define WM97XX_SLT_MASK 0x0007
++#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 */
++#define WM97XX_ADCSRC_MASK 0x7000 /* ADC source mask */
++
++#define WM97XX_AUX_ID1 0x8001
++#define WM97XX_AUX_ID2 0x8002
++#define WM97XX_AUX_ID3 0x8003
++#define WM97XX_AUX_ID4 0x8004
++
++
++/* 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_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 */
++
++
++/* 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 */
++#define WM9713_PDEN 0x0800 /* measure only when pen down */
++#define WM9713_ADCSEL_MASK 0x00fe /* ADC selection mask */
++#define WM9713_WAIT 0x0200 /* coordinate wait */
++
++/* 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
++
++/* Codec GPIO's */
++#define WM97XX_MAX_GPIO 16
++#define WM97XX_GPIO_1 (1 << 1)
++#define WM97XX_GPIO_2 (1 << 2)
++#define WM97XX_GPIO_3 (1 << 3)
++#define WM97XX_GPIO_4 (1 << 4)
++#define WM97XX_GPIO_5 (1 << 5)
++#define WM97XX_GPIO_6 (1 << 6)
++#define WM97XX_GPIO_7 (1 << 7)
++#define WM97XX_GPIO_8 (1 << 8)
++#define WM97XX_GPIO_9 (1 << 9)
++#define WM97XX_GPIO_10 (1 << 10)
++#define WM97XX_GPIO_11 (1 << 11)
++#define WM97XX_GPIO_12 (1 << 12)
++#define WM97XX_GPIO_13 (1 << 13)
++#define WM97XX_GPIO_14 (1 << 14)
++#define WM97XX_GPIO_15 (1 << 15)
++
++
++#define AC97_LINK_FRAME 21 /* time in uS for AC97 link frame */
++
++
++/*---------------- Return codes from sample reading functions ---------------*/
++
++/* More data is available; call the sample gathering function again */
++#define RC_AGAIN 0x00000001
++/* The returned sample is valid */
++#define RC_VALID 0x00000002
++/* The pen is up (the first RC_VALID without RC_PENUP means pen is down) */
++#define RC_PENUP 0x00000004
++/* The pen is down (RC_VALID implies RC_PENDOWN, but sometimes it is helpful
++ to tell the handler that the pen is down but we don't know yet his coords,
++ so the handler should not sleep or wait for pendown irq) */
++#define RC_PENDOWN 0x00000008
++
++/* The wm97xx driver provides a private API for writing platform-specific
++ * drivers.
++ */
++
++/* The structure used to return arch specific sampled data into */
++struct wm97xx_data {
++ int x;
++ int y;
++ int p;
++};
++
++/* Codec GPIO status
++ */
++typedef enum {
++ WM97XX_GPIO_HIGH,
++ WM97XX_GPIO_LOW
++} wm97xx_gpio_status_t;
++
++/* Codec GPIO direction
++ */
++typedef enum {
++ WM97XX_GPIO_IN,
++ WM97XX_GPIO_OUT
++} wm97xx_gpio_dir_t;
++
++/* Codec GPIO polarity
++ */
++typedef enum {
++ WM97XX_GPIO_POL_HIGH,
++ WM97XX_GPIO_POL_LOW
++} wm97xx_gpio_pol_t;
++
++/* Codec GPIO sticky
++ */
++typedef enum {
++ WM97XX_GPIO_STICKY,
++ WM97XX_GPIO_NOTSTICKY
++} wm97xx_gpio_sticky_t;
++
++/* Codec GPIO wake
++ */
++typedef enum {
++ WM97XX_GPIO_WAKE,
++ WM97XX_GPIO_NOWAKE
++} wm97xx_gpio_wake_t;
++
++
++/*
++ * Digitiser ioctl commands
++ */
++#define WM97XX_DIG_START 0x1
++#define WM97XX_DIG_STOP 0x2
++#define WM97XX_PHY_INIT 0x3
++#define WM97XX_AUX_PREPARE 0x4
++#define WM97XX_DIG_RESTORE 0x5
++
++struct wm97xx;
++extern struct wm97xx_codec_drv wm97xx_codec;
++
++/*
++ * Codec driver interface - allows mapping to WM9705/12/13 and newer codecs
++ */
++struct wm97xx_codec_drv {
++ u16 id;
++ char *name;
++ int (*poll_sample) (struct wm97xx *, int adcsel, int *sample); /* read 1 sample */
++ int (*poll_touch) (struct wm97xx *, struct wm97xx_data *); /* read X,Y,[P] in poll */
++ int (*digitiser_ioctl) (struct wm97xx *, int cmd);
++ int (*acc_enable) (struct wm97xx *, int enable);
++};
++
++
++/* Machine specific and accelerated touch operations */
++struct wm97xx_mach_ops {
++
++ /* accelerated touch readback - coords are transmited on AC97 link */
++ int acc_enabled;
++ void (*acc_pen_up) (struct wm97xx *);
++ int (*acc_pen_down) (struct wm97xx *);
++ int (*acc_startup) (struct wm97xx *);
++ void (*acc_shutdown) (struct wm97xx *);
++
++ /* pre and post sample - can be used to minimise any analog noise */
++ void (*pre_sample) (int); /* function to run before sampling */
++ void (*post_sample) (int); /* function to run after sampling */
++};
++
++struct wm97xx {
++ u16 dig[3], id, gpio[6], misc; /* Cached codec registers */
++ u16 dig_save[3]; /* saved during aux reading */
++ struct wm97xx_codec_drv *codec; /* attached codec driver*/
++ struct input_dev* input_dev; /* touchscreen input device */
++ ac97_t *ac97; /* ALSA codec access */
++ struct device *dev; /* ALSA device */
++ struct device *battery_dev;
++ struct device *touch_dev;
++ struct wm97xx_mach_ops *mach_ops;
++ struct mutex codec_mutex;
++ struct completion ts_init;
++ struct completion ts_exit;
++ struct task_struct *ts_task;
++ unsigned int pen_irq; /* Pen IRQ number in use */
++ wait_queue_head_t pen_irq_wait; /* Pen IRQ wait queue */
++ struct workqueue_struct *pen_irq_workq;
++ struct work_struct pen_event_work;
++ u16 acc_slot; /* AC97 slot used for acc touch data */
++ u16 acc_rate; /* acc touch data rate */
++ unsigned int ts_use_count;
++ unsigned pen_is_down:1; /* Pen is down */
++ unsigned aux_waiting:1; /* aux measurement waiting */
++ unsigned pen_probably_down:1; /* used in polling mode */
++};
++
++/* Codec GPIO access (not supported on WM9705)
++ * This can be used to set/get codec GPIO and Virtual GPIO status.
++ */
++wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio);
++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
++ wm97xx_gpio_status_t status);
++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio,
++ wm97xx_gpio_dir_t dir,
++ wm97xx_gpio_pol_t pol,
++ wm97xx_gpio_sticky_t sticky,
++ wm97xx_gpio_wake_t wake);
++
++/* codec AC97 IO access */
++int wm97xx_reg_read(struct wm97xx *wm, u16 reg);
++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val);
++
++/* aux adc readback */
++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel);
++
++/* machine ops */
++int wm97xx_register_mach_ops(struct wm97xx *, struct wm97xx_mach_ops *);
++void wm97xx_unregister_mach_ops(struct wm97xx *);
++
++extern struct bus_type wm97xx_bus_type;
++#endif
diff --git a/packages/linux/linux-rp_2.6.22.bb b/packages/linux/linux-rp_2.6.22.bb
index 50ee894ee9..939e630624 100644
--- a/packages/linux/linux-rp_2.6.22.bb
+++ b/packages/linux/linux-rp_2.6.22.bb
@@ -87,29 +87,33 @@ SRC_URI_append_collie = "\
# ${DOSRC}/collie/collie-pm-r1.patch;patch=1 \
"
+#- ${CHSRC}/usb-ohci-hooks-r1.patch;patch=1 \
+#- file://tmio-ohci-r6.patch;patch=1 \
+# wm97xx-lg13-r0.patch;patch=1 was adapted from $(DOSRC) to apply cleanly
SRC_URI_append_tosa = "\
- ${CHSRC}/usb-ohci-hooks-r1.patch;patch=1 \
${CHSRC}/tmio-core-r4.patch;patch=1 \
file://tmio-tc6393-r8.patch;patch=1 \
- file://tmio-nand-r7.patch;patch=1 \
- file://tmio-ohci-r6.patch;patch=1 \
+ file://tmio-nand-r8.patch;patch=1 \
${CHSRC}/tmio-fb-r6.patch;patch=1 \
- file://tosa-keyboard-r18.patch;patch=1 \
+ file://tmio-fb-r6-fix-r0.patch;patch=1 \
+ file://tosa-keyboard-r19.patch;patch=1 \
${DOSRC}/tosa-pxaac97-r6.patch;patch=1 \
+ file://tosa-pxaac97-r6-fix-r0.patch;patch=1 \
${DOSRC}/tosa-tmio-r6.patch;patch=1 \
- ${DOSRC}/tosa-power-r17.patch;patch=1 \
+ ${DOSRC}/tosa-power-r18.patch;patch=1 \
+ file://tosa-power-r18-fix-r0.patch;patch=1 \
file://tosa-tmio-lcd-r10.patch;patch=1 \
+ file://tosa-tmio-lcd-r10-fix-r0.patch;patch=1 \
${DOSRC}/tosa-bluetooth-r8.patch;patch=1 \
- ${DOSRC}/wm97xx-lg7-r0.patch;patch=1 \
+ file://wm97xx-lg13-r0.patch;patch=1 \
+ file://wm97xx-lg13-r0-fix-r0.patch;patch=1 \
file://wm9712-suspend-cold-res-r2.patch;patch=1 \
file://sharpsl-pm-postresume-r1.patch;patch=1 \
- ${DOSRC}/wm97xx-dig-restore-r0.patch;patch=1 \
- ${DOSRC}/wm97xx-miscdevs-resume-r0.patch;patch=1 \
file://wm9712-reset-loop-r2.patch;patch=1 \
file://tosa-lcdnoise-r1.patch;patch=1 \
- file://wm97xx-lcdnoise-r0.patch;patch=1 "
+ file://tosa-lcdnoise-r1-fix-r0.patch;patch=1 "
# ${DOSRC}/tosa-asoc-r1.patch;patch=1 "
-SRC_URI_append_htcuniversal ="file://htcuni-acx.patch;patch=1;status=external"
+SRC_URI_append_htcuniversal = "file://htcuni-acx.patch;patch=1;status=external"
S = "${WORKDIR}/linux-2.6.22"
diff --git a/packages/linux/linux-rp_2.6.23.bb b/packages/linux/linux-rp_2.6.23.bb
index 840400381e..ab0fe13805 100644
--- a/packages/linux/linux-rp_2.6.23.bb
+++ b/packages/linux/linux-rp_2.6.23.bb
@@ -93,27 +93,35 @@ SRC_URI_append_collie = "\
# ${DOSRC}/collie/collie-pm-r1.patch;patch=1 \
"
+
+# wm97xx-lg13-r0.patch, tosa-power-r18.patch and tosa-bluetooth-r8.patch
+# were adapted from $(DOSRC) to apply cleanly
+# FIXME:
+#- ${CHSRC}/usb-ohci-hooks-r1.patch;patch=1 \
+#- file://tmio-ohci-r6.patch;patch=1 \
SRC_URI_append_tosa = "\
- ${CHSRC}/usb-ohci-hooks-r1.patch;patch=1 \
${CHSRC}/tmio-core-r4.patch;patch=1 \
file://tmio-tc6393-r8.patch;patch=1 \
- file://tmio-nand-r7.patch;patch=1 \
- file://tmio-ohci-r6.patch;patch=1 \
+ file://tmio-nand-r8.patch;patch=1 \
${CHSRC}/tmio-fb-r6.patch;patch=1 \
- file://tosa-keyboard-r18.patch;patch=1 \
+ file://tmio-fb-r6-fix-r0.patch;patch=1 \
+ file://tosa-keyboard-r19.patch;patch=1 \
${DOSRC}/tosa-pxaac97-r6.patch;patch=1 \
+ file://tosa-pxaac97-r6-fix-r0.patch;patch=1 \
${DOSRC}/tosa-tmio-r6.patch;patch=1 \
- ${DOSRC}/tosa-power-r17.patch;patch=1 \
+ file://tosa-power-r18.patch;patch=1 \
+ file://tosa-power-r18-fix-r0.patch;patch=1 \
file://tosa-tmio-lcd-r10.patch;patch=1 \
- ${DOSRC}/tosa-bluetooth-r8.patch;patch=1 \
- ${DOSRC}/wm97xx-lg7-r0.patch;patch=1 \
+ file://tosa-tmio-lcd-r10-fix-r0.patch;patch=1 \
+ file://tosa-bluetooth-r8.patch;patch=1 \
+ file://wm97xx-lg13-r0.patch;patch=1 \
+ file://wm97xx-lg13-r0-fix-r0.patch;patch=1 \
file://wm9712-suspend-cold-res-r2.patch;patch=1 \
file://sharpsl-pm-postresume-r1.patch;patch=1 \
- ${DOSRC}/wm97xx-dig-restore-r0.patch;patch=1 \
- ${DOSRC}/wm97xx-miscdevs-resume-r0.patch;patch=1 \
file://wm9712-reset-loop-r2.patch;patch=1 \
file://tosa-lcdnoise-r1.patch;patch=1 \
- file://wm97xx-lcdnoise-r0.patch;patch=1 "
+ file://tosa-lcdnoise-r1-fix-r0.patch;patch=1 \
+ "
# ${DOSRC}/tosa-asoc-r1.patch;patch=1 "
SRC_URI_append_htcuniversal ="\