Skip to content

Commit 109f1b2

Browse files
committed
[DNM] Audio: Buffers: Add support for DP-to-DP component binding
See thesofproject#10562 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 109f1b2

3 files changed

Lines changed: 111 additions & 28 deletions

File tree

src/audio/buffers/audio_buffer.c

Lines changed: 57 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,50 @@ 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 may be synced by two DP modules during the same LL cycle:
60+
* - The source DP module syncs it via comp_dev_for_each_consumer (output)
61+
* - The sink DP module syncs it via comp_dev_for_each_producer (input)
62+
*
63+
* Both steps run in order (source DP first, then sink DP). Performing
64+
* them both here in a single call ensures atomicity and correct
65+
* rate-limiting. The second call for the same buffer will be a no-op
66+
* since the comp_buffer will be empty.
67+
*
68+
* Step 1: copy from input secondary buffer to primary (comp_buffer).
69+
* No limit on input side - copy all available data.
70+
*/
71+
data_src = audio_buffer_get_source(buffer->secondary_buffer_sink);
72+
data_dst = &buffer->_sink_api;
73+
74+
size_t data_available = source_get_data_available(data_src);
75+
size_t free_size = sink_get_free_size(data_dst);
76+
size_t to_copy = MIN(data_available, free_size);
77+
78+
err = source_to_sink_copy(data_src, data_dst, true, to_copy);
79+
if (err)
80+
return err;
81+
82+
/*
83+
* Step 2: copy from primary (comp_buffer) to output secondary buffer.
84+
* Apply the limit to the output side to control how much data
85+
* is made available to the downstream DP module per LL cycle.
86+
*/
87+
data_src = &buffer->_source_api;
88+
data_dst = audio_buffer_get_sink(buffer->secondary_buffer_source);
89+
90+
data_available = source_get_data_available(data_src);
91+
free_size = sink_get_free_size(data_dst);
92+
to_copy = MIN(MIN(data_available, free_size), limit);
93+
94+
err = source_to_sink_copy(data_src, data_dst, true, to_copy);
95+
return err;
96+
}
97+
5198
if (buffer->secondary_buffer_sink) {
5299
/*
53100
* audio_buffer sink API is shadowed, that means there's a secondary_buffer
@@ -203,18 +250,16 @@ uint32_t audio_buffer_sink_get_lft(struct sof_sink *sink)
203250
return us_in_buffer;
204251

205252
/*
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:
253+
* NOTE: DP-to-DP connections are now supported via dual ring_buffers
254+
* attached as secondary buffers on both sides of a comp_buffer.
255+
* Data cascades: ring_buf_src -> comp_buffer -> ring_buf_sink
256+
* with syncing during each LL cycle.
210257
*
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
258+
* Future improvements:
259+
* 1) module data should be in non-cached memory alias for reliable
260+
* cross-core access to params like period and deadlines
261+
* 2) comp_buffer should be replaced with generic audio_buffer
262+
* throughout pipeline code (Pipeline 2.0)
218263
*/
219264
}
220265

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/ipc/ipc4/helper.c

Lines changed: 48 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,52 @@ __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 (alloc)
950+
vregion_get(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+
struct mod_alloc_ctx *src_alloc = source->mod ?
966+
source->mod->priv.resources.alloc : NULL;
967+
968+
ring_buffer2 =
969+
ring_buffer_create(source,
970+
MAX(ibs, dst_module_data->mpd.in_buff_size),
971+
MAX(obs, src_module_data->mpd.out_buff_size),
972+
is_shared, buf_id);
973+
if (!ring_buffer2) {
974+
buffer_free(buffer);
975+
return IPC4_OUT_OF_MEMORY;
976+
}
977+
978+
/* refcount the source DP vregion for ring_buffer2 */
979+
if (src_alloc)
980+
vregion_get(src_alloc->vreg);
981+
982+
/* attach second ring_buffer on the source side */
983+
audio_buffer_attach_secondary_buffer(&buffer->audio_buffer, dp != source,
984+
&ring_buffer2->audio_buffer);
985+
}
953986
}
954987

955988
#endif /* CONFIG_ZEPHYR_DP_SCHEDULER */

0 commit comments

Comments
 (0)