Binary files linux-2.6.18-vanilla/arch/arm/boot/Image and linux-2.6.18/arch/arm/boot/Image differ
diff -uprN -X dontdiff linux-2.6.18-vanilla/arch/arm/kernel/process.c linux-2.6.18/arch/arm/kernel/process.c
--- linux-2.6.18-vanilla/arch/arm/kernel/process.c	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18/arch/arm/kernel/process.c	2006-10-30 18:09:54.000000000 +0100
@@ -173,8 +173,11 @@ int __init reboot_setup(char *str)
 
 __setup("reboot=", reboot_setup);
 
+#define POWER_OFF 13
+
 void machine_halt(void)
 {
+	pxa_gpio_mode(POWER_OFF | GPIO_IN);
 }
 
 
diff -uprN -X dontdiff linux-2.6.18-vanilla/arch/arm/mach-pxa/idp.c linux-2.6.18/arch/arm/mach-pxa/idp.c
--- linux-2.6.18-vanilla/arch/arm/mach-pxa/idp.c	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18/arch/arm/mach-pxa/idp.c	2006-11-10 23:55:52.000000000 +0100
@@ -18,7 +18,6 @@
 
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/irq.h>
 #include <linux/platform_device.h>
 #include <linux/fb.h>
 
@@ -31,6 +30,7 @@
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 
+#include <asm/arch/udc.h>
 #include <asm/arch/pxa-regs.h>
 #include <asm/arch/idp.h>
 #include <asm/arch/pxafb.h>
@@ -39,90 +39,62 @@
 
 #include "generic.h"
 
-/* TODO:
- * - add pxa2xx_audio_ops_t device structure
- * - Ethernet interrupt
- */
-
-static struct resource smc91x_resources[] = {
-	[0] = {
-		.start	= (IDP_ETH_PHYS + 0x300),
-		.end	= (IDP_ETH_PHYS + 0xfffff),
-		.flags	= IORESOURCE_MEM,
-	},
-	[1] = {
-		.start	= IRQ_GPIO(4),
-		.end	= IRQ_GPIO(4),
-		.flags	= IORESOURCE_IRQ,
-	}
-};
-
-static struct platform_device smc91x_device = {
-	.name		= "smc91x",
-	.id		= 0,
-	.num_resources	= ARRAY_SIZE(smc91x_resources),
-	.resource	= smc91x_resources,
-};
-
 static void idp_backlight_power(int on)
 {
-	if (on) {
-		IDP_CPLD_LCD |= (1<<1);
-	} else {
-		IDP_CPLD_LCD &= ~(1<<1);
-	}
-}
-
-static void idp_vlcd(int on)
-{
-	if (on) {
-		IDP_CPLD_LCD |= (1<<2);
-	} else {
-		IDP_CPLD_LCD &= ~(1<<2);
-	}
 }
 
 static void idp_lcd_power(int on)
 {
-	if (on) {
-		IDP_CPLD_LCD |= (1<<0);
-	} else {
-		IDP_CPLD_LCD &= ~(1<<0);
-	}
-
-	/* call idp_vlcd for now as core driver does not support
-	 * both power and vlcd hooks.  Note, this is not technically
-	 * the correct sequence, but seems to work.  Disclaimer:
-	 * this may eventually damage the display.
-	 */
-
-	idp_vlcd(on);
 }
 
 static struct pxafb_mach_info sharp_lm8v31 __initdata = {
-	.pixclock	= 270000,
-	.xres		= 640,
-	.yres		= 480,
+	.pixclock	= 0,
+	.xres		= 240,
+	.yres		= 320,
 	.bpp		= 16,
-	.hsync_len	= 1,
-	.left_margin	= 3,
-	.right_margin	= 3,
+	.hsync_len	= 3,
+	.left_margin	= 7,
+	.right_margin	= 7,
 	.vsync_len	= 1,
-	.upper_margin	= 0,
-	.lower_margin	= 0,
+	.upper_margin	= 8,
+	.lower_margin	= 8,
 	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 	.cmap_greyscale	= 0,
 	.cmap_inverse	= 0,
 	.cmap_static	= 0,
-	.lccr0		= LCCR0_SDS,
-	.lccr3		= LCCR3_PCP | LCCR3_Acb(255),
+	.lccr0		= 0x001000f9,
+	.lccr3		= 0x04b0ff09,
 	.pxafb_backlight_power = &idp_backlight_power,
 	.pxafb_lcd_power = &idp_lcd_power
 };
 
+#define NAVMAN_GPIO_USB_PULLUP 14
+
+static void udc_command(int cmd)
+{
+        switch(cmd)     {
+        case PXA2XX_UDC_CMD_CONNECT:
+                GPSR(NAVMAN_GPIO_USB_PULLUP) = GPIO_bit(NAVMAN_GPIO_USB_PULLUP);
+                break;
+        case PXA2XX_UDC_CMD_DISCONNECT:
+                GPCR(NAVMAN_GPIO_USB_PULLUP) = GPIO_bit(NAVMAN_GPIO_USB_PULLUP);
+                break;
+        }
+}
+
+static int udc_is_connected(void)
+{
+        return (GPLR(1) & GPIO_bit(1)) == 0;
+}
+
+static struct pxa2xx_udc_mach_info udc_info __initdata = {
+        .udc_is_connected       = udc_is_connected,
+        .udc_command            = udc_command,
+};
+
 static int idp_mci_init(struct device *dev, irqreturn_t (*idp_detect_int)(int, void *, struct pt_regs *), void *data)
 {
-	/* setup GPIO for PXA25x MMC controller	*/
+	/* setup GPIO for PXA25x MMC controller */
 	pxa_gpio_mode(GPIO6_MMCCLK_MD);
 	pxa_gpio_mode(GPIO8_MMCCS0_MD);
 
@@ -136,10 +108,9 @@ static struct pxamci_platform_data idp_m
 
 static void __init idp_init(void)
 {
-	printk("idp_init()\n");
+	pxa_gpio_mode(NAVMAN_GPIO_USB_PULLUP | GPIO_OUT);
 
-	platform_device_register(&smc91x_device);
-	//platform_device_register(&mst_audio_device);
+	pxa_set_udc_info(&udc_info);
 	set_pxa_fb_info(&sharp_lm8v31);
 	pxa_set_mci_info(&idp_mci_platform_data);
 }
@@ -148,37 +119,11 @@ static void __init idp_init_irq(void)
 {
 
 	pxa_init_irq();
-
-	set_irq_type(TOUCH_PANEL_IRQ, TOUCH_PANEL_IRQ_EDGE);
 }
 
-static struct map_desc idp_io_desc[] __initdata = {
-  	{
-		.virtual	=  IDP_COREVOLT_VIRT,
-		.pfn		= __phys_to_pfn(IDP_COREVOLT_PHYS),
-		.length		= IDP_COREVOLT_SIZE,
-		.type		= MT_DEVICE
-	}, {
-		.virtual	=  IDP_CPLD_VIRT,
-		.pfn		= __phys_to_pfn(IDP_CPLD_PHYS),
-		.length		= IDP_CPLD_SIZE,
-		.type		= MT_DEVICE
-	}
-};
-
 static void __init idp_map_io(void)
 {
 	pxa_map_io();
-	iotable_init(idp_io_desc, ARRAY_SIZE(idp_io_desc));
-
-	// serial ports 2 & 3
-	pxa_gpio_mode(GPIO42_BTRXD_MD);
-	pxa_gpio_mode(GPIO43_BTTXD_MD);
-	pxa_gpio_mode(GPIO44_BTCTS_MD);
-	pxa_gpio_mode(GPIO45_BTRTS_MD);
-	pxa_gpio_mode(GPIO46_STRXD_MD);
-	pxa_gpio_mode(GPIO47_STTXD_MD);
-
 }
 
 
diff -uprN -X dontdiff linux-2.6.18-vanilla/arch/arm/mm/consistent.c linux-2.6.18/arch/arm/mm/consistent.c
--- linux-2.6.18-vanilla/arch/arm/mm/consistent.c	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18/arch/arm/mm/consistent.c	2006-10-14 00:37:08.000000000 +0200
@@ -153,6 +153,8 @@ __dma_alloc(struct device *dev, size_t s
 	unsigned long order;
 	u64 mask = ISA_DMA_THRESHOLD, limit;
 
+	gfp &= ~(__GFP_COMP);
+
 	if (!consistent_pte[0]) {
 		printk(KERN_ERR "%s: not initialised\n", __func__);
 		dump_stack();
diff -uprN -X dontdiff linux-2.6.18-vanilla/drivers/input/keyboard/icn330kbd.c linux-2.6.18/drivers/input/keyboard/icn330kbd.c
--- linux-2.6.18-vanilla/drivers/input/keyboard/icn330kbd.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.18/drivers/input/keyboard/icn330kbd.c	2006-11-04 23:01:01.000000000 +0100
@@ -0,0 +1,280 @@
+/*
+ *  Keyboard driver for Navman iCN 330
+ *
+ *  Based on spitzkbd.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/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+
+#include "icn330kbd.h"
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+
+#define POWER_BUTTON 3
+
+#define NUM_SENSES 4
+#define START_SENSES 19
+
+#define POWER_OFF 13
+
+#define NR_SCANCODES (NUM_SENSES*3)
+
+#define BUTTON_RIGHT_BOTTOM 1
+#define BUTTON_KEYBOARD     2
+#define BUTTON_UP           3
+#define BUTTON_RIGHT        4
+#define BUTTON_VOLUME_UP    5
+#define BUTTON_LEFT_BOTTOM  6
+#define BUTTON_LEFT         7
+#define BUTTON_DOWN         8
+#define BUTTON_VOLUME_DOWN  9
+#define BUTTON_OK           10
+
+static unsigned char TOMTOM_KEYCODES[] = 
+{
+	BUTTON_OK, BUTTON_LEFT_BOTTOM, BUTTON_LEFT, BUTTON_KEYBOARD, 
+	BUTTON_RIGHT_BOTTOM, BUTTON_RIGHT, 0, 
+	BUTTON_VOLUME_DOWN, BUTTON_DOWN, 0, BUTTON_VOLUME_UP, BUTTON_UP
+};
+
+static unsigned char last_key_pressed = 0;
+
+static unsigned char icnkbd_keycode[NR_SCANCODES] = {
+	KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7,
+	KEY_8, KEY_9, KEY_SPACE, KEY_ENTER
+};
+
+static unsigned char last_key_pressed_group[4] = {0, 0, 0};
+
+static struct input_dev *input_dev;
+static wait_queue_head_t key_wait;
+
+static irqreturn_t icn330kbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int group, gpio_pin, index = -1;
+
+	if(irq == IRQ_GPIO(POWER_BUTTON)) {
+		// Ask init to kill us so we can power off.
+		kill_proc (1, SIGUSR1, 1);
+		return IRQ_HANDLED;
+	}       
+
+	for(group=0; group<NUM_SENSES; group++) {
+	  gpio_pin = START_SENSES+group;
+	  if(IRQ_GPIO(gpio_pin) == irq)
+	    break;
+	}
+
+	// We need to disable the GPIO interrupt when doing this check
+	// or else it will fire again
+
+	GRER(gpio_pin) &= ~GPIO_bit(gpio_pin);
+	GFER(gpio_pin) &= ~GPIO_bit(gpio_pin);
+
+	GPSR(0x24) = GPIO_bit(0x24);
+	GPSR(0x25) = GPIO_bit(0x25);
+	GPCR(0x26) = GPIO_bit(0x26);
+	
+	if((GPLR(gpio_pin) & GPIO_bit(gpio_pin)) == 0)
+		index = 0;
+
+        GPCR(0x24) = GPIO_bit(0x24);
+        GPSR(0x26) = GPIO_bit(0x26);
+
+	if((GPLR(gpio_pin) & GPIO_bit(gpio_pin)) == 0)
+		index = 1;
+
+        GPCR(0x25) = GPIO_bit(0x25);
+        GPSR(0x24) = GPIO_bit(0x24);
+
+	if((GPLR(gpio_pin) & GPIO_bit(gpio_pin)) == 0)
+		index = 2;
+	
+        GPCR(0x24) = GPIO_bit(0x24);
+        GPCR(0x26) = GPIO_bit(0x26);
+
+        GRER(gpio_pin) |= GPIO_bit(gpio_pin);
+        GFER(gpio_pin) |= GPIO_bit(gpio_pin);
+
+	if (index == -1)
+	{
+		input_report_key(input_dev, icnkbd_keycode[last_key_pressed_group[group]+group*3], 0);
+
+	} else {
+		last_key_pressed_group[group] = index;
+		input_report_key(input_dev, icnkbd_keycode[index+group*3], 1);
+
+		last_key_pressed = TOMTOM_KEYCODES[index+group*3];
+		wake_up_interruptible(&key_wait);
+	}
+
+	return IRQ_HANDLED;
+}
+
+#define RC_DEVNAME                                      "rc"
+#define RC_MAJOR                                        122
+
+typedef struct {
+        unsigned int id;
+        unsigned char batteryStatus;
+        unsigned char keycode;
+} RC_EVENT;
+
+static ssize_t rc_read(struct file *file, char __user *data, size_t len, loff_t *ppos)
+{
+	RC_EVENT ev;
+
+	if(last_key_pressed == 0) {
+		if (file->f_flags & O_NONBLOCK) {
+			return -EAGAIN;
+		} else {
+			DECLARE_WAITQUEUE(wait, current);
+			
+			add_wait_queue(&key_wait, &wait);
+			set_current_state(TASK_INTERRUPTIBLE);
+			for (;;) {
+				if(last_key_pressed != 0)
+					break;
+				if (signal_pending(current)) {
+					set_current_state(TASK_RUNNING);
+					remove_wait_queue(&key_wait, &wait);
+					return -ERESTARTSYS;
+				}
+				schedule();
+			}
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&key_wait, &wait);
+		}
+	}
+
+	ev.keycode = last_key_pressed;
+	ev.batteryStatus = 10;
+	ev.id = 0;
+
+	last_key_pressed = 0;
+
+	return __copy_to_user(data, &ev, sizeof(RC_EVENT)) ? -EFAULT : sizeof(RC_EVENT);
+}
+
+static unsigned int rc_poll(struct file *file, struct poll_table_struct *wait)
+{
+	int ret;
+	poll_wait(file, &key_wait, wait);
+
+	ret = (last_key_pressed == 0) ? 0 : (POLLIN | POLLRDNORM);
+
+	return 0;
+}
+
+
+static int rc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+        return 0;
+}
+
+static int rc_open(struct inode *inode, struct file *file)
+{
+	return nonseekable_open(inode, file);
+}
+
+static int rc_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static struct file_operations rc_fops = {
+	.owner		= THIS_MODULE,
+	.read		= rc_read,
+	.poll		= rc_poll,
+	.ioctl		= rc_ioctl,
+	.open		= rc_open,
+	.release	= rc_release,
+};
+
+
+static int __devinit icn330kbd_init(void)
+{
+	int i;
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		return -ENOMEM;
+	}
+
+	input_dev->name = "iCN 330 Keyboard";
+	input_dev->phys = "icnkbd/input0";
+
+	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);
+	input_dev->keycodemax	= ARRAY_SIZE(icnkbd_keycode);
+	input_dev->keycodesize	= sizeof(icnkbd_keycode[0]);
+	input_dev->keycode	= icnkbd_keycode;
+
+	for (i = 0; i < NR_SCANCODES; i++)
+		set_bit(icnkbd_keycode[i], input_dev->keybit);
+	clear_bit(0, input_dev->keybit);
+
+	input_register_device(input_dev);
+
+	// Register power off handler
+	pxa_gpio_mode(POWER_BUTTON | GPIO_IN);	
+	request_irq(IRQ_GPIO(POWER_BUTTON), icn330kbd_interrupt,
+		    IRQF_DISABLED|IRQF_TRIGGER_RISING,
+		    "Power Off", input_dev);
+
+	for (i = 0; i < NUM_SENSES; i++) {
+		pxa_gpio_mode((START_SENSES+i) | GPIO_IN);
+		set_irq_type(IRQ_GPIO(START_SENSES+i), IRQT_BOTHEDGE);
+		if (request_irq(IRQ_GPIO(START_SENSES+i), icn330kbd_interrupt,
+				0,
+				"iCNkbd Sense", input_dev))
+			printk(KERN_WARNING "spitzkbd: Can't get Sense IRQ: %d!\n", i);
+	}
+
+	/* Button data pins */
+	pxa_gpio_mode(0x24 | GPIO_OUT);
+	pxa_gpio_mode(0x25 | GPIO_OUT);
+	pxa_gpio_mode(0x26 | GPIO_OUT);
+
+	init_waitqueue_head(&key_wait);
+
+	register_chrdev(RC_MAJOR, RC_DEVNAME, &rc_fops);
+
+	return 0;
+}
+
+static void __exit icn330kbd_exit(void)
+{
+	int i;
+
+	for (i = 0; i < NUM_SENSES; i++)
+		free_irq(IRQ_GPIO(START_SENSES+i), input_dev);
+
+	input_unregister_device(input_dev);
+	unregister_chrdev(RC_MAJOR, RC_DEVNAME);
+}
+
+module_init(icn330kbd_init);
+module_exit(icn330kbd_exit);
+
+MODULE_AUTHOR("Rasmus Rohde <rohde@duff.dk>");
+MODULE_DESCRIPTION("iCN330 Keyboard Driver");
+MODULE_LICENSE("GPLv2");
diff -uprN -X dontdiff linux-2.6.18-vanilla/drivers/input/keyboard/icn330kbd.h linux-2.6.18/drivers/input/keyboard/icn330kbd.h
--- linux-2.6.18-vanilla/drivers/input/keyboard/icn330kbd.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.18/drivers/input/keyboard/icn330kbd.h	2006-10-17 17:19:11.000000000 +0200
@@ -0,0 +1,7 @@
+
+#define SADIV_BITCLK_3_072_MHZ  0x0c /* 48.000 KHz sampling rate */
+#define SADIV_BITCLK_2_836_MHZ  0x0d /* 44.308 KHz sampling rate */
+#define SADIV_BITCLK_1_418_MHZ  0x1a /* 22.154 KHz sampling rate */
+#define SADIV_BITCLK_1_024_MHZ  0x24 /* 16.000 KHz sampling rate */
+#define SADIV_BITCLK_708_92_KHZ 0x34 /* 11.077 KHz sampling rate */
+#define SADIV_BITCLK_512_00_KHZ 0x48 /*  8.000 KHz sampling rate */
diff -uprN -X dontdiff linux-2.6.18-vanilla/drivers/input/keyboard/Kconfig linux-2.6.18/drivers/input/keyboard/Kconfig
--- linux-2.6.18-vanilla/drivers/input/keyboard/Kconfig	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18/drivers/input/keyboard/Kconfig	2006-10-01 20:30:05.000000000 +0200
@@ -132,6 +132,17 @@ config KEYBOARD_CORGI
 	  To compile this driver as a module, choose M here: the 
 	  module will be called corgikbd.
 
+config KEYBOARD_ICN330
+        tristate "iCN330 keyboard"
+        depends on ARCH_PXA_IDP
+        default y
+        help
+          Say Y here to enable a pseudo keyboard for the Navman iCN 330
+
+          To compile this driver as a module, choose M here: the
+          module will be called icn330kbd.
+
+
 config KEYBOARD_SPITZ
 	tristate "Spitz keyboard"
 	depends on PXA_SHARPSL
diff -uprN -X dontdiff linux-2.6.18-vanilla/drivers/input/keyboard/Makefile linux-2.6.18/drivers/input/keyboard/Makefile
--- linux-2.6.18-vanilla/drivers/input/keyboard/Makefile	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18/drivers/input/keyboard/Makefile	2006-10-01 20:24:49.000000000 +0200
@@ -4,6 +4,7 @@
 
 # Each configuration option enables a list of files.
 
+obj-$(CONFIG_KEYBOARD_ICN330)		+= icn330kbd.o
 obj-$(CONFIG_KEYBOARD_ATKBD)		+= atkbd.o
 obj-$(CONFIG_KEYBOARD_SUNKBD)		+= sunkbd.o
 obj-$(CONFIG_KEYBOARD_LKKBD)		+= lkkbd.o
diff -uprN -X dontdiff linux-2.6.18-vanilla/drivers/mmc/mmc_block.c linux-2.6.18/drivers/mmc/mmc_block.c
--- linux-2.6.18-vanilla/drivers/mmc/mmc_block.c	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18/drivers/mmc/mmc_block.c	2006-10-08 20:41:23.000000000 +0200
@@ -43,7 +43,7 @@
  */
 #define MMC_SHIFT	3
 
-static int major;
+static int major = 250;
 
 /*
  * There is one mmc_blk_data per slot.
diff -uprN -X dontdiff linux-2.6.18-vanilla/drivers/mmc/mmc.c linux-2.6.18/drivers/mmc/mmc.c
--- linux-2.6.18-vanilla/drivers/mmc/mmc.c	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18/drivers/mmc/mmc.c	2006-11-02 21:52:18.000000000 +0100
@@ -668,7 +668,7 @@ static void mmc_decode_scr(struct mmc_ca
 	resp[2] = card->raw_scr[0];
 
 	scr_struct = UNSTUFF_BITS(resp, 60, 4);
-	if (scr_struct != 0) {
+	if (scr_struct != 0 && scr_struct != 1) {
 		printk("%s: unrecognised SCR structure version %d\n",
 			mmc_hostname(card->host), scr_struct);
 		mmc_card_set_bad(card);
diff -uprN -X dontdiff linux-2.6.18-vanilla/drivers/mtd/chips/jedec_probe.c linux-2.6.18/drivers/mtd/chips/jedec_probe.c
--- linux-2.6.18-vanilla/drivers/mtd/chips/jedec_probe.c	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18/drivers/mtd/chips/jedec_probe.c	2006-11-08 16:23:39.000000000 +0100
@@ -1818,7 +1818,7 @@ static inline __u8 finfo_uaddr(const str
 
 	uaddr = finfo->uaddr[uaddr_idx];
 
-	if (uaddr != MTD_UADDR_NOT_SUPPORTED ) {
+	if (uaddr == MTD_UADDR_NOT_SUPPORTED ) {
 		/* ASSERT("The unlock addresses for non-8-bit mode
 		   are bollocks. We don't really need an array."); */
 		uaddr = finfo->uaddr[0];
diff -uprN -X dontdiff linux-2.6.18-vanilla/drivers/mtd/maps/icn330-flash.c linux-2.6.18/drivers/mtd/maps/icn330-flash.c
--- linux-2.6.18-vanilla/drivers/mtd/maps/icn330-flash.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.18/drivers/mtd/maps/icn330-flash.c	2006-11-07 22:56:02.000000000 +0100
@@ -0,0 +1,90 @@
+/*
+ * $Id:  $
+ *
+ * Map driver for the Navman iCN 330
+ *
+ * Author:	Rasmus Rohde
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/arch/pxa-regs.h>
+
+#define WINDOW_SIZE 0x100000
+
+static struct map_info icn330_map = {
+	.name = "iCN 330 flash",
+	.size =	WINDOW_SIZE,
+	.phys =	PXA_CS0_PHYS,
+};
+
+static struct mtd_partition icn330_partitions[] = {
+	{
+		.name =		"All",
+		.size =		WINDOW_SIZE,
+		.offset =	0,
+		.mask_flags =	MTD_WRITEABLE  /* force read-only */
+	}
+};
+
+static struct mtd_info *mymtd;
+
+static int __init init_icn330(void)
+{
+	icn330_map.bankwidth = (BOOT_DEF & 1) ? 2 : 4;
+
+	icn330_map.virt = ioremap(icn330_map.phys, WINDOW_SIZE);
+		
+	simple_map_init(&icn330_map);
+		
+	mymtd = do_map_probe("jedec_probe", &icn330_map);
+	
+	if (!mymtd) {
+		mymtd = do_map_probe("map_rom", &icn330_map);
+	}
+
+	if (!mymtd) {
+		iounmap((void *)icn330_map.virt);
+		return -ENXIO;
+	}
+
+	mymtd->owner = THIS_MODULE;
+
+	add_mtd_partitions(mymtd, icn330_partitions, 1);
+
+	return 0;
+}
+
+static void __exit cleanup_icn330(void)
+{
+	if (mymtd) {
+		del_mtd_partitions(mymtd);
+		map_destroy(mymtd);
+	}
+	if (icn330_map.virt) {
+		iounmap((void *)icn330_map.virt);
+		icn330_map.virt = 0;
+	}
+}
+
+module_init(init_icn330);
+module_exit(cleanup_icn330);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Rasmus Rohde <rohde@duff.dk>");
+MODULE_DESCRIPTION("MTD map driver for Navman iCN 330");
diff -uprN -X dontdiff linux-2.6.18-vanilla/drivers/mtd/maps/Kconfig linux-2.6.18/drivers/mtd/maps/Kconfig
--- linux-2.6.18-vanilla/drivers/mtd/maps/Kconfig	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18/drivers/mtd/maps/Kconfig	2006-11-08 06:52:04.000000000 +0100
@@ -129,6 +129,13 @@ config MTD_LUBBOCK
 	  This provides a driver for the on-board flash of the Intel
 	  'Lubbock' XScale evaluation board.
 
+config MTD_ICN330
+        tristate "JEDEC Flash device mapped on Navman iCN 330"
+        depends on ARCH_PXA_IDP && MTD_JEDECPROBE
+        select MTD_PARTITIONS
+        help
+          This provides a driver for the on-board flash of the Intel
+
 config MTD_MAINSTONE
 	tristate "CFI Flash device mapped on Intel Mainstone XScale eval board"
 	depends on MACH_MAINSTONE && MTD_CFI_INTELEXT
diff -uprN -X dontdiff linux-2.6.18-vanilla/drivers/mtd/maps/Makefile linux-2.6.18/drivers/mtd/maps/Makefile
--- linux-2.6.18-vanilla/drivers/mtd/maps/Makefile	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18/drivers/mtd/maps/Makefile	2006-11-07 18:39:30.000000000 +0100
@@ -71,3 +71,4 @@ obj-$(CONFIG_MTD_PLATRAM)	+= plat-ram.o
 obj-$(CONFIG_MTD_OMAP_NOR)	+= omap_nor.o
 obj-$(CONFIG_MTD_MTX1)		+= mtx-1_flash.o
 obj-$(CONFIG_MTD_TQM834x)	+= tqm834x.o
+obj-$(CONFIG_MTD_ICN330)        += icn330-flash.o
diff -uprN -X dontdiff linux-2.6.18-vanilla/drivers/usb/gadget/pxa2xx_udc.c linux-2.6.18/drivers/usb/gadget/pxa2xx_udc.c
--- linux-2.6.18-vanilla/drivers/usb/gadget/pxa2xx_udc.c	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18/drivers/usb/gadget/pxa2xx_udc.c	2006-11-11 09:33:31.000000000 +0100
@@ -43,6 +43,7 @@
 #include <linux/mm.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
+#include <linux/irq.h>
 
 #include <asm/byteorder.h>
 #include <asm/dma.h>
@@ -1681,7 +1682,28 @@ int usb_gadget_unregister_driver(struct 
 }
 EXPORT_SYMBOL(usb_gadget_unregister_driver);
 
+static irqreturn_t
+icn330k_usb_connect(int irq, void *_dev, struct pt_regs *r)
+{
+        struct pxa2xx_udc       *dev = _dev;
+	int vbus;
+
+	if(irq == IRQ_GPIO(1)) {
+		dev->stats.irqs++;
+	       	if((GPLR(1) & GPIO_bit(1)) == 0) {
+			LED_CONNECTED_ON;
+			vbus = 1;
+		}
+		else {
+			LED_CONNECTED_OFF;
+			vbus = 0;
+		}
 
+        	pxa2xx_udc_vbus_session(&dev->gadget, vbus);
+	        return IRQ_HANDLED;
+	}
+	return IRQ_NONE;
+}
 /*-------------------------------------------------------------------------*/
 
 #ifdef CONFIG_ARCH_LUBBOCK
@@ -2435,7 +2457,7 @@ static struct pxa2xx_udc memory = {
 /*
  * 	probe - binds to the platform device
  */
-static int __init pxa2xx_udc_probe(struct platform_device *pdev)
+static int pxa2xx_udc_probe(struct platform_device *pdev)
 {
 	struct pxa2xx_udc *dev = &memory;
 	int retval, out_dma = 1;
@@ -2527,6 +2549,17 @@ static int __init pxa2xx_udc_probe(struc
 			driver_name, IRQ_USB, retval);
 		return -EBUSY;
 	}
+
+        pxa_gpio_mode(1 | GPIO_IN);
+        set_irq_type(IRQ_GPIO(1), IRQT_BOTHEDGE);
+        retval = request_irq(IRQ_GPIO(1), icn330k_usb_connect,
+                        0,
+                        driver_name, dev);
+        if (retval != 0) {
+                printk(KERN_ERR "%s: can't get irq gpio 1, err %d\n",
+                        driver_name, retval);
+                return -EBUSY;
+        }
 	dev->got_irq = 1;
 
 #ifdef CONFIG_ARCH_LUBBOCK
@@ -2569,7 +2602,7 @@ static void pxa2xx_udc_shutdown(struct p
 	pullup_off();
 }
 
-static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
+static int pxa2xx_udc_remove(struct platform_device *pdev)
 {
 	struct pxa2xx_udc *dev = platform_get_drvdata(pdev);
 
@@ -2579,6 +2612,7 @@ static int __exit pxa2xx_udc_remove(stru
 
 	if (dev->got_irq) {
 		free_irq(IRQ_USB, dev);
+		free_irq(IRQ_GPIO(1), dev);
 		dev->got_irq = 0;
 	}
 #ifdef CONFIG_ARCH_LUBBOCK
diff -uprN -X dontdiff linux-2.6.18-vanilla/drivers/video/pxafb.c linux-2.6.18/drivers/video/pxafb.c
--- linux-2.6.18-vanilla/drivers/video/pxafb.c	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18/drivers/video/pxafb.c	2006-10-08 22:19:13.000000000 +0200
@@ -166,6 +166,9 @@ pxafb_setcolreg(u_int regno, u_int red, 
 			pal[regno] = val;
 			ret = 0;
 		}
+		else {
+			printk("Tried to set invalid colreg\n");
+		}
 		break;
 
 	case FB_VISUAL_STATIC_PSEUDOCOLOR:
@@ -408,6 +411,21 @@ static int pxafb_mmap(struct fb_info *in
 	return -EINVAL;
 }
 
+static int pxafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	struct pxafb_info *fbi = (struct pxafb_info *)info;
+        unsigned long flags;
+
+	if (var->xoffset != 0)
+		return -EINVAL;
+
+	fbi->dmadesc_fbhigh_cpu->fsadr = fbi->screen_dma+var->yoffset*fbi->fb.fix.line_length;
+
+	DFBR0 = fbi->fdadr0 | 1;
+
+	return 0;
+}
+
 static struct fb_ops pxafb_ops = {
 	.owner		= THIS_MODULE,
 	.fb_check_var	= pxafb_check_var,
@@ -418,6 +436,7 @@ static struct fb_ops pxafb_ops = {
 	.fb_imageblit	= cfb_imageblit,
 	.fb_blank	= pxafb_blank,
 	.fb_mmap	= pxafb_mmap,
+	.fb_pan_display	= pxafb_pan_display,
 };
 
 /*
@@ -757,6 +776,8 @@ static void pxafb_enable_controller(stru
 
 	FDADR0 = fbi->fdadr0;
 	FDADR1 = fbi->fdadr1;
+	DFBR0 = fbi->fdadr0 | 1;
+
 	LCCR0 |= LCCR0_ENB;
 
 	pr_debug("FDADR0 0x%08x\n", (unsigned int) FDADR0);
@@ -1023,6 +1044,8 @@ static int __init pxafb_map_video_memory
 		/* prevent initial garbage on screen */
 		memset(fbi->map_cpu, 0, fbi->map_size);
 		fbi->fb.screen_base = fbi->map_cpu + PAGE_SIZE;
+		fbi->fb.screen_size = fbi->map_size;
+
 		fbi->screen_dma = fbi->map_dma + PAGE_SIZE;
 		/*
 		 * FIXME: this is actually the wrong thing to place in
@@ -1063,14 +1086,14 @@ static struct pxafb_info * __init pxafb_
 	fbi->fb.fix.type	= FB_TYPE_PACKED_PIXELS;
 	fbi->fb.fix.type_aux	= 0;
 	fbi->fb.fix.xpanstep	= 0;
-	fbi->fb.fix.ypanstep	= 0;
+	fbi->fb.fix.ypanstep	= 1;
 	fbi->fb.fix.ywrapstep	= 0;
 	fbi->fb.fix.accel	= FB_ACCEL_NONE;
 
 	fbi->fb.var.nonstd	= 0;
 	fbi->fb.var.activate	= FB_ACTIVATE_NOW;
-	fbi->fb.var.height	= -1;
-	fbi->fb.var.width	= -1;
+	fbi->fb.var.height	= 71;
+	fbi->fb.var.width	= 53;
 	fbi->fb.var.accel_flags	= 0;
 	fbi->fb.var.vmode	= FB_VMODE_NONINTERLACED;
 
@@ -1087,7 +1110,7 @@ static struct pxafb_info * __init pxafb_
 	fbi->fb.var.xres_virtual	= inf->xres;
 	fbi->max_yres			= inf->yres;
 	fbi->fb.var.yres		= inf->yres;
-	fbi->fb.var.yres_virtual	= inf->yres;
+	fbi->fb.var.yres_virtual	= 2 * inf->yres;
 	fbi->max_bpp			= inf->bpp;
 	fbi->fb.var.bits_per_pixel	= inf->bpp;
 	fbi->fb.var.pixclock		= inf->pixclock;
@@ -1105,7 +1128,7 @@ static struct pxafb_info * __init pxafb_
 	fbi->lccr3			= inf->lccr3;
 	fbi->state			= C_STARTUP;
 	fbi->task_state			= (u_char)-1;
-	fbi->fb.fix.smem_len		= fbi->max_xres * fbi->max_yres *
+	fbi->fb.fix.smem_len		= 2 * fbi->max_xres * fbi->max_yres *
 					  fbi->max_bpp / 8;
 
 	init_waitqueue_head(&fbi->ctrlr_wait);
diff -uprN -X dontdiff linux-2.6.18-vanilla/include/linux/utsrelease.h linux-2.6.18/include/linux/utsrelease.h
--- linux-2.6.18-vanilla/include/linux/utsrelease.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.18/include/linux/utsrelease.h	2006-09-30 09:11:35.000000000 +0200
@@ -0,0 +1 @@
+#define UTS_RELEASE "2.6.18"
diff -uprN -X dontdiff linux-2.6.18-vanilla/Makefile linux-2.6.18/Makefile
--- linux-2.6.18-vanilla/Makefile	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18/Makefile	2006-09-30 11:51:23.000000000 +0200
@@ -173,8 +173,8 @@ SUBARCH := $(shell uname -m | sed -e s/i
 # Default value for CROSS_COMPILE is not to prefix executables
 # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile
 
-ARCH		?= $(SUBARCH)
-CROSS_COMPILE	?=
+ARCH		?= arm
+CROSS_COMPILE	?= /home/rohde/develop/xscale/bin/arm-linux-
 
 # Architecture as present in compile.h
 UTS_MACHINE := $(ARCH)
diff -uprN -X dontdiff linux-2.6.18-vanilla/sound/arm/icn330_audio.c linux-2.6.18/sound/arm/icn330_audio.c
--- linux-2.6.18-vanilla/sound/arm/icn330_audio.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.18/sound/arm/icn330_audio.c	2006-10-13 22:54:50.000000000 +0200
@@ -0,0 +1,77 @@
+/*
+ * Audio support for Navman iCN 330
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <asm/hardware.h>
+#include <linux/pm.h>
+#include <asm/mach-types.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <asm/arch/pxa-regs.h>
+
+#include "pxa2xx-i2sound.h"
+
+
+static int snd_icn330_audio_open_stream(int stream)
+{
+	return 0;
+}
+
+static void snd_icn330_audio_close_stream(int stream)
+{
+}
+
+static int snd_icn330_audio_add_mixer_controls(struct snd_card *acard)
+{
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int snd_icn330_audio_suspend(pm_message_t state)
+{
+	return 0;
+}
+
+static int snd_icn330_audio_resume(void)
+{
+	return 0;
+}
+#endif
+
+static struct snd_pxa2xx_i2sound_board icn330_audio = {
+	.name			= "iCN330 Audio",
+	.desc			= "Navman iCN330 Audio",
+	.acard_id		= "iCN330 Audio",
+	.info			= SND_PXA2xx_I2SOUND_INFO_CLOCK_FROM_PXA,
+	.open_stream		= snd_icn330_audio_open_stream,
+	.close_stream		= snd_icn330_audio_close_stream,
+	.add_mixer_controls	= snd_icn330_audio_add_mixer_controls,
+#ifdef CONFIG_PM
+	.suspend		= snd_icn330_audio_suspend,
+	.resume			= snd_icn330_audio_resume
+#endif
+};
+
+static int __init snd_icn330_audio_init(void)
+{
+
+	return snd_pxa2xx_i2sound_card_activate(&icn330_audio);
+}
+
+static void __exit snd_icn330_audio_exit(void)
+{
+	snd_pxa2xx_i2sound_card_deactivate();
+}
+
+module_init(snd_icn330_audio_init);
+module_exit(snd_icn330_audio_exit);
+
+MODULE_AUTHOR("Rasmus Rohde");
+MODULE_DESCRIPTION("Audio support for Navman iCN 330");
+MODULE_LICENSE("GPL");
diff -uprN -X dontdiff linux-2.6.18-vanilla/sound/arm/Kconfig linux-2.6.18/sound/arm/Kconfig
--- linux-2.6.18-vanilla/sound/arm/Kconfig	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18/sound/arm/Kconfig	2006-10-13 22:48:54.000000000 +0200
@@ -24,6 +24,23 @@ config SND_PXA2XX_PCM
 	tristate
 	select SND_PCM
 
+config SND_PXA2xx_I2SOUND
+        tristate "I2S driver for the Intel PXA2xx chip"
+	depends on ARCH_PXA && SND
+        select SND_PCM
+	help
+         Say Y or M if you want to support an audio codec attached to
+         the PXA2xx I2S interface.
+
+config SND_PXA2xx_ICN330
+        tristate "Sound driver for the Navman iCN 330"
+	depends on ARCH_PXA && SND
+        select SND_PCM
+	select SND_PXA2xx_I2SOUND
+	help
+         Say Y or M if you want to have audio support on your Navman iCN 330.
+
+
 config SND_PXA2XX_AC97
 	tristate "AC97 driver for the Intel PXA2xx chip"
 	depends on ARCH_PXA && SND
diff -uprN -X dontdiff linux-2.6.18-vanilla/sound/arm/Makefile linux-2.6.18/sound/arm/Makefile
--- linux-2.6.18-vanilla/sound/arm/Makefile	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18/sound/arm/Makefile	2006-10-13 22:50:16.000000000 +0200
@@ -13,3 +13,9 @@ snd-pxa2xx-pcm-objs		:= pxa2xx-pcm.o
 
 obj-$(CONFIG_SND_PXA2XX_AC97)	+= snd-pxa2xx-ac97.o
 snd-pxa2xx-ac97-objs		:= pxa2xx-ac97.o
+
+obj-$(CONFIG_SND_PXA2xx_I2SOUND) += snd-pxa2xx-i2sound.o
+snd-pxa2xx-i2sound-objs          := pxa2xx-i2sound.o
+
+obj-$(CONFIG_SND_PXA2xx_ICN330)  += snd-icn330_audio.o
+snd-icn330_audio-objs           := icn330_audio.o
diff -uprN -X dontdiff linux-2.6.18-vanilla/sound/arm/pxa2xx-i2sound.c linux-2.6.18/sound/arm/pxa2xx-i2sound.c
--- linux-2.6.18-vanilla/sound/arm/pxa2xx-i2sound.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.18/sound/arm/pxa2xx-i2sound.c	2006-10-13 22:21:10.000000000 +0200
@@ -0,0 +1,952 @@
+/*
+ * PXA2xx i2Sound: support for Intel PXA2xx I2S audio
+ *
+ * Copyright (c) 2004,2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
+ *
+ * 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.
+ *
+ * History:
+ *
+ * 2005-03	Initial release; Audio engine ((c) 2004) derived from pxa-uda1380.c
+ *		-- Giorgio Padrin
+ * 2005-06	Power management -- Giorgio Padrin
+ * 2005-10	Some finishing -- Giorgio Padrin
+ */
+
+#include <sound/driver.h>
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/errno.h>
+#include <linux/kmod.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+
+#include <asm/semaphore.h>
+#include <asm/hardware.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+
+#include <asm/arch/pxa-regs.h>
+
+#include "pxa2xx-i2sound.h"
+#include <sound/pcm_params.h>
+
+struct snd_pxa2xx_i2sound_engine snd_pxa2xx_i2sound_engine;
+
+struct snd_pxa2xx_i2sound_stream {
+	int id;				/* SNDRV_PCM_STREAM_(PLAYBACK|CAPTURE) */
+
+	struct snd_pxa2xx_i2sound_engine *engine;
+
+	int dma_ch;			/* DMA channel */
+
+	dma_addr_t buffer;		/* buffer (physical address) */
+	size_t buffer_size;
+
+	size_t period_size;
+
+	pxa_dma_desc *dma_dring;	/* DMA descriptors ring */
+	char *dma_dring_store;		/* storage space for DMA descriptors ring */
+	dma_addr_t dma_dring_p;		/* (physical address) */
+	dma_addr_t dma_dring_store_p;
+	size_t dma_dring_size;
+
+	volatile int dma_running;	/* DMA running? */
+
+  	struct snd_pcm_substream *asubs;	/* attached ALSA substream */
+
+	struct snd_pcm_hardware ahw;		/* ALSA hw description */
+};
+
+struct snd_pxa2xx_i2sound_engine {
+	unsigned int rate;			/* sample rate */
+	unsigned int rate_in_use;
+	spinlock_t rate_lock;
+
+	struct snd_pxa2xx_i2sound_stream streams[2];
+};
+
+struct snd_pxa2xx_i2sound_i2slink {
+	unsigned int usage_count;
+	u32 sadiv;
+	spinlock_t lock;
+	int suspended;
+};
+
+struct snd_pxa2xx_i2sound_card {
+	const char *name;
+
+	struct snd_pxa2xx_i2sound_engine engine;
+
+	struct snd_pxa2xx_i2sound_i2slink i2slink;
+
+	struct snd_pxa2xx_i2sound_board *board;
+
+	struct snd_card *acard;		/* ALSA card */
+	struct snd_pcm *apcm;		/* ALSA pcm */
+
+	struct platform_driver pdriver;
+	struct platform_device pdev;
+};
+
+static struct snd_pxa2xx_i2sound_card card;
+
+#define flag_stream(s)		(1 << (s)->id)
+
+#define info_clock_from_pxa	((card.board->info & SND_PXA2xx_I2SOUND_INFO_CLOCK_FROM_PXA) ? 1 : 0)
+#define info_can_capture	((card.board->info & SND_PXA2xx_I2SOUND_INFO_CAN_CAPTURE) ? 1 : 0)
+#define info_half_duplex	((card.board->info & SND_PXA2xx_I2SOUND_INFO_HALF_DUPLEX) ? 1 : 0)
+
+/* PXA 25x or 27x kind detection
+   PXA21x and PXA26x belong to the PXA 25x kind  */
+static inline int pxa_25x_kind(void)
+{
+	return ((read_cpuid(CPUID_ID) & 0x3f0) != 0x110);
+}
+
+
+/* begin {{ Audio Engine }} */
+
+#define MAX_DMA_XFER_SIZE	4096
+#define MAX_BUFFER_SIZE		65536
+
+static int snd_pxa2xx_i2sound_ngn_init(void)
+{
+	spin_lock_init(&card.engine.rate_lock);
+
+	/* preallocate buffer */
+	return snd_pcm_lib_preallocate_pages_for_all(card.apcm, SNDRV_DMA_TYPE_DEV, NULL,
+						     MAX_BUFFER_SIZE,
+						     info_can_capture ? MAX_BUFFER_SIZE : 0);
+}
+
+static void snd_pxa2xx_i2sound_ngn_free(void)
+{
+	/* ALSA takes care of deallocate preallocated buffer */
+}
+
+/* Constraint: buffer_size integer multiple of period_size
+* A better way to assure it?
+*/
+static unsigned int period_sizes[]
+		= { 512, 1024, 2048, 4096, 8192, 16384, 32768 };
+static unsigned int buffer_sizes[]
+		= { 1024, 2048, 4096, 8192, 16384, 32768, 65536 };
+static struct snd_pcm_hw_constraint_list cnstr_period_sizes = {
+        	.count = ARRAY_SIZE(period_sizes),
+        	.list = period_sizes,
+	};
+static struct snd_pcm_hw_constraint_list cnstr_buffer_sizes = {
+        	.count = ARRAY_SIZE(buffer_sizes),
+        	.list = buffer_sizes,
+	};
+
+static struct snd_pcm_hw_constraint_list cnstr_rates;
+
+static void snd_pxa2xx_i2sound_ngn_free_buffer(struct snd_pxa2xx_i2sound_stream *s);
+
+static int snd_pxa2xx_i2sound_ngn_init_buffer(struct snd_pxa2xx_i2sound_stream *s,
+					      size_t buffer_size, size_t period_size)
+{
+	int ret;
+
+	unsigned int dma_dpp; /* dma descriptors per period */
+	unsigned int periods;
+	unsigned int i, j, di;
+	dma_addr_t p, d_buf;
+	size_t d_size = sizeof(pxa_dma_desc);
+
+	size_t dma_xfer_size = (period_size >= MAX_DMA_XFER_SIZE) ?
+			       MAX_DMA_XFER_SIZE : period_size;
+	dma_dpp = (period_size - 1) / dma_xfer_size + 1;
+	periods = buffer_size / period_size;
+
+	/* free old buffer in case */
+	if (s->buffer)
+		snd_pxa2xx_i2sound_ngn_free_buffer(s);
+
+	/* allocate buffer */
+	ret = snd_pcm_lib_malloc_pages(s->asubs, buffer_size);
+	if (ret < 0) return ret;
+
+	s->buffer = s->asubs->runtime->dma_addr;
+
+	/* ---- allocate and setup DMA descriptors ring ---- */
+
+	/* allocate storage space for descriptors ring (alignment issue) */
+	s->dma_dring_size = periods * dma_dpp * d_size;
+	s->dma_dring_store = dma_alloc_coherent(NULL, s->dma_dring_size + 15,
+		 				&s->dma_dring_store_p, GFP_KERNEL);
+	if (!s->dma_dring_store) {
+		s->dma_dring_size = 0;
+		snd_pxa2xx_i2sound_ngn_free_buffer(s);
+		return -ENOMEM;
+	}
+
+	/* setup the descriptors ring pointer, aligned on a 16 bytes boundary */
+	s->dma_dring = ((dma_addr_t) s->dma_dring_store & 0xf) ?
+			 (pxa_dma_desc *) (((dma_addr_t) s->dma_dring_store & ~0xf) + 16) :
+			 (pxa_dma_desc *) s->dma_dring_store;
+	s->dma_dring_p = s->dma_dring_store_p +
+			  (dma_addr_t) s->dma_dring - (dma_addr_t) s->dma_dring_store;
+
+	/* fill the descriptors ring */
+	di = 0; /* current descriptor index */
+	p = s->buffer; /* p: current period (address in buffer) */
+	 /* iterate over periods */
+	for (i = 0; i < periods; i++, p += period_size) {
+		d_buf = p; /* d_buf: address in buffer for current dma descriptor */
+		/* iterate over dma descriptors in a period */
+		for (j = 0; j < dma_dpp; j++, di++, d_buf += dma_xfer_size) {
+			/* link to next descriptor */
+			s->dma_dring[di].ddadr = s->dma_dring_p + (di + 1) * d_size;
+			if (s->id == SNDRV_PCM_STREAM_PLAYBACK) {
+				s->dma_dring[di].dsadr = d_buf;
+				s->dma_dring[di].dtadr = __PREG(SADR);
+				s->dma_dring[di].dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+							DCMD_BURST32 | DCMD_WIDTH4;
+			}
+			else {
+				s->dma_dring[di].dsadr = __PREG(SADR);
+				s->dma_dring[di].dtadr = d_buf;
+				s->dma_dring[di].dcmd =	DCMD_INCTRGADDR | DCMD_FLOWSRC |
+							DCMD_BURST32 | DCMD_WIDTH4;
+			}
+			s->dma_dring[di].dcmd |=
+			  ((p + period_size - d_buf) >= dma_xfer_size) ?
+			  dma_xfer_size : p + period_size - d_buf; /* transfer length */
+		}
+		s->dma_dring[di - 1].dcmd |= DCMD_ENDIRQEN; /* period irq */
+	}
+	s->dma_dring[di - 1].ddadr = s->dma_dring_p; /* close the ring */
+
+	return 0;
+}
+
+static void snd_pxa2xx_i2sound_ngn_free_buffer(struct snd_pxa2xx_i2sound_stream *s)
+{
+	if (s->dma_dring_store) {
+		dma_free_coherent(NULL, s->dma_dring_size + 15,
+				  s->dma_dring_store, s->dma_dring_store_p);
+		s->dma_dring_store = NULL;
+		s->dma_dring = NULL;
+		s->dma_dring_store_p = 0;
+		s->dma_dring_p = 0;
+		s->dma_dring_size = 0;
+	}
+	if (s->buffer) {
+		snd_pcm_lib_free_pages(s->asubs);
+		s->buffer = 0;
+	}
+}
+
+static int snd_pxa2xx_i2sound_ngn_set_rate(unsigned int rate);
+
+static int snd_pxa2xx_i2sound_ngn_request_rate(struct snd_pxa2xx_i2sound_stream *s,
+					       unsigned int rate)
+{
+	spin_lock(&card.engine.rate_lock);
+
+	card.engine.rate_in_use &= ~flag_stream(s);
+	if (rate != card.engine.rate) {
+		if (!card.engine.rate_in_use) {
+			if (snd_pxa2xx_i2sound_ngn_set_rate(rate) == 0)
+				card.engine.rate = rate;
+			else goto error;
+		}
+		else goto error;
+	}
+	card.engine.rate_in_use |= flag_stream(s);
+	spin_unlock(&card.engine.rate_lock);
+	return 0;
+
+ error:	spin_unlock(&card.engine.rate_lock);
+	return -EINVAL;
+}
+
+static void snd_pxa2xx_i2sound_ngn_free_rate(struct snd_pxa2xx_i2sound_stream *s)
+{
+	spin_lock(&card.engine.rate_lock);
+	card.engine.rate_in_use &= ~flag_stream(s);
+	spin_unlock(&card.engine.rate_lock);
+}
+
+static int snd_pxa2xx_i2sound_i2slink_set_rate_pxa(unsigned int rate);
+
+static int snd_pxa2xx_i2sound_ngn_set_rate(unsigned int rate)
+{
+	if (info_clock_from_pxa)
+		if (snd_pxa2xx_i2sound_i2slink_set_rate_pxa(rate) != 0)
+			return -EINVAL;
+	if (card.board->set_rate != NULL)
+		if (card.board->set_rate(rate) != 0)
+			return -EINVAL;
+	return 0;
+}
+
+static void snd_pxa2xx_i2sound_ngn_dma_irq(int ch, void *data, struct pt_regs *regs)
+{
+	struct snd_pxa2xx_i2sound_stream *s = (struct snd_pxa2xx_i2sound_stream*) data;
+	u32 dcsr;
+
+	dcsr = DCSR(ch);
+	DCSR(ch) = dcsr & ~DCSR_STOPIRQEN;
+
+	if (dcsr & DCSR_BUSERR) {
+		snd_printk(KERN_ERR "%s: bus error\n", __FUNCTION__);
+		return;
+	}
+
+	if (dcsr & DCSR_ENDINTR)
+		return snd_pcm_period_elapsed(s->asubs);
+}
+
+static inline void snd_pxa2xx_i2sound_ngn_map_dma_reqs(struct snd_pxa2xx_i2sound_stream *s)
+{
+	if (s->id == SNDRV_PCM_STREAM_PLAYBACK)
+		DRCMRTXSADR = s->dma_ch | DRCMR_MAPVLD;
+	else
+		DRCMRRXSADR = s->dma_ch | DRCMR_MAPVLD;
+}
+
+static inline void snd_pxa2xx_i2sound_ngn_unmap_dma_reqs(struct snd_pxa2xx_i2sound_stream *s)
+{
+	if (s->id == SNDRV_PCM_STREAM_PLAYBACK)
+		DRCMRTXSADR = 0;
+	else
+		DRCMRRXSADR = 0;
+}
+
+static int snd_pxa2xx_i2sound_ngn_request_dma_channel(struct snd_pxa2xx_i2sound_stream *s)
+{
+	int ret;
+	char *name = (s->id == SNDRV_PCM_STREAM_PLAYBACK) ?
+		     "i2s out" :
+		     "i2s in";
+
+	ret = pxa_request_dma(name, DMA_PRIO_LOW, snd_pxa2xx_i2sound_ngn_dma_irq, s);
+	if (ret < 0) return ret;
+	s->dma_ch = ret;
+	snd_pxa2xx_i2sound_ngn_map_dma_reqs(s);
+	return 0;
+}
+
+static void snd_pxa2xx_i2sound_ngn_free_dma_channel(struct snd_pxa2xx_i2sound_stream *s)
+{
+	snd_pxa2xx_i2sound_ngn_unmap_dma_reqs(s);
+	pxa_free_dma(s->dma_ch);
+}
+
+static int snd_pxa2xx_i2sound_ngn_attach_asubs(struct snd_pcm_substream *asubs)
+{
+	struct snd_pxa2xx_i2sound_stream *s = &card.engine.streams[asubs->stream];
+	int ret;
+
+	asubs->private_data = (void*) s;
+	s->asubs = asubs;
+
+	if ((ret = snd_pxa2xx_i2sound_ngn_request_dma_channel(s)) < 0) return ret;
+
+	/* ALSA hw description */
+	asubs->runtime->hw = s->ahw;
+
+	/* ---- constraints on hw params space ---- */
+
+	snd_pcm_hw_constraint_list(asubs->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+				   		      &cnstr_period_sizes);
+	snd_pcm_hw_constraint_list(asubs->runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+						      &cnstr_buffer_sizes);
+	if (!info_clock_from_pxa && card.board->streams_hw[s->id].rates_table != NULL) {
+		/* rates constraint */
+		cnstr_rates.count = card.board->streams_hw[s->id].rates_num;
+        	cnstr_rates.list = card.board->streams_hw[s->id].rates_table;
+		snd_pcm_hw_constraint_list(asubs->runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				   		      &cnstr_rates);
+	}
+
+	/* more board specific constraints? */
+	if (card.board->add_hw_constraints)
+		card.board->add_hw_constraints(asubs);
+
+	return 0;
+}
+
+static void snd_pxa2xx_i2sound_ngn_detach_asubs(struct snd_pcm_substream *asubs)
+{
+	struct snd_pxa2xx_i2sound_stream *s = &card.engine.streams[asubs->stream];
+
+	snd_pxa2xx_i2sound_ngn_free_dma_channel(s);
+
+	asubs->private_data = NULL;
+	s->asubs = NULL;
+}
+
+static int snd_pxa2xx_i2sound_ngn_on(struct snd_pxa2xx_i2sound_stream *s, unsigned int rate,
+				  size_t buffer_size, size_t period_size)
+{
+	int ret;
+
+	/* request sample rate */
+	ret = snd_pxa2xx_i2sound_ngn_request_rate(s, rate);
+	if (ret) return ret;
+
+	/* init buffer */
+	ret = snd_pxa2xx_i2sound_ngn_init_buffer(s, buffer_size, period_size);
+	if (ret) {
+		snd_pxa2xx_i2sound_ngn_free_rate(s);
+		return ret;
+	}
+
+	s->buffer_size = buffer_size;
+	s->period_size = period_size;
+
+	return 0;
+}
+
+static void snd_pxa2xx_i2sound_ngn_off(struct snd_pxa2xx_i2sound_stream *s)
+{
+	snd_pxa2xx_i2sound_ngn_free_rate(s);
+	snd_pxa2xx_i2sound_ngn_free_buffer(s);
+	s->buffer_size = 0;
+	s->period_size = 0;
+}
+
+static int snd_pxa2xx_i2sound_ngn_start(struct snd_pxa2xx_i2sound_stream *s)
+{
+	DDADR(s->dma_ch) = s->dma_dring_p;
+	DCSR(s->dma_ch) = DCSR_RUN;
+	s->dma_running = 1;
+	return 0;
+}
+
+static int snd_pxa2xx_i2sound_ngn_stop(struct snd_pxa2xx_i2sound_stream *s)
+{
+	unsigned int timeout = 100; /* safety timeout */
+
+	DCSR(s->dma_ch) = DCSR_STOPIRQEN;
+
+	while (!(DCSR(s->dma_ch) & DCSR_STOPSTATE) && timeout > 0) {
+		 udelay(10);
+		 timeout--;
+	}
+
+	s->dma_running = 0;
+	return 0;
+}
+
+static u32 snd_pxa2xx_i2sound_ngn_pointer(struct snd_pxa2xx_i2sound_stream *s)
+{
+	return ((s->id == SNDRV_PCM_STREAM_PLAYBACK) ?
+	      DSADR(s->dma_ch) : DTADR(s->dma_ch))
+	      - s->buffer;
+}
+
+#ifdef CONFIG_PM
+static int snd_pxa2xx_i2sound_ngn_suspend(struct snd_pxa2xx_i2sound_stream *s)
+{
+	if (s->asubs) snd_pxa2xx_i2sound_ngn_unmap_dma_reqs(s);
+	return 0;
+}
+
+static int snd_pxa2xx_i2sound_ngn_resume(struct snd_pxa2xx_i2sound_stream *s)
+{
+	if (s->asubs) snd_pxa2xx_i2sound_ngn_map_dma_reqs(s);
+	return 0;
+}
+#endif
+
+/* end {{ Audio Engine }} */
+
+
+/* begin {{ I2SLink }} */
+
+static void snd_pxa2xx_i2sound_i2slink_init(void)
+{
+	spin_lock_init(&card.i2slink.lock);
+	card.i2slink.sadiv = 0xd; /* rate 44.1 kHz */;
+}
+
+static void snd_pxa2xx_i2sound_i2slink_on(void)
+{
+	if (info_clock_from_pxa) {
+		pxa_gpio_mode(GPIO28_BITCLK_OUT_I2S_MD);
+		if (pxa_25x_kind()) pxa_gpio_mode(GPIO32_SYSCLK_I2S_MD);
+		else pxa_gpio_mode(GPIO113_I2S_SYSCLK_MD);
+	}
+	else {
+		pxa_gpio_mode(GPIO28_BITCLK_IN_I2S_MD);
+	}
+	pxa_gpio_mode(GPIO30_SDATA_OUT_I2S_MD);
+	if (info_can_capture) pxa_gpio_mode(GPIO29_SDATA_IN_I2S_MD);
+	pxa_gpio_mode(GPIO31_SYNC_I2S_MD);
+
+	pxa_set_cken(CKEN8_I2S, 1);
+	SACR0 = SACR0_RST;
+	if (info_clock_from_pxa) {
+		SADIV = card.i2slink.sadiv; /* rate 44.1 kHz */
+		SACR0 = SACR0_ENB | SACR0_BCKD | SACR0_RFTH(8) | SACR0_TFTH(8);
+	}
+	else {
+		SACR0 = SACR0_ENB | SACR0_RFTH(8) | SACR0_TFTH(8);
+	}
+}
+
+static void snd_pxa2xx_i2sound_i2slink_off(void)
+{
+	SACR0 = SACR0_RST;
+	pxa_set_cken(CKEN8_I2S, 0);
+
+	if (info_clock_from_pxa) {
+		pxa_gpio_mode(GPIO28_BITCLK | GPIO_OUT | GPIO_DFLT_LOW);
+		if (pxa_25x_kind()) pxa_gpio_mode(GPIO32_SYSCLK | GPIO_OUT | GPIO_DFLT_LOW);
+		else pxa_gpio_mode(GPIO113_I2S_SYSCLK | GPIO_OUT | GPIO_DFLT_LOW);
+	}
+	else {
+		pxa_gpio_mode(GPIO28_BITCLK | GPIO_IN);
+	}
+	pxa_gpio_mode(GPIO30_SDATA_OUT | GPIO_OUT | GPIO_DFLT_LOW);
+	if (info_can_capture) pxa_gpio_mode(GPIO29_SDATA_IN | GPIO_IN);
+	pxa_gpio_mode(GPIO31_SYNC | GPIO_OUT | GPIO_DFLT_LOW);
+}
+
+void snd_pxa2xx_i2sound_i2slink_get(void)
+{
+	spin_lock(&card.i2slink.lock);
+	if (card.i2slink.usage_count++ == 0 &&
+	    !card.i2slink.suspended)
+		snd_pxa2xx_i2sound_i2slink_on();
+	spin_unlock(&card.i2slink.lock);
+}
+
+void snd_pxa2xx_i2sound_i2slink_free(void)
+{
+	spin_lock(&card.i2slink.lock);
+	if (--card.i2slink.usage_count == 0 &&
+	    !card.i2slink.suspended)
+		snd_pxa2xx_i2sound_i2slink_off();
+	spin_unlock(&card.i2slink.lock);
+}
+
+#ifdef CONFIG_PM
+void snd_pxa2xx_i2sound_i2slink_suspend(void)
+{
+	spin_lock(&card.i2slink.lock);
+	card.i2slink.suspended = 1;
+	if (card.i2slink.usage_count == 0)
+		snd_pxa2xx_i2sound_i2slink_off();
+	spin_unlock(&card.i2slink.lock);
+}
+
+void snd_pxa2xx_i2sound_i2slink_resume(void)
+{
+	spin_lock(&card.i2slink.lock);
+	if (card.i2slink.usage_count > 0)
+		snd_pxa2xx_i2sound_i2slink_on();
+	card.i2slink.suspended = 0;
+	spin_unlock(&card.i2slink.lock);
+}
+#endif
+
+static int snd_pxa2xx_i2sound_i2slink_set_rate_pxa(unsigned int rate)
+{
+	switch (rate) {
+	case 8000: case 11025: case 16000: case 22050: case 44100: case 48000:
+		spin_lock(&card.i2slink.lock);
+		SADIV = card.i2slink.sadiv = 576000 / rate;
+		spin_unlock(&card.i2slink.lock);
+		return 0;
+	default: return -EINVAL;
+	}
+}
+
+/* end {{ I2SLink }} */
+
+
+/* begin {{ ALSA PCM Ops }} */
+
+static int snd_pxa2xx_i2sound_pcm_hw_params(struct snd_pcm_substream *asubs,
+					    struct snd_pcm_hw_params * ahw_params)
+{
+	return snd_pxa2xx_i2sound_ngn_on((struct snd_pxa2xx_i2sound_stream*) asubs->private_data,
+					 params_rate(ahw_params),
+					 params_buffer_bytes(ahw_params),
+					 params_period_bytes(ahw_params));
+}
+
+static int snd_pxa2xx_i2sound_pcm_hw_free(struct snd_pcm_substream *asubs)
+{
+	snd_pxa2xx_i2sound_ngn_off((struct snd_pxa2xx_i2sound_stream*) asubs->private_data);
+	return 0;
+}
+
+static int snd_pxa2xx_i2sound_pcm_prepare(struct snd_pcm_substream *asubs)
+{
+	return 0;
+}
+
+static int snd_pxa2xx_i2sound_pcm_trigger(struct snd_pcm_substream *asubs, int cmd)
+{
+	struct snd_pxa2xx_i2sound_stream* s =
+		(struct snd_pxa2xx_i2sound_stream*) asubs->private_data;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		snd_pxa2xx_i2sound_ngn_start(s);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		snd_pxa2xx_i2sound_ngn_stop(s);
+		break;
+	default:
+		return -EINVAL;
+		break;
+	}
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_pxa2xx_i2sound_pcm_pointer(struct snd_pcm_substream *asubs)
+{
+	struct snd_pxa2xx_i2sound_stream* s = 
+		(struct snd_pxa2xx_i2sound_stream*) asubs->private_data;
+	return bytes_to_frames(asubs->runtime,
+			       snd_pxa2xx_i2sound_ngn_pointer(s));
+}
+
+static int snd_pxa2xx_i2sound_pcm_open(struct snd_pcm_substream *asubs)
+{
+	int ret;
+	ret = snd_pxa2xx_i2sound_ngn_attach_asubs(asubs);
+	if (ret == 0) {
+		snd_pxa2xx_i2sound_i2slink_get();
+		card.board->open_stream(asubs->stream);
+	}
+	return ret;
+}
+
+static int snd_pxa2xx_i2sound_pcm_close(struct snd_pcm_substream *asubs)
+{
+	card.board->close_stream(asubs->stream);
+	snd_pxa2xx_i2sound_i2slink_free();
+	snd_pxa2xx_i2sound_ngn_detach_asubs(asubs);
+	return 0;
+}
+
+static struct snd_pcm_ops snd_pxa2xx_i2sound_pcm_ops = {
+	.open		= snd_pxa2xx_i2sound_pcm_open,
+	.close		= snd_pxa2xx_i2sound_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_pxa2xx_i2sound_pcm_hw_params,
+	.hw_free	= snd_pxa2xx_i2sound_pcm_hw_free,
+	.prepare	= snd_pxa2xx_i2sound_pcm_prepare,
+	.trigger	= snd_pxa2xx_i2sound_pcm_trigger,
+	.pointer	= snd_pxa2xx_i2sound_pcm_pointer
+};
+
+/* end {{ ALSA PCM Ops }} */
+
+
+/* begin {{ Platform Device }} */
+
+#ifdef CONFIG_PM
+static int snd_pxa2xx_i2sound_card_suspend(struct snd_card *acard, pm_message_t state);
+static int snd_pxa2xx_i2sound_card_resume(struct snd_card *acard);
+
+static int snd_pxa2xx_i2sound_pdev_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	return snd_pxa2xx_i2sound_card_suspend(card.acard, state);
+}
+
+static int snd_pxa2xx_i2sound_pdev_resume(struct platform_device *pdev)
+{
+	return snd_pxa2xx_i2sound_card_resume(card.acard);
+}
+#endif
+
+static void snd_pxa2xx_i2sound_pdev_device_release(struct device *dev) {}
+
+static int snd_pxa2xx_i2sound_pdev_register(void)
+{
+	int ret = 0;
+
+	/* platform driver */
+	card.pdriver.driver.name = card.name;
+#ifdef CONFIG_PM
+	card.pdriver.suspend = snd_pxa2xx_i2sound_pdev_suspend;
+	card.pdriver.resume = snd_pxa2xx_i2sound_pdev_resume;
+#endif
+	if ((ret = platform_driver_register(&card.pdriver))) return ret;
+
+	/* platform device */
+	card.pdev.name = card.name;
+	card.pdev.id = -1;
+	card.pdev.dev.release = snd_pxa2xx_i2sound_pdev_device_release;
+	if ((ret = platform_device_register(&card.pdev)))
+		platform_driver_unregister(&card.pdriver);
+	return ret;
+}
+
+static void snd_pxa2xx_i2sound_pdev_unregister(void)
+{
+	platform_device_unregister(&card.pdev);
+	platform_driver_unregister(&card.pdriver);
+}
+
+/* end {{ Platform Device }} */
+
+
+/* begin {{ Soundcard }} */
+
+static void snd_pxa2xx_i2sound_card_free_pcm(struct snd_pcm * apcm);
+
+static int snd_pxa2xx_i2sound_card_create_pcm(void)
+{
+	int ret;
+	int s_id;
+	struct snd_pxa2xx_i2sound_stream* s;
+	struct snd_pxa2xx_i2sound_board_stream_hw* board_s_hw;
+
+	ret = snd_pcm_new(card.acard, card.board->acard_id, 0,
+			  1,
+			  (info_can_capture) ? 1 : 0,
+			  &card.apcm);
+	if (ret < 0) return ret;
+
+	ret = snd_pxa2xx_i2sound_ngn_init();
+	if (ret < 0)	return ret;
+
+	card.apcm->info_flags = info_half_duplex ?
+				       SNDRV_PCM_INFO_HALF_DUPLEX :
+				       SNDRV_PCM_INFO_JOINT_DUPLEX;
+	strlcpy(card.apcm->name, card.name, sizeof(card.apcm->name));
+
+	for (s_id = 0; s_id < 2; s_id++) {
+		if (s_id == SNDRV_PCM_STREAM_CAPTURE && !info_can_capture)
+			continue;
+		s = &card.engine.streams[s_id];
+		s->id = s_id;
+		s->ahw.info		= SNDRV_PCM_INFO_INTERLEAVED |
+					  SNDRV_PCM_INFO_BLOCK_TRANSFER;
+		s->ahw.formats		= SNDRV_PCM_FMTBIT_S16_LE;
+		s->ahw.channels_min		= 2;
+		s->ahw.channels_max		= 2;
+		s->ahw.buffer_bytes_max	= MAX_BUFFER_SIZE;
+		s->ahw.period_bytes_min	= 512;
+		s->ahw.period_bytes_max	= MAX_BUFFER_SIZE/2;
+		s->ahw.periods_min	= 2;
+		s->ahw.periods_max	= MAX_BUFFER_SIZE/512;
+		if (!info_clock_from_pxa){
+			board_s_hw = &card.board->streams_hw[s->id];
+			s->ahw.rates 	= board_s_hw->rates;
+			s->ahw.rate_min	= board_s_hw->rate_min;
+			s->ahw.rate_max	= board_s_hw->rate_max;
+		}
+		else {
+			s->ahw.rates = SNDRV_PCM_RATE_8000  | SNDRV_PCM_RATE_16000 |
+				       SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |
+				       SNDRV_PCM_RATE_48000;
+			s->ahw.rate_min	=  8000;
+			s->ahw.rate_max = 48000;
+		}
+		snd_pcm_set_ops(card.apcm, s->id, &snd_pxa2xx_i2sound_pcm_ops);
+	}
+
+	card.apcm->private_free = snd_pxa2xx_i2sound_card_free_pcm;
+
+	return 0;
+}
+
+static void snd_pxa2xx_i2sound_card_free_pcm(struct snd_pcm * apcm)
+{
+	int s_id;
+	struct snd_pxa2xx_i2sound_stream* s;
+
+	for (s_id = 0; s_id < 2; s_id++) {
+		if (s_id == SNDRV_PCM_STREAM_CAPTURE && !info_can_capture)
+			continue;
+		s = &card.engine.streams[s_id];
+		memset(s, 0, sizeof(struct snd_pxa2xx_i2sound_stream));
+	}
+	snd_pxa2xx_i2sound_ngn_free();
+	card.apcm = NULL;
+}
+
+static int snd_pxa2xx_i2sound_board_activate(void)
+{
+	int ret = 0;
+
+	if (card.board->activate) {
+		if (info_clock_from_pxa) snd_pxa2xx_i2sound_i2slink_get();
+		ret = card.board->activate();
+		if (info_clock_from_pxa) snd_pxa2xx_i2sound_i2slink_free();
+	}
+	return ret;
+}
+
+static void snd_pxa2xx_i2sound_board_deactivate(void)
+{
+	if (card.board->deactivate) {
+		if (info_clock_from_pxa) snd_pxa2xx_i2sound_i2slink_get();
+		card.board->deactivate();
+		if (info_clock_from_pxa) snd_pxa2xx_i2sound_i2slink_free();
+	}
+}
+
+#ifdef CONFIG_PM
+static int snd_pxa2xx_i2sound_card_suspend(struct snd_card *acard, pm_message_t state)
+{
+	int s_id;
+	
+	snd_power_change_state(card.acard, SNDRV_CTL_POWER_D3hot);
+
+	snd_pcm_suspend_all(card.apcm);
+
+	for (s_id = 0; s_id < 2; s_id++) {
+		if (s_id == SNDRV_PCM_STREAM_CAPTURE && !info_can_capture)
+			continue;
+		snd_pxa2xx_i2sound_ngn_suspend(&card.engine.streams[s_id]);
+	}
+
+	if (info_clock_from_pxa) snd_pxa2xx_i2sound_i2slink_get();
+	card.board->suspend(state);
+	if (info_clock_from_pxa) snd_pxa2xx_i2sound_i2slink_free();
+
+	snd_pxa2xx_i2sound_i2slink_suspend();
+
+	return 0;
+}
+
+static int snd_pxa2xx_i2sound_card_resume(struct snd_card *acard)
+{
+	int s_id;
+
+	snd_pxa2xx_i2sound_i2slink_resume();
+
+	if (info_clock_from_pxa) snd_pxa2xx_i2sound_i2slink_get();
+	card.board->resume();
+	if (info_clock_from_pxa) snd_pxa2xx_i2sound_i2slink_free();
+
+	for (s_id = 0; s_id < 2; s_id++) {
+		if (s_id == SNDRV_PCM_STREAM_CAPTURE && !info_can_capture)
+			continue;
+		snd_pxa2xx_i2sound_ngn_resume(&card.engine.streams[s_id]);
+	}
+
+	snd_power_change_state(card.acard, SNDRV_CTL_POWER_D0);
+	
+	return 0;
+}
+#endif
+
+int snd_pxa2xx_i2sound_card_activate(struct snd_pxa2xx_i2sound_board *board)
+{
+	int ret = 0;
+
+	if (board == NULL) return -EINVAL;
+	card.board = board;
+	card.name = card.board->name;
+
+	snd_pxa2xx_i2sound_i2slink_init();
+
+	if ((ret = snd_pxa2xx_i2sound_board_activate()) < 0) goto failed_activate_board;
+
+	/* new ALSA card */
+	if ((card.acard = snd_card_new(-1, card.board->acard_id, THIS_MODULE, 0)) == NULL) {
+		card.acard = NULL;
+		ret = -ENOMEM;
+		goto failed_new_acard;
+	}
+
+	strlcpy(card.acard->driver, card.board->acard_id, sizeof(card.acard->driver));
+	strlcpy(card.acard->shortname, card.name, sizeof(card.acard->shortname));
+	strlcpy(card.acard->longname, card.board->desc, sizeof(card.acard->longname));
+
+	/* mixer */
+	if ((ret = board->add_mixer_controls(card.acard)) < 0)
+		goto failed_create_acard;
+
+	/* PCM */
+	if ((ret = snd_pxa2xx_i2sound_card_create_pcm()) < 0) goto failed_create_acard;
+
+	/* register the card */
+        if ((ret = snd_card_register(card.acard)) < 0) goto failed_create_acard;
+
+	if ((ret = snd_pxa2xx_i2sound_pdev_register()) < 0)
+		goto failed_register_pdev;
+
+	snd_printk(KERN_INFO "PXA2xx i2Sound: %s activated\n", card.name);
+
+	return 0;
+
+ failed_register_pdev:
+ failed_create_acard:
+	snd_card_free(card.acard);
+	card.acard = NULL;
+ failed_new_acard:
+	snd_pxa2xx_i2sound_board_deactivate();
+ failed_activate_board:
+	snd_pxa2xx_i2sound_i2slink_free();
+	card.board = NULL;
+	return ret;
+}
+
+void snd_pxa2xx_i2sound_card_deactivate(void)
+{
+	snd_pxa2xx_i2sound_pdev_unregister();
+	snd_card_free(card.acard);
+	card.acard = NULL;
+	snd_pxa2xx_i2sound_board_deactivate();
+	snd_pxa2xx_i2sound_i2slink_free();
+	card.board = NULL;
+	snd_printk(KERN_INFO "PXA2xx i2Sound: %s deactivated\n", card.name);
+}
+
+/* end {{ Soundcard }} */
+
+
+/* begin {{ Module }} */
+
+EXPORT_SYMBOL(snd_pxa2xx_i2sound_card_activate);
+EXPORT_SYMBOL(snd_pxa2xx_i2sound_card_deactivate);
+EXPORT_SYMBOL(snd_pxa2xx_i2sound_i2slink_get);
+EXPORT_SYMBOL(snd_pxa2xx_i2sound_i2slink_free);
+
+static int i2s_pxa_probe(struct device *dev)
+{
+	return 0;
+}
+
+static int i2s_pxa_remove(struct device *dev)
+{
+	pxa_set_cken(CKEN8_I2S, 0);
+	return 0;
+}
+
+static struct device_driver i2s_pxa_driver = {
+	.name    = "pxa2xx-i2s",
+	.bus    = &platform_bus_type,
+	.probe    = i2s_pxa_probe,
+	.remove    = i2s_pxa_remove,
+};
+
+static int __init snd_pxa_i2Sound_module_on_load(void) {
+	return driver_register(&i2s_pxa_driver);
+}
+
+static void __exit snd_pxa_i2Sound_module_on_unload(void) {
+	driver_unregister(&i2s_pxa_driver);
+}
+
+module_init(snd_pxa_i2Sound_module_on_load);
+module_exit(snd_pxa_i2Sound_module_on_unload);
+
+MODULE_AUTHOR("Giorgio Padrin");
+MODULE_DESCRIPTION("PXA2xx i2Sound: support for Intel PXA2xx I2S audio");
+MODULE_LICENSE("GPL");
+
+/* end {{ Module }} */
diff -uprN -X dontdiff linux-2.6.18-vanilla/sound/arm/pxa2xx-i2sound.h linux-2.6.18/sound/arm/pxa2xx-i2sound.h
--- linux-2.6.18-vanilla/sound/arm/pxa2xx-i2sound.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.18/sound/arm/pxa2xx-i2sound.h	2006-10-13 19:22:40.000000000 +0200
@@ -0,0 +1,73 @@
+/*
+ * PXA2xx i2Sound: support for Intel PXA2xx I2S audio
+ *
+ * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
+ *
+ * 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.
+ *
+ * History:
+ *
+ * 2005-03	Giorgio Padrin		Initial release
+ * 2005-05-02	Giorgio Padrin		added event() to board plugins interface
+ * 2005-05-14	Giorgio Padrin		Splitted event()
+ */ 
+
+#ifndef __SOUND_PXA2xx_I2SOUND_H
+#define __SOUND_PXA2xx_I2SOUND_H
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+ 
+struct snd_pxa2xx_i2sound_board_stream_hw { 
+	unsigned int rates;		/* SNDRV_PCM_RATE_* */
+	unsigned int rate_min;		/* min rate */
+	unsigned int rate_max;		/* max rate */
+	unsigned int *rates_table;	/* in case the field rates is not enough */
+	unsigned int rates_num;		/* size of rates_table */
+};
+  
+struct snd_pxa2xx_i2sound_board {
+	char *name;
+	char *desc;	/* short description */
+	char *acard_id;	/* ALSA card id */
+
+	unsigned int info;
+
+	struct snd_pxa2xx_i2sound_board_stream_hw streams_hw[2];
+
+	int (*activate)(void);
+	void (*deactivate)(void);
+
+	int (*set_rate)(unsigned int rate);
+
+	int (*open_stream)(int stream);
+	void (*close_stream)(int stream);
+
+	/* ALSA mixer */
+	int (*add_mixer_controls)(struct snd_card *acard);
+
+	/* tune ALSA hw params space at substream opening */
+	int (*add_hw_constraints)(struct snd_pcm_substream *asubs);
+
+#ifdef CONFIG_PM
+	/* Power Management */
+	int (*suspend) (pm_message_t state);
+	int (*resume) (void);
+#endif
+};
+
+#define SND_PXA2xx_I2SOUND_INFO_CLOCK_FROM_PXA	1 << 0
+#define SND_PXA2xx_I2SOUND_INFO_CAN_CAPTURE	1 << 1
+#define SND_PXA2xx_I2SOUND_INFO_HALF_DUPLEX	1 << 2
+
+void snd_pxa2xx_i2sound_i2slink_get(void);
+void snd_pxa2xx_i2sound_i2slink_free(void);
+
+int snd_pxa2xx_i2sound_card_activate(struct snd_pxa2xx_i2sound_board *board);
+void snd_pxa2xx_i2sound_card_deactivate(void);
+
+#endif /* __SOUND_PXA2xx_I2SOUND_H */
