diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 6f53c96779ff..38c79e21630e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -161,6 +161,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RK3228 imply SND_SOC_RK3308 imply SND_SOC_RK3328 + imply SND_SOC_RK3528 imply SND_SOC_RK730 imply SND_SOC_RK817 imply SND_SOC_RT274 @@ -1102,6 +1103,10 @@ config SND_SOC_RK3328 tristate "Rockchip RK3328 audio CODEC" select REGMAP_MMIO +config SND_SOC_RK3528 + tristate "Rockchip RK3528 audio CODEC" + select REGMAP_MMIO + config SND_SOC_RK730 tristate "Rockchip RK730 CODEC" select REGMAP_I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d7e238d7091d..204868b9c308 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -168,6 +168,7 @@ snd-soc-rk312x-objs := rk312x_codec.o snd-soc-rk3228-objs := rk3228_codec.o snd-soc-rk3308-objs := rk3308_codec.o snd-soc-rk3328-objs := rk3328_codec.o +snd-soc-rk3528-objs := rk3528_codec.o snd-soc-rk730-objs := rk730.o snd-soc-rk817-objs := rk817_codec.o snd-soc-rk-codec-digital-objs := rk_codec_digital.o @@ -493,6 +494,7 @@ obj-$(CONFIG_SND_SOC_RK312X) += snd-soc-rk312x.o obj-$(CONFIG_SND_SOC_RK3228) += snd-soc-rk3228.o obj-$(CONFIG_SND_SOC_RK3308) += snd-soc-rk3308.o obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o +obj-$(CONFIG_SND_SOC_RK3528) += snd-soc-rk3528.o obj-$(CONFIG_SND_SOC_RK730) += snd-soc-rk730.o obj-$(CONFIG_SND_SOC_RK817) += snd-soc-rk817.o obj-$(CONFIG_SND_SOC_RK_CODEC_DIGITAL) += snd-soc-rk-codec-digital.o diff --git a/sound/soc/codecs/rk3528_codec.c b/sound/soc/codecs/rk3528_codec.c new file mode 100644 index 000000000000..4e55c3e623cb --- /dev/null +++ b/sound/soc/codecs/rk3528_codec.c @@ -0,0 +1,771 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * rk3528_codec.c - Rockchip RK3528 SoC Codec Driver + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rk3528_codec.h" + +#define CODEC_DRV_NAME "rk3528-acodec" + +struct rk3528_codec_priv { + const struct device *plat_dev; + struct reset_control *reset; + struct regmap *regmap; + struct clk *pclk; + struct clk *mclk; + struct gpio_desc *pa_ctl_gpio; + struct snd_soc_component *component; + u32 pa_ctl_delay_ms; +}; + +static void rk3528_codec_pa_ctrl(struct rk3528_codec_priv *rk3528, bool on) +{ + if (!rk3528->pa_ctl_gpio) + return; + + if (on) { + gpiod_direction_output(rk3528->pa_ctl_gpio, on); + msleep(rk3528->pa_ctl_delay_ms); + } else { + gpiod_direction_output(rk3528->pa_ctl_gpio, on); + } +} + +static int rk3528_codec_reset(struct snd_soc_component *component) +{ + struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component); + + reset_control_assert(rk3528->reset); + usleep_range(10000, 11000); /* estimated value */ + reset_control_deassert(rk3528->reset); + + regmap_update_bits(rk3528->regmap, ACODEC_DIG00, + ACODEC_DAC_RST_MASK | + ACODEC_SYS_RST_MASK, + ACODEC_DAC_RST_N | + ACODEC_SYS_RST_N); + regmap_update_bits(rk3528->regmap, ACODEC_DIG02, + ACODEC_DAC_I2S_RST_MASK, + ACODEC_DAC_I2S_RST_N); + usleep_range(10000, 11000); /* estimated value */ + regmap_update_bits(rk3528->regmap, ACODEC_DIG00, + ACODEC_DAC_RST_MASK | + ACODEC_SYS_RST_MASK, + ACODEC_DAC_RST_P | + ACODEC_SYS_RST_P); + regmap_update_bits(rk3528->regmap, ACODEC_DIG02, + ACODEC_DAC_I2S_RST_MASK, + ACODEC_DAC_I2S_RST_P); + + return 0; +} + +static int rk3528_codec_power_on(struct rk3528_codec_priv *rk3528) +{ + /* vendor step 0, Supply the power of the digital part and reset the audio codec. */ + /* vendor step 1 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA08, + ACODEC_DAC_L_POP_CTRL_MASK, + ACODEC_DAC_L_POP_CTRL_ON); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0C, + ACODEC_DAC_R_POP_CTRL_MASK, + ACODEC_DAC_R_POP_CTRL_ON); + /* vendor step 2 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA01, + ACODEC_VREF_SEL_MASK, ACODEC_VREF_SEL(0xff)); + /* vendor step 3, supply the power of the analog part */ + /* vendor step 4 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA00, + ACODEC_VREF_MASK, ACODEC_VREF_EN); + + /* vendor step 5, Wait until the voltage of VCM keeps stable at the AVDD/2. */ + usleep_range(20000, 22000); + /* vendor step 6 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA01, + ACODEC_VREF_SEL_MASK, ACODEC_VREF_SEL(2)); + return 0; +} + +static int rk3528_codec_power_off(struct rk3528_codec_priv *rk3528) +{ + /* + * vendor step 0. Keep the power on and disable the DAC and ADC path. + */ + /* vendor step 1 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA01, + ACODEC_VREF_SEL_MASK, ACODEC_VREF_SEL(0xff)); + /* vendor step 2 */ + /* vendor step 3 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA00, + ACODEC_VREF_MASK, ACODEC_VREF_DIS); + /* vendor step 4. Wait until the voltage of VCM keep stable at AGND. */ + usleep_range(20000, 22000); + /* vendor step 5, power off the analog power supply */ + /* vendor step 6, power off the digital power supply */ + + return 0; +} + +static int rk3528_codec_dac_enable(struct rk3528_codec_priv *rk3528) +{ + /* vendor step 0, power up the codec and input the mute signal */ + /* vendor step 1 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA00, + ACODEC_IBIAS_DAC_MASK, + ACODEC_IBIAS_DAC_EN); + /* vendor step 2 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA08, + ACODEC_DAC_L_BUF_MASK, + ACODEC_DAC_L_BUF_EN); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0C, + ACODEC_DAC_R_BUF_MASK, + ACODEC_DAC_R_BUF_EN); + /* vendor step 3 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA08, + ACODEC_DAC_L_POP_CTRL_MASK, + ACODEC_DAC_L_POP_CTRL_ON); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0C, + ACODEC_DAC_R_POP_CTRL_MASK, + ACODEC_DAC_R_POP_CTRL_ON); + /* vendor step 4 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA09, + ACODEC_LINEOUT_L_MASK, + ACODEC_LINEOUT_L_EN); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0D, + ACODEC_LINEOUT_R_MASK, + ACODEC_LINEOUT_R_EN); + /* vendor step 5 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA09, + ACODEC_LINEOUT_L_INIT_MASK, + ACODEC_LINEOUT_L_INIT_WORK); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0D, + ACODEC_LINEOUT_R_INIT_MASK, + ACODEC_LINEOUT_R_INIT_WORK); + /* vendor step 6 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA08, + ACODEC_DAC_L_VREF_MASK, + ACODEC_DAC_L_VREF_EN); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0C, + ACODEC_DAC_R_VREF_MASK, + ACODEC_DAC_R_VREF_EN); + /* vendor step 7 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA08, + ACODEC_DAC_L_CLK_MASK, + ACODEC_DAC_L_CLK_EN); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0C, + ACODEC_DAC_R_CLK_MASK, + ACODEC_DAC_R_CLK_EN); + /* vendor step 8 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA08, + ACODEC_DAC_L_MASK, + ACODEC_DAC_L_EN); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0C, + ACODEC_DAC_R_MASK, + ACODEC_DAC_R_EN); + /* vendor step 9 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA08, + ACODEC_DAC_L_INIT_MASK, + ACODEC_DAC_L_WORK); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0C, + ACODEC_DAC_R_INIT_MASK, + ACODEC_DAC_R_WORK); + /* vendor step 10 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA09, + ACODEC_LINEOUT_L_MUTE_MASK, + ACODEC_LINEOUT_L_WORK); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0D, + ACODEC_LINEOUT_R_MUTE_MASK, + ACODEC_LINEOUT_R_WORK); + /* vendor step 11, select the gain */ + /* vendor step 12, play music */ + + return 0; +} + +static int rk3528_codec_dac_disable(struct rk3528_codec_priv *rk3528) +{ + /* vendor step 0, keep the dac channel work and input the mute signal */ + /* vendor step 1, select the gain */ + /* vendor step 2 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA09, + ACODEC_LINEOUT_L_MUTE_MASK, + ACODEC_LINEOUT_L_MUTE); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0D, + ACODEC_LINEOUT_R_MUTE_MASK, + ACODEC_LINEOUT_R_MUTE); + /* vendor step 3 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA09, + ACODEC_LINEOUT_L_INIT_MASK, + ACODEC_LINEOUT_L_INIT); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0D, + ACODEC_LINEOUT_R_INIT_MASK, + ACODEC_LINEOUT_R_INIT); + /* vendor step 4 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA09, + ACODEC_LINEOUT_L_MASK, + ACODEC_LINEOUT_L_DIS); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0D, + ACODEC_LINEOUT_R_MASK, + ACODEC_LINEOUT_R_DIS); + /* vendor step 5 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA08, + ACODEC_DAC_L_MASK, + ACODEC_DAC_L_DIS); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0C, + ACODEC_DAC_R_MASK, + ACODEC_DAC_R_DIS); + /* vendor step 6 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA08, + ACODEC_DAC_L_CLK_MASK, + ACODEC_DAC_L_CLK_DIS); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0C, + ACODEC_DAC_R_CLK_MASK, + ACODEC_DAC_R_CLK_DIS); + /* vendor step 7 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA08, + ACODEC_DAC_L_VREF_MASK, + ACODEC_DAC_L_VREF_DIS); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0C, + ACODEC_DAC_R_VREF_MASK, + ACODEC_DAC_R_VREF_DIS); + /* vendor step 8 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA08, + ACODEC_DAC_L_POP_CTRL_MASK, + ACODEC_DAC_L_POP_CTRL_OFF); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0C, + ACODEC_DAC_R_POP_CTRL_MASK, + ACODEC_DAC_R_POP_CTRL_OFF); + /* vendor step 9 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA08, + ACODEC_DAC_L_BUF_MASK, + ACODEC_DAC_L_BUF_DIS); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0C, + ACODEC_DAC_R_BUF_MASK, + ACODEC_DAC_R_BUF_DIS); + /* vendor step 10 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA00, + ACODEC_IBIAS_DAC_MASK, + ACODEC_IBIAS_DAC_DIS); + /* vendor step 9 */ + regmap_update_bits(rk3528->regmap, ACODEC_ANA08, + ACODEC_DAC_L_INIT_MASK, + ACODEC_DAC_L_INIT); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0C, + ACODEC_DAC_R_INIT_MASK, + ACODEC_DAC_R_INIT); + + return 0; +} + +static int rk3528_codec_dac_dig_config(struct rk3528_codec_priv *rk3528, + struct snd_pcm_hw_params *params) +{ + unsigned int dac_aif1 = 0; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + dac_aif1 |= ACODEC_DAC_I2S_16B; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + dac_aif1 |= ACODEC_DAC_I2S_20B; + break; + case SNDRV_PCM_FORMAT_S24_LE: + dac_aif1 |= ACODEC_DAC_I2S_24B; + break; + case SNDRV_PCM_FORMAT_S32_LE: + dac_aif1 |= ACODEC_DAC_I2S_32B; + break; + default: + return -EINVAL; + } + + dac_aif1 |= ACODEC_DAC_I2S_I2S; + regmap_update_bits(rk3528->regmap, ACODEC_DIG01, + ACODEC_DAC_I2S_WL_MASK | + ACODEC_DAC_I2S_FMT_MASK, + dac_aif1); + regmap_update_bits(rk3528->regmap, ACODEC_DIG02, + ACODEC_DAC_I2S_RST_MASK, + ACODEC_DAC_I2S_RST_P); + + return 0; +} + +static int rk3528_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component); + unsigned int dac_aif1 = 0, dac_aif2 = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + dac_aif2 |= ACODEC_DAC_I2S_MST_FUNC_SLAVE; + dac_aif2 |= ACODEC_DAC_I2S_MST_IO_SLAVE; + break; + case SND_SOC_DAIFMT_CBM_CFM: + dac_aif2 |= ACODEC_DAC_I2S_MST_FUNC_MASTER; + dac_aif2 |= ACODEC_DAC_I2S_MST_IO_MASTER; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dac_aif1 |= ACODEC_DAC_I2S_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + dac_aif1 |= ACODEC_DAC_I2S_LJM; + break; + default: + return -EINVAL; + } + + regmap_update_bits(rk3528->regmap, ACODEC_DIG01, + ACODEC_DAC_I2S_FMT_MASK, + dac_aif1); + regmap_update_bits(rk3528->regmap, ACODEC_DIG02, + ACODEC_DAC_I2S_MST_FUNC_MASK | + ACODEC_DAC_I2S_MST_IO_MASK, + dac_aif2); + + return 0; +} + +static int rk3528_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (mute) { + /* Mute DAC LINEOUT */ + regmap_update_bits(rk3528->regmap, + ACODEC_ANA09, + ACODEC_LINEOUT_L_MUTE_MASK, + ACODEC_LINEOUT_L_MUTE); + regmap_update_bits(rk3528->regmap, + ACODEC_ANA0D, + ACODEC_LINEOUT_R_MUTE_MASK, + ACODEC_LINEOUT_R_MUTE); + rk3528_codec_pa_ctrl(rk3528, false); + } else { + /* Unmute DAC LINEOUT */ + regmap_update_bits(rk3528->regmap, + ACODEC_ANA09, + ACODEC_LINEOUT_L_MUTE_MASK, + ACODEC_LINEOUT_L_WORK); + regmap_update_bits(rk3528->regmap, + ACODEC_ANA0D, + ACODEC_LINEOUT_R_MUTE_MASK, + ACODEC_LINEOUT_R_WORK); + rk3528_codec_pa_ctrl(rk3528, true); + } + } + + return 0; +} + +static int rk3528_codec_default_gains(struct rk3528_codec_priv *rk3528) +{ + /* Prepare DAC gains */ + /* set LINEOUT default gains */ + regmap_update_bits(rk3528->regmap, ACODEC_DIG06, + ACODEC_DAC_DIG_GAIN_MASK, + ACODEC_DAC_DIG_GAIN(ACODEC_DAC_DIG_0DB)); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0B, + ACODEC_LINEOUT_L_GAIN_MASK, + ACODEC_DAC_LINEOUT_GAIN_0DB); + regmap_update_bits(rk3528->regmap, ACODEC_ANA0F, + ACODEC_LINEOUT_R_GAIN_MASK, + ACODEC_DAC_LINEOUT_GAIN_0DB); + + return 0; +} + +static int rk3528_codec_open_playback(struct rk3528_codec_priv *rk3528) +{ + rk3528_codec_dac_enable(rk3528); + + return 0; +} + +static int rk3528_codec_close_playback(struct rk3528_codec_priv *rk3528) +{ + rk3528_codec_dac_disable(rk3528); + return 0; +} + +static int rk3528_codec_dlp_down(struct rk3528_codec_priv *rk3528) +{ + return 0; +} + +static int rk3528_codec_dlp_up(struct rk3528_codec_priv *rk3528) +{ + rk3528_codec_power_on(rk3528); + + return 0; +} + +static int rk3528_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 rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + rk3528_codec_open_playback(rk3528); + rk3528_codec_dac_dig_config(rk3528, params); + } + + return 0; +} + +static void rk3528_pcm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rk3528_codec_close_playback(rk3528); + + regcache_cache_only(rk3528->regmap, false); + regcache_sync(rk3528->regmap); +} + +static int rk3528_codec_prepare(struct rk3528_codec_priv *rk3528) +{ + /* Clear registers for ADC and DAC */ + rk3528_codec_close_playback(rk3528); + rk3528_codec_default_gains(rk3528); + + return 0; +} + +static int rk3528_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component); + int ret; + + if (!freq) + return 0; + + ret = clk_set_rate(rk3528->mclk, freq); + if (ret) + dev_err(rk3528->plat_dev, "Failed to set mclk %d\n", ret); + + return ret; +} + +static const struct snd_soc_dai_ops rk3528_dai_ops = { + .hw_params = rk3528_hw_params, + .set_fmt = rk3528_set_dai_fmt, + .mute_stream = rk3528_mute_stream, + .shutdown = rk3528_pcm_shutdown, + .set_sysclk = rk3528_set_sysclk, +}; + +static struct snd_soc_dai_driver rk3528_dai[] = { + { + .name = "rk3528-hifi", + .id = ACODEC_HIFI, + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + }, + .ops = &rk3528_dai_ops, + }, +}; + +static int rk3528_codec_probe(struct snd_soc_component *component) +{ + struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component); + + rk3528->component = component; + rk3528_codec_reset(component); + rk3528_codec_dlp_up(rk3528); + rk3528_codec_prepare(rk3528); + regcache_cache_only(rk3528->regmap, false); + regcache_sync(rk3528->regmap); + + return 0; +} + +static void rk3528_codec_remove(struct snd_soc_component *component) +{ + struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component); + + rk3528_codec_pa_ctrl(rk3528, false); + rk3528_codec_power_off(rk3528); + regcache_cache_only(rk3528->regmap, false); + regcache_sync(rk3528->regmap); +} + +static int rk3528_codec_suspend(struct snd_soc_component *component) +{ + struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component); + + rk3528_codec_dlp_down(rk3528); + regcache_cache_only(rk3528->regmap, true); + clk_disable_unprepare(rk3528->mclk); + clk_disable_unprepare(rk3528->pclk); + + return 0; +} + +static int rk3528_codec_resume(struct snd_soc_component *component) +{ + struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component); + int ret = 0; + + ret = clk_prepare_enable(rk3528->pclk); + if (ret < 0) { + dev_err(rk3528->plat_dev, + "Failed to enable acodec pclk: %d\n", ret); + goto pclk_error; + } + + ret = clk_prepare_enable(rk3528->mclk); + if (ret < 0) { + dev_err(rk3528->plat_dev, + "Failed to enable acodec mclk: %d\n", ret); + goto mclk_error; + } + + regcache_cache_only(rk3528->regmap, false); + ret = regcache_sync(rk3528->regmap); + if (ret) + goto reg_error; + + rk3528_codec_dlp_up(rk3528); + + return 0; +reg_error: + clk_disable_unprepare(rk3528->mclk); +mclk_error: + clk_disable_unprepare(rk3528->pclk); +pclk_error: + return ret; +} + +static const DECLARE_TLV_DB_SCALE(rk3528_codec_dac_lineout_gain_tlv, + -3900, 150, 600); + +static const struct snd_kcontrol_new rk3528_codec_dapm_controls[] = { + /* DAC LINEOUT */ + SOC_SINGLE_RANGE_TLV("DAC LEFT LINEOUT Volume", + ACODEC_ANA0B, + ACODEC_LINEOUT_L_GAIN_SHIFT, + ACODEC_DAC_LINEOUT_GAIN_MIN, + ACODEC_DAC_LINEOUT_GAIN_MAX, + 0, rk3528_codec_dac_lineout_gain_tlv), + SOC_SINGLE_RANGE_TLV("DAC RIGHT LINEOUT Volume", + ACODEC_ANA0F, + ACODEC_LINEOUT_R_GAIN_SHIFT, + ACODEC_DAC_LINEOUT_GAIN_MIN, + ACODEC_DAC_LINEOUT_GAIN_MAX, + 0, rk3528_codec_dac_lineout_gain_tlv), +}; + +static const struct snd_soc_component_driver soc_codec_dev_rk3528 = { + .probe = rk3528_codec_probe, + .remove = rk3528_codec_remove, + .suspend = rk3528_codec_suspend, + .resume = rk3528_codec_resume, + .controls = rk3528_codec_dapm_controls, + .num_controls = ARRAY_SIZE(rk3528_codec_dapm_controls), +}; + +/* Set the default value or reset value */ +static const struct reg_default rk3528_codec_reg_defaults[] = { + { ACODEC_DIG00, 0x71 }, + { ACODEC_DIG03, 0x53 }, + { ACODEC_DIG07, 0x03 }, + { ACODEC_DIG08, 0xc3 }, + { ACODEC_DIG09, 0x28 }, + { ACODEC_DIG0A, 0x1 }, + { ACODEC_DIG0B, 0x80 }, + { ACODEC_DIG0D, 0xc3 }, + { ACODEC_DIG0E, 0xc3 }, + { ACODEC_DIG10, 0xf1 }, + { ACODEC_DIG11, 0xf1 }, + { ACODEC_ANA02, 0x77 }, + { ACODEC_ANA08, 0x20 }, + { ACODEC_ANA0A, 0x8 }, + { ACODEC_ANA0C, 0x20 }, + { ACODEC_ANA0E, 0x8 }, +}; + +static bool rk3528_codec_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ACODEC_DIG00: + return true; + default: + return false; + } + return true; +} + +static const struct regmap_config rk3528_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = ACODEC_REG_MAX, + .volatile_reg = rk3528_codec_volatile_reg, + .reg_defaults = rk3528_codec_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rk3528_codec_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static const struct of_device_id rk3528_codec_of_match[] = { + { .compatible = "rockchip,rk3528-codec", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, rk3528_codec_of_match); + +static int rk3528_platform_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct rk3528_codec_priv *rk3528; + struct resource *res; + void __iomem *base; + int ret; + + rk3528 = devm_kzalloc(&pdev->dev, sizeof(*rk3528), GFP_KERNEL); + if (!rk3528) + return -ENOMEM; + + rk3528->plat_dev = &pdev->dev; + rk3528->reset = devm_reset_control_get_optional_exclusive(&pdev->dev, "acodec"); + if (IS_ERR(rk3528->reset)) + return PTR_ERR(rk3528->reset); + + rk3528->pa_ctl_gpio = devm_gpiod_get_optional(&pdev->dev, "pa-ctl", + GPIOD_OUT_LOW); + if (IS_ERR(rk3528->pa_ctl_gpio)) { + dev_err(&pdev->dev, "Unable to claim gpio pa-ctl\n"); + return -EINVAL; + } + + if (rk3528->pa_ctl_gpio) + of_property_read_u32(np, "pa-ctl-delay-ms", + &rk3528->pa_ctl_delay_ms); + + dev_info(&pdev->dev, "%s pa_ctl_gpio and pa_ctl_delay_ms: %d\n", + rk3528->pa_ctl_gpio ? "Use" : "No use", + rk3528->pa_ctl_delay_ms); + + /* Close external PA during startup. */ + rk3528_codec_pa_ctrl(rk3528, false); + + rk3528->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(rk3528->pclk)) { + dev_err(&pdev->dev, "Can't get acodec pclk\n"); + return -EINVAL; + } + + rk3528->mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(rk3528->mclk)) { + dev_err(&pdev->dev, "Can't get acodec mclk\n"); + return -EINVAL; + } + + ret = clk_prepare_enable(rk3528->pclk); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to enable acodec pclk: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(rk3528->mclk); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to enable acodec mclk: %d\n", ret); + goto failed_1; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + ret = PTR_ERR(base); + dev_err(&pdev->dev, "Failed to ioremap resource\n"); + goto failed; + } + + rk3528->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &rk3528_codec_regmap_config); + if (IS_ERR(rk3528->regmap)) { + ret = PTR_ERR(rk3528->regmap); + dev_err(&pdev->dev, "Failed to regmap mmio\n"); + goto failed; + } + + platform_set_drvdata(pdev, rk3528); + ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_rk3528, + rk3528_dai, ARRAY_SIZE(rk3528_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); + goto failed; + } + + return ret; + +failed: + clk_disable_unprepare(rk3528->mclk); +failed_1: + clk_disable_unprepare(rk3528->pclk); + + return ret; +} + +static int rk3528_platform_remove(struct platform_device *pdev) +{ + struct rk3528_codec_priv *rk3528 = + (struct rk3528_codec_priv *)platform_get_drvdata(pdev); + + clk_disable_unprepare(rk3528->mclk); + clk_disable_unprepare(rk3528->pclk); + + return 0; +} + +static struct platform_driver rk3528_codec_driver = { + .driver = { + .name = CODEC_DRV_NAME, + .of_match_table = of_match_ptr(rk3528_codec_of_match), + }, + .probe = rk3528_platform_probe, + .remove = rk3528_platform_remove, +}; +module_platform_driver(rk3528_codec_driver); + +MODULE_DESCRIPTION("ASoC rk3528 Codec Driver"); +MODULE_AUTHOR("Jason Zhu "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rk3528_codec.h b/sound/soc/codecs/rk3528_codec.h new file mode 100644 index 000000000000..edcca0e1df45 --- /dev/null +++ b/sound/soc/codecs/rk3528_codec.h @@ -0,0 +1,229 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * rk3528_codec.h - Rockchip RK3528 SoC Codec Driver + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + */ + +#ifndef __RK3528_CODEC_H__ +#define __RK3528_CODEC_H__ + +#define ACODEC_DIG00 0x00 /* REG 0x00 */ +#define ACODEC_DIG01 0x04 /* REG 0x01 */ +#define ACODEC_DIG02 0x08 /* REG 0x02 */ +#define ACODEC_DIG03 0x0c /* REG 0x03 */ +#define ACODEC_DIG04 0x10 /* REG 0x04 */ +#define ACODEC_DIG05 0x14 /* REG 0x05 */ +#define ACODEC_DIG06 0x18 /* REG 0x06 */ +#define ACODEC_DIG07 0x1c /* REG 0x07 */ +#define ACODEC_DIG08 0x20 /* REG 0x08 */ +#define ACODEC_DIG09 0x24 /* REG 0x09 */ +#define ACODEC_DIG0A 0x28 /* REG 0x0a */ +#define ACODEC_DIG0B 0x2c /* REG 0x0b */ +#define ACODEC_DIG0D 0x34 /* REG 0x0d */ +#define ACODEC_DIG0E 0x38 /* REG 0x0e */ +#define ACODEC_DIG10 0x40 /* REG 0x10 */ +#define ACODEC_DIG11 0x44 /* REG 0x11 */ + +#define ACODEC_ANA00 0x80 /* REG 0x20 */ +#define ACODEC_ANA01 0x84 /* REG 0x21 */ +#define ACODEC_ANA02 0x88 /* REG 0x22 */ +#define ACODEC_ANA08 0xa0 /* REG 0x28 */ +#define ACODEC_ANA09 0xa4 /* REG 0x29 */ +#define ACODEC_ANA0A 0xa8 /* REG 0x2a */ +#define ACODEC_ANA0B 0xac /* REG 0x2b */ +#define ACODEC_ANA0C 0xb0 /* REG 0x2c */ +#define ACODEC_ANA0D 0xb4 /* REG 0x2d */ +#define ACODEC_ANA0E 0xb8 /* REG 0x2e */ +#define ACODEC_ANA0F 0xbc /* REG 0x2f */ + +#define ACODEC_REG_MAX ACODEC_ANA0F + +/* ACODEC_DIG00 */ +#define ACODEC_DAC_RST_MASK (0x1 << 4) +#define ACODEC_DAC_RST_P (0x1 << 4) +#define ACODEC_DAC_RST_N (0x0 << 4) +#define ACODEC_REG_BIST_EN (0x1 << 1) +#define ACODEC_SYS_RST_MASK (0x1 << 0) +#define ACODEC_SYS_RST_P (0x1 << 0) +#define ACODEC_SYS_RST_N (0x0 << 0) + +/* ACODEC_DIG01 */ +#define ACODEC_DAC_I2S_WL_SHIFT 4 +#define ACODEC_DAC_I2S_WL_MASK (0x3 << ACODEC_DAC_I2S_WL_SHIFT) +#define ACODEC_DAC_I2S_32B (0x3 << ACODEC_DAC_I2S_WL_SHIFT) +#define ACODEC_DAC_I2S_24B (0x2 << ACODEC_DAC_I2S_WL_SHIFT) +#define ACODEC_DAC_I2S_20B (0x1 << ACODEC_DAC_I2S_WL_SHIFT) +#define ACODEC_DAC_I2S_16B (0x0 << ACODEC_DAC_I2S_WL_SHIFT) +#define ACODEC_DAC_I2S_FMT_SHIFT 2 +#define ACODEC_DAC_I2S_FMT_MASK (0x3 << ACODEC_DAC_I2S_FMT_SHIFT) +#define ACODEC_DAC_I2S_I2S (0x2 << ACODEC_DAC_I2S_FMT_SHIFT) +#define ACODEC_DAC_I2S_LJM (0x1 << ACODEC_DAC_I2S_FMT_SHIFT) + +/* ACODEC_DIG02 */ +#define ACODEC_DAC_I2S_LRP_MASK (0x1 << 7) +#define ACODEC_DAC_I2S_LRP_REVSL (0x1 << 7) +#define ACODEC_DAC_I2S_LRP_NORMAL (0x0 << 7) +#define ACODEC_DAC_I2S_LR_SWAP (0x1 << 6) +#define ACODEC_DAC_I2S_MST_FUNC_MASK (0x1 << 5) +#define ACODEC_DAC_I2S_MST_FUNC_MASTER (0x1 << 5) +#define ACODEC_DAC_I2S_MST_FUNC_SLAVE (0x0 << 5) +#define ACODEC_DAC_I2S_MST_IO_MASK (0x1 << 4) +#define ACODEC_DAC_I2S_MST_IO_MASTER (0x1 << 4) +#define ACODEC_DAC_I2S_MST_IO_SLAVE (0x0 << 4) +#define ACODEC_DAC_I2S_RST_MASK (0x1 << 0) +#define ACODEC_DAC_I2S_RST_P (0x1 << 0) +#define ACODEC_DAC_I2S_RST_N (0x0 << 0) + +/* ACODEC_DIG03 */ +#define ACODEC_DAC_MUTE_EN (0x1 << 7) +#define ACODEC_DAC_MUTE_SR_SHIFT 4 +#define ACODEC_DAC_MUTE_SR_MASK (0x7 << ACODEC_DAC_MUTE_SR_SHIFT) +#define ACODEC_DAC_MUTE_SR_96K (0x7 << ACODEC_DAC_MUTE_SR_SHIFT) +#define ACODEC_DAC_MUTE_SR_88K2 (0x6 << ACODEC_DAC_MUTE_SR_SHIFT) +#define ACODEC_DAC_MUTE_SR_48K (0x5 << ACODEC_DAC_MUTE_SR_SHIFT) +#define ACODEC_DAC_MUTE_SR_44K1 (0x4 << ACODEC_DAC_MUTE_SR_SHIFT) +#define ACODEC_DAC_MUTE_SR_32K (0x3 << ACODEC_DAC_MUTE_SR_SHIFT) +#define ACODEC_DAC_MUTE_SR_24K (0x2 << ACODEC_DAC_MUTE_SR_SHIFT) +#define ACODEC_DAC_MUTE_SR_16K (0x1 << ACODEC_DAC_MUTE_SR_SHIFT) +#define ACODEC_DAC_MUTE_SR_8K (0x0 << ACODEC_DAC_MUTE_SR_SHIFT) +#define ACODEC_DA_EN (0x1 << 3) +#define ACODEC_DITHER_EN (0x1 << 2) +#define ACODEC_DITHER_LEVEL (0x1 << 1) +#define ACODEC_DITHER_SIGN (0x1 << 0) + +/* ACODEC_DIG04 */ +#define ACODEC_DAC_DEEMP_SEL_MASK 0x3 +#define ACODEC_DAC_DEEMP_48K 0x3 +#define ACODEC_DAC_DEEMP_44K1 0x2 +#define ACODEC_DAC_DEEMP_32K 0x1 +#define ACODEC_DAC_DEEMP_DIS 0x0 + +/* ACODEC_DIG05 */ +#define ACODEC_DAC_R_BIST_SEL_SHIFT 6 +#define ACODEC_DAC_R_BIST_SEL_MASK (0x3 << ACODEC_DAC_R_BIST_SEL_SHIFT) +#define ACODEC_DAC_R_BIST_SEL_L (0x2 << ACODEC_DAC_R_BIST_SEL_SHIFT) +#define ACODEC_DAC_R_BIST_SEL_SINE (0x1 << ACODEC_DAC_R_BIST_SEL_SHIFT) +#define ACODEC_DAC_R_BIST_SEL_R (0x0 << ACODEC_DAC_R_BIST_SEL_SHIFT) +#define ACODEC_DAC_R_MUTE (0x1 << 4) +#define ACODEC_DAC_L_BIST_SEL_SHIFT 2 +#define ACODEC_DAC_L_BIST_SEL_MASK (0x3 << ACODEC_DAC_L_BIST_SEL_SHIFT) +#define ACODEC_DAC_L_BIST_SEL_R (0x2 << ACODEC_DAC_L_BIST_SEL_SHIFT) +#define ACODEC_DAC_L_BIST_SEL_SINE (0x1 << ACODEC_DAC_L_BIST_SEL_SHIFT) +#define ACODEC_DAC_L_BIST_SEL_L (0x0 << ACODEC_DAC_L_BIST_SEL_SHIFT) +#define ACODEC_DAC_J_MUTE (0x1 << 0) + +/* ACODEC_DIG06 */ +#define ACODEC_DAC_DIG_GAIN_SHIT 0 +#define ACODEC_DAC_DIG_GAIN_MASK (0xff << ACODEC_DAC_DIG_GAIN_SHIT) +/* 0.5dB every step , 1: -121dB, 255: 6dB */ +#define ACODEC_DAC_DIG_GAIN(x) ((x) & ACODEC_DAC_DIG_GAIN_MASK) +#define ACODEC_DAC_DIG_0DB 0xe1 + +/* ACODEC_ANA00 */ +#define ACODEC_IBIAS_DAC_MASK (0x1 << 1) +#define ACODEC_IBIAS_DAC_EN (0x1 << 1) +#define ACODEC_IBIAS_DAC_DIS (0x0 << 1) +#define ACODEC_VREF_MASK (0x1 << 0) +#define ACODEC_VREF_EN (0x1 << 0) +#define ACODEC_VREF_DIS (0x0 << 0) + +/* ACODEC_ANA01 */ +#define ACODEC_VREF_SEL_SHIFT 0 +/* Bit 0 is I0, bit 1 is 2 * I0 ... bit 7 is 128 * I0 */ +#define ACODEC_VREF_SEL_MASK (0xff << ACODEC_VREF_SEL_SHIFT) +#define ACODEC_VREF_SEL(x) ((x) & ACODEC_VREF_SEL_MASK) + +/* ACODEC_ANA02 */ +#define ACODEC_IBIAS_DAC_SEL_SHIFT 0 +/* Ibias_DAC = I0 * (BIT[3]*8+BIT[2]*4+BIT[1]*2+BIT[0]+1) */ +#define ACODEC_IBIAS_DAC_SEL (0xf << ACODEC_IBIAS_DAC_SEL_SHIFT) + +/* ACODEC_ANA08 */ +#define ACODEC_DAC_L_POP_CTRL_SHIFT 5 +#define ACODEC_DAC_L_POP_CTRL_MASK (0x3 << ACODEC_DAC_L_POP_CTRL_SHIFT) +#define ACODEC_DAC_L_POP_CTRL_ON (0x1 << ACODEC_DAC_L_POP_CTRL_SHIFT) +#define ACODEC_DAC_L_POP_CTRL_OFF (0x0 << ACODEC_DAC_L_POP_CTRL_SHIFT) +#define ACODEC_DAC_L_INIT_MASK (0x1 << 4) +#define ACODEC_DAC_L_WORK (0x1 << 4) +#define ACODEC_DAC_L_INIT (0x0 << 4) +#define ACODEC_DAC_L_VREF_MASK (0x1 << 3) +#define ACODEC_DAC_L_VREF_EN (0x1 << 3) +#define ACODEC_DAC_L_VREF_DIS (0x0 << 3) +#define ACODEC_DAC_L_BUF_MASK (0x1 << 2) +#define ACODEC_DAC_L_BUF_EN (0x1 << 2) +#define ACODEC_DAC_L_BUF_DIS (0x0 << 2) +#define ACODEC_DAC_L_CLK_MASK (0x1 << 1) +#define ACODEC_DAC_L_CLK_EN (0x1 << 1) +#define ACODEC_DAC_L_CLK_DIS (0x0 << 1) +#define ACODEC_DAC_L_MASK (0x1 << 0) +#define ACODEC_DAC_L_EN (0x1 << 0) +#define ACODEC_DAC_L_DIS (0x0 << 0) + +/* ACODEC_ANA09 */ +#define ACODEC_LINEOUT_L_MUTE_MASK (0x1 << 5) +#define ACODEC_LINEOUT_L_WORK (0x1 << 5) +#define ACODEC_LINEOUT_L_MUTE (0x0 << 5) +#define ACODEC_LINEOUT_L_INIT_MASK (0x1 << 4) +#define ACODEC_LINEOUT_L_INIT_WORK (0x1 << 4) +#define ACODEC_LINEOUT_L_INIT (0x0 << 4) +#define ACODEC_LINEOUT_L_MASK (0x1 << 0) +#define ACODEC_LINEOUT_L_EN (0x1 << 0) +#define ACODEC_LINEOUT_L_DIS (0x0 << 0) + +/* ACODEC_ANA0A */ +#define ACODEC_LINEOUT_L_SEL_SHIFT 0 +#define ACODEC_LINEOUT_L_SEL_MASK (0xf << ACODEC_LINEOUT_L_SEL_SHIFT) + +/* ACODEC_ANA0B */ +#define ACODEC_LINEOUT_L_GAIN_SHIFT 0 +/* 1.5dB every step. 0: -39dB, 0x1f: 0dB */ +#define ACODEC_LINEOUT_L_GAIN_MASK (0x1f << ACODEC_LINEOUT_L_GAIN_SHIFT) + +/* ACODEC_ANA0C */ +#define ACODEC_DAC_R_POP_CTRL_SHIFT 5 +#define ACODEC_DAC_R_POP_CTRL_MASK (0x3 << ACODEC_DAC_R_POP_CTRL_SHIFT) +#define ACODEC_DAC_R_POP_CTRL_ON (0x1 << ACODEC_DAC_R_POP_CTRL_SHIFT) +#define ACODEC_DAC_R_POP_CTRL_OFF (0x0 << ACODEC_DAC_R_POP_CTRL_SHIFT) +#define ACODEC_DAC_R_INIT_MASK (0x1 << 4) +#define ACODEC_DAC_R_WORK (0x1 << 4) +#define ACODEC_DAC_R_INIT (0x0 << 4) +#define ACODEC_DAC_R_VREF_MASK (0x1 << 3) +#define ACODEC_DAC_R_VREF_EN (0x1 << 3) +#define ACODEC_DAC_R_VREF_DIS (0x0 << 3) +#define ACODEC_DAC_R_BUF_MASK (0x1 << 2) +#define ACODEC_DAC_R_BUF_EN (0x1 << 2) +#define ACODEC_DAC_R_BUF_DIS (0x0 << 2) +#define ACODEC_DAC_R_CLK_MASK (0x1 << 1) +#define ACODEC_DAC_R_CLK_EN (0x1 << 1) +#define ACODEC_DAC_R_CLK_DIS (0x0 << 1) +#define ACODEC_DAC_R_MASK (0x1 << 0) +#define ACODEC_DAC_R_EN (0x1 << 0) +#define ACODEC_DAC_R_DIS (0x0 << 0) + +/* ACODEC_ANA0D */ +#define ACODEC_LINEOUT_R_MUTE_MASK (0x1 << 5) +#define ACODEC_LINEOUT_R_WORK (0x1 << 5) +#define ACODEC_LINEOUT_R_MUTE (0x0 << 5) +#define ACODEC_LINEOUT_R_INIT_MASK (0x1 << 4) +#define ACODEC_LINEOUT_R_INIT_WORK (0x1 << 4) +#define ACODEC_LINEOUT_R_INIT (0x0 << 4) +#define ACODEC_LINEOUT_R_MASK (0x1 << 0) +#define ACODEC_LINEOUT_R_EN (0x1 << 0) +#define ACODEC_LINEOUT_R_DIS (0x0 << 0) + +/* ACODEC_ANA0E */ +#define ACODEC_LINEOUT_R_SEL_SHIFT 0 +#define ACODEC_LINEOUT_R_SEL_MASK (0xf << ACODEC_LINEOUT_L_SEL_SHIFT) + +/* ACODEC_ANA0F */ +#define ACODEC_LINEOUT_R_GAIN_SHIFT 0 +/* 1.5dB every step. 0: -39dB, 0x1f: 0dB */ +#define ACODEC_LINEOUT_R_GAIN_MASK (0x1f << ACODEC_LINEOUT_L_GAIN_SHIFT) + +#define ACODEC_DAC_LINEOUT_GAIN_MAX 0x1e +#define ACODEC_DAC_LINEOUT_GAIN_MIN 0 +#define ACODEC_HIFI 0x0 +#define ACODEC_DAC_LINEOUT_GAIN_0DB 0x1a + +#endif /* __RK3528_CODEC_H__ */