This patch adds a 1:N or N:1 MUX/DEMUX component. Only one source and one sink buffer can be active at run-time. The MUX component is configured using the COMP_CMD_MUX_SWITCH command passed along with the chosen source and sink buffer configuration. It performs no processing on the input samples, but simply copies the input frames from the source to the sink buffer.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com --- src/audio/mux.c | 275 +++++++++++++++++++++++++++++++++++-- src/include/reef/audio/Makefile.am | 3 +- src/include/reef/audio/component.h | 2 + src/include/reef/audio/mux.h | 46 +++++++ src/include/uapi/ipc.h | 5 + 5 files changed, 322 insertions(+), 9 deletions(-) create mode 100644 src/include/reef/audio/mux.h
diff --git a/src/audio/mux.c b/src/audio/mux.c index f420bd3..ce10da8 100644 --- a/src/audio/mux.c +++ b/src/audio/mux.c @@ -26,35 +26,184 @@ * POSSIBILITY OF SUCH DAMAGE. * * Author: Liam Girdwood liam.r.girdwood@linux.intel.com + * Ranjani Sridharan ranjani.sridharan@linux.intel.com */
+#include <stdio.h> #include <stdint.h> #include <stddef.h> #include <reef/lock.h> +#include <reef/alloc.h> #include <reef/list.h> #include <reef/stream.h> +#include <reef/trace.h> #include <reef/audio/component.h> +#include <reef/audio/mux.h> +#include <reef/audio/pipeline.h> +#include <uapi/ipc.h>
/* tracing */ -#define trace_mux(__e) trace_event(TRACE_CLASS_MUX, __e) +#define trace_mux(__e) trace_event(TRACE_CLASS_MUX, __e) #define trace_mux_error(__e) trace_error(TRACE_CLASS_MUX, __e) #define tracev_mux(__e) tracev_event(TRACE_CLASS_MUX, __e)
+/* + * mux_comp_data structure + * num_src_buffer - number of source buffers + * num_sink_buffer - number of sink buffers + * config - current configuration of the MUX component + * mux_func - function that performs the routing of input buffer to + * the output buffer based on config + */ +struct mux_comp_data { + uint32_t num_src_buffer, num_sink_buffer; + struct mux_config *config; + int (*mux_func)(struct comp_dev *dev, struct comp_buffer *sink, + struct comp_buffer *source, uint32_t frames); + +}; + +/* mux function to copy data from source to sink buffer */ +static int mux_function(struct comp_dev *dev, struct comp_buffer *sink_buffer, + struct comp_buffer *source_buffer, uint32_t frames) +{ + int32_t *src = (int32_t *) source_buffer->r_ptr; + int32_t *dest = (int32_t *) sink_buffer->w_ptr; + int nch = dev->params.channels; + struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev); + int ret = 0; + + /* copy frames from source to destination */ + switch (config->frame_fmt) { + case SOF_IPC_FRAME_S16_LE: + memcpy(dest, src, frames * nch * sizeof(int16_t)); + break; + case SOF_IPC_FRAME_S32_LE: + case SOF_IPC_FRAME_S24_4LE: + case SOF_IPC_FRAME_FLOAT: + default: + memcpy(dest, src, frames * nch * sizeof(int32_t)); + break; + } + + return ret; +} + static struct comp_dev *mux_new(struct sof_ipc_comp *comp) { - trace_mux("new"); + struct comp_dev *dev; + struct mux_comp_data *cd; + + trace_mux("MUn"); + dev = rzalloc(RZONE_RUNTIME, RFLAGS_NONE, + COMP_SIZE(struct sof_ipc_comp_mux)); + if (dev == NULL) + return NULL; + + memcpy(&dev->comp, comp, sizeof(struct sof_ipc_comp_mux)); + + cd = rzalloc(RZONE_RUNTIME, RFLAGS_NONE, sizeof(*cd)); + if (cd == NULL) { + rfree(dev); + return NULL; + } + + cd->config = rzalloc(RZONE_RUNTIME, RFLAGS_NONE, + sizeof(struct mux_config)); + if (cd == NULL) { + rfree(dev); + rfree(cd); + return NULL; + }
- return NULL; + comp_set_drvdata(dev, cd); + cd->mux_func = mux_function; + + return dev; }
static void mux_free(struct comp_dev *dev) { + struct mux_comp_data *cd = comp_get_drvdata(dev);
+ rfree(cd->config); + rfree(cd); + rfree(dev); }
-/* set component audio stream paramters */ +/* set component audio stream parameters */ static int mux_params(struct comp_dev *dev) { + return 0; +} + +/* set new mux component configuration + * updates the connection status of sink/source buffers + */ +static int mux_switch_config(struct comp_dev *dev, + struct mux_config *new_config) +{ + struct mux_comp_data *cd = comp_get_drvdata(dev); + struct comp_buffer *source_buffer, *sink_buffer; + struct list_item *clist; + int i = 0; + + if ((new_config->in_buffer > cd->num_src_buffer) || + (new_config->out_buffer > cd->num_sink_buffer)) { + trace_mux_error("MUb"); + return -EINVAL; + } + + cd->config->in_buffer = new_config->in_buffer; + cd->config->out_buffer = new_config->out_buffer; + + /* connect new MUX source buffer and disconnect the rest */ + list_for_item_prev(clist, &dev->bsource_list) { + source_buffer = container_of(clist, struct comp_buffer, + sink_list); + if (i == cd->config->in_buffer) + source_buffer->connected = 1; + else + source_buffer->connected = 0; + i++; + } + + i = 0; + /* connect new MUX sink buffer and disconnect the rest */ + list_for_item_prev(clist, &dev->bsink_list) { + sink_buffer = container_of(clist, struct comp_buffer, + source_list); + if (i == cd->config->out_buffer) + sink_buffer->connected = 1; + else + sink_buffer->connected = 0; + i++; + } + + return 0; +} + +/* set the number of input and output buffers connected at pipeline setup */ +static int set_buffer_count(struct comp_dev *dev) +{ + struct list_item *clist; + struct mux_comp_data *cd = comp_get_drvdata(dev); + int count = 0; + + if (list_is_empty(&dev->bsource_list) || + list_is_empty(&dev->bsink_list)) { + trace_mux_error("Mbc"); + return -EINVAL; + } + + list_for_item(clist, &dev->bsource_list) + count++; + cd->num_src_buffer = count - 1; + + count = 0; + list_for_item(clist, &dev->bsink_list) + count++; + cd->num_sink_buffer = count - 1;
return 0; } @@ -62,25 +211,134 @@ static int mux_params(struct comp_dev *dev) /* used to pass standard and bespoke commands (with data) to component */ static int mux_cmd(struct comp_dev *dev, int cmd, void *data) { - /* mux will use buffer "connected" status */ - return 0; + struct sof_ipc_mux_switch *assign; + struct mux_config *new_config; + int ret = 0; + + switch (cmd) { + case COMP_CMD_MUX_SWITCH: + /* switch config only if pipeline has been stopped or paused*/ + if (dev->state == COMP_STATE_SETUP || + dev->state == COMP_STATE_PAUSED) { + assign = (struct sof_ipc_mux_switch *) data; + new_config = (struct mux_config *) assign->data; + ret = mux_switch_config(dev, new_config); + if (ret < 0) + trace_mux_error("Mec"); + } else { + trace_mux_error("Mes"); + ret = -EINVAL; + } + break; + case COMP_CMD_START: + case COMP_CMD_STOP: + case COMP_CMD_PAUSE: + case COMP_CMD_RELEASE: + default: + ret = comp_set_state(dev, cmd); + break; + } + + return ret; }
/* copy and process stream data from source to sink buffers */ static int mux_copy(struct comp_dev *dev) { + struct mux_comp_data *cd = comp_get_drvdata(dev); + struct comp_buffer *source_buffer = NULL, *sink_buffer = NULL; + int i = 0; + uint32_t copy_bytes; + struct list_item *clist; + + /* check if the selected source buffer is connected */ + list_for_item_prev(clist, &dev->bsource_list) { + if (i == cd->config->in_buffer) { + source_buffer = container_of(clist, struct comp_buffer, + sink_list); + if (source_buffer->connected == 1) + goto sink; + else { + trace_mux_error("Msc"); + return -EINVAL; + } + } + i++; + } + +sink: + i = 0; + /* check if the selected sink buffer is connected */ + list_for_item_prev(clist, &dev->bsink_list) { + if (i == cd->config->out_buffer) { + sink_buffer = container_of(clist, struct comp_buffer, + source_list); + if (sink_buffer->connected == 1) + goto copy; + else { + trace_mux_error("Msb"); + return -EINVAL; + } + } + i++; + } + +copy: + if (!source_buffer || !sink_buffer) { + trace_mux_error("Mbn"); + return -EINVAL; + } + + /* check that source has enough frames available and sink enough + * frames free. + */ + copy_bytes = comp_buffer_get_copy_bytes(dev, source_buffer, + sink_buffer); + + if (copy_bytes < dev->frames * dev->frame_bytes) + return -EINVAL; + + /* Run passthrough if buffers have enough room */ + cd->mux_func(dev, sink_buffer, source_buffer, dev->frames); + + comp_update_buffer_consume(source_buffer, + dev->params.host_period_bytes); + comp_update_buffer_produce(sink_buffer, dev->params.host_period_bytes);
return 0; }
+static int mux_preload(struct comp_dev *dev) +{ + return mux_copy(dev); +} + +static int mux_default_config(struct comp_dev *dev) +{ + struct mux_config config = {0, 0}; + int ret = 0; + + ret = set_buffer_count(dev); + if (ret < 0) + return -EINVAL; + + ret = mux_switch_config(dev, &config); + + return ret; +} + static int mux_reset(struct comp_dev *dev) { - return 0; + trace_mux("MUr"); + dev->state = COMP_STATE_INIT; + return mux_default_config(dev); }
static int mux_prepare(struct comp_dev *dev) { - return 0; + trace_mux("MUp"); + dev->state = COMP_STATE_PREPARE; + return mux_default_config(dev); }
struct comp_driver comp_mux = { @@ -93,6 +351,7 @@ struct comp_driver comp_mux = { .copy = mux_copy, .prepare = mux_prepare, .reset = mux_reset, + .preload = mux_preload, }, };
diff --git a/src/include/reef/audio/Makefile.am b/src/include/reef/audio/Makefile.am index b02684c..509a34a 100644 --- a/src/include/reef/audio/Makefile.am +++ b/src/include/reef/audio/Makefile.am @@ -1,4 +1,5 @@ noinst_HEADERS = \ component.h \ pipeline.h \ - buffer.h + buffer.h \ + mux.h diff --git a/src/include/reef/audio/component.h b/src/include/reef/audio/component.h index cbd2356..47dd7e8 100644 --- a/src/include/reef/audio/component.h +++ b/src/include/reef/audio/component.h @@ -82,6 +82,8 @@ #define COMP_CMD_EQ_IIR_CONFIG 109 /* Configuration data for IIR EQ */ #define COMP_CMD_EQ_IIR_SWITCH 110 /* Response update request for IIR EQ */
+#define COMP_CMD_MUX_SWITCH 111 /* MUX switch config */ + /* MMAP IPC status */ #define COMP_CMD_IPC_MMAP_RPOS 200 /* host read position */ #define COMP_CMD_IPC_MMAP_PPOS 201 /* DAI presentation position */ diff --git a/src/include/reef/audio/mux.h b/src/include/reef/audio/mux.h new file mode 100644 index 0000000..a765a6a --- /dev/null +++ b/src/include/reef/audio/mux.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Author: Ranjani Sridharan ranjani.sridharan@linux.intel.com + */ + +#ifndef SOF_COMP_MUX_H +#define SOF_COMP_MUX_H + +/* + * mux_config data structure + * in_buffer - ID of input buffer connected to the MUX component + * out_buffer - ID of output buffer connected to the MUX component + */ + +struct mux_config { + uint32_t in_buffer, out_buffer; +}; + + + +#endif diff --git a/src/include/uapi/ipc.h b/src/include/uapi/ipc.h index 053390c..e0d0303 100644 --- a/src/include/uapi/ipc.h +++ b/src/include/uapi/ipc.h @@ -622,6 +622,11 @@ struct sof_ipc_eq_iir_switch { int32_t data[]; } __attribute__((packed));
+struct sof_ipc_mux_switch { + struct sof_ipc_comp comp; + uint32_t data[]; +} __attribute__((packed)); + /* frees components, buffers and pipelines * SOF_IPC_TPLG_COMP_FREE, SOF_IPC_TPLG_PIPE_FREE, SOF_IPC_TPLG_BUFFER_FREE */