diff options
author | Koen Kooi <koen@openembedded.org> | 2007-10-07 09:13:26 +0000 |
---|---|---|
committer | Koen Kooi <koen@openembedded.org> | 2007-10-07 09:13:26 +0000 |
commit | d6fd6d24ea9cef7d661bed9424f54a7719bea5dc (patch) | |
tree | 53b4312019771dd2586fd4349cde67c12689b5e8 /packages/linux/linux-2.6.18/atmel-ac97c-alsa-driver.patch | |
parent | 950f437e524e56366b8e811acb729fb20d7d23f7 (diff) | |
parent | f2253ce9643943256c0cfe4f73c662a11c1792ca (diff) |
propagate from branch 'org.openembedded.dev' (head 23aeaf6386edeac3312ef7bf65975595704646cb)
to branch 'org.openembedded.dev.avr32' (head 60ed8ee80ed7d3ce9fd1d319931577988ff07968)
Diffstat (limited to 'packages/linux/linux-2.6.18/atmel-ac97c-alsa-driver.patch')
-rw-r--r-- | packages/linux/linux-2.6.18/atmel-ac97c-alsa-driver.patch | 1383 |
1 files changed, 1383 insertions, 0 deletions
diff --git a/packages/linux/linux-2.6.18/atmel-ac97c-alsa-driver.patch b/packages/linux/linux-2.6.18/atmel-ac97c-alsa-driver.patch new file mode 100644 index 0000000000..fe3d2ee209 --- /dev/null +++ b/packages/linux/linux-2.6.18/atmel-ac97c-alsa-driver.patch @@ -0,0 +1,1383 @@ +--- + 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 */ |