forked from Minki/linux
Merge series "Allwinner A64 digital audio codec fixes" from Samuel Holland <samuel@sholland.org>:
This series fixes a couple of issues with the digital audio codec in the Allwinner A64 SoC: 1) Left/right channels were swapped when playing/recording audio 2) DAPM topology was wrong, breaking some kcontrols This is the minimum set of changes necessary to fix these issues in a backward-compatible way. For that reason, some DAPM widgets still have incorrect or confusing names; those and other issues will be fixed in later patch sets. Samuel Holland (7): ASoC: dt-bindings: Add a new compatible for the A64 codec ASoC: sun8i-codec: Fix DAPM to match the hardware topology ASoC: sun8i-codec: Add missing mixer routes ASoC: sun8i-codec: Add a quirk for LRCK inversion ARM: dts: sun8i: a33: Update codec widget names arm64: dts: allwinner: a64: Update codec widget names arm64: dts: allwinner: a64: Update the audio codec compatible .../sound/allwinner,sun8i-a33-codec.yaml | 6 +- arch/arm/boot/dts/sun8i-a33-olinuxino.dts | 4 +- arch/arm/boot/dts/sun8i-a33.dtsi | 4 +- .../dts/allwinner/sun50i-a64-bananapi-m64.dts | 8 +- .../dts/allwinner/sun50i-a64-orangepi-win.dts | 8 +- .../boot/dts/allwinner/sun50i-a64-pine64.dts | 8 +- .../dts/allwinner/sun50i-a64-pinebook.dts | 8 +- .../dts/allwinner/sun50i-a64-pinephone.dtsi | 8 +- .../boot/dts/allwinner/sun50i-a64-pinetab.dts | 8 +- .../allwinner/sun50i-a64-sopine-baseboard.dts | 8 +- .../boot/dts/allwinner/sun50i-a64-teres-i.dts | 8 +- arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 11 +- sound/soc/sunxi/sun8i-codec.c | 137 ++++++++++++++---- 13 files changed, 155 insertions(+), 71 deletions(-) -- 2.26.2
This commit is contained in:
commit
2d32c6087d
@ -15,7 +15,11 @@ properties:
|
||||
const: 0
|
||||
|
||||
compatible:
|
||||
const: allwinner,sun8i-a33-codec
|
||||
oneOf:
|
||||
- items:
|
||||
- const: allwinner,sun50i-a64-codec
|
||||
- const: allwinner,sun8i-a33-codec
|
||||
- const: allwinner,sun8i-a33-codec
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/log2.h>
|
||||
@ -85,10 +86,16 @@
|
||||
#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK GENMASK(8, 6)
|
||||
#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK GENMASK(12, 9)
|
||||
|
||||
struct sun8i_codec_quirks {
|
||||
bool legacy_widgets : 1;
|
||||
bool lrck_inversion : 1;
|
||||
};
|
||||
|
||||
struct sun8i_codec {
|
||||
struct regmap *regmap;
|
||||
struct clk *clk_module;
|
||||
struct clk *clk_bus;
|
||||
struct regmap *regmap;
|
||||
struct clk *clk_module;
|
||||
struct clk *clk_bus;
|
||||
const struct sun8i_codec_quirks *quirks;
|
||||
};
|
||||
|
||||
static int sun8i_codec_runtime_resume(struct device *dev)
|
||||
@ -209,18 +216,19 @@ static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
value << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV);
|
||||
|
||||
/*
|
||||
* It appears that the DAI and the codec don't share the same
|
||||
* polarity for the LRCK signal when they mean 'normal' and
|
||||
* 'inverted' in the datasheet.
|
||||
* It appears that the DAI and the codec in the A33 SoC don't
|
||||
* share the same polarity for the LRCK signal when they mean
|
||||
* 'normal' and 'inverted' in the datasheet.
|
||||
*
|
||||
* Since the DAI here is our regular i2s driver that have been
|
||||
* tested with way more codecs than just this one, it means
|
||||
* that the codec probably gets it backward, and we have to
|
||||
* invert the value here.
|
||||
*/
|
||||
value ^= scodec->quirks->lrck_inversion;
|
||||
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
|
||||
BIT(SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV),
|
||||
!value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV);
|
||||
value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV);
|
||||
|
||||
/* DAI format */
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
@ -388,22 +396,30 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY("ADC", SUN8I_ADC_DIG_CTRL, SUN8I_ADC_DIG_CTRL_ENDA,
|
||||
0, NULL, 0),
|
||||
|
||||
/* Analog DAC AIF */
|
||||
SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Left", "Playback", 0,
|
||||
/* 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 Slot 0 Right", "Playback", 0,
|
||||
SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 0,
|
||||
SUN8I_AIF1_DACDAT_CTRL,
|
||||
SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),
|
||||
|
||||
/* Analog ADC AIF */
|
||||
SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Left ADC", "Capture", 0,
|
||||
/* AIF "ADC" Outputs */
|
||||
SND_SOC_DAPM_AIF_IN("AIF1 AD0L", "Capture", 0,
|
||||
SUN8I_AIF1_ADCDAT_CTRL,
|
||||
SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0L_ENA, 0),
|
||||
SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Right ADC", "Capture", 0,
|
||||
SND_SOC_DAPM_AIF_IN("AIF1 AD0R", "Capture", 0,
|
||||
SUN8I_AIF1_ADCDAT_CTRL,
|
||||
SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0R_ENA, 0),
|
||||
|
||||
/* ADC Inputs (connected to analog codec DAPM context) */
|
||||
SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
|
||||
|
||||
/* DAC Outputs (connected to analog codec DAPM context) */
|
||||
SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
|
||||
|
||||
/* DAC and ADC Mixers */
|
||||
SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
|
||||
sun8i_dac_mixer_controls),
|
||||
@ -449,40 +465,92 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
|
||||
/* Clock Routes */
|
||||
{ "AIF1", NULL, "SYSCLK AIF1" },
|
||||
{ "AIF1 PLL", NULL, "AIF1" },
|
||||
{ "RST AIF1", NULL, "AIF1 PLL" },
|
||||
{ "SYSCLK", NULL, "AIF1 PLL" },
|
||||
|
||||
{ "RST AIF1", NULL, "SYSCLK" },
|
||||
{ "MODCLK AFI1", NULL, "RST AIF1" },
|
||||
{ "DAC", NULL, "MODCLK AFI1" },
|
||||
{ "ADC", NULL, "MODCLK AFI1" },
|
||||
{ "AIF1 AD0L", NULL, "MODCLK AFI1" },
|
||||
{ "AIF1 AD0R", NULL, "MODCLK AFI1" },
|
||||
{ "AIF1 DA0L", NULL, "MODCLK AFI1" },
|
||||
{ "AIF1 DA0R", NULL, "MODCLK AFI1" },
|
||||
|
||||
{ "RST DAC", NULL, "SYSCLK" },
|
||||
{ "MODCLK DAC", NULL, "RST DAC" },
|
||||
{ "DAC", NULL, "MODCLK DAC" },
|
||||
{ "DACL", NULL, "DAC" },
|
||||
{ "DACR", NULL, "DAC" },
|
||||
|
||||
{ "RST ADC", NULL, "SYSCLK" },
|
||||
{ "MODCLK ADC", NULL, "RST ADC" },
|
||||
{ "ADC", NULL, "MODCLK ADC" },
|
||||
{ "ADCL", NULL, "ADC" },
|
||||
{ "ADCR", NULL, "ADC" },
|
||||
|
||||
/* DAC Routes */
|
||||
{ "AIF1 Slot 0 Right", NULL, "DAC" },
|
||||
{ "AIF1 Slot 0 Left", NULL, "DAC" },
|
||||
{ "DACL", NULL, "Left Digital DAC Mixer" },
|
||||
{ "DACR", NULL, "Right Digital DAC Mixer" },
|
||||
|
||||
/* DAC Mixer Routes */
|
||||
{ "Left Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch",
|
||||
"AIF1 Slot 0 Left"},
|
||||
{ "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch",
|
||||
"AIF1 Slot 0 Right"},
|
||||
{ "Left Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L" },
|
||||
{ "Left Digital DAC Mixer", "ADC Digital DAC Playback Switch", "ADCL" },
|
||||
|
||||
{ "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R" },
|
||||
{ "Right Digital DAC Mixer", "ADC Digital DAC Playback Switch", "ADCR" },
|
||||
|
||||
/* ADC Routes */
|
||||
{ "AIF1 Slot 0 Right ADC", NULL, "ADC" },
|
||||
{ "AIF1 Slot 0 Left ADC", NULL, "ADC" },
|
||||
{ "AIF1 AD0L", NULL, "Left Digital ADC Mixer" },
|
||||
{ "AIF1 AD0R", NULL, "Right Digital ADC Mixer" },
|
||||
|
||||
/* ADC Mixer Routes */
|
||||
{ "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch",
|
||||
"AIF1 Slot 0 Left ADC" },
|
||||
{ "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch",
|
||||
"AIF1 Slot 0 Right ADC" },
|
||||
{ "Left Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L" },
|
||||
{ "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" },
|
||||
|
||||
{ "Right Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R" },
|
||||
{ "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture 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),
|
||||
|
||||
/* Legacy DAC Outputs (connected to analog codec DAPM context) */
|
||||
SND_SOC_DAPM_DAC("AIF1 Slot 0 Left", NULL, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_DAC("AIF1 Slot 0 Right", NULL, SND_SOC_NOPM, 0, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route sun8i_codec_legacy_routes[] = {
|
||||
/* Legacy ADC Routes */
|
||||
{ "ADCL", NULL, "AIF1 Slot 0 Left ADC" },
|
||||
{ "ADCR", NULL, "AIF1 Slot 0 Right ADC" },
|
||||
|
||||
/* Legacy DAC Routes */
|
||||
{ "AIF1 Slot 0 Left", NULL, "DACL" },
|
||||
{ "AIF1 Slot 0 Right", NULL, "DACR" },
|
||||
};
|
||||
|
||||
static int sun8i_codec_component_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
|
||||
struct sun8i_codec *scodec = snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
|
||||
/* Add widgets for backward compatibility with old device trees. */
|
||||
if (scodec->quirks->legacy_widgets) {
|
||||
ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_legacy_widgets,
|
||||
ARRAY_SIZE(sun8i_codec_legacy_widgets));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_legacy_routes,
|
||||
ARRAY_SIZE(sun8i_codec_legacy_routes));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops sun8i_codec_dai_ops = {
|
||||
.hw_params = sun8i_codec_hw_params,
|
||||
.set_fmt = sun8i_set_fmt,
|
||||
@ -566,6 +634,8 @@ static int sun8i_codec_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(scodec->regmap);
|
||||
}
|
||||
|
||||
scodec->quirks = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
platform_set_drvdata(pdev, scodec);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
@ -603,8 +673,17 @@ static int sun8i_codec_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sun8i_codec_quirks sun8i_a33_quirks = {
|
||||
.legacy_widgets = true,
|
||||
.lrck_inversion = true,
|
||||
};
|
||||
|
||||
static const struct sun8i_codec_quirks sun50i_a64_quirks = {
|
||||
};
|
||||
|
||||
static const struct of_device_id sun8i_codec_of_match[] = {
|
||||
{ .compatible = "allwinner,sun8i-a33-codec" },
|
||||
{ .compatible = "allwinner,sun8i-a33-codec", .data = &sun8i_a33_quirks },
|
||||
{ .compatible = "allwinner,sun50i-a64-codec", .data = &sun50i_a64_quirks },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun8i_codec_of_match);
|
||||
|
Loading…
Reference in New Issue
Block a user