summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c43
-rw-r--r--drivers/dma/dw-axi-dmac/dw-axi-dmac.h2
2 files changed, 38 insertions, 7 deletions
diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
index a8b6c8c8ef58..830d3de76abd 100644
--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
@@ -265,14 +265,36 @@ dma_chan_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
struct dma_tx_state *txstate)
{
struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
- enum dma_status ret;
+ struct virt_dma_desc *vdesc;
+ enum dma_status status;
+ u32 completed_length;
+ unsigned long flags;
+ u32 completed_blocks;
+ size_t bytes = 0;
+ u32 length;
+ u32 len;
- ret = dma_cookie_status(dchan, cookie, txstate);
+ status = dma_cookie_status(dchan, cookie, txstate);
+ if (status == DMA_COMPLETE || !txstate)
+ return status;
- if (chan->is_paused && ret == DMA_IN_PROGRESS)
- ret = DMA_PAUSED;
+ spin_lock_irqsave(&chan->vc.lock, flags);
- return ret;
+ vdesc = vchan_find_desc(&chan->vc, cookie);
+ if (vdesc) {
+ length = vd_to_axi_desc(vdesc)->length;
+ completed_blocks = vd_to_axi_desc(vdesc)->completed_blocks;
+ len = vd_to_axi_desc(vdesc)->hw_desc[0].len;
+ completed_length = completed_blocks * len;
+ bytes = length - completed_length;
+ } else {
+ bytes = vd_to_axi_desc(vdesc)->length;
+ }
+
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
+ dma_set_residue(txstate, bytes);
+
+ return status;
}
static void write_desc_llp(struct axi_dma_hw_desc *desc, dma_addr_t adr)
@@ -549,6 +571,7 @@ static int dw_axi_dma_set_hw_desc(struct axi_dma_chan *chan,
set_desc_src_master(hw_desc);
+ hw_desc->len = len;
return 0;
}
@@ -575,6 +598,7 @@ dw_axi_dma_chan_prep_cyclic(struct dma_chan *dchan, dma_addr_t dma_addr,
chan->direction = direction;
desc->chan = chan;
chan->cyclic = true;
+ desc->length = 0;
for (i = 0; i < num_periods; i++) {
hw_desc = &desc->hw_desc[i];
@@ -584,6 +608,7 @@ dw_axi_dma_chan_prep_cyclic(struct dma_chan *dchan, dma_addr_t dma_addr,
if (status < 0)
goto err_desc_get;
+ desc->length += hw_desc->len;
/* Set end-of-link to the linked descriptor, so that cyclic
* callback function can be triggered during interrupt.
*/
@@ -636,6 +661,7 @@ dw_axi_dma_chan_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
goto err_desc_get;
desc->chan = chan;
+ desc->length = 0;
for_each_sg(sgl, sg, sg_len, i) {
mem = sg_dma_address(sg);
@@ -645,6 +671,7 @@ dw_axi_dma_chan_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
status = dw_axi_dma_set_hw_desc(chan, hw_desc, mem, len);
if (status < 0)
goto err_desc_get;
+ desc->length += hw_desc->len;
}
/* Set end-of-link to the last link descriptor of list */
@@ -690,6 +717,7 @@ dma_chan_prep_dma_memcpy(struct dma_chan *dchan, dma_addr_t dst_adr,
desc->chan = chan;
num = 0;
+ desc->length = 0;
while (len) {
xfer_len = len;
@@ -742,7 +770,8 @@ dma_chan_prep_dma_memcpy(struct dma_chan *dchan, dma_addr_t dst_adr,
set_desc_src_master(hw_desc);
set_desc_dest_master(hw_desc, desc);
-
+ hw_desc->len = xfer_len;
+ desc->length += hw_desc->len;
/* update the length and addresses for the next loop cycle */
len -= xfer_len;
dst_adr += xfer_len;
@@ -1210,7 +1239,7 @@ static int dw_probe(struct platform_device *pdev)
dw->dma.dst_addr_widths = AXI_DMA_BUSWIDTHS;
dw->dma.directions = BIT(DMA_MEM_TO_MEM);
dw->dma.directions |= BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM);
- dw->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
+ dw->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
dw->dma.dev = chip->dev;
dw->dma.device_tx_status = dma_chan_tx_status;
diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
index 3498bef5453b..46baf93de617 100644
--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
+++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
@@ -88,6 +88,7 @@ struct __packed axi_dma_lli {
struct axi_dma_hw_desc {
struct axi_dma_lli *lli;
dma_addr_t llp;
+ u32 len;
};
struct axi_dma_desc {
@@ -96,6 +97,7 @@ struct axi_dma_desc {
struct virt_dma_desc vd;
struct axi_dma_chan *chan;
u32 completed_blocks;
+ u32 length;
};
static inline struct device *dchan2dev(struct dma_chan *dchan)