[PATCH v2 18/19] dmaengine: dw-edma: Dynamically append requests while running
From: Koichiro Den
Date: Thu Jul 23 2026 - 04:46:37 EST
From: Frank Li <Frank.Li@xxxxxxx>
Publish issued descriptors into free LL entries while the channel runs.
Skip descriptors already fully published and continue with later ones.
For LL channels, dw_edma_start_transfer() reports only whether this pass
published entries. Derive BUSY or IDLE from pending ring work instead.
Let issue_pending() feed a BUSY LL channel when neither EDMA_REQ_STOP nor
EDMA_REQ_PAUSE is pending; non-LL channels remain IDLE-only.
All kicks still use the common doorbell path, so running channels continue
without another kick and stopped channels use the reconciliation and
recovery paths.
Signed-off-by: Frank Li <Frank.Li@xxxxxxx>
Co-developed-by: Koichiro Den <den@xxxxxxxxxxxxx>
Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Changes in v2:
- Enable live append only after the progress and recovery support.
- Keep non-LL channels on the IDLE-only path.
- Base resume state on pending LL entries, not whether that pass
published new entries.
drivers/dma/dw-edma/dw-edma-core.c | 47 ++++++++++++++++++++----------
1 file changed, 32 insertions(+), 15 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 1b9ba34b25c8..1ccd3783181e 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -323,25 +323,38 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan)
{
struct dw_edma_desc *desc;
struct virt_dma_desc *vd;
+ int ret = 0;
- /* Stop publishing until recovery can rebuild and republish the ring. */
- if (!chan->non_ll && chan->ll_recovering)
- return 0;
+ if (chan->non_ll) {
+ vd = vchan_next_desc(&chan->vc);
+ if (!vd)
+ return 0;
- vd = vchan_next_desc(&chan->vc);
- if (!vd)
- return 0;
+ dw_edma_core_start(vd2dw_edma_desc(vd));
+ return 1;
+ }
- desc = vd2dw_edma_desc(vd);
- if (!desc)
+ /* Stop publishing until recovery can rebuild and republish the ring. */
+ if (chan->ll_recovering)
return 0;
- if (!chan->non_ll && !chan->ll_valid)
+ if (!chan->ll_valid)
dw_edma_core_reset_ll(chan);
- dw_edma_core_start(desc);
+ list_for_each_entry(vd, &chan->vc.desc_issued, node) {
+ if (!dw_edma_core_get_free_num(chan))
+ break;
- return 1;
+ desc = vd2dw_edma_desc(vd);
+ /* A fully published descriptor may still be pending in hardware. */
+ if (desc->start_burst == desc->nburst)
+ continue;
+
+ dw_edma_core_start(desc);
+ ret = 1;
+ }
+
+ return ret;
}
static void dw_edma_terminate_vdesc(struct virt_dma_desc *vd)
@@ -908,6 +921,7 @@ static int dw_edma_device_pause(struct dma_chan *dchan)
static int dw_edma_device_resume(struct dma_chan *dchan)
{
struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+ bool active;
int err = 0;
guard(spinlock_irqsave)(&chan->vc.lock);
@@ -919,9 +933,10 @@ static int dw_edma_device_resume(struct dma_chan *dchan)
} else if (chan->request != EDMA_REQ_NONE) {
err = -EPERM;
} else {
- chan->status = EDMA_ST_BUSY;
- if (!dw_edma_start_transfer(chan))
- chan->status = EDMA_ST_IDLE;
+ active = dw_edma_start_transfer(chan);
+ if (!chan->non_ll)
+ active = dw_edma_ll_pending(chan);
+ chan->status = active ? EDMA_ST_BUSY : EDMA_ST_IDLE;
dw_edma_core_ch_maybe_doorbell(chan);
}
@@ -994,9 +1009,11 @@ static void dw_edma_device_issue_pending(struct dma_chan *dchan)
unsigned long flags;
spin_lock_irqsave(&chan->vc.lock, flags);
+ /* Only LL channels can accept work while already running. */
if (chan->configured && vchan_issue_pending(&chan->vc) &&
chan->request == EDMA_REQ_NONE &&
- chan->status == EDMA_ST_IDLE) {
+ (chan->non_ll ? chan->status == EDMA_ST_IDLE :
+ chan->status != EDMA_ST_PAUSE)) {
if (!chan->non_ll && !dw_edma_ll_pending(chan))
dw_edma_ll_irq_idx_discard(chan);
chan->status = EDMA_ST_BUSY;
--
2.51.0