dmaengine: rockchip-dma: Add support for interleaved transfer

This patch add support for interleaved transfer which used
for interleaved audio or 2d video data transfer.

Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
Change-Id: I56144d6f2316a78493ab51afea575d20b49df794
This commit is contained in:
Sugar Zhang
2025-08-08 17:38:27 +08:00
committed by Tao Huang
parent 4eb9e4ebb5
commit 3713281d30

View File

@@ -1076,6 +1076,97 @@ rk_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_le
return vchan_tx_prep(&c->vc, &ds->vd, flags);
}
static struct dma_async_tx_descriptor *
rk_dma_prep_interleaved_dma(struct dma_chan *chan, struct dma_interleaved_template *xt,
unsigned long flags)
{
struct rk_dma_chan *c = to_rk_chan(chan);
struct rk_dma_desc_sw *ds;
size_t size, src_icg, dst_icg, period_bytes, full_period_bytes, full_buffer_bytes;
size_t nump = 0, numf = 0;
int num_periods, num = 0;
dma_addr_t src = 0, dst = 0, dma_addr = 0;
if (!xt->numf || !xt->sgl[0].size || xt->frame_size != 1)
return NULL;
#ifdef CONFIG_NO_GKI
nump = xt->nump;
#else
nump = xt->sgl[1].size;
#endif
numf = xt->numf;
size = xt->sgl[0].size;
period_bytes = size * nump;
if (flags & DMA_PREP_REPEAT && (!nump || (numf % nump)))
return NULL;
if (period_bytes > DMA_MAX_SIZE) {
dev_err(chan->device->dev, "maximum period size exceeded\n");
return NULL;
}
src_icg = dmaengine_get_src_icg(xt, &xt->sgl[0]);
dst_icg = dmaengine_get_dst_icg(xt, &xt->sgl[0]);
if (flags & DMA_PREP_REPEAT) {
num_periods = numf / nump;
c->cyclic = 1;
} else {
num_periods = 1;
c->cyclic = 0;
}
if (rk_pre_config(chan, xt->dir))
return NULL;
ds = rk_alloc_desc_resource(num_periods + 1, chan);
if (!ds)
return NULL;
c->ctl0 &= ~TRF_CTL0_MSIZE_MASK;
c->ctl0 |= TRF_CTL0_MSIZE(size);
c->ccfg |= TRF_CFG_STRIDE_EN | TRF_CFG_STRIDE_MODE_TRANS;
if (xt->dir == DMA_MEM_TO_DEV) {
ds->desc_hw[0].stride_inc = STRIDE_INC(src_icg);
dma_addr = xt->src_start;
full_period_bytes = (size + src_icg) * nump;
full_buffer_bytes = (size + src_icg) * numf;
} else {
ds->desc_hw[0].stride_inc = STRIDE_INC(dst_icg);
dma_addr = xt->dst_start;
full_period_bytes = (size + dst_icg) * nump;
full_buffer_bytes = (size + dst_icg) * numf;
}
/* the first one used as cmd entry */
rk_dma_fill_desc(ds, dst, src, 0, 0, c->ctl0, c->ctl1, c->ccfg);
num = 1;
while (num < (num_periods + 1)) {
if (xt->dir == DMA_MEM_TO_DEV) {
src = dma_addr;
dst = c->dev_addr;
} else if (xt->dir == DMA_DEV_TO_MEM) {
src = c->dev_addr;
dst = dma_addr;
}
rk_dma_fill_desc(ds, dst, src, period_bytes, num++, c->ctl0, c->ctl1, c->ccfg);
dma_addr += full_period_bytes;
}
ds->desc_hw[num - 1].llp_nxt = ds->desc_hw_lli + sizeof(struct rk_desc_hw);
ds->desc_hw[num - 1].trf_ctl0 |= TRF_CTL0_CNT_CLR;
ds->size = full_buffer_bytes;
ds->dir = xt->dir;
dev_dbg(chan->device->dev, "size: %zu, src_icg: %zu, dst_icg: %zu, nump: %zu, numf: %zu\n",
size, src_icg, dst_icg, nump, numf);
return vchan_tx_prep(&c->vc, &ds->vd, flags);
}
static int rk_dma_config(struct dma_chan *chan, struct dma_slave_config *cfg)
{
struct rk_dma_chan *c = to_rk_chan(chan);
@@ -1246,12 +1337,16 @@ static int rk_dma_probe(struct platform_device *pdev)
dma_cap_set(DMA_MEMCPY, d->slave.cap_mask);
dma_cap_set(DMA_CYCLIC, d->slave.cap_mask);
dma_cap_set(DMA_PRIVATE, d->slave.cap_mask);
dma_cap_set(DMA_INTERLEAVE, d->slave.cap_mask);
dma_cap_set(DMA_REPEAT, d->slave.cap_mask);
dma_cap_set(DMA_LOAD_EOT, d->slave.cap_mask);
d->slave.dev = &pdev->dev;
d->slave.device_free_chan_resources = rk_dma_free_chan_resources;
d->slave.device_tx_status = rk_dma_tx_status;
d->slave.device_prep_dma_memcpy = rk_dma_prep_memcpy;
d->slave.device_prep_slave_sg = rk_dma_prep_slave_sg;
d->slave.device_prep_dma_cyclic = rk_dma_prep_dma_cyclic;
d->slave.device_prep_interleaved_dma = rk_dma_prep_interleaved_dma;
d->slave.device_issue_pending = rk_dma_issue_pending;
d->slave.device_config = rk_dma_config;
d->slave.device_terminate_all = rk_dma_terminate_all;