[alsa-devel] [PATCH 1/2] ASoC: new ADAV801/ADAV803 codec driver
Mike Frysinger
vapier at gentoo.org
Sat Aug 7 17:27:43 CEST 2010
From: Yi Li <yi.li at analog.com>
Signed-off-by: Yi Li <yi.li at analog.com>
Signed-off-by: Cliff Cai <cliff.cai at analog.com>
Signed-off-by: Mike Frysinger <vapier at gentoo.org>
---
sound/soc/codecs/Kconfig | 6 +-
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/adav80x.c | 631 ++++++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/adav80x.h | 93 +++++++
4 files changed, 731 insertions(+), 1 deletions(-)
create mode 100644 sound/soc/codecs/adav80x.c
create mode 100644 sound/soc/codecs/adav80x.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 5db82d2..ae01cba 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -15,8 +15,9 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AD1836 if SPI_MASTER
select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
- select SND_SOC_ADS117X
select SND_SOC_AD73311 if I2C
+ select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI
+ select SND_SOC_ADS117X
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
select SND_SOC_AK4642 if I2C
@@ -103,6 +104,9 @@ config SND_SOC_AD1980
config SND_SOC_AD73311
tristate
+config SND_SOC_ADAV80X
+ tristate
+
config SND_SOC_ADS117X
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 7593eeb..f5b3d5b 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -3,6 +3,7 @@ snd-soc-ad1836-objs := ad1836.o
snd-soc-ad193x-objs := ad193x.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
+snd-soc-adav80x-objs := adav80x.o
snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
@@ -69,6 +70,7 @@ obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
+obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o
obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
new file mode 100644
index 0000000..778473c
--- /dev/null
+++ b/sound/soc/codecs/adav80x.c
@@ -0,0 +1,631 @@
+/*
+ * ADAV80X Audio Codec driver supporting ADAV801, ADAV803
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/soc-dapm.h>
+#include "adav80x.h"
+
+
+struct adav80x_priv {
+ struct snd_soc_codec codec;
+ u16 reg_cache[ADAV80X_NUM_REGS];
+ int clk_src; /* clock source for ADC, DAC and internal clock */
+};
+
+static struct snd_soc_codec *adav80x_codec;
+struct snd_soc_codec_device soc_codec_dev_adav80x;
+static int adav80x_register(struct adav80x_priv *adav80x, int bus_type);
+static void adav80x_unregister(struct adav80x_priv *adav80x);
+
+static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", ADAV80X_DAC_CTRL1, 7, 1),
+ SND_SOC_DAPM_ADC("ADC", "Capture", ADAV80X_ADC_CTRL1, 5, 1),
+ SND_SOC_DAPM_SUPPLY("PLL_PWR", ADAV80X_PLL_CTRL1, 1, 1, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route audio_paths[] = {
+ { "DAC", NULL, "PLL_PWR" },
+ { "ADC", NULL, "PLL_PWR" },
+};
+
+/*
+ * ADAV80X volume/mute/de-emphasis etc. controls
+ */
+static const char *adav80x_deemp[] = {"None", "44.1kHz", "32kHz", "48kHz"};
+
+static const struct soc_enum adav80x_deemp_enum =
+SOC_ENUM_SINGLE(ADAV80X_DAC_CTRL2, 0, 4, adav80x_deemp);
+
+static const struct snd_kcontrol_new adav80x_snd_controls[] = {
+ /* DAC volume control */
+ SOC_DOUBLE_R("DAC Volume", ADAV80X_DAC_L_VOL,
+ ADAV80X_DAC_R_VOL, 0, 0xFF, 0),
+ /* DAC peak volume detect, read clears it */
+ SOC_DOUBLE_R("DAC Peak Volume", ADAV80X_DAC_L_PEAK_VOL,
+ ADAV80X_DAC_R_PEAK_VOL, 0, 0x3F, 0),
+
+ /* ADC volume control */
+ SOC_DOUBLE_R("ADC Volume", ADAV80X_ADC_L_VOL,
+ ADAV80X_ADC_R_VOL, 0, 0xFF, 0),
+ /* ADC peak volume detect */
+ SOC_DOUBLE_R("ADC Peak Volume", ADAV80X_ADC_L_PEAK_VOL,
+ ADAV80X_ADC_R_PEAK_VOL, 0, 0x3F, 0),
+
+ /* ADC mute */
+ SOC_DOUBLE("ADC Switch", ADAV80X_ADC_CTRL1, 2, 3, 1, 1),
+
+ /* DAC mute */
+ SOC_DOUBLE("DAC Switch", ADAV80X_DAC_CTRL1, 0, 1, 1, 1),
+
+ /* ADC high-pass filter */
+ SOC_SINGLE("ADC High Pass Filter Switch", ADAV80X_ADC_CTRL1, 6, 1, 0),
+
+ /* DAC de-emphasis */
+ SOC_ENUM("Playback Deemphasis", adav80x_deemp_enum),
+};
+
+/*
+ * DAI ops entries
+ */
+
+static int adav80x_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 rec_ctl = 0, playback_ctl = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* use internal clock 1 */
+ playback_ctl |= 0x10;
+ rec_ctl |= 0x20;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ /* DAC word length depends on input, ADC need to set word length */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ playback_ctl |= 0x1;
+ rec_ctl |= 0x1;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ playback_ctl |= 0x4;
+ rec_ctl |= 0x3;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ /* Use default - seems no polarity setting */
+ /* DAC control reg 1: CHSEL[1:0], POL[1:0] ? */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, ADAV80X_PLAYBACK_CTRL, playback_ctl);
+ snd_soc_write(codec, ADAV80X_REC_CTRL, rec_ctl);
+
+ return 0;
+}
+
+static int adav80x_set_adc_clock(struct snd_soc_codec *codec,
+ int clk_id, unsigned int sample_rate)
+{
+ int reg;
+
+ if (clk_id == ADAV80X_CLK_PLL1) {
+ /* ADC assumes that the MCLK ratre is 256 times the sample rate.
+ We also assumes PLL1 clock rate to be (256 * Fs),
+ So set ADC MCLK divider to be 1 */
+
+ reg = snd_soc_read(codec, ADAV80X_ADC_CTRL1);
+ /* ADC Modulator clock is 6.144MHz Max,
+ need to set devidor properly */
+ if (sample_rate == 96000)
+ reg |= 0x80;
+ else if (sample_rate == 48000)
+ reg &= 0x7F;
+ else
+ /* Unsupported sample rate */
+ return -1;
+
+ snd_soc_write(codec, ADAV80X_ADC_CTRL1, reg);
+ }
+
+ return 0;
+}
+
+static int adav80x_set_dac_clock(struct snd_soc_codec *codec,
+ int clk_id, unsigned int sample_rate)
+{
+ int reg;
+
+ if (clk_id == ADAV80X_CLK_PLL1) {
+ /* PLL1 clock rate is assumed to be 256 * Fs */
+
+ reg = snd_soc_read(codec, ADAV80X_DAC_CTRL2);
+ if (sample_rate == 96000)
+ /* Set the MCLK divider to be MCLK/2,
+ and MCLK = 128 * Fs */
+ reg |= 0x24;
+ else
+ reg &= 0x11;
+
+ snd_soc_write(codec, ADAV80X_DAC_CTRL2, reg);
+ }
+
+ return 0;
+}
+
+static int adav80x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int word_len = 0;
+ int rate = params_rate(params);
+
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct adav80x_priv *adav80x = codec->private_data;
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ word_len = 3;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ word_len = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ word_len = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ word_len = 0;
+ break;
+ }
+
+
+ /* Playback port does not need to set word length? */
+
+ /* Record Port Control */
+ snd_soc_update_bits(codec, ADAV80X_REC_CTRL, 0x3<<2, word_len<<2);
+
+ /* Set up clock */
+ if (adav80x->clk_src == ADAV80X_CLK_PLL1) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ adav80x_set_dac_clock(codec, ADAV80X_CLK_PLL1, rate);
+ else
+ adav80x_set_adc_clock(codec, ADAV80X_CLK_PLL1, rate);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int adav80x_soc_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return 0;
+}
+
+static int adav80x_soc_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define adav80x_soc_suspend NULL
+#define adav80x_soc_resume NULL
+#endif
+
+static int adav80x_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int reg;
+
+ reg = snd_soc_read(codec, ADAV80X_DAC_CTRL1);
+ reg = (mute > 0) ? reg & ~0x3 : reg | 0x3;
+ snd_soc_write(codec, ADAV80X_DAC_CTRL1, reg);
+
+ return 0;
+}
+
+
+static int adav80x_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct adav80x_priv *adav80x = codec->private_data;
+ int reg = 0;
+
+ /* For now, we only enable PLL1 with XIN as source */
+ if (source != ADAV80X_CLK_XIN)
+ return -1;
+
+ if (freq_in && freq_out) {
+ /* XIN - assumes 27MHz */
+ if (freq_in != 27000000)
+ return -1;
+
+ if (pll_id != ADAV80X_CLK_PLL1)
+ return -1;
+
+ /* freq_out = sample_rate * 256 */
+ switch (freq_out) {
+ case 32000:
+ reg = 0x8;
+ break;
+ case 44100:
+ reg = 0xC;
+ break;
+ case 48000:
+ reg = 0x0;
+ break;
+ case 64000:
+ reg = 0x9;
+ break;
+ case 88200:
+ reg = 0xD;
+ break;
+ case 96000:
+ reg = 0x1;
+ break;
+ }
+
+ /* Set PLL1 clock */
+ snd_soc_write(codec, ADAV80X_PLL_CTRL2, reg);
+
+ if (adav80x->clk_src == ADAV80X_CLK_PLL1)
+ return 0;
+ else
+ adav80x->clk_src = ADAV80X_CLK_PLL1;
+
+ /* select XIN as PLL1 clock source */
+ snd_soc_write(codec, ADAV80X_PLL_CLK_SRC, 0x0);
+ /* set PLL1 as clock source for internal clock, DAC, ADC */
+ snd_soc_write(codec, ADAV80X_ICLK_CTRL1, 0x4A);
+ snd_soc_write(codec, ADAV80X_ICLK_CTRL2, 0x10);
+ /* Power on PLL1, power down PLL2, power on XTAL */
+ snd_soc_write(codec, ADAV80X_PLL_CTRL1, 0x8);
+
+ } else {
+ if (adav80x->clk_src == ADAV80X_CLK_XIN)
+ return 0;
+ else
+ adav80x->clk_src = ADAV80X_CLK_XIN;
+
+ /* Turn off PLL, power on XTAL */
+ snd_soc_write(codec, ADAV80X_PLL_CTRL1, 0xC);
+
+ /* DAC, ADC, ICLK clock source - XIN */
+ snd_soc_write(codec, ADAV80X_ICLK_CTRL1, 0x0);
+ snd_soc_write(codec, ADAV80X_ICLK_CTRL2, 0x0);
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops adav80x_dai_ops = {
+ .hw_params = adav80x_hw_params,
+ .set_fmt = adav80x_set_dai_fmt,
+ .digital_mute = adav80x_mute,
+ .set_pll = adav80x_set_dai_pll,
+};
+
+/* codec DAI instance */
+/* DAC sample rates: 32/44.1/48/96/192kHz, ADC sample reate: 48/96kHz - TBD */
+struct snd_soc_dai adav80x_dai = {
+ .name = "ADAV80X",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_96000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &adav80x_dai_ops,
+};
+EXPORT_SYMBOL_GPL(adav80x_dai);
+
+
+static int adav80x_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (adav80x_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = adav80x_codec;
+ codec = adav80x_codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+ goto pcm_err;
+ }
+
+ snd_soc_add_controls(codec, adav80x_snd_controls,
+ ARRAY_SIZE(adav80x_snd_controls));
+ snd_soc_dapm_new_controls(codec, adav80x_dapm_widgets,
+ ARRAY_SIZE(adav80x_dapm_widgets));
+ snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
+
+pcm_err:
+ return ret;
+}
+
+/* power down chip */
+static int adav80x_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_adav80x = {
+ .probe = adav80x_probe,
+ .remove = adav80x_remove,
+ .suspend = adav80x_soc_suspend,
+ .resume = adav80x_soc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_adav80x);
+
+static int adav80x_register(struct adav80x_priv *adav80x, int bus_type)
+{
+ int ret;
+ struct snd_soc_codec *codec = &adav80x->codec;
+
+ if (adav80x_codec) {
+ dev_err(codec->dev, "Another adav80x is registered\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&codec->mutex);
+ codec->private_data = adav80x;
+ codec->reg_cache = adav80x->reg_cache;
+ codec->reg_cache_size = ADAV80X_NUM_REGS;
+ codec->name = "ADAV80X";
+ codec->owner = THIS_MODULE;
+ codec->dai = &adav80x_dai;
+ codec->num_dai = 1;
+
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ adav80x_dai.dev = codec->dev;
+ adav80x_codec = codec;
+
+
+ if (bus_type == SND_SOC_I2C)
+ /* addr(7-bit left shifted 1), data(8bit)*/
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, bus_type);
+ else
+ /* register format: addr(7bit), data(9bit) */
+ /* set spi bits_per_word to 8 instead of 16,
+ since addr need to be transfer before data */
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, bus_type);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to set cache I/O: %d\n",
+ ret);
+ kfree(adav80x);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ kfree(adav80x);
+ return ret;
+ }
+
+ ret = snd_soc_register_dai(&adav80x_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ snd_soc_unregister_codec(codec);
+ kfree(adav80x);
+ return ret;
+ }
+
+ /* default setting for adav80x */
+
+ /* Power down SYSCLK output, power down S/PDIF receiver */
+ snd_soc_write(codec, ADAV80X_PLL_OUTE, 0x27);
+
+ /* Disable S/PDIF transmitter */
+ snd_soc_write(codec, ADAV80X_TX_CTRL, 0x0);
+
+ /* Datapath: ADC->record, AUX_IN->SRC->AUX_OUT */
+ snd_soc_write(codec, ADAV80X_DPATH_CTRL1, 0xC4);
+ /* Datapath: playback->DAC, DIR->DIT */
+ snd_soc_write(codec, ADAV80X_DPATH_CTRL2, 0x11);
+
+ /* Soft-mute SRC output */
+ snd_soc_write(codec, ADAV80X_GDELAY_MUTE, 0x80);
+
+ /* DAC: de-emphasis: none, MCLCK divider: 1, MCLK=256xFs */
+ /* snd_soc_write(codec, ADAV80X_DAC_CTRL2, 0x0); */
+ /* Disable DAC zero flag */
+ snd_soc_write(codec, ADAV80X_DAC_CTRL3, 0x6);
+ /* DAC: volume */
+ snd_soc_write(codec, ADAV80X_DAC_L_VOL, 0xFF);
+ snd_soc_write(codec, ADAV80X_DAC_R_VOL, 0xFF);
+
+ /* ADC: power up, unmute adc channles */
+ /* snd_soc_write(codec, ADAV80X_ADC_CTRL1, 0x0); */
+ /* MCLCK divider: 1 */
+ /* snd_soc_write(codec, ADAV80X_ADC_CTRL2, 0x0); */
+ /* ADC: volumn */
+ snd_soc_write(codec, ADAV80X_ADC_L_VOL, 0xFF);
+ snd_soc_write(codec, ADAV80X_ADC_R_VOL, 0xFF);
+
+ /* Disable ALC */
+ snd_soc_write(codec, ADAV80X_ALC_CTRL1, 0x0);
+
+ return 0;
+}
+
+static void adav80x_unregister(struct adav80x_priv *adav80x)
+{
+ snd_soc_unregister_dai(&adav80x_dai);
+ snd_soc_unregister_codec(&adav80x->codec);
+ kfree(adav80x);
+ adav80x_codec = NULL;
+}
+
+static int __devinit adav80x_bus_probe(struct device *dev, void *ctrl_data,
+ int bus_type)
+{
+ struct snd_soc_codec *codec;
+ struct adav80x_priv *adav80x;
+
+ adav80x = kzalloc(sizeof(struct adav80x_priv), GFP_KERNEL);
+ if (adav80x == NULL)
+ return -ENOMEM;
+
+ codec = &adav80x->codec;
+ codec->control_data = ctrl_data;
+ codec->dev = dev;
+
+ dev_set_drvdata(dev, adav80x);
+
+ return adav80x_register(adav80x, bus_type);
+}
+
+int adav80x_bus_remove(struct device *dev)
+{
+ struct adav80x_priv *adav80x = dev_get_drvdata(dev);
+
+ adav80x_unregister(adav80x);
+ return 0;
+}
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit adav80x_spi_probe(struct spi_device *spi)
+{
+ return adav80x_bus_probe(&spi->dev, spi, SND_SOC_SPI);
+}
+
+static int __devexit adav80x_spi_remove(struct spi_device *spi)
+{
+ return adav80x_bus_remove(&spi->dev);
+}
+
+static struct spi_driver adav80x_spi_driver = {
+ .driver = {
+ .name = "adav80x",
+ .owner = THIS_MODULE,
+ },
+ .probe = adav80x_spi_probe,
+ .remove = __devexit_p(adav80x_spi_remove),
+};
+#endif
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static const struct i2c_device_id adav80x_id[] = {
+ { "adav803", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adav80x_id);
+
+static int __devinit adav80x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return adav80x_bus_probe(&client->dev, client, SND_SOC_I2C);
+}
+
+static int __devexit adav80x_i2c_remove(struct i2c_client *client)
+{
+ return adav80x_bus_remove(&client->dev);
+}
+
+static struct i2c_driver adav80x_i2c_driver = {
+ .driver = {
+ .name = "adav80x",
+ },
+ .probe = adav80x_i2c_probe,
+ .remove = __devexit_p(adav80x_i2c_remove),
+ .id_table = adav80x_id,
+};
+#endif
+
+static int __init adav80x_init(void)
+{
+ int ret;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ ret = i2c_add_driver(&adav80x_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register ADAV80X I2C driver: %d\n",
+ ret);
+ }
+#elif defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&adav80x_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register ADAV80X SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+module_init(adav80x_init);
+
+static void __exit adav80x_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&adav80x_i2c_driver);
+#elif defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&adav80x_spi_driver);
+#endif
+}
+module_exit(adav80x_exit);
+
+MODULE_DESCRIPTION("ASoC adav80x driver");
+MODULE_AUTHOR("Yi Li");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adav80x.h b/sound/soc/codecs/adav80x.h
new file mode 100644
index 0000000..b4a5a4d
--- /dev/null
+++ b/sound/soc/codecs/adav80x.h
@@ -0,0 +1,93 @@
+extern struct snd_soc_dai adav80x_dai;
+extern struct snd_soc_codec_device soc_codec_dev_adav80x;
+
+/* Clock source */
+#define ADAV80X_CLK_XIN 1
+#define ADAV80X_CLK_PLL1 2
+#define ADAV80X_CLK_PLL2 3
+#define ADAV80X_CLK_INTERNAL1 4
+#define ADAV80X_CLK_INTERNAL2 5
+#define ADAV80X_CLK_MCLKI 6
+
+
+#if (CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+/* ADAV80X I2C interface requires left-shifting reg addr for 1-bit */
+#define ADAV80X_NUM_REGS (0x7E<<1)
+
+#define ADAV80X_PLAYBACK_CTRL (0x4<<1)
+#define ADAV80X_AUX_IN_CTRL (0x5<<1)
+#define ADAV80X_REC_CTRL (0x6<<1)
+#define ADAV80X_AUX_OUT_CTRL (0x7<<1)
+#define ADAV80X_GDELAY_MUTE (0x8<<1)
+
+
+#define ADAV80X_TX_CTRL (0xC<<1)
+
+#define ADAV80X_DPATH_CTRL1 (0x62<<1)
+#define ADAV80X_DPATH_CTRL2 (0x63<<1)
+#define ADAV80X_DAC_CTRL1 (0x64<<1)
+#define ADAV80X_DAC_CTRL2 (0x65<<1)
+#define ADAV80X_DAC_CTRL3 (0x66<<1)
+#define ADAV80X_DAC_CTRL4 (0x67<<1)
+#define ADAV80X_DAC_L_VOL (0x68<<1)
+#define ADAV80X_DAC_R_VOL (0x69<<1)
+#define ADAV80X_DAC_L_PEAK_VOL (0x6A<<1)
+#define ADAV80X_DAC_R_PEAK_VOL (0x6B<<1)
+
+
+#define ADAV80X_ADC_CTRL1 (0x6E<<1)
+#define ADAV80X_ADC_CTRL2 (0x6F<<1)
+#define ADAV80X_ADC_L_VOL (0x70<<1)
+#define ADAV80X_ADC_R_VOL (0x71<<1)
+#define ADAV80X_ADC_L_PEAK_VOL (0x72<<1)
+#define ADAV80X_ADC_R_PEAK_VOL (0x73<<1)
+#define ADAV80X_PLL_CTRL1 (0x74<<1)
+#define ADAV80X_PLL_CTRL2 (0x75<<1)
+#define ADAV80X_ICLK_CTRL1 (0x76<<1)
+#define ADAV80X_ICLK_CTRL2 (0x77<<1)
+#define ADAV80X_PLL_CLK_SRC (0x78<<1)
+
+#define ADAV80X_PLL_OUTE (0x7A<<1)
+#define ADAV80X_ALC_CTRL1 (0x7B<<1)
+
+#elif defined(CONFIG_SPI_MASTER)
+/* from 0x0 to 0x7D */
+#define ADAV80X_NUM_REGS 0x7E
+
+#define ADAV80X_PLAYBACK_CTRL 0x4
+#define ADAV80X_AUX_IN_CTRL 0x5
+#define ADAV80X_REC_CTRL 0x6
+#define ADAV80X_AUX_OUT_CTRL 0x7
+#define ADAV80X_GDELAY_MUTE 0x8
+
+
+#define ADAV80X_TX_CTRL 0xC
+
+#define ADAV80X_DPATH_CTRL1 0x62
+#define ADAV80X_DPATH_CTRL2 0x63
+#define ADAV80X_DAC_CTRL1 0x64
+#define ADAV80X_DAC_CTRL2 0x65
+#define ADAV80X_DAC_CTRL3 0x66
+#define ADAV80X_DAC_CTRL4 0x67
+#define ADAV80X_DAC_L_VOL 0x68
+#define ADAV80X_DAC_R_VOL 0x69
+#define ADAV80X_DAC_L_PEAK_VOL 0x6A
+#define ADAV80X_DAC_R_PEAK_VOL 0x6B
+
+
+#define ADAV80X_ADC_CTRL1 0x6E
+#define ADAV80X_ADC_CTRL2 0x6F
+#define ADAV80X_ADC_L_VOL 0x70
+#define ADAV80X_ADC_R_VOL 0x71
+#define ADAV80X_ADC_L_PEAK_VOL 0x72
+#define ADAV80X_ADC_R_PEAK_VOL 0x73
+#define ADAV80X_PLL_CTRL1 0x74
+#define ADAV80X_PLL_CTRL2 0x75
+#define ADAV80X_ICLK_CTRL1 0x76
+#define ADAV80X_ICLK_CTRL2 0x77
+#define ADAV80X_PLL_CLK_SRC 0x78
+
+#define ADAV80X_PLL_OUTE 0x7A
+#define ADAV80X_ALC_CTRL1 0x7B
+
+#endif
--
1.7.2
More information about the Alsa-devel
mailing list