All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/5] ASoC: Add ADAU1X61 and ADAU1X81 CODECs common code
@ 2013-08-28 15:20 Lars-Peter Clausen
  2013-08-28 15:20 ` [PATCH 2/5] ASoC: Add ADAU1361/ADAU1761 audio CODEC support Lars-Peter Clausen
                   ` (4 more replies)
  0 siblings, 5 replies; 13+ messages in thread
From: Lars-Peter Clausen @ 2013-08-28 15:20 UTC (permalink / raw)
  To: Mark Brown, Liam Girdwood; +Cc: alsa-devel, Lars-Peter Clausen

The ADAU1X61 and ADAU1X81 are very similar in the digital domain, but are quite
different in the analog domain. This patch adds support for the common parts of
the ADAU1X61 and ADAU1X81 CODECs.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 include/linux/platform_data/adau17x1.h |  23 +
 sound/soc/codecs/Kconfig               |   4 +
 sound/soc/codecs/Makefile              |   2 +
 sound/soc/codecs/adau17x1.c            | 895 +++++++++++++++++++++++++++++++++
 sound/soc/codecs/adau17x1.h            | 148 ++++++
 5 files changed, 1072 insertions(+)
 create mode 100644 include/linux/platform_data/adau17x1.h
 create mode 100644 sound/soc/codecs/adau17x1.c
 create mode 100644 sound/soc/codecs/adau17x1.h

diff --git a/include/linux/platform_data/adau17x1.h b/include/linux/platform_data/adau17x1.h
new file mode 100644
index 0000000..fdf419a
--- /dev/null
+++ b/include/linux/platform_data/adau17x1.h
@@ -0,0 +1,23 @@
+/*
+ * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961/ADAU1781/ADAU1781 codecs
+ *
+ * Copyright 2011-2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __LINUX_PLATFORM_DATA_ADAU17X1_H__
+#define __LINUX_PLATFORM_DATA_ADAU17X1_H__
+
+/**
+ * enum adau17x1_micbias_voltage - Microphone bias voltage
+ * @ADAU17X1_MICBIAS_0_90_AVDD: 0.9 * AVDD
+ * @ADAU17X1_MICBIAS_0_65_AVDD: 0.65 * AVDD
+ */
+enum adau17x1_micbias_voltage {
+	ADAU17X1_MICBIAS_0_90_AVDD = 0,
+	ADAU17X1_MICBIAS_0_65_AVDD = 1,
+};
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 15106c0..56137d4 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -193,6 +193,10 @@ config SND_SOC_ADAU1701
 config SND_SOC_ADAU1373
 	tristate
 
+config SND_SOC_ADAU17X1
+	select SND_SOC_SIGMADSP
+	tristate
+
 config SND_SOC_ADAV80X
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index bc12676..b4ecf6c 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -7,6 +7,7 @@ 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-adau17x1-objs := adau17x1.o
 snd-soc-adav80x-objs := adav80x.o
 snd-soc-ads117x-objs := ads117x.o
 snd-soc-ak4104-objs := ak4104.o
@@ -138,6 +139,7 @@ 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_ADAU17X1)	+= snd-soc-adau17x1.o
 obj-$(CONFIG_SND_SOC_ADAV80X)  += snd-soc-adav80x.o
 obj-$(CONFIG_SND_SOC_ADS117X)	+= snd-soc-ads117x.o
 obj-$(CONFIG_SND_SOC_AK4104)	+= snd-soc-ak4104.o
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
new file mode 100644
index 0000000..b7d678d
--- /dev/null
+++ b/sound/soc/codecs/adau17x1.c
@@ -0,0 +1,895 @@
+/*
+ * Common code for ADAU1X61 and ADAU1X81 codecs
+ *
+ * Copyright 2011-2013 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/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/gcd.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
+#include "sigmadsp.h"
+#include "adau17x1.h"
+
+static const char * const adau17x1_capture_mixer_boost_text[] = {
+	"Normal operation", "Boost Level 1", "Boost Level 2", "Boost Level 3",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau17x1_capture_boost_enum,
+	ADAU17X1_REC_POWER_MGMT, 5, adau17x1_capture_mixer_boost_text);
+
+static const char * const adau17x1_mic_bias_mode_text[] = {
+	"Normal operation", "High performance",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau17x1_mic_bias_mode_enum,
+	ADAU17X1_MICBIAS, 3, adau17x1_mic_bias_mode_text);
+
+static const char * const adau17x1_mono_stereo_text[] = {
+	"Stereo",
+	"Mono Left Channel (L+R)",
+	"Mono Right Channel (L+R)",
+	"Mono (L+R)",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau17x1_dac_mode_enum,
+	ADAU17X1_DAC_CONTROL0, 6, adau17x1_mono_stereo_text);
+
+static const DECLARE_TLV_DB_MINMAX(adau17x1_digital_tlv, -9563, 0);
+
+static const struct snd_kcontrol_new adau17x1_controls[] = {
+	SOC_DOUBLE_R_TLV("Digital Capture Volume",
+		ADAU17X1_LEFT_INPUT_DIGITAL_VOL,
+		ADAU17X1_RIGHT_INPUT_DIGITAL_VOL,
+		0, 0xff, 1, adau17x1_digital_tlv),
+	SOC_DOUBLE_R_TLV("Digital Playback Volume", ADAU17X1_DAC_CONTROL1,
+		ADAU17X1_DAC_CONTROL2, 0, 0xff, 1, adau17x1_digital_tlv),
+
+	SOC_SINGLE("ADC High Pass Filter Switch", ADAU17X1_ADC_CONTROL,
+		5, 1, 0),
+	SOC_SINGLE("Playback De-emphasis Switch", ADAU17X1_DAC_CONTROL0,
+		2, 1, 0),
+
+	SOC_ENUM("Capture Boost", adau17x1_capture_boost_enum),
+
+	SOC_ENUM("Mic Bias Mode", adau17x1_mic_bias_mode_enum),
+
+	SOC_ENUM("DAC Mono Stereo", adau17x1_dac_mode_enum),
+};
+
+static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
+	int ret;
+
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		adau->pll_regs[5] = 1;
+	} else {
+		adau->pll_regs[5] = 0;
+		/* Bypass the PLL when disabled, otherwise registers will become
+		 * inaccessible. */
+		regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+			ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL, 0);
+	}
+
+	/* The PLL register is 6 bytes long and can only be written at once. */
+	ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
+			adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
+
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		mdelay(5);
+		regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+			ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL,
+			ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL);
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget adau17x1_dapm_widgets[] = {
+	SND_SOC_DAPM_SUPPLY_S("PLL", 3, SND_SOC_NOPM, 0, 0, adau17x1_pll_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("AIFCLK", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("MICBIAS", ADAU17X1_MICBIAS, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("Left Playback Enable", ADAU17X1_PLAY_POWER_MGMT,
+		0, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Right Playback Enable", ADAU17X1_PLAY_POWER_MGMT,
+		1, 0, NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("Left Dec Filter Enable", ADAU17X1_ADC_CONTROL,
+		0, 0, NULL,  0),
+	SND_SOC_DAPM_SUPPLY("Right Dec Filter Enable", ADAU17X1_ADC_CONTROL,
+		1, 0, NULL,  0),
+
+	SND_SOC_DAPM_DAC("Left DAC", NULL, ADAU17X1_DAC_CONTROL0, 0, 0),
+	SND_SOC_DAPM_DAC("Right DAC", NULL, ADAU17X1_DAC_CONTROL0, 1, 0),
+	SND_SOC_DAPM_ADC("Left ADC", NULL, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_ADC("Right ADC", NULL, SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_AIF_OUT("AIFOUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("AIFIN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route adau17x1_dapm_routes[] = {
+	{ "Left ADC", NULL, "SYSCLK" },
+	{ "Right ADC", NULL, "SYSCLK" },
+	{ "Left DAC", NULL, "SYSCLK" },
+	{ "Right DAC", NULL, "SYSCLK" },
+	{ "AIFOUT", NULL, "SYSCLK" },
+	{ "AIFIN", NULL, "SYSCLK" },
+
+	{ "Left ADC", NULL, "Left Dec Filter Enable" },
+	{ "Right ADC", NULL, "Right Dec Filter Enable" },
+
+	{ "AIFOUT", NULL, "AIFCLK" },
+	{ "AIFIN", NULL, "AIFCLK" },
+};
+
+static const struct snd_soc_dapm_route adau17x1_dapm_pll_route = {
+	"SYSCLK", NULL, "PLL",
+};
+
+static const char * const adau17x1_dac_mux_text[] = {
+	"AIFIN",
+	"DSP",
+};
+
+int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	struct snd_soc_dapm_update update;
+	unsigned int val, change;
+
+	if (ucontrol->value.enumerated.item[0] >= e->max)
+		return -EINVAL;
+
+	switch (ucontrol->value.enumerated.item[0]) {
+	case 0:
+		switch (e->reg) {
+		case ADAU17X1_SERIAL_INPUT_ROUTE:
+			val = (adau->tdm_dac_slot * 2) + 1;
+			adau->dsp_playback_bypass = true;
+			break;
+		case ADAU17X1_SERIAL_OUTPUT_ROUTE:
+			val = (adau->tdm_adc_slot * 2) + 1;
+			adau->dsp_capture_bypass = true;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		val = 0;
+
+		switch (e->reg) {
+		case ADAU17X1_SERIAL_INPUT_ROUTE:
+			adau->dsp_playback_bypass = false;
+			break;
+		case ADAU17X1_SERIAL_OUTPUT_ROUTE:
+			adau->dsp_capture_bypass = false;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	}
+
+	change = snd_soc_test_bits(codec, e->reg, 0xff, val);
+	if (change) {
+		update.kcontrol = kcontrol;
+		update.reg = e->reg;
+		update.mask = 0xff;
+		update.val = val;
+
+		snd_soc_dapm_mux_update_power(&codec->dapm, kcontrol,
+				ucontrol->value.enumerated.item[0], e, &update);
+	}
+
+	return change;
+}
+EXPORT_SYMBOL_GPL(adau17x1_dsp_mux_enum_put);
+
+int adau17x1_dsp_mux_enum_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned int val;
+
+	val = snd_soc_read(codec, e->reg);
+	ucontrol->value.enumerated.item[0] = val == 0;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_dsp_mux_enum_get);
+
+static const SOC_ENUM_SINGLE_DECL(adau17x1_dac_mux_enum,
+	ADAU17X1_SERIAL_INPUT_ROUTE, 0,
+	adau17x1_dac_mux_text);
+
+static const struct snd_kcontrol_new adau17x1_dac_mux =
+	ADAU17X1_DSP_MUX_ENUM("DAC Playback Mux", adau17x1_dac_mux_enum);
+
+static const struct snd_soc_dapm_widget adau17x1_dsp_dapm_widgets[] = {
+	SND_SOC_DAPM_PGA("DSP", ADAU17X1_DSP_RUN, 0, 0, NULL, 0),
+	SND_SOC_DAPM_SIGGEN("DSP Siggen"),
+
+	SND_SOC_DAPM_VIRT_MUX("DAC Playback Mux", SND_SOC_NOPM, 0, 0,
+		&adau17x1_dac_mux),
+
+};
+
+static const struct snd_soc_dapm_route adau17x1_dsp_dapm_routes[] = {
+	{ "DAC Playback Mux", "DSP", "DSP" },
+	{ "DAC Playback Mux", "AIFIN", "AIFIN" },
+
+	{ "Left DAC", NULL, "DAC Playback Mux" },
+	{ "Right DAC", NULL, "DAC Playback Mux" },
+
+	{ "AIFOUT Capture Mux", "DSP", "DSP" },
+
+	{ "AIFOUT", NULL, "AIFOUT Capture Mux" },
+
+	{ "DSP", NULL, "DSP Siggen" },
+};
+
+static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = {
+	{ "Left DAC", NULL, "AIFIN" },
+	{ "Right DAC", NULL, "AIFIN" },
+};
+
+static void adau17x1_check_aifclk(struct snd_soc_codec *codec)
+{
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+	/* If we are in master mode we need to generate bit- and frameclock,
+	 * regardless of whether there is an active path or not */
+	if (codec->active && adau->master)
+		snd_soc_dapm_force_enable_pin(&codec->dapm, "AIFCLK");
+	else
+		snd_soc_dapm_disable_pin(&codec->dapm, "AIFCLK");
+	snd_soc_dapm_sync(&codec->dapm);
+}
+
+bool adau17x1_has_dsp(struct adau *adau)
+{
+	switch (adau->type) {
+	case ADAU1761:
+	case ADAU1381:
+	case ADAU1781:
+		return true;
+	default:
+		return false;
+	}
+}
+EXPORT_SYMBOL_GPL(adau17x1_has_dsp);
+
+static int adau17x1_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 adau *adau = snd_soc_codec_get_drvdata(codec);
+	unsigned int val, div, dsp_div;
+	unsigned int freq;
+
+	if (adau->clk_src == ADAU17X1_CLK_SRC_PLL)
+		freq = adau->pll_freq;
+	else
+		freq = adau->sysclk / adau->sysclk_div;
+
+	if (freq % params_rate(params) != 0)
+		return -EINVAL;
+
+	switch (freq / params_rate(params)) {
+	case 1024: /* fs */
+		div = 0;
+		dsp_div = 1;
+		break;
+	case 6144: /* fs / 6 */
+		div = 1;
+		dsp_div = 6;
+		break;
+	case 4096: /* fs / 4 */
+		div = 2;
+		dsp_div = 5;
+		break;
+	case 3072: /* fs / 3 */
+		div = 3;
+		dsp_div = 4;
+		break;
+	case 2048: /* fs / 2 */
+		div = 4;
+		dsp_div = 3;
+		break;
+	case 1536: /* fs / 1.5 */
+		div = 5;
+		dsp_div = 2;
+		break;
+	case 512: /* fs / 0.5 */
+		div = 6;
+		dsp_div = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0, 7, div);
+	if (adau17x1_has_dsp(adau)) {
+		regmap_write(adau->regmap, ADAU17X1_SERIAL_SAMPLING_RATE, div);
+		regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div);
+	}
+
+	adau17x1_check_aifclk(codec);
+
+	if (adau->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
+		return 0;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		val = ADAU17X1_SERIAL_PORT1_DELAY16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		val = ADAU17X1_SERIAL_PORT1_DELAY8;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		val = ADAU17X1_SERIAL_PORT1_DELAY0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
+			ADAU17X1_SERIAL_PORT1_DELAY_MASK, val);
+}
+
+static int adau17x1_dai_startup(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	adau17x1_check_aifclk(dai->codec);
+
+	return 0;
+}
+
+static void adau17x1_dai_shutdown(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	adau17x1_check_aifclk(dai->codec);
+}
+
+static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
+	int source, unsigned int freq_in, unsigned int freq_out)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
+	unsigned int div;
+	unsigned int r, n, m, i, j;
+
+	if (freq_in < 8000000 || freq_in > 27000000)
+		return -EINVAL;
+
+	if (!freq_out) {
+		r = 0;
+		n = 0;
+		m = 0;
+		div = 0;
+	} else {
+		if (freq_out % freq_in != 0) {
+			div = DIV_ROUND_UP(freq_in, 13500000);
+			freq_in /= div;
+			r = freq_out / freq_in;
+			i = freq_out % freq_in;
+			j = gcd(i, freq_in);
+			n = i / j;
+			m = freq_in / j;
+			div--;
+		} else {
+			r = freq_out / freq_in;
+			n = 0;
+			m = 0;
+			div = 0;
+		}
+		if (n > 0xffff || m > 0xffff || div > 3 || r > 8 || r < 2)
+			return -EINVAL;
+	}
+
+	adau->pll_regs[0] = m >> 8;
+	adau->pll_regs[1] = m & 0xff;
+	adau->pll_regs[2] = n >> 8;
+	adau->pll_regs[3] = n & 0xff;
+	adau->pll_regs[4] = (r << 3) | (div << 1);
+	if (m != 0)
+		adau->pll_regs[4] |= 1; /* Fractional mode */
+	adau->pll_regs[5] = 0;
+
+	adau->pll_freq = freq_out;
+
+	return 0;
+}
+
+static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+	struct snd_soc_dapm_context *dapm = &dai->codec->dapm;
+
+	switch (clk_id) {
+	case ADAU17X1_CLK_SRC_MCLK:
+	case ADAU17X1_CLK_SRC_PLL:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	adau->sysclk = freq;
+
+	if (adau->clk_src != clk_id) {
+		if (clk_id == ADAU17X1_CLK_SRC_PLL) {
+			snd_soc_dapm_add_routes(dapm,
+				&adau17x1_dapm_pll_route, 1);
+		} else {
+			snd_soc_dapm_del_routes(dapm,
+				&adau17x1_dapm_pll_route, 1);
+		}
+	}
+
+	adau->clk_src = clk_id;
+
+	return 0;
+}
+
+static int adau17x1_set_dai_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+	struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+
+	if (div < 0 || div > 4)
+		return -EINVAL;
+
+	adau->sysclk_div = div;
+
+	return regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+		ADAU17X1_CLOCK_CONTROL_INFREQ_MASK, (div - 1) << 1);
+}
+
+static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
+		unsigned int fmt)
+{
+	struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+	unsigned int ctrl0, ctrl1;
+	int lrclk_pol;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		ctrl0 = ADAU17X1_SERIAL_PORT0_MASTER;
+		adau->master = true;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		ctrl0 = 0;
+		adau->master = false;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		lrclk_pol = 0;
+		ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY1;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+	case SND_SOC_DAIFMT_RIGHT_J:
+		lrclk_pol = 1;
+		ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY0;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		lrclk_pol = 1;
+		ctrl0 |= ADAU17X1_SERIAL_PORT0_PULSE_MODE;
+		ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY1;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		lrclk_pol = 1;
+		ctrl0 |= ADAU17X1_SERIAL_PORT0_PULSE_MODE;
+		ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		ctrl0 |= ADAU17X1_SERIAL_PORT0_BCLK_POL;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		lrclk_pol = !lrclk_pol;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		ctrl0 |= ADAU17X1_SERIAL_PORT0_BCLK_POL;
+		lrclk_pol = !lrclk_pol;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (lrclk_pol)
+		ctrl0 |= ADAU17X1_SERIAL_PORT0_LRCLK_POL;
+
+	regmap_write(adau->regmap, ADAU17X1_SERIAL_PORT0, ctrl0);
+	regmap_write(adau->regmap, ADAU17X1_SERIAL_PORT1, ctrl1);
+
+	adau->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+	return 0;
+}
+
+static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
+	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+	struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+	unsigned int ser_ctrl0, ser_ctrl1;
+	unsigned int conv_ctrl0, conv_ctrl1;
+
+	/* I2S mode */
+	if (slots == 0) {
+		slots = 2;
+		rx_mask = 3;
+		tx_mask = 3;
+		slot_width = 32;
+	}
+
+	switch (slots) {
+	case 2:
+		ser_ctrl0 = ADUA_SERIAL_PORT0_STEREO;
+		break;
+	case 4:
+		ser_ctrl0 = ADUA_SERIAL_PORT0_TDM4;
+		break;
+	case 8:
+		if (adau->type == ADAU1361)
+			return -EINVAL;
+
+		ser_ctrl0 = ADUA_SERIAL_PORT0_TDM8;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (slot_width * slots) {
+	case 32:
+		if (adau->type == ADAU1761)
+			return -EINVAL;
+
+		ser_ctrl1 = ADUA_SERIAL_PORT1_BCLK32;
+		break;
+	case 64:
+		ser_ctrl1 = ADUA_SERIAL_PORT1_BCLK64;
+		break;
+	case 48:
+		ser_ctrl1 = ADUA_SERIAL_PORT1_BCLK48;
+		break;
+	case 128:
+		ser_ctrl1 = ADUA_SERIAL_PORT1_BCLK128;
+		break;
+	case 256:
+		if (adau->type == ADAU1361)
+			return -EINVAL;
+
+		ser_ctrl1 = ADUA_SERIAL_PORT1_BCLK256;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (rx_mask) {
+	case 0x03:
+		conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(1);
+		adau->tdm_adc_slot = 0;
+		break;
+	case 0x0c:
+		conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(2);
+		adau->tdm_adc_slot = 1;
+		break;
+	case 0x30:
+		conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(3);
+		adau->tdm_adc_slot = 2;
+		break;
+	case 0xc0:
+		conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(4);
+		adau->tdm_adc_slot = 3;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (tx_mask) {
+	case 0x03:
+		conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(1);
+		adau->tdm_dac_slot = 0;
+		break;
+	case 0x0c:
+		conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(2);
+		adau->tdm_dac_slot = 1;
+		break;
+	case 0x30:
+		conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(3);
+		adau->tdm_dac_slot = 2;
+		break;
+	case 0xc0:
+		conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(4);
+		adau->tdm_dac_slot = 3;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
+		ADAU17X1_CONVERTER0_DAC_PAIR_MASK, conv_ctrl0);
+	regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER1,
+		ADAU17X1_CONVERTER1_ADC_PAIR_MASK, conv_ctrl1);
+	regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT0,
+		ADAU17X1_SERIAL_PORT0_TDM_MASK, ser_ctrl0);
+	regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
+		ADAU17X1_SERIAL_PORT1_BCLK_MASK, ser_ctrl1);
+
+	if (adau17x1_has_dsp(adau)) {
+		if (adau->dsp_playback_bypass) {
+			regmap_write(adau->regmap, ADAU17X1_SERIAL_INPUT_ROUTE,
+				(adau->tdm_dac_slot * 2) + 1);
+		}
+		if (adau->dsp_capture_bypass) {
+			regmap_write(adau->regmap, ADAU17X1_SERIAL_OUTPUT_ROUTE,
+				(adau->tdm_adc_slot * 2) + 1);
+		}
+	}
+
+	return 0;
+}
+
+const struct snd_soc_dai_ops adau17x1_dai_ops = {
+	.hw_params	= adau17x1_hw_params,
+	.set_sysclk	= adau17x1_set_dai_sysclk,
+	.set_fmt	= adau17x1_set_dai_fmt,
+	.set_pll	= adau17x1_set_dai_pll,
+	.set_clkdiv	= adau17x1_set_dai_clkdiv,
+	.set_tdm_slot	= adau17x1_set_dai_tdm_slot,
+	.startup	= adau17x1_dai_startup,
+	.shutdown	= adau17x1_dai_shutdown,
+};
+EXPORT_SYMBOL_GPL(adau17x1_dai_ops);
+
+int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
+	enum adau17x1_micbias_voltage micbias)
+{
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+	switch (micbias) {
+	case ADAU17X1_MICBIAS_0_90_AVDD:
+	case ADAU17X1_MICBIAS_0_65_AVDD:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	regmap_write(adau->regmap, ADAU17X1_MICBIAS, micbias << 2);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_set_micbias_voltage);
+
+bool adau17x1_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case ADAU17X1_CLOCK_CONTROL:
+	case ADAU17X1_PLL_CONTROL:
+	case ADAU17X1_REC_POWER_MGMT:
+	case ADAU17X1_MICBIAS:
+	case ADAU17X1_SERIAL_PORT0:
+	case ADAU17X1_SERIAL_PORT1:
+	case ADAU17X1_CONVERTER0:
+	case ADAU17X1_CONVERTER1:
+	case ADAU17X1_LEFT_INPUT_DIGITAL_VOL:
+	case ADAU17X1_RIGHT_INPUT_DIGITAL_VOL:
+	case ADAU17X1_ADC_CONTROL:
+	case ADAU17X1_PLAY_POWER_MGMT:
+	case ADAU17X1_DAC_CONTROL0:
+	case ADAU17X1_DAC_CONTROL1:
+	case ADAU17X1_DAC_CONTROL2:
+	case ADAU17X1_SERIAL_PORT_PAD:
+	case ADAU17X1_CONTROL_PORT_PAD0:
+	case ADAU17X1_CONTROL_PORT_PAD1:
+	case ADAU17X1_DSP_SAMPLING_RATE:
+	case ADAU17X1_SERIAL_INPUT_ROUTE:
+	case ADAU17X1_SERIAL_OUTPUT_ROUTE:
+	case ADAU17X1_DSP_ENABLE:
+	case ADAU17X1_DSP_RUN:
+	case ADAU17X1_SERIAL_SAMPLING_RATE:
+		return true;
+	default:
+		break;
+	}
+	return false;
+}
+EXPORT_SYMBOL_GPL(adau17x1_readable_register);
+
+bool adau17x1_volatile_register(struct device *dev, unsigned int reg)
+{
+	/* SigmaDSP parameter and program memory */
+	if (reg < 0x4000)
+		return true;
+
+	switch (reg) {
+	/* The PLL register is 6 bytes long */
+	case ADAU17X1_PLL_CONTROL:
+	case ADAU17X1_PLL_CONTROL + 1:
+	case ADAU17X1_PLL_CONTROL + 2:
+	case ADAU17X1_PLL_CONTROL + 3:
+	case ADAU17X1_PLL_CONTROL + 4:
+	case ADAU17X1_PLL_CONTROL + 5:
+		return true;
+	default:
+		break;
+	}
+
+	return false;
+}
+EXPORT_SYMBOL_GPL(adau17x1_volatile_register);
+
+int adau17x1_load_firmware(struct adau *adau, struct device *dev,
+	const char *firmware)
+{
+	int ret;
+	int dspsr;
+
+	ret = regmap_read(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, &dspsr);
+	if (ret)
+		return ret;
+
+	regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 1);
+	regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, 0xf);
+
+	ret = process_sigma_firmware_regmap(dev, adau->regmap, firmware);
+	if (ret) {
+		regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 0);
+		return ret;
+	}
+	regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dspsr);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_load_firmware);
+
+int adau17x1_probe(struct snd_soc_codec *codec)
+{
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP);
+	if (ret)
+		return ret;
+
+	codec->driver->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	ret = snd_soc_add_codec_controls(codec, adau17x1_controls,
+		ARRAY_SIZE(adau17x1_controls));
+	if (ret)
+		return ret;
+	ret = snd_soc_dapm_new_controls(&codec->dapm, adau17x1_dapm_widgets,
+		ARRAY_SIZE(adau17x1_dapm_widgets));
+	if (ret)
+		return ret;
+
+	if (adau17x1_has_dsp(adau)) {
+		ret = snd_soc_dapm_new_controls(&codec->dapm,
+			adau17x1_dsp_dapm_widgets,
+			ARRAY_SIZE(adau17x1_dsp_dapm_widgets));
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(adau17x1_probe);
+
+int adau17x1_add_routes(struct snd_soc_codec *codec)
+{
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	ret = snd_soc_dapm_add_routes(&codec->dapm, adau17x1_dapm_routes,
+		ARRAY_SIZE(adau17x1_dapm_routes));
+	if (ret)
+		return ret;
+
+	if (adau17x1_has_dsp(adau)) {
+		ret = snd_soc_dapm_add_routes(&codec->dapm,
+			adau17x1_dsp_dapm_routes,
+			ARRAY_SIZE(adau17x1_dsp_dapm_routes));
+	} else {
+		ret = snd_soc_dapm_add_routes(&codec->dapm,
+			adau17x1_no_dsp_dapm_routes,
+			ARRAY_SIZE(adau17x1_no_dsp_dapm_routes));
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(adau17x1_add_routes);
+
+#if IS_ENABLED(CONFIG_SPI_MASTER)
+static void adau17x1_spi_mode(struct device *dev)
+{
+	/* To get the device into SPI mode CLATCH has to be pulled low three
+	 * times. Do this by issuing three dummy reads. */
+	spi_w8r8(to_spi_device(dev), 0x00);
+	spi_w8r8(to_spi_device(dev), 0x00);
+	spi_w8r8(to_spi_device(dev), 0x00);
+}
+#else
+static inline void adau17x1_spi_mode(struct device *dev) {}
+#endif
+
+int adau17x1_suspend(struct snd_soc_codec *codec)
+{
+	codec->driver->set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_suspend);
+
+int adau17x1_resume(struct snd_soc_codec *codec)
+{
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+	if (adau->spi_mode)
+		adau17x1_spi_mode(codec->dev);
+
+	codec->driver->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	regcache_sync(adau->regmap);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_resume);
+
+int adau17x1_bus_probe(struct device *dev, struct regmap *regmap,
+	enum adau17x1_type type, bool spi_mode)
+{
+	struct adau *adau;
+
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	adau = devm_kzalloc(dev, sizeof(*adau), GFP_KERNEL);
+	if (!adau)
+		return -ENOMEM;
+
+	adau->regmap = regmap;
+	adau->spi_mode = spi_mode;
+	adau->type = type;
+	adau->sysclk_div = 1;
+
+	dev_set_drvdata(dev, adau);
+
+	if (spi_mode)
+		adau17x1_spi_mode(dev);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_bus_probe);
+
+MODULE_DESCRIPTION("ASoC ADAU1X61/ADAU1X81 common code");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h
new file mode 100644
index 0000000..d49442e
--- /dev/null
+++ b/sound/soc/codecs/adau17x1.h
@@ -0,0 +1,148 @@
+#ifndef __ADAU17X1_H__
+#define __ADAU17X1_H__
+
+#include <linux/regmap.h>
+#include <linux/platform_data/adau17x1.h>
+
+enum adau17x1_type {
+	ADAU1361,
+	ADAU1761,
+	ADAU1381,
+	ADAU1781,
+};
+
+enum adau17x1_pll {
+	ADAU17X1_PLL,
+};
+
+enum adau17x1_pll_src {
+	ADAU17X1_PLL_SRC_MCLK,
+};
+
+enum adau17x1_clk_src {
+	ADAU17X1_CLK_SRC_MCLK,
+	ADAU17X1_CLK_SRC_PLL,
+};
+
+struct adau {
+	unsigned int sysclk;
+	unsigned int sysclk_div;
+	unsigned int pll_freq;
+
+	enum adau17x1_clk_src clk_src;
+	enum adau17x1_type type;
+	bool spi_mode;
+
+	unsigned int dai_fmt;
+
+	uint8_t pll_regs[6];
+
+	bool master;
+
+	unsigned int tdm_dac_slot;
+	unsigned int tdm_adc_slot;
+
+	bool dsp_playback_bypass;
+	bool dsp_capture_bypass;
+
+	struct regmap *regmap;
+};
+
+int adau17x1_probe(struct snd_soc_codec *codec);
+int adau17x1_add_routes(struct snd_soc_codec *codec);
+int adau17x1_bus_probe(struct device *dev,
+	struct regmap *regmap, enum adau17x1_type type,
+	bool spi_mode);
+int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
+	enum adau17x1_micbias_voltage micbias);
+bool adau17x1_readable_register(struct device *dev, unsigned int reg);
+bool adau17x1_volatile_register(struct device *dev, unsigned int reg);
+int adau17x1_suspend(struct snd_soc_codec *codec);
+int adau17x1_resume(struct snd_soc_codec *codec);
+
+extern const struct snd_soc_dai_ops adau17x1_dai_ops;
+
+int adau17x1_load_firmware(struct adau *adau, struct device *dev,
+	const char *firmware);
+bool adau17x1_has_dsp(struct adau *adau);
+int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
+int adau17x1_dsp_mux_enum_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
+
+#define ADAU17X1_DSP_MUX_ENUM(xname, xenum) \
+	SOC_DAPM_ENUM_EXT(xname, xenum, \
+		adau17x1_dsp_mux_enum_get, adau17x1_dsp_mux_enum_put)
+
+#define DECLARE_ADAU17X1_AIFOUT_MUX(name, text) \
+static const char * const adau17x1_ ## name ## _aifout_text[] = { \
+	text, \
+	"DSP", \
+}; \
+static const SOC_ENUM_SINGLE_DECL(adau17x1_ ## name ## _aifout_mux_enum, \
+	ADAU17X1_SERIAL_OUTPUT_ROUTE, 0, adau17x1_ ## name ## _aifout_text); \
+static const struct snd_kcontrol_new adau17x1_ ## name ## _aifout_mux =  \
+	ADAU17X1_DSP_MUX_ENUM("AIFOUT Capture Mux", \
+	adau17x1_ ## name ## _aifout_mux_enum); \
+static const struct snd_soc_dapm_widget adau17x1_ ## name ## _dsp_widget = \
+	SND_SOC_DAPM_VIRT_MUX("AIFOUT Capture Mux", SND_SOC_NOPM, 0, 0, \
+		&adau17x1_ ## name ## _aifout_mux) \
+
+#define ADAU17X1_CLOCK_CONTROL			0x4000
+#define ADAU17X1_PLL_CONTROL			0x4002
+#define ADAU17X1_REC_POWER_MGMT			0x4009
+#define ADAU17X1_MICBIAS			0x4010
+#define ADAU17X1_SERIAL_PORT0			0x4015
+#define ADAU17X1_SERIAL_PORT1			0x4016
+#define ADAU17X1_CONVERTER0			0x4017
+#define ADAU17X1_CONVERTER1			0x4018
+#define ADAU17X1_LEFT_INPUT_DIGITAL_VOL		0x401a
+#define ADAU17X1_RIGHT_INPUT_DIGITAL_VOL	0x401b
+#define ADAU17X1_ADC_CONTROL			0x4019
+#define ADAU17X1_PLAY_POWER_MGMT		0x4029
+#define ADAU17X1_DAC_CONTROL0			0x402a
+#define ADAU17X1_DAC_CONTROL1			0x402b
+#define ADAU17X1_DAC_CONTROL2			0x402c
+#define ADAU17X1_SERIAL_PORT_PAD		0x402d
+#define ADAU17X1_CONTROL_PORT_PAD0		0x402f
+#define ADAU17X1_CONTROL_PORT_PAD1		0x4030
+#define ADAU17X1_DSP_SAMPLING_RATE		0x40eb
+#define ADAU17X1_SERIAL_INPUT_ROUTE		0x40f2
+#define ADAU17X1_SERIAL_OUTPUT_ROUTE		0x40f3
+#define ADAU17X1_DSP_ENABLE			0x40f5
+#define ADAU17X1_DSP_RUN			0x40f6
+#define ADAU17X1_SERIAL_SAMPLING_RATE		0x40f8
+
+#define ADAU17X1_SERIAL_PORT0_BCLK_POL		BIT(4)
+#define ADAU17X1_SERIAL_PORT0_LRCLK_POL		BIT(3)
+#define ADAU17X1_SERIAL_PORT0_MASTER		BIT(0)
+
+#define ADAU17X1_SERIAL_PORT1_DELAY1		0x00
+#define ADAU17X1_SERIAL_PORT1_DELAY0		0x01
+#define ADAU17X1_SERIAL_PORT1_DELAY8		0x02
+#define ADAU17X1_SERIAL_PORT1_DELAY16		0x03
+#define ADAU17X1_SERIAL_PORT1_DELAY_MASK	0x03
+
+#define ADAU17X1_CLOCK_CONTROL_INFREQ_MASK	0x6
+#define ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL	BIT(3)
+#define ADAU17X1_CLOCK_CONTROL_SYSCLK_EN	BIT(0)
+
+#define ADUA_SERIAL_PORT1_BCLK32		(0x0 << 5)
+#define ADUA_SERIAL_PORT1_BCLK48		(0x1 << 5)
+#define ADUA_SERIAL_PORT1_BCLK64		(0x2 << 5)
+#define ADUA_SERIAL_PORT1_BCLK128		(0x3 << 5)
+#define ADUA_SERIAL_PORT1_BCLK256		(0x4 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK_MASK		(0x7 << 5)
+
+#define ADUA_SERIAL_PORT0_STEREO		(0x0 << 1)
+#define ADUA_SERIAL_PORT0_TDM4			(0x1 << 1)
+#define ADUA_SERIAL_PORT0_TDM8			(0x2 << 1)
+#define ADAU17X1_SERIAL_PORT0_TDM_MASK		(0x3 << 1)
+#define ADAU17X1_SERIAL_PORT0_PULSE_MODE	BIT(5)
+
+#define ADAU17X1_CONVERTER0_DAC_PAIR(x)		(((x) - 1) << 5)
+#define ADAU17X1_CONVERTER0_DAC_PAIR_MASK	(0x3 << 5)
+#define ADAU17X1_CONVERTER1_ADC_PAIR(x)		((x) - 1)
+#define ADAU17X1_CONVERTER1_ADC_PAIR_MASK	0x3
+
+#endif
-- 
1.8.0

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

* [PATCH 2/5] ASoC: Add ADAU1361/ADAU1761 audio CODEC support
  2013-08-28 15:20 [PATCH 1/5] ASoC: Add ADAU1X61 and ADAU1X81 CODECs common code Lars-Peter Clausen
@ 2013-08-28 15:20 ` Lars-Peter Clausen
  2013-08-28 17:24   ` Mark Brown
  2013-08-28 15:20 ` [PATCH 3/5] ASoC: Add ADAU1381/ADAU1781 " Lars-Peter Clausen
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 13+ messages in thread
From: Lars-Peter Clausen @ 2013-08-28 15:20 UTC (permalink / raw)
  To: Mark Brown, Liam Girdwood; +Cc: alsa-devel, Lars-Peter Clausen

This patch adds support for the Analog Devices ADAU1361 and ADAU1761 CODECs.
The device is a a low-power, 24-bit stereo audio CODEC with multiple analog
input and outputs, one digital microphone input and an I2S interface. The device
can be controlled either via I2C or SPI. The main difference between the two
variants is that the ADAU1761 has a built-in SigmaDSP, while the ADAU1361 has
not.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 include/linux/platform_data/adau17x1.h |  67 +++
 sound/soc/codecs/Kconfig               |   5 +
 sound/soc/codecs/Makefile              |   2 +
 sound/soc/codecs/adau1761.c            | 981 +++++++++++++++++++++++++++++++++
 4 files changed, 1055 insertions(+)
 create mode 100644 sound/soc/codecs/adau1761.c

diff --git a/include/linux/platform_data/adau17x1.h b/include/linux/platform_data/adau17x1.h
index fdf419a..5f559db 100644
--- a/include/linux/platform_data/adau17x1.h
+++ b/include/linux/platform_data/adau17x1.h
@@ -20,4 +20,71 @@ enum adau17x1_micbias_voltage {
 	ADAU17X1_MICBIAS_0_65_AVDD = 1,
 };
 
+/**
+ * enum adau1761_digmic_jackdet_pin_mode - Configuration of the JACKDET/MICIN pin
+ * @ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE:	Disable the pin
+ * @ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC:	Configure the pin for usage as
+ *	digital microphone input.
+ * @ADAU1761_DIGMIC_JACKDET_PIN_MODE_JACKDETECT: Configure the pin for jack
+ *	insertion detection.
+*/
+enum adau1761_digmic_jackdet_pin_mode {
+	ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE,
+	ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC,
+	ADAU1761_DIGMIC_JACKDET_PIN_MODE_JACKDETECT,
+};
+
+/**
+ * adau1761_jackdetect_debounce_time - Jack insertion detection debounce time
+ * @ADAU1761_JACKDETECT_DEBOUNCE_5MS:  5 milliseconds
+ * @ADAU1761_JACKDETECT_DEBOUNCE_10MS: 10 milliseconds
+ * @ADAU1761_JACKDETECT_DEBOUNCE_20MS: 20 milliseconds
+ * @ADAU1761_JACKDETECT_DEBOUNCE_40MS: 40 milliseconds
+ */
+enum adau1761_jackdetect_debounce_time {
+	ADAU1761_JACKDETECT_DEBOUNCE_5MS = 0,
+	ADAU1761_JACKDETECT_DEBOUNCE_10MS = 1,
+	ADAU1761_JACKDETECT_DEBOUNCE_20MS = 2,
+	ADAU1761_JACKDETECT_DEBOUNCE_40MS = 3,
+};
+
+/**
+ * enum adau1761_output_mode - Output mode configuration
+ * @ADAU1761_OUTPUT_MODE_HEADPHONE:		Headphone output
+ * @ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS:	Capless headphone output
+ * @ADAU1761_OUTPUT_MODE_LINE:			Line output
+ */
+enum adau1761_output_mode {
+	ADAU1761_OUTPUT_MODE_HEADPHONE,
+	ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS,
+	ADAU1761_OUTPUT_MODE_LINE,
+};
+
+/**
+ * struct adau1761_platform_data - ADAU1761 Codec driver platform data
+ * @input_differential:		If true the input pins will be configured in
+ *	differential mode.
+ * @lineout_mode:		Output mode for the LOUT/ROUT pins
+ * @headphone_mode:		Output mode for the LHP/RHP pins
+ * @digmic_jackdetect_pin_mode: JACKDET/MICIN pin configuration
+ * @jackdetect_debounce_time:	Jack insertion detection debounce time.
+ *	Note: This value will only be used, if the JACKDET/MICIN pin is
+ *	configured for jack insertion detection.
+ * @jackdetect_active_low:	If true the jack insertion detection is active
+ *	low. Othwise it will be active high.
+ * @micbias_voltage:		Microphone voltage bias
+ */
+struct adau1761_platform_data {
+	bool input_differential;
+	enum adau1761_output_mode lineout_mode;
+	enum adau1761_output_mode headphone_mode;
+
+	enum adau1761_digmic_jackdet_pin_mode digmic_jackdetect_pin_mode;
+
+	enum adau1761_jackdetect_debounce_time jackdetect_debounce_time;
+	bool jackdetect_active_low;
+
+	enum adau17x1_micbias_voltage micbias_voltage;
+};
+
 #endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 56137d4..d93a984 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -20,6 +20,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_AD1980 if SND_SOC_AC97_BUS
 	select SND_SOC_AD73311
 	select SND_SOC_ADAU1373 if I2C
+	select SND_SOC_ADAU1761 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI
 	select SND_SOC_ADAU1701 if I2C
 	select SND_SOC_ADS117X
@@ -197,6 +198,10 @@ config SND_SOC_ADAU17X1
 	select SND_SOC_SIGMADSP
 	tristate
 
+config SND_SOC_ADAU1761
+	select SND_SOC_ADAU17X1
+	tristate
+
 config SND_SOC_ADAV80X
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index b4ecf6c..6aec0af 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -8,6 +8,7 @@ snd-soc-ad73311-objs := ad73311.o
 snd-soc-adau1701-objs := adau1701.o
 snd-soc-adau1373-objs := adau1373.o
 snd-soc-adau17x1-objs := adau17x1.o
+snd-soc-adau1761-objs := adau1761.o
 snd-soc-adav80x-objs := adav80x.o
 snd-soc-ads117x-objs := ads117x.o
 snd-soc-ak4104-objs := ak4104.o
@@ -140,6 +141,7 @@ 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_ADAU17X1)	+= snd-soc-adau17x1.o
+obj-$(CONFIG_SND_SOC_ADAU1761)	+= snd-soc-adau1761.o
 obj-$(CONFIG_SND_SOC_ADAV80X)  += snd-soc-adav80x.o
 obj-$(CONFIG_SND_SOC_ADS117X)	+= snd-soc-ads117x.o
 obj-$(CONFIG_SND_SOC_AK4104)	+= snd-soc-ak4104.o
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
new file mode 100644
index 0000000..852b541
--- /dev/null
+++ b/sound/soc/codecs/adau1761.c
@@ -0,0 +1,981 @@
+/*
+ * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
+ *
+ * Copyright 2011-2013 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/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/platform_data/adau17x1.h>
+
+#include "adau17x1.h"
+
+#define ADAU1761_DIGMIC_JACKDETECT	0x4008
+#define ADAU1761_REC_MIXER_LEFT0	0x400a
+#define ADAU1761_REC_MIXER_LEFT1	0x400b
+#define ADAU1761_REC_MIXER_RIGHT0	0x400c
+#define ADAU1761_REC_MIXER_RIGHT1	0x400d
+#define ADAU1761_LEFT_DIFF_INPUT_VOL	0x400e
+#define ADAU1761_RIGHT_DIFF_INPUT_VOL	0x400f
+#define ADAU1761_PLAY_LR_MIXER_LEFT	0x4020
+#define ADAU1761_PLAY_MIXER_LEFT0	0x401c
+#define ADAU1761_PLAY_MIXER_LEFT1	0x401d
+#define ADAU1761_PLAY_MIXER_RIGHT0	0x401e
+#define ADAU1761_PLAY_MIXER_RIGHT1	0x401f
+#define ADAU1761_PLAY_LR_MIXER_RIGHT	0x4021
+#define ADAU1761_PLAY_MIXER_MONO	0x4022
+#define ADAU1761_PLAY_HP_LEFT_VOL	0x4023
+#define ADAU1761_PLAY_HP_RIGHT_VOL	0x4024
+#define ADAU1761_PLAY_LINE_LEFT_VOL	0x4025
+#define ADAU1761_PLAY_LINE_RIGHT_VOL	0x4026
+#define ADAU1761_PLAY_MONO_OUTPUT_VOL	0x4027
+#define ADAU1761_POP_CLICK_SUPPRESS	0x4028
+#define ADAU1761_JACK_DETECT_PIN	0x4031
+#define ADAU1761_DEJITTER		0x4036
+#define ADAU1761_CLK_ENABLE0		0x40f9
+#define ADAU1761_CLK_ENABLE1		0x40fa
+
+#define ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW	BIT(0)
+#define ADAU1761_DIGMIC_JACKDETECT_DIGMIC	BIT(5)
+
+#define ADAU1761_DIFF_INPUT_VOL_LDEN		0x01
+
+#define ADAU1761_FIRMWARE "adau1761.bin"
+
+static const struct reg_default adau1761_reg_defaults[] = {
+	{ ADAU1761_DEJITTER,			0x03 },
+	{ ADAU1761_DIGMIC_JACKDETECT,		0x00 },
+	{ ADAU1761_REC_MIXER_LEFT0,		0x00 },
+	{ ADAU1761_REC_MIXER_LEFT1,		0x00 },
+	{ ADAU1761_REC_MIXER_RIGHT0,		0x00 },
+	{ ADAU1761_REC_MIXER_RIGHT1,		0x00 },
+	{ ADAU1761_LEFT_DIFF_INPUT_VOL,		0x00 },
+	{ ADAU1761_RIGHT_DIFF_INPUT_VOL,	0x00 },
+	{ ADAU1761_PLAY_LR_MIXER_LEFT,		0x00 },
+	{ ADAU1761_PLAY_MIXER_LEFT0,		0x00 },
+	{ ADAU1761_PLAY_MIXER_LEFT1,		0x00 },
+	{ ADAU1761_PLAY_MIXER_RIGHT0,		0x00 },
+	{ ADAU1761_PLAY_MIXER_RIGHT1,		0x00 },
+	{ ADAU1761_PLAY_LR_MIXER_RIGHT,		0x00 },
+	{ ADAU1761_PLAY_MIXER_MONO,		0x00 },
+	{ ADAU1761_PLAY_HP_LEFT_VOL,		0x00 },
+	{ ADAU1761_PLAY_HP_RIGHT_VOL,		0x00 },
+	{ ADAU1761_PLAY_LINE_LEFT_VOL,		0x00 },
+	{ ADAU1761_PLAY_LINE_RIGHT_VOL,		0x00 },
+	{ ADAU1761_PLAY_MONO_OUTPUT_VOL,	0x00 },
+	{ ADAU1761_POP_CLICK_SUPPRESS,		0x00 },
+	{ ADAU1761_JACK_DETECT_PIN,		0x00 },
+	{ ADAU1761_CLK_ENABLE0,			0x00 },
+	{ ADAU1761_CLK_ENABLE1,			0x00 },
+	{ ADAU17X1_CLOCK_CONTROL,		0x00 },
+	{ ADAU17X1_PLL_CONTROL,			0x00 },
+	{ ADAU17X1_REC_POWER_MGMT,		0x00 },
+	{ ADAU17X1_MICBIAS,			0x00 },
+	{ ADAU17X1_SERIAL_PORT0,		0x00 },
+	{ ADAU17X1_SERIAL_PORT1,		0x00 },
+	{ ADAU17X1_CONVERTER0,			0x00 },
+	{ ADAU17X1_CONVERTER1,			0x00 },
+	{ ADAU17X1_LEFT_INPUT_DIGITAL_VOL,	0x00 },
+	{ ADAU17X1_RIGHT_INPUT_DIGITAL_VOL,	0x00 },
+	{ ADAU17X1_ADC_CONTROL,			0x00 },
+	{ ADAU17X1_PLAY_POWER_MGMT,		0x00 },
+	{ ADAU17X1_DAC_CONTROL0,		0x00 },
+	{ ADAU17X1_DAC_CONTROL1,		0x00 },
+	{ ADAU17X1_DAC_CONTROL2,		0x00 },
+	{ ADAU17X1_SERIAL_PORT_PAD,		0xaa },
+	{ ADAU17X1_CONTROL_PORT_PAD0,		0xaa },
+	{ ADAU17X1_CONTROL_PORT_PAD1,		0x00 },
+	{ ADAU17X1_DSP_SAMPLING_RATE,		0x01 },
+	{ ADAU17X1_SERIAL_INPUT_ROUTE,		0x00 },
+	{ ADAU17X1_SERIAL_OUTPUT_ROUTE,		0x00 },
+	{ ADAU17X1_DSP_ENABLE,			0x00 },
+	{ ADAU17X1_DSP_RUN,			0x00 },
+	{ ADAU17X1_SERIAL_SAMPLING_RATE,	0x00 },
+};
+
+static const DECLARE_TLV_DB_SCALE(adau1761_sing_in_tlv, -1500, 300, 1);
+static const DECLARE_TLV_DB_SCALE(adau1761_diff_in_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adau1761_out_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(adau1761_sidetone_tlv, -1800, 300, 1);
+static const DECLARE_TLV_DB_SCALE(adau1761_boost_tlv, -600, 600, 1);
+static const DECLARE_TLV_DB_SCALE(adau1761_pga_boost_tlv, -2000, 2000, 1);
+
+static const unsigned int adau1761_bias_select_values[] = {
+	0, 2, 3,
+};
+
+static const char * const adau1761_bias_select_text[] = {
+	"Normal operation", "Enhanced performance", "Power saving",
+};
+
+static const char * const adau1761_bias_select_extreme_text[] = {
+	"Normal operation", "Extreme power saving", "Enhanced performance",
+	"Power saving",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1761_adc_bias_enum,
+		ADAU17X1_REC_POWER_MGMT, 3, adau1761_bias_select_extreme_text);
+static const SOC_ENUM_SINGLE_DECL(adau1761_hp_bias_enum,
+		ADAU17X1_PLAY_POWER_MGMT, 6, adau1761_bias_select_extreme_text);
+static const SOC_ENUM_SINGLE_DECL(adau1761_dac_bias_enum,
+		ADAU17X1_PLAY_POWER_MGMT, 4, adau1761_bias_select_extreme_text);
+static const SOC_VALUE_ENUM_SINGLE_DECL(adau1761_playback_bias_enum,
+		ADAU17X1_PLAY_POWER_MGMT, 2, 0x3, adau1761_bias_select_text,
+		adau1761_bias_select_values);
+static const SOC_VALUE_ENUM_SINGLE_DECL(adau1761_capture_bias_enum,
+		ADAU17X1_REC_POWER_MGMT, 1, 0x3, adau1761_bias_select_text,
+		adau1761_bias_select_values);
+
+static const struct snd_kcontrol_new adau1761_jack_detect_controls[] = {
+	SOC_SINGLE("Jack Detect Switch", ADAU1761_DIGMIC_JACKDETECT, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1761_differential_mode_controls[] = {
+	SOC_DOUBLE_R_TLV("Capture Volume", ADAU1761_LEFT_DIFF_INPUT_VOL,
+		ADAU1761_RIGHT_DIFF_INPUT_VOL, 2, 0x3f, 0,
+		adau1761_diff_in_tlv),
+	SOC_DOUBLE_R("Capture Switch", ADAU1761_LEFT_DIFF_INPUT_VOL,
+		ADAU1761_RIGHT_DIFF_INPUT_VOL, 1, 1, 0),
+
+	SOC_DOUBLE_R_TLV("PGA Boost Capture Volume", ADAU1761_REC_MIXER_LEFT1,
+		ADAU1761_REC_MIXER_RIGHT1, 3, 2, 0, adau1761_pga_boost_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_single_mode_controls[] = {
+	SOC_SINGLE_TLV("Input 1 Capture Volume", ADAU1761_REC_MIXER_LEFT0,
+		4, 7, 0, adau1761_sing_in_tlv),
+	SOC_SINGLE_TLV("Input 2 Capture Volume", ADAU1761_REC_MIXER_LEFT0,
+		1, 7, 0, adau1761_sing_in_tlv),
+	SOC_SINGLE_TLV("Input 3 Capture Volume", ADAU1761_REC_MIXER_RIGHT0,
+		4, 7, 0, adau1761_sing_in_tlv),
+	SOC_SINGLE_TLV("Input 4 Capture Volume", ADAU1761_REC_MIXER_RIGHT0,
+		1, 7, 0, adau1761_sing_in_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_controls[] = {
+	SOC_DOUBLE_R_TLV("Aux Capture Volume", ADAU1761_REC_MIXER_LEFT1,
+		ADAU1761_REC_MIXER_RIGHT1, 0, 7, 0, adau1761_sing_in_tlv),
+
+	SOC_DOUBLE_R_TLV("Headphone Playback Volume", ADAU1761_PLAY_HP_LEFT_VOL,
+		ADAU1761_PLAY_HP_RIGHT_VOL, 2, 0x3f, 0, adau1761_out_tlv),
+	SOC_DOUBLE_R("Headphone Playback Switch", ADAU1761_PLAY_HP_LEFT_VOL,
+		ADAU1761_PLAY_HP_RIGHT_VOL, 1, 1, 0),
+	SOC_DOUBLE_R_TLV("Lineout Playback Volume", ADAU1761_PLAY_LINE_LEFT_VOL,
+		ADAU1761_PLAY_LINE_RIGHT_VOL, 2, 0x3f, 0, adau1761_out_tlv),
+	SOC_DOUBLE_R("Lineout Playback Switch", ADAU1761_PLAY_LINE_LEFT_VOL,
+		ADAU1761_PLAY_LINE_RIGHT_VOL, 1, 1, 0),
+
+	SOC_ENUM("ADC Bias", adau1761_adc_bias_enum),
+	SOC_ENUM("DAC Bias", adau1761_dac_bias_enum),
+	SOC_VALUE_ENUM("Capture Bias", adau1761_capture_bias_enum),
+	SOC_VALUE_ENUM("Playback Bias", adau1761_playback_bias_enum),
+	SOC_ENUM("Headphone Bias", adau1761_hp_bias_enum),
+};
+
+static const struct snd_kcontrol_new adau1761_mono_controls[] = {
+	SOC_SINGLE_TLV("Mono Playback Volume", ADAU1761_PLAY_MONO_OUTPUT_VOL,
+		2, 0x3f, 0, adau1761_out_tlv),
+	SOC_SINGLE("Mono Playback Switch", ADAU1761_PLAY_MONO_OUTPUT_VOL,
+		1, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1761_left_mixer_controls[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Left DAC Switch",
+		ADAU1761_PLAY_MIXER_LEFT0, 5, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("Right DAC Switch",
+		ADAU1761_PLAY_MIXER_LEFT0, 6, 1, 0),
+	SOC_DAPM_SINGLE_TLV("Aux Bypass Volume",
+		ADAU1761_PLAY_MIXER_LEFT0, 1, 8, 0, adau1761_sidetone_tlv),
+	SOC_DAPM_SINGLE_TLV("Right Bypass Volume",
+		ADAU1761_PLAY_MIXER_LEFT1, 4, 8, 0, adau1761_sidetone_tlv),
+	SOC_DAPM_SINGLE_TLV("Left Bypass Volume",
+		ADAU1761_PLAY_MIXER_LEFT1, 0, 8, 0, adau1761_sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_right_mixer_controls[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Left DAC Switch",
+		ADAU1761_PLAY_MIXER_RIGHT0, 5, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("Right DAC Switch",
+		ADAU1761_PLAY_MIXER_RIGHT0, 6, 1, 0),
+	SOC_DAPM_SINGLE_TLV("Aux Bypass Volume",
+		ADAU1761_PLAY_MIXER_RIGHT0, 1, 8, 0, adau1761_sidetone_tlv),
+	SOC_DAPM_SINGLE_TLV("Right Bypass Volume",
+		ADAU1761_PLAY_MIXER_RIGHT1, 4, 8, 0, adau1761_sidetone_tlv),
+	SOC_DAPM_SINGLE_TLV("Left Bypass Volume",
+		ADAU1761_PLAY_MIXER_RIGHT1, 0, 8, 0, adau1761_sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_left_lr_mixer_controls[] = {
+	SOC_DAPM_SINGLE_TLV("Left Volume",
+		ADAU1761_PLAY_LR_MIXER_LEFT, 1, 2, 0, adau1761_boost_tlv),
+	SOC_DAPM_SINGLE_TLV("Right Volume",
+		ADAU1761_PLAY_LR_MIXER_LEFT, 3, 2, 0, adau1761_boost_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_right_lr_mixer_controls[] = {
+	SOC_DAPM_SINGLE_TLV("Left Volume",
+		ADAU1761_PLAY_LR_MIXER_RIGHT, 1, 2, 0, adau1761_boost_tlv),
+	SOC_DAPM_SINGLE_TLV("Right Volume",
+		ADAU1761_PLAY_LR_MIXER_RIGHT, 3, 2, 0, adau1761_boost_tlv),
+};
+
+static const char * const adau1761_input_mux_text[] = {
+	"ADC", "DMIC",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1761_input_mux_enum,
+	ADAU17X1_ADC_CONTROL, 2, adau1761_input_mux_text);
+
+static const struct snd_kcontrol_new adau1761_input_mux_control =
+	SOC_DAPM_ENUM("Input Select", adau1761_input_mux_enum);
+
+static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
+
+	/* After any power changes have been made the dejitter circuit
+	 * has to be reinitialized. */
+	regmap_write(adau->regmap, ADAU1761_DEJITTER, 0);
+	if (!adau->master)
+		regmap_write(adau->regmap, ADAU1761_DEJITTER, 3);
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget adau1x61_dapm_widgets[] = {
+	SND_SOC_DAPM_MIXER("Left Input Mixer", ADAU1761_REC_MIXER_LEFT0, 0, 0,
+		NULL, 0),
+	SND_SOC_DAPM_MIXER("Right Input Mixer", ADAU1761_REC_MIXER_RIGHT0, 0, 0,
+		NULL, 0),
+
+	SOC_MIXER_ARRAY("Left Playback Mixer", ADAU1761_PLAY_MIXER_LEFT0,
+		0, 0, adau1761_left_mixer_controls),
+	SOC_MIXER_ARRAY("Right Playback Mixer", ADAU1761_PLAY_MIXER_RIGHT0,
+		0, 0, adau1761_right_mixer_controls),
+	SOC_MIXER_ARRAY("Left LR Playback Mixer", ADAU1761_PLAY_LR_MIXER_LEFT,
+		0, 0, adau1761_left_lr_mixer_controls),
+	SOC_MIXER_ARRAY("Right LR Playback Mixer", ADAU1761_PLAY_LR_MIXER_RIGHT,
+		0, 0, adau1761_right_lr_mixer_controls),
+
+	SND_SOC_DAPM_SUPPLY("Headphone", ADAU1761_PLAY_HP_LEFT_VOL,
+		0, 0, NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY_S("SYSCLK", 2, SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_POST("Dejitter fixup", adau1761_dejitter_fixup),
+
+	SND_SOC_DAPM_INPUT("LAUX"),
+	SND_SOC_DAPM_INPUT("RAUX"),
+	SND_SOC_DAPM_INPUT("LINP"),
+	SND_SOC_DAPM_INPUT("LINN"),
+	SND_SOC_DAPM_INPUT("RINP"),
+	SND_SOC_DAPM_INPUT("RINN"),
+
+	SND_SOC_DAPM_OUTPUT("LOUT"),
+	SND_SOC_DAPM_OUTPUT("ROUT"),
+	SND_SOC_DAPM_OUTPUT("LHP"),
+	SND_SOC_DAPM_OUTPUT("RHP"),
+};
+
+static const struct snd_soc_dapm_widget adau1761_mono_dapm_widgets[] = {
+	SND_SOC_DAPM_MIXER("Mono Playback Mixer", ADAU1761_PLAY_MIXER_MONO,
+		0, 0, NULL, 0),
+
+	SND_SOC_DAPM_OUTPUT("MONOOUT"),
+};
+
+static const struct snd_soc_dapm_widget adau1761_capless_dapm_widgets[] = {
+	SND_SOC_DAPM_SUPPLY_S("Headphone VGND", 1, ADAU1761_PLAY_MIXER_MONO,
+		0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route adau1x61_dapm_routes[] = {
+	{ "Left Input Mixer", NULL, "LINP" },
+	{ "Left Input Mixer", NULL, "LINN" },
+	{ "Left Input Mixer", NULL, "LAUX" },
+
+	{ "Right Input Mixer", NULL, "RINP" },
+	{ "Right Input Mixer", NULL, "RINN" },
+	{ "Right Input Mixer", NULL, "RAUX" },
+
+	{ "Left Playback Mixer", NULL, "Left Playback Enable"},
+	{ "Right Playback Mixer", NULL, "Right Playback Enable"},
+	{ "Left LR Playback Mixer", NULL, "Left Playback Enable"},
+	{ "Right LR Playback Mixer", NULL, "Right Playback Enable"},
+
+	{ "Left Playback Mixer", "Left DAC Switch", "Left DAC" },
+	{ "Left Playback Mixer", "Right DAC Switch", "Right DAC" },
+
+	{ "Right Playback Mixer", "Left DAC Switch", "Left DAC" },
+	{ "Right Playback Mixer", "Right DAC Switch", "Right DAC" },
+
+	{ "Left LR Playback Mixer", "Left Volume", "Left Playback Mixer" },
+	{ "Left LR Playback Mixer", "Right Volume", "Right Playback Mixer" },
+
+	{ "Right LR Playback Mixer", "Left Volume", "Left Playback Mixer" },
+	{ "Right LR Playback Mixer", "Right Volume", "Right Playback Mixer" },
+
+	{ "Left ADC", NULL, "Left Input Mixer" },
+	{ "Right ADC", NULL, "Right Input Mixer" },
+
+	{ "LHP", NULL, "Left Playback Mixer" },
+	{ "RHP", NULL, "Right Playback Mixer" },
+
+	{ "LHP", NULL, "Headphone" },
+	{ "RHP", NULL, "Headphone" },
+
+	{ "LOUT", NULL, "Left LR Playback Mixer" },
+	{ "ROUT", NULL, "Right LR Playback Mixer" },
+
+	{ "Left Playback Mixer", "Aux Bypass Volume", "LAUX" },
+	{ "Left Playback Mixer", "Left Bypass Volume", "Left Input Mixer" },
+	{ "Left Playback Mixer", "Right Bypass Volume", "Right Input Mixer" },
+	{ "Right Playback Mixer", "Aux Bypass Volume", "RAUX" },
+	{ "Right Playback Mixer", "Left Bypass Volume", "Left Input Mixer" },
+	{ "Right Playback Mixer", "Right Bypass Volume", "Right Input Mixer" },
+};
+
+static const struct snd_soc_dapm_route adau1761_mono_dapm_routes[] = {
+	{ "Mono Playback Mixer", NULL, "Left Playback Mixer" },
+	{ "Mono Playback Mixer", NULL, "Right Playback Mixer" },
+
+	{ "MONOOUT", NULL, "Mono Playback Mixer" },
+};
+
+static const struct snd_soc_dapm_route adau1761_capless_dapm_routes[] = {
+	{ "Headphone", NULL, "Headphone VGND" },
+};
+
+static const struct snd_soc_dapm_widget adau1761_dmic_widgets[] = {
+	SND_SOC_DAPM_MUX("Input Select", SND_SOC_NOPM, 0, 0,
+		&adau1761_input_mux_control),
+
+	SND_SOC_DAPM_INPUT("DMIC"),
+};
+
+static const struct snd_soc_dapm_route adau1761_dmic_routes[] = {
+	{ "Input Select", "ADC", "Left ADC" },
+	{ "Input Select", "ADC", "Right ADC" },
+	{ "Input Select", "DMIC", "DMIC" },
+
+	{ "DMIC", NULL, "Left Dec Filter Enable" },
+	{ "DMIC", NULL, "Right Dec Filter Enable" },
+};
+
+/*
+ * We have to handle 4 different cases, which all require different routes and
+ * widgets:
+ *	- DSP, DMIC
+ *	- DSP, no DMIC
+ *	- no DSP, DMIC
+ *	- no DSP, no DMIC
+ */
+
+DECLARE_ADAU17X1_AIFOUT_MUX(dmic, "ADC/DMIC");
+DECLARE_ADAU17X1_AIFOUT_MUX(no_dmic, "ADC");
+
+static const struct snd_soc_dapm_route adau1761_dmic_no_dsp_routes[] = {
+	{ "AIFOUT", NULL, "Input Select" },
+};
+
+static const struct snd_soc_dapm_route adau1761_dmic_dsp_routes[] = {
+	{ "DSP", NULL, "Input Select" },
+	{ "AIFOUT Capture Mux", "ADC/DMIC", "Input Select" },
+};
+
+static const struct snd_soc_dapm_route adau1761_no_dmic_no_dsp_routes[] = {
+	{ "AIFOUT", NULL, "Left ADC" },
+	{ "AIFOUT", NULL, "Right ADC" },
+};
+
+static const struct snd_soc_dapm_route adau1761_no_dmic_dsp_routes[] = {
+	{ "DSP", NULL, "Left ADC" },
+	{ "DSP", NULL, "Right ADC" },
+	{ "AIFOUT Capture Mux", "ADC", "Left ADC" },
+	{ "AIFOUT Capture Mux", "ADC", "Right ADC" },
+};
+
+static const struct snd_soc_dapm_widget adau1761_dapm_widgets[] = {
+	SND_SOC_DAPM_SUPPLY("Serial Port Clock", ADAU1761_CLK_ENABLE0,
+		0, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Serial Input Routing Clock", ADAU1761_CLK_ENABLE0,
+		1, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Serial Output Routing Clock", ADAU1761_CLK_ENABLE0,
+		3, 0, NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("Decimator Resync Clock", ADAU1761_CLK_ENABLE0,
+		4, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Interpolator Resync Clock", ADAU1761_CLK_ENABLE0,
+		2, 0, NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("Slew Clock", ADAU1761_CLK_ENABLE0, 6, 0, NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY_S("Digital Clock 0", 1, ADAU1761_CLK_ENABLE1,
+		0, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("Digital Clock 1", 1, ADAU1761_CLK_ENABLE1,
+		1, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
+	{ "Left ADC", NULL, "Digital Clock 0", },
+	{ "Right ADC", NULL, "Digital Clock 0", },
+	{ "Left DAC", NULL, "Digital Clock 0", },
+	{ "Right DAC", NULL, "Digital Clock 0", },
+
+	{ "AIFCLK", NULL, "Digital Clock 1" },
+
+	{ "AIFIN", NULL, "Serial Port Clock" },
+	{ "AIFOUT", NULL, "Serial Port Clock" },
+	{ "AIFIN", NULL, "Serial Input Routing Clock" },
+	{ "AIFOUT", NULL, "Serial Output Routing Clock" },
+
+	{ "AIFIN", NULL, "Decimator Resync Clock" },
+	{ "AIFOUT", NULL, "Interpolator Resync Clock" },
+
+	{ "DSP", NULL, "Decimator Resync Clock" },
+	{ "DSP", NULL, "Interpolator Resync Clock" },
+	{ "DSP", NULL, "Digital Clock 0" },
+
+	{ "Slew Clock", NULL, "Digital Clock 0" },
+	{ "Right Playback Mixer", NULL, "Slew Clock" },
+	{ "Left Playback Mixer", NULL, "Slew Clock" },
+
+	{ "Digital Clock 0", NULL, "SYSCLK" },
+	{ "Digital Clock 1", NULL, "SYSCLK" },
+
+	{ "AIFOUT", NULL, "Decimator Resync Clock" },
+};
+
+static int adau1761_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+			ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
+			ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
+		break;
+	case SND_SOC_BIAS_OFF:
+		regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+			ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
+		break;
+
+	}
+	codec->dapm.bias_level = level;
+	return 0;
+}
+
+static enum adau1761_output_mode adau1761_get_lineout_mode(
+	struct snd_soc_codec *codec)
+{
+	struct adau1761_platform_data *pdata = codec->dev->platform_data;
+
+	if (pdata)
+		return pdata->lineout_mode;
+
+	return ADAU1761_OUTPUT_MODE_LINE;
+}
+
+static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
+{
+	struct adau1761_platform_data *pdata = codec->dev->platform_data;
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
+	enum adau1761_digmic_jackdet_pin_mode mode;
+	const struct snd_soc_dapm_widget *widgets = NULL;
+	const struct snd_soc_dapm_route *routes = NULL;
+	unsigned int num_widgets = 0;
+	unsigned int num_routes = 0;
+	unsigned int val = 0;
+	int ret;
+
+	if (pdata)
+		mode = pdata->digmic_jackdetect_pin_mode;
+	else
+		mode = ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE;
+	mode = ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC;
+
+	switch (mode) {
+	case ADAU1761_DIGMIC_JACKDET_PIN_MODE_JACKDETECT:
+		switch (pdata->jackdetect_debounce_time) {
+		case ADAU1761_JACKDETECT_DEBOUNCE_5MS:
+		case ADAU1761_JACKDETECT_DEBOUNCE_10MS:
+		case ADAU1761_JACKDETECT_DEBOUNCE_20MS:
+		case ADAU1761_JACKDETECT_DEBOUNCE_40MS:
+			val |= pdata->jackdetect_debounce_time << 6;
+			break;
+		default:
+			return -EINVAL;
+		}
+		if (pdata->jackdetect_active_low)
+			val |= ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW;
+
+		ret = snd_soc_add_codec_controls(codec,
+			adau1761_jack_detect_controls,
+			ARRAY_SIZE(adau1761_jack_detect_controls));
+		if (ret)
+			return ret;
+	case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: /* fallthrough */
+		if (adau17x1_has_dsp(adau)) {
+			routes = adau1761_no_dmic_dsp_routes;
+			num_routes = ARRAY_SIZE(adau1761_no_dmic_dsp_routes);
+			ret = snd_soc_dapm_new_controls(&codec->dapm,
+				&adau17x1_no_dmic_dsp_widget, 1);
+			if (ret)
+				return ret;
+
+		} else {
+			routes = adau1761_no_dmic_no_dsp_routes;
+			num_routes = ARRAY_SIZE(adau1761_no_dmic_no_dsp_routes);
+		}
+		break;
+	case ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC:
+		ret = snd_soc_dapm_new_controls(&codec->dapm,
+			adau1761_dmic_widgets,
+			ARRAY_SIZE(adau1761_dmic_widgets));
+		if (ret)
+			return ret;
+
+		if (adau17x1_has_dsp(adau)) {
+			routes = adau1761_dmic_dsp_routes;
+			num_routes = ARRAY_SIZE(adau1761_dmic_dsp_routes);
+			widgets = &adau17x1_dmic_dsp_widget;
+			num_widgets = 1;
+		} else {
+			routes = adau1761_dmic_no_dsp_routes;
+			num_routes = ARRAY_SIZE(adau1761_dmic_no_dsp_routes);
+		}
+
+		ret = snd_soc_dapm_new_controls(&codec->dapm, widgets,
+			num_widgets);
+		if (ret)
+			return ret;
+
+		ret = snd_soc_dapm_add_routes(&codec->dapm,
+			adau1761_dmic_routes,
+			ARRAY_SIZE(adau1761_dmic_routes));
+		if (ret)
+			return ret;
+
+		val |= ADAU1761_DIGMIC_JACKDETECT_DIGMIC;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = snd_soc_dapm_add_routes(&codec->dapm, routes, num_routes);
+	if (ret)
+		return ret;
+
+	regmap_write(adau->regmap, ADAU1761_DIGMIC_JACKDETECT, val);
+
+	return 0;
+}
+
+static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
+{
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
+	struct adau1761_platform_data *pdata = codec->dev->platform_data;
+	enum adau1761_output_mode mode;
+	int ret;
+
+	if (pdata)
+		mode = pdata->headphone_mode;
+	else
+		mode = ADAU1761_OUTPUT_MODE_HEADPHONE;
+
+	switch (mode) {
+	case ADAU1761_OUTPUT_MODE_LINE:
+		break;
+	case ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS:
+		regmap_update_bits(adau->regmap, ADAU1761_PLAY_MONO_OUTPUT_VOL,
+			3, 3);
+	case ADAU1761_OUTPUT_MODE_HEADPHONE: /* fallthrough */
+		regmap_update_bits(adau->regmap, ADAU1761_PLAY_HP_RIGHT_VOL,
+			1, 1);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (mode == ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS) {
+		ret = snd_soc_dapm_new_controls(&codec->dapm,
+			adau1761_capless_dapm_widgets,
+			ARRAY_SIZE(adau1761_capless_dapm_widgets));
+		if (ret)
+			return ret;
+		ret = snd_soc_dapm_add_routes(&codec->dapm,
+			adau1761_capless_dapm_routes,
+			ARRAY_SIZE(adau1761_capless_dapm_routes));
+	} else {
+		ret = snd_soc_dapm_new_controls(&codec->dapm,
+			adau1761_mono_dapm_widgets,
+			ARRAY_SIZE(adau1761_mono_dapm_widgets));
+		if (ret)
+			return ret;
+		ret = snd_soc_dapm_add_routes(&codec->dapm,
+			adau1761_mono_dapm_routes,
+			ARRAY_SIZE(adau1761_mono_dapm_routes));
+	}
+
+	return ret;
+}
+
+static bool adau1761_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case ADAU1761_DIGMIC_JACKDETECT:
+	case ADAU1761_REC_MIXER_LEFT0:
+	case ADAU1761_REC_MIXER_LEFT1:
+	case ADAU1761_REC_MIXER_RIGHT0:
+	case ADAU1761_REC_MIXER_RIGHT1:
+	case ADAU1761_LEFT_DIFF_INPUT_VOL:
+	case ADAU1761_RIGHT_DIFF_INPUT_VOL:
+	case ADAU1761_PLAY_LR_MIXER_LEFT:
+	case ADAU1761_PLAY_MIXER_LEFT0:
+	case ADAU1761_PLAY_MIXER_LEFT1:
+	case ADAU1761_PLAY_MIXER_RIGHT0:
+	case ADAU1761_PLAY_MIXER_RIGHT1:
+	case ADAU1761_PLAY_LR_MIXER_RIGHT:
+	case ADAU1761_PLAY_MIXER_MONO:
+	case ADAU1761_PLAY_HP_LEFT_VOL:
+	case ADAU1761_PLAY_HP_RIGHT_VOL:
+	case ADAU1761_PLAY_LINE_LEFT_VOL:
+	case ADAU1761_PLAY_LINE_RIGHT_VOL:
+	case ADAU1761_PLAY_MONO_OUTPUT_VOL:
+	case ADAU1761_POP_CLICK_SUPPRESS:
+	case ADAU1761_JACK_DETECT_PIN:
+	case ADAU1761_DEJITTER:
+	case ADAU1761_CLK_ENABLE0:
+	case ADAU1761_CLK_ENABLE1:
+		return true;
+	default:
+		break;
+	}
+
+	return adau17x1_readable_register(dev, reg);
+}
+
+static int adau1761_probe(struct snd_soc_codec *codec)
+{
+	struct adau1761_platform_data *pdata = codec->dev->platform_data;
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	ret = adau17x1_probe(codec);
+	if (ret < 0)
+		return ret;
+
+	if (pdata && pdata->input_differential) {
+		regmap_update_bits(adau->regmap, ADAU1761_LEFT_DIFF_INPUT_VOL,
+			ADAU1761_DIFF_INPUT_VOL_LDEN,
+			ADAU1761_DIFF_INPUT_VOL_LDEN);
+		regmap_update_bits(adau->regmap, ADAU1761_RIGHT_DIFF_INPUT_VOL,
+			ADAU1761_DIFF_INPUT_VOL_LDEN,
+			ADAU1761_DIFF_INPUT_VOL_LDEN);
+		ret = snd_soc_add_codec_controls(codec,
+			adau1761_differential_mode_controls,
+			ARRAY_SIZE(adau1761_differential_mode_controls));
+		if (ret)
+			return ret;
+	} else {
+		ret = snd_soc_add_codec_controls(codec,
+			adau1761_single_mode_controls,
+			ARRAY_SIZE(adau1761_single_mode_controls));
+		if (ret)
+			return ret;
+	}
+
+	switch (adau1761_get_lineout_mode(codec)) {
+	case ADAU1761_OUTPUT_MODE_LINE:
+		break;
+	case ADAU1761_OUTPUT_MODE_HEADPHONE:
+		regmap_update_bits(adau->regmap, ADAU1761_PLAY_LINE_LEFT_VOL,
+			1, 1);
+		regmap_update_bits(adau->regmap, ADAU1761_PLAY_LINE_RIGHT_VOL,
+			1, 1);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = adau1761_setup_headphone_mode(codec);
+	if (ret)
+		return ret;
+
+	ret = adau1761_setup_digmic_jackdetect(codec);
+	if (ret)
+		return ret;
+
+	ret = adau17x1_add_routes(codec);
+	if (ret < 0)
+		return ret;
+
+	if (adau->type == ADAU1761) {
+		ret = snd_soc_dapm_new_controls(&codec->dapm,
+			adau1761_dapm_widgets,
+			ARRAY_SIZE(adau1761_dapm_widgets));
+		if (ret)
+			return ret;
+
+		ret = snd_soc_dapm_add_routes(&codec->dapm,
+			adau1761_dapm_routes,
+			ARRAY_SIZE(adau1761_dapm_routes));
+		if (ret)
+			return ret;
+
+		ret = adau17x1_load_firmware(adau, codec->dev,
+			ADAU1761_FIRMWARE);
+		if (ret)
+			dev_warn(codec->dev, "Failed to firmware\n");
+	}
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver adau1761_codec_driver = {
+	.probe			= adau1761_probe,
+	.remove			= adau17x1_suspend,
+	.suspend		= adau17x1_suspend,
+	.resume			= adau17x1_resume,
+	.set_bias_level		= adau1761_set_bias_level,
+
+	.controls		= adau1761_controls,
+	.num_controls		= ARRAY_SIZE(adau1761_controls),
+	.dapm_widgets		= adau1x61_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(adau1x61_dapm_widgets),
+	.dapm_routes		= adau1x61_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(adau1x61_dapm_routes),
+};
+
+#define ADAU1761_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+	SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver adau1361_dai_driver = {
+	.name = "adau-hifi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 4,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = ADAU1761_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 4,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = ADAU1761_FORMATS,
+	},
+	.ops = &adau17x1_dai_ops,
+};
+
+static struct snd_soc_dai_driver adau1761_dai_driver = {
+	.name = "adau-hifi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 8,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = ADAU1761_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 8,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = ADAU1761_FORMATS,
+	},
+	.ops = &adau17x1_dai_ops,
+};
+
+static int adau1761_bus_probe(struct device *dev,
+	struct regmap *regmap, enum adau17x1_type type,
+	bool spi_mode)
+{
+	struct snd_soc_dai_driver *dai_drv;
+	int ret;
+
+	ret = adau17x1_bus_probe(dev, regmap, type, spi_mode);
+	if (ret)
+		return ret;
+
+	if (type == ADAU1361)
+		dai_drv = &adau1361_dai_driver;
+	else
+		dai_drv = &adau1761_dai_driver;
+
+	return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1);
+}
+
+#if IS_ENABLED(CONFIG_SPI_MASTER)
+
+static const struct regmap_config adau1761_spi_regmap_config = {
+	.val_bits		= 8,
+	.reg_bits		= 24,
+	.read_flag_mask		= 0x01,
+	.max_register		= 0x40fa,
+	.reg_defaults		= adau1761_reg_defaults,
+	.num_reg_defaults	= ARRAY_SIZE(adau1761_reg_defaults),
+	.readable_reg		= adau1761_readable_register,
+	.volatile_reg		= adau17x1_volatile_register,
+	.cache_type		= REGCACHE_RBTREE,
+};
+
+static int adau1761_spi_probe(struct spi_device *spi)
+{
+	enum adau17x1_type type = spi_get_device_id(spi)->driver_data;
+	struct regmap *regmap;
+
+	regmap = devm_regmap_init_spi(spi, &adau1761_spi_regmap_config);
+
+	return adau1761_bus_probe(&spi->dev, regmap, type, true);
+}
+
+static int adau1761_spi_remove(struct spi_device *spi)
+{
+	snd_soc_unregister_codec(&spi->dev);
+	return 0;
+}
+
+static const struct spi_device_id adau1761_spi_id[] = {
+	{ "adau1361", ADAU1361 },
+	{ "adau1461", ADAU1761 },
+	{ "adau1761", ADAU1761 },
+	{ "adau1961", ADAU1361 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, adau1761_spi_id);
+
+static struct spi_driver adau1761_spi_driver = {
+	.driver = {
+		.name	= "adau1761",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= adau1761_spi_probe,
+	.remove		= adau1761_spi_remove,
+	.id_table	= adau1761_spi_id,
+};
+
+static int __init adau1761_spi_register_driver(void)
+{
+	return spi_register_driver(&adau1761_spi_driver);
+}
+
+static void adau1761_spi_unregister_driver(void)
+{
+	spi_unregister_driver(&adau1761_spi_driver);
+}
+
+#else
+static int adau1761_spi_register_driver(void) { return 0; }
+static void adau1761_spi_unregister_driver(void) {}
+#endif
+
+#if IS_ENABLED(CONFIG_I2C)
+
+static const struct regmap_config adau1761_i2c_regmap_config = {
+	.val_bits		= 8,
+	.reg_bits		= 16,
+	.max_register		= 0x40fa,
+	.reg_defaults		= adau1761_reg_defaults,
+	.num_reg_defaults	= ARRAY_SIZE(adau1761_reg_defaults),
+	.readable_reg		= adau1761_readable_register,
+	.volatile_reg		= adau17x1_volatile_register,
+	.cache_type		= REGCACHE_RBTREE,
+};
+
+static int adau1761_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	enum adau17x1_type type = id->driver_data;
+	struct regmap *regmap;
+
+	regmap = devm_regmap_init_i2c(client, &adau1761_i2c_regmap_config);
+
+	return adau1761_bus_probe(&client->dev, regmap, type, false);
+}
+
+static int adau1761_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
+}
+
+static const struct i2c_device_id adau1761_i2c_id[] = {
+	{ "adau1361", ADAU1361 },
+	{ "adau1461", ADAU1761 },
+	{ "adau1761", ADAU1761 },
+	{ "adau1961", ADAU1361 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, adau1761_i2c_id);
+
+static struct i2c_driver adau1761_i2c_driver = {
+	.driver = {
+		.name = "adau1761",
+		.owner = THIS_MODULE,
+	},
+	.probe = adau1761_i2c_probe,
+	.remove = adau1761_i2c_remove,
+	.id_table = adau1761_i2c_id,
+};
+
+static int __init adau1761_i2c_register_driver(void)
+{
+	return i2c_add_driver(&adau1761_i2c_driver);
+}
+
+static void __exit adau1761_i2c_unregister_driver(void)
+{
+	i2c_del_driver(&adau1761_i2c_driver);
+}
+
+#else
+static int adau1761_i2c_register_driver(void) { return 0; }
+static void adau1761_i2c_unregister_driver(void) {}
+#endif
+
+static int __init adau1761_init(void)
+{
+	int ret;
+
+	ret = adau1761_spi_register_driver();
+	if (ret)
+		return ret;
+
+	ret = adau1761_i2c_register_driver();
+	if (ret)
+		adau1761_spi_unregister_driver();
+
+	return ret;
+}
+module_init(adau1761_init);
+
+static void __exit adau1761_exit(void)
+{
+	adau1761_i2c_unregister_driver();
+	adau1761_spi_unregister_driver();
+}
+module_exit(adau1761_exit);
+
+MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
-- 
1.8.0

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

* [PATCH 3/5] ASoC: Add ADAU1381/ADAU1781 audio CODEC support
  2013-08-28 15:20 [PATCH 1/5] ASoC: Add ADAU1X61 and ADAU1X81 CODECs common code Lars-Peter Clausen
  2013-08-28 15:20 ` [PATCH 2/5] ASoC: Add ADAU1361/ADAU1761 audio CODEC support Lars-Peter Clausen
@ 2013-08-28 15:20 ` Lars-Peter Clausen
  2013-08-28 15:20 ` [PATCH 4/5] ASoC: Blackfin: ADAU1X61 eval board support Lars-Peter Clausen
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 13+ messages in thread
From: Lars-Peter Clausen @ 2013-08-28 15:20 UTC (permalink / raw)
  To: Mark Brown, Liam Girdwood; +Cc: alsa-devel, Lars-Peter Clausen

This patch adds support for the Analog Devices ADAU1381 and ADAU1781 audio
CODECs. The device is a low-power, 24-bit stereo audio CODEC with multiple
analog inputs and outputs, two digital microphone inputs and an I2S interface.
The device can be controlled either using I2C or SPI. The main difference
between the two variants is that the ADAU1781 has a freely programmable SigmaDSP
processor, while the ADAU1381 has a fixed function wind noise reduction filter.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 include/linux/platform_data/adau17x1.h |  19 +
 sound/soc/codecs/Kconfig               |   5 +
 sound/soc/codecs/Makefile              |   2 +
 sound/soc/codecs/adau1781.c            | 675 +++++++++++++++++++++++++++++++++
 4 files changed, 701 insertions(+)
 create mode 100644 sound/soc/codecs/adau1781.c

diff --git a/include/linux/platform_data/adau17x1.h b/include/linux/platform_data/adau17x1.h
index 5f559db..b890507 100644
--- a/include/linux/platform_data/adau17x1.h
+++ b/include/linux/platform_data/adau17x1.h
@@ -87,4 +87,23 @@ struct adau1761_platform_data {
 	enum adau17x1_micbias_voltage micbias_voltage;
 };
 
+/**
+ * struct adau1781_platform_data - ADAU1781 Codec driver platform data
+ * @left_input_differential:	If true configure the left input as
+	differential input.
+ * @right_input_differential:	If true configure the right input as
+ *	differntial input.
+ * @use_dmic:			If true configure the MIC pins as digital
+ *	microphone pins instead of analog microphone pins.
+ * @micbias_voltage:		Microphone voltage bias
+ */
+struct adau1781_platform_data {
+	bool left_input_differential;
+	bool right_input_differential;
+
+	bool use_dmic;
+
+	enum adau17x1_micbias_voltage micbias_voltage;
+};
+
 #endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index d93a984..503f57f 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -21,6 +21,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_AD73311
 	select SND_SOC_ADAU1373 if I2C
 	select SND_SOC_ADAU1761 if SND_SOC_I2C_AND_SPI
+	select SND_SOC_ADAU1781 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI
 	select SND_SOC_ADAU1701 if I2C
 	select SND_SOC_ADS117X
@@ -202,6 +203,10 @@ config SND_SOC_ADAU1761
 	select SND_SOC_ADAU17X1
 	tristate
 
+config SND_SOC_ADAU1781
+	select SND_SOC_ADAU17X1
+	tristate
+
 config SND_SOC_ADAV80X
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 6aec0af..3f6c3a9 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -9,6 +9,7 @@ snd-soc-adau1701-objs := adau1701.o
 snd-soc-adau1373-objs := adau1373.o
 snd-soc-adau17x1-objs := adau17x1.o
 snd-soc-adau1761-objs := adau1761.o
+snd-soc-adau1781-objs := adau1781.o
 snd-soc-adav80x-objs := adav80x.o
 snd-soc-ads117x-objs := ads117x.o
 snd-soc-ak4104-objs := ak4104.o
@@ -142,6 +143,7 @@ obj-$(CONFIG_SND_SOC_ADAU1373)	+= snd-soc-adau1373.o
 obj-$(CONFIG_SND_SOC_ADAU1701)  += snd-soc-adau1701.o
 obj-$(CONFIG_SND_SOC_ADAU17X1)	+= snd-soc-adau17x1.o
 obj-$(CONFIG_SND_SOC_ADAU1761)	+= snd-soc-adau1761.o
+obj-$(CONFIG_SND_SOC_ADAU1781)	+= snd-soc-adau1781.o
 obj-$(CONFIG_SND_SOC_ADAV80X)  += snd-soc-adav80x.o
 obj-$(CONFIG_SND_SOC_ADS117X)	+= snd-soc-ads117x.o
 obj-$(CONFIG_SND_SOC_AK4104)	+= snd-soc-ak4104.o
diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c
new file mode 100644
index 0000000..08bad0b
--- /dev/null
+++ b/sound/soc/codecs/adau1781.c
@@ -0,0 +1,675 @@
+/*
+ * Driver for ADAU1781/ADAU1781 codec
+ *
+ * Copyright 2011-2013 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/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/platform_data/adau17x1.h>
+
+#include "adau17x1.h"
+
+#define ADAU1781_DMIC_BEEP_CTRL		0x4008
+#define ADAU1781_LEFT_PGA		0x400e
+#define ADAU1781_RIGHT_PGA		0x400f
+#define ADAU1781_LEFT_PLAYBACK_MIXER	0x401c
+#define ADAU1781_RIGHT_PLAYBACK_MIXER	0x401e
+#define ADAU1781_MONO_PLAYBACK_MIXER	0x401f
+#define ADAU1781_LEFT_LINEOUT		0x4025
+#define ADAU1781_RIGHT_LINEOUT		0x4026
+#define ADAU1781_SPEAKER		0x4027
+#define ADAU1781_BEEP_ZC		0x4028
+#define ADAU1781_DEJITTER		0x4032
+#define ADAU1781_DIG_PWDN0		0x4080
+#define ADAU1781_DIG_PWDN1		0x4081
+
+#define ADAU1781_INPUT_DIFFERNTIAL BIT(3)
+
+#define ADAU1381_FIRMWARE "adau1381.bin"
+#define ADAU1781_FIRMWARE "adau1781.bin"
+
+static const struct reg_default adau1781_reg_defaults[] = {
+	{ ADAU1781_DMIC_BEEP_CTRL,		0x00 },
+	{ ADAU1781_LEFT_PGA,			0xc7 },
+	{ ADAU1781_RIGHT_PGA,			0xc7 },
+	{ ADAU1781_LEFT_PLAYBACK_MIXER,		0x00 },
+	{ ADAU1781_RIGHT_PLAYBACK_MIXER,	0x00 },
+	{ ADAU1781_MONO_PLAYBACK_MIXER,		0x00 },
+	{ ADAU1781_LEFT_LINEOUT,		0x00 },
+	{ ADAU1781_RIGHT_LINEOUT,		0x00 },
+	{ ADAU1781_SPEAKER,			0x00 },
+	{ ADAU1781_BEEP_ZC,			0x19 },
+	{ ADAU1781_DEJITTER,			0x60 },
+	{ ADAU1781_DIG_PWDN1,			0x0c },
+	{ ADAU1781_DIG_PWDN1,			0x00 },
+	{ ADAU17X1_CLOCK_CONTROL,		0x00 },
+	{ ADAU17X1_PLL_CONTROL,			0x00 },
+	{ ADAU17X1_REC_POWER_MGMT,		0x00 },
+	{ ADAU17X1_MICBIAS,			0x04 },
+	{ ADAU17X1_SERIAL_PORT0,		0x00 },
+	{ ADAU17X1_SERIAL_PORT1,		0x00 },
+	{ ADAU17X1_CONVERTER0,			0x00 },
+	{ ADAU17X1_CONVERTER1,			0x00 },
+	{ ADAU17X1_LEFT_INPUT_DIGITAL_VOL,	0x00 },
+	{ ADAU17X1_RIGHT_INPUT_DIGITAL_VOL,	0x00 },
+	{ ADAU17X1_ADC_CONTROL,			0x00 },
+	{ ADAU17X1_PLAY_POWER_MGMT,		0x00 },
+	{ ADAU17X1_DAC_CONTROL0,		0x00 },
+	{ ADAU17X1_DAC_CONTROL1,		0x00 },
+	{ ADAU17X1_DAC_CONTROL2,		0x00 },
+	{ ADAU17X1_SERIAL_PORT_PAD,		0x00 },
+	{ ADAU17X1_CONTROL_PORT_PAD0,		0x00 },
+	{ ADAU17X1_CONTROL_PORT_PAD1,		0x00 },
+	{ ADAU17X1_DSP_SAMPLING_RATE,		0x01 },
+	{ ADAU17X1_SERIAL_INPUT_ROUTE,		0x00 },
+	{ ADAU17X1_SERIAL_OUTPUT_ROUTE,		0x00 },
+	{ ADAU17X1_DSP_ENABLE,			0x00 },
+	{ ADAU17X1_DSP_RUN,			0x00 },
+	{ ADAU17X1_SERIAL_SAMPLING_RATE,	0x00 },
+};
+
+static const DECLARE_TLV_DB_SCALE(adau1781_speaker_tlv, 0, 200, 0);
+
+static const unsigned int adau1781_pga_tlv[] = {
+	TLV_DB_RANGE_HEAD(4),
+	0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
+	2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0),
+	4, 4, TLV_DB_SCALE_ITEM(1700, 0, 0),
+	5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0),
+};
+
+static const unsigned int adau1781_beep_tlv[] = {
+	TLV_DB_RANGE_HEAD(4),
+	0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
+	2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0),
+	4, 4, TLV_DB_SCALE_ITEM(-2300, 0, 0),
+	5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0),
+};
+
+static const DECLARE_TLV_DB_SCALE(adau1781_sidetone_tlv, -1800, 300, 1);
+
+static const char * const adau1781_speaker_bias_select_text[] = {
+	"Normal operation", "Power saving", "Enhanced performance",
+};
+
+static const char * const adau1781_bias_select_text[] = {
+	"Normal operation", "Extreme power saving", "Power saving",
+	"Enhanced performance",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1781_adc_bias_enum,
+		ADAU17X1_REC_POWER_MGMT, 3, adau1781_bias_select_text);
+static const SOC_ENUM_SINGLE_DECL(adau1781_speaker_bias_enum,
+		ADAU17X1_PLAY_POWER_MGMT, 6, adau1781_speaker_bias_select_text);
+static const SOC_ENUM_SINGLE_DECL(adau1781_dac_bias_enum,
+		ADAU17X1_PLAY_POWER_MGMT, 4, adau1781_bias_select_text);
+static const SOC_ENUM_SINGLE_DECL(adau1781_playback_bias_enum,
+		ADAU17X1_PLAY_POWER_MGMT, 2, adau1781_bias_select_text);
+static const SOC_ENUM_SINGLE_DECL(adau1781_capture_bias_enum,
+		ADAU17X1_REC_POWER_MGMT, 1, adau1781_bias_select_text);
+
+static const struct snd_kcontrol_new adau1781_controls[] = {
+	SOC_SINGLE_TLV("Beep Capture Volume", ADAU1781_DMIC_BEEP_CTRL, 0, 7, 0,
+		adau1781_beep_tlv),
+	SOC_DOUBLE_R_TLV("PGA Capture Volume", ADAU1781_LEFT_PGA,
+		ADAU1781_RIGHT_PGA, 5, 7, 0, adau1781_pga_tlv),
+	SOC_DOUBLE_R("PGA Capture Switch", ADAU1781_LEFT_PGA,
+		ADAU1781_RIGHT_PGA, 1, 1, 0),
+
+	SOC_DOUBLE_R("Lineout Playback Switch", ADAU1781_LEFT_LINEOUT,
+		ADAU1781_RIGHT_LINEOUT, 1, 1, 0),
+	SOC_SINGLE("Beep ZC Switch", ADAU1781_BEEP_ZC, 0, 1, 0),
+
+	SOC_SINGLE("Mono Playback Switch", ADAU1781_MONO_PLAYBACK_MIXER,
+		0, 1, 0),
+	SOC_SINGLE_TLV("Mono Playback Volume", ADAU1781_SPEAKER, 6, 3, 0,
+		adau1781_speaker_tlv),
+
+	SOC_ENUM("ADC Bias", adau1781_adc_bias_enum),
+	SOC_ENUM("DAC Bias", adau1781_dac_bias_enum),
+	SOC_ENUM("Capture Bias", adau1781_capture_bias_enum),
+	SOC_ENUM("Playback Bias", adau1781_playback_bias_enum),
+	SOC_ENUM("Speaker Bias", adau1781_speaker_bias_enum),
+};
+
+static const struct snd_kcontrol_new adau1781_beep_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Beep Capture Switch", ADAU1781_DMIC_BEEP_CTRL,
+		3, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1781_left_mixer_controls[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+		ADAU1781_LEFT_PLAYBACK_MIXER, 5, 1, 0),
+	SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
+		ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new adau1781_right_mixer_controls[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+		ADAU1781_RIGHT_PLAYBACK_MIXER, 6, 1, 0),
+	SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
+		ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Left Switch",
+		ADAU1781_MONO_PLAYBACK_MIXER, 7, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("Right Switch",
+		 ADAU1781_MONO_PLAYBACK_MIXER, 6, 1, 0),
+	SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
+		ADAU1781_MONO_PLAYBACK_MIXER, 2, 8, 0, adau1781_sidetone_tlv),
+};
+
+static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+	/* After any power changes have been made the dejitter circuit
+	 * has to be reinitialized. */
+	regmap_write(adau->regmap, ADAU1781_DEJITTER, 0);
+	if (!adau->master)
+		regmap_write(adau->regmap, ADAU1781_DEJITTER, 5);
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget adau1781_dapm_widgets[] = {
+	SND_SOC_DAPM_PGA("Left PGA", ADAU1781_LEFT_PGA, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Right PGA", ADAU1781_RIGHT_PGA, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_OUT_DRV("Speaker", ADAU1781_SPEAKER, 0, 0, NULL, 0),
+
+	SOC_MIXER_NAMED_CTL_ARRAY("Beep Mixer", ADAU17X1_MICBIAS, 4, 0,
+		adau1781_beep_mixer_controls),
+
+	SOC_MIXER_ARRAY("Left Lineout Mixer", SND_SOC_NOPM, 0, 0,
+		adau1781_left_mixer_controls),
+	SOC_MIXER_ARRAY("Right Lineout Mixer", SND_SOC_NOPM, 0, 0,
+		adau1781_right_mixer_controls),
+	SOC_MIXER_ARRAY("Mono Mixer", SND_SOC_NOPM, 0, 0,
+		adau1781_mono_mixer_controls),
+
+	SND_SOC_DAPM_SUPPLY("Serial Input Routing", ADAU1781_DIG_PWDN0,
+		2, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Serial Output Routing", ADAU1781_DIG_PWDN0,
+		3, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Clock Domain Transfer", ADAU1781_DIG_PWDN0,
+		5, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Serial Ports", ADAU1781_DIG_PWDN0, 4, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("ADC Engine", ADAU1781_DIG_PWDN0, 7, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("DAC Engine", ADAU1781_DIG_PWDN1, 0, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Digital Mic", ADAU1781_DIG_PWDN1, 1, 0, NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("Sound Engine", ADAU1781_DIG_PWDN0, 0, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, ADAU1781_DIG_PWDN0, 1, 0, NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("Zero Crossing Detector", ADAU1781_DIG_PWDN1, 2, 0,
+		NULL, 0),
+
+	SND_SOC_DAPM_POST("Dejitter fixup", adau1781_dejitter_fixup),
+
+	SND_SOC_DAPM_INPUT("BEEP"),
+
+	SND_SOC_DAPM_OUTPUT("AOUTL"),
+	SND_SOC_DAPM_OUTPUT("AOUTR"),
+	SND_SOC_DAPM_OUTPUT("SP"),
+	SND_SOC_DAPM_INPUT("LMIC"),
+	SND_SOC_DAPM_INPUT("RMIC"),
+};
+
+static const struct snd_soc_dapm_route adau1781_dapm_routes[] = {
+	{ "Left Lineout Mixer", NULL, "Left Playback Enable" },
+	{ "Right Lineout Mixer", NULL, "Right Playback Enable" },
+
+	{ "Left Lineout Mixer", "Beep Playback Volume", "Beep Mixer" },
+	{ "Left Lineout Mixer", "Switch", "Left DAC" },
+
+	{ "Right Lineout Mixer", "Beep Playback Volume", "Beep Mixer" },
+	{ "Right Lineout Mixer", "Switch", "Right DAC" },
+
+	{ "Mono Mixer", "Beep Playback Volume", "Beep Mixer" },
+	{ "Mono Mixer", "Right Switch", "Right DAC" },
+	{ "Mono Mixer", "Left Switch", "Left DAC" },
+	{ "Speaker", NULL, "Mono Mixer" },
+
+	{ "Mono Mixer", NULL, "SYSCLK" },
+	{ "Left Lineout Mixer", NULL, "SYSCLK" },
+	{ "Left Lineout Mixer", NULL, "SYSCLK" },
+
+	{ "Beep Mixer", "Beep Capture Switch", "BEEP" },
+	{ "Beep Mixer", NULL, "Zero Crossing Detector" },
+
+	{ "Left DAC", NULL, "DAC Engine" },
+	{ "Right DAC", NULL, "DAC Engine" },
+
+	{ "Sound Engine", NULL, "SYSCLK" },
+	{ "DSP", NULL, "Sound Engine" },
+
+	{ "AIFCLK", NULL, "SYSCLK" },
+
+	{ "AIFIN", NULL, "Serial Input Routing" },
+	{ "AIFIN", NULL, "Serial Ports" },
+	{ "AIFIN", NULL, "Clock Domain Transfer" },
+	{ "AIFOUT", NULL, "Serial Output Routing" },
+	{ "AIFOUT", NULL, "Serial Ports" },
+	{ "AIFOUT", NULL, "Clock Domain Transfer" },
+
+	{ "AOUTL", NULL, "Left Lineout Mixer" },
+	{ "AOUTR", NULL, "Right Lineout Mixer" },
+	{ "SP", NULL, "Speaker" },
+};
+
+DECLARE_ADAU17X1_AIFOUT_MUX(adc, "ADC");
+
+static const struct snd_soc_dapm_route adau1781_adc_dapm_routes[] = {
+	{ "Left PGA", NULL, "LMIC" },
+	{ "Right PGA", NULL, "RMIC" },
+	{ "Left ADC", NULL, "Left PGA" },
+	{ "Right ADC", NULL, "Right PGA" },
+
+	{ "Left ADC", NULL, "ADC Engine" },
+	{ "Right ADC", NULL, "ADC Engine" },
+
+	{ "AIFOUT Capture Mux", "ADC", "Left ADC" },
+	{ "AIFOUT Capture Mux", "ADC", "Right ADC" },
+
+	{ "DSP", NULL, "Left ADC" },
+	{ "DSP", NULL, "Right ADC" },
+};
+
+static const char * const adau1781_dmic_select_text[] = {
+	"DMIC1", "DMIC2",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1781_dmic_select_enum, 0, 0,
+	adau1781_dmic_select_text);
+
+static const struct snd_kcontrol_new adau1781_dmic_mux =
+	SOC_DAPM_ENUM_VIRT("DMIC Select", adau1781_dmic_select_enum);
+
+static const char * const adau1781_dmic_aifout_mux_text[] = {
+	"DMIC",
+	"DSP",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1781_dmic_aifout_mux_enum,
+	ADAU17X1_SERIAL_OUTPUT_ROUTE, 0, adau1781_dmic_aifout_mux_text);
+
+static const struct snd_kcontrol_new adau1781_dmic_aifout_mux =
+	ADAU17X1_DSP_MUX_ENUM("AIFOUT Capture Mux",
+		adau1781_dmic_aifout_mux_enum);
+
+static const struct snd_soc_dapm_widget adau1781_dmic_dapm_widgets[] = {
+	SND_SOC_DAPM_VIRT_MUX("DMIC Select", SND_SOC_NOPM, 0, 0,
+		&adau1781_dmic_mux),
+
+	SND_SOC_DAPM_VIRT_MUX("AIFOUT Capture Mux", SND_SOC_NOPM, 0, 0,
+		&adau1781_dmic_aifout_mux),
+
+	SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1781_DMIC_BEEP_CTRL, 4, 0),
+	SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1781_DMIC_BEEP_CTRL, 5, 0),
+};
+
+static const struct snd_soc_dapm_route adau1781_dmic_dapm_routes[] = {
+	{ "DMIC1", NULL, "LMIC" },
+	{ "DMIC2", NULL, "RMIC" },
+
+	{ "DMIC1", NULL, "Digital Mic" },
+	{ "DMIC2", NULL, "Digital Mic" },
+	{ "DMIC1", NULL, "ADC Engine" },
+	{ "DMIC2", NULL, "ADC Engine" },
+
+	{ "DMIC Select", "DMIC1", "DMIC1" },
+	{ "DMIC Select", "DMIC2", "DMIC2" },
+
+	{ "AIFOUT Capture Mux", "DMIC", "DMIC Select" },
+	{ "DSP", NULL, "DMIC Select" },
+};
+
+static int adau1781_set_bias_level(struct snd_soc_codec *codec,
+		enum snd_soc_bias_level level)
+{
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+			ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
+			ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
+
+		/* Precharge */
+		regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0x8, 0x8);
+		break;
+	case SND_SOC_BIAS_OFF:
+		regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0xc, 0x0);
+		regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+			ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
+		break;
+	}
+
+	codec->dapm.bias_level = level;
+	return 0;
+}
+
+static bool adau1781_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case ADAU1781_DMIC_BEEP_CTRL:
+	case ADAU1781_LEFT_PGA:
+	case ADAU1781_RIGHT_PGA:
+	case ADAU1781_LEFT_PLAYBACK_MIXER:
+	case ADAU1781_RIGHT_PLAYBACK_MIXER:
+	case ADAU1781_MONO_PLAYBACK_MIXER:
+	case ADAU1781_LEFT_LINEOUT:
+	case ADAU1781_RIGHT_LINEOUT:
+	case ADAU1781_SPEAKER:
+	case ADAU1781_BEEP_ZC:
+	case ADAU1781_DEJITTER:
+	case ADAU1781_DIG_PWDN0:
+	case ADAU1781_DIG_PWDN1:
+		return true;
+	default:
+		break;
+	}
+
+	return adau17x1_readable_register(dev, reg);
+}
+
+static int adau1781_set_input_mode(struct adau *adau, unsigned int reg,
+	bool differential)
+{
+	unsigned int val;
+
+	if (differential)
+		val = ADAU1781_INPUT_DIFFERNTIAL;
+	else
+		val = 0;
+
+	return regmap_update_bits(adau->regmap, reg,
+		ADAU1781_INPUT_DIFFERNTIAL, val);
+}
+
+static int adau1781_probe(struct snd_soc_codec *codec)
+{
+	struct adau1781_platform_data *pdata = dev_get_platdata(codec->dev);
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
+	const char *firmware;
+	int ret;
+
+	ret = adau17x1_probe(codec);
+	if (ret)
+		return ret;
+
+	if (pdata) {
+		ret = adau1781_set_input_mode(adau, ADAU1781_LEFT_PGA,
+			pdata->left_input_differential);
+		if (ret)
+			return ret;
+		ret = adau1781_set_input_mode(adau, ADAU1781_RIGHT_PGA,
+			pdata->right_input_differential);
+		if (ret)
+			return ret;
+	}
+
+	if (pdata && pdata->use_dmic) {
+		ret = snd_soc_dapm_new_controls(&codec->dapm,
+			adau1781_dmic_dapm_widgets,
+			ARRAY_SIZE(adau1781_dmic_dapm_widgets));
+		if (ret)
+			return ret;
+		ret = snd_soc_dapm_add_routes(&codec->dapm,
+			adau1781_dmic_dapm_routes,
+			ARRAY_SIZE(adau1781_dmic_dapm_routes));
+		if (ret)
+			return ret;
+	} else {
+		ret = snd_soc_dapm_new_controls(&codec->dapm,
+			&adau17x1_adc_dsp_widget, 1);
+		if (ret)
+			return ret;
+		ret = snd_soc_dapm_add_routes(&codec->dapm,
+			adau1781_adc_dapm_routes,
+			ARRAY_SIZE(adau1781_adc_dapm_routes));
+		if (ret)
+			return ret;
+	}
+
+	switch (adau->type) {
+	case ADAU1381:
+		firmware = ADAU1381_FIRMWARE;
+		break;
+	case ADAU1781:
+		firmware = ADAU1781_FIRMWARE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = adau17x1_add_routes(codec);
+	if (ret < 0)
+		return ret;
+
+	ret = adau17x1_load_firmware(adau, codec->dev, firmware);
+	if (ret)
+		dev_warn(codec->dev, "Failed to load firmware\n");
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver adau1781_codec_driver = {
+	.probe			= adau1781_probe,
+	.remove			= adau17x1_suspend,
+	.suspend		= adau17x1_suspend,
+	.resume			= adau17x1_resume,
+	.set_bias_level		= adau1781_set_bias_level,
+
+	.controls		= adau1781_controls,
+	.num_controls		= ARRAY_SIZE(adau1781_controls),
+	.dapm_widgets		= adau1781_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(adau1781_dapm_widgets),
+	.dapm_routes		= adau1781_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(adau1781_dapm_routes),
+};
+
+#define ADAU1781_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+	SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver adau1781_dai_driver = {
+	.name = "adau-hifi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 8,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = ADAU1781_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 8,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = ADAU1781_FORMATS,
+	},
+	.ops = &adau17x1_dai_ops,
+};
+
+#if IS_ENABLED(CONFIG_SPI_MASTER)
+
+static const struct regmap_config adau1781_spi_regmap_config = {
+	.val_bits		= 8,
+	.reg_bits		= 24,
+	.read_flag_mask		= 0x01,
+	.max_register		= 0x40f8,
+	.reg_defaults		= adau1781_reg_defaults,
+	.num_reg_defaults	= ARRAY_SIZE(adau1781_reg_defaults),
+	.readable_reg		= adau1781_readable_register,
+	.volatile_reg		= adau17x1_volatile_register,
+	.cache_type		= REGCACHE_RBTREE,
+};
+
+static int adau1781_spi_probe(struct spi_device *spi)
+{
+	struct regmap *regmap;
+	enum adau17x1_type type = spi_get_device_id(spi)->driver_data;
+	int ret;
+
+	regmap = devm_regmap_init_spi(spi, &adau1781_spi_regmap_config);
+
+	ret = adau17x1_bus_probe(&spi->dev, regmap, type, true);
+	if (ret)
+		return ret;
+
+	return snd_soc_register_codec(&spi->dev, &adau1781_codec_driver,
+		&adau1781_dai_driver, 1);
+}
+
+static int adau1781_spi_remove(struct spi_device *spi)
+{
+	snd_soc_unregister_codec(&spi->dev);
+	return 0;
+}
+
+static const struct spi_device_id adau1781_spi_id[] = {
+	{ "adau1381", ADAU1381 },
+	{ "adau1781", ADAU1781 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, adau1781_spi_id);
+
+static struct spi_driver adau1781_spi_driver = {
+	.driver = {
+		.name	= "adau1781",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= adau1781_spi_probe,
+	.remove		= adau1781_spi_remove,
+	.id_table	= adau1781_spi_id,
+};
+
+static int __init adau1781_spi_register_driver(void)
+{
+	return spi_register_driver(&adau1781_spi_driver);
+}
+
+static void adau1781_spi_unregister_driver(void)
+{
+	spi_unregister_driver(&adau1781_spi_driver);
+}
+
+#else
+static int adau1781_spi_register_driver(void) { return 0; }
+static void adau1781_spi_unregister_driver(void) {}
+#endif
+
+#if IS_ENABLED(CONFIG_I2C)
+
+static const struct regmap_config adau1781_i2c_regmap_config = {
+	.val_bits		= 8,
+	.reg_bits		= 16,
+	.max_register		= 0x40f8,
+	.reg_defaults		= adau1781_reg_defaults,
+	.num_reg_defaults	= ARRAY_SIZE(adau1781_reg_defaults),
+	.readable_reg		= adau1781_readable_register,
+	.volatile_reg		= adau17x1_volatile_register,
+	.cache_type		= REGCACHE_RBTREE,
+};
+
+static int adau1781_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	struct regmap *regmap;
+	enum adau17x1_type type = id->driver_data;
+	int ret;
+
+	regmap = devm_regmap_init_i2c(client, &adau1781_i2c_regmap_config);
+
+	ret = adau17x1_bus_probe(&client->dev, regmap, type, false);
+	if (ret)
+		return ret;
+
+	return snd_soc_register_codec(&client->dev, &adau1781_codec_driver,
+		&adau1781_dai_driver, 1);
+}
+
+static int adau1781_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
+}
+
+static const struct i2c_device_id adau1781_i2c_id[] = {
+	{ "adau1381", ADAU1381 },
+	{ "adau1781", ADAU1781 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, adau1781_i2c_id);
+
+static struct i2c_driver adau1781_i2c_driver = {
+	.driver = {
+		.name = "adau1781",
+		.owner = THIS_MODULE,
+	},
+	.probe = adau1781_i2c_probe,
+	.remove = adau1781_i2c_remove,
+	.id_table = adau1781_i2c_id,
+};
+
+static int __init adau1781_i2c_register_driver(void)
+{
+	return i2c_add_driver(&adau1781_i2c_driver);
+}
+
+static void __exit adau1781_i2c_unregister_driver(void)
+{
+	i2c_del_driver(&adau1781_i2c_driver);
+}
+
+#else
+static int adau1781_i2c_register_driver(void) { return 0; }
+static void adau1781_i2c_unregister_driver(void) {}
+#endif
+
+static int __init adau1781_init(void)
+{
+	int ret = 0;
+
+	ret = adau1781_spi_register_driver();
+	if (ret)
+		return ret;
+
+	ret = adau1781_i2c_register_driver();
+	if (ret)
+		adau1781_spi_unregister_driver();
+
+	return ret;
+}
+module_init(adau1781_init);
+
+static void __exit adau1781_exit(void)
+{
+	adau1781_i2c_unregister_driver();
+	adau1781_spi_unregister_driver();
+}
+module_exit(adau1781_exit);
+
+MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
-- 
1.8.0

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

* [PATCH 4/5] ASoC: Blackfin: ADAU1X61 eval board support
  2013-08-28 15:20 [PATCH 1/5] ASoC: Add ADAU1X61 and ADAU1X81 CODECs common code Lars-Peter Clausen
  2013-08-28 15:20 ` [PATCH 2/5] ASoC: Add ADAU1361/ADAU1761 audio CODEC support Lars-Peter Clausen
  2013-08-28 15:20 ` [PATCH 3/5] ASoC: Add ADAU1381/ADAU1781 " Lars-Peter Clausen
@ 2013-08-28 15:20 ` Lars-Peter Clausen
  2013-08-28 17:24   ` Mark Brown
  2013-08-28 15:20 ` [PATCH 5/5] ASoC: Blackfin: ADAU1X81 " Lars-Peter Clausen
  2013-08-28 16:59 ` [PATCH 1/5] ASoC: Add ADAU1X61 and ADAU1X81 CODECs common code Mark Brown
  4 siblings, 1 reply; 13+ messages in thread
From: Lars-Peter Clausen @ 2013-08-28 15:20 UTC (permalink / raw)
  To: Mark Brown, Liam Girdwood; +Cc: alsa-devel, Lars-Peter Clausen

This patch adds a ASoC machine driver to support the EVAL-ADAU1X61 board
connected to a Analog Devices BF5XX evaluation board.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 sound/soc/blackfin/Kconfig              |  13 +++
 sound/soc/blackfin/Makefile             |   2 +
 sound/soc/blackfin/bfin-eval-adau1x61.c | 151 ++++++++++++++++++++++++++++++++
 3 files changed, 166 insertions(+)
 create mode 100644 sound/soc/blackfin/bfin-eval-adau1x61.c

diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
index 54f74f8..30e3fb9 100644
--- a/sound/soc/blackfin/Kconfig
+++ b/sound/soc/blackfin/Kconfig
@@ -43,6 +43,19 @@ config SND_SOC_BFIN_EVAL_ADAU1373
 	  Note: This driver assumes that first ADAU1373 DAI is connected to the
 	  first SPORT port on the BF5XX board.
 
+config SND_SOC_BFIN_EVAL_ADAU1X61
+	tristate "Support for the EVAL-ADAU1X61 board on Blackfin eval boards"
+	depends on SND_BF5XX_I2S && I2C
+	select SND_BF5XX_SOC_I2S
+	select SND_SOC_ADAU1761
+	help
+	  Say Y if you want to add support for the Analog Devices EVAL-ADAU1X61
+	  board connected to one of the Blackfin evaluation boards like the
+	  BF5XX-STAMP or BF5XX-EZKIT.
+
+	  Note: This driver assumes that the ADAU1X61 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 ad0a6e9..91eb0e7 100644
--- a/sound/soc/blackfin/Makefile
+++ b/sound/soc/blackfin/Makefile
@@ -22,6 +22,7 @@ snd-ssm2602-objs := bf5xx-ssm2602.o
 snd-ad73311-objs := bf5xx-ad73311.o
 snd-ad193x-objs := bf5xx-ad193x.o
 snd-soc-bfin-eval-adau1373-objs := bfin-eval-adau1373.o
+snd-soc-bfin-eval-adau1x61-objs := bfin-eval-adau1x61.o
 snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o
 snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.o
 
@@ -31,5 +32,6 @@ 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_ADAU1X61) += snd-soc-bfin-eval-adau1x61.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-adau1x61.c b/sound/soc/blackfin/bfin-eval-adau1x61.c
new file mode 100644
index 0000000..87adac8
--- /dev/null
+++ b/sound/soc/blackfin/bfin-eval-adau1x61.c
@@ -0,0 +1,151 @@
+/*
+ * Machine driver for EVAL-ADAU1x61MINIZ on Analog Devices bfin
+ * evaluation boards.
+ *
+ * Copyright 2011-2013 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 <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "../codecs/adau17x1.h"
+
+static const struct snd_soc_dapm_widget bfin_eval_adau1x61_dapm_widgets[] = {
+	SND_SOC_DAPM_LINE("In 1", NULL),
+	SND_SOC_DAPM_LINE("In 2", NULL),
+	SND_SOC_DAPM_LINE("In 3-4", NULL),
+
+	SND_SOC_DAPM_LINE("Diff Out L", NULL),
+	SND_SOC_DAPM_LINE("Diff Out R", NULL),
+	SND_SOC_DAPM_LINE("Stereo Out", NULL),
+	SND_SOC_DAPM_HP("Capless HP Out", NULL),
+};
+
+static const struct snd_soc_dapm_route bfin_eval_adau1x61_dapm_routes[] = {
+	{ "LAUX", NULL, "In 3-4" },
+	{ "RAUX", NULL, "In 3-4" },
+	{ "LINP", NULL, "In 1" },
+	{ "LINN", NULL, "In 1"},
+	{ "RINP", NULL, "In 2" },
+	{ "RINN", NULL, "In 2" },
+
+	{ "In 1", NULL, "MICBIAS" },
+	{ "In 2", NULL, "MICBIAS" },
+
+	{ "Capless HP Out", NULL, "LHP" },
+	{ "Capless HP Out", NULL, "RHP" },
+	{ "Diff Out L", NULL, "LOUT" },
+	{ "Diff Out R", NULL, "ROUT" },
+	{ "Stereo Out", NULL, "LOUT" },
+	{ "Stereo Out", NULL, "ROUT" },
+};
+
+static int bfin_eval_adau1x61_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	int pll_rate;
+	int ret;
+
+	switch (params_rate(params)) {
+	case 48000:
+	case 8000:
+	case 12000:
+	case 16000:
+	case 24000:
+	case 32000:
+	case 96000:
+		pll_rate = 48000 * 1024;
+		break;
+	case 44100:
+	case 7350:
+	case 11025:
+	case 14700:
+	case 22050:
+	case 29400:
+	case 88200:
+		pll_rate = 44100 * 1024;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = snd_soc_dai_set_pll(codec_dai, ADAU17X1_PLL,
+			ADAU17X1_PLL_SRC_MCLK, 12288000, pll_rate);
+	if (ret)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, ADAU17X1_CLK_SRC_PLL, pll_rate,
+			SND_SOC_CLOCK_IN);
+
+	return ret;
+}
+
+static const struct snd_soc_ops bfin_eval_adau1x61_ops = {
+	.hw_params = bfin_eval_adau1x61_hw_params,
+};
+
+static struct snd_soc_dai_link bfin_eval_adau1x61_dai = {
+	.name			= "adau1x61",
+	.stream_name		= "adau1x61",
+	.cpu_dai_name		= "bfin-i2s.0",
+	.codec_dai_name		= "adau-hifi",
+	.platform_name		= "bfin-i2s-pcm-audio",
+	.codec_name		= "adau1761.0-0038",
+	.ops			= &bfin_eval_adau1x61_ops,
+	.dai_fmt		= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+					SND_SOC_DAIFMT_CBM_CFM,
+};
+
+static struct snd_soc_card bfin_eval_adau1x61 = {
+	.name			= "bfin-eval-adau1x61",
+	.driver_name		= "eval-adau1x61",
+	.dai_link		= &bfin_eval_adau1x61_dai,
+	.num_links		= 1,
+
+	.dapm_widgets		= bfin_eval_adau1x61_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(bfin_eval_adau1x61_dapm_widgets),
+	.dapm_routes		= bfin_eval_adau1x61_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(bfin_eval_adau1x61_dapm_routes),
+};
+
+static int bfin_eval_adau1x61_probe(struct platform_device *pdev)
+{
+	bfin_eval_adau1x61.dev = &pdev->dev;
+
+	return snd_soc_register_card(&bfin_eval_adau1x61);
+}
+
+static int bfin_eval_adau1x61_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_adau1x61_driver = {
+	.driver = {
+		.name = "bfin-eval-adau1x61",
+		.owner = THIS_MODULE,
+		.pm = &snd_soc_pm_ops,
+	},
+	.probe = bfin_eval_adau1x61_probe,
+	.remove = bfin_eval_adau1x61_remove,
+};
+module_platform_driver(bfin_eval_adau1x61_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ALSA SoC bfin adau1x61 driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bfin-eval-adau1x61");
-- 
1.8.0

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

* [PATCH 5/5] ASoC: Blackfin: ADAU1X81 eval board support
  2013-08-28 15:20 [PATCH 1/5] ASoC: Add ADAU1X61 and ADAU1X81 CODECs common code Lars-Peter Clausen
                   ` (2 preceding siblings ...)
  2013-08-28 15:20 ` [PATCH 4/5] ASoC: Blackfin: ADAU1X61 eval board support Lars-Peter Clausen
@ 2013-08-28 15:20 ` Lars-Peter Clausen
  2013-08-28 16:59 ` [PATCH 1/5] ASoC: Add ADAU1X61 and ADAU1X81 CODECs common code Mark Brown
  4 siblings, 0 replies; 13+ messages in thread
From: Lars-Peter Clausen @ 2013-08-28 15:20 UTC (permalink / raw)
  To: Mark Brown, Liam Girdwood; +Cc: alsa-devel, Lars-Peter Clausen

This patch adds a ASoC machine driver to support the EVAL-ADAU1X81 board
connected to a Analog Devices BF5XX evaluation board.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 sound/soc/blackfin/Kconfig              |  13 +++
 sound/soc/blackfin/Makefile             |   2 +
 sound/soc/blackfin/bfin-eval-adau1x81.c | 139 ++++++++++++++++++++++++++++++++
 3 files changed, 154 insertions(+)
 create mode 100644 sound/soc/blackfin/bfin-eval-adau1x81.c

diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
index 30e3fb9..87c3980 100644
--- a/sound/soc/blackfin/Kconfig
+++ b/sound/soc/blackfin/Kconfig
@@ -56,6 +56,19 @@ config SND_SOC_BFIN_EVAL_ADAU1X61
 	  Note: This driver assumes that the ADAU1X61 is connected to the
 	  first SPORT port on the BF5XX board.
 
+config SND_SOC_BFIN_EVAL_ADAU1X81
+	tristate "Support for the EVAL-ADAU1X81 boards on Blackfin eval boards"
+	depends on SND_BF5XX_I2S && I2C
+	select SND_BF5XX_SOC_I2S
+	select SND_SOC_ADAU1781
+	help
+	  Say Y if you want to add support for the Analog Devices EVAL-ADAU1X81
+	  board connected to one of the Blackfin evaluation boards like the
+	  BF5XX-STAMP or BF5XX-EZKIT.
+
+	  Note: This driver assumes that the ADAU1X81 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 91eb0e7..f21e948 100644
--- a/sound/soc/blackfin/Makefile
+++ b/sound/soc/blackfin/Makefile
@@ -23,6 +23,7 @@ snd-ad73311-objs := bf5xx-ad73311.o
 snd-ad193x-objs := bf5xx-ad193x.o
 snd-soc-bfin-eval-adau1373-objs := bfin-eval-adau1373.o
 snd-soc-bfin-eval-adau1x61-objs := bfin-eval-adau1x61.o
+snd-soc-bfin-eval-adau1x81-objs := bfin-eval-adau1x81.o
 snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o
 snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.o
 
@@ -33,5 +34,6 @@ 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_ADAU1X61) += snd-soc-bfin-eval-adau1x61.o
+obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X81) += snd-soc-bfin-eval-adau1x81.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-adau1x81.c b/sound/soc/blackfin/bfin-eval-adau1x81.c
new file mode 100644
index 0000000..74e3459
--- /dev/null
+++ b/sound/soc/blackfin/bfin-eval-adau1x81.c
@@ -0,0 +1,139 @@
+/*
+ * Machine driver for EVAL-ADAU1x81 on Analog Devices bfin
+ * evaluation boards.
+ *
+ * Copyright 2011-2013 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 <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "../codecs/adau17x1.h"
+
+static const struct snd_soc_dapm_widget bfin_eval_adau1x81_dapm_widgets[] = {
+	SND_SOC_DAPM_LINE("Stereo In", NULL),
+	SND_SOC_DAPM_LINE("Beep", NULL),
+
+	SND_SOC_DAPM_SPK("Speaker", NULL),
+	SND_SOC_DAPM_HP("Headphone", NULL),
+};
+
+static const struct snd_soc_dapm_route bfin_eval_adau1x81_dapm_routes[] = {
+	{ "BEEP", NULL, "Beep" },
+	{ "LMIC", NULL, "Stereo In" },
+	{ "LMIC", NULL, "Stereo In" },
+
+	{ "Headphone", NULL, "AOUTL" },
+	{ "Headphone", NULL, "AOUTR" },
+	{ "Speaker", NULL, "SP" },
+};
+
+static int bfin_eval_adau1x81_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	int pll_rate;
+	int ret;
+
+	switch (params_rate(params)) {
+	case 48000:
+	case 8000:
+	case 12000:
+	case 16000:
+	case 24000:
+	case 32000:
+	case 96000:
+		pll_rate = 48000 * 1024;
+		break;
+	case 44100:
+	case 7350:
+	case 11025:
+	case 14700:
+	case 22050:
+	case 29400:
+	case 88200:
+		pll_rate = 44100 * 1024;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = snd_soc_dai_set_pll(codec_dai, ADAU17X1_PLL,
+			ADAU17X1_PLL_SRC_MCLK, 12288000, pll_rate);
+	if (ret)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, ADAU17X1_CLK_SRC_PLL, pll_rate,
+			SND_SOC_CLOCK_IN);
+
+	return ret;
+}
+
+static const struct snd_soc_ops bfin_eval_adau1x81_ops = {
+	.hw_params = bfin_eval_adau1x81_hw_params,
+};
+
+static struct snd_soc_dai_link bfin_eval_adau1x81_dai = {
+	.name			= "adau1x81",
+	.stream_name		= "adau1x81",
+	.cpu_dai_name		= "bfin-i2s.0",
+	.codec_dai_name		= "adau-hifi",
+	.platform_name		= "bfin-i2s-pcm-audio",
+	.codec_name		= "adau1781.0-0038",
+	.ops			= &bfin_eval_adau1x81_ops,
+	.dai_fmt		= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+					SND_SOC_DAIFMT_CBM_CFM,
+};
+
+static struct snd_soc_card bfin_eval_adau1x81 = {
+	.name			= "bfin-eval-adau1x81",
+	.driver_name		= "eval-adau1x81",
+	.dai_link		= &bfin_eval_adau1x81_dai,
+	.num_links		= 1,
+
+	.dapm_widgets		= bfin_eval_adau1x81_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(bfin_eval_adau1x81_dapm_widgets),
+	.dapm_routes		= bfin_eval_adau1x81_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(bfin_eval_adau1x81_dapm_routes),
+};
+
+static int bfin_eval_adau1x81_probe(struct platform_device *pdev)
+{
+	bfin_eval_adau1x81.dev = &pdev->dev;
+
+	return snd_soc_register_card(&bfin_eval_adau1x81);
+}
+
+static int bfin_eval_adau1x81_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_adau1x81_driver = {
+	.driver = {
+		.name = "bfin-eval-adau1x81",
+		.owner = THIS_MODULE,
+		.pm = &snd_soc_pm_ops,
+	},
+	.probe = bfin_eval_adau1x81_probe,
+	.remove = bfin_eval_adau1x81_remove,
+};
+module_platform_driver(bfin_eval_adau1x81_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ALSA SoC bfin adau1x81 driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bfin-eval-adau1x81");
-- 
1.8.0

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

* Re: [PATCH 1/5] ASoC: Add ADAU1X61 and ADAU1X81 CODECs common code
  2013-08-28 15:20 [PATCH 1/5] ASoC: Add ADAU1X61 and ADAU1X81 CODECs common code Lars-Peter Clausen
                   ` (3 preceding siblings ...)
  2013-08-28 15:20 ` [PATCH 5/5] ASoC: Blackfin: ADAU1X81 " Lars-Peter Clausen
@ 2013-08-28 16:59 ` Mark Brown
  2013-08-29  7:58   ` Lars-Peter Clausen
  4 siblings, 1 reply; 13+ messages in thread
From: Mark Brown @ 2013-08-28 16:59 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: alsa-devel, Liam Girdwood


[-- Attachment #1.1: Type: text/plain, Size: 1751 bytes --]

On Wed, Aug 28, 2013 at 05:20:09PM +0200, Lars-Peter Clausen wrote:

> +static void adau17x1_check_aifclk(struct snd_soc_codec *codec)
> +{
> +	struct adau *adau = snd_soc_codec_get_drvdata(codec);
> +
> +	/* If we are in master mode we need to generate bit- and frameclock,
> +	 * regardless of whether there is an active path or not */
> +	if (codec->active && adau->master)
> +		snd_soc_dapm_force_enable_pin(&codec->dapm, "AIFCLK");
> +	else
> +		snd_soc_dapm_disable_pin(&codec->dapm, "AIFCLK");
> +	snd_soc_dapm_sync(&codec->dapm);
> +}

I think this is eminently sensible but it seems like it should be a
framework feature.  That'd have to enable an actual AIF slot though
which is a bit fun, which slot do we enable for example?

> +static int adau17x1_set_dai_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
> +{
> +	struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
> +
> +	if (div < 0 || div > 4)
> +		return -EINVAL;
> +
> +	adau->sysclk_div = div;
> +
> +	return regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
> +		ADAU17X1_CLOCK_CONTROL_INFREQ_MASK, (div - 1) << 1);
> +}

What's this doing?  It'd be better to have a specific ID and check that
too.

> +int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
> +	enum adau17x1_micbias_voltage micbias)
> +{
> +	struct adau *adau = snd_soc_codec_get_drvdata(codec);
> +
> +	switch (micbias) {
> +	case ADAU17X1_MICBIAS_0_90_AVDD:
> +	case ADAU17X1_MICBIAS_0_65_AVDD:
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	regmap_write(adau->regmap, ADAU17X1_MICBIAS, micbias << 2);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(adau17x1_set_micbias_voltage);

When would a machine driver use this (as opposed to just letting it be
set by platform data)?

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



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

* Re: [PATCH 2/5] ASoC: Add ADAU1361/ADAU1761 audio CODEC support
  2013-08-28 15:20 ` [PATCH 2/5] ASoC: Add ADAU1361/ADAU1761 audio CODEC support Lars-Peter Clausen
@ 2013-08-28 17:24   ` Mark Brown
  2013-08-29  8:02     ` Lars-Peter Clausen
  0 siblings, 1 reply; 13+ messages in thread
From: Mark Brown @ 2013-08-28 17:24 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: alsa-devel, Liam Girdwood


[-- Attachment #1.1: Type: text/plain, Size: 992 bytes --]

On Wed, Aug 28, 2013 at 05:20:10PM +0200, Lars-Peter Clausen wrote:

> +static const char * const adau1761_bias_select_text[] = {
> +	"Normal operation", "Enhanced performance", "Power saving",
> +};
> +
> +static const char * const adau1761_bias_select_extreme_text[] = {
> +	"Normal operation", "Extreme power saving", "Enhanced performance",
> +	"Power saving",
> +};

Not a requirement and perhaps it has no effect but these seem like they
ought to integrate with the bias level configuration - since only the
clocks seem to be managed by set_bias_level() the driver could
presumably save a bit of power by dropping down to the minimal power
consumption bias while the device is idle.  This is what the STANDBY to
ON transition is all about.

> +static const struct snd_kcontrol_new adau1761_jack_detect_controls[] = {
> +	SOC_SINGLE("Jack Detect Switch", ADAU1761_DIGMIC_JACKDETECT, 4, 1, 0),
> +};

Why is this a user visible control and not using the jack detection
framework support?

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



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

* Re: [PATCH 4/5] ASoC: Blackfin: ADAU1X61 eval board support
  2013-08-28 15:20 ` [PATCH 4/5] ASoC: Blackfin: ADAU1X61 eval board support Lars-Peter Clausen
@ 2013-08-28 17:24   ` Mark Brown
  0 siblings, 0 replies; 13+ messages in thread
From: Mark Brown @ 2013-08-28 17:24 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: alsa-devel, Liam Girdwood


[-- Attachment #1.1: Type: text/plain, Size: 231 bytes --]

On Wed, Aug 28, 2013 at 05:20:12PM +0200, Lars-Peter Clausen wrote:
> This patch adds a ASoC machine driver to support the EVAL-ADAU1X61 board
> connected to a Analog Devices BF5XX evaluation board.

The machine drivers look good.

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



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

* Re: [PATCH 1/5] ASoC: Add ADAU1X61 and ADAU1X81 CODECs common code
  2013-08-28 16:59 ` [PATCH 1/5] ASoC: Add ADAU1X61 and ADAU1X81 CODECs common code Mark Brown
@ 2013-08-29  7:58   ` Lars-Peter Clausen
  2013-08-29 10:13     ` Mark Brown
  0 siblings, 1 reply; 13+ messages in thread
From: Lars-Peter Clausen @ 2013-08-29  7:58 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel, Liam Girdwood

On 08/28/2013 06:59 PM, Mark Brown wrote:
> On Wed, Aug 28, 2013 at 05:20:09PM +0200, Lars-Peter Clausen wrote:
> 
>> +static void adau17x1_check_aifclk(struct snd_soc_codec *codec)
>> +{
>> +	struct adau *adau = snd_soc_codec_get_drvdata(codec);
>> +
>> +	/* If we are in master mode we need to generate bit- and frameclock,
>> +	 * regardless of whether there is an active path or not */
>> +	if (codec->active && adau->master)
>> +		snd_soc_dapm_force_enable_pin(&codec->dapm, "AIFCLK");
>> +	else
>> +		snd_soc_dapm_disable_pin(&codec->dapm, "AIFCLK");
>> +	snd_soc_dapm_sync(&codec->dapm);
>> +}
> 
> I think this is eminently sensible but it seems like it should be a
> framework feature.  That'd have to enable an actual AIF slot though
> which is a bit fun, which slot do we enable for example?

It would be nice if this could be handled by the core. But how? One option
might be tracking the the DAI format in the core and power up the DAI widget
in DAPM if the stream is active regardless of if there is an active path if
the DAI is in master mode. That may power up more than what is needed
though. E.g. for this CODEC the clock generator and the rest of the DAI can
be powered up independently and we only need to power up the clock generator.

> 
>> +static int adau17x1_set_dai_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
>> +{
>> +	struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
>> +
>> +	if (div < 0 || div > 4)
>> +		return -EINVAL;
>> +
>> +	adau->sysclk_div = div;
>> +
>> +	return regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
>> +		ADAU17X1_CLOCK_CONTROL_INFREQ_MASK, (div - 1) << 1);
>> +}
> 
> What's this doing?  It'd be better to have a specific ID and check that
> too.

Setting the relationship between the external clock rate and the internal
base sample rate. There might be a better way to do this though.

> 
>> +int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
>> +	enum adau17x1_micbias_voltage micbias)
>> +{
>> +	struct adau *adau = snd_soc_codec_get_drvdata(codec);
>> +
>> +	switch (micbias) {
>> +	case ADAU17X1_MICBIAS_0_90_AVDD:
>> +	case ADAU17X1_MICBIAS_0_65_AVDD:
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	regmap_write(adau->regmap, ADAU17X1_MICBIAS, micbias << 2);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(adau17x1_set_micbias_voltage);
> 
> When would a machine driver use this (as opposed to just letting it be
> set by platform data)?

It is not meant to be used by machine drivers, but by the adau1761 and
adau1781 CODEC drivers, which set the micbias based on platform data.

Thanks for the fast review,
- Lars

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

* Re: [PATCH 2/5] ASoC: Add ADAU1361/ADAU1761 audio CODEC support
  2013-08-28 17:24   ` Mark Brown
@ 2013-08-29  8:02     ` Lars-Peter Clausen
  2013-08-29  9:53       ` Mark Brown
  0 siblings, 1 reply; 13+ messages in thread
From: Lars-Peter Clausen @ 2013-08-29  8:02 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel, Liam Girdwood

On 08/28/2013 07:24 PM, Mark Brown wrote:
> On Wed, Aug 28, 2013 at 05:20:10PM +0200, Lars-Peter Clausen wrote:
> 
>> +static const char * const adau1761_bias_select_text[] = {
>> +	"Normal operation", "Enhanced performance", "Power saving",
>> +};
>> +
>> +static const char * const adau1761_bias_select_extreme_text[] = {
>> +	"Normal operation", "Extreme power saving", "Enhanced performance",
>> +	"Power saving",
>> +};
> 
> Not a requirement and perhaps it has no effect but these seem like they
> ought to integrate with the bias level configuration - since only the
> clocks seem to be managed by set_bias_level() the driver could
> presumably save a bit of power by dropping down to the minimal power
> consumption bias while the device is idle.  This is what the STANDBY to
> ON transition is all about.

Hm, I'm not sure if it makes a difference since all the components have
individual power control bits that are managed by DAPM. I would expect the
bias current setting to only make a difference if the component is actually
powered up. I'll check with the designers, but I think this can still be
improved later on if it turns out that it saves power.

> 
>> +static const struct snd_kcontrol_new adau1761_jack_detect_controls[] = {
>> +	SOC_SINGLE("Jack Detect Switch", ADAU1761_DIGMIC_JACKDETECT, 4, 1, 0),
>> +};
> 
> Why is this a user visible control and not using the jack detection
> framework support?
> 

The CODEC unfortunately does not support reporting the jack state. All it
can do is mute the speaker output in hardware if something is plugged into
the headphone jack. The switch allows the users to control whether the
output should be auto-muted or not.

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

* Re: [PATCH 2/5] ASoC: Add ADAU1361/ADAU1761 audio CODEC support
  2013-08-29  8:02     ` Lars-Peter Clausen
@ 2013-08-29  9:53       ` Mark Brown
  0 siblings, 0 replies; 13+ messages in thread
From: Mark Brown @ 2013-08-29  9:53 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: alsa-devel, Liam Girdwood


[-- Attachment #1.1: Type: text/plain, Size: 1734 bytes --]

On Thu, Aug 29, 2013 at 10:02:47AM +0200, Lars-Peter Clausen wrote:
> On 08/28/2013 07:24 PM, Mark Brown wrote:

> > Not a requirement and perhaps it has no effect but these seem like they
> > ought to integrate with the bias level configuration - since only the
> > clocks seem to be managed by set_bias_level() the driver could
> > presumably save a bit of power by dropping down to the minimal power
> > consumption bias while the device is idle.  This is what the STANDBY to
> > ON transition is all about.

> Hm, I'm not sure if it makes a difference since all the components have
> individual power control bits that are managed by DAPM. I would expect the
> bias current setting to only make a difference if the component is actually
> powered up. I'll check with the designers, but I think this can still be
> improved later on if it turns out that it saves power.

Given that the device has non-capless headphone outputs I'd expect
there's something being maintained normally, though it's possible that
it's still ground referenced.

> >> +static const struct snd_kcontrol_new adau1761_jack_detect_controls[] = {
> >> +	SOC_SINGLE("Jack Detect Switch", ADAU1761_DIGMIC_JACKDETECT, 4, 1, 0),
> >> +};

> > Why is this a user visible control and not using the jack detection
> > framework support?

> The CODEC unfortunately does not support reporting the jack state. All it
> can do is mute the speaker output in hardware if something is plugged into
> the headphone jack. The switch allows the users to control whether the
> output should be auto-muted or not.

In that case it should be called something like "Speaker Automute
Switch" or similar.  It's a bit unfortuante that there's not even any
readback for the function.

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



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

* Re: [PATCH 1/5] ASoC: Add ADAU1X61 and ADAU1X81 CODECs common code
  2013-08-29  7:58   ` Lars-Peter Clausen
@ 2013-08-29 10:13     ` Mark Brown
  0 siblings, 0 replies; 13+ messages in thread
From: Mark Brown @ 2013-08-29 10:13 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: alsa-devel, Liam Girdwood


[-- Attachment #1.1: Type: text/plain, Size: 1749 bytes --]

On Thu, Aug 29, 2013 at 09:58:19AM +0200, Lars-Peter Clausen wrote:

> It would be nice if this could be handled by the core. But how? One option
> might be tracking the the DAI format in the core and power up the DAI widget
> in DAPM if the stream is active regardless of if there is an active path if
> the DAI is in master mode. That may power up more than what is needed
> though. E.g. for this CODEC the clock generator and the rest of the DAI can
> be powered up independently and we only need to power up the clock generator.

Yes, that was what I was thinking of.  We can provide a mechanism to
allow the driver to specify a specific widget for the clocks if one
exists and fall back on the DAI widgets otherwise.

> >> +static int adau17x1_set_dai_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
> >> +{

> > What's this doing?  It'd be better to have a specific ID and check that
> > too.

> Setting the relationship between the external clock rate and the internal
> base sample rate. There might be a better way to do this though.

If it's specifying two clock rates then specifying them both as actual
rates might make sense.  I take it it's not possible for the driver to
automatically figure out what the most sensible rate is?

> >> +int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
> >> +	enum adau17x1_micbias_voltage micbias)

> > When would a machine driver use this (as opposed to just letting it be
> > set by platform data)?

> It is not meant to be used by machine drivers, but by the adau1761 and
> adau1781 CODEC drivers, which set the micbias based on platform data.

Ah, in that case how about having generic platform data for the core and
just passing that through to a platform data parsing function?

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



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

* [PATCH 4/5] ASoC: Blackfin: ADAU1X61 eval board support
  2014-05-27  8:53 Lars-Peter Clausen
@ 2014-05-27  8:53 ` Lars-Peter Clausen
  0 siblings, 0 replies; 13+ messages in thread
From: Lars-Peter Clausen @ 2014-05-27  8:53 UTC (permalink / raw)
  To: Mark Brown, Liam Girdwood; +Cc: alsa-devel, Lars-Peter Clausen

This patch adds a ASoC machine driver to support the EVAL-ADAU1X61 board
connected to a Analog Devices BF5XX evaluation board.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 sound/soc/blackfin/Kconfig              |  13 +++
 sound/soc/blackfin/Makefile             |   2 +
 sound/soc/blackfin/bfin-eval-adau1x61.c | 142 ++++++++++++++++++++++++++++++++
 3 files changed, 157 insertions(+)
 create mode 100644 sound/soc/blackfin/bfin-eval-adau1x61.c

diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
index 6347d59..8c3a87b 100644
--- a/sound/soc/blackfin/Kconfig
+++ b/sound/soc/blackfin/Kconfig
@@ -43,6 +43,19 @@ config SND_SOC_BFIN_EVAL_ADAU1373
 	  Note: This driver assumes that first ADAU1373 DAI is connected to the
 	  first SPORT port on the BF5XX board.
 
+config SND_SOC_BFIN_EVAL_ADAU1X61
+	tristate "Support for the EVAL-ADAU1X61 board on Blackfin eval boards"
+	depends on SND_BF5XX_I2S && I2C
+	select SND_BF5XX_SOC_I2S
+	select SND_SOC_ADAU1761_I2C
+	help
+	  Say Y if you want to add support for the Analog Devices EVAL-ADAU1X61
+	  board connected to one of the Blackfin evaluation boards like the
+	  BF5XX-STAMP or BF5XX-EZKIT.
+
+	  Note: This driver assumes that the ADAU1X61 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 && SND_SOC_I2C_AND_SPI
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile
index ad0a6e9..91eb0e7 100644
--- a/sound/soc/blackfin/Makefile
+++ b/sound/soc/blackfin/Makefile
@@ -22,6 +22,7 @@ snd-ssm2602-objs := bf5xx-ssm2602.o
 snd-ad73311-objs := bf5xx-ad73311.o
 snd-ad193x-objs := bf5xx-ad193x.o
 snd-soc-bfin-eval-adau1373-objs := bfin-eval-adau1373.o
+snd-soc-bfin-eval-adau1x61-objs := bfin-eval-adau1x61.o
 snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o
 snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.o
 
@@ -31,5 +32,6 @@ 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_ADAU1X61) += snd-soc-bfin-eval-adau1x61.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-adau1x61.c b/sound/soc/blackfin/bfin-eval-adau1x61.c
new file mode 100644
index 0000000..3011906
--- /dev/null
+++ b/sound/soc/blackfin/bfin-eval-adau1x61.c
@@ -0,0 +1,142 @@
+/*
+ * Machine driver for EVAL-ADAU1x61MINIZ on Analog Devices bfin
+ * evaluation boards.
+ *
+ * Copyright 2011-2014 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 <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "../codecs/adau17x1.h"
+
+static const struct snd_soc_dapm_widget bfin_eval_adau1x61_dapm_widgets[] = {
+	SND_SOC_DAPM_LINE("In 1", NULL),
+	SND_SOC_DAPM_LINE("In 2", NULL),
+	SND_SOC_DAPM_LINE("In 3-4", NULL),
+
+	SND_SOC_DAPM_LINE("Diff Out L", NULL),
+	SND_SOC_DAPM_LINE("Diff Out R", NULL),
+	SND_SOC_DAPM_LINE("Stereo Out", NULL),
+	SND_SOC_DAPM_HP("Capless HP Out", NULL),
+};
+
+static const struct snd_soc_dapm_route bfin_eval_adau1x61_dapm_routes[] = {
+	{ "LAUX", NULL, "In 3-4" },
+	{ "RAUX", NULL, "In 3-4" },
+	{ "LINP", NULL, "In 1" },
+	{ "LINN", NULL, "In 1"},
+	{ "RINP", NULL, "In 2" },
+	{ "RINN", NULL, "In 2" },
+
+	{ "In 1", NULL, "MICBIAS" },
+	{ "In 2", NULL, "MICBIAS" },
+
+	{ "Capless HP Out", NULL, "LHP" },
+	{ "Capless HP Out", NULL, "RHP" },
+	{ "Diff Out L", NULL, "LOUT" },
+	{ "Diff Out R", NULL, "ROUT" },
+	{ "Stereo Out", NULL, "LOUT" },
+	{ "Stereo Out", NULL, "ROUT" },
+};
+
+static int bfin_eval_adau1x61_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	int pll_rate;
+	int ret;
+
+	switch (params_rate(params)) {
+	case 48000:
+	case 8000:
+	case 12000:
+	case 16000:
+	case 24000:
+	case 32000:
+	case 96000:
+		pll_rate = 48000 * 1024;
+		break;
+	case 44100:
+	case 7350:
+	case 11025:
+	case 14700:
+	case 22050:
+	case 29400:
+	case 88200:
+		pll_rate = 44100 * 1024;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = snd_soc_dai_set_pll(codec_dai, ADAU17X1_PLL,
+			ADAU17X1_PLL_SRC_MCLK, 12288000, pll_rate);
+	if (ret)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, ADAU17X1_CLK_SRC_PLL, pll_rate,
+			SND_SOC_CLOCK_IN);
+
+	return ret;
+}
+
+static const struct snd_soc_ops bfin_eval_adau1x61_ops = {
+	.hw_params = bfin_eval_adau1x61_hw_params,
+};
+
+static struct snd_soc_dai_link bfin_eval_adau1x61_dai = {
+	.name = "adau1x61",
+	.stream_name = "adau1x61",
+	.cpu_dai_name = "bfin-i2s.0",
+	.codec_dai_name = "adau-hifi",
+	.platform_name = "bfin-i2s-pcm-audio",
+	.codec_name = "adau1761.0-0038",
+	.ops = &bfin_eval_adau1x61_ops,
+	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+		SND_SOC_DAIFMT_CBM_CFM,
+};
+
+static struct snd_soc_card bfin_eval_adau1x61 = {
+	.name = "bfin-eval-adau1x61",
+	.driver_name = "eval-adau1x61",
+	.dai_link = &bfin_eval_adau1x61_dai,
+	.num_links = 1,
+
+	.dapm_widgets = bfin_eval_adau1x61_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1x61_dapm_widgets),
+	.dapm_routes = bfin_eval_adau1x61_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1x61_dapm_routes),
+	.fully_routed = true,
+};
+
+static int bfin_eval_adau1x61_probe(struct platform_device *pdev)
+{
+	bfin_eval_adau1x61.dev = &pdev->dev;
+
+	return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1x61);
+}
+
+static struct platform_driver bfin_eval_adau1x61_driver = {
+	.driver = {
+		.name = "bfin-eval-adau1x61",
+		.owner = THIS_MODULE,
+		.pm = &snd_soc_pm_ops,
+	},
+	.probe = bfin_eval_adau1x61_probe,
+};
+module_platform_driver(bfin_eval_adau1x61_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ALSA SoC bfin adau1x61 driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bfin-eval-adau1x61");
-- 
1.8.0

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

end of thread, other threads:[~2014-05-27  8:53 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-08-28 15:20 [PATCH 1/5] ASoC: Add ADAU1X61 and ADAU1X81 CODECs common code Lars-Peter Clausen
2013-08-28 15:20 ` [PATCH 2/5] ASoC: Add ADAU1361/ADAU1761 audio CODEC support Lars-Peter Clausen
2013-08-28 17:24   ` Mark Brown
2013-08-29  8:02     ` Lars-Peter Clausen
2013-08-29  9:53       ` Mark Brown
2013-08-28 15:20 ` [PATCH 3/5] ASoC: Add ADAU1381/ADAU1781 " Lars-Peter Clausen
2013-08-28 15:20 ` [PATCH 4/5] ASoC: Blackfin: ADAU1X61 eval board support Lars-Peter Clausen
2013-08-28 17:24   ` Mark Brown
2013-08-28 15:20 ` [PATCH 5/5] ASoC: Blackfin: ADAU1X81 " Lars-Peter Clausen
2013-08-28 16:59 ` [PATCH 1/5] ASoC: Add ADAU1X61 and ADAU1X81 CODECs common code Mark Brown
2013-08-29  7:58   ` Lars-Peter Clausen
2013-08-29 10:13     ` Mark Brown
2014-05-27  8:53 Lars-Peter Clausen
2014-05-27  8:53 ` [PATCH 4/5] ASoC: Blackfin: ADAU1X61 eval board support Lars-Peter Clausen

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.