logicpd-pxa270-2.6.17-rc5: add touch and audio support (by Shane Volpe)
authorCliff Brake <cbrake@bec-systems.com>
Mon, 13 Nov 2006 18:57:08 +0000 (18:57 +0000)
committerCliff Brake <cbrake@bec-systems.com>
Mon, 13 Nov 2006 18:57:08 +0000 (18:57 +0000)
packages/linux/logicpd-pxa270-2.6.17-rc5/defconfig
packages/linux/logicpd-pxa270-2.6.17-rc5/ucb1400-ac97-audio.patch [new file with mode: 0644]
packages/linux/logicpd-pxa270-2.6.17-rc5/ucb1400-touchscreen.patch [new file with mode: 0644]
packages/linux/logicpd-pxa270_2.6.17-rc5.bb

index 212e380..38b5bf8 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
 # Linux kernel version: 2.6.17-rc5
-# Tue May 30 12:40:27 2006
+# Tue Oct 31 10:23:39 2006
 #
 CONFIG_ARM=y
 CONFIG_MMU=y
@@ -56,7 +56,8 @@ CONFIG_OBSOLETE_INTERMODULE=y
 # Loadable module support
 #
 CONFIG_MODULES=y
-# CONFIG_MODULE_UNLOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
 # CONFIG_MODVERSIONS is not set
 # CONFIG_MODULE_SRCVERSION_ALL is not set
 # CONFIG_KMOD is not set
@@ -168,7 +169,6 @@ CONFIG_ALIGNMENT_TRAP=y
 CONFIG_ZBOOT_ROM_TEXT=0x0
 CONFIG_ZBOOT_ROM_BSS=0x0
 CONFIG_CMDLINE="root=/dev/mtdblock2 rootfstype=jffs2 ip=dhcp console=ttyS0,115200 mem=64M"
-#CONFIG_CMDLINE="root=/dev/nfs nfsroot=192.168.3.5:/opt/nfs-exports/pxa-nfs-root ip=dhcp console=ttyS0,115200 mem=64M"
 # CONFIG_XIP_KERNEL is not set
 
 #
@@ -537,7 +537,13 @@ CONFIG_KEYBOARD_ATKBD=y
 # CONFIG_KEYBOARD_NEWTON is not set
 # CONFIG_INPUT_MOUSE is not set
 # CONFIG_INPUT_JOYSTICK is not set
-# CONFIG_INPUT_TOUCHSCREEN is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_UCB1400=y
+CONFIG_TOUCHSCREEN_UCB1400=m
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
 # CONFIG_INPUT_MISC is not set
 
 #
@@ -692,7 +698,45 @@ CONFIG_LOGO_LINUX_CLUT224=y
 #
 # Sound
 #
-# CONFIG_SOUND is not set
+CONFIG_SOUND=m
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=m
+CONFIG_SND_TIMER=m
+CONFIG_SND_PCM=m
+# CONFIG_SND_SEQUENCER is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=m
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+
+#
+# Generic devices
+#
+CONFIG_SND_AC97_CODEC=m
+CONFIG_SND_AC97_BUS=m
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+
+#
+# ALSA ARM devices
+#
+CONFIG_SND_PXA2XX_PCM=m
+CONFIG_SND_PXA2XX_AC97=m
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
 
 #
 # USB support
@@ -764,7 +808,7 @@ CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
 #
 CONFIG_PROC_FS=y
 CONFIG_SYSFS=y
-# CONFIG_TMPFS is not set
+CONFIG_TMPFS=y
 # CONFIG_HUGETLB_PAGE is not set
 CONFIG_RAMFS=y
 # CONFIG_CONFIGFS_FS is not set
diff --git a/packages/linux/logicpd-pxa270-2.6.17-rc5/ucb1400-ac97-audio.patch b/packages/linux/logicpd-pxa270-2.6.17-rc5/ucb1400-ac97-audio.patch
new file mode 100644 (file)
index 0000000..692dd1d
--- /dev/null
@@ -0,0 +1,86 @@
+Index: linux-2.6.17-rc5/sound/pci/ac97/ac97_codec.c
+===================================================================
+--- linux-2.6.17-rc5.orig/sound/pci/ac97/ac97_codec.c
++++ linux-2.6.17-rc5/sound/pci/ac97/ac97_codec.c
+@@ -151,7 +151,7 @@ static const struct ac97_codec_id snd_ac
+ { 0x4e534300, 0xffffffff, "LM4540,43,45,46,48",       NULL,           NULL }, // only guess --jk
+ { 0x4e534331, 0xffffffff, "LM4549",           NULL,           NULL },
+ { 0x4e534350, 0xffffffff, "LM4550",           patch_lm4550,   NULL }, // volume wrap fix 
+-{ 0x50534304, 0xffffffff, "UCB1400",          NULL,           NULL },
++{ 0x50534304, 0xffffffff, "UCB1400",          patch_ucb1400,          NULL, AC97_HAS_NO_STD_PCM },
+ { 0x53494c20, 0xffffffe0, "Si3036,8",         mpatch_si3036,  mpatch_si3036, AC97_MODEM_PATCH },
+ { 0x54524102, 0xffffffff, "TR28022",          NULL,           NULL },
+ { 0x54524106, 0xffffffff, "TR28026",          NULL,           NULL },
+Index: linux-2.6.17-rc5/sound/pci/ac97/ac97_patch.c
+===================================================================
+--- linux-2.6.17-rc5.orig/sound/pci/ac97/ac97_patch.c
++++ linux-2.6.17-rc5/sound/pci/ac97/ac97_patch.c
+@@ -375,7 +375,57 @@ int patch_yamaha_ymf753(struct snd_ac97 
+       ac97->caps |= 0x04 << 10; /* Yamaha 3D enhancement */
+       return 0;
+ }
++/*
++ * UCB1400 codec
++ */
++
++#define AC97_UCB1400_FCSR1    0x6a
++#define AC97_UCB1400_FCSR2    0x6c
++
++static const snd_kcontrol_new_t ucb1400_snd_ac97_controls[] = {
++      AC97_SINGLE("Tone Control - Bass", AC97_UCB1400_FCSR1, 11, 4, 0),
++      AC97_SINGLE("Tone Control - Treble", AC97_UCB1400_FCSR1, 9, 2, 0),
++      AC97_SINGLE("Headphone Playback Switch", AC97_UCB1400_FCSR1, 6, 1, 0),
++      AC97_SINGLE("De-emphasis", AC97_UCB1400_FCSR1, 5, 1, 0),
++      AC97_SINGLE("DC Filter", AC97_UCB1400_FCSR1, 4, 1, 0),
++      AC97_SINGLE("Hi-pass Filter", AC97_UCB1400_FCSR1, 3, 1, 0),
++      AC97_SINGLE("ADC Filter", AC97_UCB1400_FCSR2, 12, 1, 0),
++};
++
++int patch_ucb1400(ac97_t * ac97)
++{
++      int err, i;
++      for(i = 0; i < ARRAY_SIZE(ucb1400_snd_ac97_controls); i++) {
++              if((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&ucb1400_snd_ac97_controls[i], ac97))) < 0)
++                      return err;
++      }
++
++      snd_ac97_write_cache(ac97,  AC97_UCB1400_FCSR1,
++                      (0 << 11) |     // 0 base boost
++                      (0 << 9)  |     // 0 treble boost
++                      (0 << 7)  |     // Mode = flat
++                      (1 << 6)  |     // Headphones enable
++                      (0 << 5)  |     // De-emphasis disabled
++                      (1 << 4)  |     // DC filter enabled
++                      (1 << 3)  |     // Hi-pass filter enabled
++                      (0 << 2)  |     // disable interrupt signalling via GPIO_INT
++                      (1 << 0)        // clear ADC overflow status if set
++              );
++
++      snd_ac97_write_cache(ac97, AC97_UCB1400_FCSR2,
++                      (0 << 15) |     // must be 0
++                      (0 << 13) |     // must be 0
++                      (1 << 12) |     // ADC filter enabled
++                      (0 << 10) |     // must be 0
++                      (0 << 4)  |     // Smart low power mode on neither Codec nor PLL
++                      (0 << 0)        // must be 0
++              );
++
++      return 0;
++}
++
++/*
+ /*
+  * May 2, 2003 Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+  *  removed broken wolfson00 patch.
+Index: linux-2.6.17-rc5/sound/pci/ac97/ac97_patch.h
+===================================================================
+--- linux-2.6.17-rc5.orig/sound/pci/ac97/ac97_patch.h
++++ linux-2.6.17-rc5/sound/pci/ac97/ac97_patch.h
+@@ -58,5 +58,6 @@ int patch_cm9780(struct snd_ac97 * ac97)
+ int patch_vt1616(struct snd_ac97 * ac97);
+ int patch_vt1617a(struct snd_ac97 * ac97);
+ int patch_it2646(struct snd_ac97 * ac97);
++int patch_ucb1400(ac97_t * ac97);
+ int mpatch_si3036(struct snd_ac97 * ac97);
+ int patch_lm4550(struct snd_ac97 * ac97);
diff --git a/packages/linux/logicpd-pxa270-2.6.17-rc5/ucb1400-touchscreen.patch b/packages/linux/logicpd-pxa270-2.6.17-rc5/ucb1400-touchscreen.patch
new file mode 100644 (file)
index 0000000..d995426
--- /dev/null
@@ -0,0 +1,706 @@
+This patch is slightly adjusted from
+
+http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=3073/1
+http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=3074/2
+http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=3075/2
+
+in order to get it to apply cleanly to the released 2.6.15 codebase
+and to put the Kconfig stuff in a more reasonable place in the tree.
+Actually, I think Kconfig should probably separate the notion of the
+touchscreen driver and the AC97-MCP layer thing; but that problem is
+basically in the underlying mcp-based ucb1x00 driver layout in the
+first place.
+Index: linux-2.6.17-rc5/drivers/mfd/Makefile
+===================================================================
+--- linux-2.6.17-rc5.orig/drivers/mfd/Makefile
++++ linux-2.6.17-rc5/drivers/mfd/Makefile
+@@ -10,3 +10,6 @@ obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-
+ ifeq ($(CONFIG_SA1100_ASSABET),y)
+ obj-$(CONFIG_MCP_UCB1200)     += ucb1x00-assabet.o
+ endif
++
++obj-$(CONFIG_TOUCHSCREEN_UCB1400)     += mcp-ac97.o ucb1x00-core.o ucb1x00-ts.o
++
+Index: linux-2.6.17-rc5/drivers/mfd/mcp-core.c
+===================================================================
+--- linux-2.6.17-rc5.orig/drivers/mfd/mcp-core.c
++++ linux-2.6.17-rc5/drivers/mfd/mcp-core.c
+@@ -18,7 +18,6 @@
+ #include <linux/slab.h>
+ #include <linux/string.h>
+-#include <asm/dma.h>
+ #include <asm/system.h>
+ #include "mcp.h"
+@@ -208,6 +207,7 @@ struct mcp *mcp_host_alloc(struct device
+               mcp->attached_device.bus = &mcp_bus_type;
+               mcp->attached_device.dma_mask = parent->dma_mask;
+               mcp->attached_device.release = mcp_release;
++              mcp->dev = &mcp->attached_device;
+       }
+       return mcp;
+ }
+Index: linux-2.6.17-rc5/drivers/mfd/mcp-sa11x0.c
+===================================================================
+--- linux-2.6.17-rc5.orig/drivers/mfd/mcp-sa11x0.c
++++ linux-2.6.17-rc5/drivers/mfd/mcp-sa11x0.c
+@@ -31,8 +31,12 @@
+ #include "mcp.h"
+ struct mcp_sa11x0 {
+-      u32     mccr0;
+-      u32     mccr1;
++      u32             mccr0;
++      u32             mccr1;
++      dma_device_t    dma_audio_rd;
++      dma_device_t    dma_audio_wr;
++      dma_device_t    dma_telco_rd;
++      dma_device_t    dma_telco_wr;
+ };
+ #define priv(mcp)     ((struct mcp_sa11x0 *)mcp_priv(mcp))
+@@ -159,10 +163,10 @@ static int mcp_sa11x0_probe(struct platf
+       mcp->owner              = THIS_MODULE;
+       mcp->ops                = &mcp_sa11x0;
+       mcp->sclk_rate          = data->sclk_rate;
+-      mcp->dma_audio_rd       = DMA_Ser4MCP0Rd;
+-      mcp->dma_audio_wr       = DMA_Ser4MCP0Wr;
+-      mcp->dma_telco_rd       = DMA_Ser4MCP1Rd;
+-      mcp->dma_telco_wr       = DMA_Ser4MCP1Wr;
++      priv(mcp)->dma_audio_rd = DMA_Ser4MCP0Rd;
++      priv(mcp)->dma_audio_wr = DMA_Ser4MCP0Wr;
++      priv(mcp)->dma_telco_rd = DMA_Ser4MCP1Rd;
++      priv(mcp)->dma_telco_wr = DMA_Ser4MCP1Wr;
+       platform_set_drvdata(pdev, mcp);
+Index: linux-2.6.17-rc5/drivers/mfd/mcp.h
+===================================================================
+--- linux-2.6.17-rc5.orig/drivers/mfd/mcp.h
++++ linux-2.6.17-rc5/drivers/mfd/mcp.h
+@@ -19,11 +19,8 @@ struct mcp {
+       int             use_count;
+       unsigned int    sclk_rate;
+       unsigned int    rw_timeout;
+-      dma_device_t    dma_audio_rd;
+-      dma_device_t    dma_audio_wr;
+-      dma_device_t    dma_telco_rd;
+-      dma_device_t    dma_telco_wr;
+       struct device   attached_device;
++      struct device   *dev;
+ };
+ struct mcp_ops {
+Index: linux-2.6.17-rc5/drivers/mfd/ucb1x00-assabet.c
+===================================================================
+--- linux-2.6.17-rc5.orig/drivers/mfd/ucb1x00-assabet.c
++++ linux-2.6.17-rc5/drivers/mfd/ucb1x00-assabet.c
+@@ -15,8 +15,6 @@
+ #include <linux/proc_fs.h>
+ #include <linux/device.h>
+-#include <asm/dma.h>
+-
+ #include "ucb1x00.h"
+ #define UCB1X00_ATTR(name,input)\
+Index: linux-2.6.17-rc5/drivers/mfd/ucb1x00-core.c
+===================================================================
+--- linux-2.6.17-rc5.orig/drivers/mfd/ucb1x00-core.c
++++ linux-2.6.17-rc5/drivers/mfd/ucb1x00-core.c
+@@ -23,6 +23,7 @@
+ #include <linux/init.h>
+ #include <linux/errno.h>
+ #include <linux/interrupt.h>
++#include <linux/kthread.h>
+ #include <linux/device.h>
+ #include <linux/mutex.h>
+@@ -31,6 +32,14 @@
+ #include "ucb1x00.h"
++#if (defined CONFIG_UCB1400) || (defined CONFIG_UCB1400_MODULE)
++#define UCB_IS_1400(id)  ((id) == UCB_ID_1400)
++#define UCB_X_CSR1        0xe         /* this fake entry will be translated by mcp */
++#define UCB_X_CSR2        0xf         /* this fake entry will be translated by mcp */
++#else
++#define UCB_IS_1400(id)  (0)
++#endif
++
+ static DEFINE_MUTEX(ucb1x00_mutex);
+ static LIST_HEAD(ucb1x00_drivers);
+ static LIST_HEAD(ucb1x00_devices);
+@@ -58,9 +67,9 @@ void ucb1x00_io_set_dir(struct ucb1x00 *
+       spin_lock_irqsave(&ucb->io_lock, flags);
+       ucb->io_dir |= out;
+       ucb->io_dir &= ~in;
++      spin_unlock_irqrestore(&ucb->io_lock, flags);
+       ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
+-      spin_unlock_irqrestore(&ucb->io_lock, flags);
+ }
+ /**
+@@ -86,9 +95,9 @@ void ucb1x00_io_write(struct ucb1x00 *uc
+       spin_lock_irqsave(&ucb->io_lock, flags);
+       ucb->io_out |= set;
+       ucb->io_out &= ~clear;
++      spin_unlock_irqrestore(&ucb->io_lock, flags);
+       ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
+-      spin_unlock_irqrestore(&ucb->io_lock, flags);
+ }
+ /**
+@@ -178,7 +187,7 @@ unsigned int ucb1x00_adc_read(struct ucb
+               schedule_timeout(1);
+       }
+-      return UCB_ADC_DAT(val);
++      return UCB_IS_1400(ucb->id) ? (val & 0x3ff) : ((val & 0x7fe0) >> 5);
+ }
+ /**
+@@ -223,6 +232,47 @@ static irqreturn_t ucb1x00_irq(int irqnr
+       return IRQ_HANDLED;
+ }
++/*
++ * A restriction with interrupts exists when using the ucb1400, as
++ * the codec read/write routines may sleep while waiting for codec
++ * access completion and uses semaphores for access control to the
++ * AC97 bus.  A complete codec read cycle could take  anywhere from
++ * 60 to 100uSec so we *definitely* don't want to spin inside the
++ * interrupt handler waiting for codec access.  So, we handle the
++ * interrupt by scheduling a RT kernel thread to run in process
++ * context instead of interrupt context.
++ */
++static int ucb1x00_thread(void *_ucb)
++{
++      struct task_struct *tsk = current;
++      struct ucb1x00 *ucb = _ucb;
++
++      tsk->policy = SCHED_FIFO;
++      tsk->rt_priority = 1;
++
++      while (!kthread_should_stop()) {
++              wait_for_completion_interruptible(&ucb->irq_wait);
++              if (try_to_freeze())
++                      continue;
++              ucb1x00_irq(ucb->irq, ucb, NULL);
++              enable_irq(ucb->irq);
++      }
++
++      ucb->irq_task = NULL;
++      return 0;
++}
++
++static irqreturn_t ucb1x00_threaded_irq(int irqnr, void *devid, struct pt_regs *regs)
++{
++      struct ucb1x00 *ucb = devid;
++      if (irqnr == ucb->irq) {
++              disable_irq(ucb->irq);
++              complete(&ucb->irq_wait);
++              return IRQ_HANDLED;
++      }
++      return IRQ_NONE;
++}
++
+ /**
+  *    ucb1x00_hook_irq - hook a UCB1x00 interrupt
+  *    @ucb:   UCB1x00 structure describing chip
+@@ -276,18 +326,22 @@ void ucb1x00_enable_irq(struct ucb1x00 *
+       if (idx < 16) {
+               spin_lock_irqsave(&ucb->lock, flags);
+-
+-              ucb1x00_enable(ucb);
+-              if (edges & UCB_RISING) {
++              if (edges & UCB_RISING)
+                       ucb->irq_ris_enbl |= 1 << idx;
+-                      ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
+-              }
+-              if (edges & UCB_FALLING) {
++              if (edges & UCB_FALLING)
+                       ucb->irq_fal_enbl |= 1 << idx;
+-                      ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
+-              }
+-              ucb1x00_disable(ucb);
+               spin_unlock_irqrestore(&ucb->lock, flags);
++
++              ucb1x00_enable(ucb);
++
++              /* This prevents spurious interrupts on the UCB1400 */
++              ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 1 << idx);
++              ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
++
++              ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
++              ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
++
++              ucb1x00_disable(ucb);
+       }
+ }
+@@ -305,18 +359,16 @@ void ucb1x00_disable_irq(struct ucb1x00 
+       if (idx < 16) {
+               spin_lock_irqsave(&ucb->lock, flags);
+-
+-              ucb1x00_enable(ucb);
+-              if (edges & UCB_RISING) {
++              if (edges & UCB_RISING)
+                       ucb->irq_ris_enbl &= ~(1 << idx);
+-                      ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
+-              }
+-              if (edges & UCB_FALLING) {
++              if (edges & UCB_FALLING)
+                       ucb->irq_fal_enbl &= ~(1 << idx);
+-                      ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
+-              }
+-              ucb1x00_disable(ucb);
+               spin_unlock_irqrestore(&ucb->lock, flags);
++
++              ucb1x00_enable(ucb);
++              ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
++              ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
++              ucb1x00_disable(ucb);
+       }
+ }
+@@ -349,16 +401,17 @@ int ucb1x00_free_irq(struct ucb1x00 *ucb
+               ucb->irq_ris_enbl &= ~(1 << idx);
+               ucb->irq_fal_enbl &= ~(1 << idx);
+-              ucb1x00_enable(ucb);
+-              ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
+-              ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
+-              ucb1x00_disable(ucb);
+-
+               irq->fn = NULL;
+               irq->devid = NULL;
+               ret = 0;
+       }
+       spin_unlock_irq(&ucb->lock);
++
++      ucb1x00_enable(ucb);
++      ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
++      ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
++      ucb1x00_disable(ucb);
++
+       return ret;
+ bad:
+@@ -478,7 +531,7 @@ static int ucb1x00_probe(struct mcp *mcp
+       mcp_enable(mcp);
+       id = mcp_reg_read(mcp, UCB_ID);
+-      if (id != UCB_ID_1200 && id != UCB_ID_1300) {
++      if (id != UCB_ID_1200 && id != UCB_ID_1300 && !UCB_IS_1400(id)) {
+               printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id);
+               goto err_disable;
+       }
+@@ -491,12 +544,13 @@ static int ucb1x00_probe(struct mcp *mcp
+       memset(ucb, 0, sizeof(struct ucb1x00));
+       ucb->cdev.class = &ucb1x00_class;
+-      ucb->cdev.dev = &mcp->attached_device;
++      ucb->cdev.dev = mcp->dev;
+       strlcpy(ucb->cdev.class_id, "ucb1x00", sizeof(ucb->cdev.class_id));
+       spin_lock_init(&ucb->lock);
+       spin_lock_init(&ucb->io_lock);
+       sema_init(&ucb->adc_sem, 1);
++       init_completion(&ucb->irq_wait);
+       ucb->id  = id;
+       ucb->mcp = mcp;
+@@ -507,13 +561,22 @@ static int ucb1x00_probe(struct mcp *mcp
+               goto err_free;
+       }
+-      ret = request_irq(ucb->irq, ucb1x00_irq, SA_TRIGGER_RISING,
+-                        "UCB1x00", ucb);
++      ret = request_irq(ucb->irq,
++                        UCB_IS_1400(id) ? ucb1x00_threaded_irq : ucb1x00_irq,
++                        0, "UCB1x00", ucb);
+       if (ret) {
+               printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n",
+                       ucb->irq, ret);
+               goto err_free;
+       }
++      if (UCB_IS_1400(id)) {
++              ucb->irq_task = kthread_run(ucb1x00_thread, ucb, "kUCB1x00d");
++              if (IS_ERR(ucb->irq_task)) {
++                      ret = PTR_ERR(ucb->irq_task);
++                      ucb->irq_task = NULL;
++                      goto err_irq;
++              }
++      }
+       mcp_set_drvdata(mcp, ucb);
+@@ -531,6 +594,8 @@ static int ucb1x00_probe(struct mcp *mcp
+       goto out;
+  err_irq:
++      if (UCB_IS_1400(id) && ucb->irq_task)
++              kthread_stop(ucb->irq_task);
+       free_irq(ucb->irq, ucb);
+  err_free:
+       kfree(ucb);
+@@ -553,6 +618,8 @@ static void ucb1x00_remove(struct mcp *m
+       }
+       mutex_unlock(&ucb1x00_mutex);
++      if (UCB_IS_1400(ucb->id) && ucb->irq_task)
++              kthread_stop(ucb->irq_task);
+       free_irq(ucb->irq, ucb);
+       class_device_unregister(&ucb->cdev);
+ }
+Index: linux-2.6.17-rc5/drivers/mfd/ucb1x00-ts.c
+===================================================================
+--- linux-2.6.17-rc5.orig/drivers/mfd/ucb1x00-ts.c
++++ linux-2.6.17-rc5/drivers/mfd/ucb1x00-ts.c
+@@ -33,10 +33,8 @@
+ #include <linux/slab.h>
+ #include <linux/kthread.h>
+-#include <asm/dma.h>
+-#include <asm/semaphore.h>
+-#include <asm/arch/collie.h>
+ #include <asm/mach-types.h>
++#include <asm/arch-sa1100/collie.h>
+ #include "ucb1x00.h"
+@@ -45,7 +43,7 @@ struct ucb1x00_ts {
+       struct input_dev        *idev;
+       struct ucb1x00          *ucb;
+-      wait_queue_head_t       irq_wait;
++      struct completion       irq_wait;
+       struct task_struct      *rtask;
+       u16                     x_res;
+       u16                     y_res;
+@@ -205,7 +203,6 @@ static int ucb1x00_thread(void *_ts)
+ {
+       struct ucb1x00_ts *ts = _ts;
+       struct task_struct *tsk = current;
+-      DECLARE_WAITQUEUE(wait, tsk);
+       int valid;
+       /*
+@@ -217,10 +214,8 @@ static int ucb1x00_thread(void *_ts)
+       valid = 0;
+-      add_wait_queue(&ts->irq_wait, &wait);
+       while (!kthread_should_stop()) {
+               unsigned int x, y, p;
+-              signed long timeout;
+               ts->restart = 0;
+@@ -242,8 +237,6 @@ static int ucb1x00_thread(void *_ts)
+               if (ucb1x00_ts_pen_down(ts)) {
+-                      set_task_state(tsk, TASK_INTERRUPTIBLE);
+-
+                       ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING);
+                       ucb1x00_disable(ts->ucb);
+@@ -256,7 +249,15 @@ static int ucb1x00_thread(void *_ts)
+                               valid = 0;
+                       }
+-                      timeout = MAX_SCHEDULE_TIMEOUT;
++                      /*
++                       * Since ucb1x00_enable_irq() might sleep due
++                       * to the way the UCB1400 regs are accessed, we
++                       * can't use set_task_state() before that call,
++                       * and not changing state before enabling the
++                       * interrupt is racy. A completion handler avoids
++                       * the issue.
++                       */
++                      wait_for_completion_interruptible(&ts->irq_wait);
+               } else {
+                       ucb1x00_disable(ts->ucb);
+@@ -271,16 +272,12 @@ static int ucb1x00_thread(void *_ts)
+                       }
+                       set_task_state(tsk, TASK_INTERRUPTIBLE);
+-                      timeout = HZ / 100;
++                      schedule_timeout(HZ/100);
+               }
+               try_to_freeze();
+-
+-              schedule_timeout(timeout);
+       }
+-      remove_wait_queue(&ts->irq_wait, &wait);
+-
+       ts->rtask = NULL;
+       return 0;
+ }
+@@ -293,7 +290,7 @@ static void ucb1x00_ts_irq(int idx, void
+ {
+       struct ucb1x00_ts *ts = id;
+       ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
+-      wake_up(&ts->irq_wait);
++      complete(&ts->irq_wait);
+ }
+ static int ucb1x00_ts_open(struct input_dev *idev)
+@@ -303,7 +300,7 @@ static int ucb1x00_ts_open(struct input_
+       BUG_ON(ts->rtask);
+-      init_waitqueue_head(&ts->irq_wait);
++      init_completion(&ts->irq_wait);
+       ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts);
+       if (ret < 0)
+               goto out;
+@@ -358,7 +355,7 @@ static int ucb1x00_ts_resume(struct ucb1
+                * after sleep.
+                */
+               ts->restart = 1;
+-              wake_up(&ts->irq_wait);
++              complete(&ts->irq_wait);
+       }
+       return 0;
+ }
+Index: linux-2.6.17-rc5/drivers/mfd/ucb1x00.h
+===================================================================
+--- linux-2.6.17-rc5.orig/drivers/mfd/ucb1x00.h
++++ linux-2.6.17-rc5/drivers/mfd/ucb1x00.h
+@@ -94,6 +94,7 @@
+ #define UCB_ID                0x0c
+ #define UCB_ID_1200           0x1004
+ #define UCB_ID_1300           0x1005
++#define UCB_ID_1400           0x4304
+ #define UCB_MODE      0x0d
+ #define UCB_MODE_DYN_VFLAG_ENA        (1 << 12)
+@@ -110,6 +111,8 @@ struct ucb1x00 {
+       spinlock_t              lock;
+       struct mcp              *mcp;
+       unsigned int            irq;
++      struct task_struct      *irq_task;
++      struct completion       irq_wait;
+       struct semaphore        adc_sem;
+       spinlock_t              io_lock;
+       u16                     id;
+@@ -122,6 +125,7 @@ struct ucb1x00 {
+       struct class_device     cdev;
+       struct list_head        node;
+       struct list_head        devs;
++
+ };
+ struct ucb1x00_driver;
+Index: linux-2.6.17-rc5/drivers/mfd/mcp-ac97.c
+===================================================================
+--- /dev/null
++++ linux-2.6.17-rc5/drivers/mfd/mcp-ac97.c
+@@ -0,0 +1,153 @@
++/*
++ * linux/drivers/misc/mcp-ac97.c
++ *
++ * Author:    Nicolas Pitre
++ * Created:   Jan 14, 2005
++ * Copyright: (C) MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This module provides the minimum replacement for mcp-core.c allowing for
++ * the UCB1400 chip to be driven by the ucb1x00 driver over an AC97 link.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/device.h>
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/ac97_codec.h>
++
++#include "mcp.h"
++
++/* ucb1x00 SIB register to ucb1400 AC-link register mapping */
++
++static const unsigned char regmap[] = {
++      0x5a,   /* UCB_IO_DATA */
++      0X5C,   /* UCB_IO_DIR */
++      0X5E,   /* UCB_IE_RIS */
++      0x60,   /* UCB_IE_FAL */
++      0x62,   /* UCB_IE_STATUS */
++      0,      /* UCB_TC_A */
++      0,      /* UCB_TC_B */
++      0,      /* UCB_AC_A */
++      0,      /* UCB_AC_B */
++      0x64,   /* UCB_TS_CR */
++      0x66,   /* UCB_ADC_CR */
++      0x68,   /* UCB_ADC_DATA */
++      0x7e,   /* UCB_ID */
++      0,      /* UCB_MODE */
++};
++
++unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg)
++{
++      ac97_t *ac97 = to_ac97_t(mcp->dev);
++      if (reg < ARRAY_SIZE(regmap)) {
++              reg = regmap[reg];
++              if (reg)
++                      return ac97->bus->ops->read(ac97, reg);
++      }
++      return -1;
++}
++EXPORT_SYMBOL(mcp_reg_read);
++
++void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val)
++{
++      ac97_t *ac97 = to_ac97_t(mcp->dev);
++      if (reg < ARRAY_SIZE(regmap)) {
++              reg = regmap[reg];
++              if (reg)
++                      ac97->bus->ops->write(ac97, reg, val);
++      }
++}
++EXPORT_SYMBOL(mcp_reg_write);
++
++void mcp_enable(struct mcp *mcp)
++{
++}
++EXPORT_SYMBOL(mcp_enable);
++
++void mcp_disable(struct mcp *mcp)
++{
++}
++EXPORT_SYMBOL(mcp_disable);
++
++#define to_mcp_driver(d)      container_of(d, struct mcp_driver, drv)
++
++static int mcp_probe(struct device *dev)
++{
++      struct mcp_driver *drv = to_mcp_driver(dev->driver);
++      struct mcp *mcp;
++      int ret;
++
++      ret = -ENOMEM;
++      mcp = kmalloc(sizeof(*mcp), GFP_KERNEL);
++      if (mcp) {
++              memset(mcp, 0, sizeof(*mcp));
++              mcp->owner = THIS_MODULE;
++              mcp->dev = dev;
++              ret = drv->probe(mcp);
++              if (ret)
++                      kfree(mcp);
++      }
++      if (!ret)
++              dev_set_drvdata(dev, mcp);
++      return ret;
++}
++
++static int mcp_remove(struct device *dev)
++{
++      struct mcp_driver *drv = to_mcp_driver(dev->driver);
++      struct mcp *mcp = dev_get_drvdata(dev);
++
++      drv->remove(mcp);
++      dev_set_drvdata(dev, NULL);
++      kfree(mcp);
++      return 0;
++}
++
++static int mcp_suspend(struct device *dev, pm_message_t state)
++{
++      struct mcp_driver *drv = to_mcp_driver(dev->driver);
++      struct mcp *mcp = dev_get_drvdata(dev);
++      int ret = 0;
++
++      if (drv->suspend)
++              ret = drv->suspend(mcp, state);
++      return ret;
++}
++
++static int mcp_resume(struct device *dev)
++{
++      struct mcp_driver *drv = to_mcp_driver(dev->driver);
++      struct mcp *mcp = dev_get_drvdata(dev);
++      int ret = 0;
++
++      if (drv->resume)
++              ret = drv->resume(mcp);
++      return ret;
++}
++
++int mcp_driver_register(struct mcp_driver *mcpdrv)
++{
++      mcpdrv->drv.owner = THIS_MODULE;
++      mcpdrv->drv.bus = &ac97_bus_type;
++      mcpdrv->drv.probe = mcp_probe;
++      mcpdrv->drv.remove = mcp_remove;
++      mcpdrv->drv.suspend = mcp_suspend;
++      mcpdrv->drv.resume = mcp_resume;
++      return driver_register(&mcpdrv->drv);
++}
++EXPORT_SYMBOL(mcp_driver_register);
++
++void mcp_driver_unregister(struct mcp_driver *mcpdrv)
++{
++      driver_unregister(&mcpdrv->drv);
++}
++EXPORT_SYMBOL(mcp_driver_unregister);
++
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17-rc5/drivers/input/touchscreen/Kconfig
+===================================================================
+--- linux-2.6.17-rc5.orig/drivers/input/touchscreen/Kconfig
++++ linux-2.6.17-rc5/drivers/input/touchscreen/Kconfig
+@@ -24,6 +24,25 @@ config TOUCHSCREEN_ADS7846
+         To compile this driver as a module, choose M here: the
+         module will be called ads7846.
++config UCB1400
++      bool
++
++config TOUCHSCREEN_UCB1400
++      tristate "UCB1400 Touchscreen support"
++      depends on ARCH_LUBBOCK || MACH_MAINSTONE || ARCH_PXA
++      select SND_AC97_BUS
++      select UCB1400
++      help
++        Say Y here if you have a touchscreen connected to a UCB1400 ADC chip
++        on the AC97 bus of a PXA255/PXA270 host.
++
++        If unsure, say N.
++
++        To compile this driver as a module, choose M here: the
++        module will be called ucb1x00-ts.  It will also build the modules
++        ucb1x00-core and mcp-ac97 which provide the compatibility layers
++        down to the AC97 bus.
++
+ config TOUCHSCREEN_BITSY
+       tristate "Compaq iPAQ H3600 (Bitsy) touchscreen"
+       depends on SA1100_BITSY
+Index: linux-2.6.17-rc5/drivers/input/Kconfig
+===================================================================
+--- linux-2.6.17-rc5.orig/drivers/input/Kconfig
++++ linux-2.6.17-rc5/drivers/input/Kconfig
+@@ -87,7 +87,7 @@ config INPUT_JOYDEV
+         module will be called joydev.
+ config INPUT_TSDEV
+-      tristate "Touchscreen interface"
++      tristate "Compaq touchscreen interface"
+       ---help---
+         Say Y here if you have an application that only can understand the
+         Compaq touchscreen protocol for absolute pointer data. This is
index 18af827..42956f8 100644 (file)
@@ -1,13 +1,14 @@
 SECTION = "kernel"
 DESCRIPTION = "Linux kernel for the LogicPD Zoom(PXA270 ref design)"
 LICENSE = "GPL"
-PR = "r1"
+PR = "r3"
 
 SRC_URI = "ftp://ftp.kernel.org/pub/linux/kernel/v2.6/testing/linux-2.6.17-rc5.tar.bz2 \
            file://linux-2.6.17-rc5.patch;pnum=0;patch=1 \
+           file://ucb1400-ac97-audio.patch;pnum=1;patch=1 \
+           file://ucb1400-touchscreen.patch;pnum=1;patch=1 \
            file://defconfig"
 
-
 S = "${WORKDIR}/linux-2.6.17-rc5"
 
 COMPATIBLE_HOST = 'arm.*-linux'