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:
+112
-65
@@ -541,11 +541,9 @@ struct dma_pl330_desc {
|
||||
/* For cyclic capability */
|
||||
bool cyclic;
|
||||
size_t num_periods;
|
||||
#ifdef CONFIG_NO_GKI
|
||||
/* interlace size */
|
||||
unsigned int src_interlace_size;
|
||||
unsigned int dst_interlace_size;
|
||||
#endif
|
||||
|
||||
/* interleaved size */
|
||||
struct data_chunk sgl;
|
||||
};
|
||||
|
||||
struct _xfer_spec {
|
||||
@@ -1204,7 +1202,7 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330,
|
||||
const struct _xfer_spec *pxs, int cyc,
|
||||
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
|
||||
@@ -1212,30 +1210,36 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330,
|
||||
*/
|
||||
if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP))
|
||||
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--) {
|
||||
off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri);
|
||||
off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype,
|
||||
pxs->desc->peri);
|
||||
off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype,
|
||||
pxs->desc->peri);
|
||||
#ifdef CONFIG_NO_GKI
|
||||
for (i = 0; i < burstn; i++) {
|
||||
off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri);
|
||||
off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype,
|
||||
pxs->desc->peri);
|
||||
off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype,
|
||||
pxs->desc->peri);
|
||||
}
|
||||
|
||||
switch (pxs->desc->rqtype) {
|
||||
case DMA_DEV_TO_MEM:
|
||||
|
||||
if (pxs->desc->dst_interlace_size)
|
||||
if (pxs->desc->sgl.dst_icg)
|
||||
off += _emit_ADDH(dry_run, &buf[off], DST,
|
||||
pxs->desc->dst_interlace_size);
|
||||
pxs->desc->sgl.dst_icg);
|
||||
break;
|
||||
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,
|
||||
pxs->desc->src_interlace_size);
|
||||
pxs->desc->sgl.src_icg);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NO_GKI
|
||||
if (!pxs->desc->src_interlace_size &&
|
||||
!pxs->desc->dst_interlace_size) {
|
||||
if (!pxs->desc->sgl.src_icg && !pxs->desc->sgl.dst_icg) {
|
||||
num_dregs = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
#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);
|
||||
|
||||
@@ -1535,26 +1529,18 @@ static inline int _setup_loops(struct pl330_dmac *pl330,
|
||||
BRST_SIZE(ccr);
|
||||
int off = 0;
|
||||
|
||||
#ifdef CONFIG_NO_GKI
|
||||
if (pxs->desc->rqtype == DMA_DEV_TO_MEM)
|
||||
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
|
||||
if (pxs->desc->sgl.size)
|
||||
bursts = x->bytes / pxs->desc->sgl.size;
|
||||
|
||||
while (bursts) {
|
||||
c = bursts;
|
||||
off += _loop(pl330, dry_run, &buf[off], &c, pxs);
|
||||
bursts -= c;
|
||||
}
|
||||
#ifdef CONFIG_NO_GKI
|
||||
if (!pxs->desc->src_interlace_size &&
|
||||
!pxs->desc->dst_interlace_size)
|
||||
|
||||
if (!pxs->desc->sgl.src_icg && !pxs->desc->sgl.dst_icg)
|
||||
off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs);
|
||||
#else
|
||||
off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs);
|
||||
#endif
|
||||
|
||||
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);
|
||||
int off = 0;
|
||||
|
||||
#ifdef CONFIG_NO_GKI
|
||||
if (pxs->desc->rqtype == DMA_DEV_TO_MEM)
|
||||
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
|
||||
if (pxs->desc->sgl.size)
|
||||
bursts = x->bytes / pxs->desc->sgl.size;
|
||||
|
||||
/* Setup Loop(s) */
|
||||
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;
|
||||
if (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->dmac->quirks);
|
||||
} 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;
|
||||
if (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->dmac->quirks);
|
||||
}
|
||||
@@ -2821,6 +2794,10 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch)
|
||||
desc->cyclic = false;
|
||||
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);
|
||||
|
||||
return desc;
|
||||
@@ -2936,10 +2913,80 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
|
||||
desc->cyclic = true;
|
||||
desc->num_periods = len / period_len;
|
||||
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
|
||||
desc->src_interlace_size = pch->slave_config.src_interlace_size;
|
||||
desc->dst_interlace_size = pch->slave_config.dst_interlace_size;
|
||||
nump = xt->nump;
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -3072,10 +3119,6 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
|
||||
desc->rqcfg.brst_len = pch->burst_len;
|
||||
desc->rqtype = direction;
|
||||
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 */
|
||||
@@ -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_CYCLIC, 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_free_chan_resources = pl330_free_chan_resources;
|
||||
pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
|
||||
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_prep_slave_sg = pl330_prep_slave_sg;
|
||||
pd->device_config = pl330_config;
|
||||
|
||||
@@ -145,6 +145,7 @@ struct data_chunk {
|
||||
* Otherwise, destination is filled contiguously (icg ignored).
|
||||
* Ignored if dst_inc is false.
|
||||
* @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[].
|
||||
* @sgl: Array of {chunk,icg} pairs that make up a frame.
|
||||
*/
|
||||
@@ -157,6 +158,9 @@ struct dma_interleaved_template {
|
||||
bool src_sgl;
|
||||
bool dst_sgl;
|
||||
size_t numf;
|
||||
#ifdef CONFIG_NO_GKI
|
||||
size_t nump;
|
||||
#endif
|
||||
size_t frame_size;
|
||||
struct data_chunk sgl[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user