Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

dmaengine: dma-axi-dmac: Gracefully terminate SW cyclic transfers

As of now, to terminate a cyclic transfer, one pretty much needs to use
brute force and terminate all transfers with .device_terminate_all().
With this change, when a cyclic transfer terminates (and generates an
EOT interrupt), look at any new pending transfer with the DMA_PREP_LOAD_EOT
flag set. If there is one, the current cyclic transfer is terminated and
the next one is enqueued. If the flag is not set, that transfer is ignored.

Signed-off-by: Nuno Sá <nuno.sa@analog.com>
Link: https://patch.msgid.link/20260303-axi-dac-cyclic-support-v2-4-0db27b4be95a@analog.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Nuno Sá and committed by
Vinod Koul
ca3bf200 c60990ba

+33 -1
+33 -1
drivers/dma/dma-axi-dmac.c
··· 233 233 struct virt_dma_desc *vdesc; 234 234 struct axi_dmac_desc *desc; 235 235 236 + /* 237 + * It means a SW cyclic transfer is in place so we should just return 238 + * the same descriptor. SW cyclic transfer termination is handled 239 + * in axi_dmac_transfer_done(). 240 + */ 236 241 if (chan->next_desc) 237 242 return chan->next_desc; 238 243 ··· 416 411 } 417 412 } 418 413 414 + static bool axi_dmac_handle_cyclic_eot(struct axi_dmac_chan *chan, 415 + struct axi_dmac_desc *active) 416 + { 417 + struct device *dev = chan_to_axi_dmac(chan)->dma_dev.dev; 418 + struct virt_dma_desc *vdesc; 419 + 420 + /* wrap around */ 421 + active->num_completed = 0; 422 + 423 + vdesc = vchan_next_desc(&chan->vchan); 424 + if (!vdesc) 425 + return false; 426 + if (!(vdesc->tx.flags & DMA_PREP_LOAD_EOT)) { 427 + dev_warn(dev, "Discarding non EOT transfer after cyclic\n"); 428 + list_del(&vdesc->node); 429 + return false; 430 + } 431 + 432 + /* then let's end the cyclic transfer */ 433 + chan->next_desc = NULL; 434 + list_del(&active->vdesc.node); 435 + vchan_cookie_complete(&active->vdesc); 436 + 437 + return true; 438 + } 439 + 419 440 static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan, 420 441 unsigned int completed_transfers) 421 442 { ··· 489 458 if (active->num_completed == active->num_sgs || 490 459 sg->partial_len) { 491 460 if (active->cyclic) { 492 - active->num_completed = 0; /* wrap around */ 461 + /* keep start_next as is, if already true... */ 462 + start_next |= axi_dmac_handle_cyclic_eot(chan, active); 493 463 } else { 494 464 list_del(&active->vdesc.node); 495 465 vchan_cookie_complete(&active->vdesc);