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[]; };