dmaengine: pl330: Add support for interleaved transfer

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

for audio situation, we add 'nump' for number of period frames.

Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
Change-Id: I502ea9c86c8403dc5b1f38abf40be8b6ee13c1dc
This commit is contained in:
Sugar Zhang
2023-09-22 09:57:32 +08:00
committed by Tao Huang
parent de0064df8f
commit 0eba9f8ec0
2 changed files with 116 additions and 65 deletions

View File

@@ -541,11 +541,9 @@ struct dma_pl330_desc {
/* For cyclic capability */ /* For cyclic capability */
bool cyclic; bool cyclic;
size_t num_periods; size_t num_periods;
#ifdef CONFIG_NO_GKI
/* interlace size */ /* interleaved size */
unsigned int src_interlace_size; struct data_chunk sgl;
unsigned int dst_interlace_size;
#endif
}; };
struct _xfer_spec { struct _xfer_spec {
@@ -1204,7 +1202,7 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330,
const struct _xfer_spec *pxs, int cyc, const struct _xfer_spec *pxs, int cyc,
enum pl330_cond cond) enum pl330_cond cond)
{ {
int off = 0; int off = 0, i = 0, burstn = 1;
/* /*
* do FLUSHP at beginning to clear any stale dma requests before the * do FLUSHP at beginning to clear any stale dma requests before the
@@ -1212,30 +1210,36 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330,
*/ */
if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)) if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP))
off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri); off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri);
if (pxs->desc->sgl.size) {
WARN_ON(BYTE_MOD_BURST_LEN(pxs->desc->sgl.size, pxs->ccr));
burstn = BYTE_TO_BURST(pxs->desc->sgl.size, pxs->ccr);
}
while (cyc--) { while (cyc--) {
off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); for (i = 0; i < burstn; i++) {
off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri);
pxs->desc->peri); off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype,
off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, pxs->desc->peri);
pxs->desc->peri); off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype,
#ifdef CONFIG_NO_GKI pxs->desc->peri);
}
switch (pxs->desc->rqtype) { switch (pxs->desc->rqtype) {
case DMA_DEV_TO_MEM: case DMA_DEV_TO_MEM:
if (pxs->desc->sgl.dst_icg)
if (pxs->desc->dst_interlace_size)
off += _emit_ADDH(dry_run, &buf[off], DST, off += _emit_ADDH(dry_run, &buf[off], DST,
pxs->desc->dst_interlace_size); pxs->desc->sgl.dst_icg);
break; break;
case DMA_MEM_TO_DEV: case DMA_MEM_TO_DEV:
if (pxs->desc->src_interlace_size) if (pxs->desc->sgl.src_icg)
off += _emit_ADDH(dry_run, &buf[off], SRC, off += _emit_ADDH(dry_run, &buf[off], SRC,
pxs->desc->src_interlace_size); pxs->desc->sgl.src_icg);
break; break;
default: default:
WARN_ON(1); WARN_ON(1);
break; break;
} }
#endif
} }
return off; return off;
@@ -1450,9 +1454,7 @@ static int _period(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[],
off += _emit_LPEND(dry_run, &buf[off], &lpend); off += _emit_LPEND(dry_run, &buf[off], &lpend);
} }
#ifdef CONFIG_NO_GKI if (!pxs->desc->sgl.src_icg && !pxs->desc->sgl.dst_icg) {
if (!pxs->desc->src_interlace_size &&
!pxs->desc->dst_interlace_size) {
num_dregs = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr); num_dregs = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr);
if (num_dregs) { if (num_dregs) {
@@ -1460,14 +1462,6 @@ static int _period(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[],
off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr);
} }
} }
#else
num_dregs = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr);
if (num_dregs) {
off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs);
off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr);
}
#endif
off += _emit_SEV(dry_run, &buf[off], ev); off += _emit_SEV(dry_run, &buf[off], ev);
@@ -1535,26 +1529,18 @@ static inline int _setup_loops(struct pl330_dmac *pl330,
BRST_SIZE(ccr); BRST_SIZE(ccr);
int off = 0; int off = 0;
#ifdef CONFIG_NO_GKI if (pxs->desc->sgl.size)
if (pxs->desc->rqtype == DMA_DEV_TO_MEM) bursts = x->bytes / pxs->desc->sgl.size;
bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) +
pxs->desc->dst_interlace_size);
else if (pxs->desc->rqtype == DMA_MEM_TO_DEV)
bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) +
pxs->desc->src_interlace_size);
#endif
while (bursts) { while (bursts) {
c = bursts; c = bursts;
off += _loop(pl330, dry_run, &buf[off], &c, pxs); off += _loop(pl330, dry_run, &buf[off], &c, pxs);
bursts -= c; bursts -= c;
} }
#ifdef CONFIG_NO_GKI
if (!pxs->desc->src_interlace_size && if (!pxs->desc->sgl.src_icg && !pxs->desc->sgl.dst_icg)
!pxs->desc->dst_interlace_size)
off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs); off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs);
#else
off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs);
#endif
return off; return off;
} }
@@ -1585,14 +1571,9 @@ static inline int _setup_xfer_cyclic(struct pl330_dmac *pl330,
unsigned long bursts = BYTE_TO_BURST(x->bytes, ccr); unsigned long bursts = BYTE_TO_BURST(x->bytes, ccr);
int off = 0; int off = 0;
#ifdef CONFIG_NO_GKI if (pxs->desc->sgl.size)
if (pxs->desc->rqtype == DMA_DEV_TO_MEM) bursts = x->bytes / pxs->desc->sgl.size;
bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr)
+ pxs->desc->dst_interlace_size);
else if (pxs->desc->rqtype == DMA_MEM_TO_DEV)
bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr)
+ pxs->desc->src_interlace_size);
#endif
/* Setup Loop(s) */ /* Setup Loop(s) */
off += _loop_cyclic(pl330, dry_run, &buf[off], bursts, pxs, ev); off += _loop_cyclic(pl330, dry_run, &buf[off], bursts, pxs, ev);
@@ -2454,10 +2435,6 @@ static int pl330_config_write(struct dma_chan *chan,
pch->fifo_addr = slave_config->dst_addr; pch->fifo_addr = slave_config->dst_addr;
if (slave_config->dst_addr_width) if (slave_config->dst_addr_width)
pch->burst_sz = __ffs(slave_config->dst_addr_width); pch->burst_sz = __ffs(slave_config->dst_addr_width);
#ifdef CONFIG_NO_GKI
if (slave_config->src_interlace_size)
pch->slave_config.src_interlace_size = slave_config->src_interlace_size;
#endif
pch->burst_len = fixup_burst_len(slave_config->dst_maxburst, pch->burst_len = fixup_burst_len(slave_config->dst_maxburst,
pch->dmac->quirks); pch->dmac->quirks);
} else if (direction == DMA_DEV_TO_MEM) { } else if (direction == DMA_DEV_TO_MEM) {
@@ -2465,10 +2442,6 @@ static int pl330_config_write(struct dma_chan *chan,
pch->fifo_addr = slave_config->src_addr; pch->fifo_addr = slave_config->src_addr;
if (slave_config->src_addr_width) if (slave_config->src_addr_width)
pch->burst_sz = __ffs(slave_config->src_addr_width); pch->burst_sz = __ffs(slave_config->src_addr_width);
#ifdef CONFIG_NO_GKI
if (slave_config->dst_interlace_size)
pch->slave_config.dst_interlace_size = slave_config->dst_interlace_size;
#endif
pch->burst_len = fixup_burst_len(slave_config->src_maxburst, pch->burst_len = fixup_burst_len(slave_config->src_maxburst,
pch->dmac->quirks); pch->dmac->quirks);
} }
@@ -2821,6 +2794,10 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch)
desc->cyclic = false; desc->cyclic = false;
desc->num_periods = 1; desc->num_periods = 1;
desc->sgl.size = 0;
desc->sgl.src_icg = 0;
desc->sgl.dst_icg = 0;
dma_async_tx_descriptor_init(&desc->txd, &pch->chan); dma_async_tx_descriptor_init(&desc->txd, &pch->chan);
return desc; return desc;
@@ -2936,10 +2913,80 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
desc->cyclic = true; desc->cyclic = true;
desc->num_periods = len / period_len; desc->num_periods = len / period_len;
desc->txd.flags = flags; desc->txd.flags = flags;
return &desc->txd;
}
static struct dma_async_tx_descriptor *pl330_prep_interleaved_dma(
struct dma_chan *chan, struct dma_interleaved_template *xt,
unsigned long flags)
{
struct dma_pl330_desc *desc = NULL;
struct dma_pl330_chan *pch = to_pchan(chan);
dma_addr_t dst = 0, src = 0;
size_t size, src_icg, dst_icg, period_bytes, buffer_bytes;
size_t nump = 0, numf = 0;
if (!xt->numf || !xt->sgl[0].size || xt->frame_size != 1)
return NULL;
#ifdef CONFIG_NO_GKI #ifdef CONFIG_NO_GKI
desc->src_interlace_size = pch->slave_config.src_interlace_size; nump = xt->nump;
desc->dst_interlace_size = pch->slave_config.dst_interlace_size;
#endif #endif
numf = xt->numf;
size = xt->sgl[0].size;
period_bytes = size * nump;
buffer_bytes = size * numf;
if (flags & DMA_PREP_REPEAT && (!nump || (numf % nump)))
return NULL;
src_icg = dmaengine_get_src_icg(xt, &xt->sgl[0]);
dst_icg = dmaengine_get_dst_icg(xt, &xt->sgl[0]);
pl330_config_write(chan, &pch->slave_config, xt->dir);
if (!pl330_prep_slave_fifo(pch, xt->dir))
return NULL;
desc = pl330_get_desc(pch);
if (!desc) {
dev_err(chan->device->dev, "Failed to get desc\n");
return NULL;
}
if (xt->dir == DMA_MEM_TO_DEV) {
desc->rqcfg.src_inc = 1;
desc->rqcfg.dst_inc = 0;
src = xt->src_start;
dst = pch->fifo_dma;
} else {
desc->rqcfg.src_inc = 0;
desc->rqcfg.dst_inc = 1;
src = pch->fifo_dma;
dst = xt->dst_start;
}
desc->rqtype = xt->dir;
desc->rqcfg.brst_size = pch->burst_sz;
desc->rqcfg.brst_len = pch->burst_len;
desc->bytes_requested = buffer_bytes;
desc->sgl.size = size;
desc->sgl.src_icg = src_icg;
desc->sgl.dst_icg = dst_icg;
desc->txd.flags = flags;
if (flags & DMA_PREP_REPEAT) {
desc->cyclic = true;
desc->num_periods = numf / nump;
fill_px(&desc->px, dst, src, period_bytes);
} else {
fill_px(&desc->px, dst, src, buffer_bytes);
}
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 &desc->txd; return &desc->txd;
} }
@@ -3072,10 +3119,6 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
desc->rqcfg.brst_len = pch->burst_len; desc->rqcfg.brst_len = pch->burst_len;
desc->rqtype = direction; desc->rqtype = direction;
desc->bytes_requested = sg_dma_len(sg); desc->bytes_requested = sg_dma_len(sg);
#ifdef CONFIG_NO_GKI
desc->src_interlace_size = pch->slave_config.src_interlace_size;
desc->dst_interlace_size = pch->slave_config.dst_interlace_size;
#endif
} }
/* Return the last desc in the chain */ /* Return the last desc in the chain */
@@ -3311,12 +3354,16 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
dma_cap_set(DMA_SLAVE, pd->cap_mask); dma_cap_set(DMA_SLAVE, pd->cap_mask);
dma_cap_set(DMA_CYCLIC, pd->cap_mask); dma_cap_set(DMA_CYCLIC, pd->cap_mask);
dma_cap_set(DMA_PRIVATE, pd->cap_mask); dma_cap_set(DMA_PRIVATE, pd->cap_mask);
dma_cap_set(DMA_INTERLEAVE, pd->cap_mask);
dma_cap_set(DMA_REPEAT, pd->cap_mask);
dma_cap_set(DMA_LOAD_EOT, pd->cap_mask);
} }
pd->device_alloc_chan_resources = pl330_alloc_chan_resources; pd->device_alloc_chan_resources = pl330_alloc_chan_resources;
pd->device_free_chan_resources = pl330_free_chan_resources; pd->device_free_chan_resources = pl330_free_chan_resources;
pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy; pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic; pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic;
pd->device_prep_interleaved_dma = pl330_prep_interleaved_dma;
pd->device_tx_status = pl330_tx_status; pd->device_tx_status = pl330_tx_status;
pd->device_prep_slave_sg = pl330_prep_slave_sg; pd->device_prep_slave_sg = pl330_prep_slave_sg;
pd->device_config = pl330_config; pd->device_config = pl330_config;

View File

@@ -145,6 +145,7 @@ struct data_chunk {
* Otherwise, destination is filled contiguously (icg ignored). * Otherwise, destination is filled contiguously (icg ignored).
* Ignored if dst_inc is false. * Ignored if dst_inc is false.
* @numf: Number of frames in this template. * @numf: Number of frames in this template.
* @nump: Number of period frames in this template.
* @frame_size: Number of chunks in a frame i.e, size of sgl[]. * @frame_size: Number of chunks in a frame i.e, size of sgl[].
* @sgl: Array of {chunk,icg} pairs that make up a frame. * @sgl: Array of {chunk,icg} pairs that make up a frame.
*/ */
@@ -157,6 +158,9 @@ struct dma_interleaved_template {
bool src_sgl; bool src_sgl;
bool dst_sgl; bool dst_sgl;
size_t numf; size_t numf;
#ifdef CONFIG_NO_GKI
size_t nump;
#endif
size_t frame_size; size_t frame_size;
struct data_chunk sgl[]; struct data_chunk sgl[];
}; };