From 0eba9f8ec037e4c7fccb16e2a433be822dd802b8 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Fri, 22 Sep 2023 09:57:32 +0800 Subject: [PATCH] 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 Change-Id: I502ea9c86c8403dc5b1f38abf40be8b6ee13c1dc --- drivers/dma/pl330.c | 177 ++++++++++++++++++++++++-------------- include/linux/dmaengine.h | 4 + 2 files changed, 116 insertions(+), 65 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index b1d6faede65b..0b2b7acf5462 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -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; diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 08537ef11a89..51510f40916d 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -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[]; };