diff options
author | Takashi Iwai <tiwai@suse.de> | 2017-11-13 17:45:57 +0300 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2017-11-13 17:45:57 +0300 |
commit | 76727c2c3bf4a5e58dff8cca23d0147ba08fb2c8 (patch) | |
tree | c84c07b9deac4425190777a962f6788d355a0dd1 /sound/soc/img/img-i2s-in.c | |
parent | c429bda21ffafb28f02fb2eb4055b4ab6879ed58 (diff) | |
parent | df6a3e245541ac61cc99f2887437e0a43dd08f2e (diff) | |
download | linux-76727c2c3bf4a5e58dff8cca23d0147ba08fb2c8.tar.xz |
Merge tag 'asoc-v4.15' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v4.15
The biggest thing this release has been the conversion of the AC98 bus
to the driver model, that's been a long time coming so thanks to Robert
Jarzmik for his dedication there. Due to there being some AC97 MFD
there's a few fairly large changes in input and the MFD layer, mainly to
the wm97xx driver.
There's also some drivers/drm changes to support the new AMD Stoney
platform, these are shared with the DRM subsystem and should be being
merged via both.
Within the subsystem the overwhelming bulk of the changes is in the
Intel drivers which continue to need lots of cleanups and fixes, this
release they've also gained support for their open source firmware.
There's also some large changs in the core as Morimoto-san continues to
mirror operations into the component level in preparation for conversion
of drivers to that.
- The AC97 bus has finally caught up with the driver model thanks to
some dedicated and persistent work from Robert Jarzmik.
- Continued work from Morimoto-san on moving us towards being able to
use components for everything.
- Lots of cleanups for the Intel platform code, including support for
their open source audio firmware.
- Support for scaling MCLK with sample rate in simple-card.
- Support for AMD Stoney platform.
Diffstat (limited to 'sound/soc/img/img-i2s-in.c')
-rw-r--r-- | sound/soc/img/img-i2s-in.c | 130 |
1 files changed, 118 insertions, 12 deletions
diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c index 567f9767fb73..d7fbb0a0a28b 100644 --- a/sound/soc/img/img-i2s-in.c +++ b/sound/soc/img/img-i2s-in.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <sound/core.h> @@ -60,8 +61,33 @@ struct img_i2s_in { void __iomem *channel_base; unsigned int active_channels; struct snd_soc_dai_driver dai_driver; + u32 suspend_ctl; + u32 *suspend_ch_ctl; }; +static int img_i2s_in_runtime_suspend(struct device *dev) +{ + struct img_i2s_in *i2s = dev_get_drvdata(dev); + + clk_disable_unprepare(i2s->clk_sys); + + return 0; +} + +static int img_i2s_in_runtime_resume(struct device *dev) +{ + struct img_i2s_in *i2s = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(i2s->clk_sys); + if (ret) { + dev_err(dev, "Unable to enable sys clock\n"); + return ret; + } + + return 0; +} + static inline void img_i2s_in_writel(struct img_i2s_in *i2s, u32 val, u32 reg) { writel(val, i2s->base + reg); @@ -279,7 +305,7 @@ static int img_i2s_in_hw_params(struct snd_pcm_substream *substream, static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai); - int i; + int i, ret; u32 chan_control_mask, lrd_set = 0, blkp_set = 0, chan_control_set = 0; u32 reg; @@ -319,6 +345,10 @@ static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) chan_control_mask = IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK; + ret = pm_runtime_get_sync(i2s->dev); + if (ret < 0) + return ret; + for (i = 0; i < i2s->active_channels; i++) img_i2s_in_ch_disable(i2s, i); @@ -338,6 +368,8 @@ static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) for (i = 0; i < i2s->active_channels; i++) img_i2s_in_ch_enable(i2s, i); + pm_runtime_put(i2s->dev); + return 0; } @@ -427,9 +459,15 @@ static int img_i2s_in_probe(struct platform_device *pdev) return PTR_ERR(i2s->clk_sys); } - ret = clk_prepare_enable(i2s->clk_sys); - if (ret) - return ret; + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_i2s_in_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto err_suspend; i2s->active_channels = 1; i2s->dma_data.addr = res->start + IMG_I2S_IN_RX_FIFO; @@ -447,7 +485,7 @@ static int img_i2s_in_probe(struct platform_device *pdev) if (IS_ERR(rst)) { if (PTR_ERR(rst) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; - goto err_clk_disable; + goto err_suspend; } dev_dbg(dev, "No top level reset found\n"); @@ -469,42 +507,110 @@ static int img_i2s_in_probe(struct platform_device *pdev) IMG_I2S_IN_CH_CTL_JUST_MASK | IMG_I2S_IN_CH_CTL_FW_MASK, IMG_I2S_IN_CH_CTL); + pm_runtime_put(&pdev->dev); + + i2s->suspend_ch_ctl = devm_kzalloc(dev, + sizeof(*i2s->suspend_ch_ctl) * i2s->max_i2s_chan, GFP_KERNEL); + if (!i2s->suspend_ch_ctl) { + ret = -ENOMEM; + goto err_suspend; + } + ret = devm_snd_soc_register_component(dev, &img_i2s_in_component, &i2s->dai_driver, 1); if (ret) - goto err_clk_disable; + goto err_suspend; ret = devm_snd_dmaengine_pcm_register(dev, &img_i2s_in_dma_config, 0); if (ret) - goto err_clk_disable; + goto err_suspend; return 0; -err_clk_disable: - clk_disable_unprepare(i2s->clk_sys); +err_suspend: + if (!pm_runtime_enabled(&pdev->dev)) + img_i2s_in_runtime_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); return ret; } static int img_i2s_in_dev_remove(struct platform_device *pdev) { - struct img_i2s_in *i2s = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + img_i2s_in_runtime_suspend(&pdev->dev); - clk_disable_unprepare(i2s->clk_sys); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int img_i2s_in_suspend(struct device *dev) +{ + struct img_i2s_in *i2s = dev_get_drvdata(dev); + int i, ret; + u32 reg; + + if (pm_runtime_status_suspended(dev)) { + ret = img_i2s_in_runtime_resume(dev); + if (ret) + return ret; + } + + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL); + i2s->suspend_ch_ctl[i] = reg; + } + + i2s->suspend_ctl = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL); + + img_i2s_in_runtime_suspend(dev); return 0; } +static int img_i2s_in_resume(struct device *dev) +{ + struct img_i2s_in *i2s = dev_get_drvdata(dev); + int i, ret; + u32 reg; + + ret = img_i2s_in_runtime_resume(dev); + if (ret) + return ret; + + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = i2s->suspend_ch_ctl[i]; + img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL); + } + + img_i2s_in_writel(i2s, i2s->suspend_ctl, IMG_I2S_IN_CTL); + + if (pm_runtime_status_suspended(dev)) + img_i2s_in_runtime_suspend(dev); + + return 0; +} +#endif + static const struct of_device_id img_i2s_in_of_match[] = { { .compatible = "img,i2s-in" }, {} }; MODULE_DEVICE_TABLE(of, img_i2s_in_of_match); +static const struct dev_pm_ops img_i2s_in_pm_ops = { + SET_RUNTIME_PM_OPS(img_i2s_in_runtime_suspend, + img_i2s_in_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(img_i2s_in_suspend, img_i2s_in_resume) +}; + static struct platform_driver img_i2s_in_driver = { .driver = { .name = "img-i2s-in", - .of_match_table = img_i2s_in_of_match + .of_match_table = img_i2s_in_of_match, + .pm = &img_i2s_in_pm_ops }, .probe = img_i2s_in_probe, .remove = img_i2s_in_dev_remove |