diff mbox

[v5] ASoC: cs53l30: Add support for Cirrus Logic CS53L30

Message ID 1458082066-2859-1-git-send-email-tim.howe@cirrus.com (mailing list archive)
State New, archived
Headers show

Commit Message

Howe, Tim March 15, 2016, 10:47 p.m. UTC
From: Tim Howe <tim.howe@cirrus.com>

Signed-off-by: Tim Howe <tim.howe@cirrus.com>
---
 .../devicetree/bindings/sound/cs53l30.txt          |  21 +
 sound/soc/codecs/Kconfig                           |   6 +
 sound/soc/codecs/Makefile                          |   2 +
 sound/soc/codecs/cs53l30.c                         | 943 +++++++++++++++++++++
 sound/soc/codecs/cs53l30.h                         | 282 ++++++
 5 files changed, 1254 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/cs53l30.txt
 create mode 100644 sound/soc/codecs/cs53l30.c
 create mode 100644 sound/soc/codecs/cs53l30.h

diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
new file mode 100644
index 0000000..2002ecd
+++ b/sound/soc/codecs/cs53l30.c
@@ -0,0 +1,943 @@
+/*
+ * cs53l30.c  --  CS53l30 ALSA Soc Audio driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Authors: Paul Handrigan <Paul.Handrigan@cirrus.com>,
+ *          Tim Howe <Tim.Howe@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include "cs53l30.h"
+
+struct  cs53l30_private {
+	struct regmap			*regmap;
+	struct gpio_desc		*reset_gpio;
+	u8				asp_config_ctl;
+	u32				mclk;
+};
+
+static const struct reg_default cs53l30_reg_defaults[] = {
+	{ CS53L30_PWRCTL,		CS53L30_THMS_PDN },
+	{ CS53L30_MCLKCTL,		CS53L30_MCLK_DIV_DFLT },
+	{ CS53L30_INT_SR_CTL,		CS53L30_INTRNL_FS_DFLT },
+	{ CS53L30_MICBIAS_CTL,		CS53L30_MIC_BIAS_DFLT },
+	{ CS53L30_ASPCFG_CTL,		CS53L30_ASP_RATE_48K },
+	{ CS53L30_ASP1_CTL,		CS53L30_ASP1_TDM_PDN },
+	{ CS53L30_ASP1_TDMTX_CTL1,	CS53L30_ASP1_CHTX_SLT47 },
+	{ CS53L30_ASP1_TDMTX_CTL2,	CS53L30_ASP1_CHTX_SLT47 },
+	{ CS53L30_ASP1_TDMTX_CTL3,	CS53L30_ASP1_CHTX_SLT47 },
+	{ CS53L30_ASP1_TDMTX_CTL4,	CS53L30_ASP1_CHTX_SLT47 },
+	{ CS53L30_ASP1_TDMTX_EN1,	CS53L30_ASP_TX_DISABLED },
+	{ CS53L30_ASP1_TDMTX_EN2,	CS53L30_ASP_TX_DISABLED },
+	{ CS53L30_ASP1_TDMTX_EN3,	CS53L30_ASP_TX_DISABLED },
+	{ CS53L30_ASP1_TDMTX_EN4,	CS53L30_ASP_TX_DISABLED },
+	{ CS53L30_ASP1_TDMTX_EN5,	CS53L30_ASP_TX_DISABLED },
+	{ CS53L30_ASP1_TDMTX_EN6,	CS53L30_ASP_TX_DISABLED },
+	{ CS53L30_ASP2_CTL,		CS53L30_ASP2_CTRL_DFLT },
+	{ CS53L30_SFT_RAMP,		CS53L30_SFT_RMP_DFLT },
+	{ CS53L30_LRCLK_CTL1,		CS53L30_LRCK_CTLX_DFLT },
+	{ CS53L30_LRCLK_CTL2,		CS53L30_LRCK_CTLX_DFLT },
+	{ CS53L30_MUTEP_CTL1,		CS53L30_MUTE_CTRL1_DFLT },
+	{ CS53L30_MUTEP_CTL2,		CS53L30_MUTE_PDN_ULP },
+	{ CS53L30_INBIAS_CTL1,		CS53L30_INBIAS_X_DFLT },
+	{ CS53L30_INBIAS_CTL2,		CS53L30_INBIAS_X_DFLT },
+	{ CS53L30_DMIC1_STR_CTL,	CS53L30_DMIC1_ST_DFLT },
+	{ CS53L30_DMIC2_STR_CTL,	CS53L30_DMIC2_ST_DFLT },
+	{ CS53L30_ADCDMIC1_CTL1,	CS53L30_ADC1_ON_AB_IN },
+	{ CS53L30_ADCDMIC1_CTL2,	CS53L30_A1_D1_CTL2_DFLT },
+	{ CS53L30_ADC1_CTL3,		CS53L30_ADC1_HPF_EN },
+	{ CS53L30_ADC1_NG_CTL,		CS53L30_ADCX_ZERO_DFLT },
+	{ CS53L30_ADC1A_AFE_CTL,	CS53L30_ADCX_ZERO_DFLT },
+	{ CS53L30_ADC1B_AFE_CTL,	CS53L30_ADCX_ZERO_DFLT },
+	{ CS53L30_ADC1A_DIG_VOL,	CS53L30_ADCX_ZERO_DFLT },
+	{ CS53L30_ADC1B_DIG_VOL,	CS53L30_ADCX_ZERO_DFLT },
+	{ CS53L30_ADCDMIC2_CTL1,	CS53L30_ADC2_ON_AB_IN },
+	{ CS53L30_ADCDMIC2_CTL2,	CS53L30_ADCX_ZERO_DFLT },
+	{ CS53L30_ADC2_CTL3,		CS53L30_ADC2_HPF_EN },
+	{ CS53L30_ADC2_NG_CTL,		CS53L30_ADCX_ZERO_DFLT },
+	{ CS53L30_ADC2A_AFE_CTL,	CS53L30_ADCX_ZERO_DFLT },
+	{ CS53L30_ADC2B_AFE_CTL,	CS53L30_ADCX_ZERO_DFLT },
+	{ CS53L30_ADC2A_DIG_VOL,	CS53L30_ADCX_ZERO_DFLT },
+	{ CS53L30_ADC2B_DIG_VOL,	CS53L30_ADCX_ZERO_DFLT },
+	{ CS53L30_INT_MASK,		CS53L30_DEVICE_INT_MASK },
+};
+
+static bool cs53l30_volatile_register(struct device *dev, unsigned int reg)
+{
+	if (reg == CS53L30_IS)
+		return true;
+	else
+		return false;
+}
+
+static bool cs53l30_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS53L30_DEVID_AB:
+	case CS53L30_DEVID_CD:
+	case CS53L30_DEVID_E:
+	case CS53L30_REVID:
+	case CS53L30_PWRCTL:
+	case CS53L30_MCLKCTL:
+	case CS53L30_INT_SR_CTL:
+	case CS53L30_MICBIAS_CTL:
+	case CS53L30_ASPCFG_CTL:
+	case CS53L30_ASP1_CTL:
+	case CS53L30_ASP1_TDMTX_CTL1:
+	case CS53L30_ASP1_TDMTX_CTL2:
+	case CS53L30_ASP1_TDMTX_CTL3:
+	case CS53L30_ASP1_TDMTX_CTL4:
+	case CS53L30_ASP1_TDMTX_EN1:
+	case CS53L30_ASP1_TDMTX_EN2:
+	case CS53L30_ASP1_TDMTX_EN3:
+	case CS53L30_ASP1_TDMTX_EN4:
+	case CS53L30_ASP1_TDMTX_EN5:
+	case CS53L30_ASP1_TDMTX_EN6:
+	case CS53L30_ASP2_CTL:
+	case CS53L30_SFT_RAMP:
+	case CS53L30_LRCLK_CTL1:
+	case CS53L30_LRCLK_CTL2:
+	case CS53L30_MUTEP_CTL1:
+	case CS53L30_MUTEP_CTL2:
+	case CS53L30_INBIAS_CTL1:
+	case CS53L30_INBIAS_CTL2:
+	case CS53L30_DMIC1_STR_CTL:
+	case CS53L30_DMIC2_STR_CTL:
+	case CS53L30_ADCDMIC1_CTL1:
+	case CS53L30_ADCDMIC1_CTL2:
+	case CS53L30_ADC1_CTL3:
+	case CS53L30_ADC1_NG_CTL:
+	case CS53L30_ADC1A_AFE_CTL:
+	case CS53L30_ADC1B_AFE_CTL:
+	case CS53L30_ADC1A_DIG_VOL:
+	case CS53L30_ADC1B_DIG_VOL:
+	case CS53L30_ADCDMIC2_CTL1:
+	case CS53L30_ADCDMIC2_CTL2:
+	case CS53L30_ADC2_CTL3:
+	case CS53L30_ADC2_NG_CTL:
+	case CS53L30_ADC2A_AFE_CTL:
+	case CS53L30_ADC2B_AFE_CTL:
+	case CS53L30_ADC2A_DIG_VOL:
+	case CS53L30_ADC2B_DIG_VOL:
+	case CS53L30_INT_MASK:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static DECLARE_TLV_DB_SCALE(adc_boost_tlv, 0, 2000, 0);
+static DECLARE_TLV_DB_SCALE(adc_ng_boost_tlv, 0, 3000, 0);
+static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(dig_tlv, -9600, 100, 1);
+
+static const char * const input1_sel_text[] = { "DMIC1 On AB In",
+	"DMIC1 On A In", "DMIC1 On B In", "ADC1 On AB In", "ADC1 On A In",
+	"ADC1 On B In", "DMIC1 Off ADC1 Off", };
+
+unsigned int const input1_sel_values[] = { CS53L30_DMIC1_ON_AB_IN,
+	CS53L30_DMIC1_ON_A_IN, CS53L30_DMIC1_ON_B_IN, CS53L30_ADC1_ON_AB_IN,
+	CS53L30_ADC1_ON_A_IN, CS53L30_ADC1_ON_B_IN, CS53L30_D1_OFF_A1_OFF, };
+
+static const char * const input2_sel_text[] = { "DMIC2 On AB In",
+	"DMIC2 On A In", "DMIC2 On B In", "ADC2 On AB In", "ADC2 On A In",
+	"ADC2 On B In", "DMIC2 Off ADC2 Off", };
+
+unsigned int const input2_sel_values[] = { CS53L30_DMIC2_ON_AB_IN,
+	CS53L30_DMIC2_ON_A_IN, CS53L30_DMIC2_ON_B_IN, CS53L30_ADC2_ON_AB_IN,
+	CS53L30_ADC2_ON_A_IN, CS53L30_ADC2_ON_B_IN, CS53L30_D2_OFF_A2_OFF, };
+
+static const char * const input1_route_sel_text[] = { "ADC1_SEL",
+						      "DMIC1_SEL" };
+
+static const struct soc_enum input1_route_sel_enum =
+	SOC_ENUM_SINGLE(CS53L30_ADCDMIC1_CTL1, 0,
+		ARRAY_SIZE(input1_route_sel_text), input1_route_sel_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(input1_sel_enum, CS53L30_ADCDMIC1_CTL1, 0,
+		CS53L30_A1_D1_PDN_MASK, input1_sel_text, input1_sel_values);
+
+static const struct snd_kcontrol_new input1_route_sel_mux =
+	SOC_DAPM_ENUM("Input 1 Route", input1_route_sel_enum);
+
+static const char * const input2_route_sel_text[] = { "ADC2_SEL",
+						      "DMIC2_SEL" };
+
+/* Note: CS53L30_ADCDMIC1_CTL1 CH_TYPE controls inputs 1 and 2 */
+static const struct soc_enum input2_route_sel_enum =
+	SOC_ENUM_SINGLE(CS53L30_ADCDMIC1_CTL1, 0,
+		ARRAY_SIZE(input2_route_sel_text), input2_route_sel_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(input2_sel_enum, CS53L30_ADCDMIC2_CTL1, 0,
+		CS53L30_A1_D1_PDN_MASK, input2_sel_text, input2_sel_values);
+
+static const struct snd_kcontrol_new input2_route_sel_mux =
+	SOC_DAPM_ENUM("Input 2 Route", input2_route_sel_enum);
+
+/*
+ * TB = 6144*(MCLK(int) scaling factor)/MCLK(internal)
+ * TB - Time base
+ * NOTE: If MCLK_INT_SCALE = 0, then TB=1
+ */
+static const char * const cs53l30_ng_delay_text[] = {
+	"TB*50ms", "TB*100ms", "TB*150ms", "TB*200ms" };
+
+static const struct soc_enum adc1_ng_delay_enum =
+	SOC_ENUM_SINGLE(CS53L30_ADC1_NG_CTL, 0,
+		ARRAY_SIZE(cs53l30_ng_delay_text), cs53l30_ng_delay_text);
+
+static const struct soc_enum adc2_ng_delay_enum =
+	SOC_ENUM_SINGLE(CS53L30_ADC2_NG_CTL, 0,
+		ARRAY_SIZE(cs53l30_ng_delay_text), cs53l30_ng_delay_text);
+
+/* The noise gate threshold selected will depend on NG Boost */
+static const char * const cs53l30_ng_thres_text[] = {
+	"-64dB/-34dB", "-66dB/-36dB", "-70dB/-40dB", "-73dB/-43dB",
+	"-76dB/-46dB", "-82dB/-52dB", "-58dB", "-64dB"};
+
+static const struct soc_enum adc1_ng_thres_enum =
+	SOC_ENUM_SINGLE(CS53L30_ADC1_NG_CTL, 2,
+		ARRAY_SIZE(cs53l30_ng_thres_text), cs53l30_ng_thres_text);
+
+static const struct soc_enum adc2_ng_thres_enum =
+	SOC_ENUM_SINGLE(CS53L30_ADC2_NG_CTL, 2,
+		ARRAY_SIZE(cs53l30_ng_thres_text), cs53l30_ng_thres_text);
+
+/* ADC Preamp gain select */
+static const char * const cs53l30_preamp_gain_sel_text[] = {
+	"0dB", "10dB", "20dB"};
+
+static const struct soc_enum adc1a_preamp_gain_enum =
+	SOC_ENUM_SINGLE(CS53L30_ADC1A_AFE_CTL, 6,
+		ARRAY_SIZE(cs53l30_preamp_gain_sel_text),
+		cs53l30_preamp_gain_sel_text);
+
+static const struct soc_enum adc1b_preamp_gain_enum =
+	SOC_ENUM_SINGLE(CS53L30_ADC1B_AFE_CTL, 6,
+		ARRAY_SIZE(cs53l30_preamp_gain_sel_text),
+		cs53l30_preamp_gain_sel_text);
+
+static const struct soc_enum adc2a_preamp_gain_enum =
+	SOC_ENUM_SINGLE(CS53L30_ADC2A_AFE_CTL, 6,
+		ARRAY_SIZE(cs53l30_preamp_gain_sel_text),
+		cs53l30_preamp_gain_sel_text);
+
+static const struct soc_enum adc2b_preamp_gain_enum =
+	SOC_ENUM_SINGLE(CS53L30_ADC2B_AFE_CTL, 6,
+		ARRAY_SIZE(cs53l30_preamp_gain_sel_text),
+		cs53l30_preamp_gain_sel_text);
+
+/* Set MIC Bias Voltage Control */
+static const char * const cs53l30_micbias_text[] = {
+	"HiZ", "1.8V", "2.75V"};
+
+static const struct soc_enum micbias_enum =
+	SOC_ENUM_SINGLE(CS53L30_MICBIAS_CTL, 0,
+		ARRAY_SIZE(cs53l30_micbias_text),
+		cs53l30_micbias_text);
+
+/* Corner frequencies are with an Fs of 48kHz. */
+static const char * const hpf_corner_freq_text[] = {
+	"1.86Hz", "120Hz", "235Hz", "466Hz"};
+
+static const struct soc_enum adc1_hpf_enum =
+	SOC_ENUM_SINGLE(CS53L30_ADC1_CTL3, 1,
+		ARRAY_SIZE(hpf_corner_freq_text), hpf_corner_freq_text);
+
+static const struct soc_enum adc2_hpf_enum =
+	SOC_ENUM_SINGLE(CS53L30_ADC2_CTL3, 1,
+		ARRAY_SIZE(hpf_corner_freq_text), hpf_corner_freq_text);
+
+static const struct snd_kcontrol_new cs53l30_snd_controls[] = {
+	SOC_SINGLE("Digital Soft-Ramp Switch", CS53L30_SFT_RAMP, 5, 1, 0),
+	SOC_SINGLE("ADC1 Noise Gate Ganging Switch",
+		CS53L30_ADC1_CTL3, 0, 1, 0),
+	SOC_SINGLE("ADC2 Noise Gate Ganging Switch",
+		CS53L30_ADC2_CTL3, 0, 1, 0),
+	SOC_SINGLE("ADC1A Noise Gate Enable Switch",
+		CS53L30_ADC1_NG_CTL, 6, 1, 0),
+	SOC_SINGLE("ADC1B Noise Gate Enable Switch",
+		CS53L30_ADC1_NG_CTL, 7, 1, 0),
+	SOC_SINGLE("ADC2A Noise Gate Enable Switch",
+		CS53L30_ADC2_NG_CTL, 6, 1, 0),
+	SOC_SINGLE("ADC2B Noise Gate Enable Switch",
+		CS53L30_ADC2_NG_CTL, 7, 1, 0),
+	SOC_SINGLE("ADC1 Notch Filter Switch",
+		CS53L30_ADCDMIC1_CTL2, 7, 1, 1),
+	SOC_SINGLE("ADC2 Notch Filter Switch",
+		CS53L30_ADCDMIC2_CTL2, 7, 1, 1),
+	SOC_SINGLE("ADC1A Invert Switch",
+		CS53L30_ADCDMIC1_CTL2, 4, 1, 0),
+	SOC_SINGLE("ADC1B Invert Switch",
+		CS53L30_ADCDMIC1_CTL2, 5, 1, 0),
+	SOC_SINGLE("ADC2A Invert Switch",
+		CS53L30_ADCDMIC2_CTL2, 4, 1, 0),
+	SOC_SINGLE("ADC2B Invert Switch",
+		CS53L30_ADCDMIC2_CTL2, 5, 1, 0),
+
+	SOC_SINGLE_TLV("ADC1A Digital Boost Volume",
+			CS53L30_ADCDMIC1_CTL2, 0, 1, 0, adc_boost_tlv),
+	SOC_SINGLE_TLV("ADC1B Digital Boost Volume",
+		       CS53L30_ADCDMIC1_CTL2, 1, 1, 0, adc_boost_tlv),
+	SOC_SINGLE_TLV("ADC2A Digital Boost Volume",
+			CS53L30_ADCDMIC2_CTL2, 0, 1, 0, adc_boost_tlv),
+	SOC_SINGLE_TLV("ADC2B Digital Boost Volume",
+		       CS53L30_ADCDMIC2_CTL2, 1, 1, 0, adc_boost_tlv),
+	SOC_SINGLE_TLV("ADC1 NG Boost Volume",
+		       CS53L30_ADC1_NG_CTL, 5, 1, 0, adc_ng_boost_tlv),
+	SOC_SINGLE_TLV("ADC2 NG Boost Volume",
+		       CS53L30_ADC2_NG_CTL, 5, 1, 0, adc_ng_boost_tlv),
+
+	SOC_ENUM("Input 1 Channel Select", input1_sel_enum),
+	SOC_ENUM("Input 2 Channel Select", input2_sel_enum),
+
+	SOC_ENUM("ADC1 HPF Select", adc1_hpf_enum),
+	SOC_ENUM("ADC2 HPF Select", adc2_hpf_enum),
+	SOC_ENUM("ADC1 NG Threshold", adc1_ng_thres_enum),
+	SOC_ENUM("ADC2 NG Threshold", adc2_ng_thres_enum),
+	SOC_ENUM("ADC1 NG Delay", adc1_ng_delay_enum),
+	SOC_ENUM("ADC2 NG Delay", adc2_ng_delay_enum),
+	SOC_ENUM("ADC1A Pre Amp Gain", adc1a_preamp_gain_enum),
+	SOC_ENUM("ADC1B Pre Amp Gain", adc1b_preamp_gain_enum),
+	SOC_ENUM("ADC2A Pre Amp Gain", adc2a_preamp_gain_enum),
+	SOC_ENUM("ADC2B Pre Amp Gain", adc2b_preamp_gain_enum),
+	SOC_ENUM("Mic Bias Voltage Select", micbias_enum),
+
+	SOC_SINGLE_SX_TLV("ADC1A PGA Volume",
+		    CS53L30_ADC1A_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+	SOC_SINGLE_SX_TLV("ADC1B PGA Volume",
+		    CS53L30_ADC1B_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+	SOC_SINGLE_SX_TLV("ADC2A PGA Volume",
+		    CS53L30_ADC2A_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+	SOC_SINGLE_SX_TLV("ADC2B PGA Volume",
+		    CS53L30_ADC2B_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+
+	SOC_SINGLE_SX_TLV("ADC1A Digital Volume",
+		    CS53L30_ADC1A_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+	SOC_SINGLE_SX_TLV("ADC1B Digital Volume",
+		    CS53L30_ADC1B_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+	SOC_SINGLE_SX_TLV("ADC2A Digital Volume",
+		    CS53L30_ADC2A_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+	SOC_SINGLE_SX_TLV("ADC2B Digital Volume",
+		    CS53L30_ADC2B_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+};
+
+static int cs53l30_asp_sdout_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		regmap_update_bits(priv->regmap, CS53L30_ASP1_CTL,
+				   CS53L30_ASP1_3ST, 0);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		regmap_update_bits(priv->regmap, CS53L30_ASP1_CTL,
+				   CS53L30_ASP1_3ST, 1);
+		break;
+	default:
+		dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget cs53l30_dapm_widgets[] = {
+	SND_SOC_DAPM_INPUT("IN1_DMIC1"),
+	SND_SOC_DAPM_INPUT("IN2"),
+	SND_SOC_DAPM_INPUT("IN3_DMIC2"),
+	SND_SOC_DAPM_INPUT("IN4"),
+	SND_SOC_DAPM_SUPPLY("MIC1 Bias", CS53L30_MICBIAS_CTL, 4, 1, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("MIC2 Bias", CS53L30_MICBIAS_CTL, 5, 1, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("MIC3 Bias", CS53L30_MICBIAS_CTL, 6, 1, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("MIC4 Bias", CS53L30_MICBIAS_CTL, 7, 1, NULL, 0),
+
+	SND_SOC_DAPM_AIF_OUT_E("ASP_SDOUT1", NULL,  0, CS53L30_ASP1_CTL,
+			       CS53L30_ASP1_SDOUT_PDN, 1,
+			cs53l30_asp_sdout_event,
+			(SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)),
+	SND_SOC_DAPM_AIF_OUT_E("ASP_SDOUT2", NULL,  0, CS53L30_ASP2_CTL,
+			       CS53L30_ASP2_SDOUT_PDN, 1,
+			cs53l30_asp_sdout_event,
+			(SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)),
+
+	SND_SOC_DAPM_MUX("Input Mux 1", SND_SOC_NOPM, 0, 0,
+			 &input1_route_sel_mux),
+	SND_SOC_DAPM_MUX("Input Mux 2", SND_SOC_NOPM, 0, 0,
+			 &input2_route_sel_mux),
+
+	SND_SOC_DAPM_ADC("ADC1A", NULL, CS53L30_ADCDMIC1_CTL1, 6, 1),
+	SND_SOC_DAPM_ADC("ADC1B", NULL, CS53L30_ADCDMIC1_CTL1, 7, 1),
+	SND_SOC_DAPM_ADC("ADC2A", NULL, CS53L30_ADCDMIC2_CTL1, 6, 1),
+	SND_SOC_DAPM_ADC("ADC2B", NULL, CS53L30_ADCDMIC2_CTL1, 7, 1),
+	SND_SOC_DAPM_ADC("DMIC1", NULL, CS53L30_ADCDMIC1_CTL1, 2, 1),
+	SND_SOC_DAPM_ADC("DMIC2", NULL, CS53L30_ADCDMIC2_CTL1, 2, 1),
+};
+
+static const struct snd_soc_dapm_route cs53l30_audio_map[] = {
+
+	/* ADC Input Paths */
+	{"ADC1A", NULL, "IN1_DMIC1"},
+	{"Input Mux 1", "ADC1_SEL", "ADC1A"},
+	{"ADC1B", NULL, "IN2"},
+
+	{"ADC2A", NULL, "IN3_DMIC2"},
+	{"Input Mux 2", "ADC2_SEL", "ADC2A"},
+	{"ADC2B", NULL, "IN4"},
+
+	/* MIC Bias Paths */
+	{"ADC1A", NULL, "MIC1 Bias"},
+	{"ADC1B", NULL, "MIC2 Bias"},
+	{"ADC2A", NULL, "MIC3 Bias"},
+	{"ADC2B", NULL, "MIC4 Bias"},
+
+	/* DMIC Paths */
+	{"DMIC1", NULL, "IN1_DMIC1"},
+	{"Input Mux 1", "DMIC1_SEL", "DMIC1"},
+
+	{"DMIC2", NULL, "IN3_DMIC2"},
+	{"Input Mux 2", "DMIC2_SEL", "DMIC2"},
+
+	/* Output Paths */
+	{"ASP_SDOUT1", NULL, "ADC1A" },
+	{"ASP_SDOUT1", NULL, "Input Mux 1"},
+	{"ASP_SDOUT1", NULL, "ADC1B"},
+
+	{"ASP_SDOUT2", NULL, "ADC2A"},
+	{"ASP_SDOUT2", NULL, "Input Mux 2"},
+	{"ASP_SDOUT2", NULL, "ADC2B"},
+
+	{"ASP1 Capture", NULL, "ASP_SDOUT1"},
+	{"ASP2 Capture", NULL, "ASP_SDOUT2"},
+};
+
+struct cs53l30_mclk_div {
+	u32 mclk;
+	u32 srate;
+	u8 asp_rate;
+	u8 internal_fs_ratio;
+	u8 mclk_int_scale;
+};
+
+static struct cs53l30_mclk_div cs53l30_mclk_coeffs[] = {
+	/* NOTE: Enable MCLK_INT_SCALE to save power. */
+
+	/* MCLK, Sample Rate, asp_rate, internal_fs_ratio, mclk_int_scale */
+	{5644800, 11025, 0x4, 1, 1},
+	{5644800, 22050, 0x8, 1, 1},
+	{5644800, 44100, 0xC, 1, 1},
+
+	{6000000,  8000, 0x1, 0, 1},
+	{6000000, 11025, 0x2, 0, 1},
+	{6000000, 12000, 0x4, 0, 1},
+	{6000000, 16000, 0x5, 0, 1},
+	{6000000, 22050, 0x6, 0, 1},
+	{6000000, 24000, 0x8, 0, 1},
+	{6000000, 32000, 0x9, 0, 1},
+	{6000000, 44100, 0xA, 0, 1},
+	{6000000, 48000, 0xC, 0, 1},
+
+	{6144000,  8000, 0x1, 1, 1},
+	{6144000, 11025, 0x2, 1, 1},
+	{6144000, 12000, 0x4, 1, 1},
+	{6144000, 16000, 0x5, 1, 1},
+	{6144000, 22050, 0x6, 1, 1},
+	{6144000, 24000, 0x8, 1, 1},
+	{6144000, 32000, 0x9, 1, 1},
+	{6144000, 44100, 0xA, 1, 1},
+	{6144000, 48000, 0xC, 1, 1},
+
+	{6400000,  8000, 0x1, 1, 1},
+	{6400000, 11025, 0x2, 1, 1},
+	{6400000, 12000, 0x4, 1, 1},
+	{6400000, 16000, 0x5, 1, 1},
+	{6400000, 22050, 0x6, 1, 1},
+	{6400000, 24000, 0x8, 1, 1},
+	{6400000, 32000, 0x9, 1, 1},
+	{6400000, 44100, 0xA, 1, 1},
+	{6400000, 48000, 0xC, 1, 1},
+};
+
+struct cs53l30_mclkx_div {
+	u32 mclkx;
+	u8 ratio;
+	u8 mclkdiv;
+};
+
+static struct cs53l30_mclkx_div cs53l30_mclkx_coeffs[] = {
+	{5644800,  1, 0},
+	{6000000,  1, 0},
+	{6144000,  1, 0},
+	{11289600, 2, 1},
+	{12288000, 2, 1},
+	{12000000, 2, 1},
+	{19200000, 3, 2},
+};
+
+static int cs53l30_get_mclkx_coeff(int mclkx)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cs53l30_mclkx_coeffs); i++) {
+		if (cs53l30_mclkx_coeffs[i].mclkx == mclkx)
+			return i;
+	}
+	return -EINVAL;
+}
+
+static int cs53l30_get_mclk_coeff(int mclk, int srate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cs53l30_mclk_coeffs); i++) {
+		if (cs53l30_mclk_coeffs[i].mclk == mclk &&
+		    cs53l30_mclk_coeffs[i].srate == srate)
+			return i;
+	}
+	return -EINVAL;
+
+}
+
+static int cs53l30_set_sysclk(struct snd_soc_dai *dai,
+			      int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+
+	int mclkx_coeff;
+	u32 mclk;
+	unsigned int mclk_ctl;
+
+	/* MCLKX -> MCLK */
+	mclkx_coeff = cs53l30_get_mclkx_coeff(freq);
+	if (mclkx_coeff < 0)
+		return mclkx_coeff;
+
+	mclk = cs53l30_mclkx_coeffs[mclkx_coeff].mclkx /
+		cs53l30_mclkx_coeffs[mclkx_coeff].ratio;
+
+	regmap_read(priv->regmap, CS53L30_MCLKCTL, &mclk_ctl);
+	mclk_ctl &= ~CS53L30_MCLK_DIV;
+	mclk_ctl |= cs53l30_mclkx_coeffs[mclkx_coeff].mclkdiv;
+
+	regmap_update_bits(priv->regmap, CS53L30_MCLKCTL, CS53L30_MCLK_DIV,
+			   mclk_ctl << CS53L30_MCLK_DIV);
+	priv->mclk = mclk;
+	return 0;
+}
+
+static int cs53l30_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+	unsigned int asp_config_ctl;
+
+	regmap_read(priv->regmap, CS53L30_ASPCFG_CTL, &asp_config_ctl);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		asp_config_ctl |= CS53L30_ASP_MS;
+		break;
+
+	case SND_SOC_DAIFMT_CBS_CFS:
+		asp_config_ctl &= ~CS53L30_ASP_MS;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/* Check to see if the SCLK is inverted */
+	if (fmt & (SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_IB_IF))
+		asp_config_ctl |= CS53L30_ASP_SCLK_INV;
+	else
+		asp_config_ctl &= ~CS53L30_ASP_SCLK_INV;
+
+	priv->asp_config_ctl = asp_config_ctl;
+	return 0;
+}
+
+static int cs53l30_pcm_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 cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+	int mclk_coeff;
+	int srate = params_rate(params);
+	unsigned int int_sr_ctl, mclk_ctl;
+
+	/* MCLK -> srate */
+	mclk_coeff = cs53l30_get_mclk_coeff(priv->mclk, srate);
+	if (mclk_coeff < 0)
+		return -EINVAL;
+
+	regmap_read(priv->regmap, CS53L30_INT_SR_CTL, &int_sr_ctl);
+	if (cs53l30_mclk_coeffs[mclk_coeff].internal_fs_ratio)
+		int_sr_ctl |= CS53L30_INTRNL_FS_RATIO;
+	else
+		int_sr_ctl &= ~CS53L30_INTRNL_FS_RATIO;
+	regmap_update_bits(priv->regmap, CS53L30_INT_SR_CTL,
+			   CS53L30_INTL_FS_RAT_MSK, int_sr_ctl <<
+			   CS53L30_INTL_FS_RAT_SFT);
+
+	regmap_read(priv->regmap, CS53L30_MCLKCTL, &mclk_ctl);
+	if (cs53l30_mclk_coeffs[mclk_coeff].mclk_int_scale)
+		mclk_ctl |= CS53L30_MCLK_INT_SCALE;
+	else
+		mclk_ctl &= ~CS53L30_MCLK_INT_SCALE;
+	regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+			   CS53L30_MCK_INT_SCL_MSK, mclk_ctl <<
+			   CS53L30_MCK_INT_SCL_SFT);
+
+	priv->asp_config_ctl &= CS53L30_ASP_CNFG_MASK;
+	priv->asp_config_ctl |= (cs53l30_mclk_coeffs[mclk_coeff].asp_rate
+				& CS53L30_ASP_RATE_MASK);
+	regmap_update_bits(priv->regmap, CS53L30_ASPCFG_CTL,
+			   CS53L30_ASP_RATE_MASK, priv->asp_config_ctl);
+
+	return 0;
+}
+
+static int cs53l30_set_bias_level(struct snd_soc_codec *codec,
+				enum snd_soc_bias_level level)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+	struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+	unsigned int reg;
+	int i, inter_max_check;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		if (dapm->bias_level == SND_SOC_BIAS_STANDBY)
+			regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+					   CS53L30_PDN_LP, 0);
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		if (dapm->bias_level == SND_SOC_BIAS_OFF) {
+			regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+					   CS53L30_MCLK_DIS, 0);
+			regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+					   CS53L30_PDN_ULP, 0);
+			msleep(50);
+		} else {
+			regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+					   CS53L30_PDN_LP, CS53L30_PDN_LP);
+		}
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		regmap_update_bits(priv->regmap, CS53L30_INT_MASK,
+				   CS53L30_PDN_DONE, 0);
+		/* If digital softramp is set, the amount of time required
+		 * for power down increases and depends on the digital
+		 * volume setting.
+		 */
+
+		/* Set the max possible time if digsft is set */
+		regmap_read(priv->regmap, CS53L30_SFT_RAMP, &reg);
+		if (reg & CS53L30_DIGSFT)
+			inter_max_check = CS53L30_PDN_POLL_MAX;
+		else
+			inter_max_check = 10;
+
+		regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+				   CS53L30_PDN_ULP, CS53L30_PDN_ULP);
+		msleep(20); /* PDN_DONE will take a min of 20ms to be set.*/
+		regmap_read(priv->regmap, CS53L30_IS, &reg); /* Clr status */
+		for (i = 0; i < inter_max_check; i++) {
+			if (inter_max_check < 10) {
+				usleep_range(1000, 1100);
+				regmap_read(priv->regmap, CS53L30_IS, &reg);
+				if (reg & CS53L30_PDN_DONE)
+					break;
+			} else {
+				usleep_range(10000, 10100);
+				regmap_read(priv->regmap, CS53L30_IS, &reg);
+				if (reg & CS53L30_PDN_DONE)
+					break;
+			}
+		}
+		/* PDN_DONE is set.  We now can disable the MCLK */
+		regmap_update_bits(priv->regmap, CS53L30_INT_MASK,
+				   CS53L30_PDN_DONE, CS53L30_PDN_DONE);
+		regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+				   CS53L30_MCLK_DIS, CS53L30_MCLK_DIS);
+		break;
+	}
+
+	return 0;
+}
+
+static int cs53l30_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+
+	return regmap_update_bits(priv->regmap, CS53L30_ASP1_CTL,
+				  CS53L30_ASP1_3ST,
+			   (CS53L30_ASP1_3ST_VAL(tristate) &
+			   CS53L30_ASP1_3ST));
+}
+
+unsigned int const cs53l30_src_rates[] = {
+	8000, 11025, 12000, 16000, 22050,
+	24000, 32000, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list src_constraints = {
+	.count  = ARRAY_SIZE(cs53l30_src_rates),
+	.list   = cs53l30_src_rates,
+};
+
+static int cs53l30_pcm_startup(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
+{
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+					SNDRV_PCM_HW_PARAM_RATE,
+					&src_constraints);
+
+	return 0;
+}
+
+/* SNDRV_PCM_RATE_KNOT -> 12000, 24000 Hz, limit with constraint list */
+#define CS53L30_RATES (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
+
+#define CS53L30_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+			SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops cs53l30_ops = {
+	.startup = cs53l30_pcm_startup,
+	.hw_params = cs53l30_pcm_hw_params,
+	.set_fmt = cs53l30_set_dai_fmt,
+	.set_sysclk = cs53l30_set_sysclk,
+	.set_tristate = cs53l30_set_tristate,
+};
+
+static struct snd_soc_dai_driver cs53l30_dai[] = {
+	{
+		.name = "cs53l30-asp1",
+		.id = CS53L30_ASP1,
+		.capture = {
+			.stream_name = "ASP1 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = CS53L30_RATES,
+			.formats = CS53L30_FORMATS,
+		},
+		.ops = &cs53l30_ops,
+		.symmetric_rates = 1,
+	 },
+	{
+		.name = "cs53l30-asp2",
+		.id = CS53L30_ASP2,
+		.capture = {
+			.stream_name = "ASP2 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = CS53L30_RATES,
+			.formats = CS53L30_FORMATS,
+		},
+		.ops = &cs53l30_ops,
+		.symmetric_rates = 1,
+	 }
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_cs53l30 = {
+	.set_bias_level = cs53l30_set_bias_level,
+
+	.dapm_widgets = cs53l30_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(cs53l30_dapm_widgets),
+	.dapm_routes = cs53l30_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(cs53l30_audio_map),
+
+	.controls = cs53l30_snd_controls,
+	.num_controls = ARRAY_SIZE(cs53l30_snd_controls),
+};
+
+static struct regmap_config cs53l30_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = CS53L30_MAX_REGISTER,
+	.reg_defaults = cs53l30_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(cs53l30_reg_defaults),
+	.volatile_reg = cs53l30_volatile_register,
+	.readable_reg = cs53l30_readable_register,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int cs53l30_i2c_probe(struct i2c_client *client,
+				       const struct i2c_device_id *id)
+{
+	struct cs53l30_private *cs53l30;
+	int ret = 0;
+	unsigned int devid = 0;
+	unsigned int reg;
+
+	cs53l30 = devm_kzalloc(&client->dev,
+			       sizeof(struct cs53l30_private), GFP_KERNEL);
+	if (!cs53l30) {
+		dev_err(&client->dev, "could not allocate codec\n");
+		return -ENOMEM;
+	}
+
+	/* Reset the Device */
+	cs53l30->reset_gpio = devm_gpiod_get_optional(&client->dev,
+		"reset", GPIOD_OUT_LOW);
+	if (IS_ERR(cs53l30->reset_gpio))
+		return PTR_ERR(cs53l30->reset_gpio);
+
+	if (cs53l30->reset_gpio)
+		gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
+
+	i2c_set_clientdata(client, cs53l30);
+
+	cs53l30->mclk = 0;
+
+	cs53l30->regmap = devm_regmap_init_i2c(client, &cs53l30_regmap);
+	if (IS_ERR(cs53l30->regmap)) {
+		ret = PTR_ERR(cs53l30->regmap);
+		dev_err(&client->dev, "regmap_init() failed: %d\n", ret);
+		return ret;
+	}
+	/* initialize codec */
+	ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_AB, &reg);
+	devid = reg << 12;
+
+	ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_CD, &reg);
+	devid |= reg << 4;
+
+	ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_E, &reg);
+	devid |= (reg & 0xF0) >> 4;
+
+	if (devid != CS53L30_DEVID) {
+		ret = -ENODEV;
+		dev_err(&client->dev,
+			"CS53L30 Device ID (%X). Expected %X\n",
+			devid, CS53L30_DEVID);
+		return ret;
+	}
+
+	ret = regmap_read(cs53l30->regmap, CS53L30_REVID, &reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "Get Revision ID failed\n");
+		return ret;
+	}
+
+	dev_info(&client->dev,
+		 "Cirrus Logic CS53L30, Revision: %02X\n", reg & 0xFF);
+
+	ret =  snd_soc_register_codec(&client->dev,
+			&soc_codec_dev_cs53l30, cs53l30_dai,
+			ARRAY_SIZE(cs53l30_dai));
+	return ret;
+}
+
+static int cs53l30_i2c_remove(struct i2c_client *client)
+{
+	struct cs53l30_private *cs53l30 = i2c_get_clientdata(client);
+
+	snd_soc_unregister_codec(&client->dev);
+
+	/* Hold down reset */
+	if (cs53l30->reset_gpio)
+		gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int cs53l30_runtime_suspend(struct device *dev)
+{
+	struct cs53l30_private *cs53l30 = dev_get_drvdata(dev);
+
+	regcache_cache_only(cs53l30->regmap, true);
+
+	/* Hold down reset */
+	if (cs53l30->reset_gpio)
+		gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
+
+	return 0;
+}
+
+static int cs53l30_runtime_resume(struct device *dev)
+{
+	struct cs53l30_private *cs53l30 = dev_get_drvdata(dev);
+
+	if (cs53l30->reset_gpio)
+		gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
+
+	regcache_cache_only(cs53l30->regmap, false);
+	regcache_sync(cs53l30->regmap);
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops cs53l30_runtime_pm = {
+	SET_RUNTIME_PM_OPS(cs53l30_runtime_suspend, cs53l30_runtime_resume,
+			   NULL)
+};
+
+static const struct of_device_id cs53l30_of_match[] = {
+	{ .compatible = "cirrus,cs53l30", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, cs53l30_of_match);
+
+static const struct i2c_device_id cs53l30_id[] = {
+	{"cs53l30", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs53l30_id);
+
+static struct i2c_driver cs53l30_i2c_driver = {
+	.driver = {
+		   .name = "cs53l30",
+		   },
+	.id_table = cs53l30_id,
+	.probe = cs53l30_i2c_probe,
+	.remove = cs53l30_i2c_remove,
+
+};
+
+module_i2c_driver(cs53l30_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS53L30 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <Paul.Handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs53l30.h b/sound/soc/codecs/cs53l30.h
new file mode 100644
index 0000000..116e358
+++ b/sound/soc/codecs/cs53l30.h
@@ -0,0 +1,282 @@
+/*
+ * ALSA SoC CS53L30 codec driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <Paul.Handrigan@cirrus.com>,
+ *         Tim Howe <Tim.Howe@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __CS53L30_H__
+#define __CS53L30_H__
+
+/* I2C Registers */
+#define CS53L30_DEVID_AB	0x01	 /* Device ID A & B [RO]. */
+#define CS53L30_DEVID_CD	0x02     /* Device ID C & D [RO]. */
+#define CS53L30_DEVID_E		0x03     /* Device ID E [RO]. */
+#define CS53L30_REVID		0x05     /* Revision ID [RO]. */
+#define CS53L30_PWRCTL		0x06     /* Power Control. */
+#define CS53L30_MCLKCTL		0x07     /* MCLK Control. */
+#define CS53L30_INT_SR_CTL	0x08     /* Internal Sample Rate Control. */
+#define CS53L30_MICBIAS_CTL	0x0A     /* Mic Bias Control. */
+#define CS53L30_ASPCFG_CTL	0x0C     /* ASP Config Control. */
+#define CS53L30_ASP1_CTL	0x0D     /* ASP1 Control. */
+#define CS53L30_ASP1_TDMTX_CTL1	0x0E     /* ASP1 TDM TX Control 1 */
+#define CS53L30_ASP1_TDMTX_CTL2	0x0F     /* ASP1 TDM TX Control 2 */
+#define CS53L30_ASP1_TDMTX_CTL3	0x10     /* ASP1 TDM TX Control 3 */
+#define CS53L30_ASP1_TDMTX_CTL4	0x11     /* ASP1 TDM TX Control 4 */
+#define CS53L30_ASP1_TDMTX_EN1	0x12     /* ASP1 TDM TX Enable 1 */
+#define CS53L30_ASP1_TDMTX_EN2	0x13     /* ASP1 TDM TX Enable 2 */
+#define CS53L30_ASP1_TDMTX_EN3	0x14     /* ASP1 TDM TX Enable 3 */
+#define CS53L30_ASP1_TDMTX_EN4	0x15     /* ASP1 TDM TX Enable 4 */
+#define CS53L30_ASP1_TDMTX_EN5	0x16     /* ASP1 TDM TX Enable 5 */
+#define CS53L30_ASP1_TDMTX_EN6	0x17     /* ASP1 TDM TX Enable 6 */
+#define CS53L30_ASP2_CTL	0x18     /* ASP2 Control. */
+#define CS53L30_SFT_RAMP	0x1A     /* Soft Ramp Control. */
+#define CS53L30_LRCLK_CTL1	0x1B     /* LRCLK Control 1. */
+#define CS53L30_LRCLK_CTL2	0x1C     /* LRCLK Control 2. */
+#define CS53L30_MUTEP_CTL1	0x1F     /* Mute Pin Control 1. */
+#define CS53L30_MUTEP_CTL2	0x20     /* Mute Pin Control 2. */
+#define CS53L30_INBIAS_CTL1	0x21     /* Input Bias Control 1. */
+#define CS53L30_INBIAS_CTL2	0x22     /* Input Bias Control 2. */
+#define CS53L30_DMIC1_STR_CTL   0x23     /* DMIC1 Stereo Control. */
+#define CS53L30_DMIC2_STR_CTL   0x24     /* DMIC2 Stereo Control. */
+#define CS53L30_ADCDMIC1_CTL1   0x25     /* ADC1/DMIC1 Control 1. */
+#define CS53L30_ADCDMIC1_CTL2   0x26     /* ADC1/DMIC1 Control 2. */
+#define CS53L30_ADC1_CTL3	0x27     /* ADC1 Control 3. */
+#define CS53L30_ADC1_NG_CTL	0x28     /* ADC1 Noise Gate Control. */
+#define CS53L30_ADC1A_AFE_CTL	0x29     /* ADC1A AFE Control. */
+#define CS53L30_ADC1B_AFE_CTL	0x2A     /* ADC1B AFE Control. */
+#define CS53L30_ADC1A_DIG_VOL	0x2B     /* ADC1A Digital Volume. */
+#define CS53L30_ADC1B_DIG_VOL	0x2C     /* ADC1B Digital Volume. */
+#define CS53L30_ADCDMIC2_CTL1   0x2D     /* ADC2/DMIC2 Control 1. */
+#define CS53L30_ADCDMIC2_CTL2   0x2E     /* ADC2/DMIC2 Control 2. */
+#define CS53L30_ADC2_CTL3	0x2F     /* ADC2 Control 3. */
+#define CS53L30_ADC2_NG_CTL	0x30     /* ADC2 Noise Gate Control. */
+#define CS53L30_ADC2A_AFE_CTL	0x31     /* ADC2A AFE Control. */
+#define CS53L30_ADC2B_AFE_CTL	0x32     /* ADC2B AFE Control. */
+#define CS53L30_ADC2A_DIG_VOL	0x33     /* ADC2A Digital Volume. */
+#define CS53L30_ADC2B_DIG_VOL	0x34     /* ADC2B Digital Volume. */
+#define CS53L30_INT_MASK	0x35     /* Interrupt Mask. */
+#define CS53L30_IS		0x36     /* Interrupt Status. */
+#define CS53L30_MAX_REGISTER	0x36
+
+/* Device ID */
+#define CS53L30_DEVID		0x53A30
+
+/* PDN_DONE Poll Maximum
+ * If soft ramp is set it will take much longer to power down
+ * the system.
+ */
+#define CS53L30_PDN_POLL_MAX	90
+
+/* Bitfield Definitions */
+
+/* CS53L30_PWRCTL */
+#define CS53L30_PDN_ULP		(1 << 7)
+#define CS53L30_PDN_LP		(1 << 6)
+#define CS53L30_DISCHARGE_FILT	(1 << 5)
+#define CS53L30_THMS_PDN	(1 << 4)
+
+/* CS53L30_MCLKCTL */
+#define CS53L30_MCLK_DIS	(1 << 7)
+#define CS53L30_MCLK_INT_SCALE	(1 << 6)
+#define CS53L30_DMIC_DRIVE	(1 << 5)
+#define CS53L30_MCLK_DIV	(3 << 2)
+#define CS53L30_MCLK_DIV_DFLT	(1 << 2)
+#define CS53L30_SYNC_EN		(1 << 1)
+#define CS53L30_MCK_INT_SCL_MSK	0x40
+#define CS53L30_MCK_INT_SCL_SFT	6
+
+/* CS53L30_INT_SR_CTL */
+#define CS53L30_INTRNL_FS_RATIO	(1 << 4)
+#define CS53L30_INTRNL_FS_DFLT	(7 << 2)
+#define CS53L30_MCLK_19MHZ_EN	(1 << 0)
+#define CS53L30_INTL_FS_RAT_MSK	0x10
+#define CS53L30_INTL_FS_RAT_SFT	4
+
+/* CS53L30_MICBIAS_CTL */
+#define CS53L30_MIC4_BIAS_PDN	(1 << 7)
+#define CS53L30_MIC3_BIAS_PDN	(1 << 6)
+#define CS53L30_MIC2_BIAS_PDN	(1 << 5)
+#define CS53L30_MIC1_BIAS_PDN	(1 << 4)
+#define CS53L30_VP_MIN		(1 << 2)
+#define CS53L30_MIC_BIAS_CTRL	(3 << 0)
+#define CS53L30_BIAS_ALL_PDN	0xF0
+#define CS53L30_MIC_BIAS_DFLT	(CS53L30_BIAS_ALL_PDN | CS53L30_VP_MIN)
+
+/* CS53L30_ASPCFG_CTL */
+#define CS53L30_ASP_MS		(1 << 7)
+#define CS53L30_ASP_SCLK_INV	(1 << 4)
+#define CS53L30_ASP_RATE_48K	(3 << 2)
+#define CS53L30_ASP_RATE_MASK	0x0F
+#define CS53L30_ASP_CNFG_MASK	0xF0
+
+/* CS53L30_ASP1_CTL  */
+#define CS53L30_ASP1_TDM_PDN	(1 << 7)
+#define CS53L30_ASP1_SDOUT_PDN	(1 << 6)
+#define CS53L30_ASP1_3ST	(1 << 5)
+#define CS53L30_SHIFT_LEFT	(1 << 4)
+#define CS53L30_ASP1_DRIVE	(1 << 0)
+#define CS53L30_ASP1_3ST_VAL(x)	((x) << 5)
+
+/* CS53L30_ASP1_TDMTX_CTL */
+#define CS53L30_ASP1_CHX_TX_ST	(1 << 7)
+#define CS53L30_ASP1_CHX_TX_LOC	0x3F
+#define CS53L30_ASP1_CHTX_SLT47	0x2F
+#define CS53L30_ASP_TX_DISABLED	0x00
+
+/* CS53L30_ASP2_CTL  */
+#define CS53L30_ASP2_SDOUT_PDN	(1 << 6)
+#define CS53L30_ASP2_DRIVE	(1 << 0)
+#define CS53L30_ASP2_CTRL_DFLT	0x00
+
+/* CS53L30_SFT_RAMP  */
+#define CS53L30_DIGSFT		(1 << 5)
+#define CS53L30_SFT_RMP_DFLT	0x00
+
+/* CS53L30_LRCLK_CTL2  */
+#define CS53L30_LRCK_50_NPW	(1 << 3)
+#define CS53L30_LRCK_TPWH	(7 << 0)
+#define CS53L30_LRCK_CTLX_DFLT	0x00
+
+/* CS53L30_MUTEP_CTL  */
+#define CS53L30_MUTE_PDN_ULP	(1 << 7)
+#define CS53L30_MUTE_PDN_LP	(1 << 6)
+#define CS53L30_MUTE_M4B_PDN	(1 << 4)
+#define CS53L30_MUTE_M3B_PDN	(1 << 3)
+#define CS53L30_MUTE_M2B_PDN	(1 << 2)
+#define CS53L30_MUTE_M1B_PDN	(1 << 1)
+#define CS53L30_MUTE_MB_ALL_PDN	(1 << 0)
+#define CS53L30_MUTE_CTRL1_DFLT	0x00
+
+/* CS53L30_MUTEP_CTL2  */
+#define CS53L30_MUTE_PIN_PLRTY	(1 << 7)
+#define CS53L30_MUTE_ASPTDM_PDN	(1 << 6)
+#define CS53L30_MTE_ASPSDO2_PDN	(1 << 5)
+#define CS53L30_MTE_ASPSDO1_PDN	(1 << 4)
+#define CS53L30_MUTE_ADC2B_PDN	(1 << 3)
+#define CS53L30_MUTE_ADC2A_PDN	(1 << 2)
+#define CS53L30_MUTE_ADC1B_PDN	(1 << 1)
+#define CS53L30_MUTE_ADC1A_PDN	(1 << 0)
+
+/* CS53L30_INBIAS_CTL1 */
+#define CS53L30_IN4M_BIAS	(3 << 6)
+#define CS53L30_IN4P_BIAS	(3 << 4)
+#define CS53L30_IN3M_BIAS	(3 << 2)
+#define CS53L30_IN3P_BIAS	(3 << 0)
+
+/* CS53L30_INBIAS_CTL2 */
+#define CS53L30_IN2M_BIAS	(3 << 6)
+#define CS53L30_IN2P_BIAS	(3 << 4)
+#define CS53L30_IN1M_BIAS	(3 << 2)
+#define CS53L30_IN1P_BIAS	(3 << 0)
+#define CS53L30_INBIAS_X_DFLT	0xAA
+
+/* CS53L30_DMIC1_STR_CTL */
+#define CS53L30_DMIC1_STERO_EN	(1 << 5)
+#define CS53L30_DMIC1_ST_DFLT	0xA8
+
+/* CS53L30_DMIC2_STR_CTL */
+#define CS53L30_DMIC2_STERO_EN	(1 << 5)
+#define CS53L30_DMIC2_ST_DFLT	0xEC
+
+/* CS53L30_ADCDMIC1_CTL1  */
+#define CS53L30_ADC1B_PDN	(1 << 7)
+#define CS53L30_ADC1A_PDN	(1 << 6)
+#define CS53L30_DMIC1_PDN	(1 << 2)
+#define CS53L30_DMIC1_SCLK_DIV	(1 << 1)
+#define CS53L30_CH_TYPE		(1 << 0)
+#define CS53L30_DMIC1_ON_AB_IN	(CS53L30_CH_TYPE)
+#define CS53L30_DMIC1_ON_A_IN	(CS53L30_ADC1B_PDN | CS53L30_CH_TYPE)
+#define CS53L30_DMIC1_ON_B_IN	(CS53L30_ADC1A_PDN | CS53L30_CH_TYPE)
+#define CS53L30_ADC1_ON_AB_IN	(CS53L30_DMIC1_PDN)
+#define CS53L30_ADC1_ON_A_IN	(CS53L30_ADC1B_PDN | CS53L30_DMIC1_PDN)
+#define CS53L30_ADC1_ON_B_IN	(CS53L30_ADC1A_PDN | CS53L30_DMIC1_PDN)
+#define CS53L30_D1_OFF_A1_OFF	(CS53L30_ADC1A_PDN | \
+				CS53L30_ADC1B_PDN | \
+				CS53L30_DMIC1_PDN)
+#define CS53L30_A1_D1_PDN_MASK	0xFF
+
+/* CS53L30_ADCDMIC1_CTL2  */
+#define CS53L30_ADC1_NOTCH_DIS	(1 << 7)
+#define CS53L30_ADC1B_INV	(1 << 5)
+#define CS53L30_ADC1A_INV	(1 << 4)
+#define CS53L30_ADC1B_DIG_BOOST	(1 << 1)
+#define CS53L30_ADC1A_DIG_BOOST	(1 << 0)
+#define CS53L30_A1_D1_CTL2_DFLT	0x00
+
+/* CS53L30_ADC1_CTL3  */
+#define CS53L30_ADC1_HPF_EN	(1 << 3)
+#define CS53L30_ADC1_HPF_CF	(3 << 1)
+#define CS53L30_ADC1_NG_ALL	(1 << 0)
+
+/* CS53L30_ADC1_NG_CTL  */
+#define CS53L30_ADC1B_NG	(1 << 7)
+#define CS53L30_ADC1A_NG	(1 << 6)
+#define CS53L30_ADC1_NG_BOOST	(1 << 5)
+#define CS53L30_ADC1_NG_THRESH	(7 << 2)
+#define CS53L30_ADC1_NG_DELAY	(3 << 0)
+#define CS53L30_ADCX_ZERO_DFLT	0x00
+
+/* CS53L30_ADC1A_AFE_CTL */
+#define CS53L30_ADC1A_PREAMP	(3 << 6)
+#define CS53L30_ADC1A_PGA_VOL	0x3F
+
+/* CS53L30_ADC1B_AFE_CTL */
+#define CS53L30_ADC1B_PREAMP	(3 << 6)
+#define CS53L30_ADC1B_PGA_VOL	0x3F
+
+/* CS53L30_ADCXX_DIG_VOL */
+#define CS53L30_MUTE_DIG_OUT	(1 << 7)
+
+/* CS53L30_ADCDMIC2_CTL1  */
+#define CS53L30_ADC2B_PDN	(1 << 7)
+#define CS53L30_ADC2A_PDN	(1 << 6)
+#define CS53L30_DMIC2_PDN	(1 << 2)
+#define CS53L30_DMIC2_CLKDIV	(1 << 1)
+#define CS53L30_DMIC2_ON_AB_IN	0x00 /* CH_TYPE must = 1 */
+#define CS53L30_DMIC2_ON_A_IN	(CS53L30_ADC2B_PDN) /* CH_TYPE must = 1 */
+#define CS53L30_DMIC2_ON_B_IN	(CS53L30_ADC2A_PDN) /* CH_TYPE must = 1 */
+#define CS53L30_ADC2_ON_AB_IN	(CS53L30_DMIC2_PDN) /* CH_TYPE must = 0 */
+#define CS53L30_ADC2_ON_A_IN	(CS53L30_ADC2B_PDN | \
+				CS53L30_DMIC2_PDN)
+#define CS53L30_ADC2_ON_B_IN	(CS53L30_ADC2A_PDN | \
+				CS53L30_DMIC2_PDN)
+#define CS53L30_D2_OFF_A2_OFF	(CS53L30_ADC2A_PDN | \
+				CS53L30_ADC2B_PDN | \
+				CS53L30_DMIC2_PDN)
+
+/* CS53L30_ADCDMIC2_CTL2  */
+#define CS53L30_ADC2_NOTCH_DIS	(1 << 7)
+#define CS53L30_ADC2B_INV	(1 << 5)
+#define CS53L30_ADC2A_INV	(1 << 4)
+#define CS53L30_ADC2B_DIG_BOOST	(1 << 1)
+#define CS53L30_ADC2A_DIG_BOOST	(1 << 0)
+
+/* CS53L30_ADC2_CTL3  */
+#define CS53L30_ADC2_HPF_EN	(1 << 3)
+#define CS53L30_ADC2_HPF_CF	(3 << 1)
+#define CS53L30_ADC2_NG_ALL	(1 << 0)
+
+/* CS53L30_INT */
+#define CS53L30_PDN_DONE	(1 << 7)
+#define CS53L30_THMS_TRIP	(1 << 6)
+#define CS53L30_SYNC_DONE	(1 << 5)
+#define CS53L30_ADC2B_OVFL	(1 << 4)
+#define CS53L30_ADC2A_OVFL	(1 << 3)
+#define CS53L30_ADC1B_OVFL	(1 << 2)
+#define CS53L30_ADC1A_OVFL	(1 << 1)
+#define CS53L30_MUTE_PIN	(1 << 0)
+#define CS53L30_DEVICE_INT_MASK	0xFF
+
+/* Serial Ports */
+#define CS53L30_ASP1		0
+#define CS53L30_ASP2		1
+
+#endif	/* __CS53L30_H__ */

Comments

Mark Brown March 28, 2016, 9:51 a.m. UTC | #1
On Tue, Mar 15, 2016 at 05:47:46PM -0500, tim.howe@cirrus.com wrote:

> +/* ADC Preamp gain select */
> +static const char * const cs53l30_preamp_gain_sel_text[] = {
> +	"0dB", "10dB", "20dB"};
> +
> +static const struct soc_enum adc1a_preamp_gain_enum =
> +	SOC_ENUM_SINGLE(CS53L30_ADC1A_AFE_CTL, 6,
> +		ARRAY_SIZE(cs53l30_preamp_gain_sel_text),
> +		cs53l30_preamp_gain_sel_text);

Why are you writing volume controls as enums?  Please use normal volume
controls with TLV information.

> +/* Set MIC Bias Voltage Control */
> +static const char * const cs53l30_micbias_text[] = {
> +	"HiZ", "1.8V", "2.75V"};

Why is this exposed to userspace and not platform data like other bias
controls?  The voltage to use will normally be determined by the
hardware design and the enable/disable should be managed via DAPM.

> +	case SND_SOC_DAPM_PRE_PMU:
> +		regmap_update_bits(priv->regmap, CS53L30_ASP1_CTL,
> +				   CS53L30_ASP1_3ST, 0);
> +		break;
> +	case SND_SOC_DAPM_POST_PMD:
> +		regmap_update_bits(priv->regmap, CS53L30_ASP1_CTL,
> +				   CS53L30_ASP1_3ST, 1);
> +		break;

Could this be a supply widget?

> +	regmap_read(priv->regmap, CS53L30_MCLKCTL, &mclk_ctl);
> +	mclk_ctl &= ~CS53L30_MCLK_DIV;
> +	mclk_ctl |= cs53l30_mclkx_coeffs[mclkx_coeff].mclkdiv;
> +
> +	regmap_update_bits(priv->regmap, CS53L30_MCLKCTL, CS53L30_MCLK_DIV,
> +			   mclk_ctl << CS53L30_MCLK_DIV);

You're using regmap_update_bits() but still implementing a manual
read/modify write cycle.  Just use regmap_update_bits.

> +	regmap_read(priv->regmap, CS53L30_ASPCFG_CTL, &asp_config_ctl);
> +
> +	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> +	case SND_SOC_DAIFMT_CBM_CFM:
> +		asp_config_ctl |= CS53L30_ASP_MS;

> +	priv->asp_config_ctl = asp_config_ctl;

The driver appears to be mixing use of priv->asp_config_ctl and the
physical value, that seems likely to go wrong somewhere along the line.
Either the variable is authorative and we should always work from that
then write it to the device when needed or the device is authorative but
currently we're mixing both.

> +	regmap_update_bits(priv->regmap, CS53L30_ASPCFG_CTL,
> +			   CS53L30_ASP_RATE_MASK, priv->asp_config_ctl);

We also write out via update_bits but don't specify all the bits
sometimes...
Nicolin Chen May 17, 2016, 11:43 p.m. UTC | #2
Hi Mark,

   I have two quick questions regarding the DAI and its TDM support.

On Tue, Mar 15, 2016 at 05:47:46PM -0500, tim.howe@cirrus.com wrote:

> +static const struct snd_soc_dapm_route cs53l30_audio_map[] = {
> +
> +	/* ADC Input Paths */
> +	{"ADC1A", NULL, "IN1_DMIC1"},
> +	{"Input Mux 1", "ADC1_SEL", "ADC1A"},
> +	{"ADC1B", NULL, "IN2"},
> +
> +	{"ADC2A", NULL, "IN3_DMIC2"},
> +	{"Input Mux 2", "ADC2_SEL", "ADC2A"},
> +	{"ADC2B", NULL, "IN4"},
> +
> +	/* MIC Bias Paths */
> +	{"ADC1A", NULL, "MIC1 Bias"},
> +	{"ADC1B", NULL, "MIC2 Bias"},
> +	{"ADC2A", NULL, "MIC3 Bias"},
> +	{"ADC2B", NULL, "MIC4 Bias"},
> +
> +	/* DMIC Paths */
> +	{"DMIC1", NULL, "IN1_DMIC1"},
> +	{"Input Mux 1", "DMIC1_SEL", "DMIC1"},
> +
> +	{"DMIC2", NULL, "IN3_DMIC2"},
> +	{"Input Mux 2", "DMIC2_SEL", "DMIC2"},
> +
> +	/* Output Paths */
> +	{"ASP_SDOUT1", NULL, "ADC1A" },
> +	{"ASP_SDOUT1", NULL, "Input Mux 1"},
> +	{"ASP_SDOUT1", NULL, "ADC1B"},
> +
> +	{"ASP_SDOUT2", NULL, "ADC2A"},
> +	{"ASP_SDOUT2", NULL, "Input Mux 2"},
> +	{"ASP_SDOUT2", NULL, "ADC2B"},
> +
> +	{"ASP1 Capture", NULL, "ASP_SDOUT1"},
> +	{"ASP2 Capture", NULL, "ASP_SDOUT2"},
> +};

This codec chip has two SDOUT (Serial Data Output) pins while sharing
one serial port -- there's only one pair of bit clock and fsync clock.

> +static struct snd_soc_dai_driver cs53l30_dai[] = {
> +	{
> +		.name = "cs53l30-asp1",
> +		.id = CS53L30_ASP1,
> +		.capture = {
> +			.stream_name = "ASP1 Capture",
> +			.channels_min = 1,
> +			.channels_max = 2,
> +			.rates = CS53L30_RATES,
> +			.formats = CS53L30_FORMATS,
> +		},
> +		.ops = &cs53l30_ops,
> +		.symmetric_rates = 1,
> +	 },
> +	{
> +		.name = "cs53l30-asp2",
> +		.id = CS53L30_ASP2,
> +		.capture = {
> +			.stream_name = "ASP2 Capture",
> +			.channels_min = 1,
> +			.channels_max = 2,
> +			.rates = CS53L30_RATES,
> +			.formats = CS53L30_FORMATS,
> +		},
> +		.ops = &cs53l30_ops,
> +		.symmetric_rates = 1,
> +	 }
> +};

In this case, would it be more appropriate to create one single DAI
with channels_max = 4 over here?

Another question is for its TDM support. This chip outputs 4-channel
data on two data output pins (SDOUT1 and SDOUT2) as normal mode; it
outputs 4-channel data on one data output pin (SDOUT1) as TDM mode.
However, the mode selection for a 4-channel recording should depend
on the hardware design: whether the SDOUT2 is connected or not. So
I am wondering if there is a common way or existing way to indicate
this hardware design. Or just by simply defining a new DT property?

Thank you
Nicolin
Mark Brown May 31, 2016, 4:53 p.m. UTC | #3
On Tue, May 17, 2016 at 04:43:33PM -0700, Nicolin Chen wrote:

> In this case, would it be more appropriate to create one single DAI
> with channels_max = 4 over here?

Yes.

> Another question is for its TDM support. This chip outputs 4-channel
> data on two data output pins (SDOUT1 and SDOUT2) as normal mode; it
> outputs 4-channel data on one data output pin (SDOUT1) as TDM mode.
> However, the mode selection for a 4-channel recording should depend
> on the hardware design: whether the SDOUT2 is connected or not. So
> I am wondering if there is a common way or existing way to indicate
> this hardware design. Or just by simply defining a new DT property?

That's a really rare thing to have as an option, most things either do
TDM or parallel data signals but not both.
Caleb Crome May 31, 2016, 5:10 p.m. UTC | #4
On Tue, May 31, 2016 at 9:53 AM, Mark Brown <broonie@kernel.org> wrote:
>
> On Tue, May 17, 2016 at 04:43:33PM -0700, Nicolin Chen wrote:
>
> > In this case, would it be more appropriate to create one single DAI
> > with channels_max = 4 over here?
>
> Yes.
>
> > Another question is for its TDM support. This chip outputs 4-channel
> > data on two data output pins (SDOUT1 and SDOUT2) as normal mode; it
> > outputs 4-channel data on one data output pin (SDOUT1) as TDM mode.
> > However, the mode selection for a 4-channel recording should depend
> > on the hardware design: whether the SDOUT2 is connected or not. So
> > I am wondering if there is a common way or existing way to indicate
> > this hardware design. Or just by simply defining a new DT property?
>
> That's a really rare thing to have as an option, most things either do
> TDM or parallel data signals but not both.


Interesting comment.  While I'm sure that's true for the moment,
microphone arrays are changing this quickly.  I fall into the oddball
category that the main chips I use are the TLV320AIC34 and CS53L30,
and both of them can switch between TDM mode or dual I2S mode for 4
channel support.

Since I need to get many channels on board, and SoCs (except for TI)
rarely have enough parallel I2S ports for mic arrays, I opt for TDM
mode often.

If you're using the CS53L30, chances are high that you're building a
mic array.  Then the question is, how many microphones?  up to 4, you
could live with dual I2S if your chip supports it.  Beyond 4, you're
almost certainly talking TDM, unless you have a TI McASP with lots of
inputs, or an XMOS part with multiple I2S interfaces.

Of course, it's highly dependent on what the SoC supports.  I would
say it's just about mandatory for the CS53L30 driver in particular to
be able to switch between TDM or I2S mode based on a DT setting
because of the huge variability of capabilities on the SoC side.   A
TI McBSP could support multiple I2S busses, but a freescale SSI must
use TDM mode.

-Caleb



>
>
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>
Paul Handrigan May 31, 2016, 5:33 p.m. UTC | #5
On 5/31/16, 12:10 PM, "Caleb Crome" <caleb@crome.org> wrote:

>On Tue, May 31, 2016 at 9:53 AM, Mark Brown <broonie@kernel.org> wrote:
>>
>> On Tue, May 17, 2016 at 04:43:33PM -0700, Nicolin Chen wrote:
>>
>> > In this case, would it be more appropriate to create one single DAI
>> > with channels_max = 4 over here?
>>
>> Yes.
>>
>> > Another question is for its TDM support. This chip outputs 4-channel
>> > data on two data output pins (SDOUT1 and SDOUT2) as normal mode; it
>> > outputs 4-channel data on one data output pin (SDOUT1) as TDM mode.
>> > However, the mode selection for a 4-channel recording should depend
>> > on the hardware design: whether the SDOUT2 is connected or not. So
>> > I am wondering if there is a common way or existing way to indicate
>> > this hardware design. Or just by simply defining a new DT property?
>>
>> That's a really rare thing to have as an option, most things either do
>> TDM or parallel data signals but not both.
>
>
>Interesting comment.  While I'm sure that's true for the moment,
>microphone arrays are changing this quickly.  I fall into the oddball
>category that the main chips I use are the TLV320AIC34 and CS53L30,
>and both of them can switch between TDM mode or dual I2S mode for 4
>channel support.
>
>Since I need to get many channels on board, and SoCs (except for TI)
>rarely have enough parallel I2S ports for mic arrays, I opt for TDM
>mode often.
>
>If you're using the CS53L30, chances are high that you're building a
>mic array.  Then the question is, how many microphones?  up to 4, you
>could live with dual I2S if your chip supports it.  Beyond 4, you're
>almost certainly talking TDM, unless you have a TI McASP with lots of
>inputs, or an XMOS part with multiple I2S interfaces.
>
>Of course, it's highly dependent on what the SoC supports.  I would
>say it's just about mandatory for the CS53L30 driver in particular to
>be able to switch between TDM or I2S mode based on a DT setting
>because of the huge variability of capabilities on the SoC side.   A
>TI McBSP could support multiple I2S busses, but a freescale SSI must
>use TDM mode.
>
>-Caleb

For this device you are using either I2S or TDM, therefore, it will
depend on the capabilities of the SoC (hence the hardware design).
This will not require a DT property since it is configured by the
cs53l30_set_dai_fmt callback.  As Calib mentioned, you will have to
use TDM if you are using greater than channels up to 16 channels for
a mic array.
 
>
>
>
>>
>>
>> _______________________________________________
>> Alsa-devel mailing list
>> Alsa-devel@alsa-project.org
>> 
>>https://urldefense.proofpoint.com/v2/url?u=http-3A__mailman.alsa-2Dprojec
>>t.org_mailman_listinfo_alsa-2Ddevel&d=DQIBaQ&c=O3LcjD-V2Iepl5V0N1424A&r=N
>>JtNI3T_InLOY17xIGk4jdUC7XljFdoy6miaxhGHOOI&m=Qrzd24GLzVm_xD3O3sFLrdCF9bUr
>>dZ_DqkhoIuAy6b0&s=szG5oWmXcuMj-OEhja5V7GMcXXMCfLn9ia0maAr8YG8&e=
>>
Mark Brown May 31, 2016, 5:35 p.m. UTC | #6
On Tue, May 31, 2016 at 10:10:11AM -0700, Caleb Crome wrote:
> On Tue, May 31, 2016 at 9:53 AM, Mark Brown <broonie@kernel.org> wrote:

> > That's a really rare thing to have as an option, most things either do
> > TDM or parallel data signals but not both.

> Interesting comment.  While I'm sure that's true for the moment,
> microphone arrays are changing this quickly.  I fall into the oddball
> category that the main chips I use are the TLV320AIC34 and CS53L30,
> and both of them can switch between TDM mode or dual I2S mode for 4
> channel support.

I'm not sure why mic arrays would drive that, it's not like they're
particularly new or innovative technology here and multi channel output
has been even more widely available for a long time?

> Since I need to get many channels on board, and SoCs (except for TI)
> rarely have enough parallel I2S ports for mic arrays, I opt for TDM
> mode often.

Modern systems all use TDM for the most part, the usage of parallel
data lines that I've seen has been for surround sound applications where
a 5.1 or 7.1 decoder will often be built by taking a bunch of high end
stereo CODECs and wiring them up in parallel, partly for performance and
physical design reasons and partly because such system designs have
often had their roots in very old systems.

This also matches the trend with more modern SoCs to use programmable
serial ports rather than dedicated I2S controllers so TDM is very easy
to configure, and of course it's fewer signals so it's easier from a
board design point of view too.
Caleb Crome May 31, 2016, 10:32 p.m. UTC | #7
On Tue, May 31, 2016 at 10:35 AM, Mark Brown <broonie@kernel.org> wrote:

> On Tue, May 31, 2016 at 10:10:11AM -0700, Caleb Crome wrote:
> > On Tue, May 31, 2016 at 9:53 AM, Mark Brown <broonie@kernel.org> wrote:
>
> > > That's a really rare thing to have as an option, most things either do
> > > TDM or parallel data signals but not both.
>
> > Interesting comment.  While I'm sure that's true for the moment,
> > microphone arrays are changing this quickly.  I fall into the oddball
> > category that the main chips I use are the TLV320AIC34 and CS53L30,
> > and both of them can switch between TDM mode or dual I2S mode for 4
> > channel support.
>
> I'm not sure why mic arrays would drive that, it's not like they're
> particularly new or innovative technology here and multi channel output
> has been even more widely available for a long time?
>

Good point.  I guess I'm coming from the SoC/mobile world where most SoCs
support stereo only, and a couple microphones at most.  Now phones have 3-4
or more microphones.   Amazon echo has 7 mics.  We're working on arrays of
16 and beyond, and getting those channels into linux has proven to be an
enormous headache given the hardware and software status of many of the
SoCs our customers want to use.


>
> > Since I need to get many channels on board, and SoCs (except for TI)
> > rarely have enough parallel I2S ports for mic arrays, I opt for TDM
> > mode often.
>
> Modern systems all use TDM for the most part ...
>

We must be looking at different sorts of chips :-)  Even on new parts like
the Snapdragon 410, they have a super inflexible I2S only port. (up to 3
parallel I2S for up to 6 channels, but no TDM).

From my experience, the SoC support for TDM has been essentially
nonexistent, except for TI's McBSP and McASP.  I'd sure welcome a Cortex
A53 or A57 (or even A15 for that matter!) that has good TDM support.

Here are the chips that we've come across lately from our customers:

MX6 (SSI):  hardware supports TDM, but linux didn't until recently (thanks
everybody for help with that!)
MX6 (ESAI): not available on MX6 solo, nor on any SOM modules I've ever
seen, but supported if you can get to the pins.
MX7: (SAI):  hardware supports TDM, linux BSP driver does not.
TI parts (McASP): supported in hardware & software
TI parts (McBSP): supported in hardware & software
Qualcomm Snapdragon 410c (MI2S):  TDM not possible in hardware.  Supports
multiple parallel I2S channels only.
Broadcom:  (forgot part number...)  hardware supports TDM.  software still
in the works but apparently somebody's working on it.

A quick grep for channels_max > 2 in sound/soc shows the following few
drivers that support channels > 2 (not necessarily even in TDM mode).  All
the rest are 2 channels max.

pxa/mmp-sspa: 128/2 (playback/capture).
pxa/pxa-ssp:     8/8
zx286702-i2s.c:  8/2
fsl_esai: 12/8
fsl_ssi: 32/32
blackfin 5:  8/8 channels
blackfin 6:  2/2 channels only.
mcbsp: 16/16
mcasp: 512/512

and a few other chips that I'm not familiar with.  So  there does not seem
to be overwhelming support for TDM in Alsa ASoC.  To what extent this is a
software vs. hardware issue could use a little investigation.

the bummer is that even newer chips like the snapdragon are still using
really inflexible ports.

So... if anybody knows of a Cortex A53 or A57 with good TDM support in
linux (at least 16 channels in and out), I'd love to know about it :-)

-Caleb
Mark Brown June 2, 2016, 5:17 p.m. UTC | #8
On Tue, May 31, 2016 at 03:32:05PM -0700, Caleb Crome wrote:
> On Tue, May 31, 2016 at 10:35 AM, Mark Brown <broonie@kernel.org> wrote:

> > I'm not sure why mic arrays would drive that, it's not like they're
> > particularly new or innovative technology here and multi channel output
> > has been even more widely available for a long time?

> Good point.  I guess I'm coming from the SoC/mobile world where most SoCs
> support stereo only, and a couple microphones at most.  Now phones have 3-4
> or more microphones.   Amazon echo has 7 mics.  We're working on arrays of

That's fairly rare, I'd say more of the vendors are doing programmable
serial ports and those that aren't have a different model...  see below.

> We must be looking at different sorts of chips :-)  Even on new parts like
> the Snapdragon 410, they have a super inflexible I2S only port. (up to 3
> parallel I2S for up to 6 channels, but no TDM).

Qualcomm have never really wanted anyone to use I2S in their mobile
products for quite some time now - they've always wanted to do a system
sale based on either their integrated CODECs or (in the current
production designs) their own external CODECs which normally use Slimbus
to connect to the CPU (they are working off and on on upstreaming
Slimbus, nobody else ever adopted it so there's relatively little
pressure).

> and a few other chips that I'm not familiar with.  So  there does not seem
> to be overwhelming support for TDM in Alsa ASoC.  To what extent this is a
> software vs. hardware issue could use a little investigation.

It's software and legacy hardware.

> So... if anybody knows of a Cortex A53 or A57 with good TDM support in
> linux (at least 16 channels in and out), I'd love to know about it :-)

Well, if you specifically mean TDM as opposed to multi-channel our
general support for that isn't good (mainly due to lack of demand).  It
sounds like you're more interested in multi channel here though.
Caleb Crome June 2, 2016, 5:40 p.m. UTC | #9
On Thu, Jun 2, 2016 at 10:17 AM, Mark Brown <broonie@kernel.org> wrote:
> On Tue, May 31, 2016 at 03:32:05PM -0700, Caleb Crome wrote:
>> On Tue, May 31, 2016 at 10:35 AM, Mark Brown <broonie@kernel.org> wrote:
>
>> > I'm not sure why mic arrays would drive that, it's not like they're
>> > particularly new or innovative technology here and multi channel output
>> > has been even more widely available for a long time?
>
>> Good point.  I guess I'm coming from the SoC/mobile world where most SoCs
>> support stereo only, and a couple microphones at most.  Now phones have 3-4
>> or more microphones.   Amazon echo has 7 mics.  We're working on arrays of
>
> That's fairly rare, I'd say more of the vendors are doing programmable
> serial ports and those that aren't have a different model...  see below.
>
>> We must be looking at different sorts of chips :-)  Even on new parts like
>> the Snapdragon 410, they have a super inflexible I2S only port. (up to 3
>> parallel I2S for up to 6 channels, but no TDM).
>
> Qualcomm have never really wanted anyone to use I2S in their mobile
> products for quite some time now - they've always wanted to do a system
> sale based on either their integrated CODECs or (in the current
> production designs) their own external CODECs which normally use Slimbus
> to connect to the CPU (they are working off and on on upstreaming
> Slimbus, nobody else ever adopted it so there's relatively little
> pressure).
>
>> and a few other chips that I'm not familiar with.  So  there does not seem
>> to be overwhelming support for TDM in Alsa ASoC.  To what extent this is a
>> software vs. hardware issue could use a little investigation.
>
> It's software and legacy hardware.
>
>> So... if anybody knows of a Cortex A53 or A57 with good TDM support in
>> linux (at least 16 channels in and out), I'd love to know about it :-)
>
> Well, if you specifically mean TDM as opposed to multi-channel our
> general support for that isn't good (mainly due to lack of demand).  It
> sounds like you're more interested in multi channel here though.

I must be missing something.  To me, TDM is synonymous with
multi-channel on a single wire. The datasheets of codecs and SoCs
invariably refer to TDM as putting multiple slots onto a single wire,
and not necessarily the actual bit-format and clocking of that wire.
The actual clocking and bits are generally configurable, but the
concept of TDM in the datasheets seems to be universal and have the
same meaning -- multi-channel on a wire.

(I recall there is also a specific TDM format with something like 384
slots per frame, which is a well defined thing, but that's not what
I'm talking about.  That's used just for telephony I beleive).


What's the linuxy name for what the datasheets call TDM format with
slots > 2?  Do we just call that 'multi-channel', or specifically say,
'channels_max > 2'?  I was always under the assumption that's what TDM
meant.

Thanks,
   -Caleb
Mark Brown June 2, 2016, 6:40 p.m. UTC | #10
On Thu, Jun 02, 2016 at 10:40:58AM -0700, Caleb Crome wrote:
> On Thu, Jun 2, 2016 at 10:17 AM, Mark Brown <broonie@kernel.org> wrote:

> > Well, if you specifically mean TDM as opposed to multi-channel our
> > general support for that isn't good (mainly due to lack of demand).  It
> > sounds like you're more interested in multi channel here though.

> I must be missing something.  To me, TDM is synonymous with
> multi-channel on a single wire. The datasheets of codecs and SoCs

TDM, at least in the sense Linux is using it, is multiple *unrelated*
audio streams on a single wire.  Any multi-channel audio stream is TDM
in some sense but the trivial extension to add two or more channels
isn't really a big deal.  This is for things more complex than just
stuffing more bytes of data onto a wire where there are going to be some
timeslots that the device should ignore as they're going to/from other
devices (or at least other streams even if the same chip is handling
them).

> (I recall there is also a specific TDM format with something like 384
> slots per frame, which is a well defined thing, but that's not what
> I'm talking about.  That's used just for telephony I beleive).

You're thinking of H.1x0 CTBus which is genuine TDM, used for routing
single channel audio streams between multiple PCI (H.100) or cPCI
(H.110) cards in a system.  It does way more than 384 slots, 4096
8kHz 8 bit timeslots IIRC.

> What's the linuxy name for what the datasheets call TDM format with
> slots > 2?  Do we just call that 'multi-channel', or specifically say,
> 'channels_max > 2'?  I was always under the assumption that's what TDM
> meant.

We don't particularly call it anything, it's such a trivial extension.
Caleb Crome June 5, 2016, 4:48 p.m. UTC | #11
On Thu, Jun 2, 2016 at 11:40 AM, Mark Brown <broonie@kernel.org> wrote:
> On Thu, Jun 02, 2016 at 10:40:58AM -0700, Caleb Crome wrote:
>> On Thu, Jun 2, 2016 at 10:17 AM, Mark Brown <broonie@kernel.org> wrote:
>
>> > Well, if you specifically mean TDM as opposed to multi-channel our
>> > general support for that isn't good (mainly due to lack of demand).  It
>> > sounds like you're more interested in multi channel here though.
>
>> I must be missing something.  To me, TDM is synonymous with
>> multi-channel on a single wire. The datasheets of codecs and SoCs
>
> TDM, at least in the sense Linux is using it, is multiple *unrelated*
> audio streams on a single wire.  Any multi-channel audio stream is TDM
> in some sense but the trivial extension to add two or more channels
> isn't really a big deal.  This is for things more complex than just
> stuffing more bytes of data onto a wire where there are going to be some
> timeslots that the device should ignore as they're going to/from other
> devices (or at least other streams even if the same chip is handling
> them).

Hi Mark,
   Thanks for taking the time to describe this to me.  This definitely
comes as a surprise to me.  I assumed that TDM mode simply means
multi-channels on a single wire (which is what it means in all the
datasheets).

Just so I have this straight, TDM in the linux sense is putting say,
6-channels on one wire where the channels are from logically different
places? i.e. chanels 0-1 are from bluetooth, 2-3 from analog in, and
4-5 from somewhere else?

So, going back to Nicolin's original question for this email, the
CS53L30, what are the proper DT settings for these modes:
The device can be configured in what amounts to at least 3 different modes:

1) 4-channel, 5-pin ADC interface:  dual I2S: shared BCLK/WCLK/MCLK,
separate data pins for channels 0/1 and 2/3 (SDOUT0/SDOUT1).
2) 4-channel, 4-pin ADC interface: MBLCK/BCLK/WCLK/SDOUT0 only.  Slots
0/1/2/3 on the SDOUT0 pin (synch master)
3) and finally, a multi-codec configuration, where 4 x CS53L30 are
used:  16-channel, 4-pin ADC (what I call TDM) interface, all on the
same 4 pins.  These are very related data streams -- they are
perfectly synchronized in hardware, so perhaps this isn't TDM in the
linux-sense.

>> Nicolin Wrote:
>> Another question is for its TDM support. This chip outputs 4-channel
>> data on two data output pins (SDOUT1 and SDOUT2) as normal mode; it
>> outputs 4-channel data on one data output pin (SDOUT1) as TDM mode.
>> However, the mode selection for a 4-channel recording should depend
>> on the hardware design: whether the SDOUT2 is connected or not. So
>> I am wondering if there is a common way or existing way to indicate
>> this hardware design. Or just by simply defining a new DT property?

As far as I can see he's not trying to define *unrelated* streams in
TDM mode, but very related streams, which is TDM in the
datasheet-sense.  And there not only needs to be a mechanism of
choosing the dual I2S mode, but also which TDM slots to drop the data
in (which I think already exists, right?)


>> What's the linuxy name for what the datasheets call TDM format with
>> slots > 2?  Do we just call that 'multi-channel', or specifically say,
>> 'channels_max > 2'?  I was always under the assumption that's what TDM
>> meant.
>
> We don't particularly call it anything, it's such a trivial extension.

That's not been my experience :-) Getting 16 channels onto a wire has
been anything but trivial because of the lack of SoC driver support
for it.  Perhaps I'm just using the wrong SoCs.

Thanks again,
 -Caleb
Mark Brown June 6, 2016, 6:11 p.m. UTC | #12
On Sun, Jun 05, 2016 at 09:48:36AM -0700, Caleb Crome wrote:
> On Thu, Jun 2, 2016 at 11:40 AM, Mark Brown <broonie@kernel.org> wrote:

> > TDM, at least in the sense Linux is using it, is multiple *unrelated*
> > audio streams on a single wire.  Any multi-channel audio stream is TDM
> > in some sense but the trivial extension to add two or more channels

>    Thanks for taking the time to describe this to me.  This definitely
> comes as a surprise to me.  I assumed that TDM mode simply means
> multi-channels on a single wire (which is what it means in all the
> datasheets).

It *really* depends on the datasheets you're looking at.  Things that
are doing generic programmable serial ports might have no concept of
channels and inflexible devices which just support configurable numbers
of channels are just as likely to refer to DSP mode or whatever as
anything else.

> Just so I have this straight, TDM in the linux sense is putting say,
> 6-channels on one wire where the channels are from logically different
> places? i.e. chanels 0-1 are from bluetooth, 2-3 from analog in, and
> 4-5 from somewhere else?

You also need some separation of devices for us to actually care - if
none of the hardware can tell this is going on it's not meaningfully
TDM.

> As far as I can see he's not trying to define *unrelated* streams in
> TDM mode, but very related streams, which is TDM in the
> datasheet-sense.  And there not only needs to be a mechanism of
> choosing the dual I2S mode, but also which TDM slots to drop the data
> in (which I think already exists, right?)

Yes.

> >> 'channels_max > 2'?  I was always under the assumption that's what TDM
> >> meant.

> > We don't particularly call it anything, it's such a trivial extension.

> That's not been my experience :-) Getting 16 channels onto a wire has
> been anything but trivial because of the lack of SoC driver support
> for it.  Perhaps I'm just using the wrong SoCs.

Yes, you're using the wrong SoCs - anything with a programmable serial
port should cope fine.
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/sound/cs53l30.txt b/Documentation/devicetree/bindings/sound/cs53l30.txt
new file mode 100644
index 0000000..a2dff95
+++ b/Documentation/devicetree/bindings/sound/cs53l30.txt
@@ -0,0 +1,21 @@ 
+CS53L30 audio CODEC
+
+Required properties:
+
+  - compatible : "cirrus,cs53l30"
+
+  - reg : the I2C address of the device
+
+Optional properties:
+
+  - reset-gpios : a GPIO spec for the reset pin.
+
+
+Example:
+
+codec: cs53l30@48 {
+        compatible = "cirrus,cs53l30";
+        reg = <0x48>;
+        reset-gpios = <&gpio 54 0>;
+};
+
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index b282169..2dd97d4 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -60,6 +60,7 @@  config SND_SOC_ALL_CODECS
 	select SND_SOC_CS4271_SPI if SPI_MASTER
 	select SND_SOC_CS42XX8_I2C if I2C
 	select SND_SOC_CS4349 if I2C
+	select SND_SOC_CS53L30 if I2C
 	select SND_SOC_CX20442 if TTY
 	select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_DA7213 if I2C
@@ -449,6 +450,11 @@  config SND_SOC_CS4349
 	tristate "Cirrus Logic CS4349 CODEC"
 	depends on I2C
 
+# Cirrus Logic Quad-Channel ADC
+config SND_SOC_CS53L30
+	tristate "Cirrus Logic CS53L30 CODEC"
+	depends on I2C
+
 config SND_SOC_CX20442
 	tristate
 	depends on TTY
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 81324bc..88dff59 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -52,6 +52,7 @@  snd-soc-cs4271-spi-objs := cs4271-spi.o
 snd-soc-cs42xx8-objs := cs42xx8.o
 snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
 snd-soc-cs4349-objs := cs4349.o
+snd-soc-cs53l30-objs := cs53l30.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
 snd-soc-da7213-objs := da7213.o
@@ -252,6 +253,7 @@  obj-$(CONFIG_SND_SOC_CS4271_SPI)	+= snd-soc-cs4271-spi.o
 obj-$(CONFIG_SND_SOC_CS42XX8)	+= snd-soc-cs42xx8.o
 obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
 obj-$(CONFIG_SND_SOC_CS4349)	+= snd-soc-cs4349.o
+obj-$(CONFIG_SND_SOC_CS53L30)	+= snd-soc-cs53l30.o
 obj-$(CONFIG_SND_SOC_CX20442)	+= snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)	+= snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_DA7213)	+= snd-soc-da7213.o