diff options
38 files changed, 20815 insertions, 52 deletions
diff --git a/packages/linux/linux-rp-2.6.22/defconfig-tosa b/packages/linux/linux-rp-2.6.22/defconfig-tosa index 74fc076608..ef44385067 100644 --- a/packages/linux/linux-rp-2.6.22/defconfig-tosa +++ b/packages/linux/linux-rp-2.6.22/defconfig-tosa @@ -262,6 +262,9 @@ CONFIG_SYN_COOKIES=y # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set # CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=m +CONFIG_INET_XFRM_MODE_TUNNEL=m +CONFIG_INET_XFRM_MODE_BEET=m CONFIG_INET_DIAG=m CONFIG_INET_TCP_DIAG=m # CONFIG_TCP_CONG_ADVANCED is not set @@ -792,9 +795,11 @@ CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_WM97XX=y # CONFIG_TOUCHSCREEN_WM9705 is not set CONFIG_TOUCHSCREEN_WM9712=y +CONFIG_TOUCHSCREEN_TOSA=y # CONFIG_TOUCHSCREEN_WM9713 is not set # CONFIG_TOUCHSCREEN_WM97XX_PXA is not set -# CONFIG_INPUT_MISC is not set +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=m # # Hardware I/O ports @@ -1040,7 +1045,7 @@ CONFIG_SND_SUPPORT_OLD_API=y # # Generic devices # -CONFIG_SND_AC97_BUS=y +CONFIG_AC97_BUS=y CONFIG_SND_DUMMY=m # CONFIG_SND_MTPAV is not set # CONFIG_SND_SERIAL_U16550 is not set @@ -1072,15 +1077,15 @@ CONFIG_SND_SOC=y # # SoC Audio for the Intel PXA2xx # -CONFIG_SND_PXA2xx_SOC=y -CONFIG_SND_PXA2xx_SOC_AC97=y -# CONFIG_SND_PXA2xx_SOC_MAINSTONE is not set -# CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM8753 is not set -# CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM9713 is not set -# CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM9712 is not set -# CONFIG_SND_PXA2xx_SOC_CORGI is not set -# CONFIG_SND_PXA2xx_SOC_SPITZ is not set -CONFIG_SND_PXA2xx_SOC_TOSA=y +CONFIG_SND_PXA2XX_SOC=y +CONFIG_SND_PXA2XX_SOC_AC97=y +# CONFIG_SND_PXA2XX_SOC_MAINSTONE is not set +# CONFIG_SND_PXA2XX_SOC_MAINSTONE_WM8753 is not set +# CONFIG_SND_PXA2XX_SOC_MAINSTONE_WM9713 is not set +# CONFIG_SND_PXA2XX_SOC_MAINSTONE_WM9712 is not set +# CONFIG_SND_PXA2XX_SOC_CORGI is not set +# CONFIG_SND_PXA2XX_SOC_SPITZ is not set +CONFIG_SND_PXA2XX_SOC_TOSA=y # # Soc Codecs @@ -1572,6 +1577,7 @@ CONFIG_DEBUG_ERRORS=y # Cryptographic options # CONFIG_CRYPTO=y +CONFIG_CRYPTO_ALGAPI=y CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_MD4=m diff --git a/packages/linux/linux-rp-2.6.22/sharpsl-pm-postresume-r1.patch b/packages/linux/linux-rp-2.6.22/sharpsl-pm-postresume-r1.patch new file mode 100644 index 0000000000..409daf03e6 --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/sharpsl-pm-postresume-r1.patch @@ -0,0 +1,30 @@ + arch/arm/common/sharpsl_pm.c | 3 +++ + include/asm-arm/hardware/sharpsl_pm.h | 1 + + 2 files changed, 4 insertions(+) + +Index: git/include/asm-arm/hardware/sharpsl_pm.h +=================================================================== +--- git.orig/include/asm-arm/hardware/sharpsl_pm.h 2006-10-31 16:09:33.000000000 +0000 ++++ git/include/asm-arm/hardware/sharpsl_pm.h 2006-11-07 22:08:41.000000000 +0000 +@@ -26,6 +26,7 @@ struct sharpsl_charger_machinfo { + void (*presuspend)(void); + void (*postsuspend)(void); + void (*earlyresume)(void); ++ void (*postresume)(void); + unsigned long (*read_devdata)(int); + #define SHARPSL_BATT_VOLT 1 + #define SHARPSL_BATT_TEMP 2 +Index: git/arch/arm/common/sharpsl_pm.c +=================================================================== +--- git.orig/arch/arm/common/sharpsl_pm.c 2006-11-07 22:03:48.000000000 +0000 ++++ git/arch/arm/common/sharpsl_pm.c 2006-11-07 22:04:20.000000000 +0000 +@@ -584,6 +584,9 @@ static int corgi_pxa_pm_enter(suspend_st + if (sharpsl_pm.machinfo->earlyresume) + sharpsl_pm.machinfo->earlyresume(); + ++ if (sharpsl_pm.machinfo->postresume) ++ sharpsl_pm.machinfo->postresume(); ++ + dev_dbg(sharpsl_pm.dev, "SharpSL resuming...\n"); + + return 0; diff --git a/packages/linux/linux-rp-2.6.22/tmio-fb-r6-fix-r0.patch b/packages/linux/linux-rp-2.6.22/tmio-fb-r6-fix-r0.patch new file mode 100644 index 0000000000..eab57c50e8 --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/tmio-fb-r6-fix-r0.patch @@ -0,0 +1,45 @@ +From 302745ce6f3bab7b1a97de32339405ae3fd8eacb Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 00:05:54 +0400 +Subject: [PATCH] tmio-fb-r6.patch fixes + +--- + drivers/video/tmiofb.c | 8 ++++---- + 1 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c +index 10b0105..72eb76c 100644 +--- a/drivers/video/tmiofb.c ++++ b/drivers/video/tmiofb.c +@@ -463,8 +463,8 @@ static int tmiofb_vblank (struct fb_info *fbi, struct fb_vblank *vblank) + #define FBIO_TMIO_ACC_WRITE 0x7C639300 + #define FBIO_TMIO_ACC_SYNC 0x7C639301 + +-static int tmiofb_ioctl (struct inode *inode, struct file *file, +- unsigned int cmd, unsigned long arg, struct fb_info *fbi) ++static int tmiofb_ioctl (struct fb_info *fbi, ++ unsigned int cmd, unsigned long arg) + { + switch (cmd) { + case FBIOGET_VBLANK: { +@@ -677,7 +677,7 @@ static struct fb_ops tmiofb_ops_acc = { + * 2000 0002 display start + * 2000 0004 line number match (0x1ff mask???) + */ +-static irqreturn_t tmiofb_irq (int irq, void *__fbi, struct pt_regs *r) ++static irqreturn_t tmiofb_irq (int irq, void *__fbi) + { + struct fb_info* fbi = __fbi; + struct tmiofb_par* par = fbi->par; +@@ -762,7 +762,7 @@ static int __init tmiofb_probe (struct device *dev) + } + fbi->screen_base = par->sram; + +- retval = request_irq (irq->start, &tmiofb_irq, SA_INTERRUPT, ++ retval = request_irq (irq->start, &tmiofb_irq, IRQF_DISABLED, + TMIO_NAME_LCD, fbi); + if (retval) + goto err_request_irq; +-- +1.4.4.4 + diff --git a/packages/linux/linux-rp-2.6.22/tmio-nand-r8.patch b/packages/linux/linux-rp-2.6.22/tmio-nand-r8.patch new file mode 100644 index 0000000000..a71fd114a8 --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/tmio-nand-r8.patch @@ -0,0 +1,594 @@ + drivers/mtd/nand/Kconfig | 7 + + drivers/mtd/nand/Makefile | 1 + + drivers/mtd/nand/tmio.c | 554 +++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 562 insertions(+), 0 deletions(-) + +diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig +index f1d60b6..b9c8796 100644 +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -69,6 +69,13 @@ config MTD_NAND_AMS_DELTA + help + Support for NAND flash on Amstrad E3 (Delta). + ++config MTD_NAND_TMIO ++ tristate "NAND Flash device on Toshiba Mobile IO Controller" ++ depends on MTD_NAND && TOSHIBA_TC6393XB ++ help ++ Support for NAND flash connected to a Toshiba Mobile IO ++ Controller in some PDAs, including the Sharp SL6000x. ++ + config MTD_NAND_TOTO + tristate "NAND Flash device on TOTO board" + depends on ARCH_OMAP && BROKEN +diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile +index edba1db..64f24e1 100644 +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -27,5 +27,6 @@ obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o + obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o + obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o + obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o ++obj-$(CONFIG_MTD_NAND_TMIO) += tmio.o + + nand-objs := nand_base.o nand_bbt.o +diff --git a/drivers/mtd/nand/tmio.c b/drivers/mtd/nand/tmio.c +new file mode 100644 +index 0000000..d196553 +--- /dev/null ++++ b/drivers/mtd/nand/tmio.c +@@ -0,0 +1,554 @@ ++/* ++ * A device driver for NAND flash connected to a Toshiba Mobile IO ++ * controller. This is known to work with the following variants: ++ * TC6393XB revision 3 ++ * ++ * Maintainer: Chris Humbert <mahadri+mtd@drigon.com> ++ * ++ * Copyright (C) 2005 Chris Humbert ++ * Copyright (C) 2005 Dirk Opfer ++ * Copyright (C) 2004 SHARP ++ * Copyright (C) 2002 Lineo Japan, Inc. ++ * Copyright (C) Ian Molton and Sebastian Carlier ++ * ++ * Based on Sharp's NAND driver, sharp_sl_tc6393.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/delay.h> ++#include <linux/wait.h> ++#include <linux/ioport.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/nand_ecc.h> ++#include <linux/mtd/partitions.h> ++#include <asm/io.h> ++#include <asm/hardware/tmio.h> ++ ++#include <linux/interrupt.h> ++ ++#define mtd_printk(level, mtd, format, arg...) \ ++ printk (level "%s: " format, mtd->name, ## arg) ++#define mtd_warn(mtd, format, arg...) \ ++ mtd_printk (KERN_WARNING, mtd, format, ## arg) ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* tmio_nfcr.mode Register Command List */ ++#define FCR_MODE_DATA 0x94 // Data Data_Mode ++#define FCR_MODE_COMMAND 0x95 // Data Command_Mode ++#define FCR_MODE_ADDRESS 0x96 // Data Address_Mode ++ ++#define FCR_MODE_HWECC_CALC 0xB4 // HW-ECC Data ++#define FCR_MODE_HWECC_RESULT 0xD4 // HW-ECC Calculation Result Read_Mode ++#define FCR_MODE_HWECC_RESET 0xF4 // HW-ECC Reset ++ ++#define FCR_MODE_POWER_ON 0x0C // Power Supply ON to SSFDC card ++#define FCR_MODE_POWER_OFF 0x08 // Power Supply OFF to SSFDC card ++ ++#define FCR_MODE_LED_OFF 0x00 // LED OFF ++#define FCR_MODE_LED_ON 0x04 // LED ON ++ ++#define FCR_MODE_EJECT_ON 0x68 // Ejection Demand from Penguin is Advanced ++#define FCR_MODE_EJECT_OFF 0x08 // Ejection Demand from Penguin is Not Advanced ++ ++#define FCR_MODE_LOCK 0x6C // Operates By Lock_Mode. Ejection Switch is Invalid ++#define FCR_MODE_UNLOCK 0x0C // Operates By UnLock_Mode.Ejection Switch is Effective ++ ++#define FCR_MODE_CONTROLLER_ID 0x40 // Controller ID Read ++#define FCR_MODE_STANDBY 0x00 // SSFDC card Changes Standby State ++ ++#define FCR_MODE_WE 0x80 ++#define FCR_MODE_ECC1 0x40 ++#define FCR_MODE_ECC0 0x20 ++#define FCR_MODE_CE 0x10 ++#define FCR_MODE_PCNT1 0x08 ++#define FCR_MODE_PCNT0 0x04 ++#define FCR_MODE_ALE 0x02 ++#define FCR_MODE_CLE 0x01 ++ ++#define FCR_STATUS_BUSY 0x80 ++ ++/* ++ * NAND Flash Host Controller Configuration Register ++ */ ++struct tmio_nfhccr { ++ u8 x00[4]; ++ u16 command; /* 0x04 Command */ ++ u8 x01[0x0a]; ++ u16 base[2]; /* 0x10 NAND Flash Control Reg Base Addr*/ ++ u8 x02[0x29]; ++ u8 intp; /* 0x3d Interrupt Pin */ ++ u8 x03[0x0a]; ++ u8 inte; /* 0x48 Interrupt Enable */ ++ u8 x04; ++ u8 ec; /* 0x4a Event Control */ ++ u8 x05; ++ u8 icc; /* 0x4c Internal Clock Control */ ++ u8 x06[0x0e]; ++ u8 eccc; /* 0x5b ECC Control */ ++ u8 x07[4]; ++ u8 nftc; /* 0x60 NAND Flash Transaction Control */ ++ u8 nfm; /* 0x61 NAND Flash Monitor */ ++ u8 nfpsc; /* 0x62 NAND Flash Power Supply Control */ ++ u8 nfdc; /* 0x63 NAND Flash Detect Control */ ++ u8 x08[0x9c]; ++} __attribute__ ((packed)); ++ ++/* ++ * NAND Flash Control Register ++ */ ++struct tmio_nfcr { ++union { ++ u8 u8; /* 0x00 Data Register */ ++ u16 u16; ++ u32 u32; ++} __attribute__ ((packed)); ++ u8 mode; /* 0x04 Mode Register */ ++ u8 status; /* 0x05 Status Register */ ++ u8 isr; /* 0x06 Interrupt Status Register */ ++ u8 imr; /* 0x07 Interrupt Mask Register */ ++} __attribute__ ((packed)); ++ ++struct tmio_nand { ++ struct mtd_info mtd; ++ struct nand_chip chip; ++ ++ struct tmio_nfhccr __iomem * ccr; ++ struct tmio_nfcr __iomem * fcr; ++ ++ unsigned int irq; ++ ++ /* for tmio_nand_read_byte */ ++ u8 read; ++ unsigned read_good:1; ++}; ++ ++#define mtd_to_tmio(m) container_of(m, struct tmio_nand, mtd) ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void tmio_nand_hwcontrol(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem *fcr = tmio->fcr; ++ struct nand_chip *chip = mtd->priv; ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ u8 mode; ++ ++ if (ctrl & NAND_NCE) { ++ mode = FCR_MODE_DATA; ++ ++ if (ctrl & NAND_CLE) ++ mode |= FCR_MODE_CLE; ++ else ++ mode &= ~FCR_MODE_CLE; ++ ++ if (ctrl & NAND_ALE) ++ mode |= FCR_MODE_ALE; ++ else ++ mode &= ~FCR_MODE_ALE; ++ } else { ++ mode = FCR_MODE_STANDBY; ++ } ++ ++ iowrite8 (mode, &fcr->mode); ++ tmio->read_good = 0; ++ } ++ ++ if (cmd != NAND_CMD_NONE) ++ writeb(cmd, chip->IO_ADDR_W); ++} ++ ++static int tmio_nand_dev_ready (struct mtd_info* mtd) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ return !(ioread8 (&fcr->status) & FCR_STATUS_BUSY); ++} ++ ++static irqreturn_t tmio_irq (int irq, void *__tmio) ++{ ++ struct tmio_nand* tmio = __tmio; ++ struct nand_chip* this = &tmio->chip; ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ /* disable RDYREQ interrupt */ ++ iowrite8 (0x00, &fcr->imr); ++ ++ if (unlikely (!waitqueue_active (&this->controller->wq))) ++ printk (KERN_WARNING TMIO_NAME_NAND ": spurious interrupt\n"); ++ ++ wake_up (&this->controller->wq); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * The TMIO core has a RDYREQ interrupt on the posedge of #SMRB. ++ * This interrupt is normally disabled, but for long operations like ++ * erase and write, we enable it to wake us up. The irq handler ++ * disables the interrupt. ++ */ ++static int ++tmio_nand_wait (struct mtd_info *mtd, struct nand_chip *this) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ long timeout; ++ ++ /* enable RDYREQ interrupt */ ++ iowrite8 (0x0f, &fcr->isr); ++ iowrite8 (0x81, &fcr->imr); ++ ++ timeout = wait_event_timeout (this->controller->wq, tmio_nand_dev_ready (mtd), ++ msecs_to_jiffies (this->state == FL_ERASING ? 400 : 20)); ++ ++ if (unlikely (!tmio_nand_dev_ready (mtd))) { ++ iowrite8 (0x00, &fcr->imr); ++ mtd_warn (mtd, "still busy with %s after %d ms\n", ++ this->state == FL_ERASING ? "erase" : "program", ++ this->state == FL_ERASING ? 400 : 20); ++ ++ } else if (unlikely (!timeout)) { ++ iowrite8 (0x00, &fcr->imr); ++ mtd_warn (mtd, "timeout waiting for interrupt\n"); ++ } ++ ++ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); ++ return this->read_byte (mtd); ++} ++ ++/* ++ * The TMIO controller combines two 8-bit data bytes into one 16-bit ++ * word. This function separates them so nand_base.c works as expected, ++ * especially its NAND_CMD_READID routines. ++ * ++ * To prevent stale data from being read, tmio_nand_hwcontrol() clears ++ * tmio->read_good. ++ */ ++static u_char tmio_nand_read_byte (struct mtd_info *mtd) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ unsigned int data; ++ ++ if (tmio->read_good--) ++ return tmio->read; ++ ++ data = ioread16 (&fcr->u16); ++ tmio->read = data >> 8; ++ return data; ++} ++ ++/* ++ * The TMIO controller converts an 8-bit NAND interface to a 16-bit ++ * bus interface, so all data reads and writes must be 16-bit wide. ++ * Thus, we implement 16-bit versions of the read, write, and verify ++ * buffer functions. ++ */ ++static void ++tmio_nand_write_buf (struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ iowrite16_rep (&fcr->u16, buf, len >> 1); ++} ++ ++static void tmio_nand_read_buf (struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ ioread16_rep (&fcr->u16, buf, len >> 1); ++} ++ ++static int ++tmio_nand_verify_buf (struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ u16* p = (u16*) buf; ++ ++ for (len >>= 1; len; len--) ++ if (*(p++) != ioread16 (&fcr->u16)) ++ return -EFAULT; ++ return 0; ++} ++ ++static void tmio_nand_enable_hwecc (struct mtd_info* mtd, int mode) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ iowrite8 (FCR_MODE_HWECC_RESET, &fcr->mode); ++ ioread8 (&fcr->u8); /* dummy read */ ++ iowrite8 (FCR_MODE_HWECC_CALC, &fcr->mode); ++} ++ ++static int tmio_nand_calculate_ecc (struct mtd_info* mtd, const u_char* dat, ++ u_char* ecc_code) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ unsigned int ecc; ++ ++ iowrite8 (FCR_MODE_HWECC_RESULT, &fcr->mode); ++ ++ ecc = ioread16 (&fcr->u16); ++ ecc_code[1] = ecc; // 000-255 LP7-0 ++ ecc_code[0] = ecc >> 8; // 000-255 LP15-8 ++ ecc = ioread16 (&fcr->u16); ++ ecc_code[2] = ecc; // 000-255 CP5-0,11b ++ ecc_code[4] = ecc >> 8; // 256-511 LP7-0 ++ ecc = ioread16 (&fcr->u16); ++ ecc_code[3] = ecc; // 256-511 LP15-8 ++ ecc_code[5] = ecc >> 8; // 256-511 CP5-0,11b ++ ++ iowrite8 (FCR_MODE_DATA, &fcr->mode); ++ return 0; ++} ++ ++static void tmio_hw_init (struct device *dev, struct tmio_nand *tmio) ++{ ++ struct resource* nfcr = tmio_resource_control (dev); ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct tmio_nfhccr __iomem * ccr = tmio->ccr; ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ unsigned long base; ++ ++ /* (89h) SMD Buffer ON By TC6393XB SystemConfig gpibfc1 */ ++ tdev->ops->clock (dev, 1); ++ tdev->ops->function (dev, 1); ++ ++ /* (4Ch) CLKRUN Enable 1st spcrunc */ ++ iowrite8 (0x81, &ccr->icc); ++ ++ /* (10h)BaseAddress 0x1000 spba.spba2 */ ++ base = nfcr->start - tdev->iomem->start; ++ iowrite16 (base, ccr->base + 0); ++ iowrite16 (base >> 16, ccr->base + 1); ++ ++ /* (04h)Command Register I/O spcmd */ ++ iowrite8 (0x02, &ccr->command); ++ ++ /* (62h) Power Supply Control ssmpwc */ ++ /* HardPowerOFF - SuspendOFF - PowerSupplyWait_4MS */ ++ iowrite8 (0x02, &ccr->nfpsc); ++ ++ /* (63h) Detect Control ssmdtc */ ++ iowrite8 (0x02, &ccr->nfdc); ++ ++ /* Interrupt status register clear sintst */ ++ iowrite8 (0x0f, &fcr->isr); ++ ++ /* After power supply, Media are reset smode */ ++ iowrite8 (FCR_MODE_POWER_ON, &fcr->mode); ++ iowrite8 (FCR_MODE_COMMAND, &fcr->mode); ++ iowrite8 (NAND_CMD_RESET, &fcr->u8); ++ ++ /* Standby Mode smode */ ++ iowrite8 (FCR_MODE_STANDBY, &fcr->mode); ++ ++ mdelay (5); ++} ++ ++static void tmio_hw_stop (struct device *dev, struct tmio_nand *tmio) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ iowrite8 (FCR_MODE_POWER_OFF, &fcr->mode); ++ tdev->ops->function (dev, 0); ++ tdev->ops->clock (dev, 0); ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++#ifdef CONFIG_MTD_PARTITIONS ++static const char *part_probes[] = { "cmdlinepart", NULL }; ++#endif ++ ++static int tmio_probe (struct device *dev) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct tmio_nand_platform_data* tnpd = dev->platform_data; ++ struct resource* ccr = tmio_resource_config (dev); ++ struct resource* fcr = tmio_resource_control (dev); ++ struct resource* irq = tmio_resource_irq (dev); ++ struct tmio_nand* tmio; ++ struct mtd_info* mtd; ++ struct nand_chip* this; ++ struct mtd_partition* parts; ++ int nbparts = 0; ++ int retval; ++ ++ if (!tnpd) ++ return -EINVAL; ++ ++ retval = request_resource (tdev->iomem, ccr); ++ if (retval) ++ goto err_request_ccr; ++ ++ retval = request_resource (tdev->iomem, fcr); ++ if (retval) ++ goto err_request_fcr; ++ ++ tmio = kzalloc (sizeof *tmio, GFP_KERNEL); ++ if (!tmio) { ++ retval = -ENOMEM; ++ goto err_kzalloc; ++ } ++ ++ dev_set_drvdata (dev, tmio); ++ mtd = &tmio->mtd; ++ this = &tmio->chip; ++ mtd->priv = this; ++ mtd->name = TMIO_NAME_NAND; ++ ++ tmio->ccr = ioremap (ccr->start, ccr->end - ccr->start + 1); ++ if (!tmio->ccr) { ++ retval = -EIO; ++ goto err_iomap_ccr; ++ } ++ ++ tmio->fcr = ioremap (fcr->start, fcr->end - fcr->start + 1); ++ if (!tmio->fcr) { ++ retval = -EIO; ++ goto err_iomap_fcr; ++ } ++ ++ tmio_hw_init (dev, tmio); ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = tmio->fcr; ++ this->IO_ADDR_W = tmio->fcr; ++ ++ /* Set address of hardware control function */ ++ this->cmd_ctrl = tmio_nand_hwcontrol; ++ this->dev_ready = tmio_nand_dev_ready; ++ this->read_byte = tmio_nand_read_byte; ++ this->write_buf = tmio_nand_write_buf; ++ this->read_buf = tmio_nand_read_buf; ++ this->verify_buf = tmio_nand_verify_buf; ++ ++ /* set eccmode using hardware ECC */ ++ this->ecc.mode = NAND_ECC_HW; ++ this->ecc.size = 512; ++ this->ecc.bytes = 6; ++ this->ecc.hwctl = tmio_nand_enable_hwecc; ++ this->ecc.calculate = tmio_nand_calculate_ecc; ++ this->ecc.correct = nand_correct_data; ++ this->badblock_pattern = tnpd->badblock_pattern; ++ ++ /* 15 us command delay time */ ++ this->chip_delay = 15; ++ ++ if (irq->start) { ++ retval = request_irq (irq->start, &tmio_irq, ++ IRQF_DISABLED, irq->name, tmio); ++ if (!retval) { ++ tmio->irq = irq->start; ++ this->waitfunc = tmio_nand_wait; ++ } else ++ mtd_warn (mtd, "request_irq error %d\n", retval); ++ } ++ ++ /* Scan to find existence of the device */ ++ if (nand_scan (mtd, 1)) { ++ retval = -ENODEV; ++ goto err_scan; ++ } ++ ++ /* Register the partitions */ ++#ifdef CONFIG_MTD_PARTITIONS ++ nbparts = parse_mtd_partitions (mtd, part_probes, &parts, 0); ++#endif ++ if (nbparts <= 0) { ++ parts = tnpd->partition; ++ nbparts = tnpd->num_partitions; ++ } ++ ++ add_mtd_partitions (mtd, parts, nbparts); ++ return 0; ++ ++err_scan: ++ if (tmio->irq) ++ free_irq (tmio->irq, tmio); ++ tmio_hw_stop (dev, tmio); ++ iounmap (tmio->fcr); ++err_iomap_fcr: ++ iounmap (tmio->ccr); ++err_iomap_ccr: ++ kfree (tmio); ++err_kzalloc: ++ release_resource (fcr); ++err_request_fcr: ++ release_resource (ccr); ++err_request_ccr: ++ return retval; ++} ++ ++static int tmio_remove (struct device *dev) ++{ ++ struct tmio_nand* tmio = dev_get_drvdata (dev); ++ ++ nand_release (&tmio->mtd); ++ if (tmio->irq) ++ free_irq (tmio->irq, tmio); ++ tmio_hw_stop (dev, tmio); ++ iounmap (tmio->fcr); ++ iounmap (tmio->ccr); ++ kfree (tmio); ++ release_resource (tmio_resource_control (dev)); ++ release_resource (tmio_resource_config (dev)); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int tmio_suspend (struct device *dev, pm_message_t state) ++{ ++ tmio_hw_stop (dev, dev_get_drvdata (dev)); ++ return 0; ++} ++ ++static int tmio_resume (struct device *dev) ++{ ++ tmio_hw_init (dev, dev_get_drvdata (dev)); ++ return 0; ++} ++#endif ++ ++static struct device_driver tmio_driver = { ++ .name = TMIO_NAME_NAND, ++ .bus = &tmio_bus_type, ++ .probe = tmio_probe, ++ .remove = tmio_remove, ++#ifdef CONFIG_PM ++ .suspend = tmio_suspend, ++ .resume = tmio_resume, ++#endif ++}; ++ ++static int __init tmio_init (void) { ++ return driver_register (&tmio_driver); ++} ++ ++static void __exit tmio_exit (void) { ++ driver_unregister (&tmio_driver); ++} ++ ++module_init (tmio_init); ++module_exit (tmio_exit); ++ ++MODULE_LICENSE ("GPL"); ++MODULE_AUTHOR ("Dirk Opfer, Chris Humbert"); ++MODULE_DESCRIPTION ("NAND flash driver on Toshiba Mobile IO controller"); diff --git a/packages/linux/linux-rp-2.6.22/tmio-ohci-r6.patch b/packages/linux/linux-rp-2.6.22/tmio-ohci-r6.patch new file mode 100644 index 0000000000..9fdd2962c9 --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/tmio-ohci-r6.patch @@ -0,0 +1,929 @@ + + drivers/usb/host/Kconfig | 1 + drivers/usb/host/ohci-hcd.c | 1 + drivers/usb/host/ohci-tmio.c | 894 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 896 insertions(+) + +Index: git/drivers/usb/host/ohci-tmio.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ git/drivers/usb/host/ohci-tmio.c 2006-11-07 21:48:33.000000000 +0000 +@@ -0,0 +1,894 @@ ++/* ++ * OHCI HCD (Host Controller Driver) for USB. ++ * ++ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> ++ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> ++ * (C) Copyright 2002 Hewlett-Packard Company ++ * ++ * Bus glue for Toshiba Mobile IO (TMIO) Controller's OHCI core ++ * (C) Copyright 2005 Chris Humbert <mahadri-usb@drigon.com> ++ * ++ * This is known to work with the following variants: ++ * TC6393XB revision 3 (32kB SRAM) ++ * ++ * The TMIO's OHCI core DMAs through a small internal buffer that ++ * is directly addressable by the CPU. dma_declare_coherent_memory ++ * and DMA bounce buffers allow the higher-level OHCI host driver to ++ * work. However, the dma API doesn't handle dma mapping failures ++ * well (dma_sg_map() is a prime example), so it is unusable. ++ * ++ * This HC pretends be a PIO-ish controller and uses the kernel's ++ * generic allocator for the entire SRAM. Using the USB core's ++ * usb_operations, we provide hcd_buffer_alloc/free. Using the OHCI's ++ * ohci_ops, we provide memory management for OHCI's TDs and EDs. We ++ * internally queue a URB's TDs until enough dma memory is available ++ * to enqueue them with the HC. ++ * ++ * Written from sparse documentation from Toshiba and Sharp's driver ++ * for the 2.4 kernel, ++ * usb-ohci-tc6393.c (C) Copyright 2004 Lineo Solutions, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/fs.h> ++#include <linux/mount.h> ++#include <linux/pagemap.h> ++#include <linux/init.h> ++#include <linux/namei.h> ++#include <linux/sched.h> ++ ++#include <linux/genalloc.h> ++#include <asm/dma-mapping.h> /* for consistent_sync() */ ++#include <asm/hardware/tmio.h> ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * USB Host Controller Configuration Register ++ */ ++struct tmio_uhccr { ++ u8 x00[8]; ++ u8 revid; /* 0x08 Revision ID */ ++ u8 x01[7]; ++ u16 basel; /* 0x10 USB Control Register Base Address Low */ ++ u16 baseh; /* 0x12 USB Control Register Base Address High */ ++ u8 x02[0x2c]; ++ u8 ilme; /* 0x40 Internal Local Memory Enable */ ++ u8 x03[0x0b]; ++ u16 pm; /* 0x4c Power Management */ ++ u8 x04[2]; ++ u8 intc; /* 0x50 INT Control */ ++ u8 x05[3]; ++ u16 lmw1l; /* 0x54 Local Memory Window 1 LMADRS Low */ ++ u16 lmw1h; /* 0x56 Local Memory Window 1 LMADRS High */ ++ u16 lmw1bl; /* 0x58 Local Memory Window 1 Base Address Low */ ++ u16 lmw1bh; /* 0x5A Local Memory Window 1 Base Address High */ ++ u16 lmw2l; /* 0x5C Local Memory Window 2 LMADRS Low */ ++ u16 lmw2h; /* 0x5E Local Memory Window 2 LMADRS High */ ++ u16 lmw2bl; /* 0x60 Local Memory Window 2 Base Address Low */ ++ u16 lmw2bh; /* 0x62 Local Memory Window 2 Base Address High */ ++ u8 x06[0x98]; ++ u8 misc; /* 0xFC MISC */ ++ u8 x07[3]; ++} __attribute__ ((packed)); ++ ++union tmio_uhccr_pm { ++ u16 raw; ++struct { ++ unsigned gcken:1; /* D0 */ ++ unsigned ckrnen:1; /* D1 */ ++ unsigned uspw1:1; /* D2 USB Port 1 Power Disable */ ++ unsigned uspw2:1; /* D3 USB Port 2 Power Disable */ ++ unsigned x00:4; ++ unsigned pmee:1; /* D8 */ ++ unsigned x01:6; ++ unsigned pmes:1; /* D15 */ ++} __attribute__ ((packed)); ++} __attribute__ ((packed)); ++ ++/*-------------------------------------------------------------------------*/ ++ ++struct tmio_dma_pool { ++ struct device* dev; ++ unsigned int size; ++}; ++ ++struct tmio_hcd { ++ struct gen_pool* poolp; ++ struct usb_operations ops; ++ struct tmio_dma_pool td_pool; ++ struct tmio_dma_pool ed_pool; ++ ++ struct tmio_uhccr __iomem *ccr; ++ void __iomem * sram; ++ size_t sram_len; ++}; ++ ++#define hcd_to_tmio(hcd) ((struct tmio_hcd*)(hcd_to_ohci (hcd) + 1)) ++ ++struct tmio_td { ++ void* data; /* td's data buffer */ ++ void __iomem * bounce; /* dma bounce buffer */ ++ dma_addr_t dma; /* bounce buffer dma address */ ++ size_t len; /* bounce buffer length */ ++ u32 info; /* parameter for td_fill */ ++}; ++ ++struct tmio_urb { ++ int td_add; /* next index to be added */ ++ int td_queue; /* next index to be HC enqueued */ ++ ++ struct tmio_td td [0]; /* private td data */ ++}; ++ ++static inline struct tmio_urb *urb_to_turb (struct urb *urb) ++{ ++ urb_priv_t* urb_priv = urb->hcpriv; ++ return (struct tmio_urb*)(urb_priv->td + urb_priv->length); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* gen_pool_alloc page allocator callback */ ++static unsigned long tmio_pool_callback(struct gen_pool *poolp) ++{ ++ return 0; ++} ++ ++static inline void tmio_pool_destroy(struct tmio_hcd *tmio) ++{ ++ struct gen_pool *poolp = tmio->poolp; ++ ++ if (!poolp) ++ return; ++ if (poolp->h) ++ kfree(poolp->h); ++ kfree(poolp); ++ tmio->poolp = NULL; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++#define BOUNDED_XYL(x,y,ylen) (((y) <= (x)) && ((x) < ((y)+(ylen)))) ++#define BOUNDED_XYY(x,y1,y2) (((y1) <= (x)) && ((x) < (y2))) ++ ++static inline dma_addr_t tmio_virt_to_dma (struct usb_hcd *hcd, void *vaddr) ++{ ++ struct resource* sram = tmio_resource_mem (hcd->self.controller); ++ struct tmio_hcd* tmio = hcd_to_tmio (hcd); ++ ++ return BOUNDED_XYL (vaddr, tmio->sram, tmio->sram_len) ++ ? sram->start + (vaddr - tmio->sram) ++ : ~0; ++} ++ ++static inline void* tmio_dma_to_virt (struct usb_hcd *hcd, dma_addr_t handle) ++{ ++ struct resource* sram = tmio_resource_mem (hcd->self.controller); ++ struct tmio_hcd* tmio = hcd_to_tmio (hcd); ++ ++ return BOUNDED_XYY (handle, sram->start, sram->end + 1) ++ ? tmio->sram + handle - sram->start ++ : NULL; ++} ++ ++/* ++ * allocate dma-able memory in the device's internal sram ++ * ++ * The generic pool allocator's minimum chunk size is 32 bytes, ++ * which is the cache line size on the PXA255, so we don't need ++ * to do anything special for smaller requests. ++ */ ++static inline void *tmio_dma_alloc (struct device *dev, size_t size, ++ dma_addr_t *handle, gfp_t mem_flags) ++{ ++ struct usb_hcd* hcd = dev_get_drvdata (dev); ++ struct tmio_hcd* tmio = hcd_to_tmio (hcd); ++ void* virt = (void*) gen_pool_alloc (tmio->poolp, size); ++ ++ return (*handle = tmio_virt_to_dma (hcd, virt)) == ~0 ? NULL : virt; ++} ++ ++static inline void tmio_dma_free (struct device *dev, size_t size, ++ void *cpu_addr, dma_addr_t handle) ++{ ++ struct usb_hcd* hcd = dev_get_drvdata (dev); ++ struct tmio_hcd* tmio = hcd_to_tmio (hcd); ++ dma_addr_t dma = tmio_virt_to_dma (hcd, cpu_addr); ++ ++ if (unlikely (dma == ~0)) { ++ dev_err (dev, "trying to free bad address 0x%p\n", cpu_addr); ++ return; ++ } ++ ++ if (unlikely (handle != dma)) ++ dev_err (dev, "dma address mismatch for 0x%p: %08x != %08x\n", ++ cpu_addr, handle, dma); ++ ++ gen_pool_free (tmio->poolp, (unsigned long) cpu_addr, size); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void *tmio_dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags, ++ dma_addr_t *handle) ++{ ++ struct tmio_dma_pool *tdp = (struct tmio_dma_pool*) pool; ++ return tmio_dma_alloc (tdp->dev, tdp->size, handle, mem_flags); ++} ++ ++static void ++tmio_dma_pool_free (struct dma_pool *pool, void *vaddr, dma_addr_t addr) ++{ ++ struct tmio_dma_pool *tdp = (struct tmio_dma_pool*) pool; ++ return tmio_dma_free (tdp->dev, tdp->size, vaddr, addr); ++} ++ ++static void *tmio_buffer_alloc (struct usb_bus *bus, size_t size, ++ gfp_t mem_flags, dma_addr_t *dma) ++{ ++ return tmio_dma_alloc (bus->controller, size, dma, mem_flags); ++} ++ ++static void tmio_buffer_free (struct usb_bus *bus, size_t size, ++ void *addr, dma_addr_t dma) ++{ ++ tmio_dma_free (bus->controller, size, addr, dma); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void tmio_hc_stop (struct usb_hcd *hcd) ++{ ++ struct device* dev = hcd->self.controller; ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct tmio_hcd* tmio = hcd_to_tmio (hcd); ++ struct tmio_uhccr __iomem* ccr = tmio->ccr; ++ union tmio_uhccr_pm pm = {0}; ++ ++ pm.gcken = 1; ++ pm.ckrnen = 1; ++ pm.uspw1 = 1; ++ pm.uspw2 = 1; ++ ++ iowrite8 (0, &ccr->intc); ++ iowrite8 (0, &ccr->ilme); ++ iowrite16(0, &ccr->basel); ++ iowrite16(0, &ccr->baseh); ++ iowrite16(pm.raw, &ccr->pm); ++ ++ tdev->ops->function (dev, 0); ++ tdev->ops->clock (dev, 0); ++} ++ ++static void tmio_hc_start (struct usb_hcd *hcd) ++{ ++ struct device* dev = hcd->self.controller; ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct tmio_hcd* tmio = hcd_to_tmio (hcd); ++ struct tmio_uhccr __iomem* ccr = tmio->ccr; ++ union tmio_uhccr_pm pm = {0}; ++ ++ pm.pmes = 1; ++ pm.pmee = 1; ++ pm.ckrnen = 1; ++ pm.gcken = 1; ++ ++ tdev->ops->clock (dev, 1); ++ tdev->ops->function (dev, 1); ++ ++ iowrite16(pm.raw, &ccr->pm); ++ iowrite16(hcd->rsrc_start, &ccr->basel); ++ iowrite16(hcd->rsrc_start >> 16, &ccr->baseh); ++ iowrite8 (1, &ccr->ilme); ++ iowrite8 (2, &ccr->intc); ++ ++ consistent_sync (tmio->sram, tmio->sram_len, DMA_BIDIRECTIONAL); ++ ++ dev_info (dev, "revision %d @ 0x%08llx, irq %d\n", ++ ioread8 (&ccr->revid), hcd->rsrc_start, hcd->irq); ++} ++ ++static void tmio_stop (struct usb_hcd *hcd) ++{ ++ struct ohci_hcd* ohci = hcd_to_ohci (hcd); ++ ++ /* NULL these so ohci_stop() doesn't try to free them */ ++ ohci->hcca = NULL; ++ ohci->td_cache = NULL; ++ ohci->ed_cache = NULL; ++ ++ ohci_stop (hcd); ++ tmio_hc_stop (hcd); ++ tmio_pool_destroy (hcd_to_tmio (hcd)); ++ ++ /* We don't free the hcca because tmio_hc_stop() turns off ++ * the sram and the memory allocation data is destroyed. */ ++} ++ ++static int tmio_start (struct usb_hcd *hcd) ++{ ++ struct device* dev = hcd->self.controller; ++ struct ohci_hcd* ohci = hcd_to_ohci (hcd); ++ struct tmio_hcd* tmio = hcd_to_tmio (hcd); ++ int retval; ++ ++ tmio_hc_start (hcd); ++ ++ tmio->poolp = gen_pool_create(0, fls(tmio->sram_len) - 1, ++ tmio_pool_callback, 0); ++ if (!tmio->poolp) { ++ retval = -ENOMEM; ++ goto err_gen_pool_create; ++ } ++ ++ gen_pool_free (tmio->poolp, (unsigned long)(tmio->sram), ++ tmio->sram_len); ++ ++ ohci->hcca = tmio_dma_alloc (dev, sizeof *ohci->hcca, ++ &ohci->hcca_dma, GFP_KERNEL); ++ if (!ohci->hcca) { ++ retval = -ENOMEM; ++ goto err_hcca_alloc; ++ } ++ ++ /* for our dma_pool_alloc/free hooks */ ++ ohci->td_cache = (struct dma_pool*) &tmio->td_pool; ++ ohci->ed_cache = (struct dma_pool*) &tmio->ed_pool; ++ ++ if ((retval = ohci_init (ohci)) < 0) ++ goto err_ohci_init; ++ ++ if ((retval = ohci_run (ohci)) < 0) ++ goto err_ohci_run; ++ ++ return 0; ++ ++err_ohci_run: ++ err ("can't start %s", hcd->self.bus_name); ++err_ohci_init: ++err_hcca_alloc: ++err_gen_pool_create: ++ tmio_stop (hcd); ++ return retval; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static inline void *tmio_urb_dma_to_virt(struct urb *urb, dma_addr_t dma) ++{ ++ if (BOUNDED_XYL(dma, urb->transfer_dma, urb->transfer_buffer_length)) ++ return urb->transfer_buffer + dma - urb->transfer_dma; ++ ++ if (BOUNDED_XYL(dma, urb->setup_dma, sizeof (struct usb_ctrlrequest))) ++ return urb->setup_packet + dma - urb->setup_dma; ++ ++ return NULL; ++} ++ ++static struct tmio_td* tmio_td_find (struct td *td, int *index) ++{ ++ struct urb* urb = td->urb; ++ urb_priv_t* urb_priv = urb->hcpriv; ++ struct tmio_urb* turb = urb_to_turb (urb); ++ int i; ++ ++ for (i=0; i < urb_priv->length; i++) ++ if (urb_priv->td[i] == td) { ++ *index = i; ++ return turb->td + i; ++ } ++ ++ return NULL; ++} ++ ++/* ++ * map the td's data to dma-able memory ++ * ++ * if this td transfers data, ++ * sets tmtd->data to the urb's data buffer ++ * sets tmtd->dma to dma-able memory ++ * sets tmtd->bounce to non-NULL if a bounce buffer is allocated ++ * copies the urb's data buffer to the bounce buffer if necessary ++ */ ++static int tmio_td_dma_map (struct ohci_hcd *ohci, struct urb *urb, ++ struct tmio_td *tmtd, int idx) ++{ ++ struct usb_hcd* hcd = ohci_to_hcd (ohci); ++ struct device* dev = hcd->self.controller; ++ dma_addr_t dma; ++ ++ if (!tmtd->len) ++ return 0; ++ ++ if (tmio_dma_to_virt (hcd, tmtd->dma)) ++ return 0; ++ ++ tmtd->data = tmio_urb_dma_to_virt (urb, tmtd->dma); ++ if (unlikely (!tmtd->data)) { ++ dev_err (dev, "TD has bad dma address 0x%08x\n", tmtd->dma); ++ return 0; ++ } ++ ++ tmtd->bounce = tmio_dma_alloc (dev, tmtd->len, &dma, GFP_ATOMIC); ++ if (!tmtd->bounce) ++ return -ENOMEM; ++ ++ if ((usb_pipecontrol (urb->pipe) && !idx) || usb_pipeout (urb->pipe)) { ++ consistent_sync (tmtd->bounce, tmtd->len, DMA_TO_DEVICE); ++ memcpy (tmtd->bounce, tmtd->data, tmtd->len); ++ } else ++ consistent_sync (tmtd->bounce, tmtd->len, DMA_FROM_DEVICE); ++ ++ tmtd->dma = dma; ++ return 0; ++} ++ ++/* ++ * unmaps the td's data from dma-able memory ++ * ++ * if a bounce buffer has been allocated, ++ * copy the bounce buffer to the urb's data buffer if necessary ++ * free the bounce buffer ++ */ ++static void tmio_td_dma_unmap (struct ohci_hcd *ohci, struct td *td) ++{ ++ struct device* dev = (ohci_to_hcd (ohci))->self.controller; ++ struct urb* urb = td->urb; ++ int idx; ++ struct tmio_td* tmtd = tmio_td_find (td, &idx); ++ ++ if (!tmtd->bounce) ++ return; ++ ++ if (usb_pipein (urb->pipe) && (usb_pipecontrol (urb->pipe) ? idx : 1)) { ++ memcpy (tmtd->data, tmtd->bounce, tmtd->len); ++ consistent_sync (tmtd->data, tmtd->len, DMA_TO_DEVICE); ++ } ++ ++ tmio_dma_free (dev, tmtd->len, tmtd->bounce, tmtd->dma); ++} ++ ++static int tmio_urb_runqueue (struct ohci_hcd *ohci, struct urb *urb) ++{ ++ struct tmio_urb* turb = urb_to_turb (urb); ++ urb_priv_t* urb_priv= urb->hcpriv; ++ int start = turb->td_queue; ++ int retval = 0; ++ int i; ++ ++ for (i = start; i < turb->td_add; i = ++turb->td_queue) { ++ struct tmio_td *tmtd = turb->td + i; ++ ++ if ((retval = tmio_td_dma_map (ohci, urb, tmtd, i))) ++ break; ++ ++ td_fill (ohci, tmtd->info, tmtd->dma, tmtd->len, urb, i); ++ } ++ ++ if (i <= start) ++ return retval; ++ ++ /* kickstart the appropriate list */ ++ wmb (); ++ switch (urb_priv->ed->type) { ++ case PIPE_BULK: ++ ohci_writel (ohci, OHCI_BLF, &ohci->regs->cmdstatus); ++ break; ++ case PIPE_CONTROL: ++ ohci_writel (ohci, OHCI_CLF, &ohci->regs->cmdstatus); ++ break; ++ } ++ ++ return retval; ++} ++ ++/* ++ * This needs to be called with ohci->lock held so the pending urb list ++ * isn't modified. ++ */ ++static int tmio_ohci_runqueue (struct ohci_hcd *ohci) ++{ ++ urb_priv_t* priv; ++ int retval = 0; ++ ++ list_for_each_entry_reverse (priv, &ohci->pending, pending) ++ if ((retval = tmio_urb_runqueue (ohci, priv->td[0]->urb))) ++ return retval; ++ ++ return retval; ++} ++ ++static void tmio_td_fill (struct ohci_hcd *ohci, u32 info, ++ dma_addr_t data, int len, struct urb *urb, int index) ++{ ++ struct tmio_urb* turb = urb_to_turb (urb); ++ struct tmio_td* tmtd = turb->td + index; ++ ++ tmtd->data = NULL; ++ tmtd->bounce = NULL; ++ tmtd->dma = data; ++ tmtd->len = len; ++ tmtd->info = info; ++ turb->td_add = index + 1; ++} ++ ++static void ++tmio_td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td) ++{ ++ tmio_td_dma_unmap (ohci, td); ++ td_done (ohci, urb, td); ++} ++ ++const static struct ohci_ops tmio_ops = { ++ .dma_pool_alloc = tmio_dma_pool_alloc, ++ .dma_pool_free = tmio_dma_pool_free, ++ .td_fill = tmio_td_fill, ++ .td_done = tmio_td_done, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static irqreturn_t tmio_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) ++{ ++ irqreturn_t retval = ohci_irq (hcd, ptregs); ++ ++ if (retval == IRQ_HANDLED) { ++ struct ohci_hcd *ohci = hcd_to_ohci (hcd); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ohci->lock, flags); ++ tmio_ohci_runqueue (ohci); ++ spin_unlock_irqrestore (&ohci->lock, flags); ++ } ++ ++ return retval; ++} ++ ++/* ++ * This is ohci_urb_enqueue with: ++ * dma address sanitization for tmio_urb_dma_to_virt() ++ * allocate extra space in urb_priv for our private data ++ * initialize urb_priv->td[0]->urb for tmio_ohci_runqueue() ++ * call tmio_ohci_runqueue() after submitting TDs ++ */ ++static int tmio_urb_enqueue ( ++ struct usb_hcd *hcd, ++ struct usb_host_endpoint *ep, ++ struct urb *urb, ++ gfp_t mem_flags ++) { ++ struct ohci_hcd *ohci = hcd_to_ohci (hcd); ++ struct ed *ed; ++ urb_priv_t *urb_priv; ++ unsigned int pipe = urb->pipe; ++ int i, size = 0; ++ unsigned long flags; ++ int retval = 0; ++ ++#ifdef OHCI_VERBOSE_DEBUG ++ urb_print (urb, "SUB", usb_pipein (pipe)); ++#endif ++ ++ /* make sure we can convert dma offsets back to virtual addresses */ ++ if (!tmio_dma_to_virt (hcd, urb->setup_dma)) ++ urb->setup_dma = 0; ++ ++ if (!tmio_dma_to_virt (hcd, urb->transfer_dma)) ++ urb->transfer_dma = sizeof (struct usb_ctrlrequest); ++ ++ /* every endpoint has a ed, locate and maybe (re)initialize it */ ++ if (! (ed = ed_get (ohci, ep, urb->dev, pipe, urb->interval))) ++ return -ENOMEM; ++ ++ /* for the private part of the URB we need the number of TDs (size) */ ++ switch (ed->type) { ++ case PIPE_CONTROL: ++ /* td_submit_urb() doesn't yet handle these */ ++ if (urb->transfer_buffer_length > 4096) ++ return -EMSGSIZE; ++ ++ /* 1 TD for setup, 1 for ACK, plus ... */ ++ size = 2; ++ /* FALLTHROUGH */ ++ // case PIPE_INTERRUPT: ++ // case PIPE_BULK: ++ default: ++ /* one TD for every 4096 Bytes (can be upto 8K) */ ++ size += urb->transfer_buffer_length / 4096; ++ /* ... and for any remaining bytes ... */ ++ if ((urb->transfer_buffer_length % 4096) != 0) ++ size++; ++ /* ... and maybe a zero length packet to wrap it up */ ++ if (size == 0) ++ size++; ++ else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0 ++ && (urb->transfer_buffer_length ++ % usb_maxpacket (urb->dev, pipe, ++ usb_pipeout (pipe))) == 0) ++ size++; ++ break; ++ case PIPE_ISOCHRONOUS: /* number of packets from URB */ ++ size = urb->number_of_packets; ++ break; ++ } ++ ++ /* allocate the private part of the URB */ ++ urb_priv = kzalloc (sizeof (urb_priv_t) ++ + size * sizeof (struct td*) ++ + sizeof (struct tmio_urb) ++ + size * sizeof (struct tmio_td), ++ mem_flags); ++ if (!urb_priv) ++ return -ENOMEM; ++ INIT_LIST_HEAD (&urb_priv->pending); ++ urb_priv->length = size; ++ urb_priv->ed = ed; ++ ++ /* allocate the TDs (deferring hash chain updates) */ ++ for (i = 0; i < size; i++) { ++ urb_priv->td [i] = td_alloc (ohci, mem_flags); ++ if (!urb_priv->td [i]) { ++ urb_priv->length = i; ++ urb_free_priv (ohci, urb_priv); ++ return -ENOMEM; ++ } ++ urb_priv->td [i]->urb = urb; ++ } ++ ++ spin_lock_irqsave (&ohci->lock, flags); ++ ++ /* don't submit to a dead HC */ ++ if (!HC_IS_RUNNING(hcd->state)) { ++ retval = -ENODEV; ++ goto fail; ++ } ++ ++ /* in case of unlink-during-submit */ ++ spin_lock (&urb->lock); ++ if (urb->status != -EINPROGRESS) { ++ spin_unlock (&urb->lock); ++ urb->hcpriv = urb_priv; ++ finish_urb (ohci, urb, NULL); ++ retval = 0; ++ goto fail; ++ } ++ ++ /* schedule the ed if needed */ ++ if (ed->state == ED_IDLE) { ++ retval = ed_schedule (ohci, ed); ++ if (retval < 0) ++ goto fail0; ++ if (ed->type == PIPE_ISOCHRONOUS) { ++ u16 frame = ohci_frame_no(ohci); ++ ++ /* delay a few frames before the first TD */ ++ frame += max_t (u16, 8, ed->interval); ++ frame &= ~(ed->interval - 1); ++ frame |= ed->branch; ++ urb->start_frame = frame; ++ ++ /* yes, only URB_ISO_ASAP is supported, and ++ * urb->start_frame is never used as input. ++ */ ++ } ++ } else if (ed->type == PIPE_ISOCHRONOUS) ++ urb->start_frame = ed->last_iso + ed->interval; ++ ++ /* fill the TDs and link them to the ed; and ++ * enable that part of the schedule, if needed ++ * and update count of queued periodic urbs ++ */ ++ urb->hcpriv = urb_priv; ++ td_submit_urb (ohci, urb); ++ tmio_ohci_runqueue (ohci); ++ ++fail0: ++ spin_unlock (&urb->lock); ++fail: ++ if (retval) ++ urb_free_priv (ohci, urb_priv); ++ spin_unlock_irqrestore (&ohci->lock, flags); ++ return retval; ++} ++ ++static const struct hc_driver tmio_hc_driver = { ++ .description = hcd_name, ++ .product_desc = "TMIO OHCI USB Host Controller", ++ .hcd_priv_size = sizeof (struct ohci_hcd) ++ + sizeof (struct tmio_hcd), ++ ++ /* generic hardware linkage */ ++ .irq = tmio_irq, ++ .flags = HCD_USB11 | HCD_MEMORY, ++ ++ /* basic lifecycle operations */ ++ .start = tmio_start, ++ .stop = tmio_stop, ++ ++ /* managing i/o requests and associated device resources */ ++ .urb_enqueue = tmio_urb_enqueue, ++ .urb_dequeue = ohci_urb_dequeue, ++ .endpoint_disable = ohci_endpoint_disable, ++ ++ /* scheduling support */ ++ .get_frame_number = ohci_get_frame, ++ ++ /* root hub support */ ++ .hub_status_data = ohci_hub_status_data, ++ .hub_control = ohci_hub_control, ++#ifdef CONFIG_PM ++ .bus_suspend = ohci_bus_suspend, ++ .bus_resume = ohci_bus_resume, ++#endif ++ .start_port_reset = ohci_start_port_reset, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* configure so an HC device and id are always provided */ ++/* always called with process context; sleeping is OK */ ++ ++/** ++ * tmio_probe - initialize TMIO-based HCDs ++ * Context: !in_interrupt() ++ * ++ * Allocates basic resources for this USB host controller, and ++ * then invokes the start() method for the HCD associated with it ++ * through the hotplug entry's driver_data. ++ */ ++static int tmio_probe (struct device *dev) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct resource* config = tmio_resource_config (dev); ++ struct resource* regs = tmio_resource_control (dev); ++ struct resource* sram = tmio_resource_mem (dev); ++ struct resource* irq = tmio_resource_irq (dev); ++ struct usb_operations* ops; ++ struct tmio_hcd* tmio; ++ struct ohci_hcd* ohci; ++ struct usb_hcd* hcd; ++ int retval; ++ ++ if (usb_disabled ()) ++ return -ENODEV; ++ ++ if (dev->dma_mask || dev->coherent_dma_mask) { ++ dev_err (dev, "DMA not supported\n"); ++ return -ENODEV; ++ } ++ ++ hcd = usb_create_hcd (&tmio_hc_driver, dev, dev->bus_id); ++ if (!hcd) { ++ retval = -ENOMEM; ++ goto err_create_hcd; ++ } ++ ++ tmio = hcd_to_tmio (hcd); ++ tmio->td_pool.dev = dev; ++ tmio->ed_pool.dev = dev; ++ tmio->td_pool.size = sizeof (struct td); ++ tmio->ed_pool.size = sizeof (struct ed); ++ ohci = hcd_to_ohci (hcd); ++ ohci_hcd_init (ohci); ++ ohci->ops = &tmio_ops; ++ ++ retval = request_resource (tdev->iomem, config); ++ if (retval) ++ goto err_request_config_resource; ++ ++ retval = request_resource (tdev->iomem, regs); ++ if (retval) ++ goto err_request_regs_resource; ++ ++ retval = request_resource (tdev->iomem, sram); ++ if (retval) ++ goto err_request_sram_resource; ++ ++ hcd->rsrc_start = regs->start; ++ hcd->rsrc_len = regs->end - regs->start + 1; ++ tmio->sram_len = sram->end - sram->start + 1; ++ ++ tmio->ccr = ioremap (config->start, config->end - config->start + 1); ++ if (!tmio->ccr) { ++ retval = -ENOMEM; ++ goto err_ioremap_ccr; ++ } ++ ++ hcd->regs = ioremap (hcd->rsrc_start, hcd->rsrc_len); ++ if (!hcd->regs) { ++ retval = -ENOMEM; ++ goto err_ioremap_regs; ++ } ++ ++ tmio->sram = ioremap (sram->start, tmio->sram_len); ++ if (!tmio->sram) { ++ retval = -ENOMEM; ++ goto err_ioremap_sram; ++ } ++ ++ /* drivers should use our coherent buffer allocator */ ++ ops = &tmio->ops; ++ memcpy (ops, hcd->self.op, sizeof *ops); ++ ops->buffer_alloc = tmio_buffer_alloc; ++ ops->buffer_free = tmio_buffer_free; ++ hcd->self.op = ops; ++ ++ retval = usb_add_hcd (hcd, irq->start, SA_INTERRUPT); ++ if (retval) ++ goto err_usb_add_hcd; ++ ++ return 0; ++ ++err_usb_add_hcd: ++ iounmap (tmio->sram); ++err_ioremap_sram: ++ iounmap (hcd->regs); ++err_ioremap_regs: ++ iounmap (tmio->ccr); ++err_ioremap_ccr: ++ release_resource (sram); ++err_request_sram_resource: ++ release_resource (regs); ++err_request_regs_resource: ++ release_resource (config); ++err_request_config_resource: ++ usb_put_hcd (hcd); ++err_create_hcd: ++ return retval; ++} ++ ++/* may be called without controller electrically present */ ++/* may be called with controller, bus, and devices active */ ++ ++/** ++ * tmio_remove - shutdown processing for TMIO-based HCDs ++ * @dev: USB Host Controller being removed ++ * Context: !in_interrupt() ++ * ++ * Reverses the effect of tmio_probe(), first invoking ++ * the HCD's stop() method. It is always called from a thread ++ * context, normally "rmmod", "apmd", or something similar. ++ */ ++static int tmio_remove (struct device *dev) ++{ ++ struct usb_hcd* hcd = dev_get_drvdata (dev); ++ struct tmio_hcd* tmio = hcd_to_tmio (hcd); ++ ++ usb_remove_hcd (hcd); ++ iounmap (tmio->sram); ++ iounmap (hcd->regs); ++ iounmap (tmio->ccr); ++ release_resource (tmio_resource_mem (dev)); ++ release_resource (tmio_resource_control (dev)); ++ release_resource (tmio_resource_config (dev)); ++ usb_put_hcd (hcd); ++ return 0; ++} ++ ++static struct device_driver tmio_ohci = { ++ .name = TMIO_NAME_OHCI, ++ .bus = &tmio_bus_type, ++ .probe = tmio_probe, ++ .remove = tmio_remove, ++}; ++ ++static int __init tmio_init (void) ++{ ++ dbg (DRIVER_INFO " (%s)", TMIO_SOC_NAME); ++ dbg ("block sizes: ed %d td %d", ++ sizeof (struct ed), sizeof (struct td)); ++ ++ return driver_register (&tmio_ohci); ++} ++ ++static void __exit tmio_exit (void) ++{ ++ driver_unregister (&tmio_ohci); ++} ++ ++module_init (tmio_init); ++module_exit (tmio_exit); +Index: git/drivers/usb/host/Kconfig +=================================================================== +--- git.orig/drivers/usb/host/Kconfig 2006-11-07 21:46:32.000000000 +0000 ++++ git/drivers/usb/host/Kconfig 2006-11-07 21:48:38.000000000 +0000 +@@ -84,6 +84,7 @@ config USB_OHCI_HCD + depends on USB && USB_ARCH_HAS_OHCI + select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 + select I2C if ARCH_PNX4008 ++ select GENERIC_ALLOCATOR if TOSHIBA_TC6393XB + ---help--- + The Open Host Controller Interface (OHCI) is a standard for accessing + USB 1.1 host controller hardware. It does more in hardware than Intel's +Index: git/drivers/usb/host/ohci-hcd.c +=================================================================== +--- git.orig/drivers/usb/host/ohci-hcd.c 2006-11-07 21:46:32.000000000 +0000 ++++ git/drivers/usb/host/ohci-hcd.c 2006-11-07 21:48:33.000000000 +0000 +@@ -944,6 +944,7 @@ MODULE_LICENSE ("GPL"); + || defined(CONFIG_ARCH_OMAP) \ + || defined (CONFIG_ARCH_LH7A404) \ + || defined (CONFIG_PXA27x) \ ++ || defined (CONFIG_TOSHIBA_TC6393XB) \ + || defined (CONFIG_ARCH_EP93XX) \ + || defined (CONFIG_SOC_AU1X00) \ + || defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \ diff --git a/packages/linux/linux-rp-2.6.22/tmio-tc6393-r8.patch b/packages/linux/linux-rp-2.6.22/tmio-tc6393-r8.patch new file mode 100644 index 0000000000..1bfdc23630 --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/tmio-tc6393-r8.patch @@ -0,0 +1,800 @@ + arch/arm/common/Kconfig | 3 + arch/arm/common/Makefile | 1 + arch/arm/common/tc6393xb.c | 668 ++++++++++++++++++++++++++++++++++++++++ + arch/arm/mach-pxa/Kconfig | 1 + include/asm-arm/arch-pxa/irqs.h | 10 + include/asm-arm/hardware/tmio.h | 44 ++ + 6 files changed, 727 insertions(+) + +Index: git/arch/arm/common/tc6393xb.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ git/arch/arm/common/tc6393xb.c 2006-11-07 22:14:49.000000000 +0000 +@@ -0,0 +1,668 @@ ++/* ++ * Toshiba TC6393XB SoC support ++ * ++ * Maintainer: Chris Humbert <mahadri-kernel@drigon.com> ++ * ++ * Copyright (c) 2005-2006 Chris Humbert ++ * Copyright (c) 2005 Dirk Opfer ++ * ++ * Based on code written by Sharp/Lineo for 2.4 kernels ++ * Based on locomo.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/errno.h> ++#include <linux/ioport.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <linux/fb.h> ++ ++#include <asm/hardware.h> ++#include <asm/mach-types.h> ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <asm/mach/irq.h> ++#include <asm/arch/irqs.h> ++#include <asm/hardware/tmio.h> ++ ++#ifndef TMIO_SOC_TC6393XB ++#error "TC6393XB SoC not configured" ++#endif ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* cell ids must be 0-based because they are used as array indexes. */ ++#define TC6393_CELL_NAND 0 ++#define TC6393_CELL_SD 1 ++#define TC6393_CELL_OHCI 2 ++#define TC6393_CELL_SERIAL 3 ++#define TC6393_CELL_LCD 4 ++#define TC6393_NUM_CELLS 5 ++ ++#define TC6393_RESOURCE(_name, _start, _end, _flags) \ ++ { \ ++ .name = _name, \ ++ .start = _start, \ ++ .end = _end, \ ++ .flags = _flags, \ ++ } ++ ++#define TC6393_MEM(name, start, size) \ ++ TC6393_RESOURCE(name, start, (start) + (size) - 1, IORESOURCE_MEM) ++ ++#define TC6393_IRQ(name, irq) \ ++ TC6393_RESOURCE(name, irq, irq, IORESOURCE_IRQ) ++ ++const static struct resource tc6393_NAND_resource[] = { ++ TC6393_MEM (TMIO_NAME_NAND, 0x000100, 0x100), ++ TC6393_MEM (TMIO_NAME_NAND, 0x001000, 0x008), ++ TC6393_MEM (TMIO_NAME_NAND, 0, 0), ++ TC6393_IRQ (TMIO_NAME_NAND, IRQ_TC6393_NAND), ++}; ++ ++const static struct resource tc6393_SD_resource[] = { ++ TC6393_MEM (TMIO_NAME_SD, 0x000200, 0x100), ++ TC6393_MEM (TMIO_NAME_SD, 0x002000, 0x200), ++ TC6393_MEM (TMIO_NAME_SD, 0, 0), ++ TC6393_IRQ (TMIO_NAME_SD, IRQ_TC6393_SD), ++}; ++ ++const static struct resource tc6393_OHCI_resource[] = { ++ TC6393_MEM (TMIO_NAME_OHCI, 0x000300, 0x100), ++ TC6393_MEM (TMIO_NAME_OHCI, 0x003000, 0x100), ++ TC6393_MEM (TMIO_NAME_OHCI, 0x010000, 32 * 1024), ++ TC6393_IRQ (TMIO_NAME_OHCI, IRQ_TC6393_OHCI), ++}; ++ ++const static struct resource tc6393_SERIAL_resource[] = { ++ TC6393_MEM (TMIO_NAME_SERIAL, 0x000400, 0x100), ++ TC6393_MEM (TMIO_NAME_SERIAL, 0x004000, 0x100), ++ TC6393_MEM (TMIO_NAME_SERIAL, 0, 0), ++ TC6393_IRQ (TMIO_NAME_SERIAL, IRQ_TC6393_SERIAL), ++}; ++ ++const static struct resource tc6393_LCD_resource[] = { ++ TC6393_MEM (TMIO_NAME_LCD, 0x000500, 0x100), ++ TC6393_MEM (TMIO_NAME_LCD, 0x005000, 0x200), ++ TC6393_MEM (TMIO_NAME_LCD, 0x100000, 1024 * 1024), ++ TC6393_IRQ (TMIO_NAME_LCD, IRQ_TC6393_LCD), ++}; ++ ++#define TC6393_CELL(_NAME) \ ++ [TC6393_CELL_##_NAME] = { \ ++ .name = TMIO_NAME_##_NAME, \ ++ .id = TC6393_CELL_##_NAME, \ ++ .resource = tc6393_##_NAME##_resource, \ ++ .num_resources = ARRAY_SIZE (tc6393_##_NAME##_resource), \ ++ } ++ ++struct tc6393_cell { ++ const char* name; ++ unsigned int id; ++ const struct resource* resource; ++ unsigned int num_resources; ++}; ++ ++const static struct tc6393_cell tc6393_cell [TC6393_NUM_CELLS] = { ++ TC6393_CELL (NAND ), ++ TC6393_CELL (SD ), ++ TC6393_CELL (OHCI ), ++ TC6393_CELL (SERIAL ), ++ TC6393_CELL (LCD ), ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* ++ * TC6393 System Configuration Register ++ */ ++struct tc6393_scr { ++ u8 x00[8]; ++ u8 revid; /* 0x08 Revision ID */ ++ u8 x01[0x47]; ++ u8 isr; /* 0x50 Interrupt Status */ ++ u8 x02; ++ u8 imr; /* 0x52 Interrupt Mask */ ++ u8 x03; ++ u8 irr; /* 0x54 Interrupt Routing */ ++ u8 x04[0x0b]; ++ u16 gper; /* 0x60 GP Enable */ ++ u8 x05[2]; ++ u16 gpi_sr[2]; /* 0x64 GPI Status */ ++ u16 gpi_imr[2]; /* 0x68 GPI INT Mask */ ++ u16 gpi_eder[2]; /* 0x6c GPI Edge Detect Enable */ ++ u16 gpi_lir[4]; /* 0x70 GPI Level Invert */ ++ u16 gpo_dsr[2]; /* 0x78 GPO Data Set */ ++ u16 gpo_doecr[2]; /* 0x7c GPO Data OE Control */ ++ u16 gp_iarcr[2]; /* 0x80 GP Internal Active Reg Control */ ++ u16 gp_iarlcr[2]; /* 0x84 GP Internal Active Reg Level Con*/ ++ u8 gpi_bcr[4]; /* 0x88 GPI Buffer Control */ ++ u16 gpa_iarcr; /* 0x8c GPa Internal Active Reg Control */ ++ u8 x06[2]; ++ u16 gpa_iarlcr; /* 0x90 GPa Internal Active Reg Level Co*/ ++ u8 x07[2]; ++ u16 gpa_bcr; /* 0x94 GPa Buffer Control */ ++ u8 x08[2]; ++ u16 ccr; /* 0x98 Clock Control */ ++ u16 pll2cr; /* 0x9a PLL2 Control */ ++ u16 pll1cr[2]; /* 0x9c PLL1 Control */ ++ u8 diarcr; /* 0xa0 Device Internal Active Reg Contr*/ ++ u8 dbocr; /* 0xa1 Device Buffer Off Control */ ++ u8 x09[0x3e]; ++ u8 fer; /* 0xe0 Function Enable */ ++ u8 x10[3]; ++ u16 mcr; /* 0xe4 Mode Control */ ++ u8 x11[0x14]; ++ u8 config; /* 0xfc Configuration Control */ ++ u8 x12[2]; ++ u8 debug; /* 0xff Debug */ ++} __attribute__ ((packed)); ++ ++union tc6393_scr_fer { ++ u8 raw; ++struct { ++ unsigned usben:1; /* D0 USB enable */ ++ unsigned lcdcven:1; /* D1 polysylicon TFT enable */ ++ unsigned slcden:1; /* D2 SLCD enable */ ++} __attribute__ ((packed)); ++} __attribute__ ((packed)); ++ ++union tc6393_scr_ccr { ++ u16 raw; ++struct { ++ unsigned ck32ken:1; /* D0 SD host clock enable */ ++ unsigned usbcken:1; /* D1 USB host clock enable */ ++ unsigned x00:2; ++ unsigned sharp:1; /* D4 ??? set in Sharp's code */ ++ unsigned x01:3; ++ enum { disable = 0, ++ m12MHz = 1, ++ m24MHz = 2, ++ m48MHz = 3, ++ } mclksel:3; /* D10-D8 LCD controller clock */ ++ unsigned x02:1; ++ enum { h24MHz = 0, ++ h48MHz = 1, ++ } hclksel:2; /* D13-D12 host bus clock */ ++ unsigned x03:2; ++} __attribute__ ((packed)); ++} __attribute__ ((packed)); ++ ++/*--------------------------------------------------------------------------*/ ++ ++struct tc6393 { ++ spinlock_t lock; /* read-modify-write lock */ ++ struct device* dev; /* TC6393 device */ ++ struct tc6393_scr __iomem *scr; /* system configuration reg */ ++ ++ struct resource rscr; /* system config reg resource */ ++ struct resource* iomem; /* entire TC6393 iomem resource */ ++ unsigned int irq; /* hardware cascade irq */ ++ ++ struct tmio_device tdev [TC6393_NUM_CELLS]; ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++static u32 tc6393_ioread32 (const void __iomem *addr) ++{ ++ return ((u32) ioread16 (addr)) | (((u32) ioread16 (addr + 2)) << 16); ++} ++ ++static u32 tc6393_iowrite32 (u32 val, const void __iomem *addr) ++{ ++ iowrite16 (val, addr); ++ iowrite16 (val >> 16, addr + 2); ++ return val; ++} ++ ++u32 get_tc6393_gpio (struct device *dev) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ ++ return tc6393_ioread32 (scr->gpo_dsr); ++} ++EXPORT_SYMBOL (get_tc6393_gpio); ++ ++u32 set_tc6393_gpio (struct device *dev, u32 bits) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned long flags; ++ u32 dsr; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ dsr = tc6393_ioread32 (scr->gpo_dsr) | bits; ++ tc6393_iowrite32 (dsr, scr->gpo_dsr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++ ++ return dsr; ++} ++EXPORT_SYMBOL (set_tc6393_gpio); ++ ++u32 reset_tc6393_gpio (struct device *dev, u32 bits) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned long flags; ++ u32 dsr; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ dsr = tc6393_ioread32 (scr->gpo_dsr) & ~bits; ++ tc6393_iowrite32 (dsr, scr->gpo_dsr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++ ++ return dsr; ++} ++EXPORT_SYMBOL (reset_tc6393_gpio); ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void ++tc6393_irq (unsigned int irq, struct irq_desc *desc) ++{ ++ struct tc6393* tc6393 = get_irq_chip_data (irq); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned int isr; ++ unsigned int bit; ++ unsigned int i; ++ ++ desc->chip->ack (irq); ++ ++ while ((isr = ioread8(&scr->isr) & ~ioread8(&scr->imr))) ++ for (bit = 1, i = IRQ_TC6393_START; i <= IRQ_TC6393_LCD; ++ bit <<= 1, i++) ++ if (isr & bit) ++ desc_handle_irq (i, irq_desc + i); ++} ++ ++static void tc6393_irq_ack (unsigned int irq) ++{ ++} ++ ++static void tc6393_irq_mask (unsigned int irq) ++{ ++ struct tc6393* tc6393 = get_irq_chip_data (irq); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned long flags; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ iowrite8 (ioread8 (&scr->imr) | (1 << (irq - IRQ_TC6393_START)), ++ &scr->imr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++} ++ ++static void tc6393_irq_unmask (unsigned int irq) ++{ ++ struct tc6393* tc6393 = get_irq_chip_data (irq); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned long flags; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ iowrite8 (ioread8 (&scr->imr) & ~(1 << (irq - IRQ_TC6393_START)), ++ &scr->imr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++} ++ ++static struct irq_chip tc6393_chip = { ++ .ack = tc6393_irq_ack, ++ .mask = tc6393_irq_mask, ++ .unmask = tc6393_irq_unmask, ++}; ++ ++static void tc6393_attach_irq (struct tc6393 *tc6393) ++{ ++ unsigned int irq; ++ ++ for (irq = IRQ_TC6393_START; irq <= IRQ_TC6393_LCD; irq++) { ++ set_irq_chip (irq, &tc6393_chip); ++ set_irq_chip_data(irq, tc6393); ++ set_irq_handler (irq, handle_edge_irq); ++ set_irq_flags (irq, IRQF_VALID | IRQF_PROBE); ++ } ++ ++ set_irq_type (tc6393->irq, IRQT_FALLING); ++ set_irq_chip_data (tc6393->irq, tc6393); ++ set_irq_chained_handler (tc6393->irq, tc6393_irq); ++} ++ ++static void tc6393_detach_irq (struct tc6393 *tc6393) ++{ ++ unsigned int irq; ++ ++ set_irq_chained_handler (tc6393->irq, NULL); ++ set_irq_chip_data (tc6393->irq, NULL); ++ ++ for (irq = IRQ_TC6393_START; irq <= IRQ_TC6393_LCD; irq++) { ++ set_irq_flags (irq, 0); ++ set_irq_chip (irq, NULL); ++ set_irq_chip_data(irq, NULL); ++ } ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++static int tc6393_bus_match (struct device *dev, struct device_driver *drv) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ const struct tc6393_cell* cell = tdev->soc_data; ++ ++ return !strcmp (cell->name, drv->name); ++} ++ ++static int tc6393_bus_suspend (struct device *dev, pm_message_t state) ++{ ++ struct device_driver* drv = dev->driver; ++ return drv && drv->suspend ? drv->suspend (dev, state) : 0; ++} ++ ++static int tc6393_bus_resume (struct device *dev) ++{ ++ struct device_driver* drv = dev->driver; ++ return drv && drv->resume ? drv->resume (dev) : 0; ++} ++ ++struct bus_type tc6393_bus_type = { ++ .name = TMIO_NAME_BUS, ++ .match = tc6393_bus_match, ++ .suspend = tc6393_bus_suspend, ++ .resume = tc6393_bus_resume, ++}; ++EXPORT_SYMBOL (tc6393_bus_type); ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void tc6393_cell_clock (struct device *dev, int enable) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ const struct tc6393_cell* cell = tdev->soc_data; ++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ union tc6393_scr_ccr ccr; ++ unsigned long flags; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ ccr.raw = ioread16 (&scr->ccr); ++ ++ switch (cell->id) { ++ case TC6393_CELL_SD: ccr.ck32ken = enable; break; ++ case TC6393_CELL_OHCI: ccr.usbcken = enable; break; ++ case TC6393_CELL_LCD: ++ ccr.mclksel = enable ? m48MHz : disable; ++ break; ++ } ++ ++ printk (KERN_DEBUG TMIO_NAME_CORE ": scr->ccr = %04x\n", ccr.raw); ++ ++ iowrite16(ccr.raw, &scr->ccr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++} ++ ++static void tc6393_cell_function (struct device *dev, int enable) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ const struct tc6393_cell* cell = tdev->soc_data; ++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ union tc6393_scr_fer fer; ++ unsigned long flags; ++ ++ if (cell->id == TC6393_CELL_NAND) { ++ if (enable) { ++ /* SMD buffer on */ ++ printk (KERN_DEBUG TMIO_NAME_CORE ": SMD buffer on\n"); ++ iowrite8 (0xff, scr->gpi_bcr + 1); ++ } ++ return; ++ } ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ fer.raw = ioread16 (&scr->fer); ++ ++ switch (cell->id) { ++ case TC6393_CELL_OHCI: fer.usben = enable; break; ++ case TC6393_CELL_LCD: fer.slcden = enable; break; ++ } ++ ++ printk (KERN_DEBUG TMIO_NAME_CORE ": scr->fer = %02x\n", fer.raw); ++ ++ iowrite8 (fer.raw, &scr->fer); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++} ++ ++static void ++tc6393_lcd_mode (struct device *dev, const struct fb_videomode *mode) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ ++ iowrite16 (mode->pixclock, scr->pll1cr + 0); ++ iowrite16 (mode->pixclock >> 16, scr->pll1cr + 1); ++} ++ ++static struct tmio_cell_ops tc6393_cell_ops = { ++ .clock = tc6393_cell_clock, ++ .function = tc6393_cell_function, ++ .lcd_mode = tc6393_lcd_mode, ++}; ++ ++static void tc6393_device_release (struct device *dev) ++{ ++} ++ ++static int ++tc6393_device_register (struct tc6393 *tc6393, struct tmio_cell *tcell) ++{ ++ const struct tc6393_cell* cell; ++ struct tmio_device* tdev; ++ struct device* dev; ++ int i; ++ ++ for (i = 0; strcmp (tcell->name, tc6393_cell [i].name); ) ++ if (++i >= ARRAY_SIZE(tc6393_cell)) ++ return -EINVAL; ++ ++ cell = tc6393_cell + i; ++ tdev = tc6393->tdev + i; ++ dev = &tdev->dev; ++ ++ tdev->ops = &tc6393_cell_ops; ++ tdev->iomem = tc6393->iomem; ++ tdev->soc_data = (void*) cell; ++ ++ dev->parent = tc6393->dev; ++ strncpy (dev->bus_id, cell->name, sizeof dev->bus_id); ++ dev->bus = &tc6393_bus_type; ++ dev->dma_mask = tc6393->dev->dma_mask; ++ dev->coherent_dma_mask = tc6393->dev->coherent_dma_mask; ++ dev->release = tc6393_device_release; ++ dev->platform_data = tcell->platform_data; ++ ++ for (i=0; i < cell->num_resources; i++) { ++ const struct resource* cr = cell->resource + i; ++ struct resource* dr = tdev->resource + i; ++ ++ dr->name = cr->name; ++ dr->start = cr->start; ++ dr->end = cr->end; ++ dr->flags = cr->flags; ++ ++ /* convert memory offsets to absolutes */ ++ if (cr->flags & IORESOURCE_MEM) { ++ dr->start += tc6393->iomem->start; ++ dr->end += tc6393->iomem->start; ++ } ++ } ++ ++ return device_register (dev); ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void tc6393_hw_init (struct tc6393 *tc6393) ++{ ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ struct tc6393_platform_data* tcpd = tc6393->dev->platform_data; ++ ++ tcpd->enable (tc6393->dev); ++ ++ iowrite8 (0, &scr->fer); ++ iowrite16(tcpd->scr_pll2cr, &scr->pll2cr); ++ iowrite16(tcpd->scr_ccr, &scr->ccr); ++ iowrite16(tcpd->scr_mcr, &scr->mcr); ++ iowrite16(tcpd->scr_gper, &scr->gper); ++ iowrite8 (0, &scr->irr); ++ iowrite8 (0xbf, &scr->imr); ++ iowrite16(tcpd->scr_gpo_dsr, scr->gpo_dsr + 0); ++ iowrite16(tcpd->scr_gpo_dsr >> 16, scr->gpo_dsr + 1); ++ iowrite16(tcpd->scr_gpo_doecr, scr->gpo_doecr + 0); ++ iowrite16(tcpd->scr_gpo_doecr >> 16, scr->gpo_doecr + 1); ++} ++ ++static int tc6393_probe (struct device *dev) ++{ ++ struct platform_device* pdev = to_platform_device (dev); ++ struct tc6393_platform_data* tcpd = dev->platform_data; ++ struct tc6393* tc6393; ++ struct resource* iomem; ++ struct resource* rscr; ++ int retval; ++ int i; ++ ++ iomem = platform_get_resource (pdev, IORESOURCE_MEM, 0); ++ if (!iomem) ++ return -EINVAL; ++ ++ tc6393 = kzalloc (sizeof *tc6393, GFP_KERNEL); ++ if (!tc6393) { ++ retval = -ENOMEM; ++ goto err_kzalloc; ++ } ++ ++ dev_set_drvdata (dev, tc6393); ++ spin_lock_init (&tc6393->lock); ++ tc6393->dev = dev; ++ tc6393->iomem = iomem; ++ tc6393->irq = platform_get_irq (pdev, 0); ++ ++ rscr = &tc6393->rscr; ++ rscr->name = TMIO_NAME_CORE; ++ rscr->start = iomem->start; ++ rscr->end = iomem->start + 0xff; ++ rscr->flags = IORESOURCE_MEM; ++ ++ retval = request_resource (iomem, rscr); ++ if (retval) ++ goto err_request_scr; ++ ++ tc6393->scr = ioremap (rscr->start, rscr->end - rscr->start + 1); ++ if (!tc6393->scr) { ++ retval = -ENOMEM; ++ goto err_ioremap; ++ } ++ ++ tc6393_hw_init (tc6393); ++ ++ printk (KERN_INFO "Toshiba %s revision %d at 0x%08lx, irq %d\n", ++ TMIO_SOC_NAME, ioread8 (&tc6393->scr->revid), ++ iomem->start, tc6393->irq); ++ ++ if (tc6393->irq) ++ tc6393_attach_irq (tc6393); ++ ++ for (i = 0; i < tcpd->num_cells; i++) ++ tc6393_device_register (tc6393, tcpd->cell + i); ++ ++ return 0; ++ ++err_ioremap: ++ release_resource (rscr); ++err_request_scr: ++ kfree(tc6393); ++err_kzalloc: ++ release_resource (iomem); ++ return retval; ++} ++ ++static int tc6393_dev_remove (struct device *dev, void *data) ++{ ++ device_unregister (dev); ++ return 0; ++} ++ ++static int tc6393_remove (struct device *dev) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ ++ device_for_each_child (dev, tc6393, tc6393_dev_remove); ++ ++ if (tc6393->irq) ++ tc6393_detach_irq (tc6393); ++ ++ iounmap (tc6393->scr); ++ release_resource (&tc6393->rscr); ++ release_resource (tc6393->iomem); ++ kfree (tc6393); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int tc6393_suspend (struct device *dev, pm_message_t state) ++{ ++ struct tc6393_platform_data* tcpd = dev->platform_data; ++ tcpd->disable (dev); ++ return 0; ++} ++ ++static int tc6393_resume (struct device *dev) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ tc6393_hw_init (tc6393); ++ return 0; ++} ++#endif ++ ++static struct device_driver tc6393_device_driver = { ++ .name = TMIO_SOC_NAME, ++ .bus = &platform_bus_type, ++ .probe = tc6393_probe, ++ .remove = tc6393_remove, ++#ifdef CONFIG_PM ++ .suspend = tc6393_suspend, ++ .resume = tc6393_resume, ++#endif ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++static int __init tc6393_init (void) ++{ ++ int retval = bus_register (&tc6393_bus_type); ++ if (retval) ++ return retval; ++ ++ return driver_register (&tc6393_device_driver); ++} ++ ++static void __exit tc6393_exit (void) ++{ ++ driver_unregister (&tc6393_device_driver); ++ bus_unregister (&tc6393_bus_type); ++} ++ ++module_init (tc6393_init); ++module_exit (tc6393_exit); ++ ++MODULE_DESCRIPTION ("TC6393 SoC bus driver"); ++MODULE_AUTHOR ("Chris Humbert, Dirk Opfer"); ++MODULE_LICENSE ("GPL"); +Index: git/arch/arm/common/Kconfig +=================================================================== +--- git.orig/arch/arm/common/Kconfig 2006-10-31 16:08:28.000000000 +0000 ++++ git/arch/arm/common/Kconfig 2006-11-07 22:13:09.000000000 +0000 +@@ -31,3 +31,6 @@ config SHARPSL_PM + + config SHARP_SCOOP + bool ++ ++config TOSHIBA_TC6393XB ++ bool +Index: git/arch/arm/mach-pxa/Kconfig +=================================================================== +--- git.orig/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:06.000000000 +0000 ++++ git/arch/arm/mach-pxa/ 2006-11-07 23:30:34.000000000 +0000 +@@ -128,6 +128,7 @@ config MACH_BORZOI + config MACH_TOSA + bool "Enable Sharp SL-6000x (Tosa) Support" + depends on PXA_SHARPSL_25x ++ select TOSHIBA_TC6393XB + + config PXA25x + bool +Index: git/arch/arm/common/Makefile +=================================================================== +--- git.orig/arch/arm/common/Makefile 2006-10-31 16:08:28.000000000 +0000 ++++ git/arch/arm/common/Makefile 2006-11-07 22:13:09.000000000 +0000 +@@ -17,3 +17,4 @@ obj-$(CONFIG_SHARPSL_PM) += sharpsl_pm.o + obj-$(CONFIG_SHARP_SCOOP) += scoop.o + obj-$(CONFIG_ARCH_IXP2000) += uengine.o + obj-$(CONFIG_ARCH_IXP23XX) += uengine.o ++obj-$(CONFIG_TOSHIBA_TC6393XB) += tc6393xb.o +Index: git/include/asm-arm/hardware/tmio.h +=================================================================== +--- git.orig/include/asm-arm/hardware/tmio.h 2006-11-07 22:13:09.000000000 +0000 ++++ git/include/asm-arm/hardware/tmio.h 2006-11-07 22:13:09.000000000 +0000 +@@ -91,6 +91,50 @@ struct tmio_device { + + /*--------------------------------------------------------------------------*/ + ++/* ++ * TC6393XB SoC ++ */ ++#ifdef CONFIG_TOSHIBA_TC6393XB ++#define TMIO_SOC_TC6393XB ++#define TMIO_SOC_NAME "TC6393XB" ++#define TMIO_NAME_BUS "tc6393-bus" ++#define TMIO_NAME_CORE "tc6393-core" ++#define TMIO_NAME_NAND "tc6393-nand" ++#define TMIO_NAME_SD "tc6393-sd" ++#define TMIO_NAME_OHCI "tc6393-ohci" ++#define TMIO_NAME_SERIAL "tc6393-serial" ++#define TMIO_NAME_LCD "tc6393-lcd" ++#define tmio_bus_type tc6393_bus_type ++ ++#define TC6393_GPIO(x) (1 << (x)) ++ ++extern struct bus_type tc6393_bus_type; ++ ++struct tc6393_platform_data { ++ u16 scr_pll2cr; /* PLL2 Control */ ++ u16 scr_ccr; /* Clock Control */ ++ u16 scr_mcr; /* Mode Control */ ++ u16 scr_gper; /* GP Enable */ ++ u32 scr_gpo_doecr; /* GPO Data OE Control */ ++ u32 scr_gpo_dsr; /* GPO Data Set */ ++ ++ /* cells to register as devices */ ++ struct tmio_cell* cell; ++ unsigned int num_cells; ++ ++ /* callbacks to enable and disable the TC6393XB's power and clock */ ++ void (*enable) (struct device *dev); ++ void (*disable) (struct device *dev); ++}; ++ ++u32 get_tc6393_gpio (struct device *dev); ++u32 set_tc6393_gpio (struct device *dev, u32 bits); ++u32 reset_tc6393_gpio (struct device *dev, u32 bits); ++ ++/*--------------------------------------------------------------------------*/ ++ ++#else + #error "no TMIO SoC configured" ++#endif + + #endif +Index: git/include/asm-arm/arch-pxa/irqs.h +=================================================================== +--- git.orig/include/asm-arm/arch-pxa/irqs.h 2006-10-31 16:09:33.000000000 +0000 ++++ git/include/asm-arm/arch-pxa/irqs.h 2006-11-07 22:13:09.000000000 +0000 +@@ -163,17 +163,27 @@ + #define IRQ_LOCOMO_SPI_OVRN (IRQ_BOARD_END + 20) + #define IRQ_LOCOMO_SPI_TEND (IRQ_BOARD_END + 21) + ++#define IRQ_TC6393_START (IRQ_BOARD_END) ++#define IRQ_TC6393_NAND (IRQ_BOARD_END + 0) ++#define IRQ_TC6393_SD (IRQ_BOARD_END + 1) ++#define IRQ_TC6393_OHCI (IRQ_BOARD_END + 2) ++#define IRQ_TC6393_SERIAL (IRQ_BOARD_END + 3) ++#define IRQ_TC6393_LCD (IRQ_BOARD_END + 4) ++ + /* + * Figure out the MAX IRQ number. + * + * If we have an SA1111, the max IRQ is S1_BVD1_STSCHG+1. + * If we have an LoCoMo, the max IRQ is IRQ_LOCOMO_SPI_TEND+1 ++ * If we have an TC6393XB, the max IRQ is IRQ_TC6393_LCD+1 + * Otherwise, we have the standard IRQs only. + */ + #ifdef CONFIG_SA1111 + #define NR_IRQS (IRQ_S1_BVD1_STSCHG + 1) + #elif defined(CONFIG_SHARP_LOCOMO) + #define NR_IRQS (IRQ_LOCOMO_SPI_TEND + 1) ++#elif defined(CONFIG_TOSHIBA_TC6393XB) ++#define NR_IRQS (IRQ_TC6393_LCD + 1) + #elif defined(CONFIG_ARCH_LUBBOCK) || \ + defined(CONFIG_MACH_LOGICPD_PXA270) || \ + defined(CONFIG_MACH_MAINSTONE) diff --git a/packages/linux/linux-rp-2.6.22/tosa-keyboard-r19.patch b/packages/linux/linux-rp-2.6.22/tosa-keyboard-r19.patch new file mode 100644 index 0000000000..948c27fdce --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/tosa-keyboard-r19.patch @@ -0,0 +1,514 @@ + drivers/input/keyboard/Kconfig | 12 - + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/tosakbd.c | 467 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 479 insertions(+), 1 deletion(-) + +Index: git/drivers/input/keyboard/Kconfig +=================================================================== +--- git.orig/drivers/input/keyboard/Kconfig 2006-10-31 16:08:57.000000000 +0000 ++++ git/drivers/input/keyboard/Kconfig 2006-11-07 22:13:10.000000000 +0000 +@@ -148,12 +148,22 @@ config KEYBOARD_SPITZ + depends on PXA_SHARPSL + default y + help +- Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000, ++ Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000, + SL-C3000 and Sl-C3100 series of PDAs. + + To compile this driver as a module, choose M here: the + module will be called spitzkbd. + ++config KEYBOARD_TOSA ++ tristate "Tosa keyboard" ++ depends on PXA_SHARPSL ++ default y ++ help ++ Say Y here to enable the keyboard on the Sharp Zaurus SL-6000x (Tosa) ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tosakbd. ++ + config KEYBOARD_AMIGA + tristate "Amiga keyboard" + depends on AMIGA +Index: git/drivers/input/keyboard/Makefile +=================================================================== +--- git.orig/drivers/input/keyboard/Makefile 2006-10-31 16:08:57.000000000 +0000 ++++ git/drivers/input/keyboard/Makefile 2006-11-07 22:13:10.000000000 +0000 +@@ -17,3 +17,4 @@ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkb + obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o + obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o + obj-$(CONFIG_KEYBOARD_ASIC3) += asic3_keys.o ++obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o +Index: git/drivers/input/keyboard/tosakbd.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ git/drivers/input/keyboard/tosakbd.c 2006-11-07 23:27:19.000000000 +0000 +@@ -0,0 +1,467 @@ ++/* ++ * Keyboard driver for Sharp Tosa models (SL-6000x) ++ * ++ * Copyright (c) 2005 Dirk Opfer ++ * ++ * Based on xtkbd.c/locomkbd.c/corgikbd.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <linux/init.h> ++#include <linux/input.h> ++#include <linux/interrupt.h> ++#include <linux/jiffies.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++ ++#include <asm/arch/tosa.h> ++#include <asm/arch/hardware.h> ++#include <asm/arch/pxa-regs.h> ++ ++ ++#define TOSA_KEY_STROBE_NUM (11) ++#define TOSA_KEY_SENSE_NUM (7) ++ ++#define KEYMASK_ON (0x1<<0) ++#define KEYMASK_REC (0x1<<1) ++#define KEYMASK_SYNC (0x1<<2) ++ ++#define KB_ROWS 7 ++#define KB_COLS 11 ++#define KB_ROWMASK(r) (1 << (r)) ++#define SCANCODE(r,c) ( ((r)<<4) + (c) + 1 ) ++#define NR_SCANCODES (SCANCODE(KB_ROWS-1,KB_COLS)+1+1) ++ ++#define SCAN_INTERVAL (HZ/10) ++#define HP_SCAN_INTERVAL (150) /* ms */ ++#define HP_STABLE_COUNT 2 ++ ++#define TOSA_KEY_CALENDER KEY_F1 ++#define TOSA_KEY_ADDRESS KEY_F2 ++#define TOSA_KEY_FN KEY_F3 ++#define TOSA_KEY_CANCEL KEY_F4 ++#define TOSA_KEY_OFF KEY_SUSPEND ++#define TOSA_KEY_CENTER KEY_F5 ++#define TOSA_KEY_REC KEY_F6 ++#define TOSA_KEY_LIGHT KEY_F7 ++#define TOSA_KEY_RECORD KEY_F8 ++#define TOSA_KEY_HOME KEY_F9 ++#define TOSA_KEY_MAIL KEY_F10 ++#define TOSA_KEY_OK KEY_F11 ++#define TOSA_KEY_MENU KEY_F12 ++#define TOSA_KEY_SYNC KEY_F13 ++ ++#define GET_ROWS_STATUS(c) ((GPLR2 & TOSA_GPIO_ALL_SENSE_BIT) >> TOSA_GPIO_ALL_SENSE_RSHIFT) ++#define KB_DISCHARGE_DELAY 10 ++#define KB_ACTIVATE_DELAY 10 ++ ++ ++static unsigned char tosakbd_keycode[NR_SCANCODES] = { ++ 0, /* 0 */ ++ 0, KEY_W, 0, 0, 0, KEY_K, KEY_BACKSPACE, KEY_P, 0, 0, 0, TOSA_KEY_OFF, 0, 0, 0, 0, /*1 - 16*/ ++ KEY_Q, KEY_E, KEY_T, KEY_Y, 0, KEY_O, KEY_I, KEY_COMMA, 0, 0, 0, TOSA_KEY_RECORD, 0, 0, 0, 0, /*17 - 32*/ ++ KEY_A, KEY_D, KEY_G, KEY_U, 0, KEY_L, KEY_ENTER, KEY_DOT, 0, 0, 0, TOSA_KEY_SYNC, 0, 0, 0, 0, /*33 - 48*/ ++ KEY_Z, KEY_C, KEY_V, KEY_J, TOSA_KEY_ADDRESS, TOSA_KEY_CANCEL, TOSA_KEY_CENTER, TOSA_KEY_OK, KEY_LEFTSHIFT, 0 , 0,0 , 0, 0, 0, 0, /*49 - 64*/ ++ KEY_S, KEY_R, KEY_B, KEY_N, TOSA_KEY_CALENDER, TOSA_KEY_HOME, TOSA_KEY_REC, TOSA_KEY_LIGHT, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /*65 - 80*/ ++ KEY_TAB, KEY_SLASH, KEY_H, KEY_M, TOSA_KEY_MENU, 0, KEY_UP, 0, 0, 0, TOSA_KEY_FN, 0, 0, 0, 0, 0, /*81 - 96*/ ++ KEY_X, KEY_F, KEY_SPACE, KEY_APOSTROPHE, TOSA_KEY_MAIL, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, /*97 - 109*/ ++}; ++ ++struct tosakbd { ++ unsigned char keycode[ARRAY_SIZE(tosakbd_keycode)]; ++ struct input_dev *input; ++ ++ spinlock_t lock; ++ struct timer_list timer; ++ struct timer_list hptimer; ++ ++ int hp_state; ++ int hp_count; ++ ++ unsigned int suspended; ++ unsigned long suspend_jiffies; ++}; ++ ++/* Helper functions for reading the keyboard matrix ++ * Note: We should really be using pxa_gpio_mode to alter GPDR but it ++ * requires a function call per GPIO bit which is excessive ++ * when we need to access 12 bits at once, multiple times. ++ * These functions must be called within local_irq_save()/local_irq_restore() ++ * or similar. ++ */ ++static inline void tosakbd_discharge_all(void) ++{ ++ /* STROBE All HiZ */ ++ GPCR1 = TOSA_GPIO_HIGH_STROBE_BIT; ++ GPDR1 &= ~TOSA_GPIO_HIGH_STROBE_BIT; ++ GPCR2 = TOSA_GPIO_LOW_STROBE_BIT; ++ GPDR2 &= ~TOSA_GPIO_LOW_STROBE_BIT; ++} ++ ++static inline void tosakbd_activate_all(void) ++{ ++ /* STROBE ALL -> High */ ++ GPSR1 = TOSA_GPIO_HIGH_STROBE_BIT; ++ GPDR1 |= TOSA_GPIO_HIGH_STROBE_BIT; ++ GPSR2 = TOSA_GPIO_LOW_STROBE_BIT; ++ GPDR2 |= TOSA_GPIO_LOW_STROBE_BIT; ++ ++ udelay(KB_DISCHARGE_DELAY); ++ ++ /* STATE CLEAR */ ++ GEDR2 |= TOSA_GPIO_ALL_SENSE_BIT; ++} ++ ++static inline void tosakbd_activate_col(int col) ++{ ++ if (col<=5) { ++ /* STROBE col -> High, not col -> HiZ */ ++ GPSR1 = TOSA_GPIO_STROBE_BIT(col); ++ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); ++ } else { ++ /* STROBE col -> High, not col -> HiZ */ ++ GPSR2 = TOSA_GPIO_STROBE_BIT(col); ++ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); ++ } ++} ++ ++static inline void tosakbd_reset_col(int col) ++{ ++ if (col<=5) { ++ /* STROBE col -> Low */ ++ GPCR1 = TOSA_GPIO_STROBE_BIT(col); ++ /* STROBE col -> out, not col -> HiZ */ ++ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); ++ } else { ++ /* STROBE col -> Low */ ++ GPCR2 = TOSA_GPIO_STROBE_BIT(col); ++ /* STROBE col -> out, not col -> HiZ */ ++ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); ++ } ++} ++ ++/* ++ * Read the GPIOs for POWER, RECORD and SYNC ++ */ ++static int read_port_key_status_raw(void) ++{ ++ int val=0; ++ ++ /* Power key */ ++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_ON_KEY))==0) ++ val |= KEYMASK_ON; ++ /* Record key */ ++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_RECORD_BTN))==0) ++ val |= KEYMASK_REC; ++ /* Sync key */ ++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_SYNC))==0) ++ val |= KEYMASK_SYNC; ++ return val; ++} ++ ++ ++/* ++ * The tosa keyboard only generates interrupts when a key is pressed. ++ * So when a key is pressed, we enable a timer. This timer scans the ++ * keyboard, and this is how we detect when the key is released. ++ */ ++ ++/* Scan the hardware keyboard and push any changes up through the input layer */ ++static void tosakbd_scankeyboard(struct tosakbd *tosakbd_data) ++{ ++ unsigned int row, col, rowd; ++ unsigned long flags; ++ unsigned int num_pressed = 0; ++ ++ if (tosakbd_data->suspended) ++ return; ++ ++ spin_lock_irqsave(&tosakbd_data->lock, flags); ++ ++ for (col = 0; col < KB_COLS; col++) { ++ /* ++ * Discharge the output driver capacitatance ++ * in the keyboard matrix. (Yes it is significant..) ++ */ ++ tosakbd_discharge_all(); ++ udelay(KB_DISCHARGE_DELAY); ++ ++ tosakbd_activate_col( col); ++ udelay(KB_ACTIVATE_DELAY); ++ ++ rowd = GET_ROWS_STATUS(col); ++ ++ for (row = 0; row < KB_ROWS; row++) { ++ unsigned int scancode, pressed; ++ scancode = SCANCODE(row, col); ++ pressed = rowd & KB_ROWMASK(row); ++ input_report_key(tosakbd_data->input, tosakbd_data->keycode[scancode], pressed); ++ if (pressed) ++ num_pressed++; ++ } ++ ++ tosakbd_reset_col(col); ++ } ++ ++ tosakbd_activate_all(); ++ ++ rowd = read_port_key_status_raw(); ++ ++ for (row = 0; row < 3; row++ ) { ++ unsigned int scancode, pressed; ++ scancode = SCANCODE(row, KB_COLS); ++ pressed = rowd & KB_ROWMASK(row); ++ input_report_key(tosakbd_data->input, tosakbd_data->keycode[scancode], pressed); ++ if (pressed) ++ num_pressed++; ++ ++ if (pressed && (tosakbd_data->keycode[scancode] == TOSA_KEY_OFF) ++ && time_after(jiffies, tosakbd_data->suspend_jiffies + msecs_to_jiffies(1000))) { ++ input_event(tosakbd_data->input, EV_PWR, TOSA_KEY_OFF, 1); ++ tosakbd_data->suspend_jiffies = jiffies; ++ } ++ } ++ ++ input_sync(tosakbd_data->input); ++ ++ /* if any keys are pressed, enable the timer */ ++ if (num_pressed) ++ mod_timer(&tosakbd_data->timer, jiffies + SCAN_INTERVAL); ++ ++ spin_unlock_irqrestore(&tosakbd_data->lock, flags); ++} ++ ++/* ++ * tosa keyboard interrupt handler. ++ */ ++static irqreturn_t tosakbd_interrupt(int irq, void *dev_id) ++{ ++ struct tosakbd *tosakbd_data = dev_id; ++ ++ if (!timer_pending(&tosakbd_data->timer)) ++ { ++ /** wait chattering delay **/ ++ udelay(20); ++ tosakbd_scankeyboard(tosakbd_data); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * tosa timer checking for released keys ++ */ ++static void tosakbd_timer_callback(unsigned long data) ++{ ++ struct tosakbd *tosakbd_data = (struct tosakbd *) data; ++ tosakbd_scankeyboard(tosakbd_data); ++} ++ ++/* ++ * The headphone generates an interrupt. ++ * We debounce the switche and pass them to the input system. ++ */ ++ ++static irqreturn_t tosakbd_hp_isr(int irq, void *dev_id) ++{ ++ struct tosakbd *tosakbd_data = dev_id; ++ ++ if (!timer_pending(&tosakbd_data->hptimer)) ++ mod_timer(&tosakbd_data->hptimer, jiffies + msecs_to_jiffies(HP_SCAN_INTERVAL)); ++ ++ return IRQ_HANDLED; ++} ++ ++static void tosakbd_hp_timer(unsigned long data) ++{ ++ struct tosakbd *tosakbd_data = (struct tosakbd *) data; ++ unsigned long state; ++ unsigned long flags; ++ ++ state = (GPLR(TOSA_GPIO_EAR_IN) & GPIO_bit(TOSA_GPIO_EAR_IN)); ++ if (state != tosakbd_data->hp_state) { ++ tosakbd_data->hp_count = 0; ++ tosakbd_data->hp_state = state; ++ } else if (tosakbd_data->hp_count < HP_STABLE_COUNT) { ++ tosakbd_data->hp_count++; ++ } ++ ++ if (tosakbd_data->hp_count >= HP_STABLE_COUNT) { ++ spin_lock_irqsave(&tosakbd_data->lock, flags); ++ ++ input_report_switch(tosakbd_data->input, SW_HEADPHONE_INSERT, ((GPLR(TOSA_GPIO_EAR_IN) & GPIO_bit(TOSA_GPIO_EAR_IN)) == 0)); ++ input_sync(tosakbd_data->input); ++ ++ spin_unlock_irqrestore(&tosakbd_data->lock, flags); ++ } else { ++ mod_timer(&tosakbd_data->hptimer, jiffies + msecs_to_jiffies(HP_SCAN_INTERVAL)); ++ } ++} ++ ++#ifdef CONFIG_PM ++static int tosakbd_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct tosakbd *tosakbd = platform_get_drvdata(dev); ++ ++ tosakbd->suspended = 1; ++ ++ return 0; ++} ++ ++static int tosakbd_resume(struct platform_device *dev) ++{ ++ struct tosakbd *tosakbd = platform_get_drvdata(dev); ++ ++ /* Upon resume, ignore the suspend key for a short while */ ++ tosakbd->suspend_jiffies = jiffies; ++ tosakbd->suspended = 0; ++ ++ return 0; ++} ++#else ++#define tosakbd_suspend NULL ++#define tosakbd_resume NULL ++#endif ++ ++static int __init tosakbd_probe(struct platform_device *pdev) { ++ ++ int i; ++ struct tosakbd *tosakbd; ++ struct input_dev *input_dev; ++ ++ tosakbd = kzalloc(sizeof(struct tosakbd), GFP_KERNEL); ++ if (!tosakbd) ++ return -ENOMEM; ++ ++ input_dev = input_allocate_device(); ++ if (!input_dev) { ++ kfree(tosakbd); ++ return -ENOMEM; ++ } ++ ++ platform_set_drvdata(pdev,tosakbd); ++ ++ spin_lock_init(&tosakbd->lock); ++ ++ /* Init Keyboard rescan timer */ ++ init_timer(&tosakbd->timer); ++ tosakbd->timer.function = tosakbd_timer_callback; ++ tosakbd->timer.data = (unsigned long) tosakbd; ++ ++ /* Init Headphone Timer */ ++ init_timer(&tosakbd->hptimer); ++ tosakbd->hptimer.function = tosakbd_hp_timer; ++ tosakbd->hptimer.data = (unsigned long) tosakbd; ++ ++ tosakbd->suspend_jiffies = jiffies; ++ ++ tosakbd->input = input_dev; ++ ++ input_dev->private = tosakbd; ++ input_dev->name = "Tosa Keyboard"; ++ input_dev->phys = "tosakbd/input0"; ++ input_dev->cdev.dev = &pdev->dev; ++ ++ input_dev->id.bustype = BUS_HOST; ++ input_dev->id.vendor = 0x0001; ++ input_dev->id.product = 0x0001; ++ input_dev->id.version = 0x0100; ++ ++ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_PWR) | BIT(EV_SW); ++ input_dev->keycode = tosakbd->keycode; ++ input_dev->keycodesize = sizeof(unsigned char); ++ input_dev->keycodemax = ARRAY_SIZE(tosakbd_keycode); ++ ++ memcpy(tosakbd->keycode, tosakbd_keycode, sizeof(tosakbd->keycode)); ++ for (i = 0; i < ARRAY_SIZE(tosakbd_keycode); i++) ++ set_bit(tosakbd->keycode[i], input_dev->keybit); ++ clear_bit(0, input_dev->keybit); ++ set_bit(SW_HEADPHONE_INSERT, input_dev->swbit); ++ ++ input_register_device(tosakbd->input); ++ ++ /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */ ++ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) { ++ pxa_gpio_mode( TOSA_GPIO_KEY_SENSE(i) | GPIO_IN); ++ if (request_irq(TOSA_IRQ_GPIO_KEY_SENSE(i), tosakbd_interrupt, ++ IRQF_DISABLED | IRQF_TRIGGER_RISING, "tosakbd", tosakbd)) { ++ printk("tosakbd: Can't get IRQ: %d !\n", i); ++ } ++ } ++ ++ /* Set Strobe lines as outputs - set high */ ++ for (i = 0; i < TOSA_KEY_STROBE_NUM; i++) { ++ pxa_gpio_mode( TOSA_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH); ++ } ++ ++ // Power&Rec Button ++ pxa_gpio_mode( TOSA_GPIO_ON_KEY | GPIO_IN); ++ pxa_gpio_mode( TOSA_GPIO_RECORD_BTN | GPIO_IN); ++ pxa_gpio_mode( TOSA_GPIO_SYNC | GPIO_IN); ++ pxa_gpio_mode( TOSA_GPIO_EAR_IN | GPIO_IN); ++ ++ if (request_irq(TOSA_IRQ_GPIO_ON_KEY, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "On key", tosakbd) || ++ request_irq(TOSA_IRQ_GPIO_RECORD_BTN, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "Record key", tosakbd) || ++ request_irq(TOSA_IRQ_GPIO_SYNC, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "Sync key", tosakbd) || ++ request_irq(TOSA_IRQ_GPIO_EAR_IN, tosakbd_hp_isr, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "HP in", tosakbd)) { ++ printk("Could not allocate KEYBD IRQ!\n"); ++ } ++ ++ printk(KERN_INFO "input: Tosa Keyboard Registered\n"); ++ ++ return 0; ++} ++ ++static int tosakbd_remove(struct platform_device *dev) { ++ ++ int i; ++ struct tosakbd *tosakbd = platform_get_drvdata(dev); ++ ++ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) ++ free_irq(TOSA_IRQ_GPIO_KEY_SENSE(i),tosakbd); ++ ++ free_irq(TOSA_IRQ_GPIO_ON_KEY,tosakbd); ++ free_irq(TOSA_IRQ_GPIO_RECORD_BTN,tosakbd); ++ free_irq(TOSA_IRQ_GPIO_SYNC,tosakbd); ++ ++ del_timer_sync(&tosakbd->timer); ++ ++ input_unregister_device(tosakbd->input); ++ ++ kfree(tosakbd); ++ ++ return 0; ++} ++ ++static struct platform_driver tosakbd_driver = { ++ .probe = tosakbd_probe, ++ .remove = tosakbd_remove, ++ .suspend = tosakbd_suspend, ++ .resume = tosakbd_resume, ++ .driver = { ++ .name = "tosa-keyboard", ++ }, ++}; ++ ++static int __devinit tosakbd_init(void) ++{ ++ return platform_driver_register(&tosakbd_driver); ++} ++ ++static void __exit tosakbd_exit(void) ++{ ++ platform_driver_unregister(&tosakbd_driver); ++} ++ ++module_init(tosakbd_init); ++module_exit(tosakbd_exit); ++ ++MODULE_AUTHOR("Dirk Opfer <Dirk@Opfer-Online.de>"); ++MODULE_DESCRIPTION("Tosa Keyboard Driver"); ++MODULE_LICENSE("GPLv2"); diff --git a/packages/linux/linux-rp-2.6.22/tosa-lcdnoise-r1-fix-r0.patch b/packages/linux/linux-rp-2.6.22/tosa-lcdnoise-r1-fix-r0.patch new file mode 100644 index 0000000000..93a9c18720 --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/tosa-lcdnoise-r1-fix-r0.patch @@ -0,0 +1,135 @@ +From eada869814636157956641ba1503f0d6cc04e2b7 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 17:43:51 +0400 +Subject: [PATCH] tosa-lcdnoise-r1.patch fixes + +--- + arch/arm/mach-pxa/tosa_lcd.c | 34 ++++++++++++++++++++++++++++++++++ + drivers/input/touchscreen/tosa_ts.c | 9 +-------- + include/asm-arm/arch-pxa/tosa.h | 5 +++++ + 3 files changed, 40 insertions(+), 8 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa_lcd.c b/arch/arm/mach-pxa/tosa_lcd.c +index d52f63f..447ca86 100644 +--- a/arch/arm/mach-pxa/tosa_lcd.c ++++ b/arch/arm/mach-pxa/tosa_lcd.c +@@ -59,6 +59,8 @@ static int bl_intensity; + static struct ssp_dev tosa_nssp_dev; + static struct ssp_state tosa_nssp_state; + static spinlock_t tosa_nssp_lock; ++static int blanked; ++static unsigned long hsync_time; + + static unsigned short normal_i2c[] = { + DAC_BASE, +@@ -130,6 +132,17 @@ static void tosa_lcd_tg_init(struct device *dev) + pxa_nssp_output(TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */ + } + ++static unsigned long calc_hsync_time(const struct fb_videomode *mode) { ++ /* The 25 and 44 'magic numbers' are from Sharp's 2.4 patches */ ++ if (mode->yres == 640) { ++ return 25; ++ } ++ if (mode->yres == 320) { ++ return 44; ++ } ++ return 0; ++} ++ + static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode) + { + const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR; +@@ -154,6 +167,8 @@ static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode) + /* set common voltage */ + i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj); + ++ blanked = 0; ++ hsync_time = calc_hsync_time(mode); + } + + static void tosa_lcd_tg_off(struct device *dev) +@@ -172,6 +187,8 @@ static void tosa_lcd_tg_off(struct device *dev) + + /* L3V Off */ + reset_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON); ++ ++ blanked = 1; + } + + static int tosa_detect_client(struct i2c_adapter* adapter, int address, int kind) { +@@ -238,6 +255,23 @@ static int tosa_detach_client(struct i2c_client* client) { + return 0; + } + ++unsigned long tosa_lcd_get_hsync_time(void) ++{ ++/* This method should eventually contain the correct algorithm for calculating ++ the hsync_time */ ++ if (blanked) ++ return 0; ++ else ++ return hsync_time; ++} ++ ++void tosa_lcd_wait_hsync(void) ++{ ++ /* Waits for a rising edge on the VGA line */ ++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0); ++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0); ++} ++ + static struct i2c_driver tosa_driver={ + .id = TOSA_LCD_I2C_DEVICEID, + .attach_adapter = tosa_attach_adapter, +diff --git a/drivers/input/touchscreen/tosa_ts.c b/drivers/input/touchscreen/tosa_ts.c +index bc733e9..134f8ce 100644 +--- a/drivers/input/touchscreen/tosa_ts.c ++++ b/drivers/input/touchscreen/tosa_ts.c +@@ -25,13 +25,6 @@ + #define CCNT_ON() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1)) + #define CCNT_OFF() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1)) + +-static inline void tosa_lcd_wait_hsync(void) +-{ +- /* Waits for a rising edge on the VGA line */ +- while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0); +- while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0); +-} +- + /* On the Sharp SL-6000 (Tosa), due to a noisy LCD, we need to perform a wait + * before sampling the Y axis of the touchscreen */ + void tosa_lcd_sync_on(int adcsel) { +@@ -54,7 +47,7 @@ void tosa_lcd_sync_on(int adcsel) { + } + } + +-void tosa_lcd_sync_off(void) { ++void tosa_lcd_sync_off(int adcsel) { + CCNT_OFF(); + } + +diff --git a/include/asm-arm/arch-pxa/tosa.h b/include/asm-arm/arch-pxa/tosa.h +index ce7322d..7f446fd 100644 +--- a/include/asm-arm/arch-pxa/tosa.h ++++ b/include/asm-arm/arch-pxa/tosa.h +@@ -1,6 +1,7 @@ + /* + * Hardware specific definitions for Sharp SL-C6000x series of PDAs + * ++ * Copyright (c) 2006 Wolfson Microelectronics PLC. + * Copyright (c) 2005 Dirk Opfer + * + * Based on Sharp's 2.4 kernel patches +@@ -187,4 +188,8 @@ + extern struct platform_device tosascoop_jc_device; + extern struct platform_device tosascoop_device; + extern struct platform_device tc6393_device; ++ ++unsigned long tosa_lcd_get_hsync_time(void); ++void tosa_lcd_wait_hsync(void); ++ + #endif /* _ASM_ARCH_TOSA_H_ */ +-- +1.4.4.4 + diff --git a/packages/linux/linux-rp-2.6.22/tosa-lcdnoise-r1.patch b/packages/linux/linux-rp-2.6.22/tosa-lcdnoise-r1.patch new file mode 100644 index 0000000000..21f3cf66b1 --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/tosa-lcdnoise-r1.patch @@ -0,0 +1,158 @@ +From 564b757ba44b517ac6d693b94a177708bb5d3887 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 17:30:30 +0400 +Subject: [PATCH] tosa-lcdnoise-r1.patch + +--- + drivers/input/touchscreen/Kconfig | 13 +++++ + drivers/input/touchscreen/Makefile | 1 + + drivers/input/touchscreen/tosa_ts.c | 102 +++++++++++++++++++++++++++++++++++ + 3 files changed, 116 insertions(+), 0 deletions(-) + +diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig +index 3ac01b4..6862e8f 100644 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -219,6 +219,19 @@ config TOUCHSCREEN_USB_DMC_TSC10 + bool "DMC TSC-10/25 device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + ++config TOUCHSCREEN_TOSA ++ tristate "Sharp Tosa touchscreen driver" ++ depends on TOUCHSCREEN_WM97XX && MACH_TOSA ++ default n ++ help ++ Say Y here to enable the driver for the touchscreen on the ++ Sharp Tosa PDA. ++ depends on TOUCHSCREEN_WM97XX && MACH_TOSA ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tosa_ts. ++ + config TOUCHSCREEN_TSC2101 + tristate "TI TSC2101 touchscreen input driver" + depends on MACH_HX2750 && INPUT && INPUT_TOUCHSCREEN +diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile +index f64d1a5..4fc0e17 100644 +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -22,6 +22,7 @@ obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o + obj-$(CONFIG_TOUCHSCREEN_TSC2101) += tsc2101_ts.o + obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o + obj-$(CONFIG_TOUCHSCREEN_WM97XX_PXA) += pxa-wm97xx.o ++obj-$(CONFIG_TOUCHSCREEN_TOSA) += tosa_ts.o + + ifeq ($(CONFIG_TOUCHSCREEN_WM9713),y) + wm97xx-ts-objs += wm9713.o +diff --git a/drivers/input/touchscreen/tosa_ts.c b/drivers/input/touchscreen/tosa_ts.c +new file mode 100644 +index 0000000..bc733e9 +--- /dev/null ++++ b/drivers/input/touchscreen/tosa_ts.c +@@ -0,0 +1,102 @@ ++/* ++ * tosa_ts.c -- Touchscreen driver for Sharp SL-6000 (Tosa). ++ * ++ * Copyright 2006 Wolfson Microelectronics PLC. ++ * Author: Mike Arthur ++ * linux@wolfsonmicro.com ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Revision history ++ * 1st Sep 2006 Initial version. ++ * ++ */ ++ ++#include <linux/wm97xx.h> ++#include <asm/arch/tosa.h> ++#include <asm/arch/hardware.h> ++#include <asm/arch/pxa-regs.h> ++ ++/* Taken from the Sharp 2.4 kernel code */ ++#define CCNT(a) asm volatile ("mrc p14, 0, %0, C1, C1, 0" : "=r"(a)) ++#define CCNT_ON() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1)) ++#define CCNT_OFF() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1)) ++ ++static inline void tosa_lcd_wait_hsync(void) ++{ ++ /* Waits for a rising edge on the VGA line */ ++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0); ++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0); ++} ++ ++/* On the Sharp SL-6000 (Tosa), due to a noisy LCD, we need to perform a wait ++ * before sampling the Y axis of the touchscreen */ ++void tosa_lcd_sync_on(int adcsel) { ++ unsigned long timer1 = 0, timer2 = 0, wait_time = 0; ++ if (adcsel == WM97XX_ADCSEL_Y) { ++ wait_time = tosa_lcd_get_hsync_time(); ++ CCNT_ON(); ++ ++ if (wait_time) { ++ /* wait for LCD rising edge */ ++ tosa_lcd_wait_hsync(); ++ /* get clock */ ++ CCNT(timer1); ++ CCNT(timer2); ++ ++ while ((timer2 - timer1) < wait_time) { ++ CCNT(timer2); ++ } ++ } ++ } ++} ++ ++void tosa_lcd_sync_off(void) { ++ CCNT_OFF(); ++} ++ ++static struct wm97xx_mach_ops tosa_mach_ops = { ++ .pre_sample = tosa_lcd_sync_on, ++ .post_sample = tosa_lcd_sync_off, ++}; ++ ++int tosa_ts_probe(struct device *dev) { ++ struct wm97xx *wm = dev->driver_data; ++ return wm97xx_register_mach_ops (wm, &tosa_mach_ops); ++} ++ ++ ++int tosa_ts_remove(struct device *dev) { ++ struct wm97xx *wm = dev->driver_data; ++ wm97xx_unregister_mach_ops (wm); ++ return 0; ++} ++ ++static struct device_driver tosa_ts_driver = { ++ .name = "wm97xx-touchscreen", ++ .bus = &wm97xx_bus_type, ++ .owner = THIS_MODULE, ++ .probe = tosa_ts_probe, ++ .remove = tosa_ts_remove, ++}; ++ ++static int __init tosa_ts_init(void) ++{ ++ return driver_register(&tosa_ts_driver); ++} ++ ++static void __exit tosa_ts_exit(void) ++{ ++ driver_unregister(&tosa_ts_driver); ++} ++ ++module_init(tosa_ts_init); ++module_exit(tosa_ts_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Mike Arthur, mike@mikearthur.co.uk, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("Sharp SL6000 Tosa Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +-- +1.4.4.4 + diff --git a/packages/linux/linux-rp-2.6.22/tosa-power-r18-fix-r0.patch b/packages/linux/linux-rp-2.6.22/tosa-power-r18-fix-r0.patch new file mode 100644 index 0000000000..8899ae270b --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/tosa-power-r18-fix-r0.patch @@ -0,0 +1,59 @@ +From 24813da9b0aac0e92635d7307837d89a9f4a1ee7 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 16:47:15 +0400 +Subject: [PATCH] tosa-power-r18.patch fixes + +--- + arch/arm/mach-pxa/tosa_pm.c | 9 +++++---- + 1 files changed, 5 insertions(+), 4 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa_pm.c b/arch/arm/mach-pxa/tosa_pm.c +index 1eab1af..2df75f0 100644 +--- a/arch/arm/mach-pxa/tosa_pm.c ++++ b/arch/arm/mach-pxa/tosa_pm.c +@@ -17,9 +17,9 @@ + #include <linux/interrupt.h> + #include <linux/platform_device.h> + #include <linux/pm.h> ++#include <linux/apm-emulation.h> + #include <linux/wm97xx.h> + +-#include <asm/apm.h> + #include <asm/irq.h> + #include <asm/mach-types.h> + #include <asm/hardware.h> +@@ -144,7 +144,7 @@ static int tosa_ac97_init(void) + pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); + pxa_gpio_mode(GPIO20_DREQ0_MD); + +- pxa_set_cken(CKEN2_AC97, 1); ++ pxa_set_cken(CKEN_AC97, 1); + /* AC97 power on sequense */ + while ( 1 ) { + GCR = 0; +@@ -184,11 +184,12 @@ static int tosa_ac97_init(void) + pxa_gpio_mode(GPIO32_SDATA_IN1_AC97_MD); + ad_polling = 1; + printk("tosa_ac97_init\n"); ++ return 0; + } + + void tosa_ac97_exit(void) + { +- if (!(CKEN & CKEN2_AC97)) ++ if (!(CKEN & CKEN_AC97)) + return; + + // power down the whole chip +@@ -197,7 +198,7 @@ void tosa_ac97_exit(void) + // GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN); + // GSR = GSR; + // GCR = GCR_ACLINK_OFF; +- pxa_set_cken(CKEN2_AC97, 0); ++ pxa_set_cken(CKEN_AC97, 0); + /* switch back to irq driver */ + ad_polling = 0; + printk("tosa_ac97_exit\n"); +-- +1.4.4.4 + diff --git a/packages/linux/linux-rp-2.6.22/tosa-pxaac97-r6-fix-r0.patch b/packages/linux/linux-rp-2.6.22/tosa-pxaac97-r6-fix-r0.patch new file mode 100644 index 0000000000..9c18aae98d --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/tosa-pxaac97-r6-fix-r0.patch @@ -0,0 +1,29 @@ +From 005693333f4b3e0495bb80cc3cfd812e3e6f0a30 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 00:48:42 +0400 +Subject: [PATCH] tosa-pxaac97-r6.patch fixes + +--- + arch/arm/mach-pxa/tosa.c | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c +index 059fa07..61536d4 100644 +--- a/arch/arm/mach-pxa/tosa.c ++++ b/arch/arm/mach-pxa/tosa.c +@@ -310,10 +310,10 @@ static void __init tosa_init(void) + PMCR = 0x01; + + // AC97 Disable all IRQ's +- pxa_set_cken(CKEN2_AC97, 1); ++ pxa_set_cken(CKEN_AC97, 1); + GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN); + GSR = GSR; +- pxa_set_cken(CKEN2_AC97, 0); ++ pxa_set_cken(CKEN_AC97, 0); + + pxa_set_mci_info(&tosa_mci_platform_data); + pxa_set_udc_info(&udc_info); +-- +1.4.4.4 + diff --git a/packages/linux/linux-rp-2.6.22/tosa-tmio-lcd-r10-fix-r0.patch b/packages/linux/linux-rp-2.6.22/tosa-tmio-lcd-r10-fix-r0.patch new file mode 100644 index 0000000000..a2e2bee151 --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/tosa-tmio-lcd-r10-fix-r0.patch @@ -0,0 +1,35 @@ +From bb3ed6577c592d86f0976a92978c9454bbdfbe59 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 02:01:23 +0400 +Subject: [PATCH] tosa-tmio-lcd-r10.patch fixes + +--- + arch/arm/mach-pxa/tosa_lcd.c | 5 +++-- + 1 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa_lcd.c b/arch/arm/mach-pxa/tosa_lcd.c +index eeeee3e..d52f63f 100644 +--- a/arch/arm/mach-pxa/tosa_lcd.c ++++ b/arch/arm/mach-pxa/tosa_lcd.c +@@ -66,7 +66,7 @@ static unsigned short normal_i2c[] = { + }; + I2C_CLIENT_INSMOD; + +-static struct corgibl_machinfo tosa_bl_machinfo = { ++static struct generic_bl_info tosa_bl_machinfo = { + .max_intensity = 255, + .default_intensity = 68, + .limit_mask = 0x0b, +@@ -80,7 +80,8 @@ int tosa_bl_intensity(void) + + static void pxa_nssp_output(unsigned char reg, unsigned char data) + { +- unsigned long flag, dummy; ++ unsigned long flag; ++ u32 dummy; + u32 dat = ( ((reg << 5) & 0xe0) | (data & 0x1f) ); + spin_lock_irqsave(&tosa_nssp_lock, flag); + +-- +1.4.4.4 + diff --git a/packages/linux/linux-rp-2.6.22/tosa-tmio-lcd-r10.patch b/packages/linux/linux-rp-2.6.22/tosa-tmio-lcd-r10.patch new file mode 100644 index 0000000000..aef3a047c1 --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/tosa-tmio-lcd-r10.patch @@ -0,0 +1,472 @@ + arch/arm/mach-pxa/Kconfig | 5 + arch/arm/mach-pxa/Makefile | 2 + arch/arm/mach-pxa/tosa.c | 49 +++++- + arch/arm/mach-pxa/tosa_lcd.c | 344 +++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 396 insertions(+), 4 deletions(-) + +Index: git/arch/arm/mach-pxa/Makefile +=================================================================== +--- git.orig/arch/arm/mach-pxa/Makefile 2006-11-07 22:13:10.000000000 +0000 ++++ git/arch/arm/mach-pxa/Makefile 2006-11-07 23:29:38.000000000 +0000 +@@ -17,7 +17,7 @@ obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o + obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o + obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o + obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o +-obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o ++obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o tosa_lcd.o + obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o + + # Support for blinky lights +Index: git/arch/arm/mach-pxa/tosa_lcd.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ git/arch/arm/mach-pxa/tosa_lcd.c 2006-11-07 23:29:25.000000000 +0000 +@@ -0,0 +1,344 @@ ++/* ++ * LCD / Backlight control code for Sharp SL-6000x (tosa) ++ * ++ * Copyright (c) 2005 Dirk Opfer ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <linux/i2c.h> ++#include <linux/fb.h> ++ ++#include <asm/mach/sharpsl_param.h> ++#include <asm/hardware.h> ++#include <asm/hardware/scoop.h> ++#include <asm/hardware/tmio.h> ++#include <asm/arch/ssp.h> ++#include <asm/arch/sharpsl.h> ++#include <asm/arch/tosa.h> ++#include <asm/arch/pxa-regs.h> ++ ++#define DAC_BASE 0x4e ++#define DAC_CH1 0 ++#define DAC_CH2 1 ++ ++#define TG_REG0_VQV 0x0001 ++#define TG_REG0_COLOR 0x0002 ++#define TG_REG0_UD 0x0004 ++#define TG_REG0_LR 0x0008 ++#define COMADJ_DEFAULT 97 ++#define TOSA_LCD_I2C_DEVICEID 0x4711 // Fixme: new value ++ ++static void tosa_lcd_tg_init(struct device *dev); ++static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode); ++static void tosa_lcd_tg_off(struct device *dev); ++static void tosa_set_backlight(int intensity); ++ ++const static struct tmio_lcd_ops tosa_tc6393_lcd_ops = { ++ .init = tosa_lcd_tg_init, ++ .tg_on = tosa_lcd_tg_on, ++ .tg_off = tosa_lcd_tg_off, ++}; ++ ++static struct platform_device *tosabl_device; ++static struct i2c_driver tosa_driver; ++static struct i2c_client* tosa_i2c_dac; ++static int initialised; ++static int comadj; ++static int bl_intensity; ++static struct ssp_dev tosa_nssp_dev; ++static struct ssp_state tosa_nssp_state; ++static spinlock_t tosa_nssp_lock; ++ ++static unsigned short normal_i2c[] = { ++ DAC_BASE, ++ I2C_CLIENT_END ++}; ++I2C_CLIENT_INSMOD; ++ ++static struct corgibl_machinfo tosa_bl_machinfo = { ++ .max_intensity = 255, ++ .default_intensity = 68, ++ .limit_mask = 0x0b, ++ .set_bl_intensity = tosa_set_backlight, ++}; ++ ++int tosa_bl_intensity(void) ++{ ++ return bl_intensity; ++} ++ ++static void pxa_nssp_output(unsigned char reg, unsigned char data) ++{ ++ unsigned long flag, dummy; ++ u32 dat = ( ((reg << 5) & 0xe0) | (data & 0x1f) ); ++ spin_lock_irqsave(&tosa_nssp_lock, flag); ++ ++ ssp_config(&tosa_nssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), 0, 0, SSCR0_SerClkDiv(128)); ++ ssp_enable(&tosa_nssp_dev); ++ ++ ssp_write_word(&tosa_nssp_dev,dat); ++ ++ /* Read null data back from device to prevent SSP overflow */ ++ ssp_read_word(&tosa_nssp_dev, &dummy); ++ ssp_disable(&tosa_nssp_dev); ++ spin_unlock_irqrestore(&tosa_nssp_lock, flag); ++ ++} ++ ++static void tosa_set_backlight(int intensity) ++{ ++ if (!tosa_i2c_dac) ++ return; ++ ++ bl_intensity = intensity; ++ /* SetBacklightDuty */ ++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH2, (unsigned char)intensity); ++ ++ /* SetBacklightVR */ ++ if (intensity) ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BL_C20MA); ++ else ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BL_C20MA); ++ ++ /* bl_enable GP04=1 otherwise GP04=0*/ ++ pxa_nssp_output(TG_GPODR2, intensity ? 0x01 : 0x00); ++} ++ ++static void tosa_lcd_tg_init(struct device *dev) ++{ ++ /* L3V On */ ++ set_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON); ++ mdelay(60); ++ ++ /* TG On */ ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_TG_ON); ++ mdelay(60); ++ ++ pxa_nssp_output(TG_TPOSCTL,0x00); /* delayed 0clk TCTL signal for VGA */ ++ pxa_nssp_output(TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */ ++} ++ ++static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode) ++{ ++ const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR; ++ pxa_nssp_output(TG_PNLCTL, value | (mode->yres == 320 ? 0 : TG_REG0_VQV)); ++ ++ /* TG LCD pannel power up */ ++ pxa_nssp_output(TG_PINICTL,0x4); ++ mdelay(50); ++ ++ /* TG LCD GVSS */ ++ pxa_nssp_output(TG_PINICTL,0x0); ++ ++ if (!initialised) ++ { ++ /* after the pannel is powered up the first time, we can access the i2c bus */ ++ /* so probe for the DAC */ ++ i2c_add_driver(&tosa_driver); ++ initialised = 1; ++ mdelay(50); ++ } ++ if (tosa_i2c_dac) ++ /* set common voltage */ ++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj); ++ ++} ++ ++static void tosa_lcd_tg_off(struct device *dev) ++{ ++ /* TG LCD VHSA off */ ++ pxa_nssp_output(TG_PINICTL,0x4); ++ mdelay(50); ++ ++ /* TG LCD signal off */ ++ pxa_nssp_output(TG_PINICTL,0x6); ++ mdelay(50); ++ ++ /* TG Off */ ++ set_tc6393_gpio(&tc6393_device.dev, TOSA_TC6393_TG_ON); ++ mdelay(100); ++ ++ /* L3V Off */ ++ reset_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON); ++} ++ ++static int tosa_detect_client(struct i2c_adapter* adapter, int address, int kind) { ++ int err = 0; ++ ++ printk("Tosa-LCD: DAC detected address:0x%2.2x\n",address); ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA )) ++ goto ERROR0; ++ ++ if (!(tosa_i2c_dac = (struct i2c_client*)kzalloc(sizeof(*tosa_i2c_dac), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ //i2c_set_clientdata(tosa_i2c_dac, data); ++ tosa_i2c_dac->addr = address; ++ tosa_i2c_dac->adapter = adapter; ++ tosa_i2c_dac->driver = &tosa_driver; ++ tosa_i2c_dac->dev.parent = &tc6393_device.dev; ++ strcpy(tosa_i2c_dac->name, "tosa lcd"); ++ if ((err = i2c_attach_client(tosa_i2c_dac))) ++ goto ERROR3; ++ ++ /* Now i2c is ready, allocate the backlight device*/ ++ tosabl_device = platform_device_alloc("corgi-bl", -1); ++ if (!tosabl_device) { ++ err = -ENOMEM; ++ goto ERROR4; ++ } ++ ++ /* set parent device */ ++ tosabl_device->dev.parent = &tosa_i2c_dac->dev; ++ tosabl_device->dev.platform_data = &tosa_bl_machinfo; ++ ++ err = platform_device_add(tosabl_device); ++ ++ if (err) ++ platform_device_put(tosabl_device); ++ ++ /* set common voltage */ ++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj); ++ ++ return 0; ++ERROR4: ++ i2c_detach_client(tosa_i2c_dac); ++ERROR3: ++ kfree(tosa_i2c_dac); ++ERROR0: ++ return err; ++} ++ ++static int tosa_attach_adapter(struct i2c_adapter* adapter) { ++ return i2c_probe(adapter, &addr_data, &tosa_detect_client); ++} ++ ++static int tosa_detach_client(struct i2c_client* client) { ++ int err; ++ ++ if ((err = i2c_detach_client(client))) { ++ printk(KERN_ERR "tosa: Cannot deregister client\n"); ++ return err; ++ } ++ kfree(client); ++ return 0; ++} ++ ++static struct i2c_driver tosa_driver={ ++ .id = TOSA_LCD_I2C_DEVICEID, ++ .attach_adapter = tosa_attach_adapter, ++ .detach_client = tosa_detach_client, ++}; ++ ++static int __init tosa_lcd_probe(struct platform_device *pdev) ++{ ++ int ret; ++ spin_lock_init(&tosa_nssp_lock); ++ ++ if (!pdev->dev.platform_data) ++ return -EINVAL; ++ ++ /* Set Common Voltage */ ++ comadj = sharpsl_param.comadj == -1 ? COMADJ_DEFAULT : sharpsl_param.comadj; ++ ++ ret=ssp_init(&tosa_nssp_dev,2,0); ++ ++ /* initialize SSP */ ++ pxa_gpio_mode(GPIO83_NSSP_TX); ++ pxa_gpio_mode(GPIO81_NSSP_CLK_OUT); ++ pxa_gpio_mode(GPIO82_NSSP_FRM_OUT); ++ ++ if (ret) ++ printk(KERN_ERR "Unable to register NSSP handler!\n"); ++ else { ++ struct tmio_lcd_ops* *tmio_ops = pdev->dev.platform_data; ++ ssp_disable(&tosa_nssp_dev); ++ initialised = 0; ++ ++ /* Set the lcd functions */ ++ *tmio_ops = (struct tmio_lcd_ops*) &tosa_tc6393_lcd_ops; ++ } ++ ++ return ret; ++} ++ ++static int tosa_lcd_remove(struct platform_device *pdev) ++{ ++ /* delete the lcd functions */ ++ struct tmio_lcd_ops* *tmio_ops = pdev->dev.platform_data; ++ *tmio_ops = NULL; ++ ++ ssp_exit(&tosa_nssp_dev); ++ ++ if (tosa_i2c_dac) { ++ i2c_detach_client(tosa_i2c_dac); ++ kfree(tosa_i2c_dac); ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++ ++static int tosa_lcd_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ ssp_flush(&tosa_nssp_dev); ++ ssp_save_state(&tosa_nssp_dev,&tosa_nssp_state); ++ return 0; ++} ++ ++static int tosa_lcd_resume(struct platform_device *pdev) ++{ ++ printk("tosa_lcd_resume\n"); ++ ssp_restore_state(&tosa_nssp_dev,&tosa_nssp_state); ++ ssp_enable(&tosa_nssp_dev); ++ printk("tosa_lcd_resume ok\n"); ++ return 0; ++} ++#else ++ ++#define tosa_lcd_suspend NULL ++#define tosa_lcd_resume NULL ++ ++#endif ++ ++ ++static struct platform_driver tosalcd_driver = { ++ .probe = tosa_lcd_probe, ++ .remove = tosa_lcd_remove, ++ .suspend = tosa_lcd_suspend, ++ .resume = tosa_lcd_resume, ++ .driver = { ++ .name = "tosa-lcd", ++ }, ++}; ++ ++static int __init tosa_lcd_init(void) ++{ ++ return platform_driver_register(&tosalcd_driver); ++} ++ ++static void __exit tosa_lcd_cleanup (void) ++{ ++ platform_driver_unregister (&tosalcd_driver); ++} ++ ++device_initcall(tosa_lcd_init); ++module_exit (tosa_lcd_cleanup); ++ ++MODULE_DESCRIPTION ("Tosa LCD device"); ++MODULE_AUTHOR ("Dirk Opfer"); ++MODULE_LICENSE ("GPL v2"); +Index: git/arch/arm/mach-pxa/tosa.c +=================================================================== +--- git.orig/arch/arm/mach-pxa/tosa.c 2006-11-07 22:13:10.000000000 +0000 ++++ git/arch/arm/mach-pxa/tosa.c 2006-11-07 23:29:38.000000000 +0000 +@@ -24,6 +24,7 @@ + #include <linux/mtd/partitions.h> + #include <linux/pm.h> + #include <linux/delay.h> ++#include <linux/fb.h> + + #include <asm/setup.h> + #include <asm/memory.h> +@@ -48,7 +49,6 @@ + + #include "generic.h" + +- + /* + * SCOOP Device + */ +@@ -345,7 +345,38 @@ static struct tmio_nand_platform_data to + .badblock_pattern = &tosa_tc6393_nand_bbt, + }; + +-extern struct tmio_lcd_platform_data tosa_tc6393_lcd_platform_data; ++static struct fb_videomode tosa_tc6393_lcd_mode[] = { ++ { ++ .xres = 480, ++ .yres = 640, ++ .pixclock = 0x002cdf00,/* PLL divisor */ ++ .left_margin = 0x004c, ++ .right_margin = 0x005b, ++ .upper_margin = 0x0001, ++ .lower_margin = 0x000d, ++ .hsync_len = 0x0002, ++ .vsync_len = 0x0001, ++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, ++ .vmode = FB_VMODE_NONINTERLACED, ++ },{ ++ .xres = 240, ++ .yres = 320, ++ .pixclock = 0x00e7f203,/* PLL divisor */ ++ .left_margin = 0x0024, ++ .right_margin = 0x002f, ++ .upper_margin = 0x0001, ++ .lower_margin = 0x000d, ++ .hsync_len = 0x0002, ++ .vsync_len = 0x0001, ++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, ++ .vmode = FB_VMODE_NONINTERLACED, ++}}; ++ ++struct tmio_lcd_platform_data tosa_tc6393_lcd_platform_data = { ++ .ops = NULL, ++ .modelist = tosa_tc6393_lcd_mode, ++ .num_modes = ARRAY_SIZE(tosa_tc6393_lcd_mode), ++}; + + static struct tmio_cell tosa_tc6393_cells[] = { + { +@@ -384,6 +415,19 @@ struct platform_device tc6393_device = { + .num_resources = ARRAY_SIZE(tc6393_resources), + .resource = tc6393_resources, + }; ++EXPORT_SYMBOL (tc6393_device); ++ ++/* ++ * Tosa LCD / Backlight stuff ++ */ ++static struct platform_device tosalcd_device = { ++ .name = "tosa-lcd", ++ .id = -1, ++ .dev = { ++ .parent = &tc6393_device.dev, ++ .platform_data = &tosa_tc6393_lcd_platform_data.ops, ++ }, ++}; + + static struct platform_device *devices[] __initdata = { + &tosascoop_device, +@@ -391,6 +435,7 @@ static struct platform_device *devices[] + &tosakbd_device, + &tosaled_device, + &tc6393_device, ++ &tosalcd_device, + }; + + static void tosa_poweroff(void) +Index: git/arch/arm/mach-pxa/Kconfig +=================================================================== +--- git.orig/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:10.000000000 +0000 ++++ git/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:10.000000000 +0000 +@@ -129,7 +129,10 @@ config MACH_TOSA + bool "Enable Sharp SL-6000x (Tosa) Support" + depends PXA_SHARPSL_25x + select TOSHIBA_TC6393XB +- select SHARPSL_PM ++ select I2C ++ select I2C_PXA ++ select SHARPSL_PM ++ select PXA_SSP + + config PXA25x + bool diff --git a/packages/linux/linux-rp-2.6.22/usb-ohci-hooks-r2.patch b/packages/linux/linux-rp-2.6.22/usb-ohci-hooks-r2.patch new file mode 100644 index 0000000000..3656e98f0c --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/usb-ohci-hooks-r2.patch @@ -0,0 +1,180 @@ +Index: linux-2.6.15/drivers/usb/host/ohci.h +=================================================================== +--- linux-2.6.15.orig/drivers/usb/host/ohci.h ++++ linux-2.6.15/drivers/usb/host/ohci.h +@@ -336,6 +336,23 @@ typedef struct urb_priv { + // sizeof (struct td) ~= 64 == 2^6 ... + #define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 6)) % TD_HASH_SIZE) + ++struct ohci_hcd; ++ ++/* ++ * Hooks to support controllers that must resort to a PIO-ish ++ * implementation because they only dma with on-chip memory. ++ */ ++struct ohci_ops { ++ void* (*dma_pool_alloc) (struct dma_pool *pool, gfp_t mem_flags, ++ dma_addr_t *handle); ++ void (*dma_pool_free) (struct dma_pool *pool, void *vaddr, ++ dma_addr_t addr); ++ ++ void (*td_fill) (struct ohci_hcd *ohci, u32 info, dma_addr_t data, ++ int len, struct urb *urb, int index); ++ void (*td_done) (struct ohci_hcd *ohci, struct urb *urb, ++ struct td *td); ++}; + + /* + * This is the full ohci controller description +@@ -346,6 +363,7 @@ typedef struct urb_priv { + + struct ohci_hcd { + spinlock_t lock; ++ const struct ohci_ops *ops; + + /* + * I/O memory used to communicate with the HC (dma-consistent) +Index: linux-2.6.15/drivers/usb/host/ohci-mem.c +=================================================================== +--- linux-2.6.15.orig/drivers/usb/host/ohci-mem.c ++++ linux-2.6.15/drivers/usb/host/ohci-mem.c +@@ -21,10 +21,13 @@ + * No memory seen by this driver is pagable. + */ + ++static const struct ohci_ops ohci_ops; ++ + /*-------------------------------------------------------------------------*/ + + static void ohci_hcd_init (struct ohci_hcd *ohci) + { ++ ohci->ops = &ohci_ops; + ohci->next_statechange = jiffies; + spin_lock_init (&ohci->lock); + INIT_LIST_HEAD (&ohci->pending); +@@ -88,7 +91,7 @@ td_alloc (struct ohci_hcd *hc, gfp_t mem + dma_addr_t dma; + struct td *td; + +- td = dma_pool_alloc (hc->td_cache, mem_flags, &dma); ++ td = hc->ops->dma_pool_alloc (hc->td_cache, mem_flags, &dma); + if (td) { + /* in case hc fetches it, make it look dead */ + memset (td, 0, sizeof *td); +@@ -110,7 +113,7 @@ td_free (struct ohci_hcd *hc, struct td + *prev = td->td_hash; + else if ((td->hwINFO & cpu_to_hc32(hc, TD_DONE)) != 0) + ohci_dbg (hc, "no hash for td %p\n", td); +- dma_pool_free (hc->td_cache, td, td->td_dma); ++ hc->ops->dma_pool_free (hc->td_cache, td, td->td_dma); + } + + /*-------------------------------------------------------------------------*/ +@@ -122,7 +125,7 @@ ed_alloc (struct ohci_hcd *hc, gfp_t mem + dma_addr_t dma; + struct ed *ed; + +- ed = dma_pool_alloc (hc->ed_cache, mem_flags, &dma); ++ ed = hc->ops->dma_pool_alloc (hc->ed_cache, mem_flags, &dma); + if (ed) { + memset (ed, 0, sizeof (*ed)); + INIT_LIST_HEAD (&ed->td_list); +@@ -134,6 +137,6 @@ ed_alloc (struct ohci_hcd *hc, gfp_t mem + static void + ed_free (struct ohci_hcd *hc, struct ed *ed) + { +- dma_pool_free (hc->ed_cache, ed, ed->dma); ++ hc->ops->dma_pool_free (hc->ed_cache, ed, ed->dma); + } + +Index: linux-2.6.15/drivers/usb/host/ohci-q.c +=================================================================== +--- linux-2.6.15.orig/drivers/usb/host/ohci-q.c ++++ linux-2.6.15/drivers/usb/host/ohci-q.c +@@ -629,7 +629,7 @@ static void td_submit_urb ( + : TD_T_TOGGLE | TD_CC | TD_DP_IN; + /* TDs _could_ transfer up to 8K each */ + while (data_len > 4096) { +- td_fill (ohci, info, data, 4096, urb, cnt); ++ ohci->ops->td_fill (ohci, info, data, 4096, urb, cnt); + data += 4096; + data_len -= 4096; + cnt++; +@@ -637,11 +637,11 @@ static void td_submit_urb ( + /* maybe avoid ED halt on final TD short read */ + if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) + info |= TD_R; +- td_fill (ohci, info, data, data_len, urb, cnt); ++ ohci->ops->td_fill (ohci, info, data, data_len, urb, cnt); + cnt++; + if ((urb->transfer_flags & URB_ZERO_PACKET) + && cnt < urb_priv->length) { +- td_fill (ohci, info, 0, 0, urb, cnt); ++ ohci->ops->td_fill (ohci, info, 0, 0, urb, cnt); + cnt++; + } + /* maybe kickstart bulk list */ +@@ -656,17 +656,18 @@ static void td_submit_urb ( + */ + case PIPE_CONTROL: + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; +- td_fill (ohci, info, urb->setup_dma, 8, urb, cnt++); ++ ohci->ops->td_fill (ohci, info, urb->setup_dma, 8, urb, cnt++); + if (data_len > 0) { + info = TD_CC | TD_R | TD_T_DATA1; + info |= is_out ? TD_DP_OUT : TD_DP_IN; + /* NOTE: mishandles transfers >8K, some >4K */ +- td_fill (ohci, info, data, data_len, urb, cnt++); ++ ohci->ops->td_fill (ohci, info, data, data_len, ++ urb, cnt++); + } + info = (is_out || data_len == 0) + ? TD_CC | TD_DP_IN | TD_T_DATA1 + : TD_CC | TD_DP_OUT | TD_T_DATA1; +- td_fill (ohci, info, data, 0, urb, cnt++); ++ ohci->ops->td_fill (ohci, info, data, 0, urb, cnt++); + /* maybe kickstart control list */ + wmb (); + ohci_writel (ohci, OHCI_CLF, &ohci->regs->cmdstatus); +@@ -685,7 +686,7 @@ static void td_submit_urb ( + // a 2^16 iso range, vs other HCs max of 2^10) + frame += cnt * urb->interval; + frame &= 0xffff; +- td_fill (ohci, TD_CC | TD_ISO | frame, ++ ohci->ops->td_fill (ohci, TD_CC | TD_ISO | frame, + data + urb->iso_frame_desc [cnt].offset, + urb->iso_frame_desc [cnt].length, urb, cnt); + } +@@ -788,6 +789,14 @@ static void td_done (struct ohci_hcd *oh + } + } + ++/* default operations for most HCs */ ++static const struct ohci_ops ohci_ops = { ++ .dma_pool_alloc = dma_pool_alloc, ++ .dma_pool_free = dma_pool_free, ++ .td_fill = td_fill, ++ .td_done = td_done, ++}; ++ + /*-------------------------------------------------------------------------*/ + + static inline struct td * +@@ -984,7 +993,7 @@ rescan_this: + *prev = td->hwNextTD | savebits; + + /* HC may have partly processed this TD */ +- td_done (ohci, urb, td); ++ ohci->ops->td_done (ohci, urb, td); + urb_priv->td_cnt++; + + /* if URB is done, clean up */ +@@ -1079,7 +1088,7 @@ dl_done_list (struct ohci_hcd *ohci, str + struct ed *ed = td->ed; + + /* update URB's length and status from TD */ +- td_done (ohci, urb, td); ++ ohci->ops->td_done (ohci, urb, td); + urb_priv->td_cnt++; + + /* If all this urb's TDs are done, call complete() */ diff --git a/packages/linux/linux-rp-2.6.22/wm9712-reset-loop-r2.patch b/packages/linux/linux-rp-2.6.22/wm9712-reset-loop-r2.patch new file mode 100644 index 0000000000..78e81ea83a --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/wm9712-reset-loop-r2.patch @@ -0,0 +1,44 @@ + sound/soc/codecs/wm9712.c | 28 ++++++++++++++++++---------- + 1 file changed, 18 insertions(+), 10 deletions(-) + +Index: git/sound/soc/codecs/wm9712.c +=================================================================== +--- git.orig/sound/soc/codecs/wm9712.c 2006-11-07 22:10:01.000000000 +0000 ++++ git/sound/soc/codecs/wm9712.c 2006-11-07 22:11:50.000000000 +0000 +@@ -618,18 +618,26 @@ static int wm9712_dapm_event(struct snd_ + + static int wm9712_reset(struct snd_soc_codec *codec, int try_warm) + { +- if (try_warm && soc_ac97_ops.warm_reset) { +- soc_ac97_ops.warm_reset(codec->ac97); +- if (!(ac97_read(codec, 0) & 0x8000)) +- return 1; +- } ++ int retry = 3; + +- soc_ac97_ops.reset(codec->ac97); +- if (ac97_read(codec, 0) & 0x8000) +- goto err; +- return 0; ++ while (retry--) ++ { ++ if(try_warm && soc_ac97_ops.warm_reset) { ++ soc_ac97_ops.warm_reset(codec->ac97); ++ if(ac97_read(codec, 0) & 0x8000) ++ continue; ++ else ++ return 1; ++ } ++ ++ soc_ac97_ops.reset(codec->ac97); ++ if(ac97_read(codec, 0) & 0x8000) ++ continue; ++ else ++ return 0; ++ ++ } + +-err: + printk(KERN_ERR "WM9712 AC97 reset failed\n"); + return -EIO; + } diff --git a/packages/linux/linux-rp-2.6.22/wm9712-suspend-cold-res-r2.patch b/packages/linux/linux-rp-2.6.22/wm9712-suspend-cold-res-r2.patch new file mode 100644 index 0000000000..5179b47cc4 --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/wm9712-suspend-cold-res-r2.patch @@ -0,0 +1,16 @@ + sound/soc/codecs/wm9712.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Index: git/sound/soc/codecs/wm9712.c +=================================================================== +--- git.orig/sound/soc/codecs/wm9712.c 2006-11-07 21:57:34.000000000 +0000 ++++ git/sound/soc/codecs/wm9712.c 2006-11-07 21:59:30.000000000 +0000 +@@ -651,7 +651,7 @@ static int wm9712_soc_resume(struct plat + int i, ret; + u16 *cache = codec->reg_cache; + +- ret = wm9712_reset(codec, 1); ++ ret = wm9712_reset(codec, 0); + if (ret < 0){ + printk(KERN_ERR "could not reset AC97 codec\n"); + return ret; diff --git a/packages/linux/linux-rp-2.6.22/wm97xx-lg13-r0-fix-r0.patch b/packages/linux/linux-rp-2.6.22/wm97xx-lg13-r0-fix-r0.patch new file mode 100644 index 0000000000..5ad0d8703d --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/wm97xx-lg13-r0-fix-r0.patch @@ -0,0 +1,128 @@ + drivers/input/power.c | 2 +- + drivers/input/touchscreen/Kconfig | 2 +- + drivers/input/touchscreen/wm97xx-core.c | 35 ++++++++++++++++--------------- + include/linux/wm97xx.h | 2 +- + 4 files changed, 21 insertions(+), 20 deletions(-) + +diff --git a/drivers/input/power.c b/drivers/input/power.c +index 4443e34..7aac875 100644 +--- a/drivers/input/power.c ++++ b/drivers/input/power.c +@@ -156,7 +156,7 @@ static void power_event(struct input_handle *handle, unsigned int type, + } + } + +-static struct input_handle *power_connect(struct input_handler *handler, ++static int power_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) + { +diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig +index 6862e8f..9b532e9 100644 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -247,7 +247,7 @@ config TOUCHSCREEN_TSC2101 + + config TOUCHSCREEN_WM97XX + tristate "Support for WM97xx AC97 touchscreen controllers" +- depends SND_AC97_BUS ++ depends AC97_BUS + + choice + prompt "WM97xx codec type" +diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c +index 9b2710e..d3ce3f3 100644 +--- a/drivers/input/touchscreen/wm97xx-core.c ++++ b/drivers/input/touchscreen/wm97xx-core.c +@@ -84,6 +84,7 @@ + #include <linux/bitops.h> + #include <linux/workqueue.h> + #include <linux/device.h> ++#include <linux/freezer.h> + #include <linux/wm97xx.h> + #include <asm/uaccess.h> + #include <asm/io.h> +@@ -241,14 +242,15 @@ WM97XX_STATUS_ATTR(gpio); + + static int wm97xx_sys_add(struct device *dev) + { ++ int err; + if (aux_sys) { +- device_create_file(dev, &dev_attr_aux1); +- device_create_file(dev, &dev_attr_aux2); +- device_create_file(dev, &dev_attr_aux3); +- device_create_file(dev, &dev_attr_aux4); ++ err = device_create_file(dev, &dev_attr_aux1); ++ err = device_create_file(dev, &dev_attr_aux2); ++ err = device_create_file(dev, &dev_attr_aux3); ++ err = device_create_file(dev, &dev_attr_aux4); + } + if (status_sys) +- device_create_file(dev, &dev_attr_gpio); ++ err = device_create_file(dev, &dev_attr_gpio); + return 0; + } + +@@ -366,12 +368,12 @@ void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir, + + /* + * Handle a pen down interrupt. +- */ +-static void wm97xx_pen_irq_worker(void *ptr) +-{ +- struct wm97xx *wm = (struct wm97xx *) ptr; +- +- /* do we need to enable the touch panel reader */ ++ */ ++static void wm97xx_pen_irq_worker(struct work_struct *work) ++{ ++ struct wm97xx *wm = container_of(work, struct wm97xx, pen_event_work); ++ ++ /* do we need to enable the touch panel reader */ + if (wm->id == WM9705_ID2) { + if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN) + wm->pen_is_down = 1; +@@ -411,9 +413,8 @@ static void wm97xx_pen_irq_worker(void *ptr) + * We have to disable the codec interrupt in the handler because it can + * take upto 1ms to clear the interrupt source. The interrupt is then enabled + * again in the slow handler when the source has been cleared. +- */ +-static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id, +- struct pt_regs *regs) ++ */ ++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id) + { + struct wm97xx *wm = (struct wm97xx *) dev_id; + disable_irq(wm->pen_irq); +@@ -428,15 +429,15 @@ static int wm97xx_init_pen_irq(struct wm97xx *wm) + { + u16 reg; + +- INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker, wm); +- if ((wm->pen_irq_workq = ++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker); ++ if ((wm->pen_irq_workq = + create_singlethread_workqueue("kwm97pen")) == NULL) { + err("could not create pen irq work queue"); + wm->pen_irq = 0; + return -EINVAL; + } + +- if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) { ++ if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, IRQF_SHARED, "wm97xx-pen", wm)) { + err("could not register codec pen down interrupt, will poll for pen down"); + destroy_workqueue(wm->pen_irq_workq); + wm->pen_irq = 0; +diff --git a/include/linux/wm97xx.h b/include/linux/wm97xx.h +index b1c1740..a9bd57e 100644 +--- a/include/linux/wm97xx.h ++++ b/include/linux/wm97xx.h +@@ -243,7 +243,7 @@ struct wm97xx { + u16 dig_save[3]; /* saved during aux reading */ + struct wm97xx_codec_drv *codec; /* attached codec driver*/ + struct input_dev* input_dev; /* touchscreen input device */ +- ac97_t *ac97; /* ALSA codec access */ ++ struct snd_ac97 *ac97; /* ALSA codec access */ + struct device *dev; /* ALSA device */ + struct device *battery_dev; + struct device *touch_dev; diff --git a/packages/linux/linux-rp-2.6.22/wm97xx-lg13-r0.patch b/packages/linux/linux-rp-2.6.22/wm97xx-lg13-r0.patch new file mode 100644 index 0000000000..c918c5daff --- /dev/null +++ b/packages/linux/linux-rp-2.6.22/wm97xx-lg13-r0.patch @@ -0,0 +1,2899 @@ +Index: linux-2.6.17/drivers/input/touchscreen/Kconfig +=================================================================== +--- linux-2.6.17.orig/drivers/input/touchscreen/Kconfig 2006-09-19 20:35:35.060495500 +0200 ++++ linux-2.6.17/drivers/input/touchscreen/Kconfig 2006-09-19 20:36:47.965051750 +0200 +@@ -121,4 +121,57 @@ config TOUCHSCREEN_TSC2101 + To compile this driver as a module, choose M here: the + module will be called ads7846_ts. + ++config TOUCHSCREEN_WM97XX ++ tristate "Support for WM97xx AC97 touchscreen controllers" ++ depends SND_AC97_BUS ++ ++choice ++ prompt "WM97xx codec type" ++ ++config TOUCHSCREEN_WM9705 ++ bool "WM9705 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have the wm9705 touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9705. ++ ++config TOUCHSCREEN_WM9712 ++ bool "WM9712 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have the wm9712 touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9712. ++ ++config TOUCHSCREEN_WM9713 ++ bool "WM9713 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have the wm9713 touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9713. ++ ++endchoice ++ ++config TOUCHSCREEN_WM97XX_PXA ++ tristate "WM97xx PXA accelerated touch" ++ depends on TOUCHSCREEN_WM97XX && ARCH_PXA ++ help ++ Say Y here for continuous mode touch on the PXA ++ ++ If unsure, say N ++ ++ To compile this driver as a module, choose M here: the ++ module will be called pxa-wm97xx ++ + endif +Index: linux-2.6.17/drivers/input/touchscreen/Makefile +=================================================================== +--- linux-2.6.17.orig/drivers/input/touchscreen/Makefile 2006-09-19 20:35:35.072496250 +0200 ++++ linux-2.6.17/drivers/input/touchscreen/Makefile 2006-09-19 20:37:40.540337500 +0200 +@@ -4,6 +4,8 @@ + + # Each configuration option enables a list of files. + ++wm97xx-ts-objs := wm97xx-core.o ++ + obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o + obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o + obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o +@@ -13,3 +15,16 @@ obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtou + obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o + obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o + obj-$(CONFIG_TOUCHSCREEN_TSC2101) += tsc2101_ts.o ++obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o ++obj-$(CONFIG_TOUCHSCREEN_WM97XX_PXA) += pxa-wm97xx.o ++ ++ifeq ($(CONFIG_TOUCHSCREEN_WM9713),y) ++wm97xx-ts-objs += wm9713.o ++endif ++ ++ifeq ($(CONFIG_TOUCHSCREEN_WM9712),y) ++wm97xx-ts-objs += wm9712.o ++endif ++ifeq ($(CONFIG_TOUCHSCREEN_WM9705),y) ++wm97xx-ts-objs += wm9705.o ++endif +Index: linux-2.6.17/drivers/input/touchscreen/pxa-wm97xx.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/pxa-wm97xx.c 2006-09-19 20:36:47.965051750 +0200 +@@ -0,0 +1,289 @@ ++/* ++ * pxa-wm97xx.c -- pxa-wm97xx Continuous Touch screen driver for ++ * Wolfson WM97xx AC97 Codecs. ++ * ++ * Copyright 2004 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Notes: ++ * This is a wm97xx extended touch driver to capture touch ++ * data in a continuous manner on the Intel XScale archictecture ++ * ++ * Features: ++ * - codecs supported:- WM9705, WM9712, WM9713 ++ * - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x ++ * ++ * Revision history ++ * 18th Aug 2004 Initial version. ++ * 26th Jul 2005 Improved continous read back and added FIFO flushing. ++ * 06th Sep 2005 Mike Arthur <linux@wolfsonmicro.com> ++ * Moved to using the wm97xx bus ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/irq.h> ++#include <linux/wm97xx.h> ++#include <asm/io.h> ++#include <asm/arch/pxa-regs.h> ++ ++#define VERSION "0.13" ++ ++struct continuous { ++ u16 id; /* codec id */ ++ u8 code; /* continuous code */ ++ u8 reads; /* number of coord reads per read cycle */ ++ u32 speed; /* number of coords per second */ ++}; ++ ++#define WM_READS(sp) ((sp / HZ) + 1) ++ ++static const struct continuous cinfo[] = { ++ {WM9705_ID2, 0, WM_READS(94), 94}, ++ {WM9705_ID2, 1, WM_READS(188), 188}, ++ {WM9705_ID2, 2, WM_READS(375), 375}, ++ {WM9705_ID2, 3, WM_READS(750), 750}, ++ {WM9712_ID2, 0, WM_READS(94), 94}, ++ {WM9712_ID2, 1, WM_READS(188), 188}, ++ {WM9712_ID2, 2, WM_READS(375), 375}, ++ {WM9712_ID2, 3, WM_READS(750), 750}, ++ {WM9713_ID2, 0, WM_READS(94), 94}, ++ {WM9713_ID2, 1, WM_READS(120), 120}, ++ {WM9713_ID2, 2, WM_READS(154), 154}, ++ {WM9713_ID2, 3, WM_READS(188), 188}, ++}; ++ ++/* continuous speed index */ ++static int sp_idx = 0; ++static u16 last = 0, tries = 0; ++ ++/* ++ * Pen sampling frequency (Hz) in continuous mode. ++ */ ++static int cont_rate = 200; ++module_param(cont_rate, int, 0); ++MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)"); ++ ++/* ++ * Pen down detection. ++ * ++ * This driver can either poll or use an interrupt to indicate a pen down ++ * event. If the irq request fails then it will fall back to polling mode. ++ */ ++static int pen_int = 1; ++module_param(pen_int, int, 0); ++MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)"); ++ ++/* ++ * Pressure readback. ++ * ++ * Set to 1 to read back pen down pressure ++ */ ++static int pressure = 0; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)"); ++ ++/* ++ * AC97 touch data slot. ++ * ++ * Touch screen readback data ac97 slot ++ */ ++static int ac97_touch_slot = 5; ++module_param(ac97_touch_slot, int, 0); ++MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number"); ++ ++ ++/* flush AC97 slot 5 FIFO on pxa machines */ ++#ifdef CONFIG_PXA27x ++void wm97xx_acc_pen_up (struct wm97xx* wm) ++{ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ ++ while (MISR & (1 << 2)) ++ MODR; ++} ++#else ++void wm97xx_acc_pen_up (struct wm97xx* wm) ++{ ++ int count = 16; ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ ++ while (count < 16) { ++ MODR; ++ count--; ++ } ++} ++#endif ++ ++int wm97xx_acc_pen_down (struct wm97xx* wm) ++{ ++ u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES; ++ int reads = 0; ++ ++ /* data is never immediately available after pen down irq */ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ ++ if (tries > 5){ ++ tries = 0; ++ return RC_PENUP; ++ } ++ ++ x = MODR; ++ if (x == last) { ++ tries++; ++ return RC_AGAIN; ++ } ++ last = x; ++ do { ++ if (reads) ++ x= MODR; ++ y= MODR; ++ if (pressure) ++ p = MODR; ++ ++ /* are samples valid */ ++ if ((x & 0x7000) != WM97XX_ADCSEL_X || ++ (y & 0x7000) != WM97XX_ADCSEL_Y || ++ (p & 0x7000) != WM97XX_ADCSEL_PRES) ++ goto up; ++ ++ /* coordinate is good */ ++ tries = 0; ++ //printk("x %x y %x p %x\n", x,y,p); ++ input_report_abs (wm->input_dev, ABS_X, x & 0xfff); ++ input_report_abs (wm->input_dev, ABS_Y, y & 0xfff); ++ input_report_abs (wm->input_dev, ABS_PRESSURE, p & 0xfff); ++ input_sync (wm->input_dev); ++ reads++; ++ } while (reads < cinfo[sp_idx].reads); ++up: ++ return RC_PENDOWN | RC_AGAIN; ++} ++ ++int wm97xx_acc_startup(struct wm97xx* wm) ++{ ++ int idx = 0; ++ ++ /* check we have a codec */ ++ if (wm->ac97 == NULL) ++ return -ENODEV; ++ ++ /* Go you big red fire engine */ ++ for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) { ++ if (wm->id != cinfo[idx].id) ++ continue; ++ sp_idx = idx; ++ if (cont_rate <= cinfo[idx].speed) ++ break; ++ } ++ wm->acc_rate = cinfo[sp_idx].code; ++ wm->acc_slot = ac97_touch_slot; ++ printk(KERN_INFO "pxa2xx accelerated touchscreen driver, %d samples (sec)\n", ++ cinfo[sp_idx].speed); ++ ++ /* codec specific irq config */ ++ if (pen_int) { ++ switch (wm->id) { ++ case WM9705_ID2: ++ wm->pen_irq = IRQ_GPIO(4); ++ set_irq_type(IRQ_GPIO(4), IRQT_BOTHEDGE); ++ break; ++ case WM9712_ID2: ++ case WM9713_ID2: ++ /* enable pen down interrupt */ ++ /* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */ ++ wm->pen_irq = MAINSTONE_AC97_IRQ; ++ wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, ++ WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_STICKY, WM97XX_GPIO_WAKE); ++ wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT, ++ WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_NOTSTICKY, WM97XX_GPIO_NOWAKE); ++ break; ++ default: ++ printk(KERN_WARNING "pen down irq not supported on this device\n"); ++ pen_int = 0; ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++void wm97xx_acc_shutdown(struct wm97xx* wm) ++{ ++ /* codec specific deconfig */ ++ if (pen_int) { ++ switch (wm->id & 0xffff) { ++ case WM9705_ID2: ++ wm->pen_irq = 0; ++ break; ++ case WM9712_ID2: ++ case WM9713_ID2: ++ /* disable interrupt */ ++ wm->pen_irq = 0; ++ break; ++ } ++ } ++} ++ ++static struct wm97xx_mach_ops pxa_mach_ops = { ++ .acc_enabled = 1, ++ .acc_pen_up = wm97xx_acc_pen_up, ++ .acc_pen_down = wm97xx_acc_pen_down, ++ .acc_startup = wm97xx_acc_startup, ++ .acc_shutdown = wm97xx_acc_shutdown, ++}; ++ ++int pxa_wm97xx_probe(struct device *dev) ++{ ++ struct wm97xx *wm = dev->driver_data; ++ return wm97xx_register_mach_ops (wm, &pxa_mach_ops); ++} ++ ++int pxa_wm97xx_remove(struct device *dev) ++{ ++ struct wm97xx *wm = dev->driver_data; ++ wm97xx_unregister_mach_ops (wm); ++ return 0; ++} ++ ++static struct device_driver pxa_wm97xx_driver = { ++ .name = "wm97xx-touchscreen", ++ .bus = &wm97xx_bus_type, ++ .owner = THIS_MODULE, ++ .probe = pxa_wm97xx_probe, ++ .remove = pxa_wm97xx_remove ++}; ++ ++static int __init pxa_wm97xx_init(void) ++{ ++ return driver_register(&pxa_wm97xx_driver); ++} ++ ++static void __exit pxa_wm97xx_exit(void) ++{ ++ driver_unregister(&pxa_wm97xx_driver); ++} ++ ++module_init(pxa_wm97xx_init); ++module_exit(pxa_wm97xx_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>"); ++MODULE_DESCRIPTION("wm97xx continuous touch driver for pxa2xx"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/drivers/input/touchscreen/wm9705.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/wm9705.c 2006-09-19 20:36:47.969052000 +0200 +@@ -0,0 +1,360 @@ ++/* ++ * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Revision history ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added pre and post sample calls. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9705_VERSION "0.62" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil = 0; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 4; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Pen detect comparator threshold. ++ * ++ * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold ++ * i.e. 1 = Vmid/15 threshold ++ * 15 = Vmid/1 threshold ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down events. ++ */ ++static int pdd = 8; ++module_param(pdd, int, 0); ++MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold"); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask = 0; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, // 1 AC97 Link frames ++ 42, // 2 ++ 84, // 4 ++ 167, // 8 ++ 333, // 16 ++ 667, // 32 ++ 1000, // 48 ++ 1333, // 64 ++ 2000, // 96 ++ 2667, // 128 ++ 3333, // 160 ++ 4000, // 192 ++ 4667, // 224 ++ 5333, // 256 ++ 6000, // 288 ++ 0 // No delay, switch matrix always on ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay (3 * AC97_LINK_FRAME + delay_table [d]); ++} ++ ++/* ++ * set up the physical settings of the WM9705 ++ */ ++static void init_wm9705_phy(struct wm97xx* wm) ++{ ++ u16 dig1 = 0, dig2 = WM97XX_RPR; ++ ++ /* ++ * mute VIDEO and AUX as they share X and Y touchscreen ++ * inputs on the WM9705 ++ */ ++ wm97xx_reg_write(wm, AC97_AUX, 0x8000); ++ wm97xx_reg_write(wm, AC97_VIDEO, 0x8000); ++ ++ /* touchpanel pressure current*/ ++ if (pil == 2) { ++ dig2 |= WM9705_PIL; ++ dbg("setting pressure measurement current to 400uA."); ++ } else if (pil) ++ dbg("setting pressure measurement current to 200uA."); ++ if(!pil) ++ pressure = 0; ++ ++ /* polling mode sample settling delay */ ++ if (delay!=4) { ++ if (delay < 0 || delay > 15) { ++ dbg("supplied delay out of range."); ++ delay = 4; ++ } ++ } ++ dig1 &= 0xff0f; ++ dig1 |= WM97XX_DELAY(delay); ++ dbg("setting adc sample delay to %d u Secs.", delay_table[delay]); ++ ++ /* WM9705 pdd */ ++ dig2 |= (pdd & 0x000f); ++ dbg("setting pdd to Vmid/%d", 1 - (pdd & 0x000f)); ++ ++ /* mask */ ++ dig2 |= ((mask & 0x3) << 4); ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++} ++ ++static int wm9705_digitiser_ioctl(struct wm97xx* wm, int cmd) ++{ ++ switch(cmd) { ++ case WM97XX_DIG_START: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ break; ++ case WM97XX_DIG_STOP: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] & ~WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_AUX_PREPARE: ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_DIG_RESTORE: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); ++ break; ++ case WM97XX_PHY_INIT: ++ init_wm9705_phy(wm); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static inline int is_pden (struct wm97xx* wm) ++{ ++ return wm->dig[2] & WM9705_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9705 adc in polling mode. ++ */ ++static int wm9705_poll_sample (struct wm97xx* wm, int adcsel, int *sample) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = ((adcsel & 0x7fff) + 3) << 12; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay (delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { ++ dbg ("adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSEL_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Sample the WM9705 touchscreen in polling mode ++ */ ++static int wm9705_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID) ++ return rc; ++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID) ++ return rc; ++ if (pil) { ++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9705 continuous mode, i.e. touch data is streamed across an AC97 slot ++ */ ++static int wm9705_acc_enable (struct wm97xx* wm, int enable) ++{ ++ u16 dig1, dig2; ++ int ret = 0; ++ ++ dig1 = wm->dig[1]; ++ dig2 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | ++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK); ++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | ++ WM97XX_DELAY (delay) | ++ WM97XX_SLT (wm->acc_slot) | ++ WM97XX_RATE (wm->acc_rate); ++ if (pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ dig2 |= WM9705_PDEN; ++ } else { ++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); ++ dig2 &= ~WM9705_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++ return ret; ++} ++ ++struct wm97xx_codec_drv wm97xx_codec = { ++ .id = WM9705_ID2, ++ .name = "wm9705", ++ .poll_sample = wm9705_poll_sample, ++ .poll_touch = wm9705_poll_touch, ++ .acc_enable = wm9705_acc_enable, ++ .digitiser_ioctl = wm9705_digitiser_ioctl, ++}; ++ ++EXPORT_SYMBOL_GPL(wm97xx_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM9705 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/drivers/input/touchscreen/wm9712.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/wm9712.c 2006-09-19 20:36:47.969052000 +0200 +@@ -0,0 +1,464 @@ ++/* ++ * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Revision history ++ * 4th Jul 2005 Initial version. ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added pre and post sample calls. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9712_VERSION "0.61" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set internal pull up for pen detect. ++ * ++ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) ++ * i.e. pull up resistance = 64k Ohms / rpu. ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down event. ++ */ ++static int rpu = 3; ++module_param(rpu, int, 0); ++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil = 0; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 3; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Set five_wire = 1 to use a 5 wire touchscreen. ++ * ++ * NOTE: Five wire mode does not allow for readback of pressure. ++ */ ++static int five_wire; ++module_param(five_wire, int, 0); ++MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen."); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask = 0; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * Coordinate Polling Enable. ++ * ++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together ++ * for every poll. ++ */ ++static int coord = 0; ++module_param(coord, int, 0); ++MODULE_PARM_DESC(coord, "Polling coordinate mode"); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, // 1 AC97 Link frames ++ 42, // 2 ++ 84, // 4 ++ 167, // 8 ++ 333, // 16 ++ 667, // 32 ++ 1000, // 48 ++ 1333, // 64 ++ 2000, // 96 ++ 2667, // 128 ++ 3333, // 160 ++ 4000, // 192 ++ 4667, // 224 ++ 5333, // 256 ++ 6000, // 288 ++ 0 // No delay, switch matrix always on ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay (3 * AC97_LINK_FRAME + delay_table [d]); ++} ++ ++/* ++ * set up the physical settings of the WM9712 ++ */ ++static void init_wm9712_phy(struct wm97xx* wm) ++{ ++ u16 dig1 = 0; ++ u16 dig2 = WM97XX_RPR | WM9712_RPU(1); ++ ++ /* WM9712 rpu */ ++ if (rpu) { ++ dig2 &= 0xffc0; ++ dig2 |= WM9712_RPU(rpu); ++ dbg("setting pen detect pull-up to %d Ohms",64000 / rpu); ++ } ++ ++ /* touchpanel pressure current*/ ++ if (pil == 2) { ++ dig2 |= WM9712_PIL; ++ dbg("setting pressure measurement current to 400uA."); ++ } else if (pil) ++ dbg("setting pressure measurement current to 200uA."); ++ if(!pil) ++ pressure = 0; ++ ++ /* WM9712 five wire */ ++ if (five_wire) { ++ dig2 |= WM9712_45W; ++ dbg("setting 5-wire touchscreen mode."); ++ } ++ ++ /* polling mode sample settling delay */ ++ if (delay < 0 || delay > 15) { ++ dbg("supplied delay out of range."); ++ delay = 4; ++ } ++ dig1 &= 0xff0f; ++ dig1 |= WM97XX_DELAY(delay); ++ dbg("setting adc sample delay to %d u Secs.", delay_table[delay]); ++ ++ /* mask */ ++ dig2 |= ((mask & 0x3) << 6); ++ if (mask) { ++ u16 reg; ++ /* Set GPIO4 as Mask Pin*/ ++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4); ++ } ++ ++ /* wait - coord mode */ ++ if(coord) ++ dig2 |= WM9712_WAIT; ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++} ++ ++static int wm9712_digitiser_ioctl(struct wm97xx* wm, int cmd) ++{ ++ u16 dig2 = wm->dig[2]; ++ ++ switch(cmd) { ++ case WM97XX_DIG_START: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ break; ++ case WM97XX_DIG_STOP: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 & ~WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_AUX_PREPARE: ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_DIG_RESTORE: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); ++ break; ++ case WM97XX_PHY_INIT: ++ init_wm9712_phy(wm); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static inline int is_pden (struct wm97xx* wm) ++{ ++ return wm->dig[2] & WM9712_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9712 adc in polling mode. ++ */ ++static int wm9712_poll_sample (struct wm97xx* wm, int adcsel, int *sample) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = ((adcsel & 0x7fff) + 3) << 12; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay (delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { ++ dbg ("adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSEL_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Read a coord from the WM9712 adc in polling mode. ++ */ ++static int wm9712_poll_coord (struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, ++ WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion and read x */ ++ poll_delay(delay); ++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ /* read back y data */ ++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (pil) ++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ else ++ data->p = DEFAULT_PRESSURE; ++ ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ /* check we have correct sample */ ++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) ++ goto err; ++ if(pil && !(data->p & WM97XX_ADCSEL_PRES)) ++ goto err; ++ ++ if (!(data->x & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ return RC_VALID; ++err: ++ return RC_PENUP; ++} ++ ++/* ++ * Sample the WM9712 touchscreen in polling mode ++ */ ++static int wm9712_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if(coord) { ++ if((rc = wm9712_poll_coord(wm, data)) != RC_VALID) ++ return rc; ++ } else { ++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID) ++ return rc; ++ ++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID) ++ return rc; ++ ++ if (pil && !five_wire) { ++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ } ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9712 continuous mode, i.e. touch data is streamed across an AC97 slot ++ */ ++static int wm9712_acc_enable (struct wm97xx* wm, int enable) ++{ ++ u16 dig1, dig2; ++ int ret = 0; ++ ++ dig1 = wm->dig[1]; ++ dig2 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | ++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK); ++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | ++ WM97XX_DELAY (delay) | ++ WM97XX_SLT (wm->acc_slot) | ++ WM97XX_RATE (wm->acc_rate); ++ if (pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ dig2 |= WM9712_PDEN; ++ } else { ++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); ++ dig2 &= ~WM9712_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++ return 0; ++} ++ ++struct wm97xx_codec_drv wm97xx_codec = { ++ .id = WM9712_ID2, ++ .name = "wm9712", ++ .poll_sample = wm9712_poll_sample, ++ .poll_touch = wm9712_poll_touch, ++ .acc_enable = wm9712_acc_enable, ++ .digitiser_ioctl = wm9712_digitiser_ioctl, ++}; ++ ++EXPORT_SYMBOL_GPL(wm97xx_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM9712 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/drivers/input/touchscreen/wm9713.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/wm9713.c 2006-09-19 20:36:47.969052000 +0200 +@@ -0,0 +1,461 @@ ++/* ++ * wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Revision history ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added pre and post sample calls. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9713_VERSION "0.53" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set internal pull up for pen detect. ++ * ++ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) ++ * i.e. pull up resistance = 64k Ohms / rpu. ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down event. ++ */ ++static int rpu = 1; ++module_param(rpu, int, 0); ++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil = 0; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 4; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask = 0; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * Coordinate Polling Enable. ++ * ++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together ++ * for every poll. ++ */ ++static int coord = 1; ++module_param(coord, int, 0); ++MODULE_PARM_DESC(coord, "Polling coordinate mode"); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, // 1 AC97 Link frames ++ 42, // 2 ++ 84, // 4 ++ 167, // 8 ++ 333, // 16 ++ 667, // 32 ++ 1000, // 48 ++ 1333, // 64 ++ 2000, // 96 ++ 2667, // 128 ++ 3333, // 160 ++ 4000, // 192 ++ 4667, // 224 ++ 5333, // 256 ++ 6000, // 288 ++ 0 // No delay, switch matrix always on ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay (3 * AC97_LINK_FRAME + delay_table [d]); ++} ++ ++/* ++ * set up the physical settings of the WM9713 ++ */ ++static void init_wm9713_phy(struct wm97xx* wm) ++{ ++ u16 dig1 = 0, dig2, dig3; ++ ++ /* default values */ ++ dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5); ++ dig3= WM9712_RPU(1); ++ ++ /* rpu */ ++ if (rpu) { ++ dig3 &= 0xffc0; ++ dig3 |= WM9712_RPU(rpu); ++ info("setting pen detect pull-up to %d Ohms",64000 / rpu); ++ } ++ ++ /* touchpanel pressure */ ++ if (pil == 2) { ++ dig3 |= WM9712_PIL; ++ info("setting pressure measurement current to 400uA."); ++ } else if (pil) ++ info ("setting pressure measurement current to 200uA."); ++ if(!pil) ++ pressure = 0; ++ ++ /* sample settling delay */ ++ if (delay < 0 || delay > 15) { ++ info ("supplied delay out of range."); ++ delay = 4; ++ info("setting adc sample delay to %d u Secs.", delay_table[delay]); ++ } ++ dig2 &= 0xff0f; ++ dig2 |= WM97XX_DELAY(delay); ++ ++ /* mask */ ++ dig3 |= ((mask & 0x3) << 4); ++ if(coord) ++ dig3 |= WM9713_WAIT; ++ ++ wm->misc = wm97xx_reg_read(wm, 0x5a); ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3); ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0); ++} ++ ++static int wm9713_digitiser_ioctl(struct wm97xx* wm, int cmd) ++{ ++ u16 val = 0; ++ ++ switch(cmd){ ++ case WM97XX_DIG_START: ++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ break; ++ case WM97XX_DIG_STOP: ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] & ~WM97XX_PRP_DET_DIG); ++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000); ++ break; ++ case WM97XX_AUX_PREPARE: ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_DIG_RESTORE: ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]); ++ break; ++ case WM97XX_PHY_INIT: ++ init_wm9713_phy(wm); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static inline int is_pden (struct wm97xx* wm) ++{ ++ return wm->dig[2] & WM9713_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9713 adc in polling mode. ++ */ ++static int wm9713_poll_sample (struct wm97xx* wm, int adcsel, int *sample) ++{ ++ u16 dig1; ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = 1 << ((adcsel & 0x7fff) + 3); ++ ++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | adcsel |WM9713_POLL); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay(delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample =wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSRC_MASK) != ffs(adcsel >> 1) << 12) { ++ dbg ("adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSRC_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Read a coordinate from the WM9713 adc in polling mode. ++ */ ++static int wm9713_poll_coord (struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ u16 dig1; ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ if(pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | WM9713_POLL | WM9713_COO); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay(delay); ++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ /* read back data */ ++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (pil) ++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ else ++ data->p = DEFAULT_PRESSURE; ++ ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ /* check we have correct sample */ ++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) ++ goto err; ++ if(pil && !(data->p & WM97XX_ADCSEL_PRES)) ++ goto err; ++ ++ if (!(data->x & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ return RC_VALID; ++err: ++ return RC_PENUP; ++} ++ ++/* ++ * Sample the WM9713 touchscreen in polling mode ++ */ ++static int wm9713_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if(coord) { ++ if((rc = wm9713_poll_coord(wm, data)) != RC_VALID) ++ return rc; ++ } else { ++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x)) != RC_VALID) ++ return rc; ++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y)) != RC_VALID) ++ return rc; ++ if (pil) { ++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES, &data->p)) != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ } ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9713 continuous mode, i.e. touch data is streamed across an AC97 slot ++ */ ++static int wm9713_acc_enable (struct wm97xx* wm, int enable) ++{ ++ u16 dig1, dig2, dig3; ++ int ret = 0; ++ ++ dig1 = wm->dig[0]; ++ dig2 = wm->dig[1]; ++ dig3 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && ++ (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X | WM9713_ADCSEL_Y; ++ if (pil) ++ dig1 |= WM9713_ADCSEL_PRES; ++ dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK | WM97XX_CM_RATE_MASK); ++ dig2 |= WM97XX_SLEN | WM97XX_DELAY (delay) | ++ WM97XX_SLT (wm->acc_slot) | WM97XX_RATE (wm->acc_rate); ++ dig3 |= WM9713_PDEN; ++ } else { ++ dig1 &= ~(WM9713_CTC | WM9713_COO); ++ dig2 &= ~WM97XX_SLEN; ++ dig3 &= ~WM9713_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3); ++ return ret; ++} ++ ++struct wm97xx_codec_drv wm97xx_codec = { ++ .id = WM9713_ID2, ++ .name = "wm9713", ++ .poll_sample = wm9713_poll_sample, ++ .poll_touch = wm9713_poll_touch, ++ .acc_enable = wm9713_acc_enable, ++ .digitiser_ioctl = wm9713_digitiser_ioctl, ++}; ++ ++EXPORT_SYMBOL_GPL(wm97xx_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM9713 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/drivers/input/touchscreen/wm97xx-core.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/wm97xx-core.c 2006-09-19 20:36:47.969052000 +0200 +@@ -0,0 +1,912 @@ ++/* ++ * wm97xx-core.c -- Touch screen driver core for Wolfson WM9705, WM9712 ++ * and WM9713 AC97 Codecs. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Notes: ++ * ++ * Features: ++ * - supports WM9705, WM9712, WM9713 ++ * - polling mode ++ * - continuous mode (arch-dependent) ++ * - adjustable rpu/dpp settings ++ * - adjustable pressure current ++ * - adjustable sample settle delay ++ * - 4 and 5 wire touchscreens (5 wire is WM9712 only) ++ * - pen down detection ++ * - battery monitor ++ * - sample AUX adc's ++ * - power management ++ * - codec GPIO ++ * - codec event notification ++ * Todo ++ * - Support for async sampling control for noisy LCD's. ++ * ++ * Revision history ++ * 7th May 2003 Initial version. ++ * 6th June 2003 Added non module support and AC97 registration. ++ * 18th June 2003 Added AUX adc sampling. ++ * 23rd June 2003 Did some minimal reformatting, fixed a couple of ++ * codec_mutexing bugs and noted a race to fix. ++ * 24th June 2003 Added power management and fixed race condition. ++ * 10th July 2003 Changed to a misc device. ++ * 31st July 2003 Moved TS_EVENT and TS_CAL to wm97xx.h ++ * 8th Aug 2003 Added option for read() calling wm97xx_sample_touch() ++ * because some ac97_read/ac_97_write call schedule() ++ * 7th Nov 2003 Added Input touch event interface, stanley.cai@intel.com ++ * 13th Nov 2003 Removed h3600 touch interface, added interrupt based ++ * pen down notification and implemented continous mode ++ * on XScale arch. ++ * 16th Nov 2003 Ian Molton <spyro@f2s.com> ++ * Modified so that it suits the new 2.6 driver model. ++ * 25th Jan 2004 Andrew Zabolotny <zap@homelink.ru> ++ * Implemented IRQ-driven pen down detection, implemented ++ * the private API meant to be exposed to platform-specific ++ * drivers, reorganized the driver so that it supports ++ * an arbitrary number of devices. ++ * 1st Feb 2004 Moved continuous mode handling to a separate ++ * architecture-dependent file. For now only PXA ++ * built-in AC97 controller is supported (pxa-ac97-wm97xx.c). ++ * 11th Feb 2004 Reduced CPU usage by keeping a cached copy of both ++ * digitizer registers instead of reading them every time. ++ * A reorganization of the whole code for better ++ * error handling. ++ * 17th Apr 2004 Added BMON support. ++ * 17th Nov 2004 Added codec GPIO, codec event handling (real and virtual ++ * GPIOs) and 2.6 power management. ++ * 29th Nov 2004 Added WM9713 support. ++ * 4th Jul 2005 Moved codec specific code out to seperate files. ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added bus interface. ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/string.h> ++#include <linux/proc_fs.h> ++#include <linux/pm.h> ++#include <linux/interrupt.h> ++#include <linux/bitops.h> ++#include <linux/workqueue.h> ++#include <linux/device.h> ++#include <linux/wm97xx.h> ++#include <asm/uaccess.h> ++#include <asm/io.h> ++ ++#define TS_NAME "wm97xx" ++#define WM_CORE_VERSION "0.63" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * WM97xx - enable/disable AUX ADC sysfs ++ */ ++static int aux_sys = 1; ++module_param(aux_sys, int, 0); ++MODULE_PARM_DESC(aux_sys, "enable AUX ADC sysfs entries"); ++ ++/* ++ * WM97xx - enable/disable codec status sysfs ++ */ ++static int status_sys = 1; ++module_param(status_sys, int, 0); ++MODULE_PARM_DESC(status_sys, "enable codec status sysfs entries"); ++ ++/* ++ * Touchscreen absolute values ++ * ++ * These parameters are used to help the input layer discard out of ++ * range readings and reduce jitter etc. ++ * ++ * o min, max:- indicate the min and max values your touch screen returns ++ * o fuzz:- use a higher number to reduce jitter ++ * ++ * The default values correspond to Mainstone II in QVGA mode ++ * ++ * Please read ++ * Documentation/input/input-programming.txt for more details. ++ */ ++ ++static int abs_x[3] = {350,3900,5}; ++module_param_array(abs_x, int, NULL, 0); ++MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz"); ++ ++static int abs_y[3] = {320,3750,40}; ++module_param_array(abs_y, int, NULL, 0); ++MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz"); ++ ++static int abs_p[3] = {0,150,4}; ++module_param_array(abs_p, int, NULL, 0); ++MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz"); ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* codec AC97 IO access */ ++int wm97xx_reg_read(struct wm97xx *wm, u16 reg) ++{ ++ if (wm->ac97) ++ return wm->ac97->bus->ops->read(wm->ac97, reg); ++ else ++ return -1; ++} ++ ++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val) ++{ ++ /* cache digitiser registers */ ++ if(reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3) ++ wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val; ++ ++ /* cache gpio regs */ ++ if(reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE) ++ wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val; ++ ++ /* wm9713 irq reg */ ++ if(reg == 0x5a) ++ wm->misc = val; ++ ++ if (wm->ac97) ++ wm->ac97->bus->ops->write(wm->ac97, reg, val); ++} ++ ++ ++/** ++ * wm97xx_read_aux_adc - Read the aux adc. ++ * @wm: wm97xx device. ++ * @adcsel: codec ADC to be read ++ * ++ * Reads the selected AUX ADC. ++ */ ++ ++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) ++{ ++ int power_adc = 0, auxval; ++ u16 power = 0; ++ ++ /* get codec */ ++ mutex_lock(&wm->codec_mutex); ++ ++ /* When the touchscreen is not in use, we may have to power up the AUX ADC ++ * before we can use sample the AUX inputs-> ++ */ ++ if (wm->id == WM9713_ID2 && ++ (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) { ++ power_adc = 1; ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff); ++ } ++ ++ /* Prepare the codec for AUX reading */ ++ wm->codec->digitiser_ioctl(wm, WM97XX_AUX_PREPARE); ++ ++ /* Turn polling mode on to read AUX ADC */ ++ wm->pen_probably_down = 1; ++ wm->codec->poll_sample(wm, adcsel, &auxval); ++ ++ if (power_adc) ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000); ++ ++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_RESTORE); ++ ++ wm->pen_probably_down = 0; ++ ++ mutex_unlock(&wm->codec_mutex); ++ return auxval & 0xfff; ++} ++ ++#define WM97XX_AUX_ATTR(name,input) \ ++static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct wm97xx *wm = (struct wm97xx*)dev->driver_data; \ ++ return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, input)); \ ++} \ ++static DEVICE_ATTR(name, 0444, name##_show, NULL) ++ ++WM97XX_AUX_ATTR(aux1, WM97XX_AUX_ID1); ++WM97XX_AUX_ATTR(aux2, WM97XX_AUX_ID2); ++WM97XX_AUX_ATTR(aux3, WM97XX_AUX_ID3); ++WM97XX_AUX_ATTR(aux4, WM97XX_AUX_ID4); ++ ++#define WM97XX_STATUS_ATTR(name) \ ++static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct wm97xx *wm = (struct wm97xx*)dev->driver_data; \ ++ return sprintf(buf, "%d\n", wm97xx_reg_read(wm, AC97_GPIO_STATUS)); \ ++} \ ++static DEVICE_ATTR(name, 0444, name##_show, NULL) ++ ++WM97XX_STATUS_ATTR(gpio); ++ ++static int wm97xx_sys_add(struct device *dev) ++{ ++ if (aux_sys) { ++ device_create_file(dev, &dev_attr_aux1); ++ device_create_file(dev, &dev_attr_aux2); ++ device_create_file(dev, &dev_attr_aux3); ++ device_create_file(dev, &dev_attr_aux4); ++ } ++ if (status_sys) ++ device_create_file(dev, &dev_attr_gpio); ++ return 0; ++} ++ ++static void wm97xx_sys_remove(struct device *dev) ++{ ++ if (status_sys) ++ device_remove_file(dev, &dev_attr_gpio); ++ if (aux_sys) { ++ device_remove_file(dev, &dev_attr_aux1); ++ device_remove_file(dev, &dev_attr_aux2); ++ device_remove_file(dev, &dev_attr_aux3); ++ device_remove_file(dev, &dev_attr_aux4); ++ } ++} ++ ++/** ++ * wm97xx_get_gpio - Get the status of a codec GPIO. ++ * @wm: wm97xx device. ++ * @gpio: gpio ++ * ++ * Get the status of a codec GPIO pin ++ */ ++ ++wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio) ++{ ++ u16 status; ++ wm97xx_gpio_status_t ret; ++ ++ mutex_lock(&wm->codec_mutex); ++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ ++ if (status & gpio) ++ ret = WM97XX_GPIO_HIGH; ++ else ++ ret = WM97XX_GPIO_LOW; ++ ++ mutex_unlock(&wm->codec_mutex); ++ return ret; ++} ++ ++/** ++ * wm97xx_set_gpio - Set the status of a codec GPIO. ++ * @wm: wm97xx device. ++ * @gpio: gpio ++ * ++ * ++ * Set the status of a codec GPIO pin ++ */ ++ ++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, ++ wm97xx_gpio_status_t status) ++{ ++ u16 reg; ++ ++ mutex_lock(&wm->codec_mutex); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ ++ if (status & WM97XX_GPIO_HIGH) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ if (wm->id == WM9712_ID2) ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1); ++ else ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg); ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++/* ++ * Codec GPIO pin configuration, this set's pin direction, polarity, ++ * stickyness and wake up. ++ */ ++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir, ++ wm97xx_gpio_pol_t pol, wm97xx_gpio_sticky_t sticky, ++ wm97xx_gpio_wake_t wake) ++{ ++ u16 reg; ++ ++ mutex_lock(&wm->codec_mutex); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ ++ if (pol == WM97XX_GPIO_POL_HIGH) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY); ++ ++ if (sticky == WM97XX_GPIO_STICKY) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP); ++ ++ if (wake == WM97XX_GPIO_WAKE) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ ++ if (dir == WM97XX_GPIO_IN) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg); ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++/* ++ * Handle a pen down interrupt. ++ */ ++static void wm97xx_pen_irq_worker(void *ptr) ++{ ++ struct wm97xx *wm = (struct wm97xx *) ptr; ++ ++ /* do we need to enable the touch panel reader */ ++ if (wm->id == WM9705_ID2) { ++ if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN) ++ wm->pen_is_down = 1; ++ else ++ wm->pen_is_down = 0; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ } else { ++ u16 status, pol; ++ mutex_lock(&wm->codec_mutex); ++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ ++ if (WM97XX_GPIO_13 & pol & status) { ++ wm->pen_is_down = 1; ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol & ~WM97XX_GPIO_13); ++ } else { ++ wm->pen_is_down = 0; ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol | WM97XX_GPIO_13); ++ } ++ ++ if (wm->id == WM9712_ID2) ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status & ~WM97XX_GPIO_13) << 1); ++ else ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, status & ~WM97XX_GPIO_13); ++ mutex_unlock(&wm->codec_mutex); ++ wake_up_interruptible(&wm->pen_irq_wait); ++ } ++ ++ if (!wm->pen_is_down && wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->mach_ops->acc_pen_up(wm); ++ enable_irq(wm->pen_irq); ++} ++ ++/* ++ * Codec PENDOWN irq handler ++ * ++ * We have to disable the codec interrupt in the handler because it can ++ * take upto 1ms to clear the interrupt source. The interrupt is then enabled ++ * again in the slow handler when the source has been cleared. ++ */ ++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id, ++ struct pt_regs *regs) ++{ ++ struct wm97xx *wm = (struct wm97xx *) dev_id; ++ disable_irq(wm->pen_irq); ++ queue_work(wm->pen_irq_workq, &wm->pen_event_work); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * initialise pen IRQ handler and workqueue ++ */ ++static int wm97xx_init_pen_irq(struct wm97xx *wm) ++{ ++ u16 reg; ++ ++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker, wm); ++ if ((wm->pen_irq_workq = ++ create_singlethread_workqueue("kwm97pen")) == NULL) { ++ err("could not create pen irq work queue"); ++ wm->pen_irq = 0; ++ return -EINVAL; ++ } ++ ++ if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) { ++ err("could not register codec pen down interrupt, will poll for pen down"); ++ destroy_workqueue(wm->pen_irq_workq); ++ wm->pen_irq = 0; ++ return -EINVAL; ++ } ++ ++ /* enable PEN down on wm9712/13 */ ++ if (wm->id != WM9705_ID2) { ++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg & 0xfffb); ++ reg = wm97xx_reg_read(wm, 0x5a); ++ wm97xx_reg_write(wm, 0x5a, reg & ~0x0001); ++ } ++ ++ return 0; ++} ++ ++/* Private struct for communication between struct wm97xx_tshread ++ * and wm97xx_read_samples */ ++struct ts_state { ++ int sleep_time; ++ int min_sleep_time; ++}; ++ ++static int wm97xx_read_samples(struct wm97xx *wm, struct ts_state *state) ++{ ++ struct wm97xx_data data; ++ int rc; ++ ++ mutex_lock(&wm->codec_mutex); ++ ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ rc = wm->mach_ops->acc_pen_down(wm); ++ else ++ rc = wm->codec->poll_touch(wm, &data); ++ ++ if (rc & RC_PENUP) { ++ if (wm->pen_is_down) { ++ wm->pen_is_down = 0; ++ dbg("pen up"); ++ input_report_abs(wm->input_dev, ABS_PRESSURE, 0); ++ input_sync(wm->input_dev); ++ } else if (!(rc & RC_AGAIN)) { ++ /* We need high frequency updates only while pen is down, ++ * the user never will be able to touch screen faster than ++ * a few times per second... On the other hand, when the ++ * user is actively working with the touchscreen we don't ++ * want to lose the quick response. So we will slowly ++ * increase sleep time after the pen is up and quicky ++ * restore it to ~one task switch when pen is down again. ++ */ ++ if (state->sleep_time < HZ / 10) ++ state->sleep_time++; ++ } ++ ++ } else if (rc & RC_VALID) { ++ dbg("pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n", ++ data.x >> 12, data.x & 0xfff, data.y >> 12, ++ data.y & 0xfff, data.p >> 12, data.p & 0xfff); ++ input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff); ++ input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff); ++ input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff); ++ input_sync(wm->input_dev); ++ wm->pen_is_down = 1; ++ state->sleep_time = state->min_sleep_time; ++ } else if (rc & RC_PENDOWN) { ++ dbg("pen down"); ++ wm->pen_is_down = 1; ++ state->sleep_time = state->min_sleep_time; ++ } ++ ++ mutex_unlock(&wm->codec_mutex); ++ return rc; ++} ++ ++/* ++* The touchscreen sample reader thread. ++*/ ++static int wm97xx_ts_read(void *data) ++{ ++ int rc; ++ struct ts_state state; ++ struct wm97xx *wm = (struct wm97xx *) data; ++ ++ /* set up thread context */ ++ wm->ts_task = current; ++ daemonize("kwm97xxts"); ++ ++ if (wm->codec == NULL) { ++ wm->ts_task = NULL; ++ printk(KERN_ERR "codec is NULL, bailing\n"); ++ } ++ ++ complete(&wm->ts_init); ++ wm->pen_is_down = 0; ++ state.min_sleep_time = HZ >= 100 ? HZ / 100 : 1; ++ if (state.min_sleep_time < 1) ++ state.min_sleep_time = 1; ++ state.sleep_time = state.min_sleep_time; ++ ++ /* touch reader loop */ ++ while (wm->ts_task) { ++ do { ++ try_to_freeze(); ++ rc = wm97xx_read_samples(wm, &state); ++ } while (rc & RC_AGAIN); ++ if (!wm->pen_is_down && wm->pen_irq) { ++ /* Nice, we don't have to poll for pen down event */ ++ wait_event_interruptible(wm->pen_irq_wait, wm->pen_is_down); ++ } else { ++ set_task_state(current, TASK_INTERRUPTIBLE); ++ schedule_timeout(state.sleep_time); ++ } ++ } ++ complete_and_exit(&wm->ts_exit, 0); ++} ++ ++/** ++ * wm97xx_ts_input_open - Open the touch screen input device. ++ * @idev: Input device to be opened. ++ * ++ * Called by the input sub system to open a wm97xx touchscreen device. ++ * Starts the touchscreen thread and touch digitiser. ++ */ ++static int wm97xx_ts_input_open(struct input_dev *idev) ++{ ++ int ret = 0; ++ struct wm97xx *wm = (struct wm97xx *) idev->private; ++ ++ mutex_lock(&wm->codec_mutex); ++ /* first time opened ? */ ++ if (wm->ts_use_count++ == 0) { ++ /* start touchscreen thread */ ++ init_completion(&wm->ts_init); ++ init_completion(&wm->ts_exit); ++ ret = kernel_thread(wm97xx_ts_read, wm, CLONE_KERNEL); ++ ++ if (ret >= 0) { ++ wait_for_completion(&wm->ts_init); ++ if (wm->ts_task == NULL) ++ ret = -EINVAL; ++ } else { ++ mutex_unlock(&wm->codec_mutex); ++ return ret; ++ } ++ ++ /* start digitiser */ ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->codec->acc_enable(wm, 1); ++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_START); ++ ++ /* init pen down/up irq handling */ ++ if (wm->pen_irq) { ++ wm97xx_init_pen_irq(wm); ++ ++ if (wm->pen_irq == 0) { ++ /* we failed to get an irq for pen down events, ++ * so we resort to polling. kickstart the reader */ ++ wm->pen_is_down = 1; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ } ++ } ++ } ++ ++ mutex_unlock(&wm->codec_mutex); ++ return 0; ++} ++ ++/** ++ * wm97xx_ts_input_close - Close the touch screen input device. ++ * @idev: Input device to be closed. ++ * ++ * Called by the input sub system to close a wm97xx touchscreen device. ++ * Kills the touchscreen thread and stops the touch digitiser. ++ */ ++ ++static void wm97xx_ts_input_close(struct input_dev *idev) ++{ ++ struct wm97xx *wm = (struct wm97xx *) idev->private; ++ ++ mutex_lock(&wm->codec_mutex); ++ if (--wm->ts_use_count == 0) { ++ /* destroy workqueues and free irqs */ ++ if (wm->pen_irq) { ++ free_irq(wm->pen_irq, wm); ++ destroy_workqueue(wm->pen_irq_workq); ++ } ++ ++ /* kill thread */ ++ if (wm->ts_task) { ++ wm->ts_task = NULL; ++ wm->pen_is_down = 1; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ wait_for_completion(&wm->ts_exit); ++ wm->pen_is_down = 0; ++ } ++ ++ /* stop digitiser */ ++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_STOP); ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->codec->acc_enable(wm, 0); ++ } ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++static int wm97xx_bus_match(struct device *dev, struct device_driver *drv) ++{ ++ return !(strcmp(dev->bus_id,drv->name)); ++} ++ ++/* ++ * The AC97 audio driver will do all the Codec suspend and resume ++ * tasks. This is just for anything machine specific or extra. ++ */ ++static int wm97xx_bus_suspend(struct device *dev, pm_message_t state) ++{ ++ int ret = 0; ++ ++ if (dev->driver && dev->driver->suspend) ++ ret = dev->driver->suspend(dev, state); ++ ++ return ret; ++} ++ ++static int wm97xx_bus_resume(struct device *dev) ++{ ++ int ret = 0; ++ ++ if (dev->driver && dev->driver->resume) ++ ret = dev->driver->resume(dev); ++ ++ return ret; ++} ++ ++struct bus_type wm97xx_bus_type = { ++ .name = "wm97xx", ++ .match = wm97xx_bus_match, ++ .suspend = wm97xx_bus_suspend, ++ .resume = wm97xx_bus_resume, ++}; ++ ++static void wm97xx_release(struct device *dev) ++{ ++ kfree(dev); ++} ++ ++static int wm97xx_probe(struct device *dev) ++{ ++ struct wm97xx* wm; ++ int ret = 0, id = 0; ++ ++ if (!(wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL))) ++ return -ENOMEM; ++ mutex_init(&wm->codec_mutex); ++ ++ init_waitqueue_head(&wm->pen_irq_wait); ++ wm->dev = dev; ++ dev->driver_data = wm; ++ wm->ac97 = to_ac97_t(dev); ++ ++ /* check that we have a supported codec */ ++ if ((id = wm97xx_reg_read(wm, AC97_VENDOR_ID1)) != WM97XX_ID1) { ++ err("could not find a wm97xx, found a %x instead\n", id); ++ kfree(wm); ++ return -ENODEV; ++ } ++ ++ wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2); ++ if(wm->id != wm97xx_codec.id) { ++ err("could not find a the selected codec, please build for wm97%2x", wm->id & 0xff); ++ kfree(wm); ++ return -ENODEV; ++ } ++ ++ if((wm->input_dev = input_allocate_device()) == NULL) { ++ kfree(wm); ++ return -ENOMEM; ++ } ++ ++ /* set up touch configuration */ ++ info("detected a wm97%2x codec", wm->id & 0xff); ++ wm->input_dev->name = "wm97xx touchscreen"; ++ wm->input_dev->open = wm97xx_ts_input_open; ++ wm->input_dev->close = wm97xx_ts_input_close; ++ set_bit(EV_ABS, wm->input_dev->evbit); ++ set_bit(ABS_X, wm->input_dev->absbit); ++ set_bit(ABS_Y, wm->input_dev->absbit); ++ set_bit(ABS_PRESSURE, wm->input_dev->absbit); ++ wm->input_dev->absmax[ABS_X] = abs_x[1]; ++ wm->input_dev->absmax[ABS_Y] = abs_y[1]; ++ wm->input_dev->absmax[ABS_PRESSURE] = abs_p[1]; ++ wm->input_dev->absmin[ABS_X] = abs_x[0]; ++ wm->input_dev->absmin[ABS_Y] = abs_y[0]; ++ wm->input_dev->absmin[ABS_PRESSURE] = abs_p[0]; ++ wm->input_dev->absfuzz[ABS_X] = abs_x[2]; ++ wm->input_dev->absfuzz[ABS_Y] = abs_y[2]; ++ wm->input_dev->absfuzz[ABS_PRESSURE] = abs_p[2]; ++ wm->input_dev->private = wm; ++ wm->codec = &wm97xx_codec; ++ if((ret = input_register_device(wm->input_dev)) < 0) { ++ kfree(wm); ++ return -ENOMEM; ++ } ++ ++ if(aux_sys) ++ wm97xx_sys_add(dev); ++ ++ /* set up physical characteristics */ ++ wm->codec->digitiser_ioctl(wm, WM97XX_PHY_INIT); ++ ++ /* load gpio cache */ ++ wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY); ++ wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP); ++ wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ ++ /* register our battery device */ ++ if (!(wm->battery_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) { ++ ret = -ENOMEM; ++ goto batt_err; ++ } ++ wm->battery_dev->bus = &wm97xx_bus_type; ++ strcpy(wm->battery_dev->bus_id,"wm97xx-battery"); ++ wm->battery_dev->driver_data = wm; ++ wm->battery_dev->parent = dev; ++ wm->battery_dev->release = wm97xx_release; ++ if((ret = device_register(wm->battery_dev)) < 0) ++ goto batt_reg_err; ++ ++ /* register our extended touch device (for machine specific extensions) */ ++ if (!(wm->touch_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) { ++ ret = -ENOMEM; ++ goto touch_err; ++ } ++ wm->touch_dev->bus = &wm97xx_bus_type; ++ strcpy(wm->touch_dev->bus_id,"wm97xx-touchscreen"); ++ wm->touch_dev->driver_data = wm; ++ wm->touch_dev->parent = dev; ++ wm->touch_dev->release = wm97xx_release; ++ if((ret = device_register(wm->touch_dev)) < 0) ++ goto touch_reg_err; ++ ++ return ret; ++ ++touch_reg_err: ++ kfree(wm->touch_dev); ++touch_err: ++ device_unregister(wm->battery_dev); ++batt_reg_err: ++ kfree(wm->battery_dev); ++batt_err: ++ input_unregister_device(wm->input_dev); ++ kfree(wm); ++ return ret; ++} ++ ++static int wm97xx_remove(struct device *dev) ++{ ++ struct wm97xx *wm = dev_get_drvdata(dev); ++ ++ /* Stop touch reader thread */ ++ if (wm->ts_task) { ++ wm->ts_task = NULL; ++ wm->pen_is_down = 1; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ wait_for_completion(&wm->ts_exit); ++ } ++ device_unregister(wm->battery_dev); ++ device_unregister(wm->touch_dev); ++ input_unregister_device(wm->input_dev); ++ ++ if(aux_sys) ++ wm97xx_sys_remove(dev); ++ ++ kfree(wm); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++int wm97xx_resume(struct device* dev) ++{ ++ struct wm97xx *wm = dev_get_drvdata(dev); ++ ++ /* restore digitiser and gpio's */ ++ if(wm->id == WM9713_ID2) { ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]); ++ wm97xx_reg_write(wm, 0x5a, wm->misc); ++ if(wm->ts_use_count) { ++ u16 reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff; ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg); ++ } ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]); ++ ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]); ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]); ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]); ++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]); ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]); ++ ++ return 0; ++} ++ ++#else ++#define wm97xx_resume NULL ++#endif ++ ++int wm97xx_register_mach_ops(struct wm97xx *wm, struct wm97xx_mach_ops *mach_ops) ++{ ++ mutex_lock(&wm->codec_mutex); ++ if(wm->mach_ops) { ++ mutex_unlock(&wm->codec_mutex); ++ return -EINVAL; ++ } ++ wm->mach_ops = mach_ops; ++ mutex_unlock(&wm->codec_mutex); ++ return 0; ++} ++ ++void wm97xx_unregister_mach_ops(struct wm97xx *wm) ++{ ++ mutex_lock(&wm->codec_mutex); ++ wm->mach_ops = NULL; ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++static struct device_driver wm97xx_driver = { ++ .name = "ac97", ++ .bus = &ac97_bus_type, ++ .owner = THIS_MODULE, ++ .probe = wm97xx_probe, ++ .remove = wm97xx_remove, ++ .resume = wm97xx_resume, ++}; ++ ++static int __init wm97xx_init(void) ++{ ++ int ret; ++ ++ info("version %s liam.girdwood@wolfsonmicro.com", WM_CORE_VERSION); ++ if((ret = bus_register(&wm97xx_bus_type)) < 0) ++ return ret; ++ return driver_register(&wm97xx_driver); ++} ++ ++static void __exit wm97xx_exit(void) ++{ ++ driver_unregister(&wm97xx_driver); ++ bus_unregister(&wm97xx_bus_type); ++} ++ ++EXPORT_SYMBOL_GPL(wm97xx_get_gpio); ++EXPORT_SYMBOL_GPL(wm97xx_set_gpio); ++EXPORT_SYMBOL_GPL(wm97xx_config_gpio); ++EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc); ++EXPORT_SYMBOL_GPL(wm97xx_reg_read); ++EXPORT_SYMBOL_GPL(wm97xx_reg_write); ++EXPORT_SYMBOL_GPL(wm97xx_bus_type); ++EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops); ++EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops); ++ ++module_init(wm97xx_init); ++module_exit(wm97xx_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/include/linux/wm97xx.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/include/linux/wm97xx.h 2006-09-19 20:36:47.973052250 +0200 +@@ -0,0 +1,291 @@ ++ ++/* ++ * Register bits and API for Wolfson WM97xx series of codecs ++ */ ++ ++#ifndef _LINUX_WM97XX_H ++#define _LINUX_WM97XX_H ++ ++#include <sound/driver.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/ac97_codec.h> ++#include <sound/initval.h> ++#include <linux/types.h> ++#include <linux/list.h> ++#include <linux/input.h> /* Input device layer */ ++ ++/* ++ * WM97xx AC97 Touchscreen registers ++ */ ++#define AC97_WM97XX_DIGITISER1 0x76 ++#define AC97_WM97XX_DIGITISER2 0x78 ++#define AC97_WM97XX_DIGITISER_RD 0x7a ++#define AC97_WM9713_DIG1 0x74 ++#define AC97_WM9713_DIG2 AC97_WM97XX_DIGITISER1 ++#define AC97_WM9713_DIG3 AC97_WM97XX_DIGITISER2 ++ ++/* ++ * WM97xx register bits ++ */ ++#define WM97XX_POLL 0x8000 /* initiate a polling measurement */ ++#define WM97XX_ADCSEL_X 0x1000 /* x coord measurement */ ++#define WM97XX_ADCSEL_Y 0x2000 /* y coord measurement */ ++#define WM97XX_ADCSEL_PRES 0x3000 /* pressure measurement */ ++#define WM97XX_ADCSEL_MASK 0x7000 ++#define WM97XX_COO 0x0800 /* enable coordinate mode */ ++#define WM97XX_CTC 0x0400 /* enable continuous mode */ ++#define WM97XX_CM_RATE_93 0x0000 /* 93.75Hz continuous rate */ ++#define WM97XX_CM_RATE_187 0x0100 /* 187.5Hz continuous rate */ ++#define WM97XX_CM_RATE_375 0x0200 /* 375Hz continuous rate */ ++#define WM97XX_CM_RATE_750 0x0300 /* 750Hz continuous rate */ ++#define WM97XX_CM_RATE_8K 0x00f0 /* 8kHz continuous rate */ ++#define WM97XX_CM_RATE_12K 0x01f0 /* 12kHz continuous rate */ ++#define WM97XX_CM_RATE_24K 0x02f0 /* 24kHz continuous rate */ ++#define WM97XX_CM_RATE_48K 0x03f0 /* 48kHz continuous rate */ ++#define WM97XX_CM_RATE_MASK 0x03f0 ++#define WM97XX_RATE(i) (((i & 3) << 8) | ((i & 4) ? 0xf0 : 0)) ++#define WM97XX_DELAY(i) ((i << 4) & 0x00f0) /* sample delay times */ ++#define WM97XX_DELAY_MASK 0x00f0 ++#define WM97XX_SLEN 0x0008 /* slot read back enable */ ++#define WM97XX_SLT(i) ((i - 5) & 0x7) /* touchpanel slot selection (5-11) */ ++#define WM97XX_SLT_MASK 0x0007 ++#define WM97XX_PRP_DETW 0x4000 /* pen detect on, digitiser off, wake up */ ++#define WM97XX_PRP_DET 0x8000 /* pen detect on, digitiser off, no wake up */ ++#define WM97XX_PRP_DET_DIG 0xc000 /* pen detect on, digitiser on */ ++#define WM97XX_RPR 0x2000 /* wake up on pen down */ ++#define WM97XX_PEN_DOWN 0x8000 /* pen is down */ ++#define WM97XX_ADCSRC_MASK 0x7000 /* ADC source mask */ ++ ++#define WM97XX_AUX_ID1 0x8001 ++#define WM97XX_AUX_ID2 0x8002 ++#define WM97XX_AUX_ID3 0x8003 ++#define WM97XX_AUX_ID4 0x8004 ++ ++ ++/* WM9712 Bits */ ++#define WM9712_45W 0x1000 /* set for 5-wire touchscreen */ ++#define WM9712_PDEN 0x0800 /* measure only when pen down */ ++#define WM9712_WAIT 0x0200 /* wait until adc is read before next sample */ ++#define WM9712_PIL 0x0100 /* current used for pressure measurement. set 400uA else 200uA */ ++#define WM9712_MASK_HI 0x0040 /* hi on mask pin (47) stops conversions */ ++#define WM9712_MASK_EDGE 0x0080 /* rising/falling edge on pin delays sample */ ++#define WM9712_MASK_SYNC 0x00c0 /* rising/falling edge on mask initiates sample */ ++#define WM9712_RPU(i) (i&0x3f) /* internal pull up on pen detect (64k / rpu) */ ++#define WM9712_PD(i) (0x1 << i) /* power management */ ++ ++/* WM9712 Registers */ ++#define AC97_WM9712_POWER 0x24 ++#define AC97_WM9712_REV 0x58 ++ ++/* WM9705 Bits */ ++#define WM9705_PDEN 0x1000 /* measure only when pen is down */ ++#define WM9705_PINV 0x0800 /* inverts sense of pen down output */ ++#define WM9705_BSEN 0x0400 /* BUSY flag enable, pin47 is 1 when busy */ ++#define WM9705_BINV 0x0200 /* invert BUSY (pin47) output */ ++#define WM9705_WAIT 0x0100 /* wait until adc is read before next sample */ ++#define WM9705_PIL 0x0080 /* current used for pressure measurement. set 400uA else 200uA */ ++#define WM9705_PHIZ 0x0040 /* set PHONE and PCBEEP inputs to high impedance */ ++#define WM9705_MASK_HI 0x0010 /* hi on mask stops conversions */ ++#define WM9705_MASK_EDGE 0x0020 /* rising/falling edge on pin delays sample */ ++#define WM9705_MASK_SYNC 0x0030 /* rising/falling edge on mask initiates sample */ ++#define WM9705_PDD(i) (i & 0x000f) /* pen detect comparator threshold */ ++ ++ ++/* WM9713 Bits */ ++#define WM9713_PDPOL 0x0400 /* Pen down polarity */ ++#define WM9713_POLL 0x0200 /* initiate a polling measurement */ ++#define WM9713_CTC 0x0100 /* enable continuous mode */ ++#define WM9713_ADCSEL_X 0x0002 /* X measurement */ ++#define WM9713_ADCSEL_Y 0x0004 /* Y measurement */ ++#define WM9713_ADCSEL_PRES 0x0008 /* Pressure measurement */ ++#define WM9713_COO 0x0001 /* enable coordinate mode */ ++#define WM9713_PDEN 0x0800 /* measure only when pen down */ ++#define WM9713_ADCSEL_MASK 0x00fe /* ADC selection mask */ ++#define WM9713_WAIT 0x0200 /* coordinate wait */ ++ ++/* AUX ADC ID's */ ++#define TS_COMP1 0x0 ++#define TS_COMP2 0x1 ++#define TS_BMON 0x2 ++#define TS_WIPER 0x3 ++ ++/* ID numbers */ ++#define WM97XX_ID1 0x574d ++#define WM9712_ID2 0x4c12 ++#define WM9705_ID2 0x4c05 ++#define WM9713_ID2 0x4c13 ++ ++/* Codec GPIO's */ ++#define WM97XX_MAX_GPIO 16 ++#define WM97XX_GPIO_1 (1 << 1) ++#define WM97XX_GPIO_2 (1 << 2) ++#define WM97XX_GPIO_3 (1 << 3) ++#define WM97XX_GPIO_4 (1 << 4) ++#define WM97XX_GPIO_5 (1 << 5) ++#define WM97XX_GPIO_6 (1 << 6) ++#define WM97XX_GPIO_7 (1 << 7) ++#define WM97XX_GPIO_8 (1 << 8) ++#define WM97XX_GPIO_9 (1 << 9) ++#define WM97XX_GPIO_10 (1 << 10) ++#define WM97XX_GPIO_11 (1 << 11) ++#define WM97XX_GPIO_12 (1 << 12) ++#define WM97XX_GPIO_13 (1 << 13) ++#define WM97XX_GPIO_14 (1 << 14) ++#define WM97XX_GPIO_15 (1 << 15) ++ ++ ++#define AC97_LINK_FRAME 21 /* time in uS for AC97 link frame */ ++ ++ ++/*---------------- Return codes from sample reading functions ---------------*/ ++ ++/* More data is available; call the sample gathering function again */ ++#define RC_AGAIN 0x00000001 ++/* The returned sample is valid */ ++#define RC_VALID 0x00000002 ++/* The pen is up (the first RC_VALID without RC_PENUP means pen is down) */ ++#define RC_PENUP 0x00000004 ++/* The pen is down (RC_VALID implies RC_PENDOWN, but sometimes it is helpful ++ to tell the handler that the pen is down but we don't know yet his coords, ++ so the handler should not sleep or wait for pendown irq) */ ++#define RC_PENDOWN 0x00000008 ++ ++/* The wm97xx driver provides a private API for writing platform-specific ++ * drivers. ++ */ ++ ++/* The structure used to return arch specific sampled data into */ ++struct wm97xx_data { ++ int x; ++ int y; ++ int p; ++}; ++ ++/* Codec GPIO status ++ */ ++typedef enum { ++ WM97XX_GPIO_HIGH, ++ WM97XX_GPIO_LOW ++} wm97xx_gpio_status_t; ++ ++/* Codec GPIO direction ++ */ ++typedef enum { ++ WM97XX_GPIO_IN, ++ WM97XX_GPIO_OUT ++} wm97xx_gpio_dir_t; ++ ++/* Codec GPIO polarity ++ */ ++typedef enum { ++ WM97XX_GPIO_POL_HIGH, ++ WM97XX_GPIO_POL_LOW ++} wm97xx_gpio_pol_t; ++ ++/* Codec GPIO sticky ++ */ ++typedef enum { ++ WM97XX_GPIO_STICKY, ++ WM97XX_GPIO_NOTSTICKY ++} wm97xx_gpio_sticky_t; ++ ++/* Codec GPIO wake ++ */ ++typedef enum { ++ WM97XX_GPIO_WAKE, ++ WM97XX_GPIO_NOWAKE ++} wm97xx_gpio_wake_t; ++ ++ ++/* ++ * Digitiser ioctl commands ++ */ ++#define WM97XX_DIG_START 0x1 ++#define WM97XX_DIG_STOP 0x2 ++#define WM97XX_PHY_INIT 0x3 ++#define WM97XX_AUX_PREPARE 0x4 ++#define WM97XX_DIG_RESTORE 0x5 ++ ++struct wm97xx; ++extern struct wm97xx_codec_drv wm97xx_codec; ++ ++/* ++ * Codec driver interface - allows mapping to WM9705/12/13 and newer codecs ++ */ ++struct wm97xx_codec_drv { ++ u16 id; ++ char *name; ++ int (*poll_sample) (struct wm97xx *, int adcsel, int *sample); /* read 1 sample */ ++ int (*poll_touch) (struct wm97xx *, struct wm97xx_data *); /* read X,Y,[P] in poll */ ++ int (*digitiser_ioctl) (struct wm97xx *, int cmd); ++ int (*acc_enable) (struct wm97xx *, int enable); ++}; ++ ++ ++/* Machine specific and accelerated touch operations */ ++struct wm97xx_mach_ops { ++ ++ /* accelerated touch readback - coords are transmited on AC97 link */ ++ int acc_enabled; ++ void (*acc_pen_up) (struct wm97xx *); ++ int (*acc_pen_down) (struct wm97xx *); ++ int (*acc_startup) (struct wm97xx *); ++ void (*acc_shutdown) (struct wm97xx *); ++ ++ /* pre and post sample - can be used to minimise any analog noise */ ++ void (*pre_sample) (int); /* function to run before sampling */ ++ void (*post_sample) (int); /* function to run after sampling */ ++}; ++ ++struct wm97xx { ++ u16 dig[3], id, gpio[6], misc; /* Cached codec registers */ ++ u16 dig_save[3]; /* saved during aux reading */ ++ struct wm97xx_codec_drv *codec; /* attached codec driver*/ ++ struct input_dev* input_dev; /* touchscreen input device */ ++ ac97_t *ac97; /* ALSA codec access */ ++ struct device *dev; /* ALSA device */ ++ struct device *battery_dev; ++ struct device *touch_dev; ++ struct wm97xx_mach_ops *mach_ops; ++ struct mutex codec_mutex; ++ struct completion ts_init; ++ struct completion ts_exit; ++ struct task_struct *ts_task; ++ unsigned int pen_irq; /* Pen IRQ number in use */ ++ wait_queue_head_t pen_irq_wait; /* Pen IRQ wait queue */ ++ struct workqueue_struct *pen_irq_workq; ++ struct work_struct pen_event_work; ++ u16 acc_slot; /* AC97 slot used for acc touch data */ ++ u16 acc_rate; /* acc touch data rate */ ++ unsigned int ts_use_count; ++ unsigned pen_is_down:1; /* Pen is down */ ++ unsigned aux_waiting:1; /* aux measurement waiting */ ++ unsigned pen_probably_down:1; /* used in polling mode */ ++}; ++ ++/* Codec GPIO access (not supported on WM9705) ++ * This can be used to set/get codec GPIO and Virtual GPIO status. ++ */ ++wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio); ++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, ++ wm97xx_gpio_status_t status); ++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, ++ wm97xx_gpio_dir_t dir, ++ wm97xx_gpio_pol_t pol, ++ wm97xx_gpio_sticky_t sticky, ++ wm97xx_gpio_wake_t wake); ++ ++/* codec AC97 IO access */ ++int wm97xx_reg_read(struct wm97xx *wm, u16 reg); ++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val); ++ ++/* aux adc readback */ ++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel); ++ ++/* machine ops */ ++int wm97xx_register_mach_ops(struct wm97xx *, struct wm97xx_mach_ops *); ++void wm97xx_unregister_mach_ops(struct wm97xx *); ++ ++extern struct bus_type wm97xx_bus_type; ++#endif diff --git a/packages/linux/linux-rp-2.6.23/defconfig-tosa b/packages/linux/linux-rp-2.6.23/defconfig-tosa index 74fc076608..08c2bbe87a 100644 --- a/packages/linux/linux-rp-2.6.23/defconfig-tosa +++ b/packages/linux/linux-rp-2.6.23/defconfig-tosa @@ -248,7 +248,7 @@ CONFIG_PACKET_MMAP=y CONFIG_UNIX=y CONFIG_XFRM=y CONFIG_XFRM_USER=m -# CONFIG_NET_KEY is not set +CONFIG_NET_KEY=m CONFIG_INET=y # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set @@ -262,6 +262,9 @@ CONFIG_SYN_COOKIES=y # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set # CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=m +CONFIG_INET_XFRM_MODE_TUNNEL=m +CONFIG_INET_XFRM_MODE_BEET=m CONFIG_INET_DIAG=m CONFIG_INET_TCP_DIAG=m # CONFIG_TCP_CONG_ADVANCED is not set @@ -792,9 +795,11 @@ CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_WM97XX=y # CONFIG_TOUCHSCREEN_WM9705 is not set CONFIG_TOUCHSCREEN_WM9712=y +CONFIG_TOUCHSCREEN_TOSA=y # CONFIG_TOUCHSCREEN_WM9713 is not set # CONFIG_TOUCHSCREEN_WM97XX_PXA is not set -# CONFIG_INPUT_MISC is not set +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=m # # Hardware I/O ports @@ -1004,10 +1009,10 @@ CONFIG_FONT_8x8=y # # Logo configuration # -CONFIG_LOGO=y +# CONFIG_LOGO is not set # CONFIG_LOGO_LINUX_MONO is not set # CONFIG_LOGO_LINUX_VGA16 is not set -CONFIG_LOGO_LINUX_CLUT224=y +# CONFIG_LOGO_LINUX_CLUT224 is not set CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_BACKLIGHT_DEVICE=y @@ -1033,14 +1038,14 @@ CONFIG_SND_OSSEMUL=y CONFIG_SND_MIXER_OSS=m CONFIG_SND_PCM_OSS=m # CONFIG_SND_DYNAMIC_MINORS is not set -CONFIG_SND_SUPPORT_OLD_API=y +# CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_VERBOSE_PRINTK is not set # CONFIG_SND_DEBUG is not set # # Generic devices # -CONFIG_SND_AC97_BUS=y +CONFIG_AC97_BUS=y CONFIG_SND_DUMMY=m # CONFIG_SND_MTPAV is not set # CONFIG_SND_SERIAL_U16550 is not set @@ -1072,15 +1077,15 @@ CONFIG_SND_SOC=y # # SoC Audio for the Intel PXA2xx # -CONFIG_SND_PXA2xx_SOC=y -CONFIG_SND_PXA2xx_SOC_AC97=y -# CONFIG_SND_PXA2xx_SOC_MAINSTONE is not set -# CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM8753 is not set -# CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM9713 is not set -# CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM9712 is not set -# CONFIG_SND_PXA2xx_SOC_CORGI is not set -# CONFIG_SND_PXA2xx_SOC_SPITZ is not set -CONFIG_SND_PXA2xx_SOC_TOSA=y +CONFIG_SND_PXA2XX_SOC=y +CONFIG_SND_PXA2XX_SOC_AC97=y +# CONFIG_SND_PXA2XX_SOC_MAINSTONE is not set +# CONFIG_SND_PXA2XX_SOC_MAINSTONE_WM8753 is not set +# CONFIG_SND_PXA2XX_SOC_MAINSTONE_WM9713 is not set +# CONFIG_SND_PXA2XX_SOC_MAINSTONE_WM9712 is not set +# CONFIG_SND_PXA2XX_SOC_CORGI is not set +# CONFIG_SND_PXA2XX_SOC_SPITZ is not set +CONFIG_SND_PXA2XX_SOC_TOSA=y # # Soc Codecs @@ -1100,6 +1105,7 @@ CONFIG_SND_SOC_WM9712=y # Open Sound System # # CONFIG_SOUND_PRIME is not set +CONFIG_HID=m # # USB support @@ -1301,12 +1307,12 @@ CONFIG_USB_IDMOUSE=m # # USB Gadget Support # -CONFIG_USB_GADGET=y +CONFIG_USB_GADGET=m # CONFIG_USB_GADGET_DEBUG_FILES is not set CONFIG_USB_GADGET_SELECTED=y # CONFIG_USB_GADGET_NET2280 is not set -CONFIG_USB_GADGET_PXA2XX=y -CONFIG_USB_PXA2XX=y +CONFIG_USB_GADGET_PXA2XX=m +CONFIG_USB_PXA2XX=m # CONFIG_USB_PXA2XX_SMALL is not set # CONFIG_USB_GADGET_PXA27X is not set # CONFIG_USB_GADGET_GOKU is not set @@ -1379,7 +1385,7 @@ CONFIG_FUSE_FS=m # # CD-ROM/DVD Filesystems # -# CONFIG_ISO9660_FS is not set +CONFIG_ISO9660_FS=m # CONFIG_UDF_FS is not set # @@ -1399,7 +1405,7 @@ CONFIG_PROC_FS=y CONFIG_SYSFS=y CONFIG_TMPFS=y # CONFIG_HUGETLB_PAGE is not set -CONFIG_RAMFS=y +# CONFIG_RAMFS is not set # CONFIG_RELAYFS_FS is not set # CONFIG_CONFIGFS_FS is not set @@ -1543,6 +1549,7 @@ CONFIG_DEBUG_KERNEL=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_DETECT_SOFTLOCKUP=y CONFIG_TIMER_STATS=y +# CONFIG_SCHED_DEBUG is not set # CONFIG_SCHEDSTATS is not set # CONFIG_DEBUG_SLAB is not set # CONFIG_DEBUG_PREEMPT is not set @@ -1572,7 +1579,8 @@ CONFIG_DEBUG_ERRORS=y # Cryptographic options # CONFIG_CRYPTO=y -CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_ALGAPI=m +CONFIG_CRYPTO_HMAC=m CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MD5=m diff --git a/packages/linux/linux-rp-2.6.23/sharpsl-pm-postresume-r1.patch b/packages/linux/linux-rp-2.6.23/sharpsl-pm-postresume-r1.patch new file mode 100644 index 0000000000..e1491d6d49 --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/sharpsl-pm-postresume-r1.patch @@ -0,0 +1,60 @@ + arch/arm/common/sharpsl_pm.c | 3 +++ + include/asm-arm/hardware/sharpsl_pm.h | 1 + + 2 files changed, 4 insertions(+) + +Index: git/include/asm-arm/hardware/sharpsl_pm.h +=================================================================== +--- git.orig/include/asm-arm/hardware/sharpsl_pm.h 2006-10-31 16:09:33.000000000 +0000 ++++ git/include/asm-arm/hardware/sharpsl_pm.h 2006-11-07 22:08:41.000000000 +0000 +@@ -26,6 +26,7 @@ struct sharpsl_charger_machinfo { + void (*presuspend)(void); + void (*postsuspend)(void); + void (*earlyresume)(void); ++ void (*postresume)(void); + unsigned long (*read_devdata)(int); + #define SHARPSL_BATT_VOLT 1 + #define SHARPSL_BATT_TEMP 2 +Index: git/arch/arm/common/sharpsl_pm.c +=================================================================== +--- git.orig/arch/arm/common/sharpsl_pm.c 2006-11-07 22:03:48.000000000 +0000 ++++ git/arch/arm/common/sharpsl_pm.c 2006-11-07 22:04:20.000000000 +0000 +@@ -584,6 +584,9 @@ static int corgi_pxa_pm_enter(suspend_st + if (sharpsl_pm.machinfo->earlyresume) + sharpsl_pm.machinfo->earlyresume(); + ++ if (sharpsl_pm.machinfo->postresume) ++ sharpsl_pm.machinfo->postresume(); ++ + dev_dbg(sharpsl_pm.dev, "SharpSL resuming...\n"); + + return 0; + arch/arm/common/sharpsl_pm.c | 3 +++ + include/asm-arm/hardware/sharpsl_pm.h | 1 + + 2 files changed, 4 insertions(+) + +Index: git/include/asm-arm/hardware/sharpsl_pm.h +=================================================================== +--- git.orig/include/asm-arm/hardware/sharpsl_pm.h 2006-10-31 16:09:33.000000000 +0000 ++++ git/include/asm-arm/hardware/sharpsl_pm.h 2006-11-07 22:08:41.000000000 +0000 +@@ -26,6 +26,7 @@ struct sharpsl_charger_machinfo { + void (*presuspend)(void); + void (*postsuspend)(void); + void (*earlyresume)(void); ++ void (*postresume)(void); + unsigned long (*read_devdata)(int); + #define SHARPSL_BATT_VOLT 1 + #define SHARPSL_BATT_TEMP 2 +Index: git/arch/arm/common/sharpsl_pm.c +=================================================================== +--- git.orig/arch/arm/common/sharpsl_pm.c 2006-11-07 22:03:48.000000000 +0000 ++++ git/arch/arm/common/sharpsl_pm.c 2006-11-07 22:04:20.000000000 +0000 +@@ -584,6 +584,9 @@ static int corgi_pxa_pm_enter(suspend_st + if (sharpsl_pm.machinfo->earlyresume) + sharpsl_pm.machinfo->earlyresume(); + ++ if (sharpsl_pm.machinfo->postresume) ++ sharpsl_pm.machinfo->postresume(); ++ + dev_dbg(sharpsl_pm.dev, "SharpSL resuming...\n"); + + return 0; diff --git a/packages/linux/linux-rp-2.6.23/tmio-fb-r6-fix-r0.patch b/packages/linux/linux-rp-2.6.23/tmio-fb-r6-fix-r0.patch new file mode 100644 index 0000000000..523edec381 --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/tmio-fb-r6-fix-r0.patch @@ -0,0 +1,90 @@ +From 302745ce6f3bab7b1a97de32339405ae3fd8eacb Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 00:05:54 +0400 +Subject: [PATCH] tmio-fb-r6.patch fixes + +--- + drivers/video/tmiofb.c | 8 ++++---- + 1 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c +index 10b0105..72eb76c 100644 +--- a/drivers/video/tmiofb.c ++++ b/drivers/video/tmiofb.c +@@ -463,8 +463,8 @@ static int tmiofb_vblank (struct fb_info *fbi, struct fb_vblank *vblank) + #define FBIO_TMIO_ACC_WRITE 0x7C639300 + #define FBIO_TMIO_ACC_SYNC 0x7C639301 + +-static int tmiofb_ioctl (struct inode *inode, struct file *file, +- unsigned int cmd, unsigned long arg, struct fb_info *fbi) ++static int tmiofb_ioctl (struct fb_info *fbi, ++ unsigned int cmd, unsigned long arg) + { + switch (cmd) { + case FBIOGET_VBLANK: { +@@ -677,7 +677,7 @@ static struct fb_ops tmiofb_ops_acc = { + * 2000 0002 display start + * 2000 0004 line number match (0x1ff mask???) + */ +-static irqreturn_t tmiofb_irq (int irq, void *__fbi, struct pt_regs *r) ++static irqreturn_t tmiofb_irq (int irq, void *__fbi) + { + struct fb_info* fbi = __fbi; + struct tmiofb_par* par = fbi->par; +@@ -762,7 +762,7 @@ static int __init tmiofb_probe (struct device *dev) + } + fbi->screen_base = par->sram; + +- retval = request_irq (irq->start, &tmiofb_irq, SA_INTERRUPT, ++ retval = request_irq (irq->start, &tmiofb_irq, IRQF_DISABLED, + TMIO_NAME_LCD, fbi); + if (retval) + goto err_request_irq; +-- +1.4.4.4 + +From 302745ce6f3bab7b1a97de32339405ae3fd8eacb Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 00:05:54 +0400 +Subject: [PATCH] tmio-fb-r6.patch fixes + +--- + drivers/video/tmiofb.c | 8 ++++---- + 1 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c +index 10b0105..72eb76c 100644 +--- a/drivers/video/tmiofb.c ++++ b/drivers/video/tmiofb.c +@@ -463,8 +463,8 @@ static int tmiofb_vblank (struct fb_info *fbi, struct fb_vblank *vblank) + #define FBIO_TMIO_ACC_WRITE 0x7C639300 + #define FBIO_TMIO_ACC_SYNC 0x7C639301 + +-static int tmiofb_ioctl (struct inode *inode, struct file *file, +- unsigned int cmd, unsigned long arg, struct fb_info *fbi) ++static int tmiofb_ioctl (struct fb_info *fbi, ++ unsigned int cmd, unsigned long arg) + { + switch (cmd) { + case FBIOGET_VBLANK: { +@@ -677,7 +677,7 @@ static struct fb_ops tmiofb_ops_acc = { + * 2000 0002 display start + * 2000 0004 line number match (0x1ff mask???) + */ +-static irqreturn_t tmiofb_irq (int irq, void *__fbi, struct pt_regs *r) ++static irqreturn_t tmiofb_irq (int irq, void *__fbi) + { + struct fb_info* fbi = __fbi; + struct tmiofb_par* par = fbi->par; +@@ -762,7 +762,7 @@ static int __init tmiofb_probe (struct device *dev) + } + fbi->screen_base = par->sram; + +- retval = request_irq (irq->start, &tmiofb_irq, SA_INTERRUPT, ++ retval = request_irq (irq->start, &tmiofb_irq, IRQF_DISABLED, + TMIO_NAME_LCD, fbi); + if (retval) + goto err_request_irq; +-- +1.4.4.4 + diff --git a/packages/linux/linux-rp-2.6.23/tmio-nand-r8.patch b/packages/linux/linux-rp-2.6.23/tmio-nand-r8.patch new file mode 100644 index 0000000000..3a30c2f34c --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/tmio-nand-r8.patch @@ -0,0 +1,1188 @@ + drivers/mtd/nand/Kconfig | 7 + + drivers/mtd/nand/Makefile | 1 + + drivers/mtd/nand/tmio.c | 554 +++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 562 insertions(+), 0 deletions(-) + +diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig +index f1d60b6..b9c8796 100644 +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -69,6 +69,13 @@ config MTD_NAND_AMS_DELTA + help + Support for NAND flash on Amstrad E3 (Delta). + ++config MTD_NAND_TMIO ++ tristate "NAND Flash device on Toshiba Mobile IO Controller" ++ depends on MTD_NAND && TOSHIBA_TC6393XB ++ help ++ Support for NAND flash connected to a Toshiba Mobile IO ++ Controller in some PDAs, including the Sharp SL6000x. ++ + config MTD_NAND_TOTO + tristate "NAND Flash device on TOTO board" + depends on ARCH_OMAP && BROKEN +diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile +index edba1db..64f24e1 100644 +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -27,5 +27,6 @@ obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o + obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o + obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o + obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o ++obj-$(CONFIG_MTD_NAND_TMIO) += tmio.o + + nand-objs := nand_base.o nand_bbt.o +diff --git a/drivers/mtd/nand/tmio.c b/drivers/mtd/nand/tmio.c +new file mode 100644 +index 0000000..d196553 +--- /dev/null ++++ b/drivers/mtd/nand/tmio.c +@@ -0,0 +1,554 @@ ++/* ++ * A device driver for NAND flash connected to a Toshiba Mobile IO ++ * controller. This is known to work with the following variants: ++ * TC6393XB revision 3 ++ * ++ * Maintainer: Chris Humbert <mahadri+mtd@drigon.com> ++ * ++ * Copyright (C) 2005 Chris Humbert ++ * Copyright (C) 2005 Dirk Opfer ++ * Copyright (C) 2004 SHARP ++ * Copyright (C) 2002 Lineo Japan, Inc. ++ * Copyright (C) Ian Molton and Sebastian Carlier ++ * ++ * Based on Sharp's NAND driver, sharp_sl_tc6393.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/delay.h> ++#include <linux/wait.h> ++#include <linux/ioport.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/nand_ecc.h> ++#include <linux/mtd/partitions.h> ++#include <asm/io.h> ++#include <asm/hardware/tmio.h> ++ ++#include <linux/interrupt.h> ++ ++#define mtd_printk(level, mtd, format, arg...) \ ++ printk (level "%s: " format, mtd->name, ## arg) ++#define mtd_warn(mtd, format, arg...) \ ++ mtd_printk (KERN_WARNING, mtd, format, ## arg) ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* tmio_nfcr.mode Register Command List */ ++#define FCR_MODE_DATA 0x94 // Data Data_Mode ++#define FCR_MODE_COMMAND 0x95 // Data Command_Mode ++#define FCR_MODE_ADDRESS 0x96 // Data Address_Mode ++ ++#define FCR_MODE_HWECC_CALC 0xB4 // HW-ECC Data ++#define FCR_MODE_HWECC_RESULT 0xD4 // HW-ECC Calculation Result Read_Mode ++#define FCR_MODE_HWECC_RESET 0xF4 // HW-ECC Reset ++ ++#define FCR_MODE_POWER_ON 0x0C // Power Supply ON to SSFDC card ++#define FCR_MODE_POWER_OFF 0x08 // Power Supply OFF to SSFDC card ++ ++#define FCR_MODE_LED_OFF 0x00 // LED OFF ++#define FCR_MODE_LED_ON 0x04 // LED ON ++ ++#define FCR_MODE_EJECT_ON 0x68 // Ejection Demand from Penguin is Advanced ++#define FCR_MODE_EJECT_OFF 0x08 // Ejection Demand from Penguin is Not Advanced ++ ++#define FCR_MODE_LOCK 0x6C // Operates By Lock_Mode. Ejection Switch is Invalid ++#define FCR_MODE_UNLOCK 0x0C // Operates By UnLock_Mode.Ejection Switch is Effective ++ ++#define FCR_MODE_CONTROLLER_ID 0x40 // Controller ID Read ++#define FCR_MODE_STANDBY 0x00 // SSFDC card Changes Standby State ++ ++#define FCR_MODE_WE 0x80 ++#define FCR_MODE_ECC1 0x40 ++#define FCR_MODE_ECC0 0x20 ++#define FCR_MODE_CE 0x10 ++#define FCR_MODE_PCNT1 0x08 ++#define FCR_MODE_PCNT0 0x04 ++#define FCR_MODE_ALE 0x02 ++#define FCR_MODE_CLE 0x01 ++ ++#define FCR_STATUS_BUSY 0x80 ++ ++/* ++ * NAND Flash Host Controller Configuration Register ++ */ ++struct tmio_nfhccr { ++ u8 x00[4]; ++ u16 command; /* 0x04 Command */ ++ u8 x01[0x0a]; ++ u16 base[2]; /* 0x10 NAND Flash Control Reg Base Addr*/ ++ u8 x02[0x29]; ++ u8 intp; /* 0x3d Interrupt Pin */ ++ u8 x03[0x0a]; ++ u8 inte; /* 0x48 Interrupt Enable */ ++ u8 x04; ++ u8 ec; /* 0x4a Event Control */ ++ u8 x05; ++ u8 icc; /* 0x4c Internal Clock Control */ ++ u8 x06[0x0e]; ++ u8 eccc; /* 0x5b ECC Control */ ++ u8 x07[4]; ++ u8 nftc; /* 0x60 NAND Flash Transaction Control */ ++ u8 nfm; /* 0x61 NAND Flash Monitor */ ++ u8 nfpsc; /* 0x62 NAND Flash Power Supply Control */ ++ u8 nfdc; /* 0x63 NAND Flash Detect Control */ ++ u8 x08[0x9c]; ++} __attribute__ ((packed)); ++ ++/* ++ * NAND Flash Control Register ++ */ ++struct tmio_nfcr { ++union { ++ u8 u8; /* 0x00 Data Register */ ++ u16 u16; ++ u32 u32; ++} __attribute__ ((packed)); ++ u8 mode; /* 0x04 Mode Register */ ++ u8 status; /* 0x05 Status Register */ ++ u8 isr; /* 0x06 Interrupt Status Register */ ++ u8 imr; /* 0x07 Interrupt Mask Register */ ++} __attribute__ ((packed)); ++ ++struct tmio_nand { ++ struct mtd_info mtd; ++ struct nand_chip chip; ++ ++ struct tmio_nfhccr __iomem * ccr; ++ struct tmio_nfcr __iomem * fcr; ++ ++ unsigned int irq; ++ ++ /* for tmio_nand_read_byte */ ++ u8 read; ++ unsigned read_good:1; ++}; ++ ++#define mtd_to_tmio(m) container_of(m, struct tmio_nand, mtd) ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void tmio_nand_hwcontrol(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem *fcr = tmio->fcr; ++ struct nand_chip *chip = mtd->priv; ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ u8 mode; ++ ++ if (ctrl & NAND_NCE) { ++ mode = FCR_MODE_DATA; ++ ++ if (ctrl & NAND_CLE) ++ mode |= FCR_MODE_CLE; ++ else ++ mode &= ~FCR_MODE_CLE; ++ ++ if (ctrl & NAND_ALE) ++ mode |= FCR_MODE_ALE; ++ else ++ mode &= ~FCR_MODE_ALE; ++ } else { ++ mode = FCR_MODE_STANDBY; ++ } ++ ++ iowrite8 (mode, &fcr->mode); ++ tmio->read_good = 0; ++ } ++ ++ if (cmd != NAND_CMD_NONE) ++ writeb(cmd, chip->IO_ADDR_W); ++} ++ ++static int tmio_nand_dev_ready (struct mtd_info* mtd) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ return !(ioread8 (&fcr->status) & FCR_STATUS_BUSY); ++} ++ ++static irqreturn_t tmio_irq (int irq, void *__tmio) ++{ ++ struct tmio_nand* tmio = __tmio; ++ struct nand_chip* this = &tmio->chip; ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ /* disable RDYREQ interrupt */ ++ iowrite8 (0x00, &fcr->imr); ++ ++ if (unlikely (!waitqueue_active (&this->controller->wq))) ++ printk (KERN_WARNING TMIO_NAME_NAND ": spurious interrupt\n"); ++ ++ wake_up (&this->controller->wq); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * The TMIO core has a RDYREQ interrupt on the posedge of #SMRB. ++ * This interrupt is normally disabled, but for long operations like ++ * erase and write, we enable it to wake us up. The irq handler ++ * disables the interrupt. ++ */ ++static int ++tmio_nand_wait (struct mtd_info *mtd, struct nand_chip *this) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ long timeout; ++ ++ /* enable RDYREQ interrupt */ ++ iowrite8 (0x0f, &fcr->isr); ++ iowrite8 (0x81, &fcr->imr); ++ ++ timeout = wait_event_timeout (this->controller->wq, tmio_nand_dev_ready (mtd), ++ msecs_to_jiffies (this->state == FL_ERASING ? 400 : 20)); ++ ++ if (unlikely (!tmio_nand_dev_ready (mtd))) { ++ iowrite8 (0x00, &fcr->imr); ++ mtd_warn (mtd, "still busy with %s after %d ms\n", ++ this->state == FL_ERASING ? "erase" : "program", ++ this->state == FL_ERASING ? 400 : 20); ++ ++ } else if (unlikely (!timeout)) { ++ iowrite8 (0x00, &fcr->imr); ++ mtd_warn (mtd, "timeout waiting for interrupt\n"); ++ } ++ ++ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); ++ return this->read_byte (mtd); ++} ++ ++/* ++ * The TMIO controller combines two 8-bit data bytes into one 16-bit ++ * word. This function separates them so nand_base.c works as expected, ++ * especially its NAND_CMD_READID routines. ++ * ++ * To prevent stale data from being read, tmio_nand_hwcontrol() clears ++ * tmio->read_good. ++ */ ++static u_char tmio_nand_read_byte (struct mtd_info *mtd) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ unsigned int data; ++ ++ if (tmio->read_good--) ++ return tmio->read; ++ ++ data = ioread16 (&fcr->u16); ++ tmio->read = data >> 8; ++ return data; ++} ++ ++/* ++ * The TMIO controller converts an 8-bit NAND interface to a 16-bit ++ * bus interface, so all data reads and writes must be 16-bit wide. ++ * Thus, we implement 16-bit versions of the read, write, and verify ++ * buffer functions. ++ */ ++static void ++tmio_nand_write_buf (struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ iowrite16_rep (&fcr->u16, buf, len >> 1); ++} ++ ++static void tmio_nand_read_buf (struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ ioread16_rep (&fcr->u16, buf, len >> 1); ++} ++ ++static int ++tmio_nand_verify_buf (struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ u16* p = (u16*) buf; ++ ++ for (len >>= 1; len; len--) ++ if (*(p++) != ioread16 (&fcr->u16)) ++ return -EFAULT; ++ return 0; ++} ++ ++static void tmio_nand_enable_hwecc (struct mtd_info* mtd, int mode) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ iowrite8 (FCR_MODE_HWECC_RESET, &fcr->mode); ++ ioread8 (&fcr->u8); /* dummy read */ ++ iowrite8 (FCR_MODE_HWECC_CALC, &fcr->mode); ++} ++ ++static int tmio_nand_calculate_ecc (struct mtd_info* mtd, const u_char* dat, ++ u_char* ecc_code) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ unsigned int ecc; ++ ++ iowrite8 (FCR_MODE_HWECC_RESULT, &fcr->mode); ++ ++ ecc = ioread16 (&fcr->u16); ++ ecc_code[1] = ecc; // 000-255 LP7-0 ++ ecc_code[0] = ecc >> 8; // 000-255 LP15-8 ++ ecc = ioread16 (&fcr->u16); ++ ecc_code[2] = ecc; // 000-255 CP5-0,11b ++ ecc_code[4] = ecc >> 8; // 256-511 LP7-0 ++ ecc = ioread16 (&fcr->u16); ++ ecc_code[3] = ecc; // 256-511 LP15-8 ++ ecc_code[5] = ecc >> 8; // 256-511 CP5-0,11b ++ ++ iowrite8 (FCR_MODE_DATA, &fcr->mode); ++ return 0; ++} ++ ++static void tmio_hw_init (struct device *dev, struct tmio_nand *tmio) ++{ ++ struct resource* nfcr = tmio_resource_control (dev); ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct tmio_nfhccr __iomem * ccr = tmio->ccr; ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ unsigned long base; ++ ++ /* (89h) SMD Buffer ON By TC6393XB SystemConfig gpibfc1 */ ++ tdev->ops->clock (dev, 1); ++ tdev->ops->function (dev, 1); ++ ++ /* (4Ch) CLKRUN Enable 1st spcrunc */ ++ iowrite8 (0x81, &ccr->icc); ++ ++ /* (10h)BaseAddress 0x1000 spba.spba2 */ ++ base = nfcr->start - tdev->iomem->start; ++ iowrite16 (base, ccr->base + 0); ++ iowrite16 (base >> 16, ccr->base + 1); ++ ++ /* (04h)Command Register I/O spcmd */ ++ iowrite8 (0x02, &ccr->command); ++ ++ /* (62h) Power Supply Control ssmpwc */ ++ /* HardPowerOFF - SuspendOFF - PowerSupplyWait_4MS */ ++ iowrite8 (0x02, &ccr->nfpsc); ++ ++ /* (63h) Detect Control ssmdtc */ ++ iowrite8 (0x02, &ccr->nfdc); ++ ++ /* Interrupt status register clear sintst */ ++ iowrite8 (0x0f, &fcr->isr); ++ ++ /* After power supply, Media are reset smode */ ++ iowrite8 (FCR_MODE_POWER_ON, &fcr->mode); ++ iowrite8 (FCR_MODE_COMMAND, &fcr->mode); ++ iowrite8 (NAND_CMD_RESET, &fcr->u8); ++ ++ /* Standby Mode smode */ ++ iowrite8 (FCR_MODE_STANDBY, &fcr->mode); ++ ++ mdelay (5); ++} ++ ++static void tmio_hw_stop (struct device *dev, struct tmio_nand *tmio) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ iowrite8 (FCR_MODE_POWER_OFF, &fcr->mode); ++ tdev->ops->function (dev, 0); ++ tdev->ops->clock (dev, 0); ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++#ifdef CONFIG_MTD_PARTITIONS ++static const char *part_probes[] = { "cmdlinepart", NULL }; ++#endif ++ ++static int tmio_probe (struct device *dev) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct tmio_nand_platform_data* tnpd = dev->platform_data; ++ struct resource* ccr = tmio_resource_config (dev); ++ struct resource* fcr = tmio_resource_control (dev); ++ struct resource* irq = tmio_resource_irq (dev); ++ struct tmio_nand* tmio; ++ struct mtd_info* mtd; ++ struct nand_chip* this; ++ struct mtd_partition* parts; ++ int nbparts = 0; ++ int retval; ++ ++ if (!tnpd) ++ return -EINVAL; ++ ++ retval = request_resource (tdev->iomem, ccr); ++ if (retval) ++ goto err_request_ccr; ++ ++ retval = request_resource (tdev->iomem, fcr); ++ if (retval) ++ goto err_request_fcr; ++ ++ tmio = kzalloc (sizeof *tmio, GFP_KERNEL); ++ if (!tmio) { ++ retval = -ENOMEM; ++ goto err_kzalloc; ++ } ++ ++ dev_set_drvdata (dev, tmio); ++ mtd = &tmio->mtd; ++ this = &tmio->chip; ++ mtd->priv = this; ++ mtd->name = TMIO_NAME_NAND; ++ ++ tmio->ccr = ioremap (ccr->start, ccr->end - ccr->start + 1); ++ if (!tmio->ccr) { ++ retval = -EIO; ++ goto err_iomap_ccr; ++ } ++ ++ tmio->fcr = ioremap (fcr->start, fcr->end - fcr->start + 1); ++ if (!tmio->fcr) { ++ retval = -EIO; ++ goto err_iomap_fcr; ++ } ++ ++ tmio_hw_init (dev, tmio); ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = tmio->fcr; ++ this->IO_ADDR_W = tmio->fcr; ++ ++ /* Set address of hardware control function */ ++ this->cmd_ctrl = tmio_nand_hwcontrol; ++ this->dev_ready = tmio_nand_dev_ready; ++ this->read_byte = tmio_nand_read_byte; ++ this->write_buf = tmio_nand_write_buf; ++ this->read_buf = tmio_nand_read_buf; ++ this->verify_buf = tmio_nand_verify_buf; ++ ++ /* set eccmode using hardware ECC */ ++ this->ecc.mode = NAND_ECC_HW; ++ this->ecc.size = 512; ++ this->ecc.bytes = 6; ++ this->ecc.hwctl = tmio_nand_enable_hwecc; ++ this->ecc.calculate = tmio_nand_calculate_ecc; ++ this->ecc.correct = nand_correct_data; ++ this->badblock_pattern = tnpd->badblock_pattern; ++ ++ /* 15 us command delay time */ ++ this->chip_delay = 15; ++ ++ if (irq->start) { ++ retval = request_irq (irq->start, &tmio_irq, ++ IRQF_DISABLED, irq->name, tmio); ++ if (!retval) { ++ tmio->irq = irq->start; ++ this->waitfunc = tmio_nand_wait; ++ } else ++ mtd_warn (mtd, "request_irq error %d\n", retval); ++ } ++ ++ /* Scan to find existence of the device */ ++ if (nand_scan (mtd, 1)) { ++ retval = -ENODEV; ++ goto err_scan; ++ } ++ ++ /* Register the partitions */ ++#ifdef CONFIG_MTD_PARTITIONS ++ nbparts = parse_mtd_partitions (mtd, part_probes, &parts, 0); ++#endif ++ if (nbparts <= 0) { ++ parts = tnpd->partition; ++ nbparts = tnpd->num_partitions; ++ } ++ ++ add_mtd_partitions (mtd, parts, nbparts); ++ return 0; ++ ++err_scan: ++ if (tmio->irq) ++ free_irq (tmio->irq, tmio); ++ tmio_hw_stop (dev, tmio); ++ iounmap (tmio->fcr); ++err_iomap_fcr: ++ iounmap (tmio->ccr); ++err_iomap_ccr: ++ kfree (tmio); ++err_kzalloc: ++ release_resource (fcr); ++err_request_fcr: ++ release_resource (ccr); ++err_request_ccr: ++ return retval; ++} ++ ++static int tmio_remove (struct device *dev) ++{ ++ struct tmio_nand* tmio = dev_get_drvdata (dev); ++ ++ nand_release (&tmio->mtd); ++ if (tmio->irq) ++ free_irq (tmio->irq, tmio); ++ tmio_hw_stop (dev, tmio); ++ iounmap (tmio->fcr); ++ iounmap (tmio->ccr); ++ kfree (tmio); ++ release_resource (tmio_resource_control (dev)); ++ release_resource (tmio_resource_config (dev)); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int tmio_suspend (struct device *dev, pm_message_t state) ++{ ++ tmio_hw_stop (dev, dev_get_drvdata (dev)); ++ return 0; ++} ++ ++static int tmio_resume (struct device *dev) ++{ ++ tmio_hw_init (dev, dev_get_drvdata (dev)); ++ return 0; ++} ++#endif ++ ++static struct device_driver tmio_driver = { ++ .name = TMIO_NAME_NAND, ++ .bus = &tmio_bus_type, ++ .probe = tmio_probe, ++ .remove = tmio_remove, ++#ifdef CONFIG_PM ++ .suspend = tmio_suspend, ++ .resume = tmio_resume, ++#endif ++}; ++ ++static int __init tmio_init (void) { ++ return driver_register (&tmio_driver); ++} ++ ++static void __exit tmio_exit (void) { ++ driver_unregister (&tmio_driver); ++} ++ ++module_init (tmio_init); ++module_exit (tmio_exit); ++ ++MODULE_LICENSE ("GPL"); ++MODULE_AUTHOR ("Dirk Opfer, Chris Humbert"); ++MODULE_DESCRIPTION ("NAND flash driver on Toshiba Mobile IO controller"); + drivers/mtd/nand/Kconfig | 7 + + drivers/mtd/nand/Makefile | 1 + + drivers/mtd/nand/tmio.c | 554 +++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 562 insertions(+), 0 deletions(-) + +diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig +index f1d60b6..b9c8796 100644 +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -69,6 +69,13 @@ config MTD_NAND_AMS_DELTA + help + Support for NAND flash on Amstrad E3 (Delta). + ++config MTD_NAND_TMIO ++ tristate "NAND Flash device on Toshiba Mobile IO Controller" ++ depends on MTD_NAND && TOSHIBA_TC6393XB ++ help ++ Support for NAND flash connected to a Toshiba Mobile IO ++ Controller in some PDAs, including the Sharp SL6000x. ++ + config MTD_NAND_TOTO + tristate "NAND Flash device on TOTO board" + depends on ARCH_OMAP && BROKEN +diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile +index edba1db..64f24e1 100644 +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -27,5 +27,6 @@ obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o + obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o + obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o + obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o ++obj-$(CONFIG_MTD_NAND_TMIO) += tmio.o + + nand-objs := nand_base.o nand_bbt.o +diff --git a/drivers/mtd/nand/tmio.c b/drivers/mtd/nand/tmio.c +new file mode 100644 +index 0000000..d196553 +--- /dev/null ++++ b/drivers/mtd/nand/tmio.c +@@ -0,0 +1,554 @@ ++/* ++ * A device driver for NAND flash connected to a Toshiba Mobile IO ++ * controller. This is known to work with the following variants: ++ * TC6393XB revision 3 ++ * ++ * Maintainer: Chris Humbert <mahadri+mtd@drigon.com> ++ * ++ * Copyright (C) 2005 Chris Humbert ++ * Copyright (C) 2005 Dirk Opfer ++ * Copyright (C) 2004 SHARP ++ * Copyright (C) 2002 Lineo Japan, Inc. ++ * Copyright (C) Ian Molton and Sebastian Carlier ++ * ++ * Based on Sharp's NAND driver, sharp_sl_tc6393.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/delay.h> ++#include <linux/wait.h> ++#include <linux/ioport.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/nand_ecc.h> ++#include <linux/mtd/partitions.h> ++#include <asm/io.h> ++#include <asm/hardware/tmio.h> ++ ++#include <linux/interrupt.h> ++ ++#define mtd_printk(level, mtd, format, arg...) \ ++ printk (level "%s: " format, mtd->name, ## arg) ++#define mtd_warn(mtd, format, arg...) \ ++ mtd_printk (KERN_WARNING, mtd, format, ## arg) ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* tmio_nfcr.mode Register Command List */ ++#define FCR_MODE_DATA 0x94 // Data Data_Mode ++#define FCR_MODE_COMMAND 0x95 // Data Command_Mode ++#define FCR_MODE_ADDRESS 0x96 // Data Address_Mode ++ ++#define FCR_MODE_HWECC_CALC 0xB4 // HW-ECC Data ++#define FCR_MODE_HWECC_RESULT 0xD4 // HW-ECC Calculation Result Read_Mode ++#define FCR_MODE_HWECC_RESET 0xF4 // HW-ECC Reset ++ ++#define FCR_MODE_POWER_ON 0x0C // Power Supply ON to SSFDC card ++#define FCR_MODE_POWER_OFF 0x08 // Power Supply OFF to SSFDC card ++ ++#define FCR_MODE_LED_OFF 0x00 // LED OFF ++#define FCR_MODE_LED_ON 0x04 // LED ON ++ ++#define FCR_MODE_EJECT_ON 0x68 // Ejection Demand from Penguin is Advanced ++#define FCR_MODE_EJECT_OFF 0x08 // Ejection Demand from Penguin is Not Advanced ++ ++#define FCR_MODE_LOCK 0x6C // Operates By Lock_Mode. Ejection Switch is Invalid ++#define FCR_MODE_UNLOCK 0x0C // Operates By UnLock_Mode.Ejection Switch is Effective ++ ++#define FCR_MODE_CONTROLLER_ID 0x40 // Controller ID Read ++#define FCR_MODE_STANDBY 0x00 // SSFDC card Changes Standby State ++ ++#define FCR_MODE_WE 0x80 ++#define FCR_MODE_ECC1 0x40 ++#define FCR_MODE_ECC0 0x20 ++#define FCR_MODE_CE 0x10 ++#define FCR_MODE_PCNT1 0x08 ++#define FCR_MODE_PCNT0 0x04 ++#define FCR_MODE_ALE 0x02 ++#define FCR_MODE_CLE 0x01 ++ ++#define FCR_STATUS_BUSY 0x80 ++ ++/* ++ * NAND Flash Host Controller Configuration Register ++ */ ++struct tmio_nfhccr { ++ u8 x00[4]; ++ u16 command; /* 0x04 Command */ ++ u8 x01[0x0a]; ++ u16 base[2]; /* 0x10 NAND Flash Control Reg Base Addr*/ ++ u8 x02[0x29]; ++ u8 intp; /* 0x3d Interrupt Pin */ ++ u8 x03[0x0a]; ++ u8 inte; /* 0x48 Interrupt Enable */ ++ u8 x04; ++ u8 ec; /* 0x4a Event Control */ ++ u8 x05; ++ u8 icc; /* 0x4c Internal Clock Control */ ++ u8 x06[0x0e]; ++ u8 eccc; /* 0x5b ECC Control */ ++ u8 x07[4]; ++ u8 nftc; /* 0x60 NAND Flash Transaction Control */ ++ u8 nfm; /* 0x61 NAND Flash Monitor */ ++ u8 nfpsc; /* 0x62 NAND Flash Power Supply Control */ ++ u8 nfdc; /* 0x63 NAND Flash Detect Control */ ++ u8 x08[0x9c]; ++} __attribute__ ((packed)); ++ ++/* ++ * NAND Flash Control Register ++ */ ++struct tmio_nfcr { ++union { ++ u8 u8; /* 0x00 Data Register */ ++ u16 u16; ++ u32 u32; ++} __attribute__ ((packed)); ++ u8 mode; /* 0x04 Mode Register */ ++ u8 status; /* 0x05 Status Register */ ++ u8 isr; /* 0x06 Interrupt Status Register */ ++ u8 imr; /* 0x07 Interrupt Mask Register */ ++} __attribute__ ((packed)); ++ ++struct tmio_nand { ++ struct mtd_info mtd; ++ struct nand_chip chip; ++ ++ struct tmio_nfhccr __iomem * ccr; ++ struct tmio_nfcr __iomem * fcr; ++ ++ unsigned int irq; ++ ++ /* for tmio_nand_read_byte */ ++ u8 read; ++ unsigned read_good:1; ++}; ++ ++#define mtd_to_tmio(m) container_of(m, struct tmio_nand, mtd) ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void tmio_nand_hwcontrol(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem *fcr = tmio->fcr; ++ struct nand_chip *chip = mtd->priv; ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ u8 mode; ++ ++ if (ctrl & NAND_NCE) { ++ mode = FCR_MODE_DATA; ++ ++ if (ctrl & NAND_CLE) ++ mode |= FCR_MODE_CLE; ++ else ++ mode &= ~FCR_MODE_CLE; ++ ++ if (ctrl & NAND_ALE) ++ mode |= FCR_MODE_ALE; ++ else ++ mode &= ~FCR_MODE_ALE; ++ } else { ++ mode = FCR_MODE_STANDBY; ++ } ++ ++ iowrite8 (mode, &fcr->mode); ++ tmio->read_good = 0; ++ } ++ ++ if (cmd != NAND_CMD_NONE) ++ writeb(cmd, chip->IO_ADDR_W); ++} ++ ++static int tmio_nand_dev_ready (struct mtd_info* mtd) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ return !(ioread8 (&fcr->status) & FCR_STATUS_BUSY); ++} ++ ++static irqreturn_t tmio_irq (int irq, void *__tmio) ++{ ++ struct tmio_nand* tmio = __tmio; ++ struct nand_chip* this = &tmio->chip; ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ /* disable RDYREQ interrupt */ ++ iowrite8 (0x00, &fcr->imr); ++ ++ if (unlikely (!waitqueue_active (&this->controller->wq))) ++ printk (KERN_WARNING TMIO_NAME_NAND ": spurious interrupt\n"); ++ ++ wake_up (&this->controller->wq); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * The TMIO core has a RDYREQ interrupt on the posedge of #SMRB. ++ * This interrupt is normally disabled, but for long operations like ++ * erase and write, we enable it to wake us up. The irq handler ++ * disables the interrupt. ++ */ ++static int ++tmio_nand_wait (struct mtd_info *mtd, struct nand_chip *this) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ long timeout; ++ ++ /* enable RDYREQ interrupt */ ++ iowrite8 (0x0f, &fcr->isr); ++ iowrite8 (0x81, &fcr->imr); ++ ++ timeout = wait_event_timeout (this->controller->wq, tmio_nand_dev_ready (mtd), ++ msecs_to_jiffies (this->state == FL_ERASING ? 400 : 20)); ++ ++ if (unlikely (!tmio_nand_dev_ready (mtd))) { ++ iowrite8 (0x00, &fcr->imr); ++ mtd_warn (mtd, "still busy with %s after %d ms\n", ++ this->state == FL_ERASING ? "erase" : "program", ++ this->state == FL_ERASING ? 400 : 20); ++ ++ } else if (unlikely (!timeout)) { ++ iowrite8 (0x00, &fcr->imr); ++ mtd_warn (mtd, "timeout waiting for interrupt\n"); ++ } ++ ++ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); ++ return this->read_byte (mtd); ++} ++ ++/* ++ * The TMIO controller combines two 8-bit data bytes into one 16-bit ++ * word. This function separates them so nand_base.c works as expected, ++ * especially its NAND_CMD_READID routines. ++ * ++ * To prevent stale data from being read, tmio_nand_hwcontrol() clears ++ * tmio->read_good. ++ */ ++static u_char tmio_nand_read_byte (struct mtd_info *mtd) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ unsigned int data; ++ ++ if (tmio->read_good--) ++ return tmio->read; ++ ++ data = ioread16 (&fcr->u16); ++ tmio->read = data >> 8; ++ return data; ++} ++ ++/* ++ * The TMIO controller converts an 8-bit NAND interface to a 16-bit ++ * bus interface, so all data reads and writes must be 16-bit wide. ++ * Thus, we implement 16-bit versions of the read, write, and verify ++ * buffer functions. ++ */ ++static void ++tmio_nand_write_buf (struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ iowrite16_rep (&fcr->u16, buf, len >> 1); ++} ++ ++static void tmio_nand_read_buf (struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ ioread16_rep (&fcr->u16, buf, len >> 1); ++} ++ ++static int ++tmio_nand_verify_buf (struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ u16* p = (u16*) buf; ++ ++ for (len >>= 1; len; len--) ++ if (*(p++) != ioread16 (&fcr->u16)) ++ return -EFAULT; ++ return 0; ++} ++ ++static void tmio_nand_enable_hwecc (struct mtd_info* mtd, int mode) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ iowrite8 (FCR_MODE_HWECC_RESET, &fcr->mode); ++ ioread8 (&fcr->u8); /* dummy read */ ++ iowrite8 (FCR_MODE_HWECC_CALC, &fcr->mode); ++} ++ ++static int tmio_nand_calculate_ecc (struct mtd_info* mtd, const u_char* dat, ++ u_char* ecc_code) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ unsigned int ecc; ++ ++ iowrite8 (FCR_MODE_HWECC_RESULT, &fcr->mode); ++ ++ ecc = ioread16 (&fcr->u16); ++ ecc_code[1] = ecc; // 000-255 LP7-0 ++ ecc_code[0] = ecc >> 8; // 000-255 LP15-8 ++ ecc = ioread16 (&fcr->u16); ++ ecc_code[2] = ecc; // 000-255 CP5-0,11b ++ ecc_code[4] = ecc >> 8; // 256-511 LP7-0 ++ ecc = ioread16 (&fcr->u16); ++ ecc_code[3] = ecc; // 256-511 LP15-8 ++ ecc_code[5] = ecc >> 8; // 256-511 CP5-0,11b ++ ++ iowrite8 (FCR_MODE_DATA, &fcr->mode); ++ return 0; ++} ++ ++static void tmio_hw_init (struct device *dev, struct tmio_nand *tmio) ++{ ++ struct resource* nfcr = tmio_resource_control (dev); ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct tmio_nfhccr __iomem * ccr = tmio->ccr; ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ unsigned long base; ++ ++ /* (89h) SMD Buffer ON By TC6393XB SystemConfig gpibfc1 */ ++ tdev->ops->clock (dev, 1); ++ tdev->ops->function (dev, 1); ++ ++ /* (4Ch) CLKRUN Enable 1st spcrunc */ ++ iowrite8 (0x81, &ccr->icc); ++ ++ /* (10h)BaseAddress 0x1000 spba.spba2 */ ++ base = nfcr->start - tdev->iomem->start; ++ iowrite16 (base, ccr->base + 0); ++ iowrite16 (base >> 16, ccr->base + 1); ++ ++ /* (04h)Command Register I/O spcmd */ ++ iowrite8 (0x02, &ccr->command); ++ ++ /* (62h) Power Supply Control ssmpwc */ ++ /* HardPowerOFF - SuspendOFF - PowerSupplyWait_4MS */ ++ iowrite8 (0x02, &ccr->nfpsc); ++ ++ /* (63h) Detect Control ssmdtc */ ++ iowrite8 (0x02, &ccr->nfdc); ++ ++ /* Interrupt status register clear sintst */ ++ iowrite8 (0x0f, &fcr->isr); ++ ++ /* After power supply, Media are reset smode */ ++ iowrite8 (FCR_MODE_POWER_ON, &fcr->mode); ++ iowrite8 (FCR_MODE_COMMAND, &fcr->mode); ++ iowrite8 (NAND_CMD_RESET, &fcr->u8); ++ ++ /* Standby Mode smode */ ++ iowrite8 (FCR_MODE_STANDBY, &fcr->mode); ++ ++ mdelay (5); ++} ++ ++static void tmio_hw_stop (struct device *dev, struct tmio_nand *tmio) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ iowrite8 (FCR_MODE_POWER_OFF, &fcr->mode); ++ tdev->ops->function (dev, 0); ++ tdev->ops->clock (dev, 0); ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++#ifdef CONFIG_MTD_PARTITIONS ++static const char *part_probes[] = { "cmdlinepart", NULL }; ++#endif ++ ++static int tmio_probe (struct device *dev) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct tmio_nand_platform_data* tnpd = dev->platform_data; ++ struct resource* ccr = tmio_resource_config (dev); ++ struct resource* fcr = tmio_resource_control (dev); ++ struct resource* irq = tmio_resource_irq (dev); ++ struct tmio_nand* tmio; ++ struct mtd_info* mtd; ++ struct nand_chip* this; ++ struct mtd_partition* parts; ++ int nbparts = 0; ++ int retval; ++ ++ if (!tnpd) ++ return -EINVAL; ++ ++ retval = request_resource (tdev->iomem, ccr); ++ if (retval) ++ goto err_request_ccr; ++ ++ retval = request_resource (tdev->iomem, fcr); ++ if (retval) ++ goto err_request_fcr; ++ ++ tmio = kzalloc (sizeof *tmio, GFP_KERNEL); ++ if (!tmio) { ++ retval = -ENOMEM; ++ goto err_kzalloc; ++ } ++ ++ dev_set_drvdata (dev, tmio); ++ mtd = &tmio->mtd; ++ this = &tmio->chip; ++ mtd->priv = this; ++ mtd->name = TMIO_NAME_NAND; ++ ++ tmio->ccr = ioremap (ccr->start, ccr->end - ccr->start + 1); ++ if (!tmio->ccr) { ++ retval = -EIO; ++ goto err_iomap_ccr; ++ } ++ ++ tmio->fcr = ioremap (fcr->start, fcr->end - fcr->start + 1); ++ if (!tmio->fcr) { ++ retval = -EIO; ++ goto err_iomap_fcr; ++ } ++ ++ tmio_hw_init (dev, tmio); ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = tmio->fcr; ++ this->IO_ADDR_W = tmio->fcr; ++ ++ /* Set address of hardware control function */ ++ this->cmd_ctrl = tmio_nand_hwcontrol; ++ this->dev_ready = tmio_nand_dev_ready; ++ this->read_byte = tmio_nand_read_byte; ++ this->write_buf = tmio_nand_write_buf; ++ this->read_buf = tmio_nand_read_buf; ++ this->verify_buf = tmio_nand_verify_buf; ++ ++ /* set eccmode using hardware ECC */ ++ this->ecc.mode = NAND_ECC_HW; ++ this->ecc.size = 512; ++ this->ecc.bytes = 6; ++ this->ecc.hwctl = tmio_nand_enable_hwecc; ++ this->ecc.calculate = tmio_nand_calculate_ecc; ++ this->ecc.correct = nand_correct_data; ++ this->badblock_pattern = tnpd->badblock_pattern; ++ ++ /* 15 us command delay time */ ++ this->chip_delay = 15; ++ ++ if (irq->start) { ++ retval = request_irq (irq->start, &tmio_irq, ++ IRQF_DISABLED, irq->name, tmio); ++ if (!retval) { ++ tmio->irq = irq->start; ++ this->waitfunc = tmio_nand_wait; ++ } else ++ mtd_warn (mtd, "request_irq error %d\n", retval); ++ } ++ ++ /* Scan to find existence of the device */ ++ if (nand_scan (mtd, 1)) { ++ retval = -ENODEV; ++ goto err_scan; ++ } ++ ++ /* Register the partitions */ ++#ifdef CONFIG_MTD_PARTITIONS ++ nbparts = parse_mtd_partitions (mtd, part_probes, &parts, 0); ++#endif ++ if (nbparts <= 0) { ++ parts = tnpd->partition; ++ nbparts = tnpd->num_partitions; ++ } ++ ++ add_mtd_partitions (mtd, parts, nbparts); ++ return 0; ++ ++err_scan: ++ if (tmio->irq) ++ free_irq (tmio->irq, tmio); ++ tmio_hw_stop (dev, tmio); ++ iounmap (tmio->fcr); ++err_iomap_fcr: ++ iounmap (tmio->ccr); ++err_iomap_ccr: ++ kfree (tmio); ++err_kzalloc: ++ release_resource (fcr); ++err_request_fcr: ++ release_resource (ccr); ++err_request_ccr: ++ return retval; ++} ++ ++static int tmio_remove (struct device *dev) ++{ ++ struct tmio_nand* tmio = dev_get_drvdata (dev); ++ ++ nand_release (&tmio->mtd); ++ if (tmio->irq) ++ free_irq (tmio->irq, tmio); ++ tmio_hw_stop (dev, tmio); ++ iounmap (tmio->fcr); ++ iounmap (tmio->ccr); ++ kfree (tmio); ++ release_resource (tmio_resource_control (dev)); ++ release_resource (tmio_resource_config (dev)); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int tmio_suspend (struct device *dev, pm_message_t state) ++{ ++ tmio_hw_stop (dev, dev_get_drvdata (dev)); ++ return 0; ++} ++ ++static int tmio_resume (struct device *dev) ++{ ++ tmio_hw_init (dev, dev_get_drvdata (dev)); ++ return 0; ++} ++#endif ++ ++static struct device_driver tmio_driver = { ++ .name = TMIO_NAME_NAND, ++ .bus = &tmio_bus_type, ++ .probe = tmio_probe, ++ .remove = tmio_remove, ++#ifdef CONFIG_PM ++ .suspend = tmio_suspend, ++ .resume = tmio_resume, ++#endif ++}; ++ ++static int __init tmio_init (void) { ++ return driver_register (&tmio_driver); ++} ++ ++static void __exit tmio_exit (void) { ++ driver_unregister (&tmio_driver); ++} ++ ++module_init (tmio_init); ++module_exit (tmio_exit); ++ ++MODULE_LICENSE ("GPL"); ++MODULE_AUTHOR ("Dirk Opfer, Chris Humbert"); ++MODULE_DESCRIPTION ("NAND flash driver on Toshiba Mobile IO controller"); diff --git a/packages/linux/linux-rp-2.6.23/tmio-tc6393-r8.patch b/packages/linux/linux-rp-2.6.23/tmio-tc6393-r8.patch new file mode 100644 index 0000000000..500fa837ec --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/tmio-tc6393-r8.patch @@ -0,0 +1,1600 @@ + arch/arm/common/Kconfig | 3 + arch/arm/common/Makefile | 1 + arch/arm/common/tc6393xb.c | 668 ++++++++++++++++++++++++++++++++++++++++ + arch/arm/mach-pxa/Kconfig | 1 + include/asm-arm/arch-pxa/irqs.h | 10 + include/asm-arm/hardware/tmio.h | 44 ++ + 6 files changed, 727 insertions(+) + +Index: git/arch/arm/common/tc6393xb.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ git/arch/arm/common/tc6393xb.c 2006-11-07 22:14:49.000000000 +0000 +@@ -0,0 +1,668 @@ ++/* ++ * Toshiba TC6393XB SoC support ++ * ++ * Maintainer: Chris Humbert <mahadri-kernel@drigon.com> ++ * ++ * Copyright (c) 2005-2006 Chris Humbert ++ * Copyright (c) 2005 Dirk Opfer ++ * ++ * Based on code written by Sharp/Lineo for 2.4 kernels ++ * Based on locomo.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/errno.h> ++#include <linux/ioport.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <linux/fb.h> ++ ++#include <asm/hardware.h> ++#include <asm/mach-types.h> ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <asm/mach/irq.h> ++#include <asm/arch/irqs.h> ++#include <asm/hardware/tmio.h> ++ ++#ifndef TMIO_SOC_TC6393XB ++#error "TC6393XB SoC not configured" ++#endif ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* cell ids must be 0-based because they are used as array indexes. */ ++#define TC6393_CELL_NAND 0 ++#define TC6393_CELL_SD 1 ++#define TC6393_CELL_OHCI 2 ++#define TC6393_CELL_SERIAL 3 ++#define TC6393_CELL_LCD 4 ++#define TC6393_NUM_CELLS 5 ++ ++#define TC6393_RESOURCE(_name, _start, _end, _flags) \ ++ { \ ++ .name = _name, \ ++ .start = _start, \ ++ .end = _end, \ ++ .flags = _flags, \ ++ } ++ ++#define TC6393_MEM(name, start, size) \ ++ TC6393_RESOURCE(name, start, (start) + (size) - 1, IORESOURCE_MEM) ++ ++#define TC6393_IRQ(name, irq) \ ++ TC6393_RESOURCE(name, irq, irq, IORESOURCE_IRQ) ++ ++const static struct resource tc6393_NAND_resource[] = { ++ TC6393_MEM (TMIO_NAME_NAND, 0x000100, 0x100), ++ TC6393_MEM (TMIO_NAME_NAND, 0x001000, 0x008), ++ TC6393_MEM (TMIO_NAME_NAND, 0, 0), ++ TC6393_IRQ (TMIO_NAME_NAND, IRQ_TC6393_NAND), ++}; ++ ++const static struct resource tc6393_SD_resource[] = { ++ TC6393_MEM (TMIO_NAME_SD, 0x000200, 0x100), ++ TC6393_MEM (TMIO_NAME_SD, 0x002000, 0x200), ++ TC6393_MEM (TMIO_NAME_SD, 0, 0), ++ TC6393_IRQ (TMIO_NAME_SD, IRQ_TC6393_SD), ++}; ++ ++const static struct resource tc6393_OHCI_resource[] = { ++ TC6393_MEM (TMIO_NAME_OHCI, 0x000300, 0x100), ++ TC6393_MEM (TMIO_NAME_OHCI, 0x003000, 0x100), ++ TC6393_MEM (TMIO_NAME_OHCI, 0x010000, 32 * 1024), ++ TC6393_IRQ (TMIO_NAME_OHCI, IRQ_TC6393_OHCI), ++}; ++ ++const static struct resource tc6393_SERIAL_resource[] = { ++ TC6393_MEM (TMIO_NAME_SERIAL, 0x000400, 0x100), ++ TC6393_MEM (TMIO_NAME_SERIAL, 0x004000, 0x100), ++ TC6393_MEM (TMIO_NAME_SERIAL, 0, 0), ++ TC6393_IRQ (TMIO_NAME_SERIAL, IRQ_TC6393_SERIAL), ++}; ++ ++const static struct resource tc6393_LCD_resource[] = { ++ TC6393_MEM (TMIO_NAME_LCD, 0x000500, 0x100), ++ TC6393_MEM (TMIO_NAME_LCD, 0x005000, 0x200), ++ TC6393_MEM (TMIO_NAME_LCD, 0x100000, 1024 * 1024), ++ TC6393_IRQ (TMIO_NAME_LCD, IRQ_TC6393_LCD), ++}; ++ ++#define TC6393_CELL(_NAME) \ ++ [TC6393_CELL_##_NAME] = { \ ++ .name = TMIO_NAME_##_NAME, \ ++ .id = TC6393_CELL_##_NAME, \ ++ .resource = tc6393_##_NAME##_resource, \ ++ .num_resources = ARRAY_SIZE (tc6393_##_NAME##_resource), \ ++ } ++ ++struct tc6393_cell { ++ const char* name; ++ unsigned int id; ++ const struct resource* resource; ++ unsigned int num_resources; ++}; ++ ++const static struct tc6393_cell tc6393_cell [TC6393_NUM_CELLS] = { ++ TC6393_CELL (NAND ), ++ TC6393_CELL (SD ), ++ TC6393_CELL (OHCI ), ++ TC6393_CELL (SERIAL ), ++ TC6393_CELL (LCD ), ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* ++ * TC6393 System Configuration Register ++ */ ++struct tc6393_scr { ++ u8 x00[8]; ++ u8 revid; /* 0x08 Revision ID */ ++ u8 x01[0x47]; ++ u8 isr; /* 0x50 Interrupt Status */ ++ u8 x02; ++ u8 imr; /* 0x52 Interrupt Mask */ ++ u8 x03; ++ u8 irr; /* 0x54 Interrupt Routing */ ++ u8 x04[0x0b]; ++ u16 gper; /* 0x60 GP Enable */ ++ u8 x05[2]; ++ u16 gpi_sr[2]; /* 0x64 GPI Status */ ++ u16 gpi_imr[2]; /* 0x68 GPI INT Mask */ ++ u16 gpi_eder[2]; /* 0x6c GPI Edge Detect Enable */ ++ u16 gpi_lir[4]; /* 0x70 GPI Level Invert */ ++ u16 gpo_dsr[2]; /* 0x78 GPO Data Set */ ++ u16 gpo_doecr[2]; /* 0x7c GPO Data OE Control */ ++ u16 gp_iarcr[2]; /* 0x80 GP Internal Active Reg Control */ ++ u16 gp_iarlcr[2]; /* 0x84 GP Internal Active Reg Level Con*/ ++ u8 gpi_bcr[4]; /* 0x88 GPI Buffer Control */ ++ u16 gpa_iarcr; /* 0x8c GPa Internal Active Reg Control */ ++ u8 x06[2]; ++ u16 gpa_iarlcr; /* 0x90 GPa Internal Active Reg Level Co*/ ++ u8 x07[2]; ++ u16 gpa_bcr; /* 0x94 GPa Buffer Control */ ++ u8 x08[2]; ++ u16 ccr; /* 0x98 Clock Control */ ++ u16 pll2cr; /* 0x9a PLL2 Control */ ++ u16 pll1cr[2]; /* 0x9c PLL1 Control */ ++ u8 diarcr; /* 0xa0 Device Internal Active Reg Contr*/ ++ u8 dbocr; /* 0xa1 Device Buffer Off Control */ ++ u8 x09[0x3e]; ++ u8 fer; /* 0xe0 Function Enable */ ++ u8 x10[3]; ++ u16 mcr; /* 0xe4 Mode Control */ ++ u8 x11[0x14]; ++ u8 config; /* 0xfc Configuration Control */ ++ u8 x12[2]; ++ u8 debug; /* 0xff Debug */ ++} __attribute__ ((packed)); ++ ++union tc6393_scr_fer { ++ u8 raw; ++struct { ++ unsigned usben:1; /* D0 USB enable */ ++ unsigned lcdcven:1; /* D1 polysylicon TFT enable */ ++ unsigned slcden:1; /* D2 SLCD enable */ ++} __attribute__ ((packed)); ++} __attribute__ ((packed)); ++ ++union tc6393_scr_ccr { ++ u16 raw; ++struct { ++ unsigned ck32ken:1; /* D0 SD host clock enable */ ++ unsigned usbcken:1; /* D1 USB host clock enable */ ++ unsigned x00:2; ++ unsigned sharp:1; /* D4 ??? set in Sharp's code */ ++ unsigned x01:3; ++ enum { disable = 0, ++ m12MHz = 1, ++ m24MHz = 2, ++ m48MHz = 3, ++ } mclksel:3; /* D10-D8 LCD controller clock */ ++ unsigned x02:1; ++ enum { h24MHz = 0, ++ h48MHz = 1, ++ } hclksel:2; /* D13-D12 host bus clock */ ++ unsigned x03:2; ++} __attribute__ ((packed)); ++} __attribute__ ((packed)); ++ ++/*--------------------------------------------------------------------------*/ ++ ++struct tc6393 { ++ spinlock_t lock; /* read-modify-write lock */ ++ struct device* dev; /* TC6393 device */ ++ struct tc6393_scr __iomem *scr; /* system configuration reg */ ++ ++ struct resource rscr; /* system config reg resource */ ++ struct resource* iomem; /* entire TC6393 iomem resource */ ++ unsigned int irq; /* hardware cascade irq */ ++ ++ struct tmio_device tdev [TC6393_NUM_CELLS]; ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++static u32 tc6393_ioread32 (const void __iomem *addr) ++{ ++ return ((u32) ioread16 (addr)) | (((u32) ioread16 (addr + 2)) << 16); ++} ++ ++static u32 tc6393_iowrite32 (u32 val, const void __iomem *addr) ++{ ++ iowrite16 (val, addr); ++ iowrite16 (val >> 16, addr + 2); ++ return val; ++} ++ ++u32 get_tc6393_gpio (struct device *dev) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ ++ return tc6393_ioread32 (scr->gpo_dsr); ++} ++EXPORT_SYMBOL (get_tc6393_gpio); ++ ++u32 set_tc6393_gpio (struct device *dev, u32 bits) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned long flags; ++ u32 dsr; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ dsr = tc6393_ioread32 (scr->gpo_dsr) | bits; ++ tc6393_iowrite32 (dsr, scr->gpo_dsr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++ ++ return dsr; ++} ++EXPORT_SYMBOL (set_tc6393_gpio); ++ ++u32 reset_tc6393_gpio (struct device *dev, u32 bits) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned long flags; ++ u32 dsr; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ dsr = tc6393_ioread32 (scr->gpo_dsr) & ~bits; ++ tc6393_iowrite32 (dsr, scr->gpo_dsr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++ ++ return dsr; ++} ++EXPORT_SYMBOL (reset_tc6393_gpio); ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void ++tc6393_irq (unsigned int irq, struct irq_desc *desc) ++{ ++ struct tc6393* tc6393 = get_irq_chip_data (irq); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned int isr; ++ unsigned int bit; ++ unsigned int i; ++ ++ desc->chip->ack (irq); ++ ++ while ((isr = ioread8(&scr->isr) & ~ioread8(&scr->imr))) ++ for (bit = 1, i = IRQ_TC6393_START; i <= IRQ_TC6393_LCD; ++ bit <<= 1, i++) ++ if (isr & bit) ++ desc_handle_irq (i, irq_desc + i); ++} ++ ++static void tc6393_irq_ack (unsigned int irq) ++{ ++} ++ ++static void tc6393_irq_mask (unsigned int irq) ++{ ++ struct tc6393* tc6393 = get_irq_chip_data (irq); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned long flags; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ iowrite8 (ioread8 (&scr->imr) | (1 << (irq - IRQ_TC6393_START)), ++ &scr->imr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++} ++ ++static void tc6393_irq_unmask (unsigned int irq) ++{ ++ struct tc6393* tc6393 = get_irq_chip_data (irq); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned long flags; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ iowrite8 (ioread8 (&scr->imr) & ~(1 << (irq - IRQ_TC6393_START)), ++ &scr->imr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++} ++ ++static struct irq_chip tc6393_chip = { ++ .ack = tc6393_irq_ack, ++ .mask = tc6393_irq_mask, ++ .unmask = tc6393_irq_unmask, ++}; ++ ++static void tc6393_attach_irq (struct tc6393 *tc6393) ++{ ++ unsigned int irq; ++ ++ for (irq = IRQ_TC6393_START; irq <= IRQ_TC6393_LCD; irq++) { ++ set_irq_chip (irq, &tc6393_chip); ++ set_irq_chip_data(irq, tc6393); ++ set_irq_handler (irq, handle_edge_irq); ++ set_irq_flags (irq, IRQF_VALID | IRQF_PROBE); ++ } ++ ++ set_irq_type (tc6393->irq, IRQT_FALLING); ++ set_irq_chip_data (tc6393->irq, tc6393); ++ set_irq_chained_handler (tc6393->irq, tc6393_irq); ++} ++ ++static void tc6393_detach_irq (struct tc6393 *tc6393) ++{ ++ unsigned int irq; ++ ++ set_irq_chained_handler (tc6393->irq, NULL); ++ set_irq_chip_data (tc6393->irq, NULL); ++ ++ for (irq = IRQ_TC6393_START; irq <= IRQ_TC6393_LCD; irq++) { ++ set_irq_flags (irq, 0); ++ set_irq_chip (irq, NULL); ++ set_irq_chip_data(irq, NULL); ++ } ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++static int tc6393_bus_match (struct device *dev, struct device_driver *drv) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ const struct tc6393_cell* cell = tdev->soc_data; ++ ++ return !strcmp (cell->name, drv->name); ++} ++ ++static int tc6393_bus_suspend (struct device *dev, pm_message_t state) ++{ ++ struct device_driver* drv = dev->driver; ++ return drv && drv->suspend ? drv->suspend (dev, state) : 0; ++} ++ ++static int tc6393_bus_resume (struct device *dev) ++{ ++ struct device_driver* drv = dev->driver; ++ return drv && drv->resume ? drv->resume (dev) : 0; ++} ++ ++struct bus_type tc6393_bus_type = { ++ .name = TMIO_NAME_BUS, ++ .match = tc6393_bus_match, ++ .suspend = tc6393_bus_suspend, ++ .resume = tc6393_bus_resume, ++}; ++EXPORT_SYMBOL (tc6393_bus_type); ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void tc6393_cell_clock (struct device *dev, int enable) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ const struct tc6393_cell* cell = tdev->soc_data; ++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ union tc6393_scr_ccr ccr; ++ unsigned long flags; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ ccr.raw = ioread16 (&scr->ccr); ++ ++ switch (cell->id) { ++ case TC6393_CELL_SD: ccr.ck32ken = enable; break; ++ case TC6393_CELL_OHCI: ccr.usbcken = enable; break; ++ case TC6393_CELL_LCD: ++ ccr.mclksel = enable ? m48MHz : disable; ++ break; ++ } ++ ++ printk (KERN_DEBUG TMIO_NAME_CORE ": scr->ccr = %04x\n", ccr.raw); ++ ++ iowrite16(ccr.raw, &scr->ccr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++} ++ ++static void tc6393_cell_function (struct device *dev, int enable) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ const struct tc6393_cell* cell = tdev->soc_data; ++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ union tc6393_scr_fer fer; ++ unsigned long flags; ++ ++ if (cell->id == TC6393_CELL_NAND) { ++ if (enable) { ++ /* SMD buffer on */ ++ printk (KERN_DEBUG TMIO_NAME_CORE ": SMD buffer on\n"); ++ iowrite8 (0xff, scr->gpi_bcr + 1); ++ } ++ return; ++ } ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ fer.raw = ioread16 (&scr->fer); ++ ++ switch (cell->id) { ++ case TC6393_CELL_OHCI: fer.usben = enable; break; ++ case TC6393_CELL_LCD: fer.slcden = enable; break; ++ } ++ ++ printk (KERN_DEBUG TMIO_NAME_CORE ": scr->fer = %02x\n", fer.raw); ++ ++ iowrite8 (fer.raw, &scr->fer); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++} ++ ++static void ++tc6393_lcd_mode (struct device *dev, const struct fb_videomode *mode) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ ++ iowrite16 (mode->pixclock, scr->pll1cr + 0); ++ iowrite16 (mode->pixclock >> 16, scr->pll1cr + 1); ++} ++ ++static struct tmio_cell_ops tc6393_cell_ops = { ++ .clock = tc6393_cell_clock, ++ .function = tc6393_cell_function, ++ .lcd_mode = tc6393_lcd_mode, ++}; ++ ++static void tc6393_device_release (struct device *dev) ++{ ++} ++ ++static int ++tc6393_device_register (struct tc6393 *tc6393, struct tmio_cell *tcell) ++{ ++ const struct tc6393_cell* cell; ++ struct tmio_device* tdev; ++ struct device* dev; ++ int i; ++ ++ for (i = 0; strcmp (tcell->name, tc6393_cell [i].name); ) ++ if (++i >= ARRAY_SIZE(tc6393_cell)) ++ return -EINVAL; ++ ++ cell = tc6393_cell + i; ++ tdev = tc6393->tdev + i; ++ dev = &tdev->dev; ++ ++ tdev->ops = &tc6393_cell_ops; ++ tdev->iomem = tc6393->iomem; ++ tdev->soc_data = (void*) cell; ++ ++ dev->parent = tc6393->dev; ++ strncpy (dev->bus_id, cell->name, sizeof dev->bus_id); ++ dev->bus = &tc6393_bus_type; ++ dev->dma_mask = tc6393->dev->dma_mask; ++ dev->coherent_dma_mask = tc6393->dev->coherent_dma_mask; ++ dev->release = tc6393_device_release; ++ dev->platform_data = tcell->platform_data; ++ ++ for (i=0; i < cell->num_resources; i++) { ++ const struct resource* cr = cell->resource + i; ++ struct resource* dr = tdev->resource + i; ++ ++ dr->name = cr->name; ++ dr->start = cr->start; ++ dr->end = cr->end; ++ dr->flags = cr->flags; ++ ++ /* convert memory offsets to absolutes */ ++ if (cr->flags & IORESOURCE_MEM) { ++ dr->start += tc6393->iomem->start; ++ dr->end += tc6393->iomem->start; ++ } ++ } ++ ++ return device_register (dev); ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void tc6393_hw_init (struct tc6393 *tc6393) ++{ ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ struct tc6393_platform_data* tcpd = tc6393->dev->platform_data; ++ ++ tcpd->enable (tc6393->dev); ++ ++ iowrite8 (0, &scr->fer); ++ iowrite16(tcpd->scr_pll2cr, &scr->pll2cr); ++ iowrite16(tcpd->scr_ccr, &scr->ccr); ++ iowrite16(tcpd->scr_mcr, &scr->mcr); ++ iowrite16(tcpd->scr_gper, &scr->gper); ++ iowrite8 (0, &scr->irr); ++ iowrite8 (0xbf, &scr->imr); ++ iowrite16(tcpd->scr_gpo_dsr, scr->gpo_dsr + 0); ++ iowrite16(tcpd->scr_gpo_dsr >> 16, scr->gpo_dsr + 1); ++ iowrite16(tcpd->scr_gpo_doecr, scr->gpo_doecr + 0); ++ iowrite16(tcpd->scr_gpo_doecr >> 16, scr->gpo_doecr + 1); ++} ++ ++static int tc6393_probe (struct device *dev) ++{ ++ struct platform_device* pdev = to_platform_device (dev); ++ struct tc6393_platform_data* tcpd = dev->platform_data; ++ struct tc6393* tc6393; ++ struct resource* iomem; ++ struct resource* rscr; ++ int retval; ++ int i; ++ ++ iomem = platform_get_resource (pdev, IORESOURCE_MEM, 0); ++ if (!iomem) ++ return -EINVAL; ++ ++ tc6393 = kzalloc (sizeof *tc6393, GFP_KERNEL); ++ if (!tc6393) { ++ retval = -ENOMEM; ++ goto err_kzalloc; ++ } ++ ++ dev_set_drvdata (dev, tc6393); ++ spin_lock_init (&tc6393->lock); ++ tc6393->dev = dev; ++ tc6393->iomem = iomem; ++ tc6393->irq = platform_get_irq (pdev, 0); ++ ++ rscr = &tc6393->rscr; ++ rscr->name = TMIO_NAME_CORE; ++ rscr->start = iomem->start; ++ rscr->end = iomem->start + 0xff; ++ rscr->flags = IORESOURCE_MEM; ++ ++ retval = request_resource (iomem, rscr); ++ if (retval) ++ goto err_request_scr; ++ ++ tc6393->scr = ioremap (rscr->start, rscr->end - rscr->start + 1); ++ if (!tc6393->scr) { ++ retval = -ENOMEM; ++ goto err_ioremap; ++ } ++ ++ tc6393_hw_init (tc6393); ++ ++ printk (KERN_INFO "Toshiba %s revision %d at 0x%08lx, irq %d\n", ++ TMIO_SOC_NAME, ioread8 (&tc6393->scr->revid), ++ iomem->start, tc6393->irq); ++ ++ if (tc6393->irq) ++ tc6393_attach_irq (tc6393); ++ ++ for (i = 0; i < tcpd->num_cells; i++) ++ tc6393_device_register (tc6393, tcpd->cell + i); ++ ++ return 0; ++ ++err_ioremap: ++ release_resource (rscr); ++err_request_scr: ++ kfree(tc6393); ++err_kzalloc: ++ release_resource (iomem); ++ return retval; ++} ++ ++static int tc6393_dev_remove (struct device *dev, void *data) ++{ ++ device_unregister (dev); ++ return 0; ++} ++ ++static int tc6393_remove (struct device *dev) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ ++ device_for_each_child (dev, tc6393, tc6393_dev_remove); ++ ++ if (tc6393->irq) ++ tc6393_detach_irq (tc6393); ++ ++ iounmap (tc6393->scr); ++ release_resource (&tc6393->rscr); ++ release_resource (tc6393->iomem); ++ kfree (tc6393); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int tc6393_suspend (struct device *dev, pm_message_t state) ++{ ++ struct tc6393_platform_data* tcpd = dev->platform_data; ++ tcpd->disable (dev); ++ return 0; ++} ++ ++static int tc6393_resume (struct device *dev) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ tc6393_hw_init (tc6393); ++ return 0; ++} ++#endif ++ ++static struct device_driver tc6393_device_driver = { ++ .name = TMIO_SOC_NAME, ++ .bus = &platform_bus_type, ++ .probe = tc6393_probe, ++ .remove = tc6393_remove, ++#ifdef CONFIG_PM ++ .suspend = tc6393_suspend, ++ .resume = tc6393_resume, ++#endif ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++static int __init tc6393_init (void) ++{ ++ int retval = bus_register (&tc6393_bus_type); ++ if (retval) ++ return retval; ++ ++ return driver_register (&tc6393_device_driver); ++} ++ ++static void __exit tc6393_exit (void) ++{ ++ driver_unregister (&tc6393_device_driver); ++ bus_unregister (&tc6393_bus_type); ++} ++ ++module_init (tc6393_init); ++module_exit (tc6393_exit); ++ ++MODULE_DESCRIPTION ("TC6393 SoC bus driver"); ++MODULE_AUTHOR ("Chris Humbert, Dirk Opfer"); ++MODULE_LICENSE ("GPL"); +Index: git/arch/arm/common/Kconfig +=================================================================== +--- git.orig/arch/arm/common/Kconfig 2006-10-31 16:08:28.000000000 +0000 ++++ git/arch/arm/common/Kconfig 2006-11-07 22:13:09.000000000 +0000 +@@ -31,3 +31,6 @@ config SHARPSL_PM + + config SHARP_SCOOP + bool ++ ++config TOSHIBA_TC6393XB ++ bool +Index: git/arch/arm/mach-pxa/Kconfig +=================================================================== +--- git.orig/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:06.000000000 +0000 ++++ git/arch/arm/mach-pxa/ 2006-11-07 23:30:34.000000000 +0000 +@@ -128,6 +128,7 @@ config MACH_BORZOI + config MACH_TOSA + bool "Enable Sharp SL-6000x (Tosa) Support" + depends on PXA_SHARPSL_25x ++ select TOSHIBA_TC6393XB + + config PXA25x + bool +Index: git/arch/arm/common/Makefile +=================================================================== +--- git.orig/arch/arm/common/Makefile 2006-10-31 16:08:28.000000000 +0000 ++++ git/arch/arm/common/Makefile 2006-11-07 22:13:09.000000000 +0000 +@@ -17,3 +17,4 @@ obj-$(CONFIG_SHARPSL_PM) += sharpsl_pm.o + obj-$(CONFIG_SHARP_SCOOP) += scoop.o + obj-$(CONFIG_ARCH_IXP2000) += uengine.o + obj-$(CONFIG_ARCH_IXP23XX) += uengine.o ++obj-$(CONFIG_TOSHIBA_TC6393XB) += tc6393xb.o +Index: git/include/asm-arm/hardware/tmio.h +=================================================================== +--- git.orig/include/asm-arm/hardware/tmio.h 2006-11-07 22:13:09.000000000 +0000 ++++ git/include/asm-arm/hardware/tmio.h 2006-11-07 22:13:09.000000000 +0000 +@@ -91,6 +91,50 @@ struct tmio_device { + + /*--------------------------------------------------------------------------*/ + ++/* ++ * TC6393XB SoC ++ */ ++#ifdef CONFIG_TOSHIBA_TC6393XB ++#define TMIO_SOC_TC6393XB ++#define TMIO_SOC_NAME "TC6393XB" ++#define TMIO_NAME_BUS "tc6393-bus" ++#define TMIO_NAME_CORE "tc6393-core" ++#define TMIO_NAME_NAND "tc6393-nand" ++#define TMIO_NAME_SD "tc6393-sd" ++#define TMIO_NAME_OHCI "tc6393-ohci" ++#define TMIO_NAME_SERIAL "tc6393-serial" ++#define TMIO_NAME_LCD "tc6393-lcd" ++#define tmio_bus_type tc6393_bus_type ++ ++#define TC6393_GPIO(x) (1 << (x)) ++ ++extern struct bus_type tc6393_bus_type; ++ ++struct tc6393_platform_data { ++ u16 scr_pll2cr; /* PLL2 Control */ ++ u16 scr_ccr; /* Clock Control */ ++ u16 scr_mcr; /* Mode Control */ ++ u16 scr_gper; /* GP Enable */ ++ u32 scr_gpo_doecr; /* GPO Data OE Control */ ++ u32 scr_gpo_dsr; /* GPO Data Set */ ++ ++ /* cells to register as devices */ ++ struct tmio_cell* cell; ++ unsigned int num_cells; ++ ++ /* callbacks to enable and disable the TC6393XB's power and clock */ ++ void (*enable) (struct device *dev); ++ void (*disable) (struct device *dev); ++}; ++ ++u32 get_tc6393_gpio (struct device *dev); ++u32 set_tc6393_gpio (struct device *dev, u32 bits); ++u32 reset_tc6393_gpio (struct device *dev, u32 bits); ++ ++/*--------------------------------------------------------------------------*/ ++ ++#else + #error "no TMIO SoC configured" ++#endif + + #endif +Index: git/include/asm-arm/arch-pxa/irqs.h +=================================================================== +--- git.orig/include/asm-arm/arch-pxa/irqs.h 2006-10-31 16:09:33.000000000 +0000 ++++ git/include/asm-arm/arch-pxa/irqs.h 2006-11-07 22:13:09.000000000 +0000 +@@ -163,17 +163,27 @@ + #define IRQ_LOCOMO_SPI_OVRN (IRQ_BOARD_END + 20) + #define IRQ_LOCOMO_SPI_TEND (IRQ_BOARD_END + 21) + ++#define IRQ_TC6393_START (IRQ_BOARD_END) ++#define IRQ_TC6393_NAND (IRQ_BOARD_END + 0) ++#define IRQ_TC6393_SD (IRQ_BOARD_END + 1) ++#define IRQ_TC6393_OHCI (IRQ_BOARD_END + 2) ++#define IRQ_TC6393_SERIAL (IRQ_BOARD_END + 3) ++#define IRQ_TC6393_LCD (IRQ_BOARD_END + 4) ++ + /* + * Figure out the MAX IRQ number. + * + * If we have an SA1111, the max IRQ is S1_BVD1_STSCHG+1. + * If we have an LoCoMo, the max IRQ is IRQ_LOCOMO_SPI_TEND+1 ++ * If we have an TC6393XB, the max IRQ is IRQ_TC6393_LCD+1 + * Otherwise, we have the standard IRQs only. + */ + #ifdef CONFIG_SA1111 + #define NR_IRQS (IRQ_S1_BVD1_STSCHG + 1) + #elif defined(CONFIG_SHARP_LOCOMO) + #define NR_IRQS (IRQ_LOCOMO_SPI_TEND + 1) ++#elif defined(CONFIG_TOSHIBA_TC6393XB) ++#define NR_IRQS (IRQ_TC6393_LCD + 1) + #elif defined(CONFIG_ARCH_LUBBOCK) || \ + defined(CONFIG_MACH_LOGICPD_PXA270) || \ + defined(CONFIG_MACH_MAINSTONE) + arch/arm/common/Kconfig | 3 + arch/arm/common/Makefile | 1 + arch/arm/common/tc6393xb.c | 668 ++++++++++++++++++++++++++++++++++++++++ + arch/arm/mach-pxa/Kconfig | 1 + include/asm-arm/arch-pxa/irqs.h | 10 + include/asm-arm/hardware/tmio.h | 44 ++ + 6 files changed, 727 insertions(+) + +Index: git/arch/arm/common/tc6393xb.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ git/arch/arm/common/tc6393xb.c 2006-11-07 22:14:49.000000000 +0000 +@@ -0,0 +1,668 @@ ++/* ++ * Toshiba TC6393XB SoC support ++ * ++ * Maintainer: Chris Humbert <mahadri-kernel@drigon.com> ++ * ++ * Copyright (c) 2005-2006 Chris Humbert ++ * Copyright (c) 2005 Dirk Opfer ++ * ++ * Based on code written by Sharp/Lineo for 2.4 kernels ++ * Based on locomo.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/errno.h> ++#include <linux/ioport.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <linux/fb.h> ++ ++#include <asm/hardware.h> ++#include <asm/mach-types.h> ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <asm/mach/irq.h> ++#include <asm/arch/irqs.h> ++#include <asm/hardware/tmio.h> ++ ++#ifndef TMIO_SOC_TC6393XB ++#error "TC6393XB SoC not configured" ++#endif ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* cell ids must be 0-based because they are used as array indexes. */ ++#define TC6393_CELL_NAND 0 ++#define TC6393_CELL_SD 1 ++#define TC6393_CELL_OHCI 2 ++#define TC6393_CELL_SERIAL 3 ++#define TC6393_CELL_LCD 4 ++#define TC6393_NUM_CELLS 5 ++ ++#define TC6393_RESOURCE(_name, _start, _end, _flags) \ ++ { \ ++ .name = _name, \ ++ .start = _start, \ ++ .end = _end, \ ++ .flags = _flags, \ ++ } ++ ++#define TC6393_MEM(name, start, size) \ ++ TC6393_RESOURCE(name, start, (start) + (size) - 1, IORESOURCE_MEM) ++ ++#define TC6393_IRQ(name, irq) \ ++ TC6393_RESOURCE(name, irq, irq, IORESOURCE_IRQ) ++ ++const static struct resource tc6393_NAND_resource[] = { ++ TC6393_MEM (TMIO_NAME_NAND, 0x000100, 0x100), ++ TC6393_MEM (TMIO_NAME_NAND, 0x001000, 0x008), ++ TC6393_MEM (TMIO_NAME_NAND, 0, 0), ++ TC6393_IRQ (TMIO_NAME_NAND, IRQ_TC6393_NAND), ++}; ++ ++const static struct resource tc6393_SD_resource[] = { ++ TC6393_MEM (TMIO_NAME_SD, 0x000200, 0x100), ++ TC6393_MEM (TMIO_NAME_SD, 0x002000, 0x200), ++ TC6393_MEM (TMIO_NAME_SD, 0, 0), ++ TC6393_IRQ (TMIO_NAME_SD, IRQ_TC6393_SD), ++}; ++ ++const static struct resource tc6393_OHCI_resource[] = { ++ TC6393_MEM (TMIO_NAME_OHCI, 0x000300, 0x100), ++ TC6393_MEM (TMIO_NAME_OHCI, 0x003000, 0x100), ++ TC6393_MEM (TMIO_NAME_OHCI, 0x010000, 32 * 1024), ++ TC6393_IRQ (TMIO_NAME_OHCI, IRQ_TC6393_OHCI), ++}; ++ ++const static struct resource tc6393_SERIAL_resource[] = { ++ TC6393_MEM (TMIO_NAME_SERIAL, 0x000400, 0x100), ++ TC6393_MEM (TMIO_NAME_SERIAL, 0x004000, 0x100), ++ TC6393_MEM (TMIO_NAME_SERIAL, 0, 0), ++ TC6393_IRQ (TMIO_NAME_SERIAL, IRQ_TC6393_SERIAL), ++}; ++ ++const static struct resource tc6393_LCD_resource[] = { ++ TC6393_MEM (TMIO_NAME_LCD, 0x000500, 0x100), ++ TC6393_MEM (TMIO_NAME_LCD, 0x005000, 0x200), ++ TC6393_MEM (TMIO_NAME_LCD, 0x100000, 1024 * 1024), ++ TC6393_IRQ (TMIO_NAME_LCD, IRQ_TC6393_LCD), ++}; ++ ++#define TC6393_CELL(_NAME) \ ++ [TC6393_CELL_##_NAME] = { \ ++ .name = TMIO_NAME_##_NAME, \ ++ .id = TC6393_CELL_##_NAME, \ ++ .resource = tc6393_##_NAME##_resource, \ ++ .num_resources = ARRAY_SIZE (tc6393_##_NAME##_resource), \ ++ } ++ ++struct tc6393_cell { ++ const char* name; ++ unsigned int id; ++ const struct resource* resource; ++ unsigned int num_resources; ++}; ++ ++const static struct tc6393_cell tc6393_cell [TC6393_NUM_CELLS] = { ++ TC6393_CELL (NAND ), ++ TC6393_CELL (SD ), ++ TC6393_CELL (OHCI ), ++ TC6393_CELL (SERIAL ), ++ TC6393_CELL (LCD ), ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* ++ * TC6393 System Configuration Register ++ */ ++struct tc6393_scr { ++ u8 x00[8]; ++ u8 revid; /* 0x08 Revision ID */ ++ u8 x01[0x47]; ++ u8 isr; /* 0x50 Interrupt Status */ ++ u8 x02; ++ u8 imr; /* 0x52 Interrupt Mask */ ++ u8 x03; ++ u8 irr; /* 0x54 Interrupt Routing */ ++ u8 x04[0x0b]; ++ u16 gper; /* 0x60 GP Enable */ ++ u8 x05[2]; ++ u16 gpi_sr[2]; /* 0x64 GPI Status */ ++ u16 gpi_imr[2]; /* 0x68 GPI INT Mask */ ++ u16 gpi_eder[2]; /* 0x6c GPI Edge Detect Enable */ ++ u16 gpi_lir[4]; /* 0x70 GPI Level Invert */ ++ u16 gpo_dsr[2]; /* 0x78 GPO Data Set */ ++ u16 gpo_doecr[2]; /* 0x7c GPO Data OE Control */ ++ u16 gp_iarcr[2]; /* 0x80 GP Internal Active Reg Control */ ++ u16 gp_iarlcr[2]; /* 0x84 GP Internal Active Reg Level Con*/ ++ u8 gpi_bcr[4]; /* 0x88 GPI Buffer Control */ ++ u16 gpa_iarcr; /* 0x8c GPa Internal Active Reg Control */ ++ u8 x06[2]; ++ u16 gpa_iarlcr; /* 0x90 GPa Internal Active Reg Level Co*/ ++ u8 x07[2]; ++ u16 gpa_bcr; /* 0x94 GPa Buffer Control */ ++ u8 x08[2]; ++ u16 ccr; /* 0x98 Clock Control */ ++ u16 pll2cr; /* 0x9a PLL2 Control */ ++ u16 pll1cr[2]; /* 0x9c PLL1 Control */ ++ u8 diarcr; /* 0xa0 Device Internal Active Reg Contr*/ ++ u8 dbocr; /* 0xa1 Device Buffer Off Control */ ++ u8 x09[0x3e]; ++ u8 fer; /* 0xe0 Function Enable */ ++ u8 x10[3]; ++ u16 mcr; /* 0xe4 Mode Control */ ++ u8 x11[0x14]; ++ u8 config; /* 0xfc Configuration Control */ ++ u8 x12[2]; ++ u8 debug; /* 0xff Debug */ ++} __attribute__ ((packed)); ++ ++union tc6393_scr_fer { ++ u8 raw; ++struct { ++ unsigned usben:1; /* D0 USB enable */ ++ unsigned lcdcven:1; /* D1 polysylicon TFT enable */ ++ unsigned slcden:1; /* D2 SLCD enable */ ++} __attribute__ ((packed)); ++} __attribute__ ((packed)); ++ ++union tc6393_scr_ccr { ++ u16 raw; ++struct { ++ unsigned ck32ken:1; /* D0 SD host clock enable */ ++ unsigned usbcken:1; /* D1 USB host clock enable */ ++ unsigned x00:2; ++ unsigned sharp:1; /* D4 ??? set in Sharp's code */ ++ unsigned x01:3; ++ enum { disable = 0, ++ m12MHz = 1, ++ m24MHz = 2, ++ m48MHz = 3, ++ } mclksel:3; /* D10-D8 LCD controller clock */ ++ unsigned x02:1; ++ enum { h24MHz = 0, ++ h48MHz = 1, ++ } hclksel:2; /* D13-D12 host bus clock */ ++ unsigned x03:2; ++} __attribute__ ((packed)); ++} __attribute__ ((packed)); ++ ++/*--------------------------------------------------------------------------*/ ++ ++struct tc6393 { ++ spinlock_t lock; /* read-modify-write lock */ ++ struct device* dev; /* TC6393 device */ ++ struct tc6393_scr __iomem *scr; /* system configuration reg */ ++ ++ struct resource rscr; /* system config reg resource */ ++ struct resource* iomem; /* entire TC6393 iomem resource */ ++ unsigned int irq; /* hardware cascade irq */ ++ ++ struct tmio_device tdev [TC6393_NUM_CELLS]; ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++static u32 tc6393_ioread32 (const void __iomem *addr) ++{ ++ return ((u32) ioread16 (addr)) | (((u32) ioread16 (addr + 2)) << 16); ++} ++ ++static u32 tc6393_iowrite32 (u32 val, const void __iomem *addr) ++{ ++ iowrite16 (val, addr); ++ iowrite16 (val >> 16, addr + 2); ++ return val; ++} ++ ++u32 get_tc6393_gpio (struct device *dev) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ ++ return tc6393_ioread32 (scr->gpo_dsr); ++} ++EXPORT_SYMBOL (get_tc6393_gpio); ++ ++u32 set_tc6393_gpio (struct device *dev, u32 bits) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned long flags; ++ u32 dsr; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ dsr = tc6393_ioread32 (scr->gpo_dsr) | bits; ++ tc6393_iowrite32 (dsr, scr->gpo_dsr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++ ++ return dsr; ++} ++EXPORT_SYMBOL (set_tc6393_gpio); ++ ++u32 reset_tc6393_gpio (struct device *dev, u32 bits) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned long flags; ++ u32 dsr; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ dsr = tc6393_ioread32 (scr->gpo_dsr) & ~bits; ++ tc6393_iowrite32 (dsr, scr->gpo_dsr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++ ++ return dsr; ++} ++EXPORT_SYMBOL (reset_tc6393_gpio); ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void ++tc6393_irq (unsigned int irq, struct irq_desc *desc) ++{ ++ struct tc6393* tc6393 = get_irq_chip_data (irq); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned int isr; ++ unsigned int bit; ++ unsigned int i; ++ ++ desc->chip->ack (irq); ++ ++ while ((isr = ioread8(&scr->isr) & ~ioread8(&scr->imr))) ++ for (bit = 1, i = IRQ_TC6393_START; i <= IRQ_TC6393_LCD; ++ bit <<= 1, i++) ++ if (isr & bit) ++ desc_handle_irq (i, irq_desc + i); ++} ++ ++static void tc6393_irq_ack (unsigned int irq) ++{ ++} ++ ++static void tc6393_irq_mask (unsigned int irq) ++{ ++ struct tc6393* tc6393 = get_irq_chip_data (irq); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned long flags; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ iowrite8 (ioread8 (&scr->imr) | (1 << (irq - IRQ_TC6393_START)), ++ &scr->imr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++} ++ ++static void tc6393_irq_unmask (unsigned int irq) ++{ ++ struct tc6393* tc6393 = get_irq_chip_data (irq); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned long flags; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ iowrite8 (ioread8 (&scr->imr) & ~(1 << (irq - IRQ_TC6393_START)), ++ &scr->imr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++} ++ ++static struct irq_chip tc6393_chip = { ++ .ack = tc6393_irq_ack, ++ .mask = tc6393_irq_mask, ++ .unmask = tc6393_irq_unmask, ++}; ++ ++static void tc6393_attach_irq (struct tc6393 *tc6393) ++{ ++ unsigned int irq; ++ ++ for (irq = IRQ_TC6393_START; irq <= IRQ_TC6393_LCD; irq++) { ++ set_irq_chip (irq, &tc6393_chip); ++ set_irq_chip_data(irq, tc6393); ++ set_irq_handler (irq, handle_edge_irq); ++ set_irq_flags (irq, IRQF_VALID | IRQF_PROBE); ++ } ++ ++ set_irq_type (tc6393->irq, IRQT_FALLING); ++ set_irq_chip_data (tc6393->irq, tc6393); ++ set_irq_chained_handler (tc6393->irq, tc6393_irq); ++} ++ ++static void tc6393_detach_irq (struct tc6393 *tc6393) ++{ ++ unsigned int irq; ++ ++ set_irq_chained_handler (tc6393->irq, NULL); ++ set_irq_chip_data (tc6393->irq, NULL); ++ ++ for (irq = IRQ_TC6393_START; irq <= IRQ_TC6393_LCD; irq++) { ++ set_irq_flags (irq, 0); ++ set_irq_chip (irq, NULL); ++ set_irq_chip_data(irq, NULL); ++ } ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++static int tc6393_bus_match (struct device *dev, struct device_driver *drv) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ const struct tc6393_cell* cell = tdev->soc_data; ++ ++ return !strcmp (cell->name, drv->name); ++} ++ ++static int tc6393_bus_suspend (struct device *dev, pm_message_t state) ++{ ++ struct device_driver* drv = dev->driver; ++ return drv && drv->suspend ? drv->suspend (dev, state) : 0; ++} ++ ++static int tc6393_bus_resume (struct device *dev) ++{ ++ struct device_driver* drv = dev->driver; ++ return drv && drv->resume ? drv->resume (dev) : 0; ++} ++ ++struct bus_type tc6393_bus_type = { ++ .name = TMIO_NAME_BUS, ++ .match = tc6393_bus_match, ++ .suspend = tc6393_bus_suspend, ++ .resume = tc6393_bus_resume, ++}; ++EXPORT_SYMBOL (tc6393_bus_type); ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void tc6393_cell_clock (struct device *dev, int enable) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ const struct tc6393_cell* cell = tdev->soc_data; ++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ union tc6393_scr_ccr ccr; ++ unsigned long flags; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ ccr.raw = ioread16 (&scr->ccr); ++ ++ switch (cell->id) { ++ case TC6393_CELL_SD: ccr.ck32ken = enable; break; ++ case TC6393_CELL_OHCI: ccr.usbcken = enable; break; ++ case TC6393_CELL_LCD: ++ ccr.mclksel = enable ? m48MHz : disable; ++ break; ++ } ++ ++ printk (KERN_DEBUG TMIO_NAME_CORE ": scr->ccr = %04x\n", ccr.raw); ++ ++ iowrite16(ccr.raw, &scr->ccr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++} ++ ++static void tc6393_cell_function (struct device *dev, int enable) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ const struct tc6393_cell* cell = tdev->soc_data; ++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ union tc6393_scr_fer fer; ++ unsigned long flags; ++ ++ if (cell->id == TC6393_CELL_NAND) { ++ if (enable) { ++ /* SMD buffer on */ ++ printk (KERN_DEBUG TMIO_NAME_CORE ": SMD buffer on\n"); ++ iowrite8 (0xff, scr->gpi_bcr + 1); ++ } ++ return; ++ } ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ fer.raw = ioread16 (&scr->fer); ++ ++ switch (cell->id) { ++ case TC6393_CELL_OHCI: fer.usben = enable; break; ++ case TC6393_CELL_LCD: fer.slcden = enable; break; ++ } ++ ++ printk (KERN_DEBUG TMIO_NAME_CORE ": scr->fer = %02x\n", fer.raw); ++ ++ iowrite8 (fer.raw, &scr->fer); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++} ++ ++static void ++tc6393_lcd_mode (struct device *dev, const struct fb_videomode *mode) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ ++ iowrite16 (mode->pixclock, scr->pll1cr + 0); ++ iowrite16 (mode->pixclock >> 16, scr->pll1cr + 1); ++} ++ ++static struct tmio_cell_ops tc6393_cell_ops = { ++ .clock = tc6393_cell_clock, ++ .function = tc6393_cell_function, ++ .lcd_mode = tc6393_lcd_mode, ++}; ++ ++static void tc6393_device_release (struct device *dev) ++{ ++} ++ ++static int ++tc6393_device_register (struct tc6393 *tc6393, struct tmio_cell *tcell) ++{ ++ const struct tc6393_cell* cell; ++ struct tmio_device* tdev; ++ struct device* dev; ++ int i; ++ ++ for (i = 0; strcmp (tcell->name, tc6393_cell [i].name); ) ++ if (++i >= ARRAY_SIZE(tc6393_cell)) ++ return -EINVAL; ++ ++ cell = tc6393_cell + i; ++ tdev = tc6393->tdev + i; ++ dev = &tdev->dev; ++ ++ tdev->ops = &tc6393_cell_ops; ++ tdev->iomem = tc6393->iomem; ++ tdev->soc_data = (void*) cell; ++ ++ dev->parent = tc6393->dev; ++ strncpy (dev->bus_id, cell->name, sizeof dev->bus_id); ++ dev->bus = &tc6393_bus_type; ++ dev->dma_mask = tc6393->dev->dma_mask; ++ dev->coherent_dma_mask = tc6393->dev->coherent_dma_mask; ++ dev->release = tc6393_device_release; ++ dev->platform_data = tcell->platform_data; ++ ++ for (i=0; i < cell->num_resources; i++) { ++ const struct resource* cr = cell->resource + i; ++ struct resource* dr = tdev->resource + i; ++ ++ dr->name = cr->name; ++ dr->start = cr->start; ++ dr->end = cr->end; ++ dr->flags = cr->flags; ++ ++ /* convert memory offsets to absolutes */ ++ if (cr->flags & IORESOURCE_MEM) { ++ dr->start += tc6393->iomem->start; ++ dr->end += tc6393->iomem->start; ++ } ++ } ++ ++ return device_register (dev); ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void tc6393_hw_init (struct tc6393 *tc6393) ++{ ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ struct tc6393_platform_data* tcpd = tc6393->dev->platform_data; ++ ++ tcpd->enable (tc6393->dev); ++ ++ iowrite8 (0, &scr->fer); ++ iowrite16(tcpd->scr_pll2cr, &scr->pll2cr); ++ iowrite16(tcpd->scr_ccr, &scr->ccr); ++ iowrite16(tcpd->scr_mcr, &scr->mcr); ++ iowrite16(tcpd->scr_gper, &scr->gper); ++ iowrite8 (0, &scr->irr); ++ iowrite8 (0xbf, &scr->imr); ++ iowrite16(tcpd->scr_gpo_dsr, scr->gpo_dsr + 0); ++ iowrite16(tcpd->scr_gpo_dsr >> 16, scr->gpo_dsr + 1); ++ iowrite16(tcpd->scr_gpo_doecr, scr->gpo_doecr + 0); ++ iowrite16(tcpd->scr_gpo_doecr >> 16, scr->gpo_doecr + 1); ++} ++ ++static int tc6393_probe (struct device *dev) ++{ ++ struct platform_device* pdev = to_platform_device (dev); ++ struct tc6393_platform_data* tcpd = dev->platform_data; ++ struct tc6393* tc6393; ++ struct resource* iomem; ++ struct resource* rscr; ++ int retval; ++ int i; ++ ++ iomem = platform_get_resource (pdev, IORESOURCE_MEM, 0); ++ if (!iomem) ++ return -EINVAL; ++ ++ tc6393 = kzalloc (sizeof *tc6393, GFP_KERNEL); ++ if (!tc6393) { ++ retval = -ENOMEM; ++ goto err_kzalloc; ++ } ++ ++ dev_set_drvdata (dev, tc6393); ++ spin_lock_init (&tc6393->lock); ++ tc6393->dev = dev; ++ tc6393->iomem = iomem; ++ tc6393->irq = platform_get_irq (pdev, 0); ++ ++ rscr = &tc6393->rscr; ++ rscr->name = TMIO_NAME_CORE; ++ rscr->start = iomem->start; ++ rscr->end = iomem->start + 0xff; ++ rscr->flags = IORESOURCE_MEM; ++ ++ retval = request_resource (iomem, rscr); ++ if (retval) ++ goto err_request_scr; ++ ++ tc6393->scr = ioremap (rscr->start, rscr->end - rscr->start + 1); ++ if (!tc6393->scr) { ++ retval = -ENOMEM; ++ goto err_ioremap; ++ } ++ ++ tc6393_hw_init (tc6393); ++ ++ printk (KERN_INFO "Toshiba %s revision %d at 0x%08lx, irq %d\n", ++ TMIO_SOC_NAME, ioread8 (&tc6393->scr->revid), ++ iomem->start, tc6393->irq); ++ ++ if (tc6393->irq) ++ tc6393_attach_irq (tc6393); ++ ++ for (i = 0; i < tcpd->num_cells; i++) ++ tc6393_device_register (tc6393, tcpd->cell + i); ++ ++ return 0; ++ ++err_ioremap: ++ release_resource (rscr); ++err_request_scr: ++ kfree(tc6393); ++err_kzalloc: ++ release_resource (iomem); ++ return retval; ++} ++ ++static int tc6393_dev_remove (struct device *dev, void *data) ++{ ++ device_unregister (dev); ++ return 0; ++} ++ ++static int tc6393_remove (struct device *dev) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ ++ device_for_each_child (dev, tc6393, tc6393_dev_remove); ++ ++ if (tc6393->irq) ++ tc6393_detach_irq (tc6393); ++ ++ iounmap (tc6393->scr); ++ release_resource (&tc6393->rscr); ++ release_resource (tc6393->iomem); ++ kfree (tc6393); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int tc6393_suspend (struct device *dev, pm_message_t state) ++{ ++ struct tc6393_platform_data* tcpd = dev->platform_data; ++ tcpd->disable (dev); ++ return 0; ++} ++ ++static int tc6393_resume (struct device *dev) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ tc6393_hw_init (tc6393); ++ return 0; ++} ++#endif ++ ++static struct device_driver tc6393_device_driver = { ++ .name = TMIO_SOC_NAME, ++ .bus = &platform_bus_type, ++ .probe = tc6393_probe, ++ .remove = tc6393_remove, ++#ifdef CONFIG_PM ++ .suspend = tc6393_suspend, ++ .resume = tc6393_resume, ++#endif ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++static int __init tc6393_init (void) ++{ ++ int retval = bus_register (&tc6393_bus_type); ++ if (retval) ++ return retval; ++ ++ return driver_register (&tc6393_device_driver); ++} ++ ++static void __exit tc6393_exit (void) ++{ ++ driver_unregister (&tc6393_device_driver); ++ bus_unregister (&tc6393_bus_type); ++} ++ ++module_init (tc6393_init); ++module_exit (tc6393_exit); ++ ++MODULE_DESCRIPTION ("TC6393 SoC bus driver"); ++MODULE_AUTHOR ("Chris Humbert, Dirk Opfer"); ++MODULE_LICENSE ("GPL"); +Index: git/arch/arm/common/Kconfig +=================================================================== +--- git.orig/arch/arm/common/Kconfig 2006-10-31 16:08:28.000000000 +0000 ++++ git/arch/arm/common/Kconfig 2006-11-07 22:13:09.000000000 +0000 +@@ -31,3 +31,6 @@ config SHARPSL_PM + + config SHARP_SCOOP + bool ++ ++config TOSHIBA_TC6393XB ++ bool +Index: git/arch/arm/mach-pxa/Kconfig +=================================================================== +--- git.orig/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:06.000000000 +0000 ++++ git/arch/arm/mach-pxa/ 2006-11-07 23:30:34.000000000 +0000 +@@ -128,6 +128,7 @@ config MACH_BORZOI + config MACH_TOSA + bool "Enable Sharp SL-6000x (Tosa) Support" + depends on PXA_SHARPSL_25x ++ select TOSHIBA_TC6393XB + + config PXA25x + bool +Index: git/arch/arm/common/Makefile +=================================================================== +--- git.orig/arch/arm/common/Makefile 2006-10-31 16:08:28.000000000 +0000 ++++ git/arch/arm/common/Makefile 2006-11-07 22:13:09.000000000 +0000 +@@ -17,3 +17,4 @@ obj-$(CONFIG_SHARPSL_PM) += sharpsl_pm.o + obj-$(CONFIG_SHARP_SCOOP) += scoop.o + obj-$(CONFIG_ARCH_IXP2000) += uengine.o + obj-$(CONFIG_ARCH_IXP23XX) += uengine.o ++obj-$(CONFIG_TOSHIBA_TC6393XB) += tc6393xb.o +Index: git/include/asm-arm/hardware/tmio.h +=================================================================== +--- git.orig/include/asm-arm/hardware/tmio.h 2006-11-07 22:13:09.000000000 +0000 ++++ git/include/asm-arm/hardware/tmio.h 2006-11-07 22:13:09.000000000 +0000 +@@ -91,6 +91,50 @@ struct tmio_device { + + /*--------------------------------------------------------------------------*/ + ++/* ++ * TC6393XB SoC ++ */ ++#ifdef CONFIG_TOSHIBA_TC6393XB ++#define TMIO_SOC_TC6393XB ++#define TMIO_SOC_NAME "TC6393XB" ++#define TMIO_NAME_BUS "tc6393-bus" ++#define TMIO_NAME_CORE "tc6393-core" ++#define TMIO_NAME_NAND "tc6393-nand" ++#define TMIO_NAME_SD "tc6393-sd" ++#define TMIO_NAME_OHCI "tc6393-ohci" ++#define TMIO_NAME_SERIAL "tc6393-serial" ++#define TMIO_NAME_LCD "tc6393-lcd" ++#define tmio_bus_type tc6393_bus_type ++ ++#define TC6393_GPIO(x) (1 << (x)) ++ ++extern struct bus_type tc6393_bus_type; ++ ++struct tc6393_platform_data { ++ u16 scr_pll2cr; /* PLL2 Control */ ++ u16 scr_ccr; /* Clock Control */ ++ u16 scr_mcr; /* Mode Control */ ++ u16 scr_gper; /* GP Enable */ ++ u32 scr_gpo_doecr; /* GPO Data OE Control */ ++ u32 scr_gpo_dsr; /* GPO Data Set */ ++ ++ /* cells to register as devices */ ++ struct tmio_cell* cell; ++ unsigned int num_cells; ++ ++ /* callbacks to enable and disable the TC6393XB's power and clock */ ++ void (*enable) (struct device *dev); ++ void (*disable) (struct device *dev); ++}; ++ ++u32 get_tc6393_gpio (struct device *dev); ++u32 set_tc6393_gpio (struct device *dev, u32 bits); ++u32 reset_tc6393_gpio (struct device *dev, u32 bits); ++ ++/*--------------------------------------------------------------------------*/ ++ ++#else + #error "no TMIO SoC configured" ++#endif + + #endif +Index: git/include/asm-arm/arch-pxa/irqs.h +=================================================================== +--- git.orig/include/asm-arm/arch-pxa/irqs.h 2006-10-31 16:09:33.000000000 +0000 ++++ git/include/asm-arm/arch-pxa/irqs.h 2006-11-07 22:13:09.000000000 +0000 +@@ -163,17 +163,27 @@ + #define IRQ_LOCOMO_SPI_OVRN (IRQ_BOARD_END + 20) + #define IRQ_LOCOMO_SPI_TEND (IRQ_BOARD_END + 21) + ++#define IRQ_TC6393_START (IRQ_BOARD_END) ++#define IRQ_TC6393_NAND (IRQ_BOARD_END + 0) ++#define IRQ_TC6393_SD (IRQ_BOARD_END + 1) ++#define IRQ_TC6393_OHCI (IRQ_BOARD_END + 2) ++#define IRQ_TC6393_SERIAL (IRQ_BOARD_END + 3) ++#define IRQ_TC6393_LCD (IRQ_BOARD_END + 4) ++ + /* + * Figure out the MAX IRQ number. + * + * If we have an SA1111, the max IRQ is S1_BVD1_STSCHG+1. + * If we have an LoCoMo, the max IRQ is IRQ_LOCOMO_SPI_TEND+1 ++ * If we have an TC6393XB, the max IRQ is IRQ_TC6393_LCD+1 + * Otherwise, we have the standard IRQs only. + */ + #ifdef CONFIG_SA1111 + #define NR_IRQS (IRQ_S1_BVD1_STSCHG + 1) + #elif defined(CONFIG_SHARP_LOCOMO) + #define NR_IRQS (IRQ_LOCOMO_SPI_TEND + 1) ++#elif defined(CONFIG_TOSHIBA_TC6393XB) ++#define NR_IRQS (IRQ_TC6393_LCD + 1) + #elif defined(CONFIG_ARCH_LUBBOCK) || \ + defined(CONFIG_MACH_LOGICPD_PXA270) || \ + defined(CONFIG_MACH_MAINSTONE) diff --git a/packages/linux/linux-rp-2.6.23/tosa-bluetooth-r8.patch b/packages/linux/linux-rp-2.6.23/tosa-bluetooth-r8.patch new file mode 100644 index 0000000000..9976549956 --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/tosa-bluetooth-r8.patch @@ -0,0 +1,388 @@ +Index: linux-2.6.17/arch/arm/mach-pxa/Makefile +=================================================================== +--- linux-2.6.17.orig/arch/arm/mach-pxa/Makefile 2006-06-20 11:45:51.252467944 +0200 ++++ linux-2.6.17/arch/arm/mach-pxa/Makefile 2006-06-20 11:46:33.619027248 +0200 +@@ -16,7 +16,7 @@ + obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o + obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o + obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o +-obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o tosa_lcd.o ++obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o tosa_lcd.o tosa_bt.o + obj-$(CONFIG_MACH_EM_X270) += em-x270.o + obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o + +Index: linux-2.6.17/arch/arm/mach-pxa/tosa_bt.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/arch/arm/mach-pxa/tosa_bt.c 2006-06-20 11:46:08.107905528 +0200 +@@ -0,0 +1,128 @@ ++/* ++ * Bluetooth control code for Sharp SL-6000x (tosa) ++ * ++ * Copyright (c) 2005 Dirk Opfer ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <asm/hardware.h> ++ ++#include <asm/hardware/scoop.h> ++#include <asm/arch/tosa.h> ++#include <asm/arch/pxa-regs.h> ++ ++ ++static int tosa_bluetooth_power(int on) ++{ ++ ++ if (!on) { //off ++ ++ set_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET); ++ pxa_gpio_mode(GPIO42_BTRXD|GPIO_IN); ++ pxa_gpio_mode(GPIO43_BTTXD|GPIO_IN); ++ pxa_gpio_mode(GPIO44_BTCTS|GPIO_IN); ++ pxa_gpio_mode(GPIO45_BTRTS|GPIO_IN); ++ mdelay(10); // wait 10ms ++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET); ++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_PWR_EN); ++ reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_BT_LED); // turn off BT LED ++ ++ } else { // on ++ ++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET); ++ set_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_PWR_EN); ++ pxa_gpio_mode(GPIO42_HWRXD_MD); ++ pxa_gpio_mode(GPIO43_HWTXD_MD); ++ pxa_gpio_mode(GPIO44_HWCTS_MD); ++ pxa_gpio_mode(GPIO45_HWRTS_MD); ++ ++ set_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET); ++ mdelay(20); // wait 20ms ++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET); ++ set_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_BT_LED); // turn BT LED on ++ } ++ return 0; ++} ++ ++/* ++ * Support Routines ++ */ ++int __init tosa_bluetooth_probe(struct platform_device *dev) ++{ ++ int ret = 0; ++ pxa_gpio_mode(GPIO42_BTRXD|GPIO_IN); ++ pxa_gpio_mode(GPIO43_BTTXD|GPIO_IN); ++ pxa_gpio_mode(GPIO44_BTCTS|GPIO_IN); ++ pxa_gpio_mode(GPIO45_BTRTS|GPIO_IN); ++ set_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_PWR_EN); ++ mdelay(5); ++ ++ if ( (GPLR(GPIO42_BTRXD) & GPIO_bit(GPIO42_BTRXD))==0 && ++ (GPLR(GPIO44_BTCTS) & GPIO_bit(GPIO44_BTCTS))==0) { ++ printk(KERN_INFO "No Bluetooth Device found!\n"); ++ ret = ENODEV; // no bluetooth ++ } else { ++ printk(KERN_INFO "Tosa Bluetooth Device found on ttyS3!\n"); ++ } ++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_PWR_EN); ++ ++ tosa_bluetooth_power(1); // Power on ++ return ret; ++} ++ ++static int tosa_bluetooth_remove(struct platform_device *dev) ++{ ++ tosa_bluetooth_power(0); // Power off ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int tosa_bluetooth_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ tosa_bluetooth_power(0); // Power off ++ return 0; ++} ++ ++static int tosa_bluetooth_resume(struct platform_device *dev) ++{ ++ tosa_bluetooth_power(1); // Power on ++ return 0; ++} ++#else ++#define tosa_bluetooth_suspend NULL ++#define tosa_bluetooth_resume NULL ++#endif ++ ++static struct platform_driver tosa_bluetooth_driver = { ++ .probe = tosa_bluetooth_probe, ++ .remove = tosa_bluetooth_remove, ++ .suspend = tosa_bluetooth_suspend, ++ .resume = tosa_bluetooth_resume, ++ .driver = { ++ .name = "tosa-bluetooth", ++ }, ++}; ++ ++int __init tosa_bluetooth_init(void) ++{ ++ return platform_driver_register(&tosa_bluetooth_driver); ++} ++ ++void __exit tosa_bluetooth_cleanup(void) ++{ ++ platform_driver_unregister(&tosa_bluetooth_driver); ++} ++ ++module_init(tosa_bluetooth_init); ++module_exit(tosa_bluetooth_cleanup); +Index: linux-2.6.17/arch/arm/mach-pxa/tosa.c +=================================================================== +--- linux-2.6.17.orig/arch/arm/mach-pxa/tosa.c 2006-06-20 11:45:51.254467640 +0200 ++++ linux-2.6.17/arch/arm/mach-pxa/tosa.c 2006-06-20 11:46:08.112904768 +0200 +@@ -288,7 +288,7 @@ + + static void tosa_tc6393_enable(struct device *dev) + { +- ++ printk("!!tosa_tc6393_enable!!\n"); + reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON); + reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC6393_SUSPEND); + reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_TC6393_REST_IN); //#PCLR +@@ -303,7 +303,7 @@ + + static void tosa_tc6393_disable(struct device *dev) + { +- ++ printk("!!tosa_tc6393_disable!!\n"); + reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON); + reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC6393_SUSPEND); + reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_TC6393_REST_IN); //#PCLR +@@ -428,6 +428,17 @@ + }, + }; + ++/* ++ * Tosa Blueooth ++ */ ++static struct platform_device tosa_bluetooth_device = { ++ .name = "tosa-bluetooth", ++ .id = -1, ++ .dev = { ++ .parent = &tosascoop_jc_device.dev, ++ }, ++}; ++ + static struct platform_device *devices[] __initdata = { + &tosascoop_device, + &tosascoop_jc_device, +@@ -435,6 +446,7 @@ + &tosaled_device, + &tc6393_device, + &tosalcd_device, ++ &tosa_bluetooth_device, + }; + + static void tosa_poweroff(void) +Index: linux-2.6.17/arch/arm/mach-pxa/Makefile +=================================================================== +--- linux-2.6.17.orig/arch/arm/mach-pxa/Makefile 2006-06-20 11:45:51.252467944 +0200 ++++ linux-2.6.17/arch/arm/mach-pxa/Makefile 2006-06-20 11:46:33.619027248 +0200 +@@ -16,7 +16,7 @@ + obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o + obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o + obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o +-obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o tosa_lcd.o ++obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o tosa_lcd.o tosa_bt.o + obj-$(CONFIG_MACH_EM_X270) += em-x270.o + obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o + +Index: linux-2.6.17/arch/arm/mach-pxa/tosa_bt.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/arch/arm/mach-pxa/tosa_bt.c 2006-06-20 11:46:08.107905528 +0200 +@@ -0,0 +1,128 @@ ++/* ++ * Bluetooth control code for Sharp SL-6000x (tosa) ++ * ++ * Copyright (c) 2005 Dirk Opfer ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <asm/hardware.h> ++ ++#include <asm/hardware/scoop.h> ++#include <asm/arch/tosa.h> ++#include <asm/arch/pxa-regs.h> ++ ++ ++static int tosa_bluetooth_power(int on) ++{ ++ ++ if (!on) { //off ++ ++ set_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET); ++ pxa_gpio_mode(GPIO42_BTRXD|GPIO_IN); ++ pxa_gpio_mode(GPIO43_BTTXD|GPIO_IN); ++ pxa_gpio_mode(GPIO44_BTCTS|GPIO_IN); ++ pxa_gpio_mode(GPIO45_BTRTS|GPIO_IN); ++ mdelay(10); // wait 10ms ++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET); ++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_PWR_EN); ++ reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_BT_LED); // turn off BT LED ++ ++ } else { // on ++ ++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET); ++ set_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_PWR_EN); ++ pxa_gpio_mode(GPIO42_HWRXD_MD); ++ pxa_gpio_mode(GPIO43_HWTXD_MD); ++ pxa_gpio_mode(GPIO44_HWCTS_MD); ++ pxa_gpio_mode(GPIO45_HWRTS_MD); ++ ++ set_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET); ++ mdelay(20); // wait 20ms ++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_RESET); ++ set_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_BT_LED); // turn BT LED on ++ } ++ return 0; ++} ++ ++/* ++ * Support Routines ++ */ ++int __init tosa_bluetooth_probe(struct platform_device *dev) ++{ ++ int ret = 0; ++ pxa_gpio_mode(GPIO42_BTRXD|GPIO_IN); ++ pxa_gpio_mode(GPIO43_BTTXD|GPIO_IN); ++ pxa_gpio_mode(GPIO44_BTCTS|GPIO_IN); ++ pxa_gpio_mode(GPIO45_BTRTS|GPIO_IN); ++ set_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_PWR_EN); ++ mdelay(5); ++ ++ if ( (GPLR(GPIO42_BTRXD) & GPIO_bit(GPIO42_BTRXD))==0 && ++ (GPLR(GPIO44_BTCTS) & GPIO_bit(GPIO44_BTCTS))==0) { ++ printk(KERN_INFO "No Bluetooth Device found!\n"); ++ ret = ENODEV; // no bluetooth ++ } else { ++ printk(KERN_INFO "Tosa Bluetooth Device found on ttyS3!\n"); ++ } ++ reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_BT_PWR_EN); ++ ++ tosa_bluetooth_power(1); // Power on ++ return ret; ++} ++ ++static int tosa_bluetooth_remove(struct platform_device *dev) ++{ ++ tosa_bluetooth_power(0); // Power off ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int tosa_bluetooth_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ tosa_bluetooth_power(0); // Power off ++ return 0; ++} ++ ++static int tosa_bluetooth_resume(struct platform_device *dev) ++{ ++ tosa_bluetooth_power(1); // Power on ++ return 0; ++} ++#else ++#define tosa_bluetooth_suspend NULL ++#define tosa_bluetooth_resume NULL ++#endif ++ ++static struct platform_driver tosa_bluetooth_driver = { ++ .probe = tosa_bluetooth_probe, ++ .remove = tosa_bluetooth_remove, ++ .suspend = tosa_bluetooth_suspend, ++ .resume = tosa_bluetooth_resume, ++ .driver = { ++ .name = "tosa-bluetooth", ++ }, ++}; ++ ++int __init tosa_bluetooth_init(void) ++{ ++ return platform_driver_register(&tosa_bluetooth_driver); ++} ++ ++void __exit tosa_bluetooth_cleanup(void) ++{ ++ platform_driver_unregister(&tosa_bluetooth_driver); ++} ++ ++module_init(tosa_bluetooth_init); ++module_exit(tosa_bluetooth_cleanup); +Index: linux-2.6.17/arch/arm/mach-pxa/tosa.c +=================================================================== +--- linux-2.6.17.orig/arch/arm/mach-pxa/tosa.c 2006-06-20 11:45:51.254467640 +0200 ++++ linux-2.6.17/arch/arm/mach-pxa/tosa.c 2006-06-20 11:46:08.112904768 +0200 +@@ -288,7 +288,7 @@ + + static void tosa_tc6393_enable(struct device *dev) + { +- ++ printk("!!tosa_tc6393_enable!!\n"); + reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON); + reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC6393_SUSPEND); + reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_TC6393_REST_IN); //#PCLR +@@ -303,7 +303,7 @@ + + static void tosa_tc6393_disable(struct device *dev) + { +- ++ printk("!!tosa_tc6393_disable!!\n"); + reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON); + reset_scoop_gpio(&tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC6393_SUSPEND); + reset_scoop_gpio(&tosascoop_device.dev,TOSA_SCOOP_TC6393_REST_IN); //#PCLR +@@ -428,6 +428,17 @@ + }, + }; + ++/* ++ * Tosa Blueooth ++ */ ++static struct platform_device tosa_bluetooth_device = { ++ .name = "tosa-bluetooth", ++ .id = -1, ++ .dev = { ++ .parent = &tosascoop_jc_device.dev, ++ }, ++}; ++ + static struct platform_device *devices[] __initdata = { + &tosascoop_device, + &tosascoop_jc_device, +@@ -435,6 +446,7 @@ + &tosaled_device, + &tc6393_device, + &tosalcd_device, ++ &tosa_bluetooth_device, + }; + + static void tosa_poweroff(void) diff --git a/packages/linux/linux-rp-2.6.23/tosa-keyboard-r19.patch b/packages/linux/linux-rp-2.6.23/tosa-keyboard-r19.patch new file mode 100644 index 0000000000..5d94f25527 --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/tosa-keyboard-r19.patch @@ -0,0 +1,1028 @@ + drivers/input/keyboard/Kconfig | 12 - + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/tosakbd.c | 467 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 479 insertions(+), 1 deletion(-) + +Index: git/drivers/input/keyboard/Kconfig +=================================================================== +--- git.orig/drivers/input/keyboard/Kconfig 2006-10-31 16:08:57.000000000 +0000 ++++ git/drivers/input/keyboard/Kconfig 2006-11-07 22:13:10.000000000 +0000 +@@ -148,12 +148,22 @@ config KEYBOARD_SPITZ + depends on PXA_SHARPSL + default y + help +- Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000, ++ Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000, + SL-C3000 and Sl-C3100 series of PDAs. + + To compile this driver as a module, choose M here: the + module will be called spitzkbd. + ++config KEYBOARD_TOSA ++ tristate "Tosa keyboard" ++ depends on PXA_SHARPSL ++ default y ++ help ++ Say Y here to enable the keyboard on the Sharp Zaurus SL-6000x (Tosa) ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tosakbd. ++ + config KEYBOARD_AMIGA + tristate "Amiga keyboard" + depends on AMIGA +Index: git/drivers/input/keyboard/Makefile +=================================================================== +--- git.orig/drivers/input/keyboard/Makefile 2006-10-31 16:08:57.000000000 +0000 ++++ git/drivers/input/keyboard/Makefile 2006-11-07 22:13:10.000000000 +0000 +@@ -17,3 +17,4 @@ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkb + obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o + obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o + obj-$(CONFIG_KEYBOARD_ASIC3) += asic3_keys.o ++obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o +Index: git/drivers/input/keyboard/tosakbd.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ git/drivers/input/keyboard/tosakbd.c 2006-11-07 23:27:19.000000000 +0000 +@@ -0,0 +1,467 @@ ++/* ++ * Keyboard driver for Sharp Tosa models (SL-6000x) ++ * ++ * Copyright (c) 2005 Dirk Opfer ++ * ++ * Based on xtkbd.c/locomkbd.c/corgikbd.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <linux/init.h> ++#include <linux/input.h> ++#include <linux/interrupt.h> ++#include <linux/jiffies.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++ ++#include <asm/arch/tosa.h> ++#include <asm/arch/hardware.h> ++#include <asm/arch/pxa-regs.h> ++ ++ ++#define TOSA_KEY_STROBE_NUM (11) ++#define TOSA_KEY_SENSE_NUM (7) ++ ++#define KEYMASK_ON (0x1<<0) ++#define KEYMASK_REC (0x1<<1) ++#define KEYMASK_SYNC (0x1<<2) ++ ++#define KB_ROWS 7 ++#define KB_COLS 11 ++#define KB_ROWMASK(r) (1 << (r)) ++#define SCANCODE(r,c) ( ((r)<<4) + (c) + 1 ) ++#define NR_SCANCODES (SCANCODE(KB_ROWS-1,KB_COLS)+1+1) ++ ++#define SCAN_INTERVAL (HZ/10) ++#define HP_SCAN_INTERVAL (150) /* ms */ ++#define HP_STABLE_COUNT 2 ++ ++#define TOSA_KEY_CALENDER KEY_F1 ++#define TOSA_KEY_ADDRESS KEY_F2 ++#define TOSA_KEY_FN KEY_F3 ++#define TOSA_KEY_CANCEL KEY_F4 ++#define TOSA_KEY_OFF KEY_SUSPEND ++#define TOSA_KEY_CENTER KEY_F5 ++#define TOSA_KEY_REC KEY_F6 ++#define TOSA_KEY_LIGHT KEY_F7 ++#define TOSA_KEY_RECORD KEY_F8 ++#define TOSA_KEY_HOME KEY_F9 ++#define TOSA_KEY_MAIL KEY_F10 ++#define TOSA_KEY_OK KEY_F11 ++#define TOSA_KEY_MENU KEY_F12 ++#define TOSA_KEY_SYNC KEY_F13 ++ ++#define GET_ROWS_STATUS(c) ((GPLR2 & TOSA_GPIO_ALL_SENSE_BIT) >> TOSA_GPIO_ALL_SENSE_RSHIFT) ++#define KB_DISCHARGE_DELAY 10 ++#define KB_ACTIVATE_DELAY 10 ++ ++ ++static unsigned char tosakbd_keycode[NR_SCANCODES] = { ++ 0, /* 0 */ ++ 0, KEY_W, 0, 0, 0, KEY_K, KEY_BACKSPACE, KEY_P, 0, 0, 0, TOSA_KEY_OFF, 0, 0, 0, 0, /*1 - 16*/ ++ KEY_Q, KEY_E, KEY_T, KEY_Y, 0, KEY_O, KEY_I, KEY_COMMA, 0, 0, 0, TOSA_KEY_RECORD, 0, 0, 0, 0, /*17 - 32*/ ++ KEY_A, KEY_D, KEY_G, KEY_U, 0, KEY_L, KEY_ENTER, KEY_DOT, 0, 0, 0, TOSA_KEY_SYNC, 0, 0, 0, 0, /*33 - 48*/ ++ KEY_Z, KEY_C, KEY_V, KEY_J, TOSA_KEY_ADDRESS, TOSA_KEY_CANCEL, TOSA_KEY_CENTER, TOSA_KEY_OK, KEY_LEFTSHIFT, 0 , 0,0 , 0, 0, 0, 0, /*49 - 64*/ ++ KEY_S, KEY_R, KEY_B, KEY_N, TOSA_KEY_CALENDER, TOSA_KEY_HOME, TOSA_KEY_REC, TOSA_KEY_LIGHT, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /*65 - 80*/ ++ KEY_TAB, KEY_SLASH, KEY_H, KEY_M, TOSA_KEY_MENU, 0, KEY_UP, 0, 0, 0, TOSA_KEY_FN, 0, 0, 0, 0, 0, /*81 - 96*/ ++ KEY_X, KEY_F, KEY_SPACE, KEY_APOSTROPHE, TOSA_KEY_MAIL, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, /*97 - 109*/ ++}; ++ ++struct tosakbd { ++ unsigned char keycode[ARRAY_SIZE(tosakbd_keycode)]; ++ struct input_dev *input; ++ ++ spinlock_t lock; ++ struct timer_list timer; ++ struct timer_list hptimer; ++ ++ int hp_state; ++ int hp_count; ++ ++ unsigned int suspended; ++ unsigned long suspend_jiffies; ++}; ++ ++/* Helper functions for reading the keyboard matrix ++ * Note: We should really be using pxa_gpio_mode to alter GPDR but it ++ * requires a function call per GPIO bit which is excessive ++ * when we need to access 12 bits at once, multiple times. ++ * These functions must be called within local_irq_save()/local_irq_restore() ++ * or similar. ++ */ ++static inline void tosakbd_discharge_all(void) ++{ ++ /* STROBE All HiZ */ ++ GPCR1 = TOSA_GPIO_HIGH_STROBE_BIT; ++ GPDR1 &= ~TOSA_GPIO_HIGH_STROBE_BIT; ++ GPCR2 = TOSA_GPIO_LOW_STROBE_BIT; ++ GPDR2 &= ~TOSA_GPIO_LOW_STROBE_BIT; ++} ++ ++static inline void tosakbd_activate_all(void) ++{ ++ /* STROBE ALL -> High */ ++ GPSR1 = TOSA_GPIO_HIGH_STROBE_BIT; ++ GPDR1 |= TOSA_GPIO_HIGH_STROBE_BIT; ++ GPSR2 = TOSA_GPIO_LOW_STROBE_BIT; ++ GPDR2 |= TOSA_GPIO_LOW_STROBE_BIT; ++ ++ udelay(KB_DISCHARGE_DELAY); ++ ++ /* STATE CLEAR */ ++ GEDR2 |= TOSA_GPIO_ALL_SENSE_BIT; ++} ++ ++static inline void tosakbd_activate_col(int col) ++{ ++ if (col<=5) { ++ /* STROBE col -> High, not col -> HiZ */ ++ GPSR1 = TOSA_GPIO_STROBE_BIT(col); ++ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); ++ } else { ++ /* STROBE col -> High, not col -> HiZ */ ++ GPSR2 = TOSA_GPIO_STROBE_BIT(col); ++ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); ++ } ++} ++ ++static inline void tosakbd_reset_col(int col) ++{ ++ if (col<=5) { ++ /* STROBE col -> Low */ ++ GPCR1 = TOSA_GPIO_STROBE_BIT(col); ++ /* STROBE col -> out, not col -> HiZ */ ++ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); ++ } else { ++ /* STROBE col -> Low */ ++ GPCR2 = TOSA_GPIO_STROBE_BIT(col); ++ /* STROBE col -> out, not col -> HiZ */ ++ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); ++ } ++} ++ ++/* ++ * Read the GPIOs for POWER, RECORD and SYNC ++ */ ++static int read_port_key_status_raw(void) ++{ ++ int val=0; ++ ++ /* Power key */ ++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_ON_KEY))==0) ++ val |= KEYMASK_ON; ++ /* Record key */ ++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_RECORD_BTN))==0) ++ val |= KEYMASK_REC; ++ /* Sync key */ ++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_SYNC))==0) ++ val |= KEYMASK_SYNC; ++ return val; ++} ++ ++ ++/* ++ * The tosa keyboard only generates interrupts when a key is pressed. ++ * So when a key is pressed, we enable a timer. This timer scans the ++ * keyboard, and this is how we detect when the key is released. ++ */ ++ ++/* Scan the hardware keyboard and push any changes up through the input layer */ ++static void tosakbd_scankeyboard(struct tosakbd *tosakbd_data) ++{ ++ unsigned int row, col, rowd; ++ unsigned long flags; ++ unsigned int num_pressed = 0; ++ ++ if (tosakbd_data->suspended) ++ return; ++ ++ spin_lock_irqsave(&tosakbd_data->lock, flags); ++ ++ for (col = 0; col < KB_COLS; col++) { ++ /* ++ * Discharge the output driver capacitatance ++ * in the keyboard matrix. (Yes it is significant..) ++ */ ++ tosakbd_discharge_all(); ++ udelay(KB_DISCHARGE_DELAY); ++ ++ tosakbd_activate_col( col); ++ udelay(KB_ACTIVATE_DELAY); ++ ++ rowd = GET_ROWS_STATUS(col); ++ ++ for (row = 0; row < KB_ROWS; row++) { ++ unsigned int scancode, pressed; ++ scancode = SCANCODE(row, col); ++ pressed = rowd & KB_ROWMASK(row); ++ input_report_key(tosakbd_data->input, tosakbd_data->keycode[scancode], pressed); ++ if (pressed) ++ num_pressed++; ++ } ++ ++ tosakbd_reset_col(col); ++ } ++ ++ tosakbd_activate_all(); ++ ++ rowd = read_port_key_status_raw(); ++ ++ for (row = 0; row < 3; row++ ) { ++ unsigned int scancode, pressed; ++ scancode = SCANCODE(row, KB_COLS); ++ pressed = rowd & KB_ROWMASK(row); ++ input_report_key(tosakbd_data->input, tosakbd_data->keycode[scancode], pressed); ++ if (pressed) ++ num_pressed++; ++ ++ if (pressed && (tosakbd_data->keycode[scancode] == TOSA_KEY_OFF) ++ && time_after(jiffies, tosakbd_data->suspend_jiffies + msecs_to_jiffies(1000))) { ++ input_event(tosakbd_data->input, EV_PWR, TOSA_KEY_OFF, 1); ++ tosakbd_data->suspend_jiffies = jiffies; ++ } ++ } ++ ++ input_sync(tosakbd_data->input); ++ ++ /* if any keys are pressed, enable the timer */ ++ if (num_pressed) ++ mod_timer(&tosakbd_data->timer, jiffies + SCAN_INTERVAL); ++ ++ spin_unlock_irqrestore(&tosakbd_data->lock, flags); ++} ++ ++/* ++ * tosa keyboard interrupt handler. ++ */ ++static irqreturn_t tosakbd_interrupt(int irq, void *dev_id) ++{ ++ struct tosakbd *tosakbd_data = dev_id; ++ ++ if (!timer_pending(&tosakbd_data->timer)) ++ { ++ /** wait chattering delay **/ ++ udelay(20); ++ tosakbd_scankeyboard(tosakbd_data); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * tosa timer checking for released keys ++ */ ++static void tosakbd_timer_callback(unsigned long data) ++{ ++ struct tosakbd *tosakbd_data = (struct tosakbd *) data; ++ tosakbd_scankeyboard(tosakbd_data); ++} ++ ++/* ++ * The headphone generates an interrupt. ++ * We debounce the switche and pass them to the input system. ++ */ ++ ++static irqreturn_t tosakbd_hp_isr(int irq, void *dev_id) ++{ ++ struct tosakbd *tosakbd_data = dev_id; ++ ++ if (!timer_pending(&tosakbd_data->hptimer)) ++ mod_timer(&tosakbd_data->hptimer, jiffies + msecs_to_jiffies(HP_SCAN_INTERVAL)); ++ ++ return IRQ_HANDLED; ++} ++ ++static void tosakbd_hp_timer(unsigned long data) ++{ ++ struct tosakbd *tosakbd_data = (struct tosakbd *) data; ++ unsigned long state; ++ unsigned long flags; ++ ++ state = (GPLR(TOSA_GPIO_EAR_IN) & GPIO_bit(TOSA_GPIO_EAR_IN)); ++ if (state != tosakbd_data->hp_state) { ++ tosakbd_data->hp_count = 0; ++ tosakbd_data->hp_state = state; ++ } else if (tosakbd_data->hp_count < HP_STABLE_COUNT) { ++ tosakbd_data->hp_count++; ++ } ++ ++ if (tosakbd_data->hp_count >= HP_STABLE_COUNT) { ++ spin_lock_irqsave(&tosakbd_data->lock, flags); ++ ++ input_report_switch(tosakbd_data->input, SW_HEADPHONE_INSERT, ((GPLR(TOSA_GPIO_EAR_IN) & GPIO_bit(TOSA_GPIO_EAR_IN)) == 0)); ++ input_sync(tosakbd_data->input); ++ ++ spin_unlock_irqrestore(&tosakbd_data->lock, flags); ++ } else { ++ mod_timer(&tosakbd_data->hptimer, jiffies + msecs_to_jiffies(HP_SCAN_INTERVAL)); ++ } ++} ++ ++#ifdef CONFIG_PM ++static int tosakbd_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct tosakbd *tosakbd = platform_get_drvdata(dev); ++ ++ tosakbd->suspended = 1; ++ ++ return 0; ++} ++ ++static int tosakbd_resume(struct platform_device *dev) ++{ ++ struct tosakbd *tosakbd = platform_get_drvdata(dev); ++ ++ /* Upon resume, ignore the suspend key for a short while */ ++ tosakbd->suspend_jiffies = jiffies; ++ tosakbd->suspended = 0; ++ ++ return 0; ++} ++#else ++#define tosakbd_suspend NULL ++#define tosakbd_resume NULL ++#endif ++ ++static int __init tosakbd_probe(struct platform_device *pdev) { ++ ++ int i; ++ struct tosakbd *tosakbd; ++ struct input_dev *input_dev; ++ ++ tosakbd = kzalloc(sizeof(struct tosakbd), GFP_KERNEL); ++ if (!tosakbd) ++ return -ENOMEM; ++ ++ input_dev = input_allocate_device(); ++ if (!input_dev) { ++ kfree(tosakbd); ++ return -ENOMEM; ++ } ++ ++ platform_set_drvdata(pdev,tosakbd); ++ ++ spin_lock_init(&tosakbd->lock); ++ ++ /* Init Keyboard rescan timer */ ++ init_timer(&tosakbd->timer); ++ tosakbd->timer.function = tosakbd_timer_callback; ++ tosakbd->timer.data = (unsigned long) tosakbd; ++ ++ /* Init Headphone Timer */ ++ init_timer(&tosakbd->hptimer); ++ tosakbd->hptimer.function = tosakbd_hp_timer; ++ tosakbd->hptimer.data = (unsigned long) tosakbd; ++ ++ tosakbd->suspend_jiffies = jiffies; ++ ++ tosakbd->input = input_dev; ++ ++ input_dev->private = tosakbd; ++ input_dev->name = "Tosa Keyboard"; ++ input_dev->phys = "tosakbd/input0"; ++ input_dev->cdev.dev = &pdev->dev; ++ ++ input_dev->id.bustype = BUS_HOST; ++ input_dev->id.vendor = 0x0001; ++ input_dev->id.product = 0x0001; ++ input_dev->id.version = 0x0100; ++ ++ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_PWR) | BIT(EV_SW); ++ input_dev->keycode = tosakbd->keycode; ++ input_dev->keycodesize = sizeof(unsigned char); ++ input_dev->keycodemax = ARRAY_SIZE(tosakbd_keycode); ++ ++ memcpy(tosakbd->keycode, tosakbd_keycode, sizeof(tosakbd->keycode)); ++ for (i = 0; i < ARRAY_SIZE(tosakbd_keycode); i++) ++ set_bit(tosakbd->keycode[i], input_dev->keybit); ++ clear_bit(0, input_dev->keybit); ++ set_bit(SW_HEADPHONE_INSERT, input_dev->swbit); ++ ++ input_register_device(tosakbd->input); ++ ++ /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */ ++ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) { ++ pxa_gpio_mode( TOSA_GPIO_KEY_SENSE(i) | GPIO_IN); ++ if (request_irq(TOSA_IRQ_GPIO_KEY_SENSE(i), tosakbd_interrupt, ++ IRQF_DISABLED | IRQF_TRIGGER_RISING, "tosakbd", tosakbd)) { ++ printk("tosakbd: Can't get IRQ: %d !\n", i); ++ } ++ } ++ ++ /* Set Strobe lines as outputs - set high */ ++ for (i = 0; i < TOSA_KEY_STROBE_NUM; i++) { ++ pxa_gpio_mode( TOSA_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH); ++ } ++ ++ // Power&Rec Button ++ pxa_gpio_mode( TOSA_GPIO_ON_KEY | GPIO_IN); ++ pxa_gpio_mode( TOSA_GPIO_RECORD_BTN | GPIO_IN); ++ pxa_gpio_mode( TOSA_GPIO_SYNC | GPIO_IN); ++ pxa_gpio_mode( TOSA_GPIO_EAR_IN | GPIO_IN); ++ ++ if (request_irq(TOSA_IRQ_GPIO_ON_KEY, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "On key", tosakbd) || ++ request_irq(TOSA_IRQ_GPIO_RECORD_BTN, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "Record key", tosakbd) || ++ request_irq(TOSA_IRQ_GPIO_SYNC, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "Sync key", tosakbd) || ++ request_irq(TOSA_IRQ_GPIO_EAR_IN, tosakbd_hp_isr, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "HP in", tosakbd)) { ++ printk("Could not allocate KEYBD IRQ!\n"); ++ } ++ ++ printk(KERN_INFO "input: Tosa Keyboard Registered\n"); ++ ++ return 0; ++} ++ ++static int tosakbd_remove(struct platform_device *dev) { ++ ++ int i; ++ struct tosakbd *tosakbd = platform_get_drvdata(dev); ++ ++ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) ++ free_irq(TOSA_IRQ_GPIO_KEY_SENSE(i),tosakbd); ++ ++ free_irq(TOSA_IRQ_GPIO_ON_KEY,tosakbd); ++ free_irq(TOSA_IRQ_GPIO_RECORD_BTN,tosakbd); ++ free_irq(TOSA_IRQ_GPIO_SYNC,tosakbd); ++ ++ del_timer_sync(&tosakbd->timer); ++ ++ input_unregister_device(tosakbd->input); ++ ++ kfree(tosakbd); ++ ++ return 0; ++} ++ ++static struct platform_driver tosakbd_driver = { ++ .probe = tosakbd_probe, ++ .remove = tosakbd_remove, ++ .suspend = tosakbd_suspend, ++ .resume = tosakbd_resume, ++ .driver = { ++ .name = "tosa-keyboard", ++ }, ++}; ++ ++static int __devinit tosakbd_init(void) ++{ ++ return platform_driver_register(&tosakbd_driver); ++} ++ ++static void __exit tosakbd_exit(void) ++{ ++ platform_driver_unregister(&tosakbd_driver); ++} ++ ++module_init(tosakbd_init); ++module_exit(tosakbd_exit); ++ ++MODULE_AUTHOR("Dirk Opfer <Dirk@Opfer-Online.de>"); ++MODULE_DESCRIPTION("Tosa Keyboard Driver"); ++MODULE_LICENSE("GPLv2"); + drivers/input/keyboard/Kconfig | 12 - + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/tosakbd.c | 467 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 479 insertions(+), 1 deletion(-) + +Index: git/drivers/input/keyboard/Kconfig +=================================================================== +--- git.orig/drivers/input/keyboard/Kconfig 2006-10-31 16:08:57.000000000 +0000 ++++ git/drivers/input/keyboard/Kconfig 2006-11-07 22:13:10.000000000 +0000 +@@ -148,12 +148,22 @@ config KEYBOARD_SPITZ + depends on PXA_SHARPSL + default y + help +- Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000, ++ Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000, + SL-C3000 and Sl-C3100 series of PDAs. + + To compile this driver as a module, choose M here: the + module will be called spitzkbd. + ++config KEYBOARD_TOSA ++ tristate "Tosa keyboard" ++ depends on PXA_SHARPSL ++ default y ++ help ++ Say Y here to enable the keyboard on the Sharp Zaurus SL-6000x (Tosa) ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tosakbd. ++ + config KEYBOARD_AMIGA + tristate "Amiga keyboard" + depends on AMIGA +Index: git/drivers/input/keyboard/Makefile +=================================================================== +--- git.orig/drivers/input/keyboard/Makefile 2006-10-31 16:08:57.000000000 +0000 ++++ git/drivers/input/keyboard/Makefile 2006-11-07 22:13:10.000000000 +0000 +@@ -17,3 +17,4 @@ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkb + obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o + obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o + obj-$(CONFIG_KEYBOARD_ASIC3) += asic3_keys.o ++obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o +Index: git/drivers/input/keyboard/tosakbd.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ git/drivers/input/keyboard/tosakbd.c 2006-11-07 23:27:19.000000000 +0000 +@@ -0,0 +1,467 @@ ++/* ++ * Keyboard driver for Sharp Tosa models (SL-6000x) ++ * ++ * Copyright (c) 2005 Dirk Opfer ++ * ++ * Based on xtkbd.c/locomkbd.c/corgikbd.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <linux/init.h> ++#include <linux/input.h> ++#include <linux/interrupt.h> ++#include <linux/jiffies.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++ ++#include <asm/arch/tosa.h> ++#include <asm/arch/hardware.h> ++#include <asm/arch/pxa-regs.h> ++ ++ ++#define TOSA_KEY_STROBE_NUM (11) ++#define TOSA_KEY_SENSE_NUM (7) ++ ++#define KEYMASK_ON (0x1<<0) ++#define KEYMASK_REC (0x1<<1) ++#define KEYMASK_SYNC (0x1<<2) ++ ++#define KB_ROWS 7 ++#define KB_COLS 11 ++#define KB_ROWMASK(r) (1 << (r)) ++#define SCANCODE(r,c) ( ((r)<<4) + (c) + 1 ) ++#define NR_SCANCODES (SCANCODE(KB_ROWS-1,KB_COLS)+1+1) ++ ++#define SCAN_INTERVAL (HZ/10) ++#define HP_SCAN_INTERVAL (150) /* ms */ ++#define HP_STABLE_COUNT 2 ++ ++#define TOSA_KEY_CALENDER KEY_F1 ++#define TOSA_KEY_ADDRESS KEY_F2 ++#define TOSA_KEY_FN KEY_F3 ++#define TOSA_KEY_CANCEL KEY_F4 ++#define TOSA_KEY_OFF KEY_SUSPEND ++#define TOSA_KEY_CENTER KEY_F5 ++#define TOSA_KEY_REC KEY_F6 ++#define TOSA_KEY_LIGHT KEY_F7 ++#define TOSA_KEY_RECORD KEY_F8 ++#define TOSA_KEY_HOME KEY_F9 ++#define TOSA_KEY_MAIL KEY_F10 ++#define TOSA_KEY_OK KEY_F11 ++#define TOSA_KEY_MENU KEY_F12 ++#define TOSA_KEY_SYNC KEY_F13 ++ ++#define GET_ROWS_STATUS(c) ((GPLR2 & TOSA_GPIO_ALL_SENSE_BIT) >> TOSA_GPIO_ALL_SENSE_RSHIFT) ++#define KB_DISCHARGE_DELAY 10 ++#define KB_ACTIVATE_DELAY 10 ++ ++ ++static unsigned char tosakbd_keycode[NR_SCANCODES] = { ++ 0, /* 0 */ ++ 0, KEY_W, 0, 0, 0, KEY_K, KEY_BACKSPACE, KEY_P, 0, 0, 0, TOSA_KEY_OFF, 0, 0, 0, 0, /*1 - 16*/ ++ KEY_Q, KEY_E, KEY_T, KEY_Y, 0, KEY_O, KEY_I, KEY_COMMA, 0, 0, 0, TOSA_KEY_RECORD, 0, 0, 0, 0, /*17 - 32*/ ++ KEY_A, KEY_D, KEY_G, KEY_U, 0, KEY_L, KEY_ENTER, KEY_DOT, 0, 0, 0, TOSA_KEY_SYNC, 0, 0, 0, 0, /*33 - 48*/ ++ KEY_Z, KEY_C, KEY_V, KEY_J, TOSA_KEY_ADDRESS, TOSA_KEY_CANCEL, TOSA_KEY_CENTER, TOSA_KEY_OK, KEY_LEFTSHIFT, 0 , 0,0 , 0, 0, 0, 0, /*49 - 64*/ ++ KEY_S, KEY_R, KEY_B, KEY_N, TOSA_KEY_CALENDER, TOSA_KEY_HOME, TOSA_KEY_REC, TOSA_KEY_LIGHT, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /*65 - 80*/ ++ KEY_TAB, KEY_SLASH, KEY_H, KEY_M, TOSA_KEY_MENU, 0, KEY_UP, 0, 0, 0, TOSA_KEY_FN, 0, 0, 0, 0, 0, /*81 - 96*/ ++ KEY_X, KEY_F, KEY_SPACE, KEY_APOSTROPHE, TOSA_KEY_MAIL, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, /*97 - 109*/ ++}; ++ ++struct tosakbd { ++ unsigned char keycode[ARRAY_SIZE(tosakbd_keycode)]; ++ struct input_dev *input; ++ ++ spinlock_t lock; ++ struct timer_list timer; ++ struct timer_list hptimer; ++ ++ int hp_state; ++ int hp_count; ++ ++ unsigned int suspended; ++ unsigned long suspend_jiffies; ++}; ++ ++/* Helper functions for reading the keyboard matrix ++ * Note: We should really be using pxa_gpio_mode to alter GPDR but it ++ * requires a function call per GPIO bit which is excessive ++ * when we need to access 12 bits at once, multiple times. ++ * These functions must be called within local_irq_save()/local_irq_restore() ++ * or similar. ++ */ ++static inline void tosakbd_discharge_all(void) ++{ ++ /* STROBE All HiZ */ ++ GPCR1 = TOSA_GPIO_HIGH_STROBE_BIT; ++ GPDR1 &= ~TOSA_GPIO_HIGH_STROBE_BIT; ++ GPCR2 = TOSA_GPIO_LOW_STROBE_BIT; ++ GPDR2 &= ~TOSA_GPIO_LOW_STROBE_BIT; ++} ++ ++static inline void tosakbd_activate_all(void) ++{ ++ /* STROBE ALL -> High */ ++ GPSR1 = TOSA_GPIO_HIGH_STROBE_BIT; ++ GPDR1 |= TOSA_GPIO_HIGH_STROBE_BIT; ++ GPSR2 = TOSA_GPIO_LOW_STROBE_BIT; ++ GPDR2 |= TOSA_GPIO_LOW_STROBE_BIT; ++ ++ udelay(KB_DISCHARGE_DELAY); ++ ++ /* STATE CLEAR */ ++ GEDR2 |= TOSA_GPIO_ALL_SENSE_BIT; ++} ++ ++static inline void tosakbd_activate_col(int col) ++{ ++ if (col<=5) { ++ /* STROBE col -> High, not col -> HiZ */ ++ GPSR1 = TOSA_GPIO_STROBE_BIT(col); ++ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); ++ } else { ++ /* STROBE col -> High, not col -> HiZ */ ++ GPSR2 = TOSA_GPIO_STROBE_BIT(col); ++ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); ++ } ++} ++ ++static inline void tosakbd_reset_col(int col) ++{ ++ if (col<=5) { ++ /* STROBE col -> Low */ ++ GPCR1 = TOSA_GPIO_STROBE_BIT(col); ++ /* STROBE col -> out, not col -> HiZ */ ++ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); ++ } else { ++ /* STROBE col -> Low */ ++ GPCR2 = TOSA_GPIO_STROBE_BIT(col); ++ /* STROBE col -> out, not col -> HiZ */ ++ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); ++ } ++} ++ ++/* ++ * Read the GPIOs for POWER, RECORD and SYNC ++ */ ++static int read_port_key_status_raw(void) ++{ ++ int val=0; ++ ++ /* Power key */ ++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_ON_KEY))==0) ++ val |= KEYMASK_ON; ++ /* Record key */ ++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_RECORD_BTN))==0) ++ val |= KEYMASK_REC; ++ /* Sync key */ ++ if ((GPLR0 & GPIO_bit(TOSA_GPIO_SYNC))==0) ++ val |= KEYMASK_SYNC; ++ return val; ++} ++ ++ ++/* ++ * The tosa keyboard only generates interrupts when a key is pressed. ++ * So when a key is pressed, we enable a timer. This timer scans the ++ * keyboard, and this is how we detect when the key is released. ++ */ ++ ++/* Scan the hardware keyboard and push any changes up through the input layer */ ++static void tosakbd_scankeyboard(struct tosakbd *tosakbd_data) ++{ ++ unsigned int row, col, rowd; ++ unsigned long flags; ++ unsigned int num_pressed = 0; ++ ++ if (tosakbd_data->suspended) ++ return; ++ ++ spin_lock_irqsave(&tosakbd_data->lock, flags); ++ ++ for (col = 0; col < KB_COLS; col++) { ++ /* ++ * Discharge the output driver capacitatance ++ * in the keyboard matrix. (Yes it is significant..) ++ */ ++ tosakbd_discharge_all(); ++ udelay(KB_DISCHARGE_DELAY); ++ ++ tosakbd_activate_col( col); ++ udelay(KB_ACTIVATE_DELAY); ++ ++ rowd = GET_ROWS_STATUS(col); ++ ++ for (row = 0; row < KB_ROWS; row++) { ++ unsigned int scancode, pressed; ++ scancode = SCANCODE(row, col); ++ pressed = rowd & KB_ROWMASK(row); ++ input_report_key(tosakbd_data->input, tosakbd_data->keycode[scancode], pressed); ++ if (pressed) ++ num_pressed++; ++ } ++ ++ tosakbd_reset_col(col); ++ } ++ ++ tosakbd_activate_all(); ++ ++ rowd = read_port_key_status_raw(); ++ ++ for (row = 0; row < 3; row++ ) { ++ unsigned int scancode, pressed; ++ scancode = SCANCODE(row, KB_COLS); ++ pressed = rowd & KB_ROWMASK(row); ++ input_report_key(tosakbd_data->input, tosakbd_data->keycode[scancode], pressed); ++ if (pressed) ++ num_pressed++; ++ ++ if (pressed && (tosakbd_data->keycode[scancode] == TOSA_KEY_OFF) ++ && time_after(jiffies, tosakbd_data->suspend_jiffies + msecs_to_jiffies(1000))) { ++ input_event(tosakbd_data->input, EV_PWR, TOSA_KEY_OFF, 1); ++ tosakbd_data->suspend_jiffies = jiffies; ++ } ++ } ++ ++ input_sync(tosakbd_data->input); ++ ++ /* if any keys are pressed, enable the timer */ ++ if (num_pressed) ++ mod_timer(&tosakbd_data->timer, jiffies + SCAN_INTERVAL); ++ ++ spin_unlock_irqrestore(&tosakbd_data->lock, flags); ++} ++ ++/* ++ * tosa keyboard interrupt handler. ++ */ ++static irqreturn_t tosakbd_interrupt(int irq, void *dev_id) ++{ ++ struct tosakbd *tosakbd_data = dev_id; ++ ++ if (!timer_pending(&tosakbd_data->timer)) ++ { ++ /** wait chattering delay **/ ++ udelay(20); ++ tosakbd_scankeyboard(tosakbd_data); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * tosa timer checking for released keys ++ */ ++static void tosakbd_timer_callback(unsigned long data) ++{ ++ struct tosakbd *tosakbd_data = (struct tosakbd *) data; ++ tosakbd_scankeyboard(tosakbd_data); ++} ++ ++/* ++ * The headphone generates an interrupt. ++ * We debounce the switche and pass them to the input system. ++ */ ++ ++static irqreturn_t tosakbd_hp_isr(int irq, void *dev_id) ++{ ++ struct tosakbd *tosakbd_data = dev_id; ++ ++ if (!timer_pending(&tosakbd_data->hptimer)) ++ mod_timer(&tosakbd_data->hptimer, jiffies + msecs_to_jiffies(HP_SCAN_INTERVAL)); ++ ++ return IRQ_HANDLED; ++} ++ ++static void tosakbd_hp_timer(unsigned long data) ++{ ++ struct tosakbd *tosakbd_data = (struct tosakbd *) data; ++ unsigned long state; ++ unsigned long flags; ++ ++ state = (GPLR(TOSA_GPIO_EAR_IN) & GPIO_bit(TOSA_GPIO_EAR_IN)); ++ if (state != tosakbd_data->hp_state) { ++ tosakbd_data->hp_count = 0; ++ tosakbd_data->hp_state = state; ++ } else if (tosakbd_data->hp_count < HP_STABLE_COUNT) { ++ tosakbd_data->hp_count++; ++ } ++ ++ if (tosakbd_data->hp_count >= HP_STABLE_COUNT) { ++ spin_lock_irqsave(&tosakbd_data->lock, flags); ++ ++ input_report_switch(tosakbd_data->input, SW_HEADPHONE_INSERT, ((GPLR(TOSA_GPIO_EAR_IN) & GPIO_bit(TOSA_GPIO_EAR_IN)) == 0)); ++ input_sync(tosakbd_data->input); ++ ++ spin_unlock_irqrestore(&tosakbd_data->lock, flags); ++ } else { ++ mod_timer(&tosakbd_data->hptimer, jiffies + msecs_to_jiffies(HP_SCAN_INTERVAL)); ++ } ++} ++ ++#ifdef CONFIG_PM ++static int tosakbd_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct tosakbd *tosakbd = platform_get_drvdata(dev); ++ ++ tosakbd->suspended = 1; ++ ++ return 0; ++} ++ ++static int tosakbd_resume(struct platform_device *dev) ++{ ++ struct tosakbd *tosakbd = platform_get_drvdata(dev); ++ ++ /* Upon resume, ignore the suspend key for a short while */ ++ tosakbd->suspend_jiffies = jiffies; ++ tosakbd->suspended = 0; ++ ++ return 0; ++} ++#else ++#define tosakbd_suspend NULL ++#define tosakbd_resume NULL ++#endif ++ ++static int __init tosakbd_probe(struct platform_device *pdev) { ++ ++ int i; ++ struct tosakbd *tosakbd; ++ struct input_dev *input_dev; ++ ++ tosakbd = kzalloc(sizeof(struct tosakbd), GFP_KERNEL); ++ if (!tosakbd) ++ return -ENOMEM; ++ ++ input_dev = input_allocate_device(); ++ if (!input_dev) { ++ kfree(tosakbd); ++ return -ENOMEM; ++ } ++ ++ platform_set_drvdata(pdev,tosakbd); ++ ++ spin_lock_init(&tosakbd->lock); ++ ++ /* Init Keyboard rescan timer */ ++ init_timer(&tosakbd->timer); ++ tosakbd->timer.function = tosakbd_timer_callback; ++ tosakbd->timer.data = (unsigned long) tosakbd; ++ ++ /* Init Headphone Timer */ ++ init_timer(&tosakbd->hptimer); ++ tosakbd->hptimer.function = tosakbd_hp_timer; ++ tosakbd->hptimer.data = (unsigned long) tosakbd; ++ ++ tosakbd->suspend_jiffies = jiffies; ++ ++ tosakbd->input = input_dev; ++ ++ input_dev->private = tosakbd; ++ input_dev->name = "Tosa Keyboard"; ++ input_dev->phys = "tosakbd/input0"; ++ input_dev->cdev.dev = &pdev->dev; ++ ++ input_dev->id.bustype = BUS_HOST; ++ input_dev->id.vendor = 0x0001; ++ input_dev->id.product = 0x0001; ++ input_dev->id.version = 0x0100; ++ ++ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_PWR) | BIT(EV_SW); ++ input_dev->keycode = tosakbd->keycode; ++ input_dev->keycodesize = sizeof(unsigned char); ++ input_dev->keycodemax = ARRAY_SIZE(tosakbd_keycode); ++ ++ memcpy(tosakbd->keycode, tosakbd_keycode, sizeof(tosakbd->keycode)); ++ for (i = 0; i < ARRAY_SIZE(tosakbd_keycode); i++) ++ set_bit(tosakbd->keycode[i], input_dev->keybit); ++ clear_bit(0, input_dev->keybit); ++ set_bit(SW_HEADPHONE_INSERT, input_dev->swbit); ++ ++ input_register_device(tosakbd->input); ++ ++ /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */ ++ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) { ++ pxa_gpio_mode( TOSA_GPIO_KEY_SENSE(i) | GPIO_IN); ++ if (request_irq(TOSA_IRQ_GPIO_KEY_SENSE(i), tosakbd_interrupt, ++ IRQF_DISABLED | IRQF_TRIGGER_RISING, "tosakbd", tosakbd)) { ++ printk("tosakbd: Can't get IRQ: %d !\n", i); ++ } ++ } ++ ++ /* Set Strobe lines as outputs - set high */ ++ for (i = 0; i < TOSA_KEY_STROBE_NUM; i++) { ++ pxa_gpio_mode( TOSA_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH); ++ } ++ ++ // Power&Rec Button ++ pxa_gpio_mode( TOSA_GPIO_ON_KEY | GPIO_IN); ++ pxa_gpio_mode( TOSA_GPIO_RECORD_BTN | GPIO_IN); ++ pxa_gpio_mode( TOSA_GPIO_SYNC | GPIO_IN); ++ pxa_gpio_mode( TOSA_GPIO_EAR_IN | GPIO_IN); ++ ++ if (request_irq(TOSA_IRQ_GPIO_ON_KEY, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "On key", tosakbd) || ++ request_irq(TOSA_IRQ_GPIO_RECORD_BTN, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "Record key", tosakbd) || ++ request_irq(TOSA_IRQ_GPIO_SYNC, tosakbd_interrupt, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "Sync key", tosakbd) || ++ request_irq(TOSA_IRQ_GPIO_EAR_IN, tosakbd_hp_isr, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "HP in", tosakbd)) { ++ printk("Could not allocate KEYBD IRQ!\n"); ++ } ++ ++ printk(KERN_INFO "input: Tosa Keyboard Registered\n"); ++ ++ return 0; ++} ++ ++static int tosakbd_remove(struct platform_device *dev) { ++ ++ int i; ++ struct tosakbd *tosakbd = platform_get_drvdata(dev); ++ ++ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) ++ free_irq(TOSA_IRQ_GPIO_KEY_SENSE(i),tosakbd); ++ ++ free_irq(TOSA_IRQ_GPIO_ON_KEY,tosakbd); ++ free_irq(TOSA_IRQ_GPIO_RECORD_BTN,tosakbd); ++ free_irq(TOSA_IRQ_GPIO_SYNC,tosakbd); ++ ++ del_timer_sync(&tosakbd->timer); ++ ++ input_unregister_device(tosakbd->input); ++ ++ kfree(tosakbd); ++ ++ return 0; ++} ++ ++static struct platform_driver tosakbd_driver = { ++ .probe = tosakbd_probe, ++ .remove = tosakbd_remove, ++ .suspend = tosakbd_suspend, ++ .resume = tosakbd_resume, ++ .driver = { ++ .name = "tosa-keyboard", ++ }, ++}; ++ ++static int __devinit tosakbd_init(void) ++{ ++ return platform_driver_register(&tosakbd_driver); ++} ++ ++static void __exit tosakbd_exit(void) ++{ ++ platform_driver_unregister(&tosakbd_driver); ++} ++ ++module_init(tosakbd_init); ++module_exit(tosakbd_exit); ++ ++MODULE_AUTHOR("Dirk Opfer <Dirk@Opfer-Online.de>"); ++MODULE_DESCRIPTION("Tosa Keyboard Driver"); ++MODULE_LICENSE("GPLv2"); diff --git a/packages/linux/linux-rp-2.6.23/tosa-lcdnoise-r1-fix-r0.patch b/packages/linux/linux-rp-2.6.23/tosa-lcdnoise-r1-fix-r0.patch new file mode 100644 index 0000000000..02dd6a74eb --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/tosa-lcdnoise-r1-fix-r0.patch @@ -0,0 +1,270 @@ +From eada869814636157956641ba1503f0d6cc04e2b7 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 17:43:51 +0400 +Subject: [PATCH] tosa-lcdnoise-r1.patch fixes + +--- + arch/arm/mach-pxa/tosa_lcd.c | 34 ++++++++++++++++++++++++++++++++++ + drivers/input/touchscreen/tosa_ts.c | 9 +-------- + include/asm-arm/arch-pxa/tosa.h | 5 +++++ + 3 files changed, 40 insertions(+), 8 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa_lcd.c b/arch/arm/mach-pxa/tosa_lcd.c +index d52f63f..447ca86 100644 +--- a/arch/arm/mach-pxa/tosa_lcd.c ++++ b/arch/arm/mach-pxa/tosa_lcd.c +@@ -59,6 +59,8 @@ static int bl_intensity; + static struct ssp_dev tosa_nssp_dev; + static struct ssp_state tosa_nssp_state; + static spinlock_t tosa_nssp_lock; ++static int blanked; ++static unsigned long hsync_time; + + static unsigned short normal_i2c[] = { + DAC_BASE, +@@ -130,6 +132,17 @@ static void tosa_lcd_tg_init(struct device *dev) + pxa_nssp_output(TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */ + } + ++static unsigned long calc_hsync_time(const struct fb_videomode *mode) { ++ /* The 25 and 44 'magic numbers' are from Sharp's 2.4 patches */ ++ if (mode->yres == 640) { ++ return 25; ++ } ++ if (mode->yres == 320) { ++ return 44; ++ } ++ return 0; ++} ++ + static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode) + { + const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR; +@@ -154,6 +167,8 @@ static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode) + /* set common voltage */ + i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj); + ++ blanked = 0; ++ hsync_time = calc_hsync_time(mode); + } + + static void tosa_lcd_tg_off(struct device *dev) +@@ -172,6 +187,8 @@ static void tosa_lcd_tg_off(struct device *dev) + + /* L3V Off */ + reset_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON); ++ ++ blanked = 1; + } + + static int tosa_detect_client(struct i2c_adapter* adapter, int address, int kind) { +@@ -238,6 +255,23 @@ static int tosa_detach_client(struct i2c_client* client) { + return 0; + } + ++unsigned long tosa_lcd_get_hsync_time(void) ++{ ++/* This method should eventually contain the correct algorithm for calculating ++ the hsync_time */ ++ if (blanked) ++ return 0; ++ else ++ return hsync_time; ++} ++ ++void tosa_lcd_wait_hsync(void) ++{ ++ /* Waits for a rising edge on the VGA line */ ++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0); ++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0); ++} ++ + static struct i2c_driver tosa_driver={ + .id = TOSA_LCD_I2C_DEVICEID, + .attach_adapter = tosa_attach_adapter, +diff --git a/drivers/input/touchscreen/tosa_ts.c b/drivers/input/touchscreen/tosa_ts.c +index bc733e9..134f8ce 100644 +--- a/drivers/input/touchscreen/tosa_ts.c ++++ b/drivers/input/touchscreen/tosa_ts.c +@@ -25,13 +25,6 @@ + #define CCNT_ON() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1)) + #define CCNT_OFF() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1)) + +-static inline void tosa_lcd_wait_hsync(void) +-{ +- /* Waits for a rising edge on the VGA line */ +- while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0); +- while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0); +-} +- + /* On the Sharp SL-6000 (Tosa), due to a noisy LCD, we need to perform a wait + * before sampling the Y axis of the touchscreen */ + void tosa_lcd_sync_on(int adcsel) { +@@ -54,7 +47,7 @@ void tosa_lcd_sync_on(int adcsel) { + } + } + +-void tosa_lcd_sync_off(void) { ++void tosa_lcd_sync_off(int adcsel) { + CCNT_OFF(); + } + +diff --git a/include/asm-arm/arch-pxa/tosa.h b/include/asm-arm/arch-pxa/tosa.h +index ce7322d..7f446fd 100644 +--- a/include/asm-arm/arch-pxa/tosa.h ++++ b/include/asm-arm/arch-pxa/tosa.h +@@ -1,6 +1,7 @@ + /* + * Hardware specific definitions for Sharp SL-C6000x series of PDAs + * ++ * Copyright (c) 2006 Wolfson Microelectronics PLC. + * Copyright (c) 2005 Dirk Opfer + * + * Based on Sharp's 2.4 kernel patches +@@ -187,4 +188,8 @@ + extern struct platform_device tosascoop_jc_device; + extern struct platform_device tosascoop_device; + extern struct platform_device tc6393_device; ++ ++unsigned long tosa_lcd_get_hsync_time(void); ++void tosa_lcd_wait_hsync(void); ++ + #endif /* _ASM_ARCH_TOSA_H_ */ +-- +1.4.4.4 + +From eada869814636157956641ba1503f0d6cc04e2b7 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 17:43:51 +0400 +Subject: [PATCH] tosa-lcdnoise-r1.patch fixes + +--- + arch/arm/mach-pxa/tosa_lcd.c | 34 ++++++++++++++++++++++++++++++++++ + drivers/input/touchscreen/tosa_ts.c | 9 +-------- + include/asm-arm/arch-pxa/tosa.h | 5 +++++ + 3 files changed, 40 insertions(+), 8 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa_lcd.c b/arch/arm/mach-pxa/tosa_lcd.c +index d52f63f..447ca86 100644 +--- a/arch/arm/mach-pxa/tosa_lcd.c ++++ b/arch/arm/mach-pxa/tosa_lcd.c +@@ -59,6 +59,8 @@ static int bl_intensity; + static struct ssp_dev tosa_nssp_dev; + static struct ssp_state tosa_nssp_state; + static spinlock_t tosa_nssp_lock; ++static int blanked; ++static unsigned long hsync_time; + + static unsigned short normal_i2c[] = { + DAC_BASE, +@@ -130,6 +132,17 @@ static void tosa_lcd_tg_init(struct device *dev) + pxa_nssp_output(TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */ + } + ++static unsigned long calc_hsync_time(const struct fb_videomode *mode) { ++ /* The 25 and 44 'magic numbers' are from Sharp's 2.4 patches */ ++ if (mode->yres == 640) { ++ return 25; ++ } ++ if (mode->yres == 320) { ++ return 44; ++ } ++ return 0; ++} ++ + static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode) + { + const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR; +@@ -154,6 +167,8 @@ static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode) + /* set common voltage */ + i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj); + ++ blanked = 0; ++ hsync_time = calc_hsync_time(mode); + } + + static void tosa_lcd_tg_off(struct device *dev) +@@ -172,6 +187,8 @@ static void tosa_lcd_tg_off(struct device *dev) + + /* L3V Off */ + reset_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON); ++ ++ blanked = 1; + } + + static int tosa_detect_client(struct i2c_adapter* adapter, int address, int kind) { +@@ -238,6 +255,23 @@ static int tosa_detach_client(struct i2c_client* client) { + return 0; + } + ++unsigned long tosa_lcd_get_hsync_time(void) ++{ ++/* This method should eventually contain the correct algorithm for calculating ++ the hsync_time */ ++ if (blanked) ++ return 0; ++ else ++ return hsync_time; ++} ++ ++void tosa_lcd_wait_hsync(void) ++{ ++ /* Waits for a rising edge on the VGA line */ ++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0); ++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0); ++} ++ + static struct i2c_driver tosa_driver={ + .id = TOSA_LCD_I2C_DEVICEID, + .attach_adapter = tosa_attach_adapter, +diff --git a/drivers/input/touchscreen/tosa_ts.c b/drivers/input/touchscreen/tosa_ts.c +index bc733e9..134f8ce 100644 +--- a/drivers/input/touchscreen/tosa_ts.c ++++ b/drivers/input/touchscreen/tosa_ts.c +@@ -25,13 +25,6 @@ + #define CCNT_ON() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1)) + #define CCNT_OFF() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1)) + +-static inline void tosa_lcd_wait_hsync(void) +-{ +- /* Waits for a rising edge on the VGA line */ +- while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0); +- while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0); +-} +- + /* On the Sharp SL-6000 (Tosa), due to a noisy LCD, we need to perform a wait + * before sampling the Y axis of the touchscreen */ + void tosa_lcd_sync_on(int adcsel) { +@@ -54,7 +47,7 @@ void tosa_lcd_sync_on(int adcsel) { + } + } + +-void tosa_lcd_sync_off(void) { ++void tosa_lcd_sync_off(int adcsel) { + CCNT_OFF(); + } + +diff --git a/include/asm-arm/arch-pxa/tosa.h b/include/asm-arm/arch-pxa/tosa.h +index ce7322d..7f446fd 100644 +--- a/include/asm-arm/arch-pxa/tosa.h ++++ b/include/asm-arm/arch-pxa/tosa.h +@@ -1,6 +1,7 @@ + /* + * Hardware specific definitions for Sharp SL-C6000x series of PDAs + * ++ * Copyright (c) 2006 Wolfson Microelectronics PLC. + * Copyright (c) 2005 Dirk Opfer + * + * Based on Sharp's 2.4 kernel patches +@@ -187,4 +188,8 @@ + extern struct platform_device tosascoop_jc_device; + extern struct platform_device tosascoop_device; + extern struct platform_device tc6393_device; ++ ++unsigned long tosa_lcd_get_hsync_time(void); ++void tosa_lcd_wait_hsync(void); ++ + #endif /* _ASM_ARCH_TOSA_H_ */ +-- +1.4.4.4 + diff --git a/packages/linux/linux-rp-2.6.23/tosa-lcdnoise-r1.patch b/packages/linux/linux-rp-2.6.23/tosa-lcdnoise-r1.patch new file mode 100644 index 0000000000..910b727b82 --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/tosa-lcdnoise-r1.patch @@ -0,0 +1,316 @@ +From 564b757ba44b517ac6d693b94a177708bb5d3887 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 17:30:30 +0400 +Subject: [PATCH] tosa-lcdnoise-r1.patch + +--- + drivers/input/touchscreen/Kconfig | 13 +++++ + drivers/input/touchscreen/Makefile | 1 + + drivers/input/touchscreen/tosa_ts.c | 102 +++++++++++++++++++++++++++++++++++ + 3 files changed, 116 insertions(+), 0 deletions(-) + +diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig +index 3ac01b4..6862e8f 100644 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -219,6 +219,19 @@ config TOUCHSCREEN_USB_DMC_TSC10 + bool "DMC TSC-10/25 device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + ++config TOUCHSCREEN_TOSA ++ tristate "Sharp Tosa touchscreen driver" ++ depends on TOUCHSCREEN_WM97XX && MACH_TOSA ++ default n ++ help ++ Say Y here to enable the driver for the touchscreen on the ++ Sharp Tosa PDA. ++ depends on TOUCHSCREEN_WM97XX && MACH_TOSA ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tosa_ts. ++ + config TOUCHSCREEN_TSC2101 + tristate "TI TSC2101 touchscreen input driver" + depends on MACH_HX2750 && INPUT && INPUT_TOUCHSCREEN +diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile +index f64d1a5..4fc0e17 100644 +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -22,6 +22,7 @@ obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o + obj-$(CONFIG_TOUCHSCREEN_TSC2101) += tsc2101_ts.o + obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o + obj-$(CONFIG_TOUCHSCREEN_WM97XX_PXA) += pxa-wm97xx.o ++obj-$(CONFIG_TOUCHSCREEN_TOSA) += tosa_ts.o + + ifeq ($(CONFIG_TOUCHSCREEN_WM9713),y) + wm97xx-ts-objs += wm9713.o +diff --git a/drivers/input/touchscreen/tosa_ts.c b/drivers/input/touchscreen/tosa_ts.c +new file mode 100644 +index 0000000..bc733e9 +--- /dev/null ++++ b/drivers/input/touchscreen/tosa_ts.c +@@ -0,0 +1,102 @@ ++/* ++ * tosa_ts.c -- Touchscreen driver for Sharp SL-6000 (Tosa). ++ * ++ * Copyright 2006 Wolfson Microelectronics PLC. ++ * Author: Mike Arthur ++ * linux@wolfsonmicro.com ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Revision history ++ * 1st Sep 2006 Initial version. ++ * ++ */ ++ ++#include <linux/wm97xx.h> ++#include <asm/arch/tosa.h> ++#include <asm/arch/hardware.h> ++#include <asm/arch/pxa-regs.h> ++ ++/* Taken from the Sharp 2.4 kernel code */ ++#define CCNT(a) asm volatile ("mrc p14, 0, %0, C1, C1, 0" : "=r"(a)) ++#define CCNT_ON() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1)) ++#define CCNT_OFF() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1)) ++ ++static inline void tosa_lcd_wait_hsync(void) ++{ ++ /* Waits for a rising edge on the VGA line */ ++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0); ++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0); ++} ++ ++/* On the Sharp SL-6000 (Tosa), due to a noisy LCD, we need to perform a wait ++ * before sampling the Y axis of the touchscreen */ ++void tosa_lcd_sync_on(int adcsel) { ++ unsigned long timer1 = 0, timer2 = 0, wait_time = 0; ++ if (adcsel == WM97XX_ADCSEL_Y) { ++ wait_time = tosa_lcd_get_hsync_time(); ++ CCNT_ON(); ++ ++ if (wait_time) { ++ /* wait for LCD rising edge */ ++ tosa_lcd_wait_hsync(); ++ /* get clock */ ++ CCNT(timer1); ++ CCNT(timer2); ++ ++ while ((timer2 - timer1) < wait_time) { ++ CCNT(timer2); ++ } ++ } ++ } ++} ++ ++void tosa_lcd_sync_off(void) { ++ CCNT_OFF(); ++} ++ ++static struct wm97xx_mach_ops tosa_mach_ops = { ++ .pre_sample = tosa_lcd_sync_on, ++ .post_sample = tosa_lcd_sync_off, ++}; ++ ++int tosa_ts_probe(struct device *dev) { ++ struct wm97xx *wm = dev->driver_data; ++ return wm97xx_register_mach_ops (wm, &tosa_mach_ops); ++} ++ ++ ++int tosa_ts_remove(struct device *dev) { ++ struct wm97xx *wm = dev->driver_data; ++ wm97xx_unregister_mach_ops (wm); ++ return 0; ++} ++ ++static struct device_driver tosa_ts_driver = { ++ .name = "wm97xx-touchscreen", ++ .bus = &wm97xx_bus_type, ++ .owner = THIS_MODULE, ++ .probe = tosa_ts_probe, ++ .remove = tosa_ts_remove, ++}; ++ ++static int __init tosa_ts_init(void) ++{ ++ return driver_register(&tosa_ts_driver); ++} ++ ++static void __exit tosa_ts_exit(void) ++{ ++ driver_unregister(&tosa_ts_driver); ++} ++ ++module_init(tosa_ts_init); ++module_exit(tosa_ts_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Mike Arthur, mike@mikearthur.co.uk, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("Sharp SL6000 Tosa Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +-- +1.4.4.4 + +From 564b757ba44b517ac6d693b94a177708bb5d3887 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 17:30:30 +0400 +Subject: [PATCH] tosa-lcdnoise-r1.patch + +--- + drivers/input/touchscreen/Kconfig | 13 +++++ + drivers/input/touchscreen/Makefile | 1 + + drivers/input/touchscreen/tosa_ts.c | 102 +++++++++++++++++++++++++++++++++++ + 3 files changed, 116 insertions(+), 0 deletions(-) + +diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig +index 3ac01b4..6862e8f 100644 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -219,6 +219,19 @@ config TOUCHSCREEN_USB_DMC_TSC10 + bool "DMC TSC-10/25 device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + ++config TOUCHSCREEN_TOSA ++ tristate "Sharp Tosa touchscreen driver" ++ depends on TOUCHSCREEN_WM97XX && MACH_TOSA ++ default n ++ help ++ Say Y here to enable the driver for the touchscreen on the ++ Sharp Tosa PDA. ++ depends on TOUCHSCREEN_WM97XX && MACH_TOSA ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tosa_ts. ++ + config TOUCHSCREEN_TSC2101 + tristate "TI TSC2101 touchscreen input driver" + depends on MACH_HX2750 && INPUT && INPUT_TOUCHSCREEN +diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile +index f64d1a5..4fc0e17 100644 +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -22,6 +22,7 @@ obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o + obj-$(CONFIG_TOUCHSCREEN_TSC2101) += tsc2101_ts.o + obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o + obj-$(CONFIG_TOUCHSCREEN_WM97XX_PXA) += pxa-wm97xx.o ++obj-$(CONFIG_TOUCHSCREEN_TOSA) += tosa_ts.o + + ifeq ($(CONFIG_TOUCHSCREEN_WM9713),y) + wm97xx-ts-objs += wm9713.o +diff --git a/drivers/input/touchscreen/tosa_ts.c b/drivers/input/touchscreen/tosa_ts.c +new file mode 100644 +index 0000000..bc733e9 +--- /dev/null ++++ b/drivers/input/touchscreen/tosa_ts.c +@@ -0,0 +1,102 @@ ++/* ++ * tosa_ts.c -- Touchscreen driver for Sharp SL-6000 (Tosa). ++ * ++ * Copyright 2006 Wolfson Microelectronics PLC. ++ * Author: Mike Arthur ++ * linux@wolfsonmicro.com ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Revision history ++ * 1st Sep 2006 Initial version. ++ * ++ */ ++ ++#include <linux/wm97xx.h> ++#include <asm/arch/tosa.h> ++#include <asm/arch/hardware.h> ++#include <asm/arch/pxa-regs.h> ++ ++/* Taken from the Sharp 2.4 kernel code */ ++#define CCNT(a) asm volatile ("mrc p14, 0, %0, C1, C1, 0" : "=r"(a)) ++#define CCNT_ON() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1)) ++#define CCNT_OFF() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1)) ++ ++static inline void tosa_lcd_wait_hsync(void) ++{ ++ /* Waits for a rising edge on the VGA line */ ++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0); ++ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0); ++} ++ ++/* On the Sharp SL-6000 (Tosa), due to a noisy LCD, we need to perform a wait ++ * before sampling the Y axis of the touchscreen */ ++void tosa_lcd_sync_on(int adcsel) { ++ unsigned long timer1 = 0, timer2 = 0, wait_time = 0; ++ if (adcsel == WM97XX_ADCSEL_Y) { ++ wait_time = tosa_lcd_get_hsync_time(); ++ CCNT_ON(); ++ ++ if (wait_time) { ++ /* wait for LCD rising edge */ ++ tosa_lcd_wait_hsync(); ++ /* get clock */ ++ CCNT(timer1); ++ CCNT(timer2); ++ ++ while ((timer2 - timer1) < wait_time) { ++ CCNT(timer2); ++ } ++ } ++ } ++} ++ ++void tosa_lcd_sync_off(void) { ++ CCNT_OFF(); ++} ++ ++static struct wm97xx_mach_ops tosa_mach_ops = { ++ .pre_sample = tosa_lcd_sync_on, ++ .post_sample = tosa_lcd_sync_off, ++}; ++ ++int tosa_ts_probe(struct device *dev) { ++ struct wm97xx *wm = dev->driver_data; ++ return wm97xx_register_mach_ops (wm, &tosa_mach_ops); ++} ++ ++ ++int tosa_ts_remove(struct device *dev) { ++ struct wm97xx *wm = dev->driver_data; ++ wm97xx_unregister_mach_ops (wm); ++ return 0; ++} ++ ++static struct device_driver tosa_ts_driver = { ++ .name = "wm97xx-touchscreen", ++ .bus = &wm97xx_bus_type, ++ .owner = THIS_MODULE, ++ .probe = tosa_ts_probe, ++ .remove = tosa_ts_remove, ++}; ++ ++static int __init tosa_ts_init(void) ++{ ++ return driver_register(&tosa_ts_driver); ++} ++ ++static void __exit tosa_ts_exit(void) ++{ ++ driver_unregister(&tosa_ts_driver); ++} ++ ++module_init(tosa_ts_init); ++module_exit(tosa_ts_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Mike Arthur, mike@mikearthur.co.uk, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("Sharp SL6000 Tosa Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +-- +1.4.4.4 + diff --git a/packages/linux/linux-rp-2.6.23/tosa-power-r18-fix-r0.patch b/packages/linux/linux-rp-2.6.23/tosa-power-r18-fix-r0.patch new file mode 100644 index 0000000000..fdfbea8ee0 --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/tosa-power-r18-fix-r0.patch @@ -0,0 +1,118 @@ +From 24813da9b0aac0e92635d7307837d89a9f4a1ee7 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 16:47:15 +0400 +Subject: [PATCH] tosa-power-r18.patch fixes + +--- + arch/arm/mach-pxa/tosa_pm.c | 9 +++++---- + 1 files changed, 5 insertions(+), 4 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa_pm.c b/arch/arm/mach-pxa/tosa_pm.c +index 1eab1af..2df75f0 100644 +--- a/arch/arm/mach-pxa/tosa_pm.c ++++ b/arch/arm/mach-pxa/tosa_pm.c +@@ -17,9 +17,9 @@ + #include <linux/interrupt.h> + #include <linux/platform_device.h> + #include <linux/pm.h> ++#include <linux/apm-emulation.h> + #include <linux/wm97xx.h> + +-#include <asm/apm.h> + #include <asm/irq.h> + #include <asm/mach-types.h> + #include <asm/hardware.h> +@@ -144,7 +144,7 @@ static int tosa_ac97_init(void) + pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); + pxa_gpio_mode(GPIO20_DREQ0_MD); + +- pxa_set_cken(CKEN2_AC97, 1); ++ pxa_set_cken(CKEN_AC97, 1); + /* AC97 power on sequense */ + while ( 1 ) { + GCR = 0; +@@ -184,11 +184,12 @@ static int tosa_ac97_init(void) + pxa_gpio_mode(GPIO32_SDATA_IN1_AC97_MD); + ad_polling = 1; + printk("tosa_ac97_init\n"); ++ return 0; + } + + void tosa_ac97_exit(void) + { +- if (!(CKEN & CKEN2_AC97)) ++ if (!(CKEN & CKEN_AC97)) + return; + + // power down the whole chip +@@ -197,7 +198,7 @@ void tosa_ac97_exit(void) + // GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN); + // GSR = GSR; + // GCR = GCR_ACLINK_OFF; +- pxa_set_cken(CKEN2_AC97, 0); ++ pxa_set_cken(CKEN_AC97, 0); + /* switch back to irq driver */ + ad_polling = 0; + printk("tosa_ac97_exit\n"); +-- +1.4.4.4 + +From 24813da9b0aac0e92635d7307837d89a9f4a1ee7 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 16:47:15 +0400 +Subject: [PATCH] tosa-power-r18.patch fixes + +--- + arch/arm/mach-pxa/tosa_pm.c | 9 +++++---- + 1 files changed, 5 insertions(+), 4 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa_pm.c b/arch/arm/mach-pxa/tosa_pm.c +index 1eab1af..2df75f0 100644 +--- a/arch/arm/mach-pxa/tosa_pm.c ++++ b/arch/arm/mach-pxa/tosa_pm.c +@@ -17,9 +17,9 @@ + #include <linux/interrupt.h> + #include <linux/platform_device.h> + #include <linux/pm.h> ++#include <linux/apm-emulation.h> + #include <linux/wm97xx.h> + +-#include <asm/apm.h> + #include <asm/irq.h> + #include <asm/mach-types.h> + #include <asm/hardware.h> +@@ -144,7 +144,7 @@ static int tosa_ac97_init(void) + pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); + pxa_gpio_mode(GPIO20_DREQ0_MD); + +- pxa_set_cken(CKEN2_AC97, 1); ++ pxa_set_cken(CKEN_AC97, 1); + /* AC97 power on sequense */ + while ( 1 ) { + GCR = 0; +@@ -184,11 +184,12 @@ static int tosa_ac97_init(void) + pxa_gpio_mode(GPIO32_SDATA_IN1_AC97_MD); + ad_polling = 1; + printk("tosa_ac97_init\n"); ++ return 0; + } + + void tosa_ac97_exit(void) + { +- if (!(CKEN & CKEN2_AC97)) ++ if (!(CKEN & CKEN_AC97)) + return; + + // power down the whole chip +@@ -197,7 +198,7 @@ void tosa_ac97_exit(void) + // GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN); + // GSR = GSR; + // GCR = GCR_ACLINK_OFF; +- pxa_set_cken(CKEN2_AC97, 0); ++ pxa_set_cken(CKEN_AC97, 0); + /* switch back to irq driver */ + ad_polling = 0; + printk("tosa_ac97_exit\n"); +-- +1.4.4.4 + diff --git a/packages/linux/linux-rp-2.6.23/tosa-power-r18.patch b/packages/linux/linux-rp-2.6.23/tosa-power-r18.patch new file mode 100644 index 0000000000..a099717749 --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/tosa-power-r18.patch @@ -0,0 +1,1382 @@ +Index: linux-2.6.17/arch/arm/mach-pxa/Makefile +=================================================================== +--- linux-2.6.17.orig/arch/arm/mach-pxa/Makefile 2006-09-19 20:51:33.984424500 +0200 ++++ linux-2.6.17/arch/arm/mach-pxa/Makefile 2006-09-19 21:08:04.922354250 +0200 +@@ -16,7 +16,7 @@ obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o + obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o + obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o + obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o +-obj-$(CONFIG_MACH_TOSA) += tosa.o ++obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o + obj-$(CONFIG_MACH_EM_X270) += em-x270.o + obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o + +Index: linux-2.6.17/arch/arm/mach-pxa/tosa_pm.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/arch/arm/mach-pxa/tosa_pm.c 2006-09-19 21:08:34.476201250 +0200 +@@ -0,0 +1,661 @@ ++/* ++ * Battery and Power Management code for the Sharp SL-6000x ++ * ++ * Copyright (c) 2005 Dirk Opfer ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/stat.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/interrupt.h> ++#include <linux/platform_device.h> ++#include <linux/pm.h> ++#include <linux/wm97xx.h> ++ ++#include <asm/apm.h> ++#include <asm/irq.h> ++#include <asm/mach-types.h> ++#include <asm/hardware.h> ++#include <asm/hardware/scoop.h> ++#include <asm/hardware/tmio.h> ++ ++#include <asm/arch/sharpsl.h> ++#include <asm/arch/tosa.h> ++#include <asm/arch/pxa-regs.h> ++#include <sound/soc.h> ++#include <sound/ac97_codec.h> ++#include "sharpsl.h" ++ ++extern int tosa_bl_intensity(void); ++volatile static int ad_polling; ++static int tosa_pm_driver_probe(struct device *dev); ++static int tosa_pm_driver_remove(struct device *dev); ++static struct wm97xx *wm9712; ++ ++/************************************************************ ++ * AC97 functions ++ ************************************************************/ ++#define AC97_TIMEOUT_VAL 0x1000000 ++ ++#define AC97_MISC_MODEM_STAT 0x0056 ++#define AC97_GPIO_CONFIG 0x004C ++ ++static u16 tosa_ac97_read(unsigned short reg) ++{ ++ volatile u32 *reg_addr; ++ volatile int timeout; ++ unsigned short data; ++ ++ if (CAR & CAR_CAIP) { ++ printk(KERN_CRIT ": CAR_CAIP already set\n"); ++ return 0; ++ } ++ ++ if (reg == AC97_GPIO_STATUS) ++ reg_addr = &PMC_REG_BASE; ++ else ++ reg_addr = &PAC_REG_BASE; ++ ++ reg_addr += (reg >> 1); ++ ++ data=0; ++ GSR = GSR_CDONE | GSR_SDONE; ++ ++ data = *reg_addr; ++ timeout = 0; ++ ++ while(((GSR & GSR_SDONE)) == 0 && (timeout++ < AC97_TIMEOUT_VAL)); ++ ++ if ((timeout >= AC97_TIMEOUT_VAL)) { ++ GSR = GSR; ++ printk(KERN_CRIT ": AC97 is busy1.\n"); ++ return data; ++ } ++ ++ // actual read ++ GSR = GSR_CDONE | GSR_SDONE; ++ data = *reg_addr; ++ ++ timeout = 0; ++ while(((GSR & GSR_SDONE) == 0) && (timeout++<AC97_TIMEOUT_VAL)); ++ if ((timeout >= AC97_TIMEOUT_VAL)) { ++ GSR = GSR; ++ printk(KERN_CRIT ": AC97 is busy2.\n"); ++ return data; ++ } ++ ++ return data; ++} ++ ++static void tosa_ac97_write(unsigned short reg, unsigned short val) ++{ ++ volatile u32 *reg_addr; ++ volatile int timeout=0; ++ ++ if (CAR & CAR_CAIP) { ++ printk(KERN_CRIT ": CAR_CAIP already set\n"); ++ return; ++ } ++ ++ GSR = GSR_CDONE | GSR_SDONE; ++ if (reg == AC97_GPIO_STATUS) ++ reg_addr = &PMC_REG_BASE; ++ else ++ reg_addr = &PAC_REG_BASE; ++ ++ reg_addr += (reg >> 1); ++ ++ *reg_addr = val; ++ while(((GSR & GSR_CDONE) == 0) && (timeout++ < AC97_TIMEOUT_VAL)); ++ if (timeout >= AC97_TIMEOUT_VAL) { ++ printk(KERN_CRIT ": AC97 is busy.\n"); ++ } ++} ++ ++static void tosa_ac97_bit_clear(u8 reg, u16 val) ++{ ++ unsigned short dat = tosa_ac97_read(reg); ++ dat &= ~val; ++ tosa_ac97_write(reg, dat); ++} ++ ++static void tosa_ac97_bit_set(u8 reg, u16 val) ++{ ++ unsigned short dat = tosa_ac97_read(reg); ++ dat |= val; ++ tosa_ac97_write(reg, dat); ++} ++ ++ ++static int tosa_ac97_init(void) ++{ ++ int timeo; ++ ++ pxa_gpio_mode(GPIO31_SYNC_AC97_MD); ++ pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); ++ pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); ++ pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); ++ pxa_gpio_mode(GPIO20_DREQ0_MD); ++ ++ pxa_set_cken(CKEN2_AC97, 1); ++ /* AC97 power on sequense */ ++ while ( 1 ) { ++ GCR = 0; ++ udelay(100); ++ GCR |= GCR_COLD_RST; ++ udelay(5); ++ GCR |= GCR_WARM_RST; ++ udelay(5); ++ for ( timeo = 0x10000; timeo > 0; timeo-- ) { ++ if ( GSR & GSR_PCR ) break; ++ mdelay(5); ++ } ++ if( timeo > 0 ) break; ++ printk(KERN_WARNING "AC97 power on retry!!\n"); ++ } ++ ++ tosa_ac97_write(AC97_EXTENDED_STATUS, 1); ++ /* ++ * Setting AC97 GPIO ++ * i/o function ++ * GPIO1: input EAR_IN signal ++ * GPIO2: output IRQ signal ++ * GPIO3: output PENDOWN signal ++ * GPIO4: input MASK signal ++ * GPIO5: input DETECT MIC signal ++ */ ++ // AC97_GPIO_FUNC AC97_MISC_MODEM_STAT ++ ++ tosa_ac97_bit_clear(AC97_MISC_MODEM_STAT, ++ ((1<<2)|(1<<3)|(1<<4)|(1<<5))); ++ tosa_ac97_bit_clear(AC97_GPIO_CONFIG,(1<<2)|(1<<3)); ++ tosa_ac97_bit_set(AC97_GPIO_CONFIG, (1<<1)|(1<<4)|(1<<5)); ++ ++ tosa_ac97_write(AC97_WM97XX_DIGITISER2, 0xc009); ++ tosa_ac97_write(AC97_WM97XX_DIGITISER1, 0x0030 | WM97XX_DELAY(4)); ++ ++ pxa_gpio_mode(GPIO32_SDATA_IN1_AC97_MD); ++ ad_polling = 1; ++ printk("tosa_ac97_init\n"); ++} ++ ++void tosa_ac97_exit(void) ++{ ++ if (!(CKEN & CKEN2_AC97)) ++ return; ++ ++ // power down the whole chip ++ tosa_ac97_write(AC97_POWERDOWN, 0x7fff); ++ ++// GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN); ++// GSR = GSR; ++// GCR = GCR_ACLINK_OFF; ++ pxa_set_cken(CKEN2_AC97, 0); ++ /* switch back to irq driver */ ++ ad_polling = 0; ++ printk("tosa_ac97_exit\n"); ++} ++ ++int ac97_ad_input(u16 adcsel) ++{ ++ unsigned short val = 0; ++ unsigned long timeout; ++ ++ // prepare ++ tosa_ac97_read(AC97_WM97XX_DIGITISER_RD); ++ ++ if (adcsel & 0x8000) ++ adcsel = ((adcsel & 0x7fff) + 3) << 12; ++ ++ /* Conversion start */ ++ tosa_ac97_write(AC97_WM97XX_DIGITISER1, (adcsel | WM97XX_POLL | WM97XX_DELAY(4))); ++ timeout = 0x1000; ++ /* wait for POLL to go low */ ++ while ((tosa_ac97_read(AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(100); ++ timeout--; ++ } ++ ++ val = tosa_ac97_read(AC97_WM97XX_DIGITISER_RD); ++ ++ val &= 0xFFF ; ++ ++ return val; ++} ++ ++ ++int tosa_read_aux_adc(u16 adcsel) ++{ ++ if (ad_polling) ++ return (ac97_ad_input(adcsel)); ++ else ++ return (wm97xx_read_aux_adc(wm9712, adcsel)); ++} ++ ++static struct device_driver tosa_pm_driver = { ++ .name = "wm97xx-battery", ++ .bus = &wm97xx_bus_type, ++ .owner = THIS_MODULE, ++ .probe = tosa_pm_driver_probe, ++ .remove = tosa_pm_driver_remove, ++}; ++ ++#if 0 ++#define TOSA_TEMP_READ_WAIT_TIME (5) // 5msec [Fix] ++int tosa_read_battery(struct wm97xx* wm, int channel) ++{ ++ //return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, input)); ++ ++ int wm_aux,i; ++ int value = 0; ++ int clear_mux; ++ ++ switch(channel) { ++ ++ case 0: // Main ++ ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON); ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON); ++ wm_aux = WM97XX_AUX_ID3; ++ break; ++ ++ case 1: // Jacket ++ ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_V_ON); ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT_SW_ON); ++ wm_aux = WM97XX_AUX_ID3; ++ break; ++ ++ case 2: // BU ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BU_CHRG_ON); ++ wm_aux = WM97XX_AUX_ID4; ++ break; ++ ++ case 3: // Main Temp ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON); ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON); ++ wm_aux = WM97XX_AUX_ID2; ++ break; ++ ++ case 4: // Jacket Temp ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON); ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON); ++ wm_aux = WM97XX_AUX_ID2; ++ break; ++ ++ default: ++ return -1; ++ } ++ ++ mdelay(TOSA_TEMP_READ_WAIT_TIME); ++ for(i=0;i<4;i++) ++ { ++ if (wm9712) ++ value += wm97xx_read_aux_adc(wm, wm_aux); ++ else ++ value += ac97_ad_input(wm, wm_aux); ++ } ++ ++ value>>=2; ++ // reset the multiplexer ++ clear_mux = TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON | TOSA_TC6393_BAT0_TH_ON | TOSA_TC6393_BAT1_TH_ON | TOSA_TC6393_BU_CHRG_ON; ++ ++ return value; ++ ++} ++#endif ++ ++/* BL 5-3 */ ++struct battery_thresh tosa_battery_levels_bl[] = { ++ { 1663, 100 }, ++ { 1605, 75 }, ++ { 1564, 50 }, ++ { 1510, 25 }, ++ { 1435, 5 }, ++ { 0, 0 }, ++}; ++ ++/* BL 2-0 */ ++struct battery_thresh tosa_battery_levels[] = { ++ { 1679, 100 }, ++ { 1617, 75 }, ++ { 1576, 50 }, ++ { 1530, 25 }, ++ { 1448, 5 }, ++ { 0, 0 }, ++}; ++ ++struct pm_devices { ++ const char * name; ++ struct bus_type *bus; ++ struct device_driver *driver; ++ struct device * dev; ++ int (*resume)(struct device *dev, void * data); // int (*resume)(struct device *dev); ++ int (*suspend)(struct device *dev, void * data);// int (*suspend)(struct device *dev, pm_message_t state); ++}; ++ ++/* Ugly ++ We need the following devices to measure the battery and control the charger: ++ Also we need access to these before we sleep and immediatly after we resume so we can't ++ control their pm via the kernel device manager. To access their pm functions we will backup ++ the suspend and resume handler and clear these pointers. ++ After that we can suspend and resume these devices. ++ ++ Don't change the order of this table!!!!! ++*/ ++static struct pm_devices dev_table[] = { ++ [0] = { ++ .name = TMIO_SOC_NAME, ++ .bus = &platform_bus_type, ++ }, ++}; ++ ++static int tosa_pm_driver_probe(struct device *dev) ++{ ++ wm9712 = dev->driver_data; ++ ad_polling = 0; ++ return 0; ++} ++ ++static int tosa_pm_driver_remove(struct device *dev) ++{ ++ wm9712 = NULL; ++ return 0; ++} ++ ++ ++static void tosa_charger_init(void) ++{ ++ int i; ++ ++ /* If this driver doesn't register, bad things will happen, Tosa won't boot, ++ and the world will possibly explode */ ++ i = driver_register(&tosa_pm_driver); ++ if (i < 0) ++ panic("Cannot register the tosa_pm driver on the wm97xx bus. Halting."); ++ ++ for(i=0;i<ARRAY_SIZE(dev_table);i++) ++ { ++ dev_table[i].driver = driver_find(dev_table[i].name, dev_table[i].bus); ++ if (dev_table[i].driver) ++ { ++ dev_table[i].resume = dev_table[i].driver->resume; ++ dev_table[i].suspend = dev_table[i].driver->suspend; ++ dev_table[i].driver->resume = NULL; ++ dev_table[i].driver->suspend = NULL; ++ } ++ } ++ ++ pxa_gpio_mode(TOSA_GPIO_AC_IN | GPIO_IN); ++ pxa_gpio_mode(TOSA_GPIO_BAT0_CRG | GPIO_IN); ++ pxa_gpio_mode(TOSA_GPIO_BAT1_CRG | GPIO_IN); ++ pxa_gpio_mode(TOSA_GPIO_BAT0_LOW | GPIO_IN); ++ pxa_gpio_mode(TOSA_GPIO_BAT1_LOW | GPIO_IN); ++ ++ pxa_gpio_mode(TOSA_GPIO_JACKET_DETECT | GPIO_IN); ++ pxa_gpio_mode(TOSA_GPIO_POWERON | GPIO_IN); ++ sharpsl_pm_pxa_init(); ++} ++ ++ ++static void tosa_measure_temp(int on) ++{ ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON | TOSA_TC6393_BAT1_TH_ON); ++ ++ if (on) ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON); ++ ++} ++ ++static void tosa_charge(int on) ++{ ++ if(on) ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_CHARGE_OFF); ++ else ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_CHARGE_OFF); ++} ++ ++static void tosa_discharge1(int on) ++{ ++} ++ ++static void tosa_discharge(int on) ++{ ++} ++ ++ ++static void tosa_presuspend(void) ++{ ++ int i; ++ unsigned long wakeup_mask; ++ ++ // put remaining devices into sleep ++ for(i=0;i<ARRAY_SIZE(dev_table);i++) ++ { ++ if(dev_table[i].suspend) ++ driver_for_each_device(dev_table[i].driver, NULL, ++ (void*)&PMSG_SUSPEND, dev_table[i].suspend); ++ } ++ ++ tosa_ac97_exit(); ++ ++ wakeup_mask = GPIO_bit(TOSA_GPIO_POWERON) | GPIO_bit(TOSA_GPIO_ON_KEY) | GPIO_bit(TOSA_GPIO_AC_IN); ++ ++ wakeup_mask |= GPIO_bit(TOSA_GPIO_BAT0_LOW); ++ PWER = wakeup_mask | PWER_RTC; ++ ++ PRER = wakeup_mask; ++ PFER = wakeup_mask; ++ ++ for (i = 0; i <=15; i++) { ++ if (PRER & PFER & GPIO_bit(i)) { ++ if (GPLR0 & GPIO_bit(i) ) ++ PRER &= ~GPIO_bit(i); ++ else ++ PFER &= ~GPIO_bit(i); ++ } ++ } ++ ++ /* Clear reset status */ ++ RCSR = RCSR_HWR | RCSR_WDR | RCSR_SMR | RCSR_GPR; ++ ++ /* Stop 3.6MHz and drive HIGH to PCMCIA and CS */ ++ PCFR = PCFR_OPDE; ++ ++ /* Resume on keyboard power key */ ++ PGSR1 = (PGSR1 & ~TOSA_GPIO_LOW_STROBE_BIT); ++ PGSR2 = (PGSR2 & ~TOSA_GPIO_HIGH_STROBE_BIT); ++ ++ GPDR0 = 0xC3810940; ++ GPDR1 = 0xFCFFAB82; ++ GPDR2 = 0x000F501f; ++// write_scoop_reg(&tosascoop_device.dev,SCOOP_GPWR,0); ++// only debug ++reset_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_NOTE_LED); ++} ++ ++void tosa_postsuspend(void) ++{ ++ int i; ++// only debug ++set_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_NOTE_LED); ++ ++ for(i=ARRAY_SIZE(dev_table);i;i--) ++ { ++ if(dev_table[i-1].resume) ++ driver_for_each_device(dev_table[i-1].driver, NULL, ++ NULL, dev_table[i-1].resume); ++ } ++ tosa_ac97_init(); ++ PMCR = 0x01; ++} ++ ++void tosa_postresume(void) ++{ ++ tosa_ac97_exit(); ++} ++ ++/* ++ * Check what brought us out of the suspend. ++ * Return: 0 to sleep, otherwise wake ++ */ ++static int tosa_should_wakeup(unsigned int resume_on_alarm) ++{ ++ int is_resume = 0; ++ ++ dev_dbg(sharpsl_pm.dev, "GPLR0 = %x,%x\n", GPLR0, PEDR); ++ ++ if ((PEDR & GPIO_bit(TOSA_GPIO_AC_IN))) { ++ if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) { ++ /* charge on */ ++ dev_dbg(sharpsl_pm.dev, "ac insert\n"); ++ sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG; ++ } else { ++ /* charge off */ ++ dev_dbg(sharpsl_pm.dev, "ac remove\n"); ++ sharpsl_pm_led(SHARPSL_LED_OFF); ++ sharpsl_pm.machinfo->charge(0); ++ sharpsl_pm.charge_mode = CHRG_OFF; ++ } ++ } ++ ++ if ((PEDR & GPIO_bit(GPIO_bit(TOSA_GPIO_BAT0_CRG)))) ++ dev_dbg(sharpsl_pm.dev, "Charge full interrupt\n"); ++ ++ if (PEDR & GPIO_bit(TOSA_GPIO_POWERON)) ++ is_resume |= GPIO_bit(TOSA_GPIO_POWERON); ++ ++ if (PEDR & GPIO_bit(TOSA_GPIO_ON_KEY)) ++ is_resume |= GPIO_bit(TOSA_GPIO_ON_KEY); ++ ++ if (resume_on_alarm && (PEDR & PWER_RTC)) ++ is_resume |= PWER_RTC; ++ ++ return is_resume; ++} ++ ++static unsigned long tosa_charger_wakeup(void) ++{ ++// return ~GPLR0 & ( GPIO_bit(TOSA_GPIO_AC_IN) | GPIO_bit(TOSA_GPIO_POWERON) | GPIO_bit(TOSA_GPIO_ON_KEY) ); ++ return (~GPLR0 & ( GPIO_bit(TOSA_GPIO_POWERON) | GPIO_bit(TOSA_GPIO_ON_KEY) )) | (GPLR0 & GPIO_bit(TOSA_GPIO_AC_IN)); ++} ++ ++unsigned long tosa_read_bat(void) ++{ ++ unsigned long value; ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON); ++ mdelay(5); ++ value = tosa_read_aux_adc(WM97XX_AUX_ID3); ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON); ++ return value; ++} ++ ++unsigned long tosapm_read_devdata(int type) ++{ ++ switch(type) { ++ case SHARPSL_STATUS_ACIN: ++ return ((GPLR(TOSA_GPIO_AC_IN) & GPIO_bit(TOSA_GPIO_AC_IN)) == 0); ++ case SHARPSL_STATUS_LOCK: ++ return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batlock); ++ case SHARPSL_STATUS_CHRGFULL: ++ return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batfull); ++ case SHARPSL_STATUS_FATAL: ++ return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_fatal); ++ case SHARPSL_ACIN_VOLT: ++ return 1000; // not used on tosa ++ case SHARPSL_BATT_TEMP: ++ return tosa_read_aux_adc(WM97XX_AUX_ID2); ++ case SHARPSL_BATT_VOLT: ++ return tosa_read_bat(); ++ default: ++ return tosa_read_bat(); ++ } ++} ++ ++static struct sharpsl_charger_machinfo tosa_pm_machinfo = { ++ .init = tosa_charger_init, ++ .exit = sharpsl_pm_pxa_remove, ++ .gpio_batlock = TOSA_GPIO_BAT_LOCKED, ++ .gpio_acin = TOSA_GPIO_AC_IN, ++ .gpio_batfull = TOSA_GPIO_BAT0_CRG, ++ .batfull_irq = 0, ++ .discharge = tosa_discharge, ++ .discharge1 = tosa_discharge1, ++ .charge = tosa_charge, ++ .measure_temp = tosa_measure_temp, ++ .presuspend = tosa_presuspend, ++ .postsuspend = tosa_postsuspend, ++ .postresume = tosa_postresume, ++ .read_devdata = tosapm_read_devdata, ++ .charger_wakeup = tosa_charger_wakeup, ++ .should_wakeup = tosa_should_wakeup, ++ .backlight_limit = corgibl_limit_intensity, ++ .backlight_get_status= tosa_bl_intensity, ++ .bat_levels = 6, ++ .bat_levels_noac = tosa_battery_levels, ++ .bat_levels_acin = tosa_battery_levels, ++ .bat_levels_noac_bl = tosa_battery_levels_bl, ++ .bat_levels_acin_bl = tosa_battery_levels_bl, ++ .charge_on_volt = 1200, // 2.9V TOSA_MAIN_BATTERY_ERR_THRESH voltage < 1200 -> error ++ .charge_on_temp = 3600, // -- TOSA_MAIN_BATTERY_EXIST_THRESH temp > 3600 -> error, no battery ++ .charge_acin_high = 1500, // not used default value ++ .charge_acin_low = 500, // not used default value ++ .fatal_acin_volt = 1572, // 3.8V ++ .fatal_noacin_volt= 1551, // 3.75V ++ .status_high_acin = 1564, // 3.78V ++ .status_low_acin = 1510, // 3.65V ++ .status_high_noac = 1564, // 3.78V ++ .status_low_noac = 1510, // 3.65V ++}; ++ ++static struct platform_device *tosapm_device; ++ ++static int __devinit tosapm_init(void) ++{ ++ int ret; ++ ++ tosapm_device = platform_device_alloc("sharpsl-pm", -1); ++ if (!tosapm_device) ++ return -ENOMEM; ++ ++ tosapm_device->dev.platform_data = &tosa_pm_machinfo; ++ ret = platform_device_add(tosapm_device); ++ ++ if (ret) ++ platform_device_put(tosapm_device); ++ ++ return ret; ++} ++ ++static void tosapm_exit(void) ++{ ++ int i; ++ ++ // restore the resume / suspend handler ++ for(i=0;i<ARRAY_SIZE(dev_table);i++) ++ { ++ if (dev_table[i].driver) ++ { ++ dev_table[i].driver->resume = dev_table[i].resume; ++ dev_table[i].driver->suspend = dev_table[i].suspend; ++ dev_table[i].resume = NULL; ++ dev_table[i].suspend = NULL; ++ put_driver(dev_table[i].driver); ++ } ++ } ++ ++ if (wm9712) ++ driver_unregister(&tosa_pm_driver); ++ ++ platform_device_unregister(tosapm_device); ++} ++ ++module_init(tosapm_init); ++module_exit(tosapm_exit); +Index: linux-2.6.17/arch/arm/mach-pxa/Kconfig +=================================================================== +--- linux-2.6.17.orig/arch/arm/mach-pxa/Kconfig 2006-09-19 20:51:40.160810500 +0200 ++++ linux-2.6.17/arch/arm/mach-pxa/Kconfig 2006-09-19 21:08:04.926354500 +0200 +@@ -110,6 +110,7 @@ config MACH_TOSA + bool "Enable Sharp SL-6000x (Tosa) Support" + depends PXA_SHARPSL_25x + select TOSHIBA_TC6393XB ++ select SHARPSL_PM + + config PXA25x + bool +Index: linux-2.6.17/arch/arm/mach-pxa/Makefile +=================================================================== +--- linux-2.6.17.orig/arch/arm/mach-pxa/Makefile 2006-09-19 20:51:33.984424500 +0200 ++++ linux-2.6.17/arch/arm/mach-pxa/Makefile 2006-09-19 21:08:04.922354250 +0200 +@@ -16,7 +16,7 @@ obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o + obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o + obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o + obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o +-obj-$(CONFIG_MACH_TOSA) += tosa.o ++obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o + obj-$(CONFIG_MACH_EM_X270) += em-x270.o + obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o + +Index: linux-2.6.17/arch/arm/mach-pxa/tosa_pm.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/arch/arm/mach-pxa/tosa_pm.c 2006-09-19 21:08:34.476201250 +0200 +@@ -0,0 +1,661 @@ ++/* ++ * Battery and Power Management code for the Sharp SL-6000x ++ * ++ * Copyright (c) 2005 Dirk Opfer ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/stat.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/interrupt.h> ++#include <linux/platform_device.h> ++#include <linux/pm.h> ++#include <linux/wm97xx.h> ++ ++#include <asm/apm.h> ++#include <asm/irq.h> ++#include <asm/mach-types.h> ++#include <asm/hardware.h> ++#include <asm/hardware/scoop.h> ++#include <asm/hardware/tmio.h> ++ ++#include <asm/arch/sharpsl.h> ++#include <asm/arch/tosa.h> ++#include <asm/arch/pxa-regs.h> ++#include <sound/soc.h> ++#include <sound/ac97_codec.h> ++#include "sharpsl.h" ++ ++extern int tosa_bl_intensity(void); ++volatile static int ad_polling; ++static int tosa_pm_driver_probe(struct device *dev); ++static int tosa_pm_driver_remove(struct device *dev); ++static struct wm97xx *wm9712; ++ ++/************************************************************ ++ * AC97 functions ++ ************************************************************/ ++#define AC97_TIMEOUT_VAL 0x1000000 ++ ++#define AC97_MISC_MODEM_STAT 0x0056 ++#define AC97_GPIO_CONFIG 0x004C ++ ++static u16 tosa_ac97_read(unsigned short reg) ++{ ++ volatile u32 *reg_addr; ++ volatile int timeout; ++ unsigned short data; ++ ++ if (CAR & CAR_CAIP) { ++ printk(KERN_CRIT ": CAR_CAIP already set\n"); ++ return 0; ++ } ++ ++ if (reg == AC97_GPIO_STATUS) ++ reg_addr = &PMC_REG_BASE; ++ else ++ reg_addr = &PAC_REG_BASE; ++ ++ reg_addr += (reg >> 1); ++ ++ data=0; ++ GSR = GSR_CDONE | GSR_SDONE; ++ ++ data = *reg_addr; ++ timeout = 0; ++ ++ while(((GSR & GSR_SDONE)) == 0 && (timeout++ < AC97_TIMEOUT_VAL)); ++ ++ if ((timeout >= AC97_TIMEOUT_VAL)) { ++ GSR = GSR; ++ printk(KERN_CRIT ": AC97 is busy1.\n"); ++ return data; ++ } ++ ++ // actual read ++ GSR = GSR_CDONE | GSR_SDONE; ++ data = *reg_addr; ++ ++ timeout = 0; ++ while(((GSR & GSR_SDONE) == 0) && (timeout++<AC97_TIMEOUT_VAL)); ++ if ((timeout >= AC97_TIMEOUT_VAL)) { ++ GSR = GSR; ++ printk(KERN_CRIT ": AC97 is busy2.\n"); ++ return data; ++ } ++ ++ return data; ++} ++ ++static void tosa_ac97_write(unsigned short reg, unsigned short val) ++{ ++ volatile u32 *reg_addr; ++ volatile int timeout=0; ++ ++ if (CAR & CAR_CAIP) { ++ printk(KERN_CRIT ": CAR_CAIP already set\n"); ++ return; ++ } ++ ++ GSR = GSR_CDONE | GSR_SDONE; ++ if (reg == AC97_GPIO_STATUS) ++ reg_addr = &PMC_REG_BASE; ++ else ++ reg_addr = &PAC_REG_BASE; ++ ++ reg_addr += (reg >> 1); ++ ++ *reg_addr = val; ++ while(((GSR & GSR_CDONE) == 0) && (timeout++ < AC97_TIMEOUT_VAL)); ++ if (timeout >= AC97_TIMEOUT_VAL) { ++ printk(KERN_CRIT ": AC97 is busy.\n"); ++ } ++} ++ ++static void tosa_ac97_bit_clear(u8 reg, u16 val) ++{ ++ unsigned short dat = tosa_ac97_read(reg); ++ dat &= ~val; ++ tosa_ac97_write(reg, dat); ++} ++ ++static void tosa_ac97_bit_set(u8 reg, u16 val) ++{ ++ unsigned short dat = tosa_ac97_read(reg); ++ dat |= val; ++ tosa_ac97_write(reg, dat); ++} ++ ++ ++static int tosa_ac97_init(void) ++{ ++ int timeo; ++ ++ pxa_gpio_mode(GPIO31_SYNC_AC97_MD); ++ pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); ++ pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); ++ pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); ++ pxa_gpio_mode(GPIO20_DREQ0_MD); ++ ++ pxa_set_cken(CKEN2_AC97, 1); ++ /* AC97 power on sequense */ ++ while ( 1 ) { ++ GCR = 0; ++ udelay(100); ++ GCR |= GCR_COLD_RST; ++ udelay(5); ++ GCR |= GCR_WARM_RST; ++ udelay(5); ++ for ( timeo = 0x10000; timeo > 0; timeo-- ) { ++ if ( GSR & GSR_PCR ) break; ++ mdelay(5); ++ } ++ if( timeo > 0 ) break; ++ printk(KERN_WARNING "AC97 power on retry!!\n"); ++ } ++ ++ tosa_ac97_write(AC97_EXTENDED_STATUS, 1); ++ /* ++ * Setting AC97 GPIO ++ * i/o function ++ * GPIO1: input EAR_IN signal ++ * GPIO2: output IRQ signal ++ * GPIO3: output PENDOWN signal ++ * GPIO4: input MASK signal ++ * GPIO5: input DETECT MIC signal ++ */ ++ // AC97_GPIO_FUNC AC97_MISC_MODEM_STAT ++ ++ tosa_ac97_bit_clear(AC97_MISC_MODEM_STAT, ++ ((1<<2)|(1<<3)|(1<<4)|(1<<5))); ++ tosa_ac97_bit_clear(AC97_GPIO_CONFIG,(1<<2)|(1<<3)); ++ tosa_ac97_bit_set(AC97_GPIO_CONFIG, (1<<1)|(1<<4)|(1<<5)); ++ ++ tosa_ac97_write(AC97_WM97XX_DIGITISER2, 0xc009); ++ tosa_ac97_write(AC97_WM97XX_DIGITISER1, 0x0030 | WM97XX_DELAY(4)); ++ ++ pxa_gpio_mode(GPIO32_SDATA_IN1_AC97_MD); ++ ad_polling = 1; ++ printk("tosa_ac97_init\n"); ++} ++ ++void tosa_ac97_exit(void) ++{ ++ if (!(CKEN & CKEN2_AC97)) ++ return; ++ ++ // power down the whole chip ++ tosa_ac97_write(AC97_POWERDOWN, 0x7fff); ++ ++// GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN); ++// GSR = GSR; ++// GCR = GCR_ACLINK_OFF; ++ pxa_set_cken(CKEN2_AC97, 0); ++ /* switch back to irq driver */ ++ ad_polling = 0; ++ printk("tosa_ac97_exit\n"); ++} ++ ++int ac97_ad_input(u16 adcsel) ++{ ++ unsigned short val = 0; ++ unsigned long timeout; ++ ++ // prepare ++ tosa_ac97_read(AC97_WM97XX_DIGITISER_RD); ++ ++ if (adcsel & 0x8000) ++ adcsel = ((adcsel & 0x7fff) + 3) << 12; ++ ++ /* Conversion start */ ++ tosa_ac97_write(AC97_WM97XX_DIGITISER1, (adcsel | WM97XX_POLL | WM97XX_DELAY(4))); ++ timeout = 0x1000; ++ /* wait for POLL to go low */ ++ while ((tosa_ac97_read(AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(100); ++ timeout--; ++ } ++ ++ val = tosa_ac97_read(AC97_WM97XX_DIGITISER_RD); ++ ++ val &= 0xFFF ; ++ ++ return val; ++} ++ ++ ++int tosa_read_aux_adc(u16 adcsel) ++{ ++ if (ad_polling) ++ return (ac97_ad_input(adcsel)); ++ else ++ return (wm97xx_read_aux_adc(wm9712, adcsel)); ++} ++ ++static struct device_driver tosa_pm_driver = { ++ .name = "wm97xx-battery", ++ .bus = &wm97xx_bus_type, ++ .owner = THIS_MODULE, ++ .probe = tosa_pm_driver_probe, ++ .remove = tosa_pm_driver_remove, ++}; ++ ++#if 0 ++#define TOSA_TEMP_READ_WAIT_TIME (5) // 5msec [Fix] ++int tosa_read_battery(struct wm97xx* wm, int channel) ++{ ++ //return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, input)); ++ ++ int wm_aux,i; ++ int value = 0; ++ int clear_mux; ++ ++ switch(channel) { ++ ++ case 0: // Main ++ ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON); ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON); ++ wm_aux = WM97XX_AUX_ID3; ++ break; ++ ++ case 1: // Jacket ++ ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_V_ON); ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT_SW_ON); ++ wm_aux = WM97XX_AUX_ID3; ++ break; ++ ++ case 2: // BU ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BU_CHRG_ON); ++ wm_aux = WM97XX_AUX_ID4; ++ break; ++ ++ case 3: // Main Temp ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON); ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON); ++ wm_aux = WM97XX_AUX_ID2; ++ break; ++ ++ case 4: // Jacket Temp ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON); ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON); ++ wm_aux = WM97XX_AUX_ID2; ++ break; ++ ++ default: ++ return -1; ++ } ++ ++ mdelay(TOSA_TEMP_READ_WAIT_TIME); ++ for(i=0;i<4;i++) ++ { ++ if (wm9712) ++ value += wm97xx_read_aux_adc(wm, wm_aux); ++ else ++ value += ac97_ad_input(wm, wm_aux); ++ } ++ ++ value>>=2; ++ // reset the multiplexer ++ clear_mux = TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON | TOSA_TC6393_BAT0_TH_ON | TOSA_TC6393_BAT1_TH_ON | TOSA_TC6393_BU_CHRG_ON; ++ ++ return value; ++ ++} ++#endif ++ ++/* BL 5-3 */ ++struct battery_thresh tosa_battery_levels_bl[] = { ++ { 1663, 100 }, ++ { 1605, 75 }, ++ { 1564, 50 }, ++ { 1510, 25 }, ++ { 1435, 5 }, ++ { 0, 0 }, ++}; ++ ++/* BL 2-0 */ ++struct battery_thresh tosa_battery_levels[] = { ++ { 1679, 100 }, ++ { 1617, 75 }, ++ { 1576, 50 }, ++ { 1530, 25 }, ++ { 1448, 5 }, ++ { 0, 0 }, ++}; ++ ++struct pm_devices { ++ const char * name; ++ struct bus_type *bus; ++ struct device_driver *driver; ++ struct device * dev; ++ int (*resume)(struct device *dev, void * data); // int (*resume)(struct device *dev); ++ int (*suspend)(struct device *dev, void * data);// int (*suspend)(struct device *dev, pm_message_t state); ++}; ++ ++/* Ugly ++ We need the following devices to measure the battery and control the charger: ++ Also we need access to these before we sleep and immediatly after we resume so we can't ++ control their pm via the kernel device manager. To access their pm functions we will backup ++ the suspend and resume handler and clear these pointers. ++ After that we can suspend and resume these devices. ++ ++ Don't change the order of this table!!!!! ++*/ ++static struct pm_devices dev_table[] = { ++ [0] = { ++ .name = TMIO_SOC_NAME, ++ .bus = &platform_bus_type, ++ }, ++}; ++ ++static int tosa_pm_driver_probe(struct device *dev) ++{ ++ wm9712 = dev->driver_data; ++ ad_polling = 0; ++ return 0; ++} ++ ++static int tosa_pm_driver_remove(struct device *dev) ++{ ++ wm9712 = NULL; ++ return 0; ++} ++ ++ ++static void tosa_charger_init(void) ++{ ++ int i; ++ ++ /* If this driver doesn't register, bad things will happen, Tosa won't boot, ++ and the world will possibly explode */ ++ i = driver_register(&tosa_pm_driver); ++ if (i < 0) ++ panic("Cannot register the tosa_pm driver on the wm97xx bus. Halting."); ++ ++ for(i=0;i<ARRAY_SIZE(dev_table);i++) ++ { ++ dev_table[i].driver = driver_find(dev_table[i].name, dev_table[i].bus); ++ if (dev_table[i].driver) ++ { ++ dev_table[i].resume = dev_table[i].driver->resume; ++ dev_table[i].suspend = dev_table[i].driver->suspend; ++ dev_table[i].driver->resume = NULL; ++ dev_table[i].driver->suspend = NULL; ++ } ++ } ++ ++ pxa_gpio_mode(TOSA_GPIO_AC_IN | GPIO_IN); ++ pxa_gpio_mode(TOSA_GPIO_BAT0_CRG | GPIO_IN); ++ pxa_gpio_mode(TOSA_GPIO_BAT1_CRG | GPIO_IN); ++ pxa_gpio_mode(TOSA_GPIO_BAT0_LOW | GPIO_IN); ++ pxa_gpio_mode(TOSA_GPIO_BAT1_LOW | GPIO_IN); ++ ++ pxa_gpio_mode(TOSA_GPIO_JACKET_DETECT | GPIO_IN); ++ pxa_gpio_mode(TOSA_GPIO_POWERON | GPIO_IN); ++ sharpsl_pm_pxa_init(); ++} ++ ++ ++static void tosa_measure_temp(int on) ++{ ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON | TOSA_TC6393_BAT1_TH_ON); ++ ++ if (on) ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON); ++ ++} ++ ++static void tosa_charge(int on) ++{ ++ if(on) ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_CHARGE_OFF); ++ else ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_CHARGE_OFF); ++} ++ ++static void tosa_discharge1(int on) ++{ ++} ++ ++static void tosa_discharge(int on) ++{ ++} ++ ++ ++static void tosa_presuspend(void) ++{ ++ int i; ++ unsigned long wakeup_mask; ++ ++ // put remaining devices into sleep ++ for(i=0;i<ARRAY_SIZE(dev_table);i++) ++ { ++ if(dev_table[i].suspend) ++ driver_for_each_device(dev_table[i].driver, NULL, ++ (void*)&PMSG_SUSPEND, dev_table[i].suspend); ++ } ++ ++ tosa_ac97_exit(); ++ ++ wakeup_mask = GPIO_bit(TOSA_GPIO_POWERON) | GPIO_bit(TOSA_GPIO_ON_KEY) | GPIO_bit(TOSA_GPIO_AC_IN); ++ ++ wakeup_mask |= GPIO_bit(TOSA_GPIO_BAT0_LOW); ++ PWER = wakeup_mask | PWER_RTC; ++ ++ PRER = wakeup_mask; ++ PFER = wakeup_mask; ++ ++ for (i = 0; i <=15; i++) { ++ if (PRER & PFER & GPIO_bit(i)) { ++ if (GPLR0 & GPIO_bit(i) ) ++ PRER &= ~GPIO_bit(i); ++ else ++ PFER &= ~GPIO_bit(i); ++ } ++ } ++ ++ /* Clear reset status */ ++ RCSR = RCSR_HWR | RCSR_WDR | RCSR_SMR | RCSR_GPR; ++ ++ /* Stop 3.6MHz and drive HIGH to PCMCIA and CS */ ++ PCFR = PCFR_OPDE; ++ ++ /* Resume on keyboard power key */ ++ PGSR1 = (PGSR1 & ~TOSA_GPIO_LOW_STROBE_BIT); ++ PGSR2 = (PGSR2 & ~TOSA_GPIO_HIGH_STROBE_BIT); ++ ++ GPDR0 = 0xC3810940; ++ GPDR1 = 0xFCFFAB82; ++ GPDR2 = 0x000F501f; ++// write_scoop_reg(&tosascoop_device.dev,SCOOP_GPWR,0); ++// only debug ++reset_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_NOTE_LED); ++} ++ ++void tosa_postsuspend(void) ++{ ++ int i; ++// only debug ++set_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_NOTE_LED); ++ ++ for(i=ARRAY_SIZE(dev_table);i;i--) ++ { ++ if(dev_table[i-1].resume) ++ driver_for_each_device(dev_table[i-1].driver, NULL, ++ NULL, dev_table[i-1].resume); ++ } ++ tosa_ac97_init(); ++ PMCR = 0x01; ++} ++ ++void tosa_postresume(void) ++{ ++ tosa_ac97_exit(); ++} ++ ++/* ++ * Check what brought us out of the suspend. ++ * Return: 0 to sleep, otherwise wake ++ */ ++static int tosa_should_wakeup(unsigned int resume_on_alarm) ++{ ++ int is_resume = 0; ++ ++ dev_dbg(sharpsl_pm.dev, "GPLR0 = %x,%x\n", GPLR0, PEDR); ++ ++ if ((PEDR & GPIO_bit(TOSA_GPIO_AC_IN))) { ++ if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) { ++ /* charge on */ ++ dev_dbg(sharpsl_pm.dev, "ac insert\n"); ++ sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG; ++ } else { ++ /* charge off */ ++ dev_dbg(sharpsl_pm.dev, "ac remove\n"); ++ sharpsl_pm_led(SHARPSL_LED_OFF); ++ sharpsl_pm.machinfo->charge(0); ++ sharpsl_pm.charge_mode = CHRG_OFF; ++ } ++ } ++ ++ if ((PEDR & GPIO_bit(GPIO_bit(TOSA_GPIO_BAT0_CRG)))) ++ dev_dbg(sharpsl_pm.dev, "Charge full interrupt\n"); ++ ++ if (PEDR & GPIO_bit(TOSA_GPIO_POWERON)) ++ is_resume |= GPIO_bit(TOSA_GPIO_POWERON); ++ ++ if (PEDR & GPIO_bit(TOSA_GPIO_ON_KEY)) ++ is_resume |= GPIO_bit(TOSA_GPIO_ON_KEY); ++ ++ if (resume_on_alarm && (PEDR & PWER_RTC)) ++ is_resume |= PWER_RTC; ++ ++ return is_resume; ++} ++ ++static unsigned long tosa_charger_wakeup(void) ++{ ++// return ~GPLR0 & ( GPIO_bit(TOSA_GPIO_AC_IN) | GPIO_bit(TOSA_GPIO_POWERON) | GPIO_bit(TOSA_GPIO_ON_KEY) ); ++ return (~GPLR0 & ( GPIO_bit(TOSA_GPIO_POWERON) | GPIO_bit(TOSA_GPIO_ON_KEY) )) | (GPLR0 & GPIO_bit(TOSA_GPIO_AC_IN)); ++} ++ ++unsigned long tosa_read_bat(void) ++{ ++ unsigned long value; ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON); ++ mdelay(5); ++ value = tosa_read_aux_adc(WM97XX_AUX_ID3); ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON); ++ return value; ++} ++ ++unsigned long tosapm_read_devdata(int type) ++{ ++ switch(type) { ++ case SHARPSL_STATUS_ACIN: ++ return ((GPLR(TOSA_GPIO_AC_IN) & GPIO_bit(TOSA_GPIO_AC_IN)) == 0); ++ case SHARPSL_STATUS_LOCK: ++ return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batlock); ++ case SHARPSL_STATUS_CHRGFULL: ++ return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batfull); ++ case SHARPSL_STATUS_FATAL: ++ return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_fatal); ++ case SHARPSL_ACIN_VOLT: ++ return 1000; // not used on tosa ++ case SHARPSL_BATT_TEMP: ++ return tosa_read_aux_adc(WM97XX_AUX_ID2); ++ case SHARPSL_BATT_VOLT: ++ return tosa_read_bat(); ++ default: ++ return tosa_read_bat(); ++ } ++} ++ ++static struct sharpsl_charger_machinfo tosa_pm_machinfo = { ++ .init = tosa_charger_init, ++ .exit = sharpsl_pm_pxa_remove, ++ .gpio_batlock = TOSA_GPIO_BAT_LOCKED, ++ .gpio_acin = TOSA_GPIO_AC_IN, ++ .gpio_batfull = TOSA_GPIO_BAT0_CRG, ++ .batfull_irq = 0, ++ .discharge = tosa_discharge, ++ .discharge1 = tosa_discharge1, ++ .charge = tosa_charge, ++ .measure_temp = tosa_measure_temp, ++ .presuspend = tosa_presuspend, ++ .postsuspend = tosa_postsuspend, ++ .postresume = tosa_postresume, ++ .read_devdata = tosapm_read_devdata, ++ .charger_wakeup = tosa_charger_wakeup, ++ .should_wakeup = tosa_should_wakeup, ++ .backlight_limit = corgibl_limit_intensity, ++ .backlight_get_status= tosa_bl_intensity, ++ .bat_levels = 6, ++ .bat_levels_noac = tosa_battery_levels, ++ .bat_levels_acin = tosa_battery_levels, ++ .bat_levels_noac_bl = tosa_battery_levels_bl, ++ .bat_levels_acin_bl = tosa_battery_levels_bl, ++ .charge_on_volt = 1200, // 2.9V TOSA_MAIN_BATTERY_ERR_THRESH voltage < 1200 -> error ++ .charge_on_temp = 3600, // -- TOSA_MAIN_BATTERY_EXIST_THRESH temp > 3600 -> error, no battery ++ .charge_acin_high = 1500, // not used default value ++ .charge_acin_low = 500, // not used default value ++ .fatal_acin_volt = 1572, // 3.8V ++ .fatal_noacin_volt= 1551, // 3.75V ++ .status_high_acin = 1564, // 3.78V ++ .status_low_acin = 1510, // 3.65V ++ .status_high_noac = 1564, // 3.78V ++ .status_low_noac = 1510, // 3.65V ++}; ++ ++static struct platform_device *tosapm_device; ++ ++static int __devinit tosapm_init(void) ++{ ++ int ret; ++ ++ tosapm_device = platform_device_alloc("sharpsl-pm", -1); ++ if (!tosapm_device) ++ return -ENOMEM; ++ ++ tosapm_device->dev.platform_data = &tosa_pm_machinfo; ++ ret = platform_device_add(tosapm_device); ++ ++ if (ret) ++ platform_device_put(tosapm_device); ++ ++ return ret; ++} ++ ++static void tosapm_exit(void) ++{ ++ int i; ++ ++ // restore the resume / suspend handler ++ for(i=0;i<ARRAY_SIZE(dev_table);i++) ++ { ++ if (dev_table[i].driver) ++ { ++ dev_table[i].driver->resume = dev_table[i].resume; ++ dev_table[i].driver->suspend = dev_table[i].suspend; ++ dev_table[i].resume = NULL; ++ dev_table[i].suspend = NULL; ++ put_driver(dev_table[i].driver); ++ } ++ } ++ ++ if (wm9712) ++ driver_unregister(&tosa_pm_driver); ++ ++ platform_device_unregister(tosapm_device); ++} ++ ++module_init(tosapm_init); ++module_exit(tosapm_exit); +Index: linux-2.6.17/arch/arm/mach-pxa/Kconfig +=================================================================== +--- linux-2.6.17.orig/arch/arm/mach-pxa/Kconfig 2006-09-19 20:51:40.160810500 +0200 ++++ linux-2.6.17/arch/arm/mach-pxa/Kconfig 2006-09-19 21:08:04.926354500 +0200 +@@ -110,6 +110,7 @@ config MACH_TOSA + bool "Enable Sharp SL-6000x (Tosa) Support" + depends PXA_SHARPSL_25x + select TOSHIBA_TC6393XB ++ select SHARPSL_PM + + config PXA25x + bool diff --git a/packages/linux/linux-rp-2.6.23/tosa-pxaac97-r6-fix-r0.patch b/packages/linux/linux-rp-2.6.23/tosa-pxaac97-r6-fix-r0.patch new file mode 100644 index 0000000000..6f6f540eff --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/tosa-pxaac97-r6-fix-r0.patch @@ -0,0 +1,58 @@ +From 005693333f4b3e0495bb80cc3cfd812e3e6f0a30 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 00:48:42 +0400 +Subject: [PATCH] tosa-pxaac97-r6.patch fixes + +--- + arch/arm/mach-pxa/tosa.c | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c +index 059fa07..61536d4 100644 +--- a/arch/arm/mach-pxa/tosa.c ++++ b/arch/arm/mach-pxa/tosa.c +@@ -310,10 +310,10 @@ static void __init tosa_init(void) + PMCR = 0x01; + + // AC97 Disable all IRQ's +- pxa_set_cken(CKEN2_AC97, 1); ++ pxa_set_cken(CKEN_AC97, 1); + GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN); + GSR = GSR; +- pxa_set_cken(CKEN2_AC97, 0); ++ pxa_set_cken(CKEN_AC97, 0); + + pxa_set_mci_info(&tosa_mci_platform_data); + pxa_set_udc_info(&udc_info); +-- +1.4.4.4 + +From 005693333f4b3e0495bb80cc3cfd812e3e6f0a30 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 00:48:42 +0400 +Subject: [PATCH] tosa-pxaac97-r6.patch fixes + +--- + arch/arm/mach-pxa/tosa.c | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c +index 059fa07..61536d4 100644 +--- a/arch/arm/mach-pxa/tosa.c ++++ b/arch/arm/mach-pxa/tosa.c +@@ -310,10 +310,10 @@ static void __init tosa_init(void) + PMCR = 0x01; + + // AC97 Disable all IRQ's +- pxa_set_cken(CKEN2_AC97, 1); ++ pxa_set_cken(CKEN_AC97, 1); + GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN); + GSR = GSR; +- pxa_set_cken(CKEN2_AC97, 0); ++ pxa_set_cken(CKEN_AC97, 0); + + pxa_set_mci_info(&tosa_mci_platform_data); + pxa_set_udc_info(&udc_info); +-- +1.4.4.4 + diff --git a/packages/linux/linux-rp-2.6.23/tosa-tmio-lcd-r10-fix-r0.patch b/packages/linux/linux-rp-2.6.23/tosa-tmio-lcd-r10-fix-r0.patch new file mode 100644 index 0000000000..73913bccc8 --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/tosa-tmio-lcd-r10-fix-r0.patch @@ -0,0 +1,70 @@ +From bb3ed6577c592d86f0976a92978c9454bbdfbe59 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 02:01:23 +0400 +Subject: [PATCH] tosa-tmio-lcd-r10.patch fixes + +--- + arch/arm/mach-pxa/tosa_lcd.c | 5 +++-- + 1 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa_lcd.c b/arch/arm/mach-pxa/tosa_lcd.c +index eeeee3e..d52f63f 100644 +--- a/arch/arm/mach-pxa/tosa_lcd.c ++++ b/arch/arm/mach-pxa/tosa_lcd.c +@@ -66,7 +66,7 @@ static unsigned short normal_i2c[] = { + }; + I2C_CLIENT_INSMOD; + +-static struct corgibl_machinfo tosa_bl_machinfo = { ++static struct generic_bl_info tosa_bl_machinfo = { + .max_intensity = 255, + .default_intensity = 68, + .limit_mask = 0x0b, +@@ -80,7 +80,8 @@ int tosa_bl_intensity(void) + + static void pxa_nssp_output(unsigned char reg, unsigned char data) + { +- unsigned long flag, dummy; ++ unsigned long flag; ++ u32 dummy; + u32 dat = ( ((reg << 5) & 0xe0) | (data & 0x1f) ); + spin_lock_irqsave(&tosa_nssp_lock, flag); + +-- +1.4.4.4 + +From bb3ed6577c592d86f0976a92978c9454bbdfbe59 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 19 Oct 2007 02:01:23 +0400 +Subject: [PATCH] tosa-tmio-lcd-r10.patch fixes + +--- + arch/arm/mach-pxa/tosa_lcd.c | 5 +++-- + 1 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa_lcd.c b/arch/arm/mach-pxa/tosa_lcd.c +index eeeee3e..d52f63f 100644 +--- a/arch/arm/mach-pxa/tosa_lcd.c ++++ b/arch/arm/mach-pxa/tosa_lcd.c +@@ -66,7 +66,7 @@ static unsigned short normal_i2c[] = { + }; + I2C_CLIENT_INSMOD; + +-static struct corgibl_machinfo tosa_bl_machinfo = { ++static struct generic_bl_info tosa_bl_machinfo = { + .max_intensity = 255, + .default_intensity = 68, + .limit_mask = 0x0b, +@@ -80,7 +80,8 @@ int tosa_bl_intensity(void) + + static void pxa_nssp_output(unsigned char reg, unsigned char data) + { +- unsigned long flag, dummy; ++ unsigned long flag; ++ u32 dummy; + u32 dat = ( ((reg << 5) & 0xe0) | (data & 0x1f) ); + spin_lock_irqsave(&tosa_nssp_lock, flag); + +-- +1.4.4.4 + diff --git a/packages/linux/linux-rp-2.6.23/tosa-tmio-lcd-r10.patch b/packages/linux/linux-rp-2.6.23/tosa-tmio-lcd-r10.patch new file mode 100644 index 0000000000..cd24cc6cdc --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/tosa-tmio-lcd-r10.patch @@ -0,0 +1,928 @@ + arch/arm/mach-pxa/Kconfig | 5 + arch/arm/mach-pxa/Makefile | 2 + arch/arm/mach-pxa/tosa.c | 49 +++++- + arch/arm/mach-pxa/tosa_lcd.c | 344 +++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 396 insertions(+), 4 deletions(-) + +Index: git/arch/arm/mach-pxa/Makefile +=================================================================== +--- git.orig/arch/arm/mach-pxa/Makefile 2006-11-07 22:13:10.000000000 +0000 ++++ git/arch/arm/mach-pxa/Makefile 2006-11-07 23:29:38.000000000 +0000 +@@ -17,7 +17,7 @@ obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o + obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o + obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o + obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o +-obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o ++obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o tosa_lcd.o + obj-$(CONFIG_MACH_EM_X270) += em-x270.o + obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o + +Index: git/arch/arm/mach-pxa/tosa_lcd.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ git/arch/arm/mach-pxa/tosa_lcd.c 2006-11-07 23:29:25.000000000 +0000 +@@ -0,0 +1,344 @@ ++/* ++ * LCD / Backlight control code for Sharp SL-6000x (tosa) ++ * ++ * Copyright (c) 2005 Dirk Opfer ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <linux/i2c.h> ++#include <linux/fb.h> ++ ++#include <asm/mach/sharpsl_param.h> ++#include <asm/hardware.h> ++#include <asm/hardware/scoop.h> ++#include <asm/hardware/tmio.h> ++#include <asm/arch/ssp.h> ++#include <asm/arch/sharpsl.h> ++#include <asm/arch/tosa.h> ++#include <asm/arch/pxa-regs.h> ++ ++#define DAC_BASE 0x4e ++#define DAC_CH1 0 ++#define DAC_CH2 1 ++ ++#define TG_REG0_VQV 0x0001 ++#define TG_REG0_COLOR 0x0002 ++#define TG_REG0_UD 0x0004 ++#define TG_REG0_LR 0x0008 ++#define COMADJ_DEFAULT 97 ++#define TOSA_LCD_I2C_DEVICEID 0x4711 // Fixme: new value ++ ++static void tosa_lcd_tg_init(struct device *dev); ++static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode); ++static void tosa_lcd_tg_off(struct device *dev); ++static void tosa_set_backlight(int intensity); ++ ++const static struct tmio_lcd_ops tosa_tc6393_lcd_ops = { ++ .init = tosa_lcd_tg_init, ++ .tg_on = tosa_lcd_tg_on, ++ .tg_off = tosa_lcd_tg_off, ++}; ++ ++static struct platform_device *tosabl_device; ++static struct i2c_driver tosa_driver; ++static struct i2c_client* tosa_i2c_dac; ++static int initialised; ++static int comadj; ++static int bl_intensity; ++static struct ssp_dev tosa_nssp_dev; ++static struct ssp_state tosa_nssp_state; ++static spinlock_t tosa_nssp_lock; ++ ++static unsigned short normal_i2c[] = { ++ DAC_BASE, ++ I2C_CLIENT_END ++}; ++I2C_CLIENT_INSMOD; ++ ++static struct corgibl_machinfo tosa_bl_machinfo = { ++ .max_intensity = 255, ++ .default_intensity = 68, ++ .limit_mask = 0x0b, ++ .set_bl_intensity = tosa_set_backlight, ++}; ++ ++int tosa_bl_intensity(void) ++{ ++ return bl_intensity; ++} ++ ++static void pxa_nssp_output(unsigned char reg, unsigned char data) ++{ ++ unsigned long flag, dummy; ++ u32 dat = ( ((reg << 5) & 0xe0) | (data & 0x1f) ); ++ spin_lock_irqsave(&tosa_nssp_lock, flag); ++ ++ ssp_config(&tosa_nssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), 0, 0, SSCR0_SerClkDiv(128)); ++ ssp_enable(&tosa_nssp_dev); ++ ++ ssp_write_word(&tosa_nssp_dev,dat); ++ ++ /* Read null data back from device to prevent SSP overflow */ ++ ssp_read_word(&tosa_nssp_dev, &dummy); ++ ssp_disable(&tosa_nssp_dev); ++ spin_unlock_irqrestore(&tosa_nssp_lock, flag); ++ ++} ++ ++static void tosa_set_backlight(int intensity) ++{ ++ if (!tosa_i2c_dac) ++ return; ++ ++ bl_intensity = intensity; ++ /* SetBacklightDuty */ ++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH2, (unsigned char)intensity); ++ ++ /* SetBacklightVR */ ++ if (intensity) ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BL_C20MA); ++ else ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BL_C20MA); ++ ++ /* bl_enable GP04=1 otherwise GP04=0*/ ++ pxa_nssp_output(TG_GPODR2, intensity ? 0x01 : 0x00); ++} ++ ++static void tosa_lcd_tg_init(struct device *dev) ++{ ++ /* L3V On */ ++ set_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON); ++ mdelay(60); ++ ++ /* TG On */ ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_TG_ON); ++ mdelay(60); ++ ++ pxa_nssp_output(TG_TPOSCTL,0x00); /* delayed 0clk TCTL signal for VGA */ ++ pxa_nssp_output(TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */ ++} ++ ++static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode) ++{ ++ const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR; ++ pxa_nssp_output(TG_PNLCTL, value | (mode->yres == 320 ? 0 : TG_REG0_VQV)); ++ ++ /* TG LCD pannel power up */ ++ pxa_nssp_output(TG_PINICTL,0x4); ++ mdelay(50); ++ ++ /* TG LCD GVSS */ ++ pxa_nssp_output(TG_PINICTL,0x0); ++ ++ if (!initialised) ++ { ++ /* after the pannel is powered up the first time, we can access the i2c bus */ ++ /* so probe for the DAC */ ++ i2c_add_driver(&tosa_driver); ++ initialised = 1; ++ mdelay(50); ++ } ++ if (tosa_i2c_dac) ++ /* set common voltage */ ++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj); ++ ++} ++ ++static void tosa_lcd_tg_off(struct device *dev) ++{ ++ /* TG LCD VHSA off */ ++ pxa_nssp_output(TG_PINICTL,0x4); ++ mdelay(50); ++ ++ /* TG LCD signal off */ ++ pxa_nssp_output(TG_PINICTL,0x6); ++ mdelay(50); ++ ++ /* TG Off */ ++ set_tc6393_gpio(&tc6393_device.dev, TOSA_TC6393_TG_ON); ++ mdelay(100); ++ ++ /* L3V Off */ ++ reset_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON); ++} ++ ++static int tosa_detect_client(struct i2c_adapter* adapter, int address, int kind) { ++ int err = 0; ++ ++ printk("Tosa-LCD: DAC detected address:0x%2.2x\n",address); ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA )) ++ goto ERROR0; ++ ++ if (!(tosa_i2c_dac = (struct i2c_client*)kzalloc(sizeof(*tosa_i2c_dac), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ //i2c_set_clientdata(tosa_i2c_dac, data); ++ tosa_i2c_dac->addr = address; ++ tosa_i2c_dac->adapter = adapter; ++ tosa_i2c_dac->driver = &tosa_driver; ++ tosa_i2c_dac->dev.parent = &tc6393_device.dev; ++ strcpy(tosa_i2c_dac->name, "tosa lcd"); ++ if ((err = i2c_attach_client(tosa_i2c_dac))) ++ goto ERROR3; ++ ++ /* Now i2c is ready, allocate the backlight device*/ ++ tosabl_device = platform_device_alloc("corgi-bl", -1); ++ if (!tosabl_device) { ++ err = -ENOMEM; ++ goto ERROR4; ++ } ++ ++ /* set parent device */ ++ tosabl_device->dev.parent = &tosa_i2c_dac->dev; ++ tosabl_device->dev.platform_data = &tosa_bl_machinfo; ++ ++ err = platform_device_add(tosabl_device); ++ ++ if (err) ++ platform_device_put(tosabl_device); ++ ++ /* set common voltage */ ++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj); ++ ++ return 0; ++ERROR4: ++ i2c_detach_client(tosa_i2c_dac); ++ERROR3: ++ kfree(tosa_i2c_dac); ++ERROR0: ++ return err; ++} ++ ++static int tosa_attach_adapter(struct i2c_adapter* adapter) { ++ return i2c_probe(adapter, &addr_data, &tosa_detect_client); ++} ++ ++static int tosa_detach_client(struct i2c_client* client) { ++ int err; ++ ++ if ((err = i2c_detach_client(client))) { ++ printk(KERN_ERR "tosa: Cannot deregister client\n"); ++ return err; ++ } ++ kfree(client); ++ return 0; ++} ++ ++static struct i2c_driver tosa_driver={ ++ .id = TOSA_LCD_I2C_DEVICEID, ++ .attach_adapter = tosa_attach_adapter, ++ .detach_client = tosa_detach_client, ++}; ++ ++static int __init tosa_lcd_probe(struct platform_device *pdev) ++{ ++ int ret; ++ spin_lock_init(&tosa_nssp_lock); ++ ++ if (!pdev->dev.platform_data) ++ return -EINVAL; ++ ++ /* Set Common Voltage */ ++ comadj = sharpsl_param.comadj == -1 ? COMADJ_DEFAULT : sharpsl_param.comadj; ++ ++ ret=ssp_init(&tosa_nssp_dev,2,0); ++ ++ /* initialize SSP */ ++ pxa_gpio_mode(GPIO83_NSSP_TX); ++ pxa_gpio_mode(GPIO81_NSSP_CLK_OUT); ++ pxa_gpio_mode(GPIO82_NSSP_FRM_OUT); ++ ++ if (ret) ++ printk(KERN_ERR "Unable to register NSSP handler!\n"); ++ else { ++ struct tmio_lcd_ops* *tmio_ops = pdev->dev.platform_data; ++ ssp_disable(&tosa_nssp_dev); ++ initialised = 0; ++ ++ /* Set the lcd functions */ ++ *tmio_ops = (struct tmio_lcd_ops*) &tosa_tc6393_lcd_ops; ++ } ++ ++ return ret; ++} ++ ++static int tosa_lcd_remove(struct platform_device *pdev) ++{ ++ /* delete the lcd functions */ ++ struct tmio_lcd_ops* *tmio_ops = pdev->dev.platform_data; ++ *tmio_ops = NULL; ++ ++ ssp_exit(&tosa_nssp_dev); ++ ++ if (tosa_i2c_dac) { ++ i2c_detach_client(tosa_i2c_dac); ++ kfree(tosa_i2c_dac); ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++ ++static int tosa_lcd_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ ssp_flush(&tosa_nssp_dev); ++ ssp_save_state(&tosa_nssp_dev,&tosa_nssp_state); ++ return 0; ++} ++ ++static int tosa_lcd_resume(struct platform_device *pdev) ++{ ++ printk("tosa_lcd_resume\n"); ++ ssp_restore_state(&tosa_nssp_dev,&tosa_nssp_state); ++ ssp_enable(&tosa_nssp_dev); ++ printk("tosa_lcd_resume ok\n"); ++ return 0; ++} ++#else ++ ++#define tosa_lcd_suspend NULL ++#define tosa_lcd_resume NULL ++ ++#endif ++ ++ ++static struct platform_driver tosalcd_driver = { ++ .probe = tosa_lcd_probe, ++ .remove = tosa_lcd_remove, ++ .suspend = tosa_lcd_suspend, ++ .resume = tosa_lcd_resume, ++ .driver = { ++ .name = "tosa-lcd", ++ }, ++}; ++ ++static int __init tosa_lcd_init(void) ++{ ++ return platform_driver_register(&tosalcd_driver); ++} ++ ++static void __exit tosa_lcd_cleanup (void) ++{ ++ platform_driver_unregister (&tosalcd_driver); ++} ++ ++device_initcall(tosa_lcd_init); ++module_exit (tosa_lcd_cleanup); ++ ++MODULE_DESCRIPTION ("Tosa LCD device"); ++MODULE_AUTHOR ("Dirk Opfer"); ++MODULE_LICENSE ("GPL v2"); +Index: git/arch/arm/mach-pxa/tosa.c +=================================================================== +--- git.orig/arch/arm/mach-pxa/tosa.c 2006-11-07 22:13:10.000000000 +0000 ++++ git/arch/arm/mach-pxa/tosa.c 2006-11-07 23:29:38.000000000 +0000 +@@ -24,6 +24,7 @@ + #include <linux/mtd/partitions.h> + #include <linux/pm.h> + #include <linux/delay.h> ++#include <linux/fb.h> + + #include <asm/setup.h> + #include <asm/memory.h> +@@ -345,7 +345,38 @@ static struct tmio_nand_platform_data to + .badblock_pattern = &tosa_tc6393_nand_bbt, + }; + +-extern struct tmio_lcd_platform_data tosa_tc6393_lcd_platform_data; ++static struct fb_videomode tosa_tc6393_lcd_mode[] = { ++ { ++ .xres = 480, ++ .yres = 640, ++ .pixclock = 0x002cdf00,/* PLL divisor */ ++ .left_margin = 0x004c, ++ .right_margin = 0x005b, ++ .upper_margin = 0x0001, ++ .lower_margin = 0x000d, ++ .hsync_len = 0x0002, ++ .vsync_len = 0x0001, ++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, ++ .vmode = FB_VMODE_NONINTERLACED, ++ },{ ++ .xres = 240, ++ .yres = 320, ++ .pixclock = 0x00e7f203,/* PLL divisor */ ++ .left_margin = 0x0024, ++ .right_margin = 0x002f, ++ .upper_margin = 0x0001, ++ .lower_margin = 0x000d, ++ .hsync_len = 0x0002, ++ .vsync_len = 0x0001, ++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, ++ .vmode = FB_VMODE_NONINTERLACED, ++}}; ++ ++struct tmio_lcd_platform_data tosa_tc6393_lcd_platform_data = { ++ .ops = NULL, ++ .modelist = tosa_tc6393_lcd_mode, ++ .num_modes = ARRAY_SIZE(tosa_tc6393_lcd_mode), ++}; + + static struct tmio_cell tosa_tc6393_cells[] = { + { +@@ -384,6 +415,19 @@ struct platform_device tc6393_device = { + .num_resources = ARRAY_SIZE(tc6393_resources), + .resource = tc6393_resources, + }; ++EXPORT_SYMBOL (tc6393_device); ++ ++/* ++ * Tosa LCD / Backlight stuff ++ */ ++static struct platform_device tosalcd_device = { ++ .name = "tosa-lcd", ++ .id = -1, ++ .dev = { ++ .parent = &tc6393_device.dev, ++ .platform_data = &tosa_tc6393_lcd_platform_data.ops, ++ }, ++}; + + static struct platform_device *devices[] __initdata = { + &tosascoop_device, +@@ -391,6 +435,7 @@ static struct platform_device *devices[] + &tosakbd_device, + &tosaled_device, + &tc6393_device, ++ &tosalcd_device, + }; + + static void tosa_poweroff(void) +Index: git/arch/arm/mach-pxa/Kconfig +=================================================================== +--- git.orig/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:10.000000000 +0000 ++++ git/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:10.000000000 +0000 +@@ -129,7 +129,10 @@ config MACH_TOSA + bool "Enable Sharp SL-6000x (Tosa) Support" + depends PXA_SHARPSL_25x + select TOSHIBA_TC6393XB +- select SHARPSL_PM ++ select I2C ++ select I2C_PXA ++ select SHARPSL_PM ++ select PXA_SSP + + config PXA25x + bool + arch/arm/mach-pxa/Kconfig | 5 + arch/arm/mach-pxa/Makefile | 2 + arch/arm/mach-pxa/tosa.c | 49 +++++- + arch/arm/mach-pxa/tosa_lcd.c | 344 +++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 396 insertions(+), 4 deletions(-) + +Index: git/arch/arm/mach-pxa/Makefile +=================================================================== +--- git.orig/arch/arm/mach-pxa/Makefile 2006-11-07 22:13:10.000000000 +0000 ++++ git/arch/arm/mach-pxa/Makefile 2006-11-07 23:29:38.000000000 +0000 +@@ -17,7 +17,7 @@ obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o + obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o + obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o + obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o +-obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o ++obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o tosa_lcd.o + obj-$(CONFIG_MACH_EM_X270) += em-x270.o + obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o + +Index: git/arch/arm/mach-pxa/tosa_lcd.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ git/arch/arm/mach-pxa/tosa_lcd.c 2006-11-07 23:29:25.000000000 +0000 +@@ -0,0 +1,344 @@ ++/* ++ * LCD / Backlight control code for Sharp SL-6000x (tosa) ++ * ++ * Copyright (c) 2005 Dirk Opfer ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <linux/i2c.h> ++#include <linux/fb.h> ++ ++#include <asm/mach/sharpsl_param.h> ++#include <asm/hardware.h> ++#include <asm/hardware/scoop.h> ++#include <asm/hardware/tmio.h> ++#include <asm/arch/ssp.h> ++#include <asm/arch/sharpsl.h> ++#include <asm/arch/tosa.h> ++#include <asm/arch/pxa-regs.h> ++ ++#define DAC_BASE 0x4e ++#define DAC_CH1 0 ++#define DAC_CH2 1 ++ ++#define TG_REG0_VQV 0x0001 ++#define TG_REG0_COLOR 0x0002 ++#define TG_REG0_UD 0x0004 ++#define TG_REG0_LR 0x0008 ++#define COMADJ_DEFAULT 97 ++#define TOSA_LCD_I2C_DEVICEID 0x4711 // Fixme: new value ++ ++static void tosa_lcd_tg_init(struct device *dev); ++static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode); ++static void tosa_lcd_tg_off(struct device *dev); ++static void tosa_set_backlight(int intensity); ++ ++const static struct tmio_lcd_ops tosa_tc6393_lcd_ops = { ++ .init = tosa_lcd_tg_init, ++ .tg_on = tosa_lcd_tg_on, ++ .tg_off = tosa_lcd_tg_off, ++}; ++ ++static struct platform_device *tosabl_device; ++static struct i2c_driver tosa_driver; ++static struct i2c_client* tosa_i2c_dac; ++static int initialised; ++static int comadj; ++static int bl_intensity; ++static struct ssp_dev tosa_nssp_dev; ++static struct ssp_state tosa_nssp_state; ++static spinlock_t tosa_nssp_lock; ++ ++static unsigned short normal_i2c[] = { ++ DAC_BASE, ++ I2C_CLIENT_END ++}; ++I2C_CLIENT_INSMOD; ++ ++static struct corgibl_machinfo tosa_bl_machinfo = { ++ .max_intensity = 255, ++ .default_intensity = 68, ++ .limit_mask = 0x0b, ++ .set_bl_intensity = tosa_set_backlight, ++}; ++ ++int tosa_bl_intensity(void) ++{ ++ return bl_intensity; ++} ++ ++static void pxa_nssp_output(unsigned char reg, unsigned char data) ++{ ++ unsigned long flag, dummy; ++ u32 dat = ( ((reg << 5) & 0xe0) | (data & 0x1f) ); ++ spin_lock_irqsave(&tosa_nssp_lock, flag); ++ ++ ssp_config(&tosa_nssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), 0, 0, SSCR0_SerClkDiv(128)); ++ ssp_enable(&tosa_nssp_dev); ++ ++ ssp_write_word(&tosa_nssp_dev,dat); ++ ++ /* Read null data back from device to prevent SSP overflow */ ++ ssp_read_word(&tosa_nssp_dev, &dummy); ++ ssp_disable(&tosa_nssp_dev); ++ spin_unlock_irqrestore(&tosa_nssp_lock, flag); ++ ++} ++ ++static void tosa_set_backlight(int intensity) ++{ ++ if (!tosa_i2c_dac) ++ return; ++ ++ bl_intensity = intensity; ++ /* SetBacklightDuty */ ++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH2, (unsigned char)intensity); ++ ++ /* SetBacklightVR */ ++ if (intensity) ++ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BL_C20MA); ++ else ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BL_C20MA); ++ ++ /* bl_enable GP04=1 otherwise GP04=0*/ ++ pxa_nssp_output(TG_GPODR2, intensity ? 0x01 : 0x00); ++} ++ ++static void tosa_lcd_tg_init(struct device *dev) ++{ ++ /* L3V On */ ++ set_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON); ++ mdelay(60); ++ ++ /* TG On */ ++ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_TG_ON); ++ mdelay(60); ++ ++ pxa_nssp_output(TG_TPOSCTL,0x00); /* delayed 0clk TCTL signal for VGA */ ++ pxa_nssp_output(TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */ ++} ++ ++static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode) ++{ ++ const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR; ++ pxa_nssp_output(TG_PNLCTL, value | (mode->yres == 320 ? 0 : TG_REG0_VQV)); ++ ++ /* TG LCD pannel power up */ ++ pxa_nssp_output(TG_PINICTL,0x4); ++ mdelay(50); ++ ++ /* TG LCD GVSS */ ++ pxa_nssp_output(TG_PINICTL,0x0); ++ ++ if (!initialised) ++ { ++ /* after the pannel is powered up the first time, we can access the i2c bus */ ++ /* so probe for the DAC */ ++ i2c_add_driver(&tosa_driver); ++ initialised = 1; ++ mdelay(50); ++ } ++ if (tosa_i2c_dac) ++ /* set common voltage */ ++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj); ++ ++} ++ ++static void tosa_lcd_tg_off(struct device *dev) ++{ ++ /* TG LCD VHSA off */ ++ pxa_nssp_output(TG_PINICTL,0x4); ++ mdelay(50); ++ ++ /* TG LCD signal off */ ++ pxa_nssp_output(TG_PINICTL,0x6); ++ mdelay(50); ++ ++ /* TG Off */ ++ set_tc6393_gpio(&tc6393_device.dev, TOSA_TC6393_TG_ON); ++ mdelay(100); ++ ++ /* L3V Off */ ++ reset_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON); ++} ++ ++static int tosa_detect_client(struct i2c_adapter* adapter, int address, int kind) { ++ int err = 0; ++ ++ printk("Tosa-LCD: DAC detected address:0x%2.2x\n",address); ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA )) ++ goto ERROR0; ++ ++ if (!(tosa_i2c_dac = (struct i2c_client*)kzalloc(sizeof(*tosa_i2c_dac), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ //i2c_set_clientdata(tosa_i2c_dac, data); ++ tosa_i2c_dac->addr = address; ++ tosa_i2c_dac->adapter = adapter; ++ tosa_i2c_dac->driver = &tosa_driver; ++ tosa_i2c_dac->dev.parent = &tc6393_device.dev; ++ strcpy(tosa_i2c_dac->name, "tosa lcd"); ++ if ((err = i2c_attach_client(tosa_i2c_dac))) ++ goto ERROR3; ++ ++ /* Now i2c is ready, allocate the backlight device*/ ++ tosabl_device = platform_device_alloc("corgi-bl", -1); ++ if (!tosabl_device) { ++ err = -ENOMEM; ++ goto ERROR4; ++ } ++ ++ /* set parent device */ ++ tosabl_device->dev.parent = &tosa_i2c_dac->dev; ++ tosabl_device->dev.platform_data = &tosa_bl_machinfo; ++ ++ err = platform_device_add(tosabl_device); ++ ++ if (err) ++ platform_device_put(tosabl_device); ++ ++ /* set common voltage */ ++ i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj); ++ ++ return 0; ++ERROR4: ++ i2c_detach_client(tosa_i2c_dac); ++ERROR3: ++ kfree(tosa_i2c_dac); ++ERROR0: ++ return err; ++} ++ ++static int tosa_attach_adapter(struct i2c_adapter* adapter) { ++ return i2c_probe(adapter, &addr_data, &tosa_detect_client); ++} ++ ++static int tosa_detach_client(struct i2c_client* client) { ++ int err; ++ ++ if ((err = i2c_detach_client(client))) { ++ printk(KERN_ERR "tosa: Cannot deregister client\n"); ++ return err; ++ } ++ kfree(client); ++ return 0; ++} ++ ++static struct i2c_driver tosa_driver={ ++ .id = TOSA_LCD_I2C_DEVICEID, ++ .attach_adapter = tosa_attach_adapter, ++ .detach_client = tosa_detach_client, ++}; ++ ++static int __init tosa_lcd_probe(struct platform_device *pdev) ++{ ++ int ret; ++ spin_lock_init(&tosa_nssp_lock); ++ ++ if (!pdev->dev.platform_data) ++ return -EINVAL; ++ ++ /* Set Common Voltage */ ++ comadj = sharpsl_param.comadj == -1 ? COMADJ_DEFAULT : sharpsl_param.comadj; ++ ++ ret=ssp_init(&tosa_nssp_dev,2,0); ++ ++ /* initialize SSP */ ++ pxa_gpio_mode(GPIO83_NSSP_TX); ++ pxa_gpio_mode(GPIO81_NSSP_CLK_OUT); ++ pxa_gpio_mode(GPIO82_NSSP_FRM_OUT); ++ ++ if (ret) ++ printk(KERN_ERR "Unable to register NSSP handler!\n"); ++ else { ++ struct tmio_lcd_ops* *tmio_ops = pdev->dev.platform_data; ++ ssp_disable(&tosa_nssp_dev); ++ initialised = 0; ++ ++ /* Set the lcd functions */ ++ *tmio_ops = (struct tmio_lcd_ops*) &tosa_tc6393_lcd_ops; ++ } ++ ++ return ret; ++} ++ ++static int tosa_lcd_remove(struct platform_device *pdev) ++{ ++ /* delete the lcd functions */ ++ struct tmio_lcd_ops* *tmio_ops = pdev->dev.platform_data; ++ *tmio_ops = NULL; ++ ++ ssp_exit(&tosa_nssp_dev); ++ ++ if (tosa_i2c_dac) { ++ i2c_detach_client(tosa_i2c_dac); ++ kfree(tosa_i2c_dac); ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++ ++static int tosa_lcd_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ ssp_flush(&tosa_nssp_dev); ++ ssp_save_state(&tosa_nssp_dev,&tosa_nssp_state); ++ return 0; ++} ++ ++static int tosa_lcd_resume(struct platform_device *pdev) ++{ ++ printk("tosa_lcd_resume\n"); ++ ssp_restore_state(&tosa_nssp_dev,&tosa_nssp_state); ++ ssp_enable(&tosa_nssp_dev); ++ printk("tosa_lcd_resume ok\n"); ++ return 0; ++} ++#else ++ ++#define tosa_lcd_suspend NULL ++#define tosa_lcd_resume NULL ++ ++#endif ++ ++ ++static struct platform_driver tosalcd_driver = { ++ .probe = tosa_lcd_probe, ++ .remove = tosa_lcd_remove, ++ .suspend = tosa_lcd_suspend, ++ .resume = tosa_lcd_resume, ++ .driver = { ++ .name = "tosa-lcd", ++ }, ++}; ++ ++static int __init tosa_lcd_init(void) ++{ ++ return platform_driver_register(&tosalcd_driver); ++} ++ ++static void __exit tosa_lcd_cleanup (void) ++{ ++ platform_driver_unregister (&tosalcd_driver); ++} ++ ++device_initcall(tosa_lcd_init); ++module_exit (tosa_lcd_cleanup); ++ ++MODULE_DESCRIPTION ("Tosa LCD device"); ++MODULE_AUTHOR ("Dirk Opfer"); ++MODULE_LICENSE ("GPL v2"); +Index: git/arch/arm/mach-pxa/tosa.c +=================================================================== +--- git.orig/arch/arm/mach-pxa/tosa.c 2006-11-07 22:13:10.000000000 +0000 ++++ git/arch/arm/mach-pxa/tosa.c 2006-11-07 23:29:38.000000000 +0000 +@@ -24,6 +24,7 @@ + #include <linux/mtd/partitions.h> + #include <linux/pm.h> + #include <linux/delay.h> ++#include <linux/fb.h> + + #include <asm/setup.h> + #include <asm/memory.h> +@@ -345,7 +345,38 @@ static struct tmio_nand_platform_data to + .badblock_pattern = &tosa_tc6393_nand_bbt, + }; + +-extern struct tmio_lcd_platform_data tosa_tc6393_lcd_platform_data; ++static struct fb_videomode tosa_tc6393_lcd_mode[] = { ++ { ++ .xres = 480, ++ .yres = 640, ++ .pixclock = 0x002cdf00,/* PLL divisor */ ++ .left_margin = 0x004c, ++ .right_margin = 0x005b, ++ .upper_margin = 0x0001, ++ .lower_margin = 0x000d, ++ .hsync_len = 0x0002, ++ .vsync_len = 0x0001, ++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, ++ .vmode = FB_VMODE_NONINTERLACED, ++ },{ ++ .xres = 240, ++ .yres = 320, ++ .pixclock = 0x00e7f203,/* PLL divisor */ ++ .left_margin = 0x0024, ++ .right_margin = 0x002f, ++ .upper_margin = 0x0001, ++ .lower_margin = 0x000d, ++ .hsync_len = 0x0002, ++ .vsync_len = 0x0001, ++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, ++ .vmode = FB_VMODE_NONINTERLACED, ++}}; ++ ++struct tmio_lcd_platform_data tosa_tc6393_lcd_platform_data = { ++ .ops = NULL, ++ .modelist = tosa_tc6393_lcd_mode, ++ .num_modes = ARRAY_SIZE(tosa_tc6393_lcd_mode), ++}; + + static struct tmio_cell tosa_tc6393_cells[] = { + { +@@ -384,6 +415,19 @@ struct platform_device tc6393_device = { + .num_resources = ARRAY_SIZE(tc6393_resources), + .resource = tc6393_resources, + }; ++EXPORT_SYMBOL (tc6393_device); ++ ++/* ++ * Tosa LCD / Backlight stuff ++ */ ++static struct platform_device tosalcd_device = { ++ .name = "tosa-lcd", ++ .id = -1, ++ .dev = { ++ .parent = &tc6393_device.dev, ++ .platform_data = &tosa_tc6393_lcd_platform_data.ops, ++ }, ++}; + + static struct platform_device *devices[] __initdata = { + &tosascoop_device, +@@ -391,6 +435,7 @@ static struct platform_device *devices[] + &tosakbd_device, + &tosaled_device, + &tc6393_device, ++ &tosalcd_device, + }; + + static void tosa_poweroff(void) +Index: git/arch/arm/mach-pxa/Kconfig +=================================================================== +--- git.orig/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:10.000000000 +0000 ++++ git/arch/arm/mach-pxa/Kconfig 2006-11-07 22:13:10.000000000 +0000 +@@ -129,7 +129,10 @@ config MACH_TOSA + bool "Enable Sharp SL-6000x (Tosa) Support" + depends PXA_SHARPSL_25x + select TOSHIBA_TC6393XB +- select SHARPSL_PM ++ select I2C ++ select I2C_PXA ++ select SHARPSL_PM ++ select PXA_SSP + + config PXA25x + bool diff --git a/packages/linux/linux-rp-2.6.23/wm9712-reset-loop-r2.patch b/packages/linux/linux-rp-2.6.23/wm9712-reset-loop-r2.patch new file mode 100644 index 0000000000..0e32a62ea6 --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/wm9712-reset-loop-r2.patch @@ -0,0 +1,88 @@ + sound/soc/codecs/wm9712.c | 28 ++++++++++++++++++---------- + 1 file changed, 18 insertions(+), 10 deletions(-) + +Index: git/sound/soc/codecs/wm9712.c +=================================================================== +--- git.orig/sound/soc/codecs/wm9712.c 2006-11-07 22:10:01.000000000 +0000 ++++ git/sound/soc/codecs/wm9712.c 2006-11-07 22:11:50.000000000 +0000 +@@ -618,18 +618,26 @@ static int wm9712_dapm_event(struct snd_ + + static int wm9712_reset(struct snd_soc_codec *codec, int try_warm) + { +- if (try_warm && soc_ac97_ops.warm_reset) { +- soc_ac97_ops.warm_reset(codec->ac97); +- if (!(ac97_read(codec, 0) & 0x8000)) +- return 1; +- } ++ int retry = 3; + +- soc_ac97_ops.reset(codec->ac97); +- if (ac97_read(codec, 0) & 0x8000) +- goto err; +- return 0; ++ while (retry--) ++ { ++ if(try_warm && soc_ac97_ops.warm_reset) { ++ soc_ac97_ops.warm_reset(codec->ac97); ++ if(ac97_read(codec, 0) & 0x8000) ++ continue; ++ else ++ return 1; ++ } ++ ++ soc_ac97_ops.reset(codec->ac97); ++ if(ac97_read(codec, 0) & 0x8000) ++ continue; ++ else ++ return 0; ++ ++ } + +-err: + printk(KERN_ERR "WM9712 AC97 reset failed\n"); + return -EIO; + } + sound/soc/codecs/wm9712.c | 28 ++++++++++++++++++---------- + 1 file changed, 18 insertions(+), 10 deletions(-) + +Index: git/sound/soc/codecs/wm9712.c +=================================================================== +--- git.orig/sound/soc/codecs/wm9712.c 2006-11-07 22:10:01.000000000 +0000 ++++ git/sound/soc/codecs/wm9712.c 2006-11-07 22:11:50.000000000 +0000 +@@ -618,18 +618,26 @@ static int wm9712_dapm_event(struct snd_ + + static int wm9712_reset(struct snd_soc_codec *codec, int try_warm) + { +- if (try_warm && soc_ac97_ops.warm_reset) { +- soc_ac97_ops.warm_reset(codec->ac97); +- if (!(ac97_read(codec, 0) & 0x8000)) +- return 1; +- } ++ int retry = 3; + +- soc_ac97_ops.reset(codec->ac97); +- if (ac97_read(codec, 0) & 0x8000) +- goto err; +- return 0; ++ while (retry--) ++ { ++ if(try_warm && soc_ac97_ops.warm_reset) { ++ soc_ac97_ops.warm_reset(codec->ac97); ++ if(ac97_read(codec, 0) & 0x8000) ++ continue; ++ else ++ return 1; ++ } ++ ++ soc_ac97_ops.reset(codec->ac97); ++ if(ac97_read(codec, 0) & 0x8000) ++ continue; ++ else ++ return 0; ++ ++ } + +-err: + printk(KERN_ERR "WM9712 AC97 reset failed\n"); + return -EIO; + } diff --git a/packages/linux/linux-rp-2.6.23/wm9712-suspend-cold-res-r2.patch b/packages/linux/linux-rp-2.6.23/wm9712-suspend-cold-res-r2.patch new file mode 100644 index 0000000000..cbf854d772 --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/wm9712-suspend-cold-res-r2.patch @@ -0,0 +1,32 @@ + sound/soc/codecs/wm9712.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Index: git/sound/soc/codecs/wm9712.c +=================================================================== +--- git.orig/sound/soc/codecs/wm9712.c 2006-11-07 21:57:34.000000000 +0000 ++++ git/sound/soc/codecs/wm9712.c 2006-11-07 21:59:30.000000000 +0000 +@@ -651,7 +651,7 @@ static int wm9712_soc_resume(struct plat + int i, ret; + u16 *cache = codec->reg_cache; + +- ret = wm9712_reset(codec, 1); ++ ret = wm9712_reset(codec, 0); + if (ret < 0){ + printk(KERN_ERR "could not reset AC97 codec\n"); + return ret; + sound/soc/codecs/wm9712.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Index: git/sound/soc/codecs/wm9712.c +=================================================================== +--- git.orig/sound/soc/codecs/wm9712.c 2006-11-07 21:57:34.000000000 +0000 ++++ git/sound/soc/codecs/wm9712.c 2006-11-07 21:59:30.000000000 +0000 +@@ -651,7 +651,7 @@ static int wm9712_soc_resume(struct plat + int i, ret; + u16 *cache = codec->reg_cache; + +- ret = wm9712_reset(codec, 1); ++ ret = wm9712_reset(codec, 0); + if (ret < 0){ + printk(KERN_ERR "could not reset AC97 codec\n"); + return ret; diff --git a/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0-fix-r0.patch b/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0-fix-r0.patch new file mode 100644 index 0000000000..f246fbfed5 --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0-fix-r0.patch @@ -0,0 +1,256 @@ + drivers/input/power.c | 2 +- + drivers/input/touchscreen/Kconfig | 2 +- + drivers/input/touchscreen/wm97xx-core.c | 35 ++++++++++++++++--------------- + include/linux/wm97xx.h | 2 +- + 4 files changed, 21 insertions(+), 20 deletions(-) + +diff --git a/drivers/input/power.c b/drivers/input/power.c +index 4443e34..7aac875 100644 +--- a/drivers/input/power.c ++++ b/drivers/input/power.c +@@ -156,7 +156,7 @@ static void power_event(struct input_handle *handle, unsigned int type, + } + } + +-static struct input_handle *power_connect(struct input_handler *handler, ++static int power_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) + { +diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig +index 6862e8f..9b532e9 100644 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -247,7 +247,7 @@ config TOUCHSCREEN_TSC2101 + + config TOUCHSCREEN_WM97XX + tristate "Support for WM97xx AC97 touchscreen controllers" +- depends SND_AC97_BUS ++ depends AC97_BUS + + choice + prompt "WM97xx codec type" +diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c +index 9b2710e..d3ce3f3 100644 +--- a/drivers/input/touchscreen/wm97xx-core.c ++++ b/drivers/input/touchscreen/wm97xx-core.c +@@ -84,6 +84,7 @@ + #include <linux/bitops.h> + #include <linux/workqueue.h> + #include <linux/device.h> ++#include <linux/freezer.h> + #include <linux/wm97xx.h> + #include <asm/uaccess.h> + #include <asm/io.h> +@@ -241,14 +242,15 @@ WM97XX_STATUS_ATTR(gpio); + + static int wm97xx_sys_add(struct device *dev) + { ++ int err; + if (aux_sys) { +- device_create_file(dev, &dev_attr_aux1); +- device_create_file(dev, &dev_attr_aux2); +- device_create_file(dev, &dev_attr_aux3); +- device_create_file(dev, &dev_attr_aux4); ++ err = device_create_file(dev, &dev_attr_aux1); ++ err = device_create_file(dev, &dev_attr_aux2); ++ err = device_create_file(dev, &dev_attr_aux3); ++ err = device_create_file(dev, &dev_attr_aux4); + } + if (status_sys) +- device_create_file(dev, &dev_attr_gpio); ++ err = device_create_file(dev, &dev_attr_gpio); + return 0; + } + +@@ -366,12 +368,12 @@ void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir, + + /* + * Handle a pen down interrupt. +- */ +-static void wm97xx_pen_irq_worker(void *ptr) +-{ +- struct wm97xx *wm = (struct wm97xx *) ptr; +- +- /* do we need to enable the touch panel reader */ ++ */ ++static void wm97xx_pen_irq_worker(struct work_struct *work) ++{ ++ struct wm97xx *wm = container_of(work, struct wm97xx, pen_event_work); ++ ++ /* do we need to enable the touch panel reader */ + if (wm->id == WM9705_ID2) { + if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN) + wm->pen_is_down = 1; +@@ -411,9 +413,8 @@ static void wm97xx_pen_irq_worker(void *ptr) + * We have to disable the codec interrupt in the handler because it can + * take upto 1ms to clear the interrupt source. The interrupt is then enabled + * again in the slow handler when the source has been cleared. +- */ +-static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id, +- struct pt_regs *regs) ++ */ ++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id) + { + struct wm97xx *wm = (struct wm97xx *) dev_id; + disable_irq(wm->pen_irq); +@@ -428,15 +429,15 @@ static int wm97xx_init_pen_irq(struct wm97xx *wm) + { + u16 reg; + +- INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker, wm); +- if ((wm->pen_irq_workq = ++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker); ++ if ((wm->pen_irq_workq = + create_singlethread_workqueue("kwm97pen")) == NULL) { + err("could not create pen irq work queue"); + wm->pen_irq = 0; + return -EINVAL; + } + +- if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) { ++ if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, IRQF_SHARED, "wm97xx-pen", wm)) { + err("could not register codec pen down interrupt, will poll for pen down"); + destroy_workqueue(wm->pen_irq_workq); + wm->pen_irq = 0; +diff --git a/include/linux/wm97xx.h b/include/linux/wm97xx.h +index b1c1740..a9bd57e 100644 +--- a/include/linux/wm97xx.h ++++ b/include/linux/wm97xx.h +@@ -243,7 +243,7 @@ struct wm97xx { + u16 dig_save[3]; /* saved during aux reading */ + struct wm97xx_codec_drv *codec; /* attached codec driver*/ + struct input_dev* input_dev; /* touchscreen input device */ +- ac97_t *ac97; /* ALSA codec access */ ++ struct snd_ac97 *ac97; /* ALSA codec access */ + struct device *dev; /* ALSA device */ + struct device *battery_dev; + struct device *touch_dev; + drivers/input/power.c | 2 +- + drivers/input/touchscreen/Kconfig | 2 +- + drivers/input/touchscreen/wm97xx-core.c | 35 ++++++++++++++++--------------- + include/linux/wm97xx.h | 2 +- + 4 files changed, 21 insertions(+), 20 deletions(-) + +diff --git a/drivers/input/power.c b/drivers/input/power.c +index 4443e34..7aac875 100644 +--- a/drivers/input/power.c ++++ b/drivers/input/power.c +@@ -156,7 +156,7 @@ static void power_event(struct input_handle *handle, unsigned int type, + } + } + +-static struct input_handle *power_connect(struct input_handler *handler, ++static int power_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) + { +diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig +index 6862e8f..9b532e9 100644 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -247,7 +247,7 @@ config TOUCHSCREEN_TSC2101 + + config TOUCHSCREEN_WM97XX + tristate "Support for WM97xx AC97 touchscreen controllers" +- depends SND_AC97_BUS ++ depends AC97_BUS + + choice + prompt "WM97xx codec type" +diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c +index 9b2710e..d3ce3f3 100644 +--- a/drivers/input/touchscreen/wm97xx-core.c ++++ b/drivers/input/touchscreen/wm97xx-core.c +@@ -84,6 +84,7 @@ + #include <linux/bitops.h> + #include <linux/workqueue.h> + #include <linux/device.h> ++#include <linux/freezer.h> + #include <linux/wm97xx.h> + #include <asm/uaccess.h> + #include <asm/io.h> +@@ -241,14 +242,15 @@ WM97XX_STATUS_ATTR(gpio); + + static int wm97xx_sys_add(struct device *dev) + { ++ int err; + if (aux_sys) { +- device_create_file(dev, &dev_attr_aux1); +- device_create_file(dev, &dev_attr_aux2); +- device_create_file(dev, &dev_attr_aux3); +- device_create_file(dev, &dev_attr_aux4); ++ err = device_create_file(dev, &dev_attr_aux1); ++ err = device_create_file(dev, &dev_attr_aux2); ++ err = device_create_file(dev, &dev_attr_aux3); ++ err = device_create_file(dev, &dev_attr_aux4); + } + if (status_sys) +- device_create_file(dev, &dev_attr_gpio); ++ err = device_create_file(dev, &dev_attr_gpio); + return 0; + } + +@@ -366,12 +368,12 @@ void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir, + + /* + * Handle a pen down interrupt. +- */ +-static void wm97xx_pen_irq_worker(void *ptr) +-{ +- struct wm97xx *wm = (struct wm97xx *) ptr; +- +- /* do we need to enable the touch panel reader */ ++ */ ++static void wm97xx_pen_irq_worker(struct work_struct *work) ++{ ++ struct wm97xx *wm = container_of(work, struct wm97xx, pen_event_work); ++ ++ /* do we need to enable the touch panel reader */ + if (wm->id == WM9705_ID2) { + if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN) + wm->pen_is_down = 1; +@@ -411,9 +413,8 @@ static void wm97xx_pen_irq_worker(void *ptr) + * We have to disable the codec interrupt in the handler because it can + * take upto 1ms to clear the interrupt source. The interrupt is then enabled + * again in the slow handler when the source has been cleared. +- */ +-static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id, +- struct pt_regs *regs) ++ */ ++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id) + { + struct wm97xx *wm = (struct wm97xx *) dev_id; + disable_irq(wm->pen_irq); +@@ -428,15 +429,15 @@ static int wm97xx_init_pen_irq(struct wm97xx *wm) + { + u16 reg; + +- INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker, wm); +- if ((wm->pen_irq_workq = ++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker); ++ if ((wm->pen_irq_workq = + create_singlethread_workqueue("kwm97pen")) == NULL) { + err("could not create pen irq work queue"); + wm->pen_irq = 0; + return -EINVAL; + } + +- if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) { ++ if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, IRQF_SHARED, "wm97xx-pen", wm)) { + err("could not register codec pen down interrupt, will poll for pen down"); + destroy_workqueue(wm->pen_irq_workq); + wm->pen_irq = 0; +diff --git a/include/linux/wm97xx.h b/include/linux/wm97xx.h +index b1c1740..a9bd57e 100644 +--- a/include/linux/wm97xx.h ++++ b/include/linux/wm97xx.h +@@ -243,7 +243,7 @@ struct wm97xx { + u16 dig_save[3]; /* saved during aux reading */ + struct wm97xx_codec_drv *codec; /* attached codec driver*/ + struct input_dev* input_dev; /* touchscreen input device */ +- ac97_t *ac97; /* ALSA codec access */ ++ struct snd_ac97 *ac97; /* ALSA codec access */ + struct device *dev; /* ALSA device */ + struct device *battery_dev; + struct device *touch_dev; diff --git a/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0.patch b/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0.patch new file mode 100644 index 0000000000..b029ccc066 --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0.patch @@ -0,0 +1,5798 @@ +Index: linux-2.6.17/drivers/input/touchscreen/Kconfig +=================================================================== +--- linux-2.6.17.orig/drivers/input/touchscreen/Kconfig 2006-09-19 20:35:35.060495500 +0200 ++++ linux-2.6.17/drivers/input/touchscreen/Kconfig 2006-09-19 20:36:47.965051750 +0200 +@@ -121,4 +121,57 @@ config TOUCHSCREEN_TSC2101 + To compile this driver as a module, choose M here: the + module will be called ads7846_ts. + ++config TOUCHSCREEN_WM97XX ++ tristate "Support for WM97xx AC97 touchscreen controllers" ++ depends SND_AC97_BUS ++ ++choice ++ prompt "WM97xx codec type" ++ ++config TOUCHSCREEN_WM9705 ++ bool "WM9705 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have the wm9705 touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9705. ++ ++config TOUCHSCREEN_WM9712 ++ bool "WM9712 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have the wm9712 touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9712. ++ ++config TOUCHSCREEN_WM9713 ++ bool "WM9713 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have the wm9713 touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9713. ++ ++endchoice ++ ++config TOUCHSCREEN_WM97XX_PXA ++ tristate "WM97xx PXA accelerated touch" ++ depends on TOUCHSCREEN_WM97XX && ARCH_PXA ++ help ++ Say Y here for continuous mode touch on the PXA ++ ++ If unsure, say N ++ ++ To compile this driver as a module, choose M here: the ++ module will be called pxa-wm97xx ++ + endif +Index: linux-2.6.17/drivers/input/touchscreen/Makefile +=================================================================== +--- linux-2.6.17.orig/drivers/input/touchscreen/Makefile 2006-09-19 20:35:35.072496250 +0200 ++++ linux-2.6.17/drivers/input/touchscreen/Makefile 2006-09-19 20:37:40.540337500 +0200 +@@ -4,6 +4,8 @@ + + # Each configuration option enables a list of files. + ++wm97xx-ts-objs := wm97xx-core.o ++ + obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o + obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o + obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o +@@ -13,3 +15,16 @@ obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtou + obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o + obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o + obj-$(CONFIG_TOUCHSCREEN_TSC2101) += tsc2101_ts.o ++obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o ++obj-$(CONFIG_TOUCHSCREEN_WM97XX_PXA) += pxa-wm97xx.o ++ ++ifeq ($(CONFIG_TOUCHSCREEN_WM9713),y) ++wm97xx-ts-objs += wm9713.o ++endif ++ ++ifeq ($(CONFIG_TOUCHSCREEN_WM9712),y) ++wm97xx-ts-objs += wm9712.o ++endif ++ifeq ($(CONFIG_TOUCHSCREEN_WM9705),y) ++wm97xx-ts-objs += wm9705.o ++endif +Index: linux-2.6.17/drivers/input/touchscreen/pxa-wm97xx.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/pxa-wm97xx.c 2006-09-19 20:36:47.965051750 +0200 +@@ -0,0 +1,289 @@ ++/* ++ * pxa-wm97xx.c -- pxa-wm97xx Continuous Touch screen driver for ++ * Wolfson WM97xx AC97 Codecs. ++ * ++ * Copyright 2004 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Notes: ++ * This is a wm97xx extended touch driver to capture touch ++ * data in a continuous manner on the Intel XScale archictecture ++ * ++ * Features: ++ * - codecs supported:- WM9705, WM9712, WM9713 ++ * - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x ++ * ++ * Revision history ++ * 18th Aug 2004 Initial version. ++ * 26th Jul 2005 Improved continous read back and added FIFO flushing. ++ * 06th Sep 2005 Mike Arthur <linux@wolfsonmicro.com> ++ * Moved to using the wm97xx bus ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/irq.h> ++#include <linux/wm97xx.h> ++#include <asm/io.h> ++#include <asm/arch/pxa-regs.h> ++ ++#define VERSION "0.13" ++ ++struct continuous { ++ u16 id; /* codec id */ ++ u8 code; /* continuous code */ ++ u8 reads; /* number of coord reads per read cycle */ ++ u32 speed; /* number of coords per second */ ++}; ++ ++#define WM_READS(sp) ((sp / HZ) + 1) ++ ++static const struct continuous cinfo[] = { ++ {WM9705_ID2, 0, WM_READS(94), 94}, ++ {WM9705_ID2, 1, WM_READS(188), 188}, ++ {WM9705_ID2, 2, WM_READS(375), 375}, ++ {WM9705_ID2, 3, WM_READS(750), 750}, ++ {WM9712_ID2, 0, WM_READS(94), 94}, ++ {WM9712_ID2, 1, WM_READS(188), 188}, ++ {WM9712_ID2, 2, WM_READS(375), 375}, ++ {WM9712_ID2, 3, WM_READS(750), 750}, ++ {WM9713_ID2, 0, WM_READS(94), 94}, ++ {WM9713_ID2, 1, WM_READS(120), 120}, ++ {WM9713_ID2, 2, WM_READS(154), 154}, ++ {WM9713_ID2, 3, WM_READS(188), 188}, ++}; ++ ++/* continuous speed index */ ++static int sp_idx = 0; ++static u16 last = 0, tries = 0; ++ ++/* ++ * Pen sampling frequency (Hz) in continuous mode. ++ */ ++static int cont_rate = 200; ++module_param(cont_rate, int, 0); ++MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)"); ++ ++/* ++ * Pen down detection. ++ * ++ * This driver can either poll or use an interrupt to indicate a pen down ++ * event. If the irq request fails then it will fall back to polling mode. ++ */ ++static int pen_int = 1; ++module_param(pen_int, int, 0); ++MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)"); ++ ++/* ++ * Pressure readback. ++ * ++ * Set to 1 to read back pen down pressure ++ */ ++static int pressure = 0; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)"); ++ ++/* ++ * AC97 touch data slot. ++ * ++ * Touch screen readback data ac97 slot ++ */ ++static int ac97_touch_slot = 5; ++module_param(ac97_touch_slot, int, 0); ++MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number"); ++ ++ ++/* flush AC97 slot 5 FIFO on pxa machines */ ++#ifdef CONFIG_PXA27x ++void wm97xx_acc_pen_up (struct wm97xx* wm) ++{ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ ++ while (MISR & (1 << 2)) ++ MODR; ++} ++#else ++void wm97xx_acc_pen_up (struct wm97xx* wm) ++{ ++ int count = 16; ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ ++ while (count < 16) { ++ MODR; ++ count--; ++ } ++} ++#endif ++ ++int wm97xx_acc_pen_down (struct wm97xx* wm) ++{ ++ u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES; ++ int reads = 0; ++ ++ /* data is never immediately available after pen down irq */ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ ++ if (tries > 5){ ++ tries = 0; ++ return RC_PENUP; ++ } ++ ++ x = MODR; ++ if (x == last) { ++ tries++; ++ return RC_AGAIN; ++ } ++ last = x; ++ do { ++ if (reads) ++ x= MODR; ++ y= MODR; ++ if (pressure) ++ p = MODR; ++ ++ /* are samples valid */ ++ if ((x & 0x7000) != WM97XX_ADCSEL_X || ++ (y & 0x7000) != WM97XX_ADCSEL_Y || ++ (p & 0x7000) != WM97XX_ADCSEL_PRES) ++ goto up; ++ ++ /* coordinate is good */ ++ tries = 0; ++ //printk("x %x y %x p %x\n", x,y,p); ++ input_report_abs (wm->input_dev, ABS_X, x & 0xfff); ++ input_report_abs (wm->input_dev, ABS_Y, y & 0xfff); ++ input_report_abs (wm->input_dev, ABS_PRESSURE, p & 0xfff); ++ input_sync (wm->input_dev); ++ reads++; ++ } while (reads < cinfo[sp_idx].reads); ++up: ++ return RC_PENDOWN | RC_AGAIN; ++} ++ ++int wm97xx_acc_startup(struct wm97xx* wm) ++{ ++ int idx = 0; ++ ++ /* check we have a codec */ ++ if (wm->ac97 == NULL) ++ return -ENODEV; ++ ++ /* Go you big red fire engine */ ++ for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) { ++ if (wm->id != cinfo[idx].id) ++ continue; ++ sp_idx = idx; ++ if (cont_rate <= cinfo[idx].speed) ++ break; ++ } ++ wm->acc_rate = cinfo[sp_idx].code; ++ wm->acc_slot = ac97_touch_slot; ++ printk(KERN_INFO "pxa2xx accelerated touchscreen driver, %d samples (sec)\n", ++ cinfo[sp_idx].speed); ++ ++ /* codec specific irq config */ ++ if (pen_int) { ++ switch (wm->id) { ++ case WM9705_ID2: ++ wm->pen_irq = IRQ_GPIO(4); ++ set_irq_type(IRQ_GPIO(4), IRQT_BOTHEDGE); ++ break; ++ case WM9712_ID2: ++ case WM9713_ID2: ++ /* enable pen down interrupt */ ++ /* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */ ++ wm->pen_irq = MAINSTONE_AC97_IRQ; ++ wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, ++ WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_STICKY, WM97XX_GPIO_WAKE); ++ wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT, ++ WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_NOTSTICKY, WM97XX_GPIO_NOWAKE); ++ break; ++ default: ++ printk(KERN_WARNING "pen down irq not supported on this device\n"); ++ pen_int = 0; ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++void wm97xx_acc_shutdown(struct wm97xx* wm) ++{ ++ /* codec specific deconfig */ ++ if (pen_int) { ++ switch (wm->id & 0xffff) { ++ case WM9705_ID2: ++ wm->pen_irq = 0; ++ break; ++ case WM9712_ID2: ++ case WM9713_ID2: ++ /* disable interrupt */ ++ wm->pen_irq = 0; ++ break; ++ } ++ } ++} ++ ++static struct wm97xx_mach_ops pxa_mach_ops = { ++ .acc_enabled = 1, ++ .acc_pen_up = wm97xx_acc_pen_up, ++ .acc_pen_down = wm97xx_acc_pen_down, ++ .acc_startup = wm97xx_acc_startup, ++ .acc_shutdown = wm97xx_acc_shutdown, ++}; ++ ++int pxa_wm97xx_probe(struct device *dev) ++{ ++ struct wm97xx *wm = dev->driver_data; ++ return wm97xx_register_mach_ops (wm, &pxa_mach_ops); ++} ++ ++int pxa_wm97xx_remove(struct device *dev) ++{ ++ struct wm97xx *wm = dev->driver_data; ++ wm97xx_unregister_mach_ops (wm); ++ return 0; ++} ++ ++static struct device_driver pxa_wm97xx_driver = { ++ .name = "wm97xx-touchscreen", ++ .bus = &wm97xx_bus_type, ++ .owner = THIS_MODULE, ++ .probe = pxa_wm97xx_probe, ++ .remove = pxa_wm97xx_remove ++}; ++ ++static int __init pxa_wm97xx_init(void) ++{ ++ return driver_register(&pxa_wm97xx_driver); ++} ++ ++static void __exit pxa_wm97xx_exit(void) ++{ ++ driver_unregister(&pxa_wm97xx_driver); ++} ++ ++module_init(pxa_wm97xx_init); ++module_exit(pxa_wm97xx_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>"); ++MODULE_DESCRIPTION("wm97xx continuous touch driver for pxa2xx"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/drivers/input/touchscreen/wm9705.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/wm9705.c 2006-09-19 20:36:47.969052000 +0200 +@@ -0,0 +1,360 @@ ++/* ++ * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Revision history ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added pre and post sample calls. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9705_VERSION "0.62" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil = 0; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 4; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Pen detect comparator threshold. ++ * ++ * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold ++ * i.e. 1 = Vmid/15 threshold ++ * 15 = Vmid/1 threshold ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down events. ++ */ ++static int pdd = 8; ++module_param(pdd, int, 0); ++MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold"); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask = 0; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, // 1 AC97 Link frames ++ 42, // 2 ++ 84, // 4 ++ 167, // 8 ++ 333, // 16 ++ 667, // 32 ++ 1000, // 48 ++ 1333, // 64 ++ 2000, // 96 ++ 2667, // 128 ++ 3333, // 160 ++ 4000, // 192 ++ 4667, // 224 ++ 5333, // 256 ++ 6000, // 288 ++ 0 // No delay, switch matrix always on ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay (3 * AC97_LINK_FRAME + delay_table [d]); ++} ++ ++/* ++ * set up the physical settings of the WM9705 ++ */ ++static void init_wm9705_phy(struct wm97xx* wm) ++{ ++ u16 dig1 = 0, dig2 = WM97XX_RPR; ++ ++ /* ++ * mute VIDEO and AUX as they share X and Y touchscreen ++ * inputs on the WM9705 ++ */ ++ wm97xx_reg_write(wm, AC97_AUX, 0x8000); ++ wm97xx_reg_write(wm, AC97_VIDEO, 0x8000); ++ ++ /* touchpanel pressure current*/ ++ if (pil == 2) { ++ dig2 |= WM9705_PIL; ++ dbg("setting pressure measurement current to 400uA."); ++ } else if (pil) ++ dbg("setting pressure measurement current to 200uA."); ++ if(!pil) ++ pressure = 0; ++ ++ /* polling mode sample settling delay */ ++ if (delay!=4) { ++ if (delay < 0 || delay > 15) { ++ dbg("supplied delay out of range."); ++ delay = 4; ++ } ++ } ++ dig1 &= 0xff0f; ++ dig1 |= WM97XX_DELAY(delay); ++ dbg("setting adc sample delay to %d u Secs.", delay_table[delay]); ++ ++ /* WM9705 pdd */ ++ dig2 |= (pdd & 0x000f); ++ dbg("setting pdd to Vmid/%d", 1 - (pdd & 0x000f)); ++ ++ /* mask */ ++ dig2 |= ((mask & 0x3) << 4); ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++} ++ ++static int wm9705_digitiser_ioctl(struct wm97xx* wm, int cmd) ++{ ++ switch(cmd) { ++ case WM97XX_DIG_START: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ break; ++ case WM97XX_DIG_STOP: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] & ~WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_AUX_PREPARE: ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_DIG_RESTORE: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); ++ break; ++ case WM97XX_PHY_INIT: ++ init_wm9705_phy(wm); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static inline int is_pden (struct wm97xx* wm) ++{ ++ return wm->dig[2] & WM9705_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9705 adc in polling mode. ++ */ ++static int wm9705_poll_sample (struct wm97xx* wm, int adcsel, int *sample) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = ((adcsel & 0x7fff) + 3) << 12; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay (delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { ++ dbg ("adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSEL_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Sample the WM9705 touchscreen in polling mode ++ */ ++static int wm9705_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID) ++ return rc; ++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID) ++ return rc; ++ if (pil) { ++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9705 continuous mode, i.e. touch data is streamed across an AC97 slot ++ */ ++static int wm9705_acc_enable (struct wm97xx* wm, int enable) ++{ ++ u16 dig1, dig2; ++ int ret = 0; ++ ++ dig1 = wm->dig[1]; ++ dig2 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | ++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK); ++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | ++ WM97XX_DELAY (delay) | ++ WM97XX_SLT (wm->acc_slot) | ++ WM97XX_RATE (wm->acc_rate); ++ if (pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ dig2 |= WM9705_PDEN; ++ } else { ++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); ++ dig2 &= ~WM9705_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++ return ret; ++} ++ ++struct wm97xx_codec_drv wm97xx_codec = { ++ .id = WM9705_ID2, ++ .name = "wm9705", ++ .poll_sample = wm9705_poll_sample, ++ .poll_touch = wm9705_poll_touch, ++ .acc_enable = wm9705_acc_enable, ++ .digitiser_ioctl = wm9705_digitiser_ioctl, ++}; ++ ++EXPORT_SYMBOL_GPL(wm97xx_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM9705 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/drivers/input/touchscreen/wm9712.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/wm9712.c 2006-09-19 20:36:47.969052000 +0200 +@@ -0,0 +1,464 @@ ++/* ++ * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Revision history ++ * 4th Jul 2005 Initial version. ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added pre and post sample calls. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9712_VERSION "0.61" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set internal pull up for pen detect. ++ * ++ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) ++ * i.e. pull up resistance = 64k Ohms / rpu. ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down event. ++ */ ++static int rpu = 3; ++module_param(rpu, int, 0); ++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil = 0; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 3; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Set five_wire = 1 to use a 5 wire touchscreen. ++ * ++ * NOTE: Five wire mode does not allow for readback of pressure. ++ */ ++static int five_wire; ++module_param(five_wire, int, 0); ++MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen."); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask = 0; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * Coordinate Polling Enable. ++ * ++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together ++ * for every poll. ++ */ ++static int coord = 0; ++module_param(coord, int, 0); ++MODULE_PARM_DESC(coord, "Polling coordinate mode"); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, // 1 AC97 Link frames ++ 42, // 2 ++ 84, // 4 ++ 167, // 8 ++ 333, // 16 ++ 667, // 32 ++ 1000, // 48 ++ 1333, // 64 ++ 2000, // 96 ++ 2667, // 128 ++ 3333, // 160 ++ 4000, // 192 ++ 4667, // 224 ++ 5333, // 256 ++ 6000, // 288 ++ 0 // No delay, switch matrix always on ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay (3 * AC97_LINK_FRAME + delay_table [d]); ++} ++ ++/* ++ * set up the physical settings of the WM9712 ++ */ ++static void init_wm9712_phy(struct wm97xx* wm) ++{ ++ u16 dig1 = 0; ++ u16 dig2 = WM97XX_RPR | WM9712_RPU(1); ++ ++ /* WM9712 rpu */ ++ if (rpu) { ++ dig2 &= 0xffc0; ++ dig2 |= WM9712_RPU(rpu); ++ dbg("setting pen detect pull-up to %d Ohms",64000 / rpu); ++ } ++ ++ /* touchpanel pressure current*/ ++ if (pil == 2) { ++ dig2 |= WM9712_PIL; ++ dbg("setting pressure measurement current to 400uA."); ++ } else if (pil) ++ dbg("setting pressure measurement current to 200uA."); ++ if(!pil) ++ pressure = 0; ++ ++ /* WM9712 five wire */ ++ if (five_wire) { ++ dig2 |= WM9712_45W; ++ dbg("setting 5-wire touchscreen mode."); ++ } ++ ++ /* polling mode sample settling delay */ ++ if (delay < 0 || delay > 15) { ++ dbg("supplied delay out of range."); ++ delay = 4; ++ } ++ dig1 &= 0xff0f; ++ dig1 |= WM97XX_DELAY(delay); ++ dbg("setting adc sample delay to %d u Secs.", delay_table[delay]); ++ ++ /* mask */ ++ dig2 |= ((mask & 0x3) << 6); ++ if (mask) { ++ u16 reg; ++ /* Set GPIO4 as Mask Pin*/ ++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4); ++ } ++ ++ /* wait - coord mode */ ++ if(coord) ++ dig2 |= WM9712_WAIT; ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++} ++ ++static int wm9712_digitiser_ioctl(struct wm97xx* wm, int cmd) ++{ ++ u16 dig2 = wm->dig[2]; ++ ++ switch(cmd) { ++ case WM97XX_DIG_START: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ break; ++ case WM97XX_DIG_STOP: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 & ~WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_AUX_PREPARE: ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_DIG_RESTORE: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); ++ break; ++ case WM97XX_PHY_INIT: ++ init_wm9712_phy(wm); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static inline int is_pden (struct wm97xx* wm) ++{ ++ return wm->dig[2] & WM9712_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9712 adc in polling mode. ++ */ ++static int wm9712_poll_sample (struct wm97xx* wm, int adcsel, int *sample) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = ((adcsel & 0x7fff) + 3) << 12; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay (delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { ++ dbg ("adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSEL_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Read a coord from the WM9712 adc in polling mode. ++ */ ++static int wm9712_poll_coord (struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, ++ WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion and read x */ ++ poll_delay(delay); ++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ /* read back y data */ ++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (pil) ++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ else ++ data->p = DEFAULT_PRESSURE; ++ ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ /* check we have correct sample */ ++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) ++ goto err; ++ if(pil && !(data->p & WM97XX_ADCSEL_PRES)) ++ goto err; ++ ++ if (!(data->x & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ return RC_VALID; ++err: ++ return RC_PENUP; ++} ++ ++/* ++ * Sample the WM9712 touchscreen in polling mode ++ */ ++static int wm9712_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if(coord) { ++ if((rc = wm9712_poll_coord(wm, data)) != RC_VALID) ++ return rc; ++ } else { ++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID) ++ return rc; ++ ++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID) ++ return rc; ++ ++ if (pil && !five_wire) { ++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ } ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9712 continuous mode, i.e. touch data is streamed across an AC97 slot ++ */ ++static int wm9712_acc_enable (struct wm97xx* wm, int enable) ++{ ++ u16 dig1, dig2; ++ int ret = 0; ++ ++ dig1 = wm->dig[1]; ++ dig2 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | ++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK); ++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | ++ WM97XX_DELAY (delay) | ++ WM97XX_SLT (wm->acc_slot) | ++ WM97XX_RATE (wm->acc_rate); ++ if (pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ dig2 |= WM9712_PDEN; ++ } else { ++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); ++ dig2 &= ~WM9712_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++ return 0; ++} ++ ++struct wm97xx_codec_drv wm97xx_codec = { ++ .id = WM9712_ID2, ++ .name = "wm9712", ++ .poll_sample = wm9712_poll_sample, ++ .poll_touch = wm9712_poll_touch, ++ .acc_enable = wm9712_acc_enable, ++ .digitiser_ioctl = wm9712_digitiser_ioctl, ++}; ++ ++EXPORT_SYMBOL_GPL(wm97xx_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM9712 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/drivers/input/touchscreen/wm9713.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/wm9713.c 2006-09-19 20:36:47.969052000 +0200 +@@ -0,0 +1,461 @@ ++/* ++ * wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Revision history ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added pre and post sample calls. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9713_VERSION "0.53" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set internal pull up for pen detect. ++ * ++ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) ++ * i.e. pull up resistance = 64k Ohms / rpu. ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down event. ++ */ ++static int rpu = 1; ++module_param(rpu, int, 0); ++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil = 0; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 4; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask = 0; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * Coordinate Polling Enable. ++ * ++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together ++ * for every poll. ++ */ ++static int coord = 1; ++module_param(coord, int, 0); ++MODULE_PARM_DESC(coord, "Polling coordinate mode"); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, // 1 AC97 Link frames ++ 42, // 2 ++ 84, // 4 ++ 167, // 8 ++ 333, // 16 ++ 667, // 32 ++ 1000, // 48 ++ 1333, // 64 ++ 2000, // 96 ++ 2667, // 128 ++ 3333, // 160 ++ 4000, // 192 ++ 4667, // 224 ++ 5333, // 256 ++ 6000, // 288 ++ 0 // No delay, switch matrix always on ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay (3 * AC97_LINK_FRAME + delay_table [d]); ++} ++ ++/* ++ * set up the physical settings of the WM9713 ++ */ ++static void init_wm9713_phy(struct wm97xx* wm) ++{ ++ u16 dig1 = 0, dig2, dig3; ++ ++ /* default values */ ++ dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5); ++ dig3= WM9712_RPU(1); ++ ++ /* rpu */ ++ if (rpu) { ++ dig3 &= 0xffc0; ++ dig3 |= WM9712_RPU(rpu); ++ info("setting pen detect pull-up to %d Ohms",64000 / rpu); ++ } ++ ++ /* touchpanel pressure */ ++ if (pil == 2) { ++ dig3 |= WM9712_PIL; ++ info("setting pressure measurement current to 400uA."); ++ } else if (pil) ++ info ("setting pressure measurement current to 200uA."); ++ if(!pil) ++ pressure = 0; ++ ++ /* sample settling delay */ ++ if (delay < 0 || delay > 15) { ++ info ("supplied delay out of range."); ++ delay = 4; ++ info("setting adc sample delay to %d u Secs.", delay_table[delay]); ++ } ++ dig2 &= 0xff0f; ++ dig2 |= WM97XX_DELAY(delay); ++ ++ /* mask */ ++ dig3 |= ((mask & 0x3) << 4); ++ if(coord) ++ dig3 |= WM9713_WAIT; ++ ++ wm->misc = wm97xx_reg_read(wm, 0x5a); ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3); ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0); ++} ++ ++static int wm9713_digitiser_ioctl(struct wm97xx* wm, int cmd) ++{ ++ u16 val = 0; ++ ++ switch(cmd){ ++ case WM97XX_DIG_START: ++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ break; ++ case WM97XX_DIG_STOP: ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] & ~WM97XX_PRP_DET_DIG); ++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000); ++ break; ++ case WM97XX_AUX_PREPARE: ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_DIG_RESTORE: ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]); ++ break; ++ case WM97XX_PHY_INIT: ++ init_wm9713_phy(wm); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static inline int is_pden (struct wm97xx* wm) ++{ ++ return wm->dig[2] & WM9713_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9713 adc in polling mode. ++ */ ++static int wm9713_poll_sample (struct wm97xx* wm, int adcsel, int *sample) ++{ ++ u16 dig1; ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = 1 << ((adcsel & 0x7fff) + 3); ++ ++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | adcsel |WM9713_POLL); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay(delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample =wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSRC_MASK) != ffs(adcsel >> 1) << 12) { ++ dbg ("adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSRC_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Read a coordinate from the WM9713 adc in polling mode. ++ */ ++static int wm9713_poll_coord (struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ u16 dig1; ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ if(pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | WM9713_POLL | WM9713_COO); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay(delay); ++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ /* read back data */ ++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (pil) ++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ else ++ data->p = DEFAULT_PRESSURE; ++ ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ /* check we have correct sample */ ++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) ++ goto err; ++ if(pil && !(data->p & WM97XX_ADCSEL_PRES)) ++ goto err; ++ ++ if (!(data->x & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ return RC_VALID; ++err: ++ return RC_PENUP; ++} ++ ++/* ++ * Sample the WM9713 touchscreen in polling mode ++ */ ++static int wm9713_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if(coord) { ++ if((rc = wm9713_poll_coord(wm, data)) != RC_VALID) ++ return rc; ++ } else { ++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x)) != RC_VALID) ++ return rc; ++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y)) != RC_VALID) ++ return rc; ++ if (pil) { ++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES, &data->p)) != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ } ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9713 continuous mode, i.e. touch data is streamed across an AC97 slot ++ */ ++static int wm9713_acc_enable (struct wm97xx* wm, int enable) ++{ ++ u16 dig1, dig2, dig3; ++ int ret = 0; ++ ++ dig1 = wm->dig[0]; ++ dig2 = wm->dig[1]; ++ dig3 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && ++ (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X | WM9713_ADCSEL_Y; ++ if (pil) ++ dig1 |= WM9713_ADCSEL_PRES; ++ dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK | WM97XX_CM_RATE_MASK); ++ dig2 |= WM97XX_SLEN | WM97XX_DELAY (delay) | ++ WM97XX_SLT (wm->acc_slot) | WM97XX_RATE (wm->acc_rate); ++ dig3 |= WM9713_PDEN; ++ } else { ++ dig1 &= ~(WM9713_CTC | WM9713_COO); ++ dig2 &= ~WM97XX_SLEN; ++ dig3 &= ~WM9713_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3); ++ return ret; ++} ++ ++struct wm97xx_codec_drv wm97xx_codec = { ++ .id = WM9713_ID2, ++ .name = "wm9713", ++ .poll_sample = wm9713_poll_sample, ++ .poll_touch = wm9713_poll_touch, ++ .acc_enable = wm9713_acc_enable, ++ .digitiser_ioctl = wm9713_digitiser_ioctl, ++}; ++ ++EXPORT_SYMBOL_GPL(wm97xx_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM9713 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/drivers/input/touchscreen/wm97xx-core.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/wm97xx-core.c 2006-09-19 20:36:47.969052000 +0200 +@@ -0,0 +1,912 @@ ++/* ++ * wm97xx-core.c -- Touch screen driver core for Wolfson WM9705, WM9712 ++ * and WM9713 AC97 Codecs. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Notes: ++ * ++ * Features: ++ * - supports WM9705, WM9712, WM9713 ++ * - polling mode ++ * - continuous mode (arch-dependent) ++ * - adjustable rpu/dpp settings ++ * - adjustable pressure current ++ * - adjustable sample settle delay ++ * - 4 and 5 wire touchscreens (5 wire is WM9712 only) ++ * - pen down detection ++ * - battery monitor ++ * - sample AUX adc's ++ * - power management ++ * - codec GPIO ++ * - codec event notification ++ * Todo ++ * - Support for async sampling control for noisy LCD's. ++ * ++ * Revision history ++ * 7th May 2003 Initial version. ++ * 6th June 2003 Added non module support and AC97 registration. ++ * 18th June 2003 Added AUX adc sampling. ++ * 23rd June 2003 Did some minimal reformatting, fixed a couple of ++ * codec_mutexing bugs and noted a race to fix. ++ * 24th June 2003 Added power management and fixed race condition. ++ * 10th July 2003 Changed to a misc device. ++ * 31st July 2003 Moved TS_EVENT and TS_CAL to wm97xx.h ++ * 8th Aug 2003 Added option for read() calling wm97xx_sample_touch() ++ * because some ac97_read/ac_97_write call schedule() ++ * 7th Nov 2003 Added Input touch event interface, stanley.cai@intel.com ++ * 13th Nov 2003 Removed h3600 touch interface, added interrupt based ++ * pen down notification and implemented continous mode ++ * on XScale arch. ++ * 16th Nov 2003 Ian Molton <spyro@f2s.com> ++ * Modified so that it suits the new 2.6 driver model. ++ * 25th Jan 2004 Andrew Zabolotny <zap@homelink.ru> ++ * Implemented IRQ-driven pen down detection, implemented ++ * the private API meant to be exposed to platform-specific ++ * drivers, reorganized the driver so that it supports ++ * an arbitrary number of devices. ++ * 1st Feb 2004 Moved continuous mode handling to a separate ++ * architecture-dependent file. For now only PXA ++ * built-in AC97 controller is supported (pxa-ac97-wm97xx.c). ++ * 11th Feb 2004 Reduced CPU usage by keeping a cached copy of both ++ * digitizer registers instead of reading them every time. ++ * A reorganization of the whole code for better ++ * error handling. ++ * 17th Apr 2004 Added BMON support. ++ * 17th Nov 2004 Added codec GPIO, codec event handling (real and virtual ++ * GPIOs) and 2.6 power management. ++ * 29th Nov 2004 Added WM9713 support. ++ * 4th Jul 2005 Moved codec specific code out to seperate files. ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added bus interface. ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/string.h> ++#include <linux/proc_fs.h> ++#include <linux/pm.h> ++#include <linux/interrupt.h> ++#include <linux/bitops.h> ++#include <linux/workqueue.h> ++#include <linux/device.h> ++#include <linux/wm97xx.h> ++#include <asm/uaccess.h> ++#include <asm/io.h> ++ ++#define TS_NAME "wm97xx" ++#define WM_CORE_VERSION "0.63" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * WM97xx - enable/disable AUX ADC sysfs ++ */ ++static int aux_sys = 1; ++module_param(aux_sys, int, 0); ++MODULE_PARM_DESC(aux_sys, "enable AUX ADC sysfs entries"); ++ ++/* ++ * WM97xx - enable/disable codec status sysfs ++ */ ++static int status_sys = 1; ++module_param(status_sys, int, 0); ++MODULE_PARM_DESC(status_sys, "enable codec status sysfs entries"); ++ ++/* ++ * Touchscreen absolute values ++ * ++ * These parameters are used to help the input layer discard out of ++ * range readings and reduce jitter etc. ++ * ++ * o min, max:- indicate the min and max values your touch screen returns ++ * o fuzz:- use a higher number to reduce jitter ++ * ++ * The default values correspond to Mainstone II in QVGA mode ++ * ++ * Please read ++ * Documentation/input/input-programming.txt for more details. ++ */ ++ ++static int abs_x[3] = {350,3900,5}; ++module_param_array(abs_x, int, NULL, 0); ++MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz"); ++ ++static int abs_y[3] = {320,3750,40}; ++module_param_array(abs_y, int, NULL, 0); ++MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz"); ++ ++static int abs_p[3] = {0,150,4}; ++module_param_array(abs_p, int, NULL, 0); ++MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz"); ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* codec AC97 IO access */ ++int wm97xx_reg_read(struct wm97xx *wm, u16 reg) ++{ ++ if (wm->ac97) ++ return wm->ac97->bus->ops->read(wm->ac97, reg); ++ else ++ return -1; ++} ++ ++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val) ++{ ++ /* cache digitiser registers */ ++ if(reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3) ++ wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val; ++ ++ /* cache gpio regs */ ++ if(reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE) ++ wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val; ++ ++ /* wm9713 irq reg */ ++ if(reg == 0x5a) ++ wm->misc = val; ++ ++ if (wm->ac97) ++ wm->ac97->bus->ops->write(wm->ac97, reg, val); ++} ++ ++ ++/** ++ * wm97xx_read_aux_adc - Read the aux adc. ++ * @wm: wm97xx device. ++ * @adcsel: codec ADC to be read ++ * ++ * Reads the selected AUX ADC. ++ */ ++ ++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) ++{ ++ int power_adc = 0, auxval; ++ u16 power = 0; ++ ++ /* get codec */ ++ mutex_lock(&wm->codec_mutex); ++ ++ /* When the touchscreen is not in use, we may have to power up the AUX ADC ++ * before we can use sample the AUX inputs-> ++ */ ++ if (wm->id == WM9713_ID2 && ++ (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) { ++ power_adc = 1; ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff); ++ } ++ ++ /* Prepare the codec for AUX reading */ ++ wm->codec->digitiser_ioctl(wm, WM97XX_AUX_PREPARE); ++ ++ /* Turn polling mode on to read AUX ADC */ ++ wm->pen_probably_down = 1; ++ wm->codec->poll_sample(wm, adcsel, &auxval); ++ ++ if (power_adc) ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000); ++ ++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_RESTORE); ++ ++ wm->pen_probably_down = 0; ++ ++ mutex_unlock(&wm->codec_mutex); ++ return auxval & 0xfff; ++} ++ ++#define WM97XX_AUX_ATTR(name,input) \ ++static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct wm97xx *wm = (struct wm97xx*)dev->driver_data; \ ++ return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, input)); \ ++} \ ++static DEVICE_ATTR(name, 0444, name##_show, NULL) ++ ++WM97XX_AUX_ATTR(aux1, WM97XX_AUX_ID1); ++WM97XX_AUX_ATTR(aux2, WM97XX_AUX_ID2); ++WM97XX_AUX_ATTR(aux3, WM97XX_AUX_ID3); ++WM97XX_AUX_ATTR(aux4, WM97XX_AUX_ID4); ++ ++#define WM97XX_STATUS_ATTR(name) \ ++static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct wm97xx *wm = (struct wm97xx*)dev->driver_data; \ ++ return sprintf(buf, "%d\n", wm97xx_reg_read(wm, AC97_GPIO_STATUS)); \ ++} \ ++static DEVICE_ATTR(name, 0444, name##_show, NULL) ++ ++WM97XX_STATUS_ATTR(gpio); ++ ++static int wm97xx_sys_add(struct device *dev) ++{ ++ if (aux_sys) { ++ device_create_file(dev, &dev_attr_aux1); ++ device_create_file(dev, &dev_attr_aux2); ++ device_create_file(dev, &dev_attr_aux3); ++ device_create_file(dev, &dev_attr_aux4); ++ } ++ if (status_sys) ++ device_create_file(dev, &dev_attr_gpio); ++ return 0; ++} ++ ++static void wm97xx_sys_remove(struct device *dev) ++{ ++ if (status_sys) ++ device_remove_file(dev, &dev_attr_gpio); ++ if (aux_sys) { ++ device_remove_file(dev, &dev_attr_aux1); ++ device_remove_file(dev, &dev_attr_aux2); ++ device_remove_file(dev, &dev_attr_aux3); ++ device_remove_file(dev, &dev_attr_aux4); ++ } ++} ++ ++/** ++ * wm97xx_get_gpio - Get the status of a codec GPIO. ++ * @wm: wm97xx device. ++ * @gpio: gpio ++ * ++ * Get the status of a codec GPIO pin ++ */ ++ ++wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio) ++{ ++ u16 status; ++ wm97xx_gpio_status_t ret; ++ ++ mutex_lock(&wm->codec_mutex); ++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ ++ if (status & gpio) ++ ret = WM97XX_GPIO_HIGH; ++ else ++ ret = WM97XX_GPIO_LOW; ++ ++ mutex_unlock(&wm->codec_mutex); ++ return ret; ++} ++ ++/** ++ * wm97xx_set_gpio - Set the status of a codec GPIO. ++ * @wm: wm97xx device. ++ * @gpio: gpio ++ * ++ * ++ * Set the status of a codec GPIO pin ++ */ ++ ++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, ++ wm97xx_gpio_status_t status) ++{ ++ u16 reg; ++ ++ mutex_lock(&wm->codec_mutex); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ ++ if (status & WM97XX_GPIO_HIGH) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ if (wm->id == WM9712_ID2) ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1); ++ else ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg); ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++/* ++ * Codec GPIO pin configuration, this set's pin direction, polarity, ++ * stickyness and wake up. ++ */ ++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir, ++ wm97xx_gpio_pol_t pol, wm97xx_gpio_sticky_t sticky, ++ wm97xx_gpio_wake_t wake) ++{ ++ u16 reg; ++ ++ mutex_lock(&wm->codec_mutex); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ ++ if (pol == WM97XX_GPIO_POL_HIGH) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY); ++ ++ if (sticky == WM97XX_GPIO_STICKY) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP); ++ ++ if (wake == WM97XX_GPIO_WAKE) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ ++ if (dir == WM97XX_GPIO_IN) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg); ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++/* ++ * Handle a pen down interrupt. ++ */ ++static void wm97xx_pen_irq_worker(void *ptr) ++{ ++ struct wm97xx *wm = (struct wm97xx *) ptr; ++ ++ /* do we need to enable the touch panel reader */ ++ if (wm->id == WM9705_ID2) { ++ if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN) ++ wm->pen_is_down = 1; ++ else ++ wm->pen_is_down = 0; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ } else { ++ u16 status, pol; ++ mutex_lock(&wm->codec_mutex); ++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ ++ if (WM97XX_GPIO_13 & pol & status) { ++ wm->pen_is_down = 1; ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol & ~WM97XX_GPIO_13); ++ } else { ++ wm->pen_is_down = 0; ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol | WM97XX_GPIO_13); ++ } ++ ++ if (wm->id == WM9712_ID2) ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status & ~WM97XX_GPIO_13) << 1); ++ else ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, status & ~WM97XX_GPIO_13); ++ mutex_unlock(&wm->codec_mutex); ++ wake_up_interruptible(&wm->pen_irq_wait); ++ } ++ ++ if (!wm->pen_is_down && wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->mach_ops->acc_pen_up(wm); ++ enable_irq(wm->pen_irq); ++} ++ ++/* ++ * Codec PENDOWN irq handler ++ * ++ * We have to disable the codec interrupt in the handler because it can ++ * take upto 1ms to clear the interrupt source. The interrupt is then enabled ++ * again in the slow handler when the source has been cleared. ++ */ ++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id, ++ struct pt_regs *regs) ++{ ++ struct wm97xx *wm = (struct wm97xx *) dev_id; ++ disable_irq(wm->pen_irq); ++ queue_work(wm->pen_irq_workq, &wm->pen_event_work); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * initialise pen IRQ handler and workqueue ++ */ ++static int wm97xx_init_pen_irq(struct wm97xx *wm) ++{ ++ u16 reg; ++ ++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker, wm); ++ if ((wm->pen_irq_workq = ++ create_singlethread_workqueue("kwm97pen")) == NULL) { ++ err("could not create pen irq work queue"); ++ wm->pen_irq = 0; ++ return -EINVAL; ++ } ++ ++ if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) { ++ err("could not register codec pen down interrupt, will poll for pen down"); ++ destroy_workqueue(wm->pen_irq_workq); ++ wm->pen_irq = 0; ++ return -EINVAL; ++ } ++ ++ /* enable PEN down on wm9712/13 */ ++ if (wm->id != WM9705_ID2) { ++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg & 0xfffb); ++ reg = wm97xx_reg_read(wm, 0x5a); ++ wm97xx_reg_write(wm, 0x5a, reg & ~0x0001); ++ } ++ ++ return 0; ++} ++ ++/* Private struct for communication between struct wm97xx_tshread ++ * and wm97xx_read_samples */ ++struct ts_state { ++ int sleep_time; ++ int min_sleep_time; ++}; ++ ++static int wm97xx_read_samples(struct wm97xx *wm, struct ts_state *state) ++{ ++ struct wm97xx_data data; ++ int rc; ++ ++ mutex_lock(&wm->codec_mutex); ++ ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ rc = wm->mach_ops->acc_pen_down(wm); ++ else ++ rc = wm->codec->poll_touch(wm, &data); ++ ++ if (rc & RC_PENUP) { ++ if (wm->pen_is_down) { ++ wm->pen_is_down = 0; ++ dbg("pen up"); ++ input_report_abs(wm->input_dev, ABS_PRESSURE, 0); ++ input_sync(wm->input_dev); ++ } else if (!(rc & RC_AGAIN)) { ++ /* We need high frequency updates only while pen is down, ++ * the user never will be able to touch screen faster than ++ * a few times per second... On the other hand, when the ++ * user is actively working with the touchscreen we don't ++ * want to lose the quick response. So we will slowly ++ * increase sleep time after the pen is up and quicky ++ * restore it to ~one task switch when pen is down again. ++ */ ++ if (state->sleep_time < HZ / 10) ++ state->sleep_time++; ++ } ++ ++ } else if (rc & RC_VALID) { ++ dbg("pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n", ++ data.x >> 12, data.x & 0xfff, data.y >> 12, ++ data.y & 0xfff, data.p >> 12, data.p & 0xfff); ++ input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff); ++ input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff); ++ input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff); ++ input_sync(wm->input_dev); ++ wm->pen_is_down = 1; ++ state->sleep_time = state->min_sleep_time; ++ } else if (rc & RC_PENDOWN) { ++ dbg("pen down"); ++ wm->pen_is_down = 1; ++ state->sleep_time = state->min_sleep_time; ++ } ++ ++ mutex_unlock(&wm->codec_mutex); ++ return rc; ++} ++ ++/* ++* The touchscreen sample reader thread. ++*/ ++static int wm97xx_ts_read(void *data) ++{ ++ int rc; ++ struct ts_state state; ++ struct wm97xx *wm = (struct wm97xx *) data; ++ ++ /* set up thread context */ ++ wm->ts_task = current; ++ daemonize("kwm97xxts"); ++ ++ if (wm->codec == NULL) { ++ wm->ts_task = NULL; ++ printk(KERN_ERR "codec is NULL, bailing\n"); ++ } ++ ++ complete(&wm->ts_init); ++ wm->pen_is_down = 0; ++ state.min_sleep_time = HZ >= 100 ? HZ / 100 : 1; ++ if (state.min_sleep_time < 1) ++ state.min_sleep_time = 1; ++ state.sleep_time = state.min_sleep_time; ++ ++ /* touch reader loop */ ++ while (wm->ts_task) { ++ do { ++ try_to_freeze(); ++ rc = wm97xx_read_samples(wm, &state); ++ } while (rc & RC_AGAIN); ++ if (!wm->pen_is_down && wm->pen_irq) { ++ /* Nice, we don't have to poll for pen down event */ ++ wait_event_interruptible(wm->pen_irq_wait, wm->pen_is_down); ++ } else { ++ set_task_state(current, TASK_INTERRUPTIBLE); ++ schedule_timeout(state.sleep_time); ++ } ++ } ++ complete_and_exit(&wm->ts_exit, 0); ++} ++ ++/** ++ * wm97xx_ts_input_open - Open the touch screen input device. ++ * @idev: Input device to be opened. ++ * ++ * Called by the input sub system to open a wm97xx touchscreen device. ++ * Starts the touchscreen thread and touch digitiser. ++ */ ++static int wm97xx_ts_input_open(struct input_dev *idev) ++{ ++ int ret = 0; ++ struct wm97xx *wm = (struct wm97xx *) idev->private; ++ ++ mutex_lock(&wm->codec_mutex); ++ /* first time opened ? */ ++ if (wm->ts_use_count++ == 0) { ++ /* start touchscreen thread */ ++ init_completion(&wm->ts_init); ++ init_completion(&wm->ts_exit); ++ ret = kernel_thread(wm97xx_ts_read, wm, CLONE_KERNEL); ++ ++ if (ret >= 0) { ++ wait_for_completion(&wm->ts_init); ++ if (wm->ts_task == NULL) ++ ret = -EINVAL; ++ } else { ++ mutex_unlock(&wm->codec_mutex); ++ return ret; ++ } ++ ++ /* start digitiser */ ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->codec->acc_enable(wm, 1); ++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_START); ++ ++ /* init pen down/up irq handling */ ++ if (wm->pen_irq) { ++ wm97xx_init_pen_irq(wm); ++ ++ if (wm->pen_irq == 0) { ++ /* we failed to get an irq for pen down events, ++ * so we resort to polling. kickstart the reader */ ++ wm->pen_is_down = 1; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ } ++ } ++ } ++ ++ mutex_unlock(&wm->codec_mutex); ++ return 0; ++} ++ ++/** ++ * wm97xx_ts_input_close - Close the touch screen input device. ++ * @idev: Input device to be closed. ++ * ++ * Called by the input sub system to close a wm97xx touchscreen device. ++ * Kills the touchscreen thread and stops the touch digitiser. ++ */ ++ ++static void wm97xx_ts_input_close(struct input_dev *idev) ++{ ++ struct wm97xx *wm = (struct wm97xx *) idev->private; ++ ++ mutex_lock(&wm->codec_mutex); ++ if (--wm->ts_use_count == 0) { ++ /* destroy workqueues and free irqs */ ++ if (wm->pen_irq) { ++ free_irq(wm->pen_irq, wm); ++ destroy_workqueue(wm->pen_irq_workq); ++ } ++ ++ /* kill thread */ ++ if (wm->ts_task) { ++ wm->ts_task = NULL; ++ wm->pen_is_down = 1; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ wait_for_completion(&wm->ts_exit); ++ wm->pen_is_down = 0; ++ } ++ ++ /* stop digitiser */ ++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_STOP); ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->codec->acc_enable(wm, 0); ++ } ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++static int wm97xx_bus_match(struct device *dev, struct device_driver *drv) ++{ ++ return !(strcmp(dev->bus_id,drv->name)); ++} ++ ++/* ++ * The AC97 audio driver will do all the Codec suspend and resume ++ * tasks. This is just for anything machine specific or extra. ++ */ ++static int wm97xx_bus_suspend(struct device *dev, pm_message_t state) ++{ ++ int ret = 0; ++ ++ if (dev->driver && dev->driver->suspend) ++ ret = dev->driver->suspend(dev, state); ++ ++ return ret; ++} ++ ++static int wm97xx_bus_resume(struct device *dev) ++{ ++ int ret = 0; ++ ++ if (dev->driver && dev->driver->resume) ++ ret = dev->driver->resume(dev); ++ ++ return ret; ++} ++ ++struct bus_type wm97xx_bus_type = { ++ .name = "wm97xx", ++ .match = wm97xx_bus_match, ++ .suspend = wm97xx_bus_suspend, ++ .resume = wm97xx_bus_resume, ++}; ++ ++static void wm97xx_release(struct device *dev) ++{ ++ kfree(dev); ++} ++ ++static int wm97xx_probe(struct device *dev) ++{ ++ struct wm97xx* wm; ++ int ret = 0, id = 0; ++ ++ if (!(wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL))) ++ return -ENOMEM; ++ mutex_init(&wm->codec_mutex); ++ ++ init_waitqueue_head(&wm->pen_irq_wait); ++ wm->dev = dev; ++ dev->driver_data = wm; ++ wm->ac97 = to_ac97_t(dev); ++ ++ /* check that we have a supported codec */ ++ if ((id = wm97xx_reg_read(wm, AC97_VENDOR_ID1)) != WM97XX_ID1) { ++ err("could not find a wm97xx, found a %x instead\n", id); ++ kfree(wm); ++ return -ENODEV; ++ } ++ ++ wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2); ++ if(wm->id != wm97xx_codec.id) { ++ err("could not find a the selected codec, please build for wm97%2x", wm->id & 0xff); ++ kfree(wm); ++ return -ENODEV; ++ } ++ ++ if((wm->input_dev = input_allocate_device()) == NULL) { ++ kfree(wm); ++ return -ENOMEM; ++ } ++ ++ /* set up touch configuration */ ++ info("detected a wm97%2x codec", wm->id & 0xff); ++ wm->input_dev->name = "wm97xx touchscreen"; ++ wm->input_dev->open = wm97xx_ts_input_open; ++ wm->input_dev->close = wm97xx_ts_input_close; ++ set_bit(EV_ABS, wm->input_dev->evbit); ++ set_bit(ABS_X, wm->input_dev->absbit); ++ set_bit(ABS_Y, wm->input_dev->absbit); ++ set_bit(ABS_PRESSURE, wm->input_dev->absbit); ++ wm->input_dev->absmax[ABS_X] = abs_x[1]; ++ wm->input_dev->absmax[ABS_Y] = abs_y[1]; ++ wm->input_dev->absmax[ABS_PRESSURE] = abs_p[1]; ++ wm->input_dev->absmin[ABS_X] = abs_x[0]; ++ wm->input_dev->absmin[ABS_Y] = abs_y[0]; ++ wm->input_dev->absmin[ABS_PRESSURE] = abs_p[0]; ++ wm->input_dev->absfuzz[ABS_X] = abs_x[2]; ++ wm->input_dev->absfuzz[ABS_Y] = abs_y[2]; ++ wm->input_dev->absfuzz[ABS_PRESSURE] = abs_p[2]; ++ wm->input_dev->private = wm; ++ wm->codec = &wm97xx_codec; ++ if((ret = input_register_device(wm->input_dev)) < 0) { ++ kfree(wm); ++ return -ENOMEM; ++ } ++ ++ if(aux_sys) ++ wm97xx_sys_add(dev); ++ ++ /* set up physical characteristics */ ++ wm->codec->digitiser_ioctl(wm, WM97XX_PHY_INIT); ++ ++ /* load gpio cache */ ++ wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY); ++ wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP); ++ wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ ++ /* register our battery device */ ++ if (!(wm->battery_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) { ++ ret = -ENOMEM; ++ goto batt_err; ++ } ++ wm->battery_dev->bus = &wm97xx_bus_type; ++ strcpy(wm->battery_dev->bus_id,"wm97xx-battery"); ++ wm->battery_dev->driver_data = wm; ++ wm->battery_dev->parent = dev; ++ wm->battery_dev->release = wm97xx_release; ++ if((ret = device_register(wm->battery_dev)) < 0) ++ goto batt_reg_err; ++ ++ /* register our extended touch device (for machine specific extensions) */ ++ if (!(wm->touch_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) { ++ ret = -ENOMEM; ++ goto touch_err; ++ } ++ wm->touch_dev->bus = &wm97xx_bus_type; ++ strcpy(wm->touch_dev->bus_id,"wm97xx-touchscreen"); ++ wm->touch_dev->driver_data = wm; ++ wm->touch_dev->parent = dev; ++ wm->touch_dev->release = wm97xx_release; ++ if((ret = device_register(wm->touch_dev)) < 0) ++ goto touch_reg_err; ++ ++ return ret; ++ ++touch_reg_err: ++ kfree(wm->touch_dev); ++touch_err: ++ device_unregister(wm->battery_dev); ++batt_reg_err: ++ kfree(wm->battery_dev); ++batt_err: ++ input_unregister_device(wm->input_dev); ++ kfree(wm); ++ return ret; ++} ++ ++static int wm97xx_remove(struct device *dev) ++{ ++ struct wm97xx *wm = dev_get_drvdata(dev); ++ ++ /* Stop touch reader thread */ ++ if (wm->ts_task) { ++ wm->ts_task = NULL; ++ wm->pen_is_down = 1; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ wait_for_completion(&wm->ts_exit); ++ } ++ device_unregister(wm->battery_dev); ++ device_unregister(wm->touch_dev); ++ input_unregister_device(wm->input_dev); ++ ++ if(aux_sys) ++ wm97xx_sys_remove(dev); ++ ++ kfree(wm); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++int wm97xx_resume(struct device* dev) ++{ ++ struct wm97xx *wm = dev_get_drvdata(dev); ++ ++ /* restore digitiser and gpio's */ ++ if(wm->id == WM9713_ID2) { ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]); ++ wm97xx_reg_write(wm, 0x5a, wm->misc); ++ if(wm->ts_use_count) { ++ u16 reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff; ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg); ++ } ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]); ++ ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]); ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]); ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]); ++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]); ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]); ++ ++ return 0; ++} ++ ++#else ++#define wm97xx_resume NULL ++#endif ++ ++int wm97xx_register_mach_ops(struct wm97xx *wm, struct wm97xx_mach_ops *mach_ops) ++{ ++ mutex_lock(&wm->codec_mutex); ++ if(wm->mach_ops) { ++ mutex_unlock(&wm->codec_mutex); ++ return -EINVAL; ++ } ++ wm->mach_ops = mach_ops; ++ mutex_unlock(&wm->codec_mutex); ++ return 0; ++} ++ ++void wm97xx_unregister_mach_ops(struct wm97xx *wm) ++{ ++ mutex_lock(&wm->codec_mutex); ++ wm->mach_ops = NULL; ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++static struct device_driver wm97xx_driver = { ++ .name = "ac97", ++ .bus = &ac97_bus_type, ++ .owner = THIS_MODULE, ++ .probe = wm97xx_probe, ++ .remove = wm97xx_remove, ++ .resume = wm97xx_resume, ++}; ++ ++static int __init wm97xx_init(void) ++{ ++ int ret; ++ ++ info("version %s liam.girdwood@wolfsonmicro.com", WM_CORE_VERSION); ++ if((ret = bus_register(&wm97xx_bus_type)) < 0) ++ return ret; ++ return driver_register(&wm97xx_driver); ++} ++ ++static void __exit wm97xx_exit(void) ++{ ++ driver_unregister(&wm97xx_driver); ++ bus_unregister(&wm97xx_bus_type); ++} ++ ++EXPORT_SYMBOL_GPL(wm97xx_get_gpio); ++EXPORT_SYMBOL_GPL(wm97xx_set_gpio); ++EXPORT_SYMBOL_GPL(wm97xx_config_gpio); ++EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc); ++EXPORT_SYMBOL_GPL(wm97xx_reg_read); ++EXPORT_SYMBOL_GPL(wm97xx_reg_write); ++EXPORT_SYMBOL_GPL(wm97xx_bus_type); ++EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops); ++EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops); ++ ++module_init(wm97xx_init); ++module_exit(wm97xx_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/include/linux/wm97xx.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/include/linux/wm97xx.h 2006-09-19 20:36:47.973052250 +0200 +@@ -0,0 +1,291 @@ ++ ++/* ++ * Register bits and API for Wolfson WM97xx series of codecs ++ */ ++ ++#ifndef _LINUX_WM97XX_H ++#define _LINUX_WM97XX_H ++ ++#include <sound/driver.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/ac97_codec.h> ++#include <sound/initval.h> ++#include <linux/types.h> ++#include <linux/list.h> ++#include <linux/input.h> /* Input device layer */ ++ ++/* ++ * WM97xx AC97 Touchscreen registers ++ */ ++#define AC97_WM97XX_DIGITISER1 0x76 ++#define AC97_WM97XX_DIGITISER2 0x78 ++#define AC97_WM97XX_DIGITISER_RD 0x7a ++#define AC97_WM9713_DIG1 0x74 ++#define AC97_WM9713_DIG2 AC97_WM97XX_DIGITISER1 ++#define AC97_WM9713_DIG3 AC97_WM97XX_DIGITISER2 ++ ++/* ++ * WM97xx register bits ++ */ ++#define WM97XX_POLL 0x8000 /* initiate a polling measurement */ ++#define WM97XX_ADCSEL_X 0x1000 /* x coord measurement */ ++#define WM97XX_ADCSEL_Y 0x2000 /* y coord measurement */ ++#define WM97XX_ADCSEL_PRES 0x3000 /* pressure measurement */ ++#define WM97XX_ADCSEL_MASK 0x7000 ++#define WM97XX_COO 0x0800 /* enable coordinate mode */ ++#define WM97XX_CTC 0x0400 /* enable continuous mode */ ++#define WM97XX_CM_RATE_93 0x0000 /* 93.75Hz continuous rate */ ++#define WM97XX_CM_RATE_187 0x0100 /* 187.5Hz continuous rate */ ++#define WM97XX_CM_RATE_375 0x0200 /* 375Hz continuous rate */ ++#define WM97XX_CM_RATE_750 0x0300 /* 750Hz continuous rate */ ++#define WM97XX_CM_RATE_8K 0x00f0 /* 8kHz continuous rate */ ++#define WM97XX_CM_RATE_12K 0x01f0 /* 12kHz continuous rate */ ++#define WM97XX_CM_RATE_24K 0x02f0 /* 24kHz continuous rate */ ++#define WM97XX_CM_RATE_48K 0x03f0 /* 48kHz continuous rate */ ++#define WM97XX_CM_RATE_MASK 0x03f0 ++#define WM97XX_RATE(i) (((i & 3) << 8) | ((i & 4) ? 0xf0 : 0)) ++#define WM97XX_DELAY(i) ((i << 4) & 0x00f0) /* sample delay times */ ++#define WM97XX_DELAY_MASK 0x00f0 ++#define WM97XX_SLEN 0x0008 /* slot read back enable */ ++#define WM97XX_SLT(i) ((i - 5) & 0x7) /* touchpanel slot selection (5-11) */ ++#define WM97XX_SLT_MASK 0x0007 ++#define WM97XX_PRP_DETW 0x4000 /* pen detect on, digitiser off, wake up */ ++#define WM97XX_PRP_DET 0x8000 /* pen detect on, digitiser off, no wake up */ ++#define WM97XX_PRP_DET_DIG 0xc000 /* pen detect on, digitiser on */ ++#define WM97XX_RPR 0x2000 /* wake up on pen down */ ++#define WM97XX_PEN_DOWN 0x8000 /* pen is down */ ++#define WM97XX_ADCSRC_MASK 0x7000 /* ADC source mask */ ++ ++#define WM97XX_AUX_ID1 0x8001 ++#define WM97XX_AUX_ID2 0x8002 ++#define WM97XX_AUX_ID3 0x8003 ++#define WM97XX_AUX_ID4 0x8004 ++ ++ ++/* WM9712 Bits */ ++#define WM9712_45W 0x1000 /* set for 5-wire touchscreen */ ++#define WM9712_PDEN 0x0800 /* measure only when pen down */ ++#define WM9712_WAIT 0x0200 /* wait until adc is read before next sample */ ++#define WM9712_PIL 0x0100 /* current used for pressure measurement. set 400uA else 200uA */ ++#define WM9712_MASK_HI 0x0040 /* hi on mask pin (47) stops conversions */ ++#define WM9712_MASK_EDGE 0x0080 /* rising/falling edge on pin delays sample */ ++#define WM9712_MASK_SYNC 0x00c0 /* rising/falling edge on mask initiates sample */ ++#define WM9712_RPU(i) (i&0x3f) /* internal pull up on pen detect (64k / rpu) */ ++#define WM9712_PD(i) (0x1 << i) /* power management */ ++ ++/* WM9712 Registers */ ++#define AC97_WM9712_POWER 0x24 ++#define AC97_WM9712_REV 0x58 ++ ++/* WM9705 Bits */ ++#define WM9705_PDEN 0x1000 /* measure only when pen is down */ ++#define WM9705_PINV 0x0800 /* inverts sense of pen down output */ ++#define WM9705_BSEN 0x0400 /* BUSY flag enable, pin47 is 1 when busy */ ++#define WM9705_BINV 0x0200 /* invert BUSY (pin47) output */ ++#define WM9705_WAIT 0x0100 /* wait until adc is read before next sample */ ++#define WM9705_PIL 0x0080 /* current used for pressure measurement. set 400uA else 200uA */ ++#define WM9705_PHIZ 0x0040 /* set PHONE and PCBEEP inputs to high impedance */ ++#define WM9705_MASK_HI 0x0010 /* hi on mask stops conversions */ ++#define WM9705_MASK_EDGE 0x0020 /* rising/falling edge on pin delays sample */ ++#define WM9705_MASK_SYNC 0x0030 /* rising/falling edge on mask initiates sample */ ++#define WM9705_PDD(i) (i & 0x000f) /* pen detect comparator threshold */ ++ ++ ++/* WM9713 Bits */ ++#define WM9713_PDPOL 0x0400 /* Pen down polarity */ ++#define WM9713_POLL 0x0200 /* initiate a polling measurement */ ++#define WM9713_CTC 0x0100 /* enable continuous mode */ ++#define WM9713_ADCSEL_X 0x0002 /* X measurement */ ++#define WM9713_ADCSEL_Y 0x0004 /* Y measurement */ ++#define WM9713_ADCSEL_PRES 0x0008 /* Pressure measurement */ ++#define WM9713_COO 0x0001 /* enable coordinate mode */ ++#define WM9713_PDEN 0x0800 /* measure only when pen down */ ++#define WM9713_ADCSEL_MASK 0x00fe /* ADC selection mask */ ++#define WM9713_WAIT 0x0200 /* coordinate wait */ ++ ++/* AUX ADC ID's */ ++#define TS_COMP1 0x0 ++#define TS_COMP2 0x1 ++#define TS_BMON 0x2 ++#define TS_WIPER 0x3 ++ ++/* ID numbers */ ++#define WM97XX_ID1 0x574d ++#define WM9712_ID2 0x4c12 ++#define WM9705_ID2 0x4c05 ++#define WM9713_ID2 0x4c13 ++ ++/* Codec GPIO's */ ++#define WM97XX_MAX_GPIO 16 ++#define WM97XX_GPIO_1 (1 << 1) ++#define WM97XX_GPIO_2 (1 << 2) ++#define WM97XX_GPIO_3 (1 << 3) ++#define WM97XX_GPIO_4 (1 << 4) ++#define WM97XX_GPIO_5 (1 << 5) ++#define WM97XX_GPIO_6 (1 << 6) ++#define WM97XX_GPIO_7 (1 << 7) ++#define WM97XX_GPIO_8 (1 << 8) ++#define WM97XX_GPIO_9 (1 << 9) ++#define WM97XX_GPIO_10 (1 << 10) ++#define WM97XX_GPIO_11 (1 << 11) ++#define WM97XX_GPIO_12 (1 << 12) ++#define WM97XX_GPIO_13 (1 << 13) ++#define WM97XX_GPIO_14 (1 << 14) ++#define WM97XX_GPIO_15 (1 << 15) ++ ++ ++#define AC97_LINK_FRAME 21 /* time in uS for AC97 link frame */ ++ ++ ++/*---------------- Return codes from sample reading functions ---------------*/ ++ ++/* More data is available; call the sample gathering function again */ ++#define RC_AGAIN 0x00000001 ++/* The returned sample is valid */ ++#define RC_VALID 0x00000002 ++/* The pen is up (the first RC_VALID without RC_PENUP means pen is down) */ ++#define RC_PENUP 0x00000004 ++/* The pen is down (RC_VALID implies RC_PENDOWN, but sometimes it is helpful ++ to tell the handler that the pen is down but we don't know yet his coords, ++ so the handler should not sleep or wait for pendown irq) */ ++#define RC_PENDOWN 0x00000008 ++ ++/* The wm97xx driver provides a private API for writing platform-specific ++ * drivers. ++ */ ++ ++/* The structure used to return arch specific sampled data into */ ++struct wm97xx_data { ++ int x; ++ int y; ++ int p; ++}; ++ ++/* Codec GPIO status ++ */ ++typedef enum { ++ WM97XX_GPIO_HIGH, ++ WM97XX_GPIO_LOW ++} wm97xx_gpio_status_t; ++ ++/* Codec GPIO direction ++ */ ++typedef enum { ++ WM97XX_GPIO_IN, ++ WM97XX_GPIO_OUT ++} wm97xx_gpio_dir_t; ++ ++/* Codec GPIO polarity ++ */ ++typedef enum { ++ WM97XX_GPIO_POL_HIGH, ++ WM97XX_GPIO_POL_LOW ++} wm97xx_gpio_pol_t; ++ ++/* Codec GPIO sticky ++ */ ++typedef enum { ++ WM97XX_GPIO_STICKY, ++ WM97XX_GPIO_NOTSTICKY ++} wm97xx_gpio_sticky_t; ++ ++/* Codec GPIO wake ++ */ ++typedef enum { ++ WM97XX_GPIO_WAKE, ++ WM97XX_GPIO_NOWAKE ++} wm97xx_gpio_wake_t; ++ ++ ++/* ++ * Digitiser ioctl commands ++ */ ++#define WM97XX_DIG_START 0x1 ++#define WM97XX_DIG_STOP 0x2 ++#define WM97XX_PHY_INIT 0x3 ++#define WM97XX_AUX_PREPARE 0x4 ++#define WM97XX_DIG_RESTORE 0x5 ++ ++struct wm97xx; ++extern struct wm97xx_codec_drv wm97xx_codec; ++ ++/* ++ * Codec driver interface - allows mapping to WM9705/12/13 and newer codecs ++ */ ++struct wm97xx_codec_drv { ++ u16 id; ++ char *name; ++ int (*poll_sample) (struct wm97xx *, int adcsel, int *sample); /* read 1 sample */ ++ int (*poll_touch) (struct wm97xx *, struct wm97xx_data *); /* read X,Y,[P] in poll */ ++ int (*digitiser_ioctl) (struct wm97xx *, int cmd); ++ int (*acc_enable) (struct wm97xx *, int enable); ++}; ++ ++ ++/* Machine specific and accelerated touch operations */ ++struct wm97xx_mach_ops { ++ ++ /* accelerated touch readback - coords are transmited on AC97 link */ ++ int acc_enabled; ++ void (*acc_pen_up) (struct wm97xx *); ++ int (*acc_pen_down) (struct wm97xx *); ++ int (*acc_startup) (struct wm97xx *); ++ void (*acc_shutdown) (struct wm97xx *); ++ ++ /* pre and post sample - can be used to minimise any analog noise */ ++ void (*pre_sample) (int); /* function to run before sampling */ ++ void (*post_sample) (int); /* function to run after sampling */ ++}; ++ ++struct wm97xx { ++ u16 dig[3], id, gpio[6], misc; /* Cached codec registers */ ++ u16 dig_save[3]; /* saved during aux reading */ ++ struct wm97xx_codec_drv *codec; /* attached codec driver*/ ++ struct input_dev* input_dev; /* touchscreen input device */ ++ ac97_t *ac97; /* ALSA codec access */ ++ struct device *dev; /* ALSA device */ ++ struct device *battery_dev; ++ struct device *touch_dev; ++ struct wm97xx_mach_ops *mach_ops; ++ struct mutex codec_mutex; ++ struct completion ts_init; ++ struct completion ts_exit; ++ struct task_struct *ts_task; ++ unsigned int pen_irq; /* Pen IRQ number in use */ ++ wait_queue_head_t pen_irq_wait; /* Pen IRQ wait queue */ ++ struct workqueue_struct *pen_irq_workq; ++ struct work_struct pen_event_work; ++ u16 acc_slot; /* AC97 slot used for acc touch data */ ++ u16 acc_rate; /* acc touch data rate */ ++ unsigned int ts_use_count; ++ unsigned pen_is_down:1; /* Pen is down */ ++ unsigned aux_waiting:1; /* aux measurement waiting */ ++ unsigned pen_probably_down:1; /* used in polling mode */ ++}; ++ ++/* Codec GPIO access (not supported on WM9705) ++ * This can be used to set/get codec GPIO and Virtual GPIO status. ++ */ ++wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio); ++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, ++ wm97xx_gpio_status_t status); ++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, ++ wm97xx_gpio_dir_t dir, ++ wm97xx_gpio_pol_t pol, ++ wm97xx_gpio_sticky_t sticky, ++ wm97xx_gpio_wake_t wake); ++ ++/* codec AC97 IO access */ ++int wm97xx_reg_read(struct wm97xx *wm, u16 reg); ++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val); ++ ++/* aux adc readback */ ++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel); ++ ++/* machine ops */ ++int wm97xx_register_mach_ops(struct wm97xx *, struct wm97xx_mach_ops *); ++void wm97xx_unregister_mach_ops(struct wm97xx *); ++ ++extern struct bus_type wm97xx_bus_type; ++#endif +Index: linux-2.6.17/drivers/input/touchscreen/Kconfig +=================================================================== +--- linux-2.6.17.orig/drivers/input/touchscreen/Kconfig 2006-09-19 20:35:35.060495500 +0200 ++++ linux-2.6.17/drivers/input/touchscreen/Kconfig 2006-09-19 20:36:47.965051750 +0200 +@@ -121,4 +121,57 @@ config TOUCHSCREEN_TSC2101 + To compile this driver as a module, choose M here: the + module will be called ads7846_ts. + ++config TOUCHSCREEN_WM97XX ++ tristate "Support for WM97xx AC97 touchscreen controllers" ++ depends SND_AC97_BUS ++ ++choice ++ prompt "WM97xx codec type" ++ ++config TOUCHSCREEN_WM9705 ++ bool "WM9705 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have the wm9705 touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9705. ++ ++config TOUCHSCREEN_WM9712 ++ bool "WM9712 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have the wm9712 touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9712. ++ ++config TOUCHSCREEN_WM9713 ++ bool "WM9713 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have the wm9713 touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9713. ++ ++endchoice ++ ++config TOUCHSCREEN_WM97XX_PXA ++ tristate "WM97xx PXA accelerated touch" ++ depends on TOUCHSCREEN_WM97XX && ARCH_PXA ++ help ++ Say Y here for continuous mode touch on the PXA ++ ++ If unsure, say N ++ ++ To compile this driver as a module, choose M here: the ++ module will be called pxa-wm97xx ++ + endif +Index: linux-2.6.17/drivers/input/touchscreen/Makefile +=================================================================== +--- linux-2.6.17.orig/drivers/input/touchscreen/Makefile 2006-09-19 20:35:35.072496250 +0200 ++++ linux-2.6.17/drivers/input/touchscreen/Makefile 2006-09-19 20:37:40.540337500 +0200 +@@ -4,6 +4,8 @@ + + # Each configuration option enables a list of files. + ++wm97xx-ts-objs := wm97xx-core.o ++ + obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o + obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o + obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o +@@ -13,3 +15,16 @@ obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtou + obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o + obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o + obj-$(CONFIG_TOUCHSCREEN_TSC2101) += tsc2101_ts.o ++obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o ++obj-$(CONFIG_TOUCHSCREEN_WM97XX_PXA) += pxa-wm97xx.o ++ ++ifeq ($(CONFIG_TOUCHSCREEN_WM9713),y) ++wm97xx-ts-objs += wm9713.o ++endif ++ ++ifeq ($(CONFIG_TOUCHSCREEN_WM9712),y) ++wm97xx-ts-objs += wm9712.o ++endif ++ifeq ($(CONFIG_TOUCHSCREEN_WM9705),y) ++wm97xx-ts-objs += wm9705.o ++endif +Index: linux-2.6.17/drivers/input/touchscreen/pxa-wm97xx.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/pxa-wm97xx.c 2006-09-19 20:36:47.965051750 +0200 +@@ -0,0 +1,289 @@ ++/* ++ * pxa-wm97xx.c -- pxa-wm97xx Continuous Touch screen driver for ++ * Wolfson WM97xx AC97 Codecs. ++ * ++ * Copyright 2004 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Notes: ++ * This is a wm97xx extended touch driver to capture touch ++ * data in a continuous manner on the Intel XScale archictecture ++ * ++ * Features: ++ * - codecs supported:- WM9705, WM9712, WM9713 ++ * - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x ++ * ++ * Revision history ++ * 18th Aug 2004 Initial version. ++ * 26th Jul 2005 Improved continous read back and added FIFO flushing. ++ * 06th Sep 2005 Mike Arthur <linux@wolfsonmicro.com> ++ * Moved to using the wm97xx bus ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/irq.h> ++#include <linux/wm97xx.h> ++#include <asm/io.h> ++#include <asm/arch/pxa-regs.h> ++ ++#define VERSION "0.13" ++ ++struct continuous { ++ u16 id; /* codec id */ ++ u8 code; /* continuous code */ ++ u8 reads; /* number of coord reads per read cycle */ ++ u32 speed; /* number of coords per second */ ++}; ++ ++#define WM_READS(sp) ((sp / HZ) + 1) ++ ++static const struct continuous cinfo[] = { ++ {WM9705_ID2, 0, WM_READS(94), 94}, ++ {WM9705_ID2, 1, WM_READS(188), 188}, ++ {WM9705_ID2, 2, WM_READS(375), 375}, ++ {WM9705_ID2, 3, WM_READS(750), 750}, ++ {WM9712_ID2, 0, WM_READS(94), 94}, ++ {WM9712_ID2, 1, WM_READS(188), 188}, ++ {WM9712_ID2, 2, WM_READS(375), 375}, ++ {WM9712_ID2, 3, WM_READS(750), 750}, ++ {WM9713_ID2, 0, WM_READS(94), 94}, ++ {WM9713_ID2, 1, WM_READS(120), 120}, ++ {WM9713_ID2, 2, WM_READS(154), 154}, ++ {WM9713_ID2, 3, WM_READS(188), 188}, ++}; ++ ++/* continuous speed index */ ++static int sp_idx = 0; ++static u16 last = 0, tries = 0; ++ ++/* ++ * Pen sampling frequency (Hz) in continuous mode. ++ */ ++static int cont_rate = 200; ++module_param(cont_rate, int, 0); ++MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)"); ++ ++/* ++ * Pen down detection. ++ * ++ * This driver can either poll or use an interrupt to indicate a pen down ++ * event. If the irq request fails then it will fall back to polling mode. ++ */ ++static int pen_int = 1; ++module_param(pen_int, int, 0); ++MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)"); ++ ++/* ++ * Pressure readback. ++ * ++ * Set to 1 to read back pen down pressure ++ */ ++static int pressure = 0; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)"); ++ ++/* ++ * AC97 touch data slot. ++ * ++ * Touch screen readback data ac97 slot ++ */ ++static int ac97_touch_slot = 5; ++module_param(ac97_touch_slot, int, 0); ++MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number"); ++ ++ ++/* flush AC97 slot 5 FIFO on pxa machines */ ++#ifdef CONFIG_PXA27x ++void wm97xx_acc_pen_up (struct wm97xx* wm) ++{ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ ++ while (MISR & (1 << 2)) ++ MODR; ++} ++#else ++void wm97xx_acc_pen_up (struct wm97xx* wm) ++{ ++ int count = 16; ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ ++ while (count < 16) { ++ MODR; ++ count--; ++ } ++} ++#endif ++ ++int wm97xx_acc_pen_down (struct wm97xx* wm) ++{ ++ u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES; ++ int reads = 0; ++ ++ /* data is never immediately available after pen down irq */ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ ++ if (tries > 5){ ++ tries = 0; ++ return RC_PENUP; ++ } ++ ++ x = MODR; ++ if (x == last) { ++ tries++; ++ return RC_AGAIN; ++ } ++ last = x; ++ do { ++ if (reads) ++ x= MODR; ++ y= MODR; ++ if (pressure) ++ p = MODR; ++ ++ /* are samples valid */ ++ if ((x & 0x7000) != WM97XX_ADCSEL_X || ++ (y & 0x7000) != WM97XX_ADCSEL_Y || ++ (p & 0x7000) != WM97XX_ADCSEL_PRES) ++ goto up; ++ ++ /* coordinate is good */ ++ tries = 0; ++ //printk("x %x y %x p %x\n", x,y,p); ++ input_report_abs (wm->input_dev, ABS_X, x & 0xfff); ++ input_report_abs (wm->input_dev, ABS_Y, y & 0xfff); ++ input_report_abs (wm->input_dev, ABS_PRESSURE, p & 0xfff); ++ input_sync (wm->input_dev); ++ reads++; ++ } while (reads < cinfo[sp_idx].reads); ++up: ++ return RC_PENDOWN | RC_AGAIN; ++} ++ ++int wm97xx_acc_startup(struct wm97xx* wm) ++{ ++ int idx = 0; ++ ++ /* check we have a codec */ ++ if (wm->ac97 == NULL) ++ return -ENODEV; ++ ++ /* Go you big red fire engine */ ++ for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) { ++ if (wm->id != cinfo[idx].id) ++ continue; ++ sp_idx = idx; ++ if (cont_rate <= cinfo[idx].speed) ++ break; ++ } ++ wm->acc_rate = cinfo[sp_idx].code; ++ wm->acc_slot = ac97_touch_slot; ++ printk(KERN_INFO "pxa2xx accelerated touchscreen driver, %d samples (sec)\n", ++ cinfo[sp_idx].speed); ++ ++ /* codec specific irq config */ ++ if (pen_int) { ++ switch (wm->id) { ++ case WM9705_ID2: ++ wm->pen_irq = IRQ_GPIO(4); ++ set_irq_type(IRQ_GPIO(4), IRQT_BOTHEDGE); ++ break; ++ case WM9712_ID2: ++ case WM9713_ID2: ++ /* enable pen down interrupt */ ++ /* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */ ++ wm->pen_irq = MAINSTONE_AC97_IRQ; ++ wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, ++ WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_STICKY, WM97XX_GPIO_WAKE); ++ wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT, ++ WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_NOTSTICKY, WM97XX_GPIO_NOWAKE); ++ break; ++ default: ++ printk(KERN_WARNING "pen down irq not supported on this device\n"); ++ pen_int = 0; ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++void wm97xx_acc_shutdown(struct wm97xx* wm) ++{ ++ /* codec specific deconfig */ ++ if (pen_int) { ++ switch (wm->id & 0xffff) { ++ case WM9705_ID2: ++ wm->pen_irq = 0; ++ break; ++ case WM9712_ID2: ++ case WM9713_ID2: ++ /* disable interrupt */ ++ wm->pen_irq = 0; ++ break; ++ } ++ } ++} ++ ++static struct wm97xx_mach_ops pxa_mach_ops = { ++ .acc_enabled = 1, ++ .acc_pen_up = wm97xx_acc_pen_up, ++ .acc_pen_down = wm97xx_acc_pen_down, ++ .acc_startup = wm97xx_acc_startup, ++ .acc_shutdown = wm97xx_acc_shutdown, ++}; ++ ++int pxa_wm97xx_probe(struct device *dev) ++{ ++ struct wm97xx *wm = dev->driver_data; ++ return wm97xx_register_mach_ops (wm, &pxa_mach_ops); ++} ++ ++int pxa_wm97xx_remove(struct device *dev) ++{ ++ struct wm97xx *wm = dev->driver_data; ++ wm97xx_unregister_mach_ops (wm); ++ return 0; ++} ++ ++static struct device_driver pxa_wm97xx_driver = { ++ .name = "wm97xx-touchscreen", ++ .bus = &wm97xx_bus_type, ++ .owner = THIS_MODULE, ++ .probe = pxa_wm97xx_probe, ++ .remove = pxa_wm97xx_remove ++}; ++ ++static int __init pxa_wm97xx_init(void) ++{ ++ return driver_register(&pxa_wm97xx_driver); ++} ++ ++static void __exit pxa_wm97xx_exit(void) ++{ ++ driver_unregister(&pxa_wm97xx_driver); ++} ++ ++module_init(pxa_wm97xx_init); ++module_exit(pxa_wm97xx_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>"); ++MODULE_DESCRIPTION("wm97xx continuous touch driver for pxa2xx"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/drivers/input/touchscreen/wm9705.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/wm9705.c 2006-09-19 20:36:47.969052000 +0200 +@@ -0,0 +1,360 @@ ++/* ++ * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Revision history ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added pre and post sample calls. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9705_VERSION "0.62" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil = 0; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 4; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Pen detect comparator threshold. ++ * ++ * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold ++ * i.e. 1 = Vmid/15 threshold ++ * 15 = Vmid/1 threshold ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down events. ++ */ ++static int pdd = 8; ++module_param(pdd, int, 0); ++MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold"); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask = 0; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, // 1 AC97 Link frames ++ 42, // 2 ++ 84, // 4 ++ 167, // 8 ++ 333, // 16 ++ 667, // 32 ++ 1000, // 48 ++ 1333, // 64 ++ 2000, // 96 ++ 2667, // 128 ++ 3333, // 160 ++ 4000, // 192 ++ 4667, // 224 ++ 5333, // 256 ++ 6000, // 288 ++ 0 // No delay, switch matrix always on ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay (3 * AC97_LINK_FRAME + delay_table [d]); ++} ++ ++/* ++ * set up the physical settings of the WM9705 ++ */ ++static void init_wm9705_phy(struct wm97xx* wm) ++{ ++ u16 dig1 = 0, dig2 = WM97XX_RPR; ++ ++ /* ++ * mute VIDEO and AUX as they share X and Y touchscreen ++ * inputs on the WM9705 ++ */ ++ wm97xx_reg_write(wm, AC97_AUX, 0x8000); ++ wm97xx_reg_write(wm, AC97_VIDEO, 0x8000); ++ ++ /* touchpanel pressure current*/ ++ if (pil == 2) { ++ dig2 |= WM9705_PIL; ++ dbg("setting pressure measurement current to 400uA."); ++ } else if (pil) ++ dbg("setting pressure measurement current to 200uA."); ++ if(!pil) ++ pressure = 0; ++ ++ /* polling mode sample settling delay */ ++ if (delay!=4) { ++ if (delay < 0 || delay > 15) { ++ dbg("supplied delay out of range."); ++ delay = 4; ++ } ++ } ++ dig1 &= 0xff0f; ++ dig1 |= WM97XX_DELAY(delay); ++ dbg("setting adc sample delay to %d u Secs.", delay_table[delay]); ++ ++ /* WM9705 pdd */ ++ dig2 |= (pdd & 0x000f); ++ dbg("setting pdd to Vmid/%d", 1 - (pdd & 0x000f)); ++ ++ /* mask */ ++ dig2 |= ((mask & 0x3) << 4); ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++} ++ ++static int wm9705_digitiser_ioctl(struct wm97xx* wm, int cmd) ++{ ++ switch(cmd) { ++ case WM97XX_DIG_START: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ break; ++ case WM97XX_DIG_STOP: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] & ~WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_AUX_PREPARE: ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_DIG_RESTORE: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); ++ break; ++ case WM97XX_PHY_INIT: ++ init_wm9705_phy(wm); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static inline int is_pden (struct wm97xx* wm) ++{ ++ return wm->dig[2] & WM9705_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9705 adc in polling mode. ++ */ ++static int wm9705_poll_sample (struct wm97xx* wm, int adcsel, int *sample) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = ((adcsel & 0x7fff) + 3) << 12; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay (delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { ++ dbg ("adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSEL_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Sample the WM9705 touchscreen in polling mode ++ */ ++static int wm9705_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID) ++ return rc; ++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID) ++ return rc; ++ if (pil) { ++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9705 continuous mode, i.e. touch data is streamed across an AC97 slot ++ */ ++static int wm9705_acc_enable (struct wm97xx* wm, int enable) ++{ ++ u16 dig1, dig2; ++ int ret = 0; ++ ++ dig1 = wm->dig[1]; ++ dig2 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | ++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK); ++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | ++ WM97XX_DELAY (delay) | ++ WM97XX_SLT (wm->acc_slot) | ++ WM97XX_RATE (wm->acc_rate); ++ if (pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ dig2 |= WM9705_PDEN; ++ } else { ++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); ++ dig2 &= ~WM9705_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++ return ret; ++} ++ ++struct wm97xx_codec_drv wm97xx_codec = { ++ .id = WM9705_ID2, ++ .name = "wm9705", ++ .poll_sample = wm9705_poll_sample, ++ .poll_touch = wm9705_poll_touch, ++ .acc_enable = wm9705_acc_enable, ++ .digitiser_ioctl = wm9705_digitiser_ioctl, ++}; ++ ++EXPORT_SYMBOL_GPL(wm97xx_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM9705 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/drivers/input/touchscreen/wm9712.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/wm9712.c 2006-09-19 20:36:47.969052000 +0200 +@@ -0,0 +1,464 @@ ++/* ++ * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Revision history ++ * 4th Jul 2005 Initial version. ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added pre and post sample calls. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9712_VERSION "0.61" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set internal pull up for pen detect. ++ * ++ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) ++ * i.e. pull up resistance = 64k Ohms / rpu. ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down event. ++ */ ++static int rpu = 3; ++module_param(rpu, int, 0); ++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil = 0; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 3; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Set five_wire = 1 to use a 5 wire touchscreen. ++ * ++ * NOTE: Five wire mode does not allow for readback of pressure. ++ */ ++static int five_wire; ++module_param(five_wire, int, 0); ++MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen."); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask = 0; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * Coordinate Polling Enable. ++ * ++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together ++ * for every poll. ++ */ ++static int coord = 0; ++module_param(coord, int, 0); ++MODULE_PARM_DESC(coord, "Polling coordinate mode"); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, // 1 AC97 Link frames ++ 42, // 2 ++ 84, // 4 ++ 167, // 8 ++ 333, // 16 ++ 667, // 32 ++ 1000, // 48 ++ 1333, // 64 ++ 2000, // 96 ++ 2667, // 128 ++ 3333, // 160 ++ 4000, // 192 ++ 4667, // 224 ++ 5333, // 256 ++ 6000, // 288 ++ 0 // No delay, switch matrix always on ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay (3 * AC97_LINK_FRAME + delay_table [d]); ++} ++ ++/* ++ * set up the physical settings of the WM9712 ++ */ ++static void init_wm9712_phy(struct wm97xx* wm) ++{ ++ u16 dig1 = 0; ++ u16 dig2 = WM97XX_RPR | WM9712_RPU(1); ++ ++ /* WM9712 rpu */ ++ if (rpu) { ++ dig2 &= 0xffc0; ++ dig2 |= WM9712_RPU(rpu); ++ dbg("setting pen detect pull-up to %d Ohms",64000 / rpu); ++ } ++ ++ /* touchpanel pressure current*/ ++ if (pil == 2) { ++ dig2 |= WM9712_PIL; ++ dbg("setting pressure measurement current to 400uA."); ++ } else if (pil) ++ dbg("setting pressure measurement current to 200uA."); ++ if(!pil) ++ pressure = 0; ++ ++ /* WM9712 five wire */ ++ if (five_wire) { ++ dig2 |= WM9712_45W; ++ dbg("setting 5-wire touchscreen mode."); ++ } ++ ++ /* polling mode sample settling delay */ ++ if (delay < 0 || delay > 15) { ++ dbg("supplied delay out of range."); ++ delay = 4; ++ } ++ dig1 &= 0xff0f; ++ dig1 |= WM97XX_DELAY(delay); ++ dbg("setting adc sample delay to %d u Secs.", delay_table[delay]); ++ ++ /* mask */ ++ dig2 |= ((mask & 0x3) << 6); ++ if (mask) { ++ u16 reg; ++ /* Set GPIO4 as Mask Pin*/ ++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4); ++ } ++ ++ /* wait - coord mode */ ++ if(coord) ++ dig2 |= WM9712_WAIT; ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++} ++ ++static int wm9712_digitiser_ioctl(struct wm97xx* wm, int cmd) ++{ ++ u16 dig2 = wm->dig[2]; ++ ++ switch(cmd) { ++ case WM97XX_DIG_START: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ break; ++ case WM97XX_DIG_STOP: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 & ~WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_AUX_PREPARE: ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_DIG_RESTORE: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); ++ break; ++ case WM97XX_PHY_INIT: ++ init_wm9712_phy(wm); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static inline int is_pden (struct wm97xx* wm) ++{ ++ return wm->dig[2] & WM9712_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9712 adc in polling mode. ++ */ ++static int wm9712_poll_sample (struct wm97xx* wm, int adcsel, int *sample) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = ((adcsel & 0x7fff) + 3) << 12; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay (delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { ++ dbg ("adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSEL_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Read a coord from the WM9712 adc in polling mode. ++ */ ++static int wm9712_poll_coord (struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, ++ WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion and read x */ ++ poll_delay(delay); ++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ /* read back y data */ ++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (pil) ++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ else ++ data->p = DEFAULT_PRESSURE; ++ ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ /* check we have correct sample */ ++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) ++ goto err; ++ if(pil && !(data->p & WM97XX_ADCSEL_PRES)) ++ goto err; ++ ++ if (!(data->x & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ return RC_VALID; ++err: ++ return RC_PENUP; ++} ++ ++/* ++ * Sample the WM9712 touchscreen in polling mode ++ */ ++static int wm9712_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if(coord) { ++ if((rc = wm9712_poll_coord(wm, data)) != RC_VALID) ++ return rc; ++ } else { ++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID) ++ return rc; ++ ++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID) ++ return rc; ++ ++ if (pil && !five_wire) { ++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ } ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9712 continuous mode, i.e. touch data is streamed across an AC97 slot ++ */ ++static int wm9712_acc_enable (struct wm97xx* wm, int enable) ++{ ++ u16 dig1, dig2; ++ int ret = 0; ++ ++ dig1 = wm->dig[1]; ++ dig2 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | ++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK); ++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | ++ WM97XX_DELAY (delay) | ++ WM97XX_SLT (wm->acc_slot) | ++ WM97XX_RATE (wm->acc_rate); ++ if (pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ dig2 |= WM9712_PDEN; ++ } else { ++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); ++ dig2 &= ~WM9712_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++ return 0; ++} ++ ++struct wm97xx_codec_drv wm97xx_codec = { ++ .id = WM9712_ID2, ++ .name = "wm9712", ++ .poll_sample = wm9712_poll_sample, ++ .poll_touch = wm9712_poll_touch, ++ .acc_enable = wm9712_acc_enable, ++ .digitiser_ioctl = wm9712_digitiser_ioctl, ++}; ++ ++EXPORT_SYMBOL_GPL(wm97xx_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM9712 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/drivers/input/touchscreen/wm9713.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/wm9713.c 2006-09-19 20:36:47.969052000 +0200 +@@ -0,0 +1,461 @@ ++/* ++ * wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Revision history ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added pre and post sample calls. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9713_VERSION "0.53" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set internal pull up for pen detect. ++ * ++ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) ++ * i.e. pull up resistance = 64k Ohms / rpu. ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down event. ++ */ ++static int rpu = 1; ++module_param(rpu, int, 0); ++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil = 0; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 4; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask = 0; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * Coordinate Polling Enable. ++ * ++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together ++ * for every poll. ++ */ ++static int coord = 1; ++module_param(coord, int, 0); ++MODULE_PARM_DESC(coord, "Polling coordinate mode"); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, // 1 AC97 Link frames ++ 42, // 2 ++ 84, // 4 ++ 167, // 8 ++ 333, // 16 ++ 667, // 32 ++ 1000, // 48 ++ 1333, // 64 ++ 2000, // 96 ++ 2667, // 128 ++ 3333, // 160 ++ 4000, // 192 ++ 4667, // 224 ++ 5333, // 256 ++ 6000, // 288 ++ 0 // No delay, switch matrix always on ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay (3 * AC97_LINK_FRAME + delay_table [d]); ++} ++ ++/* ++ * set up the physical settings of the WM9713 ++ */ ++static void init_wm9713_phy(struct wm97xx* wm) ++{ ++ u16 dig1 = 0, dig2, dig3; ++ ++ /* default values */ ++ dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5); ++ dig3= WM9712_RPU(1); ++ ++ /* rpu */ ++ if (rpu) { ++ dig3 &= 0xffc0; ++ dig3 |= WM9712_RPU(rpu); ++ info("setting pen detect pull-up to %d Ohms",64000 / rpu); ++ } ++ ++ /* touchpanel pressure */ ++ if (pil == 2) { ++ dig3 |= WM9712_PIL; ++ info("setting pressure measurement current to 400uA."); ++ } else if (pil) ++ info ("setting pressure measurement current to 200uA."); ++ if(!pil) ++ pressure = 0; ++ ++ /* sample settling delay */ ++ if (delay < 0 || delay > 15) { ++ info ("supplied delay out of range."); ++ delay = 4; ++ info("setting adc sample delay to %d u Secs.", delay_table[delay]); ++ } ++ dig2 &= 0xff0f; ++ dig2 |= WM97XX_DELAY(delay); ++ ++ /* mask */ ++ dig3 |= ((mask & 0x3) << 4); ++ if(coord) ++ dig3 |= WM9713_WAIT; ++ ++ wm->misc = wm97xx_reg_read(wm, 0x5a); ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3); ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0); ++} ++ ++static int wm9713_digitiser_ioctl(struct wm97xx* wm, int cmd) ++{ ++ u16 val = 0; ++ ++ switch(cmd){ ++ case WM97XX_DIG_START: ++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ break; ++ case WM97XX_DIG_STOP: ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] & ~WM97XX_PRP_DET_DIG); ++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000); ++ break; ++ case WM97XX_AUX_PREPARE: ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_DIG_RESTORE: ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]); ++ break; ++ case WM97XX_PHY_INIT: ++ init_wm9713_phy(wm); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static inline int is_pden (struct wm97xx* wm) ++{ ++ return wm->dig[2] & WM9713_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9713 adc in polling mode. ++ */ ++static int wm9713_poll_sample (struct wm97xx* wm, int adcsel, int *sample) ++{ ++ u16 dig1; ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = 1 << ((adcsel & 0x7fff) + 3); ++ ++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | adcsel |WM9713_POLL); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay(delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample =wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSRC_MASK) != ffs(adcsel >> 1) << 12) { ++ dbg ("adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSRC_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Read a coordinate from the WM9713 adc in polling mode. ++ */ ++static int wm9713_poll_coord (struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ u16 dig1; ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ if(pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | WM9713_POLL | WM9713_COO); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay(delay); ++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ /* read back data */ ++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (pil) ++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ else ++ data->p = DEFAULT_PRESSURE; ++ ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ /* check we have correct sample */ ++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) ++ goto err; ++ if(pil && !(data->p & WM97XX_ADCSEL_PRES)) ++ goto err; ++ ++ if (!(data->x & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ return RC_VALID; ++err: ++ return RC_PENUP; ++} ++ ++/* ++ * Sample the WM9713 touchscreen in polling mode ++ */ ++static int wm9713_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if(coord) { ++ if((rc = wm9713_poll_coord(wm, data)) != RC_VALID) ++ return rc; ++ } else { ++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x)) != RC_VALID) ++ return rc; ++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y)) != RC_VALID) ++ return rc; ++ if (pil) { ++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES, &data->p)) != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ } ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9713 continuous mode, i.e. touch data is streamed across an AC97 slot ++ */ ++static int wm9713_acc_enable (struct wm97xx* wm, int enable) ++{ ++ u16 dig1, dig2, dig3; ++ int ret = 0; ++ ++ dig1 = wm->dig[0]; ++ dig2 = wm->dig[1]; ++ dig3 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && ++ (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X | WM9713_ADCSEL_Y; ++ if (pil) ++ dig1 |= WM9713_ADCSEL_PRES; ++ dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK | WM97XX_CM_RATE_MASK); ++ dig2 |= WM97XX_SLEN | WM97XX_DELAY (delay) | ++ WM97XX_SLT (wm->acc_slot) | WM97XX_RATE (wm->acc_rate); ++ dig3 |= WM9713_PDEN; ++ } else { ++ dig1 &= ~(WM9713_CTC | WM9713_COO); ++ dig2 &= ~WM97XX_SLEN; ++ dig3 &= ~WM9713_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3); ++ return ret; ++} ++ ++struct wm97xx_codec_drv wm97xx_codec = { ++ .id = WM9713_ID2, ++ .name = "wm9713", ++ .poll_sample = wm9713_poll_sample, ++ .poll_touch = wm9713_poll_touch, ++ .acc_enable = wm9713_acc_enable, ++ .digitiser_ioctl = wm9713_digitiser_ioctl, ++}; ++ ++EXPORT_SYMBOL_GPL(wm97xx_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM9713 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/drivers/input/touchscreen/wm97xx-core.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/wm97xx-core.c 2006-09-19 20:36:47.969052000 +0200 +@@ -0,0 +1,912 @@ ++/* ++ * wm97xx-core.c -- Touch screen driver core for Wolfson WM9705, WM9712 ++ * and WM9713 AC97 Codecs. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Notes: ++ * ++ * Features: ++ * - supports WM9705, WM9712, WM9713 ++ * - polling mode ++ * - continuous mode (arch-dependent) ++ * - adjustable rpu/dpp settings ++ * - adjustable pressure current ++ * - adjustable sample settle delay ++ * - 4 and 5 wire touchscreens (5 wire is WM9712 only) ++ * - pen down detection ++ * - battery monitor ++ * - sample AUX adc's ++ * - power management ++ * - codec GPIO ++ * - codec event notification ++ * Todo ++ * - Support for async sampling control for noisy LCD's. ++ * ++ * Revision history ++ * 7th May 2003 Initial version. ++ * 6th June 2003 Added non module support and AC97 registration. ++ * 18th June 2003 Added AUX adc sampling. ++ * 23rd June 2003 Did some minimal reformatting, fixed a couple of ++ * codec_mutexing bugs and noted a race to fix. ++ * 24th June 2003 Added power management and fixed race condition. ++ * 10th July 2003 Changed to a misc device. ++ * 31st July 2003 Moved TS_EVENT and TS_CAL to wm97xx.h ++ * 8th Aug 2003 Added option for read() calling wm97xx_sample_touch() ++ * because some ac97_read/ac_97_write call schedule() ++ * 7th Nov 2003 Added Input touch event interface, stanley.cai@intel.com ++ * 13th Nov 2003 Removed h3600 touch interface, added interrupt based ++ * pen down notification and implemented continous mode ++ * on XScale arch. ++ * 16th Nov 2003 Ian Molton <spyro@f2s.com> ++ * Modified so that it suits the new 2.6 driver model. ++ * 25th Jan 2004 Andrew Zabolotny <zap@homelink.ru> ++ * Implemented IRQ-driven pen down detection, implemented ++ * the private API meant to be exposed to platform-specific ++ * drivers, reorganized the driver so that it supports ++ * an arbitrary number of devices. ++ * 1st Feb 2004 Moved continuous mode handling to a separate ++ * architecture-dependent file. For now only PXA ++ * built-in AC97 controller is supported (pxa-ac97-wm97xx.c). ++ * 11th Feb 2004 Reduced CPU usage by keeping a cached copy of both ++ * digitizer registers instead of reading them every time. ++ * A reorganization of the whole code for better ++ * error handling. ++ * 17th Apr 2004 Added BMON support. ++ * 17th Nov 2004 Added codec GPIO, codec event handling (real and virtual ++ * GPIOs) and 2.6 power management. ++ * 29th Nov 2004 Added WM9713 support. ++ * 4th Jul 2005 Moved codec specific code out to seperate files. ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added bus interface. ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/string.h> ++#include <linux/proc_fs.h> ++#include <linux/pm.h> ++#include <linux/interrupt.h> ++#include <linux/bitops.h> ++#include <linux/workqueue.h> ++#include <linux/device.h> ++#include <linux/wm97xx.h> ++#include <asm/uaccess.h> ++#include <asm/io.h> ++ ++#define TS_NAME "wm97xx" ++#define WM_CORE_VERSION "0.63" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * WM97xx - enable/disable AUX ADC sysfs ++ */ ++static int aux_sys = 1; ++module_param(aux_sys, int, 0); ++MODULE_PARM_DESC(aux_sys, "enable AUX ADC sysfs entries"); ++ ++/* ++ * WM97xx - enable/disable codec status sysfs ++ */ ++static int status_sys = 1; ++module_param(status_sys, int, 0); ++MODULE_PARM_DESC(status_sys, "enable codec status sysfs entries"); ++ ++/* ++ * Touchscreen absolute values ++ * ++ * These parameters are used to help the input layer discard out of ++ * range readings and reduce jitter etc. ++ * ++ * o min, max:- indicate the min and max values your touch screen returns ++ * o fuzz:- use a higher number to reduce jitter ++ * ++ * The default values correspond to Mainstone II in QVGA mode ++ * ++ * Please read ++ * Documentation/input/input-programming.txt for more details. ++ */ ++ ++static int abs_x[3] = {350,3900,5}; ++module_param_array(abs_x, int, NULL, 0); ++MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz"); ++ ++static int abs_y[3] = {320,3750,40}; ++module_param_array(abs_y, int, NULL, 0); ++MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz"); ++ ++static int abs_p[3] = {0,150,4}; ++module_param_array(abs_p, int, NULL, 0); ++MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz"); ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* codec AC97 IO access */ ++int wm97xx_reg_read(struct wm97xx *wm, u16 reg) ++{ ++ if (wm->ac97) ++ return wm->ac97->bus->ops->read(wm->ac97, reg); ++ else ++ return -1; ++} ++ ++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val) ++{ ++ /* cache digitiser registers */ ++ if(reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3) ++ wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val; ++ ++ /* cache gpio regs */ ++ if(reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE) ++ wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val; ++ ++ /* wm9713 irq reg */ ++ if(reg == 0x5a) ++ wm->misc = val; ++ ++ if (wm->ac97) ++ wm->ac97->bus->ops->write(wm->ac97, reg, val); ++} ++ ++ ++/** ++ * wm97xx_read_aux_adc - Read the aux adc. ++ * @wm: wm97xx device. ++ * @adcsel: codec ADC to be read ++ * ++ * Reads the selected AUX ADC. ++ */ ++ ++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) ++{ ++ int power_adc = 0, auxval; ++ u16 power = 0; ++ ++ /* get codec */ ++ mutex_lock(&wm->codec_mutex); ++ ++ /* When the touchscreen is not in use, we may have to power up the AUX ADC ++ * before we can use sample the AUX inputs-> ++ */ ++ if (wm->id == WM9713_ID2 && ++ (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) { ++ power_adc = 1; ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff); ++ } ++ ++ /* Prepare the codec for AUX reading */ ++ wm->codec->digitiser_ioctl(wm, WM97XX_AUX_PREPARE); ++ ++ /* Turn polling mode on to read AUX ADC */ ++ wm->pen_probably_down = 1; ++ wm->codec->poll_sample(wm, adcsel, &auxval); ++ ++ if (power_adc) ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000); ++ ++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_RESTORE); ++ ++ wm->pen_probably_down = 0; ++ ++ mutex_unlock(&wm->codec_mutex); ++ return auxval & 0xfff; ++} ++ ++#define WM97XX_AUX_ATTR(name,input) \ ++static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct wm97xx *wm = (struct wm97xx*)dev->driver_data; \ ++ return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, input)); \ ++} \ ++static DEVICE_ATTR(name, 0444, name##_show, NULL) ++ ++WM97XX_AUX_ATTR(aux1, WM97XX_AUX_ID1); ++WM97XX_AUX_ATTR(aux2, WM97XX_AUX_ID2); ++WM97XX_AUX_ATTR(aux3, WM97XX_AUX_ID3); ++WM97XX_AUX_ATTR(aux4, WM97XX_AUX_ID4); ++ ++#define WM97XX_STATUS_ATTR(name) \ ++static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct wm97xx *wm = (struct wm97xx*)dev->driver_data; \ ++ return sprintf(buf, "%d\n", wm97xx_reg_read(wm, AC97_GPIO_STATUS)); \ ++} \ ++static DEVICE_ATTR(name, 0444, name##_show, NULL) ++ ++WM97XX_STATUS_ATTR(gpio); ++ ++static int wm97xx_sys_add(struct device *dev) ++{ ++ if (aux_sys) { ++ device_create_file(dev, &dev_attr_aux1); ++ device_create_file(dev, &dev_attr_aux2); ++ device_create_file(dev, &dev_attr_aux3); ++ device_create_file(dev, &dev_attr_aux4); ++ } ++ if (status_sys) ++ device_create_file(dev, &dev_attr_gpio); ++ return 0; ++} ++ ++static void wm97xx_sys_remove(struct device *dev) ++{ ++ if (status_sys) ++ device_remove_file(dev, &dev_attr_gpio); ++ if (aux_sys) { ++ device_remove_file(dev, &dev_attr_aux1); ++ device_remove_file(dev, &dev_attr_aux2); ++ device_remove_file(dev, &dev_attr_aux3); ++ device_remove_file(dev, &dev_attr_aux4); ++ } ++} ++ ++/** ++ * wm97xx_get_gpio - Get the status of a codec GPIO. ++ * @wm: wm97xx device. ++ * @gpio: gpio ++ * ++ * Get the status of a codec GPIO pin ++ */ ++ ++wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio) ++{ ++ u16 status; ++ wm97xx_gpio_status_t ret; ++ ++ mutex_lock(&wm->codec_mutex); ++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ ++ if (status & gpio) ++ ret = WM97XX_GPIO_HIGH; ++ else ++ ret = WM97XX_GPIO_LOW; ++ ++ mutex_unlock(&wm->codec_mutex); ++ return ret; ++} ++ ++/** ++ * wm97xx_set_gpio - Set the status of a codec GPIO. ++ * @wm: wm97xx device. ++ * @gpio: gpio ++ * ++ * ++ * Set the status of a codec GPIO pin ++ */ ++ ++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, ++ wm97xx_gpio_status_t status) ++{ ++ u16 reg; ++ ++ mutex_lock(&wm->codec_mutex); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ ++ if (status & WM97XX_GPIO_HIGH) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ if (wm->id == WM9712_ID2) ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1); ++ else ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg); ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++/* ++ * Codec GPIO pin configuration, this set's pin direction, polarity, ++ * stickyness and wake up. ++ */ ++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir, ++ wm97xx_gpio_pol_t pol, wm97xx_gpio_sticky_t sticky, ++ wm97xx_gpio_wake_t wake) ++{ ++ u16 reg; ++ ++ mutex_lock(&wm->codec_mutex); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ ++ if (pol == WM97XX_GPIO_POL_HIGH) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY); ++ ++ if (sticky == WM97XX_GPIO_STICKY) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP); ++ ++ if (wake == WM97XX_GPIO_WAKE) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ ++ if (dir == WM97XX_GPIO_IN) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg); ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++/* ++ * Handle a pen down interrupt. ++ */ ++static void wm97xx_pen_irq_worker(void *ptr) ++{ ++ struct wm97xx *wm = (struct wm97xx *) ptr; ++ ++ /* do we need to enable the touch panel reader */ ++ if (wm->id == WM9705_ID2) { ++ if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN) ++ wm->pen_is_down = 1; ++ else ++ wm->pen_is_down = 0; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ } else { ++ u16 status, pol; ++ mutex_lock(&wm->codec_mutex); ++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ ++ if (WM97XX_GPIO_13 & pol & status) { ++ wm->pen_is_down = 1; ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol & ~WM97XX_GPIO_13); ++ } else { ++ wm->pen_is_down = 0; ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol | WM97XX_GPIO_13); ++ } ++ ++ if (wm->id == WM9712_ID2) ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status & ~WM97XX_GPIO_13) << 1); ++ else ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, status & ~WM97XX_GPIO_13); ++ mutex_unlock(&wm->codec_mutex); ++ wake_up_interruptible(&wm->pen_irq_wait); ++ } ++ ++ if (!wm->pen_is_down && wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->mach_ops->acc_pen_up(wm); ++ enable_irq(wm->pen_irq); ++} ++ ++/* ++ * Codec PENDOWN irq handler ++ * ++ * We have to disable the codec interrupt in the handler because it can ++ * take upto 1ms to clear the interrupt source. The interrupt is then enabled ++ * again in the slow handler when the source has been cleared. ++ */ ++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id, ++ struct pt_regs *regs) ++{ ++ struct wm97xx *wm = (struct wm97xx *) dev_id; ++ disable_irq(wm->pen_irq); ++ queue_work(wm->pen_irq_workq, &wm->pen_event_work); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * initialise pen IRQ handler and workqueue ++ */ ++static int wm97xx_init_pen_irq(struct wm97xx *wm) ++{ ++ u16 reg; ++ ++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker, wm); ++ if ((wm->pen_irq_workq = ++ create_singlethread_workqueue("kwm97pen")) == NULL) { ++ err("could not create pen irq work queue"); ++ wm->pen_irq = 0; ++ return -EINVAL; ++ } ++ ++ if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) { ++ err("could not register codec pen down interrupt, will poll for pen down"); ++ destroy_workqueue(wm->pen_irq_workq); ++ wm->pen_irq = 0; ++ return -EINVAL; ++ } ++ ++ /* enable PEN down on wm9712/13 */ ++ if (wm->id != WM9705_ID2) { ++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg & 0xfffb); ++ reg = wm97xx_reg_read(wm, 0x5a); ++ wm97xx_reg_write(wm, 0x5a, reg & ~0x0001); ++ } ++ ++ return 0; ++} ++ ++/* Private struct for communication between struct wm97xx_tshread ++ * and wm97xx_read_samples */ ++struct ts_state { ++ int sleep_time; ++ int min_sleep_time; ++}; ++ ++static int wm97xx_read_samples(struct wm97xx *wm, struct ts_state *state) ++{ ++ struct wm97xx_data data; ++ int rc; ++ ++ mutex_lock(&wm->codec_mutex); ++ ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ rc = wm->mach_ops->acc_pen_down(wm); ++ else ++ rc = wm->codec->poll_touch(wm, &data); ++ ++ if (rc & RC_PENUP) { ++ if (wm->pen_is_down) { ++ wm->pen_is_down = 0; ++ dbg("pen up"); ++ input_report_abs(wm->input_dev, ABS_PRESSURE, 0); ++ input_sync(wm->input_dev); ++ } else if (!(rc & RC_AGAIN)) { ++ /* We need high frequency updates only while pen is down, ++ * the user never will be able to touch screen faster than ++ * a few times per second... On the other hand, when the ++ * user is actively working with the touchscreen we don't ++ * want to lose the quick response. So we will slowly ++ * increase sleep time after the pen is up and quicky ++ * restore it to ~one task switch when pen is down again. ++ */ ++ if (state->sleep_time < HZ / 10) ++ state->sleep_time++; ++ } ++ ++ } else if (rc & RC_VALID) { ++ dbg("pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n", ++ data.x >> 12, data.x & 0xfff, data.y >> 12, ++ data.y & 0xfff, data.p >> 12, data.p & 0xfff); ++ input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff); ++ input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff); ++ input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff); ++ input_sync(wm->input_dev); ++ wm->pen_is_down = 1; ++ state->sleep_time = state->min_sleep_time; ++ } else if (rc & RC_PENDOWN) { ++ dbg("pen down"); ++ wm->pen_is_down = 1; ++ state->sleep_time = state->min_sleep_time; ++ } ++ ++ mutex_unlock(&wm->codec_mutex); ++ return rc; ++} ++ ++/* ++* The touchscreen sample reader thread. ++*/ ++static int wm97xx_ts_read(void *data) ++{ ++ int rc; ++ struct ts_state state; ++ struct wm97xx *wm = (struct wm97xx *) data; ++ ++ /* set up thread context */ ++ wm->ts_task = current; ++ daemonize("kwm97xxts"); ++ ++ if (wm->codec == NULL) { ++ wm->ts_task = NULL; ++ printk(KERN_ERR "codec is NULL, bailing\n"); ++ } ++ ++ complete(&wm->ts_init); ++ wm->pen_is_down = 0; ++ state.min_sleep_time = HZ >= 100 ? HZ / 100 : 1; ++ if (state.min_sleep_time < 1) ++ state.min_sleep_time = 1; ++ state.sleep_time = state.min_sleep_time; ++ ++ /* touch reader loop */ ++ while (wm->ts_task) { ++ do { ++ try_to_freeze(); ++ rc = wm97xx_read_samples(wm, &state); ++ } while (rc & RC_AGAIN); ++ if (!wm->pen_is_down && wm->pen_irq) { ++ /* Nice, we don't have to poll for pen down event */ ++ wait_event_interruptible(wm->pen_irq_wait, wm->pen_is_down); ++ } else { ++ set_task_state(current, TASK_INTERRUPTIBLE); ++ schedule_timeout(state.sleep_time); ++ } ++ } ++ complete_and_exit(&wm->ts_exit, 0); ++} ++ ++/** ++ * wm97xx_ts_input_open - Open the touch screen input device. ++ * @idev: Input device to be opened. ++ * ++ * Called by the input sub system to open a wm97xx touchscreen device. ++ * Starts the touchscreen thread and touch digitiser. ++ */ ++static int wm97xx_ts_input_open(struct input_dev *idev) ++{ ++ int ret = 0; ++ struct wm97xx *wm = (struct wm97xx *) idev->private; ++ ++ mutex_lock(&wm->codec_mutex); ++ /* first time opened ? */ ++ if (wm->ts_use_count++ == 0) { ++ /* start touchscreen thread */ ++ init_completion(&wm->ts_init); ++ init_completion(&wm->ts_exit); ++ ret = kernel_thread(wm97xx_ts_read, wm, CLONE_KERNEL); ++ ++ if (ret >= 0) { ++ wait_for_completion(&wm->ts_init); ++ if (wm->ts_task == NULL) ++ ret = -EINVAL; ++ } else { ++ mutex_unlock(&wm->codec_mutex); ++ return ret; ++ } ++ ++ /* start digitiser */ ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->codec->acc_enable(wm, 1); ++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_START); ++ ++ /* init pen down/up irq handling */ ++ if (wm->pen_irq) { ++ wm97xx_init_pen_irq(wm); ++ ++ if (wm->pen_irq == 0) { ++ /* we failed to get an irq for pen down events, ++ * so we resort to polling. kickstart the reader */ ++ wm->pen_is_down = 1; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ } ++ } ++ } ++ ++ mutex_unlock(&wm->codec_mutex); ++ return 0; ++} ++ ++/** ++ * wm97xx_ts_input_close - Close the touch screen input device. ++ * @idev: Input device to be closed. ++ * ++ * Called by the input sub system to close a wm97xx touchscreen device. ++ * Kills the touchscreen thread and stops the touch digitiser. ++ */ ++ ++static void wm97xx_ts_input_close(struct input_dev *idev) ++{ ++ struct wm97xx *wm = (struct wm97xx *) idev->private; ++ ++ mutex_lock(&wm->codec_mutex); ++ if (--wm->ts_use_count == 0) { ++ /* destroy workqueues and free irqs */ ++ if (wm->pen_irq) { ++ free_irq(wm->pen_irq, wm); ++ destroy_workqueue(wm->pen_irq_workq); ++ } ++ ++ /* kill thread */ ++ if (wm->ts_task) { ++ wm->ts_task = NULL; ++ wm->pen_is_down = 1; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ wait_for_completion(&wm->ts_exit); ++ wm->pen_is_down = 0; ++ } ++ ++ /* stop digitiser */ ++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_STOP); ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->codec->acc_enable(wm, 0); ++ } ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++static int wm97xx_bus_match(struct device *dev, struct device_driver *drv) ++{ ++ return !(strcmp(dev->bus_id,drv->name)); ++} ++ ++/* ++ * The AC97 audio driver will do all the Codec suspend and resume ++ * tasks. This is just for anything machine specific or extra. ++ */ ++static int wm97xx_bus_suspend(struct device *dev, pm_message_t state) ++{ ++ int ret = 0; ++ ++ if (dev->driver && dev->driver->suspend) ++ ret = dev->driver->suspend(dev, state); ++ ++ return ret; ++} ++ ++static int wm97xx_bus_resume(struct device *dev) ++{ ++ int ret = 0; ++ ++ if (dev->driver && dev->driver->resume) ++ ret = dev->driver->resume(dev); ++ ++ return ret; ++} ++ ++struct bus_type wm97xx_bus_type = { ++ .name = "wm97xx", ++ .match = wm97xx_bus_match, ++ .suspend = wm97xx_bus_suspend, ++ .resume = wm97xx_bus_resume, ++}; ++ ++static void wm97xx_release(struct device *dev) ++{ ++ kfree(dev); ++} ++ ++static int wm97xx_probe(struct device *dev) ++{ ++ struct wm97xx* wm; ++ int ret = 0, id = 0; ++ ++ if (!(wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL))) ++ return -ENOMEM; ++ mutex_init(&wm->codec_mutex); ++ ++ init_waitqueue_head(&wm->pen_irq_wait); ++ wm->dev = dev; ++ dev->driver_data = wm; ++ wm->ac97 = to_ac97_t(dev); ++ ++ /* check that we have a supported codec */ ++ if ((id = wm97xx_reg_read(wm, AC97_VENDOR_ID1)) != WM97XX_ID1) { ++ err("could not find a wm97xx, found a %x instead\n", id); ++ kfree(wm); ++ return -ENODEV; ++ } ++ ++ wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2); ++ if(wm->id != wm97xx_codec.id) { ++ err("could not find a the selected codec, please build for wm97%2x", wm->id & 0xff); ++ kfree(wm); ++ return -ENODEV; ++ } ++ ++ if((wm->input_dev = input_allocate_device()) == NULL) { ++ kfree(wm); ++ return -ENOMEM; ++ } ++ ++ /* set up touch configuration */ ++ info("detected a wm97%2x codec", wm->id & 0xff); ++ wm->input_dev->name = "wm97xx touchscreen"; ++ wm->input_dev->open = wm97xx_ts_input_open; ++ wm->input_dev->close = wm97xx_ts_input_close; ++ set_bit(EV_ABS, wm->input_dev->evbit); ++ set_bit(ABS_X, wm->input_dev->absbit); ++ set_bit(ABS_Y, wm->input_dev->absbit); ++ set_bit(ABS_PRESSURE, wm->input_dev->absbit); ++ wm->input_dev->absmax[ABS_X] = abs_x[1]; ++ wm->input_dev->absmax[ABS_Y] = abs_y[1]; ++ wm->input_dev->absmax[ABS_PRESSURE] = abs_p[1]; ++ wm->input_dev->absmin[ABS_X] = abs_x[0]; ++ wm->input_dev->absmin[ABS_Y] = abs_y[0]; ++ wm->input_dev->absmin[ABS_PRESSURE] = abs_p[0]; ++ wm->input_dev->absfuzz[ABS_X] = abs_x[2]; ++ wm->input_dev->absfuzz[ABS_Y] = abs_y[2]; ++ wm->input_dev->absfuzz[ABS_PRESSURE] = abs_p[2]; ++ wm->input_dev->private = wm; ++ wm->codec = &wm97xx_codec; ++ if((ret = input_register_device(wm->input_dev)) < 0) { ++ kfree(wm); ++ return -ENOMEM; ++ } ++ ++ if(aux_sys) ++ wm97xx_sys_add(dev); ++ ++ /* set up physical characteristics */ ++ wm->codec->digitiser_ioctl(wm, WM97XX_PHY_INIT); ++ ++ /* load gpio cache */ ++ wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY); ++ wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP); ++ wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ ++ /* register our battery device */ ++ if (!(wm->battery_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) { ++ ret = -ENOMEM; ++ goto batt_err; ++ } ++ wm->battery_dev->bus = &wm97xx_bus_type; ++ strcpy(wm->battery_dev->bus_id,"wm97xx-battery"); ++ wm->battery_dev->driver_data = wm; ++ wm->battery_dev->parent = dev; ++ wm->battery_dev->release = wm97xx_release; ++ if((ret = device_register(wm->battery_dev)) < 0) ++ goto batt_reg_err; ++ ++ /* register our extended touch device (for machine specific extensions) */ ++ if (!(wm->touch_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) { ++ ret = -ENOMEM; ++ goto touch_err; ++ } ++ wm->touch_dev->bus = &wm97xx_bus_type; ++ strcpy(wm->touch_dev->bus_id,"wm97xx-touchscreen"); ++ wm->touch_dev->driver_data = wm; ++ wm->touch_dev->parent = dev; ++ wm->touch_dev->release = wm97xx_release; ++ if((ret = device_register(wm->touch_dev)) < 0) ++ goto touch_reg_err; ++ ++ return ret; ++ ++touch_reg_err: ++ kfree(wm->touch_dev); ++touch_err: ++ device_unregister(wm->battery_dev); ++batt_reg_err: ++ kfree(wm->battery_dev); ++batt_err: ++ input_unregister_device(wm->input_dev); ++ kfree(wm); ++ return ret; ++} ++ ++static int wm97xx_remove(struct device *dev) ++{ ++ struct wm97xx *wm = dev_get_drvdata(dev); ++ ++ /* Stop touch reader thread */ ++ if (wm->ts_task) { ++ wm->ts_task = NULL; ++ wm->pen_is_down = 1; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ wait_for_completion(&wm->ts_exit); ++ } ++ device_unregister(wm->battery_dev); ++ device_unregister(wm->touch_dev); ++ input_unregister_device(wm->input_dev); ++ ++ if(aux_sys) ++ wm97xx_sys_remove(dev); ++ ++ kfree(wm); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++int wm97xx_resume(struct device* dev) ++{ ++ struct wm97xx *wm = dev_get_drvdata(dev); ++ ++ /* restore digitiser and gpio's */ ++ if(wm->id == WM9713_ID2) { ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]); ++ wm97xx_reg_write(wm, 0x5a, wm->misc); ++ if(wm->ts_use_count) { ++ u16 reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff; ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg); ++ } ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]); ++ ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]); ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]); ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]); ++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]); ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]); ++ ++ return 0; ++} ++ ++#else ++#define wm97xx_resume NULL ++#endif ++ ++int wm97xx_register_mach_ops(struct wm97xx *wm, struct wm97xx_mach_ops *mach_ops) ++{ ++ mutex_lock(&wm->codec_mutex); ++ if(wm->mach_ops) { ++ mutex_unlock(&wm->codec_mutex); ++ return -EINVAL; ++ } ++ wm->mach_ops = mach_ops; ++ mutex_unlock(&wm->codec_mutex); ++ return 0; ++} ++ ++void wm97xx_unregister_mach_ops(struct wm97xx *wm) ++{ ++ mutex_lock(&wm->codec_mutex); ++ wm->mach_ops = NULL; ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++static struct device_driver wm97xx_driver = { ++ .name = "ac97", ++ .bus = &ac97_bus_type, ++ .owner = THIS_MODULE, ++ .probe = wm97xx_probe, ++ .remove = wm97xx_remove, ++ .resume = wm97xx_resume, ++}; ++ ++static int __init wm97xx_init(void) ++{ ++ int ret; ++ ++ info("version %s liam.girdwood@wolfsonmicro.com", WM_CORE_VERSION); ++ if((ret = bus_register(&wm97xx_bus_type)) < 0) ++ return ret; ++ return driver_register(&wm97xx_driver); ++} ++ ++static void __exit wm97xx_exit(void) ++{ ++ driver_unregister(&wm97xx_driver); ++ bus_unregister(&wm97xx_bus_type); ++} ++ ++EXPORT_SYMBOL_GPL(wm97xx_get_gpio); ++EXPORT_SYMBOL_GPL(wm97xx_set_gpio); ++EXPORT_SYMBOL_GPL(wm97xx_config_gpio); ++EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc); ++EXPORT_SYMBOL_GPL(wm97xx_reg_read); ++EXPORT_SYMBOL_GPL(wm97xx_reg_write); ++EXPORT_SYMBOL_GPL(wm97xx_bus_type); ++EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops); ++EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops); ++ ++module_init(wm97xx_init); ++module_exit(wm97xx_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/include/linux/wm97xx.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/include/linux/wm97xx.h 2006-09-19 20:36:47.973052250 +0200 +@@ -0,0 +1,291 @@ ++ ++/* ++ * Register bits and API for Wolfson WM97xx series of codecs ++ */ ++ ++#ifndef _LINUX_WM97XX_H ++#define _LINUX_WM97XX_H ++ ++#include <sound/driver.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/ac97_codec.h> ++#include <sound/initval.h> ++#include <linux/types.h> ++#include <linux/list.h> ++#include <linux/input.h> /* Input device layer */ ++ ++/* ++ * WM97xx AC97 Touchscreen registers ++ */ ++#define AC97_WM97XX_DIGITISER1 0x76 ++#define AC97_WM97XX_DIGITISER2 0x78 ++#define AC97_WM97XX_DIGITISER_RD 0x7a ++#define AC97_WM9713_DIG1 0x74 ++#define AC97_WM9713_DIG2 AC97_WM97XX_DIGITISER1 ++#define AC97_WM9713_DIG3 AC97_WM97XX_DIGITISER2 ++ ++/* ++ * WM97xx register bits ++ */ ++#define WM97XX_POLL 0x8000 /* initiate a polling measurement */ ++#define WM97XX_ADCSEL_X 0x1000 /* x coord measurement */ ++#define WM97XX_ADCSEL_Y 0x2000 /* y coord measurement */ ++#define WM97XX_ADCSEL_PRES 0x3000 /* pressure measurement */ ++#define WM97XX_ADCSEL_MASK 0x7000 ++#define WM97XX_COO 0x0800 /* enable coordinate mode */ ++#define WM97XX_CTC 0x0400 /* enable continuous mode */ ++#define WM97XX_CM_RATE_93 0x0000 /* 93.75Hz continuous rate */ ++#define WM97XX_CM_RATE_187 0x0100 /* 187.5Hz continuous rate */ ++#define WM97XX_CM_RATE_375 0x0200 /* 375Hz continuous rate */ ++#define WM97XX_CM_RATE_750 0x0300 /* 750Hz continuous rate */ ++#define WM97XX_CM_RATE_8K 0x00f0 /* 8kHz continuous rate */ ++#define WM97XX_CM_RATE_12K 0x01f0 /* 12kHz continuous rate */ ++#define WM97XX_CM_RATE_24K 0x02f0 /* 24kHz continuous rate */ ++#define WM97XX_CM_RATE_48K 0x03f0 /* 48kHz continuous rate */ ++#define WM97XX_CM_RATE_MASK 0x03f0 ++#define WM97XX_RATE(i) (((i & 3) << 8) | ((i & 4) ? 0xf0 : 0)) ++#define WM97XX_DELAY(i) ((i << 4) & 0x00f0) /* sample delay times */ ++#define WM97XX_DELAY_MASK 0x00f0 ++#define WM97XX_SLEN 0x0008 /* slot read back enable */ ++#define WM97XX_SLT(i) ((i - 5) & 0x7) /* touchpanel slot selection (5-11) */ ++#define WM97XX_SLT_MASK 0x0007 ++#define WM97XX_PRP_DETW 0x4000 /* pen detect on, digitiser off, wake up */ ++#define WM97XX_PRP_DET 0x8000 /* pen detect on, digitiser off, no wake up */ ++#define WM97XX_PRP_DET_DIG 0xc000 /* pen detect on, digitiser on */ ++#define WM97XX_RPR 0x2000 /* wake up on pen down */ ++#define WM97XX_PEN_DOWN 0x8000 /* pen is down */ ++#define WM97XX_ADCSRC_MASK 0x7000 /* ADC source mask */ ++ ++#define WM97XX_AUX_ID1 0x8001 ++#define WM97XX_AUX_ID2 0x8002 ++#define WM97XX_AUX_ID3 0x8003 ++#define WM97XX_AUX_ID4 0x8004 ++ ++ ++/* WM9712 Bits */ ++#define WM9712_45W 0x1000 /* set for 5-wire touchscreen */ ++#define WM9712_PDEN 0x0800 /* measure only when pen down */ ++#define WM9712_WAIT 0x0200 /* wait until adc is read before next sample */ ++#define WM9712_PIL 0x0100 /* current used for pressure measurement. set 400uA else 200uA */ ++#define WM9712_MASK_HI 0x0040 /* hi on mask pin (47) stops conversions */ ++#define WM9712_MASK_EDGE 0x0080 /* rising/falling edge on pin delays sample */ ++#define WM9712_MASK_SYNC 0x00c0 /* rising/falling edge on mask initiates sample */ ++#define WM9712_RPU(i) (i&0x3f) /* internal pull up on pen detect (64k / rpu) */ ++#define WM9712_PD(i) (0x1 << i) /* power management */ ++ ++/* WM9712 Registers */ ++#define AC97_WM9712_POWER 0x24 ++#define AC97_WM9712_REV 0x58 ++ ++/* WM9705 Bits */ ++#define WM9705_PDEN 0x1000 /* measure only when pen is down */ ++#define WM9705_PINV 0x0800 /* inverts sense of pen down output */ ++#define WM9705_BSEN 0x0400 /* BUSY flag enable, pin47 is 1 when busy */ ++#define WM9705_BINV 0x0200 /* invert BUSY (pin47) output */ ++#define WM9705_WAIT 0x0100 /* wait until adc is read before next sample */ ++#define WM9705_PIL 0x0080 /* current used for pressure measurement. set 400uA else 200uA */ ++#define WM9705_PHIZ 0x0040 /* set PHONE and PCBEEP inputs to high impedance */ ++#define WM9705_MASK_HI 0x0010 /* hi on mask stops conversions */ ++#define WM9705_MASK_EDGE 0x0020 /* rising/falling edge on pin delays sample */ ++#define WM9705_MASK_SYNC 0x0030 /* rising/falling edge on mask initiates sample */ ++#define WM9705_PDD(i) (i & 0x000f) /* pen detect comparator threshold */ ++ ++ ++/* WM9713 Bits */ ++#define WM9713_PDPOL 0x0400 /* Pen down polarity */ ++#define WM9713_POLL 0x0200 /* initiate a polling measurement */ ++#define WM9713_CTC 0x0100 /* enable continuous mode */ ++#define WM9713_ADCSEL_X 0x0002 /* X measurement */ ++#define WM9713_ADCSEL_Y 0x0004 /* Y measurement */ ++#define WM9713_ADCSEL_PRES 0x0008 /* Pressure measurement */ ++#define WM9713_COO 0x0001 /* enable coordinate mode */ ++#define WM9713_PDEN 0x0800 /* measure only when pen down */ ++#define WM9713_ADCSEL_MASK 0x00fe /* ADC selection mask */ ++#define WM9713_WAIT 0x0200 /* coordinate wait */ ++ ++/* AUX ADC ID's */ ++#define TS_COMP1 0x0 ++#define TS_COMP2 0x1 ++#define TS_BMON 0x2 ++#define TS_WIPER 0x3 ++ ++/* ID numbers */ ++#define WM97XX_ID1 0x574d ++#define WM9712_ID2 0x4c12 ++#define WM9705_ID2 0x4c05 ++#define WM9713_ID2 0x4c13 ++ ++/* Codec GPIO's */ ++#define WM97XX_MAX_GPIO 16 ++#define WM97XX_GPIO_1 (1 << 1) ++#define WM97XX_GPIO_2 (1 << 2) ++#define WM97XX_GPIO_3 (1 << 3) ++#define WM97XX_GPIO_4 (1 << 4) ++#define WM97XX_GPIO_5 (1 << 5) ++#define WM97XX_GPIO_6 (1 << 6) ++#define WM97XX_GPIO_7 (1 << 7) ++#define WM97XX_GPIO_8 (1 << 8) ++#define WM97XX_GPIO_9 (1 << 9) ++#define WM97XX_GPIO_10 (1 << 10) ++#define WM97XX_GPIO_11 (1 << 11) ++#define WM97XX_GPIO_12 (1 << 12) ++#define WM97XX_GPIO_13 (1 << 13) ++#define WM97XX_GPIO_14 (1 << 14) ++#define WM97XX_GPIO_15 (1 << 15) ++ ++ ++#define AC97_LINK_FRAME 21 /* time in uS for AC97 link frame */ ++ ++ ++/*---------------- Return codes from sample reading functions ---------------*/ ++ ++/* More data is available; call the sample gathering function again */ ++#define RC_AGAIN 0x00000001 ++/* The returned sample is valid */ ++#define RC_VALID 0x00000002 ++/* The pen is up (the first RC_VALID without RC_PENUP means pen is down) */ ++#define RC_PENUP 0x00000004 ++/* The pen is down (RC_VALID implies RC_PENDOWN, but sometimes it is helpful ++ to tell the handler that the pen is down but we don't know yet his coords, ++ so the handler should not sleep or wait for pendown irq) */ ++#define RC_PENDOWN 0x00000008 ++ ++/* The wm97xx driver provides a private API for writing platform-specific ++ * drivers. ++ */ ++ ++/* The structure used to return arch specific sampled data into */ ++struct wm97xx_data { ++ int x; ++ int y; ++ int p; ++}; ++ ++/* Codec GPIO status ++ */ ++typedef enum { ++ WM97XX_GPIO_HIGH, ++ WM97XX_GPIO_LOW ++} wm97xx_gpio_status_t; ++ ++/* Codec GPIO direction ++ */ ++typedef enum { ++ WM97XX_GPIO_IN, ++ WM97XX_GPIO_OUT ++} wm97xx_gpio_dir_t; ++ ++/* Codec GPIO polarity ++ */ ++typedef enum { ++ WM97XX_GPIO_POL_HIGH, ++ WM97XX_GPIO_POL_LOW ++} wm97xx_gpio_pol_t; ++ ++/* Codec GPIO sticky ++ */ ++typedef enum { ++ WM97XX_GPIO_STICKY, ++ WM97XX_GPIO_NOTSTICKY ++} wm97xx_gpio_sticky_t; ++ ++/* Codec GPIO wake ++ */ ++typedef enum { ++ WM97XX_GPIO_WAKE, ++ WM97XX_GPIO_NOWAKE ++} wm97xx_gpio_wake_t; ++ ++ ++/* ++ * Digitiser ioctl commands ++ */ ++#define WM97XX_DIG_START 0x1 ++#define WM97XX_DIG_STOP 0x2 ++#define WM97XX_PHY_INIT 0x3 ++#define WM97XX_AUX_PREPARE 0x4 ++#define WM97XX_DIG_RESTORE 0x5 ++ ++struct wm97xx; ++extern struct wm97xx_codec_drv wm97xx_codec; ++ ++/* ++ * Codec driver interface - allows mapping to WM9705/12/13 and newer codecs ++ */ ++struct wm97xx_codec_drv { ++ u16 id; ++ char *name; ++ int (*poll_sample) (struct wm97xx *, int adcsel, int *sample); /* read 1 sample */ ++ int (*poll_touch) (struct wm97xx *, struct wm97xx_data *); /* read X,Y,[P] in poll */ ++ int (*digitiser_ioctl) (struct wm97xx *, int cmd); ++ int (*acc_enable) (struct wm97xx *, int enable); ++}; ++ ++ ++/* Machine specific and accelerated touch operations */ ++struct wm97xx_mach_ops { ++ ++ /* accelerated touch readback - coords are transmited on AC97 link */ ++ int acc_enabled; ++ void (*acc_pen_up) (struct wm97xx *); ++ int (*acc_pen_down) (struct wm97xx *); ++ int (*acc_startup) (struct wm97xx *); ++ void (*acc_shutdown) (struct wm97xx *); ++ ++ /* pre and post sample - can be used to minimise any analog noise */ ++ void (*pre_sample) (int); /* function to run before sampling */ ++ void (*post_sample) (int); /* function to run after sampling */ ++}; ++ ++struct wm97xx { ++ u16 dig[3], id, gpio[6], misc; /* Cached codec registers */ ++ u16 dig_save[3]; /* saved during aux reading */ ++ struct wm97xx_codec_drv *codec; /* attached codec driver*/ ++ struct input_dev* input_dev; /* touchscreen input device */ ++ ac97_t *ac97; /* ALSA codec access */ ++ struct device *dev; /* ALSA device */ ++ struct device *battery_dev; ++ struct device *touch_dev; ++ struct wm97xx_mach_ops *mach_ops; ++ struct mutex codec_mutex; ++ struct completion ts_init; ++ struct completion ts_exit; ++ struct task_struct *ts_task; ++ unsigned int pen_irq; /* Pen IRQ number in use */ ++ wait_queue_head_t pen_irq_wait; /* Pen IRQ wait queue */ ++ struct workqueue_struct *pen_irq_workq; ++ struct work_struct pen_event_work; ++ u16 acc_slot; /* AC97 slot used for acc touch data */ ++ u16 acc_rate; /* acc touch data rate */ ++ unsigned int ts_use_count; ++ unsigned pen_is_down:1; /* Pen is down */ ++ unsigned aux_waiting:1; /* aux measurement waiting */ ++ unsigned pen_probably_down:1; /* used in polling mode */ ++}; ++ ++/* Codec GPIO access (not supported on WM9705) ++ * This can be used to set/get codec GPIO and Virtual GPIO status. ++ */ ++wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio); ++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, ++ wm97xx_gpio_status_t status); ++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, ++ wm97xx_gpio_dir_t dir, ++ wm97xx_gpio_pol_t pol, ++ wm97xx_gpio_sticky_t sticky, ++ wm97xx_gpio_wake_t wake); ++ ++/* codec AC97 IO access */ ++int wm97xx_reg_read(struct wm97xx *wm, u16 reg); ++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val); ++ ++/* aux adc readback */ ++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel); ++ ++/* machine ops */ ++int wm97xx_register_mach_ops(struct wm97xx *, struct wm97xx_mach_ops *); ++void wm97xx_unregister_mach_ops(struct wm97xx *); ++ ++extern struct bus_type wm97xx_bus_type; ++#endif diff --git a/packages/linux/linux-rp_2.6.22.bb b/packages/linux/linux-rp_2.6.22.bb index 50ee894ee9..939e630624 100644 --- a/packages/linux/linux-rp_2.6.22.bb +++ b/packages/linux/linux-rp_2.6.22.bb @@ -87,29 +87,33 @@ SRC_URI_append_collie = "\ # ${DOSRC}/collie/collie-pm-r1.patch;patch=1 \ " +#- ${CHSRC}/usb-ohci-hooks-r1.patch;patch=1 \ +#- file://tmio-ohci-r6.patch;patch=1 \ +# wm97xx-lg13-r0.patch;patch=1 was adapted from $(DOSRC) to apply cleanly SRC_URI_append_tosa = "\ - ${CHSRC}/usb-ohci-hooks-r1.patch;patch=1 \ ${CHSRC}/tmio-core-r4.patch;patch=1 \ file://tmio-tc6393-r8.patch;patch=1 \ - file://tmio-nand-r7.patch;patch=1 \ - file://tmio-ohci-r6.patch;patch=1 \ + file://tmio-nand-r8.patch;patch=1 \ ${CHSRC}/tmio-fb-r6.patch;patch=1 \ - file://tosa-keyboard-r18.patch;patch=1 \ + file://tmio-fb-r6-fix-r0.patch;patch=1 \ + file://tosa-keyboard-r19.patch;patch=1 \ ${DOSRC}/tosa-pxaac97-r6.patch;patch=1 \ + file://tosa-pxaac97-r6-fix-r0.patch;patch=1 \ ${DOSRC}/tosa-tmio-r6.patch;patch=1 \ - ${DOSRC}/tosa-power-r17.patch;patch=1 \ + ${DOSRC}/tosa-power-r18.patch;patch=1 \ + file://tosa-power-r18-fix-r0.patch;patch=1 \ file://tosa-tmio-lcd-r10.patch;patch=1 \ + file://tosa-tmio-lcd-r10-fix-r0.patch;patch=1 \ ${DOSRC}/tosa-bluetooth-r8.patch;patch=1 \ - ${DOSRC}/wm97xx-lg7-r0.patch;patch=1 \ + file://wm97xx-lg13-r0.patch;patch=1 \ + file://wm97xx-lg13-r0-fix-r0.patch;patch=1 \ file://wm9712-suspend-cold-res-r2.patch;patch=1 \ file://sharpsl-pm-postresume-r1.patch;patch=1 \ - ${DOSRC}/wm97xx-dig-restore-r0.patch;patch=1 \ - ${DOSRC}/wm97xx-miscdevs-resume-r0.patch;patch=1 \ file://wm9712-reset-loop-r2.patch;patch=1 \ file://tosa-lcdnoise-r1.patch;patch=1 \ - file://wm97xx-lcdnoise-r0.patch;patch=1 " + file://tosa-lcdnoise-r1-fix-r0.patch;patch=1 " # ${DOSRC}/tosa-asoc-r1.patch;patch=1 " -SRC_URI_append_htcuniversal ="file://htcuni-acx.patch;patch=1;status=external" +SRC_URI_append_htcuniversal = "file://htcuni-acx.patch;patch=1;status=external" S = "${WORKDIR}/linux-2.6.22" diff --git a/packages/linux/linux-rp_2.6.23.bb b/packages/linux/linux-rp_2.6.23.bb index 840400381e..ab0fe13805 100644 --- a/packages/linux/linux-rp_2.6.23.bb +++ b/packages/linux/linux-rp_2.6.23.bb @@ -93,27 +93,35 @@ SRC_URI_append_collie = "\ # ${DOSRC}/collie/collie-pm-r1.patch;patch=1 \ " + +# wm97xx-lg13-r0.patch, tosa-power-r18.patch and tosa-bluetooth-r8.patch +# were adapted from $(DOSRC) to apply cleanly +# FIXME: +#- ${CHSRC}/usb-ohci-hooks-r1.patch;patch=1 \ +#- file://tmio-ohci-r6.patch;patch=1 \ SRC_URI_append_tosa = "\ - ${CHSRC}/usb-ohci-hooks-r1.patch;patch=1 \ ${CHSRC}/tmio-core-r4.patch;patch=1 \ file://tmio-tc6393-r8.patch;patch=1 \ - file://tmio-nand-r7.patch;patch=1 \ - file://tmio-ohci-r6.patch;patch=1 \ + file://tmio-nand-r8.patch;patch=1 \ ${CHSRC}/tmio-fb-r6.patch;patch=1 \ - file://tosa-keyboard-r18.patch;patch=1 \ + file://tmio-fb-r6-fix-r0.patch;patch=1 \ + file://tosa-keyboard-r19.patch;patch=1 \ ${DOSRC}/tosa-pxaac97-r6.patch;patch=1 \ + file://tosa-pxaac97-r6-fix-r0.patch;patch=1 \ ${DOSRC}/tosa-tmio-r6.patch;patch=1 \ - ${DOSRC}/tosa-power-r17.patch;patch=1 \ + file://tosa-power-r18.patch;patch=1 \ + file://tosa-power-r18-fix-r0.patch;patch=1 \ file://tosa-tmio-lcd-r10.patch;patch=1 \ - ${DOSRC}/tosa-bluetooth-r8.patch;patch=1 \ - ${DOSRC}/wm97xx-lg7-r0.patch;patch=1 \ + file://tosa-tmio-lcd-r10-fix-r0.patch;patch=1 \ + file://tosa-bluetooth-r8.patch;patch=1 \ + file://wm97xx-lg13-r0.patch;patch=1 \ + file://wm97xx-lg13-r0-fix-r0.patch;patch=1 \ file://wm9712-suspend-cold-res-r2.patch;patch=1 \ file://sharpsl-pm-postresume-r1.patch;patch=1 \ - ${DOSRC}/wm97xx-dig-restore-r0.patch;patch=1 \ - ${DOSRC}/wm97xx-miscdevs-resume-r0.patch;patch=1 \ file://wm9712-reset-loop-r2.patch;patch=1 \ file://tosa-lcdnoise-r1.patch;patch=1 \ - file://wm97xx-lcdnoise-r0.patch;patch=1 " + file://tosa-lcdnoise-r1-fix-r0.patch;patch=1 \ + " # ${DOSRC}/tosa-asoc-r1.patch;patch=1 " SRC_URI_append_htcuniversal ="\ |