diff --git a/drivers/dma/rockchip-dma.c b/drivers/dma/rockchip-dma.c index 3fbccc21660e..74c823ac960f 100644 --- a/drivers/dma/rockchip-dma.c +++ b/drivers/dma/rockchip-dma.c @@ -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;