diff mbox series

[v1,5/5] ASoC: wm8904: add DMIC support

Message ID 20250206163152.423199-6-francesco@dolcini.it (mailing list archive)
State New
Headers show
Series ASoC: wm8904: Add DMIC and DRC support | expand

Commit Message

Francesco Dolcini Feb. 6, 2025, 4:31 p.m. UTC
From: Ernest Van Hoecke <ernest.vanhoecke@toradex.com>

The WM8904 codec supports both ADC and DMIC inputs.

Get input pin functionality from the platform data and add the necessary
controls depending on the possible additional routing.

The ADC and DMIC share the IN1L/DMICDAT1 and IN1R/DMICDAT2 pins.

When both are connected to an analog input, only the ADC is used. When
both are connected to a DMIC, only the DMIC is used, and a mux is added
to select the DMIC source. When one line is a DMIC and the other an
analog input, the DMIC source is set from the platform data and a mux is
added to select whether to use the ADC or DMIC.

Signed-off-by: Ernest Van Hoecke <ernest.vanhoecke@toradex.com>
Signed-off-by: Francesco Dolcini <francesco.dolcini@toradex.com>
---
 sound/soc/codecs/wm8904.c | 82 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 80 insertions(+), 2 deletions(-)

Comments

Charles Keepax Feb. 17, 2025, 10:44 a.m. UTC | #1
On Thu, Feb 06, 2025 at 05:31:52PM +0100, Francesco Dolcini wrote:
> From: Ernest Van Hoecke <ernest.vanhoecke@toradex.com>
> +static const struct snd_soc_dapm_route dmic_intercon[] = {
> +	{ "DMIC Mux", "DMIC1", "IN1L" },
> +	{ "DMIC Mux", "DMIC2", "IN1R" },
> +
> +	{ "ADCL", NULL, "DMIC Mux" },
> +	{ "ADCR", NULL, "DMIC Mux" },
> +};
> +
> +static const struct snd_soc_dapm_route cin_intercon[] = {
> +	{ "Left Capture Input", "ADC", "Left Capture PGA" },
> +	{ "Left Capture Input", "DMIC", "IN1L" },
> +	{ "Right Capture Input", "ADC", "Right Capture PGA" },
> +	{ "Right Capture Input", "DMIC", "IN1R" },

Am I misunderstanding things or does something not quite look
right with the routes here? Shouldn't you end up with the these
three situations:

Analogue:
	{ "Left Capture Input", "ADC", "Left Capture PGA" },
	{ "Right Capture Input", "ADC", "Right Capture PGA" },

Digital in1l_as_dmicdat1:
	{ "Left Capture Input", "DMIC", "IN1L" },
	{ "Right Capture Input", "DMIC", "IN1L" },

Digital in1r_as_dmicdat2:
	{ "Left Capture Input", "DMIC", "IN1R" },
	{ "Right Capture Input", "DMIC", "IN1R" },

So I think you need to add the DMIC routes conditionally here.

Also is there not some part of the existing analogue routing that
needs disconnected in the DMIC cases? This only addes routes,
which feels like the existing analogue path is never
disconnected?

Thanks,
Charles

> +
> +	{ "ADCL", NULL, "Left Capture Input" },
> +	{ "ADCR", NULL, "Right Capture Input" },
> +};
diff mbox series

Patch

diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 899ef6679f7e..a024cfc136c3 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -844,6 +844,26 @@  static int out_pga_event(struct snd_soc_dapm_widget *w,
 	return 0;
 }
 
+static const char * const dmic_text[] = {
+	"DMIC1", "DMIC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(dmic_enum, WM8904_DIGITAL_MICROPHONE_0,
+			    WM8904_DMIC_SRC_SHIFT, dmic_text);
+
+static const struct snd_kcontrol_new dmic_mux =
+	SOC_DAPM_ENUM("DMIC Mux", dmic_enum);
+
+static const char * const cin_text[] = {
+	"ADC", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(cin_enum, WM8904_DIGITAL_MICROPHONE_0,
+			    WM8904_DMIC_ENA_SHIFT, cin_text);
+
+static const struct snd_kcontrol_new cin_mux =
+	SOC_DAPM_ENUM("Capture Input", cin_enum);
+
 static const char *input_mode_text[] = {
 	"Single-Ended", "Differential Line", "Differential Mic"
 };
@@ -963,6 +983,15 @@  SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0),
 SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0),
 };
 
+static const struct snd_soc_dapm_widget wm8904_dmic_dapm_widgets[] = {
+SND_SOC_DAPM_MUX("DMIC Mux", SND_SOC_NOPM, 0, 0, &dmic_mux),
+};
+
+static const struct snd_soc_dapm_widget wm8904_cin_dapm_widgets[] = {
+SND_SOC_DAPM_MUX("Left Capture Input", SND_SOC_NOPM, 0, 0, &cin_mux),
+SND_SOC_DAPM_MUX("Right Capture Input", SND_SOC_NOPM, 0, 0, &cin_mux),
+};
+
 static const struct snd_soc_dapm_widget wm8904_dac_dapm_widgets[] = {
 SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
 SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
@@ -1107,6 +1136,24 @@  static const struct snd_soc_dapm_route adc_intercon[] = {
 	{ "ADCR", NULL, "Right Capture PGA" },
 };
 
+static const struct snd_soc_dapm_route dmic_intercon[] = {
+	{ "DMIC Mux", "DMIC1", "IN1L" },
+	{ "DMIC Mux", "DMIC2", "IN1R" },
+
+	{ "ADCL", NULL, "DMIC Mux" },
+	{ "ADCR", NULL, "DMIC Mux" },
+};
+
+static const struct snd_soc_dapm_route cin_intercon[] = {
+	{ "Left Capture Input", "ADC", "Left Capture PGA" },
+	{ "Left Capture Input", "DMIC", "IN1L" },
+	{ "Right Capture Input", "ADC", "Right Capture PGA" },
+	{ "Right Capture Input", "DMIC", "IN1R" },
+
+	{ "ADCL", NULL, "Left Capture Input" },
+	{ "ADCR", NULL, "Right Capture Input" },
+};
+
 static const struct snd_soc_dapm_route dac_intercon[] = {
 	{ "DACL Mux", "Left", "AIFINL" },
 	{ "DACL Mux", "Right", "AIFINR" },
@@ -2052,6 +2099,7 @@  static void wm8904_handle_retune_mobile_pdata(struct snd_soc_component *componen
 
 static void wm8904_handle_pdata(struct snd_soc_component *component)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
 	struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
 	struct wm8904_pdata *pdata = wm8904->pdata;
 	int ret, i;
@@ -2062,6 +2110,35 @@  static void wm8904_handle_pdata(struct snd_soc_component *component)
 		return;
 	}
 
+	if (pdata->in1l_as_dmicdat1 && pdata->in1r_as_dmicdat2) {
+		dev_dbg(component->dev, "Activating DMICDAT1 and DMICDAT2\n");
+		snd_soc_component_update_bits(component, WM8904_DIGITAL_MICROPHONE_0,
+					      WM8904_DMIC_ENA_MASK,
+					      1 << WM8904_DMIC_ENA_SHIFT);
+
+		/* Need a control and routing to switch between DMIC1 and 2 */
+		snd_soc_dapm_new_controls(dapm, wm8904_dmic_dapm_widgets,
+					  ARRAY_SIZE(wm8904_dmic_dapm_widgets));
+		snd_soc_dapm_add_routes(dapm, dmic_intercon,
+					ARRAY_SIZE(dmic_intercon));
+	} else if (pdata->in1l_as_dmicdat1 || pdata->in1r_as_dmicdat2) {
+		unsigned int dmic_src = pdata->in1l_as_dmicdat1 ? 0 : 1;
+
+		dev_dbg(component->dev, "DMIC_SRC (0 or 1): %d\n", dmic_src);
+		snd_soc_component_update_bits(component, WM8904_DIGITAL_MICROPHONE_0,
+					      WM8904_DMIC_SRC_MASK,
+					      dmic_src << WM8904_DMIC_SRC_SHIFT);
+
+		/* Need a control and routing to switch between DMIC and ADC */
+		snd_soc_dapm_new_controls(dapm, wm8904_cin_dapm_widgets,
+					  ARRAY_SIZE(wm8904_cin_dapm_widgets));
+		snd_soc_dapm_add_routes(dapm, cin_intercon,
+					ARRAY_SIZE(cin_intercon));
+	} else {
+		snd_soc_component_update_bits(component, WM8904_DIGITAL_MICROPHONE_0,
+					      WM8904_DMIC_ENA_MASK, 0);
+	}
+
 	dev_dbg(component->dev, "%d DRC configurations\n", pdata->num_drc_cfgs);
 
 	if (pdata->num_drc_cfgs) {
@@ -2117,10 +2194,11 @@  static int wm8904_probe(struct snd_soc_component *component)
 		return -EINVAL;
 	}
 
-	wm8904_handle_pdata(component);
-
 	wm8904_add_widgets(component);
 
+	/* This can add dependent widgets, so it is done after add_widgets */
+	wm8904_handle_pdata(component);
+
 	return 0;
 }