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 +