diff --git a/asoc/codecs/wcd938x/internal.h b/asoc/codecs/wcd938x/internal.h index ea1ff234d0..e1fea5c41a 100644 --- a/asoc/codecs/wcd938x/internal.h +++ b/asoc/codecs/wcd938x/internal.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */ #ifndef _WCD938X_INTERNAL_H @@ -17,6 +17,7 @@ #define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50) #define MAX_PORT 8 #define MAX_CH_PER_PORT 8 +#define TX_ADC_MAX 4 enum { TX_HDR12 = 0, @@ -63,6 +64,7 @@ struct wcd938x_priv { struct wcd938x_mbhc *mbhc; u32 hph_mode; + u32 tx_mode[TX_ADC_MAX]; bool comp1_enable; bool comp2_enable; diff --git a/asoc/codecs/wcd938x/wcd938x.c b/asoc/codecs/wcd938x/wcd938x.c index 87668313bd..74c206430d 100644 --- a/asoc/codecs/wcd938x/wcd938x.c +++ b/asoc/codecs/wcd938x/wcd938x.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */ #include @@ -30,6 +30,27 @@ #define WCD938X_VERSION_1_0 1 #define WCD938X_VERSION_ENTRY_SIZE 32 +#define ADC_MODE_VAL_HIFI 0x01 +#define ADC_MODE_VAL_LO_HIF 0x02 +#define ADC_MODE_VAL_NORMAL 0x03 +#define ADC_MODE_VAL_LP 0x05 +#define ADC_MODE_VAL_ULP1 0x09 +#define ADC_MODE_VAL_ULP2 0x0B + +#define STRING(name) #name +#define WCD_DAPM_ENUM(name, reg, offset, text) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM(STRING(name), name##_enum) + +#define WCD_DAPM_ENUM_EXT(name, reg, offset, text, getname, putname) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM_EXT(STRING(name), name##_enum, getname, putname) + +#define WCD_DAPM_MUX(name, shift, kctl) \ + SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, shift, 0, &kctl##_mux) + enum { WCD9380 = 0, WCD9385, @@ -47,6 +68,16 @@ enum { HPH_PA_DELAY, }; +enum { + ADC_MODE_INVALID = 0, + ADC_MODE_HIFI, + ADC_MODE_LO_HIF, + ADC_MODE_NORMAL, + ADC_MODE_LP, + ADC_MODE_ULP1, + ADC_MODE_ULP2, +}; + static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); @@ -311,8 +342,6 @@ static int wcd938x_rx_clk_enable(struct snd_soc_component *component) struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); if (wcd938x->rx_clk_cnt == 0) { - snd_soc_component_update_bits(component, - WCD938X_DIGITAL_CDC_DIG_CLK_CTL, 0x08, 0x08); snd_soc_component_update_bits(component, WCD938X_DIGITAL_CDC_ANA_CLK_CTL, 0x01, 0x01); snd_soc_component_update_bits(component, @@ -945,17 +974,22 @@ static int wcd938x_codec_enable_dmic(struct snd_soc_dapm_widget *w, case 0: case 1: dmic_clk_cnt = &(wcd938x->dmic_0_1_clk_cnt); - dmic_clk_reg = WCD938X_DIGITAL_CDC_DMIC_CTL; + dmic_clk_reg = WCD938X_DIGITAL_CDC_DMIC1_CTL; break; case 2: case 3: dmic_clk_cnt = &(wcd938x->dmic_2_3_clk_cnt); - dmic_clk_reg = WCD938X_DIGITAL_CDC_DMIC1_CTL; + dmic_clk_reg = WCD938X_DIGITAL_CDC_DMIC2_CTL; break; case 4: case 5: dmic_clk_cnt = &(wcd938x->dmic_4_5_clk_cnt); - dmic_clk_reg = WCD938X_DIGITAL_CDC_DMIC2_CTL; + dmic_clk_reg = WCD938X_DIGITAL_CDC_DMIC3_CTL; + break; + case 6: + case 7: + dmic_clk_cnt = &(wcd938x->dmic_6_7_clk_cnt); + dmic_clk_reg = WCD938X_DIGITAL_CDC_DMIC4_CTL; break; default: dev_err(component->dev, "%s: Invalid DMIC Selection\n", @@ -969,6 +1003,9 @@ static int wcd938x_codec_enable_dmic(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_PRE_PMU: snd_soc_component_update_bits(component, WCD938X_DIGITAL_CDC_DIG_CLK_CTL, 0x80, 0x80); + /* enable clock scaling */ + snd_soc_component_update_bits(component, + WCD938X_DIGITAL_CDC_DMIC_CTL, 0x06, 0x06); snd_soc_component_update_bits(component, dmic_clk_reg, 0x07, 0x02); snd_soc_component_update_bits(component, @@ -1107,24 +1144,90 @@ static int wcd938x_tx_swr_ctrl(struct snd_soc_dapm_widget *w, return ret; } +static int wcd938x_get_adc_mode(int val) +{ + int ret = 0; + + switch (val) { + case ADC_MODE_INVALID: + ret = ADC_MODE_VAL_NORMAL; + break; + case ADC_MODE_HIFI: + ret = ADC_MODE_VAL_HIFI; + break; + case ADC_MODE_LO_HIF: + ret = ADC_MODE_VAL_LO_HIF; + break; + case ADC_MODE_NORMAL: + ret = ADC_MODE_VAL_NORMAL; + break; + case ADC_MODE_LP: + ret = ADC_MODE_VAL_LP; + break; + case ADC_MODE_ULP1: + ret = ADC_MODE_VAL_ULP1; + break; + case ADC_MODE_ULP2: + ret = ADC_MODE_VAL_ULP2; + break; + default: + ret = -EINVAL; + pr_err("%s: invalid ADC mode value %d\n", __func__, val); + break; + } + return ret; +} + static int wcd938x_codec_enable_adc(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event){ - + int mode; struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, w->name, event); switch (event) { case SND_SOC_DAPM_PRE_PMU: + mode = wcd938x_get_adc_mode(wcd938x->tx_mode[w->shift]); + if (mode < 0) { + dev_info(component->dev, + "%s: invalid mode, setting to normal mode\n", + __func__); + mode = ADC_MODE_VAL_NORMAL; + } snd_soc_component_update_bits(component, WCD938X_DIGITAL_CDC_DIG_CLK_CTL, 0x80, 0x80); snd_soc_component_update_bits(component, WCD938X_DIGITAL_CDC_ANA_CLK_CTL, 0x08, 0x08); snd_soc_component_update_bits(component, WCD938X_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x10); + switch (w->shift) { + case 0: + snd_soc_component_update_bits(component, + WCD938X_DIGITAL_CDC_TX_ANA_MODE_0_1, 0x0F, + mode); + break; + case 1: + snd_soc_component_update_bits(component, + WCD938X_DIGITAL_CDC_TX_ANA_MODE_0_1, 0xF0, + mode << 4); + break; + case 2: + snd_soc_component_update_bits(component, + WCD938X_DIGITAL_CDC_TX_ANA_MODE_2_3, 0x0F, + mode); + break; + case 3: + snd_soc_component_update_bits(component, + WCD938X_DIGITAL_CDC_TX_ANA_MODE_2_3, 0xF0, + mode << 4); + break; + default: + break; + } wcd938x_tx_connect_port(component, ADC1 + (w->shift), true); break; case SND_SOC_DAPM_POST_PMD: @@ -1433,6 +1536,37 @@ static int wcd938x_codec_enable_micbias(struct snd_soc_dapm_widget *w, return __wcd938x_codec_enable_micbias(w, event); } +static int wcd938x_tx_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd938x->tx_mode[widget->shift]; + return 0; +} + +static int wcd938x_tx_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(component->dev, "%s: mode: %d\n", __func__, mode_val); + + wcd938x->tx_mode[widget->shift] = mode_val; + return 0; +} + static int wcd938x_rx_hph_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1605,6 +1739,20 @@ static int wcd938x_tx_hdr_put(struct snd_kcontrol *kcontrol, return 0; } +static const char * const tx_mode_mux_text[] = { + "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP", + "ADC_ULP1", "ADC_ULP2", +}; + +WCD_DAPM_ENUM_EXT(tx0_mode, SND_SOC_NOPM, 0, tx_mode_mux_text, + wcd938x_tx_mode_get, wcd938x_tx_mode_put); +WCD_DAPM_ENUM_EXT(tx1_mode, SND_SOC_NOPM, 1, tx_mode_mux_text, + wcd938x_tx_mode_get, wcd938x_tx_mode_put); +WCD_DAPM_ENUM_EXT(tx2_mode, SND_SOC_NOPM, 2, tx_mode_mux_text, + wcd938x_tx_mode_get, wcd938x_tx_mode_put); +WCD_DAPM_ENUM_EXT(tx3_mode, SND_SOC_NOPM, 3, tx_mode_mux_text, + wcd938x_tx_mode_get, wcd938x_tx_mode_put); + static const char * const rx_hph_mode_mux_text[] = { "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI", "CLS_H_ULP", "CLS_AB_HIFI", @@ -1821,6 +1969,11 @@ static const struct snd_soc_dapm_widget wcd938x_dapm_widgets[] = { SND_SOC_DAPM_MUX("ADC4 MUX", SND_SOC_NOPM, 0, 0, &tx_adc4_mux), + WCD_DAPM_MUX("TX0 MODE", 0, tx0_mode), + WCD_DAPM_MUX("TX1 MODE", 1, tx1_mode), + WCD_DAPM_MUX("TX2 MODE", 2, tx2_mode), + WCD_DAPM_MUX("TX3 MODE", 3, tx3_mode), + /*tx mixers*/ SND_SOC_DAPM_MIXER_E("ADC1_MIXER", SND_SOC_NOPM, 0, 0, adc1_switch, ARRAY_SIZE(adc1_switch),