diff --git a/src/audio/copier/copier.c b/src/audio/copier/copier.c index 2375b540b493..7f2746d6bf9b 100644 --- a/src/audio/copier/copier.c +++ b/src/audio/copier/copier.c @@ -5,6 +5,8 @@ // Author: Rander Wang #include +#include +#include #include #include #include @@ -605,72 +607,138 @@ static int copier_copy_to_sinks(struct copier_data *cd, struct comp_dev *dev, return ret; } +/** + * @brief Describe a source read window as a cir_buf_source for the PCM converter. + * + * Acquire the source data window through the source API and bridge it into a + * cir_buf_source descriptor (buffer base/end address and read pointer). The read + * pointer is not advanced here; the caller releases the source when done. Channel + * count is passed to the converter separately. + * + * @param cir Descriptor to populate. + * @param source Source handle to read from. + * @param bytes Amount of data to make available to the converter. + * @return 0 on success, negative error code otherwise. + */ +static int copier_source_get_stream(struct cir_buf_source *cir, + struct sof_source *source, size_t bytes) +{ + const void *data_ptr, *buf_start; + size_t buf_size; + int ret; + + ret = source_get_data(source, bytes, &data_ptr, &buf_start, &buf_size); + if (ret) + return ret; + + cir->buf_start = buf_start; + cir->buf_end = (const char *)buf_start + buf_size; + cir->ptr = data_ptr; + + return 0; +} + +/** + * @brief Describe a sink write window as a cir_buf_sink for the PCM converter. + * + * Sibling of copier_source_get_stream() for the sink side. Acquire the sink + * buffer through the sink API and bridge it into a cir_buf_sink descriptor. The + * write pointer is not advanced here; the caller commits the sink when done. + * + * @param cir Descriptor to populate. + * @param sink Sink handle to write to. + * @param bytes Amount of free space to make available to the converter. + * @return 0 on success, negative error code otherwise. + */ +static int copier_sink_get_stream(struct cir_buf_sink *cir, + struct sof_sink *sink, size_t bytes) +{ + void *data_ptr, *buf_start; + size_t buf_size; + int ret; + + ret = sink_get_buffer(sink, bytes, &data_ptr, &buf_start, &buf_size); + if (ret) + return ret; + + cir->buf_start = buf_start; + cir->buf_end = (char *)buf_start + buf_size; + cir->ptr = data_ptr; + + return 0; +} + static int copier_module_copy(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 copier_data *cd = module_get_private_data(mod); - struct comp_buffer *src_c; - struct comp_copy_limits processed_data; + struct cir_buf_source src_cir; + struct sof_source *source; + uint32_t src_frames_avail, frames = 0; + int ret; int i; - if (!num_input_buffers || !num_output_buffers) + if (!num_of_sources || !num_of_sinks) return 0; - src_c = container_of(input_buffers[0].data, struct comp_buffer, stream); + source = sources[0]; - processed_data.source_bytes = 0; + src_frames_avail = source_get_data_frames_available(source); + ret = copier_source_get_stream(&src_cir, source, + src_frames_avail * source_get_frame_bytes(source)); + if (ret) + return ret; /* convert format and copy to each active sink */ - for (i = 0; i < num_output_buffers; i++) { + for (i = 0; i < num_of_sinks; i++) { struct comp_buffer *sink_c; struct comp_dev *sink_dev; + struct cir_buf_sink snk_cir; + size_t sink_bytes, source_samples; + int sink_queue_id; - sink_c = container_of(output_buffers[i].data, struct comp_buffer, stream); + /* + * The consumer component's activity state is a pipeline-topology property + * with no sink/source API equivalent, so it is still read via comp_buffer. + */ + sink_c = comp_buffer_get_from_sink(sinks[i]); sink_dev = comp_buffer_get_sink_component(sink_c); - processed_data.sink_bytes = 0; - if (sink_dev->state == COMP_STATE_ACTIVE) { - /* Bridge the legacy audio_stream buffers into cir_buf descriptors for - * the new pcm_converter interface (read/write pointers = offset 0). - */ - struct cir_buf_source src_cir = { - .buf_start = audio_stream_get_addr(input_buffers[0].data), - .buf_end = audio_stream_get_end_addr(input_buffers[0].data), - .ptr = audio_stream_get_rptr(input_buffers[0].data), - }; - struct cir_buf_sink snk_cir = { - .buf_start = audio_stream_get_addr(output_buffers[i].data), - .buf_end = audio_stream_get_end_addr(output_buffers[i].data), - .ptr = audio_stream_get_wptr(output_buffers[i].data), - }; - uint32_t source_samples; - int sink_queue_id; - pcm_converter_func converter; - - /* - * Buffer ID is constructed as IPC4_COMP_ID(src_queue, dst_queue). - * From the buffer's perspective, copier's sink is the source, - * so we use IPC4_SRC_QUEUE_ID() to get the correct copier sink index. - */ - sink_queue_id = IPC4_SRC_QUEUE_ID(buf_get_id(sink_c)); - if (sink_queue_id >= IPC4_COPIER_MODULE_OUTPUT_PINS_COUNT) - return -EINVAL; - converter = cd->converter[sink_queue_id]; + if (sink_dev->state != COMP_STATE_ACTIVE) + continue; - comp_get_copy_limits(src_c, sink_c, &processed_data); + /* + * Buffer ID is constructed as IPC4_COMP_ID(src_queue, dst_queue). + * From the buffer's perspective, copier's sink is the source, + * so we use IPC4_SRC_QUEUE_ID() to get the correct copier sink index. + */ + sink_queue_id = IPC4_SRC_QUEUE_ID(sink_get_id(sinks[i])); + if (sink_queue_id >= IPC4_COPIER_MODULE_OUTPUT_PINS_COUNT) { + source_release_data(source, 0); + return -EINVAL; + } - source_samples = processed_data.frames * - audio_stream_get_channels(input_buffers[0].data); - converter(&src_cir, audio_stream_get_channels(input_buffers[0].data), - &snk_cir, audio_stream_get_channels(output_buffers[i].data), - source_samples, DUMMY_CHMAP); + frames = MIN(src_frames_avail, sink_get_free_frames(sinks[i])); + sink_bytes = frames * sink_get_frame_bytes(sinks[i]); + source_samples = frames * source_get_channels(source); - output_buffers[i].size = processed_data.sink_bytes; - cd->output_total_data_processed += processed_data.sink_bytes; + ret = copier_sink_get_stream(&snk_cir, sinks[i], sink_bytes); + if (ret) { + source_release_data(source, 0); + return ret; } + + cd->converter[sink_queue_id](&src_cir, source_get_channels(source), + &snk_cir, sink_get_channels(sinks[i]), + source_samples, DUMMY_CHMAP); + + /* commit produced data to the sink (cache writeback + advance wptr) */ + sink_commit_buffer(sinks[i], sink_bytes); + cd->output_total_data_processed += sink_bytes; } - input_buffers[0].consumed = processed_data.source_bytes; + /* consume the data read by the converter from the source */ + source_release_data(source, frames * source_get_frame_bytes(source)); return 0; } @@ -730,8 +798,8 @@ static int copier_multi_endpoint_dai_copy(struct copier_data *cd, struct comp_de * gateway, i.e., produce/consume single stream. */ static int copier_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 copier_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; @@ -755,8 +823,7 @@ static int copier_process(struct processing_module *mod, } /* module copier case */ - return copier_module_copy(mod, input_buffers, num_input_buffers, output_buffers, - num_output_buffers); + return copier_module_copy(mod, sources, num_of_sources, sinks, num_of_sinks); } static int copier_params(struct processing_module *mod) @@ -1288,7 +1355,7 @@ static APP_SYSUSER_DATA const struct module_endpoint_ops copier_endpoint_ops = { static APP_SYSUSER_DATA const struct module_interface copier_interface = { .init = copier_init, .prepare = copier_prepare, - .process_audio_stream = copier_process, + .process = copier_process, .reset = copier_reset, .free = copier_free, .set_configuration = copier_set_configuration, diff --git a/src/audio/copier/copier_ipcgtw.c b/src/audio/copier/copier_ipcgtw.c index 115c9a4a7e2d..29c7780f6423 100644 --- a/src/audio/copier/copier_ipcgtw.c +++ b/src/audio/copier/copier_ipcgtw.c @@ -4,6 +4,9 @@ #include #include +#include +#include +#include #include #include #include @@ -30,65 +33,99 @@ static struct comp_dev *find_ipcgtw_by_node_id(union ipc4_connector_node_id node return NULL; } -static inline void audio_stream_copy_bytes_from_linear(const void *linear_source, - struct audio_stream *sink, - unsigned int bytes) +static int sink_copy_bytes_from_linear(const void *linear_source, + struct sof_sink *sink, size_t bytes) { const uint8_t *src = (const uint8_t *)linear_source; - uint8_t *snk = audio_stream_wrap(sink, audio_stream_get_wptr(sink)); - size_t bytes_snk, bytes_copied; - - while (bytes) { - bytes_snk = audio_stream_bytes_without_wrap(sink, snk); - bytes_copied = MIN(bytes, bytes_snk); - memcpy_s(snk, bytes_copied, src, bytes_copied); - bytes -= bytes_copied; - src += bytes_copied; - snk = audio_stream_wrap(sink, snk + bytes_copied); + uint8_t *snk, *snk_begin, *snk_end; + size_t remaining = bytes; + size_t snk_size; + int ret; + + if (!bytes) + return 0; + + ret = sink_get_buffer(sink, bytes, (void **)&snk, (void **)&snk_begin, &snk_size); + if (ret) + return ret; + + snk_end = snk_begin + snk_size; + while (remaining) { + size_t chunk = MIN(remaining, (size_t)cir_buf_bytes_without_wrap(snk, snk_end)); + + ret = memcpy_s(snk, chunk, src, chunk); + if (ret) { + sink_commit_buffer(sink, bytes - remaining); + return ret; + } + + remaining -= chunk; + src += chunk; + snk = cir_buf_wrap(snk + chunk, snk_begin, snk_end); } + + return sink_commit_buffer(sink, bytes); } -static inline -void audio_stream_copy_bytes_to_linear(const struct audio_stream *source, - void *linear_sink, unsigned int bytes) +static int source_copy_bytes_to_linear(struct sof_source *source, + void *linear_sink, size_t bytes) { - uint8_t *src = audio_stream_wrap(source, audio_stream_get_rptr(source)); + const uint8_t *src, *src_begin, *src_end; uint8_t *snk = (uint8_t *)linear_sink; - size_t bytes_src, bytes_copied; - - while (bytes) { - bytes_src = audio_stream_bytes_without_wrap(source, src); - bytes_copied = MIN(bytes, bytes_src); - memcpy_s(snk, bytes_copied, src, bytes_copied); - bytes -= bytes_copied; - src = audio_stream_wrap(source, src + bytes_copied); - snk += bytes_copied; + size_t remaining = bytes; + size_t src_size; + int ret; + + if (!bytes) + return 0; + + ret = source_get_data(source, bytes, (const void **)&src, + (const void **)&src_begin, &src_size); + if (ret) + return ret; + + src_end = src_begin + src_size; + while (remaining) { + size_t chunk = MIN(remaining, (size_t)cir_buf_bytes_without_wrap(src, src_end)); + + ret = memcpy_s(snk, chunk, src, chunk); + if (ret) { + source_release_data(source, bytes - remaining); + return ret; + } + + remaining -= chunk; + snk += chunk; + src = cir_buf_wrap(src + chunk, src_begin, src_end); } + + return source_release_data(source, bytes); } -static inline struct comp_buffer *get_buffer(struct comp_dev *dev) +static inline struct sof_source *ipcgtw_get_source(struct comp_dev *dev) { - if (dev->direction == SOF_IPC_STREAM_PLAYBACK) { - if (list_is_empty(&dev->bsink_list)) - return NULL; - return comp_dev_get_first_data_consumer(dev); - } + struct processing_module *mod = comp_mod(dev); - assert(dev->direction == SOF_IPC_STREAM_CAPTURE); + return mod->num_of_sources ? mod->sources[0] : NULL; +} - if (list_is_empty(&dev->bsource_list)) - return NULL; - return comp_dev_get_first_data_producer(dev); +static inline struct sof_sink *ipcgtw_get_sink(struct comp_dev *dev) +{ + struct processing_module *mod = comp_mod(dev); + + return mod->num_of_sinks ? mod->sinks[0] : NULL; } int copier_ipcgtw_process(const struct ipc4_ipcgtw_cmd *cmd, void *reply_payload, uint32_t *reply_payload_size) { const struct ipc4_ipc_gateway_cmd_data *in; + struct ipc4_ipc_gateway_cmd_data_reply *out; + struct sof_source *source = NULL; + struct sof_sink *sink = NULL; struct comp_dev *dev; - struct comp_buffer *buf; uint32_t data_size; - struct ipc4_ipc_gateway_cmd_data_reply *out; + int ret; dcache_invalidate_region((__sparse_force void __sparse_cache *)MAILBOX_HOSTBOX_BASE, sizeof(struct ipc4_ipc_gateway_cmd_data)); @@ -101,28 +138,31 @@ int copier_ipcgtw_process(const struct ipc4_ipcgtw_cmd *cmd, comp_dbg(dev, "%x %x", cmd->primary.dat, cmd->extension.dat); - buf = get_buffer(dev); - - if (!buf) { - /* NOTE: this func is called from IPC processing task and can be potentially - * called before pipeline start even before buffer has been attached. In such - * case do not report error but return 0 bytes available for GET_DATA and - * 0 bytes free for SET_DATA. - */ - comp_warn(dev, "no buffer found"); - } - out = (struct ipc4_ipc_gateway_cmd_data_reply *)reply_payload; + if (dev->direction == SOF_IPC_STREAM_PLAYBACK) + sink = ipcgtw_get_sink(dev); + else + source = ipcgtw_get_source(dev); + + /* + * NOTE: this func is called from the IPC processing task and can potentially be + * called before pipeline start, even before the buffer has been attached. In that + * case the sink/source handles are not available yet, so do not report an error but + * return 0 bytes available for GET_DATA and 0 bytes free for SET_DATA. + */ + if (!sink && !source) + comp_warn(dev, "no buffer found"); + switch (cmd->primary.r.cmd) { case IPC4_IPCGWCMD_GET_DATA: - if (buf) { + if (source) { data_size = MIN(cmd->extension.r.data_size, SOF_IPC_MSG_MAX_SIZE - 4); - data_size = MIN(data_size, audio_stream_get_avail_bytes(&buf->stream)); - buffer_stream_invalidate(buf, data_size); - audio_stream_copy_bytes_to_linear(&buf->stream, out->payload, data_size); - comp_update_buffer_consume(buf, data_size); - out->u.size_avail = audio_stream_get_avail_bytes(&buf->stream); + data_size = MIN(data_size, source_get_data_available(source)); + ret = source_copy_bytes_to_linear(source, out->payload, data_size); + if (ret) + return ret; + out->u.size_avail = source_get_data_available(source); *reply_payload_size = data_size + 4; } else { out->u.size_avail = 0; @@ -131,18 +171,17 @@ int copier_ipcgtw_process(const struct ipc4_ipcgtw_cmd *cmd, break; case IPC4_IPCGWCMD_SET_DATA: - if (buf) { + if (sink) { data_size = MIN(cmd->extension.r.data_size, - audio_stream_get_free_bytes(&buf->stream)); + sink_get_free_size(sink)); dcache_invalidate_region((__sparse_force void __sparse_cache *) MAILBOX_HOSTBOX_BASE, data_size + offsetof(struct ipc4_ipc_gateway_cmd_data, payload)); - audio_stream_copy_bytes_from_linear(in->payload, &buf->stream, - data_size); - buffer_stream_writeback(buf, data_size); - comp_update_buffer_produce(buf, data_size); + ret = sink_copy_bytes_from_linear(in->payload, sink, data_size); + if (ret) + return ret; out->u.size_consumed = data_size; *reply_payload_size = 4; } else { @@ -153,8 +192,10 @@ int copier_ipcgtw_process(const struct ipc4_ipcgtw_cmd *cmd, case IPC4_IPCGWCMD_FLUSH_DATA: *reply_payload_size = 0; - if (buf) - audio_stream_reset(&buf->stream); + if (sink) + audio_buffer_reset(sof_audio_buffer_from_sink(sink)); + else if (source) + audio_buffer_reset(sof_audio_buffer_from_source(source)); break; default: @@ -169,13 +210,23 @@ int copier_ipcgtw_process(const struct ipc4_ipcgtw_cmd *cmd, int copier_ipcgtw_params(struct ipcgtw_data *ipcgtw_data, struct comp_dev *dev, struct sof_ipc_stream_params *params) { + struct sof_sink *sink = NULL; + struct sof_source *source = NULL; struct comp_buffer *buf; int err; comp_dbg(dev, "ipcgtw_params()"); - buf = get_buffer(dev); - if (!buf) { + if (dev->direction == SOF_IPC_STREAM_PLAYBACK) + sink = ipcgtw_get_sink(dev); + else + source = ipcgtw_get_source(dev); + + if (sink) + buf = comp_buffer_get_from_sink(sink); + else if (source) + buf = comp_buffer_get_from_source(source); + else { comp_err(dev, "no buffer found"); return -EINVAL; } @@ -194,13 +245,20 @@ int copier_ipcgtw_params(struct ipcgtw_data *ipcgtw_data, struct comp_dev *dev, void copier_ipcgtw_reset(struct comp_dev *dev) { - struct comp_buffer *buf = get_buffer(dev); - - if (buf) { - audio_stream_reset(&buf->stream); - } else { + struct sof_sink *sink = NULL; + struct sof_source *source = NULL; + + if (dev->direction == SOF_IPC_STREAM_PLAYBACK) + sink = ipcgtw_get_sink(dev); + else + source = ipcgtw_get_source(dev); + + if (sink) + audio_buffer_reset(sof_audio_buffer_from_sink(sink)); + else if (source) + audio_buffer_reset(sof_audio_buffer_from_source(source)); + else comp_warn(dev, "no buffer found"); - } } __cold int copier_ipcgtw_create(struct processing_module *mod,