diff options
Diffstat (limited to 'recipes-kernel/linux/linux-yocto-3.14/h1940/0014-dmaengine-s3c24xx-dma-Add-cyclic-transfer-support.patch')
-rw-r--r-- | recipes-kernel/linux/linux-yocto-3.14/h1940/0014-dmaengine-s3c24xx-dma-Add-cyclic-transfer-support.patch | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/recipes-kernel/linux/linux-yocto-3.14/h1940/0014-dmaengine-s3c24xx-dma-Add-cyclic-transfer-support.patch b/recipes-kernel/linux/linux-yocto-3.14/h1940/0014-dmaengine-s3c24xx-dma-Add-cyclic-transfer-support.patch new file mode 100644 index 0000000..b3c68ed --- /dev/null +++ b/recipes-kernel/linux/linux-yocto-3.14/h1940/0014-dmaengine-s3c24xx-dma-Add-cyclic-transfer-support.patch @@ -0,0 +1,183 @@ +From a2b08b319caba9b3297efa7d1d48cba4f99281f2 Mon Sep 17 00:00:00 2001 +From: Vasily Khoruzhick <anarsoul@gmail.com> +Date: Sun, 18 May 2014 11:44:51 +0300 +Subject: [PATCH 14/17] dmaengine: s3c24xx-dma: Add cyclic transfer support + +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 + +Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com> +--- + drivers/dma/s3c24xx-dma.c | 112 +++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 111 insertions(+), 1 deletion(-) + +diff --git a/drivers/dma/s3c24xx-dma.c b/drivers/dma/s3c24xx-dma.c +index 2167608..141e220 100644 +--- a/drivers/dma/s3c24xx-dma.c ++++ b/drivers/dma/s3c24xx-dma.c +@@ -164,6 +164,7 @@ struct s3c24xx_sg { + * @disrcc: value for source control register + * @didstc: value for destination control register + * @dcon: base value for dcon register ++ * @cyclic: indicate cyclic transfer + */ + struct s3c24xx_txd { + struct virt_dma_desc vd; +@@ -173,6 +174,7 @@ struct s3c24xx_txd { + u32 disrcc; + u32 didstc; + u32 dcon; ++ bool cyclic; + }; + + struct s3c24xx_dma_chan; +@@ -669,8 +671,10 @@ static irqreturn_t s3c24xx_dma_irq(int irq, void *data) + /* when more sg's are in this txd, start the next one */ + if (!list_is_last(txd->at, &txd->dsg_list)) { + txd->at = txd->at->next; ++ if (txd->cyclic) ++ vchan_cyclic_callback(&txd->vd); + s3c24xx_dma_start_next_sg(s3cchan, txd); +- } else { ++ } else if (!txd->cyclic) { + s3cchan->at = NULL; + vchan_cookie_complete(&txd->vd); + +@@ -682,6 +686,12 @@ static irqreturn_t s3c24xx_dma_irq(int irq, void *data) + s3c24xx_dma_start_next_txd(s3cchan); + else + s3c24xx_dma_phy_free(s3cchan); ++ } else { ++ vchan_cyclic_callback(&txd->vd); ++ ++ /* Cyclic: reset at beginning */ ++ txd->at = txd->dsg_list.next; ++ s3c24xx_dma_start_next_sg(s3cchan, txd); + } + } + spin_unlock(&s3cchan->vc.lock); +@@ -877,6 +887,104 @@ static struct dma_async_tx_descriptor *s3c24xx_dma_prep_memcpy( + return vchan_tx_prep(&s3cchan->vc, &txd->vd, flags); + } + ++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); ++ ++ if (!is_slave_direction(direction)) { ++ dev_err(&s3cdma->pdev->dev, ++ "direction %d unsupported\n", direction); ++ return NULL; ++ } ++ ++ 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 { ++ 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; ++ } ++ ++ 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); ++} ++ + static struct dma_async_tx_descriptor *s3c24xx_dma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, +@@ -1197,6 +1305,7 @@ static int s3c24xx_dma_probe(struct platform_device *pdev) + + /* Initialize slave engine for SoC internal dedicated peripherals */ + dma_cap_set(DMA_SLAVE, s3cdma->slave.cap_mask); ++ dma_cap_set(DMA_CYCLIC, s3cdma->slave.cap_mask); + dma_cap_set(DMA_PRIVATE, s3cdma->slave.cap_mask); + s3cdma->slave.dev = &pdev->dev; + s3cdma->slave.device_alloc_chan_resources = +@@ -1206,6 +1315,7 @@ static int s3c24xx_dma_probe(struct platform_device *pdev) + s3cdma->slave.device_tx_status = s3c24xx_dma_tx_status; + s3cdma->slave.device_issue_pending = s3c24xx_dma_issue_pending; + s3cdma->slave.device_prep_slave_sg = s3c24xx_dma_prep_slave_sg; ++ s3cdma->slave.device_prep_dma_cyclic = s3c24xx_dma_prep_dma_cyclic; + s3cdma->slave.device_control = s3c24xx_dma_control; + + /* Register as many memcpy channels as there are physical channels */ +-- +1.9.3 + |