linux/sound/soc/codecs/rk3328_codec.c
Mark Brown cd0df1706d
ASoC: rk3328: Use modern ASoC DAI format terminology
As part of moving to remove the old style defines for the bus clocks update
the rk3328 driver to use more modern terminology for clocking.

Signed-off-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20220602135316.3554400-4-broonie@kernel.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2022-06-06 13:09:03 +01:00

536 lines
14 KiB
C

// SPDX-License-Identifier: GPL-2.0
//
// rk3328 ALSA SoC Audio driver
//
// Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
#include "rk3328_codec.h"
/*
* volume setting
* 0: -39dB
* 26: 0dB
* 31: 6dB
* Step: 1.5dB
*/
#define OUT_VOLUME (0x18)
#define RK3328_GRF_SOC_CON2 (0x0408)
#define RK3328_GRF_SOC_CON10 (0x0428)
#define INITIAL_FREQ (11289600)
struct rk3328_codec_priv {
struct regmap *regmap;
struct gpio_desc *mute;
struct clk *mclk;
struct clk *pclk;
unsigned int sclk;
int spk_depop_time; /* msec */
};
static const struct reg_default rk3328_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 rk3328_codec_reset(struct rk3328_codec_priv *rk3328)
{
regmap_write(rk3328->regmap, CODEC_RESET, 0x00);
mdelay(10);
regmap_write(rk3328->regmap, CODEC_RESET, 0x03);
return 0;
}
static int rk3328_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct rk3328_codec_priv *rk3328 =
snd_soc_component_get_drvdata(dai->component);
unsigned int val;
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
case SND_SOC_DAIFMT_CBC_CFC:
val = PIN_DIRECTION_IN | DAC_I2S_MODE_SLAVE;
break;
case SND_SOC_DAIFMT_CBP_CFP:
val = PIN_DIRECTION_OUT | DAC_I2S_MODE_MASTER;
break;
default:
return -EINVAL;
}
regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL1,
PIN_DIRECTION_MASK | DAC_I2S_MODE_MASK, val);
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(rk3328->regmap, DAC_INIT_CTRL2,
DAC_MODE_MASK, val);
return 0;
}
static int rk3328_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
{
struct rk3328_codec_priv *rk3328 =
snd_soc_component_get_drvdata(dai->component);
unsigned int val;
if (mute)
val = HPOUTL_MUTE | HPOUTR_MUTE;
else
val = HPOUTL_UNMUTE | HPOUTR_UNMUTE;
regmap_update_bits(rk3328->regmap, HPOUT_CTRL,
HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, val);
return 0;
}
static int rk3328_codec_power_on(struct rk3328_codec_priv *rk3328, int wait_ms)
{
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_PRECHARGE);
mdelay(10);
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
DAC_CHARGE_CURRENT_ALL_MASK,
DAC_CHARGE_CURRENT_ALL_ON);
mdelay(wait_ms);
return 0;
}
static int rk3328_codec_power_off(struct rk3328_codec_priv *rk3328, int wait_ms)
{
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_DISCHARGE);
mdelay(10);
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
DAC_CHARGE_CURRENT_ALL_MASK,
DAC_CHARGE_CURRENT_ALL_ON);
mdelay(wait_ms);
return 0;
}
static const struct rk3328_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_MASK | HPOUTR_ZERO_CROSSING_MASK,
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 },
};
static int rk3328_codec_open_playback(struct rk3328_codec_priv *rk3328)
{
int i;
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
DAC_CHARGE_CURRENT_ALL_MASK,
DAC_CHARGE_CURRENT_I);
for (i = 0; i < ARRAY_SIZE(playback_open_list); i++) {
regmap_update_bits(rk3328->regmap,
playback_open_list[i].reg,
playback_open_list[i].msk,
playback_open_list[i].val);
mdelay(1);
}
msleep(rk3328->spk_depop_time);
gpiod_set_value(rk3328->mute, 0);
regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL,
HPOUTL_GAIN_MASK, OUT_VOLUME);
regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL,
HPOUTR_GAIN_MASK, OUT_VOLUME);
return 0;
}
static const struct rk3328_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_UNSELECT | DACR_UNSELECT },
{ 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 },
};
static int rk3328_codec_close_playback(struct rk3328_codec_priv *rk3328)
{
size_t i;
gpiod_set_value(rk3328->mute, 1);
regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL,
HPOUTL_GAIN_MASK, 0);
regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL,
HPOUTR_GAIN_MASK, 0);
for (i = 0; i < ARRAY_SIZE(playback_close_list); i++) {
regmap_update_bits(rk3328->regmap,
playback_close_list[i].reg,
playback_close_list[i].msk,
playback_close_list[i].val);
mdelay(1);
}
/* Workaround for silence when changed Fs 48 -> 44.1kHz */
rk3328_codec_reset(rk3328);
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
DAC_CHARGE_CURRENT_ALL_MASK,
DAC_CHARGE_CURRENT_ALL_ON);
return 0;
}
static int rk3328_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct rk3328_codec_priv *rk3328 =
snd_soc_component_get_drvdata(dai->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(rk3328->regmap, DAC_INIT_CTRL2, DAC_VDL_MASK, val);
val = DAC_WL_32BITS | DAC_RST_DIS;
regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL3,
DAC_WL_MASK | DAC_RST_MASK, val);
return 0;
}
static int rk3328_pcm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct rk3328_codec_priv *rk3328 =
snd_soc_component_get_drvdata(dai->component);
return rk3328_codec_open_playback(rk3328);
}
static void rk3328_pcm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct rk3328_codec_priv *rk3328 =
snd_soc_component_get_drvdata(dai->component);
rk3328_codec_close_playback(rk3328);
}
static const struct snd_soc_dai_ops rk3328_dai_ops = {
.hw_params = rk3328_hw_params,
.set_fmt = rk3328_set_dai_fmt,
.mute_stream = rk3328_mute_stream,
.startup = rk3328_pcm_startup,
.shutdown = rk3328_pcm_shutdown,
.no_capture_mute = 1,
};
static struct snd_soc_dai_driver rk3328_dai[] = {
{
.name = "rk3328-hifi",
.id = RK3328_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 = &rk3328_dai_ops,
},
};
static int rk3328_codec_probe(struct snd_soc_component *component)
{
struct rk3328_codec_priv *rk3328 =
snd_soc_component_get_drvdata(component);
rk3328_codec_reset(rk3328);
rk3328_codec_power_on(rk3328, 0);
return 0;
}
static void rk3328_codec_remove(struct snd_soc_component *component)
{
struct rk3328_codec_priv *rk3328 =
snd_soc_component_get_drvdata(component);
rk3328_codec_close_playback(rk3328);
rk3328_codec_power_off(rk3328, 0);
}
static const struct snd_soc_component_driver soc_codec_rk3328 = {
.probe = rk3328_codec_probe,
.remove = rk3328_codec_remove,
};
static bool rk3328_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 rk3328_codec_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case CODEC_RESET:
return true;
default:
return false;
}
}
static const struct regmap_config rk3328_codec_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = HPOUT_POP_CTRL,
.writeable_reg = rk3328_codec_write_read_reg,
.readable_reg = rk3328_codec_write_read_reg,
.volatile_reg = rk3328_codec_volatile_reg,
.reg_defaults = rk3328_codec_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(rk3328_codec_reg_defaults),
.cache_type = REGCACHE_FLAT,
};
static int rk3328_platform_probe(struct platform_device *pdev)
{
struct device_node *rk3328_np = pdev->dev.of_node;
struct rk3328_codec_priv *rk3328;
struct regmap *grf;
void __iomem *base;
int ret = 0;
rk3328 = devm_kzalloc(&pdev->dev, sizeof(*rk3328), GFP_KERNEL);
if (!rk3328)
return -ENOMEM;
grf = syscon_regmap_lookup_by_phandle(rk3328_np,
"rockchip,grf");
if (IS_ERR(grf)) {
dev_err(&pdev->dev, "missing 'rockchip,grf'\n");
return PTR_ERR(grf);
}
/* enable i2s_acodec_en */
regmap_write(grf, RK3328_GRF_SOC_CON2,
(BIT(14) << 16 | BIT(14)));
ret = of_property_read_u32(rk3328_np, "spk-depop-time-ms",
&rk3328->spk_depop_time);
if (ret < 0) {
dev_info(&pdev->dev, "spk_depop_time use default value.\n");
rk3328->spk_depop_time = 200;
}
rk3328->mute = gpiod_get_optional(&pdev->dev, "mute", GPIOD_OUT_HIGH);
if (IS_ERR(rk3328->mute))
return PTR_ERR(rk3328->mute);
/*
* Rock64 is the only supported platform to have widely relied on
* this; if we do happen to come across an old DTB, just leave the
* external mute forced off.
*/
if (!rk3328->mute && of_machine_is_compatible("pine64,rock64")) {
dev_warn(&pdev->dev, "assuming implicit control of GPIO_MUTE; update devicetree if possible\n");
regmap_write(grf, RK3328_GRF_SOC_CON10, BIT(17) | BIT(1));
}
rk3328->mclk = devm_clk_get(&pdev->dev, "mclk");
if (IS_ERR(rk3328->mclk))
return PTR_ERR(rk3328->mclk);
ret = clk_prepare_enable(rk3328->mclk);
if (ret)
return ret;
clk_set_rate(rk3328->mclk, INITIAL_FREQ);
rk3328->pclk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(rk3328->pclk)) {
dev_err(&pdev->dev, "can't get acodec pclk\n");
ret = PTR_ERR(rk3328->pclk);
goto err_unprepare_mclk;
}
ret = clk_prepare_enable(rk3328->pclk);
if (ret < 0) {
dev_err(&pdev->dev, "failed to enable acodec pclk\n");
goto err_unprepare_mclk;
}
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base)) {
ret = PTR_ERR(base);
goto err_unprepare_pclk;
}
rk3328->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&rk3328_codec_regmap_config);
if (IS_ERR(rk3328->regmap)) {
ret = PTR_ERR(rk3328->regmap);
goto err_unprepare_pclk;
}
platform_set_drvdata(pdev, rk3328);
ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_rk3328,
rk3328_dai,
ARRAY_SIZE(rk3328_dai));
if (ret)
goto err_unprepare_pclk;
return 0;
err_unprepare_pclk:
clk_disable_unprepare(rk3328->pclk);
err_unprepare_mclk:
clk_disable_unprepare(rk3328->mclk);
return ret;
}
static const struct of_device_id rk3328_codec_of_match[] __maybe_unused = {
{ .compatible = "rockchip,rk3328-codec", },
{},
};
MODULE_DEVICE_TABLE(of, rk3328_codec_of_match);
static struct platform_driver rk3328_codec_driver = {
.driver = {
.name = "rk3328-codec",
.of_match_table = of_match_ptr(rk3328_codec_of_match),
},
.probe = rk3328_platform_probe,
};
module_platform_driver(rk3328_codec_driver);
MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
MODULE_DESCRIPTION("ASoC rk3328 codec driver");
MODULE_LICENSE("GPL v2");