[Sound-open-firmware] [PATCH v2 1/2] MUX component added

Ranjani Sridharan ranjani.sridharan at linux.intel.com
Tue Sep 5 21:53:36 CEST 2017


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 at 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 at linux.intel.com>
> > + *	   Ranjani Sridharan <ranjani.sridharan at 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 at 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
> >    */
> > 
> 
> 


More information about the Sound-open-firmware mailing list