Alsa-devel
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
July 2007
- 109 participants
- 155 discussions
[alsa-devel] [Fwd: Re: default setting not fully working with new ALSA device]
by Tobin Davis 05 Jul '07
by Tobin Davis 05 Jul '07
05 Jul '07
-------- Forwarded Message --------
From: Peter Matthias <PeterMatthias(a)web.de>
To: Tobin Davis <tdavis(a)dsl-only.net>
Subject: Re: [alsa-devel] default setting not fully working with new
ALSA device
Date: Thu, 5 Jul 2007 18:34:08 +0200
The Board is a MSI K9AGM2-FIH, Sockel AM2, ATI RS690G, mATX, PCIe
The script gave following output:
"Your ALSA information is located at http://pastebin.ca/604529"
Other drivers are statically linked in my kernel.
Thank you for your work! In case of any questions, contact me.
Peter
Am Mittwoch, 4. Juli 2007 schrieben Sie:
> Could you post the output from
> http://bulletproof.servebeer.com/alsa/scripts/alsa-info.sh ? This
> script will give us more detailed information on what sound chip it is.
> We will also need to know the brand of board, for the driver file.
>
> Thanks,
>
> Tobin
>
> On Wed, 2007-07-04 at 16:48 +0200, Peter Matthias wrote:
> > Hello,
> >
> > I have got a 690G board with following lspic -nv output:
> >
> > 01:05.2 0403: 1002:7919
> > Subsystem: 1002:7919
> > Flags: bus master, fast devsel, latency 64, IRQ 19
> > Memory at fe9e8000 (64-bit, non-prefetchable) [size=16K]
> > Capabilities: [50] Power Management version 2
> > Capabilities: [60] Message Signalled Interrupts: 64bit+ Queue=0/0
> > Enable-
> >
> > With the default setting, microphone does not work. With module
> > parameter "modprobe -i snd-hda-intel model=6stack-dig" microphone works
> > well. Please add to default setting. It was written in Linux Kernel
> > documentation to report this issue here.
> >
> > Thank you,
> > Peter
> > _______________________________________________
> > Alsa-devel mailing list
> > Alsa-devel(a)alsa-project.org
> > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
--
Tobin Davis <tdavis(a)dsl-only.net>
1
0
[PATCH 1/1] ALSA sound driver for the AT73C213 DAC using Atmel SSC driver
by Hans-Christian Egtvedt 05 Jul '07
by Hans-Christian Egtvedt 05 Jul '07
05 Jul '07
This patch adds support for the AT73C213 DAC using the misc Atmel SSC driver in
I2S mode. The driver also requires a SPI to setup the registers and control
volume.
It has been tested with an AT32AP7000 on the ATSTK1000 development board. The
driver should also work with any Atmel device with an SSC module supported by
the Atmel SSC driver (atmel-ssc).
The atmel-ssc driver is just submitted to the Linux kernel. Please see mail
thread http://lkml.org/lkml/2007/7/16/32
Signed-off-by: Hans-Christian Egtvedt <hcegtvedt(a)atmel.com>
---
include/linux/spi/at73c213.h | 25 +
sound/spi/Kconfig | 25 +
sound/spi/Makefile | 3 +
sound/spi/at73c213.c | 1121 ++++++++++++++++++++++++++++++++++++++++++
sound/spi/at73c213.h | 119 +++++
5 files changed, 1293 insertions(+), 0 deletions(-)
create mode 100644 include/linux/spi/at73c213.h
create mode 100644 sound/spi/at73c213.c
create mode 100644 sound/spi/at73c213.h
diff --git a/include/linux/spi/at73c213.h b/include/linux/spi/at73c213.h
new file mode 100644
index 0000000..0f20a70
--- /dev/null
+++ b/include/linux/spi/at73c213.h
@@ -0,0 +1,25 @@
+/*
+ * Board-specific data used to set up AT73c213 audio DAC driver.
+ */
+
+#ifndef __LINUX_SPI_AT73C213_H
+#define __LINUX_SPI_AT73C213_H
+
+/**
+ * at73c213_board_info - how the external DAC is wired to the device.
+ *
+ * @ssc_id: SSC platform_driver id the DAC shall use to stream the audio.
+ * @dac_clk: the external clock used to provide master clock to the DAC.
+ * @shortname: a short discription for the DAC, seen by userspace tools.
+ *
+ * This struct contains the configuration of the hardware connection to the
+ * external DAC. The DAC needs a master clock and a I2S audio stream. It also
+ * provides a name which is used to identify it in userspace tools.
+ */
+struct at73c213_board_info {
+ int ssc_id;
+ struct clk *dac_clk;
+ char shortname[32];
+};
+
+#endif /* __LINUX_SPI_AT73C213_H */
diff --git a/sound/spi/Kconfig b/sound/spi/Kconfig
index 60fbb2a..0d08c29 100644
--- a/sound/spi/Kconfig
+++ b/sound/spi/Kconfig
@@ -3,4 +3,29 @@
menu "SPI devices"
depends on SND != n
+config SND_AT73C213
+ tristate "Atmel AT73C213 DAC driver"
+ depends on ATMEL_SSC
+ select SND_PCM
+ help
+ Say Y here if you want to use the Atmel AT73C213 external DAC. This
+ DAC can be found on Atmel development boards.
+
+ This driver requires the Atmel SSC driver for sound sink, a
+ peripheral found on most AT91 and AVR32 microprocessors.
+
+ To compile this driver as a module, choose M here: the module will be
+ called snd-at73c213.
+
+config SND_AT73C213_TARGET_BITRATE
+ int "Target bitrate for AT73C213"
+ depends on SND_AT73C213
+ default "48000"
+ range 8000 50000
+ help
+ Sets the target bitrate for the bitrate calculator in the driver.
+ Limited by hardware to be between 8000 Hz and 50000 Hz.
+
+ Set to 48000 Hz by default.
+
endmenu
diff --git a/sound/spi/Makefile b/sound/spi/Makefile
index 30d8ec9..026fb73 100644
--- a/sound/spi/Makefile
+++ b/sound/spi/Makefile
@@ -1,2 +1,5 @@
# Makefile for SPI drivers
+snd-at73c213-objs := at73c213.o
+
+obj-$(CONFIG_SND_AT73C213) += snd-at73c213.o
diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c
new file mode 100644
index 0000000..a466929
--- /dev/null
+++ b/sound/spi/at73c213.c
@@ -0,0 +1,1121 @@
+/*
+ * Driver for AT73C213 16-bit stereo DAC connected to Atmel SSC
+ *
+ * Copyright (C) 2006-2007 Atmel Norway
+ *
+ * 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.
+ */
+
+/*#define DEBUG*/
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/device.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/io.h>
+
+#include <sound/driver.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include <linux/atmel-ssc.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/at73c213.h>
+
+#include "at73c213.h"
+
+#define BITRATE_MIN 8000 /* Hardware limit? */
+#define BITRATE_TARGET CONFIG_SND_AT73C213_TARGET_BITRATE
+#define BITRATE_MAX 50000 /* Hardware limit. */
+
+/* Initial (hardware reset) AT73C213 register values. */
+static u8 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 */
+};
+
+struct snd_at73c213 {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct snd_pcm_substream *substream;
+ struct at73c213_board_info *board;
+ int irq;
+ int period;
+ unsigned long bitrate;
+ struct clk *bitclk;
+ struct ssc_device *ssc;
+ struct spi_device *spi;
+ u8 spi_wbuffer[2];
+ u8 spi_rbuffer[2];
+ /* Image of the SPI registers in AT73C213. */
+ u8 reg_image[18];
+ /* Protect registers against concurrent access. */
+ spinlock_t lock;
+};
+
+#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,
+ };
+ int retval;
+
+ 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);
+
+ retval = spi_sync(chip->spi, &msg);
+
+ if (!retval)
+ chip->reg_image[reg] = val;
+
+ return retval;
+}
+
+static struct snd_pcm_hardware 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, /* Replaced by chip->bitrate later. */
+ .rate_max = 50000, /* Replaced by chip->bitrate later. */
+ .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(struct snd_at73c213 *chip)
+{
+ unsigned long ssc_rate = clk_get_rate(chip->ssc->clk);
+ unsigned long dac_rate_new, ssc_div, status;
+ unsigned long ssc_div_max, ssc_div_min;
+ int max_tries;
+
+ /*
+ * We connect two clocks here, picking divisors so the I2S clocks
+ * out data at the same rate the DAC clocks it in ... and as close
+ * as practical to the desired target rate.
+ *
+ * The DAC master clock (MCLK) is programmable, and is either 256
+ * or (not here) 384 times the I2S output clock (BCLK).
+ */
+
+ /* SSC clock / (bitrate * stereo * 16-bit). */
+ ssc_div = ssc_rate / (BITRATE_TARGET * 2 * 16);
+ ssc_div_min = ssc_rate / (BITRATE_MAX * 2 * 16);
+ ssc_div_max = ssc_rate / (BITRATE_MIN * 2 * 16);
+ max_tries = (ssc_div_max - ssc_div_min) / 2;
+
+ if (max_tries < 1)
+ max_tries = 1;
+
+ /* ssc_div must be a power of 2. */
+ ssc_div = (ssc_div + 1) & ~1UL;
+
+ if ((ssc_rate / (ssc_div * 2 * 16)) < BITRATE_MIN) {
+ ssc_div -= 2;
+ if ((ssc_rate / (ssc_div * 2 * 16)) > BITRATE_MAX)
+ return -ENXIO;
+ }
+
+ /* Search for a possible bitrate. */
+ do {
+ /* SSC clock / (ssc divider * 16-bit * stereo). */
+ if ((ssc_rate / (ssc_div * 2 * 16)) < BITRATE_MIN)
+ return -ENXIO;
+
+ /* 256 / (2 * 16) = 8 */
+ dac_rate_new = 8 * (ssc_rate / ssc_div);
+
+ status = clk_round_rate(chip->board->dac_clk, dac_rate_new);
+ if (status < 0)
+ return status;
+
+ /* Ignore difference smaller than 256 Hz. */
+ if ((status/256) == (dac_rate_new/256))
+ goto set_rate;
+
+ ssc_div += 2;
+ } while (--max_tries);
+
+ /* Not able to find a valid bitrate. */
+ return -ENXIO;
+
+set_rate:
+ status = clk_set_rate(chip->board->dac_clk, status);
+ if (status < 0)
+ return status;
+
+ /* Set divider in SSC device. */
+ ssc_writel(chip->ssc->regs, CMR, ssc_div/2);
+
+ /* SSC clock / (ssc divider * 16-bit * stereo). */
+ chip->bitrate = ssc_rate / (ssc_div * 16 * 2);
+
+ dev_info(&chip->spi->dev,
+ "at73c213: supported bitrate is %lu (%lu divider)\n",
+ chip->bitrate, ssc_div);
+
+ return 0;
+}
+
+static int snd_at73c213_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ snd_at73c213_playback_hw.rate_min = chip->bitrate;
+ snd_at73c213_playback_hw.rate_max = chip->bitrate;
+ runtime->hw = snd_at73c213_playback_hw;
+ chip->substream = substream;
+
+ return 0;
+}
+
+static int snd_at73c213_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
+ chip->substream = NULL;
+ return 0;
+}
+
+static int snd_at73c213_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int snd_at73c213_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_at73c213_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int block_size;
+
+ block_size = frames_to_bytes(runtime, runtime->period_size);
+
+ chip->period = 0;
+
+ ssc_writel(chip->ssc->regs, PDC_TPR,
+ (long)runtime->dma_addr);
+ ssc_writel(chip->ssc->regs, PDC_TCR, runtime->period_size * 2);
+ ssc_writel(chip->ssc->regs, PDC_TNPR,
+ (long)runtime->dma_addr + block_size);
+ ssc_writel(chip->ssc->regs, PDC_TNCR, runtime->period_size * 2);
+
+ return 0;
+}
+
+static int snd_at73c213_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
+ int retval = 0;
+
+ spin_lock(&chip->lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ ssc_writel(chip->ssc->regs, IER, SSC_BIT(IER_ENDTX));
+ ssc_writel(chip->ssc->regs, PDC_PTCR, SSC_BIT(PDC_PTCR_TXTEN));
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ ssc_writel(chip->ssc->regs, PDC_PTCR, SSC_BIT(PDC_PTCR_TXTDIS));
+ ssc_writel(chip->ssc->regs, IDR, SSC_BIT(IDR_ENDTX));
+ break;
+ default:
+ dev_dbg(&chip->spi->dev, "spurious command %x\n", cmd);
+ retval = -EINVAL;
+ break;
+ }
+
+ spin_unlock(&chip->lock);
+
+ return retval;
+}
+
+static snd_pcm_uframes_t
+snd_at73c213_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_uframes_t pos;
+ unsigned long bytes;
+
+ bytes = ssc_readl(chip->ssc->regs, PDC_TPR)
+ - (unsigned long)runtime->dma_addr;
+
+ pos = bytes_to_frames(runtime, bytes);
+ if (pos >= runtime->buffer_size)
+ pos -= runtime->buffer_size;
+
+ return pos;
+}
+
+static struct snd_pcm_ops 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,
+};
+
+static void snd_at73c213_pcm_free(struct snd_pcm *pcm)
+{
+ struct snd_at73c213 *chip = snd_pcm_chip(pcm);
+ if (chip->pcm) {
+ snd_pcm_lib_preallocate_free_for_all(chip->pcm);
+ chip->pcm = NULL;
+ }
+}
+
+static int __devinit snd_at73c213_pcm_new(struct snd_at73c213 *chip, int device)
+{
+ struct snd_pcm *pcm;
+ int retval;
+
+ retval = snd_pcm_new(chip->card, chip->card->shortname,
+ device, 1, 0, &pcm);
+ if (retval < 0)
+ goto out;
+
+ 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);
+
+ retval = snd_pcm_lib_preallocate_pages_for_all(chip->pcm,
+ SNDRV_DMA_TYPE_DEV, &chip->spi->dev,
+ 64 * 1024, 64 * 1024);
+out:
+ return retval;
+}
+
+static irqreturn_t snd_at73c213_interrupt(int irq, void *dev_id)
+{
+ struct snd_at73c213 *chip = dev_id;
+ struct snd_pcm_runtime *runtime = chip->substream->runtime;
+ u32 status;
+ int offset;
+ int block_size;
+ int next_period;
+ int retval = IRQ_NONE;
+
+ spin_lock(&chip->lock);
+
+ block_size = frames_to_bytes(runtime, runtime->period_size);
+ status = ssc_readl(chip->ssc->regs, IMR);
+
+ if (status & SSC_BIT(IMR_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;
+
+ ssc_writel(chip->ssc->regs, PDC_TNPR,
+ (long)runtime->dma_addr + offset);
+ ssc_writel(chip->ssc->regs, PDC_TNCR, runtime->period_size * 2);
+ retval = IRQ_HANDLED;
+ }
+
+ ssc_readl(chip->ssc->regs, IMR);
+ spin_unlock(&chip->lock);
+
+ if (status & SSC_BIT(IMR_ENDTX))
+ snd_pcm_period_elapsed(chip->substream);
+
+ return retval;
+}
+
+/*
+ * Mixer functions.
+ */
+static int snd_at73c213_mono_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol);
+ 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_irq(&chip->lock);
+
+ ucontrol->value.integer.value[0] = (chip->reg_image[reg] >> shift) & mask;
+
+ if (invert)
+ ucontrol->value.integer.value[0] =
+ (mask - ucontrol->value.integer.value[0]);
+
+ spin_unlock_irq(&chip->lock);
+
+ 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);
+ 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_irq(&chip->lock);
+
+ val = (chip->reg_image[reg] & ~(mask << shift)) | val;
+ change = val != chip->reg_image[reg];
+ retval = snd_at73c213_write_reg(chip, reg, val);
+
+ spin_unlock_irq(&chip->lock);
+
+ if (retval)
+ return retval;
+
+ return change;
+}
+
+static int snd_at73c213_stereo_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+
+ if (mask == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = 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);
+ 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_irq(&chip->lock);
+
+ ucontrol->value.integer.value[0] =
+ (chip->reg_image[left_reg] >> shift_left) & mask;
+ ucontrol->value.integer.value[1] =
+ (chip->reg_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_irq(&chip->lock);
+
+ 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);
+ 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_irq(&chip->lock);
+
+ val1 = (chip->reg_image[left_reg] & ~(mask << shift_left)) | val1;
+ val2 = (chip->reg_image[right_reg] & ~(mask << shift_right)) | val2;
+ change = val1 != chip->reg_image[left_reg]
+ || val2 != chip->reg_image[right_reg];
+ retval = snd_at73c213_write_reg(chip, left_reg, val1);
+ if (retval) {
+ spin_unlock_irq(&chip->lock);
+ goto out;
+ }
+ retval = snd_at73c213_write_reg(chip, right_reg, val2);
+ if (retval) {
+ spin_unlock_irq(&chip->lock);
+ goto out;
+ }
+
+ spin_unlock_irq(&chip->lock);
+
+ 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);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0xff;
+
+ spin_lock_irq(&chip->lock);
+
+ ucontrol->value.integer.value[0] = (chip->reg_image[reg] >> shift) & 0x01;
+
+ if (invert)
+ ucontrol->value.integer.value[0] =
+ (0x01 - ucontrol->value.integer.value[0]);
+
+ spin_unlock_irq(&chip->lock);
+
+ 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);
+ 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_irq(&chip->lock);
+
+ val |= (chip->reg_image[reg] & ~(mask << shift));
+ change = val != chip->reg_image[reg];
+
+ retval = snd_at73c213_write_reg(chip, reg, val);
+
+ spin_unlock_irq(&chip->lock);
+
+ if (retval)
+ return retval;
+
+ return change;
+}
+
+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;
+ /* When inverted will give values 0x10001 => 0. */
+ 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;
+ /* When inverted will give values 0x10001 => 0. */
+ uinfo->value.integer.min = 14;
+ uinfo->value.integer.max = 31;
+
+ return 0;
+}
+
+#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++) {
+ errval = snd_ctl_add(card,
+ snd_ctl_new1(&snd_at73c213_controls[idx],
+ chip));
+ if (errval < 0)
+ goto cleanup;
+ }
+
+ return 0;
+
+cleanup:
+ for (idx = 1; idx < ARRAY_SIZE(snd_at73c213_controls) + 1; idx++) {
+ struct snd_kcontrol *kctl;
+ kctl = snd_ctl_find_numid(card, idx);
+ if (kctl)
+ snd_ctl_remove(card, kctl);
+ }
+ return errval;
+}
+
+/*
+ * Device functions
+ */
+static int snd_at73c213_ssc_init(struct snd_at73c213 *chip)
+{
+ /*
+ * Continuous clock output.
+ * Starts on falling TF.
+ * Delay 1 cycle (1 bit).
+ * Periode is 16 bit (16 - 1).
+ */
+ ssc_writel(chip->ssc->regs, TCMR,
+ SSC_BF(TCMR_CKO, 1)
+ | SSC_BF(TCMR_START, 4)
+ | SSC_BF(TCMR_STTDLY, 1)
+ | SSC_BF(TCMR_PERIOD, 16 - 1));
+ /*
+ * Data length is 16 bit (16 - 1).
+ * Transmit MSB first.
+ * Transmit 2 words each transfer.
+ * Frame sync length is 16 bit (16 - 1).
+ * Frame starts on negative pulse.
+ */
+ ssc_writel(chip->ssc->regs, TFMR,
+ SSC_BF(TFMR_DATLEN, 16 - 1)
+ | SSC_BIT(TFMR_MSBF)
+ | SSC_BF(TFMR_DATNB, 1)
+ | SSC_BF(TFMR_FSLEN, 16 - 1)
+ | SSC_BF(TFMR_FSOS, 1));
+
+ return 0;
+}
+
+static int snd_at73c213_chip_init(struct snd_at73c213 *chip)
+{
+ int retval;
+ unsigned char dac_ctrl = 0;
+
+ retval = snd_at73c213_set_bitrate(chip);
+ if (retval)
+ goto out;
+
+ /* Enable DAC master clock. */
+ clk_enable(chip->board->dac_clk);
+
+ /* Initialize at73c213 on SPI bus. */
+ retval = snd_at73c213_write_reg(chip, DAC_RST, 0x04);
+ if (retval)
+ goto out_clk;
+ msleep(1);
+ retval = snd_at73c213_write_reg(chip, DAC_RST, 0x03);
+ if (retval)
+ goto out_clk;
+
+ /* Precharge everything. */
+ retval = snd_at73c213_write_reg(chip, DAC_PRECH, 0xff);
+ if (retval)
+ goto out_clk;
+ retval = snd_at73c213_write_reg(chip, PA_CTRL, (1<<PA_CTRL_APAPRECH));
+ if (retval)
+ goto out_clk;
+ retval = snd_at73c213_write_reg(chip, DAC_CTRL,
+ (1<<DAC_CTRL_ONLNOL) | (1<<DAC_CTRL_ONLNOR));
+ if (retval)
+ goto out_clk;
+
+ msleep(50);
+
+ /* Stop precharging PA. */
+ retval = snd_at73c213_write_reg(chip, PA_CTRL,
+ (1<<PA_CTRL_APALP) | 0x0f);
+ if (retval)
+ goto out_clk;
+
+ msleep(450);
+
+ /* Stop precharging DAC, turn on master power. */
+ retval = snd_at73c213_write_reg(chip, DAC_PRECH, (1<<DAC_PRECH_ONMSTR));
+ if (retval)
+ goto out_clk;
+
+ msleep(1);
+
+ /* Turn on DAC. */
+ dac_ctrl = (1<<DAC_CTRL_ONDACL) | (1<<DAC_CTRL_ONDACR)
+ | (1<<DAC_CTRL_ONLNOL) | (1<<DAC_CTRL_ONLNOR);
+
+ retval = snd_at73c213_write_reg(chip, DAC_CTRL, dac_ctrl);
+ if (retval)
+ goto out_clk;
+
+ /* Mute sound. */
+ retval = snd_at73c213_write_reg(chip, DAC_LMPG, 0x3f);
+ if (retval)
+ goto out_clk;
+ retval = snd_at73c213_write_reg(chip, DAC_RMPG, 0x3f);
+ if (retval)
+ goto out_clk;
+ retval = snd_at73c213_write_reg(chip, DAC_LLOG, 0x3f);
+ if (retval)
+ goto out_clk;
+ retval = snd_at73c213_write_reg(chip, DAC_RLOG, 0x3f);
+ if (retval)
+ goto out_clk;
+ retval = snd_at73c213_write_reg(chip, DAC_LLIG, 0x11);
+ if (retval)
+ goto out_clk;
+ retval = snd_at73c213_write_reg(chip, DAC_RLIG, 0x11);
+ if (retval)
+ goto out_clk;
+ retval = snd_at73c213_write_reg(chip, DAC_AUXG, 0x11);
+ if (retval)
+ goto out_clk;
+
+ /* Enable I2S device, i.e. clock output. */
+ ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN));
+
+ goto out;
+
+out_clk:
+ clk_disable(chip->board->dac_clk);
+out:
+ return retval;
+}
+
+static int snd_at73c213_dev_free(struct snd_device *device)
+{
+ struct snd_at73c213 *chip = device->device_data;
+
+ ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
+ if (chip->irq >= 0) {
+ free_irq(chip->irq, chip);
+ chip->irq = -1;
+ }
+
+ return 0;
+}
+
+static int __devinit snd_at73c213_dev_init(struct snd_card *card,
+ struct spi_device *spi)
+{
+ static struct snd_device_ops ops = {
+ .dev_free = snd_at73c213_dev_free,
+ };
+ struct snd_at73c213 *chip = get_chip(card);
+ int irq, retval;
+
+ irq = chip->ssc->irq;
+ if (irq < 0)
+ return irq;
+
+ spin_lock_init(&chip->lock);
+ chip->card = card;
+ chip->irq = -1;
+
+ retval = request_irq(irq, snd_at73c213_interrupt, 0, "at73c213", chip);
+ if (retval) {
+ dev_dbg(&chip->spi->dev, "unable to request irq %d\n", irq);
+ goto out;
+ }
+ chip->irq = irq;
+
+ memcpy(&chip->reg_image, &snd_at73c213_original_image,
+ sizeof(snd_at73c213_original_image));
+
+ retval = snd_at73c213_ssc_init(chip);
+ if (retval)
+ goto out_irq;
+
+ retval = snd_at73c213_chip_init(chip);
+ if (retval)
+ goto out_irq;
+
+ retval = snd_at73c213_pcm_new(chip, 0);
+ if (retval)
+ goto out_irq;
+
+ retval = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (retval)
+ goto out_irq;
+
+ retval = snd_at73c213_mixer(chip);
+ if (retval)
+ goto out_snd_dev;
+
+ snd_card_set_dev(card, &spi->dev);
+
+ goto out;
+
+out_snd_dev:
+ snd_device_free(card, chip);
+out_irq:
+ free_irq(chip->irq, chip);
+ chip->irq = -1;
+out:
+ return retval;
+}
+
+static int snd_at73c213_probe(struct spi_device *spi)
+{
+ struct snd_card *card;
+ struct snd_at73c213 *chip;
+ struct at73c213_board_info *board;
+ int retval;
+ char id[16];
+
+ board = spi->dev.platform_data;
+ if (!board) {
+ dev_dbg(&spi->dev, "no platform_data\n");
+ return -ENXIO;
+ }
+
+ if (!board->dac_clk) {
+ dev_dbg(&spi->dev, "no DAC clk\n");
+ return -ENXIO;
+ }
+
+ if (IS_ERR(board->dac_clk)) {
+ dev_dbg(&spi->dev, "no DAC clk\n");
+ return PTR_ERR(board->dac_clk);
+ }
+
+ retval = -ENOMEM;
+
+ /* Allocate "card" using some unused identifiers. */
+ snprintf(id, sizeof id, "at73c213_%d", board->ssc_id);
+ card = snd_card_new(-1, id, THIS_MODULE, sizeof(struct snd_at73c213));
+ if (!card)
+ goto out;
+
+ chip = card->private_data;
+ chip->spi = spi;
+ chip->board = board;
+
+ chip->ssc = ssc_request(board->ssc_id);
+ if (IS_ERR(chip->ssc)) {
+ dev_dbg(&spi->dev, "could not get ssc%d device\n",
+ board->ssc_id);
+ retval = PTR_ERR(chip->ssc);
+ goto out_card;
+ }
+
+ retval = snd_at73c213_dev_init(card, spi);
+ if (retval)
+ goto out_ssc;
+
+ strcpy(card->driver, "at73c213");
+ strcpy(card->shortname, board->shortname);
+ sprintf(card->longname, "%s on irq %d", card->shortname, chip->irq);
+
+ retval = snd_card_register(card);
+ if (retval)
+ goto out_ssc;
+
+ dev_set_drvdata(&spi->dev, card);
+
+ goto out;
+
+out_ssc:
+ ssc_free(chip->ssc);
+out_card:
+ snd_card_free(card);
+out:
+ return retval;
+}
+
+static int __devexit snd_at73c213_remove(struct spi_device *spi)
+{
+ struct snd_card *card = dev_get_drvdata(&spi->dev);
+ struct snd_at73c213 *chip = card->private_data;
+ int retval;
+
+ /* Stop playback. */
+ ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
+
+ /* Mute sound. */
+ retval = snd_at73c213_write_reg(chip, DAC_LMPG, 0x3f);
+ if (retval)
+ goto out;
+ retval = snd_at73c213_write_reg(chip, DAC_RMPG, 0x3f);
+ if (retval)
+ goto out;
+ retval = snd_at73c213_write_reg(chip, DAC_LLOG, 0x3f);
+ if (retval)
+ goto out;
+ retval = snd_at73c213_write_reg(chip, DAC_RLOG, 0x3f);
+ if (retval)
+ goto out;
+ retval = snd_at73c213_write_reg(chip, DAC_LLIG, 0x11);
+ if (retval)
+ goto out;
+ retval = snd_at73c213_write_reg(chip, DAC_RLIG, 0x11);
+ if (retval)
+ goto out;
+ retval = snd_at73c213_write_reg(chip, DAC_AUXG, 0x11);
+ if (retval)
+ goto out;
+
+ /* Turn off PA. */
+ retval = snd_at73c213_write_reg(chip, PA_CTRL, (chip->reg_image[PA_CTRL]|0x0f));
+ if (retval)
+ goto out;
+ msleep(10);
+ retval = snd_at73c213_write_reg(chip, PA_CTRL, (1<<PA_CTRL_APALP)|0x0f);
+ if (retval)
+ goto out;
+
+ /* Turn off external DAC. */
+ retval = snd_at73c213_write_reg(chip, DAC_CTRL, 0x0c);
+ if (retval)
+ goto out;
+ msleep(2);
+ retval = snd_at73c213_write_reg(chip, DAC_CTRL, 0x00);
+ if (retval)
+ goto out;
+
+ /* Turn off master power. */
+ retval = snd_at73c213_write_reg(chip, DAC_PRECH, 0x00);
+ if (retval)
+ goto out;
+
+out:
+ /* Stop DAC master clock. */
+ clk_disable(chip->board->dac_clk);
+
+ ssc_free(chip->ssc);
+ snd_card_free(card);
+ dev_set_drvdata(&spi->dev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int snd_at73c213_suspend(struct spi_device *spi, pm_message_t msg)
+{
+ struct snd_card *card = dev_get_drvdata(&spi->dev);
+ struct snd_at73c213 *chip = card->private_data;
+
+ ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
+ clk_disable(chip->board->dac_clk);
+
+ return 0;
+}
+
+static int snd_at73c213_resume(struct spi_device *spi)
+{
+ struct snd_card *card = dev_get_drvdata(&spi->dev);
+ struct snd_at73c213 *chip = card->private_data;
+
+ clk_enable(chip->board->dac_clk);
+ ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN));
+
+ return 0;
+}
+#else
+#define snd_at73c213_suspend NULL
+#define snd_at73c213_resume NULL
+#endif
+
+static struct spi_driver at73c213_driver = {
+ .driver = {
+ .name = "at73c213",
+ },
+ .probe = snd_at73c213_probe,
+ .suspend = snd_at73c213_suspend,
+ .resume = snd_at73c213_resume,
+ .remove = __devexit_p(snd_at73c213_remove),
+};
+
+static int __init at73c213_init(void)
+{
+ return spi_register_driver(&at73c213_driver);
+}
+module_init(at73c213_init);
+
+static void __exit at73c213_exit(void)
+{
+ spi_unregister_driver(&at73c213_driver);
+}
+module_exit(at73c213_exit);
+
+MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt(a)atmel.com>");
+MODULE_DESCRIPTION("Sound driver for AT73C213 with Atmel SSC");
+MODULE_LICENSE("GPL");
diff --git a/sound/spi/at73c213.h b/sound/spi/at73c213.h
new file mode 100644
index 0000000..fd8b372
--- /dev/null
+++ b/sound/spi/at73c213.h
@@ -0,0 +1,119 @@
+/*
+ * Driver for the AT73C213 16-bit stereo DAC on Atmel ATSTK1000
+ *
+ * Copyright (C) 2006 - 2007 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this
+ * distribution in the file called COPYING.
+ */
+
+#ifndef _SND_AT73C213_H
+#define _SND_AT73C213_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 /* _SND_AT73C213_H */
--
1.5.2.3
--=-AUyv9PDBqTOzFdJ/BnKL--
1
0
[PATCH 1/1] ALSA sound driver for the AT73C213 DAC using Atmel SSC driver
by Hans-Christian Egtvedt 05 Jul '07
by Hans-Christian Egtvedt 05 Jul '07
05 Jul '07
This patch adds support for the AT73C213 DAC using the misc Atmel SSC driver in
I2S mode. The driver also requires a SPI to setup the registers and control
volume.
It has been tested with an AT32AP7000 on the ATSTK1000 development board. The
driver should also work with any Atmel device with an SSC module supported by
the Atmel SSC driver (atmel-ssc).
The atmel-ssc driver is just submitted to the Linux kernel. Please see mail
thread http://lkml.org/lkml/2007/7/16/32
Signed-off-by: Hans-Christian Egtvedt <hcegtvedt(a)atmel.com>
---
include/linux/spi/at73c213.h | 25 +
sound/spi/Kconfig | 25 +
sound/spi/Makefile | 3 +
sound/spi/at73c213.c | 1121 ++++++++++++++++++++++++++++++++++++++++++
sound/spi/at73c213.h | 119 +++++
5 files changed, 1293 insertions(+), 0 deletions(-)
create mode 100644 include/linux/spi/at73c213.h
create mode 100644 sound/spi/at73c213.c
create mode 100644 sound/spi/at73c213.h
diff --git a/include/linux/spi/at73c213.h b/include/linux/spi/at73c213.h
new file mode 100644
index 0000000..0f20a70
--- /dev/null
+++ b/include/linux/spi/at73c213.h
@@ -0,0 +1,25 @@
+/*
+ * Board-specific data used to set up AT73c213 audio DAC driver.
+ */
+
+#ifndef __LINUX_SPI_AT73C213_H
+#define __LINUX_SPI_AT73C213_H
+
+/**
+ * at73c213_board_info - how the external DAC is wired to the device.
+ *
+ * @ssc_id: SSC platform_driver id the DAC shall use to stream the audio.
+ * @dac_clk: the external clock used to provide master clock to the DAC.
+ * @shortname: a short discription for the DAC, seen by userspace tools.
+ *
+ * This struct contains the configuration of the hardware connection to the
+ * external DAC. The DAC needs a master clock and a I2S audio stream. It also
+ * provides a name which is used to identify it in userspace tools.
+ */
+struct at73c213_board_info {
+ int ssc_id;
+ struct clk *dac_clk;
+ char shortname[32];
+};
+
+#endif /* __LINUX_SPI_AT73C213_H */
diff --git a/sound/spi/Kconfig b/sound/spi/Kconfig
index 60fbb2a..0d08c29 100644
--- a/sound/spi/Kconfig
+++ b/sound/spi/Kconfig
@@ -3,4 +3,29 @@
menu "SPI devices"
depends on SND != n
+config SND_AT73C213
+ tristate "Atmel AT73C213 DAC driver"
+ depends on ATMEL_SSC
+ select SND_PCM
+ help
+ Say Y here if you want to use the Atmel AT73C213 external DAC. This
+ DAC can be found on Atmel development boards.
+
+ This driver requires the Atmel SSC driver for sound sink, a
+ peripheral found on most AT91 and AVR32 microprocessors.
+
+ To compile this driver as a module, choose M here: the module will be
+ called snd-at73c213.
+
+config SND_AT73C213_TARGET_BITRATE
+ int "Target bitrate for AT73C213"
+ depends on SND_AT73C213
+ default "48000"
+ range 8000 50000
+ help
+ Sets the target bitrate for the bitrate calculator in the driver.
+ Limited by hardware to be between 8000 Hz and 50000 Hz.
+
+ Set to 48000 Hz by default.
+
endmenu
diff --git a/sound/spi/Makefile b/sound/spi/Makefile
index 30d8ec9..026fb73 100644
--- a/sound/spi/Makefile
+++ b/sound/spi/Makefile
@@ -1,2 +1,5 @@
# Makefile for SPI drivers
+snd-at73c213-objs := at73c213.o
+
+obj-$(CONFIG_SND_AT73C213) += snd-at73c213.o
diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c
new file mode 100644
index 0000000..f514f47
--- /dev/null
+++ b/sound/spi/at73c213.c
@@ -0,0 +1,1121 @@
+/*
+ * Driver for AT73C213 16-bit stereo DAC connected to Atmel SSC
+ *
+ * Copyright (C) 2006-2007 Atmel Norway
+ *
+ * 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.
+ */
+
+/*#define DEBUG*/
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/device.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/io.h>
+
+#include <sound/driver.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include <linux/atmel-ssc.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/at73c213.h>
+
+#include "at73c213.h"
+
+#define BITRATE_MIN 8000 /* Hardware limit? */
+#define BITRATE_TARGET CONFIG_SND_AT73C213_TARGET_BITRATE
+#define BITRATE_MAX 50000 /* Hardware limit. */
+
+/* Initial (hardware reset) AT73C213 register values. */
+static u8 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 */
+};
+
+struct snd_at73c213 {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct snd_pcm_substream *substream;
+ struct at73c213_board_info *board;
+ int irq;
+ int period;
+ unsigned long bitrate;
+ struct clk *bitclk;
+ struct ssc_device *ssc;
+ struct spi_device *spi;
+ u8 spi_wbuffer[2];
+ u8 spi_rbuffer[2];
+ /* Image of the SPI registers in AT73C213. */
+ u8 reg_image[18];
+ /* Protect registers against concurrent access. */
+ spinlock_t lock;
+};
+
+#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,
+ };
+ int retval;
+
+ 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);
+
+ retval = spi_sync(chip->spi, &msg);
+
+ if (!retval)
+ chip->reg_image[reg] = val;
+
+ return retval;
+}
+
+static struct snd_pcm_hardware 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, /* Replaced by chip->bitrate later. */
+ .rate_max = 50000, /* Replaced by chip->bitrate later. */
+ .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(struct snd_at73c213 *chip)
+{
+ unsigned long ssc_rate = clk_get_rate(chip->ssc->clk);
+ unsigned long dac_rate_new, ssc_div, status;
+ unsigned long ssc_div_max, ssc_div_min;
+ int max_tries;
+
+ /*
+ * We connect two clocks here, picking divisors so the I2S clocks
+ * out data at the same rate the DAC clocks it in ... and as close
+ * as practical to the desired target rate.
+ *
+ * The DAC master clock (MCLK) is programmable, and is either 256
+ * or (not here) 384 times the I2S output clock (BCLK).
+ */
+
+ /* SSC clock / (bitrate * stereo * 16-bit). */
+ ssc_div = ssc_rate / (BITRATE_TARGET * 2 * 16);
+ ssc_div_min = ssc_rate / (BITRATE_MAX * 2 * 16);
+ ssc_div_max = ssc_rate / (BITRATE_MIN * 2 * 16);
+ max_tries = (ssc_div_max - ssc_div_min) / 2;
+
+ if (max_tries < 1)
+ max_tries = 1;
+
+ /* ssc_div must be a power of 2. */
+ ssc_div = (ssc_div + 1) & ~1UL;
+
+ if ((ssc_rate / (ssc_div * 2 * 16)) < BITRATE_MIN) {
+ ssc_div -= 2;
+ if ((ssc_rate / (ssc_div * 2 * 16)) > BITRATE_MAX)
+ return -ENXIO;
+ }
+
+ /* Search for a possible bitrate. */
+ do {
+ /* SSC clock / (ssc divider * 16-bit * stereo). */
+ if ((ssc_rate / (ssc_div * 2 * 16)) < BITRATE_MIN)
+ return -ENXIO;
+
+ /* 256 / (2 * 16) = 8 */
+ dac_rate_new = 8 * (ssc_rate / ssc_div);
+
+ status = clk_round_rate(chip->board->dac_clk, dac_rate_new);
+ if (status < 0)
+ return status;
+
+ /* Ignore difference smaller than 256 Hz. */
+ if ((status/256) == (dac_rate_new/256))
+ goto set_rate;
+
+ ssc_div += 2;
+ } while (--max_tries);
+
+ /* Not able to find a valid bitrate. */
+ return -ENXIO;
+
+set_rate:
+ status = clk_set_rate(chip->board->dac_clk, status);
+ if (status < 0)
+ return status;
+
+ /* Set divider in SSC device. */
+ ssc_writel(chip->ssc->regs, CMR, ssc_div/2);
+
+ /* SSC clock / (ssc divider * 16-bit * stereo). */
+ chip->bitrate = ssc_rate / (ssc_div * 16 * 2);
+
+ dev_info(&chip->spi->dev,
+ "at73c213: supported bitrate is %lu (%lu divider)\n",
+ chip->bitrate, ssc_div);
+
+ return 0;
+}
+
+static int snd_at73c213_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ snd_at73c213_playback_hw.rate_min = chip->bitrate;
+ snd_at73c213_playback_hw.rate_max = chip->bitrate;
+ runtime->hw = snd_at73c213_playback_hw;
+ chip->substream = substream;
+
+ return 0;
+}
+
+static int snd_at73c213_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
+ chip->substream = NULL;
+ return 0;
+}
+
+static int snd_at73c213_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int snd_at73c213_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_at73c213_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int block_size;
+
+ block_size = frames_to_bytes(runtime, runtime->period_size);
+
+ chip->period = 0;
+
+ ssc_writel(chip->ssc->regs, PDC_TPR,
+ (long)runtime->dma_addr);
+ ssc_writel(chip->ssc->regs, PDC_TCR, runtime->period_size * 2);
+ ssc_writel(chip->ssc->regs, PDC_TNPR,
+ (long)runtime->dma_addr + block_size);
+ ssc_writel(chip->ssc->regs, PDC_TNCR, runtime->period_size * 2);
+
+ return 0;
+}
+
+static int snd_at73c213_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
+ int retval = 0;
+
+ spin_lock(&chip->lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ ssc_writel(chip->ssc->regs, IER, SSC_BIT(IER_ENDTX));
+ ssc_writel(chip->ssc->regs, PDC_PTCR, SSC_BIT(PDC_PTCR_TXTEN));
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ ssc_writel(chip->ssc->regs, PDC_PTCR, SSC_BIT(PDC_PTCR_TXTDIS));
+ ssc_writel(chip->ssc->regs, IDR, SSC_BIT(IDR_ENDTX));
+ break;
+ default:
+ dev_dbg(&chip->spi->dev, "spurious command %x\n", cmd);
+ retval = -EINVAL;
+ break;
+ }
+
+ spin_unlock(&chip->lock);
+
+ return retval;
+}
+
+static snd_pcm_uframes_t
+snd_at73c213_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_uframes_t pos;
+ unsigned long bytes;
+
+ bytes = ssc_readl(chip->ssc->regs, PDC_TPR)
+ - (unsigned long)runtime->dma_addr;
+
+ pos = bytes_to_frames(runtime, bytes);
+ if (pos >= runtime->buffer_size)
+ pos -= runtime->buffer_size;
+
+ return pos;
+}
+
+static struct snd_pcm_ops 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,
+};
+
+static void snd_at73c213_pcm_free(struct snd_pcm *pcm)
+{
+ struct snd_at73c213 *chip = snd_pcm_chip(pcm);
+ if (chip->pcm) {
+ snd_pcm_lib_preallocate_free_for_all(chip->pcm);
+ chip->pcm = NULL;
+ }
+}
+
+static int __devinit snd_at73c213_pcm_new(struct snd_at73c213 *chip, int device)
+{
+ struct snd_pcm *pcm;
+ int retval;
+
+ retval = snd_pcm_new(chip->card, chip->card->shortname,
+ device, 1, 0, &pcm);
+ if (retval < 0)
+ goto out;
+
+ 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);
+
+ retval = snd_pcm_lib_preallocate_pages_for_all(chip->pcm,
+ SNDRV_DMA_TYPE_DEV, &chip->ssc->pdev->dev,
+ 64 * 1024, 64 * 1024);
+out:
+ return retval;
+}
+
+static irqreturn_t snd_at73c213_interrupt(int irq, void *dev_id)
+{
+ struct snd_at73c213 *chip = dev_id;
+ struct snd_pcm_runtime *runtime = chip->substream->runtime;
+ u32 status;
+ int offset;
+ int block_size;
+ int next_period;
+ int retval = IRQ_NONE;
+
+ spin_lock(&chip->lock);
+
+ block_size = frames_to_bytes(runtime, runtime->period_size);
+ status = ssc_readl(chip->ssc->regs, IMR);
+
+ if (status & SSC_BIT(IMR_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;
+
+ ssc_writel(chip->ssc->regs, PDC_TNPR,
+ (long)runtime->dma_addr + offset);
+ ssc_writel(chip->ssc->regs, PDC_TNCR, runtime->period_size * 2);
+ retval = IRQ_HANDLED;
+ }
+
+ ssc_readl(chip->ssc->regs, IMR);
+ spin_unlock(&chip->lock);
+
+ if (status & SSC_BIT(IMR_ENDTX))
+ snd_pcm_period_elapsed(chip->substream);
+
+ return retval;
+}
+
+/*
+ * Mixer functions.
+ */
+static int snd_at73c213_mono_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol);
+ 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_irq(&chip->lock);
+
+ ucontrol->value.integer.value[0] = (chip->reg_image[reg] >> shift) & mask;
+
+ if (invert)
+ ucontrol->value.integer.value[0] =
+ (mask - ucontrol->value.integer.value[0]);
+
+ spin_unlock_irq(&chip->lock);
+
+ 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);
+ 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_irq(&chip->lock);
+
+ val = (chip->reg_image[reg] & ~(mask << shift)) | val;
+ change = val != chip->reg_image[reg];
+ retval = snd_at73c213_write_reg(chip, reg, val);
+
+ spin_unlock_irq(&chip->lock);
+
+ if (retval)
+ return retval;
+
+ return change;
+}
+
+static int snd_at73c213_stereo_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+
+ if (mask == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = 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);
+ 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_irq(&chip->lock);
+
+ ucontrol->value.integer.value[0] =
+ (chip->reg_image[left_reg] >> shift_left) & mask;
+ ucontrol->value.integer.value[1] =
+ (chip->reg_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_irq(&chip->lock);
+
+ 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);
+ 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_irq(&chip->lock);
+
+ val1 = (chip->reg_image[left_reg] & ~(mask << shift_left)) | val1;
+ val2 = (chip->reg_image[right_reg] & ~(mask << shift_right)) | val2;
+ change = val1 != chip->reg_image[left_reg]
+ || val2 != chip->reg_image[right_reg];
+ retval = snd_at73c213_write_reg(chip, left_reg, val1);
+ if (retval) {
+ spin_unlock_irq(&chip->lock);
+ goto out;
+ }
+ retval = snd_at73c213_write_reg(chip, right_reg, val2);
+ if (retval) {
+ spin_unlock_irq(&chip->lock);
+ goto out;
+ }
+
+ spin_unlock_irq(&chip->lock);
+
+ 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);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0xff;
+
+ spin_lock_irq(&chip->lock);
+
+ ucontrol->value.integer.value[0] = (chip->reg_image[reg] >> shift) & 0x01;
+
+ if (invert)
+ ucontrol->value.integer.value[0] =
+ (0x01 - ucontrol->value.integer.value[0]);
+
+ spin_unlock_irq(&chip->lock);
+
+ 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);
+ 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_irq(&chip->lock);
+
+ val |= (chip->reg_image[reg] & ~(mask << shift));
+ change = val != chip->reg_image[reg];
+
+ retval = snd_at73c213_write_reg(chip, reg, val);
+
+ spin_unlock_irq(&chip->lock);
+
+ if (retval)
+ return retval;
+
+ return change;
+}
+
+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;
+ /* When inverted will give values 0x10001 => 0. */
+ 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;
+ /* When inverted will give values 0x10001 => 0. */
+ uinfo->value.integer.min = 14;
+ uinfo->value.integer.max = 31;
+
+ return 0;
+}
+
+#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++) {
+ errval = snd_ctl_add(card,
+ snd_ctl_new1(&snd_at73c213_controls[idx],
+ chip));
+ if (errval < 0)
+ goto cleanup;
+ }
+
+ return 0;
+
+cleanup:
+ for (idx = 1; idx < ARRAY_SIZE(snd_at73c213_controls) + 1; idx++) {
+ struct snd_kcontrol *kctl;
+ kctl = snd_ctl_find_numid(card, idx);
+ if (kctl)
+ snd_ctl_remove(card, kctl);
+ }
+ return errval;
+}
+
+/*
+ * Device functions
+ */
+static int snd_at73c213_ssc_init(struct snd_at73c213 *chip)
+{
+ /*
+ * Continuous clock output.
+ * Starts on falling TF.
+ * Delay 1 cycle (1 bit).
+ * Periode is 16 bit (16 - 1).
+ */
+ ssc_writel(chip->ssc->regs, TCMR,
+ SSC_BF(TCMR_CKO, 1)
+ | SSC_BF(TCMR_START, 4)
+ | SSC_BF(TCMR_STTDLY, 1)
+ | SSC_BF(TCMR_PERIOD, 16 - 1));
+ /*
+ * Data length is 16 bit (16 - 1).
+ * Transmit MSB first.
+ * Transmit 2 words each transfer.
+ * Frame sync length is 16 bit (16 - 1).
+ * Frame starts on negative pulse.
+ */
+ ssc_writel(chip->ssc->regs, TFMR,
+ SSC_BF(TFMR_DATLEN, 16 - 1)
+ | SSC_BIT(TFMR_MSBF)
+ | SSC_BF(TFMR_DATNB, 1)
+ | SSC_BF(TFMR_FSLEN, 16 - 1)
+ | SSC_BF(TFMR_FSOS, 1));
+
+ return 0;
+}
+
+static int snd_at73c213_chip_init(struct snd_at73c213 *chip)
+{
+ int retval;
+ unsigned char dac_ctrl = 0;
+
+ retval = snd_at73c213_set_bitrate(chip);
+ if (retval)
+ goto out;
+
+ /* Enable DAC master clock. */
+ clk_enable(chip->board->dac_clk);
+
+ /* Initialize at73c213 on SPI bus. */
+ retval = snd_at73c213_write_reg(chip, DAC_RST, 0x04);
+ if (retval)
+ goto out_clk;
+ msleep(1);
+ retval = snd_at73c213_write_reg(chip, DAC_RST, 0x03);
+ if (retval)
+ goto out_clk;
+
+ /* Precharge everything. */
+ retval = snd_at73c213_write_reg(chip, DAC_PRECH, 0xff);
+ if (retval)
+ goto out_clk;
+ retval = snd_at73c213_write_reg(chip, PA_CTRL, (1<<PA_CTRL_APAPRECH));
+ if (retval)
+ goto out_clk;
+ retval = snd_at73c213_write_reg(chip, DAC_CTRL,
+ (1<<DAC_CTRL_ONLNOL) | (1<<DAC_CTRL_ONLNOR));
+ if (retval)
+ goto out_clk;
+
+ msleep(50);
+
+ /* Stop precharging PA. */
+ retval = snd_at73c213_write_reg(chip, PA_CTRL,
+ (1<<PA_CTRL_APALP) | 0x0f);
+ if (retval)
+ goto out_clk;
+
+ msleep(450);
+
+ /* Stop precharging DAC, turn on master power. */
+ retval = snd_at73c213_write_reg(chip, DAC_PRECH, (1<<DAC_PRECH_ONMSTR));
+ if (retval)
+ goto out_clk;
+
+ msleep(1);
+
+ /* Turn on DAC. */
+ dac_ctrl = (1<<DAC_CTRL_ONDACL) | (1<<DAC_CTRL_ONDACR)
+ | (1<<DAC_CTRL_ONLNOL) | (1<<DAC_CTRL_ONLNOR);
+
+ retval = snd_at73c213_write_reg(chip, DAC_CTRL, dac_ctrl);
+ if (retval)
+ goto out_clk;
+
+ /* Mute sound. */
+ retval = snd_at73c213_write_reg(chip, DAC_LMPG, 0x3f);
+ if (retval)
+ goto out_clk;
+ retval = snd_at73c213_write_reg(chip, DAC_RMPG, 0x3f);
+ if (retval)
+ goto out_clk;
+ retval = snd_at73c213_write_reg(chip, DAC_LLOG, 0x3f);
+ if (retval)
+ goto out_clk;
+ retval = snd_at73c213_write_reg(chip, DAC_RLOG, 0x3f);
+ if (retval)
+ goto out_clk;
+ retval = snd_at73c213_write_reg(chip, DAC_LLIG, 0x11);
+ if (retval)
+ goto out_clk;
+ retval = snd_at73c213_write_reg(chip, DAC_RLIG, 0x11);
+ if (retval)
+ goto out_clk;
+ retval = snd_at73c213_write_reg(chip, DAC_AUXG, 0x11);
+ if (retval)
+ goto out_clk;
+
+ /* Enable I2S device, i.e. clock output. */
+ ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN));
+
+ goto out;
+
+out_clk:
+ clk_disable(chip->board->dac_clk);
+out:
+ return retval;
+}
+
+static int snd_at73c213_dev_free(struct snd_device *device)
+{
+ struct snd_at73c213 *chip = device->device_data;
+
+ ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
+ if (chip->irq >= 0) {
+ free_irq(chip->irq, chip);
+ chip->irq = -1;
+ }
+
+ return 0;
+}
+
+static int __devinit snd_at73c213_dev_init(struct snd_card *card,
+ struct spi_device *spi)
+{
+ static struct snd_device_ops ops = {
+ .dev_free = snd_at73c213_dev_free,
+ };
+ struct snd_at73c213 *chip = get_chip(card);
+ int irq, retval;
+
+ irq = chip->ssc->irq;
+ if (irq < 0)
+ return irq;
+
+ spin_lock_init(&chip->lock);
+ chip->card = card;
+ chip->irq = -1;
+
+ retval = request_irq(irq, snd_at73c213_interrupt, 0, "at73c213", chip);
+ if (retval) {
+ dev_dbg(&chip->spi->dev, "unable to request irq %d\n", irq);
+ goto out;
+ }
+ chip->irq = irq;
+
+ memcpy(&chip->reg_image, &snd_at73c213_original_image,
+ sizeof(snd_at73c213_original_image));
+
+ retval = snd_at73c213_ssc_init(chip);
+ if (retval)
+ goto out_irq;
+
+ retval = snd_at73c213_chip_init(chip);
+ if (retval)
+ goto out_irq;
+
+ retval = snd_at73c213_pcm_new(chip, 0);
+ if (retval)
+ goto out_irq;
+
+ retval = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (retval)
+ goto out_irq;
+
+ retval = snd_at73c213_mixer(chip);
+ if (retval)
+ goto out_snd_dev;
+
+ snd_card_set_dev(card, &spi->dev);
+
+ goto out;
+
+out_snd_dev:
+ snd_device_free(card, chip);
+out_irq:
+ free_irq(chip->irq, chip);
+ chip->irq = -1;
+out:
+ return retval;
+}
+
+static int snd_at73c213_probe(struct spi_device *spi)
+{
+ struct snd_card *card;
+ struct snd_at73c213 *chip;
+ struct at73c213_board_info *board;
+ int retval;
+ char id[16];
+
+ board = spi->dev.platform_data;
+ if (!board) {
+ dev_dbg(&spi->dev, "no platform_data\n");
+ return -ENXIO;
+ }
+
+ if (!board->dac_clk) {
+ dev_dbg(&spi->dev, "no DAC clk\n");
+ return -ENXIO;
+ }
+
+ if (IS_ERR(board->dac_clk)) {
+ dev_dbg(&spi->dev, "no DAC clk\n");
+ return PTR_ERR(board->dac_clk);
+ }
+
+ retval = -ENOMEM;
+
+ /* Allocate "card" using some unused identifiers. */
+ snprintf(id, sizeof id, "at73c213_%d", board->ssc_id);
+ card = snd_card_new(-1, id, THIS_MODULE, sizeof(struct snd_at73c213));
+ if (!card)
+ goto out;
+
+ chip = card->private_data;
+ chip->spi = spi;
+ chip->board = board;
+
+ chip->ssc = ssc_request(board->ssc_id);
+ if (IS_ERR(chip->ssc)) {
+ dev_dbg(&spi->dev, "could not get ssc%d device\n",
+ board->ssc_id);
+ retval = PTR_ERR(chip->ssc);
+ goto out_card;
+ }
+
+ retval = snd_at73c213_dev_init(card, spi);
+ if (retval)
+ goto out_ssc;
+
+ strcpy(card->driver, "at73c213");
+ strcpy(card->shortname, board->shortname);
+ sprintf(card->longname, "%s on irq %d", card->shortname, chip->irq);
+
+ retval = snd_card_register(card);
+ if (retval)
+ goto out_ssc;
+
+ dev_set_drvdata(&spi->dev, card);
+
+ goto out;
+
+out_ssc:
+ ssc_free(chip->ssc);
+out_card:
+ snd_card_free(card);
+out:
+ return retval;
+}
+
+static int __devexit snd_at73c213_remove(struct spi_device *spi)
+{
+ struct snd_card *card = dev_get_drvdata(&spi->dev);
+ struct snd_at73c213 *chip = card->private_data;
+ int retval;
+
+ /* Stop playback. */
+ ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
+
+ /* Mute sound. */
+ retval = snd_at73c213_write_reg(chip, DAC_LMPG, 0x3f);
+ if (retval)
+ goto out;
+ retval = snd_at73c213_write_reg(chip, DAC_RMPG, 0x3f);
+ if (retval)
+ goto out;
+ retval = snd_at73c213_write_reg(chip, DAC_LLOG, 0x3f);
+ if (retval)
+ goto out;
+ retval = snd_at73c213_write_reg(chip, DAC_RLOG, 0x3f);
+ if (retval)
+ goto out;
+ retval = snd_at73c213_write_reg(chip, DAC_LLIG, 0x11);
+ if (retval)
+ goto out;
+ retval = snd_at73c213_write_reg(chip, DAC_RLIG, 0x11);
+ if (retval)
+ goto out;
+ retval = snd_at73c213_write_reg(chip, DAC_AUXG, 0x11);
+ if (retval)
+ goto out;
+
+ /* Turn off PA. */
+ retval = snd_at73c213_write_reg(chip, PA_CTRL, (chip->reg_image[PA_CTRL]|0x0f));
+ if (retval)
+ goto out;
+ msleep(10);
+ retval = snd_at73c213_write_reg(chip, PA_CTRL, (1<<PA_CTRL_APALP)|0x0f);
+ if (retval)
+ goto out;
+
+ /* Turn off external DAC. */
+ retval = snd_at73c213_write_reg(chip, DAC_CTRL, 0x0c);
+ if (retval)
+ goto out;
+ msleep(2);
+ retval = snd_at73c213_write_reg(chip, DAC_CTRL, 0x00);
+ if (retval)
+ goto out;
+
+ /* Turn off master power. */
+ retval = snd_at73c213_write_reg(chip, DAC_PRECH, 0x00);
+ if (retval)
+ goto out;
+
+out:
+ /* Stop DAC master clock. */
+ clk_disable(chip->board->dac_clk);
+
+ ssc_free(chip->ssc);
+ snd_card_free(card);
+ dev_set_drvdata(&spi->dev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int snd_at73c213_suspend(struct spi_device *spi, pm_message_t msg)
+{
+ struct snd_card *card = dev_get_drvdata(&spi->dev);
+ struct snd_at73c213 *chip = card->private_data;
+
+ ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
+ clk_disable(chip->board->dac_clk);
+
+ return 0;
+}
+
+static int snd_at73c213_resume(struct spi_device *spi)
+{
+ struct snd_card *card = dev_get_drvdata(&spi->dev);
+ struct snd_at73c213 *chip = card->private_data;
+
+ clk_enable(chip->board->dac_clk);
+ ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN));
+
+ return 0;
+}
+#else
+#define snd_at73c213_suspend NULL
+#define snd_at73c213_resume NULL
+#endif
+
+static struct spi_driver at73c213_driver = {
+ .driver = {
+ .name = "at73c213",
+ },
+ .probe = snd_at73c213_probe,
+ .suspend = snd_at73c213_suspend,
+ .resume = snd_at73c213_resume,
+ .remove = __devexit_p(snd_at73c213_remove),
+};
+
+static int __init at73c213_init(void)
+{
+ return spi_register_driver(&at73c213_driver);
+}
+module_init(at73c213_init);
+
+static void __exit at73c213_exit(void)
+{
+ spi_unregister_driver(&at73c213_driver);
+}
+module_exit(at73c213_exit);
+
+MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt(a)atmel.com>");
+MODULE_DESCRIPTION("Sound driver for AT73C213 with Atmel SSC");
+MODULE_LICENSE("GPL");
diff --git a/sound/spi/at73c213.h b/sound/spi/at73c213.h
new file mode 100644
index 0000000..fd8b372
--- /dev/null
+++ b/sound/spi/at73c213.h
@@ -0,0 +1,119 @@
+/*
+ * Driver for the AT73C213 16-bit stereo DAC on Atmel ATSTK1000
+ *
+ * Copyright (C) 2006 - 2007 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this
+ * distribution in the file called COPYING.
+ */
+
+#ifndef _SND_AT73C213_H
+#define _SND_AT73C213_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 /* _SND_AT73C213_H */
--
1.5.2.3
--=-zhf1GaBSeEtL5hs3rxas--
1
0
Maybe an error in snd_pcm_drain - not sure what effect it might have...
snd_pcm_group_for_each_entry(s, substream) {
runtime = s->runtime;
...
}
up_read(&snd_pcm_link_rwsem);
snd_pcm_stream_lock_irq(substream);
/* pcm_native.c around line 1491: */
/* error? runtime has been changed by group_for_each
should use substream->runtime instead?
*/
/* resume pause */
if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
snd_pcm_pause(substream, 0);
--
Eliot
2
1
05 Jul '07
Record doesn't work if enabling mmap emulation and rate conversion
needed, this patch fix this bug.
diff -uNr alsa-lib-1.0.14a/src/pcm/pcm_hw.c alsa-lib-new/src/pcm/pcm_hw.c
--- alsa-lib-1.0.14a/src/pcm/pcm_hw.c 2007-06-11 16:53:13.000000000 +0800
+++ alsa-lib-new/src/pcm/pcm_hw.c 2007-07-04 12:16:31.000000000 +0800
@@ -96,6 +96,7 @@
int shadow_appl_ptr: 1,
avail_update_flag: 1,
mmap_shm: 1;
+ snd_pcm_uframes_t hw_ptr;
snd_pcm_uframes_t appl_ptr;
/* restricted parameters */
snd_pcm_format_t format;
@@ -373,7 +374,9 @@
if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
if (hw->mmap_shm) {
hw->shadow_appl_ptr = 1;
+ hw->hw_ptr = 0;
hw->appl_ptr = 0;
+ snd_pcm_set_hw_ptr(pcm, &hw->hw_ptr, -1, 0);
snd_pcm_set_appl_ptr(pcm, &hw->appl_ptr, -1, 0);
} else {
hw->shadow_appl_ptr = 0;
@@ -949,28 +952,91 @@
return size;
}
+static inline snd_pcm_uframes_t snd_pcm_hw_capture_avail(snd_pcm_t *pcm)
+{
+ snd_pcm_sframes_t avail;
+ snd_pcm_hw_t *hw = pcm->private_data;
+
+ avail = hw->mmap_status->hw_ptr - hw->mmap_control->appl_ptr;
+ if (avail < 0)
+ avail += pcm->boundary;
+ return avail;
+}
+
+static snd_pcm_sframes_t snd_pcm_hw_read_mmap(snd_pcm_t *pcm,
snd_pcm_uframes_t size)
+{
+ snd_pcm_uframes_t xfer = 0;
+ snd_pcm_sframes_t err = 0;
+ snd_pcm_hw_t *hw = pcm->private_data;
+ if (! size)
+ return 0;
+ while (xfer < size) {
+ snd_pcm_uframes_t frames = size - xfer;
+ snd_pcm_uframes_t appl_offset = hw->mmap_control->appl_ptr %
pcm->buffer_size;
+ snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset;
+ if (cont < frames)
+ frames = cont;
+ switch (pcm->access) {
+ case SND_PCM_ACCESS_MMAP_INTERLEAVED:
+ {
+ const snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm);
+ char *buf = snd_pcm_channel_area_addr(a, appl_offset);
+ err = _snd_pcm_readi(pcm, buf, frames);
+ if (err >= 0)
+ frames = err;
+ break;
+ }
+ case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
+ {
+ snd_pcm_uframes_t channels = pcm->channels;
+ unsigned int c;
+ void *bufs[channels];
+ const snd_pcm_channel_area_t *areas = snd_pcm_mmap_areas(pcm);
+ for (c = 0; c < channels; ++c) {
+ const snd_pcm_channel_area_t *a = &areas[c];
+ bufs[c] = snd_pcm_channel_area_addr(a, appl_offset);
+ }
+ err = _snd_pcm_readn(pcm->fast_op_arg, bufs, frames);
+ if (err >= 0)
+ frames = err;
+ }
+ default:
+ SNDMSG("invalid access type %d", pcm->access);
+ return -EINVAL;
+ }
+ if (err < 0)
+ break;
+ xfer += frames;
+ }
+ if (xfer > 0)
+ return xfer;
+ return err;
+}
+
static snd_pcm_sframes_t snd_pcm_hw_avail_update(snd_pcm_t *pcm)
{
snd_pcm_hw_t *hw = pcm->private_data;
- snd_pcm_uframes_t avail;
+ snd_pcm_uframes_t avail, xfer_avail;
sync_ptr(hw, 0);
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
avail = snd_pcm_mmap_playback_avail(pcm);
} else {
avail = snd_pcm_mmap_capture_avail(pcm);
- if (avail > 0 && hw->mmap_shm) {
+ if (avail < pcm->avail_min && hw->mmap_shm) {
snd_pcm_sframes_t err;
- snd_pcm_hw_t *hw = pcm->private_data;
- hw->avail_update_flag = 1;
- err = snd_pcm_read_mmap(pcm, avail);
- hw->avail_update_flag = 0;
- if (err < 0)
- return err;
- if ((snd_pcm_uframes_t)err != avail)
- SNDMSG("short read %ld for avail %ld", err, avail);
- return err;
- }
+ xfer_avail = snd_pcm_hw_capture_avail(pcm);
+ xfer_avail -= xfer_avail % pcm->xfer_align;
+ if (xfer_avail > 0) {
+ hw->avail_update_flag = 1;
+ err = snd_pcm_hw_read_mmap(pcm, xfer_avail);
+ hw->avail_update_flag = 0;
+ if (err < 0)
+ return err;
+ hw->hw_ptr += err;
+ avail = snd_pcm_mmap_capture_avail(pcm);
+ }
+ }
}
switch (FAST_PCM_STATE(hw)) {
case SNDRV_PCM_STATE_RUNNING:
2
4
04 Jul '07
Hello,
I have got a 690G board with following lspic -nv output:
01:05.2 0403: 1002:7919
Subsystem: 1002:7919
Flags: bus master, fast devsel, latency 64, IRQ 19
Memory at fe9e8000 (64-bit, non-prefetchable) [size=16K]
Capabilities: [50] Power Management version 2
Capabilities: [60] Message Signalled Interrupts: 64bit+ Queue=0/0
Enable-
With the default setting, microphone does not work. With module
parameter "modprobe -i snd-hda-intel model=6stack-dig" microphone works well.
Please add to default setting. It was written in Linux Kernel documentation
to report this issue here.
Thank you,
Peter
2
1
04 Jul '07
Hi, i've got an hda/alc268 card (pci id: [8086:284b], subsytem id:
[1025:011f]) on my laptop, and the sound is ok for the loudspeakers
but it is very very low for the headphones (the volume is at the top
in the mixer, if i reduce the volume in the mixer the sound gets even
lower).
Under windows vista the headphones work well (ok, worked, before that
i removed windows from my computer).
I am using alsa hg, the most recent version at the moment i'm writing
this e-mail.
I there is any experiment that i can do or if i can help in any way (i
know C very well) to fix this bug just let me know, and i'll be happy
to contribute.
More information about the hardware follows.
Thanks!
--
Ciao
Maurizio
http://stregatto.wordpress.com
"Well we all shine on
Like the moon and the stars and the sun" (John Lennon)
lspci entry:
00:1b.0 Audio device [0403]: Intel Corporation 82801H (ICH8 Family) HD
Audio Controller [8086:284b] (rev 03)
Subsystem: Acer Incorporated [ALI] Unknown device [1025:011f]
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop-
ParErr- Stepping- SERR+ FastB2B-
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort-
<TAbort- <MAbort- >SERR- <PERR-
Latency: 0, Cache Line Size: 64 bytes
Interrupt: pin A routed to IRQ 22
Region 0: Memory at fc300000 (64-bit, non-prefetchable) [size=16K]
Capabilities: [50] Power Management version 2
Flags: PMEClk- DSI- D1- D2- AuxCurrent=55mA
PME(D0+,D1-,D2-,D3hot+,D3cold+)
Status: D0 PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [60] Message Signalled Interrupts: Mask- 64bit+
Queue=0/0 Enable-
Address: 0000000000000000 Data: 0000
Capabilities: [70] Express Unknown type IRQ 0
Device: Supported: MaxPayload 128 bytes, PhantFunc 0, ExtTag-
Device: Latency L0s <64ns, L1 <1us
Device: Errors: Correctable- Non-Fatal- Fatal- Unsupported-
Device: RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop+
Device: MaxPayload 128 bytes, MaxReadReq 128 bytes
Link: Supported Speed unknown, Width x0, ASPM unknown, Port 0
Link: Latency L0s <64ns, L1 <1us
Link: ASPM Disabled CommClk- ExtSynch-
Link: Speed unknown, Width x0
/proc/asound/card0/codec#0
Codec: Realtek ALC268
Address: 0
Vendor Id: 0x10ec0268
Subsystem Id: 0x1025011f
Revision Id: 0x100003
No Modem Function Group found
Default PCM:
rates [0x560]: 44100 48000 96000 192000
bits [0xe]: 16 20 24
formats [0x1]: PCM
Default Amp-In caps: N/A
Default Amp-Out caps: N/A
Node 0x02 [Audio Output] wcaps 0x1d: Stereo Amp-Out
Amp-Out caps: ofs=0x40, nsteps=0x40, stepsize=0x03, mute=0
Amp-Out vals: [0x40 0x40]
PCM:
rates [0x560]: 44100 48000 96000 192000
bits [0xe]: 16 20 24
formats [0x1]: PCM
Node 0x03 [Audio Output] wcaps 0x1d: Stereo Amp-Out
Amp-Out caps: ofs=0x40, nsteps=0x40, stepsize=0x03, mute=0
Amp-Out vals: [0x2f 0x2f]
PCM:
rates [0x560]: 44100 48000 96000 192000
bits [0xe]: 16 20 24
formats [0x1]: PCM
Node 0x04 [Vendor Defined Widget] wcaps 0xf00000: Mono
Node 0x05 [Vendor Defined Widget] wcaps 0xf00000: Mono
Node 0x06 [Audio Output] wcaps 0x211: Stereo Digital
PCM:
rates [0x5e0]: 44100 48000 88200 96000 192000
bits [0x1e]: 16 20 24 32
formats [0x1]: PCM
Node 0x07 [Audio Input] wcaps 0x100111: Stereo
PCM:
rates [0x160]: 44100 48000 96000
bits [0x6]: 16 20
formats [0x1]: PCM
Connection: 1
0x24
Node 0x08 [Audio Input] wcaps 0x100111: Stereo
PCM:
rates [0x160]: 44100 48000 96000
bits [0x6]: 16 20
formats [0x1]: PCM
Connection: 1
0x23
Node 0x09 [Vendor Defined Widget] wcaps 0xf00000: Mono
Node 0x0a [Vendor Defined Widget] wcaps 0xf00000: Mono
Node 0x0b [Vendor Defined Widget] wcaps 0xf00000: Mono
Node 0x0c [Vendor Defined Widget] wcaps 0xf00000: Mono
Node 0x0d [Vendor Defined Widget] wcaps 0xf00000: Mono
Node 0x0e [Audio Mixer] wcaps 0x20010a: Mono Amp-In
Amp-In caps: ofs=0x00, nsteps=0x00, stepsize=0x00, mute=1
Amp-In vals: [0x00]
Connection: 1
0x02
Node 0x0f [Audio Mixer] wcaps 0x20010b: Stereo Amp-In
Amp-In caps: ofs=0x00, nsteps=0x00, stepsize=0x00, mute=1
Amp-In vals: [0x00 0x00] [0x80 0x80]
Connection: 2
0x02 0x1d
Node 0x10 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In
Amp-In caps: ofs=0x00, nsteps=0x00, stepsize=0x00, mute=1
Amp-In vals: [0x00 0x00] [0x80 0x80] [0x80 0x80]
Connection: 3
0x03 0x1d 0x02
Node 0x11 [Vendor Defined Widget] wcaps 0xf00000: Mono
Node 0x12 [Pin Complex] wcaps 0x400001: Stereo
Pincap 0x0820: IN
Pin Default 0x411111f0: [N/A] Speaker at Ext Rear
Conn = 1/8, Color = Black
Pin-ctls: 0x00:
Node 0x13 [Pin Complex] wcaps 0x400001: Stereo
Pincap 0x0820: IN
Pin Default 0x411111f0: [N/A] Speaker at Ext Rear
Conn = 1/8, Color = Black
Pin-ctls: 0x00:
Node 0x14 [Pin Complex] wcaps 0x40018d: Stereo Amp-Out
Amp-Out caps: ofs=0x00, nsteps=0x00, stepsize=0x00, mute=1
Amp-Out vals: [0x00 0x00]
Pincap 0x081003c: IN OUT HP EAPD Detect
Pin Default 0x02214020: [Jack] HP Out at Ext Front
Conn = 1/8, Color = Green
Pin-ctls: 0xc0: OUT HP
Connection: 1
0x0f
Node 0x15 [Pin Complex] wcaps 0x40018d: Stereo Amp-Out
Amp-Out caps: ofs=0x00, nsteps=0x00, stepsize=0x00, mute=1
Amp-Out vals: [0x80 0x80]
Pincap 0x081003c: IN OUT HP EAPD Detect
Pin Default 0x99130110: [Fixed] Speaker at Int ATAPI
Conn = ATAPI, Color = Unknown
Pin-ctls: 0x40: OUT
Connection: 1
0x10
Node 0x16 [Pin Complex] wcaps 0x40010c: Mono Amp-Out
Amp-Out caps: ofs=0x00, nsteps=0x00, stepsize=0x00, mute=1
Amp-Out vals: [0x80]
Pincap 0x0810: OUT
Pin Default 0x411111f0: [N/A] Speaker at Ext Rear
Conn = 1/8, Color = Black
Pin-ctls: 0x00:
Connection: 1
0x0e
Node 0x17 [Vendor Defined Widget] wcaps 0xf00000: Mono
Node 0x18 [Pin Complex] wcaps 0x40018f: Stereo Amp-In Amp-Out
Amp-In caps: ofs=0x00, nsteps=0x02, stepsize=0x4f, mute=0
Amp-In vals: [0x00 0x00]
Amp-Out caps: ofs=0x00, nsteps=0x00, stepsize=0x00, mute=1
Amp-Out vals: [0x80 0x80]
Pincap 0x083734: IN OUT Detect
Pin Default 0x02a19830: [Jack] Mic at Ext Front
Conn = 1/8, Color = Pink
Pin-ctls: 0x24: IN
Connection: 1
0x02
Node 0x19 [Pin Complex] wcaps 0x40008b: Stereo Amp-In
Amp-In caps: ofs=0x00, nsteps=0x02, stepsize=0x4f, mute=0
Amp-In vals:
Pincap 0x083724: IN Detect
Pin Default 0x99a30931: [Fixed] Mic at Int ATAPI
Conn = ATAPI, Color = Unknown
Pin-ctls: 0x24: IN
Node 0x1a [Pin Complex] wcaps 0x40018f: Stereo Amp-In Amp-Out
Amp-In caps: ofs=0x00, nsteps=0x02, stepsize=0x4f, mute=0
Amp-In vals: [0x00 0x00]
Amp-Out caps: ofs=0x00, nsteps=0x00, stepsize=0x00, mute=1
Amp-Out vals: [0x80 0x80]
Pincap 0x083734: IN OUT Detect
Pin Default 0x0281303e: [Jack] Line In at Ext Front
Conn = 1/8, Color = Blue
Pin-ctls: 0x20: IN
Connection: 1
0x02
Node 0x1b [Vendor Defined Widget] wcaps 0xf00000: Mono
Node 0x1c [Pin Complex] wcaps 0x400001: Stereo
Pincap 0x0820: IN
Pin Default 0x411111f0: [N/A] Speaker at Ext Rear
Conn = 1/8, Color = Black
Pin-ctls: 0x20: IN
Node 0x1d [Pin Complex] wcaps 0x400000: Mono
Pincap 0x0820: IN
Pin Default 0x4017952d: [N/A] Speaker at Ext N/A
Conn = Analog, Color = Pink
Pin-ctls: 0x20: IN
Node 0x1e [Pin Complex] wcaps 0x400380: Mono Digital
Pincap 0x0810: OUT
Pin Default 0x411111f0: [N/A] Speaker at Ext Rear
Conn = 1/8, Color = Black
Pin-ctls: 0x40: OUT
Connection: 1
0x06
Node 0x1f [Vendor Defined Widget] wcaps 0xf00000: Mono
Node 0x20 [Vendor Defined Widget] wcaps 0xf00040: Mono
Node 0x21 [Vendor Defined Widget] wcaps 0xf00000: Mono
Node 0x22 [Vendor Defined Widget] wcaps 0xf00000: Mono
Node 0x23 [Audio Selector] wcaps 0x30010d: Stereo Amp-Out
Amp-Out caps: ofs=0x0b, nsteps=0x1f, stepsize=0x05, mute=1
Amp-Out vals: [0x90 0x90]
Connection: 7
0x18 0x19 0x1a 0x1c 0x14 0x15* 0x12
Node 0x24 [Audio Selector] wcaps 0x30010d: Stereo Amp-Out
Amp-Out caps: ofs=0x0b, nsteps=0x1f, stepsize=0x05, mute=1
Amp-Out vals: [0x01 0x01]
Connection: 7
0x18 0x19 0x1a 0x1c 0x14* 0x15 0x13
/proc/asound/card0/codec#1:
Codec: Conexant ID 2c06
Address: 1
Vendor Id: 0x14f12c06
Subsystem Id: 0x10250093
Revision Id: 0x100000
Modem Function Group: 0x2
1
0
I'm in the early stages of testing an ASOC driver, i.e. I'm trying to get playback to work.
What is the easiest way to test playback? Is there a particular tool I should be using
that does the most basic and direct playback of audio data through ALSA?
--
Timur Tabi
Linux Kernel Developer @ Freescale
2
1
I'm encountering the following problem on Fedora Core 6 running on a Dell
Optiplex 745. This is a pretty vanilla computer with an ICH8 chipset and
integrated AD1983 sound capabilities.
I have wired up a funky line-input cable so that I have completely separate
audio sources playing on the left and right inputs.
When I either play these through to the speaker output directly (using
alsamixer), or record the audio using arecord, the result is that there is a
slight bit of bleeding or crossover, so I can faintly hear the left input on
the right channel, and vice versa. If I record a wav file and then play it
on another computer, the left/right bleeding follows the wav file, so it
seems that the problem is in the capture process somehow.
When I install Windows XP onto the same machine, the problem disappears, so
I can rule out hardware errors.
The application I'm working on relies on being able to reliably capture the
right and left channels of audio independently. Since the problem can be
seen using the standard arecord/aplay tools, though, I don't think it's my
code.
I've tried all the possible combinations inside alsamixer, and I don't think
there's anything I've messed up there.
I've put the relevant system details below. Thanks for any help you can
provide.
--Howdy
=============================
Howdy Pierce
Managing Partner
Cardinal Peak, LLC
work: (303) 665-3962 x101
cell: (303) 589-1645
email: howdy(a)cardinalpeak.com
web: www.cardinalpeak.com
=============================
[root@dundee ~]# cat /proc/asound/cards
0 [Intel ]: HDA-Intel - HDA Intel
HDA Intel at 0xdfdfc000 irq 169
[root@dundee ~]# cat /proc/asound/devices
2: : timer
3: : sequencer
4: [ 0- 1]: digital audio playback
5: [ 0- 0]: digital audio playback
6: [ 0- 0]: digital audio capture
7: [ 0] : control
[root@dundee ~]# lspci
00:00.0 Host bridge: Intel Corporation 82Q963/Q965 Memory Controller Hub
(rev 02)
00:01.0 PCI bridge: Intel Corporation 82Q963/Q965 PCI Express Root Port (rev
02)
00:02.0 Display controller: Intel Corporation 82Q963/Q965 Integrated
Graphics Controller (rev 02)
00:02.1 Display controller: Intel Corporation 82Q963/Q965 Integrated
Graphics Controller (rev 02)
00:1a.0 USB Controller: Intel Corporation 82801H (ICH8 Family) USB UHCI #4
(rev 02)
00:1a.1 USB Controller: Intel Corporation 82801H (ICH8 Family) USB UHCI #5
(rev 02)
00:1a.7 USB Controller: Intel Corporation 82801H (ICH8 Family) USB2 EHCI #2
(rev 02)
00:1b.0 Audio device: Intel Corporation 82801H (ICH8 Family) HD Audio
Controller (rev 02)
00:1c.0 PCI bridge: Intel Corporation 82801H (ICH8 Family) PCI Express Port
1 (rev 02)
00:1c.4 PCI bridge: Intel Corporation 82801H (ICH8 Family) PCI Express Port
5 (rev 02)
00:1d.0 USB Controller: Intel Corporation 82801H (ICH8 Family) USB UHCI #1
(rev 02)
00:1d.1 USB Controller: Intel Corporation 82801H (ICH8 Family) USB UHCI #2
(rev 02)
00:1d.2 USB Controller: Intel Corporation 82801H (ICH8 Family) USB UHCI #3
(rev 02)
00:1d.7 USB Controller: Intel Corporation 82801H (ICH8 Family) USB2 EHCI #1
(rev 02)
00:1e.0 PCI bridge: Intel Corporation 82801 PCI Bridge (rev f2)
00:1f.0 ISA bridge: Intel Corporation 82801HB/HR (ICH8/R) LPC Interface
Controller (rev 02)
00:1f.2 IDE interface: Intel Corporation 82801H (ICH8 Family) 4 port SATA
IDE Controller (rev 02)
00:1f.3 SMBus: Intel Corporation 82801H (ICH8 Family) SMBus Controller (rev
02)
00:1f.5 IDE interface: Intel Corporation 82801H (ICH8 Family) 2 port SATA
IDE Controller (rev 02)
03:00.0 Ethernet controller: Broadcom Corporation NetXtreme BCM5754 Gigabit
Ethernet PCI Express (rev 02)
04:02.0 VGA compatible controller: nVidia Corporation NV18 [GeForce4 MX 4000
AGP 8x] (rev c1)
[root@dundee ~]# rpm --query --all | grep alsa
alsa-lib-1.0.12-2.fc6
alsa-lib-devel-1.0.12-2.fc6
alsa-utils-1.0.12-3.fc6
[root@dundee Intel]# cat /proc/asound/Intel/codec#0
Codec: Analog Devices AD1983
Address: 0
Vendor Id: 0x11d41983
Subsystem Id: 0x102801da
Revision Id: 0x100400
Default PCM: rates 0x07f, bits 0x0e, types 0x1
Default Amp-In caps: N/A
Default Amp-Out caps: ofs=0x3d, nsteps=0x3f, stepsize=0x05, mute=1
Node 0x02 [Audio Output] wcaps 0x30311: Stereo Digital
PCM: rates 0x060, bits 0x02, types 0x5
Connection: 2
0x01* 0x04
Node 0x03 [Audio Output] wcaps 0x441: Stereo
Power: 0x0
Node 0x04 [Audio Input] wcaps 0x100501: Stereo
Power: 0x0
Connection: 1
0x14
Node 0x05 [Pin Complex] wcaps 0x400185: Stereo Amp-Out
Amp-Out caps: ofs=0x3d, nsteps=0x3f, stepsize=0x05, mute=1
Amp-Out vals: [0x32 0x32]
Pincap 0x0817: OUT Detect
Pin Default 0x01014010: [Jack] Line Out at Ext Rear
Conn = 1/8, Color = Green
Pin-ctls: 0x40: OUT
Connection: 2
0x03 0x0e*
Node 0x06 [Pin Complex] wcaps 0x400185: Stereo Amp-Out
Amp-Out caps: ofs=0x3d, nsteps=0x3f, stepsize=0x05, mute=1
Amp-Out vals: [0x25 0x25]
Pincap 0x081f: OUT HP Detect
Pin Default 0x0221101f: [Jack] HP Out at Ext Front
Conn = 1/8, Color = Black
Pin-ctls: 0xc0: OUT HP
Connection: 2
0x03 0x0e*
Node 0x07 [Pin Complex] wcaps 0x400104: Mono Amp-Out
Amp-Out caps: ofs=0x3d, nsteps=0x3f, stepsize=0x05, mute=1
Amp-Out vals: [0x80]
Pincap 0x0810: OUT
Pin Default 0x991301f0: [Fixed] Speaker at Int ATAPI
Conn = ATAPI, Color = Unknown
Pin-ctls: 0x40: OUT
Connection: 1
0x0f
Node 0x08 [Pin Complex] wcaps 0x400081: Stereo
Pincap 0x081727: IN Detect
Pin Default 0x02a11020: [Jack] Mic at Ext Front
Conn = 1/8, Color = Black
Pin-ctls: 0x24: IN
Node 0x09 [Pin Complex] wcaps 0x400081: Stereo
Pincap 0x081727: IN Detect
Pin Default 0x0181302e: [Jack] Line In at Ext Rear
Conn = 1/8, Color = Blue
Pin-ctls: 0x20: IN
Node 0x0a [Pin Complex] wcaps 0x400301: Stereo Digital
Pincap 0x0810: OUT
Pin Default 0x414511f0: [N/A] SPDIF Out at Ext Rear
Conn = Optical, Color = Black
Pin-ctls: 0x40: OUT
Connection: 1
0x02
Node 0x0b [Audio Selector] wcaps 0x300101: Stereo
Connection: 4
0x03 0x0c 0x0d 0x0e*
Node 0x0c [Audio Selector] wcaps 0x30010d: Stereo Amp-Out
Amp-Out caps: ofs=0x00, nsteps=0x03, stepsize=0x27, mute=0
Amp-Out vals: [0x00 0x00]
Connection: 2
0x08* 0x09
Node 0x0d [Audio Selector] wcaps 0x300101: Stereo
Connection: 2
0x09* 0x08
Node 0x0e [Audio Mixer] wcaps 0x200101: Stereo
Connection: 3
0x11 0x12 0x13
Node 0x0f [Audio Mixer] wcaps 0x200100: Mono
Connection: 1
0x0b
Node 0x10 [Beep Generator Widget] wcaps 0x70000c: Mono Amp-Out
Amp-Out caps: ofs=0x0f, nsteps=0x0f, stepsize=0x0b, mute=1
Amp-Out vals: [0x80]
Node 0x11 [Audio Selector] wcaps 0x30010d: Stereo Amp-Out
Amp-Out caps: ofs=0x17, nsteps=0x1f, stepsize=0x05, mute=1
Amp-Out vals: [0x97 0x17]
Connection: 1
0x03
Node 0x12 [Audio Selector] wcaps 0x30010d: Stereo Amp-Out
Amp-Out caps: ofs=0x17, nsteps=0x1f, stepsize=0x05, mute=1
Amp-Out vals: [0x80 0x80]
Connection: 1
0x0c
Node 0x13 [Audio Selector] wcaps 0x30010d: Stereo Amp-Out
Amp-Out caps: ofs=0x17, nsteps=0x1f, stepsize=0x05, mute=1
Amp-Out vals: [0x9f 0x9f]
Connection: 1
0x0d
Node 0x14 [Audio Selector] wcaps 0x30010d: Stereo Amp-Out
Amp-Out caps: ofs=0x00, nsteps=0x0f, stepsize=0x05, mute=1
Amp-Out vals: [0x0f 0x0f]
Connection: 4
0x0c 0x0d* 0x0e 0x0f
Node 0x15 [Power Widget] wcaps 0x500500: Mono
Power: 0x0
Connection: 15
0x05 0x06* 0x07 0x08 0x09 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13
0x14
2
3
Multiple users report the PCMCIA Audigy 2 adapter hangs the machine in
2.6.21 while it work OK in 2.6.20:
https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=242208
3
3