[alsa-devel] [PATCH v3 0/6] ASoC: sun4i-codec: Add support for A31 Codec
![](https://secure.gravatar.com/avatar/53e17915a0ebda94220596c4137b83fa.jpg?s=120&d=mm&r=g)
Hi everyone,
This is v3 of my Allwinner A31 audio codec support series. These are the remaining driver patches not yet merged, with 1 new fix, and the dts file patches.
The A31's internal codec is similar (in terms of DMA, interface and control layouts) to the one found in the A10/A13/A20 SoCs. However it has more external inputs and outputs, the mixer controls are now stereo (left/right separated), and it also has some audio processing features (not supported yet).
Changes since v2:
- Added patch """ASoC: sun4i-codec: Add "Right Mixer" to "Line Out Mono Diff." route""" which fixes line out when all the sources have their left channel muted.
- Dropped unused codec_pa_pin from A31 hummingbird dts patch. In the past we always provided a pinmux setting for GPIOs used, to avoid accidentally muxing a pin that was already used for GPIO, or vice versa. The pinctrl subsystem now supports exclusive use of a pin for GPIO, which the sunxi platform wants to migrate to. This drops the requirement of the explicit pinmux setting for GPIOs.
- Added Maxime's Acked-by for the two existing patches from v2.
After these are merged I plan to submit patches to support the audio codec found in Allwinner's A23 and H3 SoCs.
Regards ChenYu
Chen-Yu Tsai (6): ASoC: sun4i-codec: Add support for optional reset control to quirks ASoC: sun4i-codec: Add support for A31 ADC capture path ASoC: sun4i-codec: Add "Right Mixer" to "Line Out Mono Diff." route ARM: dts: sun6i: Add audio codec device node ARM: dts: sun6i: hummingbird: Enable internal audio codec ARM: dts: sun6i: sina31s: Enable internal audio codec
arch/arm/boot/dts/sun6i-a31-hummingbird.dts | 13 ++++ arch/arm/boot/dts/sun6i-a31.dtsi | 13 ++++ arch/arm/boot/dts/sun6i-a31s-sina31s.dts | 8 +++ sound/soc/sunxi/sun4i-codec.c | 94 ++++++++++++++++++++++++++++- 4 files changed, 127 insertions(+), 1 deletion(-)
![](https://secure.gravatar.com/avatar/53e17915a0ebda94220596c4137b83fa.jpg?s=120&d=mm&r=g)
The later Allwinner SoCs have a dedicated reset controller, and peripherals have dedicated reset controls which need to be deasserted before the associated peripheral can be used.
Add support for this to the quirks structure and probe/remove functions.
Signed-off-by: Chen-Yu Tsai wens@csie.org Acked-by: Maxime Ripard maxime.ripard@free-electrons.com --- sound/soc/sunxi/sun4i-codec.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 1934db29b2b5..b28b82a5ec62 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -30,6 +30,7 @@ #include <linux/of_platform.h> #include <linux/clk.h> #include <linux/regmap.h> +#include <linux/reset.h> #include <linux/gpio/consumer.h>
#include <sound/core.h> @@ -217,6 +218,7 @@ struct sun4i_codec { struct regmap *regmap; struct clk *clk_apb; struct clk *clk_module; + struct reset_control *rst; struct gpio_desc *gpio_pa;
/* ADC_FIFOC register is at different offset on different SoCs */ @@ -1167,6 +1169,7 @@ struct sun4i_codec_quirks { struct reg_field reg_adc_fifoc; /* used for regmap_field */ unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */ unsigned int reg_adc_rxdata; /* RX FIFO offset for DMA config */ + bool has_reset; };
static const struct sun4i_codec_quirks sun4i_codec_quirks = { @@ -1262,6 +1265,14 @@ static int sun4i_codec_probe(struct platform_device *pdev) return PTR_ERR(scodec->clk_module); }
+ if (quirks->has_reset) { + scodec->rst = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(scodec->rst)) { + dev_err(&pdev->dev, "Failed to get reset control\n"); + return PTR_ERR(scodec->rst); + } + }; + scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa", GPIOD_OUT_LOW); if (IS_ERR(scodec->gpio_pa)) { @@ -1288,6 +1299,16 @@ static int sun4i_codec_probe(struct platform_device *pdev) return -EINVAL; }
+ /* Deassert the reset control */ + if (scodec->rst) { + ret = reset_control_deassert(scodec->rst); + if (ret) { + dev_err(&pdev->dev, + "Failed to deassert the reset control\n"); + goto err_clk_disable; + } + } + /* DMA configuration for TX FIFO */ scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata; scodec->playback_dma_data.maxburst = 8; @@ -1302,7 +1323,7 @@ static int sun4i_codec_probe(struct platform_device *pdev) &sun4i_codec_dai, 1); if (ret) { dev_err(&pdev->dev, "Failed to register our codec\n"); - goto err_clk_disable; + goto err_assert_reset; }
ret = devm_snd_soc_register_component(&pdev->dev, @@ -1339,6 +1360,9 @@ static int sun4i_codec_probe(struct platform_device *pdev)
err_unregister_codec: snd_soc_unregister_codec(&pdev->dev); +err_assert_reset: + if (scodec->rst) + reset_control_assert(scodec->rst); err_clk_disable: clk_disable_unprepare(scodec->clk_apb); return ret; @@ -1351,6 +1375,8 @@ static int sun4i_codec_remove(struct platform_device *pdev)
snd_soc_unregister_card(card); snd_soc_unregister_codec(&pdev->dev); + if (scodec->rst) + reset_control_assert(scodec->rst); clk_disable_unprepare(scodec->clk_apb);
return 0;
![](https://secure.gravatar.com/avatar/53e17915a0ebda94220596c4137b83fa.jpg?s=120&d=mm&r=g)
The A31's internal codec capture path has a mixer in front of the ADC for each channel, capable of selecting various inputs, including microphones, line in, phone in, and the main output mixer.
This patch adds the various controls, widgets and routes needed for audio capture from the already supported inputs on the A31.
Signed-off-by: Chen-Yu Tsai wens@csie.org Acked-by: Maxime Ripard maxime.ripard@free-electrons.com --- sound/soc/sunxi/sun4i-codec.c | 65 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+)
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index b28b82a5ec62..6379efd21f00 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -788,6 +788,30 @@ static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = { SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2, 1, 0), };
+/* ADC mixer controls */ +static const struct snd_kcontrol_new sun6i_codec_adc_mixer_controls[] = { + SOC_DAPM_DOUBLE("Mixer Capture Switch", + SUN6I_CODEC_ADC_ACTL, + SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL, + SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR, 1, 0), + SOC_DAPM_DOUBLE("Mixer Reversed Capture Switch", + SUN6I_CODEC_ADC_ACTL, + SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR, + SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL, 1, 0), + SOC_DAPM_DOUBLE("Line In Capture Switch", + SUN6I_CODEC_ADC_ACTL, + SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL, + SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR, 1, 0), + SOC_DAPM_DOUBLE("Mic1 Capture Switch", + SUN6I_CODEC_ADC_ACTL, + SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1, + SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1, 1, 0), + SOC_DAPM_DOUBLE("Mic2 Capture Switch", + SUN6I_CODEC_ADC_ACTL, + SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2, + SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2, 1, 0), +}; + /* headphone controls */ static const char * const sun6i_codec_hp_src_enum_text[] = { "DAC", "Mixer", @@ -887,6 +911,10 @@ static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = { SOC_SINGLE_TLV("Mic2 Boost Volume", SUN6I_CODEC_MIC_CTRL, SUN6I_CODEC_MIC_CTRL_MIC2BOOST, 0x7, 0, sun6i_codec_mic_gain_scale), + SOC_DOUBLE_TLV("ADC Capture Volume", + SUN6I_CODEC_ADC_ACTL, SUN6I_CODEC_ADC_ACTL_ADCLG, + SUN6I_CODEC_ADC_ACTL_ADCRG, 0x7, 0, + sun6i_codec_out_mixer_pregain_scale), };
static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = { @@ -912,6 +940,23 @@ static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = { /* Line In */ SND_SOC_DAPM_INPUT("LINEIN"),
+ /* Digital parts of the ADCs */ + SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC, + SUN6I_CODEC_ADC_FIFOC_EN_AD, 0, + NULL, 0), + + /* Analog parts of the ADCs */ + SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL, + SUN6I_CODEC_ADC_ACTL_ADCLEN, 0), + SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL, + SUN6I_CODEC_ADC_ACTL_ADCREN, 0), + + /* ADC Mixers */ + SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0, + sun6i_codec_adc_mixer_controls), + SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0, + sun6i_codec_adc_mixer_controls), + /* Digital parts of the DACs */ SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC, SUN4I_CODEC_DAC_DPC_EN_DA, 0, @@ -975,6 +1020,20 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = { { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
+ /* Left ADC Mixer Routes */ + { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" }, + { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" }, + { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" }, + { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, + { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, + + /* Right ADC Mixer Routes */ + { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" }, + { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" }, + { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" }, + { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, + { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, + /* Headphone Routes */ { "Headphone Source Playback Route", "DAC", "Left DAC" }, { "Headphone Source Playback Route", "DAC", "Right DAC" }, @@ -989,6 +1048,12 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = { { "Line Out Source Playback Route", "Stereo", "Right Mixer" }, { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" }, { "LINEOUT", NULL, "Line Out Source Playback Route" }, + + /* ADC Routes */ + { "Left ADC", NULL, "ADC Enable" }, + { "Right ADC", NULL, "ADC Enable" }, + { "Left ADC", NULL, "Left ADC Mixer" }, + { "Right ADC", NULL, "Right ADC Mixer" }, };
static struct snd_soc_codec_driver sun6i_codec_codec = {
![](https://secure.gravatar.com/avatar/53e17915a0ebda94220596c4137b83fa.jpg?s=120&d=mm&r=g)
The mono differential output for "Line Out" downmixes the stereo audio from the mixer, instead of just taking the left channel.
Add a route from the "Right Mixer" to "Line Out Source Playback Route" through the "Mono Differential" path, so DAPM doesn't shut down everything if the left channel is muted.
Fixes: 0f909f98d7cb ("ASoC: sun4i-codec: Add support for A31 Line Out playback") Signed-off-by: Chen-Yu Tsai wens@csie.org --- sound/soc/sunxi/sun4i-codec.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 6379efd21f00..006ca3c17e80 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -1047,6 +1047,7 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = { { "Line Out Source Playback Route", "Stereo", "Left Mixer" }, { "Line Out Source Playback Route", "Stereo", "Right Mixer" }, { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" }, + { "Line Out Source Playback Route", "Mono Differential", "Right Mixer" }, { "LINEOUT", NULL, "Line Out Source Playback Route" },
/* ADC Routes */
![](https://secure.gravatar.com/avatar/d930951cb00393ecf9c3db3a56d78fa9.jpg?s=120&d=mm&r=g)
The patch
ASoC: sun4i-codec: Add "Right Mixer" to "Line Out Mono Diff." route
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 19426bdedb72b965db0ebf2106e95e9eeb3b5935 Mon Sep 17 00:00:00 2001
From: Chen-Yu Tsai wens@csie.org Date: Sun, 8 Jan 2017 02:57:06 +0800 Subject: [PATCH] ASoC: sun4i-codec: Add "Right Mixer" to "Line Out Mono Diff." route
The mono differential output for "Line Out" downmixes the stereo audio from the mixer, instead of just taking the left channel.
Add a route from the "Right Mixer" to "Line Out Source Playback Route" through the "Mono Differential" path, so DAPM doesn't shut down everything if the left channel is muted.
Fixes: 0f909f98d7cb ("ASoC: sun4i-codec: Add support for A31 Line Out playback") Signed-off-by: Chen-Yu Tsai wens@csie.org Acked-by: Maxime Ripard maxime.ripard@free-electrons.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/sunxi/sun4i-codec.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 848af01692a0..c3aab10fa085 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -1058,6 +1058,7 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = { { "Line Out Source Playback Route", "Stereo", "Left Mixer" }, { "Line Out Source Playback Route", "Stereo", "Right Mixer" }, { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" }, + { "Line Out Source Playback Route", "Mono Differential", "Right Mixer" }, { "LINEOUT", NULL, "Line Out Source Playback Route" },
/* ADC Routes */
![](https://secure.gravatar.com/avatar/53e17915a0ebda94220596c4137b83fa.jpg?s=120&d=mm&r=g)
The A31 SoC includes the Allwinner audio codec, capable of 24-bit playback up to 192 kHz and 24-bit capture up to 48 kHz.
Signed-off-by: Chen-Yu Tsai wens@csie.org --- arch/arm/boot/dts/sun6i-a31.dtsi | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi index 2e8bf93dcfb2..f68e6102b01b 100644 --- a/arch/arm/boot/dts/sun6i-a31.dtsi +++ b/arch/arm/boot/dts/sun6i-a31.dtsi @@ -784,6 +784,19 @@ reset-names = "ahb"; };
+ codec: codec@01c22c00 { + #sound-dai-cells = <0>; + compatible = "allwinner,sun6i-a31-codec"; + reg = <0x01c22c00 0x98>; + interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_APB1_CODEC>, <&ccu CLK_CODEC>; + clock-names = "apb", "codec"; + resets = <&ccu RST_APB1_CODEC>; + dmas = <&dma 15>, <&dma 15>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + timer@01c60000 { compatible = "allwinner,sun6i-a31-hstimer", "allwinner,sun7i-a20-hstimer";
![](https://secure.gravatar.com/avatar/6f5935d8c7d3d83ae6348a4ee8090399.jpg?s=120&d=mm&r=g)
On Mon, Nov 07, 2016 at 06:07:01PM +0800, Chen-Yu Tsai wrote:
The A31 SoC includes the Allwinner audio codec, capable of 24-bit playback up to 192 kHz and 24-bit capture up to 48 kHz.
Signed-off-by: Chen-Yu Tsai wens@csie.org
arch/arm/boot/dts/sun6i-a31.dtsi | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi index 2e8bf93dcfb2..f68e6102b01b 100644 --- a/arch/arm/boot/dts/sun6i-a31.dtsi +++ b/arch/arm/boot/dts/sun6i-a31.dtsi @@ -784,6 +784,19 @@ reset-names = "ahb"; };
codec: codec@01c22c00 {
#sound-dai-cells = <0>;
compatible = "allwinner,sun6i-a31-codec";
reg = <0x01c22c00 0x98>;
The memory mapped region is 0x400. I fixed this and applied.
Thanks! Maxime
![](https://secure.gravatar.com/avatar/53e17915a0ebda94220596c4137b83fa.jpg?s=120&d=mm&r=g)
The Hummingbird A31 has headset and line in audio jacks and an onboard mic routed to the pins for the SoC's internal codec. The line out pins are routed to an onboard speaker amp, whose output is available on a pin header.
Signed-off-by: Chen-Yu Tsai wens@csie.org --- arch/arm/boot/dts/sun6i-a31-hummingbird.dts | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts index 9a74637f677f..4e0516026596 100644 --- a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts +++ b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts @@ -69,6 +69,19 @@ }; };
+&codec { + allwinner,audio-routing = + "Headphone", "HP", + "Speaker", "LINEOUT", + "LINEIN", "Line In", + "MIC1", "Mic", + "MIC2", "Headset Mic", + "Mic", "MBIAS", + "Headset Mic", "HBIAS"; + allwinner,pa-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */ + status = "okay"; +}; + &cpu0 { cpu-supply = <®_dcdc3>; };
![](https://secure.gravatar.com/avatar/6f5935d8c7d3d83ae6348a4ee8090399.jpg?s=120&d=mm&r=g)
On Mon, Nov 07, 2016 at 06:07:02PM +0800, Chen-Yu Tsai wrote:
The Hummingbird A31 has headset and line in audio jacks and an onboard mic routed to the pins for the SoC's internal codec. The line out pins are routed to an onboard speaker amp, whose output is available on a pin header.
Signed-off-by: Chen-Yu Tsai wens@csie.org
Applied, thanks! Maxime
![](https://secure.gravatar.com/avatar/53e17915a0ebda94220596c4137b83fa.jpg?s=120&d=mm&r=g)
The SinA31s routes the SoC's LINEOUT pins to a line out jack, and MIC1 to a microphone jack, with MBIAS providing phantom power.
Signed-off-by: Chen-Yu Tsai wens@csie.org --- arch/arm/boot/dts/sun6i-a31s-sina31s.dts | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/arch/arm/boot/dts/sun6i-a31s-sina31s.dts b/arch/arm/boot/dts/sun6i-a31s-sina31s.dts index 6ead2f5c847a..c35ec112f5a0 100644 --- a/arch/arm/boot/dts/sun6i-a31s-sina31s.dts +++ b/arch/arm/boot/dts/sun6i-a31s-sina31s.dts @@ -65,6 +65,14 @@ }; };
+&codec { + allwinner,audio-routing = + "Line Out", "LINEOUT", + "MIC1", "Mic", + "Mic", "MBIAS"; + status = "okay"; +}; + &ehci0 { /* USB 2.0 4 port hub IC */ status = "okay";
![](https://secure.gravatar.com/avatar/6f5935d8c7d3d83ae6348a4ee8090399.jpg?s=120&d=mm&r=g)
On Mon, Nov 07, 2016 at 06:07:03PM +0800, Chen-Yu Tsai wrote:
The SinA31s routes the SoC's LINEOUT pins to a line out jack, and MIC1 to a microphone jack, with MBIAS providing phantom power.
Signed-off-by: Chen-Yu Tsai wens@csie.org
Applied, thanks! Maxime
participants (3)
-
Chen-Yu Tsai
-
Mark Brown
-
Maxime Ripard