From: Liam Girdwood liam.r.girdwood@linux.intel.com
Preload entire pipeline and all components by walking the pipeline graph.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com Modified-by: Keyon Jie yang.jie@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) {