diff --git a/config/kernel/linux-sunxi64-current.config b/config/kernel/linux-sunxi64-current.config index 29ab323fe..9674fcea1 100644 --- a/config/kernel/linux-sunxi64-current.config +++ b/config/kernel/linux-sunxi64-current.config @@ -3941,7 +3941,7 @@ CONFIG_MFD_SUN4I_GPADC=y # CONFIG_MFD_BCM590XX is not set CONFIG_MFD_BD9571MWV=m CONFIG_MFD_AC100=y -# CONFIG_MFD_AC200 is not set +CONFIG_MFD_AC200=m CONFIG_MFD_AXP20X=y CONFIG_MFD_AXP20X_I2C=y CONFIG_MFD_AXP20X_RSB=y @@ -5310,6 +5310,7 @@ CONFIG_SND_SOC_I2C_AND_SPI=y # CODEC drivers # CONFIG_SND_SOC_WM_ADSP=m +CONFIG_SND_SOC_AC200_CODEC=m # CONFIG_SND_SOC_AC97_CODEC is not set CONFIG_SND_SOC_ADAU_UTILS=m CONFIG_SND_SOC_ADAU1372=m @@ -5500,7 +5501,6 @@ CONFIG_SND_SOC_LPASS_WSA_MACRO=m CONFIG_SND_SOC_LPASS_VA_MACRO=m CONFIG_SND_SOC_LPASS_RX_MACRO=m CONFIG_SND_SOC_LPASS_TX_MACRO=m -CONFIG_SND_SOC_ACX00=m # end of CODEC drivers CONFIG_SND_SIMPLE_CARD_UTILS=m @@ -7326,6 +7326,7 @@ CONFIG_PHY_SUN4I_USB=y CONFIG_PHY_SUN6I_MIPI_DPHY=y CONFIG_PHY_SUN9I_USB=y CONFIG_PHY_SUN50I_USB3=y +CONFIG_AC200_PHY_CTL=m # # PHY drivers for Broadcom platforms diff --git a/config/kernel/linux-sunxi64-edge.config b/config/kernel/linux-sunxi64-edge.config index 0a7ab1d27..8e1562a86 100644 --- a/config/kernel/linux-sunxi64-edge.config +++ b/config/kernel/linux-sunxi64-edge.config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm64 6.5.1 Kernel Configuration +# Linux/arm64 6.5.3 Kernel Configuration # CONFIG_CC_VERSION_TEXT="aarch64-linux-gnu-gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0" CONFIG_CC_IS_GCC=y @@ -2885,6 +2885,7 @@ CONFIG_RTL8189FS=m CONFIG_RTL8189ES=m # CONFIG_WLAN_VENDOR_ZYDAS is not set CONFIG_WLAN_VENDOR_QUANTENNA=y +# CONFIG_SPARD_WLAN_SUPPORT is not set CONFIG_USB_NET_RNDIS_WLAN=m # CONFIG_MAC80211_HWSIM is not set CONFIG_VIRT_WIFI=m @@ -4000,7 +4001,7 @@ CONFIG_MFD_SUN4I_GPADC=y # CONFIG_MFD_BCM590XX is not set CONFIG_MFD_BD9571MWV=m CONFIG_MFD_AC100=y -# CONFIG_MFD_AC200 is not set +CONFIG_MFD_AC200=m CONFIG_MFD_AXP20X=y CONFIG_MFD_AXP20X_I2C=y CONFIG_MFD_AXP20X_RSB=y @@ -4630,10 +4631,7 @@ CONFIG_MEDIA_ATTACH=y # IR I2C driver auto-selected by 'Autoselect ancillary drivers' # CONFIG_VIDEO_IR_I2C=m - -# -# Camera sensor devices -# +CONFIG_VIDEO_CAMERA_SENSOR=y CONFIG_VIDEO_APTINA_PLL=m CONFIG_VIDEO_CCS_PLL=m CONFIG_VIDEO_AR0521=m @@ -4702,9 +4700,6 @@ CONFIG_VIDEO_S5K6A3=m # CONFIG_VIDEO_ST_VGXY61 is not set CONFIG_VIDEO_CCS=m CONFIG_VIDEO_ET8EK8=m -CONFIG_VIDEO_HM5065=m -CONFIG_VIDEO_GC2145=m -# end of Camera sensor devices # # Lens drivers @@ -4714,6 +4709,8 @@ CONFIG_VIDEO_GC2145=m # CONFIG_VIDEO_DW9714 is not set # CONFIG_VIDEO_DW9768 is not set # CONFIG_VIDEO_DW9807_VCM is not set +CONFIG_VIDEO_HM5065=m +CONFIG_VIDEO_GC2145=m # end of Lens drivers # @@ -5397,6 +5394,7 @@ CONFIG_SND_SOC_I2C_AND_SPI=y # CODEC drivers # CONFIG_SND_SOC_WM_ADSP=m +CONFIG_SND_SOC_AC200_CODEC=m # CONFIG_SND_SOC_AC97_CODEC is not set CONFIG_SND_SOC_ADAU_UTILS=m CONFIG_SND_SOC_ADAU1372=m @@ -5598,7 +5596,6 @@ CONFIG_SND_SOC_LPASS_WSA_MACRO=m CONFIG_SND_SOC_LPASS_VA_MACRO=m CONFIG_SND_SOC_LPASS_RX_MACRO=m CONFIG_SND_SOC_LPASS_TX_MACRO=m -CONFIG_SND_SOC_ACX00=m # end of CODEC drivers CONFIG_SND_SIMPLE_CARD_UTILS=m @@ -7445,6 +7442,7 @@ CONFIG_PHY_SUN4I_USB=y CONFIG_PHY_SUN6I_MIPI_DPHY=y CONFIG_PHY_SUN9I_USB=y CONFIG_PHY_SUN50I_USB3=y +CONFIG_AC200_PHY_CTL=m # # PHY drivers for Broadcom platforms @@ -7997,10 +7995,7 @@ CONFIG_IMA_APPRAISE=y # CONFIG_IMA_APPRAISE_BUILD_POLICY is not set CONFIG_IMA_APPRAISE_BOOTPARAM=y # CONFIG_IMA_APPRAISE_MODSIG is not set -CONFIG_IMA_TRUSTED_KEYRING=y # CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY is not set -# CONFIG_IMA_BLACKLIST_KEYRING is not set -# CONFIG_IMA_LOAD_X509 is not set CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS=y CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS=y # CONFIG_IMA_DISABLE_HTABLE is not set diff --git a/patch/kernel/archive/sunxi-6.1/patches.armbian/ASoC-AC200-Initial-driver.patch b/patch/kernel/archive/sunxi-6.1/patches.armbian/ASoC-AC200-Initial-driver.patch new file mode 100644 index 000000000..4f02be91f --- /dev/null +++ b/patch/kernel/archive/sunxi-6.1/patches.armbian/ASoC-AC200-Initial-driver.patch @@ -0,0 +1,844 @@ +From a53d7dcd7a940a0701b22da4c98d3002c01b1174 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Thu, 1 Sep 2022 17:36:53 +0200 +Subject: [PATCH] ASoC: AC200: Initial driver + +Signed-off-by: Jernej Skrabec +--- + sound/soc/codecs/Kconfig | 10 + + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/ac200.c | 774 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 786 insertions(+) + create mode 100644 sound/soc/codecs/ac200.c + +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +index e9d3617bb068..dc6de4ced0c6 100644 +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -17,6 +17,7 @@ config SND_SOC_ALL_CODECS + imply SND_SOC_88PM860X + imply SND_SOC_L3 + imply SND_SOC_AB8500_CODEC ++ imply SND_SOC_AC200_CODEC + imply SND_SOC_AC97_CODEC + imply SND_SOC_AD1836 + imply SND_SOC_AD193X_SPI +@@ -370,6 +371,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 e2d463a317e5..fb7030376397 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 +@@ -361,6 +362,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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++MODULE_LICENSE("GPL"); +-- +2.34.1 + diff --git a/patch/kernel/archive/sunxi-6.1/patches.armbian/Rollback-r_rsb-to-r_i2c.patch b/patch/kernel/archive/sunxi-6.1/patches.armbian/Rollback-r_rsb-to-r_i2c.patch deleted file mode 100644 index 3c730fdc4..000000000 --- a/patch/kernel/archive/sunxi-6.1/patches.armbian/Rollback-r_rsb-to-r_i2c.patch +++ /dev/null @@ -1,32 +0,0 @@ -From fad789644794fc525a38d27927996ae089477e3f Mon Sep 17 00:00:00 2001 -From: Ukhellfire -Date: Fri, 1 Apr 2022 09:44:19 +0100 -Subject: [PATCH 169/170] Rollback r_rsb to r_i2c - ---- - arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts -index cc5a73026..0b07f8ca2 100644 ---- a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts -+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts -@@ -208,12 +208,12 @@ &pio { - vcc-pg-supply = <®_vcc_wifi_io>; - }; - --&r_rsb { -+&r_i2c { - status = "okay"; - -- axp805: pmic@745 { -+ axp805: pmic@36 { - compatible = "x-powers,axp805", "x-powers,axp806"; -- reg = <0x745>; -+ reg = <0x36>; - interrupt-parent = <&r_intc>; - interrupts = ; - interrupt-controller; --- -2.35.3 - diff --git a/patch/kernel/archive/sunxi-6.1/patches.armbian/add-initial-support-for-orangepi3-lts.patch b/patch/kernel/archive/sunxi-6.1/patches.armbian/add-initial-support-for-orangepi3-lts.patch index f040a159c..8c1228ec7 100644 --- a/patch/kernel/archive/sunxi-6.1/patches.armbian/add-initial-support-for-orangepi3-lts.patch +++ b/patch/kernel/archive/sunxi-6.1/patches.armbian/add-initial-support-for-orangepi3-lts.patch @@ -5,9 +5,8 @@ Subject: [PATCH 151/158] add initial support for orangepi3-lts --- arch/arm64/boot/dts/allwinner/Makefile | 1 + - .../allwinner/sun50i-h6-orangepi-3-lts.dts | 398 ++++++++++++++++++ - arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 101 ++++- - 3 files changed, 487 insertions(+), 13 deletions(-) + .../allwinner/sun50i-h6-orangepi-3-lts.dts | 399 ++++++++++++++++++ + 2 files changed, 400 insertions(+), 0 deletions(-) create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts diff --git a/arch/arm64/boot/dts/allwinner/Makefile b/arch/arm64/boot/dts/allwinner/Makefile @@ -27,8 +26,9 @@ new file mode 100644 index 000000000..cc5a73026 --- /dev/null +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts -@@ -0,0 +1,398 @@ +@@ -0,0 +1,399 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++// Copyright (C) 2023 Jernej Skrabec +// Copyright (C) 2019 OndÅ™ej Jirman + +/dts-v1/; @@ -43,10 +43,8 @@ index 000000000..cc5a73026 + compatible = "xunlong,orangepi-3-lts", "allwinner,sun50i-h6"; + + aliases { -+ serial0 = &uart0; -+ serial1 = &uart1; -+ serial9 = &r_uart; + ethernet0 = &emac; ++ serial0 = &uart0; + }; + + chosen { @@ -135,6 +133,19 @@ index 000000000..cc5a73026 + }; +}; + ++&ac200_codec { ++ avcc-supply = <®_aldo2>; ++ status = "okay"; ++}; ++ ++&ac200_pwm_clk { ++ status = "okay"; ++}; ++ ++&analog { ++ status = "okay"; ++}; ++ +&cpu0 { + cpu-supply = <®_dcdca>; +}; @@ -155,12 +166,24 @@ index 000000000..cc5a73026 + status = "okay"; +}; + ++&emac { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&ext_rgmii_pins>; ++ phy-mode = "rgmii-id"; ++ phy-handle = <&ext_rgmii_phy>; ++ phy-supply = <®_gmac_3v3>; ++ allwinner,rx-delay-ps = <200>; ++ allwinner,tx-delay-ps = <300>; ++ status = "okay"; ++}; ++ +&gpu { + mali-supply = <®_dcdcc>; + status = "okay"; +}; + +&hdmi { ++ hvcc-supply = <®_bldo2>; + status = "okay"; +}; + @@ -170,14 +193,15 @@ index 000000000..cc5a73026 + }; +}; + -+&emac { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&ext_rgmii_pins>; -+ phy-mode = "rgmii-id"; -+ phy-handle = <&ext_rgmii_phy>; -+ phy-supply = <®_gmac_3v3>; -+ allwinner,rx-delay-ps = <200>; -+ allwinner,tx-delay-ps = <300>; ++&i2c3 { ++ status = "okay"; ++}; ++ ++&i2s1 { ++ status = "okay"; ++}; ++ ++&i2s3 { + status = "okay"; +}; + @@ -192,10 +216,6 @@ index 000000000..cc5a73026 + }; +}; + -+&i2s1 { -+ status = "okay"; -+}; -+ +&mmc0 { + vmmc-supply = <®_cldo1>; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ @@ -213,8 +233,6 @@ index 000000000..cc5a73026 +}; + +&mmc2 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&mmc2_pins>; + vmmc-supply = <®_cldo1>; + vqmmc-supply = <®_bldo2>; + bus-width = <8>; @@ -238,12 +256,20 @@ index 000000000..cc5a73026 + vcc-pg-supply = <®_vcc_wifi_io>; +}; + -+&r_rsb { ++&pwm { ++ status = "okay"; ++}; ++ ++&r_ir { ++ status = "okay"; ++}; ++ ++&r_i2c { + status = "okay"; + -+ axp805: pmic@745 { ++ axp805: pmic@36 { + compatible = "x-powers,axp805", "x-powers,axp806"; -+ reg = <0x745>; ++ reg = <0x36>; + interrupt-parent = <&r_intc>; + interrupts = ; + interrupt-controller; @@ -296,11 +322,11 @@ index 000000000..cc5a73026 + regulator-name = "vcc-efuse-pcie-hdmi-pc"; + }; + -+ reg_blod3: bldo3 { ++ reg_bldo3: bldo3 { + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; -+ regulator-name = "vcc-wifi-io-pm-pg"; ++ regulator-name = "vcc-pm-pg-dcxoio-wifi"; + }; + + bldo4 { @@ -359,26 +385,6 @@ index 000000000..cc5a73026 + }; +}; + -+&pwm { -+ status = "okay"; -+}; -+ -+&ac200_pwm_clk { -+ status = "okay"; -+}; -+ -+&i2s3 { -+ status = "okay"; -+}; -+ -+&i2c3 { -+ status = "okay"; -+}; -+ -+&r_ir { -+ status = "okay"; -+}; -+ +&rtc { + clocks = <&ext_osc32k>; +}; @@ -387,12 +393,6 @@ index 000000000..cc5a73026 + status = "okay"; +}; + -+&sound_ac200 { -+ status = "okay"; -+}; -+ -+/delete-node/ &spi0; -+ +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_ph_pins>; @@ -426,185 +426,6 @@ index 000000000..cc5a73026 +&usb3phy { + status = "okay"; +}; -diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi -index 11e905afa..791c124f4 100644 ---- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi -+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi -@@ -92,6 +92,13 @@ osc24M: osc24M_clk { - clock-output-names = "osc24M"; - }; - -+ ext_osc32k: ext_osc32k_clk { -+ #clock-cells = <0>; -+ compatible = "fixed-clock"; -+ clock-frequency = <32768>; -+ clock-output-names = "ext_osc32k"; -+ }; -+ - pmu { - compatible = "arm,cortex-a53-pmu"; - interrupts = , -@@ -127,6 +134,28 @@ cpu { - }; - }; - -+ sound_ac200: sound_ac200 { -+ status = "disabled"; -+ compatible = "simple-audio-card"; -+ simple-audio-card,format = "i2s"; -+ simple-audio-card,frame-master = <&i2s3_master>; -+ simple-audio-card,bitclock-master = <&i2s3_master>; -+ simple-audio-card,name = "allwinner,ac200-codec"; -+ simple-audio-card,mclk-fs = <512>; -+ i2s3_master: simple-audio-card,cpu { -+ sound-dai = <&i2s3>; -+ system-clock-frequency = <22579200>; -+ dai-tdm-slot-num = <2>; -+ dai-tdm-slot-width = <32>; -+ }; -+ simple-audio-card,codec { -+ sound-dai = <&ac200_codec>; -+ system-clock-frequency = <22579200>; -+ dai-tdm-slot-num = <2>; -+ dai-tdm-slot-width = <32>; -+ }; -+ }; -+ - timer { - compatible = "arm,armv8-timer"; - arm,no-tick-in-suspend; -@@ -382,7 +411,6 @@ pwm: pwm@300a000 { - pio: pinctrl@300b000 { - compatible = "allwinner,sun50i-h6-pinctrl"; - reg = <0x0300b000 0x400>; -- interrupt-parent = <&r_intc>; - interrupts = , - , - , -@@ -532,6 +560,11 @@ uart3_rts_cts_pins: uart3-rts-cts-pins { - pins = "PD25", "PD26"; - function = "uart3"; - }; -+ -+ i2s3_pins: i2s3-pins { -+ pins = "PB12", "PB13", "PB14", "PB15", "PB16"; -+ function = "i2s3"; -+ }; - }; - - iommu: iommu@30f0000 { -@@ -730,6 +763,7 @@ i2c3: i2c@5002c00 { - ac200: mfd@10 { - compatible = "x-powers,ac200"; - reg = <0x10>; -+ clocks = <&ac200_pwm_clk>; - interrupt-parent = <&pio>; - interrupts = <1 20 IRQ_TYPE_LEVEL_LOW>; - interrupt-controller; -@@ -737,11 +771,16 @@ ac200: mfd@10 { - - ac200_ephy: phy { - compatible = "x-powers,ac200-ephy"; -- clocks = <&ac200_pwm_clk>; - nvmem-cells = <&ephy_calibration>; - nvmem-cell-names = "calibration"; - status = "disabled"; - }; -+ -+ ac200_codec: codec { -+ #sound-dai-cells = <0>; -+ compatible = "x-powers,ac200-codec"; -+ status = "okay"; -+ }; - }; - }; - -@@ -778,6 +817,21 @@ i2s1: i2s@5091000 { - status = "disabled"; - }; - -+ i2s3: i2s@508f000 { -+ #sound-dai-cells = <0>; -+ compatible = "allwinner,sun50i-h6-i2s"; -+ reg = <0x0508f000 0x1000>; -+ interrupts = ; -+ clocks = <&ccu CLK_BUS_I2S3>, <&ccu CLK_I2S3>; -+ clock-names = "apb", "mod"; -+ dmas = <&dma 6>, <&dma 6>; -+ resets = <&ccu RST_BUS_I2S3>; -+ dma-names = "rx", "tx"; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2s3_pins>; -+ status = "disabled"; -+ }; -+ - spdif: spdif@5093000 { - #sound-dai-cells = <0>; - compatible = "allwinner,sun50i-h6-spdif"; -@@ -1073,6 +1127,7 @@ rtc: rtc@7000000 { - interrupts = , - ; - clock-output-names = "osc32k", "osc32k-out", "iosc"; -+ clocks = <&ext_osc32k>; - #clock-cells = <1>; - }; - -@@ -1139,17 +1194,18 @@ r_uart_pins: r-uart-pins { - }; - - r_ir: ir@7040000 { -- compatible = "allwinner,sun50i-h6-ir", -- "allwinner,sun6i-a31-ir"; -- reg = <0x07040000 0x400>; -- interrupts = ; -- clocks = <&r_ccu CLK_R_APB1_IR>, -- <&r_ccu CLK_IR>; -- clock-names = "apb", "ir"; -- resets = <&r_ccu RST_R_APB1_IR>; -- pinctrl-names = "default"; -- pinctrl-0 = <&r_ir_rx_pin>; -- status = "disabled"; -+ compatible = "allwinner,sun50i-h6-ir", -+ "allwinner,sun6i-a31-ir"; -+ reg = <0x07040000 0x400>; -+ interrupt-parent = <&r_intc>; -+ interrupts = ; -+ clocks = <&r_ccu CLK_R_APB1_IR>, -+ <&r_ccu CLK_IR>; -+ clock-names = "apb", "ir"; -+ resets = <&r_ccu RST_R_APB1_IR>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&r_ir_rx_pin>; -+ status = "disabled"; - }; - - r_i2c: i2c@7081400 { -@@ -1191,6 +1247,25 @@ ths: thermal-sensor@5070400 { - nvmem-cell-names = "calibration"; - #thermal-sensor-cells = <1>; - }; -+ -+ sunxi-info { -+ compatible = "allwinner,sun50i-h6-sys-info"; -+ status = "okay"; -+ }; -+ -+ addr_mgt: addr-mgt { -+ compatible = "allwinner,sunxi-addr_mgt"; -+ type_addr_wifi = <0x2>; -+ type_addr_bt = <0x2>; -+ type_addr_eth = <0x2>; -+ status = "okay"; -+ }; -+ -+ dump_reg: dump_reg@20000 { -+ compatible = "allwinner,sunxi-dump-reg"; -+ reg = <0x0 0x03001000 0x0 0x0f20>; -+ status = "okay"; -+ }; - }; - - thermal-zones { -- 2.35.3 diff --git a/patch/kernel/archive/sunxi-6.1/patches.armbian/add-nodes-for-sunxi-info-addr-dump-reg.patch b/patch/kernel/archive/sunxi-6.1/patches.armbian/add-nodes-for-sunxi-info-addr-dump-reg.patch new file mode 100644 index 000000000..2fc8f7f55 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.1/patches.armbian/add-nodes-for-sunxi-info-addr-dump-reg.patch @@ -0,0 +1,42 @@ +From c21550d5fbd11743bee19e142d3eb04d9ea68177 Mon Sep 17 00:00:00 2001 +From: The-going <48602507+The-going@users.noreply.github.com> +Date: Sat, 16 Apr 2022 11:51:35 +0300 +Subject: [PATCH 151/158] add nodes for sunxi-info, sunxi-addr and sunxi-dump-reg + +--- + arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 101 ++++- + 1 files changed, 487 insertions(+), 13 deletions(-) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +index 11e905afa..791c124f4 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +@@ -1191,6 +1247,25 @@ ths: thermal-sensor@5070400 { + nvmem-cell-names = "calibration"; + #thermal-sensor-cells = <1>; + }; ++ ++ sunxi-info { ++ compatible = "allwinner,sun50i-h6-sys-info"; ++ status = "okay"; ++ }; ++ ++ addr_mgt: addr-mgt { ++ compatible = "allwinner,sunxi-addr_mgt"; ++ type_addr_wifi = <0x2>; ++ type_addr_bt = <0x2>; ++ type_addr_eth = <0x2>; ++ status = "okay"; ++ }; ++ ++ dump_reg: dump_reg@20000 { ++ compatible = "allwinner,sunxi-dump-reg"; ++ reg = <0x0 0x03001000 0x0 0x0f20>; ++ status = "okay"; ++ }; + }; + + thermal-zones { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.1/patches.armbian/allwinner-h6-Support-ac200-audio-codec.patch b/patch/kernel/archive/sunxi-6.1/patches.armbian/allwinner-h6-Support-ac200-audio-codec.patch deleted file mode 100644 index 44b6299ff..000000000 --- a/patch/kernel/archive/sunxi-6.1/patches.armbian/allwinner-h6-Support-ac200-audio-codec.patch +++ /dev/null @@ -1,1875 +0,0 @@ -From 9c44539b138423709e988c8b2ecaea9f399ff24f Mon Sep 17 00:00:00 2001 -From: afaulkner420 -Date: Fri, 25 Mar 2022 20:33:02 +0000 -Subject: [PATCH] allwinner: h6: Support ac200 audio codec - ---- - drivers/mfd/Makefile | 2 +- - drivers/mfd/{ac200.c => sunxi-ac200.c} | 16 +- - include/linux/mfd/ac200.h | 2 + - sound/soc/codecs/Kconfig | 8 + - sound/soc/codecs/Makefile | 2 + - sound/soc/codecs/acx00.c | 1371 ++++++++++++++++++++++++ - sound/soc/codecs/acx00.h | 356 ++++++ - 7 files changed, 1755 insertions(+), 2 deletions(-) - rename drivers/mfd/{ac200.c => sunxi-ac200.c} (93%) - create mode 100644 sound/soc/codecs/acx00.c - create mode 100644 sound/soc/codecs/acx00.h - -diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile -index 91dc530d0..f2e05f275 100644 ---- a/drivers/mfd/Makefile -+++ b/drivers/mfd/Makefile -@@ -144,7 +144,7 @@ obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o - obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o - - obj-$(CONFIG_MFD_AC100) += ac100.o --obj-$(CONFIG_MFD_AC200) += ac200.o -+obj-$(CONFIG_MFD_AC200) += sunxi-ac200.o - obj-$(CONFIG_MFD_AXP20X) += axp20x.o - obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o - obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o -diff --git a/drivers/mfd/ac200.c b/drivers/mfd/sunxi-ac200.c -similarity index 93% -rename from drivers/mfd/ac200.c -rename to drivers/mfd/sunxi-ac200.c -index 570573790..368a54587 100644 ---- a/drivers/mfd/ac200.c -+++ b/drivers/mfd/sunxi-ac200.c -@@ -41,6 +41,7 @@ static const struct regmap_range_cfg ac200_range_cfg[] = { - }; - - static const struct regmap_config ac200_regmap_config = { -+ .name = "ac200", - .reg_bits = 8, - .val_bits = 16, - .ranges = ac200_range_cfg, -@@ -75,6 +76,10 @@ static const struct mfd_cell ac200_cells[] = { - .resources = ephy_resource, - .of_compatible = "x-powers,ac200-ephy", - }, -+ { -+ .name = "acx00-codec", -+ .of_compatible = "x-powers,ac200-codec", -+ }, - }; - - static int ac200_i2c_probe(struct i2c_client *i2c, -@@ -97,8 +102,17 @@ static int ac200_i2c_probe(struct i2c_client *i2c, - return ret; - } - -- /* do a reset to put chip in a known state */ -+ ac200->clk = devm_clk_get(dev, NULL); -+ if (IS_ERR(ac200->clk)) { -+ dev_err(dev, "Can't obtain the clock!\n"); -+ return PTR_ERR(ac200->clk); -+ } - -+ ret = clk_prepare_enable(ac200->clk); -+ if (ret) -+ return ret; -+ -+ /* do a reset to put chip in a known state */ - ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0); - if (ret) - return ret; -diff --git a/include/linux/mfd/ac200.h b/include/linux/mfd/ac200.h -index 0c677094a..c8c140226 100644 ---- a/include/linux/mfd/ac200.h -+++ b/include/linux/mfd/ac200.h -@@ -9,6 +9,7 @@ - #define __LINUX_MFD_AC200_H - - #include -+#include - - /* interface registers (can be accessed from any page) */ - #define AC200_TWI_CHANGE_TO_RSB 0x3E -@@ -201,6 +202,7 @@ - #define AC200_IC_CHARA1 0xA1F2 - - struct ac200_dev { -+ struct clk *clk; - struct regmap *regmap; - struct regmap_irq_chip_data *regmap_irqc; - }; -diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig -index d7548c916..d3158ef17 100644 ---- a/sound/soc/codecs/Kconfig -+++ b/sound/soc/codecs/Kconfig -@@ -2164,4 +2164,12 @@ config SND_SOC_LPASS_TX_MACRO - select SND_SOC_LPASS_MACRO_COMMON - tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" - -+config SND_SOC_ACX00 -+ tristate "ACX00 Codec" -+ select MFD_ACX00 -+ default n -+ help -+ ACX00 now used as SUN50IW6 internal Codec, Connect Through I2S0. -+ Say Y or M if you want to add support internal audio codec. -+ - endmenu -diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile -index e6e050c23..96fd048b2 100644 ---- a/sound/soc/codecs/Makefile -+++ b/sound/soc/codecs/Makefile -@@ -346,6 +346,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o - snd-soc-wsa881x-objs := wsa881x.o - snd-soc-wsa883x-objs := wsa883x.o - snd-soc-zl38060-objs := zl38060.o -+snd-soc-acx00-objs := acx00.o - # Amp - snd-soc-max9877-objs := max9877.o - snd-soc-max98504-objs := max98504.o -@@ -707,6 +708,7 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o - obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o - obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o - obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o -+obj-$(CONFIG_SND_SOC_ACX00) += snd-soc-acx00.o - - # Amp - obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o -diff --git a/sound/soc/codecs/acx00.c b/sound/soc/codecs/acx00.c -new file mode 100644 -index 000000000..ab7467e4e ---- /dev/null -+++ b/sound/soc/codecs/acx00.c -@@ -0,0 +1,1371 @@ -+/* -+ * acx00.c -- ACX00 ALSA Soc Audio Codec driver -+ * -+ * (C) Copyright 2010-2016 Allwinnertech Technology., Ltd. -+ * -+ * Author: Wolfgang Huang -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "acx00.h" -+ -+ -+#define ACX00_DEF_VOL 0x9F9F -+#undef ACX00_DAPM_LINEOUT -+ -+struct acx00_priv { -+ struct ac200_dev *acx00; /* parent mfd device struct */ -+ struct snd_soc_component *component; -+ struct clk *clk; -+ unsigned int sample_rate; -+ unsigned int fmt; -+ unsigned int enable; -+ unsigned int spk_gpio; -+ unsigned int switch_gpio; -+ bool spk_gpio_used; -+ struct mutex mutex; -+ struct delayed_work spk_work; -+ struct delayed_work resume_work; -+}; -+ -+struct sample_rate { -+ unsigned int samplerate; -+ unsigned int rate_bit; -+}; -+ -+static const struct sample_rate sample_rate_conv[] = { -+ {44100, 7}, -+ {48000, 8}, -+ {8000, 0}, -+ {32000, 6}, -+ {22050, 4}, -+ {24000, 5}, -+ {16000, 3}, -+ {11025, 1}, -+ {12000, 2}, -+ {192000, 10}, -+ {96000, 9}, -+}; -+ -+void __iomem *io_stat_addr; -+ -+static const DECLARE_TLV_DB_SCALE(i2s_mixer_adc_tlv, -600, 600, 1); -+static const DECLARE_TLV_DB_SCALE(i2s_mixer_dac_tlv, -600, 600, 1); -+static const DECLARE_TLV_DB_SCALE(dac_mixer_adc_tlv, -600, 600, 1); -+static const DECLARE_TLV_DB_SCALE(dac_mixer_dac_tlv, -600, 600, 1); -+static const DECLARE_TLV_DB_SCALE(line_out_tlv, -450, 150, 0); -+static const DECLARE_TLV_DB_SCALE(mic_out_tlv, -450, 150, 0); -+static const DECLARE_TLV_DB_SCALE(phoneout_tlv, -450, 150, 0); -+static const DECLARE_TLV_DB_SCALE(adc_input_tlv, -450, 150, 0); -+static const DECLARE_TLV_DB_SCALE(lineout_tlv, -4800, 150, 1); -+static const unsigned int mic_boost_tlv[] = { -+ 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 acx00_codec_controls[] = { -+ SOC_DOUBLE_TLV("I2S Mixer ADC Volume", AC_I2S_MIXER_GAIN, -+ I2S_MIXERL_GAIN_ADC, I2S_MIXERR_GAIN_ADC, -+ 0x1, 0, i2s_mixer_adc_tlv), -+ SOC_DOUBLE_TLV("I2S Mixer DAC Volume", AC_I2S_MIXER_GAIN, -+ I2S_MIXERL_GAIN_DAC, I2S_MIXERR_GAIN_DAC, -+ 0x1, 0, i2s_mixer_dac_tlv), -+ SOC_DOUBLE_TLV("DAC Mixer ADC Volume", AC_DAC_MIXER_GAIN, -+ DAC_MIXERL_GAIN_ADC, DAC_MIXERR_GAIN_ADC, -+ 0x1, 0, dac_mixer_adc_tlv), -+ SOC_DOUBLE_TLV("DAC Mxier DAC Volume", AC_DAC_MIXER_GAIN, -+ DAC_MIXERL_GAIN_DAC, DAC_MIXERR_GAIN_DAC, -+ 0x1, 0, dac_mixer_dac_tlv), -+ SOC_SINGLE_TLV("Line Out Mixer Volume", AC_OUT_MIXER_CTL, -+ OUT_MIXER_LINE_VOL, 0x7, 0, line_out_tlv), -+ SOC_DOUBLE_TLV("MIC Out Mixer Volume", AC_OUT_MIXER_CTL, -+ OUT_MIXER_MIC1_VOL, OUT_MIXER_MIC2_VOL, -+ 0x7, 0, mic_out_tlv), -+ SOC_SINGLE_TLV("ADC Input Volume", AC_ADC_MIC_CTL, -+ ADC_GAIN, 0x07, 0, adc_input_tlv), -+ SOC_SINGLE_TLV("Master Volume", AC_LINEOUT_CTL, -+ LINEOUT_VOL, 0x1f, 0, lineout_tlv), -+ SOC_SINGLE_TLV("MIC1 Boost Volume", AC_ADC_MIC_CTL, -+ MIC1_BOOST, 0x07, 0, mic_boost_tlv), -+ SOC_SINGLE_TLV("MIC2 Boost Volume", AC_ADC_MIC_CTL, -+ MIC2_BOOST, 0x07, 0, mic_boost_tlv), -+}; -+ -+/* Enable I2S & DAC clk, then enable the DAC digital part */ -+static int acx00_playback_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *k, int event) -+{ -+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ snd_soc_component_update_bits(component, AC_SYS_CLK_CTL, -+ (0x1<dapm); -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ snd_soc_component_update_bits(component, AC_SYS_CLK_CTL, -+ (0x1<spk_gpio, 1); -+} -+ -+static int acx00_lineout_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *k, int event) -+{ -+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); -+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component); -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ if (!priv->enable) { -+ snd_soc_component_update_bits(priv->component, AC_LINEOUT_CTL, -+ (1<component, AC_LINEOUT_CTL, -+ (1<enable = 1; -+ } -+#ifdef ACX00_DAPM_LINEOUT -+ snd_soc_component_update_bits(component, AC_LINEOUT_CTL, -+ (1<spk_gpio_used) { -+ if (spk_delay == 0) { -+ gpio_set_value(priv->spk_gpio, 1); -+ /* -+ * time delay to wait spk pa work fine, -+ * general setting 50ms -+ */ -+ mdelay(50); -+ } else -+ schedule_delayed_work(&priv->spk_work, -+ msecs_to_jiffies(spk_delay)); -+ } -+ break; -+ case SND_SOC_DAPM_PRE_PMD: -+ mdelay(50); -+ if (priv->spk_gpio_used) { -+ gpio_set_value(priv->spk_gpio, 0); -+ msleep(50); -+ } -+#ifdef ACX00_DAPM_LINEOUT -+ snd_soc_component_update_bits(component, AC_LINEOUT_CTL, -+ (1<regmap, reg, &val); -+ -+ if (ret < 0) -+ return ret; -+ else -+ return val; -+} -+ -+int acx00_reg_write(struct ac200_dev *acx00, unsigned short reg, unsigned short val) -+{ -+ return regmap_write(acx00->regmap, reg, val); -+} -+ -+static void acx00_codec_init(struct snd_soc_component *component) -+{ -+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component); -+ -+ acx00_reg_write(priv->acx00, 0x50, 0x82b1); -+ acx00_reg_write(priv->acx00, 0xc, 0xce01); -+ -+ /* acx00_codec sysctl init */ -+ acx00_reg_write(priv->acx00, 0x0010, 0x03); -+ acx00_reg_write(priv->acx00, 0x0012, 0x01); -+ -+ /* The bit3 need to setup to 1 for bias current. */ -+ snd_soc_component_update_bits(component, AC_MICBIAS_CTL, -+ (0x1 << ADDA_BIAS_CUR), (0x1 << ADDA_BIAS_CUR)); -+ -+ /* enable the output & global enable bit */ -+ snd_soc_component_update_bits(component, AC_I2S_CTL, -+ (1<spk_gpio_used) { -+ snd_soc_component_update_bits(priv->component, AC_LINEOUT_CTL, -+ (1<component, AC_LINEOUT_CTL, -+ (1<enable = 1; -+ } -+#ifndef ACX00_DAPM_LINEOUT -+ snd_soc_component_update_bits(component, AC_LINEOUT_CTL, (1<component; -+ int i; -+ -+ switch (params_format(params)) { -+ case SNDRV_PCM_FORMAT_S16_LE: -+ snd_soc_component_update_bits(component, AC_I2S_FMT0, -+ (7<dev, "unrecognized format support\n"); -+ break; -+ } -+ for (i = 0; i < ARRAY_SIZE(sample_rate_conv); i++) { -+ if (sample_rate_conv[i].samplerate == params_rate(params)) { -+ snd_soc_component_update_bits(component, AC_SYS_SR_CTL, -+ (SYS_SR_MASK<component; -+ -+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { -+ /* codec clk & FRM master */ -+ case SND_SOC_DAIFMT_CBM_CFM: -+ snd_soc_component_update_bits(component, AC_I2S_CLK, -+ (0x1<dev, "format setting failed\n"); -+ break; -+ } -+ -+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { -+ case SND_SOC_DAIFMT_NB_NF: -+ snd_soc_component_update_bits(component, AC_I2S_FMT1, -+ (0x1<dev, "invert clk setting failed\n"); -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+static int acx00_codec_dai_set_clkdiv(struct snd_soc_dai *codec_dai, -+ int clk_id, int clk_div) -+{ -+ struct acx00_priv *priv = snd_soc_dai_get_drvdata(codec_dai); -+ struct snd_soc_component *component = priv->component; -+ unsigned int bclk_div; -+ /* -+ * when PCM mode, setting as 64fs, when I2S mode as 32fs, -+ * then two channel, then just as 64fs -+ */ -+ unsigned int div_ratio = clk_div / 64; -+ -+ switch (div_ratio) { -+ case 1: -+ bclk_div = I2S_BCLK_DIV_1; -+ break; -+ case 2: -+ bclk_div = I2S_BCLK_DIV_2; -+ break; -+ case 4: -+ bclk_div = I2S_BCLK_DIV_3; -+ break; -+ case 6: -+ bclk_div = I2S_BCLK_DIV_4; -+ break; -+ case 8: -+ bclk_div = I2S_BCLK_DIV_5; -+ break; -+ case 12: -+ bclk_div = I2S_BCLK_DIV_6; -+ break; -+ case 16: -+ bclk_div = I2S_BCLK_DIV_7; -+ break; -+ case 24: -+ bclk_div = I2S_BCLK_DIV_8; -+ break; -+ case 32: -+ bclk_div = I2S_BCLK_DIV_9; -+ break; -+ case 48: -+ bclk_div = I2S_BCLK_DIV_10; -+ break; -+ case 64: -+ bclk_div = I2S_BCLK_DIV_11; -+ break; -+ case 96: -+ bclk_div = I2S_BCLK_DIV_12; -+ break; -+ case 128: -+ bclk_div = I2S_BCLK_DIV_13; -+ break; -+ case 176: -+ bclk_div = I2S_BCLK_DIV_14; -+ break; -+ case 192: -+ bclk_div = I2S_BCLK_DIV_15; -+ break; -+ default: -+ dev_err(component->dev, "setting blck div failed\n"); -+ break; -+ } -+ -+ snd_soc_component_update_bits(component, AC_I2S_CLK, -+ (I2S_BCLK_DIV_MASK<component; -+ return 0; -+} -+ -+static int acx00_codec_prepare(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *codec_dai) -+{ -+ struct snd_soc_component *component = codec_dai->component; -+ -+ snd_soc_component_update_bits(component, AC_SYS_CLK_CTL, -+ (0x1<stream == SNDRV_PCM_STREAM_PLAYBACK) { -+ if (acx00_loop_en) -+ snd_soc_component_update_bits(component, AC_I2S_FMT0, -+ (0x1<component; -+ -+ if (mute) -+ snd_soc_component_write(component, AC_I2S_DAC_VOL, 0); -+ else -+ snd_soc_component_write(component, AC_I2S_DAC_VOL, ACX00_DEF_VOL); -+ return 0; -+} -+ -+static void acx00_codec_shutdown(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct snd_soc_component *component = dai->component; -+ -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) -+ acx00_codec_txctrl_enable(component, 0); -+ else -+ acx00_codec_rxctrl_enable(component, 0); -+} -+ -+static const struct snd_soc_dai_ops acx00_codec_dai_ops = { -+ .hw_params = acx00_codec_hw_params, -+ .shutdown = acx00_codec_shutdown, -+// .digital_mute = acx00_codec_digital_mute, -+ .set_sysclk = acx00_codec_dai_set_sysclk, -+ .set_fmt = acx00_codec_dai_set_fmt, -+ .set_clkdiv = acx00_codec_dai_set_clkdiv, -+ .startup = acx00_codec_startup, -+ .trigger = acx00_codec_trigger, -+ .prepare = acx00_codec_prepare, -+}; -+ -+static struct snd_soc_dai_driver acx00_codec_dai[] = { -+ { -+ .name = "acx00-dai", -+ .playback = { -+ .stream_name = "Playback", -+ .channels_min = 1, -+ .channels_max = 2, -+ .rates = SNDRV_PCM_RATE_8000_192000 -+ | SNDRV_PCM_RATE_KNOT, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE -+ | SNDRV_PCM_FMTBIT_S24_LE -+ | SNDRV_PCM_FMTBIT_S32_LE, -+ }, -+ -+ .capture = { -+ .stream_name = "Capture", -+ .channels_min = 1, -+ .channels_max = 2, -+ .rates = SNDRV_PCM_RATE_8000_192000 -+ | SNDRV_PCM_RATE_KNOT, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE -+ | SNDRV_PCM_FMTBIT_S24_LE -+ | SNDRV_PCM_FMTBIT_S32_LE, -+ }, -+ -+ .ops = &acx00_codec_dai_ops, -+ }, -+}; -+ -+static void acx00_codec_resume_work(struct work_struct *work) -+{ -+ struct acx00_priv *priv = container_of(work, -+ struct acx00_priv, resume_work.work); -+ -+ acx00_codec_init(priv->component); -+} -+ -+static int acx00_codec_probe(struct snd_soc_component *component) -+{ -+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component); -+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); -+ int ret = 0; -+ -+ mutex_init(&priv->mutex); -+ -+ priv->component = component; -+#if 0 -+ /* Add virtual switch */ -+ ret = snd_soc_add_component_controls(component, acx00_codec_controls, -+ ARRAY_SIZE(acx00_codec_controls)); -+ if (ret) { -+ pr_err("[audio-codec] Failed to register audio mode control, will continue without it.\n"); -+ } -+ snd_soc_dapm_new_controls(dapm, acx00_codec_dapm_widgets, ARRAY_SIZE(acx00_codec_dapm_widgets)); -+ snd_soc_dapm_add_routes(dapm, acx00_codec_dapm_routes, ARRAY_SIZE(acx00_codec_dapm_routes)); -+#endif -+ /* using late_initcall to wait 120ms acx00-core to make chip reset */ -+ acx00_codec_init(component); -+ INIT_DELAYED_WORK(&priv->spk_work, acx00_spk_enable); -+ INIT_DELAYED_WORK(&priv->resume_work, acx00_codec_resume_work); -+ return 0; -+} -+ -+static void acx00_codec_remove(struct snd_soc_component *component) -+{ -+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component); -+ -+ cancel_delayed_work_sync(&priv->spk_work); -+ cancel_delayed_work_sync(&priv->resume_work); -+} -+ -+static unsigned int acx00_codec_read(struct snd_soc_component *component, -+ unsigned int reg) -+{ -+ unsigned int data; -+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component); -+ -+ /* Device I/O API */ -+ data = acx00_reg_read(priv->acx00, reg); -+ return data; -+} -+ -+static int acx00_codec_write(struct snd_soc_component *component, -+ unsigned int reg, unsigned int value) -+{ -+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component); -+ -+ return acx00_reg_write(priv->acx00, reg, value); -+} -+ -+static int sunxi_gpio_iodisable(u32 gpio) -+{ -+ char pin_name[8]; -+ u32 config, ret; -+#if 0 -+ sunxi_gpio_to_name(gpio, pin_name); -+ config = 7 << 16; -+ ret = pin_config_set(SUNXI_PINCTRL, pin_name, config); -+#endif -+ return ret; -+} -+ -+static int acx00_codec_suspend(struct snd_soc_component *component) -+{ -+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component); -+ -+ pr_debug("Enter %s\n", __func__); -+ -+ clk_disable_unprepare(priv->clk); -+ -+ /* PA_CTRL first setting low state, then make it iodisabled */ -+ if (priv->spk_gpio_used) { -+ sunxi_gpio_iodisable(priv->spk_gpio); -+ msleep(30); -+ } -+ -+ /* -+ * when codec suspend, then the register reset, if auto reset produce -+ * Pop & Click noise, then we should cut down the LINEOUT in this town. -+ */ -+ if (priv->enable) { -+ snd_soc_component_update_bits(component, AC_LINEOUT_CTL, -+ (1<component, AC_LINEOUT_CTL, -+ (1<component, AC_LINEOUT_CTL, -+ (1<enable = 0; -+ } -+ -+ pr_debug("Exit %s\n", __func__); -+ -+ return 0; -+} -+ -+static int acx00_codec_resume(struct snd_soc_component *component) -+{ -+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component); -+ -+ pr_debug("Enter %s\n", __func__); -+ -+ if (clk_prepare_enable(priv->clk)) { -+ dev_err(component->dev, "codec resume clk failed\n"); -+ return -EBUSY; -+ } -+ -+ schedule_delayed_work(&priv->resume_work, msecs_to_jiffies(300)); -+ -+ if (priv->spk_gpio_used) { -+ gpio_direction_output(priv->spk_gpio, 1); -+ gpio_set_value(priv->spk_gpio, 0); -+ } -+ -+ pr_debug("Exit %s\n", __func__); -+ -+ return 0; -+} -+ -+ -+static int acx00_codec_set_bias_level(struct snd_soc_component *component, -+ enum snd_soc_bias_level level) -+{ -+ component->dapm.bias_level = level; -+ return 0; -+} -+ -+struct label { -+ const char *name; -+ int value; -+}; -+ -+#define LABEL(constant) { #constant, constant } -+#define LABEL_END { NULL, -1 } -+ -+static struct label reg_labels[] = { -+ LABEL(AC_SYS_CLK_CTL), -+ LABEL(AC_SYS_MOD_RST), -+ LABEL(AC_SYS_SR_CTL), -+ LABEL(AC_I2S_CTL), -+ LABEL(AC_I2S_CLK), -+ LABEL(AC_I2S_FMT0), -+ LABEL(AC_I2S_FMT1), -+ LABEL(AC_I2S_MIXER_SRC), -+ LABEL(AC_I2S_MIXER_GAIN), -+ LABEL(AC_I2S_DAC_VOL), -+ LABEL(AC_I2S_ADC_VOL), -+ LABEL(AC_DAC_CTL), -+ LABEL(AC_DAC_MIXER_SRC), -+ LABEL(AC_DAC_MIXER_GAIN), -+ LABEL(AC_OUT_MIXER_CTL), -+ LABEL(AC_OUT_MIXER_SRC), -+ LABEL(AC_LINEOUT_CTL), -+ LABEL(AC_ADC_CTL), -+ LABEL(AC_MICBIAS_CTL), -+ LABEL(AC_ADC_MIC_CTL), -+ LABEL(AC_ADC_MIXER_SRC), -+ LABEL(AC_BIAS_CTL), -+ LABEL(AC_ANALOG_PROF_CTL), -+ LABEL_END, -+}; -+ -+static ssize_t show_audio_reg(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct acx00_priv *priv = dev_get_drvdata(dev); -+ int count = 0, i = 0; -+ unsigned int reg_val; -+ -+ count += sprintf(buf, "dump audio reg:\n"); -+ -+ while (reg_labels[i].name != NULL) { -+ reg_val = acx00_reg_read(priv->acx00, reg_labels[i].value); -+ count += sprintf(buf + count, "%s 0x%x: 0x%x\n", -+ reg_labels[i].name, (reg_labels[i].value), reg_val); -+ i++; -+ } -+ -+ return count; -+} -+ -+/* -+ * param 1: 0 read;1 write -+ * param 2: 1 digital reg; 2 analog reg -+ * param 3: reg value; -+ * param 4: write value; -+ * read: -+ * echo 0,1,0x00> audio_reg -+ * echo 0,2,0x00> audio_reg -+ * write: -+ * echo 1,1,0x00,0xa > audio_reg -+ * echo 1,2,0x00,0xff > audio_reg -+*/ -+static ssize_t store_audio_reg(struct device *dev, -+ struct device_attribute *attr, const char *buf, size_t count) -+{ -+ int ret; -+ int rw_flag; -+ unsigned int input_reg_val = 0; -+ int input_reg_group = 0; -+ unsigned int input_reg_offset = 0; -+ struct acx00_priv *priv = dev_get_drvdata(dev); -+ -+ ret = sscanf(buf, "%d,%d,0x%x,0x%x", &rw_flag, &input_reg_group, -+ &input_reg_offset, &input_reg_val); -+ dev_info(dev, "ret:%d, reg_group:%d, reg_offset:%d, reg_val:0x%x\n", -+ ret, input_reg_group, input_reg_offset, input_reg_val); -+ -+ if (input_reg_group != 1) { -+ pr_err("not exist reg group\n"); -+ ret = count; -+ goto out; -+ } -+ if (!(rw_flag == 1 || rw_flag == 0)) { -+ pr_err("not rw_flag\n"); -+ ret = count; -+ goto out; -+ } -+ -+ if (rw_flag) { -+ acx00_reg_write(priv->acx00, input_reg_offset, input_reg_val); -+ } else { -+ input_reg_val = acx00_reg_read(priv->acx00, input_reg_offset); -+ dev_info(dev, "\n\n Reg[0x%x] : 0x%04x\n\n", -+ input_reg_offset, input_reg_val); -+ } -+ ret = count; -+ -+out: -+ return ret; -+} -+ -+static DEVICE_ATTR(audio_reg, 0644, show_audio_reg, store_audio_reg); -+ -+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 struct snd_soc_component_driver soc_codec_driver_acx00 = { -+ .probe = acx00_codec_probe, -+ .remove = acx00_codec_remove, -+ .suspend = acx00_codec_suspend, -+ .resume = acx00_codec_resume, -+ .read = acx00_codec_read, -+ .write = acx00_codec_write, -+// .ignore_pmdown_time = 1, -+ .set_bias_level = acx00_codec_set_bias_level, -+ .controls = acx00_codec_controls, -+ .num_controls = ARRAY_SIZE(acx00_codec_controls), -+ .dapm_widgets = acx00_codec_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(acx00_codec_dapm_widgets), -+ .dapm_routes = acx00_codec_dapm_routes, -+ .num_dapm_routes = ARRAY_SIZE(acx00_codec_dapm_routes), -+}; -+ -+/* through acx00 is part of mfd devices, after the mfd */ -+static int acx00_codec_dev_probe(struct platform_device *pdev) -+{ -+ struct acx00_priv *priv; -+ int ret; -+ struct device_node *np = of_find_compatible_node(NULL, NULL, "allwinner,ac200_codec"); -+ -+ priv = devm_kzalloc(&pdev->dev, sizeof(struct acx00_priv), GFP_KERNEL); -+ if (!priv) { -+ dev_err(&pdev->dev, "acx00 codec priv mem alloc failed\n"); -+ return -ENOMEM; -+ } -+ -+ platform_set_drvdata(pdev, priv); -+ priv->acx00 = dev_get_drvdata(pdev->dev.parent); -+ -+ if (np) { -+ ret = of_get_named_gpio(np, "gpio-spk", 0); -+ if (ret >= 0) { -+ priv->spk_gpio_used = 1; -+ priv->spk_gpio = ret; -+ if (!gpio_is_valid(priv->spk_gpio)) { -+ dev_err(&pdev->dev, "gpio-spk is valid\n"); -+ ret = -EINVAL; -+ goto err_devm_kfree; -+ } else { -+ ret = devm_gpio_request(&pdev->dev, -+ priv->spk_gpio, "SPK"); -+ if (ret) { -+ dev_err(&pdev->dev, -+ "failed request gpio-spk\n"); -+ ret = -EBUSY; -+ goto err_devm_kfree; -+ } else { -+ gpio_direction_output(priv->spk_gpio, -+ 1); -+ gpio_set_value(priv->spk_gpio, 0); -+ } -+ } -+ } else { -+ priv->spk_gpio_used = 0; -+ } -+ -+ ret = of_get_named_gpio(np, "gpio-switch", 0); -+ if (ret >= 0) { -+ priv->switch_gpio = ret; -+ if (!gpio_is_valid(priv->switch_gpio)) { -+ dev_err(&pdev->dev, "gpio-switch is valid\n"); -+ ret = -EINVAL; -+ goto err_devm_kfree; -+ } else { -+ ret = devm_gpio_request(&pdev->dev, priv->switch_gpio, "SWITCH"); -+ if (ret) { -+ dev_err(&pdev->dev, -+ "failed request gpio-switch\n"); -+ ret = -EBUSY; -+ goto err_devm_kfree; -+ } else { -+ gpio_direction_output(priv->switch_gpio, 1); -+ gpio_set_value(priv->switch_gpio, 1); -+ } -+ } -+ } -+ } -+ -+ ret = snd_soc_register_component(&pdev->dev, &soc_codec_driver_acx00, -+ acx00_codec_dai, ARRAY_SIZE(acx00_codec_dai)); -+ -+ if (ret < 0) -+ dev_err(&pdev->dev, "Failed register acx00: %d\n", ret); -+ -+ ret = sysfs_create_group(&pdev->dev.kobj, &audio_debug_attr_group); -+ if (ret) -+ dev_warn(&pdev->dev, "failed to create attr group\n"); -+ -+ return 0; -+ -+err_devm_kfree: -+ devm_kfree(&pdev->dev, priv); -+ return ret; -+} -+ -+/* Mark this space to clear the LINEOUT & gpio */ -+static void acx00_codec_dev_shutdown(struct platform_device *pdev) -+{ -+ struct acx00_priv *priv = platform_get_drvdata(pdev); -+ -+ if (priv->spk_gpio_used) -+ gpio_set_value(priv->spk_gpio, 0); -+} -+ -+static int acx00_codec_dev_remove(struct platform_device *pdev) -+{ -+ struct acx00_priv *priv = platform_get_drvdata(pdev); -+ -+#ifndef ACX00_DAPM_LINEOUT -+ /* -+ snd_soc_component_update_bits(priv->component, AC_LINEOUT_CTL, -+ (1<dev); -+ clk_disable_unprepare(priv->clk); -+ devm_kfree(&pdev->dev, priv); -+ return 0; -+} -+ -+static const struct of_device_id acx00_codec_match[] = { -+ { .compatible = "x-powers,ac200-codec" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, acx00_codec_match); -+ -+static struct platform_driver acx00_codec_driver = { -+ .driver = { -+ .name = "acx00-codec", -+ .of_match_table = acx00_codec_match, -+ }, -+ .probe = acx00_codec_dev_probe, -+ .remove = acx00_codec_dev_remove, -+ .shutdown = acx00_codec_dev_shutdown, -+}; -+ -+static int __init acx00_codec_driver_init(void) -+{ -+ return platform_driver_register(&acx00_codec_driver); -+} -+ -+static void __exit acx00_codec_driver_exit(void) -+{ -+ platform_driver_unregister(&acx00_codec_driver); -+} -+late_initcall(acx00_codec_driver_init); -+module_exit(acx00_codec_driver_exit); -+ -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("SUNXI ASoC ACX00 Codec Driver"); -+MODULE_AUTHOR("wolfgang huang"); -+MODULE_ALIAS("platform:acx00-codec"); -diff --git a/sound/soc/codecs/acx00.h b/sound/soc/codecs/acx00.h -new file mode 100644 -index 000000000..5137cf365 ---- /dev/null -+++ b/sound/soc/codecs/acx00.h -@@ -0,0 +1,356 @@ -+/* -+ * sound\soc\codecs\acx00.h -+ * (C) Copyright 2012-2016 -+ * Allwinner Technology Co., Ltd. -+ * Wolfgang Huang -+ * -+ * some simple description for this code -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation; either version 2 of -+ * the License, or (at your option) any later version. -+ * -+ */ -+ -+#ifndef __ACX00_H_ -+#define __ACX00_H_ -+ -+/* ACX00 register offset list */ -+#define AC_SYS_CLK_CTL 0x2000 -+#define AC_SYS_MOD_RST 0x2002 -+#define AC_SYS_SR_CTL 0x2004 -+/* Left blank */ -+#define AC_I2S_CTL 0x2100 -+#define AC_I2S_CLK 0x2102 -+#define AC_I2S_FMT0 0x2104 -+/* Left blank */ -+#define AC_I2S_FMT1 0x2108 -+/* Left blank */ -+#define AC_I2S_MIXER_SRC 0x2114 -+#define AC_I2S_MIXER_GAIN 0x2116 -+#define AC_I2S_DAC_VOL 0x2118 -+#define AC_I2S_ADC_VOL 0x211A -+/* Left blank */ -+#define AC_DAC_CTL 0x2200 -+#define AC_DAC_MIXER_SRC 0x2202 -+#define AC_DAC_MIXER_GAIN 0x2204 -+/* Left blank */ -+#define AC_OUT_MIXER_CTL 0x2220 -+#define AC_OUT_MIXER_SRC 0x2222 -+#define AC_LINEOUT_CTL 0x2224 -+/* Left blank */ -+#define AC_ADC_CTL 0x2300 -+/* Left blank */ -+#define AC_MICBIAS_CTL 0x2310 -+/* Left blank */ -+#define AC_ADC_MIC_CTL 0x2320 -+#define AC_ADC_MIXER_SRC 0x2322 -+/* Left blank */ -+#define AC_BIAS_CTL 0x232A -+#define AC_ANALOG_PROF_CTL 0x232C -+/* Left blank */ -+#define AC_ADC_DAPL_CTRL 0x2500 -+#define AC_ADC_DAPR_CTRL 0x2502 -+#define AC_ADC_DAPLSTA 0x2504 -+#define AC_ADC_DAPRSTA 0x2506 -+#define AC_ADC_DAP_LTL 0x2508 -+#define AC_ADC_DAP_RTL 0x250A -+#define AC_ADC_DAP_LHAC 0x250C -+#define AC_ADC_DAP_LLAC 0x250E -+#define AC_ADC_DAP_RHAC 0x2510 -+#define AC_ADC_DAP_RLAC 0x2512 -+#define AC_ADC_DAP_LDT 0x2514 -+#define AC_ADC_DAP_LAT 0x2516 -+#define AC_ADC_DAP_RDT 0x2518 -+#define AC_ADC_DAP_RAT 0x251A -+#define AC_ADC_DAP_NTH 0x251C -+#define AC_ADC_DAP_LHNAC 0x251E -+#define AC_ADC_DAP_LLNAC 0x2520 -+#define AC_ADC_DAP_RHNAC 0x2522 -+#define AC_ADC_DAP_RLNAC 0x2524 -+#define AC_ADC_DAP_HHPFC 0x2526 -+#define AC_ADC_DAP_LHPFC 0x2528 -+#define AC_ADC_DAP_OPT 0x252A -+/* Left blank */ -+#define AC_AGC_SEL 0x2480 -+/* Left blank */ -+#define AC_ADC_DAPL_CTRL 0x2500 -+#define AC_ADC_DAPR_CTRL 0x2502 -+#define AC_ADC_DAPLSTA 0x2504 -+#define AC_ADC_DAPRSTA 0x2506 -+#define AC_ADC_DAP_LTL 0x2508 -+#define AC_ADC_DAP_RTL 0x250A -+#define AC_ADC_DAP_LHAC 0x250C -+#define AC_ADC_DAP_LLAC 0x250E -+#define AC_ADC_DAP_RHAC 0x2510 -+#define AC_ADC_DAP_RLAC 0x2512 -+#define AC_ADC_DAP_LDT 0x2514 -+#define AC_ADC_DAP_LAT 0x2516 -+#define AC_ADC_DAP_RDT 0x2518 -+#define AC_ADC_DAP_RAT 0x251A -+#define AC_ADC_DAP_NTH 0x251C -+#define AC_ADC_DAP_LHNAC 0x251E -+#define AC_ADC_DAP_LLNAC 0x2520 -+#define AC_ADC_DAP_RHNAC 0x2522 -+#define AC_ADC_DAP_RLNAC 0x2524 -+#define AC_ADC_DAP_HHPFC 0x2526 -+#define AC_ADC_DAP_LHPFC 0x2528 -+#define AC_ADC_DAP_OPT 0x252A -+/* Left blank */ -+#define AC_DRC_SEL 0x2f80 -+/* Left blank */ -+#define AC_DRC_CHAN_CTRL 0x3000 -+#define AC_DRC_HHPFC 0x3002 -+#define AC_DRC_LHPFC 0x3004 -+#define AC_DRC_CTRL 0x3006 -+#define AC_DRC_LPFHAT 0x3008 -+#define AC_DRC_LPFLAT 0x300A -+#define AC_DRC_RPFHAT 0x300C -+#define AC_DRC_RPFLAT 0x300E -+#define AC_DRC_LPFHRT 0x3010 -+#define AC_DRC_LPFLRT 0x3012 -+#define AC_DRC_RPFHRT 0x3014 -+#define AC_DRC_RPFLRT 0x3016 -+#define AC_DRC_LRMSHAT 0x3018 -+#define AC_DRC_LRMSLAT 0x301A -+#define AC_DRC_RRMSHAT 0x301C -+#define AC_DRC_RRMSLAT 0x301E -+#define AC_DRC_HCT 0x3020 -+#define AC_DRC_LCT 0x3022 -+#define AC_DRC_HKC 0x3024 -+#define AC_DRC_LKC 0x3026 -+#define AC_DRC_HOPC 0x3028 -+#define AC_DRC_LOPC 0x302A -+#define AC_DRC_HLT 0x302C -+#define AC_DRC_LLT 0x302E -+#define AC_DRC_HKI 0x3030 -+#define AC_DRC_LKI 0x3032 -+#define AC_DRC_HOPL 0x3034 -+#define AC_DRC_LOPL 0x3036 -+#define AC_DRC_HET 0x3038 -+#define AC_DRC_LET 0x303A -+#define AC_DRC_HKE 0x303C -+#define AC_DRC_LKE 0x303E -+#define AC_DRC_HOPE 0x3040 -+#define AC_DRC_LOPE 0x3042 -+#define AC_DRC_HKN 0x3044 -+#define AC_DRC_LKN 0x3046 -+#define AC_DRC_SFHAT 0x3048 -+#define AC_DRC_SFLAT 0x304A -+#define AC_DRC_SFHRT 0x304C -+#define AC_DRC_SFLRT 0x304E -+#define AC_DRC_MXGHS 0x3050 -+#define AC_DRC_MXGLS 0x3052 -+#define AC_DRC_MNGHS 0x3054 -+#define AC_DRC_MNGLS 0x3056 -+#define AC_DRC_EPSHC 0x3058 -+#define AC_DRC_EPSLC 0x305A -+#define AC_DRC_OPT 0x305C -+#define AC_DRC_HPFHGAIN 0x305E -+#define AC_DRC_HPFLGAIN 0x3060 -+#define AC_DRC_BISTCR 0x3100 -+#define AC_DRC_BISTST 0x3102 -+ -+/* AC_SYS_CLK_CTL : 0x2000 */ -+#define SYS_CLK_I2S 15 -+#define SYS_CLK_AGC 7 -+#define SYS_CLK_DRC 6 -+#define SYS_CLK_ADC 3 -+#define SYS_CLK_DAC 2 -+ -+/* AC_SYS_MOD_RST : 0x2002 */ -+#define MOD_RST_I2S 15 -+#define MOD_RST_AGC 7 -+#define MOD_RST_DRC 6 -+#define MOD_RST_ADC 3 -+#define MOD_RST_DAC 2 -+ -+/* AC_SYS_SR_CTL : 0x2004 */ -+#define SYS_SR_BIT 0 -+#define SYS_SR_MASK 0xF -+#define SYS_SR_BIT_0 0 /* 8000 */ -+#define SYS_SR_BIT_1 1 /* 11025 */ -+#define SYS_SR_BIT_2 2 /* 12000 */ -+#define SYS_SR_BIT_3 3 /* 16000 */ -+#define SYS_SR_BIT_4 4 /* 22050 */ -+#define SYS_SR_BIT_5 5 /* 24000 */ -+#define SYS_SR_BIT_6 6 /* 32000 */ -+#define SYS_SR_BIT_7 7 /* 44100 */ -+#define SYS_SR_BIT_8 8 /* 48000 */ -+#define SYS_SR_BIT_9 9 /* 96000 */ -+#define SYS_SR_BIT_10 10 /* 192000 */ -+ -+/* AC_I2S_CTL : 0x2100 */ -+#define I2S_SDO0_EN 3 -+#define I2S_TX_EN 2 -+#define I2S_RX_EN 1 -+#define I2S_GEN 0 -+ -+/* AC_I2S_CLK : 0x2102 */ -+#define I2S_BCLK_OUT 15 -+#define I2S_LRCK_OUT 14 -+#define I2S_BLCK_DIV 10 -+#define I2S_LRCK_PERIOD 0 -+/* BCLK DIV Define */ -+#define I2S_BCLK_DIV_MASK 0xF -+#define I2S_BCLK_DIV_1 1 -+#define I2S_BCLK_DIV_2 2 -+#define I2S_BCLK_DIV_3 3 -+#define I2S_BCLK_DIV_4 4 -+#define I2S_BCLK_DIV_5 5 -+#define I2S_BCLK_DIV_6 6 -+#define I2S_BCLK_DIV_7 7 -+#define I2S_BCLK_DIV_8 8 -+#define I2S_BCLK_DIV_9 9 -+#define I2S_BCLK_DIV_10 10 -+#define I2S_BCLK_DIV_11 11 -+#define I2S_BCLK_DIV_12 12 -+#define I2S_BCLK_DIV_13 13 -+#define I2S_BCLK_DIV_14 14 -+#define I2S_BCLK_DIV_15 15 -+#define I2S_LRCK_PERIOD_MASK 0x3FF -+ -+/* AC_I2S_FMT0 : 0x2104 */ -+#define I2S_FMT_MODE 14 -+#define I2S_FMT_TX_OFFSET 10 -+#define I2S_FMT_RX_OFFSET 8 -+#define I2S_FMT_SAMPLE 4 -+#define I2S_FMT_SLOT_WIDTH 1 -+#define I2S_FMT_LOOP 0 -+ -+/* AC_I2S_FMT1 : 0x2108 */ -+#define I2S_FMT_BCLK_POLAR 15 -+#define I2S_FMT_LRCK_POLAR 14 -+#define I2S_FMT_EDGE_TRANSFER 13 -+#define I2S_FMT_RX_MLS 11 -+#define I2S_FMT_TX_MLS 10 -+#define I2S_FMT_EXTEND 9 -+#define I2S_FMT_LRCK_WIDTH 4 /* PCM long/short Frame */ -+#define I2S_MFT_RX_PDM 2 -+#define I2S_FMT_TX_PDM 0 -+ -+/* AC_I2S_MIXER_SRC : 0x2114 */ -+#define I2S_MIXERL_SRC_DAC 13 -+#define I2S_MIXERL_SRC_ADC 12 -+#define I2S_MIXERR_SRC_DAC 9 -+#define I2S_MIXERR_SRC_ADC 8 -+ -+/* AC_I2S_MIXER_GAIN : 0x2116 */ -+#define I2S_MIXERL_GAIN_DAC 13 -+#define I2S_MIXERL_GAIN_ADC 12 -+#define I2S_MIXERR_GAIN_DAC 9 -+#define I2S_MIXERR_GAIN_ADC 8 -+ -+ -+/* AC_I2S_DAC_VOL : 0x2118 */ -+#define I2S_DACL_VOL 8 -+#define I2S_DACR_VOL 0 -+ -+/* AC_I2S_ADC_VOL : 0x211A */ -+#define I2S_ADCL_VOL 8 -+#define I2S_ADCR_VOL 0 -+ -+/* AC_DAC_CTL : 0x2200 */ -+#define DAC_CTL_DAC_EN 15 -+#define DAC_CTL_HPF_EN 14 -+#define DAC_CTL_FIR 13 -+#define DAC_CTL_MODQU 8 -+ -+/* AC_DAC_MIXER_SRC : 0x2202 */ -+#define DAC_MIXERL_SRC_DAC 13 -+#define DAC_MIXERL_SRC_ADC 12 -+#define DAC_MIXERR_SRC_DAC 9 -+#define DAC_MIXERR_SRC_ADC 8 -+ -+/* AC_DAC_MIXER_GAIN : 0x2204 */ -+#define DAC_MIXERL_GAIN_DAC 13 -+#define DAC_MIXERL_GAIN_ADC 12 -+#define DAC_MIXERR_GAIN_DAC 9 -+#define DAC_MIXERR_GAIN_ADC 8 -+ -+/* AC_OUT_MIXER_CTL : 0x2220 */ -+#define OUT_MIXER_DACR_EN 15 -+#define OUT_MIXER_DACL_EN 14 -+#define OUT_MIXER_RMIX_EN 13 -+#define OUT_MIXER_LMIX_EN 12 -+#define OUT_MIXER_LINE_VOL 8 -+#define OUT_MIXER_MIC1_VOL 4 -+#define OUT_MIXER_MIC2_VOL 0 -+ -+/* AC_OUT_MIXER_SRC : 0x2222 */ -+#define OUT_MIXERR_SRC_MIC1 14 -+#define OUT_MIXERR_SRC_MIC2 13 -+#define OUT_MIXERR_SRC_PHPN 12 -+#define OUT_MIXERR_SRC_PHP 11 -+#define OUT_MIXERR_SRC_LINER 10 -+#define OUT_MIXERR_SRC_DACR 9 -+#define OUT_MIXERR_SRC_DACL 8 -+#define OUT_MIXERL_SRC_MIC1 6 -+#define OUT_MIXERL_SRC_MIC2 5 -+#define OUT_MIXERL_SRC_PHPN 4 -+#define OUT_MIXERL_SRC_PHN 3 -+#define OUT_MIXERL_SRC_LINEL 2 -+#define OUT_MIXERL_SRC_DACL 1 -+#define OUT_MIXERL_SRC_DACR 0 -+ -+/* AC_LINEOUT_CTL : 0x2224 */ -+#define LINEOUT_EN 15 -+#define LINEL_SRC_EN 14 -+#define LINER_SRC_EN 13 -+#define LINEL_SRC 12 -+#define LINER_SRC 11 -+/* ramp just skip */ -+#define LINE_SLOPE_SEL 8 -+#define LINE_ANTI_TIME 5 -+#define LINEOUT_VOL 0 -+ -+/* AC_ADC_CTL : 0x2300 */ -+#define ADC_EN 15 -+#define ADC_ENDM 14 -+#define ADC_FIR 13 -+#define ADC_DELAY_TIME 2 -+#define ADC_DELAY_EN 1 -+ -+/* AC_MICBIAS_CTL : 0x2310 */ -+#define MMBIAS_EN 15 -+#define MMBIAS_CHOPPER 14 -+#define MMBIAS_CHOP_CLK 12 -+#define MMBIAS_SEL 8 -+#define ADDA_BIAS_CUR 3 -+ -+/* AC_ADC_MIC_CTL : 0x2320 */ -+#define ADCR_EN 15 -+#define ADCL_EN 14 -+#define ADC_GAIN 8 -+#define MIC1_GAIN_EN 7 -+#define MIC1_BOOST 4 -+#define MIC2_GAIN_EN 3 -+#define MIC2_BOOST 0 -+ -+/* AC_ADC_MIXER_SRC : 0x2322 */ -+#define ADC_MIXERR_MIC1 14 -+#define ADC_MIXERR_MIC2 13 -+#define ADC_MIXERR_PHPN 12 -+#define ADC_MIXERR_PHP 11 -+#define ADC_MIXERR_LINER 10 -+#define ADC_MIXERR_MIXR 9 -+#define ADC_MIXERR_MIXL 8 -+#define ADC_MIXERL_MIC1 6 -+#define ADC_MIXERL_MIC2 5 -+#define ADC_MIXERL_PHPN 4 -+#define ADC_MIXERL_PHN 3 -+#define ADC_MIXERL_LINEL 2 -+#define ADC_MIXERL_MIXL 1 -+#define ADC_MIXERL_MIXR 0 -+ -+/* AC_BIAS_CTL : 0x232A */ -+ -+/* AC_ANALOG_PROF_CTL : 0x232C */ -+/* used for current performance measure */ -+ -+/* AC_DLDO_OSC_CTL : 0x2340 */ -+/* AC_ALDO_CTL : 0x2342 */ -+/* used for digital & analog LDO test... etc */ -+ -+#endif --- -2.35.3 - diff --git a/patch/kernel/archive/sunxi-6.5/patches.armbian/arm64-dts-sun50i-h6-Add-AC200-EPHY-related-nodes.patch b/patch/kernel/archive/sunxi-6.1/patches.armbian/arm64-dts-allwinner-h6-Add-AC200-EPHY-nodes.patch similarity index 53% rename from patch/kernel/archive/sunxi-6.5/patches.armbian/arm64-dts-sun50i-h6-Add-AC200-EPHY-related-nodes.patch rename to patch/kernel/archive/sunxi-6.1/patches.armbian/arm64-dts-allwinner-h6-Add-AC200-EPHY-nodes.patch index a085081be..b1596978b 100644 --- a/patch/kernel/archive/sunxi-6.5/patches.armbian/arm64-dts-sun50i-h6-Add-AC200-EPHY-related-nodes.patch +++ b/patch/kernel/archive/sunxi-6.1/patches.armbian/arm64-dts-allwinner-h6-Add-AC200-EPHY-nodes.patch @@ -1,18 +1,30 @@ -From fbb61f5656e716e63aba5b04f42966656790cd4b Mon Sep 17 00:00:00 2001 +From d1c807041c254b02e944bf12b8d0ea39953ffdd6 Mon Sep 17 00:00:00 2001 From: Jernej Skrabec -Date: Sun, 12 Jan 2020 12:09:12 +0100 -Subject: [PATCH 072/153] arm64:dts: sun50i-h6: Add AC200 EPHY related nodes +Date: Fri, 16 Aug 2019 16:40:20 +0200 +Subject: [PATCH] arm64: dts: allwinner: h6: Add AC200 EPHY nodes + +All Allwinner H6 SoCs feature a co-packaged AC200 die, which replaces +the integrated PHY and audio circuitry of its H3/H5 predecessors. It is +using an internal I2C connection, but otherwise pretty much behaves as +it would be externally connected. + +Since every H6 SoC contains this chip, add the required DT nodes to the +SoC .dtsi, but keep them disabled. This is for now just covering the +AC200 MFD parent and its EPHY child. +Any board making use of one of the integrated PHY needs to enable it and +connect the MAC and PHY accordingly. Signed-off-by: Jernej Skrabec +Signed-off-by: Andre Przywara --- - arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 63 ++++++++++++++++++++ - 1 file changed, 63 insertions(+) + arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 73 ++++++++++++++++++++ + 1 file changed, 73 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi -index fcabca590..8118a3465 100644 +index 9eee1a1e189d..b1f3724b42ca 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi -@@ -23,6 +23,16 @@ aliases { +@@ -24,6 +24,16 @@ aliases { mmc2 = &mmc2; }; @@ -29,22 +41,25 @@ index fcabca590..8118a3465 100644 cpus { #address-cells = <1>; #size-cells = <0>; -@@ -320,6 +330,10 @@ ths_calibration: thermal-sensor-calibration@14 { - reg = <0x14 0x8>; - }; - -+ ephy_calibration: ephy-calibration@2c { -+ reg = <0x2c 0x2>; -+ }; -+ +@@ -334,6 +344,14 @@ ths_calibration: thermal-sensor-calibration@14 { cpu_speed_grade: cpu-speed-grade@1c { reg = <0x1c 0x4>; }; -@@ -377,6 +391,14 @@ ext_rgmii_pins: rgmii-pins { ++ ++ ephy_calib: ephy_calib@2c { ++ reg = <0x2c 0x2>; ++ }; ++ ++ ac200_bg: ac200_bg@30 { ++ reg = <0x30 0x2>; ++ }; + }; + + timer@3009000 { +@@ -389,6 +407,13 @@ ext_rgmii_pins: rgmii-pins { drive-strength = <40>; }; -+ /omit-if-no-ref/ + ext_rmii_pins: rmii_pins { + pins = "PA0", "PA1", "PA2", "PA3", "PA4", + "PA5", "PA6", "PA7", "PA8", "PA9"; @@ -55,19 +70,20 @@ index fcabca590..8118a3465 100644 hdmi_pins: hdmi-pins { pins = "PH8", "PH9", "PH10"; function = "hdmi"; -@@ -397,6 +419,11 @@ i2c2_pins: i2c2-pins { +@@ -409,6 +434,12 @@ i2c2_pins: i2c2-pins { function = "i2c2"; }; + i2c3_pins: i2c3-pins { + pins = "PB17", "PB18"; + function = "i2c3"; ++ bias-pull-up; + }; + mmc0_pins: mmc0-pins { pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5"; -@@ -414,6 +441,11 @@ mmc1_pins: mmc1-pins { +@@ -436,6 +467,11 @@ mmc2_pins: mmc2-pins { bias-pull-up; }; @@ -76,10 +92,10 @@ index fcabca590..8118a3465 100644 + function = "pwm1"; + }; + - mmc2_pins: mmc2-pins { - pins = "PC1", "PC4", "PC5", "PC6", - "PC7", "PC8", "PC9", "PC10", -@@ -656,6 +688,37 @@ spi1: spi@5011000 { + /omit-if-no-ref/ + spi0_pins: spi0-pins { + pins = "PC0", "PC2", "PC3"; +@@ -647,6 +683,43 @@ i2c2: i2c@5002800 { #size-cells = <0>; }; @@ -92,6 +108,7 @@ index fcabca590..8118a3465 100644 + resets = <&ccu RST_BUS_I2C3>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c3_pins>; ++ clock-frequency = <100000>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; @@ -99,24 +116,29 @@ index fcabca590..8118a3465 100644 + ac200: mfd@10 { + compatible = "x-powers,ac200"; + reg = <0x10>; ++ clocks = <&ac200_pwm_clk>; + interrupt-parent = <&pio>; + interrupts = <1 20 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + #interrupt-cells = <1>; ++ nvmem-cells = <&ac200_bg>; ++ nvmem-cell-names = "bandgap"; + -+ ac200_ephy: phy { -+ compatible = "x-powers,ac200-ephy"; -+ clocks = <&ac200_pwm_clk>; -+ nvmem-cells = <&ephy_calibration>; ++ ac200_ephy_ctl: syscon { ++ compatible = "x-powers,ac200-ephy-ctl"; ++ nvmem-cells = <&ephy_calib>; + nvmem-cell-names = "calibration"; ++ #clock-cells = <0>; ++ #reset-cells = <0>; ++ phy-mode = "rmii"; + status = "disabled"; + }; + }; + }; + - emac: ethernet@5020000 { - compatible = "allwinner,sun50i-h6-emac", - "allwinner,sun50i-a64-emac"; + spi0: spi@5010000 { + compatible = "allwinner,sun50i-h6-spi", + "allwinner,sun8i-h3-spi"; -- -2.35.3 +2.34.1 diff --git a/patch/kernel/archive/sunxi-6.1/patches.armbian/arm64-dts-allwinner-h6-add-AC200-codec-nodes.patch b/patch/kernel/archive/sunxi-6.1/patches.armbian/arm64-dts-allwinner-h6-add-AC200-codec-nodes.patch new file mode 100644 index 000000000..6a64751b7 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.1/patches.armbian/arm64-dts-allwinner-h6-add-AC200-codec-nodes.patch @@ -0,0 +1,87 @@ +From a809376f8af7cab04996585f9f68e1f6cf1afd73 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Thu, 1 Sep 2022 17:45:03 +0200 +Subject: [PATCH] arm64: dts: allwinner: h6: add AC200 codec nodes + +Signed-off-by: Jernej Skrabec +--- + arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 42 ++++++++++++++++++++ + 1 file changed, 42 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +index b1f3724b42ca..e1c6673da881 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +@@ -24,6 +24,22 @@ aliases { + mmc2 = &mmc2; + }; + ++ analog: analog-codec { ++ compatible = "simple-audio-card"; ++ simple-audio-card,format = "i2s"; ++ simple-audio-card,name = "ac200-audio"; ++ simple-audio-card,mclk-fs = <512>; ++ status = "disabled"; ++ ++ simple-audio-card,cpu { ++ sound-dai = <&i2s3>; ++ }; ++ ++ simple-audio-card,codec { ++ sound-dai = <&ac200_codec>; ++ }; ++ }; ++ + ac200_pwm_clk: ac200_clk { + compatible = "pwm-clock"; + #clock-cells = <0>; +@@ -440,6 +456,11 @@ i2c3_pins: i2c3-pins { + bias-pull-up; + }; + ++ i2s3_pins: i2s3-pins { ++ pins = "PB12", "PB13", "PB14", "PB15", "PB16"; ++ function = "i2s3"; ++ }; ++ + mmc0_pins: mmc0-pins { + pins = "PF0", "PF1", "PF2", "PF3", + "PF4", "PF5"; +@@ -717,6 +738,12 @@ ac200_ephy_ctl: syscon { + phy-mode = "rmii"; + status = "disabled"; + }; ++ ++ ac200_codec: codec { ++ #sound-dai-cells = <0>; ++ compatible = "x-powers,ac200-codec"; ++ status = "disabled"; ++ }; + }; + }; + +@@ -774,6 +801,21 @@ mdio: mdio { + }; + }; + ++ i2s3: i2s@508f000 { ++ #sound-dai-cells = <0>; ++ compatible = "allwinner,sun50i-h6-i2s"; ++ reg = <0x0508f000 0x1000>; ++ interrupts = ; ++ clocks = <&ccu CLK_BUS_I2S3>, <&ccu CLK_I2S3>; ++ clock-names = "apb", "mod"; ++ dmas = <&dma 6>, <&dma 6>; ++ resets = <&ccu RST_BUS_I2S3>; ++ dma-names = "rx", "tx"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s3_pins>; ++ status = "disabled"; ++ }; ++ + i2s1: i2s@5091000 { + #sound-dai-cells = <0>; + compatible = "allwinner,sun50i-h6-i2s"; +-- +2.34.1 + diff --git a/patch/kernel/archive/sunxi-6.1/patches.armbian/arm64-dts-allwinner-h6-enable-AC200-codec.patch b/patch/kernel/archive/sunxi-6.1/patches.armbian/arm64-dts-allwinner-h6-enable-AC200-codec.patch new file mode 100644 index 000000000..aca9f9b47 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.1/patches.armbian/arm64-dts-allwinner-h6-enable-AC200-codec.patch @@ -0,0 +1,142 @@ +From 37a4b303a1f6094f9f26bf1e82a7b1a679540651 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Thu, 1 Sep 2022 17:49:28 +0200 +Subject: [PATCH] arm64: dts: allwinner: h6: enable AC200 codec + +Enable AC200 analog codec on H6 based boards where present. + +Signed-off-by: Jernej Skrabec +--- + .../dts/allwinner/sun50i-h6-orangepi-3.dts | 25 +++++++++++++++++++ + .../boot/dts/allwinner/sun50i-h6-pine-h64.dts | 25 +++++++++++++++++++ + .../allwinner/sun50i-h6-tanix-tx6-mini.dts | 14 +++++++++++ + 3 files changed, 64 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts +index 59e9095d7a15..f1fcd37d0fcc 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts +@@ -104,6 +104,19 @@ wifi_pwrseq: wifi-pwrseq { + }; + }; + ++&ac200_codec { ++ avcc-supply = <®_aldo2>; ++ status = "okay"; ++}; ++ ++&ac200_pwm_clk { ++ status = "okay"; ++}; ++ ++&analog { ++ status = "okay"; ++}; ++ + &cpu0 { + cpu-supply = <®_dcdca>; + }; +@@ -172,6 +185,14 @@ &i2s1 { + status = "okay"; + }; + ++&i2c3 { ++ status = "okay"; ++}; ++ ++&i2s3 { ++ status = "okay"; ++}; ++ + &mmc0 { + vmmc-supply = <®_cldo1>; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ +@@ -219,6 +240,10 @@ &pio { + vcc-pg-supply = <®_vcc_wifi_io>; + }; + ++&pwm { ++ status = "okay"; ++}; ++ + &r_ir { + status = "okay"; + }; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts +index 454d2a2974c9..5bb973ea3fb4 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts +@@ -93,6 +93,19 @@ &dwc3 { + status = "okay"; + }; + ++&ac200_codec { ++ avcc-supply = <®_aldo2>; ++ status = "okay"; ++}; ++ ++&ac200_pwm_clk { ++ status = "okay"; ++}; ++ ++&analog { ++ status = "okay"; ++}; ++ + &cpu0 { + cpu-supply = <®_dcdca>; + }; +@@ -139,6 +152,14 @@ &i2s1 { + status = "okay"; + }; + ++&i2c3 { ++ status = "okay"; ++}; ++ ++&i2s3 { ++ status = "okay"; ++}; ++ + &mdio { + ext_rgmii_phy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; +@@ -195,6 +216,10 @@ &pio { + vcc-pg-supply = <®_aldo1>; + }; + ++&pwm { ++ status = "okay"; ++}; ++ + &r_i2c { + status = "okay"; + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix-tx6-mini.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix-tx6-mini.dts +index 08d84160d88f..931e8b99fd93 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix-tx6-mini.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix-tx6-mini.dts +@@ -10,6 +10,20 @@ / { + compatible = "oranth,tanix-tx6-mini", "allwinner,sun50i-h6"; + }; + ++ ++&ac200_codec { ++ avcc-supply = <®_vcc3v3>; ++ status = "okay"; ++}; ++ ++&analog { ++ status = "okay"; ++}; ++ ++&i2s3 { ++ status = "okay"; ++}; ++ + &r_ir { + linux,rc-map-name = "rc-tanix-tx3mini"; + }; +-- +2.34.1 + diff --git a/patch/kernel/archive/sunxi-6.1/patches.armbian/arm64-dts-allwinner-h6-tanix-enable-Ethernet.patch b/patch/kernel/archive/sunxi-6.1/patches.armbian/arm64-dts-allwinner-h6-tanix-enable-Ethernet.patch new file mode 100644 index 000000000..7340deaaa --- /dev/null +++ b/patch/kernel/archive/sunxi-6.1/patches.armbian/arm64-dts-allwinner-h6-tanix-enable-Ethernet.patch @@ -0,0 +1,97 @@ +From 93df3a3c4c7afccbceceab0a8318dcd94cff3259 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sat, 24 Aug 2019 01:03:05 +0200 +Subject: [PATCH] arm64: dts: allwinner: h6: tanix: enable Ethernet + +Tanix-TX6 and TX6 mini boards provide an 100MBit/s Ethernet port, which +uses the EPHY integrated into the Allwinner H6 SoC. + +Enable the MAC node, plus the required AC200 nodes to allow configuring +and enabling the integrated PHY. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Andre Przywara +--- + .../boot/dts/allwinner/sun50i-h6-tanix.dtsi | 38 +++++++++++++++++++ + 1 file changed, 38 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi +index 4903d6358112..51a75debab44 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi +@@ -10,6 +10,7 @@ + + / { + aliases { ++ ethernet0 = &emac; + serial0 = &uart0; + }; + +@@ -84,6 +85,16 @@ wifi_pwrseq: wifi-pwrseq { + }; + }; + ++&ac200_ephy_ctl { ++ x-powers,led-polarity = ; ++ phy-address = <1>; ++ status = "okay"; ++}; ++ ++&ac200_pwm_clk { ++ status = "okay"; ++}; ++ + &cpu0 { + cpu-supply = <®_vdd_cpu_gpu>; + }; +@@ -104,6 +115,14 @@ &ehci3 { + status = "okay"; + }; + ++&emac { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&ext_rmii_pins>; ++ phy-mode = "rmii"; ++ phy-handle = <&ext_rmii_phy>; ++ status = "okay"; ++}; ++ + &gpu { + mali-supply = <®_vdd_cpu_gpu>; + status = "okay"; +@@ -119,6 +138,21 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2c3 { ++ status = "okay"; ++}; ++ ++&mdio { ++ ext_rmii_phy: ethernet-phy@1 { ++ compatible = "ethernet-phy-id0044.1400", ++ "ethernet-phy-ieee802.3-c22"; ++ reg = <1>; ++ resets = <&ac200_ephy_ctl>; ++ reset-names = "phy"; ++ clocks = <&ac200_ephy_ctl>; ++ }; ++}; ++ + &mmc0 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_pins>; +@@ -161,6 +195,10 @@ &pio { + vcc-pg-supply = <®_vcc1v8>; + }; + ++&pwm { ++ status = "okay"; ++}; ++ + &r_ir { + status = "okay"; + }; +-- +2.34.1 + diff --git a/patch/kernel/archive/sunxi-6.1/patches.armbian/clk-gate-add-support-for-regmap-based-gates.patch b/patch/kernel/archive/sunxi-6.1/patches.armbian/clk-gate-add-support-for-regmap-based-gates.patch new file mode 100644 index 000000000..6f0d2b18d --- /dev/null +++ b/patch/kernel/archive/sunxi-6.1/patches.armbian/clk-gate-add-support-for-regmap-based-gates.patch @@ -0,0 +1,251 @@ +From 948a378f8aef5bd7240a38e3a864c94276d6aa56 Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Fri, 10 Jun 2022 18:20:29 +0100 +Subject: [PATCH] clk: gate: add support for regmap based gates + +While we have nice wrappers for simple bit-flip MMIO based clock gates, +a single bit to toggle in a regmap still requires to write a lot of clock +framework boilerplate. + +Support generic wrappers for regmap based clock gates, by adding them to +the existing clock-gates.c file. Since a read-modify-write operation in a +regmap can be much more complex than a readl/writel pair, we cannot use +the .enable/.disable ops members, but do the actual flipping already in +.prepare/.unprepare, where we can sleep. Also we cannot provide an +.is_enabled function, since this must not sleep as well. +On the upside all the locking for the r/m/w operation is provided by +regmap already, so we can skip that. +The rest of the CCF boilerplate code can be shared. + +Signed-off-by: Andre Przywara +--- + drivers/clk/clk-gate.c | 60 ++++++++++++++++++++++++++++++++++-- + include/linux/clk-provider.h | 36 +++++++++++++++++++--- + 2 files changed, 89 insertions(+), 7 deletions(-) + +diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c +index 64283807600b..f83aef5e6e79 100644 +--- a/drivers/clk/clk-gate.c ++++ b/drivers/clk/clk-gate.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -124,11 +125,42 @@ const struct clk_ops clk_gate_ops = { + }; + EXPORT_SYMBOL_GPL(clk_gate_ops); + ++static int clk_gate_regmap_setclrbit(struct clk_hw *hw, bool enable) ++{ ++ struct clk_gate *gate = to_clk_gate(hw); ++ bool set = gate->flags & CLK_GATE_SET_TO_DISABLE; ++ ++ set ^= enable; ++ ++ if (set) ++ return regmap_set_bits(gate->regmap, gate->regmap_offs, ++ BIT(gate->bit_idx)); ++ else ++ return regmap_clear_bits(gate->regmap, gate->regmap_offs, ++ BIT(gate->bit_idx)); ++} ++ ++static int clk_gate_regmap_prepare(struct clk_hw *hw) ++{ ++ return clk_gate_regmap_setclrbit(hw, true); ++} ++ ++static void clk_gate_regmap_unprepare(struct clk_hw *hw) ++{ ++ clk_gate_regmap_setclrbit(hw, false); ++} ++ ++const struct clk_ops clk_gate_regmap_ops = { ++ .prepare = clk_gate_regmap_prepare, ++ .unprepare = clk_gate_regmap_unprepare, ++}; ++ + struct clk_hw *__clk_hw_register_gate(struct device *dev, + struct device_node *np, const char *name, + const char *parent_name, const struct clk_hw *parent_hw, + const struct clk_parent_data *parent_data, + unsigned long flags, ++ struct regmap *regmap, unsigned int regmap_offs, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock) + { +@@ -150,7 +182,10 @@ struct clk_hw *__clk_hw_register_gate(struct device *dev, + return ERR_PTR(-ENOMEM); + + init.name = name; +- init.ops = &clk_gate_ops; ++ if (regmap) ++ init.ops = &clk_gate_regmap_ops; ++ else ++ init.ops = &clk_gate_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.parent_hws = parent_hw ? &parent_hw : NULL; +@@ -162,6 +197,8 @@ struct clk_hw *__clk_hw_register_gate(struct device *dev, + + /* struct clk_gate assignments */ + gate->reg = reg; ++ gate->regmap = regmap; ++ gate->regmap_offs = regmap_offs; + gate->bit_idx = bit_idx; + gate->flags = clk_gate_flags; + gate->lock = lock; +@@ -197,6 +234,22 @@ struct clk *clk_register_gate(struct device *dev, const char *name, + } + EXPORT_SYMBOL_GPL(clk_register_gate); + ++struct clk *clk_register_regmap_gate(struct device *dev, const char *name, ++ const char *parent_name, unsigned long flags, ++ struct regmap *regmap, unsigned int regmap_offs, ++ u8 bit_idx, u8 clk_gate_flags) ++{ ++ struct clk_hw *hw; ++ ++ hw = clk_hw_register_regmap_gate(dev, name, parent_name, flags, regmap, ++ regmap_offs, bit_idx, clk_gate_flags); ++ ++ if (IS_ERR(hw)) ++ return ERR_CAST(hw); ++ return hw->clk; ++} ++EXPORT_SYMBOL_GPL(clk_register_regmap_gate); ++ + void clk_unregister_gate(struct clk *clk) + { + struct clk_gate *gate; +@@ -234,6 +287,7 @@ struct clk_hw *__devm_clk_hw_register_gate(struct device *dev, + const char *parent_name, const struct clk_hw *parent_hw, + const struct clk_parent_data *parent_data, + unsigned long flags, ++ struct regmap *regmap, unsigned int regmap_offs, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock) + { +@@ -244,8 +298,8 @@ struct clk_hw *__devm_clk_hw_register_gate(struct device *dev, + return ERR_PTR(-ENOMEM); + + hw = __clk_hw_register_gate(dev, np, name, parent_name, parent_hw, +- parent_data, flags, reg, bit_idx, +- clk_gate_flags, lock); ++ parent_data, flags, regmap, regmap_offs, ++ reg, bit_idx, clk_gate_flags, lock); + + if (!IS_ERR(hw)) { + *ptr = hw; +diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h +index 267cd06b54a0..5de2c071fcf8 100644 +--- a/include/linux/clk-provider.h ++++ b/include/linux/clk-provider.h +@@ -8,6 +8,7 @@ + + #include + #include ++#include + + /* + * flags used across common struct clk. these flags should only affect the +@@ -510,6 +511,8 @@ void of_fixed_clk_setup(struct device_node *np); + struct clk_gate { + struct clk_hw hw; + void __iomem *reg; ++ struct regmap *regmap; ++ unsigned int regmap_offs; + u8 bit_idx; + u8 flags; + spinlock_t *lock; +@@ -527,6 +530,7 @@ struct clk_hw *__clk_hw_register_gate(struct device *dev, + const char *parent_name, const struct clk_hw *parent_hw, + const struct clk_parent_data *parent_data, + unsigned long flags, ++ struct regmap *regmap, unsigned int regmap_offs, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock); + struct clk_hw *__devm_clk_hw_register_gate(struct device *dev, +@@ -534,12 +538,17 @@ struct clk_hw *__devm_clk_hw_register_gate(struct device *dev, + const char *parent_name, const struct clk_hw *parent_hw, + const struct clk_parent_data *parent_data, + unsigned long flags, ++ struct regmap *regmap, unsigned int regmap_offs, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock); + struct clk *clk_register_gate(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock); ++struct clk *clk_register_regmap_gate(struct device *dev, const char *name, ++ const char *parent_name, unsigned long flags, ++ struct regmap *regmap, unsigned int regmap_offs, ++ u8 bit_idx, u8 clk_gate_flags); + /** + * clk_hw_register_gate - register a gate clock with the clock framework + * @dev: device that is registering this clock +@@ -554,8 +563,14 @@ struct clk *clk_register_gate(struct device *dev, const char *name, + #define clk_hw_register_gate(dev, name, parent_name, flags, reg, bit_idx, \ + clk_gate_flags, lock) \ + __clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL, \ +- NULL, (flags), (reg), (bit_idx), \ ++ NULL, (flags), NULL, 0, (reg), (bit_idx), \ + (clk_gate_flags), (lock)) ++ ++#define clk_hw_register_regmap_gate(dev, name, parent_name, flags, regmap, \ ++ regmap_offs, bit_idx, clk_gate_flags) \ ++ __clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL, \ ++ NULL, (flags), regmap, regmap_offs, NULL, \ ++ (bit_idx), (clk_gate_flags), NULL) + /** + * clk_hw_register_gate_parent_hw - register a gate clock with the clock + * framework +@@ -571,8 +586,15 @@ struct clk *clk_register_gate(struct device *dev, const char *name, + #define clk_hw_register_gate_parent_hw(dev, name, parent_hw, flags, reg, \ + bit_idx, clk_gate_flags, lock) \ + __clk_hw_register_gate((dev), NULL, (name), NULL, (parent_hw), \ +- NULL, (flags), (reg), (bit_idx), \ ++ NULL, (flags), NULL, 0, (reg), (bit_idx), \ + (clk_gate_flags), (lock)) ++ ++#define clk_hw_register_regmap_gate_parent_hw(dev, name, parent_hw, flags, \ ++ regmap, regmap_offs, bit_idx, \ ++ clk_gate_flags) \ ++ __clk_hw_register_gate((dev), NULL, (name), NULL, (parent_hw), \ ++ NULL, (flags), regmap, regmap_offs, NULL, \ ++ (bit_idx), (clk_gate_flags), NULL) + /** + * clk_hw_register_gate_parent_data - register a gate clock with the clock + * framework +@@ -588,7 +610,7 @@ struct clk *clk_register_gate(struct device *dev, const char *name, + #define clk_hw_register_gate_parent_data(dev, name, parent_data, flags, reg, \ + bit_idx, clk_gate_flags, lock) \ + __clk_hw_register_gate((dev), NULL, (name), NULL, NULL, (parent_data), \ +- (flags), (reg), (bit_idx), \ ++ (flags), NULL, 0, (reg), (bit_idx), \ + (clk_gate_flags), (lock)) + /** + * devm_clk_hw_register_gate - register a gate clock with the clock framework +@@ -604,8 +626,14 @@ struct clk *clk_register_gate(struct device *dev, const char *name, + #define devm_clk_hw_register_gate(dev, name, parent_name, flags, reg, bit_idx,\ + clk_gate_flags, lock) \ + __devm_clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL, \ +- NULL, (flags), (reg), (bit_idx), \ ++ NULL, (flags), NULL, 0, (reg), (bit_idx), \ + (clk_gate_flags), (lock)) ++#define devm_clk_hw_register_regmap_gate(dev, name, parent_name, flags, \ ++ regmap, regmap_offs, bit_idx, \ ++ clk_gate_flags) \ ++ __devm_clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL, \ ++ NULL, (flags), (regmap), (regmap_offs), NULL, \ ++ (bit_idx), (clk_gate_flags), NULL) + void clk_unregister_gate(struct clk *clk); + void clk_hw_unregister_gate(struct clk_hw *hw); + int clk_gate_is_enabled(struct clk_hw *hw); +-- +2.34.1 + diff --git a/patch/kernel/archive/sunxi-6.1/patches.armbian/drv-mfd-Add-support-for-AC200.patch b/patch/kernel/archive/sunxi-6.1/patches.armbian/drv-mfd-Add-support-for-AC200.patch deleted file mode 100644 index 7f19be025..000000000 --- a/patch/kernel/archive/sunxi-6.1/patches.armbian/drv-mfd-Add-support-for-AC200.patch +++ /dev/null @@ -1,440 +0,0 @@ -From 6fc4380edfa07505416a18c91e81fe4e7ddb23cb Mon Sep 17 00:00:00 2001 -From: Jernej Skrabec -Date: Fri, 16 Aug 2019 16:38:21 +0200 -Subject: [PATCH 004/158] drv:mfd: Add support for AC200 - -Signed-off-by: Jernej Skrabec ---- - drivers/mfd/Kconfig | 9 ++ - drivers/mfd/Makefile | 1 + - drivers/mfd/ac200.c | 170 +++++++++++++++++++++++++++++++ - include/linux/mfd/ac200.h | 208 ++++++++++++++++++++++++++++++++++++++ - 4 files changed, 388 insertions(+) - create mode 100644 drivers/mfd/ac200.c - create mode 100644 include/linux/mfd/ac200.h - -diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig -index 9940e2724..27285ddaa 100644 ---- a/drivers/mfd/Kconfig -+++ b/drivers/mfd/Kconfig -@@ -178,6 +178,15 @@ config MFD_AC100 - This driver include only the core APIs. You have to select individual - components like codecs or RTC under the corresponding menus. - -+config MFD_AC200 -+ bool "X-Powers AC200" -+ select MFD_CORE -+ depends on I2C -+ help -+ If you say Y here you get support for the X-Powers AC200 IC. -+ This driver include only the core APIs. You have to select individual -+ components like Ethernet PHY or RTC under the corresponding menus. -+ - config MFD_AXP20X - tristate - select MFD_CORE -diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile -index 7ed3ef4a6..91dc530d0 100644 ---- a/drivers/mfd/Makefile -+++ b/drivers/mfd/Makefile -@@ -144,6 +144,7 @@ obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o - obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o - - obj-$(CONFIG_MFD_AC100) += ac100.o -+obj-$(CONFIG_MFD_AC200) += ac200.o - obj-$(CONFIG_MFD_AXP20X) += axp20x.o - obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o - obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o -diff --git a/drivers/mfd/ac200.c b/drivers/mfd/ac200.c -new file mode 100644 -index 000000000..570573790 ---- /dev/null -+++ b/drivers/mfd/ac200.c -@@ -0,0 +1,170 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * MFD core driver for X-Powers' AC200 IC -+ * -+ * The AC200 is a chip which is co-packaged with Allwinner H6 SoC and -+ * includes analog audio codec, analog TV encoder, ethernet PHY, eFuse -+ * and RTC. -+ * -+ * Copyright (c) 2020 Jernej Skrabec -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* Interrupts */ -+#define AC200_IRQ_RTC 0 -+#define AC200_IRQ_EPHY 1 -+#define AC200_IRQ_TVE 2 -+ -+/* IRQ enable register */ -+#define AC200_SYS_IRQ_ENABLE_OUT_EN BIT(15) -+#define AC200_SYS_IRQ_ENABLE_RTC BIT(12) -+#define AC200_SYS_IRQ_ENABLE_EPHY BIT(8) -+#define AC200_SYS_IRQ_ENABLE_TVE BIT(4) -+ -+static const struct regmap_range_cfg ac200_range_cfg[] = { -+ { -+ .range_min = AC200_SYS_VERSION, -+ .range_max = AC200_IC_CHARA1, -+ .selector_reg = AC200_TWI_REG_ADDR_H, -+ .selector_mask = 0xff, -+ .selector_shift = 0, -+ .window_start = 0, -+ .window_len = 256, -+ } -+}; -+ -+static const struct regmap_config ac200_regmap_config = { -+ .reg_bits = 8, -+ .val_bits = 16, -+ .ranges = ac200_range_cfg, -+ .num_ranges = ARRAY_SIZE(ac200_range_cfg), -+ .max_register = AC200_IC_CHARA1, -+}; -+ -+static const struct regmap_irq ac200_regmap_irqs[] = { -+ REGMAP_IRQ_REG(AC200_IRQ_RTC, 0, AC200_SYS_IRQ_ENABLE_RTC), -+ REGMAP_IRQ_REG(AC200_IRQ_EPHY, 0, AC200_SYS_IRQ_ENABLE_EPHY), -+ REGMAP_IRQ_REG(AC200_IRQ_TVE, 0, AC200_SYS_IRQ_ENABLE_TVE), -+}; -+ -+static const struct regmap_irq_chip ac200_regmap_irq_chip = { -+ .name = "ac200_irq_chip", -+ .status_base = AC200_SYS_IRQ_STATUS, -+ .mask_base = AC200_SYS_IRQ_ENABLE, -+ .mask_invert = true, -+ .irqs = ac200_regmap_irqs, -+ .num_irqs = ARRAY_SIZE(ac200_regmap_irqs), -+ .num_regs = 1, -+}; -+ -+static const struct resource ephy_resource[] = { -+ DEFINE_RES_IRQ(AC200_IRQ_EPHY), -+}; -+ -+static const struct mfd_cell ac200_cells[] = { -+ { -+ .name = "ac200-ephy", -+ .num_resources = ARRAY_SIZE(ephy_resource), -+ .resources = ephy_resource, -+ .of_compatible = "x-powers,ac200-ephy", -+ }, -+}; -+ -+static int ac200_i2c_probe(struct i2c_client *i2c, -+ const struct i2c_device_id *id) -+{ -+ struct device *dev = &i2c->dev; -+ struct ac200_dev *ac200; -+ int ret; -+ -+ ac200 = devm_kzalloc(dev, sizeof(*ac200), GFP_KERNEL); -+ if (!ac200) -+ return -ENOMEM; -+ -+ i2c_set_clientdata(i2c, ac200); -+ -+ ac200->regmap = devm_regmap_init_i2c(i2c, &ac200_regmap_config); -+ if (IS_ERR(ac200->regmap)) { -+ ret = PTR_ERR(ac200->regmap); -+ dev_err(dev, "regmap init failed: %d\n", ret); -+ return ret; -+ } -+ -+ /* do a reset to put chip in a known state */ -+ -+ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0); -+ if (ret) -+ return ret; -+ -+ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 1); -+ if (ret) -+ return ret; -+ -+ /* enable interrupt pin */ -+ -+ ret = regmap_write(ac200->regmap, AC200_SYS_IRQ_ENABLE, -+ AC200_SYS_IRQ_ENABLE_OUT_EN); -+ if (ret) -+ return ret; -+ -+ ret = regmap_add_irq_chip(ac200->regmap, i2c->irq, IRQF_ONESHOT, 0, -+ &ac200_regmap_irq_chip, &ac200->regmap_irqc); -+ if (ret) -+ return ret; -+ -+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, ac200_cells, -+ ARRAY_SIZE(ac200_cells), NULL, 0, NULL); -+ if (ret) { -+ dev_err(dev, "failed to add MFD devices: %d\n", ret); -+ regmap_del_irq_chip(i2c->irq, ac200->regmap_irqc); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int ac200_i2c_remove(struct i2c_client *i2c) -+{ -+ struct ac200_dev *ac200 = i2c_get_clientdata(i2c); -+ -+ regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0); -+ -+ mfd_remove_devices(&i2c->dev); -+ regmap_del_irq_chip(i2c->irq, ac200->regmap_irqc); -+ -+ return 0; -+} -+ -+static const struct i2c_device_id ac200_ids[] = { -+ { "ac200", }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(i2c, ac200_ids); -+ -+static const struct of_device_id ac200_of_match[] = { -+ { .compatible = "x-powers,ac200" }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(of, ac200_of_match); -+ -+static struct i2c_driver ac200_i2c_driver = { -+ .driver = { -+ .name = "ac200", -+ .of_match_table = of_match_ptr(ac200_of_match), -+ }, -+ .probe = ac200_i2c_probe, -+ .remove = ac200_i2c_remove, -+ .id_table = ac200_ids, -+}; -+module_i2c_driver(ac200_i2c_driver); -+ -+MODULE_DESCRIPTION("MFD core driver for AC200"); -+MODULE_AUTHOR("Jernej Skrabec "); -+MODULE_LICENSE("GPL v2"); -diff --git a/include/linux/mfd/ac200.h b/include/linux/mfd/ac200.h -new file mode 100644 -index 000000000..0c677094a ---- /dev/null -+++ b/include/linux/mfd/ac200.h -@@ -0,0 +1,208 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * AC200 register list -+ * -+ * Copyright (C) 2019 Jernej Skrabec -+ */ -+ -+#ifndef __LINUX_MFD_AC200_H -+#define __LINUX_MFD_AC200_H -+ -+#include -+ -+/* interface registers (can be accessed from any page) */ -+#define AC200_TWI_CHANGE_TO_RSB 0x3E -+#define AC200_TWI_PAD_DELAY 0xC4 -+#define AC200_TWI_REG_ADDR_H 0xFE -+ -+/* General registers */ -+#define AC200_SYS_VERSION 0x0000 -+#define AC200_SYS_CONTROL 0x0002 -+#define AC200_SYS_IRQ_ENABLE 0x0004 -+#define AC200_SYS_IRQ_STATUS 0x0006 -+#define AC200_SYS_CLK_CTL 0x0008 -+#define AC200_SYS_DLDO_OSC_CTL 0x000A -+#define AC200_SYS_PLL_CTL0 0x000C -+#define AC200_SYS_PLL_CTL1 0x000E -+#define AC200_SYS_AUDIO_CTL0 0x0010 -+#define AC200_SYS_AUDIO_CTL1 0x0012 -+#define AC200_SYS_EPHY_CTL0 0x0014 -+#define AC200_SYS_EPHY_CTL1 0x0016 -+#define AC200_SYS_TVE_CTL0 0x0018 -+#define AC200_SYS_TVE_CTL1 0x001A -+ -+/* Audio Codec registers */ -+#define AC200_AC_SYS_CLK_CTL 0x2000 -+#define AC200_SYS_MOD_RST 0x2002 -+#define AC200_SYS_SAMP_CTL 0x2004 -+#define AC200_I2S_CTL 0x2100 -+#define AC200_I2S_CLK 0x2102 -+#define AC200_I2S_FMT0 0x2104 -+#define AC200_I2S_FMT1 0x2108 -+#define AC200_I2S_MIX_SRC 0x2114 -+#define AC200_I2S_MIX_GAIN 0x2116 -+#define AC200_I2S_DACDAT_DVC 0x2118 -+#define AC200_I2S_ADCDAT_DVC 0x211A -+#define AC200_AC_DAC_DPC 0x2200 -+#define AC200_AC_DAC_MIX_SRC 0x2202 -+#define AC200_AC_DAC_MIX_GAIN 0x2204 -+#define AC200_DACA_OMIXER_CTRL 0x2220 -+#define AC200_OMIXER_SR 0x2222 -+#define AC200_LINEOUT_CTRL 0x2224 -+#define AC200_AC_ADC_DPC 0x2300 -+#define AC200_MBIAS_CTRL 0x2310 -+#define AC200_ADC_MIC_CTRL 0x2320 -+#define AC200_ADCMIXER_SR 0x2322 -+#define AC200_ANALOG_TUNING0 0x232A -+#define AC200_ANALOG_TUNING1 0x232C -+#define AC200_AC_AGC_SEL 0x2480 -+#define AC200_ADC_DAPLCTRL 0x2500 -+#define AC200_ADC_DAPRCTRL 0x2502 -+#define AC200_ADC_DAPLSTA 0x2504 -+#define AC200_ADC_DAPRSTA 0x2506 -+#define AC200_ADC_DAPLTL 0x2508 -+#define AC200_ADC_DAPRTL 0x250A -+#define AC200_ADC_DAPLHAC 0x250C -+#define AC200_ADC_DAPLLAC 0x250E -+#define AC200_ADC_DAPRHAC 0x2510 -+#define AC200_ADC_DAPRLAC 0x2512 -+#define AC200_ADC_DAPLDT 0x2514 -+#define AC200_ADC_DAPLAT 0x2516 -+#define AC200_ADC_DAPRDT 0x2518 -+#define AC200_ADC_DAPRAT 0x251A -+#define AC200_ADC_DAPNTH 0x251C -+#define AC200_ADC_DAPLHNAC 0x251E -+#define AC200_ADC_DAPLLNAC 0x2520 -+#define AC200_ADC_DAPRHNAC 0x2522 -+#define AC200_ADC_DAPRLNAC 0x2524 -+#define AC200_AC_DAPHHPFC 0x2526 -+#define AC200_AC_DAPLHPFC 0x2528 -+#define AC200_AC_DAPOPT 0x252A -+#define AC200_AC_DAC_DAPCTRL 0x3000 -+#define AC200_AC_DRC_HHPFC 0x3002 -+#define AC200_AC_DRC_LHPFC 0x3004 -+#define AC200_AC_DRC_CTRL 0x3006 -+#define AC200_AC_DRC_LPFHAT 0x3008 -+#define AC200_AC_DRC_LPFLAT 0x300A -+#define AC200_AC_DRC_RPFHAT 0x300C -+#define AC200_AC_DRC_RPFLAT 0x300E -+#define AC200_AC_DRC_LPFHRT 0x3010 -+#define AC200_AC_DRC_LPFLRT 0x3012 -+#define AC200_AC_DRC_RPFHRT 0x3014 -+#define AC200_AC_DRC_RPFLRT 0x3016 -+#define AC200_AC_DRC_LRMSHAT 0x3018 -+#define AC200_AC_DRC_LRMSLAT 0x301A -+#define AC200_AC_DRC_RRMSHAT 0x301C -+#define AC200_AC_DRC_RRMSLAT 0x301E -+#define AC200_AC_DRC_HCT 0x3020 -+#define AC200_AC_DRC_LCT 0x3022 -+#define AC200_AC_DRC_HKC 0x3024 -+#define AC200_AC_DRC_LKC 0x3026 -+#define AC200_AC_DRC_HOPC 0x3028 -+#define AC200_AC_DRC_LOPC 0x302A -+#define AC200_AC_DRC_HLT 0x302C -+#define AC200_AC_DRC_LLT 0x302E -+#define AC200_AC_DRC_HKI 0x3030 -+#define AC200_AC_DRC_LKI 0x3032 -+#define AC200_AC_DRC_HOPL 0x3034 -+#define AC200_AC_DRC_LOPL 0x3036 -+#define AC200_AC_DRC_HET 0x3038 -+#define AC200_AC_DRC_LET 0x303A -+#define AC200_AC_DRC_HKE 0x303C -+#define AC200_AC_DRC_LKE 0x303E -+#define AC200_AC_DRC_HOPE 0x3040 -+#define AC200_AC_DRC_LOPE 0x3042 -+#define AC200_AC_DRC_HKN 0x3044 -+#define AC200_AC_DRC_LKN 0x3046 -+#define AC200_AC_DRC_SFHAT 0x3048 -+#define AC200_AC_DRC_SFLAT 0x304A -+#define AC200_AC_DRC_SFHRT 0x304C -+#define AC200_AC_DRC_SFLRT 0x304E -+#define AC200_AC_DRC_MXGHS 0x3050 -+#define AC200_AC_DRC_MXGLS 0x3052 -+#define AC200_AC_DRC_MNGHS 0x3054 -+#define AC200_AC_DRC_MNGLS 0x3056 -+#define AC200_AC_DRC_EPSHC 0x3058 -+#define AC200_AC_DRC_EPSLC 0x305A -+#define AC200_AC_DRC_HPFHGAIN 0x305E -+#define AC200_AC_DRC_HPFLGAIN 0x3060 -+#define AC200_AC_DRC_BISTCR 0x3100 -+#define AC200_AC_DRC_BISTST 0x3102 -+ -+/* TVE registers */ -+#define AC200_TVE_CTL0 0x4000 -+#define AC200_TVE_CTL1 0x4002 -+#define AC200_TVE_MOD0 0x4004 -+#define AC200_TVE_MOD1 0x4006 -+#define AC200_TVE_DAC_CFG0 0x4008 -+#define AC200_TVE_DAC_CFG1 0x400A -+#define AC200_TVE_YC_DELAY 0x400C -+#define AC200_TVE_YC_FILTER 0x400E -+#define AC200_TVE_BURST_FRQ0 0x4010 -+#define AC200_TVE_BURST_FRQ1 0x4012 -+#define AC200_TVE_FRONT_PORCH 0x4014 -+#define AC200_TVE_BACK_PORCH 0x4016 -+#define AC200_TVE_TOTAL_LINE 0x401C -+#define AC200_TVE_FIRST_ACTIVE 0x401E -+#define AC200_TVE_BLACK_LEVEL 0x4020 -+#define AC200_TVE_BLANK_LEVEL 0x4022 -+#define AC200_TVE_PLUG_EN 0x4030 -+#define AC200_TVE_PLUG_IRQ_EN 0x4032 -+#define AC200_TVE_PLUG_IRQ_STA 0x4034 -+#define AC200_TVE_PLUG_STA 0x4038 -+#define AC200_TVE_PLUG_DEBOUNCE 0x4040 -+#define AC200_TVE_DAC_TEST 0x4042 -+#define AC200_TVE_PLUG_PULSE_LEVEL 0x40F4 -+#define AC200_TVE_PLUG_PULSE_START 0x40F8 -+#define AC200_TVE_PLUG_PULSE_PERIOD 0x40FA -+#define AC200_TVE_IF_CTL 0x5000 -+#define AC200_TVE_IF_TIM0 0x5008 -+#define AC200_TVE_IF_TIM1 0x500A -+#define AC200_TVE_IF_TIM2 0x500C -+#define AC200_TVE_IF_TIM3 0x500E -+#define AC200_TVE_IF_SYNC0 0x5010 -+#define AC200_TVE_IF_SYNC1 0x5012 -+#define AC200_TVE_IF_SYNC2 0x5014 -+#define AC200_TVE_IF_TIM4 0x5016 -+#define AC200_TVE_IF_STATUS 0x5018 -+ -+/* EPHY registers */ -+#define AC200_EPHY_CTL 0x6000 -+#define AC200_EPHY_BIST 0x6002 -+ -+/* eFuse registers (0x8000 - 0x9FFF, layout unknown) */ -+ -+/* RTC registers */ -+#define AC200_LOSC_CTRL0 0xA000 -+#define AC200_LOSC_CTRL1 0xA002 -+#define AC200_LOSC_AUTO_SWT_STA 0xA004 -+#define AC200_INTOSC_CLK_PRESCAL 0xA008 -+#define AC200_RTC_YY_MM_DD0 0xA010 -+#define AC200_RTC_YY_MM_DD1 0xA012 -+#define AC200_RTC_HH_MM_SS0 0xA014 -+#define AC200_RTC_HH_MM_SS1 0xA016 -+#define AC200_ALARM0_CUR_VLU0 0xA024 -+#define AC200_ALARM0_CUR_VLU1 0xA026 -+#define AC200_ALARM0_ENABLE 0xA028 -+#define AC200_ALARM0_IRQ_EN 0xA02C -+#define AC200_ALARM0_IRQ_STA 0xA030 -+#define AC200_ALARM1_WK_HH_MM_SS0 0xA040 -+#define AC200_ALARM1_WK_HH_MM_SS1 0xA042 -+#define AC200_ALARM1_ENABLE 0xA044 -+#define AC200_ALARM1_IRQ_EN 0xA048 -+#define AC200_ALARM1_IRQ_STA 0xA04C -+#define AC200_ALARM_CONFIG 0xA050 -+#define AC200_LOSC_OUT_GATING 0xA060 -+#define AC200_GP_DATA(x) (0xA100 + (x) * 2) -+#define AC200_RTC_DEB 0xA170 -+#define AC200_GPL_HOLD_OUTPUT 0xA180 -+#define AC200_VDD_RTC 0xA190 -+#define AC200_IC_CHARA0 0xA1F0 -+#define AC200_IC_CHARA1 0xA1F2 -+ -+struct ac200_dev { -+ struct regmap *regmap; -+ struct regmap_irq_chip_data *regmap_irqc; -+}; -+ -+#endif /* __LINUX_MFD_AC200_H */ --- -2.35.3 - diff --git a/patch/kernel/archive/sunxi-6.1/patches.armbian/drv-net-phy-Add-support-for-AC200-EPHY.patch b/patch/kernel/archive/sunxi-6.1/patches.armbian/drv-net-phy-Add-support-for-AC200-EPHY.patch deleted file mode 100644 index 37cf83343..000000000 --- a/patch/kernel/archive/sunxi-6.1/patches.armbian/drv-net-phy-Add-support-for-AC200-EPHY.patch +++ /dev/null @@ -1,272 +0,0 @@ -From b22ce8f1db276a0bc23c20a800e3f438df60737c Mon Sep 17 00:00:00 2001 -From: Jernej Skrabec -Date: Fri, 16 Aug 2019 16:38:57 +0200 -Subject: [PATCH 010/170] drv:net:phy: Add support for AC200 EPHY - -Signed-off-by: Jernej Skrabec ---- - drivers/net/phy/Kconfig | 7 ++ - drivers/net/phy/Makefile | 1 + - drivers/net/phy/ac200.c | 220 +++++++++++++++++++++++++++++++++++++++ - 3 files changed, 228 insertions(+) - create mode 100644 drivers/net/phy/ac200.c - -diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig -index 9fee639ee..cb9ad6790 100644 ---- a/drivers/net/phy/Kconfig -+++ b/drivers/net/phy/Kconfig -@@ -63,6 +63,13 @@ config SFP - - comment "MII PHY device drivers" - -+config AC200_PHY -+ tristate "AC200 EPHY" -+ depends on NVMEM -+ depends on OF -+ help -+ Fast ethernet PHY as found in X-Powers AC200 multi-function device. -+ - config AMD_PHY - tristate "AMD PHYs" - help -diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile -index b12b1d86f..e3469be6f 100644 ---- a/drivers/net/phy/Makefile -+++ b/drivers/net/phy/Makefile -@@ -30,6 +30,7 @@ obj-$(CONFIG_SFP) += sfp.o - sfp-obj-$(CONFIG_SFP) += sfp-bus.o - obj-y += $(sfp-obj-y) $(sfp-obj-m) - -+obj-$(CONFIG_AC200_PHY) += ac200.o - obj-$(CONFIG_ADIN_PHY) += adin.o - obj-$(CONFIG_ADIN1100_PHY) += adin1100.o - obj-$(CONFIG_AMD_PHY) += amd.o -diff --git a/drivers/net/phy/ac200.c b/drivers/net/phy/ac200.c -new file mode 100644 -index 000000000..cb713188f ---- /dev/null -+++ b/drivers/net/phy/ac200.c -@@ -0,0 +1,220 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/** -+ * Driver for AC200 Ethernet PHY -+ * -+ * Copyright (c) 2020 Jernej Skrabec -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define AC200_EPHY_ID 0x00441400 -+#define AC200_EPHY_ID_MASK 0x0ffffff0 -+ -+/* macros for system ephy control 0 register */ -+#define AC200_EPHY_RESET_INVALID BIT(0) -+#define AC200_EPHY_SYSCLK_GATING BIT(1) -+ -+/* macros for system ephy control 1 register */ -+#define AC200_EPHY_E_EPHY_MII_IO_EN BIT(0) -+#define AC200_EPHY_E_LNK_LED_IO_EN BIT(1) -+#define AC200_EPHY_E_SPD_LED_IO_EN BIT(2) -+#define AC200_EPHY_E_DPX_LED_IO_EN BIT(3) -+ -+/* macros for ephy control register */ -+#define AC200_EPHY_SHUTDOWN BIT(0) -+#define AC200_EPHY_LED_POL BIT(1) -+#define AC200_EPHY_CLK_SEL BIT(2) -+#define AC200_EPHY_ADDR(x) (((x) & 0x1F) << 4) -+#define AC200_EPHY_XMII_SEL BIT(11) -+#define AC200_EPHY_CALIB(x) (((x) & 0xF) << 12) -+ -+struct ac200_ephy_dev { -+ struct clk *clk; -+ struct phy_driver *ephy; -+ struct regmap *regmap; -+}; -+ -+static char *ac200_phy_name = "AC200 EPHY"; -+ -+static int ac200_ephy_config_init(struct phy_device *phydev) -+{ -+ const struct ac200_ephy_dev *priv = phydev->drv->driver_data; -+ unsigned int value; -+ int ret; -+ -+ phy_write(phydev, 0x1f, 0x0100); /* Switch to Page 1 */ -+ phy_write(phydev, 0x12, 0x4824); /* Disable APS */ -+ -+ phy_write(phydev, 0x1f, 0x0200); /* Switch to Page 2 */ -+ phy_write(phydev, 0x18, 0x0000); /* PHYAFE TRX optimization */ -+ -+ phy_write(phydev, 0x1f, 0x0600); /* Switch to Page 6 */ -+ phy_write(phydev, 0x14, 0x708f); /* PHYAFE TX optimization */ -+ phy_write(phydev, 0x13, 0xF000); /* PHYAFE RX optimization */ -+ phy_write(phydev, 0x15, 0x1530); -+ -+ phy_write(phydev, 0x1f, 0x0800); /* Switch to Page 6 */ -+ phy_write(phydev, 0x18, 0x00bc); /* PHYAFE TRX optimization */ -+ -+ phy_write(phydev, 0x1f, 0x0100); /* switch to page 1 */ -+ phy_clear_bits(phydev, 0x17, BIT(3)); /* disable intelligent IEEE */ -+ -+ /* next two blocks disable 802.3az IEEE */ -+ phy_write(phydev, 0x1f, 0x0200); /* switch to page 2 */ -+ phy_write(phydev, 0x18, 0x0000); -+ -+ phy_write(phydev, 0x1f, 0x0000); /* switch to page 0 */ -+ phy_clear_bits_mmd(phydev, 0x7, 0x3c, BIT(1)); -+ -+ if (phydev->interface == PHY_INTERFACE_MODE_RMII) -+ value = AC200_EPHY_XMII_SEL; -+ else -+ value = 0; -+ -+ ret = regmap_update_bits(priv->regmap, AC200_EPHY_CTL, -+ AC200_EPHY_XMII_SEL, value); -+ if (ret) -+ return ret; -+ -+ /* FIXME: This is H6 specific */ -+ phy_set_bits(phydev, 0x13, BIT(12)); -+ -+ return 0; -+} -+ -+static int ac200_ephy_probe(struct platform_device *pdev) -+{ -+ struct ac200_dev *ac200 = dev_get_drvdata(pdev->dev.parent); -+ struct device *dev = &pdev->dev; -+ struct ac200_ephy_dev *priv; -+ struct nvmem_cell *calcell; -+ struct phy_driver *ephy; -+ u16 *caldata, calib; -+ size_t callen; -+ int ret; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ ephy = devm_kzalloc(dev, sizeof(*ephy), GFP_KERNEL); -+ if (!ephy) -+ return -ENOMEM; -+ -+ priv->clk = devm_clk_get(dev, NULL); -+ if (IS_ERR(priv->clk)) { -+ dev_err(dev, "Can't obtain the clock!\n"); -+ return PTR_ERR(priv->clk); -+ } -+ -+ calcell = devm_nvmem_cell_get(dev, "calibration"); -+ if (IS_ERR(calcell)) { -+ dev_err(dev, "Unable to find calibration data!\n"); -+ return PTR_ERR(calcell); -+ } -+ -+ caldata = nvmem_cell_read(calcell, &callen); -+ if (IS_ERR(caldata)) { -+ dev_err(dev, "Unable to read calibration data!\n"); -+ return PTR_ERR(caldata); -+ } -+ -+ if (callen != 2) { -+ dev_err(dev, "Calibration data has wrong length: 2 != %zu\n", -+ callen); -+ kfree(caldata); -+ return -EINVAL; -+ } -+ -+ calib = *caldata + 3; -+ kfree(caldata); -+ -+ ret = clk_prepare_enable(priv->clk); -+ if (ret) -+ return ret; -+ -+ ephy->phy_id = AC200_EPHY_ID; -+ ephy->phy_id_mask = AC200_EPHY_ID_MASK; -+ ephy->name = ac200_phy_name; -+ ephy->driver_data = priv; -+ ephy->soft_reset = genphy_soft_reset; -+ ephy->config_init = ac200_ephy_config_init; -+ ephy->suspend = genphy_suspend; -+ ephy->resume = genphy_resume; -+ -+ priv->ephy = ephy; -+ priv->regmap = ac200->regmap; -+ platform_set_drvdata(pdev, priv); -+ -+ ret = regmap_write(ac200->regmap, AC200_SYS_EPHY_CTL0, -+ AC200_EPHY_RESET_INVALID | -+ AC200_EPHY_SYSCLK_GATING); -+ if (ret) -+ return ret; -+ -+ ret = regmap_write(ac200->regmap, AC200_SYS_EPHY_CTL1, -+ AC200_EPHY_E_EPHY_MII_IO_EN | -+ AC200_EPHY_E_LNK_LED_IO_EN | -+ AC200_EPHY_E_SPD_LED_IO_EN | -+ AC200_EPHY_E_DPX_LED_IO_EN); -+ if (ret) -+ return ret; -+ -+ ret = regmap_write(ac200->regmap, AC200_EPHY_CTL, -+ AC200_EPHY_LED_POL | -+ AC200_EPHY_CLK_SEL | -+ AC200_EPHY_ADDR(1) | -+ AC200_EPHY_CALIB(calib)); -+ if (ret) -+ return ret; -+ -+ ret = phy_driver_register(priv->ephy, THIS_MODULE); -+ if (ret) { -+ dev_err(dev, "Unable to register phy\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int ac200_ephy_remove(struct platform_device *pdev) -+{ -+ struct ac200_ephy_dev *priv = platform_get_drvdata(pdev); -+ -+ phy_driver_unregister(priv->ephy); -+ -+ regmap_write(priv->regmap, AC200_EPHY_CTL, AC200_EPHY_SHUTDOWN); -+ regmap_write(priv->regmap, AC200_SYS_EPHY_CTL1, 0); -+ regmap_write(priv->regmap, AC200_SYS_EPHY_CTL0, 0); -+ -+ clk_disable_unprepare(priv->clk); -+ -+ return 0; -+} -+ -+static const struct of_device_id ac200_ephy_match[] = { -+ { .compatible = "x-powers,ac200-ephy" }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(of, ac200_ephy_match); -+ -+static struct platform_driver ac200_ephy_driver = { -+ .probe = ac200_ephy_probe, -+ .remove = ac200_ephy_remove, -+ .driver = { -+ .name = "ac200-ephy", -+ .of_match_table = ac200_ephy_match, -+ }, -+}; -+module_platform_driver(ac200_ephy_driver); -+ -+MODULE_AUTHOR("Jernej Skrabec "); -+MODULE_DESCRIPTION("AC200 Ethernet PHY driver"); -+MODULE_LICENSE("GPL"); --- -2.35.3 - diff --git a/patch/kernel/archive/sunxi-6.1/patches.armbian/mfd-Add-support-for-X-Powers-AC200-EPHY-syscon.patch b/patch/kernel/archive/sunxi-6.1/patches.armbian/mfd-Add-support-for-X-Powers-AC200-EPHY-syscon.patch new file mode 100644 index 000000000..252ba126e --- /dev/null +++ b/patch/kernel/archive/sunxi-6.1/patches.armbian/mfd-Add-support-for-X-Powers-AC200-EPHY-syscon.patch @@ -0,0 +1,375 @@ +From 3f2e8f9da20ce69785be3135e2a2db74955d239b Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Mon, 13 Jun 2022 17:37:19 +0100 +Subject: [PATCH] mfd: Add support for X-Powers AC200 EPHY syscon + +The X-Powers AC200 mixed signal chip contains a 100Mbit/s Ethernet PHY. +While the PHY is using the standard MDIO and MII/RMII interfaces to +the MAC, there are some registers in the AC200 that need to be setup to +make the PHY functional: +- The MDIO PHY address needs to set. +- The LED polarity needs to be configured. +- The MII interface mode needs to be set (MII or RMII). +- There is a reset line that controls the PHY operation. +- There is a clock gate that blocks or forwards the AC200's internal clock + to the PHY. + +This driver here takes care of those setup needs, but does not cover the +actual PHY operation. Once the PHY is set up and enabled, it behaves like +a standard MDIO/MII controlled PHY, and can be driven by the IEEE802.3-C22 +driver. + +To some degree those parameters mimic the typical wired setup of a +physical PHY chip: the LED polarity, MII interface mode and PHY address +are typically configued via connecting certain pins. We use the +devicetree to learn those parameters, which depend on the board setup. + +This driver is a child of the AC200 MFD parent device, and uses its +regmap and input clock. It also provides a reset and clock controller, +which the actual PHY device (node) needs to refer to. This ensures that +this PHY control device is initialised before the PHY is expected to work. + +Signed-off-by: Andre Przywara +--- + drivers/phy/allwinner/Kconfig | 9 + + drivers/phy/allwinner/Makefile | 1 + + drivers/phy/allwinner/ac200-ephy-ctl.c | 301 +++++++++++++++++++++++++ + 3 files changed, 311 insertions(+) + create mode 100644 drivers/phy/allwinner/ac200-ephy-ctl.c + +diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig +index e93a53139460..d3614169de5c 100644 +--- a/drivers/phy/allwinner/Kconfig ++++ b/drivers/phy/allwinner/Kconfig +@@ -58,3 +58,12 @@ config PHY_SUN50I_USB3 + part of Allwinner H6 SoC. + + This driver controls each individual USB 2+3 host PHY combo. ++ ++config AC200_PHY_CTL ++ tristate "X-Power AC200 PHY control driver" ++ depends on MFD_AC200 ++ depends on RESET_CONTROLLER ++ help ++ Enable this to support the Ethernet PHY operation of the AC200 ++ mixed signal chip. This driver just enables and configures the ++ PHY, the PHY itself is supported by a standard driver. +diff --git a/drivers/phy/allwinner/Makefile b/drivers/phy/allwinner/Makefile +index bd74901a1255..0eecec7a908a 100644 +--- a/drivers/phy/allwinner/Makefile ++++ b/drivers/phy/allwinner/Makefile +@@ -3,3 +3,4 @@ obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o + obj-$(CONFIG_PHY_SUN6I_MIPI_DPHY) += phy-sun6i-mipi-dphy.o + obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o + obj-$(CONFIG_PHY_SUN50I_USB3) += phy-sun50i-usb3.o ++obj-$(CONFIG_AC200_PHY_CTL) += ac200-ephy-ctl.o +diff --git a/drivers/phy/allwinner/ac200-ephy-ctl.c b/drivers/phy/allwinner/ac200-ephy-ctl.c +new file mode 100644 +index 000000000000..8efeaf18e42c +--- /dev/null ++++ b/drivers/phy/allwinner/ac200-ephy-ctl.c +@@ -0,0 +1,301 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/** ++ * syscon driver to control and configure AC200 Ethernet PHY ++ * Copyright (c) 2022 Arm Ltd. ++ * ++ * TODO's and questions: ++ * ========================= ++ * - This driver is something like a syscon driver, as it controls various ++ * bits and registers that effect other devices (the actual PHY). It's ++ * unclear where it should live, though: ++ * - it could be integrated into the MFD driver, but this looks messy ++ * - it could live at the current location (drivers/phy/allwinner), but that ++ * sounds wrong ++ * - it could be a separate file, but in drivers/mfd ++ * - anything else ++ * ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* macros for system ephy control 0 register */ ++#define AC200_SYS_EPHY_CTL0 0x0014 ++#define AC200_EPHY_RESET_INVALID BIT(0) ++#define AC200_EPHY_SYSCLK_GATING 1 ++ ++/* macros for system ephy control 1 register */ ++#define AC200_SYS_EPHY_CTL1 0x0016 ++#define AC200_EPHY_E_EPHY_MII_IO_EN BIT(0) ++#define AC200_EPHY_E_LNK_LED_IO_EN BIT(1) ++#define AC200_EPHY_E_SPD_LED_IO_EN BIT(2) ++#define AC200_EPHY_E_DPX_LED_IO_EN BIT(3) ++ ++/* macros for ephy control register */ ++#define AC200_EPHY_CTL 0x6000 ++#define AC200_EPHY_SHUTDOWN BIT(0) ++#define AC200_EPHY_LED_POL BIT(1) ++#define AC200_EPHY_CLK_SEL BIT(2) ++#define AC200_EPHY_ADDR(x) (((x) & 0x1F) << 4) ++#define AC200_EPHY_XMII_SEL BIT(11) ++#define AC200_EPHY_CALIB(x) (((x) & 0xF) << 12) ++ ++struct ac200_ephy_ctl_dev { ++ struct reset_controller_dev rcdev; ++ struct clk_hw *gate_clk; ++ struct regmap *regmap; ++}; ++ ++static struct ac200_ephy_ctl_dev *to_phy_dev(struct reset_controller_dev *rcdev) ++{ ++ return container_of(rcdev, struct ac200_ephy_ctl_dev, rcdev); ++} ++ ++static int ephy_ctl_reset(struct reset_controller_dev *rcdev, unsigned long id) ++{ ++ struct ac200_ephy_ctl_dev *ac200 = to_phy_dev(rcdev); ++ int ret; ++ ++ ret = regmap_clear_bits(ac200->regmap, AC200_SYS_EPHY_CTL0, ++ AC200_EPHY_RESET_INVALID); ++ if (ret) ++ return ret; ++ ++ /* This is going via I2C, so there is plenty of built-in delay. */ ++ return regmap_set_bits(ac200->regmap, AC200_SYS_EPHY_CTL0, ++ AC200_EPHY_RESET_INVALID); ++} ++ ++static int ephy_ctl_assert(struct reset_controller_dev *rcdev, unsigned long id) ++{ ++ struct ac200_ephy_ctl_dev *ac200 = to_phy_dev(rcdev); ++ ++ return regmap_clear_bits(ac200->regmap, AC200_SYS_EPHY_CTL0, ++ AC200_EPHY_RESET_INVALID); ++} ++ ++static int ephy_ctl_deassert(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ struct ac200_ephy_ctl_dev *ac200 = to_phy_dev(rcdev); ++ ++ return regmap_set_bits(ac200->regmap, AC200_SYS_EPHY_CTL0, ++ AC200_EPHY_RESET_INVALID); ++} ++ ++static int ephy_ctl_status(struct reset_controller_dev *rcdev, unsigned long id) ++{ ++ struct ac200_ephy_ctl_dev *ac200 = to_phy_dev(rcdev); ++ ++ return regmap_test_bits(ac200->regmap, AC200_SYS_EPHY_CTL0, ++ AC200_EPHY_RESET_INVALID); ++} ++ ++static int ephy_ctl_reset_of_xlate(struct reset_controller_dev *rcdev, ++ const struct of_phandle_args *reset_spec) ++{ ++ if (WARN_ON(reset_spec->args_count != 0)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++const struct reset_control_ops ephy_ctl_reset_ops = { ++ .assert = ephy_ctl_assert, ++ .deassert = ephy_ctl_deassert, ++ .reset = ephy_ctl_reset, ++ .status = ephy_ctl_status, ++}; ++ ++static void ac200_ephy_ctl_disable(struct ac200_ephy_ctl_dev *priv) ++{ ++ regmap_write(priv->regmap, AC200_EPHY_CTL, AC200_EPHY_SHUTDOWN); ++ regmap_write(priv->regmap, AC200_SYS_EPHY_CTL1, 0); ++ regmap_write(priv->regmap, AC200_SYS_EPHY_CTL0, 0); ++} ++ ++static int ac200_ephy_ctl_probe(struct platform_device *pdev) ++{ ++ struct reset_controller_dev *rcdev; ++ struct device *dev = &pdev->dev; ++ struct ac200_ephy_ctl_dev *priv; ++ struct nvmem_cell *calcell; ++ const char *parent_name; ++ phy_interface_t phy_if; ++ u16 *caldata, ephy_ctl; ++ struct clk *clk; ++ size_t callen; ++ u32 value; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, priv); ++ ++ priv->regmap = dev_get_regmap(dev->parent, NULL); ++ if (!priv->regmap) ++ return -EPROBE_DEFER; ++ ++ calcell = devm_nvmem_cell_get(dev, "calibration"); ++ if (IS_ERR(calcell)) ++ return dev_err_probe(dev, PTR_ERR(calcell), ++ "Unable to find calibration data!\n"); ++ ++ caldata = nvmem_cell_read(calcell, &callen); ++ if (IS_ERR(caldata)) { ++ dev_err(dev, "Unable to read calibration data!\n"); ++ return PTR_ERR(caldata); ++ } ++ ++ if (callen != 2) { ++ dev_err(dev, "Calibration data length must be 2 bytes!\n"); ++ kfree(caldata); ++ return -EINVAL; ++ } ++ ++ ephy_ctl = AC200_EPHY_CALIB(*caldata + 3); ++ kfree(caldata); ++ ++ ret = of_get_phy_mode(dev->of_node, &phy_if); ++ if (ret) { ++ dev_err(dev, "Unable to read PHY connection mode\n"); ++ return ret; ++ } ++ ++ switch (phy_if) { ++ case PHY_INTERFACE_MODE_MII: ++ break; ++ case PHY_INTERFACE_MODE_RMII: ++ ephy_ctl |= AC200_EPHY_XMII_SEL; ++ break; ++ default: ++ dev_err(dev, "Illegal PHY connection mode (%d), only RMII or MII supported\n", ++ phy_if); ++ return -EINVAL; ++ } ++ ++ ret = of_property_read_u32(dev->of_node, "x-powers,led-polarity", ++ &value); ++ if (ret) { ++ dev_err(dev, "Unable to read LED polarity setting\n"); ++ return ret; ++ } ++ ++ if (value == GPIO_ACTIVE_LOW) ++ ephy_ctl |= AC200_EPHY_LED_POL; ++ ++ ret = of_property_read_u32(dev->of_node, "phy-address", &value); ++ if (ret) { ++ dev_err(dev, "Unable to read PHY address value\n"); ++ return ret; ++ } ++ ++ ephy_ctl |= AC200_EPHY_ADDR(value); ++ ++ clk = clk_get(dev->parent, NULL); ++ if (IS_ERR(clk)) ++ return dev_err_probe(dev, PTR_ERR(clk), ++ "Unable to obtain the clock\n"); ++ ++ if (clk_get_rate(clk) == 24000000) ++ ephy_ctl |= AC200_EPHY_CLK_SEL; ++ ++ clk_put(clk); ++ ++ /* Assert reset and gate clock, to disable PHY for now */ ++ ret = regmap_write(priv->regmap, AC200_SYS_EPHY_CTL0, 0); ++ if (ret) ++ return ret; ++ ++ ret = regmap_write(priv->regmap, AC200_SYS_EPHY_CTL1, ++ AC200_EPHY_E_EPHY_MII_IO_EN | ++ AC200_EPHY_E_LNK_LED_IO_EN | ++ AC200_EPHY_E_SPD_LED_IO_EN | ++ AC200_EPHY_E_DPX_LED_IO_EN); ++ if (ret) ++ return ret; ++ ++ ret = regmap_write(priv->regmap, AC200_EPHY_CTL, ephy_ctl); ++ if (ret) ++ return ret; ++ ++ rcdev = &priv->rcdev; ++ rcdev->owner = dev->driver->owner; ++ rcdev->nr_resets = 1; ++ rcdev->ops = &ephy_ctl_reset_ops; ++ rcdev->of_node = dev->of_node; ++ rcdev->of_reset_n_cells = 0; ++ rcdev->of_xlate = ephy_ctl_reset_of_xlate; ++ ++ ret = devm_reset_controller_register(dev, rcdev); ++ if (ret) { ++ dev_err(dev, "Unable to register reset controller: %d\n", ret); ++ goto err_disable_ephy; ++ } ++ ++ parent_name = of_clk_get_parent_name(dev->parent->of_node, 0); ++ priv->gate_clk = devm_clk_hw_register_regmap_gate(dev, ++ "ac200-ephy-ctl-gate", parent_name, 0, ++ priv->regmap, AC200_SYS_EPHY_CTL0, ++ AC200_EPHY_SYSCLK_GATING, 0); ++ if (IS_ERR(priv->gate_clk)) { ++ ret = PTR_ERR(priv->gate_clk); ++ dev_err(dev, "Unable to register gate clock: %d\n", ret); ++ goto err_disable_ephy; ++ } ++ ++ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, ++ priv->gate_clk); ++ if (ret) { ++ dev_err(dev, "Unable to register clock provider: %d\n", ret); ++ goto err_disable_ephy; ++ } ++ ++ return 0; ++ ++err_disable_ephy: ++ ac200_ephy_ctl_disable(priv); ++ ++ return ret; ++} ++ ++static int ac200_ephy_ctl_remove(struct platform_device *pdev) ++{ ++ struct ac200_ephy_ctl_dev *priv = platform_get_drvdata(pdev); ++ ++ ac200_ephy_ctl_disable(priv); ++ ++ return 0; ++} ++ ++static const struct of_device_id ac200_ephy_ctl_match[] = { ++ { .compatible = "x-powers,ac200-ephy-ctl" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, ac200_ephy_ctl_match); ++ ++static struct platform_driver ac200_ephy_ctl_driver = { ++ .probe = ac200_ephy_ctl_probe, ++ .remove = ac200_ephy_ctl_remove, ++ .driver = { ++ .name = "ac200-ephy-ctl", ++ .of_match_table = ac200_ephy_ctl_match, ++ }, ++}; ++module_platform_driver(ac200_ephy_ctl_driver); ++ ++MODULE_AUTHOR("Andre Przywara "); ++MODULE_DESCRIPTION("AC200 Ethernet PHY control driver"); ++MODULE_LICENSE("GPL"); +-- +2.34.1 + diff --git a/patch/kernel/archive/sunxi-6.1/patches.armbian/mfd-Add-support-for-X-Powers-AC200.patch b/patch/kernel/archive/sunxi-6.1/patches.armbian/mfd-Add-support-for-X-Powers-AC200.patch new file mode 100644 index 000000000..107e2ee11 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.1/patches.armbian/mfd-Add-support-for-X-Powers-AC200.patch @@ -0,0 +1,267 @@ +From b9cfea3958c89d9a29b46fec023035a9964304bd Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Fri, 16 Aug 2019 16:38:21 +0200 +Subject: [PATCH] mfd: Add support for X-Powers AC200 + +The X-Powers AC200 is a mixed signal multi-purpose chip, which provides +audio DAC/ADCs, a CVBS video encoder, a 100Mbit/s Ethernet PHY and a +real-time clock. Its control registers can be accessed via I2C or +Allwinner's RSB bus. +Beside this chip being used on some older boards (for instance the Remix +Mini PC), it is quite wide spread due to its die being co-packaged on the +Allwinner H6 and H616 SoCs, which use its audio, video and PHY +functionality. + +Aside from the RTC, the other functions do not need constant +hand-holding via the I2C registers, but rather need to be configured and +enabled only once. + +We model the control side of this chip using the MFD subsystem. This +driver here just provides the parent device for the various subfunctions, +and takes care of enabling clocks and reset, but also provides the regmap, +which the respective child drivers will use. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Andre Przywara +--- + drivers/mfd/Kconfig | 12 +++ + drivers/mfd/Makefile | 1 + + drivers/mfd/ac200.c | 191 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 204 insertions(+) + create mode 100644 drivers/mfd/ac200.c + +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index 9da8235cb690..6fbf88940f26 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -179,6 +179,18 @@ config MFD_AC100 + This driver include only the core APIs. You have to select individual + components like codecs or RTC under the corresponding menus. + ++config MFD_AC200 ++ tristate "X-Powers AC200" ++ select MFD_CORE ++ select REGMAP_I2C ++ depends on COMMON_CLK ++ depends on I2C ++ depends on OF ++ help ++ If you say Y here you get support for the X-Powers AC200 IC. ++ This driver include only the core APIs. You have to select individual ++ components like Ethernet PHY or codec under the corresponding menus. ++ + config MFD_AXP20X + tristate + select MFD_CORE +diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile +index 7ed3ef4a698c..91dc530d0bde 100644 +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -144,6 +144,7 @@ obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o + obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o + + obj-$(CONFIG_MFD_AC100) += ac100.o ++obj-$(CONFIG_MFD_AC200) += ac200.o + obj-$(CONFIG_MFD_AXP20X) += axp20x.o + obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o + obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o +diff --git a/drivers/mfd/ac200.c b/drivers/mfd/ac200.c +new file mode 100644 +index 000000000000..625b119f53cf +--- /dev/null ++++ b/drivers/mfd/ac200.c +@@ -0,0 +1,191 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * MFD core driver for X-Powers' AC200 IC ++ * ++ * The AC200 is a chip which is co-packaged with Allwinner H6 SoC and ++ * includes analog audio codec, analog TV encoder, ethernet PHY, eFuse ++ * and RTC. ++ * ++ * Copyright (c) 2019 Jernej Skrabec ++ * ++ * Based on AC100 driver with following copyrights: ++ * Copyright (2016) Chen-Yu Tsai ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct ac200_dev { ++ struct clk *clk; ++ struct regmap *regmap; ++}; ++ ++#define AC200_SYS_CONTROL 0x0002 ++#define AC200_SYS_BG_CTL 0x0050 ++ ++/* interface register (can be accessed from any page) */ ++#define AC200_TWI_REG_ADDR_H 0xFE ++ ++#define AC200_MAX_REG 0xA1F2 ++ ++static const struct regmap_range_cfg ac200_range_cfg[] = { ++ { ++ .range_max = AC200_MAX_REG, ++ .selector_reg = AC200_TWI_REG_ADDR_H, ++ .selector_mask = 0xff, ++ .selector_shift = 0, ++ .window_start = 0, ++ .window_len = 256, ++ } ++}; ++ ++static const struct regmap_config ac200_regmap_config = { ++ .name = "AC200", ++ .reg_bits = 8, ++ .reg_stride = 2, ++ .val_bits = 16, ++ .ranges = ac200_range_cfg, ++ .num_ranges = ARRAY_SIZE(ac200_range_cfg), ++ .max_register = AC200_MAX_REG, ++}; ++ ++static struct mfd_cell ac200_cells[] = { ++ { ++ .name = "ac200-codec", ++ .of_compatible = "x-powers,ac200-codec", ++ }, { ++ .name = "ac200-ephy-ctl", ++ .of_compatible = "x-powers,ac200-ephy-ctl", ++ }, ++}; ++ ++static int ac200_i2c_probe(struct i2c_client *i2c, ++ const struct i2c_device_id *id) ++{ ++ struct device *dev = &i2c->dev; ++ struct nvmem_cell *bgcell; ++ struct ac200_dev *ac200; ++ u16 *bgdata, bgval; ++ size_t bglen; ++ int ret; ++ ++ ac200 = devm_kzalloc(dev, sizeof(*ac200), GFP_KERNEL); ++ if (!ac200) ++ return -ENOMEM; ++ ++ i2c_set_clientdata(i2c, ac200); ++ ++ ac200->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(ac200->clk)) ++ return dev_err_probe(dev, PTR_ERR(ac200->clk), ++ "Can't obtain the clock\n"); ++ ++ ac200->regmap = devm_regmap_init_i2c(i2c, &ac200_regmap_config); ++ if (IS_ERR(ac200->regmap)) { ++ ret = PTR_ERR(ac200->regmap); ++ dev_err(dev, "Regmap init failed: %d\n", ret); ++ return ret; ++ } ++ ++ bgcell = devm_nvmem_cell_get(dev, "bandgap"); ++ if (IS_ERR(bgcell)) ++ return dev_err_probe(dev, PTR_ERR(bgcell), ++ "Unable to find bandgap data!\n"); ++ ++ bgdata = nvmem_cell_read(bgcell, &bglen); ++ if (IS_ERR(bgdata)) { ++ dev_err(dev, "Unable to read bandgap data!\n"); ++ return PTR_ERR(bgdata); ++ } ++ ++ if (bglen != 2) { ++ dev_err(dev, "Invalid nvmem bandgap length!\n"); ++ kfree(bgdata); ++ return -EINVAL; ++ } ++ ++ bgval = *bgdata; ++ kfree(bgdata); ++ ++ ret = clk_prepare_enable(ac200->clk); ++ if (ret) ++ return ret; ++ ++ /* ++ * There is no documentation on how long we have to wait before ++ * executing first operation. Vendor driver sleeps for 40 ms. ++ */ ++ msleep(40); ++ ++ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0); ++ if (ret) ++ goto err; ++ ++ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 1); ++ if (ret) ++ goto err; ++ ++ if (bgval) { ++ /* bandgap register is not documented */ ++ ret = regmap_write(ac200->regmap, AC200_SYS_BG_CTL, ++ 0x8280 | bgval); ++ if (ret) ++ goto err; ++ } ++ ++ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, ac200_cells, ++ ARRAY_SIZE(ac200_cells), NULL, 0, NULL); ++ if (ret) { ++ dev_err(dev, "Failed to add MFD devices: %d\n", ret); ++ goto err; ++ } ++ ++ return 0; ++ ++err: ++ clk_disable_unprepare(ac200->clk); ++ return ret; ++} ++ ++static void ac200_i2c_remove(struct i2c_client *i2c) ++{ ++ struct ac200_dev *ac200 = i2c_get_clientdata(i2c); ++ ++ regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0); ++ ++ clk_disable_unprepare(ac200->clk); ++} ++ ++static const struct i2c_device_id ac200_ids[] = { ++ { "ac200", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ac200_ids); ++ ++static const struct of_device_id ac200_of_match[] = { ++ { .compatible = "x-powers,ac200" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, ac200_of_match); ++ ++static struct i2c_driver ac200_i2c_driver = { ++ .driver = { ++ .name = "ac200", ++ .of_match_table = of_match_ptr(ac200_of_match), ++ }, ++ .probe = ac200_i2c_probe, ++ .remove = ac200_i2c_remove, ++ .id_table = ac200_ids, ++}; ++module_i2c_driver(ac200_i2c_driver); ++ ++MODULE_DESCRIPTION("MFD core driver for AC200"); ++MODULE_AUTHOR("Jernej Skrabec "); ++MODULE_LICENSE("GPL v2"); +-- +2.34.1 + diff --git a/patch/kernel/archive/sunxi-6.1/patches.armbian/mfd-sunxi-ac200-fix-error-initialization.patch b/patch/kernel/archive/sunxi-6.1/patches.armbian/mfd-sunxi-ac200-fix-error-initialization.patch deleted file mode 100644 index bb12c779b..000000000 --- a/patch/kernel/archive/sunxi-6.1/patches.armbian/mfd-sunxi-ac200-fix-error-initialization.patch +++ /dev/null @@ -1,34 +0,0 @@ -From eab277e9ca1f5558dbbf9820dbf6cf4edf4d4c39 Mon Sep 17 00:00:00 2001 -From: The-going <48602507+The-going@users.noreply.github.com> -Date: Tue, 28 Feb 2023 13:39:51 +0300 -Subject: [PATCH] mfd: sunxi-ac200: fix error initialization - ---- - drivers/mfd/sunxi-ac200.c | 4 +--- - 1 file changed, 1 insertion(+), 3 deletions(-) - -diff --git a/drivers/mfd/sunxi-ac200.c b/drivers/mfd/sunxi-ac200.c -index 368a54587..b784a82f3 100644 ---- a/drivers/mfd/sunxi-ac200.c -+++ b/drivers/mfd/sunxi-ac200.c -@@ -144,7 +144,7 @@ static int ac200_i2c_probe(struct i2c_client *i2c, - return 0; - } - --static int ac200_i2c_remove(struct i2c_client *i2c) -+static void ac200_i2c_remove(struct i2c_client *i2c) - { - struct ac200_dev *ac200 = i2c_get_clientdata(i2c); - -@@ -152,8 +152,6 @@ static int ac200_i2c_remove(struct i2c_client *i2c) - - mfd_remove_devices(&i2c->dev); - regmap_del_irq_chip(i2c->irq, ac200->regmap_irqc); -- -- return 0; - } - - static const struct i2c_device_id ac200_ids[] = { --- -2.35.3 - diff --git a/patch/kernel/archive/sunxi-6.1/patches.armbian/net-phy-Add-support-for-AC200-EPHY.patch b/patch/kernel/archive/sunxi-6.1/patches.armbian/net-phy-Add-support-for-AC200-EPHY.patch new file mode 100644 index 000000000..6bc3a33fd --- /dev/null +++ b/patch/kernel/archive/sunxi-6.1/patches.armbian/net-phy-Add-support-for-AC200-EPHY.patch @@ -0,0 +1,143 @@ +From af665542835fd39edf63c8c9ad1f7267e64a2320 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Fri, 16 Aug 2019 16:38:57 +0200 +Subject: [PATCH] net: phy: Add support for AC200 EPHY + +The X-Powers AC200 mixed signal chip contains a 100Mbit/s Ethernet PHY. +While its sporting a usable default setup, and can be controlled by the +generic IEEE802.3-C22 PHY driver, the BSP sets up some extra registers, +which this driver here covers. + +Add a PHY driver matching the AC200 EPHY ID registers, and which +programs some PHY registers according to the BSP code. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Andre Przywara +--- + drivers/net/phy/Kconfig | 7 ++++ + drivers/net/phy/Makefile | 1 + + drivers/net/phy/ac200-phy.c | 82 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 90 insertions(+) + create mode 100644 drivers/net/phy/ac200.c + +diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig +index c57a0262fb64..a1f8fdfc5762 100644 +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -63,6 +63,13 @@ config SFP + + comment "MII PHY device drivers" + ++config AC200_PHY ++ tristate "AC200 EPHY" ++ depends on NVMEM ++ depends on OF ++ help ++ Fast ethernet PHY as found in X-Powers AC200 multi-function device. ++ + config AMD_PHY + tristate "AMD PHYs" + help +diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile +index f7138d3c896b..9ad2b8cc01b1 100644 +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -30,6 +30,7 @@ obj-$(CONFIG_SFP) += sfp.o + sfp-obj-$(CONFIG_SFP) += sfp-bus.o + obj-y += $(sfp-obj-y) $(sfp-obj-m) + ++obj-$(CONFIG_AC200_PHY) += ac200-phy.o + obj-$(CONFIG_ADIN_PHY) += adin.o + obj-$(CONFIG_ADIN1100_PHY) += adin1100.o + obj-$(CONFIG_AMD_PHY) += amd.o +diff --git a/drivers/net/phy/ac200-phy.c b/drivers/net/phy/ac200-phy.c +new file mode 100644 +index 000000000000..8499914f49b8 +--- /dev/null ++++ b/drivers/net/phy/ac200-phy.c +@@ -0,0 +1,82 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/** ++ * Driver for AC200 Ethernet PHY ++ * ++ * Copyright (c) 2019 Jernej Skrabec ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define AC200_EPHY_ID 0x00441400 ++#define AC200_EPHY_ID_MASK 0x0ffffff0 ++ ++static int ac200_ephy_config_init(struct phy_device *phydev) ++{ ++ phy_write(phydev, 0x1f, 0x0100); /* Switch to Page 1 */ ++ phy_write(phydev, 0x12, 0x4824); /* Disable APS */ ++ ++ phy_write(phydev, 0x1f, 0x0200); /* Switch to Page 2 */ ++ phy_write(phydev, 0x18, 0x0000); /* PHYAFE TRX optimization */ ++ ++ phy_write(phydev, 0x1f, 0x0600); /* Switch to Page 6 */ ++ phy_write(phydev, 0x14, 0x708f); /* PHYAFE TX optimization */ ++ phy_write(phydev, 0x13, 0xF000); /* PHYAFE RX optimization */ ++ phy_write(phydev, 0x15, 0x1530); ++ ++ phy_write(phydev, 0x1f, 0x0800); /* Switch to Page 8 */ ++ phy_write(phydev, 0x18, 0x00bc); /* PHYAFE TRX optimization */ ++ ++ phy_write(phydev, 0x1f, 0x0100); /* switch to page 1 */ ++ phy_clear_bits(phydev, 0x17, BIT(3)); /* disable intelligent EEE */ ++ ++ /* disable 802.3az EEE */ ++ phy_write(phydev, 0x1f, 0x0200); /* switch to page 2 */ ++ phy_write(phydev, 0x18, 0x0000); ++ phy_write(phydev, 0x1f, 0x0000); /* switch to page 0 */ ++ phy_clear_bits_mmd(phydev, 0x7, 0x3c, BIT(1)); ++ ++ /* FIXME: This is probably H6 specific */ ++ phy_set_bits(phydev, 0x13, BIT(12)); ++ ++ return 0; ++} ++ ++static int ac200_ephy_probe(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ struct clk *clk; ++ ++ clk = devm_clk_get_optional_enabled(dev, NULL); ++ if (IS_ERR(clk)) ++ return dev_err_probe(dev, PTR_ERR(clk), ++ "Failed to request clock\n"); ++ ++ return 0; ++} ++ ++static struct phy_driver ac200_ephy_driver[] = { ++ { ++ .phy_id = AC200_EPHY_ID, ++ .phy_id_mask = AC200_EPHY_ID_MASK, ++ .name = "Allwinner AC200 EPHY", ++ .soft_reset = genphy_soft_reset, ++ .config_init = ac200_ephy_config_init, ++ .probe = ac200_ephy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ } ++}; ++module_phy_driver(ac200_ephy_driver); ++ ++MODULE_AUTHOR("Jernej Skrabec "); ++MODULE_DESCRIPTION("AC200 Ethernet PHY driver"); ++MODULE_LICENSE("GPL"); ++ ++static const struct mdio_device_id __maybe_unused ac200_ephy_phy_tbl[] = { ++ { AC200_EPHY_ID, AC200_EPHY_ID_MASK }, ++ { } ++}; ++MODULE_DEVICE_TABLE(mdio, ac200_ephy_phy_tbl); +-- +2.34.1 + diff --git a/patch/kernel/archive/sunxi-6.1/series.armbian b/patch/kernel/archive/sunxi-6.1/series.armbian index b3312bf9c..fe45e579c 100644 --- a/patch/kernel/archive/sunxi-6.1/series.armbian +++ b/patch/kernel/archive/sunxi-6.1/series.armbian @@ -4,9 +4,7 @@ # git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git # patches.armbian/Doc-dt-bindings-usb-add-binding-for-DWC3-controller-on-Allwinne.patch - patches.armbian/drv-mfd-Add-support-for-AC200.patch patches.armbian/drv-pinctrl-pinctrl-sun50i-a64-disable_strict_mode.patch - patches.armbian/drv-net-phy-Add-support-for-AC200-EPHY.patch patches.armbian/drv-rtc-sun6i-support-RTCs-without-external-LOSCs.patch patches.armbian/drv-gpu-drm-gem-dma-Export-with-handle-allocator.patch patches.armbian/drv-gpu-drm-sun4i-Add-GEM-allocator.patch @@ -72,7 +70,6 @@ patches.armbian/Bananapro-add-AXP209-regulators.patch patches.armbian/arm-dts-sunxi-h3-h5.dtsi-force-mmc0-bus-width.patch patches.armbian/arm64-dts-sun50i-a64-pine64-enable-wifi-mmc1.patch - patches.armbian/arm64-dts-sun50i-h6-Add-AC200-EPHY-related-nodes.patch patches.armbian/arm64-dts-sun50i-a64-sopine-baseboard-Add-i2s2-mmc1.patch patches.armbian/arm64-dts-sun50i-h6-Add-r_uart-uart2-3-pins.patch ####### sun50i-h616 ####### @@ -163,16 +160,12 @@ patches.armbian/Add-dump_reg-and-sunxi-sysinfo-drivers.patch patches.armbian/Add-sunxi-addr-driver-Used-to-fix-uwe5622-bluetooth-MAC-address.patch patches.armbian/net-phy-Support-yt8531c.patch - patches.armbian/allwinner-h6-Support-ac200-audio-codec.patch patches.armbian/nvmem-sunxi_sid-add-sunxi_get_soc_chipid-sunxi_get_serial.patch - patches.armbian/add-initial-support-for-orangepi3-lts.patch patches.armbian/mmc-host-sunxi-mmc-Fix-H6-emmc.patch patches.armbian/arm64-dts-allwinner-sun50i-h6-Fix-H6-emmc.patch - patches.armbian/Rollback-r_rsb-to-r_i2c.patch patches.armbian/arm64-dts-sun50i-h5-nanopi-r1s-h5-add-rtl8153-support.patch patches.armbian/net-usb-r8152-add-LED-configuration-from-OF.patch patches.armbian/arm64-dts-sun50i-h6-orangepi.dtsi-Rollback-r_rsb-to-r_i2c.patch - patches.armbian/mfd-sunxi-ac200-fix-error-initialization.patch patches.armbian/arm-dts-sunxi-h3-h5-add_tve.patch patches.armbian/arm-dts-sun8i-h3-fix-thermal-read.patch patches.armbian/arm64-dts-sun50i-h616-bigtreetech-cb1.patch @@ -181,3 +174,14 @@ patches.armbian/arm64-dts-sun50i-h5-enable-power-button-for-orangepi-prime.patch patches.armbian/arm64-dts-sun50i-h5-Add-missing-GPU-trip-point.patch patches.armbian/drivers-devfreq-sun8i-a33-mbus-disable-autorefresh.patch + patches.armbian/clk-gate-add-support-for-regmap-based-gates.patch + patches.armbian/mfd-Add-support-for-X-Powers-AC200.patch + patches.armbian/mfd-Add-support-for-X-Powers-AC200-EPHY-syscon.patch + patches.armbian/net-phy-Add-support-for-AC200-EPHY.patch + patches.armbian/arm64-dts-allwinner-h6-Add-AC200-EPHY-nodes.patch + patches.armbian/arm64-dts-allwinner-h6-tanix-enable-Ethernet.patch + patches.armbian/ASoC-AC200-Initial-driver.patch + patches.armbian/arm64-dts-allwinner-h6-add-AC200-codec-nodes.patch + patches.armbian/arm64-dts-allwinner-h6-enable-AC200-codec.patch + patches.armbian/add-nodes-for-sunxi-info-addr-dump-reg.patch + patches.armbian/add-initial-support-for-orangepi3-lts.patch diff --git a/patch/kernel/archive/sunxi-6.1/series.conf b/patch/kernel/archive/sunxi-6.1/series.conf index 0fd07e67c..8cad273c2 100644 --- a/patch/kernel/archive/sunxi-6.1/series.conf +++ b/patch/kernel/archive/sunxi-6.1/series.conf @@ -408,9 +408,7 @@ # ################################################################################ patches.armbian/Doc-dt-bindings-usb-add-binding-for-DWC3-controller-on-Allwinne.patch - patches.armbian/drv-mfd-Add-support-for-AC200.patch patches.armbian/drv-pinctrl-pinctrl-sun50i-a64-disable_strict_mode.patch - patches.armbian/drv-net-phy-Add-support-for-AC200-EPHY.patch patches.armbian/drv-rtc-sun6i-support-RTCs-without-external-LOSCs.patch patches.armbian/drv-gpu-drm-gem-dma-Export-with-handle-allocator.patch patches.armbian/drv-gpu-drm-sun4i-Add-GEM-allocator.patch @@ -476,7 +474,6 @@ patches.armbian/Bananapro-add-AXP209-regulators.patch patches.armbian/arm-dts-sunxi-h3-h5.dtsi-force-mmc0-bus-width.patch patches.armbian/arm64-dts-sun50i-a64-pine64-enable-wifi-mmc1.patch - patches.armbian/arm64-dts-sun50i-h6-Add-AC200-EPHY-related-nodes.patch patches.armbian/arm64-dts-sun50i-a64-sopine-baseboard-Add-i2s2-mmc1.patch patches.armbian/arm64-dts-sun50i-h6-Add-r_uart-uart2-3-pins.patch ####### sun50i-h616 ####### @@ -567,16 +564,12 @@ patches.armbian/Add-dump_reg-and-sunxi-sysinfo-drivers.patch patches.armbian/Add-sunxi-addr-driver-Used-to-fix-uwe5622-bluetooth-MAC-address.patch patches.armbian/net-phy-Support-yt8531c.patch - patches.armbian/allwinner-h6-Support-ac200-audio-codec.patch patches.armbian/nvmem-sunxi_sid-add-sunxi_get_soc_chipid-sunxi_get_serial.patch - patches.armbian/add-initial-support-for-orangepi3-lts.patch patches.armbian/mmc-host-sunxi-mmc-Fix-H6-emmc.patch patches.armbian/arm64-dts-allwinner-sun50i-h6-Fix-H6-emmc.patch - patches.armbian/Rollback-r_rsb-to-r_i2c.patch patches.armbian/arm64-dts-sun50i-h5-nanopi-r1s-h5-add-rtl8153-support.patch patches.armbian/net-usb-r8152-add-LED-configuration-from-OF.patch patches.armbian/arm64-dts-sun50i-h6-orangepi.dtsi-Rollback-r_rsb-to-r_i2c.patch - patches.armbian/mfd-sunxi-ac200-fix-error-initialization.patch patches.armbian/arm-dts-sunxi-h3-h5-add_tve.patch patches.armbian/arm-dts-sun8i-h3-fix-thermal-read.patch patches.armbian/arm64-dts-sun50i-h616-bigtreetech-cb1.patch @@ -585,3 +578,14 @@ patches.armbian/arm64-dts-sun50i-h5-enable-power-button-for-orangepi-prime.patch patches.armbian/arm64-dts-sun50i-h5-Add-missing-GPU-trip-point.patch patches.armbian/drivers-devfreq-sun8i-a33-mbus-disable-autorefresh.patch + patches.armbian/clk-gate-add-support-for-regmap-based-gates.patch + patches.armbian/mfd-Add-support-for-X-Powers-AC200.patch + patches.armbian/mfd-Add-support-for-X-Powers-AC200-EPHY-syscon.patch + patches.armbian/net-phy-Add-support-for-AC200-EPHY.patch + patches.armbian/arm64-dts-allwinner-h6-Add-AC200-EPHY-nodes.patch + patches.armbian/arm64-dts-allwinner-h6-tanix-enable-Ethernet.patch + patches.armbian/ASoC-AC200-Initial-driver.patch + patches.armbian/arm64-dts-allwinner-h6-add-AC200-codec-nodes.patch + patches.armbian/arm64-dts-allwinner-h6-enable-AC200-codec.patch + patches.armbian/add-nodes-for-sunxi-info-addr-dump-reg.patch + patches.armbian/add-initial-support-for-orangepi3-lts.patch diff --git a/patch/kernel/archive/sunxi-6.5/patches.armbian/ASoC-AC200-Initial-driver.patch b/patch/kernel/archive/sunxi-6.5/patches.armbian/ASoC-AC200-Initial-driver.patch new file mode 100644 index 000000000..4f02be91f --- /dev/null +++ b/patch/kernel/archive/sunxi-6.5/patches.armbian/ASoC-AC200-Initial-driver.patch @@ -0,0 +1,844 @@ +From a53d7dcd7a940a0701b22da4c98d3002c01b1174 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Thu, 1 Sep 2022 17:36:53 +0200 +Subject: [PATCH] ASoC: AC200: Initial driver + +Signed-off-by: Jernej Skrabec +--- + sound/soc/codecs/Kconfig | 10 + + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/ac200.c | 774 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 786 insertions(+) + create mode 100644 sound/soc/codecs/ac200.c + +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +index e9d3617bb068..dc6de4ced0c6 100644 +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -17,6 +17,7 @@ config SND_SOC_ALL_CODECS + imply SND_SOC_88PM860X + imply SND_SOC_L3 + imply SND_SOC_AB8500_CODEC ++ imply SND_SOC_AC200_CODEC + imply SND_SOC_AC97_CODEC + imply SND_SOC_AD1836 + imply SND_SOC_AD193X_SPI +@@ -370,6 +371,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 e2d463a317e5..fb7030376397 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 +@@ -361,6 +362,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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++MODULE_LICENSE("GPL"); +-- +2.34.1 + diff --git a/patch/kernel/archive/sunxi-6.5/patches.armbian/Rollback-r_rsb-to-r_i2c.patch b/patch/kernel/archive/sunxi-6.5/patches.armbian/Rollback-r_rsb-to-r_i2c.patch deleted file mode 100644 index cb3bdd68f..000000000 --- a/patch/kernel/archive/sunxi-6.5/patches.armbian/Rollback-r_rsb-to-r_i2c.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 6bdb276a08c82541b6c408b9b3cdf432ed8bb3de Mon Sep 17 00:00:00 2001 -From: Ukhellfire -Date: Fri, 1 Apr 2022 09:44:19 +0100 -Subject: [PATCH 149/153] Rollback r_rsb to r_i2c - ---- - arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts -index cc5a73026..0b07f8ca2 100644 ---- a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts -+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts -@@ -208,12 +208,12 @@ &pio { - vcc-pg-supply = <®_vcc_wifi_io>; - }; - --&r_rsb { -+&r_i2c { - status = "okay"; - -- axp805: pmic@745 { -+ axp805: pmic@36 { - compatible = "x-powers,axp805", "x-powers,axp806"; -- reg = <0x745>; -+ reg = <0x36>; - interrupt-parent = <&r_intc>; - interrupts = ; - interrupt-controller; --- -2.35.3 - diff --git a/patch/kernel/archive/sunxi-6.5/patches.armbian/add-initial-support-for-orangepi3-lts.patch b/patch/kernel/archive/sunxi-6.5/patches.armbian/add-initial-support-for-orangepi3-lts.patch index a79386ca6..8c1228ec7 100644 --- a/patch/kernel/archive/sunxi-6.5/patches.armbian/add-initial-support-for-orangepi3-lts.patch +++ b/patch/kernel/archive/sunxi-6.5/patches.armbian/add-initial-support-for-orangepi3-lts.patch @@ -1,13 +1,12 @@ -From c697fff6d002b50d264dc9d1b3c2731cf991e854 Mon Sep 17 00:00:00 2001 +From c21550d5fbd11743bee19e142d3eb04d9ea68177 Mon Sep 17 00:00:00 2001 From: The-going <48602507+The-going@users.noreply.github.com> Date: Sat, 16 Apr 2022 11:51:35 +0300 -Subject: [PATCH 146/153] add initial support for orangepi3-lts +Subject: [PATCH 151/158] add initial support for orangepi3-lts --- arch/arm64/boot/dts/allwinner/Makefile | 1 + - .../allwinner/sun50i-h6-orangepi-3-lts.dts | 398 ++++++++++++++++++ - arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 101 ++++- - 3 files changed, 487 insertions(+), 13 deletions(-) + .../allwinner/sun50i-h6-orangepi-3-lts.dts | 399 ++++++++++++++++++ + 2 files changed, 400 insertions(+), 0 deletions(-) create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts diff --git a/arch/arm64/boot/dts/allwinner/Makefile b/arch/arm64/boot/dts/allwinner/Makefile @@ -27,8 +26,9 @@ new file mode 100644 index 000000000..cc5a73026 --- /dev/null +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts -@@ -0,0 +1,398 @@ +@@ -0,0 +1,399 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++// Copyright (C) 2023 Jernej Skrabec +// Copyright (C) 2019 OndÅ™ej Jirman + +/dts-v1/; @@ -43,10 +43,8 @@ index 000000000..cc5a73026 + compatible = "xunlong,orangepi-3-lts", "allwinner,sun50i-h6"; + + aliases { -+ serial0 = &uart0; -+ serial1 = &uart1; -+ serial9 = &r_uart; + ethernet0 = &emac; ++ serial0 = &uart0; + }; + + chosen { @@ -135,6 +133,19 @@ index 000000000..cc5a73026 + }; +}; + ++&ac200_codec { ++ avcc-supply = <®_aldo2>; ++ status = "okay"; ++}; ++ ++&ac200_pwm_clk { ++ status = "okay"; ++}; ++ ++&analog { ++ status = "okay"; ++}; ++ +&cpu0 { + cpu-supply = <®_dcdca>; +}; @@ -155,12 +166,24 @@ index 000000000..cc5a73026 + status = "okay"; +}; + ++&emac { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&ext_rgmii_pins>; ++ phy-mode = "rgmii-id"; ++ phy-handle = <&ext_rgmii_phy>; ++ phy-supply = <®_gmac_3v3>; ++ allwinner,rx-delay-ps = <200>; ++ allwinner,tx-delay-ps = <300>; ++ status = "okay"; ++}; ++ +&gpu { + mali-supply = <®_dcdcc>; + status = "okay"; +}; + +&hdmi { ++ hvcc-supply = <®_bldo2>; + status = "okay"; +}; + @@ -170,14 +193,15 @@ index 000000000..cc5a73026 + }; +}; + -+&emac { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&ext_rgmii_pins>; -+ phy-mode = "rgmii-id"; -+ phy-handle = <&ext_rgmii_phy>; -+ phy-supply = <®_gmac_3v3>; -+ allwinner,rx-delay-ps = <200>; -+ allwinner,tx-delay-ps = <300>; ++&i2c3 { ++ status = "okay"; ++}; ++ ++&i2s1 { ++ status = "okay"; ++}; ++ ++&i2s3 { + status = "okay"; +}; + @@ -192,10 +216,6 @@ index 000000000..cc5a73026 + }; +}; + -+&i2s1 { -+ status = "okay"; -+}; -+ +&mmc0 { + vmmc-supply = <®_cldo1>; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ @@ -213,8 +233,6 @@ index 000000000..cc5a73026 +}; + +&mmc2 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&mmc2_pins>; + vmmc-supply = <®_cldo1>; + vqmmc-supply = <®_bldo2>; + bus-width = <8>; @@ -238,12 +256,20 @@ index 000000000..cc5a73026 + vcc-pg-supply = <®_vcc_wifi_io>; +}; + -+&r_rsb { ++&pwm { ++ status = "okay"; ++}; ++ ++&r_ir { ++ status = "okay"; ++}; ++ ++&r_i2c { + status = "okay"; + -+ axp805: pmic@745 { ++ axp805: pmic@36 { + compatible = "x-powers,axp805", "x-powers,axp806"; -+ reg = <0x745>; ++ reg = <0x36>; + interrupt-parent = <&r_intc>; + interrupts = ; + interrupt-controller; @@ -296,11 +322,11 @@ index 000000000..cc5a73026 + regulator-name = "vcc-efuse-pcie-hdmi-pc"; + }; + -+ reg_blod3: bldo3 { ++ reg_bldo3: bldo3 { + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; -+ regulator-name = "vcc-wifi-io-pm-pg"; ++ regulator-name = "vcc-pm-pg-dcxoio-wifi"; + }; + + bldo4 { @@ -359,26 +385,6 @@ index 000000000..cc5a73026 + }; +}; + -+&pwm { -+ status = "okay"; -+}; -+ -+&ac200_pwm_clk { -+ status = "okay"; -+}; -+ -+&i2s3 { -+ status = "okay"; -+}; -+ -+&i2c3 { -+ status = "okay"; -+}; -+ -+&r_ir { -+ status = "okay"; -+}; -+ +&rtc { + clocks = <&ext_osc32k>; +}; @@ -387,12 +393,6 @@ index 000000000..cc5a73026 + status = "okay"; +}; + -+&sound_ac200 { -+ status = "okay"; -+}; -+ -+/delete-node/ &spi0; -+ +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_ph_pins>; @@ -426,185 +426,6 @@ index 000000000..cc5a73026 +&usb3phy { + status = "okay"; +}; -diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi -index e6fd95b6f..ed54c2c11 100644 ---- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi -+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi -@@ -92,6 +92,13 @@ osc24M: osc24M_clk { - clock-output-names = "osc24M"; - }; - -+ ext_osc32k: ext_osc32k_clk { -+ #clock-cells = <0>; -+ compatible = "fixed-clock"; -+ clock-frequency = <32768>; -+ clock-output-names = "ext_osc32k"; -+ }; -+ - pmu { - compatible = "arm,cortex-a53-pmu"; - interrupts = , -@@ -127,6 +134,28 @@ cpu { - }; - }; - -+ sound_ac200: sound_ac200 { -+ status = "disabled"; -+ compatible = "simple-audio-card"; -+ simple-audio-card,format = "i2s"; -+ simple-audio-card,frame-master = <&i2s3_master>; -+ simple-audio-card,bitclock-master = <&i2s3_master>; -+ simple-audio-card,name = "allwinner,ac200-codec"; -+ simple-audio-card,mclk-fs = <512>; -+ i2s3_master: simple-audio-card,cpu { -+ sound-dai = <&i2s3>; -+ system-clock-frequency = <22579200>; -+ dai-tdm-slot-num = <2>; -+ dai-tdm-slot-width = <32>; -+ }; -+ simple-audio-card,codec { -+ sound-dai = <&ac200_codec>; -+ system-clock-frequency = <22579200>; -+ dai-tdm-slot-num = <2>; -+ dai-tdm-slot-width = <32>; -+ }; -+ }; -+ - timer { - compatible = "arm,armv8-timer"; - arm,no-tick-in-suspend; -@@ -383,7 +412,6 @@ pwm: pwm@300a000 { - pio: pinctrl@300b000 { - compatible = "allwinner,sun50i-h6-pinctrl"; - reg = <0x0300b000 0x400>; -- interrupt-parent = <&r_intc>; - interrupts = , - , - , -@@ -533,6 +561,11 @@ uart3_rts_cts_pins: uart3-rts-cts-pins { - pins = "PD25", "PD26"; - function = "uart3"; - }; -+ -+ i2s3_pins: i2s3-pins { -+ pins = "PB12", "PB13", "PB14", "PB15", "PB16"; -+ function = "i2s3"; -+ }; - }; - - iommu: iommu@30f0000 { -@@ -731,6 +764,7 @@ i2c3: i2c@5002c00 { - ac200: mfd@10 { - compatible = "x-powers,ac200"; - reg = <0x10>; -+ clocks = <&ac200_pwm_clk>; - interrupt-parent = <&pio>; - interrupts = <1 20 IRQ_TYPE_LEVEL_LOW>; - interrupt-controller; -@@ -738,11 +772,16 @@ ac200: mfd@10 { - - ac200_ephy: phy { - compatible = "x-powers,ac200-ephy"; -- clocks = <&ac200_pwm_clk>; - nvmem-cells = <&ephy_calibration>; - nvmem-cell-names = "calibration"; - status = "disabled"; - }; -+ -+ ac200_codec: codec { -+ #sound-dai-cells = <0>; -+ compatible = "x-powers,ac200-codec"; -+ status = "okay"; -+ }; - }; - }; - -@@ -779,6 +818,21 @@ i2s1: i2s@5091000 { - status = "disabled"; - }; - -+ i2s3: i2s@508f000 { -+ #sound-dai-cells = <0>; -+ compatible = "allwinner,sun50i-h6-i2s"; -+ reg = <0x0508f000 0x1000>; -+ interrupts = ; -+ clocks = <&ccu CLK_BUS_I2S3>, <&ccu CLK_I2S3>; -+ clock-names = "apb", "mod"; -+ dmas = <&dma 6>, <&dma 6>; -+ resets = <&ccu RST_BUS_I2S3>; -+ dma-names = "rx", "tx"; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2s3_pins>; -+ status = "disabled"; -+ }; -+ - spdif: spdif@5093000 { - #sound-dai-cells = <0>; - compatible = "allwinner,sun50i-h6-spdif"; -@@ -1074,6 +1128,7 @@ rtc: rtc@7000000 { - interrupts = , - ; - clock-output-names = "osc32k", "osc32k-out", "iosc"; -+ clocks = <&ext_osc32k>; - #clock-cells = <1>; - }; - -@@ -1140,17 +1195,18 @@ r_uart_pins: r-uart-pins { - }; - - r_ir: ir@7040000 { -- compatible = "allwinner,sun50i-h6-ir", -- "allwinner,sun6i-a31-ir"; -- reg = <0x07040000 0x400>; -- interrupts = ; -- clocks = <&r_ccu CLK_R_APB1_IR>, -- <&r_ccu CLK_IR>; -- clock-names = "apb", "ir"; -- resets = <&r_ccu RST_R_APB1_IR>; -- pinctrl-names = "default"; -- pinctrl-0 = <&r_ir_rx_pin>; -- status = "disabled"; -+ compatible = "allwinner,sun50i-h6-ir", -+ "allwinner,sun6i-a31-ir"; -+ reg = <0x07040000 0x400>; -+ interrupt-parent = <&r_intc>; -+ interrupts = ; -+ clocks = <&r_ccu CLK_R_APB1_IR>, -+ <&r_ccu CLK_IR>; -+ clock-names = "apb", "ir"; -+ resets = <&r_ccu RST_R_APB1_IR>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&r_ir_rx_pin>; -+ status = "disabled"; - }; - - r_i2c: i2c@7081400 { -@@ -1192,6 +1248,25 @@ ths: thermal-sensor@5070400 { - nvmem-cell-names = "calibration"; - #thermal-sensor-cells = <1>; - }; -+ -+ sunxi-info { -+ compatible = "allwinner,sun50i-h6-sys-info"; -+ status = "okay"; -+ }; -+ -+ addr_mgt: addr-mgt { -+ compatible = "allwinner,sunxi-addr_mgt"; -+ type_addr_wifi = <0x2>; -+ type_addr_bt = <0x2>; -+ type_addr_eth = <0x2>; -+ status = "okay"; -+ }; -+ -+ dump_reg: dump_reg@20000 { -+ compatible = "allwinner,sunxi-dump-reg"; -+ reg = <0x0 0x03001000 0x0 0x0f20>; -+ status = "okay"; -+ }; - }; - - thermal-zones { -- 2.35.3 diff --git a/patch/kernel/archive/sunxi-6.5/patches.armbian/add-nodes-for-sunxi-info-addr-dump-reg.patch b/patch/kernel/archive/sunxi-6.5/patches.armbian/add-nodes-for-sunxi-info-addr-dump-reg.patch new file mode 100644 index 000000000..2fc8f7f55 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.5/patches.armbian/add-nodes-for-sunxi-info-addr-dump-reg.patch @@ -0,0 +1,42 @@ +From c21550d5fbd11743bee19e142d3eb04d9ea68177 Mon Sep 17 00:00:00 2001 +From: The-going <48602507+The-going@users.noreply.github.com> +Date: Sat, 16 Apr 2022 11:51:35 +0300 +Subject: [PATCH 151/158] add nodes for sunxi-info, sunxi-addr and sunxi-dump-reg + +--- + arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 101 ++++- + 1 files changed, 487 insertions(+), 13 deletions(-) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +index 11e905afa..791c124f4 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +@@ -1191,6 +1247,25 @@ ths: thermal-sensor@5070400 { + nvmem-cell-names = "calibration"; + #thermal-sensor-cells = <1>; + }; ++ ++ sunxi-info { ++ compatible = "allwinner,sun50i-h6-sys-info"; ++ status = "okay"; ++ }; ++ ++ addr_mgt: addr-mgt { ++ compatible = "allwinner,sunxi-addr_mgt"; ++ type_addr_wifi = <0x2>; ++ type_addr_bt = <0x2>; ++ type_addr_eth = <0x2>; ++ status = "okay"; ++ }; ++ ++ dump_reg: dump_reg@20000 { ++ compatible = "allwinner,sunxi-dump-reg"; ++ reg = <0x0 0x03001000 0x0 0x0f20>; ++ status = "okay"; ++ }; + }; + + thermal-zones { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.5/patches.armbian/allwinner-h6-Support-ac200-audio-codec.patch b/patch/kernel/archive/sunxi-6.5/patches.armbian/allwinner-h6-Support-ac200-audio-codec.patch deleted file mode 100644 index 18705af0f..000000000 --- a/patch/kernel/archive/sunxi-6.5/patches.armbian/allwinner-h6-Support-ac200-audio-codec.patch +++ /dev/null @@ -1,1875 +0,0 @@ -From 08d8881e87cf80b9fe04361dde8cc7a592884499 Mon Sep 17 00:00:00 2001 -From: afaulkner420 -Date: Fri, 25 Mar 2022 20:33:02 +0000 -Subject: [PATCH 144/153] allwinner: h6: Support ac200 audio codec - ---- - drivers/mfd/Makefile | 2 +- - drivers/mfd/{ac200.c => sunxi-ac200.c} | 16 +- - include/linux/mfd/ac200.h | 2 + - sound/soc/codecs/Kconfig | 8 + - sound/soc/codecs/Makefile | 2 + - sound/soc/codecs/acx00.c | 1371 ++++++++++++++++++++++++ - sound/soc/codecs/acx00.h | 356 ++++++ - 7 files changed, 1755 insertions(+), 2 deletions(-) - rename drivers/mfd/{ac200.c => sunxi-ac200.c} (93%) - create mode 100644 sound/soc/codecs/acx00.c - create mode 100644 sound/soc/codecs/acx00.h - -diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile -index b2e69fbce..0e4a09539 100644 ---- a/drivers/mfd/Makefile -+++ b/drivers/mfd/Makefile -@@ -141,7 +141,7 @@ obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o - obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o - - obj-$(CONFIG_MFD_AC100) += ac100.o --obj-$(CONFIG_MFD_AC200) += ac200.o -+obj-$(CONFIG_MFD_AC200) += sunxi-ac200.o - obj-$(CONFIG_MFD_AXP20X) += axp20x.o - obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o - obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o -diff --git a/drivers/mfd/ac200.c b/drivers/mfd/sunxi-ac200.c -similarity index 93% -rename from drivers/mfd/ac200.c -rename to drivers/mfd/sunxi-ac200.c -index 570573790..368a54587 100644 ---- a/drivers/mfd/ac200.c -+++ b/drivers/mfd/sunxi-ac200.c -@@ -41,6 +41,7 @@ static const struct regmap_range_cfg ac200_range_cfg[] = { - }; - - static const struct regmap_config ac200_regmap_config = { -+ .name = "ac200", - .reg_bits = 8, - .val_bits = 16, - .ranges = ac200_range_cfg, -@@ -75,6 +76,10 @@ static const struct mfd_cell ac200_cells[] = { - .resources = ephy_resource, - .of_compatible = "x-powers,ac200-ephy", - }, -+ { -+ .name = "acx00-codec", -+ .of_compatible = "x-powers,ac200-codec", -+ }, - }; - - static int ac200_i2c_probe(struct i2c_client *i2c, -@@ -97,8 +102,17 @@ static int ac200_i2c_probe(struct i2c_client *i2c, - return ret; - } - -- /* do a reset to put chip in a known state */ -+ ac200->clk = devm_clk_get(dev, NULL); -+ if (IS_ERR(ac200->clk)) { -+ dev_err(dev, "Can't obtain the clock!\n"); -+ return PTR_ERR(ac200->clk); -+ } - -+ ret = clk_prepare_enable(ac200->clk); -+ if (ret) -+ return ret; -+ -+ /* do a reset to put chip in a known state */ - ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0); - if (ret) - return ret; -diff --git a/include/linux/mfd/ac200.h b/include/linux/mfd/ac200.h -index 0c677094a..c8c140226 100644 ---- a/include/linux/mfd/ac200.h -+++ b/include/linux/mfd/ac200.h -@@ -9,6 +9,7 @@ - #define __LINUX_MFD_AC200_H - - #include -+#include - - /* interface registers (can be accessed from any page) */ - #define AC200_TWI_CHANGE_TO_RSB 0x3E -@@ -201,6 +202,7 @@ - #define AC200_IC_CHARA1 0xA1F2 - - struct ac200_dev { -+ struct clk *clk; - struct regmap *regmap; - struct regmap_irq_chip_data *regmap_irqc; - }; -diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig -index 944d0ea1a..359c515ef 100644 ---- a/sound/soc/codecs/Kconfig -+++ b/sound/soc/codecs/Kconfig -@@ -2170,4 +2170,12 @@ config SND_SOC_LPASS_TX_MACRO - select SND_SOC_LPASS_MACRO_COMMON - tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" - -+config SND_SOC_ACX00 -+ tristate "ACX00 Codec" -+ select MFD_ACX00 -+ default n -+ help -+ ACX00 now used as SUN50IW6 internal Codec, Connect Through I2S0. -+ Say Y or M if you want to add support internal audio codec. -+ - endmenu -diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile -index e2d463a31..ccb2a0fe4 100644 ---- a/sound/soc/codecs/Makefile -+++ b/sound/soc/codecs/Makefile -@@ -347,6 +347,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o - snd-soc-wsa881x-objs := wsa881x.o - snd-soc-wsa883x-objs := wsa883x.o - snd-soc-zl38060-objs := zl38060.o -+snd-soc-acx00-objs := acx00.o - # Amp - snd-soc-max9877-objs := max9877.o - snd-soc-max98504-objs := max98504.o -@@ -709,6 +710,7 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o - obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o - obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o - obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o -+obj-$(CONFIG_SND_SOC_ACX00) += snd-soc-acx00.o - - # Amp - obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o -diff --git a/sound/soc/codecs/acx00.c b/sound/soc/codecs/acx00.c -new file mode 100644 -index 000000000..ab7467e4e ---- /dev/null -+++ b/sound/soc/codecs/acx00.c -@@ -0,0 +1,1371 @@ -+/* -+ * acx00.c -- ACX00 ALSA Soc Audio Codec driver -+ * -+ * (C) Copyright 2010-2016 Allwinnertech Technology., Ltd. -+ * -+ * Author: Wolfgang Huang -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "acx00.h" -+ -+ -+#define ACX00_DEF_VOL 0x9F9F -+#undef ACX00_DAPM_LINEOUT -+ -+struct acx00_priv { -+ struct ac200_dev *acx00; /* parent mfd device struct */ -+ struct snd_soc_component *component; -+ struct clk *clk; -+ unsigned int sample_rate; -+ unsigned int fmt; -+ unsigned int enable; -+ unsigned int spk_gpio; -+ unsigned int switch_gpio; -+ bool spk_gpio_used; -+ struct mutex mutex; -+ struct delayed_work spk_work; -+ struct delayed_work resume_work; -+}; -+ -+struct sample_rate { -+ unsigned int samplerate; -+ unsigned int rate_bit; -+}; -+ -+static const struct sample_rate sample_rate_conv[] = { -+ {44100, 7}, -+ {48000, 8}, -+ {8000, 0}, -+ {32000, 6}, -+ {22050, 4}, -+ {24000, 5}, -+ {16000, 3}, -+ {11025, 1}, -+ {12000, 2}, -+ {192000, 10}, -+ {96000, 9}, -+}; -+ -+void __iomem *io_stat_addr; -+ -+static const DECLARE_TLV_DB_SCALE(i2s_mixer_adc_tlv, -600, 600, 1); -+static const DECLARE_TLV_DB_SCALE(i2s_mixer_dac_tlv, -600, 600, 1); -+static const DECLARE_TLV_DB_SCALE(dac_mixer_adc_tlv, -600, 600, 1); -+static const DECLARE_TLV_DB_SCALE(dac_mixer_dac_tlv, -600, 600, 1); -+static const DECLARE_TLV_DB_SCALE(line_out_tlv, -450, 150, 0); -+static const DECLARE_TLV_DB_SCALE(mic_out_tlv, -450, 150, 0); -+static const DECLARE_TLV_DB_SCALE(phoneout_tlv, -450, 150, 0); -+static const DECLARE_TLV_DB_SCALE(adc_input_tlv, -450, 150, 0); -+static const DECLARE_TLV_DB_SCALE(lineout_tlv, -4800, 150, 1); -+static const unsigned int mic_boost_tlv[] = { -+ 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 acx00_codec_controls[] = { -+ SOC_DOUBLE_TLV("I2S Mixer ADC Volume", AC_I2S_MIXER_GAIN, -+ I2S_MIXERL_GAIN_ADC, I2S_MIXERR_GAIN_ADC, -+ 0x1, 0, i2s_mixer_adc_tlv), -+ SOC_DOUBLE_TLV("I2S Mixer DAC Volume", AC_I2S_MIXER_GAIN, -+ I2S_MIXERL_GAIN_DAC, I2S_MIXERR_GAIN_DAC, -+ 0x1, 0, i2s_mixer_dac_tlv), -+ SOC_DOUBLE_TLV("DAC Mixer ADC Volume", AC_DAC_MIXER_GAIN, -+ DAC_MIXERL_GAIN_ADC, DAC_MIXERR_GAIN_ADC, -+ 0x1, 0, dac_mixer_adc_tlv), -+ SOC_DOUBLE_TLV("DAC Mxier DAC Volume", AC_DAC_MIXER_GAIN, -+ DAC_MIXERL_GAIN_DAC, DAC_MIXERR_GAIN_DAC, -+ 0x1, 0, dac_mixer_dac_tlv), -+ SOC_SINGLE_TLV("Line Out Mixer Volume", AC_OUT_MIXER_CTL, -+ OUT_MIXER_LINE_VOL, 0x7, 0, line_out_tlv), -+ SOC_DOUBLE_TLV("MIC Out Mixer Volume", AC_OUT_MIXER_CTL, -+ OUT_MIXER_MIC1_VOL, OUT_MIXER_MIC2_VOL, -+ 0x7, 0, mic_out_tlv), -+ SOC_SINGLE_TLV("ADC Input Volume", AC_ADC_MIC_CTL, -+ ADC_GAIN, 0x07, 0, adc_input_tlv), -+ SOC_SINGLE_TLV("Master Volume", AC_LINEOUT_CTL, -+ LINEOUT_VOL, 0x1f, 0, lineout_tlv), -+ SOC_SINGLE_TLV("MIC1 Boost Volume", AC_ADC_MIC_CTL, -+ MIC1_BOOST, 0x07, 0, mic_boost_tlv), -+ SOC_SINGLE_TLV("MIC2 Boost Volume", AC_ADC_MIC_CTL, -+ MIC2_BOOST, 0x07, 0, mic_boost_tlv), -+}; -+ -+/* Enable I2S & DAC clk, then enable the DAC digital part */ -+static int acx00_playback_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *k, int event) -+{ -+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ snd_soc_component_update_bits(component, AC_SYS_CLK_CTL, -+ (0x1<dapm); -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ snd_soc_component_update_bits(component, AC_SYS_CLK_CTL, -+ (0x1<spk_gpio, 1); -+} -+ -+static int acx00_lineout_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *k, int event) -+{ -+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); -+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component); -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ if (!priv->enable) { -+ snd_soc_component_update_bits(priv->component, AC_LINEOUT_CTL, -+ (1<component, AC_LINEOUT_CTL, -+ (1<enable = 1; -+ } -+#ifdef ACX00_DAPM_LINEOUT -+ snd_soc_component_update_bits(component, AC_LINEOUT_CTL, -+ (1<spk_gpio_used) { -+ if (spk_delay == 0) { -+ gpio_set_value(priv->spk_gpio, 1); -+ /* -+ * time delay to wait spk pa work fine, -+ * general setting 50ms -+ */ -+ mdelay(50); -+ } else -+ schedule_delayed_work(&priv->spk_work, -+ msecs_to_jiffies(spk_delay)); -+ } -+ break; -+ case SND_SOC_DAPM_PRE_PMD: -+ mdelay(50); -+ if (priv->spk_gpio_used) { -+ gpio_set_value(priv->spk_gpio, 0); -+ msleep(50); -+ } -+#ifdef ACX00_DAPM_LINEOUT -+ snd_soc_component_update_bits(component, AC_LINEOUT_CTL, -+ (1<regmap, reg, &val); -+ -+ if (ret < 0) -+ return ret; -+ else -+ return val; -+} -+ -+int acx00_reg_write(struct ac200_dev *acx00, unsigned short reg, unsigned short val) -+{ -+ return regmap_write(acx00->regmap, reg, val); -+} -+ -+static void acx00_codec_init(struct snd_soc_component *component) -+{ -+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component); -+ -+ acx00_reg_write(priv->acx00, 0x50, 0x82b1); -+ acx00_reg_write(priv->acx00, 0xc, 0xce01); -+ -+ /* acx00_codec sysctl init */ -+ acx00_reg_write(priv->acx00, 0x0010, 0x03); -+ acx00_reg_write(priv->acx00, 0x0012, 0x01); -+ -+ /* The bit3 need to setup to 1 for bias current. */ -+ snd_soc_component_update_bits(component, AC_MICBIAS_CTL, -+ (0x1 << ADDA_BIAS_CUR), (0x1 << ADDA_BIAS_CUR)); -+ -+ /* enable the output & global enable bit */ -+ snd_soc_component_update_bits(component, AC_I2S_CTL, -+ (1<spk_gpio_used) { -+ snd_soc_component_update_bits(priv->component, AC_LINEOUT_CTL, -+ (1<component, AC_LINEOUT_CTL, -+ (1<enable = 1; -+ } -+#ifndef ACX00_DAPM_LINEOUT -+ snd_soc_component_update_bits(component, AC_LINEOUT_CTL, (1<component; -+ int i; -+ -+ switch (params_format(params)) { -+ case SNDRV_PCM_FORMAT_S16_LE: -+ snd_soc_component_update_bits(component, AC_I2S_FMT0, -+ (7<dev, "unrecognized format support\n"); -+ break; -+ } -+ for (i = 0; i < ARRAY_SIZE(sample_rate_conv); i++) { -+ if (sample_rate_conv[i].samplerate == params_rate(params)) { -+ snd_soc_component_update_bits(component, AC_SYS_SR_CTL, -+ (SYS_SR_MASK<component; -+ -+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { -+ /* codec clk & FRM master */ -+ case SND_SOC_DAIFMT_CBM_CFM: -+ snd_soc_component_update_bits(component, AC_I2S_CLK, -+ (0x1<dev, "format setting failed\n"); -+ break; -+ } -+ -+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { -+ case SND_SOC_DAIFMT_NB_NF: -+ snd_soc_component_update_bits(component, AC_I2S_FMT1, -+ (0x1<dev, "invert clk setting failed\n"); -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+static int acx00_codec_dai_set_clkdiv(struct snd_soc_dai *codec_dai, -+ int clk_id, int clk_div) -+{ -+ struct acx00_priv *priv = snd_soc_dai_get_drvdata(codec_dai); -+ struct snd_soc_component *component = priv->component; -+ unsigned int bclk_div; -+ /* -+ * when PCM mode, setting as 64fs, when I2S mode as 32fs, -+ * then two channel, then just as 64fs -+ */ -+ unsigned int div_ratio = clk_div / 64; -+ -+ switch (div_ratio) { -+ case 1: -+ bclk_div = I2S_BCLK_DIV_1; -+ break; -+ case 2: -+ bclk_div = I2S_BCLK_DIV_2; -+ break; -+ case 4: -+ bclk_div = I2S_BCLK_DIV_3; -+ break; -+ case 6: -+ bclk_div = I2S_BCLK_DIV_4; -+ break; -+ case 8: -+ bclk_div = I2S_BCLK_DIV_5; -+ break; -+ case 12: -+ bclk_div = I2S_BCLK_DIV_6; -+ break; -+ case 16: -+ bclk_div = I2S_BCLK_DIV_7; -+ break; -+ case 24: -+ bclk_div = I2S_BCLK_DIV_8; -+ break; -+ case 32: -+ bclk_div = I2S_BCLK_DIV_9; -+ break; -+ case 48: -+ bclk_div = I2S_BCLK_DIV_10; -+ break; -+ case 64: -+ bclk_div = I2S_BCLK_DIV_11; -+ break; -+ case 96: -+ bclk_div = I2S_BCLK_DIV_12; -+ break; -+ case 128: -+ bclk_div = I2S_BCLK_DIV_13; -+ break; -+ case 176: -+ bclk_div = I2S_BCLK_DIV_14; -+ break; -+ case 192: -+ bclk_div = I2S_BCLK_DIV_15; -+ break; -+ default: -+ dev_err(component->dev, "setting blck div failed\n"); -+ break; -+ } -+ -+ snd_soc_component_update_bits(component, AC_I2S_CLK, -+ (I2S_BCLK_DIV_MASK<component; -+ return 0; -+} -+ -+static int acx00_codec_prepare(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *codec_dai) -+{ -+ struct snd_soc_component *component = codec_dai->component; -+ -+ snd_soc_component_update_bits(component, AC_SYS_CLK_CTL, -+ (0x1<stream == SNDRV_PCM_STREAM_PLAYBACK) { -+ if (acx00_loop_en) -+ snd_soc_component_update_bits(component, AC_I2S_FMT0, -+ (0x1<component; -+ -+ if (mute) -+ snd_soc_component_write(component, AC_I2S_DAC_VOL, 0); -+ else -+ snd_soc_component_write(component, AC_I2S_DAC_VOL, ACX00_DEF_VOL); -+ return 0; -+} -+ -+static void acx00_codec_shutdown(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct snd_soc_component *component = dai->component; -+ -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) -+ acx00_codec_txctrl_enable(component, 0); -+ else -+ acx00_codec_rxctrl_enable(component, 0); -+} -+ -+static const struct snd_soc_dai_ops acx00_codec_dai_ops = { -+ .hw_params = acx00_codec_hw_params, -+ .shutdown = acx00_codec_shutdown, -+// .digital_mute = acx00_codec_digital_mute, -+ .set_sysclk = acx00_codec_dai_set_sysclk, -+ .set_fmt = acx00_codec_dai_set_fmt, -+ .set_clkdiv = acx00_codec_dai_set_clkdiv, -+ .startup = acx00_codec_startup, -+ .trigger = acx00_codec_trigger, -+ .prepare = acx00_codec_prepare, -+}; -+ -+static struct snd_soc_dai_driver acx00_codec_dai[] = { -+ { -+ .name = "acx00-dai", -+ .playback = { -+ .stream_name = "Playback", -+ .channels_min = 1, -+ .channels_max = 2, -+ .rates = SNDRV_PCM_RATE_8000_192000 -+ | SNDRV_PCM_RATE_KNOT, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE -+ | SNDRV_PCM_FMTBIT_S24_LE -+ | SNDRV_PCM_FMTBIT_S32_LE, -+ }, -+ -+ .capture = { -+ .stream_name = "Capture", -+ .channels_min = 1, -+ .channels_max = 2, -+ .rates = SNDRV_PCM_RATE_8000_192000 -+ | SNDRV_PCM_RATE_KNOT, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE -+ | SNDRV_PCM_FMTBIT_S24_LE -+ | SNDRV_PCM_FMTBIT_S32_LE, -+ }, -+ -+ .ops = &acx00_codec_dai_ops, -+ }, -+}; -+ -+static void acx00_codec_resume_work(struct work_struct *work) -+{ -+ struct acx00_priv *priv = container_of(work, -+ struct acx00_priv, resume_work.work); -+ -+ acx00_codec_init(priv->component); -+} -+ -+static int acx00_codec_probe(struct snd_soc_component *component) -+{ -+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component); -+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); -+ int ret = 0; -+ -+ mutex_init(&priv->mutex); -+ -+ priv->component = component; -+#if 0 -+ /* Add virtual switch */ -+ ret = snd_soc_add_component_controls(component, acx00_codec_controls, -+ ARRAY_SIZE(acx00_codec_controls)); -+ if (ret) { -+ pr_err("[audio-codec] Failed to register audio mode control, will continue without it.\n"); -+ } -+ snd_soc_dapm_new_controls(dapm, acx00_codec_dapm_widgets, ARRAY_SIZE(acx00_codec_dapm_widgets)); -+ snd_soc_dapm_add_routes(dapm, acx00_codec_dapm_routes, ARRAY_SIZE(acx00_codec_dapm_routes)); -+#endif -+ /* using late_initcall to wait 120ms acx00-core to make chip reset */ -+ acx00_codec_init(component); -+ INIT_DELAYED_WORK(&priv->spk_work, acx00_spk_enable); -+ INIT_DELAYED_WORK(&priv->resume_work, acx00_codec_resume_work); -+ return 0; -+} -+ -+static void acx00_codec_remove(struct snd_soc_component *component) -+{ -+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component); -+ -+ cancel_delayed_work_sync(&priv->spk_work); -+ cancel_delayed_work_sync(&priv->resume_work); -+} -+ -+static unsigned int acx00_codec_read(struct snd_soc_component *component, -+ unsigned int reg) -+{ -+ unsigned int data; -+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component); -+ -+ /* Device I/O API */ -+ data = acx00_reg_read(priv->acx00, reg); -+ return data; -+} -+ -+static int acx00_codec_write(struct snd_soc_component *component, -+ unsigned int reg, unsigned int value) -+{ -+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component); -+ -+ return acx00_reg_write(priv->acx00, reg, value); -+} -+ -+static int sunxi_gpio_iodisable(u32 gpio) -+{ -+ char pin_name[8]; -+ u32 config, ret; -+#if 0 -+ sunxi_gpio_to_name(gpio, pin_name); -+ config = 7 << 16; -+ ret = pin_config_set(SUNXI_PINCTRL, pin_name, config); -+#endif -+ return ret; -+} -+ -+static int acx00_codec_suspend(struct snd_soc_component *component) -+{ -+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component); -+ -+ pr_debug("Enter %s\n", __func__); -+ -+ clk_disable_unprepare(priv->clk); -+ -+ /* PA_CTRL first setting low state, then make it iodisabled */ -+ if (priv->spk_gpio_used) { -+ sunxi_gpio_iodisable(priv->spk_gpio); -+ msleep(30); -+ } -+ -+ /* -+ * when codec suspend, then the register reset, if auto reset produce -+ * Pop & Click noise, then we should cut down the LINEOUT in this town. -+ */ -+ if (priv->enable) { -+ snd_soc_component_update_bits(component, AC_LINEOUT_CTL, -+ (1<component, AC_LINEOUT_CTL, -+ (1<component, AC_LINEOUT_CTL, -+ (1<enable = 0; -+ } -+ -+ pr_debug("Exit %s\n", __func__); -+ -+ return 0; -+} -+ -+static int acx00_codec_resume(struct snd_soc_component *component) -+{ -+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component); -+ -+ pr_debug("Enter %s\n", __func__); -+ -+ if (clk_prepare_enable(priv->clk)) { -+ dev_err(component->dev, "codec resume clk failed\n"); -+ return -EBUSY; -+ } -+ -+ schedule_delayed_work(&priv->resume_work, msecs_to_jiffies(300)); -+ -+ if (priv->spk_gpio_used) { -+ gpio_direction_output(priv->spk_gpio, 1); -+ gpio_set_value(priv->spk_gpio, 0); -+ } -+ -+ pr_debug("Exit %s\n", __func__); -+ -+ return 0; -+} -+ -+ -+static int acx00_codec_set_bias_level(struct snd_soc_component *component, -+ enum snd_soc_bias_level level) -+{ -+ component->dapm.bias_level = level; -+ return 0; -+} -+ -+struct label { -+ const char *name; -+ int value; -+}; -+ -+#define LABEL(constant) { #constant, constant } -+#define LABEL_END { NULL, -1 } -+ -+static struct label reg_labels[] = { -+ LABEL(AC_SYS_CLK_CTL), -+ LABEL(AC_SYS_MOD_RST), -+ LABEL(AC_SYS_SR_CTL), -+ LABEL(AC_I2S_CTL), -+ LABEL(AC_I2S_CLK), -+ LABEL(AC_I2S_FMT0), -+ LABEL(AC_I2S_FMT1), -+ LABEL(AC_I2S_MIXER_SRC), -+ LABEL(AC_I2S_MIXER_GAIN), -+ LABEL(AC_I2S_DAC_VOL), -+ LABEL(AC_I2S_ADC_VOL), -+ LABEL(AC_DAC_CTL), -+ LABEL(AC_DAC_MIXER_SRC), -+ LABEL(AC_DAC_MIXER_GAIN), -+ LABEL(AC_OUT_MIXER_CTL), -+ LABEL(AC_OUT_MIXER_SRC), -+ LABEL(AC_LINEOUT_CTL), -+ LABEL(AC_ADC_CTL), -+ LABEL(AC_MICBIAS_CTL), -+ LABEL(AC_ADC_MIC_CTL), -+ LABEL(AC_ADC_MIXER_SRC), -+ LABEL(AC_BIAS_CTL), -+ LABEL(AC_ANALOG_PROF_CTL), -+ LABEL_END, -+}; -+ -+static ssize_t show_audio_reg(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct acx00_priv *priv = dev_get_drvdata(dev); -+ int count = 0, i = 0; -+ unsigned int reg_val; -+ -+ count += sprintf(buf, "dump audio reg:\n"); -+ -+ while (reg_labels[i].name != NULL) { -+ reg_val = acx00_reg_read(priv->acx00, reg_labels[i].value); -+ count += sprintf(buf + count, "%s 0x%x: 0x%x\n", -+ reg_labels[i].name, (reg_labels[i].value), reg_val); -+ i++; -+ } -+ -+ return count; -+} -+ -+/* -+ * param 1: 0 read;1 write -+ * param 2: 1 digital reg; 2 analog reg -+ * param 3: reg value; -+ * param 4: write value; -+ * read: -+ * echo 0,1,0x00> audio_reg -+ * echo 0,2,0x00> audio_reg -+ * write: -+ * echo 1,1,0x00,0xa > audio_reg -+ * echo 1,2,0x00,0xff > audio_reg -+*/ -+static ssize_t store_audio_reg(struct device *dev, -+ struct device_attribute *attr, const char *buf, size_t count) -+{ -+ int ret; -+ int rw_flag; -+ unsigned int input_reg_val = 0; -+ int input_reg_group = 0; -+ unsigned int input_reg_offset = 0; -+ struct acx00_priv *priv = dev_get_drvdata(dev); -+ -+ ret = sscanf(buf, "%d,%d,0x%x,0x%x", &rw_flag, &input_reg_group, -+ &input_reg_offset, &input_reg_val); -+ dev_info(dev, "ret:%d, reg_group:%d, reg_offset:%d, reg_val:0x%x\n", -+ ret, input_reg_group, input_reg_offset, input_reg_val); -+ -+ if (input_reg_group != 1) { -+ pr_err("not exist reg group\n"); -+ ret = count; -+ goto out; -+ } -+ if (!(rw_flag == 1 || rw_flag == 0)) { -+ pr_err("not rw_flag\n"); -+ ret = count; -+ goto out; -+ } -+ -+ if (rw_flag) { -+ acx00_reg_write(priv->acx00, input_reg_offset, input_reg_val); -+ } else { -+ input_reg_val = acx00_reg_read(priv->acx00, input_reg_offset); -+ dev_info(dev, "\n\n Reg[0x%x] : 0x%04x\n\n", -+ input_reg_offset, input_reg_val); -+ } -+ ret = count; -+ -+out: -+ return ret; -+} -+ -+static DEVICE_ATTR(audio_reg, 0644, show_audio_reg, store_audio_reg); -+ -+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 struct snd_soc_component_driver soc_codec_driver_acx00 = { -+ .probe = acx00_codec_probe, -+ .remove = acx00_codec_remove, -+ .suspend = acx00_codec_suspend, -+ .resume = acx00_codec_resume, -+ .read = acx00_codec_read, -+ .write = acx00_codec_write, -+// .ignore_pmdown_time = 1, -+ .set_bias_level = acx00_codec_set_bias_level, -+ .controls = acx00_codec_controls, -+ .num_controls = ARRAY_SIZE(acx00_codec_controls), -+ .dapm_widgets = acx00_codec_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(acx00_codec_dapm_widgets), -+ .dapm_routes = acx00_codec_dapm_routes, -+ .num_dapm_routes = ARRAY_SIZE(acx00_codec_dapm_routes), -+}; -+ -+/* through acx00 is part of mfd devices, after the mfd */ -+static int acx00_codec_dev_probe(struct platform_device *pdev) -+{ -+ struct acx00_priv *priv; -+ int ret; -+ struct device_node *np = of_find_compatible_node(NULL, NULL, "allwinner,ac200_codec"); -+ -+ priv = devm_kzalloc(&pdev->dev, sizeof(struct acx00_priv), GFP_KERNEL); -+ if (!priv) { -+ dev_err(&pdev->dev, "acx00 codec priv mem alloc failed\n"); -+ return -ENOMEM; -+ } -+ -+ platform_set_drvdata(pdev, priv); -+ priv->acx00 = dev_get_drvdata(pdev->dev.parent); -+ -+ if (np) { -+ ret = of_get_named_gpio(np, "gpio-spk", 0); -+ if (ret >= 0) { -+ priv->spk_gpio_used = 1; -+ priv->spk_gpio = ret; -+ if (!gpio_is_valid(priv->spk_gpio)) { -+ dev_err(&pdev->dev, "gpio-spk is valid\n"); -+ ret = -EINVAL; -+ goto err_devm_kfree; -+ } else { -+ ret = devm_gpio_request(&pdev->dev, -+ priv->spk_gpio, "SPK"); -+ if (ret) { -+ dev_err(&pdev->dev, -+ "failed request gpio-spk\n"); -+ ret = -EBUSY; -+ goto err_devm_kfree; -+ } else { -+ gpio_direction_output(priv->spk_gpio, -+ 1); -+ gpio_set_value(priv->spk_gpio, 0); -+ } -+ } -+ } else { -+ priv->spk_gpio_used = 0; -+ } -+ -+ ret = of_get_named_gpio(np, "gpio-switch", 0); -+ if (ret >= 0) { -+ priv->switch_gpio = ret; -+ if (!gpio_is_valid(priv->switch_gpio)) { -+ dev_err(&pdev->dev, "gpio-switch is valid\n"); -+ ret = -EINVAL; -+ goto err_devm_kfree; -+ } else { -+ ret = devm_gpio_request(&pdev->dev, priv->switch_gpio, "SWITCH"); -+ if (ret) { -+ dev_err(&pdev->dev, -+ "failed request gpio-switch\n"); -+ ret = -EBUSY; -+ goto err_devm_kfree; -+ } else { -+ gpio_direction_output(priv->switch_gpio, 1); -+ gpio_set_value(priv->switch_gpio, 1); -+ } -+ } -+ } -+ } -+ -+ ret = snd_soc_register_component(&pdev->dev, &soc_codec_driver_acx00, -+ acx00_codec_dai, ARRAY_SIZE(acx00_codec_dai)); -+ -+ if (ret < 0) -+ dev_err(&pdev->dev, "Failed register acx00: %d\n", ret); -+ -+ ret = sysfs_create_group(&pdev->dev.kobj, &audio_debug_attr_group); -+ if (ret) -+ dev_warn(&pdev->dev, "failed to create attr group\n"); -+ -+ return 0; -+ -+err_devm_kfree: -+ devm_kfree(&pdev->dev, priv); -+ return ret; -+} -+ -+/* Mark this space to clear the LINEOUT & gpio */ -+static void acx00_codec_dev_shutdown(struct platform_device *pdev) -+{ -+ struct acx00_priv *priv = platform_get_drvdata(pdev); -+ -+ if (priv->spk_gpio_used) -+ gpio_set_value(priv->spk_gpio, 0); -+} -+ -+static int acx00_codec_dev_remove(struct platform_device *pdev) -+{ -+ struct acx00_priv *priv = platform_get_drvdata(pdev); -+ -+#ifndef ACX00_DAPM_LINEOUT -+ /* -+ snd_soc_component_update_bits(priv->component, AC_LINEOUT_CTL, -+ (1<dev); -+ clk_disable_unprepare(priv->clk); -+ devm_kfree(&pdev->dev, priv); -+ return 0; -+} -+ -+static const struct of_device_id acx00_codec_match[] = { -+ { .compatible = "x-powers,ac200-codec" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, acx00_codec_match); -+ -+static struct platform_driver acx00_codec_driver = { -+ .driver = { -+ .name = "acx00-codec", -+ .of_match_table = acx00_codec_match, -+ }, -+ .probe = acx00_codec_dev_probe, -+ .remove = acx00_codec_dev_remove, -+ .shutdown = acx00_codec_dev_shutdown, -+}; -+ -+static int __init acx00_codec_driver_init(void) -+{ -+ return platform_driver_register(&acx00_codec_driver); -+} -+ -+static void __exit acx00_codec_driver_exit(void) -+{ -+ platform_driver_unregister(&acx00_codec_driver); -+} -+late_initcall(acx00_codec_driver_init); -+module_exit(acx00_codec_driver_exit); -+ -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("SUNXI ASoC ACX00 Codec Driver"); -+MODULE_AUTHOR("wolfgang huang"); -+MODULE_ALIAS("platform:acx00-codec"); -diff --git a/sound/soc/codecs/acx00.h b/sound/soc/codecs/acx00.h -new file mode 100644 -index 000000000..5137cf365 ---- /dev/null -+++ b/sound/soc/codecs/acx00.h -@@ -0,0 +1,356 @@ -+/* -+ * sound\soc\codecs\acx00.h -+ * (C) Copyright 2012-2016 -+ * Allwinner Technology Co., Ltd. -+ * Wolfgang Huang -+ * -+ * some simple description for this code -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation; either version 2 of -+ * the License, or (at your option) any later version. -+ * -+ */ -+ -+#ifndef __ACX00_H_ -+#define __ACX00_H_ -+ -+/* ACX00 register offset list */ -+#define AC_SYS_CLK_CTL 0x2000 -+#define AC_SYS_MOD_RST 0x2002 -+#define AC_SYS_SR_CTL 0x2004 -+/* Left blank */ -+#define AC_I2S_CTL 0x2100 -+#define AC_I2S_CLK 0x2102 -+#define AC_I2S_FMT0 0x2104 -+/* Left blank */ -+#define AC_I2S_FMT1 0x2108 -+/* Left blank */ -+#define AC_I2S_MIXER_SRC 0x2114 -+#define AC_I2S_MIXER_GAIN 0x2116 -+#define AC_I2S_DAC_VOL 0x2118 -+#define AC_I2S_ADC_VOL 0x211A -+/* Left blank */ -+#define AC_DAC_CTL 0x2200 -+#define AC_DAC_MIXER_SRC 0x2202 -+#define AC_DAC_MIXER_GAIN 0x2204 -+/* Left blank */ -+#define AC_OUT_MIXER_CTL 0x2220 -+#define AC_OUT_MIXER_SRC 0x2222 -+#define AC_LINEOUT_CTL 0x2224 -+/* Left blank */ -+#define AC_ADC_CTL 0x2300 -+/* Left blank */ -+#define AC_MICBIAS_CTL 0x2310 -+/* Left blank */ -+#define AC_ADC_MIC_CTL 0x2320 -+#define AC_ADC_MIXER_SRC 0x2322 -+/* Left blank */ -+#define AC_BIAS_CTL 0x232A -+#define AC_ANALOG_PROF_CTL 0x232C -+/* Left blank */ -+#define AC_ADC_DAPL_CTRL 0x2500 -+#define AC_ADC_DAPR_CTRL 0x2502 -+#define AC_ADC_DAPLSTA 0x2504 -+#define AC_ADC_DAPRSTA 0x2506 -+#define AC_ADC_DAP_LTL 0x2508 -+#define AC_ADC_DAP_RTL 0x250A -+#define AC_ADC_DAP_LHAC 0x250C -+#define AC_ADC_DAP_LLAC 0x250E -+#define AC_ADC_DAP_RHAC 0x2510 -+#define AC_ADC_DAP_RLAC 0x2512 -+#define AC_ADC_DAP_LDT 0x2514 -+#define AC_ADC_DAP_LAT 0x2516 -+#define AC_ADC_DAP_RDT 0x2518 -+#define AC_ADC_DAP_RAT 0x251A -+#define AC_ADC_DAP_NTH 0x251C -+#define AC_ADC_DAP_LHNAC 0x251E -+#define AC_ADC_DAP_LLNAC 0x2520 -+#define AC_ADC_DAP_RHNAC 0x2522 -+#define AC_ADC_DAP_RLNAC 0x2524 -+#define AC_ADC_DAP_HHPFC 0x2526 -+#define AC_ADC_DAP_LHPFC 0x2528 -+#define AC_ADC_DAP_OPT 0x252A -+/* Left blank */ -+#define AC_AGC_SEL 0x2480 -+/* Left blank */ -+#define AC_ADC_DAPL_CTRL 0x2500 -+#define AC_ADC_DAPR_CTRL 0x2502 -+#define AC_ADC_DAPLSTA 0x2504 -+#define AC_ADC_DAPRSTA 0x2506 -+#define AC_ADC_DAP_LTL 0x2508 -+#define AC_ADC_DAP_RTL 0x250A -+#define AC_ADC_DAP_LHAC 0x250C -+#define AC_ADC_DAP_LLAC 0x250E -+#define AC_ADC_DAP_RHAC 0x2510 -+#define AC_ADC_DAP_RLAC 0x2512 -+#define AC_ADC_DAP_LDT 0x2514 -+#define AC_ADC_DAP_LAT 0x2516 -+#define AC_ADC_DAP_RDT 0x2518 -+#define AC_ADC_DAP_RAT 0x251A -+#define AC_ADC_DAP_NTH 0x251C -+#define AC_ADC_DAP_LHNAC 0x251E -+#define AC_ADC_DAP_LLNAC 0x2520 -+#define AC_ADC_DAP_RHNAC 0x2522 -+#define AC_ADC_DAP_RLNAC 0x2524 -+#define AC_ADC_DAP_HHPFC 0x2526 -+#define AC_ADC_DAP_LHPFC 0x2528 -+#define AC_ADC_DAP_OPT 0x252A -+/* Left blank */ -+#define AC_DRC_SEL 0x2f80 -+/* Left blank */ -+#define AC_DRC_CHAN_CTRL 0x3000 -+#define AC_DRC_HHPFC 0x3002 -+#define AC_DRC_LHPFC 0x3004 -+#define AC_DRC_CTRL 0x3006 -+#define AC_DRC_LPFHAT 0x3008 -+#define AC_DRC_LPFLAT 0x300A -+#define AC_DRC_RPFHAT 0x300C -+#define AC_DRC_RPFLAT 0x300E -+#define AC_DRC_LPFHRT 0x3010 -+#define AC_DRC_LPFLRT 0x3012 -+#define AC_DRC_RPFHRT 0x3014 -+#define AC_DRC_RPFLRT 0x3016 -+#define AC_DRC_LRMSHAT 0x3018 -+#define AC_DRC_LRMSLAT 0x301A -+#define AC_DRC_RRMSHAT 0x301C -+#define AC_DRC_RRMSLAT 0x301E -+#define AC_DRC_HCT 0x3020 -+#define AC_DRC_LCT 0x3022 -+#define AC_DRC_HKC 0x3024 -+#define AC_DRC_LKC 0x3026 -+#define AC_DRC_HOPC 0x3028 -+#define AC_DRC_LOPC 0x302A -+#define AC_DRC_HLT 0x302C -+#define AC_DRC_LLT 0x302E -+#define AC_DRC_HKI 0x3030 -+#define AC_DRC_LKI 0x3032 -+#define AC_DRC_HOPL 0x3034 -+#define AC_DRC_LOPL 0x3036 -+#define AC_DRC_HET 0x3038 -+#define AC_DRC_LET 0x303A -+#define AC_DRC_HKE 0x303C -+#define AC_DRC_LKE 0x303E -+#define AC_DRC_HOPE 0x3040 -+#define AC_DRC_LOPE 0x3042 -+#define AC_DRC_HKN 0x3044 -+#define AC_DRC_LKN 0x3046 -+#define AC_DRC_SFHAT 0x3048 -+#define AC_DRC_SFLAT 0x304A -+#define AC_DRC_SFHRT 0x304C -+#define AC_DRC_SFLRT 0x304E -+#define AC_DRC_MXGHS 0x3050 -+#define AC_DRC_MXGLS 0x3052 -+#define AC_DRC_MNGHS 0x3054 -+#define AC_DRC_MNGLS 0x3056 -+#define AC_DRC_EPSHC 0x3058 -+#define AC_DRC_EPSLC 0x305A -+#define AC_DRC_OPT 0x305C -+#define AC_DRC_HPFHGAIN 0x305E -+#define AC_DRC_HPFLGAIN 0x3060 -+#define AC_DRC_BISTCR 0x3100 -+#define AC_DRC_BISTST 0x3102 -+ -+/* AC_SYS_CLK_CTL : 0x2000 */ -+#define SYS_CLK_I2S 15 -+#define SYS_CLK_AGC 7 -+#define SYS_CLK_DRC 6 -+#define SYS_CLK_ADC 3 -+#define SYS_CLK_DAC 2 -+ -+/* AC_SYS_MOD_RST : 0x2002 */ -+#define MOD_RST_I2S 15 -+#define MOD_RST_AGC 7 -+#define MOD_RST_DRC 6 -+#define MOD_RST_ADC 3 -+#define MOD_RST_DAC 2 -+ -+/* AC_SYS_SR_CTL : 0x2004 */ -+#define SYS_SR_BIT 0 -+#define SYS_SR_MASK 0xF -+#define SYS_SR_BIT_0 0 /* 8000 */ -+#define SYS_SR_BIT_1 1 /* 11025 */ -+#define SYS_SR_BIT_2 2 /* 12000 */ -+#define SYS_SR_BIT_3 3 /* 16000 */ -+#define SYS_SR_BIT_4 4 /* 22050 */ -+#define SYS_SR_BIT_5 5 /* 24000 */ -+#define SYS_SR_BIT_6 6 /* 32000 */ -+#define SYS_SR_BIT_7 7 /* 44100 */ -+#define SYS_SR_BIT_8 8 /* 48000 */ -+#define SYS_SR_BIT_9 9 /* 96000 */ -+#define SYS_SR_BIT_10 10 /* 192000 */ -+ -+/* AC_I2S_CTL : 0x2100 */ -+#define I2S_SDO0_EN 3 -+#define I2S_TX_EN 2 -+#define I2S_RX_EN 1 -+#define I2S_GEN 0 -+ -+/* AC_I2S_CLK : 0x2102 */ -+#define I2S_BCLK_OUT 15 -+#define I2S_LRCK_OUT 14 -+#define I2S_BLCK_DIV 10 -+#define I2S_LRCK_PERIOD 0 -+/* BCLK DIV Define */ -+#define I2S_BCLK_DIV_MASK 0xF -+#define I2S_BCLK_DIV_1 1 -+#define I2S_BCLK_DIV_2 2 -+#define I2S_BCLK_DIV_3 3 -+#define I2S_BCLK_DIV_4 4 -+#define I2S_BCLK_DIV_5 5 -+#define I2S_BCLK_DIV_6 6 -+#define I2S_BCLK_DIV_7 7 -+#define I2S_BCLK_DIV_8 8 -+#define I2S_BCLK_DIV_9 9 -+#define I2S_BCLK_DIV_10 10 -+#define I2S_BCLK_DIV_11 11 -+#define I2S_BCLK_DIV_12 12 -+#define I2S_BCLK_DIV_13 13 -+#define I2S_BCLK_DIV_14 14 -+#define I2S_BCLK_DIV_15 15 -+#define I2S_LRCK_PERIOD_MASK 0x3FF -+ -+/* AC_I2S_FMT0 : 0x2104 */ -+#define I2S_FMT_MODE 14 -+#define I2S_FMT_TX_OFFSET 10 -+#define I2S_FMT_RX_OFFSET 8 -+#define I2S_FMT_SAMPLE 4 -+#define I2S_FMT_SLOT_WIDTH 1 -+#define I2S_FMT_LOOP 0 -+ -+/* AC_I2S_FMT1 : 0x2108 */ -+#define I2S_FMT_BCLK_POLAR 15 -+#define I2S_FMT_LRCK_POLAR 14 -+#define I2S_FMT_EDGE_TRANSFER 13 -+#define I2S_FMT_RX_MLS 11 -+#define I2S_FMT_TX_MLS 10 -+#define I2S_FMT_EXTEND 9 -+#define I2S_FMT_LRCK_WIDTH 4 /* PCM long/short Frame */ -+#define I2S_MFT_RX_PDM 2 -+#define I2S_FMT_TX_PDM 0 -+ -+/* AC_I2S_MIXER_SRC : 0x2114 */ -+#define I2S_MIXERL_SRC_DAC 13 -+#define I2S_MIXERL_SRC_ADC 12 -+#define I2S_MIXERR_SRC_DAC 9 -+#define I2S_MIXERR_SRC_ADC 8 -+ -+/* AC_I2S_MIXER_GAIN : 0x2116 */ -+#define I2S_MIXERL_GAIN_DAC 13 -+#define I2S_MIXERL_GAIN_ADC 12 -+#define I2S_MIXERR_GAIN_DAC 9 -+#define I2S_MIXERR_GAIN_ADC 8 -+ -+ -+/* AC_I2S_DAC_VOL : 0x2118 */ -+#define I2S_DACL_VOL 8 -+#define I2S_DACR_VOL 0 -+ -+/* AC_I2S_ADC_VOL : 0x211A */ -+#define I2S_ADCL_VOL 8 -+#define I2S_ADCR_VOL 0 -+ -+/* AC_DAC_CTL : 0x2200 */ -+#define DAC_CTL_DAC_EN 15 -+#define DAC_CTL_HPF_EN 14 -+#define DAC_CTL_FIR 13 -+#define DAC_CTL_MODQU 8 -+ -+/* AC_DAC_MIXER_SRC : 0x2202 */ -+#define DAC_MIXERL_SRC_DAC 13 -+#define DAC_MIXERL_SRC_ADC 12 -+#define DAC_MIXERR_SRC_DAC 9 -+#define DAC_MIXERR_SRC_ADC 8 -+ -+/* AC_DAC_MIXER_GAIN : 0x2204 */ -+#define DAC_MIXERL_GAIN_DAC 13 -+#define DAC_MIXERL_GAIN_ADC 12 -+#define DAC_MIXERR_GAIN_DAC 9 -+#define DAC_MIXERR_GAIN_ADC 8 -+ -+/* AC_OUT_MIXER_CTL : 0x2220 */ -+#define OUT_MIXER_DACR_EN 15 -+#define OUT_MIXER_DACL_EN 14 -+#define OUT_MIXER_RMIX_EN 13 -+#define OUT_MIXER_LMIX_EN 12 -+#define OUT_MIXER_LINE_VOL 8 -+#define OUT_MIXER_MIC1_VOL 4 -+#define OUT_MIXER_MIC2_VOL 0 -+ -+/* AC_OUT_MIXER_SRC : 0x2222 */ -+#define OUT_MIXERR_SRC_MIC1 14 -+#define OUT_MIXERR_SRC_MIC2 13 -+#define OUT_MIXERR_SRC_PHPN 12 -+#define OUT_MIXERR_SRC_PHP 11 -+#define OUT_MIXERR_SRC_LINER 10 -+#define OUT_MIXERR_SRC_DACR 9 -+#define OUT_MIXERR_SRC_DACL 8 -+#define OUT_MIXERL_SRC_MIC1 6 -+#define OUT_MIXERL_SRC_MIC2 5 -+#define OUT_MIXERL_SRC_PHPN 4 -+#define OUT_MIXERL_SRC_PHN 3 -+#define OUT_MIXERL_SRC_LINEL 2 -+#define OUT_MIXERL_SRC_DACL 1 -+#define OUT_MIXERL_SRC_DACR 0 -+ -+/* AC_LINEOUT_CTL : 0x2224 */ -+#define LINEOUT_EN 15 -+#define LINEL_SRC_EN 14 -+#define LINER_SRC_EN 13 -+#define LINEL_SRC 12 -+#define LINER_SRC 11 -+/* ramp just skip */ -+#define LINE_SLOPE_SEL 8 -+#define LINE_ANTI_TIME 5 -+#define LINEOUT_VOL 0 -+ -+/* AC_ADC_CTL : 0x2300 */ -+#define ADC_EN 15 -+#define ADC_ENDM 14 -+#define ADC_FIR 13 -+#define ADC_DELAY_TIME 2 -+#define ADC_DELAY_EN 1 -+ -+/* AC_MICBIAS_CTL : 0x2310 */ -+#define MMBIAS_EN 15 -+#define MMBIAS_CHOPPER 14 -+#define MMBIAS_CHOP_CLK 12 -+#define MMBIAS_SEL 8 -+#define ADDA_BIAS_CUR 3 -+ -+/* AC_ADC_MIC_CTL : 0x2320 */ -+#define ADCR_EN 15 -+#define ADCL_EN 14 -+#define ADC_GAIN 8 -+#define MIC1_GAIN_EN 7 -+#define MIC1_BOOST 4 -+#define MIC2_GAIN_EN 3 -+#define MIC2_BOOST 0 -+ -+/* AC_ADC_MIXER_SRC : 0x2322 */ -+#define ADC_MIXERR_MIC1 14 -+#define ADC_MIXERR_MIC2 13 -+#define ADC_MIXERR_PHPN 12 -+#define ADC_MIXERR_PHP 11 -+#define ADC_MIXERR_LINER 10 -+#define ADC_MIXERR_MIXR 9 -+#define ADC_MIXERR_MIXL 8 -+#define ADC_MIXERL_MIC1 6 -+#define ADC_MIXERL_MIC2 5 -+#define ADC_MIXERL_PHPN 4 -+#define ADC_MIXERL_PHN 3 -+#define ADC_MIXERL_LINEL 2 -+#define ADC_MIXERL_MIXL 1 -+#define ADC_MIXERL_MIXR 0 -+ -+/* AC_BIAS_CTL : 0x232A */ -+ -+/* AC_ANALOG_PROF_CTL : 0x232C */ -+/* used for current performance measure */ -+ -+/* AC_DLDO_OSC_CTL : 0x2340 */ -+/* AC_ALDO_CTL : 0x2342 */ -+/* used for digital & analog LDO test... etc */ -+ -+#endif --- -2.35.3 - diff --git a/patch/kernel/archive/sunxi-6.1/patches.armbian/arm64-dts-sun50i-h6-Add-AC200-EPHY-related-nodes.patch b/patch/kernel/archive/sunxi-6.5/patches.armbian/arm64-dts-allwinner-h6-Add-AC200-EPHY-nodes.patch similarity index 53% rename from patch/kernel/archive/sunxi-6.1/patches.armbian/arm64-dts-sun50i-h6-Add-AC200-EPHY-related-nodes.patch rename to patch/kernel/archive/sunxi-6.5/patches.armbian/arm64-dts-allwinner-h6-Add-AC200-EPHY-nodes.patch index 0aac50b26..b1596978b 100644 --- a/patch/kernel/archive/sunxi-6.1/patches.armbian/arm64-dts-sun50i-h6-Add-AC200-EPHY-related-nodes.patch +++ b/patch/kernel/archive/sunxi-6.5/patches.armbian/arm64-dts-allwinner-h6-Add-AC200-EPHY-nodes.patch @@ -1,18 +1,30 @@ -From 87349f8edd636a63897629b4b7698893d55d127a Mon Sep 17 00:00:00 2001 +From d1c807041c254b02e944bf12b8d0ea39953ffdd6 Mon Sep 17 00:00:00 2001 From: Jernej Skrabec -Date: Sun, 12 Jan 2020 12:09:12 +0100 -Subject: [PATCH 072/158] arm64:dts: sun50i-h6: Add AC200 EPHY related nodes +Date: Fri, 16 Aug 2019 16:40:20 +0200 +Subject: [PATCH] arm64: dts: allwinner: h6: Add AC200 EPHY nodes + +All Allwinner H6 SoCs feature a co-packaged AC200 die, which replaces +the integrated PHY and audio circuitry of its H3/H5 predecessors. It is +using an internal I2C connection, but otherwise pretty much behaves as +it would be externally connected. + +Since every H6 SoC contains this chip, add the required DT nodes to the +SoC .dtsi, but keep them disabled. This is for now just covering the +AC200 MFD parent and its EPHY child. +Any board making use of one of the integrated PHY needs to enable it and +connect the MAC and PHY accordingly. Signed-off-by: Jernej Skrabec +Signed-off-by: Andre Przywara --- - arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 63 ++++++++++++++++++++ - 1 file changed, 63 insertions(+) + arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 73 ++++++++++++++++++++ + 1 file changed, 73 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi -index 95f242ff9..80d7f4854 100644 +index 9eee1a1e189d..b1f3724b42ca 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi -@@ -23,6 +23,16 @@ aliases { +@@ -24,6 +24,16 @@ aliases { mmc2 = &mmc2; }; @@ -29,22 +41,25 @@ index 95f242ff9..80d7f4854 100644 cpus { #address-cells = <1>; #size-cells = <0>; -@@ -319,6 +329,10 @@ ths_calibration: thermal-sensor-calibration@14 { - reg = <0x14 0x8>; - }; - -+ ephy_calibration: ephy-calibration@2c { -+ reg = <0x2c 0x2>; -+ }; -+ +@@ -334,6 +344,14 @@ ths_calibration: thermal-sensor-calibration@14 { cpu_speed_grade: cpu-speed-grade@1c { reg = <0x1c 0x4>; }; -@@ -376,6 +390,14 @@ ext_rgmii_pins: rgmii-pins { ++ ++ ephy_calib: ephy_calib@2c { ++ reg = <0x2c 0x2>; ++ }; ++ ++ ac200_bg: ac200_bg@30 { ++ reg = <0x30 0x2>; ++ }; + }; + + timer@3009000 { +@@ -389,6 +407,13 @@ ext_rgmii_pins: rgmii-pins { drive-strength = <40>; }; -+ /omit-if-no-ref/ + ext_rmii_pins: rmii_pins { + pins = "PA0", "PA1", "PA2", "PA3", "PA4", + "PA5", "PA6", "PA7", "PA8", "PA9"; @@ -55,19 +70,20 @@ index 95f242ff9..80d7f4854 100644 hdmi_pins: hdmi-pins { pins = "PH8", "PH9", "PH10"; function = "hdmi"; -@@ -396,6 +418,11 @@ i2c2_pins: i2c2-pins { +@@ -409,6 +434,12 @@ i2c2_pins: i2c2-pins { function = "i2c2"; }; + i2c3_pins: i2c3-pins { + pins = "PB17", "PB18"; + function = "i2c3"; ++ bias-pull-up; + }; + mmc0_pins: mmc0-pins { pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5"; -@@ -413,6 +440,11 @@ mmc1_pins: mmc1-pins { +@@ -436,6 +467,11 @@ mmc2_pins: mmc2-pins { bias-pull-up; }; @@ -76,10 +92,10 @@ index 95f242ff9..80d7f4854 100644 + function = "pwm1"; + }; + - mmc2_pins: mmc2-pins { - pins = "PC1", "PC4", "PC5", "PC6", - "PC7", "PC8", "PC9", "PC10", -@@ -655,6 +687,37 @@ spi1: spi@5011000 { + /omit-if-no-ref/ + spi0_pins: spi0-pins { + pins = "PC0", "PC2", "PC3"; +@@ -647,6 +683,43 @@ i2c2: i2c@5002800 { #size-cells = <0>; }; @@ -92,6 +108,7 @@ index 95f242ff9..80d7f4854 100644 + resets = <&ccu RST_BUS_I2C3>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c3_pins>; ++ clock-frequency = <100000>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; @@ -99,24 +116,29 @@ index 95f242ff9..80d7f4854 100644 + ac200: mfd@10 { + compatible = "x-powers,ac200"; + reg = <0x10>; ++ clocks = <&ac200_pwm_clk>; + interrupt-parent = <&pio>; + interrupts = <1 20 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + #interrupt-cells = <1>; ++ nvmem-cells = <&ac200_bg>; ++ nvmem-cell-names = "bandgap"; + -+ ac200_ephy: phy { -+ compatible = "x-powers,ac200-ephy"; -+ clocks = <&ac200_pwm_clk>; -+ nvmem-cells = <&ephy_calibration>; ++ ac200_ephy_ctl: syscon { ++ compatible = "x-powers,ac200-ephy-ctl"; ++ nvmem-cells = <&ephy_calib>; + nvmem-cell-names = "calibration"; ++ #clock-cells = <0>; ++ #reset-cells = <0>; ++ phy-mode = "rmii"; + status = "disabled"; + }; + }; + }; + - emac: ethernet@5020000 { - compatible = "allwinner,sun50i-h6-emac", - "allwinner,sun50i-a64-emac"; + spi0: spi@5010000 { + compatible = "allwinner,sun50i-h6-spi", + "allwinner,sun8i-h3-spi"; -- -2.35.3 +2.34.1 diff --git a/patch/kernel/archive/sunxi-6.5/patches.armbian/arm64-dts-allwinner-h6-add-AC200-codec-nodes.patch b/patch/kernel/archive/sunxi-6.5/patches.armbian/arm64-dts-allwinner-h6-add-AC200-codec-nodes.patch new file mode 100644 index 000000000..6a64751b7 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.5/patches.armbian/arm64-dts-allwinner-h6-add-AC200-codec-nodes.patch @@ -0,0 +1,87 @@ +From a809376f8af7cab04996585f9f68e1f6cf1afd73 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Thu, 1 Sep 2022 17:45:03 +0200 +Subject: [PATCH] arm64: dts: allwinner: h6: add AC200 codec nodes + +Signed-off-by: Jernej Skrabec +--- + arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 42 ++++++++++++++++++++ + 1 file changed, 42 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +index b1f3724b42ca..e1c6673da881 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +@@ -24,6 +24,22 @@ aliases { + mmc2 = &mmc2; + }; + ++ analog: analog-codec { ++ compatible = "simple-audio-card"; ++ simple-audio-card,format = "i2s"; ++ simple-audio-card,name = "ac200-audio"; ++ simple-audio-card,mclk-fs = <512>; ++ status = "disabled"; ++ ++ simple-audio-card,cpu { ++ sound-dai = <&i2s3>; ++ }; ++ ++ simple-audio-card,codec { ++ sound-dai = <&ac200_codec>; ++ }; ++ }; ++ + ac200_pwm_clk: ac200_clk { + compatible = "pwm-clock"; + #clock-cells = <0>; +@@ -440,6 +456,11 @@ i2c3_pins: i2c3-pins { + bias-pull-up; + }; + ++ i2s3_pins: i2s3-pins { ++ pins = "PB12", "PB13", "PB14", "PB15", "PB16"; ++ function = "i2s3"; ++ }; ++ + mmc0_pins: mmc0-pins { + pins = "PF0", "PF1", "PF2", "PF3", + "PF4", "PF5"; +@@ -717,6 +738,12 @@ ac200_ephy_ctl: syscon { + phy-mode = "rmii"; + status = "disabled"; + }; ++ ++ ac200_codec: codec { ++ #sound-dai-cells = <0>; ++ compatible = "x-powers,ac200-codec"; ++ status = "disabled"; ++ }; + }; + }; + +@@ -774,6 +801,21 @@ mdio: mdio { + }; + }; + ++ i2s3: i2s@508f000 { ++ #sound-dai-cells = <0>; ++ compatible = "allwinner,sun50i-h6-i2s"; ++ reg = <0x0508f000 0x1000>; ++ interrupts = ; ++ clocks = <&ccu CLK_BUS_I2S3>, <&ccu CLK_I2S3>; ++ clock-names = "apb", "mod"; ++ dmas = <&dma 6>, <&dma 6>; ++ resets = <&ccu RST_BUS_I2S3>; ++ dma-names = "rx", "tx"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s3_pins>; ++ status = "disabled"; ++ }; ++ + i2s1: i2s@5091000 { + #sound-dai-cells = <0>; + compatible = "allwinner,sun50i-h6-i2s"; +-- +2.34.1 + diff --git a/patch/kernel/archive/sunxi-6.5/patches.armbian/arm64-dts-allwinner-h6-enable-AC200-codec.patch b/patch/kernel/archive/sunxi-6.5/patches.armbian/arm64-dts-allwinner-h6-enable-AC200-codec.patch new file mode 100644 index 000000000..aca9f9b47 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.5/patches.armbian/arm64-dts-allwinner-h6-enable-AC200-codec.patch @@ -0,0 +1,142 @@ +From 37a4b303a1f6094f9f26bf1e82a7b1a679540651 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Thu, 1 Sep 2022 17:49:28 +0200 +Subject: [PATCH] arm64: dts: allwinner: h6: enable AC200 codec + +Enable AC200 analog codec on H6 based boards where present. + +Signed-off-by: Jernej Skrabec +--- + .../dts/allwinner/sun50i-h6-orangepi-3.dts | 25 +++++++++++++++++++ + .../boot/dts/allwinner/sun50i-h6-pine-h64.dts | 25 +++++++++++++++++++ + .../allwinner/sun50i-h6-tanix-tx6-mini.dts | 14 +++++++++++ + 3 files changed, 64 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts +index 59e9095d7a15..f1fcd37d0fcc 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts +@@ -104,6 +104,19 @@ wifi_pwrseq: wifi-pwrseq { + }; + }; + ++&ac200_codec { ++ avcc-supply = <®_aldo2>; ++ status = "okay"; ++}; ++ ++&ac200_pwm_clk { ++ status = "okay"; ++}; ++ ++&analog { ++ status = "okay"; ++}; ++ + &cpu0 { + cpu-supply = <®_dcdca>; + }; +@@ -172,6 +185,14 @@ &i2s1 { + status = "okay"; + }; + ++&i2c3 { ++ status = "okay"; ++}; ++ ++&i2s3 { ++ status = "okay"; ++}; ++ + &mmc0 { + vmmc-supply = <®_cldo1>; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ +@@ -219,6 +240,10 @@ &pio { + vcc-pg-supply = <®_vcc_wifi_io>; + }; + ++&pwm { ++ status = "okay"; ++}; ++ + &r_ir { + status = "okay"; + }; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts +index 454d2a2974c9..5bb973ea3fb4 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts +@@ -93,6 +93,19 @@ &dwc3 { + status = "okay"; + }; + ++&ac200_codec { ++ avcc-supply = <®_aldo2>; ++ status = "okay"; ++}; ++ ++&ac200_pwm_clk { ++ status = "okay"; ++}; ++ ++&analog { ++ status = "okay"; ++}; ++ + &cpu0 { + cpu-supply = <®_dcdca>; + }; +@@ -139,6 +152,14 @@ &i2s1 { + status = "okay"; + }; + ++&i2c3 { ++ status = "okay"; ++}; ++ ++&i2s3 { ++ status = "okay"; ++}; ++ + &mdio { + ext_rgmii_phy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; +@@ -195,6 +216,10 @@ &pio { + vcc-pg-supply = <®_aldo1>; + }; + ++&pwm { ++ status = "okay"; ++}; ++ + &r_i2c { + status = "okay"; + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix-tx6-mini.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix-tx6-mini.dts +index 08d84160d88f..931e8b99fd93 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix-tx6-mini.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix-tx6-mini.dts +@@ -10,6 +10,20 @@ / { + compatible = "oranth,tanix-tx6-mini", "allwinner,sun50i-h6"; + }; + ++ ++&ac200_codec { ++ avcc-supply = <®_vcc3v3>; ++ status = "okay"; ++}; ++ ++&analog { ++ status = "okay"; ++}; ++ ++&i2s3 { ++ status = "okay"; ++}; ++ + &r_ir { + linux,rc-map-name = "rc-tanix-tx3mini"; + }; +-- +2.34.1 + diff --git a/patch/kernel/archive/sunxi-6.5/patches.armbian/arm64-dts-allwinner-h6-tanix-enable-Ethernet.patch b/patch/kernel/archive/sunxi-6.5/patches.armbian/arm64-dts-allwinner-h6-tanix-enable-Ethernet.patch new file mode 100644 index 000000000..7340deaaa --- /dev/null +++ b/patch/kernel/archive/sunxi-6.5/patches.armbian/arm64-dts-allwinner-h6-tanix-enable-Ethernet.patch @@ -0,0 +1,97 @@ +From 93df3a3c4c7afccbceceab0a8318dcd94cff3259 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sat, 24 Aug 2019 01:03:05 +0200 +Subject: [PATCH] arm64: dts: allwinner: h6: tanix: enable Ethernet + +Tanix-TX6 and TX6 mini boards provide an 100MBit/s Ethernet port, which +uses the EPHY integrated into the Allwinner H6 SoC. + +Enable the MAC node, plus the required AC200 nodes to allow configuring +and enabling the integrated PHY. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Andre Przywara +--- + .../boot/dts/allwinner/sun50i-h6-tanix.dtsi | 38 +++++++++++++++++++ + 1 file changed, 38 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi +index 4903d6358112..51a75debab44 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi +@@ -10,6 +10,7 @@ + + / { + aliases { ++ ethernet0 = &emac; + serial0 = &uart0; + }; + +@@ -84,6 +85,16 @@ wifi_pwrseq: wifi-pwrseq { + }; + }; + ++&ac200_ephy_ctl { ++ x-powers,led-polarity = ; ++ phy-address = <1>; ++ status = "okay"; ++}; ++ ++&ac200_pwm_clk { ++ status = "okay"; ++}; ++ + &cpu0 { + cpu-supply = <®_vdd_cpu_gpu>; + }; +@@ -104,6 +115,14 @@ &ehci3 { + status = "okay"; + }; + ++&emac { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&ext_rmii_pins>; ++ phy-mode = "rmii"; ++ phy-handle = <&ext_rmii_phy>; ++ status = "okay"; ++}; ++ + &gpu { + mali-supply = <®_vdd_cpu_gpu>; + status = "okay"; +@@ -119,6 +138,21 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2c3 { ++ status = "okay"; ++}; ++ ++&mdio { ++ ext_rmii_phy: ethernet-phy@1 { ++ compatible = "ethernet-phy-id0044.1400", ++ "ethernet-phy-ieee802.3-c22"; ++ reg = <1>; ++ resets = <&ac200_ephy_ctl>; ++ reset-names = "phy"; ++ clocks = <&ac200_ephy_ctl>; ++ }; ++}; ++ + &mmc0 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_pins>; +@@ -161,6 +195,10 @@ &pio { + vcc-pg-supply = <®_vcc1v8>; + }; + ++&pwm { ++ status = "okay"; ++}; ++ + &r_ir { + status = "okay"; + }; +-- +2.34.1 + diff --git a/patch/kernel/archive/sunxi-6.5/patches.armbian/clk-gate-add-support-for-regmap-based-gates.patch b/patch/kernel/archive/sunxi-6.5/patches.armbian/clk-gate-add-support-for-regmap-based-gates.patch new file mode 100644 index 000000000..015432bc1 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.5/patches.armbian/clk-gate-add-support-for-regmap-based-gates.patch @@ -0,0 +1,251 @@ +From 7fcd40e7c5dd47fa7a7527d0c75685c460dc49b4 Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Fri, 10 Jun 2022 18:20:29 +0100 +Subject: [PATCH] clk: gate: add support for regmap based gates + +While we have nice wrappers for simple bit-flip MMIO based clock gates, +a single bit to toggle in a regmap still requires to write a lot of clock +framework boilerplate. + +Support generic wrappers for regmap based clock gates, by adding them to +the existing clock-gates.c file. Since a read-modify-write operation in a +regmap can be much more complex than a readl/writel pair, we cannot use +the .enable/.disable ops members, but do the actual flipping already in +.prepare/.unprepare, where we can sleep. Also we cannot provide an +.is_enabled function, since this must not sleep as well. +On the upside all the locking for the r/m/w operation is provided by +regmap already, so we can skip that. +The rest of the CCF boilerplate code can be shared. + +Signed-off-by: Andre Przywara +--- + drivers/clk/clk-gate.c | 60 ++++++++++++++++++++++++++++++++++-- + include/linux/clk-provider.h | 36 +++++++++++++++++++--- + 2 files changed, 89 insertions(+), 7 deletions(-) + +diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c +index 64283807600b..f83aef5e6e79 100644 +--- a/drivers/clk/clk-gate.c ++++ b/drivers/clk/clk-gate.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -124,11 +125,42 @@ const struct clk_ops clk_gate_ops = { + }; + EXPORT_SYMBOL_GPL(clk_gate_ops); + ++static int clk_gate_regmap_setclrbit(struct clk_hw *hw, bool enable) ++{ ++ struct clk_gate *gate = to_clk_gate(hw); ++ bool set = gate->flags & CLK_GATE_SET_TO_DISABLE; ++ ++ set ^= enable; ++ ++ if (set) ++ return regmap_set_bits(gate->regmap, gate->regmap_offs, ++ BIT(gate->bit_idx)); ++ else ++ return regmap_clear_bits(gate->regmap, gate->regmap_offs, ++ BIT(gate->bit_idx)); ++} ++ ++static int clk_gate_regmap_prepare(struct clk_hw *hw) ++{ ++ return clk_gate_regmap_setclrbit(hw, true); ++} ++ ++static void clk_gate_regmap_unprepare(struct clk_hw *hw) ++{ ++ clk_gate_regmap_setclrbit(hw, false); ++} ++ ++const struct clk_ops clk_gate_regmap_ops = { ++ .prepare = clk_gate_regmap_prepare, ++ .unprepare = clk_gate_regmap_unprepare, ++}; ++ + struct clk_hw *__clk_hw_register_gate(struct device *dev, + struct device_node *np, const char *name, + const char *parent_name, const struct clk_hw *parent_hw, + const struct clk_parent_data *parent_data, + unsigned long flags, ++ struct regmap *regmap, unsigned int regmap_offs, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock) + { +@@ -150,7 +182,10 @@ struct clk_hw *__clk_hw_register_gate(struct device *dev, + return ERR_PTR(-ENOMEM); + + init.name = name; +- init.ops = &clk_gate_ops; ++ if (regmap) ++ init.ops = &clk_gate_regmap_ops; ++ else ++ init.ops = &clk_gate_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.parent_hws = parent_hw ? &parent_hw : NULL; +@@ -162,6 +197,8 @@ struct clk_hw *__clk_hw_register_gate(struct device *dev, + + /* struct clk_gate assignments */ + gate->reg = reg; ++ gate->regmap = regmap; ++ gate->regmap_offs = regmap_offs; + gate->bit_idx = bit_idx; + gate->flags = clk_gate_flags; + gate->lock = lock; +@@ -197,6 +234,22 @@ struct clk *clk_register_gate(struct device *dev, const char *name, + } + EXPORT_SYMBOL_GPL(clk_register_gate); + ++struct clk *clk_register_regmap_gate(struct device *dev, const char *name, ++ const char *parent_name, unsigned long flags, ++ struct regmap *regmap, unsigned int regmap_offs, ++ u8 bit_idx, u8 clk_gate_flags) ++{ ++ struct clk_hw *hw; ++ ++ hw = clk_hw_register_regmap_gate(dev, name, parent_name, flags, regmap, ++ regmap_offs, bit_idx, clk_gate_flags); ++ ++ if (IS_ERR(hw)) ++ return ERR_CAST(hw); ++ return hw->clk; ++} ++EXPORT_SYMBOL_GPL(clk_register_regmap_gate); ++ + void clk_unregister_gate(struct clk *clk) + { + struct clk_gate *gate; +@@ -234,6 +287,7 @@ struct clk_hw *__devm_clk_hw_register_gate(struct device *dev, + const char *parent_name, const struct clk_hw *parent_hw, + const struct clk_parent_data *parent_data, + unsigned long flags, ++ struct regmap *regmap, unsigned int regmap_offs, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock) + { +@@ -244,8 +298,8 @@ struct clk_hw *__devm_clk_hw_register_gate(struct device *dev, + return ERR_PTR(-ENOMEM); + + hw = __clk_hw_register_gate(dev, np, name, parent_name, parent_hw, +- parent_data, flags, reg, bit_idx, +- clk_gate_flags, lock); ++ parent_data, flags, regmap, regmap_offs, ++ reg, bit_idx, clk_gate_flags, lock); + + if (!IS_ERR(hw)) { + *ptr = hw; +diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h +index 0f0cd01906b4..3f7ac68e8a1b 100644 +--- a/include/linux/clk-provider.h ++++ b/include/linux/clk-provider.h +@@ -8,6 +8,7 @@ + + #include + #include ++#include + + /* + * flags used across common struct clk. these flags should only affect the +@@ -512,6 +513,8 @@ void of_fixed_clk_setup(struct device_node *np); + struct clk_gate { + struct clk_hw hw; + void __iomem *reg; ++ struct regmap *regmap; ++ unsigned int regmap_offs; + u8 bit_idx; + u8 flags; + spinlock_t *lock; +@@ -529,6 +532,7 @@ struct clk_hw *__clk_hw_register_gate(struct device *dev, + const char *parent_name, const struct clk_hw *parent_hw, + const struct clk_parent_data *parent_data, + unsigned long flags, ++ struct regmap *regmap, unsigned int regmap_offs, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock); + struct clk_hw *__devm_clk_hw_register_gate(struct device *dev, +@@ -536,12 +540,17 @@ struct clk_hw *__devm_clk_hw_register_gate(struct device *dev, + const char *parent_name, const struct clk_hw *parent_hw, + const struct clk_parent_data *parent_data, + unsigned long flags, ++ struct regmap *regmap, unsigned int regmap_offs, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock); + struct clk *clk_register_gate(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock); ++struct clk *clk_register_regmap_gate(struct device *dev, const char *name, ++ const char *parent_name, unsigned long flags, ++ struct regmap *regmap, unsigned int regmap_offs, ++ u8 bit_idx, u8 clk_gate_flags); + /** + * clk_hw_register_gate - register a gate clock with the clock framework + * @dev: device that is registering this clock +@@ -556,8 +565,14 @@ struct clk *clk_register_gate(struct device *dev, const char *name, + #define clk_hw_register_gate(dev, name, parent_name, flags, reg, bit_idx, \ + clk_gate_flags, lock) \ + __clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL, \ +- NULL, (flags), (reg), (bit_idx), \ ++ NULL, (flags), NULL, 0, (reg), (bit_idx), \ + (clk_gate_flags), (lock)) ++ ++#define clk_hw_register_regmap_gate(dev, name, parent_name, flags, regmap, \ ++ regmap_offs, bit_idx, clk_gate_flags) \ ++ __clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL, \ ++ NULL, (flags), regmap, regmap_offs, NULL, \ ++ (bit_idx), (clk_gate_flags), NULL) + /** + * clk_hw_register_gate_parent_hw - register a gate clock with the clock + * framework +@@ -573,8 +588,15 @@ struct clk *clk_register_gate(struct device *dev, const char *name, + #define clk_hw_register_gate_parent_hw(dev, name, parent_hw, flags, reg, \ + bit_idx, clk_gate_flags, lock) \ + __clk_hw_register_gate((dev), NULL, (name), NULL, (parent_hw), \ +- NULL, (flags), (reg), (bit_idx), \ ++ NULL, (flags), NULL, 0, (reg), (bit_idx), \ + (clk_gate_flags), (lock)) ++ ++#define clk_hw_register_regmap_gate_parent_hw(dev, name, parent_hw, flags, \ ++ regmap, regmap_offs, bit_idx, \ ++ clk_gate_flags) \ ++ __clk_hw_register_gate((dev), NULL, (name), NULL, (parent_hw), \ ++ NULL, (flags), regmap, regmap_offs, NULL, \ ++ (bit_idx), (clk_gate_flags), NULL) + /** + * clk_hw_register_gate_parent_data - register a gate clock with the clock + * framework +@@ -590,7 +612,7 @@ struct clk *clk_register_gate(struct device *dev, const char *name, + #define clk_hw_register_gate_parent_data(dev, name, parent_data, flags, reg, \ + bit_idx, clk_gate_flags, lock) \ + __clk_hw_register_gate((dev), NULL, (name), NULL, NULL, (parent_data), \ +- (flags), (reg), (bit_idx), \ ++ (flags), NULL, 0, (reg), (bit_idx), \ + (clk_gate_flags), (lock)) + /** + * devm_clk_hw_register_gate - register a gate clock with the clock framework +@@ -606,8 +628,14 @@ struct clk *clk_register_gate(struct device *dev, const char *name, + #define devm_clk_hw_register_gate(dev, name, parent_name, flags, reg, bit_idx,\ + clk_gate_flags, lock) \ + __devm_clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL, \ +- NULL, (flags), (reg), (bit_idx), \ ++ NULL, (flags), NULL, 0, (reg), (bit_idx), \ + (clk_gate_flags), (lock)) ++#define devm_clk_hw_register_regmap_gate(dev, name, parent_name, flags, \ ++ regmap, regmap_offs, bit_idx, \ ++ clk_gate_flags) \ ++ __devm_clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL, \ ++ NULL, (flags), (regmap), (regmap_offs), NULL, \ ++ (bit_idx), (clk_gate_flags), NULL) + /** + * devm_clk_hw_register_gate_parent_data - register a gate clock with the + * clock framework +-- +2.34.1 + diff --git a/patch/kernel/archive/sunxi-6.5/patches.armbian/drv-mfd-Add-support-for-AC200.patch b/patch/kernel/archive/sunxi-6.5/patches.armbian/drv-mfd-Add-support-for-AC200.patch deleted file mode 100644 index 78c9283ed..000000000 --- a/patch/kernel/archive/sunxi-6.5/patches.armbian/drv-mfd-Add-support-for-AC200.patch +++ /dev/null @@ -1,439 +0,0 @@ -From 945eee6dd75294cd527b1ca6109758afb262a45c Mon Sep 17 00:00:00 2001 -From: Jernej Skrabec -Date: Fri, 16 Aug 2019 16:38:21 +0200 -Subject: [PATCH 004/153] drv:mfd: Add support for AC200 - -Signed-off-by: Jernej Skrabec ---- - drivers/mfd/Kconfig | 9 ++ - drivers/mfd/Makefile | 1 + - drivers/mfd/ac200.c | 170 +++++++++++++++++++++++++++++++ - include/linux/mfd/ac200.h | 208 ++++++++++++++++++++++++++++++++++++++ - 4 files changed, 388 insertions(+) - create mode 100644 drivers/mfd/ac200.c - create mode 100644 include/linux/mfd/ac200.h - -diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig -index 30db49f31..473359275 100644 ---- a/drivers/mfd/Kconfig -+++ b/drivers/mfd/Kconfig -@@ -190,6 +190,15 @@ config MFD_AC100 - This driver include only the core APIs. You have to select individual - components like codecs or RTC under the corresponding menus. - -+config MFD_AC200 -+ bool "X-Powers AC200" -+ select MFD_CORE -+ depends on I2C -+ help -+ If you say Y here you get support for the X-Powers AC200 IC. -+ This driver include only the core APIs. You have to select individual -+ components like Ethernet PHY or RTC under the corresponding menus. -+ - config MFD_AXP20X - tristate - select MFD_CORE -diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile -index 457471478..b2e69fbce 100644 ---- a/drivers/mfd/Makefile -+++ b/drivers/mfd/Makefile -@@ -141,6 +141,7 @@ obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o - obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o - - obj-$(CONFIG_MFD_AC100) += ac100.o -+obj-$(CONFIG_MFD_AC200) += ac200.o - obj-$(CONFIG_MFD_AXP20X) += axp20x.o - obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o - obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o -diff --git a/drivers/mfd/ac200.c b/drivers/mfd/ac200.c -new file mode 100644 -index 000000000..570573790 ---- /dev/null -+++ b/drivers/mfd/ac200.c -@@ -0,0 +1,169 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * MFD core driver for X-Powers' AC200 IC -+ * -+ * The AC200 is a chip which is co-packaged with Allwinner H6 SoC and -+ * includes analog audio codec, analog TV encoder, ethernet PHY, eFuse -+ * and RTC. -+ * -+ * Copyright (c) 2020 Jernej Skrabec -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* Interrupts */ -+#define AC200_IRQ_RTC 0 -+#define AC200_IRQ_EPHY 1 -+#define AC200_IRQ_TVE 2 -+ -+/* IRQ enable register */ -+#define AC200_SYS_IRQ_ENABLE_OUT_EN BIT(15) -+#define AC200_SYS_IRQ_ENABLE_RTC BIT(12) -+#define AC200_SYS_IRQ_ENABLE_EPHY BIT(8) -+#define AC200_SYS_IRQ_ENABLE_TVE BIT(4) -+ -+static const struct regmap_range_cfg ac200_range_cfg[] = { -+ { -+ .range_min = AC200_SYS_VERSION, -+ .range_max = AC200_IC_CHARA1, -+ .selector_reg = AC200_TWI_REG_ADDR_H, -+ .selector_mask = 0xff, -+ .selector_shift = 0, -+ .window_start = 0, -+ .window_len = 256, -+ } -+}; -+ -+static const struct regmap_config ac200_regmap_config = { -+ .reg_bits = 8, -+ .val_bits = 16, -+ .ranges = ac200_range_cfg, -+ .num_ranges = ARRAY_SIZE(ac200_range_cfg), -+ .max_register = AC200_IC_CHARA1, -+}; -+ -+static const struct regmap_irq ac200_regmap_irqs[] = { -+ REGMAP_IRQ_REG(AC200_IRQ_RTC, 0, AC200_SYS_IRQ_ENABLE_RTC), -+ REGMAP_IRQ_REG(AC200_IRQ_EPHY, 0, AC200_SYS_IRQ_ENABLE_EPHY), -+ REGMAP_IRQ_REG(AC200_IRQ_TVE, 0, AC200_SYS_IRQ_ENABLE_TVE), -+}; -+ -+static const struct regmap_irq_chip ac200_regmap_irq_chip = { -+ .name = "ac200_irq_chip", -+ .status_base = AC200_SYS_IRQ_STATUS, -+ .mask_base = AC200_SYS_IRQ_ENABLE, -+ .mask_invert = true, -+ .irqs = ac200_regmap_irqs, -+ .num_irqs = ARRAY_SIZE(ac200_regmap_irqs), -+ .num_regs = 1, -+}; -+ -+static const struct resource ephy_resource[] = { -+ DEFINE_RES_IRQ(AC200_IRQ_EPHY), -+}; -+ -+static const struct mfd_cell ac200_cells[] = { -+ { -+ .name = "ac200-ephy", -+ .num_resources = ARRAY_SIZE(ephy_resource), -+ .resources = ephy_resource, -+ .of_compatible = "x-powers,ac200-ephy", -+ }, -+}; -+ -+static int ac200_i2c_probe(struct i2c_client *i2c) -+{ -+ struct device *dev = &i2c->dev; -+ struct ac200_dev *ac200; -+ int ret; -+ -+ ac200 = devm_kzalloc(dev, sizeof(*ac200), GFP_KERNEL); -+ if (!ac200) -+ return -ENOMEM; -+ -+ i2c_set_clientdata(i2c, ac200); -+ -+ ac200->regmap = devm_regmap_init_i2c(i2c, &ac200_regmap_config); -+ if (IS_ERR(ac200->regmap)) { -+ ret = PTR_ERR(ac200->regmap); -+ dev_err(dev, "regmap init failed: %d\n", ret); -+ return ret; -+ } -+ -+ /* do a reset to put chip in a known state */ -+ -+ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0); -+ if (ret) -+ return ret; -+ -+ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 1); -+ if (ret) -+ return ret; -+ -+ /* enable interrupt pin */ -+ -+ ret = regmap_write(ac200->regmap, AC200_SYS_IRQ_ENABLE, -+ AC200_SYS_IRQ_ENABLE_OUT_EN); -+ if (ret) -+ return ret; -+ -+ ret = regmap_add_irq_chip(ac200->regmap, i2c->irq, IRQF_ONESHOT, 0, -+ &ac200_regmap_irq_chip, &ac200->regmap_irqc); -+ if (ret) -+ return ret; -+ -+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, ac200_cells, -+ ARRAY_SIZE(ac200_cells), NULL, 0, NULL); -+ if (ret) { -+ dev_err(dev, "failed to add MFD devices: %d\n", ret); -+ regmap_del_irq_chip(i2c->irq, ac200->regmap_irqc); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int ac200_i2c_remove(struct i2c_client *i2c) -+{ -+ struct ac200_dev *ac200 = i2c_get_clientdata(i2c); -+ -+ regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0); -+ -+ mfd_remove_devices(&i2c->dev); -+ regmap_del_irq_chip(i2c->irq, ac200->regmap_irqc); -+ -+ return 0; -+} -+ -+static const struct i2c_device_id ac200_ids[] = { -+ { "ac200", }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(i2c, ac200_ids); -+ -+static const struct of_device_id ac200_of_match[] = { -+ { .compatible = "x-powers,ac200" }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(of, ac200_of_match); -+ -+static struct i2c_driver ac200_i2c_driver = { -+ .driver = { -+ .name = "ac200", -+ .of_match_table = of_match_ptr(ac200_of_match), -+ }, -+ .probe = ac200_i2c_probe, -+ .remove = ac200_i2c_remove, -+ .id_table = ac200_ids, -+}; -+module_i2c_driver(ac200_i2c_driver); -+ -+MODULE_DESCRIPTION("MFD core driver for AC200"); -+MODULE_AUTHOR("Jernej Skrabec "); -+MODULE_LICENSE("GPL v2"); -diff --git a/include/linux/mfd/ac200.h b/include/linux/mfd/ac200.h -new file mode 100644 -index 000000000..0c677094a ---- /dev/null -+++ b/include/linux/mfd/ac200.h -@@ -0,0 +1,208 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * AC200 register list -+ * -+ * Copyright (C) 2019 Jernej Skrabec -+ */ -+ -+#ifndef __LINUX_MFD_AC200_H -+#define __LINUX_MFD_AC200_H -+ -+#include -+ -+/* interface registers (can be accessed from any page) */ -+#define AC200_TWI_CHANGE_TO_RSB 0x3E -+#define AC200_TWI_PAD_DELAY 0xC4 -+#define AC200_TWI_REG_ADDR_H 0xFE -+ -+/* General registers */ -+#define AC200_SYS_VERSION 0x0000 -+#define AC200_SYS_CONTROL 0x0002 -+#define AC200_SYS_IRQ_ENABLE 0x0004 -+#define AC200_SYS_IRQ_STATUS 0x0006 -+#define AC200_SYS_CLK_CTL 0x0008 -+#define AC200_SYS_DLDO_OSC_CTL 0x000A -+#define AC200_SYS_PLL_CTL0 0x000C -+#define AC200_SYS_PLL_CTL1 0x000E -+#define AC200_SYS_AUDIO_CTL0 0x0010 -+#define AC200_SYS_AUDIO_CTL1 0x0012 -+#define AC200_SYS_EPHY_CTL0 0x0014 -+#define AC200_SYS_EPHY_CTL1 0x0016 -+#define AC200_SYS_TVE_CTL0 0x0018 -+#define AC200_SYS_TVE_CTL1 0x001A -+ -+/* Audio Codec registers */ -+#define AC200_AC_SYS_CLK_CTL 0x2000 -+#define AC200_SYS_MOD_RST 0x2002 -+#define AC200_SYS_SAMP_CTL 0x2004 -+#define AC200_I2S_CTL 0x2100 -+#define AC200_I2S_CLK 0x2102 -+#define AC200_I2S_FMT0 0x2104 -+#define AC200_I2S_FMT1 0x2108 -+#define AC200_I2S_MIX_SRC 0x2114 -+#define AC200_I2S_MIX_GAIN 0x2116 -+#define AC200_I2S_DACDAT_DVC 0x2118 -+#define AC200_I2S_ADCDAT_DVC 0x211A -+#define AC200_AC_DAC_DPC 0x2200 -+#define AC200_AC_DAC_MIX_SRC 0x2202 -+#define AC200_AC_DAC_MIX_GAIN 0x2204 -+#define AC200_DACA_OMIXER_CTRL 0x2220 -+#define AC200_OMIXER_SR 0x2222 -+#define AC200_LINEOUT_CTRL 0x2224 -+#define AC200_AC_ADC_DPC 0x2300 -+#define AC200_MBIAS_CTRL 0x2310 -+#define AC200_ADC_MIC_CTRL 0x2320 -+#define AC200_ADCMIXER_SR 0x2322 -+#define AC200_ANALOG_TUNING0 0x232A -+#define AC200_ANALOG_TUNING1 0x232C -+#define AC200_AC_AGC_SEL 0x2480 -+#define AC200_ADC_DAPLCTRL 0x2500 -+#define AC200_ADC_DAPRCTRL 0x2502 -+#define AC200_ADC_DAPLSTA 0x2504 -+#define AC200_ADC_DAPRSTA 0x2506 -+#define AC200_ADC_DAPLTL 0x2508 -+#define AC200_ADC_DAPRTL 0x250A -+#define AC200_ADC_DAPLHAC 0x250C -+#define AC200_ADC_DAPLLAC 0x250E -+#define AC200_ADC_DAPRHAC 0x2510 -+#define AC200_ADC_DAPRLAC 0x2512 -+#define AC200_ADC_DAPLDT 0x2514 -+#define AC200_ADC_DAPLAT 0x2516 -+#define AC200_ADC_DAPRDT 0x2518 -+#define AC200_ADC_DAPRAT 0x251A -+#define AC200_ADC_DAPNTH 0x251C -+#define AC200_ADC_DAPLHNAC 0x251E -+#define AC200_ADC_DAPLLNAC 0x2520 -+#define AC200_ADC_DAPRHNAC 0x2522 -+#define AC200_ADC_DAPRLNAC 0x2524 -+#define AC200_AC_DAPHHPFC 0x2526 -+#define AC200_AC_DAPLHPFC 0x2528 -+#define AC200_AC_DAPOPT 0x252A -+#define AC200_AC_DAC_DAPCTRL 0x3000 -+#define AC200_AC_DRC_HHPFC 0x3002 -+#define AC200_AC_DRC_LHPFC 0x3004 -+#define AC200_AC_DRC_CTRL 0x3006 -+#define AC200_AC_DRC_LPFHAT 0x3008 -+#define AC200_AC_DRC_LPFLAT 0x300A -+#define AC200_AC_DRC_RPFHAT 0x300C -+#define AC200_AC_DRC_RPFLAT 0x300E -+#define AC200_AC_DRC_LPFHRT 0x3010 -+#define AC200_AC_DRC_LPFLRT 0x3012 -+#define AC200_AC_DRC_RPFHRT 0x3014 -+#define AC200_AC_DRC_RPFLRT 0x3016 -+#define AC200_AC_DRC_LRMSHAT 0x3018 -+#define AC200_AC_DRC_LRMSLAT 0x301A -+#define AC200_AC_DRC_RRMSHAT 0x301C -+#define AC200_AC_DRC_RRMSLAT 0x301E -+#define AC200_AC_DRC_HCT 0x3020 -+#define AC200_AC_DRC_LCT 0x3022 -+#define AC200_AC_DRC_HKC 0x3024 -+#define AC200_AC_DRC_LKC 0x3026 -+#define AC200_AC_DRC_HOPC 0x3028 -+#define AC200_AC_DRC_LOPC 0x302A -+#define AC200_AC_DRC_HLT 0x302C -+#define AC200_AC_DRC_LLT 0x302E -+#define AC200_AC_DRC_HKI 0x3030 -+#define AC200_AC_DRC_LKI 0x3032 -+#define AC200_AC_DRC_HOPL 0x3034 -+#define AC200_AC_DRC_LOPL 0x3036 -+#define AC200_AC_DRC_HET 0x3038 -+#define AC200_AC_DRC_LET 0x303A -+#define AC200_AC_DRC_HKE 0x303C -+#define AC200_AC_DRC_LKE 0x303E -+#define AC200_AC_DRC_HOPE 0x3040 -+#define AC200_AC_DRC_LOPE 0x3042 -+#define AC200_AC_DRC_HKN 0x3044 -+#define AC200_AC_DRC_LKN 0x3046 -+#define AC200_AC_DRC_SFHAT 0x3048 -+#define AC200_AC_DRC_SFLAT 0x304A -+#define AC200_AC_DRC_SFHRT 0x304C -+#define AC200_AC_DRC_SFLRT 0x304E -+#define AC200_AC_DRC_MXGHS 0x3050 -+#define AC200_AC_DRC_MXGLS 0x3052 -+#define AC200_AC_DRC_MNGHS 0x3054 -+#define AC200_AC_DRC_MNGLS 0x3056 -+#define AC200_AC_DRC_EPSHC 0x3058 -+#define AC200_AC_DRC_EPSLC 0x305A -+#define AC200_AC_DRC_HPFHGAIN 0x305E -+#define AC200_AC_DRC_HPFLGAIN 0x3060 -+#define AC200_AC_DRC_BISTCR 0x3100 -+#define AC200_AC_DRC_BISTST 0x3102 -+ -+/* TVE registers */ -+#define AC200_TVE_CTL0 0x4000 -+#define AC200_TVE_CTL1 0x4002 -+#define AC200_TVE_MOD0 0x4004 -+#define AC200_TVE_MOD1 0x4006 -+#define AC200_TVE_DAC_CFG0 0x4008 -+#define AC200_TVE_DAC_CFG1 0x400A -+#define AC200_TVE_YC_DELAY 0x400C -+#define AC200_TVE_YC_FILTER 0x400E -+#define AC200_TVE_BURST_FRQ0 0x4010 -+#define AC200_TVE_BURST_FRQ1 0x4012 -+#define AC200_TVE_FRONT_PORCH 0x4014 -+#define AC200_TVE_BACK_PORCH 0x4016 -+#define AC200_TVE_TOTAL_LINE 0x401C -+#define AC200_TVE_FIRST_ACTIVE 0x401E -+#define AC200_TVE_BLACK_LEVEL 0x4020 -+#define AC200_TVE_BLANK_LEVEL 0x4022 -+#define AC200_TVE_PLUG_EN 0x4030 -+#define AC200_TVE_PLUG_IRQ_EN 0x4032 -+#define AC200_TVE_PLUG_IRQ_STA 0x4034 -+#define AC200_TVE_PLUG_STA 0x4038 -+#define AC200_TVE_PLUG_DEBOUNCE 0x4040 -+#define AC200_TVE_DAC_TEST 0x4042 -+#define AC200_TVE_PLUG_PULSE_LEVEL 0x40F4 -+#define AC200_TVE_PLUG_PULSE_START 0x40F8 -+#define AC200_TVE_PLUG_PULSE_PERIOD 0x40FA -+#define AC200_TVE_IF_CTL 0x5000 -+#define AC200_TVE_IF_TIM0 0x5008 -+#define AC200_TVE_IF_TIM1 0x500A -+#define AC200_TVE_IF_TIM2 0x500C -+#define AC200_TVE_IF_TIM3 0x500E -+#define AC200_TVE_IF_SYNC0 0x5010 -+#define AC200_TVE_IF_SYNC1 0x5012 -+#define AC200_TVE_IF_SYNC2 0x5014 -+#define AC200_TVE_IF_TIM4 0x5016 -+#define AC200_TVE_IF_STATUS 0x5018 -+ -+/* EPHY registers */ -+#define AC200_EPHY_CTL 0x6000 -+#define AC200_EPHY_BIST 0x6002 -+ -+/* eFuse registers (0x8000 - 0x9FFF, layout unknown) */ -+ -+/* RTC registers */ -+#define AC200_LOSC_CTRL0 0xA000 -+#define AC200_LOSC_CTRL1 0xA002 -+#define AC200_LOSC_AUTO_SWT_STA 0xA004 -+#define AC200_INTOSC_CLK_PRESCAL 0xA008 -+#define AC200_RTC_YY_MM_DD0 0xA010 -+#define AC200_RTC_YY_MM_DD1 0xA012 -+#define AC200_RTC_HH_MM_SS0 0xA014 -+#define AC200_RTC_HH_MM_SS1 0xA016 -+#define AC200_ALARM0_CUR_VLU0 0xA024 -+#define AC200_ALARM0_CUR_VLU1 0xA026 -+#define AC200_ALARM0_ENABLE 0xA028 -+#define AC200_ALARM0_IRQ_EN 0xA02C -+#define AC200_ALARM0_IRQ_STA 0xA030 -+#define AC200_ALARM1_WK_HH_MM_SS0 0xA040 -+#define AC200_ALARM1_WK_HH_MM_SS1 0xA042 -+#define AC200_ALARM1_ENABLE 0xA044 -+#define AC200_ALARM1_IRQ_EN 0xA048 -+#define AC200_ALARM1_IRQ_STA 0xA04C -+#define AC200_ALARM_CONFIG 0xA050 -+#define AC200_LOSC_OUT_GATING 0xA060 -+#define AC200_GP_DATA(x) (0xA100 + (x) * 2) -+#define AC200_RTC_DEB 0xA170 -+#define AC200_GPL_HOLD_OUTPUT 0xA180 -+#define AC200_VDD_RTC 0xA190 -+#define AC200_IC_CHARA0 0xA1F0 -+#define AC200_IC_CHARA1 0xA1F2 -+ -+struct ac200_dev { -+ struct regmap *regmap; -+ struct regmap_irq_chip_data *regmap_irqc; -+}; -+ -+#endif /* __LINUX_MFD_AC200_H */ --- -2.35.3 - diff --git a/patch/kernel/archive/sunxi-6.5/patches.armbian/drv-net-phy-Add-support-for-AC200-EPHY.patch b/patch/kernel/archive/sunxi-6.5/patches.armbian/drv-net-phy-Add-support-for-AC200-EPHY.patch deleted file mode 100644 index 0c0ab5442..000000000 --- a/patch/kernel/archive/sunxi-6.5/patches.armbian/drv-net-phy-Add-support-for-AC200-EPHY.patch +++ /dev/null @@ -1,272 +0,0 @@ -From b7c0b9b054e7a9df3d2d0087491017f345d18daf Mon Sep 17 00:00:00 2001 -From: Jernej Skrabec -Date: Fri, 16 Aug 2019 16:38:57 +0200 -Subject: [PATCH 006/153] drv:net:phy: Add support for AC200 EPHY - -Signed-off-by: Jernej Skrabec ---- - drivers/net/phy/Kconfig | 7 ++ - drivers/net/phy/Makefile | 1 + - drivers/net/phy/ac200.c | 220 +++++++++++++++++++++++++++++++++++++++ - 3 files changed, 228 insertions(+) - create mode 100644 drivers/net/phy/ac200.c - -diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig -index 1327290de..61684250a 100644 ---- a/drivers/net/phy/Kconfig -+++ b/drivers/net/phy/Kconfig -@@ -62,6 +62,13 @@ config SFP - - comment "MII PHY device drivers" - -+config AC200_PHY -+ tristate "AC200 EPHY" -+ depends on NVMEM -+ depends on OF -+ help -+ Fast ethernet PHY as found in X-Powers AC200 multi-function device. -+ - config AMD_PHY - tristate "AMD PHYs" - help -diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile -index f7138d3c8..9ad2b8cc0 100644 ---- a/drivers/net/phy/Makefile -+++ b/drivers/net/phy/Makefile -@@ -30,6 +30,7 @@ obj-$(CONFIG_SFP) += sfp.o - sfp-obj-$(CONFIG_SFP) += sfp-bus.o - obj-y += $(sfp-obj-y) $(sfp-obj-m) - -+obj-$(CONFIG_AC200_PHY) += ac200.o - obj-$(CONFIG_ADIN_PHY) += adin.o - obj-$(CONFIG_ADIN1100_PHY) += adin1100.o - obj-$(CONFIG_AMD_PHY) += amd.o -diff --git a/drivers/net/phy/ac200.c b/drivers/net/phy/ac200.c -new file mode 100644 -index 000000000..cb713188f ---- /dev/null -+++ b/drivers/net/phy/ac200.c -@@ -0,0 +1,220 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/** -+ * Driver for AC200 Ethernet PHY -+ * -+ * Copyright (c) 2020 Jernej Skrabec -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define AC200_EPHY_ID 0x00441400 -+#define AC200_EPHY_ID_MASK 0x0ffffff0 -+ -+/* macros for system ephy control 0 register */ -+#define AC200_EPHY_RESET_INVALID BIT(0) -+#define AC200_EPHY_SYSCLK_GATING BIT(1) -+ -+/* macros for system ephy control 1 register */ -+#define AC200_EPHY_E_EPHY_MII_IO_EN BIT(0) -+#define AC200_EPHY_E_LNK_LED_IO_EN BIT(1) -+#define AC200_EPHY_E_SPD_LED_IO_EN BIT(2) -+#define AC200_EPHY_E_DPX_LED_IO_EN BIT(3) -+ -+/* macros for ephy control register */ -+#define AC200_EPHY_SHUTDOWN BIT(0) -+#define AC200_EPHY_LED_POL BIT(1) -+#define AC200_EPHY_CLK_SEL BIT(2) -+#define AC200_EPHY_ADDR(x) (((x) & 0x1F) << 4) -+#define AC200_EPHY_XMII_SEL BIT(11) -+#define AC200_EPHY_CALIB(x) (((x) & 0xF) << 12) -+ -+struct ac200_ephy_dev { -+ struct clk *clk; -+ struct phy_driver *ephy; -+ struct regmap *regmap; -+}; -+ -+static char *ac200_phy_name = "AC200 EPHY"; -+ -+static int ac200_ephy_config_init(struct phy_device *phydev) -+{ -+ const struct ac200_ephy_dev *priv = phydev->drv->driver_data; -+ unsigned int value; -+ int ret; -+ -+ phy_write(phydev, 0x1f, 0x0100); /* Switch to Page 1 */ -+ phy_write(phydev, 0x12, 0x4824); /* Disable APS */ -+ -+ phy_write(phydev, 0x1f, 0x0200); /* Switch to Page 2 */ -+ phy_write(phydev, 0x18, 0x0000); /* PHYAFE TRX optimization */ -+ -+ phy_write(phydev, 0x1f, 0x0600); /* Switch to Page 6 */ -+ phy_write(phydev, 0x14, 0x708f); /* PHYAFE TX optimization */ -+ phy_write(phydev, 0x13, 0xF000); /* PHYAFE RX optimization */ -+ phy_write(phydev, 0x15, 0x1530); -+ -+ phy_write(phydev, 0x1f, 0x0800); /* Switch to Page 6 */ -+ phy_write(phydev, 0x18, 0x00bc); /* PHYAFE TRX optimization */ -+ -+ phy_write(phydev, 0x1f, 0x0100); /* switch to page 1 */ -+ phy_clear_bits(phydev, 0x17, BIT(3)); /* disable intelligent IEEE */ -+ -+ /* next two blocks disable 802.3az IEEE */ -+ phy_write(phydev, 0x1f, 0x0200); /* switch to page 2 */ -+ phy_write(phydev, 0x18, 0x0000); -+ -+ phy_write(phydev, 0x1f, 0x0000); /* switch to page 0 */ -+ phy_clear_bits_mmd(phydev, 0x7, 0x3c, BIT(1)); -+ -+ if (phydev->interface == PHY_INTERFACE_MODE_RMII) -+ value = AC200_EPHY_XMII_SEL; -+ else -+ value = 0; -+ -+ ret = regmap_update_bits(priv->regmap, AC200_EPHY_CTL, -+ AC200_EPHY_XMII_SEL, value); -+ if (ret) -+ return ret; -+ -+ /* FIXME: This is H6 specific */ -+ phy_set_bits(phydev, 0x13, BIT(12)); -+ -+ return 0; -+} -+ -+static int ac200_ephy_probe(struct platform_device *pdev) -+{ -+ struct ac200_dev *ac200 = dev_get_drvdata(pdev->dev.parent); -+ struct device *dev = &pdev->dev; -+ struct ac200_ephy_dev *priv; -+ struct nvmem_cell *calcell; -+ struct phy_driver *ephy; -+ u16 *caldata, calib; -+ size_t callen; -+ int ret; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ ephy = devm_kzalloc(dev, sizeof(*ephy), GFP_KERNEL); -+ if (!ephy) -+ return -ENOMEM; -+ -+ priv->clk = devm_clk_get(dev, NULL); -+ if (IS_ERR(priv->clk)) { -+ dev_err(dev, "Can't obtain the clock!\n"); -+ return PTR_ERR(priv->clk); -+ } -+ -+ calcell = devm_nvmem_cell_get(dev, "calibration"); -+ if (IS_ERR(calcell)) { -+ dev_err(dev, "Unable to find calibration data!\n"); -+ return PTR_ERR(calcell); -+ } -+ -+ caldata = nvmem_cell_read(calcell, &callen); -+ if (IS_ERR(caldata)) { -+ dev_err(dev, "Unable to read calibration data!\n"); -+ return PTR_ERR(caldata); -+ } -+ -+ if (callen != 2) { -+ dev_err(dev, "Calibration data has wrong length: 2 != %zu\n", -+ callen); -+ kfree(caldata); -+ return -EINVAL; -+ } -+ -+ calib = *caldata + 3; -+ kfree(caldata); -+ -+ ret = clk_prepare_enable(priv->clk); -+ if (ret) -+ return ret; -+ -+ ephy->phy_id = AC200_EPHY_ID; -+ ephy->phy_id_mask = AC200_EPHY_ID_MASK; -+ ephy->name = ac200_phy_name; -+ ephy->driver_data = priv; -+ ephy->soft_reset = genphy_soft_reset; -+ ephy->config_init = ac200_ephy_config_init; -+ ephy->suspend = genphy_suspend; -+ ephy->resume = genphy_resume; -+ -+ priv->ephy = ephy; -+ priv->regmap = ac200->regmap; -+ platform_set_drvdata(pdev, priv); -+ -+ ret = regmap_write(ac200->regmap, AC200_SYS_EPHY_CTL0, -+ AC200_EPHY_RESET_INVALID | -+ AC200_EPHY_SYSCLK_GATING); -+ if (ret) -+ return ret; -+ -+ ret = regmap_write(ac200->regmap, AC200_SYS_EPHY_CTL1, -+ AC200_EPHY_E_EPHY_MII_IO_EN | -+ AC200_EPHY_E_LNK_LED_IO_EN | -+ AC200_EPHY_E_SPD_LED_IO_EN | -+ AC200_EPHY_E_DPX_LED_IO_EN); -+ if (ret) -+ return ret; -+ -+ ret = regmap_write(ac200->regmap, AC200_EPHY_CTL, -+ AC200_EPHY_LED_POL | -+ AC200_EPHY_CLK_SEL | -+ AC200_EPHY_ADDR(1) | -+ AC200_EPHY_CALIB(calib)); -+ if (ret) -+ return ret; -+ -+ ret = phy_driver_register(priv->ephy, THIS_MODULE); -+ if (ret) { -+ dev_err(dev, "Unable to register phy\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int ac200_ephy_remove(struct platform_device *pdev) -+{ -+ struct ac200_ephy_dev *priv = platform_get_drvdata(pdev); -+ -+ phy_driver_unregister(priv->ephy); -+ -+ regmap_write(priv->regmap, AC200_EPHY_CTL, AC200_EPHY_SHUTDOWN); -+ regmap_write(priv->regmap, AC200_SYS_EPHY_CTL1, 0); -+ regmap_write(priv->regmap, AC200_SYS_EPHY_CTL0, 0); -+ -+ clk_disable_unprepare(priv->clk); -+ -+ return 0; -+} -+ -+static const struct of_device_id ac200_ephy_match[] = { -+ { .compatible = "x-powers,ac200-ephy" }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(of, ac200_ephy_match); -+ -+static struct platform_driver ac200_ephy_driver = { -+ .probe = ac200_ephy_probe, -+ .remove = ac200_ephy_remove, -+ .driver = { -+ .name = "ac200-ephy", -+ .of_match_table = ac200_ephy_match, -+ }, -+}; -+module_platform_driver(ac200_ephy_driver); -+ -+MODULE_AUTHOR("Jernej Skrabec "); -+MODULE_DESCRIPTION("AC200 Ethernet PHY driver"); -+MODULE_LICENSE("GPL"); --- -2.35.3 - diff --git a/patch/kernel/archive/sunxi-6.5/patches.armbian/mfd-Add-support-for-X-Powers-AC200-EPHY-syscon.patch b/patch/kernel/archive/sunxi-6.5/patches.armbian/mfd-Add-support-for-X-Powers-AC200-EPHY-syscon.patch new file mode 100644 index 000000000..252ba126e --- /dev/null +++ b/patch/kernel/archive/sunxi-6.5/patches.armbian/mfd-Add-support-for-X-Powers-AC200-EPHY-syscon.patch @@ -0,0 +1,375 @@ +From 3f2e8f9da20ce69785be3135e2a2db74955d239b Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Mon, 13 Jun 2022 17:37:19 +0100 +Subject: [PATCH] mfd: Add support for X-Powers AC200 EPHY syscon + +The X-Powers AC200 mixed signal chip contains a 100Mbit/s Ethernet PHY. +While the PHY is using the standard MDIO and MII/RMII interfaces to +the MAC, there are some registers in the AC200 that need to be setup to +make the PHY functional: +- The MDIO PHY address needs to set. +- The LED polarity needs to be configured. +- The MII interface mode needs to be set (MII or RMII). +- There is a reset line that controls the PHY operation. +- There is a clock gate that blocks or forwards the AC200's internal clock + to the PHY. + +This driver here takes care of those setup needs, but does not cover the +actual PHY operation. Once the PHY is set up and enabled, it behaves like +a standard MDIO/MII controlled PHY, and can be driven by the IEEE802.3-C22 +driver. + +To some degree those parameters mimic the typical wired setup of a +physical PHY chip: the LED polarity, MII interface mode and PHY address +are typically configued via connecting certain pins. We use the +devicetree to learn those parameters, which depend on the board setup. + +This driver is a child of the AC200 MFD parent device, and uses its +regmap and input clock. It also provides a reset and clock controller, +which the actual PHY device (node) needs to refer to. This ensures that +this PHY control device is initialised before the PHY is expected to work. + +Signed-off-by: Andre Przywara +--- + drivers/phy/allwinner/Kconfig | 9 + + drivers/phy/allwinner/Makefile | 1 + + drivers/phy/allwinner/ac200-ephy-ctl.c | 301 +++++++++++++++++++++++++ + 3 files changed, 311 insertions(+) + create mode 100644 drivers/phy/allwinner/ac200-ephy-ctl.c + +diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig +index e93a53139460..d3614169de5c 100644 +--- a/drivers/phy/allwinner/Kconfig ++++ b/drivers/phy/allwinner/Kconfig +@@ -58,3 +58,12 @@ config PHY_SUN50I_USB3 + part of Allwinner H6 SoC. + + This driver controls each individual USB 2+3 host PHY combo. ++ ++config AC200_PHY_CTL ++ tristate "X-Power AC200 PHY control driver" ++ depends on MFD_AC200 ++ depends on RESET_CONTROLLER ++ help ++ Enable this to support the Ethernet PHY operation of the AC200 ++ mixed signal chip. This driver just enables and configures the ++ PHY, the PHY itself is supported by a standard driver. +diff --git a/drivers/phy/allwinner/Makefile b/drivers/phy/allwinner/Makefile +index bd74901a1255..0eecec7a908a 100644 +--- a/drivers/phy/allwinner/Makefile ++++ b/drivers/phy/allwinner/Makefile +@@ -3,3 +3,4 @@ obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o + obj-$(CONFIG_PHY_SUN6I_MIPI_DPHY) += phy-sun6i-mipi-dphy.o + obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o + obj-$(CONFIG_PHY_SUN50I_USB3) += phy-sun50i-usb3.o ++obj-$(CONFIG_AC200_PHY_CTL) += ac200-ephy-ctl.o +diff --git a/drivers/phy/allwinner/ac200-ephy-ctl.c b/drivers/phy/allwinner/ac200-ephy-ctl.c +new file mode 100644 +index 000000000000..8efeaf18e42c +--- /dev/null ++++ b/drivers/phy/allwinner/ac200-ephy-ctl.c +@@ -0,0 +1,301 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/** ++ * syscon driver to control and configure AC200 Ethernet PHY ++ * Copyright (c) 2022 Arm Ltd. ++ * ++ * TODO's and questions: ++ * ========================= ++ * - This driver is something like a syscon driver, as it controls various ++ * bits and registers that effect other devices (the actual PHY). It's ++ * unclear where it should live, though: ++ * - it could be integrated into the MFD driver, but this looks messy ++ * - it could live at the current location (drivers/phy/allwinner), but that ++ * sounds wrong ++ * - it could be a separate file, but in drivers/mfd ++ * - anything else ++ * ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* macros for system ephy control 0 register */ ++#define AC200_SYS_EPHY_CTL0 0x0014 ++#define AC200_EPHY_RESET_INVALID BIT(0) ++#define AC200_EPHY_SYSCLK_GATING 1 ++ ++/* macros for system ephy control 1 register */ ++#define AC200_SYS_EPHY_CTL1 0x0016 ++#define AC200_EPHY_E_EPHY_MII_IO_EN BIT(0) ++#define AC200_EPHY_E_LNK_LED_IO_EN BIT(1) ++#define AC200_EPHY_E_SPD_LED_IO_EN BIT(2) ++#define AC200_EPHY_E_DPX_LED_IO_EN BIT(3) ++ ++/* macros for ephy control register */ ++#define AC200_EPHY_CTL 0x6000 ++#define AC200_EPHY_SHUTDOWN BIT(0) ++#define AC200_EPHY_LED_POL BIT(1) ++#define AC200_EPHY_CLK_SEL BIT(2) ++#define AC200_EPHY_ADDR(x) (((x) & 0x1F) << 4) ++#define AC200_EPHY_XMII_SEL BIT(11) ++#define AC200_EPHY_CALIB(x) (((x) & 0xF) << 12) ++ ++struct ac200_ephy_ctl_dev { ++ struct reset_controller_dev rcdev; ++ struct clk_hw *gate_clk; ++ struct regmap *regmap; ++}; ++ ++static struct ac200_ephy_ctl_dev *to_phy_dev(struct reset_controller_dev *rcdev) ++{ ++ return container_of(rcdev, struct ac200_ephy_ctl_dev, rcdev); ++} ++ ++static int ephy_ctl_reset(struct reset_controller_dev *rcdev, unsigned long id) ++{ ++ struct ac200_ephy_ctl_dev *ac200 = to_phy_dev(rcdev); ++ int ret; ++ ++ ret = regmap_clear_bits(ac200->regmap, AC200_SYS_EPHY_CTL0, ++ AC200_EPHY_RESET_INVALID); ++ if (ret) ++ return ret; ++ ++ /* This is going via I2C, so there is plenty of built-in delay. */ ++ return regmap_set_bits(ac200->regmap, AC200_SYS_EPHY_CTL0, ++ AC200_EPHY_RESET_INVALID); ++} ++ ++static int ephy_ctl_assert(struct reset_controller_dev *rcdev, unsigned long id) ++{ ++ struct ac200_ephy_ctl_dev *ac200 = to_phy_dev(rcdev); ++ ++ return regmap_clear_bits(ac200->regmap, AC200_SYS_EPHY_CTL0, ++ AC200_EPHY_RESET_INVALID); ++} ++ ++static int ephy_ctl_deassert(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ struct ac200_ephy_ctl_dev *ac200 = to_phy_dev(rcdev); ++ ++ return regmap_set_bits(ac200->regmap, AC200_SYS_EPHY_CTL0, ++ AC200_EPHY_RESET_INVALID); ++} ++ ++static int ephy_ctl_status(struct reset_controller_dev *rcdev, unsigned long id) ++{ ++ struct ac200_ephy_ctl_dev *ac200 = to_phy_dev(rcdev); ++ ++ return regmap_test_bits(ac200->regmap, AC200_SYS_EPHY_CTL0, ++ AC200_EPHY_RESET_INVALID); ++} ++ ++static int ephy_ctl_reset_of_xlate(struct reset_controller_dev *rcdev, ++ const struct of_phandle_args *reset_spec) ++{ ++ if (WARN_ON(reset_spec->args_count != 0)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++const struct reset_control_ops ephy_ctl_reset_ops = { ++ .assert = ephy_ctl_assert, ++ .deassert = ephy_ctl_deassert, ++ .reset = ephy_ctl_reset, ++ .status = ephy_ctl_status, ++}; ++ ++static void ac200_ephy_ctl_disable(struct ac200_ephy_ctl_dev *priv) ++{ ++ regmap_write(priv->regmap, AC200_EPHY_CTL, AC200_EPHY_SHUTDOWN); ++ regmap_write(priv->regmap, AC200_SYS_EPHY_CTL1, 0); ++ regmap_write(priv->regmap, AC200_SYS_EPHY_CTL0, 0); ++} ++ ++static int ac200_ephy_ctl_probe(struct platform_device *pdev) ++{ ++ struct reset_controller_dev *rcdev; ++ struct device *dev = &pdev->dev; ++ struct ac200_ephy_ctl_dev *priv; ++ struct nvmem_cell *calcell; ++ const char *parent_name; ++ phy_interface_t phy_if; ++ u16 *caldata, ephy_ctl; ++ struct clk *clk; ++ size_t callen; ++ u32 value; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, priv); ++ ++ priv->regmap = dev_get_regmap(dev->parent, NULL); ++ if (!priv->regmap) ++ return -EPROBE_DEFER; ++ ++ calcell = devm_nvmem_cell_get(dev, "calibration"); ++ if (IS_ERR(calcell)) ++ return dev_err_probe(dev, PTR_ERR(calcell), ++ "Unable to find calibration data!\n"); ++ ++ caldata = nvmem_cell_read(calcell, &callen); ++ if (IS_ERR(caldata)) { ++ dev_err(dev, "Unable to read calibration data!\n"); ++ return PTR_ERR(caldata); ++ } ++ ++ if (callen != 2) { ++ dev_err(dev, "Calibration data length must be 2 bytes!\n"); ++ kfree(caldata); ++ return -EINVAL; ++ } ++ ++ ephy_ctl = AC200_EPHY_CALIB(*caldata + 3); ++ kfree(caldata); ++ ++ ret = of_get_phy_mode(dev->of_node, &phy_if); ++ if (ret) { ++ dev_err(dev, "Unable to read PHY connection mode\n"); ++ return ret; ++ } ++ ++ switch (phy_if) { ++ case PHY_INTERFACE_MODE_MII: ++ break; ++ case PHY_INTERFACE_MODE_RMII: ++ ephy_ctl |= AC200_EPHY_XMII_SEL; ++ break; ++ default: ++ dev_err(dev, "Illegal PHY connection mode (%d), only RMII or MII supported\n", ++ phy_if); ++ return -EINVAL; ++ } ++ ++ ret = of_property_read_u32(dev->of_node, "x-powers,led-polarity", ++ &value); ++ if (ret) { ++ dev_err(dev, "Unable to read LED polarity setting\n"); ++ return ret; ++ } ++ ++ if (value == GPIO_ACTIVE_LOW) ++ ephy_ctl |= AC200_EPHY_LED_POL; ++ ++ ret = of_property_read_u32(dev->of_node, "phy-address", &value); ++ if (ret) { ++ dev_err(dev, "Unable to read PHY address value\n"); ++ return ret; ++ } ++ ++ ephy_ctl |= AC200_EPHY_ADDR(value); ++ ++ clk = clk_get(dev->parent, NULL); ++ if (IS_ERR(clk)) ++ return dev_err_probe(dev, PTR_ERR(clk), ++ "Unable to obtain the clock\n"); ++ ++ if (clk_get_rate(clk) == 24000000) ++ ephy_ctl |= AC200_EPHY_CLK_SEL; ++ ++ clk_put(clk); ++ ++ /* Assert reset and gate clock, to disable PHY for now */ ++ ret = regmap_write(priv->regmap, AC200_SYS_EPHY_CTL0, 0); ++ if (ret) ++ return ret; ++ ++ ret = regmap_write(priv->regmap, AC200_SYS_EPHY_CTL1, ++ AC200_EPHY_E_EPHY_MII_IO_EN | ++ AC200_EPHY_E_LNK_LED_IO_EN | ++ AC200_EPHY_E_SPD_LED_IO_EN | ++ AC200_EPHY_E_DPX_LED_IO_EN); ++ if (ret) ++ return ret; ++ ++ ret = regmap_write(priv->regmap, AC200_EPHY_CTL, ephy_ctl); ++ if (ret) ++ return ret; ++ ++ rcdev = &priv->rcdev; ++ rcdev->owner = dev->driver->owner; ++ rcdev->nr_resets = 1; ++ rcdev->ops = &ephy_ctl_reset_ops; ++ rcdev->of_node = dev->of_node; ++ rcdev->of_reset_n_cells = 0; ++ rcdev->of_xlate = ephy_ctl_reset_of_xlate; ++ ++ ret = devm_reset_controller_register(dev, rcdev); ++ if (ret) { ++ dev_err(dev, "Unable to register reset controller: %d\n", ret); ++ goto err_disable_ephy; ++ } ++ ++ parent_name = of_clk_get_parent_name(dev->parent->of_node, 0); ++ priv->gate_clk = devm_clk_hw_register_regmap_gate(dev, ++ "ac200-ephy-ctl-gate", parent_name, 0, ++ priv->regmap, AC200_SYS_EPHY_CTL0, ++ AC200_EPHY_SYSCLK_GATING, 0); ++ if (IS_ERR(priv->gate_clk)) { ++ ret = PTR_ERR(priv->gate_clk); ++ dev_err(dev, "Unable to register gate clock: %d\n", ret); ++ goto err_disable_ephy; ++ } ++ ++ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, ++ priv->gate_clk); ++ if (ret) { ++ dev_err(dev, "Unable to register clock provider: %d\n", ret); ++ goto err_disable_ephy; ++ } ++ ++ return 0; ++ ++err_disable_ephy: ++ ac200_ephy_ctl_disable(priv); ++ ++ return ret; ++} ++ ++static int ac200_ephy_ctl_remove(struct platform_device *pdev) ++{ ++ struct ac200_ephy_ctl_dev *priv = platform_get_drvdata(pdev); ++ ++ ac200_ephy_ctl_disable(priv); ++ ++ return 0; ++} ++ ++static const struct of_device_id ac200_ephy_ctl_match[] = { ++ { .compatible = "x-powers,ac200-ephy-ctl" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, ac200_ephy_ctl_match); ++ ++static struct platform_driver ac200_ephy_ctl_driver = { ++ .probe = ac200_ephy_ctl_probe, ++ .remove = ac200_ephy_ctl_remove, ++ .driver = { ++ .name = "ac200-ephy-ctl", ++ .of_match_table = ac200_ephy_ctl_match, ++ }, ++}; ++module_platform_driver(ac200_ephy_ctl_driver); ++ ++MODULE_AUTHOR("Andre Przywara "); ++MODULE_DESCRIPTION("AC200 Ethernet PHY control driver"); ++MODULE_LICENSE("GPL"); +-- +2.34.1 + diff --git a/patch/kernel/archive/sunxi-6.5/patches.armbian/mfd-Add-support-for-X-Powers-AC200.patch b/patch/kernel/archive/sunxi-6.5/patches.armbian/mfd-Add-support-for-X-Powers-AC200.patch new file mode 100644 index 000000000..e5cbd9444 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.5/patches.armbian/mfd-Add-support-for-X-Powers-AC200.patch @@ -0,0 +1,266 @@ +From b9cfea3958c89d9a29b46fec023035a9964304bd Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Fri, 16 Aug 2019 16:38:21 +0200 +Subject: [PATCH] mfd: Add support for X-Powers AC200 + +The X-Powers AC200 is a mixed signal multi-purpose chip, which provides +audio DAC/ADCs, a CVBS video encoder, a 100Mbit/s Ethernet PHY and a +real-time clock. Its control registers can be accessed via I2C or +Allwinner's RSB bus. +Beside this chip being used on some older boards (for instance the Remix +Mini PC), it is quite wide spread due to its die being co-packaged on the +Allwinner H6 and H616 SoCs, which use its audio, video and PHY +functionality. + +Aside from the RTC, the other functions do not need constant +hand-holding via the I2C registers, but rather need to be configured and +enabled only once. + +We model the control side of this chip using the MFD subsystem. This +driver here just provides the parent device for the various subfunctions, +and takes care of enabling clocks and reset, but also provides the regmap, +which the respective child drivers will use. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Andre Przywara +--- + drivers/mfd/Kconfig | 12 +++ + drivers/mfd/Makefile | 1 + + drivers/mfd/ac200.c | 190 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 203 insertions(+) + create mode 100644 drivers/mfd/ac200.c + +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index 9da8235cb690..6fbf88940f26 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -179,6 +179,18 @@ config MFD_AC100 + This driver include only the core APIs. You have to select individual + components like codecs or RTC under the corresponding menus. + ++config MFD_AC200 ++ tristate "X-Powers AC200" ++ select MFD_CORE ++ select REGMAP_I2C ++ depends on COMMON_CLK ++ depends on I2C ++ depends on OF ++ help ++ If you say Y here you get support for the X-Powers AC200 IC. ++ This driver include only the core APIs. You have to select individual ++ components like Ethernet PHY or codec under the corresponding menus. ++ + config MFD_AXP20X + tristate + select MFD_CORE +diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile +index 7ed3ef4a698c..91dc530d0bde 100644 +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -144,6 +144,7 @@ obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o + obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o + + obj-$(CONFIG_MFD_AC100) += ac100.o ++obj-$(CONFIG_MFD_AC200) += ac200.o + obj-$(CONFIG_MFD_AXP20X) += axp20x.o + obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o + obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o +diff --git a/drivers/mfd/ac200.c b/drivers/mfd/ac200.c +new file mode 100644 +index 000000000000..625b119f53cf +--- /dev/null ++++ b/drivers/mfd/ac200.c +@@ -0,0 +1,190 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * MFD core driver for X-Powers' AC200 IC ++ * ++ * The AC200 is a chip which is co-packaged with Allwinner H6 SoC and ++ * includes analog audio codec, analog TV encoder, ethernet PHY, eFuse ++ * and RTC. ++ * ++ * Copyright (c) 2019 Jernej Skrabec ++ * ++ * Based on AC100 driver with following copyrights: ++ * Copyright (2016) Chen-Yu Tsai ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct ac200_dev { ++ struct clk *clk; ++ struct regmap *regmap; ++}; ++ ++#define AC200_SYS_CONTROL 0x0002 ++#define AC200_SYS_BG_CTL 0x0050 ++ ++/* interface register (can be accessed from any page) */ ++#define AC200_TWI_REG_ADDR_H 0xFE ++ ++#define AC200_MAX_REG 0xA1F2 ++ ++static const struct regmap_range_cfg ac200_range_cfg[] = { ++ { ++ .range_max = AC200_MAX_REG, ++ .selector_reg = AC200_TWI_REG_ADDR_H, ++ .selector_mask = 0xff, ++ .selector_shift = 0, ++ .window_start = 0, ++ .window_len = 256, ++ } ++}; ++ ++static const struct regmap_config ac200_regmap_config = { ++ .name = "AC200", ++ .reg_bits = 8, ++ .reg_stride = 2, ++ .val_bits = 16, ++ .ranges = ac200_range_cfg, ++ .num_ranges = ARRAY_SIZE(ac200_range_cfg), ++ .max_register = AC200_MAX_REG, ++}; ++ ++static struct mfd_cell ac200_cells[] = { ++ { ++ .name = "ac200-codec", ++ .of_compatible = "x-powers,ac200-codec", ++ }, { ++ .name = "ac200-ephy-ctl", ++ .of_compatible = "x-powers,ac200-ephy-ctl", ++ }, ++}; ++ ++static int ac200_i2c_probe(struct i2c_client *i2c) ++{ ++ struct device *dev = &i2c->dev; ++ struct nvmem_cell *bgcell; ++ struct ac200_dev *ac200; ++ u16 *bgdata, bgval; ++ size_t bglen; ++ int ret; ++ ++ ac200 = devm_kzalloc(dev, sizeof(*ac200), GFP_KERNEL); ++ if (!ac200) ++ return -ENOMEM; ++ ++ i2c_set_clientdata(i2c, ac200); ++ ++ ac200->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(ac200->clk)) ++ return dev_err_probe(dev, PTR_ERR(ac200->clk), ++ "Can't obtain the clock\n"); ++ ++ ac200->regmap = devm_regmap_init_i2c(i2c, &ac200_regmap_config); ++ if (IS_ERR(ac200->regmap)) { ++ ret = PTR_ERR(ac200->regmap); ++ dev_err(dev, "Regmap init failed: %d\n", ret); ++ return ret; ++ } ++ ++ bgcell = devm_nvmem_cell_get(dev, "bandgap"); ++ if (IS_ERR(bgcell)) ++ return dev_err_probe(dev, PTR_ERR(bgcell), ++ "Unable to find bandgap data!\n"); ++ ++ bgdata = nvmem_cell_read(bgcell, &bglen); ++ if (IS_ERR(bgdata)) { ++ dev_err(dev, "Unable to read bandgap data!\n"); ++ return PTR_ERR(bgdata); ++ } ++ ++ if (bglen != 2) { ++ dev_err(dev, "Invalid nvmem bandgap length!\n"); ++ kfree(bgdata); ++ return -EINVAL; ++ } ++ ++ bgval = *bgdata; ++ kfree(bgdata); ++ ++ ret = clk_prepare_enable(ac200->clk); ++ if (ret) ++ return ret; ++ ++ /* ++ * There is no documentation on how long we have to wait before ++ * executing first operation. Vendor driver sleeps for 40 ms. ++ */ ++ msleep(40); ++ ++ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0); ++ if (ret) ++ goto err; ++ ++ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 1); ++ if (ret) ++ goto err; ++ ++ if (bgval) { ++ /* bandgap register is not documented */ ++ ret = regmap_write(ac200->regmap, AC200_SYS_BG_CTL, ++ 0x8280 | bgval); ++ if (ret) ++ goto err; ++ } ++ ++ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, ac200_cells, ++ ARRAY_SIZE(ac200_cells), NULL, 0, NULL); ++ if (ret) { ++ dev_err(dev, "Failed to add MFD devices: %d\n", ret); ++ goto err; ++ } ++ ++ return 0; ++ ++err: ++ clk_disable_unprepare(ac200->clk); ++ return ret; ++} ++ ++static void ac200_i2c_remove(struct i2c_client *i2c) ++{ ++ struct ac200_dev *ac200 = i2c_get_clientdata(i2c); ++ ++ regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0); ++ ++ clk_disable_unprepare(ac200->clk); ++} ++ ++static const struct i2c_device_id ac200_ids[] = { ++ { "ac200", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ac200_ids); ++ ++static const struct of_device_id ac200_of_match[] = { ++ { .compatible = "x-powers,ac200" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, ac200_of_match); ++ ++static struct i2c_driver ac200_i2c_driver = { ++ .driver = { ++ .name = "ac200", ++ .of_match_table = of_match_ptr(ac200_of_match), ++ }, ++ .probe = ac200_i2c_probe, ++ .remove = ac200_i2c_remove, ++ .id_table = ac200_ids, ++}; ++module_i2c_driver(ac200_i2c_driver); ++ ++MODULE_DESCRIPTION("MFD core driver for AC200"); ++MODULE_AUTHOR("Jernej Skrabec "); ++MODULE_LICENSE("GPL v2"); +-- +2.34.1 + diff --git a/patch/kernel/archive/sunxi-6.5/patches.armbian/mfd-sunxi-ac200-fix-error-initialization.patch b/patch/kernel/archive/sunxi-6.5/patches.armbian/mfd-sunxi-ac200-fix-error-initialization.patch deleted file mode 100644 index 8130ce939..000000000 --- a/patch/kernel/archive/sunxi-6.5/patches.armbian/mfd-sunxi-ac200-fix-error-initialization.patch +++ /dev/null @@ -1,34 +0,0 @@ -From eab277e9ca1f5558dbbf9820dbf6cf4edf4d4c39 Mon Sep 17 00:00:00 2001 -From: The-going <48602507+The-going@users.noreply.github.com> -Date: Tue, 28 Feb 2023 13:39:51 +0300 -Subject: [PATCH 153/153] mfd: sunxi-ac200: fix error initialization - ---- - drivers/mfd/sunxi-ac200.c | 4 +--- - 1 file changed, 1 insertion(+), 3 deletions(-) - -diff --git a/drivers/mfd/sunxi-ac200.c b/drivers/mfd/sunxi-ac200.c -index 368a54587..b784a82f3 100644 ---- a/drivers/mfd/sunxi-ac200.c -+++ b/drivers/mfd/sunxi-ac200.c -@@ -144,7 +144,7 @@ static int ac200_i2c_probe(struct i2c_client *i2c, - return 0; - } - --static int ac200_i2c_remove(struct i2c_client *i2c) -+static void ac200_i2c_remove(struct i2c_client *i2c) - { - struct ac200_dev *ac200 = i2c_get_clientdata(i2c); - -@@ -152,8 +152,6 @@ static int ac200_i2c_remove(struct i2c_client *i2c) - - mfd_remove_devices(&i2c->dev); - regmap_del_irq_chip(i2c->irq, ac200->regmap_irqc); -- -- return 0; - } - - static const struct i2c_device_id ac200_ids[] = { --- -2.35.3 - diff --git a/patch/kernel/archive/sunxi-6.5/patches.armbian/net-phy-Add-support-for-AC200-EPHY.patch b/patch/kernel/archive/sunxi-6.5/patches.armbian/net-phy-Add-support-for-AC200-EPHY.patch new file mode 100644 index 000000000..6bc3a33fd --- /dev/null +++ b/patch/kernel/archive/sunxi-6.5/patches.armbian/net-phy-Add-support-for-AC200-EPHY.patch @@ -0,0 +1,143 @@ +From af665542835fd39edf63c8c9ad1f7267e64a2320 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Fri, 16 Aug 2019 16:38:57 +0200 +Subject: [PATCH] net: phy: Add support for AC200 EPHY + +The X-Powers AC200 mixed signal chip contains a 100Mbit/s Ethernet PHY. +While its sporting a usable default setup, and can be controlled by the +generic IEEE802.3-C22 PHY driver, the BSP sets up some extra registers, +which this driver here covers. + +Add a PHY driver matching the AC200 EPHY ID registers, and which +programs some PHY registers according to the BSP code. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Andre Przywara +--- + drivers/net/phy/Kconfig | 7 ++++ + drivers/net/phy/Makefile | 1 + + drivers/net/phy/ac200-phy.c | 82 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 90 insertions(+) + create mode 100644 drivers/net/phy/ac200.c + +diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig +index c57a0262fb64..a1f8fdfc5762 100644 +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -63,6 +63,13 @@ config SFP + + comment "MII PHY device drivers" + ++config AC200_PHY ++ tristate "AC200 EPHY" ++ depends on NVMEM ++ depends on OF ++ help ++ Fast ethernet PHY as found in X-Powers AC200 multi-function device. ++ + config AMD_PHY + tristate "AMD PHYs" + help +diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile +index f7138d3c896b..9ad2b8cc01b1 100644 +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -30,6 +30,7 @@ obj-$(CONFIG_SFP) += sfp.o + sfp-obj-$(CONFIG_SFP) += sfp-bus.o + obj-y += $(sfp-obj-y) $(sfp-obj-m) + ++obj-$(CONFIG_AC200_PHY) += ac200-phy.o + obj-$(CONFIG_ADIN_PHY) += adin.o + obj-$(CONFIG_ADIN1100_PHY) += adin1100.o + obj-$(CONFIG_AMD_PHY) += amd.o +diff --git a/drivers/net/phy/ac200-phy.c b/drivers/net/phy/ac200-phy.c +new file mode 100644 +index 000000000000..8499914f49b8 +--- /dev/null ++++ b/drivers/net/phy/ac200-phy.c +@@ -0,0 +1,82 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/** ++ * Driver for AC200 Ethernet PHY ++ * ++ * Copyright (c) 2019 Jernej Skrabec ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define AC200_EPHY_ID 0x00441400 ++#define AC200_EPHY_ID_MASK 0x0ffffff0 ++ ++static int ac200_ephy_config_init(struct phy_device *phydev) ++{ ++ phy_write(phydev, 0x1f, 0x0100); /* Switch to Page 1 */ ++ phy_write(phydev, 0x12, 0x4824); /* Disable APS */ ++ ++ phy_write(phydev, 0x1f, 0x0200); /* Switch to Page 2 */ ++ phy_write(phydev, 0x18, 0x0000); /* PHYAFE TRX optimization */ ++ ++ phy_write(phydev, 0x1f, 0x0600); /* Switch to Page 6 */ ++ phy_write(phydev, 0x14, 0x708f); /* PHYAFE TX optimization */ ++ phy_write(phydev, 0x13, 0xF000); /* PHYAFE RX optimization */ ++ phy_write(phydev, 0x15, 0x1530); ++ ++ phy_write(phydev, 0x1f, 0x0800); /* Switch to Page 8 */ ++ phy_write(phydev, 0x18, 0x00bc); /* PHYAFE TRX optimization */ ++ ++ phy_write(phydev, 0x1f, 0x0100); /* switch to page 1 */ ++ phy_clear_bits(phydev, 0x17, BIT(3)); /* disable intelligent EEE */ ++ ++ /* disable 802.3az EEE */ ++ phy_write(phydev, 0x1f, 0x0200); /* switch to page 2 */ ++ phy_write(phydev, 0x18, 0x0000); ++ phy_write(phydev, 0x1f, 0x0000); /* switch to page 0 */ ++ phy_clear_bits_mmd(phydev, 0x7, 0x3c, BIT(1)); ++ ++ /* FIXME: This is probably H6 specific */ ++ phy_set_bits(phydev, 0x13, BIT(12)); ++ ++ return 0; ++} ++ ++static int ac200_ephy_probe(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ struct clk *clk; ++ ++ clk = devm_clk_get_optional_enabled(dev, NULL); ++ if (IS_ERR(clk)) ++ return dev_err_probe(dev, PTR_ERR(clk), ++ "Failed to request clock\n"); ++ ++ return 0; ++} ++ ++static struct phy_driver ac200_ephy_driver[] = { ++ { ++ .phy_id = AC200_EPHY_ID, ++ .phy_id_mask = AC200_EPHY_ID_MASK, ++ .name = "Allwinner AC200 EPHY", ++ .soft_reset = genphy_soft_reset, ++ .config_init = ac200_ephy_config_init, ++ .probe = ac200_ephy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ } ++}; ++module_phy_driver(ac200_ephy_driver); ++ ++MODULE_AUTHOR("Jernej Skrabec "); ++MODULE_DESCRIPTION("AC200 Ethernet PHY driver"); ++MODULE_LICENSE("GPL"); ++ ++static const struct mdio_device_id __maybe_unused ac200_ephy_phy_tbl[] = { ++ { AC200_EPHY_ID, AC200_EPHY_ID_MASK }, ++ { } ++}; ++MODULE_DEVICE_TABLE(mdio, ac200_ephy_phy_tbl); +-- +2.34.1 + diff --git a/patch/kernel/archive/sunxi-6.5/series.armbian b/patch/kernel/archive/sunxi-6.5/series.armbian index 0caeb50dd..b64f3e666 100644 --- a/patch/kernel/archive/sunxi-6.5/series.armbian +++ b/patch/kernel/archive/sunxi-6.5/series.armbian @@ -4,9 +4,7 @@ # ################################################################################ patches.armbian/Doc-dt-bindings-usb-add-binding-for-DWC3-controller-on-Allwinne.patch - patches.armbian/drv-mfd-Add-support-for-AC200.patch patches.armbian/drv-pinctrl-pinctrl-sun50i-a64-disable_strict_mode.patch - patches.armbian/drv-net-phy-Add-support-for-AC200-EPHY.patch patches.armbian/drv-rtc-sun6i-support-RTCs-without-external-LOSCs.patch patches.armbian/drv-gpu-drm-gem-dma-Export-with-handle-allocator.patch patches.armbian/drv-gpu-drm-sun4i-Add-GEM-allocator.patch @@ -81,7 +79,6 @@ patches.armbian/Bananapro-add-AXP209-regulators.patch patches.armbian/arm-dts-sunxi-h3-h5.dtsi-force-mmc0-bus-width.patch patches.armbian/arm64-dts-sun50i-a64-pine64-enable-wifi-mmc1.patch - patches.armbian/arm64-dts-sun50i-h6-Add-AC200-EPHY-related-nodes.patch patches.armbian/arm64-dts-sun50i-a64-sopine-baseboard-Add-i2s2-mmc1.patch patches.armbian/arm64-dts-sun50i-h6-Add-r_uart-uart2-3-pins.patch ####### sun50i-h616 ####### @@ -169,19 +166,26 @@ #### Needs to be redone - patches.armbian/net-phy-Support-yt8531c.patch #### - patches.armbian/allwinner-h6-Support-ac200-audio-codec.patch patches.armbian/nvmem-sunxi_sid-add-sunxi_get_soc_chipid-sunxi_get_serial.patch - patches.armbian/add-initial-support-for-orangepi3-lts.patch patches.armbian/mmc-host-sunxi-mmc-Fix-H6-emmc.patch patches.armbian/arm64-dts-allwinner-sun50i-h6-Fix-H6-emmc.patch - patches.armbian/Rollback-r_rsb-to-r_i2c.patch patches.armbian/arm64-dts-sun50i-h5-nanopi-r1s-h5-add-rtl8153-suppor.patch patches.armbian/net-usb-r8152-add-LED-configuration-from-OF.patch patches.armbian/arm64-dts-sun50i-h6-orangepi.dtsi-Rollback-r_rsb-to-r_i2c.patch - patches.armbian/mfd-sunxi-ac200-fix-error-initialization.patch patches.armbian/arm64-dts-sun50i-h616-bigtreetech-cb1.patch patches.armbian/arm-dts-sun8i-h3-nanopi-duo2-enable-powerbutton-and-ethernet.patch patches.armbian/arm-dts-sun8i-h3-reduce-opp-microvolt-to-prevent-not.patch patches.armbian/arm64-dts-sun50i-h5-enable-power-button-for-orangepi-prime.patch patches.armbian/arm-dts-sunxi-h3-h5-add_tve.patch patches.armbian/drivers-devfreq-sun8i-a33-mbus-disable-autorefresh.patch + patches.armbian/clk-gate-add-support-for-regmap-based-gates.patch + patches.armbian/mfd-Add-support-for-X-Powers-AC200.patch + patches.armbian/mfd-Add-support-for-X-Powers-AC200-EPHY-syscon.patch + patches.armbian/net-phy-Add-support-for-AC200-EPHY.patch + patches.armbian/arm64-dts-allwinner-h6-Add-AC200-EPHY-nodes.patch + patches.armbian/arm64-dts-allwinner-h6-tanix-enable-Ethernet.patch + patches.armbian/ASoC-AC200-Initial-driver.patch + patches.armbian/arm64-dts-allwinner-h6-add-AC200-codec-nodes.patch + patches.armbian/arm64-dts-allwinner-h6-enable-AC200-codec.patch + patches.armbian/add-nodes-for-sunxi-info-addr-dump-reg.patch + patches.armbian/add-initial-support-for-orangepi3-lts.patch diff --git a/patch/kernel/archive/sunxi-6.5/series.conf b/patch/kernel/archive/sunxi-6.5/series.conf index e629d5c85..ac873b170 100644 --- a/patch/kernel/archive/sunxi-6.5/series.conf +++ b/patch/kernel/archive/sunxi-6.5/series.conf @@ -482,9 +482,7 @@ # ################################################################################ patches.armbian/Doc-dt-bindings-usb-add-binding-for-DWC3-controller-on-Allwinne.patch - patches.armbian/drv-mfd-Add-support-for-AC200.patch patches.armbian/drv-pinctrl-pinctrl-sun50i-a64-disable_strict_mode.patch - patches.armbian/drv-net-phy-Add-support-for-AC200-EPHY.patch patches.armbian/drv-rtc-sun6i-support-RTCs-without-external-LOSCs.patch patches.armbian/drv-gpu-drm-gem-dma-Export-with-handle-allocator.patch patches.armbian/drv-gpu-drm-sun4i-Add-GEM-allocator.patch @@ -559,7 +557,6 @@ patches.armbian/Bananapro-add-AXP209-regulators.patch patches.armbian/arm-dts-sunxi-h3-h5.dtsi-force-mmc0-bus-width.patch patches.armbian/arm64-dts-sun50i-a64-pine64-enable-wifi-mmc1.patch - patches.armbian/arm64-dts-sun50i-h6-Add-AC200-EPHY-related-nodes.patch patches.armbian/arm64-dts-sun50i-a64-sopine-baseboard-Add-i2s2-mmc1.patch patches.armbian/arm64-dts-sun50i-h6-Add-r_uart-uart2-3-pins.patch ####### sun50i-h616 ####### @@ -647,19 +644,26 @@ #### Needs to be redone - patches.armbian/net-phy-Support-yt8531c.patch #### - patches.armbian/allwinner-h6-Support-ac200-audio-codec.patch patches.armbian/nvmem-sunxi_sid-add-sunxi_get_soc_chipid-sunxi_get_serial.patch - patches.armbian/add-initial-support-for-orangepi3-lts.patch patches.armbian/mmc-host-sunxi-mmc-Fix-H6-emmc.patch patches.armbian/arm64-dts-allwinner-sun50i-h6-Fix-H6-emmc.patch - patches.armbian/Rollback-r_rsb-to-r_i2c.patch patches.armbian/arm64-dts-sun50i-h5-nanopi-r1s-h5-add-rtl8153-suppor.patch patches.armbian/net-usb-r8152-add-LED-configuration-from-OF.patch patches.armbian/arm64-dts-sun50i-h6-orangepi.dtsi-Rollback-r_rsb-to-r_i2c.patch - patches.armbian/mfd-sunxi-ac200-fix-error-initialization.patch patches.armbian/arm64-dts-sun50i-h616-bigtreetech-cb1.patch patches.armbian/arm-dts-sun8i-h3-nanopi-duo2-enable-powerbutton-and-ethernet.patch patches.armbian/arm-dts-sun8i-h3-reduce-opp-microvolt-to-prevent-not.patch patches.armbian/arm64-dts-sun50i-h5-enable-power-button-for-orangepi-prime.patch patches.armbian/arm-dts-sunxi-h3-h5-add_tve.patch patches.armbian/drivers-devfreq-sun8i-a33-mbus-disable-autorefresh.patch + patches.armbian/clk-gate-add-support-for-regmap-based-gates.patch + patches.armbian/mfd-Add-support-for-X-Powers-AC200.patch + patches.armbian/mfd-Add-support-for-X-Powers-AC200-EPHY-syscon.patch + patches.armbian/net-phy-Add-support-for-AC200-EPHY.patch + patches.armbian/arm64-dts-allwinner-h6-Add-AC200-EPHY-nodes.patch + patches.armbian/arm64-dts-allwinner-h6-tanix-enable-Ethernet.patch + patches.armbian/ASoC-AC200-Initial-driver.patch + patches.armbian/arm64-dts-allwinner-h6-add-AC200-codec-nodes.patch + patches.armbian/arm64-dts-allwinner-h6-enable-AC200-codec.patch + patches.armbian/add-nodes-for-sunxi-info-addr-dump-reg.patch + patches.armbian/add-initial-support-for-orangepi3-lts.patch