From c5fc8be5e748057a249e1812dd742f5aa4282a4b Mon Sep 17 00:00:00 2001 From: Stephen Graf Date: Wed, 26 Mar 2025 17:06:51 +0000 Subject: [PATCH] sunxi-6.13: Updated patch for sound on H616/618 SoCs The Linux kernel now provides an analog codec for H616 SoCs in sun41.codec.c. We will use it. --- .../Sound-for-H616-H618-Allwinner-SOCs.patch | 1327 ++--------------- 1 file changed, 103 insertions(+), 1224 deletions(-) diff --git a/patch/kernel/archive/sunxi-6.13/patches.armbian/Sound-for-H616-H618-Allwinner-SOCs.patch b/patch/kernel/archive/sunxi-6.13/patches.armbian/Sound-for-H616-H618-Allwinner-SOCs.patch index 36eac2aa64..32cf2c0673 100644 --- a/patch/kernel/archive/sunxi-6.13/patches.armbian/Sound-for-H616-H618-Allwinner-SOCs.patch +++ b/patch/kernel/archive/sunxi-6.13/patches.armbian/Sound-for-H616-H618-Allwinner-SOCs.patch @@ -1,69 +1,57 @@ -From 050de33c37da9f6ef591036362b588bb8b68de10 Mon Sep 17 00:00:00 2001 +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Stephen Graf -Date: Thu, 9 May 2024 20:59:34 -0700 -Subject: [PATCH] Sound for H616, H618 Allwinner SOCs +Date: Wed, 26 Mar 2025 17:06:51 +0000 +Subject: Sound for H616, H618 Allwinner SOCs + +The Linux kernel now (v6.13) provides an analog codec +for H616 SoCs in sun41.codec.c. We will use it. + + arch/arm64/boot/dts/allwinner/sun50i-h616-orangepi-zero.dtsi + arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi include/sound/soc-dai.h + sound/soc/Kconfig sound/soc/Makefile sound/soc/soc-core.c + sound/soc/sunxi_v2/Kconfig sound/soc/sunxi_v2/Makefile + sound/soc/sunxi_v2/drv_hdmi.h sound/soc/sunxi_v2/snd_sunxi_ahub.c + sound/soc/sunxi_v2/snd_sunxi_ahub.h sound/soc/sunxi_v2/snd_sunxi_ahub_dam.c + sound/soc/sunxi_v2/snd_sunxi_ahub_dam.h sound/soc/sunxi_v2/snd_sunxi_common.c + sound/soc/sunxi_v2/snd_sunxi_common.h sound/soc/sunxi_v2/snd_sunxi_log.h + sound/soc/sunxi_v2/snd_sunxi_mach.c sound/soc/sunxi_v2/snd_sunxi_mach.h + sound/soc/sunxi_v2/snd_sunxi_mach_utils.c + sound/soc/sunxi_v2/snd_sunxi_mach_utils.h Signed-off-by: Stephen Graf - -Signed-off-by: The-going <48602507+The-going@users.noreply.github.com> -Fix error: incompatible pointer type -Fix warning: no previous prototype -For v6.13.7 --- - .../allwinner/sun50i-h616-orangepi-zero.dtsi | 18 + - .../arm64/boot/dts/allwinner/sun50i-h616.dtsi | 65 +- - include/sound/soc-dai.h | 13 + - sound/soc/Kconfig | 1 + - sound/soc/Makefile | 1 + - sound/soc/soc-core.c | 25 + - sound/soc/sunxi/Kconfig | 8 + - sound/soc/sunxi/Makefile | 1 + - sound/soc/sunxi/sun50iw9-codec.c | 1091 ++++++++++++ - sound/soc/sunxi_v2/Kconfig | 48 + - sound/soc/sunxi_v2/Makefile | 11 + - sound/soc/sunxi_v2/drv_hdmi.h | 63 + - sound/soc/sunxi_v2/snd_sunxi_ahub.c | 1475 +++++++++++++++++ - sound/soc/sunxi_v2/snd_sunxi_ahub.h | 74 + - sound/soc/sunxi_v2/snd_sunxi_ahub_dam.c | 532 ++++++ - sound/soc/sunxi_v2/snd_sunxi_ahub_dam.h | 294 ++++ - sound/soc/sunxi_v2/snd_sunxi_common.c | 267 +++ - sound/soc/sunxi_v2/snd_sunxi_common.h | 67 + - sound/soc/sunxi_v2/snd_sunxi_log.h | 29 + - sound/soc/sunxi_v2/snd_sunxi_mach.c | 479 ++++++ - sound/soc/sunxi_v2/snd_sunxi_mach.h | 20 + - sound/soc/sunxi_v2/snd_sunxi_mach_utils.c | 422 +++++ - sound/soc/sunxi_v2/snd_sunxi_mach_utils.h | 116 ++ - 23 files changed, 5117 insertions(+), 3 deletions(-) - create mode 100644 sound/soc/sunxi/sun50iw9-codec.c - create mode 100644 sound/soc/sunxi_v2/Kconfig - create mode 100644 sound/soc/sunxi_v2/Makefile - create mode 100644 sound/soc/sunxi_v2/drv_hdmi.h - create mode 100644 sound/soc/sunxi_v2/snd_sunxi_ahub.c - create mode 100644 sound/soc/sunxi_v2/snd_sunxi_ahub.h - create mode 100644 sound/soc/sunxi_v2/snd_sunxi_ahub_dam.c - create mode 100644 sound/soc/sunxi_v2/snd_sunxi_ahub_dam.h - create mode 100644 sound/soc/sunxi_v2/snd_sunxi_common.c - create mode 100644 sound/soc/sunxi_v2/snd_sunxi_common.h - create mode 100644 sound/soc/sunxi_v2/snd_sunxi_log.h - create mode 100644 sound/soc/sunxi_v2/snd_sunxi_mach.c - create mode 100644 sound/soc/sunxi_v2/snd_sunxi_mach.h - create mode 100644 sound/soc/sunxi_v2/snd_sunxi_mach_utils.c - create mode 100644 sound/soc/sunxi_v2/snd_sunxi_mach_utils.h + arch/arm64/boot/dts/allwinner/sun50i-h616-orangepi-zero.dtsi | 12 + + arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi | 60 +- + include/sound/soc-dai.h | 13 + + sound/soc/Kconfig | 1 + + sound/soc/Makefile | 1 + + sound/soc/soc-core.c | 25 + + sound/soc/sunxi_v2/Kconfig | 45 + + sound/soc/sunxi_v2/Makefile | 11 + + sound/soc/sunxi_v2/drv_hdmi.h | 63 + + sound/soc/sunxi_v2/snd_sunxi_ahub.c | 1475 ++++++++++ + sound/soc/sunxi_v2/snd_sunxi_ahub.h | 74 + + sound/soc/sunxi_v2/snd_sunxi_ahub_dam.c | 532 ++++ + sound/soc/sunxi_v2/snd_sunxi_ahub_dam.h | 294 ++ + sound/soc/sunxi_v2/snd_sunxi_common.c | 267 ++ + sound/soc/sunxi_v2/snd_sunxi_common.h | 67 + + sound/soc/sunxi_v2/snd_sunxi_log.h | 29 + + sound/soc/sunxi_v2/snd_sunxi_mach.c | 479 +++ + sound/soc/sunxi_v2/snd_sunxi_mach.h | 20 + + sound/soc/sunxi_v2/snd_sunxi_mach_utils.c | 422 +++ + sound/soc/sunxi_v2/snd_sunxi_mach_utils.h | 116 + + 20 files changed, 4005 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616-orangepi-zero.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616-orangepi-zero.dtsi -index 60f643abb3eb..8fd53e25afde 100644 +index 60f643abb..109c33c65 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h616-orangepi-zero.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h616-orangepi-zero.dtsi -@@ -110,6 +110,24 @@ &de { +@@ -108,10 +108,22 @@ &codec { + + &de { status = "okay"; }; -+&codec { -+ allwinner,audio-routing = -+ "Line Out", "LINEOUT"; -+ status = "okay"; -+}; -+ +&ahub_dam_plat { + status = "okay"; +}; @@ -79,11 +67,15 @@ index 60f643abb3eb..8fd53e25afde 100644 &ehci1 { status = "okay"; }; + + /* USB 2 & 3 are on headers only. */ diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi -index 668ed3b9090a..8f992bd4a9d2 100644 +index 62fd294e5..90e65be77 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi -@@ -539,7 +539,8 @@ gic: interrupt-controller@3021000 { +@@ -615,11 +615,12 @@ gic: interrupt-controller@3021000 { + interrupt-controller; + #interrupt-cells = <3>; }; iommu: iommu@30f0000 { @@ -93,16 +85,9 @@ index 668ed3b9090a..8f992bd4a9d2 100644 reg = <0x030f0000 0x10000>; interrupts = ; clocks = <&ccu CLK_BUS_IOMMU>; -@@ -889,14 +890,72 @@ codec: codec@5096000 { - reg = <0x05096000 0x31c>; - interrupts = ; - clocks = <&ccu CLK_BUS_AUDIO_CODEC>, -- <&ccu CLK_AUDIO_CODEC_1X>; -- clock-names = "apb", "codec"; -+ <&ccu CLK_AUDIO_CODEC_1X>, -+ <&ccu CLK_AUDIO_CODEC_4X>; -+ clock-names = "apb", "audio-codec-1x", "audio-codec-4x"; - resets = <&ccu RST_BUS_AUDIO_CODEC>; + resets = <&ccu RST_BUS_IOMMU>; + #iommu-cells = <1>; +@@ -973,10 +974,67 @@ codec: codec@5096000 { dmas = <&dma 6>; dma-names = "tx"; status = "disabled"; @@ -168,11 +153,15 @@ index 668ed3b9090a..8f992bd4a9d2 100644 usbotg: usb@5100000 { compatible = "allwinner,sun50i-h616-musb", "allwinner,sun8i-h3-musb"; + reg = <0x05100000 0x0400>; + clocks = <&ccu CLK_BUS_OTG>; diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h -index aab57c19f62b..91309f090e27 100644 +index aab57c19f..91309f090 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h -@@ -410,6 +410,15 @@ struct snd_soc_dai_driver { +@@ -408,20 +408,33 @@ struct snd_soc_dai_driver { + unsigned int id; + unsigned int base; struct snd_soc_dobj dobj; const struct of_phandle_args *dai_args; @@ -188,7 +177,10 @@ index aab57c19f62b..91309f090e27 100644 /* ops */ const struct snd_soc_dai_ops *ops; const struct snd_soc_cdai_ops *cops; -@@ -420,6 +429,10 @@ struct snd_soc_dai_driver { + + /* DAI capabilities */ + struct snd_soc_pcm_stream capture; + struct snd_soc_pcm_stream playback; unsigned int symmetric_rate:1; unsigned int symmetric_channels:1; unsigned int symmetric_sample_bits:1; @@ -199,11 +191,15 @@ index aab57c19f62b..91309f090e27 100644 }; /* for Playback/Capture */ + struct snd_soc_dai_stream { + struct snd_soc_dapm_widget *widget; diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig -index 5efba76abb31..b1b4693ca49b 100644 +index 5efba76ab..b1b4693ca 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig -@@ -117,6 +117,7 @@ source "sound/soc/starfive/Kconfig" +@@ -115,10 +115,11 @@ source "sound/soc/spear/Kconfig" + source "sound/soc/sprd/Kconfig" + source "sound/soc/starfive/Kconfig" source "sound/soc/sti/Kconfig" source "sound/soc/stm/Kconfig" source "sound/soc/sunxi/Kconfig" @@ -211,11 +207,15 @@ index 5efba76abb31..b1b4693ca49b 100644 source "sound/soc/tegra/Kconfig" source "sound/soc/ti/Kconfig" source "sound/soc/uniphier/Kconfig" + source "sound/soc/ux500/Kconfig" + source "sound/soc/xilinx/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile -index 08baaa11d813..ca53a0853e0f 100644 +index 08baaa11d..ca53a0853 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile -@@ -70,6 +70,7 @@ obj-$(CONFIG_SND_SOC) += starfive/ +@@ -68,10 +68,11 @@ obj-$(CONFIG_SND_SOC) += spear/ + obj-$(CONFIG_SND_SOC) += sprd/ + obj-$(CONFIG_SND_SOC) += starfive/ obj-$(CONFIG_SND_SOC) += sti/ obj-$(CONFIG_SND_SOC) += stm/ obj-$(CONFIG_SND_SOC) += sunxi/ @@ -223,11 +223,15 @@ index 08baaa11d813..ca53a0853e0f 100644 obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += ti/ obj-$(CONFIG_SND_SOC) += uniphier/ + obj-$(CONFIG_SND_SOC) += ux500/ + obj-$(CONFIG_SND_SOC) += xilinx/ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c -index a1dace4bb616..ec05135e25fa 100644 +index a1dace4bb..ec05135e2 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c -@@ -2629,6 +2629,7 @@ struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component, +@@ -2627,10 +2627,11 @@ struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component, + struct snd_soc_dai_driver *dai_drv, + bool legacy_dai_naming) { struct device *dev = component->dev; struct snd_soc_dai *dai; @@ -235,7 +239,11 @@ index a1dace4bb616..ec05135e25fa 100644 lockdep_assert_held(&client_mutex); -@@ -2657,6 +2658,30 @@ struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component, + dai = devm_kzalloc(dev, sizeof(*dai), GFP_KERNEL); + if (dai == NULL) +@@ -2655,10 +2656,34 @@ struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component, + dai->id = component->num_dai; + } if (!dai->name) return NULL; @@ -266,1140 +274,14 @@ index a1dace4bb616..ec05135e25fa 100644 dai->component = component; dai->dev = dev; dai->driver = dai_drv; -diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig -index 753c38c5d554..0f6579ea7143 100644 ---- a/sound/soc/sunxi/Kconfig -+++ b/sound/soc/sunxi/Kconfig -@@ -10,6 +10,14 @@ config SND_SUN4I_CODEC - Select Y or M to add support for the Codec embedded in the Allwinner - A10 and affiliated SoCs. -+config SND_SUN50IW9_CODEC -+ tristate "Allwinner H616 Codec Support" -+ select SND_SOC_GENERIC_DMAENGINE_PCM -+ select REGMAP_MMIO -+ help -+ Select Y or M to add support for the Codec embedded in the Allwinner -+ H616 and affiliated SoCs. -+ - config SND_SUN8I_CODEC - tristate "Allwinner SUN8I audio codec" - depends on OF -diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile -index 7d1a70bcf73e..7a10ad229006 100644 ---- a/sound/soc/sunxi/Makefile -+++ b/sound/soc/sunxi/Makefile -@@ -4,6 +4,7 @@ obj-$(CONFIG_SND_AC100_CODEC) += ac100-codec.o - obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o - obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o - obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o -+obj-$(CONFIG_SND_SUN50IW9_CODEC) += sun50iw9-codec.o - obj-$(CONFIG_SND_SUN50I_CODEC_ANALOG) += sun50i-codec-analog.o - obj-$(CONFIG_SND_SUN8I_CODEC) += sun8i-codec.o - obj-$(CONFIG_SND_SUN8I_ADDA_PR_REGMAP) += sun8i-adda-pr-regmap.o -diff --git a/sound/soc/sunxi/sun50iw9-codec.c b/sound/soc/sunxi/sun50iw9-codec.c -new file mode 100644 -index 000000000000..a9dfc1a031e4 ---- /dev/null -+++ b/sound/soc/sunxi/sun50iw9-codec.c -@@ -0,0 +1,1091 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright 2014 Emilio López -+ * Copyright 2014 Jon Smirl -+ * Copyright 2015 Maxime Ripard -+ * Copyright 2015 Adam Sampson -+ * Copyright 2016 Chen-Yu Tsai -+ * Copyright 2021 gryzun -+ * -+ * Based on the Allwinner SDK driver, released under the GPL. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define SUNXI_DAC_DPC (0x00) -+#define SUNXI_DAC_DPC_EN_DA (31) -+#define SUNXI_DAC_DPC_DVOL (12) -+#define SUNXI_DAC_DPC_HPF_EN (18) -+ -+#define SUNXI_DAC_FIFOC (0x10) -+#define SUNXI_DAC_FIFOC_DAC_FS (29) -+#define SUNXI_DAC_FIFOC_TX_FIFO_MODE (24) -+#define SUNXI_DAC_FIFOC_DRQ_CLR_CNT (21) -+#define SUNXI_DAC_FIFOC_MONO_EN (6) -+#define SUNXI_DAC_FIFOC_TX_SAMPLE_BITS (5) -+#define SUNXI_DAC_FIFOC_DAC_DRQ_EN (4) -+#define SUNXI_DAC_FIFOC_FIFO_FLUSH (0) -+ -+#define SUNXI_DAC_FIFO_STA (0x14) -+#define SUNXI_DAC_TXE_INT (3) -+#define SUNXI_DAC_TXU_INT (2) -+#define SUNXI_DAC_TXO_INT (1) -+#define SUNXI_DAC_TXDATA (0x20) -+#define SUNXI_DAC_CNT (0x24) -+#define SUNXI_DAC_DG_REG (0x28) -+#define SUNXI_DAC_DAP_CTL (0xf0) -+ -+#define SUNXI_DAC_AC_DAC_REG (0x310) -+#define SUNXI_DAC_LEN (15) -+#define SUNXI_DAC_REN (14) -+#define SUNXI_LINEOUTL_EN (13) -+#define SUNXI_LMUTE (12) -+#define SUNXI_LINEOUTR_EN (11) -+#define SUNXI_RMUTE (10) -+#define SUNXI_RSWITCH (9) -+#define SUNXI_RAMPEN (8) -+#define SUNXI_LINEOUTL_SEL (6) -+#define SUNXI_LINEOUTR_SEL (5) -+#define SUNXI_LINEOUT_VOL (0) -+ -+#define SUNXI_DAC_AC_MIXER_REG (0x314) -+#define SUNXI_LMIX_LDAC (21) -+#define SUNXI_LMIX_RDAC (20) -+#define SUNXI_RMIX_RDAC (17) -+#define SUNXI_RMIX_LDAC (16) -+#define SUNXI_LMIXEN (11) -+#define SUNXI_RMIXEN (10) -+ -+#define SUNXI_DAC_AC_RAMP_REG (0x31c) -+#define SUNXI_RAMP_STEP (4) -+#define SUNXI_RDEN (0) -+ -+#define LABEL(constant) \ -+ { \ -+#constant, constant, 0 \ -+ } -+#define LABEL_END \ -+ { \ -+ NULL, 0, -1 \ -+ } -+ -+struct audiocodec_reg_label -+{ -+ const char *name; -+ const unsigned int address; -+ int value; -+}; -+ -+static struct audiocodec_reg_label reg_labels[] = { -+ LABEL(SUNXI_DAC_DPC), -+ LABEL(SUNXI_DAC_FIFOC), -+ LABEL(SUNXI_DAC_FIFO_STA), -+ LABEL(SUNXI_DAC_CNT), -+ LABEL(SUNXI_DAC_DG_REG), -+ LABEL(SUNXI_DAC_DAP_CTL), -+ LABEL(SUNXI_DAC_AC_DAC_REG), -+ LABEL(SUNXI_DAC_AC_MIXER_REG), -+ LABEL(SUNXI_DAC_AC_RAMP_REG), -+ LABEL_END, -+}; -+ -+struct regmap *codec_regmap_debug = NULL; -+ -+struct sun50i_h616_codec -+{ -+ unsigned char *name; -+ struct device *dev; -+ 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 */ -+ struct regmap_field *reg_adc_fifoc; -+ -+ struct snd_dmaengine_dai_dma_data playback_dma_data; -+}; -+ -+static void sun50i_h616_codec_start_playback(struct sun50i_h616_codec *scodec) -+{ -+ /* Flush TX FIFO */ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_FIFOC, -+ BIT(SUNXI_DAC_FIFOC_FIFO_FLUSH), -+ BIT(SUNXI_DAC_FIFOC_FIFO_FLUSH)); -+ -+ /* Enable DAC DRQ */ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_FIFOC, -+ BIT(SUNXI_DAC_FIFOC_DAC_DRQ_EN), -+ BIT(SUNXI_DAC_FIFOC_DAC_DRQ_EN)); -+} -+ -+static void sun50i_h616_codec_stop_playback(struct sun50i_h616_codec *scodec) -+{ -+ /* Disable DAC DRQ */ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_FIFOC, -+ BIT(SUNXI_DAC_FIFOC_DAC_DRQ_EN), -+ 0); -+} -+ -+static int sun50i_h616_codec_trigger(struct snd_pcm_substream *substream, int cmd, -+ struct snd_soc_dai *dai) -+{ -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct sun50i_h616_codec *scodec = snd_soc_card_get_drvdata(rtd->card); -+ -+ switch (cmd) -+ { -+ case SNDRV_PCM_TRIGGER_START: -+ case SNDRV_PCM_TRIGGER_RESUME: -+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) -+ regmap_update_bits(scodec->regmap, -+ SUNXI_DAC_FIFOC, -+ (0x1 << SUNXI_DAC_FIFOC_DAC_DRQ_EN), -+ (0x1 << SUNXI_DAC_FIFOC_DAC_DRQ_EN)); -+ break; -+ case SNDRV_PCM_TRIGGER_STOP: -+ case SNDRV_PCM_TRIGGER_SUSPEND: -+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) -+ regmap_update_bits(scodec->regmap, -+ SUNXI_DAC_FIFOC, -+ (0x1 << SUNXI_DAC_FIFOC_DAC_DRQ_EN), -+ (0x0 << SUNXI_DAC_FIFOC_DAC_DRQ_EN)); -+ break; -+ default: -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+static int sun50i_h616_codec_prepare(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct sun50i_h616_codec *scodec = snd_soc_card_get_drvdata(rtd->card); -+ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_FIFOC, -+ (0x1 << SUNXI_DAC_FIFOC_FIFO_FLUSH), -+ (0x1 << SUNXI_DAC_FIFOC_FIFO_FLUSH)); -+ regmap_write(scodec->regmap, SUNXI_DAC_FIFO_STA, -+ (0x1 << SUNXI_DAC_TXE_INT | 1 << SUNXI_DAC_TXU_INT | 0x1 << SUNXI_DAC_TXO_INT)); -+ regmap_write(scodec->regmap, SUNXI_DAC_CNT, 0); -+ -+ return 0; -+} -+ -+static unsigned long sun50i_h616_codec_get_mod_freq(struct snd_pcm_hw_params *params) -+{ -+ unsigned int rate = params_rate(params); -+ -+ switch (rate) -+ { -+ case 176400: -+ case 88200: -+ case 44100: -+ case 33075: -+ case 22050: -+ case 14700: -+ case 11025: -+ case 7350: -+ return 22579200; -+ -+ case 192000: -+ case 96000: -+ case 48000: -+ case 32000: -+ case 24000: -+ case 16000: -+ case 12000: -+ case 8000: -+ return 24576000; -+ -+ default: -+ return 0; -+ } -+} -+ -+static int sun50i_h616_codec_get_hw_rate(struct snd_pcm_hw_params *params) -+{ -+ unsigned int rate = params_rate(params); -+ -+ switch (rate) -+ { -+ case 192000: -+ case 176400: -+ return 6; -+ -+ case 96000: -+ case 88200: -+ return 7; -+ -+ case 48000: -+ case 44100: -+ return 0; -+ -+ case 32000: -+ case 33075: -+ return 1; -+ -+ case 24000: -+ case 22050: -+ return 2; -+ -+ case 16000: -+ case 14700: -+ return 3; -+ -+ case 12000: -+ case 11025: -+ return 4; -+ -+ case 8000: -+ case 7350: -+ return 5; -+ -+ default: -+ return -EINVAL; -+ } -+} -+ -+static int sun50i_h616_codec_hw_params_playback(struct sun50i_h616_codec *scodec, -+ struct snd_pcm_hw_params *params, -+ unsigned int hwrate) -+{ -+ u32 val; -+ -+ /* Set DAC sample rate */ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_FIFOC, -+ 7 << SUNXI_DAC_FIFOC_DAC_FS, -+ hwrate << SUNXI_DAC_FIFOC_DAC_FS); -+ -+ /* Set the number of channels we want to use */ -+ if (params_channels(params) == 1) -+ val = BIT(SUNXI_DAC_FIFOC_MONO_EN); -+ else -+ val = 0; -+ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_FIFOC, -+ BIT(SUNXI_DAC_FIFOC_MONO_EN), -+ val); -+ -+ /* Set the number of sample bits to either 16 or 24 bits */ -+ if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) -+ { -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_FIFOC, -+ BIT(SUNXI_DAC_FIFOC_TX_SAMPLE_BITS), -+ BIT(SUNXI_DAC_FIFOC_TX_SAMPLE_BITS)); -+ -+ /* Set TX FIFO mode to padding the LSBs with 0 */ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_FIFOC, -+ BIT(SUNXI_DAC_FIFOC_TX_FIFO_MODE), -+ 0); -+ -+ scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; -+ } -+ else -+ { -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_FIFOC, -+ BIT(SUNXI_DAC_FIFOC_TX_SAMPLE_BITS), -+ 0); -+ -+ /* Set TX FIFO mode to repeat the MSB */ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_FIFOC, -+ BIT(SUNXI_DAC_FIFOC_TX_FIFO_MODE), -+ BIT(SUNXI_DAC_FIFOC_TX_FIFO_MODE)); -+ -+ scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; -+ } -+ -+ return 0; -+} -+ -+struct sample_rate -+{ -+ unsigned int samplerate; -+ unsigned int rate_bit; -+}; -+ -+static const struct sample_rate sample_rate_conv[] = { -+ {44100, 0}, -+ {48000, 0}, -+ {8000, 5}, -+ {32000, 1}, -+ {22050, 2}, -+ {24000, 2}, -+ {16000, 3}, -+ {11025, 4}, -+ {12000, 4}, -+ {192000, 6}, -+ {96000, 7}, -+}; -+ -+static int sun50i_h616_codec_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) -+{ -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct sun50i_h616_codec *scodec = snd_soc_card_get_drvdata(rtd->card); -+ unsigned long clk_freq; -+ int ret, hwrate; -+ int i; -+ -+ clk_freq = sun50i_h616_codec_get_mod_freq(params); -+ if (!clk_freq) -+ return -EINVAL; -+ -+ ret = clk_set_rate(scodec->clk_module, clk_freq * 2); -+ if (ret) -+ return ret; -+ -+ hwrate = sun50i_h616_codec_get_hw_rate(params); -+ if (hwrate < 0) -+ return hwrate; -+ -+ switch (params_format(params)) -+ { -+ case SNDRV_PCM_FORMAT_S16_LE: -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) -+ { -+ regmap_update_bits(scodec->regmap, -+ SUNXI_DAC_FIFOC, -+ (0x3 << SUNXI_DAC_FIFOC_TX_FIFO_MODE), -+ (0x3 << SUNXI_DAC_FIFOC_TX_FIFO_MODE)); -+ regmap_update_bits(scodec->regmap, -+ SUNXI_DAC_FIFOC, -+ (0x1 << SUNXI_DAC_FIFOC_TX_SAMPLE_BITS), -+ (0x0 << SUNXI_DAC_FIFOC_TX_SAMPLE_BITS)); -+ } -+ break; -+ case SNDRV_PCM_FORMAT_S24_LE: -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) -+ { -+ regmap_update_bits(scodec->regmap, -+ SUNXI_DAC_FIFOC, -+ (0x3 << SUNXI_DAC_FIFOC_TX_FIFO_MODE), -+ (0x0 << SUNXI_DAC_FIFOC_TX_FIFO_MODE)); -+ regmap_update_bits(scodec->regmap, -+ SUNXI_DAC_FIFOC, -+ (0x1 << SUNXI_DAC_FIFOC_TX_SAMPLE_BITS), -+ (0x1 << SUNXI_DAC_FIFOC_TX_SAMPLE_BITS)); -+ } -+ break; -+ default: -+ break; -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(sample_rate_conv); i++) -+ { -+ if (sample_rate_conv[i].samplerate == params_rate(params)) -+ { -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) -+ { -+ regmap_update_bits(scodec->regmap, -+ SUNXI_DAC_FIFOC, -+ (0x7 << SUNXI_DAC_FIFOC_DAC_FS), -+ (sample_rate_conv[i].rate_bit << SUNXI_DAC_FIFOC_DAC_FS)); -+ } -+ } -+ } -+ -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) -+ { -+ switch (params_channels(params)) -+ { -+ case 1: -+ regmap_update_bits(scodec->regmap, -+ SUNXI_DAC_FIFOC, -+ (0x1 << SUNXI_DAC_FIFOC_MONO_EN), -+ (0x1 << SUNXI_DAC_FIFOC_MONO_EN)); -+ break; -+ case 2: -+ regmap_update_bits(scodec->regmap, -+ SUNXI_DAC_FIFOC, -+ (0x1 << SUNXI_DAC_FIFOC_MONO_EN), -+ (0x0 << SUNXI_DAC_FIFOC_MONO_EN)); -+ break; -+ default: -+ pr_err("[%s] Playback cannot support %d channels.\n", -+ __func__, params_channels(params)); -+ return -EINVAL; -+ } -+ } -+ -+ return 0; -+} -+ -+static unsigned int sun50i_h616_codec_src_rates[] = { -+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, -+ 44100, 48000, 96000, 192000}; -+ -+static struct snd_pcm_hw_constraint_list sun50i_h616_codec_constraints = { -+ .count = ARRAY_SIZE(sun50i_h616_codec_src_rates), -+ .list = sun50i_h616_codec_src_rates, -+}; -+ -+static int sun50i_h616_codec_startup(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct sun50i_h616_codec *scodec = snd_soc_card_get_drvdata(rtd->card); -+ -+ snd_pcm_hw_constraint_list(substream->runtime, 0, -+ SNDRV_PCM_HW_PARAM_RATE, &sun50i_h616_codec_constraints); -+ -+ /* -+ * Stop issuing DRQ when we have room for less than 16 samples -+ * in our TX FIFO -+ */ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_FIFOC, -+ 3 << SUNXI_DAC_FIFOC_DRQ_CLR_CNT, -+ 3 << SUNXI_DAC_FIFOC_DRQ_CLR_CNT); -+ -+ return clk_prepare_enable(scodec->clk_module); -+} -+ -+static void sun50i_h616_codec_shutdown(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct sun50i_h616_codec *scodec = snd_soc_card_get_drvdata(rtd->card); -+ -+ clk_disable_unprepare(scodec->clk_module); -+} -+ -+static const struct snd_soc_dai_ops sun50i_h616_codec_dai_ops = { -+ .startup = sun50i_h616_codec_startup, -+ .shutdown = sun50i_h616_codec_shutdown, -+ .trigger = sun50i_h616_codec_trigger, -+ .hw_params = sun50i_h616_codec_hw_params, -+ .prepare = sun50i_h616_codec_prepare, -+}; -+ -+static struct snd_soc_dai_driver sun50i_h616_codec_dai = { -+ .name = "Codec", -+ .ops = &sun50i_h616_codec_dai_ops, -+ .playback = { -+ .stream_name = "Codec Playback", -+ .channels_min = 1, -+ .channels_max = 2, -+ .rate_min = 8000, -+ .rate_max = 192000, -+ .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | -+ SNDRV_PCM_FMTBIT_S24_LE, -+ .sig_bits = 24, -+ }, -+}; -+ -+static int sunxi_lineout_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *k, int event) -+{ -+ struct sun50i_h616_codec *scodec = snd_soc_card_get_drvdata(w->dapm->card); -+ -+ switch (event) -+ { -+ case SND_SOC_DAPM_POST_PMU: -+ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_AC_RAMP_REG, -+ (0x1 << SUNXI_RDEN), (0x1 << SUNXI_RDEN)); -+ msleep(25); -+ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_AC_DAC_REG, -+ (0x1 << SUNXI_LINEOUTL_EN) | (0x1 << SUNXI_LINEOUTR_EN), -+ (0x1 << SUNXI_LINEOUTL_EN) | (0x1 << SUNXI_LINEOUTR_EN)); -+ break; -+ case SND_SOC_DAPM_PRE_PMD: -+ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_AC_RAMP_REG, -+ (0x1 << SUNXI_RDEN), (0x0 << SUNXI_RDEN)); -+ msleep(25); -+ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_AC_DAC_REG, -+ (0x1 << SUNXI_LINEOUTL_EN) | (0x1 << SUNXI_LINEOUTR_EN), -+ (0x0 << SUNXI_LINEOUTL_EN) | (0x0 << SUNXI_LINEOUTR_EN)); -+ -+ break; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ -+static const DECLARE_TLV_DB_SCALE(digital_tlv, 0, -116, -7424); -+static const DECLARE_TLV_DB_SCALE(linein_to_l_r_mix_vol_tlv, -450, 150, 0); -+static const DECLARE_TLV_DB_SCALE(fmin_to_l_r_mix_vol_tlv, -450, 150, 0); -+ -+static const unsigned int lineout_tlv[] = { -+ TLV_DB_RANGE_HEAD(2), -+ 0, -+ 0, -+ TLV_DB_SCALE_ITEM(0, 0, 1), -+ 1, -+ 31, -+ TLV_DB_SCALE_ITEM(-4350, 150, 1), -+}; -+ -+/*lineoutL mux select */ -+const char *const left_lineoutl_text[] = { -+ "LOMixer", -+ "LROMixer", -+}; -+ -+static const struct soc_enum left_lineout_enum = -+ SOC_ENUM_SINGLE(SUNXI_DAC_AC_DAC_REG, SUNXI_LINEOUTL_SEL, -+ ARRAY_SIZE(left_lineoutl_text), left_lineoutl_text); -+ -+static const struct snd_kcontrol_new left_lineout_mux = -+ SOC_DAPM_ENUM("Left LINEOUT Mux", left_lineout_enum); -+ -+/*lineoutR mux select */ -+const char *const right_lineoutr_text[] = { -+ "ROMixer", -+ "LROMixer", -+}; -+ -+static const struct soc_enum right_lineout_enum = -+ SOC_ENUM_SINGLE(SUNXI_DAC_AC_DAC_REG, SUNXI_LINEOUTR_SEL, -+ ARRAY_SIZE(right_lineoutr_text), right_lineoutr_text); -+ -+static const struct snd_kcontrol_new right_lineout_mux = -+ SOC_DAPM_ENUM("Right LINEOUT Mux", right_lineout_enum); -+ -+static const struct snd_kcontrol_new sun50i_h616_codec_codec_controls[] = { -+ -+ SOC_SINGLE_TLV("digital volume", SUNXI_DAC_DPC, -+ SUNXI_DAC_DPC_DVOL, 0x3F, 0, digital_tlv), -+ -+ SOC_SINGLE_TLV("LINEOUT volume", SUNXI_DAC_AC_DAC_REG, -+ SUNXI_LINEOUT_VOL, 0x1F, 0, lineout_tlv), -+}; -+ -+static const struct snd_kcontrol_new left_output_mixer[] = { -+ SOC_DAPM_SINGLE("DACL Switch", SUNXI_DAC_AC_MIXER_REG, SUNXI_LMIX_LDAC, 1, 0), -+ SOC_DAPM_SINGLE("DACR Switch", SUNXI_DAC_AC_MIXER_REG, SUNXI_LMIX_RDAC, 1, 0), -+}; -+ -+static const struct snd_kcontrol_new right_output_mixer[] = { -+ SOC_DAPM_SINGLE("DACL Switch", SUNXI_DAC_AC_MIXER_REG, SUNXI_RMIX_LDAC, 1, 0), -+ SOC_DAPM_SINGLE("DACR Switch", SUNXI_DAC_AC_MIXER_REG, SUNXI_RMIX_RDAC, 1, 0), -+}; -+ -+static const struct snd_soc_dapm_widget sun50i_h616_codec_codec_widgets[] = { -+ -+ /* Digital parts of the DACs */ -+ SND_SOC_DAPM_SUPPLY("DAC Enable", SUNXI_DAC_DPC, -+ SUNXI_DAC_DPC_EN_DA, 0, NULL, 0), -+ -+ SND_SOC_DAPM_AIF_IN_E("DACL", "Codec Playback", 0, SUNXI_DAC_AC_DAC_REG, SUNXI_DAC_LEN, 0, -+ NULL, 0), -+ SND_SOC_DAPM_AIF_IN_E("DACR", "Codec Playback", 0, SUNXI_DAC_AC_DAC_REG, SUNXI_DAC_REN, 0, -+ NULL, 0), -+ -+ SND_SOC_DAPM_MIXER("Left Output Mixer", SUNXI_DAC_AC_MIXER_REG, SUNXI_LMIXEN, 0, -+ left_output_mixer, ARRAY_SIZE(left_output_mixer)), -+ SND_SOC_DAPM_MIXER("Right Output Mixer", SUNXI_DAC_AC_MIXER_REG, SUNXI_RMIXEN, 0, -+ right_output_mixer, ARRAY_SIZE(right_output_mixer)), -+ -+ SND_SOC_DAPM_MUX("Left LINEOUT Mux", SND_SOC_NOPM, -+ 0, 0, &left_lineout_mux), -+ SND_SOC_DAPM_MUX("Right LINEOUT Mux", SND_SOC_NOPM, -+ 0, 0, &right_lineout_mux), -+ -+ SND_SOC_DAPM_OUTPUT("LINEOUTL"), -+ SND_SOC_DAPM_OUTPUT("LINEOUTR"), -+ -+ SND_SOC_DAPM_LINE("LINEOUT", sunxi_lineout_event), -+}; -+ -+static const struct snd_soc_component_driver sun50i_h616_codec_codec = { -+ .controls = sun50i_h616_codec_codec_controls, -+ .num_controls = ARRAY_SIZE(sun50i_h616_codec_codec_controls), -+ .dapm_widgets = sun50i_h616_codec_codec_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sun50i_h616_codec_codec_widgets), -+ .idle_bias_on = 1, -+ .use_pmdown_time = 1, -+ .endianness = 1, -+}; -+ -+static const struct snd_soc_component_driver sun50i_h616_codec_component = { -+ .name = "sun50i_h616-codec", -+ .legacy_dai_naming = 1, -+#ifdef CONFIG_DEBUG_FS -+ .debugfs_prefix = "cpu", -+#endif -+}; -+ -+#define SUN50IW9_CODEC_RATES (SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT) -+#define SUN50IW9_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) -+ -+static int sun50i_h616_codec_dai_probe(struct snd_soc_dai *dai) -+{ -+ struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai); -+ struct sun50i_h616_codec *scodec = snd_soc_card_get_drvdata(card); -+ -+ snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data, -+ NULL); -+ -+ return 0; -+} -+ -+static struct snd_soc_dai_driver dummy_cpu_dai = { -+ .name = "sun50i_h616-codec-cpu-dai", -+ .probe = sun50i_h616_codec_dai_probe, -+ .playback = { -+ .stream_name = "Playback", -+ .channels_min = 1, -+ .channels_max = 2, -+ .rates = SUN50IW9_CODEC_RATES, -+ .formats = SUN50IW9_CODEC_FORMATS, -+ .sig_bits = 24, -+ }, -+}; -+ -+static struct snd_soc_dai_link *sun50i_h616_codec_create_link(struct device *dev, -+ int *num_links) -+{ -+ struct snd_soc_dai_link *link = devm_kzalloc(dev, sizeof(*link), -+ GFP_KERNEL); -+ struct snd_soc_dai_link_component *dlc = devm_kzalloc(dev, -+ 3 * sizeof(*dlc), GFP_KERNEL); -+ if (!link || !dlc) -+ return NULL; -+ -+ link->cpus = &dlc[0]; -+ link->codecs = &dlc[1]; -+ link->platforms = &dlc[2]; -+ -+ link->num_cpus = 1; -+ link->num_codecs = 1; -+ link->num_platforms = 1; -+ -+ link->name = "cdc"; -+ link->stream_name = "CDC PCM"; -+ link->codecs->dai_name = "Codec"; -+ link->cpus->dai_name = dev_name(dev); -+ link->codecs->name = dev_name(dev); -+ link->platforms->name = dev_name(dev); -+ link->dai_fmt = SND_SOC_DAIFMT_I2S; -+ link->playback_only = true; -+ link->capture_only = false; -+ -+ *num_links = 1; -+ -+ return link; -+}; -+ -+static int sun50i_h616_codec_spk_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *k, int event) -+{ -+ struct sun50i_h616_codec *scodec = snd_soc_card_get_drvdata(w->dapm->card); -+ -+ gpiod_set_value_cansleep(scodec->gpio_pa, -+ !!SND_SOC_DAPM_EVENT_ON(event)); -+ -+ if (SND_SOC_DAPM_EVENT_ON(event)) -+ { -+ /* -+ * Need a delay to wait for DAC to push the data. 700ms seems -+ * to be the best compromise not to feel this delay while -+ * playing a sound. -+ */ -+ msleep(700); -+ } -+ -+ return 0; -+} -+ -+static const struct snd_soc_dapm_widget sun6i_codec_card_dapm_widgets[] = { -+ SND_SOC_DAPM_LINE("Line Out", NULL), -+ SND_SOC_DAPM_SPK("Speaker", sun50i_h616_codec_spk_event), -+}; -+ -+/* Connect digital side enables to analog side widgets */ -+static const struct snd_soc_dapm_route sun8i_codec_card_routes[] = { -+ /* DAC Routes */ -+ {"DACR", NULL, "DAC Enable"}, -+ {"DACL", NULL, "DAC Enable"}, -+ -+ {"Left Output Mixer", "DACR Switch", "DACR"}, -+ {"Left Output Mixer", "DACL Switch", "DACL"}, -+ -+ {"Right Output Mixer", "DACL Switch", "DACL"}, -+ {"Right Output Mixer", "DACR Switch", "DACR"}, -+ -+ {"Left LINEOUT Mux", "LOMixer", "Left Output Mixer"}, -+ {"Left LINEOUT Mux", "LROMixer", "Right Output Mixer"}, -+ {"Right LINEOUT Mux", "ROMixer", "Right Output Mixer"}, -+ {"Right LINEOUT Mux", "LROMixer", "Left Output Mixer"}, -+ -+ {"LINEOUTL", NULL, "Left LINEOUT Mux"}, -+ {"LINEOUTR", NULL, "Right LINEOUT Mux"}, -+ -+ {"LINEOUT", NULL, "LINEOUTL"}, -+ {"LINEOUT", NULL, "LINEOUTR"}, -+ -+ {"Speaker", NULL, "LINEOUTL"}, -+ {"Speaker", NULL, "LINEOUTR"}, -+}; -+ -+static const struct snd_kcontrol_new sunxi_card_controls[] = { -+ SOC_DAPM_PIN_SWITCH("LINEOUT"), -+}; -+ -+static struct snd_soc_card *sun50i_h616_codec_create_card(struct device *dev) -+{ -+ struct snd_soc_card *card; -+ int ret; -+ -+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); -+ if (!card) -+ return ERR_PTR(-ENOMEM); -+ -+ card->dai_link = sun50i_h616_codec_create_link(dev, &card->num_links); -+ if (!card->dai_link) -+ return ERR_PTR(-ENOMEM); -+ -+ card->dev = dev; -+ card->owner = THIS_MODULE; -+ card->name = "audiocodec"; -+ card->controls = sunxi_card_controls; -+ card->num_controls = ARRAY_SIZE(sunxi_card_controls), -+ card->dapm_widgets = sun6i_codec_card_dapm_widgets; -+ card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets); -+ card->dapm_routes = sun8i_codec_card_routes; -+ card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes); -+ card->fully_routed = true; -+ -+ ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing"); -+ if (ret) -+ dev_warn(dev, "failed to parse audio-routing: %d\n", ret); -+ -+ return card; -+}; -+ -+static const struct regmap_config sun50i_h616_codec_regmap_config = { -+ .reg_bits = 32, -+ .reg_stride = 4, -+ .val_bits = 32, -+ .max_register = SUNXI_DAC_AC_RAMP_REG, -+ .cache_type = REGCACHE_NONE, -+}; -+ -+struct sun50i_h616_codec_quirks -+{ -+ const struct regmap_config *regmap_config; -+ const struct snd_soc_component_driver *codec; -+ struct snd_soc_card *(*create_card)(struct device *dev); -+ 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 sun50i_h616_codec_quirks sun50i_h616_codec_quirks = { -+ .regmap_config = &sun50i_h616_codec_regmap_config, -+ .codec = &sun50i_h616_codec_codec, -+ .create_card = sun50i_h616_codec_create_card, -+ .reg_dac_txdata = SUNXI_DAC_TXDATA, -+ .has_reset = true, -+}; -+ -+static const struct of_device_id sun50i_h616_codec_of_match[] = { -+ { -+ .compatible = "allwinner,sun50i-h616-codec", -+ .data = &sun50i_h616_codec_quirks, -+ }, -+ {}}; -+MODULE_DEVICE_TABLE(of, sun50i_h616_codec_of_match); -+ -+static ssize_t show_audio_reg(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ int count = 0, i = 0; -+ unsigned int reg_val; -+ unsigned int size = ARRAY_SIZE(reg_labels); -+ -+ count += sprintf(buf, "dump audiocodec reg:\n"); -+ -+ while ((i < size) && (reg_labels[i].name != NULL)) -+ { -+ regmap_read(codec_regmap_debug, -+ reg_labels[i].address, ®_val); -+ count += sprintf(buf + count, "%-20s [0x%03x]: 0x%-10x save_val:0x%x\n", -+ reg_labels[i].name, (reg_labels[i].address), -+ reg_val, reg_labels[i].value); -+ i++; -+ } -+ -+ return count; -+} -+ -+static DEVICE_ATTR(audio_reg, 0644, show_audio_reg, NULL); -+ -+static struct attribute *audio_debug_attrs[] = { -+ &dev_attr_audio_reg.attr, -+ NULL, -+}; -+ -+static struct attribute_group audio_debug_attr_group = { -+ .name = "audio_reg_debug", -+ .attrs = audio_debug_attrs, -+}; -+ -+static void sunxi_codec_init(struct sun50i_h616_codec *scodec) -+{ -+ /* Disable DRC function for playback */ -+ regmap_write(scodec->regmap, SUNXI_DAC_DAP_CTL, 0); -+ -+ /* Enable HPF(high passed filter) */ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_DPC, -+ (0x1 << SUNXI_DAC_DPC_HPF_EN), (0x1 << SUNXI_DAC_DPC_HPF_EN)); -+ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_AC_DAC_REG, -+ (0x1f << SUNXI_LINEOUT_VOL), -+ (0x1a << SUNXI_LINEOUT_VOL)); -+ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_DPC, -+ (0x3f << SUNXI_DAC_DPC_DVOL), (0 << SUNXI_DAC_DPC_DVOL)); -+ -+ /* Mixer to channel LINEOUT MUTE control init */ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_AC_DAC_REG, -+ (0x1 << SUNXI_LMUTE), (0x1 << SUNXI_LMUTE)); -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_AC_DAC_REG, -+ (0x1 << SUNXI_RMUTE), (0x1 << SUNXI_RMUTE)); -+ -+ /* ramp func about */ -+ if (0) -+ { -+ /* Not used the ramp func cause there is the MUTE to avoid pop noise */ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_AC_DAC_REG, -+ (0x1 << SUNXI_RSWITCH), (0x1 << SUNXI_RSWITCH)); -+ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_AC_DAC_REG, -+ (0x1 << SUNXI_RAMPEN), (0x0 << SUNXI_RAMPEN)); -+ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_AC_RAMP_REG, -+ (0x7 << SUNXI_RAMP_STEP), (0x0 << SUNXI_RAMP_STEP)); -+ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_AC_RAMP_REG, -+ (0x1 << SUNXI_RDEN), (0x0 << SUNXI_RDEN)); -+ } -+ else -+ { -+ /* If no MUTE to avoid pop, just use the ramp func to avoid it */ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_AC_DAC_REG, -+ (0x1 << SUNXI_RSWITCH), (0x0 << SUNXI_RSWITCH)); -+ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_AC_DAC_REG, -+ (0x1 << SUNXI_RAMPEN), (0x1 << SUNXI_RAMPEN)); -+ -+ regmap_update_bits(scodec->regmap, SUNXI_DAC_AC_RAMP_REG, -+ (0x7 << SUNXI_RAMP_STEP), (0x1 << SUNXI_RAMP_STEP)); -+ } -+} -+ -+static int sun50i_h616_codec_probe(struct platform_device *pdev) -+{ -+ struct snd_soc_card *card; -+ struct sun50i_h616_codec *scodec; -+ const struct sun50i_h616_codec_quirks *quirks; -+ struct resource *res; -+ void __iomem *base; -+ int ret; -+ -+ scodec = devm_kzalloc(&pdev->dev, sizeof(struct sun50i_h616_codec), GFP_KERNEL); -+ if (!scodec) -+ return -ENOMEM; -+ -+ scodec->dev = &pdev->dev; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ base = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(base)) -+ return PTR_ERR(base); -+ -+ quirks = of_device_get_match_data(&pdev->dev); -+ if (quirks == NULL) -+ { -+ dev_err(&pdev->dev, "Failed to determine the quirks to use\n"); -+ return -ENODEV; -+ } -+ -+ scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base, -+ quirks->regmap_config); -+ if (IS_ERR(scodec->regmap)) -+ { -+ dev_err(&pdev->dev, "Failed to create our regmap\n"); -+ return PTR_ERR(scodec->regmap); -+ } -+ -+ /* Get the clocks from the DT */ -+ scodec->clk_apb = devm_clk_get(&pdev->dev, "apb"); -+ if (IS_ERR(scodec->clk_apb)) -+ { -+ dev_err(&pdev->dev, "Failed to get the APB clock\n"); -+ return PTR_ERR(scodec->clk_apb); -+ } -+ -+ scodec->clk_module = devm_clk_get(&pdev->dev, "audio-codec-1x"); -+ if (IS_ERR(scodec->clk_module)) -+ { -+ dev_err(&pdev->dev, "Failed to get the codec module clock\n"); -+ return PTR_ERR(scodec->clk_module); -+ } -+ -+ if (quirks->has_reset) -+ { -+ scodec->rst = devm_reset_control_get_exclusive(&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)) -+ { -+ ret = PTR_ERR(scodec->gpio_pa); -+ if (ret != -EPROBE_DEFER) -+ dev_err(&pdev->dev, "Failed to get pa gpio: %d\n", ret); -+ return ret; -+ } -+ -+ /* Enable the bus clock */ -+ if (clk_prepare_enable(scodec->clk_apb)) -+ { -+ dev_err(&pdev->dev, "Failed to enable the APB clock\n"); -+ 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; -+ scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; -+ -+ ret = devm_snd_soc_register_component(&pdev->dev, quirks->codec, -+ &sun50i_h616_codec_dai, 1); -+ if (ret) -+ { -+ dev_err(&pdev->dev, "Failed to register our codec\n"); -+ goto err_assert_reset; -+ } -+ -+ ret = devm_snd_soc_register_component(&pdev->dev, -+ &sun50i_h616_codec_component, -+ &dummy_cpu_dai, 1); -+ if (ret) -+ { -+ dev_err(&pdev->dev, "Failed to register our DAI\n"); -+ goto err_assert_reset; -+ } -+ -+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); -+ if (ret) -+ { -+ dev_err(&pdev->dev, "Failed to register against DMAEngine\n"); -+ goto err_assert_reset; -+ } -+ -+ card = quirks->create_card(&pdev->dev); -+ if (IS_ERR(card)) -+ { -+ ret = PTR_ERR(card); -+ dev_err(&pdev->dev, "Failed to create our card\n"); -+ goto err_assert_reset; -+ } -+ -+ snd_soc_card_set_drvdata(card, scodec); -+ -+ codec_regmap_debug = scodec->regmap; -+ -+ ret = snd_soc_register_card(card); -+ if (ret) -+ { -+ dev_err(&pdev->dev, "Failed to register our card\n"); -+ goto err_assert_reset; -+ } -+ -+ ret = sysfs_create_group(&pdev->dev.kobj, &audio_debug_attr_group); -+ if (ret) -+ dev_warn(&pdev->dev, "failed to create attr group\n"); -+ -+ sunxi_codec_init(scodec); -+ -+ return 0; -+ -+err_assert_reset: -+ if (scodec->rst) -+ reset_control_assert(scodec->rst); -+err_clk_disable: -+ clk_disable_unprepare(scodec->clk_apb); -+ return ret; -+} -+ -+static void sun50i_h616_codec_remove(struct platform_device *pdev) -+{ -+ struct snd_soc_card *card = platform_get_drvdata(pdev); -+ struct sun50i_h616_codec *scodec = snd_soc_card_get_drvdata(card); -+ -+ snd_soc_unregister_card(card); -+ if (scodec->rst) -+ reset_control_assert(scodec->rst); -+ clk_disable_unprepare(scodec->clk_apb); -+} -+ -+static struct platform_driver sun50i_h616_codec_driver = { -+ .driver = { -+ .name = "sun50i-h616-codec", -+ .of_match_table = sun50i_h616_codec_of_match, -+ }, -+ .probe = sun50i_h616_codec_probe, -+ .remove = sun50i_h616_codec_remove, -+}; -+module_platform_driver(sun50i_h616_codec_driver); -+ -+MODULE_DESCRIPTION("Allwinner H616 codec driver"); -+MODULE_AUTHOR("Emilio López "); -+MODULE_AUTHOR("Jon Smirl "); -+MODULE_AUTHOR("Maxime Ripard "); -+MODULE_AUTHOR("Chen-Yu Tsai "); -+MODULE_AUTHOR("Leeboby "); -+MODULE_LICENSE("GPL"); + /* see for_each_component_dais */ diff --git a/sound/soc/sunxi_v2/Kconfig b/sound/soc/sunxi_v2/Kconfig new file mode 100644 -index 000000000000..37fc579ba9db +index 000000000..e5d4aff7e --- /dev/null +++ b/sound/soc/sunxi_v2/Kconfig -@@ -0,0 +1,48 @@ +@@ -0,0 +1,45 @@ +# common +config SND_SOC_SUNXI_MACH + tristate @@ -1411,9 +293,6 @@ index 000000000000..37fc579ba9db +config SND_SOC_SUNXI_INTERNALCODEC + tristate + -+config SND_SOC_SUNXI_SUN50IW9_CODEC -+ tristate -+ +# menu select +menu "Allwinner SoC Audio support V2" + depends on ARCH_SUNXI @@ -1425,7 +304,7 @@ index 000000000000..37fc579ba9db + select SND_SOC_GENERIC_DMAENGINE_PCM + select SND_SOC_SUNXI_MACH + select SND_SOC_SUNXI_INTERNALCODEC -+ select SND_SOC_SUNXI_SUN50IW9_CODEC ++ select SND_SUN4I_CODEC + depends on ARCH_SUNXI + help + Select Y or M to support analog-audio Module in the Allwinner SoCs. @@ -1450,7 +329,7 @@ index 000000000000..37fc579ba9db +endmenu diff --git a/sound/soc/sunxi_v2/Makefile b/sound/soc/sunxi_v2/Makefile new file mode 100644 -index 000000000000..c7c2ef8f9fe9 +index 000000000..c7c2ef8f9 --- /dev/null +++ b/sound/soc/sunxi_v2/Makefile @@ -0,0 +1,11 @@ @@ -1467,7 +346,7 @@ index 000000000000..c7c2ef8f9fe9 +obj-$(CONFIG_SND_SOC_SUNXI_MACH) += snd_soc_sunxi_machine.o diff --git a/sound/soc/sunxi_v2/drv_hdmi.h b/sound/soc/sunxi_v2/drv_hdmi.h new file mode 100644 -index 000000000000..2e05489b01e1 +index 000000000..2e05489b0 --- /dev/null +++ b/sound/soc/sunxi_v2/drv_hdmi.h @@ -0,0 +1,63 @@ @@ -1536,7 +415,7 @@ index 000000000000..2e05489b01e1 +#endif diff --git a/sound/soc/sunxi_v2/snd_sunxi_ahub.c b/sound/soc/sunxi_v2/snd_sunxi_ahub.c new file mode 100644 -index 000000000000..6d4a847a928f +index 000000000..6d4a847a9 --- /dev/null +++ b/sound/soc/sunxi_v2/snd_sunxi_ahub.c @@ -0,0 +1,1475 @@ @@ -3017,7 +1896,7 @@ index 000000000000..6d4a847a928f +MODULE_DESCRIPTION("sunxi soundcard platform of ahub"); diff --git a/sound/soc/sunxi_v2/snd_sunxi_ahub.h b/sound/soc/sunxi_v2/snd_sunxi_ahub.h new file mode 100644 -index 000000000000..cd4be46b6ebf +index 000000000..cd4be46b6 --- /dev/null +++ b/sound/soc/sunxi_v2/snd_sunxi_ahub.h @@ -0,0 +1,74 @@ @@ -3097,7 +1976,7 @@ index 000000000000..cd4be46b6ebf +#endif /* __SND_SUNXI_AHUB_H */ diff --git a/sound/soc/sunxi_v2/snd_sunxi_ahub_dam.c b/sound/soc/sunxi_v2/snd_sunxi_ahub_dam.c new file mode 100644 -index 000000000000..f3c7bb3c7cd2 +index 000000000..f3c7bb3c7 --- /dev/null +++ b/sound/soc/sunxi_v2/snd_sunxi_ahub_dam.c @@ -0,0 +1,532 @@ @@ -3635,7 +2514,7 @@ index 000000000000..f3c7bb3c7cd2 +MODULE_DESCRIPTION("sunxi soundcard platform of ahub_dam"); diff --git a/sound/soc/sunxi_v2/snd_sunxi_ahub_dam.h b/sound/soc/sunxi_v2/snd_sunxi_ahub_dam.h new file mode 100644 -index 000000000000..436742d87e8f +index 000000000..436742d87 --- /dev/null +++ b/sound/soc/sunxi_v2/snd_sunxi_ahub_dam.h @@ -0,0 +1,294 @@ @@ -3935,7 +2814,7 @@ index 000000000000..436742d87e8f +#endif /* __SND_SUNXI_AHUB_DAM_H */ diff --git a/sound/soc/sunxi_v2/snd_sunxi_common.c b/sound/soc/sunxi_v2/snd_sunxi_common.c new file mode 100644 -index 000000000000..410ab75aea5a +index 000000000..410ab75ae --- /dev/null +++ b/sound/soc/sunxi_v2/snd_sunxi_common.c @@ -0,0 +1,267 @@ @@ -4208,7 +3087,7 @@ index 000000000000..410ab75aea5a +} diff --git a/sound/soc/sunxi_v2/snd_sunxi_common.h b/sound/soc/sunxi_v2/snd_sunxi_common.h new file mode 100644 -index 000000000000..7b88d20c25e0 +index 000000000..7b88d20c2 --- /dev/null +++ b/sound/soc/sunxi_v2/snd_sunxi_common.h @@ -0,0 +1,67 @@ @@ -4282,7 +3161,7 @@ index 000000000000..7b88d20c25e0 \ No newline at end of file diff --git a/sound/soc/sunxi_v2/snd_sunxi_log.h b/sound/soc/sunxi_v2/snd_sunxi_log.h new file mode 100644 -index 000000000000..89ad9fe71936 +index 000000000..89ad9fe71 --- /dev/null +++ b/sound/soc/sunxi_v2/snd_sunxi_log.h @@ -0,0 +1,29 @@ @@ -4317,7 +3196,7 @@ index 000000000000..89ad9fe71936 +#endif /* __SND_SUNXI_LOG_H */ diff --git a/sound/soc/sunxi_v2/snd_sunxi_mach.c b/sound/soc/sunxi_v2/snd_sunxi_mach.c new file mode 100644 -index 000000000000..74d276a2da92 +index 000000000..74d276a2d --- /dev/null +++ b/sound/soc/sunxi_v2/snd_sunxi_mach.c @@ -0,0 +1,479 @@ @@ -4802,7 +3681,7 @@ index 000000000000..74d276a2da92 +MODULE_DESCRIPTION("sunxi soundcard machine"); diff --git a/sound/soc/sunxi_v2/snd_sunxi_mach.h b/sound/soc/sunxi_v2/snd_sunxi_mach.h new file mode 100644 -index 000000000000..6f9ea8ccf8b5 +index 000000000..6f9ea8ccf --- /dev/null +++ b/sound/soc/sunxi_v2/snd_sunxi_mach.h @@ -0,0 +1,20 @@ @@ -4828,7 +3707,7 @@ index 000000000000..6f9ea8ccf8b5 +#endif /* __SND_SUNXI_MACH_H */ diff --git a/sound/soc/sunxi_v2/snd_sunxi_mach_utils.c b/sound/soc/sunxi_v2/snd_sunxi_mach_utils.c new file mode 100644 -index 000000000000..15f474e5cbeb +index 000000000..15f474e5c --- /dev/null +++ b/sound/soc/sunxi_v2/snd_sunxi_mach_utils.c @@ -0,0 +1,422 @@ @@ -5256,7 +4135,7 @@ index 000000000000..15f474e5cbeb +MODULE_DESCRIPTION("sunxi soundcard machine utils"); diff --git a/sound/soc/sunxi_v2/snd_sunxi_mach_utils.h b/sound/soc/sunxi_v2/snd_sunxi_mach_utils.h new file mode 100644 -index 000000000000..a9cffa0d859b +index 000000000..a9cffa0d8 --- /dev/null +++ b/sound/soc/sunxi_v2/snd_sunxi_mach_utils.h @@ -0,0 +1,116 @@ @@ -5377,5 +4256,5 @@ index 000000000000..a9cffa0d859b + +#endif /* __SND_SUNXI_MACH_UTILS_H */ -- -2.35.3 +Created with Armbian build tools https://github.com/armbian/build