[PATCH 4/7] ASoC: codecs: wcd938x: add basic controls
From: Srinivas Kandagatla
Date: Thu Mar 11 2021 - 12:36:04 EST
This patch adds basic controls found in wcd938x codec.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@xxxxxxxxxx>
---
sound/soc/codecs/wcd938x.c | 439 +++++++++++++++++++++++++++++++++++++
1 file changed, 439 insertions(+)
diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c
index b82dd0dc15e8..92b2a8e6bb6c 100644
--- a/sound/soc/codecs/wcd938x.c
+++ b/sound/soc/codecs/wcd938x.c
@@ -188,6 +188,8 @@ enum {
};
static struct wcd938x_priv *g_wcd938x;
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
static const struct reg_default wcd938x_defaults[] = {
{WCD938X_ANA_PAGE_REGISTER, 0x00},
@@ -1273,6 +1275,418 @@ static int wcd938x_io_init(struct wcd938x_sdw_priv *wcd)
}
+static int wcd938x_sdw_connect_port(struct wcd938x_sdw_ch_info *ch_info,
+ struct sdw_port_config *port_config,
+ u32 mstr_port_num,
+ u8 enable)
+{
+ u8 ch_mask, port_num;
+
+ port_num = ch_info->port_num;
+ ch_mask = ch_info->ch_mask;
+
+ port_config->num = port_num;
+
+ if (enable)
+ port_config->ch_mask |= ch_mask;
+ else
+ port_config->ch_mask &= ~ch_mask;
+
+ return 0;
+}
+
+static int wcd938x_connect_port(struct snd_soc_component *component, u8 ch_id, u8 enable)
+{
+ struct wcd938x_sdw_priv *wcd = snd_soc_component_get_drvdata(component);
+ u8 port_num, mstr_port_num;
+
+ port_num = wcd->ch_info[ch_id].port_num;
+ mstr_port_num = wcd->port_map[port_num - 1];
+
+ return wcd938x_sdw_connect_port(&wcd->ch_info[ch_id],
+ &wcd->port_config[port_num],
+ mstr_port_num,
+ enable);
+}
+
+static int wcd938x_tx_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wcd938x_sdw_priv *wcd = snd_soc_component_get_drvdata(component);
+ struct wcd938x_priv *wcd938x = wcd->wcd938x;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int path = e->shift_l;
+
+ ucontrol->value.integer.value[0] = wcd938x->tx_mode[path];
+
+ return 0;
+}
+
+static int wcd938x_tx_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wcd938x_sdw_priv *wcd = snd_soc_component_get_drvdata(component);
+ struct wcd938x_priv *wcd938x = wcd->wcd938x;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int path = e->shift_l;
+
+ wcd938x->tx_mode[path] = ucontrol->value.enumerated.item[0];
+
+ return 0;
+}
+
+static int wcd938x_rx_hph_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wcd938x_sdw_priv *wcd = snd_soc_component_get_drvdata(component);
+ struct wcd938x_priv *wcd938x = wcd->wcd938x;
+
+ ucontrol->value.integer.value[0] = wcd938x->hph_mode;
+
+ return 0;
+}
+
+static int wcd938x_rx_hph_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wcd938x_sdw_priv *wcd = snd_soc_component_get_drvdata(component);
+ struct wcd938x_priv *wcd938x = wcd->wcd938x;
+ u32 mode_val;
+
+ mode_val = ucontrol->value.enumerated.item[0];
+
+ if (wcd938x->variant == WCD9380) {
+ if (mode_val == CLS_H_HIFI || mode_val == CLS_AB_HIFI) {
+ dev_info(component->dev,
+ "%s:Invalid HPH Mode, default to CLS_H_ULP\n",
+ __func__);
+ mode_val = CLS_H_ULP;
+ }
+ }
+
+ if (mode_val == CLS_H_NORMAL) {
+ dev_info(component->dev,
+ "%s:Invalid HPH Mode, default to class_AB\n",
+ __func__);
+ mode_val = CLS_H_ULP;
+ }
+ wcd938x->hph_mode = mode_val;
+
+ return 0;
+}
+
+static int wcd938x_ear_pa_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+
+ ucontrol->value.integer.value[0] = snd_soc_component_read_field(component,
+ WCD938X_ANA_EAR_COMPANDER_CTL,
+ WCD938X_EAR_GAIN_MASK);
+
+ return 0;
+}
+
+static int wcd938x_ear_pa_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wcd938x_sdw_priv *wcd = snd_soc_component_get_drvdata(component);
+ struct wcd938x_priv *wcd938x = wcd->wcd938x;
+
+ if (!wcd938x->comp1_enable) {
+ snd_soc_component_write_field(component,
+ WCD938X_ANA_EAR_COMPANDER_CTL,
+ WCD938X_EAR_GAIN_MASK,
+ ucontrol->value.integer.value[0]);
+ }
+
+ return 0;
+}
+
+static int wcd938x_get_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wcd938x_sdw_priv *wcd = snd_soc_component_get_drvdata(component);
+ struct wcd938x_priv *wcd938x = wcd->wcd938x;
+ struct soc_mixer_control *mc;
+ bool hphr;
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+
+ if (hphr)
+ ucontrol->value.integer.value[0] = wcd938x->comp2_enable;
+ else
+ ucontrol->value.integer.value[0] = wcd938x->comp1_enable;
+
+ return 0;
+}
+
+static int wcd938x_set_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wcd938x_sdw_priv *wcd = snd_soc_component_get_drvdata(component);
+ struct wcd938x_priv *wcd938x = wcd->wcd938x;
+ int value = ucontrol->value.integer.value[0];
+ struct soc_mixer_control *mc;
+ bool hphr;
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+
+ if (hphr)
+ wcd938x->comp2_enable = value;
+ else
+ wcd938x->comp1_enable = value;
+
+ if (value)
+ wcd938x_connect_port(component, mc->reg, true);
+ else
+ wcd938x_connect_port(component, mc->reg, false);
+
+ return 0;
+}
+
+static int wcd938x_ldoh_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wcd938x_sdw_priv *wcd = snd_soc_component_get_drvdata(component);
+ struct wcd938x_priv *wcd938x = wcd->wcd938x;
+
+ ucontrol->value.integer.value[0] = wcd938x->ldoh;
+
+ return 0;
+}
+
+static int wcd938x_ldoh_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wcd938x_sdw_priv *wcd = snd_soc_component_get_drvdata(component);
+ struct wcd938x_priv *wcd938x = wcd->wcd938x;
+
+ wcd938x->ldoh = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int wcd938x_bcs_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wcd938x_sdw_priv *wcd = snd_soc_component_get_drvdata(component);
+ struct wcd938x_priv *wcd938x = wcd->wcd938x;
+
+ ucontrol->value.integer.value[0] = wcd938x->bcs_dis;
+
+ return 0;
+}
+
+static int wcd938x_bcs_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wcd938x_sdw_priv *wcd = snd_soc_component_get_drvdata(component);
+ struct wcd938x_priv *wcd938x = wcd->wcd938x;
+
+ wcd938x->bcs_dis = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static const char * const tx_mode_mux_text_wcd9380[] = {
+ "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP",
+};
+
+static const char * const tx_mode_mux_text[] = {
+ "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP",
+ "ADC_ULP1", "ADC_ULP2",
+};
+
+static const char * const rx_hph_mode_mux_text_wcd9380[] = {
+ "CLS_H_INVALID", "CLS_H_INVALID_1", "CLS_H_LP", "CLS_AB",
+ "CLS_H_LOHIFI", "CLS_H_ULP", "CLS_H_INVALID_2", "CLS_AB_LP",
+ "CLS_AB_LOHIFI",
+};
+
+static const char * const wcd938x_ear_pa_gain_text[] = {
+ "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB", "G_0_DB",
+ "G_M1P5_DB", "G_M3_DB", "G_M4P5_DB",
+ "G_M6_DB", "G_7P5_DB", "G_M9_DB",
+ "G_M10P5_DB", "G_M12_DB", "G_M13P5_DB",
+ "G_M15_DB", "G_M16P5_DB", "G_M18_DB",
+};
+
+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", "CLS_AB_LP", "CLS_AB_LOHIFI",
+};
+
+static const struct soc_enum tx_mode_mux_enum_wcd9380[] = {
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text_wcd9380),
+ tx_mode_mux_text_wcd9380),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text_wcd9380),
+ tx_mode_mux_text_wcd9380),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text_wcd9380),
+ tx_mode_mux_text_wcd9380),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text_wcd9380),
+ tx_mode_mux_text_wcd9380),
+};
+
+static const struct soc_enum tx_mode_mux_enum[] = {
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text),
+ tx_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text),
+ tx_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text),
+ tx_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text),
+ tx_mode_mux_text),
+};
+
+static const struct soc_enum rx_hph_mode_mux_enum_wcd9380 =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text_wcd9380),
+ rx_hph_mode_mux_text_wcd9380);
+
+static const struct soc_enum rx_hph_mode_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text),
+ rx_hph_mode_mux_text);
+
+static SOC_ENUM_SINGLE_EXT_DECL(wcd938x_ear_pa_gain_enum, wcd938x_ear_pa_gain_text);
+
+static const struct snd_kcontrol_new wcd9380_snd_controls[] = {
+ SOC_ENUM_EXT("EAR PA GAIN", wcd938x_ear_pa_gain_enum,
+ wcd938x_ear_pa_gain_get, wcd938x_ear_pa_gain_put),
+ SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum_wcd9380,
+ wcd938x_rx_hph_mode_get, wcd938x_rx_hph_mode_put),
+ SOC_ENUM_EXT("TX0 MODE", tx_mode_mux_enum_wcd9380[0],
+ wcd938x_tx_mode_get, wcd938x_tx_mode_put),
+ SOC_ENUM_EXT("TX1 MODE", tx_mode_mux_enum_wcd9380[1],
+ wcd938x_tx_mode_get, wcd938x_tx_mode_put),
+ SOC_ENUM_EXT("TX2 MODE", tx_mode_mux_enum_wcd9380[2],
+ wcd938x_tx_mode_get, wcd938x_tx_mode_put),
+ SOC_ENUM_EXT("TX3 MODE", tx_mode_mux_enum_wcd9380[3],
+ wcd938x_tx_mode_get, wcd938x_tx_mode_put),
+};
+
+static const struct snd_kcontrol_new wcd9385_snd_controls[] = {
+ SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum,
+ wcd938x_rx_hph_mode_get, wcd938x_rx_hph_mode_put),
+ SOC_ENUM_EXT("TX0 MODE", tx_mode_mux_enum[0],
+ wcd938x_tx_mode_get, wcd938x_tx_mode_put),
+ SOC_ENUM_EXT("TX1 MODE", tx_mode_mux_enum[1],
+ wcd938x_tx_mode_get, wcd938x_tx_mode_put),
+ SOC_ENUM_EXT("TX2 MODE", tx_mode_mux_enum[2],
+ wcd938x_tx_mode_get, wcd938x_tx_mode_put),
+ SOC_ENUM_EXT("TX3 MODE", tx_mode_mux_enum[3],
+ wcd938x_tx_mode_get, wcd938x_tx_mode_put),
+};
+
+static int wcd938x_get_swr_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct wcd938x_sdw_priv *wcd = snd_soc_component_get_drvdata(comp);
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ int portidx = mixer->reg;
+
+ ucontrol->value.integer.value[0] = wcd->port_enable[portidx];
+
+ return 0;
+}
+
+static int wcd938x_set_swr_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct wcd938x_sdw_priv *wcd = snd_soc_component_get_drvdata(comp);
+ struct soc_mixer_control *mixer =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int portidx = mixer->reg;
+ bool enable;
+
+ if (ucontrol->value.integer.value[0])
+ enable = true;
+ else
+ enable = false;
+
+ wcd->port_enable[portidx] = enable;
+
+ wcd938x_connect_port(comp, portidx, enable);
+
+ return 0;
+
+}
+
+static const struct snd_kcontrol_new wcd938x_rx_snd_controls[] = {
+ SOC_SINGLE_EXT("HPHL_COMP Switch", WCD938X_COMP_L, 0, 1, 0,
+ wcd938x_get_compander, wcd938x_set_compander),
+ SOC_SINGLE_EXT("HPHR_COMP Switch", WCD938X_COMP_R, 1, 1, 0,
+ wcd938x_get_compander, wcd938x_set_compander),
+ SOC_SINGLE_EXT("HPHL Switch", WCD938X_HPH_L, 0, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("HPHR Switch", WCD938X_HPH_R, 0, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("CLSH Switch", WCD938X_CLSH, 0, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("LO Switch", WCD938X_LO, 0, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DSD_L Switch", WCD938X_DSD_L, 0, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DSD_R Switch", WCD938X_DSD_R, 0, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_TLV("HPHL Volume", WCD938X_HPH_L_EN, 0, 20, 1, line_gain),
+ SOC_SINGLE_TLV("HPHR Volume", WCD938X_HPH_R_EN, 0, 20, 1, line_gain),
+};
+
+static const struct snd_kcontrol_new wcd938x_snd_controls[] = {
+
+ SOC_SINGLE_EXT("ADC1 Switch", WCD938X_ADC1, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("ADC2 Switch", WCD938X_ADC2, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("ADC3 Switch", WCD938X_ADC3, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("ADC4 Switch", WCD938X_ADC4, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC0 Switch", WCD938X_DMIC0, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC1 Switch", WCD938X_DMIC1, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("MBHC Switch", WCD938X_MBHC, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC2 Switch", WCD938X_DMIC2, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC3 Switch", WCD938X_DMIC3, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC4 Switch", WCD938X_DMIC4, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC5 Switch", WCD938X_DMIC5, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC6 Switch", WCD938X_DMIC6, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC7 Switch", WCD938X_DMIC7, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("LDOH Enable Switch", SND_SOC_NOPM, 0, 1, 0,
+ wcd938x_ldoh_get, wcd938x_ldoh_put),
+ SOC_SINGLE_EXT("ADC2_BCS Disable Switch", SND_SOC_NOPM, 0, 1, 0,
+ wcd938x_bcs_get, wcd938x_bcs_put),
+
+ SOC_SINGLE_TLV("ADC1 Volume", WCD938X_ANA_TX_CH1, 0, 20, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", WCD938X_ANA_TX_CH2, 0, 20, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC3 Volume", WCD938X_ANA_TX_CH3, 0, 20, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC4 Volume", WCD938X_ANA_TX_CH4, 0, 20, 0, analog_gain),
+};
+
static int wcd938x_get_micb_vout_ctl_val(u32 micb_mv)
{
/* min micbias voltage is 1V and maximum is 2.85V */
@@ -1418,17 +1832,42 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
wcd938x_io_init(wcd);
+ if (wcd938x->variant == WCD9380) {
+ ret = snd_soc_add_component_controls(component, wcd9380_snd_controls,
+ ARRAY_SIZE(wcd9380_snd_controls));
+ if (ret < 0) {
+ dev_err(component->dev,
+ "%s: Failed to add snd ctrls for variant: %d\n",
+ __func__, wcd938x->variant);
+ goto err;
+ }
+ }
+ if (wcd938x->variant == WCD9385) {
+ ret = snd_soc_add_component_controls(component, wcd9385_snd_controls,
+ ARRAY_SIZE(wcd9385_snd_controls));
+ if (ret < 0) {
+ dev_err(component->dev,
+ "%s: Failed to add snd ctrls for variant: %d\n",
+ __func__, wcd938x->variant);
+ goto err;
+ }
+ }
+err:
return ret;
}
static const struct snd_soc_component_driver soc_codec_dev_wcd938x_sdw_rx = {
.name = "wcd938x_codec_rx",
.probe = wcd938x_soc_codec_rx_probe,
+ .controls = wcd938x_rx_snd_controls,
+ .num_controls = ARRAY_SIZE(wcd938x_rx_snd_controls),
};
static const struct snd_soc_component_driver soc_codec_dev_wcd938x_sdw_tx = {
.name = "wcd938x_codec_tx",
.probe = wcd938x_soc_codec_probe,
+ .controls = wcd938x_snd_controls,
+ .num_controls = ARRAY_SIZE(wcd938x_snd_controls),
};
static void wcd938x_dt_parse_micbias_info(struct device *dev, struct wcd938x_priv *wcd)
--
2.21.0