[PATCH v3 20/24] dmaengine: dw-edma: Dynamically append requests while running
Koichiro Den <[email protected]>
| Newsgroups | org.kernel.vger.dmaengine,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
From: Frank Li <[email protected]> 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 restart paths. Signed-off-by: Frank Li <[email protected]> Co-developed-by: Koichiro Den <[email protected]> Signed-off-by: Koichiro Den <[email protected]> --- Changes in v3: - Adapt start and append paths to serialized ABORT handling. (Sashiko) - Move recovery gating to the later recovery-infrastructure patch. drivers/dma/dw-edma/dw-edma-core.c | 53 ++++++++++++++++++------------ 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c index bbd417e98a9d..98b8732a6cb0 100644 --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -382,35 +382,42 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan) { struct dw_edma_desc *desc; struct virt_dma_desc *vd; + int ret = 0; - if (!chan->non_ll) { - if (dw_edma_abort_is_pending(chan)) + if (chan->non_ll) { + vd = vchan_next_desc(&chan->vc); + if (!vd) return 0; - if (!chan->ll_valid) - dw_edma_core_reset_ll(chan); - } - - vd = vchan_next_desc(&chan->vc); - if (!vd) - return 0; - desc = vd2dw_edma_desc(vd); - if (!desc) - return 0; - - if (chan->non_ll) { guard(raw_spinlock_irqsave)(dw_edma_event_lock(chan)); if (dw_edma_abort_latch_locked(chan)) return 0; - dw_edma_core_start(desc); + dw_edma_core_start(vd2dw_edma_desc(vd)); return 1; } - dw_edma_core_start(desc); + if (dw_edma_abort_is_pending(chan)) + return 0; - return 1; + if (!chan->ll_valid) + dw_edma_core_reset_ll(chan); + + list_for_each_entry(vd, &chan->vc.desc_issued, node) { + if (!dw_edma_core_get_free_num(chan)) + break; + + 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) @@ -896,6 +903,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); @@ -907,9 +915,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_or_recheck(chan); } @@ -970,9 +979,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_snapshot_discard(chan); chan->status = EDMA_ST_BUSY; -- 2.51.0