Skip to content

Commit

Permalink
zephyr: Explicitly manage DAI power states during D3 transitions
Browse files Browse the repository at this point in the history
Zephyr's device power management framework offers two methods for
reducing power consumption: Device Runtime Power Management and
System-Managed Device Power Management. These methods allow devices to
be suspended when idle, either independently or as part of system power
state transitions. The framework is designed to minimize power usage
with minimal intervention from applications, relying on device drivers
to manage the power state transitions of their devices.

The SOF firmware uses Zephyr's Device Runtime Power Management for DAIs,
where the device driver is responsible for indicating the active or idle
state of the device. However, during system-wide power state transitions
such as D3 entry and exit, explicit power state management is necessary
to maintain audio data integrity and prevent artifacts. In SOF, entry
into the D3 power state requires that there be no active audio
pipelines. This means that all pipelines must be paused (if not
deleted), and while paused DAIs remain powered up, they must be
explicitly managed to ensure they are in the correct state for D3
transitions.

This patch enhances the SOF firmware's power management by adding static
helper functions `suspend_dais()` and `resume_dais()` within `cpu.c`.
These functions manage the power states of DAI components explicitly
during D3 state transitions, ensuring that DAIs are suspended before the
DSP core enters D3 and resumed upon wake-up. By implementing this logic
within the SOF firmware, we provide a more integrated and reliable power
management process that aligns with the complex requirements of audio
DSP workloads. This approach ensures that the SOF firmware can manage
DAI power states effectively, complementing Zephyr's device runtime PM
framework.

Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
  • Loading branch information
tmleman committed Sep 11, 2024
1 parent 8c75f80 commit b7564b1
Showing 1 changed file with 72 additions and 0 deletions.
72 changes: 72 additions & 0 deletions zephyr/lib/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@
* \authors Tomasz Leman <tomasz.m.leman@intel.com>
*/

#include <sof/audio/component.h>
#include <sof/init.h>
#include <sof/lib/cpu.h>
#include <sof/lib/pm_runtime.h>
#include <ipc/topology.h>
#include <module/module/base.h>
#include <rtos/alloc.h>

#include "../audio/copier/copier.h"

/* Zephyr includes */
#include <version.h>
#include <zephyr/kernel.h>
Expand Down Expand Up @@ -47,6 +51,70 @@ extern void *global_imr_ram_storage;
#endif

#if CONFIG_PM
#ifdef CONFIG_ADSP_IMR_CONTEXT_SAVE
/*
* SOF explicitly manages DAI power states to meet the audio-specific requirement
* that all audio pipelines must be paused prior to entering the D3 power state.
* Zephyr's PM framework is designed to suspend devices based on their runtime
* usage, which does not align with the audio pipeline lifecycle managed by SOF.
* During system PM transitions, Zephyr does not automatically handle the suspension
* of DAIs, as it lacks the context of audio pipeline states. Therefore, SOF
* implements additional logic to synchronize DAI states with the DSP core during
* audio pipeline pauses and resumes. This ensures seamless audio performance and
* data integrity across D3 transitions, which is critical for SOF's operation
* and currently outside the scope of Zephyr's device-level PM capabilities.
*/
static void suspend_dais(void)
{
struct ipc_comp_dev *icd;
struct list_item *clist;
struct processing_module *mod;
struct copier_data *cd;
struct dai_data *dd;

list_for_item(clist, &ipc_get()->comp_list) {
icd = container_of(clist, struct ipc_comp_dev, list);
if (icd->type != COMP_TYPE_COMPONENT)
continue;

if (dev_comp_type(icd->cd) == SOF_COMP_DAI) {
mod = comp_mod(icd->cd);
cd = module_get_private_data(mod);
dd = cd->dd[0];
if (dai_remove(dd->dai->dev) < 0) {
tr_err(&zephyr_tr, "DAI suspend failed, type %d index %d",
dd->dai->type, dd->dai->index);
}
}
}
}

static void resume_dais(void)
{
struct ipc_comp_dev *icd;
struct list_item *clist;
struct processing_module *mod;
struct copier_data *cd;
struct dai_data *dd;

list_for_item(clist, &ipc_get()->comp_list) {
icd = container_of(clist, struct ipc_comp_dev, list);
if (icd->type != COMP_TYPE_COMPONENT)
continue;

if (dev_comp_type(icd->cd) == SOF_COMP_DAI) {
mod = comp_mod(icd->cd);
cd = module_get_private_data(mod);
dd = cd->dd[0];
if (dai_probe(dd->dai->dev) < 0) {
tr_err(&zephyr_tr, "DAI resume failed, type %d index %d",
dd->dai->type, dd->dai->index);
}
}
}
}
#endif /* CONFIG_ADSP_IMR_CONTEXT_SAVE */

void cpu_notify_state_entry(enum pm_state state)
{
if (!cpu_is_primary(arch_proc_id()))
Expand Down Expand Up @@ -80,6 +148,8 @@ void cpu_notify_state_entry(enum pm_state state)
k_panic();
}

/* Suspend all DAI components before entering D3 state. */
suspend_dais();
#endif /* CONFIG_ADSP_IMR_CONTEXT_SAVE */
}
}
Expand All @@ -99,6 +169,8 @@ void cpu_notify_state_exit(enum pm_state state)
#endif

#ifdef CONFIG_ADSP_IMR_CONTEXT_SAVE
/* Resume all DAI components after exiting D3 state. */
resume_dais();
/* free global_imr_ram_storage */
rfree(global_imr_ram_storage);
global_imr_ram_storage = NULL;
Expand Down

0 comments on commit b7564b1

Please sign in to comment.