diff --git a/src/audio/pipeline/pipeline-stream.c b/src/audio/pipeline/pipeline-stream.c index af696810d5c5..8b16345e79f9 100644 --- a/src/audio/pipeline/pipeline-stream.c +++ b/src/audio/pipeline/pipeline-stream.c @@ -11,12 +11,15 @@ #include #include #include +#include #include #include #include #include #include #include +#include +#include #include #include @@ -311,17 +314,80 @@ static void pipeline_trigger_xrun(struct pipeline *p, struct comp_dev **host) } while (true); } +#if CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL +static int pipeline_calc_cps_consumption(struct comp_dev *current, + struct comp_buffer *calling_buf, + struct pipeline_walk_context *ctx, int dir) +{ + struct pipeline_data *ppl_data = ctx->comp_data; + struct ipc4_base_module_cfg *cd; + int comp_core, kcps; + + pipe_dbg(ppl_data->p, "pipeline_calc_cps_consumption(), current->comp.id = %u, dir = %u", + dev_comp_id(current), dir); + + if (!comp_is_single_pipeline(current, ppl_data->start)) { + pipe_dbg(ppl_data->p, "pipeline_calc_cps_consumption(), current is from another pipeline"); + return 0; + } + comp_core = current->ipc_config.core; + + /* modules created through module adapter have different priv_data */ + if (current->drv->type != SOF_COMP_MODULE_ADAPTER) { + cd = comp_get_drvdata(current); + } else { + struct processing_module *mod = comp_get_drvdata(current); + struct module_data *md = &mod->priv; + + cd = &md->cfg.base_cfg; + } + + if (cd->cpc == 0) { + /* Use maximum clock budget, assume 1ms chunk size */ + ppl_data->kcps[comp_core] = CLK_MAX_CPU_HZ / 1000; + tr_warn(pipe, + "0 CPS requested for module: %#x, core: %d using safe max KCPS: %u", + current->ipc_config.id, comp_core, ppl_data->kcps[comp_core]); + + return PPL_STATUS_PATH_STOP; + } else { + kcps = cd->cpc * 1000 / current->period; + tr_dbg(pipe, "Module: %#x KCPS consumption: %d, core: %d", + current->ipc_config.id, kcps, comp_core); + + ppl_data->kcps[comp_core] += kcps; + } + + return pipeline_for_each_comp(current, ctx, dir); +} +#endif + /* trigger pipeline in IPC context */ int pipeline_trigger(struct pipeline *p, struct comp_dev *host, int cmd) { int ret; - +#if CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL + struct pipeline_data data = { + .start = p->source_comp, + .p = p, + }; + struct pipeline_walk_context walk_ctx = { + .comp_func = pipeline_calc_cps_consumption, + .comp_data = &data, + }; + bool trigger_first = false; + uint32_t flags = 0; +#endif pipe_info(p, "pipe trigger cmd %d", cmd); p->trigger.aborted = false; switch (cmd) { case COMP_TRIGGER_PAUSE: +#if CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL + trigger_first = true; + COMPILER_FALLTHROUGH; +#endif case COMP_TRIGGER_STOP: if (p->status == COMP_STATE_PAUSED || p->xrun_bytes) { /* The task isn't running, trigger inline */ @@ -338,9 +404,40 @@ int pipeline_trigger(struct pipeline *p, struct comp_dev *host, int cmd) case COMP_TRIGGER_PRE_RELEASE: case COMP_TRIGGER_PRE_START: /* Add all connected pipelines to the list and trigger them all */ +#if CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL + irq_local_disable(flags); + /* setup walking ctx for removing consumption */ + if (!trigger_first) { + ret = walk_ctx.comp_func(p->source_comp, NULL, &walk_ctx, PPL_DIR_DOWNSTREAM); + + for (int i = 0; i < arch_num_cpus(); i++) { + if (data.kcps[i] > 0) { + core_kcps_adjust(i, data.kcps[i]); + tr_info(pipe, "Sum of KCPS consumption: %d, core: %d", core_kcps_get(i), i); + } + } + } +#endif ret = pipeline_trigger_list(p, host, cmd); - if (ret < 0) + if (ret < 0) { +#if CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL + irq_local_enable(flags); +#endif return ret; + } +#if CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL + if (trigger_first) { + ret = walk_ctx.comp_func(p->source_comp, NULL, &walk_ctx, PPL_DIR_DOWNSTREAM); + + for (int i = 0; i < arch_num_cpus(); i++) { + if (data.kcps[i] > 0) { + core_kcps_adjust(i, -data.kcps[i]); + tr_info(pipe, "Sum of KCPS consumption: %d, core: %d", core_kcps_get(i), i); + } + } + } + irq_local_enable(flags); +#endif /* IPC response will be sent from the task, unless it was paused */ return PPL_STATUS_SCHEDULED; } diff --git a/src/include/sof/audio/pipeline.h b/src/include/sof/audio/pipeline.h index 8dc5ea16d51d..0edb3305680c 100644 --- a/src/include/sof/audio/pipeline.h +++ b/src/include/sof/audio/pipeline.h @@ -122,6 +122,9 @@ struct pipeline_data { struct pipeline *p; int cmd; uint32_t delay_ms; /* between PRE_{START,RELEASE} and {START,RELEASE} */ +#if CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL + uint32_t kcps[CONFIG_CORE_COUNT]; /**< the max count of KCPS */ +#endif }; /** \brief Task type registered by pipelines. */ diff --git a/src/lib/cpu-clk-manager.c b/src/lib/cpu-clk-manager.c index d23bb6e91cb3..ee92196aea2e 100644 --- a/src/lib/cpu-clk-manager.c +++ b/src/lib/cpu-clk-manager.c @@ -64,7 +64,7 @@ int core_kcps_adjust(int adjusted_core_id, int kcps_delta) for (core_id = 0; core_id < CONFIG_CORE_COUNT; core_id++) { /* Convert kcps to cps */ - ret = request_freq_change(core_id, freq * 1000); + ret = request_freq_change(core_id, MIN(freq * 1000, CLK_MAX_CPU_HZ)); if (ret < 0) goto out; } diff --git a/src/platform/Kconfig b/src/platform/Kconfig index 66dce8ea6f85..462074a028a9 100644 --- a/src/platform/Kconfig +++ b/src/platform/Kconfig @@ -377,6 +377,14 @@ config CAVS_USE_LPRO_IN_WAITI After waiti exit clock source will be restored. Choose n if unclear. +config KCPS_DYNAMIC_CLOCK_CONTROL + bool "Use KCPS budget to determine DSP clock" + default n + depends on IPC_MAJOR_4 + help + Select if we want to use compute budget + expressed in Kilo Cycles Per Second (KCPS) to determine DSP clock. + config L3_HEAP bool "Use L3 memory heap" default n diff --git a/src/platform/imx93_a55/include/platform/lib/clk.h b/src/platform/imx93_a55/include/platform/lib/clk.h index 6703c1304a7a..cb834906b485 100644 --- a/src/platform/imx93_a55/include/platform/lib/clk.h +++ b/src/platform/imx93_a55/include/platform/lib/clk.h @@ -12,6 +12,7 @@ #define CPU_DEFAULT_IDX 0 #define NUM_CLOCKS 1 #define NUM_CPU_FREQ 1 +#define CLK_MAX_CPU_HZ CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC struct sof;