Skip to content

Commit 46ecff9

Browse files
committed
[DNM] 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 heap for the source side of the comp_buffer. - Track ring_buffer client_count on the DP heap with a NULL guard to avoid unsafe container_of when CONFIG_USERSPACE is disabled. 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: - Add DP heap client_count decrement in ring_buffer_free() with a NULL heap guard, matching the pattern used in comp_buffer_free(). Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent 96ab79a commit 46ecff9

File tree

3 files changed

+124
-32
lines changed

3 files changed

+124
-32
lines changed

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: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <sof/audio/module_adapter/module/generic.h>
1111
#include <sof/audio/ring_buffer.h>
1212
#include <sof/audio/component.h>
13+
#include <sof/schedule/dp_schedule.h>
1314

1415
#include <rtos/alloc.h>
1516
#include <ipc/topology.h>
@@ -85,7 +86,6 @@ static inline void ring_buffer_writeback_shared(struct ring_buffer *ring_buffer,
8586
dcache_writeback_region(ptr, size);
8687
}
8788

88-
8989
/**
9090
* @brief remove the queue from the list, free memory
9191
*/
@@ -94,11 +94,20 @@ static void ring_buffer_free(struct sof_audio_buffer *audio_buffer)
9494
if (!audio_buffer)
9595
return;
9696

97-
struct ring_buffer *ring_buffer = container_of(audio_buffer,
98-
struct ring_buffer, audio_buffer);
97+
struct ring_buffer *ring_buffer =
98+
container_of(audio_buffer, struct ring_buffer, audio_buffer);
99+
struct k_heap *heap = audio_buffer->heap;
100+
101+
sof_heap_free(heap, (__sparse_force void *)ring_buffer->_data_buffer);
102+
sof_heap_free(heap, ring_buffer);
103+
104+
/* decrement DP heap client_count, matching the increment in ipc_comp_connect */
105+
if (heap) {
106+
struct dp_heap_user *dp_user = container_of(heap, struct dp_heap_user, heap);
99107

100-
sof_heap_free(audio_buffer->heap, (__sparse_force void *)ring_buffer->_data_buffer);
101-
sof_heap_free(audio_buffer->heap, ring_buffer);
108+
if (!--dp_user->client_count)
109+
rfree(dp_user);
110+
}
102111
}
103112

104113
static void ring_buffer_reset(struct sof_audio_buffer *audio_buffer)

src/ipc/ipc4/helper.c

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -643,18 +643,14 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect)
643643
struct k_heap *dp_heap;
644644

645645
#if CONFIG_ZEPHYR_DP_SCHEDULER
646-
if (source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP &&
647-
sink->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) {
648-
tr_err(&ipc_tr, "DP to DP binding is not supported: can't bind %x to %x",
649-
src_id, sink_id);
650-
return IPC4_INVALID_REQUEST;
651-
}
652-
646+
bool src_is_dp = source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP;
647+
bool sink_is_dp = sink->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP;
648+
bool dp_to_dp = src_is_dp && sink_is_dp;
653649
struct comp_dev *dp;
654650

655-
if (sink->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP)
651+
if (sink_is_dp)
656652
dp = sink;
657-
else if (source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP)
653+
else if (src_is_dp)
658654
dp = source;
659655
else
660656
dp = NULL;
@@ -722,8 +718,8 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect)
722718
*
723719
* size = 2*max(obs of source module, ibs of destination module)
724720
* (obs and ibs is single buffer size)
725-
* in case of DP -> LL
726-
* size = 2*ibs of destination (LL) module. DP queue will handle obs of DP module
721+
* in case of DP -> LL or DP -> DP
722+
* size = 2*ibs of destination module. DP queue will handle obs of DP module
727723
*/
728724
if (source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_LL)
729725
buf_size = MAX(ibs, obs) * 2;
@@ -761,12 +757,13 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect)
761757
#if CONFIG_ZEPHYR_DP_SCHEDULER
762758
struct ring_buffer *ring_buffer = NULL;
763759

764-
if (sink->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP ||
765-
source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) {
760+
if (src_is_dp || sink_is_dp) {
766761
struct processing_module *srcmod = comp_mod(source);
767762
struct module_data *src_module_data = &srcmod->priv;
768763
struct processing_module *dstmod = comp_mod(sink);
769764
struct module_data *dst_module_data = &dstmod->priv;
765+
bool is_shared = audio_buffer_is_shared(&buffer->audio_buffer);
766+
uint32_t buf_id = buf_get_id(buffer);
770767

771768
/*
772769
* Handle cases where the size of the ring buffer depends on the
@@ -778,16 +775,57 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect)
778775
*/
779776
ring_buffer = ring_buffer_create(dp, MAX(ibs, dst_module_data->mpd.in_buff_size),
780777
MAX(obs, src_module_data->mpd.out_buff_size),
781-
audio_buffer_is_shared(&buffer->audio_buffer),
782-
buf_get_id(buffer));
778+
is_shared, buf_id);
783779
if (!ring_buffer) {
784780
buffer_free(buffer);
785781
return IPC4_OUT_OF_MEMORY;
786782
}
787783

784+
/* track ring_buffer as a client of the DP heap */
785+
if (dp_heap) {
786+
struct dp_heap_user *dp_rb_user =
787+
container_of(dp_heap, struct dp_heap_user, heap);
788+
789+
dp_rb_user->client_count++;
790+
}
791+
788792
/* data destination module needs to use ring_buffer */
789793
audio_buffer_attach_secondary_buffer(&buffer->audio_buffer, dp == source,
790794
&ring_buffer->audio_buffer);
795+
796+
/*
797+
* DP-to-DP binding: both source and sink are DP modules.
798+
* A second ring_buffer is needed on the other side of the comp_buffer
799+
* so each DP module has its own lock-free ring_buffer interface.
800+
* Data flows: src_DP → ring_buf_src → comp_buffer → ring_buf_sink → sink_DP
801+
* The comp_buffer acts as the intermediary synced during LL cycles.
802+
*/
803+
if (dp_to_dp) {
804+
struct ring_buffer *ring_buffer2;
805+
struct k_heap *src_heap = source->mod->priv.resources.heap;
806+
807+
ring_buffer2 =
808+
ring_buffer_create(source,
809+
MAX(ibs, dst_module_data->mpd.in_buff_size),
810+
MAX(obs, src_module_data->mpd.out_buff_size),
811+
is_shared, buf_id);
812+
if (!ring_buffer2) {
813+
buffer_free(buffer);
814+
return IPC4_OUT_OF_MEMORY;
815+
}
816+
817+
/* track ring_buffer2 on source's DP heap */
818+
if (src_heap) {
819+
struct dp_heap_user *src_dp_user =
820+
container_of(src_heap, struct dp_heap_user, heap);
821+
822+
src_dp_user->client_count++;
823+
}
824+
825+
/* attach second ring_buffer on the source side */
826+
audio_buffer_attach_secondary_buffer(&buffer->audio_buffer, dp != source,
827+
&ring_buffer2->audio_buffer);
828+
}
791829
}
792830

793831
#endif /* CONFIG_ZEPHYR_DP_SCHEDULER */

0 commit comments

Comments
 (0)