From 364e4978ba844f8e2d78b0cad6245f4133e79a2b Mon Sep 17 00:00:00 2001 From: Piotr Hoppe Date: Fri, 19 Jun 2026 17:40:11 +0200 Subject: [PATCH] module: mux: rework module to use sink/source api Rework the mux module to only use the sink/source api to prepare the SOF for the full transition to pipeline 2.0. Signed-off-by: Piotr Hoppe --- src/audio/buffers/comp_buffer.c | 16 ++ src/audio/mux/mux.c | 218 ++++++++------ src/audio/mux/mux.h | 16 +- src/audio/mux/mux_generic.c | 360 ++++++++++++------------ src/include/module/audio/audio_stream.h | 18 ++ src/include/module/audio/sink_api.h | 26 ++ src/include/module/audio/source_api.h | 26 ++ 7 files changed, 409 insertions(+), 271 deletions(-) diff --git a/src/audio/buffers/comp_buffer.c b/src/audio/buffers/comp_buffer.c index 8a3d44133d4b..c579b9cc6d81 100644 --- a/src/audio/buffers/comp_buffer.c +++ b/src/audio/buffers/comp_buffer.c @@ -100,6 +100,20 @@ static int comp_buffer_commit_buffer(struct sof_sink *sink, size_t commit_size) return 0; } +static int comp_buffer_sink_get_state(struct sof_sink *sink) +{ + struct comp_buffer *buffer = comp_buffer_get_from_sink(sink); + + return comp_get_state(comp_buffer_get_sink_component(buffer)); +} + +static int comp_buffer_source_get_state(struct sof_source *source) +{ + struct comp_buffer *buffer = comp_buffer_get_from_source(source); + + return comp_get_state(comp_buffer_get_source_component(buffer)); +} + static int comp_buffer_set_ipc_params(struct sof_audio_buffer *audio_buffer, struct sof_ipc_stream_params *params, bool force_update) @@ -179,6 +193,7 @@ APP_TASK_DATA static const struct source_ops comp_buffer_source_ops = { .audio_set_ipc_params = audio_buffer_source_set_ipc_params, .on_audio_format_set = audio_buffer_source_on_audio_format_set, .set_alignment_constants = audio_buffer_source_set_alignment_constants, + .get_state = comp_buffer_source_get_state, }; APP_TASK_DATA static const struct sink_ops comp_buffer_sink_ops = { @@ -189,6 +204,7 @@ APP_TASK_DATA static const struct sink_ops comp_buffer_sink_ops = { .on_audio_format_set = audio_buffer_sink_on_audio_format_set, .set_alignment_constants = audio_buffer_sink_set_alignment_constants, .get_lft = audio_buffer_sink_get_lft, + .get_state = comp_buffer_sink_get_state, }; static const struct audio_buffer_ops audio_buffer_ops = { diff --git a/src/audio/mux/mux.c b/src/audio/mux/mux.c index 1476779e7324..0187aa641a93 100644 --- a/src/audio/mux/mux.c +++ b/src/audio/mux/mux.c @@ -156,10 +156,10 @@ static int get_stream_index(struct comp_dev *dev, struct comp_data *cd, uint32_t } static void mux_prepare_active_look_up(struct comp_data *cd, - struct audio_stream *sink, - const struct audio_stream **sources) + struct sof_sink *sink, + struct sof_source **sources) { - const struct audio_stream *source; + struct sof_source *source; int elem; int active_elem = 0; @@ -169,8 +169,8 @@ static void mux_prepare_active_look_up(struct comp_data *cd, if (!source) continue; - if (cd->lookup[0].copy_elem[elem].in_ch >= audio_stream_get_channels(source) || - cd->lookup[0].copy_elem[elem].out_ch >= audio_stream_get_channels(sink)) + if (cd->lookup[0].copy_elem[elem].in_ch >= source_get_channels(source) || + cd->lookup[0].copy_elem[elem].out_ch >= sink_get_channels(sink)) continue; cd->active_lookup.copy_elem[active_elem] = cd->lookup[0].copy_elem[elem]; @@ -203,8 +203,8 @@ static struct mux_look_up *get_lookup_table(struct comp_dev *dev, struct comp_da } static void demux_prepare_active_look_up(struct comp_data *cd, - struct audio_stream *sink, - const struct audio_stream *source, + struct sof_sink *sink, + struct sof_source *source, struct mux_look_up *look_up) { int elem; @@ -212,8 +212,8 @@ static void demux_prepare_active_look_up(struct comp_data *cd, /* init pointers */ for (elem = 0; elem < look_up->num_elems; elem++) { - if (look_up->copy_elem[elem].in_ch >= audio_stream_get_channels(source) || - look_up->copy_elem[elem].out_ch >= audio_stream_get_channels(sink)) + if (look_up->copy_elem[elem].in_ch >= source_get_channels(source) || + look_up->copy_elem[elem].out_ch >= sink_get_channels(sink)) continue; cd->active_lookup.copy_elem[active_elem] = look_up->copy_elem[elem]; @@ -225,56 +225,78 @@ static void demux_prepare_active_look_up(struct comp_data *cd, /* process and copy stream data from source to sink buffers */ static int demux_process(struct processing_module *mod, - struct input_stream_buffer *input_buffers, int num_input_buffers, - struct output_stream_buffer *output_buffers, int num_output_buffers) + struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) { struct comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - struct comp_buffer *sink; - struct audio_stream *sinks_stream[MUX_MAX_STREAMS] = { NULL }; - struct mux_look_up *look_ups[MUX_MAX_STREAMS] = { NULL }; - int frames; - int sink_bytes; - int source_bytes; + struct sof_source *source = sources[0]; + const void *source_data; + const void *source_start; + size_t source_size; + size_t source_bytes; + uint32_t frames; + int ret; int i; comp_dbg(dev, "entry"); - /* align sink streams with their respective configurations */ - comp_dev_for_each_consumer(dev, sink) { - if (comp_buffer_get_sink_state(sink) == dev->state) { - i = get_stream_index(dev, cd, buffer_pipeline_id(sink)); - /* return if index wrong */ - if (i < 0) { - return i; - } + /* if there are no sinks active, then there is nothing to do */ + if (num_of_sinks == 0) + return 0; - look_ups[i] = get_lookup_table(dev, cd, buffer_pipeline_id(sink)); - sinks_stream[i] = &sink->stream; - } + /* the same number of frames is distributed to every sink, so it is + * limited by both the source availability and every active sink's free + * space + */ + frames = source_get_data_frames_available(source); + for (i = 0; i < num_of_sinks; i++) { + if (sink_get_state(sinks[i]) != dev->state) + continue; + + frames = MIN(frames, sink_get_free_frames(sinks[i])); } - /* if there are no sinks active, then sinks[] is also empty */ - if (num_output_buffers == 0) + if (!frames) return 0; - frames = input_buffers[0].size; - source_bytes = frames * audio_stream_frame_bytes(mod->input_buffers[0].data); - sink_bytes = frames * audio_stream_frame_bytes(mod->output_buffers[0].data); + /* the source is read-only and shared by all sinks, so it is obtained + * once here and released once after all sinks have been served + */ + source_bytes = frames * source_get_frame_bytes(source); + ret = source_get_data(source, source_bytes, &source_data, &source_start, + &source_size); + if (ret) + return ret; /* produce output, one sink at a time */ - for (i = 0; i < num_output_buffers; i++) { - if (sinks_stream[i]) { - demux_prepare_active_look_up(cd, sinks_stream[i], - input_buffers[0].data, look_ups[i]); - cd->demux(dev, sinks_stream[i], input_buffers[0].data, - frames, &cd->active_lookup); + for (i = 0; i < num_of_sinks; i++) { + struct sof_sink *sink = sinks[i]; + uint32_t pipeline_id = sink_get_pipeline_id(sink); + struct mux_look_up *look_up; + + /* skip sinks that are not in the same state as the component */ + if (sink_get_state(sink) != dev->state) + continue; + + /* return if configuration for this pipeline is missing */ + if (get_stream_index(dev, cd, pipeline_id) < 0) { + source_release_data(source, 0); + return -EINVAL; + } + + look_up = get_lookup_table(dev, cd, pipeline_id); + demux_prepare_active_look_up(cd, sink, source, look_up); + ret = cd->demux(dev, sink, source, source_data, source_start, + source_size, frames, &cd->active_lookup); + if (ret) { + source_release_data(source, 0); + return ret; } - mod->output_buffers[i].size = sink_bytes; } - /* Update consumed */ - mod->input_buffers[0].consumed = source_bytes; + /* consume the processed data from the source */ + source_release_data(source, source_bytes); return 0; } @@ -305,58 +327,88 @@ static int demux_trigger(struct processing_module *mod, int cmd) /* process and copy stream data from source to sink buffers */ static int mux_process(struct processing_module *mod, - struct input_stream_buffer *input_buffers, int num_input_buffers, - struct output_stream_buffer *output_buffers, int num_output_buffers) + struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) { struct comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - struct comp_buffer *source; - const struct audio_stream *sources_stream[MUX_MAX_STREAMS] = { NULL }; - int frames = 0; - int sink_bytes; - int i, j; + struct sof_sink *sink = sinks[0]; + struct sof_source *active_sources[MUX_MAX_STREAMS] = { NULL }; + struct cir_buf_source source_bufs[MUX_MAX_STREAMS] = { 0 }; + size_t source_bytes[MUX_MAX_STREAMS] = { 0 }; + struct cir_buf_sink sink_buf; + size_t sink_bytes, size; + uint32_t frames; + int i, idx, ret; comp_dbg(dev, "entry"); - /* align source streams with their respective configurations */ - j = 0; - comp_dev_for_each_producer(dev, source) { - if (comp_buffer_get_source_state(source) == dev->state) { - if (frames) - frames = MIN(frames, input_buffers[j].size); - else - frames = input_buffers[j].size; - - i = get_stream_index(dev, cd, buffer_pipeline_id(source)); - /* return if index wrong */ - if (i < 0) { - return i; - } + /* if there are no sources or sinks active, then there is nothing to do */ + if (num_of_sinks == 0 || num_of_sources == 0) + return 0; - sources_stream[i] = &source->stream; - } - j++; + /* the same number of frames is taken from every active source and + * written to the single sink, so it is limited by both the sink free + * space and every active source's availability + */ + frames = sink_get_free_frames(sink); + for (i = 0; i < num_of_sources; i++) { + struct sof_source *source = sources[i]; + + /* skip sources that are not in the same state as the component */ + if (source_get_comp_state(source) != dev->state) + continue; + + /* map the source to its configured stream index */ + idx = get_stream_index(dev, cd, source_get_pipeline_id(source)); + if (idx < 0) + return idx; + + active_sources[idx] = source; + frames = MIN(frames, source_get_data_frames_available(source)); } - /* check if there are any sources active */ - if (num_input_buffers == 0) + if (!frames) return 0; - sink_bytes = frames * audio_stream_frame_bytes(mod->output_buffers[0].data); - mux_prepare_active_look_up(cd, output_buffers[0].data, &sources_stream[0]); + /* build the active routing table for the current connections */ + mux_prepare_active_look_up(cd, sink, active_sources); - /* produce output */ - cd->mux(dev, output_buffers[0].data, &sources_stream[0], frames, &cd->active_lookup); - - /* Update consumed per source using each source's own frame size */ - j = 0; - comp_dev_for_each_producer(dev, source) { - if (comp_buffer_get_source_state(source) == dev->state) - mod->input_buffers[j].consumed = - frames * audio_stream_frame_bytes(mod->input_buffers[j].data); - j++; + /* acquire the single sink circular buffer for the whole period */ + sink_bytes = frames * sink_get_frame_bytes(sink); + ret = sink_get_buffer(sink, sink_bytes, &sink_buf.ptr, &sink_buf.buf_start, &size); + if (ret) + return ret; + sink_buf.buf_end = (char *)sink_buf.buf_start + size; + + /* acquire every active source circular buffer */ + for (i = 0; i < MUX_MAX_STREAMS; i++) { + if (!active_sources[i]) + continue; + + source_bytes[i] = frames * source_get_frame_bytes(active_sources[i]); + ret = source_get_data(active_sources[i], source_bytes[i], &source_bufs[i].ptr, + &source_bufs[i].buf_start, &size); + if (ret) { + /* release the sources acquired so far and the sink */ + for (--i; i >= 0; i--) + if (active_sources[i]) + source_release_data(active_sources[i], 0); + sink_commit_buffer(sink, 0); + return ret; + } + source_bufs[i].buf_end = (const char *)source_bufs[i].buf_start + size; } - mod->output_buffers[0].size = sink_bytes; + + /* produce output */ + cd->mux(dev, sink, &sink_buf, active_sources, source_bufs, frames, &cd->active_lookup); + + /* commit the produced data and consume from every active source */ + sink_commit_buffer(sink, sink_bytes); + for (i = 0; i < MUX_MAX_STREAMS; i++) + if (active_sources[i]) + source_release_data(active_sources[i], source_bytes[i]); + return 0; } @@ -443,7 +495,7 @@ static const struct module_interface mux_interface = { .set_configuration = mux_set_config, .get_configuration = mux_get_config, .prepare = mux_prepare, - .process_audio_stream = mux_process, + .process = mux_process, .reset = mux_reset, .free = mux_free, }; @@ -454,7 +506,7 @@ static const struct module_interface demux_interface = { .set_configuration = mux_set_config, .get_configuration = mux_get_config, .prepare = mux_prepare, - .process_audio_stream = demux_process, + .process = demux_process, .trigger = demux_trigger, .reset = mux_reset, .free = mux_free, diff --git a/src/audio/mux/mux.h b/src/audio/mux/mux.h index 0fb11d8300b5..51a5018291d7 100644 --- a/src/audio/mux/mux.h +++ b/src/audio/mux/mux.h @@ -28,6 +28,10 @@ #endif struct comp_buffer; struct comp_dev; +struct sof_source; +struct sof_sink; +struct cir_buf_source; +struct cir_buf_sink; /** \brief Supported streams count. */ #if CONFIG_IPC_MAJOR_3 @@ -66,11 +70,13 @@ struct mux_stream_data { uint8_t reserved2[3]; // padding to ensure proper alignment of following instances } __attribute__((packed, aligned(4))); -typedef void(*demux_func)(struct comp_dev *dev, struct audio_stream *sink, - const struct audio_stream *source, uint32_t frames, - struct mux_look_up *look_up); -typedef void(*mux_func)(struct comp_dev *dev, struct audio_stream *sink, - const struct audio_stream **sources, uint32_t frames, +typedef int(*demux_func)(struct comp_dev *dev, struct sof_sink *sink, + struct sof_source *source, const void *source_data, + const void *source_start, size_t source_size, + uint32_t frames, struct mux_look_up *look_up); +typedef void(*mux_func)(struct comp_dev *dev, struct sof_sink *sink, + struct cir_buf_sink *sink_buf, struct sof_source **sources, + struct cir_buf_source *source_bufs, uint32_t frames, struct mux_look_up *look_up); /** diff --git a/src/audio/mux/mux_generic.c b/src/audio/mux/mux_generic.c index 20d21afdffb1..360d1ee263ac 100644 --- a/src/audio/mux/mux_generic.c +++ b/src/audio/mux/mux_generic.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -20,70 +21,32 @@ LOG_MODULE_DECLARE(muxdemux, CONFIG_SOF_LOG_LEVEL); -static void mux_check_for_wrap(struct audio_stream *sink, - const struct audio_stream **sources, +static void mux_check_for_wrap(struct cir_buf_sink *sink, + struct cir_buf_source *source_bufs, struct mux_look_up *lookup) { - const struct audio_stream *source; + struct cir_buf_source *source; uint32_t elem; /* check sources and destinations for wrap */ for (elem = 0; elem < lookup->num_elems; elem++) { - source = sources[lookup->copy_elem[elem].stream_id]; + source = &source_bufs[lookup->copy_elem[elem].stream_id]; lookup->copy_elem[elem].dest = - audio_stream_wrap(sink, lookup->copy_elem[elem].dest); + cir_buf_wrap(lookup->copy_elem[elem].dest, + sink->buf_start, sink->buf_end); lookup->copy_elem[elem].src = - audio_stream_wrap(source, lookup->copy_elem[elem].src); - } -} - -static void demux_check_for_wrap(struct audio_stream *sink, - const struct audio_stream *source, - struct mux_look_up *lookup) -{ - uint32_t elem; - - /* check sources and destinations for wrap */ - for (elem = 0; elem < lookup->num_elems; elem++) { - lookup->copy_elem[elem].dest = - audio_stream_wrap(sink, lookup->copy_elem[elem].dest); - lookup->copy_elem[elem].src = - audio_stream_wrap(source, lookup->copy_elem[elem].src); + cir_buf_wrap(lookup->copy_elem[elem].src, + (void *)source->buf_start, (void *)source->buf_end); } } #if CONFIG_FORMAT_S16LE -static uint32_t demux_calc_frames_without_wrap_s16(struct audio_stream *sink, - const struct audio_stream *source, - struct mux_look_up *lookup) -{ - uint32_t frames; - uint32_t min_frames; - void *ptr; - - /* for demux we process each source buffer separately - dest/src for - * each copy_elem refers to the same sink/source buffer, so min_frames - * calculation based only on lookup table first element is sufficient. - */ - ptr = (int16_t *)lookup->copy_elem[0].dest - - lookup->copy_elem[0].out_ch; - min_frames = audio_stream_frames_without_wrap(sink, ptr); - - ptr = (int16_t *)lookup->copy_elem[0].src - - lookup->copy_elem[0].in_ch; - frames = audio_stream_frames_without_wrap(source, ptr); - - min_frames = (frames < min_frames) ? frames : min_frames; - - return min_frames; -} - -static uint32_t mux_calc_frames_without_wrap_s16(struct audio_stream *sink, - const struct audio_stream **sources, +static uint32_t mux_calc_frames_without_wrap_s16(struct cir_buf_sink *sink, + struct cir_buf_source *source_bufs, struct mux_look_up *lookup) { - const struct audio_stream *source; + struct cir_buf_source *source; uint32_t frames; uint32_t min_frames; uint32_t elem; @@ -95,14 +58,16 @@ static uint32_t mux_calc_frames_without_wrap_s16(struct audio_stream *sink, */ ptr = (int16_t *)lookup->copy_elem[0].dest - lookup->copy_elem[0].out_ch; - min_frames = audio_stream_frames_without_wrap(sink, ptr); + min_frames = circ_buf_frames_without_wrap(ptr, sink->buf_end, sizeof(int16_t), + lookup->copy_elem[0].dest_inc); for (elem = 0; elem < lookup->num_elems; elem++) { - source = sources[lookup->copy_elem[elem].stream_id]; + source = &source_bufs[lookup->copy_elem[elem].stream_id]; ptr = (int16_t *)lookup->copy_elem[elem].src - lookup->copy_elem[elem].in_ch; - frames = audio_stream_frames_without_wrap(source, ptr); + frames = circ_buf_frames_without_wrap(ptr, source->buf_end, sizeof(int16_t), + lookup->copy_elem[elem].src_inc); min_frames = (frames < min_frames) ? frames : min_frames; } @@ -110,78 +75,80 @@ static uint32_t mux_calc_frames_without_wrap_s16(struct audio_stream *sink, return min_frames; } -static void mux_init_look_up_pointers_s16(struct audio_stream *sink, - const struct audio_stream **sources, +static void mux_init_look_up_pointers_s16(struct sof_sink *sink, + struct cir_buf_sink *sink_buf, + struct sof_source **sources, + struct cir_buf_source *source_bufs, struct mux_look_up *lookup) { - const struct audio_stream *source; uint32_t elem; + uint32_t sid; /* init pointers */ for (elem = 0; elem < lookup->num_elems; elem++) { - source = sources[lookup->copy_elem[elem].stream_id]; + sid = lookup->copy_elem[elem].stream_id; - lookup->copy_elem[elem].src = (int16_t *)audio_stream_get_rptr(source) + + lookup->copy_elem[elem].src = (int16_t *)source_bufs[sid].ptr + lookup->copy_elem[elem].in_ch; - lookup->copy_elem[elem].src_inc = audio_stream_get_channels(source); + lookup->copy_elem[elem].src_inc = source_get_channels(sources[sid]); - lookup->copy_elem[elem].dest = (int16_t *)audio_stream_get_wptr(sink) + + lookup->copy_elem[elem].dest = (int16_t *)sink_buf->ptr + lookup->copy_elem[elem].out_ch; - lookup->copy_elem[elem].dest_inc = audio_stream_get_channels(sink); + lookup->copy_elem[elem].dest_inc = sink_get_channels(sink); } } -static void demux_init_look_up_pointers_s16(struct audio_stream *sink, - const struct audio_stream *source, - struct mux_look_up *lookup) +static int demux_s16le(struct comp_dev *dev, struct sof_sink *sink, + struct sof_source *source, const void *source_data, + const void *source_start, size_t source_size, + uint32_t frames, struct mux_look_up *lookup) { + const int16_t *x_start = source_start; + const int16_t *x_end = x_start + (source_size >> 1); + int16_t *y, *y_start, *y_end; + int y_size; + int source_channels = source_get_channels(source); + int sink_channels = sink_get_channels(sink); + int bytes = frames * sink_get_frame_bytes(sink); uint32_t elem; - - /* init pointers */ - for (elem = 0; elem < lookup->num_elems; elem++) { - lookup->copy_elem[elem].src = (int16_t *)audio_stream_get_rptr(source) + - lookup->copy_elem[elem].in_ch; - lookup->copy_elem[elem].src_inc = audio_stream_get_channels(source); - - lookup->copy_elem[elem].dest = (int16_t *)audio_stream_get_wptr(sink) + - lookup->copy_elem[elem].out_ch; - lookup->copy_elem[elem].dest_inc = audio_stream_get_channels(sink); - } -} - -/** - * Source stream are routed to sinks with regard to look up table based on - * routing bitmasks from mux_stream_data structures array. Each sink channel - * has it's own lookup[].copy_elem describing source and sink fragment of - * memory featured in copying. - * - * @param[in] dev Component device - * @param[in,out] sink Destination buffer. - * @param[in,out] sources Array of source buffers. - * @param[in] frames Number of frames to process. - * @param[in] lookup mux look up table. - */ -static void demux_s16le(struct comp_dev *dev, struct audio_stream *sink, - const struct audio_stream *source, uint32_t frames, - struct mux_look_up *lookup) -{ uint32_t i; - int16_t *src; - int16_t *dst; - uint32_t elem; - uint32_t frames_without_wrap; + int ret; comp_dbg(dev, "entry"); if (!lookup || !lookup->num_elems) - return; + return 0; - demux_init_look_up_pointers_s16(sink, source, lookup); + /* obtain the sink circular buffer for this output stream */ + ret = sink_get_buffer_s16(sink, bytes, &y, &y_start, &y_size); + if (ret) + return ret; - while (frames) { - frames_without_wrap = - demux_calc_frames_without_wrap_s16(sink, source, lookup); + y_end = y_start + y_size; + /* init pointers based on the freshly obtained buffers */ + for (elem = 0; elem < lookup->num_elems; elem++) { + lookup->copy_elem[elem].src = (int16_t *)source_data + + lookup->copy_elem[elem].in_ch; + lookup->copy_elem[elem].src_inc = source_channels; + + lookup->copy_elem[elem].dest = y + lookup->copy_elem[elem].out_ch; + lookup->copy_elem[elem].dest_inc = sink_channels; + } + + while (frames) { + int16_t *src = (int16_t *)lookup->copy_elem[0].src - + lookup->copy_elem[0].in_ch; + int16_t *dst = (int16_t *)lookup->copy_elem[0].dest - + lookup->copy_elem[0].out_ch; + uint32_t source_frames_without_wrap = + circ_buf_frames_without_wrap(src, x_end, sizeof(*src), source_channels); + uint32_t sink_frames_without_wrap = + circ_buf_frames_without_wrap(dst, y_end, sizeof(*dst), sink_channels); + uint32_t frames_without_wrap; + + frames_without_wrap = MIN(source_frames_without_wrap, + sink_frames_without_wrap); frames_without_wrap = MIN(frames, frames_without_wrap); for (i = 0; i < frames_without_wrap; i++) { @@ -196,10 +163,24 @@ static void demux_s16le(struct comp_dev *dev, struct audio_stream *sink, } } - demux_check_for_wrap(sink, source, lookup); + /* check sources and destinations for wrap */ + for (elem = 0; elem < lookup->num_elems; elem++) { + src = (int16_t *)lookup->copy_elem[elem].src; + dst = (int16_t *)lookup->copy_elem[elem].dest; + if (src >= x_end) + src -= (source_size >> 1); + if (dst >= y_end) + dst -= y_size; + lookup->copy_elem[elem].src = src; + lookup->copy_elem[elem].dest = dst; + } frames -= frames_without_wrap; } + + sink_commit_buffer(sink, bytes); + + return 0; } /** @@ -214,8 +195,9 @@ static void demux_s16le(struct comp_dev *dev, struct audio_stream *sink, * @param[in] frames Number of frames to process. * @param[in] lookup mux look up table. */ -static void mux_s16le(struct comp_dev *dev, struct audio_stream *sink, - const struct audio_stream **sources, uint32_t frames, +static void mux_s16le(struct comp_dev *dev, struct sof_sink *sink, + struct cir_buf_sink *sink_buf, struct sof_source **sources, + struct cir_buf_source *source_bufs, uint32_t frames, struct mux_look_up *lookup) { uint32_t i; @@ -229,11 +211,11 @@ static void mux_s16le(struct comp_dev *dev, struct audio_stream *sink, if (!lookup || !lookup->num_elems) return; - mux_init_look_up_pointers_s16(sink, sources, lookup); + mux_init_look_up_pointers_s16(sink, sink_buf, sources, source_bufs, lookup); while (frames) { frames_without_wrap = - mux_calc_frames_without_wrap_s16(sink, sources, lookup); + mux_calc_frames_without_wrap_s16(sink_buf, source_bufs, lookup); frames_without_wrap = MIN(frames, frames_without_wrap); @@ -249,7 +231,7 @@ static void mux_s16le(struct comp_dev *dev, struct audio_stream *sink, } } - mux_check_for_wrap(sink, sources, lookup); + mux_check_for_wrap(sink_buf, source_bufs, lookup); frames -= frames_without_wrap; } @@ -258,11 +240,11 @@ static void mux_s16le(struct comp_dev *dev, struct audio_stream *sink, #if CONFIG_FORMAT_S24LE || CONFIG_FORMAT_S32LE -static uint32_t mux_calc_frames_without_wrap_s32(struct audio_stream *sink, - const struct audio_stream **sources, +static uint32_t mux_calc_frames_without_wrap_s32(struct cir_buf_sink *sink, + struct cir_buf_source *source_bufs, struct mux_look_up *lookup) { - const struct audio_stream *source; + struct cir_buf_source *source; uint32_t frames; uint32_t min_frames; uint32_t elem; @@ -273,13 +255,15 @@ static uint32_t mux_calc_frames_without_wrap_s32(struct audio_stream *sink, * calculation based only on lookup table first element is sufficient. */ ptr = (int32_t *)lookup->copy_elem[0].dest - lookup->copy_elem[0].out_ch; - min_frames = audio_stream_frames_without_wrap(sink, ptr); + min_frames = circ_buf_frames_without_wrap(ptr, sink->buf_end, sizeof(int32_t), + lookup->copy_elem[0].dest_inc); for (elem = 0; elem < lookup->num_elems; elem++) { - source = sources[lookup->copy_elem[elem].stream_id]; + source = &source_bufs[lookup->copy_elem[elem].stream_id]; ptr = (int32_t *)lookup->copy_elem[elem].src - lookup->copy_elem[elem].in_ch; - frames = audio_stream_frames_without_wrap(source, ptr); + frames = circ_buf_frames_without_wrap(ptr, source->buf_end, sizeof(int32_t), + lookup->copy_elem[elem].src_inc); min_frames = (frames < min_frames) ? frames : min_frames; } @@ -287,101 +271,96 @@ static uint32_t mux_calc_frames_without_wrap_s32(struct audio_stream *sink, return min_frames; } -static uint32_t demux_calc_frames_without_wrap_s32(struct audio_stream *sink, - const struct audio_stream *source, - struct mux_look_up *lookup) -{ - uint32_t frames; - uint32_t min_frames; - void *ptr; - - /* for demux we process each source buffer separately - dest/src for - * each copy_elem refers to the same sink/source buffer, so min_frames - * calculation based only on lookup table first element is sufficient. - */ - ptr = (int32_t *)lookup->copy_elem[0].dest - lookup->copy_elem[0].out_ch; - min_frames = audio_stream_frames_without_wrap(sink, ptr); - - ptr = (int32_t *)lookup->copy_elem[0].src - lookup->copy_elem[0].in_ch; - frames = audio_stream_frames_without_wrap(source, ptr); - - min_frames = (frames < min_frames) ? frames : min_frames; - - return min_frames; -} - -static void mux_init_look_up_pointers_s32(struct audio_stream *sink, - const struct audio_stream **sources, +static void mux_init_look_up_pointers_s32(struct sof_sink *sink, + struct cir_buf_sink *sink_buf, + struct sof_source **sources, + struct cir_buf_source *source_bufs, struct mux_look_up *lookup) { - const struct audio_stream *source; uint32_t elem; + uint32_t sid; /* init pointers */ for (elem = 0; elem < lookup->num_elems; elem++) { - source = sources[lookup->copy_elem[elem].stream_id]; + sid = lookup->copy_elem[elem].stream_id; - lookup->copy_elem[elem].src = (int32_t *)audio_stream_get_rptr(source) + + lookup->copy_elem[elem].src = (int32_t *)source_bufs[sid].ptr + lookup->copy_elem[elem].in_ch; - lookup->copy_elem[elem].src_inc = audio_stream_get_channels(source); + lookup->copy_elem[elem].src_inc = source_get_channels(sources[sid]); - lookup->copy_elem[elem].dest = (int32_t *)audio_stream_get_wptr(sink) + + lookup->copy_elem[elem].dest = (int32_t *)sink_buf->ptr + lookup->copy_elem[elem].out_ch; - lookup->copy_elem[elem].dest_inc = audio_stream_get_channels(sink); - } -} - -static void demux_init_look_up_pointers_s32(struct audio_stream *sink, - const struct audio_stream *source, - struct mux_look_up *lookup) -{ - uint32_t elem; - - /* init pointers */ - for (elem = 0; elem < lookup->num_elems; elem++) { - lookup->copy_elem[elem].src = (int32_t *)audio_stream_get_rptr(source) + - lookup->copy_elem[elem].in_ch; - lookup->copy_elem[elem].src_inc = audio_stream_get_channels(source); - - lookup->copy_elem[elem].dest = (int32_t *)audio_stream_get_wptr(sink) + - lookup->copy_elem[elem].out_ch; - lookup->copy_elem[elem].dest_inc = audio_stream_get_channels(sink); + lookup->copy_elem[elem].dest_inc = sink_get_channels(sink); } } /** - * Source stream are routed to sinks with regard to look up table based on + * Source stream is routed to sinks with regard to look up table based on * routing bitmasks from mux_stream_data structures array. Each sink channel * has it's own lookup[].copy_elem describing source and sink fragment of * memory featured in copying. * * @param[in] dev Component device - * @param[in,out] sink Destination buffer. - * @param[in,out] sources Array of source buffers. + * @param[in,out] sink Destination sink (sof_sink handle). + * @param[in] source Source handle, used for channel count metadata only. + * @param[in] source_data Read pointer into the source circular buffer. + * @param[in] source_start Start address of the source circular buffer. + * @param[in] source_size Size of the source circular buffer in bytes. * @param[in] frames Number of frames to process. * @param[in] lookup mux look up table. + * @return 0 on success, negative error code otherwise. */ -static void demux_s32le(struct comp_dev *dev, struct audio_stream *sink, - const struct audio_stream *source, uint32_t frames, - struct mux_look_up *lookup) +static int demux_s32le(struct comp_dev *dev, struct sof_sink *sink, + struct sof_source *source, const void *source_data, + const void *source_start, size_t source_size, + uint32_t frames, struct mux_look_up *lookup) { - uint32_t i; - int32_t *src; - int32_t *dst; + const int32_t *x_start = source_start; + const int32_t *x_end = x_start + (source_size >> 2); + int32_t *y, *y_start, *y_end; + int y_size; + int source_channels = source_get_channels(source); + int sink_channels = sink_get_channels(sink); + int bytes = frames * sink_get_frame_bytes(sink); uint32_t elem; - uint32_t frames_without_wrap; + uint32_t i; + int ret; comp_dbg(dev, "entry"); if (!lookup || !lookup->num_elems) - return; + return 0; - demux_init_look_up_pointers_s32(sink, source, lookup); + /* obtain the sink circular buffer for this output stream */ + ret = sink_get_buffer_s32(sink, bytes, &y, &y_start, &y_size); + if (ret) + return ret; - while (frames) { - frames_without_wrap = - demux_calc_frames_without_wrap_s32(sink, source, lookup); + y_end = y_start + y_size; + + /* init pointers based on the freshly obtained buffers */ + for (elem = 0; elem < lookup->num_elems; elem++) { + lookup->copy_elem[elem].src = (int32_t *)source_data + + lookup->copy_elem[elem].in_ch; + lookup->copy_elem[elem].src_inc = source_channels; + lookup->copy_elem[elem].dest = y + lookup->copy_elem[elem].out_ch; + lookup->copy_elem[elem].dest_inc = sink_channels; + } + + while (frames) { + int32_t *src = (int32_t *)lookup->copy_elem[0].src - + lookup->copy_elem[0].in_ch; + int32_t *dst = (int32_t *)lookup->copy_elem[0].dest - + lookup->copy_elem[0].out_ch; + uint32_t source_frames_without_wrap = + circ_buf_frames_without_wrap(src, x_end, sizeof(*src), source_channels); + uint32_t sink_frames_without_wrap = + circ_buf_frames_without_wrap(dst, y_end, sizeof(*dst), sink_channels); + uint32_t frames_without_wrap; + + frames_without_wrap = MIN(source_frames_without_wrap, + sink_frames_without_wrap); frames_without_wrap = MIN(frames, frames_without_wrap); for (i = 0; i < frames_without_wrap; i++) { @@ -396,10 +375,24 @@ static void demux_s32le(struct comp_dev *dev, struct audio_stream *sink, } } - demux_check_for_wrap(sink, source, lookup); + /* check sources and destinations for wrap */ + for (elem = 0; elem < lookup->num_elems; elem++) { + src = (int32_t *)lookup->copy_elem[elem].src; + dst = (int32_t *)lookup->copy_elem[elem].dest; + if (src >= x_end) + src -= (source_size >> 2); + if (dst >= y_end) + dst -= y_size; + lookup->copy_elem[elem].src = src; + lookup->copy_elem[elem].dest = dst; + } frames -= frames_without_wrap; } + + sink_commit_buffer(sink, bytes); + + return 0; } /** @@ -414,8 +407,9 @@ static void demux_s32le(struct comp_dev *dev, struct audio_stream *sink, * @param[in] frames Number of frames to process. * @param[in] lookup mux look up table. */ -static void mux_s32le(struct comp_dev *dev, struct audio_stream *sink, - const struct audio_stream **sources, uint32_t frames, +static void mux_s32le(struct comp_dev *dev, struct sof_sink *sink, + struct cir_buf_sink *sink_buf, struct sof_source **sources, + struct cir_buf_source *source_bufs, uint32_t frames, struct mux_look_up *lookup) { uint32_t i; @@ -429,11 +423,11 @@ static void mux_s32le(struct comp_dev *dev, struct audio_stream *sink, if (!lookup || !lookup->num_elems) return; - mux_init_look_up_pointers_s32(sink, sources, lookup); + mux_init_look_up_pointers_s32(sink, sink_buf, sources, source_bufs, lookup); while (frames) { frames_without_wrap = - mux_calc_frames_without_wrap_s32(sink, sources, lookup); + mux_calc_frames_without_wrap_s32(sink_buf, source_bufs, lookup); frames_without_wrap = MIN(frames, frames_without_wrap); @@ -449,7 +443,7 @@ static void mux_s32le(struct comp_dev *dev, struct audio_stream *sink, } } - mux_check_for_wrap(sink, sources, lookup); + mux_check_for_wrap(sink_buf, source_bufs, lookup); frames -= frames_without_wrap; } diff --git a/src/include/module/audio/audio_stream.h b/src/include/module/audio/audio_stream.h index 2d8308447d11..78345861a755 100644 --- a/src/include/module/audio/audio_stream.h +++ b/src/include/module/audio/audio_stream.h @@ -75,6 +75,24 @@ struct sof_audio_stream_params { enum sof_audio_buffer_state state; /**< audio stream state */ }; +/** + * @brief Number of whole frames available before a circular buffer wraps. + * + * Computes (end - began) / channels expressed in frames. The pointer distance + * is evaluated in bytes so the helper works for any sample width. + * + * @param began current position in the circular buffer + * @param end one past the last sample of the circular buffer + * @param sample_bytes size of a single sample in bytes (2 for s16, 4 for s32) + * @param channels number of channels, i.e. samples per frame + * @return number of frames that can be processed without wrapping + */ +static inline int circ_buf_frames_without_wrap(const void *began, const void *end, + int sample_bytes, int channels) +{ + return ((const char *)end - (const char *)began) / sample_bytes / channels; +} + /** * @brief Read-only view of source data in a circular buffer. * diff --git a/src/include/module/audio/sink_api.h b/src/include/module/audio/sink_api.h index 14c14af0187b..3101c960c803 100644 --- a/src/include/module/audio/sink_api.h +++ b/src/include/module/audio/sink_api.h @@ -83,6 +83,13 @@ struct sink_ops { */ uint32_t (*get_lft)(struct sof_sink *sink); + /** + * OPTIONAL: get the state of the component consuming data from this sink + * + * see comment of sink_get_state() + */ + int (*get_state)(struct sof_sink *sink); + /** * OPTIONAL: Notification to the sink implementation about changes in audio format * @@ -318,6 +325,25 @@ static inline uint32_t sink_get_pipeline_id(struct sof_sink *sink) return sink->audio_stream_params->pipeline_id; } +/** + * @brief get the state of the component consuming data from this sink + * + * This allows a data producer to query the state of its downstream consumer + * without reaching into the underlying buffer implementation. + * + * @param sink a sink to be checked + * + * @return state of the consuming component, or COMP_STATE_NOT_EXIST (0) if there + * is no connected consumer or the sink implementation does not track it + */ +static inline int sink_get_state(struct sof_sink *sink) +{ + if (!sink->ops->get_state) + return 0; /* COMP_STATE_NOT_EXIST */ + + return sink->ops->get_state(sink); +} + /** * @brief hook to be called when a module connects to the API * diff --git a/src/include/module/audio/source_api.h b/src/include/module/audio/source_api.h index ea4af5442249..33497ba136fc 100644 --- a/src/include/module/audio/source_api.h +++ b/src/include/module/audio/source_api.h @@ -79,6 +79,13 @@ struct source_ops { */ int (*release_data)(struct sof_source *source, size_t free_size); + /** + * OPTIONAL: get the state of the component producing data to this source + * + * see comment of source_get_comp_state() + */ + int (*get_state)(struct sof_source *source); + /** * OPTIONAL: Notification to the source implementation about changes in audio format * @@ -341,6 +348,25 @@ static inline enum sof_audio_buffer_state source_get_state(const struct sof_sour return source->audio_stream_params->state; } +/** + * @brief get the state of the component producing data to this source + * + * This allows a data consumer to query the state of its upstream producer + * without reaching into the underlying buffer implementation. + * + * @param source a source to be checked + * + * @return state of the producing component, or COMP_STATE_NOT_EXIST (0) if there + * is no connected producer or the source implementation does not track it + */ +static inline int source_get_comp_state(struct sof_source *source) +{ + if (!source->ops->get_state) + return 0; /* COMP_STATE_NOT_EXIST */ + + return source->ops->get_state(source); +} + static inline uint32_t source_align_frames_round_up(struct sof_source *source, uint32_t frames) { uint16_t align = source->audio_stream_params->align_frame_cnt;