[Sound-open-firmware] [PATCH] pipeline: Add support for full pipeline pre-loading.

Keyon Jie yang.jie at linux.intel.com
Fri Feb 10 15:06:27 CET 2017


From: Liam Girdwood <liam.r.girdwood at linux.intel.com>

Preload entire pipeline and all components by walking the pipeline graph.

Signed-off-by: Liam Girdwood <liam.r.girdwood at linux.intel.com>
Modified-by: Keyon Jie <yang.jie at linux.intel.com>
---
 src/audio/dai.c                    |  8 ++++
 src/audio/host.c                   |  9 ++--
 src/audio/mixer.c                  | 46 ++++++++++++++++--
 src/audio/pipeline.c               | 96 ++++++++++++++++++++++++++++++++++++--
 src/audio/volume.c                 | 18 ++++---
 src/include/reef/audio/component.h | 17 ++++++-
 6 files changed, 173 insertions(+), 21 deletions(-)

diff --git a/src/audio/dai.c b/src/audio/dai.c
index 230acbf..75f9acf 100644
--- a/src/audio/dai.c
+++ b/src/audio/dai.c
@@ -488,6 +488,12 @@ static int dai_copy(struct comp_dev *dev)
 	return 0;
 }
 
+/* source component will preload dai */
+static int dai_preload(struct comp_dev *dev)
+{
+	return 0;
+}
+
 static int dai_config(struct comp_dev *dev, struct dai_config *dai_config)
 {
 	struct dai_data *dd = comp_get_drvdata(dev);
@@ -513,6 +519,7 @@ static struct comp_driver comp_dai_ssp = {
 		.prepare	= dai_prepare,
 		.reset		= dai_reset,
 		.dai_config	= dai_config,
+		.preload	= dai_preload,
 		.dai_set_loopback = dai_set_loopback,
 	},
 };
@@ -526,6 +533,7 @@ static struct comp_driver comp_dai_hda = {
 		.cmd		= dai_cmd,
 		.copy		= dai_copy,
 		.prepare	= dai_prepare,
+		.preload	= dai_preload,
 	},
 };
 
diff --git a/src/audio/host.c b/src/audio/host.c
index d71c010..d1e4eaf 100644
--- a/src/audio/host.c
+++ b/src/audio/host.c
@@ -586,8 +586,9 @@ static int host_preload(struct comp_dev *dev)
 	int ret, i;
 
 	trace_host("PrL");
+
 	/* preload all periods */
-	for (i = 0; i < PLAT_HOST_PERIODS; i++) {
+	for (i = 0; i < dev->preload; i++) {
 		/* do DMA transfer */
 		wait_init(&hd->complete);
 		dma_set_config(hd->dma, hd->chan, &hd->config);
@@ -632,11 +633,8 @@ static int host_prepare(struct comp_dev *dev)
 		hd->params.period_frames * hd->params.frame_size;
 	hd->split_remaining = 0;
 
+	dev->preload = PLAT_HOST_PERIODS;
 
-	if (hd->params.direction == STREAM_DIRECTION_PLAYBACK)
-		host_preload(dev);
-
-	host_update_buffer_consume(hd);
 	dev->state = COMP_STATE_PREPARE;
 	return 0;
 }
@@ -830,6 +828,7 @@ struct comp_driver comp_host = {
 		.cmd		= host_cmd,
 		.copy		= host_copy,
 		.prepare	= host_prepare,
+		.preload	= host_preload,
 		.host_buffer	= host_buffer,
 	},
 };
diff --git a/src/audio/mixer.c b/src/audio/mixer.c
index c653fd9..740ccaf 100644
--- a/src/audio/mixer.c
+++ b/src/audio/mixer.c
@@ -286,18 +286,53 @@ static int mixer_reset(struct comp_dev *dev)
 	return 0;
 }
 
+/*
+ * Prepare the mixer. The mixer may already be running at this point with other
+ * sources. Make sure we only prepare the "prepared" source streams and not
+ * the active or inactive sources.
+ *
+ * We should also make sure that we propagate the prepare call to downstream
+ * if downstream is not currently active.
+ */
 static int mixer_prepare(struct comp_dev *dev)
 {
 	struct mixer_data *md = comp_get_drvdata(dev);
-	int i;
+	struct list_item * blist;
+	struct comp_buffer *source;
+	int downstream = 0;
 
 	trace_mixer("MPp");
-	md->mix_func = mix_n;
 
-	dev->state = COMP_STATE_PREPARE;
+	if (dev->state != COMP_STATE_RUNNING) {
+		md->mix_func = mix_n;
+		dev->state = COMP_STATE_PREPARE;
+		dev->preload = PLAT_INT_PERIODS;
+	}
+
+	/* check each mixer source state */
+	list_for_item(blist, &dev->bsource_list) {
+		source = container_of(blist, struct comp_buffer, sink_list);
+
+		/* only prepare downstream if we have no active sources */
+		if (source->source->state == COMP_STATE_PAUSED ||
+				source->source->state == COMP_STATE_RUNNING) {
+			downstream = 1;
+		}
+	}
+
+	/* prepare downstream */
+	return downstream;
+}
+
+static int mixer_preload(struct comp_dev *dev)
+{
+	int i;
+
+	if (dev->state != COMP_STATE_PREPARE)
+		return 1;
 
-	/* mix periods */
-	for (i = 0; i < PLAT_HOST_PERIODS; i++)
+	/* preload and mix periods if inactive */
+	for (i = 0; i < dev->preload; i++)
 		mixer_copy(dev);
 
 	return 0;
@@ -310,6 +345,7 @@ struct comp_driver comp_mixer = {
 		.free		= mixer_free,
 		.params		= mixer_params,
 		.prepare	= mixer_prepare,
+		.preload	= mixer_preload,
 		.cmd		= mixer_cmd,
 		.copy		= mixer_copy,
 		.reset		= mixer_reset,
diff --git a/src/audio/pipeline.c b/src/audio/pipeline.c
index 41693ae..5c03659 100644
--- a/src/audio/pipeline.c
+++ b/src/audio/pipeline.c
@@ -306,6 +306,78 @@ int pipeline_comp_connect(struct pipeline *p, struct comp_dev *source,
 	return 0;
 }
 
+/* preload component buffers prior to playback start */
+static int component_preload(struct comp_dev *comp, uint32_t depth,
+		uint32_t max_depth)
+{
+	struct list_item *clist;
+	int err;
+
+	/* bail if we are at max_depth */
+	if (depth > max_depth)
+		return 0;
+
+	trace_value(comp->id);
+	err = comp_preload(comp);
+
+	/* dont walk the graph any further if this component fails */
+	if (err < 0) {
+		trace_pipe_error("eOp");
+		return err;
+	} else if (err > 0 || comp->is_dai) {
+		/* we finish walking the graph if we reach the DAI or component is
+		 * currently active and configured already (err > 0).
+		 */
+		trace_pipe("prl");
+		trace_value((uint32_t)err);
+		trace_value(comp->is_dai);
+		trace_value(comp->id);
+		trace_value(comp->is_host);
+		return 0;
+	}
+
+	/* now run this operation downstream */
+	list_for_item(clist, &comp->bsink_list) {
+		struct comp_buffer *buffer;
+
+		buffer = container_of(clist, struct comp_buffer, source_list);
+
+		/* dont go downstream if this component is not connected */
+		if (!buffer->connected)
+			continue;
+
+		err = component_preload(buffer->sink, depth + 1, max_depth);
+		if (err < 0)
+			break;
+	}
+
+	return err;
+}
+
+/* work out the pipeline depth from this component to the farthest endpoint */
+static int pipeline_depth(struct comp_dev *comp, int current)
+{
+	struct list_item *clist;
+	int depth = 0;
+
+	/* go down stream to deepest endpoint */
+	list_for_item(clist, &comp->bsink_list) {
+		struct comp_buffer *buffer;
+
+		buffer = container_of(clist, struct comp_buffer, source_list);
+
+		/* dont go downstream if this component is not connected */
+		if (!buffer->connected)
+			continue;
+
+		depth = pipeline_depth(buffer->sink, current + 1);
+		if (depth > current)
+			current = depth;
+	}
+
+	return current;
+}
+
 /* call op on all downstream components - locks held by caller */
 static int component_op_sink(struct op_data *op_data, struct comp_dev *comp)
 {
@@ -459,7 +531,7 @@ static int component_op_source(struct op_data *op_data, struct comp_dev *comp)
 int pipeline_prepare(struct pipeline *p, struct comp_dev *host)
 {
 	struct op_data op_data;
-	int ret;
+	int ret, depth, i;
 
 	trace_pipe("Ppr");
 
@@ -467,12 +539,28 @@ int pipeline_prepare(struct pipeline *p, struct comp_dev *host)
 	op_data.op = COMP_OPS_PREPARE;
 
 	spin_lock(&p->lock);
-	if (host->direction == STREAM_DIRECTION_PLAYBACK)
+	if (host->direction == STREAM_DIRECTION_PLAYBACK) {
+
+		/* first of all prepare the pipeline */
 		ret = component_op_sink(&op_data, host);
-	else
+		if (ret < 0)
+			goto out;
+
+		/* then preload buffers - the buffers must be moved
+		 * downstream so that every component has full buffers for
+		 * trigger start */
+		depth = pipeline_depth(host, 0);
+		for (i = depth; i > 0; i--) {
+			ret = component_preload(host, 0, i);
+			if (ret < 0)
+				break;
+		}
+	} else {
 		ret = component_op_source(&op_data, host);
-	spin_unlock(&p->lock);
+	}
 
+out:
+	spin_unlock(&p->lock);
 	return ret;
 }
 
diff --git a/src/audio/volume.c b/src/audio/volume.c
index f347847..1eb6004 100644
--- a/src/audio/volume.c
+++ b/src/audio/volume.c
@@ -548,16 +548,21 @@ found:
 			*cd->hvol[i] = cd->volume[i];
 	}
 
-	/* copy periods from host */
-	if (source->params.direction == STREAM_DIRECTION_PLAYBACK) {
-		for (i = 0; i < PLAT_INT_PERIODS; i++)
-			volume_copy(dev);
-	}
-
+	dev->preload = PLAT_INT_PERIODS;
 	dev->state = COMP_STATE_PREPARE;
 	return 0;
 }
 
+static int volume_preload(struct comp_dev *dev)
+{
+	int i;
+
+	for (i = 0; i < dev->preload; i++)
+		volume_copy(dev);
+
+	return 0;
+}
+
 static int volume_reset(struct comp_dev *dev)
 {
 	dev->state = COMP_STATE_INIT;
@@ -575,6 +580,7 @@ struct comp_driver comp_volume = {
 		.copy		= volume_copy,
 		.prepare	= volume_prepare,
 		.reset		= volume_reset,
+		.preload	= volume_preload,
 	},
 };
 
diff --git a/src/include/reef/audio/component.h b/src/include/reef/audio/component.h
index 3abae3d..1e784f5 100644
--- a/src/include/reef/audio/component.h
+++ b/src/include/reef/audio/component.h
@@ -140,7 +140,12 @@ struct buffer_desc {
 	struct period_desc source_period;
 };
 
-/* audio component operations - all mandatory */
+/*
+ * Audio component operations - all mandatory.
+ *
+ * All component operations must return 0 for success, negative values for
+ * errors and 1 to stop the pipeline walk operation.
+ */
 struct comp_ops {
 	/* component creation and destruction */
 	struct comp_dev *(*new)(uint32_t type, uint32_t index,
@@ -150,6 +155,9 @@ struct comp_ops {
 	/* set component audio stream paramters */
 	int (*params)(struct comp_dev *dev, struct stream_params *params);
 
+	/* preload buffers */
+	int (*preload)(struct comp_dev *dev);
+
 	/* set component audio stream paramters */
 	int (*dai_config)(struct comp_dev *dev, struct dai_config *dai_config);
 
@@ -194,6 +202,7 @@ struct comp_dev {
 	uint32_t is_host;	/* component is graph host endpoint */
 	uint32_t is_mixer;	/* component is mixer */
 	uint32_t direction;	/* STREAM_DIRECTION_ */
+	uint16_t preload;       /* number of periods to preload during prepare() */
 	spinlock_t lock;	/* lock for this component */
 	void *private;		/* private data */
 	struct comp_driver *drv;
@@ -281,6 +290,12 @@ static inline int comp_prepare(struct comp_dev *dev)
 	return dev->drv->ops.prepare(dev);
 }
 
+/* component preload buffers -mandatory  */
+static inline int comp_preload(struct comp_dev *dev)
+{
+	return dev->drv->ops.preload(dev);
+}
+
 /* copy component buffers - mandatory */
 static inline int comp_copy(struct comp_dev *dev)
 {
-- 
2.7.4



More information about the Sound-open-firmware mailing list