mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 19:08:57 +09:00
ASoC: rockchip: i2s-tdm: 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.
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
Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
Change-Id: Ibda030dad44830f9f4eeb6448c14d4053a096fc6
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clk/rockchip.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
@@ -88,6 +89,8 @@ struct rk_i2s_tdm_dev {
|
||||
struct snd_pcm_substream *substreams[SNDRV_PCM_STREAM_LAST + 1];
|
||||
struct reset_control *tx_reset;
|
||||
struct reset_control *rx_reset;
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *clk_state;
|
||||
const struct rk_i2s_soc_data *soc_data;
|
||||
#ifdef HAVE_SYNC_RESET
|
||||
void __iomem *cru_base;
|
||||
@@ -160,6 +163,20 @@ static int i2s_tdm_runtime_suspend(struct device *dev)
|
||||
clk_disable_unprepare(i2s_tdm->mclk_tx);
|
||||
clk_disable_unprepare(i2s_tdm->mclk_rx);
|
||||
|
||||
pinctrl_pm_select_idle_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_i2s_tdm_pinctrl_select_clk_state(struct device *dev)
|
||||
{
|
||||
struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
|
||||
|
||||
if (IS_ERR_OR_NULL(i2s_tdm->pinctrl) || !i2s_tdm->clk_state)
|
||||
return 0;
|
||||
|
||||
pinctrl_select_state(i2s_tdm->pinctrl, i2s_tdm->clk_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -168,6 +185,13 @@ static int i2s_tdm_runtime_resume(struct device *dev)
|
||||
struct rk_i2s_tdm_dev *i2s_tdm = 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_tdm->is_master_mode)
|
||||
rockchip_i2s_tdm_pinctrl_select_clk_state(dev);
|
||||
|
||||
ret = clk_prepare_enable(i2s_tdm->mclk_tx);
|
||||
if (ret)
|
||||
goto err_mclk_tx;
|
||||
@@ -182,6 +206,13 @@ static int i2s_tdm_runtime_resume(struct device *dev)
|
||||
if (ret)
|
||||
goto err_regmap;
|
||||
|
||||
/*
|
||||
* should be placed after regcache sync done to back
|
||||
* to the slave mode and then enable clk state.
|
||||
*/
|
||||
if (!i2s_tdm->is_master_mode)
|
||||
rockchip_i2s_tdm_pinctrl_select_clk_state(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_regmap:
|
||||
@@ -2152,6 +2183,15 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev)
|
||||
|
||||
i2s_tdm->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf");
|
||||
|
||||
i2s_tdm->pinctrl = devm_pinctrl_get(&pdev->dev);
|
||||
if (!IS_ERR_OR_NULL(i2s_tdm->pinctrl)) {
|
||||
i2s_tdm->clk_state = pinctrl_lookup_state(i2s_tdm->pinctrl, "clk");
|
||||
if (IS_ERR(i2s_tdm->clk_state)) {
|
||||
i2s_tdm->clk_state = NULL;
|
||||
dev_dbg(i2s_tdm->dev, "Have no clk pinctrl state\n");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYNC_RESET
|
||||
sync = of_device_is_compatible(node, "rockchip,px30-i2s-tdm") ||
|
||||
of_device_is_compatible(node, "rockchip,rk1808-i2s-tdm") ||
|
||||
|
||||
Reference in New Issue
Block a user