Allow pipeline host and DAI components to be timestamped in a platform specific manner so that host DMA & DAI DMA position alongside any local DSP timestamp can be returned to the host drivers.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/audio/pipeline.c | 103 ++++++++++++++++++++++++- src/include/reef/audio/component.h | 13 ++++ src/include/reef/audio/pipeline.h | 5 ++ src/include/uapi/ipc.h | 29 ++++++- src/platform/baytrail/include/platform/timer.h | 14 ++++ src/platform/baytrail/timer.c | 36 +++++++++ 6 files changed, 196 insertions(+), 4 deletions(-)
diff --git a/src/audio/pipeline.c b/src/audio/pipeline.c index 185b4f3..817ff08 100644 --- a/src/audio/pipeline.c +++ b/src/audio/pipeline.c @@ -679,7 +679,7 @@ static int pipeline_copy_to_downstream(struct comp_dev *start, return 0; }
- /* travel downstream to source end point(s) */ + /* travel downstream to sink end point(s) */ list_for_item(clist, ¤t->bsink_list) { struct comp_buffer *buffer;
@@ -702,6 +702,107 @@ static int pipeline_copy_to_downstream(struct comp_dev *start, return err; }
+static int timestamp_downstream(struct comp_dev *start, + struct comp_dev *current, struct sof_ipc_stream_posn *posn) +{ + struct list_item *clist; + int res = 0; + + /* is component a DAI endpoint ? */ + if (current != start) { + + /* go downstream if we are not endpoint */ + if (!current->is_endpoint) + goto downstream; + + if (current->comp.id == SOF_COMP_DAI || + current->comp.id == SOF_COMP_SG_DAI) { + platform_dai_timestamp(current, posn); + return 1; + } + } + + +downstream: + /* travel downstream to sink end point(s) */ + list_for_item(clist, ¤t->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 || buffer->sink->state != COMP_STATE_RUNNING) + continue; + + /* continue downstream */ + res = timestamp_downstream(start, buffer->sink, posn); + if (res == 1) + break; + } + + /* return back upstream */ + return res; +} + + +static int timestamp_upstream(struct comp_dev *start, + struct comp_dev *current, struct sof_ipc_stream_posn *posn) +{ + struct list_item *clist; + int res = 0; + + /* is component a DAI endpoint ? */ + if (current != start) { + + /* go downstream if we are not endpoint */ + if (!current->is_endpoint) + goto upstream; + + if (current->comp.id == SOF_COMP_DAI || + current->comp.id == SOF_COMP_SG_DAI) { + platform_dai_timestamp(current, posn); + return 1; + } + } + + +upstream: + /* travel upstream to source end point(s) */ + list_for_item(clist, ¤t->bsource_list) { + struct comp_buffer *buffer; + + buffer = container_of(clist, struct comp_buffer, sink_list); + + /* dont go downstream if this component is not connected */ + if (!buffer->connected || buffer->source->state != COMP_STATE_RUNNING) + continue; + + /* continue downstream */ + res = timestamp_upstream(start, buffer->sink, posn); + if (res == 1) + break; + } + + /* return back upstream */ + return res; +} + +/* + * Get the timestamps for host and first active DAI found. + */ +void pipeline_get_timestamp(struct pipeline *p, struct comp_dev *host, + struct sof_ipc_stream_posn *posn) +{ + platform_host_timestamp(host, posn); + + if (host->params.direction == SOF_IPC_STREAM_PLAYBACK) { + timestamp_downstream(host, host, posn); + } else { + timestamp_upstream(host, host, posn); + } +} + + /* notify pipeline that this component requires buffers emptied/filled */ void pipeline_schedule_copy(struct pipeline *p, struct comp_dev *dev) { diff --git a/src/include/reef/audio/component.h b/src/include/reef/audio/component.h index 3238e13..a74395c 100644 --- a/src/include/reef/audio/component.h +++ b/src/include/reef/audio/component.h @@ -139,6 +139,10 @@ struct comp_ops { /* host buffer config */ int (*host_buffer)(struct comp_dev *dev, struct dma_sg_elem *elem, uint32_t host_size); + + /* position */ + int (*position)(struct comp_dev *dev, + struct sof_ipc_stream_posn *posn); };
@@ -264,6 +268,15 @@ static inline int comp_dai_config(struct comp_dev *dev, return 0; }
+/* component rendering position */ +static inline int comp_position(struct comp_dev *dev, + struct sof_ipc_stream_posn *posn) +{ + if (dev->drv->ops.position) + return dev->drv->ops.position(dev, posn); + return 0; +} + /* default base component initialisations */ void sys_comp_dai_init(void); void sys_comp_host_init(void); diff --git a/src/include/reef/audio/pipeline.h b/src/include/reef/audio/pipeline.h index 8e029d8..ca1947a 100644 --- a/src/include/reef/audio/pipeline.h +++ b/src/include/reef/audio/pipeline.h @@ -108,8 +108,13 @@ int init_static_pipeline(struct ipc *ipc); /* pipeline creation */ int init_pipeline(void);
+/* schedule a copy operation for this pipeline */ void pipeline_schedule_copy(struct pipeline *p, struct comp_dev *dev);
+/* get time pipeline timestamps from host to dai */ +void pipeline_get_timestamp(struct pipeline *p, struct comp_dev *host_dev, + struct sof_ipc_stream_posn *posn); + void pipeline_schedule(void *arg);
#endif diff --git a/src/include/uapi/ipc.h b/src/include/uapi/ipc.h index 38397dd..19ee898 100644 --- a/src/include/uapi/ipc.h +++ b/src/include/uapi/ipc.h @@ -360,12 +360,35 @@ struct sof_ipc_stream { uint32_t comp_id; } __attribute__((packed));
+ +/* flags indicating which time stamps are in sync with each other */ +#define SOF_TIME_HOST_SYNC (1 << 0) +#define SOF_TIME_DAI_SYNC (1 << 1) +#define SOF_TIME_WALL_SYNC (1 << 2) +#define SOF_TIME_STAMP_SYNC (1 << 3) + +/* flags indicating which time stamps are valid */ +#define SOF_TIME_HOST_VALID (1 << 8) +#define SOF_TIME_DAI_VALID (1 << 9) +#define SOF_TIME_WALL_VALID (1 << 10) +#define SOF_TIME_STAMP_VALID (1 << 11) + +/* flags indicating time stamps are 64bit else 3use low 32bit */ +#define SOF_TIME_HOST_64 (1 << 16) +#define SOF_TIME_DAI_64 (1 << 17) +#define SOF_TIME_WALL_64 (1 << 18) +#define SOF_TIME_STAMP_64 (1 << 19) + struct sof_ipc_stream_posn { struct sof_ipc_reply rhdr; uint32_t comp_id; - uint32_t host_posn; /* in frames */ - uint32_t dai_posn; /* in frames */ - uint64_t timestamp; + uint32_t flags; /* SOF_TIME_ */ + uint32_t wallclock_hz; /* frequency of wallclock in Hz */ + uint32_t timestamp_ns; /* resolution of timestamp in ns */ + uint64_t host_posn; /* host DMA position in bytes */ + uint64_t dai_posn; /* DAI DMA position in bytes */ + uint64_t wallclock; /* audio wall clock */ + uint64_t timestamp; /* system time stamp */ } __attribute__((packed));
/* diff --git a/src/platform/baytrail/include/platform/timer.h b/src/platform/baytrail/include/platform/timer.h index 0534958..309828c 100644 --- a/src/platform/baytrail/include/platform/timer.h +++ b/src/platform/baytrail/include/platform/timer.h @@ -46,10 +46,24 @@
#define TIMER_AUDIO TIMER3
+struct comp_dev; +struct sof_ipc_stream_posn; + void platform_timer_set(struct timer *timer, uint32_t ticks); void platform_timer_clear(struct timer *timer); uint32_t platform_timer_get(struct timer *timer); void platform_timer_start(struct timer *timer); void platform_timer_stop(struct timer *timer);
+/* get timestamp for host stream DMA position */ +void platform_host_timestamp(struct comp_dev *host, + struct sof_ipc_stream_posn *posn); + +/* get timestamp for DAI stream DMA position */ +void platform_dai_timestamp(struct comp_dev *dai, + struct sof_ipc_stream_posn *posn); + +/* get current wallclock for componnent */ +void platform_dai_wallclock(struct comp_dev *dai, uint64_t *wallclock); + #endif diff --git a/src/platform/baytrail/timer.c b/src/platform/baytrail/timer.c index 4bcabc9..3d08f6f 100644 --- a/src/platform/baytrail/timer.c +++ b/src/platform/baytrail/timer.c @@ -33,6 +33,7 @@ #include <platform/timer.h> #include <platform/shim.h> #include <reef/debug.h> +#include <reef/audio/component.h> #include <stdint.h>
void platform_timer_start(struct timer *timer) @@ -71,3 +72,38 @@ uint32_t platform_timer_get(struct timer *timer) { return shim_read(SHIM_EXT_TIMER_STAT); } + +/* get timestamp for host stream DMA position */ +void platform_host_timestamp(struct comp_dev *host, + struct sof_ipc_stream_posn *posn) +{ + int err; + + /* get host postion */ + err = comp_position(host, posn); + if (err == 0) + posn->flags |= SOF_TIME_HOST_VALID; +} + +/* get timestamp for DAI stream DMA position */ +void platform_dai_timestamp(struct comp_dev *dai, + struct sof_ipc_stream_posn *posn) +{ + int err; + + /* get DAI postion */ + err = comp_position(dai, posn); + if (err == 0) + posn->flags |= SOF_TIME_DAI_VALID; + + /* get SSP wallclock - DAI sets this to stream start value */ + posn->wallclock = shim_read(SHIM_EXT_TIMER_STAT) - posn->wallclock; + posn->flags |= SOF_TIME_WALL_VALID; +} + +/* get current wallclock for componnent */ +void platform_dai_wallclock(struct comp_dev *dai, uint64_t *wallclock) +{ + /* only 1 wallclock on BYT */ + *wallclock = shim_read(SHIM_EXT_TIMER_STAT); +}