aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-kernel/linux/linux-yocto-3.14/h1940/0014-dmaengine-s3c24xx-dma-Add-cyclic-transfer-support.patch
diff options
context:
space:
mode:
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.patch183
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
+