All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/6] ASoC: add ADAU1361 codec driver
@ 2010-08-07 20:28 Mike Frysinger
       [not found] ` <1281212905-12957-1-git-send-email-vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
                   ` (4 more replies)
  0 siblings, 5 replies; 18+ messages in thread
From: Mike Frysinger @ 2010-08-07 20:28 UTC (permalink / raw)
  To: alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Mark Brown
  Cc: uclinux-dist-devel-ZG0+EudsQA8dtHy/vicBwGD2FQJk+8+b, Cliff Cai

From: Cliff Cai <cliff.cai-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org>

Signed-off-by: Cliff Cai <cliff.cai-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Mike Frysinger <vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 2/6] ASoC: Blackfin: new machine driver for ADAU1361 codecs
       [not found] ` <1281212905-12957-1-git-send-email-vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
@ 2010-08-07 20:28   ` Mike Frysinger
  2010-08-07 21:08     ` Mark Brown
  2010-08-07 20:28   ` [PATCH 5/6] ASoC: new ADAU1761 codec driver Mike Frysinger
  2010-08-07 20:28   ` [PATCH 6/6] ASoC: Blackfin: new machine driver for ADAU1761 codecs Mike Frysinger
  2 siblings, 1 reply; 18+ messages in thread
From: Mike Frysinger @ 2010-08-07 20:28 UTC (permalink / raw)
  To: alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Mark Brown
  Cc: uclinux-dist-devel-ZG0+EudsQA8dtHy/vicBwGD2FQJk+8+b, Cliff Cai

From: Cliff Cai <cliff.cai-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org>

Signed-off-by: Cliff Cai <cliff.cai-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Mike Frysinger <vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
---
 sound/soc/blackfin/Kconfig          |    9 ++
 sound/soc/blackfin/Makefile         |    2 +
 sound/soc/blackfin/bf5xx-adau1361.c |  144 +++++++++++++++++++++++++++++++++++
 3 files changed, 155 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/blackfin/bf5xx-adau1361.c

diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
index b73ee38..56eb6c6 100644
--- a/sound/soc/blackfin/Kconfig
+++ b/sound/soc/blackfin/Kconfig
@@ -32,6 +32,15 @@ config SND_BFIN_AD73311_SE
 	  Enter the GPIO used to control AD73311's SE pin. Acceptable
 	  values are 0 to 7
 
+config SND_BF5XX_SOC_ADAU1361
+	tristate "SoC ADAU1361 Audio support"
+	depends on SND_BF5XX_I2S
+	select SND_BF5XX_SOC_I2S
+	select SND_SOC_ADAU1361
+	select I2C
+	help
+	  Say Y if you want to add support for ADAU1361 SoC audio.
+
 config SND_BF5XX_SOC_ADAV80X
 	tristate "SoC ADAV801/3 Audio support"
 	depends on SND_BF5XX_I2S && (SPI_MASTER || I2C)
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile
index 872dac8..9de1fc9 100644
--- a/sound/soc/blackfin/Makefile
+++ b/sound/soc/blackfin/Makefile
@@ -21,6 +21,7 @@ snd-ad1980-objs := bf5xx-ad1980.o
 snd-ssm2602-objs := bf5xx-ssm2602.o
 snd-ad73311-objs := bf5xx-ad73311.o
 snd-ad193x-objs := bf5xx-ad193x.o
+snd-adau1361-objs := bf5xx-adau1361.o
 snd-adav80x-objs := bf5xx-adav80x.o
 
 obj-$(CONFIG_SND_BF5XX_SOC_AD1836) += snd-ad1836.o
@@ -28,4 +29,5 @@ obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o
 obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
 obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
 obj-$(CONFIG_SND_BF5XX_SOC_AD193X) += snd-ad193x.o
+obj-$(CONFIG_SND_BF5XX_SOC_ADAU1361) += snd-adau1361.o
 obj-$(CONFIG_SND_BF5XX_SOC_ADAV80X) += snd-adav80x.o
diff --git a/sound/soc/blackfin/bf5xx-adau1361.c b/sound/soc/blackfin/bf5xx-adau1361.c
new file mode 100644
index 0000000..1748738
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-adau1361.c
@@ -0,0 +1,144 @@
+/*
+ * board driver for adau1361 sound chip
+ *
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+
+#include <asm/dma.h>
+#include <asm/portmux.h>
+#include <linux/gpio.h>
+#include "../codecs/adau1361.h"
+#include "bf5xx-sport.h"
+#include "bf5xx-i2s-pcm.h"
+#include "bf5xx-i2s.h"
+
+static int bf5xx_adau1361_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	unsigned int pll_in = 0, pll_out = 0;
+	int ret = 0;
+
+	switch (params_rate(params)) {
+	case 11025:
+	case 22050:
+	case 44100:
+	case 88200:
+		pll_out = ADAU1361_PLL_FREQ_441;
+		break;
+	case 8000:
+	case 12000:
+	case 16000:
+	case 24000:
+	case 32000:
+	case 48000:
+	case 64000:
+	case 96000:
+		pll_out = ADAU1361_PLL_FREQ_48;
+		break;
+	default:
+		pll_out = ADAU1361_PLL_FREQ_441;
+		break;
+	}
+
+	/* set codec DAI configuration */
+	ret = codec_dai->ops->set_fmt(codec_dai,
+		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+		SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set cpu DAI configuration */
+	ret = cpu_dai->ops->set_fmt(cpu_dai,
+		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+		SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set the codec system clock for DAC and ADC */
+	pll_in = ADAU1361_MCLK_RATE; /* fixed rate MCLK */
+	ret = codec_dai->ops->set_sysclk(codec_dai, ADAU1361_MCLK_ID, pll_in,
+		SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	/* codec PLL input is PCLK/4 */
+	ret = codec_dai->ops->set_pll(codec_dai, 0, 0, pll_in, pll_out);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct snd_soc_ops bf5xx_adau1361_ops = {
+	.hw_params = bf5xx_adau1361_hw_params,
+};
+
+static struct snd_soc_dai_link bf5xx_adau1361_dai = {
+	.name = "adau1361",
+	.stream_name = "adau1361",
+	.cpu_dai = &bf5xx_i2s_dai,
+	.codec_dai = &adau1361_dai,
+	.ops = &bf5xx_adau1361_ops,
+};
+
+static struct snd_soc_card bf5xx_adau1361 = {
+	.name = "bf5xx_adau1361",
+	.platform = &bf5xx_i2s_soc_platform,
+	.dai_link = &bf5xx_adau1361_dai,
+	.num_links = 1,
+};
+
+static struct snd_soc_device bf5xx_adau1361_snd_devdata = {
+	.card = &bf5xx_adau1361,
+	.codec_dev = &soc_codec_dev_adau1361,
+};
+
+static struct platform_device *bf5xx_adau1361_snd_device;
+
+static int __init bf5xx_adau1361_init(void)
+{
+	int ret;
+
+	pr_debug("%s enter\n", __func__);
+	bf5xx_adau1361_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!bf5xx_adau1361_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(bf5xx_adau1361_snd_device,
+				&bf5xx_adau1361_snd_devdata);
+	bf5xx_adau1361_snd_devdata.dev = &bf5xx_adau1361_snd_device->dev;
+	ret = platform_device_add(bf5xx_adau1361_snd_device);
+
+	if (ret)
+		platform_device_put(bf5xx_adau1361_snd_device);
+
+	return ret;
+}
+module_init(bf5xx_adau1361_init);
+
+static void __exit bf5xx_adau1361_exit(void)
+{
+	pr_debug("%s enter\n", __func__);
+	platform_device_unregister(bf5xx_adau1361_snd_device);
+}
+module_exit(bf5xx_adau1361_exit);
+
+/* Module information */
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("ALSA SoC adau1361");
+MODULE_LICENSE("GPL");
-- 
1.7.2

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 3/6] ASoC: new ADAU1381 codec driver
  2010-08-07 20:28 [PATCH 1/6] ASoC: add ADAU1361 codec driver Mike Frysinger
       [not found] ` <1281212905-12957-1-git-send-email-vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
@ 2010-08-07 20:28 ` Mike Frysinger
  2010-08-07 21:43   ` Mark Brown
  2010-08-07 20:28 ` [PATCH 4/6] ASoC: Blackfin: new machine driver for ADAU1381 codecs Mike Frysinger
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 18+ messages in thread
From: Mike Frysinger @ 2010-08-07 20:28 UTC (permalink / raw)
  To: alsa-devel, Mark Brown; +Cc: uclinux-dist-devel, Cliff Cai

From: Cliff Cai <cliff.cai@analog.com>

Signed-off-by: Cliff Cai <cliff.cai@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 sound/soc/codecs/Kconfig    |    4 +
 sound/soc/codecs/Makefile   |    2 +
 sound/soc/codecs/adau1381.c |  842 +++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/adau1381.h |  222 ++++++++++++
 4 files changed, 1070 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/codecs/adau1381.c
 create mode 100644 sound/soc/codecs/adau1381.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index f37d5f4..e7195ca 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -17,6 +17,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_AD1980 if SND_SOC_AC97_BUS
 	select SND_SOC_AD73311 if I2C
 	select SND_SOC_ADAU1361 if I2C
+	select SND_SOC_ADAU1381 if I2C
 	select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI
 	select SND_SOC_ADS117X
 	select SND_SOC_AK4104 if SPI_MASTER
@@ -108,6 +109,9 @@ config SND_SOC_AD73311
 config SND_SOC_ADAU1361
 	tristate
 
+config SND_SOC_ADAU1381
+	tristate
+
 config SND_SOC_ADAV80X
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index d05b672..887ff5d 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -4,6 +4,7 @@ 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-adau1381-objs := adau1381.o
 snd-soc-adav80x-objs := adav80x.o
 snd-soc-ads117x-objs := ads117x.o
 snd-soc-ak4104-objs := ak4104.o
@@ -72,6 +73,7 @@ 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_ADAU1381)	+= snd-soc-adau1381.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/adau1381.c b/sound/soc/codecs/adau1381.c
new file mode 100644
index 0000000..359886d
--- /dev/null
+++ b/sound/soc/codecs/adau1381.c
@@ -0,0 +1,842 @@
+/*
+ * Driver for ADAU1381 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 "adau1381.h"
+
+#define AUDIO_NAME "adau1381"
+#define ADAU1381_VERSION "0.1"
+
+#define CAP_MIC  1
+#define CAP_LINE 2
+#define CAPTURE_SOURCE_NUMBER 2
+#define ADAU1381_DIG_MIC 0
+
+struct snd_soc_codec_device soc_codec_dev_adau1381;
+static struct snd_soc_codec *adau1381_codec;
+/* codec private data */
+struct adau1381_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 adau1381_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 adau1381_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 ADAU1381 register (6byte pll reg)
+ */
+static int adau1381_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++)
+		adau1381_write_reg_cache(codec, reg+i, (unsigned int)buf[i]);
+
+	return 0;
+}
+
+/*
+ * write a multibyte ADAU1381 register (6byte pll reg)
+ */
+static int adau1381_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;
+	}
+}
+
+static const struct snd_kcontrol_new adau1381_snd_controls[] = {
+SOC_DOUBLE_R("Master Playback Volume", ADAU_LDACATT-ADAU_FIRSTREG,
+	ADAU_RDACATT-ADAU_FIRSTREG, 0, 255, 1),
+SOC_DOUBLE_R("Capture Volume", ADAU_LADCATT-ADAU_FIRSTREG,
+	ADAU_RADCATT-ADAU_FIRSTREG, 0, 255, 1),
+};
+
+/*
+ * _DAPM_
+ */
+
+static const struct snd_soc_dapm_widget adau1381_dapm_widgets[] = {
+
+SND_SOC_DAPM_MIXER("Left Out", ADAU_PLBPWRM-ADAU_FIRSTREG, 0, 0, NULL, 0),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+
+SND_SOC_DAPM_MIXER("Right Out", ADAU_PLBPWRM-ADAU_FIRSTREG, 1, 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("HP Bias Left", ADAU_PLBPWRM-ADAU_FIRSTREG, 6, 1, NULL, 0),
+
+SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_MIXER("ADC Left", ADAU_ADCCTL0-ADAU_FIRSTREG, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("ADC Right", ADAU_ADCCTL0-ADAU_FIRSTREG, 1, 0, NULL, 0),
+
+#if !defined(ADAU1381_DIG_MIC)
+SND_SOC_DAPM_MICBIAS("Mic Bias", ADAU_RECMBIA-ADAU_FIRSTREG, 0, 0),
+SND_SOC_DAPM_MIXER("Left Mic Mixer", ADAU_RECVLCL-ADAU_FIRSTREG, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Right Mic Mixer", ADAU_RECVLCR-ADAU_FIRSTREG, 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-ADAU_FIRSTREG, 1, 1, NULL, 0),
+SND_SOC_DAPM_MIXER("Right Input", ADAU_RECPWRM-ADAU_FIRSTREG, 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"},
+	{"Mic Bias", NULL, "LMICIN"},
+	{"Mic Bias", NULL, "RMICIN"},
+	{"Left Mic Mixer", NULL, "Mic Bias"},
+	{"Right Mic Mixer", NULL, "Mic Bias"},
+	{"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 adau1381_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, adau1381_dapm_widgets,
+				  ARRAY_SIZE(adau1381_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 adau1381_pll_init(struct snd_soc_codec *codec)
+{
+	struct adau1381_priv *adau1381 = codec->private_data;
+	u8 *pll_reg = adau1381->adau1381_pll_reg;
+	int ix = 0;
+
+	/* Init ADAU1381 clocking */
+	snd_soc_write(codec, ADAU_CLKCTRL,
+		(CLKCTRL_SRC_PLL | CLKCTRL_FRQ_1024 | CLKCTRL_DISABLE));
+
+	ix = get_pll_settings(adau1381->sysclk, adau1381->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;
+	adau1381_write_reg_block(codec, ADAU_PLLCTRL, 6, pll_reg);
+
+	adau1381->pll_enable = 0;
+
+	return 0;
+}
+
+static int adau1381_pll_enable(struct snd_soc_codec *codec, int enable)
+{
+	struct adau1381_priv *adau1381 = codec->private_data;
+	u8 *pll_reg = adau1381->adau1381_pll_reg;
+	int counter = 0;
+
+	if (enable) {
+		pll_reg[5]  = PLLCTRL_ENABLE;
+		adau1381_write_reg_block(codec, ADAU_PLLCTRL, 6, pll_reg);
+
+		/* wait for PLL lock*/
+		do {
+			++counter;
+			schedule_timeout_interruptible(msecs_to_jiffies(1));
+			adau1381_read_reg_block(codec, ADAU_PLLCTRL, 6);
+		} while (0 == (snd_soc_read(codec, ADAU_PLLCTRL + 5) & 0x2)
+			&& counter < 20);
+		if (counter >= 20)
+			return -1;
+
+		adau1381->pll_enable = 1;
+
+		/* Init ADAU1381 clocking */
+		snd_soc_write(codec, ADAU_CLKCTRL,
+			(CLKCTRL_SRC_PLL | CLKCTRL_FRQ_1024 | CLKCTRL_ENABLE));
+	}
+
+	return 0;
+
+}
+
+static int adau1381_reg_init(struct snd_soc_codec *codec)
+{
+	struct adau1381_mode_register regdata;
+	struct adau1381_mode_register *registers = 0;
+	int i;
+#ifdef ADAU1381_DIG_MIC
+	int mode = 1;
+#else /* analog mic */
+	int mode = 0;
+#endif
+
+	/* Load deault regsiter settings */
+	for (i = 0; i < RESET_REGISTER_COUNT; ++i) {
+		regdata = adau1381_reset[i];
+		snd_soc_write(codec, regdata.regaddress, regdata.regvalue);
+	}
+	/* Load mode registers */
+	registers = adau1381_mode_registers[mode];
+	for (i = 0; i < MODE_REGISTER_COUNT; ++i) {
+		regdata = registers[i];
+		snd_soc_write(codec, regdata.regaddress, regdata.regvalue);
+	}
+
+	snd_soc_write(codec, ADAU_SPRTCT1, 0x00);
+
+
+
+	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 const struct _srate_set erate_iface[] = {
+	{8000, 0x6},
+	{11025, 0x5},
+	{12000, 0x5},
+	{16000, 0x4},
+	{22050, 0x3},
+	{24000, 0x3},
+	{32000, 0x2},
+	{44100, 0x1},
+	{48000, 0x1},
+	{88200, 0x0},
+	{96000, 0x0},
+};
+
+static int adau1381_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 adau1381_priv *adau1381 = codec->private_data;
+	int rate = params_rate(params);
+	int i;
+
+	/* initialize the PLL */
+	if (adau1381_pll_init(codec) != 0)
+		return -EINVAL;
+	for (i = 0; i < ARRAY_SIZE(srate_iface); i++) {
+		if (srate_iface[i].fs == rate) {
+			adau1381->rate_index = i;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int adau1381_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 adau1381_priv *adau1381 = codec->private_data;
+	u8 reg = 0;
+	int ret = 0;
+
+	reg = srate_iface[adau1381->rate_index].reg;
+	ret = adau1381_pll_enable(codec, 1);
+	if (ret)
+		dev_err(codec->dev, "Failed to initialize PLL");
+	snd_soc_write(codec, ADAU_DIGPWR0, 0xF3);
+	snd_soc_write(codec, ADAU_DIGPWR0, 0x0D);
+	reg = (snd_soc_read(codec, ADAU_CONVCT0) & 0xF8) | reg;
+	snd_soc_write(codec, ADAU_CONVCT0, reg);
+	reg = (snd_soc_read(codec, ADAU_SAMRATE)) | reg;
+	snd_soc_write(codec, ADAU_SAMRATE, reg);
+
+	reg = (snd_soc_read(codec, ADAU_FRMRATE)) | reg;
+	snd_soc_write(codec, ADAU_FRMRATE, reg);
+	snd_soc_write(codec, ADAU_ENGIRUN, 0x01);
+
+	return ret;
+}
+
+static void adau1381_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;
+
+	snd_soc_write(codec, ADAU_DIGPWR0, 0x0);
+	snd_soc_write(codec, ADAU_DIGPWR0, 0x0);
+	reg = snd_soc_read(codec, ADAU_CLKCTRL);
+	snd_soc_write(codec, ADAU_CLKCTRL, reg & ~0x1);
+	snd_soc_write(codec, ADAU_FRMRATE, 0x7F);
+	msleep(3);
+	snd_soc_write(codec, ADAU_ENGIRUN, 0x0);
+
+}
+
+static int adau1381_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 adau1381_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+		int clk_id, int source, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct adau1381_priv *adau1381 = codec->private_data;
+
+	switch (freq) {
+	case 12000000:
+		adau1381->sysclk = freq;
+		return 0;
+	case 12288000:
+		adau1381->sysclk = freq;
+		return 0;
+	}
+
+	/* supported 12MHz MCLK only for now */
+	return -EINVAL;
+}
+
+static int adau1381_set_dai_pll(struct snd_soc_dai *codec_dai,
+		int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct adau1381_priv *adau1381 = codec->private_data;
+
+	/* fixed MCLK only supported for now */
+	if (adau1381->sysclk != freq_in)
+		return -EINVAL;
+
+	/* Only update pll when freq changes */
+	if (adau1381->pll_enable && adau1381->pll_out == freq_out)
+		return 0;
+
+	switch (freq_out) {
+	case 45158400:
+		adau1381->pll_out = freq_out;
+		break;
+	case 49152000:
+		adau1381->pll_out = freq_out;
+		break;
+	default:
+		dev_err(codec->dev, "adau1381_set_dai_pll: undefined pll freq:%d", freq_out);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+static int adau1381_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_CLKCTRL,
+			(CLKCTRL_SRC_PLL | CLKCTRL_FRQ_1024 | CLKCTRL_DISABLE));
+		break;
+
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define ADAU1381_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 ADAU1381_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 adau1381_dai_ops = {
+	.hw_params	= adau1381_hw_params,
+	.prepare	= adau1381_pcm_prepare,
+	.shutdown	= adau1381_shutdown,
+	.set_fmt	= adau1381_set_dai_fmt,
+	.set_sysclk	= adau1381_set_dai_sysclk,
+	.set_pll	= adau1381_set_dai_pll,
+};
+
+struct snd_soc_dai adau1381_dai = {
+	.name = "ADAU1381",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = ADAU1381_RATES,
+		.formats = ADAU1381_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = ADAU1381_RATES,
+		.formats = ADAU1381_FORMATS,
+	},
+	.ops = &adau1381_dai_ops,
+};
+EXPORT_SYMBOL_GPL(adau1381_dai);
+
+static int adau1381_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;
+
+	adau1381_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static void adau1381_resume_wq_handler(struct work_struct *work)
+{
+	struct adau1381_priv *adau1381 = container_of(work, struct adau1381_priv, resume_work);
+	struct snd_soc_codec *codec = &adau1381->codec;
+	unsigned int i, v;
+
+	adau1381_pll_init(codec);
+	adau1381_pll_enable(codec, 1);
+
+	/* sync reg_cache with the hardware */
+	for (i = ADAU_FIRSTREG; i <= ADAU_LASTREG; ++i) {
+
+		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_RECPWRM, RECPWRM_RUN_PWR);
+	snd_soc_write(codec, ADAU_PLBPWRM, PLBPWRM_RUN_PWR);
+
+	adau1381_set_bias_level(codec, SND_SOC_BIAS_ON);
+
+}
+
+static int adau1381_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	struct adau1381_priv *adau1381 = codec->private_data;
+
+	adau1381->pdev = pdev;
+	schedule_work(&adau1381->resume_work);
+	return 0;
+}
+
+/*
+ * initialise the adau1381 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int adau1381_register(struct adau1381_priv *adau1381, enum snd_soc_control_type control)
+{
+	struct snd_soc_codec *codec = &adau1381->codec;
+	int ret = 0;
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+	codec->name = "adau1381";
+	codec->owner = THIS_MODULE;
+	codec->set_bias_level = adau1381_set_bias_level;
+	codec->dai = &adau1381_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(&adau1381_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 adau1381_unregister(struct adau1381_priv *adau1381)
+{
+	struct snd_soc_codec *codec = &adau1381->codec;
+
+	adau1381_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	kfree(codec->reg_cache);
+	snd_soc_unregister_dai(&adau1381_dai);
+	snd_soc_unregister_codec(codec);
+	kfree(adau1381);
+	adau1381_codec = NULL;
+}
+
+static int adau1381_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	struct adau1381_priv *adau1381;
+	int ret = 0;
+
+	socdev->card->codec = adau1381_codec;
+	codec = adau1381_codec;
+	adau1381 = codec->private_data;
+	adau1381->in_source = CAP_MIC; /*default is mic input*/
+	adau1381->sysclk = ADAU1381_MCLK_RATE;
+	adau1381->pll_out = ADAU1381_PLL_FREQ_48;
+	adau1381->dapm_lineL = DAPM_LINE_DEF;
+	adau1381->dapm_lineR = DAPM_LINE_DEF;
+	adau1381->dapm_hpL = DAPM_HP_DEF;
+	adau1381->dapm_hpR = DAPM_HP_DEF;
+	adau1381->pdev = pdev;
+
+	ret = adau1381_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, adau1381_snd_controls,
+			     ARRAY_SIZE(adau1381_snd_controls));
+	adau1381_add_widgets(codec);
+pcm_err:
+	return ret;
+}
+
+/* remove everything here */
+static int adau1381_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_adau1381 = {
+	.probe =	adau1381_probe,
+	.remove =	adau1381_remove,
+	.suspend =	adau1381_suspend,
+	.resume =	adau1381_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_adau1381);
+
+
+static __devinit int adau1381_i2c_probe(struct i2c_client *i2c,
+			      const struct i2c_device_id *id)
+{
+	struct adau1381_priv *adau1381;
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	adau1381 = kzalloc(sizeof(struct adau1381_priv), GFP_KERNEL);
+	if (adau1381 == NULL)
+		return -ENOMEM;
+	codec = &adau1381->codec;
+	codec->private_data = adau1381;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+
+	i2c_set_clientdata(i2c, adau1381);
+	codec->control_data = i2c;
+
+	codec->dev = &i2c->dev;
+	adau1381_codec = codec;
+
+	INIT_WORK(&adau1381->resume_work, adau1381_resume_wq_handler);
+	ret = adau1381_register(adau1381, SND_SOC_I2C);
+	if (ret < 0)
+		dev_err(&i2c->dev, "failed to initialize\n");
+
+	return ret;
+}
+
+static __devexit int adau1381_i2c_remove(struct i2c_client *client)
+{
+	struct adau1381_priv *adau1381 = i2c_get_clientdata(client);
+	adau1381_unregister(adau1381);
+	return 0;
+}
+
+static const struct i2c_device_id adau1381_i2c_id[] = {
+	{ "adau1381", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, adau1381_i2c_id);
+
+/* corgi i2c codec control layer */
+static struct i2c_driver adau1381_i2c_driver = {
+	.driver = {
+		.name = "adau1381",
+		.owner = THIS_MODULE,
+	},
+	.probe    = adau1381_i2c_probe,
+	.remove   = __devexit_p(adau1381_i2c_remove),
+	.id_table = adau1381_i2c_id,
+};
+
+static int __init adau1381_modinit(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&adau1381_i2c_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register adau1381 I2C driver: %d\n",
+		       ret);
+	}
+
+	return ret;
+}
+module_init(adau1381_modinit);
+
+static void __exit adau1381_exit(void)
+{
+	i2c_del_driver(&adau1381_i2c_driver);
+}
+module_exit(adau1381_exit);
+
+MODULE_DESCRIPTION("ASoC ADAU1381 driver");
+MODULE_AUTHOR("Cliff Cai");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1381.h b/sound/soc/codecs/adau1381.h
new file mode 100644
index 0000000..e11c5cd
--- /dev/null
+++ b/sound/soc/codecs/adau1381.h
@@ -0,0 +1,222 @@
+/*
+ * header file fortone adau1381 sound chip
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+
+#ifndef __ADAU1381_H__
+#define __ADAU1381_H__
+
+struct adau1381_setup_data {
+	unsigned short i2c_bus;
+	unsigned short i2c_address;
+};
+
+struct adau1381_mode_register {
+	u16  regaddress;
+	u16  regvalue;
+};
+
+#define RESET_REGISTER_COUNT 40
+#define MODE_REGISTER_COUNT 1
+
+#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
+
+/* adau1381_set_dai_sysclk clk_id */
+#define ADAU1381_MCLK_ID	0
+#define ADAU1381_BCLK_ID	0x33
+
+#define ADAU1381_MCLK_RATE	12288000
+
+#define ADAU1381_PLL_FREQ_441	45158400
+#define ADAU1381_PLL_FREQ_48	49152000
+
+
+/* ADAU1381 control registers */
+#define ADAU_FIRSTREG	0x4000
+
+#define ADAU_CLKCTRL	0x4000
+#define ADAU_RGUCTRL	0x4001
+#define ADAU_PLLCTRL	0x4002
+#define ADAU_RECCTRL	0x4008
+#define ADAU_RECPWRM	0x4009
+#define ADAU_RECGAIL	0x400E
+#define ADAU_RECGAIR	0x400F
+#define ADAU_RECMBIA	0x4010
+#define ADAU_SPRTCT0	0x4015
+#define ADAU_SPRTCT1	0x4016
+#define ADAU_CONVCT0	0x4017
+#define ADAU_CONVCT1	0x4018
+#define ADAU_ADCCTL0	0x4019
+#define ADAU_LADCATT	0x401A
+#define ADAU_RADCATT	0x401B
+#define ADAU_PLMLCTL	0x401C
+#define ADAU_PLMRCTL	0x401E
+#define ADAU_PLBMCTL	0x401F
+#define ADAU_PLBCAMP	0x4020
+#define ADAU_RLOMUTE	0x4025
+#define ADAU_LLOMUTE	0x4026
+#define ADAU_PLSPCTL	0x4027
+#define ADAU_ZXDETCT	0x4028
+#define ADAU_PLBPWRM	0x4029
+#define ADAU_DACCTRL	0x402A
+#define ADAU_LDACATT	0x402B
+#define ADAU_RDACATT	0x402C
+#define ADAU_SERPAD0	0x402D
+#define ADAU_SERPAD1	0x402E
+#define ADAU_COMPAD0	0x402F
+#define ADAU_COMPAD1	0x4030
+#define ADAU_MCKOCTL	0x4031
+#define ADAU_DIGPWR0	0x4080
+#define ADAU_DIGPWR1	0x4081
+#define ADAU_FRMRATE	0x40EB
+#define ADAU_INPRCON	0x40F2
+#define ADAU_OUPRCON	0x40F3
+#define ADAU_PINCONF	0x40F4
+#define ADAU_ENGIRUN	0x40F6
+#define ADAU_SAMRATE	0x40F8
+
+
+#define ADAU_LASTREG	0x40F8
+
+#define ADAU_NUMCACHEREG	40
+
+/* 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 ADAU1381_BITSFRAM_32	0x4000
+#define ADAU1381_BITSFRAM_48	0x8000
+
+/*playback output control*/
+#define ADAU1381_VOLUME_MASK 0xFC
+#define ADAU1381_VOLUME_BITS 0x2
+#define ADAU1381_MUTE_MASK 0x02
+#define ADAU1381_MUTE_BITS 0x1
+#define ADAU1381_ADVOL_MASK 0xff
+
+/*
+ * Reset Mode - ADC capture/DAC playback
+ * (AInput mixers 0db, AOuput mixers 0db, HP out ON)
+*/
+static struct adau1381_mode_register adau1381_reset[RESET_REGISTER_COUNT] = {
+	/* mute outputs */
+	{ADAU_RGUCTRL, 0x00},
+	{ADAU_RECCTRL, 0x00},
+	{ADAU_RECPWRM, 0x00},
+	{ADAU_RECGAIL, 0x07},
+	{ADAU_RECGAIR, 0x07},
+	{ADAU_RECMBIA, RECMBIA_DISABLE},
+	{ADAU_SPRTCT0, ADAU_SRPT_CTRL0},
+	{ADAU_SPRTCT1, 0x21}, /*0x21 = 32bclocks frame, 0x41 = 48*/
+	{ADAU_CONVCT0, 0x00},
+	{ADAU_CONVCT1, 0x00},
+	{ADAU_ADCCTL0, 0x00},
+	{ADAU_LADCATT, 0x00},
+	{ADAU_RADCATT, 0x00},
+	{ADAU_PLMLCTL, 0x20},
+	{ADAU_PLMRCTL, 0x20},
+	{ADAU_PLBMCTL, 0x00},
+	{ADAU_PLBCAMP, 0x00},
+	{ADAU_RLOMUTE, 0x02},
+	{ADAU_LLOMUTE, 0x02},
+	{ADAU_PLSPCTL, 0x01},
+	{ADAU_ZXDETCT, 0x01},
+	{ADAU_PLBPWRM, 0x00},
+	{ADAU_DACCTRL, 0x03},
+	{ADAU_LDACATT, 0x00},
+	{ADAU_RDACATT, 0x00},
+	{ADAU_SERPAD0, 0xAA},
+	{ADAU_SERPAD1, 0x00},
+	{ADAU_COMPAD0, 0xAA},
+	{ADAU_COMPAD1, 0x00},
+	{ADAU_MCKOCTL, 0x00},
+};
+
+static struct adau1381_mode_register adau1381_mode0[MODE_REGISTER_COUNT] = {
+	/*analog mic*/
+	{ADAU_RECCTRL, 0x00},
+};
+
+static struct adau1381_mode_register adau1381_mode1[MODE_REGISTER_COUNT] = {
+	/*digital mic*/
+	{ADAU_RECCTRL, 0x10},
+};
+
+static struct adau1381_mode_register *adau1381_mode_registers[] = {
+	adau1381_mode0,
+	adau1381_mode1,
+};
+
+extern struct snd_soc_dai adau1381_dai;
+extern struct snd_soc_codec_device soc_codec_dev_adau1381;
+
+#endif
-- 
1.7.2

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 4/6] ASoC: Blackfin: new machine driver for ADAU1381 codecs
  2010-08-07 20:28 [PATCH 1/6] ASoC: add ADAU1361 codec driver Mike Frysinger
       [not found] ` <1281212905-12957-1-git-send-email-vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
  2010-08-07 20:28 ` [PATCH 3/6] ASoC: new ADAU1381 codec driver Mike Frysinger
@ 2010-08-07 20:28 ` Mike Frysinger
  2010-08-07 21:20 ` [PATCH 1/6] ASoC: add ADAU1361 codec driver Liam Girdwood
  2010-08-07 22:06 ` Mark Brown
  4 siblings, 0 replies; 18+ messages in thread
From: Mike Frysinger @ 2010-08-07 20:28 UTC (permalink / raw)
  To: alsa-devel, Mark Brown; +Cc: uclinux-dist-devel, Cliff Cai

From: Cliff Cai <cliff.cai@analog.com>

Signed-off-by: Cliff Cai <cliff.cai@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 sound/soc/blackfin/Kconfig          |    9 ++
 sound/soc/blackfin/Makefile         |    2 +
 sound/soc/blackfin/bf5xx-adau1381.c |  144 +++++++++++++++++++++++++++++++++++
 3 files changed, 155 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/blackfin/bf5xx-adau1381.c

diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
index 56eb6c6..5df4e4b 100644
--- a/sound/soc/blackfin/Kconfig
+++ b/sound/soc/blackfin/Kconfig
@@ -41,6 +41,15 @@ config SND_BF5XX_SOC_ADAU1361
 	help
 	  Say Y if you want to add support for ADAU1361 SoC audio.
 
+config SND_BF5XX_SOC_ADAU1381
+	tristate "SoC ADAU1381 Audio support"
+	depends on SND_BF5XX_I2S
+	select SND_BF5XX_SOC_I2S
+	select SND_SOC_ADAU1381
+	select I2C
+	help
+	  Say Y if you want to add support for ADAU1381 SoC audio.
+
 config SND_BF5XX_SOC_ADAV80X
 	tristate "SoC ADAV801/3 Audio support"
 	depends on SND_BF5XX_I2S && (SPI_MASTER || I2C)
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile
index 9de1fc9..02bb207 100644
--- a/sound/soc/blackfin/Makefile
+++ b/sound/soc/blackfin/Makefile
@@ -22,6 +22,7 @@ snd-ssm2602-objs := bf5xx-ssm2602.o
 snd-ad73311-objs := bf5xx-ad73311.o
 snd-ad193x-objs := bf5xx-ad193x.o
 snd-adau1361-objs := bf5xx-adau1361.o
+snd-adau1381-objs := bf5xx-adau1381.o
 snd-adav80x-objs := bf5xx-adav80x.o
 
 obj-$(CONFIG_SND_BF5XX_SOC_AD1836) += snd-ad1836.o
@@ -30,4 +31,5 @@ obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
 obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
 obj-$(CONFIG_SND_BF5XX_SOC_AD193X) += snd-ad193x.o
 obj-$(CONFIG_SND_BF5XX_SOC_ADAU1361) += snd-adau1361.o
+obj-$(CONFIG_SND_BF5XX_SOC_ADAU1381) += snd-adau1381.o
 obj-$(CONFIG_SND_BF5XX_SOC_ADAV80X) += snd-adav80x.o
diff --git a/sound/soc/blackfin/bf5xx-adau1381.c b/sound/soc/blackfin/bf5xx-adau1381.c
new file mode 100644
index 0000000..ca2a9e4
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-adau1381.c
@@ -0,0 +1,144 @@
+/*
+ * board driver for adau1381 sound chip
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+
+#include <asm/dma.h>
+#include <asm/portmux.h>
+#include <linux/gpio.h>
+#include "../codecs/adau1381.h"
+#include "bf5xx-sport.h"
+#include "bf5xx-i2s-pcm.h"
+#include "bf5xx-i2s.h"
+
+static int bf5xx_adau1381_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	unsigned int pll_in = 0, pll_out = 0;
+	int ret = 0;
+
+	switch (params_rate(params)) {
+	case 11025:
+	case 22050:
+	case 44100:
+	case 88200:
+		pll_out = ADAU1381_PLL_FREQ_441;
+		break;
+	case 8000:
+	case 12000:
+	case 16000:
+	case 24000:
+	case 32000:
+	case 48000:
+	case 64000:
+	case 96000:
+		pll_out = ADAU1381_PLL_FREQ_48;
+		break;
+	default:
+		pll_out = ADAU1381_PLL_FREQ_441;
+		break;
+	}
+
+	/* set codec DAI configuration */
+	ret = codec_dai->ops->set_fmt(codec_dai,
+		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+		SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set cpu DAI configuration */
+	ret = cpu_dai->ops->set_fmt(cpu_dai,
+		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+		SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set the codec system clock for DAC and ADC */
+	pll_in = ADAU1381_MCLK_RATE; /* fixed rate MCLK */
+	ret = codec_dai->ops->set_sysclk(codec_dai, ADAU1381_MCLK_ID, pll_in,
+		SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	/* codec PLL input is PCLK/4 */
+	ret = codec_dai->ops->set_pll(codec_dai, 0, 0, pll_in, pll_out);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct snd_soc_ops bf5xx_adau1381_ops = {
+	.hw_params = bf5xx_adau1381_hw_params,
+};
+
+static struct snd_soc_dai_link bf5xx_adau1381_dai = {
+	.name = "adau1381",
+	.stream_name = "adau1381",
+	.cpu_dai = &bf5xx_i2s_dai,
+	.codec_dai = &adau1381_dai,
+	.ops = &bf5xx_adau1381_ops,
+};
+
+static struct snd_soc_card bf5xx_adau1381 = {
+	.name = "bf5xx_adau1381",
+	.platform = &bf5xx_i2s_soc_platform,
+	.dai_link = &bf5xx_adau1381_dai,
+	.num_links = 1,
+};
+
+static struct snd_soc_device bf5xx_adau1381_snd_devdata = {
+	.card = &bf5xx_adau1381,
+	.codec_dev = &soc_codec_dev_adau1381,
+};
+
+static struct platform_device *bf5xx_adau1381_snd_device;
+
+static int __init bf5xx_adau1381_init(void)
+{
+	int ret;
+
+	pr_debug("%s enter\n", __func__);
+	bf5xx_adau1381_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!bf5xx_adau1381_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(bf5xx_adau1381_snd_device,
+				&bf5xx_adau1381_snd_devdata);
+	bf5xx_adau1381_snd_devdata.dev = &bf5xx_adau1381_snd_device->dev;
+	ret = platform_device_add(bf5xx_adau1381_snd_device);
+
+	if (ret)
+		platform_device_put(bf5xx_adau1381_snd_device);
+
+	return ret;
+}
+module_init(bf5xx_adau1381_init);
+
+static void __exit bf5xx_adau1381_exit(void)
+{
+	pr_debug("%s enter\n", __func__);
+	platform_device_unregister(bf5xx_adau1381_snd_device);
+}
+module_exit(bf5xx_adau1381_exit);
+
+/* Module information */
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("ALSA SoC adau1381");
+MODULE_LICENSE("GPL");
-- 
1.7.2

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 5/6] ASoC: new ADAU1761 codec driver
       [not found] ` <1281212905-12957-1-git-send-email-vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
  2010-08-07 20:28   ` [PATCH 2/6] ASoC: Blackfin: new machine driver for ADAU1361 codecs Mike Frysinger
@ 2010-08-07 20:28   ` Mike Frysinger
  2010-08-07 22:16     ` Mark Brown
  2010-08-07 20:28   ` [PATCH 6/6] ASoC: Blackfin: new machine driver for ADAU1761 codecs Mike Frysinger
  2 siblings, 1 reply; 18+ messages in thread
From: Mike Frysinger @ 2010-08-07 20:28 UTC (permalink / raw)
  To: alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Mark Brown
  Cc: uclinux-dist-devel-ZG0+EudsQA8dtHy/vicBwGD2FQJk+8+b, Cliff Cai

From: Cliff Cai <cliff.cai-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org>

Signed-off-by: Cliff Cai <cliff.cai-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Mike Frysinger <vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
---
 sound/soc/codecs/Kconfig    |    5 +
 sound/soc/codecs/Makefile   |    2 +
 sound/soc/codecs/adau1761.c | 1077 +++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/adau1761.h |  320 +++++++++++++
 4 files changed, 1404 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/codecs/adau1761.c
 create mode 100644 sound/soc/codecs/adau1761.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index e7195ca..79f2b5e 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -18,6 +18,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_AD73311 if I2C
 	select SND_SOC_ADAU1361 if I2C
 	select SND_SOC_ADAU1381 if I2C
+	select SND_SOC_ADAU1761 if I2C
 	select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI
 	select SND_SOC_ADS117X
 	select SND_SOC_AK4104 if SPI_MASTER
@@ -112,6 +113,10 @@ config SND_SOC_ADAU1361
 config SND_SOC_ADAU1381
 	tristate
 
+config SND_SOC_ADAU1761
+	tristate
+	select SIGMA
+
 config SND_SOC_ADAV80X
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 887ff5d..3eb7dcf 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -5,6 +5,7 @@ snd-soc-ad1980-objs := ad1980.o
 snd-soc-ad73311-objs := ad73311.o
 snd-soc-adau1361-objs := adau1361.o
 snd-soc-adau1381-objs := adau1381.o
+snd-soc-adau1761-objs := adau1761.o
 snd-soc-adav80x-objs := adav80x.o
 snd-soc-ads117x-objs := ads117x.o
 snd-soc-ak4104-objs := ak4104.o
@@ -74,6 +75,7 @@ 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_ADAU1381)	+= snd-soc-adau1381.o
+obj-$(CONFIG_SND_SOC_ADAU1761)	+= snd-soc-adau1761.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/adau1761.c b/sound/soc/codecs/adau1761.c
new file mode 100644
index 0000000..30dd706
--- /dev/null
+++ b/sound/soc/codecs/adau1761.c
@@ -0,0 +1,1077 @@
+/*
+ * Driver for ADAU1761 sound codec
+ *
+ * Copyright 2009 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/sigma.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 "adau1761.h"
+
+#define AUDIO_NAME "adau1761"
+#define ADAU1761_VERSION "0.20"
+#define ADAU1761_FIRMWARE "adau1761.bin"
+
+#define CAP_MIC  1
+#define CAP_LINE 2
+#define CAPTURE_SOURCE_NUMBER 2
+#define ADAU1761_DIG_MIC 0
+
+struct snd_soc_codec_device soc_codec_dev_adau1761;
+static struct snd_soc_codec *adau1761_codec;
+/* codec private data */
+struct adau1761_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 adau1761_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 adau1761_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 ADAU1761 register (6byte pll reg)
+ */
+static int adau1761_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++)
+		adau1761_write_reg_cache(codec, reg+i, (unsigned int)buf[i]);
+
+	return 0;
+}
+/*
+ * write a multibyte ADAU1761 register (6byte pll reg)
+ */
+static int adau1761_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;
+	}
+}
+
+/*
+ * adau1761 controls
+ */
+static int adau1761_mux_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct adau1761_priv *adau1761 = codec->private_data;
+
+	if (adau1761->in_source & CAP_MIC)
+		ucontrol->value.integer.value[0] = 0x0;
+	else
+		ucontrol->value.integer.value[0] = 0x1;
+
+	return 0;
+}
+
+static int adau1761_mux_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct adau1761_priv *adau1761 = codec->private_data;
+	int src = ucontrol->value.integer.value[0];
+	u8 regvalue = 0;
+
+
+	if (src == 0) {/* Select Mic */
+		adau1761->in_source = CAP_MIC;
+#ifdef ADAU1761_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 */
+		adau1761->in_source = CAP_LINE;
+#ifdef ADAU1761_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 adau1761_mic_boost_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct adau1761_priv *adau1761 = codec->private_data;
+
+	if (adau1761->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 adau1761_mic_boost_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct adau1761_priv *adau1761 = codec->private_data;
+	int val = ucontrol->value.integer.value[0];
+	u8 regvalue = 0;
+
+	if (adau1761->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 *adau1761_input_select[] = {"Mic", "Line"};
+static const struct soc_enum adau1761_enums[] = {
+	SOC_ENUM_SINGLE(ADAU_RECMLC1-ADAU_FIRSTREG, 0, 2, adau1761_input_select),
+};
+
+static const struct snd_kcontrol_new adau1761_snd_controls[] = {
+SOC_DOUBLE_R("Master Playback Volume", ADAU_DACCTL1-ADAU_FIRSTREG,
+	ADAU_DACCTL2-ADAU_FIRSTREG, 0, 255, 1),
+SOC_DOUBLE_R("Capture Volume", ADAU_ADCCTL1-ADAU_FIRSTREG,
+	ADAU_ADCCTL2-ADAU_FIRSTREG, 0, 255, 1),
+SOC_DOUBLE_R("Capture Switch", ADAU_RECMLC0-ADAU_FIRSTREG,
+	ADAU_RECMRC0-ADAU_FIRSTREG, 0, 1, 0),
+SOC_ENUM_EXT("Capture Source", adau1761_enums[0],
+	adau1761_mux_get, adau1761_mux_put),
+SOC_SINGLE_EXT("Mic Boost (+20dB)", ADAU_RECMLC1-ADAU_FIRSTREG, 0, 1, 0,
+	adau1761_mic_boost_get, adau1761_mic_boost_put),
+SOC_DOUBLE_R("Headphone Playback Volume", ADAU_PLBHPVL-ADAU_FIRSTREG,
+	ADAU_PLBHPVR-ADAU_FIRSTREG, 2, 63, 0),
+SOC_DOUBLE_R("Line Playback Volume", ADAU_PLBLOVL-ADAU_FIRSTREG,
+	ADAU_PLBLOVR-ADAU_FIRSTREG, 2, 63, 0),
+};
+
+/*
+ * _DAPM_
+ */
+
+static int adau1761_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 */
+		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 adau1761_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("LineLeft Bypass Switch", ADAU_PLBLOVL-ADAU_FIRSTREG, 1, 1, 0),
+SOC_DAPM_SINGLE("HPLeft Bypass Switch", ADAU_PLBHPVL-ADAU_FIRSTREG, 1, 1, 0),
+};
+
+/* Right mixer */
+static const struct snd_kcontrol_new adau1761_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("LineRight Bypass Switch", ADAU_PLBLOVR-ADAU_FIRSTREG, 1, 1, 0),
+SOC_DAPM_SINGLE("HPRight Bypass Switch", ADAU_PLBHPVR-ADAU_FIRSTREG, 1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget adau1761_dapm_widgets[] = {
+
+SND_SOC_DAPM_MIXER("Left Mixer", ADAU_PLBPWRM-ADAU_FIRSTREG, 2, 1, \
+	&adau1761_left_mixer_controls[0], ARRAY_SIZE(adau1761_left_mixer_controls)),
+SND_SOC_DAPM_MIXER("Left Out", ADAU_PLBPWRM-ADAU_FIRSTREG, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Left Line Mixer", ADAU_PLBMLLO-ADAU_FIRSTREG, 0, 0, NULL, 0),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+
+SND_SOC_DAPM_MIXER("Right Mixer", ADAU_PLBPWRM-ADAU_FIRSTREG, 3, 1, \
+	&adau1761_right_mixer_controls[0], ARRAY_SIZE(adau1761_right_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right Out", ADAU_PLBPWRM-ADAU_FIRSTREG, 1, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Right Line Mixer", ADAU_PLBMRLO-ADAU_FIRSTREG, 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-ADAU_FIRSTREG, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("DAC Enable Right", ADAU_DACCTL0-ADAU_FIRSTREG, 1, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("HP Bias Left", ADAU_PLBPWRM-ADAU_FIRSTREG, 6, 1, NULL, 0),
+SND_SOC_DAPM_MIXER("HP Bias Right", ADAU_PLBLRMC-ADAU_FIRSTREG, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_MIXER("ADC Left", ADAU_ADCCTL0-ADAU_FIRSTREG, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("ADC Right", ADAU_ADCCTL0-ADAU_FIRSTREG, 1, 0, NULL, 0),
+
+#if !defined(ADAU1761_DIG_MIC)
+SND_SOC_DAPM_MICBIAS("Mic Bias", ADAU_RECMBIA-ADAU_FIRSTREG, 0, 0),
+SND_SOC_DAPM_MIXER("Left Mic Mixer", ADAU_RECVLCL-ADAU_FIRSTREG, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Right Mic Mixer", ADAU_RECVLCR-ADAU_FIRSTREG, 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-ADAU_FIRSTREG, 1, 1, NULL, 0),
+SND_SOC_DAPM_MIXER("Right Input", ADAU_RECPWRM-ADAU_FIRSTREG, 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"},
+	{"Mic Bias", NULL, "LMICIN"},
+	{"Mic Bias", NULL, "RMICIN"},
+	{"Left Mic Mixer", NULL, "Mic Bias"},
+	{"Right Mic Mixer", NULL, "Mic Bias"},
+	{"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 adau1761_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, adau1761_dapm_widgets,
+				  ARRAY_SIZE(adau1761_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 adau1761_pll_init(struct snd_soc_codec *codec)
+{
+	struct adau1761_priv *adau1761 = codec->private_data;
+	u8 *pll_reg = adau1761->adau1761_pll_reg;
+	int ix = 0;
+	/* Clear dsp Run bit */
+	snd_soc_write(codec, ADAU_DSP_RUN, 0x00);
+
+	/* Init ADAU1761 clocking */
+	snd_soc_write(codec, ADAU_CLKCTRL,
+		(CLKCTRL_SRC_PLL | CLKCTRL_FRQ_1024 | CLKCTRL_DISABLE));
+
+	ix = get_pll_settings(adau1761->sysclk, adau1761->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;
+	adau1761_write_reg_block(codec, ADAU_PLLCTRL, 6, pll_reg);
+
+	adau1761->pll_enable = 0;
+
+	return 0;
+}
+
+static int adau1761_pll_enable(struct snd_soc_codec *codec, int enable)
+{
+	struct adau1761_priv *adau1761 = codec->private_data;
+	u8 *pll_reg = adau1761->adau1761_pll_reg;
+	int counter = 0;
+
+	if (enable) {
+		pll_reg[5]  = PLLCTRL_ENABLE;
+		adau1761_write_reg_block(codec, ADAU_PLLCTRL, 6, pll_reg);
+
+		/* wait for PLL lock*/
+		do {
+			++counter;
+			schedule_timeout_interruptible(msecs_to_jiffies(1));
+			adau1761_read_reg_block(codec, ADAU_PLLCTRL, 6);
+		} while (0 == (snd_soc_read(codec, ADAU_PLLCTRL + 5) & 0x2)
+			&& counter < 20);
+		if (counter >= 20)
+			return -1;
+
+		adau1761->pll_enable = 1;
+
+		/* Init ADAU1761 clocking */
+		snd_soc_write(codec, ADAU_CLKCTRL,
+			(CLKCTRL_SRC_PLL | CLKCTRL_FRQ_1024 | CLKCTRL_ENABLE));
+		/* restore dsp state */
+		snd_soc_write(codec, ADAU_DSP_RUN, 0x01);
+	}
+
+	return 0;
+
+}
+
+/*
+ * read ADAU1761 hw register and update cache
+ */
+static int adau1761_read_reg_byte(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u8 addr[2];
+	u8 buf[1] = {0};
+
+	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) {
+		printk(KERN_ERR "read_reg_byte:address write failed.");
+		return -EIO;
+	}
+
+	if (i2c_master_recv(codec->control_data, buf, 1) != 1)
+		return -EIO;
+
+	return buf[0];
+}
+
+static int adau1761_setprogram(struct snd_soc_codec *codec)
+{
+	int ret = 0;
+
+	snd_soc_write(codec, ADAU_DSP_ENA, 0x01);
+	/* don't waste time writing program for adau1361 */
+	if (adau1761_read_reg_byte(codec, ADAU_DSP_ENA) != 0x01)
+		return -ENODEV;
+
+	ret = process_sigma_firmware(codec->control_data, ADAU1761_FIRMWARE);
+
+	return ret;
+}
+
+static int adau1761_reg_init(struct snd_soc_codec *codec)
+{
+	struct adau1761_mode_register regdata;
+	struct adau1761_mode_register *registers = 0;
+	int i;
+#ifdef ADAU1761_DIG_MIC
+	int mode = 1;
+#else /* analog mic */
+	int mode = 0;
+#endif
+	adau1761_pll_init(codec);
+	adau1761_pll_enable(codec, 1);
+
+	/* Load deault regsiter settings */
+	for (i = 0; i < RESET_REGISTER_COUNT; ++i) {
+		regdata = adau1761_reset[i];
+		snd_soc_write(codec, regdata.regaddress, regdata.regvalue);
+	}
+	/* Load mode registers */
+	registers = adau1761_mode_registers[mode];
+	for (i = 0; i < MODE_REGISTER_COUNT; ++i) {
+		regdata = registers[i];
+		snd_soc_write(codec, regdata.regaddress, regdata.regvalue);
+	}
+	/* Load default program */
+	adau1761_setprogram(codec);
+	snd_soc_write(codec, ADAU_SPRTCT1, 0x00);
+	/* 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 adau1761_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 adau1761_priv *adau1761 = codec->private_data;
+	int rate = params_rate(params);
+	int i;
+
+	/* initialize the PLL */
+	if (adau1761_pll_init(codec) != 0)
+		return -EINVAL;
+	for (i = 0; i < ARRAY_SIZE(srate_iface); i++) {
+		if (srate_iface[i].fs == rate) {
+			adau1761->rate_index = i;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int adau1761_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 adau1761_priv *adau1761 = codec->private_data;
+	u8 reg = 0;
+	int ret = 0;
+
+	reg = srate_iface[adau1761->rate_index].reg;
+	ret = adau1761_pll_enable(codec, 1);
+	if (ret)
+		dev_err(codec->dev, "Failed to initialize PLL");
+
+	snd_soc_write(codec, ADAU_SER_SRT, reg);
+	/* Set DSP Rate to follow the serial rate */
+	snd_soc_write(codec, ADAU_DSP_SR1, 0x07);
+
+	reg = (snd_soc_read(codec, ADAU_CONVCT0) & 0xF8) | reg;
+	snd_soc_write(codec, ADAU_CONVCT0, reg);
+
+	return ret;
+}
+
+static void adau1761_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;
+
+	snd_soc_write(codec, ADAU_DSP_RUN, 0x0);
+	reg = snd_soc_read(codec, ADAU_CLKCTRL);
+	snd_soc_write(codec, ADAU_CLKCTRL, reg & ~0x1);
+
+}
+
+static int adau1761_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;
+		snd_soc_write(codec, ADAU_CLK_EN1, CLK_EN1_MASTER);
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS: /*slave*/
+		reg |= 0x0;
+		snd_soc_write(codec, ADAU_CLK_EN1, CLK_EN1_SLAVE);
+		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 adau1761_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 adau1761_priv *adau1761 = codec->private_data;
+
+	switch (freq) {
+	case 12000000:
+		adau1761->sysclk = freq;
+		return 0;
+	case 12288000:
+		adau1761->sysclk = freq;
+		return 0;
+	}
+
+	/* supported 12MHz MCLK only for now */
+	return -EINVAL;
+}
+
+static int adau1761_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 adau1761_priv *adau1761 = codec->private_data;
+
+	/* fixed MCLK only supported for now */
+	if (adau1761->sysclk != freq_in)
+		return -EINVAL;
+
+	/* Only update pll when freq changes */
+	if (adau1761->pll_enable && adau1761->pll_out == freq_out)
+		return 0;
+
+	switch (freq_out) {
+	case 45158400:
+		adau1761->pll_out = freq_out;
+		break;
+	case 49152000:
+		adau1761->pll_out = freq_out;
+		break;
+	default:
+		dev_err(codec->dev, "adau1761_set_dai_pll: undefined pll freq:%d", freq_out);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+static int adau1761_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 ADAU1761_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 ADAU1761_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 adau1761_dai_ops = {
+	.hw_params	= adau1761_hw_params,
+	.prepare	= adau1761_pcm_prepare,
+	.shutdown	= adau1761_shutdown,
+	.digital_mute	= adau1761_mute,
+	.set_fmt	= adau1761_set_dai_fmt,
+	.set_sysclk	= adau1761_set_dai_sysclk,
+	.set_pll	= adau1761_set_dai_pll,
+};
+
+struct snd_soc_dai adau1761_dai = {
+	.name = "ADAU1761",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = ADAU1761_RATES,
+		.formats = ADAU1761_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = ADAU1761_RATES,
+		.formats = ADAU1761_FORMATS,
+	},
+	.ops = &adau1761_dai_ops,
+};
+EXPORT_SYMBOL_GPL(adau1761_dai);
+
+static int adau1761_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;
+
+	adau1761_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static void adau1761_resume_wq_handler(struct work_struct *work)
+{
+	struct adau1761_priv *adau1761 = container_of(work, struct adau1761_priv, resume_work);
+	struct snd_soc_codec *codec = &adau1761->codec;
+	unsigned int i, v;
+
+	adau1761_pll_init(codec);
+	adau1761_pll_enable(codec, 1);
+	/* Load program */
+	adau1761_setprogram(codec);
+
+	/* 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;
+		/* skip reserved registers */
+		if (i > ADAU_MCLKPAD && i < ADAU_DSP_CRC)
+			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);
+
+	adau1761_set_bias_level(codec, SND_SOC_BIAS_ON);
+
+}
+
+static int adau1761_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	struct adau1761_priv *adau1761 = codec->private_data;
+
+	adau1761->pdev = pdev;
+	schedule_work(&adau1761->resume_work);
+	return 0;
+}
+
+/*
+ * initialise the adau1761 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int adau1761_register(struct adau1761_priv *adau1761, enum snd_soc_control_type control)
+{
+	struct snd_soc_codec *codec = &adau1761->codec;
+	int ret = 0;
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+	codec->name = "adau1761";
+	codec->owner = THIS_MODULE;
+	codec->set_bias_level = adau1761_set_bias_level;
+	codec->dai = &adau1761_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(&adau1761_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 adau1761_unregister(struct adau1761_priv *adau1761)
+{
+	struct snd_soc_codec *codec = &adau1761->codec;
+
+	adau1761_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	kfree(codec->reg_cache);
+	snd_soc_unregister_dai(&adau1761_dai);
+	snd_soc_unregister_codec(codec);
+	kfree(adau1761);
+	adau1761_codec = NULL;
+}
+
+static ssize_t adau1371_dsp_load(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct snd_soc_device *socdev = dev_get_drvdata(dev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	int ret = 0;
+
+	ret = adau1761_setprogram(codec);
+	if (ret)
+		return ret;
+	else
+		return count;
+}
+static DEVICE_ATTR(dsp, 0644, NULL, adau1371_dsp_load);
+
+static int adau1761_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	struct adau1761_priv *adau1761;
+	int ret = 0;
+
+	socdev->card->codec = adau1761_codec;
+	codec = adau1761_codec;
+	adau1761 = codec->private_data;
+	adau1761->in_source = CAP_MIC; /*default is mic input*/
+	adau1761->sysclk = ADAU1761_MCLK_RATE;
+	adau1761->pll_out = ADAU1761_PLL_FREQ_48;
+	adau1761->dapm_lineL = DAPM_LINE_DEF;
+	adau1761->dapm_lineR = DAPM_LINE_DEF;
+	adau1761->dapm_hpL = DAPM_HP_DEF;
+	adau1761->dapm_hpR = DAPM_HP_DEF;
+	adau1761->pdev = pdev;
+
+	ret = adau1761_reg_init(codec);
+	if (ret < 0)
+		dev_err(codec->dev, "failed to initialize\n");
+
+	ret = device_create_file(codec->dev, &dev_attr_dsp);
+	if (ret)
+		dev_err(socdev->dev, "device_create_file() failed\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, adau1761_snd_controls,
+			     ARRAY_SIZE(adau1761_snd_controls));
+	adau1761_add_widgets(codec);
+pcm_err:
+	device_remove_file(codec->dev, &dev_attr_dsp);
+	return ret;
+}
+
+/* remove everything here */
+static int adau1761_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_adau1761 = {
+	.probe =	adau1761_probe,
+	.remove =	adau1761_remove,
+	.suspend =	adau1761_suspend,
+	.resume =	adau1761_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_adau1761);
+
+
+static __devinit int adau1761_i2c_probe(struct i2c_client *i2c,
+			      const struct i2c_device_id *id)
+{
+	struct adau1761_priv *adau1761;
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	adau1761 = kzalloc(sizeof(struct adau1761_priv), GFP_KERNEL);
+	if (adau1761 == NULL)
+		return -ENOMEM;
+	codec = &adau1761->codec;
+	codec->private_data = adau1761;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+
+	i2c_set_clientdata(i2c, adau1761);
+	codec->control_data = i2c;
+
+	codec->dev = &i2c->dev;
+	adau1761_codec = codec;
+
+	INIT_WORK(&adau1761->resume_work, adau1761_resume_wq_handler);
+	ret = adau1761_register(adau1761, SND_SOC_I2C);
+	if (ret < 0)
+		dev_err(&i2c->dev, "failed to initialize\n");
+
+	return ret;
+}
+
+static __devexit int adau1761_i2c_remove(struct i2c_client *client)
+{
+	struct adau1761_priv *adau1761 = i2c_get_clientdata(client);
+	adau1761_unregister(adau1761);
+	return 0;
+}
+
+static const struct i2c_device_id adau1761_i2c_id[] = {
+	{ "adau1761", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, adau1761_i2c_id);
+
+/* corgi i2c codec control layer */
+static struct i2c_driver adau1761_i2c_driver = {
+	.driver = {
+		.name = "adau1761",
+		.owner = THIS_MODULE,
+	},
+	.probe    = adau1761_i2c_probe,
+	.remove   = __devexit_p(adau1761_i2c_remove),
+	.id_table = adau1761_i2c_id,
+};
+
+static int __init adau1761_modinit(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&adau1761_i2c_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register adau1761 I2C driver: %d\n",
+		       ret);
+	}
+
+	return ret;
+}
+module_init(adau1761_modinit);
+
+static void __exit adau1761_exit(void)
+{
+	i2c_del_driver(&adau1761_i2c_driver);
+}
+module_exit(adau1761_exit);
+
+MODULE_DESCRIPTION("ASoC ADAU1761 driver");
+MODULE_AUTHOR("John McCarty, Cliff Cai");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1761.h b/sound/soc/codecs/adau1761.h
new file mode 100644
index 0000000..a227123
--- /dev/null
+++ b/sound/soc/codecs/adau1761.h
@@ -0,0 +1,320 @@
+/*
+ * header file fortone adau1761 sound chip
+ *
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+
+#ifndef __ADAU1761_H__
+#define __ADAU1761_H__
+
+struct adau1761_setup_data {
+	unsigned short i2c_bus;
+	unsigned short i2c_address;
+};
+
+struct adau1761_mode_register {
+	u16  regaddress;
+	u16  regvalue;
+};
+
+#define RESET_REGISTER_COUNT 54
+#define MODE_REGISTER_COUNT 10
+
+/* DSP program patch for ADAU1761 ADC IIS 16bit mode */
+#define ADAU1716_ADC_SOFT_PATCH 1
+#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
+
+/* adau1761_set_dai_sysclk clk_id */
+#define ADAU1761_MCLK_ID	0
+#define ADAU1761_BCLK_ID	0x33
+
+#define ADAU1761_MCLK_RATE	12288000
+
+#define ADAU1761_PLL_FREQ_441	45158400
+#define ADAU1761_PLL_FREQ_48	49152000
+
+/* ADAU1761 Memory */
+#define ADAU_PROGRAM	0x0800
+#define ADAU_PARAMS	0x0000
+#define ADAU_PROGSIZE	0x0400
+#define ADAU_PARASIZE	0x0400
+
+/* ADAU1761 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_SERPAD1	0x402E
+#define ADAU_COMPAD0	0x402F
+#define ADAU_COMPAD1	0x4030
+#define ADAU_MCLKPAD	0x4031
+
+/* DSP Core */
+#define ADAU_DSP_CRC	0x40C0
+#define ADAU_DSP_GPI	0x40C6
+#define ADAU_NON_MOD	0x40E9
+#define ADAU_WATCGDG	0x40D0
+#define ADAU_DSP_SR0	0x40EA
+#define ADAU_DSP_SR1	0x40EB
+
+#define ADAU_INP_ROT	0x40F2
+#define ADAU_OUT_ROT	0x40F3
+#define ADAU_SER_DAT	0x40F4
+#define ADAU_DSP_ENA	0x40F5
+#define ADAU_DSP_RUN	0x40F6
+#define ADAU_DSP_SLW	0x40F7
+#define ADAU_SER_SRT	0x40F8
+#define ADAU_CLK_EN0	0x40F9
+#define ADAU_CLK_EN1	0x40FA
+
+#define ADAU_LASTREG	0x40FA
+#define ADAU_NUMCACHEREG	251
+
+/* 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 CLK_EN1_MASTER		0x03
+#define CLK_EN1_SLAVE		0x01
+#define CLK_EN1_OFF		0x00
+#define CLK_EN0_OFF		0x00
+#define CLK_EN0_ON		0x5F
+#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 ADAU1761_BITSFRAM_32	0x4000
+#define ADAU1761_BITSFRAM_48	0x8000
+
+/*playback output control*/
+#define ADAU1761_VOLUME_MASK 0xFC
+#define ADAU1761_VOLUME_BITS 0x2
+#define ADAU1761_MUTE_MASK 0x02
+#define ADAU1761_MUTE_BITS 0x1
+#define ADAU1761_ADVOL_MASK 0xff
+
+/*
+ * Reset Mode - ADC capture/DAC playback, dsp core disabled
+ * (AInput mixers 0db, AOuput mixers 0db, HP out ON, DSP core OFF)
+*/
+static struct adau1761_mode_register adau1761_reset[RESET_REGISTER_COUNT] = {
+	/* mute outputs */
+	{ADAU_PLBMNOC, 0xE5},
+	{ADAU_PLBHPVL, 0x01},
+	{ADAU_PLBHPVR, 0x01},
+	{ADAU_PLBLOVL, 0x00},
+	{ADAU_PLBLOVR, 0x00},
+
+	{ADAU_DSP_RUN, 0x00},
+	{ADAU_DSP_ENA, 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, 0x21}, /*0x21 = 32bclocks frame, 0x41 = 48*/
+	{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_DSP_CRC, 0x00},
+	{ADAU_DSP_GPI, 0x00},
+	{ADAU_DSP_SR1, 0x07},
+	{ADAU_SERPAD0, 0xAA},
+	{ADAU_SERPAD1, 0x00},
+	{ADAU_COMPAD0, 0xAA},
+	{ADAU_COMPAD1, 0x00},
+	{ADAU_MCLKPAD, 0x0A},
+	{ADAU_INP_ROT, 0x01},
+#ifdef ADAU1716_ADC_SOFT_PATCH
+	{ADAU_OUT_ROT, 0x00},
+#else
+	{ADAU_OUT_ROT, 0x01},
+#endif
+	{ADAU_SER_SRT, 0x00},
+	{ADAU_DSP_SLW, 0x00},
+	{ADAU_SER_DAT, 0x00},
+	{ADAU_CLK_EN0, 0x5F},
+	{ADAU_CLK_EN1, 0x01},
+};
+
+/*
+ * Default Mode
+ * Analog microphones, ADC capture/DAC playback, dsp core disabled
+ * (AInput mixers ON, AOuput mixers ON, HP out ON)
+*/
+static struct adau1761_mode_register adau1761_mode0[MODE_REGISTER_COUNT] = {
+	/* mute outputs */
+	{ADAU_PLBHPVL, 0x03},
+	{ADAU_PLBHPVR, 0x03},
+	{ADAU_PLBLOVL, 0x02},
+	{ADAU_PLBLOVR, 0x02},
+	{ADAU_PLBMNOC, 0xE5},
+
+	{ADAU_CLK_EN0, 0x5F},
+	{ADAU_CLK_EN1, 0x01},
+
+	/*analog mic*/
+	{ADAU_RECVLCL, 0x82},
+	{ADAU_RECVLCR, 0x82},
+	{ADAU_MICCTRL, 0x00},
+};
+
+/*
+ * Digital Microphone mode,
+ * IIS Master, ADC capture/DAC playback, dsp core disabled
+ * (AInput mixers OFF, AOuput mixers ON, HP out ON)
+ */
+static struct adau1761_mode_register adau1761_mode1[MODE_REGISTER_COUNT] = {
+	/* mute outputs */
+	{ADAU_PLBHPVL, 0x03},
+	{ADAU_PLBHPVR, 0x03},
+	{ADAU_PLBLOVL, 0x02},
+	{ADAU_PLBLOVR, 0x02},
+	{ADAU_PLBMNOC, 0xE5},
+
+	{ADAU_CLK_EN0, 0x5F},
+	{ADAU_CLK_EN1, 0x03},
+
+	/*digital mic*/
+	{ADAU_RECVLCL, 0x00},
+	{ADAU_RECVLCR, 0x00},
+	{ADAU_MICCTRL, 0x20},
+};
+
+static struct adau1761_mode_register *adau1761_mode_registers[] = {
+	adau1761_mode0,
+	adau1761_mode1,
+};
+
+extern struct snd_soc_dai adau1761_dai;
+extern struct snd_soc_codec_device soc_codec_dev_adau1761;
+
+#endif
-- 
1.7.2

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 6/6] ASoC: Blackfin: new machine driver for ADAU1761 codecs
       [not found] ` <1281212905-12957-1-git-send-email-vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
  2010-08-07 20:28   ` [PATCH 2/6] ASoC: Blackfin: new machine driver for ADAU1361 codecs Mike Frysinger
  2010-08-07 20:28   ` [PATCH 5/6] ASoC: new ADAU1761 codec driver Mike Frysinger
@ 2010-08-07 20:28   ` Mike Frysinger
  2 siblings, 0 replies; 18+ messages in thread
From: Mike Frysinger @ 2010-08-07 20:28 UTC (permalink / raw)
  To: alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Mark Brown
  Cc: uclinux-dist-devel-ZG0+EudsQA8dtHy/vicBwGD2FQJk+8+b, Cliff Cai

From: Cliff Cai <cliff.cai-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org>

Signed-off-by: Cliff Cai <cliff.cai-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Mike Frysinger <vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
---
 sound/soc/blackfin/Kconfig          |    9 ++
 sound/soc/blackfin/Makefile         |    2 +
 sound/soc/blackfin/bf5xx-adau1761.c |  144 +++++++++++++++++++++++++++++++++++
 3 files changed, 155 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/blackfin/bf5xx-adau1761.c

diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
index 5df4e4b..64b90c1 100644
--- a/sound/soc/blackfin/Kconfig
+++ b/sound/soc/blackfin/Kconfig
@@ -50,6 +50,15 @@ config SND_BF5XX_SOC_ADAU1381
 	help
 	  Say Y if you want to add support for ADAU1381 SoC audio.
 
+config SND_BF5XX_SOC_ADAU1761
+	tristate "SoC ADAU1761 Audio support"
+	depends on SND_BF5XX_I2S
+	select SND_BF5XX_SOC_I2S
+	select SND_SOC_ADAU1761
+	select I2C
+	help
+	  Say Y if you want to add support for ADAU1761 SoC audio.
+
 config SND_BF5XX_SOC_ADAV80X
 	tristate "SoC ADAV801/3 Audio support"
 	depends on SND_BF5XX_I2S && (SPI_MASTER || I2C)
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile
index 02bb207..33449b9 100644
--- a/sound/soc/blackfin/Makefile
+++ b/sound/soc/blackfin/Makefile
@@ -23,6 +23,7 @@ snd-ad73311-objs := bf5xx-ad73311.o
 snd-ad193x-objs := bf5xx-ad193x.o
 snd-adau1361-objs := bf5xx-adau1361.o
 snd-adau1381-objs := bf5xx-adau1381.o
+snd-adau1761-objs := bf5xx-adau1761.o
 snd-adav80x-objs := bf5xx-adav80x.o
 
 obj-$(CONFIG_SND_BF5XX_SOC_AD1836) += snd-ad1836.o
@@ -32,4 +33,5 @@ obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
 obj-$(CONFIG_SND_BF5XX_SOC_AD193X) += snd-ad193x.o
 obj-$(CONFIG_SND_BF5XX_SOC_ADAU1361) += snd-adau1361.o
 obj-$(CONFIG_SND_BF5XX_SOC_ADAU1381) += snd-adau1381.o
+obj-$(CONFIG_SND_BF5XX_SOC_ADAU1761) += snd-adau1761.o
 obj-$(CONFIG_SND_BF5XX_SOC_ADAV80X) += snd-adav80x.o
diff --git a/sound/soc/blackfin/bf5xx-adau1761.c b/sound/soc/blackfin/bf5xx-adau1761.c
new file mode 100644
index 0000000..641c705
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-adau1761.c
@@ -0,0 +1,144 @@
+/*
+ * board driver for adau1761 sound chip
+ *
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+
+#include <asm/dma.h>
+#include <asm/portmux.h>
+#include <linux/gpio.h>
+#include "../codecs/adau1761.h"
+#include "bf5xx-sport.h"
+#include "bf5xx-i2s-pcm.h"
+#include "bf5xx-i2s.h"
+
+static int bf5xx_adau1761_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	unsigned int pll_in = 0, pll_out = 0;
+	int ret = 0;
+
+	switch (params_rate(params)) {
+	case 11025:
+	case 22050:
+	case 44100:
+	case 88200:
+		pll_out = ADAU1761_PLL_FREQ_441;
+		break;
+	case 8000:
+	case 12000:
+	case 16000:
+	case 24000:
+	case 32000:
+	case 48000:
+	case 64000:
+	case 96000:
+		pll_out = ADAU1761_PLL_FREQ_48;
+		break;
+	default:
+		pll_out = ADAU1761_PLL_FREQ_441;
+		break;
+	}
+
+	/* set codec DAI configuration */
+	ret = codec_dai->ops->set_fmt(codec_dai,
+		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+		SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set cpu DAI configuration */
+	ret = cpu_dai->ops->set_fmt(cpu_dai,
+		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+		SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set the codec system clock for DAC and ADC */
+	pll_in = ADAU1761_MCLK_RATE; /* fixed rate MCLK */
+	ret = codec_dai->ops->set_sysclk(codec_dai, ADAU1761_MCLK_ID, pll_in,
+		SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	/* codec PLL input is PCLK/4 */
+	ret = codec_dai->ops->set_pll(codec_dai, 0, 0, pll_in, pll_out);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct snd_soc_ops bf5xx_adau1761_ops = {
+	.hw_params = bf5xx_adau1761_hw_params,
+};
+
+static struct snd_soc_dai_link bf5xx_adau1761_dai = {
+	.name = "adau1761",
+	.stream_name = "adau1761",
+	.cpu_dai = &bf5xx_i2s_dai,
+	.codec_dai = &adau1761_dai,
+	.ops = &bf5xx_adau1761_ops,
+};
+
+static struct snd_soc_card bf5xx_adau1761 = {
+	.name = "bf5xx_adau1761",
+	.platform = &bf5xx_i2s_soc_platform,
+	.dai_link = &bf5xx_adau1761_dai,
+	.num_links = 1,
+};
+
+static struct snd_soc_device bf5xx_adau1761_snd_devdata = {
+	.card = &bf5xx_adau1761,
+	.codec_dev = &soc_codec_dev_adau1761,
+};
+
+static struct platform_device *bf5xx_adau1761_snd_device;
+
+static int __init bf5xx_adau1761_init(void)
+{
+	int ret;
+
+	pr_debug("%s enter\n", __func__);
+	bf5xx_adau1761_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!bf5xx_adau1761_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(bf5xx_adau1761_snd_device,
+				&bf5xx_adau1761_snd_devdata);
+	bf5xx_adau1761_snd_devdata.dev = &bf5xx_adau1761_snd_device->dev;
+	ret = platform_device_add(bf5xx_adau1761_snd_device);
+
+	if (ret)
+		platform_device_put(bf5xx_adau1761_snd_device);
+
+	return ret;
+}
+module_init(bf5xx_adau1761_init);
+
+static void __exit bf5xx_adau1761_exit(void)
+{
+	pr_debug("%s enter\n", __func__);
+	platform_device_unregister(bf5xx_adau1761_snd_device);
+}
+module_exit(bf5xx_adau1761_exit);
+
+/* Module information */
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("ALSA SoC adau1761");
+MODULE_LICENSE("GPL");
-- 
1.7.2

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/6] ASoC: Blackfin: new machine driver for ADAU1361 codecs
  2010-08-07 20:28   ` [PATCH 2/6] ASoC: Blackfin: new machine driver for ADAU1361 codecs Mike Frysinger
@ 2010-08-07 21:08     ` Mark Brown
  2010-08-07 21:14       ` [Uclinux-dist-devel] " Mike Frysinger
  0 siblings, 1 reply; 18+ messages in thread
From: Mark Brown @ 2010-08-07 21:08 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: uclinux-dist-devel, alsa-devel, Cliff Cai

On 7 Aug 2010, at 21:28, Mike Frysinger <vapier@gentoo.org> wrote:

> +config SND_BF5XX_SOC_ADAU1361
> +	tristate "SoC ADAU1361 Audio support"
> +	depends on SND_BF5XX_I2S
> +	select SND_BF5XX_SOC_I2S
> +	select SND_SOC_ADAU1361
> +	select I2C
> +	help
> +	  Say Y if you want to add support for ADAU1361 SoC audio.
> +

Identical comments to the last machine driver you posted here.

It'd be really helpful if you could do a bit more pre-review on the stuff you're posting, especially where you've got a bunch of drivers that follow similar patterns and are going to generate exactly the same feedback. Posting a large batch of patches at once isn't a problem but if you're sending additional patches later on it'd be helpful to take into account the feedback that has already been given on prior serieses.

> +	switch (params_rate(params)) {
> +	case 11025:
> +	case 22050:
> +	case 44100:
> +	case 88200:
> +		pll_out = ADAU1361_PLL_FREQ_441;
> +		break;
> +	case 8000:
> +	case 12000:
> +	case 16000:
> +	case 24000:
> +	case 32000:
> +	case 48000:
> +	case 64000:
> +	case 96000:
> +		pll_out = ADAU1361_PLL_FREQ_48;
> +		break;
> +	default:
> +		pll_out = ADAU1361_PLL_FREQ_441;
> +		break;
> +	}

It seems silly for the machine drivers to all have to manually do this lookup - it appears to be entirely and inflexibly related to the sample rate in use. May as well just specify the input clock to the device.

> +	/* set codec DAI configuration */
> +	ret = codec_dai->ops->set_fmt(codec_dai,
> +		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
> +		SND_SOC_DAIFMT_CBM_CFM);
> +	if (ret < 0)
> +		return ret;

There are accessor functions for this stuff. I've stopped reviewing the code at this point.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [Uclinux-dist-devel] [PATCH 2/6] ASoC: Blackfin: new machine driver for ADAU1361 codecs
  2010-08-07 21:08     ` Mark Brown
@ 2010-08-07 21:14       ` Mike Frysinger
  2010-08-07 22:29         ` Mark Brown
  0 siblings, 1 reply; 18+ messages in thread
From: Mike Frysinger @ 2010-08-07 21:14 UTC (permalink / raw)
  To: Mark Brown; +Cc: uclinux-dist-devel, alsa-devel, Cliff Cai

On Sat, Aug 7, 2010 at 17:08, Mark Brown wrote:
> On 7 Aug 2010, at 21:28, Mike Frysinger <vapier@gentoo.org> wrote:
>
>> +config SND_BF5XX_SOC_ADAU1361
>> +     tristate "SoC ADAU1361 Audio support"
>> +     depends on SND_BF5XX_I2S
>> +     select SND_BF5XX_SOC_I2S
>> +     select SND_SOC_ADAU1361
>> +     select I2C
>> +     help
>> +       Say Y if you want to add support for ADAU1361 SoC audio.
>> +
>
> Identical comments to the last machine driver you posted here.
>
> It'd be really helpful if you could do a bit more pre-review on the stuff you're posting, especially where you've got a bunch of drivers that follow similar patterns and are going to generate exactly the same feedback. Posting a large batch of patches at once isn't a problem but if you're sending additional patches later on it'd be helpful to take into account the feedback that has already been given on prior serieses.

probably, but i'm not familiar with the ASoC stuff to fix/review any
of this.  i'll open another tracker item for these codecs.
-mike
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 1/6] ASoC: add ADAU1361 codec driver
  2010-08-07 20:28 [PATCH 1/6] ASoC: add ADAU1361 codec driver Mike Frysinger
                   ` (2 preceding siblings ...)
  2010-08-07 20:28 ` [PATCH 4/6] ASoC: Blackfin: new machine driver for ADAU1381 codecs Mike Frysinger
@ 2010-08-07 21:20 ` Liam Girdwood
  2010-08-07 22:06 ` Mark Brown
  4 siblings, 0 replies; 18+ messages in thread
From: Liam Girdwood @ 2010-08-07 21:20 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: uclinux-dist-devel, alsa-devel, Mark Brown, Cliff Cai

Hi Mike,

On Sat, 2010-08-07 at 16:28 -0400, Mike Frysinger wrote:
> From: Cliff Cai <cliff.cai@analog.com>
> 
> Signed-off-by: Cliff Cai <cliff.cai@analog.com>
> Signed-off-by: Mike Frysinger <vapier@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

As Mark has mentioned, please re-work your CODEC probe() and remove()
calls against the soon to be merged multi-component branch :-

git://git.kernel.org/pub/scm/linux/kernel/git/lrg/asoc-2.6.git topic/multi-component

The machine drivers will also need modified to use the component
dev_name() now instead of a pointer when creating a DAI link.

Thanks

Liam
-- 
Freelance Developer, SlimLogic Ltd
ASoC and Voltage Regulator Maintainer.
http://www.slimlogic.co.uk

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 3/6] ASoC: new ADAU1381 codec driver
  2010-08-07 20:28 ` [PATCH 3/6] ASoC: new ADAU1381 codec driver Mike Frysinger
@ 2010-08-07 21:43   ` Mark Brown
  0 siblings, 0 replies; 18+ messages in thread
From: Mark Brown @ 2010-08-07 21:43 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: uclinux-dist-devel, alsa-devel, Cliff Cai

On 7 Aug 2010, at 21:28, Mike Frysinger <vapier@gentoo.org> wrote:

> From: Cliff Cai <cliff.cai@analog.com>
> 
> Signed-off-by: Cliff Cai <cliff.cai@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>

As with all your other postings today this needs to be redone based on the multi-component branch. It is safe to assume that any new drivers like this ought to be done against the multi-component branch.

> +#define AUDIO_NAME "adau1381"
> +#define ADAU1381_VERSION "0.1"

Remove the version number at least.

> +#define CAP_MIC  1
> +#define CAP_LINE 2
> +#define CAPTURE_SOURCE_NUMBER 2
> +#define ADAU1381_DIG_MIC 0

No ifdefs, this is all stuff that should be configured by the machine driver for the particular board rather than hard coded by the CODEC.

This comes back to the issue I keep raising with the way you guys are doing machine drivers. The machine drivers should describe how a particular board is set up while all the Blackfin machine drivers are producing a "generic" machine driver which attempts to cover all possible machines, which results in things like this and the SPORT stuff in Kconfig as soon as you get even a mildly flexible system setup. It really would be a good idea to fix up your machine drivers to use the framework in the intended fashion, especially as people start deploying more flexible CODECs in conjunction with Blackfin.

> +	if (i2c_master_recv(codec->control_data, buf, len) != len)
> +		return -EIO;

If i2c_master_recv() gave you an error code you should return that. -EIO is reasonable for short reads.

> +#if !defined(ADAU1381_DIG_MIC)
> +SND_SOC_DAPM_MICBIAS("Mic Bias", ADAU_RECMBIA-ADAU_FIRSTREG, 0, 0),
> +SND_SOC_DAPM_MIXER("Left Mic Mixer", ADAU_RECVLCL-ADAU_FIRSTREG, 0, 0, NULL, 0),
> +SND_SOC_DAPM_MIXER("Right Mic Mixer", ADAU_RECVLCR-ADAU_FIRSTREG, 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),

It's difficult to see the point of the two biases here - they're not representing anything in the chip (they're _NOPM).

> +	snd_soc_write(codec, ADAU_DIGPWR0, 0xF3);
> +	snd_soc_write(codec, ADAU_DIGPWR0, 0x0D);
> +	reg = (snd_soc_read(codec, ADAU_CONVCT0) & 0xF8) | reg;
> +	snd_soc_write(codec, ADAU_CONVCT0, reg);
> +	reg = (snd_soc_read(codec, ADAU_SAMRATE)) | reg;
> +	snd_soc_write(codec, ADAU_SAMRATE, reg);
> +
> +	reg = (snd_soc_read(codec, ADAU_FRMRATE)) | reg;
> +	snd_soc_write(codec, ADAU_FRMRATE, reg);
> +	snd_soc_write(codec, ADAU_ENGIRUN, 0x01);

It's hard to tell what all this is doing due to the use of magic numbers but it feels like something that'd be better done through the power management infrastructure (either DAPM or the bias level configuration) rather than this.

> +	/* 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;
> +	}

Here and elsewhere in the driver I'd expect the default case to return an error rather than zero when it's bombing out.

> +	switch (freq) {
> +	case 12000000:
> +		adau1381->sysclk = freq;
> +		return 0;
> +	case 12288000:
> +		adau1381->sysclk = freq;
> +		return 0;
> +	}
> +
> +	/* supported 12MHz MCLK only for now */
> +	return -EINVAL;

This comment is bit rotted - it'd probably be clearer to just use a default: case in the switch statement.

> +	/* Only update pll when freq changes */
> +	if (adau1381->pll_enable && adau1381->pll_out == freq_out)
> +		return 0;

Given that you only store the value and don't actually reconfigure the PLL here this is redundant. Doesn't do any harm, though.

> +	case SND_SOC_BIAS_STANDBY:
> +		snd_soc_write(codec, ADAU_CLKCTRL,
> +			(CLKCTRL_SRC_PLL | CLKCTRL_FRQ_1024 | CLKCTRL_DISABLE));
> +		break;

What exactly is this doing? Looking at the bitfields being set it looks like it's doing something that ought to be done elsewhere.

> +static int adau1381_resume(struct platform_device *pdev)
> +{
> +	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> +	struct snd_soc_codec *codec = socdev->card->codec;
> +	struct adau1381_priv *adau1381 = codec->private_data;
> +
> +	adau1381->pdev = pdev;

Why is this being done here rather than on init? Though all this will change when you redo this against multi-component?

> +	schedule_work(&adau1381->resume_work);

Why are you deferring this to scheduled work? ASoC already defers all resume work for non-AC97 devices to a workqueue in order to minimise the impact of things like lengthy rise times for VMIDs and register restores over slow buses so adding an additional deferral seems odd. This code seems likely to cause problems since it's not synchronised with userspace at all (the ASoC core stuff is) which means that applications may start trying to use the device before the work has completed.


> +	adau1381->in_source = CAP_MIC; /*default is mic input*/
> +	adau1381->sysclk = ADAU1381_MCLK_RATE;
> +	adau1381->pll_out = ADAU1381_PLL_FREQ_48;
> +	adau1381->dapm_lineL = DAPM_LINE_DEF;
> +	adau1381->dapm_lineR = DAPM_LINE_DEF;
> +	adau1381->dapm_hpL = DAPM_HP_DEF;
> +	adau1381->dapm_hpR = DAPM_HP_DEF;

As with your other drivers you should use chip defaults.

> +struct adau1381_setup_data {
> +	unsigned short i2c_bus;
> +	unsigned short i2c_address;
> +};

This shouldn't be here.

> +#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

This should be controlled at runtime via set_dai_fmt(). There's also a namespace pollution issue with the define, but given that it shouldn't be there in the first place...


> +/*
> + * Reset Mode - ADC capture/DAC playback
> + * (AInput mixers 0db, AOuput mixers 0db, HP out ON)
> +*/
> +static struct adau1381_mode_register adau1381_reset[RESET_REGISTER_COUNT] = {
> +	/* mute outputs */
> +	{ADAU_RGUCTRL, 0x00},

This and all the other tables need to be in the body of the driver rather than the header file. I'm somewhat surprised the compiler isn't complaining about this stuff.

> +	{ADAU_SPRTCT1, 0x21}, /*0x21 = 32bclocks frame, 0x41 = 48*/

Is this not the chip default?

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 1/6] ASoC: add ADAU1361 codec driver
  2010-08-07 20:28 [PATCH 1/6] ASoC: add ADAU1361 codec driver Mike Frysinger
                   ` (3 preceding siblings ...)
  2010-08-07 21:20 ` [PATCH 1/6] ASoC: add ADAU1361 codec driver Liam Girdwood
@ 2010-08-07 22:06 ` Mark Brown
  4 siblings, 0 replies; 18+ messages in thread
From: Mark Brown @ 2010-08-07 22:06 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: uclinux-dist-devel, alsa-devel, Cliff Cai

On 7 Aug 2010, at 21:28, Mike Frysinger <vapier@gentoo.org> wrote:

> +#define AUDIO_NAME "adau1361"
> +#define ADAU1361_VERSION "0.1"

As ever, remove the version numbers and you need to convert to mullet-component.

> +#define CAP_MIC  1
> +#define CAP_LINE 2
> +#define CAPTURE_SOURCE_NUMBER 2

This looks rather like more machine driver stuff.

> +	/* dapm */
> +	u8 dapm_lineL;
> +	u8 dapm_lineR;
> +	u8 dapm_hpL;
> +	u8 dapm_hpR;

> +	addr[0] = (u8)(reg >> 8);

I suspect the casts are redundant.


> +	if (i2c_master_recv(codec->control_data, buf, len) != len)
> +		return -EIO;

Pass back the error code if you got one.

> +	if (codec->hw_write(codec->control_data, buf, count) == count)
> +		return 0;
> +	else {
> +		dev_err(codec->dev, "address block write failed.");
> +		return -EIO;

Likewise.

> +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);

As well as the issue with the configuration of the digital microphone with an ifdef rather than a runtime selection it looks rather like this register configuration is doing a bunch of stuff like configuring volume controls and power which I'd rather expect to be done elsewhere.

> +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;
> +}

I'm having a hard time seeing why you're not just using standard controls here. You've also got a mono control exposed to the user but apparently stereo control in the chip - that seems restrictive for machine drivers (for example, a system might have a mono microphone plus a line input).

> 
> +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);

Ick, no. Your mute control should be muting and unmuting the DAC only. Quite apart from anything else this is going to leave the inputs muted unless there's an active playback which seems likely to cause problems during recordings.


> +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);

Why interruptible?

> +		if (counter >= 20)
> +			return -1;

Return proper error codes.


> +	/* 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);

Use chip defaults (this also applies to the register setting above).

> +	/* initialize the PLL */
> +	if (adau1361_pll_init(codec) != 0)
> +		return -EINVAL;

Pass back the error code.

> +	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;
> +	}

Return an error if the mode is unsupported here and elsewhere.

> +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;
> +

Similar comments to the previous driver here - use the default for the switch statement and your comment is bit. I've not made further comments on this but many of the issues in the driver I previously reviewed also apply here.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 5/6] ASoC: new ADAU1761 codec driver
  2010-08-07 20:28   ` [PATCH 5/6] ASoC: new ADAU1761 codec driver Mike Frysinger
@ 2010-08-07 22:16     ` Mark Brown
  2010-08-07 22:29       ` [Uclinux-dist-devel] " Mike Frysinger
  0 siblings, 1 reply; 18+ messages in thread
From: Mark Brown @ 2010-08-07 22:16 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: uclinux-dist-devel, alsa-devel, Cliff Cai

On 7 Aug 2010, at 21:28, Mike Frysinger <vapier@gentoo.org> wrote:

> +config SND_SOC_ADAU1761
> +	tristate
> +	select SIGMA
> +

What is SIGMA?

> +#define CAP_MIC  1
> +#define CAP_LINE 2
> +#define CAPTURE_SOURCE_NUMBER 2
> +#define ADAU1761_DIG_MIC 0

So, it looks like there's an awful lot of similarity between this and some of the other drivers. I'm going to skip a lot of repetitive comments but please assume that points which can be applied to this driver do apply.

> +static const struct snd_kcontrol_new adau1761_right_mixer_controls[] = {
> +SOC_DAPM_SINGLE("LineRight Bypass Switch", ADAU_PLBLOVR-ADAU_FIRSTREG, 1, 1, 0),
> +SOC_DAPM_SINGLE("HPRight Bypass Switch", ADAU_PLBHPVR-ADAU_FIRSTREG, 1, 1, 0),
> +};

Remember that the name of the mixer will be prepended to these - you can probably drop the Rights.

> +	snd_soc_write(codec, ADAU_DSP_RUN, 0x0);
> +	reg = snd_soc_read(codec, ADAU_CLKCTRL);
> +	snd_soc_write(codec, ADAU_CLKCTRL, reg & ~0x1);

I suspect the DSP power may warrant being managed via DAPM.

> +static ssize_t adau1371_dsp_load(struct device *dev,
> +				  struct device_attribute *attr,
> +				  const char *buf, size_t count)
> +{
> +	struct snd_soc_device *socdev = dev_get_drvdata(dev);
> +	struct snd_soc_codec *codec = socdev->card->codec;
> +	int ret = 0;
> +
> +	ret = adau1761_setprogram(codec);
> +	if (ret)
> +		return ret;
> +	else
> +		return count;
> +}
> +static DEVICE_ATTR(dsp, 0644, NULL, adau1371_dsp_load);

This looks redundant - it doesn't offer any opportunity to configure the firmware to download and you're already (as one one would expect) automatically downloading the firmware. If this is being exposed at all it should be via debugfs.

The permissions also look wrong given that you don't have a read function.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [Uclinux-dist-devel] [PATCH 2/6] ASoC: Blackfin: new machine driver for ADAU1361 codecs
  2010-08-07 21:14       ` [Uclinux-dist-devel] " Mike Frysinger
@ 2010-08-07 22:29         ` Mark Brown
  2010-08-07 22:31           ` Mike Frysinger
  0 siblings, 1 reply; 18+ messages in thread
From: Mark Brown @ 2010-08-07 22:29 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: uclinux-dist-devel, alsa-devel, Cliff Cai, Liam Girdwood

On 7 Aug 2010, at 22:14, Mike Frysinger <vapier.adi@gmail.com> wrote:

> On Sat, Aug 7, 2010 at 17:08, Mark Brown wrote:

>> It'd be really helpful if you could do a bit more pre-review on the stuff you're posting, especially where you've got a bunch of drivers that follow similar patterns and are going to generate exactly the same feedback. Posting a large batch of patches at once isn't a problem but if you're sending additional patches later on it'd be helpful to take into account the feedback that has already been given on prior serieses.
> 

> probably, but i'm not familiar with the ASoC stuff to fix/review any
> of this.  i'll open another tracker item for these codecs.

It'd probably be enough to just avoid posting additional batches of patches without incorporating previous review, either by just posting everything in one series or by holding off on later ones after getting the first batch of review. The reason I mentioned this was that I'd already reviewed previous postings (and seen that you'd read my review) but was noticing many of the same issues cropping up in the new postings.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [Uclinux-dist-devel] [PATCH 5/6] ASoC: new ADAU1761 codec driver
  2010-08-07 22:16     ` Mark Brown
@ 2010-08-07 22:29       ` Mike Frysinger
  2010-08-07 22:36         ` Mark Brown
  0 siblings, 1 reply; 18+ messages in thread
From: Mike Frysinger @ 2010-08-07 22:29 UTC (permalink / raw)
  To: Mark Brown; +Cc: uclinux-dist-devel, alsa-devel, Cliff Cai

On Sat, Aug 7, 2010 at 18:16, Mark Brown wrote:
> On 7 Aug 2010, at 21:28, Mike Frysinger wrote:
>> +config SND_SOC_ADAU1761
>> +     tristate
>> +     select SIGMA
>> +
>
> What is SIGMA?

it's a new firmware loader that's standardized across SigmaDSP parts.
i (correctly) assumed this driver wasnt going to be accepted right
away, so i wanted to get feedback in parallel to the firmware loader
being merged.

>> +static ssize_t adau1371_dsp_load(struct device *dev,
>> +                               struct device_attribute *attr,
>> +                               const char *buf, size_t count)
>> +{
>> +     struct snd_soc_device *socdev = dev_get_drvdata(dev);
>> +     struct snd_soc_codec *codec = socdev->card->codec;
>> +     int ret = 0;
>> +
>> +     ret = adau1761_setprogram(codec);
>> +     if (ret)
>> +             return ret;
>> +     else
>> +             return count;
>> +}
>> +static DEVICE_ATTR(dsp, 0644, NULL, adau1371_dsp_load);
>
> This looks redundant - it doesn't offer any opportunity to configure the firmware to download and you're already (as one one would expect) automatically downloading the firmware. If this is being exposed at all it should be via debugfs.

SigmaDSP parts have the ability to change firmwares on the fly.  this
isnt a debug feature, it's somewhat core to these parts.  idea being
that you can load up different mini signal processing engines based on
the current needs.
-mike
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [Uclinux-dist-devel] [PATCH 2/6] ASoC: Blackfin: new machine driver for ADAU1361 codecs
  2010-08-07 22:29         ` Mark Brown
@ 2010-08-07 22:31           ` Mike Frysinger
  0 siblings, 0 replies; 18+ messages in thread
From: Mike Frysinger @ 2010-08-07 22:31 UTC (permalink / raw)
  To: Mark Brown; +Cc: uclinux-dist-devel, alsa-devel, Cliff Cai, Liam Girdwood

On Sat, Aug 7, 2010 at 18:29, Mark Brown wrote:
> On 7 Aug 2010, at 22:14, Mike Frysinger wrote:
>> On Sat, Aug 7, 2010 at 17:08, Mark Brown wrote:
>>> It'd be really helpful if you could do a bit more pre-review on the stuff you're posting, especially where you've got a bunch of drivers that follow similar patterns and are going to generate exactly the same feedback. Posting a large batch of patches at once isn't a problem but if you're sending additional patches later on it'd be helpful to take into account the feedback that has already been given on prior serieses.
>>
>>
>> probably, but i'm not familiar with the ASoC stuff to fix/review any
>> of this.  i'll open another tracker item for these codecs.
>
> It'd probably be enough to just avoid posting additional batches of patches without incorporating previous review, either by just posting everything in one series or by holding off on later ones after getting the first batch of review. The reason I mentioned this was that I'd already reviewed previous postings (and seen that you'd read my review) but was noticing many of the same issues cropping up in the new postings.

i'd like to have sent these out as one batch, but it takes time for me
to clean up the commits being sent to me.  you'd probably barf all
over the changesets that i have to process ;).

i dont have any other codec drivers atm though.  just bug fixes that i
need to further clean up/split/document/etc...
-mike
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [Uclinux-dist-devel] [PATCH 5/6] ASoC: new ADAU1761 codec driver
  2010-08-07 22:29       ` [Uclinux-dist-devel] " Mike Frysinger
@ 2010-08-07 22:36         ` Mark Brown
  2010-08-07 22:41           ` Mike Frysinger
  0 siblings, 1 reply; 18+ messages in thread
From: Mark Brown @ 2010-08-07 22:36 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: uclinux-dist-devel, alsa-devel, Cliff Cai

On 7 Aug 2010, at 23:29, Mike Frysinger <vapier.adi@gmail.com> wrote:

>>> +static ssize_t adau1371_dsp_load(struct device *dev,
>>> +                               struct device_attribute *attr,
>>> +                               const char *buf, size_t count)
>>> +{
>>> +     struct snd_soc_device *socdev = dev_get_drvdata(dev);
>>> +     struct snd_soc_codec *codec = socdev->card->codec;
>>> +     int ret = 0;
>>> +
>>> +     ret = adau1761_setprogram(codec);
>>> +     if (ret)
>>> +             return ret;
>>> +     else
>>> +             return count;
>>> +}
>>> +static DEVICE_ATTR(dsp, 0644, NULL, adau1371_dsp_load);

>> This looks redundant - it doesn't offer any opportunity to configure the firmware to download and you're already (as one one would expect) automatically downloading the firmware. If this is being exposed at all it should be via debugfs.

> SigmaDSP parts have the ability to change firmwares on the fly.  this
> isnt a debug feature, it's somewhat core to these parts.  idea being
> that you can load up different mini signal processing engines based on
> the current needs.

Right, but see my point about there being no opportunity to configure the firmware - if you were allowing the user to select between multiple firmwares available then I expect that the value written into the sysfs file would be parsed as the name of the new firmware file to load or otherwise select a new firmware. With the current code the value written is totally ignored and the application layer has to overwrite the firmware in the filesystem (or have a custom helper program which does the equivalent thereof) so as far as the kernel is concerned it's going to reload the same firmware which isn't ideal.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [Uclinux-dist-devel] [PATCH 5/6] ASoC: new ADAU1761 codec driver
  2010-08-07 22:36         ` Mark Brown
@ 2010-08-07 22:41           ` Mike Frysinger
  2010-08-07 23:10             ` Mark Brown
  0 siblings, 1 reply; 18+ messages in thread
From: Mike Frysinger @ 2010-08-07 22:41 UTC (permalink / raw)
  To: Mark Brown; +Cc: uclinux-dist-devel, alsa-devel, Cliff Cai

On Sat, Aug 7, 2010 at 18:36, Mark Brown wrote:
> On 7 Aug 2010, at 23:29, Mike Frysinger wrote:
>>>> +static ssize_t adau1371_dsp_load(struct device *dev,
>>>> +                               struct device_attribute *attr,
>>>> +                               const char *buf, size_t count)
>>>> +{
>>>> +     struct snd_soc_device *socdev = dev_get_drvdata(dev);
>>>> +     struct snd_soc_codec *codec = socdev->card->codec;
>>>> +     int ret = 0;
>>>> +
>>>> +     ret = adau1761_setprogram(codec);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +     else
>>>> +             return count;
>>>> +}
>>>> +static DEVICE_ATTR(dsp, 0644, NULL, adau1371_dsp_load);
>>>
>>> This looks redundant - it doesn't offer any opportunity to configure the
>>> firmware to download and you're already (as one one would expect)
>>> automatically downloading the firmware. If this is being exposed at all it
>>> should be via debugfs.
>>
>> SigmaDSP parts have the ability to change firmwares on the fly.  this
>> isnt a debug feature, it's somewhat core to these parts.  idea being
>> that you can load up different mini signal processing engines based on
>> the current needs.
>
> Right, but see my point about there being no opportunity to configure the
> firmware - if you were allowing the user to select between multiple
> firmwares available then I expect that the value written into the sysfs file
> would be parsed as the name of the new firmware file to load or otherwise
> select a new firmware. With the current code the value written is totally
> ignored and the application layer has to overwrite the firmware in the
> filesystem (or have a custom helper program which does the equivalent
> thereof) so as far as the kernel is concerned it's going to reload the same
> firmware which isn't ideal.

yes, that is the current limitation that'll need to be fixed.  not a
big hassle for us to replace the file when doing initial testing ;).
-mike
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [Uclinux-dist-devel] [PATCH 5/6] ASoC: new ADAU1761 codec driver
  2010-08-07 22:41           ` Mike Frysinger
@ 2010-08-07 23:10             ` Mark Brown
  0 siblings, 0 replies; 18+ messages in thread
From: Mark Brown @ 2010-08-07 23:10 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: uclinux-dist-devel, alsa-devel, Cliff Cai

On 7 Aug 2010, at 23:41, Mike Frysinger <vapier.adi@gmail.com> wrote:

> yes, that is the current limitation that'll need to be fixed.  not a
> big hassle for us to replace the file when doing initial testing ;).

Sure, which is why I say that if you're going to expose this feature in this manner it should be via debugfs. That's the place for this sort of development only hack.

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2010-08-07 23:10 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-08-07 20:28 [PATCH 1/6] ASoC: add ADAU1361 codec driver Mike Frysinger
     [not found] ` <1281212905-12957-1-git-send-email-vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
2010-08-07 20:28   ` [PATCH 2/6] ASoC: Blackfin: new machine driver for ADAU1361 codecs Mike Frysinger
2010-08-07 21:08     ` Mark Brown
2010-08-07 21:14       ` [Uclinux-dist-devel] " Mike Frysinger
2010-08-07 22:29         ` Mark Brown
2010-08-07 22:31           ` Mike Frysinger
2010-08-07 20:28   ` [PATCH 5/6] ASoC: new ADAU1761 codec driver Mike Frysinger
2010-08-07 22:16     ` Mark Brown
2010-08-07 22:29       ` [Uclinux-dist-devel] " Mike Frysinger
2010-08-07 22:36         ` Mark Brown
2010-08-07 22:41           ` Mike Frysinger
2010-08-07 23:10             ` Mark Brown
2010-08-07 20:28   ` [PATCH 6/6] ASoC: Blackfin: new machine driver for ADAU1761 codecs Mike Frysinger
2010-08-07 20:28 ` [PATCH 3/6] ASoC: new ADAU1381 codec driver Mike Frysinger
2010-08-07 21:43   ` Mark Brown
2010-08-07 20:28 ` [PATCH 4/6] ASoC: Blackfin: new machine driver for ADAU1381 codecs Mike Frysinger
2010-08-07 21:20 ` [PATCH 1/6] ASoC: add ADAU1361 codec driver Liam Girdwood
2010-08-07 22:06 ` Mark Brown

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.