summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/dma/dma-axi-dmac.c34
1 files changed, 33 insertions, 1 deletions
diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c
index a47e08d3dbbc..e9814d725322 100644
--- a/drivers/dma/dma-axi-dmac.c
+++ b/drivers/dma/dma-axi-dmac.c
@@ -233,6 +233,11 @@ static struct axi_dmac_desc *axi_dmac_get_next_desc(struct axi_dmac *dmac,
struct virt_dma_desc *vdesc;
struct axi_dmac_desc *desc;
+ /*
+ * It means a SW cyclic transfer is in place so we should just return
+ * the same descriptor. SW cyclic transfer termination is handled
+ * in axi_dmac_transfer_done().
+ */
if (chan->next_desc)
return chan->next_desc;
@@ -411,6 +416,32 @@ static void axi_dmac_compute_residue(struct axi_dmac_chan *chan,
}
}
+static bool axi_dmac_handle_cyclic_eot(struct axi_dmac_chan *chan,
+ struct axi_dmac_desc *active)
+{
+ struct device *dev = chan_to_axi_dmac(chan)->dma_dev.dev;
+ struct virt_dma_desc *vdesc;
+
+ /* wrap around */
+ active->num_completed = 0;
+
+ vdesc = vchan_next_desc(&chan->vchan);
+ if (!vdesc)
+ return false;
+ if (!(vdesc->tx.flags & DMA_PREP_LOAD_EOT)) {
+ dev_warn(dev, "Discarding non EOT transfer after cyclic\n");
+ list_del(&vdesc->node);
+ return false;
+ }
+
+ /* then let's end the cyclic transfer */
+ chan->next_desc = NULL;
+ list_del(&active->vdesc.node);
+ vchan_cookie_complete(&active->vdesc);
+
+ return true;
+}
+
static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan,
unsigned int completed_transfers)
{
@@ -458,7 +489,8 @@ static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan,
if (active->num_completed == active->num_sgs ||
sg->partial_len) {
if (active->cyclic) {
- active->num_completed = 0; /* wrap around */
+ /* keep start_next as is, if already true... */
+ start_next |= axi_dmac_handle_cyclic_eot(chan, active);
} else {
list_del(&active->vdesc.node);
vchan_cookie_complete(&active->vdesc);