ASoC: rockchip: i2s: Add support for pinctrl idle state

This patch switch pinctrl to idle state when runtime suspend.

At the moment, it's used for workaround for I2S0/2/3 (PD_AUDIO)
slave IO issue on RK3588 SoCs.

The issue acts like that when PD_AUDIO off, the BCLK/LRCK pin
will pull down the external clk to half-level.

The root cause is that when PD_AUDIO off, the BCLK/LRCK pin
are clamped as drive output low.

This patch also try to solve clk leak for master output mode.

OTOH, the ASoC framework set pinctrl state first and then do
runtime PM resume (enable PD). it's reasonable, but for the
current issue, a few half-level cycles leak after resume, so,
we split pinctrl-clk out to control it separately.

  snd_pcm_open

      for_each_rtd_components(rtd, i, component)
      	pinctrl_pm_select_default_state(component->dev);

      ret = snd_soc_pcm_component_pm_runtime_get(rtd, substream);

e.g. pinctrl idle for i2s0

i2s0_gpio: i2s0-gpio {
	rockchip,pins =
		/* i2s0_lrck_gpio */
		<1 RK_PC5 0 &pcfg_pull_none>,
		/* i2s0_sclk_gpio */
		<1 RK_PC3 0 &pcfg_pull_none>;
};

&i2s0_8ch {
	pinctrl-names = "default", "idle", "clk";
	pinctrl-1 = <&i2s0_gpio>;
	pinctrl-2 = <&i2s0_lrck
		     &i2s0_sclk>;
};

cat /sys/kernel/debug/pinctrl/pinctrl-handles

device: fe470000.i2s current state: idle
  state: default
    type: MUX_GROUP controller rockchip-pinctrl group: i2s0-sdi0 (26) function: i2s0 (24)
    type: CONFIGS_PIN controller rockchip-pinctrl pin gpio1-28 (60)config 00000001
    type: MUX_GROUP controller rockchip-pinctrl group: i2s0-sdi1 (27) function: i2s0 (24)
    type: CONFIGS_PIN controller rockchip-pinctrl pin gpio1-27 (59)config 00000001
    type: MUX_GROUP controller rockchip-pinctrl group: i2s0-sdi2 (28) function: i2s0 (24)
    type: CONFIGS_PIN controller rockchip-pinctrl pin gpio1-26 (58)config 00000001
    type: MUX_GROUP controller rockchip-pinctrl group: i2s0-sdi3 (29) function: i2s0 (24)
    type: CONFIGS_PIN controller rockchip-pinctrl pin gpio1-25 (57)config 00000001
    type: MUX_GROUP controller rockchip-pinctrl group: i2s0-sdo0 (30) function: i2s0 (24)
    type: CONFIGS_PIN controller rockchip-pinctrl pin gpio1-23 (55)config 00000001
    type: MUX_GROUP controller rockchip-pinctrl group: i2s0-sdo1 (31) function: i2s0 (24)
    type: CONFIGS_PIN controller rockchip-pinctrl pin gpio1-24 (56)config 00000001
  state: idle
    type: MUX_GROUP controller rockchip-pinctrl group: i2s0-idle (23) function: i2s0 (24)
    type: CONFIGS_PIN controller rockchip-pinctrl pin gpio1-21 (53)config 00000001
    type: CONFIGS_PIN controller rockchip-pinctrl pin gpio1-19 (51)config 00000001
  state: clk
    type: MUX_GROUP controller rockchip-pinctrl group: i2s0-lrck (24) function: i2s0 (24)
    type: CONFIGS_PIN controller rockchip-pinctrl pin gpio1-21 (53)config 00000001
    type: MUX_GROUP controller rockchip-pinctrl group: i2s0-sclk (25) function: i2s0 (24)
    type: CONFIGS_PIN controller rockchip-pinctrl pin gpio1-19 (51)config 00000001

Fixes: 89e11e98f4 ("arm64: dts: rockchip: rk3588: Add pinctrl idle for I2S0/2/3")
Change-Id: I41dcdabd8e4015c7bd7285a03a24a3765a0b576f
Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
This commit is contained in:
Sugar Zhang
2023-09-25 09:10:50 +08:00
parent 2d0fac2bdb
commit a3d49e9543

View File

@@ -75,6 +75,7 @@ struct rk_i2s_dev {
struct pinctrl *pinctrl;
struct pinctrl_state *bclk_on;
struct pinctrl_state *bclk_off;
struct pinctrl_state *clk_state;
unsigned int clk_trcm;
unsigned int mclk_root_rate;
@@ -129,6 +130,20 @@ static int i2s_runtime_suspend(struct device *dev)
regcache_cache_only(i2s->regmap, true);
clk_disable_unprepare(i2s->mclk);
pinctrl_pm_select_idle_state(dev);
return 0;
}
static int rockchip_i2s_pinctrl_select_clk_state(struct device *dev)
{
struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(i2s->pinctrl) || !i2s->clk_state)
return 0;
pinctrl_select_state(i2s->pinctrl, i2s->clk_state);
return 0;
}
@@ -137,6 +152,13 @@ static int i2s_runtime_resume(struct device *dev)
struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
int ret;
/*
* pinctrl default state is invoked by ASoC framework, so,
* we just handle clk state here if DT assigned.
*/
if (i2s->is_master_mode)
rockchip_i2s_pinctrl_select_clk_state(dev);
ret = clk_prepare_enable(i2s->mclk);
if (ret) {
dev_err(i2s->dev, "clock enable failed %d\n", ret);
@@ -150,6 +172,13 @@ static int i2s_runtime_resume(struct device *dev)
if (ret)
clk_disable_unprepare(i2s->mclk);
/*
* should be placed after regcache sync done to back
* to the slave mode and then enable clk state.
*/
if (!i2s->is_master_mode)
rockchip_i2s_pinctrl_select_clk_state(dev);
return ret;
}
@@ -1202,6 +1231,12 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
return -EINVAL;
}
}
i2s->clk_state = pinctrl_lookup_state(i2s->pinctrl, "clk");
if (IS_ERR(i2s->clk_state)) {
i2s->clk_state = NULL;
dev_dbg(i2s->dev, "Have no clk pinctrl state\n");
}
} else {
dev_dbg(&pdev->dev, "failed to find i2s pinctrl\n");
}