diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index 5d26885a1d08fc6e7d646e255206059b098f0f98..2274c0897f7263f53d8b345d55ac48cf1002e184 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -10,6 +10,7 @@ * */ #include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/delay.h> #include <linux/dmaengine.h> #include <linux/module.h> @@ -121,8 +122,59 @@ static int fsl_rpmsg_startup(struct snd_pcm_substream *substream, return ret; } +static int fsl_rpmsg_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct fsl_rpmsg_i2s *rpmsg_i2s = snd_soc_dai_get_drvdata(dai); + struct clk *p = rpmsg_i2s->mclk, *pll = 0, *npll = 0; + u64 rate = params_rate(params); + int ret; + + while (p && rpmsg_i2s->pll8k && rpmsg_i2s->pll11k) { + struct clk *pp = clk_get_parent(p); + + if (clk_is_match(pp, rpmsg_i2s->pll8k) || + clk_is_match(pp, rpmsg_i2s->pll11k)) { + pll = pp; + break; + } + p = pp; + } + + if (pll) { + npll = (do_div(rate, 8000) ? rpmsg_i2s->pll11k : + rpmsg_i2s->pll8k); + if (!clk_is_match(pll, npll)) { + ret = clk_set_parent(p, npll); + if (ret < 0) + dev_warn(dai->dev, + "failed to set parent %s: %d\n", + __clk_get_name(npll), ret); + } + } + + ret = clk_prepare_enable(rpmsg_i2s->mclk); + if (ret) + dev_err(dai->dev, "failed to enable mclk: %d\n", ret); + + return ret; +} + +static int fsl_rpmsg_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct fsl_rpmsg_i2s *rpmsg_i2s = snd_soc_dai_get_drvdata(dai); + + clk_disable_unprepare(rpmsg_i2s->mclk); + + return 0; +} + static const struct snd_soc_dai_ops fsl_rpmsg_dai_ops = { .startup = fsl_rpmsg_startup, + .hw_params = fsl_rpmsg_hw_params, + .hw_free = fsl_rpmsg_hw_free, }; static struct snd_soc_dai_driver fsl_rpmsg_i2s_dai = { @@ -359,6 +411,26 @@ static int fsl_rpmsg_i2s_probe(struct platform_device *pdev) &i2s_info->prealloc_buffer_size)) i2s_info->prealloc_buffer_size = IMX_DEFAULT_DMABUF_SIZE; + rpmsg_i2s->ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(rpmsg_i2s->ipg)) + rpmsg_i2s->ipg = NULL; + + rpmsg_i2s->mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(rpmsg_i2s->mclk)) + rpmsg_i2s->mclk = NULL; + + rpmsg_i2s->dma = devm_clk_get(&pdev->dev, "dma"); + if (IS_ERR(rpmsg_i2s->dma)) + rpmsg_i2s->dma = NULL; + + rpmsg_i2s->pll8k = devm_clk_get(&pdev->dev, "pll8k"); + if (IS_ERR(rpmsg_i2s->pll8k)) + rpmsg_i2s->pll8k = NULL; + + rpmsg_i2s->pll11k = devm_clk_get(&pdev->dev, "pll11k"); + if (IS_ERR(rpmsg_i2s->pll11k)) + rpmsg_i2s->pll11k = NULL; + platform_set_drvdata(pdev, rpmsg_i2s); pm_runtime_enable(&pdev->dev); @@ -385,9 +457,27 @@ static int fsl_rpmsg_i2s_remove(struct platform_device *pdev) static int fsl_rpmsg_i2s_runtime_resume(struct device *dev) { struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(rpmsg_i2s->ipg); + if (ret) { + dev_err(dev, "failed to enable ipg clock: %d\n", ret); + goto ipg_err; + } + + ret = clk_prepare_enable(rpmsg_i2s->dma); + if (ret < 0) { + dev_err(dev, "Failed to enable dma clock %d\n", ret); + goto dma_err; + } cpu_latency_qos_add_request(&rpmsg_i2s->pm_qos_req, 0); return 0; + +dma_err: + clk_disable_unprepare(rpmsg_i2s->ipg); +ipg_err: + return ret; } static int fsl_rpmsg_i2s_runtime_suspend(struct device *dev) @@ -395,6 +485,10 @@ static int fsl_rpmsg_i2s_runtime_suspend(struct device *dev) struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(dev); cpu_latency_qos_remove_request(&rpmsg_i2s->pm_qos_req); + + clk_disable_unprepare(rpmsg_i2s->dma); + clk_disable_unprepare(rpmsg_i2s->ipg); + return 0; } #endif diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.h b/sound/soc/fsl/fsl_rpmsg_i2s.h index dab7aeab3bcd78aec69bb0f4be821d998466bd41..5808a705523166c20e12183e86d32520629ac24f 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.h +++ b/sound/soc/fsl/fsl_rpmsg_i2s.h @@ -433,6 +433,11 @@ struct fsl_rpmsg_i2s { int rates; u64 formats; int enable_lpa; + struct clk *ipg; + struct clk *mclk; + struct clk *dma; + struct clk *pll8k; + struct clk *pll11k; }; #define RPMSG_CODEC_DRV_NAME_WM8960 "rpmsg-audio-codec-wm8960"