772 lines
21 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* rk3528_codec.c - Rockchip RK3528 SoC Codec Driver
*
* Copyright (C) 2022 Rockchip Electronics Co., Ltd.
*/
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#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 <jason.zhu@rock-chips.com>");
MODULE_LICENSE("GPL");