// SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. */ #include #include #include #include #include #include "wcd934x-dsd.h" #define DSD_VOLUME_MAX_0dB 0 #define DSD_VOLUME_MIN_M110dB -110 #define DSD_VOLUME_RANGE_CHECK(x) ((x >= DSD_VOLUME_MIN_M110dB) &&\ (x <= DSD_VOLUME_MAX_0dB)) #define DSD_VOLUME_STEPS 3 #define DSD_VOLUME_UPDATE_DELAY_MS 30 #define DSD_VOLUME_USLEEP_MARGIN_US 100 #define DSD_VOLUME_STEP_DELAY_US ((1000 * DSD_VOLUME_UPDATE_DELAY_MS) / \ (2 * DSD_VOLUME_STEPS)) #define TAVIL_VERSION_1_0 0 #define TAVIL_VERSION_1_1 1 static const DECLARE_TLV_DB_MINMAX(tavil_dsd_db_scale, DSD_VOLUME_MIN_M110dB, DSD_VOLUME_MAX_0dB); 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"}, {"DSD_FILTER_0", NULL, "RX INT3 NATIVE SUPPLY"}, {"RX INT3 MIX3", "DSD LO1 Switch", "DSD_FILTER_0"}, {"DSD_FILTER_1", NULL, "RX INT4 NATIVE SUPPLY"}, {"RX INT4 MIX3", "DSD LO2 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_component *component; if (!dsd_conf || !dsd_conf->component) return -EINVAL; component = dsd_conf->component; if (!is_valid_dsd_interpolator(interp_num)) { dev_err(component->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_component_update_bits(component, 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->component) return; snd_soc_component_update_bits(dsd_conf->component, WCD934X_CDC_DSD0_PATH_CTL, 0x02, 0x02); snd_soc_component_update_bits(dsd_conf->component, WCD934X_CDC_DSD0_PATH_CTL, 0x01, 0x00); snd_soc_component_update_bits(dsd_conf->component, WCD934X_CDC_DSD1_PATH_CTL, 0x02, 0x02); snd_soc_component_update_bits(dsd_conf->component, 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_component *component; if (!dsd_conf || !dsd_conf->component) return; component = dsd_conf->component; dsd_inp_sel = DSD_INP_SEL_RX0 + rx_port - WCD934X_RX_PORT_START_NUMBER; val0 = snd_soc_component_read32(component, WCD934X_CDC_DSD0_CFG0); val1 = snd_soc_component_read32(component, 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_component_read32(component, int_fs_reg) & 0x0f) < 0x09) { dev_dbg(component->dev, "%s: Set Interp %d to sample_rate val 0x%x\n", __func__, interp_num, sample_rate_val); snd_soc_component_update_bits(component, int_fs_reg, 0x0F, sample_rate_val); } } } EXPORT_SYMBOL(tavil_dsd_set_interp_rate); static int tavil_set_dsd_mode(struct snd_soc_component *component, 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(component); 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(component->dev, "%s: Invalid DSD rate: %d\n", __func__, sample_rate); return -EINVAL; } snd_soc_component_update_bits(component, dsd_out_sel_reg, 0x01, dsd_mode); return 0; } static void tavil_dsd_data_pull(struct snd_soc_component *component, 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_component_update_bits(component, WCD934X_CDC_RX7_RX_PATH_MIX_CTL, 0x20, clk_en); dsd_inp_sel = (snd_soc_component_read32( component, WCD934X_CDC_DSD0_CFG0) & 0x3C) >> 2; dsd_inp_sel = (enable) ? dsd_inp_sel : 0; if (dsd_inp_sel < 9) { snd_soc_component_update_bits(component, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 0x0F, dsd_inp_sel); snd_soc_component_update_bits(component, WCD934X_CDC_RX7_RX_PATH_MIX_CTL, 0x0F, pcm_rate_val); snd_soc_component_update_bits(component, WCD934X_CDC_RX7_RX_PATH_MIX_CTL, 0x10, mute_en); } } if (dsd_num & 0x02) { snd_soc_component_update_bits(component, WCD934X_CDC_RX8_RX_PATH_MIX_CTL, 0x20, clk_en); dsd_inp_sel = (snd_soc_component_read32( component, WCD934X_CDC_DSD1_CFG0) & 0x3C) >> 2; dsd_inp_sel = (enable) ? dsd_inp_sel : 0; if (dsd_inp_sel < 9) { snd_soc_component_update_bits(component, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 0x0F, dsd_inp_sel); snd_soc_component_update_bits(component, WCD934X_CDC_RX8_RX_PATH_MIX_CTL, 0x0F, pcm_rate_val); snd_soc_component_update_bits(component, WCD934X_CDC_RX8_RX_PATH_MIX_CTL, 0x10, mute_en); } } } static void tavil_dsd_update_volume(struct tavil_dsd_config *dsd_conf) { snd_soc_component_update_bits(dsd_conf->component, WCD934X_CDC_TOP_TOP_CFG0, 0x01, 0x01); snd_soc_component_update_bits(dsd_conf->component, WCD934X_CDC_TOP_TOP_CFG0, 0x01, 0x00); } static int tavil_enable_dsd(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(component); int rc, clk_users; int interp_idx; u8 pcm_rate_val; if (!dsd_conf) { dev_err(component->dev, "%s: null dsd_config pointer\n", __func__); return -EINVAL; } dev_dbg(component->dev, "%s: DSD%d, event: %d\n", __func__, w->shift, event); if (w->shift == DSD0) { /* Read out select */ if (snd_soc_component_read32( component, 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_component_read32( component, WCD934X_CDC_DSD1_CFG0) & 0x02) interp_idx = INTERP_LO2; else interp_idx = INTERP_HPHR; } else { dev_err(component->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(component, event, interp_idx); rc = tavil_set_dsd_mode(component, w->shift, &pcm_rate_val); if (rc) return rc; tavil_dsd_data_pull(component, (1 << w->shift), pcm_rate_val, true); snd_soc_component_update_bits(component, WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL, 0x01, 0x01); if (w->shift == DSD0) { snd_soc_component_update_bits(component, WCD934X_CDC_DSD0_PATH_CTL, 0x02, 0x02); snd_soc_component_update_bits(component, WCD934X_CDC_DSD0_PATH_CTL, 0x02, 0x00); snd_soc_component_update_bits(component, WCD934X_CDC_DSD0_PATH_CTL, 0x01, 0x01); /* Apply Gain */ snd_soc_component_write(component, WCD934X_CDC_DSD0_CFG1, dsd_conf->volume[DSD0]); if (dsd_conf->version == TAVIL_VERSION_1_1) tavil_dsd_update_volume(dsd_conf); } else if (w->shift == DSD1) { snd_soc_component_update_bits(component, WCD934X_CDC_DSD1_PATH_CTL, 0x02, 0x02); snd_soc_component_update_bits(component, WCD934X_CDC_DSD1_PATH_CTL, 0x02, 0x00); snd_soc_component_update_bits(component, WCD934X_CDC_DSD1_PATH_CTL, 0x01, 0x01); /* Apply Gain */ snd_soc_component_write(component, WCD934X_CDC_DSD1_CFG1, dsd_conf->volume[DSD1]); if (dsd_conf->version == TAVIL_VERSION_1_1) tavil_dsd_update_volume(dsd_conf); } /* 10msec sleep required after DSD clock is set */ usleep_range(10000, 10100); if (clk_users > 1) { snd_soc_component_update_bits(component, WCD934X_ANA_RX_SUPPLIES, 0x02, 0x02); if (w->shift == DSD0) snd_soc_component_update_bits(component, WCD934X_CDC_DSD0_CFG2, 0x04, 0x00); if (w->shift == DSD1) snd_soc_component_update_bits(component, WCD934X_CDC_DSD1_CFG2, 0x04, 0x00); } break; case SND_SOC_DAPM_POST_PMD: if (w->shift == DSD0) { snd_soc_component_update_bits(component, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04); snd_soc_component_update_bits(component, WCD934X_CDC_DSD0_PATH_CTL, 0x01, 0x00); } else if (w->shift == DSD1) { snd_soc_component_update_bits(component, WCD934X_CDC_DSD1_CFG2, 0x04, 0x04); snd_soc_component_update_bits(component, WCD934X_CDC_DSD1_PATH_CTL, 0x01, 0x00); } tavil_codec_enable_interp_clk(component, event, interp_idx); if (!(snd_soc_component_read32( component, WCD934X_CDC_DSD0_PATH_CTL) & 0x01) && !(snd_soc_component_read32( component, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) { snd_soc_component_update_bits(component, WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL, 0x01, 0x00); tavil_dsd_data_pull(component, 0x03, 0x04, false); tavil_dsd_reset(dsd_conf); } break; } return 0; } static int tavil_dsd_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = DSD_VOLUME_MIN_M110dB; uinfo->value.integer.max = DSD_VOLUME_MAX_0dB; return 0; } static int tavil_dsd_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(component); int nv[DSD_MAX], cv[DSD_MAX]; int step_size, nv1; int i, dsd_idx; if (!dsd_conf) return 0; mutex_lock(&dsd_conf->vol_mutex); for (dsd_idx = DSD0; dsd_idx < DSD_MAX; dsd_idx++) { cv[dsd_idx] = dsd_conf->volume[dsd_idx]; nv[dsd_idx] = ucontrol->value.integer.value[dsd_idx]; } if ((!DSD_VOLUME_RANGE_CHECK(nv[DSD0])) || (!DSD_VOLUME_RANGE_CHECK(nv[DSD1]))) goto done; for (dsd_idx = DSD0; dsd_idx < DSD_MAX; dsd_idx++) { if (cv[dsd_idx] == nv[dsd_idx]) continue; dev_dbg(component->dev, "%s: DSD%d cur.vol: %d, new vol: %d\n", __func__, dsd_idx, cv[dsd_idx], nv[dsd_idx]); step_size = (nv[dsd_idx] - cv[dsd_idx]) / DSD_VOLUME_STEPS; nv1 = cv[dsd_idx]; for (i = 0; i < DSD_VOLUME_STEPS; i++) { nv1 += step_size; snd_soc_component_write(component, WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx, nv1); if (dsd_conf->version == TAVIL_VERSION_1_1) tavil_dsd_update_volume(dsd_conf); /* sleep required after each volume step */ usleep_range(DSD_VOLUME_STEP_DELAY_US, (DSD_VOLUME_STEP_DELAY_US + DSD_VOLUME_USLEEP_MARGIN_US)); } if (nv1 != nv[dsd_idx]) { snd_soc_component_write(component, WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx, nv[dsd_idx]); if (dsd_conf->version == TAVIL_VERSION_1_1) tavil_dsd_update_volume(dsd_conf); } dsd_conf->volume[dsd_idx] = nv[dsd_idx]; } done: mutex_unlock(&dsd_conf->vol_mutex); return 0; } static int tavil_dsd_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(component); if (dsd_conf) { ucontrol->value.integer.value[0] = dsd_conf->volume[DSD0]; ucontrol->value.integer.value[1] = dsd_conf->volume[DSD1]; } return 0; } static const struct snd_kcontrol_new tavil_dsd_vol_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "DSD Volume", .info = tavil_dsd_vol_info, .get = tavil_dsd_vol_get, .put = tavil_dsd_vol_put, .tlv = { .p = tavil_dsd_db_scale }, }, }; 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_post_ssr_init - DSD intialization after subsystem restart * * @component: pointer to snd_soc_component * * Returns 0 on success or error on failure */ int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_conf) { struct snd_soc_component *component; if (!dsd_conf || !dsd_conf->component) return -EINVAL; component = dsd_conf->component; /* Disable DSD Interrupts */ snd_soc_component_update_bits(component, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x08); /* DSD registers init */ if (dsd_conf->version == TAVIL_VERSION_1_0) { snd_soc_component_update_bits(component, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00); snd_soc_component_update_bits(component, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00); } /* DSD0: Mute EN */ snd_soc_component_update_bits(component, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04); /* DSD1: Mute EN */ snd_soc_component_update_bits(component, WCD934X_CDC_DSD1_CFG2, 0x04, 0x04); snd_soc_component_update_bits(component, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x10, 0x10); snd_soc_component_update_bits(component, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x10, 0x10); snd_soc_component_update_bits(component, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x0E, 0x0A); snd_soc_component_update_bits(component, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x0E, 0x0A); snd_soc_component_update_bits(component, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x07, 0x04); snd_soc_component_update_bits(component, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x07, 0x04); /* Enable DSD Interrupts */ snd_soc_component_update_bits(component, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x00); return 0; } EXPORT_SYMBOL(tavil_dsd_post_ssr_init); /** * tavil_dsd_init - DSD intialization * * @component: pointer to snd_soc_component * * Returns pointer to tavil_dsd_config for success or NULL for failure */ struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_component *component) { struct snd_soc_dapm_context *dapm; struct tavil_dsd_config *dsd_conf; u8 val; if (!component) return NULL; dapm = snd_soc_component_get_dapm(component); /* Read efuse register to check if DSD is supported */ val = snd_soc_component_read32(component, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14); if (val & 0x80) { dev_info(component->dev, "%s: DSD unsupported for this codec version\n", __func__); return NULL; } dsd_conf = devm_kzalloc(component->dev, sizeof(struct tavil_dsd_config), GFP_KERNEL); if (!dsd_conf) return NULL; dsd_conf->component = component; /* Read version */ dsd_conf->version = snd_soc_component_read32(component, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0); /* DSD registers init */ if (dsd_conf->version == TAVIL_VERSION_1_0) { snd_soc_component_update_bits(component, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00); snd_soc_component_update_bits(component, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00); } /* DSD0: Mute EN */ snd_soc_component_update_bits(component, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04); /* DSD1: Mute EN */ snd_soc_component_update_bits(component, WCD934X_CDC_DSD1_CFG2, 0x04, 0x04); snd_soc_component_update_bits(component, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x10, 0x10); snd_soc_component_update_bits(component, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x10, 0x10); snd_soc_component_update_bits(component, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x0E, 0x0A); snd_soc_component_update_bits(component, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x0E, 0x0A); snd_soc_component_update_bits(component, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x07, 0x04); snd_soc_component_update_bits(component, 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)); mutex_init(&dsd_conf->vol_mutex); dsd_conf->volume[DSD0] = DSD_VOLUME_MAX_0dB; dsd_conf->volume[DSD1] = DSD_VOLUME_MAX_0dB; snd_soc_add_component_controls(component, tavil_dsd_vol_controls, ARRAY_SIZE(tavil_dsd_vol_controls)); /* Enable DSD Interrupts */ snd_soc_component_update_bits(component, 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_component *component; if (!dsd_conf) return; component = dsd_conf->component; mutex_destroy(&dsd_conf->vol_mutex); /* Disable DSD Interrupts */ snd_soc_component_update_bits(component, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x08); devm_kfree(component->dev, dsd_conf); } EXPORT_SYMBOL(tavil_dsd_deinit);