[PATCH 0/8] ASoC: Intel: avs: PCM power management
Goal of the series is implementation of suspend/resume operations for a PCM stream along with all the collaterals connected to the subject.
Start with splitting avs_dai_fe_hw_free() as ideally we would like to reuse as much of existing code as possible but snd_pcm_lib_free_pages() is not desired part of the function when speaking of suspend operation.
The actual implementation of suspend/resume() for component drivers follows. For most scenarios, the PM flow is similar to standard streaming one, except for the part where the position register are being saved and the lack of PCM pages freeing. To reduce code duplication, all avs_dai_suspend_XXX() and avs_dai_resume_XXX() functions reuse their non-PM equivalents. Order of operations is affected by the fact that path binding/unbinding happens only in FE part of the stream.
Above essentially unlocks SX+streaming scenarios i.e.: power transitions with an ongoing stream.
As some streams are allowed to run in low power state, support is provided for S0iX state. The handlers check ACPI capabilities and the number of active low-power paths before deciding between SX and S0iX flows.
The last portion of the patchset is addition of power/clock gating overrides. There is no single set of registers that ensures AudioDSP firmware loads 100% of time on every single configuration. By having them exposed, user can have the loading procedure behavior adjusted for their configuration without having to recompile the kernel.
Amadeusz Sławiński (1): ASoC: Intel: avs: Handle SUSPEND and RESUME triggers
Cezary Rojewski (6): ASoC: Intel: avs: Split pcm pages freeing operation from hw_free() ASoC: Intel: avs: Introduce PCM power management routines ASoC: Intel: avs: Restart instead of resuming HDA capture streams ASoC: Intel: avs: Count low power streams ASoC: Intel: avs: Power and clock gating policy overriding ASoC: Intel: avs: Enact power gating policy
Piotr Maziarz (1): ASoC: Intel: avs: Standby power-state support
include/sound/hdaudio_ext.h | 5 + sound/soc/intel/avs/avs.h | 1 + sound/soc/intel/avs/core.c | 97 +++++++-- sound/soc/intel/avs/loader.c | 10 + sound/soc/intel/avs/pcm.c | 387 +++++++++++++++++++++++++++++++---- 5 files changed, 440 insertions(+), 60 deletions(-)
Prepare for introduction of PCM power management support. As freeing pages during the suspend operation is not desired, separate snd_pcm_lib_free_pages() from existing avs_dai_fe_hw_free() so that majority of the code found within it can be reused for standard and PM flows both.
Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- sound/soc/intel/avs/pcm.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 8037b15cbdcf..d584e955216c 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -499,7 +499,7 @@ static int avs_dai_fe_hw_params(struct snd_pcm_substream *substream, return ret; }
-static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +static int __avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct avs_dma_data *data; struct hdac_ext_stream *host_stream; @@ -523,9 +523,15 @@ static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_so snd_hdac_stream_cleanup(hdac_stream(host_stream)); hdac_stream(host_stream)->prepared = false;
- ret = snd_pcm_lib_free_pages(substream); - if (ret < 0) - dev_dbg(dai->dev, "Failed to free pages!\n"); + return ret; +} + +static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + int ret; + + ret = __avs_dai_fe_hw_free(substream, dai); + snd_pcm_lib_free_pages(substream);
return ret; }
Implement suspend/resume() operations for component drivers. For most scenarios, the PM flow is similar to standard streaming one, except for the part where the position register are being saved and the lack of PCM pages freeing. To reduce code duplication, all avs_dai_suspend_XXX() and avs_dai_resume_XXX() functions reuse their non-PM equivalents.
Given that path binding/unbinding happens only in FE part of the stream, the order of suspend() goes:
1. hw_free() all FE DAIs, paths are unbound here 2. hw_free() all BE DAIs
Consequently, for resume() its:
1. hw_params() all BE DAIs 2. hw_params() all FE DAIs, paths are bound here 3. prepare() all BE DAIs 4. prepare() all FE DAIs
As component->suspend/resume() do not provide substream pointer, store it ourselves so that the PM flow has all the necessary information to proceed.
Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- include/sound/hdaudio_ext.h | 5 + sound/soc/intel/avs/pcm.c | 227 +++++++++++++++++++++++++++++++++++- 2 files changed, 228 insertions(+), 4 deletions(-)
diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 83aed26ab143..6598e238b9c5 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -66,6 +66,11 @@ struct hdac_ext_stream {
u32 dpib; u32 lpib; + u32 pphcllpl; + u32 pphcllpu; + u32 pphcldpl; + u32 pphcldpu; + bool decoupled:1; bool link_locked:1; bool link_prepared; diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index d584e955216c..55f213d5bab9 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -28,6 +28,8 @@ struct avs_dma_data { * host stream assigned */ struct hdac_ext_stream *host_stream; + + struct snd_pcm_substream *substream; };
static struct avs_tplg_path_template * @@ -55,7 +57,8 @@ avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction) return dw->priv; }
-static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool is_fe) +static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool is_fe, + const struct snd_soc_dai_ops *ops) { struct avs_tplg_path_template *template; struct avs_dma_data *data; @@ -71,6 +74,7 @@ static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_d if (!data) return -ENOMEM;
+ data->substream = substream; data->template = template; snd_soc_dai_set_dma_data(dai, substream, data);
@@ -151,9 +155,11 @@ static int avs_dai_prepare(struct avs_dev *adev, struct snd_pcm_substream *subst return ret; }
+static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops; + static int avs_dai_nonhda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - return avs_dai_startup(substream, dai, false); + return avs_dai_startup(substream, dai, false, &avs_dai_nonhda_be_ops); }
static void avs_dai_nonhda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) @@ -245,9 +251,11 @@ static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops = { .trigger = avs_dai_nonhda_be_trigger, };
+static const struct snd_soc_dai_ops avs_dai_hda_be_ops; + static int avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - return avs_dai_startup(substream, dai, false); + return avs_dai_startup(substream, dai, false, &avs_dai_hda_be_ops); }
static void avs_dai_hda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) @@ -407,6 +415,8 @@ static const struct snd_pcm_hw_constraint_list hw_rates = { .mask = 0, };
+const struct snd_soc_dai_ops avs_dai_fe_ops; + static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -416,7 +426,7 @@ static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_so struct hdac_ext_stream *host_stream; int ret;
- ret = avs_dai_startup(substream, dai, true); + ret = avs_dai_startup(substream, dai, true, &avs_dai_fe_ops); if (ret) return ret;
@@ -758,6 +768,211 @@ static void avs_component_remove(struct snd_soc_component *component) } }
+static int avs_dai_resume_hw_params(struct snd_soc_dai *dai, struct avs_dma_data *data) +{ + struct snd_pcm_substream *substream; + struct snd_soc_pcm_runtime *rtd; + int ret; + + substream = data->substream; + rtd = snd_pcm_substream_chip(substream); + + ret = dai->driver->ops->hw_params(substream, &rtd->dpcm[substream->stream].hw_params, dai); + if (ret) + dev_err(dai->dev, "hw_params on resume failed: %d\n", ret); + + return ret; +} + +static int avs_dai_resume_fe_prepare(struct snd_soc_dai *dai, struct avs_dma_data *data) +{ + struct hdac_ext_stream *host_stream; + struct hdac_bus *bus; + int ret; + + host_stream = data->host_stream; + bus = hdac_stream(host_stream)->bus; + + /* Set DRSM before programming stream and position registers. */ + snd_hdac_ext_stream_drsm_enable(bus, true, hdac_stream(host_stream)->index); + + ret = dai->driver->ops->prepare(data->substream, dai); + if (ret) { + dev_err(dai->dev, "prepare FE on resume failed: %d\n", ret); + return ret; + } + + writel(host_stream->pphcllpl, host_stream->pphc_addr + AZX_REG_PPHCLLPL); + writel(host_stream->pphcllpu, host_stream->pphc_addr + AZX_REG_PPHCLLPU); + writel(host_stream->pphcldpl, host_stream->pphc_addr + AZX_REG_PPHCLDPL); + writel(host_stream->pphcldpu, host_stream->pphc_addr + AZX_REG_PPHCLDPU); + + /* As per HW spec recommendation, program LPIB and DPIB to the same value. */ + snd_hdac_ext_stream_set_lpib(host_stream, host_stream->lpib); + snd_hdac_ext_stream_set_dpibr(bus, host_stream, host_stream->lpib); + + return 0; +} + +static int avs_dai_resume_be_prepare(struct snd_soc_dai *dai, struct avs_dma_data *data) +{ + int ret; + + ret = dai->driver->ops->prepare(data->substream, dai); + if (ret) { + dev_err(dai->dev, "prepare BE on resume failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int avs_dai_suspend_fe_hw_free(struct snd_soc_dai *dai, struct avs_dma_data *data) +{ + struct hdac_ext_stream *host_stream; + struct snd_soc_pcm_runtime *rtd; + int ret; + + rtd = snd_pcm_substream_chip(data->substream); + host_stream = data->host_stream; + + /* Store position addresses so we can resume from them later on. */ + host_stream->lpib = snd_hdac_stream_get_pos_lpib(hdac_stream(host_stream)); + host_stream->pphcllpl = readl(host_stream->pphc_addr + AZX_REG_PPHCLLPL); + host_stream->pphcllpu = readl(host_stream->pphc_addr + AZX_REG_PPHCLLPU); + host_stream->pphcldpl = readl(host_stream->pphc_addr + AZX_REG_PPHCLDPL); + host_stream->pphcldpu = readl(host_stream->pphc_addr + AZX_REG_PPHCLDPU); + + if (!rtd->dai_link->ignore_suspend) { + ret = __avs_dai_fe_hw_free(data->substream, dai); + if (ret < 0) { + dev_err(dai->dev, "hw_free FE on suspend failed: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int avs_dai_suspend_be_hw_free(struct snd_soc_dai *dai, struct avs_dma_data *data) +{ + struct snd_soc_pcm_runtime *rtd; + int ret; + + rtd = snd_pcm_substream_chip(data->substream); + + if (!rtd->dai_link->ignore_suspend) { + ret = dai->driver->ops->hw_free(data->substream, dai); + if (ret < 0) { + dev_err(dai->dev, "hw_free BE on suspend failed: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int avs_component_pm_op(struct snd_soc_component *component, bool be, + int (*op)(struct snd_soc_dai *, struct avs_dma_data *)) +{ + struct snd_soc_pcm_runtime *rtd; + struct avs_dma_data *data; + struct snd_soc_dai *dai; + int ret; + + for_each_component_dais(component, dai) { + data = dai->playback_dma_data; + if (data) { + rtd = snd_pcm_substream_chip(data->substream); + if (rtd->dai_link->no_pcm == be) { + ret = op(dai, data); + if (ret < 0) + return ret; + } + } + + data = dai->capture_dma_data; + if (data) { + rtd = snd_pcm_substream_chip(data->substream); + if (rtd->dai_link->no_pcm == be) { + ret = op(dai, data); + if (ret < 0) + return ret; + } + } + } + + return 0; +} + +static int avs_component_resume_hw_params(struct snd_soc_component *component, bool be) +{ + return avs_component_pm_op(component, be, &avs_dai_resume_hw_params); +} + +static int avs_component_resume_prepare(struct snd_soc_component *component, bool be) +{ + int (*prepare_cb)(struct snd_soc_dai *dai, struct avs_dma_data *data); + + if (be) + prepare_cb = &avs_dai_resume_be_prepare; + else + prepare_cb = &avs_dai_resume_fe_prepare; + + return avs_component_pm_op(component, be, prepare_cb); +} + +static int avs_component_suspend_hw_free(struct snd_soc_component *component, bool be) +{ + int (*hw_free_cb)(struct snd_soc_dai *dai, struct avs_dma_data *data); + + if (be) + hw_free_cb = &avs_dai_suspend_be_hw_free; + else + hw_free_cb = &avs_dai_suspend_fe_hw_free; + + return avs_component_pm_op(component, be, hw_free_cb); +} + +static int avs_component_suspend(struct snd_soc_component *component) +{ + int ret; + + /* + * When freeing paths, FEs need to be first as they perform + * path unbinding. + */ + ret = avs_component_suspend_hw_free(component, false); + if (ret) + return ret; + + return avs_component_suspend_hw_free(component, true); +} + +static int avs_component_resume(struct snd_soc_component *component) +{ + int ret; + + /* + * When creating paths, FEs need to be last as they perform + * path binding. + */ + ret = avs_component_resume_hw_params(component, true); + if (ret) + return ret; + + ret = avs_component_resume_hw_params(component, false); + if (ret) + return ret; + + /* It is expected that the LINK stream is prepared first. */ + ret = avs_component_resume_prepare(component, true); + if (ret) + return ret; + + return avs_component_resume_prepare(component, false); +} + static int avs_component_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -846,6 +1061,8 @@ static const struct snd_soc_component_driver avs_component_driver = { .name = "avs-pcm", .probe = avs_component_probe, .remove = avs_component_remove, + .suspend = avs_component_suspend, + .resume = avs_component_resume, .open = avs_component_open, .pointer = avs_component_pointer, .mmap = avs_component_mmap, @@ -1161,6 +1378,8 @@ static const struct snd_soc_component_driver avs_hda_component_driver = { .name = "avs-hda-pcm", .probe = avs_component_hda_probe, .remove = avs_component_hda_remove, + .suspend = avs_component_suspend, + .resume = avs_component_resume, .open = avs_component_hda_open, .close = avs_component_hda_close, .pointer = avs_component_pointer,
On 10/19/22 12:53, Cezary Rojewski wrote:
Implement suspend/resume() operations for component drivers. For most scenarios, the PM flow is similar to standard streaming one, except for the part where the position register are being saved and the lack of PCM pages freeing. To reduce code duplication, all avs_dai_suspend_XXX() and avs_dai_resume_XXX() functions reuse their non-PM equivalents.
Given that path binding/unbinding happens only in FE part of the stream, the order of suspend() goes:
- hw_free() all FE DAIs, paths are unbound here
- hw_free() all BE DAIs
Consequently, for resume() its:
- hw_params() all BE DAIs
- hw_params() all FE DAIs, paths are bound here
- prepare() all BE DAIs
- prepare() all FE DAIs
As component->suspend/resume() do not provide substream pointer, store it ourselves so that the PM flow has all the necessary information to proceed.
Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com
include/sound/hdaudio_ext.h | 5 + sound/soc/intel/avs/pcm.c | 227 +++++++++++++++++++++++++++++++++++- 2 files changed, 228 insertions(+), 4 deletions(-)
diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 83aed26ab143..6598e238b9c5 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -66,6 +66,11 @@ struct hdac_ext_stream {
u32 dpib; u32 lpib;
- u32 pphcllpl;
- u32 pphcllpu;
- u32 pphcldpl;
- u32 pphcldpu;
This is clearly going to conflict with my own rename/move changes in "ALSA/ASoC: hda: move SPIB/DRMS functionality from ext layer"
The SPIB and DRMS handling are not DSP-specific and should be handled in the 'generic' sound/hda layer. In theory the HDaudio legacy driver should have used those capabilities.
It should be a simple rebase though for this patch.
On 2022-10-19 8:02 PM, Pierre-Louis Bossart wrote:
...
This is clearly going to conflict with my own rename/move changes in "ALSA/ASoC: hda: move SPIB/DRMS functionality from ext layer"
The SPIB and DRMS handling are not DSP-specific and should be handled in the 'generic' sound/hda layer. In theory the HDaudio legacy driver should have used those capabilities.
It should be a simple rebase though for this patch.
No problem with waiting for the series you mention to be merged first. Will rebase once it's done.
Also, that's a good point in regard to DRSM. This could indeed be added into common sound/hda lib. Something like below perhaps?
int snd_hdac_stream_resume(struct hdac_stream *azx_dev) { struct hdac_bus *bus = azx_dev->bus; u32 mask, reg; int ret;
mask = 1 << azx_dev->index;
ret = readb_poll_timeout(bus->drsmcap + AZX_REG_DRSM_CTL, reg, !(reg & mask), 250, 2000); if (ret) dev_dbg(bus->dev, "polling RSM 0x%08x failed: %d\n", mask, ret); return ret; } EXPORT_SYMBOL_GPL(snd_hdac_stream_resume);
On 10/20/22 02:56, Cezary Rojewski wrote:
On 2022-10-19 8:02 PM, Pierre-Louis Bossart wrote:
...
This is clearly going to conflict with my own rename/move changes in "ALSA/ASoC: hda: move SPIB/DRMS functionality from ext layer"
The SPIB and DRMS handling are not DSP-specific and should be handled in the 'generic' sound/hda layer. In theory the HDaudio legacy driver should have used those capabilities.
It should be a simple rebase though for this patch.
No problem with waiting for the series you mention to be merged first. Will rebase once it's done.
Also, that's a good point in regard to DRSM. This could indeed be added into common sound/hda lib. Something like below perhaps?
Between SPIB and DRSM, I would pick SPIB as the most interesting for the legacy HDaudio driver. This prevents the DMA hardware from playing stale data and would allow for xruns to be detected in cleaner ways. The programming sequences are relatively straightforward when the .ack is used.
I must admit my ignorance of how DRSM works exactly. We haven't used it in the SOF driver where INFO_RESUME is disabled, and with a DSP I am not too sure how to restart precisely from the same location with all the intermediate buffering and processing that may happen.
int snd_hdac_stream_resume(struct hdac_stream *azx_dev) { struct hdac_bus *bus = azx_dev->bus; u32 mask, reg; int ret;
mask = 1 << azx_dev->index;
ret = readb_poll_timeout(bus->drsmcap + AZX_REG_DRSM_CTL, reg, !(reg & mask), 250, 2000); if (ret) dev_dbg(bus->dev, "polling RSM 0x%08x failed: %d\n", mask, ret); return ret; } EXPORT_SYMBOL_GPL(snd_hdac_stream_resume);
From: Amadeusz Sławiński amadeuszx.slawinski@linux.intel.com
With power management operations added, service SUSPEND and RESUME trigger commands for running streams.
Signed-off-by: Amadeusz Sławiński amadeuszx.slawinski@linux.intel.com Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- sound/soc/intel/avs/pcm.c | 81 +++++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 15 deletions(-)
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 55f213d5bab9..5c9923cba126 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -208,30 +208,43 @@ static int avs_dai_nonhda_be_prepare(struct snd_pcm_substream *substream, struct static int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct avs_dma_data *data; int ret = 0;
data = snd_soc_dai_get_dma_data(dai, substream);
switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + if (rtd->dai_link->ignore_suspend) + break; + fallthrough; case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = avs_path_pause(data->path); + if (ret < 0) { + dev_err(dai->dev, "pause BE path failed: %d\n", ret); + break; + } + ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO); if (ret < 0) dev_err(dai->dev, "run BE path failed: %d\n", ret); break;
+ case SNDRV_PCM_TRIGGER_SUSPEND: + if (rtd->dai_link->ignore_suspend) + break; + fallthrough; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: ret = avs_path_pause(data->path); if (ret < 0) dev_err(dai->dev, "pause BE path failed: %d\n", ret);
- if (cmd == SNDRV_PCM_TRIGGER_STOP) { - ret = avs_path_reset(data->path); - if (ret < 0) - dev_err(dai->dev, "reset BE path failed: %d\n", ret); - } + ret = avs_path_reset(data->path); + if (ret < 0) + dev_err(dai->dev, "reset BE path failed: %d\n", ret); break;
default: @@ -351,6 +364,7 @@ static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct sn static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct hdac_ext_stream *link_stream; struct avs_dma_data *data; int ret = 0; @@ -361,15 +375,29 @@ static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd, link_stream = substream->runtime->private_data;
switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + if (rtd->dai_link->ignore_suspend) + break; + fallthrough; case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: snd_hdac_ext_link_stream_start(link_stream);
+ ret = avs_path_pause(data->path); + if (ret < 0) { + dev_err(dai->dev, "pause BE path failed: %d\n", ret); + break; + } + ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO); if (ret < 0) dev_err(dai->dev, "run BE path failed: %d\n", ret); break;
+ case SNDRV_PCM_TRIGGER_SUSPEND: + if (rtd->dai_link->ignore_suspend) + break; + fallthrough; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: ret = avs_path_pause(data->path); @@ -378,11 +406,9 @@ static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd,
snd_hdac_ext_link_stream_clear(link_stream);
- if (cmd == SNDRV_PCM_TRIGGER_STOP) { - ret = avs_path_reset(data->path); - if (ret < 0) - dev_err(dai->dev, "reset BE path failed: %d\n", ret); - } + ret = avs_path_reset(data->path); + if (ret < 0) + dev_err(dai->dev, "reset BE path failed: %d\n", ret); break;
default: @@ -587,6 +613,7 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so
static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct avs_dma_data *data; struct hdac_ext_stream *host_stream; struct hdac_bus *bus; @@ -598,17 +625,42 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru bus = hdac_stream(host_stream)->bus;
switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + if (rtd->dai_link->ignore_suspend) + break; + fallthrough; case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: spin_lock_irqsave(&bus->reg_lock, flags); snd_hdac_stream_start(hdac_stream(host_stream), true); spin_unlock_irqrestore(&bus->reg_lock, flags);
+ if (cmd == SNDRV_PCM_TRIGGER_RESUME) { + u32 mask = 1 << hdac_stream(host_stream)->index; + u32 reg; + + ret = readb_poll_timeout(bus->drsmcap + AZX_REG_DRSM_CTL, reg, + (reg & mask) == 0, 250, 2000); + if (ret) + dev_dbg(dai->dev, "polling RSM 0x%08x failed: %d\n", mask, ret); + } + + ret = avs_path_pause(data->path); + if (ret < 0) { + dev_err(dai->dev, "pause FE path failed: %d\n", ret); + break; + } + ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO); if (ret < 0) dev_err(dai->dev, "run FE path failed: %d\n", ret); + break;
+ case SNDRV_PCM_TRIGGER_SUSPEND: + if (rtd->dai_link->ignore_suspend) + break; + fallthrough; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: ret = avs_path_pause(data->path); @@ -619,11 +671,9 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru snd_hdac_stream_stop(hdac_stream(host_stream)); spin_unlock_irqrestore(&bus->reg_lock, flags);
- if (cmd == SNDRV_PCM_TRIGGER_STOP) { - ret = avs_path_reset(data->path); - if (ret < 0) - dev_err(dai->dev, "reset FE path failed: %d\n", ret); - } + ret = avs_path_reset(data->path); + if (ret < 0) + dev_err(dai->dev, "reset FE path failed: %d\n", ret); break;
default: @@ -987,6 +1037,7 @@ static int avs_component_open(struct snd_soc_component *component, SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP;
hwparams.formats = SNDRV_PCM_FMTBIT_S16_LE |
Resuming of capture streams for HD-Audio is unsupported so remove the relevant flag from the hardware params when assigning them during avs_component_hda_open().
Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- sound/soc/intel/avs/pcm.c | 50 ++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 22 deletions(-)
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 5c9923cba126..cce3756e2a4e 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -1023,34 +1023,34 @@ static int avs_component_resume(struct snd_soc_component *component) return avs_component_resume_prepare(component, false); }
+static const struct snd_pcm_hardware avs_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .buffer_bytes_max = AZX_MAX_BUF_SIZE, + .period_bytes_min = 128, + .period_bytes_max = AZX_MAX_BUF_SIZE / 2, + .periods_min = 2, + .periods_max = AZX_MAX_FRAG, + .fifo_size = 0, +}; + static int avs_component_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct snd_pcm_hardware hwparams;
/* only FE DAI links are handled here */ if (rtd->dai_link->no_pcm) return 0;
- hwparams.info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_RESUME | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP; - - hwparams.formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE; - hwparams.period_bytes_min = 128; - hwparams.period_bytes_max = AZX_MAX_BUF_SIZE / 2; - hwparams.periods_min = 2; - hwparams.periods_max = AZX_MAX_FRAG; - hwparams.buffer_bytes_max = AZX_MAX_BUF_SIZE; - hwparams.fifo_size = 0; - - return snd_soc_set_runtime_hwparams(substream, &hwparams); + return snd_soc_set_runtime_hwparams(substream, &avs_pcm_hardware); }
static unsigned int avs_hda_stream_dpib_read(struct hdac_ext_stream *stream) @@ -1394,9 +1394,15 @@ static int avs_component_hda_open(struct snd_soc_component *component, struct hdac_ext_stream *link_stream; struct hda_codec *codec;
- /* only BE DAI links are handled here */ - if (!rtd->dai_link->no_pcm) - return avs_component_open(component, substream); + if (!rtd->dai_link->no_pcm) { + struct snd_pcm_hardware hwparams = avs_pcm_hardware; + + /* RESUME unsupported for de-coupled HD-Audio capture. */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + hwparams.info &= ~SNDRV_PCM_INFO_RESUME; + + return snd_soc_set_runtime_hwparams(substream, &hwparams); + }
codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev); link_stream = snd_hdac_ext_stream_assign(&codec->bus->core, substream,
Streaming in S0iX differs from SX scenarios. Store the number of so-called low-power streams to be able to differentiate between the two.
Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- sound/soc/intel/avs/avs.h | 1 + sound/soc/intel/avs/pcm.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+)
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 91f78eb11bc1..fb73d207697f 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -127,6 +127,7 @@ struct avs_dev { struct list_head fw_list; int *core_refs; /* reference count per core */ char **lib_names; + int num_lp_paths;
struct completion fw_ready; struct work_struct probe_work; diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index cce3756e2a4e..a869402e0f4c 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -60,6 +60,8 @@ avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction) static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool is_fe, const struct snd_soc_dai_ops *ops) { + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct avs_dev *adev = to_avs_dev(dai->dev); struct avs_tplg_path_template *template; struct avs_dma_data *data;
@@ -78,6 +80,9 @@ static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_d data->template = template; snd_soc_dai_set_dma_data(dai, substream, data);
+ if (rtd->dai_link->ignore_suspend) + adev->num_lp_paths++; + return 0; }
@@ -164,8 +169,13 @@ static int avs_dai_nonhda_be_startup(struct snd_pcm_substream *substream, struct
static void avs_dai_nonhda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct avs_dev *adev = to_avs_dev(dai->dev); struct avs_dma_data *data;
+ if (rtd->dai_link->ignore_suspend) + adev->num_lp_paths--; + data = snd_soc_dai_get_dma_data(dai, substream);
snd_soc_dai_set_dma_data(dai, substream, NULL); @@ -479,8 +489,13 @@ static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_so
static void avs_dai_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct avs_dev *adev = to_avs_dev(dai->dev); struct avs_dma_data *data;
+ if (rtd->dai_link->ignore_suspend) + adev->num_lp_paths--; + data = snd_soc_dai_get_dma_data(dai, substream);
snd_soc_dai_set_dma_data(dai, substream, NULL);
From: Piotr Maziarz piotrx.maziarz@linux.intel.com
Introduce avs_suspend_standby() and avs_resume_standby() to support S0iX streaming. The AudioDSP is not shutdown during such scenario and the PCI device is armed for possible wake operation through an audio event.
Signed-off-by: Piotr Maziarz piotrx.maziarz@linux.intel.com Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- sound/soc/intel/avs/core.c | 75 ++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 7 deletions(-)
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index d067ce951afc..841bc9fa0d3b 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -534,12 +534,30 @@ static void avs_pci_remove(struct pci_dev *pci) pm_runtime_get_noresume(&pci->dev); }
-static int __maybe_unused avs_suspend_common(struct avs_dev *adev) +static int avs_suspend_standby(struct avs_dev *adev) +{ + struct hdac_bus *bus = &adev->base.core; + struct pci_dev *pci = adev->base.pci; + + if (bus->cmd_dma_state) + snd_hdac_bus_stop_cmd_io(bus); + + snd_hdac_ext_bus_link_power_down_all(bus); + + enable_irq_wake(pci->irq); + pci_save_state(pci); + + return 0; +} + +static int __maybe_unused avs_suspend_common(struct avs_dev *adev, bool low_power) { struct hdac_bus *bus = &adev->base.core; int ret;
flush_work(&adev->probe_work); + if ((acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) && low_power && adev->num_lp_paths) + return avs_suspend_standby(adev);
snd_hdac_ext_bus_link_power_down_all(bus);
@@ -577,11 +595,30 @@ static int __maybe_unused avs_suspend_common(struct avs_dev *adev) return 0; }
-static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool purge) +static int avs_resume_standby(struct avs_dev *adev) +{ + struct hdac_bus *bus = &adev->base.core; + struct pci_dev *pci = adev->base.pci; + + pci_restore_state(pci); + disable_irq_wake(pci->irq); + + snd_hdac_ext_bus_link_power_up_all(bus); + + if (bus->cmd_dma_state) + snd_hdac_bus_init_cmd_io(bus); + + return 0; +} + +static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool low_power, bool purge) { struct hdac_bus *bus = &adev->base.core; int ret;
+ if ((acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) && low_power && adev->num_lp_paths) + return avs_resume_standby(adev); + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true); avs_hdac_bus_init_chip(bus, true);
@@ -599,26 +636,50 @@ static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool purge)
static int __maybe_unused avs_suspend(struct device *dev) { - return avs_suspend_common(to_avs_dev(dev)); + return avs_suspend_common(to_avs_dev(dev), true); }
static int __maybe_unused avs_resume(struct device *dev) { - return avs_resume_common(to_avs_dev(dev), true); + return avs_resume_common(to_avs_dev(dev), true, true); }
static int __maybe_unused avs_runtime_suspend(struct device *dev) { - return avs_suspend_common(to_avs_dev(dev)); + return avs_suspend_common(to_avs_dev(dev), true); }
static int __maybe_unused avs_runtime_resume(struct device *dev) { - return avs_resume_common(to_avs_dev(dev), true); + return avs_resume_common(to_avs_dev(dev), true, false); +} + +static int __maybe_unused avs_freeze(struct device *dev) +{ + return avs_suspend_common(to_avs_dev(dev), false); +} +static int __maybe_unused avs_thaw(struct device *dev) +{ + return avs_resume_common(to_avs_dev(dev), false, true); +} + +static int __maybe_unused avs_poweroff(struct device *dev) +{ + return avs_suspend_common(to_avs_dev(dev), false); +} + +static int __maybe_unused avs_restore(struct device *dev) +{ + return avs_resume_common(to_avs_dev(dev), false, true); }
static const struct dev_pm_ops avs_dev_pm = { - SET_SYSTEM_SLEEP_PM_OPS(avs_suspend, avs_resume) + .suspend = avs_suspend, + .resume = avs_resume, + .freeze = avs_freeze, + .thaw = avs_thaw, + .poweroff = avs_poweroff, + .restore = avs_restore, SET_RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL) };
Hi Cezary,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on broonie-sound/for-next] [also build test ERROR on tiwai-sound/for-next linus/master v6.1-rc1 next-20221020] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Cezary-Rojewski/ASoC-Intel-av... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next patch link: https://lore.kernel.org/r/20221019175317.1540919-7-cezary.rojewski%40intel.c... patch subject: [PATCH 6/8] ASoC: Intel: avs: Standby power-state support config: openrisc-randconfig-r024-20221020 compiler: or1k-linux-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/52c1923c9d4ae230b55c6c0262e6d6... git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Cezary-Rojewski/ASoC-Intel-avs-PCM-power-management/20221020-103921 git checkout 52c1923c9d4ae230b55c6c0262e6d6bbe1fc2584 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=openrisc SHELL=/bin/bash drivers/gpu/drm/ttm/ drivers/pci/controller/ sound/soc/intel/avs/
If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot lkp@intel.com
All errors (new ones prefixed by >>):
sound/soc/intel/avs/core.c: In function 'avs_suspend_common':
sound/soc/intel/avs/core.c:559:14: error: 'acpi_gbl_FADT' undeclared (first use in this function); did you mean 'acpi_table_fadt'?
559 | if ((acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) && low_power && adev->num_lp_paths) | ^~~~~~~~~~~~~ | acpi_table_fadt sound/soc/intel/avs/core.c:559:14: note: each undeclared identifier is reported only once for each function it appears in sound/soc/intel/avs/core.c: In function 'avs_resume_common': sound/soc/intel/avs/core.c:619:14: error: 'acpi_gbl_FADT' undeclared (first use in this function); did you mean 'acpi_table_fadt'? 619 | if ((acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) && low_power && adev->num_lp_paths) | ^~~~~~~~~~~~~ | acpi_table_fadt
vim +559 sound/soc/intel/avs/core.c
552 553 static int __maybe_unused avs_suspend_common(struct avs_dev *adev, bool low_power) 554 { 555 struct hdac_bus *bus = &adev->base.core; 556 int ret; 557 558 flush_work(&adev->probe_work);
559 if ((acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) && low_power && adev->num_lp_paths)
560 return avs_suspend_standby(adev); 561 562 snd_hdac_ext_bus_link_power_down_all(bus); 563 564 ret = avs_ipc_set_dx(adev, AVS_MAIN_CORE_MASK, false); 565 /* 566 * pm_runtime is blocked on DSP failure but system-wide suspend is not. 567 * Do not block entire system from suspending if that's the case. 568 */ 569 if (ret && ret != -EPERM) { 570 dev_err(adev->dev, "set dx failed: %d\n", ret); 571 return AVS_IPC_RET(ret); 572 } 573 574 avs_ipc_block(adev->ipc); 575 avs_dsp_op(adev, int_control, false); 576 snd_hdac_ext_bus_ppcap_int_enable(bus, false); 577 578 ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); 579 if (ret < 0) { 580 dev_err(adev->dev, "core_mask %ld disable failed: %d\n", AVS_MAIN_CORE_MASK, ret); 581 return ret; 582 } 583 584 snd_hdac_ext_bus_ppcap_enable(bus, false); 585 /* disable LP SRAM retention */ 586 avs_hda_power_gating_enable(adev, false); 587 snd_hdac_bus_stop_chip(bus); 588 /* disable CG when putting controller to reset */ 589 avs_hdac_clock_gating_enable(bus, false); 590 snd_hdac_bus_enter_link_reset(bus); 591 avs_hdac_clock_gating_enable(bus, true); 592 593 snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); 594 595 return 0; 596 } 597
Provide pgctl/cgctl_mask module parameters for overriding power and clock gating policies respectively. These help deal with rare firmware loading failures on some configurations. There're no golden masks that cover all known problems so leave the defaults as is.
While at it, update avs_hda_l1sen_enable()'s definition so it aligns with its power/clock friends.
Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- sound/soc/intel/avs/core.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index 841bc9fa0d3b..da7aa7537435 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -27,6 +27,14 @@ #include "avs.h" #include "cldma.h"
+static u32 pgctl_mask = AZX_PGCTL_LSRMD_MASK; +module_param(pgctl_mask, uint, 0444); +MODULE_PARM_DESC(pgctl_mask, "PCI PGCTL policy override"); + +static u32 cgctl_mask = AZX_CGCTL_MISCBDCGE_MASK; +module_param(cgctl_mask, uint, 0444); +MODULE_PARM_DESC(cgctl_mask, "PCI CGCTL policy override"); + static void avs_hda_update_config_dword(struct hdac_bus *bus, u32 reg, u32 mask, u32 value) { @@ -41,19 +49,16 @@ avs_hda_update_config_dword(struct hdac_bus *bus, u32 reg, u32 mask, u32 value)
void avs_hda_power_gating_enable(struct avs_dev *adev, bool enable) { - u32 value; + u32 value = enable ? 0 : pgctl_mask;
- value = enable ? 0 : AZX_PGCTL_LSRMD_MASK; - avs_hda_update_config_dword(&adev->base.core, AZX_PCIREG_PGCTL, - AZX_PGCTL_LSRMD_MASK, value); + avs_hda_update_config_dword(&adev->base.core, AZX_PCIREG_PGCTL, pgctl_mask, value); }
static void avs_hdac_clock_gating_enable(struct hdac_bus *bus, bool enable) { - u32 value; + u32 value = enable ? cgctl_mask : 0;
- value = enable ? AZX_CGCTL_MISCBDCGE_MASK : 0; - avs_hda_update_config_dword(bus, AZX_PCIREG_CGCTL, AZX_CGCTL_MISCBDCGE_MASK, value); + avs_hda_update_config_dword(bus, AZX_PCIREG_CGCTL, cgctl_mask, value); }
void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable) @@ -63,9 +68,8 @@ void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable)
void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable) { - u32 value; + u32 value = enable ? AZX_VS_EM2_L1SEN : 0;
- value = enable ? AZX_VS_EM2_L1SEN : 0; snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, value); }
Update all firmware loading functions to also account for the power gating policy. As module loading routine is missing the chicken bits manipulation entirely, add the entire set there.
Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- sound/soc/intel/avs/loader.c | 10 ++++++++++ sound/soc/intel/avs/pcm.c | 2 ++ 2 files changed, 12 insertions(+)
diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c index 2d80a271eb50..35811fe4a160 100644 --- a/sound/soc/intel/avs/loader.c +++ b/sound/soc/intel/avs/loader.c @@ -224,11 +224,19 @@ static int avs_cldma_load_module(struct avs_dev *adev, struct avs_module_entry * if (ret < 0) return ret;
+ avs_hda_power_gating_enable(adev, false); + avs_hda_clock_gating_enable(adev, false); + avs_hda_l1sen_enable(adev, false); + hda_cldma_set_data(cl, (void *)mod->data, mod->size); hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS)); ret = avs_ipc_load_modules(adev, &mentry->module_id, 1); hda_cldma_stop(cl);
+ avs_hda_l1sen_enable(adev, true); + avs_hda_clock_gating_enable(adev, true); + avs_hda_power_gating_enable(adev, true); + if (ret) { dev_err(adev->dev, "load module %d failed: %d\n", mentry->module_id, ret); avs_release_last_firmware(adev); @@ -605,6 +613,7 @@ int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge) for (i = 1; i < adev->fw_cfg.max_libs_count; i++) memset(adev->lib_names[i], 0, AVS_LIB_NAME_SIZE);
+ avs_hda_power_gating_enable(adev, false); avs_hda_clock_gating_enable(adev, false); avs_hda_l1sen_enable(adev, false);
@@ -625,6 +634,7 @@ int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge) reenable_gating: avs_hda_l1sen_enable(adev, true); avs_hda_clock_gating_enable(adev, true); + avs_hda_power_gating_enable(adev, true);
if (ret < 0) return ret; diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index a869402e0f4c..d2f1aacb3b2c 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -743,6 +743,7 @@ static int avs_component_load_libraries(struct avs_soc_component *acomp) if (ret < 0) return ret;
+ avs_hda_power_gating_enable(adev, false); avs_hda_clock_gating_enable(adev, false); avs_hda_l1sen_enable(adev, false);
@@ -750,6 +751,7 @@ static int avs_component_load_libraries(struct avs_soc_component *acomp)
avs_hda_l1sen_enable(adev, true); avs_hda_clock_gating_enable(adev, true); + avs_hda_power_gating_enable(adev, true);
if (!ret) ret = avs_module_info_init(adev, false);
participants (3)
-
Cezary Rojewski
-
kernel test robot
-
Pierre-Louis Bossart