Commit 9cb2b379 authored by Shengjiu Wang's avatar Shengjiu Wang Committed by Mark Brown

ASoC: fsl_spdif: Add pm runtime function

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: default avatarShengjiu Wang <shengjiu.wang@nxp.com>
Acked-by: default avatarNicolin Chen <nicoleotsuka@gmail.com>
Link: https://lore.kernel.org/r/579c0d71e976f34f23f40daa9f1aa06c4baca2f1.1592552389.git.shengjiu.wang@nxp.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 3499f9ad
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/pm_runtime.h>
#include <sound/asoundef.h> #include <sound/asoundef.h>
#include <sound/dmaengine_pcm.h> #include <sound/dmaengine_pcm.h>
...@@ -495,29 +496,14 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, ...@@ -495,29 +496,14 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
struct platform_device *pdev = spdif_priv->pdev; struct platform_device *pdev = spdif_priv->pdev;
struct regmap *regmap = spdif_priv->regmap; struct regmap *regmap = spdif_priv->regmap;
u32 scr, mask; u32 scr, mask;
int i;
int ret; int ret;
/* Reset module and interrupts only for first initialization */ /* Reset module and interrupts only for first initialization */
if (!snd_soc_dai_active(cpu_dai)) { 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); ret = spdif_softreset(spdif_priv);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to soft reset\n"); dev_err(&pdev->dev, "failed to soft reset\n");
goto err; return ret;
} }
/* Disable all the interrupts */ /* Disable all the interrupts */
...@@ -531,18 +517,10 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, ...@@ -531,18 +517,10 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK | mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
SCR_TXSEL_MASK | SCR_USRC_SEL_MASK | SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
SCR_TXFIFO_FSEL_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 { } else {
scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC; scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC;
mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK| mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_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); regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
...@@ -550,17 +528,6 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, ...@@ -550,17 +528,6 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0); regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);
return 0; 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;
} }
static void fsl_spdif_shutdown(struct snd_pcm_substream *substream, static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
...@@ -569,20 +536,17 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream, ...@@ -569,20 +536,17 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data; 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 fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
struct regmap *regmap = spdif_priv->regmap; struct regmap *regmap = spdif_priv->regmap;
u32 scr, mask, i; u32 scr, mask;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
scr = 0; scr = 0;
mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK | mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
SCR_TXSEL_MASK | SCR_USRC_SEL_MASK | SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
SCR_TXFIFO_FSEL_MASK; SCR_TXFIFO_FSEL_MASK;
for (i = 0; i < SPDIF_TXRATE_MAX; i++)
clk_disable_unprepare(spdif_priv->txclk[i]);
} else { } else {
scr = SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO; scr = SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO;
mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK| mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK; SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
clk_disable_unprepare(spdif_priv->rxclk);
} }
regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr); regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
...@@ -591,9 +555,6 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream, ...@@ -591,9 +555,6 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
spdif_intr_status_clear(spdif_priv); spdif_intr_status_clear(spdif_priv);
regmap_update_bits(regmap, REG_SPDIF_SCR, regmap_update_bits(regmap, REG_SPDIF_SCR,
SCR_LOW_POWER, SCR_LOW_POWER); 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 +1311,8 @@ static int fsl_spdif_probe(struct platform_device *pdev) ...@@ -1350,6 +1311,8 @@ static int fsl_spdif_probe(struct platform_device *pdev)
/* Register with ASoC */ /* Register with ASoC */
dev_set_drvdata(&pdev->dev, spdif_priv); 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, ret = devm_snd_soc_register_component(&pdev->dev, &fsl_spdif_component,
&spdif_priv->cpu_dai_drv, 1); &spdif_priv->cpu_dai_drv, 1);
...@@ -1365,36 +1328,90 @@ static int fsl_spdif_probe(struct platform_device *pdev) ...@@ -1365,36 +1328,90 @@ static int fsl_spdif_probe(struct platform_device *pdev)
return ret; return ret;
} }
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM
static int fsl_spdif_suspend(struct device *dev) static int fsl_spdif_runtime_suspend(struct device *dev)
{ {
struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev); struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
int i;
regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC, regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC,
&spdif_priv->regcache_srpc); &spdif_priv->regcache_srpc);
regcache_cache_only(spdif_priv->regmap, true); 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; 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); 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_tx_clk;
}
ret = clk_prepare_enable(spdif_priv->rxclk);
if (ret)
goto disable_tx_clk;
regcache_cache_only(spdif_priv->regmap, false); regcache_cache_only(spdif_priv->regmap, false);
regcache_mark_dirty(spdif_priv->regmap);
regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SRPC, regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SRPC,
SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK, SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK,
spdif_priv->regcache_srpc); 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:
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 /* CONFIG_PM */
static const struct dev_pm_ops fsl_spdif_pm = { 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[] = { static const struct of_device_id fsl_spdif_dt_ids[] = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment