dmaengine: dw-edma: Fix to change for continuous transfer
authorShunsuke Mie <mie@igel.co.jp>
Tue, 11 Apr 2023 10:17:57 +0000 (19:17 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 11 May 2023 14:03:37 +0000 (23:03 +0900)
[ Upstream commit a251994a441ee0a69ba7062c8cd2d08ead3db379 ]

The dw-edma driver stops after processing a DMA request even if a request
remains in the issued queue, which is not the expected behavior. The DMA
engine API requires continuous processing.

Add a trigger to start after one processing finished if there are requests
remain.

Fixes: e63d79d1ffcd ("dmaengine: Add Synopsys eDMA IP core driver")
Signed-off-by: Shunsuke Mie <mie@igel.co.jp>
Link: https://lore.kernel.org/r/20230411101758.438472-1-mie@igel.co.jp
Signed-off-by: Vinod Koul <vkoul@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/dma/dw-edma/dw-edma-core.c

index 52bdf04..cb46cbe 100644 (file)
@@ -170,7 +170,7 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc)
        dw_edma_free_desc(vd2dw_edma_desc(vdesc));
 }
 
-static void dw_edma_start_transfer(struct dw_edma_chan *chan)
+static int dw_edma_start_transfer(struct dw_edma_chan *chan)
 {
        struct dw_edma_chunk *child;
        struct dw_edma_desc *desc;
@@ -178,16 +178,16 @@ static void dw_edma_start_transfer(struct dw_edma_chan *chan)
 
        vd = vchan_next_desc(&chan->vc);
        if (!vd)
-               return;
+               return 0;
 
        desc = vd2dw_edma_desc(vd);
        if (!desc)
-               return;
+               return 0;
 
        child = list_first_entry_or_null(&desc->chunk->list,
                                         struct dw_edma_chunk, list);
        if (!child)
-               return;
+               return 0;
 
        dw_edma_v0_core_start(child, !desc->xfer_sz);
        desc->xfer_sz += child->ll_region.sz;
@@ -195,6 +195,8 @@ static void dw_edma_start_transfer(struct dw_edma_chan *chan)
        list_del(&child->list);
        kfree(child);
        desc->chunks_alloc--;
+
+       return 1;
 }
 
 static int dw_edma_device_config(struct dma_chan *dchan,
@@ -572,14 +574,14 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
                switch (chan->request) {
                case EDMA_REQ_NONE:
                        desc = vd2dw_edma_desc(vd);
-                       if (desc->chunks_alloc) {
-                               chan->status = EDMA_ST_BUSY;
-                               dw_edma_start_transfer(chan);
-                       } else {
+                       if (!desc->chunks_alloc) {
                                list_del(&vd->node);
                                vchan_cookie_complete(vd);
-                               chan->status = EDMA_ST_IDLE;
                        }
+
+                       /* Continue transferring if there are remaining chunks or issued requests.
+                        */
+                       chan->status = dw_edma_start_transfer(chan) ? EDMA_ST_BUSY : EDMA_ST_IDLE;
                        break;
 
                case EDMA_REQ_STOP: