Skip to content

Commit 7c41c51

Browse files
singalsuseping66
authored andcommitted
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 Kconfig: - Add CONFIG_DP_TO_DP_BIND (default y) depending on CONFIG_ZEPHYR_DP_SCHEDULER to allow enabling/disabling DP-to-DP component binding. Changes in helper.c: - Allow DP-to-DP binding when CONFIG_DP_TO_DP_BIND is enabled; reject with IPC4_INVALID_REQUEST when disabled. - 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(), guarded by CONFIG_DP_TO_DP_BIND. Changes in audio_buffer.c: - When CONFIG_DP_TO_DP_BIND is enabled, 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() under CONFIG_DP_TO_DP_BIND 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: - For DP-to-DP binding, release the DP vregion in ring_buffer_free() via vregion_put() and free the mod_alloc_ctx when the refcount reaches zero, guarded by CONFIG_DP_TO_DP_BIND. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent dfbcdc4 commit 7c41c51

4 files changed

Lines changed: 142 additions & 23 deletions

File tree

src/audio/buffers/audio_buffer.c

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,16 @@
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 CONFIG_DP_TO_DP_BIND
28+
/* check per-side: allow attaching on both sides (needed for DP-to-DP) */
29+
if (at_input && buffer->secondary_buffer_sink)
30+
return -EINVAL;
31+
if (!at_input && buffer->secondary_buffer_source)
32+
return -EINVAL;
33+
#else
2734
if (buffer->secondary_buffer_sink || buffer->secondary_buffer_source)
2835
return -EINVAL;
36+
#endif
2937

3038
/* secondary buffer must share audio params with the primary buffer */
3139
secondary_buffer->audio_stream_params = buffer->audio_stream_params;
@@ -48,6 +56,56 @@ int audio_buffer_sync_secondary_buffer(struct sof_audio_buffer *buffer, size_t l
4856
struct sof_source *data_src;
4957
struct sof_sink *data_dst;
5058

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

205263
/*
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:
264+
* NOTE: DP-to-DP connections are now supported via dual ring_buffers
265+
* attached as secondary buffers on both sides of a comp_buffer.
266+
* Data cascades: ring_buf_src -> comp_buffer -> ring_buf_sink
267+
* with syncing during each LL cycle.
210268
*
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
269+
* Future improvements:
270+
* 1) module data should be in non-cached memory alias for reliable
271+
* cross-core access to params like period and deadlines
272+
* 2) comp_buffer should be replaced with generic audio_buffer
273+
* throughout pipeline code (Pipeline 2.0)
218274
*/
219275
}
220276

src/audio/buffers/ring_buffer.c

Lines changed: 12 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,18 @@ 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+
#if CONFIG_DP_TO_DP_BIND
105+
/*
106+
* For DP-to-DP binding: matches vregion_get() in ipc_comp_connect()
107+
* for each ring_buffer. Releases the DP module's virtual memory region
108+
* and frees the module allocation context when the refcount reaches zero.
109+
*/
110+
if (alloc && alloc->vreg) {
111+
if (!vregion_put(alloc->vreg))
112+
rfree(alloc);
113+
}
114+
#endif
104115
}
105116

106117
static void ring_buffer_reset(struct sof_audio_buffer *audio_buffer)

src/ipc/ipc4/helper.c

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -803,18 +803,22 @@ __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) {
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+
#if CONFIG_DP_TO_DP_BIND
809+
bool dp_to_dp = src_is_dp && sink_is_dp;
810+
#else
811+
if (src_is_dp && sink_is_dp) {
808812
tr_err(&ipc_tr, "DP to DP binding is not supported: can't bind %x to %x",
809813
src_id, sink_id);
810814
return IPC4_INVALID_REQUEST;
811815
}
812-
816+
#endif
813817
struct comp_dev *dp;
814818

815-
if (sink->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP)
819+
if (sink_is_dp)
816820
dp = sink;
817-
else if (source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP)
821+
else if (src_is_dp)
818822
dp = source;
819823
else
820824
dp = NULL;
@@ -887,8 +891,8 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect)
887891
*
888892
* size = 2*max(obs of source module, ibs of destination module)
889893
* (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
894+
* in case of DP -> LL or DP -> DP
895+
* size = 2*ibs of destination module. DP queue will handle obs of DP module
892896
*/
893897
if (source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_LL)
894898
buf_size = MAX(ibs, obs) * 2;
@@ -923,12 +927,13 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect)
923927
#if CONFIG_ZEPHYR_DP_SCHEDULER
924928
struct ring_buffer *ring_buffer = NULL;
925929

926-
if (sink->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP ||
927-
source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) {
930+
if (src_is_dp || sink_is_dp) {
928931
struct processing_module *srcmod = comp_mod(source);
929932
struct module_data *src_module_data = &srcmod->priv;
930933
struct processing_module *dstmod = comp_mod(sink);
931934
struct module_data *dst_module_data = &dstmod->priv;
935+
bool is_shared = audio_buffer_is_shared(&buffer->audio_buffer);
936+
uint32_t buf_id = buf_get_id(buffer);
932937

933938
/*
934939
* Handle cases where the size of the ring buffer depends on the
@@ -940,16 +945,54 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect)
940945
*/
941946
ring_buffer = ring_buffer_create(dp, MAX(ibs, dst_module_data->mpd.in_buff_size),
942947
MAX(obs, src_module_data->mpd.out_buff_size),
943-
audio_buffer_is_shared(&buffer->audio_buffer),
944-
buf_get_id(buffer));
948+
is_shared, buf_id);
945949
if (!ring_buffer) {
946950
buffer_free(buffer);
947951
return IPC4_OUT_OF_MEMORY;
948952
}
949953

954+
#if CONFIG_DP_TO_DP_BIND
955+
/* refcount the DP vregion for this ring_buffer (matches vregion_put in
956+
* ring_buffer_free for DP-to-DP binding)
957+
*/
958+
if (ring_buffer->audio_buffer.alloc)
959+
vregion_get(ring_buffer->audio_buffer.alloc->vreg);
960+
#endif
961+
950962
/* data destination module needs to use ring_buffer */
951963
audio_buffer_attach_secondary_buffer(&buffer->audio_buffer, dp == source,
952964
&ring_buffer->audio_buffer);
965+
966+
#if CONFIG_DP_TO_DP_BIND
967+
/*
968+
* DP-to-DP binding: both source and sink are DP modules.
969+
* A second ring_buffer is needed on the other side of the comp_buffer
970+
* so each DP module has its own lock-free ring_buffer interface.
971+
* Data flows: src_DP -> ring_buf_src -> comp_buffer -> ring_buf_sink -> sink_DP
972+
* The comp_buffer acts as the intermediary synced during LL cycles.
973+
*/
974+
if (dp_to_dp) {
975+
struct ring_buffer *ring_buffer2;
976+
977+
ring_buffer2 =
978+
ring_buffer_create(source,
979+
MAX(ibs, dst_module_data->mpd.in_buff_size),
980+
MAX(obs, src_module_data->mpd.out_buff_size),
981+
is_shared, buf_id);
982+
if (!ring_buffer2) {
983+
buffer_free(buffer);
984+
return IPC4_OUT_OF_MEMORY;
985+
}
986+
987+
/* refcount the source DP vregion for ring_buffer2 */
988+
if (ring_buffer2->audio_buffer.alloc)
989+
vregion_get(ring_buffer2->audio_buffer.alloc->vreg);
990+
991+
/* attach second ring_buffer on the source side */
992+
audio_buffer_attach_secondary_buffer(&buffer->audio_buffer, dp != source,
993+
&ring_buffer2->audio_buffer);
994+
}
995+
#endif
953996
}
954997

955998
#endif /* CONFIG_ZEPHYR_DP_SCHEDULER */

zephyr/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,15 @@ config ZEPHYR_DP_SCHEDULER
249249
DP modules can be located in dieffrent cores than LL pipeline modules, may have
250250
different tick (i.e. 300ms for speech reccognition, etc.)
251251

252+
config DP_TO_DP_BIND
253+
bool "Support DP to DP component binding"
254+
default y
255+
depends on ZEPHYR_DP_SCHEDULER
256+
help
257+
Enable binding between two Data Processing (DP) scheduled components.
258+
This allows connecting DP modules together (e.g. DP source to DP sink)
259+
via intermediate buffering.
260+
252261
config CROSS_CORE_STREAM
253262
bool "Enable cross-core connected pipelines"
254263
default y if IPC_MAJOR_4

0 commit comments

Comments
 (0)