From bef4992ac1a23512c6a694d7c19caa154ccaafe3 Mon Sep 17 00:00:00 2001 From: Paolo Sabatino Date: Sun, 12 Sep 2021 13:39:53 +0000 Subject: [PATCH] rk322x: analog audio codec for edge kernel --- config/kernel/linux-rk322x-edge.config | 3 +- ...linux-0008-rk322x-analog-audio-codec.patch | 913 ++++++++++++++++++ .../rk322x-5.14/board-rk322x-box.patch | 28 +- 3 files changed, 941 insertions(+), 3 deletions(-) create mode 100644 patch/kernel/archive/rk322x-5.14/02-linux-0008-rk322x-analog-audio-codec.patch diff --git a/config/kernel/linux-rk322x-edge.config b/config/kernel/linux-rk322x-edge.config index f89bb1984..037a9e408 100644 --- a/config/kernel/linux-rk322x-edge.config +++ b/config/kernel/linux-rk322x-edge.config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm 5.14.2 Kernel Configuration +# Linux/arm 5.14.3 Kernel Configuration # CONFIG_CC_VERSION_TEXT="arm-linux-gnueabihf-gcc (GNU Toolchain for the A-profile Architecture 8.3-2019.03 (arm-rel-8.36)) 8.3.0" CONFIG_CC_IS_GCC=y @@ -4672,6 +4672,7 @@ CONFIG_SND_SOC_PCM1789_I2C=m # CONFIG_SND_SOC_PCM5102A is not set # CONFIG_SND_SOC_PCM512x_I2C is not set # CONFIG_SND_SOC_PCM512x_SPI is not set +CONFIG_SND_SOC_RK3228=m CONFIG_SND_SOC_RK3328=m # CONFIG_SND_SOC_RK817 is not set CONFIG_SND_SOC_RL6231=m diff --git a/patch/kernel/archive/rk322x-5.14/02-linux-0008-rk322x-analog-audio-codec.patch b/patch/kernel/archive/rk322x-5.14/02-linux-0008-rk322x-analog-audio-codec.patch new file mode 100644 index 000000000..a39a0c2f2 --- /dev/null +++ b/patch/kernel/archive/rk322x-5.14/02-linux-0008-rk322x-analog-audio-codec.patch @@ -0,0 +1,913 @@ +From b4f40590a4f946d8ee704faf8579930e53ef4650 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Sun, 12 Sep 2021 10:15:56 +0000 +Subject: [PATCH] rk322x: analog audio codec + +--- + .../bindings/sound/rockchip,rk3228-codec.txt | 22 + + arch/arm/boot/dts/rk322x.dtsi | 9 + + drivers/clk/rockchip/clk-rk3228.c | 2 +- + include/dt-bindings/clock/rk3228-cru.h | 1 + + sound/soc/codecs/Kconfig | 5 + + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/rk3228_codec.c | 545 ++++++++++++++++++ + sound/soc/codecs/rk3228_codec.h | 218 +++++++ + 8 files changed, 803 insertions(+), 1 deletion(-) + create mode 100644 Documentation/devicetree/bindings/sound/rockchip,rk3228-codec.txt + create mode 100644 sound/soc/codecs/rk3228_codec.c + create mode 100644 sound/soc/codecs/rk3228_codec.h + +diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3228-codec.txt b/Documentation/devicetree/bindings/sound/rockchip,rk3228-codec.txt +new file mode 100644 +index 000000000..9191a8593 +--- /dev/null ++++ b/Documentation/devicetree/bindings/sound/rockchip,rk3228-codec.txt +@@ -0,0 +1,22 @@ ++* Rockchip Rk3228 internal codec ++ ++Required properties: ++ ++- compatible: "rockchip,rk3228-codec" ++- reg: physical base address of the controller and length of memory mapped ++ region. ++- clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names. ++- clock-names: a list of clock names, one for each entry in clocks. ++- spk-en-gpio: speaker enable gpio. ++- spk-depop-time-ms: speaker depop time msec. ++ ++Example for rk3228 internal codec: ++ ++codec: codec@12010000 { ++ compatible = "rockchip,rk3228-codec"; ++ reg = <0x12010000 0x1000>; ++ clocks = <&cru SCLK_I2S_OUT>, <&cru PCLK_ACODECPHY>, <&cru SCLK_I2S1>; ++ clock-names = "mclk", "pclk", "sclk"; ++ spk-en-gpio = <&gpio1 RK_PA3 GPIO_ACTIVE_HIGH>; ++ status = "disabled"; ++}; +diff --git a/arch/arm/boot/dts/rk322x.dtsi b/arch/arm/boot/dts/rk322x.dtsi +index 75af99c76..c2670d498 100644 +--- a/arch/arm/boot/dts/rk322x.dtsi ++++ b/arch/arm/boot/dts/rk322x.dtsi +@@ -145,6 +145,15 @@ i2s1: i2s1@100b0000 { + status = "disabled"; + }; + ++ codec: codec@12010000 { ++ compatible = "rockchip,rk3228-codec"; ++ reg = <0x12010000 0x1000>; ++ clocks = <&cru SCLK_I2S_OUT>, <&cru PCLK_ACODECPHY>, <&cru SCLK_I2S1>; ++ clock-names = "mclk", "pclk", "sclk"; ++ spk-en-gpio = <&gpio1 RK_PA3 GPIO_ACTIVE_HIGH>; ++ status = "disabled"; ++ }; ++ + i2s0: i2s0@100c0000 { + compatible = "rockchip,rk3228-i2s", "rockchip,rk3066-i2s"; + reg = <0x100c0000 0x4000>; +diff --git a/drivers/clk/rockchip/clk-rk3228.c b/drivers/clk/rockchip/clk-rk3228.c +index a24a35553..69f8c792f 100644 +--- a/drivers/clk/rockchip/clk-rk3228.c ++++ b/drivers/clk/rockchip/clk-rk3228.c +@@ -620,7 +620,7 @@ static struct rockchip_clk_branch rk3228_clk_branches[] __initdata = { + GATE(0, "pclk_sim", "pclk_cpu", 0, RK2928_CLKGATE_CON(10), 3, GFLAGS), + + GATE(0, "pclk_ddrphy", "pclk_phy_pre", 0, RK2928_CLKGATE_CON(10), 3, GFLAGS), +- GATE(0, "pclk_acodecphy", "pclk_phy_pre", 0, RK2928_CLKGATE_CON(10), 5, GFLAGS), ++ GATE(PCLK_ACODECPHY, "pclk_acodecphy", "pclk_phy_pre", 0, RK2928_CLKGATE_CON(10), 5, GFLAGS), + GATE(PCLK_HDMI_PHY, "pclk_hdmiphy", "pclk_phy_pre", 0, RK2928_CLKGATE_CON(10), 7, GFLAGS), + GATE(0, "pclk_vdacphy", "pclk_phy_pre", 0, RK2928_CLKGATE_CON(10), 8, GFLAGS), + GATE(0, "pclk_phy_noc", "pclk_phy_pre", 0, RK2928_CLKGATE_CON(10), 9, GFLAGS), +diff --git a/include/dt-bindings/clock/rk3228-cru.h b/include/dt-bindings/clock/rk3228-cru.h +index de550ea56..30d44ce90 100644 +--- a/include/dt-bindings/clock/rk3228-cru.h ++++ b/include/dt-bindings/clock/rk3228-cru.h +@@ -115,6 +115,7 @@ + #define PCLK_HDMI_CTRL 364 + #define PCLK_HDMI_PHY 365 + #define PCLK_GMAC 367 ++#define PCLK_ACODECPHY 368 + + /* hclk gates */ + #define HCLK_I2S0_8CH 442 +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +index db1607120..5f7f01102 100644 +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -154,6 +154,7 @@ config SND_SOC_ALL_CODECS + imply SND_SOC_PCM5102A + imply SND_SOC_PCM512x_I2C + imply SND_SOC_PCM512x_SPI ++ imply SND_SOC_RK3228 + imply SND_SOC_RK3328 + imply SND_SOC_RK817 + imply SND_SOC_RT274 +@@ -1063,6 +1064,10 @@ config SND_SOC_PCM512x_SPI + select SND_SOC_PCM512x + select REGMAP_SPI + ++config SND_SOC_RK3228 ++ select REGMAP_MMIO ++ tristate "Rockchip RK3228 CODEC" ++ + config SND_SOC_RK3328 + tristate "Rockchip RK3328 audio CODEC" + select REGMAP_MMIO +diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile +index 7bb38c370..232b5c43e 100644 +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -165,6 +165,7 @@ snd-soc-pcm5102a-objs := pcm5102a.o + snd-soc-pcm512x-objs := pcm512x.o + snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o + snd-soc-pcm512x-spi-objs := pcm512x-spi.o ++snd-soc-rk3228-objs := rk3228_codec.o + snd-soc-rk3328-objs := rk3328_codec.o + snd-soc-rk817-objs := rk817_codec.o + snd-soc-rl6231-objs := rl6231.o +@@ -491,6 +492,7 @@ obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o + obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o + obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o + obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o ++obj-$(CONFIG_SND_SOC_RK3228) += snd-soc-rk3228.o + obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o + obj-$(CONFIG_SND_SOC_RK817) += snd-soc-rk817.o + obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o +diff --git a/sound/soc/codecs/rk3228_codec.c b/sound/soc/codecs/rk3228_codec.c +new file mode 100644 +index 000000000..b65307435 +--- /dev/null ++++ b/sound/soc/codecs/rk3228_codec.c +@@ -0,0 +1,545 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// ++// rk3228_codec.c -- rk3228 ALSA Soc Audio driver ++// ++// Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved. ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "rk3228_codec.h" ++ ++/* ++ * volume setting ++ * 0: -39dB ++ * 26: 0dB ++ * 31: 6dB ++ * Step: 1.5dB ++ */ ++#define OUT_VOLUME (0x18) ++#define INITIAL_FREQ (11289600) ++ ++struct rk3228_codec_priv { ++ struct regmap *regmap; ++ struct clk *mclk; ++ struct clk *pclk; ++ struct clk *sclk; ++ struct gpio_desc *spk_en_gpio; ++ int spk_depop_time; /* msec */ ++}; ++ ++static const struct reg_default rk3228_codec_reg_defaults[] = { ++ { CODEC_RESET, 0x03 }, ++ { DAC_INIT_CTRL1, 0x00 }, ++ { DAC_INIT_CTRL2, 0x50 }, ++ { DAC_INIT_CTRL3, 0x0e }, ++ { DAC_PRECHARGE_CTRL, 0x01 }, ++ { DAC_PWR_CTRL, 0x00 }, ++ { DAC_CLK_CTRL, 0x00 }, ++ { HPMIX_CTRL, 0x00 }, ++ { HPOUT_CTRL, 0x00 }, ++ { HPOUTL_GAIN_CTRL, 0x00 }, ++ { HPOUTR_GAIN_CTRL, 0x00 }, ++ { HPOUT_POP_CTRL, 0x11 }, ++}; ++ ++static int rk3228_codec_reset(struct snd_soc_component *component) ++{ ++ struct rk3228_codec_priv *rk3228 = snd_soc_component_get_drvdata(component); ++ ++ regmap_write(rk3228->regmap, CODEC_RESET, 0); ++ mdelay(10); ++ regmap_write(rk3228->regmap, CODEC_RESET, 0x03); ++ ++ return 0; ++} ++ ++static int rk3228_set_dai_fmt(struct snd_soc_dai *dai, ++ unsigned int fmt) ++{ ++ struct snd_soc_component *component = dai->component; ++ struct rk3228_codec_priv *rk3228 = snd_soc_component_get_drvdata(component); ++ unsigned int val = 0; ++ ++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBS_CFS: ++ val |= PIN_DIRECTION_IN | DAC_I2S_MODE_SLAVE; ++ break; ++ case SND_SOC_DAIFMT_CBM_CFM: ++ val |= PIN_DIRECTION_OUT | DAC_I2S_MODE_MASTER; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ regmap_update_bits(rk3228->regmap, DAC_INIT_CTRL1, ++ PIN_DIRECTION_MASK | DAC_I2S_MODE_MASK, val); ++ ++ val = 0; ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_DSP_A: ++ case SND_SOC_DAIFMT_DSP_B: ++ val |= DAC_MODE_PCM; ++ break; ++ case SND_SOC_DAIFMT_I2S: ++ val |= DAC_MODE_I2S; ++ break; ++ case SND_SOC_DAIFMT_RIGHT_J: ++ val |= DAC_MODE_RJM; ++ break; ++ case SND_SOC_DAIFMT_LEFT_J: ++ val |= DAC_MODE_LJM; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ regmap_update_bits(rk3228->regmap, DAC_INIT_CTRL2, ++ DAC_MODE_MASK, val); ++ return 0; ++} ++ ++static void rk3228_analog_output(struct rk3228_codec_priv *rk3228, int mute) ++{ ++ if (rk3228->spk_en_gpio) ++ gpiod_set_value(rk3228->spk_en_gpio, mute); ++} ++ ++static int rk3228_mute_stream(struct snd_soc_dai *dai, int mute, int direction) ++{ ++ struct snd_soc_component *component = dai->component; ++ struct rk3228_codec_priv *rk3228 = snd_soc_component_get_drvdata(component); ++ unsigned int val = 0; ++ ++ if (direction != SNDRV_PCM_STREAM_PLAYBACK) ++ return 0; ++ ++ if (mute) ++ val = HPOUTL_MUTE | HPOUTR_MUTE; ++ else ++ val = HPOUTL_UNMUTE | HPOUTR_UNMUTE; ++ ++ regmap_update_bits(rk3228->regmap, HPOUT_CTRL, ++ HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, val); ++ return 0; ++} ++ ++static int rk3228_codec_power_on(struct snd_soc_component *component, int wait_ms) ++{ ++ struct rk3228_codec_priv *rk3228 = snd_soc_component_get_drvdata(component); ++ ++ regmap_update_bits(rk3228->regmap, DAC_PRECHARGE_CTRL, ++ DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_PRECHARGE); ++ mdelay(10); ++ regmap_update_bits(rk3228->regmap, DAC_PRECHARGE_CTRL, ++ DAC_CHARGE_CURRENT_ALL_MASK, ++ DAC_CHARGE_CURRENT_ALL_ON); ++ ++ mdelay(wait_ms); ++ ++ return 0; ++} ++ ++static int rk3228_codec_power_off(struct snd_soc_component *component, int wait_ms) ++{ ++ struct rk3228_codec_priv *rk3228 = snd_soc_component_get_drvdata(component); ++ ++ regmap_update_bits(rk3228->regmap, DAC_PRECHARGE_CTRL, ++ DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_DISCHARGE); ++ mdelay(10); ++ regmap_update_bits(rk3228->regmap, DAC_PRECHARGE_CTRL, ++ DAC_CHARGE_CURRENT_ALL_MASK, ++ DAC_CHARGE_CURRENT_ALL_ON); ++ ++ mdelay(wait_ms); ++ ++ return 0; ++} ++ ++static struct rk3228_reg_msk_val playback_open_list[] = { ++ { DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_ON }, ++ { DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK, ++ DACL_PATH_REFV_ON | DACR_PATH_REFV_ON }, ++ { DAC_PWR_CTRL, HPOUTL_ZERO_CROSSING_ON | HPOUTR_ZERO_CROSSING_ON, ++ HPOUTL_ZERO_CROSSING_ON | HPOUTR_ZERO_CROSSING_ON }, ++ { HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK, ++ HPOUTR_POP_WORK | HPOUTL_POP_WORK }, ++ { HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_EN | HPMIXR_EN }, ++ { HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK, ++ HPMIXL_INIT_EN | HPMIXR_INIT_EN }, ++ { HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_EN | HPOUTR_EN }, ++ { HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK, ++ HPOUTL_INIT_EN | HPOUTR_INIT_EN }, ++ { DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK, ++ DACL_REFV_ON | DACR_REFV_ON }, ++ { DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK, ++ DACL_CLK_ON | DACR_CLK_ON }, ++ { DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_ON | DACR_ON }, ++ { DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK, ++ DACL_INIT_ON | DACR_INIT_ON }, ++ { DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK, ++ DACL_SELECT | DACR_SELECT }, ++ { HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK, ++ HPMIXL_INIT2_EN | HPMIXR_INIT2_EN }, ++ { HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, ++ HPOUTL_UNMUTE | HPOUTR_UNMUTE }, ++}; ++ ++#define PLAYBACK_OPEN_LIST_LEN ARRAY_SIZE(playback_open_list) ++ ++static int rk3228_codec_open_playback(struct snd_soc_component *component) ++{ ++ struct rk3228_codec_priv *rk3228 = snd_soc_component_get_drvdata(component); ++ int i = 0; ++ ++ regmap_update_bits(rk3228->regmap, DAC_PRECHARGE_CTRL, ++ DAC_CHARGE_CURRENT_ALL_MASK, ++ DAC_CHARGE_CURRENT_I); ++ ++ for (i = 0; i < PLAYBACK_OPEN_LIST_LEN; i++) { ++ regmap_update_bits(rk3228->regmap, ++ playback_open_list[i].reg, ++ playback_open_list[i].msk, ++ playback_open_list[i].val); ++ mdelay(1); ++ } ++ ++ msleep(rk3228->spk_depop_time); ++ rk3228_analog_output(rk3228, 1); ++ ++ regmap_update_bits(rk3228->regmap, HPOUTL_GAIN_CTRL, ++ HPOUTL_GAIN_MASK, OUT_VOLUME); ++ regmap_update_bits(rk3228->regmap, HPOUTR_GAIN_CTRL, ++ HPOUTR_GAIN_MASK, OUT_VOLUME); ++ return 0; ++} ++ ++static struct rk3228_reg_msk_val playback_close_list[] = { ++ { HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK, ++ HPMIXL_INIT2_DIS | HPMIXR_INIT2_DIS }, ++ { DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK, ++ DACL_DESELECT | DACR_DESELECT }, ++ { HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, ++ HPOUTL_MUTE | HPOUTR_MUTE }, ++ { HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK, ++ HPOUTL_INIT_DIS | HPOUTR_INIT_DIS }, ++ { HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_DIS | HPOUTR_DIS }, ++ { HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_DIS | HPMIXR_DIS }, ++ { DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_OFF | DACR_OFF }, ++ { DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK, ++ DACL_CLK_OFF | DACR_CLK_OFF }, ++ { DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK, ++ DACL_REFV_OFF | DACR_REFV_OFF }, ++ { HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK, ++ HPOUTR_POP_XCHARGE | HPOUTL_POP_XCHARGE }, ++ { DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK, ++ DACL_PATH_REFV_OFF | DACR_PATH_REFV_OFF }, ++ { DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_OFF }, ++ { HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK, ++ HPMIXL_INIT_DIS | HPMIXR_INIT_DIS }, ++ { DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK, ++ DACL_INIT_OFF | DACR_INIT_OFF }, ++}; ++ ++#define PLAYBACK_CLOSE_LIST_LEN ARRAY_SIZE(playback_close_list) ++ ++static int rk3228_codec_close_playback(struct snd_soc_component *component) ++{ ++ struct rk3228_codec_priv *rk3228 = snd_soc_component_get_drvdata(component); ++ int i = 0; ++ ++ rk3228_analog_output(rk3228, 0); ++ ++ regmap_update_bits(rk3228->regmap, HPOUTL_GAIN_CTRL, ++ HPOUTL_GAIN_MASK, 0); ++ regmap_update_bits(rk3228->regmap, HPOUTR_GAIN_CTRL, ++ HPOUTR_GAIN_MASK, 0); ++ ++ for (i = 0; i < PLAYBACK_CLOSE_LIST_LEN; i++) { ++ regmap_update_bits(rk3228->regmap, ++ playback_close_list[i].reg, ++ playback_close_list[i].msk, ++ playback_close_list[i].val); ++ mdelay(1); ++ } ++ ++ regmap_update_bits(rk3228->regmap, DAC_PRECHARGE_CTRL, ++ DAC_CHARGE_CURRENT_ALL_MASK, ++ DAC_CHARGE_CURRENT_I); ++ return 0; ++} ++ ++static int rk3228_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ struct rk3228_codec_priv *rk3228 = snd_soc_component_get_drvdata(component); ++ unsigned int val = 0; ++ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S16_LE: ++ val |= DAC_VDL_16BITS; ++ break; ++ case SNDRV_PCM_FORMAT_S20_3LE: ++ val |= DAC_VDL_20BITS; ++ break; ++ case SNDRV_PCM_FORMAT_S24_LE: ++ val |= DAC_VDL_24BITS; ++ break; ++ case SNDRV_PCM_FORMAT_S32_LE: ++ val |= DAC_VDL_32BITS; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ regmap_update_bits(rk3228->regmap, DAC_INIT_CTRL2, DAC_VDL_MASK, val); ++ val = DAC_WL_32BITS | DAC_RST_DIS; ++ regmap_update_bits(rk3228->regmap, DAC_INIT_CTRL3, ++ DAC_WL_MASK | DAC_RST_MASK, val); ++ ++ return 0; ++} ++ ++static int rk3228_pcm_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ ++ return rk3228_codec_open_playback(component); ++} ++ ++static void rk3228_pcm_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ ++ rk3228_codec_close_playback(component); ++} ++ ++static struct snd_soc_dai_ops rk3228_dai_ops = { ++ .hw_params = rk3228_hw_params, ++ .set_fmt = rk3228_set_dai_fmt, ++ .mute_stream = rk3228_mute_stream, ++ .startup = rk3228_pcm_startup, ++ .shutdown = rk3228_pcm_shutdown, ++}; ++ ++static struct snd_soc_dai_driver rk3228_dai[] = { ++ { ++ .name = "rk3228-hifi", ++ .id = RK3228_HIFI, ++ .playback = { ++ .stream_name = "HIFI Playback", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = (SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S20_3LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE), ++ }, ++ /*.capture = { ++ .stream_name = "HIFI Capture", ++ .channels_min = 2, ++ .channels_max = 8, ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = (SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S20_3LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE), ++ },*/ ++ .ops = &rk3228_dai_ops, ++ }, ++}; ++ ++static int rk3228_codec_probe(struct snd_soc_component *component) ++{ ++ rk3228_codec_reset(component); ++ rk3228_codec_power_on(component, 0); ++ ++ return 0; ++} ++ ++static void rk3228_codec_remove(struct snd_soc_component *component) ++{ ++ rk3228_codec_close_playback(component); ++ rk3228_codec_power_off(component, 0); ++} ++ ++static struct snd_soc_component_driver soc_codec_dev_rk3228 = { ++ .probe = rk3228_codec_probe, ++ .remove = rk3228_codec_remove, ++}; ++ ++static bool rk3228_codec_write_read_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case CODEC_RESET: ++ case DAC_INIT_CTRL1: ++ case DAC_INIT_CTRL2: ++ case DAC_INIT_CTRL3: ++ case DAC_PRECHARGE_CTRL: ++ case DAC_PWR_CTRL: ++ case DAC_CLK_CTRL: ++ case HPMIX_CTRL: ++ case DAC_SELECT: ++ case HPOUT_CTRL: ++ case HPOUTL_GAIN_CTRL: ++ case HPOUTR_GAIN_CTRL: ++ case HPOUT_POP_CTRL: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool rk3228_codec_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case CODEC_RESET: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static const struct regmap_config rk3228_codec_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = HPOUT_POP_CTRL, ++ .writeable_reg = rk3228_codec_write_read_reg, ++ .readable_reg = rk3228_codec_write_read_reg, ++ .volatile_reg = rk3228_codec_volatile_reg, ++ .reg_defaults = rk3228_codec_reg_defaults, ++ .num_reg_defaults = ARRAY_SIZE(rk3228_codec_reg_defaults), ++ .cache_type = REGCACHE_FLAT, ++}; ++ ++#ifdef CONFIG_OF ++static const struct of_device_id rk3228codec_of_match[] = { ++ { .compatible = "rockchip,rk3228-codec", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rk3228codec_of_match); ++#endif ++ ++static int rk3228_platform_probe(struct platform_device *pdev) ++{ ++ struct device_node *rk3228_np = pdev->dev.of_node; ++ struct rk3228_codec_priv *rk3228; ++ struct resource *res; ++ void __iomem *base; ++ int ret = 0; ++ ++ rk3228 = devm_kzalloc(&pdev->dev, sizeof(*rk3228), GFP_KERNEL); ++ if (!rk3228) ++ return -ENOMEM; ++ ++ rk3228->mclk = devm_clk_get(&pdev->dev, "mclk"); ++ if (PTR_ERR(rk3228->mclk) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ ++ rk3228->pclk = devm_clk_get(&pdev->dev, "pclk"); ++ if (IS_ERR(rk3228->pclk)) ++ return PTR_ERR(rk3228->pclk); ++ ++ rk3228->sclk = devm_clk_get(&pdev->dev, "sclk"); ++ if (IS_ERR(rk3228->sclk)) ++ return PTR_ERR(rk3228->sclk); ++ ++ rk3228->spk_en_gpio = devm_gpiod_get_optional(&pdev->dev, ++ "spk-en", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(rk3228->spk_en_gpio)) ++ return PTR_ERR(rk3228->spk_en_gpio); ++ ++ ret = of_property_read_u32(rk3228_np, "spk-depop-time-ms", ++ &rk3228->spk_depop_time); ++ if (ret < 0) { ++ dev_info(&pdev->dev, "spk_depop_time use default value.\n"); ++ rk3228->spk_depop_time = 100; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ ret = clk_prepare_enable(rk3228->mclk); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(rk3228->pclk); ++ if (ret < 0) ++ goto err_pclk; ++ ++ ret = clk_prepare_enable(rk3228->sclk); ++ if (ret) ++ goto err_sclk; ++ ++ clk_set_rate(rk3228->sclk, INITIAL_FREQ); ++ ++ rk3228->regmap = devm_regmap_init_mmio(&pdev->dev, base, ++ &rk3228_codec_regmap_config); ++ if (IS_ERR(rk3228->regmap)) { ++ ret = PTR_ERR(rk3228->regmap); ++ goto err_clk; ++ } ++ ++ platform_set_drvdata(pdev, rk3228); ++ ++ ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_rk3228, ++ rk3228_dai, ARRAY_SIZE(rk3228_dai)); ++ if (!ret) ++ return 0; ++ ++err_clk: ++ clk_disable_unprepare(rk3228->sclk); ++err_sclk: ++ clk_disable_unprepare(rk3228->pclk); ++err_pclk: ++ clk_disable_unprepare(rk3228->mclk); ++ ++ return ret; ++} ++ ++static int rk3228_platform_remove(struct platform_device *pdev) ++{ ++ struct rk3228_codec_priv *rk3228 = platform_get_drvdata(pdev); ++ ++ if (!IS_ERR(rk3228->mclk)) ++ clk_disable_unprepare(rk3228->mclk); ++ ++ if (!IS_ERR(rk3228->pclk)) ++ clk_disable_unprepare(rk3228->pclk); ++ ++ if (!IS_ERR(rk3228->sclk)) ++ clk_disable_unprepare(rk3228->sclk); ++ ++ return 0; ++} ++ ++static struct platform_driver rk3228_codec_driver = { ++ .driver = { ++ .name = "rk3228-codec", ++ .of_match_table = of_match_ptr(rk3228codec_of_match), ++ }, ++ .probe = rk3228_platform_probe, ++ .remove = rk3228_platform_remove, ++}; ++module_platform_driver(rk3228_codec_driver); ++ ++MODULE_AUTHOR("Sugar Zhang "); ++MODULE_DESCRIPTION("ASoC rk3228 codec driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/sound/soc/codecs/rk3228_codec.h b/sound/soc/codecs/rk3228_codec.h +new file mode 100644 +index 000000000..7283d0ba8 +--- /dev/null ++++ b/sound/soc/codecs/rk3228_codec.h +@@ -0,0 +1,218 @@ ++/* ++ * rk3228_codec.h -- rk3228 ALSA Soc Audio driver ++ * ++ * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ */ ++ ++#ifndef _RK3228_CODEC_H ++#define _RK3228_CODEC_H ++ ++/* codec register */ ++#define CODEC_RESET (0x00 << 2) ++#define DAC_INIT_CTRL1 (0x03 << 2) ++#define DAC_INIT_CTRL2 (0x04 << 2) ++#define DAC_INIT_CTRL3 (0x05 << 2) ++#define DAC_PRECHARGE_CTRL (0x22 << 2) ++#define DAC_PWR_CTRL (0x23 << 2) ++#define DAC_CLK_CTRL (0x24 << 2) ++#define HPMIX_CTRL (0x25 << 2) ++#define DAC_SELECT (0x26 << 2) ++#define HPOUT_CTRL (0x27 << 2) ++#define HPOUTL_GAIN_CTRL (0x28 << 2) ++#define HPOUTR_GAIN_CTRL (0x29 << 2) ++#define HPOUT_POP_CTRL (0x2a << 2) ++ ++/* REG00: CODEC_RESET */ ++#define PWR_RST_BYPASS_DIS BIT(6) ++#define PWR_RST_BYPASS_EN BIT(6) ++#define DIG_CORE_RST (0 << 1) ++#define DIG_CORE_WORK BIT(1) ++#define SYS_RST (0) ++#define SYS_WORK BIT(0) ++ ++/* REG03: DAC_INIT_CTRL1 */ ++#define PIN_DIRECTION_MASK BIT(5) ++#define PIN_DIRECTION_IN (0 << 5) ++#define PIN_DIRECTION_OUT BIT(5) ++#define DAC_I2S_MODE_MASK BIT(4) ++#define DAC_I2S_MODE_SLAVE (0 << 4) ++#define DAC_I2S_MODE_MASTER BIT(4) ++ ++/* REG04: DAC_INIT_CTRL2 */ ++#define DAC_I2S_LRP_MASK BIT(7) ++#define DAC_I2S_LRP_NORMAL (0 << 7) ++#define DAC_I2S_LRP_REVERSAL BIT(7) ++#define DAC_VDL_MASK (3 << 5) ++#define DAC_VDL_16BITS (0 << 5) ++#define DAC_VDL_20BITS BIT(5) ++#define DAC_VDL_24BITS (2 << 5) ++#define DAC_VDL_32BITS (3 << 5) ++#define DAC_MODE_MASK (3 << 3) ++#define DAC_MODE_RJM (0 << 3) ++#define DAC_MODE_LJM BIT(3) ++#define DAC_MODE_I2S (2 << 3) ++#define DAC_MODE_PCM (3 << 3) ++#define DAC_LR_SWAP_MASK BIT(2) ++#define DAC_LR_SWAP_DIS (0 << 2) ++#define DAC_LR_SWAP_EN BIT(2) ++ ++/* REG05: DAC_INIT_CTRL3 */ ++#define DAC_WL_MASK (3 << 2) ++#define DAC_WL_16BITS (0 << 2) ++#define DAC_WL_20BITS BIT(2) ++#define DAC_WL_24BITS (2 << 2) ++#define DAC_WL_32BITS (3 << 2) ++#define DAC_RST_MASK BIT(1) ++#define DAC_RST_EN (0 << 1) ++#define DAC_RST_DIS BIT(1) ++#define DAC_BCP_MASK BIT(0) ++#define DAC_BCP_NORMAL (0 << 0) ++#define DAC_BCP_REVERSAL BIT(0) ++ ++/* REG22: DAC_PRECHARGE_CTRL */ ++#define DAC_CHARGE_PRECHARGE BIT(7) ++#define DAC_CHARGE_DISCHARGE (0 << 7) ++#define DAC_CHARGE_XCHARGE_MASK BIT(7) ++#define DAC_CHARGE_CURRENT_64I BIT(6) ++#define DAC_CHARGE_CURRENT_64I_MASK BIT(6) ++#define DAC_CHARGE_CURRENT_32I BIT(5) ++#define DAC_CHARGE_CURRENT_32I_MASK BIT(5) ++#define DAC_CHARGE_CURRENT_16I BIT(4) ++#define DAC_CHARGE_CURRENT_16I_MASK BIT(4) ++#define DAC_CHARGE_CURRENT_08I BIT(3) ++#define DAC_CHARGE_CURRENT_08I_MASK BIT(3) ++#define DAC_CHARGE_CURRENT_04I BIT(2) ++#define DAC_CHARGE_CURRENT_04I_MASK BIT(2) ++#define DAC_CHARGE_CURRENT_02I BIT(1) ++#define DAC_CHARGE_CURRENT_02I_MASK BIT(1) ++#define DAC_CHARGE_CURRENT_I BIT(0) ++#define DAC_CHARGE_CURRENT_I_MASK BIT(0) ++#define DAC_CHARGE_CURRENT_ALL_MASK (0x7f) ++#define DAC_CHARGE_CURRENT_ALL_OFF (0x0) ++#define DAC_CHARGE_CURRENT_ALL_ON (0x7f) ++ ++/* REG23: DAC_PWR_CTRL */ ++#define DAC_PWR_OFF (0 << 6) ++#define DAC_PWR_ON BIT(6) ++#define DAC_PWR_MASK BIT(6) ++#define DACL_PATH_REFV_OFF (0 << 5) ++#define DACL_PATH_REFV_ON BIT(5) ++#define DACL_PATH_REFV_MASK BIT(5) ++#define HPOUTL_ZERO_CROSSING_OFF (0 << 4) ++#define HPOUTL_ZERO_CROSSING_ON BIT(4) ++#define DACR_PATH_REFV_OFF (0 << 1) ++#define DACR_PATH_REFV_ON BIT(1) ++#define DACR_PATH_REFV_MASK BIT(1) ++#define HPOUTR_ZERO_CROSSING_OFF (0 << 0) ++#define HPOUTR_ZERO_CROSSING_ON BIT(0) ++ ++/* REG24: DAC_CLK_CTRL */ ++#define DACL_REFV_OFF (0 << 7) ++#define DACL_REFV_ON BIT(7) ++#define DACL_REFV_MASK BIT(7) ++#define DACL_CLK_OFF (0 << 6) ++#define DACL_CLK_ON BIT(6) ++#define DACL_CLK_MASK BIT(6) ++#define DACL_OFF (0 << 5) ++#define DACL_ON BIT(5) ++#define DACL_MASK BIT(5) ++#define DACL_INIT_OFF (0 << 4) ++#define DACL_INIT_ON BIT(4) ++#define DACL_INIT_MASK BIT(4) ++#define DACR_REFV_OFF (0 << 3) ++#define DACR_REFV_ON BIT(3) ++#define DACR_REFV_MASK BIT(3) ++#define DACR_CLK_OFF (0 << 2) ++#define DACR_CLK_ON BIT(2) ++#define DACR_CLK_MASK BIT(2) ++#define DACR_OFF (0 << 1) ++#define DACR_ON BIT(1) ++#define DACR_MASK BIT(1) ++#define DACR_INIT_OFF (0 << 0) ++#define DACR_INIT_ON BIT(0) ++#define DACR_INIT_MASK BIT(0) ++ ++/* REG25: HPMIX_CTRL*/ ++#define HPMIXL_DIS (0 << 6) ++#define HPMIXL_EN BIT(6) ++#define HPMIXL_MASK BIT(6) ++#define HPMIXL_INIT_DIS (0 << 5) ++#define HPMIXL_INIT_EN BIT(5) ++#define HPMIXL_INIT_MASK BIT(5) ++#define HPMIXL_INIT2_DIS (0 << 4) ++#define HPMIXL_INIT2_EN BIT(4) ++#define HPMIXL_INIT2_MASK BIT(4) ++#define HPMIXR_DIS (0 << 2) ++#define HPMIXR_EN BIT(2) ++#define HPMIXR_MASK BIT(2) ++#define HPMIXR_INIT_DIS (0 << 1) ++#define HPMIXR_INIT_EN BIT(1) ++#define HPMIXR_INIT_MASK BIT(1) ++#define HPMIXR_INIT2_DIS (0 << 0) ++#define HPMIXR_INIT2_EN BIT(0) ++#define HPMIXR_INIT2_MASK BIT(0) ++ ++/* REG26: DAC_SELECT */ ++#define DACL_SELECT BIT(4) ++#define DACL_SELECT_MASK BIT(4) ++#define DACL_DESELECT (0 << 4) ++#define DACR_SELECT BIT(0) ++#define DACR_SELECT_MASK BIT(0) ++#define DACR_DESELECT (0 << 0) ++ ++/* REG27: HPOUT_CTRL */ ++#define HPOUTL_DIS (0 << 7) ++#define HPOUTL_EN BIT(7) ++#define HPOUTL_MASK BIT(7) ++#define HPOUTL_INIT_DIS (0 << 6) ++#define HPOUTL_INIT_EN BIT(6) ++#define HPOUTL_INIT_MASK BIT(6) ++#define HPOUTL_MUTE (0 << 5) ++#define HPOUTL_UNMUTE BIT(5) ++#define HPOUTL_MUTE_MASK BIT(5) ++#define HPOUTR_DIS (0 << 4) ++#define HPOUTR_EN BIT(4) ++#define HPOUTR_MASK BIT(4) ++#define HPOUTR_INIT_DIS (0 << 3) ++#define HPOUTR_INIT_EN BIT(3) ++#define HPOUTR_INIT_MASK BIT(3) ++#define HPOUTR_MUTE (0 << 2) ++#define HPOUTR_UNMUTE BIT(2) ++#define HPOUTR_MUTE_MASK BIT(2) ++ ++/* REG28: HPOUTL_GAIN_CTRL */ ++#define HPOUTL_GAIN_MASK (0X1f << 0) ++ ++/* REG29: HPOUTR_GAIN_CTRL */ ++#define HPOUTR_GAIN_MASK (0X1f << 0) ++ ++/* REG2a: HPOUT_POP_CTRL */ ++#define HPOUTR_POP_XCHARGE BIT(4) ++#define HPOUTR_POP_WORK (2 << 4) ++#define HPOUTR_POP_MASK (3 << 4) ++#define HPOUTL_POP_XCHARGE BIT(0) ++#define HPOUTL_POP_WORK (2 << 0) ++#define HPOUTL_POP_MASK (3 << 0) ++ ++#define RK3228_HIFI (0) ++ ++struct rk3228_reg_msk_val { ++ unsigned int reg; ++ unsigned int msk; ++ unsigned int val; ++}; ++ ++#endif +-- +2.25.1 + diff --git a/patch/kernel/archive/rk322x-5.14/board-rk322x-box.patch b/patch/kernel/archive/rk322x-5.14/board-rk322x-box.patch index 25b763080..a35a5ed91 100644 --- a/patch/kernel/archive/rk322x-5.14/board-rk322x-box.patch +++ b/patch/kernel/archive/rk322x-5.14/board-rk322x-box.patch @@ -1,9 +1,9 @@ diff --git a/arch/arm/boot/dts/rk322x-box.dts b/arch/arm/boot/dts/rk322x-box.dts new file mode 100644 -index 000000000..ab10b4f36 +index 000000000..22c401d9c --- /dev/null +++ b/arch/arm/boot/dts/rk322x-box.dts -@@ -0,0 +1,474 @@ +@@ -0,0 +1,498 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) + +/dts-v1/; @@ -82,6 +82,20 @@ index 000000000..ab10b4f36 + }; + }; + ++ analog-sound { ++ compatible = "simple-audio-card"; ++ simple-audio-card,format = "i2s"; ++ simple-audio-card,mclk-fs = <256>; ++ simple-audio-card,name = "analog"; ++ simple-audio-card,cpu { ++ sound-dai = <&i2s1>; ++ }; ++ ++ simple-audio-card,codec { ++ sound-dai = <&codec>; ++ }; ++ }; ++ + vcc_sys: vcc-sys-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_sys"; @@ -289,6 +303,16 @@ index 000000000..ab10b4f36 + status = "okay"; +}; + ++&i2s1 { ++ #sound-dai-cells = <0>; ++ status = "okay"; ++}; ++ ++&codec { ++ #sound-dai-cells = <0>; ++ status = "okay"; ++}; ++ +&u2phy0 { + u2phy0_host: host-port { + phy-supply = <&vcc_host>;