[alsa-devel] [PATCH 1/6] ASoC: add ADAU1361 codec driver
Mike Frysinger
vapier at gentoo.org
Sat Aug 7 22:28:20 CEST 2010
From: Cliff Cai <cliff.cai 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 | 4 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/adau1361.c | 992 +++++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/adau1361.h | 264 ++++++++++++
4 files changed, 1262 insertions(+), 0 deletions(-)
create mode 100644 sound/soc/codecs/adau1361.c
create mode 100644 sound/soc/codecs/adau1361.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index ae01cba..f37d5f4 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -16,6 +16,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
select SND_SOC_AD73311 if I2C
+ select SND_SOC_ADAU1361 if I2C
select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI
select SND_SOC_ADS117X
select SND_SOC_AK4104 if SPI_MASTER
@@ -104,6 +105,9 @@ config SND_SOC_AD1980
config SND_SOC_AD73311
tristate
+config SND_SOC_ADAU1361
+ tristate
+
config SND_SOC_ADAV80X
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index f5b3d5b..d05b672 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-adau1361-objs := adau1361.o
snd-soc-adav80x-objs := adav80x.o
snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o
@@ -70,6 +71,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_ADAU1361) += snd-soc-adau1361.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
diff --git a/sound/soc/codecs/adau1361.c b/sound/soc/codecs/adau1361.c
new file mode 100644
index 0000000..ad78420
--- /dev/null
+++ b/sound/soc/codecs/adau1361.c
@@ -0,0 +1,992 @@
+/*
+ * Driver for ADAU1361 sound codec
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "adau1361.h"
+
+#define AUDIO_NAME "adau1361"
+#define ADAU1361_VERSION "0.1"
+
+#define CAP_MIC 1
+#define CAP_LINE 2
+#define CAPTURE_SOURCE_NUMBER 2
+
+struct snd_soc_codec_device soc_codec_dev_adau1361;
+static struct snd_soc_codec *adau1361_codec;
+/* codec private data */
+struct adau1361_priv {
+ unsigned int sysclk;
+ unsigned int in_source;
+ unsigned int out_route;
+ unsigned int pll_out;
+ struct work_struct resume_work;
+ struct snd_soc_codec codec;
+ int dapm_state_suspend;
+ struct platform_device *pdev;
+ u8 pll_enable;
+ u8 adau1361_pll_reg[6];
+ u8 rate_index;
+ /* dapm */
+ u8 dapm_lineL;
+ u8 dapm_lineR;
+ u8 dapm_hpL;
+ u8 dapm_hpR;
+};
+
+/*
+ * write register cache
+ */
+static inline int adau1361_write_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ u8 *cache = codec->reg_cache;
+
+ if (reg < ADAU_FIRSTREG)
+ reg = reg + ADAU_FIRSTREG;
+
+ if ((reg < ADAU_FIRSTREG) || (reg > ADAU_LASTREG))
+ return -1;
+
+ cache[reg - ADAU_FIRSTREG] = value;
+
+ return 0;
+}
+
+/*
+ * read a multi-byte ADAU1361 register (6byte pll reg)
+ */
+static int adau1361_read_reg_block(struct snd_soc_codec *codec,
+ unsigned int reg, u8 len)
+{
+ u8 buf[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ u8 addr[2];
+ unsigned int i;
+
+ if (reg < ADAU_FIRSTREG)
+ reg = reg + ADAU_FIRSTREG;
+
+ if ((reg < ADAU_FIRSTREG) || (reg > ADAU_LASTREG))
+ return -EIO;
+
+ addr[0] = (u8)(reg >> 8);
+ addr[1] = (u8)(reg & 0xFF);
+
+ /* write the 2byte read address */
+ if (codec->hw_write(codec->control_data, addr, 2) != 2) {
+ dev_err(codec->dev, "read_reg_byte:address write failed.");
+ return -EIO;
+ }
+
+ if (i2c_master_recv(codec->control_data, buf, len) != len)
+ return -EIO;
+
+ for (i = 0; i < len; i++)
+ adau1361_write_reg_cache(codec, reg+i, (unsigned int)buf[i]);
+
+ return 0;
+}
+
+/*
+ * write a multibyte ADAU1361 register (6byte pll reg)
+ */
+static int adau1361_write_reg_block(struct snd_soc_codec *codec,
+ unsigned int reg, u8 length, u8 *values)
+{
+ int count = length + 2; /*data plus 16bit register address*/
+ u8 buf[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+ buf[0] = (u8)(reg >> 8);
+ buf[1] = (u8)(reg & 0xFF);
+
+ if (length > 0)
+ memcpy(&buf[2], values, length);
+
+ if (codec->hw_write(codec->control_data, buf, count) == count)
+ return 0;
+ else {
+ dev_err(codec->dev, "address block write failed.");
+ return -EIO;
+ }
+}
+
+/*
+ * adau1361 controls
+ */
+static int adau1361_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct adau1361_priv *adau1361 = codec->private_data;
+
+ if (adau1361->in_source & CAP_MIC)
+ ucontrol->value.integer.value[0] = 0x0;
+ else
+ ucontrol->value.integer.value[0] = 0x1;
+
+ return 0;
+}
+
+static int adau1361_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct adau1361_priv *adau1361 = codec->private_data;
+ int src = ucontrol->value.integer.value[0];
+ u8 regvalue = 0;
+
+ if (src == 0) {/* Select Mic */
+ adau1361->in_source = CAP_MIC;
+#ifdef ADAU1361_DIG_MIC
+ regvalue = (snd_soc_read(codec, ADAU_ADCCTL0) & 0xFB)|0x4;
+ snd_soc_write(codec, ADAU_ADCCTL0, regvalue);
+#else
+ snd_soc_write(codec, ADAU_RECMBIA, RECMBIA_DISABLE);
+ regvalue = (snd_soc_read(codec, ADAU_RECVLCL)
+ | RECVLC_ENABLE_MASK);
+ snd_soc_write(codec, ADAU_RECVLCL, regvalue);
+ regvalue = (snd_soc_read(codec, ADAU_RECVLCR)
+ | RECVLC_ENABLE_MASK);
+ snd_soc_write(codec, ADAU_RECVLCR, regvalue);
+ snd_soc_write(codec, ADAU_RECMLC1, RECMLC_MIC_0DB);
+ snd_soc_write(codec, ADAU_RECMRC1, RECMLC_MIC_0DB);
+#endif
+ } else if (src == 1) {/* Select Line */
+ adau1361->in_source = CAP_LINE;
+#ifdef ADAU1361_DIG_MIC
+ regvalue = (snd_soc_read(codec, ADAU_ADCCTL0) & 0xFB);
+ snd_soc_write(codec, ADAU_ADCCTL0, regvalue);
+#endif
+ snd_soc_write(codec, ADAU_RECMBIA, RECMBIA_DISABLE);
+ regvalue = (snd_soc_read(codec, ADAU_RECVLCL)
+ & RECVLC_DISABLE_MASK);
+ snd_soc_write(codec, ADAU_RECVLCL, regvalue);
+ regvalue = (snd_soc_read(codec, ADAU_RECVLCR)
+ & RECVLC_DISABLE_MASK);
+ snd_soc_write(codec, ADAU_RECVLCR, regvalue);
+ snd_soc_write(codec, ADAU_RECMLC1, RECMLC_LINE_0DB);
+ snd_soc_write(codec, ADAU_RECMRC1, RECMLC_LINE_0DB);
+ }
+
+ return 0;
+}
+
+static int adau1361_mic_boost_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct adau1361_priv *adau1361 = codec->private_data;
+
+ if (adau1361->in_source & CAP_MIC)
+ ucontrol->value.integer.value[0] =
+ (RECMLC_MIC_20DB ==
+ snd_soc_read(codec, ADAU_RECMLC1));
+ else
+ ucontrol->value.integer.value[0] = 0x0;
+
+ ucontrol->value.integer.value[1] = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int adau1361_mic_boost_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct adau1361_priv *adau1361 = codec->private_data;
+ int val = ucontrol->value.integer.value[0];
+ u8 regvalue = 0;
+
+ if (adau1361->in_source & CAP_MIC) {
+ regvalue = (val) ? RECMLC_MIC_20DB : RECMLC_MIC_0DB;
+ if (snd_soc_read(codec, ADAU_RECMLC1) != regvalue) {
+ snd_soc_write(codec, ADAU_RECMLC1, regvalue);
+ snd_soc_write(codec, ADAU_RECMRC1, regvalue);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static const char *adau1361_input_select[] = {"Mic", "Line"};
+static const struct soc_enum adau1361_enums[] = {
+ SOC_ENUM_SINGLE(ADAU_RECMLC1, 0, 2, adau1361_input_select),
+};
+
+static const struct snd_kcontrol_new adau1361_snd_controls[] = {
+SOC_DOUBLE_R("Master Playback Volume", ADAU_DACCTL1,
+ ADAU_DACCTL2, 0, 255, 1),
+SOC_DOUBLE_R("Capture Volume", ADAU_ADCCTL1,
+ ADAU_ADCCTL2, 0, 255, 1),
+SOC_DOUBLE_R("Capture Switch", ADAU_RECMLC0,
+ ADAU_RECMRC0, 0, 1, 0),
+SOC_ENUM_EXT("Capture Source", adau1361_enums[0],
+ adau1361_mux_get, adau1361_mux_put),
+SOC_SINGLE_EXT("Mic Boost (+20dB)", ADAU_RECMLC1, 0, 1, 0,
+ adau1361_mic_boost_get, adau1361_mic_boost_put),
+SOC_DOUBLE_R("Headphone Playback Volume", ADAU_PLBHPVL,
+ ADAU_PLBHPVR, 2, 63, 0),
+SOC_DOUBLE_R("Line Playback Volume", ADAU_PLBLOVL,
+ ADAU_PLBLOVR, 2, 63, 0),
+};
+
+/*
+ * _DAPM_
+ */
+
+static int adau1361_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 reg = 0;
+
+ if (mute) {
+ /* mute inputs */
+ reg = (snd_soc_read(codec, ADAU_RECMLC0) & 0xFE) | 0x0;
+ snd_soc_write(codec, ADAU_RECMLC0, reg);
+ reg = (snd_soc_read(codec, ADAU_RECMRC0) & 0xFE) | 0x0;
+ snd_soc_write(codec, ADAU_RECMRC0, reg);
+ /* mute outputs */
+ reg = (snd_soc_read(codec, ADAU_PLBMLC0) & 0xFE) | 0x1;
+ snd_soc_write(codec, ADAU_PLBMLC0, reg);
+ reg = (snd_soc_read(codec, ADAU_PLBMRC0) & 0xFE) | 0x1;
+ snd_soc_write(codec, ADAU_PLBMRC0, reg);
+
+ } else {
+ /* un-mute outputs, according to the spec,
+ we should enable mixer3 and mixer4 here,
+ but it seems that things are converse here.
+ */
+ reg = (snd_soc_read(codec, ADAU_PLBMLC0) & 0xFE) | 0x0;
+ snd_soc_write(codec, ADAU_PLBMLC0, reg);
+ reg = (snd_soc_read(codec, ADAU_PLBMRC0) & 0xFE) | 0x0;
+ snd_soc_write(codec, ADAU_PLBMRC0, reg);
+ /* un-mute inputs */
+ reg = (snd_soc_read(codec, ADAU_RECMLC0) & 0xFE) | 0x1;
+ snd_soc_write(codec, ADAU_RECMLC0, reg);
+ reg = (snd_soc_read(codec, ADAU_RECMRC0) & 0xFE) | 0x1;
+ snd_soc_write(codec, ADAU_RECMRC0, reg);
+ }
+
+ return 0;
+}
+
+/* Left Mixer */
+static const struct snd_kcontrol_new adau1361_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("LineLeft Bypass Switch", ADAU_PLBLOVL, 1, 1, 0),
+SOC_DAPM_SINGLE("HPLeft Bypass Switch", ADAU_PLBHPVL, 1, 1, 0),
+};
+
+/* Right mixer */
+static const struct snd_kcontrol_new adau1361_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("LineRight Bypass Switch", ADAU_PLBLOVR, 1, 1, 0),
+SOC_DAPM_SINGLE("HPRight Bypass Switch", ADAU_PLBHPVR, 1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget adau1361_dapm_widgets[] = {
+
+SND_SOC_DAPM_MIXER("Left Mixer", ADAU_PLBPWRM, 2, 1, \
+ &adau1361_left_mixer_controls[0], ARRAY_SIZE(adau1361_left_mixer_controls)),
+SND_SOC_DAPM_MIXER("Left Out", ADAU_PLBPWRM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Left Line Mixer", ADAU_PLBMLLO, 0, 0, NULL, 0),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+
+SND_SOC_DAPM_MIXER("Right Mixer", ADAU_PLBPWRM, 3, 1, \
+ &adau1361_right_mixer_controls[0], ARRAY_SIZE(adau1361_right_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right Out", ADAU_PLBPWRM, 1, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Right Line Mixer", ADAU_PLBMRLO, 0, 0, NULL, 0),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("RHPOUT"),
+
+SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_MIXER("DAC Enable Left", ADAU_DACCTL0, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("DAC Enable Right", ADAU_DACCTL0, 1, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("HP Bias Left", ADAU_PLBPWRM, 6, 1, NULL, 0),
+SND_SOC_DAPM_MIXER("HP Bias Right", ADAU_PLBLRMC, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_MIXER("ADC Left", ADAU_ADCCTL0, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("ADC Right", ADAU_ADCCTL0, 1, 0, NULL, 0),
+
+#if !defined(ADAU1361_DIG_MIC)
+SND_SOC_DAPM_MICBIAS("Mic Bias", ADAU_RECMBIA, 0, 0),
+SND_SOC_DAPM_MIXER("Left Mic Mixer", ADAU_RECVLCL, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Right Mic Mixer", ADAU_RECVLCR, 0, 0, NULL, 0),
+#else
+SND_SOC_DAPM_MICBIAS("Mic Bias Left", SND_SOC_NOPM, 1, 0),
+SND_SOC_DAPM_MICBIAS("Mic Bias Right", SND_SOC_NOPM, 1, 0),
+SND_SOC_DAPM_MIXER("Left Mic Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Right Mic Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+#endif
+
+SND_SOC_DAPM_MIXER("Left Input", ADAU_RECPWRM, 1, 1, NULL, 0),
+SND_SOC_DAPM_MIXER("Right Input", ADAU_RECPWRM, 2, 1, NULL, 0),
+
+SND_SOC_DAPM_INPUT("LMICIN"),
+SND_SOC_DAPM_INPUT("RMICIN"),
+SND_SOC_DAPM_INPUT("LLINEIN"),
+SND_SOC_DAPM_INPUT("RLINEIN"),
+};
+
+static const struct snd_soc_dapm_route audio_conns[] = {
+ /* DAC */
+ {"DAC Enable Left", NULL, "DAC"},
+ {"DAC Enable Right", NULL, "DAC"},
+
+ /* mixers */
+ {"Left Mixer", NULL, "DAC Enable Left"},
+ {"Right Mixer", NULL, "DAC Enable Right"},
+
+ /* outputs */
+ {"Left Out", NULL, "Left Mixer"},
+ {"Right Out", NULL, "Right Mixer"},
+
+ /* line Out */
+ {"Left Line Mixer", NULL, "Left Out"},
+ {"Right Line Mixer", NULL, "Right Out"},
+ {"LOUT", "LineLeft Bypass Switch", "Left Line Mixer"},
+ {"ROUT", "LineRight Bypass Switch", "Right Line Mixer"},
+
+ /* headphone out */
+ {"HP Bias Left", NULL, "Left Out"},
+ {"HP Bias Right", NULL, "Right Out"},
+ {"LHPOUT", "HPLeft Bypass Switch", "HP Bias Left"},
+ {"RHPOUT", "HPRight Bypass Switch", "HP Bias Right"},
+
+ /* inputs */
+ {"Left Input", NULL, "LLINEIN"},
+ {"Right Input", NULL, "RLINEIN"},
+ {"ADC Left", NULL, "Left Input"},
+ {"ADC Right", NULL, "Right Input"},
+ {"ADC Left", NULL, "Left Mic Mixer"},
+ {"ADC Right", NULL, "Right Mic Mixer"},
+ {"ADC", NULL, "ADC Left"},
+ {"ADC", NULL, "ADC Right"},
+
+};
+
+static int adau1361_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, adau1361_dapm_widgets,
+ ARRAY_SIZE(adau1361_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_conns, ARRAY_SIZE(audio_conns));
+
+ return 0;
+}
+
+/* PLL dividors */
+struct _pll_div {
+ u32 mclk;
+ u32 pll_freq;
+ u16 den;
+ u16 num;
+ u8 param;
+};
+
+static const struct _pll_div clock_dividers[] = {
+ { 12000000, 45158400, 625, 477, /*44.1kHz*/
+ (PLLCTRL_INTPART_R3|PLLCTRL_INPUT_DIV1|PLLCTRL_TYPE_FRAC) },
+ {12000000, 49152000, 125, 12, /*48kHz*/
+ (PLLCTRL_INTPART_R4|PLLCTRL_INPUT_DIV1|PLLCTRL_TYPE_FRAC) },
+ {12288000, 45158400, 40, 27, /*44.1Khz*/
+ (PLLCTRL_INTPART_R3|PLLCTRL_INPUT_DIV1|PLLCTRL_TYPE_FRAC) },
+ {12288000, 49152000, 0, 0, /*48kHz*/
+ (PLLCTRL_INTPART_R4|PLLCTRL_INPUT_DIV1|PLLCTRL_TYPE_INT) },
+};
+
+static inline int get_pll_settings(int mclk, int pll_out)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(clock_dividers); i++) {
+ if (clock_dividers[i].mclk == mclk
+ && clock_dividers[i].pll_freq == pll_out)
+ return i;
+ }
+ return 0;
+}
+
+static int adau1361_pll_init(struct snd_soc_codec *codec)
+{
+ struct adau1361_priv *adau1361 = codec->private_data;
+ u8 *pll_reg = adau1361->adau1361_pll_reg;
+ int ix = 0;
+
+ /* Init ADAU1361 clocking */
+ snd_soc_write(codec, ADAU_CLKCTRL,
+ (CLKCTRL_SRC_PLL | CLKCTRL_FRQ_1024 | CLKCTRL_DISABLE));
+
+ ix = get_pll_settings(adau1361->sysclk, adau1361->pll_out);
+
+ pll_reg[0] = (clock_dividers[ix].den >> 8);
+ pll_reg[1] = (clock_dividers[ix].den & 0xFF);
+ pll_reg[2] = (clock_dividers[ix].num >> 8);
+ pll_reg[3] = (clock_dividers[ix].num & 0xFF);
+ pll_reg[4] = clock_dividers[ix].param;
+ pll_reg[5] = PLLCTRL_DISABLE;
+ adau1361_write_reg_block(codec, ADAU_PLLCTRL, 6, pll_reg);
+
+ adau1361->pll_enable = 0;
+
+ return 0;
+}
+
+static int adau1361_pll_enable(struct snd_soc_codec *codec, int enable)
+{
+ struct adau1361_priv *adau1361 = codec->private_data;
+ u8 *pll_reg = adau1361->adau1361_pll_reg;
+ int counter = 0;
+
+ if (enable) {
+ pll_reg[5] = PLLCTRL_ENABLE;
+ adau1361_write_reg_block(codec, ADAU_PLLCTRL, 6, pll_reg);
+
+ /* wait for PLL lock*/
+ do {
+ ++counter;
+ schedule_timeout_interruptible(msecs_to_jiffies(1));
+ adau1361_read_reg_block(codec, ADAU_PLLCTRL, 6);
+ } while (0 == (snd_soc_read(codec, ADAU_PLLCTRL + 5) & 0x2)
+ && counter < 20);
+ if (counter >= 20)
+ return -1;
+
+ adau1361->pll_enable = 1;
+
+ /* Init ADAU1361 clocking */
+ snd_soc_write(codec, ADAU_CLKCTRL,
+ (CLKCTRL_SRC_PLL | CLKCTRL_FRQ_1024 | CLKCTRL_ENABLE));
+ }
+
+ return 0;
+
+}
+
+static int adau1361_reg_init(struct snd_soc_codec *codec)
+{
+ struct adau1361_mode_register regdata;
+ struct adau1361_mode_register *registers = 0;
+ int i;
+#ifdef ADAU1361_DIG_MIC
+ int mode = 1;
+#else /* analog mic */
+ int mode = 0;
+#endif
+ adau1361_pll_init(codec);
+ adau1361_pll_enable(codec, 1);
+ /* Load deault regsiter settings */
+ for (i = 0; i < RESET_REGISTER_COUNT; ++i) {
+ regdata = adau1361_reset[i];
+ snd_soc_write(codec, regdata.regaddress, regdata.regvalue);
+ }
+ /* Load mode registers */
+ registers = adau1361_mode_registers[mode];
+ for (i = 0; i < MODE_REGISTER_COUNT; ++i) {
+ regdata = registers[i];
+ snd_soc_write(codec, regdata.regaddress, regdata.regvalue);
+ }
+ /* unmute outputs */
+ snd_soc_write(codec, ADAU_PLBHPVL, DAPM_HP_DEF);
+ snd_soc_write(codec, ADAU_PLBHPVR, DAPM_HP_DEF);
+ snd_soc_write(codec, ADAU_PLBLOVL, DAPM_LINE_DEF);
+ snd_soc_write(codec, ADAU_PLBLOVR, DAPM_LINE_DEF);
+
+ return 0;
+}
+
+struct _srate_set {
+ int fs;
+ u8 reg;
+};
+
+static const struct _srate_set srate_iface[] = {
+ {8000, 0x1},
+ {11025, 0x2},
+ {12000, 0x2},
+ {16000, 0x3},
+ {22050, 0x4},
+ {24000, 0x4},
+ {32000, 0x5},
+ {44100, 0x0},
+ {48000, 0x0},
+ {88200, 0x6},
+ {96000, 0x6},
+};
+
+static int adau1361_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ 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 adau1361_priv *adau1361 = codec->private_data;
+ int rate = params_rate(params);
+ int i;
+
+ /* initialize the PLL */
+ if (adau1361_pll_init(codec) != 0)
+ return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(srate_iface); i++) {
+ if (srate_iface[i].fs == rate) {
+ adau1361->rate_index = i;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int adau1361_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ 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 adau1361_priv *adau1361 = codec->private_data;
+ u8 reg = 0;
+ int ret = 0;
+
+ reg = srate_iface[adau1361->rate_index].reg;
+ ret = adau1361_pll_enable(codec, 1);
+ if (ret)
+ dev_err(codec->dev, "Failed to initialize PLL");
+
+ reg = (snd_soc_read(codec, ADAU_CONVCT0) & 0xF8) | reg;
+ snd_soc_write(codec, ADAU_CONVCT0, reg);
+
+ return ret;
+}
+
+static void adau1361_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ u8 reg;
+
+ if (!codec->active) {
+ reg = snd_soc_read(codec, ADAU_CLKCTRL);
+ snd_soc_write(codec, ADAU_CLKCTRL, reg & ~0x1);
+ }
+}
+
+static int adau1361_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 reg = 0;
+
+ /* set master/slave audio interface */
+ reg = (snd_soc_read(codec, ADAU_SPRTCT0) & 0xFE);
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM: /*master*/
+ reg |= 0x1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS: /*slave*/
+ reg &= ~0x1;
+ break;
+ default:
+ return 0;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ /* TODO: support TDM */
+ default:
+ return 0;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ /* TODO: support signal inversions */
+ default:
+ return 0;
+ }
+
+ /* set I2S iface format*/
+ snd_soc_write(codec, ADAU_SPRTCT0, reg);
+ return 0;
+}
+
+/*
+ * Clock after PLL and dividers
+ */
+static int adau1361_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct adau1361_priv *adau1361 = codec->private_data;
+
+ switch (freq) {
+ case 12000000:
+ adau1361->sysclk = freq;
+ return 0;
+ case 12288000:
+ adau1361->sysclk = freq;
+ return 0;
+ }
+
+ /* supported 12MHz MCLK only for now */
+ return -EINVAL;
+}
+
+static int adau1361_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 adau1361_priv *adau1361 = codec->private_data;
+
+ /* fixed MCLK only supported for now */
+ if (adau1361->sysclk != freq_in)
+ return -EINVAL;
+
+ /* Only update pll when freq changes */
+ if (adau1361->pll_enable && adau1361->pll_out == freq_out)
+ return 0;
+
+ switch (freq_out) {
+ case 45158400:
+ adau1361->pll_out = freq_out;
+ break;
+ case 49152000:
+ adau1361->pll_out = freq_out;
+ break;
+ default:
+ dev_err(codec->dev, "adau1361_set_dai_pll: undefined pll freq:%d", freq_out);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int adau1361_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_write(codec, ADAU_CLKCTRL,
+ (CLKCTRL_SRC_PLL | CLKCTRL_FRQ_1024 | CLKCTRL_DISABLE));
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* everything off, dac mute, inactive */
+ snd_soc_write(codec, ADAU_RECPWRM, RECPWRM_LOW_PWR);
+ snd_soc_write(codec, ADAU_PLBPWRM, PLBPWRM_LOW_PWR);
+ snd_soc_write(codec, ADAU_PLBCTRL, PLBCTRL_POP_OFF);
+ snd_soc_write(codec, ADAU_CLKCTRL,
+ (CLKCTRL_SRC_PLL | CLKCTRL_FRQ_1024 | CLKCTRL_DISABLE));
+ break;
+
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define ADAU1361_RATES (SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000)
+
+#define ADAU1361_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops adau1361_dai_ops = {
+ .hw_params = adau1361_hw_params,
+ .prepare = adau1361_pcm_prepare,
+ .shutdown = adau1361_shutdown,
+ .digital_mute = adau1361_mute,
+ .set_fmt = adau1361_set_dai_fmt,
+ .set_sysclk = adau1361_set_dai_sysclk,
+ .set_pll = adau1361_set_dai_pll,
+};
+
+struct snd_soc_dai adau1361_dai = {
+ .name = "ADAU1361",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ADAU1361_RATES,
+ .formats = ADAU1361_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ADAU1361_RATES,
+ .formats = ADAU1361_FORMATS,
+ },
+ .ops = &adau1361_dai_ops,
+};
+EXPORT_SYMBOL_GPL(adau1361_dai);
+
+static int adau1361_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ adau1361_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static void adau1361_resume_wq_handler(struct work_struct *work)
+{
+ struct adau1361_priv *adau1361 = container_of(work, struct adau1361_priv, resume_work);
+ struct snd_soc_codec *codec = &adau1361->codec;
+ unsigned int i, v;
+
+ adau1361_pll_init(codec);
+ adau1361_pll_enable(codec, 1);
+
+ /* sync reg_cache with the hardware */
+ for (i = ADAU_FIRSTREG; i <= ADAU_LASTREG; ++i) {
+ /* skip over the 6byte PLL control register */
+ if (i >= ADAU_PLLCTRL && i < ADAU_MICCTRL)
+ continue;
+
+ v = snd_soc_read(codec, i);
+ if (snd_soc_write(codec, i, v) != 0) {
+ dev_err(codec->dev, "ERROR WRITING %.4X AT REG %x\n", v, i);
+ return;
+ }
+ }
+
+ snd_soc_write(codec, ADAU_PLBCTRL, PLBCTRL_POP_ON);
+ snd_soc_write(codec, ADAU_RECPWRM, RECPWRM_RUN_PWR);
+ snd_soc_write(codec, ADAU_PLBPWRM, PLBPWRM_RUN_PWR);
+
+ adau1361_set_bias_level(codec, SND_SOC_BIAS_ON);
+
+}
+
+static int adau1361_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct adau1361_priv *adau1361 = codec->private_data;
+
+ adau1361->pdev = pdev;
+ schedule_work(&adau1361->resume_work);
+ return 0;
+}
+
+/*
+ * initialise the adau1361 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int adau1361_register(struct adau1361_priv *adau1361, enum snd_soc_control_type control)
+{
+ struct snd_soc_codec *codec = &adau1361->codec;
+ int ret = 0;
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ codec->name = "adau1361";
+ codec->owner = THIS_MODULE;
+ codec->set_bias_level = adau1361_set_bias_level;
+ codec->dai = &adau1361_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = ADAU_NUMCACHEREG;
+ codec->reg_cache = kzalloc(ADAU_NUMCACHEREG, GFP_KERNEL);
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ ret = snd_soc_codec_set_cache_io(codec, 16, 8, control);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_dai(&adau1361_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ snd_soc_unregister_codec(codec);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void adau1361_unregister(struct adau1361_priv *adau1361)
+{
+ struct snd_soc_codec *codec = &adau1361->codec;
+
+ adau1361_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ kfree(codec->reg_cache);
+ snd_soc_unregister_dai(&adau1361_dai);
+ snd_soc_unregister_codec(codec);
+ kfree(adau1361);
+ adau1361_codec = NULL;
+}
+
+static int adau1361_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ struct adau1361_priv *adau1361;
+ int ret = 0;
+
+ socdev->card->codec = adau1361_codec;
+ codec = adau1361_codec;
+ adau1361 = codec->private_data;
+ adau1361->in_source = CAP_MIC; /*default is mic input*/
+ adau1361->sysclk = ADAU1361_MCLK_RATE;
+ adau1361->pll_out = ADAU1361_PLL_FREQ_48;
+ adau1361->dapm_lineL = DAPM_LINE_DEF;
+ adau1361->dapm_lineR = DAPM_LINE_DEF;
+ adau1361->dapm_hpL = DAPM_HP_DEF;
+ adau1361->dapm_hpR = DAPM_HP_DEF;
+ adau1361->pdev = pdev;
+
+ ret = adau1361_reg_init(codec);
+ if (ret < 0)
+ dev_err(codec->dev, "failed to initialize\n");
+ /* 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, adau1361_snd_controls,
+ ARRAY_SIZE(adau1361_snd_controls));
+ adau1361_add_widgets(codec);
+pcm_err:
+ return ret;
+}
+
+/* remove everything here */
+static int adau1361_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_adau1361 = {
+ .probe = adau1361_probe,
+ .remove = adau1361_remove,
+ .suspend = adau1361_suspend,
+ .resume = adau1361_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_adau1361);
+
+
+static __devinit int adau1361_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct adau1361_priv *adau1361;
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ adau1361 = kzalloc(sizeof(struct adau1361_priv), GFP_KERNEL);
+ if (adau1361 == NULL)
+ return -ENOMEM;
+ codec = &adau1361->codec;
+ codec->private_data = adau1361;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+
+ i2c_set_clientdata(i2c, adau1361);
+ codec->control_data = i2c;
+
+ codec->dev = &i2c->dev;
+ adau1361_codec = codec;
+
+ INIT_WORK(&adau1361->resume_work, adau1361_resume_wq_handler);
+ ret = adau1361_register(adau1361, SND_SOC_I2C);
+ if (ret < 0)
+ dev_err(&i2c->dev, "failed to initialize\n");
+
+ return ret;
+}
+
+static __devexit int adau1361_i2c_remove(struct i2c_client *client)
+{
+ struct adau1361_priv *adau1361 = i2c_get_clientdata(client);
+ adau1361_unregister(adau1361);
+ return 0;
+}
+
+static const struct i2c_device_id adau1361_i2c_id[] = {
+ { "adau1361", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1361_i2c_id);
+
+/* corgi i2c codec control layer */
+static struct i2c_driver adau1361_i2c_driver = {
+ .driver = {
+ .name = "adau1361",
+ .owner = THIS_MODULE,
+ },
+ .probe = adau1361_i2c_probe,
+ .remove = __devexit_p(adau1361_i2c_remove),
+ .id_table = adau1361_i2c_id,
+};
+
+static int __init adau1361_modinit(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&adau1361_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register adau1361 I2C driver: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+module_init(adau1361_modinit);
+
+static void __exit adau1361_exit(void)
+{
+ i2c_del_driver(&adau1361_i2c_driver);
+}
+module_exit(adau1361_exit);
+
+MODULE_DESCRIPTION("ASoC ADAU1361 driver");
+MODULE_AUTHOR("Cliff Cai");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1361.h b/sound/soc/codecs/adau1361.h
new file mode 100644
index 0000000..79cf576
--- /dev/null
+++ b/sound/soc/codecs/adau1361.h
@@ -0,0 +1,264 @@
+/*
+ * header file fortone adau1361 sound chip
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+
+#ifndef __ADAU1361_H__
+#define __ADAU1361_H__
+
+struct adau1361_setup_data {
+ unsigned short i2c_bus;
+ unsigned short i2c_address;
+};
+
+struct adau1361_mode_register {
+ u16 regaddress;
+ u16 regvalue;
+};
+
+#define RESET_REGISTER_COUNT 42
+#define MODE_REGISTER_COUNT 8
+
+#define MASTER_MODE 1
+#ifdef MASTER_MODE
+/* IIS mater mode*/
+#define ADAU_SRPT_CTRL0 0x01
+#else
+/* IIS slave mode*/
+#define ADAU_SRPT_CTRL0 0x00
+#endif
+
+/* adau1361_set_dai_sysclk clk_id */
+#define ADAU1361_MCLK_ID 0
+#define ADAU1361_BCLK_ID 0x33
+
+#define ADAU1361_MCLK_RATE 12288000
+
+#define ADAU1361_PLL_FREQ_441 45158400
+#define ADAU1361_PLL_FREQ_48 49152000
+
+
+/* ADAU1361 control registers */
+#define ADAU_FIRSTREG 0x4000
+
+#define ADAU_CLKCTRL 0x4000
+#define ADAU_PLLCTRL 0x4002
+#define ADAU_MICCTRL 0x4008
+#define ADAU_RECPWRM 0x4009
+#define ADAU_RECMLC0 0x400A
+#define ADAU_RECMLC1 0x400B
+#define ADAU_RECMRC0 0x400C
+#define ADAU_RECMRC1 0x400D
+#define ADAU_RECVLCL 0x400E
+#define ADAU_RECVLCR 0x400F
+
+#define ADAU_RECMBIA 0x4010
+#define ADAU_ALCCTR0 0x4011
+#define ADAU_ALCCTR1 0x4012
+#define ADAU_ALCCTR2 0x4013
+#define ADAU_ALCCTR3 0x4014
+#define ADAU_SPRTCT0 0x4015
+#define ADAU_SPRTCT1 0x4016
+#define ADAU_CONVCT0 0x4017
+#define ADAU_CONVCT1 0x4018
+#define ADAU_ADCCTL0 0x4019
+#define ADAU_ADCCTL1 0x401A
+#define ADAU_ADCCTL2 0x401B
+#define ADAU_PLBMLC0 0x401C
+#define ADAU_PLBMLC1 0x401D
+#define ADAU_PLBMRC0 0x401E
+#define ADAU_PLBMRC1 0x401F
+
+#define ADAU_PLBMLLO 0x4020
+#define ADAU_PLBMRLO 0x4021
+#define ADAU_PLBLRMC 0x4022
+#define ADAU_PLBHPVL 0x4023
+#define ADAU_PLBHPVR 0x4024
+#define ADAU_PLBLOVL 0x4025
+#define ADAU_PLBLOVR 0x4026
+#define ADAU_PLBMNOC 0x4027
+#define ADAU_PLBCTRL 0x4028
+#define ADAU_PLBPWRM 0x4029
+
+#define ADAU_DACCTL0 0x402A
+#define ADAU_DACCTL1 0x402B
+#define ADAU_DACCTL2 0x402C
+#define ADAU_SERPAD0 0x402D
+#define ADAU_COMPAD0 0x402F
+#define ADAU_COMPAD1 0x4030
+#define ADAU_MCLKPAD 0x4031
+
+#define ADAU_LASTREG 0x4031
+
+#define ADAU_NUMCACHEREG 44
+
+/* Register field definitions */
+/* Clock Control */
+#define CLKCTRL_SRC_MCLK 0x0
+#define CLKCTRL_SRC_PLL 0x8
+#define CLKCTRL_FRQ_256 0x0
+#define CLKCTRL_FRQ_512 0x2
+#define CLKCTRL_FRQ_768 0x4
+#define CLKCTRL_FRQ_1024 0x6
+#define CLKCTRL_DISABLE 0x0
+#define CLKCTRL_ENABLE 0x1
+
+/* PLL Control -- 6 bytes*/
+/*Bytes 5-6*/
+#define PLLCTRL_DEN_MSB 0x00
+#define PLLCTRL_DEN_LSB 0x00
+/*Bytes 3-4*/
+#define PLLCTRL_NUM_MSB 0x00
+#define PLLCTRL_NUM_LSB 0x00
+/*Byte 2*/
+#define PLLCTRL_INTPART_R2 0x10
+#define PLLCTRL_INTPART_R3 0x18
+#define PLLCTRL_INTPART_R4 0x20
+#define PLLCTRL_INTPART_R5 0x28
+#define PLLCTRL_INTPART_R6 0x30
+#define PLLCTRL_INTPART_R7 0x38
+#define PLLCTRL_INTPART_R8 0x40
+#define PLLCTRL_INPUT_DIV1 0x00
+#define PLLCTRL_INPUT_DIV2 0x02
+#define PLLCTRL_INPUT_DIV3 0x04
+#define PLLCTRL_INPUT_DIV4 0x06
+#define PLLCTRL_TYPE_INT 0x0
+#define PLLCTRL_TYPE_FRAC 0x1
+/*Byte 1*/
+#define PLLCTRL_DISABLE 0x0
+#define PLLCTRL_ENABLE 0x1
+
+/*ADC*/
+#define ADCCTL_DISABLE_MASK 0xFC
+#define ADCCTL_ENABLE_MASK 0x03
+
+/*MIC*/
+#define RECMBIA_DISABLE 0x00
+#define RECMBIA_ENABLE 0x01
+#define RECVLC_DISABLE_MASK 0xFC
+#define RECVLC_ENABLE_MASK 0x03
+
+#define RECMLC_MIC_0DB 0x08
+#define RECMLC_MIC_20DB 0x10
+#define RECMLC_LINE_0DB 0x05
+
+/* PWN MNGMNT */
+#define RECPWRM_LOW_PWR 0x0E
+#define PLBPWRM_LOW_PWR 0x5C
+#define PLBCTRL_POP_LPWR 0x10
+#define PLBCTRL_POP_OFF 0x06
+#define PLBCTRL_POP_ON 0x00
+#define RECPWRM_RUN_PWR 0x00
+#define PLBPWRM_RUN_PWR 0x03
+#define DAPM_LINE_DEF 0xE6
+#define DAPM_HP_DEF 0xE7
+#define PLB_MUTE_MASK 0x03
+
+#define ADAU1361_BITSFRAM_32 0x4000
+#define ADAU1361_BITSFRAM_48 0x8000
+
+/*playback output control*/
+#define ADAU1361_VOLUME_MASK 0xFC
+#define ADAU1361_VOLUME_BITS 0x2
+#define ADAU1361_MUTE_MASK 0x02
+#define ADAU1361_MUTE_BITS 0x1
+#define ADAU1361_ADVOL_MASK 0xff
+
+/*
+ * Reset Mode - ADC capture/DAC playback
+ * (AInput mixers 0db, AOuput mixers 0db, HP out ON)
+*/
+static struct adau1361_mode_register adau1361_reset[RESET_REGISTER_COUNT] = {
+ /* mute outputs */
+ {ADAU_PLBMNOC, 0xE5},
+ {ADAU_PLBHPVL, 0x01},
+ {ADAU_PLBHPVR, 0x01},
+ {ADAU_PLBLOVL, 0x00},
+ {ADAU_PLBLOVR, 0x00},
+ {ADAU_MICCTRL, 0x00},
+ {ADAU_RECPWRM, 0x00},
+ {ADAU_RECMLC0, 0x01},
+ {ADAU_RECMLC1, RECMLC_MIC_0DB},
+ {ADAU_RECMRC0, 0x01},
+ {ADAU_RECMRC1, RECMLC_MIC_0DB},
+ {ADAU_RECVLCL, 0x82},
+ {ADAU_RECVLCR, 0x82},
+ {ADAU_RECMBIA, RECMBIA_DISABLE},
+ {ADAU_ALCCTR0, 0x00},
+ {ADAU_ALCCTR1, 0x00},
+ {ADAU_ALCCTR2, 0x00},
+ {ADAU_ALCCTR3, 0x1F},
+ {ADAU_SPRTCT0, ADAU_SRPT_CTRL0},
+ {ADAU_SPRTCT1, 0x01}, /* 0x01 = 64bclocks frame */
+ {ADAU_CONVCT0, 0x00},
+ {ADAU_CONVCT1, 0x00},
+ {ADAU_ADCCTL0, 0x00},
+ {ADAU_ADCCTL1, 0x00},
+ {ADAU_ADCCTL2, 0x00},
+ {ADAU_PLBMLC0, 0x21},
+ {ADAU_PLBMLC1, 0x00},
+ {ADAU_PLBMRC0, 0x41},
+ {ADAU_PLBMRC1, 0x00},
+ {ADAU_PLBMLLO, 0x03},
+ {ADAU_PLBMRLO, 0x09},
+ {ADAU_PLBLRMC, 0x01},
+ {ADAU_PLBCTRL, 0x00},
+ {ADAU_PLBPWRM, 0x00},
+ {ADAU_DACCTL0, 0x03},
+ {ADAU_DACCTL1, 0x00},
+ {ADAU_DACCTL2, 0x00},
+ {ADAU_SERPAD0, 0xAA},
+ {ADAU_COMPAD0, 0xAA},
+ {ADAU_COMPAD1, 0x00},
+ {ADAU_MCLKPAD, 0x0A},
+};
+
+/*
+ * Default Mode
+ * Analog microphones, ADC capture/DAC playback
+ * (AInput mixers ON, AOuput mixers ON, HP out ON)
+*/
+static struct adau1361_mode_register adau1361_mode0[MODE_REGISTER_COUNT] = {
+ /* mute outputs */
+ {ADAU_PLBHPVL, 0x03},
+ {ADAU_PLBHPVR, 0x03},
+ {ADAU_PLBLOVL, 0x02},
+ {ADAU_PLBLOVR, 0x02},
+ {ADAU_PLBMNOC, 0xE5},
+ /*analog mic*/
+ {ADAU_RECVLCL, 0x42},
+ {ADAU_RECVLCR, 0x42},
+ {ADAU_MICCTRL, 0x00},
+};
+
+/*
+ * Digital Microphone mode,
+ * IIS Master, ADC capture/DAC playback
+ * (AInput mixers OFF, AOuput mixers ON, HP out ON)
+ */
+static struct adau1361_mode_register adau1361_mode1[MODE_REGISTER_COUNT] = {
+ /* mute outputs */
+ {ADAU_PLBHPVL, 0x03},
+ {ADAU_PLBHPVR, 0x03},
+ {ADAU_PLBLOVL, 0x02},
+ {ADAU_PLBLOVR, 0x02},
+ {ADAU_PLBMNOC, 0xE5},
+ /*digital mic*/
+ {ADAU_RECVLCL, 0x00},
+ {ADAU_RECVLCR, 0x00},
+ {ADAU_MICCTRL, 0x20},
+};
+
+static struct adau1361_mode_register *adau1361_mode_registers[] = {
+ adau1361_mode0,
+ adau1361_mode1,
+};
+
+extern struct snd_soc_dai adau1361_dai;
+extern struct snd_soc_codec_device soc_codec_dev_adau1361;
+
+#endif
--
1.7.2
More information about the Alsa-devel
mailing list