From e8c8e9de4ee3e9e1231b4f179c3f0e340bc2b1cf Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 13 Dec 2022 13:28:49 +0200 Subject: ASoC: mchp-pdmc: use runtime pm for clock power saving Implement clock power saving taking advantage of runtime PM infrastructure. This simplifies the code and allow using the same infrastructure for suspend to RAM functionalities. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20221213112851.89212-3-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- sound/soc/atmel/mchp-pdmc.c | 126 +++++++++++++++++++++++++++++--------------- 1 file changed, 84 insertions(+), 42 deletions(-) (limited to 'sound/soc/atmel') diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c index 44aefbd5b62c..f184404e74e5 100644 --- a/sound/soc/atmel/mchp-pdmc.c +++ b/sound/soc/atmel/mchp-pdmc.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -115,7 +116,6 @@ struct mchp_pdmc { int mic_no; int sinc_order; bool audio_filter_en; - u8 gclk_enabled:1; }; static const char *const mchp_pdmc_sinc_filter_order_text[] = { @@ -454,13 +454,6 @@ static int mchp_pdmc_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); - int ret; - - ret = clk_prepare_enable(dd->pclk); - if (ret) { - dev_err(dd->dev, "failed to enable the peripheral clock: %d\n", ret); - return ret; - } regmap_write(dd->regmap, MCHP_PDMC_CR, MCHP_PDMC_CR_SWRST); @@ -470,14 +463,6 @@ static int mchp_pdmc_startup(struct snd_pcm_substream *substream, return 0; } -static void mchp_pdmc_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); - - clk_disable_unprepare(dd->pclk); -} - static int mchp_pdmc_dai_probe(struct snd_soc_dai *dai) { struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); @@ -594,11 +579,6 @@ static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream, cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i); } - if (dd->gclk_enabled) { - clk_disable_unprepare(dd->gclk); - dd->gclk_enabled = 0; - } - for (osr_start = dd->audio_filter_en ? 64 : 8; osr_start <= 256 && best_diff_rate; osr_start *= 2) { long round_rate; @@ -620,8 +600,12 @@ static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + /* CLK is enabled by runtime PM. */ + clk_disable_unprepare(dd->gclk); + /* set the rate */ ret = clk_set_rate(dd->gclk, gclk_rate); + clk_prepare_enable(dd->gclk); if (ret) { dev_err(comp->dev, "unable to set rate %lu to GCLK: %d\n", gclk_rate, ret); @@ -636,9 +620,6 @@ static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream, mr_val |= MCHP_PDMC_MR_CHUNK(dd->addr.maxburst); dev_dbg(comp->dev, "maxburst set to %d\n", dd->addr.maxburst); - clk_prepare_enable(dd->gclk); - dd->gclk_enabled = 1; - snd_soc_component_update_bits(comp, MCHP_PDMC_MR, MCHP_PDMC_MR_OSR_MASK | MCHP_PDMC_MR_SINCORDER_MASK | @@ -650,19 +631,6 @@ static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream, return 0; } -static int mchp_pdmc_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); - - if (dd->gclk_enabled) { - clk_disable_unprepare(dd->gclk); - dd->gclk_enabled = 0; - } - - return 0; -} - static int mchp_pdmc_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -711,9 +679,7 @@ static int mchp_pdmc_trigger(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { .set_fmt = mchp_pdmc_set_fmt, .startup = mchp_pdmc_startup, - .shutdown = mchp_pdmc_shutdown, .hw_params = mchp_pdmc_hw_params, - .hw_free = mchp_pdmc_hw_free, .trigger = mchp_pdmc_trigger, }; @@ -864,6 +830,7 @@ static const struct regmap_config mchp_pdmc_regmap_config = { .readable_reg = mchp_pdmc_readable_reg, .writeable_reg = mchp_pdmc_writeable_reg, .precious_reg = mchp_pdmc_precious_reg, + .cache_type = REGCACHE_FLAT, }; static int mchp_pdmc_dt_init(struct mchp_pdmc *dd) @@ -970,6 +937,49 @@ static struct snd_dmaengine_pcm_config mchp_pdmc_config = { .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, }; +static int mchp_pdmc_runtime_suspend(struct device *dev) +{ + struct mchp_pdmc *dd = dev_get_drvdata(dev); + + regcache_cache_only(dd->regmap, true); + + clk_disable_unprepare(dd->gclk); + clk_disable_unprepare(dd->pclk); + + return 0; +} + +static int mchp_pdmc_runtime_resume(struct device *dev) +{ + struct mchp_pdmc *dd = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(dd->pclk); + if (ret) { + dev_err(dd->dev, + "failed to enable the peripheral clock: %d\n", ret); + return ret; + } + ret = clk_prepare_enable(dd->gclk); + if (ret) { + dev_err(dd->dev, + "failed to enable generic clock: %d\n", ret); + goto disable_pclk; + } + + regcache_cache_only(dd->regmap, false); + regcache_mark_dirty(dd->regmap); + ret = regcache_sync(dd->regmap); + if (ret) { + regcache_cache_only(dd->regmap, true); + clk_disable_unprepare(dd->gclk); +disable_pclk: + clk_disable_unprepare(dd->pclk); + } + + return ret; +} + static int mchp_pdmc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1039,18 +1049,25 @@ static int mchp_pdmc_probe(struct platform_device *pdev) dd->addr.addr = (dma_addr_t)res->start + MCHP_PDMC_RHR; platform_set_drvdata(pdev, dd); + pm_runtime_enable(dd->dev); + if (!pm_runtime_enabled(dd->dev)) { + ret = mchp_pdmc_runtime_resume(dd->dev); + if (ret) + return ret; + } + /* register platform */ ret = devm_snd_dmaengine_pcm_register(dev, &mchp_pdmc_config, 0); if (ret) { dev_err(dev, "could not register platform: %d\n", ret); - return ret; + goto pm_runtime_suspend; } ret = devm_snd_soc_register_component(dev, &mchp_pdmc_dai_component, &mchp_pdmc_dai, 1); if (ret) { dev_err(dev, "could not register CPU DAI: %d\n", ret); - return ret; + goto pm_runtime_suspend; } /* print IP version */ @@ -1059,6 +1076,25 @@ static int mchp_pdmc_probe(struct platform_device *pdev) version & MCHP_PDMC_VER_VERSION); return 0; + +pm_runtime_suspend: + if (!pm_runtime_status_suspended(dd->dev)) + mchp_pdmc_runtime_suspend(dd->dev); + pm_runtime_disable(dd->dev); + + return ret; +} + +static int mchp_pdmc_remove(struct platform_device *pdev) +{ + struct mchp_pdmc *dd = platform_get_drvdata(pdev); + + if (!pm_runtime_status_suspended(dd->dev)) + mchp_pdmc_runtime_suspend(dd->dev); + + pm_runtime_disable(dd->dev); + + return 0; } static const struct of_device_id mchp_pdmc_of_match[] = { @@ -1070,13 +1106,19 @@ static const struct of_device_id mchp_pdmc_of_match[] = { }; MODULE_DEVICE_TABLE(of, mchp_pdmc_of_match); +static const struct dev_pm_ops mchp_pdmc_pm_ops = { + RUNTIME_PM_OPS(mchp_pdmc_runtime_suspend, mchp_pdmc_runtime_resume, + NULL) +}; + static struct platform_driver mchp_pdmc_driver = { .driver = { .name = "mchp-pdmc", .of_match_table = of_match_ptr(mchp_pdmc_of_match), - .pm = &snd_soc_pm_ops, + .pm = pm_ptr(&mchp_pdmc_pm_ops), }, .probe = mchp_pdmc_probe, + .remove = mchp_pdmc_remove, }; module_platform_driver(mchp_pdmc_driver); -- cgit v1.2.3 From 404c61c47d84bf1662e9d698e4f0c42898893c59 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 13 Dec 2022 13:28:50 +0200 Subject: ASoC: mchp-pdmc: add support for suspend to RAM Add support for suspend to RAM by re-aranging the lines in switch..case from mchp_pdmc_trigger() and saving/restoring the enabled interrupts. These are necessary as AT91 devices has a special power saving mode (called backup and self-refresh) where most of the SoC parts are powered off and thus we need to reconfigure the PDMC on resume. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20221213112851.89212-4-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- sound/soc/atmel/mchp-pdmc.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'sound/soc/atmel') diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c index f184404e74e5..cf4084dcbd5e 100644 --- a/sound/soc/atmel/mchp-pdmc.c +++ b/sound/soc/atmel/mchp-pdmc.c @@ -113,6 +113,7 @@ struct mchp_pdmc { struct clk *pclk; struct clk *gclk; u32 pdmcen; + u32 suspend_irq; int mic_no; int sinc_order; bool audio_filter_en; @@ -641,22 +642,27 @@ static int mchp_pdmc_trigger(struct snd_pcm_substream *substream, #endif switch (cmd) { - case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_START: /* Enable overrun and underrun error interrupts */ - regmap_write(dd->regmap, MCHP_PDMC_IER, + regmap_write(dd->regmap, MCHP_PDMC_IER, dd->suspend_irq | MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR); + dd->suspend_irq = 0; + fallthrough; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: snd_soc_component_update_bits(cpu, MCHP_PDMC_MR, MCHP_PDMC_MR_PDMCEN_MASK, dd->pdmcen); break; - case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + regmap_read(dd->regmap, MCHP_PDMC_IMR, &dd->suspend_irq); + fallthrough; + case SNDRV_PCM_TRIGGER_STOP: /* Disable overrun and underrun error interrupts */ - regmap_write(dd->regmap, MCHP_PDMC_IDR, + regmap_write(dd->regmap, MCHP_PDMC_IDR, dd->suspend_irq | MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR); + fallthrough; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_soc_component_update_bits(cpu, MCHP_PDMC_MR, MCHP_PDMC_MR_PDMCEN_MASK, 0); break; @@ -1107,6 +1113,7 @@ static const struct of_device_id mchp_pdmc_of_match[] = { MODULE_DEVICE_TABLE(of, mchp_pdmc_of_match); static const struct dev_pm_ops mchp_pdmc_pm_ops = { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) RUNTIME_PM_OPS(mchp_pdmc_runtime_suspend, mchp_pdmc_runtime_resume, NULL) }; -- cgit v1.2.3 From 24c86c8a3bb1bd12aa8bc32933df7394b83233dc Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 13 Dec 2022 13:28:51 +0200 Subject: ASoC: mchp-spdiftx: use FIELD_PREP() where possible Use FIELD_PREP() in macro definitions. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20221213112851.89212-5-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- sound/soc/atmel/mchp-spdiftx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/soc/atmel') diff --git a/sound/soc/atmel/mchp-spdiftx.c b/sound/soc/atmel/mchp-spdiftx.c index dc96a6fbf514..20d135c718b0 100644 --- a/sound/soc/atmel/mchp-spdiftx.c +++ b/sound/soc/atmel/mchp-spdiftx.c @@ -6,6 +6,7 @@ // // Author: Codrin Ciubotariu +#include #include #include #include @@ -71,11 +72,11 @@ /* Valid Bits per Sample */ #define SPDIFTX_MR_VBPS_MASK GENMASK(13, 8) -#define SPDIFTX_MR_VBPS(bps) (((bps) << 8) & SPDIFTX_MR_VBPS_MASK) +#define SPDIFTX_MR_VBPS(bps) FIELD_PREP(SPDIFTX_MR_VBPS_MASK, bps) /* Chunk Size */ #define SPDIFTX_MR_CHUNK_MASK GENMASK(19, 16) -#define SPDIFTX_MR_CHUNK(size) (((size) << 16) & SPDIFTX_MR_CHUNK_MASK) +#define SPDIFTX_MR_CHUNK(size) FIELD_PREP(SPDIFTX_MR_CHUNK_MASK, size) /* Validity Bits for Channels 1 and 2 */ #define SPDIFTX_MR_VALID1 BIT(24) @@ -88,8 +89,7 @@ /* Bytes per Sample */ #define SPDIFTX_MR_BPS_MASK GENMASK(29, 28) -#define SPDIFTX_MR_BPS(bytes) \ - ((((bytes) - 1) << 28) & SPDIFTX_MR_BPS_MASK) +#define SPDIFTX_MR_BPS(bytes) FIELD_PREP(SPDIFTX_MR_BPS_MASK, (bytes - 1)) /* * ---- Interrupt Enable/Disable/Mask/Status Register (Write/Read-only) ---- -- cgit v1.2.3