Hi Pierre,
I'll let Liam handle the first question how to send an IPC command from the userspace.
As for your second question, currently, we do not handle preloading when switching buffers in the MUX component. So yes, you would notice a stall in the main pipeline when you switch. You would also notice that there will be a loss of 1 or 2 periods worth of data due to switching.
Thanks, Ranjani On Tue, 2017-09-05 at 14:32 -0500, Pierre-Louis Bossart wrote:
On 9/5/17 6:58 AM, Ranjani Sridharan wrote:
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.
Sorry for the late feedback, but re-looking at the code, I am still not sure how the 1:N case is handled and how I would use it.
Let's take the simplifying case of my 1:2 case for byt-cr support, where I need to send data to both SSP0/SSP2 but only one is activated by a DAPM route. I don't currently have any controls to know in userspace which of the two is activated, so how would I send a switch command?
Even if I did send an IPC command 'manually' (with a kcontrol?), what happens if the switch command is invoked to go from one to the other pipeline while audio is flowing, could this lead to the main pipeline stalling?
What am I missing?
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com
src/audio/mux.c | 293 +++++++++++++++++++++++++++++++++++-- src/include/reef/audio/Makefile.am | 3 +- src/include/reef/audio/component.h | 1 - src/include/reef/audio/mux.h | 50 +++++++ src/include/uapi/ipc.h | 1 - 5 files changed, 335 insertions(+), 13 deletions(-) create mode 100644 src/include/reef/audio/mux.h
diff --git a/src/audio/mux.c b/src/audio/mux.c index f420bd3..e010104 100644 --- a/src/audio/mux.c +++ b/src/audio/mux.c @@ -26,61 +26,333 @@ * 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:
memcpy(dest, src, frames * nch * sizeof(int32_t));
break;
- default:
trace_mux_error("MUf");
ret = -EINVAL;
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;
- }
- cd->config->source_buffer_id = -1;
- cd->config->sink_buffer_id = -1;
- comp_set_drvdata(dev, cd);
- cd->mux_func = mux_function;
- return NULL;
- 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_route(struct comp_dev *dev,
- int32_t source_buffer_index, int sink_buffer_index)
+{
- struct mux_comp_data *cd = comp_get_drvdata(dev);
- struct comp_buffer *source_buffer, *sink_buffer;
- struct list_item *clist;
- int i = 0;
- if ((source_buffer_index > (cd->num_src_buffer - 1)) ||
- (sink_buffer_index > (cd->num_sink_buffer - 1))) {
trace_mux_error("MUb");
return -EINVAL;
- }
- /* switch source buffer if needed */
- if (source_buffer_index != cd->config->source_buffer_id) {
cd->config->source_buffer_id =
source_buffer_index;
/* 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->source_buffer_id) {
source_buffer->connected = 1;
cd->config->source_buffer =
source_buffer;
} else
source_buffer->connected = 0;
i++;
}
- }
- /* switch sink buffer if needed */
- i = 0;
- if (sink_buffer_index != cd->config->sink_buffer_id) {
cd->config->sink_buffer_id = sink_buffer_index;
/* 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->sink_buffer_id) {
sink_buffer->connected = 1;
cd->config->sink_buffer =
sink_buffer;
} 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;
- count = 0;
- list_for_item(clist, &dev->bsink_list)
count++;
- cd->num_sink_buffer = count;
return 0; } +static int mux_ctrl_cmd(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) +{
- struct mux_comp_data *cd = comp_get_drvdata(dev);
- int source_buffer_index = -1, sink_buffer_index = -1, ret
= 0;
- switch (cdata->cmd) {
- case SOF_CTRL_CMD_ROUTE:
/* check if the ctrl cmd is for this component */
if (cdata->comp_id != dev->comp.id)
break;
if (cdata->num_elems < 1) {
trace_mux_error("Mcd");
ret = -EINVAL;
}
if (cdata->compv[0].index == 0) {
/* new source buffer index */
source_buffer_index = cdata-
compv[0].uvalue;
/* sink buffer index is unchanged */
sink_buffer_index = cd->config-
sink_buffer_id;
} else {
/* new sink buffer index */
sink_buffer_index = cdata-
compv[0].uvalue;
/* source buffer index is unchanged */
source_buffer_index = cd->config-
source_buffer_id;
}
/* switch to the new source/sink buffer(s)*/
ret = mux_route(dev, source_buffer_index,
sink_buffer_index);
break;
- default:
break;
- }
- if (ret < 0)
trace_mux_error("Mct");
- return ret;
+}
/* 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_ctrl_data *cdata = data;
- int ret = 0;
- switch (cmd) {
- case COMP_CMD_SET_VALUE:
ret = mux_ctrl_cmd(dev, cdata);
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 */ +/* copy and process stream data from source to sink buffers
- returns the number of frames copied
- */
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;
- uint32_t copy_bytes;
- return 0;
- /* get the connected source and sink buffers */
- source_buffer = cd->config->source_buffer;
- sink_buffer = cd->config->sink_buffer;
- 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->params.host_period_bytes) {
trace_mux_error("Mcb");
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 dev->frames;
+}
+static int mux_preload(struct comp_dev *dev) +{
- return mux_copy(dev);
+}
+static int mux_default_config(struct comp_dev *dev) +{
- int ret = 0;
- ret = set_buffer_count(dev);
- if (ret < 0)
return -EINVAL;
- ret = mux_route(dev, 0, 0);
- 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 +365,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 e20ffed..ed29da4 100644 --- a/src/include/reef/audio/component.h +++ b/src/include/reef/audio/component.h @@ -80,7 +80,6 @@ #define COMP_CMD_SET_DATA 102 #define COMP_CMD_GET_DATA 103
/* 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..6c6b6af --- /dev/null +++ b/src/include/reef/audio/mux.h @@ -0,0 +1,50 @@ +/*
- 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
+#include <reef/audio/component.h> +/*
- mux_config data structure
- in_buffer_id - ID of input buffer connected to
the MUX component
- out_buffer_id - ID of output buffer connected to
the MUX component
- in_buffer - pointer to the connected source
buffer
- out_buffer - pointer to the connected sink buffer
- */
+struct mux_config {
- uint32_t source_buffer_id, sink_buffer_id;
- struct comp_buffer *source_buffer, *sink_buffer;
+};
+#endif diff --git a/src/include/uapi/ipc.h b/src/include/uapi/ipc.h index c8cdedb..be4c9f2 100644 --- a/src/include/uapi/ipc.h +++ b/src/include/uapi/ipc.h @@ -641,7 +641,6 @@ struct sof_ipc_comp_eq_iir { struct sof_ipc_comp_config config; } __attribute__((packed));
/* frees components, buffers and pipelines * SOF_IPC_TPLG_COMP_FREE, SOF_IPC_TPLG_PIPE_FREE, SOF_IPC_TPLG_BUFFER_FREE */