armbian_build/patch/kernel/archive/sunxi-6.6/patches.armbian/ASoC-AC200-Initial-driver.patch
Gunjan Gupta d1186b8a0e kernel: sunxi: Add patches for 6.6 kernel
I have changed the way the patches are generated a bit. Instead of using orange-pi branch from megous tree for 6.6 kernel, I have used the following kernel branches

	a83t-suspend, af8133j, anx, audio,
	axp, cam, drm, err, fixes, mbus,
	modem, opi3, pb, pinetab, pp, ppkb,
	samuel, speed, tbs-a711, ths

These branches were carefully chosen to include only allwinner related patches and remove importing of the rockchip related patches into the allwinner kernel.

Following patches are modified to fix patch application failure
- patches.armbian/arm64-dts-sun50i-h616-orangepi-zero2-reg_usb1_vbus-status-ok.patch
- patches.armbian/arm64-dts-sun50i-h616-orangepi-zero2-Enable-GPU-mali.patch
- patches.armbian/arm64-dts-allwinner-h616-Add-efuse_xlate-cpu-frequency-scaling-v1_6_2.patch
- patches.armbian/arm64-dts-allwinner-h616-LED-green_power_on-red_status_heartbeat.patch
- patches.armbian/arm64-dts-allwinner-overlay-Add-Overlays-for-sunxi64.patch
- patches.armbian/arm64-dts-sun50i-h616-bigtreetech-cb1.patch

Following patches are modified because of kernel api change to fix compilation failure
- patches.armbian/drv-gpu-drm-sun4i-Add-HDMI-audio-sun4i-hdmi-encoder.patch
- patches.armbian/drv-of-Device-Tree-Overlay-ConfigFS-interface.patch
2023-10-30 22:58:11 +05:30

844 lines
27 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Thu, 1 Sep 2022 17:36:53 +0200
Subject: ASoC: AC200: Initial driver
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
sound/soc/codecs/Kconfig | 10 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/ac200.c | 774 ++++++++++
3 files changed, 786 insertions(+)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 68e051ddecd6..aed686afff16 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -16,6 +16,7 @@ config SND_SOC_ALL_CODECS
depends on COMPILE_TEST
imply SND_SOC_88PM860X
imply SND_SOC_AB8500_CODEC
+ imply SND_SOC_AC200_CODEC
imply SND_SOC_AC97_CODEC
imply SND_SOC_AD1836
imply SND_SOC_AD193X_SPI
@@ -397,6 +398,15 @@ config SND_SOC_AB8500_CODEC
tristate
depends on ABX500_CORE
+config SND_SOC_AC200_CODEC
+ tristate "AC200 Codec"
+ depends on MFD_AC200
+ help
+ Enable support for X-Powers AC200 analog audio codec.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-ac200.
+
config SND_SOC_AC97_CODEC
tristate "Build generic ASoC AC97 CODEC driver"
select SND_AC97_CODEC
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 6c56841521f5..b0860c75f4a3 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
snd-soc-88pm860x-objs := 88pm860x-codec.o
snd-soc-ab8500-codec-objs := ab8500-codec.o
+snd-soc-ac200-objs := ac200.o
snd-soc-ac97-objs := ac97.o
snd-soc-ad1836-objs := ad1836.o
snd-soc-ad193x-objs := ad193x.o
@@ -387,6 +388,7 @@ snd-soc-simple-mux-objs := simple-mux.o
obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o
+obj-$(CONFIG_SND_SOC_AC200_CODEC) += snd-soc-ac200.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o
diff --git a/sound/soc/codecs/ac200.c b/sound/soc/codecs/ac200.c
new file mode 100644
index 000000000000..113a45408116
--- /dev/null
+++ b/sound/soc/codecs/ac200.c
@@ -0,0 +1,774 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * X-Powers AC200 Codec Driver
+ *
+ * Copyright (C) 2022 Jernej Skrabec <jernej.skrabec@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define AC200_CODEC_RATES (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000 | \
+ SNDRV_PCM_RATE_KNOT)
+
+#define AC200_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define AC200_SYS_AUDIO_CTL0 0x0010
+#define AC200_SYS_AUDIO_CTL0_MCLK_GATING BIT(1)
+#define AC200_SYS_AUDIO_CTL0_RST_INVALID BIT(0)
+#define AC200_SYS_AUDIO_CTL1 0x0012
+#define AC200_SYS_AUDIO_CTL1_I2S_IO_EN BIT(0)
+
+#define AC200_SYS_CLK_CTL 0x2000
+#define AC200_SYS_CLK_CTL_I2S 15
+#define AC200_SYS_CLK_CTL_ADC 3
+#define AC200_SYS_CLK_CTL_DAC 2
+#define AC200_SYS_MOD_RST 0x2002
+#define AC200_SYS_MOD_RST_I2S 15
+#define AC200_SYS_MOD_RST_ADC 3
+#define AC200_SYS_MOD_RST_DAC 2
+#define AC200_SYS_SR_CTL 0x2004
+#define AC200_SYS_SR_CTL_SR_MASK GENMASK(3, 0)
+#define AC200_SYS_SR_CTL_SR(x) (x)
+#define AC200_I2S_CTL 0x2100
+#define AC200_I2S_CTL_SDO_EN 3
+#define AC200_I2S_CTL_TX_EN 2
+#define AC200_I2S_CTL_RX_EN 1
+#define AC200_I2S_CTL_GEN 0
+#define AC200_I2S_CLK 0x2102
+#define AC200_I2S_CLK_BCLK_OUT BIT(15)
+#define AC200_I2S_CLK_LRCK_OUT BIT(14)
+#define AC200_I2S_CLK_BCLKDIV_MASK GENMASK(13, 10)
+#define AC200_I2S_CLK_BCLKDIV(x) ((x) << 10)
+#define AC200_I2S_CLK_LRCK_MASK GENMASK(9, 0)
+#define AC200_I2S_CLK_LRCK(x) ((x) - 1)
+#define AC200_I2S_FMT0 0x2104
+#define AC200_I2S_FMT0_MODE_MASK GENMASK(15, 14)
+#define AC200_I2S_FMT0_MODE(x) ((x) << 14)
+#define AC200_I2S_FMT0_MODE_PCM 0
+#define AC200_I2S_FMT0_MODE_LEFT 1
+#define AC200_I2S_FMT0_MODE_RIGHT 2
+#define AC200_I2S_FMT0_TX_OFFSET_MASK GENMASK(11, 10)
+#define AC200_I2S_FMT0_TX_OFFSET(x) ((x) << 10)
+#define AC200_I2S_FMT0_RX_OFFSET_MASK GENMASK(9, 8)
+#define AC200_I2S_FMT0_RX_OFFSET(x) ((x) << 8)
+#define AC200_I2S_FMT0_SR_MASK GENMASK(6, 4)
+#define AC200_I2S_FMT0_SR(x) ((x) << 4)
+#define AC200_I2S_FMT0_SW_MASK GENMASK(3, 1)
+#define AC200_I2S_FMT0_SW(x) ((x) << 1)
+#define AC200_I2S_FMT1 0x2108
+#define AC200_I2S_FMT1_BCLK_POL_INVERT BIT(15)
+#define AC200_I2S_FMT1_LRCK_POL_INVERT BIT(14)
+#define AC200_I2S_MIX_SRC 0x2114
+#define AC200_I2S_MIX_SRC_LMIX_DAC 13
+#define AC200_I2S_MIX_SRC_LMIX_ADC 12
+#define AC200_I2S_MIX_SRC_RMIX_DAC 9
+#define AC200_I2S_MIX_SRC_RMIX_ADC 8
+#define AC200_I2S_MIX_GAIN 0x2116
+#define AC200_I2S_MIX_GAIN_LMIX_DAC 13
+#define AC200_I2S_MIX_GAIN_LMIX_ADC 12
+#define AC200_I2S_MIX_GAIN_RMIX_DAC 9
+#define AC200_I2S_MIX_GAIN_RMIX_ADC 8
+#define AC200_I2S_DAC_VOL 0x2118
+#define AC200_I2S_DAC_VOL_LEFT 8
+#define AC200_I2S_DAC_VOL_RIGHT 0
+#define AC200_I2S_ADC_VOL 0x211A
+#define AC200_I2S_ADC_VOL_LEFT 8
+#define AC200_I2S_ADC_VOL_RIGHT 0
+#define AC200_DAC_CTL 0x2200
+#define AC200_DAC_CTL_DAC_EN 15
+#define AC200_DAC_MIX_SRC 0x2202
+#define AC200_DAC_MIX_SRC_LMIX_DAC 13
+#define AC200_DAC_MIX_SRC_LMIX_ADC 12
+#define AC200_DAC_MIX_SRC_RMIX_DAC 9
+#define AC200_DAC_MIX_SRC_RMIX_ADC 8
+#define AC200_DAC_MIX_GAIN 0x2204
+#define AC200_DAC_MIX_GAIN_LMIX_DAC 13
+#define AC200_DAC_MIX_GAIN_LMIX_ADC 12
+#define AC200_DAC_MIX_GAIN_RMIX_DAC 9
+#define AC200_DAC_MIX_GAIN_RMIX_ADC 8
+#define AC200_OUT_MIX_CTL 0x2220
+#define AC200_OUT_MIX_CTL_RDAC_EN 15
+#define AC200_OUT_MIX_CTL_LDAC_EN 14
+#define AC200_OUT_MIX_CTL_RMIX_EN 13
+#define AC200_OUT_MIX_CTL_LMIX_EN 12
+#define AC200_OUT_MIX_CTL_MIC1_VOL 4
+#define AC200_OUT_MIX_CTL_MIC2_VOL 0
+#define AC200_OUT_MIX_SRC 0x2222
+#define AC200_OUT_MIX_SRC_RMIX_MIC1 14
+#define AC200_OUT_MIX_SRC_RMIX_MIC2 13
+#define AC200_OUT_MIX_SRC_RMIX_RDAC 9
+#define AC200_OUT_MIX_SRC_RMIX_LDAC 8
+#define AC200_OUT_MIX_SRC_LMIX_MIC1 6
+#define AC200_OUT_MIX_SRC_LMIX_MIC2 5
+#define AC200_OUT_MIX_SRC_LMIX_RDAC 1
+#define AC200_OUT_MIX_SRC_LMIX_LDAC 0
+#define AC200_LINEOUT_CTL 0x2224
+#define AC200_LINEOUT_CTL_EN 15
+#define AC200_LINEOUT_CTL_LEN 14
+#define AC200_LINEOUT_CTL_REN 13
+#define AC200_LINEOUT_CTL_LMONO 12
+#define AC200_LINEOUT_CTL_RMONO 11
+#define AC200_LINEOUT_CTL_VOL 0
+#define AC200_ADC_CTL 0x2300
+#define AC200_ADC_CTL_ADC_EN 15
+#define AC200_MBIAS_CTL 0x2310
+#define AC200_MBIAS_CTL_MBIAS_EN 15
+#define AC200_MBIAS_CTL_ADDA_BIAS_EN 3
+#define AC200_ADC_MIC_CTL 0x2320
+#define AC200_ADC_MIC_CTL_RADC_EN 15
+#define AC200_ADC_MIC_CTL_LADC_EN 14
+#define AC200_ADC_MIC_CTL_ADC_VOL 8
+#define AC200_ADC_MIC_CTL_MIC1_GAIN_EN 7
+#define AC200_ADC_MIC_CTL_MIC1_BOOST 4
+#define AC200_ADC_MIC_CTL_MIC2_GAIN_EN 3
+#define AC200_ADC_MIC_CTL_MIC2_BOOST 0
+#define AC200_ADC_MIX_SRC 0x2322
+#define AC200_ADC_MIX_SRC_RMIX_MIC1 14
+#define AC200_ADC_MIX_SRC_RMIX_MIC2 13
+#define AC200_ADC_MIX_SRC_RMIX_RMIX 9
+#define AC200_ADC_MIX_SRC_RMIX_LMIX 8
+#define AC200_ADC_MIX_SRC_LMIX_MIC1 6
+#define AC200_ADC_MIX_SRC_LMIX_MIC2 5
+#define AC200_ADC_MIX_SRC_LMIX_LMIX 1
+#define AC200_ADC_MIX_SRC_LMIX_RMIX 0
+
+struct ac200_codec {
+ struct regmap *regmap;
+ unsigned int format;
+};
+
+struct ac200_map {
+ int match;
+ int value;
+};
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(mixer_scale, -600, 600, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(gain_scale, -450, 150, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(lineout_scale, -4650, 150, 1);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(codec_scale, -12000, 75, 1);
+static const unsigned int mic_scale[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
+};
+
+static const struct snd_kcontrol_new ac200_codec_controls[] = {
+ SOC_DOUBLE_TLV("Master Playback Volume", AC200_I2S_DAC_VOL,
+ AC200_I2S_DAC_VOL_LEFT, AC200_I2S_DAC_VOL_RIGHT,
+ 0xff, 0, codec_scale),
+ SOC_DOUBLE_TLV("Master Capture Volume", AC200_I2S_ADC_VOL,
+ AC200_I2S_ADC_VOL_LEFT, AC200_I2S_ADC_VOL_RIGHT,
+ 0xff, 0, codec_scale),
+ SOC_DOUBLE_TLV("I2S ADC Capture Volume", AC200_I2S_MIX_GAIN,
+ AC200_I2S_MIX_GAIN_LMIX_ADC, AC200_I2S_MIX_GAIN_RMIX_ADC,
+ 0x1, 1, mixer_scale),
+ SOC_DOUBLE_TLV("I2S DAC Capture Volume", AC200_I2S_MIX_GAIN,
+ AC200_I2S_MIX_GAIN_LMIX_DAC, AC200_I2S_MIX_GAIN_RMIX_DAC,
+ 0x1, 1, mixer_scale),
+ SOC_DOUBLE_TLV("DAC I2S Playback Volume", AC200_DAC_MIX_GAIN,
+ AC200_DAC_MIX_GAIN_LMIX_DAC, AC200_DAC_MIX_GAIN_RMIX_DAC,
+ 0x1, 1, mixer_scale),
+ SOC_DOUBLE_TLV("ADC Playback Volume", AC200_DAC_MIX_GAIN,
+ AC200_DAC_MIX_GAIN_LMIX_ADC, AC200_DAC_MIX_GAIN_RMIX_ADC,
+ 0x1, 1, mixer_scale),
+ SOC_SINGLE_TLV("MIC1 Playback Volume", AC200_OUT_MIX_CTL,
+ AC200_OUT_MIX_CTL_MIC1_VOL, 0x7, 0, gain_scale),
+ SOC_SINGLE_TLV("MIC2 Playback Volume", AC200_OUT_MIX_CTL,
+ AC200_OUT_MIX_CTL_MIC2_VOL, 0x7, 0, gain_scale),
+ SOC_SINGLE_TLV("ADC Volume", AC200_ADC_MIC_CTL,
+ AC200_ADC_MIC_CTL_ADC_VOL, 0x07, 0, gain_scale),
+ SOC_SINGLE_TLV("Line Out Playback Volume", AC200_LINEOUT_CTL,
+ AC200_LINEOUT_CTL_VOL, 0x1f, 0, lineout_scale),
+ SOC_SINGLE_TLV("MIC1 Boost Volume", AC200_ADC_MIC_CTL,
+ AC200_ADC_MIC_CTL_MIC1_BOOST, 0x07, 0, mic_scale),
+ SOC_SINGLE_TLV("MIC2 Boost Volume", AC200_ADC_MIC_CTL,
+ AC200_ADC_MIC_CTL_MIC2_BOOST, 0x07, 0, mic_scale),
+ SOC_DOUBLE("Line Out Playback Switch", AC200_LINEOUT_CTL,
+ AC200_LINEOUT_CTL_LEN, AC200_LINEOUT_CTL_REN, 1, 0),
+};
+
+static const struct snd_kcontrol_new i2s_mixer[] = {
+ SOC_DAPM_DOUBLE("I2S DAC Capture Switch", AC200_I2S_MIX_SRC,
+ AC200_I2S_MIX_SRC_LMIX_DAC,
+ AC200_I2S_MIX_SRC_RMIX_DAC, 1, 0),
+ SOC_DAPM_DOUBLE("I2S ADC Capture Switch", AC200_I2S_MIX_SRC,
+ AC200_I2S_MIX_SRC_LMIX_ADC,
+ AC200_I2S_MIX_SRC_RMIX_ADC, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac_mixer[] = {
+ SOC_DAPM_DOUBLE("DAC I2S Playback Switch", AC200_DAC_MIX_SRC,
+ AC200_DAC_MIX_SRC_LMIX_DAC,
+ AC200_DAC_MIX_SRC_RMIX_DAC, 1, 0),
+ SOC_DAPM_DOUBLE("ADC Playback Switch", AC200_DAC_MIX_SRC,
+ AC200_DAC_MIX_SRC_LMIX_ADC,
+ AC200_DAC_MIX_SRC_RMIX_ADC, 1, 0),
+};
+
+static const struct snd_kcontrol_new output_mixer[] = {
+ SOC_DAPM_DOUBLE("MIC1 Playback Switch", AC200_OUT_MIX_SRC,
+ AC200_OUT_MIX_SRC_LMIX_MIC1,
+ AC200_OUT_MIX_SRC_RMIX_MIC1, 1, 0),
+ SOC_DAPM_DOUBLE("MIC2 Playback Switch", AC200_OUT_MIX_SRC,
+ AC200_OUT_MIX_SRC_LMIX_MIC2,
+ AC200_OUT_MIX_SRC_RMIX_MIC2, 1, 0),
+ SOC_DAPM_DOUBLE("DAC Playback Switch", AC200_OUT_MIX_SRC,
+ AC200_OUT_MIX_SRC_LMIX_LDAC,
+ AC200_OUT_MIX_SRC_RMIX_RDAC, 1, 0),
+ SOC_DAPM_DOUBLE("DAC Reversed Playback Switch", AC200_OUT_MIX_SRC,
+ AC200_OUT_MIX_SRC_LMIX_RDAC,
+ AC200_OUT_MIX_SRC_RMIX_LDAC, 1, 0),
+};
+
+static const struct snd_kcontrol_new input_mixer[] = {
+ SOC_DAPM_DOUBLE("MIC1 Capture Switch", AC200_ADC_MIX_SRC,
+ AC200_ADC_MIX_SRC_LMIX_MIC1,
+ AC200_ADC_MIX_SRC_RMIX_MIC1, 1, 0),
+ SOC_DAPM_DOUBLE("MIC2 Capture Switch", AC200_ADC_MIX_SRC,
+ AC200_ADC_MIX_SRC_LMIX_MIC2,
+ AC200_ADC_MIX_SRC_RMIX_MIC2, 1, 0),
+ SOC_DAPM_DOUBLE("Output Mixer Capture Switch", AC200_ADC_MIX_SRC,
+ AC200_ADC_MIX_SRC_LMIX_LMIX,
+ AC200_ADC_MIX_SRC_RMIX_RMIX, 1, 0),
+ SOC_DAPM_DOUBLE("Output Mixer Reverse Capture Switch",
+ AC200_ADC_MIX_SRC,
+ AC200_ADC_MIX_SRC_LMIX_RMIX,
+ AC200_ADC_MIX_SRC_RMIX_LMIX, 1, 0),
+};
+
+const char * const lineout_mux_enum_text[] = {
+ "Stereo", "Mono",
+};
+
+static SOC_ENUM_DOUBLE_DECL(lineout_mux_enum, AC200_LINEOUT_CTL,
+ AC200_LINEOUT_CTL_LMONO, AC200_LINEOUT_CTL_RMONO,
+ lineout_mux_enum_text);
+
+static const struct snd_kcontrol_new lineout_mux =
+ SOC_DAPM_ENUM("Line Out Source Playback Route", lineout_mux_enum);
+
+static const struct snd_soc_dapm_widget ac200_codec_dapm_widgets[] = {
+ /* Regulator */
+ SND_SOC_DAPM_REGULATOR_SUPPLY("avcc", 0, 0),
+
+ /* System clocks */
+ SND_SOC_DAPM_SUPPLY("CLK SYS I2S", AC200_SYS_CLK_CTL,
+ AC200_SYS_CLK_CTL_I2S, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK SYS DAC", AC200_SYS_CLK_CTL,
+ AC200_SYS_CLK_CTL_DAC, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK SYS ADC", AC200_SYS_CLK_CTL,
+ AC200_SYS_CLK_CTL_ADC, 0, NULL, 0),
+
+ /* Module resets */
+ SND_SOC_DAPM_SUPPLY("RST SYS I2S", AC200_SYS_MOD_RST,
+ AC200_SYS_MOD_RST_I2S, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RST SYS DAC", AC200_SYS_MOD_RST,
+ AC200_SYS_MOD_RST_DAC, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RST SYS ADC", AC200_SYS_MOD_RST,
+ AC200_SYS_MOD_RST_DAC, 0, NULL, 0),
+
+ /* I2S gates */
+ SND_SOC_DAPM_SUPPLY("CLK I2S GEN", AC200_I2S_CTL,
+ AC200_I2S_CTL_GEN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK I2S SDO", AC200_I2S_CTL,
+ AC200_I2S_CTL_SDO_EN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK I2S TX", AC200_I2S_CTL,
+ AC200_I2S_CTL_TX_EN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK I2S RX", AC200_I2S_CTL,
+ AC200_I2S_CTL_RX_EN, 0, NULL, 0),
+
+ /* Module supplies */
+ SND_SOC_DAPM_SUPPLY("ADC Enable", AC200_ADC_CTL,
+ AC200_ADC_CTL_ADC_EN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Enable", AC200_DAC_CTL,
+ AC200_DAC_CTL_DAC_EN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Line Out Enable", AC200_LINEOUT_CTL,
+ AC200_LINEOUT_CTL_EN, 0, NULL, 0),
+
+ /* Bias */
+ SND_SOC_DAPM_SUPPLY("MIC Bias", AC200_MBIAS_CTL,
+ AC200_MBIAS_CTL_MBIAS_EN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADDA Bias", AC200_MBIAS_CTL,
+ AC200_MBIAS_CTL_ADDA_BIAS_EN, 0, NULL, 0),
+
+ /* DAC */
+ SND_SOC_DAPM_DAC("Left DAC", "Playback", AC200_OUT_MIX_CTL,
+ AC200_OUT_MIX_CTL_LDAC_EN, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Playback", AC200_OUT_MIX_CTL,
+ AC200_OUT_MIX_CTL_RDAC_EN, 0),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("Left ADC", "Capture", AC200_ADC_MIC_CTL,
+ AC200_ADC_MIC_CTL_LADC_EN, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Capture", AC200_ADC_MIC_CTL,
+ AC200_ADC_MIC_CTL_RADC_EN, 0),
+
+ /* Mixers */
+ SND_SOC_DAPM_MIXER("Left Output Mixer", AC200_OUT_MIX_CTL,
+ AC200_OUT_MIX_CTL_LMIX_EN, 0,
+ output_mixer, ARRAY_SIZE(output_mixer)),
+ SND_SOC_DAPM_MIXER("Right Output Mixer", AC200_OUT_MIX_CTL,
+ AC200_OUT_MIX_CTL_RMIX_EN, 0,
+ output_mixer, ARRAY_SIZE(output_mixer)),
+
+ SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0,
+ input_mixer, ARRAY_SIZE(input_mixer)),
+ SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0,
+ input_mixer, ARRAY_SIZE(input_mixer)),
+
+ SND_SOC_DAPM_MIXER("Left DAC Mixer", SND_SOC_NOPM, 0, 0,
+ dac_mixer, ARRAY_SIZE(dac_mixer)),
+ SND_SOC_DAPM_MIXER("Right DAC Mixer", SND_SOC_NOPM, 0, 0,
+ dac_mixer, ARRAY_SIZE(dac_mixer)),
+
+ SND_SOC_DAPM_MIXER("Left I2S Mixer", SND_SOC_NOPM, 0, 0,
+ i2s_mixer, ARRAY_SIZE(i2s_mixer)),
+ SND_SOC_DAPM_MIXER("Right I2S Mixer", SND_SOC_NOPM, 0, 0,
+ i2s_mixer, ARRAY_SIZE(i2s_mixer)),
+
+ /* Muxes */
+ SND_SOC_DAPM_MUX("Line Out Source Playback Route",
+ SND_SOC_NOPM, 0, 0, &lineout_mux),
+
+ /* Gain/attenuation */
+ SND_SOC_DAPM_PGA("MIC1 Amplifier", AC200_ADC_MIC_CTL,
+ AC200_ADC_MIC_CTL_MIC1_GAIN_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIC2 Amplifier", AC200_ADC_MIC_CTL,
+ AC200_ADC_MIC_CTL_MIC2_GAIN_EN, 0, NULL, 0),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("LINEOUT"),
+};
+
+static const struct snd_soc_dapm_route ac200_codec_dapm_routes[] = {
+ { "RST SYS I2S", NULL, "CLK SYS I2S" },
+ { "RST SYS ADC", NULL, "CLK SYS ADC" },
+ { "RST SYS DAC", NULL, "CLK SYS DAC" },
+
+ { "CLK I2S GEN", NULL, "RST SYS I2S" },
+ { "CLK I2S SDO", NULL, "CLK I2S GEN" },
+ { "CLK I2S TX", NULL, "CLK I2S SDO" },
+ { "CLK I2S RX", NULL, "CLK I2S SDO" },
+
+ { "ADC Enable", NULL, "RST SYS ADC" },
+ { "ADC Enable", NULL, "ADDA Bias" },
+ { "ADC Enable", NULL, "avcc" },
+ { "DAC Enable", NULL, "RST SYS DAC" },
+ { "DAC Enable", NULL, "ADDA Bias" },
+ { "DAC Enable", NULL, "avcc" },
+
+ { "Left DAC", NULL, "DAC Enable" },
+ { "Left DAC", NULL, "CLK I2S RX" },
+ { "Right DAC", NULL, "DAC Enable" },
+ { "Right DAC", NULL, "CLK I2S RX" },
+
+ { "Left ADC", NULL, "ADC Enable" },
+ { "Left ADC", NULL, "CLK I2S TX" },
+ { "Right ADC", NULL, "ADC Enable" },
+ { "Right ADC", NULL, "CLK I2S TX" },
+
+ { "Left Output Mixer", "MIC1 Playback Switch", "MIC1 Amplifier" },
+ { "Left Output Mixer", "MIC2 Playback Switch", "MIC2 Amplifier" },
+ { "Left Output Mixer", "DAC Playback Switch", "Left DAC Mixer" },
+ { "Left Output Mixer", "DAC Reversed Playback Switch", "Right DAC Mixer" },
+
+ { "Right Output Mixer", "MIC1 Playback Switch", "MIC1 Amplifier" },
+ { "Right Output Mixer", "MIC2 Playback Switch", "MIC2 Amplifier" },
+ { "Right Output Mixer", "DAC Playback Switch", "Right DAC Mixer" },
+ { "Right Output Mixer", "DAC Reversed Playback Switch", "Left DAC Mixer" },
+
+ { "Left Input Mixer", "MIC1 Capture Switch", "MIC1 Amplifier" },
+ { "Left Input Mixer", "MIC2 Capture Switch", "MIC2 Amplifier" },
+ { "Left Input Mixer", "Output Mixer Capture Switch", "Left Output Mixer" },
+ { "Left Input Mixer", "Output Mixer Reverse Capture Switch", "Right Output Mixer" },
+
+ { "Right Input Mixer", "MIC1 Capture Switch", "MIC1 Amplifier" },
+ { "Right Input Mixer", "MIC2 Capture Switch", "MIC2 Amplifier" },
+ { "Right Input Mixer", "Output Mixer Capture Switch", "Right Output Mixer" },
+ { "Right Input Mixer", "Output Mixer Reverse Capture Switch", "Left Output Mixer" },
+
+ { "Left I2S Mixer", "I2S DAC Capture Switch", "Left DAC" },
+ { "Left I2S Mixer", "I2S ADC Capture Switch", "Left Input Mixer" },
+ { "Right I2S Mixer", "I2S DAC Capture Switch", "Right DAC" },
+ { "Right I2S Mixer", "I2S ADC Capture Switch", "Right Input Mixer" },
+
+ { "Left DAC Mixer", "DAC I2S Playback Switch", "Left DAC" },
+ { "Left DAC Mixer", "ADC Playback Switch", "Left Input Mixer" },
+ { "Right DAC Mixer", "DAC I2S Playback Switch", "Right DAC" },
+ { "Right DAC Mixer", "ADC Playback Switch", "Right Input Mixer" },
+
+ { "Line Out Source Playback Route", "Stereo", "Left Output Mixer" },
+ { "Line Out Source Playback Route", "Stereo", "Right Output Mixer" },
+ { "Line Out Source Playback Route", "Mono", "Right Output Mixer" },
+ { "Line Out Source Playback Route", "Mono", "Left Output Mixer" },
+
+ { "Left ADC", NULL, "Left I2S Mixer" },
+ { "Right ADC", NULL, "Right I2S Mixer" },
+
+ { "LINEOUT", NULL, "Line Out Enable", },
+ { "LINEOUT", NULL, "Line Out Source Playback Route" },
+
+ { "MIC1", NULL, "MIC Bias" },
+ { "MIC2", NULL, "MIC Bias" },
+ { "MIC1 Amplifier", NULL, "MIC1" },
+ { "MIC2 Amplifier", NULL, "MIC2" },
+};
+
+static int ac200_get_sr_sw(unsigned int width)
+{
+ switch (width) {
+ case 8:
+ return 1;
+ case 12:
+ return 2;
+ case 16:
+ return 3;
+ case 20:
+ return 4;
+ case 24:
+ return 5;
+ case 28:
+ return 6;
+ case 32:
+ return 7;
+ }
+
+ return -EINVAL;
+}
+
+static const struct ac200_map ac200_bclk_div_map[] = {
+ { .match = 1, .value = 1 },
+ { .match = 2, .value = 2 },
+ { .match = 4, .value = 3 },
+ { .match = 6, .value = 4 },
+ { .match = 8, .value = 5 },
+ { .match = 12, .value = 6 },
+ { .match = 16, .value = 7 },
+ { .match = 24, .value = 8 },
+ { .match = 32, .value = 9 },
+ { .match = 48, .value = 10 },
+ { .match = 64, .value = 11 },
+ { .match = 96, .value = 12 },
+ { .match = 128, .value = 13 },
+ { .match = 176, .value = 14 },
+ { .match = 192, .value = 15 },
+};
+
+static int ac200_get_bclk_div(unsigned int sample_rate, unsigned int period)
+{
+ unsigned int sysclk_rate = (sample_rate % 4000) ? 22579200 : 24576000;
+ unsigned int div = sysclk_rate / sample_rate / period;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ac200_bclk_div_map); i++) {
+ const struct ac200_map *bdiv = &ac200_bclk_div_map[i];
+
+ if (bdiv->match == div)
+ return bdiv->value;
+ }
+
+ return -EINVAL;
+}
+
+static const struct ac200_map ac200_ssr_map[] = {
+ { .match = 8000, .value = 0 },
+ { .match = 11025, .value = 1 },
+ { .match = 12000, .value = 2 },
+ { .match = 16000, .value = 3 },
+ { .match = 22050, .value = 4 },
+ { .match = 24000, .value = 5 },
+ { .match = 32000, .value = 6 },
+ { .match = 44100, .value = 7 },
+ { .match = 48000, .value = 8 },
+ { .match = 96000, .value = 9 },
+ { .match = 192000, .value = 10 },
+};
+
+static int ac200_get_ssr(unsigned int sample_rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ac200_ssr_map); i++) {
+ const struct ac200_map *ssr = &ac200_ssr_map[i];
+
+ if (ssr->match == sample_rate)
+ return ssr->value;
+ }
+
+ return -EINVAL;
+}
+
+static int ac200_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct ac200_codec *priv = snd_soc_dai_get_drvdata(dai);
+ unsigned int slot_width = params_physical_width(params);
+ unsigned int sample_rate = params_rate(params);
+ int sr, period, sw, bclkdiv, ssr;
+
+ sr = ac200_get_sr_sw(params_width(params));
+ if (sr < 0)
+ return sr;
+
+ sw = ac200_get_sr_sw(slot_width);
+ if (sw < 0)
+ return sw;
+
+ regmap_update_bits(priv->regmap, AC200_I2S_FMT0,
+ AC200_I2S_FMT0_SR_MASK |
+ AC200_I2S_FMT0_SW_MASK,
+ AC200_I2S_FMT0_SR(sr) |
+ AC200_I2S_FMT0_SW(sw));
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ period = slot_width;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ period = slot_width * 2;
+ break;
+ }
+
+ bclkdiv = ac200_get_bclk_div(sample_rate, period);
+ if (bclkdiv < 0)
+ return bclkdiv;
+
+ regmap_update_bits(priv->regmap, AC200_I2S_CLK,
+ AC200_I2S_CLK_LRCK_MASK |
+ AC200_I2S_CLK_BCLKDIV_MASK,
+ AC200_I2S_CLK_LRCK(period) |
+ AC200_I2S_CLK_BCLKDIV(bclkdiv));
+
+ ssr = ac200_get_ssr(sample_rate);
+ if (ssr < 0)
+ return ssr;
+
+ regmap_update_bits(priv->regmap, AC200_SYS_SR_CTL,
+ AC200_SYS_SR_CTL_SR_MASK,
+ AC200_SYS_SR_CTL_SR(ssr));
+
+ return 0;
+}
+
+static int ac200_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct ac200_codec *priv = snd_soc_dai_get_drvdata(dai);
+ unsigned long offset, mode, value;
+
+ priv->format = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ value = AC200_I2S_CLK_BCLK_OUT | AC200_I2S_CLK_LRCK_OUT;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ value = AC200_I2S_CLK_LRCK_OUT;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFC:
+ value = AC200_I2S_CLK_BCLK_OUT;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ value = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(priv->regmap, AC200_I2S_CLK,
+ AC200_I2S_CLK_BCLK_OUT |
+ AC200_I2S_CLK_LRCK_OUT, value);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ mode = AC200_I2S_FMT0_MODE_LEFT;
+ offset = 1;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ mode = AC200_I2S_FMT0_MODE_RIGHT;
+ offset = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ mode = AC200_I2S_FMT0_MODE_LEFT;
+ offset = 0;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ mode = AC200_I2S_FMT0_MODE_PCM;
+ offset = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ mode = AC200_I2S_FMT0_MODE_PCM;
+ offset = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(priv->regmap, AC200_I2S_FMT0,
+ AC200_I2S_FMT0_MODE_MASK |
+ AC200_I2S_FMT0_TX_OFFSET_MASK |
+ AC200_I2S_FMT0_RX_OFFSET_MASK,
+ AC200_I2S_FMT0_MODE(mode) |
+ AC200_I2S_FMT0_TX_OFFSET(offset) |
+ AC200_I2S_FMT0_RX_OFFSET(offset));
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ value = 0;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ value = AC200_I2S_FMT1_LRCK_POL_INVERT;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ value = AC200_I2S_FMT1_BCLK_POL_INVERT;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ value = AC200_I2S_FMT1_BCLK_POL_INVERT |
+ AC200_I2S_FMT1_LRCK_POL_INVERT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(priv->regmap, AC200_I2S_FMT1,
+ AC200_I2S_FMT1_BCLK_POL_INVERT |
+ AC200_I2S_FMT1_LRCK_POL_INVERT, value);
+
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ac200_codec_dai_ops = {
+ .hw_params = ac200_codec_hw_params,
+ .set_fmt = ac200_codec_set_fmt,
+};
+
+static struct snd_soc_dai_driver ac200_codec_dai = {
+ .name = "ac200-dai",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AC200_CODEC_RATES,
+ .formats = AC200_CODEC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AC200_CODEC_RATES,
+ .formats = AC200_CODEC_FORMATS,
+ },
+ .ops = &ac200_codec_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static int ac200_codec_component_probe(struct snd_soc_component *component)
+{
+ struct ac200_codec *priv = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_init_regmap(component, priv->regmap);
+
+ return 0;
+}
+
+static struct snd_soc_component_driver ac200_soc_component = {
+ .controls = ac200_codec_controls,
+ .num_controls = ARRAY_SIZE(ac200_codec_controls),
+ .dapm_widgets = ac200_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ac200_codec_dapm_widgets),
+ .dapm_routes = ac200_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ac200_codec_dapm_routes),
+ .probe = ac200_codec_component_probe,
+};
+
+static int ac200_codec_probe(struct platform_device *pdev)
+{
+ struct ac200_codec *priv;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct ac200_codec), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!priv->regmap)
+ return -EPROBE_DEFER;
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = regmap_write(priv->regmap, AC200_SYS_AUDIO_CTL0,
+ AC200_SYS_AUDIO_CTL0_RST_INVALID |
+ AC200_SYS_AUDIO_CTL0_MCLK_GATING);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->regmap, AC200_SYS_AUDIO_CTL1,
+ AC200_SYS_AUDIO_CTL1_I2S_IO_EN);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &ac200_soc_component,
+ &ac200_codec_dai, 1);
+
+ if (ret)
+ dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static int ac200_codec_remove(struct platform_device *pdev)
+{
+ struct ac200_codec *priv = dev_get_drvdata(&pdev->dev);
+
+ regmap_write(priv->regmap, AC200_SYS_AUDIO_CTL0, 0);
+ regmap_write(priv->regmap, AC200_SYS_AUDIO_CTL1, 0);
+
+ return 0;
+}
+
+static const struct of_device_id ac200_codec_match[] = {
+ { .compatible = "x-powers,ac200-codec" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ac200_codec_match);
+
+static struct platform_driver ac200_codec_driver = {
+ .driver = {
+ .name = "ac200-codec",
+ .of_match_table = ac200_codec_match,
+ },
+ .probe = ac200_codec_probe,
+ .remove = ac200_codec_remove,
+};
+module_platform_driver(ac200_codec_driver);
+
+MODULE_DESCRIPTION("X-Powers AC200 Codec Driver");
+MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@gmail.com>");
+MODULE_LICENSE("GPL");
--
Armbian