[PATCH v4 5/8] ASoC: qcom: lpass-va-macro: Add SoundWire microphone support

From: Mohammad Rafi Shaik

Date: Tue Sep 22 2026 - 05:48:16 EST


The VA decimators can currently capture audio only from DMIC inputs.
Add support for routing audio from SoundWire microphone (SMIC) inputs
as well.

Update the decimator source selection logic to switch between DMIC and
SMIC sources based on the active input path.

The ADC_DMIC_SEL bit of CDC_VA_TXn_TX_PATH_CFG0 is shared by the DMIC and
the SoundWire mic mux, so a mixer write to the mux that is not part of the
active path can leave it stale. Program it from the resolved mux state
when the decimator is enabled, so capture does not depend on the order in
which userspace restores the two controls.

Add a helper to detect whether a decimator is using a SoundWire microphone
input. Use this information to skip DMIC-specific HPF programming sequences
when the decimator is configured for an SMIC source.

This enables VA decimators to capture audio from either DMIC or SoundWire
microphone inputs.

Signed-off-by: Mohammad Rafi Shaik <mohammad.rafi.shaik@xxxxxxxxxxxxxxxx>
---
sound/soc/codecs/lpass-va-macro.c | 144 +++++++++++++++++++++++++++++++++++---
1 file changed, 135 insertions(+), 9 deletions(-)

diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c
index 02dc3346924f..79abbdd158b7 100644
--- a/sound/soc/codecs/lpass-va-macro.c
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -794,10 +794,22 @@ static int va_macro_put_dec_enum(struct snd_kcontrol *kcontrol,
return -EINVAL;
}

- if (val != 0)
- snd_soc_component_update_bits(component, mic_sel_reg,
- CDC_VA_TX_PATH_ADC_DMIC_SEL_MASK,
- CDC_VA_TX_PATH_ADC_DMIC_SEL_DMIC);
+ if (val != 0) {
+ /*
+ * SMIC enums use shift 0 (lower nibble of CFG0),
+ * DMIC enums use shift 4 (upper nibble of CFG0).
+ * Use e->shift_l to distinguish them without relying
+ * on widget name comparisons.
+ */
+ if (e->shift_l == 0)
+ snd_soc_component_update_bits(component, mic_sel_reg,
+ CDC_VA_TX_PATH_ADC_DMIC_SEL_MASK,
+ CDC_VA_TX_PATH_ADC_DMIC_SEL_ADC);
+ else
+ snd_soc_component_update_bits(component, mic_sel_reg,
+ CDC_VA_TX_PATH_ADC_DMIC_SEL_MASK,
+ CDC_VA_TX_PATH_ADC_DMIC_SEL_DMIC);
+ }

return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
}
@@ -978,6 +990,16 @@ static int va_macro_enable_dmic(struct snd_soc_dapm_widget *w,
return 0;
}

+static bool is_amic_enabled(struct snd_soc_component *comp, int decimator)
+{
+ u16 adc_mux_reg;
+
+ adc_mux_reg = CDC_VA_INP_MUX_ADC_MUX0_CFG1 +
+ VA_MACRO_ADC_MUX_CFG_OFFSET * decimator;
+
+ return snd_soc_component_read(comp, adc_mux_reg) & BIT(0);
+}
+
static int va_macro_enable_dec(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -987,7 +1009,6 @@ static int va_macro_enable_dec(struct snd_soc_dapm_widget *w,
u16 tx_gain_ctl_reg;
u16 adpt_ctrl_reg;
u8 hpf_cut_off_freq;
-
struct va_macro *va = snd_soc_component_get_drvdata(comp);

decimator = w->shift;
@@ -1004,6 +1025,17 @@ static int va_macro_enable_dec(struct snd_soc_dapm_widget *w,

switch (event) {
case SND_SOC_DAPM_PRE_PMU:
+ /*
+ * ADC_DMIC_SEL is shared by the DMIC and the SoundWire mic
+ * mux, so a mixer write to the mux that is not part of the
+ * active path can leave it stale. Program it from the mux
+ * state DAPM resolved for this decimator.
+ */
+ snd_soc_component_update_bits(comp, dec_cfg_reg,
+ CDC_VA_TX_PATH_ADC_DMIC_SEL_MASK,
+ is_amic_enabled(comp, decimator) ?
+ CDC_VA_TX_PATH_ADC_DMIC_SEL_ADC :
+ CDC_VA_TX_PATH_ADC_DMIC_SEL_DMIC);
snd_soc_component_update_bits(comp,
dec_cfg_reg, CDC_VA_ADC_MODE_MASK,
va->dec_mode[decimator] << CDC_VA_ADC_MODE_SHIFT);
@@ -1018,9 +1050,10 @@ static int va_macro_enable_dec(struct snd_soc_dapm_widget *w,
snd_soc_component_update_bits(comp, tx_vol_ctl_reg,
CDC_VA_TX_PATH_CLK_EN_MASK,
CDC_VA_TX_PATH_CLK_EN);
- snd_soc_component_update_bits(comp, hpf_gate_reg,
- CDC_VA_TX_HPF_ZERO_GATE_MASK,
- CDC_VA_TX_HPF_ZERO_GATE);
+ if (!is_amic_enabled(comp, decimator))
+ snd_soc_component_update_bits(comp, hpf_gate_reg,
+ CDC_VA_TX_HPF_ZERO_GATE_MASK,
+ CDC_VA_TX_HPF_ZERO_GATE);

usleep_range(1000, 1010);
hpf_cut_off_freq = (snd_soc_component_read(comp, dec_cfg_reg) &
@@ -1046,7 +1079,6 @@ static int va_macro_enable_dec(struct snd_soc_dapm_widget *w,
0x0);
}

-
usleep_range(1000, 1010);
snd_soc_component_update_bits(comp, hpf_gate_reg,
CDC_VA_TX_HPF_ZERO_GATE_MASK,
@@ -1246,6 +1278,39 @@ static const char * const adc_mux_text[] = {
"VA_DMIC", "SWR_MIC"
};

+static const char * const smic_mux_text[] = {
+ "ZERO", "ADC0", "ADC1", "ADC2", "ADC3", "SWR_MIC0", "SWR_MIC1",
+ "SWR_MIC2", "SWR_MIC3", "SWR_MIC4", "SWR_MIC5", "SWR_MIC6", "SWR_MIC7"
+};
+
+static SOC_ENUM_SINGLE_DECL(va_smic0_enum, CDC_VA_INP_MUX_ADC_MUX0_CFG0,
+ 0, smic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(va_smic1_enum, CDC_VA_INP_MUX_ADC_MUX1_CFG0,
+ 0, smic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(va_smic2_enum, CDC_VA_INP_MUX_ADC_MUX2_CFG0,
+ 0, smic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(va_smic3_enum, CDC_VA_INP_MUX_ADC_MUX3_CFG0,
+ 0, smic_mux_text);
+
+static const struct snd_kcontrol_new va_smic0_mux = SOC_DAPM_ENUM_EXT("va_smic0",
+ va_smic0_enum, snd_soc_dapm_get_enum_double,
+ va_macro_put_dec_enum);
+
+static const struct snd_kcontrol_new va_smic1_mux = SOC_DAPM_ENUM_EXT("va_smic1",
+ va_smic1_enum, snd_soc_dapm_get_enum_double,
+ va_macro_put_dec_enum);
+
+static const struct snd_kcontrol_new va_smic2_mux = SOC_DAPM_ENUM_EXT("va_smic2",
+ va_smic2_enum, snd_soc_dapm_get_enum_double,
+ va_macro_put_dec_enum);
+
+static const struct snd_kcontrol_new va_smic3_mux = SOC_DAPM_ENUM_EXT("va_smic3",
+ va_smic3_enum, snd_soc_dapm_get_enum_double,
+ va_macro_put_dec_enum);
+
static SOC_ENUM_SINGLE_DECL(va_dec0_enum, CDC_VA_INP_MUX_ADC_MUX0_CFG1,
0, adc_mux_text);
static SOC_ENUM_SINGLE_DECL(va_dec1_enum, CDC_VA_INP_MUX_ADC_MUX1_CFG1,
@@ -1381,6 +1446,11 @@ static const struct snd_soc_dapm_widget va_macro_dapm_widgets[] = {
SND_SOC_DAPM_MUX("VA DMIC MUX2", SND_SOC_NOPM, 0, 0, &va_dmic2_mux),
SND_SOC_DAPM_MUX("VA DMIC MUX3", SND_SOC_NOPM, 0, 0, &va_dmic3_mux),

+ SND_SOC_DAPM_MUX("VA SMIC MUX0", SND_SOC_NOPM, 0, 0, &va_smic0_mux),
+ SND_SOC_DAPM_MUX("VA SMIC MUX1", SND_SOC_NOPM, 0, 0, &va_smic1_mux),
+ SND_SOC_DAPM_MUX("VA SMIC MUX2", SND_SOC_NOPM, 0, 0, &va_smic2_mux),
+ SND_SOC_DAPM_MUX("VA SMIC MUX3", SND_SOC_NOPM, 0, 0, &va_smic3_mux),
+
SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-micb", 0, 0),
SND_SOC_DAPM_INPUT("DMIC0 Pin"),
SND_SOC_DAPM_INPUT("DMIC1 Pin"),
@@ -1525,6 +1595,62 @@ static const struct snd_soc_dapm_route va_audio_map[] = {
{"VA DMIC MUX3", "DMIC6", "VA DMIC6"},
{"VA DMIC MUX3", "DMIC7", "VA DMIC7"},

+ {"VA DEC0 MUX", "SWR_MIC", "VA SMIC MUX0"},
+ {"VA SMIC MUX0", "ADC0", "VA SWR_ADC0"},
+ {"VA SMIC MUX0", "ADC1", "VA SWR_ADC1"},
+ {"VA SMIC MUX0", "ADC2", "VA SWR_ADC2"},
+ {"VA SMIC MUX0", "ADC3", "VA SWR_ADC3"},
+ {"VA SMIC MUX0", "SWR_MIC0", "VA SWR_MIC0"},
+ {"VA SMIC MUX0", "SWR_MIC1", "VA SWR_MIC1"},
+ {"VA SMIC MUX0", "SWR_MIC2", "VA SWR_MIC2"},
+ {"VA SMIC MUX0", "SWR_MIC3", "VA SWR_MIC3"},
+ {"VA SMIC MUX0", "SWR_MIC4", "VA SWR_MIC4"},
+ {"VA SMIC MUX0", "SWR_MIC5", "VA SWR_MIC5"},
+ {"VA SMIC MUX0", "SWR_MIC6", "VA SWR_MIC6"},
+ {"VA SMIC MUX0", "SWR_MIC7", "VA SWR_MIC7"},
+
+ {"VA DEC1 MUX", "SWR_MIC", "VA SMIC MUX1"},
+ {"VA SMIC MUX1", "ADC0", "VA SWR_ADC0"},
+ {"VA SMIC MUX1", "ADC1", "VA SWR_ADC1"},
+ {"VA SMIC MUX1", "ADC2", "VA SWR_ADC2"},
+ {"VA SMIC MUX1", "ADC3", "VA SWR_ADC3"},
+ {"VA SMIC MUX1", "SWR_MIC0", "VA SWR_MIC0"},
+ {"VA SMIC MUX1", "SWR_MIC1", "VA SWR_MIC1"},
+ {"VA SMIC MUX1", "SWR_MIC2", "VA SWR_MIC2"},
+ {"VA SMIC MUX1", "SWR_MIC3", "VA SWR_MIC3"},
+ {"VA SMIC MUX1", "SWR_MIC4", "VA SWR_MIC4"},
+ {"VA SMIC MUX1", "SWR_MIC5", "VA SWR_MIC5"},
+ {"VA SMIC MUX1", "SWR_MIC6", "VA SWR_MIC6"},
+ {"VA SMIC MUX1", "SWR_MIC7", "VA SWR_MIC7"},
+
+ {"VA DEC2 MUX", "SWR_MIC", "VA SMIC MUX2"},
+ {"VA SMIC MUX2", "ADC0", "VA SWR_ADC0"},
+ {"VA SMIC MUX2", "ADC1", "VA SWR_ADC1"},
+ {"VA SMIC MUX2", "ADC2", "VA SWR_ADC2"},
+ {"VA SMIC MUX2", "ADC3", "VA SWR_ADC3"},
+ {"VA SMIC MUX2", "SWR_MIC0", "VA SWR_MIC0"},
+ {"VA SMIC MUX2", "SWR_MIC1", "VA SWR_MIC1"},
+ {"VA SMIC MUX2", "SWR_MIC2", "VA SWR_MIC2"},
+ {"VA SMIC MUX2", "SWR_MIC3", "VA SWR_MIC3"},
+ {"VA SMIC MUX2", "SWR_MIC4", "VA SWR_MIC4"},
+ {"VA SMIC MUX2", "SWR_MIC5", "VA SWR_MIC5"},
+ {"VA SMIC MUX2", "SWR_MIC6", "VA SWR_MIC6"},
+ {"VA SMIC MUX2", "SWR_MIC7", "VA SWR_MIC7"},
+
+ {"VA DEC3 MUX", "SWR_MIC", "VA SMIC MUX3"},
+ {"VA SMIC MUX3", "ADC0", "VA SWR_ADC0"},
+ {"VA SMIC MUX3", "ADC1", "VA SWR_ADC1"},
+ {"VA SMIC MUX3", "ADC2", "VA SWR_ADC2"},
+ {"VA SMIC MUX3", "ADC3", "VA SWR_ADC3"},
+ {"VA SMIC MUX3", "SWR_MIC0", "VA SWR_MIC0"},
+ {"VA SMIC MUX3", "SWR_MIC1", "VA SWR_MIC1"},
+ {"VA SMIC MUX3", "SWR_MIC2", "VA SWR_MIC2"},
+ {"VA SMIC MUX3", "SWR_MIC3", "VA SWR_MIC3"},
+ {"VA SMIC MUX3", "SWR_MIC4", "VA SWR_MIC4"},
+ {"VA SMIC MUX3", "SWR_MIC5", "VA SWR_MIC5"},
+ {"VA SMIC MUX3", "SWR_MIC6", "VA SWR_MIC6"},
+ {"VA SMIC MUX3", "SWR_MIC7", "VA SWR_MIC7"},
+
{ "VA DMIC0", NULL, "DMIC0 Pin" },
{ "VA DMIC1", NULL, "DMIC1 Pin" },
{ "VA DMIC2", NULL, "DMIC2 Pin" },

--
2.34.1