diff --git a/src/audio/buffers/comp_buffer.c b/src/audio/buffers/comp_buffer.c index cc38a7dfead7..c2b64ce04f3b 100644 --- a/src/audio/buffers/comp_buffer.c +++ b/src/audio/buffers/comp_buffer.c @@ -217,9 +217,9 @@ static struct comp_buffer *buffer_alloc_struct(struct mod_alloc_ctx *alloc, if (!alloc || !alloc->vreg) buffer = sof_heap_alloc(alloc ? alloc->heap : NULL, flags, sizeof(*buffer), 0); else if (is_shared) - buffer = vregion_alloc_coherent(alloc->vreg, VREGION_MEM_TYPE_INTERIM, sizeof(*buffer)); + buffer = vregion_alloc_coherent(alloc->vreg, alloc->vregion_type, sizeof(*buffer)); else - buffer = vregion_alloc(alloc->vreg, VREGION_MEM_TYPE_INTERIM, sizeof(*buffer)); + buffer = vregion_alloc(alloc->vreg, alloc->vregion_type, sizeof(*buffer)); if (!buffer) { tr_err(&buffer_tr, "could not alloc structure"); return NULL; diff --git a/src/audio/buffers/ring_buffer.c b/src/audio/buffers/ring_buffer.c index fe67027df8db..a9b1d6161c36 100644 --- a/src/audio/buffers/ring_buffer.c +++ b/src/audio/buffers/ring_buffer.c @@ -304,9 +304,9 @@ struct ring_buffer *ring_buffer_create(struct comp_dev *dev, size_t min_availabl if (!vreg) ring_buffer = sof_heap_alloc(heap, memory_flags, sizeof(*ring_buffer), 0); else if (is_shared) - ring_buffer = vregion_alloc_coherent(vreg, VREGION_MEM_TYPE_INTERIM, sizeof(*ring_buffer)); + ring_buffer = vregion_alloc_coherent(vreg, VREGION_MEM_TYPE_LIFETIME, sizeof(*ring_buffer)); else - ring_buffer = vregion_alloc(vreg, VREGION_MEM_TYPE_INTERIM, sizeof(*ring_buffer)); + ring_buffer = vregion_alloc(vreg, VREGION_MEM_TYPE_LIFETIME, sizeof(*ring_buffer)); if (!ring_buffer) return NULL; @@ -383,7 +383,7 @@ struct ring_buffer *ring_buffer_create(struct comp_dev *dev, size_t min_availabl void *data_buf; if (vreg) - data_buf = vregion_alloc_align(vreg, VREGION_MEM_TYPE_INTERIM, ring_buffer->data_buffer_size, + data_buf = vregion_alloc_align(vreg, VREGION_MEM_TYPE_LIFETIME, ring_buffer->data_buffer_size, PLATFORM_DCACHE_ALIGN); else data_buf = sof_heap_alloc(heap, user_get_buffer_memory_region(dev->drv), diff --git a/src/audio/module_adapter/module/generic.c b/src/audio/module_adapter/module/generic.c index 2989bceb160b..1643f956cf01 100644 --- a/src/audio/module_adapter/module/generic.c +++ b/src/audio/module_adapter/module/generic.c @@ -251,16 +251,18 @@ void *z_impl_mod_alloc_ext(struct processing_module *mod, uint32_t flags, size_t return NULL; } - /* Allocate memory for module */ + /* Allocate memory for module. Before init completes, use + * lifetime partition so allocations persist across prepare/reset. + */ void *ptr; if (!res->alloc->vreg) ptr = sof_heap_alloc(res->alloc->heap, flags, size, alignment); else if (flags & SOF_MEM_FLAG_COHERENT) - ptr = vregion_alloc_coherent_align(res->alloc->vreg, VREGION_MEM_TYPE_INTERIM, + ptr = vregion_alloc_coherent_align(res->alloc->vreg, res->alloc->vregion_type, size, alignment); else - ptr = vregion_alloc_align(res->alloc->vreg, VREGION_MEM_TYPE_INTERIM, + ptr = vregion_alloc_align(res->alloc->vreg, res->alloc->vregion_type, size, alignment); if (!ptr) { diff --git a/src/audio/module_adapter/module_adapter.c b/src/audio/module_adapter/module_adapter.c index adcb5ae829e0..f21c8ce1849a 100644 --- a/src/audio/module_adapter/module_adapter.c +++ b/src/audio/module_adapter/module_adapter.c @@ -59,23 +59,46 @@ struct comp_dev *module_adapter_new(const struct comp_driver *drv, #endif static struct vregion *module_adapter_dp_heap_new(const struct comp_ipc_config *config, + const struct module_ext_init_data *ext_init, size_t *heap_size) { /* src-lite with 8 channels has been seen allocating 14k in one go */ - /* FIXME: the size will be derived from configuration */ - const size_t buf_size = 28 * 1024; + size_t buf_size = CONFIG_SOF_USERSPACE_DP_DEFAULT_HEAP_SIZE; + size_t lifetime_size = 4096; - /* - * A 1-to-1 replacement of the original heap implementation would be to - * have "lifetime size" equal to 0. But (1) this is invalid for - * vregion_create() and (2) we gradually move objects, that are simple - * to move to the lifetime buffer. Make it 4k for the beginning. - */ - return vregion_create(4096, buf_size - 4096); +#if CONFIG_IPC_MAJOR_4 + if (config->ipc_extended_init && ext_init && ext_init->dp_data && + (ext_init->dp_data->lifetime_heap_bytes > 0 || + ext_init->dp_data->interim_heap_bytes > 0)) { + if (ext_init->dp_data->lifetime_heap_bytes > 64*1024*1024 || + ext_init->dp_data->interim_heap_bytes > 64*1024*1024 || + ext_init->dp_data->lifetime_heap_bytes == 0 || + ext_init->dp_data->interim_heap_bytes == 0) { + LOG_ERR("Bad lifetime %u or interim %u heap size for %#x", + ext_init->dp_data->lifetime_heap_bytes, + ext_init->dp_data->interim_heap_bytes, config->id); + return NULL; + } + + lifetime_size = ext_init->dp_data->lifetime_heap_bytes; + buf_size = ext_init->dp_data->lifetime_heap_bytes + + ext_init->dp_data->interim_heap_bytes; + + LOG_INF("to %u + %u = %zu byte heap size requested in IPC for %#x", + ext_init->dp_data->lifetime_heap_bytes, + ext_init->dp_data->interim_heap_bytes, buf_size, config->id); + } +#endif + + *heap_size = buf_size; + + return vregion_create(lifetime_size, buf_size - lifetime_size); } -static struct processing_module *module_adapter_mem_alloc(const struct comp_driver *drv, - const struct comp_ipc_config *config) +static +struct processing_module *module_adapter_mem_alloc(const struct comp_driver *drv, + const struct comp_ipc_config *config, + const struct module_ext_init_data *ext_init) { struct k_heap *mod_heap; struct vregion *mod_vreg; @@ -94,7 +117,7 @@ static struct processing_module *module_adapter_mem_alloc(const struct comp_driv if (config->proc_domain == COMP_PROCESSING_DOMAIN_DP && IS_ENABLED(CONFIG_USERSPACE) && !IS_ENABLED(CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP)) { - mod_vreg = module_adapter_dp_heap_new(config, &heap_size); + mod_vreg = module_adapter_dp_heap_new(config, ext_init, &heap_size); if (!mod_vreg) { comp_cl_err(drv, "Failed to allocate DP module heap / vregion"); return NULL; @@ -126,6 +149,8 @@ static struct processing_module *module_adapter_mem_alloc(const struct comp_driv memset(mod, 0, sizeof(*mod)); alloc->heap = mod_heap; alloc->vreg = mod_vreg; + /* Set vreg type to lifetime mode first, will become interim in module_adapter_prepare() */ + alloc->vregion_type = VREGION_MEM_TYPE_LIFETIME; mod->priv.resources.alloc = alloc; mod_resource_init(mod); @@ -230,8 +255,14 @@ struct comp_dev *module_adapter_new_ext(const struct comp_driver *drv, return NULL; } #endif + const struct module_ext_init_data *ext_init = +#if CONFIG_IPC_MAJOR_4 + &ext_data; +#else + NULL; +#endif - struct processing_module *mod = module_adapter_mem_alloc(drv, config); + struct processing_module *mod = module_adapter_mem_alloc(drv, config, ext_init); if (!mod) return NULL; @@ -244,6 +275,18 @@ struct comp_dev *module_adapter_new_ext(const struct comp_driver *drv, struct comp_dev *dev = mod->dev; + dst = &mod->priv.cfg; + /* + * NOTE: dst->ext_data points to stack variable and contains + * pointers to IPC payload mailbox, so its only valid in + * functions that are called from this function. This is + * why the pointer is set to NULL before this function + * exits. + */ +#if CONFIG_IPC_MAJOR_4 + dst->ext_data = &ext_data; +#endif + #if CONFIG_ZEPHYR_DP_SCHEDULER /* create a task for DP processing */ if (config->proc_domain == COMP_PROCESSING_DOMAIN_DP) { @@ -256,16 +299,6 @@ struct comp_dev *module_adapter_new_ext(const struct comp_driver *drv, } #endif /* CONFIG_ZEPHYR_DP_SCHEDULER */ - dst = &mod->priv.cfg; - /* - * NOTE: dst->ext_data points to stack variable and contains - * pointers to IPC payload mailbox, so its only valid in - * functions that called from this function. This why - * the pointer is set NULL before this function exits. - */ -#if CONFIG_IPC_MAJOR_4 - dst->ext_data = &ext_data; -#endif ret = module_adapter_init_data(dev, dst, config, &spec); if (ret) { comp_err(dev, "%d: module init data failed", @@ -391,6 +424,9 @@ int module_adapter_prepare(struct comp_dev *dev) int i = 0; comp_dbg(dev, "start"); + + /* Prepare has been called, from now on all vreg allocs should be INTERIM. */ + mod->priv.resources.alloc->vregion_type = VREGION_MEM_TYPE_INTERIM; #if CONFIG_IPC_MAJOR_4 /* allocate stream_params and retrieve the params from the basecfg if needed */ if (!mod->stream_params) { diff --git a/src/audio/pipeline/pipeline-schedule.c b/src/audio/pipeline/pipeline-schedule.c index 45fd1eed639c..5ad8a5074f0e 100644 --- a/src/audio/pipeline/pipeline-schedule.c +++ b/src/audio/pipeline/pipeline-schedule.c @@ -388,6 +388,7 @@ int pipeline_comp_dp_task_init(struct comp_dev *comp) { /* DP tasks are guaranteed to have a module_adapter */ struct processing_module *mod = comp_mod(comp); + size_t stack_size = TASK_DP_STACK_SIZE; struct task_ops ops = { .run = dp_task_run, .get_deadline = NULL, @@ -403,8 +404,17 @@ int pipeline_comp_dp_task_init(struct comp_dev *comp) unsigned int flags = IS_ENABLED(CONFIG_USERSPACE) ? K_USER : 0; #endif + if (mod->priv.cfg.ext_data && mod->priv.cfg.ext_data->dp_data && + mod->priv.cfg.ext_data->dp_data->stack_bytes > 0) { + stack_size = MAX(mod->priv.cfg.ext_data->dp_data->stack_bytes, + CONFIG_ZEPHYR_DP_SCHEDULER_MIN_STACK_SIZE); + comp_info(comp, "stack size set to %zu, %zu requested, min allowed %zu", + stack_size, mod->priv.cfg.ext_data->dp_data->stack_bytes, + CONFIG_ZEPHYR_DP_SCHEDULER_MIN_STACK_SIZE); + } + return scheduler_dp_task_init(&comp->task, SOF_UUID(dp_task_uuid), &ops, mod, - comp->ipc_config.core, TASK_DP_STACK_SIZE, flags); + comp->ipc_config.core, stack_size, flags); } #endif /* CONFIG_ZEPHYR_DP_SCHEDULER */ diff --git a/src/audio/src/src.c b/src/audio/src/src.c index 9870ad911ca8..b75ff5a4cce7 100644 --- a/src/audio/src/src.c +++ b/src/audio/src/src.c @@ -34,19 +34,16 @@ LOG_MODULE_DECLARE(src, CONFIG_SOF_LOG_LEVEL); -static int src_prepare(struct processing_module *mod, - struct sof_source **sources, int num_of_sources, - struct sof_sink **sinks, int num_of_sinks) +/* Set rate table pointers, compute rate indices, and copy filter stages. + * Must be in src.c because src_table1/2, src_in_fs, etc. come from + * the coefficient headers included by this file. + */ +static int src_setup_stages(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); struct src_param *a = &cd->param; int ret; - comp_info(mod->dev, "entry"); - - if (num_of_sources != 1 || num_of_sinks != 1) - return -EINVAL; - a->in_fs = src_in_fs; a->out_fs = src_out_fs; a->num_in_fs = NUM_IN_FS; @@ -54,27 +51,46 @@ static int src_prepare(struct processing_module *mod, a->max_fir_delay_size_xnch = (PLATFORM_MAX_CHANNELS * MAX_FIR_DELAY_SIZE); a->max_out_delay_size_xnch = (PLATFORM_MAX_CHANNELS * MAX_OUT_DELAY_SIZE); - src_get_source_sink_params(mod->dev, sources[0], sinks[0]); - ret = src_param_set(mod->dev, cd); if (ret < 0) return ret; - ret = src_allocate_copy_stages(mod, a, - src_table1[a->idx_out][a->idx_in], - src_table2[a->idx_out][a->idx_in]); - if (ret < 0) - return ret; + return src_allocate_copy_stages(mod, a, + src_table1[a->idx_out][a->idx_in], + src_table2[a->idx_out][a->idx_in]); +} - ret = src_params_general(mod, sources[0], sinks[0]); +static int src_do_init(struct processing_module *mod) +{ + struct comp_data *cd; + int ret; + + ret = src_init(mod); if (ret < 0) return ret; - return src_prepare_general(mod, sources[0], sinks[0]); + cd = module_get_private_data(mod); + cd->setup_stages = src_setup_stages; + + return src_init_stages(mod); +} + +static int src_prepare(struct processing_module *mod, + struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) +{ + comp_info(mod->dev, "entry"); + + if (num_of_sources != 1 || num_of_sinks != 1) + return -EINVAL; + + src_get_source_sink_params(mod->dev, sources[0], sinks[0]); + + return src_prepare_do(mod, sources[0], sinks[0]); } static const struct module_interface src_interface = { - .init = src_init, + .init = src_do_init, .prepare = src_prepare, .process = src_process, .is_ready_to_process = src_is_ready_to_process, diff --git a/src/audio/src/src_common.c b/src/audio/src/src_common.c index 3a9c7dac280f..e4ccf76ae3ba 100644 --- a/src/audio/src/src_common.c +++ b/src/audio/src/src_common.c @@ -574,6 +574,86 @@ int src_params_general(struct processing_module *mod, return 0; } +/* Allocate delay lines and initialize the polyphase SRC filter. + * Assumes that cd->param rate table pointers (in_fs, out_fs, etc.) + * and stage pointers (stage1, stage2) are already set up via + * cd->setup_stages(). + */ +int src_allocate_delay_lines(struct processing_module *mod) +{ + struct comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + size_t delay_lines_size; + int32_t *buffer_start; + int n; + int ret; + + /* For LL modules dev->period is already set from the pipeline. + * Compute dev->frames so buffer sizing works. + */ + if (!dev->frames) + component_set_nearest_period_frames(dev, cd->sink_rate); + + if (!cd->sink_rate) { + comp_err(dev, "zero sink rate"); + return -EINVAL; + } + + cd->source_frames = dev->frames * cd->source_rate / cd->sink_rate; + cd->sink_frames = dev->frames; + + /* Allocate needed memory for delay lines */ + ret = src_buffer_lengths(dev, cd, cd->channels_count); + if (ret < 0) { + comp_err(dev, "src_buffer_lengths() failed"); + return ret; + } + + delay_lines_size = ALIGN_UP(sizeof(int32_t) * cd->param.total, 8); + if (delay_lines_size == 0) { + comp_err(dev, "delay_lines_size = 0"); + return -EINVAL; + } + + mod_free(mod, cd->delay_lines); + + cd->delay_lines = mod_alloc(mod, delay_lines_size); + if (!cd->delay_lines) { + comp_err(dev, "failed to alloc cd->delay_lines, delay_lines_size = %zu", + delay_lines_size); + return -ENOMEM; + } + + memset(cd->delay_lines, 0, delay_lines_size); + buffer_start = cd->delay_lines + ALIGN_UP(cd->param.sbuf_length, 2); + + /* Initialize SRC for actual sample rate */ + n = src_polyphase_init(&cd->src, &cd->param, buffer_start); + + /* Reset stage buffer */ + cd->sbuf_r_ptr = cd->delay_lines; + cd->sbuf_w_ptr = cd->delay_lines; + cd->sbuf_avail = 0; + + switch (n) { + case 0: + cd->src_func = src_copy_sxx; + break; + case 1: + cd->src_func = src_1s; + break; + case 2: + cd->src_func = src_2s; + break; + default: + comp_info(dev, "missing coefficients for requested rates combination"); + cd->src_func = src_fallback; + return -EINVAL; + } + + return 0; +} + int src_param_set(struct comp_dev *dev, struct comp_data *cd) { struct src_param *a = &cd->param; diff --git a/src/audio/src/src_common.h b/src/audio/src/src_common.h index 98f29a263131..126b112737d3 100644 --- a/src/audio/src/src_common.h +++ b/src/audio/src/src_common.h @@ -167,6 +167,7 @@ struct comp_data { int (*src_func)(struct comp_data *cd, struct sof_source *source, struct sof_sink *sink); void (*polyphase_func)(struct src_stage_prm *s); + int (*setup_stages)(struct processing_module *mod); }; #if CONFIG_IPC_MAJOR_4 @@ -218,6 +219,7 @@ static inline int src_fallback(struct comp_data *cd, int src_allocate_copy_stages(struct processing_module *mod, struct src_param *prm, const struct src_stage *stage_src1, const struct src_stage *stage_src2); +int src_allocate_delay_lines(struct processing_module *mod); int src_rate_check(const void *spec); int src_set_params(struct processing_module *mod, struct sof_sink *sink); @@ -227,6 +229,9 @@ int src_prepare_general(struct processing_module *mod, struct sof_source *source, struct sof_sink *sink); int src_init(struct processing_module *mod); +int src_init_stages(struct processing_module *mod); +int src_prepare_do(struct processing_module *mod, + struct sof_source *source, struct sof_sink *sink); int src_copy_sxx(struct comp_data *cd, struct sof_source *source, struct sof_sink *sink); diff --git a/src/audio/src/src_ipc3.c b/src/audio/src/src_ipc3.c index 541efcdb24be..636c50bfdcea 100644 --- a/src/audio/src/src_ipc3.c +++ b/src/audio/src/src_ipc3.c @@ -195,3 +195,27 @@ int src_init(struct processing_module *mod) return 0; } +/* IPC3: No filter allocation at init, change ipc3 behavior as little as possible */ +int src_init_stages(struct processing_module *mod) +{ + return 0; +} + +/* IPC3: Full filter setup at prepare time */ +int src_prepare_do(struct processing_module *mod, + struct sof_source *source, struct sof_sink *sink) +{ + struct comp_data *cd = module_get_private_data(mod); + int ret; + + ret = cd->setup_stages(mod); + if (ret < 0) + return ret; + + ret = src_params_general(mod, source, sink); + if (ret < 0) + return ret; + + return src_prepare_general(mod, source, sink); +} + diff --git a/src/audio/src/src_ipc4.c b/src/audio/src/src_ipc4.c index 91f286347a5f..6e6f3c51b7be 100644 --- a/src/audio/src/src_ipc4.c +++ b/src/audio/src/src_ipc4.c @@ -246,3 +246,67 @@ __cold int src_init(struct processing_module *mod) return 0; } +/* Called after src_init() and setup_stages callback is set. + * Allocate filter stages and delay lines at init time. + */ +int src_init_stages(struct processing_module *mod) +{ + struct comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + int ret; + + ret = cd->setup_stages(mod); + if (ret < 0) + return ret; + + /* For DP modules, dev->period is not yet set at init time (it's + * computed in src_set_params at prepare). Derive it here from the + * IPC config's output buffer size so that delay line allocation + * uses correct buffer sizes. + */ + if (dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP && !dev->frames) { + uint32_t frame_bytes = cd->channels_count * cd->sample_container_bytes; + + if (frame_bytes && cd->sink_rate) { + dev->period = 1000000ULL * + (cd->ipc_config.base.obs / frame_bytes) / + cd->sink_rate; + dev->period /= LL_TIMER_PERIOD_US; + dev->period *= LL_TIMER_PERIOD_US; + component_set_nearest_period_frames(dev, cd->sink_rate); + } + } + + return src_allocate_delay_lines(mod); +} + +/* At prepare time just verify rates and set downstream params */ +int src_prepare_do(struct processing_module *mod, + struct sof_source *source, struct sof_sink *sink) +{ + struct comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + int ret; + + if (cd->source_rate != cd->ipc_config.base.audio_fmt.sampling_frequency || + cd->sink_rate != cd->ipc_config.sink_rate) { + comp_err(mod->dev, "rate mismatch: source %u/%u sink %u/%u", + cd->source_rate, + cd->ipc_config.base.audio_fmt.sampling_frequency, + cd->sink_rate, cd->ipc_config.sink_rate); + return -EINVAL; + } + + ret = src_set_params(mod, sink); + if (ret < 0) { + comp_err(mod->dev, "set params failed."); + return ret; + } + + /* Update frame counts with final dev->frames from src_set_params */ + cd->source_frames = dev->frames * cd->source_rate / cd->sink_rate; + cd->sink_frames = dev->frames; + + return src_prepare_general(mod, source, sink); +} + diff --git a/src/audio/src/src_lite.c b/src/audio/src/src_lite.c index 9d5593ff34ca..89d205fc7b85 100644 --- a/src/audio/src/src_lite.c +++ b/src/audio/src/src_lite.c @@ -14,24 +14,16 @@ LOG_MODULE_REGISTER(src_lite, CONFIG_SOF_LOG_LEVEL); -/* - * This function is 100% identical to src_prepare(), but it's - * assigning different coefficient arrays because it's including - * different headers. +/* Set rate table pointers, compute rate indices, and copy filter stages. + * Must be in src_lite.c because src_table1/2, src_in_fs, etc. come from + * the coefficient headers included by this file. */ -static int src_lite_prepare(struct processing_module *mod, - struct sof_source **sources, int num_of_sources, - struct sof_sink **sinks, int num_of_sinks) +static int src_lite_setup_stages(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); struct src_param *a = &cd->param; int ret; - comp_info(mod->dev, "entry"); - - if (num_of_sources != 1 || num_of_sinks != 1) - return -EINVAL; - a->in_fs = src_in_fs; a->out_fs = src_out_fs; a->num_in_fs = NUM_IN_FS; @@ -43,21 +35,42 @@ static int src_lite_prepare(struct processing_module *mod, if (ret < 0) return ret; - ret = src_allocate_copy_stages(mod, a, - src_table1[a->idx_out][a->idx_in], - src_table2[a->idx_out][a->idx_in]); - if (ret < 0) - return ret; + return src_allocate_copy_stages(mod, a, + src_table1[a->idx_out][a->idx_in], + src_table2[a->idx_out][a->idx_in]); +} + +static int src_lite_do_init(struct processing_module *mod) +{ + struct comp_data *cd; + int ret; - ret = src_params_general(mod, sources[0], sinks[0]); + ret = src_init(mod); if (ret < 0) return ret; - return src_prepare_general(mod, sources[0], sinks[0]); + cd = module_get_private_data(mod); + cd->setup_stages = src_lite_setup_stages; + + return src_init_stages(mod); +} + +static int src_lite_prepare(struct processing_module *mod, + struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) +{ + comp_info(mod->dev, "entry"); + + if (num_of_sources != 1 || num_of_sinks != 1) + return -EINVAL; + + src_get_source_sink_params(mod->dev, sources[0], sinks[0]); + + return src_prepare_do(mod, sources[0], sinks[0]); } const struct module_interface src_lite_interface = { - .init = src_init, + .init = src_lite_do_init, .prepare = src_lite_prepare, .process = src_process, .is_ready_to_process = src_is_ready_to_process, diff --git a/src/include/sof/audio/component.h b/src/include/sof/audio/component.h index 7fc9feb53736..814d4f6fab83 100644 --- a/src/include/sof/audio/component.h +++ b/src/include/sof/audio/component.h @@ -584,6 +584,7 @@ struct vregion; struct mod_alloc_ctx { struct k_heap *heap; struct vregion *vreg; + int vregion_type; /* enum vregion_mem_type */ }; /** diff --git a/src/include/sof/lib/vregion.h b/src/include/sof/lib/vregion.h index 612443f5bc48..a45f696a5c0e 100644 --- a/src/include/sof/lib/vregion.h +++ b/src/include/sof/lib/vregion.h @@ -22,6 +22,7 @@ struct vregion; * - lifetime: allocation that cannot be freed i.e. init data, pipeline data. */ enum vregion_mem_type { + VREGION_MEM_TYPE_INDIFFERENT, /* lifetime before interim init, interim after */ VREGION_MEM_TYPE_INTERIM, /* interim allocation that can be freed */ VREGION_MEM_TYPE_LIFETIME, /* lifetime allocation */ }; diff --git a/src/lib/objpool.c b/src/lib/objpool.c index 6925e6f7070a..989cace258c0 100644 --- a/src/lib/objpool.c +++ b/src/lib/objpool.c @@ -44,10 +44,10 @@ static int objpool_add(struct objpool_head *head, unsigned int n, size_t size, u pobjpool = sof_heap_alloc(head->heap, flags, aligned_size + sizeof(*pobjpool), 0); else if (flags & SOF_MEM_FLAG_COHERENT) - pobjpool = vregion_alloc_coherent(head->vreg, VREGION_MEM_TYPE_INTERIM, + pobjpool = vregion_alloc_coherent(head->vreg, VREGION_MEM_TYPE_INDIFFERENT, aligned_size + sizeof(*pobjpool)); else - pobjpool = vregion_alloc(head->vreg, VREGION_MEM_TYPE_INTERIM, + pobjpool = vregion_alloc(head->vreg, VREGION_MEM_TYPE_INDIFFERENT, aligned_size + sizeof(*pobjpool)); if (!pobjpool) diff --git a/src/schedule/zephyr_dp_schedule_application.c b/src/schedule/zephyr_dp_schedule_application.c index 06f2d7b46b12..e38bed8ff6a8 100644 --- a/src/schedule/zephyr_dp_schedule_application.c +++ b/src/schedule/zephyr_dp_schedule_application.c @@ -22,6 +22,7 @@ #include #include +#include #include "zephyr_dp_schedule.h" @@ -410,6 +411,15 @@ void scheduler_dp_internal_free(struct task *task) mod_free(pdata->mod, container_of(task, struct scheduler_dp_task_memory, task)); } +static void scheduler_dp_thread_name_set(k_tid_t thread_id, struct processing_module *mod) +{ + char name[CONFIG_THREAD_MAX_NAME_LEN]; + + snprintf(name, sizeof(name), "DP comp id %#x", mod->dev->ipc_config.id); + + k_thread_name_set(thread_id, name); +} + /* Called only in IPC context */ int scheduler_dp_task_init(struct task **task, const struct sof_uuid_entry *uid, const struct task_ops *ops, struct processing_module *mod, @@ -493,7 +503,7 @@ int scheduler_dp_task_init(struct task **task, const struct sof_uuid_entry *uid, pdata->thread_id = k_thread_create(pdata->thread, p_stack, stack_size, dp_thread_fn, ptask, NULL, NULL, CONFIG_DP_THREAD_PRIORITY, ptask->flags, K_FOREVER); - + scheduler_dp_thread_name_set(pdata->thread_id, mod); #ifdef CONFIG_SCHED_CPU_MASK /* pin the thread to specific core */ ret = k_thread_cpu_pin(pdata->thread_id, core); diff --git a/tools/topology/topology2/cavs-nocodec.conf b/tools/topology/topology2/cavs-nocodec.conf index fea906f98741..320a8e8ff02c 100644 --- a/tools/topology/topology2/cavs-nocodec.conf +++ b/tools/topology/topology2/cavs-nocodec.conf @@ -698,9 +698,11 @@ IncludeByKey.PASSTHROUGH { IncludeByKey.SRC_DOMAIN { "DP" { core_id $DP_SRC_CORE_ID - domain_id 123 - stack_bytes_requirement 4096 - heap_bytes_requirement 8192 + domain_id 0 + stack_bytes_requirement 2048 + interim_heap_bytes_requirement "$[(24 * 1024)]" + lifetime_heap_bytes_requirement 4096 + shared_bytes_requirement 0 } } } @@ -1382,9 +1384,11 @@ IncludeByKey.PASSTHROUGH { IncludeByKey.SRC_DOMAIN { "DP" { core_id $DP_SRC_CORE_ID - domain_id 123 - stack_bytes_requirement 4096 - heap_bytes_requirement 8192 + domain_id 0 + stack_bytes_requirement 2048 + interim_heap_bytes_requirement "$[(24 * 1024)]" + lifetime_heap_bytes_requirement 4096 + shared_bytes_requirement 0 } } diff --git a/tools/topology/topology2/include/components/widget-common.conf b/tools/topology/topology2/include/components/widget-common.conf index 43ac5691c778..b6aef0ce77af 100644 --- a/tools/topology/topology2/include/components/widget-common.conf +++ b/tools/topology/topology2/include/components/widget-common.conf @@ -176,6 +176,6 @@ DefineAttribute."shared_bytes_requirement" { # These default values are here until we have measured module specific numbers stack_bytes_requirement 8192 -interim_heap_bytes_requirement 4096 +interim_heap_bytes_requirement 8192 lifetime_heap_bytes_requirement 16384 shared_bytes_requirement 4096 \ No newline at end of file diff --git a/zephyr/Kconfig b/zephyr/Kconfig index f1d1896c4234..555609a81dcc 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -61,6 +61,15 @@ config SOF_ZEPHYR_HEAP_SIZE NOTE: Keep in mind that the heap size should not be greater than the physical memory size of the system defined in DT (and this includes baseFW text/data). +config SOF_USERSPACE_DP_DEFAULT_HEAP_SIZE + int "Default heap size for DP userspace threads" + default 20480 + help + Defines the default heap size for userspace DP processing + threads. The value can be overridden with IPC module init + ext_init module payload. The default is derived from what is + required for SRC module to produce all supported conversions. + config SOF_USERSPACE_USE_SHARED_HEAP bool "Use shared heap for SOF userspace modules" depends on USERSPACE @@ -205,6 +214,15 @@ config ZEPHYR_DP_SCHEDULER DP modules can be located in dieffrent cores than LL pipeline modules, may have different tick (i.e. 300ms for speech reccognition, etc.) +config ZEPHYR_DP_SCHEDULER_MIN_STACK_SIZE + int "Minimum stack size for DP processing thread" + default 512 + help + Defines the minimum stack size allowed for DP processing + threads despite what is requested in the module init IPC + ext_init payload. If the stack size requested in the IPC is + smaller than this, then the value defined here takes over. + config CROSS_CORE_STREAM bool "Enable cross-core connected pipelines" default y if IPC_MAJOR_4 diff --git a/zephyr/lib/fast-get.c b/zephyr/lib/fast-get.c index 9c502284f082..01441808329a 100644 --- a/zephyr/lib/fast-get.c +++ b/zephyr/lib/fast-get.c @@ -194,8 +194,8 @@ const void *fast_get(struct mod_alloc_ctx *alloc, const void *dram_ptr, size_t s } if (alloc && alloc->vreg && size <= FAST_GET_MAX_COPY_SIZE) - /* A userspace allocation, that won't be shared */ - ret = vregion_alloc_align(alloc->vreg, VREGION_MEM_TYPE_INTERIM, alloc_size, + /* Use mem type set by caller (mod_fast_get sets per module state) */ + ret = vregion_alloc_align(alloc->vreg, alloc->vregion_type, alloc_size, alloc_align); else ret = sof_heap_alloc(heap, alloc_flags, alloc_size, alloc_align); diff --git a/zephyr/lib/vregion.c b/zephyr/lib/vregion.c index 84af0d0645e6..fdfb3bf218b8 100644 --- a/zephyr/lib/vregion.c +++ b/zephyr/lib/vregion.c @@ -87,8 +87,9 @@ struct vregion { struct k_mutex lock; /* protect vregion heaps and use-count */ unsigned int use_count; - /* interim heap */ + /* interim heap - created lazily on first interim allocation */ struct interim_heap interim; /* interim heap */ + bool interim_initialized; /* true after k_heap_init for interim */ /* lifetime heap */ struct vlinear_heap lifetime; /* lifetime linear heap */ @@ -118,13 +119,13 @@ struct vregion *vregion_create(size_t lifetime_size, size_t interim_size) } /* - * Align up lifetime sizes and interim sizes to nearest page, the - * vregion structure is stored in lifetime area so account for its size too. + * Sum lifetime and interim together into one contiguous buffer. + * The vregion struct is stored at the start, followed by lifetime + * allocations growing upward. The interim k_heap is created lazily + * from the remaining space on the first interim allocation. */ - lifetime_size += sizeof(*vr); - lifetime_size = ALIGN_UP(lifetime_size, CONFIG_MM_DRV_PAGE_SIZE); - interim_size = ALIGN_UP(interim_size, CONFIG_MM_DRV_PAGE_SIZE); - total_size = lifetime_size + interim_size; + total_size = lifetime_size + interim_size + sizeof(*vr); + total_size = ALIGN_UP(total_size, CONFIG_MM_DRV_PAGE_SIZE); /* allocate pages for vregion */ pages = total_size / CONFIG_MM_DRV_PAGE_SIZE; @@ -132,27 +133,21 @@ struct vregion *vregion_create(size_t lifetime_size, size_t interim_size) if (!vregion_base) return NULL; - /* init vregion - place it at the start of the lifetime region */ - vr = (struct vregion *)(vregion_base + interim_size); + /* init vregion - place it at the start of the buffer */ + vr = (struct vregion *)vregion_base; vr->base = vregion_base; vr->size = total_size; vr->pages = pages; - /* set partition sizes */ - vr->interim.heap.heap.init_bytes = interim_size; - vr->lifetime.size = lifetime_size; - - /* set base addresses for partitions */ - vr->interim.heap.heap.init_mem = vr->base; - vr->lifetime.base = vr->base + interim_size; - - /* set alloc ptr addresses for lifetime linear partitions */ - vr->lifetime.ptr = vr->lifetime.base + - ALIGN_UP(sizeof(*vr), CONFIG_DCACHE_LINE_SIZE); /* skip vregion struct */ + /* lifetime linear allocator starts right after the vregion struct */ + vr->lifetime.base = vregion_base; + vr->lifetime.size = total_size; + vr->lifetime.ptr = vregion_base + + ALIGN_UP(sizeof(*vr), CONFIG_DCACHE_LINE_SIZE); vr->lifetime.used = ALIGN_UP(sizeof(*vr), CONFIG_DCACHE_LINE_SIZE); - /* init interim heaps */ - k_heap_init(&vr->interim.heap, vr->interim.heap.heap.init_mem, interim_size); + /* interim heap is not initialized yet - will be created lazily */ + vr->interim_initialized = false; k_mutex_init(&vr->lock); /* The creator is the first user */ @@ -161,8 +156,6 @@ struct vregion *vregion_create(size_t lifetime_size, size_t interim_size) /* log the new vregion */ LOG_INF("new at base %p size %#zx pages %u struct embedded at %p", (void *)vr->base, total_size, pages, (void *)vr); - LOG_DBG(" interim size %#zx at %p", interim_size, (void *)vr->interim.heap.heap.init_mem); - LOG_DBG(" lifetime size %#zx at %p", lifetime_size, (void *)vr->lifetime.base); return vr; } @@ -209,19 +202,53 @@ struct vregion *vregion_put(struct vregion *vr) return NULL; } +/** + * @brief Initialize the interim heap from remaining vregion space. + * + * Called on first interim allocation. Creates the k_heap from all buffer + * space not yet consumed by lifetime allocations. + * + * @param[in] vr Pointer to the virtual region instance. + */ +static void interim_heap_init(struct vregion *vr) +{ + uint8_t *interim_base; + size_t interim_size; + + /* interim heap starts right after current lifetime pointer, page-aligned */ + interim_base = UINT_TO_POINTER(ALIGN_UP(POINTER_TO_UINT(vr->lifetime.ptr), + CONFIG_MM_DRV_PAGE_SIZE)); + interim_size = (vr->base + vr->size) - interim_base; + + LOG_INF("creating interim heap: lifetime used %zu, interim available %zu at %p", + vr->lifetime.used, interim_size, (void *)interim_base); + + /* cap lifetime so no more lifetime allocs can grow into interim */ + vr->lifetime.size = interim_base - vr->base; + + vr->interim.heap.heap.init_mem = interim_base; + vr->interim.heap.heap.init_bytes = interim_size; + k_heap_init(&vr->interim.heap, interim_base, interim_size); + vr->interim_initialized = true; +} + /** * @brief Allocate memory with alignment from the virtual region dynamic heap. * + * @param[in] vr Pointer to the virtual region instance. * @param[in] heap Pointer to the heap to use. * @param[in] size Size of the allocation. * @param[in] align Alignment of the allocation. * @return void* Pointer to the allocated memory, or NULL on failure. */ -static void *interim_alloc(struct interim_heap *heap, +static void *interim_alloc(struct vregion *vr, struct interim_heap *heap, size_t size, size_t align) { void *ptr; + if (!vr->interim_initialized) + interim_heap_init(vr); + ptr = k_heap_aligned_alloc(&heap->heap, align, size, K_NO_WAIT); if (!ptr) LOG_WRN("interim alloc failed for %d bytes align %d", @@ -244,19 +271,29 @@ static void interim_free(struct interim_heap *heap, void *ptr) /** * @brief Allocate memory from the virtual region lifetime allocator. * + * If the interim heap has already been created (i.e., an interim allocation + * was made), log a warning and fall back to interim allocation. + * + * @param[in] vr Pointer to the virtual region instance. * @param[in] heap Pointer to the linear heap to use. * @param[in] size Size of the allocation. * @param[in] align Alignment of the allocation. * * @return void* Pointer to the allocated memory, or NULL on failure. */ -static void *lifetime_alloc(struct vlinear_heap *heap, +static void *lifetime_alloc(struct vregion *vr, struct vlinear_heap *heap, size_t size, size_t align) { void *ptr; uint8_t *aligned_ptr; size_t heap_obj_size; + /* If interim heap already exists, lifetime partition is sealed */ + if (vr->interim_initialized) { + LOG_WRN("lifetime alloc after interim created, using interim for %zu bytes", size); + return interim_alloc(vr, &vr->interim, size, align); + } + /* align heap pointer to alignment requested */ aligned_ptr = UINT_TO_POINTER(ALIGN_UP(POINTER_TO_UINT(heap->ptr), align)); @@ -312,14 +349,15 @@ void vregion_free(struct vregion *vr, void *ptr) if (sys_cache_is_ptr_uncached(ptr)) ptr = sys_cache_cached_ptr_get(ptr); - if (ptr >= (void *)vr->interim.heap.heap.init_mem && + /* Check if pointer is in interim heap (if initialized) */ + if (vr->interim_initialized && + ptr >= (void *)vr->interim.heap.heap.init_mem && ptr < (void *)((uint8_t *)vr->interim.heap.heap.init_mem + vr->interim.heap.heap.init_bytes)) - /* pointer is in interim heap */ interim_free(&vr->interim, ptr); else if (ptr >= (void *)vr->lifetime.base && ptr < (void *)(vr->lifetime.base + vr->lifetime.size)) - /* pointer is in lifetime heap */ + /* pointer is in lifetime area - no-op free */ lifetime_free(&vr->lifetime, ptr); else LOG_ERR("error: vregion free invalid pointer %p", ptr); @@ -353,10 +391,16 @@ void *vregion_alloc_align(struct vregion *vr, enum vregion_mem_type type, switch (type) { case VREGION_MEM_TYPE_INTERIM: - p = interim_alloc(&vr->interim, size, alignment); + p = interim_alloc(vr, &vr->interim, size, alignment); break; case VREGION_MEM_TYPE_LIFETIME: - p = lifetime_alloc(&vr->lifetime, size, alignment); + p = lifetime_alloc(vr, &vr->lifetime, size, alignment); + break; + case VREGION_MEM_TYPE_INDIFFERENT: + if (vr->interim_initialized) + p = interim_alloc(vr, &vr->interim, size, alignment); + else + p = lifetime_alloc(vr, &vr->lifetime, size, alignment); break; default: LOG_ERR("error: invalid memory type %d", type);