Title: [7448] trunk/sound/soc: task[#5443]add ADAU1371 support

Diff

Modified: trunk/sound/soc/blackfin/Kconfig (7447 => 7448)


--- trunk/sound/soc/blackfin/Kconfig	2009-09-24 05:44:36 UTC (rev 7447)
+++ trunk/sound/soc/blackfin/Kconfig	2009-09-24 09:48:30 UTC (rev 7448)
@@ -24,6 +24,15 @@
 	help
 	  Say Y if you want to add support for AD73311 codec on Blackfin.
 
+config SND_BF5XX_SOC_ADAU1371
+	tristate "SoC ADAU1371 Audio support"
+	depends on SND_BF5XX_I2S
+	select SND_BF5XX_SOC_I2S
+	select SND_SOC_ADAU1371
+	select I2C
+	help
+	  Say Y if you want to add support for ADAU1371 SoC audio.
+
 config SND_BFIN_AD73311_SE
 	int "PF pin for AD73311L Chip Select"
 	depends on SND_BF5XX_SOC_AD73311

Modified: trunk/sound/soc/blackfin/Makefile (7447 => 7448)


--- trunk/sound/soc/blackfin/Makefile	2009-09-24 05:44:36 UTC (rev 7447)
+++ trunk/sound/soc/blackfin/Makefile	2009-09-24 09:48:30 UTC (rev 7448)
@@ -21,9 +21,11 @@
 snd-ssm2602-objs := bf5xx-ssm2602.o
 snd-ad73311-objs := bf5xx-ad73311.o
 snd-ad1938-objs := bf5xx-ad1938.o
+snd-adau1371-objs := bf5xx-adau1371.o
 
 obj-$(CONFIG_SND_BF5XX_SOC_AD1836) += snd-ad1836.o
 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_AD1938) += snd-ad1938.o
+obj-$(CONFIG_SND_BF5XX_SOC_ADAU1371) += snd-adau1371.o

Added: trunk/sound/soc/blackfin/bf5xx-adau1371.c (0 => 7448)


--- trunk/sound/soc/blackfin/bf5xx-adau1371.c	                        (rev 0)
+++ trunk/sound/soc/blackfin/bf5xx-adau1371.c	2009-09-24 09:48:30 UTC (rev 7448)
@@ -0,0 +1,162 @@
+/*
+ * File:         sound/soc/blackfin/bf5xx-adau1371.c
+ * Author:       Cliff Cai <[email protected]>
+ *
+ * Created:      Tue June 06 2008
+ * Description:  board driver for adau1371 sound chip
+ *
+ * Modified:
+ *               Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#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/adau1371.h"
+#include "bf5xx-sport.h"
+#include "bf5xx-i2s-pcm.h"
+#include "bf5xx-i2s.h"
+
+static struct snd_soc_card bf5xx_adau1371;
+
+static int bf5xx_adau1371_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+	pr_debug("%s enter\n", __func__);
+	cpu_dai->private_data = sport_handle;
+	return 0;
+}
+
+static int bf5xx_adau1371_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;
+	int ret = 0;
+	/* Change input crystal source here */
+	unsigned int clk = 12288000;
+	/* unsigned int clk = 11289600; */
+	pr_debug("%s rate %d format %x\n", __func__, params_rate(params),
+		params_format(params));
+	/*
+	 * If you are using the SPORT to generate clocking then this is
+	 * where to do it.
+	 */
+
+	/*
+	 * CODEC is master for BCLK and LRC in this configuration.
+	 */
+	/* set codec DAI configuration */
+	ret = snd_soc_dai_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 = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, 0);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct snd_soc_ops bf5xx_adau1371_ops = {
+	.startup = bf5xx_adau1371_startup,
+	.hw_params = bf5xx_adau1371_hw_params,
+};
+
+static struct snd_soc_dai_link bf5xx_adau1371_dai = {
+	.name = "adau1371",
+	.stream_name = "adau1371",
+	.cpu_dai = &bf5xx_i2s_dai,
+	.codec_dai = &adau1371_dai,
+	.ops = &bf5xx_adau1371_ops,
+};
+
+static struct adau1371_setup_data bf5xx_adau1371_setup = {
+	.i2c_bus = 0,
+	.i2c_address = 0x1a,
+};
+
+static struct snd_soc_card bf5xx_adau1371 = {
+	.name = "bf5xx_adau1371",
+	.platform = &bf5xx_i2s_soc_platform,
+	.dai_link = &bf5xx_adau1371_dai,
+	.num_links = 1,
+};
+
+static struct snd_soc_device bf5xx_adau1371_snd_devdata = {
+	.card = &bf5xx_adau1371,
+	.codec_dev = &soc_codec_dev_adau1371,
+	.codec_data = &bf5xx_adau1371_setup,
+};
+
+static struct platform_device *bf5xx_adau1371_snd_device;
+
+static int __init bf5xx_adau1371_init(void)
+{
+	int ret;
+
+	pr_debug("%s enter\n", __func__);
+	bf5xx_adau1371_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!bf5xx_adau1371_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(bf5xx_adau1371_snd_device,
+				&bf5xx_adau1371_snd_devdata);
+	bf5xx_adau1371_snd_devdata.dev = &bf5xx_adau1371_snd_device->dev;
+	ret = platform_device_add(bf5xx_adau1371_snd_device);
+
+	if (ret)
+		platform_device_put(bf5xx_adau1371_snd_device);
+
+	return ret;
+}
+
+static void __exit bf5xx_adau1371_exit(void)
+{
+	pr_debug("%s enter\n", __func__);
+	platform_device_unregister(bf5xx_adau1371_snd_device);
+}
+
+module_init(bf5xx_adau1371_init);
+module_exit(bf5xx_adau1371_exit);
+
+/* Module information */
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("ALSA SoC adau1371");
+MODULE_LICENSE("GPL");
+

Modified: trunk/sound/soc/codecs/Kconfig (7447 => 7448)


--- trunk/sound/soc/codecs/Kconfig	2009-09-24 05:44:36 UTC (rev 7447)
+++ trunk/sound/soc/codecs/Kconfig	2009-09-24 09:48:30 UTC (rev 7448)
@@ -48,6 +48,7 @@
 	select SND_SOC_WM9705 if SND_SOC_AC97_BUS
 	select SND_SOC_WM9712 if SND_SOC_AC97_BUS
 	select SND_SOC_WM9713 if SND_SOC_AC97_BUS
+	select SND_SOC_ADAU1371 if I2C
         help
           Normally ASoC codec drivers are only built if a machine driver which
           uses them is also built since they are only usable with a machine
@@ -184,3 +185,6 @@
 
 config SND_SOC_WM9713
 	tristate
+
+config SND_SOC_ADAU1371
+        tristate

Modified: trunk/sound/soc/codecs/Makefile (7447 => 7448)


--- trunk/sound/soc/codecs/Makefile	2009-09-24 05:44:36 UTC (rev 7447)
+++ trunk/sound/soc/codecs/Makefile	2009-09-24 09:48:30 UTC (rev 7448)
@@ -36,6 +36,7 @@
 snd-soc-wm9705-objs := wm9705.o
 snd-soc-wm9712-objs := wm9712.o
 snd-soc-wm9713-objs := wm9713.o
+snd-soc-adau1371-objs := adau1371.o
 
 obj-$(CONFIG_SND_SOC_AC97_CODEC)	+= snd-soc-ac97.o
 obj-$(CONFIG_SND_SOC_AD1836)	+= snd-soc-ad1836.o
@@ -75,3 +76,4 @@
 obj-$(CONFIG_SND_SOC_WM9705)	+= snd-soc-wm9705.o
 obj-$(CONFIG_SND_SOC_WM9712)	+= snd-soc-wm9712.o
 obj-$(CONFIG_SND_SOC_WM9713)	+= snd-soc-wm9713.o
+obj-$(CONFIG_SND_SOC_ADAU1371)	+= snd-soc-adau1371.o

Added: trunk/sound/soc/codecs/adau1371.c (0 => 7448)


--- trunk/sound/soc/codecs/adau1371.c	                        (rev 0)
+++ trunk/sound/soc/codecs/adau1371.c	2009-09-24 09:48:30 UTC (rev 7448)
@@ -0,0 +1,1105 @@
+/*
+ * File:         sound/soc/codecs/adau1371.c
+ * Author:       Cliff Cai <[email protected]>
+ *
+ * Created:      Thu September 03 2009
+ * Description:  Driver for adau1371 sound chip
+ *
+ * Modified:
+ *               Copyright 2009 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#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/platform_device.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 "adau1371.h"
+
+#define adau1371_VERSION "0.1"
+
+struct snd_soc_codec_device soc_codec_dev_adau1371;
+
+struct adau1371_priv {
+	unsigned int sysclk;
+	unsigned int out_chan_mask;
+	unsigned int in_chan_mask;
+};
+/*
+ * adau1371 register cache
+ * We can't read the adau1371 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ * There is no point in caching the reset register
+ */
+static const u8 adau1371_reg[ADAU1371_CACHEREGNUM] = {
+	0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, /* 0x00-0x07 */
+	0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x00, /* 0x08-0x0f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+	0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  /* 0xf8-0xff */
+};
+
+struct _pll_settings {
+	u32 mclk;
+	u32 rate;
+	u16 n;	/* N and M registers are inverted on REVB*/
+	u16 m;
+	u8 input_div:2;
+	u8 integer:4;
+	u8 type:1;
+};
+
+/* PLL settings coefficients, add more here... */
+static const struct _pll_settings pll_settings[] = {
+	/* 96k */
+	/* {12288000, 96000, 0, 0, 0x0, 0x8, 0x0}, */
+	/* 88.2k */
+	/* {12288000, 88200, 20, 7, 0x0, 0x7, 0x1}, */
+	/* 48k */
+	{12288000, 48000, 0, 0, 0x0, 0x4, 0x0},
+	/* 44.1k */
+	{12288000, 44100,  40, 27, 0x0, 0x3, 0x1},
+	{11289600, 44100, 0, 0, 0x0, 0x4, 0x0},
+	/* 22.050k */
+	{12288000, 22050,  40, 27, 0x1, 0x3, 0x1},
+	{11289600, 22050, 0, 0, 0x0, 0x2, 0x0},
+	/* 8k */
+	/* {12288000, 8000, 3, 2, 0x3, 0x2, 0x1}, */
+
+};
+
+/*
+ * read adau1371 register cache
+ */
+static inline unsigned int adau1371_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u8 *cache = codec->reg_cache;
+	if (reg == ADAU1371_RESET)
+		return 0;
+	if (reg > ADAU1371_CACHEREGNUM)
+		return -1;
+	return cache[reg];
+}
+
+/*
+ * write adau1371 register cache
+ */
+static inline void adau1371_write_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg, u8 value)
+{
+	u8 *cache = codec->reg_cache;
+	if (reg >= ADAU1371_CACHEREGNUM)
+		return;
+	cache[reg] = value;
+}
+
+/*
+ * write to the adau1371 register space
+ */
+static int adau1371_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	u8 data[2];
+
+	data[0] = reg;
+	data[1] = value;
+	adau1371_write_reg_cache(codec, reg, value);
+	if (codec->hw_write(codec->control_data, data, 2) == 2)
+		return 0;
+	else	{
+		printk(KERN_ERR "I2C bus error\n");
+		return -EIO;
+	}
+}
+
+static int adau1371_volume_info(struct snd_kcontrol *kcontrol,
+						struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = CHANNELS_OUTPUT;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 31;
+	return 0;
+}
+
+
+static int adau1371_volume_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int i;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct adau1371_priv *chip = codec->private_data;
+	u8 *cache = codec->reg_cache;
+
+	for (i = 0; i < CHANNELS_OUTPUT; ++i) {
+		if (chip->out_chan_mask & PB_LINE)
+			ucontrol->value.integer.value[i] =
+				cache[ADAU1371_LLINEVOL+i];
+		if (chip->out_chan_mask & PB_CD)
+			ucontrol->value.integer.value[i] =
+				cache[ADAU1371_LCDVOL+i];
+		if (chip->out_chan_mask & PB_HP)
+			ucontrol->value.integer.value[i] =
+				cache[ADAU1371_LHPVOL+i];
+	}
+
+	return 0;
+}
+
+
+static int adau1371_volume_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+
+	int i;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct adau1371_priv *chip = codec->private_data;
+	int change = 0;
+	u8 *cache = codec->reg_cache;
+
+	for (i = 0; i < CHANNELS_OUTPUT; ++i) {
+		int vol  = ucontrol->value.integer.value[i];
+		if (vol < 0)
+			vol = 0;
+		if (vol > 31)
+			vol = 31;
+		if (chip->out_chan_mask & PB_LINE) {
+			if (cache[ADAU1371_LLINEVOL+i] != vol) {
+				change = 1;
+				adau1371_write(codec, ADAU1371_LLINEVOL + i, vol);
+			}
+		} else if (chip->out_chan_mask & PB_CD) {
+			if (cache[ADAU1371_LCDVOL+i] != vol) {
+				change = 1;
+				adau1371_write(codec, ADAU1371_LCDVOL+i, vol);
+			}
+		} else if (chip->out_chan_mask & PB_HP) {
+			if (cache[ADAU1371_LHPVOL+i] != vol) {
+				change = 1;
+				adau1371_write(codec, ADAU1371_LHPVOL + i, vol);
+			}
+		}
+	}
+
+	return change;
+}
+
+static int adau1371_cap_volume_info(struct snd_kcontrol *kcontrol,
+						struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = CHANNELS_INPUT;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 31;
+	return 0;
+}
+
+static int adau1371_cap_volume_get(struct snd_kcontrol *kcontrol,
+						struct snd_ctl_elem_value *ucontrol)
+{
+	int i;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct adau1371_priv *chip = codec->private_data;
+	u8 *cache = codec->reg_cache;
+
+	for (i = 0; i < CHANNELS_INPUT; ++i) {
+		if (chip->in_chan_mask & CAP_INPA)
+			ucontrol->value.integer.value[i] =
+				cache[ADAU1371_INALVOL+i];
+		else if (chip->in_chan_mask & CAP_INPB)
+				ucontrol->value.integer.value[i] =
+				cache[ADAU1371_INBLVOL+i];
+		else if (chip->in_chan_mask & CAP_INPC)
+				ucontrol->value.integer.value[i] =
+				cache[ADAU1371_INCLVOL+i];
+		else if (chip->in_chan_mask & CAP_INPD)
+				ucontrol->value.integer.value[i] =
+				cache[ADAU1371_INDLVOL+i];
+	}
+	return 0;
+}
+
+static int adau1371_cap_volume_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int i;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct adau1371_priv *chip = codec->private_data;
+	int change = 0;
+	u8 *cache = codec->reg_cache;
+
+	for (i = 0; i < CHANNELS_INPUT; ++i) {
+		int vol  = ucontrol->value.integer.value[i];
+		if (vol < 0)
+			vol = 0;
+		if (vol > 31)
+			vol = 31;
+
+		if (chip->in_chan_mask & CAP_INPA) {
+			if (cache[ADAU1371_INALVOL+i] != vol) {
+				change = 1;
+				adau1371_write(codec, ADAU1371_INALVOL + i, vol);
+			}
+		} else if (chip->in_chan_mask & CAP_INPB) {
+			if (cache[ADAU1371_INBLVOL+i] != vol) {
+				change = 1;
+				adau1371_write(codec, ADAU1371_INBLVOL+i, vol);
+			}
+		} else if (chip->in_chan_mask & CAP_INPC) {
+			if (cache[ADAU1371_INCLVOL+i] != vol) {
+				change = 1;
+				adau1371_write(codec, ADAU1371_INCLVOL+i, vol);
+			}
+		} else if (chip->in_chan_mask & CAP_INPD) {
+			if (cache[ADAU1371_INDLVOL+i] != vol) {
+				change = 1;
+				adau1371_write(codec, ADAU1371_INDLVOL+i, vol);
+			}
+		}
+	}
+
+	return change;
+}
+
+static int adau1371_play_mute_info(struct snd_kcontrol *kcontrol,
+						struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = CHANNELS_OUTPUT;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int adau1371_play_mute_get(struct snd_kcontrol *kcontrol,
+						struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	u8 reg = adau1371_read_reg_cache(codec, ADAU1371_PWRCTLA);
+	int curr =  (reg & DAC_MUTE_MASK) >> 4 ;
+	int i;
+	for (i = 0; i < CHANNELS_OUTPUT; ++i)
+		ucontrol->value.integer.value[i] =
+			(curr & (1 << i)) ? 1 : 0;
+	return 0;
+}
+
+static int adau1371_play_mute_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	int mute = 0;
+	int i;
+	u8 reg = adau1371_read_reg_cache(codec, ADAU1371_PWRCTLB);
+	int curr =  (reg & DAC_MUTE_MASK) >> 4 ;
+
+	for (i = 0; i < CHANNELS_OUTPUT; ++i)
+		if (ucontrol->value.integer.value[i])
+			mute |= (1<<i);
+
+	if (curr != mute) {
+		adau1371_write(codec, ADAU1371_PWRCTLB, (reg & ~DAC_MUTE_MASK) | mute << 4);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int adau1371_cap_mute_info(struct snd_kcontrol *kcontrol,
+						struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = CHANNELS_INPUT;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int adau1371_cap_mute_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	u8 reg = adau1371_read_reg_cache(codec, ADAU1371_PWRCTLA);
+	int curr =  (reg & ADC_MUTE_MASK) >> 6 ;
+	int i;
+	for (i = 0; i < CHANNELS_INPUT; ++i)
+		ucontrol->value.integer.value[i] =
+			(curr & (1 << i)) ? 1 : 0;
+	return 0;
+}
+
+static int adau1371_cap_mute_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	int mute = 0;
+	int i;
+	u8 reg = adau1371_read_reg_cache(codec, ADAU1371_PWRCTLA);
+	int curr =  (reg & ADC_MUTE_MASK) >> 6 ;
+
+	for (i = 0; i < CHANNELS_INPUT; ++i)
+		if (ucontrol->value.integer.value[i])
+			mute |= (1<<i);
+
+	if (curr != mute) {
+		adau1371_write(codec, ADAU1371_PWRCTLA, (reg & ~ADC_MUTE_MASK) | mute << 6);
+		return 1;
+	}
+
+	return 0;
+
+}
+
+
+#define CAPTURE_SOURCE_NUMBER 4
+
+static int adau1371_mux_info(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[CAPTURE_SOURCE_NUMBER] = {
+		"INPA", "INPB", "INPC", "INPD"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = CAPTURE_SOURCE_NUMBER;
+	if (uinfo->value.enumerated.item >= CAPTURE_SOURCE_NUMBER)
+		uinfo->value.enumerated.item = CAPTURE_SOURCE_NUMBER - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int adau1371_mux_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct adau1371_priv *chip = codec->private_data;
+
+	if (chip->in_chan_mask & CAP_INPA)
+		ucontrol->value.integer.value[0] = 0;
+	else if (chip->in_chan_mask & CAP_INPB)
+		ucontrol->value.integer.value[0] = 1;
+	else if (chip->in_chan_mask & CAP_INPC)
+		ucontrol->value.integer.value[0] = 2;
+	else if (chip->in_chan_mask & CAP_INPD)
+		ucontrol->value.integer.value[0] = 3;
+
+	return 0;
+}
+
+static int adau1371_mux_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct adau1371_priv *chip = codec->private_data;
+	u8 *cache = codec->reg_cache;
+	int i;
+	u8 reg;
+
+	reg = adau1371_read_reg_cache(codec, ADAU1371_PWRCTLA);
+	i = ucontrol->value.integer.value[0];
+	if (i == 0) {
+		chip->in_chan_mask = CAP_INPA;
+		adau1371_write(codec, ADAU1371_LADCMIX, AIN1_SIGNAL_ENA);
+		adau1371_write(codec, ADAU1371_RADCMIX, AIN1_SIGNAL_ENA);
+		adau1371_write(codec, ADAU1371_INALVOL, cache[ADAU1371_INALVOL]);
+		adau1371_write(codec, ADAU1371_INARVOL, cache[ADAU1371_INARVOL]);
+		adau1371_write(codec, ADAU1371_PWRCTLA, reg | PWRCTLA_INAPD);
+	} else if (i == 1) {
+		chip->in_chan_mask = CAP_INPB;
+		adau1371_write(codec, ADAU1371_LADCMIX, AIN2_SIGNAL_ENA);
+		adau1371_write(codec, ADAU1371_RADCMIX, AIN2_SIGNAL_ENA);
+		adau1371_write(codec, ADAU1371_INBLVOL, cache[ADAU1371_INBLVOL]);
+		adau1371_write(codec, ADAU1371_INBRVOL, cache[ADAU1371_INBRVOL]);
+		adau1371_write(codec, ADAU1371_PWRCTLA, reg | PWRCTLA_INBPD);
+	} else if (i == 2) {
+		chip->in_chan_mask = CAP_INPC;
+		adau1371_write(codec, ADAU1371_LADCMIX, AIN3_SIGNAL_ENA);
+		adau1371_write(codec, ADAU1371_RADCMIX, AIN3_SIGNAL_ENA);
+		adau1371_write(codec, ADAU1371_INCLVOL, cache[ADAU1371_INCLVOL]);
+		adau1371_write(codec, ADAU1371_INCRVOL, cache[ADAU1371_INCRVOL]);
+		adau1371_write(codec, ADAU1371_PWRCTLA, reg | PWRCTLA_INCPD);
+	} else if (i == 3) {
+		chip->in_chan_mask = CAP_INPD;
+		adau1371_write(codec, ADAU1371_LADCMIX, AIN4_SIGNAL_ENA);
+		adau1371_write(codec, ADAU1371_RADCMIX, AIN4_SIGNAL_ENA);
+		adau1371_write(codec, ADAU1371_INDLVOL, cache[ADAU1371_INDLVOL]);
+		adau1371_write(codec, ADAU1371_INDRVOL, cache[ADAU1371_INDRVOL]);
+		adau1371_write(codec, ADAU1371_PWRCTLA, reg | PWRCTLA_INDPD);
+	}
+
+	return 1;
+}
+
+#define OUTPUT_NUMBER 3
+static int adau1371_play_sel_info(struct snd_kcontrol *kcontrol,
+						struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[OUTPUT_NUMBER] = {"Line", "ClassD", "HeadPhone"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item >= OUTPUT_NUMBER)
+		uinfo->value.enumerated.item = OUTPUT_NUMBER - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int adau1371_play_sel_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct adau1371_priv *chip = codec->private_data;
+
+	if (chip->out_chan_mask & PB_LINE)
+		ucontrol->value.enumerated.item[0] = 0;
+	if (chip->out_chan_mask & PB_CD)
+		ucontrol->value.enumerated.item[0] = 1;
+	if (chip->out_chan_mask & PB_HP)
+		ucontrol->value.enumerated.item[0] = 2;
+
+	return 0;
+}
+
+static int adau1371_play_sel_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct adau1371_priv *chip = codec->private_data;
+	u8 *cache = codec->reg_cache;
+	u8 reg;
+	if (ucontrol->value.enumerated.item[0] >= OUTPUT_NUMBER)
+		return -EINVAL;
+
+	reg = adau1371_read_reg_cache(codec, ADAU1371_PWRCTLB);
+	chip->out_chan_mask = 0;
+	if (ucontrol->value.enumerated.item[0] == 0) {
+		chip->out_chan_mask = PB_LINE;
+		adau1371_write(codec, ADAU1371_LLINEMIX, LDAC_SIGNAL_ENA);
+		adau1371_write(codec, ADAU1371_RLINEMIX, RDAC_SIGNAL_ENA);
+		adau1371_write(codec, ADAU1371_LLINEVOL, cache[ADAU1371_LLINEVOL]);
+		adau1371_write(codec, ADAU1371_RLINEVOL, cache[ADAU1371_RLINEVOL]);
+		adau1371_write(codec, ADAU1371_PWRCTLB, reg | PWRCTLB_LLNPD | PWRCTLB_RLNPD);
+	}
+	if (ucontrol->value.enumerated.item[0] == 1) {
+		chip->out_chan_mask = PB_CD;
+		adau1371_write(codec, ADAU1371_LCDMIX, LDAC_SIGNAL_ENA);
+		adau1371_write(codec, ADAU1371_RCDMIX, RDAC_SIGNAL_ENA);
+		adau1371_write(codec, ADAU1371_LCDVOL, cache[ADAU1371_LCDVOL]);
+		adau1371_write(codec, ADAU1371_RCDVOL, cache[ADAU1371_RCDVOL]);
+		adau1371_write(codec, ADAU1371_PWRCTLB, reg | PWRCTLB_LCDPD | PWRCTLB_RCDPD);
+	}
+	if (ucontrol->value.enumerated.item[0] == 2) {
+		chip->out_chan_mask = PB_HP;
+		adau1371_write(codec, ADAU1371_LHPMIX, LDAC_SIGNAL_ENA);
+		adau1371_write(codec, ADAU1371_RHPMIX, RDAC_SIGNAL_ENA);
+		adau1371_write(codec, ADAU1371_LHPVOL, cache[ADAU1371_LHPVOL]);
+		adau1371_write(codec, ADAU1371_RHPVOL, cache[ADAU1371_RHPVOL]);
+		adau1371_write(codec, ADAU1371_PWRCTLB, reg | PWRCTLB_HPPD);
+	}
+	return 1;
+}
+
+static const struct snd_kcontrol_new adau1371_snd_controls[] = {
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name  = "Master Playback Volume",\
+	.info  = adau1371_volume_info, .get  = adau1371_volume_get,\
+	.put  = adau1371_volume_put},
+
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name  = "Playback Switch",\
+	.info  = adau1371_play_mute_info, .get  = adau1371_play_mute_get,\
+	.put  = adau1371_play_mute_put},
+
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name  = "Capture Volume",\
+	.info  = adau1371_cap_volume_info, .get  = adau1371_cap_volume_get,\
+	.put  = adau1371_cap_volume_put},
+
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name  = "Record Switch",\
+	.info  = adau1371_cap_mute_info, .get  = adau1371_cap_mute_get,\
+	.put  = adau1371_cap_mute_put},
+
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name  = "Input source",\
+	.info  = adau1371_mux_info, .get  = adau1371_mux_get,\
+	.put  = adau1371_mux_put},
+
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name  = "Output mixer",\
+	.info  = adau1371_play_sel_info, .get  = adau1371_play_sel_get,\
+	.put  = adau1371_play_sel_put}
+};
+
+#define adau1371_reset(c)	adau1371_write(c, ADAU1371_RESET, 0)
+
+static inline int get_coeff(int mclk, int rate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pll_settings); i++) {
+		if (pll_settings[i].rate == rate && pll_settings[i].mclk == mclk)
+			return i;
+	}
+	return i;
+}
+
+/* Set rate and format */
+static int adau1371_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params,
+	struct snd_soc_dai *dai)
+{
+	u8 reg;
+	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 adau1371_priv *adau1371 = codec->private_data;
+	int i = 0;
+	u8 dai_ctl = 0;
+	/* 8000 can't be got through PLL on REVB,so use external clock directly */
+	if (adau1371->sysclk == 12288000 && params_rate(params) == 8000)
+		adau1371_write(codec, ADAU1371_CLKSDIV, CLKSDIV_PLL_BYPASS\
+			| (5 << CLKSDIV_CLKDIV_SHIFT));
+	else {
+		i = get_coeff(adau1371->sysclk, params_rate(params));
+		if (i == ARRAY_SIZE(pll_settings))
+			return -EINVAL;
+
+		reg = adau1371_read_reg_cache(codec, ADAU1371_CLKSDIV);
+		adau1371_write(codec, ADAU1371_CLKSDIV, reg & ~CLKSDIV_COREN);
+		/* Set PLL */
+		adau1371_write(codec, ADAU1371_PLLCTLB, 0x00);
+		adau1371_write(codec, ADAU1371_PLLMHI,\
+			(pll_settings[i].m & 0xff00) >> 8);
+		adau1371_write(codec, ADAU1371_PLLMLOW, pll_settings[i].m\
+			& 0xff);
+		adau1371_write(codec, ADAU1371_PLLNHI,\
+			(pll_settings[i].n & 0xff00) >> 8);
+		adau1371_write(codec, ADAU1371_PLLNLOW, pll_settings[i].n\
+			& 0xff);
+		adau1371_write(codec, ADAU1371_PLLCTLA, pll_settings[i].integer << 3 |\
+			 pll_settings[i].input_div << 1 | pll_settings[i].type);
+	}
+
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		dai_ctl |= DAICTL_WLEN16;
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		dai_ctl |= DAICTL_WLEN20;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dai_ctl |= DAICTL_WLEN24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		dai_ctl |= DAICTL_WLEN32;
+		break;
+	}
+	reg = adau1371_read_reg_cache(codec, ADAU1371_DAIACTL);
+	adau1371_write(codec, ADAU1371_DAIACTL, reg | dai_ctl);
+
+	return 0;
+}
+
+static int adau1371_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;
+	u8 reg;
+	/* Enable PLL and wait 4 ms for PLL lock */
+	adau1371_write(codec, ADAU1371_PLLCTLB, PLLCTLB_PLLEN);
+	msleep(5);
+	/* Use DAI A */
+	adau1371_write(codec, ADAU1371_SRCDAICTL, SRCDAICTL_DAIA_ENA);
+	udelay(10);
+	reg = adau1371_read_reg_cache(codec, ADAU1371_CLKSDIV);
+	adau1371_write(codec, ADAU1371_CLKSDIV, reg | CLKSDIV_COREN);
+
+	return 0;
+}
+
+static void adau1371_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;
+
+	reg = adau1371_read_reg_cache(codec, ADAU1371_CLKSDIV);
+	/* deactivate */
+	if (!codec->active)
+		adau1371_write(codec, ADAU1371_CLKSDIV, reg & ~CLKSDIV_COREN);
+	adau1371_write(codec, ADAU1371_PLLCTLB, 0x0);
+	adau1371_write(codec, ADAU1371_SRCDAICTL, 0x0);
+
+}
+
+static int adau1371_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	if (mute)
+		adau1371_write(codec, ADAU1371_LLINEVOL, 0x00);
+	else
+		adau1371_write(codec, ADAU1371_LLINEVOL, 0x1f);
+	return 0;
+}
+
+static int adau1371_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 adau1371_priv *adau1371 = codec->private_data;
+
+	adau1371->sysclk = freq;
+	return 0;
+}
+
+static int adau1371_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u8 dai_ctl = 0;
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		 dai_ctl |= 0x40;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		dai_ctl |= DAICTL_FMI2S;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		dai_ctl |= DAICTL_FMRJUST;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		dai_ctl |= DAICTL_FMLJUST;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		dai_ctl |= DAICTL_FMDSP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		dai_ctl |= DAICTL_BLKINVA | DAICTL_LRPA;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		dai_ctl |= DAICTL_BLKINVA;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		dai_ctl |= DAICTL_LRPA;
+		break;
+	default:
+		return -EINVAL;
+	}
+	dai_ctl |= 0x00;
+	/* set DAIA */
+	adau1371_write(codec, ADAU1371_DAIACTL, dai_ctl);
+	return 0;
+}
+
+static int adau1371_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	u8 reg_pwr, reg_clk;
+
+	reg_pwr = adau1371_read_reg_cache(codec, ADAU1371_PWRCTLB);
+	reg_clk = adau1371_read_reg_cache(codec, ADAU1371_CLKSDIV);
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		adau1371_write(codec, ADAU1371_PWRCTLB, reg_pwr | PWRCTLB_PWDB);
+		adau1371_write(codec, ADAU1371_CLKSDIV, reg_clk | CLKSDIV_COREN);
+		/* vref/mid, osc on, dac unmute */
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		/* everything off except vref/vmid, */
+		adau1371_write(codec, ADAU1371_CLKSDIV, reg_clk & ~CLKSDIV_COREN);
+		break;
+	case SND_SOC_BIAS_OFF:
+		adau1371_write(codec, ADAU1371_PWRCTLB, reg_pwr & ~PWRCTLB_PWDB);
+		adau1371_write(codec, ADAU1371_CLKSDIV, reg_clk & ~CLKSDIV_COREN);
+		break;
+
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define adau1371_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_22050 |\
+		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define adau1371_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 adau1371_dai_ops = {
+	.prepare	= adau1371_pcm_prepare,
+	.hw_params	= adau1371_hw_params,
+	.shutdown	= adau1371_shutdown,
+	.digital_mute	= adau1371_mute,
+	.set_sysclk	= adau1371_set_dai_sysclk,
+	.set_fmt	= adau1371_set_dai_fmt,
+};
+
+struct snd_soc_dai adau1371_dai = {
+	.name = "adau1371",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = adau1371_RATES,
+		.formats = adau1371_FORMATS,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = adau1371_RATES,
+		.formats = adau1371_FORMATS},
+	.ops = &adau1371_dai_ops,
+};
+EXPORT_SYMBOL_GPL(adau1371_dai);
+
+static int adau1371_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;
+
+	adau1371_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int adau1371_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	int i;
+	u8 data[2];
+	u16 *cache = codec->reg_cache;
+
+	/* Sync reg_cache with the hardware */
+	for (i = 0; i < ARRAY_SIZE(adau1371_reg); i++) {
+		data[0] = i;
+		data[1] = cache[i];
+		codec->hw_write(codec->control_data, data, 2);
+	}
+	adau1371_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	adau1371_set_bias_level(codec, codec->suspend_bias_level);
+	return 0;
+}
+
+/*
+ * initialise the adau1371 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int adau1371_init(struct snd_soc_device *socdev)
+{
+	struct snd_soc_codec *codec = socdev->card->codec;
+	int reg = 0, ret = 0;
+	struct adau1371_priv *adau1371 = codec->private_data;
+	codec->name = "adau1371";
+	codec->owner = THIS_MODULE;
+	codec->read = adau1371_read_reg_cache;
+	codec->write = adau1371_write;
+	codec->set_bias_level = adau1371_set_bias_level;
+	codec->dai = &adau1371_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = sizeof(adau1371_reg);
+	codec->reg_cache = kmemdup(adau1371_reg, sizeof(adau1371_reg),
+					GFP_KERNEL);
+	if (codec->reg_cache == NULL)
+		return -ENOMEM;
+
+	adau1371_reset(codec);
+	/* By default line out and input A are enabled*/
+	adau1371->out_chan_mask = PB_LINE;
+	adau1371->in_chan_mask = CAP_INPA;
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		pr_err("adau1371: failed to create pcms\n");
+		goto pcm_err;
+	}
+	/* Playback mix settings, line out switched to DACs*/
+	adau1371_write(codec, ADAU1371_LLINEMIX, LDAC_SIGNAL_ENA);
+	adau1371_write(codec, ADAU1371_RLINEMIX, RDAC_SIGNAL_ENA);
+	/* Line out volume gain:0 db by default */
+	adau1371_write(codec, ADAU1371_LLINEVOL, 0x1f);
+	adau1371_write(codec, ADAU1371_RLINEVOL, 0x1f);
+	adau1371_write(codec, ADAU1371_DAIAPBLVOL, 0x80);
+	adau1371_write(codec, ADAU1371_DAIAPBRVOL, 0x80);
+
+	/* Capture mix settings, AIN1 switched to ADCs */
+	adau1371_write(codec, ADAU1371_LADCMIX, AIN1_SIGNAL_ENA);
+	adau1371_write(codec, ADAU1371_RADCMIX, AIN1_SIGNAL_ENA);
+	/* Input volume gain:0 db by default */
+	adau1371_write(codec, ADAU1371_INPUTMODE, 0x00);
+	adau1371_write(codec, ADAU1371_INALVOL, 0x1f);
+	adau1371_write(codec, ADAU1371_INARVOL, 0x1f);
+	adau1371_write(codec, ADAU1371_DAIRECLVOL, 0x80);
+	adau1371_write(codec, ADAU1371_DAIRECRVOL, 0x80);
+	/* Shoul be set on REVB to enable ADCs*/
+	adau1371_write(codec, ADAU1371_CODECCTL, 0x10);
+	/* 64 bits per frame*/
+	adau1371_write(codec, ADAU1371_BCLKDIV, BCLKDIV_BPFA64);
+	/* Use PLL, the clock divisor is 4 */
+	adau1371_write(codec, ADAU1371_CLKSDIV, (3 << CLKSDIV_CLKDIV_SHIFT));
+	/* PWR on input port A,right and left ADCs.*/
+	reg = PWRCTLA_INAPD | PWRCTLA_MICBPD | PWRCTLA_LADCPD | PWRCTLA_RADCPD;
+	adau1371_write(codec, ADAU1371_PWRCTLA, reg);
+	/* PWR on line out,right and left DACs and whole chip.*/
+	reg = PWRCTLB_RLNPD | PWRCTLB_LLNPD | PWRCTLB_RDACPD |\
+		PWRCTLB_LDACPD | PWRCTLB_PWDB;
+	adau1371_write(codec, ADAU1371_PWRCTLB, reg);
+
+	/* Enable playback and capture */
+	adau1371_write(codec, ADAU1371_DIGEN, DIGEN_PBEN | DIGEN_RECEN);
+
+	snd_soc_add_controls(codec, adau1371_snd_controls,
+				ARRAY_SIZE(adau1371_snd_controls));
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		pr_err("adau1371: failed to register card\n");
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	kfree(codec->reg_cache);
+	return ret;
+}
+
+static struct snd_soc_device *adau1371_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+static int adau1371_i2c_probe(struct i2c_client *i2c,
+			     const struct i2c_device_id *id)
+{
+	struct snd_soc_device *socdev = adau1371_socdev;
+	struct snd_soc_codec *codec = socdev->card->codec;
+	int ret;
+
+	i2c_set_clientdata(i2c, codec);
+	codec->control_data = i2c;
+
+	ret = adau1371_init(socdev);
+	if (ret < 0)
+		pr_err("failed to initialise adau1371\n");
+
+	return ret;
+}
+
+static int adau1371_i2c_remove(struct i2c_client *client)
+{
+	struct snd_soc_codec *codec = i2c_get_clientdata(client);
+	kfree(codec->reg_cache);
+	return 0;
+}
+
+static const struct i2c_device_id adau1371_i2c_id[] = {
+	{ "adau1371", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, adau1371_i2c_id);
+/* corgi i2c codec control layer */
+static struct i2c_driver adau1371_i2c_driver = {
+	.driver = {
+		.name = "adau1371 I2C Codec",
+		.owner = THIS_MODULE,
+	},
+	.probe = adau1371_i2c_probe,
+	.remove = adau1371_i2c_remove,
+	.id_table = adau1371_i2c_id,
+};
+
+static int adau1371_add_i2c_device(struct platform_device *pdev,
+				  const struct adau1371_setup_data *setup)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+	int ret;
+
+	ret = i2c_add_driver(&adau1371_i2c_driver);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't add i2c driver\n");
+		return ret;
+	}
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = setup->i2c_address;
+	strlcpy(info.type, "adau1371", I2C_NAME_SIZE);
+	adapter = i2c_get_adapter(setup->i2c_bus);
+	if (!adapter) {
+		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+		setup->i2c_bus);
+		goto err_driver;
+	}
+	client = i2c_new_device(adapter, &info);
+	i2c_put_adapter(adapter);
+	if (!client) {
+		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+		(unsigned int)info.addr);
+		goto err_driver;
+	}
+	return 0;
+err_driver:
+	i2c_del_driver(&adau1371_i2c_driver);
+	return -ENODEV;
+}
+#endif
+
+static int adau1371_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct adau1371_setup_data *setup;
+	struct snd_soc_codec *codec;
+	struct adau1371_priv *adau1371;
+	int ret = 0;
+
+	pr_info("adau1371 Audio Codec %s", adau1371_VERSION);
+
+	setup = socdev->codec_data;
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+	adau1371 = kzalloc(sizeof(struct adau1371_priv), GFP_KERNEL);
+	if (adau1371 == NULL) {
+		kfree(codec);
+		return -ENOMEM;
+	}
+
+	codec->private_data = adau1371;
+	socdev->card->codec = codec;
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	adau1371_socdev = socdev;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	if (setup->i2c_address) {
+		codec->hw_write = (hw_write_t)i2c_master_send;
+		ret = adau1371_add_i2c_device(pdev, setup);
+	}
+#else
+	/* other interfaces */
+#endif
+	return ret;
+}
+
+/* remove everything here */
+static int adau1371_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	if (codec->control_data)
+		adau1371_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_unregister_device(codec->control_data);
+	i2c_del_driver(&adau1371_i2c_driver);
+#endif
+	kfree(codec->private_data);
+	kfree(codec);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_adau1371 = {
+	.probe = 	adau1371_probe,
+	.remove = 	adau1371_remove,
+	.suspend = 	adau1371_suspend,
+	.resume =	adau1371_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_adau1371);
+
+static int __init adau1371_modinit(void)
+{
+	return snd_soc_register_dai(&adau1371_dai);
+}
+module_init(adau1371_modinit);
+
+static void __exit adau1371_exit(void)
+{
+	snd_soc_unregister_dai(&adau1371_dai);
+}
+module_exit(adau1371_exit);
+
+MODULE_DESCRIPTION("ASoC adau1371 driver");
+MODULE_AUTHOR("Cliff Cai");
+MODULE_LICENSE("GPL");

Added: trunk/sound/soc/codecs/adau1371.h (0 => 7448)


--- trunk/sound/soc/codecs/adau1371.h	                        (rev 0)
+++ trunk/sound/soc/codecs/adau1371.h	2009-09-24 09:48:30 UTC (rev 7448)
@@ -0,0 +1,215 @@
+/*
+ * File:         sound/soc/codecs/adau1371.h
+ * Author:       Cliff Cai <[email protected]>
+ *
+ * Created:       Thu September 03 2009
+ *
+ * Modified:
+ *               Copyright 2009 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _ADAU1371_H
+#define _ADAU1371_H
+
+/* ADAU1371 Codec Register definitions */
+
+#define ADAU1371_INPUTMODE   0x00
+#define ADAU1371_INALVOL   0x01
+#define ADAU1371_INARVOL   0x02
+#define ADAU1371_INBLVOL   0x03
+#define ADAU1371_INBRVOL   0x04
+#define ADAU1371_INCLVOL   0x05
+#define ADAU1371_INCRVOL   0x06
+#define ADAU1371_INDLVOL   0x07
+#define ADAU1371_INDRVOL   0x08
+#define ADAU1371_LLINEVOL  0x09
+#define ADAU1371_RLINEVOL   0x0A
+#define ADAU1371_LCDVOL    0x0B
+#define ADAU1371_RCDVOL   0x0C
+#define ADAU1371_LHPVOL      0x0D
+#define ADAU1371_RHPVOL    0x0E
+
+#define ADAU1371_HPCTRL    0x0F
+#define ADAU1371_LSCTRL    0x10
+#define ADAU1371_LINECTL    0x11
+#define ADAU1371_ADCGAIN    0x12
+
+#define ADAU1371_LADCMIX    0x13
+#define ADAU1371_RADCMIX    0x14
+#define ADAU1371_LLINEMIX    0x15
+#define ADAU1371_RLINEMIX    0x16
+#define ADAU1371_LCDMIX    0x17
+#define ADAU1371_RCDMIX    0x18
+#define ADAU1371_LHPMIX    0x19
+#define ADAU1371_RHPMIX    0x1A
+
+#define ADAU1371_REFCTL    0x1B
+#define ADAU1371_CODECCTL    0x1C
+#define ADAU1371_PWRCTLA    0x1D
+#define ADAU1371_PWRCTLB    0x1E
+#define ADAU1371_HEADDECT    0x1F
+
+#define ADAU1371_PLLMHI    0x20
+#define ADAU1371_PLLMLOW    0x21
+#define ADAU1371_PLLNHI    0x22
+#define ADAU1371_PLLNLOW    0x23
+#define ADAU1371_PLLCTLA    0x24
+#define ADAU1371_PLLCTLB    0x25
+
+#define ADAU1371_CHIPSTAT    0x26
+#define ADAU1371_CODECSTAT    0x27
+#define ADAU1371_HPSTAT    0x28
+#define ADAU1371_ADDCTL    0x29
+
+#define ADAU1371_CLKSDIV    0x30
+#define ADAU1371_CLKODIV    0x31
+#define ADAU1371_DAIACTL    0x32
+#define ADAU1371_DAIBCTL    0x33
+#define ADAU1371_BCLKDIV    0x34
+#define ADAU1371_SRCA    0x35
+#define ADAU1371_SRCB    0x36
+#define ADAU1371_SRCC    0x37
+#define ADAU1371_SRCMIX    0x38
+#define ADAU1371_SRCDAICTL    0x39
+#define ADAU1371_DAIAPBLVOL    0x3A
+#define ADAU1371_DAIAPBRVOL    0x3B
+#define ADAU1371_DAIBPBLVOL    0x3C
+#define ADAU1371_DAIBPBRVOL    0x3D
+#define ADAU1371_DAIRECLVOL    0x3E
+#define ADAU1371_DAIRECRVOL    0x3F
+#define ADAU1371_CODECPBLVOL    0x40
+#define ADAU1371_CODECPBRVOL    0x41
+#define ADAU1371_CODECRECLVOL    0x42
+#define ADAU1371_CODECRECRVOL    0x43
+#define ADAU1371_VOLMOD    0x44
+#define ADAU1371_DIGMIC		0xB1
+#define ADAU1371_DIGEN	  	0xC0
+#define ADAU1371_RESET    0xFF
+
+
+/*ADAU1371 Codec Register Field definitions
+ *(Mask value to extract the corresponding Register field)
+ */
+
+#define VOL_MASK 		0x1F
+#define AIN1_SIGNAL_ENA 	0x01
+#define AIN2_SIGNAL_ENA 	0x02
+#define AIN3_SIGNAL_ENA 	0x04
+#define AIN4_SIGNAL_ENA 	0x08
+#define LDAC_SIGNAL_ENA 	0x10
+#define RDAC_SIGNAL_ENA 	0x20
+
+/* PWR Management */
+#define PWRCTLA_INAPD 	0x01
+#define PWRCTLA_INBPD 	0x02
+#define PWRCTLA_INCPD 	0x04
+#define PWRCTLA_INDPD 	0x08
+#define PWRCTLA_PASSPD 	0x10
+#define PWRCTLA_MICBPD 	0x20
+#define PWRCTLA_LADCPD 	0x40
+#define PWRCTLA_RADCPD 	0x80
+
+#define PWRCTLB_PWDB 		0x01
+#define PWRCTLB_HPPD 	0x02
+#define PWRCTLB_LCDPD 	0x04
+#define PWRCTLB_RCDPD 	0x08
+#define PWRCTLB_LDACPD 	0x10
+#define PWRCTLB_RDACPD 	0x20
+#define PWRCTLB_LLNPD 	0x40
+#define PWRCTLB_RLNPD 	0x80
+
+/* PLL Control */
+
+#define PLLCTLA_X_SHIFT		1
+#define PLLCTLA_R_SHIFT		3
+#define PLLCTLB_PLLEN		0x01 /* PLL enable */
+#define PLLCTLB_LOCK		0x20 /* Lock poll */
+
+/*Clock GEN*/
+
+#define CLKSDIV_COREN 		0x80 /* Core clock enable */
+#define CLKSDIV_PLL_BYPASS 	0x40 /* Bypass PLL */
+#define CLKSDIV_CLKDIV_SHIFT 	3
+#define CLKSDIV_MCLKDIV_SHIFT 	0
+
+/* DAI Control */
+
+#define DAICTL_FMRJUST	0x00 /* Audio interface mode*/
+#define DAICTL_FMLJUST	0x01
+#define DAICTL_FMI2S	0x02
+#define DAICTL_FMDSP	0x03
+
+#define DAICTL_WLEN16	0x00 /* Word length */
+#define DAICTL_WLEN20	0x04
+#define DAICTL_WLEN24	0x08
+#define DAICTL_WLEN32	0x0c
+
+#define DAICTL_LRPA	0x10
+#define DAICTL_SWAPA	0x20
+#define DAICTL_MSA	0x40 /*Codec in master mode*/
+#define DAICTL_BLKINVA	0x80
+
+/* Number of bit clock per frame */
+#define BCLKDIV_BPFA256 	0x00
+#define BCLKDIV_BPFA128 	0x01
+#define BCLKDIV_BPFA64 		0x02
+#define BCLKDIV_BPFA32 		0x03
+
+/* SRC/DAI Control */
+#define SRCDAICTL_DAIA_ENA	0x01
+#define SRCDAICTL_DAIB_ENA	0x02
+#define SRCDAICTL_SRCREC_ENA	0x04
+#define SRCDAICTL_SRCPB_ENA	0x08
+/* DIGMIC */
+#define DIGMIC_EN	0x01
+/* DIGEN */
+#define DIGEN_PBEN	0x01
+#define DIGEN_RECEN	0x02
+#define DIGEN_FDSPEN	0x04
+
+#define PB_LINE 0x01
+#define PB_CD   0x02
+#define PB_HP   0x04
+
+#define CAP_INPA 0x01
+#define CAP_INPB 0x02
+#define CAP_INPC 0x04
+#define CAP_INPD 0x08
+
+#define ADC_MUTE_MASK 0xc0
+#define DAC_MUTE_MASK 0x30
+
+#define ADAU1371_CACHEREGNUM 	0x100
+
+#define ADAU1371_SYSCLK	0
+#define ADAU1371_DAI		0
+
+#define CHANNELS_INPUT 2
+#define CHANNELS_OUTPUT 2
+
+struct adau1371_setup_data {
+	int i2c_bus;
+	unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai adau1371_dai;
+extern struct snd_soc_codec_device soc_codec_dev_adau1371;
+
+#endif
_______________________________________________
Linux-kernel-commits mailing list
[email protected]
https://blackfin.uclinux.org/mailman/listinfo/linux-kernel-commits

Reply via email to