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(a)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