Add pm runtime support and move clock handling there. Close the clocks at suspend to reduce the power consumption. fsl_spdif_suspend is replaced by pm_runtime_force_suspend. fsl_spdif_resume is replaced by pm_runtime_force_resume. Signed-off-by: Shengjiu Wang <shengjiu.wang@xxxxxxx> --- sound/soc/fsl/fsl_spdif.c | 113 ++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 46 deletions(-) diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 5bc0e4729341..46719fd2f1ec 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -16,6 +16,7 @@ #include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/regmap.h> +#include <linux/pm_runtime.h> #include <sound/asoundef.h> #include <sound/dmaengine_pcm.h> @@ -495,25 +496,10 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, struct platform_device *pdev = spdif_priv->pdev; struct regmap *regmap = spdif_priv->regmap; u32 scr, mask; - int i; int ret; /* Reset module and interrupts only for first initialization */ if (!snd_soc_dai_active(cpu_dai)) { - ret = clk_prepare_enable(spdif_priv->coreclk); - if (ret) { - dev_err(&pdev->dev, "failed to enable core clock\n"); - return ret; - } - - if (!IS_ERR(spdif_priv->spbaclk)) { - ret = clk_prepare_enable(spdif_priv->spbaclk); - if (ret) { - dev_err(&pdev->dev, "failed to enable spba clock\n"); - goto err_spbaclk; - } - } - ret = spdif_softreset(spdif_priv); if (ret) { dev_err(&pdev->dev, "failed to soft reset\n"); @@ -531,18 +517,10 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK | SCR_TXSEL_MASK | SCR_USRC_SEL_MASK | SCR_TXFIFO_FSEL_MASK; - for (i = 0; i < SPDIF_TXRATE_MAX; i++) { - ret = clk_prepare_enable(spdif_priv->txclk[i]); - if (ret) - goto disable_txclk; - } } else { scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC; mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK| SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK; - ret = clk_prepare_enable(spdif_priv->rxclk); - if (ret) - goto err; } regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr); @@ -551,15 +529,7 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, return 0; -disable_txclk: - for (i--; i >= 0; i--) - clk_disable_unprepare(spdif_priv->txclk[i]); err: - if (!IS_ERR(spdif_priv->spbaclk)) - clk_disable_unprepare(spdif_priv->spbaclk); -err_spbaclk: - clk_disable_unprepare(spdif_priv->coreclk); - return ret; } @@ -569,20 +539,17 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); struct regmap *regmap = spdif_priv->regmap; - u32 scr, mask, i; + u32 scr, mask; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { scr = 0; mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK | SCR_TXSEL_MASK | SCR_USRC_SEL_MASK | SCR_TXFIFO_FSEL_MASK; - for (i = 0; i < SPDIF_TXRATE_MAX; i++) - clk_disable_unprepare(spdif_priv->txclk[i]); } else { scr = SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO; mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK| SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK; - clk_disable_unprepare(spdif_priv->rxclk); } regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr); @@ -591,9 +558,6 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream, spdif_intr_status_clear(spdif_priv); regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, SCR_LOW_POWER); - if (!IS_ERR(spdif_priv->spbaclk)) - clk_disable_unprepare(spdif_priv->spbaclk); - clk_disable_unprepare(spdif_priv->coreclk); } } @@ -1350,6 +1314,8 @@ static int fsl_spdif_probe(struct platform_device *pdev) /* Register with ASoC */ dev_set_drvdata(&pdev->dev, spdif_priv); + pm_runtime_enable(&pdev->dev); + regcache_cache_only(spdif_priv->regmap, true); ret = devm_snd_soc_register_component(&pdev->dev, &fsl_spdif_component, &spdif_priv->cpu_dai_drv, 1); @@ -1365,36 +1331,91 @@ static int fsl_spdif_probe(struct platform_device *pdev) return ret; } -#ifdef CONFIG_PM_SLEEP -static int fsl_spdif_suspend(struct device *dev) +#ifdef CONFIG_PM +static int fsl_spdif_runtime_suspend(struct device *dev) { struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev); + int i; regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC, &spdif_priv->regcache_srpc); - regcache_cache_only(spdif_priv->regmap, true); - regcache_mark_dirty(spdif_priv->regmap); + + clk_disable_unprepare(spdif_priv->rxclk); + + for (i = 0; i < SPDIF_TXRATE_MAX; i++) + clk_disable_unprepare(spdif_priv->txclk[i]); + + if (!IS_ERR(spdif_priv->spbaclk)) + clk_disable_unprepare(spdif_priv->spbaclk); + clk_disable_unprepare(spdif_priv->coreclk); return 0; } -static int fsl_spdif_resume(struct device *dev) +static int fsl_spdif_runtime_resume(struct device *dev) { struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev); + int ret; + int i; + + ret = clk_prepare_enable(spdif_priv->coreclk); + if (ret) { + dev_err(dev, "failed to enable core clock\n"); + return ret; + } + + if (!IS_ERR(spdif_priv->spbaclk)) { + ret = clk_prepare_enable(spdif_priv->spbaclk); + if (ret) { + dev_err(dev, "failed to enable spba clock\n"); + goto disable_core_clk; + } + } + + for (i = 0; i < SPDIF_TXRATE_MAX; i++) { + ret = clk_prepare_enable(spdif_priv->txclk[i]); + if (ret) + goto disable_spba_clk; + } + + ret = clk_prepare_enable(spdif_priv->rxclk); + if (ret) + goto disable_tx_clk; regcache_cache_only(spdif_priv->regmap, false); + regcache_mark_dirty(spdif_priv->regmap); regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SRPC, SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK, spdif_priv->regcache_srpc); - return regcache_sync(spdif_priv->regmap); + ret = regcache_sync(spdif_priv->regmap); + if (ret) + goto disable_rx_clk; + + return 0; + +disable_rx_clk: + clk_disable_unprepare(spdif_priv->rxclk); +disable_tx_clk: +disable_spba_clk: + for (i--; i >= 0; i--) + clk_disable_unprepare(spdif_priv->txclk[i]); + if (!IS_ERR(spdif_priv->spbaclk)) + clk_disable_unprepare(spdif_priv->spbaclk); +disable_core_clk: + clk_disable_unprepare(spdif_priv->coreclk); + + return ret; } -#endif /* CONFIG_PM_SLEEP */ +#endif static const struct dev_pm_ops fsl_spdif_pm = { - SET_SYSTEM_SLEEP_PM_OPS(fsl_spdif_suspend, fsl_spdif_resume) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(fsl_spdif_runtime_suspend, fsl_spdif_runtime_resume, + NULL) }; static const struct of_device_id fsl_spdif_dt_ids[] = { -- 2.21.0