diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index 320d08009105..05a9ac6b3aba 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -73,8 +73,8 @@ if(NOT CONFIG_LIBRARY) if(CONFIG_COMP_ASRC) add_subdirectory(asrc) endif() - if(CONFIG_COMP_CODEC_ADAPTER) - add_subdirectory(codec_adapter) + if(CONFIG_COMP_MODULE_ADAPTER) + add_subdirectory(module_adapter) endif() if(CONFIG_COMP_IGO_NR) add_subdirectory(igo_nr) diff --git a/src/audio/Kconfig b/src/audio/Kconfig index 0fa0619dc633..f84e5490ba49 100644 --- a/src/audio/Kconfig +++ b/src/audio/Kconfig @@ -333,18 +333,18 @@ config COMP_TDFB for channels selection, channel filter coefficients, and output streams mixing. -config COMP_CODEC_ADAPTER - bool "Codec adapter" +config COMP_MODULE_ADAPTER + bool "Module adapter" default n help This component is an adapter between SoF components and any external third party codecs/libraries. In order to make use of it the library itself should be statically linked with the SoF FW binary image and the codec details, such as its ID or specific methods provided in generic interface object located under - "src\include\sof\audio\codec_adapter\interfaces.h". It is possible to link several + "src\include\sof\audio\module_adapter\interfaces.h". It is possible to link several different codecs and use them in parallel. -rsource "codec_adapter/Kconfig" +rsource "module_adapter/Kconfig" config COMP_IGO_NR bool "IGO NR component" diff --git a/src/audio/codec_adapter/CMakeLists.txt b/src/audio/module_adapter/CMakeLists.txt similarity index 100% rename from src/audio/codec_adapter/CMakeLists.txt rename to src/audio/module_adapter/CMakeLists.txt diff --git a/src/audio/codec_adapter/Kconfig b/src/audio/module_adapter/Kconfig similarity index 98% rename from src/audio/codec_adapter/Kconfig rename to src/audio/module_adapter/Kconfig index 221eeaa08fa2..cf151bf34df1 100644 --- a/src/audio/codec_adapter/Kconfig +++ b/src/audio/module_adapter/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: BSD-3-Clause -menu "Codec Adapter codecs" - visible if COMP_CODEC_ADAPTER +menu "Processing modules" + visible if COMP_MODULE_ADAPTER config CADENCE_CODEC bool "Cadence codec" diff --git a/src/audio/codec_adapter/codec/cadence.c b/src/audio/module_adapter/codec/cadence.c similarity index 100% rename from src/audio/codec_adapter/codec/cadence.c rename to src/audio/module_adapter/codec/cadence.c diff --git a/src/audio/codec_adapter/codec/generic.c b/src/audio/module_adapter/codec/generic.c similarity index 99% rename from src/audio/codec_adapter/codec/generic.c rename to src/audio/module_adapter/codec/generic.c index ece2109cc109..27425615b125 100644 --- a/src/audio/codec_adapter/codec/generic.c +++ b/src/audio/module_adapter/codec/generic.c @@ -11,7 +11,7 @@ * */ -#include +#include /*****************************************************************************/ /* Local helper functions */ diff --git a/src/audio/codec_adapter/codec/passthrough.c b/src/audio/module_adapter/codec/passthrough.c similarity index 100% rename from src/audio/codec_adapter/codec/passthrough.c rename to src/audio/module_adapter/codec/passthrough.c diff --git a/src/audio/codec_adapter/codec/waves.c b/src/audio/module_adapter/codec/waves.c similarity index 99% rename from src/audio/codec_adapter/codec/waves.c rename to src/audio/module_adapter/codec/waves.c index d61f7c7c73d4..fa648ef4dae9 100644 --- a/src/audio/codec_adapter/codec/waves.c +++ b/src/audio/module_adapter/codec/waves.c @@ -4,8 +4,8 @@ // // Author: Oleksandr Strelchenko // -#include -#include +#include +#include #include #include diff --git a/src/audio/codec_adapter/codec_adapter.c b/src/audio/module_adapter/codec_adapter.c similarity index 99% rename from src/audio/codec_adapter/codec_adapter.c rename to src/audio/module_adapter/codec_adapter.c index da8f108c77d4..390df52e761d 100644 --- a/src/audio/codec_adapter/codec_adapter.c +++ b/src/audio/module_adapter/codec_adapter.c @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/audio/module_adapter/module/cadence.c b/src/audio/module_adapter/module/cadence.c new file mode 100644 index 000000000000..eab5b0d1b15e --- /dev/null +++ b/src/audio/module_adapter/module/cadence.c @@ -0,0 +1,740 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Author: Marcin Rajwa + +/* + * \file cadence.c + * \brief Cadence Codec API + * \author Marcin Rajwa + * + */ + +#include +#include +#include + +/* d8218443-5ff3-4a4c-b388-6cfe07b956aa */ +DECLARE_SOF_RT_UUID("cadence_codec", cadence_uuid, 0xd8218443, 0x5ff3, 0x4a4c, + 0xb3, 0x88, 0x6c, 0xfe, 0x07, 0xb9, 0x56, 0xaa); + +DECLARE_TR_CTX(cadence_tr, SOF_UUID(cadence_uuid), LOG_LEVEL_INFO); + +enum cadence_api_id { + CADENCE_CODEC_WRAPPER_ID = 0x01, + CADENCE_CODEC_AAC_DEC_ID = 0x02, + CADENCE_CODEC_BSAC_DEC_ID = 0x03, + CADENCE_CODEC_DAB_DEC_ID = 0x04, + CADENCE_CODEC_DRM_DEC_ID = 0x05, + CADENCE_CODEC_MP3_DEC_ID = 0x06, + CADENCE_CODEC_SBC_DEC_ID = 0x07, + CADENCE_CODEC_VORBIS_DEC_ID = 0x08, + CADENCE_CODEC_SRC_PP_ID = 0x09, +}; + +#define DEFAULT_CODEC_ID CADENCE_CODEC_WRAPPER_ID + +/*****************************************************************************/ +/* Cadence API functions array */ +/*****************************************************************************/ +static struct cadence_api cadence_api_table[] = { +#ifdef CONFIG_CADENCE_CODEC_WRAPPER + { + .id = CADENCE_CODEC_WRAPPER_ID, + .api = cadence_api_function + }, +#endif +#ifdef CONFIG_CADENCE_CODEC_AAC_DEC + { + .id = CADENCE_CODEC_AAC_DEC_ID, + .api = xa_aac_dec, + }, +#endif +#ifdef CONFIG_CADENCE_CODEC_BSAC_DEC + { + .id = CADENCE_CODEC_BSAC_DEC_ID, + .api = xa_bsac_dec, + }, +#endif +#ifdef CONFIG_CADENCE_CODEC_DAB_DEC + { + .id = CADENCE_CODEC_DAB_DEC_ID, + .api = xa_dabplus_dec, + }, +#endif +#ifdef CONFIG_CADENCE_CODEC_DRM_DEC + { + .id = CADENCE_CODEC_DRM_DEC_ID, + .api = xa_drm_dec, + }, +#endif +#ifdef CONFIG_CADENCE_CODEC_MP3_DEC + { + .id = CADENCE_CODEC_MP3_DEC_ID, + .api = xa_mp3_dec, + }, +#endif +#ifdef CONFIG_CADENCE_CODEC_SBC_DEC + { + .id = CADENCE_CODEC_SBC_DEC_ID, + .api = xa_sbc_dec, + }, +#endif +#ifdef CONFIG_CADENCE_CODEC_VORBIS_DEC + { + .id = CADENCE_CODEC_VORBIS_DEC_ID, + .api = xa_vorbis_dec, + }, +#endif +#ifdef CONFIG_CADENCE_CODEC_SRC_PP + { + .id = CADENCE_CODEC_SRC_PP_ID, + .api = xa_src_pp, + }, +#endif +}; + +static int cadence_code_get_api_id(uint32_t compress_id) +{ + /* convert compress id to SOF cadence SOF id */ + switch (compress_id) { + case SND_AUDIOCODEC_MP3: + return CADENCE_CODEC_MP3_DEC_ID; + case SND_AUDIOCODEC_AAC: + return CADENCE_CODEC_AAC_DEC_ID; + case SND_AUDIOCODEC_VORBIS: + return CADENCE_CODEC_VORBIS_DEC_ID; + default: + return -EINVAL; + } +} + +static int cadence_codec_resolve_api(struct processing_module *mod) +{ + int ret; + struct snd_codec codec_params; + struct comp_dev *dev = mod->dev; + struct cadence_codec_data *cd = module_get_private_data(mod); + uint32_t api_id = CODEC_GET_API_ID(DEFAULT_CODEC_ID); + uint32_t n_apis = ARRAY_SIZE(cadence_api_table); + int i; + + if (mod->stream_params->ext_data_length) { + ret = memcpy_s(&codec_params, mod->stream_params->ext_data_length, + (uint8_t *)mod->stream_params + sizeof(*mod->stream_params), + mod->stream_params->ext_data_length); + if (ret < 0) + return ret; + + ret = cadence_code_get_api_id(codec_params.id); + if (ret < 0) + return ret; + + api_id = ret; + } + + /* Find and assign API function */ + for (i = 0; i < n_apis; i++) { + if (cadence_api_table[i].id == api_id) { + cd->api = cadence_api_table[i].api; + break; + } + } + + /* Verify API assignment */ + if (!cd->api) { + comp_err(dev, "cadence_codec_resolve_api(): could not find API function for id %x", + api_id); + return -EINVAL; + } + + cd->api_id = api_id; + + return 0; +} + +static int cadence_codec_post_init(struct processing_module *mod) +{ + int ret; + struct comp_dev *dev = mod->dev; + struct cadence_codec_data *cd = module_get_private_data(mod); + uint32_t obj_size; + + comp_dbg(dev, "cadence_codec_post_init() start"); + + ret = cadence_codec_resolve_api(mod); + if (ret < 0) + return ret; + + /* Obtain codec name */ + API_CALL(cd, XA_API_CMD_GET_LIB_ID_STRINGS, + XA_CMD_TYPE_LIB_NAME, cd->name, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_init() error %x: failed to get lib name", + ret); + return ret; + } + /* Get codec object size */ + API_CALL(cd, XA_API_CMD_GET_API_SIZE, 0, &obj_size, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_init() error %x: failed to get lib object size", + ret); + return ret; + } + /* Allocate space for codec object */ + cd->self = rballoc(0, SOF_MEM_CAPS_RAM, obj_size); + if (!cd->self) { + comp_err(dev, "cadence_codec_init(): failed to allocate space for lib object"); + return -ENOMEM; + } + + comp_dbg(dev, "cadence_codec_post_init(): allocated %d bytes for lib object", obj_size); + + /* Set all params to their default values */ + API_CALL(cd, XA_API_CMD_INIT, XA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS, + NULL, ret); + if (ret != LIB_NO_ERROR) { + rfree(cd->self); + return ret; + } + + comp_dbg(dev, "cadence_codec_post_init() done"); + + return 0; +} + +static int cadence_codec_init(struct processing_module *mod) +{ + int ret; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + struct cadence_codec_data *cd = NULL; + + comp_dbg(dev, "cadence_codec_init() start"); + + cd = rballoc(0, SOF_MEM_CAPS_RAM, sizeof(struct cadence_codec_data)); + if (!cd) { + comp_err(dev, "cadence_codec_init(): failed to allocate memory for cadence codec data"); + return -ENOMEM; + } + + codec->private = cd; + codec->mpd.init_done = 0; + cd->self = NULL; + cd->mem_tabs = NULL; + cd->api = NULL; + cd->setup_cfg.avail = false; + + /* copy the setup config only for the first init */ + if (codec->state == MODULE_DISABLED && codec->cfg.avail) { + struct module_config *setup_cfg = &cd->setup_cfg; + + /* allocate memory for set up config */ + setup_cfg->data = rballoc(0, SOF_MEM_CAPS_RAM, codec->cfg.size); + if (!setup_cfg->data) { + comp_err(dev, "cadence_codec_init(): failed to alloc setup config"); + ret = -ENOMEM; + goto free; + } + + /* copy the setup config */ + setup_cfg->size = codec->cfg.size; + ret = memcpy_s(setup_cfg->data, setup_cfg->size, codec->cfg.data, setup_cfg->size); + if (ret) { + comp_err(dev, "cadence_codec_init(): failed to copy setup config %d", ret); + goto free; + } + setup_cfg->avail = true; + } + + comp_dbg(dev, "cadence_codec_init() done"); + + return 0; +free: + rfree(cd); + return ret; +} + +static int cadence_codec_apply_config(struct processing_module *mod) +{ + int ret = 0; + int size; + struct module_config *cfg; + void *data; + struct module_param *param; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + struct cadence_codec_data *cd = codec->private; + + comp_dbg(dev, "cadence_codec_apply_config() start"); + + cfg = &codec->cfg; + + /* use setup config if no runtime config available. This will be true during reset */ + if (!cfg->avail) + cfg = &cd->setup_cfg; + + data = cfg->data; + size = cfg->size; + + if (!cfg->avail || !size) { + comp_err(dev, "cadence_codec_apply_config() error: no config available"); + return -EIO; + } + + /* Read parameters stored in `data` - it may keep plenty of + * parameters. The `size` variable is equal to param->size * count, + * where count is number of parameters stored in `data`. + */ + while (size > 0) { + param = data; + comp_dbg(dev, "cadence_codec_apply_config() applying param %d value %d", + param->id, param->data[0]); + /* Set read parameter */ + API_CALL(cd, XA_API_CMD_SET_CONFIG_PARAM, param->id, + param->data, ret); + if (ret != LIB_NO_ERROR) { + if (LIB_IS_FATAL_ERROR(ret)) { + comp_err(dev, "cadence_codec_apply_config(): failed to apply parameter: %d value: %d error: %#x", + param->id, *(int32_t *)param->data, ret); + + return ret; + } + comp_warn(dev, "cadence_codec_apply_config(): applied parameter %d value %d with return code: %#x", + param->id, *(int32_t *)param->data, ret); + } + /* Obtain next parameter, it starts right after the preceding one */ + data = (char *)data + param->size; + size -= param->size; + } + + comp_dbg(dev, "cadence_codec_apply_config() done"); + + return 0; +} + +static int init_memory_tables(struct processing_module *mod) +{ + int ret, no_mem_tables, i, mem_type, mem_size, mem_alignment; + void *ptr, *scratch, *persistent; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + struct cadence_codec_data *cd = codec->private; + + scratch = NULL; + persistent = NULL; + + /* Calculate the size of all memory blocks required */ + API_CALL(cd, XA_API_CMD_INIT, XA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS, + NULL, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "init_memory_tables() error %x: failed to calculate memory blocks size", + ret); + return ret; + } + + /* Get number of memory tables */ + API_CALL(cd, XA_API_CMD_GET_N_MEMTABS, 0, &no_mem_tables, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "init_memory_tables() error %x: failed to get number of memory tables", + ret); + return ret; + } + + /* Initialize each memory table */ + for (i = 0; i < no_mem_tables; i++) { + /* Get type of memory - it specifies how the memory will be used */ + API_CALL(cd, XA_API_CMD_GET_MEM_INFO_TYPE, i, &mem_type, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "init_memory_tables() error %x: failed to get mem. type info of id %d out of %d", + ret, i, no_mem_tables); + goto err; + } + /* Get size of memory needed for memory allocation for this + * particular memory type. + */ + API_CALL(cd, XA_API_CMD_GET_MEM_INFO_SIZE, i, &mem_size, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "init_memory_tables() error %x: failed to get mem. size for mem. type %d", + ret, mem_type); + goto err; + } + /* Get alignment constrains */ + API_CALL(cd, XA_API_CMD_GET_MEM_INFO_ALIGNMENT, i, &mem_alignment, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "init_memory_tables() error %x: failed to get mem. alignment of mem. type %d", + ret, mem_type); + goto err; + } + /* Allocate memory for this type, taking alignment into account */ + ptr = module_allocate_memory(mod, mem_size, mem_alignment); + if (!ptr) { + comp_err(dev, "init_memory_tables() error %x: failed to allocate memory for %d", + ret, mem_type); + ret = -EINVAL; + goto err; + } + /* Finally, provide this memory for codec */ + API_CALL(cd, XA_API_CMD_SET_MEM_PTR, i, ptr, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "init_memory_tables() error %x: failed to set memory pointer for %d", + ret, mem_type); + goto err; + } + + switch ((unsigned int)mem_type) { + case XA_MEMTYPE_SCRATCH: + scratch = ptr; + break; + case XA_MEMTYPE_PERSIST: + persistent = ptr; + break; + case XA_MEMTYPE_INPUT: + codec->mpd.in_buff = ptr; + codec->mpd.in_buff_size = mem_size; + break; + case XA_MEMTYPE_OUTPUT: + codec->mpd.out_buff = ptr; + codec->mpd.out_buff_size = mem_size; + break; + default: + comp_err(dev, "init_memory_tables() error %x: unrecognized memory type!", + mem_type); + ret = -EINVAL; + goto err; + } + + comp_dbg(dev, "init_memory_tables: allocated memory of %d bytes and alignment %d for mem. type %d", + mem_size, mem_alignment, mem_type); + } + + return 0; +err: + if (scratch) + module_free_memory(mod, scratch); + if (persistent) + module_free_memory(mod, persistent); + if (codec->mpd.in_buff) + module_free_memory(mod, codec->mpd.in_buff); + if (codec->mpd.out_buff) + module_free_memory(mod, codec->mpd.out_buff); + return ret; +} + +static int cadence_codec_get_samples(struct processing_module *mod) +{ + struct cadence_codec_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + + comp_dbg(dev, "cadence_codec_get_samples() start"); + + switch (cd->api_id) { + case CADENCE_CODEC_WRAPPER_ID: + return 0; + case CADENCE_CODEC_MP3_DEC_ID: + /* MPEG-1 Layer 3 */ + return 1152; + case CADENCE_CODEC_AAC_DEC_ID: + return 1024; + default: + break; + } + + return 0; +} + +static int cadence_codec_init_process(struct processing_module *mod) +{ + int ret; + struct module_data *codec = &mod->priv; + struct cadence_codec_data *cd = codec->private; + struct comp_dev *dev = mod->dev; + + API_CALL(cd, XA_API_CMD_SET_INPUT_BYTES, 0, &codec->mpd.avail, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_init_process() error %x: failed to set size of input data", + ret); + return ret; + } + + API_CALL(cd, XA_API_CMD_INIT, XA_CMD_TYPE_INIT_PROCESS, NULL, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_init_process() error %x: failed to initialize codec", + ret); + return ret; + } + + API_CALL(cd, XA_API_CMD_INIT, XA_CMD_TYPE_INIT_DONE_QUERY, + &codec->mpd.init_done, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_init_process() error %x: failed to get lib init status", + ret); + return ret; + } + + API_CALL(cd, XA_API_CMD_GET_CURIDX_INPUT_BUF, 0, &codec->mpd.consumed, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_init_process() error %x: could not get consumed bytes", + ret); + return ret; + } + + return 0; +} + +static int cadence_codec_prepare(struct processing_module *mod) +{ + int ret = 0, mem_tabs_size; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + struct cadence_codec_data *cd = codec->private; + + comp_dbg(dev, "cadence_codec_prepare() start"); + + ret = cadence_codec_post_init(mod); + if (ret) + return ret; + + ret = cadence_codec_apply_config(mod); + if (ret) { + comp_err(dev, "cadence_codec_prepare() error %x: failed to apply config", + ret); + return ret; + } + + /* Allocate memory for the codec */ + API_CALL(cd, XA_API_CMD_GET_MEMTABS_SIZE, 0, &mem_tabs_size, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_prepare() error %x: failed to get memtabs size", + ret); + return ret; + } + + cd->mem_tabs = module_allocate_memory(mod, mem_tabs_size, 4); + if (!cd->mem_tabs) { + comp_err(dev, "cadence_codec_prepare() error: failed to allocate space for memtabs"); + return -ENOMEM; + } + + comp_dbg(dev, "cadence_codec_prepare(): allocated %d bytes for memtabs", mem_tabs_size); + + API_CALL(cd, XA_API_CMD_SET_MEMTABS_PTR, 0, cd->mem_tabs, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_prepare() error %x: failed to set memtabs", + ret); + goto free; + } + + ret = init_memory_tables(mod); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_prepare() error %x: failed to init memory tables", + ret); + goto free; + } + /* Check init done status. Note, it may happen that init_done flag will return + * false value, this is normal since some codec variants needs input in order to + * fully finish initialization. That's why at codec_adapter_copy() we call + * codec_init_process() base on result obtained below. + */ +#ifdef CONFIG_CADENCE_CODEC_WRAPPER + /* TODO: remove the "#ifdef CONFIG_CADENCE_CODEC_WRAPPER" once cadence fixes the bug + * in the init/prepare sequence. Basically below API_CALL shall return 1 for + * PCM streams and 0 for compress ones. As it turns out currently it returns 1 + * in both cases so in turn compress stream won't finish its prepare during first copy + * in codec_adapter_copy(). + */ + API_CALL(cd, XA_API_CMD_INIT, XA_CMD_TYPE_INIT_DONE_QUERY, + &codec->mpd.init_done, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_init_process() error %x: failed to get lib init status", + ret); + return ret; + } +#endif + comp_dbg(dev, "cadence_codec_prepare() done"); + return 0; +free: + module_free_memory(mod, cd->mem_tabs); + return ret; +} + +static int +cadence_codec_process(struct processing_module *mod, + struct input_stream_buffer *input_buffers, int num_input_buffers, + struct output_stream_buffer *output_buffers, int num_output_buffers) +{ + struct comp_buffer *local_buff; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + struct cadence_codec_data *cd = codec->private; + int output_bytes = cadence_codec_get_samples(mod) * + mod->stream_params->sample_container_bytes * + mod->stream_params->channels; + uint32_t remaining = input_buffers[0].size; + int ret; + + /* Proceed only if we have enough data to fill the module buffer completely */ + if (input_buffers[0].size < codec->mpd.in_buff_size) { + comp_dbg(dev, "cadence_codec_process(): not enough data to process"); + return -ENODATA; + } + + if (!codec->mpd.init_done) { + memcpy_s(codec->mpd.in_buff, codec->mpd.in_buff_size, input_buffers[0].data, + codec->mpd.in_buff_size); + codec->mpd.avail = codec->mpd.in_buff_size; + + ret = cadence_codec_init_process(mod); + if (ret) + return ret; + + remaining -= codec->mpd.consumed; + input_buffers[0].consumed = codec->mpd.consumed; + } + + /* do not proceed with processing if not enough free space left in the local buffer */ + local_buff = list_first_item(&mod->sink_buffer_list, struct comp_buffer, sink_list); + if (local_buff->stream.free < output_bytes) + return -ENOSPC; + + /* Proceed only if we have enough data to fill the module buffer completely */ + if (remaining < codec->mpd.in_buff_size) + return -ENODATA; + + memcpy_s(codec->mpd.in_buff, codec->mpd.in_buff_size, + (uint8_t *)input_buffers[0].data + input_buffers[0].consumed, + codec->mpd.in_buff_size); + codec->mpd.avail = codec->mpd.in_buff_size; + + comp_dbg(dev, "cadence_codec_process() start"); + + API_CALL(cd, XA_API_CMD_SET_INPUT_BYTES, 0, &codec->mpd.avail, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_process() error %x: failed to set size of input data", + ret); + return ret; + } + + API_CALL(cd, XA_API_CMD_EXECUTE, XA_CMD_TYPE_DO_EXECUTE, NULL, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_process() error %x: processing failed", + ret); + return ret; + } + + API_CALL(cd, XA_API_CMD_GET_OUTPUT_BYTES, 0, &codec->mpd.produced, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_process() error %x: could not get produced bytes", + ret); + return ret; + } + + API_CALL(cd, XA_API_CMD_GET_CURIDX_INPUT_BUF, 0, &codec->mpd.consumed, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_process() error %x: could not get consumed bytes", + ret); + return ret; + } + + /* update consumed with the number of samples consumed during init */ + input_buffers[0].consumed += codec->mpd.consumed; + codec->mpd.consumed = input_buffers[0].consumed; + + /* copy the produced samples into the output buffer */ + memcpy_s(output_buffers[0].data, codec->mpd.produced, codec->mpd.out_buff, + codec->mpd.produced); + output_buffers[0].size = codec->mpd.produced; + + comp_dbg(dev, "cadence_codec_process() done"); + + return 0; +} + +static int cadence_codec_reset(struct processing_module *mod) +{ + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + struct cadence_codec_data *cd = codec->private; + int ret; + + /* + * Current CADENCE API doesn't support reset of codec's runtime parameters. + * So, free all memory associated with runtime params. These will be reallocated during + * prepare. + */ + module_free_all_memory(mod); + + /* reset to default params */ + API_CALL(cd, XA_API_CMD_INIT, XA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS, NULL, ret); + if (ret != LIB_NO_ERROR) + return ret; + + codec->mpd.init_done = 0; + + rfree(cd->self); + cd->self = NULL; + + ret = cadence_codec_prepare(mod); + if (ret) { + comp_err(dev, "cadence_codec_reset() error %x: could not re-prepare codec after reset", + ret); + } + + return ret; +} + +static int cadence_codec_free(struct processing_module *mod) +{ + struct cadence_codec_data *cd = module_get_private_data(mod); + + rfree(cd->setup_cfg.data); + module_free_all_memory(mod); + rfree(cd->self); + rfree(cd); + return 0; +} + +static int +cadence_codec_set_configuration(struct processing_module *mod, uint32_t config_id, + enum module_cfg_fragment_position pos, uint32_t data_offset_size, + const uint8_t *fragment, size_t fragment_size, uint8_t *response, + size_t response_size) +{ + struct module_data *md = &mod->priv; + struct comp_dev *dev = mod->dev; + int ret; + + ret = module_set_configuration(mod, config_id, pos, data_offset_size, fragment, + fragment_size, response, response_size); + if (ret < 0) + return ret; + + /* return if more fragments are expected or if the module is not prepared */ + if ((pos != MODULE_CFG_FRAGMENT_LAST && pos != MODULE_CFG_FRAGMENT_SINGLE) || + md->state < MODULE_IDLE) + return 0; + + /* whole configuration received, apply it now */ + ret = cadence_codec_apply_config(mod); + if (ret) { + comp_err(dev, "cadence_codec_set_configuration(): error %x: runtime config apply failed", + ret); + return ret; + } + + comp_dbg(dev, "cadence_codec_set_configuration(): config applied"); + + return 0; +} + +static struct module_interface cadence_interface = { + .init = cadence_codec_init, + .prepare = cadence_codec_prepare, + .process = cadence_codec_process, + .set_configuration = cadence_codec_set_configuration, + .reset = cadence_codec_reset, + .free = cadence_codec_free +}; + +DECLARE_MODULE_ADAPTER(cadence_interface, cadence_uuid, cadence_tr); diff --git a/src/audio/module_adapter/module/dts.c b/src/audio/module_adapter/module/dts.c new file mode 100644 index 000000000000..0afb402fa1ce --- /dev/null +++ b/src/audio/module_adapter/module/dts.c @@ -0,0 +1,429 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2021 Xperi. All rights reserved. +// +// Author: Mark Barton + +#include "sof/audio/module_adapter/module/generic.h" + +#include "DtsSofInterface.h" + +/* d95fc34f-370f-4ac7-bc86-bfdc5be241e6 */ +DECLARE_SOF_RT_UUID("dts_codec", dts_uuid, 0xd95fc34f, 0x370f, 0x4ac7, + 0xbc, 0x86, 0xbf, 0xdc, 0x5b, 0xe2, 0x41, 0xe6); +DECLARE_TR_CTX(dts_tr, SOF_UUID(dts_uuid), LOG_LEVEL_INFO); + +#define MAX_EXPECTED_DTS_CONFIG_DATA_SIZE 8192 + +static void *dts_effect_allocate_codec_memory(void *mod_void, unsigned int length, + unsigned int alignment) +{ + struct processing_module *mod = mod_void; + struct comp_dev *dev = mod->dev; + void *pMem; + + comp_dbg(dev, "dts_effect_allocate_codec_memory() start"); + + pMem = module_allocate_memory(mod, (uint32_t)length, (uint32_t)alignment); + + if (pMem == NULL) + comp_err(dev, + "dts_effect_allocate_codec_memory() failed to allocate %d bytes", length); + + comp_dbg(dev, "dts_effect_allocate_codec_memory() done"); + return pMem; +} + +static int dts_effect_convert_sof_interface_result(struct comp_dev *dev, + DtsSofInterfaceResult dts_result) +{ + int ret; + + switch (dts_result) { + case DTS_SOF_INTERFACE_RESULT_SUCCESS: + ret = 0; + break; + case DTS_SOF_INTERFACE_RESULT_ERROR_NO_MEMORY: + ret = -ENOMEM; + break; + case DTS_SOF_INTERFACE_RESULT_ERROR_DTS_INTERNAL_MODULE_ERROR: + ret = -EIO; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int dts_effect_populate_buffer_configuration(struct comp_dev *dev, + DtsSofInterfaceBufferConfiguration *buffer_config) +{ + struct comp_buffer *source = list_first_item(&dev->bsource_list, struct comp_buffer, + sink_list); + const struct audio_stream *stream; + DtsSofInterfaceBufferLayout buffer_layout; + DtsSofInterfaceBufferFormat buffer_format; + comp_dbg(dev, "dts_effect_populate_buffer_configuration() start"); + + if (!source) + return -EINVAL; + + stream = &source->stream; + + switch (source->buffer_fmt) { + case SOF_IPC_BUFFER_INTERLEAVED: + buffer_layout = DTS_SOF_INTERFACE_BUFFER_LAYOUT_INTERLEAVED; + break; + case SOF_IPC_BUFFER_NONINTERLEAVED: + buffer_layout = DTS_SOF_INTERFACE_BUFFER_LAYOUT_NONINTERLEAVED; + break; + default: + return -EINVAL; + } + + switch (stream->frame_fmt) { + case SOF_IPC_FRAME_S16_LE: + buffer_format = DTS_SOF_INTERFACE_BUFFER_FORMAT_SINT16LE; + break; + case SOF_IPC_FRAME_S24_4LE: + buffer_format = DTS_SOF_INTERFACE_BUFFER_FORMAT_SINT24LE; + break; + case SOF_IPC_FRAME_S32_LE: + buffer_format = DTS_SOF_INTERFACE_BUFFER_FORMAT_SINT32LE; + break; + case SOF_IPC_FRAME_FLOAT: + buffer_format = DTS_SOF_INTERFACE_BUFFER_FORMAT_FLOAT32; + break; + default: + return -EINVAL; + } + + buffer_config->bufferLayout = buffer_layout; + buffer_config->bufferFormat = buffer_format; + buffer_config->sampleRate = stream->rate; + buffer_config->numChannels = stream->channels; + buffer_config->periodInFrames = dev->frames; + /* totalBufferLengthInBytes will be populated in dtsSofInterfacePrepare */ + buffer_config->totalBufferLengthInBytes = 0; + + comp_dbg(dev, "dts_effect_populate_buffer_configuration() done"); + + return 0; +} + +static int dts_codec_init(struct processing_module *mod) +{ + int ret; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + DtsSofInterfaceResult dts_result; + DtsSofInterfaceVersionInfo interface_version; + DtsSofInterfaceVersionInfo sdk_version; + + comp_dbg(dev, "dts_codec_init() start"); + + dts_result = dtsSofInterfaceInit((DtsSofInterfaceInst **)&(codec->private), + dts_effect_allocate_codec_memory, mod); + ret = dts_effect_convert_sof_interface_result(dev, dts_result); + + if (ret) + comp_err(dev, "dts_codec_init() dtsSofInterfaceInit failed %d %d", ret, dts_result); + + /* Obtain the current versions of DTS interface and SDK */ + dts_result = dtsSofInterfaceGetVersion(&interface_version, &sdk_version); + + /* Not necessary to fail initialisation if only get version failed */ + if (dts_result == DTS_SOF_INTERFACE_RESULT_SUCCESS) { + comp_info(dev, + "dts_codec_init() DTS SOF Interface version %d.%d.%d.%d", + interface_version.major, + interface_version.minor, + interface_version.patch, + interface_version.build); + comp_info(dev, + "dts_codec_init() DTS SDK version %d.%d.%d.%d", + sdk_version.major, + sdk_version.minor, + sdk_version.patch, + sdk_version.build); + } + + if (ret) + comp_err(dev, "dts_codec_init() failed %d %d", ret, dts_result); + + comp_dbg(dev, "dts_codec_init() done"); + + return ret; +} + +static int dts_codec_prepare(struct processing_module *mod) +{ + int ret; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + DtsSofInterfaceBufferConfiguration buffer_configuration; + DtsSofInterfaceResult dts_result; + + comp_dbg(dev, "dts_codec_prepare() start"); + + ret = dts_effect_populate_buffer_configuration(dev, &buffer_configuration); + if (ret) { + comp_err(dev, + "dts_codec_prepare() dts_effect_populate_buffer_configuration failed %d", + ret); + return ret; + } + + dts_result = dtsSofInterfacePrepare( + (DtsSofInterfaceInst *)codec->private, + &buffer_configuration, + &codec->mpd.in_buff, + &codec->mpd.in_buff_size, + &codec->mpd.out_buff, + &codec->mpd.out_buff_size); + ret = dts_effect_convert_sof_interface_result(dev, dts_result); + + if (ret) + comp_err(dev, "dts_codec_prepare() failed %d", ret); + + comp_dbg(dev, "dts_codec_prepare() done"); + + return ret; +} + +static int dts_codec_init_process(struct comp_dev *dev) +{ + int ret; + struct module_data *codec = &mod->priv; + DtsSofInterfaceResult dts_result; + + comp_dbg(dev, "dts_codec_init_process() start"); + + dts_result = dtsSofInterfaceInitProcess(codec->private); + ret = dts_effect_convert_sof_interface_result(dev, dts_result); + + codec->mpd.produced = 0; + codec->mpd.consumed = 0; + codec->mpd.init_done = 1; + + if (ret) + comp_err(dev, "dts_codec_init_process() failed %d %d", ret, dts_result); + + comp_dbg(dev, "dts_codec_init_process() done"); + + return ret; +} + +static int +dts_codec_process(struct processing_module *mod, + struct input_stream_buffer *input_buffers, int num_input_buffers, + struct output_stream_buffer *output_buffers, int num_output_buffers) +{ + int ret; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + DtsSofInterfaceResult dts_result; + unsigned int bytes_processed = 0; + + /* Proceed only if we have enough data to fill the module buffer completely */ + if (input_buffers[0].size < codec->mpd.in_buff_size) { + comp_dbg(dev, "dts_codec_process(): not enough data to process"); + return -ENODATA; + } + + if (!codec->mpd.init_done) { + ret = dts_codec_init_process(dev); + if (ret < 0) + return ret; + } + + memcpy_s(codec->mpd.in_buff, codec->mpd.in_buff_size, + input_buffers[0].data, codec->mpd.in_buff_size); + codec->mpd.avail = codec->mpd.in_buff_size; + + comp_dbg(dev, "dts_codec_process() start"); + + dts_result = dtsSofInterfaceProcess(codec->private, &bytes_processed); + ret = dts_effect_convert_sof_interface_result(dev, dts_result); + + codec->mpd.consumed = !ret ? bytes_processed : 0; + codec->mpd.produced = !ret ? bytes_processed : 0; + input_buffers[0].consumed = codec->mpd.consumed; + + if (ret) { + comp_err(dev, "dts_codec_process() failed %d %d", ret, dts_result); + return ret; + } + + /* copy the produced samples into the output buffer */ + memcpy_s(output_buffers[0].data, codec->mpd.produced, codec->mpd.out_buff, + codec->mpd.produced); + output_buffers[0].size = codec->mpd.produced; + + comp_dbg(dev, "dts_codec_process() done"); + + return ret; +} + +static int dts_codec_apply_config(struct comp_dev *dev) +{ + int ret = 0; + struct module_data *codec = &mod->priv; + struct module_config *config; + struct module_param *param; + uint32_t config_header_size; + uint32_t config_data_size; + uint32_t param_header_size; + uint32_t param_data_size; + uint32_t i; + uint32_t param_number = 0; + DtsSofInterfaceResult dts_result; + + comp_dbg(dev, "dts_codec_apply_config() start"); + + config = &codec->cfg; + + /* Check that config->data isn't invalid and has size greater than 0 */ + config_header_size = sizeof(config->size) + sizeof(config->avail); + if (config->size < config_header_size) { + comp_err(dev, "dts_codec_apply_config() config->data is invalid"); + return -EINVAL; + } else if (config->size == config_header_size) { + comp_err(dev, "dts_codec_apply_config() size of config->data is 0"); + return -EINVAL; + } + + /* Calculate size of config->data */ + config_data_size = config->size - config_header_size; + + /* Check that config->data is not greater than the max expected for DTS data */ + if (config_data_size > MAX_EXPECTED_DTS_CONFIG_DATA_SIZE) { + comp_err(dev, + "dts_codec_apply_config() size of config->data is larger than max for DTS data"); + return -EINVAL; + } + + /* Allow for multiple module_params to be packed into the data pointed to by config + */ + for (i = 0; i < config_data_size; param_number++) { + param = (struct module_param *)((char *)config->data + i); + param_header_size = sizeof(param->id) + sizeof(param->size); + + /* If param->size is less than param_header_size, then this param is not valid */ + if (param->size < param_header_size) { + comp_err(dev, "dts_codec_apply_config() param is invalid"); + return -EINVAL; + } + + /* Only process param->data if it has size greater than 0 */ + if (param->size > param_header_size) { + /* Calculate size of param->data */ + param_data_size = param->size - param_header_size; + + if (param_data_size) { + dts_result = dtsSofInterfaceApplyConfig(codec->private, param->id, + param->data, param_data_size); + ret = dts_effect_convert_sof_interface_result(dev, dts_result); + if (ret) { + comp_err(dev, + "dts_codec_apply_config() dtsSofInterfaceApplyConfig failed %d", + dts_result); + return ret; + } + } + } + + /* Advance to the next module_param */ + i += param->size; + } + + comp_dbg(dev, "dts_codec_apply_config() done"); + + return ret; +} + +static int dts_codec_reset(struct processing_module *mod) +{ + int ret; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + DtsSofInterfaceResult dts_result; + + comp_dbg(dev, "dts_codec_reset() start"); + + dts_result = dtsSofInterfaceReset(codec->private); + ret = dts_effect_convert_sof_interface_result(dev, dts_result); + + if (ret) + comp_err(dev, "dts_codec_reset() failed %d %d", ret, dts_result); + + comp_dbg(dev, "dts_codec_reset() done"); + + return ret; +} + +static int dts_codec_free(struct processing_module *mod) +{ + int ret; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + DtsSofInterfaceResult dts_result; + + comp_dbg(dev, "dts_codec_free() start"); + + dts_result = dtsSofInterfaceFree(codec->private); + ret = dts_effect_convert_sof_interface_result(dev, dts_result); + + if (ret) + comp_err(dev, "dts_codec_free() failed %d %d", ret, dts_result); + + comp_dbg(dev, "dts_codec_free() done"); + + return ret; +} + +static int +dts_codec_set_configuration(struct processing_module *mod, uint32_t config_id, + enum module_cfg_fragment_position pos, uint32_t data_offset_size, + const uint8_t *fragment, size_t fragment_size, uint8_t *response, + size_t response_size) +{ + struct module_data *md = &mod->priv; + struct comp_dev *dev = mod->dev; + int ret; + + ret = module_set_configuration(mod, config_id, pos, data_offset_size, fragment, + fragment_size, response, response_size); + if (ret < 0) + return ret; + + /* return if more fragments are expected or if the module is not prepared */ + if ((pos != MODULE_CFG_FRAGMENT_LAST && pos != MODULE_CFG_FRAGMENT_SINGLE) || + md->state < MODULE_INITIALIZED) + return 0; + + /* whole configuration received, apply it now */ + ret = dts_codec_apply_config(dev); + if (ret) { + comp_err(dev, "dts_codec_set_configuration(): error %x: runtime config apply failed", + ret); + return ret; + } + + comp_dbg(dev, "dts_codec_set_configuration(): config applied"); + + return 0; +} + +static struct module_interface dts_interface = { + .init = dts_codec_init, + .prepare = dts_codec_prepare, + .process = dts_codec_process, + .set_configuration = dts_codec_set_configuration, + .reset = dts_codec_reset, + .free = dts_codec_free +}; + +DECLARE_MODULE_ADAPTER(dts_interface, dts_uuid, dts_tr); diff --git a/src/audio/module_adapter/module/generic.c b/src/audio/module_adapter/module/generic.c new file mode 100644 index 000000000000..a330359ca0fa --- /dev/null +++ b/src/audio/module_adapter/module/generic.c @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Author: Marcin Rajwa + +/* + * \file generic.c + * \brief Generic Codec API + * \author Marcin Rajwa + * + */ + +#include + +/*****************************************************************************/ +/* Local helper functions */ +/*****************************************************************************/ +static int validate_config(struct module_config *cfg); + +int module_load_config(struct comp_dev *dev, void *cfg, size_t size) +{ + int ret; + struct module_config *dst; + struct processing_module *mod = comp_get_drvdata(dev); + struct module_data *md = &mod->priv; + + comp_dbg(dev, "module_load_config() start"); + + if (!cfg || !size) { + comp_err(dev, "module_load_config(): wrong input params! dev %x, cfg %x size %d", + (uint32_t)dev, (uint32_t)cfg, size); + return -EINVAL; + } + + dst = &md->cfg; + + if (!dst->data) { + /* No space for config available yet, allocate now */ + dst->data = rballoc(0, SOF_MEM_CAPS_RAM, size); + } else if (dst->size != size) { + /* The size allocated for previous config doesn't match the new one. + * Free old container and allocate new one. + */ + rfree(dst->data); + dst->data = rballoc(0, SOF_MEM_CAPS_RAM, size); + } + if (!dst->data) { + comp_err(dev, "module_load_config(): failed to allocate space for setup config."); + ret = -ENOMEM; + goto err; + } + + ret = memcpy_s(dst->data, size, cfg, size); + assert(!ret); + ret = validate_config(dst->data); + if (ret) { + comp_err(dev, "module_load_config(): validation of config failed!"); + ret = -EINVAL; + goto err; + } + + /* Config loaded, mark it as valid */ + dst->size = size; + dst->avail = true; + + comp_dbg(dev, "module_load_config() done"); + return ret; +err: + if (dst->data) + rfree(dst->data); + dst->data = NULL; + return ret; +} + +int module_init(struct processing_module *mod, struct module_interface *interface) +{ + int ret; + struct module_data *md = &mod->priv; + struct comp_dev *dev = mod->dev; + + comp_info(dev, "module_init() start"); + + if (mod->priv.state == MODULE_INITIALIZED) + return 0; + if (mod->priv.state > MODULE_INITIALIZED) + return -EPERM; + + if (!interface) { + comp_err(dev, "module_init(): could not find module interface for comp id %d", + dev_comp_id(dev)); + return -EIO; + } + + if (!interface->init || !interface->prepare || !interface->process || + !interface->reset || !interface->free) { + comp_err(dev, "module_init(): comp %d is missing mandatory interfaces", + dev_comp_id(dev)); + return -EIO; + } + + /* Assign interface */ + md->ops = interface; + /* Init memory list */ + list_init(&md->memory.mem_list); + + /* Now we can proceed with module specific initialization */ + ret = md->ops->init(mod); + if (ret) { + comp_err(dev, "module_init() error %d: module specific init failed, comp id %d", + ret, dev_comp_id(dev)); + return ret; + } + + comp_info(dev, "module_init() done"); + md->state = MODULE_INITIALIZED; + + return ret; +} + +void *module_allocate_memory(struct processing_module *mod, uint32_t size, uint32_t alignment) +{ + struct comp_dev *dev = mod->dev; + struct module_memory *container; + void *ptr; + + if (!size) { + comp_err(dev, "module_allocate_memory: requested allocation of 0 bytes."); + return NULL; + } + + /* Allocate memory container */ + container = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, + sizeof(struct module_memory)); + if (!container) { + comp_err(dev, "module_allocate_memory: failed to allocate memory container."); + return NULL; + } + + /* Allocate memory for module */ + if (alignment) + ptr = rballoc_align(0, SOF_MEM_CAPS_RAM, size, alignment); + else + ptr = rballoc(0, SOF_MEM_CAPS_RAM, size); + + if (!ptr) { + comp_err(dev, "module_allocate_memory: failed to allocate memory for comp %x.", + dev_comp_id(dev)); + return NULL; + } + /* Store reference to allocated memory */ + container->ptr = ptr; + list_item_prepend(&container->mem_list, &mod->priv.memory.mem_list); + + return ptr; +} + +int module_free_memory(struct processing_module *mod, void *ptr) +{ + struct module_memory *mem; + struct list_item *mem_list; + struct list_item *_mem_list; + + if (!ptr) + return 0; + + /* Find which container keeps this memory */ + list_for_item_safe(mem_list, _mem_list, &mod->priv.memory.mem_list) { + mem = container_of(mem_list, struct module_memory, mem_list); + if (mem->ptr == ptr) { + rfree(mem->ptr); + list_item_del(&mem->mem_list); + rfree(mem); + return 0; + } + } + + comp_err(mod->dev, "module_free_memory: error: could not find memory pointed by %p", + ptr); + + return -EINVAL; +} + +static int validate_config(struct module_config *cfg) +{ + /* TODO: validation of codec specific setup config */ + return 0; +} + +int module_prepare(struct processing_module *mod) +{ + int ret; + struct module_data *md = &mod->priv; + struct comp_dev *dev = mod->dev; + + comp_dbg(dev, "module_prepare() start"); + + if (mod->priv.state == MODULE_IDLE) + return 0; + if (mod->priv.state < MODULE_INITIALIZED) + return -EPERM; + + ret = md->ops->prepare(mod); + if (ret) { + comp_err(dev, "module_prepare() error %d: module specific prepare failed, comp_id %d", + ret, dev_comp_id(dev)); + return ret; + } + + /* After prepare is done we no longer need runtime configuration + * as it has been applied during the procedure - it is safe to + * free it. + */ + if (md->cfg.data) + rfree(md->cfg.data); + + md->cfg.avail = false; + md->cfg.data = NULL; + + md->state = MODULE_IDLE; + comp_dbg(dev, "module_prepare() done"); + + return ret; +} + +int module_process(struct processing_module *mod, struct input_stream_buffer *input_buffers, + int num_input_buffers, struct output_stream_buffer *output_buffers, + int num_output_buffers) +{ + struct comp_dev *dev = mod->dev; + int ret; + + struct module_data *md = &mod->priv; + + comp_dbg(dev, "module_process() start"); + + if (md->state != MODULE_IDLE) { + comp_err(dev, "module_process(): wrong state of comp_id %x, state %d", + dev_comp_id(dev), md->state); + return -EPERM; + } + + /* set state to processing */ + md->state = MODULE_PROCESSING; + + ret = md->ops->process(mod, input_buffers, num_input_buffers, output_buffers, + num_output_buffers); + if (ret && ret != -ENOSPC && ret != -ENODATA) { + comp_err(dev, "module_process() error %d: for comp %d", + ret, dev_comp_id(dev)); + return ret; + } + + comp_dbg(dev, "module_process() done"); + + /* reset state to idle */ + md->state = MODULE_IDLE; + return ret; +} + +int module_reset(struct processing_module *mod) +{ + int ret; + struct module_data *md = &mod->priv; + + /* if the module was never prepared, no need to reset */ + if (md->state < MODULE_IDLE) + return 0; + + ret = md->ops->reset(mod); + if (ret) { + comp_err(mod->dev, "module_reset() error %d: module specific reset() failed for comp %d", + ret, dev_comp_id(mod->dev)); + return ret; + } + + md->cfg.avail = false; + md->cfg.size = 0; + rfree(md->cfg.data); + + /* module resets itself to the initial condition after prepare() + * so let's change its state to reflect that. + */ + md->state = MODULE_IDLE; + + return 0; +} + +void module_free_all_memory(struct processing_module *mod) +{ + struct module_memory *mem; + struct list_item *mem_list; + struct list_item *_mem_list; + + /* Find which container keeps this memory */ + list_for_item_safe(mem_list, _mem_list, &mod->priv.memory.mem_list) { + mem = container_of(mem_list, struct module_memory, mem_list); + rfree(mem->ptr); + list_item_del(&mem->mem_list); + rfree(mem); + } +} + +int module_free(struct processing_module *mod) +{ + int ret; + struct module_data *md = &mod->priv; + + ret = md->ops->free(mod); + if (ret) + comp_warn(mod->dev, "module_free(): error: %d for %d", + ret, dev_comp_id(mod->dev)); + + /* Free all memory requested by module */ + module_free_all_memory(mod); + /* Free all memory shared by module_adapter & module */ + md->cfg.avail = false; + md->cfg.size = 0; + rfree(md->cfg.data); + if (md->runtime_params) + rfree(md->runtime_params); + + md->state = MODULE_DISABLED; + + return ret; +} + +/** + * \brief Set module configuration - Common method to assemble large configuration message + * \param[in] mod - struct processing_module pointer + * \param[in] config_id - Configuration ID + * \param[in] pos - position of the fragment in the large message + * \param[in] data_offset_size: size of the whole configuration if it is the first fragment or the + * only fragment. Otherwise, it is the offset of the fragment in the + * whole configuration. + * \param[in] fragment: configuration fragment buffer + * \param[in] fragment_size: size of @fragment + * \params[in] response: optional response buffer to fill + * \params[in] response_size: size of @response + * + * \return: 0 upon success or error upon failure + */ +int module_set_configuration(struct processing_module *mod, + uint32_t config_id, + enum module_cfg_fragment_position pos, size_t data_offset_size, + const uint8_t *fragment, size_t fragment_size, uint8_t *response, + size_t response_size) +{ + struct module_data *md = &mod->priv; + struct comp_dev *dev = mod->dev; + size_t offset = 0; + uint8_t *dst; + int ret; + + switch (pos) { + case MODULE_CFG_FRAGMENT_FIRST: + case MODULE_CFG_FRAGMENT_SINGLE: + /* + * verify input params & allocate memory for the config blob when the first + * fragment arrives + */ + md->new_cfg_size = data_offset_size; + + /* Check that there is no previous request in progress */ + if (md->runtime_params) { + comp_err(dev, "module_set_configuration(): error: busy with previous request"); + return -EBUSY; + } + + if (!md->new_cfg_size) + return 0; + + if (md->new_cfg_size > MAX_BLOB_SIZE) { + comp_err(dev, "module_set_configuration(): error: blob size is too big cfg size %d, allowed %d", + md->new_cfg_size, MAX_BLOB_SIZE); + return -EINVAL; + } + + /* Allocate buffer for new params */ + md->runtime_params = rballoc(0, SOF_MEM_CAPS_RAM, md->new_cfg_size); + if (!md->runtime_params) { + comp_err(dev, "module_set_configuration(): space allocation for new params failed"); + return -ENOMEM; + } + + memset(md->runtime_params, 0, md->new_cfg_size); + break; + default: + if (!md->runtime_params) { + comp_err(dev, "module_set_configuration(): error: no memory available for runtime params in consecutive load"); + return -EIO; + } + + /* set offset for intermediate and last fragments */ + offset = data_offset_size; + break; + } + + dst = (uint8_t *)md->runtime_params + offset; + + ret = memcpy_s(dst, md->new_cfg_size - offset, fragment, fragment_size); + if (ret < 0) { + comp_err(dev, "module_set_configuration(): error: %d failed to copy fragment", + ret); + return ret; + } + + /* return as more fragments of config data expected */ + if (pos == MODULE_CFG_FRAGMENT_MIDDLE || pos == MODULE_CFG_FRAGMENT_FIRST) + return 0; + + /* config fully copied, now load it */ + ret = module_load_config(dev, md->runtime_params, md->new_cfg_size); + if (ret) + comp_err(dev, "module_set_configuration(): error %d: config failed", ret); + else + comp_dbg(dev, "module_set_configuration(): config load successful"); + + md->new_cfg_size = 0; + + if (md->runtime_params) + rfree(md->runtime_params); + md->runtime_params = NULL; + + return ret; +} diff --git a/src/audio/module_adapter/module/passthrough.c b/src/audio/module_adapter/module/passthrough.c new file mode 100644 index 000000000000..23f310bf068d --- /dev/null +++ b/src/audio/module_adapter/module/passthrough.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright 2020 NXP +// +// Author: Daniel Baluta +// +// Passthrough codec implementation to demonstrate Codec Adapter API + +#include + +/* 376b5e44-9c82-4ec2-bc83-10ea101afa8f */ +DECLARE_SOF_RT_UUID("passthrough_codec", passthrough_uuid, 0x376b5e44, 0x9c82, 0x4ec2, + 0xbc, 0x83, 0x10, 0xea, 0x10, 0x1a, 0xf8, 0x8f); +DECLARE_TR_CTX(passthrough_tr, SOF_UUID(passthrough_uuid), LOG_LEVEL_INFO); + +static int passthrough_codec_init(struct processing_module *mod) +{ + comp_info(mod->dev, "passthrough_codec_init() start"); + return 0; +} + +static int passthrough_codec_prepare(struct processing_module *mod) +{ + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + + comp_info(dev, "passthrough_codec_prepare()"); + + codec->mpd.in_buff = rballoc(0, SOF_MEM_CAPS_RAM, mod->period_bytes); + if (!codec->mpd.in_buff) { + comp_err(dev, "passthrough_codec_prepare(): Failed to alloc in_buff"); + return -ENOMEM; + } + codec->mpd.in_buff_size = mod->period_bytes; + + codec->mpd.out_buff = rballoc(0, SOF_MEM_CAPS_RAM, mod->period_bytes); + if (!codec->mpd.out_buff) { + comp_err(dev, "passthrough_codec_prepare(): Failed to alloc out_buff"); + rfree(codec->mpd.in_buff); + return -ENOMEM; + } + codec->mpd.out_buff_size = mod->period_bytes; + + return 0; +} + +static int passthrough_codec_init_process(struct processing_module *mod) +{ + struct module_data *codec = &mod->priv; + struct comp_dev *dev = mod->dev; + + comp_dbg(dev, "passthrough_codec_init_process()"); + + codec->mpd.produced = 0; + codec->mpd.consumed = 0; + codec->mpd.init_done = 1; + + return 0; +} + +static int +passthrough_codec_process(struct processing_module *mod, + struct input_stream_buffer *input_buffers, int num_input_buffers, + struct output_stream_buffer *output_buffers, int num_output_buffers) +{ + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + + /* Proceed only if we have enough data to fill the module buffer completely */ + if (input_buffers[0].size < codec->mpd.in_buff_size) { + comp_dbg(dev, "passthrough_codec_process(): not enough data to process"); + return -ENODATA; + } + + if (!codec->mpd.init_done) + passthrough_codec_init_process(mod); + + memcpy_s(codec->mpd.in_buff, codec->mpd.in_buff_size, + input_buffers[0].data, codec->mpd.in_buff_size); + + comp_dbg(dev, "passthrough_codec_process()"); + + memcpy_s(codec->mpd.out_buff, codec->mpd.out_buff_size, + codec->mpd.in_buff, codec->mpd.in_buff_size); + codec->mpd.produced = mod->period_bytes; + codec->mpd.consumed = mod->period_bytes; + input_buffers[0].consumed = codec->mpd.consumed; + + /* copy the produced samples into the output buffer */ + memcpy_s(output_buffers[0].data, codec->mpd.produced, codec->mpd.out_buff, + codec->mpd.produced); + output_buffers[0].size = codec->mpd.produced; + + return 0; +} + +static int passthrough_codec_reset(struct processing_module *mod) +{ + comp_info(mod->dev, "passthrough_codec_reset()"); + + /* nothing to do */ + return 0; +} + +static int passthrough_codec_free(struct processing_module *mod) +{ + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + + comp_info(dev, "passthrough_codec_free()"); + + rfree(codec->mpd.in_buff); + rfree(codec->mpd.out_buff); + + return 0; +} + +static struct module_interface passthrough_interface = { + .init = passthrough_codec_init, + .prepare = passthrough_codec_prepare, + .process = passthrough_codec_process, + .reset = passthrough_codec_reset, + .free = passthrough_codec_free +}; + +DECLARE_MODULE_ADAPTER(passthrough_interface, passthrough_uuid, passthrough_tr); diff --git a/src/audio/module_adapter/module/waves.c b/src/audio/module_adapter/module/waves.c new file mode 100644 index 000000000000..499c4bbf46ee --- /dev/null +++ b/src/audio/module_adapter/module/waves.c @@ -0,0 +1,874 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2020 Waves Audio Ltd. All rights reserved. +// +// Author: Oleksandr Strelchenko +// +#include +#include +#include + +#include "MaxxEffect/MaxxEffect.h" +#include "MaxxEffect/MaxxStream.h" +#include "MaxxEffect/MaxxStatus.h" +#include "MaxxEffect/Initialize/MaxxEffect_Initialize.h" +#include "MaxxEffect/Process/MaxxEffect_Process.h" +#include "MaxxEffect/Process/MaxxEffect_Reset.h" +#include "MaxxEffect/Control/RPC/MaxxEffect_RPC_Server.h" +#include "MaxxEffect/Control/Direct/MaxxEffect_Revision.h" + +#define MAX_CONFIG_SIZE_BYTES (8192) +#define NUM_IO_STREAMS (1) + +/* d944281a-afe9-4695-a043-d7f62b89538e*/ +DECLARE_SOF_RT_UUID("waves_codec", waves_uuid, 0xd944281a, 0xafe9, 0x4695, + 0xa0, 0x43, 0xd7, 0xf6, 0x2b, 0x89, 0x53, 0x8e); +DECLARE_TR_CTX(waves_tr, SOF_UUID(waves_uuid), LOG_LEVEL_INFO); + +struct waves_codec_data { + uint32_t sample_rate; + uint32_t buffer_bytes; + uint32_t buffer_samples; + uint32_t sample_size_in_bytes; + uint64_t reserved; + + MaxxEffect_t *effect; + uint32_t effect_size; + MaxxStreamFormat_t i_format; + MaxxStreamFormat_t o_format; + MaxxStream_t i_stream; + MaxxStream_t o_stream; + MaxxBuffer_t i_buffer; + MaxxBuffer_t o_buffer; + uint32_t response_max_bytes; + uint32_t request_max_bytes; + void *response; + struct module_config setup_cfg; +}; + +enum waves_codec_params { + PARAM_NOP = 0, + PARAM_MESSAGE = 1, + PARAM_REVISION = 2 +}; + +/* convert MaxxBuffer_Format_t to number of bytes it requires */ +static int32_t sample_format_convert_to_bytes(MaxxBuffer_Format_t format) +{ + int32_t res; + + switch (format) { + case MAXX_BUFFER_FORMAT_Q1_15: + res = sizeof(uint16_t); + break; + case MAXX_BUFFER_FORMAT_Q1_23: + res = 3; /* 3 bytes */ + break; + case MAXX_BUFFER_FORMAT_Q9_23: + COMPILER_FALLTHROUGH; + case MAXX_BUFFER_FORMAT_Q1_31: + COMPILER_FALLTHROUGH; + case MAXX_BUFFER_FORMAT_Q5_27: + res = sizeof(uint32_t); + break; + case MAXX_BUFFER_FORMAT_FLOAT: + res = sizeof(float); + break; + default: + res = -EINVAL; + break; + } + return res; +} + +/* convert enum sof_ipc_frame to MaxxBuffer_Format_t */ +static MaxxBuffer_Format_t format_convert_sof_to_me(enum sof_ipc_frame format) +{ + MaxxBuffer_Format_t res; + + switch (format) { + case SOF_IPC_FRAME_S16_LE: + res = MAXX_BUFFER_FORMAT_Q1_15; + break; + case SOF_IPC_FRAME_S24_4LE: + res = MAXX_BUFFER_FORMAT_Q9_23; + break; + case SOF_IPC_FRAME_S32_LE: + res = MAXX_BUFFER_FORMAT_Q1_31; + break; + case SOF_IPC_FRAME_FLOAT: + res = MAXX_BUFFER_FORMAT_FLOAT; + break; + default: + res = -EINVAL; + break; + } + return res; +} + +/* convert sof frame format to MaxxBuffer_Layout_t */ +static MaxxBuffer_Layout_t layout_convert_sof_to_me(uint32_t layout) +{ + MaxxBuffer_Layout_t res; + + switch (layout) { + case SOF_IPC_BUFFER_INTERLEAVED: + res = MAXX_BUFFER_LAYOUT_INTERLEAVED; + break; + case SOF_IPC_BUFFER_NONINTERLEAVED: + res = MAXX_BUFFER_LAYOUT_DEINTERLEAVED; + break; + default: + res = -EINVAL; + break; + } + return res; +} + +/* check if sample format supported by codec */ +static bool format_is_supported(enum sof_ipc_frame format) +{ + bool supported; + + switch (format) { + case SOF_IPC_FRAME_S16_LE: + COMPILER_FALLTHROUGH; + case SOF_IPC_FRAME_S24_4LE: + COMPILER_FALLTHROUGH; + case SOF_IPC_FRAME_S32_LE: + supported = true; + break; + case SOF_IPC_FRAME_FLOAT: + COMPILER_FALLTHROUGH; + default: + supported = false; + break; + } + return supported; +} + +/* check if buffer layout supported by codec */ +static bool layout_is_supported(uint32_t layout) +{ + bool supported; + + switch (layout) { + case SOF_IPC_BUFFER_INTERLEAVED: + supported = true; + break; + case SOF_IPC_BUFFER_NONINTERLEAVED: + COMPILER_FALLTHROUGH; + default: + supported = false; + break; + } + return supported; +} + +/* check if sample rate supported by codec */ +static bool rate_is_supported(uint32_t rate) +{ + bool supported; + + switch (rate) { + case 44100: + COMPILER_FALLTHROUGH; + case 48000: + supported = true; + break; + default: + supported = false; + break; + } + return supported; +} + +/* allocate memory for MaxxEffect object */ +static int waves_effect_allocate(struct processing_module *mod) +{ + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + struct waves_codec_data *waves_codec = codec->private; + MaxxStatus_t status; + + comp_dbg(dev, "waves_effect_allocate() start"); + + status = MaxxEffect_GetEffectSize(&waves_codec->effect_size); + if (status) { + comp_err(dev, "waves_effect_allocate() MaxxEffect_GetEffectSize returned %d", + status); + return -EINVAL; + } + + waves_codec->effect = (MaxxEffect_t *)module_allocate_memory(mod, + waves_codec->effect_size, 16); + + if (!waves_codec->effect) { + comp_err(dev, "waves_effect_allocate() failed to allocate %d bytes for effect", + waves_codec->effect_size); + return -ENOMEM; + } + + comp_info(dev, "waves_codec_init() allocated %d bytes for effect", + waves_codec->effect_size); + + comp_dbg(dev, "waves_codec_init() done"); + return 0; +} + +/* checks if sink/source parameters fit MaxxEffect */ +static int waves_effect_check(struct comp_dev *dev) +{ + struct comp_buffer *sink = list_first_item(&dev->bsink_list, struct comp_buffer, + source_list); + struct comp_buffer *source = list_first_item(&dev->bsource_list, struct comp_buffer, + sink_list); + const struct audio_stream *src_fmt = &source->stream; + const struct audio_stream *snk_fmt = &sink->stream; + + /* Init sink & source buffers */ + comp_dbg(dev, "waves_effect_check() start"); + + if (!source || !sink) { + comp_err(dev, "waves_effect_check() source/sink buffer not found"); + return -EINVAL; + } + + /* todo use fallback to comp_verify_params when ready */ + + /* resampling not supported */ + if (src_fmt->rate != snk_fmt->rate) { + comp_err(dev, "waves_effect_check() source %d sink %d rate mismatch", + src_fmt->rate, snk_fmt->rate); + return -EINVAL; + } + + /* upmix/downmix not supported */ + if (src_fmt->channels != snk_fmt->channels) { + comp_err(dev, "waves_effect_check() source %d sink %d channels mismatch", + src_fmt->channels, snk_fmt->channels); + return -EINVAL; + } + + /* different frame format not supported */ + if (src_fmt->frame_fmt != snk_fmt->frame_fmt) { + comp_err(dev, "waves_effect_check() source %d sink %d sample format mismatch", + src_fmt->frame_fmt, snk_fmt->frame_fmt); + return -EINVAL; + } + + /* different interleaving is not supported */ + if (source->buffer_fmt != sink->buffer_fmt) { + comp_err(dev, "waves_effect_check() source %d sink %d buffer format mismatch"); + return -EINVAL; + } + + if (!format_is_supported(src_fmt->frame_fmt)) { + comp_err(dev, "waves_effect_check() float samples not supported"); + return -EINVAL; + } + + if (!layout_is_supported(source->buffer_fmt)) { + comp_err(dev, "waves_effect_check() non interleaved format not supported"); + return -EINVAL; + } + + if (!rate_is_supported(src_fmt->rate)) { + comp_err(dev, "waves_effect_check() rate %d not supported", src_fmt->rate); + return -EINVAL; + } + + if (src_fmt->channels != 2) { + comp_err(dev, "waves_effect_check() channels %d not supported", src_fmt->channels); + return -EINVAL; + } + + comp_dbg(dev, "waves_effect_check() done"); + return 0; +} + +/* initializes MaxxEffect based on stream parameters */ +static int waves_effect_init(struct processing_module *mod) +{ + struct comp_dev *dev = mod->dev; + struct comp_buffer *source = list_first_item(&dev->bsource_list, struct comp_buffer, + sink_list); + struct module_data *codec = &mod->priv; + struct waves_codec_data *waves_codec = codec->private; + + const struct audio_stream *src_fmt = &source->stream; + + MaxxStatus_t status; + MaxxBuffer_Format_t sample_format; + MaxxBuffer_Layout_t buffer_format; + int32_t sample_bytes; + MaxxStreamFormat_t *i_formats[NUM_IO_STREAMS] = { &waves_codec->i_format }; + MaxxStreamFormat_t *o_formats[NUM_IO_STREAMS] = { &waves_codec->o_format }; + + comp_dbg(dev, "waves_effect_init() start"); + + sample_format = format_convert_sof_to_me(src_fmt->frame_fmt); + if (sample_format < 0) { + comp_err(dev, "waves_effect_init() sof sample format %d not supported", + src_fmt->frame_fmt); + return -EINVAL; + } + + buffer_format = layout_convert_sof_to_me(source->buffer_fmt); + if (buffer_format < 0) { + comp_err(dev, "waves_effect_init() sof buffer format %d not supported", + source->buffer_fmt); + return -EINVAL; + } + + sample_bytes = sample_format_convert_to_bytes(sample_format); + if (sample_bytes < 0) { + comp_err(dev, "waves_effect_init() sample_format %d not supported", + sample_format); + return -EINVAL; + } + + waves_codec->request_max_bytes = 0; + waves_codec->response_max_bytes = 0; + waves_codec->response = 0; + waves_codec->i_buffer = 0; + waves_codec->o_buffer = 0; + + waves_codec->i_format.sampleRate = src_fmt->rate; + waves_codec->i_format.numChannels = src_fmt->channels; + waves_codec->i_format.samplesFormat = sample_format; + waves_codec->i_format.samplesLayout = buffer_format; + + waves_codec->o_format = waves_codec->i_format; + + waves_codec->sample_size_in_bytes = sample_bytes; + waves_codec->buffer_samples = (src_fmt->rate * 2) / 1000; /* 2 ms io buffers */ + waves_codec->buffer_bytes = waves_codec->buffer_samples * src_fmt->channels * + waves_codec->sample_size_in_bytes; + + // trace allows printing only up-to 4 words at a time + // logging all the information in two calls + comp_info(dev, "waves_effect_init() rate %d, channels %d", waves_codec->i_format.sampleRate, + waves_codec->i_format.numChannels); + + comp_info(dev, "waves_effect_init() format %d, layout %d, frame %d", + waves_codec->i_format.samplesFormat, waves_codec->i_format.samplesLayout, + waves_codec->buffer_samples); + + status = MaxxEffect_Initialize(waves_codec->effect, i_formats, 1, o_formats, 1); + + if (status) { + comp_err(dev, "waves_effect_init() MaxxEffect_Initialize returned %d", status); + return -EINVAL; + } + + comp_dbg(dev, "waves_effect_init() done"); + return 0; +} + +/* allocate additional buffers for MaxxEffect */ +static int waves_effect_buffers(struct processing_module *mod) +{ + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + struct waves_codec_data *waves_codec = codec->private; + MaxxStatus_t status; + int ret; + void *i_buffer = NULL, *o_buffer = NULL, *response = NULL; + + comp_dbg(dev, "waves_effect_buffers() start"); + + status = MaxxEffect_GetMessageMaxSize(waves_codec->effect, &waves_codec->request_max_bytes, + &waves_codec->response_max_bytes); + + if (status) { + comp_err(dev, "waves_effect_buffers() MaxxEffect_GetMessageMaxSize returned %d", + status); + ret = -EINVAL; + goto err; + } + + response = module_allocate_memory(mod, waves_codec->response_max_bytes, 16); + if (!response) { + comp_err(dev, "waves_effect_buffers() failed to allocate %d bytes for response", + waves_codec->response_max_bytes); + ret = -ENOMEM; + goto err; + } + + i_buffer = module_allocate_memory(mod, waves_codec->buffer_bytes, 16); + if (!i_buffer) { + comp_err(dev, "waves_effect_buffers() failed to allocate %d bytes for i_buffer", + waves_codec->buffer_bytes); + ret = -ENOMEM; + goto err; + } + + o_buffer = module_allocate_memory(mod, waves_codec->buffer_bytes, 16); + if (!o_buffer) { + comp_err(dev, "waves_effect_buffers() failed to allocate %d bytes for o_buffer", + waves_codec->buffer_bytes); + ret = -ENOMEM; + goto err; + } + + waves_codec->i_buffer = i_buffer; + waves_codec->o_buffer = o_buffer; + waves_codec->response = response; + codec->mpd.in_buff = waves_codec->i_buffer; + codec->mpd.in_buff_size = waves_codec->buffer_bytes; + codec->mpd.out_buff = waves_codec->o_buffer; + codec->mpd.out_buff_size = waves_codec->buffer_bytes; + + comp_info(dev, "waves_effect_buffers() size response %d, i_buffer %d, o_buffer %d", + waves_codec->response_max_bytes, waves_codec->buffer_bytes, + waves_codec->buffer_bytes); + + comp_dbg(dev, "waves_effect_buffers() done"); + return 0; + +err: + if (i_buffer) + module_free_memory(mod, i_buffer); + if (o_buffer) + module_free_memory(mod, o_buffer); + if (response) + module_free_memory(mod, response); + return ret; +} + +/* get MaxxEffect revision */ +static int waves_effect_revision(struct processing_module *mod) +{ + struct module_data *codec = &mod->priv; + struct waves_codec_data *waves_codec = codec->private; + struct comp_dev *dev = mod->dev; + const char *revision = NULL; + uint32_t revision_len; + MaxxStatus_t status; + + comp_info(dev, "waves_effect_revision() start"); + + status = MaxxEffect_Revision_Get(waves_codec->effect, &revision, &revision_len); + + if (status) { + comp_err(dev, "waves_effect_revision() MaxxEffect_Revision_Get returned %d", + status); + return -EINVAL; + } + +#if CONFIG_TRACEV + if (revision_len) { + const uint32_t *ptr = (uint32_t *)revision; + uint32_t len = revision_len / sizeof(uint32_t); + uint32_t idx = 0; + + /* get requests from codec_adapter are not supported + * printing strings is not supported + * so dumping revision string to trace log as ascii values + * if simply write a for loop here then depending on trace filtering settings + * some parts of revision might not be printed - this is highly unwanted + */ + dump_hex(ptr, idx, len); + dump_hex(ptr, idx, len); + dump_hex(ptr, idx, len); + dump_hex(ptr, idx, len); + dump_hex(ptr, idx, len); + dump_hex(ptr, idx, len); + dump_hex(ptr, idx, len); + dump_hex(ptr, idx, len); + dump_hex(ptr, idx, len); + dump_hex(ptr, idx, len); + } +#endif + + comp_info(dev, "waves_effect_revision() done"); + return 0; +} + +/* apply MaxxEffect message */ +static int waves_effect_message(struct processing_module *mod, void *data, uint32_t size) +{ + struct module_data *codec = &mod->priv; + struct waves_codec_data *waves_codec = codec->private; + struct module_data *codec = &mod->priv; + MaxxStatus_t status; + uint32_t response_size = 0; + + comp_info(dev, "waves_effect_message() start data %p size %d", data, size); + + status = MaxxEffect_Message(waves_codec->effect, data, size, + waves_codec->response, &response_size); + + if (status) { + comp_err(dev, "waves_effect_message() MaxxEffect_Message returned %d", status); + return -EINVAL; + } + +#if CONFIG_TRACEV + /* at time of writing codec adapter does not support getting something from codec + * so response is stored to internal structure and dumped into trace messages + */ + if (response_size) { + uint32_t idx; + uint32_t len = response_size / sizeof(uint32_t); + const uint32_t *ptr = (uint32_t *)waves_codec->response; + + for (idx = 0; idx < len; ) + dump_hex(ptr, idx, len); + } +#endif + + return 0; +} + +/* apply codec config */ +static int waves_effect_config(struct processing_module *mod) +{ + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + struct waves_codec_data *waves_codec = codec->private; + struct module_param *param; + struct module_config *cfg; + uint32_t index; + uint32_t param_number = 0; + int ret = 0; + + comp_info(dev, "waves_codec_configure() start"); + + cfg = &codec->cfg; + + /* use setup config if no runtime config available */ + if (!cfg->avail) + cfg = &waves_codec->setup_cfg; + + comp_info(dev, "waves_codec_configure() config %p, size %d, avail %d", + cfg->data, cfg->size, cfg->avail); + + if (!cfg->avail || !cfg->size) { + comp_err(dev, "waves_codec_configure() no config, avail %d, size %d", + cfg->avail, cfg->size); + return -EINVAL; + } + + if (cfg->size > MAX_CONFIG_SIZE_BYTES) { + comp_err(dev, "waves_codec_configure() provided config is too big, size %d", + cfg->size); + return -EINVAL; + } + + /* incoming data in cfg->data is arranged according to struct module_param + * there migh be more than one struct module_param inside cfg->data, glued back to back + */ + for (index = 0; index < cfg->size && (!ret); param_number++) { + uint32_t param_data_size; + + param = (struct module_param *)((char *)cfg->data + index); + param_data_size = param->size - sizeof(param->size) - sizeof(param->id); + + comp_info(dev, "waves_codec_configure() param num %d id %d size %d", + param_number, param->id, param->size); + + switch (param->id) { + case PARAM_NOP: + comp_info(dev, "waves_codec_configure() NOP"); + break; + case PARAM_MESSAGE: + ret = waves_effect_message(mod, param->data, param_data_size); + break; + case PARAM_REVISION: + ret = waves_effect_revision(mod); + break; + default: + ret = -EINVAL; + break; + } + + index += param->size; + } + + if (ret) + comp_err(dev, "waves_codec_configure() failed %d", ret); + + comp_dbg(dev, "waves_codec_configure() done"); + return ret; +} + +/* apply setup config */ +static int waves_effect_setup_config(struct processing_module *mod) +{ + struct comp_dev *dev = mod->dev; + int ret; + + comp_dbg(dev, "waves_effect_setup_config() start"); + + ret = waves_effect_config(mod); + if (ret < 0) { + comp_err(dev, "waves_effect_setup_config(): fail to apply config"); + return ret; + } + + comp_dbg(dev, "waves_effect_setup_config() done"); + return 0; +} + +static int waves_codec_init(struct processing_module *mod) +{ + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + struct waves_codec_data *waves_codec; + int ret = 0; + + comp_dbg(dev, "waves_codec_init() start"); + + waves_codec = module_allocate_memory(mod, sizeof(struct waves_codec_data), 16); + if (!waves_codec) { + comp_err(dev, "waves_codec_init() failed to allocate %d bytes for waves_codec_data", + sizeof(struct waves_codec_data)); + ret = -ENOMEM; + } else { + memset(waves_codec, 0, sizeof(struct waves_codec_data)); + codec->private = waves_codec; + + ret = waves_effect_allocate(mod); + if (ret) { + module_free_memory(mod, waves_codec); + codec->private = NULL; + } + } + + if (ret) + comp_err(dev, "waves_codec_init() failed %d", ret); + + waves_codec->setup_cfg.avail = false; + + /* copy the setup config only for the first init */ + if (codec->state == MODULE_DISABLED && codec->cfg.avail) { + struct module_config *setup_cfg = &waves_codec->setup_cfg; + + /* allocate memory for set up config */ + setup_cfg->data = rballoc(0, SOF_MEM_CAPS_RAM, codec->cfg.size); + if (!setup_cfg->data) { + comp_err(dev, "cadence_codec_init(): failed to alloc setup config"); + module_free_memory(mod, waves_codec); + return -ENOMEM; + } + + /* copy the setup config */ + setup_cfg->size = codec->cfg.size; + ret = memcpy_s(setup_cfg->data, setup_cfg->size, codec->cfg.data, setup_cfg->size); + if (ret) { + comp_err(dev, "cadence_codec_init(): failed to copy setup config %d", ret); + module_free_memory(mod, waves_codec); + return ret; + } + setup_cfg->avail = true; + } + + comp_dbg(dev, "waves_codec_init() done"); + return ret; +} + +static int waves_codec_prepare(struct processing_module *mod) +{ + struct comp_dev *dev = mod->dev; + int ret; + + comp_dbg(dev, "waves_codec_prepare() start"); + + ret = waves_effect_check(dev); + + if (!ret) + ret = waves_effect_init(mod); + + if (!ret) + ret = waves_effect_buffers(mod); + + if (!ret) + ret = waves_effect_setup_config(mod); + + if (ret) + comp_err(dev, "waves_codec_prepare() failed %d", ret); + + comp_dbg(dev, "waves_codec_prepare() done"); + return ret; +} + +static int waves_codec_init_process(struct processing_module *mod) +{ + struct module_data *codec = &mod->priv; + struct comp_dev *dev = mod->dev; + + comp_dbg(dev, "waves_codec_init_process()"); + + codec->mpd.produced = 0; + codec->mpd.consumed = 0; + codec->mpd.init_done = 1; + + return 0; +} + +static int +waves_codec_process(struct processing_module *mod, + struct input_stream_buffer *input_buffers, int num_input_buffers, + struct output_stream_buffer *output_buffers, int num_output_buffers) +{ + int ret; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + struct waves_codec_data *waves_codec = codec->private; + + /* Proceed only if we have enough data to fill the module buffer completely */ + if (input_buffers[0].size < codec->mpd.in_buff_size) { + comp_dbg(dev, "waves_codec_process(): not enough data to process"); + return -ENODATA; + } + + if (!codec->mpd.init_done) + waves_codec_init_process(dev); + + memcpy_s(codec->mpd.in_buff, codec->mpd.in_buff_size, + input_buffers[0].data, codec->mpd.in_buff_size); + codec->mpd.avail = codec->mpd.in_buff_size; + + comp_dbg(dev, "waves_codec_process() start"); + + MaxxStream_t *i_streams[NUM_IO_STREAMS] = { &waves_codec->i_stream }; + MaxxStream_t *o_streams[NUM_IO_STREAMS] = { &waves_codec->o_stream }; + MaxxStatus_t status; + uint32_t num_input_samples = waves_codec->buffer_samples; + + /* here input buffer should always be filled up as requested + * since no one updates it`s size except code in prepare. + * on the other hand there is available/produced counters in mpd, check them anyways + */ + if (codec->mpd.avail != waves_codec->buffer_bytes) { + comp_warn(dev, "waves_codec_process() input buffer %d is not full %d", + codec->mpd.avail, waves_codec->buffer_bytes); + num_input_samples = codec->mpd.avail / + (waves_codec->sample_size_in_bytes * waves_codec->i_format.numChannels); + } + + waves_codec->i_stream.buffersArray = &waves_codec->i_buffer; + waves_codec->i_stream.numAvailableSamples = num_input_samples; + waves_codec->i_stream.numProcessedSamples = 0; + waves_codec->i_stream.maxNumSamples = waves_codec->buffer_samples; + + waves_codec->o_stream.buffersArray = &waves_codec->o_buffer; + waves_codec->o_stream.numAvailableSamples = 0; + waves_codec->o_stream.numProcessedSamples = 0; + waves_codec->o_stream.maxNumSamples = waves_codec->buffer_samples; + + status = MaxxEffect_Process(waves_codec->effect, i_streams, o_streams); + if (status) { + comp_err(dev, "waves_codec_process() MaxxEffect_Process returned %d", status); + ret = -EINVAL; + } else { + codec->mpd.produced = waves_codec->o_stream.numAvailableSamples * + waves_codec->o_format.numChannels * waves_codec->sample_size_in_bytes; + codec->mpd.consumed = codec->mpd.produced; + input_buffers[0].consumed = codec->mpd.consumed; + ret = 0; + /* copy the produced samples into the output buffer */ + memcpy_s(output_buffers[0].data, codec->mpd.produced, codec->mpd.out_buff, + codec->mpd.produced); + output_buffers[0].size = codec->mpd.produced; + } + + if (ret) + comp_err(dev, "waves_codec_process() failed %d", ret); + + comp_dbg(dev, "waves_codec_process() done"); + return ret; +} + +static int waves_codec_apply_config(struct comp_dev *dev) +{ + int ret; + + comp_dbg(dev, "waves_codec_apply_config() start"); + ret = waves_effect_config(dev); + + if (ret) + comp_err(dev, "waves_codec_apply_config() failed %d", ret); + + comp_dbg(dev, "waves_codec_apply_config() done"); + return ret; +} + +static int waves_codec_reset(struct processing_module *mod) +{ + MaxxStatus_t status; + int ret = 0; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + struct waves_codec_data *waves_codec = codec->private; + + comp_dbg(dev, "waves_codec_reset() start"); + + status = MaxxEffect_Reset(waves_codec->effect); + if (status) { + comp_err(dev, "waves_codec_reset() MaxxEffect_Reset returned %d", status); + ret = -EINVAL; + } + + if (ret) + comp_err(dev, "waves_codec_reset() failed %d", ret); + + comp_dbg(dev, "waves_codec_reset() done"); + return ret; +} + +static int waves_codec_free(struct processing_module *mod) +{ + /* codec is using codec_adapter method module_allocate_memory for all allocations + * codec_adapter will free it all on component free call + * nothing to do here + */ + comp_dbg(mod->dev, "waves_codec_free()"); + return 0; +} + +static int +waves_codec_set_configuration(struct processing_module *mod, uint32_t config_id, + enum module_cfg_fragment_position pos, uint32_t data_offset_size, + const uint8_t *fragment, size_t fragment_size, uint8_t *response, + size_t response_size) +{ + struct module_data *md = &mod->priv; + struct comp_dev *dev = mod->dev; + int ret; + + ret = module_set_configuration(mod, config_id, pos, data_offset_size, fragment, + fragment_size, response, response_size); + if (ret < 0) + return ret; + + /* return if more fragments are expected or if the module is not prepared */ + if ((pos != MODULE_CFG_FRAGMENT_LAST && pos != MODULE_CFG_FRAGMENT_SINGLE) || + md->state < MODULE_INITIALIZED) + return 0; + + /* whole configuration received, apply it now */ + ret = waves_codec_apply_config(dev); + if (ret) { + comp_err(dev, "waves_codec_set_configuration(): error %x: runtime config apply failed", + ret); + return ret; + } + + comp_dbg(dev, "waves_codec_set_configuration(): config applied"); + + return 0; +} + +static struct module_interface waves_interface = { + .init = waves_codec_init, + .prepare = waves_codec_prepare, + .process = waves_codec_process, + .set_configuration = waves_codec_set_configuration, + .reset = waves_codec_reset, + .free = waves_codec_free +}; + +DECLARE_MODULE_ADAPTER(waves_interface, waves_uuid, waves_tr); diff --git a/src/audio/module_adapter/module_adapter.c b/src/audio/module_adapter/module_adapter.c new file mode 100644 index 000000000000..30c06130aac5 --- /dev/null +++ b/src/audio/module_adapter/module_adapter.c @@ -0,0 +1,877 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Author: Marcin Rajwa + +/** + * \file + * \brief Module Adapter: Processing component aimed to work with external module libraries + * \author Marcin Rajwa + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * \brief Create a module adapter component. + * \param[in] drv - component driver pointer. + * \param[in] config - component ipc descriptor pointer. + * + * \return: a pointer to newly created module adapter component on success. NULL on error. + */ +struct comp_dev *module_adapter_new(const struct comp_driver *drv, + struct comp_ipc_config *config, + struct module_interface *interface, void *spec) +{ + int ret; + struct comp_dev *dev; + struct processing_module *mod; + unsigned char *data; + uint32_t size; + + switch (config->type) { + case SOF_COMP_MODULE_ADAPTER: + { + struct ipc_config_process *ipc_module_adapter = spec; + + size = ipc_module_adapter->size; + data = ipc_module_adapter->data; + break; + } + default: + return NULL; + } + + comp_cl_dbg(drv, "module_adapter_new() start"); + + if (!config) { + comp_cl_err(drv, "module_adapter_new(), wrong input params! drv = %x config = %x", + (uint32_t)drv, (uint32_t)config); + return NULL; + } + + dev = comp_alloc(drv, sizeof(*dev)); + if (!dev) { + comp_cl_err(drv, "module_adapter_new(), failed to allocate memory for comp_dev"); + return NULL; + } + dev->ipc_config = *config; + dev->drv = drv; + + mod = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, sizeof(*mod)); + if (!mod) { + comp_err(dev, "module_adapter_new(), failed to allocate memory for module"); + rfree(dev); + return NULL; + } + + mod->dev = dev; + + comp_set_drvdata(dev, mod); + list_init(&mod->sink_buffer_list); + + /* Copy initial config */ + if (size) { + ret = module_load_config(dev, data, size); + if (ret) { + comp_err(dev, "module_adapter_new() error %d: config loading has failed.", + ret); + goto err; + } + } + + /* Init processing module */ + ret = module_init(mod, interface); + if (ret) { + comp_err(dev, "module_adapter_new() %d: module initialization failed", + ret); + goto err; + } + + dev->state = COMP_STATE_READY; + + comp_dbg(dev, "module_adapter_new() done"); + return dev; +err: + rfree(mod); + rfree(dev); + return NULL; +} + +/* + * \brief Prepare the module + * \param[in] dev - component device pointer. + * + * \return integer representing either: + * 0 - success + * value < 0 - failure. + */ +int module_adapter_prepare(struct comp_dev *dev) +{ + int ret; + struct processing_module *mod = comp_get_drvdata(dev); + struct module_data *md = &mod->priv; + struct list_item *blist, *_blist; + uint32_t buff_periods; + uint32_t buff_size; /* size of local buffer */ + int i = 0; + + comp_dbg(dev, "module_adapter_prepare() start"); + + /* Are we already prepared? */ + ret = comp_set_state(dev, COMP_TRIGGER_PREPARE); + if (ret < 0) + return ret; + + if (ret == COMP_STATUS_STATE_ALREADY_SET) { + comp_warn(dev, "module_adapter_prepare(): module has already been prepared"); + return PPL_STATUS_PATH_STOP; + } + + /* Prepare module */ + ret = module_prepare(mod); + if (ret) { + comp_err(dev, "module_adapter_prepare() error %x: module prepare failed", + ret); + + return -EIO; + } + + mod->deep_buff_bytes = 0; + + /* Module is prepared, now we need to configure processing settings. + * If module internal buffer is not equal to natural multiple of pipeline + * buffer we have a situation where module adapter have to deep buffer certain amount + * of samples on its start (typically few periods) in order to regularly + * generate output once started (same situation happens for compress streams + * as well). + */ + if (md->mpd.in_buff_size > mod->period_bytes) { + buff_periods = (md->mpd.in_buff_size % mod->period_bytes) ? + (md->mpd.in_buff_size / mod->period_bytes) + 2 : + (md->mpd.in_buff_size / mod->period_bytes) + 1; + } else { + buff_periods = (mod->period_bytes % md->mpd.in_buff_size) ? + (mod->period_bytes / md->mpd.in_buff_size) + 2 : + (mod->period_bytes / md->mpd.in_buff_size) + 1; + } + + /* + * deep_buffer_bytes is a measure of how many bytes we need to send to the DAI before + * the module starts producing samples. In a normal copy() walk it might be possible that + * the first period_bytes copied to input_buffer might not be enough for the processing + * to begin. So, in order to prevent the DAI from starving, it needs to be fed zeroes until + * the module starts processing and generating output samples. + */ + if (md->mpd.in_buff_size != mod->period_bytes) + mod->deep_buff_bytes = MIN(mod->period_bytes, md->mpd.in_buff_size) * buff_periods; + + if (md->mpd.out_buff_size > mod->period_bytes) { + buff_periods = (md->mpd.out_buff_size % mod->period_bytes) ? + (md->mpd.out_buff_size / mod->period_bytes) + 2 : + (md->mpd.out_buff_size / mod->period_bytes) + 1; + } else { + buff_periods = (mod->period_bytes % md->mpd.out_buff_size) ? + (mod->period_bytes / md->mpd.out_buff_size) + 2 : + (mod->period_bytes / md->mpd.out_buff_size) + 1; + } + + /* + * It is possible that the module process() will produce more data than period_bytes but + * the DAI can consume only period_bytes every period. So, the local buffer needs to be + * large enough to save the produced output samples. + */ + buff_size = MAX(mod->period_bytes, md->mpd.out_buff_size) * buff_periods; + mod->output_buffer_size = buff_size; + + /* compute number of input buffers */ + list_for_item(blist, &dev->bsource_list) + mod->num_input_buffers++; + + /* compute number of output buffers */ + list_for_item(blist, &dev->bsink_list) + mod->num_output_buffers++; + + if (!mod->num_input_buffers || !mod->num_output_buffers) { + comp_err(dev, "module_adapter_prepare(): invalid number of source/sink buffers"); + return -EINVAL; + } + + /* allocate memory for input buffers */ + mod->input_buffers = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, + sizeof(*mod->input_buffers) * mod->num_input_buffers); + if (!mod->input_buffers) { + comp_err(dev, "module_adapter_prepare(): failed to allocate input buffers"); + return -ENOMEM; + } + + /* allocate memory for input buffer data */ + list_for_item(blist, &dev->bsource_list) { + size_t size = MAX(mod->deep_buff_bytes, mod->period_bytes); + + mod->input_buffers[i].data = rballoc(0, SOF_MEM_CAPS_RAM, size); + if (!mod->input_buffers[i].data) { + comp_err(mod->dev, "module_adapter_prepare(): Failed to alloc input buffer data"); + ret = -ENOMEM; + goto in_free; + } + i++; + } + + /* allocate memory for output buffers */ + mod->output_buffers = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, + sizeof(*mod->output_buffers) * mod->num_output_buffers); + if (!mod->output_buffers) { + comp_err(dev, "module_adapter_prepare(): failed to allocate output buffers"); + ret = -ENOMEM; + goto in_free; + } + + /* allocate memory for output buffer data */ + i = 0; + list_for_item(blist, &dev->bsink_list) { + mod->output_buffers[i].data = rballoc(0, SOF_MEM_CAPS_RAM, md->mpd.out_buff_size); + if (!mod->output_buffers[i].data) { + comp_err(mod->dev, "module_adapter_prepare(): Failed to alloc output buffer data"); + ret = -ENOMEM; + goto out_free; + } + i++; + } + + /* allocate buffer for all sinks */ + if (list_is_empty(&mod->sink_buffer_list)) { + for (i = 0; i < mod->num_output_buffers; i++) { + struct comp_buffer *buffer = buffer_alloc(buff_size, SOF_MEM_CAPS_RAM, + PLATFORM_DCACHE_ALIGN); + if (!buffer) { + comp_err(dev, "module_adapter_prepare(): failed to allocate local buffer"); + ret = -ENOMEM; + goto free; + } + list_item_append(&buffer->sink_list, &mod->sink_buffer_list); + buffer_set_params(buffer, mod->stream_params, BUFFER_UPDATE_FORCE); + buffer_reset_pos(buffer, NULL); + } + } else { + list_for_item(blist, &mod->sink_buffer_list) { + struct comp_buffer *buffer = container_of(blist, struct comp_buffer, + sink_list); + + ret = buffer_set_size(buffer, buff_size); + if (ret < 0) { + comp_err(dev, "module_adapter_prepare(): buffer_set_size() failed, buff_size = %u", + buff_size); + goto free; + } + buffer_set_params(buffer, mod->stream_params, BUFFER_UPDATE_FORCE); + buffer_reset_pos(buffer, NULL); + } + } + + comp_dbg(dev, "module_adapter_prepare() done"); + + return 0; + +free: + list_for_item_safe(blist, _blist, &mod->sink_buffer_list) { + struct comp_buffer *buffer = container_of(blist, struct comp_buffer, + sink_list); + + list_item_del(&buffer->sink_list); + buffer_free(buffer); + } +out_free: + for (i = 0; i < mod->num_output_buffers; i++) + rfree(mod->output_buffers[i].data); + + rfree(mod->output_buffers); + +in_free: + for (i = 0; i < mod->num_input_buffers; i++) + rfree(mod->input_buffers[i].data); + + rfree(mod->input_buffers); + return ret; +} + +int module_adapter_params(struct comp_dev *dev, struct sof_ipc_stream_params *params) +{ + int ret; + struct processing_module *mod = comp_get_drvdata(dev); + + ret = comp_verify_params(dev, 0, params); + if (ret < 0) { + comp_err(dev, "module_adapter_params(): comp_verify_params() failed."); + return ret; + } + + /* allocate stream_params each time */ + if (mod->stream_params) + rfree(mod->stream_params); + + mod->stream_params = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, + sizeof(*mod->stream_params) + params->ext_data_length); + if (!mod->stream_params) + return -ENOMEM; + + ret = memcpy_s(mod->stream_params, sizeof(struct sof_ipc_stream_params), + params, sizeof(struct sof_ipc_stream_params)); + if (ret < 0) + return ret; + + if (params->ext_data_length) { + ret = memcpy_s((uint8_t *)mod->stream_params->data, + params->ext_data_length, + (uint8_t *)params->data, + params->ext_data_length); + if (ret < 0) + return ret; + } + + mod->period_bytes = params->sample_container_bytes * + params->channels * params->rate / 1000; + return 0; +} + +/** + * Function to copy from source buffer to the module buffer + * @source: source audio buffer stream + * @buff: pointer to the module input buffer + * @buff_size: size of the module input buffer + * @bytes: number of bytes available in the source buffer + */ +static void +ca_copy_from_source_to_module(const struct audio_stream *source, void *buff, uint32_t buff_size, + size_t bytes) +{ + /* head_size - available data until end of source buffer */ + const int without_wrap = audio_stream_bytes_without_wrap(source, source->r_ptr); + uint32_t head_size = MIN(bytes, without_wrap); + /* tail_size - residual data to be copied starting from the beginning of the buffer */ + uint32_t tail_size = bytes - head_size; + + /* copy head_size to module buffer */ + memcpy(buff, source->r_ptr, MIN(buff_size, head_size)); + + /* copy residual samples after wrap */ + if (tail_size) + memcpy((char *)buff + head_size, + audio_stream_wrap(source, (char *)source->r_ptr + head_size), + MIN(buff_size, tail_size)); +} + +/** + * Function to copy processed samples from the module buffer to sink buffer + * @sink: sink audio buffer stream + * @buff: pointer to the module output buffer + * @bytes: number of bytes available in the module output buffer + */ +static void +ca_copy_from_module_to_sink(const struct audio_stream *sink, void *buff, size_t bytes) +{ + /* head_size - free space until end of sink buffer */ + const int without_wrap = audio_stream_bytes_without_wrap(sink, sink->w_ptr); + uint32_t head_size = MIN(bytes, without_wrap); + /* tail_size - rest of the bytes that needs to be written + * starting from the beginning of the buffer + */ + uint32_t tail_size = bytes - head_size; + + /* copy "head_size" samples to sink buffer */ + memcpy(sink->w_ptr, buff, MIN(sink->size, head_size)); + + /* copy rest of the samples after buffer wrap */ + if (tail_size) + memcpy(audio_stream_wrap(sink, (char *)sink->w_ptr + head_size), + (char *)buff + head_size, MIN(sink->size, tail_size)); +} + +/** + * \brief Generate zero samples of "bytes" size for the sink. + * \param[in] sink - a pointer to sink buffer. + * \param[in] bytes - number of zero bytes to produce. + * + * \return: none. + */ +static void generate_zeroes(struct comp_buffer *sink, uint32_t bytes) +{ + uint32_t tmp, copy_bytes = bytes; + void *ptr; + + while (copy_bytes) { + ptr = audio_stream_wrap(&sink->stream, sink->stream.w_ptr); + tmp = audio_stream_bytes_without_wrap(&sink->stream, + ptr); + tmp = MIN(tmp, copy_bytes); + ptr = (char *)ptr + tmp; + copy_bytes -= tmp; + } + comp_update_buffer_produce(sink, bytes); +} + +static void module_copy_samples(struct comp_dev *dev, struct comp_buffer *src_buffer, + struct comp_buffer *sink_buffer, uint32_t produced) +{ + struct processing_module *mod = comp_get_drvdata(dev); + struct comp_copy_limits cl; + uint32_t copy_bytes; + + if (!produced && !mod->deep_buff_bytes) { + comp_dbg(dev, "module_copy_samples(): nothing processed in this call"); + /* + * No data produced anything in this period but there still be data in the buffer + * to copy to sink + */ + if (audio_stream_get_avail_bytes(&src_buffer->stream) >= mod->period_bytes) + goto copy_period; + + return; + } + + if (mod->deep_buff_bytes) { + if (mod->deep_buff_bytes >= audio_stream_get_avail_bytes(&src_buffer->stream)) { + generate_zeroes(sink_buffer, mod->period_bytes); + return; + } + + comp_dbg(dev, "module_copy_samples(): deep buffering has ended after gathering %d bytes of processed data", + audio_stream_get_avail_bytes(&src_buffer->stream)); + mod->deep_buff_bytes = 0; + } + +copy_period: + comp_get_copy_limits_with_lock(src_buffer, sink_buffer, &cl); + copy_bytes = cl.frames * cl.source_frame_bytes; + if (!copy_bytes) + return; + audio_stream_copy(&src_buffer->stream, 0, &sink_buffer->stream, 0, + copy_bytes / mod->stream_params->sample_container_bytes); + buffer_stream_writeback(sink_buffer, copy_bytes); + + comp_update_buffer_produce(sink_buffer, copy_bytes); + comp_update_buffer_consume(src_buffer, copy_bytes); +} + +static void module_adapter_process_output(struct comp_dev *dev) +{ + struct processing_module *mod = comp_get_drvdata(dev); + struct module_data *md = &mod->priv; + struct comp_buffer *sink; + struct list_item *blist; + int i; + + /* + * When a module produces only period_bytes every period, skip copying the produced + * samples to the intermediate buffer and copy to the sink buffer directly + */ + if (md->mpd.out_buff_size == mod->period_bytes) { + i = 0; + list_for_item(blist, &dev->bsink_list) { + sink = container_of(blist, struct comp_buffer, source_list); + ca_copy_from_module_to_sink(&sink->stream, mod->output_buffers[i].data, + mod->output_buffers[i].size); + audio_stream_produce(&sink->stream, mod->output_buffers[i].size); + mod->output_buffers[i].size = 0; + i++; + } + return; + } + + /* + * copy all produced output samples to output buffers. This loop will do nothing when + * there are no samples produced. + */ + i = 0; + list_for_item(blist, &mod->sink_buffer_list) { + if (mod->output_buffers[i].size > 0) { + struct comp_buffer *buffer; + + buffer = container_of(blist, struct comp_buffer, sink_list); + ca_copy_from_module_to_sink(&buffer->stream, mod->output_buffers[i].data, + mod->output_buffers[i].size); + audio_stream_produce(&buffer->stream, mod->output_buffers[i].size); + } + i++; + } + + /* copy from all output local buffers to sink buffers */ + i = 0; + list_for_item(blist, &dev->bsink_list) { + struct list_item *_blist; + int j = 0; + + sink = container_of(blist, struct comp_buffer, source_list); + list_for_item(_blist, &mod->sink_buffer_list) { + if (i == j) { + struct comp_buffer *src_buffer; + + src_buffer = container_of(_blist, struct comp_buffer, sink_list); + module_copy_samples(dev, src_buffer, sink, + mod->output_buffers[i].size); + mod->output_buffers[i].size = 0; + break; + } + j++; + } + } +} + +int module_adapter_copy(struct comp_dev *dev) +{ + struct comp_buffer *source; + struct comp_buffer *sink; + struct processing_module *mod = comp_get_drvdata(dev); + struct list_item *blist; + size_t size = MAX(mod->deep_buff_bytes, mod->period_bytes); + uint32_t min_free_frames = UINT_MAX; + int ret, i = 0; + + /* get the least number of free frames from all sinks */ + list_for_item(blist, &mod->sink_buffer_list) { + sink = container_of(blist, struct comp_buffer, sink_list); + min_free_frames = MIN(min_free_frames, + audio_stream_get_free_frames(&sink->stream)); + } + + /* copy source samples into input buffer */ + list_for_item(blist, &dev->bsource_list) { + uint32_t bytes_to_process; + int frames, source_frame_bytes; + + source = container_of(blist, struct comp_buffer, sink_list); + + source = buffer_acquire(source); + frames = MIN(min_free_frames, audio_stream_get_avail_frames(&source->stream)); + source_frame_bytes = audio_stream_frame_bytes(&source->stream); + source = buffer_release(source); + + bytes_to_process = frames * source_frame_bytes; + + buffer_stream_invalidate(source, bytes_to_process); + mod->input_buffers[i].size = bytes_to_process; + mod->input_buffers[i].consumed = 0; + ca_copy_from_source_to_module(&source->stream, mod->input_buffers[i].data, + bytes_to_process, bytes_to_process); + i++; + } + + ret = module_process(mod, mod->input_buffers, mod->num_input_buffers, + mod->output_buffers, mod->num_output_buffers); + if (ret) { + if (ret != -ENOSPC && ret != -ENODATA) { + comp_err(dev, "module_adapter_copy() error %x: module processing failed", + ret); + goto out; + } + + ret = 0; + } + + /* consume from all input buffers */ + i = 0; + list_for_item(blist, &dev->bsource_list) { + source = container_of(blist, struct comp_buffer, sink_list); + comp_update_buffer_consume(source, mod->input_buffers[i].consumed); + bzero(mod->input_buffers[i].data, size); + mod->input_buffers[i].size = 0; + mod->input_buffers[i].consumed = 0; + i++; + } + + module_adapter_process_output(dev); + + return 0; +out: + for (i = 0; i < mod->num_output_buffers; i++) + mod->output_buffers[i].size = 0; + + for (i = 0; i < mod->num_input_buffers; i++) { + bzero(mod->input_buffers[i].data, size); + mod->input_buffers[i].size = 0; + mod->input_buffers[i].consumed = 0; + } + + return ret; +} + +static int module_adapter_set_params(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) +{ + struct processing_module *mod = comp_get_drvdata(dev); + struct module_data *md = &mod->priv; + enum module_cfg_fragment_position pos; + uint32_t data_offset_size; + static uint32_t size; + + comp_dbg(dev, "module_adapter_set_params(): num_of_elem %d, elem remain %d msg_index %u", + cdata->num_elems, cdata->elems_remaining, cdata->msg_index); + + /* set the fragment position, data offset and config data size */ + if (!cdata->msg_index) { + size = cdata->num_elems + cdata->elems_remaining; + data_offset_size = size; + if (cdata->elems_remaining) + pos = MODULE_CFG_FRAGMENT_FIRST; + else + pos = MODULE_CFG_FRAGMENT_SINGLE; + } else { + data_offset_size = size - (cdata->num_elems + cdata->elems_remaining); + if (cdata->elems_remaining) + pos = MODULE_CFG_FRAGMENT_MIDDLE; + else + pos = MODULE_CFG_FRAGMENT_LAST; + } + + /* IPC3 does not use config_id, so pass 0 for config ID as it will be ignored anyway */ + if (md->ops->set_configuration) + return md->ops->set_configuration(mod, 0, pos, data_offset_size, + (const uint8_t *)cdata->data->data, + cdata->num_elems, NULL, 0); + + comp_warn(dev, "module_adapter_set_params(): no set_configuration op set for %d", + dev_comp_id(dev)); + return 0; +} + +static int module_adapter_ctrl_set_data(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) +{ + int ret; + struct processing_module *mod = comp_get_drvdata(dev); + + comp_dbg(dev, "module_adapter_ctrl_set_data() start, state %d, cmd %d", + mod->priv.state, cdata->cmd); + + /* Check version from ABI header */ + if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { + comp_err(dev, "module_adapter_ctrl_set_data(): ABI mismatch!"); + return -EINVAL; + } + + switch (cdata->cmd) { + case SOF_CTRL_CMD_ENUM: + comp_err(dev, "module_adapter_ctrl_set_data(): set enum is not implemented"); + ret = -EIO; + break; + case SOF_CTRL_CMD_BINARY: + ret = module_adapter_set_params(dev, cdata); + break; + default: + comp_err(dev, "module_adapter_ctrl_set_data error: unknown set data command"); + ret = -EINVAL; + break; + } + + return ret; +} + +/* Used to pass standard and bespoke commands (with data) to component */ +int module_adapter_cmd(struct comp_dev *dev, int cmd, void *data, int max_data_size) +{ + struct sof_ipc_ctrl_data *cdata = ASSUME_ALIGNED(data, 4); + struct processing_module *mod = comp_get_drvdata(dev); + struct module_data *md = &mod->priv; + int ret = 0; + + comp_dbg(dev, "module_adapter_cmd() %d start", cmd); + + switch (cmd) { + case COMP_CMD_SET_DATA: + ret = module_adapter_ctrl_set_data(dev, cdata); + break; + case COMP_CMD_GET_DATA: + comp_err(dev, "module_adapter_cmd() get_data not implemented yet."); + ret = -ENODATA; + break; + case COMP_CMD_SET_VALUE: + /* + * IPC3 does not use config_id, so pass 0 for config ID as it will be ignored + * anyway. Also, pass the 0 as the fragment size as it is not relevant for the + * SET_VALUE command. + */ + if (md->ops->set_configuration) + ret = md->ops->set_configuration(mod, 0, MODULE_CFG_FRAGMENT_SINGLE, 0, + (const uint8_t *)cdata, 0, NULL, 0); + break; + case COMP_CMD_GET_VALUE: + /* + * IPC3 does not use config_id, so pass 0 for config ID as it will be ignored + * anyway. Also, pass the 0 as the fragment size and data offset as they are not + * relevant for the GET_VALUE command. + */ + if (md->ops->get_configuration) + ret = md->ops->get_configuration(mod, 0, 0, (uint8_t *)cdata, 0); + break; + default: + comp_err(dev, "module_adapter_cmd() error: unknown command"); + ret = -EINVAL; + break; + } + + comp_dbg(dev, "module_adapter_cmd() done"); + return ret; +} + +int module_adapter_trigger(struct comp_dev *dev, int cmd) +{ + comp_dbg(dev, "module_adapter_trigger(): cmd %d", cmd); + + return comp_set_state(dev, cmd); +} + +int module_adapter_reset(struct comp_dev *dev) +{ + int ret, i; + struct processing_module *mod = comp_get_drvdata(dev); + struct list_item *blist; + + comp_dbg(dev, "module_adapter_reset(): resetting"); + + ret = module_reset(mod); + if (ret) { + comp_err(dev, "module_adapter_reset(): failed with error: %d", ret); + } + + for (i = 0; i < mod->num_output_buffers; i++) + rfree(mod->output_buffers[i].data); + + rfree(mod->output_buffers); + + for (i = 0; i < mod->num_input_buffers; i++) + rfree(mod->input_buffers[i].data); + + rfree(mod->input_buffers); + + mod->num_input_buffers = 0; + mod->num_output_buffers = 0; + + list_for_item(blist, &mod->sink_buffer_list) { + struct comp_buffer *buffer = container_of(blist, struct comp_buffer, + sink_list); + + buffer_zero(buffer); + } + + rfree(mod->stream_params); + mod->stream_params = NULL; + + comp_dbg(dev, "module_adapter_reset(): done"); + + return comp_set_state(dev, COMP_TRIGGER_RESET); +} + +void module_adapter_free(struct comp_dev *dev) +{ + int ret; + struct processing_module *mod = comp_get_drvdata(dev); + struct list_item *blist, *_blist; + + comp_dbg(dev, "module_adapter_free(): start"); + + ret = module_free(mod); + if (ret) + comp_err(dev, "module_adapter_free(): failed with error: %d", ret); + + list_for_item_safe(blist, _blist, &mod->sink_buffer_list) { + struct comp_buffer *buffer = container_of(blist, struct comp_buffer, + sink_list); + + list_item_del(&buffer->sink_list); + buffer_free(buffer); + } + + rfree(mod); + rfree(dev); +} + +#if CONFIG_IPC_MAJOR_4 +int module_set_large_config(struct comp_dev *dev, uint32_t param_id, bool first_block, + bool last_block, uint32_t data_offset_size, char *data) +{ + struct processing_module *mod = comp_get_drvdata(dev); + struct module_data *md = &mod->priv; + enum module_cfg_fragment_position pos; + size_t fragment_size; + + /* set fragment position */ + if (first_block) { + if (last_block) + pos = MODULE_CFG_FRAGMENT_SINGLE; + else + pos = MODULE_CFG_FRAGMENT_FIRST; + } else { + if (!last_block) + pos = MODULE_CFG_FRAGMENT_MIDDLE; + else + pos = MODULE_CFG_FRAGMENT_LAST; + } + + switch (pos) { + case MODULE_CFG_FRAGMENT_SINGLE: + fragment_size = data_offset_size; + break; + case MODULE_CFG_FRAGMENT_MIDDLE: + case MODULE_CFG_FRAGMENT_FIRST: + fragment_size = SOF_IPC_MSG_MAX_SIZE; + break; + case MODULE_CFG_FRAGMENT_LAST: + fragment_size = md->new_cfg_size - data_offset_size; + break; + default: + comp_err(dev, "module_set_large_config(): invalid fragment position"); + return -EINVAL; + } + + if (md->ops->set_configuration) + return md->ops->set_configuration(mod, param_id, pos, data_offset_size, + (const uint8_t *)data, + fragment_size, NULL, 0); + return 0; +} + +int module_get_large_config(struct comp_dev *dev, uint32_t param_id, bool first_block, + bool last_block, uint32_t *data_offset_size, char *data) +{ + struct processing_module *mod = comp_get_drvdata(dev); + struct module_data *md = &mod->priv; + size_t fragment_size; + + /* set fragment size */ + if (first_block) { + if (last_block) + fragment_size = md->cfg.size; + else + fragment_size = SOF_IPC_MSG_MAX_SIZE; + } else { + if (!last_block) + fragment_size = SOF_IPC_MSG_MAX_SIZE; + else + fragment_size = md->cfg.size - *data_offset_size; + } + + if (md->ops->get_configuration) + return md->ops->get_configuration(mod, param_id, data_offset_size, + (uint8_t *)data, fragment_size); + return 0; +} +#else +int module_set_large_config(struct comp_dev *dev, uint32_t param_id, bool first_block, + bool last_block, uint32_t data_offset, char *data) +{ + return 0; +} + +int module_get_large_config(struct comp_dev *dev, uint32_t param_id, bool first_block, + bool last_block, uint32_t *data_offset, char *data) +{ + return 0; +} +#endif diff --git a/src/include/sof/audio/codec_adapter/codec/cadence.h b/src/include/sof/audio/module_adapter/codec/cadence.h similarity index 100% rename from src/include/sof/audio/codec_adapter/codec/cadence.h rename to src/include/sof/audio/module_adapter/codec/cadence.h diff --git a/src/include/sof/audio/codec_adapter/codec/generic.h b/src/include/sof/audio/module_adapter/codec/generic.h similarity index 100% rename from src/include/sof/audio/codec_adapter/codec/generic.h rename to src/include/sof/audio/module_adapter/codec/generic.h diff --git a/src/include/sof/audio/codec_adapter/codec/passthrough.h b/src/include/sof/audio/module_adapter/codec/passthrough.h similarity index 100% rename from src/include/sof/audio/codec_adapter/codec/passthrough.h rename to src/include/sof/audio/module_adapter/codec/passthrough.h diff --git a/src/include/sof/audio/codec_adapter/codec/waves.h b/src/include/sof/audio/module_adapter/codec/waves.h similarity index 100% rename from src/include/sof/audio/codec_adapter/codec/waves.h rename to src/include/sof/audio/module_adapter/codec/waves.h diff --git a/src/include/sof/audio/codec_adapter/codec_adapter.h b/src/include/sof/audio/module_adapter/codec_adapter.h similarity index 92% rename from src/include/sof/audio/codec_adapter/codec_adapter.h rename to src/include/sof/audio/module_adapter/codec_adapter.h index f648c1818858..80276425976f 100644 --- a/src/include/sof/audio/codec_adapter/codec_adapter.h +++ b/src/include/sof/audio/module_adapter/codec_adapter.h @@ -8,7 +8,7 @@ #ifndef __SOF_AUDIO_CODEC_ADAPTER__ #define __SOF_AUDIO_CODEC_ADAPTER__ -#include +#include #define MAX_BLOB_SIZE 8192 /*****************************************************************************/ diff --git a/src/include/sof/audio/module_adapter/module/cadence.h b/src/include/sof/audio/module_adapter/module/cadence.h new file mode 100644 index 000000000000..31867acbd237 --- /dev/null +++ b/src/include/sof/audio/module_adapter/module/cadence.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2019 Intel Corporation. All rights reserved. + * + * Author: Marcin Rajwa + */ + +#ifndef __SOF_AUDIO_CADENCE_CODEC__ +#define __SOF_AUDIO_CADENCE_CODEC__ + +#include +#include +#include +#include + +#define LIB_NAME_MAX_LEN 30 +#define LIB_NO_ERROR XA_NO_ERROR +#define LIB_IS_FATAL_ERROR(e) ((e) & XA_FATAL_ERROR) +#define CODEC_GET_API_ID(id) ((id) & 0xFF) + +/*****************************************************************************/ +/* Cadence API functions */ +/*****************************************************************************/ +extern xa_codec_func_t cadence_api_function; +extern xa_codec_func_t xa_aac_dec; +extern xa_codec_func_t xa_bsac_dec; +extern xa_codec_func_t xa_dabplus_dec; +extern xa_codec_func_t xa_drm_dec; +extern xa_codec_func_t xa_mp3_dec; +extern xa_codec_func_t xa_sbc_dec; +extern xa_codec_func_t xa_vorbis_dec; +extern xa_codec_func_t xa_src_pp; + +/*****************************************************************************/ +/* Cadence private data types */ +/*****************************************************************************/ +struct cadence_api { + uint32_t id; + xa_codec_func_t *api; +}; + +struct cadence_codec_data { + char name[LIB_NAME_MAX_LEN]; + void *self; + xa_codec_func_t *api; + void *mem_tabs; + uint32_t api_id; + struct module_config setup_cfg; +}; + +#endif /* __SOF_AUDIO_CADENCE_CODEC__ */ diff --git a/src/include/sof/audio/module_adapter/module/generic.h b/src/include/sof/audio/module_adapter/module/generic.h new file mode 100644 index 000000000000..50786095c40b --- /dev/null +++ b/src/include/sof/audio/module_adapter/module/generic.h @@ -0,0 +1,333 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2020 Intel Corporation. All rights reserved. + * + * + * \file generic.h + * \brief Generic Module API header file + * \author Marcin Rajwa + * + */ + +#ifndef __SOF_AUDIO_MODULE_GENERIC__ +#define __SOF_AUDIO_MODULE_GENERIC__ + +#include +#include +#include +#include + +#define module_get_private_data(mod) (mod->priv.private) +#define MAX_BLOB_SIZE 8192 + +#define API_CALL(cd, cmd, sub_cmd, value, ret) \ + do { \ + ret = (cd)->api((cd)->self, \ + (cmd), \ + (sub_cmd), \ + (value)); \ + } while (0) + +#define DECLARE_MODULE_ADAPTER(adapter, uuid, tr) \ +static struct comp_dev *adapter_shim_new(const struct comp_driver *drv, \ + struct comp_ipc_config *config, \ + void *spec) \ +{ \ + return module_adapter_new(drv, config, &(adapter), spec);\ +} \ +\ +static const struct comp_driver comp_module_adapter = { \ + .type = SOF_COMP_MODULE_ADAPTER, \ + .uid = SOF_RT_UUID(uuid), \ + .tctx = &(tr), \ + .ops = { \ + .create = adapter_shim_new, \ + .prepare = module_adapter_prepare, \ + .params = module_adapter_params, \ + .copy = module_adapter_copy, \ + .cmd = module_adapter_cmd, \ + .trigger = module_adapter_trigger, \ + .reset = module_adapter_reset, \ + .free = module_adapter_free, \ + .set_large_config = module_set_large_config,\ + .get_large_config = module_get_large_config,\ + }, \ +}; \ +\ +static SHARED_DATA struct comp_driver_info comp_module_adapter_info = { \ + .drv = &comp_module_adapter, \ +}; \ +\ +UT_STATIC void sys_comp_module_##adapter##_init(void) \ +{ \ + comp_register(platform_shared_get(&comp_module_adapter_info, \ + sizeof(comp_module_adapter_info))); \ +} \ +\ +DECLARE_MODULE(sys_comp_module_##adapter##_init) + +struct processing_module; + +/** + * \enum module_cfg_fragment_position + * \brief Fragment position in config + * MODULE_CFG_FRAGMENT_FIRST: first fragment of the large configuration + * MODULE_CFG_FRAGMENT_SINGLE: only fragment of the configuration + * MODULE_CFG_FRAGMENT_LAST: last fragment of the configuration + * MODULE_CFG_FRAGMENT_MIDDLE: intermediate fragment of the large configuration + */ +enum module_cfg_fragment_position { + MODULE_CFG_FRAGMENT_MIDDLE, + MODULE_CFG_FRAGMENT_FIRST, + MODULE_CFG_FRAGMENT_LAST, + MODULE_CFG_FRAGMENT_SINGLE, +}; + +/** + * \enum module_processing_mode + * MODULE_PROCESSING_NORMAL: Indicates that module is expected to apply its custom processing on + * the input signal + * MODULE_PROCESSING_BYPASS: Indicates that module is expected to skip custom processing on + * the input signal and act as a passthrough component + */ + +enum module_processing_mode { + MODULE_PROCESSING_NORMAL, + MODULE_PROCESSING_BYPASS, +}; + +/** + * \struct input_stream_buffer + * \brief Input stream buffer + */ +struct input_stream_buffer { + void *data; /* data stream buffer */ + uint32_t size; /* size of data in the buffer */ + uint32_t consumed; /* number of bytes consumed by the module */ + + /* Indicates end of stream condition has occurred on the input stream */ + bool end_of_stream; +}; + +/** + * \struct output_stream_buffer + * \brief Output stream buffer + */ +struct output_stream_buffer { + void *data; /* data stream buffer */ + uint32_t size; /* size of data in the buffer */ +}; + +/*****************************************************************************/ +/* Module generic data types */ +/*****************************************************************************/ +/** + * \struct module_interface + * \brief 3rd party processing module interface + */ +struct module_interface { + /** + * Module specific initialization procedure, called as part of + * module_adapter component creation in .new() + */ + int (*init)(struct processing_module *mod); + /** + * Module specific prepare procedure, called as part of module_adapter + * component preparation in .prepare() + */ + int (*prepare)(struct processing_module *mod); + /** + * Module specific processing procedure, called as part of module_adapter + * component copy in .copy(). This procedure is responsible to consume + * samples provided by the module_adapter and produce/output the processed + * ones back to module_adapter. + */ + int (*process)(struct processing_module *mod, struct input_stream_buffer *input_buffers, + int num_input_buffers, struct output_stream_buffer *output_buffers, + int num_output_buffers); + + /** + * Set module configuration for the given configuration ID + * + * If the complete configuration message is greater than MAX_BLOB_SIZE bytes, the + * transmission will be split into several smaller fragments. + * In this case the ADSP System will perform multiple calls to SetConfiguration() until + * completion of the configuration message sending. + * \note config_id indicates ID of the configuration message only on the first fragment + * sending, otherwise it is set to 0. + */ + int (*set_configuration)(struct processing_module *mod, + uint32_t config_id, + enum module_cfg_fragment_position pos, uint32_t data_offset_size, + const uint8_t *fragment, size_t fragment_size, uint8_t *response, + size_t response_size); + + /** + * Get module runtime configuration for the given configuration ID + * + * If the complete configuration message is greater than MAX_BLOB_SIZE bytes, the + * transmission will be split into several smaller fragments. + * In this case the ADSP System will perform multiple calls to GetConfiguration() until + * completion of the configuration message retrieval. + * \note config_id indicates ID of the configuration message only on the first fragment + * retrieval, otherwise it is set to 0. + */ + int (*get_configuration)(struct processing_module *mod, + uint32_t config_id, uint32_t data_offset_size, + const uint8_t *fragment, size_t fragment_size); + + /** + * Set processing mode for the module + */ + int (*set_processing_mode)(struct processing_module *mod, + enum module_processing_mode mode); + + /** + * Get the current processing mode for the module + */ + enum module_processing_mode (*get_processing_mode)(struct processing_module *mod); + + /** + * Module specific reset procedure, called as part of module_adapter component + * reset in .reset(). This should reset all parameters to their initial stage + * but leave allocated memory intact. + */ + int (*reset)(struct processing_module *mod); + /** + * Module specific free procedure, called as part of module_adapter component + * free in .free(). This should free all memory allocated by module. + */ + int (*free)(struct processing_module *mod); +}; + +/** + * \enum module_state + * \brief Module-specific states + */ +enum module_state { + MODULE_DISABLED, /**< Module isn't initialized yet or has been freed.*/ + MODULE_INITIALIZED, /**< Module initialized or reset. */ + MODULE_IDLE, /**< Module is idle now. */ + MODULE_PROCESSING, /**< Module is processing samples now. */ +}; + +/** + * \struct module_param + * \brief Module TLV parameters container - used for both config types. + * For example if one want to set the sample_rate to 16 [kHz] and this + * parameter was assigned to id 0x01, its max size is four bytes then the + * configuration filed should look like this (note little-endian format): + * 0x01 0x00 0x00 0x00, 0x0C 0x00 0x00 0x00, 0x10 0x00 0x00 0x00. + */ +struct module_param { + /** + * Specifies the unique id of a parameter. For example the parameter + * sample_rate may have an id of 0x01. + */ + uint32_t id; + uint32_t size; /**< The size of whole parameter - id + size + data */ + int32_t data[]; /**< A pointer to memory where config is stored.*/ +}; + +/** + * \struct module_config + * \brief Module config container, used for both config types. + */ +struct module_config { + size_t size; /**< Specifies the size of whole config */ + bool avail; /**< Marks config as available to use.*/ + void *data; /**< tlv config, a pointer to memory where config is stored. */ +}; + +/** + * \struct module_memory + * \brief module memory block - used for every memory allocated by module + */ +struct module_memory { + void *ptr; /**< A pointr to particular memory block */ + struct list_item mem_list; /**< list of memory allocated by module */ +}; + +/** + * \struct module_processing_data + * \brief Processing data shared between particular module & module_adapter + */ +struct module_processing_data { + uint32_t in_buff_size; /**< Specifies the size of module input buffer. */ + uint32_t out_buff_size; /**< Specifies the size of module output buffer.*/ + uint32_t avail; /**< Specifies how much data is available for module to process.*/ + uint32_t produced; /**< Specifies how much data the module produced in its last task.*/ + uint32_t consumed; /**< Specified how much data the module consumed in its last task */ + uint32_t init_done; /**< Specifies if the module initialization is finished */ + void *in_buff; /**< A pointer to module input buffer. */ + void *out_buff; /**< A pointer to module output buffer. */ +}; + +/** private, runtime module data */ +struct module_data { + enum module_state state; + size_t new_cfg_size; /**< size of new module config data */ + void *private; /**< self object, memory tables etc here */ + void *runtime_params; + struct module_config cfg; /**< module configuration data */ + struct module_interface *ops; /**< module specific operations */ + struct module_memory memory; /**< memory allocated by module */ + struct module_processing_data mpd; /**< shared data comp <-> module */ +}; + +/* module_adapter private, runtime data */ +struct processing_module { + struct module_data priv; /**< module private data */ + struct sof_ipc_stream_params *stream_params; + struct list_item sink_buffer_list; /* list of sink buffers to save produced output */ + + /* + * This is a temporary change in order to support the trace messages in the modules. This + * will be removed once the trace API is updated. + */ + struct comp_dev *dev; + uint32_t period_bytes; /** pipeline period bytes */ + uint32_t deep_buff_bytes; /**< copy start threshold */ + uint32_t output_buffer_size; /**< size of local buffer to save produced samples */ + struct input_stream_buffer *input_buffers; + struct output_stream_buffer *output_buffers; + uint32_t num_input_buffers; /**< number of input buffers */ + uint32_t num_output_buffers; /**< number of output buffers */ +}; + +/*****************************************************************************/ +/* Module generic interfaces */ +/*****************************************************************************/ +int module_load_config(struct comp_dev *dev, void *cfg, size_t size); +int module_init(struct processing_module *mod, struct module_interface *interface); +void *module_allocate_memory(struct processing_module *mod, uint32_t size, uint32_t alignment); +int module_free_memory(struct processing_module *mod, void *ptr); +void module_free_all_memory(struct processing_module *mod); +int module_prepare(struct processing_module *mod); +int module_process(struct processing_module *mod, struct input_stream_buffer *input_buffers, + int num_input_buffers, struct output_stream_buffer *output_buffers, + int num_output_buffers); +int module_reset(struct processing_module *mod); +int module_free(struct processing_module *mod); +int module_set_configuration(struct processing_module *mod, + uint32_t config_id, + enum module_cfg_fragment_position pos, size_t data_offset_size, + const uint8_t *fragment, size_t fragment_size, uint8_t *response, + size_t response_size); + +struct comp_dev *module_adapter_new(const struct comp_driver *drv, + struct comp_ipc_config *config, + struct module_interface *interface, void *spec); +int module_adapter_prepare(struct comp_dev *dev); +int module_adapter_params(struct comp_dev *dev, struct sof_ipc_stream_params *params); +int module_adapter_copy(struct comp_dev *dev); +int module_adapter_cmd(struct comp_dev *dev, int cmd, void *data, int max_data_size); +int module_adapter_trigger(struct comp_dev *dev, int cmd); +void module_adapter_free(struct comp_dev *dev); +int module_adapter_reset(struct comp_dev *dev); +int module_set_large_config(struct comp_dev *dev, uint32_t param_id, bool first_block, + bool last_block, uint32_t data_offset, char *data); +int module_get_large_config(struct comp_dev *dev, uint32_t param_id, bool first_block, + bool last_block, uint32_t *data_offset, char *data); + +#endif /* __SOF_AUDIO_MODULE_GENERIC__ */