From cacf1949cdd6d29dfd45c0224ba98b4b0779d79f Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Wed, 20 Jul 2016 16:34:52 +0800 Subject: [PATCH 1/6] ASoC: wcd934x: add MAD support for wcd934x codec MAD (Microphone Activity Detection) hardware block in wcd934x audio codec samples the microphone at configured intervals to sense presence of valid audio signal. This is used for voice activation use cases. Change adds widgets and routes to enable the MAD block. CRs-Fixed: 1049012 Change-Id: I1ce4f3f215850fada6c25192a4351f3a9c76a370 Signed-off-by: Meng Wang Signed-off-by: Bhalchandra Gajare --- sound/soc/codecs/wcd934x/wcd934x-routing.h | 13 + sound/soc/codecs/wcd934x/wcd934x.c | 465 ++++++++++++++++++++- 2 files changed, 477 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wcd934x/wcd934x-routing.h b/sound/soc/codecs/wcd934x/wcd934x-routing.h index 72976a9ec0fe..00cdfcd038df 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-routing.h +++ b/sound/soc/codecs/wcd934x/wcd934x-routing.h @@ -114,6 +114,19 @@ const struct snd_soc_dapm_route tavil_slim_audio_map[] = { const struct snd_soc_dapm_route tavil_audio_map[] = { + /* MAD */ + {"MAD_SEL MUX", "SPE", "MAD_CPE_INPUT"}, + {"MAD_SEL MUX", "MSM", "MADINPUT"}, + + {"MAD_INP MUX", "MAD", "MAD_SEL MUX"}, + {"MAD_INP MUX", "DEC1", "ADC MUX1"}, + + {"MAD_CPE1", "Switch", "MAD_INP MUX"}, + {"MAD_CPE2", "Switch", "MAD_INP MUX"}, + + {"MAD_CPE_OUT1", NULL, "MAD_CPE1"}, + {"MAD_CPE_OUT2", NULL, "MAD_CPE2"}, + /* VI Feedback */ {"AIF4_VI Mixer", "SPKR_VI_1", "VIINPUT"}, {"AIF4_VI Mixer", "SPKR_VI_2", "VIINPUT"}, diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index 631727fbdd8c..26059caba07a 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -127,6 +127,8 @@ static const struct snd_kcontrol_new name##_mux = \ #define CPE_ERR_WDOG_BITE BIT(0) #define CPE_FATAL_IRQS CPE_ERR_WDOG_BITE +#define WCD934X_MAD_AUDIO_FIRMWARE_PATH "wcd934x/wcd934x_mad_audio.bin" + enum { VI_SENSE_1, VI_SENSE_2, @@ -450,6 +452,9 @@ struct tavil_priv { /* Tavil Interpolator Mode Select for EAR, HPH_L and HPH_R */ u32 hph_mode; + /* Mad switch reference count */ + int mad_switch_cnt; + u16 prim_int_users[WCD934X_NUM_INTERPOLATORS]; /* to track the status */ unsigned long status_mask; @@ -1953,6 +1958,212 @@ static int tavil_config_compander(struct snd_soc_codec *codec, int interp_n, return 0; } +static int tavil_codec_config_mad(struct snd_soc_codec *codec) +{ + int ret = 0; + int idx; + const struct firmware *fw; + struct firmware_cal *hwdep_cal = NULL; + struct wcd_mad_audio_cal *mad_cal = NULL; + const void *data; + const char *filename = WCD934X_MAD_AUDIO_FIRMWARE_PATH; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + size_t cal_size; + + hwdep_cal = wcdcal_get_fw_cal(tavil->fw_data, WCD9XXX_MAD_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration\n", + __func__); + } else { + ret = request_firmware(&fw, filename, codec->dev); + if (ret || !fw) { + dev_err(codec->dev, + "%s: MAD firmware acquire failed, err = %d\n", + __func__, ret); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, "%s: using request_firmware calibration\n", + __func__); + } + + if (cal_size < sizeof(*mad_cal)) { + dev_err(codec->dev, + "%s: Incorrect size %zd for MAD Cal, expected %zd\n", + __func__, cal_size, sizeof(*mad_cal)); + ret = -ENOMEM; + goto done; + } + + mad_cal = (struct wcd_mad_audio_cal *) (data); + if (!mad_cal) { + dev_err(codec->dev, + "%s: Invalid calibration data\n", + __func__); + ret = -EINVAL; + goto done; + } + + snd_soc_write(codec, WCD934X_SOC_MAD_MAIN_CTL_2, + mad_cal->microphone_info.cycle_time); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_MAIN_CTL_1, 0xFF << 3, + ((uint16_t)mad_cal->microphone_info.settle_time) + << 3); + + /* Audio */ + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_8, + mad_cal->audio_info.rms_omit_samples); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_1, + 0x07 << 4, mad_cal->audio_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, 0x03 << 2, + mad_cal->audio_info.detection_mechanism << 2); + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_7, + mad_cal->audio_info.rms_diff_threshold & 0x3F); + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_5, + mad_cal->audio_info.rms_threshold_lsb); + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_6, + mad_cal->audio_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->audio_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL, + mad_cal->audio_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Audio IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->audio_info.iir_coefficients[idx]); + } + + /* Beacon */ + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_8, + mad_cal->beacon_info.rms_omit_samples); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_CTL_1, + 0x07 << 4, mad_cal->beacon_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_CTL_2, 0x03 << 2, + mad_cal->beacon_info.detection_mechanism << 2); + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_7, + mad_cal->beacon_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_5, + mad_cal->beacon_info.rms_threshold_lsb); + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_6, + mad_cal->beacon_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->beacon_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL, + mad_cal->beacon_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Beacon IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->beacon_info.iir_coefficients[idx]); + } + + /* Ultrasound */ + snd_soc_update_bits(codec, WCD934X_SOC_MAD_ULTR_CTL_1, + 0x07 << 4, + mad_cal->ultrasound_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_ULTR_CTL_2, 0x03 << 2, + mad_cal->ultrasound_info.detection_mechanism << 2); + snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_7, + mad_cal->ultrasound_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_5, + mad_cal->ultrasound_info.rms_threshold_lsb); + snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_6, + mad_cal->ultrasound_info.rms_threshold_msb); + +done: + if (!hwdep_cal) + release_firmware(fw); + + return ret; +} + +static int tavil_codec_enable_mad(struct snd_soc_codec *codec, bool enable) +{ + int rc = 0; + + /* Return if CPE INPUT is DEC1 */ + if (snd_soc_read(codec, WCD934X_CPE_SS_SVA_CFG) & 0x04) { + dev_dbg(codec->dev, "%s: MAD is bypassed, skip mad %s\n", + __func__, enable ? "enable" : "disable"); + return rc; + } + + dev_dbg(codec->dev, "%s: enable = %s\n", __func__, + enable ? "enable" : "disable"); + + if (enable) { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, + 0x03, 0x03); + rc = tavil_codec_config_mad(codec); + if (IS_ERR_VALUE(rc)) { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, + 0x03, 0x00); + goto done; + } + + /* Turn on MAD clk */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, + 0x01, 0x01); + + /* Undo reset for MAD */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, + 0x02, 0x00); + } else { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, + 0x03, 0x00); + /* Reset the MAD block */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, + 0x02, 0x02); + /* Turn off MAD clk */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, + 0x01, 0x00); + } +done: + return rc; +} + +static int tavil_codec_cpe_mad_ctl(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 tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int rc = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil->mad_switch_cnt++; + if (tavil->mad_switch_cnt != 1) + goto done; + + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x20); + rc = tavil_codec_enable_mad(codec, true); + if (IS_ERR_VALUE(rc)) { + tavil->mad_switch_cnt--; + goto done; + } + + break; + case SND_SOC_DAPM_PRE_PMD: + tavil->mad_switch_cnt--; + if (tavil->mad_switch_cnt != 0) + goto done; + + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x00); + tavil_codec_enable_mad(codec, false); + break; + } +done: + dev_dbg(tavil->dev, "%s: event = %d, mad_switch_cnt = %d\n", + __func__, event, tavil->mad_switch_cnt); + return rc; +} + static int tavil_codec_enable_interpolator(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -3223,11 +3434,139 @@ static int tavil_amic_pwr_lvl_put(struct snd_kcontrol *kcontrol, snd_soc_update_bits(codec, amic_reg, WCD934X_AMIC_PWR_LVL_MASK, mode_val << WCD934X_AMIC_PWR_LVL_SHIFT); - ret: return 0; } +static const char *const tavil_conn_mad_text[] = { + "NOTUSED1", "ADC1", "ADC2", "ADC3", "ADC4", "NOTUSED5", + "NOTUSED6", "NOTUSED2", "DMIC0", "DMIC1", "DMIC2", "DMIC3", + "DMIC4", "DMIC5", "NOTUSED3", "NOTUSED4" +}; + +static const struct soc_enum tavil_conn_mad_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tavil_conn_mad_text), + tavil_conn_mad_text); + +static int tavil_mad_input_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u8 tavil_mad_input; + + tavil_mad_input = snd_soc_read(codec, WCD934X_SOC_MAD_INP_SEL) & 0x0F; + ucontrol->value.integer.value[0] = tavil_mad_input; + + dev_dbg(codec->dev, "%s: tavil_mad_input = %s\n", __func__, + tavil_conn_mad_text[tavil_mad_input]); + + return 0; +} + +static int tavil_mad_input_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->component.card; + u8 tavil_mad_input; + char mad_amic_input_widget[6]; + const char *mad_input_widget; + const char *source_widget = NULL; + u32 adc, i, mic_bias_found = 0; + int ret = 0; + char *mad_input; + + tavil_mad_input = ucontrol->value.integer.value[0]; + + if (strnstr(tavil_conn_mad_text[tavil_mad_input], "NOTUSED", + sizeof("NOTUSED"))) { + dev_err(codec->dev, + "%s: Unsupported tavil_mad_input = %s\n", + __func__, tavil_conn_mad_text[tavil_mad_input]); + return -EINVAL; + } + + if (strnstr(tavil_conn_mad_text[tavil_mad_input], + "ADC", sizeof("ADC"))) { + mad_input = strpbrk(tavil_conn_mad_text[tavil_mad_input], + "1234"); + if (!mad_input) { + dev_err(codec->dev, "%s: Invalid MAD input %s\n", + __func__, tavil_conn_mad_text[tavil_mad_input]); + return -EINVAL; + } + + ret = kstrtouint(mad_input, 10, &adc); + if ((ret < 0) || (adc > 4)) { + dev_err(codec->dev, "%s: Invalid ADC = %s\n", __func__, + tavil_conn_mad_text[tavil_mad_input]); + return -EINVAL; + } + + /*AMIC4 and AMIC5 share ADC4*/ + if ((adc == 4) && + (snd_soc_read(codec, WCD934X_TX_NEW_AMIC_4_5_SEL) & 0x10)) + adc = 5; + + snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc); + + mad_input_widget = mad_amic_input_widget; + } else { + /* DMIC type input widget*/ + mad_input_widget = tavil_conn_mad_text[tavil_mad_input]; + } + + dev_dbg(codec->dev, + "%s: tavil input widget = %s\n", __func__, + mad_input_widget); + + for (i = 0; i < card->num_of_dapm_routes; i++) { + if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) { + source_widget = card->of_dapm_routes[i].source; + if (!source_widget) { + dev_err(codec->dev, + "%s: invalid source widget\n", + __func__); + return -EINVAL; + } + + if (strnstr(source_widget, + "MIC BIAS1", sizeof("MIC BIAS1"))) { + mic_bias_found = 1; + break; + } else if (strnstr(source_widget, + "MIC BIAS2", sizeof("MIC BIAS2"))) { + mic_bias_found = 2; + break; + } else if (strnstr(source_widget, + "MIC BIAS3", sizeof("MIC BIAS3"))) { + mic_bias_found = 3; + break; + } else if (strnstr(source_widget, + "MIC BIAS4", sizeof("MIC BIAS4"))) { + mic_bias_found = 4; + break; + } + } + } + + if (!mic_bias_found) { + dev_err(codec->dev, "%s: mic bias not found for input %s\n", + __func__, mad_input_widget); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: mic_bias found = %d\n", __func__, + mic_bias_found); + + snd_soc_update_bits(codec, WCD934X_SOC_MAD_INP_SEL, + 0x0F, tavil_mad_input); + snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, + 0x03, mic_bias_found); + + return 0; +} + static int tavil_ear_pa_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -3502,6 +3841,9 @@ static const struct snd_kcontrol_new tavil_snd_controls[] = { SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0, tavil_compander_get, tavil_compander_put), + SOC_ENUM_EXT("MAD Input", tavil_conn_mad_enum, + tavil_mad_input_get, tavil_mad_input_put), + SOC_SINGLE_EXT("DMIC1_CLK_PIN_MODE", SND_SOC_NOPM, 17, 1, 0, tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), @@ -3722,6 +4064,14 @@ static const char * const rx_int8_interp_mux_text[] = { "ZERO", "RX INT8 SEC MIX" }; +static const char * const mad_sel_txt[] = { + "SPE", "MSM" +}; + +static const char * const mad_inp_mux_txt[] = { + "MAD", "DEC1" +}; + static const char * const adc_mux_text[] = { "DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2" }; @@ -4112,6 +4462,12 @@ WCD_DAPM_ENUM(rx_int7_interp, WCD934X_CDC_RX7_RX_PATH_CTL, 5, WCD_DAPM_ENUM(rx_int8_interp, WCD934X_CDC_RX8_RX_PATH_CTL, 5, rx_int8_interp_mux_text); +WCD_DAPM_ENUM(mad_sel, WCD934X_CPE_SS_SVA_CFG, 0, + mad_sel_txt); + +WCD_DAPM_ENUM(mad_inp_mux, WCD934X_CPE_SS_SVA_CFG, 2, + mad_inp_mux_txt); + WCD_DAPM_ENUM_EXT(rx_int0_dem_inp, WCD934X_CDC_RX0_RX_PATH_SEC0, 0, rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, tavil_int_dem_inp_mux_put); @@ -4141,6 +4497,12 @@ WCD_DAPM_ENUM_EXT(tx_adc_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 2, WCD_DAPM_ENUM_EXT(tx_adc_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 4, adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +static const struct snd_kcontrol_new mad_cpe1_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new mad_cpe2_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + static const struct snd_kcontrol_new adc_us_mux0_switch = SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); @@ -4589,6 +4951,23 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { SND_SOC_DAPM_SWITCH("ADC US MUX8", WCD934X_CDC_TX8_TX_PATH_192_CTL, 0, 0, &adc_us_mux8_switch), + /* MAD related widgets */ + SND_SOC_DAPM_INPUT("MAD_CPE_INPUT"), + SND_SOC_DAPM_INPUT("MADINPUT"), + + WCD_DAPM_MUX("MAD_SEL MUX", 0, mad_sel), + WCD_DAPM_MUX("MAD_INP MUX", 0, mad_inp_mux), + + SND_SOC_DAPM_SWITCH_E("MAD_CPE1", SND_SOC_NOPM, 0, 0, + &mad_cpe1_switch, tavil_codec_cpe_mad_ctl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SWITCH_E("MAD_CPE2", SND_SOC_NOPM, 0, 0, + &mad_cpe2_switch, tavil_codec_cpe_mad_ctl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT1"), + SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT2"), + SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM, 0, 0, tavil_codec_ear_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | @@ -5405,6 +5784,7 @@ static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = { {WCD934X_CDC_BOOST0_BOOST_CFG2, 0x1C, 0x08}, {WCD934X_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12}, {WCD934X_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08}, + {WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x1F, 0x03}, }; static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = { @@ -5437,6 +5817,7 @@ static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = { {WCD934X_CODEC_RPM_CLK_GATE, 0x08, 0x00}, {WCD934X_TLMM_DMIC3_CLK_PINCFG, 0xFF, 0x0a}, {WCD934X_TLMM_DMIC3_DATA_PINCFG, 0xFF, 0x0a}, + {WCD934X_CPE_SS_SVA_CFG, 0x60, 0x00}, }; static void tavil_codec_init_reg(struct snd_soc_codec *codec) @@ -5642,6 +6023,8 @@ static int tavil_handle_pdata(struct tavil_priv *tavil, struct wcd9xxx_pdata *pdata) { struct snd_soc_codec *codec = tavil->codec; + u8 mad_dmic_ctl_val; + u32 def_dmic_rate, dmic_clk_drv; int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; int rc = 0; @@ -5666,6 +6049,86 @@ static int tavil_handle_pdata(struct tavil_priv *tavil, snd_soc_update_bits(codec, WCD934X_ANA_MICB3, 0x3F, vout_ctl_3); snd_soc_update_bits(codec, WCD934X_ANA_MICB4, 0x3F, vout_ctl_4); + /* Set the DMIC sample rate */ + switch (pdata->mclk_rate) { + case WCD934X_MCLK_CLK_9P6MHZ: + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + break; + case WCD934X_MCLK_CLK_12P288MHZ: + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ; + break; + default: + /* should never happen */ + dev_err(codec->dev, "%s: Invalid mclk_rate %d\n", + __func__, pdata->mclk_rate); + rc = -EINVAL; + goto done; + }; + + if (pdata->dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, "%s: dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + pdata->dmic_sample_rate = def_dmic_rate; + } + if (pdata->mad_dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, "%s: mad_dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + /* + * use dmic_sample_rate as the default for MAD + * if mad dmic sample rate is undefined + */ + pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate; + } + + if (pdata->dmic_clk_drv == + WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED) { + pdata->dmic_clk_drv = WCD934X_DMIC_CLK_DRIVE_DEFAULT; + dev_info(codec->dev, + "%s: dmic_clk_strength invalid, default = %d\n", + __func__, pdata->dmic_clk_drv); + } + + switch (pdata->dmic_clk_drv) { + case 2: + dmic_clk_drv = 0; + break; + case 4: + dmic_clk_drv = 1; + break; + case 8: + dmic_clk_drv = 2; + break; + case 16: + dmic_clk_drv = 3; + break; + default: + dev_err(codec->dev, + "%s: invalid dmic_clk_drv %d, using default\n", + __func__, pdata->dmic_clk_drv); + dmic_clk_drv = 0; + break; + } + + snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_PAD_DRVCTL_0, + 0x0C, dmic_clk_drv << 2); + + /* + * Default the DMIC clk rates to mad_dmic_sample_rate, + * whereas, the anc/txfe dmic rates to dmic_sample_rate + * since the anc/txfe are independent of mad block. + */ + mad_dmic_ctl_val = tavil_get_dmic_clk_val(tavil->codec, + pdata->mclk_rate, + pdata->mad_dmic_sample_rate); + snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC0_CTL, + 0x0E, mad_dmic_ctl_val << 1); + snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC1_CTL, + 0x0E, mad_dmic_ctl_val << 1); + snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC2_CTL, + 0x0E, mad_dmic_ctl_val << 1); + done: return rc; } From 6ed085d498aef4fac580f3ddeb9b9504b6175016 Mon Sep 17 00:00:00 2001 From: Bhalchandra Gajare Date: Wed, 17 Aug 2016 13:35:34 -0700 Subject: [PATCH 2/6] ASoC: wcd934x: Add support for voltage scaling WCD934X audio codec supports static voltage scaling (SVS) mode, which puts the codec into lowest power state. The codec has different power states (SVS2, SVS and Nominal). Add support in codec driver to vote for SVS when audio usecases are enabled and remove vote for SVS when audio usecases are disabled. The codec driver only votes for SVS or no SVS and the hardware internally manages to go into Nominal mode. CRs-Fixed: 1049012 Change-Id: I6f66d3136e3c49da54f5919184bc113267105463 Signed-off-by: Bhalchandra Gajare --- sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c | 26 +++++++++ sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h | 5 ++ sound/soc/codecs/wcd934x/wcd934x.c | 60 +++++++++++++++++++++ 3 files changed, 91 insertions(+) diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c index 306a4352ca46..225b3a755f66 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -466,6 +466,11 @@ static void wcd_cntl_do_shutdown(struct wcd_dsp_cntl *cntl) /* Put WDSP in reset state */ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL, 0x02, 0x00); + + /* If DSP transitions from boot to shutdown, then vote for SVS */ + if (cntl->is_wdsp_booted) + cntl->cdc_cb->cdc_vote_svs(codec, true); + cntl->is_wdsp_booted = false; } static int wcd_cntl_do_boot(struct wcd_dsp_cntl *cntl) @@ -507,6 +512,7 @@ static int wcd_cntl_do_boot(struct wcd_dsp_cntl *cntl) if (cntl->debug_mode) { wait_for_completion(&cntl->boot_complete); dev_dbg(codec->dev, "%s: WDSP booted in dbg mode\n", __func__); + cntl->is_wdsp_booted = true; goto done; } @@ -521,11 +527,16 @@ static int wcd_cntl_do_boot(struct wcd_dsp_cntl *cntl) } dev_dbg(codec->dev, "%s: WDSP booted in normal mode\n", __func__); + cntl->is_wdsp_booted = true; /* Enable WDOG */ snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG, 0x10, 0x10); done: + /* If dsp booted up, then remove vote on SVS */ + if (cntl->is_wdsp_booted) + cntl->cdc_cb->cdc_vote_svs(codec, false); + return ret; err_boot: /* call shutdown to perform cleanup */ @@ -899,6 +910,14 @@ void wcd_dsp_cntl_init(struct snd_soc_codec *codec, return; } + if (!params->cb || !params->cb->cdc_clk_en || + !params->cb->cdc_vote_svs) { + dev_err(codec->dev, + "%s: clk_en and vote_svs callbacks must be provided\n", + __func__); + return; + } + control = kzalloc(sizeof(*control), GFP_KERNEL); if (!(control)) return; @@ -911,6 +930,13 @@ void wcd_dsp_cntl_init(struct snd_soc_codec *codec, init_completion(&control->boot_complete); mutex_init(&control->clk_mutex); + /* + * The default state of WDSP is in SVS mode. + * Vote for SVS now, the vote will be removed only + * after DSP is booted up. + */ + control->cdc_cb->cdc_vote_svs(codec, true); + /* * If this is the last component needed by master to be ready, * then component_bind will be called within the component_add. diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h index caa7edc02da8..3d6db776a0b5 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h @@ -20,6 +20,8 @@ struct wcd_dsp_cdc_cb { /* Callback to enable codec clock */ int (*cdc_clk_en)(struct snd_soc_codec *, bool); + /* Callback to vote and unvote for SVS2 mode */ + void (*cdc_vote_svs)(struct snd_soc_codec *, bool); }; struct wcd_dsp_irq_info { @@ -83,6 +85,9 @@ struct wcd_dsp_cntl { /* clk related */ struct mutex clk_mutex; bool is_clk_enabled; + + /* Keep track of WDSP boot status */ + bool is_wdsp_booted; }; void wcd_dsp_cntl_init(struct snd_soc_codec *codec, diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index 26059caba07a..364aa17ea7e6 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -488,6 +488,10 @@ struct tavil_priv { /* cal info for codec */ struct fw_info *fw_data; + + /* SVS voting related */ + struct mutex svs_mutex; + int svs_ref_cnt; }; static const struct tavil_reg_mask_val tavil_spkr_default[] = { @@ -715,6 +719,36 @@ void *tavil_get_afe_config(struct snd_soc_codec *codec, } EXPORT_SYMBOL(tavil_get_afe_config); +static void tavil_vote_svs(struct tavil_priv *tavil, bool vote) +{ + struct wcd9xxx *wcd9xxx; + + wcd9xxx = tavil->wcd9xxx; + + mutex_lock(&tavil->svs_mutex); + if (vote) { + tavil->svs_ref_cnt++; + if (tavil->svs_ref_cnt == 1) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0, + 0x01, 0x01); + } else { + /* Do not decrement ref count if it is already 0 */ + if (tavil->svs_ref_cnt == 0) + goto done; + + tavil->svs_ref_cnt--; + if (tavil->svs_ref_cnt == 0) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0, + 0x01, 0x00); + } +done: + dev_dbg(tavil->dev, "%s: vote = %s, updated ref cnt = %u\n", __func__, + vote ? "vote" : "Unvote", tavil->svs_ref_cnt); + mutex_unlock(&tavil->svs_mutex); +} + static int tavil_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -5664,6 +5698,7 @@ static int __tavil_cdc_mclk_enable_locked(struct tavil_priv *tavil, dev_dbg(tavil->dev, "%s: mclk_enable = %u\n", __func__, enable); if (enable) { + tavil_vote_svs(tavil, true); ret = tavil_cdc_req_mclk_enable(tavil, true); if (ret) goto done; @@ -5671,6 +5706,7 @@ static int __tavil_cdc_mclk_enable_locked(struct tavil_priv *tavil, set_bit(AUDIO_NOMINAL, &tavil->status_mask); } else { tavil_cdc_req_mclk_enable(tavil, false); + tavil_vote_svs(tavil, false); } done: @@ -6148,8 +6184,16 @@ static void tavil_enable_sido_buck(struct snd_soc_codec *codec) tavil->resmgr->sido_input_src = SIDO_SOURCE_RCO_BG; } +static void tavil_cdc_vote_svs(struct snd_soc_codec *codec, bool vote) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + return tavil_vote_svs(tavil, vote); +} + struct wcd_dsp_cdc_cb cdc_cb = { .cdc_clk_en = tavil_codec_internal_rco_ctrl, + .cdc_vote_svs = tavil_cdc_vote_svs, }; static int tavil_wdsp_initialize(struct snd_soc_codec *codec) @@ -6326,6 +6370,12 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec) tavil_wdsp_initialize(codec); + /* + * Once the codec initialization is completed, the svs vote + * can be released allowing the codec to go to SVS2. + */ + tavil_vote_svs(tavil, false); + return ret; err_pdata: @@ -6870,6 +6920,14 @@ static int tavil_probe(struct platform_device *pdev) mutex_init(&tavil->swr.write_mutex); mutex_init(&tavil->swr.clk_mutex); mutex_init(&tavil->codec_mutex); + mutex_init(&tavil->svs_mutex); + + /* + * Codec hardware by default comes up in SVS mode. + * Initialize the svs_ref_cnt to 1 to reflect the hardware + * state in the driver. + */ + tavil->svs_ref_cnt = 1; /* * Init resource manager so that if child nodes such as SoundWire @@ -6932,6 +6990,7 @@ err_clk: wcd_resmgr_remove(tavil->resmgr); err_resmgr: mutex_destroy(&tavil->micb_lock); + mutex_destroy(&tavil->svs_mutex); mutex_destroy(&tavil->codec_mutex); mutex_destroy(&tavil->swr.read_mutex); mutex_destroy(&tavil->swr.write_mutex); @@ -6950,6 +7009,7 @@ static int tavil_remove(struct platform_device *pdev) return -EINVAL; mutex_destroy(&tavil->micb_lock); + mutex_destroy(&tavil->svs_mutex); mutex_destroy(&tavil->codec_mutex); mutex_destroy(&tavil->swr.read_mutex); mutex_destroy(&tavil->swr.write_mutex); From 2071a4cd7d51d71b8324275ab13e87bde3ffdb4b Mon Sep 17 00:00:00 2001 From: Phani Kumar Uppalapati Date: Wed, 10 Aug 2016 23:59:41 -0700 Subject: [PATCH 3/6] ASoC: wcd934x: Add support for codec MCLK2 enablement Add support to enable codec MCLK2 which is used for 44.1KHz and its multiples sample rate audio playback. CRs-Fixed: 1054643 Change-Id: Id157439b2a46ac4fa54b4a768f9555cf390399b5 Signed-off-by: Phani Kumar Uppalapati --- sound/soc/codecs/wcd934x/wcd934x.c | 118 +++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index 364aa17ea7e6..5e2cdcab4153 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -492,6 +492,8 @@ struct tavil_priv { /* SVS voting related */ struct mutex svs_mutex; int svs_ref_cnt; + + int native_clk_users; }; static const struct tavil_reg_mask_val tavil_spkr_default[] = { @@ -2198,6 +2200,62 @@ done: return rc; } +static int tavil_enable_native_supply(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 tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (++tavil->native_clk_users == 1) { + snd_soc_update_bits(codec, WCD934X_CLK_SYS_PLL_ENABLES, + 0x01, 0x01); + /* Sleep 100 us as per HW sequence */ + usleep_range(100, 120); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x06, 0x02); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, + 0x04, 0x00); + /* Sleep 30 us as per HW sequence */ + usleep_range(30, 50); + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x02); + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x10, 0x10); + } + break; + case SND_SOC_DAPM_PRE_PMD: + if (tavil->native_clk_users && + (--tavil->native_clk_users == 0)) { + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x10, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x06, 0x00); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_PLL_ENABLES, + 0x01, 0x00); + } + break; + } + + dev_dbg(codec->dev, "%s: native_clk_users: %d, event: %d\n", + __func__, tavil->native_clk_users, event); + + return 0; +} + static int tavil_codec_enable_interpolator(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -5050,6 +5108,26 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0, tavil_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("RX INT1 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_HPHL, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT2 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_HPHR, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT3 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_LO1, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT4 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_LO2, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT7 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_SPKR1, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT8 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_SPKR2, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + }; static int tavil_get_channel_map(struct snd_soc_dai *dai, @@ -5801,6 +5879,31 @@ static const struct wcd_resmgr_cb tavil_resmgr_cb = { .cdc_rco_ctrl = __tavil_codec_internal_rco_ctrl, }; +static const struct tavil_reg_mask_val tavil_codec_mclk2_defaults[] = { + /* + * PLL Settings: + * Clock Root: MCLK2, + * Clock Source: EXT_CLK, + * Clock Destination: MCLK2 + * Clock Freq In: 19.2MHz, + * Clock Freq Out: 11.2896MHz + */ + {WCD934X_CLK_SYS_MCLK2_PRG1, 0x60, 0x20}, + {WCD934X_CLK_SYS_INT_POST_DIV_REG0, 0xFF, 0x5E}, + {WCD934X_CLK_SYS_INT_POST_DIV_REG1, 0x1F, 0x1F}, + {WCD934X_CLK_SYS_INT_REF_DIV_REG0, 0xFF, 0x54}, + {WCD934X_CLK_SYS_INT_REF_DIV_REG1, 0xFF, 0x01}, + {WCD934X_CLK_SYS_INT_FILTER_REG1, 0x07, 0x04}, + {WCD934X_CLK_SYS_INT_PLL_L_VAL, 0xFF, 0x93}, + {WCD934X_CLK_SYS_INT_PLL_N_VAL, 0xFF, 0xFA}, + {WCD934X_CLK_SYS_INT_TEST_REG0, 0xFF, 0x90}, + {WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG, 0xFF, 0x7E}, + {WCD934X_CLK_SYS_INT_VCO_PROG, 0xFF, 0xF8}, + {WCD934X_CLK_SYS_INT_TEST_REG1, 0xFF, 0x68}, + {WCD934X_CLK_SYS_INT_LDO_LOCK_CFG, 0xFF, 0x40}, + {WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG, 0xFF, 0x32}, +}; + static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = { {WCD934X_BIAS_VBG_FINE_ADJ, 0xFF, 0x75}, {WCD934X_CODEC_CPR_SVS_CX_VDD, 0xFF, 0x7C}, /* value in svs mode */ @@ -6248,6 +6351,18 @@ struct wcd934x_mbhc *tavil_soc_get_mbhc(struct snd_soc_codec *codec) } EXPORT_SYMBOL(tavil_soc_get_mbhc); +static void tavil_mclk2_reg_defaults(struct tavil_priv *tavil) +{ + int i; + struct snd_soc_codec *codec = tavil->codec; + + /* MCLK2 configuration */ + for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_defaults); i++) + snd_soc_update_bits(codec, tavil_codec_mclk2_defaults[i].reg, + tavil_codec_mclk2_defaults[i].mask, + tavil_codec_mclk2_defaults[i].val); +} + static int tavil_soc_codec_probe(struct snd_soc_codec *codec) { struct wcd9xxx *control; @@ -6366,6 +6481,9 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec) INIT_DELAYED_WORK(&tavil->tx_mute_dwork[i].dwork, tavil_tx_mute_update_callback); } + + tavil_mclk2_reg_defaults(tavil); + snd_soc_dapm_sync(dapm); tavil_wdsp_initialize(codec); From 1aa6e9851e9dfbcc395d2f240dc810f5ba005775 Mon Sep 17 00:00:00 2001 From: Vidyakumar Athota Date: Wed, 27 Jul 2016 16:12:16 -0700 Subject: [PATCH 4/6] ASoC: wcd934x: add ASRC mixing support WCD934X supports mixing of integer sample rate clips with fractional sample rate clips using ASRC(asynchronous sample rate converter) block. Add support to use ASRC block in codec. CRs-Fixed: 1054643 Change-Id: I3a04cc902c74b49e1d40c5a52acbf1f7c907c34e Signed-off-by: Vidyakumar Athota --- sound/soc/codecs/wcd934x/wcd934x-routing.h | 119 ++- sound/soc/codecs/wcd934x/wcd934x.c | 949 +++++++++++++-------- sound/soc/codecs/wcd934x/wcd934x.h | 2 + 3 files changed, 693 insertions(+), 377 deletions(-) diff --git a/sound/soc/codecs/wcd934x/wcd934x-routing.h b/sound/soc/codecs/wcd934x/wcd934x-routing.h index 00cdfcd038df..bdb9ab22293d 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-routing.h +++ b/sound/soc/codecs/wcd934x/wcd934x-routing.h @@ -731,7 +731,8 @@ const struct snd_soc_dapm_route tavil_audio_map[] = { {"RX INT1_2 MUX", "RX5", "CDC_IF RX5 MUX"}, {"RX INT1_2 MUX", "RX6", "CDC_IF RX6 MUX"}, {"RX INT1_2 MUX", "RX7", "CDC_IF RX7 MUX"}, - {"RX INT1 SEC MIX", NULL, "RX INT1_2 MUX"}, + {"RX INT1_2 INTERP", NULL, "RX INT1_2 MUX"}, + {"RX INT1 SEC MIX", NULL, "RX INT1_2 INTERP"}, /* Mixing path INT2 */ {"RX INT2_2 MUX", "RX0", "CDC_IF RX0 MUX"}, @@ -742,7 +743,8 @@ const struct snd_soc_dapm_route tavil_audio_map[] = { {"RX INT2_2 MUX", "RX5", "CDC_IF RX5 MUX"}, {"RX INT2_2 MUX", "RX6", "CDC_IF RX6 MUX"}, {"RX INT2_2 MUX", "RX7", "CDC_IF RX7 MUX"}, - {"RX INT2 SEC MIX", NULL, "RX INT2_2 MUX"}, + {"RX INT2_2 INTERP", NULL, "RX INT2_2 MUX"}, + {"RX INT2 SEC MIX", NULL, "RX INT2_2 INTERP"}, /* Mixing path INT3 */ {"RX INT3_2 MUX", "RX0", "CDC_IF RX0 MUX"}, @@ -753,7 +755,8 @@ const struct snd_soc_dapm_route tavil_audio_map[] = { {"RX INT3_2 MUX", "RX5", "CDC_IF RX5 MUX"}, {"RX INT3_2 MUX", "RX6", "CDC_IF RX6 MUX"}, {"RX INT3_2 MUX", "RX7", "CDC_IF RX7 MUX"}, - {"RX INT3 SEC MIX", NULL, "RX INT3_2 MUX"}, + {"RX INT3_2 INTERP", NULL, "RX INT3_2 MUX"}, + {"RX INT3 SEC MIX", NULL, "RX INT3_2 INTERP"}, /* Mixing path INT4 */ {"RX INT4_2 MUX", "RX0", "CDC_IF RX0 MUX"}, @@ -764,7 +767,8 @@ const struct snd_soc_dapm_route tavil_audio_map[] = { {"RX INT4_2 MUX", "RX5", "CDC_IF RX5 MUX"}, {"RX INT4_2 MUX", "RX6", "CDC_IF RX6 MUX"}, {"RX INT4_2 MUX", "RX7", "CDC_IF RX7 MUX"}, - {"RX INT4 SEC MIX", NULL, "RX INT4_2 MUX"}, + {"RX INT4_2 INTERP", NULL, "RX INT4_2 MUX"}, + {"RX INT4 SEC MIX", NULL, "RX INT4_2 INTERP"}, /* Mixing path INT7 */ {"RX INT7_2 MUX", "RX0", "CDC_IF RX0 MUX"}, @@ -775,7 +779,8 @@ const struct snd_soc_dapm_route tavil_audio_map[] = { {"RX INT7_2 MUX", "RX5", "CDC_IF RX5 MUX"}, {"RX INT7_2 MUX", "RX6", "CDC_IF RX6 MUX"}, {"RX INT7_2 MUX", "RX7", "CDC_IF RX7 MUX"}, - {"RX INT7 SEC MIX", NULL, "RX INT7_2 MUX"}, + {"RX INT7_2 INTERP", NULL, "RX INT7_2 MUX"}, + {"RX INT7 SEC MIX", NULL, "RX INT7_2 INTERP"}, /* Mixing path INT8 */ {"RX INT8_2 MUX", "RX0", "CDC_IF RX0 MUX"}, @@ -786,67 +791,70 @@ const struct snd_soc_dapm_route tavil_audio_map[] = { {"RX INT8_2 MUX", "RX5", "CDC_IF RX5 MUX"}, {"RX INT8_2 MUX", "RX6", "CDC_IF RX6 MUX"}, {"RX INT8_2 MUX", "RX7", "CDC_IF RX7 MUX"}, - {"RX INT8 SEC MIX", NULL, "RX INT8_2 MUX"}, + {"RX INT8_2 INTERP", NULL, "RX INT8_2 MUX"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_2 INTERP"}, - {"RX INT0 SEC MIX", NULL, "RX INT0_1 MIX1"}, + {"RX INT0_1 INTERP", NULL, "RX INT0_1 MIX1"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_1 INTERP"}, {"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"}, {"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"}, - {"RX INT0 INTERP", NULL, "RX INT0 MIX2"}, - {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 INTERP"}, + {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 MIX2"}, {"RX INT0 DAC", NULL, "RX INT0 DEM MUX"}, {"RX INT0 DAC", NULL, "RX_BIAS"}, {"EAR PA", NULL, "RX INT0 DAC"}, {"EAR", NULL, "EAR PA"}, - {"RX INT1 SEC MIX", NULL, "RX INT1_1 MIX1"}, + {"RX INT1_1 INTERP", NULL, "RX INT1_1 MIX1"}, + {"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 INTERP", NULL, "RX INT1 MIX2"}, - {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 INTERP"}, + {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 MIX2"}, {"RX INT1 DAC", NULL, "RX INT1 DEM MUX"}, {"RX INT1 DAC", NULL, "RX_BIAS"}, {"HPHL PA", NULL, "RX INT1 DAC"}, {"HPHL", NULL, "HPHL PA"}, - {"RX INT2 SEC MIX", NULL, "RX INT2_1 MIX1"}, + {"RX INT2_1 INTERP", NULL, "RX INT2_1 MIX1"}, + {"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 INTERP", NULL, "RX INT2 MIX2"}, - {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 INTERP"}, + {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 MIX2"}, {"RX INT2 DAC", NULL, "RX INT2 DEM MUX"}, {"RX INT2 DAC", NULL, "RX_BIAS"}, {"HPHR PA", NULL, "RX INT2 DAC"}, {"HPHR", NULL, "HPHR PA"}, - {"RX INT3 SEC MIX", NULL, "RX INT3_1 MIX1"}, + {"RX INT3_1 INTERP", NULL, "RX INT3_1 MIX1"}, + {"RX INT3 SEC MIX", NULL, "RX INT3_1 INTERP"}, {"RX INT3 MIX2", NULL, "RX INT3 SEC MIX"}, {"RX INT3 MIX2", NULL, "RX INT3 MIX2 INP"}, - {"RX INT3 INTERP", NULL, "RX INT3 MIX2"}, - {"RX INT3 DAC", NULL, "RX INT3 INTERP"}, + {"RX INT3 DAC", NULL, "RX INT3 MIX2"}, {"RX INT3 DAC", NULL, "RX_BIAS"}, {"LINEOUT1 PA", NULL, "RX INT3 DAC"}, {"LINEOUT1", NULL, "LINEOUT1 PA"}, + {"RX INT4_1 INTERP", NULL, "RX INT4_1 MIX1"}, + {"RX INT4 SEC MIX", NULL, "RX INT4_1 INTERP"}, {"RX INT4 SEC MIX", NULL, "RX INT4_1 MIX1"}, {"RX INT4 MIX2", NULL, "RX INT4 SEC MIX"}, {"RX INT4 MIX2", NULL, "RX INT4 MIX2 INP"}, - {"RX INT4 INTERP", NULL, "RX INT4 MIX2"}, - {"RX INT4 DAC", NULL, "RX INT4 INTERP"}, + {"RX INT4 DAC", NULL, "RX INT4 MIX2"}, {"RX INT4 DAC", NULL, "RX_BIAS"}, {"LINEOUT2 PA", NULL, "RX INT4 DAC"}, {"LINEOUT2", NULL, "LINEOUT2 PA"}, - {"RX INT7 SEC MIX", NULL, "RX INT7_1 MIX1"}, + {"RX INT7_1 INTERP", NULL, "RX INT7_1 MIX1"}, + {"RX INT7 SEC MIX", NULL, "RX INT7_1 INTERP"}, {"RX INT7 MIX2", NULL, "RX INT7 SEC MIX"}, {"RX INT7 MIX2", NULL, "RX INT7 MIX2 INP"}, - {"RX INT7 INTERP", NULL, "RX INT7 MIX2"}, - {"RX INT7 CHAIN", NULL, "RX INT7 INTERP"}, + {"RX INT7 CHAIN", NULL, "RX INT7 MIX2"}, {"RX INT7 CHAIN", NULL, "RX_BIAS"}, {"SPK1 OUT", NULL, "RX INT7 CHAIN"}, + {"RX INT8_1 INTERP", NULL, "RX INT8_1 MIX1"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_1 INTERP"}, {"RX INT8 SEC MIX", NULL, "RX INT8_1 MIX1"}, - {"RX INT8 INTERP", NULL, "RX INT8 SEC MIX"}, - {"RX INT8 CHAIN", NULL, "RX INT8 INTERP"}, + {"RX INT8 CHAIN", NULL, "RX INT8 SEC MIX"}, {"RX INT8 CHAIN", NULL, "RX_BIAS"}, {"SPK2 OUT", NULL, "RX INT8 CHAIN"}, @@ -1014,6 +1022,67 @@ const struct snd_soc_dapm_route tavil_audio_map[] = { {"RX INT4 MIX2 INP", "SRC1", "SRC1"}, {"RX INT7 MIX2 INP", "SRC0", "SRC0"}, {"RX INT7 MIX2 INP", "SRC1", "SRC1"}, + + /* Native clk main path routing */ + {"RX INT1_1 NATIVE MUX", "ON", "RX INT1_1 MIX1"}, + {"RX INT1_1 INTERP", NULL, "RX INT1_1 NATIVE MUX"}, + {"RX INT1_1 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"}, + + {"RX INT2_1 NATIVE MUX", "ON", "RX INT2_1 MIX1"}, + {"RX INT2_1 INTERP", NULL, "RX INT2_1 NATIVE MUX"}, + {"RX INT2_1 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"}, + + {"RX INT3_1 NATIVE MUX", "ON", "RX INT3_1 MIX1"}, + {"RX INT3_1 INTERP", NULL, "RX INT3_1 NATIVE MUX"}, + {"RX INT3_1 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"}, + + {"RX INT4_1 NATIVE MUX", "ON", "RX INT4_1 MIX1"}, + {"RX INT4_1 INTERP", NULL, "RX INT4_1 NATIVE MUX"}, + {"RX INT4_1 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"}, + + /* Native clk mix path routing */ + {"RX INT1_2 NATIVE MUX", "ON", "RX INT1_2 MUX"}, + {"RX INT1_2 INTERP", NULL, "RX INT1_2 NATIVE MUX"}, + {"RX INT1_2 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"}, + + {"RX INT2_2 NATIVE MUX", "ON", "RX INT2_2 MUX"}, + {"RX INT2_2 INTERP", NULL, "RX INT2_2 NATIVE MUX"}, + {"RX INT2_2 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"}, + + {"RX INT3_2 NATIVE MUX", "ON", "RX INT3_2 MUX"}, + {"RX INT3_2 INTERP", NULL, "RX INT3_2 NATIVE MUX"}, + {"RX INT3_2 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"}, + + {"RX INT4_2 NATIVE MUX", "ON", "RX INT4_2 MUX"}, + {"RX INT4_2 INTERP", NULL, "RX INT4_2 NATIVE MUX"}, + {"RX INT4_2 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"}, + + {"RX INT7_2 NATIVE MUX", "ON", "RX INT7_2 MUX"}, + {"RX INT7_2 INTERP", NULL, "RX INT7_2 NATIVE MUX"}, + {"RX INT7_2 NATIVE MUX", NULL, "RX INT7 NATIVE SUPPLY"}, + + {"RX INT8_2 NATIVE MUX", "ON", "RX INT8_2 MUX"}, + {"RX INT8_2 INTERP", NULL, "RX INT8_2 NATIVE MUX"}, + {"RX INT8_2 NATIVE MUX", NULL, "RX INT8 NATIVE SUPPLY"}, + + /* ASRC Routing */ + {"ASRC0 MUX", "ASRC_IN_HPHL", "RX INT1_2 INTERP"}, + {"RX INT1 SEC MIX", "HPHL Switch", "ASRC0 MUX"}, + + {"ASRC1 MUX", "ASRC_IN_HPHR", "RX INT2_2 INTERP"}, + {"RX INT2 SEC MIX", "HPHR Switch", "ASRC1 MUX"}, + + {"ASRC0 MUX", "ASRC_IN_LO1", "RX INT3_2 INTERP"}, + {"RX INT3 SEC MIX", "LO1 Switch", "ASRC0 MUX"}, + + {"ASRC1 MUX", "ASRC_IN_LO2", "RX INT4_2 INTERP"}, + {"RX INT4 SEC MIX", "LO2 Switch", "ASRC1 MUX"}, + + {"ASRC2 MUX", "ASRC_IN_SPKR1", "RX INT7_2 INTERP"}, + {"RX INT7 SEC MIX", NULL, "ASRC2 MUX"}, + + {"ASRC3 MUX", "ASRC_IN_SPKR2", "RX INT8_2 INTERP"}, + {"RX INT8 SEC MIX", NULL, "ASRC3 MUX"}, }; #endif diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index 5e2cdcab4153..9fb40274d9f9 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -94,6 +94,7 @@ static const struct snd_kcontrol_new name##_mux = \ #define WCD934X_NUM_INTERPOLATORS 9 #define WCD934X_NUM_DECIMATORS 9 +#define WCD934X_RX_PATH_CTL_OFFSET 20 #define BYTE_BIT_MASK(nr) (1 << ((nr) % BITS_PER_BYTE)) @@ -300,6 +301,24 @@ enum { COMPANDER_MAX, }; +enum { + ASRC_IN_HPHL, + ASRC_IN_LO1, + ASRC_IN_HPHR, + ASRC_IN_LO2, + ASRC_IN_SPKR1, + ASRC_IN_SPKR2, + ASRC_INVALID, +}; + +enum { + ASRC0, + ASRC1, + ASRC2, + ASRC3, + ASRC_MAX, +}; + static struct afe_param_slimbus_slave_port_cfg tavil_slimbus_slave_port_cfg = { .minor_version = 1, .slimbus_dev_id = AFE_SLIMBUS_DEVICE_1, @@ -455,7 +474,6 @@ struct tavil_priv { /* Mad switch reference count */ int mad_switch_cnt; - u16 prim_int_users[WCD934X_NUM_INTERPOLATORS]; /* to track the status */ unsigned long status_mask; @@ -494,6 +512,10 @@ struct tavil_priv { int svs_ref_cnt; int native_clk_users; + /* ASRC users count */ + int asrc_users[ASRC_MAX]; + /* Main path clock users count */ + int main_clk_users[WCD934X_NUM_INTERPOLATORS]; }; static const struct tavil_reg_mask_val tavil_spkr_default[] = { @@ -1745,255 +1767,6 @@ static int tavil_codec_enable_swr(struct snd_soc_dapm_widget *w, return __tavil_codec_enable_swr(w, event); } -static u16 tavil_interp_get_primary_reg(u16 reg, u16 *ind) -{ - u16 prim_int_reg = 0; - - switch (reg) { - case WCD934X_CDC_RX0_RX_PATH_CTL: - case WCD934X_CDC_RX0_RX_PATH_MIX_CTL: - prim_int_reg = WCD934X_CDC_RX0_RX_PATH_CTL; - *ind = 0; - break; - case WCD934X_CDC_RX1_RX_PATH_CTL: - case WCD934X_CDC_RX1_RX_PATH_MIX_CTL: - prim_int_reg = WCD934X_CDC_RX1_RX_PATH_CTL; - *ind = 1; - break; - case WCD934X_CDC_RX2_RX_PATH_CTL: - case WCD934X_CDC_RX2_RX_PATH_MIX_CTL: - prim_int_reg = WCD934X_CDC_RX2_RX_PATH_CTL; - *ind = 2; - break; - case WCD934X_CDC_RX3_RX_PATH_CTL: - case WCD934X_CDC_RX3_RX_PATH_MIX_CTL: - prim_int_reg = WCD934X_CDC_RX3_RX_PATH_CTL; - *ind = 3; - break; - case WCD934X_CDC_RX4_RX_PATH_CTL: - case WCD934X_CDC_RX4_RX_PATH_MIX_CTL: - prim_int_reg = WCD934X_CDC_RX4_RX_PATH_CTL; - *ind = 4; - break; - case WCD934X_CDC_RX7_RX_PATH_CTL: - case WCD934X_CDC_RX7_RX_PATH_MIX_CTL: - prim_int_reg = WCD934X_CDC_RX7_RX_PATH_CTL; - *ind = 7; - break; - case WCD934X_CDC_RX8_RX_PATH_CTL: - case WCD934X_CDC_RX8_RX_PATH_MIX_CTL: - prim_int_reg = WCD934X_CDC_RX8_RX_PATH_CTL; - *ind = 8; - break; - }; - - return prim_int_reg; -} - -static void tavil_codec_hd2_control(struct snd_soc_codec *codec, - u16 prim_int_reg, int event) -{ - u16 hd2_scale_reg; - u16 hd2_enable_reg = 0; - - if (prim_int_reg == WCD934X_CDC_RX1_RX_PATH_CTL) { - hd2_scale_reg = WCD934X_CDC_RX1_RX_PATH_SEC3; - hd2_enable_reg = WCD934X_CDC_RX1_RX_PATH_CFG0; - } - if (prim_int_reg == WCD934X_CDC_RX2_RX_PATH_CTL) { - hd2_scale_reg = WCD934X_CDC_RX2_RX_PATH_SEC3; - hd2_enable_reg = WCD934X_CDC_RX2_RX_PATH_CFG0; - } - - if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { - snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x10); - snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x01); - snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04); - } - - if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { - snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00); - snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x00); - snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00); - } -} - -static int tavil_codec_enable_prim_interpolator(struct snd_soc_codec *codec, - u16 reg, int event) -{ - struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); - u16 prim_int_reg; - u16 ind = 0; - - prim_int_reg = tavil_interp_get_primary_reg(reg, &ind); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - tavil->prim_int_users[ind]++; - if (tavil->prim_int_users[ind] == 1) { - /* PGA Mute enable */ - snd_soc_update_bits(codec, prim_int_reg, - 0x10, 0x10); - tavil_codec_hd2_control(codec, prim_int_reg, event); - /* RX path CLK enable */ - snd_soc_update_bits(codec, prim_int_reg, - 1 << 0x5, 1 << 0x5); - } - if ((reg != prim_int_reg) && - ((snd_soc_read(codec, prim_int_reg)) & 0x10)) - snd_soc_update_bits(codec, reg, 0x10, 0x10); - break; - case SND_SOC_DAPM_POST_PMD: - tavil->prim_int_users[ind]--; - if (tavil->prim_int_users[ind] == 0) { - snd_soc_update_bits(codec, prim_int_reg, - 1 << 0x5, 0 << 0x5); - snd_soc_update_bits(codec, prim_int_reg, - 0x40, 0x40); - snd_soc_update_bits(codec, prim_int_reg, - 0x40, 0x00); - tavil_codec_hd2_control(codec, prim_int_reg, event); - } - break; - }; - - dev_dbg(codec->dev, "%s: primary interpolator: INT%d, users: %d\n", - __func__, ind, tavil->prim_int_users[ind]); - return 0; -} - -static int tavil_codec_enable_mix_path(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 tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); - u16 gain_reg; - int offset_val = 0; - int val = 0; - - dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); - - switch (w->reg) { - case WCD934X_CDC_RX0_RX_PATH_MIX_CTL: - gain_reg = WCD934X_CDC_RX0_RX_VOL_MIX_CTL; - break; - case WCD934X_CDC_RX1_RX_PATH_MIX_CTL: - gain_reg = WCD934X_CDC_RX1_RX_VOL_MIX_CTL; - break; - case WCD934X_CDC_RX2_RX_PATH_MIX_CTL: - gain_reg = WCD934X_CDC_RX2_RX_VOL_MIX_CTL; - break; - case WCD934X_CDC_RX3_RX_PATH_MIX_CTL: - gain_reg = WCD934X_CDC_RX3_RX_VOL_MIX_CTL; - break; - case WCD934X_CDC_RX4_RX_PATH_MIX_CTL: - gain_reg = WCD934X_CDC_RX4_RX_VOL_MIX_CTL; - break; - case WCD934X_CDC_RX7_RX_PATH_MIX_CTL: - __tavil_codec_enable_swr(w, event); - gain_reg = WCD934X_CDC_RX7_RX_VOL_MIX_CTL; - break; - case WCD934X_CDC_RX8_RX_PATH_MIX_CTL: - __tavil_codec_enable_swr(w, event); - gain_reg = WCD934X_CDC_RX8_RX_VOL_MIX_CTL; - break; - default: - dev_err(codec->dev, "%s: No gain register avail for %s\n", - __func__, w->name); - return 0; - }; - - switch (event) { - case SND_SOC_DAPM_POST_PMU: - if ((tavil->swr.spkr_gain_offset == - WCD934X_RX_GAIN_OFFSET_M1P5_DB) && - (tavil->comp_enabled[COMPANDER_7] || - tavil->comp_enabled[COMPANDER_8]) && - (gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL || - gain_reg == WCD934X_CDC_RX8_RX_VOL_MIX_CTL)) { - snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1, - 0x01, 0x01); - snd_soc_update_bits(codec, - WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, - 0x01, 0x01); - snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1, - 0x01, 0x01); - snd_soc_update_bits(codec, - WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, - 0x01, 0x01); - offset_val = -2; - } - val = snd_soc_read(codec, gain_reg); - val += offset_val; - snd_soc_write(codec, gain_reg, val); - break; - case SND_SOC_DAPM_POST_PMD: - if ((tavil->swr.spkr_gain_offset == - WCD934X_RX_GAIN_OFFSET_M1P5_DB) && - (tavil->comp_enabled[COMPANDER_7] || - tavil->comp_enabled[COMPANDER_8]) && - (gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL || - gain_reg == WCD934X_CDC_RX8_RX_VOL_MIX_CTL)) { - snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1, - 0x01, 0x00); - snd_soc_update_bits(codec, - WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, - 0x01, 0x00); - snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1, - 0x01, 0x00); - snd_soc_update_bits(codec, - WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, - 0x01, 0x00); - offset_val = 2; - val = snd_soc_read(codec, gain_reg); - val += offset_val; - snd_soc_write(codec, gain_reg, val); - } - break; - }; - - return 0; -} - -static int tavil_config_compander(struct snd_soc_codec *codec, int interp_n, - int event) -{ - struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); - int comp; - u16 comp_ctl0_reg, rx_path_cfg0_reg; - - /* EAR does not have compander */ - if (!interp_n) - return 0; - - comp = interp_n - 1; - dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n", - __func__, event, comp + 1, tavil->comp_enabled[comp]); - - if (!tavil->comp_enabled[comp]) - return 0; - - comp_ctl0_reg = WCD934X_CDC_COMPANDER1_CTL0 + (comp * 8); - rx_path_cfg0_reg = WCD934X_CDC_RX1_RX_PATH_CFG0 + (comp * 20); - - if (SND_SOC_DAPM_EVENT_ON(event)) { - /* Enable Compander Clock */ - snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01); - snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); - snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); - snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02); - } - - if (SND_SOC_DAPM_EVENT_OFF(event)) { - snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); - snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); - snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); - snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); - } - - return 0; -} - static int tavil_codec_config_mad(struct snd_soc_codec *codec) { int ret = 0; @@ -2200,6 +1973,151 @@ done: return rc; } +static int tavil_codec_enable_asrc(struct snd_soc_codec *codec, + int asrc_in, int event) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u16 cfg_reg, ctl_reg, clk_reg, asrc_ctl, mix_ctl_reg; + int asrc, ret = 0; + u8 main_sr, mix_sr, asrc_mode = 0; + + switch (asrc_in) { + case ASRC_IN_HPHL: + cfg_reg = WCD934X_CDC_RX1_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX1_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC0_CTL1; + asrc = ASRC0; + break; + case ASRC_IN_LO1: + cfg_reg = WCD934X_CDC_RX3_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX3_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC0_CTL1; + asrc = ASRC0; + break; + case ASRC_IN_HPHR: + cfg_reg = WCD934X_CDC_RX2_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX2_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC1_CTL1; + asrc = ASRC1; + break; + case ASRC_IN_LO2: + cfg_reg = WCD934X_CDC_RX4_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX4_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC1_CTL1; + asrc = ASRC1; + break; + case ASRC_IN_SPKR1: + cfg_reg = WCD934X_CDC_RX7_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX7_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC2_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC2_CTL1; + asrc = ASRC2; + break; + case ASRC_IN_SPKR2: + cfg_reg = WCD934X_CDC_RX8_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX8_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC3_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC3_CTL1; + asrc = ASRC3; + break; + default: + dev_err(codec->dev, "%s: Invalid asrc input :%d\n", __func__, + asrc_in); + ret = -EINVAL; + goto done; + }; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tavil->asrc_users[asrc] == 0) { + snd_soc_update_bits(codec, cfg_reg, 0x80, 0x80); + snd_soc_update_bits(codec, clk_reg, 0x01, 0x01); + main_sr = snd_soc_read(codec, ctl_reg) & 0x0F; + mix_ctl_reg = ctl_reg + 5; + mix_sr = snd_soc_read(codec, mix_ctl_reg) & 0x0F; + /* Integer main and Fractional mix path */ + if (main_sr < 8 && mix_sr > 9) { + asrc_mode = 2; + } else if (main_sr > 8 && mix_sr < 8) { + /* Fractional main and Integer mix path */ + if (mix_sr < 5) + asrc_mode = 1; + else + asrc_mode = 3; + } else if (main_sr < 8 && mix_sr < 8) { + /* Integer main and Integer mix path */ + asrc_mode = 5; + } + dev_dbg(codec->dev, "%s: main_sr:%d mix_sr:%d asrc_mode %d\n", + __func__, main_sr, mix_sr, asrc_mode); + snd_soc_update_bits(codec, asrc_ctl, 0x07, asrc_mode); + } + tavil->asrc_users[asrc]++; + break; + case SND_SOC_DAPM_POST_PMD: + tavil->asrc_users[asrc]--; + if (tavil->asrc_users[asrc] <= 0) { + tavil->asrc_users[asrc] = 0; + snd_soc_update_bits(codec, asrc_ctl, 0x07, 0x00); + snd_soc_update_bits(codec, cfg_reg, 0x80, 0x00); + snd_soc_update_bits(codec, clk_reg, 0x01, 0x00); + } + break; + }; + + dev_dbg(codec->dev, "%s: ASRC%d, users: %d\n", + __func__, asrc, tavil->asrc_users[asrc]); + +done: + return ret; +} + +static int tavil_codec_enable_asrc_resampler(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 ret = 0; + u8 cfg, asrc_in; + + cfg = snd_soc_read(codec, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0); + if (!(cfg & 0xFF)) { + dev_err(codec->dev, "%s: ASRC%u input not selected\n", + __func__, w->shift); + return -EINVAL; + } + + switch (w->shift) { + case ASRC0: + asrc_in = ((cfg & 0x03) == 1) ? ASRC_IN_HPHL : ASRC_IN_LO1; + ret = tavil_codec_enable_asrc(codec, asrc_in, event); + break; + case ASRC1: + asrc_in = ((cfg & 0x0C) == 4) ? ASRC_IN_HPHR : ASRC_IN_LO2; + ret = tavil_codec_enable_asrc(codec, asrc_in, event); + break; + case ASRC2: + asrc_in = ((cfg & 0x30) == 0x20) ? ASRC_IN_SPKR1 : ASRC_INVALID; + ret = tavil_codec_enable_asrc(codec, asrc_in, event); + break; + case ASRC3: + asrc_in = ((cfg & 0xC0) == 0x80) ? ASRC_IN_SPKR2 : ASRC_INVALID; + ret = tavil_codec_enable_asrc(codec, asrc_in, event); + break; + default: + dev_err(codec->dev, "%s: Invalid asrc:%u\n", __func__, + w->shift); + ret = -EINVAL; + break; + }; + + return ret; +} + static int tavil_enable_native_supply(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -2211,7 +2129,6 @@ static int tavil_enable_native_supply(struct snd_soc_dapm_widget *w, if (++tavil->native_clk_users == 1) { snd_soc_update_bits(codec, WCD934X_CLK_SYS_PLL_ENABLES, 0x01, 0x01); - /* Sleep 100 us as per HW sequence */ usleep_range(100, 120); snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, 0x06, 0x02); @@ -2219,7 +2136,6 @@ static int tavil_enable_native_supply(struct snd_soc_dapm_widget *w, 0x01, 0x01); snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, 0x04, 0x00); - /* Sleep 30 us as per HW sequence */ usleep_range(30, 50); snd_soc_update_bits(codec, WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL, @@ -2256,9 +2172,226 @@ static int tavil_enable_native_supply(struct snd_soc_dapm_widget *w, return 0; } -static int tavil_codec_enable_interpolator(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, - int event) +static void tavil_codec_hd2_control(struct snd_soc_codec *codec, + u16 interp_idx, int event) +{ + u16 hd2_scale_reg; + u16 hd2_enable_reg = 0; + + switch (interp_idx) { + case INTERP_HPHL: + hd2_scale_reg = WCD934X_CDC_RX1_RX_PATH_SEC3; + hd2_enable_reg = WCD934X_CDC_RX1_RX_PATH_CFG0; + break; + case INTERP_HPHR: + hd2_scale_reg = WCD934X_CDC_RX2_RX_PATH_SEC3; + hd2_enable_reg = WCD934X_CDC_RX2_RX_PATH_CFG0; + break; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x10); + snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x01); + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00); + } +} + +static int tavil_config_compander(struct snd_soc_codec *codec, int interp_n, + int event) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int comp; + u16 comp_ctl0_reg, rx_path_cfg0_reg; + + /* EAR does not have compander */ + if (!interp_n) + return 0; + + comp = interp_n - 1; + dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n", + __func__, event, comp + 1, tavil->comp_enabled[comp]); + + if (!tavil->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = WCD934X_CDC_COMPANDER1_CTL0 + (comp * 8); + rx_path_cfg0_reg = WCD934X_CDC_RX1_RX_PATH_CFG0 + (comp * 20); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); + } + + return 0; +} + +/** + * tavil_codec_enable_interp_clk - Enable main path Interpolator + * clock. + * + * @codec: Codec instance + * @event: Indicates speaker path gain offset value + * @intp_idx: Interpolator index + * Returns number of main clock users + */ +int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec, + int event, int interp_idx) +{ + struct tavil_priv *tavil; + u16 main_reg; + + if (!codec) { + pr_err("%s: codec is NULL\n", __func__); + return -EINVAL; + } + + tavil = snd_soc_codec_get_drvdata(codec); + main_reg = WCD934X_CDC_RX0_RX_PATH_CTL + (interp_idx * 20); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (tavil->main_clk_users[interp_idx] == 0) { + /* Main path PGA mute enable */ + snd_soc_update_bits(codec, main_reg, 0x10, 0x10); + /* Clk enable */ + snd_soc_update_bits(codec, main_reg, 0x20, 0x20); + tavil_codec_hd2_control(codec, interp_idx, event); + tavil_config_compander(codec, interp_idx, event); + } + tavil->main_clk_users[interp_idx]++; + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + tavil->main_clk_users[interp_idx]--; + if (tavil->main_clk_users[interp_idx] <= 0) { + tavil->main_clk_users[interp_idx] = 0; + tavil_config_compander(codec, interp_idx, event); + tavil_codec_hd2_control(codec, interp_idx, event); + /* Clk Disable */ + snd_soc_update_bits(codec, main_reg, 0x20, 0x00); + /* Reset enable and disable */ + snd_soc_update_bits(codec, main_reg, 0x40, 0x40); + snd_soc_update_bits(codec, main_reg, 0x40, 0x00); + /* Reset rate to 48K*/ + snd_soc_update_bits(codec, main_reg, 0x0F, 0x04); + } + } + + dev_dbg(codec->dev, "%s event %d main_clk_users %d\n", + __func__, event, tavil->main_clk_users[interp_idx]); + + return tavil->main_clk_users[interp_idx]; +} +EXPORT_SYMBOL(tavil_codec_enable_interp_clk); + +static int tavil_codec_enable_mix_path(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 tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u16 gain_reg, mix_reg; + int offset_val = 0; + int val = 0; + + if (w->shift >= WCD934X_NUM_INTERPOLATORS || + w->shift == INTERP_LO3_NA || w->shift == INTERP_LO4_NA) { + dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n", + __func__, w->shift, w->name); + return -EINVAL; + }; + + gain_reg = WCD934X_CDC_RX0_RX_VOL_MIX_CTL + + (w->shift * WCD934X_RX_PATH_CTL_OFFSET); + mix_reg = WCD934X_CDC_RX0_RX_PATH_MIX_CTL + + (w->shift * WCD934X_RX_PATH_CTL_OFFSET); + + if (w->shift == INTERP_SPKR1 || w->shift == INTERP_SPKR2) + __tavil_codec_enable_swr(w, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil_codec_enable_interp_clk(codec, event, w->shift); + /* Clk enable */ + snd_soc_update_bits(codec, mix_reg, 0x20, 0x20); + break; + case SND_SOC_DAPM_POST_PMU: + if ((tavil->swr.spkr_gain_offset == + WCD934X_RX_GAIN_OFFSET_M1P5_DB) && + (tavil->comp_enabled[COMPANDER_7] || + tavil->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL || + gain_reg == WCD934X_CDC_RX8_RX_VOL_MIX_CTL)) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + break; + case SND_SOC_DAPM_POST_PMD: + /* Clk Disable */ + snd_soc_update_bits(codec, mix_reg, 0x20, 0x00); + tavil_codec_enable_interp_clk(codec, event, w->shift); + /* Reset enable and disable */ + snd_soc_update_bits(codec, mix_reg, 0x40, 0x40); + snd_soc_update_bits(codec, mix_reg, 0x40, 0x00); + + if ((tavil->swr.spkr_gain_offset == + WCD934X_RX_GAIN_OFFSET_M1P5_DB) && + (tavil->comp_enabled[COMPANDER_7] || + tavil->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL || + gain_reg == WCD934X_CDC_RX8_RX_VOL_MIX_CTL)) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + break; + }; + dev_dbg(codec->dev, "%s event %d name %s\n", __func__, event, w->name); + + return 0; +} + +static int tavil_codec_enable_main_path(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 tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); @@ -2269,40 +2402,23 @@ static int tavil_codec_enable_interpolator(struct snd_soc_dapm_widget *w, dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); - if (!(strcmp(w->name, "RX INT0 INTERP"))) { - reg = WCD934X_CDC_RX0_RX_PATH_CTL; - gain_reg = WCD934X_CDC_RX0_RX_VOL_CTL; - } else if (!(strcmp(w->name, "RX INT1 INTERP"))) { - reg = WCD934X_CDC_RX1_RX_PATH_CTL; - gain_reg = WCD934X_CDC_RX1_RX_VOL_CTL; - } else if (!(strcmp(w->name, "RX INT2 INTERP"))) { - reg = WCD934X_CDC_RX2_RX_PATH_CTL; - gain_reg = WCD934X_CDC_RX2_RX_VOL_CTL; - } else if (!(strcmp(w->name, "RX INT3 INTERP"))) { - reg = WCD934X_CDC_RX3_RX_PATH_CTL; - gain_reg = WCD934X_CDC_RX3_RX_VOL_CTL; - } else if (!(strcmp(w->name, "RX INT4 INTERP"))) { - reg = WCD934X_CDC_RX4_RX_PATH_CTL; - gain_reg = WCD934X_CDC_RX4_RX_VOL_CTL; - } else if (!(strcmp(w->name, "RX INT7 INTERP"))) { - reg = WCD934X_CDC_RX7_RX_PATH_CTL; - gain_reg = WCD934X_CDC_RX7_RX_VOL_CTL; - } else if (!(strcmp(w->name, "RX INT8 INTERP"))) { - reg = WCD934X_CDC_RX8_RX_PATH_CTL; - gain_reg = WCD934X_CDC_RX8_RX_VOL_CTL; - } else { - dev_err(codec->dev, "%s: Interpolator reg not found\n", - __func__); + if (w->shift >= WCD934X_NUM_INTERPOLATORS || + w->shift == INTERP_LO3_NA || w->shift == INTERP_LO4_NA) { + dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n", + __func__, w->shift, w->name); return -EINVAL; - } + }; + + reg = WCD934X_CDC_RX0_RX_PATH_CTL + (w->shift * + WCD934X_RX_PATH_CTL_OFFSET); + gain_reg = WCD934X_CDC_RX0_RX_VOL_CTL + (w->shift * + WCD934X_RX_PATH_CTL_OFFSET); switch (event) { case SND_SOC_DAPM_PRE_PMU: - /* Reset if needed */ - tavil_codec_enable_prim_interpolator(codec, reg, event); + tavil_codec_enable_interp_clk(codec, event, w->shift); break; case SND_SOC_DAPM_POST_PMU: - tavil_config_compander(codec, w->shift, event); /* apply gain after int clk is enabled */ if ((tavil->swr.spkr_gain_offset == WCD934X_RX_GAIN_OFFSET_M1P5_DB) && @@ -2327,8 +2443,8 @@ static int tavil_codec_enable_interpolator(struct snd_soc_dapm_widget *w, snd_soc_write(codec, gain_reg, val); break; case SND_SOC_DAPM_POST_PMD: - tavil_config_compander(codec, w->shift, event); - tavil_codec_enable_prim_interpolator(codec, reg, event); + tavil_codec_enable_interp_clk(codec, event, w->shift); + if ((tavil->swr.spkr_gain_offset == WCD934X_RX_GAIN_OFFSET_M1P5_DB) && (tavil->comp_enabled[COMPANDER_7] || @@ -4128,32 +4244,60 @@ static const char * const rx_int_dem_inp_mux_text[] = { "NORMAL_DSM_OUT", "CLSH_DSM_OUT", }; -static const char * const rx_int0_interp_mux_text[] = { - "ZERO", "RX INT0 MIX2", +static const char * const rx_int0_1_interp_mux_text[] = { + "ZERO", "RX INT0_1 MIX1", }; -static const char * const rx_int1_interp_mux_text[] = { - "ZERO", "RX INT1 MIX2", +static const char * const rx_int1_1_interp_mux_text[] = { + "ZERO", "RX INT1_1 MIX1", }; -static const char * const rx_int2_interp_mux_text[] = { - "ZERO", "RX INT2 MIX2", +static const char * const rx_int2_1_interp_mux_text[] = { + "ZERO", "RX INT2_1 MIX1", }; -static const char * const rx_int3_interp_mux_text[] = { - "ZERO", "RX INT3 MIX2", +static const char * const rx_int3_1_interp_mux_text[] = { + "ZERO", "RX INT3_1 MIX1", }; -static const char * const rx_int4_interp_mux_text[] = { - "ZERO", "RX INT4 MIX2", +static const char * const rx_int4_1_interp_mux_text[] = { + "ZERO", "RX INT4_1 MIX1", }; -static const char * const rx_int7_interp_mux_text[] = { - "ZERO", "RX INT7 MIX2", +static const char * const rx_int7_1_interp_mux_text[] = { + "ZERO", "RX INT7_1 MIX1", }; -static const char * const rx_int8_interp_mux_text[] = { - "ZERO", "RX INT8 SEC MIX" +static const char * const rx_int8_1_interp_mux_text[] = { + "ZERO", "RX INT8_1 MIX1", +}; + +static const char * const rx_int0_2_interp_mux_text[] = { + "ZERO", "RX INT0_2 MUX", +}; + +static const char * const rx_int1_2_interp_mux_text[] = { + "ZERO", "RX INT1_2 MUX", +}; + +static const char * const rx_int2_2_interp_mux_text[] = { + "ZERO", "RX INT2_2 MUX", +}; + +static const char * const rx_int3_2_interp_mux_text[] = { + "ZERO", "RX INT3_2 MUX", +}; + +static const char * const rx_int4_2_interp_mux_text[] = { + "ZERO", "RX INT4_2 MUX", +}; + +static const char * const rx_int7_2_interp_mux_text[] = { + "ZERO", "RX INT7_2 MUX", +}; + +static const char * const rx_int8_2_interp_mux_text[] = { + "ZERO", "RX INT8_2 MUX", }; static const char * const mad_sel_txt[] = { @@ -4214,6 +4358,26 @@ static const char *const cdc_if_rx7_mux_text[] = { "SLIM RX7", "I2S_0 RX7" }; +static const char * const asrc0_mux_text[] = { + "ZERO", "ASRC_IN_HPHL", "ASRC_IN_LO1", +}; + +static const char * const asrc1_mux_text[] = { + "ZERO", "ASRC_IN_HPHR", "ASRC_IN_LO2", +}; + +static const char * const asrc2_mux_text[] = { + "ZERO", "ASRC_IN_SPKR1", +}; + +static const char * const asrc3_mux_text[] = { + "ZERO", "ASRC_IN_SPKR2", +}; + +static const char * const native_mux_text[] = { + "OFF", "ON", +}; + static const struct snd_kcontrol_new aif4_vi_mixer[] = { SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, WCD934X_TX14, 1, 0, tavil_vi_feed_mixer_get, tavil_vi_feed_mixer_put), @@ -4539,20 +4703,21 @@ WCD_DAPM_ENUM(iir1_inp2, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0, WCD_DAPM_ENUM(iir1_inp3, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0, iir_inp_mux_text); -WCD_DAPM_ENUM(rx_int0_interp, WCD934X_CDC_RX0_RX_PATH_CTL, 5, - rx_int0_interp_mux_text); -WCD_DAPM_ENUM(rx_int1_interp, WCD934X_CDC_RX1_RX_PATH_CTL, 5, - rx_int1_interp_mux_text); -WCD_DAPM_ENUM(rx_int2_interp, WCD934X_CDC_RX2_RX_PATH_CTL, 5, - rx_int2_interp_mux_text); -WCD_DAPM_ENUM(rx_int3_interp, WCD934X_CDC_RX3_RX_PATH_CTL, 5, - rx_int3_interp_mux_text); -WCD_DAPM_ENUM(rx_int4_interp, WCD934X_CDC_RX4_RX_PATH_CTL, 5, - rx_int4_interp_mux_text); -WCD_DAPM_ENUM(rx_int7_interp, WCD934X_CDC_RX7_RX_PATH_CTL, 5, - rx_int7_interp_mux_text); -WCD_DAPM_ENUM(rx_int8_interp, WCD934X_CDC_RX8_RX_PATH_CTL, 5, - rx_int8_interp_mux_text); +WCD_DAPM_ENUM(rx_int0_1_interp, SND_SOC_NOPM, 0, rx_int0_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int1_1_interp, SND_SOC_NOPM, 0, rx_int1_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int2_1_interp, SND_SOC_NOPM, 0, rx_int2_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int3_1_interp, SND_SOC_NOPM, 0, rx_int3_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int4_1_interp, SND_SOC_NOPM, 0, rx_int4_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int7_1_interp, SND_SOC_NOPM, 0, rx_int7_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int8_1_interp, SND_SOC_NOPM, 0, rx_int8_1_interp_mux_text); + +WCD_DAPM_ENUM(rx_int0_2_interp, SND_SOC_NOPM, 0, rx_int0_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int1_2_interp, SND_SOC_NOPM, 0, rx_int1_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int2_2_interp, SND_SOC_NOPM, 0, rx_int2_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int3_2_interp, SND_SOC_NOPM, 0, rx_int3_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int4_2_interp, SND_SOC_NOPM, 0, rx_int4_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int7_2_interp, SND_SOC_NOPM, 0, rx_int7_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int8_2_interp, SND_SOC_NOPM, 0, rx_int8_2_interp_mux_text); WCD_DAPM_ENUM(mad_sel, WCD934X_CPE_SS_SVA_CFG, 0, mad_sel_txt); @@ -4589,6 +4754,27 @@ WCD_DAPM_ENUM_EXT(tx_adc_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 2, WCD_DAPM_ENUM_EXT(tx_adc_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 4, adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM(asrc0, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 0, + asrc0_mux_text); +WCD_DAPM_ENUM(asrc1, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 2, + asrc1_mux_text); +WCD_DAPM_ENUM(asrc2, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 4, + asrc2_mux_text); +WCD_DAPM_ENUM(asrc3, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 6, + asrc3_mux_text); + +WCD_DAPM_ENUM(int1_1_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int2_1_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int3_1_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int4_1_native, SND_SOC_NOPM, 0, native_mux_text); + +WCD_DAPM_ENUM(int1_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int2_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int3_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int4_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int7_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int8_2_native, SND_SOC_NOPM, 0, native_mux_text); + static const struct snd_kcontrol_new mad_cpe1_switch = SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); @@ -4622,6 +4808,22 @@ static const struct snd_kcontrol_new adc_us_mux7_switch = static const struct snd_kcontrol_new adc_us_mux8_switch = SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); +static const struct snd_kcontrol_new rx_int1_asrc_switch[] = { + SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int2_asrc_switch[] = { + SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int3_asrc_switch[] = { + SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int4_asrc_switch[] = { + SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0), +}; + 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, @@ -4663,27 +4865,34 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { WCD_DAPM_MUX("CDC_IF RX6 MUX", WCD934X_RX6, cdc_if_rx6), WCD_DAPM_MUX("CDC_IF RX7 MUX", WCD934X_RX7, cdc_if_rx7), - SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", WCD934X_CDC_RX0_RX_PATH_MIX_CTL, - 5, 0, &rx_int0_2_mux, tavil_codec_enable_mix_path, - SND_SOC_DAPM_POST_PMU), - SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", WCD934X_CDC_RX1_RX_PATH_MIX_CTL, - 5, 0, &rx_int1_2_mux, tavil_codec_enable_mix_path, - SND_SOC_DAPM_POST_PMU), - SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", WCD934X_CDC_RX2_RX_PATH_MIX_CTL, - 5, 0, &rx_int2_2_mux, tavil_codec_enable_mix_path, - SND_SOC_DAPM_POST_PMU), - SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", WCD934X_CDC_RX3_RX_PATH_MIX_CTL, - 5, 0, &rx_int3_2_mux, tavil_codec_enable_mix_path, - SND_SOC_DAPM_POST_PMU), - SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", WCD934X_CDC_RX4_RX_PATH_MIX_CTL, - 5, 0, &rx_int4_2_mux, tavil_codec_enable_mix_path, - SND_SOC_DAPM_POST_PMU), - SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", WCD934X_CDC_RX7_RX_PATH_MIX_CTL, - 5, 0, &rx_int7_2_mux, tavil_codec_enable_mix_path, - SND_SOC_DAPM_POST_PMU), - SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", WCD934X_CDC_RX8_RX_PATH_MIX_CTL, - 5, 0, &rx_int8_2_mux, tavil_codec_enable_mix_path, - SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_EAR, 0, + &rx_int0_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int1_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int2_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", SND_SOC_NOPM, INTERP_LO1, 0, + &rx_int3_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", SND_SOC_NOPM, INTERP_LO2, 0, + &rx_int4_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", SND_SOC_NOPM, INTERP_SPKR1, 0, + &rx_int7_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", SND_SOC_NOPM, INTERP_SPKR2, 0, + &rx_int8_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), WCD_DAPM_MUX("RX INT0_1 MIX1 INP0", 0, rx_int0_1_mix_inp0), WCD_DAPM_MUX("RX INT0_1 MIX1 INP1", 0, rx_int0_1_mix_inp1), @@ -4723,13 +4932,17 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int1_asrc_switch, ARRAY_SIZE(rx_int1_asrc_switch)), SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int2_asrc_switch, ARRAY_SIZE(rx_int2_asrc_switch)), SND_SOC_DAPM_MIXER("RX INT3_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int3_asrc_switch, ARRAY_SIZE(rx_int3_asrc_switch)), SND_SOC_DAPM_MIXER("RX INT4_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int4_asrc_switch, ARRAY_SIZE(rx_int4_asrc_switch)), SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -4995,35 +5208,43 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { WCD_DAPM_MUX("RX INT1 DEM MUX", 0, rx_int1_dem_inp), WCD_DAPM_MUX("RX INT2 DEM MUX", 0, rx_int2_dem_inp), - SND_SOC_DAPM_MUX_E("RX INT0 INTERP", SND_SOC_NOPM, INTERP_EAR, 0, - &rx_int0_interp_mux, tavil_codec_enable_interpolator, + SND_SOC_DAPM_MUX_E("RX INT0_1 INTERP", SND_SOC_NOPM, INTERP_EAR, 0, + &rx_int0_1_interp_mux, tavil_codec_enable_main_path, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MUX_E("RX INT1 INTERP", SND_SOC_NOPM, INTERP_HPHL, 0, - &rx_int1_interp_mux, tavil_codec_enable_interpolator, + SND_SOC_DAPM_MUX_E("RX INT1_1 INTERP", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int1_1_interp_mux, tavil_codec_enable_main_path, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MUX_E("RX INT2 INTERP", SND_SOC_NOPM, INTERP_HPHR, 0, - &rx_int2_interp_mux, tavil_codec_enable_interpolator, + SND_SOC_DAPM_MUX_E("RX INT2_1 INTERP", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int2_1_interp_mux, tavil_codec_enable_main_path, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MUX_E("RX INT3 INTERP", SND_SOC_NOPM, INTERP_LO1, 0, - &rx_int3_interp_mux, tavil_codec_enable_interpolator, + SND_SOC_DAPM_MUX_E("RX INT3_1 INTERP", SND_SOC_NOPM, INTERP_LO1, 0, + &rx_int3_1_interp_mux, tavil_codec_enable_main_path, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MUX_E("RX INT4 INTERP", SND_SOC_NOPM, INTERP_LO2, 0, - &rx_int4_interp_mux, tavil_codec_enable_interpolator, + SND_SOC_DAPM_MUX_E("RX INT4_1 INTERP", SND_SOC_NOPM, INTERP_LO2, 0, + &rx_int4_1_interp_mux, tavil_codec_enable_main_path, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MUX_E("RX INT7 INTERP", SND_SOC_NOPM, INTERP_SPKR1, 0, - &rx_int7_interp_mux, tavil_codec_enable_interpolator, + SND_SOC_DAPM_MUX_E("RX INT7_1 INTERP", SND_SOC_NOPM, INTERP_SPKR1, 0, + &rx_int7_1_interp_mux, tavil_codec_enable_main_path, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MUX_E("RX INT8 INTERP", SND_SOC_NOPM, INTERP_SPKR2, 0, - &rx_int8_interp_mux, tavil_codec_enable_interpolator, + SND_SOC_DAPM_MUX_E("RX INT8_1 INTERP", SND_SOC_NOPM, INTERP_SPKR2, 0, + &rx_int8_1_interp_mux, tavil_codec_enable_main_path, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + WCD_DAPM_MUX("RX INT0_2 INTERP", 0, rx_int0_2_interp), + WCD_DAPM_MUX("RX INT1_2 INTERP", 0, rx_int1_2_interp), + WCD_DAPM_MUX("RX INT2_2 INTERP", 0, rx_int2_2_interp), + WCD_DAPM_MUX("RX INT3_2 INTERP", 0, rx_int3_2_interp), + WCD_DAPM_MUX("RX INT4_2 INTERP", 0, rx_int4_2_interp), + WCD_DAPM_MUX("RX INT7_2 INTERP", 0, rx_int7_2_interp), + WCD_DAPM_MUX("RX INT8_2 INTERP", 0, rx_int8_2_interp), + SND_SOC_DAPM_SWITCH("ADC US MUX0", WCD934X_CDC_TX0_TX_PATH_192_CTL, 0, 0, &adc_us_mux0_switch), SND_SOC_DAPM_SWITCH("ADC US MUX1", WCD934X_CDC_TX1_TX_PATH_192_CTL, 0, @@ -5128,6 +5349,30 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { INTERP_SPKR2, 0, tavil_enable_native_supply, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + WCD_DAPM_MUX("RX INT1_1 NATIVE MUX", 0, int1_1_native), + WCD_DAPM_MUX("RX INT2_1 NATIVE MUX", 0, int2_1_native), + WCD_DAPM_MUX("RX INT3_1 NATIVE MUX", 0, int3_1_native), + WCD_DAPM_MUX("RX INT4_1 NATIVE MUX", 0, int4_1_native), + + WCD_DAPM_MUX("RX INT1_2 NATIVE MUX", 0, int1_2_native), + WCD_DAPM_MUX("RX INT2_2 NATIVE MUX", 0, int2_2_native), + WCD_DAPM_MUX("RX INT3_2 NATIVE MUX", 0, int3_2_native), + WCD_DAPM_MUX("RX INT4_2 NATIVE MUX", 0, int4_2_native), + WCD_DAPM_MUX("RX INT7_2 NATIVE MUX", 0, int7_2_native), + WCD_DAPM_MUX("RX INT8_2 NATIVE MUX", 0, int8_2_native), + + SND_SOC_DAPM_MUX_E("ASRC0 MUX", SND_SOC_NOPM, ASRC0, 0, + &asrc0_mux, tavil_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ASRC1 MUX", SND_SOC_NOPM, ASRC1, 0, + &asrc1_mux, tavil_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ASRC2 MUX", SND_SOC_NOPM, ASRC2, 0, + &asrc2_mux, tavil_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ASRC3 MUX", SND_SOC_NOPM, ASRC3, 0, + &asrc3_mux, tavil_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), }; static int tavil_get_channel_map(struct snd_soc_dai *dai, diff --git a/sound/soc/codecs/wcd934x/wcd934x.h b/sound/soc/codecs/wcd934x/wcd934x.h index 8bc2ded2763f..4690d344a1d6 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.h +++ b/sound/soc/codecs/wcd934x/wcd934x.h @@ -163,4 +163,6 @@ extern int tavil_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, int req_volt, int micb_num); 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); #endif From 10b823cd45fd688d25f5c82765cc5a90ea8208a7 Mon Sep 17 00:00:00 2001 From: Phani Kumar Uppalapati Date: Wed, 20 Jul 2016 16:43:15 -0700 Subject: [PATCH 5/6] 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 --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/wcd934x/Makefile | 2 + sound/soc/codecs/wcd934x/wcd934x-dsd.c | 557 +++++++++++++++++++++ sound/soc/codecs/wcd934x/wcd934x-dsd.h | 89 ++++ sound/soc/codecs/wcd934x/wcd934x-routing.h | 6 +- sound/soc/codecs/wcd934x/wcd934x.c | 197 +++++++- sound/soc/codecs/wcd934x/wcd934x.h | 17 +- 7 files changed, 852 insertions(+), 20 deletions(-) create mode 100644 sound/soc/codecs/wcd934x/wcd934x-dsd.c create mode 100644 sound/soc/codecs/wcd934x/wcd934x-dsd.h 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 +#include +#include +#include +#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 +#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 @@ -71,9 +72,22 @@ enum { WCD934X_TX_MAX, }; +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 From 68eef60c8f9764efe34ca888bdbf3dd203441181 Mon Sep 17 00:00:00 2001 From: Phani Kumar Uppalapati Date: Fri, 19 Aug 2016 00:13:15 -0700 Subject: [PATCH 6/6] ASoC: wcd934x: Add DSD volume support Add support for adjusting volume when DSD (Direct Stream Digital) audio playback is in progress. Change-Id: Ica51d40911d16059e8af21c60794b35c68bb695d Signed-off-by: Phani Kumar Uppalapati --- sound/soc/codecs/wcd934x/wcd934x-dsd.c | 126 ++++++++++++++++++++++++- sound/soc/codecs/wcd934x/wcd934x-dsd.h | 2 + 2 files changed, 126 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.c b/sound/soc/codecs/wcd934x/wcd934x-dsd.c index ef20c53eeb96..246b3bfab876 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsd.c +++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.c @@ -13,9 +13,24 @@ #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)) + +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" @@ -358,6 +373,7 @@ 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); + struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec); int rc, clk_users; int interp_idx; u8 pcm_rate_val; @@ -404,7 +420,7 @@ static int tavil_enable_dsd(struct snd_soc_dapm_widget *w, 0x01, 0x01); /* Apply Gain */ snd_soc_write(codec, WCD934X_CDC_DSD0_CFG1, - snd_soc_read(codec, WCD934X_CDC_DSD0_CFG1)); + dsd_conf->volume[DSD0]); if (clk_users > 1) snd_soc_update_bits(codec, @@ -419,7 +435,7 @@ static int tavil_enable_dsd(struct snd_soc_dapm_widget *w, 0x01, 0x01); /* Apply Gain */ snd_soc_write(codec, WCD934X_CDC_DSD1_CFG1, - snd_soc_read(codec, WCD934X_CDC_DSD1_CFG1)); + dsd_conf->volume[DSD1]); if (clk_users > 1) snd_soc_update_bits(codec, @@ -457,6 +473,103 @@ static int tavil_enable_dsd(struct snd_soc_dapm_widget *w, 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_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec); + 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(codec->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_write(codec, + WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx, + nv1); + /* 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_write(codec, + WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx, + nv[dsd_idx]); + + 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_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec); + + 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, @@ -528,6 +641,13 @@ struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec) 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_codec_controls(codec, tavil_dsd_vol_controls, + ARRAY_SIZE(tavil_dsd_vol_controls)); + /* Enable DSD Interrupts */ snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x00); @@ -549,6 +669,8 @@ void tavil_dsd_deinit(struct tavil_dsd_config *dsd_conf) codec = dsd_conf->codec; + mutex_destroy(&dsd_conf->vol_mutex); + /* Disable DSD Interrupts */ snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x08); diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.h b/sound/soc/codecs/wcd934x/wcd934x-dsd.h index 884e41d797d9..c033795beb9b 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsd.h +++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.h @@ -38,6 +38,8 @@ struct tavil_dsd_config { struct snd_soc_codec *codec; unsigned int dsd_interp_mixer[INTERP_MAX]; u32 base_sample_rate[DSD_MAX]; + int volume[DSD_MAX]; + struct mutex vol_mutex; }; #ifdef CONFIG_SND_SOC_WCD934X_DSD