diff --git a/sound/soc/codecs/rk_dsm.c b/sound/soc/codecs/rk_dsm.c index 15cb7f89e1e9..d198d0ed9316 100644 --- a/sound/soc/codecs/rk_dsm.c +++ b/sound/soc/codecs/rk_dsm.c @@ -23,15 +23,72 @@ #include #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);