Skip to content

Commit 01bd5c4

Browse files
committed
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 <seppo.ingalsuo@linux.intel.com>
1 parent 150c9ff commit 01bd5c4

20 files changed

Lines changed: 822 additions & 2 deletions

src/audio/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ if(NOT CONFIG_COMP_MODULE_SHARED_LIBRARY_BUILD)
115115
list(APPEND base_files host-legacy.c)
116116
sof_list_append_ifdef(CONFIG_COMP_DAI base_files dai-legacy.c)
117117
endif()
118+
if(CONFIG_COMP_TEMPLATE_COMP)
119+
add_subdirectory(template_comp)
120+
endif()
118121
endif()
119122

120123
### Common files (also used in shared library build)

src/audio/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ rsource "mfcc/Kconfig"
164164

165165
rsource "codec/Kconfig"
166166

167+
rsource "template_comp/Kconfig"
168+
167169
endmenu # "Audio components"
168170

169171
menu "Data formats"
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# SPDX-License-Identifier: BSD-3-Clause
2+
3+
if(CONFIG_COMP_TEMPLATE_COMP STREQUAL "m")
4+
add_subdirectory(llext ${PROJECT_BINARY_DIR}/template_comp_llext)
5+
add_dependencies(app template_comp)
6+
else()
7+
add_local_sources(sof template.c)
8+
add_local_sources(sof template-generic.c)
9+
10+
if(CONFIG_IPC_MAJOR_3)
11+
add_local_sources(sof template-ipc3.c)
12+
elseif(CONFIG_IPC_MAJOR_4)
13+
add_local_sources(sof template-ipc4.c)
14+
endif()
15+
endif()

src/audio/template_comp/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# SPDX-License-Identifier: BSD-3-Clause
2+
3+
config COMP_TEMPLATE_COMP
4+
tristate "Template_comp example component"
5+
default y
6+
help
7+
Select for template_comp component. Reason for existence
8+
is to provide a minimal component example and use as
9+
placeholder in processing pipelines. As example processing
10+
it swaps or reverses the channels when the switch control
11+
is enabled.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright (c) 2025 Intel Corporation.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
sof_llext_build("template_comp"
5+
SOURCES ../template_comp.c
6+
LIB openmodules
7+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include <tools/rimage/config/platform.toml>
2+
#define LOAD_TYPE "2"
3+
#include "../template_comp.toml"
4+
5+
[module]
6+
count = __COUNTER__
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
//
3+
// Copyright(c) 2025 Intel Corporation.
4+
5+
#include <sof/audio/module_adapter/module/generic.h>
6+
#include <sof/audio/component.h>
7+
#include <sof/audio/sink_api.h>
8+
#include <sof/audio/sink_source_utils.h>
9+
#include <sof/audio/source_api.h>
10+
#include <stdint.h>
11+
#include "template.h"
12+
13+
#if CONFIG_FORMAT_S16LE
14+
/**
15+
* template_comp_s16() - Process S16_LE format.
16+
* @mod: Pointer to module data.
17+
* @source: Source for PCM samples data.
18+
* @sink: Sink for PCM samples data.
19+
* @frames: Number of audio data frames to process.
20+
*
21+
* This is the processing function for 16-bit signed integer PCM formats. The
22+
* audio samples in every frame are re-order to channels order defined in
23+
* component data channels_order[].
24+
*
25+
* Return: Value zero for success, otherwise an error code.
26+
*/
27+
static int template_comp_s16(const struct processing_module *mod,
28+
struct sof_source *source,
29+
struct sof_sink *sink,
30+
uint32_t frames)
31+
{
32+
struct template_comp_comp_data *cd = module_get_private_data(mod);
33+
int16_t *x, *x_start, *x_end;
34+
int16_t *y, *y_start, *y_end;
35+
size_t size;
36+
int x_size, y_size;
37+
int source_samples_without_wrap;
38+
int samples_without_wrap;
39+
int samples = frames * cd->channels;
40+
int bytes = frames * cd->frame_bytes;
41+
int ret;
42+
int ch;
43+
int i;
44+
45+
/* Get pointer to source data in circular buffer, get buffer start and size to
46+
* check for wrap. The size in bytes is converted to number of s16 samples to
47+
* control the samples process loop. If the number of bytes requested is not
48+
* possible, an error is returned.
49+
*/
50+
ret = source_get_data(source, bytes, (void const **)&x, (void const **)&x_start, &size);
51+
x_size = size >> 1; /* Bytes to number of s16 samples */
52+
if (ret)
53+
return ret;
54+
55+
/* Similarly get pointer to sink data in circular buffer, buffer start and size. */
56+
ret = sink_get_buffer(sink, bytes, (void **)&y, (void **)&y_start, &size);
57+
y_size = size >> 1; /* Bytes to number of s16 samples */
58+
if (ret)
59+
return ret;
60+
61+
/* Set helper pointers to buffer end for wrap check. Then loop until all
62+
* samples are processed.
63+
*/
64+
x_end = x_start + x_size;
65+
y_end = y_start + y_size;
66+
while (samples) {
67+
/* Find out samples to process before first wrap or end of data. */
68+
source_samples_without_wrap = x_end - x;
69+
samples_without_wrap = y_end - y;
70+
samples_without_wrap = MIN(samples_without_wrap, source_samples_without_wrap);
71+
samples_without_wrap = MIN(samples_without_wrap, samples);
72+
73+
/* Since the example processing is for frames of audio channels, process
74+
* with step of channels count.
75+
*/
76+
for (i = 0; i < samples_without_wrap; i += cd->channels) {
77+
/* In inner loop process the frame. As example re-arrange the channels
78+
* as defined in array channels_order[].
79+
*/
80+
for (ch = 0; ch < cd->channels; ch++) {
81+
*y = *(x + cd->channels_order[ch]);
82+
y++;
83+
}
84+
x += cd->channels;
85+
}
86+
87+
/* One of the buffers needs a wrap (or end of data), so check for wrap */
88+
x = (x >= x_end) ? x - x_size : x;
89+
y = (y >= y_end) ? y - y_size : y;
90+
91+
/* Update processed samples count for next loop iteration. */
92+
samples -= samples_without_wrap;
93+
}
94+
95+
/* Update the source and sink for bytes consumed and produced. Return success. */
96+
source_release_data(source, bytes);
97+
sink_commit_buffer(sink, bytes);
98+
return 0;
99+
}
100+
#endif /* CONFIG_FORMAT_S16LE */
101+
102+
#if CONFIG_FORMAT_S32LE || CONFIG_FORMAT_S32LE
103+
/**
104+
* template_comp_s32() - Process S32_LE or S24_4LE format.
105+
* @mod: Pointer to module data.
106+
* @source: Source for PCM samples data.
107+
* @sink: Sink for PCM samples data.
108+
* @frames: Number of audio data frames to process.
109+
*
110+
* Processing function for signed integer 32-bit PCM formats. The same
111+
* function works for s24 and s32 formats since the samples values are
112+
* not modified in computation. The audio samples in every frame are
113+
* re-order to channels order defined in component data channels_order[].
114+
*
115+
* Return: Value zero for success, otherwise an error code.
116+
*/
117+
static int template_comp_s32(const struct processing_module *mod,
118+
struct sof_source *source,
119+
struct sof_sink *sink,
120+
uint32_t frames)
121+
{
122+
struct template_comp_comp_data *cd = module_get_private_data(mod);
123+
int32_t *x, *x_start, *x_end;
124+
int32_t *y, *y_start, *y_end;
125+
size_t size;
126+
int x_size, y_size;
127+
int source_samples_without_wrap;
128+
int samples_without_wrap;
129+
int samples = frames * cd->channels;
130+
int bytes = frames * cd->frame_bytes;
131+
int ret;
132+
int ch;
133+
int i;
134+
135+
/* Get pointer to source data in circular buffer, get buffer start and size to
136+
* check for wrap. The size in bytes is converted to number of s16 samples to
137+
* control the samples process loop. If the number of bytes requested is not
138+
* possible, an error is returned.
139+
*/
140+
ret = source_get_data(source, bytes, (void const **)&x, (void const **)&x_start, &size);
141+
x_size = size >> 2; /* Bytes to number of s32 samples */
142+
if (ret)
143+
return ret;
144+
145+
/* Similarly get pointer to sink data in circular buffer, buffer start and size. */
146+
ret = sink_get_buffer(sink, bytes, (void **)&y, (void **)&y_start, &size);
147+
y_size = size >> 2; /* Bytes to number of s32 samples */
148+
if (ret)
149+
return ret;
150+
151+
/* Set helper pointers to buffer end for wrap check. Then loop until all
152+
* samples are processed.
153+
*/
154+
x_end = x_start + x_size;
155+
y_end = y_start + y_size;
156+
while (samples) {
157+
/* Find out samples to process before first wrap or end of data. */
158+
source_samples_without_wrap = x_end - x;
159+
samples_without_wrap = y_end - y;
160+
samples_without_wrap = MIN(samples_without_wrap, source_samples_without_wrap);
161+
samples_without_wrap = MIN(samples_without_wrap, samples);
162+
163+
/* Since the example processing is for frames of audio channels, process
164+
* with step of channels count.
165+
*/
166+
for (i = 0; i < samples_without_wrap; i += cd->channels) {
167+
/* In inner loop process the frame. As example re-arrange the channels
168+
* as defined in array channels_order[].
169+
*/
170+
for (ch = 0; ch < cd->channels; ch++) {
171+
*y = *(x + cd->channels_order[ch]);
172+
y++;
173+
}
174+
x += cd->channels;
175+
}
176+
177+
/* One of the buffers needs a wrap (or end of data), so check for wrap */
178+
x = (x >= x_end) ? x - x_size : x;
179+
y = (y >= y_end) ? y - y_size : y;
180+
181+
/* Update processed samples count for next loop iteration. */
182+
samples -= samples_without_wrap;
183+
}
184+
185+
/* Update the source and sink for bytes consumed and produced. Return success. */
186+
source_release_data(source, bytes);
187+
sink_commit_buffer(sink, bytes);
188+
return 0;
189+
}
190+
#endif /* CONFIG_FORMAT_S32LE || CONFIG_FORMAT_S24LE */
191+
192+
/* This struct array defines the used processing functions for
193+
* the PCM formats
194+
*/
195+
const struct template_comp_proc_fnmap template_comp_proc_fnmap[] = {
196+
#if CONFIG_FORMAT_S16LE
197+
{ SOF_IPC_FRAME_S16_LE, template_comp_s16 },
198+
#endif
199+
#if CONFIG_FORMAT_S24LE
200+
{ SOF_IPC_FRAME_S24_4LE, template_comp_s32 },
201+
#endif
202+
#if CONFIG_FORMAT_S32LE
203+
{ SOF_IPC_FRAME_S32_LE, template_comp_s32 },
204+
#endif
205+
};
206+
207+
/**
208+
* template_comp_find_proc_func() - Find suitable processing function.
209+
* @src_fmt: Enum value for PCM format.
210+
*
211+
* This function finds the suitable processing function to use for
212+
* the used PCM format. If not found, return NULL.
213+
*
214+
* Return: Pointer to processing function for the requested PCM format.
215+
*/
216+
template_comp_func template_comp_find_proc_func(enum sof_ipc_frame src_fmt)
217+
{
218+
int i;
219+
220+
/* Find suitable processing function from map */
221+
for (i = 0; i < ARRAY_SIZE(template_comp_proc_fnmap); i++)
222+
if (src_fmt == template_comp_proc_fnmap[i].frame_fmt)
223+
return template_comp_proc_fnmap[i].template_comp_proc_func;
224+
225+
return NULL;
226+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
//
3+
// Copyright(c) 2025 Intel Corporation.
4+
5+
#include <sof/audio/module_adapter/module/generic.h>
6+
#include <sof/audio/component.h>
7+
#include "template.h"
8+
9+
LOG_MODULE_DECLARE(template_comp, CONFIG_SOF_LOG_LEVEL);
10+
11+
/* This function handles the real-time controls. The ALSA controls have the
12+
* param_id set to indicate the control type. The control ID, from topology,
13+
* is used to separate the controls instances of same type. In control payload
14+
* the num_elems defines to how many channels the control is applied to.
15+
*/
16+
__cold int template_comp_set_config(struct processing_module *mod, uint32_t param_id,
17+
enum module_cfg_fragment_position pos,
18+
uint32_t data_offset_size, const uint8_t *fragment,
19+
size_t fragment_size, uint8_t *response,
20+
size_t response_size)
21+
{
22+
struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment;
23+
struct template_comp_comp_data *cd = module_get_private_data(mod);
24+
struct comp_dev *dev = mod->dev;
25+
26+
assert_can_be_cold();
27+
28+
comp_dbg(dev, "template_comp_set_config()");
29+
30+
switch (cdata->cmd) {
31+
case SOF_CTRL_CMD_SWITCH:
32+
if (cdata->index == 0) {
33+
if (cdata->num_elems == 1) {
34+
cd->enable = cdata->chanv[0].value;
35+
comp_info(dev, "Setting enable = %d.", cd->enable);
36+
} else {
37+
comp_err(dev, "Illegal switch control num_elems = %d.",
38+
cdata->num_elems);
39+
return -EINVAL;
40+
}
41+
} else {
42+
comp_err(dev, "Illegal switch control index = %d.", cdata->index);
43+
return -EINVAL;
44+
}
45+
return 0;
46+
47+
case SOF_CTRL_CMD_ENUM:
48+
comp_err(dev, "Illegal enum control, no support in this component.");
49+
return -EINVAL;
50+
case SOF_CTRL_CMD_BINARY:
51+
comp_err(dev, "Illegal bytes control, no support in this component.");
52+
return -EINVAL;
53+
}
54+
55+
comp_err(dev, "Illegal control, unknown type.");
56+
return -EINVAL;
57+
}
58+
59+
__cold int template_comp_get_config(struct processing_module *mod,
60+
uint32_t config_id, uint32_t *data_offset_size,
61+
uint8_t *fragment, size_t fragment_size)
62+
{
63+
struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment;
64+
struct template_comp_comp_data *cd = module_get_private_data(mod);
65+
struct comp_dev *dev = mod->dev;
66+
67+
assert_can_be_cold();
68+
69+
comp_info(dev, "template_comp_get_config()");
70+
71+
switch (cdata->cmd) {
72+
case SOF_CTRL_CMD_SWITCH:
73+
if (cdata->index == 0) {
74+
if (cdata->num_elems == 1) {
75+
cdata->chanv[0].value = cd->enable;
76+
} else {
77+
comp_err(dev, "Illegal switch control num_elems = %d.",
78+
cdata->num_elems);
79+
return -EINVAL;
80+
}
81+
} else {
82+
comp_err(dev, "Illegal switch control index = %d.", cdata->index);
83+
return -EINVAL;
84+
}
85+
return 0;
86+
87+
case SOF_CTRL_CMD_ENUM:
88+
comp_err(dev, "Illegal enum control, no support in this component.");
89+
return -EINVAL;
90+
91+
case SOF_CTRL_CMD_BINARY:
92+
comp_err(dev, "Illegal bytes control, no support in this component.");
93+
return -EINVAL;
94+
}
95+
96+
comp_err(dev, "Illegal control, unknown type.");
97+
return -EINVAL;
98+
}

0 commit comments

Comments
 (0)