linux-rp: update Tosa patchset in 2.6.22 and 2.6.23 - close #3233
authorMarcin Juszkiewicz <hrw@openembedded.org>
Tue, 30 Oct 2007 15:21:19 +0000 (15:21 +0000)
committerMarcin Juszkiewicz <hrw@openembedded.org>
Tue, 30 Oct 2007 15:21:19 +0000 (15:21 +0000)
USB Host patches are not updated

38 files changed:
packages/linux/linux-rp-2.6.22/defconfig-tosa
packages/linux/linux-rp-2.6.22/sharpsl-pm-postresume-r1.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.22/tmio-fb-r6-fix-r0.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.22/tmio-nand-r8.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.22/tmio-ohci-r6.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.22/tmio-tc6393-r8.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.22/tosa-keyboard-r19.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.22/tosa-lcdnoise-r1-fix-r0.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.22/tosa-lcdnoise-r1.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.22/tosa-power-r18-fix-r0.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.22/tosa-pxaac97-r6-fix-r0.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.22/tosa-tmio-lcd-r10-fix-r0.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.22/tosa-tmio-lcd-r10.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.22/usb-ohci-hooks-r2.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.22/wm9712-reset-loop-r2.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.22/wm9712-suspend-cold-res-r2.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.22/wm97xx-lg13-r0-fix-r0.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.22/wm97xx-lg13-r0.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/defconfig-tosa
packages/linux/linux-rp-2.6.23/sharpsl-pm-postresume-r1.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/tmio-fb-r6-fix-r0.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/tmio-nand-r8.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/tmio-tc6393-r8.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/tosa-bluetooth-r8.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/tosa-keyboard-r19.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/tosa-lcdnoise-r1-fix-r0.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/tosa-lcdnoise-r1.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/tosa-power-r18-fix-r0.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/tosa-power-r18.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/tosa-pxaac97-r6-fix-r0.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/tosa-tmio-lcd-r10-fix-r0.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/tosa-tmio-lcd-r10.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/wm9712-reset-loop-r2.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/wm9712-suspend-cold-res-r2.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0-fix-r0.patch [new file with mode: 0644]
packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0.patch [new file with mode: 0644]
packages/linux/linux-rp_2.6.22.bb
packages/linux/linux-rp_2.6.23.bb

index 74fc076..ef44385 100644 (file)
@@ -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 (file)
index 0000000..409daf0
--- /dev/null
@@ -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 (file)
index 0000000..eab57c5
--- /dev/null
@@ -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 (file)
index 0000000..a71fd11
--- /dev/null
@@ -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 (file)
index 0000000..9fdd296
--- /dev/null
@@ -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 (file)
index 0000000..1bfdc23
--- /dev/null
@@ -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 (file)
index 0000000..948c27f
--- /dev/null
@@ -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 (file)
index 0000000..93a9c18
--- /dev/null
@@ -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 (file)
index 0000000..21f3cf6
--- /dev/null
@@ -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 (file)
index 0000000..8899ae2
--- /dev/null
@@ -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 (file)
index 0000000..9c18aae
--- /dev/null
@@ -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 (file)
index 0000000..a2e2bee
--- /dev/null
@@ -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 (file)
index 0000000..aef3a04
--- /dev/null
@@ -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 (file)
index 0000000..3656e98
--- /dev/null
@@ -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 (file)
index 0000000..78e81ea
--- /dev/null
@@ -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 (file)
index 0000000..5179b47
--- /dev/null
@@ -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 (file)
index 0000000..5ad0d87
--- /dev/null
@@ -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 (file)
index 0000000..c918c5d
--- /dev/null
@@ -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
index 74fc076..08c2bbe 100644 (file)
@@ -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 (file)
index 0000000..e1491d6
--- /dev/null
@@ -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 (file)
index 0000000..523edec
--- /dev/null
@@ -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 (file)
index 0000000..3a30c2f
--- /dev/null
@@ -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 (file)
index 0000000..500fa83
--- /dev/null
@@ -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 (file)
index 0000000..9976549
--- /dev/null
@@ -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 (file)
index 0000000..5d94f25
--- /dev/null
@@ -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 (file)
index 0000000..02dd6a7
--- /dev/null
@@ -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 (file)
index 0000000..910b727
--- /dev/null
@@ -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 (file)
index 0000000..fdfbea8
--- /dev/null
@@ -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 (file)
index 0000000..a099717
--- /dev/null
@@ -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 (file)
index 0000000..6f6f540
--- /dev/null
@@ -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 (file)
index 0000000..73913bc
--- /dev/null
@@ -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 (file)
index 0000000..cd24cc6
--- /dev/null
@@ -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 (file)
index 0000000..0e32a62
--- /dev/null
@@ -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 (file)
index 0000000..cbf854d
--- /dev/null
@@ -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 (file)
index 0000000..f246fbf
--- /dev/null
@@ -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 (file)
index 0000000..b029ccc
--- /dev/null
@@ -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
index 50ee894..939e630 100644 (file)
@@ -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"
index 8404003..ab0fe13 100644 (file)
@@ -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 ="\