[Sound-open-firmware] [PATCH] ipc: pipeline: Add pipeline position/timestamp readback to IPC

Liam Girdwood liam.r.girdwood at linux.intel.com
Tue Aug 15 17:39:26 CEST 2017


Allow the IPC to send and receive pipeline position and timestamp data.

Signed-off-by: Liam Girdwood <liam.r.girdwood at 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,
-- 
2.11.0



More information about the Sound-open-firmware mailing list