[PATCH 2/2] dmaengine: s3c24xx-dma: Add cyclic transfer support
Andy Shevchenko
andriy.shevchenko at linux.intel.com
Mon May 19 01:40:30 PDT 2014
On Sun, 2014-05-18 at 23:44 +0300, Vasily Khoruzhick wrote:
> Many audio interface drivers require support of cyclic transfers to work
> correctly, for example Samsung ASoC DMA driver. This patch adds support
> for cyclic transfers to the s3c24xx-dma driver
> +static struct dma_async_tx_descriptor *s3c24xx_dma_prep_dma_cyclic(
> + struct dma_chan *chan, dma_addr_t addr, size_t size, size_t period,
> + enum dma_transfer_direction direction, unsigned long flags,
> + void *context)
> +{
> + struct s3c24xx_dma_chan *s3cchan = to_s3c24xx_dma_chan(chan);
> + struct s3c24xx_dma_engine *s3cdma = s3cchan->host;
> + const struct s3c24xx_dma_platdata *pdata = s3cdma->pdata;
> + struct s3c24xx_dma_channel *cdata = &pdata->channels[s3cchan->id];
> + struct s3c24xx_txd *txd;
> + struct s3c24xx_sg *dsg;
> + unsigned sg_len;
> + dma_addr_t slave_addr;
> + u32 hwcfg = 0;
> + int i;
> +
> + dev_dbg(&s3cdma->pdev->dev,
> + "prepare cyclic transaction of %d bytes with period %d from %s\n",
> + size, period, s3cchan->name);
> +
> + txd = s3c24xx_dma_get_txd();
> + if (!txd)
> + return NULL;
> +
> + txd->cyclic = 1;
> +
> + if (cdata->handshake)
> + txd->dcon |= S3C24XX_DCON_HANDSHAKE;
> +
> + switch (cdata->bus) {
> + case S3C24XX_DMA_APB:
> + txd->dcon |= S3C24XX_DCON_SYNC_PCLK;
> + hwcfg |= S3C24XX_DISRCC_LOC_APB;
> + break;
> + case S3C24XX_DMA_AHB:
> + txd->dcon |= S3C24XX_DCON_SYNC_HCLK;
> + hwcfg |= S3C24XX_DISRCC_LOC_AHB;
> + break;
> + }
> +
> + /*
> + * Always assume our peripheral desintation is a fixed
> + * address in memory.
> + */
> + hwcfg |= S3C24XX_DISRCC_INC_FIXED;
> +
> + /*
> + * Individual dma operations are requested by the slave,
> + * so serve only single atomic operations (S3C24XX_DCON_SERV_SINGLE).
> + */
> + txd->dcon |= S3C24XX_DCON_SERV_SINGLE;
> +
> + if (direction == DMA_MEM_TO_DEV) {
> + txd->disrcc = S3C24XX_DISRCC_LOC_AHB |
> + S3C24XX_DISRCC_INC_INCREMENT;
> + txd->didstc = hwcfg;
> + slave_addr = s3cchan->cfg.dst_addr;
> + txd->width = s3cchan->cfg.dst_addr_width;
> + } else if (direction == DMA_DEV_TO_MEM) {
> + txd->disrcc = hwcfg;
> + txd->didstc = S3C24XX_DIDSTC_LOC_AHB |
> + S3C24XX_DIDSTC_INC_INCREMENT;
> + slave_addr = s3cchan->cfg.src_addr;
> + txd->width = s3cchan->cfg.src_addr_width;
> + } else {
> + s3c24xx_dma_free_txd(txd);
> + dev_err(&s3cdma->pdev->dev,
> + "direction %d unsupported\n", direction);
> + return NULL;
> + }
Instead of doing this, you may put few lines on top of function
if (!is_slave_direction) {
dev_err(&s3cdma->pdev->dev, "direction %d unsupported\n", direction);
return NULL;
}
As an additional effect you can transform the 'else if' to just 'else' above the code.
> +
> + sg_len = size / period;
> +
> + for (i = 0; i < sg_len; i++) {
> + dsg = kzalloc(sizeof(*dsg), GFP_NOWAIT);
> + if (!dsg) {
> + s3c24xx_dma_free_txd(txd);
> + return NULL;
> + }
> + list_add_tail(&dsg->node, &txd->dsg_list);
> +
> + dsg->len = period;
> + /* Check last period length */
> + if (i == (sg_len - 1))
> + dsg->len = size - (period * i);
> + if (direction == DMA_MEM_TO_DEV) {
> + dsg->src_addr = addr + (period * i);
> + dsg->dst_addr = slave_addr;
> + } else { /* DMA_DEV_TO_MEM */
> + dsg->src_addr = slave_addr;
> + dsg->dst_addr = addr + (period * i);
> + }
> + }
> +
> + return vchan_tx_prep(&s3cchan->vc, &txd->vd, flags);
> +}
--
Andy Shevchenko <andriy.shevchenko at intel.com>
Intel Finland Oy
More information about the linux-arm-kernel
mailing list