mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
dmaengine: fsl-edma: Implement device_prep_peripheral_dma_vec
Add implementation of .device_prep_peripheral_dma_vec() callback to setup a scatter/gather DMA transfer from an array of dma_vec structures. Setup a cyclic transfer if the DMA_PREP_REPEAT flag is set. Reviewed-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Benoît Monin <benoit.monin@bootlin.com> Link: https://patch.msgid.link/20260916-fsl-edma-dyn-sg-v11-1-02792de71b92@bootlin.com Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
parent
ef399305a5
commit
e2174129dd
|
|
@ -3,6 +3,7 @@
|
|||
// Copyright (c) 2013-2014 Freescale Semiconductor, Inc
|
||||
// Copyright (c) 2017 Sysam, Angelo Dureghello <angelo@sysam.it>
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dmapool.h>
|
||||
|
|
@ -673,6 +674,122 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
|
|||
return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags);
|
||||
}
|
||||
|
||||
struct dma_async_tx_descriptor *
|
||||
fsl_edma_prep_peripheral_dma_vec(struct dma_chan *chan, const struct dma_vec *vecs,
|
||||
size_t nb, enum dma_transfer_direction direction,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
|
||||
dma_addr_t src_addr, dst_addr, last_sg;
|
||||
struct fsl_edma_desc *fsl_desc;
|
||||
u16 soff, doff, iter;
|
||||
u32 nbytes;
|
||||
int i;
|
||||
|
||||
if (!is_slave_direction(direction))
|
||||
return NULL;
|
||||
|
||||
if (!fsl_edma_prep_slave_dma(fsl_chan, direction))
|
||||
return NULL;
|
||||
|
||||
fsl_desc = fsl_edma_alloc_desc(fsl_chan, nb);
|
||||
if (!fsl_desc)
|
||||
return NULL;
|
||||
fsl_desc->iscyclic = flags & DMA_PREP_REPEAT;
|
||||
fsl_desc->dirn = direction;
|
||||
|
||||
if (direction == DMA_MEM_TO_DEV) {
|
||||
if (!fsl_chan->cfg.src_addr_width)
|
||||
fsl_chan->cfg.src_addr_width = fsl_chan->cfg.dst_addr_width;
|
||||
fsl_chan->attr =
|
||||
fsl_edma_get_tcd_attr(fsl_chan->cfg.src_addr_width,
|
||||
fsl_chan->cfg.dst_addr_width);
|
||||
nbytes = fsl_chan->cfg.dst_addr_width * fsl_chan->cfg.dst_maxburst;
|
||||
} else {
|
||||
if (!fsl_chan->cfg.dst_addr_width)
|
||||
fsl_chan->cfg.dst_addr_width = fsl_chan->cfg.src_addr_width;
|
||||
fsl_chan->attr =
|
||||
fsl_edma_get_tcd_attr(fsl_chan->cfg.src_addr_width,
|
||||
fsl_chan->cfg.dst_addr_width);
|
||||
nbytes = fsl_chan->cfg.src_addr_width * fsl_chan->cfg.src_maxburst;
|
||||
}
|
||||
|
||||
for (i = 0; i < nb; i++) {
|
||||
if (direction == DMA_MEM_TO_DEV) {
|
||||
src_addr = vecs[i].addr;
|
||||
dst_addr = fsl_chan->dma_dev_addr;
|
||||
soff = fsl_chan->cfg.dst_addr_width;
|
||||
doff = 0;
|
||||
} else if (direction == DMA_DEV_TO_MEM) {
|
||||
src_addr = fsl_chan->dma_dev_addr;
|
||||
dst_addr = vecs[i].addr;
|
||||
soff = 0;
|
||||
doff = fsl_chan->cfg.src_addr_width;
|
||||
} else {
|
||||
/* DMA_DEV_TO_DEV */
|
||||
src_addr = fsl_chan->cfg.src_addr;
|
||||
dst_addr = fsl_chan->cfg.dst_addr;
|
||||
soff = 0;
|
||||
doff = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Choose the suitable burst length if dma_vec length is not
|
||||
* multiple of burst length so that the whole transfer length is
|
||||
* multiple of minor loop(burst length).
|
||||
*/
|
||||
if (nbytes && vecs[i].len % nbytes) {
|
||||
u32 width = (direction == DMA_DEV_TO_MEM) ? doff : soff;
|
||||
u32 burst = (direction == DMA_DEV_TO_MEM) ?
|
||||
fsl_chan->cfg.src_maxburst :
|
||||
fsl_chan->cfg.dst_maxburst;
|
||||
int j;
|
||||
|
||||
for (j = burst; j > 1; j--) {
|
||||
if (!(vecs[i].len % (j * width))) {
|
||||
nbytes = j * width;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Set burst size as 1 if there's no suitable one */
|
||||
if (j == 1)
|
||||
nbytes = width;
|
||||
}
|
||||
|
||||
if (!nbytes || vecs[i].len / nbytes > FIELD_MAX(EDMA_TCD_ITER_MASK))
|
||||
goto err_free_desc;
|
||||
|
||||
iter = vecs[i].len / nbytes;
|
||||
if (i < nb - 1) {
|
||||
last_sg = fsl_desc->tcd[(i + 1)].ptcd;
|
||||
fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[i].vtcd, src_addr,
|
||||
dst_addr, fsl_chan->attr, soff,
|
||||
nbytes, 0, iter, iter, doff, last_sg,
|
||||
false, false, true);
|
||||
} else {
|
||||
if (fsl_desc->iscyclic) {
|
||||
last_sg = fsl_desc->tcd[0].ptcd;
|
||||
fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[i].vtcd, src_addr,
|
||||
dst_addr, fsl_chan->attr, soff,
|
||||
nbytes, 0, iter, iter, doff, last_sg,
|
||||
true, false, true);
|
||||
} else {
|
||||
last_sg = 0;
|
||||
fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[i].vtcd, src_addr,
|
||||
dst_addr, fsl_chan->attr, soff,
|
||||
nbytes, 0, iter, iter, doff, last_sg,
|
||||
true, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags);
|
||||
|
||||
err_free_desc:
|
||||
fsl_edma_free_desc(&fsl_desc->vdesc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
|
||||
struct dma_chan *chan, struct scatterlist *sgl,
|
||||
unsigned int sg_len, enum dma_transfer_direction direction,
|
||||
|
|
|
|||
|
|
@ -496,6 +496,10 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
|
|||
struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
|
||||
size_t period_len, enum dma_transfer_direction direction,
|
||||
unsigned long flags);
|
||||
struct dma_async_tx_descriptor *fsl_edma_prep_peripheral_dma_vec(
|
||||
struct dma_chan *chan, const struct dma_vec *vecs,
|
||||
size_t nb, enum dma_transfer_direction direction,
|
||||
unsigned long flags);
|
||||
struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
|
||||
struct dma_chan *chan, struct scatterlist *sgl,
|
||||
unsigned int sg_len, enum dma_transfer_direction direction,
|
||||
|
|
|
|||
|
|
@ -846,6 +846,8 @@ static int fsl_edma_probe(struct platform_device *pdev)
|
|||
fsl_edma->dma_dev.device_free_chan_resources
|
||||
= fsl_edma_free_chan_resources;
|
||||
fsl_edma->dma_dev.device_tx_status = fsl_edma_tx_status;
|
||||
fsl_edma->dma_dev.device_prep_peripheral_dma_vec
|
||||
= fsl_edma_prep_peripheral_dma_vec;
|
||||
fsl_edma->dma_dev.device_prep_slave_sg = fsl_edma_prep_slave_sg;
|
||||
fsl_edma->dma_dev.device_prep_dma_cyclic = fsl_edma_prep_dma_cyclic;
|
||||
fsl_edma->dma_dev.device_prep_dma_memcpy = fsl_edma_prep_memcpy;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user