Allow the IPC to send and receive pipeline position and timestamp data.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/audio/dai.c | 20 ++++++++++++++++++ src/audio/host.c | 22 +++++++++++++++++++- src/include/reef/ipc.h | 2 +- src/ipc/intel-ipc.c | 56 ++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 87 insertions(+), 13 deletions(-)
diff --git a/src/audio/dai.c b/src/audio/dai.c index f7868cd..4464398 100644 --- a/src/audio/dai.c +++ b/src/audio/dai.c @@ -68,6 +68,7 @@ struct dai_data { uint32_t dai_pos_blks; /* position in bytes (nearest block) */
volatile uint64_t *dai_pos; /* host can read back this value without IPC */ + uint64_t wallclock; /* wall clock at stream start */ };
static int dai_cmd(struct comp_dev *dev, int cmd, void *data); @@ -485,6 +486,9 @@ static int dai_cmd(struct comp_dev *dev, int cmd, void *data) if (dev->state == COMP_STATE_PAUSED) { dai_trigger(dd->dai, cmd, dev->params.direction); dma_release(dd->dma, dd->chan); + + /* update starting wallclock */ + platform_dai_wallclock(dev, &dd->wallclock); dev->state = COMP_STATE_RUNNING; } break; @@ -495,6 +499,9 @@ static int dai_cmd(struct comp_dev *dev, int cmd, void *data) if (ret < 0) return ret; dai_trigger(dd->dai, cmd, dev->params.direction); + + /* update starting wallclock */ + platform_dai_wallclock(dev, &dd->wallclock); dev->state = COMP_STATE_RUNNING; } break; @@ -525,6 +532,18 @@ static int dai_preload(struct comp_dev *dev) return 0; }
+static int dai_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn) +{ + struct dai_data *dd = comp_get_drvdata(dev); + + /* TODO: improve accuracy by adding current DMA position */ + posn->dai_posn = dev->position; + + /* set stream start wallclock */ + posn->wallclock = dd->wallclock; + return 0; +} + static int dai_config(struct comp_dev *dev, struct dai_config *dai_config) { struct dai_data *dd = comp_get_drvdata(dev); @@ -562,6 +581,7 @@ static struct comp_driver comp_dai = { .reset = dai_reset, .dai_config = dai_config, .preload = dai_preload, + .position = dai_position, }, };
diff --git a/src/audio/host.c b/src/audio/host.c index 5cdc4da..27b5418 100644 --- a/src/audio/host.c +++ b/src/audio/host.c @@ -152,14 +152,19 @@ static void host_dma_cb(void *data, uint32_t type, struct dma_sg_elem *next) /* send IPC message to driver if needed */ hd->report_pos += local_elem->size; hd->posn.host_posn += local_elem->size; + + /* NO_IRQ mode if host_period_size == 0 */ if (dev->params.host_period_bytes != 0 && hd->report_pos >= dev->params.host_period_bytes) { hd->report_pos = 0; /* update for host side */ if (hd->host_pos) { *hd->host_pos = hd->local_pos; - ipc_stream_send_notification(dev, &hd->posn); } + + /* send timestamps to host */ + pipeline_get_timestamp(dev->pipeline, dev, &hd->posn); + ipc_stream_send_position(dev, &hd->posn); }
/* update src and dest positions and check for overflow */ @@ -271,6 +276,9 @@ static struct comp_dev *host_new(struct sof_ipc_comp *comp) /* set up callback */ dma_set_cb(hd->dma, hd->chan, DMA_IRQ_TYPE_LLIST, host_dma_cb, dev);
+ /* init posn data. TODO: other fields */ + hd->posn.comp_id = comp->id; + return dev;
error: @@ -523,6 +531,17 @@ static int host_stop(struct comp_dev *dev) return 0; }
+static int host_position(struct comp_dev *dev, + struct sof_ipc_stream_posn *posn) +{ + struct host_data *hd = comp_get_drvdata(dev); + + /* TODO: improve accuracy by adding current DMA position */ + posn->host_posn = hd->local_pos; + + return 0; +} + /* used to pass standard and bespoke commands (with data) to component */ static int host_cmd(struct comp_dev *dev, int cmd, void *data) { @@ -641,6 +660,7 @@ struct comp_driver comp_host = { .prepare = host_prepare, .preload = host_preload, .host_buffer = host_buffer, + .position = host_position, }, };
diff --git a/src/include/reef/ipc.h b/src/include/reef/ipc.h index 92a6f6e..96f2924 100644 --- a/src/include/reef/ipc.h +++ b/src/include/reef/ipc.h @@ -114,7 +114,7 @@ void ipc_free(struct ipc *ipc);
int ipc_process_msg_queue(void);
-int ipc_stream_send_notification(struct comp_dev *cdev, +int ipc_stream_send_position(struct comp_dev *cdev, struct sof_ipc_stream_posn *posn); int ipc_queue_host_message(struct ipc *ipc, uint32_t header, void *tx_data, size_t tx_bytes, void *rx_data, diff --git a/src/ipc/intel-ipc.c b/src/ipc/intel-ipc.c index d122b30..dbb0db1 100644 --- a/src/ipc/intel-ipc.c +++ b/src/ipc/intel-ipc.c @@ -274,9 +274,52 @@ static int ipc_stream_pcm_free(uint32_t header)
/* reset the pipeline */ pipeline_reset(pcm_dev->cd->pipeline, pcm_dev->cd); + +/* get stream position */ +static int ipc_stream_position(uint32_t header) +{ + struct sof_ipc_stream *stream = _ipc->comp_data; + struct sof_ipc_stream_posn posn; + struct ipc_comp_dev *pcm_dev; + + trace_ipc("pos"); + + memset(&posn, 0, sizeof(posn)); + + /* get the pcm_dev */ + pcm_dev = ipc_get_comp(_ipc, stream->comp_id); + if (pcm_dev == NULL) { + trace_ipc_error("epo"); + return -ENODEV; + } + + /* set message fields - TODO; get others */ + posn.rhdr.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_POSITION; + posn.rhdr.hdr.size = sizeof(posn); + posn.comp_id = stream->comp_id; + + /* get the stream positions and timestamps */ + pipeline_get_timestamp(pcm_dev->cd->pipeline, pcm_dev->cd, &posn); + + /* copy positions to outbox */ + mailbox_outbox_write(0, &posn, sizeof(posn)); return 0; }
+/* send stream position */ +int ipc_stream_send_position(struct comp_dev *cdev, + struct sof_ipc_stream_posn *posn) +{ + uint32_t header; + + header = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_POSITION; + posn->rhdr.hdr.cmd = header; + posn->rhdr.hdr.size = sizeof(*posn); + + return ipc_queue_host_message(_ipc, header, posn, sizeof(*posn), + NULL, 0, NULL, NULL); +} + static int ipc_stream_trigger(uint32_t header) { struct ipc_comp_dev *pcm_dev; @@ -343,6 +386,8 @@ static int ipc_glb_stream_message(uint32_t header) case iCS(SOF_IPC_STREAM_TRIG_DRAIN): case iCS(SOF_IPC_STREAM_TRIG_XRUN): return ipc_stream_trigger(header); + case iCS(SOF_IPC_STREAM_POSITION): + return ipc_stream_position(header); default: return -EINVAL; } @@ -712,17 +757,6 @@ static inline struct ipc_msg *msg_get_empty(struct ipc *ipc) return msg; }
-/* Send stream command */ -int ipc_stream_send_notification(struct comp_dev *cdev, - struct sof_ipc_stream_posn *posn) -{ - uint32_t header; - - header = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_POSITION; -trace_value(header); - return ipc_queue_host_message(_ipc, header, posn, sizeof(*posn), - NULL, 0, NULL, NULL); -}
int ipc_queue_host_message(struct ipc *ipc, uint32_t header, void *tx_data, size_t tx_bytes, void *rx_data,