---
 sound/avr32/Kconfig  |   25 +
 sound/avr32/Makefile |    3 
 sound/avr32/ac97c.c  | 1250 +++++++++++++++++++++++++++++++++++++++++++++++++++
 sound/avr32/ac97c.h  |   71 ++
 4 files changed, 1349 insertions(+)

Index: linux-2.6.18-avr32/sound/avr32/Kconfig
===================================================================
--- linux-2.6.18-avr32.orig/sound/avr32/Kconfig	2006-11-02 15:56:20.000000000 +0100
+++ linux-2.6.18-avr32/sound/avr32/Kconfig	2006-11-02 16:02:29.000000000 +0100
@@ -3,4 +3,29 @@
 menu "ALSA AVR32 devices"
 	depends on SND != n && AVR32
 
+config SND_ATMEL_AC97
+	tristate "Atmel AC97 Controller Driver"
+	depends on SND
+	select SND_PCM
+	select SND_AC97_CODEC
+	help
+	  ALSA sound driver for the Atmel AC97 controller.
+
+config SND_ATMEL_AC97_USE_ALSA_MALLOC_CALLS
+	bool "Use the built-in malloc calls in the alsa driver"
+	default n
+	depends on SND_ATMEL_AC97
+	help
+	  Say Y if the built-in malloc calls in the alsa driver should be
+	  used instead of the native dma_alloc_coherent and dma_free_coherent
+	  function calls. Enabling this feature may break the rmmod feature.
+
+config SND_ATMEL_AC97C_USE_PDC
+	bool "Use PDC for DMA transfers to/from the Atmel AC97 Controller"
+	default n
+	depends on SND_ATMEL_AC97
+	help
+	  Say Y if PDC (Peripheral DMA Controller) is used for DMA transfers
+	  to/from the Atmel AC97C instead of using the generic DMA framework.
+
 endmenu
Index: linux-2.6.18-avr32/sound/avr32/Makefile
===================================================================
--- linux-2.6.18-avr32.orig/sound/avr32/Makefile	2006-11-02 15:56:20.000000000 +0100
+++ linux-2.6.18-avr32/sound/avr32/Makefile	2006-11-02 16:02:29.000000000 +0100
@@ -1,3 +1,6 @@
 #
 # Makefile for ALSA
 #
+
+snd-atmel-ac97-objs		:= ac97c.o
+obj-$(CONFIG_SND_ATMEL_AC97)	+= snd-atmel-ac97.o
Index: linux-2.6.18-avr32/sound/avr32/ac97c.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.18-avr32/sound/avr32/ac97c.c	2006-11-02 16:02:56.000000000 +0100
@@ -0,0 +1,1250 @@
+/*
+ * Driver for the Atmel AC97 Controller
+ *
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/ac97_codec.h>
+#ifndef SND_ATMEL_AC97_USE_ALSA_MALLOC_CALLS
+#include <sound/memalloc.h>
+#endif
+
+#include <asm/io.h>
+
+#include "ac97c.h"
+
+static DEFINE_MUTEX(opened_mutex);
+
+/* 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");
+
+#ifndef CONFIG_SND_ATMEL_AC97C_USE_PDC
+#include <asm/dma-controller.h>
+
+struct atmel_ac97_dma_info {
+	struct dma_request_cyclic req_tx;
+	struct dma_request_cyclic req_rx;
+	unsigned short rx_periph_id;
+	unsigned short tx_periph_id;
+};
+#endif
+
+
+typedef struct atmel_ac97 {
+	spinlock_t lock;
+	void __iomem *regs;
+	int period;
+
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	ac97_t *ac97;
+	ac97_bus_t *ac97_bus;
+	int irq;
+	int opened;
+	u64 cur_format;
+	unsigned int cur_rate;
+	struct clk *mck;
+	struct platform_device *pdev;
+        struct atmel_ac97_dma_info dma;
+} atmel_ac97_t;
+#define get_chip(card) ((atmel_ac97_t *)(card)->private_data)
+
+#define ac97c_writel(chip, reg, val)			\
+	__raw_writel((val), (chip)->regs + AC97C_##reg)
+#define ac97c_readl(chip, reg)				\
+	__raw_readl((chip)->regs + AC97C_##reg)
+
+/* PCM part */
+
+static snd_pcm_hardware_t snd_atmel_ac97_playback_hw = {
+	.info			= (SNDRV_PCM_INFO_INTERLEAVED
+				  |SNDRV_PCM_INFO_MMAP
+				  |SNDRV_PCM_INFO_MMAP_VALID
+				  |SNDRV_PCM_INFO_BLOCK_TRANSFER
+				  |SNDRV_PCM_INFO_JOINT_DUPLEX),
+	.formats		= (SNDRV_PCM_FMTBIT_S16_BE|SNDRV_PCM_FMTBIT_S16_LE),
+	.rates			= (SNDRV_PCM_RATE_CONTINUOUS),
+	.rate_min		= 4000,
+	.rate_max		= 48000,
+	.channels_min		= 1,
+	.channels_max		= 6,
+	.buffer_bytes_max	= 64*1024,
+	.period_bytes_min	= 512,
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+	.period_bytes_max	= 64*1024,
+#else
+	.period_bytes_max	= 4095,
+#endif
+	.periods_min		= 8,
+	.periods_max		= 1024,
+};
+
+static snd_pcm_hardware_t snd_atmel_ac97_capture_hw = {
+	.info			= (SNDRV_PCM_INFO_INTERLEAVED
+				  |SNDRV_PCM_INFO_MMAP
+				  |SNDRV_PCM_INFO_MMAP_VALID
+				  |SNDRV_PCM_INFO_BLOCK_TRANSFER
+				  |SNDRV_PCM_INFO_JOINT_DUPLEX),
+	.formats		= (SNDRV_PCM_FMTBIT_S16_BE|SNDRV_PCM_FMTBIT_S16_LE),
+	.rates			= (SNDRV_PCM_RATE_CONTINUOUS),
+	.rate_min		= 4000,
+	.rate_max		= 48000,
+	.channels_min		= 1,
+	.channels_max		= 2,
+	.buffer_bytes_max	= 64*1024,
+	.period_bytes_min	= 512,
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+	.period_bytes_max	= 64*1024,
+#else
+	.period_bytes_max	= 4095,
+#endif
+	.periods_min		= 8,
+	.periods_max		= 1024,
+};
+
+/* Joint full duplex variables */
+unsigned int hw_rates[1];
+unsigned int hw_formats[1];
+struct snd_pcm_hw_constraint_list hw_constraint_rates;
+struct snd_pcm_hw_constraint_list hw_constraint_formats;
+
+/*
+ * PCM functions
+ */
+static int
+snd_atmel_ac97_playback_open(snd_pcm_substream_t *substream)
+{
+	atmel_ac97_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	mutex_lock(&opened_mutex);
+	chip->opened++;
+	runtime->hw = snd_atmel_ac97_playback_hw;
+	if (chip->cur_rate) {
+		runtime->hw.rate_min = chip->cur_rate;
+		runtime->hw.rate_max = chip->cur_rate;
+	}
+	if (chip->cur_format)
+		runtime->hw.formats = (1ULL<<chip->cur_format);
+	mutex_unlock(&opened_mutex);
+	chip->playback_substream = substream;
+	chip->period = 0;
+	return 0;
+}
+
+static int
+snd_atmel_ac97_capture_open(snd_pcm_substream_t *substream)
+{
+	atmel_ac97_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	mutex_lock(&opened_mutex);
+	chip->opened++;
+	runtime->hw = snd_atmel_ac97_capture_hw;
+	if (chip->cur_rate) {
+		runtime->hw.rate_min = chip->cur_rate;
+		runtime->hw.rate_max = chip->cur_rate;
+	}
+	if (chip->cur_format)
+		runtime->hw.formats = (1ULL<<chip->cur_format);
+	mutex_unlock(&opened_mutex);
+	chip->capture_substream = substream;
+	chip->period = 0;
+	return 0;
+}
+
+static int snd_atmel_ac97_playback_close(snd_pcm_substream_t *substream)
+{
+	atmel_ac97_t *chip = snd_pcm_substream_chip(substream);
+	mutex_lock(&opened_mutex);
+	chip->opened--;
+	if (!chip->opened) {
+		chip->cur_rate = 0;
+		chip->cur_format = 0;
+	}
+	mutex_unlock(&opened_mutex);
+	return 0;
+}
+
+static int snd_atmel_ac97_capture_close(snd_pcm_substream_t *substream)
+{
+	atmel_ac97_t *chip = snd_pcm_substream_chip(substream);
+	mutex_lock(&opened_mutex);
+	chip->opened--;
+	if (!chip->opened) {
+		chip->cur_rate = 0;
+		chip->cur_format = 0;
+	}
+	mutex_unlock(&opened_mutex);
+	return 0;
+}
+
+static int snd_atmel_ac97_playback_hw_params(snd_pcm_substream_t *substream,
+				    snd_pcm_hw_params_t *hw_params)
+{
+	atmel_ac97_t *chip = snd_pcm_substream_chip(substream);
+#ifdef SND_ATMEL_AC97_USE_ALSA_MALLOC_CALLS
+	int err;
+	err = snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+
+	if (err < 0)
+		return err;
+
+	/* Set restrictions to params */
+	mutex_lock(&opened_mutex);
+	chip->cur_rate = params_rate(hw_params);
+	chip->cur_format = params_format(hw_params);
+	mutex_unlock(&opened_mutex);
+
+	return err;
+#else
+	int pg;
+	size_t size = params_buffer_bytes(hw_params);
+	struct snd_pcm_runtime *runtime;
+	struct snd_dma_buffer *dmab = NULL;
+
+	substream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV;
+	snd_assert(substream != NULL, return -EINVAL);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -EINVAL);
+
+	/* Set restrictions to params */
+	mutex_lock(&opened_mutex);
+	chip->cur_rate = params_rate(hw_params);
+	chip->cur_format = params_format(hw_params);
+	mutex_unlock(&opened_mutex);
+
+	/* check if buffer is already allocated */
+	if (runtime->dma_buffer_p) {
+		size_t size_previouse;
+		int pg_previouse;
+
+		/* new buffer is smaler than previouse allocated buffer */
+		if (runtime->dma_buffer_p->bytes >= size) {
+			runtime->dma_bytes = size;
+			return 0; /* don't change buffer size */
+		}
+
+		size_previouse = runtime->dma_buffer_p->bytes;
+		pg_previouse = get_order(size_previouse);
+
+		dma_free_coherent(runtime->dma_buffer_p->dev.dev,
+				PAGE_SIZE << pg_previouse,
+				runtime->dma_buffer_p->area,
+				runtime->dma_buffer_p->addr);
+
+		kfree(runtime->dma_buffer_p);
+	}
+
+	dmab = kzalloc(sizeof(*dmab), GFP_KERNEL);
+	if (!dmab)
+		return -ENOMEM;
+
+	dmab->dev = substream->dma_buffer.dev;
+	dmab->bytes = 0;
+
+	pg = get_order(size);
+
+	dmab->area = dma_alloc_coherent(
+			substream->dma_buffer.dev.dev,
+			PAGE_SIZE << pg,
+			(dma_addr_t *)&dmab->addr,
+			GFP_KERNEL);
+
+	if (!dmab->area) {
+		kfree(dmab);
+		return -ENOMEM;
+	}
+
+	dmab->bytes = size;
+
+	snd_pcm_set_runtime_buffer(substream, dmab);
+	runtime->dma_bytes = size;
+	return 1;
+#endif
+}
+
+static int snd_atmel_ac97_capture_hw_params(snd_pcm_substream_t *substream,
+				    snd_pcm_hw_params_t *hw_params)
+{
+	atmel_ac97_t *chip = snd_pcm_substream_chip(substream);
+#ifdef SND_ATMEL_AC97_USE_ALSA_MALLOC_CALLS
+	int err;
+	err = snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+
+	if (err < 0)
+		return err;
+
+	/* Set restrictions to params */
+	mutex_lock(&opened_mutex);
+	chip->cur_rate = params_rate(hw_params);
+	chip->cur_format = params_format(hw_params);
+	mutex_unlock(&opened_mutex);
+
+	return err;
+#else
+	int pg;
+	size_t size = params_buffer_bytes(hw_params);
+	struct snd_pcm_runtime *runtime;
+	struct snd_dma_buffer *dmab = NULL;
+
+	substream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV;
+	snd_assert(substream != NULL, return -EINVAL);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -EINVAL);
+
+	/* Set restrictions to params */
+	mutex_lock(&opened_mutex);
+	chip->cur_rate = params_rate(hw_params);
+	chip->cur_format = params_format(hw_params);
+	mutex_unlock(&opened_mutex);
+
+	/* check if buffer is already allocated */
+	if (runtime->dma_buffer_p) {
+		size_t size_previouse;
+		int pg_previouse;
+
+		/* new buffer is smaler than previouse allocated buffer */
+		if (runtime->dma_buffer_p->bytes >= size) {
+			runtime->dma_bytes = size;
+			return 0; /* don't change buffer size */
+		}
+
+		size_previouse = runtime->dma_buffer_p->bytes;
+		pg_previouse = get_order(size_previouse);
+
+		dma_free_coherent(runtime->dma_buffer_p->dev.dev,
+				PAGE_SIZE << pg_previouse,
+				runtime->dma_buffer_p->area,
+				runtime->dma_buffer_p->addr);
+
+		kfree(runtime->dma_buffer_p);
+	}
+
+	dmab = kzalloc(sizeof(*dmab), GFP_KERNEL);
+	if (!dmab)
+		return -ENOMEM;
+
+	dmab->dev = substream->dma_buffer.dev;
+	dmab->bytes = 0;
+
+	pg = get_order(size);
+
+	dmab->area = dma_alloc_coherent(
+			substream->dma_buffer.dev.dev,
+			PAGE_SIZE << pg,
+			(dma_addr_t *)&dmab->addr,
+			GFP_KERNEL);
+
+	if (!dmab->area) {
+		kfree(dmab);
+		return -ENOMEM;
+	}
+
+	dmab->bytes = size;
+
+	snd_pcm_set_runtime_buffer(substream, dmab);
+	runtime->dma_bytes = size;
+	return 1;
+#endif
+}
+
+static int snd_atmel_ac97_playback_hw_free(snd_pcm_substream_t *substream)
+{
+#ifdef SND_ATMEL_AC97_USE_ALSA_MALLOC_CALLS
+	return snd_pcm_lib_free_pages(substream);
+#else
+	int pg;
+	struct snd_pcm_runtime *runtime;
+	struct snd_dma_buffer *dmab = NULL;
+
+	snd_assert(substream != NULL, return -EINVAL);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -EINVAL);
+	dmab = runtime->dma_buffer_p;
+
+	if (!dmab)
+		return 0;
+
+	if (!dmab->area)
+		return 0;
+
+	pg = get_order(dmab->bytes);
+	dma_free_coherent(dmab->dev.dev, PAGE_SIZE << pg, dmab->area, dmab->addr);
+	kfree(runtime->dma_buffer_p);
+	snd_pcm_set_runtime_buffer(substream, NULL);
+	return 0;
+#endif
+}
+
+static int snd_atmel_ac97_capture_hw_free(snd_pcm_substream_t *substream)
+{
+
+#ifdef SND_ATMEL_AC97_USE_ALSA_MALLOC_CALLS
+	return snd_pcm_lib_free_pages(substream);
+#else
+	int pg;
+	struct snd_pcm_runtime *runtime;
+	struct snd_dma_buffer *dmab = NULL;
+
+	snd_assert(substream != NULL, return -EINVAL);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -EINVAL);
+	dmab = runtime->dma_buffer_p;
+
+	if (!dmab)
+		return 0;
+
+	if (!dmab->area)
+		return 0;
+
+	pg = get_order(dmab->bytes);
+	dma_free_coherent(dmab->dev.dev, PAGE_SIZE << pg, dmab->area, dmab->addr);
+	kfree(runtime->dma_buffer_p);
+	snd_pcm_set_runtime_buffer(substream, NULL);
+	return 0;
+#endif
+}
+
+static int snd_atmel_ac97_playback_prepare(snd_pcm_substream_t *substream)
+{
+	atmel_ac97_t *chip = snd_pcm_substream_chip(substream);
+	struct platform_device *pdev = chip->pdev;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int block_size = frames_to_bytes(runtime, runtime->period_size);
+	unsigned long word = 0;
+	unsigned long buffer_size = 0;
+
+	dma_sync_single_for_device(&pdev->dev, runtime->dma_addr,
+			block_size * 2, DMA_TO_DEVICE);
+
+	/* Assign slots to channels */
+	switch (substream->runtime->channels) {
+        case 1:
+		word |= AC97C_CH_ASSIGN(PCM_LEFT, A);
+		break;
+	case 2:
+		/* Assign Left and Right slot to Channel A */
+		word |= AC97C_CH_ASSIGN(PCM_LEFT, A)
+			| AC97C_CH_ASSIGN(PCM_RIGHT, A);
+		break;
+        default:
+		/* TODO: support more than two channels */
+		return -EINVAL;
+		break;
+	}
+	ac97c_writel(chip, OCA, word);
+
+	/* Configure sample format and size */
+        word = AC97C_CMR_PDCEN | AC97C_CMR_SIZE_16;
+
+        switch (runtime->format){
+        case SNDRV_PCM_FORMAT_S16_LE:
+		word |= AC97C_CMR_CEM_LITTLE;
+		break;
+        case SNDRV_PCM_FORMAT_S16_BE:
+        default:
+		word &= ~AC97C_CMR_CEM_LITTLE;
+		break;
+        }
+
+	ac97c_writel(chip, CAMR, word);
+
+        /* Set variable rate if needed */
+        if (runtime->rate != 48000) {
+		word = ac97c_readl(chip, MR);
+		word |= AC97C_MR_VRA;
+		ac97c_writel(chip, MR, word);
+        } else {
+		/* Clear Variable Rate Bit */
+		word = ac97c_readl(chip, MR);
+		word &= ~AC97C_MR_VRA;
+		ac97c_writel(chip, MR, word);
+        }
+
+        /* Set rate */
+        snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
+
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+	/* Initialize and start the PDC */
+        ac97c_writel(chip, CATPR, runtime->dma_addr);
+	ac97c_writel(chip, CATCR, block_size / 4);
+	ac97c_writel(chip, CATNPR, runtime->dma_addr + block_size);
+	ac97c_writel(chip, CATNCR, block_size / 4);
+	ac97c_writel(chip, PTCR, PDC_PTCR_TXTEN);
+	/* Enable Channel A interrupts */
+	ac97c_writel(chip, IER, AC97C_SR_CAEVT);
+#else
+	buffer_size = frames_to_bytes(runtime, runtime->period_size) *
+		runtime->periods;
+
+	chip->dma.req_tx.buffer_size = buffer_size;
+        chip->dma.req_tx.periods = runtime->periods;
+
+	BUG_ON(chip->dma.req_tx.buffer_size !=
+			(chip->dma.req_tx.periods *
+			 frames_to_bytes(runtime, runtime->period_size)));
+
+        chip->dma.req_tx.buffer_start = runtime->dma_addr;
+        chip->dma.req_tx.data_reg = (dma_addr_t)(chip->regs + AC97C_CATHR + 2);
+        chip->dma.req_tx.periph_id = chip->dma.tx_periph_id;
+        chip->dma.req_tx.direction = DMA_DIR_MEM_TO_PERIPH;
+        chip->dma.req_tx.width = DMA_WIDTH_16BIT;
+        chip->dma.req_tx.dev_id = chip;
+#endif
+
+	return 0;
+}
+
+static int snd_atmel_ac97_capture_prepare(snd_pcm_substream_t *substream)
+{
+	atmel_ac97_t *chip = snd_pcm_substream_chip(substream);
+	struct platform_device *pdev = chip->pdev;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int block_size = frames_to_bytes(runtime, runtime->period_size);
+	unsigned long word = 0;
+	unsigned long buffer_size = 0;
+
+	dma_sync_single_for_device(&pdev->dev, runtime->dma_addr,
+			block_size * 2, DMA_FROM_DEVICE);
+
+	/* Assign slots to channels */
+	switch (substream->runtime->channels) {
+        case 1:
+		word |= AC97C_CH_ASSIGN(PCM_LEFT, A);
+		break;
+        case 2:
+		/* Assign Left and Right slot to Channel A */
+		word |= AC97C_CH_ASSIGN(PCM_LEFT, A)
+			| AC97C_CH_ASSIGN(PCM_RIGHT, A);
+		break;
+	default:
+		/* TODO: support more than two channels */
+		return -EINVAL;
+		break;
+	}
+	ac97c_writel(chip, ICA, word);
+
+	/* Configure sample format and size */
+        word = AC97C_CMR_PDCEN | AC97C_CMR_SIZE_16;
+
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		word |= AC97C_CMR_CEM_LITTLE;
+		break;
+	case SNDRV_PCM_FORMAT_S16_BE:
+	default:
+		word &= ~(AC97C_CMR_CEM_LITTLE);
+		break;
+	}
+
+	ac97c_writel(chip, CAMR, word);
+
+	/* Set variable rate if needed */
+	if (runtime->rate != 48000) {
+		word = ac97c_readl(chip, MR);
+		word |= AC97C_MR_VRA;
+		ac97c_writel(chip, MR, word);
+	} else {
+		/* Clear Variable Rate Bit */
+		word = ac97c_readl(chip, MR);
+		word &= ~(AC97C_MR_VRA);
+		ac97c_writel(chip, MR, word);
+	}
+
+	/* Set rate */
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+	/* Initialize and start the PDC */
+	ac97c_writel(chip, CARPR, runtime->dma_addr);
+	ac97c_writel(chip, CARCR, block_size / 4);
+	ac97c_writel(chip, CARNPR, runtime->dma_addr + block_size);
+	ac97c_writel(chip, CARNCR, block_size / 4);
+	ac97c_writel(chip, PTCR, PDC_PTCR_RXEN);
+	/* Enable Channel A interrupts */
+	ac97c_writel(chip, IER, AC97C_SR_CAEVT);
+#else
+	buffer_size = frames_to_bytes(runtime, runtime->period_size) *
+		runtime->periods;
+
+	chip->dma.req_rx.buffer_size = buffer_size;
+	chip->dma.req_rx.periods = runtime->periods;
+
+	BUG_ON(chip->dma.req_rx.buffer_size !=
+			(chip->dma.req_rx.periods *
+			 frames_to_bytes(runtime, runtime->period_size)));
+
+	chip->dma.req_rx.buffer_start = runtime->dma_addr;
+	chip->dma.req_rx.data_reg = (dma_addr_t)(chip->regs + AC97C_CARHR + 2);
+	chip->dma.req_rx.periph_id = chip->dma.rx_periph_id;
+	chip->dma.req_rx.direction = DMA_DIR_PERIPH_TO_MEM;
+	chip->dma.req_rx.width = DMA_WIDTH_16BIT;
+	chip->dma.req_rx.dev_id = chip;
+#endif
+
+	return 0;
+}
+
+static int snd_atmel_ac97_playback_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	atmel_ac97_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long camr;
+	int flags, err = 0;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	camr = ac97c_readl(chip, CAMR);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		err = dma_prepare_request_cyclic(chip->dma.req_tx.req.dmac,
+				&chip->dma.req_tx);
+		dma_start_request(chip->dma.req_tx.req.dmac,
+				chip->dma.req_tx.req.channel);
+		camr |= (AC97C_CMR_CENA
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+				|AC97C_CMR_TXRDY
+#endif
+			);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		err = dma_stop_request(chip->dma.req_tx.req.dmac,
+				chip->dma.req_tx.req.channel);
+		if (chip->opened <= 1) {
+			camr &= ~(AC97C_CMR_CENA
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+					|AC97C_CMR_TXRDY
+#endif
+				 );
+		}
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+		else {
+			camr &= ~(AC97C_CMR_TXRDY);
+		}
+#endif
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+
+	ac97c_writel(chip, CAMR, camr);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return err;
+}
+
+static int snd_atmel_ac97_capture_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	atmel_ac97_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long camr;
+	int flags, err = 0;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	camr = ac97c_readl(chip, CAMR);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		err = dma_prepare_request_cyclic(chip->dma.req_rx.req.dmac,
+				&chip->dma.req_rx);
+		dma_start_request(chip->dma.req_rx.req.dmac,
+				chip->dma.req_rx.req.channel);
+		camr |= (AC97C_CMR_CENA
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+			 | AC97C_CMR_RXRDY
+#endif
+			);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		err = dma_stop_request(chip->dma.req_rx.req.dmac,
+				chip->dma.req_rx.req.channel);
+		mutex_lock(&opened_mutex);
+		if (chip->opened <= 1) {
+			camr &= ~(AC97C_CMR_CENA
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+					| AC97C_CMR_RXRDY
+#endif
+				 );
+		}
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+		else {
+			  camr &= ~(AC97C_CSR_RXRDY);
+		}
+#endif
+		mutex_unlock(&opened_mutex);
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+
+	ac97c_writel(chip, CAMR, camr);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return err;
+}
+
+static snd_pcm_uframes_t snd_atmel_ac97_playback_pointer(snd_pcm_substream_t *substream)
+{
+	atmel_ac97_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_uframes_t pos;
+	unsigned long bytes;
+
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+	bytes = ac97c_readl(chip, CATPR) - runtime->dma_addr;
+#else
+        bytes = (dma_get_current_pos
+			(chip->dma.req_tx.req.dmac,
+			 chip->dma.req_tx.req.channel) - runtime->dma_addr);
+#endif
+	pos = bytes_to_frames(runtime, bytes);
+	if (pos >= runtime->buffer_size)
+		pos -= runtime->buffer_size;
+
+	return pos;
+}
+
+static snd_pcm_uframes_t snd_atmel_ac97_capture_pointer(snd_pcm_substream_t *substream)
+{
+	atmel_ac97_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_uframes_t pos;
+	unsigned long bytes;
+
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+	bytes = ac97c_readl(chip, CARPR) - runtime->dma_addr;
+#else
+        bytes = (dma_get_current_pos
+			(chip->dma.req_rx.req.dmac,chip->dma.req_rx.req.channel) -
+			runtime->dma_addr);
+#endif
+	pos = bytes_to_frames(runtime, bytes);
+	if (pos >= runtime->buffer_size)
+		pos -= runtime->buffer_size;
+
+
+	return pos;
+}
+
+static snd_pcm_ops_t atmel_ac97_playback_ops = {
+	.open		= snd_atmel_ac97_playback_open,
+	.close		= snd_atmel_ac97_playback_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_atmel_ac97_playback_hw_params,
+	.hw_free	= snd_atmel_ac97_playback_hw_free,
+	.prepare	= snd_atmel_ac97_playback_prepare,
+	.trigger	= snd_atmel_ac97_playback_trigger,
+	.pointer	= snd_atmel_ac97_playback_pointer,
+};
+
+static snd_pcm_ops_t atmel_ac97_capture_ops = {
+	.open		= snd_atmel_ac97_capture_open,
+	.close		= snd_atmel_ac97_capture_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_atmel_ac97_capture_hw_params,
+	.hw_free	= snd_atmel_ac97_capture_hw_free,
+	.prepare	= snd_atmel_ac97_capture_prepare,
+	.trigger	= snd_atmel_ac97_capture_trigger,
+	.pointer	= snd_atmel_ac97_capture_pointer,
+};
+
+static struct ac97_pcm atmel_ac97_pcm_defs[] __devinitdata = {
+	/* Playback */
+	{
+		.exclusive = 1,
+		.r = { {
+			.slots = ((1 << AC97_SLOT_PCM_LEFT)
+					| (1 << AC97_SLOT_PCM_RIGHT)
+					| (1 << AC97_SLOT_PCM_CENTER)
+					| (1 << AC97_SLOT_PCM_SLEFT)
+					| (1 << AC97_SLOT_PCM_SRIGHT)
+					| (1 << AC97_SLOT_LFE)),
+		} }
+	},
+	/* PCM in */
+	{
+		.stream = 1,
+		.exclusive = 1,
+		.r = { {
+			.slots = ((1 << AC97_SLOT_PCM_LEFT)
+					| (1 << AC97_SLOT_PCM_RIGHT)),
+		} }
+	},
+	/* Mic in */
+	{
+		.stream = 1,
+		.exclusive = 1,
+		.r = { {
+			.slots = (1<<AC97_SLOT_MIC),
+		} }
+	},
+};
+
+static int __devinit snd_atmel_ac97_pcm_new(atmel_ac97_t *chip)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	err = snd_ac97_pcm_assign(chip->ac97_bus,
+			ARRAY_SIZE(atmel_ac97_pcm_defs),
+			atmel_ac97_pcm_defs);
+	if (err)
+		return err;
+
+	err = snd_pcm_new(chip->card, "Atmel-AC97", 0, 1, 1, &pcm);
+	if (err)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&atmel_ac97_playback_ops);
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+			&atmel_ac97_capture_ops);
+
+#ifdef SND_ATMEL_AC97_USE_ALSA_MALLOC_CALLS
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      &chip->pdev->dev,
+					      128 * 1024, 128 * 1024);
+#endif
+
+	pcm->private_data = chip;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "Atmel-AC97");
+	chip->pcm = pcm;
+
+	return 0;
+}
+
+/* Mixer part */
+static int snd_atmel_ac97_mixer_new(atmel_ac97_t *chip)
+{
+	int err;
+	ac97_template_t template;
+
+	memset(&template, 0, sizeof(template));
+	template.private_data = chip;
+	err = snd_ac97_mixer(chip->ac97_bus, &template, &chip->ac97);
+
+	return err;
+}
+
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+static irqreturn_t snd_atmel_ac97_interrupt(int irq, void *dev_id,
+					    struct pt_regs *regs)
+{
+	atmel_ac97_t *chip = dev_id;
+	unsigned long status;
+
+	status = ac97c_readl(chip, SR);
+
+	if (status & AC97C_SR_CAEVT) {
+		snd_pcm_runtime_t *runtime;
+		int offset, next_period, block_size;
+		unsigned long casr;
+
+		/* FIXME: separate playback from capture */
+		runtime = chip->playback_substream->runtime;
+		block_size = frames_to_bytes(runtime, runtime->period_size);
+
+		casr = ac97c_readl(chip, CASR);
+
+		if (casr & AC97C_CSR_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;
+
+			ac97c_writel(chip, CATNPR,
+				     runtime->dma_addr + offset);
+			ac97c_writel(chip, CATNCR, block_size / 4);
+
+			snd_pcm_period_elapsed(chip->playback_substream);
+		}
+		else if (casr & AC97C_CSR_ENDRX) {
+			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;
+
+			ac97c_writel(chip, CARNPR,
+				     runtime->dma_addr + offset);
+			ac97c_writel(chip, CARNCR, block_size / 4);
+
+			snd_pcm_period_elapsed(chip->capture_substream);
+		} else {
+			snd_printk(KERN_INFO
+			       "atmel-ac97: spurious interrupt, status = 0x%08lx\n",
+			       (unsigned long)casr);
+		}
+	} else {
+		snd_printk(KERN_INFO
+		       "atmel-ac97: spurious interrupt, status = 0x%08lx\n",
+		       status);
+	}
+
+	(volatile int)ac97c_readl(chip, SR);
+
+	return IRQ_HANDLED;
+}
+
+#else
+
+static void atmel_ac97_error(struct dma_request *_req)
+{
+	struct dma_request_cyclic *req = to_dma_request_cyclic(_req);
+
+	printk(KERN_WARNING
+	       "DMA Controller error, channel %d (AC97C)\n",
+	       req->req.channel);
+}
+
+static void atmel_ac97_block_complete(struct dma_request *_req)
+{
+	struct dma_request_cyclic *req = to_dma_request_cyclic(_req);
+	atmel_ac97_t *chip = req->dev_id;
+	if (req->periph_id == chip->dma.tx_periph_id)
+		snd_pcm_period_elapsed(chip->playback_substream);
+	else
+		snd_pcm_period_elapsed(chip->capture_substream);
+}
+
+#endif
+
+/* CODEC part */
+
+static void snd_atmel_ac97_write(ac97_t *ac97, unsigned short reg,
+				 unsigned short val)
+{
+	atmel_ac97_t *chip = ac97->private_data;
+	unsigned long word;
+	int timeout = 40;
+
+	word = (reg & 0x7f) << 16 | val;
+
+	do {
+		if (ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) {
+			ac97c_writel(chip, COTHR, word);
+			return;
+		}
+		udelay(1);
+	} while (--timeout);
+
+	snd_printk(KERN_WARNING "atmel-ac97: codec write timeout\n");
+}
+
+static unsigned short snd_atmel_ac97_read(ac97_t *ac97,
+					  unsigned short reg)
+{
+	atmel_ac97_t *chip = ac97->private_data;
+	unsigned long word;
+	int timeout = 40;
+	int write = 10;
+
+	word = (0x80 | (reg & 0x7f)) << 16;
+
+	if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0)
+		ac97c_readl(chip, CORHR);
+
+retry_write:
+	timeout = 40;
+
+	do {
+                if ((ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) != 0) {
+			ac97c_writel(chip, COTHR, word);
+			goto read_reg;
+                }
+		mdelay(10);
+	} while (--timeout);
+
+	if (!--write)
+		goto timed_out;
+	goto retry_write;
+
+read_reg:
+	do {
+		if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0){
+			unsigned short val = ac97c_readl(chip, CORHR);
+			return val;
+		}
+		mdelay(10);
+	} while (--timeout);
+
+	if (!--write)
+		goto timed_out;
+	goto retry_write;
+
+timed_out:
+	snd_printk(KERN_INFO "atmel-ac97: codec read timeout\n");
+	return 0xffff;
+}
+
+static void snd_atmel_ac97_reset(atmel_ac97_t *chip)
+{
+	/* TODO: Perform hard reset of codec as well */
+	ac97c_writel(chip, MR, AC97C_MR_WRST);
+	mdelay(1);
+	ac97c_writel(chip, MR, AC97C_MR_ENA);
+}
+
+static void snd_atmel_ac97_destroy(snd_card_t *card)
+{
+	atmel_ac97_t *chip = get_chip(card);
+
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+	if (chip->irq != -1)
+		free_irq(chip->irq, chip);
+#endif
+	if (chip->regs)
+		iounmap(chip->regs);
+
+	if (chip->mck) {
+		clk_disable(chip->mck);
+		clk_put(chip->mck);
+	}
+
+#ifndef CONFIG_SND_ATMEL_AC97C_USE_PDC
+        if (chip->dma.req_tx.req.dmac){
+		dma_release_channel(chip->dma.req_tx.req.dmac,
+				    chip->dma.req_tx.req.channel);
+        }
+        if (chip->dma.req_rx.req.dmac) {
+		dma_release_channel(chip->dma.req_rx.req.dmac,
+				    chip->dma.req_rx.req.channel);
+        }
+#endif
+}
+
+static int __devinit snd_atmel_ac97_create(snd_card_t *card,
+					   struct platform_device *pdev)
+{
+	static ac97_bus_ops_t ops = {
+		.write	= snd_atmel_ac97_write,
+		.read	= snd_atmel_ac97_read,
+	};
+	atmel_ac97_t *chip = get_chip(card);
+	struct resource *regs;
+	struct clk *mck;
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+	int irq;
+#endif
+        int err;
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs)
+		return -ENXIO;
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+#endif
+
+	mck = clk_get(&pdev->dev, "mck");
+	if (IS_ERR(mck))
+		return PTR_ERR(mck);
+	clk_enable(mck);
+	chip->mck = mck;
+
+	card->private_free = snd_atmel_ac97_destroy;
+
+	spin_lock_init(&chip->lock);
+	chip->card = card;
+	chip->pdev = pdev;
+	chip->irq = -1;
+
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+	err = request_irq(irq, snd_atmel_ac97_interrupt, 0,
+			  "ac97", chip);
+	if (err) {
+		snd_printk("unable to request IRQ%d\n", irq);
+		return err;
+	}
+	chip->irq = irq;
+#endif
+
+	chip->regs = ioremap(regs->start, regs->end - regs->start + 1);
+	if (!chip->regs)
+		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_atmel_ac97_probe(struct platform_device *pdev)
+{
+	static int dev;
+	snd_card_t *card;
+	atmel_ac97_t *chip;
+	int err;
+        int ch;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	err = -ENOMEM;
+
+	mutex_init(&opened_mutex);
+
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE,
+			    sizeof(atmel_ac97_t));
+	if (!card)
+		goto out;
+	chip = get_chip(card);
+
+	err = snd_atmel_ac97_create(card, pdev);
+	if (err)
+		goto out_free_card;
+
+	snd_atmel_ac97_reset(chip);
+
+	err = snd_atmel_ac97_mixer_new(chip);
+	if (err)
+		goto out_free_card;
+
+	err = snd_atmel_ac97_pcm_new(chip);
+	if (err)
+		goto out_free_card;
+
+#ifndef CONFIG_SND_ATMEL_AC97C_USE_PDC
+	/* TODO: Get this information from the platform device */
+	chip->dma.req_tx.req.dmac = find_dma_controller(0);
+	if (!chip->dma.req_tx.req.dmac) {
+		printk(KERN_ERR
+		       "atmel-ac97c: No DMA controller for TX, aborting\n");
+		goto out_free_card;
+	}
+	chip->dma.req_rx.req.dmac = find_dma_controller(0);
+	if (!chip->dma.req_rx.req.dmac) {
+		snd_printk(KERN_ERR
+		       "atmel-ac97c: No DMA controller available for RX, aborting\n");
+		goto out_free_card;
+	}
+
+	chip->dma.rx_periph_id = 3;
+	chip->dma.tx_periph_id = 4;
+
+	ch = dma_alloc_channel(chip->dma.req_tx.req.dmac);
+	if (ch < 0) {
+		printk(KERN_ERR
+		       "atmel-ac97c: Unable to allocate TX DMA channel, aborting\n");
+		goto out_free_card;
+	}
+	chip->dma.req_tx.req.channel = ch;
+	chip->dma.req_tx.width = DMA_WIDTH_16BIT;
+	chip->dma.req_tx.req.block_complete = atmel_ac97_block_complete;
+	chip->dma.req_tx.req.error = atmel_ac97_error;
+
+	ch = dma_alloc_channel(chip->dma.req_rx.req.dmac);
+	if (ch < 0) {
+		snd_printk(KERN_ERR
+		       "atmel-ac97c: Unable to allocate RX DMA channel, aborting\n");
+		goto out_free_card;
+	}
+	chip->dma.req_rx.req.channel = ch;
+	chip->dma.req_rx.width = DMA_WIDTH_16BIT;
+	chip->dma.req_rx.req.block_complete = atmel_ac97_block_complete;
+	chip->dma.req_rx.req.error = atmel_ac97_error;
+#endif
+
+	strcpy(card->driver, "ac97c");
+	strcpy(card->shortname, "Atmel-AC97");
+#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC
+	sprintf(card->longname, "Atmel AVR32 AC97 Controller at 0x%p, irq %i",
+		chip->regs, chip->irq);
+#else
+	sprintf(card->longname, "Atmel AVR32 AC97 Controller at 0x%p, dma rx %i and tx %i",
+		chip->regs, chip->dma.rx_periph_id, chip->dma.tx_periph_id);
+#endif
+
+	err = snd_card_register(card);
+	if (err)
+		goto out_free_card;
+
+	platform_set_drvdata(pdev, card);
+	dev++;
+	return 0;
+
+out_free_card:
+	snd_card_free(card);
+out:
+	return err;
+}
+
+static int __devexit snd_atmel_ac97_remove(struct platform_device *pdev)
+{
+	snd_card_t *card = platform_get_drvdata(pdev);
+
+	snd_card_free(card);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static struct platform_driver atmel_ac97_driver = {
+	.probe		= snd_atmel_ac97_probe,
+	.remove		= __devexit_p(snd_atmel_ac97_remove),
+	.driver		= {
+		.name	= "ac97c",
+	},
+};
+
+static int __init atmel_ac97_init(void)
+{
+	return platform_driver_register(&atmel_ac97_driver);
+}
+
+static void __exit atmel_ac97_exit(void)
+{
+	platform_driver_unregister(&atmel_ac97_driver);
+}
+
+module_init(atmel_ac97_init);
+module_exit(atmel_ac97_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Driver for Atmel AC97 Controller");
+MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>");
Index: linux-2.6.18-avr32/sound/avr32/ac97c.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.18-avr32/sound/avr32/ac97c.h	2006-11-02 15:56:20.000000000 +0100
@@ -0,0 +1,71 @@
+/*
+ * Register definitions for the Atmel AC97 Controller.
+ *
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __SOUND_AVR32_AC97C_H
+#define __SOUND_AVR32_AC97C_H
+
+#define AC97C_MR		0x08
+#define AC97C_ICA		0x10
+#define AC97C_OCA		0x14
+#define AC97C_CARHR		0x20
+#define AC97C_CATHR		0x24
+#define AC97C_CASR		0x28
+#define AC97C_CAMR		0x2c
+#define AC97C_CBRHR		0x30
+#define AC97C_CBTHR		0x34
+#define AC97C_CBSR		0x38
+#define AC97C_CBMR		0x3c
+#define AC97C_CORHR		0x40
+#define AC97C_COTHR		0x44
+#define AC97C_COSR		0x48
+#define AC97C_COMR		0x4c
+#define AC97C_SR		0x50
+#define AC97C_IER		0x54
+#define AC97C_IDR		0x58
+#define AC97C_IMR		0x5c
+#define AC97C_VERSION		0xfc
+
+#define AC97C_CATPR		PDC_TPR
+#define AC97C_CATCR		PDC_TCR
+#define AC97C_CATNPR		PDC_TNPR
+#define AC97C_CATNCR		PDC_TNCR
+#define AC97C_CARPR		PDC_RPR
+#define AC97C_CARCR		PDC_RCR
+#define AC97C_CARNPR		PDC_RNPR
+#define AC97C_CARNCR		PDC_RNCR
+#define AC97C_PTCR		PDC_PTCR
+
+#define AC97C_MR_ENA		(1 << 0)
+#define AC97C_MR_WRST		(1 << 1)
+#define AC97C_MR_VRA		(1 << 2)
+
+#define AC97C_CSR_TXRDY		(1 << 0)
+#define AC97C_CSR_UNRUN		(1 << 2)
+#define AC97C_CSR_RXRDY		(1 << 4)
+#define AC97C_CSR_ENDTX		(1 << 10)
+#define AC97C_CSR_ENDRX		(1 << 14)
+
+#define AC97C_CMR_SIZE_20	(0 << 16)
+#define AC97C_CMR_SIZE_18	(1 << 16)
+#define AC97C_CMR_SIZE_16	(2 << 16)
+#define AC97C_CMR_SIZE_10	(3 << 16)
+#define AC97C_CMR_CEM_LITTLE	(1 << 18)
+#define AC97C_CMR_CEM_BIG	(0 << 18)
+#define AC97C_CMR_CENA		(1 << 21)
+#define AC97C_CMR_PDCEN		(1 << 22)
+
+#define AC97C_SR_CAEVT		(1 << 3)
+
+#define AC97C_CH_ASSIGN(slot, channel)					\
+	(AC97C_CHANNEL_##channel << (3 * (AC97_SLOT_##slot - 3)))
+#define AC97C_CHANNEL_NONE	0x0
+#define AC97C_CHANNEL_A		0x1
+#define AC97C_CHANNEL_B		0x2
+
+#endif /* __SOUND_AVR32_AC97C_H */