Skip to content

Commit 2fc03a7

Browse files
committed
pipeline: introduce declarative static topology definition and loader
Add support for declarative static audio pipelines in Sound Open Firmware. In microcontroller, hostless, and standalone embedded environments (such as ESP32-P4/S3/C6, PJRC Teensy 4.1, Nordic nRF54, RP2350, smart speakers, and audio bridges), there is no dynamic IPC host driver (Linux ALSA/SoundWire or Windows) to build topologies at runtime. This introduces: 1. Declarative topology structures, capability flags, and constructor macros in <sof/audio/pipeline/static_pipeline.h> with full Doxygen docblocks covering all structures, fields, enums, macros, and public APIs. Complete sample rate masks (8k through 384k) aligned with SOF_RATE_* and frame format masks aligned with enum sof_ipc_frame. 2. Ops-driven static module architecture: generic headers and loader engine are 100% decoupled from individual audio processing components. Audio processing modules define and register their own operations (struct sof_static_module_ops) providing .create, .apply_volume, .apply_switch, and .apply_enum callbacks directly in their own source files (SRC, ASRC, Volume/Gain, Level Multiplier, Selector, EQ IIR, DRC, and TDFB) using DECLARE_STATIC_MODULE_OPS(). 3. Generic module operations registry and base IPC4 config synthesizer in static_pipeline_modules.c: completely generic infrastructure for ops registration, lookup with error logging, base configuration synthesis with macros for IPC4 channel maps, and default fallback component instantiation. 4. Concise, generic constructor macros (SOF_STATIC_MODULE, SOF_STATIC_MODULE_RATE_CONV, SOF_STATIC_ENDPOINT_DAI, and SOF_STATIC_ENDPOINT_USB) allowing clean, compact topology definitions for any audio processing module and DAI endpoint. 5. Modular IPC configuration and base config synthesizer: synthesizes standard IPC4 base module configurations (struct ipc4_base_module_cfg) on behalf of the absent host, calculating input and output buffer sizes (IBS/OBS) dynamically from component capabilities and pipeline periods. 6. Dynamic multi-rate, multi-channel, and format negotiation: eliminates hardcoded sample rates and channel counts in buffer initialization and pipeline/component prepare, supporting sample rate converters (SRC/ASRC), up/down mixers, and format conversions with per-buffer rate/channel inheritance. 7. Generic static topology loader engine in static_pipeline_loader.c to parse and instantiate native SOF components, buffers, routes, and kcontrols directly at boot. 8. Component preparation and parameter negotiation to ensure all processing modules reach ready state prior to streaming. 9. Direct pipeline start, stop, and trigger control with separate start/stop execution paths (sof_static_pipeline_start, sof_static_pipeline_stop), strict error checking preventing triggers on prepare failures, state validation, and copy task management. 10. Decoupled custom kcontrol callback handler to support platform- and board-specific controls without polluting core audio modules. 11. CONFIG_STATIC_PIPELINE Kconfig option and CMakeLists.txt integration. 12. Comprehensive developer documentation, architecture overview, and step-by-step tutorial with multi-rate SRC examples in src/audio/pipeline/README.md. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
1 parent b6c6a05 commit 2fc03a7

15 files changed

Lines changed: 3003 additions & 1 deletion

File tree

‎src/audio/Kconfig‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ config HOST_DMA_IPC_POSITION_UPDATES
5151
by sending an IPC message whenever one period of audio is transferred.
5252
Most platforms provide more efficient ways to query the DMA status.
5353

54+
config STATIC_PIPELINE
55+
bool "Declarative Static Audio Pipeline Loader"
56+
default n
57+
help
58+
Enable support for declarative static audio pipelines in Sound
59+
Open Firmware. This provides a compile-time and runtime loader
60+
engine for hostless, microcontroller, and standalone embedded
61+
targets that do not rely on dynamic IPC topology messages from
62+
a host operating system.
63+
5464
config COMP_CHAIN_DMA
5565
bool "Chain DMA component"
5666
depends on IPC_MAJOR_4

‎src/audio/asrc/asrc.c‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,4 +846,39 @@ SOF_LLEXT_BUILDINFO;
846846
DECLARE_MODULE_ADAPTER(asrc_interface, ASRC_UUID, asrc_tr);
847847
SOF_MODULE_INIT(asrc, sys_comp_module_asrc_interface_init);
848848

849+
#if CONFIG_STATIC_PIPELINE
850+
#include <sof/audio/pipeline/static_pipeline.h>
851+
852+
#if CONFIG_IPC_MAJOR_4
853+
#include "asrc_ipc4.h"
854+
855+
static struct comp_dev *asrc_static_create(const struct comp_driver *drv,
856+
struct comp_ipc_config *cfg,
857+
const struct sof_static_comp *cdesc,
858+
uint32_t period_us)
859+
{
860+
struct ipc4_asrc_module_cfg asrc_cfg;
861+
862+
memset(&asrc_cfg, 0, sizeof(asrc_cfg));
863+
sof_static_init_base_cfg(&asrc_cfg.base, cdesc, period_us);
864+
asrc_cfg.out_freq = (enum ipc4_sampling_frequency)(cdesc->caps.sink_rate ?
865+
cdesc->caps.sink_rate :
866+
cdesc->caps.default_rate);
867+
868+
struct ipc_config_process spec = {
869+
.size = sizeof(asrc_cfg),
870+
.data = (const uint8_t *)&asrc_cfg,
871+
};
872+
return drv->ops.create(drv, cfg, &spec);
873+
}
874+
875+
static struct sof_static_module_ops asrc_static_ops = {
876+
.uuid = &ASRC_UUID,
877+
.create = asrc_static_create,
878+
};
879+
880+
DECLARE_STATIC_MODULE_OPS(asrc, &asrc_static_ops);
881+
#endif /* CONFIG_IPC_MAJOR_4 */
882+
#endif /* CONFIG_STATIC_PIPELINE */
883+
849884
#endif

‎src/audio/drc/drc.c‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,4 +481,30 @@ SOF_LLEXT_BUILDINFO;
481481
DECLARE_MODULE_ADAPTER(drc_interface, drc_uuid, drc_tr);
482482
SOF_MODULE_INIT(drc, sys_comp_module_drc_interface_init);
483483

484+
#if CONFIG_STATIC_PIPELINE
485+
#include <sof/audio/pipeline/static_pipeline.h>
486+
487+
static int drc_static_apply_switch(struct comp_dev *dev, uint32_t channels, int32_t val)
488+
{
489+
struct processing_module *mod = comp_mod(dev);
490+
491+
if (!mod)
492+
return -EINVAL;
493+
494+
struct drc_comp_data *cd = module_get_private_data(mod);
495+
496+
if (cd)
497+
cd->enable_switch = (val != 0);
498+
499+
return 0;
500+
}
501+
502+
static struct sof_static_module_ops drc_static_ops = {
503+
.uuid = &drc_uuid,
504+
.apply_switch = drc_static_apply_switch,
505+
};
506+
507+
DECLARE_STATIC_MODULE_OPS(drc, &drc_static_ops);
508+
#endif /* CONFIG_STATIC_PIPELINE */
509+
484510
#endif

‎src/audio/eq_iir/eq_iir.c‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,4 +258,43 @@ DECLARE_TR_CTX(eq_iir_tr, SOF_UUID(eq_iir_uuid), LOG_LEVEL_INFO);
258258
DECLARE_MODULE_ADAPTER(eq_iir_interface, eq_iir_uuid, eq_iir_tr);
259259
SOF_MODULE_INIT(eq_iir, sys_comp_module_eq_iir_interface_init);
260260

261+
#if CONFIG_STATIC_PIPELINE
262+
#include <sof/audio/pipeline/static_pipeline.h>
263+
264+
static int eq_iir_static_apply_switch(struct comp_dev *dev, uint32_t channels, int32_t val)
265+
{
266+
struct processing_module *mod = comp_mod(dev);
267+
268+
if (!mod)
269+
return -EINVAL;
270+
271+
struct comp_data *cd = module_get_private_data(mod);
272+
273+
if (cd) {
274+
if (val == 0) {
275+
cd->eq_iir_func = eq_iir_pass;
276+
} else if (cd->iir_delay_size) {
277+
#if CONFIG_FORMAT_FLOAT
278+
struct comp_buffer *sourceb = comp_dev_get_first_data_producer(dev);
279+
280+
if (sourceb && audio_stream_get_frm_fmt(&sourceb->stream) == SOF_IPC_FRAME_FLOAT)
281+
cd->eq_iir_func = eq_iir_float_default;
282+
else
283+
cd->eq_iir_func = eq_iir_s16_default;
284+
#else
285+
cd->eq_iir_func = eq_iir_s16_default;
286+
#endif
287+
}
288+
}
289+
return 0;
290+
}
291+
292+
static struct sof_static_module_ops eq_iir_static_ops = {
293+
.uuid = &eq_iir_uuid,
294+
.apply_switch = eq_iir_static_apply_switch,
295+
};
296+
297+
DECLARE_STATIC_MODULE_OPS(eq_iir, &eq_iir_static_ops);
298+
#endif /* CONFIG_STATIC_PIPELINE */
299+
261300
#endif

‎src/audio/level_multiplier/level_multiplier.c‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,4 +204,46 @@ DECLARE_TR_CTX(level_multiplier_tr, SOF_UUID(level_multiplier_uuid), LOG_LEVEL_I
204204
DECLARE_MODULE_ADAPTER(level_multiplier_interface, level_multiplier_uuid, level_multiplier_tr);
205205
SOF_MODULE_INIT(level_multiplier, sys_comp_module_level_multiplier_interface_init);
206206

207+
#if CONFIG_STATIC_PIPELINE
208+
#include <sof/audio/pipeline/static_pipeline.h>
209+
210+
static int lm_static_apply_volume(struct comp_dev *dev, uint32_t channels, int32_t val)
211+
{
212+
struct processing_module *mod = comp_mod(dev);
213+
214+
if (!mod)
215+
return -EINVAL;
216+
217+
struct level_multiplier_comp_data *cd = module_get_private_data(mod);
218+
219+
if (cd)
220+
cd->gain = val;
221+
222+
return 0;
223+
}
224+
225+
static int lm_static_apply_switch(struct comp_dev *dev, uint32_t channels, int32_t val)
226+
{
227+
struct processing_module *mod = comp_mod(dev);
228+
229+
if (!mod)
230+
return -EINVAL;
231+
232+
struct level_multiplier_comp_data *cd = module_get_private_data(mod);
233+
234+
if (cd)
235+
cd->gain = (val == 0) ? 0 : LEVEL_MULTIPLIER_GAIN_ONE;
236+
237+
return 0;
238+
}
239+
240+
static struct sof_static_module_ops lm_static_ops = {
241+
.uuid = &level_multiplier_uuid,
242+
.apply_volume = lm_static_apply_volume,
243+
.apply_switch = lm_static_apply_switch,
244+
};
245+
246+
DECLARE_STATIC_MODULE_OPS(level_multiplier, &lm_static_ops);
247+
#endif /* CONFIG_STATIC_PIPELINE */
248+
207249
#endif

‎src/audio/pipeline/CMakeLists.txt‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@ add_local_sources(sof
77
pipeline-xrun.c
88
pipeline-schedule.c
99
)
10+
11+
add_local_sources_ifdef(CONFIG_STATIC_PIPELINE sof
12+
static_pipeline_loader.c
13+
static_pipeline_modules.c
14+
)

0 commit comments

Comments
 (0)