[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