Each left/right pair of AIF input/output channels can be swapped or combined. This is useful for sending a mono audio source to both sides of a stereo sink, or for creating complex mixing scenarios. Add the support to control this feature from userspace. Signed-off-by: Samuel Holland <samuel@xxxxxxxxxxxx> --- sound/soc/sunxi/sun8i-codec.c | 82 ++++++++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 6 deletions(-) diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 2c89974243e1..578c0c0e6330 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -49,19 +49,23 @@ #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV 9 #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV 6 #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ 4 #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16 (1 << 4) #define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT 2 #define SUN8I_AIF1_ADCDAT_CTRL 0x044 #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA 15 #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA 14 +#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_SRC 10 +#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_SRC 8 #define SUN8I_AIF1_DACDAT_CTRL 0x048 #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA 15 #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA 14 +#define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_SRC 10 +#define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_SRC 8 #define SUN8I_AIF1_MXR_SRC 0x04c #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L 15 #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL 14 #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL 13 #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR 12 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R 11 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR 10 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR 9 @@ -327,16 +331,30 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL, SUN8I_SYS_SR_CTRL_AIF1_FS_MASK, sample_rate << SUN8I_SYS_SR_CTRL_AIF1_FS); return 0; } +static const char *const sun8i_aif_stereo_mux_enum_values[] = { + "Stereo", "Reverse Stereo", "Sum Mono", "Mix Mono" +}; + +static SOC_ENUM_DOUBLE_DECL(sun8i_aif1_ad0_stereo_mux_enum, + SUN8I_AIF1_ADCDAT_CTRL, + SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_SRC, + SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_SRC, + sun8i_aif_stereo_mux_enum_values); + +static const struct snd_kcontrol_new sun8i_aif1_ad0_stereo_mux_control = + SOC_DAPM_ENUM("AIF1 AD0 Stereo Capture Route", + sun8i_aif1_ad0_stereo_mux_enum); + static const struct snd_kcontrol_new sun8i_aif1_ad0_mixer_controls[] = { SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch", SUN8I_AIF1_MXR_SRC, SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L, SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R, 1, 0), SOC_DAPM_DOUBLE("AIF2 Digital ADC Capture Switch", SUN8I_AIF1_MXR_SRC, SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL, @@ -346,16 +364,26 @@ static const struct snd_kcontrol_new sun8i_aif1_ad0_mixer_controls[] = { SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL, SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR, 1, 0), SOC_DAPM_DOUBLE("AIF2 Inv Digital ADC Capture Switch", SUN8I_AIF1_MXR_SRC, SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR, SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0), }; +static SOC_ENUM_DOUBLE_DECL(sun8i_aif1_da0_stereo_mux_enum, + SUN8I_AIF1_DACDAT_CTRL, + SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_SRC, + SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_SRC, + sun8i_aif_stereo_mux_enum_values); + +static const struct snd_kcontrol_new sun8i_aif1_da0_stereo_mux_control = + SOC_DAPM_ENUM("AIF1 DA0 Stereo Playback Route", + sun8i_aif1_da0_stereo_mux_enum); + static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = { SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC, SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L, SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R, 1, 0), SOC_DAPM_DOUBLE("AIF1 Slot 1 Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC, SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L, @@ -412,22 +440,34 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { /* AIF "ADC" Outputs */ SND_SOC_DAPM_AIF_OUT("AIF1 AD0L", "Capture", 0, SUN8I_AIF1_ADCDAT_CTRL, SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0), SND_SOC_DAPM_AIF_OUT("AIF1 AD0R", "Capture", 1, SUN8I_AIF1_ADCDAT_CTRL, SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0), + /* AIF "ADC" Mono/Stereo Muxes */ + SND_SOC_DAPM_MUX("AIF1 AD0L Stereo Mux", SND_SOC_NOPM, 0, 0, + &sun8i_aif1_ad0_stereo_mux_control), + SND_SOC_DAPM_MUX("AIF1 AD0R Stereo Mux", SND_SOC_NOPM, 0, 0, + &sun8i_aif1_ad0_stereo_mux_control), + /* AIF "ADC" Mixers */ SOC_MIXER_ARRAY("AIF1 AD0L Mixer", SND_SOC_NOPM, 0, 0, sun8i_aif1_ad0_mixer_controls), SOC_MIXER_ARRAY("AIF1 AD0R Mixer", SND_SOC_NOPM, 0, 0, sun8i_aif1_ad0_mixer_controls), + /* AIF "DAC" Mono/Stereo Muxes */ + SND_SOC_DAPM_MUX("AIF1 DA0L Stereo Mux", SND_SOC_NOPM, 0, 0, + &sun8i_aif1_da0_stereo_mux_control), + SND_SOC_DAPM_MUX("AIF1 DA0R Stereo Mux", SND_SOC_NOPM, 0, 0, + &sun8i_aif1_da0_stereo_mux_control), + /* AIF "DAC" Inputs */ SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "Playback", 0, SUN8I_AIF1_DACDAT_CTRL, SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0), SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 1, SUN8I_AIF1_DACDAT_CTRL, SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0), @@ -468,35 +508,65 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { { "CLK DAC", NULL, "SYSCLK" }, { "RST DAC", NULL, "CLK DAC" }, { "DAC", NULL, "RST DAC" }, { "DACL", NULL, "DAC" }, { "DACR", NULL, "DAC" }, /* AIF "ADC" Output Routes */ - { "AIF1 AD0L", NULL, "AIF1 AD0L Mixer" }, - { "AIF1 AD0R", NULL, "AIF1 AD0R Mixer" }, + { "AIF1 AD0L", NULL, "AIF1 AD0L Stereo Mux" }, + { "AIF1 AD0R", NULL, "AIF1 AD0R Stereo Mux" }, + + /* AIF "ADC" Mono/Stereo Mux Routes */ + { "AIF1 AD0L Stereo Mux", "Stereo", "AIF1 AD0L Mixer" }, + { "AIF1 AD0L Stereo Mux", "Reverse Stereo", "AIF1 AD0R Mixer" }, + { "AIF1 AD0L Stereo Mux", "Sum Mono", "AIF1 AD0L Mixer" }, + { "AIF1 AD0L Stereo Mux", "Sum Mono", "AIF1 AD0R Mixer" }, + { "AIF1 AD0L Stereo Mux", "Mix Mono", "AIF1 AD0L Mixer" }, + { "AIF1 AD0L Stereo Mux", "Mix Mono", "AIF1 AD0R Mixer" }, + + { "AIF1 AD0R Stereo Mux", "Stereo", "AIF1 AD0R Mixer" }, + { "AIF1 AD0R Stereo Mux", "Reverse Stereo", "AIF1 AD0L Mixer" }, + { "AIF1 AD0R Stereo Mux", "Sum Mono", "AIF1 AD0L Mixer" }, + { "AIF1 AD0R Stereo Mux", "Sum Mono", "AIF1 AD0R Mixer" }, + { "AIF1 AD0R Stereo Mux", "Mix Mono", "AIF1 AD0L Mixer" }, + { "AIF1 AD0R Stereo Mux", "Mix Mono", "AIF1 AD0R Mixer" }, /* AIF "ADC" Mixer Routes */ - { "AIF1 AD0L Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L" }, + { "AIF1 AD0L Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L Stereo Mux" }, { "AIF1 AD0L Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" }, - { "AIF1 AD0R Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R" }, + { "AIF1 AD0R Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R Stereo Mux" }, { "AIF1 AD0R Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" }, + /* AIF "DAC" Mono/Stereo Mux Routes */ + { "AIF1 DA0L Stereo Mux", "Stereo", "AIF1 DA0L" }, + { "AIF1 DA0L Stereo Mux", "Reverse Stereo", "AIF1 DA0R" }, + { "AIF1 DA0L Stereo Mux", "Sum Mono", "AIF1 DA0L" }, + { "AIF1 DA0L Stereo Mux", "Sum Mono", "AIF1 DA0R" }, + { "AIF1 DA0L Stereo Mux", "Mix Mono", "AIF1 DA0L" }, + { "AIF1 DA0L Stereo Mux", "Mix Mono", "AIF1 DA0R" }, + + { "AIF1 DA0R Stereo Mux", "Stereo", "AIF1 DA0R" }, + { "AIF1 DA0R Stereo Mux", "Reverse Stereo", "AIF1 DA0L" }, + { "AIF1 DA0R Stereo Mux", "Sum Mono", "AIF1 DA0L" }, + { "AIF1 DA0R Stereo Mux", "Sum Mono", "AIF1 DA0R" }, + { "AIF1 DA0R Stereo Mux", "Mix Mono", "AIF1 DA0L" }, + { "AIF1 DA0R Stereo Mux", "Mix Mono", "AIF1 DA0R" }, + /* DAC Output Routes */ { "DACL", NULL, "DACL Mixer" }, { "DACR", NULL, "DACR Mixer" }, /* DAC Mixer Routes */ - { "DACL Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L" }, + { "DACL Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L Stereo Mux" }, { "DACL Mixer", "ADC Digital DAC Playback Switch", "ADCL" }, - { "DACR Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R" }, + { "DACR Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R Stereo Mux" }, { "DACR Mixer", "ADC Digital DAC Playback Switch", "ADCR" }, }; static const struct snd_soc_dapm_widget sun8i_codec_legacy_widgets[] = { /* Legacy ADC Inputs (connected to analog codec DAPM context) */ SND_SOC_DAPM_ADC("AIF1 Slot 0 Left ADC", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_ADC("AIF1 Slot 0 Right ADC", NULL, SND_SOC_NOPM, 0, 0), -- 2.26.2