Commit 2d32c608 authored by Mark Brown's avatar Mark Brown

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
parents fbe7a5db 7518805f
...@@ -15,7 +15,11 @@ properties: ...@@ -15,7 +15,11 @@ properties:
const: 0 const: 0
compatible: compatible:
const: allwinner,sun8i-a33-codec oneOf:
- items:
- const: allwinner,sun50i-a64-codec
- const: allwinner,sun8i-a33-codec
- const: allwinner,sun8i-a33-codec
reg: reg:
maxItems: 1 maxItems: 1
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/log2.h> #include <linux/log2.h>
...@@ -85,10 +86,16 @@ ...@@ -85,10 +86,16 @@
#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK GENMASK(8, 6) #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK GENMASK(8, 6)
#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK GENMASK(12, 9) #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 sun8i_codec {
struct regmap *regmap; struct regmap *regmap;
struct clk *clk_module; struct clk *clk_module;
struct clk *clk_bus; struct clk *clk_bus;
const struct sun8i_codec_quirks *quirks;
}; };
static int sun8i_codec_runtime_resume(struct device *dev) 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) ...@@ -209,18 +216,19 @@ static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
value << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV); value << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV);
/* /*
* It appears that the DAI and the codec don't share the same * It appears that the DAI and the codec in the A33 SoC don't
* polarity for the LRCK signal when they mean 'normal' and * share the same polarity for the LRCK signal when they mean
* 'inverted' in the datasheet. * 'normal' and 'inverted' in the datasheet.
* *
* Since the DAI here is our regular i2s driver that have been * Since the DAI here is our regular i2s driver that have been
* tested with way more codecs than just this one, it means * tested with way more codecs than just this one, it means
* that the codec probably gets it backward, and we have to * that the codec probably gets it backward, and we have to
* invert the value here. * invert the value here.
*/ */
value ^= scodec->quirks->lrck_inversion;
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
BIT(SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV), BIT(SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV),
!value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV); value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV);
/* DAI format */ /* DAI format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
...@@ -388,22 +396,30 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { ...@@ -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, SND_SOC_DAPM_SUPPLY("ADC", SUN8I_ADC_DIG_CTRL, SUN8I_ADC_DIG_CTRL_ENDA,
0, NULL, 0), 0, NULL, 0),
/* Analog DAC AIF */ /* AIF "DAC" Inputs */
SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Left", "Playback", 0, SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "Playback", 0,
SUN8I_AIF1_DACDAT_CTRL, SUN8I_AIF1_DACDAT_CTRL,
SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0), 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,
SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0), SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),
/* Analog ADC AIF */ /* AIF "ADC" Outputs */
SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Left ADC", "Capture", 0, SND_SOC_DAPM_AIF_IN("AIF1 AD0L", "Capture", 0,
SUN8I_AIF1_ADCDAT_CTRL, SUN8I_AIF1_ADCDAT_CTRL,
SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0L_ENA, 0), 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,
SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0R_ENA, 0), 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 */ /* DAC and ADC Mixers */
SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0, SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
sun8i_dac_mixer_controls), sun8i_dac_mixer_controls),
...@@ -449,40 +465,92 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { ...@@ -449,40 +465,92 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
/* Clock Routes */ /* Clock Routes */
{ "AIF1", NULL, "SYSCLK AIF1" }, { "AIF1", NULL, "SYSCLK AIF1" },
{ "AIF1 PLL", NULL, "AIF1" }, { "AIF1 PLL", NULL, "AIF1" },
{ "RST AIF1", NULL, "AIF1 PLL" }, { "SYSCLK", NULL, "AIF1 PLL" },
{ "RST AIF1", NULL, "SYSCLK" },
{ "MODCLK AFI1", NULL, "RST AIF1" }, { "MODCLK AFI1", NULL, "RST AIF1" },
{ "DAC", NULL, "MODCLK AFI1" }, { "AIF1 AD0L", NULL, "MODCLK AFI1" },
{ "ADC", NULL, "MODCLK AFI1" }, { "AIF1 AD0R", NULL, "MODCLK AFI1" },
{ "AIF1 DA0L", NULL, "MODCLK AFI1" },
{ "AIF1 DA0R", NULL, "MODCLK AFI1" },
{ "RST DAC", NULL, "SYSCLK" }, { "RST DAC", NULL, "SYSCLK" },
{ "MODCLK DAC", NULL, "RST DAC" }, { "MODCLK DAC", NULL, "RST DAC" },
{ "DAC", NULL, "MODCLK DAC" }, { "DAC", NULL, "MODCLK DAC" },
{ "DACL", NULL, "DAC" },
{ "DACR", NULL, "DAC" },
{ "RST ADC", NULL, "SYSCLK" }, { "RST ADC", NULL, "SYSCLK" },
{ "MODCLK ADC", NULL, "RST ADC" }, { "MODCLK ADC", NULL, "RST ADC" },
{ "ADC", NULL, "MODCLK ADC" }, { "ADC", NULL, "MODCLK ADC" },
{ "ADCL", NULL, "ADC" },
{ "ADCR", NULL, "ADC" },
/* DAC Routes */ /* DAC Routes */
{ "AIF1 Slot 0 Right", NULL, "DAC" }, { "DACL", NULL, "Left Digital DAC Mixer" },
{ "AIF1 Slot 0 Left", NULL, "DAC" }, { "DACR", NULL, "Right Digital DAC Mixer" },
/* DAC Mixer Routes */ /* DAC Mixer Routes */
{ "Left Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", { "Left Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L" },
"AIF1 Slot 0 Left"}, { "Left Digital DAC Mixer", "ADC Digital DAC Playback Switch", "ADCL" },
{ "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch",
"AIF1 Slot 0 Right"}, { "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 */ /* ADC Routes */
{ "AIF1 Slot 0 Right ADC", NULL, "ADC" }, { "AIF1 AD0L", NULL, "Left Digital ADC Mixer" },
{ "AIF1 Slot 0 Left ADC", NULL, "ADC" }, { "AIF1 AD0R", NULL, "Right Digital ADC Mixer" },
/* ADC Mixer Routes */ /* ADC Mixer Routes */
{ "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", { "Left Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L" },
"AIF1 Slot 0 Left ADC" }, { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" },
{ "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch",
"AIF1 Slot 0 Right ADC" }, { "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 = { static const struct snd_soc_dai_ops sun8i_codec_dai_ops = {
.hw_params = sun8i_codec_hw_params, .hw_params = sun8i_codec_hw_params,
.set_fmt = sun8i_set_fmt, .set_fmt = sun8i_set_fmt,
...@@ -566,6 +634,8 @@ static int sun8i_codec_probe(struct platform_device *pdev) ...@@ -566,6 +634,8 @@ static int sun8i_codec_probe(struct platform_device *pdev)
return PTR_ERR(scodec->regmap); return PTR_ERR(scodec->regmap);
} }
scodec->quirks = of_device_get_match_data(&pdev->dev);
platform_set_drvdata(pdev, scodec); platform_set_drvdata(pdev, scodec);
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
...@@ -603,8 +673,17 @@ static int sun8i_codec_remove(struct platform_device *pdev) ...@@ -603,8 +673,17 @@ static int sun8i_codec_remove(struct platform_device *pdev)
return 0; 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[] = { 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); MODULE_DEVICE_TABLE(of, sun8i_codec_of_match);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment