Skip to content

Commit ec07df2

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 b94ee8f commit ec07df2

File tree

15 files changed

+437
-2
lines changed

15 files changed

+437
-2
lines changed

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: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
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_comp.c)
8+
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 component"
5+
default y
6+
help
7+
Select for template_comp component. Reason for existence
8+
is a minimal component example and possible use as
9+
placeholder in processing pipelines. As example processing
10+
it swaps or reverses the channels when the control switch
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: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
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 <rtos/init.h>
7+
#include "template_comp.h"
8+
9+
LOG_MODULE_REGISTER(template_comp, CONFIG_SOF_LOG_LEVEL);
10+
11+
/* Use e.g. command uuidgen from package uuid-runtime, add it to
12+
* uuid-registry.txt in SOF top level.
13+
*/
14+
SOF_DEFINE_REG_UUID(template_comp);
15+
16+
DECLARE_TR_CTX(template_comp_tr, SOF_UUID(template_comp_uuid), LOG_LEVEL_INFO);
17+
18+
static int template_comp_init(struct processing_module *mod)
19+
{
20+
struct module_data *md = &mod->priv;
21+
struct comp_dev *dev = mod->dev;
22+
struct template_comp_comp_data *cd;
23+
24+
comp_info(dev, "template_comp_init()");
25+
26+
cd = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, sizeof(*cd));
27+
if (!cd)
28+
return -ENOMEM;
29+
30+
md->private = cd;
31+
return 0;
32+
}
33+
34+
__cold static int template_comp_free(struct processing_module *mod)
35+
{
36+
struct template_comp_comp_data *cd = module_get_private_data(mod);
37+
38+
assert_can_be_cold();
39+
40+
comp_info(mod->dev, "template_comp_free()");
41+
rfree(cd);
42+
return 0;
43+
}
44+
45+
#if CONFIG_FORMAT_S16LE
46+
static void template_comp_s16(const struct processing_module *mod,
47+
const struct audio_stream *source,
48+
struct audio_stream *sink,
49+
uint32_t frames)
50+
{
51+
struct template_comp_comp_data *cd = module_get_private_data(mod);
52+
int16_t *x = audio_stream_get_rptr(source);
53+
int16_t *y = audio_stream_get_wptr(sink);
54+
int nbuf;
55+
int nfrm;
56+
int ch;
57+
int i;
58+
59+
while (frames) {
60+
nbuf = audio_stream_frames_without_wrap(source, x);
61+
nfrm = MIN(frames, nbuf);
62+
nbuf = audio_stream_frames_without_wrap(sink, y);
63+
nfrm = MIN(nfrm, nbuf);
64+
for (i = 0; i < nfrm; i++) {
65+
for (ch = 0; ch < cd->channels; ch++) {
66+
*y = *(x + cd->channels_order[ch]);
67+
y++;
68+
}
69+
x += cd->channels;
70+
}
71+
frames -= nfrm;
72+
x = audio_stream_wrap(source, x);
73+
y = audio_stream_wrap(sink, y);
74+
}
75+
}
76+
#endif /* CONFIG_FORMAT_S16LE */
77+
78+
#if CONFIG_FORMAT_S32LE
79+
/* Same function works for s24 and s32 since
80+
* the samples values are not modified.
81+
*/
82+
static void template_comp_s32(const struct processing_module *mod,
83+
const struct audio_stream *source,
84+
struct audio_stream *sink,
85+
uint32_t frames)
86+
{
87+
struct template_comp_comp_data *cd = module_get_private_data(mod);
88+
int32_t *x = audio_stream_get_rptr(source);
89+
int32_t *y = audio_stream_get_wptr(sink);
90+
int nbuf;
91+
int nfrm;
92+
int ch;
93+
int i;
94+
95+
while (frames) {
96+
nbuf = audio_stream_frames_without_wrap(source, x);
97+
nfrm = MIN(frames, nbuf);
98+
nbuf = audio_stream_frames_without_wrap(sink, y);
99+
nfrm = MIN(nfrm, nbuf);
100+
for (i = 0; i < nfrm; i++) {
101+
for (ch = 0; ch < cd->channels; ch++) {
102+
*y = *(x + cd->channels_order[ch]);
103+
y++;
104+
}
105+
x += cd->channels;
106+
}
107+
frames -= nfrm;
108+
x = audio_stream_wrap(source, x);
109+
y = audio_stream_wrap(sink, y);
110+
}
111+
}
112+
#endif /* CONFIG_FORMAT_S32LE */
113+
114+
const struct template_comp_proc_fnmap template_comp_proc_fnmap[] = {
115+
#if CONFIG_FORMAT_S16LE
116+
{ SOF_IPC_FRAME_S16_LE, template_comp_s16 },
117+
#endif
118+
#if CONFIG_FORMAT_S24LE
119+
{ SOF_IPC_FRAME_S24_4LE, template_comp_s32 },
120+
#endif
121+
#if CONFIG_FORMAT_S32LE
122+
{ SOF_IPC_FRAME_S32_LE, template_comp_s32 },
123+
#endif
124+
};
125+
126+
static inline template_comp_func template_comp_find_proc_func(enum sof_ipc_frame src_fmt)
127+
{
128+
int i;
129+
130+
/* Find suitable processing function from map */
131+
for (i = 0; i < ARRAY_SIZE(template_comp_proc_fnmap); i++)
132+
if (src_fmt == template_comp_proc_fnmap[i].frame_fmt)
133+
return template_comp_proc_fnmap[i].template_comp_proc_func;
134+
135+
return NULL;
136+
}
137+
138+
static int template_comp_process(struct processing_module *mod,
139+
struct input_stream_buffer *input_buffers,
140+
int num_input_buffers,
141+
struct output_stream_buffer *output_buffers,
142+
int num_output_buffers)
143+
{
144+
struct template_comp_comp_data *cd = module_get_private_data(mod);
145+
struct comp_dev *dev = mod->dev;
146+
struct audio_stream *source = input_buffers[0].data;
147+
struct audio_stream *sink = output_buffers[0].data;
148+
int frames = input_buffers[0].size;
149+
150+
comp_dbg(dev, "template_comp_process()");
151+
152+
if (cd->enable) {
153+
cd->template_comp_func(mod, source, sink, frames);
154+
} else {
155+
/* copy from source to sink */
156+
audio_stream_copy(source, 0, sink, 0, cd->channels * frames);
157+
}
158+
159+
/* calc new free and available */
160+
module_update_buffer_position(&input_buffers[0], &output_buffers[0], frames);
161+
return 0;
162+
}
163+
164+
static int template_comp_params(struct processing_module *mod)
165+
{
166+
struct sof_ipc_stream_params *params = mod->stream_params;
167+
struct sof_ipc_stream_params comp_params;
168+
struct comp_dev *dev = mod->dev;
169+
struct comp_buffer *sinkb;
170+
enum sof_ipc_frame valid_fmt, frame_fmt;
171+
int i;
172+
173+
comp_dbg(dev, "template_comp_params()");
174+
175+
comp_params = *params;
176+
comp_params.channels = mod->priv.cfg.base_cfg.audio_fmt.channels_count;
177+
comp_params.rate = mod->priv.cfg.base_cfg.audio_fmt.sampling_frequency;
178+
comp_params.buffer_fmt = mod->priv.cfg.base_cfg.audio_fmt.interleaving_style;
179+
180+
audio_stream_fmt_conversion(mod->priv.cfg.base_cfg.audio_fmt.depth,
181+
mod->priv.cfg.base_cfg.audio_fmt.valid_bit_depth,
182+
&frame_fmt, &valid_fmt,
183+
mod->priv.cfg.base_cfg.audio_fmt.s_type);
184+
185+
comp_params.frame_fmt = frame_fmt;
186+
187+
for (i = 0; i < SOF_IPC_MAX_CHANNELS; i++)
188+
comp_params.chmap[i] = (mod->priv.cfg.base_cfg.audio_fmt.ch_map >> i * 4) & 0xf;
189+
190+
component_set_nearest_period_frames(dev, comp_params.rate);
191+
sinkb = comp_dev_get_first_data_consumer(dev);
192+
return buffer_set_params(sinkb, &comp_params, true);
193+
}
194+
195+
static int template_comp_prepare(struct processing_module *mod,
196+
struct sof_source **sources, int num_of_sources,
197+
struct sof_sink **sinks, int num_of_sinks)
198+
{
199+
struct template_comp_comp_data *cd = module_get_private_data(mod);
200+
struct comp_dev *dev = mod->dev;
201+
struct comp_buffer *sourceb;
202+
int ret;
203+
int i;
204+
205+
comp_info(dev, "template_comp_prepare()");
206+
207+
ret = template_comp_params(mod);
208+
if (ret < 0)
209+
return ret;
210+
211+
/* Assume 1 source and 1 sink buffer only */
212+
sourceb = comp_dev_get_first_data_producer(dev);
213+
214+
/* get source data format */
215+
cd->source_format = audio_stream_get_frm_fmt(&sourceb->stream);
216+
cd->channels = audio_stream_get_channels(&sourceb->stream);
217+
218+
/* Initialize channels order for reversing */
219+
for (i = 0; i < cd->channels; i++)
220+
cd->channels_order[i] = cd->channels - i - 1;
221+
222+
cd->template_comp_func = template_comp_find_proc_func(cd->source_format);
223+
if (!cd->template_comp_func) {
224+
comp_err(dev, "No processing function found for format %d.",
225+
cd->source_format);
226+
return -EINVAL;
227+
}
228+
229+
return 0;
230+
}
231+
232+
static int template_comp_reset(struct processing_module *mod)
233+
{
234+
struct template_comp_comp_data *cd = module_get_private_data(mod);
235+
236+
comp_info(mod->dev, "template_comp_reset()");
237+
238+
/* reset component data same as in init() */
239+
cd->channels = 0;
240+
241+
return 0;
242+
}
243+
244+
__cold static int template_comp_set_config(struct processing_module *mod,
245+
uint32_t param_id,
246+
enum module_cfg_fragment_position pos,
247+
uint32_t data_offset_size,
248+
const uint8_t *fragment,
249+
size_t fragment_size,
250+
uint8_t *response,
251+
size_t response_size)
252+
{
253+
struct sof_ipc4_control_msg_payload *ctl = (struct sof_ipc4_control_msg_payload *)fragment;
254+
struct template_comp_comp_data *cd = module_get_private_data(mod);
255+
struct comp_dev *dev = mod->dev;
256+
257+
switch (param_id) {
258+
case SOF_IPC4_SWITCH_CONTROL_PARAM_ID:
259+
comp_dbg(dev, "SOF_IPC4_SWITCH_CONTROL_PARAM_ID id = %d, num_elems = %d",
260+
ctl->id, ctl->num_elems);
261+
262+
if (ctl->id == 0 && ctl->num_elems == 1) {
263+
cd->enable = ctl->chanv[0].value;
264+
comp_info(dev, "process_enabled = %d", cd->enable);
265+
} else {
266+
comp_err(dev, "Illegal control id = %d, num_elems = %d",
267+
ctl->id, ctl->num_elems);
268+
return -EINVAL;
269+
}
270+
271+
return 0;
272+
273+
case SOF_IPC4_ENUM_CONTROL_PARAM_ID:
274+
comp_err(dev, "template_comp_set_ipc_config(), illegal control.");
275+
return -EINVAL;
276+
}
277+
278+
comp_dbg(mod->dev, "template_comp_set_ipc_config(), SOF_CTRL_CMD_BINARY");
279+
return -EINVAL;
280+
}
281+
282+
static const struct module_interface template_comp_interface = {
283+
.init = template_comp_init,
284+
.prepare = template_comp_prepare,
285+
.process_audio_stream = template_comp_process,
286+
.set_configuration = template_comp_set_config,
287+
.reset = template_comp_reset,
288+
.free = template_comp_free
289+
};
290+
291+
DECLARE_MODULE_ADAPTER(template_comp_interface, template_comp_uuid, template_comp_tr);
292+
SOF_MODULE_INIT(template_comp, sys_comp_module_template_comp_interface_init);
293+
294+
#if CONFIG_COMP_TEMPLATE_COMP_MODULE
295+
/* modular: llext dynamic link */
296+
297+
#include <module/module/api_ver.h>
298+
#include <module/module/llext.h>
299+
#include <rimage/sof/user/manifest.h>
300+
301+
SOF_LLEXT_MOD_ENTRY(template_comp, &template_comp_interface);
302+
303+
static const struct sof_man_module_manifest mod_manifest __section(".module") __used =
304+
SOF_LLEXT_MODULE_MANIFEST("TEMPLATE_COMP", template_comp_llext_entry, 1,
305+
SOF_REG_UUID(template_comp), 40);
306+
307+
SOF_LLEXT_BUILDINFO;
308+
309+
#endif
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/* SPDX-License-Identifier: BSD-3-Clause
2+
*
3+
* Copyright(c) 2025 Intel Corporation.
4+
*
5+
*/
6+
#ifndef __SOF_AUDIO_TEMPLATE_COMP_H__
7+
#define __SOF_AUDIO_TEMPLATE_COMP_H__
8+
9+
#include <sof/audio/module_adapter/module/generic.h>
10+
#include <stdbool.h>
11+
#include <stdint.h>
12+
13+
typedef void (*template_comp_func)(const struct processing_module *mod,
14+
const struct audio_stream *source,
15+
struct audio_stream *sink,
16+
uint32_t frames);
17+
18+
/* Template_Comp component private data */
19+
struct template_comp_comp_data {
20+
template_comp_func template_comp_func; /**< processing function */
21+
int channels_order[PLATFORM_MAX_CHANNELS];
22+
int source_format;
23+
int channels; /**< channels count */
24+
bool enable; /**< enable processing */
25+
};
26+
27+
struct template_comp_proc_fnmap {
28+
enum sof_ipc_frame frame_fmt;
29+
template_comp_func template_comp_proc_func;
30+
};
31+
32+
#endif // __SOF_AUDIO_TEMPLATE_COMP_H__

0 commit comments

Comments
 (0)