diff --git a/src/audio/component.c b/src/audio/component.c index 90999deb841a..f798e0a971e0 100644 --- a/src/audio/component.c +++ b/src/audio/component.c @@ -506,7 +506,7 @@ static bool comp_check_eos(struct comp_dev *dev) enum sof_audio_buffer_state sink_state = AUDIOBUF_STATE_INITIAL; struct comp_buffer *buffer; - if (!dev->pipeline->expect_eos) + if (!dev->expect_eos) return false; comp_dev_for_each_producer(dev, buffer) { diff --git a/src/audio/host-zephyr.c b/src/audio/host-zephyr.c index 5038bcc78320..58a22253f20d 100644 --- a/src/audio/host-zephyr.c +++ b/src/audio/host-zephyr.c @@ -396,7 +396,7 @@ static inline bool host_handle_eos(struct host_data *hd, struct comp_dev *dev, struct sof_audio_buffer *buffer = &hd->local_buffer->audio_buffer; enum sof_audio_buffer_state state = audio_buffer_get_state(buffer); - if (!dev->pipeline->expect_eos) + if (!dev->expect_eos) return false; if (!avail_samples) { diff --git a/src/audio/module_adapter/module/cadence.c b/src/audio/module_adapter/module/cadence.c index 672cef86c0a2..7edfa032321f 100644 --- a/src/audio/module_adapter/module/cadence.c +++ b/src/audio/module_adapter/module/cadence.c @@ -13,7 +13,7 @@ LOG_MODULE_REGISTER(cadence_codec, CONFIG_SOF_LOG_LEVEL); /*****************************************************************************/ /* Cadence API functions array */ /*****************************************************************************/ -struct cadence_api cadence_api_table[] = { +const struct cadence_api cadence_api_table[] = { #ifdef CONFIG_CADENCE_CODEC_WRAPPER { .id = CADENCE_CODEC_WRAPPER_ID, @@ -541,7 +541,7 @@ int cadence_codec_process_data(struct processing_module *mod, return 0; } - if (dev->pipeline->expect_eos) { + if (dev->expect_eos) { /* Signal that the stream is expected to end anytime soon */ API_CALL(cd, XA_API_CMD_INPUT_OVER, 0, NULL, ret); if (ret != LIB_NO_ERROR) { @@ -596,7 +596,7 @@ int cadence_codec_process_data(struct processing_module *mod, return ret; } - if (dev->pipeline->expect_eos) { + if (dev->expect_eos) { /* * AAC decoder cannot signal DONE, check if it stopped * producing data when EOS is expected diff --git a/src/audio/module_adapter/module/generic.c b/src/audio/module_adapter/module/generic.c index 3e328b093962..639dbc954dbb 100644 --- a/src/audio/module_adapter/module/generic.c +++ b/src/audio/module_adapter/module/generic.c @@ -124,9 +124,13 @@ int module_init(struct processing_module *mod) /* Now we can proceed with module specific initialization */ #if CONFIG_SOF_USERSPACE_APPLICATION - if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) - ret = scheduler_dp_thread_ipc(mod, SOF_IPC4_MOD_INIT_INSTANCE, NULL); - else + if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) { + union scheduler_dp_thread_ipc_param param = { + .ext_data = mod->priv.cfg.ext_data, + }; + + ret = scheduler_dp_thread_ipc(mod, SOF_IPC4_MOD_INIT_INSTANCE, ¶m); + } else #endif ret = interface->init(mod); @@ -202,9 +206,9 @@ void *z_impl_mod_balloc_align(struct processing_module *mod, size_t size, size_t return NULL; } - /* Allocate buffer memory for module */ - void *ptr = sof_heap_alloc(res->alloc->heap, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_LARGE_BUFFER, - size, alignment); + /* Allocate buffer memory for module, same as mod_alloc_ext() */ + void *ptr = sof_ctx_alloc(res->alloc, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_LARGE_BUFFER, + size, alignment); if (!ptr) { comp_err(mod->dev, "Failed to alloc %zu bytes %zu alignment for comp %#x.", diff --git a/src/audio/pipeline/pipeline-graph.c b/src/audio/pipeline/pipeline-graph.c index e6f56eaf7096..7582eee1e53e 100644 --- a/src/audio/pipeline/pipeline-graph.c +++ b/src/audio/pipeline/pipeline-graph.c @@ -344,6 +344,39 @@ int pipeline_free(struct pipeline *p) return 0; } +static int pipeline_comp_set_eos(struct comp_dev *current, + struct comp_buffer *calling_buf, + struct pipeline_walk_context *ctx, int dir) +{ + if (ctx->comp_data != (void *)current->pipeline) + return 0; + + current->expect_eos = *(bool *)ctx->buff_data; + + return pipeline_for_each_comp(current, ctx, dir); +} + +void pipeline_set_eos(struct pipeline *p, bool eos) +{ + struct pipeline_walk_context walk_ctx = { + .comp_func = pipeline_comp_set_eos, + .comp_data = p, + .buff_data = &eos, + }; + struct comp_dev *start; + int dir; + + if (p->source_comp->direction == SOF_IPC_STREAM_PLAYBACK) { + dir = PPL_DIR_UPSTREAM; + start = p->sink_comp; + } else { + dir = PPL_DIR_DOWNSTREAM; + start = p->source_comp; + } + + walk_ctx.comp_func(start, NULL, &walk_ctx, dir); +} + static int pipeline_comp_complete(struct comp_dev *current, struct comp_buffer *calling_buf, struct pipeline_walk_context *ctx, int dir) diff --git a/src/include/sof/audio/component.h b/src/include/sof/audio/component.h index 672603e430c7..48d0764f26d8 100644 --- a/src/include/sof/audio/component.h +++ b/src/include/sof/audio/component.h @@ -647,6 +647,7 @@ struct comp_dev { /* runtime */ uint16_t state; /**< COMP_STATE_ */ uint32_t frames; /**< number of frames we copy to sink */ + bool expect_eos; /**< end of stream expected */ struct pipeline *pipeline; /**< pipeline we belong to */ struct task *task; /**< component's processing task used diff --git a/src/include/sof/audio/module_adapter/module/cadence.h b/src/include/sof/audio/module_adapter/module/cadence.h index ccca668bf76a..05db21443ea4 100644 --- a/src/include/sof/audio/module_adapter/module/cadence.h +++ b/src/include/sof/audio/module_adapter/module/cadence.h @@ -97,7 +97,7 @@ struct ipc4_cadence_module_cfg { } __packed __aligned(4); #endif -extern struct cadence_api cadence_api_table[]; +extern const struct cadence_api cadence_api_table[]; int cadence_codec_set_configuration(struct processing_module *mod, uint32_t config_id, enum module_cfg_fragment_position pos, diff --git a/src/include/sof/audio/pipeline.h b/src/include/sof/audio/pipeline.h index 858c81d98a2f..1b704ed83fe1 100644 --- a/src/include/sof/audio/pipeline.h +++ b/src/include/sof/audio/pipeline.h @@ -70,7 +70,6 @@ struct pipeline { int32_t xrun_bytes; /* last xrun length */ uint32_t status; /* pipeline status */ struct tr_ctx tctx; /* trace settings */ - bool expect_eos; /* pipeline is expecting end of stream */ /* scheduling */ #ifdef CONFIG_IPC_MAJOR_4 @@ -225,6 +224,13 @@ void pipeline_posn_grant_access(struct k_thread *thread); */ int pipeline_reset(struct pipeline *p, struct comp_dev *host_cd); +/** + * \brief Sets End Of Stream state for all devices in the pipeline. + * \param[in] p pipeline. + * \param[in] eos End Of Stream state. + */ +void pipeline_set_eos(struct pipeline *p, bool eos); + /** * \brief Walks the pipeline graph for each component. * \param[in] current Current pipeline component. diff --git a/src/include/sof/schedule/dp_schedule.h b/src/include/sof/schedule/dp_schedule.h index 449bf2151872..2c72178cd766 100644 --- a/src/include/sof/schedule/dp_schedule.h +++ b/src/include/sof/schedule/dp_schedule.h @@ -17,6 +17,7 @@ #include struct processing_module; +struct module_ext_init_data; /** * @@ -115,6 +116,11 @@ struct sof_sink; */ union scheduler_dp_thread_ipc_param { const struct bind_info *bind_data; + /* SOF_IPC4_MOD_INIT_INSTANCE: ext_data points to the caller's stack frame and is + * only valid until scheduler_dp_thread_ipc() returns; ipc_thread_flatten() copies + * it by value into DP-thread-accessible memory before the DP thread runs init(). + */ + const struct module_ext_init_data *ext_data; struct { unsigned int trigger_cmd; enum ipc4_pipeline_state state; diff --git a/src/ipc/ipc4/handler-user.c b/src/ipc/ipc4/handler-user.c index c14bbe7a7ce5..5cea1bf7dbe4 100644 --- a/src/ipc/ipc4/handler-user.c +++ b/src/ipc/ipc4/handler-user.c @@ -256,7 +256,7 @@ int ipc4_pipeline_prepare(struct ipc_comp_dev *ppl_icd, uint32_t cmd) switch (cmd) { case SOF_IPC4_PIPELINE_STATE_RUNNING: - if (ppl_icd->pipeline->expect_eos) { + if (ppl_icd->pipeline->source_comp && ppl_icd->pipeline->source_comp->expect_eos) { ipc_cmd_err(&ipc_tr, "pipeline %d: Can't transition from EOS to RUNNING", ppl_icd->id); return IPC4_INVALID_REQUEST; @@ -320,7 +320,7 @@ int ipc4_pipeline_prepare(struct ipc_comp_dev *ppl_icd, uint32_t cmd) ppl_icd->id, status); return IPC4_INVALID_REQUEST; } - ppl_icd->pipeline->expect_eos = true; + pipeline_set_eos(ppl_icd->pipeline, true); return 0; /* Must return here. Any other transition clears expect_eos. */ /* special case - TODO */ case SOF_IPC4_PIPELINE_STATE_SAVED: @@ -334,7 +334,7 @@ int ipc4_pipeline_prepare(struct ipc_comp_dev *ppl_icd, uint32_t cmd) if (ret < 0) return IPC4_INVALID_REQUEST; - ppl_icd->pipeline->expect_eos = false; + pipeline_set_eos(ppl_icd->pipeline, false); return ret; } diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index 2db4edaacc8e..72da2d2a86d5 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -590,6 +590,14 @@ static int llext_manager_mod_find(const struct lib_manager_mod_ctx *ctx, unsigne { unsigned int i; + /* + * n_mod == 0 is reachable for a genuine (non-NULL) library context, e.g. + * before its module segments are registered - without this check the + * i == 0 case below reads ctx->mod[-1], out of bounds. + */ + if (!ctx->n_mod) + return -ENOENT; + for (i = 0; i < ctx->n_mod; i++) if (ctx->mod[i].start_idx > idx) break; @@ -1002,6 +1010,11 @@ static int llext_manager_add_mod_domain(struct lib_manager_module *mctx, struct int llext_manager_add_domain(const uint32_t component_id, struct k_mem_domain *domain) { const uint32_t module_id = IPC4_MOD_ID(component_id); + + /* Native (base firmware) modules aren't managed by lib_manager, nothing to add */ + if (!LIB_MANAGER_GET_LIB_ID(module_id)) + return 0; + struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); const uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); const int mod_idx = llext_manager_mod_find(ctx, entry_index); @@ -1087,6 +1100,11 @@ static int llext_manager_rm_mod_domain(struct lib_manager_module *mctx, struct k int llext_manager_rm_domain(const uint32_t component_id, struct k_mem_domain *domain) { const uint32_t module_id = IPC4_MOD_ID(component_id); + + /* Native (base firmware) modules aren't managed by lib_manager, nothing to remove */ + if (!LIB_MANAGER_GET_LIB_ID(module_id)) + return 0; + struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); const uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); const int mod_idx = llext_manager_mod_find(ctx, entry_index); diff --git a/src/schedule/zephyr_dp_schedule_application.c b/src/schedule/zephyr_dp_schedule_application.c index 043d97a8c89b..0cf8c457b9b8 100644 --- a/src/schedule/zephyr_dp_schedule_application.c +++ b/src/schedule/zephyr_dp_schedule_application.c @@ -54,6 +54,14 @@ struct ipc4_flat { struct sof_source *source[CONFIG_MODULE_MAX_CONNECTIONS]; struct sof_sink *sink[CONFIG_MODULE_MAX_CONNECTIONS]; } pipeline_state; + /* + * SOF_IPC4_MOD_INIT_INSTANCE: a by-value copy of the caller's + * ext_data. The original lives on the calling thread's stack, + * which the DP thread's memory domain doesn't have access to. + * The pointers it contains reference the IPC mailbox, which stays + * valid and is covered by the DP thread's SOF_DP_PART_CFG partition. + */ + struct module_ext_init_data init_instance; }; }; @@ -73,6 +81,12 @@ static int ipc_thread_flatten(unsigned int cmd, const union scheduler_dp_thread_ flat->bind.bu = *param->bind_data->ipc4_data; flat->bind.type = param->bind_data->bind_type; break; + case SOF_IPC4_MOD_INIT_INSTANCE: + if (param->ext_data) + flat->init_instance = *param->ext_data; + else + flat->init_instance = (struct module_ext_init_data){ 0 }; + break; case SOF_IPC4_GLB_SET_PIPELINE_STATE: flat->pipeline_state.trigger_cmd = param->pipeline_state.trigger_cmd; switch (param->pipeline_state.trigger_cmd) { @@ -133,7 +147,13 @@ static void ipc_thread_unflatten_run(struct processing_module *pmod, struct ipc4 flat->ret = ops->free(pmod); break; case SOF_IPC4_MOD_INIT_INSTANCE: + /* + * Repoint ext_data at the copy ipc_thread_flatten() made in DP-thread- + * accessible memory; the original caller-stack copy is out of reach here. + */ + pmod->priv.cfg.ext_data = &flat->init_instance; flat->ret = ops->init(pmod); + pmod->priv.cfg.ext_data = NULL; break; case SOF_IPC4_GLB_SET_PIPELINE_STATE: switch (flat->pipeline_state.trigger_cmd) { diff --git a/test/cmocka/src/audio/mux/demux_copy.c b/test/cmocka/src/audio/mux/demux_copy.c index 8e331446bbf4..cb789707306f 100644 --- a/test/cmocka/src/audio/mux/demux_copy.c +++ b/test/cmocka/src/audio/mux/demux_copy.c @@ -169,7 +169,7 @@ static int setup_test_case(void **state) dummy_pipe = test_malloc(sizeof(*dummy_pipe)); if (!dummy_pipe) return -ENOMEM; - dummy_pipe->expect_eos = false; + dev->expect_eos = false; dev->pipeline = dummy_pipe; mod = comp_mod(dev); diff --git a/test/cmocka/src/audio/mux/mux_copy.c b/test/cmocka/src/audio/mux/mux_copy.c index 66b21b0df27c..a4c42f99e94e 100644 --- a/test/cmocka/src/audio/mux/mux_copy.c +++ b/test/cmocka/src/audio/mux/mux_copy.c @@ -191,7 +191,7 @@ static int setup_test_case(void **state) dummy_pipe = test_malloc(sizeof(*dummy_pipe)); if (!dummy_pipe) return -ENOMEM; - dummy_pipe->expect_eos = false; + dev->expect_eos = false; dev->pipeline = dummy_pipe; mod = comp_mod(dev); diff --git a/tools/topology/topology2/include/components/decoder.conf b/tools/topology/topology2/include/components/decoder.conf index b01a1c56d050..79788de728ae 100644 --- a/tools/topology/topology2/include/components/decoder.conf +++ b/tools/topology/topology2/include/components/decoder.conf @@ -57,6 +57,16 @@ Class.Widget."decoder" { # cadence codec UUID uuid "43:84:21:d8:f3:5f:4c:4a:b3:88:6c:fe:07:b9:56:aa" + + # Cadence codec memory tables (persist/scratch/input/output) can exceed the + # generic widget-common.conf default; give decoders more headroom. This same + # vregion also backs every buffer bound to a DP module (ipc4_comp_connect() + # allocates from dp->mod->priv.resources.alloc), so it must cover the + # codec's own memory tables plus every connected buffer, not just the codec. + heap_bytes_requirement 196608 + # Cadence codec init (e.g. xa_aac_dec) can need more stack than the + # generic widget-common.conf default when run in the DP userspace thread. + stack_bytes_requirement 16384 no_pm "true" num_output_pins 1 num_input_pins 1 diff --git a/tools/topology/topology2/include/components/encoder.conf b/tools/topology/topology2/include/components/encoder.conf index cde2346e3885..e8be2e6adb45 100644 --- a/tools/topology/topology2/include/components/encoder.conf +++ b/tools/topology/topology2/include/components/encoder.conf @@ -57,6 +57,16 @@ Class.Widget."encoder" { # cadence codec UUID uuid "43:84:21:d8:f3:5f:4c:4a:b3:88:6c:fe:07:b9:56:aa" + + # Cadence codec memory tables (persist/scratch/input/output) can exceed the + # generic widget-common.conf default; give encoders more headroom. This same + # vregion also backs every buffer bound to a DP module (ipc4_comp_connect() + # allocates from dp->mod->priv.resources.alloc), so it must cover the + # codec's own memory tables plus every connected buffer, not just the codec. + heap_bytes_requirement 131072 + # Cadence codec init (e.g. xa_mp3_enc) can need more stack than the + # generic widget-common.conf default when run in the DP userspace thread. + stack_bytes_requirement 16384 no_pm "true" num_output_pins 1 num_input_pins 1 diff --git a/zephyr/lib/vregion.c b/zephyr/lib/vregion.c index fb65906446fa..1653d48a9a66 100644 --- a/zephyr/lib/vregion.c +++ b/zephyr/lib/vregion.c @@ -130,8 +130,15 @@ struct vregion *vregion_create(size_t memsize) */ total_size = ALIGN_UP(memsize, CONFIG_MM_DRV_PAGE_SIZE); - /* allocate vregion metadata separately to keep it inaccessible to the user */ - vr = rmalloc(0, sizeof(*vr)); + /* + * allocate vregion metadata separately to keep it inaccessible to the + * user. The vregion is created on the DP module's own core but is + * later read/written from other cores too (e.g. buffer_new() on the + * IPC core routes through it for buffers connecting to this module), + * so it must be coherent - plain cached memory left writes from the + * creating core invisible to other cores without an explicit flush. + */ + vr = rmalloc(SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT, sizeof(*vr)); if (!vr) return NULL;