[Sound-open-firmware] [PATCH 7/8] audio: host: Add host GW DMA support to host component.

Keyon Jie yang.jie at linux.intel.com
Thu Feb 8 13:48:12 CET 2018


From: Liam Girdwood <liam.r.girdwood at linux.intel.com>

Host GW DMA has no callback so host component has to manually notify
DMA driver when to copy each period.

Signed-off-by: Liam Girdwood <liam.r.girdwood at linux.intel.com>
Signed-off-by: Keyon Jie <yang.jie at linux.intel.com>
---
 src/audio/host.c | 201 ++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 176 insertions(+), 25 deletions(-)

diff --git a/src/audio/host.c b/src/audio/host.c
index 0d0a7bb..17218fe 100644
--- a/src/audio/host.c
+++ b/src/audio/host.c
@@ -84,6 +84,9 @@ struct host_data {
 	uint32_t period_bytes;
 	uint32_t period_count;
 
+#if defined CONFIG_DMA_GW
+	uint32_t first_copy;
+#endif
 	/* stream info */
 	struct sof_ipc_stream_posn posn; /* TODO: update this */
 };
@@ -101,6 +104,8 @@ static inline struct dma_sg_elem *next_buffer(struct hc_buf *hc)
 	return elem;
 }
 
+#if !defined CONFIG_DMA_GW
+
 /*
  * Host period copy between DSP and host DMA completion.
  * This is called  by DMA driver every time when DMA completes its current
@@ -226,6 +231,87 @@ static void host_dma_cb(void *data, uint32_t type, struct dma_sg_elem *next)
 	wait_completed(&hd->complete);
 }
 
+#else
+
+static void host_gw_dma_update(struct comp_dev *dev)
+{
+	struct host_data *hd = comp_get_drvdata(dev);
+	struct dma_sg_elem *local_elem;
+	struct dma_sg_elem *source_elem;
+	struct dma_sg_elem *sink_elem;
+	struct comp_buffer *dma_buffer;
+
+	local_elem = list_first_item(&hd->config.elem_list,
+				     struct dma_sg_elem, list);
+
+	tracev_host("upd");
+
+	/* update buffer positions */
+	dma_buffer = hd->dma_buffer;
+
+	if (dev->params.direction == SOF_IPC_STREAM_PLAYBACK) {
+		/* invalidate audio data */
+		dcache_invalidate_region(dma_buffer->w_ptr, local_elem->size);
+
+		/* recalc available buffer space */
+		comp_update_buffer_produce(hd->dma_buffer, local_elem->size);
+	} else {
+		/* recalc available buffer space */
+		comp_update_buffer_consume(hd->dma_buffer, local_elem->size);
+
+		/* writeback audio data */
+		dcache_writeback_region(dma_buffer->r_ptr, local_elem->size);
+	}
+
+	dev->position += local_elem->size;
+
+	/* new local period, update host buffer position blks */
+	hd->local_pos += local_elem->size;
+
+	/* buffer overlap, hard code host buffer size at the moment ? */
+	if (hd->local_pos >= hd->host_size)
+		hd->local_pos = 0;
+
+	/* send IPC message to driver if needed */
+	hd->report_pos += local_elem->size;
+	/* update for host side */
+	if (hd->host_pos)
+		*hd->host_pos = hd->local_pos;
+
+	/* 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;
+
+		/* send timestamps to host */
+		pipeline_get_timestamp(dev->pipeline, dev, &hd->posn);
+		ipc_stream_send_position(dev, &hd->posn);
+	}
+
+	/* update src/dest positions for local buf, and check for overflow */
+	if (dev->params.direction == SOF_IPC_STREAM_PLAYBACK) {
+		local_elem->dest += local_elem->size;
+		if (local_elem->dest == hd->sink->current_end) {
+			/* end of elem, so use next */
+			sink_elem = next_buffer(hd->sink);
+			hd->sink->current_end = sink_elem->dest +
+				sink_elem->size;
+			local_elem->dest = sink_elem->dest;
+		}
+	} else {
+		local_elem->src += local_elem->size;
+		if (local_elem->src == hd->source->current_end) {
+			/* end of elem, so use next */
+			source_elem = next_buffer(hd->source);
+			hd->source->current_end = source_elem->src +
+				source_elem->size;
+			local_elem->src = source_elem->src;
+		}
+	}
+}
+
+#endif
+
 static struct comp_dev *host_new(struct sof_ipc_comp *comp)
 {
 	struct comp_dev *dev;
@@ -259,7 +345,14 @@ static struct comp_dev *host_new(struct sof_ipc_comp *comp)
 
 	comp_set_drvdata(dev, hd);
 
+#if !defined CONFIG_DMA_GW
 	hd->dma = dma_get(ipc_host->dmac_id);
+#else
+	if (ipc_host->direction == SOF_IPC_STREAM_PLAYBACK)
+		hd->dma = dma_get(DMA_HOST_OUT_DMAC);
+	else
+		hd->dma = dma_get(DMA_HOST_IN_DMAC);
+#endif
 	if (hd->dma == NULL) {
 		trace_host_error("eDM");
 		goto error;
@@ -271,6 +364,7 @@ static struct comp_dev *host_new(struct sof_ipc_comp *comp)
 	list_init(&hd->local.elem_list);
 	list_item_prepend(&elem->list, &hd->config.elem_list);
 
+#if !defined CONFIG_DMA_GW
 	/* get DMA channel from DMAC */
 	hd->chan = dma_channel_get(hd->dma, 0);
 	if (hd->chan < 0) {
@@ -280,6 +374,7 @@ 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);
+#endif
 
 	/* init posn data. TODO: other fields */
 	hd->posn.comp_id = comp->id;
@@ -302,7 +397,10 @@ static void host_free(struct comp_dev *dev)
 
 	elem = list_first_item(&hd->config.elem_list,
 		struct dma_sg_elem, list);
+
+#if !defined CONFIG_DMA_GW
 	dma_channel_put(hd->dma, hd->chan);
+#endif
 
 	rfree(elem);
 	rfree(hd);
@@ -334,6 +432,16 @@ static int create_local_elems(struct comp_dev *dev)
 		e->size = hd->period_bytes;
 
 		list_item_append(&e->list, &hd->local.elem_list);
+#if defined CONFIG_DMA_GW
+		/*
+		 * for dma gateway, we don't allocate extra sg elements, so,
+		 * just reuse local elements for config.elem_list.
+		 * And, as the first element has been added at host_new, so
+		 * add from the 2nd element here
+		 */
+		if (i >= 1)
+			list_item_append(&e->list, &hd->config.elem_list);
+#endif
 	}
 
 	return 0;
@@ -448,14 +556,24 @@ static int host_params(struct comp_dev *dev)
 	if (err < 0)
 		return err;
 
-	/* set up DMA configuration - copy in words for all formats as
-	   this is most optimal for memory <-> memory copies. */
-	config->src_width = sizeof(uint32_t);
-	config->dest_width = sizeof(uint32_t);
+	/* set up DMA configuration - copy in sample bytes. */
+	config->src_width = comp_sample_bytes(dev);
+	config->dest_width = comp_sample_bytes(dev);
 	config->cyclic = 0;
 
 	host_elements_reset(dev);
 
+#if defined CONFIG_DMA_GW
+	dev->params.stream_tag -= 1;
+	/* get DMA channel from DMAC */
+	hd->chan = dma_channel_get(hd->dma, dev->params.stream_tag);
+	if (hd->chan < 0) {
+		trace_host_error("eDC");
+		return -ENODEV;
+	}
+	dma_set_config(hd->dma, hd->chan, &hd->config);
+#endif
+
 	return 0;
 }
 
@@ -477,6 +595,10 @@ static int host_prepare(struct comp_dev *dev)
 	hd->split_remaining = 0;
 	dev->position = 0;
 
+#if defined CONFIG_DMA_GW
+	hd->first_copy = 1;
+#endif
+
 	return 0;
 }
 
@@ -522,7 +644,9 @@ static int host_position(struct comp_dev *dev,
 static int host_cmd(struct comp_dev *dev, int cmd, void *data)
 {
 	int ret = 0;
-
+#if defined CONFIG_DMA_GW
+	struct host_data *hd = comp_get_drvdata(dev);
+#endif
 	trace_host("cmd");
 
 	ret = comp_set_state(dev, cmd);
@@ -534,11 +658,23 @@ static int host_cmd(struct comp_dev *dev, int cmd, void *data)
 		ret = host_stop(dev);
 		break;
 	case COMP_CMD_START:
+#if defined CONFIG_DMA_GW
+		dma_start(hd->dma, hd->chan);
+#endif
 		/* preload first playback period for preloader task */
 		if (dev->params.direction == SOF_IPC_STREAM_PLAYBACK) {
+#if !defined CONFIG_DMA_GW
 			ret = host_copy(dev);
 			if (ret == dev->frames)
 				ret = 0;
+#else
+			/*
+			 * host dma will not start copy at this point yet,
+			 * just produce empty period bytes for it.
+			 */
+			comp_update_buffer_produce(hd->dma_buffer,
+						   hd->period_bytes);
+#endif
 		}
 		break;
 	default:
@@ -590,6 +726,11 @@ static int host_reset(struct comp_dev *dev)
 		rfree(e);
 	}
 
+#if defined CONFIG_DMA_GW
+	dma_stop(hd->dma, hd->chan);
+	dma_channel_put(hd->dma, hd->chan);
+#endif
+
 	host_pointer_reset(dev);
 	hd->host_pos = NULL;
 	hd->source = NULL;
@@ -603,46 +744,56 @@ static int host_reset(struct comp_dev *dev)
 static int host_copy(struct comp_dev *dev)
 {
 	struct host_data *hd = comp_get_drvdata(dev);
-	struct comp_buffer *dma_buffer;
 	struct dma_sg_elem *local_elem;
 
-	tracev_host("cpy");
+	trace_host("cpy");
 
 	if (dev->state != COMP_STATE_ACTIVE)
 		return 0;
 
+#if defined CONFIG_DMA_GW
+	if (hd->first_copy) {
+		/*
+		 * host dma will not start copy at this point yet, just produce
+		 * empty period bytes for it.
+		 */
+		comp_update_buffer_produce(hd->dma_buffer,
+					   hd->period_bytes);
+		hd->first_copy = 0;
+		return 0;
+	}
+#endif
 	local_elem = list_first_item(&hd->config.elem_list,
 		struct dma_sg_elem, list);
 
 	/* enough free or avail to copy ? */
 	if (dev->params.direction == SOF_IPC_STREAM_PLAYBACK) {
-
-		dma_buffer = list_first_item(&dev->bsink_list,
-			struct comp_buffer, source_list);
-
-		if (dma_buffer->free < local_elem->size) {
-			/* make sure there is free bytes for next period */
-			trace_host_error("xro");
-			comp_overrun(dev, dma_buffer, local_elem->size, 0);
-			return -EIO;
+		if (hd->dma_buffer->free < local_elem->size) {
+			/* buffer is enough avail, just return. */
+			trace_host("Bea");
+			return 0;
 		}
 	} else {
 
-		dma_buffer = list_first_item(&dev->bsource_list,
-			struct comp_buffer, sink_list);
-
-		if (dma_buffer->avail < local_elem->size) {
-			/* make sure there is avail bytes for next period */
-			trace_host_error("xru");
-			comp_underrun(dev, dma_buffer, local_elem->size, 0);
-			return -EIO;
+		if (hd->dma_buffer->avail < local_elem->size) {
+			/* buffer is enough empty, just return. */
+			trace_host("Bee");
+			return 0;
 		}
 	}
 
+#if defined CONFIG_DMA_GW
+
+	/* update host pointers from last period */
+	host_gw_dma_update(dev);
+
+	/* tell gateway to copy another period */
+	dma_copy(hd->dma, hd->chan, hd->period_bytes);
+#else
 	/* do DMA transfer */
 	dma_set_config(hd->dma, hd->chan, &hd->config);
 	dma_start(hd->dma, hd->chan);
-
+#endif
 	return dev->frames;
 }
 
-- 
2.11.0



More information about the Sound-open-firmware mailing list