All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/4] ASoC: DAPM: Allow multiple mixer sources to be routed via the same switch
@ 2011-08-10  3:52 ` Lars-Peter Clausen
  0 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-10  3:52 UTC (permalink / raw)
  To: Mark Brown, Liam Girdwood
  Cc: alsa-devel, device-drivers-devel, linux-kernel, Mike Frysinger,
	Lars-Peter Clausen

Currently it is only possible to route one source per switch into a mixer.
This patch modifies the code, so that it is possible to route multiple sources
into a mixer via the same switch. One use-case for this is routing a stereo
channel pair into a mono-mixer via the same switch.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 sound/soc/soc-dapm.c |    6 +++++-
 1 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index c265311..170c4ff 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -443,6 +443,11 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
 			if (path->name != (char *)w->kcontrol_news[i].name)
 				continue;
 
+			if (w->kcontrols[i]) {
+				path->kcontrol = w->kcontrols[i];
+				continue;
+			}
+
 			wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
 				    sizeof(struct snd_soc_dapm_widget *),
 			wlist = kzalloc(wlistsize, GFP_KERNEL);
@@ -1556,7 +1561,6 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
 		/* found, now check type */
 		found = 1;
 		path->connect = connect;
-		break;
 	}
 
 	if (found)
-- 
1.7.2.5


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

* [PATCH 1/4] ASoC: DAPM: Allow multiple mixer sources to be routed via the same switch
@ 2011-08-10  3:52 ` Lars-Peter Clausen
  0 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-10  3:52 UTC (permalink / raw)
  To: Mark Brown, Liam Girdwood
  Cc: Mike Frysinger, alsa-devel, Lars-Peter Clausen, linux-kernel,
	device-drivers-devel

Currently it is only possible to route one source per switch into a mixer.
This patch modifies the code, so that it is possible to route multiple sources
into a mixer via the same switch. One use-case for this is routing a stereo
channel pair into a mono-mixer via the same switch.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 sound/soc/soc-dapm.c |    6 +++++-
 1 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index c265311..170c4ff 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -443,6 +443,11 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
 			if (path->name != (char *)w->kcontrol_news[i].name)
 				continue;
 
+			if (w->kcontrols[i]) {
+				path->kcontrol = w->kcontrols[i];
+				continue;
+			}
+
 			wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
 				    sizeof(struct snd_soc_dapm_widget *),
 			wlist = kzalloc(wlistsize, GFP_KERNEL);
@@ -1556,7 +1561,6 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
 		/* found, now check type */
 		found = 1;
 		path->connect = connect;
-		break;
 	}
 
 	if (found)
-- 
1.7.2.5

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

* [PATCH 2/4] ASoC: Add ADAU1373 codec support
  2011-08-10  3:52 ` Lars-Peter Clausen
@ 2011-08-10  3:52   ` Lars-Peter Clausen
  -1 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-10  3:52 UTC (permalink / raw)
  To: Mark Brown, Liam Girdwood
  Cc: alsa-devel, device-drivers-devel, linux-kernel, Mike Frysinger,
	Lars-Peter Clausen

This patch adds support for the Analog Devices ADAU1373 audio codec.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 include/sound/adau1373.h    |   22 +
 sound/soc/codecs/Kconfig    |    3 +
 sound/soc/codecs/Makefile   |    2 +
 sound/soc/codecs/adau1373.c | 1374 +++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/adau1373.h |   29 +
 5 files changed, 1430 insertions(+), 0 deletions(-)
 create mode 100644 include/sound/adau1373.h
 create mode 100644 sound/soc/codecs/adau1373.c
 create mode 100644 sound/soc/codecs/adau1373.h

diff --git a/include/sound/adau1373.h b/include/sound/adau1373.h
new file mode 100644
index 0000000..41a622e
--- /dev/null
+++ b/include/sound/adau1373.h
@@ -0,0 +1,22 @@
+/*
+ * Analog Devices ADAU1373 Audio Codec drive
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __SOUND_ADAU1373_H__
+#define __SOUND_ADAU1373_H__
+
+struct adau1373_platform_data {
+	bool input_differential[4];
+	bool lineout_differential;
+	bool lineout_ground_sense;
+
+	unsigned int num_drc;
+	uint8_t drc_setting[3][13];
+};
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 665d924..bac0edb 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -139,6 +139,9 @@ config SND_SOC_ADAU1701
 	select SIGMA
 	tristate
 
+config SND_SOC_ADAU1373
+	tristate
+
 config SND_SOC_ADAV80X
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 5119a7e..70c1769 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -5,6 +5,7 @@ snd-soc-ad193x-objs := ad193x.o
 snd-soc-ad1980-objs := ad1980.o
 snd-soc-ad73311-objs := ad73311.o
 snd-soc-adau1701-objs := adau1701.o
+snd-soc-adau1373-objs := adau1373.o
 snd-soc-adav80x-objs := adav80x.o
 snd-soc-ads117x-objs := ads117x.o
 snd-soc-ak4104-objs := ak4104.o
@@ -100,6 +101,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_ADAU1373)	+= snd-soc-adau1373.o
 obj-$(CONFIG_SND_SOC_ADAU1701)  += snd-soc-adau1701.o
 obj-$(CONFIG_SND_SOC_ADAV80X)  += snd-soc-adav80x.o
 obj-$(CONFIG_SND_SOC_ADS117X)	+= snd-soc-ads117x.o
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
new file mode 100644
index 0000000..91ee728
--- /dev/null
+++ b/sound/soc/codecs/adau1373.c
@@ -0,0 +1,1374 @@
+/*
+ * Analog Devices ADAU1373 Audio Codec drive
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/gcd.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/adau1373.h>
+
+#include "adau1373.h"
+
+struct adau1373_dai {
+	unsigned int clk_src;
+	unsigned int sysclk;
+	bool enable_src;
+	bool master;
+};
+
+struct adau1373 {
+	struct adau1373_dai dais[3];
+};
+
+#define ADAU1373_INPUT_MODE	0x00
+#define ADAU1373_AINL_CTRL(x)	(0x01 + (x) * 2)
+#define ADAU1373_AINR_CTRL(x)	(0x02 + (x) * 2)
+#define ADAU1373_LLINE_OUT(x)	(0x9 + (x) * 2)
+#define ADAU1373_RLINE_OUT(x)	(0xa + (x) * 2)
+#define ADAU1373_LSPK_OUT	0x0d
+#define ADAU1373_RSPK_OUT	0x0e
+#define ADAU1373_LHP_OUT	0x0f
+#define ADAU1373_RHP_OUT	0x10
+#define ADAU1373_ADC_GAIN	0x11
+#define ADAU1373_LADC_MIXER	0x12
+#define ADAU1373_RADC_MIXER	0x13
+#define ADAU1373_LLINE1_MIX	0x14
+#define ADAU1373_RLINE1_MIX	0x15
+#define ADAU1373_LLINE2_MIX	0x16
+#define ADAU1373_RLINE2_MIX	0x17
+#define ADAU1373_LSPK_MIX	0x18
+#define ADAU1373_RSPK_MIX	0x19
+#define ADAU1373_LHP_MIX	0x1a
+#define ADAU1373_RHP_MIX	0x1b
+#define ADAU1373_EP_MIX		0x1c
+#define ADAU1373_HP_CTRL	0x1d
+#define ADAU1373_HP_CTRL2	0x1e
+#define ADAU1373_LS_CTRL	0x1f
+#define ADAU1373_EP_CTRL	0x21
+#define ADAU1373_MICBIAS_CTRL1	0x22
+#define ADAU1373_MICBIAS_CTRL2	0x23
+#define ADAU1373_OUTPUT_CTRL	0x24
+#define ADAU1373_PWDN_CTRL1	0x25
+#define ADAU1373_PWDN_CTRL2	0x26
+#define ADAU1373_PWDN_CTRL3	0x27
+#define ADAU1373_DPLL_CTRL(x)	(0x28 + (x) * 7)
+#define ADAU1373_PLL_CTRL1(x)	(0x29 + (x) * 7)
+#define ADAU1373_PLL_CTRL2(x)	(0x2a + (x) * 7)
+#define ADAU1373_PLL_CTRL3(x)	(0x2b + (x) * 7)
+#define ADAU1373_PLL_CTRL4(x)	(0x2c + (x) * 7)
+#define ADAU1373_PLL_CTRL5(x)	(0x2d + (x) * 7)
+#define ADAU1373_PLL_CTRL6(x)	(0x2e + (x) * 7)
+#define ADAU1373_PLL_CTRL7(x)	(0x2f + (x) * 7)
+#define ADAU1373_HEADDECT	0x36
+#define ADAU1373_ADC_DAC_STATUS	0x37
+#define ADAU1373_ADC_CTRL	0x3c
+#define ADAU1373_DAI(x)		(0x44 + (x))
+#define ADAU1373_CLK_SRC_DIV(x)	(0x40 + (x) * 2)
+#define ADAU1373_BCLKDIV(x)	(0x47 + (x))
+#define ADAU1373_SRC_RATIOA(x)	(0x4a + (x) * 2)
+#define ADAU1373_SRC_RATIOB(x)	(0x4b + (x) * 2)
+#define ADAU1373_DEEMP_CTRL	0x50
+#define ADAU1373_SRC_DAI_CTRL(x) (0x51 + (x))
+#define ADAU1373_DIN_MIX_CTRL(x) (0x56 + (x))
+#define ADAU1373_DOUT_MIX_CTRL(x) (0x5b + (x))
+#define ADAU1373_DAI_PBL_VOL(x)	(0x62 + (x) * 2)
+#define ADAU1373_DAI_PBR_VOL(x)	(0x63 + (x) * 2)
+#define ADAU1373_DAI_RECL_VOL(x) (0x68 + (x) * 2)
+#define ADAU1373_DAI_RECR_VOL(x) (0x69 + (x) * 2)
+#define ADAU1373_DAC1_PBL_VOL	0x6e
+#define ADAU1373_DAC1_PBR_VOL	0x6f
+#define ADAU1373_DAC2_PBL_VOL	0x70
+#define ADAU1373_DAC2_PBR_VOL	0x71
+#define ADAU1373_ADC_RECL_VOL	0x72
+#define ADAU1373_ADC_RECR_VOL	0x73
+#define ADAU1373_DMIC_RECL_VOL	0x74
+#define ADAU1373_DMIC_RECR_VOL	0x75
+#define ADAU1373_VOL_GAIN1	0x76
+#define ADAU1373_VOL_GAIN2	0x77
+#define ADAU1373_VOL_GAIN3	0x78
+#define ADAU1373_HPF_CTRL	0x7d
+#define ADAU1373_BASS1		0x7e
+#define ADAU1373_BASS2		0x7f
+#define ADAU1373_DRC(x)		(0x80 + (x) * 0x10)
+#define ADAU1373_3D_CTRL1	0xc0
+#define ADAU1373_3D_CTRL2	0xc1
+#define ADAU1373_FDSP_SEL1	0xdc
+#define ADAU1373_FDSP_SEL2	0xdd
+#define ADAU1373_FDSP_SEL3	0xde
+#define ADAU1373_FDSP_SEL4	0xdf
+#define ADAU1373_DIGMICCTRL	0xe2
+#define ADAU1373_DIGEN		0xeb
+#define ADAU1373_SOFT_RESET	0xff
+
+
+#define ADAU1373_PLL_CTRL6_DPLL_BYPASS	BIT(1)
+#define ADAU1373_PLL_CTRL6_PLL_EN	BIT(0)
+
+#define ADAU1373_DAI_INVERT_BCLK	BIT(7)
+#define ADAU1373_DAI_MASTER		BIT(6)
+#define ADAU1373_DAI_INVERT_LRCLK	BIT(4)
+#define ADAU1373_DAI_WLEN_16		0x0
+#define ADAU1373_DAI_WLEN_20		0x4
+#define ADAU1373_DAI_WLEN_24		0x8
+#define ADAU1373_DAI_WLEN_32		0xc
+#define ADAU1373_DAI_WLEN_MASK		0xc
+#define ADAU1373_DAI_FORMAT_RIGHT_J	0x0
+#define ADAU1373_DAI_FORMAT_LEFT_J	0x1
+#define ADAU1373_DAI_FORMAT_I2S		0x2
+#define ADAU1373_DAI_FORMAT_DSP		0x3
+
+#define ADAU1373_BCLKDIV_SOURCE		BIT(5)
+#define ADAU1373_BCLKDIV_32		0x03
+#define ADAU1373_BCLKDIV_64		0x02
+#define ADAU1373_BCLKDIV_128		0x01
+#define ADAU1373_BCLKDIV_256		0x00
+
+#define ADAU1373_ADC_CTRL_PEAK_DETECT	BIT(0)
+#define ADAU1373_ADC_CTRL_RESET		BIT(1)
+#define ADAU1373_ADC_CTRL_RESET_FORCE	BIT(2)
+
+#define ADAU1373_OUTPUT_CTRL_LDIFF	BIT(3)
+#define ADAU1373_OUTPUT_CTRL_LNFBEN	BIT(2)
+
+
+static const uint8_t adau1373_default_regs[] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, /* 0x30 */
+	0x00, 0x00, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x00, /* 0x40 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x78, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, /* 0x80 */
+	0x00, 0xc0, 0x88, 0x7a, 0xdf, 0x20, 0x00, 0x00,
+	0x78, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, /* 0x90 */
+	0x00, 0xc0, 0x88, 0x7a, 0xdf, 0x20, 0x00, 0x00,
+	0x78, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, /* 0xa0 */
+	0x00, 0xc0, 0x88, 0x7a, 0xdf, 0x20, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0 */
+	0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, /* 0xe0 */
+	0x00, 0x1f, 0x0f, 0x00, 0x00,
+};
+
+static const unsigned int adau1373_out_tlv[] = {
+	TLV_DB_RANGE_HEAD(4),
+	0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1),
+	8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0),
+	16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0),
+	24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0),
+};
+
+static const DECLARE_TLV_DB_MINMAX(adau1373_digital_tlv, -9563, 0);
+static const DECLARE_TLV_DB_SCALE(adau1373_in_pga_tlv, -1300, 100, 1);
+static const DECLARE_TLV_DB_SCALE(adau1373_ep_tlv, -600, 600, 1);
+
+static const char *adau1373_fdsp_sel_text[] = {
+	"None",
+	"Channel 1",
+	"Channel 2",
+	"Channel 3",
+	"Channel 4",
+	"Channel 5",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_drc1_channel_enum,
+	ADAU1373_FDSP_SEL1, 4, adau1373_fdsp_sel_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_drc2_channel_enum,
+	ADAU1373_FDSP_SEL1, 0, adau1373_fdsp_sel_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_drc3_channel_enum,
+	ADAU1373_FDSP_SEL2, 0, adau1373_fdsp_sel_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_hpf_channel_enum,
+	ADAU1373_FDSP_SEL3, 0, adau1373_fdsp_sel_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_bass_channel_enum,
+	ADAU1373_FDSP_SEL4, 4, adau1373_fdsp_sel_text);
+
+static const char *adau1373_hpf_cutoff_text[] = {
+	"3.7Hz", "50Hz", "100Hz", "150Hz", "200Hz", "250Hz", "300Hz", "350Hz",
+	"400Hz", "450Hz", "500Hz", "550Hz", "600Hz", "650Hz", "700Hz", "750Hz",
+	"800Hz",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_hpf_cutoff_enum,
+	ADAU1373_HPF_CTRL, 3, adau1373_hpf_cutoff_text);
+
+static const char *adau1373_bass_lpf_cutoff_text[] = {
+	"801Hz", "1001Hz",
+};
+
+static const char *adau1373_bass_clip_level_text[] = {
+	"0.125", "0.250", "0.370", "0.500", "0.625", "0.750", "0.875",
+};
+
+static const unsigned int adau1373_bass_clip_level_values[] = {
+	1, 2, 3, 4, 5, 6, 7,
+};
+
+static const char *adau1373_bass_hpf_cutoff_text[] = {
+	"158Hz", "232Hz", "347Hz", "520Hz",
+};
+
+static const unsigned int adau1373_bass_tlv[] = {
+	TLV_DB_RANGE_HEAD(4),
+	0, 2, TLV_DB_SCALE_ITEM(-600, 600, 1),
+	3, 4, TLV_DB_SCALE_ITEM(950, 250, 0),
+	5, 7, TLV_DB_SCALE_ITEM(1400, 150, 0),
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_bass_lpf_cutoff_enum,
+	ADAU1373_BASS1, 5, adau1373_bass_lpf_cutoff_text);
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(adau1373_bass_clip_level_enum,
+	ADAU1373_BASS1, 2, 7, adau1373_bass_clip_level_text,
+	adau1373_bass_clip_level_values);
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_bass_hpf_cutoff_enum,
+	ADAU1373_BASS1, 0, adau1373_bass_hpf_cutoff_text);
+
+static const char *adau1373_3d_level_text[] = {
+	"0%", "6.67%", "13.33%", "20%", "26.67%", "33.33%",
+	"40%", "46.67%", "53.33%", "60%", "66.67%", "73.33%",
+	"80%", "86.67", "99.33%", "100%"
+};
+
+static const char *adau1373_3d_cutoff_text[] = {
+	"No 3D", "0.03125 fs", "0.04583 fs", "0.075 fs", "0.11458 fs",
+	"0.16875 fs", "0.27083 fs"
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_3d_level_enum,
+	ADAU1373_3D_CTRL1, 4, adau1373_3d_level_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_3d_cutoff_enum,
+	ADAU1373_3D_CTRL1, 0, adau1373_3d_cutoff_text);
+
+static const unsigned int adau1373_3d_tlv[] = {
+	TLV_DB_RANGE_HEAD(2),
+	0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+	1, 7, TLV_DB_LINEAR_ITEM(-1800, -120),
+};
+
+static const char *adau1373_mono_stereo_text[] = {
+	"Mute",
+	"Mono Right Channel (L+R)",
+	"Mono Left Channel (L+R)",
+	"Stereo",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_lineout1_mode_enum,
+	ADAU1373_OUTPUT_CTRL, 4, adau1373_mono_stereo_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_lineout2_mode_enum,
+	ADAU1373_OUTPUT_CTRL, 6, adau1373_mono_stereo_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_speaker_mode_enum,
+	ADAU1373_LS_CTRL, 4, adau1373_mono_stereo_text);
+
+static const char *adau1373_micbias_text[] = {
+	"2.9 V",
+	"2.2 V",
+	"2.6 V",
+	"1.8 V",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_micbias2_enum,
+	ADAU1373_EP_CTRL, 4, adau1373_micbias_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_micbias1_enum,
+	ADAU1373_EP_CTRL, 2, adau1373_micbias_text);
+
+static const struct snd_kcontrol_new adau1373_controls[] = {
+	SOC_DOUBLE_R_TLV("AIF1 Capture Volume", ADAU1373_DAI_RECL_VOL(0),
+		ADAU1373_DAI_RECR_VOL(0), 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("AIF2 Capture Volume", ADAU1373_DAI_RECL_VOL(1),
+		ADAU1373_DAI_RECR_VOL(1), 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("AIF3 Capture Volume", ADAU1373_DAI_RECL_VOL(2),
+		ADAU1373_DAI_RECR_VOL(2), 0, 0xff, 1, adau1373_digital_tlv),
+
+	SOC_DOUBLE_R_TLV("ADC Capture Volume", ADAU1373_ADC_RECL_VOL,
+		ADAU1373_ADC_RECR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("DMIC Capture Volume", ADAU1373_DMIC_RECL_VOL,
+		ADAU1373_DMIC_RECR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+
+	SOC_DOUBLE_R_TLV("AIF1 Playback Volume", ADAU1373_DAI_PBL_VOL(0),
+		ADAU1373_DAI_PBR_VOL(0), 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("AIF2 Playback Volume", ADAU1373_DAI_PBL_VOL(1),
+		ADAU1373_DAI_PBR_VOL(1), 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("AIF3 Playback Volume", ADAU1373_DAI_PBL_VOL(2),
+		ADAU1373_DAI_PBR_VOL(2), 0, 0xff, 1, adau1373_digital_tlv),
+
+	SOC_DOUBLE_R_TLV("DAC1 Playback Volume", ADAU1373_DAC1_PBL_VOL,
+		ADAU1373_DAC1_PBR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("DAC2 Playback Volume", ADAU1373_DAC2_PBL_VOL,
+		ADAU1373_DAC2_PBR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+
+	SOC_DOUBLE_R_TLV("Lineout1 Playback Volume", ADAU1373_LLINE_OUT(0),
+		ADAU1373_RLINE_OUT(0), 0, 0x1f, 0, adau1373_out_tlv),
+	SOC_DOUBLE_R_TLV("Speaker Playback Volume", ADAU1373_LSPK_OUT,
+		ADAU1373_RSPK_OUT, 0, 0x1f, 0, adau1373_out_tlv),
+	SOC_DOUBLE_R_TLV("Headphone Playback Volume", ADAU1373_LHP_OUT,
+		ADAU1373_RHP_OUT, 0, 0x1f, 0, adau1373_out_tlv),
+
+	SOC_DOUBLE_R_TLV("Input 1 Capture Volume", ADAU1373_AINL_CTRL(0),
+		ADAU1373_AINR_CTRL(0), 0, 0x1f, 0, adau1373_in_pga_tlv),
+	SOC_DOUBLE_R_TLV("Input 2 Capture Volume", ADAU1373_AINL_CTRL(1),
+		ADAU1373_AINR_CTRL(1), 0, 0x1f, 0, adau1373_in_pga_tlv),
+	SOC_DOUBLE_R_TLV("Input 3 Capture Volume", ADAU1373_AINL_CTRL(2),
+		ADAU1373_AINR_CTRL(2), 0, 0x1f, 0, adau1373_in_pga_tlv),
+	SOC_DOUBLE_R_TLV("Input 4 Capture Volume", ADAU1373_AINL_CTRL(3),
+		ADAU1373_AINR_CTRL(3), 0, 0x1f, 0, adau1373_in_pga_tlv),
+
+	SOC_SINGLE_TLV("Earpiece Playback Volume", ADAU1373_EP_CTRL, 0, 3, 0,
+		adau1373_ep_tlv),
+
+	SOC_DOUBLE("AIF3 Playback Boost(+6dB)", ADAU1373_VOL_GAIN1, 4, 5, 1, 0),
+	SOC_DOUBLE("AIF2 Playback Boost(+6dB)", ADAU1373_VOL_GAIN1, 2, 3, 1, 0),
+	SOC_DOUBLE("AIF1 Playback Boost(+6dB)", ADAU1373_VOL_GAIN1, 0, 1, 1, 0),
+	SOC_DOUBLE("AIF3 Capture Boost(+6dB)", ADAU1373_VOL_GAIN2, 4, 5, 1, 0),
+	SOC_DOUBLE("AIF2 Capture Boost(+6dB)", ADAU1373_VOL_GAIN2, 2, 3, 1, 0),
+	SOC_DOUBLE("AIF1 Capture Boost(+6dB)", ADAU1373_VOL_GAIN2, 0, 1, 1, 0),
+	SOC_DOUBLE("DMIC Capture Boost(+6dB)", ADAU1373_VOL_GAIN3, 6, 7, 1, 0),
+	SOC_DOUBLE("ADC Capture Boost(+6dB)", ADAU1373_VOL_GAIN3, 4, 5, 1, 0),
+	SOC_DOUBLE("DAC2 Playback Boost(+6dB)", ADAU1373_VOL_GAIN3, 2, 3, 1, 0),
+	SOC_DOUBLE("DAC1 Playback Boost(+6dB)", ADAU1373_VOL_GAIN3, 0, 1, 1, 0),
+
+	SOC_DOUBLE("Input1 Boost(+20dB)", ADAU1373_ADC_GAIN, 0, 4, 1, 0),
+	SOC_DOUBLE("Input2 Boost(+20dB)", ADAU1373_ADC_GAIN, 1, 5, 1, 0),
+	SOC_DOUBLE("Input3 Boost(+20dB)", ADAU1373_ADC_GAIN, 2, 6, 1, 0),
+	SOC_DOUBLE("Input4 Boost(+20dB)", ADAU1373_ADC_GAIN, 3, 7, 1, 0),
+
+	SOC_ENUM("Lineout1 Mono Stereo", adau1373_lineout1_mode_enum),
+	SOC_ENUM("Speaker Mono Stereo", adau1373_speaker_mode_enum),
+
+	SOC_ENUM("Mic Bias1", adau1373_micbias1_enum),
+	SOC_ENUM("Mic Bias2", adau1373_micbias2_enum),
+
+	SOC_ENUM("HPF Cutoff", adau1373_hpf_cutoff_enum),
+	SOC_DOUBLE("HPF Switch", ADAU1373_HPF_CTRL, 1, 0, 1, 0),
+	SOC_ENUM("HPF Channel", adau1373_hpf_channel_enum),
+
+	SOC_ENUM("Bass HPF Cutoff", adau1373_bass_hpf_cutoff_enum),
+	SOC_VALUE_ENUM("Bass Clip Level Threshold",
+	    adau1373_bass_clip_level_enum),
+	SOC_ENUM("Bass LPF Cutoff", adau1373_bass_lpf_cutoff_enum),
+	SOC_DOUBLE("Bass Switch", ADAU1373_BASS2, 0, 1, 1, 0),
+	SOC_SINGLE_TLV("Bass Volume", ADAU1373_BASS2, 2, 7, 0,
+	    adau1373_bass_tlv),
+	SOC_ENUM("Bass Channel", adau1373_bass_channel_enum),
+
+	SOC_ENUM("3D HPF Cutoff", adau1373_3d_cutoff_enum),
+	SOC_ENUM("3D Level", adau1373_3d_level_enum),
+	SOC_SINGLE("3D Switch", ADAU1373_3D_CTRL2, 0, 1, 0),
+	SOC_SINGLE_TLV("3D Volume", ADAU1373_3D_CTRL2, 2, 7, 0, adau1373_3d_tlv),
+	SOC_ENUM("3D Channel", adau1373_bass_channel_enum),
+};
+
+static const struct snd_kcontrol_new adau1373_lineout2_controls[] = {
+	SOC_DOUBLE_R_TLV("Lineout2 Playback Volume", ADAU1373_LLINE_OUT(1),
+		ADAU1373_RLINE_OUT(1), 0, 0x1f, 0, adau1373_out_tlv),
+	SOC_ENUM("Lineout2 Mono Stereo", adau1373_lineout2_mode_enum),
+};
+
+static const struct snd_kcontrol_new adau1373_drc_controls[] = {
+	SOC_ENUM("DRC1 Channel", adau1373_drc1_channel_enum),
+	SOC_ENUM("DRC2 Channel", adau1373_drc2_channel_enum),
+	SOC_ENUM("DRC3 Channel", adau1373_drc3_channel_enum),
+};
+
+static int adau1373_pll_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	unsigned int pll_id = w->name[3] - '1';
+	unsigned int val;
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		val = ADAU1373_PLL_CTRL6_PLL_EN;
+	else
+		val = 0;
+
+	snd_soc_update_bits(codec, ADAU1373_PLL_CTRL6(pll_id),
+		ADAU1373_PLL_CTRL6_PLL_EN, val);
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		mdelay(5);
+
+	return 0;
+}
+
+static const char *adau1373_decimator_text[] = {
+	"ADC",
+	"DMIC1",
+};
+
+static const struct soc_enum adau1373_decimator_enum =
+	SOC_ENUM_SINGLE(0, 0, 2, adau1373_decimator_text);
+
+static const struct snd_kcontrol_new adau1373_decimator_mux =
+	SOC_DAPM_ENUM_VIRT("Decimator Mux", adau1373_decimator_enum);
+
+static const struct snd_kcontrol_new adau1373_left_adc_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DAC1 Switch", ADAU1373_LADC_MIXER, 4, 1, 0),
+	SOC_DAPM_SINGLE("Input 4 Switch", ADAU1373_LADC_MIXER, 3, 1, 0),
+	SOC_DAPM_SINGLE("Input 3 Switch", ADAU1373_LADC_MIXER, 2, 1, 0),
+	SOC_DAPM_SINGLE("Input 2 Switch", ADAU1373_LADC_MIXER, 1, 1, 0),
+	SOC_DAPM_SINGLE("Input 1 Switch", ADAU1373_LADC_MIXER, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1373_right_adc_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DAC1 Switch", ADAU1373_RADC_MIXER, 4, 1, 0),
+	SOC_DAPM_SINGLE("Input 4 Switch", ADAU1373_RADC_MIXER, 3, 1, 0),
+	SOC_DAPM_SINGLE("Input 3 Switch", ADAU1373_RADC_MIXER, 2, 1, 0),
+	SOC_DAPM_SINGLE("Input 2 Switch", ADAU1373_RADC_MIXER, 1, 1, 0),
+	SOC_DAPM_SINGLE("Input 1 Switch", ADAU1373_RADC_MIXER, 0, 1, 0),
+};
+
+#define DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+	SOC_DAPM_SINGLE("Left DAC2 Switch", _reg, 7, 1, 0), \
+	SOC_DAPM_SINGLE("Right DAC2 Switch", _reg, 6, 1, 0), \
+	SOC_DAPM_SINGLE("Left DAC1 Switch", _reg, 5, 1, 0), \
+	SOC_DAPM_SINGLE("Right DAC1 Switch", _reg, 4, 1, 0), \
+	SOC_DAPM_SINGLE("Input 4 Bypass Switch", _reg, 3, 1, 0), \
+	SOC_DAPM_SINGLE("Input 3 Bypass Switch", _reg, 2, 1, 0), \
+	SOC_DAPM_SINGLE("Input 2 Bypass Switch", _reg, 1, 1, 0), \
+	SOC_DAPM_SINGLE("Input 1 Bypass Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_line1_mixer_controls,
+	ADAU1373_LLINE1_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_line1_mixer_controls,
+	ADAU1373_RLINE1_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_line2_mixer_controls,
+	ADAU1373_LLINE2_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_line2_mixer_controls,
+	ADAU1373_RLINE2_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_spk_mixer_controls,
+	ADAU1373_LSPK_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_spk_mixer_controls,
+	ADAU1373_RSPK_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_ep_mixer_controls,
+	ADAU1373_EP_MIX);
+
+static const struct snd_kcontrol_new adau1373_left_hp_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Left DAC1 Switch", ADAU1373_LHP_MIX, 5, 1, 0),
+	SOC_DAPM_SINGLE("Left DAC2 Switch", ADAU1373_LHP_MIX, 4, 1, 0),
+	SOC_DAPM_SINGLE("Input 4 Bypass Switch", ADAU1373_LHP_MIX, 3, 1, 0),
+	SOC_DAPM_SINGLE("Input 3 Bypass Switch", ADAU1373_LHP_MIX, 2, 1, 0),
+	SOC_DAPM_SINGLE("Input 2 Bypass Switch", ADAU1373_LHP_MIX, 1, 1, 0),
+	SOC_DAPM_SINGLE("Input 1 Bypass Switch", ADAU1373_LHP_MIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1373_right_hp_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Right DAC1 Switch", ADAU1373_RHP_MIX, 5, 1, 0),
+	SOC_DAPM_SINGLE("Right DAC2 Switch", ADAU1373_RHP_MIX, 4, 1, 0),
+	SOC_DAPM_SINGLE("Input 4 Bypass Switch", ADAU1373_RHP_MIX, 3, 1, 0),
+	SOC_DAPM_SINGLE("Input 3 Bypass Switch", ADAU1373_RHP_MIX, 2, 1, 0),
+	SOC_DAPM_SINGLE("Input 2 Bypass Switch", ADAU1373_RHP_MIX, 1, 1, 0),
+	SOC_DAPM_SINGLE("Input 1 Bypass Switch", ADAU1373_RHP_MIX, 0, 1, 0),
+};
+
+#define DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+	SOC_DAPM_SINGLE("DMIC2 Swapped Switch", _reg, 6, 1, 0), \
+	SOC_DAPM_SINGLE("DMIC2 Switch", _reg, 5, 1, 0), \
+	SOC_DAPM_SINGLE("ADC/DMIC1 Swapped Switch", _reg, 4, 1, 0), \
+	SOC_DAPM_SINGLE("ADC/DMIC1 Switch", _reg, 3, 1, 0), \
+	SOC_DAPM_SINGLE("AIF3 Switch", _reg, 2, 1, 0), \
+	SOC_DAPM_SINGLE("AIF2 Switch", _reg, 1, 1, 0), \
+	SOC_DAPM_SINGLE("AIF1 Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel1_mixer_controls,
+	ADAU1373_DIN_MIX_CTRL(0));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel2_mixer_controls,
+	ADAU1373_DIN_MIX_CTRL(1));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel3_mixer_controls,
+	ADAU1373_DIN_MIX_CTRL(2));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel4_mixer_controls,
+	ADAU1373_DIN_MIX_CTRL(3));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel5_mixer_controls,
+	ADAU1373_DIN_MIX_CTRL(4));
+
+#define DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+	SOC_DAPM_SINGLE("DSP Channel5 Switch", _reg, 4, 1, 0), \
+	SOC_DAPM_SINGLE("DSP Channel4 Switch", _reg, 3, 1, 0), \
+	SOC_DAPM_SINGLE("DSP Channel3 Switch", _reg, 2, 1, 0), \
+	SOC_DAPM_SINGLE("DSP Channel2 Switch", _reg, 1, 1, 0), \
+	SOC_DAPM_SINGLE("DSP Channel1 Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif1_mixer_controls,
+	ADAU1373_DOUT_MIX_CTRL(0));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif2_mixer_controls,
+	ADAU1373_DOUT_MIX_CTRL(1));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif3_mixer_controls,
+	ADAU1373_DOUT_MIX_CTRL(2));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_dac1_mixer_controls,
+	ADAU1373_DOUT_MIX_CTRL(3));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_dac2_mixer_controls,
+	ADAU1373_DOUT_MIX_CTRL(4));
+
+static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = {
+	/* Datasheet claims Left ADC is bit 6 and Right ADC is bit 7, but that
+	 * doesn't seem to be the case. */
+	SND_SOC_DAPM_ADC("Left ADC", NULL, ADAU1373_PWDN_CTRL1, 7, 0),
+	SND_SOC_DAPM_ADC("Right ADC", NULL, ADAU1373_PWDN_CTRL1, 6, 0),
+
+	SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1373_DIGMICCTRL, 0, 0),
+	SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1373_DIGMICCTRL, 2, 0),
+
+	SND_SOC_DAPM_VIRT_MUX("Decimator Mux", SND_SOC_NOPM, 0, 0,
+		&adau1373_decimator_mux),
+
+	SND_SOC_DAPM_SUPPLY("MICBIAS2", ADAU1373_PWDN_CTRL1, 5, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("MICBIAS1", ADAU1373_PWDN_CTRL1, 4, 0, NULL, 0),
+
+	SND_SOC_DAPM_PGA("IN4PGA", ADAU1373_PWDN_CTRL1, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("IN3PGA", ADAU1373_PWDN_CTRL1, 2, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("IN2PGA", ADAU1373_PWDN_CTRL1, 1, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("IN1PGA", ADAU1373_PWDN_CTRL1, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_DAC("Left DAC2", NULL, ADAU1373_PWDN_CTRL2, 7, 0),
+	SND_SOC_DAPM_DAC("Right DAC2", NULL, ADAU1373_PWDN_CTRL2, 6, 0),
+	SND_SOC_DAPM_DAC("Left DAC1", NULL, ADAU1373_PWDN_CTRL2, 5, 0),
+	SND_SOC_DAPM_DAC("Right DAC1", NULL, ADAU1373_PWDN_CTRL2, 4, 0),
+
+	SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_left_adc_mixer_controls),
+	SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_right_adc_mixer_controls),
+
+	SOC_MIXER_ARRAY("Left Lineout2 Mixer", ADAU1373_PWDN_CTRL2, 3, 0,
+		adau1373_left_line2_mixer_controls),
+	SOC_MIXER_ARRAY("Right Lineout2 Mixer", ADAU1373_PWDN_CTRL2, 2, 0,
+		adau1373_right_line2_mixer_controls),
+	SOC_MIXER_ARRAY("Left Lineout1 Mixer", ADAU1373_PWDN_CTRL2, 1, 0,
+		adau1373_left_line1_mixer_controls),
+	SOC_MIXER_ARRAY("Right Lineout1 Mixer", ADAU1373_PWDN_CTRL2, 0, 0,
+		adau1373_right_line1_mixer_controls),
+
+	SOC_MIXER_ARRAY("Earpiece Mixer", ADAU1373_PWDN_CTRL3, 4, 0,
+		adau1373_ep_mixer_controls),
+	SOC_MIXER_ARRAY("Left Speaker Mixer", ADAU1373_PWDN_CTRL3, 3, 0,
+		adau1373_left_spk_mixer_controls),
+	SOC_MIXER_ARRAY("Right Speaker Mixer", ADAU1373_PWDN_CTRL3, 2, 0,
+		adau1373_right_spk_mixer_controls),
+	SOC_MIXER_ARRAY("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_left_hp_mixer_controls),
+	SOC_MIXER_ARRAY("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_right_hp_mixer_controls),
+	SND_SOC_DAPM_SUPPLY("Headphone Enable", ADAU1373_PWDN_CTRL3, 1, 0,
+		NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("AIF1 CLK", ADAU1373_SRC_DAI_CTRL(0), 0, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF2 CLK", ADAU1373_SRC_DAI_CTRL(1), 0, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF3 CLK", ADAU1373_SRC_DAI_CTRL(2), 0, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF1 IN SRC", ADAU1373_SRC_DAI_CTRL(0), 2, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF1 OUT SRC", ADAU1373_SRC_DAI_CTRL(0), 1, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF2 IN SRC", ADAU1373_SRC_DAI_CTRL(1), 2, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF2 OUT SRC", ADAU1373_SRC_DAI_CTRL(1), 1, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF3 IN SRC", ADAU1373_SRC_DAI_CTRL(2), 2, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF3 OUT SRC", ADAU1373_SRC_DAI_CTRL(2), 1, 0,
+	    NULL, 0),
+
+	SND_SOC_DAPM_AIF_IN("AIF1 IN", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AIF1 OUT", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("AIF2 IN", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AIF2 OUT", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("AIF3 IN", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AIF3 OUT", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+	SOC_MIXER_ARRAY("DSP Channel1 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dsp_channel1_mixer_controls),
+	SOC_MIXER_ARRAY("DSP Channel2 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dsp_channel2_mixer_controls),
+	SOC_MIXER_ARRAY("DSP Channel3 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dsp_channel3_mixer_controls),
+	SOC_MIXER_ARRAY("DSP Channel4 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dsp_channel4_mixer_controls),
+	SOC_MIXER_ARRAY("DSP Channel5 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dsp_channel5_mixer_controls),
+
+	SOC_MIXER_ARRAY("AIF1 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_aif1_mixer_controls),
+	SOC_MIXER_ARRAY("AIF2 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_aif2_mixer_controls),
+	SOC_MIXER_ARRAY("AIF3 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_aif3_mixer_controls),
+	SOC_MIXER_ARRAY("DAC1 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dac1_mixer_controls),
+	SOC_MIXER_ARRAY("DAC2 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dac2_mixer_controls),
+
+	SND_SOC_DAPM_SUPPLY("DSP", ADAU1373_DIGEN, 4, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Recording Engine B", ADAU1373_DIGEN, 3, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Recording Engine A", ADAU1373_DIGEN, 2, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Playback Engine B", ADAU1373_DIGEN, 1, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Playback Engine A", ADAU1373_DIGEN, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("PLL1", SND_SOC_NOPM, 0, 0, adau1373_pll_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SUPPLY("PLL2", SND_SOC_NOPM, 0, 0, adau1373_pll_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SUPPLY("SYSCLK1", ADAU1373_CLK_SRC_DIV(0), 7, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("SYSCLK2", ADAU1373_CLK_SRC_DIV(1), 7, 0, NULL, 0),
+
+	SND_SOC_DAPM_INPUT("AIN1L"),
+	SND_SOC_DAPM_INPUT("AIN1R"),
+	SND_SOC_DAPM_INPUT("AIN2L"),
+	SND_SOC_DAPM_INPUT("AIN2R"),
+	SND_SOC_DAPM_INPUT("AIN3L"),
+	SND_SOC_DAPM_INPUT("AIN3R"),
+	SND_SOC_DAPM_INPUT("AIN4L"),
+	SND_SOC_DAPM_INPUT("AIN4R"),
+
+	SND_SOC_DAPM_INPUT("DMIC1DAT"),
+	SND_SOC_DAPM_INPUT("DMIC2DAT"),
+
+	SND_SOC_DAPM_OUTPUT("LOUT1L"),
+	SND_SOC_DAPM_OUTPUT("LOUT1R"),
+	SND_SOC_DAPM_OUTPUT("LOUT2L"),
+	SND_SOC_DAPM_OUTPUT("LOUT2R"),
+	SND_SOC_DAPM_OUTPUT("HPL"),
+	SND_SOC_DAPM_OUTPUT("HPR"),
+	SND_SOC_DAPM_OUTPUT("SPKL"),
+	SND_SOC_DAPM_OUTPUT("SPKR"),
+	SND_SOC_DAPM_OUTPUT("EP"),
+};
+
+static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
+	struct snd_soc_dapm_widget *sink)
+{
+	struct snd_soc_codec *codec = source->codec;
+	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+	unsigned int dai;
+	const char *clk;
+
+	dai = sink->name[3] - '1';
+
+	if (!adau1373->dais[dai].master)
+		return 0;
+
+	if (adau1373->dais[dai].clk_src == ADAU1373_CLK_SRC_PLL1)
+		clk = "SYSCLK1";
+	else
+		clk = "SYSCLK2";
+
+	return strcmp(source->name, clk) == 0;
+}
+
+static int adau1373_check_src(struct snd_soc_dapm_widget *source,
+	struct snd_soc_dapm_widget *sink)
+{
+	struct snd_soc_codec *codec = source->codec;
+	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+	unsigned int dai;
+
+	dai = sink->name[3] - '1';
+
+	return adau1373->dais[dai].enable_src;
+}
+
+#define DSP_CHANNEL_MIXER_ROUTES(_sink) \
+	{ _sink, "DMIC2 Swapped Switch", "DMIC2" }, \
+	{ _sink, "DMIC2 Switch", "DMIC2" }, \
+	{ _sink, "ADC/DMIC1 Swapped Switch", "Decimator Mux" }, \
+	{ _sink, "ADC/DMIC1 Switch", "Decimator Mux" }, \
+	{ _sink, "AIF1 Switch", "AIF1 IN" }, \
+	{ _sink, "AIF2 Switch", "AIF2 IN" }, \
+	{ _sink, "AIF3 Switch", "AIF3 IN" }
+
+#define DSP_OUTPUT_MIXER_ROUTES(_sink) \
+	{ _sink, "DSP Channel1 Switch", "DSP Channel1 Mixer" }, \
+	{ _sink, "DSP Channel2 Switch", "DSP Channel2 Mixer" }, \
+	{ _sink, "DSP Channel3 Switch", "DSP Channel3 Mixer" }, \
+	{ _sink, "DSP Channel4 Switch", "DSP Channel4 Mixer" }, \
+	{ _sink, "DSP Channel5 Switch", "DSP Channel5 Mixer" }
+
+#define LEFT_OUTPUT_MIXER_ROUTES(_sink) \
+	{ _sink, "Right DAC2 Switch", "Right DAC2" }, \
+	{ _sink, "Left DAC2 Switch", "Left DAC2" }, \
+	{ _sink, "Right DAC1 Switch", "Right DAC1" }, \
+	{ _sink, "Left DAC1 Switch", "Left DAC1" }, \
+	{ _sink, "Input 1 Bypass Switch", "IN1PGA" }, \
+	{ _sink, "Input 2 Bypass Switch", "IN2PGA" }, \
+	{ _sink, "Input 3 Bypass Switch", "IN3PGA" }, \
+	{ _sink, "Input 4 Bypass Switch", "IN4PGA" }
+
+#define RIGHT_OUTPUT_MIXER_ROUTES(_sink) \
+	{ _sink, "Right DAC2 Switch", "Right DAC2" }, \
+	{ _sink, "Left DAC2 Switch", "Left DAC2" }, \
+	{ _sink, "Right DAC1 Switch", "Right DAC1" }, \
+	{ _sink, "Left DAC1 Switch", "Left DAC1" }, \
+	{ _sink, "Input 1 Bypass Switch", "IN1PGA" }, \
+	{ _sink, "Input 2 Bypass Switch", "IN2PGA" }, \
+	{ _sink, "Input 3 Bypass Switch", "IN3PGA" }, \
+	{ _sink, "Input 4 Bypass Switch", "IN4PGA" }
+
+static const struct snd_soc_dapm_route adau1373_dapm_routes[] = {
+	{ "Left ADC Mixer", "DAC1 Switch", "Left DAC1" },
+	{ "Left ADC Mixer", "Input 1 Switch", "IN1PGA" },
+	{ "Left ADC Mixer", "Input 2 Switch", "IN2PGA" },
+	{ "Left ADC Mixer", "Input 3 Switch", "IN3PGA" },
+	{ "Left ADC Mixer", "Input 4 Switch", "IN4PGA" },
+
+	{ "Right ADC Mixer", "DAC1 Switch", "Right DAC1" },
+	{ "Right ADC Mixer", "Input 1 Switch", "IN1PGA" },
+	{ "Right ADC Mixer", "Input 2 Switch", "IN2PGA" },
+	{ "Right ADC Mixer", "Input 3 Switch", "IN3PGA" },
+	{ "Right ADC Mixer", "Input 4 Switch", "IN4PGA" },
+
+	{ "Left ADC", NULL, "Left ADC Mixer" },
+	{ "Right ADC", NULL, "Right ADC Mixer" },
+
+	{ "Decimator Mux", "ADC", "Left ADC" },
+	{ "Decimator Mux", "ADC", "Right ADC" },
+	{ "Decimator Mux", "DMIC1", "DMIC1" },
+
+	DSP_CHANNEL_MIXER_ROUTES("DSP Channel1 Mixer"),
+	DSP_CHANNEL_MIXER_ROUTES("DSP Channel2 Mixer"),
+	DSP_CHANNEL_MIXER_ROUTES("DSP Channel3 Mixer"),
+	DSP_CHANNEL_MIXER_ROUTES("DSP Channel4 Mixer"),
+	DSP_CHANNEL_MIXER_ROUTES("DSP Channel5 Mixer"),
+
+	DSP_OUTPUT_MIXER_ROUTES("AIF1 Mixer"),
+	DSP_OUTPUT_MIXER_ROUTES("AIF2 Mixer"),
+	DSP_OUTPUT_MIXER_ROUTES("AIF3 Mixer"),
+	DSP_OUTPUT_MIXER_ROUTES("DAC1 Mixer"),
+	DSP_OUTPUT_MIXER_ROUTES("DAC2 Mixer"),
+
+	{ "AIF1 OUT", NULL, "AIF1 Mixer" },
+	{ "AIF2 OUT", NULL, "AIF2 Mixer" },
+	{ "AIF3 OUT", NULL, "AIF3 Mixer" },
+	{ "Left DAC1", NULL, "DAC1 Mixer" },
+	{ "Right DAC1", NULL, "DAC1 Mixer" },
+	{ "Left DAC2", NULL, "DAC2 Mixer" },
+	{ "Right DAC2", NULL, "DAC2 Mixer" },
+
+	LEFT_OUTPUT_MIXER_ROUTES("Left Lineout1 Mixer"),
+	RIGHT_OUTPUT_MIXER_ROUTES("Right Lineout1 Mixer"),
+	LEFT_OUTPUT_MIXER_ROUTES("Left Lineout2 Mixer"),
+	RIGHT_OUTPUT_MIXER_ROUTES("Right Lineout2 Mixer"),
+	LEFT_OUTPUT_MIXER_ROUTES("Left Speaker Mixer"),
+	RIGHT_OUTPUT_MIXER_ROUTES("Right Speaker Mixer"),
+
+	{ "Left Headphone Mixer", "Left DAC2 Switch", "Left DAC2" },
+	{ "Left Headphone Mixer", "Left DAC1 Switch", "Left DAC1" },
+	{ "Left Headphone Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+	{ "Left Headphone Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+	{ "Left Headphone Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+	{ "Left Headphone Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+	{ "Right Headphone Mixer", "Right DAC2 Switch", "Right DAC2" },
+	{ "Right Headphone Mixer", "Right DAC1 Switch", "Right DAC1" },
+	{ "Right Headphone Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+	{ "Right Headphone Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+	{ "Right Headphone Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+	{ "Right Headphone Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+
+	{ "Left Headphone Mixer", NULL, "Headphone Enable" },
+	{ "Right Headphone Mixer", NULL, "Headphone Enable" },
+
+	{ "Earpiece Mixer", "Right DAC2 Switch", "Right DAC2" },
+	{ "Earpiece Mixer", "Left DAC2 Switch", "Left DAC2" },
+	{ "Earpiece Mixer", "Right DAC1 Switch", "Right DAC1" },
+	{ "Earpiece Mixer", "Left DAC1 Switch", "Left DAC1" },
+	{ "Earpiece Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+	{ "Earpiece Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+	{ "Earpiece Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+	{ "Earpiece Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+
+	{ "LOUT1L", NULL, "Left Lineout1 Mixer" },
+	{ "LOUT1R", NULL, "Right Lineout1 Mixer" },
+	{ "LOUT2L", NULL, "Left Lineout2 Mixer" },
+	{ "LOUT2R", NULL, "Right Lineout2 Mixer" },
+	{ "SPKL", NULL, "Left Speaker Mixer" },
+	{ "SPKR", NULL, "Right Speaker Mixer" },
+	{ "HPL", NULL, "Left Headphone Mixer" },
+	{ "HPR", NULL, "Right Headphone Mixer" },
+	{ "EP", NULL, "Earpiece Mixer" },
+
+	{ "IN1PGA", NULL, "AIN1L" },
+	{ "IN2PGA", NULL, "AIN2L" },
+	{ "IN3PGA", NULL, "AIN3L" },
+	{ "IN4PGA", NULL, "AIN4L" },
+	{ "IN1PGA", NULL, "AIN1R" },
+	{ "IN2PGA", NULL, "AIN2R" },
+	{ "IN3PGA", NULL, "AIN3R" },
+	{ "IN4PGA", NULL, "AIN4R" },
+
+	{ "SYSCLK1", NULL, "PLL1" },
+	{ "SYSCLK2", NULL, "PLL2" },
+
+	{ "Left DAC1", NULL, "SYSCLK1" },
+	{ "Right DAC1", NULL, "SYSCLK1" },
+	{ "Left DAC2", NULL, "SYSCLK1" },
+	{ "Right DAC2", NULL, "SYSCLK1" },
+	{ "Left ADC", NULL, "SYSCLK1" },
+	{ "Right ADC", NULL, "SYSCLK1" },
+
+	{ "DSP", NULL, "SYSCLK1" },
+
+	{ "AIF1 Mixer", NULL, "DSP" },
+	{ "AIF2 Mixer", NULL, "DSP" },
+	{ "AIF3 Mixer", NULL, "DSP" },
+	{ "DAC1 Mixer", NULL, "DSP" },
+	{ "DAC2 Mixer", NULL, "DSP" },
+	{ "DAC1 Mixer", NULL, "Playback Engine A" },
+	{ "DAC2 Mixer", NULL, "Playback Engine B" },
+	{ "Left ADC Mixer", NULL, "Recording Engine A" },
+	{ "Right ADC Mixer", NULL, "Recording Engine A" },
+
+	{ "AIF1 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+	{ "AIF2 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+	{ "AIF3 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+	{ "AIF1 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+	{ "AIF2 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+	{ "AIF3 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+
+	{ "AIF1 IN", NULL, "AIF1 CLK" },
+	{ "AIF1 OUT", NULL, "AIF1 CLK" },
+	{ "AIF2 IN", NULL, "AIF2 CLK" },
+	{ "AIF2 OUT", NULL, "AIF2 CLK" },
+	{ "AIF3 IN", NULL, "AIF3 CLK" },
+	{ "AIF3 OUT", NULL, "AIF3 CLK" },
+	{ "AIF1 IN", NULL, "AIF1 IN SRC", adau1373_check_src },
+	{ "AIF1 OUT", NULL, "AIF1 OUT SRC", adau1373_check_src },
+	{ "AIF2 IN", NULL, "AIF2 IN SRC", adau1373_check_src },
+	{ "AIF2 OUT", NULL, "AIF2 OUT SRC", adau1373_check_src },
+	{ "AIF3 IN", NULL, "AIF3 IN SRC", adau1373_check_src },
+	{ "AIF3 OUT", NULL, "AIF3 OUT SRC", adau1373_check_src },
+
+	{ "DMIC1", NULL, "DMIC1DAT" },
+	{ "DMIC1", NULL, "SYSCLK1" },
+	{ "DMIC1", NULL, "Recording Engine A" },
+	{ "DMIC2", NULL, "DMIC2DAT" },
+	{ "DMIC2", NULL, "SYSCLK1" },
+	{ "DMIC2", NULL, "Recording Engine B" },
+};
+
+static int adau1373_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+	struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+	unsigned int div;
+	unsigned int freq;
+	unsigned int ctrl;
+
+	freq = adau1373_dai->sysclk;
+
+	if (freq % params_rate(params) != 0)
+		return -EINVAL;
+
+	switch (freq / params_rate(params)) {
+	case 1024: /* fs */
+		div = 0;
+		break;
+	case 1536: /* 2/3 fs */
+		div = 1;
+		break;
+	case 2048: /* 1/2 fs */
+		div = 2;
+		break;
+	case 3072: /* 1/3 fs */
+		div = 3;
+		break;
+	case 4096: /* 1/4 fs */
+		div = 4;
+		break;
+	case 6144: /* 1/6 fs */
+		div = 5;
+		break;
+	case 5632: /* 2/11 fs */
+		div = 6;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	adau1373_dai->enable_src = (div != 0);
+
+	snd_soc_update_bits(codec, ADAU1373_BCLKDIV(dai->id),
+		~ADAU1373_BCLKDIV_SOURCE, (div << 2) | ADAU1373_BCLKDIV_64);
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		ctrl = ADAU1373_DAI_WLEN_16;
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		ctrl = ADAU1373_DAI_WLEN_20;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ctrl = ADAU1373_DAI_WLEN_24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		ctrl = ADAU1373_DAI_WLEN_32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return snd_soc_update_bits(codec, ADAU1373_DAI(dai->id),
+			ADAU1373_DAI_WLEN_MASK, ctrl);
+}
+
+static int adau1373_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+	struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+	unsigned int ctrl;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		ctrl = ADAU1373_DAI_MASTER;
+		adau1373_dai->master = true;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		ctrl = 0;
+		adau1373_dai->master = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		ctrl |= ADAU1373_DAI_FORMAT_I2S;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		ctrl |= ADAU1373_DAI_FORMAT_LEFT_J;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		ctrl |= ADAU1373_DAI_FORMAT_RIGHT_J;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		ctrl |= ADAU1373_DAI_FORMAT_DSP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		ctrl |= ADAU1373_DAI_INVERT_BCLK;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		ctrl |= ADAU1373_DAI_INVERT_LRCLK;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		ctrl |= ADAU1373_DAI_INVERT_LRCLK | ADAU1373_DAI_INVERT_BCLK;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, ADAU1373_DAI(dai->id),
+		~ADAU1373_DAI_WLEN_MASK, ctrl);
+
+	return 0;
+}
+
+static int adau1373_set_dai_sysclk(struct snd_soc_dai *dai,
+	int clk_id, unsigned int freq, int dir)
+{
+	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(dai->codec);
+	struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+
+	switch (clk_id) {
+	case ADAU1373_CLK_SRC_PLL1:
+	case ADAU1373_CLK_SRC_PLL2:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	adau1373_dai->sysclk = freq;
+	adau1373_dai->clk_src = clk_id;
+
+	snd_soc_update_bits(dai->codec, ADAU1373_BCLKDIV(dai->id),
+		ADAU1373_BCLKDIV_SOURCE, clk_id << 5);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops adau1373_dai_ops = {
+	.hw_params	= adau1373_hw_params,
+	.set_sysclk	= adau1373_set_dai_sysclk,
+	.set_fmt	= adau1373_set_dai_fmt,
+};
+
+#define ADAU1373_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_driver adau1373_dai_driver[] = {
+	{
+		.id = 0,
+		.name = "adau1373-aif1",
+		.playback = {
+			.stream_name = "AIF1 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.capture = {
+			.stream_name = "AIF1 Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.ops = &adau1373_dai_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.id = 1,
+		.name = "adau1373-aif2",
+		.playback = {
+			.stream_name = "AIF2 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.capture = {
+			.stream_name = "AIF2 Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.ops = &adau1373_dai_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.id = 2,
+		.name = "adau1373-aif3",
+		.playback = {
+			.stream_name = "AIF3 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.capture = {
+			.stream_name = "AIF3 Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.ops = &adau1373_dai_ops,
+		.symmetric_rates = 1,
+	},
+};
+
+static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
+	int source, unsigned int freq_in, unsigned int freq_out)
+{
+	unsigned int dpll_div = 0;
+	unsigned int x, r, n, m, i, j, mode;
+
+	switch (pll_id) {
+	case ADAU1373_PLL1:
+	case ADAU1373_PLL2:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (source) {
+	case ADAU1373_PLL_SRC_BCLK1:
+	case ADAU1373_PLL_SRC_BCLK2:
+	case ADAU1373_PLL_SRC_BCLK3:
+	case ADAU1373_PLL_SRC_LRCLK1:
+	case ADAU1373_PLL_SRC_LRCLK2:
+	case ADAU1373_PLL_SRC_LRCLK3:
+	case ADAU1373_PLL_SRC_MCLK1:
+	case ADAU1373_PLL_SRC_MCLK2:
+	case ADAU1373_PLL_SRC_GPIO1:
+	case ADAU1373_PLL_SRC_GPIO2:
+	case ADAU1373_PLL_SRC_GPIO3:
+	case ADAU1373_PLL_SRC_GPIO4:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (freq_in < 7813 || freq_in > 27000000)
+		return -EINVAL;
+
+	if (freq_out < 45158000 || freq_out > 49152000)
+		return -EINVAL;
+
+	/* APLL input needs to be >= 8Mhz, so in case freq_in is less we use the
+	 * DPLL to get it there. DPLL_out = (DPLL_in / div) * 1024 */
+	while (freq_in < 8000000) {
+		freq_in *= 2;
+		dpll_div++;
+	}
+
+	if (freq_out % freq_in != 0) {
+		/* fout = fin * (r + (n/m)) / x */
+		x = DIV_ROUND_UP(freq_in, 13500000);
+		freq_in /= x;
+		r = freq_out / freq_in;
+		i = freq_out % freq_in;
+		j = gcd(i, freq_in);
+		n = i / j;
+		m = freq_in / j;
+		x--;
+		mode = 1;
+	} else {
+		/* fout = fin / r */
+		r = freq_out / freq_in;
+		n = 0;
+		m = 0;
+		x = 0;
+		mode = 0;
+	}
+
+	if (r < 2 || r > 8 || x > 3 || m > 0xffff || n > 0xffff)
+		return -EINVAL;
+
+	if (dpll_div) {
+		dpll_div = 11 - dpll_div;
+		snd_soc_update_bits(codec, ADAU1373_PLL_CTRL6(pll_id),
+			ADAU1373_PLL_CTRL6_DPLL_BYPASS, 0);
+	} else {
+		snd_soc_update_bits(codec, ADAU1373_PLL_CTRL6(pll_id),
+			ADAU1373_PLL_CTRL6_DPLL_BYPASS,
+			ADAU1373_PLL_CTRL6_DPLL_BYPASS);
+	}
+
+	snd_soc_write(codec, ADAU1373_DPLL_CTRL(pll_id),
+		(source << 4) | dpll_div);
+	snd_soc_write(codec, ADAU1373_PLL_CTRL1(pll_id), (m >> 8) & 0xff);
+	snd_soc_write(codec, ADAU1373_PLL_CTRL2(pll_id), m & 0xff);
+	snd_soc_write(codec, ADAU1373_PLL_CTRL3(pll_id), (n >> 8) & 0xff);
+	snd_soc_write(codec, ADAU1373_PLL_CTRL4(pll_id), n & 0xff);
+	snd_soc_write(codec, ADAU1373_PLL_CTRL5(pll_id),
+		(r << 3) | (x << 1) | mode);
+
+	/* Set SYSCLK to 256 * fs */
+	snd_soc_update_bits(codec, ADAU1373_CLK_SRC_DIV(pll_id), 0x3f, 0x09);
+
+	return 0;
+}
+
+static void adau1373_load_drc_settings(struct snd_soc_codec *codec,
+	unsigned int nr, uint8_t *drc)
+{
+	unsigned int i;
+
+	for (i = 0; i < 13; ++i)
+		snd_soc_write(codec, ADAU1373_DRC(nr) + i, drc[i]);
+}
+
+static int adau1373_probe(struct snd_soc_codec *codec)
+{
+	struct adau1373_platform_data *pdata = codec->dev->platform_data;
+	bool lineout_differential = false;
+	unsigned int val;
+	int ret;
+	int i;
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+	if (ret) {
+		dev_err(codec->dev, "failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	codec->dapm.idle_bias_off = true;
+
+	snd_soc_write(codec, ADAU1373_ADC_CTRL,
+	    ADAU1373_ADC_CTRL_RESET_FORCE | ADAU1373_ADC_CTRL_PEAK_DETECT);
+
+	if (pdata) {
+		for (i = 0; i < pdata->num_drc; ++i) {
+			adau1373_load_drc_settings(codec, i,
+				pdata->drc_setting[i]);
+		}
+
+		snd_soc_add_controls(codec, adau1373_drc_controls,
+			pdata->num_drc);
+
+		val = 0;
+		for (i = 0; i < 4; ++i) {
+			if (pdata->input_differential[i])
+				val |= BIT(i);
+		}
+		snd_soc_write(codec, ADAU1373_INPUT_MODE, val);
+
+		val = 0;
+		if (pdata->lineout_differential)
+			val |= ADAU1373_OUTPUT_CTRL_LDIFF;
+		if (pdata->lineout_ground_sense)
+			val |= ADAU1373_OUTPUT_CTRL_LNFBEN;
+		snd_soc_write(codec, ADAU1373_OUTPUT_CTRL, val);
+
+		lineout_differential = pdata->lineout_differential;
+	}
+
+	if (!lineout_differential) {
+		snd_soc_add_controls(codec, adau1373_lineout2_controls,
+			ARRAY_SIZE(adau1373_lineout2_controls));
+	}
+
+	return 0;
+}
+
+static int adau1373_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_update_bits(codec, ADAU1373_PWDN_CTRL3, 1, 1);
+		break;
+	case SND_SOC_BIAS_OFF:
+		snd_soc_update_bits(codec, ADAU1373_PWDN_CTRL3, 1, 0);
+		break;
+	}
+	codec->dapm.bias_level = level;
+	return 0;
+}
+
+static int adau1373_remove(struct snd_soc_codec *codec)
+{
+	adau1373_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int adau1373_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+	return adau1373_set_bias_level(codec, SND_SOC_BIAS_OFF);
+}
+
+static int adau1373_resume(struct snd_soc_codec *codec)
+{
+	adau1373_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_cache_sync(codec);
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver adau1373_codec_driver = {
+	.probe =	adau1373_probe,
+	.remove =	adau1373_remove,
+	.suspend =	adau1373_suspend,
+	.resume =	adau1373_resume,
+	.set_bias_level = adau1373_set_bias_level,
+	.reg_cache_size = ARRAY_SIZE(adau1373_default_regs),
+	.reg_cache_default = adau1373_default_regs,
+	.reg_word_size = sizeof(uint8_t),
+
+	.set_pll = adau1373_set_pll,
+
+	.controls = adau1373_controls,
+	.num_controls = ARRAY_SIZE(adau1373_controls),
+	.dapm_widgets = adau1373_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(adau1373_dapm_widgets),
+	.dapm_routes = adau1373_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(adau1373_dapm_routes),
+};
+
+static int __devinit adau1373_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	struct adau1373 *adau1373;
+	int ret;
+
+	adau1373 = kzalloc(sizeof(*adau1373), GFP_KERNEL);
+	if (!adau1373)
+		return -ENOMEM;
+
+	dev_set_drvdata(&client->dev, adau1373);
+
+	ret = snd_soc_register_codec(&client->dev, &adau1373_codec_driver,
+			adau1373_dai_driver, ARRAY_SIZE(adau1373_dai_driver));
+	if (ret < 0)
+		kfree(adau1373);
+
+	return ret;
+}
+
+static int __devexit adau1373_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	kfree(dev_get_drvdata(&client->dev));
+	return 0;
+}
+
+static const struct i2c_device_id adau1373_i2c_id[] = {
+	{ "adau1373", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, adau1373_i2c_id);
+
+static struct i2c_driver adau1373_i2c_driver = {
+	.driver = {
+		.name = "adau1373",
+		.owner = THIS_MODULE,
+	},
+	.probe = adau1373_i2c_probe,
+	.remove = __devexit_p(adau1373_i2c_remove),
+	.id_table = adau1373_i2c_id,
+};
+
+static int __init adau1373_init(void)
+{
+	return i2c_add_driver(&adau1373_i2c_driver);
+}
+module_init(adau1373_init);
+
+static void __exit adau1373_exit(void)
+{
+	i2c_del_driver(&adau1373_i2c_driver);
+}
+module_exit(adau1373_exit);
+
+MODULE_DESCRIPTION("ASoC ADAU1373 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1373.h b/sound/soc/codecs/adau1373.h
new file mode 100644
index 0000000..c6ab553
--- /dev/null
+++ b/sound/soc/codecs/adau1373.h
@@ -0,0 +1,29 @@
+#ifndef __ADAU1373_H__
+#define __ADAU1373_H__
+
+enum adau1373_pll_src {
+	ADAU1373_PLL_SRC_MCLK1 = 0,
+	ADAU1373_PLL_SRC_BCLK1 = 1,
+	ADAU1373_PLL_SRC_BCLK2 = 2,
+	ADAU1373_PLL_SRC_BCLK3 = 3,
+	ADAU1373_PLL_SRC_LRCLK1 = 4,
+	ADAU1373_PLL_SRC_LRCLK2 = 5,
+	ADAU1373_PLL_SRC_LRCLK3 = 6,
+	ADAU1373_PLL_SRC_GPIO1 = 7,
+	ADAU1373_PLL_SRC_GPIO2 = 8,
+	ADAU1373_PLL_SRC_GPIO3 = 9,
+	ADAU1373_PLL_SRC_GPIO4 = 10,
+	ADAU1373_PLL_SRC_MCLK2 = 11,
+};
+
+enum adau1373_pll {
+	ADAU1373_PLL1 = 0,
+	ADAU1373_PLL2 = 1,
+};
+
+enum adau1373_clk_src {
+	ADAU1373_CLK_SRC_PLL1 = 0,
+	ADAU1373_CLK_SRC_PLL2 = 1,
+};
+
+#endif
-- 
1.7.2.5


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

* [PATCH 2/4] ASoC: Add ADAU1373 codec support
@ 2011-08-10  3:52   ` Lars-Peter Clausen
  0 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-10  3:52 UTC (permalink / raw)
  To: Mark Brown, Liam Girdwood
  Cc: Mike Frysinger, alsa-devel, Lars-Peter Clausen, linux-kernel,
	device-drivers-devel

This patch adds support for the Analog Devices ADAU1373 audio codec.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 include/sound/adau1373.h    |   22 +
 sound/soc/codecs/Kconfig    |    3 +
 sound/soc/codecs/Makefile   |    2 +
 sound/soc/codecs/adau1373.c | 1374 +++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/adau1373.h |   29 +
 5 files changed, 1430 insertions(+), 0 deletions(-)
 create mode 100644 include/sound/adau1373.h
 create mode 100644 sound/soc/codecs/adau1373.c
 create mode 100644 sound/soc/codecs/adau1373.h

diff --git a/include/sound/adau1373.h b/include/sound/adau1373.h
new file mode 100644
index 0000000..41a622e
--- /dev/null
+++ b/include/sound/adau1373.h
@@ -0,0 +1,22 @@
+/*
+ * Analog Devices ADAU1373 Audio Codec drive
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __SOUND_ADAU1373_H__
+#define __SOUND_ADAU1373_H__
+
+struct adau1373_platform_data {
+	bool input_differential[4];
+	bool lineout_differential;
+	bool lineout_ground_sense;
+
+	unsigned int num_drc;
+	uint8_t drc_setting[3][13];
+};
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 665d924..bac0edb 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -139,6 +139,9 @@ config SND_SOC_ADAU1701
 	select SIGMA
 	tristate
 
+config SND_SOC_ADAU1373
+	tristate
+
 config SND_SOC_ADAV80X
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 5119a7e..70c1769 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -5,6 +5,7 @@ snd-soc-ad193x-objs := ad193x.o
 snd-soc-ad1980-objs := ad1980.o
 snd-soc-ad73311-objs := ad73311.o
 snd-soc-adau1701-objs := adau1701.o
+snd-soc-adau1373-objs := adau1373.o
 snd-soc-adav80x-objs := adav80x.o
 snd-soc-ads117x-objs := ads117x.o
 snd-soc-ak4104-objs := ak4104.o
@@ -100,6 +101,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_ADAU1373)	+= snd-soc-adau1373.o
 obj-$(CONFIG_SND_SOC_ADAU1701)  += snd-soc-adau1701.o
 obj-$(CONFIG_SND_SOC_ADAV80X)  += snd-soc-adav80x.o
 obj-$(CONFIG_SND_SOC_ADS117X)	+= snd-soc-ads117x.o
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
new file mode 100644
index 0000000..91ee728
--- /dev/null
+++ b/sound/soc/codecs/adau1373.c
@@ -0,0 +1,1374 @@
+/*
+ * Analog Devices ADAU1373 Audio Codec drive
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/gcd.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/adau1373.h>
+
+#include "adau1373.h"
+
+struct adau1373_dai {
+	unsigned int clk_src;
+	unsigned int sysclk;
+	bool enable_src;
+	bool master;
+};
+
+struct adau1373 {
+	struct adau1373_dai dais[3];
+};
+
+#define ADAU1373_INPUT_MODE	0x00
+#define ADAU1373_AINL_CTRL(x)	(0x01 + (x) * 2)
+#define ADAU1373_AINR_CTRL(x)	(0x02 + (x) * 2)
+#define ADAU1373_LLINE_OUT(x)	(0x9 + (x) * 2)
+#define ADAU1373_RLINE_OUT(x)	(0xa + (x) * 2)
+#define ADAU1373_LSPK_OUT	0x0d
+#define ADAU1373_RSPK_OUT	0x0e
+#define ADAU1373_LHP_OUT	0x0f
+#define ADAU1373_RHP_OUT	0x10
+#define ADAU1373_ADC_GAIN	0x11
+#define ADAU1373_LADC_MIXER	0x12
+#define ADAU1373_RADC_MIXER	0x13
+#define ADAU1373_LLINE1_MIX	0x14
+#define ADAU1373_RLINE1_MIX	0x15
+#define ADAU1373_LLINE2_MIX	0x16
+#define ADAU1373_RLINE2_MIX	0x17
+#define ADAU1373_LSPK_MIX	0x18
+#define ADAU1373_RSPK_MIX	0x19
+#define ADAU1373_LHP_MIX	0x1a
+#define ADAU1373_RHP_MIX	0x1b
+#define ADAU1373_EP_MIX		0x1c
+#define ADAU1373_HP_CTRL	0x1d
+#define ADAU1373_HP_CTRL2	0x1e
+#define ADAU1373_LS_CTRL	0x1f
+#define ADAU1373_EP_CTRL	0x21
+#define ADAU1373_MICBIAS_CTRL1	0x22
+#define ADAU1373_MICBIAS_CTRL2	0x23
+#define ADAU1373_OUTPUT_CTRL	0x24
+#define ADAU1373_PWDN_CTRL1	0x25
+#define ADAU1373_PWDN_CTRL2	0x26
+#define ADAU1373_PWDN_CTRL3	0x27
+#define ADAU1373_DPLL_CTRL(x)	(0x28 + (x) * 7)
+#define ADAU1373_PLL_CTRL1(x)	(0x29 + (x) * 7)
+#define ADAU1373_PLL_CTRL2(x)	(0x2a + (x) * 7)
+#define ADAU1373_PLL_CTRL3(x)	(0x2b + (x) * 7)
+#define ADAU1373_PLL_CTRL4(x)	(0x2c + (x) * 7)
+#define ADAU1373_PLL_CTRL5(x)	(0x2d + (x) * 7)
+#define ADAU1373_PLL_CTRL6(x)	(0x2e + (x) * 7)
+#define ADAU1373_PLL_CTRL7(x)	(0x2f + (x) * 7)
+#define ADAU1373_HEADDECT	0x36
+#define ADAU1373_ADC_DAC_STATUS	0x37
+#define ADAU1373_ADC_CTRL	0x3c
+#define ADAU1373_DAI(x)		(0x44 + (x))
+#define ADAU1373_CLK_SRC_DIV(x)	(0x40 + (x) * 2)
+#define ADAU1373_BCLKDIV(x)	(0x47 + (x))
+#define ADAU1373_SRC_RATIOA(x)	(0x4a + (x) * 2)
+#define ADAU1373_SRC_RATIOB(x)	(0x4b + (x) * 2)
+#define ADAU1373_DEEMP_CTRL	0x50
+#define ADAU1373_SRC_DAI_CTRL(x) (0x51 + (x))
+#define ADAU1373_DIN_MIX_CTRL(x) (0x56 + (x))
+#define ADAU1373_DOUT_MIX_CTRL(x) (0x5b + (x))
+#define ADAU1373_DAI_PBL_VOL(x)	(0x62 + (x) * 2)
+#define ADAU1373_DAI_PBR_VOL(x)	(0x63 + (x) * 2)
+#define ADAU1373_DAI_RECL_VOL(x) (0x68 + (x) * 2)
+#define ADAU1373_DAI_RECR_VOL(x) (0x69 + (x) * 2)
+#define ADAU1373_DAC1_PBL_VOL	0x6e
+#define ADAU1373_DAC1_PBR_VOL	0x6f
+#define ADAU1373_DAC2_PBL_VOL	0x70
+#define ADAU1373_DAC2_PBR_VOL	0x71
+#define ADAU1373_ADC_RECL_VOL	0x72
+#define ADAU1373_ADC_RECR_VOL	0x73
+#define ADAU1373_DMIC_RECL_VOL	0x74
+#define ADAU1373_DMIC_RECR_VOL	0x75
+#define ADAU1373_VOL_GAIN1	0x76
+#define ADAU1373_VOL_GAIN2	0x77
+#define ADAU1373_VOL_GAIN3	0x78
+#define ADAU1373_HPF_CTRL	0x7d
+#define ADAU1373_BASS1		0x7e
+#define ADAU1373_BASS2		0x7f
+#define ADAU1373_DRC(x)		(0x80 + (x) * 0x10)
+#define ADAU1373_3D_CTRL1	0xc0
+#define ADAU1373_3D_CTRL2	0xc1
+#define ADAU1373_FDSP_SEL1	0xdc
+#define ADAU1373_FDSP_SEL2	0xdd
+#define ADAU1373_FDSP_SEL3	0xde
+#define ADAU1373_FDSP_SEL4	0xdf
+#define ADAU1373_DIGMICCTRL	0xe2
+#define ADAU1373_DIGEN		0xeb
+#define ADAU1373_SOFT_RESET	0xff
+
+
+#define ADAU1373_PLL_CTRL6_DPLL_BYPASS	BIT(1)
+#define ADAU1373_PLL_CTRL6_PLL_EN	BIT(0)
+
+#define ADAU1373_DAI_INVERT_BCLK	BIT(7)
+#define ADAU1373_DAI_MASTER		BIT(6)
+#define ADAU1373_DAI_INVERT_LRCLK	BIT(4)
+#define ADAU1373_DAI_WLEN_16		0x0
+#define ADAU1373_DAI_WLEN_20		0x4
+#define ADAU1373_DAI_WLEN_24		0x8
+#define ADAU1373_DAI_WLEN_32		0xc
+#define ADAU1373_DAI_WLEN_MASK		0xc
+#define ADAU1373_DAI_FORMAT_RIGHT_J	0x0
+#define ADAU1373_DAI_FORMAT_LEFT_J	0x1
+#define ADAU1373_DAI_FORMAT_I2S		0x2
+#define ADAU1373_DAI_FORMAT_DSP		0x3
+
+#define ADAU1373_BCLKDIV_SOURCE		BIT(5)
+#define ADAU1373_BCLKDIV_32		0x03
+#define ADAU1373_BCLKDIV_64		0x02
+#define ADAU1373_BCLKDIV_128		0x01
+#define ADAU1373_BCLKDIV_256		0x00
+
+#define ADAU1373_ADC_CTRL_PEAK_DETECT	BIT(0)
+#define ADAU1373_ADC_CTRL_RESET		BIT(1)
+#define ADAU1373_ADC_CTRL_RESET_FORCE	BIT(2)
+
+#define ADAU1373_OUTPUT_CTRL_LDIFF	BIT(3)
+#define ADAU1373_OUTPUT_CTRL_LNFBEN	BIT(2)
+
+
+static const uint8_t adau1373_default_regs[] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, /* 0x30 */
+	0x00, 0x00, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x00, /* 0x40 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x78, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, /* 0x80 */
+	0x00, 0xc0, 0x88, 0x7a, 0xdf, 0x20, 0x00, 0x00,
+	0x78, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, /* 0x90 */
+	0x00, 0xc0, 0x88, 0x7a, 0xdf, 0x20, 0x00, 0x00,
+	0x78, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, /* 0xa0 */
+	0x00, 0xc0, 0x88, 0x7a, 0xdf, 0x20, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0 */
+	0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, /* 0xe0 */
+	0x00, 0x1f, 0x0f, 0x00, 0x00,
+};
+
+static const unsigned int adau1373_out_tlv[] = {
+	TLV_DB_RANGE_HEAD(4),
+	0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1),
+	8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0),
+	16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0),
+	24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0),
+};
+
+static const DECLARE_TLV_DB_MINMAX(adau1373_digital_tlv, -9563, 0);
+static const DECLARE_TLV_DB_SCALE(adau1373_in_pga_tlv, -1300, 100, 1);
+static const DECLARE_TLV_DB_SCALE(adau1373_ep_tlv, -600, 600, 1);
+
+static const char *adau1373_fdsp_sel_text[] = {
+	"None",
+	"Channel 1",
+	"Channel 2",
+	"Channel 3",
+	"Channel 4",
+	"Channel 5",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_drc1_channel_enum,
+	ADAU1373_FDSP_SEL1, 4, adau1373_fdsp_sel_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_drc2_channel_enum,
+	ADAU1373_FDSP_SEL1, 0, adau1373_fdsp_sel_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_drc3_channel_enum,
+	ADAU1373_FDSP_SEL2, 0, adau1373_fdsp_sel_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_hpf_channel_enum,
+	ADAU1373_FDSP_SEL3, 0, adau1373_fdsp_sel_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_bass_channel_enum,
+	ADAU1373_FDSP_SEL4, 4, adau1373_fdsp_sel_text);
+
+static const char *adau1373_hpf_cutoff_text[] = {
+	"3.7Hz", "50Hz", "100Hz", "150Hz", "200Hz", "250Hz", "300Hz", "350Hz",
+	"400Hz", "450Hz", "500Hz", "550Hz", "600Hz", "650Hz", "700Hz", "750Hz",
+	"800Hz",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_hpf_cutoff_enum,
+	ADAU1373_HPF_CTRL, 3, adau1373_hpf_cutoff_text);
+
+static const char *adau1373_bass_lpf_cutoff_text[] = {
+	"801Hz", "1001Hz",
+};
+
+static const char *adau1373_bass_clip_level_text[] = {
+	"0.125", "0.250", "0.370", "0.500", "0.625", "0.750", "0.875",
+};
+
+static const unsigned int adau1373_bass_clip_level_values[] = {
+	1, 2, 3, 4, 5, 6, 7,
+};
+
+static const char *adau1373_bass_hpf_cutoff_text[] = {
+	"158Hz", "232Hz", "347Hz", "520Hz",
+};
+
+static const unsigned int adau1373_bass_tlv[] = {
+	TLV_DB_RANGE_HEAD(4),
+	0, 2, TLV_DB_SCALE_ITEM(-600, 600, 1),
+	3, 4, TLV_DB_SCALE_ITEM(950, 250, 0),
+	5, 7, TLV_DB_SCALE_ITEM(1400, 150, 0),
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_bass_lpf_cutoff_enum,
+	ADAU1373_BASS1, 5, adau1373_bass_lpf_cutoff_text);
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(adau1373_bass_clip_level_enum,
+	ADAU1373_BASS1, 2, 7, adau1373_bass_clip_level_text,
+	adau1373_bass_clip_level_values);
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_bass_hpf_cutoff_enum,
+	ADAU1373_BASS1, 0, adau1373_bass_hpf_cutoff_text);
+
+static const char *adau1373_3d_level_text[] = {
+	"0%", "6.67%", "13.33%", "20%", "26.67%", "33.33%",
+	"40%", "46.67%", "53.33%", "60%", "66.67%", "73.33%",
+	"80%", "86.67", "99.33%", "100%"
+};
+
+static const char *adau1373_3d_cutoff_text[] = {
+	"No 3D", "0.03125 fs", "0.04583 fs", "0.075 fs", "0.11458 fs",
+	"0.16875 fs", "0.27083 fs"
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_3d_level_enum,
+	ADAU1373_3D_CTRL1, 4, adau1373_3d_level_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_3d_cutoff_enum,
+	ADAU1373_3D_CTRL1, 0, adau1373_3d_cutoff_text);
+
+static const unsigned int adau1373_3d_tlv[] = {
+	TLV_DB_RANGE_HEAD(2),
+	0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+	1, 7, TLV_DB_LINEAR_ITEM(-1800, -120),
+};
+
+static const char *adau1373_mono_stereo_text[] = {
+	"Mute",
+	"Mono Right Channel (L+R)",
+	"Mono Left Channel (L+R)",
+	"Stereo",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_lineout1_mode_enum,
+	ADAU1373_OUTPUT_CTRL, 4, adau1373_mono_stereo_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_lineout2_mode_enum,
+	ADAU1373_OUTPUT_CTRL, 6, adau1373_mono_stereo_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_speaker_mode_enum,
+	ADAU1373_LS_CTRL, 4, adau1373_mono_stereo_text);
+
+static const char *adau1373_micbias_text[] = {
+	"2.9 V",
+	"2.2 V",
+	"2.6 V",
+	"1.8 V",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_micbias2_enum,
+	ADAU1373_EP_CTRL, 4, adau1373_micbias_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_micbias1_enum,
+	ADAU1373_EP_CTRL, 2, adau1373_micbias_text);
+
+static const struct snd_kcontrol_new adau1373_controls[] = {
+	SOC_DOUBLE_R_TLV("AIF1 Capture Volume", ADAU1373_DAI_RECL_VOL(0),
+		ADAU1373_DAI_RECR_VOL(0), 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("AIF2 Capture Volume", ADAU1373_DAI_RECL_VOL(1),
+		ADAU1373_DAI_RECR_VOL(1), 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("AIF3 Capture Volume", ADAU1373_DAI_RECL_VOL(2),
+		ADAU1373_DAI_RECR_VOL(2), 0, 0xff, 1, adau1373_digital_tlv),
+
+	SOC_DOUBLE_R_TLV("ADC Capture Volume", ADAU1373_ADC_RECL_VOL,
+		ADAU1373_ADC_RECR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("DMIC Capture Volume", ADAU1373_DMIC_RECL_VOL,
+		ADAU1373_DMIC_RECR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+
+	SOC_DOUBLE_R_TLV("AIF1 Playback Volume", ADAU1373_DAI_PBL_VOL(0),
+		ADAU1373_DAI_PBR_VOL(0), 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("AIF2 Playback Volume", ADAU1373_DAI_PBL_VOL(1),
+		ADAU1373_DAI_PBR_VOL(1), 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("AIF3 Playback Volume", ADAU1373_DAI_PBL_VOL(2),
+		ADAU1373_DAI_PBR_VOL(2), 0, 0xff, 1, adau1373_digital_tlv),
+
+	SOC_DOUBLE_R_TLV("DAC1 Playback Volume", ADAU1373_DAC1_PBL_VOL,
+		ADAU1373_DAC1_PBR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("DAC2 Playback Volume", ADAU1373_DAC2_PBL_VOL,
+		ADAU1373_DAC2_PBR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+
+	SOC_DOUBLE_R_TLV("Lineout1 Playback Volume", ADAU1373_LLINE_OUT(0),
+		ADAU1373_RLINE_OUT(0), 0, 0x1f, 0, adau1373_out_tlv),
+	SOC_DOUBLE_R_TLV("Speaker Playback Volume", ADAU1373_LSPK_OUT,
+		ADAU1373_RSPK_OUT, 0, 0x1f, 0, adau1373_out_tlv),
+	SOC_DOUBLE_R_TLV("Headphone Playback Volume", ADAU1373_LHP_OUT,
+		ADAU1373_RHP_OUT, 0, 0x1f, 0, adau1373_out_tlv),
+
+	SOC_DOUBLE_R_TLV("Input 1 Capture Volume", ADAU1373_AINL_CTRL(0),
+		ADAU1373_AINR_CTRL(0), 0, 0x1f, 0, adau1373_in_pga_tlv),
+	SOC_DOUBLE_R_TLV("Input 2 Capture Volume", ADAU1373_AINL_CTRL(1),
+		ADAU1373_AINR_CTRL(1), 0, 0x1f, 0, adau1373_in_pga_tlv),
+	SOC_DOUBLE_R_TLV("Input 3 Capture Volume", ADAU1373_AINL_CTRL(2),
+		ADAU1373_AINR_CTRL(2), 0, 0x1f, 0, adau1373_in_pga_tlv),
+	SOC_DOUBLE_R_TLV("Input 4 Capture Volume", ADAU1373_AINL_CTRL(3),
+		ADAU1373_AINR_CTRL(3), 0, 0x1f, 0, adau1373_in_pga_tlv),
+
+	SOC_SINGLE_TLV("Earpiece Playback Volume", ADAU1373_EP_CTRL, 0, 3, 0,
+		adau1373_ep_tlv),
+
+	SOC_DOUBLE("AIF3 Playback Boost(+6dB)", ADAU1373_VOL_GAIN1, 4, 5, 1, 0),
+	SOC_DOUBLE("AIF2 Playback Boost(+6dB)", ADAU1373_VOL_GAIN1, 2, 3, 1, 0),
+	SOC_DOUBLE("AIF1 Playback Boost(+6dB)", ADAU1373_VOL_GAIN1, 0, 1, 1, 0),
+	SOC_DOUBLE("AIF3 Capture Boost(+6dB)", ADAU1373_VOL_GAIN2, 4, 5, 1, 0),
+	SOC_DOUBLE("AIF2 Capture Boost(+6dB)", ADAU1373_VOL_GAIN2, 2, 3, 1, 0),
+	SOC_DOUBLE("AIF1 Capture Boost(+6dB)", ADAU1373_VOL_GAIN2, 0, 1, 1, 0),
+	SOC_DOUBLE("DMIC Capture Boost(+6dB)", ADAU1373_VOL_GAIN3, 6, 7, 1, 0),
+	SOC_DOUBLE("ADC Capture Boost(+6dB)", ADAU1373_VOL_GAIN3, 4, 5, 1, 0),
+	SOC_DOUBLE("DAC2 Playback Boost(+6dB)", ADAU1373_VOL_GAIN3, 2, 3, 1, 0),
+	SOC_DOUBLE("DAC1 Playback Boost(+6dB)", ADAU1373_VOL_GAIN3, 0, 1, 1, 0),
+
+	SOC_DOUBLE("Input1 Boost(+20dB)", ADAU1373_ADC_GAIN, 0, 4, 1, 0),
+	SOC_DOUBLE("Input2 Boost(+20dB)", ADAU1373_ADC_GAIN, 1, 5, 1, 0),
+	SOC_DOUBLE("Input3 Boost(+20dB)", ADAU1373_ADC_GAIN, 2, 6, 1, 0),
+	SOC_DOUBLE("Input4 Boost(+20dB)", ADAU1373_ADC_GAIN, 3, 7, 1, 0),
+
+	SOC_ENUM("Lineout1 Mono Stereo", adau1373_lineout1_mode_enum),
+	SOC_ENUM("Speaker Mono Stereo", adau1373_speaker_mode_enum),
+
+	SOC_ENUM("Mic Bias1", adau1373_micbias1_enum),
+	SOC_ENUM("Mic Bias2", adau1373_micbias2_enum),
+
+	SOC_ENUM("HPF Cutoff", adau1373_hpf_cutoff_enum),
+	SOC_DOUBLE("HPF Switch", ADAU1373_HPF_CTRL, 1, 0, 1, 0),
+	SOC_ENUM("HPF Channel", adau1373_hpf_channel_enum),
+
+	SOC_ENUM("Bass HPF Cutoff", adau1373_bass_hpf_cutoff_enum),
+	SOC_VALUE_ENUM("Bass Clip Level Threshold",
+	    adau1373_bass_clip_level_enum),
+	SOC_ENUM("Bass LPF Cutoff", adau1373_bass_lpf_cutoff_enum),
+	SOC_DOUBLE("Bass Switch", ADAU1373_BASS2, 0, 1, 1, 0),
+	SOC_SINGLE_TLV("Bass Volume", ADAU1373_BASS2, 2, 7, 0,
+	    adau1373_bass_tlv),
+	SOC_ENUM("Bass Channel", adau1373_bass_channel_enum),
+
+	SOC_ENUM("3D HPF Cutoff", adau1373_3d_cutoff_enum),
+	SOC_ENUM("3D Level", adau1373_3d_level_enum),
+	SOC_SINGLE("3D Switch", ADAU1373_3D_CTRL2, 0, 1, 0),
+	SOC_SINGLE_TLV("3D Volume", ADAU1373_3D_CTRL2, 2, 7, 0, adau1373_3d_tlv),
+	SOC_ENUM("3D Channel", adau1373_bass_channel_enum),
+};
+
+static const struct snd_kcontrol_new adau1373_lineout2_controls[] = {
+	SOC_DOUBLE_R_TLV("Lineout2 Playback Volume", ADAU1373_LLINE_OUT(1),
+		ADAU1373_RLINE_OUT(1), 0, 0x1f, 0, adau1373_out_tlv),
+	SOC_ENUM("Lineout2 Mono Stereo", adau1373_lineout2_mode_enum),
+};
+
+static const struct snd_kcontrol_new adau1373_drc_controls[] = {
+	SOC_ENUM("DRC1 Channel", adau1373_drc1_channel_enum),
+	SOC_ENUM("DRC2 Channel", adau1373_drc2_channel_enum),
+	SOC_ENUM("DRC3 Channel", adau1373_drc3_channel_enum),
+};
+
+static int adau1373_pll_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	unsigned int pll_id = w->name[3] - '1';
+	unsigned int val;
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		val = ADAU1373_PLL_CTRL6_PLL_EN;
+	else
+		val = 0;
+
+	snd_soc_update_bits(codec, ADAU1373_PLL_CTRL6(pll_id),
+		ADAU1373_PLL_CTRL6_PLL_EN, val);
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		mdelay(5);
+
+	return 0;
+}
+
+static const char *adau1373_decimator_text[] = {
+	"ADC",
+	"DMIC1",
+};
+
+static const struct soc_enum adau1373_decimator_enum =
+	SOC_ENUM_SINGLE(0, 0, 2, adau1373_decimator_text);
+
+static const struct snd_kcontrol_new adau1373_decimator_mux =
+	SOC_DAPM_ENUM_VIRT("Decimator Mux", adau1373_decimator_enum);
+
+static const struct snd_kcontrol_new adau1373_left_adc_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DAC1 Switch", ADAU1373_LADC_MIXER, 4, 1, 0),
+	SOC_DAPM_SINGLE("Input 4 Switch", ADAU1373_LADC_MIXER, 3, 1, 0),
+	SOC_DAPM_SINGLE("Input 3 Switch", ADAU1373_LADC_MIXER, 2, 1, 0),
+	SOC_DAPM_SINGLE("Input 2 Switch", ADAU1373_LADC_MIXER, 1, 1, 0),
+	SOC_DAPM_SINGLE("Input 1 Switch", ADAU1373_LADC_MIXER, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1373_right_adc_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DAC1 Switch", ADAU1373_RADC_MIXER, 4, 1, 0),
+	SOC_DAPM_SINGLE("Input 4 Switch", ADAU1373_RADC_MIXER, 3, 1, 0),
+	SOC_DAPM_SINGLE("Input 3 Switch", ADAU1373_RADC_MIXER, 2, 1, 0),
+	SOC_DAPM_SINGLE("Input 2 Switch", ADAU1373_RADC_MIXER, 1, 1, 0),
+	SOC_DAPM_SINGLE("Input 1 Switch", ADAU1373_RADC_MIXER, 0, 1, 0),
+};
+
+#define DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+	SOC_DAPM_SINGLE("Left DAC2 Switch", _reg, 7, 1, 0), \
+	SOC_DAPM_SINGLE("Right DAC2 Switch", _reg, 6, 1, 0), \
+	SOC_DAPM_SINGLE("Left DAC1 Switch", _reg, 5, 1, 0), \
+	SOC_DAPM_SINGLE("Right DAC1 Switch", _reg, 4, 1, 0), \
+	SOC_DAPM_SINGLE("Input 4 Bypass Switch", _reg, 3, 1, 0), \
+	SOC_DAPM_SINGLE("Input 3 Bypass Switch", _reg, 2, 1, 0), \
+	SOC_DAPM_SINGLE("Input 2 Bypass Switch", _reg, 1, 1, 0), \
+	SOC_DAPM_SINGLE("Input 1 Bypass Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_line1_mixer_controls,
+	ADAU1373_LLINE1_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_line1_mixer_controls,
+	ADAU1373_RLINE1_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_line2_mixer_controls,
+	ADAU1373_LLINE2_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_line2_mixer_controls,
+	ADAU1373_RLINE2_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_spk_mixer_controls,
+	ADAU1373_LSPK_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_spk_mixer_controls,
+	ADAU1373_RSPK_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_ep_mixer_controls,
+	ADAU1373_EP_MIX);
+
+static const struct snd_kcontrol_new adau1373_left_hp_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Left DAC1 Switch", ADAU1373_LHP_MIX, 5, 1, 0),
+	SOC_DAPM_SINGLE("Left DAC2 Switch", ADAU1373_LHP_MIX, 4, 1, 0),
+	SOC_DAPM_SINGLE("Input 4 Bypass Switch", ADAU1373_LHP_MIX, 3, 1, 0),
+	SOC_DAPM_SINGLE("Input 3 Bypass Switch", ADAU1373_LHP_MIX, 2, 1, 0),
+	SOC_DAPM_SINGLE("Input 2 Bypass Switch", ADAU1373_LHP_MIX, 1, 1, 0),
+	SOC_DAPM_SINGLE("Input 1 Bypass Switch", ADAU1373_LHP_MIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1373_right_hp_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Right DAC1 Switch", ADAU1373_RHP_MIX, 5, 1, 0),
+	SOC_DAPM_SINGLE("Right DAC2 Switch", ADAU1373_RHP_MIX, 4, 1, 0),
+	SOC_DAPM_SINGLE("Input 4 Bypass Switch", ADAU1373_RHP_MIX, 3, 1, 0),
+	SOC_DAPM_SINGLE("Input 3 Bypass Switch", ADAU1373_RHP_MIX, 2, 1, 0),
+	SOC_DAPM_SINGLE("Input 2 Bypass Switch", ADAU1373_RHP_MIX, 1, 1, 0),
+	SOC_DAPM_SINGLE("Input 1 Bypass Switch", ADAU1373_RHP_MIX, 0, 1, 0),
+};
+
+#define DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+	SOC_DAPM_SINGLE("DMIC2 Swapped Switch", _reg, 6, 1, 0), \
+	SOC_DAPM_SINGLE("DMIC2 Switch", _reg, 5, 1, 0), \
+	SOC_DAPM_SINGLE("ADC/DMIC1 Swapped Switch", _reg, 4, 1, 0), \
+	SOC_DAPM_SINGLE("ADC/DMIC1 Switch", _reg, 3, 1, 0), \
+	SOC_DAPM_SINGLE("AIF3 Switch", _reg, 2, 1, 0), \
+	SOC_DAPM_SINGLE("AIF2 Switch", _reg, 1, 1, 0), \
+	SOC_DAPM_SINGLE("AIF1 Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel1_mixer_controls,
+	ADAU1373_DIN_MIX_CTRL(0));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel2_mixer_controls,
+	ADAU1373_DIN_MIX_CTRL(1));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel3_mixer_controls,
+	ADAU1373_DIN_MIX_CTRL(2));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel4_mixer_controls,
+	ADAU1373_DIN_MIX_CTRL(3));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel5_mixer_controls,
+	ADAU1373_DIN_MIX_CTRL(4));
+
+#define DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+	SOC_DAPM_SINGLE("DSP Channel5 Switch", _reg, 4, 1, 0), \
+	SOC_DAPM_SINGLE("DSP Channel4 Switch", _reg, 3, 1, 0), \
+	SOC_DAPM_SINGLE("DSP Channel3 Switch", _reg, 2, 1, 0), \
+	SOC_DAPM_SINGLE("DSP Channel2 Switch", _reg, 1, 1, 0), \
+	SOC_DAPM_SINGLE("DSP Channel1 Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif1_mixer_controls,
+	ADAU1373_DOUT_MIX_CTRL(0));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif2_mixer_controls,
+	ADAU1373_DOUT_MIX_CTRL(1));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif3_mixer_controls,
+	ADAU1373_DOUT_MIX_CTRL(2));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_dac1_mixer_controls,
+	ADAU1373_DOUT_MIX_CTRL(3));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_dac2_mixer_controls,
+	ADAU1373_DOUT_MIX_CTRL(4));
+
+static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = {
+	/* Datasheet claims Left ADC is bit 6 and Right ADC is bit 7, but that
+	 * doesn't seem to be the case. */
+	SND_SOC_DAPM_ADC("Left ADC", NULL, ADAU1373_PWDN_CTRL1, 7, 0),
+	SND_SOC_DAPM_ADC("Right ADC", NULL, ADAU1373_PWDN_CTRL1, 6, 0),
+
+	SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1373_DIGMICCTRL, 0, 0),
+	SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1373_DIGMICCTRL, 2, 0),
+
+	SND_SOC_DAPM_VIRT_MUX("Decimator Mux", SND_SOC_NOPM, 0, 0,
+		&adau1373_decimator_mux),
+
+	SND_SOC_DAPM_SUPPLY("MICBIAS2", ADAU1373_PWDN_CTRL1, 5, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("MICBIAS1", ADAU1373_PWDN_CTRL1, 4, 0, NULL, 0),
+
+	SND_SOC_DAPM_PGA("IN4PGA", ADAU1373_PWDN_CTRL1, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("IN3PGA", ADAU1373_PWDN_CTRL1, 2, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("IN2PGA", ADAU1373_PWDN_CTRL1, 1, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("IN1PGA", ADAU1373_PWDN_CTRL1, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_DAC("Left DAC2", NULL, ADAU1373_PWDN_CTRL2, 7, 0),
+	SND_SOC_DAPM_DAC("Right DAC2", NULL, ADAU1373_PWDN_CTRL2, 6, 0),
+	SND_SOC_DAPM_DAC("Left DAC1", NULL, ADAU1373_PWDN_CTRL2, 5, 0),
+	SND_SOC_DAPM_DAC("Right DAC1", NULL, ADAU1373_PWDN_CTRL2, 4, 0),
+
+	SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_left_adc_mixer_controls),
+	SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_right_adc_mixer_controls),
+
+	SOC_MIXER_ARRAY("Left Lineout2 Mixer", ADAU1373_PWDN_CTRL2, 3, 0,
+		adau1373_left_line2_mixer_controls),
+	SOC_MIXER_ARRAY("Right Lineout2 Mixer", ADAU1373_PWDN_CTRL2, 2, 0,
+		adau1373_right_line2_mixer_controls),
+	SOC_MIXER_ARRAY("Left Lineout1 Mixer", ADAU1373_PWDN_CTRL2, 1, 0,
+		adau1373_left_line1_mixer_controls),
+	SOC_MIXER_ARRAY("Right Lineout1 Mixer", ADAU1373_PWDN_CTRL2, 0, 0,
+		adau1373_right_line1_mixer_controls),
+
+	SOC_MIXER_ARRAY("Earpiece Mixer", ADAU1373_PWDN_CTRL3, 4, 0,
+		adau1373_ep_mixer_controls),
+	SOC_MIXER_ARRAY("Left Speaker Mixer", ADAU1373_PWDN_CTRL3, 3, 0,
+		adau1373_left_spk_mixer_controls),
+	SOC_MIXER_ARRAY("Right Speaker Mixer", ADAU1373_PWDN_CTRL3, 2, 0,
+		adau1373_right_spk_mixer_controls),
+	SOC_MIXER_ARRAY("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_left_hp_mixer_controls),
+	SOC_MIXER_ARRAY("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_right_hp_mixer_controls),
+	SND_SOC_DAPM_SUPPLY("Headphone Enable", ADAU1373_PWDN_CTRL3, 1, 0,
+		NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("AIF1 CLK", ADAU1373_SRC_DAI_CTRL(0), 0, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF2 CLK", ADAU1373_SRC_DAI_CTRL(1), 0, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF3 CLK", ADAU1373_SRC_DAI_CTRL(2), 0, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF1 IN SRC", ADAU1373_SRC_DAI_CTRL(0), 2, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF1 OUT SRC", ADAU1373_SRC_DAI_CTRL(0), 1, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF2 IN SRC", ADAU1373_SRC_DAI_CTRL(1), 2, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF2 OUT SRC", ADAU1373_SRC_DAI_CTRL(1), 1, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF3 IN SRC", ADAU1373_SRC_DAI_CTRL(2), 2, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF3 OUT SRC", ADAU1373_SRC_DAI_CTRL(2), 1, 0,
+	    NULL, 0),
+
+	SND_SOC_DAPM_AIF_IN("AIF1 IN", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AIF1 OUT", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("AIF2 IN", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AIF2 OUT", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("AIF3 IN", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AIF3 OUT", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+	SOC_MIXER_ARRAY("DSP Channel1 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dsp_channel1_mixer_controls),
+	SOC_MIXER_ARRAY("DSP Channel2 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dsp_channel2_mixer_controls),
+	SOC_MIXER_ARRAY("DSP Channel3 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dsp_channel3_mixer_controls),
+	SOC_MIXER_ARRAY("DSP Channel4 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dsp_channel4_mixer_controls),
+	SOC_MIXER_ARRAY("DSP Channel5 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dsp_channel5_mixer_controls),
+
+	SOC_MIXER_ARRAY("AIF1 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_aif1_mixer_controls),
+	SOC_MIXER_ARRAY("AIF2 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_aif2_mixer_controls),
+	SOC_MIXER_ARRAY("AIF3 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_aif3_mixer_controls),
+	SOC_MIXER_ARRAY("DAC1 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dac1_mixer_controls),
+	SOC_MIXER_ARRAY("DAC2 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dac2_mixer_controls),
+
+	SND_SOC_DAPM_SUPPLY("DSP", ADAU1373_DIGEN, 4, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Recording Engine B", ADAU1373_DIGEN, 3, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Recording Engine A", ADAU1373_DIGEN, 2, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Playback Engine B", ADAU1373_DIGEN, 1, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Playback Engine A", ADAU1373_DIGEN, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("PLL1", SND_SOC_NOPM, 0, 0, adau1373_pll_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SUPPLY("PLL2", SND_SOC_NOPM, 0, 0, adau1373_pll_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SUPPLY("SYSCLK1", ADAU1373_CLK_SRC_DIV(0), 7, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("SYSCLK2", ADAU1373_CLK_SRC_DIV(1), 7, 0, NULL, 0),
+
+	SND_SOC_DAPM_INPUT("AIN1L"),
+	SND_SOC_DAPM_INPUT("AIN1R"),
+	SND_SOC_DAPM_INPUT("AIN2L"),
+	SND_SOC_DAPM_INPUT("AIN2R"),
+	SND_SOC_DAPM_INPUT("AIN3L"),
+	SND_SOC_DAPM_INPUT("AIN3R"),
+	SND_SOC_DAPM_INPUT("AIN4L"),
+	SND_SOC_DAPM_INPUT("AIN4R"),
+
+	SND_SOC_DAPM_INPUT("DMIC1DAT"),
+	SND_SOC_DAPM_INPUT("DMIC2DAT"),
+
+	SND_SOC_DAPM_OUTPUT("LOUT1L"),
+	SND_SOC_DAPM_OUTPUT("LOUT1R"),
+	SND_SOC_DAPM_OUTPUT("LOUT2L"),
+	SND_SOC_DAPM_OUTPUT("LOUT2R"),
+	SND_SOC_DAPM_OUTPUT("HPL"),
+	SND_SOC_DAPM_OUTPUT("HPR"),
+	SND_SOC_DAPM_OUTPUT("SPKL"),
+	SND_SOC_DAPM_OUTPUT("SPKR"),
+	SND_SOC_DAPM_OUTPUT("EP"),
+};
+
+static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
+	struct snd_soc_dapm_widget *sink)
+{
+	struct snd_soc_codec *codec = source->codec;
+	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+	unsigned int dai;
+	const char *clk;
+
+	dai = sink->name[3] - '1';
+
+	if (!adau1373->dais[dai].master)
+		return 0;
+
+	if (adau1373->dais[dai].clk_src == ADAU1373_CLK_SRC_PLL1)
+		clk = "SYSCLK1";
+	else
+		clk = "SYSCLK2";
+
+	return strcmp(source->name, clk) == 0;
+}
+
+static int adau1373_check_src(struct snd_soc_dapm_widget *source,
+	struct snd_soc_dapm_widget *sink)
+{
+	struct snd_soc_codec *codec = source->codec;
+	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+	unsigned int dai;
+
+	dai = sink->name[3] - '1';
+
+	return adau1373->dais[dai].enable_src;
+}
+
+#define DSP_CHANNEL_MIXER_ROUTES(_sink) \
+	{ _sink, "DMIC2 Swapped Switch", "DMIC2" }, \
+	{ _sink, "DMIC2 Switch", "DMIC2" }, \
+	{ _sink, "ADC/DMIC1 Swapped Switch", "Decimator Mux" }, \
+	{ _sink, "ADC/DMIC1 Switch", "Decimator Mux" }, \
+	{ _sink, "AIF1 Switch", "AIF1 IN" }, \
+	{ _sink, "AIF2 Switch", "AIF2 IN" }, \
+	{ _sink, "AIF3 Switch", "AIF3 IN" }
+
+#define DSP_OUTPUT_MIXER_ROUTES(_sink) \
+	{ _sink, "DSP Channel1 Switch", "DSP Channel1 Mixer" }, \
+	{ _sink, "DSP Channel2 Switch", "DSP Channel2 Mixer" }, \
+	{ _sink, "DSP Channel3 Switch", "DSP Channel3 Mixer" }, \
+	{ _sink, "DSP Channel4 Switch", "DSP Channel4 Mixer" }, \
+	{ _sink, "DSP Channel5 Switch", "DSP Channel5 Mixer" }
+
+#define LEFT_OUTPUT_MIXER_ROUTES(_sink) \
+	{ _sink, "Right DAC2 Switch", "Right DAC2" }, \
+	{ _sink, "Left DAC2 Switch", "Left DAC2" }, \
+	{ _sink, "Right DAC1 Switch", "Right DAC1" }, \
+	{ _sink, "Left DAC1 Switch", "Left DAC1" }, \
+	{ _sink, "Input 1 Bypass Switch", "IN1PGA" }, \
+	{ _sink, "Input 2 Bypass Switch", "IN2PGA" }, \
+	{ _sink, "Input 3 Bypass Switch", "IN3PGA" }, \
+	{ _sink, "Input 4 Bypass Switch", "IN4PGA" }
+
+#define RIGHT_OUTPUT_MIXER_ROUTES(_sink) \
+	{ _sink, "Right DAC2 Switch", "Right DAC2" }, \
+	{ _sink, "Left DAC2 Switch", "Left DAC2" }, \
+	{ _sink, "Right DAC1 Switch", "Right DAC1" }, \
+	{ _sink, "Left DAC1 Switch", "Left DAC1" }, \
+	{ _sink, "Input 1 Bypass Switch", "IN1PGA" }, \
+	{ _sink, "Input 2 Bypass Switch", "IN2PGA" }, \
+	{ _sink, "Input 3 Bypass Switch", "IN3PGA" }, \
+	{ _sink, "Input 4 Bypass Switch", "IN4PGA" }
+
+static const struct snd_soc_dapm_route adau1373_dapm_routes[] = {
+	{ "Left ADC Mixer", "DAC1 Switch", "Left DAC1" },
+	{ "Left ADC Mixer", "Input 1 Switch", "IN1PGA" },
+	{ "Left ADC Mixer", "Input 2 Switch", "IN2PGA" },
+	{ "Left ADC Mixer", "Input 3 Switch", "IN3PGA" },
+	{ "Left ADC Mixer", "Input 4 Switch", "IN4PGA" },
+
+	{ "Right ADC Mixer", "DAC1 Switch", "Right DAC1" },
+	{ "Right ADC Mixer", "Input 1 Switch", "IN1PGA" },
+	{ "Right ADC Mixer", "Input 2 Switch", "IN2PGA" },
+	{ "Right ADC Mixer", "Input 3 Switch", "IN3PGA" },
+	{ "Right ADC Mixer", "Input 4 Switch", "IN4PGA" },
+
+	{ "Left ADC", NULL, "Left ADC Mixer" },
+	{ "Right ADC", NULL, "Right ADC Mixer" },
+
+	{ "Decimator Mux", "ADC", "Left ADC" },
+	{ "Decimator Mux", "ADC", "Right ADC" },
+	{ "Decimator Mux", "DMIC1", "DMIC1" },
+
+	DSP_CHANNEL_MIXER_ROUTES("DSP Channel1 Mixer"),
+	DSP_CHANNEL_MIXER_ROUTES("DSP Channel2 Mixer"),
+	DSP_CHANNEL_MIXER_ROUTES("DSP Channel3 Mixer"),
+	DSP_CHANNEL_MIXER_ROUTES("DSP Channel4 Mixer"),
+	DSP_CHANNEL_MIXER_ROUTES("DSP Channel5 Mixer"),
+
+	DSP_OUTPUT_MIXER_ROUTES("AIF1 Mixer"),
+	DSP_OUTPUT_MIXER_ROUTES("AIF2 Mixer"),
+	DSP_OUTPUT_MIXER_ROUTES("AIF3 Mixer"),
+	DSP_OUTPUT_MIXER_ROUTES("DAC1 Mixer"),
+	DSP_OUTPUT_MIXER_ROUTES("DAC2 Mixer"),
+
+	{ "AIF1 OUT", NULL, "AIF1 Mixer" },
+	{ "AIF2 OUT", NULL, "AIF2 Mixer" },
+	{ "AIF3 OUT", NULL, "AIF3 Mixer" },
+	{ "Left DAC1", NULL, "DAC1 Mixer" },
+	{ "Right DAC1", NULL, "DAC1 Mixer" },
+	{ "Left DAC2", NULL, "DAC2 Mixer" },
+	{ "Right DAC2", NULL, "DAC2 Mixer" },
+
+	LEFT_OUTPUT_MIXER_ROUTES("Left Lineout1 Mixer"),
+	RIGHT_OUTPUT_MIXER_ROUTES("Right Lineout1 Mixer"),
+	LEFT_OUTPUT_MIXER_ROUTES("Left Lineout2 Mixer"),
+	RIGHT_OUTPUT_MIXER_ROUTES("Right Lineout2 Mixer"),
+	LEFT_OUTPUT_MIXER_ROUTES("Left Speaker Mixer"),
+	RIGHT_OUTPUT_MIXER_ROUTES("Right Speaker Mixer"),
+
+	{ "Left Headphone Mixer", "Left DAC2 Switch", "Left DAC2" },
+	{ "Left Headphone Mixer", "Left DAC1 Switch", "Left DAC1" },
+	{ "Left Headphone Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+	{ "Left Headphone Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+	{ "Left Headphone Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+	{ "Left Headphone Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+	{ "Right Headphone Mixer", "Right DAC2 Switch", "Right DAC2" },
+	{ "Right Headphone Mixer", "Right DAC1 Switch", "Right DAC1" },
+	{ "Right Headphone Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+	{ "Right Headphone Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+	{ "Right Headphone Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+	{ "Right Headphone Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+
+	{ "Left Headphone Mixer", NULL, "Headphone Enable" },
+	{ "Right Headphone Mixer", NULL, "Headphone Enable" },
+
+	{ "Earpiece Mixer", "Right DAC2 Switch", "Right DAC2" },
+	{ "Earpiece Mixer", "Left DAC2 Switch", "Left DAC2" },
+	{ "Earpiece Mixer", "Right DAC1 Switch", "Right DAC1" },
+	{ "Earpiece Mixer", "Left DAC1 Switch", "Left DAC1" },
+	{ "Earpiece Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+	{ "Earpiece Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+	{ "Earpiece Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+	{ "Earpiece Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+
+	{ "LOUT1L", NULL, "Left Lineout1 Mixer" },
+	{ "LOUT1R", NULL, "Right Lineout1 Mixer" },
+	{ "LOUT2L", NULL, "Left Lineout2 Mixer" },
+	{ "LOUT2R", NULL, "Right Lineout2 Mixer" },
+	{ "SPKL", NULL, "Left Speaker Mixer" },
+	{ "SPKR", NULL, "Right Speaker Mixer" },
+	{ "HPL", NULL, "Left Headphone Mixer" },
+	{ "HPR", NULL, "Right Headphone Mixer" },
+	{ "EP", NULL, "Earpiece Mixer" },
+
+	{ "IN1PGA", NULL, "AIN1L" },
+	{ "IN2PGA", NULL, "AIN2L" },
+	{ "IN3PGA", NULL, "AIN3L" },
+	{ "IN4PGA", NULL, "AIN4L" },
+	{ "IN1PGA", NULL, "AIN1R" },
+	{ "IN2PGA", NULL, "AIN2R" },
+	{ "IN3PGA", NULL, "AIN3R" },
+	{ "IN4PGA", NULL, "AIN4R" },
+
+	{ "SYSCLK1", NULL, "PLL1" },
+	{ "SYSCLK2", NULL, "PLL2" },
+
+	{ "Left DAC1", NULL, "SYSCLK1" },
+	{ "Right DAC1", NULL, "SYSCLK1" },
+	{ "Left DAC2", NULL, "SYSCLK1" },
+	{ "Right DAC2", NULL, "SYSCLK1" },
+	{ "Left ADC", NULL, "SYSCLK1" },
+	{ "Right ADC", NULL, "SYSCLK1" },
+
+	{ "DSP", NULL, "SYSCLK1" },
+
+	{ "AIF1 Mixer", NULL, "DSP" },
+	{ "AIF2 Mixer", NULL, "DSP" },
+	{ "AIF3 Mixer", NULL, "DSP" },
+	{ "DAC1 Mixer", NULL, "DSP" },
+	{ "DAC2 Mixer", NULL, "DSP" },
+	{ "DAC1 Mixer", NULL, "Playback Engine A" },
+	{ "DAC2 Mixer", NULL, "Playback Engine B" },
+	{ "Left ADC Mixer", NULL, "Recording Engine A" },
+	{ "Right ADC Mixer", NULL, "Recording Engine A" },
+
+	{ "AIF1 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+	{ "AIF2 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+	{ "AIF3 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+	{ "AIF1 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+	{ "AIF2 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+	{ "AIF3 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+
+	{ "AIF1 IN", NULL, "AIF1 CLK" },
+	{ "AIF1 OUT", NULL, "AIF1 CLK" },
+	{ "AIF2 IN", NULL, "AIF2 CLK" },
+	{ "AIF2 OUT", NULL, "AIF2 CLK" },
+	{ "AIF3 IN", NULL, "AIF3 CLK" },
+	{ "AIF3 OUT", NULL, "AIF3 CLK" },
+	{ "AIF1 IN", NULL, "AIF1 IN SRC", adau1373_check_src },
+	{ "AIF1 OUT", NULL, "AIF1 OUT SRC", adau1373_check_src },
+	{ "AIF2 IN", NULL, "AIF2 IN SRC", adau1373_check_src },
+	{ "AIF2 OUT", NULL, "AIF2 OUT SRC", adau1373_check_src },
+	{ "AIF3 IN", NULL, "AIF3 IN SRC", adau1373_check_src },
+	{ "AIF3 OUT", NULL, "AIF3 OUT SRC", adau1373_check_src },
+
+	{ "DMIC1", NULL, "DMIC1DAT" },
+	{ "DMIC1", NULL, "SYSCLK1" },
+	{ "DMIC1", NULL, "Recording Engine A" },
+	{ "DMIC2", NULL, "DMIC2DAT" },
+	{ "DMIC2", NULL, "SYSCLK1" },
+	{ "DMIC2", NULL, "Recording Engine B" },
+};
+
+static int adau1373_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+	struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+	unsigned int div;
+	unsigned int freq;
+	unsigned int ctrl;
+
+	freq = adau1373_dai->sysclk;
+
+	if (freq % params_rate(params) != 0)
+		return -EINVAL;
+
+	switch (freq / params_rate(params)) {
+	case 1024: /* fs */
+		div = 0;
+		break;
+	case 1536: /* 2/3 fs */
+		div = 1;
+		break;
+	case 2048: /* 1/2 fs */
+		div = 2;
+		break;
+	case 3072: /* 1/3 fs */
+		div = 3;
+		break;
+	case 4096: /* 1/4 fs */
+		div = 4;
+		break;
+	case 6144: /* 1/6 fs */
+		div = 5;
+		break;
+	case 5632: /* 2/11 fs */
+		div = 6;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	adau1373_dai->enable_src = (div != 0);
+
+	snd_soc_update_bits(codec, ADAU1373_BCLKDIV(dai->id),
+		~ADAU1373_BCLKDIV_SOURCE, (div << 2) | ADAU1373_BCLKDIV_64);
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		ctrl = ADAU1373_DAI_WLEN_16;
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		ctrl = ADAU1373_DAI_WLEN_20;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ctrl = ADAU1373_DAI_WLEN_24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		ctrl = ADAU1373_DAI_WLEN_32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return snd_soc_update_bits(codec, ADAU1373_DAI(dai->id),
+			ADAU1373_DAI_WLEN_MASK, ctrl);
+}
+
+static int adau1373_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+	struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+	unsigned int ctrl;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		ctrl = ADAU1373_DAI_MASTER;
+		adau1373_dai->master = true;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		ctrl = 0;
+		adau1373_dai->master = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		ctrl |= ADAU1373_DAI_FORMAT_I2S;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		ctrl |= ADAU1373_DAI_FORMAT_LEFT_J;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		ctrl |= ADAU1373_DAI_FORMAT_RIGHT_J;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		ctrl |= ADAU1373_DAI_FORMAT_DSP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		ctrl |= ADAU1373_DAI_INVERT_BCLK;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		ctrl |= ADAU1373_DAI_INVERT_LRCLK;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		ctrl |= ADAU1373_DAI_INVERT_LRCLK | ADAU1373_DAI_INVERT_BCLK;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, ADAU1373_DAI(dai->id),
+		~ADAU1373_DAI_WLEN_MASK, ctrl);
+
+	return 0;
+}
+
+static int adau1373_set_dai_sysclk(struct snd_soc_dai *dai,
+	int clk_id, unsigned int freq, int dir)
+{
+	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(dai->codec);
+	struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+
+	switch (clk_id) {
+	case ADAU1373_CLK_SRC_PLL1:
+	case ADAU1373_CLK_SRC_PLL2:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	adau1373_dai->sysclk = freq;
+	adau1373_dai->clk_src = clk_id;
+
+	snd_soc_update_bits(dai->codec, ADAU1373_BCLKDIV(dai->id),
+		ADAU1373_BCLKDIV_SOURCE, clk_id << 5);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops adau1373_dai_ops = {
+	.hw_params	= adau1373_hw_params,
+	.set_sysclk	= adau1373_set_dai_sysclk,
+	.set_fmt	= adau1373_set_dai_fmt,
+};
+
+#define ADAU1373_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_driver adau1373_dai_driver[] = {
+	{
+		.id = 0,
+		.name = "adau1373-aif1",
+		.playback = {
+			.stream_name = "AIF1 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.capture = {
+			.stream_name = "AIF1 Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.ops = &adau1373_dai_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.id = 1,
+		.name = "adau1373-aif2",
+		.playback = {
+			.stream_name = "AIF2 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.capture = {
+			.stream_name = "AIF2 Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.ops = &adau1373_dai_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.id = 2,
+		.name = "adau1373-aif3",
+		.playback = {
+			.stream_name = "AIF3 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.capture = {
+			.stream_name = "AIF3 Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.ops = &adau1373_dai_ops,
+		.symmetric_rates = 1,
+	},
+};
+
+static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
+	int source, unsigned int freq_in, unsigned int freq_out)
+{
+	unsigned int dpll_div = 0;
+	unsigned int x, r, n, m, i, j, mode;
+
+	switch (pll_id) {
+	case ADAU1373_PLL1:
+	case ADAU1373_PLL2:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (source) {
+	case ADAU1373_PLL_SRC_BCLK1:
+	case ADAU1373_PLL_SRC_BCLK2:
+	case ADAU1373_PLL_SRC_BCLK3:
+	case ADAU1373_PLL_SRC_LRCLK1:
+	case ADAU1373_PLL_SRC_LRCLK2:
+	case ADAU1373_PLL_SRC_LRCLK3:
+	case ADAU1373_PLL_SRC_MCLK1:
+	case ADAU1373_PLL_SRC_MCLK2:
+	case ADAU1373_PLL_SRC_GPIO1:
+	case ADAU1373_PLL_SRC_GPIO2:
+	case ADAU1373_PLL_SRC_GPIO3:
+	case ADAU1373_PLL_SRC_GPIO4:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (freq_in < 7813 || freq_in > 27000000)
+		return -EINVAL;
+
+	if (freq_out < 45158000 || freq_out > 49152000)
+		return -EINVAL;
+
+	/* APLL input needs to be >= 8Mhz, so in case freq_in is less we use the
+	 * DPLL to get it there. DPLL_out = (DPLL_in / div) * 1024 */
+	while (freq_in < 8000000) {
+		freq_in *= 2;
+		dpll_div++;
+	}
+
+	if (freq_out % freq_in != 0) {
+		/* fout = fin * (r + (n/m)) / x */
+		x = DIV_ROUND_UP(freq_in, 13500000);
+		freq_in /= x;
+		r = freq_out / freq_in;
+		i = freq_out % freq_in;
+		j = gcd(i, freq_in);
+		n = i / j;
+		m = freq_in / j;
+		x--;
+		mode = 1;
+	} else {
+		/* fout = fin / r */
+		r = freq_out / freq_in;
+		n = 0;
+		m = 0;
+		x = 0;
+		mode = 0;
+	}
+
+	if (r < 2 || r > 8 || x > 3 || m > 0xffff || n > 0xffff)
+		return -EINVAL;
+
+	if (dpll_div) {
+		dpll_div = 11 - dpll_div;
+		snd_soc_update_bits(codec, ADAU1373_PLL_CTRL6(pll_id),
+			ADAU1373_PLL_CTRL6_DPLL_BYPASS, 0);
+	} else {
+		snd_soc_update_bits(codec, ADAU1373_PLL_CTRL6(pll_id),
+			ADAU1373_PLL_CTRL6_DPLL_BYPASS,
+			ADAU1373_PLL_CTRL6_DPLL_BYPASS);
+	}
+
+	snd_soc_write(codec, ADAU1373_DPLL_CTRL(pll_id),
+		(source << 4) | dpll_div);
+	snd_soc_write(codec, ADAU1373_PLL_CTRL1(pll_id), (m >> 8) & 0xff);
+	snd_soc_write(codec, ADAU1373_PLL_CTRL2(pll_id), m & 0xff);
+	snd_soc_write(codec, ADAU1373_PLL_CTRL3(pll_id), (n >> 8) & 0xff);
+	snd_soc_write(codec, ADAU1373_PLL_CTRL4(pll_id), n & 0xff);
+	snd_soc_write(codec, ADAU1373_PLL_CTRL5(pll_id),
+		(r << 3) | (x << 1) | mode);
+
+	/* Set SYSCLK to 256 * fs */
+	snd_soc_update_bits(codec, ADAU1373_CLK_SRC_DIV(pll_id), 0x3f, 0x09);
+
+	return 0;
+}
+
+static void adau1373_load_drc_settings(struct snd_soc_codec *codec,
+	unsigned int nr, uint8_t *drc)
+{
+	unsigned int i;
+
+	for (i = 0; i < 13; ++i)
+		snd_soc_write(codec, ADAU1373_DRC(nr) + i, drc[i]);
+}
+
+static int adau1373_probe(struct snd_soc_codec *codec)
+{
+	struct adau1373_platform_data *pdata = codec->dev->platform_data;
+	bool lineout_differential = false;
+	unsigned int val;
+	int ret;
+	int i;
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+	if (ret) {
+		dev_err(codec->dev, "failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	codec->dapm.idle_bias_off = true;
+
+	snd_soc_write(codec, ADAU1373_ADC_CTRL,
+	    ADAU1373_ADC_CTRL_RESET_FORCE | ADAU1373_ADC_CTRL_PEAK_DETECT);
+
+	if (pdata) {
+		for (i = 0; i < pdata->num_drc; ++i) {
+			adau1373_load_drc_settings(codec, i,
+				pdata->drc_setting[i]);
+		}
+
+		snd_soc_add_controls(codec, adau1373_drc_controls,
+			pdata->num_drc);
+
+		val = 0;
+		for (i = 0; i < 4; ++i) {
+			if (pdata->input_differential[i])
+				val |= BIT(i);
+		}
+		snd_soc_write(codec, ADAU1373_INPUT_MODE, val);
+
+		val = 0;
+		if (pdata->lineout_differential)
+			val |= ADAU1373_OUTPUT_CTRL_LDIFF;
+		if (pdata->lineout_ground_sense)
+			val |= ADAU1373_OUTPUT_CTRL_LNFBEN;
+		snd_soc_write(codec, ADAU1373_OUTPUT_CTRL, val);
+
+		lineout_differential = pdata->lineout_differential;
+	}
+
+	if (!lineout_differential) {
+		snd_soc_add_controls(codec, adau1373_lineout2_controls,
+			ARRAY_SIZE(adau1373_lineout2_controls));
+	}
+
+	return 0;
+}
+
+static int adau1373_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_update_bits(codec, ADAU1373_PWDN_CTRL3, 1, 1);
+		break;
+	case SND_SOC_BIAS_OFF:
+		snd_soc_update_bits(codec, ADAU1373_PWDN_CTRL3, 1, 0);
+		break;
+	}
+	codec->dapm.bias_level = level;
+	return 0;
+}
+
+static int adau1373_remove(struct snd_soc_codec *codec)
+{
+	adau1373_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int adau1373_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+	return adau1373_set_bias_level(codec, SND_SOC_BIAS_OFF);
+}
+
+static int adau1373_resume(struct snd_soc_codec *codec)
+{
+	adau1373_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_cache_sync(codec);
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver adau1373_codec_driver = {
+	.probe =	adau1373_probe,
+	.remove =	adau1373_remove,
+	.suspend =	adau1373_suspend,
+	.resume =	adau1373_resume,
+	.set_bias_level = adau1373_set_bias_level,
+	.reg_cache_size = ARRAY_SIZE(adau1373_default_regs),
+	.reg_cache_default = adau1373_default_regs,
+	.reg_word_size = sizeof(uint8_t),
+
+	.set_pll = adau1373_set_pll,
+
+	.controls = adau1373_controls,
+	.num_controls = ARRAY_SIZE(adau1373_controls),
+	.dapm_widgets = adau1373_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(adau1373_dapm_widgets),
+	.dapm_routes = adau1373_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(adau1373_dapm_routes),
+};
+
+static int __devinit adau1373_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	struct adau1373 *adau1373;
+	int ret;
+
+	adau1373 = kzalloc(sizeof(*adau1373), GFP_KERNEL);
+	if (!adau1373)
+		return -ENOMEM;
+
+	dev_set_drvdata(&client->dev, adau1373);
+
+	ret = snd_soc_register_codec(&client->dev, &adau1373_codec_driver,
+			adau1373_dai_driver, ARRAY_SIZE(adau1373_dai_driver));
+	if (ret < 0)
+		kfree(adau1373);
+
+	return ret;
+}
+
+static int __devexit adau1373_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	kfree(dev_get_drvdata(&client->dev));
+	return 0;
+}
+
+static const struct i2c_device_id adau1373_i2c_id[] = {
+	{ "adau1373", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, adau1373_i2c_id);
+
+static struct i2c_driver adau1373_i2c_driver = {
+	.driver = {
+		.name = "adau1373",
+		.owner = THIS_MODULE,
+	},
+	.probe = adau1373_i2c_probe,
+	.remove = __devexit_p(adau1373_i2c_remove),
+	.id_table = adau1373_i2c_id,
+};
+
+static int __init adau1373_init(void)
+{
+	return i2c_add_driver(&adau1373_i2c_driver);
+}
+module_init(adau1373_init);
+
+static void __exit adau1373_exit(void)
+{
+	i2c_del_driver(&adau1373_i2c_driver);
+}
+module_exit(adau1373_exit);
+
+MODULE_DESCRIPTION("ASoC ADAU1373 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1373.h b/sound/soc/codecs/adau1373.h
new file mode 100644
index 0000000..c6ab553
--- /dev/null
+++ b/sound/soc/codecs/adau1373.h
@@ -0,0 +1,29 @@
+#ifndef __ADAU1373_H__
+#define __ADAU1373_H__
+
+enum adau1373_pll_src {
+	ADAU1373_PLL_SRC_MCLK1 = 0,
+	ADAU1373_PLL_SRC_BCLK1 = 1,
+	ADAU1373_PLL_SRC_BCLK2 = 2,
+	ADAU1373_PLL_SRC_BCLK3 = 3,
+	ADAU1373_PLL_SRC_LRCLK1 = 4,
+	ADAU1373_PLL_SRC_LRCLK2 = 5,
+	ADAU1373_PLL_SRC_LRCLK3 = 6,
+	ADAU1373_PLL_SRC_GPIO1 = 7,
+	ADAU1373_PLL_SRC_GPIO2 = 8,
+	ADAU1373_PLL_SRC_GPIO3 = 9,
+	ADAU1373_PLL_SRC_GPIO4 = 10,
+	ADAU1373_PLL_SRC_MCLK2 = 11,
+};
+
+enum adau1373_pll {
+	ADAU1373_PLL1 = 0,
+	ADAU1373_PLL2 = 1,
+};
+
+enum adau1373_clk_src {
+	ADAU1373_CLK_SRC_PLL1 = 0,
+	ADAU1373_CLK_SRC_PLL2 = 1,
+};
+
+#endif
-- 
1.7.2.5

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

* [PATCH 3/4] ASoC: Blackfin: ADAU1373 eval board support
  2011-08-10  3:52 ` Lars-Peter Clausen
@ 2011-08-10  3:52   ` Lars-Peter Clausen
  -1 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-10  3:52 UTC (permalink / raw)
  To: Mark Brown, Liam Girdwood
  Cc: alsa-devel, device-drivers-devel, linux-kernel, Mike Frysinger,
	Lars-Peter Clausen

Add a machine driver to support the EVAL-ADAU1373 board connected to a
Analog Devices BF5XX evaluation board.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 sound/soc/blackfin/Kconfig              |   14 ++
 sound/soc/blackfin/Makefile             |    2 +
 sound/soc/blackfin/bfin-eval-adau1373.c |  202 +++++++++++++++++++++++++++++++
 3 files changed, 218 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/blackfin/bfin-eval-adau1373.c

diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
index fe9d548..9f537b4 100644
--- a/sound/soc/blackfin/Kconfig
+++ b/sound/soc/blackfin/Kconfig
@@ -27,6 +27,20 @@ config SND_SOC_BFIN_EVAL_ADAU1701
 	  board connected to one of the Blackfin evaluation boards like the
 	  BF5XX-STAMP or BF5XX-EZKIT.
 
+config SND_SOC_BFIN_EVAL_ADAU1373
+	tristate "Support for the EVAL-ADAU1373 board on Blackfin eval boards"
+	depends on SND_BF5XX_I2S
+	select SND_BF5XX_SOC_I2S
+	select SND_SOC_ADAU1373
+	select I2C
+	help
+	  Say Y if you want to add support for the Analog Devices EVAL-ADAU1373
+	  board connected to one of the Blackfin evaluation boards like the
+	  BF5XX-STAMP or BF5XX-EZKIT.
+
+	  Note: This driver assumes that first ADAU1373 DAI is connected to the
+	  first SPORT port on the BF5XX board.
+
 config SND_SOC_BFIN_EVAL_ADAV80X
 	tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards"
 	depends on SND_BF5XX_I2S && (SPI_MASTER || I2C)
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile
index 6018bf5..1bf86cc 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-soc-bfin-eval-adau1373-objs := bfin-eval-adau1373.o
 snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o
 snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.o
 
@@ -29,5 +30,6 @@ 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_SOC_BFIN_EVAL_ADAU1373) += snd-soc-bfin-eval-adau1373.o
 obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) += snd-soc-bfin-eval-adau1701.o
 obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAV80X) += snd-soc-bfin-eval-adav80x.o
diff --git a/sound/soc/blackfin/bfin-eval-adau1373.c b/sound/soc/blackfin/bfin-eval-adau1373.c
new file mode 100644
index 0000000..901b1cc
--- /dev/null
+++ b/sound/soc/blackfin/bfin-eval-adau1373.c
@@ -0,0 +1,202 @@
+/*
+ * Machine driver for EVAL-ADAU1373 on Analog Devices bfin
+ * evaluation boards.
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "../codecs/adau1373.h"
+
+static const struct snd_soc_dapm_widget bfin_eval_adau1373_dapm_widgets[] = {
+	SND_SOC_DAPM_LINE("Line In1", NULL),
+	SND_SOC_DAPM_LINE("Line In2", NULL),
+	SND_SOC_DAPM_LINE("Line In3", NULL),
+	SND_SOC_DAPM_LINE("Line In4", NULL),
+
+	SND_SOC_DAPM_LINE("Line Out1", NULL),
+	SND_SOC_DAPM_LINE("Line Out2", NULL),
+	SND_SOC_DAPM_LINE("Stereo Out", NULL),
+	SND_SOC_DAPM_HP("Headphone", NULL),
+	SND_SOC_DAPM_HP("Earpiece", NULL),
+	SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_soc_dapm_route bfin_eval_adau1373_dapm_routes[] = {
+	{ "AIN1L", NULL, "Line In1" },
+	{ "AIN1R", NULL, "Line In1" },
+	{ "AIN2L", NULL, "Line In2" },
+	{ "AIN2R", NULL, "Line In2" },
+	{ "AIN3L", NULL, "Line In3" },
+	{ "AIN3R", NULL, "Line In3" },
+	{ "AIN4L", NULL, "Line In4" },
+	{ "AIN4R", NULL, "Line In4" },
+
+	/* MICBIAS can be connected via a jumper to the line-in jack, since w
+	   don't know which one is going to be used, just power both. */
+	{ "Line In1", NULL, "MICBIAS1" },
+	{ "Line In2", NULL, "MICBIAS1" },
+	{ "Line In3", NULL, "MICBIAS1" },
+	{ "Line In4", NULL, "MICBIAS1" },
+	{ "Line In1", NULL, "MICBIAS2" },
+	{ "Line In2", NULL, "MICBIAS2" },
+	{ "Line In3", NULL, "MICBIAS2" },
+	{ "Line In4", NULL, "MICBIAS2" },
+
+	{ "Line Out1", NULL, "LOUT1L" },
+	{ "Line Out1", NULL, "LOUT1R" },
+	{ "Line Out2", NULL, "LOUT2L" },
+	{ "Line Out2", NULL, "LOUT2R" },
+	{ "Headphone", NULL, "HPL" },
+	{ "Headphone", NULL, "HPR" },
+	{ "Earpiece", NULL, "EP" },
+	{ "Speaker", NULL, "SPKL" },
+	{ "Stereo Out", NULL, "SPKR" },
+};
+
+static int bfin_eval_adau1373_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 *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	int ret;
+	int pll_rate;
+
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+			SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+	if (ret)
+		return ret;
+
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+			SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+	if (ret)
+		return ret;
+
+	switch (params_rate(params)) {
+	case 48000:
+	case 8000:
+	case 12000:
+	case 16000:
+	case 24000:
+	case 32000:
+		pll_rate = 48000 * 1024;
+		break;
+	case 44100:
+	case 7350:
+	case 11025:
+	case 14700:
+	case 22050:
+	case 29400:
+		pll_rate = 44100 * 1024;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = snd_soc_dai_set_pll(codec_dai, ADAU1373_PLL1,
+			ADAU1373_PLL_SRC_MCLK1, 12288000, pll_rate);
+	if (ret)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1373_CLK_SRC_PLL1, pll_rate,
+			SND_SOC_CLOCK_IN);
+
+	return ret;
+}
+
+static int bfin_eval_adau1373_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	unsigned int pll_rate = 48000 * 1024;
+	int ret;
+
+	ret = snd_soc_dai_set_pll(codec_dai, ADAU1373_PLL1,
+			ADAU1373_PLL_SRC_MCLK1, 12288000, pll_rate);
+	if (ret)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1373_CLK_SRC_PLL1, pll_rate,
+			SND_SOC_CLOCK_IN);
+
+	return ret;
+}
+static struct snd_soc_ops bfin_eval_adau1373_ops = {
+	.hw_params = bfin_eval_adau1373_hw_params,
+};
+
+static struct snd_soc_dai_link bfin_eval_adau1373_dai = {
+	.name = "adau1373",
+	.stream_name = "adau1373",
+	.cpu_dai_name = "bfin-i2s.0",
+	.codec_dai_name = "adau1373-aif1",
+	.platform_name = "bfin-i2s-pcm-audio",
+	.codec_name = "adau1373.0-001a",
+	.ops = &bfin_eval_adau1373_ops,
+	.init = bfin_eval_adau1373_codec_init,
+};
+
+static struct snd_soc_card bfin_eval_adau1373 = {
+	.name = "bfin-eval-adau1373",
+	.dai_link = &bfin_eval_adau1373_dai,
+	.num_links = 1,
+
+	.dapm_widgets		= bfin_eval_adau1373_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(bfin_eval_adau1373_dapm_widgets),
+	.dapm_routes		= bfin_eval_adau1373_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(bfin_eval_adau1373_dapm_routes),
+};
+
+static int bfin_eval_adau1373_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &bfin_eval_adau1373;
+
+	card->dev = &pdev->dev;
+
+	return snd_soc_register_card(&bfin_eval_adau1373);
+}
+
+static int __devexit bfin_eval_adau1373_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_card(card);
+
+	return 0;
+}
+
+static struct platform_driver bfin_eval_adau1373_driver = {
+	.driver = {
+		.name = "bfin-eval-adau1373",
+		.owner = THIS_MODULE,
+		.pm = &snd_soc_pm_ops,
+	},
+	.probe = bfin_eval_adau1373_probe,
+	.remove = __devexit_p(bfin_eval_adau1373_remove),
+};
+
+static int __init bfin_eval_adau1373_init(void)
+{
+	return platform_driver_register(&bfin_eval_adau1373_driver);
+}
+module_init(bfin_eval_adau1373_init);
+
+static void __exit bfin_eval_adau1373_exit(void)
+{
+	platform_driver_unregister(&bfin_eval_adau1373_driver);
+}
+module_exit(bfin_eval_adau1373_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ALSA SoC bfin adau1373 driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bfin-eval-adau1373");
-- 
1.7.2.5


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

* [PATCH 3/4] ASoC: Blackfin: ADAU1373 eval board support
@ 2011-08-10  3:52   ` Lars-Peter Clausen
  0 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-10  3:52 UTC (permalink / raw)
  To: Mark Brown, Liam Girdwood
  Cc: Mike Frysinger, alsa-devel, Lars-Peter Clausen, linux-kernel,
	device-drivers-devel

Add a machine driver to support the EVAL-ADAU1373 board connected to a
Analog Devices BF5XX evaluation board.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 sound/soc/blackfin/Kconfig              |   14 ++
 sound/soc/blackfin/Makefile             |    2 +
 sound/soc/blackfin/bfin-eval-adau1373.c |  202 +++++++++++++++++++++++++++++++
 3 files changed, 218 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/blackfin/bfin-eval-adau1373.c

diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
index fe9d548..9f537b4 100644
--- a/sound/soc/blackfin/Kconfig
+++ b/sound/soc/blackfin/Kconfig
@@ -27,6 +27,20 @@ config SND_SOC_BFIN_EVAL_ADAU1701
 	  board connected to one of the Blackfin evaluation boards like the
 	  BF5XX-STAMP or BF5XX-EZKIT.
 
+config SND_SOC_BFIN_EVAL_ADAU1373
+	tristate "Support for the EVAL-ADAU1373 board on Blackfin eval boards"
+	depends on SND_BF5XX_I2S
+	select SND_BF5XX_SOC_I2S
+	select SND_SOC_ADAU1373
+	select I2C
+	help
+	  Say Y if you want to add support for the Analog Devices EVAL-ADAU1373
+	  board connected to one of the Blackfin evaluation boards like the
+	  BF5XX-STAMP or BF5XX-EZKIT.
+
+	  Note: This driver assumes that first ADAU1373 DAI is connected to the
+	  first SPORT port on the BF5XX board.
+
 config SND_SOC_BFIN_EVAL_ADAV80X
 	tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards"
 	depends on SND_BF5XX_I2S && (SPI_MASTER || I2C)
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile
index 6018bf5..1bf86cc 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-soc-bfin-eval-adau1373-objs := bfin-eval-adau1373.o
 snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o
 snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.o
 
@@ -29,5 +30,6 @@ 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_SOC_BFIN_EVAL_ADAU1373) += snd-soc-bfin-eval-adau1373.o
 obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) += snd-soc-bfin-eval-adau1701.o
 obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAV80X) += snd-soc-bfin-eval-adav80x.o
diff --git a/sound/soc/blackfin/bfin-eval-adau1373.c b/sound/soc/blackfin/bfin-eval-adau1373.c
new file mode 100644
index 0000000..901b1cc
--- /dev/null
+++ b/sound/soc/blackfin/bfin-eval-adau1373.c
@@ -0,0 +1,202 @@
+/*
+ * Machine driver for EVAL-ADAU1373 on Analog Devices bfin
+ * evaluation boards.
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "../codecs/adau1373.h"
+
+static const struct snd_soc_dapm_widget bfin_eval_adau1373_dapm_widgets[] = {
+	SND_SOC_DAPM_LINE("Line In1", NULL),
+	SND_SOC_DAPM_LINE("Line In2", NULL),
+	SND_SOC_DAPM_LINE("Line In3", NULL),
+	SND_SOC_DAPM_LINE("Line In4", NULL),
+
+	SND_SOC_DAPM_LINE("Line Out1", NULL),
+	SND_SOC_DAPM_LINE("Line Out2", NULL),
+	SND_SOC_DAPM_LINE("Stereo Out", NULL),
+	SND_SOC_DAPM_HP("Headphone", NULL),
+	SND_SOC_DAPM_HP("Earpiece", NULL),
+	SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_soc_dapm_route bfin_eval_adau1373_dapm_routes[] = {
+	{ "AIN1L", NULL, "Line In1" },
+	{ "AIN1R", NULL, "Line In1" },
+	{ "AIN2L", NULL, "Line In2" },
+	{ "AIN2R", NULL, "Line In2" },
+	{ "AIN3L", NULL, "Line In3" },
+	{ "AIN3R", NULL, "Line In3" },
+	{ "AIN4L", NULL, "Line In4" },
+	{ "AIN4R", NULL, "Line In4" },
+
+	/* MICBIAS can be connected via a jumper to the line-in jack, since w
+	   don't know which one is going to be used, just power both. */
+	{ "Line In1", NULL, "MICBIAS1" },
+	{ "Line In2", NULL, "MICBIAS1" },
+	{ "Line In3", NULL, "MICBIAS1" },
+	{ "Line In4", NULL, "MICBIAS1" },
+	{ "Line In1", NULL, "MICBIAS2" },
+	{ "Line In2", NULL, "MICBIAS2" },
+	{ "Line In3", NULL, "MICBIAS2" },
+	{ "Line In4", NULL, "MICBIAS2" },
+
+	{ "Line Out1", NULL, "LOUT1L" },
+	{ "Line Out1", NULL, "LOUT1R" },
+	{ "Line Out2", NULL, "LOUT2L" },
+	{ "Line Out2", NULL, "LOUT2R" },
+	{ "Headphone", NULL, "HPL" },
+	{ "Headphone", NULL, "HPR" },
+	{ "Earpiece", NULL, "EP" },
+	{ "Speaker", NULL, "SPKL" },
+	{ "Stereo Out", NULL, "SPKR" },
+};
+
+static int bfin_eval_adau1373_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 *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	int ret;
+	int pll_rate;
+
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+			SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+	if (ret)
+		return ret;
+
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+			SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+	if (ret)
+		return ret;
+
+	switch (params_rate(params)) {
+	case 48000:
+	case 8000:
+	case 12000:
+	case 16000:
+	case 24000:
+	case 32000:
+		pll_rate = 48000 * 1024;
+		break;
+	case 44100:
+	case 7350:
+	case 11025:
+	case 14700:
+	case 22050:
+	case 29400:
+		pll_rate = 44100 * 1024;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = snd_soc_dai_set_pll(codec_dai, ADAU1373_PLL1,
+			ADAU1373_PLL_SRC_MCLK1, 12288000, pll_rate);
+	if (ret)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1373_CLK_SRC_PLL1, pll_rate,
+			SND_SOC_CLOCK_IN);
+
+	return ret;
+}
+
+static int bfin_eval_adau1373_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	unsigned int pll_rate = 48000 * 1024;
+	int ret;
+
+	ret = snd_soc_dai_set_pll(codec_dai, ADAU1373_PLL1,
+			ADAU1373_PLL_SRC_MCLK1, 12288000, pll_rate);
+	if (ret)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1373_CLK_SRC_PLL1, pll_rate,
+			SND_SOC_CLOCK_IN);
+
+	return ret;
+}
+static struct snd_soc_ops bfin_eval_adau1373_ops = {
+	.hw_params = bfin_eval_adau1373_hw_params,
+};
+
+static struct snd_soc_dai_link bfin_eval_adau1373_dai = {
+	.name = "adau1373",
+	.stream_name = "adau1373",
+	.cpu_dai_name = "bfin-i2s.0",
+	.codec_dai_name = "adau1373-aif1",
+	.platform_name = "bfin-i2s-pcm-audio",
+	.codec_name = "adau1373.0-001a",
+	.ops = &bfin_eval_adau1373_ops,
+	.init = bfin_eval_adau1373_codec_init,
+};
+
+static struct snd_soc_card bfin_eval_adau1373 = {
+	.name = "bfin-eval-adau1373",
+	.dai_link = &bfin_eval_adau1373_dai,
+	.num_links = 1,
+
+	.dapm_widgets		= bfin_eval_adau1373_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(bfin_eval_adau1373_dapm_widgets),
+	.dapm_routes		= bfin_eval_adau1373_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(bfin_eval_adau1373_dapm_routes),
+};
+
+static int bfin_eval_adau1373_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &bfin_eval_adau1373;
+
+	card->dev = &pdev->dev;
+
+	return snd_soc_register_card(&bfin_eval_adau1373);
+}
+
+static int __devexit bfin_eval_adau1373_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_card(card);
+
+	return 0;
+}
+
+static struct platform_driver bfin_eval_adau1373_driver = {
+	.driver = {
+		.name = "bfin-eval-adau1373",
+		.owner = THIS_MODULE,
+		.pm = &snd_soc_pm_ops,
+	},
+	.probe = bfin_eval_adau1373_probe,
+	.remove = __devexit_p(bfin_eval_adau1373_remove),
+};
+
+static int __init bfin_eval_adau1373_init(void)
+{
+	return platform_driver_register(&bfin_eval_adau1373_driver);
+}
+module_init(bfin_eval_adau1373_init);
+
+static void __exit bfin_eval_adau1373_exit(void)
+{
+	platform_driver_unregister(&bfin_eval_adau1373_driver);
+}
+module_exit(bfin_eval_adau1373_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ALSA SoC bfin adau1373 driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bfin-eval-adau1373");
-- 
1.7.2.5

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

* [PATCH 4/4] Blackfin: bf537: Stamp: Register ASoC EVAL-ADAU1373 board driver
  2011-08-10  3:52 ` Lars-Peter Clausen
@ 2011-08-10  3:52   ` Lars-Peter Clausen
  -1 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-10  3:52 UTC (permalink / raw)
  To: Mark Brown, Liam Girdwood
  Cc: alsa-devel, device-drivers-devel, linux-kernel, Mike Frysinger,
	Lars-Peter Clausen, Mike Frysinger

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Cc: Mike Frysinger <vapier@gentoo.org>
---
 arch/blackfin/mach-bf537/boards/stamp.c |   12 ++++++++++++
 1 files changed, 12 insertions(+), 0 deletions(-)

diff --git a/arch/blackfin/mach-bf537/boards/stamp.c b/arch/blackfin/mach-bf537/boards/stamp.c
index 4037043..6662c37 100644
--- a/arch/blackfin/mach-bf537/boards/stamp.c
+++ b/arch/blackfin/mach-bf537/boards/stamp.c
@@ -2674,6 +2674,13 @@ static struct platform_device iio_gpio_trigger = {
 };
 #endif
 
+#if defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373) || \
+	defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373_MODULE)
+static struct platform_device bf5xx_adau1373_device = {
+	.name = "bfin-eval-adau1373",
+};
+#endif
+
 #if defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) || \
 	defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701_MODULE)
 static struct platform_device bf5xx_adau1701_device = {
@@ -2842,6 +2849,11 @@ static struct platform_device *stamp_devices[] __initdata = {
 	&iio_gpio_trigger,
 #endif
 
+#if defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373) || \
+	defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373_MODULE)
+	&bf5xx_adau1373_device,
+#endif
+
 #if defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) || \
 	defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701_MODULE)
 	&bf5xx_adau1701_device,
-- 
1.7.2.5


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

* [PATCH 4/4] Blackfin: bf537: Stamp: Register ASoC EVAL-ADAU1373 board driver
@ 2011-08-10  3:52   ` Lars-Peter Clausen
  0 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-10  3:52 UTC (permalink / raw)
  To: Mark Brown, Liam Girdwood
  Cc: alsa-devel, Lars-Peter Clausen, Mike Frysinger, Mike Frysinger,
	linux-kernel, device-drivers-devel

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Cc: Mike Frysinger <vapier@gentoo.org>
---
 arch/blackfin/mach-bf537/boards/stamp.c |   12 ++++++++++++
 1 files changed, 12 insertions(+), 0 deletions(-)

diff --git a/arch/blackfin/mach-bf537/boards/stamp.c b/arch/blackfin/mach-bf537/boards/stamp.c
index 4037043..6662c37 100644
--- a/arch/blackfin/mach-bf537/boards/stamp.c
+++ b/arch/blackfin/mach-bf537/boards/stamp.c
@@ -2674,6 +2674,13 @@ static struct platform_device iio_gpio_trigger = {
 };
 #endif
 
+#if defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373) || \
+	defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373_MODULE)
+static struct platform_device bf5xx_adau1373_device = {
+	.name = "bfin-eval-adau1373",
+};
+#endif
+
 #if defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) || \
 	defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701_MODULE)
 static struct platform_device bf5xx_adau1701_device = {
@@ -2842,6 +2849,11 @@ static struct platform_device *stamp_devices[] __initdata = {
 	&iio_gpio_trigger,
 #endif
 
+#if defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373) || \
+	defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373_MODULE)
+	&bf5xx_adau1373_device,
+#endif
+
 #if defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) || \
 	defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701_MODULE)
 	&bf5xx_adau1701_device,
-- 
1.7.2.5

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

* Re: [alsa-devel] [PATCH 3/4] ASoC: Blackfin: ADAU1373 eval board support
  2011-08-10  3:52   ` Lars-Peter Clausen
@ 2011-08-10  5:27     ` Scott Jiang
  -1 siblings, 0 replies; 36+ messages in thread
From: Scott Jiang @ 2011-08-10  5:27 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Mark Brown, Liam Girdwood, Mike Frysinger, alsa-devel,
	linux-kernel, device-drivers-devel, uclinux-dist-devel

> +config SND_SOC_BFIN_EVAL_ADAU1373
> +       tristate "Support for the EVAL-ADAU1373 board on Blackfin eval boards"
> +       depends on SND_BF5XX_I2S
> +       select SND_BF5XX_SOC_I2S
> +       select SND_SOC_ADAU1373
> +       select I2C

Is it better to use depends on I2C?

> +       help
> +         Say Y if you want to add support for the Analog Devices EVAL-ADAU1373
> +         board connected to one of the Blackfin evaluation boards like the
> +         BF5XX-STAMP or BF5XX-EZKIT.

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

* Re: [PATCH 3/4] ASoC: Blackfin: ADAU1373 eval board support
@ 2011-08-10  5:27     ` Scott Jiang
  0 siblings, 0 replies; 36+ messages in thread
From: Scott Jiang @ 2011-08-10  5:27 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: alsa-devel, Mike Frysinger, Mark Brown, linux-kernel,
	device-drivers-devel, uclinux-dist-devel, Liam Girdwood

> +config SND_SOC_BFIN_EVAL_ADAU1373
> +       tristate "Support for the EVAL-ADAU1373 board on Blackfin eval boards"
> +       depends on SND_BF5XX_I2S
> +       select SND_BF5XX_SOC_I2S
> +       select SND_SOC_ADAU1373
> +       select I2C

Is it better to use depends on I2C?

> +       help
> +         Say Y if you want to add support for the Analog Devices EVAL-ADAU1373
> +         board connected to one of the Blackfin evaluation boards like the
> +         BF5XX-STAMP or BF5XX-EZKIT.

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

* Re: [alsa-devel] [PATCH 3/4] ASoC: Blackfin: ADAU1373 eval board support
  2011-08-10  5:27     ` Scott Jiang
@ 2011-08-10  6:09       ` Lars-Peter Clausen
  -1 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-10  6:09 UTC (permalink / raw)
  To: Scott Jiang
  Cc: Mark Brown, Liam Girdwood, Mike Frysinger, alsa-devel,
	linux-kernel, device-drivers-devel, uclinux-dist-devel

On 08/10/2011 07:27 AM, Scott Jiang wrote:
>> +config SND_SOC_BFIN_EVAL_ADAU1373
>> +       tristate "Support for the EVAL-ADAU1373 board on Blackfin eval boards"
>> +       depends on SND_BF5XX_I2S
>> +       select SND_BF5XX_SOC_I2S
>> +       select SND_SOC_ADAU1373
>> +       select I2C
> 
> Is it better to use depends on I2C?
> 

Not sure, I just kept the scheme used by the other blackfin board drivers. But
I guess it makes some sense change it to 'depends'. Especially if we consider
that there are going to be board drivers with support for both I2C and SPI.

- Lars

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

* Re: [PATCH 3/4] ASoC: Blackfin: ADAU1373 eval board support
@ 2011-08-10  6:09       ` Lars-Peter Clausen
  0 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-10  6:09 UTC (permalink / raw)
  To: Scott Jiang
  Cc: alsa-devel, Mike Frysinger, Mark Brown, linux-kernel,
	device-drivers-devel, uclinux-dist-devel, Liam Girdwood

On 08/10/2011 07:27 AM, Scott Jiang wrote:
>> +config SND_SOC_BFIN_EVAL_ADAU1373
>> +       tristate "Support for the EVAL-ADAU1373 board on Blackfin eval boards"
>> +       depends on SND_BF5XX_I2S
>> +       select SND_BF5XX_SOC_I2S
>> +       select SND_SOC_ADAU1373
>> +       select I2C
> 
> Is it better to use depends on I2C?
> 

Not sure, I just kept the scheme used by the other blackfin board drivers. But
I guess it makes some sense change it to 'depends'. Especially if we consider
that there are going to be board drivers with support for both I2C and SPI.

- Lars

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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
  2011-08-10  3:52   ` Lars-Peter Clausen
@ 2011-08-10  6:11     ` Lars-Peter Clausen
  -1 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-10  6:11 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Mark Brown, Liam Girdwood, alsa-devel, device-drivers-devel,
	linux-kernel, Mike Frysinger

On 08/10/2011 05:52 AM, Lars-Peter Clausen wrote:
> This patch adds support for the Analog Devices ADAU1373 audio codec.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> ---
>  include/sound/adau1373.h    |   22 +
>  sound/soc/codecs/Kconfig    |    3 +
>  sound/soc/codecs/Makefile   |    2 +
>  sound/soc/codecs/adau1373.c | 1374 +++++++++++++++++++++++++++++++++++++++++++
>  sound/soc/codecs/adau1373.h |   29 +
>  5 files changed, 1430 insertions(+), 0 deletions(-)
>  create mode 100644 include/sound/adau1373.h
>  create mode 100644 sound/soc/codecs/adau1373.c
>  create mode 100644 sound/soc/codecs/adau1373.h
> 
> [...]
> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
> index 665d924..bac0edb 100644
> --- a/sound/soc/codecs/Kconfig
> +++ b/sound/soc/codecs/Kconfig
> @@ -139,6 +139,9 @@ config SND_SOC_ADAU1701
>  	select SIGMA
>  	tristate
>  
> +config SND_SOC_ADAU1373
> +	tristate
> +

Forgot to add it to SND_SOC_ALL_CODECS. Will be in v2.

>  config SND_SOC_ADAV80X
>  	tristate
>  
> [...]

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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
@ 2011-08-10  6:11     ` Lars-Peter Clausen
  0 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-10  6:11 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: alsa-devel, Mike Frysinger, Mark Brown, linux-kernel,
	device-drivers-devel, Liam Girdwood

On 08/10/2011 05:52 AM, Lars-Peter Clausen wrote:
> This patch adds support for the Analog Devices ADAU1373 audio codec.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> ---
>  include/sound/adau1373.h    |   22 +
>  sound/soc/codecs/Kconfig    |    3 +
>  sound/soc/codecs/Makefile   |    2 +
>  sound/soc/codecs/adau1373.c | 1374 +++++++++++++++++++++++++++++++++++++++++++
>  sound/soc/codecs/adau1373.h |   29 +
>  5 files changed, 1430 insertions(+), 0 deletions(-)
>  create mode 100644 include/sound/adau1373.h
>  create mode 100644 sound/soc/codecs/adau1373.c
>  create mode 100644 sound/soc/codecs/adau1373.h
> 
> [...]
> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
> index 665d924..bac0edb 100644
> --- a/sound/soc/codecs/Kconfig
> +++ b/sound/soc/codecs/Kconfig
> @@ -139,6 +139,9 @@ config SND_SOC_ADAU1701
>  	select SIGMA
>  	tristate
>  
> +config SND_SOC_ADAU1373
> +	tristate
> +

Forgot to add it to SND_SOC_ALL_CODECS. Will be in v2.

>  config SND_SOC_ADAV80X
>  	tristate
>  
> [...]

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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
  2011-08-10  3:52   ` Lars-Peter Clausen
@ 2011-08-11  9:27     ` Mark Brown
  -1 siblings, 0 replies; 36+ messages in thread
From: Mark Brown @ 2011-08-11  9:27 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Liam Girdwood, alsa-devel, device-drivers-devel, linux-kernel,
	Mike Frysinger

On Wed, Aug 10, 2011 at 05:52:42AM +0200, Lars-Peter Clausen wrote:

> +static const char *adau1373_micbias_text[] = {
> +	"2.9 V",
> +	"2.2 V",
> +	"2.6 V",
> +	"1.8 V",
> +};

This should be controlled by platform data and/or the machine driver,
not interactively by the user.  With some microphone technologies there
is actually a risk of damage with higher micbiases.

> +	SOC_DOUBLE("AIF3 Playback Boost(+6dB)", ADAU1373_VOL_GAIN1, 4, 5, 1, 0),
> +	SOC_DOUBLE("AIF2 Playback Boost(+6dB)", ADAU1373_VOL_GAIN1, 2, 3, 1, 0),
> +	SOC_DOUBLE("AIF1 Playback Boost(+6dB)", ADAU1373_VOL_GAIN1, 0, 1, 1, 0),
> +	SOC_DOUBLE("AIF3 Capture Boost(+6dB)", ADAU1373_VOL_GAIN2, 4, 5, 1, 0),
> +	SOC_DOUBLE("AIF2 Capture Boost(+6dB)", ADAU1373_VOL_GAIN2, 2, 3, 1, 0),
> +	SOC_DOUBLE("AIF1 Capture Boost(+6dB)", ADAU1373_VOL_GAIN2, 0, 1, 1, 0),
> +	SOC_DOUBLE("DMIC Capture Boost(+6dB)", ADAU1373_VOL_GAIN3, 6, 7, 1, 0),
> +	SOC_DOUBLE("ADC Capture Boost(+6dB)", ADAU1373_VOL_GAIN3, 4, 5, 1, 0),
> +	SOC_DOUBLE("DAC2 Playback Boost(+6dB)", ADAU1373_VOL_GAIN3, 2, 3, 1, 0),
> +	SOC_DOUBLE("DAC1 Playback Boost(+6dB)", ADAU1373_VOL_GAIN3, 0, 1, 1, 0),
> +
> +	SOC_DOUBLE("Input1 Boost(+20dB)", ADAU1373_ADC_GAIN, 0, 4, 1, 0),
> +	SOC_DOUBLE("Input2 Boost(+20dB)", ADAU1373_ADC_GAIN, 1, 5, 1, 0),
> +	SOC_DOUBLE("Input3 Boost(+20dB)", ADAU1373_ADC_GAIN, 2, 6, 1, 0),
> +	SOC_DOUBLE("Input4 Boost(+20dB)", ADAU1373_ADC_GAIN, 3, 7, 1, 0),

All this stuff should be TLV.

> +	SOC_ENUM("Lineout1 Mono Stereo", adau1373_lineout1_mode_enum),
> +	SOC_ENUM("Speaker Mono Stereo", adau1373_speaker_mode_enum),

I'd expect these to be platform data/machine data rather than user
control?  The speaker wiring isn't going to vary dynamically...

> +	switch (freq / params_rate(params)) {
> +	case 1024: /* fs */
> +		div = 0;
> +		break;
> +	case 1536: /* 2/3 fs */
> +		div = 1;
> +		break;

These comments look inaccuate, fs is the sample rate so a divide of 1
would be fs.

> +static void adau1373_load_drc_settings(struct snd_soc_codec *codec,
> +	unsigned int nr, uint8_t *drc)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < 13; ++i)
> +		snd_soc_write(codec, ADAU1373_DRC(nr) + i, drc[i]);

ARRAY_SIZE() or a #define or something rather than a magic number.

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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
@ 2011-08-11  9:27     ` Mark Brown
  0 siblings, 0 replies; 36+ messages in thread
From: Mark Brown @ 2011-08-11  9:27 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Mike Frysinger, alsa-devel, Liam Girdwood, device-drivers-devel,
	linux-kernel

On Wed, Aug 10, 2011 at 05:52:42AM +0200, Lars-Peter Clausen wrote:

> +static const char *adau1373_micbias_text[] = {
> +	"2.9 V",
> +	"2.2 V",
> +	"2.6 V",
> +	"1.8 V",
> +};

This should be controlled by platform data and/or the machine driver,
not interactively by the user.  With some microphone technologies there
is actually a risk of damage with higher micbiases.

> +	SOC_DOUBLE("AIF3 Playback Boost(+6dB)", ADAU1373_VOL_GAIN1, 4, 5, 1, 0),
> +	SOC_DOUBLE("AIF2 Playback Boost(+6dB)", ADAU1373_VOL_GAIN1, 2, 3, 1, 0),
> +	SOC_DOUBLE("AIF1 Playback Boost(+6dB)", ADAU1373_VOL_GAIN1, 0, 1, 1, 0),
> +	SOC_DOUBLE("AIF3 Capture Boost(+6dB)", ADAU1373_VOL_GAIN2, 4, 5, 1, 0),
> +	SOC_DOUBLE("AIF2 Capture Boost(+6dB)", ADAU1373_VOL_GAIN2, 2, 3, 1, 0),
> +	SOC_DOUBLE("AIF1 Capture Boost(+6dB)", ADAU1373_VOL_GAIN2, 0, 1, 1, 0),
> +	SOC_DOUBLE("DMIC Capture Boost(+6dB)", ADAU1373_VOL_GAIN3, 6, 7, 1, 0),
> +	SOC_DOUBLE("ADC Capture Boost(+6dB)", ADAU1373_VOL_GAIN3, 4, 5, 1, 0),
> +	SOC_DOUBLE("DAC2 Playback Boost(+6dB)", ADAU1373_VOL_GAIN3, 2, 3, 1, 0),
> +	SOC_DOUBLE("DAC1 Playback Boost(+6dB)", ADAU1373_VOL_GAIN3, 0, 1, 1, 0),
> +
> +	SOC_DOUBLE("Input1 Boost(+20dB)", ADAU1373_ADC_GAIN, 0, 4, 1, 0),
> +	SOC_DOUBLE("Input2 Boost(+20dB)", ADAU1373_ADC_GAIN, 1, 5, 1, 0),
> +	SOC_DOUBLE("Input3 Boost(+20dB)", ADAU1373_ADC_GAIN, 2, 6, 1, 0),
> +	SOC_DOUBLE("Input4 Boost(+20dB)", ADAU1373_ADC_GAIN, 3, 7, 1, 0),

All this stuff should be TLV.

> +	SOC_ENUM("Lineout1 Mono Stereo", adau1373_lineout1_mode_enum),
> +	SOC_ENUM("Speaker Mono Stereo", adau1373_speaker_mode_enum),

I'd expect these to be platform data/machine data rather than user
control?  The speaker wiring isn't going to vary dynamically...

> +	switch (freq / params_rate(params)) {
> +	case 1024: /* fs */
> +		div = 0;
> +		break;
> +	case 1536: /* 2/3 fs */
> +		div = 1;
> +		break;

These comments look inaccuate, fs is the sample rate so a divide of 1
would be fs.

> +static void adau1373_load_drc_settings(struct snd_soc_codec *codec,
> +	unsigned int nr, uint8_t *drc)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < 13; ++i)
> +		snd_soc_write(codec, ADAU1373_DRC(nr) + i, drc[i]);

ARRAY_SIZE() or a #define or something rather than a magic number.

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

* Re: [PATCH 1/4] ASoC: DAPM: Allow multiple mixer sources to be routed via the same switch
  2011-08-10  3:52 ` Lars-Peter Clausen
@ 2011-08-11  9:31   ` Mark Brown
  -1 siblings, 0 replies; 36+ messages in thread
From: Mark Brown @ 2011-08-11  9:31 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Liam Girdwood, alsa-devel, device-drivers-devel, linux-kernel,
	Mike Frysinger

On Wed, Aug 10, 2011 at 05:52:41AM +0200, Lars-Peter Clausen wrote:
> Currently it is only possible to route one source per switch into a mixer.
> This patch modifies the code, so that it is possible to route multiple sources
> into a mixer via the same switch. One use-case for this is routing a stereo
> channel pair into a mono-mixer via the same switch.

I'd have thought the dapm_is_shared_control() stuff should handle this?

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

* Re: [PATCH 1/4] ASoC: DAPM: Allow multiple mixer sources to be routed via the same switch
@ 2011-08-11  9:31   ` Mark Brown
  0 siblings, 0 replies; 36+ messages in thread
From: Mark Brown @ 2011-08-11  9:31 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Mike Frysinger, alsa-devel, Liam Girdwood, device-drivers-devel,
	linux-kernel

On Wed, Aug 10, 2011 at 05:52:41AM +0200, Lars-Peter Clausen wrote:
> Currently it is only possible to route one source per switch into a mixer.
> This patch modifies the code, so that it is possible to route multiple sources
> into a mixer via the same switch. One use-case for this is routing a stereo
> channel pair into a mono-mixer via the same switch.

I'd have thought the dapm_is_shared_control() stuff should handle this?

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

* Re: [alsa-devel] [PATCH 1/4] ASoC: DAPM: Allow multiple mixer sources to be routed via the same switch
  2011-08-11  9:31   ` Mark Brown
@ 2011-08-11  9:59     ` Lars-Peter Clausen
  -1 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-11  9:59 UTC (permalink / raw)
  To: Mark Brown
  Cc: Mike Frysinger, alsa-devel, Liam Girdwood, device-drivers-devel,
	linux-kernel

On 08/11/2011 11:31 AM, Mark Brown wrote:
> On Wed, Aug 10, 2011 at 05:52:41AM +0200, Lars-Peter Clausen wrote:
>> Currently it is only possible to route one source per switch into a mixer.
>> This patch modifies the code, so that it is possible to route multiple sources
>> into a mixer via the same switch. One use-case for this is routing a stereo
>> channel pair into a mono-mixer via the same switch.
>
> I'd have thought the dapm_is_shared_control() stuff should handle this?


The dapm_is_shared_control() stuff is about sharing kcontrols between multiple
muxes. This is about sharing the same kcontrol between multiple paths of the
same mixer.

i.e

{ "Mono Mixer", "DAC", "Left DAC" },
{ "Mono Mixer", "DAC", "Right DAC" },

doesn't work without this patch.

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

* Re: [PATCH 1/4] ASoC: DAPM: Allow multiple mixer sources to be routed via the same switch
@ 2011-08-11  9:59     ` Lars-Peter Clausen
  0 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-11  9:59 UTC (permalink / raw)
  To: Mark Brown
  Cc: device-drivers-devel, alsa-devel, Liam Girdwood, Mike Frysinger,
	linux-kernel

On 08/11/2011 11:31 AM, Mark Brown wrote:
> On Wed, Aug 10, 2011 at 05:52:41AM +0200, Lars-Peter Clausen wrote:
>> Currently it is only possible to route one source per switch into a mixer.
>> This patch modifies the code, so that it is possible to route multiple sources
>> into a mixer via the same switch. One use-case for this is routing a stereo
>> channel pair into a mono-mixer via the same switch.
>
> I'd have thought the dapm_is_shared_control() stuff should handle this?


The dapm_is_shared_control() stuff is about sharing kcontrols between multiple
muxes. This is about sharing the same kcontrol between multiple paths of the
same mixer.

i.e

{ "Mono Mixer", "DAC", "Left DAC" },
{ "Mono Mixer", "DAC", "Right DAC" },

doesn't work without this patch.

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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
  2011-08-11  9:27     ` Mark Brown
@ 2011-08-11 10:11       ` Lars-Peter Clausen
  -1 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-11 10:11 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, alsa-devel, device-drivers-devel, linux-kernel,
	Mike Frysinger


>> +	SOC_ENUM("Lineout1 Mono Stereo", adau1373_lineout1_mode_enum),
>> +	SOC_ENUM("Speaker Mono Stereo", adau1373_speaker_mode_enum),
> 
> I'd expect these to be platform data/machine data rather than user
> control?  The speaker wiring isn't going to vary dynamically...
>
You still might want to switch, for whatever particular reason, between mono
and stereo at runtime.

>> +	switch (freq / params_rate(params)) {
>> +	case 1024: /* fs */
>> +		div = 0;
>> +		break;
>> +	case 1536: /* 2/3 fs */
>> +		div = 1;
>> +		break;
> 
> These comments look inaccuate, fs is the sample rate so a divide of 1
> would be fs.

div contains the register value representation of that particular divider.

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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
@ 2011-08-11 10:11       ` Lars-Peter Clausen
  0 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-11 10:11 UTC (permalink / raw)
  To: Mark Brown
  Cc: Mike Frysinger, alsa-devel, Liam Girdwood, device-drivers-devel,
	linux-kernel


>> +	SOC_ENUM("Lineout1 Mono Stereo", adau1373_lineout1_mode_enum),
>> +	SOC_ENUM("Speaker Mono Stereo", adau1373_speaker_mode_enum),
> 
> I'd expect these to be platform data/machine data rather than user
> control?  The speaker wiring isn't going to vary dynamically...
>
You still might want to switch, for whatever particular reason, between mono
and stereo at runtime.

>> +	switch (freq / params_rate(params)) {
>> +	case 1024: /* fs */
>> +		div = 0;
>> +		break;
>> +	case 1536: /* 2/3 fs */
>> +		div = 1;
>> +		break;
> 
> These comments look inaccuate, fs is the sample rate so a divide of 1
> would be fs.

div contains the register value representation of that particular divider.

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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
  2011-08-11 10:11       ` Lars-Peter Clausen
@ 2011-08-11 13:16         ` Mark Brown
  -1 siblings, 0 replies; 36+ messages in thread
From: Mark Brown @ 2011-08-11 13:16 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Liam Girdwood, alsa-devel, device-drivers-devel, linux-kernel,
	Mike Frysinger

On Thu, 2011-08-11 at 12:11 +0200, Lars-Peter Clausen wrote:
> >> +	SOC_ENUM("Lineout1 Mono Stereo", adau1373_lineout1_mode_enum),
> >> +	SOC_ENUM("Speaker Mono Stereo", adau1373_speaker_mode_enum),

> > I'd expect these to be platform data/machine data rather than user
> > control?  The speaker wiring isn't going to vary dynamically...

> You still might want to switch, for whatever particular reason, between mono
> and stereo at runtime.

Sorry, what does this actually control? I guess I've been mislead but
from the name of the control I'd expect it to control if the outputs
were physically connected as mono or stereo outputs.

> >> +	switch (freq / params_rate(params)) {
> >> +	case 1024: /* fs */
> >> +		div = 0;
> >> +		break;
> >> +	case 1536: /* 2/3 fs */
> >> +		div = 1;
> >> +		break;

> > These comments look inaccuate, fs is the sample rate so a divide of 1
> > would be fs.

> div contains the register value representation of that particular divider.

div isn't the issue here. The comments seem to indicate that the result
of the divisions are some multiple of fs but fs usually means some
multiple of the sample rate. For example 2/3fs for 44.1kHz would be
29.4kHz.


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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
@ 2011-08-11 13:16         ` Mark Brown
  0 siblings, 0 replies; 36+ messages in thread
From: Mark Brown @ 2011-08-11 13:16 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Mike Frysinger, alsa-devel, Liam Girdwood, device-drivers-devel,
	linux-kernel

On Thu, 2011-08-11 at 12:11 +0200, Lars-Peter Clausen wrote:
> >> +	SOC_ENUM("Lineout1 Mono Stereo", adau1373_lineout1_mode_enum),
> >> +	SOC_ENUM("Speaker Mono Stereo", adau1373_speaker_mode_enum),

> > I'd expect these to be platform data/machine data rather than user
> > control?  The speaker wiring isn't going to vary dynamically...

> You still might want to switch, for whatever particular reason, between mono
> and stereo at runtime.

Sorry, what does this actually control? I guess I've been mislead but
from the name of the control I'd expect it to control if the outputs
were physically connected as mono or stereo outputs.

> >> +	switch (freq / params_rate(params)) {
> >> +	case 1024: /* fs */
> >> +		div = 0;
> >> +		break;
> >> +	case 1536: /* 2/3 fs */
> >> +		div = 1;
> >> +		break;

> > These comments look inaccuate, fs is the sample rate so a divide of 1
> > would be fs.

> div contains the register value representation of that particular divider.

div isn't the issue here. The comments seem to indicate that the result
of the divisions are some multiple of fs but fs usually means some
multiple of the sample rate. For example 2/3fs for 44.1kHz would be
29.4kHz.

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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
  2011-08-11 13:16         ` Mark Brown
@ 2011-08-12  0:20           ` Lars-Peter Clausen
  -1 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-12  0:20 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, alsa-devel, device-drivers-devel, linux-kernel,
	Mike Frysinger

On 08/11/2011 03:16 PM, Mark Brown wrote:
> On Thu, 2011-08-11 at 12:11 +0200, Lars-Peter Clausen wrote:
>>>> +	SOC_ENUM("Lineout1 Mono Stereo", adau1373_lineout1_mode_enum),
>>>> +	SOC_ENUM("Speaker Mono Stereo", adau1373_speaker_mode_enum),
> 
>>> I'd expect these to be platform data/machine data rather than user
>>> control?  The speaker wiring isn't going to vary dynamically...
> 
>> You still might want to switch, for whatever particular reason, between mono
>> and stereo at runtime.
> 
> Sorry, what does this actually control? I guess I've been mislead but
> from the name of the control I'd expect it to control if the outputs
> were physically connected as mono or stereo outputs.

It controls how Lineout and Speaker mixers distribute the left and right input
channels to their output channels.

> 
>>>> +	switch (freq / params_rate(params)) {
>>>> +	case 1024: /* fs */
>>>> +		div = 0;
>>>> +		break;
>>>> +	case 1536: /* 2/3 fs */
>>>> +		div = 1;
>>>> +		break;
> 
>>> These comments look inaccuate, fs is the sample rate so a divide of 1
>>> would be fs.
> 
>> div contains the register value representation of that particular divider.
> 
> div isn't the issue here. The comments seem to indicate that the result
> of the divisions are some multiple of fs but fs usually means some
> multiple of the sample rate. For example 2/3fs for 44.1kHz would be
> 29.4kHz.
> 

Ah, ok. Yes, I guess the comments are a bit confusing since fs refers to the
internal sample rate and not to the sample rate of the DAI. I'll try to clarify it.

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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
@ 2011-08-12  0:20           ` Lars-Peter Clausen
  0 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-12  0:20 UTC (permalink / raw)
  To: Mark Brown
  Cc: Mike Frysinger, alsa-devel, Liam Girdwood, device-drivers-devel,
	linux-kernel

On 08/11/2011 03:16 PM, Mark Brown wrote:
> On Thu, 2011-08-11 at 12:11 +0200, Lars-Peter Clausen wrote:
>>>> +	SOC_ENUM("Lineout1 Mono Stereo", adau1373_lineout1_mode_enum),
>>>> +	SOC_ENUM("Speaker Mono Stereo", adau1373_speaker_mode_enum),
> 
>>> I'd expect these to be platform data/machine data rather than user
>>> control?  The speaker wiring isn't going to vary dynamically...
> 
>> You still might want to switch, for whatever particular reason, between mono
>> and stereo at runtime.
> 
> Sorry, what does this actually control? I guess I've been mislead but
> from the name of the control I'd expect it to control if the outputs
> were physically connected as mono or stereo outputs.

It controls how Lineout and Speaker mixers distribute the left and right input
channels to their output channels.

> 
>>>> +	switch (freq / params_rate(params)) {
>>>> +	case 1024: /* fs */
>>>> +		div = 0;
>>>> +		break;
>>>> +	case 1536: /* 2/3 fs */
>>>> +		div = 1;
>>>> +		break;
> 
>>> These comments look inaccuate, fs is the sample rate so a divide of 1
>>> would be fs.
> 
>> div contains the register value representation of that particular divider.
> 
> div isn't the issue here. The comments seem to indicate that the result
> of the divisions are some multiple of fs but fs usually means some
> multiple of the sample rate. For example 2/3fs for 44.1kHz would be
> 29.4kHz.
> 

Ah, ok. Yes, I guess the comments are a bit confusing since fs refers to the
internal sample rate and not to the sample rate of the DAI. I'll try to clarify it.

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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
  2011-08-12  0:20           ` Lars-Peter Clausen
@ 2011-08-12  1:20             ` Mark Brown
  -1 siblings, 0 replies; 36+ messages in thread
From: Mark Brown @ 2011-08-12  1:20 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Liam Girdwood, alsa-devel, device-drivers-devel, linux-kernel,
	Mike Frysinger

On Fri, 2011-08-12 at 02:20 +0200, Lars-Peter Clausen wrote:
> On 08/11/2011 03:16 PM, Mark Brown wrote:

> > Sorry, what does this actually control? I guess I've been mislead but
> > from the name of the control I'd expect it to control if the outputs
> > were physically connected as mono or stereo outputs.

> It controls how Lineout and Speaker mixers distribute the left and right input
> channels to their output channels.

Ah, I see. I guess that should really be represented in DAPM so that if
we're playing a mono signal out we figure out that both left and right
channels are connected even if there's only input for one of them,
especially for the line outputs which may be connected to external
speaker drivers or something that needs power management.


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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
@ 2011-08-12  1:20             ` Mark Brown
  0 siblings, 0 replies; 36+ messages in thread
From: Mark Brown @ 2011-08-12  1:20 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Mike Frysinger, alsa-devel, Liam Girdwood, device-drivers-devel,
	linux-kernel

On Fri, 2011-08-12 at 02:20 +0200, Lars-Peter Clausen wrote:
> On 08/11/2011 03:16 PM, Mark Brown wrote:

> > Sorry, what does this actually control? I guess I've been mislead but
> > from the name of the control I'd expect it to control if the outputs
> > were physically connected as mono or stereo outputs.

> It controls how Lineout and Speaker mixers distribute the left and right input
> channels to their output channels.

Ah, I see. I guess that should really be represented in DAPM so that if
we're playing a mono signal out we figure out that both left and right
channels are connected even if there's only input for one of them,
especially for the line outputs which may be connected to external
speaker drivers or something that needs power management.

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

* Re: [alsa-devel] [PATCH 1/4] ASoC: DAPM: Allow multiple mixer sources to be routed via the same switch
  2011-08-11  9:59     ` Lars-Peter Clausen
  (?)
@ 2011-08-12  1:22     ` Mark Brown
  -1 siblings, 0 replies; 36+ messages in thread
From: Mark Brown @ 2011-08-12  1:22 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Mike Frysinger, alsa-devel, Liam Girdwood, device-drivers-devel,
	linux-kernel

On Thu, 2011-08-11 at 11:59 +0200, Lars-Peter Clausen wrote:


> The dapm_is_shared_control() stuff is about sharing kcontrols between multiple
> muxes. This is about sharing the same kcontrol between multiple paths of the
> same mixer.

> i.e

> { "Mono Mixer", "DAC", "Left DAC" },
> { "Mono Mixer", "DAC", "Right DAC" },

> doesn't work without this patch.

OK, makes sense.


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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
  2011-08-12  1:20             ` Mark Brown
@ 2011-08-13  3:25               ` Lars-Peter Clausen
  -1 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-13  3:25 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, alsa-devel, device-drivers-devel, linux-kernel,
	Mike Frysinger

On 08/12/2011 03:20 AM, Mark Brown wrote:
> On Fri, 2011-08-12 at 02:20 +0200, Lars-Peter Clausen wrote:
>> On 08/11/2011 03:16 PM, Mark Brown wrote:
> 
>>> Sorry, what does this actually control? I guess I've been mislead but
>>> from the name of the control I'd expect it to control if the outputs
>>> were physically connected as mono or stereo outputs.
> 
>> It controls how Lineout and Speaker mixers distribute the left and right input
>> channels to their output channels.
> 
> Ah, I see. I guess that should really be represented in DAPM so that if
> we're playing a mono signal out we figure out that both left and right
> channels are connected even if there's only input for one of them,
> especially for the line outputs which may be connected to external
> speaker drivers or something that needs power management.
> 

Looks as if both my understanding and my explanation on how this controls works
were a bit inaccurate.

So here is how it works: There are 4 possible settings
Mute: Both the left and the right mixer are muted
Left Channel (L+R): The right mixer is muted. The left mixer is unmuted and for
the input bypass signals both the left and the right channel are mixed into the
output.
Right Channel (L+R): Similar to Left Channel (L+R), only for the right mixer.
Stereo: Both the left and the right mixer are unmuted. For the left mixer the
left channels of the input bypass signals are mixed into the output and for the
right mixer the right channels.

Since this driver's DAPM already doesn't distinguish between the left and the
right channel of the input bypass signals none of the outputs or mixers are not
powered when they should be.

So I would like to keep these as enums, as they are right now.

- Lars

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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
@ 2011-08-13  3:25               ` Lars-Peter Clausen
  0 siblings, 0 replies; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-13  3:25 UTC (permalink / raw)
  To: Mark Brown
  Cc: Mike Frysinger, alsa-devel, Liam Girdwood, device-drivers-devel,
	linux-kernel

On 08/12/2011 03:20 AM, Mark Brown wrote:
> On Fri, 2011-08-12 at 02:20 +0200, Lars-Peter Clausen wrote:
>> On 08/11/2011 03:16 PM, Mark Brown wrote:
> 
>>> Sorry, what does this actually control? I guess I've been mislead but
>>> from the name of the control I'd expect it to control if the outputs
>>> were physically connected as mono or stereo outputs.
> 
>> It controls how Lineout and Speaker mixers distribute the left and right input
>> channels to their output channels.
> 
> Ah, I see. I guess that should really be represented in DAPM so that if
> we're playing a mono signal out we figure out that both left and right
> channels are connected even if there's only input for one of them,
> especially for the line outputs which may be connected to external
> speaker drivers or something that needs power management.
> 

Looks as if both my understanding and my explanation on how this controls works
were a bit inaccurate.

So here is how it works: There are 4 possible settings
Mute: Both the left and the right mixer are muted
Left Channel (L+R): The right mixer is muted. The left mixer is unmuted and for
the input bypass signals both the left and the right channel are mixed into the
output.
Right Channel (L+R): Similar to Left Channel (L+R), only for the right mixer.
Stereo: Both the left and the right mixer are unmuted. For the left mixer the
left channels of the input bypass signals are mixed into the output and for the
right mixer the right channels.

Since this driver's DAPM already doesn't distinguish between the left and the
right channel of the input bypass signals none of the outputs or mixers are not
powered when they should be.

So I would like to keep these as enums, as they are right now.

- Lars

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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
  2011-08-13  3:25               ` Lars-Peter Clausen
@ 2011-08-14 10:24                 ` Mark Brown
  -1 siblings, 0 replies; 36+ messages in thread
From: Mark Brown @ 2011-08-14 10:24 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Liam Girdwood, alsa-devel, device-drivers-devel, linux-kernel,
	Mike Frysinger

On Sat, Aug 13, 2011 at 05:25:41AM +0200, Lars-Peter Clausen wrote:

> Since this driver's DAPM already doesn't distinguish between the left and the
> right channel of the input bypass signals none of the outputs or mixers are not
> powered when they should be.
> 
> So I would like to keep these as enums, as they are right now.

OK.

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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
@ 2011-08-14 10:24                 ` Mark Brown
  0 siblings, 0 replies; 36+ messages in thread
From: Mark Brown @ 2011-08-14 10:24 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Mike Frysinger, alsa-devel, Liam Girdwood, device-drivers-devel,
	linux-kernel

On Sat, Aug 13, 2011 at 05:25:41AM +0200, Lars-Peter Clausen wrote:

> Since this driver's DAPM already doesn't distinguish between the left and the
> right channel of the input bypass signals none of the outputs or mixers are not
> powered when they should be.
> 
> So I would like to keep these as enums, as they are right now.

OK.

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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
  2011-08-15 18:15 ` [PATCH 2/4] ASoC: Add ADAU1373 codec support Lars-Peter Clausen
@ 2011-08-16 15:54     ` Mark Brown
  0 siblings, 0 replies; 36+ messages in thread
From: Mark Brown @ 2011-08-16 15:54 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Liam Girdwood, alsa-devel, device-drivers-devel, linux-kernel,
	Mike Frysinger

On Mon, Aug 15, 2011 at 08:15:22PM +0200, Lars-Peter Clausen wrote:
> This patch adds support for the Analog Devices ADAU1373 audio codec.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>

Applied, thanks.

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

* Re: [PATCH 2/4] ASoC: Add ADAU1373 codec support
@ 2011-08-16 15:54     ` Mark Brown
  0 siblings, 0 replies; 36+ messages in thread
From: Mark Brown @ 2011-08-16 15:54 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Mike Frysinger, alsa-devel, Liam Girdwood, device-drivers-devel,
	linux-kernel

On Mon, Aug 15, 2011 at 08:15:22PM +0200, Lars-Peter Clausen wrote:
> This patch adds support for the Analog Devices ADAU1373 audio codec.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>

Applied, thanks.

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

* [PATCH 2/4] ASoC: Add ADAU1373 codec support
  2011-08-15 18:15 Lars-Peter Clausen
@ 2011-08-15 18:15 ` Lars-Peter Clausen
  2011-08-16 15:54     ` Mark Brown
  0 siblings, 1 reply; 36+ messages in thread
From: Lars-Peter Clausen @ 2011-08-15 18:15 UTC (permalink / raw)
  To: Mark Brown, Liam Girdwood
  Cc: alsa-devel, device-drivers-devel, linux-kernel, Mike Frysinger,
	Lars-Peter Clausen

This patch adds support for the Analog Devices ADAU1373 audio codec.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>

---
Changes since v1:
	* Replace some magic values with defines
	* Use TLV volume controls instead of switches for boost controls
	* Made bias voltage platform data instead of exposing it as a user
	  adjustable control
	* Renamed "Mono Stero" controls to "LR Mux"
---
 include/sound/adau1373.h    |   34 +
 sound/soc/codecs/Kconfig    |    4 +
 sound/soc/codecs/Makefile   |    2 +
 sound/soc/codecs/adau1373.c | 1414 +++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/adau1373.h |   29 +
 5 files changed, 1483 insertions(+), 0 deletions(-)
 create mode 100644 include/sound/adau1373.h
 create mode 100644 sound/soc/codecs/adau1373.c
 create mode 100644 sound/soc/codecs/adau1373.h

diff --git a/include/sound/adau1373.h b/include/sound/adau1373.h
new file mode 100644
index 0000000..1b19c76
--- /dev/null
+++ b/include/sound/adau1373.h
@@ -0,0 +1,34 @@
+/*
+ * Analog Devices ADAU1373 Audio Codec drive
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __SOUND_ADAU1373_H__
+#define __SOUND_ADAU1373_H__
+
+enum adau1373_micbias_voltage {
+	ADAU1373_MICBIAS_2_9V = 0,
+	ADAU1373_MICBIAS_2_2V = 1,
+	ADAU1373_MICBIAS_2_6V = 2,
+	ADAU1373_MICBIAS_1_8V = 3,
+};
+
+#define ADAU1373_DRC_SIZE 13
+
+struct adau1373_platform_data {
+	bool input_differential[4];
+	bool lineout_differential;
+	bool lineout_ground_sense;
+
+	unsigned int num_drc;
+	uint8_t drc_setting[3][ADAU1373_DRC_SIZE];
+
+	enum adau1373_micbias_voltage micbias1;
+	enum adau1373_micbias_voltage micbias2;
+};
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 665d924..71b46c8 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -17,6 +17,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
+	select SND_SOC_ADAU1373 if I2C
 	select SND_SOC_ADAV80X
 	select SND_SOC_ADS117X
 	select SND_SOC_AK4104 if SPI_MASTER
@@ -139,6 +140,9 @@ config SND_SOC_ADAU1701
 	select SIGMA
 	tristate
 
+config SND_SOC_ADAU1373
+	tristate
+
 config SND_SOC_ADAV80X
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 5119a7e..70c1769 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -5,6 +5,7 @@ snd-soc-ad193x-objs := ad193x.o
 snd-soc-ad1980-objs := ad1980.o
 snd-soc-ad73311-objs := ad73311.o
 snd-soc-adau1701-objs := adau1701.o
+snd-soc-adau1373-objs := adau1373.o
 snd-soc-adav80x-objs := adav80x.o
 snd-soc-ads117x-objs := ads117x.o
 snd-soc-ak4104-objs := ak4104.o
@@ -100,6 +101,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_ADAU1373)	+= snd-soc-adau1373.o
 obj-$(CONFIG_SND_SOC_ADAU1701)  += snd-soc-adau1701.o
 obj-$(CONFIG_SND_SOC_ADAV80X)  += snd-soc-adav80x.o
 obj-$(CONFIG_SND_SOC_ADS117X)	+= snd-soc-ads117x.o
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
new file mode 100644
index 0000000..2aa40c3
--- /dev/null
+++ b/sound/soc/codecs/adau1373.c
@@ -0,0 +1,1414 @@
+/*
+ * Analog Devices ADAU1373 Audio Codec drive
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/gcd.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/adau1373.h>
+
+#include "adau1373.h"
+
+struct adau1373_dai {
+	unsigned int clk_src;
+	unsigned int sysclk;
+	bool enable_src;
+	bool master;
+};
+
+struct adau1373 {
+	struct adau1373_dai dais[3];
+};
+
+#define ADAU1373_INPUT_MODE	0x00
+#define ADAU1373_AINL_CTRL(x)	(0x01 + (x) * 2)
+#define ADAU1373_AINR_CTRL(x)	(0x02 + (x) * 2)
+#define ADAU1373_LLINE_OUT(x)	(0x9 + (x) * 2)
+#define ADAU1373_RLINE_OUT(x)	(0xa + (x) * 2)
+#define ADAU1373_LSPK_OUT	0x0d
+#define ADAU1373_RSPK_OUT	0x0e
+#define ADAU1373_LHP_OUT	0x0f
+#define ADAU1373_RHP_OUT	0x10
+#define ADAU1373_ADC_GAIN	0x11
+#define ADAU1373_LADC_MIXER	0x12
+#define ADAU1373_RADC_MIXER	0x13
+#define ADAU1373_LLINE1_MIX	0x14
+#define ADAU1373_RLINE1_MIX	0x15
+#define ADAU1373_LLINE2_MIX	0x16
+#define ADAU1373_RLINE2_MIX	0x17
+#define ADAU1373_LSPK_MIX	0x18
+#define ADAU1373_RSPK_MIX	0x19
+#define ADAU1373_LHP_MIX	0x1a
+#define ADAU1373_RHP_MIX	0x1b
+#define ADAU1373_EP_MIX		0x1c
+#define ADAU1373_HP_CTRL	0x1d
+#define ADAU1373_HP_CTRL2	0x1e
+#define ADAU1373_LS_CTRL	0x1f
+#define ADAU1373_EP_CTRL	0x21
+#define ADAU1373_MICBIAS_CTRL1	0x22
+#define ADAU1373_MICBIAS_CTRL2	0x23
+#define ADAU1373_OUTPUT_CTRL	0x24
+#define ADAU1373_PWDN_CTRL1	0x25
+#define ADAU1373_PWDN_CTRL2	0x26
+#define ADAU1373_PWDN_CTRL3	0x27
+#define ADAU1373_DPLL_CTRL(x)	(0x28 + (x) * 7)
+#define ADAU1373_PLL_CTRL1(x)	(0x29 + (x) * 7)
+#define ADAU1373_PLL_CTRL2(x)	(0x2a + (x) * 7)
+#define ADAU1373_PLL_CTRL3(x)	(0x2b + (x) * 7)
+#define ADAU1373_PLL_CTRL4(x)	(0x2c + (x) * 7)
+#define ADAU1373_PLL_CTRL5(x)	(0x2d + (x) * 7)
+#define ADAU1373_PLL_CTRL6(x)	(0x2e + (x) * 7)
+#define ADAU1373_PLL_CTRL7(x)	(0x2f + (x) * 7)
+#define ADAU1373_HEADDECT	0x36
+#define ADAU1373_ADC_DAC_STATUS	0x37
+#define ADAU1373_ADC_CTRL	0x3c
+#define ADAU1373_DAI(x)		(0x44 + (x))
+#define ADAU1373_CLK_SRC_DIV(x)	(0x40 + (x) * 2)
+#define ADAU1373_BCLKDIV(x)	(0x47 + (x))
+#define ADAU1373_SRC_RATIOA(x)	(0x4a + (x) * 2)
+#define ADAU1373_SRC_RATIOB(x)	(0x4b + (x) * 2)
+#define ADAU1373_DEEMP_CTRL	0x50
+#define ADAU1373_SRC_DAI_CTRL(x) (0x51 + (x))
+#define ADAU1373_DIN_MIX_CTRL(x) (0x56 + (x))
+#define ADAU1373_DOUT_MIX_CTRL(x) (0x5b + (x))
+#define ADAU1373_DAI_PBL_VOL(x)	(0x62 + (x) * 2)
+#define ADAU1373_DAI_PBR_VOL(x)	(0x63 + (x) * 2)
+#define ADAU1373_DAI_RECL_VOL(x) (0x68 + (x) * 2)
+#define ADAU1373_DAI_RECR_VOL(x) (0x69 + (x) * 2)
+#define ADAU1373_DAC1_PBL_VOL	0x6e
+#define ADAU1373_DAC1_PBR_VOL	0x6f
+#define ADAU1373_DAC2_PBL_VOL	0x70
+#define ADAU1373_DAC2_PBR_VOL	0x71
+#define ADAU1373_ADC_RECL_VOL	0x72
+#define ADAU1373_ADC_RECR_VOL	0x73
+#define ADAU1373_DMIC_RECL_VOL	0x74
+#define ADAU1373_DMIC_RECR_VOL	0x75
+#define ADAU1373_VOL_GAIN1	0x76
+#define ADAU1373_VOL_GAIN2	0x77
+#define ADAU1373_VOL_GAIN3	0x78
+#define ADAU1373_HPF_CTRL	0x7d
+#define ADAU1373_BASS1		0x7e
+#define ADAU1373_BASS2		0x7f
+#define ADAU1373_DRC(x)		(0x80 + (x) * 0x10)
+#define ADAU1373_3D_CTRL1	0xc0
+#define ADAU1373_3D_CTRL2	0xc1
+#define ADAU1373_FDSP_SEL1	0xdc
+#define ADAU1373_FDSP_SEL2	0xdd
+#define ADAU1373_FDSP_SEL3	0xde
+#define ADAU1373_FDSP_SEL4	0xdf
+#define ADAU1373_DIGMICCTRL	0xe2
+#define ADAU1373_DIGEN		0xeb
+#define ADAU1373_SOFT_RESET	0xff
+
+
+#define ADAU1373_PLL_CTRL6_DPLL_BYPASS	BIT(1)
+#define ADAU1373_PLL_CTRL6_PLL_EN	BIT(0)
+
+#define ADAU1373_DAI_INVERT_BCLK	BIT(7)
+#define ADAU1373_DAI_MASTER		BIT(6)
+#define ADAU1373_DAI_INVERT_LRCLK	BIT(4)
+#define ADAU1373_DAI_WLEN_16		0x0
+#define ADAU1373_DAI_WLEN_20		0x4
+#define ADAU1373_DAI_WLEN_24		0x8
+#define ADAU1373_DAI_WLEN_32		0xc
+#define ADAU1373_DAI_WLEN_MASK		0xc
+#define ADAU1373_DAI_FORMAT_RIGHT_J	0x0
+#define ADAU1373_DAI_FORMAT_LEFT_J	0x1
+#define ADAU1373_DAI_FORMAT_I2S		0x2
+#define ADAU1373_DAI_FORMAT_DSP		0x3
+
+#define ADAU1373_BCLKDIV_SOURCE		BIT(5)
+#define ADAU1373_BCLKDIV_32		0x03
+#define ADAU1373_BCLKDIV_64		0x02
+#define ADAU1373_BCLKDIV_128		0x01
+#define ADAU1373_BCLKDIV_256		0x00
+
+#define ADAU1373_ADC_CTRL_PEAK_DETECT	BIT(0)
+#define ADAU1373_ADC_CTRL_RESET		BIT(1)
+#define ADAU1373_ADC_CTRL_RESET_FORCE	BIT(2)
+
+#define ADAU1373_OUTPUT_CTRL_LDIFF	BIT(3)
+#define ADAU1373_OUTPUT_CTRL_LNFBEN	BIT(2)
+
+#define ADAU1373_PWDN_CTRL3_PWR_EN BIT(0)
+
+#define ADAU1373_EP_CTRL_MICBIAS1_OFFSET 4
+#define ADAU1373_EP_CTRL_MICBIAS2_OFFSET 2
+
+static const uint8_t adau1373_default_regs[] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, /* 0x30 */
+	0x00, 0x00, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x00, /* 0x40 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x78, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, /* 0x80 */
+	0x00, 0xc0, 0x88, 0x7a, 0xdf, 0x20, 0x00, 0x00,
+	0x78, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, /* 0x90 */
+	0x00, 0xc0, 0x88, 0x7a, 0xdf, 0x20, 0x00, 0x00,
+	0x78, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, /* 0xa0 */
+	0x00, 0xc0, 0x88, 0x7a, 0xdf, 0x20, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0 */
+	0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, /* 0xe0 */
+	0x00, 0x1f, 0x0f, 0x00, 0x00,
+};
+
+static const unsigned int adau1373_out_tlv[] = {
+	TLV_DB_RANGE_HEAD(4),
+	0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1),
+	8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0),
+	16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0),
+	24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0),
+};
+
+static const DECLARE_TLV_DB_MINMAX(adau1373_digital_tlv, -9563, 0);
+static const DECLARE_TLV_DB_SCALE(adau1373_in_pga_tlv, -1300, 100, 1);
+static const DECLARE_TLV_DB_SCALE(adau1373_ep_tlv, -600, 600, 1);
+
+static const DECLARE_TLV_DB_SCALE(adau1373_input_boost_tlv, 0, 2000, 0);
+static const DECLARE_TLV_DB_SCALE(adau1373_gain_boost_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adau1373_speaker_boost_tlv, 1200, 600, 0);
+
+static const char *adau1373_fdsp_sel_text[] = {
+	"None",
+	"Channel 1",
+	"Channel 2",
+	"Channel 3",
+	"Channel 4",
+	"Channel 5",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_drc1_channel_enum,
+	ADAU1373_FDSP_SEL1, 4, adau1373_fdsp_sel_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_drc2_channel_enum,
+	ADAU1373_FDSP_SEL1, 0, adau1373_fdsp_sel_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_drc3_channel_enum,
+	ADAU1373_FDSP_SEL2, 0, adau1373_fdsp_sel_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_hpf_channel_enum,
+	ADAU1373_FDSP_SEL3, 0, adau1373_fdsp_sel_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_bass_channel_enum,
+	ADAU1373_FDSP_SEL4, 4, adau1373_fdsp_sel_text);
+
+static const char *adau1373_hpf_cutoff_text[] = {
+	"3.7Hz", "50Hz", "100Hz", "150Hz", "200Hz", "250Hz", "300Hz", "350Hz",
+	"400Hz", "450Hz", "500Hz", "550Hz", "600Hz", "650Hz", "700Hz", "750Hz",
+	"800Hz",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_hpf_cutoff_enum,
+	ADAU1373_HPF_CTRL, 3, adau1373_hpf_cutoff_text);
+
+static const char *adau1373_bass_lpf_cutoff_text[] = {
+	"801Hz", "1001Hz",
+};
+
+static const char *adau1373_bass_clip_level_text[] = {
+	"0.125", "0.250", "0.370", "0.500", "0.625", "0.750", "0.875",
+};
+
+static const unsigned int adau1373_bass_clip_level_values[] = {
+	1, 2, 3, 4, 5, 6, 7,
+};
+
+static const char *adau1373_bass_hpf_cutoff_text[] = {
+	"158Hz", "232Hz", "347Hz", "520Hz",
+};
+
+static const unsigned int adau1373_bass_tlv[] = {
+	TLV_DB_RANGE_HEAD(4),
+	0, 2, TLV_DB_SCALE_ITEM(-600, 600, 1),
+	3, 4, TLV_DB_SCALE_ITEM(950, 250, 0),
+	5, 7, TLV_DB_SCALE_ITEM(1400, 150, 0),
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_bass_lpf_cutoff_enum,
+	ADAU1373_BASS1, 5, adau1373_bass_lpf_cutoff_text);
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(adau1373_bass_clip_level_enum,
+	ADAU1373_BASS1, 2, 7, adau1373_bass_clip_level_text,
+	adau1373_bass_clip_level_values);
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_bass_hpf_cutoff_enum,
+	ADAU1373_BASS1, 0, adau1373_bass_hpf_cutoff_text);
+
+static const char *adau1373_3d_level_text[] = {
+	"0%", "6.67%", "13.33%", "20%", "26.67%", "33.33%",
+	"40%", "46.67%", "53.33%", "60%", "66.67%", "73.33%",
+	"80%", "86.67", "99.33%", "100%"
+};
+
+static const char *adau1373_3d_cutoff_text[] = {
+	"No 3D", "0.03125 fs", "0.04583 fs", "0.075 fs", "0.11458 fs",
+	"0.16875 fs", "0.27083 fs"
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_3d_level_enum,
+	ADAU1373_3D_CTRL1, 4, adau1373_3d_level_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_3d_cutoff_enum,
+	ADAU1373_3D_CTRL1, 0, adau1373_3d_cutoff_text);
+
+static const unsigned int adau1373_3d_tlv[] = {
+	TLV_DB_RANGE_HEAD(2),
+	0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+	1, 7, TLV_DB_LINEAR_ITEM(-1800, -120),
+};
+
+static const char *adau1373_lr_mux_text[] = {
+	"Mute",
+	"Right Channel (L+R)",
+	"Left Channel (L+R)",
+	"Stereo",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_lineout1_lr_mux_enum,
+	ADAU1373_OUTPUT_CTRL, 4, adau1373_lr_mux_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_lineout2_lr_mux_enum,
+	ADAU1373_OUTPUT_CTRL, 6, adau1373_lr_mux_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_speaker_lr_mux_enum,
+	ADAU1373_LS_CTRL, 4, adau1373_lr_mux_text);
+
+static const struct snd_kcontrol_new adau1373_controls[] = {
+	SOC_DOUBLE_R_TLV("AIF1 Capture Volume", ADAU1373_DAI_RECL_VOL(0),
+		ADAU1373_DAI_RECR_VOL(0), 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("AIF2 Capture Volume", ADAU1373_DAI_RECL_VOL(1),
+		ADAU1373_DAI_RECR_VOL(1), 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("AIF3 Capture Volume", ADAU1373_DAI_RECL_VOL(2),
+		ADAU1373_DAI_RECR_VOL(2), 0, 0xff, 1, adau1373_digital_tlv),
+
+	SOC_DOUBLE_R_TLV("ADC Capture Volume", ADAU1373_ADC_RECL_VOL,
+		ADAU1373_ADC_RECR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("DMIC Capture Volume", ADAU1373_DMIC_RECL_VOL,
+		ADAU1373_DMIC_RECR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+
+	SOC_DOUBLE_R_TLV("AIF1 Playback Volume", ADAU1373_DAI_PBL_VOL(0),
+		ADAU1373_DAI_PBR_VOL(0), 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("AIF2 Playback Volume", ADAU1373_DAI_PBL_VOL(1),
+		ADAU1373_DAI_PBR_VOL(1), 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("AIF3 Playback Volume", ADAU1373_DAI_PBL_VOL(2),
+		ADAU1373_DAI_PBR_VOL(2), 0, 0xff, 1, adau1373_digital_tlv),
+
+	SOC_DOUBLE_R_TLV("DAC1 Playback Volume", ADAU1373_DAC1_PBL_VOL,
+		ADAU1373_DAC1_PBR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+	SOC_DOUBLE_R_TLV("DAC2 Playback Volume", ADAU1373_DAC2_PBL_VOL,
+		ADAU1373_DAC2_PBR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+
+	SOC_DOUBLE_R_TLV("Lineout1 Playback Volume", ADAU1373_LLINE_OUT(0),
+		ADAU1373_RLINE_OUT(0), 0, 0x1f, 0, adau1373_out_tlv),
+	SOC_DOUBLE_R_TLV("Speaker Playback Volume", ADAU1373_LSPK_OUT,
+		ADAU1373_RSPK_OUT, 0, 0x1f, 0, adau1373_out_tlv),
+	SOC_DOUBLE_R_TLV("Headphone Playback Volume", ADAU1373_LHP_OUT,
+		ADAU1373_RHP_OUT, 0, 0x1f, 0, adau1373_out_tlv),
+
+	SOC_DOUBLE_R_TLV("Input 1 Capture Volume", ADAU1373_AINL_CTRL(0),
+		ADAU1373_AINR_CTRL(0), 0, 0x1f, 0, adau1373_in_pga_tlv),
+	SOC_DOUBLE_R_TLV("Input 2 Capture Volume", ADAU1373_AINL_CTRL(1),
+		ADAU1373_AINR_CTRL(1), 0, 0x1f, 0, adau1373_in_pga_tlv),
+	SOC_DOUBLE_R_TLV("Input 3 Capture Volume", ADAU1373_AINL_CTRL(2),
+		ADAU1373_AINR_CTRL(2), 0, 0x1f, 0, adau1373_in_pga_tlv),
+	SOC_DOUBLE_R_TLV("Input 4 Capture Volume", ADAU1373_AINL_CTRL(3),
+		ADAU1373_AINR_CTRL(3), 0, 0x1f, 0, adau1373_in_pga_tlv),
+
+	SOC_SINGLE_TLV("Earpiece Playback Volume", ADAU1373_EP_CTRL, 0, 3, 0,
+		adau1373_ep_tlv),
+
+	SOC_DOUBLE_TLV("AIF3 Boost Playback Volume", ADAU1373_VOL_GAIN1, 4, 5,
+		1, 0, adau1373_gain_boost_tlv),
+	SOC_DOUBLE_TLV("AIF2 Boost Playback Volume", ADAU1373_VOL_GAIN1, 2, 3,
+		1, 0, adau1373_gain_boost_tlv),
+	SOC_DOUBLE_TLV("AIF1 Boost Playback Volume", ADAU1373_VOL_GAIN1, 0, 1,
+		1, 0, adau1373_gain_boost_tlv),
+	SOC_DOUBLE_TLV("AIF3 Boost Capture Volume", ADAU1373_VOL_GAIN2, 4, 5,
+		1, 0, adau1373_gain_boost_tlv),
+	SOC_DOUBLE_TLV("AIF2 Boost Capture Volume", ADAU1373_VOL_GAIN2, 2, 3,
+		1, 0, adau1373_gain_boost_tlv),
+	SOC_DOUBLE_TLV("AIF1 Boost Capture Volume", ADAU1373_VOL_GAIN2, 0, 1,
+		1, 0, adau1373_gain_boost_tlv),
+	SOC_DOUBLE_TLV("DMIC Boost Capture Volume", ADAU1373_VOL_GAIN3, 6, 7,
+		1, 0, adau1373_gain_boost_tlv),
+	SOC_DOUBLE_TLV("ADC Boost Capture Volume", ADAU1373_VOL_GAIN3, 4, 5,
+		1, 0, adau1373_gain_boost_tlv),
+	SOC_DOUBLE_TLV("DAC2 Boost Playback Volume", ADAU1373_VOL_GAIN3, 2, 3,
+		1, 0, adau1373_gain_boost_tlv),
+	SOC_DOUBLE_TLV("DAC1 Boost Playback Volume", ADAU1373_VOL_GAIN3, 0, 1,
+		1, 0, adau1373_gain_boost_tlv),
+
+	SOC_DOUBLE_TLV("Input 1 Boost Capture Volume", ADAU1373_ADC_GAIN, 0, 4,
+		1, 0, adau1373_input_boost_tlv),
+	SOC_DOUBLE_TLV("Input 2 Boost Capture Volume", ADAU1373_ADC_GAIN, 1, 5,
+		1, 0, adau1373_input_boost_tlv),
+	SOC_DOUBLE_TLV("Input 3 Boost Capture Volume", ADAU1373_ADC_GAIN, 2, 6,
+		1, 0, adau1373_input_boost_tlv),
+	SOC_DOUBLE_TLV("Input 4 Boost Capture Volume", ADAU1373_ADC_GAIN, 3, 7,
+		1, 0, adau1373_input_boost_tlv),
+
+	SOC_DOUBLE_TLV("Speaker Boost Playback Volume", ADAU1373_LS_CTRL, 2, 3,
+		1, 0, adau1373_speaker_boost_tlv),
+
+	SOC_ENUM("Lineout1 LR Mux", adau1373_lineout1_lr_mux_enum),
+	SOC_ENUM("Speaker LR Mux", adau1373_speaker_lr_mux_enum),
+
+	SOC_ENUM("HPF Cutoff", adau1373_hpf_cutoff_enum),
+	SOC_DOUBLE("HPF Switch", ADAU1373_HPF_CTRL, 1, 0, 1, 0),
+	SOC_ENUM("HPF Channel", adau1373_hpf_channel_enum),
+
+	SOC_ENUM("Bass HPF Cutoff", adau1373_bass_hpf_cutoff_enum),
+	SOC_VALUE_ENUM("Bass Clip Level Threshold",
+	    adau1373_bass_clip_level_enum),
+	SOC_ENUM("Bass LPF Cutoff", adau1373_bass_lpf_cutoff_enum),
+	SOC_DOUBLE("Bass Playback Switch", ADAU1373_BASS2, 0, 1, 1, 0),
+	SOC_SINGLE_TLV("Bass Playback Volume", ADAU1373_BASS2, 2, 7, 0,
+	    adau1373_bass_tlv),
+	SOC_ENUM("Bass Channel", adau1373_bass_channel_enum),
+
+	SOC_ENUM("3D Freq", adau1373_3d_cutoff_enum),
+	SOC_ENUM("3D Level", adau1373_3d_level_enum),
+	SOC_SINGLE("3D Playback Switch", ADAU1373_3D_CTRL2, 0, 1, 0),
+	SOC_SINGLE_TLV("3D Playback Volume", ADAU1373_3D_CTRL2, 2, 7, 0,
+		adau1373_3d_tlv),
+	SOC_ENUM("3D Channel", adau1373_bass_channel_enum),
+
+	SOC_SINGLE("Zero Cross Switch", ADAU1373_PWDN_CTRL3, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1373_lineout2_controls[] = {
+	SOC_DOUBLE_R_TLV("Lineout2 Playback Volume", ADAU1373_LLINE_OUT(1),
+		ADAU1373_RLINE_OUT(1), 0, 0x1f, 0, adau1373_out_tlv),
+	SOC_ENUM("Lineout2 LR Mux", adau1373_lineout2_lr_mux_enum),
+};
+
+static const struct snd_kcontrol_new adau1373_drc_controls[] = {
+	SOC_ENUM("DRC1 Channel", adau1373_drc1_channel_enum),
+	SOC_ENUM("DRC2 Channel", adau1373_drc2_channel_enum),
+	SOC_ENUM("DRC3 Channel", adau1373_drc3_channel_enum),
+};
+
+static int adau1373_pll_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	unsigned int pll_id = w->name[3] - '1';
+	unsigned int val;
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		val = ADAU1373_PLL_CTRL6_PLL_EN;
+	else
+		val = 0;
+
+	snd_soc_update_bits(codec, ADAU1373_PLL_CTRL6(pll_id),
+		ADAU1373_PLL_CTRL6_PLL_EN, val);
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		mdelay(5);
+
+	return 0;
+}
+
+static const char *adau1373_decimator_text[] = {
+	"ADC",
+	"DMIC1",
+};
+
+static const struct soc_enum adau1373_decimator_enum =
+	SOC_ENUM_SINGLE(0, 0, 2, adau1373_decimator_text);
+
+static const struct snd_kcontrol_new adau1373_decimator_mux =
+	SOC_DAPM_ENUM_VIRT("Decimator Mux", adau1373_decimator_enum);
+
+static const struct snd_kcontrol_new adau1373_left_adc_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DAC1 Switch", ADAU1373_LADC_MIXER, 4, 1, 0),
+	SOC_DAPM_SINGLE("Input 4 Switch", ADAU1373_LADC_MIXER, 3, 1, 0),
+	SOC_DAPM_SINGLE("Input 3 Switch", ADAU1373_LADC_MIXER, 2, 1, 0),
+	SOC_DAPM_SINGLE("Input 2 Switch", ADAU1373_LADC_MIXER, 1, 1, 0),
+	SOC_DAPM_SINGLE("Input 1 Switch", ADAU1373_LADC_MIXER, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1373_right_adc_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DAC1 Switch", ADAU1373_RADC_MIXER, 4, 1, 0),
+	SOC_DAPM_SINGLE("Input 4 Switch", ADAU1373_RADC_MIXER, 3, 1, 0),
+	SOC_DAPM_SINGLE("Input 3 Switch", ADAU1373_RADC_MIXER, 2, 1, 0),
+	SOC_DAPM_SINGLE("Input 2 Switch", ADAU1373_RADC_MIXER, 1, 1, 0),
+	SOC_DAPM_SINGLE("Input 1 Switch", ADAU1373_RADC_MIXER, 0, 1, 0),
+};
+
+#define DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+	SOC_DAPM_SINGLE("Left DAC2 Switch", _reg, 7, 1, 0), \
+	SOC_DAPM_SINGLE("Right DAC2 Switch", _reg, 6, 1, 0), \
+	SOC_DAPM_SINGLE("Left DAC1 Switch", _reg, 5, 1, 0), \
+	SOC_DAPM_SINGLE("Right DAC1 Switch", _reg, 4, 1, 0), \
+	SOC_DAPM_SINGLE("Input 4 Bypass Switch", _reg, 3, 1, 0), \
+	SOC_DAPM_SINGLE("Input 3 Bypass Switch", _reg, 2, 1, 0), \
+	SOC_DAPM_SINGLE("Input 2 Bypass Switch", _reg, 1, 1, 0), \
+	SOC_DAPM_SINGLE("Input 1 Bypass Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_line1_mixer_controls,
+	ADAU1373_LLINE1_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_line1_mixer_controls,
+	ADAU1373_RLINE1_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_line2_mixer_controls,
+	ADAU1373_LLINE2_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_line2_mixer_controls,
+	ADAU1373_RLINE2_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_spk_mixer_controls,
+	ADAU1373_LSPK_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_spk_mixer_controls,
+	ADAU1373_RSPK_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_ep_mixer_controls,
+	ADAU1373_EP_MIX);
+
+static const struct snd_kcontrol_new adau1373_left_hp_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Left DAC1 Switch", ADAU1373_LHP_MIX, 5, 1, 0),
+	SOC_DAPM_SINGLE("Left DAC2 Switch", ADAU1373_LHP_MIX, 4, 1, 0),
+	SOC_DAPM_SINGLE("Input 4 Bypass Switch", ADAU1373_LHP_MIX, 3, 1, 0),
+	SOC_DAPM_SINGLE("Input 3 Bypass Switch", ADAU1373_LHP_MIX, 2, 1, 0),
+	SOC_DAPM_SINGLE("Input 2 Bypass Switch", ADAU1373_LHP_MIX, 1, 1, 0),
+	SOC_DAPM_SINGLE("Input 1 Bypass Switch", ADAU1373_LHP_MIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1373_right_hp_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Right DAC1 Switch", ADAU1373_RHP_MIX, 5, 1, 0),
+	SOC_DAPM_SINGLE("Right DAC2 Switch", ADAU1373_RHP_MIX, 4, 1, 0),
+	SOC_DAPM_SINGLE("Input 4 Bypass Switch", ADAU1373_RHP_MIX, 3, 1, 0),
+	SOC_DAPM_SINGLE("Input 3 Bypass Switch", ADAU1373_RHP_MIX, 2, 1, 0),
+	SOC_DAPM_SINGLE("Input 2 Bypass Switch", ADAU1373_RHP_MIX, 1, 1, 0),
+	SOC_DAPM_SINGLE("Input 1 Bypass Switch", ADAU1373_RHP_MIX, 0, 1, 0),
+};
+
+#define DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+	SOC_DAPM_SINGLE("DMIC2 Swapped Switch", _reg, 6, 1, 0), \
+	SOC_DAPM_SINGLE("DMIC2 Switch", _reg, 5, 1, 0), \
+	SOC_DAPM_SINGLE("ADC/DMIC1 Swapped Switch", _reg, 4, 1, 0), \
+	SOC_DAPM_SINGLE("ADC/DMIC1 Switch", _reg, 3, 1, 0), \
+	SOC_DAPM_SINGLE("AIF3 Switch", _reg, 2, 1, 0), \
+	SOC_DAPM_SINGLE("AIF2 Switch", _reg, 1, 1, 0), \
+	SOC_DAPM_SINGLE("AIF1 Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel1_mixer_controls,
+	ADAU1373_DIN_MIX_CTRL(0));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel2_mixer_controls,
+	ADAU1373_DIN_MIX_CTRL(1));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel3_mixer_controls,
+	ADAU1373_DIN_MIX_CTRL(2));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel4_mixer_controls,
+	ADAU1373_DIN_MIX_CTRL(3));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel5_mixer_controls,
+	ADAU1373_DIN_MIX_CTRL(4));
+
+#define DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+	SOC_DAPM_SINGLE("DSP Channel5 Switch", _reg, 4, 1, 0), \
+	SOC_DAPM_SINGLE("DSP Channel4 Switch", _reg, 3, 1, 0), \
+	SOC_DAPM_SINGLE("DSP Channel3 Switch", _reg, 2, 1, 0), \
+	SOC_DAPM_SINGLE("DSP Channel2 Switch", _reg, 1, 1, 0), \
+	SOC_DAPM_SINGLE("DSP Channel1 Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif1_mixer_controls,
+	ADAU1373_DOUT_MIX_CTRL(0));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif2_mixer_controls,
+	ADAU1373_DOUT_MIX_CTRL(1));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif3_mixer_controls,
+	ADAU1373_DOUT_MIX_CTRL(2));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_dac1_mixer_controls,
+	ADAU1373_DOUT_MIX_CTRL(3));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_dac2_mixer_controls,
+	ADAU1373_DOUT_MIX_CTRL(4));
+
+static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = {
+	/* Datasheet claims Left ADC is bit 6 and Right ADC is bit 7, but that
+	 * doesn't seem to be the case. */
+	SND_SOC_DAPM_ADC("Left ADC", NULL, ADAU1373_PWDN_CTRL1, 7, 0),
+	SND_SOC_DAPM_ADC("Right ADC", NULL, ADAU1373_PWDN_CTRL1, 6, 0),
+
+	SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1373_DIGMICCTRL, 0, 0),
+	SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1373_DIGMICCTRL, 2, 0),
+
+	SND_SOC_DAPM_VIRT_MUX("Decimator Mux", SND_SOC_NOPM, 0, 0,
+		&adau1373_decimator_mux),
+
+	SND_SOC_DAPM_SUPPLY("MICBIAS2", ADAU1373_PWDN_CTRL1, 5, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("MICBIAS1", ADAU1373_PWDN_CTRL1, 4, 0, NULL, 0),
+
+	SND_SOC_DAPM_PGA("IN4PGA", ADAU1373_PWDN_CTRL1, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("IN3PGA", ADAU1373_PWDN_CTRL1, 2, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("IN2PGA", ADAU1373_PWDN_CTRL1, 1, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("IN1PGA", ADAU1373_PWDN_CTRL1, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_DAC("Left DAC2", NULL, ADAU1373_PWDN_CTRL2, 7, 0),
+	SND_SOC_DAPM_DAC("Right DAC2", NULL, ADAU1373_PWDN_CTRL2, 6, 0),
+	SND_SOC_DAPM_DAC("Left DAC1", NULL, ADAU1373_PWDN_CTRL2, 5, 0),
+	SND_SOC_DAPM_DAC("Right DAC1", NULL, ADAU1373_PWDN_CTRL2, 4, 0),
+
+	SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_left_adc_mixer_controls),
+	SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_right_adc_mixer_controls),
+
+	SOC_MIXER_ARRAY("Left Lineout2 Mixer", ADAU1373_PWDN_CTRL2, 3, 0,
+		adau1373_left_line2_mixer_controls),
+	SOC_MIXER_ARRAY("Right Lineout2 Mixer", ADAU1373_PWDN_CTRL2, 2, 0,
+		adau1373_right_line2_mixer_controls),
+	SOC_MIXER_ARRAY("Left Lineout1 Mixer", ADAU1373_PWDN_CTRL2, 1, 0,
+		adau1373_left_line1_mixer_controls),
+	SOC_MIXER_ARRAY("Right Lineout1 Mixer", ADAU1373_PWDN_CTRL2, 0, 0,
+		adau1373_right_line1_mixer_controls),
+
+	SOC_MIXER_ARRAY("Earpiece Mixer", ADAU1373_PWDN_CTRL3, 4, 0,
+		adau1373_ep_mixer_controls),
+	SOC_MIXER_ARRAY("Left Speaker Mixer", ADAU1373_PWDN_CTRL3, 3, 0,
+		adau1373_left_spk_mixer_controls),
+	SOC_MIXER_ARRAY("Right Speaker Mixer", ADAU1373_PWDN_CTRL3, 2, 0,
+		adau1373_right_spk_mixer_controls),
+	SOC_MIXER_ARRAY("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_left_hp_mixer_controls),
+	SOC_MIXER_ARRAY("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_right_hp_mixer_controls),
+	SND_SOC_DAPM_SUPPLY("Headphone Enable", ADAU1373_PWDN_CTRL3, 1, 0,
+		NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("AIF1 CLK", ADAU1373_SRC_DAI_CTRL(0), 0, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF2 CLK", ADAU1373_SRC_DAI_CTRL(1), 0, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF3 CLK", ADAU1373_SRC_DAI_CTRL(2), 0, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF1 IN SRC", ADAU1373_SRC_DAI_CTRL(0), 2, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF1 OUT SRC", ADAU1373_SRC_DAI_CTRL(0), 1, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF2 IN SRC", ADAU1373_SRC_DAI_CTRL(1), 2, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF2 OUT SRC", ADAU1373_SRC_DAI_CTRL(1), 1, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF3 IN SRC", ADAU1373_SRC_DAI_CTRL(2), 2, 0,
+	    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AIF3 OUT SRC", ADAU1373_SRC_DAI_CTRL(2), 1, 0,
+	    NULL, 0),
+
+	SND_SOC_DAPM_AIF_IN("AIF1 IN", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AIF1 OUT", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("AIF2 IN", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AIF2 OUT", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("AIF3 IN", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AIF3 OUT", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+	SOC_MIXER_ARRAY("DSP Channel1 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dsp_channel1_mixer_controls),
+	SOC_MIXER_ARRAY("DSP Channel2 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dsp_channel2_mixer_controls),
+	SOC_MIXER_ARRAY("DSP Channel3 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dsp_channel3_mixer_controls),
+	SOC_MIXER_ARRAY("DSP Channel4 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dsp_channel4_mixer_controls),
+	SOC_MIXER_ARRAY("DSP Channel5 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dsp_channel5_mixer_controls),
+
+	SOC_MIXER_ARRAY("AIF1 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_aif1_mixer_controls),
+	SOC_MIXER_ARRAY("AIF2 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_aif2_mixer_controls),
+	SOC_MIXER_ARRAY("AIF3 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_aif3_mixer_controls),
+	SOC_MIXER_ARRAY("DAC1 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dac1_mixer_controls),
+	SOC_MIXER_ARRAY("DAC2 Mixer", SND_SOC_NOPM, 0, 0,
+		adau1373_dac2_mixer_controls),
+
+	SND_SOC_DAPM_SUPPLY("DSP", ADAU1373_DIGEN, 4, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Recording Engine B", ADAU1373_DIGEN, 3, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Recording Engine A", ADAU1373_DIGEN, 2, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Playback Engine B", ADAU1373_DIGEN, 1, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Playback Engine A", ADAU1373_DIGEN, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("PLL1", SND_SOC_NOPM, 0, 0, adau1373_pll_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SUPPLY("PLL2", SND_SOC_NOPM, 0, 0, adau1373_pll_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SUPPLY("SYSCLK1", ADAU1373_CLK_SRC_DIV(0), 7, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("SYSCLK2", ADAU1373_CLK_SRC_DIV(1), 7, 0, NULL, 0),
+
+	SND_SOC_DAPM_INPUT("AIN1L"),
+	SND_SOC_DAPM_INPUT("AIN1R"),
+	SND_SOC_DAPM_INPUT("AIN2L"),
+	SND_SOC_DAPM_INPUT("AIN2R"),
+	SND_SOC_DAPM_INPUT("AIN3L"),
+	SND_SOC_DAPM_INPUT("AIN3R"),
+	SND_SOC_DAPM_INPUT("AIN4L"),
+	SND_SOC_DAPM_INPUT("AIN4R"),
+
+	SND_SOC_DAPM_INPUT("DMIC1DAT"),
+	SND_SOC_DAPM_INPUT("DMIC2DAT"),
+
+	SND_SOC_DAPM_OUTPUT("LOUT1L"),
+	SND_SOC_DAPM_OUTPUT("LOUT1R"),
+	SND_SOC_DAPM_OUTPUT("LOUT2L"),
+	SND_SOC_DAPM_OUTPUT("LOUT2R"),
+	SND_SOC_DAPM_OUTPUT("HPL"),
+	SND_SOC_DAPM_OUTPUT("HPR"),
+	SND_SOC_DAPM_OUTPUT("SPKL"),
+	SND_SOC_DAPM_OUTPUT("SPKR"),
+	SND_SOC_DAPM_OUTPUT("EP"),
+};
+
+static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
+	struct snd_soc_dapm_widget *sink)
+{
+	struct snd_soc_codec *codec = source->codec;
+	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+	unsigned int dai;
+	const char *clk;
+
+	dai = sink->name[3] - '1';
+
+	if (!adau1373->dais[dai].master)
+		return 0;
+
+	if (adau1373->dais[dai].clk_src == ADAU1373_CLK_SRC_PLL1)
+		clk = "SYSCLK1";
+	else
+		clk = "SYSCLK2";
+
+	return strcmp(source->name, clk) == 0;
+}
+
+static int adau1373_check_src(struct snd_soc_dapm_widget *source,
+	struct snd_soc_dapm_widget *sink)
+{
+	struct snd_soc_codec *codec = source->codec;
+	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+	unsigned int dai;
+
+	dai = sink->name[3] - '1';
+
+	return adau1373->dais[dai].enable_src;
+}
+
+#define DSP_CHANNEL_MIXER_ROUTES(_sink) \
+	{ _sink, "DMIC2 Swapped Switch", "DMIC2" }, \
+	{ _sink, "DMIC2 Switch", "DMIC2" }, \
+	{ _sink, "ADC/DMIC1 Swapped Switch", "Decimator Mux" }, \
+	{ _sink, "ADC/DMIC1 Switch", "Decimator Mux" }, \
+	{ _sink, "AIF1 Switch", "AIF1 IN" }, \
+	{ _sink, "AIF2 Switch", "AIF2 IN" }, \
+	{ _sink, "AIF3 Switch", "AIF3 IN" }
+
+#define DSP_OUTPUT_MIXER_ROUTES(_sink) \
+	{ _sink, "DSP Channel1 Switch", "DSP Channel1 Mixer" }, \
+	{ _sink, "DSP Channel2 Switch", "DSP Channel2 Mixer" }, \
+	{ _sink, "DSP Channel3 Switch", "DSP Channel3 Mixer" }, \
+	{ _sink, "DSP Channel4 Switch", "DSP Channel4 Mixer" }, \
+	{ _sink, "DSP Channel5 Switch", "DSP Channel5 Mixer" }
+
+#define LEFT_OUTPUT_MIXER_ROUTES(_sink) \
+	{ _sink, "Right DAC2 Switch", "Right DAC2" }, \
+	{ _sink, "Left DAC2 Switch", "Left DAC2" }, \
+	{ _sink, "Right DAC1 Switch", "Right DAC1" }, \
+	{ _sink, "Left DAC1 Switch", "Left DAC1" }, \
+	{ _sink, "Input 1 Bypass Switch", "IN1PGA" }, \
+	{ _sink, "Input 2 Bypass Switch", "IN2PGA" }, \
+	{ _sink, "Input 3 Bypass Switch", "IN3PGA" }, \
+	{ _sink, "Input 4 Bypass Switch", "IN4PGA" }
+
+#define RIGHT_OUTPUT_MIXER_ROUTES(_sink) \
+	{ _sink, "Right DAC2 Switch", "Right DAC2" }, \
+	{ _sink, "Left DAC2 Switch", "Left DAC2" }, \
+	{ _sink, "Right DAC1 Switch", "Right DAC1" }, \
+	{ _sink, "Left DAC1 Switch", "Left DAC1" }, \
+	{ _sink, "Input 1 Bypass Switch", "IN1PGA" }, \
+	{ _sink, "Input 2 Bypass Switch", "IN2PGA" }, \
+	{ _sink, "Input 3 Bypass Switch", "IN3PGA" }, \
+	{ _sink, "Input 4 Bypass Switch", "IN4PGA" }
+
+static const struct snd_soc_dapm_route adau1373_dapm_routes[] = {
+	{ "Left ADC Mixer", "DAC1 Switch", "Left DAC1" },
+	{ "Left ADC Mixer", "Input 1 Switch", "IN1PGA" },
+	{ "Left ADC Mixer", "Input 2 Switch", "IN2PGA" },
+	{ "Left ADC Mixer", "Input 3 Switch", "IN3PGA" },
+	{ "Left ADC Mixer", "Input 4 Switch", "IN4PGA" },
+
+	{ "Right ADC Mixer", "DAC1 Switch", "Right DAC1" },
+	{ "Right ADC Mixer", "Input 1 Switch", "IN1PGA" },
+	{ "Right ADC Mixer", "Input 2 Switch", "IN2PGA" },
+	{ "Right ADC Mixer", "Input 3 Switch", "IN3PGA" },
+	{ "Right ADC Mixer", "Input 4 Switch", "IN4PGA" },
+
+	{ "Left ADC", NULL, "Left ADC Mixer" },
+	{ "Right ADC", NULL, "Right ADC Mixer" },
+
+	{ "Decimator Mux", "ADC", "Left ADC" },
+	{ "Decimator Mux", "ADC", "Right ADC" },
+	{ "Decimator Mux", "DMIC1", "DMIC1" },
+
+	DSP_CHANNEL_MIXER_ROUTES("DSP Channel1 Mixer"),
+	DSP_CHANNEL_MIXER_ROUTES("DSP Channel2 Mixer"),
+	DSP_CHANNEL_MIXER_ROUTES("DSP Channel3 Mixer"),
+	DSP_CHANNEL_MIXER_ROUTES("DSP Channel4 Mixer"),
+	DSP_CHANNEL_MIXER_ROUTES("DSP Channel5 Mixer"),
+
+	DSP_OUTPUT_MIXER_ROUTES("AIF1 Mixer"),
+	DSP_OUTPUT_MIXER_ROUTES("AIF2 Mixer"),
+	DSP_OUTPUT_MIXER_ROUTES("AIF3 Mixer"),
+	DSP_OUTPUT_MIXER_ROUTES("DAC1 Mixer"),
+	DSP_OUTPUT_MIXER_ROUTES("DAC2 Mixer"),
+
+	{ "AIF1 OUT", NULL, "AIF1 Mixer" },
+	{ "AIF2 OUT", NULL, "AIF2 Mixer" },
+	{ "AIF3 OUT", NULL, "AIF3 Mixer" },
+	{ "Left DAC1", NULL, "DAC1 Mixer" },
+	{ "Right DAC1", NULL, "DAC1 Mixer" },
+	{ "Left DAC2", NULL, "DAC2 Mixer" },
+	{ "Right DAC2", NULL, "DAC2 Mixer" },
+
+	LEFT_OUTPUT_MIXER_ROUTES("Left Lineout1 Mixer"),
+	RIGHT_OUTPUT_MIXER_ROUTES("Right Lineout1 Mixer"),
+	LEFT_OUTPUT_MIXER_ROUTES("Left Lineout2 Mixer"),
+	RIGHT_OUTPUT_MIXER_ROUTES("Right Lineout2 Mixer"),
+	LEFT_OUTPUT_MIXER_ROUTES("Left Speaker Mixer"),
+	RIGHT_OUTPUT_MIXER_ROUTES("Right Speaker Mixer"),
+
+	{ "Left Headphone Mixer", "Left DAC2 Switch", "Left DAC2" },
+	{ "Left Headphone Mixer", "Left DAC1 Switch", "Left DAC1" },
+	{ "Left Headphone Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+	{ "Left Headphone Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+	{ "Left Headphone Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+	{ "Left Headphone Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+	{ "Right Headphone Mixer", "Right DAC2 Switch", "Right DAC2" },
+	{ "Right Headphone Mixer", "Right DAC1 Switch", "Right DAC1" },
+	{ "Right Headphone Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+	{ "Right Headphone Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+	{ "Right Headphone Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+	{ "Right Headphone Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+
+	{ "Left Headphone Mixer", NULL, "Headphone Enable" },
+	{ "Right Headphone Mixer", NULL, "Headphone Enable" },
+
+	{ "Earpiece Mixer", "Right DAC2 Switch", "Right DAC2" },
+	{ "Earpiece Mixer", "Left DAC2 Switch", "Left DAC2" },
+	{ "Earpiece Mixer", "Right DAC1 Switch", "Right DAC1" },
+	{ "Earpiece Mixer", "Left DAC1 Switch", "Left DAC1" },
+	{ "Earpiece Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+	{ "Earpiece Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+	{ "Earpiece Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+	{ "Earpiece Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+
+	{ "LOUT1L", NULL, "Left Lineout1 Mixer" },
+	{ "LOUT1R", NULL, "Right Lineout1 Mixer" },
+	{ "LOUT2L", NULL, "Left Lineout2 Mixer" },
+	{ "LOUT2R", NULL, "Right Lineout2 Mixer" },
+	{ "SPKL", NULL, "Left Speaker Mixer" },
+	{ "SPKR", NULL, "Right Speaker Mixer" },
+	{ "HPL", NULL, "Left Headphone Mixer" },
+	{ "HPR", NULL, "Right Headphone Mixer" },
+	{ "EP", NULL, "Earpiece Mixer" },
+
+	{ "IN1PGA", NULL, "AIN1L" },
+	{ "IN2PGA", NULL, "AIN2L" },
+	{ "IN3PGA", NULL, "AIN3L" },
+	{ "IN4PGA", NULL, "AIN4L" },
+	{ "IN1PGA", NULL, "AIN1R" },
+	{ "IN2PGA", NULL, "AIN2R" },
+	{ "IN3PGA", NULL, "AIN3R" },
+	{ "IN4PGA", NULL, "AIN4R" },
+
+	{ "SYSCLK1", NULL, "PLL1" },
+	{ "SYSCLK2", NULL, "PLL2" },
+
+	{ "Left DAC1", NULL, "SYSCLK1" },
+	{ "Right DAC1", NULL, "SYSCLK1" },
+	{ "Left DAC2", NULL, "SYSCLK1" },
+	{ "Right DAC2", NULL, "SYSCLK1" },
+	{ "Left ADC", NULL, "SYSCLK1" },
+	{ "Right ADC", NULL, "SYSCLK1" },
+
+	{ "DSP", NULL, "SYSCLK1" },
+
+	{ "AIF1 Mixer", NULL, "DSP" },
+	{ "AIF2 Mixer", NULL, "DSP" },
+	{ "AIF3 Mixer", NULL, "DSP" },
+	{ "DAC1 Mixer", NULL, "DSP" },
+	{ "DAC2 Mixer", NULL, "DSP" },
+	{ "DAC1 Mixer", NULL, "Playback Engine A" },
+	{ "DAC2 Mixer", NULL, "Playback Engine B" },
+	{ "Left ADC Mixer", NULL, "Recording Engine A" },
+	{ "Right ADC Mixer", NULL, "Recording Engine A" },
+
+	{ "AIF1 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+	{ "AIF2 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+	{ "AIF3 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+	{ "AIF1 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+	{ "AIF2 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+	{ "AIF3 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+
+	{ "AIF1 IN", NULL, "AIF1 CLK" },
+	{ "AIF1 OUT", NULL, "AIF1 CLK" },
+	{ "AIF2 IN", NULL, "AIF2 CLK" },
+	{ "AIF2 OUT", NULL, "AIF2 CLK" },
+	{ "AIF3 IN", NULL, "AIF3 CLK" },
+	{ "AIF3 OUT", NULL, "AIF3 CLK" },
+	{ "AIF1 IN", NULL, "AIF1 IN SRC", adau1373_check_src },
+	{ "AIF1 OUT", NULL, "AIF1 OUT SRC", adau1373_check_src },
+	{ "AIF2 IN", NULL, "AIF2 IN SRC", adau1373_check_src },
+	{ "AIF2 OUT", NULL, "AIF2 OUT SRC", adau1373_check_src },
+	{ "AIF3 IN", NULL, "AIF3 IN SRC", adau1373_check_src },
+	{ "AIF3 OUT", NULL, "AIF3 OUT SRC", adau1373_check_src },
+
+	{ "DMIC1", NULL, "DMIC1DAT" },
+	{ "DMIC1", NULL, "SYSCLK1" },
+	{ "DMIC1", NULL, "Recording Engine A" },
+	{ "DMIC2", NULL, "DMIC2DAT" },
+	{ "DMIC2", NULL, "SYSCLK1" },
+	{ "DMIC2", NULL, "Recording Engine B" },
+};
+
+static int adau1373_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+	struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+	unsigned int div;
+	unsigned int freq;
+	unsigned int ctrl;
+
+	freq = adau1373_dai->sysclk;
+
+	if (freq % params_rate(params) != 0)
+		return -EINVAL;
+
+	switch (freq / params_rate(params)) {
+	case 1024: /* sysclk / 256 */
+		div = 0;
+		break;
+	case 1536: /* 2/3 sysclk / 256 */
+		div = 1;
+		break;
+	case 2048: /* 1/2 sysclk / 256 */
+		div = 2;
+		break;
+	case 3072: /* 1/3 sysclk / 256 */
+		div = 3;
+		break;
+	case 4096: /* 1/4 sysclk / 256 */
+		div = 4;
+		break;
+	case 6144: /* 1/6 sysclk / 256 */
+		div = 5;
+		break;
+	case 5632: /* 2/11 sysclk / 256 */
+		div = 6;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	adau1373_dai->enable_src = (div != 0);
+
+	snd_soc_update_bits(codec, ADAU1373_BCLKDIV(dai->id),
+		~ADAU1373_BCLKDIV_SOURCE, (div << 2) | ADAU1373_BCLKDIV_64);
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		ctrl = ADAU1373_DAI_WLEN_16;
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		ctrl = ADAU1373_DAI_WLEN_20;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ctrl = ADAU1373_DAI_WLEN_24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		ctrl = ADAU1373_DAI_WLEN_32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return snd_soc_update_bits(codec, ADAU1373_DAI(dai->id),
+			ADAU1373_DAI_WLEN_MASK, ctrl);
+}
+
+static int adau1373_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+	struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+	unsigned int ctrl;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		ctrl = ADAU1373_DAI_MASTER;
+		adau1373_dai->master = true;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		ctrl = 0;
+		adau1373_dai->master = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		ctrl |= ADAU1373_DAI_FORMAT_I2S;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		ctrl |= ADAU1373_DAI_FORMAT_LEFT_J;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		ctrl |= ADAU1373_DAI_FORMAT_RIGHT_J;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		ctrl |= ADAU1373_DAI_FORMAT_DSP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		ctrl |= ADAU1373_DAI_INVERT_BCLK;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		ctrl |= ADAU1373_DAI_INVERT_LRCLK;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		ctrl |= ADAU1373_DAI_INVERT_LRCLK | ADAU1373_DAI_INVERT_BCLK;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, ADAU1373_DAI(dai->id),
+		~ADAU1373_DAI_WLEN_MASK, ctrl);
+
+	return 0;
+}
+
+static int adau1373_set_dai_sysclk(struct snd_soc_dai *dai,
+	int clk_id, unsigned int freq, int dir)
+{
+	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(dai->codec);
+	struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+
+	switch (clk_id) {
+	case ADAU1373_CLK_SRC_PLL1:
+	case ADAU1373_CLK_SRC_PLL2:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	adau1373_dai->sysclk = freq;
+	adau1373_dai->clk_src = clk_id;
+
+	snd_soc_update_bits(dai->codec, ADAU1373_BCLKDIV(dai->id),
+		ADAU1373_BCLKDIV_SOURCE, clk_id << 5);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops adau1373_dai_ops = {
+	.hw_params	= adau1373_hw_params,
+	.set_sysclk	= adau1373_set_dai_sysclk,
+	.set_fmt	= adau1373_set_dai_fmt,
+};
+
+#define ADAU1373_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_driver adau1373_dai_driver[] = {
+	{
+		.id = 0,
+		.name = "adau1373-aif1",
+		.playback = {
+			.stream_name = "AIF1 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.capture = {
+			.stream_name = "AIF1 Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.ops = &adau1373_dai_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.id = 1,
+		.name = "adau1373-aif2",
+		.playback = {
+			.stream_name = "AIF2 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.capture = {
+			.stream_name = "AIF2 Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.ops = &adau1373_dai_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.id = 2,
+		.name = "adau1373-aif3",
+		.playback = {
+			.stream_name = "AIF3 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.capture = {
+			.stream_name = "AIF3 Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = ADAU1373_FORMATS,
+		},
+		.ops = &adau1373_dai_ops,
+		.symmetric_rates = 1,
+	},
+};
+
+static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
+	int source, unsigned int freq_in, unsigned int freq_out)
+{
+	unsigned int dpll_div = 0;
+	unsigned int x, r, n, m, i, j, mode;
+
+	switch (pll_id) {
+	case ADAU1373_PLL1:
+	case ADAU1373_PLL2:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (source) {
+	case ADAU1373_PLL_SRC_BCLK1:
+	case ADAU1373_PLL_SRC_BCLK2:
+	case ADAU1373_PLL_SRC_BCLK3:
+	case ADAU1373_PLL_SRC_LRCLK1:
+	case ADAU1373_PLL_SRC_LRCLK2:
+	case ADAU1373_PLL_SRC_LRCLK3:
+	case ADAU1373_PLL_SRC_MCLK1:
+	case ADAU1373_PLL_SRC_MCLK2:
+	case ADAU1373_PLL_SRC_GPIO1:
+	case ADAU1373_PLL_SRC_GPIO2:
+	case ADAU1373_PLL_SRC_GPIO3:
+	case ADAU1373_PLL_SRC_GPIO4:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (freq_in < 7813 || freq_in > 27000000)
+		return -EINVAL;
+
+	if (freq_out < 45158000 || freq_out > 49152000)
+		return -EINVAL;
+
+	/* APLL input needs to be >= 8Mhz, so in case freq_in is less we use the
+	 * DPLL to get it there. DPLL_out = (DPLL_in / div) * 1024 */
+	while (freq_in < 8000000) {
+		freq_in *= 2;
+		dpll_div++;
+	}
+
+	if (freq_out % freq_in != 0) {
+		/* fout = fin * (r + (n/m)) / x */
+		x = DIV_ROUND_UP(freq_in, 13500000);
+		freq_in /= x;
+		r = freq_out / freq_in;
+		i = freq_out % freq_in;
+		j = gcd(i, freq_in);
+		n = i / j;
+		m = freq_in / j;
+		x--;
+		mode = 1;
+	} else {
+		/* fout = fin / r */
+		r = freq_out / freq_in;
+		n = 0;
+		m = 0;
+		x = 0;
+		mode = 0;
+	}
+
+	if (r < 2 || r > 8 || x > 3 || m > 0xffff || n > 0xffff)
+		return -EINVAL;
+
+	if (dpll_div) {
+		dpll_div = 11 - dpll_div;
+		snd_soc_update_bits(codec, ADAU1373_PLL_CTRL6(pll_id),
+			ADAU1373_PLL_CTRL6_DPLL_BYPASS, 0);
+	} else {
+		snd_soc_update_bits(codec, ADAU1373_PLL_CTRL6(pll_id),
+			ADAU1373_PLL_CTRL6_DPLL_BYPASS,
+			ADAU1373_PLL_CTRL6_DPLL_BYPASS);
+	}
+
+	snd_soc_write(codec, ADAU1373_DPLL_CTRL(pll_id),
+		(source << 4) | dpll_div);
+	snd_soc_write(codec, ADAU1373_PLL_CTRL1(pll_id), (m >> 8) & 0xff);
+	snd_soc_write(codec, ADAU1373_PLL_CTRL2(pll_id), m & 0xff);
+	snd_soc_write(codec, ADAU1373_PLL_CTRL3(pll_id), (n >> 8) & 0xff);
+	snd_soc_write(codec, ADAU1373_PLL_CTRL4(pll_id), n & 0xff);
+	snd_soc_write(codec, ADAU1373_PLL_CTRL5(pll_id),
+		(r << 3) | (x << 1) | mode);
+
+	/* Set sysclk to pll_rate / 4 */
+	snd_soc_update_bits(codec, ADAU1373_CLK_SRC_DIV(pll_id), 0x3f, 0x09);
+
+	return 0;
+}
+
+static void adau1373_load_drc_settings(struct snd_soc_codec *codec,
+	unsigned int nr, uint8_t *drc)
+{
+	unsigned int i;
+
+	for (i = 0; i < ADAU1373_DRC_SIZE; ++i)
+		snd_soc_write(codec, ADAU1373_DRC(nr) + i, drc[i]);
+}
+
+static bool adau1373_valid_micbias(enum adau1373_micbias_voltage micbias)
+{
+	switch (micbias) {
+	case ADAU1373_MICBIAS_2_9V:
+	case ADAU1373_MICBIAS_2_2V:
+	case ADAU1373_MICBIAS_2_6V:
+	case ADAU1373_MICBIAS_1_8V:
+		return true;
+	default:
+		break;
+	}
+	return false;
+}
+
+static int adau1373_probe(struct snd_soc_codec *codec)
+{
+	struct adau1373_platform_data *pdata = codec->dev->platform_data;
+	bool lineout_differential = false;
+	unsigned int val;
+	int ret;
+	int i;
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+	if (ret) {
+		dev_err(codec->dev, "failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	codec->dapm.idle_bias_off = true;
+
+	if (pdata) {
+		if (pdata->num_drc > ARRAY_SIZE(pdata->drc_setting))
+			return -EINVAL;
+
+		if (!adau1373_valid_micbias(pdata->micbias1) ||
+			!adau1373_valid_micbias(pdata->micbias2))
+			return -EINVAL;
+
+		for (i = 0; i < pdata->num_drc; ++i) {
+			adau1373_load_drc_settings(codec, i,
+				pdata->drc_setting[i]);
+		}
+
+		snd_soc_add_controls(codec, adau1373_drc_controls,
+			pdata->num_drc);
+
+		val = 0;
+		for (i = 0; i < 4; ++i) {
+			if (pdata->input_differential[i])
+				val |= BIT(i);
+		}
+		snd_soc_write(codec, ADAU1373_INPUT_MODE, val);
+
+		val = 0;
+		if (pdata->lineout_differential)
+			val |= ADAU1373_OUTPUT_CTRL_LDIFF;
+		if (pdata->lineout_ground_sense)
+			val |= ADAU1373_OUTPUT_CTRL_LNFBEN;
+		snd_soc_write(codec, ADAU1373_OUTPUT_CTRL, val);
+
+		lineout_differential = pdata->lineout_differential;
+
+		snd_soc_write(codec, ADAU1373_EP_CTRL,
+			(pdata->micbias1 << ADAU1373_EP_CTRL_MICBIAS1_OFFSET) |
+			(pdata->micbias2 << ADAU1373_EP_CTRL_MICBIAS2_OFFSET));
+	}
+
+	if (!lineout_differential) {
+		snd_soc_add_controls(codec, adau1373_lineout2_controls,
+			ARRAY_SIZE(adau1373_lineout2_controls));
+	}
+
+	snd_soc_write(codec, ADAU1373_ADC_CTRL,
+	    ADAU1373_ADC_CTRL_RESET_FORCE | ADAU1373_ADC_CTRL_PEAK_DETECT);
+
+	return 0;
+}
+
+static int adau1373_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_update_bits(codec, ADAU1373_PWDN_CTRL3,
+			ADAU1373_PWDN_CTRL3_PWR_EN, ADAU1373_PWDN_CTRL3_PWR_EN);
+		break;
+	case SND_SOC_BIAS_OFF:
+		snd_soc_update_bits(codec, ADAU1373_PWDN_CTRL3,
+			ADAU1373_PWDN_CTRL3_PWR_EN, 0);
+		break;
+	}
+	codec->dapm.bias_level = level;
+	return 0;
+}
+
+static int adau1373_remove(struct snd_soc_codec *codec)
+{
+	adau1373_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int adau1373_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+	return adau1373_set_bias_level(codec, SND_SOC_BIAS_OFF);
+}
+
+static int adau1373_resume(struct snd_soc_codec *codec)
+{
+	adau1373_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_cache_sync(codec);
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver adau1373_codec_driver = {
+	.probe =	adau1373_probe,
+	.remove =	adau1373_remove,
+	.suspend =	adau1373_suspend,
+	.resume =	adau1373_resume,
+	.set_bias_level = adau1373_set_bias_level,
+	.reg_cache_size = ARRAY_SIZE(adau1373_default_regs),
+	.reg_cache_default = adau1373_default_regs,
+	.reg_word_size = sizeof(uint8_t),
+
+	.set_pll = adau1373_set_pll,
+
+	.controls = adau1373_controls,
+	.num_controls = ARRAY_SIZE(adau1373_controls),
+	.dapm_widgets = adau1373_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(adau1373_dapm_widgets),
+	.dapm_routes = adau1373_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(adau1373_dapm_routes),
+};
+
+static int __devinit adau1373_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	struct adau1373 *adau1373;
+	int ret;
+
+	adau1373 = kzalloc(sizeof(*adau1373), GFP_KERNEL);
+	if (!adau1373)
+		return -ENOMEM;
+
+	dev_set_drvdata(&client->dev, adau1373);
+
+	ret = snd_soc_register_codec(&client->dev, &adau1373_codec_driver,
+			adau1373_dai_driver, ARRAY_SIZE(adau1373_dai_driver));
+	if (ret < 0)
+		kfree(adau1373);
+
+	return ret;
+}
+
+static int __devexit adau1373_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	kfree(dev_get_drvdata(&client->dev));
+	return 0;
+}
+
+static const struct i2c_device_id adau1373_i2c_id[] = {
+	{ "adau1373", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, adau1373_i2c_id);
+
+static struct i2c_driver adau1373_i2c_driver = {
+	.driver = {
+		.name = "adau1373",
+		.owner = THIS_MODULE,
+	},
+	.probe = adau1373_i2c_probe,
+	.remove = __devexit_p(adau1373_i2c_remove),
+	.id_table = adau1373_i2c_id,
+};
+
+static int __init adau1373_init(void)
+{
+	return i2c_add_driver(&adau1373_i2c_driver);
+}
+module_init(adau1373_init);
+
+static void __exit adau1373_exit(void)
+{
+	i2c_del_driver(&adau1373_i2c_driver);
+}
+module_exit(adau1373_exit);
+
+MODULE_DESCRIPTION("ASoC ADAU1373 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1373.h b/sound/soc/codecs/adau1373.h
new file mode 100644
index 0000000..c6ab553
--- /dev/null
+++ b/sound/soc/codecs/adau1373.h
@@ -0,0 +1,29 @@
+#ifndef __ADAU1373_H__
+#define __ADAU1373_H__
+
+enum adau1373_pll_src {
+	ADAU1373_PLL_SRC_MCLK1 = 0,
+	ADAU1373_PLL_SRC_BCLK1 = 1,
+	ADAU1373_PLL_SRC_BCLK2 = 2,
+	ADAU1373_PLL_SRC_BCLK3 = 3,
+	ADAU1373_PLL_SRC_LRCLK1 = 4,
+	ADAU1373_PLL_SRC_LRCLK2 = 5,
+	ADAU1373_PLL_SRC_LRCLK3 = 6,
+	ADAU1373_PLL_SRC_GPIO1 = 7,
+	ADAU1373_PLL_SRC_GPIO2 = 8,
+	ADAU1373_PLL_SRC_GPIO3 = 9,
+	ADAU1373_PLL_SRC_GPIO4 = 10,
+	ADAU1373_PLL_SRC_MCLK2 = 11,
+};
+
+enum adau1373_pll {
+	ADAU1373_PLL1 = 0,
+	ADAU1373_PLL2 = 1,
+};
+
+enum adau1373_clk_src {
+	ADAU1373_CLK_SRC_PLL1 = 0,
+	ADAU1373_CLK_SRC_PLL2 = 1,
+};
+
+#endif
-- 
1.7.2.5


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

end of thread, other threads:[~2011-08-16 15:54 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-08-10  3:52 [PATCH 1/4] ASoC: DAPM: Allow multiple mixer sources to be routed via the same switch Lars-Peter Clausen
2011-08-10  3:52 ` Lars-Peter Clausen
2011-08-10  3:52 ` [PATCH 2/4] ASoC: Add ADAU1373 codec support Lars-Peter Clausen
2011-08-10  3:52   ` Lars-Peter Clausen
2011-08-10  6:11   ` Lars-Peter Clausen
2011-08-10  6:11     ` Lars-Peter Clausen
2011-08-11  9:27   ` Mark Brown
2011-08-11  9:27     ` Mark Brown
2011-08-11 10:11     ` Lars-Peter Clausen
2011-08-11 10:11       ` Lars-Peter Clausen
2011-08-11 13:16       ` Mark Brown
2011-08-11 13:16         ` Mark Brown
2011-08-12  0:20         ` Lars-Peter Clausen
2011-08-12  0:20           ` Lars-Peter Clausen
2011-08-12  1:20           ` Mark Brown
2011-08-12  1:20             ` Mark Brown
2011-08-13  3:25             ` Lars-Peter Clausen
2011-08-13  3:25               ` Lars-Peter Clausen
2011-08-14 10:24               ` Mark Brown
2011-08-14 10:24                 ` Mark Brown
2011-08-10  3:52 ` [PATCH 3/4] ASoC: Blackfin: ADAU1373 eval board support Lars-Peter Clausen
2011-08-10  3:52   ` Lars-Peter Clausen
2011-08-10  5:27   ` [alsa-devel] " Scott Jiang
2011-08-10  5:27     ` Scott Jiang
2011-08-10  6:09     ` [alsa-devel] " Lars-Peter Clausen
2011-08-10  6:09       ` Lars-Peter Clausen
2011-08-10  3:52 ` [PATCH 4/4] Blackfin: bf537: Stamp: Register ASoC EVAL-ADAU1373 board driver Lars-Peter Clausen
2011-08-10  3:52   ` Lars-Peter Clausen
2011-08-11  9:31 ` [PATCH 1/4] ASoC: DAPM: Allow multiple mixer sources to be routed via the same switch Mark Brown
2011-08-11  9:31   ` Mark Brown
2011-08-11  9:59   ` [alsa-devel] " Lars-Peter Clausen
2011-08-11  9:59     ` Lars-Peter Clausen
2011-08-12  1:22     ` [alsa-devel] " Mark Brown
2011-08-15 18:15 Lars-Peter Clausen
2011-08-15 18:15 ` [PATCH 2/4] ASoC: Add ADAU1373 codec support Lars-Peter Clausen
2011-08-16 15:54   ` Mark Brown
2011-08-16 15:54     ` 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.