diff --git a/src/audio/pipeline/static_pipeline_loader.c b/src/audio/pipeline/static_pipeline_loader.c index dcd7ae88eb0f..f95ec79921a5 100644 --- a/src/audio/pipeline/static_pipeline_loader.c +++ b/src/audio/pipeline/static_pipeline_loader.c @@ -18,6 +18,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -25,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -230,12 +235,11 @@ static int send_comp_config(struct comp_dev *dev, const void *abi_blob, size_t a * * Configures the IPC stream parameters (sampling rate, channel count, frame format, * container sizes, valid bit depth, host period bytes, and ALSA channel map) and - * synchronizes the underlying audio stream (buf->stream) as well as its associated - * sink and source interfaces. + * synchronizes the buffer's sink and source interfaces via the SOF Sink/Source APIs. * * Intermediate circular buffers connect adjacent processing components in the - * graph. Initializing their stream parameters and reset state ensures that - * buffer_set_params() and sink/source getters establish identical format + * graph. Initializing their sink/source parameters and reset state ensures that + * downstream consumers and upstream producers establish identical format * expectations prior to component parameter negotiation and streaming. * * \param[in,out] buf Pointer to allocated intermediate component buffer. @@ -281,36 +285,32 @@ static void init_buffer_params(struct comp_buffer *buf, uint32_t dir, enum sof_i params.chmap[c] = SOF_CHMAP_NA; } - /* Apply stream parameters to buffer structure */ - buffer_set_params(buf, ¶ms, BUFFER_UPDATE_FORCE); - - /* Configure circular audio stream parameters and byte alignment */ - audio_stream_set_valid_fmt(&buf->stream, fmt); - audio_stream_set_rate(&buf->stream, rate); - audio_stream_set_channels(&buf->stream, channels); - audio_stream_set_frm_fmt(&buf->stream, fmt); - audio_stream_set_align(SOF_FRAME_BYTE_ALIGN, sample_bytes, &buf->stream); - audio_stream_reset(&buf->stream); - - /* Synchronize sink interface parameters so downstream consumers match */ + /* Synchronize sink interface parameters so upstream producers match */ struct sof_sink *sink = audio_buffer_get_sink(&buf->audio_buffer); if (sink) { + sink_set_params(sink, ¶ms, BUFFER_UPDATE_FORCE); sink_set_valid_fmt(sink, fmt); sink_set_rate(sink, rate); sink_set_channels(sink, channels); sink_set_frm_fmt(sink, fmt); sink_set_alignment_constants(sink, SOF_FRAME_BYTE_ALIGN, sample_bytes); + sink_set_min_free_space(sink, params.host_period_bytes); } - /* Synchronize source interface parameters so upstream producers match */ + /* Synchronize source interface parameters so downstream consumers match */ struct sof_source *source = audio_buffer_get_source(&buf->audio_buffer); if (source) { + source_set_params(source, ¶ms, BUFFER_UPDATE_FORCE); source_set_valid_fmt(source, fmt); source_set_rate(source, rate); source_set_channels(source, channels); source_set_frm_fmt(source, fmt); source_set_alignment_constants(source, SOF_FRAME_BYTE_ALIGN, sample_bytes); + source_set_min_available(source, params.host_period_bytes); } + + /* Reset read/write pointers and zero buffer contents via audio_buffer API */ + audio_buffer_reset(&buf->audio_buffer); } /** @@ -704,9 +704,10 @@ static int sof_static_topology_init_buffers(const struct sof_static_topology *to } /** - * \brief Stage 4: Connect pipeline graph routes. + * \brief Stage 4: Connect pipeline graph routes and bind sink/source interfaces. * - * Establish graph connections: producer comp -> intermediate buffer -> consumer comp. + * Establish graph connections: producer comp -> intermediate buffer -> consumer comp, + * and bind the buffer's sof_sink and sof_source interfaces to the connected components. * * \param[in] topo Topology descriptor pointer. * \return 0 on success, negative errno on failure. @@ -725,6 +726,31 @@ static int sof_static_topology_init_routes(const struct sof_static_topology *top return -EINVAL; } + struct sof_sink *buf_sink = audio_buffer_get_sink(&buf->audio_buffer); + struct sof_source *buf_source = audio_buffer_get_source(&buf->audio_buffer); + if (!buf_sink || !buf_source) { + LOG_ERR("Buffer %u missing sink/source interface", r->buffer_id); + return -EINVAL; + } + + /* Preserve initial buffer format parameters configured in Stage 3 */ + enum sof_ipc_frame init_frm_fmt = source_get_frm_fmt(buf_source); + enum sof_ipc_frame init_valid_fmt = source_get_valid_fmt(buf_source); + uint32_t init_rate = source_get_rate(buf_source); + uint32_t init_channels = source_get_channels(buf_source); + + /* Determine source and destination queue indices for IPC4 module binding */ + uint32_t src_queue = 0; + uint32_t dst_queue = 0; + + for (size_t j = 0; j < i; j++) { + if (topo->routes[j].src_comp_id == r->src_comp_id) + src_queue++; + if (topo->routes[j].sink_comp_id == r->sink_comp_id) + dst_queue++; + } + buf_sink->audio_stream_params->id = IPC4_COMP_ID(src_queue, dst_queue); + /* Connect producer component to buffer and buffer to consumer component */ int ret = pipeline_connect(src, buf, PPL_CONN_DIR_COMP_TO_BUFFER); if (ret < 0) { @@ -740,7 +766,65 @@ static int sof_static_topology_init_routes(const struct sof_static_topology *top return ret; } - LOG_DBG("Connected: %u -> [buf %u] -> %u", r->src_comp_id, r->buffer_id, r->sink_comp_id); + /* Bind sink/source API handles to producer and consumer components */ + struct ipc4_module_bind_unbind bu = { 0 }; + + bu.primary.r.module_id = IPC4_MOD_ID(r->src_comp_id); + bu.primary.r.instance_id = IPC4_INST_ID(r->src_comp_id); + bu.primary.r.type = SOF_IPC4_MOD_BIND; + bu.primary.r.msg_tgt = SOF_IPC4_MESSAGE_TARGET_MODULE_MSG; + bu.extension.r.dst_module_id = IPC4_MOD_ID(r->sink_comp_id); + bu.extension.r.dst_instance_id = IPC4_INST_ID(r->sink_comp_id); + bu.extension.r.src_queue = src_queue; + bu.extension.r.dst_queue = dst_queue; + + struct bind_info bind_data = { + .ipc4_data = &bu, + .bind_type = COMP_BIND_TYPE_SINK, + .sink = buf_sink, + }; + + ret = comp_bind(src, &bind_data); + if (ret < 0) { + LOG_ERR("Failed comp_bind src %u -> buf %u: %d", + r->src_comp_id, r->buffer_id, ret); + return ret; + } + + bind_data.bind_type = COMP_BIND_TYPE_SOURCE; + bind_data.source = buf_source; + ret = comp_bind(sink, &bind_data); + if (ret < 0) { + LOG_ERR("Failed comp_bind buf %u -> sink %u: %d", + r->buffer_id, r->sink_comp_id, ret); + return ret; + } + + /* Restore Stage 3 buffer format parameters and configure IBS/OBS thresholds */ + sink_set_frm_fmt(buf_sink, init_frm_fmt); + sink_set_valid_fmt(buf_sink, init_valid_fmt); + sink_set_rate(buf_sink, init_rate); + sink_set_channels(buf_sink, init_channels); + source_set_frm_fmt(buf_source, init_frm_fmt); + source_set_valid_fmt(buf_source, init_valid_fmt); + source_set_rate(buf_source, init_rate); + source_set_channels(buf_source, init_channels); + + if (dev_comp_type(src) == SOF_COMP_MODULE_ADAPTER) { + struct processing_module *src_mod = comp_mod(src); + + if (src_mod && src_mod->priv.cfg.base_cfg.obs) + sink_set_min_free_space(buf_sink, src_mod->priv.cfg.base_cfg.obs); + } + if (dev_comp_type(sink) == SOF_COMP_MODULE_ADAPTER) { + struct processing_module *sink_mod = comp_mod(sink); + + if (sink_mod && sink_mod->priv.cfg.base_cfg.ibs) + source_set_min_available(buf_source, sink_mod->priv.cfg.base_cfg.ibs); + } + + LOG_DBG("Connected and bound: %u -> [buf %u] -> %u", + r->src_comp_id, r->buffer_id, r->sink_comp_id); } return 0; @@ -859,6 +943,62 @@ static int sof_static_topology_init_pipeline_endpoints(const struct sof_static_t return 0; } +/** + * \brief Check whether a component has any bound sink or source interfaces. + * + * Uses the SOF Sink/Source APIs (processing_module bound sources/sinks for + * module adapter components, and sof_audio_buffer sink/source state for + * endpoint components) rather than inspecting legacy comp_buffer lists. + * + * \param[in] topo Active topology descriptor. + * \param[in] dev Component device to inspect. + * \return True if the component has at least one active sink or source, false otherwise. + */ +static bool sof_static_comp_has_sink_source(const struct sof_static_topology *topo, + struct comp_dev *dev) +{ + if (!dev) + return false; + + if (dev_comp_type(dev) == SOF_COMP_MODULE_ADAPTER) { + struct processing_module *mod = comp_mod(dev); + + return mod && (mod->num_of_sources > 0 || mod->num_of_sinks > 0); + } + + if (!topo) + return false; + + uint32_t comp_id = dev_comp_id(dev); + + for (size_t i = 0; i < topo->num_routes; i++) { + const struct sof_static_route *r = &topo->routes[i]; + + if (r->src_comp_id != comp_id && r->sink_comp_id != comp_id) + continue; + + struct comp_buffer *buf = sof_static_buffer_get(r->buffer_id); + if (!buf) + continue; + + if (r->src_comp_id == comp_id) { + struct sof_source *source = audio_buffer_get_source(&buf->audio_buffer); + + if (source && source_get_comp_state(source) != COMP_STATE_NOT_EXIST) + return true; + } + + if (r->sink_comp_id == comp_id) { + struct sof_sink *sink = audio_buffer_get_sink(&buf->audio_buffer); + + if (sink && sink_get_state(sink) != COMP_STATE_NOT_EXIST) + return true; + } + } + + return false; +} + /** * \brief Stage 6: Explicitly configure and prepare all components. * @@ -876,8 +1016,10 @@ static int sof_static_topology_prepare_comps(const struct sof_static_topology *t if (!dev) continue; - /* Only prepare components currently in READY state */ + /* Only prepare components currently in READY state that have bound sinks/sources */ if (dev->state == COMP_STATE_READY) { + if (!sof_static_comp_has_sink_source(topo, dev)) + continue; const struct sof_static_comp *cdesc = &topo->comps[i]; const struct sof_static_pipeline_desc *pdesc = sof_static_find_pipeline_desc(topo, cdesc->pipeline_id); @@ -1005,10 +1147,51 @@ static int sof_static_topology_init_controls(const struct sof_static_topology *t */ static void sof_static_topology_unwind(void) { - /* Free allocated intermediate buffers */ + /* Unbind, disconnect, and free allocated intermediate buffers */ for (size_t i = 0; i < s_num_buffers; i++) { if (s_buffers[i]) { - buffer_free(s_buffers[i]); + struct comp_buffer *buf = s_buffers[i]; + struct sof_sink *buf_sink = audio_buffer_get_sink(&buf->audio_buffer); + struct sof_source *buf_source = audio_buffer_get_source(&buf->audio_buffer); + struct comp_dev *src = comp_buffer_get_source_component(buf); + struct comp_dev *sink = comp_buffer_get_sink_component(buf); + uint32_t queue_id = buf_sink ? sink_get_id(buf_sink) : 0; + struct ipc4_module_bind_unbind bu = { 0 }; + + bu.primary.r.type = SOF_IPC4_MOD_UNBIND; + bu.primary.r.msg_tgt = SOF_IPC4_MESSAGE_TARGET_MODULE_MSG; + bu.extension.r.src_queue = IPC4_SRC_QUEUE_ID(queue_id); + bu.extension.r.dst_queue = IPC4_SINK_QUEUE_ID(queue_id); + + if (src) { + bu.primary.r.module_id = IPC4_MOD_ID(dev_comp_id(src)); + bu.primary.r.instance_id = IPC4_INST_ID(dev_comp_id(src)); + } + if (sink) { + bu.extension.r.dst_module_id = IPC4_MOD_ID(dev_comp_id(sink)); + bu.extension.r.dst_instance_id = IPC4_INST_ID(dev_comp_id(sink)); + } + + if (src && buf_sink) { + struct bind_info unbind_src = { + .ipc4_data = &bu, + .bind_type = COMP_BIND_TYPE_SINK, + .sink = buf_sink, + }; + comp_unbind(src, &unbind_src); + pipeline_disconnect(src, buf, PPL_CONN_DIR_COMP_TO_BUFFER); + } + if (sink && buf_source) { + struct bind_info unbind_sink = { + .ipc4_data = &bu, + .bind_type = COMP_BIND_TYPE_SOURCE, + .source = buf_source, + }; + comp_unbind(sink, &unbind_sink); + pipeline_disconnect(sink, buf, PPL_CONN_DIR_BUFFER_TO_COMP); + } + + audio_buffer_free(&buf->audio_buffer); s_buffers[i] = NULL; } } @@ -1109,6 +1292,9 @@ int sof_static_topology_init(const struct sof_static_topology *topo) goto err_unwind; } + /* Publish topology as active before initializing kcontrols */ + s_active_topo = topo; + /* Stage 7: Initialize kcontrols with default values */ ret = sof_static_topology_init_controls(topo); if (ret < 0) { @@ -1116,14 +1302,12 @@ int sof_static_topology_init(const struct sof_static_topology *topo) goto err_unwind; } - /* Publish topology as active only after all construction stages succeed */ - s_active_topo = topo; - LOG_INF("Static audio topology initialized successfully (%zu pipelines, %zu comps, %zu buffers, %zu controls)", topo->num_pipelines, topo->num_comps, topo->num_buffers, topo->num_controls); return 0; err_unwind: + s_active_topo = NULL; sof_static_topology_unwind(); return ret; } @@ -1744,8 +1928,38 @@ int sof_static_pipeline_set_sample_rate(uint32_t pipeline_id, uint32_t rate) } } - /* Update buffer parameters for all buffers whose producer belongs to this pipeline */ + /* Update IPC4 module base_cfg and buffer parameters for components in this pipeline */ if (s_active_topo) { + for (size_t c = 0; c < s_active_topo->num_comps; c++) { + const struct sof_static_comp *cdesc = &s_active_topo->comps[c]; + + if (cdesc->pipeline_id != pipeline_id) + continue; + + struct comp_dev *cdev = sof_static_comp_get(cdesc->id); + + if (cdev && dev_comp_type(cdev) == SOF_COMP_MODULE_ADAPTER) { + struct processing_module *mod = comp_mod(cdev); + + if (mod) { + struct ipc4_base_module_cfg *bcfg = &mod->priv.cfg.base_cfg; + uint32_t period_us = cdev->period ? cdev->period : 1000; + uint32_t frames = (rate * period_us) / 1000000; + uint32_t frame_bytes = (bcfg->audio_fmt.depth / 8) * + bcfg->audio_fmt.channels_count; + + if (frames == 0) + frames = 48; + bcfg->audio_fmt.sampling_frequency = rate; + if (frame_bytes) { + bcfg->ibs = frames * frame_bytes; + if (!cdesc->caps.sink_rate) + bcfg->obs = bcfg->ibs; + } + } + } + } + for (size_t r = 0; r < s_active_topo->num_routes; r++) { const struct sof_static_route *route = &s_active_topo->routes[r]; const struct sof_static_comp *src_cdesc = @@ -1755,10 +1969,41 @@ int sof_static_pipeline_set_sample_rate(uint32_t pipeline_id, uint32_t rate) struct comp_buffer *buf = sof_static_buffer_get(route->buffer_id); if (buf) { + struct sof_sink *sink = + audio_buffer_get_sink(&buf->audio_buffer); + struct sof_source *source = + audio_buffer_get_source(&buf->audio_buffer); + enum sof_ipc_frame fmt = source ? + source_get_frm_fmt(source) : SOF_IPC_FRAME_S16_LE; + uint32_t channels = source ? + source_get_channels(source) : 2; + init_buffer_params(buf, src_cdesc->direction, - audio_stream_get_frm_fmt(&buf->stream), - rate, - audio_stream_get_channels(&buf->stream)); + fmt, rate, channels); + + struct comp_dev *src_dev = + sof_static_comp_get(route->src_comp_id); + struct comp_dev *sink_dev = + sof_static_comp_get(route->sink_comp_id); + + if (sink && src_dev && + dev_comp_type(src_dev) == SOF_COMP_MODULE_ADAPTER) { + struct processing_module *src_mod = + comp_mod(src_dev); + + if (src_mod && src_mod->priv.cfg.base_cfg.obs) + sink_set_min_free_space(sink, + src_mod->priv.cfg.base_cfg.obs); + } + if (source && sink_dev && + dev_comp_type(sink_dev) == SOF_COMP_MODULE_ADAPTER) { + struct processing_module *sink_mod = + comp_mod(sink_dev); + + if (sink_mod && sink_mod->priv.cfg.base_cfg.ibs) + source_set_min_available(source, + sink_mod->priv.cfg.base_cfg.ibs); + } } } }