From 57c5910c5a45a1626e11900dd53ae1f4087f4383 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Thu, 3 Apr 2025 13:42:08 +0300 Subject: [PATCH 1/2] Audio: template_comp: Add a new SOF template component This patch contains all needed to add a new minimal SOF component to FW build for TGL and more recent platforms plus sof-testbench4 simulation. the component name is template_comp and it is easy to duplicate for new component development from scratch. The component supports one switch kcontrol. When enabled the component swaps or reverses the channels order if there are two or more channels. Signed-off-by: Seppo Ingalsuo --- src/audio/CMakeLists.txt | 3 + src/audio/Kconfig | 2 + src/audio/template_comp/CMakeLists.txt | 15 ++ src/audio/template_comp/Kconfig | 11 + src/audio/template_comp/llext/CMakeLists.txt | 7 + src/audio/template_comp/llext/llext.toml.h | 6 + src/audio/template_comp/template-generic.c | 230 +++++++++++++++++++ src/audio/template_comp/template-ipc3.c | 96 ++++++++ src/audio/template_comp/template-ipc4.c | 60 +++++ src/audio/template_comp/template.c | 210 +++++++++++++++++ src/audio/template_comp/template.h | 110 +++++++++ src/audio/template_comp/template_comp.toml | 21 ++ src/include/sof/audio/component.h | 1 + tools/rimage/config/lnl.toml.h | 4 + tools/rimage/config/mtl.toml.h | 4 + tools/rimage/config/ptl.toml.h | 4 + tools/rimage/config/tgl-h.toml | 18 +- tools/rimage/config/tgl.toml | 18 +- tools/testbench/utils_ipc4.c | 1 + uuid-registry.txt | 1 + 20 files changed, 820 insertions(+), 2 deletions(-) create mode 100644 src/audio/template_comp/CMakeLists.txt create mode 100644 src/audio/template_comp/Kconfig create mode 100644 src/audio/template_comp/llext/CMakeLists.txt create mode 100644 src/audio/template_comp/llext/llext.toml.h create mode 100644 src/audio/template_comp/template-generic.c create mode 100644 src/audio/template_comp/template-ipc3.c create mode 100644 src/audio/template_comp/template-ipc4.c create mode 100644 src/audio/template_comp/template.c create mode 100644 src/audio/template_comp/template.h create mode 100644 src/audio/template_comp/template_comp.toml diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index 53626f45419d..dfa145ad566d 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -115,6 +115,9 @@ if(NOT CONFIG_COMP_MODULE_SHARED_LIBRARY_BUILD) list(APPEND base_files host-legacy.c) sof_list_append_ifdef(CONFIG_COMP_DAI base_files dai-legacy.c) endif() + if(CONFIG_COMP_TEMPLATE_COMP) + add_subdirectory(template_comp) + endif() endif() ### Common files (also used in shared library build) diff --git a/src/audio/Kconfig b/src/audio/Kconfig index d0be938077df..f76a99c2335e 100644 --- a/src/audio/Kconfig +++ b/src/audio/Kconfig @@ -164,6 +164,8 @@ rsource "mfcc/Kconfig" rsource "codec/Kconfig" +rsource "template_comp/Kconfig" + endmenu # "Audio components" menu "Data formats" diff --git a/src/audio/template_comp/CMakeLists.txt b/src/audio/template_comp/CMakeLists.txt new file mode 100644 index 000000000000..d21be2f9ab88 --- /dev/null +++ b/src/audio/template_comp/CMakeLists.txt @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: BSD-3-Clause + +if(CONFIG_COMP_TEMPLATE_COMP STREQUAL "m") + add_subdirectory(llext ${PROJECT_BINARY_DIR}/template_comp_llext) + add_dependencies(app template_comp) +else() + add_local_sources(sof template.c) + add_local_sources(sof template-generic.c) + + if(CONFIG_IPC_MAJOR_3) + add_local_sources(sof template-ipc3.c) + elseif(CONFIG_IPC_MAJOR_4) + add_local_sources(sof template-ipc4.c) + endif() +endif() diff --git a/src/audio/template_comp/Kconfig b/src/audio/template_comp/Kconfig new file mode 100644 index 000000000000..29d385cbbe98 --- /dev/null +++ b/src/audio/template_comp/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: BSD-3-Clause + +config COMP_TEMPLATE_COMP + tristate "Template_comp example component" + default y + help + Select for template_comp component. Reason for existence + is to provide a minimal component example and use as + placeholder in processing pipelines. As example processing + it swaps or reverses the channels when the switch control + is enabled. diff --git a/src/audio/template_comp/llext/CMakeLists.txt b/src/audio/template_comp/llext/CMakeLists.txt new file mode 100644 index 000000000000..9e97d91f28fc --- /dev/null +++ b/src/audio/template_comp/llext/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2025 Intel Corporation. +# SPDX-License-Identifier: Apache-2.0 + +sof_llext_build("template_comp" + SOURCES ../template_comp.c + LIB openmodules +) diff --git a/src/audio/template_comp/llext/llext.toml.h b/src/audio/template_comp/llext/llext.toml.h new file mode 100644 index 000000000000..4808c9af8f42 --- /dev/null +++ b/src/audio/template_comp/llext/llext.toml.h @@ -0,0 +1,6 @@ +#include +#define LOAD_TYPE "2" +#include "../template_comp.toml" + +[module] +count = __COUNTER__ diff --git a/src/audio/template_comp/template-generic.c b/src/audio/template_comp/template-generic.c new file mode 100644 index 000000000000..39617e667b90 --- /dev/null +++ b/src/audio/template_comp/template-generic.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include "template.h" + +#if CONFIG_FORMAT_S16LE +/** + * template_comp_s16() - Process S16_LE format. + * @mod: Pointer to module data. + * @source: Source for PCM samples data. + * @sink: Sink for PCM samples data. + * @frames: Number of audio data frames to process. + * + * This is the processing function for 16-bit signed integer PCM formats. The + * audio samples in every frame are re-order to channels order defined in + * component data channel_map[]. + * + * Return: Value zero for success, otherwise an error code. + */ +static int template_comp_s16(const struct processing_module *mod, + struct sof_source *source, + struct sof_sink *sink, + uint32_t frames) +{ + struct template_comp_comp_data *cd = module_get_private_data(mod); + int16_t *x, *x_start, *x_end; + int16_t *y, *y_start, *y_end; + size_t size; + int x_size, y_size; + int source_samples_without_wrap; + int samples_without_wrap; + int samples = frames * cd->channels; + int bytes = frames * cd->frame_bytes; + int ret; + int ch; + int i; + + /* Get pointer to source data in circular buffer, get buffer start and size to + * check for wrap. The size in bytes is converted to number of s16 samples to + * control the samples process loop. If the number of bytes requested is not + * possible, an error is returned. + */ + ret = source_get_data(source, bytes, (void const **)&x, (void const **)&x_start, &size); + if (ret) + return ret; + + x_size = size >> 1; /* Bytes to number of s16 samples */ + + /* Similarly get pointer to sink data in circular buffer, buffer start and size. */ + ret = sink_get_buffer(sink, bytes, (void **)&y, (void **)&y_start, &size); + if (ret) + return ret; + + y_size = size >> 1; /* Bytes to number of s16 samples */ + + /* Set helper pointers to buffer end for wrap check. Then loop until all + * samples are processed. + */ + x_end = x_start + x_size; + y_end = y_start + y_size; + while (samples) { + /* Find out samples to process before first wrap or end of data. */ + source_samples_without_wrap = x_end - x; + samples_without_wrap = y_end - y; + samples_without_wrap = MIN(samples_without_wrap, source_samples_without_wrap); + samples_without_wrap = MIN(samples_without_wrap, samples); + + /* Since the example processing is for frames of audio channels, process + * with step of channels count. + */ + for (i = 0; i < samples_without_wrap; i += cd->channels) { + /* In inner loop process the frame. As example re-arrange the channels + * as defined in array channel_map[]. + */ + for (ch = 0; ch < cd->channels; ch++) { + *y = *(x + cd->channel_map[ch]); + y++; + } + x += cd->channels; + } + + /* One of the buffers needs a wrap (or end of data), so check for wrap */ + x = (x >= x_end) ? x - x_size : x; + y = (y >= y_end) ? y - y_size : y; + + /* Update processed samples count for next loop iteration. */ + samples -= samples_without_wrap; + } + + /* Update the source and sink for bytes consumed and produced. Return success. */ + source_release_data(source, bytes); + sink_commit_buffer(sink, bytes); + return 0; +} +#endif /* CONFIG_FORMAT_S16LE */ + +#if CONFIG_FORMAT_S32LE || CONFIG_FORMAT_S32LE +/** + * template_comp_s32() - Process S32_LE or S24_4LE format. + * @mod: Pointer to module data. + * @source: Source for PCM samples data. + * @sink: Sink for PCM samples data. + * @frames: Number of audio data frames to process. + * + * Processing function for signed integer 32-bit PCM formats. The same + * function works for s24 and s32 formats since the samples values are + * not modified in computation. The audio samples in every frame are + * re-order to channels order defined in component data channel_map[]. + * + * Return: Value zero for success, otherwise an error code. + */ +static int template_comp_s32(const struct processing_module *mod, + struct sof_source *source, + struct sof_sink *sink, + uint32_t frames) +{ + struct template_comp_comp_data *cd = module_get_private_data(mod); + int32_t *x, *x_start, *x_end; + int32_t *y, *y_start, *y_end; + size_t size; + int x_size, y_size; + int source_samples_without_wrap; + int samples_without_wrap; + int samples = frames * cd->channels; + int bytes = frames * cd->frame_bytes; + int ret; + int ch; + int i; + + /* Get pointer to source data in circular buffer, get buffer start and size to + * check for wrap. The size in bytes is converted to number of s16 samples to + * control the samples process loop. If the number of bytes requested is not + * possible, an error is returned. + */ + ret = source_get_data(source, bytes, (void const **)&x, (void const **)&x_start, &size); + if (ret) + return ret; + + x_size = size >> 2; /* Bytes to number of s32 samples */ + + /* Similarly get pointer to sink data in circular buffer, buffer start and size. */ + ret = sink_get_buffer(sink, bytes, (void **)&y, (void **)&y_start, &size); + if (ret) + return ret; + + y_size = size >> 2; /* Bytes to number of s32 samples */ + + /* Set helper pointers to buffer end for wrap check. Then loop until all + * samples are processed. + */ + x_end = x_start + x_size; + y_end = y_start + y_size; + while (samples) { + /* Find out samples to process before first wrap or end of data. */ + source_samples_without_wrap = x_end - x; + samples_without_wrap = y_end - y; + samples_without_wrap = MIN(samples_without_wrap, source_samples_without_wrap); + samples_without_wrap = MIN(samples_without_wrap, samples); + + /* Since the example processing is for frames of audio channels, process + * with step of channels count. + */ + for (i = 0; i < samples_without_wrap; i += cd->channels) { + /* In inner loop process the frame. As example re-arrange the channels + * as defined in array channel_map[]. + */ + for (ch = 0; ch < cd->channels; ch++) { + *y = *(x + cd->channel_map[ch]); + y++; + } + x += cd->channels; + } + + /* One of the buffers needs a wrap (or end of data), so check for wrap */ + x = (x >= x_end) ? x - x_size : x; + y = (y >= y_end) ? y - y_size : y; + + /* Update processed samples count for next loop iteration. */ + samples -= samples_without_wrap; + } + + /* Update the source and sink for bytes consumed and produced. Return success. */ + source_release_data(source, bytes); + sink_commit_buffer(sink, bytes); + return 0; +} +#endif /* CONFIG_FORMAT_S32LE || CONFIG_FORMAT_S24LE */ + +/* This struct array defines the used processing functions for + * the PCM formats + */ +const struct template_comp_proc_fnmap template_comp_proc_fnmap[] = { +#if CONFIG_FORMAT_S16LE + { SOF_IPC_FRAME_S16_LE, template_comp_s16 }, +#endif +#if CONFIG_FORMAT_S24LE + { SOF_IPC_FRAME_S24_4LE, template_comp_s32 }, +#endif +#if CONFIG_FORMAT_S32LE + { SOF_IPC_FRAME_S32_LE, template_comp_s32 }, +#endif +}; + +/** + * template_comp_find_proc_func() - Find suitable processing function. + * @src_fmt: Enum value for PCM format. + * + * This function finds the suitable processing function to use for + * the used PCM format. If not found, return NULL. + * + * Return: Pointer to processing function for the requested PCM format. + */ +template_comp_func template_comp_find_proc_func(enum sof_ipc_frame src_fmt) +{ + int i; + + /* Find suitable processing function from map */ + for (i = 0; i < ARRAY_SIZE(template_comp_proc_fnmap); i++) + if (src_fmt == template_comp_proc_fnmap[i].frame_fmt) + return template_comp_proc_fnmap[i].template_comp_proc_func; + + return NULL; +} diff --git a/src/audio/template_comp/template-ipc3.c b/src/audio/template_comp/template-ipc3.c new file mode 100644 index 000000000000..757afb8bf923 --- /dev/null +++ b/src/audio/template_comp/template-ipc3.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. + +#include +#include +#include "template.h" + +LOG_MODULE_DECLARE(template_comp, CONFIG_SOF_LOG_LEVEL); + +/* This function handles the real-time controls. The ALSA controls have the + * param_id set to indicate the control type. The control ID, from topology, + * is used to separate the controls instances of same type. In control payload + * the num_elems defines to how many channels the control is applied to. + */ +__cold int template_comp_set_config(struct processing_module *mod, uint32_t param_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 sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment; + struct template_comp_comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + + assert_can_be_cold(); + + comp_dbg(dev, "template_comp_set_config()"); + + switch (cdata->cmd) { + case SOF_CTRL_CMD_SWITCH: + if (cdata->index != 0) { + comp_err(dev, "Illegal switch control index = %d.", cdata->index); + return -EINVAL; + } + + if (cdata->num_elems != 1) { + comp_err(dev, "Illegal switch control num_elems = %d.", cdata->num_elems); + return -EINVAL; + } + + cd->enable = cdata->chanv[0].value; + comp_info(dev, "Setting enable = %d.", cd->enable); + return 0; + + case SOF_CTRL_CMD_ENUM: + comp_err(dev, "Illegal enum control, no support in this component."); + return -EINVAL; + case SOF_CTRL_CMD_BINARY: + comp_err(dev, "Illegal bytes control, no support in this component."); + return -EINVAL; + } + + comp_err(dev, "Illegal control, unknown type."); + return -EINVAL; +} + +__cold int template_comp_get_config(struct processing_module *mod, + uint32_t config_id, uint32_t *data_offset_size, + uint8_t *fragment, size_t fragment_size) +{ + struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment; + struct template_comp_comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + + assert_can_be_cold(); + + comp_info(dev, "template_comp_get_config()"); + + switch (cdata->cmd) { + case SOF_CTRL_CMD_SWITCH: + if (cdata->index != 0) { + comp_err(dev, "Illegal switch control index = %d.", cdata->index); + return -EINVAL; + } + + if (cdata->num_elems != 1) { + comp_err(dev, "Illegal switch control num_elems = %d.", cdata->num_elems); + return -EINVAL; + } + + cdata->chanv[0].value = cd->enable; + return 0; + + case SOF_CTRL_CMD_ENUM: + comp_err(dev, "Illegal enum control, no support in this component."); + return -EINVAL; + + case SOF_CTRL_CMD_BINARY: + comp_err(dev, "Illegal bytes control, no support in this component."); + return -EINVAL; + } + + comp_err(dev, "Illegal control, unknown type."); + return -EINVAL; +} diff --git a/src/audio/template_comp/template-ipc4.c b/src/audio/template_comp/template-ipc4.c new file mode 100644 index 000000000000..e11a6695ef9d --- /dev/null +++ b/src/audio/template_comp/template-ipc4.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. + +#include +#include +#include "template.h" + +LOG_MODULE_DECLARE(template_comp, CONFIG_SOF_LOG_LEVEL); + +/* IPC4 controls handler */ +__cold int template_comp_set_config(struct processing_module *mod, + uint32_t param_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 sof_ipc4_control_msg_payload *ctl = (struct sof_ipc4_control_msg_payload *)fragment; + struct template_comp_comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + + assert_can_be_cold(); + + switch (param_id) { + case SOF_IPC4_SWITCH_CONTROL_PARAM_ID: + comp_dbg(dev, "Switch control id = %d, num_elems = %d.", ctl->id, ctl->num_elems); + if (ctl->id != 0) { + comp_err(dev, "Illegal switch control id = %d.", ctl->id); + return -EINVAL; + } + + if (ctl->num_elems != 1) { + comp_err(dev, "Illegal switch control num_elems = %d.", ctl->num_elems); + return -EINVAL; + } + + cd->enable = ctl->chanv[0].value; + comp_info(dev, "Setting enable = %d.", cd->enable); + return 0; + + case SOF_IPC4_ENUM_CONTROL_PARAM_ID: + comp_err(dev, "Illegal enum control, no support in this component."); + return -EINVAL; + } + + comp_err(mod->dev, "Illegal bytes control, no support in this component."); + return -EINVAL; +} + +/* Not used in IPC4 systems, if IPC4 only component, omit .get_configuration set */ +__cold int template_comp_get_config(struct processing_module *mod, + uint32_t config_id, uint32_t *data_offset_size, + uint8_t *fragment, size_t fragment_size) +{ + assert_can_be_cold(); + return 0; +} diff --git a/src/audio/template_comp/template.c b/src/audio/template_comp/template.c new file mode 100644 index 000000000000..06f702645531 --- /dev/null +++ b/src/audio/template_comp/template.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. + +#include +#include +#include +#include +#include +#include "template.h" + +/* UUID identifies the components. Use e.g. command uuidgen from package + * uuid-runtime, add it to uuid-registry.txt in SOF top level. + */ +SOF_DEFINE_REG_UUID(template_comp); + +/* Creates logging data for the component */ +LOG_MODULE_REGISTER(template_comp, CONFIG_SOF_LOG_LEVEL); + +/* Creates the compont trace. Traces show in trace console the component + * info, warning, and error messages. + */ +DECLARE_TR_CTX(template_comp_tr, SOF_UUID(template_comp_uuid), LOG_LEVEL_INFO); + +/** + * template_comp_init() - Initialize the template component. + * @mod: Pointer to module data. + * + * This function is called when the instance is created. The + * macro __cold informs that the code that is non-critical + * is loaded to slower but large DRAM. + * + * Return: Zero if success, otherwise error code. + */ +__cold static int template_comp_init(struct processing_module *mod) +{ + struct module_data *md = &mod->priv; + struct comp_dev *dev = mod->dev; + struct template_comp_comp_data *cd; + + comp_info(dev, "template_comp_init()"); + + cd = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, sizeof(*cd)); + if (!cd) + return -ENOMEM; + + md->private = cd; + return 0; +} + +/** + * template_comp_process() - The audio data processing function. + * @mod: Pointer to module data. + * @sources: Pointer to audio samples data sources array. + * @num_of_sources: Number of sources in the array. + * @sinks: Pointer to audio samples data sinks array. + * @num_of_sinks: Number of sinks in the array. + * + * This is the processing function that is called for scheduled + * pipelines. The processing is controlled by the enable switch. + * + * Return: Zero if success, otherwise error code. + */ +static int template_comp_process(struct processing_module *mod, + struct sof_source **sources, + int num_of_sources, + struct sof_sink **sinks, + int num_of_sinks) +{ + struct template_comp_comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + struct sof_source *source = sources[0]; + struct sof_sink *sink = sinks[0]; + int frames = source_get_data_frames_available(source); + int sink_frames = sink_get_free_frames(sink); + + comp_dbg(dev, "template_comp_process()"); + + frames = MIN(frames, sink_frames); + if (cd->enable) + /* Process the data with the channels swap example function. */ + return cd->template_comp_func(mod, source, sink, frames); + + /* Just copy from source to sink. */ + source_to_sink_copy(source, sink, true, frames * cd->frame_bytes); + return 0; +} + +/** + * template_comp_prepare() - Prepare the component for processing. + * @mod: Pointer to module data. + * @sources: Pointer to audio samples data sources array. + * @num_of_sources: Number of sources in the array. + * @sinks: Pointer to audio samples data sinks array. + * @num_of_sinks: Number of sinks in the array. + * + * Function prepare is called just before the pipeline is started. In + * this case the audio format parameters are for better code performance + * saved to component data to avoid to find out them in process. The + * processing function pointer is set to process the current audio format. + * + * Return: Value zero if success, otherwise error code. + */ +static int template_comp_prepare(struct processing_module *mod, + struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) +{ + struct template_comp_comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + enum sof_ipc_frame source_format; + int i; + + comp_dbg(dev, "template_comp_prepare()"); + + if (num_of_sources != 1 || num_of_sinks != 1) + return -EINVAL; + + /* get source data format */ + cd->frame_bytes = source_get_frame_bytes(sources[0]); + cd->channels = source_get_channels(sources[0]); + source_format = source_get_frm_fmt(sources[0]); + + /* Initialize channels order for reversing */ + for (i = 0; i < cd->channels; i++) + cd->channel_map[i] = cd->channels - i - 1; + + cd->template_comp_func = template_comp_find_proc_func(source_format); + if (!cd->template_comp_func) { + comp_err(dev, "No processing function found for format %d.", + source_format); + return -EINVAL; + } + + return 0; +} + +/** + * template_comp_reset() - Reset the component. + * @mod: Pointer to module data. + * + * The component reset is called when pipeline is stopped. The reset + * should return the component to same state as init. + * + * Return: Value zero, always success. + */ +static int template_comp_reset(struct processing_module *mod) +{ + struct template_comp_comp_data *cd = module_get_private_data(mod); + + comp_dbg(mod->dev, "template_comp_reset()"); + memset(cd, 0, sizeof(*cd)); + return 0; +} + +/** + * template_comp_free() - Free dynamic allocations. + * @mod: Pointer to module data. + * + * Component free is called when the pipelines are deleted. All + * dynamic allocations need to be freed here. The macro __cold + * instructs the build to locate this performance wise non-critical + * function to large and slower DRAM. + * + * Return: Value zero, always success. + */ +__cold static int template_comp_free(struct processing_module *mod) +{ + struct template_comp_comp_data *cd = module_get_private_data(mod); + + assert_can_be_cold(); + + comp_dbg(mod->dev, "template_comp_free()"); + rfree(cd); + return 0; +} + +/* This defines the module operations */ +static const struct module_interface template_comp_interface = { + .init = template_comp_init, + .prepare = template_comp_prepare, + .process = template_comp_process, + .set_configuration = template_comp_set_config, + .get_configuration = template_comp_get_config, + .reset = template_comp_reset, + .free = template_comp_free +}; + +/* This controls build of the module. If COMP_MODULE is selected in kconfig + * this is build as dynamically loadable module. + */ +#if CONFIG_COMP_TEMPLATE_COMP_MODULE + +#include +#include +#include + +SOF_LLEXT_MOD_ENTRY(template_comp, &template_comp_interface); + +static const struct sof_man_module_manifest mod_manifest __section(".module") __used = + SOF_LLEXT_MODULE_MANIFEST("TEMPLATE", template_comp_llext_entry, 1, + SOF_REG_UUID(template_comp), 40); + +SOF_LLEXT_BUILDINFO; + +#else + +DECLARE_MODULE_ADAPTER(template_comp_interface, template_comp_uuid, template_comp_tr); +SOF_MODULE_INIT(template_comp, sys_comp_module_template_comp_interface_init); + +#endif diff --git a/src/audio/template_comp/template.h b/src/audio/template_comp/template.h new file mode 100644 index 000000000000..667d95702c10 --- /dev/null +++ b/src/audio/template_comp/template.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. + * + */ +#ifndef __SOF_AUDIO_TEMPLATE_COMP_H__ +#define __SOF_AUDIO_TEMPLATE_COMP_H__ + +#include +#include +#include + +/** + * struct template_comp_func - Function call pointer for process function + * @mod: Pointer to module data. + * @source: Source for PCM samples data. + * @sink: Sink for PCM samples data. + * @frames: Number of audio data frames to process. + */ +typedef int (*template_comp_func)(const struct processing_module *mod, + struct sof_source *source, + struct sof_sink *sink, + uint32_t frames); + +/* Template_Comp component private data */ + +/** + * struct template_comp_comp_data + * @template_comp_func: Pointer to used processing function. + * @channels_order[]: Vector with desired sink channels order. + * @source_format: Source samples format. + * @frame_bytes: Number of bytes in an audio frame. + * @channels: Channels count. + * @enable: Control processing on/off, on - reorder channels + */ +struct template_comp_comp_data { + template_comp_func template_comp_func; + int channel_map[PLATFORM_MAX_CHANNELS]; + int source_format; + int frame_bytes; + int channels; + bool enable; +}; + +/** + * struct template_comp_proc_fnmap - processing functions for frame formats + * @frame_fmt: Current frame format + * @template_comp_proc_func: Function pointer for the suitable processing function + */ +struct template_comp_proc_fnmap { + enum sof_ipc_frame frame_fmt; + template_comp_func template_comp_proc_func; +}; + +/** + * template_comp_find_proc_func() - Find suitable processing function. + * @src_fmt: Enum value for PCM format. + * + * This function finds the suitable processing function to use for + * the used PCM format. If not found, return NULL. + * + * Return: Pointer to processing function for the requested PCM format. + */ +template_comp_func template_comp_find_proc_func(enum sof_ipc_frame src_fmt); + +/** + * template_comp_set_config() - Handle controls set + * @mod: Pointer to module data. + * @param_id: Id to know control type, used to know ALSA control type. + * @pos: Position of the fragment in the large message. + * @data_offset_size: Size of the whole configuration if it is the first or only + * fragment. Otherwise it is offset of the fragment. + * @fragment: Message payload data. + * @fragment_size: Size of this fragment. + * @response_size: Size of response. + * + * This function handles the real-time controls. The ALSA controls have the + * param_id set to indicate the control type. The control ID, from topology, + * is used to separate the controls instances of same type. In control payload + * the num_elems defines to how many channels the control is applied to. + * + * Return: Zero if success, otherwise error code. + */ +int template_comp_set_config(struct processing_module *mod, + uint32_t param_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); + +/** + * template_comp_set_config() - Handle controls get + * @mod: Pointer to module data. + * @config_id: Configuration ID. + * @data_offset_size: Size of the whole configuration if it is the first or only + * fragment. Otherwise it is offset of the fragment. + * @fragment: Message payload data. + * @fragment_size: Size of this fragment. + * + * This function is used for controls get. + * + * Return: Zero if success, otherwise error code. + */ +int template_comp_get_config(struct processing_module *mod, + uint32_t config_id, uint32_t *data_offset_size, + uint8_t *fragment, size_t fragment_size); + +#endif // __SOF_AUDIO_TEMPLATE_COMP_H__ diff --git a/src/audio/template_comp/template_comp.toml b/src/audio/template_comp/template_comp.toml new file mode 100644 index 000000000000..c4d28b125fe3 --- /dev/null +++ b/src/audio/template_comp/template_comp.toml @@ -0,0 +1,21 @@ +#ifndef LOAD_TYPE +#define LOAD_TYPE "0" +#endif + + REM # Template component module config + [[module.entry]] + name = "TEMPLATE" + uuid = UUIDREG_STR_TEMPLATE_COMP + affinity_mask = "0x1" + instance_count = "40" + domain_types = "0" + load_type = LOAD_TYPE + module_type = "9" + auto_start = "0" + sched_caps = [1, 0x00008000] + REM # pin = [dir, type, sample rate, size, container, channel-cfg] + pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] + REM # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] + mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] + + index = __COUNTER__ diff --git a/src/include/sof/audio/component.h b/src/include/sof/audio/component.h index b6976973b5c9..acfb12607aab 100644 --- a/src/include/sof/audio/component.h +++ b/src/include/sof/audio/component.h @@ -900,6 +900,7 @@ void sys_comp_module_selector_interface_init(void); void sys_comp_module_src_interface_init(void); void sys_comp_module_src_lite_interface_init(void); void sys_comp_module_tdfb_interface_init(void); +void sys_comp_module_template_comp_interface_init(void); void sys_comp_module_volume_interface_init(void); void sys_comp_module_tester_interface_init(void); diff --git a/tools/rimage/config/lnl.toml.h b/tools/rimage/config/lnl.toml.h index 90ac20abbace..6d1e0f0ef04a 100644 --- a/tools/rimage/config/lnl.toml.h +++ b/tools/rimage/config/lnl.toml.h @@ -134,5 +134,9 @@ #include