diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 5c4318c5a5b1e42b47d6e0917a26fdd7a265b485..b0a9c94bc0deac11bed9aae65df3a3805bcda0c1 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -703,25 +703,32 @@ static void dma_do_tasklet(unsigned long data) spin_lock_irqsave(&chan->desc_lock, flags); - /* update the cookie if we have some descriptors to cleanup */ - if (!list_empty(&chan->chain_running)) { - dma_cookie_t cookie; - - desc = to_mmp_pdma_desc(chan->chain_running.prev); - cookie = desc->async_tx.cookie; - dma_cookie_complete(&desc->async_tx); + list_for_each_entry_safe(desc, _desc, &chan->chain_running, node) { + /* + * move the descriptors to a temporary list so we can drop + * the lock during the entire cleanup operation + */ + list_del(&desc->node); + list_add(&desc->node, &chain_cleanup); - dev_dbg(chan->dev, "completed_cookie=%d\n", cookie); + /* + * Look for the first list entry which has the ENDIRQEN flag + * set. That is the descriptor we got an interrupt for, so + * complete that transaction and its cookie. + */ + if (desc->desc.dcmd & DCMD_ENDIRQEN) { + dma_cookie_t cookie = desc->async_tx.cookie; + dma_cookie_complete(&desc->async_tx); + dev_dbg(chan->dev, "completed_cookie=%d\n", cookie); + break; + } } /* - * move the descriptors to a temporary list so we can drop the lock - * during the entire cleanup operation + * The hardware is idle and ready for more when the + * chain_running list is empty. */ - list_splice_tail_init(&chan->chain_running, &chain_cleanup); - - /* the hardware is now idle and ready for more */ - chan->idle = true; + chan->idle = list_empty(&chan->chain_running); /* Start any pending transactions automatically */ start_pending_queue(chan);