diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-14 18:01:46 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-14 18:01:46 -0800 |
commit | 4e4510fec4af08ead21f6934c1410af1f19a8cad (patch) | |
tree | 2bafab4f7cc2cdf2983186b24140f6303d4dfc8c /sound/soc/img | |
parent | 4008e6a9bcee2f3b61bb11951de0fb0ed764cb91 (diff) | |
parent | 7087cb8fad5e19113d82f47f351fc6b338948d5f (diff) | |
download | linux-4e4510fec4af08ead21f6934c1410af1f19a8cad.tar.bz2 |
Merge tag 'sound-4.15-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"There are no big surprising changes in this cycle, yet not too boring,
either. The biggest change from diffstat POV is the removal of the
legacy OSS driver codes that have been already disabled for a long
time. This will bring a few trivial merge conflicts.
As new features in ASoC side, there are two things: a new AC97 bus
implementation and AMD Stony platform support. Both include the
relevant changes shared with other subsystems, e.g. AC97 MFD changes
and DRM AMD changes.
Some other highlighted topics are:
- A bunch of USB-audio drivers got the hardening against the
malicious device accesses with a new helper code for endpoint
sanity check
- Lots of cleanups for ASoC Intel platform code, including support
for their open source audio firmware
- Continued ASoC core componentization works
- Support for scaling MCLK with sample rate in ASoC simple-card
- Stabler PCM hot-unplug capability, especially for ASoC usages"
* tag 'sound-4.15-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (302 commits)
Documentation: sound: hd-audio: notes.rst
ASoC: bcm2835: Support left/right justified and DSP modes
ASoC: bcm2835: Enforce full symmetry
ASoC: bcm2835: Support additional samplerates up to 384kHz
ASoC: bcm2835: Add support for TDM modes
ASoC: add mclk-fs support to audio graph card
ASoC: add mclk-fs to audio graph card binding
ASoC: rt5514: work around link error
ASoC: rt5514: mark PM functions as __maybe_unused
ASoC: rt5663: Check the JD status in the button pushing
ASoC: amd: Modified DMA transfer Mechanism for Playback
ASoC: rt5645: Wait for 400msec before concluding on value of RT5645_VENDOR_ID2
ASoC: sun4i-codec: fixed 32bit audio capture support for H3/H2+
ASoC: da7213: add support for DSP modes
ASoC: sun8i-codec: Add a comment on the LRCK inversion
ASoC: sun8i-codec: Set the BCLK divider
ASoC: rt5663: Delay and retry reading rt5663 ID register
ASoC: amd: use do_div rather than 64 bit division to fix 32 bit builds
ASoC: cs42l56: Fix reset GPIO name in example DT binding
ASoC: rt5514-spi: check irq status to schedule data copy in resume function
...
Diffstat (limited to 'sound/soc/img')
-rw-r--r-- | sound/soc/img/img-i2s-in.c | 130 | ||||
-rw-r--r-- | sound/soc/img/img-i2s-out.c | 120 | ||||
-rw-r--r-- | sound/soc/img/img-parallel-out.c | 6 | ||||
-rw-r--r-- | sound/soc/img/img-spdif-in.c | 110 | ||||
-rw-r--r-- | sound/soc/img/img-spdif-out.c | 87 |
5 files changed, 381 insertions, 72 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 diff --git a/sound/soc/img/img-i2s-out.c b/sound/soc/img/img-i2s-out.c index 78b7f6cd675b..30a95bcef2db 100644 --- a/sound/soc/img/img-i2s-out.c +++ b/sound/soc/img/img-i2s-out.c @@ -63,29 +63,36 @@ struct img_i2s_out { unsigned int active_channels; struct reset_control *rst; struct snd_soc_dai_driver dai_driver; + u32 suspend_ctl; + u32 *suspend_ch_ctl; }; -static int img_i2s_out_suspend(struct device *dev) +static int img_i2s_out_runtime_suspend(struct device *dev) { struct img_i2s_out *i2s = dev_get_drvdata(dev); - if (!i2s->force_clk_active) - clk_disable_unprepare(i2s->clk_ref); + clk_disable_unprepare(i2s->clk_ref); + clk_disable_unprepare(i2s->clk_sys); return 0; } -static int img_i2s_out_resume(struct device *dev) +static int img_i2s_out_runtime_resume(struct device *dev) { struct img_i2s_out *i2s = dev_get_drvdata(dev); int ret; - if (!i2s->force_clk_active) { - ret = clk_prepare_enable(i2s->clk_ref); - if (ret) { - dev_err(dev, "clk_enable failed: %d\n", ret); - return ret; - } + ret = clk_prepare_enable(i2s->clk_sys); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(i2s->clk_ref); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + clk_disable_unprepare(i2s->clk_sys); + return ret; } return 0; @@ -287,7 +294,7 @@ static int img_i2s_out_hw_params(struct snd_pcm_substream *substream, static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); - int i; + int i, ret; bool force_clk_active; u32 chan_control_mask, control_mask, chan_control_set = 0; u32 reg, control_set = 0; @@ -342,6 +349,10 @@ static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) chan_control_mask = IMG_I2S_OUT_CHAN_CTL_CLKT_MASK; + ret = pm_runtime_get_sync(i2s->dev); + if (ret < 0) + return ret; + img_i2s_out_disable(i2s); reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); @@ -361,6 +372,7 @@ static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) img_i2s_out_ch_enable(i2s, i); img_i2s_out_enable(i2s); + pm_runtime_put(i2s->dev); i2s->force_clk_active = force_clk_active; @@ -467,9 +479,20 @@ static int img_i2s_out_probe(struct platform_device *pdev) return PTR_ERR(i2s->clk_ref); } - ret = clk_prepare_enable(i2s->clk_sys); - if (ret) - return ret; + i2s->suspend_ch_ctl = devm_kzalloc(dev, + sizeof(*i2s->suspend_ch_ctl) * i2s->max_i2s_chan, GFP_KERNEL); + if (!i2s->suspend_ch_ctl) + return -ENOMEM; + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_i2s_out_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto err_suspend; reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK; img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); @@ -483,13 +506,7 @@ static int img_i2s_out_probe(struct platform_device *pdev) img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); img_i2s_out_reset(i2s); - - pm_runtime_enable(&pdev->dev); - if (!pm_runtime_enabled(&pdev->dev)) { - ret = img_i2s_out_resume(&pdev->dev); - if (ret) - goto err_pm_disable; - } + pm_runtime_put(&pdev->dev); i2s->active_channels = 1; i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO; @@ -517,26 +534,70 @@ static int img_i2s_out_probe(struct platform_device *pdev) err_suspend: if (!pm_runtime_status_suspended(&pdev->dev)) - img_i2s_out_suspend(&pdev->dev); + img_i2s_out_runtime_suspend(&pdev->dev); err_pm_disable: pm_runtime_disable(&pdev->dev); - clk_disable_unprepare(i2s->clk_sys); return ret; } static int img_i2s_out_dev_remove(struct platform_device *pdev) { - struct img_i2s_out *i2s = platform_get_drvdata(pdev); - pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) - img_i2s_out_suspend(&pdev->dev); + img_i2s_out_runtime_suspend(&pdev->dev); - clk_disable_unprepare(i2s->clk_sys); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int img_i2s_out_suspend(struct device *dev) +{ + struct img_i2s_out *i2s = dev_get_drvdata(dev); + int i, ret; + u32 reg; + + if (pm_runtime_status_suspended(dev)) { + ret = img_i2s_out_runtime_resume(dev); + if (ret) + return ret; + } + + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL); + i2s->suspend_ch_ctl[i] = reg; + } + + i2s->suspend_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); + + img_i2s_out_runtime_suspend(dev); + + return 0; +} + +static int img_i2s_out_resume(struct device *dev) +{ + struct img_i2s_out *i2s = dev_get_drvdata(dev); + int i, ret; + u32 reg; + + ret = img_i2s_out_runtime_resume(dev); + if (ret) + return ret; + + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = i2s->suspend_ch_ctl[i]; + img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); + } + + img_i2s_out_writel(i2s, i2s->suspend_ctl, IMG_I2S_OUT_CTL); + + if (pm_runtime_status_suspended(dev)) + img_i2s_out_runtime_suspend(dev); return 0; } +#endif static const struct of_device_id img_i2s_out_of_match[] = { { .compatible = "img,i2s-out" }, @@ -545,8 +606,9 @@ static const struct of_device_id img_i2s_out_of_match[] = { MODULE_DEVICE_TABLE(of, img_i2s_out_of_match); static const struct dev_pm_ops img_i2s_out_pm_ops = { - SET_RUNTIME_PM_OPS(img_i2s_out_suspend, - img_i2s_out_resume, NULL) + SET_RUNTIME_PM_OPS(img_i2s_out_runtime_suspend, + img_i2s_out_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(img_i2s_out_suspend, img_i2s_out_resume) }; static struct platform_driver img_i2s_out_driver = { diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c index 23b0f0f6ec9c..acc005217be0 100644 --- a/sound/soc/img/img-parallel-out.c +++ b/sound/soc/img/img-parallel-out.c @@ -153,6 +153,7 @@ static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai); u32 reg, control_set = 0; + int ret; switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: @@ -164,9 +165,14 @@ static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } + ret = pm_runtime_get_sync(prl->dev); + if (ret < 0) + return ret; + reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL); reg = (reg & ~IMG_PRL_OUT_CTL_EDGE_MASK) | control_set; img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL); + pm_runtime_put(prl->dev); return 0; } diff --git a/sound/soc/img/img-spdif-in.c b/sound/soc/img/img-spdif-in.c index 8adfd65d4390..cedd40c8d1f3 100644 --- a/sound/soc/img/img-spdif-in.c +++ b/sound/soc/img/img-spdif-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> @@ -82,11 +83,36 @@ struct img_spdif_in { unsigned int single_freq; unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN]; bool active; + u32 suspend_clkgen; + u32 suspend_ctl; /* Write-only registers */ unsigned int aclkgen_regs[IMG_SPDIF_IN_NUM_ACLKGEN]; }; +static int img_spdif_in_runtime_suspend(struct device *dev) +{ + struct img_spdif_in *spdif = dev_get_drvdata(dev); + + clk_disable_unprepare(spdif->clk_sys); + + return 0; +} + +static int img_spdif_in_runtime_resume(struct device *dev) +{ + struct img_spdif_in *spdif = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(spdif->clk_sys); + if (ret) { + dev_err(dev, "Unable to enable sys clock\n"); + return ret; + } + + return 0; +} + static inline void img_spdif_in_writel(struct img_spdif_in *spdif, u32 val, u32 reg) { @@ -723,15 +749,21 @@ static int img_spdif_in_probe(struct platform_device *pdev) return PTR_ERR(spdif->clk_sys); } - ret = clk_prepare_enable(spdif->clk_sys); - if (ret) - return ret; + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_spdif_in_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto err_suspend; rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); if (IS_ERR(rst)) { if (PTR_ERR(rst) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; - goto err_clk_disable; + goto err_pm_put; } dev_dbg(dev, "No top level reset found\n"); img_spdif_in_writel(spdif, IMG_SPDIF_IN_SOFT_RESET_MASK, @@ -759,42 +791,98 @@ static int img_spdif_in_probe(struct platform_device *pdev) IMG_SPDIF_IN_CTL_TRK_MASK; img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); + pm_runtime_put(&pdev->dev); + ret = devm_snd_soc_register_component(&pdev->dev, &img_spdif_in_component, &img_spdif_in_dai, 1); if (ret) - goto err_clk_disable; + goto err_suspend; ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) - goto err_clk_disable; + goto err_suspend; return 0; -err_clk_disable: - clk_disable_unprepare(spdif->clk_sys); +err_pm_put: + pm_runtime_put(&pdev->dev); +err_suspend: + if (!pm_runtime_enabled(&pdev->dev)) + img_spdif_in_runtime_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); return ret; } static int img_spdif_in_dev_remove(struct platform_device *pdev) { - struct img_spdif_in *spdif = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + img_spdif_in_runtime_suspend(&pdev->dev); - clk_disable_unprepare(spdif->clk_sys); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int img_spdif_in_suspend(struct device *dev) +{ + struct img_spdif_in *spdif = dev_get_drvdata(dev); + int ret; + + if (pm_runtime_status_suspended(dev)) { + ret = img_spdif_in_runtime_resume(dev); + if (ret) + return ret; + } + + spdif->suspend_clkgen = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CLKGEN); + spdif->suspend_ctl = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); + + img_spdif_in_runtime_suspend(dev); return 0; } +static int img_spdif_in_resume(struct device *dev) +{ + struct img_spdif_in *spdif = dev_get_drvdata(dev); + int i, ret; + + ret = img_spdif_in_runtime_resume(dev); + if (ret) + return ret; + + for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) + img_spdif_in_aclkgen_writel(spdif, i); + + img_spdif_in_writel(spdif, spdif->suspend_clkgen, IMG_SPDIF_IN_CLKGEN); + img_spdif_in_writel(spdif, spdif->suspend_ctl, IMG_SPDIF_IN_CTL); + + if (pm_runtime_status_suspended(dev)) + img_spdif_in_runtime_suspend(dev); + + return 0; +} +#endif + static const struct of_device_id img_spdif_in_of_match[] = { { .compatible = "img,spdif-in" }, {} }; MODULE_DEVICE_TABLE(of, img_spdif_in_of_match); +static const struct dev_pm_ops img_spdif_in_pm_ops = { + SET_RUNTIME_PM_OPS(img_spdif_in_runtime_suspend, + img_spdif_in_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(img_spdif_in_suspend, img_spdif_in_resume) +}; + static struct platform_driver img_spdif_in_driver = { .driver = { .name = "img-spdif-in", - .of_match_table = img_spdif_in_of_match + .of_match_table = img_spdif_in_of_match, + .pm = &img_spdif_in_pm_ops }, .probe = img_spdif_in_probe, .remove = img_spdif_in_dev_remove diff --git a/sound/soc/img/img-spdif-out.c b/sound/soc/img/img-spdif-out.c index 383655da2e60..934ed3df2ebf 100644 --- a/sound/soc/img/img-spdif-out.c +++ b/sound/soc/img/img-spdif-out.c @@ -47,25 +47,36 @@ struct img_spdif_out { struct snd_dmaengine_dai_dma_data dma_data; struct device *dev; struct reset_control *rst; + u32 suspend_ctl; + u32 suspend_csl; + u32 suspend_csh; }; -static int img_spdif_out_suspend(struct device *dev) +static int img_spdif_out_runtime_suspend(struct device *dev) { struct img_spdif_out *spdif = dev_get_drvdata(dev); clk_disable_unprepare(spdif->clk_ref); + clk_disable_unprepare(spdif->clk_sys); return 0; } -static int img_spdif_out_resume(struct device *dev) +static int img_spdif_out_runtime_resume(struct device *dev) { struct img_spdif_out *spdif = dev_get_drvdata(dev); int ret; + ret = clk_prepare_enable(spdif->clk_sys); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + ret = clk_prepare_enable(spdif->clk_ref); if (ret) { dev_err(dev, "clk_enable failed: %d\n", ret); + clk_disable_unprepare(spdif->clk_sys); return ret; } @@ -355,21 +366,21 @@ static int img_spdif_out_probe(struct platform_device *pdev) return PTR_ERR(spdif->clk_ref); } - ret = clk_prepare_enable(spdif->clk_sys); - if (ret) - return ret; - - img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK, - IMG_SPDIF_OUT_CTL); - - img_spdif_out_reset(spdif); - pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { - ret = img_spdif_out_resume(&pdev->dev); + ret = img_spdif_out_runtime_resume(&pdev->dev); if (ret) goto err_pm_disable; } + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto err_suspend; + + img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK, + IMG_SPDIF_OUT_CTL); + + img_spdif_out_reset(spdif); + pm_runtime_put(&pdev->dev); spin_lock_init(&spdif->lock); @@ -393,27 +404,62 @@ static int img_spdif_out_probe(struct platform_device *pdev) err_suspend: if (!pm_runtime_status_suspended(&pdev->dev)) - img_spdif_out_suspend(&pdev->dev); + img_spdif_out_runtime_suspend(&pdev->dev); err_pm_disable: pm_runtime_disable(&pdev->dev); - clk_disable_unprepare(spdif->clk_sys); return ret; } static int img_spdif_out_dev_remove(struct platform_device *pdev) { - struct img_spdif_out *spdif = platform_get_drvdata(pdev); - pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) - img_spdif_out_suspend(&pdev->dev); + img_spdif_out_runtime_suspend(&pdev->dev); - clk_disable_unprepare(spdif->clk_sys); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int img_spdif_out_suspend(struct device *dev) +{ + struct img_spdif_out *spdif = dev_get_drvdata(dev); + int ret; + + if (pm_runtime_status_suspended(dev)) { + ret = img_spdif_out_runtime_resume(dev); + if (ret) + return ret; + } + + spdif->suspend_ctl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL); + spdif->suspend_csl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL); + spdif->suspend_csh = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV); + + img_spdif_out_runtime_suspend(dev); return 0; } +static int img_spdif_out_resume(struct device *dev) +{ + struct img_spdif_out *spdif = dev_get_drvdata(dev); + int ret; + + ret = img_spdif_out_runtime_resume(dev); + if (ret) + return ret; + + img_spdif_out_writel(spdif, spdif->suspend_ctl, IMG_SPDIF_OUT_CTL); + img_spdif_out_writel(spdif, spdif->suspend_csl, IMG_SPDIF_OUT_CSL); + img_spdif_out_writel(spdif, spdif->suspend_csh, IMG_SPDIF_OUT_CSH_UV); + + if (pm_runtime_status_suspended(dev)) + img_spdif_out_runtime_suspend(dev); + + return 0; +} +#endif static const struct of_device_id img_spdif_out_of_match[] = { { .compatible = "img,spdif-out" }, {} @@ -421,8 +467,9 @@ static const struct of_device_id img_spdif_out_of_match[] = { MODULE_DEVICE_TABLE(of, img_spdif_out_of_match); static const struct dev_pm_ops img_spdif_out_pm_ops = { - SET_RUNTIME_PM_OPS(img_spdif_out_suspend, - img_spdif_out_resume, NULL) + SET_RUNTIME_PM_OPS(img_spdif_out_runtime_suspend, + img_spdif_out_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(img_spdif_out_suspend, img_spdif_out_resume) }; static struct platform_driver img_spdif_out_driver = { |