summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhani Kumar Uppalapati <phaniu@codeaurora.org>2016-07-20 16:43:15 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-08-26 11:59:05 -0700
commit10b823cd45fd688d25f5c82765cc5a90ea8208a7 (patch)
treea22a7fe7e457944931a30f82c7678356b38c2ff4
parent1aa6e9851e9dfbcc395d2f240dc810f5ba005775 (diff)
ASoC: wcd934x: Add support for DSD audio playback
Add DAPM (Dynamic Audio Power Management) widgets and routing to enable support for DSD (Direct Stream Digital) audio playback on wcd934x codec. Change-Id: I06e1b0134cea58adedbd9113a51529b2b73da835 Signed-off-by: Phani Kumar Uppalapati <phaniu@codeaurora.org>
-rw-r--r--sound/soc/codecs/Kconfig4
-rw-r--r--sound/soc/codecs/wcd934x/Makefile2
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-dsd.c557
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-dsd.h89
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-routing.h6
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x.c197
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x.h17
7 files changed, 852 insertions, 20 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 49c4087c7408..38b8a806584f 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -713,6 +713,9 @@ config SND_SOC_UDA134X
config SND_SOC_UDA1380
tristate
+config SND_SOC_WCD934X_DSD
+ tristate
+
config SND_SOC_WCD9320
tristate
@@ -732,6 +735,7 @@ config SND_SOC_WCD934X
select SND_SOC_WCD_DSP_MGR
select SND_SOC_WCD_SPI
select SND_SOC_WCD934X_MBHC
+ select SND_SOC_WCD934X_DSD
config SND_SOC_WCD934X_MBHC
tristate
diff --git a/sound/soc/codecs/wcd934x/Makefile b/sound/soc/codecs/wcd934x/Makefile
index 09adfba3f0a4..2843fa11d58e 100644
--- a/sound/soc/codecs/wcd934x/Makefile
+++ b/sound/soc/codecs/wcd934x/Makefile
@@ -5,3 +5,5 @@ snd-soc-wcd934x-objs := wcd934x.o wcd934x-dsp-cntl.o
obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o
snd-soc-wcd934x-mbhc-objs := wcd934x-mbhc.o
obj-$(CONFIG_SND_SOC_WCD934X_MBHC) += snd-soc-wcd934x-mbhc.o
+snd-soc-wcd934x-dsd-objs := wcd934x-dsd.o
+obj-$(CONFIG_SND_SOC_WCD934X_DSD) += snd-soc-wcd934x-dsd.o
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.c b/sound/soc/codecs/wcd934x/wcd934x-dsd.c
new file mode 100644
index 000000000000..ef20c53eeb96
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.c
@@ -0,0 +1,557 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mfd/wcd934x/registers.h>
+#include <sound/control.h>
+#include "wcd934x-dsd.h"
+
+static const char *const dsd_if_text[] = {
+ "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7",
+ "DSD_DATA_PAD"
+};
+
+static const char * const dsd_filt0_mux_text[] = {
+ "ZERO", "DSD_L IF MUX",
+};
+
+static const char * const dsd_filt1_mux_text[] = {
+ "ZERO", "DSD_R IF MUX",
+};
+
+static const struct soc_enum dsd_filt0_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_DSD0_PATH_CTL, 0,
+ ARRAY_SIZE(dsd_filt0_mux_text), dsd_filt0_mux_text);
+
+static const struct soc_enum dsd_filt1_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_DSD1_PATH_CTL, 0,
+ ARRAY_SIZE(dsd_filt1_mux_text), dsd_filt1_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(dsd_l_if_enum, WCD934X_CDC_DSD0_CFG0,
+ 2, dsd_if_text);
+static SOC_ENUM_SINGLE_DECL(dsd_r_if_enum, WCD934X_CDC_DSD1_CFG0,
+ 2, dsd_if_text);
+
+static const struct snd_kcontrol_new dsd_filt0_mux =
+ SOC_DAPM_ENUM("DSD Filt0 Mux", dsd_filt0_mux_enum);
+
+static const struct snd_kcontrol_new dsd_filt1_mux =
+ SOC_DAPM_ENUM("DSD Filt1 Mux", dsd_filt1_mux_enum);
+
+static const struct snd_kcontrol_new dsd_l_if_mux =
+ SOC_DAPM_ENUM("DSD Left If Mux", dsd_l_if_enum);
+static const struct snd_kcontrol_new dsd_r_if_mux =
+ SOC_DAPM_ENUM("DSD Right If Mux", dsd_r_if_enum);
+
+static const struct snd_soc_dapm_route tavil_dsd_audio_map[] = {
+ {"DSD_L IF MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"DSD_L IF MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"DSD_L IF MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"DSD_L IF MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"DSD_L IF MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"DSD_L IF MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"DSD_L IF MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"DSD_L IF MUX", "RX7", "CDC_IF RX7 MUX"},
+
+ {"DSD_FILTER_0", NULL, "DSD_L IF MUX"},
+ {"DSD_FILTER_0", NULL, "RX INT1 NATIVE SUPPLY"},
+ {"RX INT1 MIX3", "DSD HPHL Switch", "DSD_FILTER_0"},
+
+ {"DSD_R IF MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"DSD_R IF MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"DSD_R IF MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"DSD_R IF MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"DSD_R IF MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"DSD_R IF MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"DSD_R IF MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"DSD_R IF MUX", "RX7", "CDC_IF RX7 MUX"},
+
+ {"DSD_FILTER_1", NULL, "DSD_R IF MUX"},
+ {"DSD_FILTER_1", NULL, "RX INT2 NATIVE SUPPLY"},
+ {"RX INT2 MIX3", "DSD HPHR Switch", "DSD_FILTER_1"},
+};
+
+static bool is_valid_dsd_interpolator(int interp_num)
+{
+ if ((interp_num == INTERP_HPHL) || (interp_num == INTERP_HPHR) ||
+ (interp_num == INTERP_LO1) || (interp_num == INTERP_LO2))
+ return true;
+
+ return false;
+}
+
+/**
+ * tavil_dsd_set_mixer_value - Set DSD HPH/LO mixer value
+ *
+ * @dsd_conf: pointer to dsd config
+ * @interp_num: Interpolator number (HPHL/R, LO1/2)
+ * @sw_value: Mixer switch value
+ *
+ * Returns 0 on success or -EINVAL on failure
+ */
+int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf,
+ int interp_num, int sw_value)
+{
+ if (!dsd_conf)
+ return -EINVAL;
+
+ if (!is_valid_dsd_interpolator(interp_num))
+ return -EINVAL;
+
+ dsd_conf->dsd_interp_mixer[interp_num] = !!sw_value;
+
+ return 0;
+}
+EXPORT_SYMBOL(tavil_dsd_set_mixer_value);
+
+/**
+ * tavil_dsd_get_current_mixer_value - Get DSD HPH/LO mixer value
+ *
+ * @dsd_conf: pointer to dsd config
+ * @interp_num: Interpolator number (HPHL/R, LO1/2)
+ *
+ * Returns current mixer val for success or -EINVAL for failure
+ */
+int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf,
+ int interp_num)
+{
+ if (!dsd_conf)
+ return -EINVAL;
+
+ if (!is_valid_dsd_interpolator(interp_num))
+ return -EINVAL;
+
+ return dsd_conf->dsd_interp_mixer[interp_num];
+}
+EXPORT_SYMBOL(tavil_dsd_get_current_mixer_value);
+
+/**
+ * tavil_dsd_set_out_select - DSD0/1 out select to HPH or LO
+ *
+ * @dsd_conf: pointer to dsd config
+ * @interp_num: Interpolator number (HPHL/R, LO1/2)
+ *
+ * Returns 0 for success or -EINVAL for failure
+ */
+int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf,
+ int interp_num)
+{
+ unsigned int reg, val;
+ struct snd_soc_codec *codec;
+
+ if (!dsd_conf || !dsd_conf->codec)
+ return -EINVAL;
+
+ codec = dsd_conf->codec;
+
+ if (!is_valid_dsd_interpolator(interp_num)) {
+ dev_err(codec->dev, "%s: Invalid Interpolator: %d for DSD\n",
+ __func__, interp_num);
+ return -EINVAL;
+ }
+
+ switch (interp_num) {
+ case INTERP_HPHL:
+ reg = WCD934X_CDC_DSD0_CFG0;
+ val = 0x00;
+ break;
+ case INTERP_HPHR:
+ reg = WCD934X_CDC_DSD1_CFG0;
+ val = 0x00;
+ break;
+ case INTERP_LO1:
+ reg = WCD934X_CDC_DSD0_CFG0;
+ val = 0x02;
+ break;
+ case INTERP_LO2:
+ reg = WCD934X_CDC_DSD1_CFG0;
+ val = 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, reg, 0x02, val);
+
+ return 0;
+}
+EXPORT_SYMBOL(tavil_dsd_set_out_select);
+
+/**
+ * tavil_dsd_reset - Reset DSD block
+ *
+ * @dsd_conf: pointer to dsd config
+ *
+ */
+void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf)
+{
+ if (!dsd_conf || !dsd_conf->codec)
+ return;
+
+ snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD0_PATH_CTL,
+ 0x02, 0x02);
+ snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD0_PATH_CTL,
+ 0x01, 0x00);
+ snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD1_PATH_CTL,
+ 0x02, 0x02);
+ snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD1_PATH_CTL,
+ 0x01, 0x00);
+}
+EXPORT_SYMBOL(tavil_dsd_reset);
+
+/**
+ * tavil_dsd_set_interp_rate - Set interpolator rate for DSD
+ *
+ * @dsd_conf: pointer to dsd config
+ * @rx_port: RX port number
+ * @sample_rate: Sample rate of the RX interpolator
+ * @sample_rate_val: Interpolator rate value
+ */
+void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port,
+ u32 sample_rate, u8 sample_rate_val)
+{
+ u8 dsd_inp_sel;
+ u8 dsd0_inp, dsd1_inp;
+ u8 val0, val1;
+ u8 dsd0_out_sel, dsd1_out_sel;
+ u16 int_fs_reg, interp_num = 0;
+ struct snd_soc_codec *codec;
+
+ if (!dsd_conf || !dsd_conf->codec)
+ return;
+
+ codec = dsd_conf->codec;
+
+ dsd_inp_sel = DSD_INP_SEL_RX0 + rx_port - WCD934X_RX_PORT_START_NUMBER;
+
+ val0 = snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0);
+ val1 = snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0);
+ dsd0_inp = (val0 & 0x3C) >> 2;
+ dsd1_inp = (val1 & 0x3C) >> 2;
+ dsd0_out_sel = (val0 & 0x02) >> 1;
+ dsd1_out_sel = (val1 & 0x02) >> 1;
+
+ /* Set HPHL or LO1 interp rate based on out select */
+ if (dsd_inp_sel == dsd0_inp) {
+ interp_num = dsd0_out_sel ? INTERP_LO1 : INTERP_HPHL;
+ dsd_conf->base_sample_rate[DSD0] = sample_rate;
+ }
+
+ /* Set HPHR or LO2 interp rate based on out select */
+ if (dsd_inp_sel == dsd1_inp) {
+ interp_num = dsd1_out_sel ? INTERP_LO2 : INTERP_HPHR;
+ dsd_conf->base_sample_rate[DSD1] = sample_rate;
+ }
+
+ if (interp_num) {
+ int_fs_reg = WCD934X_CDC_RX0_RX_PATH_CTL + 20 * interp_num;
+ if ((snd_soc_read(codec, int_fs_reg) & 0x0f) < 0x09) {
+ dev_dbg(codec->dev, "%s: Set Interp %d to sample_rate val 0x%x\n",
+ __func__, interp_num, sample_rate_val);
+ snd_soc_update_bits(codec, int_fs_reg, 0x0F,
+ sample_rate_val);
+ }
+ }
+}
+EXPORT_SYMBOL(tavil_dsd_set_interp_rate);
+
+static int tavil_set_dsd_mode(struct snd_soc_codec *codec, int dsd_num,
+ u8 *pcm_rate_val)
+{
+ unsigned int dsd_out_sel_reg;
+ u8 dsd_mode;
+ u32 sample_rate;
+ struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec);
+
+ if (!dsd_conf)
+ return -EINVAL;
+
+ if ((dsd_num < 0) || (dsd_num > 1))
+ return -EINVAL;
+
+ sample_rate = dsd_conf->base_sample_rate[dsd_num];
+ dsd_out_sel_reg = WCD934X_CDC_DSD0_CFG0 + dsd_num * 16;
+
+ switch (sample_rate) {
+ case 176400:
+ dsd_mode = 0; /* DSD_64 */
+ *pcm_rate_val = 0xb;
+ break;
+ case 352800:
+ dsd_mode = 1; /* DSD_128 */
+ *pcm_rate_val = 0xc;
+ break;
+ default:
+ dev_err(codec->dev, "%s: Invalid DSD rate: %d\n",
+ __func__, sample_rate);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, dsd_out_sel_reg, 0x01, dsd_mode);
+
+ return 0;
+}
+
+static void tavil_dsd_data_pull(struct snd_soc_codec *codec, int dsd_num,
+ u8 pcm_rate_val, bool enable)
+{
+ u8 clk_en, mute_en;
+ u8 dsd_inp_sel;
+
+ if (enable) {
+ clk_en = 0x20;
+ mute_en = 0x10;
+ } else {
+ clk_en = 0x00;
+ mute_en = 0x00;
+ }
+
+ if (dsd_num & 0x01) {
+ snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_MIX_CTL,
+ 0x20, clk_en);
+ dsd_inp_sel = (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) &
+ 0x3C) >> 2;
+ dsd_inp_sel = (enable) ? dsd_inp_sel : 0;
+ if (dsd_inp_sel < 9) {
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1,
+ 0x0F, dsd_inp_sel);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX7_RX_PATH_MIX_CTL,
+ 0x0F, pcm_rate_val);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX7_RX_PATH_MIX_CTL,
+ 0x10, mute_en);
+ }
+ }
+ if (dsd_num & 0x02) {
+ snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_MIX_CTL,
+ 0x20, clk_en);
+ dsd_inp_sel = (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) &
+ 0x3C) >> 2;
+ dsd_inp_sel = (enable) ? dsd_inp_sel : 0;
+ if (dsd_inp_sel < 9) {
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1,
+ 0x0F, dsd_inp_sel);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX8_RX_PATH_MIX_CTL,
+ 0x0F, pcm_rate_val);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX8_RX_PATH_MIX_CTL,
+ 0x10, mute_en);
+ }
+ }
+}
+
+static int tavil_enable_dsd(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ int rc, clk_users;
+ int interp_idx;
+ u8 pcm_rate_val;
+
+ if (w->shift == DSD0) {
+ /* Read out select */
+ if (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) & 0x02)
+ interp_idx = INTERP_LO1;
+ else
+ interp_idx = INTERP_HPHL;
+ } else if (w->shift == DSD1) {
+ /* Read out select */
+ if (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) & 0x02)
+ interp_idx = INTERP_LO2;
+ else
+ interp_idx = INTERP_HPHR;
+ } else {
+ dev_err(codec->dev, "%s: Unsupported DSD:%d\n",
+ __func__, w->shift);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ clk_users = tavil_codec_enable_interp_clk(codec, event,
+ interp_idx);
+
+ rc = tavil_set_dsd_mode(codec, w->shift, &pcm_rate_val);
+ if (rc)
+ return rc;
+
+ tavil_dsd_data_pull(codec, (1 << w->shift), pcm_rate_val,
+ true);
+
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL, 0x01,
+ 0x01);
+ if (w->shift == DSD0) {
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL,
+ 0x02, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL,
+ 0x01, 0x01);
+ /* Apply Gain */
+ snd_soc_write(codec, WCD934X_CDC_DSD0_CFG1,
+ snd_soc_read(codec, WCD934X_CDC_DSD0_CFG1));
+
+ if (clk_users > 1)
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_DSD0_CFG2,
+ 0x04, 0x00);
+ } else if (w->shift == DSD1) {
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL,
+ 0x02, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL,
+ 0x01, 0x01);
+ /* Apply Gain */
+ snd_soc_write(codec, WCD934X_CDC_DSD1_CFG1,
+ snd_soc_read(codec, WCD934X_CDC_DSD1_CFG1));
+
+ if (clk_users > 1)
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_DSD1_CFG2,
+ 0x04, 0x00);
+ }
+ /* 10msec sleep required after DSD clock is set */
+ usleep_range(10000, 10100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (w->shift == DSD0) {
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2,
+ 0x04, 0x04);
+ } else if (w->shift == DSD1) {
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2,
+ 0x04, 0x04);
+ }
+
+ tavil_codec_enable_interp_clk(codec, event, interp_idx);
+
+ if (!(snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01) &&
+ !(snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) {
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL,
+ 0x01, 0x00);
+ tavil_dsd_data_pull(codec, 0x03, 0x04, false);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tavil_dsd_widgets[] = {
+ SND_SOC_DAPM_MUX("DSD_L IF MUX", SND_SOC_NOPM, 0, 0, &dsd_l_if_mux),
+ SND_SOC_DAPM_MUX_E("DSD_FILTER_0", SND_SOC_NOPM, 0, 0, &dsd_filt0_mux,
+ tavil_enable_dsd,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("DSD_R IF MUX", SND_SOC_NOPM, 0, 0, &dsd_r_if_mux),
+ SND_SOC_DAPM_MUX_E("DSD_FILTER_1", SND_SOC_NOPM, 1, 0, &dsd_filt1_mux,
+ tavil_enable_dsd,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+/**
+ * tavil_dsd_init - DSD intialization
+ *
+ * @codec: pointer to snd_soc_codec
+ *
+ * Returns pointer to tavil_dsd_config for success or NULL for failure
+ */
+struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm;
+ struct tavil_dsd_config *dsd_conf;
+ u8 val;
+
+ if (!codec)
+ return NULL;
+
+ dapm = snd_soc_codec_get_dapm(codec);
+
+ /* Read efuse register to check if DSD is supported */
+ val = snd_soc_read(codec, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14);
+ if (val & 0x80) {
+ dev_info(codec->dev, "%s: DSD unsupported for this codec version\n",
+ __func__);
+ return NULL;
+ }
+
+ dsd_conf = devm_kzalloc(codec->dev, sizeof(struct tavil_dsd_config),
+ GFP_KERNEL);
+ if (!dsd_conf)
+ return NULL;
+
+ dsd_conf->codec = codec;
+
+ /* DSD registers init */
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00);
+ /* DSD0: Mute EN */
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04);
+ /* DSD1: Mute EN */
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x10,
+ 0x10);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x10,
+ 0x10);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x0E,
+ 0x0A);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x0E,
+ 0x0A);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x07,
+ 0x04);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x07,
+ 0x04);
+
+ snd_soc_dapm_new_controls(dapm, tavil_dsd_widgets,
+ ARRAY_SIZE(tavil_dsd_widgets));
+
+ snd_soc_dapm_add_routes(dapm, tavil_dsd_audio_map,
+ ARRAY_SIZE(tavil_dsd_audio_map));
+
+ /* Enable DSD Interrupts */
+ snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x00);
+
+ return dsd_conf;
+}
+EXPORT_SYMBOL(tavil_dsd_init);
+
+/**
+ * tavil_dsd_deinit - DSD de-intialization
+ *
+ * @dsd_conf: pointer to tavil_dsd_config
+ */
+void tavil_dsd_deinit(struct tavil_dsd_config *dsd_conf)
+{
+ struct snd_soc_codec *codec;
+
+ if (!dsd_conf)
+ return;
+
+ codec = dsd_conf->codec;
+
+ /* Disable DSD Interrupts */
+ snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x08);
+
+ devm_kfree(codec->dev, dsd_conf);
+}
+EXPORT_SYMBOL(tavil_dsd_deinit);
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.h b/sound/soc/codecs/wcd934x/wcd934x-dsd.h
new file mode 100644
index 000000000000..884e41d797d9
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.h
@@ -0,0 +1,89 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD934X_DSD_H__
+#define __WCD934X_DSD_H__
+
+#include <sound/soc.h>
+#include "wcd934x.h"
+
+enum {
+ DSD0,
+ DSD1,
+ DSD_MAX,
+};
+
+enum {
+ DSD_INP_SEL_ZERO = 0,
+ DSD_INP_SEL_RX0,
+ DSD_INP_SEL_RX1,
+ DSD_INP_SEL_RX2,
+ DSD_INP_SEL_RX3,
+ DSD_INP_SEL_RX4,
+ DSD_INP_SEL_RX5,
+ DSD_INP_SEL_RX6,
+ DSD_INP_SEL_RX7,
+};
+
+struct tavil_dsd_config {
+ struct snd_soc_codec *codec;
+ unsigned int dsd_interp_mixer[INTERP_MAX];
+ u32 base_sample_rate[DSD_MAX];
+};
+
+#ifdef CONFIG_SND_SOC_WCD934X_DSD
+int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf,
+ int interp_num, int sw_value);
+int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf,
+ int interp_num);
+int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf,
+ int interp_num);
+void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf);
+void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port,
+ u32 sample_rate, u8 sample_rate_val);
+struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec);
+void tavil_dsd_deinit(struct tavil_dsd_config *dsd_config);
+#else
+int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf,
+ int interp_num, int sw_value)
+{
+ return 0;
+}
+
+int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf,
+ int interp_num)
+{
+ return 0;
+}
+
+int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf,
+ int interp_num)
+{
+ return 0;
+}
+
+void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf)
+{ }
+
+void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port,
+ u32 sample_rate, u8 sample_rate_val)
+{ }
+
+struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec)
+{
+ return NULL;
+}
+
+void tavil_dsd_deinit(struct tavil_dsd_config *dsd_config)
+{ }
+#endif
+#endif
diff --git a/sound/soc/codecs/wcd934x/wcd934x-routing.h b/sound/soc/codecs/wcd934x/wcd934x-routing.h
index bdb9ab22293d..8b20805de6f9 100644
--- a/sound/soc/codecs/wcd934x/wcd934x-routing.h
+++ b/sound/soc/codecs/wcd934x/wcd934x-routing.h
@@ -808,7 +808,8 @@ const struct snd_soc_dapm_route tavil_audio_map[] = {
{"RX INT1 SEC MIX", NULL, "RX INT1_1 INTERP"},
{"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"},
{"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"},
- {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 MIX2"},
+ {"RX INT1 MIX3", NULL, "RX INT1 MIX2"},
+ {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 MIX3"},
{"RX INT1 DAC", NULL, "RX INT1 DEM MUX"},
{"RX INT1 DAC", NULL, "RX_BIAS"},
{"HPHL PA", NULL, "RX INT1 DAC"},
@@ -818,7 +819,8 @@ const struct snd_soc_dapm_route tavil_audio_map[] = {
{"RX INT2 SEC MIX", NULL, "RX INT2_1 INTERP"},
{"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"},
{"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"},
- {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 MIX2"},
+ {"RX INT2 MIX3", NULL, "RX INT2 MIX2"},
+ {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 MIX3"},
{"RX INT2 DAC", NULL, "RX INT2 DEM MUX"},
{"RX INT2 DAC", NULL, "RX_BIAS"},
{"HPHR PA", NULL, "RX INT2 DAC"},
diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c
index 9fb40274d9f9..50a023013fa8 100644
--- a/sound/soc/codecs/wcd934x/wcd934x.c
+++ b/sound/soc/codecs/wcd934x/wcd934x.c
@@ -47,8 +47,7 @@
#include "../wcd9xxx-common-v2.h"
#include "../wcd9xxx-resmgr-v2.h"
#include "../wcdcal-hwdep.h"
-
-#define WCD934X_RX_PORT_START_NUMBER 16
+#include "wcd934x-dsd.h"
#define WCD934X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
@@ -178,18 +177,6 @@ enum {
INTn_2_INP_SEL_PROXIMITY,
};
-enum {
- INTERP_EAR = 0,
- INTERP_HPHL,
- INTERP_HPHR,
- INTERP_LO1,
- INTERP_LO2,
- INTERP_LO3_NA, /* LO3 not avalible in Tavil*/
- INTERP_LO4_NA,
- INTERP_SPKR1,
- INTERP_SPKR2,
-};
-
static const struct intr_data wcd934x_intr_table[] = {
{WCD9XXX_IRQ_SLIMBUS, false},
{WCD934X_IRQ_MBHC_SW_DET, true},
@@ -197,7 +184,7 @@ static const struct intr_data wcd934x_intr_table[] = {
{WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET, true},
{WCD934X_IRQ_MBHC_ELECT_INS_REM_DET, true},
{WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, true},
- {WCD934X_IRQ_FLL_LOCK_LOSS, false},
+ {WCD934X_IRQ_MISC, false},
{WCD934X_IRQ_HPH_PA_CNPL_COMPLETE, false},
{WCD934X_IRQ_HPH_PA_CNPR_COMPLETE, false},
{WCD934X_IRQ_EAR_PA_CNP_COMPLETE, false},
@@ -516,6 +503,7 @@ struct tavil_priv {
int asrc_users[ASRC_MAX];
/* Main path clock users count */
int main_clk_users[WCD934X_NUM_INTERPOLATORS];
+ struct tavil_dsd_config *dsd_config;
};
static const struct tavil_reg_mask_val tavil_spkr_default[] = {
@@ -1402,6 +1390,7 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ struct tavil_dsd_config *dsd_conf = tavil->dsd_config;
dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
@@ -1430,8 +1419,25 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
snd_soc_update_bits(codec,
WCD934X_CDC_RX2_RX_PATH_MIX_CTL,
0x10, 0x00);
+ if (dsd_conf &&
+ (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) {
+ /* Set regulator mode to AB if DSD is enabled */
+ snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2,
+ 0x04, 0x00);
+ }
break;
- default:
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Enable DSD Mute before PA disable */
+ if (dsd_conf &&
+ (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01))
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2,
+ 0x04, 0x04);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 5ms sleep is required after PA disable */
+ usleep_range(5000, 5100);
break;
};
@@ -1444,6 +1450,7 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ struct tavil_dsd_config *dsd_conf = tavil->dsd_config;
dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
@@ -1469,8 +1476,25 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
snd_soc_update_bits(codec,
WCD934X_CDC_RX1_RX_PATH_MIX_CTL,
0x10, 0x00);
+ if (dsd_conf &&
+ (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) {
+ /* Set regulator mode to AB if DSD is enabled */
+ snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2,
+ 0x04, 0x00);
+ }
break;
- default:
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Enable DSD Mute before PA disable */
+ if (dsd_conf &&
+ (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01))
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2,
+ 0x04, 0x04);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 5ms sleep is required after PA disable */
+ usleep_range(5000, 5100);
break;
};
@@ -1562,6 +1586,7 @@ static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
int hph_mode = tavil->hph_mode;
u8 dem_inp;
+ struct tavil_dsd_config *dsd_conf = tavil->dsd_config;
dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__,
w->name, event, hph_mode);
@@ -1580,6 +1605,11 @@ static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
/* Disable AutoChop timer during power up */
snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1,
0x02, 0x00);
+
+ if (dsd_conf &&
+ (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01))
+ hph_mode = CLS_H_HIFI;
+
wcd_clsh_fsm(codec, &tavil->clsh_d,
WCD_CLSH_EVENT_PRE_DAC,
WCD_CLSH_STATE_HPHR,
@@ -1611,6 +1641,7 @@ static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
int hph_mode = tavil->hph_mode;
u8 dem_inp;
int ret = 0;
+ struct tavil_dsd_config *dsd_conf = tavil->dsd_config;
dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__,
w->name, event, hph_mode);
@@ -1626,6 +1657,10 @@ static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
__func__, hph_mode);
return -EINVAL;
}
+ if (dsd_conf &&
+ (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01))
+ hph_mode = CLS_H_HIFI;
+
wcd_clsh_fsm(codec, &tavil->clsh_d,
WCD_CLSH_EVENT_PRE_DAC,
WCD_CLSH_STATE_HPHL,
@@ -2389,6 +2424,29 @@ static int tavil_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
return 0;
}
+/**
+ * tavil_get_dsd_config - Get pointer to dsd config structure
+ *
+ * @codec: pointer to snd_soc_codec structure
+ *
+ * Returns pointer to tavil_dsd_config structure
+ */
+struct tavil_dsd_config *tavil_get_dsd_config(struct snd_soc_codec *codec)
+{
+ struct tavil_priv *tavil;
+
+ if (!codec)
+ return NULL;
+
+ tavil = snd_soc_codec_get_drvdata(codec);
+
+ if (!tavil)
+ return NULL;
+
+ return tavil->dsd_config;
+}
+EXPORT_SYMBOL(tavil_get_dsd_config);
+
static int tavil_codec_enable_main_path(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
@@ -4824,6 +4882,61 @@ static const struct snd_kcontrol_new rx_int4_asrc_switch[] = {
SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0),
};
+static int tavil_dsd_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config;
+ int val;
+
+ val = tavil_dsd_get_current_mixer_value(dsd_conf, mc->shift);
+
+ ucontrol->value.integer.value[0] = ((val < 0) ? 0 : val);
+
+ return 0;
+}
+
+static int tavil_dsd_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+ unsigned int wval = ucontrol->value.integer.value[0];
+ struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config;
+
+ if (!dsd_conf)
+ return 0;
+
+ mutex_lock(&tavil_p->codec_mutex);
+
+ tavil_dsd_set_out_select(dsd_conf, mc->shift);
+ tavil_dsd_set_mixer_value(dsd_conf, mc->shift, wval);
+
+ mutex_unlock(&tavil_p->codec_mutex);
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, wval, NULL);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new hphl_mixer[] = {
+ SOC_SINGLE_EXT("DSD HPHL Switch", SND_SOC_NOPM, INTERP_HPHL, 1, 0,
+ tavil_dsd_mixer_get, tavil_dsd_mixer_put),
+};
+
+static const struct snd_kcontrol_new hphr_mixer[] = {
+ SOC_SINGLE_EXT("DSD HPHR Switch", SND_SOC_NOPM, INTERP_HPHR, 1, 0,
+ tavil_dsd_mixer_get, tavil_dsd_mixer_put),
+};
+
static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
AIF1_PB, 0, tavil_codec_enable_slimrx,
@@ -4950,7 +5063,11 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1 MIX3", SND_SOC_NOPM, 0, 0, hphl_mixer,
+ ARRAY_SIZE(hphl_mixer)),
SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2 MIX3", SND_SOC_NOPM, 0, 0, hphr_mixer,
+ ARRAY_SIZE(hphr_mixer)),
SND_SOC_DAPM_MIXER("RX INT3 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT4 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -5665,6 +5782,7 @@ static int tavil_set_prim_interpolator_rate(struct snd_soc_dai *dai,
struct snd_soc_codec *codec = dai->codec;
struct wcd9xxx_ch *ch;
struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ struct tavil_dsd_config *dsd_conf = tavil->dsd_config;
list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, list) {
int_1_mix1_inp = INTn_1_INP_SEL_RX0 + ch->port -
@@ -5727,6 +5845,9 @@ static int tavil_set_prim_interpolator_rate(struct snd_soc_dai *dai,
}
int_mux_cfg0 += 2;
}
+ if (dsd_conf)
+ tavil_dsd_set_interp_rate(dsd_conf, ch->port,
+ sample_rate, rate_reg_val);
}
return 0;
@@ -6239,6 +6360,32 @@ static void tavil_slim_interface_init_reg(struct snd_soc_codec *codec)
0xFF);
}
+static irqreturn_t tavil_misc_irq(int irq, void *data)
+{
+ struct tavil_priv *tavil = data;
+ int misc_val;
+
+ /* Find source of interrupt */
+ regmap_read(tavil->wcd9xxx->regmap, WCD934X_INTR_CODEC_MISC_STATUS,
+ &misc_val);
+
+ if (misc_val & 0x08) {
+ dev_info(tavil->dev, "%s: irq: %d, DSD DC detected!\n",
+ __func__, irq);
+ /* DSD DC interrupt, reset DSD path */
+ tavil_dsd_reset(tavil->dsd_config);
+ } else {
+ dev_err(tavil->dev, "%s: Codec misc irq: %d, val: 0x%x\n",
+ __func__, irq, misc_val);
+ }
+
+ /* Clear interrupt status */
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_INTR_CODEC_MISC_CLEAR, misc_val, 0x00);
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t tavil_slimbus_irq(int irq, void *data)
{
struct tavil_priv *tavil = data;
@@ -6352,6 +6499,13 @@ static int tavil_setup_irqs(struct tavil_priv *tavil)
else
tavil_slim_interface_init_reg(codec);
+ /* Register for misc interrupts as well */
+ ret = wcd9xxx_request_irq(core_res, WCD934X_IRQ_MISC,
+ tavil_misc_irq, "CDC MISC Irq", tavil);
+ if (ret)
+ dev_err(codec->dev, "%s: Failed to request cdc misc irq\n",
+ __func__);
+
return ret;
}
@@ -6729,6 +6883,11 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec)
tavil_mclk2_reg_defaults(tavil);
+ /* DSD initialization */
+ tavil->dsd_config = tavil_dsd_init(codec);
+ if (IS_ERR_OR_NULL(tavil->dsd_config))
+ dev_dbg(tavil->dev, "%s: DSD init failed\n", __func__);
+
snd_soc_dapm_sync(dapm);
tavil_wdsp_initialize(codec);
@@ -7381,6 +7540,10 @@ static int tavil_remove(struct platform_device *pdev)
snd_soc_unregister_codec(&pdev->dev);
clk_put(tavil->wcd_ext_clk);
wcd_resmgr_remove(tavil->resmgr);
+ if (tavil->dsd_config) {
+ tavil_dsd_deinit(tavil->dsd_config);
+ tavil->dsd_config = NULL;
+ }
devm_kfree(&pdev->dev, tavil);
return 0;
}
diff --git a/sound/soc/codecs/wcd934x/wcd934x.h b/sound/soc/codecs/wcd934x/wcd934x.h
index 4690d344a1d6..9cffa168c298 100644
--- a/sound/soc/codecs/wcd934x/wcd934x.h
+++ b/sound/soc/codecs/wcd934x/wcd934x.h
@@ -22,6 +22,7 @@
#define WCD934X_REGISTER_START_OFFSET 0x800
#define WCD934X_SB_PGD_PORT_RX_BASE 0x40
#define WCD934X_SB_PGD_PORT_TX_BASE 0x50
+#define WCD934X_RX_PORT_START_NUMBER 16
#define WCD934X_DMIC_CLK_DIV_2 0x0
#define WCD934X_DMIC_CLK_DIV_3 0x1
@@ -72,8 +73,21 @@ enum {
};
enum {
+ INTERP_EAR = 0,
+ INTERP_HPHL,
+ INTERP_HPHR,
+ INTERP_LO1,
+ INTERP_LO2,
+ INTERP_LO3_NA, /* LO3 not avalible in Tavil*/
+ INTERP_LO4_NA,
+ INTERP_SPKR1,
+ INTERP_SPKR2,
+ INTERP_MAX,
+};
+
+enum {
/* INTR_REG 0 */
- WCD934X_IRQ_FLL_LOCK_LOSS = 1,
+ WCD934X_IRQ_MISC = 1,
WCD934X_IRQ_HPH_PA_OCPL_FAULT,
WCD934X_IRQ_HPH_PA_OCPR_FAULT,
WCD934X_IRQ_EAR_PA_OCP_FAULT,
@@ -165,4 +179,5 @@ extern int tavil_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec,
extern struct wcd934x_mbhc *tavil_soc_get_mbhc(struct snd_soc_codec *codec);
extern int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec,
int event, int intp_idx);
+extern struct tavil_dsd_config *tavil_get_dsd_config(struct snd_soc_codec *);
#endif