Index: linux-2.6.20-at91/arch/arm/mach-at91rm9200/Kconfig
===================================================================
--- linux-2.6.20-at91.orig/arch/arm/mach-at91rm9200/Kconfig
+++ linux-2.6.20-at91/arch/arm/mach-at91rm9200/Kconfig
@@ -9,7 +9,7 @@ config ARCH_AT91RM9200
 	bool "AT91RM9200"
 
 config ARCH_AT91SAM9260
-	bool "AT91SAM9260"
+	bool "AT91SAM9260 or AT91SAM9XE"
 
 config ARCH_AT91SAM9261
 	bool "AT91SAM9261"
@@ -93,13 +93,22 @@ endif
 
 if ARCH_AT91SAM9260
 
-comment "AT91SAM9260 Board Type"
+comment "AT91SAM9260 Variants"
+
+config ARCH_AT91SAM9260_SAM9XE
+	bool "AT91SAM9XE"
+	depends on ARCH_AT91SAM9260
+	help
+	  Select this if you are using Atmel's AT91SAM9XE System-on-Chip.
+	  They are basicaly AT91SAM9260s with various sizes of embedded Flash.
+
+comment "AT91SAM9260 / AT91SAM9XE Board Type"
 
 config MACH_AT91SAM9260EK
-	bool "Atmel AT91SAM9260-EK Evaluation Kit"
+	bool "Atmel AT91SAM9260-EK / AT91SAM9XE Evaluation Kit"
 	depends on ARCH_AT91SAM9260
 	help
-	  Select this if you are using Atmel's AT91SAM9260-EK Evaluation Kit.
+	  Select this if you are using Atmel's AT91SAM9260-EK or AT91SAM9XE Evaluation Kit
 	  <http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3933>
 
 endif
Index: linux-2.6.20-at91/arch/arm/mach-at91rm9200/at91sam9260.c
===================================================================
--- linux-2.6.20-at91.orig/arch/arm/mach-at91rm9200/at91sam9260.c
+++ linux-2.6.20-at91/arch/arm/mach-at91rm9200/at91sam9260.c
@@ -14,6 +14,7 @@
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
+#include <asm/arch/cpu.h>
 #include <asm/arch/at91sam9260.h>
 #include <asm/arch/at91_pmc.h>
 #include <asm/arch/at91_rstc.h>
@@ -27,7 +28,11 @@ static struct map_desc at91sam9260_io_de
 		.pfn		= __phys_to_pfn(AT91_BASE_SYS),
 		.length		= SZ_16K,
 		.type		= MT_DEVICE,
-	}, {
+	}
+};
+
+static struct map_desc at91sam9260_sram_desc[] __initdata = {
+	{
 		.virtual	= AT91_IO_VIRT_BASE - AT91SAM9260_SRAM0_SIZE,
 		.pfn		= __phys_to_pfn(AT91SAM9260_SRAM0_BASE),
 		.length		= AT91SAM9260_SRAM0_SIZE,
@@ -37,7 +42,14 @@ static struct map_desc at91sam9260_io_de
 		.pfn		= __phys_to_pfn(AT91SAM9260_SRAM1_BASE),
 		.length		= AT91SAM9260_SRAM1_SIZE,
 		.type		= MT_DEVICE,
-	},
+	}
+};
+
+static struct map_desc at91sam9xe_sram_desc[] __initdata = {
+	{
+		.pfn		= __phys_to_pfn(AT91SAM9XE_SRAM_BASE),
+		.type		= MT_DEVICE,
+	}
 };
 
 /* --------------------------------------------------------------------
@@ -82,6 +94,11 @@ static struct clk usart2_clk = {
 	.pmc_mask	= 1 << AT91SAM9260_ID_US2,
 	.type		= CLK_TYPE_PERIPHERAL,
 };
+static struct clk ssc_clk = {
+	.name		= "ssc_clk",
+	.pmc_mask	= 1 << AT91SAM9260_ID_SSC,
+	.type		= CLK_TYPE_PERIPHERAL,
+};
 static struct clk mmc_clk = {
 	.name		= "mci_clk",
 	.pmc_mask	= 1 << AT91SAM9260_ID_MCI,
@@ -127,8 +144,8 @@ static struct clk ohci_clk = {
 	.pmc_mask	= 1 << AT91SAM9260_ID_UHP,
 	.type		= CLK_TYPE_PERIPHERAL,
 };
-static struct clk ether_clk = {
-	.name		= "ether_clk",
+static struct clk macb_clk = {
+	.name		= "macb_clk",
 	.pmc_mask	= 1 << AT91SAM9260_ID_EMAC,
 	.type		= CLK_TYPE_PERIPHERAL,
 };
@@ -181,12 +198,12 @@ static struct clk *periph_clocks[] __ini
 	&twi_clk,
 	&spi0_clk,
 	&spi1_clk,
-	// ssc
+	&ssc_clk,
 	&tc0_clk,
 	&tc1_clk,
 	&tc2_clk,
 	&ohci_clk,
-	&ether_clk,
+	&macb_clk,
 	&isi_clk,
 	&usart3_clk,
 	&usart4_clk,
@@ -255,10 +272,36 @@ static void at91sam9260_reset(void)
  *  AT91SAM9260 processor initialization
  * -------------------------------------------------------------------- */
 
+static void __init at91sam9xe_initialize(void)
+{
+	unsigned long cidr, sram_size;
+
+	cidr = at91_sys_read(AT91_DBGU_CIDR);
+
+	switch (cidr & AT91_CIDR_SRAMSIZ) {
+		case AT91_CIDR_SRAMSIZ_32K:
+			sram_size = 2 * SZ_16K;
+			break;
+		case AT91_CIDR_SRAMSIZ_16K:
+		default:
+			sram_size = SZ_16K;
+	}
+
+	at91sam9xe_sram_desc->virtual = AT91_IO_VIRT_BASE - sram_size;
+	at91sam9xe_sram_desc->length = sram_size;
+
+	iotable_init(at91sam9xe_sram_desc, ARRAY_SIZE(at91sam9xe_sram_desc));
+}
+
 void __init at91sam9260_initialize(unsigned long main_clock)
 {
 	/* Map peripherals */
 	iotable_init(at91sam9260_io_desc, ARRAY_SIZE(at91sam9260_io_desc));
+	
+	if (cpu_is_at91sam9xe())
+		at91sam9xe_initialize();
+	else
+		iotable_init(at91sam9260_sram_desc, ARRAY_SIZE(at91sam9260_sram_desc));	
 
 	at91_arch_reset = at91sam9260_reset;
 	at91_extern_irq = (1 << AT91SAM9260_ID_IRQ0) | (1 << AT91SAM9260_ID_IRQ1)
Index: linux-2.6.20-at91/include/asm-arm/arch-at91rm9200/at91_dbgu.h
===================================================================
--- linux-2.6.20-at91.orig/include/asm-arm/arch-at91rm9200/at91_dbgu.h
+++ linux-2.6.20-at91/include/asm-arm/arch-at91rm9200/at91_dbgu.h
@@ -35,6 +35,20 @@
 #define		AT91_CIDR_NVPSIZ	(0xf  << 8)		/* Nonvolatile Program Memory Size */
 #define		AT91_CIDR_NVPSIZ2	(0xf  << 12)		/* Second Nonvolatile Program Memory Size */
 #define		AT91_CIDR_SRAMSIZ	(0xf  << 16)		/* Internal SRAM Size */
+#define			AT91_CIDR_SRAMSIZ_1K	(1 << 16)
+#define			AT91_CIDR_SRAMSIZ_2K	(2 << 16)
+#define			AT91_CIDR_SRAMSIZ_112K	(4 << 16)
+#define			AT91_CIDR_SRAMSIZ_4K	(5 << 16)
+#define			AT91_CIDR_SRAMSIZ_80K	(6 << 16)
+#define			AT91_CIDR_SRAMSIZ_160K	(7 << 16)
+#define			AT91_CIDR_SRAMSIZ_8K	(8 << 16)
+#define			AT91_CIDR_SRAMSIZ_16K	(9 << 16)
+#define			AT91_CIDR_SRAMSIZ_32K	(10 << 16)
+#define			AT91_CIDR_SRAMSIZ_64K	(11 << 16)
+#define			AT91_CIDR_SRAMSIZ_128K	(12 << 16)
+#define			AT91_CIDR_SRAMSIZ_256K	(13 << 16)
+#define			AT91_CIDR_SRAMSIZ_96K	(14 << 16)
+#define			AT91_CIDR_SRAMSIZ_512K	(15 << 16)
 #define		AT91_CIDR_ARCH		(0xff << 20)		/* Architecture Identifier */
 #define		AT91_CIDR_NVPTYP	(7    << 28)		/* Nonvolatile Program Memory Type */
 #define		AT91_CIDR_EXT		(1    << 31)		/* Extension Flag */
Index: linux-2.6.20-at91/include/asm-arm/arch-at91rm9200/at91sam9260.h
===================================================================
--- linux-2.6.20-at91.orig/include/asm-arm/arch-at91rm9200/at91sam9260.h
+++ linux-2.6.20-at91/include/asm-arm/arch-at91rm9200/at91sam9260.h
@@ -113,6 +113,10 @@
 
 #define AT91SAM9260_UHP_BASE	0x00500000	/* USB Host controller */
 
+#define AT91SAM9XE_FLASH_BASE	0x00200000	/* Internal FLASH base address */
+#define AT91SAM9XE_SRAM_BASE	0x00300000	/* Internal SRAM base address */
+
+
 #if 0
 /*
  * PIO pin definitions (peripheral A/B multiplexing).
Index: linux-2.6.20-at91/include/asm-arm/arch-at91rm9200/cpu.h
===================================================================
--- linux-2.6.20-at91.orig/include/asm-arm/arch-at91rm9200/cpu.h
+++ linux-2.6.20-at91/include/asm-arm/arch-at91rm9200/cpu.h
@@ -22,6 +22,9 @@
 #define ARCH_ID_AT91SAM9261	0x019703a0
 #define ARCH_ID_AT91SAM9263	0x019607a0
 
+#define ARCH_ID_AT91SAM9XE128	0x329973a0
+#define ARCH_ID_AT91SAM9XE256	0x329a93a0
+#define ARCH_ID_AT91SAM9XE512	0x329aa3a0
 
 static inline unsigned long at91_cpu_identify(void)
 {
@@ -29,6 +32,16 @@ static inline unsigned long at91_cpu_ide
 }
 
 
+#define ARCH_FAMILY_AT91X92	0x09200000
+#define ARCH_FAMILY_AT91SAM9	0x01900000
+#define ARCH_FAMILY_AT91SAM9XE	0x02900000
+
+static inline unsigned long at91_arch_identify(void)
+{
+	return (at91_sys_read(AT91_DBGU_CIDR) & AT91_CIDR_ARCH);
+}
+
+
 #ifdef CONFIG_ARCH_AT91RM9200
 #define cpu_is_at91rm9200()	(at91_cpu_identify() == ARCH_ID_AT91RM9200)
 #else
@@ -36,8 +49,10 @@ static inline unsigned long at91_cpu_ide
 #endif
 
 #ifdef CONFIG_ARCH_AT91SAM9260
-#define cpu_is_at91sam9260()	(at91_cpu_identify() == ARCH_ID_AT91SAM9260)
+#define cpu_is_at91sam9xe()	(at91_arch_identify() == ARCH_FAMILY_AT91SAM9XE)
+#define cpu_is_at91sam9260()	((at91_cpu_identify() == ARCH_ID_AT91SAM9260) || cpu_is_at91sam9xe())
 #else
+#define cpu_is_at91sam9xe()	(0)
 #define cpu_is_at91sam9260()	(0)
 #endif
 
Index: linux-2.6.20-at91/arch/arm/mach-at91rm9200/at91sam9260_devices.c
===================================================================
--- linux-2.6.20-at91.orig/arch/arm/mach-at91rm9200/at91sam9260_devices.c
+++ linux-2.6.20-at91/arch/arm/mach-at91rm9200/at91sam9260_devices.c
@@ -320,16 +320,16 @@ void __init at91_add_device_nand(struct 
 	at91_sys_write(AT91_SMC_SETUP(3), AT91_SMC_NWESETUP_(0) | AT91_SMC_NCS_WRSETUP_(0)
 			| AT91_SMC_NRDSETUP_(0) | AT91_SMC_NCS_RDSETUP_(0));
 
-	at91_sys_write(AT91_SMC_PULSE(3), AT91_SMC_NWEPULSE_(2) | AT91_SMC_NCS_WRPULSE_(5)
-			| AT91_SMC_NRDPULSE_(2) | AT91_SMC_NCS_RDPULSE_(5));
+	at91_sys_write(AT91_SMC_PULSE(3), AT91_SMC_NWEPULSE_(3) | AT91_SMC_NCS_WRPULSE_(3)
+			| AT91_SMC_NRDPULSE_(3) | AT91_SMC_NCS_RDPULSE_(3));
 
-	at91_sys_write(AT91_SMC_CYCLE(3), AT91_SMC_NWECYCLE_(7) | AT91_SMC_NRDCYCLE_(7));
+	at91_sys_write(AT91_SMC_CYCLE(3), AT91_SMC_NWECYCLE_(5) | AT91_SMC_NRDCYCLE_(5));
 
 	if (data->bus_width_16)
 		mode = AT91_SMC_DBW_16;
 	else
 		mode = AT91_SMC_DBW_8;
-	at91_sys_write(AT91_SMC_MODE(3), mode | AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_TDF_(1));
+	at91_sys_write(AT91_SMC_MODE(3), mode | AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_TDF_(2));
 
 	/* enable pin */
 	if (data->enable_pin)
Index: linux-2.6.20-at91/drivers/input/touchscreen/ads7846.c
===================================================================
--- linux-2.6.20-at91.orig/drivers/input/touchscreen/ads7846.c
+++ linux-2.6.20-at91/drivers/input/touchscreen/ads7846.c
@@ -17,8 +17,9 @@
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  */
-#include <linux/device.h>
+#include <linux/hwmon.h>
 #include <linux/init.h>
+#include <linux/err.h>
 #include <linux/delay.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
@@ -38,7 +39,8 @@
 /*
  * This code has been heavily tested on a Nokia 770, and lightly
  * tested on other ads7846 devices (OSK/Mistral, Lubbock).
- * Support for ads7843 and ads7845 has only been stubbed in.
+ * Support for ads7843 tested on Atmel at91sam926x-EK.
+ * Support for ads7845 has only been stubbed in.
  *
  * IRQ handling needs a workaround because of a shortcoming in handling
  * edge triggered IRQs on some platforms like the OMAP1/2. These
@@ -54,7 +56,8 @@
  * files.
  */
 
-#define	TS_POLL_PERIOD	msecs_to_jiffies(10)
+#define TS_POLL_DELAY	(1 * 1000000)	/* ns delay before the first sample */
+#define TS_POLL_PERIOD	(5 * 1000000)	/* ns delay between samples */
 
 /* this driver doesn't aim at the peak continuous sample rate */
 #define	SAMPLE_BITS	(8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */)
@@ -63,12 +66,12 @@ struct ts_event {
 	/* For portability, we can't read 12 bit values using SPI (which
 	 * would make the controller deliver them as native byteorder u16
 	 * with msbs zeroed).  Instead, we read them as two 8-bit values,
-	 * which need byteswapping then range adjustment.
+	 * *** WHICH NEED BYTESWAPPING *** and range adjustment.
 	 */
-	__be16 x;
-	__be16 y;
-	__be16 z1, z2;
-	int    ignore;
+	u16	x;
+	u16	y;
+	u16	z1, z2;
+	int	ignore;
 };
 
 struct ads7846 {
@@ -76,7 +79,12 @@ struct ads7846 {
 	char			phys[32];
 
 	struct spi_device	*spi;
+
+#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE)
 	struct attribute_group	*attr_group;
+	struct class_device	*hwmon;
+#endif
+
 	u16			model;
 	u16			vref_delay_usecs;
 	u16			x_plate_ohms;
@@ -99,13 +107,16 @@ struct ads7846 {
 	u16			debounce_rep;
 
 	spinlock_t		lock;
-	struct timer_list	timer;		/* P: lock */
+	struct hrtimer		timer;
 	unsigned		pendown:1;	/* P: lock */
 	unsigned		pending:1;	/* P: lock */
 // FIXME remove "irq_disabled"
 	unsigned		irq_disabled:1;	/* P: lock */
 	unsigned		disabled:1;
 
+	int			(*filter)(void *data, int data_idx, int *val);
+	void			*filter_data;
+	void			(*filter_cleanup)(void *data);
 	int			(*get_pendown_state)(void);
 };
 
@@ -142,15 +153,16 @@ struct ads7846 {
 #define	MAX_12BIT	((1<<12)-1)
 
 /* leave ADC powered up (disables penirq) between differential samples */
-#define	READ_12BIT_DFR(x) (ADS_START | ADS_A2A1A0_d_ ## x \
-	| ADS_12_BIT | ADS_DFR)
-
-#define	READ_Y	(READ_12BIT_DFR(y)  | ADS_PD10_ADC_ON)
-#define	READ_Z1	(READ_12BIT_DFR(z1) | ADS_PD10_ADC_ON)
-#define	READ_Z2	(READ_12BIT_DFR(z2) | ADS_PD10_ADC_ON)
+#define	READ_12BIT_DFR(x, adc, vref) (ADS_START | ADS_A2A1A0_d_ ## x \
+	| ADS_12_BIT | ADS_DFR | \
+	(adc ? ADS_PD10_ADC_ON : 0) | (vref ? ADS_PD10_REF_ON : 0))
+
+#define	READ_Y(vref)	(READ_12BIT_DFR(y,  1, vref))
+#define	READ_Z1(vref)	(READ_12BIT_DFR(z1, 1, vref))
+#define	READ_Z2(vref)	(READ_12BIT_DFR(z2, 1, vref))
 
-#define	READ_X	(READ_12BIT_DFR(x)  | ADS_PD10_ADC_ON)
-#define	PWRDOWN	(READ_12BIT_DFR(y)  | ADS_PD10_PDOWN)	/* LAST */
+#define	READ_X(vref)	(READ_12BIT_DFR(x,  1, vref))
+#define	PWRDOWN		(READ_12BIT_DFR(y,  0, 0))	/* LAST */
 
 /* single-ended samples need to first power up reference voltage;
  * we leave both ADC and VREF powered
@@ -158,14 +170,19 @@ struct ads7846 {
 #define	READ_12BIT_SER(x) (ADS_START | ADS_A2A1A0_ ## x \
 	| ADS_12_BIT | ADS_SER)
 
-#define	REF_ON	(READ_12BIT_DFR(x) | ADS_PD10_ALL_ON)
-#define	REF_OFF	(READ_12BIT_DFR(y) | ADS_PD10_PDOWN)
+#define	REF_ON	(READ_12BIT_DFR(x, 1, 1))
+#define	REF_OFF	(READ_12BIT_DFR(y, 0, 0))
 
 /*--------------------------------------------------------------------------*/
 
 /*
  * Non-touchscreen sensors only use single-ended conversions.
+ * The range is GND..vREF. The ads7843 and ads7835 must use external vREF;
+ * ads7846 lets that pin be unconnected, to use internal vREF.
  */
+static unsigned vREF_mV;
+module_param(vREF_mV, uint, 0);
+MODULE_PARM_DESC(vREF_mV, "external vREF voltage, in milliVolts");
 
 struct ser_req {
 	u8			ref_on;
@@ -193,50 +210,53 @@ static int ads7846_read12_ser(struct dev
 	struct ser_req		*req = kzalloc(sizeof *req, GFP_KERNEL);
 	int			status;
 	int			sample;
-	int			i;
+	int			use_internal;
 
 	if (!req)
 		return -ENOMEM;
 
 	spi_message_init(&req->msg);
 
-	/* activate reference, so it has time to settle; */
-	req->ref_on = REF_ON;
-	req->xfer[0].tx_buf = &req->ref_on;
-	req->xfer[0].len = 1;
-	req->xfer[1].rx_buf = &req->scratch;
-	req->xfer[1].len = 2;
-
-	/*
-	 * for external VREF, 0 usec (and assume it's always on);
-	 * for 1uF, use 800 usec;
-	 * no cap, 100 usec.
-	 */
-	req->xfer[1].delay_usecs = ts->vref_delay_usecs;
+	/* FIXME boards with ads7846 might use external vref instead ... */
+	use_internal = (ts->model == 7846);
+
+	/* maybe turn on internal vREF, and let it settle */
+	if (use_internal) {
+		req->ref_on = REF_ON;
+		req->xfer[0].tx_buf = &req->ref_on;
+		req->xfer[0].len = 1;
+		spi_message_add_tail(&req->xfer[0], &req->msg);
+
+		req->xfer[1].rx_buf = &req->scratch;
+		req->xfer[1].len = 2;
+
+		/* for 1uF, settle for 800 usec; no cap, 100 usec.  */
+		req->xfer[1].delay_usecs = ts->vref_delay_usecs;
+		spi_message_add_tail(&req->xfer[1], &req->msg);
+	}
 
 	/* take sample */
 	req->command = (u8) command;
 	req->xfer[2].tx_buf = &req->command;
 	req->xfer[2].len = 1;
+	spi_message_add_tail(&req->xfer[2], &req->msg);
+
 	req->xfer[3].rx_buf = &req->sample;
 	req->xfer[3].len = 2;
+	spi_message_add_tail(&req->xfer[3], &req->msg);
 
 	/* REVISIT:  take a few more samples, and compare ... */
 
-	/* turn off reference */
-	req->ref_off = REF_OFF;
+	/* converter in low power mode & enable PENIRQ */
+	req->ref_off = PWRDOWN;
 	req->xfer[4].tx_buf = &req->ref_off;
 	req->xfer[4].len = 1;
+	spi_message_add_tail(&req->xfer[4], &req->msg);
+
 	req->xfer[5].rx_buf = &req->scratch;
 	req->xfer[5].len = 2;
-
 	CS_CHANGE(req->xfer[5]);
-
-	/* group all the transfers together, so we can't interfere with
-	 * reading touchscreen state; disable penirq while sampling
-	 */
-	for (i = 0; i < 6; i++)
-		spi_message_add_tail(&req->xfer[i], &req->msg);
+	spi_message_add_tail(&req->xfer[5], &req->msg);
 
 	ts->irq_disabled = 1;
 	disable_irq(spi->irq);
@@ -256,25 +276,173 @@ static int ads7846_read12_ser(struct dev
 	return status ? status : sample;
 }
 
-#define SHOW(name) static ssize_t \
+#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE)
+
+#define SHOW(name, var, adjust) static ssize_t \
 name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
 { \
+	struct ads7846 *ts = dev_get_drvdata(dev); \
 	ssize_t v = ads7846_read12_ser(dev, \
-			READ_12BIT_SER(name) | ADS_PD10_ALL_ON); \
+			READ_12BIT_SER(var) | ADS_PD10_ALL_ON); \
 	if (v < 0) \
 		return v; \
-	return sprintf(buf, "%u\n", (unsigned) v); \
+	return sprintf(buf, "%u\n", adjust(ts, v)); \
 } \
 static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL);
 
-SHOW(temp0)
-SHOW(temp1)
-SHOW(vaux)
-SHOW(vbatt)
+
+/* Sysfs conventions report temperatures in millidegrees Celcius.
+ * ADS7846 could use the low-accuracy two-sample scheme, but can't do the high
+ * accuracy scheme without calibration data.  For now we won't try either;
+ * userspace sees raw sensor values, and must scale/calibrate appropriately.
+ */
+static inline unsigned null_adjust(struct ads7846 *ts, ssize_t v)
+{
+	return v;
+}
+
+SHOW(temp0, temp0, null_adjust)		/* temp1_input */
+SHOW(temp1, temp1, null_adjust)		/* temp2_input */
+
+
+/* sysfs conventions report voltages in millivolts.  We can convert voltages
+ * if we know vREF.  userspace may need to scale vAUX to match the board's
+ * external resistors; we assume that vBATT only uses the internal ones.
+ */
+static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v)
+{
+	unsigned retval = v;
+
+	/* external resistors may scale vAUX into 0..vREF */
+	retval *= vREF_mV;
+	retval = retval >> 12;
+	return retval;
+}
+
+static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v)
+{
+	unsigned retval = vaux_adjust(ts, v);
+
+	/* ads7846 has a resistor ladder to scale this signal down */
+	if (ts->model == 7846)
+		retval *= 4;
+	return retval;
+}
+
+SHOW(in0_input, vaux, vaux_adjust)
+SHOW(in1_input, vbatt, vbatt_adjust)
+
+
+static struct attribute *ads7846_attributes[] = {
+	&dev_attr_temp0.attr,
+	&dev_attr_temp1.attr,
+	&dev_attr_in0_input.attr,
+	&dev_attr_in1_input.attr,
+	NULL,
+};
+
+static struct attribute_group ads7846_attr_group = {
+	.attrs = ads7846_attributes,
+};
+
+static struct attribute *ads7843_attributes[] = {
+	&dev_attr_in0_input.attr,
+	&dev_attr_in1_input.attr,
+	NULL,
+};
+
+static struct attribute_group ads7843_attr_group = {
+	.attrs = ads7843_attributes,
+};
+
+static struct attribute *ads7845_attributes[] = {
+	&dev_attr_in0_input.attr,
+	NULL,
+};
+
+static struct attribute_group ads7845_attr_group = {
+	.attrs = ads7845_attributes,
+};
+
+static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)
+{
+	struct class_device *hwmon;
+	int err;
+
+	/* hwmon sensors need a reference voltage */
+	switch (ts->model) {
+	case 7846:
+		if (!vREF_mV) {
+			dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n");
+			vREF_mV = 2500;
+		}
+		break;
+	case 7845:
+	case 7843:
+		if (!vREF_mV) {
+			dev_warn(&spi->dev,
+				"external vREF for ADS%d not specified\n",
+				ts->model);
+			return 0;
+		}
+		break;
+	}
+
+	/* different chips have different sensor groups */
+	switch (ts->model) {
+	case 7846:
+		ts->attr_group = &ads7846_attr_group;
+		break;
+	case 7845:
+		ts->attr_group = &ads7845_attr_group;
+		break;
+	case 7843:
+		ts->attr_group = &ads7843_attr_group;
+		break;
+	default:
+		dev_dbg(&spi->dev, "ADS%d not recognized\n", ts->model);
+		return 0;
+	}
+
+	err = sysfs_create_group(&spi->dev.kobj, ts->attr_group);
+	if (err)
+		return err;
+
+	hwmon = hwmon_device_register(&spi->dev);
+	if (IS_ERR(hwmon)) {
+		sysfs_remove_group(&spi->dev.kobj, ts->attr_group);
+		return PTR_ERR(hwmon);
+	}
+
+	ts->hwmon = hwmon;
+	return 0;
+}
+
+static void ads784x_hwmon_unregister(struct spi_device *spi,
+				     struct ads7846 *ts)
+{
+	if (ts->hwmon) {
+		sysfs_remove_group(&spi->dev.kobj, ts->attr_group);
+		hwmon_device_unregister(ts->hwmon);
+	}
+}
+
+#else
+static inline int ads784x_hwmon_register(struct spi_device *spi,
+					 struct ads7846 *ts)
+{
+	return 0;
+}
+
+static inline void ads784x_hwmon_unregister(struct spi_device *spi,
+					    struct ads7846 *ts)
+{
+}
+#endif
 
 static int is_pen_down(struct device *dev)
 {
-	struct ads7846		*ts = dev_get_drvdata(dev);
+	struct ads7846	*ts = dev_get_drvdata(dev);
 
 	return ts->pendown;
 }
@@ -318,46 +486,14 @@ static ssize_t ads7846_disable_store(str
 
 static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store);
 
-static struct attribute *ads7846_attributes[] = {
-	&dev_attr_temp0.attr,
-	&dev_attr_temp1.attr,
-	&dev_attr_vbatt.attr,
-	&dev_attr_vaux.attr,
-	&dev_attr_pen_down.attr,
-	&dev_attr_disable.attr,
-	NULL,
-};
-
-static struct attribute_group ads7846_attr_group = {
-	.attrs = ads7846_attributes,
-};
-
-/*
- * ads7843/7845 don't have temperature sensors, and
- * use the other sensors a bit differently too
- */
-
-static struct attribute *ads7843_attributes[] = {
-	&dev_attr_vbatt.attr,
-	&dev_attr_vaux.attr,
+static struct attribute *ads784x_attributes[] = {
 	&dev_attr_pen_down.attr,
 	&dev_attr_disable.attr,
 	NULL,
 };
 
-static struct attribute_group ads7843_attr_group = {
-	.attrs = ads7843_attributes,
-};
-
-static struct attribute *ads7845_attributes[] = {
-	&dev_attr_vaux.attr,
-	&dev_attr_pen_down.attr,
-	&dev_attr_disable.attr,
-	NULL,
-};
-
-static struct attribute_group ads7845_attr_group = {
-	.attrs = ads7845_attributes,
+static struct attribute_group ads784x_attr_group = {
+	.attrs = ads784x_attributes,
 };
 
 /*--------------------------------------------------------------------------*/
@@ -373,25 +509,22 @@ static struct attribute_group ads7845_at
 static void ads7846_rx(void *ads)
 {
 	struct ads7846		*ts = ads;
-	struct input_dev	*input_dev = ts->input;
 	unsigned		Rt;
-	unsigned		sync = 0;
 	u16			x, y, z1, z2;
-	unsigned long		flags;
 
-	/* adjust:  on-wire is a must-ignore bit, a BE12 value, then padding;
-	 * built from two 8 bit values written msb-first.
+	/* ads7846_rx_val() did in-place conversion (including byteswap) from
+	 * on-the-wire format as part of debouncing to get stable readings.
 	 */
-	x = (be16_to_cpu(ts->tc.x) >> 3) & 0x0fff;
-	y = (be16_to_cpu(ts->tc.y) >> 3) & 0x0fff;
-	z1 = (be16_to_cpu(ts->tc.z1) >> 3) & 0x0fff;
-	z2 = (be16_to_cpu(ts->tc.z2) >> 3) & 0x0fff;
+	x = ts->tc.x;
+	y = ts->tc.y;
+	z1 = ts->tc.z1;
+	z2 = ts->tc.z2;
 
 	/* range filtering */
 	if (x == MAX_12BIT)
 		x = 0;
 
-	if (likely(x && z1 && !device_suspended(&ts->spi->dev))) {
+	if (likely(x && z1)) {
 		/* compute touch pressure resistance using equation #2 */
 		Rt = z2;
 		Rt -= z1;
@@ -402,101 +535,134 @@ static void ads7846_rx(void *ads)
 	} else
 		Rt = 0;
 
+	if (ts->model == 7843)
+		Rt = ts->pressure_max / 2;
+
+
 	/* Sample found inconsistent by debouncing or pressure is beyond
-	* the maximum. Don't report it to user space, repeat at least
-	* once more the measurement */
+	 * the maximum. Don't report it to user space, repeat at least
+	 * once more the measurement
+	 */
 	if (ts->tc.ignore || Rt > ts->pressure_max) {
-		mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD);
+#ifdef VERBOSE
+		pr_debug("%s: ignored %d pressure %d\n",
+			ts->spi->dev.bus_id, ts->tc.ignore, Rt);
+#endif
+		hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
+			      HRTIMER_REL);
 		return;
 	}
 
-	/* NOTE:  "pendown" is inferred from pressure; we don't rely on
-	 * being able to check nPENIRQ status, or "friendly" trigger modes
-	 * (both-edges is much better than just-falling or low-level).
-	 *
-	 * REVISIT:  some boards may require reading nPENIRQ; it's
-	 * needed on 7843.  and 7845 reads pressure differently...
+	/* NOTE: We can't rely on the pressure to determine the pen down
+	 * state, even this controller has a pressure sensor.  The pressure
+	 * value can fluctuate for quite a while after lifting the pen and
+	 * in some cases may not even settle at the expected value.
 	 *
-	 * REVISIT:  the touchscreen might not be connected; this code
-	 * won't notice that, even if nPENIRQ never fires ...
+	 * The only safe way to check for the pen up condition is in the
+	 * timer by reading the pen signal state (it's a GPIO _and_ IRQ).
 	 */
-	if (!ts->pendown && Rt != 0) {
-		input_report_key(input_dev, BTN_TOUCH, 1);
-		sync = 1;
-	} else if (ts->pendown && Rt == 0) {
-		input_report_key(input_dev, BTN_TOUCH, 0);
-		sync = 1;
-	}
-
 	if (Rt) {
-		input_report_abs(input_dev, ABS_X, x);
-		input_report_abs(input_dev, ABS_Y, y);
-		sync = 1;
-	}
+		struct input_dev *input = ts->input;
 
-	if (sync) {
-		input_report_abs(input_dev, ABS_PRESSURE, Rt);
-		input_sync(input_dev);
-	}
-
-#ifdef	VERBOSE
-	if (Rt || ts->pendown)
-		pr_debug("%s: %d/%d/%d%s\n", ts->spi->dev.bus_id,
-			x, y, Rt, Rt ? "" : " UP");
+		if (!ts->pendown) {
+			input_report_key(input, BTN_TOUCH, 1);
+			ts->pendown = 1;
+#ifdef VERBOSE
+			dev_dbg(&ts->spi->dev, "DOWN\n");
 #endif
+		}
+		input_report_abs(input, ABS_X, x);
+		input_report_abs(input, ABS_Y, y);
+		input_report_abs(input, ABS_PRESSURE, Rt);
+
+		input_sync(input);
+#ifdef VERBOSE
+		dev_dbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt);
+#endif
+	}
 
-	spin_lock_irqsave(&ts->lock, flags);
-
-	ts->pendown = (Rt != 0);
-	mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD);
-
-	spin_unlock_irqrestore(&ts->lock, flags);
+	hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), HRTIMER_REL);
 }
 
-static void ads7846_debounce(void *ads)
+static int ads7846_debounce(void *ads, int data_idx, int *val)
 {
 	struct ads7846		*ts = ads;
-	struct spi_message	*m;
-	struct spi_transfer	*t;
-	int			val;
-	int			status;
 
-	m = &ts->msg[ts->msg_idx];
-	t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
-	val = (be16_to_cpu(*(__be16 *)t->rx_buf) >> 3) & 0x0fff;
-	if (!ts->read_cnt || (abs(ts->last_read - val) > ts->debounce_tol)) {
+	if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) {
+		/* Start over collecting consistent readings. */
+		ts->read_rep = 0;
 		/* Repeat it, if this was the first read or the read
 		 * wasn't consistent enough. */
 		if (ts->read_cnt < ts->debounce_max) {
-			ts->last_read = val;
+			ts->last_read = *val;
 			ts->read_cnt++;
+			return ADS7846_FILTER_REPEAT;
 		} else {
 			/* Maximum number of debouncing reached and still
 			 * not enough number of consistent readings. Abort
 			 * the whole sample, repeat it in the next sampling
 			 * period.
 			 */
-			ts->tc.ignore = 1;
 			ts->read_cnt = 0;
-			/* Last message will contain ads7846_rx() as the
-			 * completion function.
-			 */
-			m = ts->last_msg;
+			return ADS7846_FILTER_IGNORE;
 		}
-		/* Start over collecting consistent readings. */
-		ts->read_rep = 0;
 	} else {
 		if (++ts->read_rep > ts->debounce_rep) {
 			/* Got a good reading for this coordinate,
 			 * go for the next one. */
-			ts->tc.ignore = 0;
-			ts->msg_idx++;
 			ts->read_cnt = 0;
 			ts->read_rep = 0;
-			m++;
-		} else
+			return ADS7846_FILTER_OK;
+		} else {
 			/* Read more values that are consistent. */
 			ts->read_cnt++;
+			return ADS7846_FILTER_REPEAT;
+		}
+	}
+}
+
+static int ads7846_no_filter(void *ads, int data_idx, int *val)
+{
+	return ADS7846_FILTER_OK;
+}
+
+static void ads7846_rx_val(void *ads)
+{
+	struct ads7846 *ts = ads;
+	struct spi_message *m;
+	struct spi_transfer *t;
+	u16 *rx_val;
+	int val;
+	int action;
+	int status;
+
+	m = &ts->msg[ts->msg_idx];
+	t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
+	rx_val = t->rx_buf;
+
+	/* adjust:  on-wire is a must-ignore bit, a BE12 value, then padding;
+	 * built from two 8 bit values written msb-first.
+	 */
+	val = be16_to_cpu(*rx_val) >> 3;
+
+	action = ts->filter(ts->filter_data, ts->msg_idx, &val);
+	switch (action) {
+	case ADS7846_FILTER_REPEAT:
+		break;
+	case ADS7846_FILTER_IGNORE:
+		ts->tc.ignore = 1;
+		/* Last message will contain ads7846_rx() as the
+		 * completion function.
+		 */
+		m = ts->last_msg;
+		break;
+	case ADS7846_FILTER_OK:
+		*rx_val = val;
+		ts->tc.ignore = 0;
+		m = &ts->msg[++ts->msg_idx];
+		break;
+	default:
+		BUG();
 	}
 	status = spi_async(ts->spi, m);
 	if (status)
@@ -504,21 +670,34 @@ static void ads7846_debounce(void *ads)
 				status);
 }
 
-static void ads7846_timer(unsigned long handle)
+static int ads7846_timer(struct hrtimer *handle)
 {
-	struct ads7846	*ts = (void *)handle;
+	struct ads7846	*ts = container_of(handle, struct ads7846, timer);
 	int		status = 0;
 
 	spin_lock_irq(&ts->lock);
 
-	if (unlikely(ts->msg_idx && !ts->pendown)) {
+	if (unlikely(!ts->get_pendown_state() ||
+		     device_suspended(&ts->spi->dev))) {
+		if (ts->pendown) {
+			struct input_dev *input = ts->input;
+
+			input_report_key(input, BTN_TOUCH, 0);
+			input_report_abs(input, ABS_PRESSURE, 0);
+			input_sync(input);
+
+			ts->pendown = 0;
+#ifdef VERBOSE
+			dev_dbg(&ts->spi->dev, "UP\n");
+#endif
+		}
+
 		/* measurement cycle ended */
 		if (!device_suspended(&ts->spi->dev)) {
 			ts->irq_disabled = 0;
 			enable_irq(ts->spi->irq);
 		}
 		ts->pending = 0;
-		ts->msg_idx = 0;
 	} else {
 		/* pen is still down, continue with the measurement */
 		ts->msg_idx = 0;
@@ -528,6 +707,7 @@ static void ads7846_timer(unsigned long 
 	}
 
 	spin_unlock_irq(&ts->lock);
+	return HRTIMER_NORESTART;
 }
 
 static irqreturn_t ads7846_irq(int irq, void *handle)
@@ -546,7 +726,8 @@ static irqreturn_t ads7846_irq(int irq, 
 			ts->irq_disabled = 1;
 			disable_irq(ts->spi->irq);
 			ts->pending = 1;
-			mod_timer(&ts->timer, jiffies);
+			hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY),
+					HRTIMER_REL);
 		}
 	}
 	spin_unlock_irqrestore(&ts->lock, flags);
@@ -632,6 +813,7 @@ static int __devinit ads7846_probe(struc
 	struct ads7846_platform_data	*pdata = spi->dev.platform_data;
 	struct spi_message		*m;
 	struct spi_transfer		*x;
+	int				vref;
 	int				err;
 
 	if (!spi->irq) {
@@ -665,6 +847,10 @@ static int __devinit ads7846_probe(struc
 	 * may not.  So we stick to very-portable 8 bit words, both RX and TX.
 	 */
 	spi->bits_per_word = 8;
+	spi->mode = SPI_MODE_1;
+	err = spi_setup(spi);
+	if (err < 0)
+		return err;
 
 	ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL);
 	input_dev = input_allocate_device();
@@ -679,8 +865,7 @@ static int __devinit ads7846_probe(struc
 	ts->spi = spi;
 	ts->input = input_dev;
 
-	init_timer(&ts->timer);
-	ts->timer.data = (unsigned long) ts;
+	hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_REL);
 	ts->timer.function = ads7846_timer;
 
 	spin_lock_init(&ts->lock);
@@ -689,14 +874,25 @@ static int __devinit ads7846_probe(struc
 	ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;
 	ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
 	ts->pressure_max = pdata->pressure_max ? : ~0;
-	if (pdata->debounce_max) {
+
+	if (pdata->filter != NULL) {
+		if (pdata->filter_init != NULL) {
+			err = pdata->filter_init(pdata, &ts->filter_data);
+			if (err < 0)
+				goto err_free_mem;
+		}
+		ts->filter = pdata->filter;
+		ts->filter_cleanup = pdata->filter_cleanup;
+	} else if (pdata->debounce_max) {
 		ts->debounce_max = pdata->debounce_max;
+		if (ts->debounce_max < 2)
+			ts->debounce_max = 2;
 		ts->debounce_tol = pdata->debounce_tol;
 		ts->debounce_rep = pdata->debounce_rep;
-		if (ts->debounce_rep > ts->debounce_max + 1)
-			ts->debounce_rep = ts->debounce_max - 1;
+		ts->filter = ads7846_debounce;
+		ts->filter_data = ts;
 	} else
-		ts->debounce_tol = ~0;
+		ts->filter = ads7846_no_filter;
 	ts->get_pendown_state = pdata->get_pendown_state;
 
 	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id);
@@ -718,6 +914,8 @@ static int __devinit ads7846_probe(struc
 	input_set_abs_params(input_dev, ABS_PRESSURE,
 			pdata->pressure_min, pdata->pressure_max, 0, 0);
 
+	vref = pdata->keep_vref_on;
+
 	/* set up the transfers to read touchscreen state; this assumes we
 	 * use formula #2 for pressure, not #3.
 	 */
@@ -727,7 +925,7 @@ static int __devinit ads7846_probe(struc
 	spi_message_init(m);
 
 	/* y- still on; turn on only y+ (and ADC) */
-	ts->read_y = READ_Y;
+	ts->read_y = READ_Y(vref);
 	x->tx_buf = &ts->read_y;
 	x->len = 1;
 	spi_message_add_tail(x, m);
@@ -737,7 +935,7 @@ static int __devinit ads7846_probe(struc
 	x->len = 2;
 	spi_message_add_tail(x, m);
 
-	m->complete = ads7846_debounce;
+	m->complete = ads7846_rx_val;
 	m->context = ts;
 
 	m++;
@@ -745,7 +943,7 @@ static int __devinit ads7846_probe(struc
 
 	/* turn y- off, x+ on, then leave in lowpower */
 	x++;
-	ts->read_x = READ_X;
+	ts->read_x = READ_X(vref);
 	x->tx_buf = &ts->read_x;
 	x->len = 1;
 	spi_message_add_tail(x, m);
@@ -755,7 +953,7 @@ static int __devinit ads7846_probe(struc
 	x->len = 2;
 	spi_message_add_tail(x, m);
 
-	m->complete = ads7846_debounce;
+	m->complete = ads7846_rx_val;
 	m->context = ts;
 
 	/* turn y+ off, x- on; we'll use formula #2 */
@@ -764,7 +962,7 @@ static int __devinit ads7846_probe(struc
 		spi_message_init(m);
 
 		x++;
-		ts->read_z1 = READ_Z1;
+		ts->read_z1 = READ_Z1(vref);
 		x->tx_buf = &ts->read_z1;
 		x->len = 1;
 		spi_message_add_tail(x, m);
@@ -774,14 +972,14 @@ static int __devinit ads7846_probe(struc
 		x->len = 2;
 		spi_message_add_tail(x, m);
 
-		m->complete = ads7846_debounce;
+		m->complete = ads7846_rx_val;
 		m->context = ts;
 
 		m++;
 		spi_message_init(m);
 
 		x++;
-		ts->read_z2 = READ_Z2;
+		ts->read_z2 = READ_Z2(vref);
 		x->tx_buf = &ts->read_z2;
 		x->len = 1;
 		spi_message_add_tail(x, m);
@@ -791,7 +989,7 @@ static int __devinit ads7846_probe(struc
 		x->len = 2;
 		spi_message_add_tail(x, m);
 
-		m->complete = ads7846_debounce;
+		m->complete = ads7846_rx_val;
 		m->context = ts;
 	}
 
@@ -820,31 +1018,24 @@ static int __devinit ads7846_probe(struc
 			spi->dev.driver->name, ts)) {
 		dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
 		err = -EBUSY;
-		goto err_free_mem;
+		goto err_cleanup_filter;
 	}
 
+	err = ads784x_hwmon_register(spi, ts);
+	if (err)
+		goto err_free_irq;
+
 	dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq);
 
-	/* take a first sample, leaving nPENIRQ active; avoid
+	/* take a first sample, leaving nPENIRQ active and vREF off; avoid
 	 * the touchscreen, in case it's not connected.
 	 */
 	(void) ads7846_read12_ser(&spi->dev,
 			  READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON);
 
-	switch (ts->model) {
-	case 7846:
-		ts->attr_group = &ads7846_attr_group;
-		break;
-	case 7845:
-		ts->attr_group = &ads7845_attr_group;
-		break;
-	default:
-		ts->attr_group = &ads7843_attr_group;
-		break;
-	}
-	err = sysfs_create_group(&spi->dev.kobj, ts->attr_group);
+	err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group);
 	if (err)
-		goto err_free_irq;
+		goto err_remove_hwmon;
 
 	err = input_register_device(input_dev);
 	if (err)
@@ -853,9 +1044,14 @@ static int __devinit ads7846_probe(struc
 	return 0;
 
  err_remove_attr_group:
-	sysfs_remove_group(&spi->dev.kobj, ts->attr_group);
+	sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
+ err_remove_hwmon:
+	ads784x_hwmon_unregister(spi, ts);
  err_free_irq:
 	free_irq(spi->irq, ts);
+ err_cleanup_filter:
+	if (ts->filter_cleanup)
+		ts->filter_cleanup(ts->filter_data);
  err_free_mem:
 	input_free_device(input_dev);
 	kfree(ts);
@@ -866,16 +1062,20 @@ static int __devexit ads7846_remove(stru
 {
 	struct ads7846		*ts = dev_get_drvdata(&spi->dev);
 
+	ads784x_hwmon_unregister(spi, ts);
 	input_unregister_device(ts->input);
 
 	ads7846_suspend(spi, PMSG_SUSPEND);
 
-	sysfs_remove_group(&spi->dev.kobj, ts->attr_group);
+	sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
 
 	free_irq(ts->spi->irq, ts);
 	/* suspend left the IRQ disabled */
 	enable_irq(ts->spi->irq);
 
+	if (ts->filter_cleanup)
+		ts->filter_cleanup(ts->filter_data);
+
 	kfree(ts);
 
 	dev_dbg(&spi->dev, "unregistered touchscreen\n");
Index: linux-2.6.20-at91/include/linux/spi/ads7846.h
===================================================================
--- linux-2.6.20-at91.orig/include/linux/spi/ads7846.h
+++ linux-2.6.20-at91/include/linux/spi/ads7846.h
@@ -5,9 +5,17 @@
  *
  * It's OK if the min/max values are zero.
  */
+enum ads7846_filter {
+	ADS7846_FILTER_OK,
+	ADS7846_FILTER_REPEAT,
+	ADS7846_FILTER_IGNORE,
+};
+
 struct ads7846_platform_data {
 	u16	model;			/* 7843, 7845, 7846. */
 	u16	vref_delay_usecs;	/* 0 for external vref; etc */
+	int	keep_vref_on:1;		/* set to keep vref on for differential
+					 * measurements as well */
 	u16	x_plate_ohms;
 	u16	y_plate_ohms;
 
@@ -21,5 +29,9 @@ struct ads7846_platform_data {
 	u16	debounce_rep;		/* additional consecutive good readings
 					 * required after the first two */
 	int	(*get_pendown_state)(void);
+	int	(*filter_init)	(struct ads7846_platform_data *pdata,
+				 void **filter_data);
+	int	(*filter)	(void *filter_data, int data_idx, int *val);
+	void	(*filter_cleanup)(void *filter_data);
 };
 
Index: linux-2.6.20-at91/arch/arm/mach-at91rm9200/board-sam9263ek.c
===================================================================
--- linux-2.6.20-at91.orig/arch/arm/mach-at91rm9200/board-sam9263ek.c
+++ linux-2.6.20-at91/arch/arm/mach-at91rm9200/board-sam9263ek.c
@@ -25,6 +25,11 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+#include <linux/fb.h>
+#include <linux/clk.h>
+
+#include <video/atmel_lcdc.h>
 
 #include <asm/hardware.h>
 #include <asm/setup.h>
@@ -84,6 +89,40 @@ static struct at91_udc_data __initdata e
 	.pullup_pin	= 0,		/* pull-up driven by UDC */
 };
 
+/*
+ * Touchscreen ads7843
+ */
+#if defined(CONFIG_TOUCHSCREEN_ADS7846) || defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE)
+
+int ads7843_pendown_state(void)
+{
+	return !at91_get_gpio_value(AT91_PIN_PA15);
+}
+
+static struct ads7846_platform_data ads_info = {
+	.model			= 7843,
+	.x_min	= 150,	.x_max	= 3830,
+	.y_min	= 190,	.y_max	= 3830,
+	.vref_delay_usecs	= 100,
+	.x_plate_ohms		= 450,
+	.y_plate_ohms		= 250,
+	.pressure_max		= 15000,
+	.debounce_max		= 1,
+	.debounce_rep		= 0,
+	.debounce_tol		= (~0),
+	.get_pendown_state	= ads7843_pendown_state,
+};
+
+void __init at91_add_device_ts(void)
+{
+	/* Configure Interrupt 1 as external IRQ, with pullup */
+	at91_set_B_periph(AT91_PIN_PA15, 1);		/* IRQ1 */
+	/* ts busy */
+	at91_set_gpio_input(AT91_PIN_PA31, 1);
+}
+#else
+void __init at91_add_device_ts(void) {}
+#endif
 
 /*
  * SPI devices.
@@ -97,8 +136,25 @@ static struct spi_board_info ek_spi_devi
 		.bus_num	= 0,
 	},
 #endif
+#if defined(CONFIG_TOUCHSCREEN_ADS7846) || defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE)
+	{
+		.modalias	= "ads7846",
+		.chip_select	= 3,
+		.max_speed_hz	= 125000	/* max sample rate at 3V */
+					* 26,	/* command + data + overhead */
+		.bus_num	= 0,
+		.platform_data	= &ads_info,
+		.irq		= AT91SAM9263_ID_IRQ1,
+	},
+#endif
 };
 
+/*
+ * MACB device
+ */
+static struct  __initdata  at91_eth_data ek_macb_data = {
+	.is_rmii = 1,
+};
 
 /*
  * MCI (SD/MMC)
@@ -147,6 +203,66 @@ static struct at91_nand_data __initdata 
 #endif
 };
 
+/*
+ * LCD Controller
+ */
+#if defined(CONFIG_FB_ATMEL) || defined(CONFIG_FB_ATMEL_MODULE)
+static struct fb_videomode at91_tft_vga_modes[] = {
+	{
+	        .name           = "TX09D50VM1CCA @ 60",
+		.refresh	= 60,
+		.xres		= 240,		.yres		= 320,
+		.pixclock	= KHZ2PICOS(4965),
+
+		.left_margin	= 1,		.right_margin	= 33,
+		.upper_margin	= 1,		.lower_margin	= 0,
+		.hsync_len	= 5,		.vsync_len	= 1,
+
+		.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+};
+
+static struct fb_monspecs at91fb_default_monspecs = {
+	.manufacturer	= "HIT",
+	.monitor        = "TX09D70VM1CCA",
+
+        .modedb		= at91_tft_vga_modes,
+	.modedb_len	= ARRAY_SIZE(at91_tft_vga_modes),
+	.hfmin		= 15000,
+	.hfmax		= 64000,
+	.vfmin		= 50,
+	.vfmax		= 150,
+};
+
+/* Driver defaults */
+#define AT91SAM9261_DEFAULT_LCDCON2 	(ATMEL_LCDC_MEMOR_LITTLE \
+					| ATMEL_LCDC_DISTYPE_TFT    \
+					| ATMEL_LCDC_CLKMOD_ALWAYSACTIVE)
+
+#define AT91SAM9261_DEFAULT_FB_FLAGS	(FBINFO_DEFAULT \
+					| FBINFO_PARTIAL_PAN_OK \
+					| FBINFO_HWACCEL_XPAN \
+					| FBINFO_HWACCEL_YPAN)
+
+/* Driver datas */
+static struct atmel_lcdfb_info __initdata ek_lcdc_data = {
+	.default_bpp = 16,
+	.default_dmacon = ATMEL_LCDC_DMAEN,
+	.default_lcdcon2 = AT91SAM9261_DEFAULT_LCDCON2,
+	.default_monspecs = &at91fb_default_monspecs,
+	.default_flags = AT91SAM9261_DEFAULT_FB_FLAGS,
+	.power_control_pin = AT91_PIN_PD12,
+	.guard_time = 1,
+};
+
+#else
+static struct atmel_lcdfb_info __initdata ek_lcdc_data;
+#endif
+
+struct atmel_ac97_data ek_ac97_data = {
+	.reset_pin = AT91_PIN_PA13,
+};
 
 static void __init ek_board_init(void)
 {
@@ -156,12 +272,22 @@ static void __init ek_board_init(void)
 	at91_add_device_usbh(&ek_usbh_data);
 	/* USB Device */
 	at91_add_device_udc(&ek_udc_data);
+	/* select SPI clk for Dataflash card slot */
+	at91_set_gpio_output(AT91_PIN_PE20, 1);
 	/* SPI */
 	at91_add_device_spi(ek_spi_devices, ARRAY_SIZE(ek_spi_devices));
 	/* MMC */
 	at91_add_device_mmc(1, &ek_mmc_data);
+	/* MACB */
+	at91_add_device_eth(&ek_macb_data);
 	/* NAND */
 	at91_add_device_nand(&ek_nand_data);
+	/* LCD Controller */
+	at91_add_device_lcdc(&ek_lcdc_data);
+	/* Touchscreen */
+	at91_add_device_ts();
+	/* AC97 */
+	at91_add_device_ac97(&ek_ac97_data);
 }
 
 MACHINE_START(AT91SAM9263EK, "Atmel AT91SAM9263-EK")
Index: linux-2.6.20-at91/arch/arm/mach-at91rm9200/at91sam9263.c
===================================================================
--- linux-2.6.20-at91.orig/arch/arm/mach-at91rm9200/at91sam9263.c
+++ linux-2.6.20-at91/arch/arm/mach-at91rm9200/at91sam9263.c
@@ -107,8 +107,13 @@ static struct clk tcb_clk = {
 	.pmc_mask	= 1 << AT91SAM9263_ID_TCB,
 	.type		= CLK_TYPE_PERIPHERAL,
 };
-static struct clk ether_clk = {
-	.name		= "ether_clk",
+static struct clk ac97_clk = {
+	.name		= "ac97_clk",
+	.pmc_mask	= 1 << AT91SAM9263_ID_AC97C,
+	.type		= CLK_TYPE_PERIPHERAL,
+};
+static struct clk macb_clk = {
+	.name		= "macb_clk",
 	.pmc_mask	= 1 << AT91SAM9263_ID_EMAC,
 	.type		= CLK_TYPE_PERIPHERAL,
 };
@@ -124,7 +129,7 @@ static struct clk isi_clk = {
 };
 static struct clk lcdc_clk = {
 	.name		= "lcdc_clk",
-	.pmc_mask	= 1 << AT91SAM9263_ID_ISI,
+	.pmc_mask	= 1 << AT91SAM9263_ID_LCDC,
 	.type		= CLK_TYPE_PERIPHERAL,
 };
 static struct clk ohci_clk = {
@@ -147,10 +152,10 @@ static struct clk *periph_clocks[] __ini
 	&spi0_clk,
 	&spi1_clk,
 	// ssc0 .. ssc1
-	// ac97
+	&ac97_clk,
 	&tcb_clk,
 	// pwmc
-	&ether_clk,
+	&macb_clk,
 	// 2dge
 	&udc_clk,
 	&isi_clk,
Index: linux-2.6.20-at91/arch/arm/mach-at91rm9200/at91sam9263_devices.c
===================================================================
--- linux-2.6.20-at91.orig/arch/arm/mach-at91rm9200/at91sam9263_devices.c
+++ linux-2.6.20-at91/arch/arm/mach-at91rm9200/at91sam9263_devices.c
@@ -13,6 +13,12 @@
 #include <asm/mach/map.h>
 
 #include <linux/platform_device.h>
+#include <linux/fb.h>
+
+#include <video/atmel_lcdc.h>
+
+#include <asm/delay.h>
+
 
 #include <asm/arch/board.h>
 #include <asm/arch/gpio.h>
@@ -459,6 +465,63 @@ void __init at91_add_device_i2c(void)
 void __init at91_add_device_i2c(void) {}
 #endif
 
+/* --------------------------------------------------------------------
+ *  AC97
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_SND_AT91_AC97) || defined(CONFIG_SND_AT91_AC97_MODULE)
+static u64 ac97_dmamask = 0xffffffffUL;
+static struct atmel_ac97_data ac97_data;
+
+static struct resource ac97_resources[] = {
+	[0] = {
+		.start	= AT91SAM9263_BASE_AC97C,
+		.end	= AT91SAM9263_BASE_AC97C + SZ_16K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9263_ID_AC97C,
+		.end	= AT91SAM9263_ID_AC97C,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device at91sam9263_ac97_device = {
+	.name		= "ac97c",
+	.id		= 1,
+	.dev		= {
+				.dma_mask		= &ac97_dmamask,
+				.coherent_dma_mask	= 0xffffffff,
+				.platform_data		= &ac97_data,
+	},
+	.resource	= ac97_resources,
+	.num_resources	= ARRAY_SIZE(ac97_resources),
+};
+
+void __init at91_add_device_ac97(struct atmel_ac97_data *ek_data)
+{
+	/* Set platform data */
+	ac97_data = *ek_data;
+
+	/* pins used for AC97 interface */
+	at91_set_A_periph(AT91_PIN_PB0, 0);  /* AC97FS */
+	at91_set_A_periph(AT91_PIN_PB1, 0);  /* AC97CK */
+	at91_set_A_periph(AT91_PIN_PB2, 0);  /* AC97TX */
+	at91_set_A_periph(AT91_PIN_PB3, 0);  /* AC97RX */
+
+	/* reset */
+	at91_set_gpio_output(ac97_data.reset_pin, 0);
+	at91_set_gpio_value(ac97_data.reset_pin, 0);
+	udelay(10);
+	at91_set_gpio_value(ac97_data.reset_pin, 1);
+
+	platform_device_register(&at91sam9263_ac97_device);
+}
+#else
+void __init at91_add_device_ac97(struct atmel_ac97_data *ek_data) {}
+#endif
+
+
 
 /* --------------------------------------------------------------------
  *  SPI
@@ -573,6 +636,77 @@ void __init at91_add_device_spi(struct s
 
 
 /* --------------------------------------------------------------------
+ *  LCD Controller
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_FB_ATMEL) || defined(CONFIG_FB_ATMEL_MODULE)
+static u64 lcdc_dmamask = 0xffffffffUL;
+static struct atmel_lcdfb_info lcdc_data;
+
+static struct resource lcdc_resources[] = {
+	[0] = {
+		.start	= AT91SAM9263_LCDC_BASE,
+		.end	= AT91SAM9263_LCDC_BASE + SZ_4K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9263_ID_LCDC,
+		.end	= AT91SAM9263_ID_LCDC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device at91_lcdc_device = {
+	.name           = "atmel_lcdfb",
+	.id             = 0,
+	.dev            = {
+				.dma_mask          = &lcdc_dmamask,
+				.coherent_dma_mask = 0xffffffff,
+				.platform_data     = &lcdc_data,
+	},
+	.resource       = lcdc_resources,
+	.num_resources  = ARRAY_SIZE(lcdc_resources),
+};
+
+void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data)
+{
+	if (!data) {
+		return;
+	}
+
+	/* configure PIO for LCDC */
+        at91_set_A_periph(AT91_PIN_PC1, 0);	/* LCDHSYNC */
+        at91_set_A_periph(AT91_PIN_PC2, 0);	/* LCDDOTCK */
+        at91_set_A_periph(AT91_PIN_PC3, 0);	/* LCDDEN */
+        at91_set_B_periph(AT91_PIN_PB9, 0);	/* LCDCC */
+        at91_set_A_periph(AT91_PIN_PC6, 0);	/* LCDD2 */
+        at91_set_A_periph(AT91_PIN_PC7, 0);	/* LCDD3 */
+        at91_set_A_periph(AT91_PIN_PC8, 0);	/* LCDD4 */
+        at91_set_A_periph(AT91_PIN_PC9, 0);	/* LCDD5 */
+        at91_set_A_periph(AT91_PIN_PC10, 0);	/* LCDD6 */
+        at91_set_A_periph(AT91_PIN_PC11, 0);	/* LCDD7 */
+        at91_set_A_periph(AT91_PIN_PC14, 0);	/* LCDD10 */
+        at91_set_A_periph(AT91_PIN_PC15, 0);	/* LCDD11 */
+        at91_set_A_periph(AT91_PIN_PC16, 0);	/* LCDD12 */
+        at91_set_B_periph(AT91_PIN_PC12, 0);	/* LCDD13 */
+        at91_set_A_periph(AT91_PIN_PC18, 0);	/* LCDD14 */
+        at91_set_A_periph(AT91_PIN_PC19, 0);	/* LCDD15 */
+        at91_set_A_periph(AT91_PIN_PC22, 0);	/* LCDD18 */
+        at91_set_A_periph(AT91_PIN_PC23, 0);	/* LCDD19 */
+        at91_set_A_periph(AT91_PIN_PC24, 0);	/* LCDD20 */
+        at91_set_B_periph(AT91_PIN_PC17, 0);	/* LCDD21 */
+        at91_set_A_periph(AT91_PIN_PC26, 0);	/* LCDD22 */
+        at91_set_A_periph(AT91_PIN_PC27, 0);	/* LCDD23 */
+
+	lcdc_data = *data;
+	platform_device_register(&at91_lcdc_device);
+}
+#else
+void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data) {}
+#endif
+
+
+/* --------------------------------------------------------------------
  *  LEDs
  * -------------------------------------------------------------------- */
 
Index: linux-2.6.20-at91/include/asm-arm/arch-at91rm9200/board.h
===================================================================
--- linux-2.6.20-at91.orig/include/asm-arm/arch-at91rm9200/board.h
+++ linux-2.6.20-at91/include/asm-arm/arch-at91rm9200/board.h
@@ -34,6 +34,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/device.h>
 #include <linux/spi/spi.h>
+#include <video/atmel_lcdc.h>
 
  /* USB Device */
 struct at91_udc_data {
@@ -98,6 +99,9 @@ extern void __init at91_add_device_i2c(v
  /* SPI */
 extern void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices);
 
+ /* LCD Controler */
+extern void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data);
+
  /* Serial */
 struct at91_uart_config {
 	unsigned short	console_tty;	/* tty number of serial console */
@@ -128,4 +132,18 @@ struct at91_gpio_led {
 };
 extern void __init at91_gpio_leds(struct at91_gpio_led *leds, int nr);
 
+/* SSC + AT73C213 */
+struct atmel_at73c213_data {
+	struct clk *at73_mck; 	// MCK AT73C213 clock   
+	unsigned int ssc_div;   // ssc divisor
+};
+extern void __init at91_add_device_ssc_at71c213(void);
+
+/* AC97 */
+struct atmel_ac97_data {
+	u8 reset_pin;
+};
+
+extern void __init at91_add_device_ac97(struct atmel_ac97_data *ek_data);
+
 #endif
Index: linux-2.6.20-at91/include/video/atmel_lcdc.h
===================================================================
--- /dev/null
+++ linux-2.6.20-at91/include/video/atmel_lcdc.h
@@ -0,0 +1,193 @@
+/*
+ *  include/video/atmel_lcdc.h
+ *
+ *  Header file for AT91/AT32 LCD Controller
+ *
+ *  Data structure and register user interface
+ *
+ *  Copyright (C) 2007 Atmel Corporation
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ATMEL_LCDC_H__
+#define __ATMEL_LCDC_H__
+
+ /* LCD Controller info data structure */
+struct atmel_lcdfb_info {
+	spinlock_t		lock;
+	struct fb_info		*info;
+	void __iomem		*mmio;
+	unsigned long		irq_base;
+	dma_addr_t		map_dma;
+	void			*map_cpu;
+	size_t			map_size;
+
+	unsigned int		guard_time;
+	struct platform_device	*pdev;
+	struct clk		*bus_clk;
+	struct clk		*lcdc_clk;
+	unsigned int		default_bpp;
+	unsigned int		default_lcdcon2;
+	unsigned int		default_dmacon;
+	int			default_flags;
+	u8			power_control_pin;
+	struct fb_monspecs	 *default_monspecs;
+};
+
+#define ATMEL_LCDC_DMABADDR1	0x00		/* DMA Base Address Register 1 */
+#define ATMEL_LCDC_DMABADDR2	0x04		/* DMA Base Address Register 2 */
+#define ATMEL_LCDC_DMAFRMPT1	0x08		/* DMA Frame Pointer Register 1 */
+#define ATMEL_LCDC_DMAFRMPT2	0x0c		/* DMA Frame Pointer Register 2 */
+#define ATMEL_LCDC_DMAFRMADD1	0x10		/* DMA Frame Address Register 1 */
+#define ATMEL_LCDC_DMAFRMADD2	0x14		/* DMA Frame Address Register 2 */
+
+#define ATMEL_LCDC_DMAFRMCFG	0x18		/* DMA Frame Configuration Register */
+#define		ATMEL_LCDC_FRSIZE	(0x7fffff <<  0)	/* Frame Size */
+#define		ATMEL_LCDC_BLENGTH_OFFSET	24		/* Burst Length */
+#define		ATMEL_LCDC_BLENGTH	(0x7f     << ATMEL_LCDC_BLENGTH_OFFSET)
+
+#define ATMEL_LCDC_DMACON	0x1c		/* DMA Control Register */
+#define		ATMEL_LCDC_DMAEN	(0x1 << 0)	/* DMA Enable */
+#define		ATMEL_LCDC_DMARST	(0x1 << 1)	/* DMA Reset */
+#define		ATMEL_LCDC_DMABUSY	(0x1 << 2)	/* DMA Busy */
+
+#define ATMEL_LCDC_LCDCON1	0x0800		/* LCD Control Register 1 */
+#define		ATMEL_LCDC_BYPASS	(1     <<  0)	/* Bypass lcd_dotck divider */
+#define		ATMEL_LCDC_CLKVAL_OFFSET	12	/* Clock Divider */
+#define		ATMEL_LCDC_CLKVAL	(0x1ff << ATMEL_LCDC_CLKVAL_OFFSET)
+#define		ATMEL_LCDC_LINCNT	(0x7ff << 21)	/* Line Counter */
+
+#define ATMEL_LCDC_LCDCON2	0x0804		/* LCD Control Register 2 */
+#define		ATMEL_LCDC_DISTYPE	(3 << 0)	/* Display Type */
+#define			ATMEL_LCDC_DISTYPE_STNMONO	(0 << 0)
+#define			ATMEL_LCDC_DISTYPE_STNCOLOR	(1 << 0)
+#define			ATMEL_LCDC_DISTYPE_TFT		(2 << 0)
+#define		ATMEL_LCDC_SCANMOD	(1 << 2)	/* Scan Mode */
+#define			ATMEL_LCDC_SCANMOD_SINGLE	(0 << 2)
+#define			ATMEL_LCDC_SCANMOD_DUAL		(1 << 2)
+#define		ATMEL_LCDC_IFWIDTH	(3 << 3)	/*Interface Width */
+#define			ATMEL_LCDC_IFWIDTH_4		(0 << 3)
+#define			ATMEL_LCDC_IFWIDTH_8		(1 << 3)
+#define			ATMEL_LCDC_IFWIDTH_16		(2 << 3)
+#define		ATMEL_LCDC_PIXELSIZE	(7 << 5)	/* Bits per pixel */
+#define			ATMEL_LCDC_PIXELSIZE_1		(0 << 5)
+#define			ATMEL_LCDC_PIXELSIZE_2		(1 << 5)
+#define			ATMEL_LCDC_PIXELSIZE_4		(2 << 5)
+#define			ATMEL_LCDC_PIXELSIZE_8		(3 << 5)
+#define			ATMEL_LCDC_PIXELSIZE_16		(4 << 5)
+#define			ATMEL_LCDC_PIXELSIZE_24		(5 << 5)
+#define			ATMEL_LCDC_PIXELSIZE_32		(6 << 5)
+#define		ATMEL_LCDC_INVVD	(1 << 8)	/* LCD Data polarity */
+#define			ATMEL_LCDC_INVVD_NORMAL		(0 << 8)
+#define			ATMEL_LCDC_INVVD_INVERTED	(1 << 8)
+#define		ATMEL_LCDC_INVFRAME	(1 << 9 )	/* LCD VSync polarity */
+#define			ATMEL_LCDC_INVFRAME_NORMAL	(0 << 9)
+#define			ATMEL_LCDC_INVFRAME_INVERTED	(1 << 9)
+#define		ATMEL_LCDC_INVLINE	(1 << 10)	/* LCD HSync polarity */
+#define			ATMEL_LCDC_INVLINE_NORMAL	(0 << 10)
+#define			ATMEL_LCDC_INVLINE_INVERTED	(1 << 10)
+#define		ATMEL_LCDC_INVCLK	(1 << 11)	/* LCD dotclk polarity */
+#define			ATMEL_LCDC_INVCLK_NORMAL	(0 << 11)
+#define			ATMEL_LCDC_INVCLK_INVERTED	(1 << 11)
+#define		ATMEL_LCDC_INVDVAL	(1 << 12)	/* LCD dval polarity */
+#define			ATMEL_LCDC_INVDVAL_NORMAL	(0 << 12)
+#define			ATMEL_LCDC_INVDVAL_INVERTED	(1 << 12)
+#define		ATMEL_LCDC_CLKMOD	(1 << 15)	/* LCD dotclk mode */
+#define			ATMEL_LCDC_CLKMOD_ACTIVEDISPLAY	(0 << 15)
+#define			ATMEL_LCDC_CLKMOD_ALWAYSACTIVE	(1 << 15)
+#define		ATMEL_LCDC_MEMOR	(1 << 31)	/* Memory Ordering Format */
+#define			ATMEL_LCDC_MEMOR_BIG		(0 << 31)
+#define			ATMEL_LCDC_MEMOR_LITTLE		(1 << 31)
+
+#define ATMEL_LCDC_TIM1		0x0808		/* LCD Timing Register 1 */
+#define		ATMEL_LCDC_VFP		(0xff <<  0)	/* Vertical Front Porch */
+#define		ATMEL_LCDC_VBP_OFFSET		8	/* Vertical Back Porch */
+#define		ATMEL_LCDC_VBP		(0xff <<  ATMEL_LCDC_VBP_OFFSET)
+#define		ATMEL_LCDC_VPW_OFFSET		16	/* Vertical Synchronization Pulse Width */
+#define		ATMEL_LCDC_VPW		(0x3f << ATMEL_LCDC_VPW_OFFSET)
+#define		ATMEL_LCDC_VHDLY_OFFSET		24	/* Vertical to Horizontal Delay */
+#define		ATMEL_LCDC_VHDLY	(0xf  << ATMEL_LCDC_VHDLY_OFFSET)
+
+#define ATMEL_LCDC_TIM2		0x080c		/* LCD Timing Register 2 */
+#define		ATMEL_LCDC_HBP		(0xff  <<  0)	/* Horizontal Back Porch */
+#define		ATMEL_LCDC_HPW_OFFSET		8	/* Horizontal Synchronization Pulse Width */
+#define		ATMEL_LCDC_HPW		(0x3f  <<  ATMEL_LCDC_HPW_OFFSET)
+#define		ATMEL_LCDC_HFP_OFFSET		21	/* Horizontal Front Porch */
+#define		ATMEL_LCDC_HFP		(0x7ff << ATMEL_LCDC_HFP_OFFSET)
+
+#define ATMEL_LCDC_LCDFRMCFG	0x0810		/* LCD Frame Configuration Register */
+#define		ATMEL_LCDC_LINEVAL	(0x7ff <<  0)	/* Vertical Size of LCD Module */
+#define		ATMEL_LCDC_HOZVAL_OFFSET	21	/* Horizontal Size of LCD Module */
+#define		ATMEL_LCDC_HOZVAL	(0x7ff << ATMEL_LCDC_HOZVAL_OFFSET)
+
+#define ATMEL_LCDC_FIFO		0x0814		/* LCD FIFO Register */
+#define		ATMEL_LCDC_FIFOTH	(0xffff)	/* FIFO Threshold */
+
+#define ATMEL_LCDC_MVAL		0x0818		/* LCD Mode Toggle Rate Value Register */
+
+#define ATMEL_LCDC_DP1_2		0x081c		/* Dithering Pattern DP1_2 Register */
+#define ATMEL_LCDC_DP4_7		0x0820		/* Dithering Pattern DP4_7 Register */
+#define ATMEL_LCDC_DP3_5		0x0824		/* Dithering Pattern DP3_5 Register */
+#define ATMEL_LCDC_DP2_3		0x0828		/* Dithering Pattern DP2_3 Register */
+#define ATMEL_LCDC_DP5_7		0x082c		/* Dithering Pattern DP5_7 Register */
+#define ATMEL_LCDC_DP3_4		0x0830		/* Dithering Pattern DP3_4 Register */
+#define ATMEL_LCDC_DP4_5		0x0834		/* Dithering Pattern DP4_5 Register */
+#define ATMEL_LCDC_DP6_7		0x0838		/* Dithering Pattern DP6_7 Register */
+#define		ATMEL_LCDC_DP1_2_VAL	(0xff)
+#define		ATMEL_LCDC_DP4_7_VAL	(0xfffffff)
+#define		ATMEL_LCDC_DP3_5_VAL	(0xfffff)
+#define		ATMEL_LCDC_DP2_3_VAL	(0xfff)
+#define		ATMEL_LCDC_DP5_7_VAL	(0xfffffff)
+#define		ATMEL_LCDC_DP3_4_VAL	(0xffff)
+#define		ATMEL_LCDC_DP4_5_VAL	(0xfffff)
+#define		ATMEL_LCDC_DP6_7_VAL	(0xfffffff)
+
+#define ATMEL_LCDC_PWRCON	0x083c		/* Power Control Register */
+#define		ATMEL_LCDC_PWR		(1    <<  0)	/* LCD Module Power Control */
+#define		ATMEL_LCDC_GUARDT_OFFSET	1	/* Delay in Frame Period */
+#define		ATMEL_LCDC_GUARDT	(0x7f <<  ATMEL_LCDC_GUARDT_OFFSET)
+#define		ATMEL_LCDC_BUSY		(1    << 31)	/* LCD Busy */
+
+#define ATMEL_LCDC_CONTRAST_CTR	0x0840		/* Contrast Control Register */
+#define		ATMEL_LCDC_PS		(3 << 0)	/* Contrast Counter Prescaler */
+#define			ATMEL_LCDC_PS_DIV1		(0 << 0)
+#define			ATMEL_LCDC_PS_DIV2		(1 << 0)
+#define			ATMEL_LCDC_PS_DIV4		(2 << 0)
+#define			ATMEL_LCDC_PS_DIV8		(3 << 0)
+#define		ATMEL_LCDC_POL		(1 << 2)	/* Polarity of output Pulse */
+#define			ATMEL_LCDC_POL_NEGATIVE		(0 << 2)
+#define			ATMEL_LCDC_POL_POSITIVE		(1 << 2)
+#define		ATMEL_LCDC_ENA		(1 << 3)	/* PWM generator Control */
+#define			ATMEL_LCDC_ENA_PWMDISABLE	(0 << 3)
+#define			ATMEL_LCDC_ENA_PWMENABLE	(1 << 3)
+
+#define ATMEL_LCDC_CONTRAST_VAL	0x0844		/* Contrast Value Register */
+#define		ATMEL_LCDC_CVAL	(0xff)		/* PWM compare value */
+
+#define ATMEL_LCDC_IER		0x0848		/* Interrupt Enable Register */
+#define ATMEL_LCDC_IDR		0x084c		/* Interrupt Disable Register */
+#define ATMEL_LCDC_IMR		0x0850		/* Interrupt Mask Register */
+#define ATMEL_LCDC_ISR		0x0854		/* Interrupt Status Register */
+#define ATMEL_LCDC_ICR		0x0858		/* Interrupt Clear Register */
+#define		ATMEL_LCDC_LNI		(1 << 0)	/* Line Interrupt */
+#define		ATMEL_LCDC_LSTLNI	(1 << 1)	/* Last Line Interrupt */
+#define		ATMEL_LCDC_EOFI		(1 << 2)	/* DMA End Of Frame Interrupt */
+#define		ATMEL_LCDC_UFLWI	(1 << 4)	/* FIFO Underflow Interrupt */
+#define		ATMEL_LCDC_OWRI		(1 << 5)	/* FIFO Overwrite Interrupt */
+#define		ATMEL_LCDC_MERI		(1 << 6)	/* DMA Memory Error Interrupt */
+
+#define ATMEL_LCDC_LUT_(n)	(0x0c00 + ((n)*4))	/* Palette Entry 0..255 */
+
+#endif /* __ATMEL_LCDC_H__ */
Index: linux-2.6.20-at91/drivers/video/atmel_lcdfb.c
===================================================================
--- /dev/null
+++ linux-2.6.20-at91/drivers/video/atmel_lcdfb.c
@@ -0,0 +1,781 @@
+/*
+ *  drivers/video/atmel_lcdfb.c
+ *
+ *  Driver for AT91/AVR32 LCD Controller
+ *
+ *  Copyright (C) 2007 Atmel Corporation
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <asm/arch/board.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/gpio.h>
+
+#include <video/atmel_lcdc.h>
+
+#define lcdc_readl(sinfo, reg)		__raw_readl((sinfo)->mmio+(reg))
+#define lcdc_writel(sinfo, reg, val)	__raw_writel((val), (sinfo)->mmio+(reg))
+
+/* More or less configurable parameters */
+#define ATMEL_LCDC_FIFO_SIZE		512
+#define ATMEL_LCDC_CRST_VAL_DEFAULT	0xc8
+#define ATMEL_LCDC_DMA_BURST_LEN	16
+
+#define LCD_POWER_ON	0
+#define LCD_POWER_OFF	1
+
+#if defined(CONFIG_ARCH_AT91)
+static inline void atmel_lcdfb_update_dma2d(struct fb_info *info,
+					struct fb_var_screeninfo *var) { }
+static inline void atmel_lcdfb_set_2dcfg(struct fb_info *info) { }
+#elif defined(CONFIG_AVR32)
+static void atmel_lcdfb_update_dma2d(struct fb_info *info,
+					struct fb_var_screeninfo *var)
+{
+	dma2dcfg = lcdc_readl(sinfo, ATMEL_LCDC_DMA2DCFG);
+	dma2dcfg = LCDC_INSBF(DMA2DCFG_PIXELOFF, pixeloff, dma2dcfg);
+	lcdc_writel(sinfo, ATMEL_LCDC_DMA2DCFG, dma2dcfg);
+
+	/* Update configuration */
+	lcdc_writel(sinfo, ATMEL_LCDC_DMACON,
+		lcdc_readl(sinfo, ATMEL_LCDC_DMACON) | LCDC_BIT(DMACON_DMAUPDT));
+}
+
+static void atmel_lcdfb_set_2dcfg(struct fb_info *info)
+{
+	/* ...set 2D configuration (necessary for xres_virtual != xres) */
+	value = LCDC_MKBF(DMA2DCFG_ADDRINC,
+			  info->var.xres_virtual - info->var.xres);
+	lcdc_writel(sinfo, DMA2DCFG, value);
+
+	/* ...wait for DMA engine to become idle... */
+	while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & LCDC_BIT(DMACON_DMABUSY))
+		msleep(10);
+}
+#endif
+
+
+static struct fb_fix_screeninfo atmel_lcdfb_fix __initdata = {
+	.type		= FB_TYPE_PACKED_PIXELS,
+	.visual		= FB_VISUAL_TRUECOLOR,
+	.xpanstep	= 0,
+	.ypanstep	= 0,
+	.ywrapstep	= 0,
+	.accel		= FB_ACCEL_NONE,
+};
+
+static u32 pseudo_palette[16] = {
+	0x000000,
+	0xaa0000,
+	0x00aa00,
+	0xaa5500,
+	0x0000aa,
+	0xaa00aa,
+	0x00aaaa,
+	0xaaaaaa,
+	0x555555,
+	0xff5555,
+	0x55ff55,
+	0xffff55,
+	0x5555ff,
+	0xff55ff,
+	0x55ffff,
+	0xffffff
+};
+
+static void atmel_lcdfb_update_dma(struct fb_info *info,
+			       struct fb_var_screeninfo *var)
+{
+	struct atmel_lcdfb_info *sinfo = info->par;
+	struct fb_fix_screeninfo *fix = &info->fix;
+	unsigned long dma_addr;
+
+	dma_addr = (fix->smem_start + var->yoffset * fix->line_length
+		    + var->xoffset * var->bits_per_pixel / 8);
+
+	dma_addr &= ~3UL;
+
+	/* Set framebuffer DMA base address and pixel offset */
+	lcdc_writel(sinfo, ATMEL_LCDC_DMABADDR1, dma_addr);
+
+	atmel_lcdfb_update_dma2d(info, var);
+}
+
+static inline void atmel_lcdfb_unmap_video_memory(struct atmel_lcdfb_info *sinfo)
+{
+	struct fb_info *info = sinfo->info;
+
+	dma_free_writecombine(info->device, sinfo->map_size,
+				sinfo->map_cpu, sinfo->map_dma);
+}
+
+/**
+ *	atmel_lcdfb_alloc_framebuffer - Allocate framebuffer memory
+ *	@sinfo: the frame buffer to allocate memory for
+ */
+static int atmel_lcdfb_map_video_memory(struct atmel_lcdfb_info *sinfo)
+{
+	struct fb_info *info = sinfo->info;
+	struct fb_var_screeninfo *var = &info->var;
+
+	sinfo->map_size = (var->xres_virtual * var->yres_virtual
+			    * ((var->bits_per_pixel + 7) / 8));
+
+	sinfo->map_cpu = dma_alloc_writecombine(info->device, sinfo->map_size,
+					      &sinfo->map_dma, GFP_KERNEL);
+
+	if (!sinfo->map_cpu) {
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/**
+ *      atmel_lcdfb_check_var - Validates a var passed in.
+ *      @var: frame buffer variable screen structure
+ *      @info: frame buffer structure that represents a single frame buffer
+ *
+ *	Checks to see if the hardware supports the state requested by
+ *	var passed in. This function does not alter the hardware
+ *	state!!!  This means the data stored in struct fb_info and
+ *	struct atmel_lcdfb_info do not change. This includes the var
+ *	inside of struct fb_info.  Do NOT change these. This function
+ *	can be called on its own if we intent to only test a mode and
+ *	not actually set it. The stuff in modedb.c is a example of
+ *	this. If the var passed in is slightly off by what the
+ *	hardware can support then we alter the var PASSED in to what
+ *	we can do. If the hardware doesn't support mode change a
+ *	-EINVAL will be returned by the upper layers. You don't need
+ *	to implement this function then. If you hardware doesn't
+ *	support changing the resolution then this function is not
+ *	needed. In this case the driver would just provide a var that
+ *	represents the static state the screen is in.
+ *
+ *	Returns negative errno on error, or zero on success.
+ */
+static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var,
+			     struct fb_info *info)
+{
+	struct device *dev = info->device;
+	struct atmel_lcdfb_info *sinfo = info->par;
+	unsigned long clk_value_khz = 0;
+
+	clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000;
+
+	dev_dbg(dev, "%s:\n", __func__);
+	dev_dbg(dev, "  resolution: %ux%u\n", var->xres, var->yres);
+	dev_dbg(dev, "  pixclk:     %lu KHz\n", PICOS2KHZ(var->pixclock));
+	dev_dbg(dev, "  bpp:        %u\n", var->bits_per_pixel);
+	dev_dbg(dev, "  clk:        %lu KHz\n", clk_value_khz);
+
+	if ((PICOS2KHZ(var->pixclock) * var->bits_per_pixel / 8) > clk_value_khz) {
+		dev_err(dev, "%lu KHz pixel clock is too fast\n", PICOS2KHZ(var->pixclock));
+		return -EINVAL;
+	}
+
+	/* Force same alignment for each line */
+	var->xres = (var->xres + 3) & ~3UL;
+	var->xres_virtual = (var->xres_virtual + 3) & ~3UL;
+
+	var->red.msb_right = var->green.msb_right = var->blue.msb_right = 0;
+	var->transp.offset = var->transp.length = 0;
+
+	switch (var->bits_per_pixel) {
+	case 2:
+	case 4:
+	case 8:
+		var->red.offset = var->green.offset = var->blue.offset = 0;
+		var->red.length = var->green.length = var->blue.length
+			= var->bits_per_pixel;
+		break;
+	case 15:
+	case 16:
+		var->red.offset = 0;
+		var->green.offset = 5;
+		var->blue.offset = 10;
+		var->red.length = var->green.length = var->blue.length = 5;
+		break;
+	case 24:
+	case 32:
+		var->red.offset = 16;
+		var->green.offset = 8;
+		var->blue.offset = 0;
+		var->red.length = var->green.length = var->blue.length = 8;
+		break;
+	default:
+		dev_err(dev, "color depth %d not supported\n",
+					var->bits_per_pixel);
+		return -EINVAL;
+	}
+
+	var->xoffset = var->yoffset = 0;
+	var->red.msb_right = var->green.msb_right = var->blue.msb_right =
+		var->transp.msb_right = 0;
+
+	return 0;
+}
+
+/**
+ *      atmel_lcdfb_set_par - Alters the hardware state.
+ *      @info: frame buffer structure that represents a single frame buffer
+ *
+ *	Using the fb_var_screeninfo in fb_info we set the resolution
+ *	of the this particular framebuffer. This function alters the
+ *	par AND the fb_fix_screeninfo stored in fb_info. It doesn't
+ *	not alter var in fb_info since we are using that data. This
+ *	means we depend on the data in var inside fb_info to be
+ *	supported by the hardware.  atmel_lcdfb_check_var is always called
+ *	before atmel_lcdfb_set_par to ensure this.  Again if you can't
+ *	change the resolution you don't need this function.
+ *
+ */
+static int atmel_lcdfb_set_par(struct fb_info *info)
+{
+	struct atmel_lcdfb_info *sinfo = info->par;
+	unsigned long value;
+	unsigned long clk_value_khz = 0;
+
+	dev_dbg(info->device, "%s:\n", __func__);
+	dev_dbg(info->device, "  * resolution: %ux%u (%ux%u virtual)\n",
+		 info->var.xres, info->var.yres,
+		 info->var.xres_virtual, info->var.yres_virtual);
+
+	/* Turn off the LCD controller and the DMA controller */
+	lcdc_writel(sinfo, ATMEL_LCDC_PWRCON, sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET);
+
+	lcdc_writel(sinfo, ATMEL_LCDC_DMACON, 0);
+
+	/* Reset LCDC DMA*/
+	lcdc_writel(sinfo, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMARST);
+
+	if (info->var.bits_per_pixel <= 8)
+		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+	else
+		info->fix.visual = FB_VISUAL_TRUECOLOR;
+
+	info->fix.line_length = info->var.xres_virtual * (info->var.bits_per_pixel / 8);
+
+	/* Re-initialize the DMA engine... */
+	dev_dbg(info->device, "  * update DMA engine\n");
+	atmel_lcdfb_update_dma(info, &info->var);
+
+	/* ...set frame size and burst length = 8 words (?) */
+	value = (info->var.yres * info->var.xres * info->var.bits_per_pixel) / 32;
+	value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET);
+	lcdc_writel(sinfo, ATMEL_LCDC_DMAFRMCFG, value);
+
+	atmel_lcdfb_set_2dcfg(info);
+
+	/* Now, the LCDC core... */
+
+	/* Set pixel clock */
+	clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000;
+
+	value = clk_value_khz / PICOS2KHZ(info->var.pixclock);
+
+	if (clk_value_khz % PICOS2KHZ(info->var.pixclock))
+		value++;
+
+	value = (value / 2) - 1;
+
+	if (value == 0) {
+		dev_notice(info->device, "Bypassing pixel clock divider\n");
+		lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS);
+	} else
+		lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, value << ATMEL_LCDC_CLKVAL_OFFSET);
+
+	/* Initialize control register 2 */
+	value = sinfo->default_lcdcon2;
+
+	if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))
+		value |= ATMEL_LCDC_INVLINE_INVERTED;
+	if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))
+		value |= ATMEL_LCDC_INVFRAME_INVERTED;
+
+	switch (info->var.bits_per_pixel) {
+		case 1:	value |= ATMEL_LCDC_PIXELSIZE_1; break;
+		case 2: value |= ATMEL_LCDC_PIXELSIZE_2; break;
+		case 4: value |= ATMEL_LCDC_PIXELSIZE_4; break;
+		case 8: value |= ATMEL_LCDC_PIXELSIZE_8; break;
+		case 15: /* fall through */
+		case 16: value |= ATMEL_LCDC_PIXELSIZE_16; break;
+		case 24: value |= ATMEL_LCDC_PIXELSIZE_24; break;
+		case 32: value |= ATMEL_LCDC_PIXELSIZE_32; break;
+		default: BUG(); break;
+	}
+	dev_dbg(info->device, "  * LCDCON2 = %08lx\n", value);
+	lcdc_writel(sinfo, ATMEL_LCDC_LCDCON2, value);
+
+	/* Vertical timing */
+	value = (info->var.vsync_len - 1) << ATMEL_LCDC_VPW_OFFSET;
+	value |= info->var.upper_margin << ATMEL_LCDC_VBP_OFFSET;
+	value |= info->var.lower_margin;
+	dev_dbg(info->device, "  * LCDTIM1 = %08lx\n", value);
+	lcdc_writel(sinfo, ATMEL_LCDC_TIM1, value);
+
+	/* Horizontal timing */
+	value = (info->var.right_margin - 1) << ATMEL_LCDC_HFP_OFFSET;
+	value |= (info->var.hsync_len - 1) << ATMEL_LCDC_HPW_OFFSET;
+	value |= (info->var.left_margin - 1);
+	dev_dbg(info->device, "  * LCDTIM2 = %08lx\n", value);
+	lcdc_writel(sinfo, ATMEL_LCDC_TIM2, value);
+
+	/* Display size */
+	value = (info->var.xres - 1) << ATMEL_LCDC_HOZVAL_OFFSET;
+	value |= info->var.yres - 1;
+	lcdc_writel(sinfo, ATMEL_LCDC_LCDFRMCFG, value);
+
+	/* FIFO Threshold: Use formula from data sheet */
+	value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3);
+	lcdc_writel(sinfo, ATMEL_LCDC_FIFO, value);
+
+	/* Toggle LCD_MODE every frame */
+	lcdc_writel(sinfo, ATMEL_LCDC_MVAL, 0);
+
+	/* Disable all interrupts */
+	lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL);
+
+	// Set contrast
+	value = ATMEL_LCDC_PS_DIV8 | ATMEL_LCDC_POL_POSITIVE | ATMEL_LCDC_ENA_PWMENABLE;
+	lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, value);
+	lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CRST_VAL_DEFAULT);
+	/* ...wait for DMA engine to become idle... */
+	while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY)
+		msleep(10);
+
+	dev_dbg(info->device, "  * re-enable DMA engine\n");
+	/* ...and enable it with updated configuration */
+	lcdc_writel(sinfo, ATMEL_LCDC_DMACON, sinfo->default_dmacon);
+
+	dev_dbg(info->device, "  * re-enable LCDC core\n");
+	lcdc_writel(sinfo, ATMEL_LCDC_PWRCON,
+		(sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR);
+
+	dev_dbg(info->device, "  * DONE\n");
+
+	return 0;
+}
+
+static inline u_int chan_to_field(u_int chan, const struct fb_bitfield *bf)
+{
+	chan &= 0xffff;
+	chan >>= 16 - bf->length;
+	return chan << bf->offset;
+}
+
+/**
+ *  	atmel_lcdfb_setcolreg - Optional function. Sets a color register.
+ *      @regno: Which register in the CLUT we are programming
+ *      @red: The red value which can be up to 16 bits wide
+ *	@green: The green value which can be up to 16 bits wide
+ *	@blue:  The blue value which can be up to 16 bits wide.
+ *	@transp: If supported the alpha value which can be up to 16 bits wide.
+ *      @info: frame buffer info structure
+ *
+ *  	Set a single color register. The values supplied have a 16 bit
+ *  	magnitude which needs to be scaled in this function for the hardware.
+ *	Things to take into consideration are how many color registers, if
+ *	any, are supported with the current color visual. With truecolor mode
+ *	no color palettes are supported. Here a psuedo palette is created
+ *	which we store the value in pseudo_palette in struct fb_info. For
+ *	pseudocolor mode we have a limited color palette. To deal with this
+ *	we can program what color is displayed for a particular pixel value.
+ *	DirectColor is similar in that we can program each color field. If
+ *	we have a static colormap we don't need to implement this function.
+ *
+ *	Returns negative errno on error, or zero on success. In an
+ *	ideal world, this would have been the case, but as it turns
+ *	out, the other drivers return 1 on failure, so that's what
+ *	we're going to do.
+ */
+static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red,
+			     unsigned int green, unsigned int blue,
+			     unsigned int transp, struct fb_info *info)
+{
+	struct atmel_lcdfb_info *sinfo = info->par;
+	unsigned int val;
+	u32 *pal;
+	int ret = 1;
+
+	if (info->var.grayscale)
+		red = green = blue = (19595 * red + 38470 * green
+				      + 7471 * blue) >> 16;
+
+	switch (info->fix.visual) {
+	case FB_VISUAL_TRUECOLOR:
+		if (regno < 16) {
+			pal = info->pseudo_palette;
+
+			val  = chan_to_field(red, &info->var.red);
+			val |= chan_to_field(green, &info->var.green);
+			val |= chan_to_field(blue, &info->var.blue);
+
+			pal[regno] = val;
+			ret = 0;
+		}
+		break;
+
+	case FB_VISUAL_PSEUDOCOLOR:
+		if (regno < 256) {
+			val  = ((red   >> 11) & 0x001f);
+			val |= ((green >>  6) & 0x03e0);
+			val |= ((blue  >>  1) & 0x7c00);
+
+			/*
+			 * TODO: intensity bit. Maybe something like
+			 *   ~(red[10] ^ green[10] ^ blue[10]) & 1
+			 */
+
+			lcdc_writel(sinfo, ATMEL_LCDC_LUT_(regno), val);
+			ret = 0;
+		}
+		break;
+	}
+
+	return ret;
+}
+
+static int atmel_lcdfb_pan_display(struct fb_var_screeninfo *var,
+			       struct fb_info *info)
+{
+	dev_dbg(info->device, "%s\n", __func__);
+
+	atmel_lcdfb_update_dma(info, var);
+
+	return 0;
+}
+
+static struct fb_ops atmel_lcdfb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_check_var	= atmel_lcdfb_check_var,
+	.fb_set_par	= atmel_lcdfb_set_par,
+	.fb_setcolreg	= atmel_lcdfb_setcolreg,
+	.fb_pan_display	= atmel_lcdfb_pan_display,
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_imageblit,
+};
+
+static irqreturn_t atmel_lcdfb_interrupt(int irq, void *dev_id)
+{
+	struct fb_info *info = dev_id;
+	struct atmel_lcdfb_info *sinfo = info->par;
+	u32 status;
+
+	status = lcdc_readl(sinfo, ATMEL_LCDC_ISR);
+	lcdc_writel(sinfo, ATMEL_LCDC_IDR, status);
+	return IRQ_HANDLED;
+}
+
+static int atmel_lcdfb_init_fbinfo(struct atmel_lcdfb_info *sinfo)
+{
+	struct fb_info *info = sinfo->info;
+	int ret = 0;
+
+	info->screen_base = sinfo->map_cpu;
+	info->fix.smem_start = sinfo->map_dma;
+	info->fix.smem_len = sinfo->map_size;
+
+	memset(info->screen_base, 0, info->fix.smem_len);
+	info->var.activate |= FB_ACTIVATE_FORCE | FB_ACTIVATE_NOW;
+
+	dev_info(info->device,
+	       "%luKiB frame buffer at %08lx (mapped at %p)\n",
+	       (unsigned long)info->fix.smem_len / 1024,
+	       (unsigned long)info->fix.smem_start,
+	       info->screen_base);
+
+	/* Allocate colormap */
+	ret = fb_alloc_cmap(&info->cmap, 256, 0);
+	if (ret < 0)
+		dev_err(info->device, "Alloc color map failed\n");
+
+	return ret;
+}
+
+static int atmel_lcdfb_start_clock(struct atmel_lcdfb_info *sinfo)
+{
+	int ret = 0;
+
+	if (sinfo->bus_clk && !IS_ERR(sinfo->bus_clk))
+		clk_enable(sinfo->bus_clk);
+	else {
+		if (cpu_is_at91sam9261()) {
+			/* not an error for other cpus */
+			ret = -ENXIO;
+		}
+	}
+
+	if (sinfo->lcdc_clk && !IS_ERR(sinfo->lcdc_clk))
+		clk_enable(sinfo->lcdc_clk);
+	else
+		ret = -ENXIO;
+
+	return ret;
+}
+
+static void atmel_lcdfb_stop_clock(struct atmel_lcdfb_info *sinfo)
+{
+	if (sinfo->bus_clk && !IS_ERR(sinfo->bus_clk))
+		clk_disable(sinfo->bus_clk);
+	if (sinfo->lcdc_clk && !IS_ERR(sinfo->lcdc_clk))
+		clk_disable(sinfo->lcdc_clk);
+}
+
+
+static int atmel_lcdfb_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct fb_info *info;
+	struct atmel_lcdfb_info *sinfo;
+	struct resource *regs = NULL;
+	struct resource *map = NULL;
+	int ret;
+
+	dev_dbg(dev, "%s BEGIN\n", __func__);
+
+	ret = -ENOMEM;
+	info = framebuffer_alloc(sizeof(struct atmel_lcdfb_info), dev);
+	if (!info) {
+		dev_err(dev, "cannot allocate memory\n");
+		goto out;
+	}
+
+	sinfo = info->par;
+	memcpy(sinfo, dev->platform_data, sizeof(struct atmel_lcdfb_info));
+	sinfo->info = info;
+	sinfo->pdev = pdev;
+
+	strcpy(info->fix.id, sinfo->pdev->name);
+	info->flags = sinfo->default_flags;
+	info->pseudo_palette = pseudo_palette;
+	info->fbops = &atmel_lcdfb_ops;
+
+	memcpy(&info->monspecs, sinfo->default_monspecs, sizeof(info->monspecs));
+	info->fix = atmel_lcdfb_fix;
+
+	/* Enable LCDC Clocks */
+	if (cpu_is_at91sam9261())
+		sinfo->bus_clk = clk_get(dev, "hck1");
+	sinfo->lcdc_clk = clk_get(dev, "lcdc_clk");
+	if (atmel_lcdfb_start_clock(sinfo)) {
+		dev_err(dev, "unable to clock LCDC\n");
+		goto free_info;
+	}
+
+	ret = fb_find_mode(&info->var, info, NULL, info->monspecs.modedb,
+			info->monspecs.modedb_len, info->monspecs.modedb,
+			sinfo->default_bpp);
+	if (!ret) {
+		dev_err(dev, "no suitable video mode found\n");
+		goto stop_clk;
+	}
+
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs) {
+		dev_err(dev, "resources unusable\n");
+		ret = -ENXIO;
+		goto stop_clk;
+	}
+
+	sinfo->irq_base = platform_get_irq(pdev, 0);
+	if (sinfo->irq_base < 0) {
+		dev_err(dev, "unable to get irq\n");
+		ret = sinfo->irq_base;
+		goto stop_clk;
+	}
+
+	/* Initialize video memory */
+	map = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (map) {
+		/* use a pre-allocated memory buffer */
+		sinfo->map_dma = map->start;
+		sinfo->map_size = map->end - map->start + 1;
+		if (!request_mem_region(sinfo->map_dma,
+					sinfo->map_size, pdev->name)) {
+			ret = -EBUSY;
+			goto stop_clk;
+		}
+
+		sinfo->map_cpu = ioremap(sinfo->map_dma, sinfo->map_size);
+		if (!sinfo->map_cpu)
+			goto release_intmem;
+	} else {
+		/* alocate memory buffer */
+		ret = atmel_lcdfb_map_video_memory(sinfo);
+		if (ret < 0) {
+			dev_err(dev, "cannot allocate framebuffer: %d\n", ret);
+			goto stop_clk;
+		}
+	}
+
+	/* LCDC registers */
+	info->fix.mmio_start = regs->start;
+	info->fix.mmio_len = regs->end - regs->start + 1;
+
+	if (!request_mem_region(info->fix.mmio_start,
+				info->fix.mmio_len, pdev->name)) {
+		ret = -EBUSY;
+		goto free_fb;
+	}
+
+	sinfo->mmio = ioremap(info->fix.mmio_start, info->fix.mmio_len);
+	if (!sinfo->mmio) {
+		dev_err(dev, "cannot map LCDC registers\n");
+		goto release_mem;
+	}
+
+	/* interrupt */
+	ret = request_irq(sinfo->irq_base, atmel_lcdfb_interrupt, 0, pdev->name, info);
+	if (ret) {
+		dev_err(dev, "request_irq failed: %d\n", ret);
+		goto unmap_mmio;
+	}
+
+	ret = atmel_lcdfb_init_fbinfo(sinfo);
+	if (ret < 0) {
+		dev_err(dev, "init fbinfo failed: %d\n", ret);
+		goto unregister_irqs;
+	}
+
+	/*
+	 * This makes sure that our colour bitfield
+	 * descriptors are correctly initialised.
+	 */
+	atmel_lcdfb_check_var(&info->var, info);
+	atmel_lcdfb_set_par(info);
+
+	ret = fb_set_var(info, &info->var);
+	if (ret) {
+		dev_warn(dev, "unable to set display parameters\n");
+		goto free_cmap;
+	}
+
+	dev_set_drvdata(dev, info);
+
+	/*
+	 * Tell the world that we're ready to go
+	 */
+	ret = register_framebuffer(info);
+	if (ret < 0) {
+		dev_err(dev, "failed to register framebuffer device: %d\n", ret);
+		goto free_cmap;
+	}
+
+	/* Power up the LCDC screen */
+	if (sinfo->power_control_pin)
+		at91_set_gpio_value(sinfo->power_control_pin, LCD_POWER_ON);
+
+	dev_info(dev, "fb%d: Atmel LCDC at 0x%08lx (mapped at %p), irq %lu\n",
+		       info->node, info->fix.mmio_start, sinfo->mmio, sinfo->irq_base);
+
+	return 0;
+
+
+free_cmap:
+	fb_dealloc_cmap(&info->cmap);
+unregister_irqs:
+	free_irq(sinfo->irq_base, info);
+unmap_mmio:
+	iounmap(sinfo->mmio);
+release_mem:
+ 	release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
+free_fb:
+	if (map) {
+		iounmap(sinfo->map_cpu);
+	} else {
+		atmel_lcdfb_unmap_video_memory(sinfo);
+	}
+
+release_intmem:
+	if (map) {
+		release_mem_region(sinfo->map_dma, sinfo->map_size);
+	}
+stop_clk:
+	atmel_lcdfb_stop_clock(sinfo);
+free_info:
+	framebuffer_release(info);
+out:
+	dev_dbg(dev, "%s FAILED\n", __func__);
+	return ret;
+}
+
+static int atmel_lcdfb_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct atmel_lcdfb_info *sinfo = info->par;
+
+	if (!sinfo)
+		return 0;
+
+	if (sinfo->power_control_pin)
+		at91_set_gpio_value(sinfo->power_control_pin, LCD_POWER_OFF);
+	unregister_framebuffer(info);
+	atmel_lcdfb_stop_clock(sinfo);
+	fb_dealloc_cmap(&info->cmap);
+	free_irq(sinfo->irq_base, info);
+	iounmap(sinfo->mmio);
+ 	release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
+	if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) {
+		iounmap(sinfo->map_cpu);
+		release_mem_region(sinfo->map_dma, sinfo->map_size);
+	} else {
+		atmel_lcdfb_unmap_video_memory(sinfo);
+	}
+
+	dev_set_drvdata(dev, NULL);
+	framebuffer_release(info);
+
+	return 0;
+}
+
+static struct platform_driver atmel_lcdfb_driver = {
+	.probe		= atmel_lcdfb_probe,
+	.remove		= atmel_lcdfb_remove,
+	.driver		= {
+		.name	= "atmel_lcdfb",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init atmel_lcdfb_init(void)
+{
+	return platform_driver_register(&atmel_lcdfb_driver);
+}
+
+static void __exit atmel_lcdfb_exit(void)
+{
+	platform_driver_unregister(&atmel_lcdfb_driver);
+}
+
+module_init(atmel_lcdfb_init);
+module_exit(atmel_lcdfb_exit);
+
+MODULE_AUTHOR("Atmel Corporation");
+MODULE_DESCRIPTION("AT91/AT32 LCD Controller framebuffer driver");
+MODULE_LICENSE("GPL");
Index: linux-2.6.20-at91/drivers/video/Kconfig
===================================================================
--- linux-2.6.20-at91.orig/drivers/video/Kconfig
+++ linux-2.6.20-at91/drivers/video/Kconfig
@@ -698,6 +698,22 @@ config FB_S1D13XXX
 	  working with S1D13806). Product specs at
 	  <http://www.erd.epson.com/vdc/html/legacy_13xxx.htm>
 
+config FB_ATMEL
+	tristate "AT91/AT32 LCD Controller support"
+	depends on FB && (ARCH_AT91SAM9261 || ARCH_AT91SAM9263 || AVR32)
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	help
+	  This enables support for the AT91/AT32 LCD Controller.
+
+config FB_INTSRAM
+	bool "Frame Buffer in internal SRAM"
+	depends on FB_ATMEL && ARCH_AT91SAM9261
+	help
+	  Say Y if you want to map Frame Buffer in internal SRAM. Say N if you want
+	  to let frame buffer in external SDRAM.
+
 config FB_NVIDIA
 	tristate "nVidia Framebuffer Support"
 	depends on FB && PCI
Index: linux-2.6.20-at91/drivers/video/Makefile
===================================================================
--- linux-2.6.20-at91.orig/drivers/video/Makefile
+++ linux-2.6.20-at91/drivers/video/Makefile
@@ -78,6 +78,7 @@ obj-$(CONFIG_FB_SA1100)           += sa1
 obj-$(CONFIG_FB_SUN3)             += sun3fb.o
 obj-$(CONFIG_FB_HIT)              += hitfb.o
 obj-$(CONFIG_FB_EPSON1355)	  += epson1355fb.o
+obj-$(CONFIG_FB_ATMEL)		  += atmel_lcdfb.o
 obj-$(CONFIG_FB_PVR2)             += pvr2fb.o
 obj-$(CONFIG_FB_VOODOO1)          += sstfb.o
 obj-$(CONFIG_FB_ARMCLCD)	  += amba-clcd.o
Index: linux-2.6.20-at91/arch/arm/mach-at91rm9200/board-sam9261ek.c
===================================================================
--- linux-2.6.20-at91.orig/arch/arm/mach-at91rm9200/board-sam9261ek.c
+++ linux-2.6.20-at91/arch/arm/mach-at91rm9200/board-sam9261ek.c
@@ -26,6 +26,11 @@
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
 #include <linux/dm9000.h>
+#include <linux/spi/ads7846.h>
+#include <linux/fb.h>
+#include <linux/clk.h>
+
+#include <video/atmel_lcdc.h>
 
 #include <asm/hardware.h>
 #include <asm/setup.h>
@@ -148,6 +153,105 @@ static struct at91_udc_data __initdata e
 
 
 /*
+ * Touchscreen ads7843
+ */
+#if defined(CONFIG_TOUCHSCREEN_ADS7846) || defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE)
+
+int ads7843_pendown_state(void)
+{
+	return !at91_get_gpio_value(AT91_PIN_PC2);
+}
+
+static struct ads7846_platform_data ads_info = {
+	.model			= 7843,
+	.x_min	= 150,	.x_max	= 3830,
+	.y_min	= 190,	.y_max	= 3830,
+	.vref_delay_usecs	= 100,
+	.x_plate_ohms		= 450,
+	.y_plate_ohms		= 250,
+	.pressure_max		= 15000,
+	.debounce_max		= 1,
+	.debounce_rep		= 0,
+	.debounce_tol		= (~0),
+	.get_pendown_state	= ads7843_pendown_state,
+};
+
+void __init at91_add_device_ts(void)
+{
+	/* Configure Interrupt 1 as external IRQ, with pullup */
+	at91_set_B_periph(AT91_PIN_PC2, 1);		/* IRQ0 */
+	/* ts busy */
+	at91_set_gpio_input(AT91_PIN_PA11, 1);
+}
+#else
+void __init at91_add_device_ts(void) {}
+#endif
+
+/*
+ * AT73C213
+ */
+#if defined(CONFIG_SND_AT73C213) || defined(CONFIG_SND_AT73C213_MODULE)
+static struct atmel_at73c213_data at73c213_data;
+
+static u64 ssc1_dmamask = 0xffffffffUL;
+static struct resource ssc1_resource[] = {
+	[0] = {
+		.start	= AT91SAM9261_BASE_SSC1,
+		.end	= AT91SAM9261_BASE_SSC1 + SZ_16K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9261_ID_SSC1,
+		.end	= AT91SAM9261_ID_SSC1,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device at91sam9261_ssc1_device = {
+	.name		= "atmel_ssc_at73c213",
+	.id			= -1,
+	.dev		= {
+				.dma_mask		= &ssc1_dmamask,
+				.coherent_dma_mask	= 0xffffffff,
+				.platform_data		= &at73c213_data,
+	},
+	.resource		= ssc1_resource,
+	.num_resources	= ARRAY_SIZE(ssc1_resource),
+};
+void __init at91_add_device_ssc1_at73c213(void)
+{
+	struct clk *at73_clk;
+	struct clk *ssc_clk;
+	struct clk *parent_clk;
+
+	/* Set SSC1 IO */
+	at91_set_B_periph(AT91_PIN_PA17, 0);		/* TD1 */
+	at91_set_B_periph(AT91_PIN_PA18, 0);		/* TF1 */
+	at91_set_B_periph(AT91_PIN_PA19, 0);		/* TK1 */
+
+	/* AT73C213 MCK Clock */
+	at91_set_B_periph(AT91_PIN_PB31, 0);		/* PCK2 */
+
+	at73_clk = clk_get(NULL, "pck2");
+	parent_clk = clk_get(NULL, "plla");
+	clk_set_parent(at73_clk, parent_clk);
+	clk_set_rate(at73_clk, 12416000);
+	clk_enable(at73_clk);
+
+	ssc_clk = clk_get(NULL, "ssc1_clk");
+	clk_enable(ssc_clk);
+	
+	at73c213_data.ssc_div  = 32;  
+	at73c213_data.at73_mck = at73_clk;  
+
+	platform_device_register(&at91sam9261_ssc1_device);
+}
+#else
+	void __init at91_add_device_ssc1_at73c213(void) {}
+#endif
+
+
+/*
  * MCI (SD/MMC)
  */
 static struct at91_mmc_data __initdata ek_mmc_data = {
@@ -204,6 +308,17 @@ static struct spi_board_info ek_spi_devi
 		.max_speed_hz	= 15 * 1000 * 1000,
 		.bus_num	= 0,
 	},
+#if defined(CONFIG_TOUCHSCREEN_ADS7846) || defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE)
+	{
+		.modalias	= "ads7846",
+		.chip_select	= 2,
+		.max_speed_hz	= 125000	/* max sample rate at 3V */
+					* 26,	/* command + data + overhead */
+		.bus_num	= 0,
+		.platform_data	= &ads_info,
+		.irq		= AT91SAM9261_ID_IRQ0,
+	},
+#endif
 #if defined(CONFIG_MTD_AT91_DATAFLASH_CARD)
 	{	/* DataFlash card - jumper (J12) configurable to CS3 or CS0 */
 		.modalias	= "mtd_dataflash",
@@ -211,9 +326,9 @@ static struct spi_board_info ek_spi_devi
 		.max_speed_hz	= 15 * 1000 * 1000,
 		.bus_num	= 0,
 	},
-#elif defined(CONFIG_SND_AT73C213)
+#elif defined(CONFIG_SND_AT73C213) || defined(CONFIG_SND_AT73C213_MODULE)
 	{	/* AT73C213 DAC */
-		.modalias	= "snd_at73c213",
+		.modalias	= "at73c213",
 		.chip_select	= 3,
 		.max_speed_hz	= 10 * 1000 * 1000,
 		.bus_num	= 0,
@@ -222,6 +337,64 @@ static struct spi_board_info ek_spi_devi
 };
 
 
+/*
+ * LCD Controller
+ */
+#if defined(CONFIG_FB_ATMEL) || defined(CONFIG_FB_ATMEL_MODULE)
+static struct fb_videomode at91_tft_vga_modes[] = {
+	{
+	        .name           = "TX09D50VM1CCA @ 60",
+		.refresh	= 60,
+		.xres		= 240,		.yres		= 320,
+		.pixclock	= KHZ2PICOS(4965),
+
+		.left_margin	= 1,		.right_margin	= 33,
+		.upper_margin	= 1,		.lower_margin	= 0,
+		.hsync_len	= 5,		.vsync_len	= 1,
+
+		.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+};
+
+static struct fb_monspecs at91fb_default_monspecs = {
+	.manufacturer	= "HIT",
+	.monitor        = "TX09D50VM1CCA",
+
+        .modedb		= at91_tft_vga_modes,
+	.modedb_len	= ARRAY_SIZE(at91_tft_vga_modes),
+	.hfmin		= 15000,
+	.hfmax		= 64000,
+	.vfmin		= 50,
+	.vfmax		= 150,
+};
+
+/* Driver defaults */
+#define AT91SAM9261_DEFAULT_LCDCON2 	(ATMEL_LCDC_MEMOR_LITTLE \
+					| ATMEL_LCDC_DISTYPE_TFT    \
+					| ATMEL_LCDC_CLKMOD_ALWAYSACTIVE)
+
+#define AT91SAM9261_DEFAULT_FB_FLAGS	(FBINFO_DEFAULT \
+					| FBINFO_PARTIAL_PAN_OK \
+					| FBINFO_HWACCEL_XPAN \
+					| FBINFO_HWACCEL_YPAN)
+
+/* Driver datas */
+static struct atmel_lcdfb_info __initdata ek_lcdc_data = {
+	.default_bpp = 16,
+	.default_dmacon = ATMEL_LCDC_DMAEN,
+	.default_lcdcon2 = AT91SAM9261_DEFAULT_LCDCON2,
+	.default_monspecs = &at91fb_default_monspecs,
+	.default_flags = AT91SAM9261_DEFAULT_FB_FLAGS,
+	.power_control_pin = AT91_PIN_PA12,
+	.guard_time = 1,
+};
+
+#else
+static struct atmel_lcdfb_info __initdata ek_lcdc_data;
+#endif
+
+
 static void __init ek_board_init(void)
 {
 	/* Serial */
@@ -245,6 +418,12 @@ static void __init ek_board_init(void)
 	/* MMC */
 	at91_add_device_mmc(0, &ek_mmc_data);
 #endif
+	/* LCD Controller */
+	at91_add_device_lcdc(&ek_lcdc_data);
+	/* Touchscreen */
+	at91_add_device_ts();
+	/* AT73C213 & SSC1 port */
+	at91_add_device_ssc1_at73c213(); 
 }
 
 MACHINE_START(AT91SAM9261EK, "Atmel AT91SAM9261-EK")
Index: linux-2.6.20-at91/arch/arm/mach-at91rm9200/at91sam9261_devices.c
===================================================================
--- linux-2.6.20-at91.orig/arch/arm/mach-at91rm9200/at91sam9261_devices.c
+++ linux-2.6.20-at91/arch/arm/mach-at91rm9200/at91sam9261_devices.c
@@ -14,6 +14,9 @@
 #include <asm/mach/map.h>
 
 #include <linux/platform_device.h>
+#include <linux/fb.h>
+
+#include <video/atmel_lcdc.h>
 
 #include <asm/arch/board.h>
 #include <asm/arch/gpio.h>
@@ -345,7 +348,13 @@ static struct platform_device at91sam926
 	.num_resources	= ARRAY_SIZE(spi0_resources),
 };
 
-static const unsigned spi0_standard_cs[4] = { AT91_PIN_PA3, AT91_PIN_PA4, AT91_PIN_PA5, AT91_PIN_PA6 };
+static const unsigned spi0_standard_cs[4] = { AT91_PIN_PA3, AT91_PIN_PA4, AT91_PIN_PA28, 
+#if defined(CONFIG_MACH_AT91SAM9261EK) && defined(CONFIG_SND_AT73C213) 
+	AT91_PIN_PA29 };
+#else
+	AT91_PIN_PA6 };
+#endif
+
 
 static struct resource spi1_resources[] = {
 	[0] = {
@@ -430,9 +439,9 @@ void __init at91_add_device_spi(struct s
  *  LCD Controller
  * -------------------------------------------------------------------- */
 
-#if defined(CONFIG_FB_AT91) || defined(CONFIG_FB_AT91_MODULE)
+#if defined(CONFIG_FB_ATMEL) || defined(CONFIG_FB_ATMEL_MODULE)
 static u64 lcdc_dmamask = 0xffffffffUL;
-static struct at91fb_info lcdc_data;
+static struct atmel_lcdfb_info lcdc_data;
 
 static struct resource lcdc_resources[] = {
 	[0] = {
@@ -455,18 +464,18 @@ static struct resource lcdc_resources[] 
 };
 
 static struct platform_device at91_lcdc_device = {
-	.name		= "at91-fb",
-	.id		= 0,
-	.dev		= {
-				.dma_mask		= &lcdc_dmamask,
-				.coherent_dma_mask	= 0xffffffff,
-				.platform_data		= &lcdc_data,
+	.name           = "atmel_lcdfb",
+	.id             = 0,
+	.dev            = {
+				.dma_mask          = &lcdc_dmamask,
+				.coherent_dma_mask = 0xffffffff,
+				.platform_data     = &lcdc_data,
 	},
 	.resource	= lcdc_resources,
 	.num_resources	= ARRAY_SIZE(lcdc_resources),
 };
 
-void __init at91_add_device_lcdc(struct at91fb_info *data)
+void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data)
 {
 	if (!data) {
 		return;
@@ -499,7 +508,7 @@ void __init at91_add_device_lcdc(struct 
 	platform_device_register(&at91_lcdc_device);
 }
 #else
-void __init at91_add_device_lcdc(struct at91fb_info *data) {}
+void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data) {}
 #endif
 
 
@@ -625,8 +634,10 @@ static inline void configure_usart0_pins
 {
 	at91_set_A_periph(AT91_PIN_PC8, 1);		/* TXD0 */
 	at91_set_A_periph(AT91_PIN_PC9, 0);		/* RXD0 */
+#if !defined(CONFIG_DM9000)
 	at91_set_A_periph(AT91_PIN_PC10, 0);		/* RTS0 */
 	at91_set_A_periph(AT91_PIN_PC11, 0);		/* CTS0 */
+#endif
 }
 
 static struct resource uart1_resources[] = {
Index: linux-2.6.20-at91/sound/arm/Kconfig
===================================================================
--- linux-2.6.20-at91.orig/sound/arm/Kconfig
+++ linux-2.6.20-at91/sound/arm/Kconfig
@@ -33,4 +33,20 @@ config SND_PXA2XX_AC97
 	  Say Y or M if you want to support any AC97 codec attached to
 	  the PXA2xx AC97 interface.
 
+config SND_AT73C213
+	tristate "Atmel AT73C213 DAC driver"
+ 	depends on SND && SPI_ATMEL && ARCH_AT91 && AT91_PROGRAMMABLE_CLOCKS
+	select SND_PCM
+	help
+	  Say Y here if you want to use the Atmel AT73C213 external DAC.
+
+config SND_AT91_AC97
+	tristate "AC97 Controller driver for SAM926X familly from ATMEL"
+	depends on SND && ARCH_AT91
+	select SND_PCM
+	select SND_AC97_CODEC
+	help
+	  Say Y or M if you want to support any AC97 codec attached to
+	  the SAM926X AC97 Controller.
+
 endmenu
Index: linux-2.6.20-at91/sound/arm/at91-ac97.c
===================================================================
--- /dev/null
+++ linux-2.6.20-at91/sound/arm/at91-ac97.c
@@ -0,0 +1,654 @@
+/* drivers/sound/arm/at91-ac97c.c
+ *
+ * Driver for the Atmel AC97 Controller
+ *
+ * Copyright (C) 2005 Atmel Norway
+ */
+
+
+#undef DEBUG
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/atmel_pdc.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/cacheflush.h>
+
+#include <asm/arch/gpio.h>
+#include <asm/arch/ac97c.h>
+#include <asm/arch/board.h>
+
+#define platform_num_resources(dev)     ((dev)->num_resources)
+#define platform_resource_start(dev, i) ((dev)->resource[(i)].start)
+#define platform_resource_end(dev, i)   ((dev)->resource[(i)].end)
+#define platform_resource_flags(dev, i) ((dev)->resource[(i)].flags)
+#define platform_resource_len(dev, i)                   \
+        (platform_resource_end((dev), (i)) -            \
+         platform_resource_start((dev), (i)) + 1)
+
+
+/* module parameters */
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for AC97 controller");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for AC97 controller");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable AC97 controller");
+
+
+
+typedef struct at91_ac97 {
+	spinlock_t lock;
+	void *regs;
+	int period;
+	struct clk *ac97_clk;
+	snd_pcm_substream_t *playback_substream;
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	ac97_t *ac97;
+	ac97_bus_t *ac97_bus;
+	int irq;
+	struct platform_device *pdev;
+	u8 reset_pin;
+} at91_ac97_t;
+
+
+
+
+#define get_chip(card) ((at91_ac97_t *)(card)->private_data)
+
+
+
+#define ac97c_writel(chip, reg, val)			\
+	writel((val), (chip)->regs + AC97C_##reg)
+
+#define ac97c_readl(chip, reg)				\
+	readl((chip)->regs + AC97C_##reg)
+
+// PIO management functions
+void at91_ac97c_drive_reset(at91_ac97_t *chip, unsigned int value)
+{
+	at91_set_gpio_value(chip->reset_pin, value);
+}
+
+
+static const char driver_name[] = "at91-ac97";
+
+/* PCM part */
+
+static snd_pcm_hardware_t snd_at91_ac97_hw = {
+	.info			= (SNDRV_PCM_INFO_INTERLEAVED
+				   | SNDRV_PCM_INFO_MMAP
+				   | SNDRV_PCM_INFO_MMAP_VALID
+                                   | SNDRV_PCM_INFO_BLOCK_TRANSFER),
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
+	.rates			= SNDRV_PCM_RATE_CONTINUOUS,
+	.rate_min		= 8000,
+	.rate_max		= 48000,
+	.channels_min		= 2,
+	.channels_max		= 2,
+	.buffer_bytes_max	= 256*1024,
+	.period_bytes_min	= 1024,
+	.period_bytes_max	= 4*1024,
+	.periods_min		= 1,
+	.periods_max		= 64,
+};
+
+static int snd_at91_ac97_playback_open(snd_pcm_substream_t *substream)
+{
+	at91_ac97_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	runtime->hw = snd_at91_ac97_hw;
+	chip->playback_substream = substream;
+	chip->period = 0;
+
+	snd_printd(KERN_DEBUG "%s : snd_at91_ac97_playback_open\n\r", driver_name);
+
+	return 0;
+}
+
+static int snd_at91_ac97_playback_close(snd_pcm_substream_t *substream)
+{
+        at91_ac97_t *chip = snd_pcm_substream_chip(substream);
+
+        chip->playback_substream = NULL;
+	return 0;
+}
+
+static int snd_at91_ac97_hw_params(snd_pcm_substream_t *substream,
+				    snd_pcm_hw_params_t *hw_params)
+{
+	int err;
+
+	err = snd_pcm_lib_malloc_pages(substream,
+				       params_buffer_bytes(hw_params));
+	return err;
+}
+
+static int snd_at91_ac97_hw_free(snd_pcm_substream_t *substream)
+{
+
+	snd_pcm_lib_free_pages(substream);
+
+	return 0;
+}
+
+static int snd_at91_ac97_playback_prepare(snd_pcm_substream_t *substream)
+{
+	at91_ac97_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int block_size = frames_to_bytes(runtime, runtime->period_size);
+	unsigned long word = 0;
+
+	//clean_dcache_region(runtime->dma_area, block_size * 2);
+	snd_printd(KERN_DEBUG "%s : block_size = %d\n\r", driver_name, block_size);
+
+	/* Assign slots to channels */
+	switch (substream->runtime->channels) {
+          /* TODO: Support more than two channels */
+        case 1:
+          word |= AT91C_AC97C_CHID3_CA;
+          break;
+	case 2:
+        default:
+          /* Assign Left and Right slots (3,4) to Channel A */
+          word |= AT91C_AC97C_CHID3_CA | AT91C_AC97C_CHID4_CA;
+          break;
+	}
+
+	ac97c_writel(chip, OCA, word);
+
+	/*
+	 * Configure sample format and size.
+	 * FIXME: Avoid conflicts with capture channel.
+	 */
+        word = AT91C_AC97C_PDCEN | AT91C_AC97C_SIZE_16_BITS;
+
+        switch (runtime->format){
+        case SNDRV_PCM_FORMAT_S16_BE:
+          word |= AT91C_AC97C_CEM;
+          break;
+        case SNDRV_PCM_FORMAT_S16_LE:
+        default:
+          break;
+        }
+
+	ac97c_writel(chip, CAMR, word);
+
+        /* Set variable rate if needed */
+        if ( runtime->rate != 48000 ){
+          word = ac97c_readl(chip, MR);
+          word |= AT91C_AC97C_VRA;
+          ac97c_writel(chip, MR, word);
+        } else {
+          /* Clear Variable Rate Bit */
+          word = ac97c_readl(chip, MR);
+          word &= ~AT91C_AC97C_VRA;
+          ac97c_writel(chip, MR, word);
+        }
+
+        /* Set rate */
+        snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
+
+	snd_printd(KERN_DEBUG "%s : dma_addr = %x\n\r : dma_area = %x\n\r : dma_bytes = %d\n\r",
+		   driver_name, runtime->dma_addr, runtime->dma_area, runtime->dma_bytes);
+
+	/* Initialize and start the PDC */
+	writel(runtime->dma_addr, chip->regs + ATMEL_PDC_TPR);
+	writel(block_size / 2, chip->regs + ATMEL_PDC_TCR);
+	writel(runtime->dma_addr + block_size, chip->regs + ATMEL_PDC_TNPR);
+	writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR);
+
+	/* Enable Channel A interrupts */
+	ac97c_writel(chip, IER, AT91C_AC97C_CAEVT);
+
+	snd_printd(KERN_DEBUG "%s : snd_at91_ac97_playback_prepare\n\r", driver_name);
+
+	return 0;
+}
+
+static int at91_ac97_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	at91_ac97_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long camr, ptcr = 0, flags;
+	int err = 0;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	camr = ac97c_readl(chip, CAMR);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+          camr |= (AT91C_AC97C_CEN | AT91C_AC97C_ENDTX);
+	  ptcr = ATMEL_PDC_TXTEN;
+	  break;
+	case SNDRV_PCM_TRIGGER_STOP:
+          camr &= ~(AT91C_AC97C_CEN | AT91C_AC97C_ENDTX);
+	  ptcr = ATMEL_PDC_TXTDIS;
+          break;
+	default:
+          err = -EINVAL;
+          break;
+	}
+
+	ac97c_writel(chip, CAMR, camr);
+	writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	snd_printd(KERN_DEBUG "%s : snd_at91_ac97_trigger\n\r", driver_name);
+
+	return err;
+}
+
+static snd_pcm_uframes_t snd_at91_ac97_pointer(snd_pcm_substream_t *substream)
+{
+	at91_ac97_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_uframes_t pos;
+	unsigned long bytes;
+
+	bytes = readl(chip->regs + ATMEL_PDC_TPR) - runtime->dma_addr;
+
+	pos = bytes_to_frames(runtime, bytes);
+	if (pos >= runtime->buffer_size)
+		pos -= runtime->buffer_size;
+
+	snd_printd(KERN_DEBUG "%s : snd_at91_ac97_pointer\n\r", driver_name);
+
+	return pos;
+}
+
+static snd_pcm_ops_t at91_ac97_playback_ops = {
+	.open		= snd_at91_ac97_playback_open,
+	.close		= snd_at91_ac97_playback_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_at91_ac97_hw_params,
+	.hw_free	= snd_at91_ac97_hw_free,
+	.prepare	= snd_at91_ac97_playback_prepare,
+	.trigger	= at91_ac97_trigger,
+	.pointer	= snd_at91_ac97_pointer,
+};
+
+
+static struct ac97_pcm at91_ac97_pcm_defs[] __devinitdata = {
+	/* Playback */
+	{
+		.exclusive = 1,
+		.r = { {
+			.slots = ((1 << AC97_SLOT_PCM_LEFT)
+				  | (1 << AC97_SLOT_PCM_RIGHT)),
+		} },
+	},
+};
+
+static int __devinit snd_at91_ac97_pcm_new(at91_ac97_t *chip)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	err = snd_ac97_pcm_assign(chip->ac97_bus, 1, at91_ac97_pcm_defs);
+	if (err)
+		return err;
+
+	err = snd_pcm_new(chip->card, "Atmel AC97", 0, 1, 0, &pcm);
+	if (err)
+		return err;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      &chip->pdev->dev,
+					      128 * 1024, 256 * 1024);
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &at91_ac97_playback_ops);
+
+	pcm->private_data = chip;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "Atmel AC97");
+	chip->pcm = pcm;
+
+	return 0;
+}
+
+/* Mixer part */
+static int snd_at91_ac97_mixer_new(at91_ac97_t *chip)
+{
+	int err;
+	ac97_template_t template;
+
+	memset(&template, 0, sizeof(template));
+	template.private_data = chip;
+	template.num = 0;
+	template.addr = 0;
+	err = snd_ac97_mixer(chip->ac97_bus, &template, &chip->ac97);
+
+	return err;
+}
+
+
+static irqreturn_t snd_at91_ac97_interrupt(int irq, void *dev_id)
+{
+	at91_ac97_t *chip = dev_id;
+	unsigned long status;
+	unsigned long dummy;
+
+	status = ac97c_readl(chip, SR);
+
+	if (status & AT91C_AC97C_CAEVT) {
+		snd_pcm_runtime_t *runtime;
+		int offset, next_period, block_size;
+		unsigned long casr;
+
+		runtime = chip->playback_substream->runtime;
+		block_size = frames_to_bytes(runtime, runtime->period_size);
+
+		casr = ac97c_readl(chip, CASR);
+
+		if (casr & AT91C_AC97C_ENDTX) {
+			chip->period++;
+			if (chip->period == runtime->periods)
+				chip->period = 0;
+			next_period = chip->period + 1;
+			if (next_period == runtime->periods)
+				next_period = 0;
+
+			offset = block_size * next_period;
+
+			writel(runtime->dma_addr + offset, chip->regs + ATMEL_PDC_TNPR);
+			writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR);
+
+			snd_pcm_period_elapsed(chip->playback_substream);
+		} else {
+                  printk(KERN_WARNING
+                         "Spurious AC97A interrupt, status = 0x%08lx\n",
+                         (unsigned long)casr);
+		}
+	} else {
+		printk(KERN_WARNING
+		       "Spurious AC97 interrupt, status = 0x%08lx\n",
+		       status);
+	}
+
+	dummy = ac97c_readl(chip, SR);
+
+	return IRQ_HANDLED;
+}
+
+
+/* CODEC part */
+
+static void snd_at91_ac97_hard_reset(at91_ac97_t *chip)
+{
+       // Enable AC97 Controller.
+       // Perform a cold (hard) reset of the AC97 codec.
+       ac97c_writel(chip, MR, 0);
+       ac97c_writel(chip, MR, AT91C_AC97C_ENA);
+
+       at91_ac97c_drive_reset(chip, 0);
+       udelay(1);
+       at91_ac97c_drive_reset(chip, 1);
+       udelay(1);
+}
+
+
+static void snd_at91_ac97_write(ac97_t *ac97, unsigned short reg,
+				 unsigned short val)
+{
+	at91_ac97_t *chip = ac97->private_data;
+	unsigned long word;
+	int timeout = 0x100;
+
+	snd_printd(KERN_DEBUG "%s : Writing codec register 0x%x = 0x%x\n\r", driver_name, reg, val);
+
+	word = (reg & 0x7f) << 16 | val;
+
+	do {
+		if (ac97c_readl(chip, COSR) & AT91C_AC97C_TXRDY) {
+			ac97c_writel(chip, COTHR, word);
+			return;
+		}
+		udelay(1);
+	} while (--timeout);
+
+	snd_printk(KERN_WARNING "at91-ac97: codec write timeout\n\r");
+}
+
+static unsigned short snd_at91_ac97_read(ac97_t *ac97,
+					  unsigned short reg)
+{
+	at91_ac97_t *chip = ac97->private_data;
+	unsigned long word;
+	int timeout = 0x100;
+
+
+	word = (0x80 | (reg & 0x7f)) << 16;
+
+	do {
+                if (ac97c_readl(chip, COSR) & AT91C_AC97C_TXRDY){
+                     ac97c_writel(chip, COTHR, word);
+                     break;
+                }
+		udelay(1);
+	} while (--timeout);
+
+	if (!timeout)
+		goto timed_out;
+
+	timeout = 0x100;
+
+	do {
+                if (ac97c_readl(chip, COSR) & AT91C_AC97C_RXRDY){
+                     unsigned short val = (unsigned short) ac97c_readl(chip, CORHR);
+		     return val;
+		}
+		udelay(1);
+	} while (--timeout);
+
+	if (!timeout)
+		goto timed_out;
+
+timed_out:
+	snd_printk(KERN_WARNING "at91-ac97: codec read timeout\n\r");
+	return 0xffff;
+}
+
+static void snd_at91_ac97_warm_reset(ac97_t *ac97)
+{
+        at91_ac97_t *chip = ac97->private_data;
+	volatile unsigned int mr = ac97c_readl(chip, MR);
+
+	mr |= AT91C_AC97C_WRST;
+
+	ac97c_writel(chip, MR, mr);
+	udelay(1);
+
+	mr &= ~AT91C_AC97C_WRST;
+	ac97c_writel(chip, MR, mr);
+}
+
+static void snd_at91_ac97_destroy(snd_card_t *card)
+{
+	at91_ac97_t *chip = get_chip(card);
+
+	if (chip->irq != -1)
+		free_irq(chip->irq, chip);
+
+	if (chip->regs)
+		iounmap(chip->regs);
+}
+
+static int __devinit snd_at91_ac97_create(snd_card_t *card,
+					   struct platform_device *pdev)
+{
+	static ac97_bus_ops_t ops = {
+		.write	= snd_at91_ac97_write,
+		.read	= snd_at91_ac97_read,
+		.reset  = snd_at91_ac97_warm_reset,
+	};
+
+	at91_ac97_t *chip = get_chip(card);
+	int irq, err = 0;
+
+
+
+	card->private_free = snd_at91_ac97_destroy;
+
+	spin_lock_init(&chip->lock);
+	chip->card = card;
+	chip->pdev = pdev;
+	chip->irq = -1;
+
+	if (!(platform_resource_flags(pdev, 0) & IORESOURCE_MEM)
+	    || !(platform_resource_flags(pdev, 1) & IORESOURCE_IRQ))
+		return -ENODEV;
+
+	irq = platform_resource_start(pdev, 1);
+
+	err = request_irq(irq, snd_at91_ac97_interrupt, 0, "ac97", chip);
+	if (err) {
+		snd_printk(KERN_WARNING "unable to request IRQ%d\n", irq);
+		return err;
+	}
+
+	chip->irq = irq;
+    	snd_printk(KERN_INFO "AC97C regs = %08X \n", platform_resource_start(pdev, 0));
+    	snd_printk(KERN_INFO "AC97C irq  = %d \n",irq);
+
+	chip->regs = ioremap(platform_resource_start(pdev, 0),
+			     platform_resource_len(pdev, 0));
+	if (!chip->regs) {
+	        snd_printk(KERN_WARNING "unable to remap AC97C io memory\n");
+		return -ENOMEM;
+	}
+
+	snd_card_set_dev(card, &pdev->dev);
+
+	err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus);
+
+	return err;
+}
+
+static int __devinit snd_at91_ac97_probe(struct platform_device *pdev)
+{
+	static int dev;
+	struct atmel_ac97_data *pdata = pdev->dev.platform_data;
+	snd_card_t *card;
+	at91_ac97_t *chip;
+	int err;
+
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE,
+			    sizeof(at91_ac97_t));
+	if (!card)
+		return -ENOMEM;
+	chip = get_chip(card);
+
+	err = snd_at91_ac97_create(card, pdev);
+	if (err)
+		goto out_free_card;
+
+	// Enable AC97 Controller clock
+	chip->reset_pin = pdata->reset_pin;
+	chip->ac97_clk = clk_get(NULL, "ac97_clk");
+	if(!chip->ac97_clk)
+		goto out_free_card;
+
+	clk_enable(chip->ac97_clk);
+
+	// Perform a codec hard reset.
+	// This also enables the AC97 Controller.
+	snd_at91_ac97_hard_reset(chip);
+
+	err = snd_at91_ac97_mixer_new(chip);
+	if (err)
+		goto out_free_card;
+
+	err = snd_at91_ac97_pcm_new(chip);
+	if (err)
+		goto out_free_card;
+
+	strcpy(card->driver, "ac97c");
+	strcpy(card->shortname, "Atmel AC97");
+	sprintf(card->longname, "Atmel AC97 Controller at %#lx, irq %i",
+		(unsigned long) platform_resource_start(pdev, 0), (int) chip->irq);
+
+	err = snd_card_register(card);
+	if (err)
+		goto out_free_card;
+
+	dev_set_drvdata(&pdev->dev, card);
+	dev++;
+	return 0;
+
+out_free_card:
+	snd_card_free(card);
+	return err;
+}
+
+static int __devexit snd_at91_ac97_remove(struct  platform_device *pdev)
+{
+        snd_card_t *card = dev_get_drvdata(&pdev->dev);
+	at91_ac97_t *chip = get_chip(card);
+
+
+	snd_card_free(card);
+
+	// Disable AC97 Controller
+	ac97c_writel(chip, MR, 0);
+
+	// Disable AC97 Controller clock
+	clk_disable(chip->ac97_clk);
+
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver at91_ac97_driver =
+{
+	.probe      = snd_at91_ac97_probe,
+	.remove     = __devexit_p(snd_at91_ac97_remove),
+	.driver     =
+	{
+		.name       = "ac97c",
+	}
+	,
+};
+
+static int __init at91_ac97_init(void)
+{
+	return platform_driver_register(&at91_ac97_driver);
+}
+
+static void __exit at91_ac97_exit(void)
+{
+	platform_driver_unregister(&at91_ac97_driver);
+}
+
+module_init(at91_ac97_init);
+module_exit(at91_ac97_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Driver for Atmel AC97 Controller");
+MODULE_AUTHOR("Atmel");
Index: linux-2.6.20-at91/sound/arm/Makefile
===================================================================
--- linux-2.6.20-at91.orig/sound/arm/Makefile
+++ linux-2.6.20-at91/sound/arm/Makefile
@@ -13,3 +13,10 @@ 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_AT73C213)	+= snd-at73c213.o
+snd-at73c213-objs		:= at73c213.o
+
+obj-$(CONFIG_SND_AT91_AC97)	+= snd-at91-ac97.o
+snd-at91-ac97-objs		:= at91-ac97.o
+
Index: linux-2.6.20-at91/include/asm-arm/arch-at91rm9200/ac97c.h
===================================================================
--- /dev/null
+++ linux-2.6.20-at91/include/asm-arm/arch-at91rm9200/ac97c.h
@@ -0,0 +1,282 @@
+/* linux/include/asm-arm/arch-at91rm9200/ac97c.h
+ *
+ * Hardware definition for the ac97c peripheral in the ATMEL at91sam926x processor
+ *
+ * Generated  12/09/2005 (11:54:20) AT91 SW Application Group from AC97C_XXXX V1.3
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
+ * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the  GNU General Public License along
+ * with this program; if not, write  to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifndef __AC97C_H
+#define __AC97C_H
+
+/* -------------------------------------------------------- */
+/* AC97C ID definitions for  AT91SAM926x           */
+/* -------------------------------------------------------- */
+#ifndef AT91C_ID_AC97C
+#define AT91C_ID_AC97C 	18 /**< AC97 Controller id */
+#endif /* AT91C_ID_AC97C */
+
+/* -------------------------------------------------------- */
+/* AC97C Base Address definitions for  AT91SAM926x   */
+/* -------------------------------------------------------- */
+#define AT91C_BASE_AC97C     	0xFFFA0000 /**< AC97C base address */
+
+/* -------------------------------------------------------- */
+/* PIO definition for AC97C hardware peripheral */
+/* -------------------------------------------------------- */
+#define AT91C_PB1_AC97CK   	(1 << 1) /**<  */
+#define AT91C_PB0_AC97FS   	(1 << 0) /**<  */
+#define AT91C_PB3_AC97RX   	(1 << 3) /**<  */
+#define AT91C_PB2_AC97TX   	(1 << 2) /**<  */
+
+
+/* -------------------------------------------------------- */
+/* Register offset definition for AC97C hardware peripheral */
+/* -------------------------------------------------------- */
+#define AC97C_MR 	(0x0008) 	/**< Mode Register */
+#define AC97C_ICA 	(0x0010) 	/**< Input Channel AssignementRegister */
+#define AC97C_OCA 	(0x0014) 	/**< Output Channel Assignement Register */
+#define AC97C_CARHR 	(0x0020) 	/**< Channel A Receive Holding Register */
+#define AC97C_CATHR 	(0x0024) 	/**< Channel A Transmit Holding Register */
+#define AC97C_CASR 	(0x0028) 	/**< Channel A Status Register */
+#define AC97C_CAMR 	(0x002C) 	/**< Channel A Mode Register */
+#define AC97C_CBRHR 	(0x0030) 	/**< Channel B Receive Holding Register (optional) */
+#define AC97C_CBTHR 	(0x0034) 	/**< Channel B Transmit Holding Register (optional) */
+#define AC97C_CBSR 	(0x0038) 	/**< Channel B Status Register */
+#define AC97C_CBMR 	(0x003C) 	/**< Channel B Mode Register */
+#define AC97C_CORHR 	(0x0040) 	/**< COdec Transmit Holding Register */
+#define AC97C_COTHR 	(0x0044) 	/**< COdec Transmit Holding Register */
+#define AC97C_COSR 	(0x0048) 	/**< CODEC Status Register */
+#define AC97C_COMR 	(0x004C) 	/**< CODEC Mask Status Register */
+#define AC97C_SR 	(0x0050) 	/**< Status Register */
+#define AC97C_IER 	(0x0054) 	/**< Interrupt Enable Register */
+#define AC97C_IDR 	(0x0058) 	/**< Interrupt Disable Register */
+#define AC97C_IMR 	(0x005C) 	/**< Interrupt Mask Register */
+#define AC97C_VERSION 	(0x00FC) 	/**< Version Register */
+
+/* -------------------------------------------------------- */
+/* Bitfields definition for AC97C hardware peripheral */
+/* -------------------------------------------------------- */
+/* --- Register AC97C_MR */
+#define AT91C_AC97C_ENA       (0x1 << 0 ) /**< (AC97C) AC97 Controller Global Enable */
+#define AT91C_AC97C_WRST      (0x1 << 1 ) /**< (AC97C) Warm Reset */
+#define AT91C_AC97C_VRA       (0x1 << 2 ) /**< (AC97C) Variable RAte (for Data Slots) */
+/* --- Register AC97C_ICA */
+#define AT91C_AC97C_CHID3     (0x7 << 0 ) /**< (AC97C) Channel Id for the input slot 3 */
+#define 	AT91C_AC97C_CHID3_NONE                 0x0 /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID3_CA                   0x1 /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID3_CB                   0x2 /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID3_CC                   0x3 /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID4     (0x7 << 3 ) /**< (AC97C) Channel Id for the input slot 4 */
+#define 	AT91C_AC97C_CHID4_NONE                 (0x0 <<  3) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID4_CA                   (0x1 <<  3) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID4_CB                   (0x2 <<  3) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID4_CC                   (0x3 <<  3) /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID5     (0x7 << 6 ) /**< (AC97C) Channel Id for the input slot 5 */
+#define 	AT91C_AC97C_CHID5_NONE                 (0x0 <<  6) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID5_CA                   (0x1 <<  6) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID5_CB                   (0x2 <<  6) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID5_CC                   (0x3 <<  6) /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID6     (0x7 << 9 ) /**< (AC97C) Channel Id for the input slot 6 */
+#define 	AT91C_AC97C_CHID6_NONE                 (0x0 <<  9) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID6_CA                   (0x1 <<  9) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID6_CB                   (0x2 <<  9) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID6_CC                   (0x3 <<  9) /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID7     (0x7 << 12) /**< (AC97C) Channel Id for the input slot 7 */
+#define 	AT91C_AC97C_CHID7_NONE                 (0x0 << 12) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID7_CA                   (0x1 << 12) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID7_CB                   (0x2 << 12) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID7_CC                   (0x3 << 12) /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID8     (0x7 << 15) /**< (AC97C) Channel Id for the input slot 8 */
+#define 	AT91C_AC97C_CHID8_NONE                 (0x0 << 15) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID8_CA                   (0x1 << 15) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID8_CB                   (0x2 << 15) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID8_CC                   (0x3 << 15) /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID9     (0x7 << 18) /**< (AC97C) Channel Id for the input slot 9 */
+#define 	AT91C_AC97C_CHID9_NONE                 (0x0 << 18) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID9_CA                   (0x1 << 18) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID9_CB                   (0x2 << 18) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID9_CC                   (0x3 << 18) /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID10    (0x7 << 21) /**< (AC97C) Channel Id for the input slot 10 */
+#define 	AT91C_AC97C_CHID10_NONE                 (0x0 << 21) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID10_CA                   (0x1 << 21) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID10_CB                   (0x2 << 21) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID10_CC                   (0x3 << 21) /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID11    (0x7 << 24) /**< (AC97C) Channel Id for the input slot 11 */
+#define 	AT91C_AC97C_CHID11_NONE                 (0x0 << 24) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID11_CA                   (0x1 << 24) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID11_CB                   (0x2 << 24) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID11_CC                   (0x3 << 24) /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID12    (0x7 << 27) /**< (AC97C) Channel Id for the input slot 12 */
+#define 	AT91C_AC97C_CHID12_NONE                 (0x0 << 27) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID12_CA                   (0x1 << 27) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID12_CB                   (0x2 << 27) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID12_CC                   (0x3 << 27) /**< (AC97C) Channel C data will be transmitted during this slot */
+/* --- Register AC97C_OCA */
+#define AT91C_AC97C_CHID3     (0x7 << 0 ) /**< (AC97C) Channel Id for the input slot 3 */
+#define 	AT91C_AC97C_CHID3_NONE                 0x0 /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID3_CA                   0x1 /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID3_CB                   0x2 /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID3_CC                   0x3 /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID4     (0x7 << 3 ) /**< (AC97C) Channel Id for the input slot 4 */
+#define 	AT91C_AC97C_CHID4_NONE                 (0x0 <<  3) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID4_CA                   (0x1 <<  3) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID4_CB                   (0x2 <<  3) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID4_CC                   (0x3 <<  3) /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID5     (0x7 << 6 ) /**< (AC97C) Channel Id for the input slot 5 */
+#define 	AT91C_AC97C_CHID5_NONE                 (0x0 <<  6) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID5_CA                   (0x1 <<  6) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID5_CB                   (0x2 <<  6) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID5_CC                   (0x3 <<  6) /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID6     (0x7 << 9 ) /**< (AC97C) Channel Id for the input slot 6 */
+#define 	AT91C_AC97C_CHID6_NONE                 (0x0 <<  9) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID6_CA                   (0x1 <<  9) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID6_CB                   (0x2 <<  9) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID6_CC                   (0x3 <<  9) /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID7     (0x7 << 12) /**< (AC97C) Channel Id for the input slot 7 */
+#define 	AT91C_AC97C_CHID7_NONE                 (0x0 << 12) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID7_CA                   (0x1 << 12) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID7_CB                   (0x2 << 12) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID7_CC                   (0x3 << 12) /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID8     (0x7 << 15) /**< (AC97C) Channel Id for the input slot 8 */
+#define 	AT91C_AC97C_CHID8_NONE                 (0x0 << 15) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID8_CA                   (0x1 << 15) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID8_CB                   (0x2 << 15) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID8_CC                   (0x3 << 15) /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID9     (0x7 << 18) /**< (AC97C) Channel Id for the input slot 9 */
+#define 	AT91C_AC97C_CHID9_NONE                 (0x0 << 18) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID9_CA                   (0x1 << 18) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID9_CB                   (0x2 << 18) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID9_CC                   (0x3 << 18) /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID10    (0x7 << 21) /**< (AC97C) Channel Id for the input slot 10 */
+#define 	AT91C_AC97C_CHID10_NONE                 (0x0 << 21) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID10_CA                   (0x1 << 21) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID10_CB                   (0x2 << 21) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID10_CC                   (0x3 << 21) /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID11    (0x7 << 24) /**< (AC97C) Channel Id for the input slot 11 */
+#define 	AT91C_AC97C_CHID11_NONE                 (0x0 << 24) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID11_CA                   (0x1 << 24) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID11_CB                   (0x2 << 24) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID11_CC                   (0x3 << 24) /**< (AC97C) Channel C data will be transmitted during this slot */
+#define AT91C_AC97C_CHID12    (0x7 << 27) /**< (AC97C) Channel Id for the input slot 12 */
+#define 	AT91C_AC97C_CHID12_NONE                 (0x0 << 27) /**< (AC97C) No data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID12_CA                   (0x1 << 27) /**< (AC97C) Channel A data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID12_CB                   (0x2 << 27) /**< (AC97C) Channel B data will be transmitted during this slot */
+#define 	AT91C_AC97C_CHID12_CC                   (0x3 << 27) /**< (AC97C) Channel C data will be transmitted during this slot */
+/* --- Register AC97C_CARHR */
+#define AT91C_AC97C_RDATA     (0xFFFFF << 0 ) /**< (AC97C) Receive data */
+/* --- Register AC97C_CATHR */
+#define AT91C_AC97C_TDATA     (0xFFFFF << 0 ) /**< (AC97C) Transmit data */
+/* --- Register AC97C_CASR */
+#define AT91C_AC97C_TXRDY     (0x1 << 0 ) /**< (AC97C)  */
+#define AT91C_AC97C_TXEMPTY   (0x1 << 1 ) /**< (AC97C)  */
+#define AT91C_AC97C_UNRUN     (0x1 << 2 ) /**< (AC97C)  */
+#define AT91C_AC97C_RXRDY     (0x1 << 4 ) /**< (AC97C)  */
+#define AT91C_AC97C_OVRUN     (0x1 << 5 ) /**< (AC97C)  */
+#define AT91C_AC97C_ENDTX     (0x1 << 10) /**< (AC97C)  */
+#define AT91C_AC97C_TXBUFE    (0x1 << 11) /**< (AC97C)  */
+#define AT91C_AC97C_ENDRX     (0x1 << 14) /**< (AC97C)  */
+#define AT91C_AC97C_RXBUFF    (0x1 << 15) /**< (AC97C)  */
+/* --- Register AC97C_CAMR */
+#define AT91C_AC97C_TXRDY     (0x1 << 0 ) /**< (AC97C)  */
+#define AT91C_AC97C_TXEMPTY   (0x1 << 1 ) /**< (AC97C)  */
+#define AT91C_AC97C_UNRUN     (0x1 << 2 ) /**< (AC97C)  */
+#define AT91C_AC97C_RXRDY     (0x1 << 4 ) /**< (AC97C)  */
+#define AT91C_AC97C_OVRUN     (0x1 << 5 ) /**< (AC97C)  */
+#define AT91C_AC97C_ENDTX     (0x1 << 10) /**< (AC97C)  */
+#define AT91C_AC97C_TXBUFE    (0x1 << 11) /**< (AC97C)  */
+#define AT91C_AC97C_ENDRX     (0x1 << 14) /**< (AC97C)  */
+#define AT91C_AC97C_RXBUFF    (0x1 << 15) /**< (AC97C)  */
+#define AT91C_AC97C_SIZE      (0x3 << 16) /**< (AC97C)  */
+#define 	AT91C_AC97C_SIZE_20_BITS              (0x0 << 16) /**< (AC97C) Data size is 20 bits */
+#define 	AT91C_AC97C_SIZE_18_BITS              (0x1 << 16) /**< (AC97C) Data size is 18 bits */
+#define 	AT91C_AC97C_SIZE_16_BITS              (0x2 << 16) /**< (AC97C) Data size is 16 bits */
+#define 	AT91C_AC97C_SIZE_10_BITS              (0x3 << 16) /**< (AC97C) Data size is 10 bits */
+#define AT91C_AC97C_CEM       (0x1 << 18) /**< (AC97C)  */
+#define AT91C_AC97C_CEN       (0x1 << 21) /**< (AC97C)  */
+#define AT91C_AC97C_PDCEN     (0x1 << 22) /**< (AC97C)  */
+/* --- Register AC97C_CBRHR */
+#define AT91C_AC97C_RDATA     (0xFFFFF << 0 ) /**< (AC97C) Receive data */
+/* --- Register AC97C_CBTHR */
+#define AT91C_AC97C_TDATA     (0xFFFFF << 0 ) /**< (AC97C) Transmit data */
+/* --- Register AC97C_CBSR */
+#define AT91C_AC97C_TXRDY     (0x1 << 0 ) /**< (AC97C)  */
+#define AT91C_AC97C_TXEMPTY   (0x1 << 1 ) /**< (AC97C)  */
+#define AT91C_AC97C_UNRUN     (0x1 << 2 ) /**< (AC97C)  */
+#define AT91C_AC97C_RXRDY     (0x1 << 4 ) /**< (AC97C)  */
+#define AT91C_AC97C_OVRUN     (0x1 << 5 ) /**< (AC97C)  */
+/* --- Register AC97C_CBMR */
+#define AT91C_AC97C_TXRDY     (0x1 << 0 ) /**< (AC97C)  */
+#define AT91C_AC97C_TXEMPTY   (0x1 << 1 ) /**< (AC97C)  */
+#define AT91C_AC97C_UNRUN     (0x1 << 2 ) /**< (AC97C)  */
+#define AT91C_AC97C_RXRDY     (0x1 << 4 ) /**< (AC97C)  */
+#define AT91C_AC97C_OVRUN     (0x1 << 5 ) /**< (AC97C)  */
+#define AT91C_AC97C_SIZE      (0x3 << 16) /**< (AC97C)  */
+#define 	AT91C_AC97C_SIZE_20_BITS              (0x0 << 16) /**< (AC97C) Data size is 20 bits */
+#define 	AT91C_AC97C_SIZE_18_BITS              (0x1 << 16) /**< (AC97C) Data size is 18 bits */
+#define 	AT91C_AC97C_SIZE_16_BITS              (0x2 << 16) /**< (AC97C) Data size is 16 bits */
+#define 	AT91C_AC97C_SIZE_10_BITS              (0x3 << 16) /**< (AC97C) Data size is 10 bits */
+#define AT91C_AC97C_CEM       (0x1 << 18) /**< (AC97C)  */
+#define AT91C_AC97C_CEN       (0x1 << 21) /**< (AC97C)  */
+/* --- Register AC97C_CORHR */
+#define AT91C_AC97C_SDATA     (0xFFFF << 0 ) /**< (AC97C) Status Data */
+/* --- Register AC97C_COTHR */
+#define AT91C_AC97C_CDATA     (0xFFFF << 0 ) /**< (AC97C) Command Data */
+#define AT91C_AC97C_CADDR     (0x7F << 16) /**< (AC97C) COdec control register index */
+#define AT91C_AC97C_READ      (0x1 << 23) /**< (AC97C) Read/Write command */
+/* --- Register AC97C_COSR */
+#define AT91C_AC97C_TXRDY     (0x1 << 0 ) /**< (AC97C)  */
+#define AT91C_AC97C_TXEMPTY   (0x1 << 1 ) /**< (AC97C)  */
+#define AT91C_AC97C_UNRUN     (0x1 << 2 ) /**< (AC97C)  */
+#define AT91C_AC97C_RXRDY     (0x1 << 4 ) /**< (AC97C)  */
+/* --- Register AC97C_COMR */
+#define AT91C_AC97C_TXRDY     (0x1 << 0 ) /**< (AC97C)  */
+#define AT91C_AC97C_TXEMPTY   (0x1 << 1 ) /**< (AC97C)  */
+#define AT91C_AC97C_UNRUN     (0x1 << 2 ) /**< (AC97C)  */
+#define AT91C_AC97C_RXRDY     (0x1 << 4 ) /**< (AC97C)  */
+/* --- Register AC97C_SR */
+#define AT91C_AC97C_SOF       (0x1 << 0 ) /**< (AC97C)  */
+#define AT91C_AC97C_WKUP      (0x1 << 1 ) /**< (AC97C)  */
+#define AT91C_AC97C_COEVT     (0x1 << 2 ) /**< (AC97C)  */
+#define AT91C_AC97C_CAEVT     (0x1 << 3 ) /**< (AC97C)  */
+#define AT91C_AC97C_CBEVT     (0x1 << 4 ) /**< (AC97C)  */
+/* --- Register AC97C_IER */
+#define AT91C_AC97C_SOF       (0x1 << 0 ) /**< (AC97C)  */
+#define AT91C_AC97C_WKUP      (0x1 << 1 ) /**< (AC97C)  */
+#define AT91C_AC97C_COEVT     (0x1 << 2 ) /**< (AC97C)  */
+#define AT91C_AC97C_CAEVT     (0x1 << 3 ) /**< (AC97C)  */
+#define AT91C_AC97C_CBEVT     (0x1 << 4 ) /**< (AC97C)  */
+/* --- Register AC97C_IDR */
+#define AT91C_AC97C_SOF       (0x1 << 0 ) /**< (AC97C)  */
+#define AT91C_AC97C_WKUP      (0x1 << 1 ) /**< (AC97C)  */
+#define AT91C_AC97C_COEVT     (0x1 << 2 ) /**< (AC97C)  */
+#define AT91C_AC97C_CAEVT     (0x1 << 3 ) /**< (AC97C)  */
+#define AT91C_AC97C_CBEVT     (0x1 << 4 ) /**< (AC97C)  */
+/* --- Register AC97C_IMR */
+#define AT91C_AC97C_SOF       (0x1 << 0 ) /**< (AC97C)  */
+#define AT91C_AC97C_WKUP      (0x1 << 1 ) /**< (AC97C)  */
+#define AT91C_AC97C_COEVT     (0x1 << 2 ) /**< (AC97C)  */
+#define AT91C_AC97C_CAEVT     (0x1 << 3 ) /**< (AC97C)  */
+#define AT91C_AC97C_CBEVT     (0x1 << 4 ) /**< (AC97C)  */
+
+#endif /* __AC97C_H */
Index: linux-2.6.20-at91/sound/arm/at73c213.c
===================================================================
--- /dev/null
+++ linux-2.6.20-at91/sound/arm/at73c213.c
@@ -0,0 +1,668 @@
+/*
+ * Driver for the at73c213 16-bit stereo DAC on Atmel SSC
+ *
+ * Copyright (C) 2006 Atmel
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this
+ * distribution in the file called COPYING.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include <asm/io.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <linux/atmel_pdc.h>
+#include <asm/arch/board.h>
+
+#include "at73c213.h"
+
+/*-----------------------------------------------------------------------------
+ *  AT73C213 SPI
+ *-----------------------------------------------------------------------------*/
+static struct spi_device *at73c213_spi_device;
+static unsigned char at73c213_regs[0x11];
+static unsigned char at73c213_spi_wbuffer[2];
+static unsigned char at73c213_spi_rbuffer[2];
+
+/*-----------------------------------------------------------------------------
+ *  AT73C213 SPI write regs
+ *-----------------------------------------------------------------------------*/
+static int at73c213_write_reg(u8 reg, u8 val)
+{
+    struct spi_message msg;
+    struct spi_transfer msg_xfer =
+    {
+        .len        = 2,
+        .cs_change  = 0,
+    };
+
+    spi_message_init(&msg);
+
+    at73c213_spi_wbuffer[0] = reg;
+    at73c213_spi_wbuffer[1] = val;
+
+    msg_xfer.tx_buf = at73c213_spi_wbuffer;
+    msg_xfer.rx_buf = at73c213_spi_rbuffer;
+    spi_message_add_tail(&msg_xfer, &msg);
+
+    at73c213_regs[reg] = val;
+
+    return spi_sync(at73c213_spi_device, &msg);
+}
+
+/*-----------------------------------------------------------------------------
+ *  AT73C213 SPI read regs
+ *-----------------------------------------------------------------------------*/
+static unsigned char at73c213_read_reg(unsigned char reg)
+{
+    return at73c213_regs[reg];
+}
+
+/*-----------------------------------------------------------------------------
+ *  AT73C213 - Init
+ *-----------------------------------------------------------------------------*/
+static void at73c213_hw_init(void)
+{
+/*
+	From AT73C213 datasheet
+	Path DAC to headset output
+	1. Write @0x10 => 0x03 (deassert the reset)
+	2. Write @0x0C => 0xFF (precharge + master on)
+	3. Write @0x00 => 0x30 (ONLNOL and ONLONOR set to 1)
+	4. Delay 500 ms
+	5. Write @0x0C => 0x01 (precharge off + master on)
+	6. Delay 1ms
+	7. Write @0x00 => 0x3C (ONLNOL, ONLNOR, ONDACR and ONDACL set to 1)
+*/
+	/* Make sure everything is off */
+	at73c213_write_reg(DAC_CTRL, 0x00);
+
+	msleep(500);
+
+	/* de-reset the device */
+    	at73c213_write_reg(DAC_RST, 0x03);
+
+	/* Turn on precharge */
+	at73c213_write_reg(DAC_PRECH, 0xFF);
+	at73c213_write_reg(DAC_CTRL, 0x30);
+
+    	/* Wait 500 ms*/
+    	msleep(500);
+
+	at73c213_write_reg(DAC_PRECH, 0x01);
+
+	msleep(1);
+
+	at73c213_write_reg(DAC_CTRL, 0x3C);
+	at73c213_write_reg(DAC_LLOG, 0x1f);
+	at73c213_write_reg(DAC_RLOG, 0x1f);
+}
+
+/*-----------------------------------------------------------------------------
+ * snd_at73c213_probe
+ *----------------------------------------------------------------------------*/
+static int __devinit at73c213_probe(struct spi_device *spi)
+{
+	int retval = 0;
+
+	if(!spi)
+		return -ENXIO;
+
+	at73c213_spi_device = spi;
+
+	return retval;
+}
+
+/*-----------------------------------------------------------------------------
+ * snd_at73c213_remove
+ *----------------------------------------------------------------------------*/
+static int __devexit at73c213_remove(struct spi_device *spi)
+{
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/*-----------------------------------------------------------------------------
+* snd_at73c213_suspend
+*----------------------------------------------------------------------------*/
+static int at73c213_suspend(struct spi_device *spi)
+{
+	return 0;
+}
+
+/*-----------------------------------------------------------------------------
+* snd_at73c213_resume
+*----------------------------------------------------------------------------*/
+static int at73c213_resume(struct spi_device *spi)
+{
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+/*-----------------------------------------------------------------------------
+ * AT73C213 Driver
+ *----------------------------------------------------------------------------*/
+static struct spi_driver at73c213_driver =
+{
+	.driver =
+	{
+		.name       = "at73c213",
+		.bus        = &spi_bus_type,
+		.owner      = THIS_MODULE,
+	}
+	,
+		.probe      = at73c213_probe,
+		.remove     = __devexit_p(at73c213_remove),
+		/* TODO:  investigate suspend and resume... */
+	#ifdef CONFIG_PM
+		.resume     = at73c213_resume,
+		.suspend    = at73c213_suspend,
+	#endif
+};
+
+/********************************************************************************
+*   Mixer
+********************************************************************************/
+
+/*-----------------------------------------------------------------------------
+ *  Info functions
+ *----------------------------------------------------------------------------*/
+static int at73c213_info_pcm_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0x0;
+	uinfo->value.integer.max = 0x1f;
+	return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ *  Get functions
+ *----------------------------------------------------------------------------*/
+static int at73c213_get_pcm_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ucontrol->value.integer.value[0] = (0x1f - at73c213_read_reg(DAC_LLOG)); /*left */
+	ucontrol->value.integer.value[1] = (0x1f - at73c213_read_reg(DAC_RLOG)); /*right*/
+	return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ *  Put functions
+ *----------------------------------------------------------------------------*/
+static int at73c213_put_pcm_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	at73c213_write_reg(DAC_LLOG,(0x1f - ucontrol->value.integer.value[0]));
+	at73c213_write_reg(DAC_RLOG,(0x1f - ucontrol->value.integer.value[1]));
+	return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ *  Controls
+ *----------------------------------------------------------------------------*/
+static struct snd_kcontrol_new snd_at73c213_controls[] =
+{
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name  = "PCM Playback Volume",
+		.info  = at73c213_info_pcm_volume,
+		.get   = at73c213_get_pcm_volume,
+		.put   = at73c213_put_pcm_volume
+	},
+};
+
+/*-----------------------------------------------------------------------------
+ *  Controls
+ *----------------------------------------------------------------------------*/
+static int __devinit snd_chip_at73c213_mixer_new(snd_card_t *card)
+{
+	int idx, err;
+
+	snd_assert(card != NULL, return -EINVAL);
+
+	if(at73c213_spi_device == NULL) {
+		printk(KERN_WARNING "No at73c231_spi_device found\n");
+		return -EFAULT;
+	}
+
+	/*  Set Mixer IOCTL */
+	for (idx = 0; idx < ARRAY_SIZE(snd_at73c213_controls); idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_at73c213_controls[idx], NULL))) < 0)
+			return err;
+	}
+
+	/* Init DAC Hardware */
+	at73c213_hw_init();
+
+	return 0;
+}
+
+/********************************************************************************
+*   DSP
+********************************************************************************/
+
+/*-----------------------------------------------------------------------------
+ * snd_at73c213_playback_hw
+ *----------------------------------------------------------------------------*/
+static snd_pcm_hardware_t snd_at73c213_playback_hw =
+{
+	.info    = (	SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER  ),
+	.formats = SNDRV_PCM_FMTBIT_S16_LE,
+        .rates =  SNDRV_PCM_RATE_CONTINUOUS,
+        .rate_min =         48000,
+        .rate_max =         48000,
+    	.channels_min   = 2,
+    	.channels_max   = 2,
+    	.buffer_bytes_max = 64 * 1024 - 1,
+    	.period_bytes_min = 1024,
+    	.period_bytes_max = 64 * 1024 - 1,
+    	.periods_min    = 4,
+    	.periods_max    = 1024,
+};
+
+/*-----------------------------------------------------------------------------
+ * snd_at73c213_pcm_open - open callback
+ *----------------------------------------------------------------------------*/
+static int snd_at73c213_pcm_open(snd_pcm_substream_t *substream)
+{
+	struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	runtime->hw = snd_at73c213_playback_hw;
+	chip->substream = substream;
+
+	return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ * snd_at73c213_pcm_close - close callback
+ *----------------------------------------------------------------------------*/
+static int snd_at73c213_pcm_close(snd_pcm_substream_t *substream)
+{
+	struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
+	chip->substream = NULL;
+	return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ * snd_at73c213_pcm_hw_params - hw_params callback
+ *----------------------------------------------------------------------------*/
+static int snd_at73c213_pcm_hw_params(snd_pcm_substream_t *substream,
+                                      snd_pcm_hw_params_t *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream,
+                                    params_buffer_bytes(hw_params));
+}
+
+/*-----------------------------------------------------------------------------
+ * snd_at73c213_pcm_hw_free - hw_free callback
+ *----------------------------------------------------------------------------*/
+static int snd_at73c213_pcm_hw_free(snd_pcm_substream_t *substream)
+{
+    return snd_pcm_lib_free_pages(substream);
+}
+
+/*-----------------------------------------------------------------------------
+ * snd_at73c213_pcm_prepare - prepare callback
+ *----------------------------------------------------------------------------*/
+static int snd_at73c213_pcm_prepare(snd_pcm_substream_t *substream)
+{
+	struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
+	struct platform_device *pdev = chip->pdev;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int block_size;
+
+	block_size = frames_to_bytes(runtime, runtime->period_size);
+
+	chip->period = 0;
+
+	/* Make sure that our data are actually readable by the SSC */
+	dma_sync_single_for_device(&pdev->dev, runtime->dma_addr,
+			block_size, DMA_TO_DEVICE);
+	dma_sync_single_for_device(&pdev->dev, runtime->dma_addr + block_size,
+			block_size, DMA_TO_DEVICE);
+
+	writel(runtime->dma_addr, chip->ssc_regs + PDC_TPR);
+	writel(runtime->period_size * 2, chip->ssc_regs + PDC_TCR);
+	writel(runtime->dma_addr + block_size, chip->ssc_regs + PDC_TNPR);
+	writel(runtime->period_size * 2, chip->ssc_regs + PDC_TNCR);
+
+	return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ * snd_at73c213_pcm_trigger - trigger callback
+ *----------------------------------------------------------------------------*/
+static int snd_at73c213_pcm_trigger(snd_pcm_substream_t *substream,
+                                    int cmd)
+{
+	struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
+	int retval = 0;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	switch (cmd)
+	{
+		case SNDRV_PCM_TRIGGER_START:
+			writel(SSC_INT_ENDTX, chip->ssc_regs + SSC_IER);
+			writel(PDC_PTCR_TXTEN, chip->ssc_regs + PDC_PTCR);
+			break;
+		case SNDRV_PCM_TRIGGER_STOP:
+			writel(PDC_PTCR_TXTDIS, chip->ssc_regs + PDC_PTCR);
+			writel(SSC_INT_ENDTX, chip->ssc_regs + SSC_IDR);
+			break;
+		default:
+			printk(KERN_WARNING "at73c213: spurious command %x\n", cmd);
+			retval = -EINVAL;
+			break;
+	}
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return retval;
+}
+
+/*-----------------------------------------------------------------------------
+ * snd_pcm_uframes_t - pointer callback
+ *----------------------------------------------------------------------------*/
+static snd_pcm_uframes_t snd_at73c213_pcm_pointer(snd_pcm_substream_t *substream)
+{
+	struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_uframes_t pos;
+	unsigned long bytes;
+
+	bytes = readl(chip->ssc_regs + PDC_TPR) - runtime->dma_addr;
+
+	pos = bytes_to_frames(runtime, bytes);
+	if (pos >= runtime->buffer_size)
+		pos -= runtime->buffer_size;
+
+	return pos;
+}
+
+/*-----------------------------------------------------------------------------
+ * operators
+ *----------------------------------------------------------------------------*/
+static snd_pcm_ops_t at73c213_playback_ops =
+{
+	.open       = snd_at73c213_pcm_open,
+	.close      = snd_at73c213_pcm_close,
+	.ioctl      = snd_pcm_lib_ioctl,
+	.hw_params  = snd_at73c213_pcm_hw_params,
+	.hw_free    = snd_at73c213_pcm_hw_free,
+	.prepare    = snd_at73c213_pcm_prepare,
+	.trigger    = snd_at73c213_pcm_trigger,
+	.pointer    = snd_at73c213_pcm_pointer,
+};
+
+/*-----------------------------------------------------------------------------
+ * snd_at73c213_pcm_free free a pcm device
+ *----------------------------------------------------------------------------*/
+static void snd_at73c213_pcm_free(snd_pcm_t *pcm)
+{
+	struct snd_at73c213 *chip = snd_pcm_chip(pcm);
+
+	if (chip->pcm != 0 )
+	{
+		snd_pcm_lib_preallocate_free_for_all(chip->pcm);
+		chip->pcm = NULL;
+	}
+}
+
+/*-----------------------------------------------------------------------------
+ * snd_at73c213_new_pcm create a new pcm device
+ *----------------------------------------------------------------------------*/
+static int __devinit snd_at73c213_new_pcm(snd_card_t * card)
+{
+	snd_pcm_t *pcm;
+	struct snd_at73c213 * chip = card->private_data;
+	int err;
+
+	err = snd_pcm_new(chip->card, "AT73C213", 0, 1, 0, &pcm);
+	if (err)
+		return err;
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_at73c213_pcm_free;
+	pcm->info_flags = SNDRV_PCM_INFO_BLOCK_TRANSFER;
+	pcm->private_data = chip;
+	strcpy( pcm->name, "AT73C213" );
+	chip->pcm = pcm;
+
+	/* set operators */
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &at73c213_playback_ops);
+
+	/* pre-allocation of buffers */
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, NULL, 64 * 1024, 64 * 1024);
+
+	return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ * snd_at73c213_interrupt
+ *---------------------------------------------------------------------------*/
+static irqreturn_t snd_at73c213_interrupt(int irq, void *dev_id)
+{
+	struct snd_at73c213 *chip = dev_id;
+	struct platform_device *pdev = chip->pdev;
+	snd_pcm_runtime_t *runtime = chip->substream->runtime;
+	u32 status;
+	int offset, next_period, block_size;
+
+	spin_lock(&chip->lock);
+
+	block_size = frames_to_bytes(runtime, runtime->period_size);
+
+	status = readl(chip->ssc_regs + SSC_IMR);
+
+	if (status & SSC_INT_ENDTX)
+	{
+		chip->period++;
+		if (chip->period == runtime->periods)
+			chip->period = 0;
+		next_period = chip->period + 1;
+		if (next_period == runtime->periods)
+			next_period = 0;
+
+		offset = block_size * next_period;
+
+		/* Make sure that our data are actually readable by the SSC */
+		dma_sync_single_for_device(&pdev->dev, runtime->dma_addr + offset,
+				block_size, DMA_TO_DEVICE);
+		writel(runtime->dma_addr + offset, chip->ssc_regs + PDC_TNPR);
+		writel(runtime->period_size * 2, chip->ssc_regs + PDC_TNCR);
+
+		if (next_period == 0)
+		{
+			(void)readl(chip->ssc_regs + PDC_TPR);
+			(void)readl(chip->ssc_regs + PDC_TCR);
+		}
+	}
+	else
+	{
+		printk(KERN_WARNING
+				"Spurious SSC interrupt, status = 0x%08lx\n",
+				(unsigned long)status);
+		writel(status, chip->ssc_regs + SSC_IDR);
+	}
+
+	(void)readl(chip->ssc_regs + SSC_IMR);
+	spin_unlock(&chip->lock);
+
+	if (status & SSC_INT_ENDTX)
+		snd_pcm_period_elapsed(chip->substream);
+
+	return IRQ_HANDLED;
+}
+
+/*-----------------------------------------------------------------------------
+ * snd_at73c213_chip_init
+ *----------------------------------------------------------------------------*/
+static int snd_at73c213_chip_init(struct snd_at73c213 *chip)
+{
+	/* Reset the SSC */
+	writel(SSC_CR_SWRST, chip->ssc_regs + SSC_CR);
+
+	/* Enable SSC and setup for I2S */
+	writel(chip->ssc_div, chip->ssc_regs + SSC_CMR);
+
+	/* CKO, START, STTDLY, PERIOD */
+	writel((1<<2)|(4<<8)|(1<<16)|(15<<24), chip->ssc_regs + SSC_TCMR);
+
+	/* DATLEN, MSBF, DATNB, FSLEN, FSOS */
+	writel((15<<0)|(1<<7)|(1<<8)|(15<<16)|(1<<20), chip->ssc_regs + SSC_TFMR);
+
+	/* Enable SSC RX */
+	writel(SSC_CR_TXEN, chip->ssc_regs + SSC_CR);
+}
+
+/*-----------------------------------------------------------------------------
+ * snd_at73c213_probe
+ *----------------------------------------------------------------------------*/
+static int __devinit snd_at73c213_probe(struct platform_device *pdev)
+{
+	struct atmel_at73c213_data *pdata = pdev->dev.platform_data;
+	struct snd_at73c213 *chip;
+	snd_card_t          *card;
+	int irq, ret;
+	struct resource *res;
+
+	/* register the soundcard */
+	card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, sizeof(struct snd_at73c213));
+	if (card == NULL){
+		return -ENOMEM;
+	}
+
+	chip = card->private_data;
+	chip->ssc_div = pdata->ssc_div;
+	chip->at73_mck = pdata->at73_mck;
+	spin_lock_init(&chip->lock);
+	chip->card = card;
+	strcpy( card->driver, "AT73C213" );
+	strcpy( card->shortname, "AT73C213" );
+	strcpy( card->longname, "AT73C213" );
+
+	if (!pdev)
+		return -ENXIO;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENXIO;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+	chip->irq = irq;
+
+	/* Request mem region */
+	if (!request_mem_region(res->start, res->end - res->start + 1, pdev->name))
+		return -EBUSY;
+
+	/* Remap SSC register */
+	chip->ssc_regs = ioremap(res->start, res->end - res->start + 1);
+	if (!chip->ssc_regs)
+		return -ENOMEM;
+
+	snd_chip_at73c213_mixer_new(card);
+
+	snd_at73c213_new_pcm(card);
+
+	ret = request_irq(chip->irq, snd_at73c213_interrupt, 0, "AT73C213", chip);
+	if (ret)
+		return ret;
+	snd_at73c213_chip_init(chip);
+
+	ret = snd_card_register(card);
+	if(ret)
+		return ret;
+
+	return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ * snd_at73c213_remove
+ *----------------------------------------------------------------------------*/
+static int __devexit snd_at73c213_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ * snd_at73c213_driver
+ *----------------------------------------------------------------------------*/
+static struct platform_driver snd_at73c213_driver =
+{
+	.probe      = snd_at73c213_probe,
+	.remove     = __devexit_p(snd_at73c213_remove),
+	.driver     =
+	{
+		.name       = "atmel_ssc_at73c213",
+	}
+	,
+};
+
+static int __init snd_at73c213_init(void)
+{
+	int ret;
+
+	ret = spi_register_driver(&at73c213_driver);
+	if(ret)
+		return ret;
+
+	ret = platform_driver_register(&snd_at73c213_driver);
+	if(ret)
+	{
+		spi_unregister_driver(&at73c213_driver);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __exit snd_at73c213_exit(void)
+{
+	platform_driver_unregister(&snd_at73c213_driver);
+	spi_unregister_driver(&at73c213_driver);
+}
+
+/********************************************************************************
+*   Module
+********************************************************************************/
+MODULE_AUTHOR("Atmel");
+MODULE_DESCRIPTION("at73c213 snd driver");
+MODULE_LICENSE("GPL");
+
+module_init(snd_at73c213_init);
+module_exit(snd_at73c213_exit);
Index: linux-2.6.20-at91/sound/arm/at73c213.h
===================================================================
--- /dev/null
+++ linux-2.6.20-at91/sound/arm/at73c213.h
@@ -0,0 +1,182 @@
+/*
+ * Driver for the AT73C213 16-bit stereo DAC
+ *
+ * Copyright (C) 2006 Atmel
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this
+ * distribution in the file called COPYING.
+ */
+
+#ifndef SND_AT73C213_MIXER_H_
+#define SND_AT73C213_MIXER_H_
+
+/* DAC control register */
+#define DAC_CTRL		0x00
+#define DAC_CTRL_ONPADRV	7
+#define DAC_CTRL_ONAUXIN	6
+#define DAC_CTRL_ONDACR		5
+#define DAC_CTRL_ONDACL		4
+#define DAC_CTRL_ONLNOR		3
+#define DAC_CTRL_ONLNOL		2
+#define DAC_CTRL_ONLNIR		1
+#define DAC_CTRL_ONLNIL		0
+
+/* DAC left line in gain register */
+#define DAC_LLIG		0x01
+#define DAC_LLIG_LLIG		0
+
+/* DAC right line in gain register */
+#define DAC_RLIG		0x02
+#define DAC_RLIG_RLIG		0
+
+/* DAC Left Master Playback Gain Register */
+#define DAC_LMPG		0x03
+#define DAC_LMPG_LMPG		0
+
+/* DAC Right Master Playback Gain Register */
+#define DAC_RMPG		0x04
+#define DAC_RMPG_RMPG		0
+
+/* DAC Left Line Out Gain Register */
+#define DAC_LLOG		0x05
+#define DAC_LLOG_LLOG		0
+
+/* DAC Right Line Out Gain Register */
+#define DAC_RLOG		0x06
+#define DAC_RLOG_RLOG		0
+
+/* DAC Output Level Control Register */
+#define DAC_OLC			0x07
+#define DAC_OLC_RSHORT		7
+#define DAC_OLC_ROLC		4
+#define DAC_OLC_LSHORT		3
+#define DAC_OLC_LOLC		0
+
+/* DAC Mixer Control Register */
+#define DAC_MC			0x08
+#define DAC_MC_INVR		5
+#define DAC_MC_INVL		4
+#define DAC_MC_RMSMIN2		3
+#define DAC_MC_RMSMIN1		2
+#define DAC_MC_LMSMIN2		1
+#define DAC_MC_LMSMIN1		0
+
+/* DAC Clock and Sampling Frequency Control Register */
+#define DAC_CSFC		0x09
+#define DAC_CSFC_OVRSEL		4
+
+/* DAC Miscellaneous Register */
+#define DAC_MISC		0x0A
+#define DAC_MISC_VCMCAPSEL	7
+#define DAC_MISC_DINTSEL	4
+#define DAC_MISC_DITHEN		3
+#define DAC_MISC_DEEMPEN	2
+#define DAC_MISC_NBITS		0
+
+/* DAC Precharge Control Register */
+#define DAC_PRECH		0x0C
+#define DAC_PRECH_PRCHGPDRV	7
+#define DAC_PRECH_PRCHGAUX1	6
+#define DAC_PRECH_PRCHGLNOR	5
+#define DAC_PRECH_PRCHGLNOL	4
+#define DAC_PRECH_PRCHGLNIR	3
+#define DAC_PRECH_PRCHGLNIL	2
+#define DAC_PRECH_PRCHG		1
+#define DAC_PRECH_ONMSTR	0
+
+/* DAC Auxiliary Input Gain Control Register */
+#define DAC_AUXG		0x0D
+#define DAC_AUXG_AUXG		0
+
+/* DAC Reset Register */
+#define DAC_RST			0x10
+#define DAC_RST_RESMASK		2
+#define DAC_RST_RESFILZ		1
+#define DAC_RST_RSTZ		0
+
+/* Power Amplifier Control Register */
+#define PA_CTRL			0x11
+#define PA_CTRL_APAON		6
+#define PA_CTRL_APAPRECH	5
+#define PA_CTRL_APALP		4
+#define PA_CTRL_APAGAIN		0
+
+/* PDC */
+#define PDC_RPR		0x100	/* Receive Pointer Register */
+#define PDC_RCR		0x104	/* Receive Counter Register */
+#define PDC_TPR		0x108	/* Transmit Pointer Register */
+#define PDC_TCR		0x10c	/* Transmit Counter Register */
+#define PDC_RNPR		0x110	/* Receive Next Pointer Register */
+#define PDC_RNCR		0x114	/* Receive Next Counter Register */
+#define PDC_TNPR		0x118	/* Transmit Next Pointer Register */
+#define PDC_TNCR		0x11c	/* Transmit Next Counter Register */
+
+#define PDC_PTCR		0x120	/* Transfer Control Register */
+#define		PDC_PTCR_RXTEN		(1 << 0)	/* Receiver Transfer Enable */
+#define		PDC_PTCR_RXTDIS		(1 << 1)	/* Receiver Transfer Disable */
+#define		PDC_PTCR_TXTEN		(1 << 8)	/* Transmitter Transfer Enable */
+#define		PDC_PTCR_TXTDIS		(1 << 9)	/* Transmitter Transfer Disable */
+
+#define PDC_PTSR		0x124	/* Transfer Status Register */
+#define SSC_CMR		0x04
+#define SSC_CR		0x00
+#define SSC_TCMR	0x18
+#define SSC_TFMR	0x1C
+
+/* SSC register definitions */
+#define SSC_CR		0x00
+#define SSC_CMR		0x04
+#define SSC_TCMR	0x18
+#define SSC_TFMR	0x1C
+#define SSC_THR		0x24
+#define SSC_SR		0x40
+#define SSC_IER		0x44
+#define SSC_IDR		0x48
+#define SSC_IMR		0x4C
+
+/* SSC fields definitions */
+#define SSC_CR_TXEN	0x00000100
+#define SSC_CR_TXDIS	0x00000200
+#define SSC_CR_SWRST	0x00008000
+
+/* SSC interrupt definitions */
+#define SSC0_IRQ	10
+#define SSC_INT_ENDTX	0x00000004
+#define SSC_INT_TXBUFE	0x00000008
+
+
+/* chip-specific data */
+struct snd_at73c213 {
+	snd_card_t			*card;
+	snd_pcm_t			*pcm;
+	snd_pcm_substream_t	*substream;
+	struct spi_device	*spi;
+	struct clk			*ssc_clk;
+	struct clk			*at73_mck;
+	spinlock_t			lock;
+	struct platform_device		*pdev;
+	void __iomem		*ssc_regs;
+	int					ext_clk_rate;
+	int 			ssc_div;
+	int					irq;
+	int					period;
+	u8					spi_wbuffer[2];
+	u8					spi_rbuffer[2];
+};
+#endif
+
Index: linux-2.6.20-at91/arch/arm/mach-at91rm9200/at91sam9261.c
===================================================================
--- linux-2.6.20-at91.orig/arch/arm/mach-at91rm9200/at91sam9261.c
+++ linux-2.6.20-at91/arch/arm/mach-at91rm9200/at91sam9261.c
@@ -72,6 +72,11 @@ static struct clk usart2_clk = {
 	.pmc_mask	= 1 << AT91SAM9261_ID_US2,
 	.type		= CLK_TYPE_PERIPHERAL,
 };
+static struct clk ssc1_clk = {
+	.name		= "ssc1_clk",
+	.pmc_mask	= 1 << AT91SAM9261_ID_SSC1,
+	.type		= CLK_TYPE_PERIPHERAL,
+};
 static struct clk mmc_clk = {
 	.name		= "mci_clk",
 	.pmc_mask	= 1 << AT91SAM9261_ID_MCI,
@@ -136,6 +141,7 @@ static struct clk *periph_clocks[] __ini
 	&spi0_clk,
 	&spi1_clk,
 	// ssc 0 .. ssc2
+	&ssc1_clk,
 	&tc0_clk,
 	&tc1_clk,
 	&tc2_clk,
Index: linux-2.6.20-at91/arch/arm/mach-at91rm9200/board-sam9260ek.c
===================================================================
--- linux-2.6.20-at91.orig/arch/arm/mach-at91rm9200/board-sam9260ek.c
+++ linux-2.6.20-at91/arch/arm/mach-at91rm9200/board-sam9260ek.c
@@ -25,6 +25,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
+#include <linux/clk.h>
 
 #include <asm/hardware.h>
 #include <asm/setup.h>
@@ -85,6 +86,69 @@ static struct at91_udc_data __initdata e
 
 
 /*
+ * AT73C213
+ */
+#if defined(CONFIG_SND_AT73C213) || defined(CONFIG_SND_AT73C213_MODULE)
+static struct atmel_at73c213_data at73c213_data;
+
+static u64 ssc_dmamask = 0xffffffffUL;
+static struct resource ssc_resource[] = {
+	[0] = {
+		.start	= AT91SAM9260_BASE_SSC,
+		.end	= AT91SAM9260_BASE_SSC + SZ_16K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9260_ID_SSC,
+		.end	= AT91SAM9260_ID_SSC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device at91sam9260_ssc_device = {
+	.name		= "atmel_ssc_at73c213",
+	.id		= -1,
+	.dev		= {
+				.dma_mask		= &ssc_dmamask,
+				.coherent_dma_mask	= 0xffffffff,
+				.platform_data		= &at73c213_data,
+	},
+	.resource		= ssc_resource,
+	.num_resources	= ARRAY_SIZE(ssc_resource),
+};
+void __init at91_add_device_ssc_at73c213(void)
+{
+	struct clk *at73_clk;
+	struct clk *ssc_clk;
+	struct clk *parent_clk;
+
+	/* Set SSC1 IO */
+	at91_set_A_periph(AT91_PIN_PB16, 0);		/* TK0 */
+	at91_set_A_periph(AT91_PIN_PB17, 0);		/* TF0 */
+	at91_set_A_periph(AT91_PIN_PB18, 0);		/* TD0 */
+
+	/* AT73C213 MCK Clock */
+	at91_set_B_periph(AT91_PIN_PC1, 0);		/* PCK0 */
+
+	at73_clk = clk_get(NULL, "pck0");
+	parent_clk = clk_get(NULL, "plla");
+	clk_set_parent(at73_clk, parent_clk);
+	clk_set_rate(at73_clk, 12416000);
+	clk_enable(at73_clk);
+
+	ssc_clk = clk_get(NULL, "ssc_clk");
+	clk_enable(ssc_clk);
+
+	at73c213_data.ssc_div  = 32;
+	at73c213_data.at73_mck = at73_clk;
+
+	platform_device_register(&at91sam9260_ssc_device);
+}
+#else
+	void __init at91_add_device_ssc_at73c213(void) {}
+#endif
+
+/*
  * SPI devices.
  */
 static struct spi_board_info ek_spi_devices[] = {
@@ -104,9 +168,9 @@ static struct spi_board_info ek_spi_devi
 	},
 #endif
 #endif
-#if defined(CONFIG_SND_AT73C213)
+#if defined(CONFIG_SND_AT73C213) || defined(CONFIG_SND_AT73C213_MODULE)
 	{	/* AT73C213 DAC */
-		.modalias	= "snd_at73c213",
+		.modalias	= "at73c213",
 		.chip_select	= 0,
 		.max_speed_hz	= 10 * 1000 * 1000,
 		.bus_num	= 1,
@@ -188,6 +252,8 @@ static void __init ek_board_init(void)
 	at91_add_device_eth(&ek_macb_data);
 	/* MMC */
 	at91_add_device_mmc(0, &ek_mmc_data);
+	/* AT73C213 & SSC port */
+	at91_add_device_ssc_at73c213();
 }
 
 MACHINE_START(AT91SAM9260EK, "Atmel AT91SAM9260-EK")
Index: linux-2.6.20-at91/drivers/net/macb.h
===================================================================
--- linux-2.6.20-at91.orig/drivers/net/macb.h
+++ linux-2.6.20-at91/drivers/net/macb.h
@@ -368,9 +368,12 @@ struct macb {
 	unsigned int		tx_head, tx_tail;
 	struct dma_desc		*tx_ring;
 	struct ring_info	*tx_skb;
-
+#if defined(CONFIG_ARCH_AT91)
+	void			*tx_buffers;
+#endif
 	spinlock_t		lock;
 	struct platform_device	*pdev;
+	struct clk		*macb_clk;
 	struct clk		*pclk;
 	struct clk		*hclk;
 	struct net_device	*dev;
@@ -380,7 +383,9 @@ struct macb {
 	dma_addr_t		rx_ring_dma;
 	dma_addr_t		tx_ring_dma;
 	dma_addr_t		rx_buffers_dma;
-
+#if defined(CONFIG_ARCH_AT91)
+	dma_addr_t		tx_buffers_dma;
+#endif
 	unsigned int		rx_pending, tx_pending;
 
 	struct delayed_work	periodic_task;
Index: linux-2.6.20-at91/drivers/net/macb.c
===================================================================
--- linux-2.6.20-at91.orig/drivers/net/macb.c
+++ linux-2.6.20-at91/drivers/net/macb.c
@@ -36,9 +36,21 @@
 /* Make the IP header word-aligned (the ethernet header is 14 bytes) */
 #define RX_OFFSET		2
 
-#define TX_RING_SIZE		128
+#if defined(CONFIG_ARCH_AT91) && defined(CONFIG_MACB_TX_SRAM) 
+  #if defined(CONFIG_ARCH_AT91SAM9260)
+    #define TX_RING_SIZE		2	
+  #elif defined(CONFIG_ARCH_AT91SAM9263)
+    #define TX_RING_SIZE		32	
+  #endif
+  #define TX_BUFFER_SIZE		1536
+  #define TX_RING_BYTES		(sizeof(struct dma_desc) * TX_RING_SIZE)
+  #define TX_DMA_SIZE		((TX_RING_BYTES) + (TX_RING_SIZE) * (TX_BUFFER_SIZE))
+#else
+  #define TX_RING_SIZE		128
+  #define TX_RING_BYTES		(sizeof(struct dma_desc) * TX_RING_SIZE)
+#endif
+
 #define DEF_TX_RING_PENDING	(TX_RING_SIZE - 1)
-#define TX_RING_BYTES		(sizeof(struct dma_desc) * TX_RING_SIZE)
 
 #define TX_RING_GAP(bp)						\
 	(TX_RING_SIZE - (bp)->tx_pending)
@@ -319,8 +331,10 @@ static void macb_tx(struct macb *bp)
 
 		dev_dbg(&bp->pdev->dev, "skb %u (data %p) TX complete\n",
 			tail, skb->data);
+#if !defined(CONFIG_MACB_TX_SRAM) 
 		dma_unmap_single(&bp->pdev->dev, rp->mapping, skb->len,
 				 DMA_TO_DEVICE);
+#endif
 		bp->stats.tx_packets++;
 		bp->stats.tx_bytes += skb->len;
 		rp->skb = NULL;
@@ -602,8 +616,13 @@ static int macb_start_xmit(struct sk_buf
 
 	entry = bp->tx_head;
 	dev_dbg(&bp->pdev->dev, "Allocated ring entry %u\n", entry);
+#if defined(CONFIG_ARCH_AT91) && defined(CONFIG_MACB_TX_SRAM)
+	mapping = bp->tx_ring[entry].addr;
+	memcpy(bp->tx_buffers + entry * TX_BUFFER_SIZE, skb->data, len);
+#else
 	mapping = dma_map_single(&bp->pdev->dev, skb->data,
 				 len, DMA_TO_DEVICE);
+#endif
 	bp->tx_skb[entry].skb = skb;
 	bp->tx_skb[entry].mapping = mapping;
 	dev_dbg(&bp->pdev->dev, "Mapped skb data %p to DMA addr %08lx\n",
@@ -614,7 +633,9 @@ static int macb_start_xmit(struct sk_buf
 	if (entry == (TX_RING_SIZE - 1))
 		ctrl |= MACB_BIT(TX_WRAP);
 
+#if !defined(CONFIG_MACB_TX_SRAM)
 	bp->tx_ring[entry].addr = mapping;
+#endif
 	bp->tx_ring[entry].ctrl = ctrl;
 	wmb();
 
@@ -645,8 +666,12 @@ static void macb_free_consistent(struct 
 		bp->rx_ring = NULL;
 	}
 	if (bp->tx_ring) {
+#if defined(CONFIG_ARCH_AT91) && defined(CONFIG_MACB_TX_SRAM)
+		iounmap((void *)bp->tx_ring);
+#else
 		dma_free_coherent(&bp->pdev->dev, TX_RING_BYTES,
 				  bp->tx_ring, bp->tx_ring_dma);
+#endif
 		bp->tx_ring = NULL;
 	}
 	if (bp->rx_buffers) {
@@ -655,6 +680,11 @@ static void macb_free_consistent(struct 
 				  bp->rx_buffers, bp->rx_buffers_dma);
 		bp->rx_buffers = NULL;
 	}
+
+#if defined(CONFIG_ARCH_AT91) && defined(CONFIG_MACB_TX_SRAM)
+	if (bp->tx_ring_dma)
+		release_mem_region(bp->tx_ring_dma, TX_DMA_SIZE);
+#endif
 }
 
 static int macb_alloc_consistent(struct macb *bp)
@@ -675,6 +705,34 @@ static int macb_alloc_consistent(struct 
 		"Allocated RX ring of %d bytes at %08lx (mapped %p)\n",
 		size, (unsigned long)bp->rx_ring_dma, bp->rx_ring);
 
+#if defined(CONFIG_ARCH_AT91) && defined(CONFIG_MACB_TX_SRAM) 
+#if defined(CONFIG_ARCH_AT91SAM9260)
+	if (request_mem_region(AT91SAM9260_SRAM0_BASE, TX_DMA_SIZE, "macb")) {
+		bp->tx_ring_dma = AT91SAM9260_SRAM0_BASE;
+	} else {
+		if (request_mem_region(AT91SAM9260_SRAM1_BASE, TX_DMA_SIZE, "macb")) {
+			bp->tx_ring_dma = AT91SAM9260_SRAM1_BASE;
+		} else {
+			printk(KERN_WARNING "Cannot request SRAM memory for TX ring, already used\n");
+			return -EBUSY;
+		}
+	}
+#elif defined(CONFIG_ARCH_AT91SAM9263)
+	if (request_mem_region(AT91SAM9263_SRAM0_BASE, TX_DMA_SIZE, "macb")) {
+		bp->tx_ring_dma = AT91SAM9263_SRAM0_BASE;
+	} else {
+		printk(KERN_WARNING "Cannot request SRAM memory for TX ring, already used\n");
+		return -EBUSY;
+	}
+#endif
+
+	bp->tx_ring = ioremap(bp->tx_ring_dma, TX_DMA_SIZE);
+	if (!bp->tx_ring)
+		return -ENOMEM;
+
+	bp->tx_buffers_dma = bp->tx_ring_dma + TX_RING_BYTES;
+	bp->tx_buffers = (char*) bp->tx_ring + TX_RING_BYTES;
+#else
 	size = TX_RING_BYTES;
 	bp->tx_ring = dma_alloc_coherent(&bp->pdev->dev, size,
 					 &bp->tx_ring_dma, GFP_KERNEL);
@@ -683,6 +741,7 @@ static int macb_alloc_consistent(struct 
 	dev_dbg(&bp->pdev->dev,
 		"Allocated TX ring of %d bytes at %08lx (mapped %p)\n",
 		size, (unsigned long)bp->tx_ring_dma, bp->tx_ring);
+#endif
 
 	size = RX_RING_SIZE * RX_BUFFER_SIZE;
 	bp->rx_buffers = dma_alloc_coherent(&bp->pdev->dev, size,
@@ -713,10 +772,18 @@ static void macb_init_rings(struct macb 
 	}
 	bp->rx_ring[RX_RING_SIZE - 1].addr |= MACB_BIT(RX_WRAP);
 
+#if defined(CONFIG_ARCH_AT91) && defined(CONFIG_MACB_TX_SRAM)  
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		bp->tx_ring[i].addr = bp->tx_buffers_dma + i * TX_BUFFER_SIZE;
+		bp->tx_ring[i].ctrl = MACB_BIT(TX_USED);
+	}
+#else
 	for (i = 0; i < TX_RING_SIZE; i++) {
 		bp->tx_ring[i].addr = 0;
 		bp->tx_ring[i].ctrl = MACB_BIT(TX_USED);
 	}
+#endif
+
 	bp->tx_ring[TX_RING_SIZE - 1].ctrl |= MACB_BIT(TX_WRAP);
 
 	bp->rx_tail = bp->tx_head = bp->tx_tail = 0;
@@ -883,27 +950,15 @@ static struct net_device_stats *macb_get
 static int macb_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
 	struct macb *bp = netdev_priv(dev);
-	int ret;
-	unsigned long flags;
 
-	spin_lock_irqsave(&bp->lock, flags);
-	ret = mii_ethtool_gset(&bp->mii, cmd);
-	spin_unlock_irqrestore(&bp->lock, flags);
-
-	return ret;
+	return mii_ethtool_gset(&bp->mii, cmd);
 }
 
 static int macb_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
 	struct macb *bp = netdev_priv(dev);
-	int ret;
-	unsigned long flags;
-
-	spin_lock_irqsave(&bp->lock, flags);
-	ret = mii_ethtool_sset(&bp->mii, cmd);
-	spin_unlock_irqrestore(&bp->lock, flags);
 
-	return ret;
+	return mii_ethtool_sset(&bp->mii, cmd);
 }
 
 static void macb_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
@@ -932,17 +987,11 @@ static struct ethtool_ops macb_ethtool_o
 static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
 	struct macb *bp = netdev_priv(dev);
-	int ret;
-	unsigned long flags;
 
 	if (!netif_running(dev))
 		return -EINVAL;
 
-	spin_lock_irqsave(&bp->lock, flags);
-	ret = generic_mii_ioctl(&bp->mii, if_mii(rq), cmd, NULL);
-	spin_unlock_irqrestore(&bp->lock, flags);
-
-	return ret;
+	return generic_mii_ioctl(&bp->mii, if_mii(rq), cmd, NULL);
 }
 
 static ssize_t macb_mii_show(const struct class_device *cd, char *buf,
@@ -1047,12 +1096,12 @@ static int __devinit macb_probe(struct p
 	spin_lock_init(&bp->lock);
 
 #if defined(CONFIG_ARCH_AT91)
-	bp->pclk = clk_get(&pdev->dev, "macb_clk");
-	if (IS_ERR(bp->pclk)) {
+	bp->macb_clk = clk_get(&pdev->dev, "macb_clk");
+	if (IS_ERR(bp->macb_clk)) {
 		dev_err(&pdev->dev, "failed to get macb_clk\n");
 		goto err_out_free_dev;
 	}
-	clk_enable(bp->pclk);
+	clk_enable(bp->macb_clk);
 #else
 	bp->pclk = clk_get(&pdev->dev, "pclk");
 	if (IS_ERR(bp->pclk)) {
@@ -1102,7 +1151,12 @@ static int __devinit macb_probe(struct p
 	init_completion(&bp->mdio_complete);
 
 	/* Set MII management clock divider */
+#if defined(CONFIG_ARCH_AT91)
+	pclk_hz = clk_get_rate(bp->macb_clk);
+#else
 	pclk_hz = clk_get_rate(bp->pclk);
+#endif
+
 	if (pclk_hz <= 20000000)
 		config = MACB_BF(CLK, MACB_CLK_DIV8);
 	else if (pclk_hz <= 40000000)
@@ -1128,17 +1182,17 @@ static int __devinit macb_probe(struct p
 
 	pdata = pdev->dev.platform_data;
 	if (pdata && pdata->is_rmii)
-#if defined(CONFIG_ARCH_AT91)
+	#if defined(CONFIG_ARCH_AT91)
 		macb_writel(bp, USRIO, (MACB_BIT(RMII) | MACB_BIT(CLKEN)) );
-#else
+	#else
 		macb_writel(bp, USRIO, 0);
-#endif
+	#endif
 	else
-#if defined(CONFIG_ARCH_AT91)
+	#if defined(CONFIG_ARCH_AT91)
 		macb_writel(bp, USRIO, MACB_BIT(CLKEN));
-#else
-		macb_writel(bp, USRIO, MACB_BIT(MII));
-#endif
+	#else
+	 	macb_writel(bp, USRIO, MACB_BIT(MII));
+	#endif
 
 	bp->tx_pending = DEF_TX_RING_PENDING;
 
@@ -1165,13 +1219,15 @@ err_out_free_irq:
 err_out_iounmap:
 	iounmap(bp->regs);
 err_out_disable_clocks:
-#ifndef CONFIG_ARCH_AT91
+#if defined(CONFIG_ARCH_AT91)
+	clk_disable(bp->macb_clk);	
+#else
 	clk_disable(bp->hclk);
-	clk_put(bp->hclk);
-#endif
 	clk_disable(bp->pclk);
+	clk_put(bp->hclk);
 err_out_put_pclk:
 	clk_put(bp->pclk);
+#endif
 err_out_free_dev:
 	free_netdev(dev);
 err_out:
@@ -1192,11 +1248,9 @@ static int __devexit macb_remove(struct 
 		unregister_netdev(dev);
 		free_irq(dev->irq, dev);
 		iounmap(bp->regs);
-#ifndef CONFIG_ARCH_AT91
 		clk_disable(bp->hclk);
-		clk_put(bp->hclk);
-#endif
 		clk_disable(bp->pclk);
+		clk_put(bp->hclk);
 		clk_put(bp->pclk);
 		free_netdev(dev);
 		platform_set_drvdata(pdev, NULL);
Index: linux-2.6.20-at91/drivers/net/Kconfig
===================================================================
--- linux-2.6.20-at91.orig/drivers/net/Kconfig
+++ linux-2.6.20-at91/drivers/net/Kconfig
@@ -199,6 +199,12 @@ config MACB
 	  To compile this driver as a module, choose M here: the module
 	  will be called macb.
 
+config MACB_TX_SRAM
+	bool "Atmel MACB TX buffers in internal SRAM"
+	depends on NET_ETHERNET && MACB && (ARCH_AT91SAM9260 || ARCH_AT91SAM9263)
+	help
+	  Use internal SRAM for TX buffers.
+
 source "drivers/net/arm/Kconfig"
 
 config MACE
