mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 19:08:57 +09:00
ASoC: codecs: rk_dsm: add support iomux switching for anti-pop automatically
The LP/LN and RP/RN differential pairs of rkdsm should keep in the same direction as much as possible when working and shutting down, otherwise the common mode output is likely to cause pop sound. Therefore, we can introduce the ioc register address as REG resource in dts, and switch the dsm pin to function and io state respectively when dsm starts and stops. Change-Id: I4a3e6f5e45f030fdbab74ee7d9a769fc8d77f86d Signed-off-by: Xing Zheng <zhengxing@rock-chips.com>
This commit is contained in:
@@ -23,15 +23,72 @@
|
||||
#include <sound/tlv.h>
|
||||
#include "rk_dsm.h"
|
||||
|
||||
#define RK3506_DSM_AUDM0_EN BIT(0)
|
||||
#define RK3506_DSM_AUDM1_EN BIT(1)
|
||||
#define RK3506_IOC1_REG_BASE (0xff660000)
|
||||
#define RK3506_GPIO1_IOC_GPIO1C_IOMUX_SEL_0 (0x0030) /* DSM_AUD_RP_M0 / DSM_AUD_RN_M0 */
|
||||
#define RK3506_GPIO1_IOC_GPIO1C2_SEL_MASK GENMASK(11, 8)
|
||||
#define RK3506_GPIO1_IOC_DSM_AUD_RP_M0 (4 << 8)
|
||||
#define RK3506_GPIO1_IOC_GPIO2C2 (0 << 8)
|
||||
#define RK3506_GPIO1_IOC_GPIO1C1_SEL_MASK GENMASK(7, 4)
|
||||
#define RK3506_GPIO1_IOC_DSM_AUD_RN_M0 (4 << 4)
|
||||
#define RK3506_GPIO1_IOC_GPIO2C1 (0 << 4)
|
||||
#define RK3506_GPIO1_IOC_GPIO1D_IOMUX_SEL_0 (0x0038) /* DSM_AUD_LP_M0 / DSM_AUD_LN_M0 */
|
||||
#define RK3506_GPIO1_IOC_GPIO1D1_SEL_MASK GENMASK(7, 4)
|
||||
#define RK3506_GPIO1_IOC_DSM_AUD_LP_M0 (4 << 4)
|
||||
#define RK3506_GPIO1_IOC_GPIO2D1 (0 << 4)
|
||||
#define RK3506_GPIO1_IOC_GPIO1D0_SEL_MASK GENMASK(3, 0)
|
||||
#define RK3506_GPIO1_IOC_DSM_AUD_LN_M0 (4 << 0)
|
||||
#define RK3506_GPIO1_IOC_GPIO2D0 (0 << 0)
|
||||
#define RK3506_IOC2_REG_BASE (0xff4d8000)
|
||||
#define RK3506_GPIO2_IOC_GPIO2B_IOMUX_SEL_1 (0x004c) /* DSM_AUD_LP_M1 / DSM_AUD_LN_M1 / DSM_AUD_RP_M1 / DSM_AUD_RN_M1 */
|
||||
#define RK3506_GPIO2_IOC_GPIO2B7_SEL_MASK GENMASK(15, 12)
|
||||
#define RK3506_GPIO2_IOC_DSM_AUD_LP_M1 (2 << 12)
|
||||
#define RK3506_GPIO2_IOC_GPIO2B7 (0 << 12)
|
||||
#define RK3506_GPIO2_IOC_GPIO2B6_SEL_MASK GENMASK(11, 8)
|
||||
#define RK3506_GPIO2_IOC_DSM_AUD_LN_M1 (2 << 8)
|
||||
#define RK3506_GPIO2_IOC_GPIO2B6 (0 << 8)
|
||||
#define RK3506_GPIO2_IOC_GPIO2B5_SEL_MASK GENMASK(7, 4)
|
||||
#define RK3506_GPIO2_IOC_DSM_AUD_RP_M1 (2 << 4)
|
||||
#define RK3506_GPIO2_IOC_GPIO2B5 (0 << 4)
|
||||
#define RK3506_GPIO2_IOC_GPIO2B4_SEL_MASK GENMASK(3, 0)
|
||||
#define RK3506_GPIO2_IOC_DSM_AUD_RN_M1 (2 << 0)
|
||||
#define RK3506_GPIO2_IOC_GPIO2B4 (0 << 0)
|
||||
|
||||
#define RK3506_GRF_SOC_CON0 (0x0)
|
||||
#define RK3506_DSM_SEL (9)
|
||||
#define RK3562_GRF_PERI_AUDIO_CON (0x0070)
|
||||
#define RK3576_SYS_GRF_SOC_CON2 (0x0008)
|
||||
#define RK3576_DSM_SEL (0x0)
|
||||
|
||||
#define RKDSM_VOL_VAL_MAX (0xff)
|
||||
|
||||
enum {
|
||||
RKDSM_ON_GPIO = 0,
|
||||
RKDSM_ON_FUNC,
|
||||
};
|
||||
|
||||
struct rk_dsm_soc_data {
|
||||
int (*init)(struct device *dev);
|
||||
void (*deinit)(struct device *dev);
|
||||
int (*iomux_switch)(struct device *dev, int type);
|
||||
};
|
||||
|
||||
struct rk_dsm_vols {
|
||||
int vol_l;
|
||||
int vol_r;
|
||||
int polarity;
|
||||
};
|
||||
|
||||
struct rk_dsm_iomux_res {
|
||||
phys_addr_t regbase;
|
||||
unsigned long size;
|
||||
};
|
||||
|
||||
struct rk_dsm_iomux {
|
||||
void __iomem **ioc;
|
||||
unsigned int audm_en;
|
||||
int res_num;
|
||||
};
|
||||
|
||||
struct rk_dsm_priv {
|
||||
@@ -43,6 +100,12 @@ struct rk_dsm_priv {
|
||||
struct gpio_desc *pa_ctl;
|
||||
struct reset_control *rc;
|
||||
const struct rk_dsm_soc_data *data;
|
||||
struct device *dev;
|
||||
struct rk_dsm_vols vols;
|
||||
struct rk_dsm_iomux iomuxes;
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pin_state;
|
||||
struct pinctrl_state *io_state;
|
||||
};
|
||||
|
||||
/* DAC digital gain */
|
||||
@@ -92,6 +155,8 @@ static int rk_dsm_dac_vol_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
struct rk_dsm_priv *rd =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
unsigned int reg = mc->reg;
|
||||
unsigned int rreg = mc->rreg;
|
||||
unsigned int shift = mc->shift;
|
||||
@@ -116,6 +181,9 @@ static int rk_dsm_dac_vol_put(struct snd_kcontrol *kcontrol,
|
||||
snd_soc_component_update_bits(component, reg, val_mask, val);
|
||||
snd_soc_component_update_bits(component, rreg, val_mask, val);
|
||||
snd_soc_component_write(component, DACVOGP, sign);
|
||||
rd->vols.vol_l = val;
|
||||
rd->vols.vol_r = val;
|
||||
rd->vols.polarity = sign;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -355,9 +423,7 @@ static int rk_dsm_hw_params(struct snd_pcm_substream *substream,
|
||||
DSM_DACDSM_CTRL_DSM_EN_MASK,
|
||||
DSM_DACDSM_CTRL_DSM_MODE_CKE_EN |
|
||||
DSM_DACDSM_CTRL_DSM_EN);
|
||||
}
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
regmap_update_bits(rd->regmap, I2S_XFER,
|
||||
DSM_I2S_XFER_RXS_MASK,
|
||||
DSM_I2S_XFER_RXS_START);
|
||||
@@ -366,6 +432,9 @@ static int rk_dsm_hw_params(struct snd_pcm_substream *substream,
|
||||
DSM_DACDIGEN_DACEN_L0R1_MASK,
|
||||
DSM_DACDIGEN_DAC_GLBEN_EN |
|
||||
DSM_DACDIGEN_DACEN_L0R1_EN);
|
||||
|
||||
if (rd->data && rd->data->iomux_switch)
|
||||
rd->data->iomux_switch(rd->dev, RKDSM_ON_FUNC);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -377,6 +446,11 @@ static int rk_dsm_pcm_startup(struct snd_pcm_substream *substream,
|
||||
struct rk_dsm_priv *rd =
|
||||
snd_soc_component_get_drvdata(dai->component);
|
||||
|
||||
/* Recover DAC Volumes */
|
||||
regmap_write(rd->regmap, DACVOLL0, rd->vols.vol_l);
|
||||
regmap_write(rd->regmap, DACVOLR0, rd->vols.vol_r);
|
||||
regmap_write(rd->regmap, DACVOGP, rd->vols.polarity);
|
||||
|
||||
gpiod_set_value(rd->pa_ctl, 1);
|
||||
if (rd->pa_ctl_delay_ms)
|
||||
msleep(rd->pa_ctl_delay_ms);
|
||||
@@ -425,11 +499,44 @@ static void rk_dsm_pcm_shutdown(struct snd_pcm_substream *substream,
|
||||
rk_dsm_reset(rd);
|
||||
}
|
||||
|
||||
static int rk_dsm_pcm_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct rk_dsm_priv *rd =
|
||||
snd_soc_component_get_drvdata(dai->component);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
/**
|
||||
* After receiving the stop action, adjust the volume to the
|
||||
* minimum as soon as possible and switch the differential pair
|
||||
* of DSM to IO state, so that the same direction change of P/N
|
||||
* level can reduce the pop sound to the minimum.
|
||||
*/
|
||||
regmap_write(rd->regmap, DACVOLL0, RKDSM_VOL_VAL_MAX);
|
||||
regmap_write(rd->regmap, DACVOLR0, RKDSM_VOL_VAL_MAX);
|
||||
regmap_write(rd->regmap, DACVOGP, DSM_DACVOGP_VOLGPL0_NEG | DSM_DACVOGP_VOLGPR0_NEG);
|
||||
regcache_cache_only(rd->regmap, false);
|
||||
regcache_mark_dirty(rd->regmap);
|
||||
regcache_sync(rd->regmap);
|
||||
if (rd->data && rd->data->iomux_switch)
|
||||
rd->data->iomux_switch(rd->dev, RKDSM_ON_GPIO);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops rd_dai_ops = {
|
||||
.hw_params = rk_dsm_hw_params,
|
||||
.set_fmt = rk_dsm_set_dai_fmt,
|
||||
.startup = rk_dsm_pcm_startup,
|
||||
.shutdown = rk_dsm_pcm_shutdown,
|
||||
.trigger = rk_dsm_pcm_trigger,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver rd_dai[] = {
|
||||
@@ -473,7 +580,71 @@ static const struct regmap_config rd_regmap_config = {
|
||||
static int rk3506_soc_init(struct device *dev)
|
||||
{
|
||||
struct rk_dsm_priv *rd = dev_get_drvdata(dev);
|
||||
struct rk_dsm_iomux_res res[] = {
|
||||
{ .regbase = RK3506_IOC1_REG_BASE, .size = 0x100, },
|
||||
{ .regbase = RK3506_IOC2_REG_BASE, .size = 0x100, },
|
||||
};
|
||||
int c;
|
||||
|
||||
rd->iomuxes.res_num = ARRAY_SIZE(res);
|
||||
rd->iomuxes.ioc = devm_kcalloc(dev, rd->iomuxes.res_num, sizeof(void __iomem *), GFP_KERNEL);
|
||||
if (!rd->iomuxes.ioc)
|
||||
return -ENOMEM;
|
||||
|
||||
for (c = 0; c < rd->iomuxes.res_num; c++) {
|
||||
rd->iomuxes.ioc[c] = ioremap(res[c].regbase, res[c].size);
|
||||
if (!rd->iomuxes.ioc[c]) {
|
||||
int n;
|
||||
|
||||
/* iounmap previous mapped address */
|
||||
for (n = 0; n < rd->iomuxes.res_num; n++) {
|
||||
if (rd->iomuxes.ioc[n]) {
|
||||
iounmap(rd->iomuxes.ioc[n]);
|
||||
rd->iomuxes.ioc[n] = NULL;
|
||||
}
|
||||
}
|
||||
dev_err(dev, "ioremap res[%d] start: 0x%lx failed\n",
|
||||
c, (unsigned long)res[c].regbase);
|
||||
return -ENOMEM;
|
||||
}
|
||||
dev_info(dev, "ioremap ioc res[%d] start: 0x%lx success\n", c, (unsigned long)res[c].regbase);
|
||||
}
|
||||
|
||||
rd->pinctrl = devm_pinctrl_get(dev);
|
||||
if (!IS_ERR_OR_NULL(rd->pinctrl)) {
|
||||
const char *io_name;
|
||||
const char *pin_name;
|
||||
|
||||
if (rd->iomuxes.audm_en == (RK3506_DSM_AUDM0_EN | RK3506_DSM_AUDM1_EN)) {
|
||||
io_name = "audm0m1-iodown";
|
||||
pin_name = "audm0m1-pins";
|
||||
} else if (rd->iomuxes.audm_en == RK3506_DSM_AUDM1_EN) {
|
||||
io_name = "audm1-iodown";
|
||||
pin_name = "audm1-pins";
|
||||
} else {
|
||||
/* default case */
|
||||
io_name = "audm0-iodown";
|
||||
pin_name = "audm0-pins";
|
||||
rd->iomuxes.audm_en = RK3506_DSM_AUDM0_EN;
|
||||
}
|
||||
|
||||
rd->io_state = pinctrl_lookup_state(rd->pinctrl, io_name);
|
||||
if (IS_ERR(rd->io_state)) {
|
||||
rd->io_state = NULL;
|
||||
dev_err(dev, "Have no dsm pinctrl io state by: %s\n", io_name);
|
||||
}
|
||||
|
||||
rd->pin_state = pinctrl_lookup_state(rd->pinctrl, pin_name);
|
||||
if (IS_ERR(rd->pin_state)) {
|
||||
rd->pin_state = NULL;
|
||||
dev_err(dev, "Have no dsm pinctrl pin state, by: %s\n", pin_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (rd->data && rd->data->iomux_switch)
|
||||
rd->data->iomux_switch(rd->dev, RKDSM_ON_GPIO);
|
||||
|
||||
dev_info(dev, "iomuxes audm_en: 0x%x\n", rd->iomuxes.audm_en);
|
||||
/* enable internal codec to sai3 */
|
||||
return regmap_write(rd->grf, RK3506_GRF_SOC_CON0,
|
||||
BIT(RK3506_DSM_SEL) << 16 | BIT(RK3506_DSM_SEL));
|
||||
@@ -482,13 +653,102 @@ static int rk3506_soc_init(struct device *dev)
|
||||
static void rk3506_soc_deinit(struct device *dev)
|
||||
{
|
||||
struct rk_dsm_priv *rd = dev_get_drvdata(dev);
|
||||
int c;
|
||||
|
||||
regmap_write(rd->grf, RK3506_GRF_SOC_CON0, BIT(RK3506_DSM_SEL) << 16);
|
||||
for (c = 0; c < rd->iomuxes.res_num; c++) {
|
||||
if (rd->iomuxes.ioc[c]) {
|
||||
iounmap(rd->iomuxes.ioc[c]);
|
||||
rd->iomuxes.ioc[c] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int rk3506_soc_iomux_switch(struct device *dev, int type)
|
||||
{
|
||||
struct rk_dsm_priv *rd = dev_get_drvdata(dev);
|
||||
struct rk_dsm_iomux *iomuxes = &rd->iomuxes;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
if (iomuxes->audm_en & RK3506_DSM_AUDM0_EN) {
|
||||
/**
|
||||
* DSM_AUD_RN_M0 - GPIO1_C1
|
||||
* DSM_AUD_RP_M0 - GPIO1_C2
|
||||
* DSM_AUD_LN_M0 - GPIO1_D0
|
||||
* DSM_AUD_LP_M0 - GPIO1_D1
|
||||
*/
|
||||
if (type == RKDSM_ON_FUNC) {
|
||||
writel(((RK3506_GPIO1_IOC_GPIO1C2_SEL_MASK |
|
||||
RK3506_GPIO1_IOC_GPIO1C1_SEL_MASK) << 16) |
|
||||
(RK3506_GPIO1_IOC_DSM_AUD_RP_M0 |
|
||||
RK3506_GPIO1_IOC_DSM_AUD_RN_M0),
|
||||
iomuxes->ioc[0] + RK3506_GPIO1_IOC_GPIO1C_IOMUX_SEL_0);
|
||||
writel(((RK3506_GPIO1_IOC_GPIO1D1_SEL_MASK |
|
||||
RK3506_GPIO1_IOC_GPIO1D0_SEL_MASK) << 16) |
|
||||
(RK3506_GPIO1_IOC_DSM_AUD_LP_M0 |
|
||||
RK3506_GPIO1_IOC_DSM_AUD_LN_M0),
|
||||
iomuxes->ioc[0] + RK3506_GPIO1_IOC_GPIO1D_IOMUX_SEL_0);
|
||||
} else {
|
||||
/* RKDSM_ON_GPIO */
|
||||
writel(((RK3506_GPIO1_IOC_GPIO1C2_SEL_MASK |
|
||||
RK3506_GPIO1_IOC_GPIO1C1_SEL_MASK) << 16) |
|
||||
(RK3506_GPIO1_IOC_GPIO2C2 |
|
||||
RK3506_GPIO1_IOC_GPIO2C1),
|
||||
iomuxes->ioc[0] + RK3506_GPIO1_IOC_GPIO1C_IOMUX_SEL_0);
|
||||
writel(((RK3506_GPIO1_IOC_GPIO1D1_SEL_MASK |
|
||||
RK3506_GPIO1_IOC_GPIO1D0_SEL_MASK) << 16) |
|
||||
(RK3506_GPIO1_IOC_GPIO2D1 |
|
||||
RK3506_GPIO1_IOC_GPIO2D0),
|
||||
iomuxes->ioc[0] + RK3506_GPIO1_IOC_GPIO1D_IOMUX_SEL_0);
|
||||
}
|
||||
}
|
||||
|
||||
if (iomuxes->audm_en & RK3506_DSM_AUDM1_EN) {
|
||||
/**
|
||||
* DSM_AUD_RN_M1 - GPIO2_B4
|
||||
* DSM_AUD_RP_M1 - GPIO2_B5
|
||||
* DSM_AUD_LN_M1 - GPIO2_B6
|
||||
* DSM_AUD_LP_M1 - GPIO2_B7
|
||||
*/
|
||||
if (type == RKDSM_ON_FUNC) {
|
||||
writel(((RK3506_GPIO2_IOC_GPIO2B7_SEL_MASK |
|
||||
RK3506_GPIO2_IOC_GPIO2B6_SEL_MASK |
|
||||
RK3506_GPIO2_IOC_GPIO2B5_SEL_MASK |
|
||||
RK3506_GPIO2_IOC_GPIO2B4_SEL_MASK) << 16) |
|
||||
(RK3506_GPIO2_IOC_DSM_AUD_LP_M1 |
|
||||
RK3506_GPIO2_IOC_DSM_AUD_LN_M1 |
|
||||
RK3506_GPIO2_IOC_DSM_AUD_RP_M1 |
|
||||
RK3506_GPIO2_IOC_DSM_AUD_RN_M1),
|
||||
iomuxes->ioc[1] + RK3506_GPIO2_IOC_GPIO2B_IOMUX_SEL_1);
|
||||
} else {
|
||||
/* RKDSM_ON_GPIO */
|
||||
writel(((RK3506_GPIO2_IOC_GPIO2B7_SEL_MASK |
|
||||
RK3506_GPIO2_IOC_GPIO2B6_SEL_MASK |
|
||||
RK3506_GPIO2_IOC_GPIO2B5_SEL_MASK |
|
||||
RK3506_GPIO2_IOC_GPIO2B4_SEL_MASK) << 16) |
|
||||
(RK3506_GPIO2_IOC_GPIO2B7 |
|
||||
RK3506_GPIO2_IOC_GPIO2B6 |
|
||||
RK3506_GPIO2_IOC_GPIO2B5 |
|
||||
RK3506_GPIO2_IOC_GPIO2B4),
|
||||
iomuxes->ioc[1] + RK3506_GPIO2_IOC_GPIO2B_IOMUX_SEL_1);
|
||||
}
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
|
||||
/* Keeping sync with pinctrl framework */
|
||||
if (type == RKDSM_ON_FUNC)
|
||||
pinctrl_select_state(rd->pinctrl, rd->pin_state);
|
||||
else
|
||||
pinctrl_select_state(rd->pinctrl, rd->io_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rk_dsm_soc_data rk3506_data = {
|
||||
.init = rk3506_soc_init,
|
||||
.deinit = rk3506_soc_deinit,
|
||||
.iomux_switch = rk3506_soc_iomux_switch,
|
||||
};
|
||||
|
||||
static int rk3562_soc_init(struct device *dev)
|
||||
@@ -601,6 +861,9 @@ static int rk_dsm_platform_probe(struct platform_device *pdev)
|
||||
&rd->pa_ctl_delay_ms))
|
||||
rd->pa_ctl_delay_ms = 0;
|
||||
|
||||
if (device_property_read_u32(&pdev->dev, "rockchip,dsm-audm-en", &rd->iomuxes.audm_en))
|
||||
rd->iomuxes.audm_en = 0;
|
||||
|
||||
rd->rc = devm_reset_control_get(&pdev->dev, "reset");
|
||||
|
||||
rd->clk_dac = devm_clk_get(&pdev->dev, "dac");
|
||||
@@ -620,6 +883,7 @@ static int rk_dsm_platform_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(rd->regmap))
|
||||
return PTR_ERR(rd->regmap);
|
||||
|
||||
rd->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, rd);
|
||||
|
||||
rd->data = device_get_match_data(&pdev->dev);
|
||||
|
||||
Reference in New Issue
Block a user