linux-stable/sound/soc/codecs/rk3328_codec.c
Robin Murphy 87d12d5545
ASoC: rockchip: Make RK3328 GPIO_MUTE control explicit
The RK3328 reference design uses an external line driver IC as a buffer
on the analog codec output, enabled by the GPIO_MUTE pin, and such a
configuration is currently assumed in the codec driver's direct poking
of GRF_SOC_CON10 to control the GPIO_MUTE output value. However, some
boards wire up analog audio yet use that pin for some other purpose, so
that assumption doesn't always hold. Update this functionality to rely
on an explicit GPIO descriptor, such that it can be managed at the
board level.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>
Link: https://lore.kernel.org/r/5bc383ed1832f0f5d1dcb3c97ad92fd68e5217e3.1581376744.git.robin.murphy@arm.com
Signed-off-by: Mark Brown <broonie@kernel.org>
2020-02-19 17:43:21 +00:00

518 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_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(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_digital_mute(struct snd_soc_dai *dai, int mute)
{
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,
.digital_mute = rk3328_digital_mute,
.startup = rk3328_pcm_startup,
.shutdown = rk3328_pcm_shutdown,
};
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");
return PTR_ERR(rk3328->pclk);
}
ret = clk_prepare_enable(rk3328->pclk);
if (ret < 0) {
dev_err(&pdev->dev, "failed to enable acodec pclk\n");
return ret;
}
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
rk3328->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&rk3328_codec_regmap_config);
if (IS_ERR(rk3328->regmap))
return PTR_ERR(rk3328->regmap);
platform_set_drvdata(pdev, rk3328);
return devm_snd_soc_register_component(&pdev->dev, &soc_codec_rk3328,
rk3328_dai,
ARRAY_SIZE(rk3328_dai));
}
static const struct of_device_id rk3328_codec_of_match[] = {
{ .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");