From 783d99cacf2025127d8d297f7527725e83295e73 Mon Sep 17 00:00:00 2001 From: Jun Lai Date: Thu, 19 Mar 2026 11:26:13 +0800 Subject: [PATCH] dax: sharing one instance across multiple dax adapters DAX adapters may be created across multiple pipelines simultaneously, but only one instance can be active at any given time. This approach prevents memory exhaustion issues that would arise if multiple DAX adapters were created concurrently. Signed-off-by: Jun Lai --- src/audio/module_adapter/module/dolby/dax.c | 195 +++++++++++++++++--- third_party/include/dax_inf.h | 10 + 2 files changed, 180 insertions(+), 25 deletions(-) diff --git a/src/audio/module_adapter/module/dolby/dax.c b/src/audio/module_adapter/module/dolby/dax.c index 0261d3767b3c..6d7912d33aaa 100644 --- a/src/audio/module_adapter/module/dolby/dax.c +++ b/src/audio/module_adapter/module/dolby/dax.c @@ -38,9 +38,26 @@ SOF_DEFINE_REG_UUID(dolby_dax_audio_processing); #define DAX_ENUM_PROFILE_CONTROL_ID 0 #define DAX_ENUM_DEVICE_CONTROL_ID 1 +#define DAX_OWNER_ID_INVALID 0 + +struct dax_shared_resource { + void *instance; + struct dax_buffer persist_buffer; + struct dax_buffer scratch_buffer; + atomic_t owner_id_counter; + atomic_t owner; + atomic_t force_owner; /* the owner should obtain resource immediately */ + atomic_t ref_count; + atomic_t initialized; +}; + +static struct dax_shared_resource shared_resource; + struct dax_adapter_data { struct sof_dax dax_ctx; atomic_t proc_flags; + int32_t owner_id; + int32_t is_registered; }; enum dax_flag_opt_mode { @@ -246,9 +263,19 @@ static void destroy_instance(struct processing_module *mod) struct dax_adapter_data *adapter_data = module_get_private_data(mod); struct sof_dax *dax_ctx = &adapter_data->dax_ctx; - dax_free(dax_ctx); /* free internal dax instance in dax_ctx */ - dax_buffer_release(mod, &dax_ctx->persist_buffer); - dax_buffer_release(mod, &dax_ctx->scratch_buffer); + if (dax_ctx) { + dax_ctx->p_dax = shared_resource.instance; + dax_free(dax_ctx); /* free internal dax instance in dax_ctx if it is valid */ + rfree(shared_resource.persist_buffer.addr); + rfree(shared_resource.scratch_buffer.addr); + memset(&shared_resource.persist_buffer, 0, sizeof(shared_resource.persist_buffer)); + memset(&shared_resource.scratch_buffer, 0, sizeof(shared_resource.scratch_buffer)); + memset(&dax_ctx->persist_buffer, 0, sizeof(dax_ctx->persist_buffer)); + memset(&dax_ctx->scratch_buffer, 0, sizeof(dax_ctx->scratch_buffer)); + shared_resource.instance = NULL; + dax_ctx->p_dax = NULL; + comp_info(mod->dev, "freed instance"); + } } static int establish_instance(struct processing_module *mod) @@ -261,25 +288,35 @@ static int establish_instance(struct processing_module *mod) uint32_t scratch_sz; persist_sz = dax_query_persist_memory(dax_ctx); - if (dax_buffer_alloc(mod, &dax_ctx->persist_buffer, persist_sz) != 0) { + shared_resource.persist_buffer.addr = rballoc(SOF_MEM_FLAG_LARGE_BUFFER, persist_sz); + if (!shared_resource.persist_buffer.addr) { comp_err(dev, "allocate %u bytes failed for persist", persist_sz); ret = -ENOMEM; goto err; } scratch_sz = dax_query_scratch_memory(dax_ctx); - if (dax_buffer_alloc(mod, &dax_ctx->scratch_buffer, scratch_sz) != 0) { + shared_resource.scratch_buffer.addr = rballoc(SOF_MEM_FLAG_LARGE_BUFFER, scratch_sz); + if (!shared_resource.scratch_buffer.addr) { comp_err(dev, "allocate %u bytes failed for scratch", scratch_sz); ret = -ENOMEM; goto err; } + + shared_resource.persist_buffer.size = persist_sz; + shared_resource.scratch_buffer.size = scratch_sz; + dax_ctx->persist_buffer = shared_resource.persist_buffer; + dax_ctx->scratch_buffer = shared_resource.scratch_buffer; ret = dax_init(dax_ctx); if (ret != 0) { comp_err(dev, "dax instance initialization failed, ret %d", ret); goto err; } + shared_resource.instance = dax_ctx->p_dax; - /* set DAX_ENABLE_MASK bit to trigger the fully update of kcontrol values */ - flag_process(adapter_data, DAX_ENABLE_MASK, DAX_FLAG_SET); + /* reset dax_ctx here because acquire_ownership is only the way to get shared instance */ + dax_ctx->p_dax = NULL; + memset(&dax_ctx->persist_buffer, 0, sizeof(dax_ctx->persist_buffer)); + memset(&dax_ctx->scratch_buffer, 0, sizeof(dax_ctx->scratch_buffer)); comp_info(dev, "allocated: persist %u, scratch %u. version: %s", persist_sz, scratch_sz, dax_get_version()); @@ -290,6 +327,82 @@ static int establish_instance(struct processing_module *mod) return ret; } +static bool is_instance_owned(struct processing_module *mod) +{ + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + + return atomic_read(&shared_resource.owner) == adapter_data->owner_id; +} + +static void release_ownership(struct processing_module *mod) +{ + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + + atomic_set(&shared_resource.owner, DAX_OWNER_ID_INVALID); + dax_ctx->p_dax = NULL; + memset(&dax_ctx->persist_buffer, 0, sizeof(dax_ctx->persist_buffer)); + memset(&dax_ctx->scratch_buffer, 0, sizeof(dax_ctx->scratch_buffer)); + comp_info(mod->dev, "unbinded DAX instance from owner %d", adapter_data->owner_id); +} + +static void set_ownership(struct processing_module *mod, int32_t owner_id) +{ + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + + atomic_set(&shared_resource.owner, owner_id); + dax_ctx->p_dax = shared_resource.instance; + dax_ctx->persist_buffer = shared_resource.persist_buffer; + dax_ctx->scratch_buffer = shared_resource.scratch_buffer; + + /* reset instance buffer data */ + dax_set_enable(0, dax_ctx); + + /* set DAX_ENABLE_MASK bit to trigger the fully update of kcontrol values */ + flag_process(adapter_data, DAX_ENABLE_MASK, DAX_FLAG_SET); + + comp_info(mod->dev, "binded DAX instance to owner %d", adapter_data->owner_id); +} + +/* can only be called within sof_dax_process */ +static int acquire_ownership(struct processing_module *mod) +{ + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + int32_t force_owner; + + if (atomic_read(&shared_resource.initialized) == 0) + return -EINVAL; + + force_owner = atomic_read(&shared_resource.force_owner); + if (force_owner != DAX_OWNER_ID_INVALID && force_owner != adapter_data->owner_id) { + /* release current ownership cause force_owner has more priority */ + if (is_instance_owned(mod)) + release_ownership(mod); + return 0; + } + + /* transfer owner safely */ + if (atomic_read(&shared_resource.owner) == DAX_OWNER_ID_INVALID) { + set_ownership(mod, adapter_data->owner_id); + if (force_owner == adapter_data->owner_id) + atomic_set(&shared_resource.force_owner, DAX_OWNER_ID_INVALID); + return 0; + } + + /* highest priority for speaker */ + if (dax_ctx->out_device == DAX_AUDIO_DEVICE_OUT_SPEAKER) { + atomic_set(&shared_resource.force_owner, adapter_data->owner_id); + return 0; + } + + dax_ctx->p_dax = NULL; + memset(&dax_ctx->persist_buffer, 0, sizeof(dax_ctx->persist_buffer)); + memset(&dax_ctx->scratch_buffer, 0, sizeof(dax_ctx->scratch_buffer)); + return -EBUSY; +} + static int set_tuning_file(struct processing_module *mod, void *value, uint32_t size) { int ret = 0; @@ -313,17 +426,14 @@ static int set_tuning_file(struct processing_module *mod, void *value, uint32_t static int set_enable(struct processing_module *mod, int32_t enable) { - int ret = 0; + int ret; struct dax_adapter_data *adapter_data = module_get_private_data(mod); struct sof_dax *dax_ctx = &adapter_data->dax_ctx; - if (enable) { - ret = dax_set_enable(1, dax_ctx); - dax_ctx->enable = (ret == 0 ? 1 : 0); - } else { - dax_ctx->enable = 0; - dax_set_enable(0, dax_ctx); - } + if (!is_instance_owned(mod)) + return 0; + + ret = dax_set_enable(enable, dax_ctx); comp_info(mod->dev, "set dax enable %d, ret %d", enable, ret); return ret; @@ -336,7 +446,7 @@ static int set_volume(struct processing_module *mod, int32_t abs_volume) struct sof_dax *dax_ctx = &adapter_data->dax_ctx; dax_ctx->volume = abs_volume; - if (!dax_ctx->enable) + if (!dax_ctx->enable || !is_instance_owned(mod)) return 0; ret = dax_set_volume(abs_volume, dax_ctx); @@ -351,6 +461,9 @@ static int set_device(struct processing_module *mod, int32_t out_device) struct sof_dax *dax_ctx = &adapter_data->dax_ctx; dax_ctx->out_device = out_device; + if (!dax_ctx->enable || !is_instance_owned(mod)) + return 0; + ret = dax_set_device(out_device, dax_ctx); comp_info(mod->dev, "set device %d, ret %d", out_device, ret); @@ -364,6 +477,9 @@ static int set_crosstalk_cancellation_enable(struct processing_module *mod, int3 struct sof_dax *dax_ctx = &adapter_data->dax_ctx; dax_ctx->ctc_enable = enable; + if (!dax_ctx->enable || !is_instance_owned(mod)) + return 0; + ret = dax_set_ctc_enable(enable, dax_ctx); comp_info(mod->dev, "set ctc enable %d, ret %d", enable, ret); @@ -382,7 +498,7 @@ static int set_profile(struct processing_module *mod, int32_t profile_id) void *params; dax_ctx->profile = profile_id; - if (!dax_ctx->enable) + if (!dax_ctx->enable || !is_instance_owned(mod)) return 0; params = dax_find_params(DAX_PARAM_ID_PROFILE, profile_id, ¶ms_sz, dax_ctx); @@ -403,7 +519,7 @@ static int set_tuning_device(struct processing_module *mod, int32_t tuning_devic void *params; dax_ctx->tuning_device = tuning_device; - if (!dax_ctx->enable) + if (!dax_ctx->enable || !is_instance_owned(mod)) return 0; params = dax_find_params(DAX_PARAM_ID_TUNING_DEVICE, tuning_device, ¶ms_sz, dax_ctx); @@ -424,7 +540,7 @@ static int set_content_processing_enable(struct processing_module *mod, int32_t void *params; dax_ctx->content_processing_enable = enable; - if (!dax_ctx->enable) + if (!dax_ctx->enable || !is_instance_owned(mod)) return 0; params = dax_find_params(DAX_PARAM_ID_CP_ENABLE, enable, ¶ms_sz, dax_ctx); @@ -542,6 +658,9 @@ static void check_and_update_settings(struct processing_module *mod) struct dax_adapter_data *adapter_data = module_get_private_data(mod); struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + if (!is_instance_owned(mod)) + return; + if (flag_process(adapter_data, DAX_ENABLE_MASK, DAX_FLAG_READ_AND_CLEAR)) { set_enable(mod, dax_ctx->enable); if (dax_ctx->enable) { @@ -580,13 +699,16 @@ static int sof_dax_reset(struct processing_module *mod) struct dax_adapter_data *adapter_data = module_get_private_data(mod); struct sof_dax *dax_ctx; - /* dax instance will be established on prepare(), and destroyed on reset() */ if (adapter_data) { dax_ctx = &adapter_data->dax_ctx; if (flag_process(adapter_data, DAX_PROCESSING_MASK, DAX_FLAG_READ)) { flag_process(adapter_data, DAX_RESET_MASK, DAX_FLAG_SET); } else { - destroy_instance(mod); + if (adapter_data->is_registered == 1) { + atomic_sub(&shared_resource.ref_count, 1); + release_ownership(mod); + adapter_data->is_registered = 0; + } dax_buffer_release(mod, &dax_ctx->input_buffer); dax_buffer_release(mod, &dax_ctx->output_buffer); } @@ -606,6 +728,12 @@ static int sof_dax_free(struct processing_module *mod) flag_process(adapter_data, DAX_FREE_MASK, DAX_FLAG_SET); } else { sof_dax_reset(mod); + + if (atomic_read(&shared_resource.ref_count) == 0) { + destroy_instance(mod); + atomic_set(&shared_resource.initialized, 0); + } + dax_buffer_release(mod, &dax_ctx->tuning_file_buffer); mod_data_blob_handler_free(mod, dax_ctx->blob_handler); dax_ctx->blob_handler = NULL; @@ -661,6 +789,12 @@ static int sof_dax_init(struct processing_module *mod) return -ENOMEM; } + if (atomic_read(&shared_resource.owner_id_counter) == INT32_MAX) + atomic_set(&shared_resource.owner_id_counter, 0); + atomic_add(&shared_resource.owner_id_counter, 1); + adapter_data->owner_id = atomic_read(&shared_resource.owner_id_counter); + comp_info(dev, "initialized, owner id %d", adapter_data->owner_id); + return 0; } @@ -757,10 +891,20 @@ static int sof_dax_prepare(struct processing_module *mod, struct sof_source **so if (ret != 0) return ret; - /* dax instance will be established on prepare(), and destroyed on reset() */ - ret = establish_instance(mod); - if (ret != 0) - return ret; + if (adapter_data->is_registered == 0) { + adapter_data->is_registered = 1; + atomic_add(&shared_resource.ref_count, 1); + } + + if (atomic_read(&shared_resource.initialized) == 0) { + atomic_set(&shared_resource.initialized, 1); + ret = establish_instance(mod); + if (ret) { + /* rollback initialization state */ + atomic_set(&shared_resource.initialized, 0); + return ret; + } + } dax_ctx->sof_period_bytes = dev->frames * dax_ctx->output_media_format.num_channels * @@ -832,6 +976,7 @@ static int sof_dax_process(struct processing_module *mod, struct sof_source **so dax_buffer_produce(dax_input_buffer, consumed_bytes); source_release_data(source, consumed_bytes); + acquire_ownership(mod); check_and_update_settings(mod); /* internal input buffer -> internal output buffer */ diff --git a/third_party/include/dax_inf.h b/third_party/include/dax_inf.h index a257cfd9e24f..def501f2c50f 100644 --- a/third_party/include/dax_inf.h +++ b/third_party/include/dax_inf.h @@ -14,6 +14,12 @@ #include #include +enum dax_out_device { + DAX_AUDIO_DEVICE_UNSUPPORTED = -1, + DAX_AUDIO_DEVICE_OUT_SPEAKER = 0, + DAX_AUDIO_DEVICE_OUT_WIRED_HEADPHONE = 1, +}; + enum dax_frame_fmt { DAX_FMT_UNSUPPORTED = -1, DAX_FMT_SHORT_16 = 4, @@ -144,6 +150,10 @@ int dax_init(struct sof_dax *dax_ctx); /** * @brief Process audio data through the DAX module * + * If DAX is disabled or the DAX instance is invalid (dax_ctx->p_dax is NULL), + * the dax_process will by default perform only copy operations, without any + * audio processing. + * * @param[in] dax_ctx Pointer to the DAX context structure * * @return Bytes of processed. negative error code on failure