Skip to content

Commit 99e1cf2

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 f58e079 commit 99e1cf2

20 files changed

Lines changed: 780 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: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
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+
ret = source_get_data(source, bytes, (void const **)&x, (void const **)&x_start, &size);
46+
x_size = size >> 1; /* Bytes to number of s16 samples */
47+
if (ret)
48+
return ret;
49+
50+
ret = sink_get_buffer(sink, bytes, (void **)&y, (void **)&y_start, &size);
51+
y_size = size >> 1; /* Bytes to number of s16 samples */
52+
if (ret)
53+
return ret;
54+
55+
x_end = x_start + x_size;
56+
y_end = y_start + y_size;
57+
while (samples) {
58+
source_samples_without_wrap = x_end - x;
59+
samples_without_wrap = y_end - y;
60+
samples_without_wrap = MIN(samples_without_wrap, source_samples_without_wrap);
61+
samples_without_wrap = MIN(samples_without_wrap, samples);
62+
for (i = 0; i < samples_without_wrap; i += cd->channels) {
63+
for (ch = 0; ch < cd->channels; ch++) {
64+
*y = *(x + cd->channels_order[ch]);
65+
y++;
66+
}
67+
x += cd->channels;
68+
}
69+
70+
x = (x >= x_end) ? x - x_size : x;
71+
y = (y >= y_end) ? y - y_size : y;
72+
samples -= samples_without_wrap;
73+
}
74+
75+
source_release_data(source, bytes);
76+
sink_commit_buffer(sink, bytes);
77+
return 0;
78+
}
79+
#endif /* CONFIG_FORMAT_S16LE */
80+
81+
#if CONFIG_FORMAT_S32LE
82+
/**
83+
* template_comp_s32() - Process S32_LE or S24_4LE format.
84+
* @mod: Pointer to module data.
85+
* @source: Source for PCM samples data.
86+
* @sink: Sink for PCM samples data.
87+
* @frames: Number of audio data frames to process.
88+
*
89+
* Processing function for signed integer 32-bit PCM formats. The same
90+
* function works for s24 and s32 formats since the samples values are
91+
* not modified in computation. The audio samples in every frame are
92+
* re-order to channels order defined in component data channels_order[].
93+
*
94+
* Return: Value zero for success, otherwise an error code.
95+
*/
96+
static int template_comp_s32(const struct processing_module *mod,
97+
struct sof_source *source,
98+
struct sof_sink *sink,
99+
uint32_t frames)
100+
{
101+
struct template_comp_comp_data *cd = module_get_private_data(mod);
102+
int32_t *x, *x_start, *x_end;
103+
int32_t *y, *y_start, *y_end;
104+
size_t size;
105+
int x_size, y_size;
106+
int source_samples_without_wrap;
107+
int samples_without_wrap;
108+
int samples = frames * cd->channels;
109+
int bytes = frames * cd->frame_bytes;
110+
int ret;
111+
int ch;
112+
int i;
113+
114+
ret = source_get_data(source, bytes, (void const **)&x, (void const **)&x_start, &size);
115+
x_size = size >> 2; /* Bytes to number of s32 samples */
116+
if (ret)
117+
return ret;
118+
119+
ret = sink_get_buffer(sink, bytes, (void **)&y, (void **)&y_start, &size);
120+
y_size = size >> 2; /* Bytes to number of s32 samples */
121+
if (ret)
122+
return ret;
123+
124+
x_end = x_start + x_size;
125+
y_end = y_start + y_size;
126+
while (samples) {
127+
source_samples_without_wrap = x_end - x;
128+
samples_without_wrap = y_end - y;
129+
samples_without_wrap = MIN(samples_without_wrap, source_samples_without_wrap);
130+
samples_without_wrap = MIN(samples_without_wrap, samples);
131+
for (i = 0; i < samples_without_wrap; i += cd->channels) {
132+
for (ch = 0; ch < cd->channels; ch++) {
133+
*y = *(x + cd->channels_order[ch]);
134+
y++;
135+
}
136+
x += cd->channels;
137+
}
138+
139+
x = (x >= x_end) ? x - x_size : x;
140+
y = (y >= y_end) ? y - y_size : y;
141+
samples -= samples_without_wrap;
142+
}
143+
144+
source_release_data(source, bytes);
145+
sink_commit_buffer(sink, bytes);
146+
return 0;
147+
}
148+
#endif /* CONFIG_FORMAT_S32LE */
149+
150+
/* This struct array defines the used processing functions for
151+
* the PCM formats
152+
*/
153+
const struct template_comp_proc_fnmap template_comp_proc_fnmap[] = {
154+
#if CONFIG_FORMAT_S16LE
155+
{ SOF_IPC_FRAME_S16_LE, template_comp_s16 },
156+
#endif
157+
#if CONFIG_FORMAT_S24LE
158+
{ SOF_IPC_FRAME_S24_4LE, template_comp_s32 },
159+
#endif
160+
#if CONFIG_FORMAT_S32LE
161+
{ SOF_IPC_FRAME_S32_LE, template_comp_s32 },
162+
#endif
163+
};
164+
165+
/**
166+
* template_comp_find_proc_func() - Find suitable processing function.
167+
* @src_fmt: Enum value for PCM format.
168+
*
169+
* This function finds the suitable processing function to use for
170+
* the used PCM format. If not found, return NULL.
171+
*
172+
* Return: Pointer to processing function for the requested PCM format.
173+
*/
174+
template_comp_func template_comp_find_proc_func(enum sof_ipc_frame src_fmt)
175+
{
176+
int i;
177+
178+
/* Find suitable processing function from map */
179+
for (i = 0; i < ARRAY_SIZE(template_comp_proc_fnmap); i++)
180+
if (src_fmt == template_comp_proc_fnmap[i].frame_fmt)
181+
return template_comp_proc_fnmap[i].template_comp_proc_func;
182+
183+
return NULL;
184+
}
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)