[Sound-open-firmware] [PATCH] dma: Add support for callback DMA reload.

Liam Girdwood liam.r.girdwood at linux.intel.com
Thu Dec 22 14:05:09 CET 2016


Add support to immediately reload the DMA during a DMA transfer completion
callback. This allows components to respond quickly to any physical
DMA copy updates i.e. period split over two non continuous physical host
pages.

Signed-off-by: Liam Girdwood <liam.r.girdwood at linux.intel.com>
---
 src/audio/dai.c                    |  2 +-
 src/drivers/dw-dma.c               | 53 ++++++++++++++++++++++++++++++++------
 src/include/reef/audio/component.h |  3 ++-
 src/include/reef/dma.h             |  5 ++--
 src/ipc/intel-ipc.c                |  2 +-
 5 files changed, 52 insertions(+), 13 deletions(-)

diff --git a/src/audio/dai.c b/src/audio/dai.c
index afa1be1..7bbde08 100644
--- a/src/audio/dai.c
+++ b/src/audio/dai.c
@@ -68,7 +68,7 @@ struct dai_data {
 };
 
 /* this is called by DMA driver every time descriptor has completed */
-static void dai_dma_cb(void *data, uint32_t type)
+static void dai_dma_cb(void *data, uint32_t type, struct dma_sg_elem *next)
 {
 	struct comp_dev *dev = (struct comp_dev *)data;
 	struct dai_data *dd = comp_get_drvdata(dev);
diff --git a/src/drivers/dw-dma.c b/src/drivers/dw-dma.c
index 8834ca3..ecd152d 100644
--- a/src/drivers/dw-dma.c
+++ b/src/drivers/dw-dma.c
@@ -184,7 +184,7 @@ struct dma_chan_data {
 
 	struct work work;
 
-	void (*cb)(void *data, uint32_t type);	/* client callback function */
+	void (*cb)(void *data, uint32_t type, struct dma_sg_elem *next);	/* client callback function */
 	void *cb_data;		/* client callback data */
 	int cb_type;		/* callback type */
 };
@@ -195,7 +195,9 @@ struct dma_pdata {
 	uint32_t class;		/* channel class - set for controller atm */
 };
 
-static inline void dw_dma_chan_reload(struct dma *dma, int channel);
+static inline void dw_dma_chan_reload_lli(struct dma *dma, int channel);
+static inline void dw_dma_chan_reload_next(struct dma *dma, int channel,
+		struct dma_sg_elem *next);
 
 static inline void dw_write(struct dma *dma, uint32_t reg, uint32_t value)
 {
@@ -385,6 +387,7 @@ out:
 static int dw_dma_release(struct dma *dma, int channel)
 {
 	struct dma_pdata *p = dma_get_drvdata(dma);
+	struct dma_sg_elem next;
 	uint32_t flags;
 
 	spin_lock_irq(&dma->lock, flags);
@@ -393,8 +396,8 @@ static int dw_dma_release(struct dma *dma, int channel)
 
 	if (p->chan[channel].status == DMA_STATUS_PAUSED) {
 		if (p->chan[channel].cb && p->chan[channel].cb_type & DMA_IRQ_TYPE_LLIST)
-			p->chan[channel].cb(p->chan[channel].cb_data, DMA_IRQ_TYPE_LLIST);
-		dw_dma_chan_reload(dma, channel);
+			p->chan[channel].cb(p->chan[channel].cb_data, DMA_IRQ_TYPE_LLIST, &next);
+		dw_dma_chan_reload_lli(dma, channel);
 	}
 
 	/* resume and reload DMA */
@@ -711,7 +714,8 @@ static int dw_dma_pm_context_store(struct dma *dma)
 }
 
 static void dw_dma_set_cb(struct dma *dma, int channel, int type,
-		void (*cb)(void *data, uint32_t type), void *data)
+		void (*cb)(void *data, uint32_t type, struct dma_sg_elem *next),
+		void *data)
 {
 	struct dma_pdata *p = dma_get_drvdata(dma);
 	uint32_t flags;
@@ -723,7 +727,8 @@ static void dw_dma_set_cb(struct dma *dma, int channel, int type,
 	spin_unlock_irq(&dma->lock, flags);
 }
 
-static inline void dw_dma_chan_reload(struct dma *dma, int channel)
+/* reload using LLI data */
+static inline void dw_dma_chan_reload_lli(struct dma *dma, int channel)
 {
 	struct dma_pdata *p = dma_get_drvdata(dma);
 	struct dw_lli2 *lli = p->chan[channel].lli_current;
@@ -754,11 +759,39 @@ static inline void dw_dma_chan_reload(struct dma *dma, int channel)
 	dw_write(dma, DW_DMA_CHAN_EN, CHAN_ENABLE(channel));
 }
 
+/* reload using callback data */
+static inline void dw_dma_chan_reload_next(struct dma *dma, int channel,
+		struct dma_sg_elem *next)
+{
+	struct dma_pdata *p = dma_get_drvdata(dma);
+	struct dw_lli2 *lli = p->chan[channel].lli_current;
+
+	/* channel needs started from scratch, so write SARn, DARn */
+	dw_write(dma, DW_SAR(channel), next->src);
+	dw_write(dma, DW_DAR(channel), next->dest);
+
+	/* set transfer size of element */
+	lli->ctrl_hi = DW_CTLH_CLASS(p->class) |
+		(next->size & DW_CTLH_BLOCK_TS_MASK);
+
+	/* program CTLn */
+	dw_write(dma, DW_CTRL_LOW(channel), lli->ctrl_lo);
+	dw_write(dma, DW_CTRL_HIGH(channel), lli->ctrl_hi);
+
+	/* program CFGn */
+	dw_write(dma, DW_CFG_LOW(channel), p->chan[channel].cfg_lo);
+	dw_write(dma, DW_CFG_HIGH(channel), p->chan[channel].cfg_hi);
+
+	/* enable the channel */
+	dw_write(dma, DW_DMA_CHAN_EN, CHAN_ENABLE(channel));
+}
+
 /* this will probably be called at the end of every period copied */
 static void dw_dma_irq_handler(void *data)
 {
 	struct dma *dma = (struct dma *)data;
 	struct dma_pdata *p = dma_get_drvdata(dma);
+	struct dma_sg_elem next;
 	uint32_t status_tfr = 0, status_block = 0, status_err = 0, status_intr;
 	uint32_t mask, pmask;
 	int i;
@@ -806,12 +839,16 @@ static void dw_dma_irq_handler(void *data)
 				continue;
 			}
 
+			next.size = 0;
 			if (p->chan[i].cb)
 				p->chan[i].cb(p->chan[i].cb_data,
-					DMA_IRQ_TYPE_LLIST);
+					DMA_IRQ_TYPE_LLIST, &next);
 
 			/* check for reload channel */
-			dw_dma_chan_reload(dma, i);
+			if (next.size > 0)
+				dw_dma_chan_reload_next(dma, i, &next);
+			else
+				dw_dma_chan_reload_lli(dma, i);
 		}
 #if DW_USE_HW_LLI
 		/* end of a LLI block */
diff --git a/src/include/reef/audio/component.h b/src/include/reef/audio/component.h
index 61afa5a..850165b 100644
--- a/src/include/reef/audio/component.h
+++ b/src/include/reef/audio/component.h
@@ -161,7 +161,8 @@ struct comp_ops {
 	int (*copy)(struct comp_dev *dev);
 
 	/* host buffer config */
-	int (*host_buffer)(struct comp_dev *dev, struct dma_sg_elem *elem);
+	int (*host_buffer)(struct comp_dev *dev, struct dma_sg_elem *elem,
+			uint32_t host_size);
 };
 
 
diff --git a/src/include/reef/dma.h b/src/include/reef/dma.h
index b9e8a45..6de2ede 100644
--- a/src/include/reef/dma.h
+++ b/src/include/reef/dma.h
@@ -104,7 +104,8 @@ struct dma_ops {
 		struct dma_sg_config *config);
 
 	void (*set_cb)(struct dma *dma, int channel, int type,
-		void (*cb)(void *data, uint32_t type), void *data);
+		void (*cb)(void *data, uint32_t type, struct dma_sg_elem *next),
+		void *data);
 
 	int (*pm_context_restore)(struct dma *dma);
 	int (*pm_context_store)(struct dma *dma);
@@ -162,7 +163,7 @@ static inline void dma_channel_put(struct dma *dma, int channel)
 }
 
 static inline void dma_set_cb(struct dma *dma, int channel, int type,
-	void (*cb)(void *data, uint32_t type), void *data)
+	void (*cb)(void *data, uint32_t type, struct dma_sg_elem *next), void *data)
 {
 	dma->ops->set_cb(dma, channel, type, cb, data);
 }
diff --git a/src/ipc/intel-ipc.c b/src/ipc/intel-ipc.c
index 7aa24f3..d735f26 100644
--- a/src/ipc/intel-ipc.c
+++ b/src/ipc/intel-ipc.c
@@ -143,7 +143,7 @@ static uint32_t ipc_fw_caps(uint32_t header)
 	return IPC_INTEL_GLB_REPLY_SUCCESS;
 }
 
-static void dma_complete(void *data, uint32_t type)
+static void dma_complete(void *data, uint32_t type, struct dma_sg_elem *next)
 {
 	struct intel_ipc_data *iipc = (struct intel_ipc_data *)data;
 
-- 
2.9.3



More information about the Sound-open-firmware mailing list