Skip to content

Commit 58d6216

Browse files
committed
Audio: Buffers: Add support for DP-to-DP component binding
Previously binding two DP (Data Processing) scheduled components was rejected with IPC4_INVALID_REQUEST. This patch adds support for DP-to-DP binding by creating a dual ring buffer configuration where each DP module gets its own ring buffer on either side of the intermediate comp_buffer. Data flow for DP-to-DP: src_DP -> ring_buf_src -> comp_buffer -> ring_buf_sink -> sink_DP Changes in helper.c: - Remove the DP-to-DP bind rejection in ipc_comp_connect(). - Add src_is_dp, sink_is_dp, and dp_to_dp flags to detect the DP-to-DP case. - Create a second ring_buffer allocated from the source module's mod_alloc_ctx for the source side of the comp_buffer. - Refcount the DP vregion for each created ring_buffer via vregion_get(), with a NULL alloc guard. Changes in audio_buffer.c: - Change audio_buffer_attach_secondary_buffer() from a global rejection to per-side checks, allowing both secondary_buffer_sink and secondary_buffer_source to be set simultaneously. - Add a dual-secondary sync path in audio_buffer_sync_secondary_buffer() that cascades data through: input ring_buffer -> comp_buffer -> output ring_buffer, with rate-limiting applied on the output side. Changes in ring_buffer.c: - Release the DP vregion in ring_buffer_free() via vregion_put() and free the mod_alloc_ctx when the refcount reaches zero, matching the pattern used in comp_buffer_free(). Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent f3732b2 commit 58d6216

4 files changed

Lines changed: 114 additions & 29 deletions

File tree

src/audio/buffers/audio_buffer.c

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@
2424
int audio_buffer_attach_secondary_buffer(struct sof_audio_buffer *buffer, bool at_input,
2525
struct sof_audio_buffer *secondary_buffer)
2626
{
27-
if (buffer->secondary_buffer_sink || buffer->secondary_buffer_source)
27+
/* check per-side: allow attaching on both sides (needed for DP-to-DP) */
28+
if (at_input && buffer->secondary_buffer_sink)
29+
return -EINVAL;
30+
if (!at_input && buffer->secondary_buffer_source)
2831
return -EINVAL;
2932

3033
/* secondary buffer must share audio params with the primary buffer */
@@ -48,6 +51,54 @@ int audio_buffer_sync_secondary_buffer(struct sof_audio_buffer *buffer, size_t l
4851
struct sof_source *data_src;
4952
struct sof_sink *data_dst;
5053

54+
if (buffer->secondary_buffer_sink && buffer->secondary_buffer_source) {
55+
/*
56+
* DP-to-DP case: both secondary buffers present.
57+
* Data flows: input_ring_buffer -> comp_buffer -> output_ring_buffer
58+
*
59+
* This buffer is visited twice during each LL cycle:
60+
* - In the input loop (sink DP module via comp_dev_for_each_producer) with
61+
* limit == SIZE_MAX. In a DP-to-DP connection, input to sink DP is fed
62+
* via output_ring_buffer; the intermediate comp_buffer transfer is driven
63+
* by the output loop. Hence, this call is a no-op.
64+
* - In the output loop (source DP module via comp_dev_for_each_consumer) with
65+
* limit == source_get_min_available(downstream). This executes the 2-step
66+
* cascade with rate-limiting properly applied on the output transfer.
67+
*/
68+
if (limit == SIZE_MAX)
69+
return 0;
70+
71+
/*
72+
* Step 1: copy from input secondary buffer to primary (comp_buffer).
73+
* No limit on input side - copy all available data.
74+
*/
75+
data_src = audio_buffer_get_source(buffer->secondary_buffer_sink);
76+
data_dst = &buffer->_sink_api;
77+
78+
size_t data_available = source_get_data_available(data_src);
79+
size_t free_size = sink_get_free_size(data_dst);
80+
size_t to_copy = MIN(data_available, free_size);
81+
82+
err = source_to_sink_copy(data_src, data_dst, true, to_copy);
83+
if (err)
84+
return err;
85+
86+
/*
87+
* Step 2: copy from primary (comp_buffer) to output secondary buffer.
88+
* Apply the limit to the output side to control how much data
89+
* is made available to the downstream DP module per LL cycle.
90+
*/
91+
data_src = &buffer->_source_api;
92+
data_dst = audio_buffer_get_sink(buffer->secondary_buffer_source);
93+
94+
data_available = source_get_data_available(data_src);
95+
free_size = sink_get_free_size(data_dst);
96+
to_copy = MIN(MIN(data_available, free_size), limit);
97+
98+
err = source_to_sink_copy(data_src, data_dst, true, to_copy);
99+
return err;
100+
}
101+
51102
if (buffer->secondary_buffer_sink) {
52103
/*
53104
* audio_buffer sink API is shadowed, that means there's a secondary_buffer
@@ -203,18 +254,16 @@ uint32_t audio_buffer_sink_get_lft(struct sof_sink *sink)
203254
return us_in_buffer;
204255

205256
/*
206-
* TODO, Currently there's no DP to DP connection
207-
* >>> the code below is never accessible and won't work because of cache incoherence <<<
208-
*
209-
* to make DP to DP connection possible:
257+
* NOTE: DP-to-DP connections are now supported via dual ring_buffers
258+
* attached as secondary buffers on both sides of a comp_buffer.
259+
* Data cascades: ring_buf_src -> comp_buffer -> ring_buf_sink
260+
* with syncing during each LL cycle.
210261
*
211-
* 1) module data must be ALWAYS located in non cached memory alias, allowing
212-
* cross core access to params like period (needed below) and calling
213-
* module_get_deadline for the next module, regardless of cores the modules are
214-
* running on
215-
* 2) comp_buffer must be removed from all pipeline code, replaced with a generic abstract
216-
* class audio_buffer - allowing using comp_buffer and ring_buffer without current
217-
* "hybrid buffer" solution
262+
* Future improvements:
263+
* 1) module data should be in non-cached memory alias for reliable
264+
* cross-core access to params like period and deadlines
265+
* 2) comp_buffer should be replaced with generic audio_buffer
266+
* throughout pipeline code (Pipeline 2.0)
218267
*/
219268
}
220269

src/audio/buffers/ring_buffer.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ static inline void ring_buffer_writeback_shared(struct ring_buffer *ring_buffer,
8686
dcache_writeback_region(ptr, size);
8787
}
8888

89-
9089
/**
9190
* @brief remove the queue from the list, free memory
9291
*/
@@ -101,6 +100,12 @@ static void ring_buffer_free(struct sof_audio_buffer *audio_buffer)
101100

102101
sof_ctx_free(alloc, (__sparse_force void *)ring_buffer->_data_buffer);
103102
sof_ctx_free(alloc, ring_buffer);
103+
104+
/* matches vregion_get() in ipc_comp_connect() for each ring_buffer */
105+
if (alloc && alloc->vreg) {
106+
if (!vregion_put(alloc->vreg))
107+
rfree(alloc);
108+
}
104109
}
105110

106111
static void ring_buffer_reset(struct sof_audio_buffer *audio_buffer)

src/audio/module_adapter/module_adapter.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1164,7 +1164,7 @@ static int module_adapter_copy_ring_buffers(struct comp_dev *dev)
11641164
/* input - we need to copy data from audio_stream (as source)
11651165
* to ring_buffer (as sink)
11661166
*/
1167-
err = audio_buffer_sync_secondary_buffer(&buffer->audio_buffer, UINT_MAX);
1167+
err = audio_buffer_sync_secondary_buffer(&buffer->audio_buffer, SIZE_MAX);
11681168

11691169
if (err) {
11701170
comp_err(dev, "LL to DP copy error status: %d", err);

src/ipc/ipc4/helper.c

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -803,18 +803,14 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect)
803803
struct mod_alloc_ctx *alloc;
804804

805805
#if CONFIG_ZEPHYR_DP_SCHEDULER
806-
if (source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP &&
807-
sink->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) {
808-
tr_err(&ipc_tr, "DP to DP binding is not supported: can't bind %x to %x",
809-
src_id, sink_id);
810-
return IPC4_INVALID_REQUEST;
811-
}
812-
806+
bool src_is_dp = source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP;
807+
bool sink_is_dp = sink->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP;
808+
bool dp_to_dp = src_is_dp && sink_is_dp;
813809
struct comp_dev *dp;
814810

815-
if (sink->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP)
811+
if (sink_is_dp)
816812
dp = sink;
817-
else if (source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP)
813+
else if (src_is_dp)
818814
dp = source;
819815
else
820816
dp = NULL;
@@ -887,8 +883,8 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect)
887883
*
888884
* size = 2*max(obs of source module, ibs of destination module)
889885
* (obs and ibs is single buffer size)
890-
* in case of DP -> LL
891-
* size = 2*ibs of destination (LL) module. DP queue will handle obs of DP module
886+
* in case of DP -> LL or DP -> DP
887+
* size = 2*ibs of destination module. DP queue will handle obs of DP module
892888
*/
893889
if (source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_LL)
894890
buf_size = MAX(ibs, obs) * 2;
@@ -923,12 +919,13 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect)
923919
#if CONFIG_ZEPHYR_DP_SCHEDULER
924920
struct ring_buffer *ring_buffer = NULL;
925921

926-
if (sink->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP ||
927-
source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) {
922+
if (src_is_dp || sink_is_dp) {
928923
struct processing_module *srcmod = comp_mod(source);
929924
struct module_data *src_module_data = &srcmod->priv;
930925
struct processing_module *dstmod = comp_mod(sink);
931926
struct module_data *dst_module_data = &dstmod->priv;
927+
bool is_shared = audio_buffer_is_shared(&buffer->audio_buffer);
928+
uint32_t buf_id = buf_get_id(buffer);
932929

933930
/*
934931
* Handle cases where the size of the ring buffer depends on the
@@ -940,16 +937,50 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect)
940937
*/
941938
ring_buffer = ring_buffer_create(dp, MAX(ibs, dst_module_data->mpd.in_buff_size),
942939
MAX(obs, src_module_data->mpd.out_buff_size),
943-
audio_buffer_is_shared(&buffer->audio_buffer),
944-
buf_get_id(buffer));
940+
is_shared, buf_id);
945941
if (!ring_buffer) {
946942
buffer_free(buffer);
947943
return IPC4_OUT_OF_MEMORY;
948944
}
949945

946+
/* refcount the DP vregion for this ring_buffer (matches vregion_put in
947+
* ring_buffer_free)
948+
*/
949+
if (ring_buffer->audio_buffer.alloc)
950+
vregion_get(ring_buffer->audio_buffer.alloc->vreg);
951+
950952
/* data destination module needs to use ring_buffer */
951953
audio_buffer_attach_secondary_buffer(&buffer->audio_buffer, dp == source,
952954
&ring_buffer->audio_buffer);
955+
956+
/*
957+
* DP-to-DP binding: both source and sink are DP modules.
958+
* A second ring_buffer is needed on the other side of the comp_buffer
959+
* so each DP module has its own lock-free ring_buffer interface.
960+
* Data flows: src_DP -> ring_buf_src -> comp_buffer -> ring_buf_sink -> sink_DP
961+
* The comp_buffer acts as the intermediary synced during LL cycles.
962+
*/
963+
if (dp_to_dp) {
964+
struct ring_buffer *ring_buffer2;
965+
966+
ring_buffer2 =
967+
ring_buffer_create(source,
968+
MAX(ibs, dst_module_data->mpd.in_buff_size),
969+
MAX(obs, src_module_data->mpd.out_buff_size),
970+
is_shared, buf_id);
971+
if (!ring_buffer2) {
972+
buffer_free(buffer);
973+
return IPC4_OUT_OF_MEMORY;
974+
}
975+
976+
/* refcount the source DP vregion for ring_buffer2 */
977+
if (ring_buffer2->audio_buffer.alloc)
978+
vregion_get(ring_buffer2->audio_buffer.alloc->vreg);
979+
980+
/* attach second ring_buffer on the source side */
981+
audio_buffer_attach_secondary_buffer(&buffer->audio_buffer, dp != source,
982+
&ring_buffer2->audio_buffer);
983+
}
953984
}
954985

955986
#endif /* CONFIG_ZEPHYR_DP_SCHEDULER */

0 commit comments

Comments
 (0)