diff options
Diffstat (limited to 'packages/linux/linux-2.6.18/at73c213-alsa-driver.patch')
-rw-r--r-- | packages/linux/linux-2.6.18/at73c213-alsa-driver.patch | 1485 |
1 files changed, 0 insertions, 1485 deletions
diff --git a/packages/linux/linux-2.6.18/at73c213-alsa-driver.patch b/packages/linux/linux-2.6.18/at73c213-alsa-driver.patch deleted file mode 100644 index ceb12cc950..0000000000 --- a/packages/linux/linux-2.6.18/at73c213-alsa-driver.patch +++ /dev/null @@ -1,1485 +0,0 @@ -From nobody Mon Sep 17 00:00:00 2001 -From: Hans-Christian Egtvedt <hcegtvedt@atmel.com> -Date: Fri Apr 28 15:30:44 2006 +0200 -Subject: [PATCH] at73c213 ALSA driver - -This driver uses the SSC and SPI modules to communicate with an at73c213 -sound chip on the AT32STK1000. - ---- - - sound/avr32/Kconfig | 20 - sound/avr32/Makefile | 3 - sound/avr32/at73c213.c | 1296 +++++++++++++++++++++++++++++++++++++++++++++++++ - sound/avr32/at73c213.h | 120 ++++ - 4 files changed, 1439 insertions(+) - create mode 100644 sound/avr32/at73c213.c - create mode 100644 sound/avr32/at73c213.h - -859730d5cbe00b7935c4e30d179c5c5b096deb3c -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 15:56:20.000000000 +0100 -@@ -28,4 +28,24 @@ config SND_ATMEL_AC97C_USE_PDC - Say Y if PDC (Peripheral DMA Controller) is used for DMA transfers - to/from the Atmel AC97C instead of using the generic DMA framework. - -+config SND_AT73C213 -+ tristate "Atmel AT73C213 DAC driver" -+ depends on SND && SPI_ATMEL -+ select SND_PCM -+ help -+ Say Y here if you want to use the Atmel AT73C213 external -+ DAC on the ATSTK1000 development board. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called snd-at73c213. -+ -+config SND_AT73C213_USE_ALSA_MALLOC_CALLS -+ bool "Use the built-in malloc calls in the alsa driver" -+ default n -+ depends on SND_AT73C213 -+ 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 brake the rmmod feature. -+ - 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 15:56:20.000000000 +0100 -@@ -4,3 +4,6 @@ - - snd-atmel-ac97-objs := ac97c.o - obj-$(CONFIG_SND_ATMEL_AC97) += snd-atmel-ac97.o -+ -+snd-at73c213-objs := at73c213.o -+obj-$(CONFIG_SND_AT73C213) += snd-at73c213.o -Index: linux-2.6.18-avr32/sound/avr32/at73c213.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.18-avr32/sound/avr32/at73c213.c 2006-11-02 16:01:55.000000000 +0100 -@@ -0,0 +1,1296 @@ -+/* -+ * Driver for the at73c213 16-bit stereo DAC on Atmel ATSTK1000 -+ * -+ * Copyright (C) 2006 Atmel Norway -+ * -+ * 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. -+ */ -+#undef DEBUG -+#include <linux/clk.h> -+#include <linux/delay.h> -+#include <linux/device.h> -+#include <linux/dma-mapping.h> -+#include <linux/init.h> -+#include <linux/interrupt.h> -+#include <linux/kmod.h> -+#include <linux/module.h> -+#include <linux/platform_device.h> -+ -+#include <sound/initval.h> -+#include <sound/driver.h> -+#include <sound/control.h> -+#include <sound/core.h> -+#include <sound/pcm.h> -+#ifndef SND_AT73C213_USE_ALSA_MALLOC_CALLS -+#include <sound/memalloc.h> -+#endif -+ -+#include <linux/spi/spi.h> -+ -+#include <asm/io.h> -+#include <asm/processor.h> -+ -+#include "at73c213.h" -+ -+/* 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; -+ -+/* Register defines */ -+#define PIOA_BASE 0xFFE02800 -+#define SSC0_BASE 0xFFE01C00 -+#define PM_BASE 0xFFF00000 -+ -+#define PM_CKSEL 0x04 -+#define PM_APBAMASK 0x10 -+#define PM_GCCTRL 0x60 -+ -+#define PIO_PER 0x00 -+#define PIO_PDR 0x04 -+#define PIO_PUER 0x64 -+#define PIO_ASR 0x70 -+#define PIO_BSR 0x74 -+ -+#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 -+ -+/* PDC register definitions */ -+#define PDC_RPR 0x100 -+#define PDC_RCR 0x104 -+#define PDC_TPR 0x108 -+#define PDC_TCR 0x10c -+#define PDC_RNPR 0x110 -+#define PDC_RNCR 0x114 -+#define PDC_TNPR 0x118 -+#define PDC_TNCR 0x11c -+#define PDC_PTCR 0x120 -+#define PDC_PTSR 0x124 -+ -+/* PDC fields definitions */ -+#define PDC_PTCR_RXTEN 0x0001 -+#define PDC_PTCR_RXTDIS 0x0002 -+#define PDC_PTCR_TXTEN 0x0100 -+#define PDC_PTCR_TXTDIS 0x0200 -+ -+static int bitrate; -+static int gclk_div; -+static int ssc_div; -+static int spi = 0; -+static int ssc = 1; -+ -+module_param(spi, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); -+MODULE_PARM_DESC(spi, "Which SPI interface to use to communicate with the at73c213"); -+module_param(ssc, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); -+MODULE_PARM_DESC(ssc, "Which SSC interface to use to communicate with the at73c213"); -+ -+/* Initial AT73C213 register values */ -+static unsigned char snd_at73c213_original_image[18] = -+{ -+ 0x00, /* 00 - CTRL */ -+ 0x05, /* 01 - LLIG */ -+ 0x05, /* 02 - RLIG */ -+ 0x08, /* 03 - LPMG */ -+ 0x08, /* 04 - RPMG */ -+ 0x00, /* 05 - LLOG */ -+ 0x00, /* 06 - RLOG */ -+ 0x22, /* 07 - OLC */ -+ 0x09, /* 08 - MC */ -+ 0x00, /* 09 - CSFC */ -+ 0x00, /* 0A - MISC */ -+ 0x00, /* 0B - */ -+ 0x00, /* 0C - PRECH */ -+ 0x05, /* 0D - AUXG */ -+ 0x00, /* 0E - */ -+ 0x00, /* 0F - */ -+ 0x00, /* 10 - RST */ -+ 0x00, /* 11 - PA_CTRL */ -+}; -+ -+/* chip-specific data */ -+struct snd_at73c213 { -+ snd_card_t *card; -+ snd_pcm_t *pcm; -+ snd_pcm_substream_t *substream; -+ int irq; -+ int period; -+ void __iomem *regs; -+ struct clk *ssc_clk; -+ struct spi_device *spi; -+ u8 spi_wbuffer[2]; -+ u8 spi_rbuffer[2]; -+ /* image of the SPI registers in AT73C213 */ -+ u8 image[18]; -+ spinlock_t lock; -+ struct platform_device *pdev; -+}; -+ -+#define get_chip(card) ((struct snd_at73c213 *)card->private_data) -+ -+static int -+snd_at73c213_write_reg(struct snd_at73c213 *chip, u8 reg, u8 val) -+{ -+ struct spi_message msg; -+ struct spi_transfer msg_xfer = { -+ .len = 2, -+ .cs_change = 0, -+ }; -+ -+ spi_message_init(&msg); -+ -+ chip->spi_wbuffer[0] = reg; -+ chip->spi_wbuffer[1] = val; -+ -+ msg_xfer.tx_buf = chip->spi_wbuffer; -+ msg_xfer.rx_buf = chip->spi_rbuffer; -+ spi_message_add_tail(&msg_xfer, &msg); -+ -+ return spi_sync(chip->spi, &msg); -+} -+ -+#define write_reg(_spi, reg, val) \ -+ do { \ -+ retval = snd_at73c213_write_reg(_spi, reg, val); \ -+ if (retval) \ -+ goto out; \ -+ } while (0) -+ -+static snd_pcm_hardware_t snd_at73c213_playback_hw = { -+ .info = SNDRV_PCM_INFO_INTERLEAVED | -+ SNDRV_PCM_INFO_BLOCK_TRANSFER, -+ .formats = SNDRV_PCM_FMTBIT_S16_BE, -+ .rates = SNDRV_PCM_RATE_CONTINUOUS, -+ .rate_min = 8000, /* This will be overwritten with bitrate */ -+ .rate_max = 50000, /* This will be overwritten with bitrate */ -+ .channels_min = 2, -+ .channels_max = 2, -+ .buffer_bytes_max = 64 * 1024 - 1, -+ .period_bytes_min = 512, -+ .period_bytes_max = 64 * 1024 - 1, -+ .periods_min = 4, -+ .periods_max = 1024, -+}; -+ -+/* calculate and set bitrate and divisions */ -+static int snd_at73c213_set_bitrate_and_div(void) -+{ -+ extern struct avr32_cpuinfo boot_cpu_data; -+ unsigned long pll0_hz, apba_hz; -+ unsigned long apba_realdiv, gclk_realdiv, ssc_realdiv, wanted_bitrate; -+ char cpusel, ahbsel, apbasel; -+ int regval; -+ -+ regval = __raw_readl((void __iomem *)PM_BASE + PM_CKSEL); -+ wanted_bitrate = 48000; -+ -+ cpusel = regval & 0x07; -+ ahbsel = (regval>>8) & 0x07; -+ apbasel = (regval>>16) & 0x07; -+ -+ /* FIXME: Use the clk framework for this */ -+ if ((regval&(1<<7)) != 0) { -+ pll0_hz = clk_get_rate(boot_cpu_data.clk)/(1<<(cpusel+1)); -+ } else { -+ pll0_hz = clk_get_rate(boot_cpu_data.clk); -+ } -+ -+ if ((regval&(1<<23)) != 0) { -+ apba_hz = pll0_hz/(1<<(apbasel+1)); -+ apba_realdiv = (1<<(apbasel+1)); -+ } else { -+ apba_hz = pll0_hz; -+ apba_realdiv = 1; -+ } -+ -+calculate: -+ /* Adjust bitrate as close as possible to 48000 Hz */ -+ gclk_realdiv = pll0_hz/(wanted_bitrate*256); -+ ssc_realdiv = 2 * apba_realdiv * gclk_realdiv; -+ -+ if ((gclk_realdiv % 2) == 0) -+ goto setbitrates; -+ -+ if(wanted_bitrate >= 22050 && wanted_bitrate <= 48000) -+ wanted_bitrate -= 50; -+ else if (wanted_bitrate < 22050) -+ wanted_bitrate = 48050; -+ else if (wanted_bitrate <= 50000) -+ wanted_bitrate += 50; -+ else { -+ printk(KERN_ERR "at73c213 could not set dividers for a valid bitrate\n"); -+ return -EINVAL; -+ } -+ -+ goto calculate; -+ -+setbitrates: -+ bitrate = pll0_hz/(gclk_realdiv*256); -+ gclk_div = (gclk_realdiv/2)-1; -+ ssc_realdiv = 2*apba_realdiv*gclk_realdiv; -+ ssc_div = ssc_realdiv/(2*apba_realdiv); -+ -+ printk(KERN_INFO "at73c213: bitrate is %d Hz\n", bitrate); -+ -+ return 0; -+} -+ -+/* 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; -+ -+ snd_at73c213_playback_hw.rate_min = bitrate; -+ snd_at73c213_playback_hw.rate_max = bitrate; -+ runtime->hw = snd_at73c213_playback_hw; -+ chip->substream = substream; -+ -+ return 0; -+} -+ -+/* 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; -+} -+ -+/* hw_params callback */ -+static int snd_at73c213_pcm_hw_params(snd_pcm_substream_t *substream, -+ snd_pcm_hw_params_t *hw_params) -+{ -+#ifdef SND_AT73C213_USE_ALSA_MALLOC_CALLS -+ return snd_pcm_lib_malloc_pages(substream, -+ params_buffer_bytes(hw_params)); -+#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); -+ -+ /* 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 -+} -+ -+/* hw_free callback */ -+static int snd_at73c213_pcm_hw_free(snd_pcm_substream_t *substream) -+{ -+#ifdef SND_AT73C213_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 -+} -+ -+/* 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); -+ -+ __raw_writel(runtime->dma_addr, chip->regs + PDC_TPR); -+ __raw_writel(runtime->period_size * 2, chip->regs + PDC_TCR); -+ __raw_writel(runtime->dma_addr + block_size, chip->regs + PDC_TNPR); -+ __raw_writel(runtime->period_size * 2, chip->regs + PDC_TNCR); -+ -+ return 0; -+} -+ -+/* 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; -+ int flags = 0; -+ -+ spin_lock_irqsave(&chip->lock, flags); -+ -+ switch (cmd) { -+ case SNDRV_PCM_TRIGGER_START: -+ __raw_writel(SSC_INT_ENDTX, chip->regs + SSC_IER); -+ __raw_writel(PDC_PTCR_TXTEN, chip->regs + PDC_PTCR); -+ break; -+ case SNDRV_PCM_TRIGGER_STOP: -+ __raw_writel(PDC_PTCR_TXTDIS, chip->regs + PDC_PTCR); -+ __raw_writel(SSC_INT_ENDTX, chip->regs + SSC_IDR); -+ break; -+ default: -+ printk(KERN_WARNING "at73c213: spuriouse command %x\n", cmd); -+ retval = -EINVAL; -+ break; -+ } -+ -+ spin_unlock_irqrestore(&chip->lock, flags); -+ -+ return retval; -+} -+ -+/* 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 = __raw_readl(chip->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, -+}; -+ -+/* 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 ) { -+#ifdef SND_AT73C213_USE_ALSA_MALLOC_CALLS -+ snd_pcm_lib_preallocate_free_for_all(chip->pcm); -+#endif -+ chip->pcm = NULL; -+ } -+} -+ -+/* create a new pcm device */ -+static int __devinit snd_at73c213_new_pcm(struct snd_at73c213 *chip, int device) -+{ -+ snd_pcm_t *pcm; -+ int retval; -+ -+ retval = snd_pcm_new(chip->card, chip->card->shortname, device, 1, 0, &pcm); -+ if (retval < 0) -+ return retval; -+ -+ pcm->private_data = chip; -+ pcm->private_free = snd_at73c213_pcm_free; -+ pcm->info_flags = SNDRV_PCM_INFO_BLOCK_TRANSFER; -+ strcpy(pcm->name, "at73c213"); -+ chip->pcm = pcm; -+ -+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &at73c213_playback_ops); -+ -+#ifdef SND_AT73C213_USE_ALSA_MALLOC_CALLS -+ snd_pcm_lib_preallocate_pages_for_all(chip->pcm, SNDRV_DMA_TYPE_DEV, -+ &chip->pdev->dev, 64 * 1024, 64 * 1024); -+#endif -+ -+ return 0; -+} -+ -+static irqreturn_t snd_at73c213_interrupt(int irq, void *dev_id, -+ struct pt_regs *regs) -+{ -+ 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 = __raw_readl(chip->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); -+ __raw_writel(runtime->dma_addr + offset, chip->regs + PDC_TNPR); -+ __raw_writel(runtime->period_size * 2, chip->regs + PDC_TNCR); -+ -+ if (next_period == 0) { -+ (void)__raw_readl(chip->regs + PDC_TPR); -+ (void)__raw_readl(chip->regs + PDC_TCR); -+ } -+ } else { -+ printk(KERN_WARNING -+ "Spurious SSC interrupt, status = 0x%08lx\n", -+ (unsigned long)status); -+ __raw_writel(status, chip->regs + SSC_IDR); -+ } -+ -+ (void)__raw_readl(chip->regs + SSC_IMR); -+ spin_unlock(&chip->lock); -+ -+ if (status & SSC_INT_ENDTX) -+ snd_pcm_period_elapsed(chip->substream); -+ -+ return IRQ_HANDLED; -+} -+ -+/* -+ * Mixer functions -+ */ -+#if 0 /* Function not in use */ -+static int snd_at73c213_mono_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ unsigned long mask = (kcontrol->private_value >> 16) & 0xff; -+ -+ uinfo->type = (mask == 1) ? -+ SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; -+ uinfo->count = 1; -+ uinfo->value.integer.min = 0; -+ uinfo->value.integer.max = mask; -+ -+ return 0; -+} -+#endif -+ -+static int snd_at73c213_mono_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); -+ unsigned long flags; -+ int reg = kcontrol->private_value & 0xff; -+ int shift = (kcontrol->private_value >> 8) & 0xff; -+ int mask = (kcontrol->private_value >> 16) & 0xff; -+ int invert = (kcontrol->private_value >> 24) & 0xff; -+ -+ spin_lock_irqsave(&chip->lock, flags); -+ -+ ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; -+ -+ if (invert) -+ ucontrol->value.integer.value[0] = -+ (mask - ucontrol->value.integer.value[0]); -+ -+ spin_unlock_irqrestore(&chip->lock, flags); -+ -+ return 0; -+} -+ -+static int snd_at73c213_mono_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); -+ unsigned long flags; -+ int reg = kcontrol->private_value & 0xff; -+ int shift = (kcontrol->private_value >> 8) & 0xff; -+ int mask = (kcontrol->private_value >> 16) & 0xff; -+ int invert = (kcontrol->private_value >> 24) & 0xff; -+ int change, retval; -+ unsigned short val; -+ -+ val = (ucontrol->value.integer.value[0] & mask); -+ if (invert) -+ val = mask - val; -+ val <<= shift; -+ -+ spin_lock_irqsave(&chip->lock, flags); -+ -+ val = (chip->image[reg] & ~(mask << shift)) | val; -+ change = val != chip->image[reg]; -+ write_reg(chip, reg, val); -+ -+ chip->image[reg] = val; -+ -+ spin_unlock_irqrestore(&chip->lock, flags); -+ -+ return change; -+ -+out: -+ return retval; -+} -+ -+static int snd_at73c213_stereo_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ int mask = (kcontrol->private_value >> 24) & 0xFF; -+ -+ uinfo->type = mask == 1 ? -+ SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; -+ uinfo->count = 2; -+ uinfo->value.integer.min = 0; -+ uinfo->value.integer.max = mask; -+ -+ return 0; -+} -+ -+static int snd_at73c213_stereo_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); -+ unsigned long flags; -+ int left_reg = kcontrol->private_value & 0xff; -+ int right_reg = (kcontrol->private_value >> 8) & 0xff; -+ int shift_left = (kcontrol->private_value >> 16) & 0x07; -+ int shift_right = (kcontrol->private_value >> 19) & 0x07; -+ int mask = (kcontrol->private_value >> 24) & 0xff; -+ int invert = (kcontrol->private_value >> 22) & 1; -+ -+ spin_lock_irqsave(&chip->lock, flags); -+ -+ ucontrol->value.integer.value[0] = -+ (chip->image[left_reg] >> shift_left) & mask; -+ ucontrol->value.integer.value[1] = -+ (chip->image[right_reg] >> shift_right) & mask; -+ -+ if (invert) { -+ ucontrol->value.integer.value[0] = -+ (mask - ucontrol->value.integer.value[0]); -+ ucontrol->value.integer.value[1] = -+ (mask - ucontrol->value.integer.value[1]); -+ } -+ -+ spin_unlock_irqrestore(&chip->lock, flags); -+ -+ return 0; -+} -+ -+static int snd_at73c213_stereo_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); -+ unsigned long flags; -+ int left_reg = kcontrol->private_value & 0xff; -+ int right_reg = (kcontrol->private_value >> 8) & 0xff; -+ int shift_left = (kcontrol->private_value >> 16) & 0x07; -+ int shift_right = (kcontrol->private_value >> 19) & 0x07; -+ int mask = (kcontrol->private_value >> 24) & 0xff; -+ int invert = (kcontrol->private_value >> 22) & 1; -+ int change, retval; -+ unsigned short val1, val2; -+ -+ val1 = ucontrol->value.integer.value[0] & mask; -+ val2 = ucontrol->value.integer.value[1] & mask; -+ if (invert) { -+ val1 = mask - val1; -+ val2 = mask - val2; -+ } -+ val1 <<= shift_left; -+ val2 <<= shift_right; -+ -+ spin_lock_irqsave(&chip->lock, flags); -+ -+ val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; -+ val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; -+ change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; -+ write_reg(chip, left_reg, val1); -+ write_reg(chip, right_reg, val2); -+ -+ chip->image[left_reg] = val1; -+ chip->image[right_reg] = val2; -+ -+ spin_unlock_irqrestore(&chip->lock, flags); -+ -+ return change; -+ -+out: -+ return retval; -+} -+ -+static int snd_at73c213_mono_switch_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; -+ uinfo->count = 1; -+ uinfo->value.integer.min = 0; -+ uinfo->value.integer.max = 1; -+ -+ return 0; -+} -+ -+static int snd_at73c213_mono_switch_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); -+ unsigned long flags; -+ int reg = kcontrol->private_value & 0xff; -+ int shift = (kcontrol->private_value >> 8) & 0xff; -+ int invert = (kcontrol->private_value >> 24) & 0xff; -+ -+ spin_lock_irqsave(&chip->lock, flags); -+ -+ ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & 0x01; -+ -+ if (invert) -+ ucontrol->value.integer.value[0] = -+ (0x01 - ucontrol->value.integer.value[0]); -+ -+ spin_unlock_irqrestore(&chip->lock, flags); -+ -+ return 0; -+} -+ -+static int snd_at73c213_mono_switch_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); -+ unsigned long flags; -+ int reg = kcontrol->private_value & 0xff; -+ int shift = (kcontrol->private_value >> 8) & 0xff; -+ int mask = (kcontrol->private_value >> 16) & 0xff; -+ int invert = (kcontrol->private_value >> 24) & 0xff; -+ int change, retval; -+ unsigned short val; -+ -+ if (ucontrol->value.integer.value[0]) -+ val = mask; -+ else -+ val = 0; -+ -+ if (invert) -+ val = mask - val; -+ val <<= shift; -+ -+ spin_lock_irqsave(&chip->lock, flags); -+ -+ val |= (chip->image[reg] & ~(mask << shift)); -+ change = val != chip->image[reg]; -+ -+ write_reg(chip, reg, val); -+ -+ chip->image[reg] = val; -+ -+ spin_unlock_irqrestore(&chip->lock, flags); -+ -+ return change; -+ -+out: -+ return retval; -+} -+ -+static int snd_at73c213_pa_volume_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; -+ uinfo->count = 1; -+ uinfo->value.integer.min = 0; -+ uinfo->value.integer.max = ((kcontrol->private_value >> 16) & 0xFF) - 1; -+ -+ return 0; -+} -+ -+static int snd_at73c213_line_capture_volume_info( -+ struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; -+ uinfo->count = 2; -+ uinfo->value.integer.min = 14; -+ uinfo->value.integer.max = 31; -+ -+ return 0; -+} -+ -+static int snd_at73c213_aux_capture_volume_info( -+ struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; -+ uinfo->count = 1; -+ uinfo->value.integer.min = 14; -+ uinfo->value.integer.max = 31; -+ -+ return 0; -+} -+ -+#define AT73C213_MONO(xname, xindex, reg, shift, mask, invert) \ -+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ -+ .info = snd_at73c213_mono_info, \ -+ .get = snd_at73c213_mono_get, .put = snd_at73c213_mono_put, \ -+ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } -+ -+#define AT73C213_MONO_SWITCH(xname, xindex, reg, shift, mask, invert) \ -+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ -+ .info = snd_at73c213_mono_switch_info, \ -+ .get = snd_at73c213_mono_switch_get, .put = snd_at73c213_mono_switch_put, \ -+ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } -+ -+#define AT73C213_STEREO(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ -+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ -+ .info = snd_at73c213_stereo_info, \ -+ .get = snd_at73c213_stereo_get, .put = snd_at73c213_stereo_put, \ -+ .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } -+ -+static struct snd_kcontrol_new snd_at73c213_controls[] __devinitdata = { -+AT73C213_STEREO("Master Playback Volume", 0, DAC_LMPG, DAC_RMPG, 0, 0, 0x1F, 1), -+AT73C213_STEREO("Master Playback Switch", 0, DAC_LMPG, DAC_RMPG, 5, 5, 1, 1), -+AT73C213_STEREO("PCM Playback Volume", 0, DAC_LLOG, DAC_RLOG, 0, 0, 0x1F, 1), -+AT73C213_STEREO("PCM Playback Switch", 0, DAC_LLOG, DAC_RLOG, 5, 5, 1, 1), -+AT73C213_MONO_SWITCH("Mono PA Playback Switch", 0, DAC_CTRL, DAC_CTRL_ONPADRV, 0x01, 0), -+{ -+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, -+ .name = "PA Playback Volume", -+ .index = 0, -+ .info = snd_at73c213_pa_volume_info, -+ .get = snd_at73c213_mono_get, -+ .put = snd_at73c213_mono_put, -+ .private_value = PA_CTRL|(PA_CTRL_APAGAIN<<8)|(0x0F<<16)|(1<<24), -+}, -+AT73C213_MONO_SWITCH("PA High Gain Playback Switch", 0, PA_CTRL, PA_CTRL_APALP, 0x01, 1), -+AT73C213_MONO_SWITCH("PA Playback Switch", 0, PA_CTRL, PA_CTRL_APAON, 0x01, 0), -+{ -+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, -+ .name = "Aux Capture Volume", -+ .index = 0, -+ .info = snd_at73c213_aux_capture_volume_info, -+ .get = snd_at73c213_mono_get, -+ .put = snd_at73c213_mono_put, -+ .private_value = DAC_AUXG|(0<<8)|(0x1F<<16)|(1<<24), -+}, -+AT73C213_MONO_SWITCH("Aux Capture Switch", 0, DAC_CTRL, DAC_CTRL_ONAUXIN, 0x01, 0), -+{ -+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, -+ .name = "Line Capture Volume", -+ .index = 0, -+ .info = snd_at73c213_line_capture_volume_info, -+ .get = snd_at73c213_stereo_get, -+ .put = snd_at73c213_stereo_put, -+ .private_value = DAC_LLIG|(DAC_RLIG<<8)|(0<<16)|(0<<19)|(0x1F<<24)|(1<<22), -+}, -+AT73C213_MONO_SWITCH("Line Capture Switch", 0, DAC_CTRL, 0, 0x03, 0), -+}; -+ -+static int __devinit snd_at73c213_mixer(struct snd_at73c213 *chip) -+{ -+ struct snd_card *card; -+ int errval, idx; -+ -+ if (chip == NULL || chip->pcm == NULL) -+ return -EINVAL; -+ -+ card = chip->card; -+ -+ strcpy(card->mixername, chip->pcm->name); -+ -+ for (idx = 0; idx < ARRAY_SIZE(snd_at73c213_controls); idx++) { -+ if ((errval = snd_ctl_add(card, -+ snd_ctl_new1(&snd_at73c213_controls[idx], -+ chip))) < 0) -+ return errval; -+ } -+ -+ return 0; -+} -+ -+/* -+ * Device functions -+ */ -+static int snd_at73c213_chip_init(struct snd_at73c213 *chip) -+{ -+ int retval; -+ unsigned char dac_ctrl = 0; -+ -+ /* XXX: Unmask the APB clock for SSC0 */ -+ __raw_writel(__raw_readl((void __iomem *)PM_BASE + PM_APBAMASK)|(1<<7), -+ (void __iomem *)PM_BASE + PM_APBAMASK); -+ -+ /* Wait for clock to be stable */ -+ msleep(10); -+ -+ retval = snd_at73c213_set_bitrate_and_div(); -+ if (retval) -+ goto out; -+ -+ /* Reset the SSC */ -+ __raw_writel(SSC_CR_SWRST, chip->regs + SSC_CR); -+ -+ /* Enable GCLK0 */ -+ __raw_writel((1<<30), (void __iomem *)(PIOA_BASE + PIO_PDR)); -+ __raw_writel((1<<30), (void __iomem *)(PIOA_BASE + PIO_ASR)); -+ __raw_writel(((gclk_div<<8)|0x10|0x04|0x02), (void __iomem *)(PM_BASE + PM_GCCTRL)); -+ -+ /* Enable SSC and setup for I2S */ -+ __raw_writel(ssc_div, chip->regs + SSC_CMR); -+ -+ /* CKO, START, STTDLY, PERIOD */ -+ __raw_writel((1<<2)|(4<<8)|(1<<16)|(15<<24), chip->regs + SSC_TCMR); -+ -+ /* DATLEN, MSBF, DATNB, FSLEN, FSOS */ -+ __raw_writel((15<<0)|(1<<7)|(1<<8)|(15<<16)|(1<<20), chip->regs + SSC_TFMR); -+ -+ /* Initialize at73c213 on SPI bus */ -+ /* Reset the device */ -+ write_reg(chip, DAC_RST, 0x04); -+ msleep(1); -+ write_reg(chip, DAC_RST, 0x03); -+ -+ /* Turn on precharge */ -+ write_reg(chip, DAC_PRECH, 0xFF); -+ write_reg(chip, PA_CTRL, (1<<PA_CTRL_APAPRECH)); -+ write_reg(chip, DAC_CTRL, (1<<DAC_CTRL_ONLNOL)|(1<<DAC_CTRL_ONLNOR)); -+ -+ msleep(50); -+ -+ /* Stop precharging PA */ -+ write_reg(chip, PA_CTRL, (1<<PA_CTRL_APALP)|0x0F); -+ chip->image[PA_CTRL] = (1<<PA_CTRL_APALP)|0x0F; -+ -+ msleep(450); -+ -+ /* Stop precharging, turn on master power */ -+ write_reg(chip, DAC_PRECH, (1<<DAC_PRECH_ONMSTR)); -+ chip->image[DAC_PRECH] = (1<<DAC_PRECH_ONMSTR); -+ -+ msleep(1); -+ -+ /* Turn on DAC */ -+ dac_ctrl = (1<<DAC_CTRL_ONDACL)|(1<<DAC_CTRL_ONDACR)| -+ (1<<DAC_CTRL_ONLNOL)|(1<<DAC_CTRL_ONLNOR); -+ -+ write_reg(chip, DAC_CTRL, dac_ctrl); -+ chip->image[DAC_CTRL] = dac_ctrl; -+ -+ /* Mute sound */ -+ write_reg(chip, DAC_LMPG, 0x3F); -+ chip->image[DAC_LMPG] = 0x3F; -+ write_reg(chip, DAC_RMPG, 0x3F); -+ chip->image[DAC_RMPG] = 0x3F; -+ write_reg(chip, DAC_LLOG, 0x3F); -+ chip->image[DAC_LLOG] = 0x3F; -+ write_reg(chip, DAC_RLOG, 0x3F); -+ chip->image[DAC_RLOG] = 0x3F; -+ write_reg(chip, DAC_LLIG, 0x11); -+ chip->image[DAC_LLIG] = 0x11; -+ write_reg(chip, DAC_RLIG, 0x11); -+ chip->image[DAC_RLIG] = 0x11; -+ write_reg(chip, DAC_AUXG, 0x11); -+ chip->image[DAC_AUXG] = 0x11; -+ -+ /* Turn on SSC transmitter */ -+ __raw_writel(SSC_CR_TXEN, chip->regs + SSC_CR); -+ -+out: -+ return retval; -+} -+ -+static int snd_at73c213_dev_free(snd_device_t *device) -+{ -+ struct snd_at73c213 *chip = device->device_data; -+ -+ if (chip->regs) { -+ __raw_writel(SSC_CR_TXDIS, chip->regs + SSC_CR); -+ iounmap(chip->regs); -+ } -+ -+ if (chip->irq >= 0) -+ free_irq(chip->irq, chip); -+ -+ if (chip->ssc_clk) { -+ clk_disable(chip->ssc_clk); -+ clk_put(chip->ssc_clk); -+ } -+ -+ return 0; -+} -+ -+static int __devinit snd_at73c213_create(snd_card_t *card, -+ struct platform_device *pdev) -+{ -+ static snd_device_ops_t ops = { -+ .dev_free = snd_at73c213_dev_free, -+ }; -+ struct snd_at73c213 *chip = get_chip(card); -+ struct resource *regs; -+ struct clk *ssc_clk; -+ int irq, retval; -+ -+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!regs) -+ return -ENXIO; -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) -+ return irq; -+ -+ ssc_clk = clk_get(&pdev->dev, "mck"); -+ if (IS_ERR(ssc_clk)) -+ return PTR_ERR(ssc_clk); -+ clk_enable(ssc_clk); -+ chip->ssc_clk = ssc_clk; -+ -+ spin_lock_init(&chip->lock); -+ chip->card = card; -+ chip->pdev = pdev; -+ chip->irq = -1; -+ -+ retval = -ENOMEM; -+ -+ retval = spi_setup(chip->spi); -+ if (retval) -+ goto out; -+ -+ chip->regs = ioremap(regs->start, regs->end - regs->start + 1); -+ if (!chip->regs) -+ goto out; -+ -+ retval = request_irq(irq, snd_at73c213_interrupt, 0, "at73c213", chip); -+ if (retval) { -+ snd_printk("unable to request IRQ%d\n", irq); -+ goto out; -+ } -+ chip->irq = irq; -+ -+ memcpy(&chip->image, &snd_at73c213_original_image, -+ sizeof(snd_at73c213_original_image)); -+ -+ retval = snd_at73c213_chip_init(chip); -+ if (retval) -+ goto out; -+ -+ retval = snd_at73c213_new_pcm(chip, 0); -+ if (retval) -+ goto out; -+ -+ retval = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); -+ if (retval) -+ goto out; -+ -+ retval = snd_at73c213_mixer(chip); -+ if (retval) -+ goto out; -+ -+ snd_card_set_dev(card, &pdev->dev); -+ -+out: -+ return retval; -+} -+ -+static int __devinit snd_at73c213_probe(struct platform_device *pdev) -+{ -+ static int dev; -+ struct spi_board_info *binfo; -+ struct spi_master *smaster; -+ struct snd_at73c213 *chip; -+ snd_card_t *card; -+ int retval; -+ -+ if (dev >= SNDRV_CARDS) -+ return -ENODEV; -+ if (!enable[dev]) { -+ dev++; -+ return -ENOENT; -+ } -+ -+ if (spi < 0 || ssc < 0) -+ return -ENODEV; -+ -+ retval = -ENOMEM; -+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, -+ sizeof(struct snd_at73c213)); -+ if (!card) -+ goto out; -+ -+ chip = card->private_data; -+ -+ retval = -ENODEV; -+ -+ /* Get the SPI bus */ -+ binfo = pdev->dev.platform_data; -+ if (!binfo) { -+ printk(KERN_WARNING "at73c213: could not get platform data\n"); -+ goto out; -+ } -+ -+ smaster = spi_busnum_to_master(spi); -+ if (!smaster) { -+ request_module("spi1"); -+ smaster = spi_busnum_to_master(spi); -+ if (!smaster) { -+ printk(KERN_WARNING -+ "at73c213: could not get " -+ "SPI bus %d, remembered to load " -+ "the spi_atmel module?\n", spi); -+ goto out; -+ } -+ } -+ -+ chip->spi = spi_new_device(smaster, binfo); -+ if (!chip->spi) { -+ printk(KERN_WARNING "at73c213: could not get SPI device %d\n", spi); -+ goto out; -+ } -+ -+ chip->spi->mode = SPI_MODE_1; -+ chip->spi->bits_per_word = 8; -+ -+ retval = snd_at73c213_create(card, pdev); -+ if (retval) -+ goto out_free_card; -+ -+ strcpy(card->driver, "at73c213"); -+ strcpy(card->shortname, "at73c213 (AVR32 STK1000)"); -+ sprintf(card->longname, "%s at %p (irq %i)", card->shortname, chip->regs, chip->irq); -+ -+ retval = snd_card_register(card); -+ if (retval) -+ goto out_free_card; -+ -+ platform_set_drvdata(pdev, card); -+ dev++; -+ return 0; -+ -+out_free_card: -+ snd_card_free(card); -+out: -+ return retval; -+} -+ -+static int __devexit snd_at73c213_remove(struct platform_device *pdev) -+{ -+ struct snd_card *card = platform_get_drvdata(pdev); -+ struct snd_at73c213 *chip = card->private_data; -+ int retval; -+ -+ /* Stop playback */ -+ __raw_writel(SSC_CR_TXDIS, chip->regs + SSC_CR); -+ -+ /* Stop GLCK0 */ -+ __raw_writel(0, (void __iomem *)PM_BASE + PM_GCCTRL); -+ -+ /* Mute sound */ -+ write_reg(chip, DAC_LMPG, 0x3F); -+ chip->image[DAC_LMPG] = 0x3F; -+ write_reg(chip, DAC_RMPG, 0x3F); -+ chip->image[DAC_RMPG] = 0x3F; -+ write_reg(chip, DAC_LLOG, 0x3F); -+ chip->image[DAC_LLOG] = 0x3F; -+ write_reg(chip, DAC_RLOG, 0x3F); -+ chip->image[DAC_RLOG] = 0x3F; -+ write_reg(chip, DAC_LLIG, 0x11); -+ chip->image[DAC_LLIG] = 0x11; -+ write_reg(chip, DAC_RLIG, 0x11); -+ chip->image[DAC_RLIG] = 0x11; -+ write_reg(chip, DAC_AUXG, 0x11); -+ chip->image[DAC_AUXG] = 0x11; -+ -+ /* Turn off PA */ -+ write_reg(chip, PA_CTRL, (chip->image[PA_CTRL]|0x0F)); -+ chip->image[PA_CTRL] |= 0x0F; -+ msleep(10); -+ write_reg(chip, PA_CTRL, (1<<PA_CTRL_APALP)|0x0F); -+ chip->image[PA_CTRL] = (1<<PA_CTRL_APALP)|0x0F; -+ -+ /* Turn off external DAC */ -+ write_reg(chip, DAC_CTRL, 0x0C); -+ chip->image[DAC_CTRL] = 0x0C; -+ msleep(2); -+ write_reg(chip, DAC_CTRL, 0x00); -+ chip->image[DAC_CTRL] = 0x00; -+ -+ /* Turn off master power */ -+ write_reg(chip, DAC_PRECH, 0x00); -+ chip->image[DAC_PRECH] = 0x00; -+ -+ msleep(10); -+ -+out: -+ if (chip->spi) -+ spi_unregister_device(chip->spi); -+ -+ if (card) { -+ snd_card_free(card); -+ platform_set_drvdata(pdev, NULL); -+ } -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PM -+static int snd_at73c213_suspend(struct platform_device *pdev, pm_message_t state, u32 level) -+{ -+ struct snd_card *card = at32_get_drvdata(pdev); -+ struct snd_at73c213 *chip = card->private_data; -+ -+ printk(KERN_DEBUG "at73c213: suspending\n"); -+ -+ /* Stop SSC and GCLK0 */ -+ -+ spi_suspend(chip->spi, state); -+ -+ return 0; -+} -+ -+static int snd_at73c213_resume(struct platform_device *pdev, u32 level) -+{ -+ struct snd_card *card = at32_get_drvdata(pdev); -+ struct snd_at73c213 *chip = card->private_data; -+ -+ printk(KERN_DEBUG "at73c213: resuming\n"); -+ -+ /* Start GLCK0 and SSC */ -+ -+ spi_resume(chip->spi); -+ -+ return 0; -+} -+#endif /* CONFIG_PM */ -+ -+/* Driver core initialization */ -+static struct platform_driver at73c213_driver = { -+ .probe = snd_at73c213_probe, -+ .remove = __devexit_p(snd_at73c213_remove), -+ .driver = { -+ .name = "at73c213", -+ } -+#ifdef CONFIG_PM -+ .resume = snd_at73c213_resume, -+ .suspend = snd_at73c213_suspend, -+#endif -+}; -+ -+static int __init at73c213_init(void) -+{ -+ return platform_driver_register(&at73c213_driver); -+} -+ -+static void __exit at73c213_exit(void) -+{ -+ platform_driver_unregister(&at73c213_driver); -+} -+ -+MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>"); -+MODULE_DESCRIPTION("Sound driver for at73c213 on STK1000"); -+MODULE_LICENSE("GPL"); -+ -+module_init(at73c213_init); -+module_exit(at73c213_exit); -+ -Index: linux-2.6.18-avr32/sound/avr32/at73c213.h -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.18-avr32/sound/avr32/at73c213.h 2006-11-02 15:56:20.000000000 +0100 -@@ -0,0 +1,120 @@ -+/* -+ * Driver for the AT73C213 16-bit stereo DAC on Atmel ATSTK1000 -+ * -+ * Copyright (C) 2006 Atmel Norway -+ * -+ * 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 -+ -+#endif -+ |