Unverified Commit 2d32c608 authored by Mark Brown's avatar Mark Brown
Browse files

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
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -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
+108 −29
Original line number Diff line number Diff line
@@ -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;
	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);