Skip to content

Commit b6612c8

Browse files
Ernest Van Hoeckebroonie
authored andcommitted
ASoC: wm8904: add DMIC support
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. This leads to a few scenarios requiring different DAPM routing: - When both are connected to an analog input, only the ADC is used. - 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. - When both are connected to a DMIC, another mux is added to this to select the DMIC source. Note that we still need to be able to select the ADC system for use with the IN2L, IN2R, IN3L and IN3R pins. Signed-off-by: Ernest Van Hoecke <ernest.vanhoecke@toradex.com> Signed-off-by: Francesco Dolcini <francesco.dolcini@toradex.com> Reviewed-by: Charles Keepax <ckeepax@opensource.cirrus.com> Link: https://patch.msgid.link/20250319142059.46692-6-francesco@dolcini.it Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent afe66ef commit b6612c8

File tree

1 file changed

+120
-5
lines changed

1 file changed

+120
-5
lines changed

sound/soc/codecs/wm8904.c

Lines changed: 120 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,26 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
844844
return 0;
845845
}
846846

847+
static const char * const dmic_text[] = {
848+
"DMIC1", "DMIC2"
849+
};
850+
851+
static SOC_ENUM_SINGLE_DECL(dmic_enum, WM8904_DIGITAL_MICROPHONE_0,
852+
WM8904_DMIC_SRC_SHIFT, dmic_text);
853+
854+
static const struct snd_kcontrol_new dmic_mux =
855+
SOC_DAPM_ENUM("DMIC Mux", dmic_enum);
856+
857+
static const char * const cin_text[] = {
858+
"ADC", "DMIC"
859+
};
860+
861+
static SOC_ENUM_SINGLE_DECL(cin_enum, WM8904_DIGITAL_MICROPHONE_0,
862+
WM8904_DMIC_ENA_SHIFT, cin_text);
863+
864+
static const struct snd_kcontrol_new cin_mux =
865+
SOC_DAPM_ENUM("Capture Input", cin_enum);
866+
847867
static const char *input_mode_text[] = {
848868
"Single-Ended", "Differential Line", "Differential Mic"
849869
};
@@ -963,6 +983,15 @@ SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0),
963983
SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0),
964984
};
965985

986+
static const struct snd_soc_dapm_widget wm8904_dmic_dapm_widgets[] = {
987+
SND_SOC_DAPM_MUX("DMIC Mux", SND_SOC_NOPM, 0, 0, &dmic_mux),
988+
};
989+
990+
static const struct snd_soc_dapm_widget wm8904_cin_dapm_widgets[] = {
991+
SND_SOC_DAPM_MUX("Left Capture Input", SND_SOC_NOPM, 0, 0, &cin_mux),
992+
SND_SOC_DAPM_MUX("Right Capture Input", SND_SOC_NOPM, 0, 0, &cin_mux),
993+
};
994+
966995
static const struct snd_soc_dapm_widget wm8904_dac_dapm_widgets[] = {
967996
SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
968997
SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
@@ -1101,12 +1130,45 @@ static const struct snd_soc_dapm_route adc_intercon[] = {
11011130
{ "AIFOUTR", NULL, "AIFOUTR Mux" },
11021131

11031132
{ "ADCL", NULL, "CLK_DSP" },
1104-
{ "ADCL", NULL, "Left Capture PGA" },
1105-
11061133
{ "ADCR", NULL, "CLK_DSP" },
1134+
};
1135+
1136+
/* No DMICs, always connect PGAs */
1137+
static const struct snd_soc_dapm_route cin_nodmic_con[] = {
1138+
{ "ADCL", NULL, "Left Capture PGA" },
11071139
{ "ADCR", NULL, "Right Capture PGA" },
11081140
};
11091141

1142+
/* DMIC system in use: mux between ADC and DMICDAT1, 2 or both */
1143+
static const struct snd_soc_dapm_route cin_adc_dmic_con[] = {
1144+
{ "Left Capture Input", "ADC", "Left Capture PGA" },
1145+
{ "Right Capture Input", "ADC", "Right Capture PGA" },
1146+
1147+
{ "ADCL", NULL, "Left Capture Input" },
1148+
{ "ADCR", NULL, "Right Capture Input" },
1149+
};
1150+
1151+
/* IN1L as DMICDAT1 */
1152+
static const struct snd_soc_dapm_route cin_dmic1_con[] = {
1153+
{ "Left Capture Input", "DMIC", "IN1L" },
1154+
{ "Right Capture Input", "DMIC", "IN1L" },
1155+
};
1156+
1157+
/* IN1R as DMICDAT2 */
1158+
static const struct snd_soc_dapm_route cin_dmic2_con[] = {
1159+
{ "Left Capture Input", "DMIC", "IN1R" },
1160+
{ "Right Capture Input", "DMIC", "IN1R" },
1161+
};
1162+
1163+
/* DMICDAT1 and DMICDAT2: mux between them, ADC still used for IN2 and IN3 */
1164+
static const struct snd_soc_dapm_route cin_2dmics_con[] = {
1165+
{ "DMIC Mux", "DMIC1", "IN1L" },
1166+
{ "DMIC Mux", "DMIC2", "IN1R" },
1167+
1168+
{ "Left Capture Input", "DMIC", "DMIC Mux" },
1169+
{ "Right Capture Input", "DMIC", "DMIC Mux" },
1170+
};
1171+
11101172
static const struct snd_soc_dapm_route dac_intercon[] = {
11111173
{ "DACL Mux", "Left", "AIFINL" },
11121174
{ "DACL Mux", "Right", "AIFINR" },
@@ -2050,18 +2112,70 @@ static void wm8904_handle_retune_mobile_pdata(struct snd_soc_component *componen
20502112
"Failed to add ReTune Mobile control: %d\n", ret);
20512113
}
20522114

2115+
static void wm8904_handle_dmic_pdata(struct snd_soc_component *component)
2116+
{
2117+
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
2118+
struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
2119+
struct wm8904_pdata *pdata = wm8904->pdata;
2120+
unsigned int dmic_src;
2121+
2122+
if (!pdata->in1l_as_dmicdat1 && !pdata->in1r_as_dmicdat2) {
2123+
snd_soc_dapm_add_routes(dapm, cin_nodmic_con,
2124+
ARRAY_SIZE(cin_nodmic_con));
2125+
snd_soc_component_update_bits(component, WM8904_DIGITAL_MICROPHONE_0,
2126+
WM8904_DMIC_ENA_MASK, 0);
2127+
return;
2128+
}
2129+
2130+
/* Need a control and routing to switch between DMIC and ADC */
2131+
snd_soc_dapm_new_controls(dapm, wm8904_cin_dapm_widgets,
2132+
ARRAY_SIZE(wm8904_cin_dapm_widgets));
2133+
snd_soc_dapm_add_routes(dapm, cin_adc_dmic_con,
2134+
ARRAY_SIZE(cin_adc_dmic_con));
2135+
2136+
if (pdata->in1l_as_dmicdat1 && pdata->in1r_as_dmicdat2) {
2137+
/* Need a control and routing to mux between DMICDAT1 and 2 */
2138+
dev_dbg(component->dev, "DMICDAT1 and DMICDAT2 in use\n");
2139+
snd_soc_dapm_new_controls(dapm, wm8904_dmic_dapm_widgets,
2140+
ARRAY_SIZE(wm8904_dmic_dapm_widgets));
2141+
snd_soc_dapm_add_routes(dapm, cin_2dmics_con,
2142+
ARRAY_SIZE(cin_2dmics_con));
2143+
return;
2144+
}
2145+
2146+
/* Either DMICDAT1 or DMICDAT2 is in use, not both */
2147+
if (pdata->in1l_as_dmicdat1) {
2148+
dmic_src = 0;
2149+
snd_soc_dapm_add_routes(dapm, cin_dmic1_con,
2150+
ARRAY_SIZE(cin_dmic1_con));
2151+
} else {
2152+
dmic_src = 1;
2153+
snd_soc_dapm_add_routes(dapm, cin_dmic2_con,
2154+
ARRAY_SIZE(cin_dmic2_con));
2155+
}
2156+
dev_dbg(component->dev, "DMIC_SRC (0 or 1): %d\n", dmic_src);
2157+
snd_soc_component_update_bits(component, WM8904_DIGITAL_MICROPHONE_0,
2158+
WM8904_DMIC_SRC_MASK,
2159+
dmic_src << WM8904_DMIC_SRC_SHIFT);
2160+
}
2161+
20532162
static void wm8904_handle_pdata(struct snd_soc_component *component)
20542163
{
2164+
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
20552165
struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
20562166
struct wm8904_pdata *pdata = wm8904->pdata;
20572167
int ret, i;
20582168

20592169
if (!pdata) {
2170+
snd_soc_dapm_add_routes(dapm, cin_nodmic_con,
2171+
ARRAY_SIZE(cin_nodmic_con));
20602172
snd_soc_add_component_controls(component, wm8904_eq_controls,
2061-
ARRAY_SIZE(wm8904_eq_controls));
2173+
ARRAY_SIZE(wm8904_eq_controls));
20622174
return;
20632175
}
20642176

2177+
wm8904_handle_dmic_pdata(component);
2178+
20652179
dev_dbg(component->dev, "%d DRC configurations\n", pdata->num_drc_cfgs);
20662180

20672181
if (pdata->num_drc_cfgs) {
@@ -2117,10 +2231,11 @@ static int wm8904_probe(struct snd_soc_component *component)
21172231
return -EINVAL;
21182232
}
21192233

2120-
wm8904_handle_pdata(component);
2121-
21222234
wm8904_add_widgets(component);
21232235

2236+
/* This can add dependent widgets, so it is done after add_widgets */
2237+
wm8904_handle_pdata(component);
2238+
21242239
return 0;
21252240
}
21262241

0 commit comments

Comments
 (0)