[PATCH v7 3/3] dmaengine: zxdma: Support cyclic dma

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Support cyclic dma for audio playback

Signed-off-by: Jun Nie <jun.nie@xxxxxxxxxx>
---
 drivers/dma/zx296702_dma.c | 93 +++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 84 insertions(+), 9 deletions(-)

diff --git a/drivers/dma/zx296702_dma.c b/drivers/dma/zx296702_dma.c
index 3939277..18a1561 100644
--- a/drivers/dma/zx296702_dma.c
+++ b/drivers/dma/zx296702_dma.c
@@ -101,6 +101,7 @@ struct zx_dma_chan {
 	struct dma_slave_config slave_cfg;
 	int			id; /* Request phy chan id */
 	u32			ccfg;
+	u32			cyclic;
 	struct virt_dma_chan	vc;
 	struct zx_dma_phy	*phy;
 	struct list_head	node;
@@ -278,7 +279,7 @@ static irqreturn_t zx_dma_int_handler(int irq, void *dev_id)
 	u32 serr = readl_relaxed(d->base + REG_ZX_SRC_ERR_IRQ);
 	u32 derr = readl_relaxed(d->base + REG_ZX_DST_ERR_IRQ);
 	u32 cfg = readl_relaxed(d->base + REG_ZX_CFG_ERR_IRQ);
-	u32 i, irq_chan = 0;
+	u32 i, irq_chan = 0, task = 0;
 
 	while (tc) {
 		i = __ffs(tc);
@@ -289,11 +290,16 @@ static irqreturn_t zx_dma_int_handler(int irq, void *dev_id)
 			unsigned long flags;
 
 			spin_lock_irqsave(&c->vc.lock, flags);
-			vchan_cookie_complete(&p->ds_run->vd);
-			p->ds_done = p->ds_run;
+			if (c->cyclic) {
+				vchan_cyclic_callback(&p->ds_run->vd);
+			} else {
+				vchan_cookie_complete(&p->ds_run->vd);
+				p->ds_done = p->ds_run;
+				task = 1;
+			}
 			spin_unlock_irqrestore(&c->vc.lock, flags);
+			irq_chan |= BIT(i);
 		}
-		irq_chan |= BIT(i);
 	}
 
 	if (serr || derr || cfg)
@@ -305,12 +311,9 @@ static irqreturn_t zx_dma_int_handler(int irq, void *dev_id)
 	writel_relaxed(derr, d->base + REG_ZX_DST_ERR_IRQ_RAW);
 	writel_relaxed(cfg, d->base + REG_ZX_CFG_ERR_IRQ_RAW);
 
-	if (irq_chan) {
+	if (task)
 		zx_dma_task(d);
-		return IRQ_HANDLED;
-	} else {
-		return IRQ_NONE;
-	}
+	return IRQ_HANDLED;
 }
 
 static void zx_dma_free_chan_resources(struct dma_chan *chan)
@@ -533,6 +536,7 @@ static struct dma_async_tx_descriptor *zx_dma_prep_memcpy(
 		len -= copy;
 	} while (len);
 
+	c->cyclic = 0;
 	ds->desc_hw[num - 1].lli = 0;	/* end of link */
 	ds->desc_hw[num - 1].ctr |= ZX_IRQ_ENABLE_ALL;
 	return vchan_tx_prep(&c->vc, &ds->vd, flags);
@@ -565,6 +569,7 @@ static struct dma_async_tx_descriptor *zx_dma_prep_slave_sg(
 	if (!ds)
 		return NULL;
 
+	c->cyclic = 0;
 	num = 0;
 	for_each_sg(sgl, sg, sglen, i) {
 		addr = sg_dma_address(sg);
@@ -595,6 +600,49 @@ static struct dma_async_tx_descriptor *zx_dma_prep_slave_sg(
 	return vchan_tx_prep(&c->vc, &ds->vd, flags);
 }
 
+static struct dma_async_tx_descriptor *zx_dma_prep_dma_cyclic(
+		struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
+		size_t period_len, enum dma_transfer_direction dir,
+		unsigned long flags)
+{
+	struct zx_dma_chan *c = to_zx_chan(chan);
+	struct zx_dma_desc_sw *ds;
+	dma_addr_t src = 0, dst = 0;
+	int num_periods = buf_len / period_len;
+	int buf = 0, num = 0;
+
+	if (period_len > DMA_MAX_SIZE) {
+		dev_err(chan->device->dev, "maximum period size exceeded\n");
+		return NULL;
+	}
+
+	if (zx_pre_config(c, dir))
+		return NULL;
+
+	ds = zx_alloc_desc_resource(num_periods, chan);
+	if (!ds)
+		return NULL;
+	c->cyclic = 1;
+
+	while (buf < buf_len) {
+		if (dir == DMA_MEM_TO_DEV) {
+			src = dma_addr;
+			dst = c->dev_addr;
+		} else if (dir == DMA_DEV_TO_MEM) {
+			src = c->dev_addr;
+			dst = dma_addr;
+		}
+		zx_dma_fill_desc(ds, dst, src, period_len, num++,
+				 c->ccfg | ZX_IRQ_ENABLE_ALL);
+		dma_addr += period_len;
+		buf += period_len;
+	}
+
+	ds->desc_hw[num - 1].lli = ds->desc_hw_lli;
+	ds->size = buf_len;
+	return vchan_tx_prep(&c->vc, &ds->vd, flags);
+}
+
 static int zx_dma_config(struct dma_chan *chan,
 			 struct dma_slave_config *cfg)
 {
@@ -640,6 +688,30 @@ static int zx_dma_terminate_all(struct dma_chan *chan)
 	return 0;
 }
 
+static int zx_dma_transfer_pause(struct dma_chan *chan)
+{
+	struct zx_dma_chan *c = to_zx_chan(chan);
+	u32 val = 0;
+
+	val = readl_relaxed(c->phy->base + REG_ZX_CTRL);
+	val &= ~ZX_CH_ENABLE;
+	writel_relaxed(val, c->phy->base + REG_ZX_CTRL);
+
+	return 0;
+}
+
+static int zx_dma_transfer_resume(struct dma_chan *chan)
+{
+	struct zx_dma_chan *c = to_zx_chan(chan);
+	u32 val = 0;
+
+	val = readl_relaxed(c->phy->base + REG_ZX_CTRL);
+	val |= ZX_CH_ENABLE;
+	writel_relaxed(val, c->phy->base + REG_ZX_CTRL);
+
+	return 0;
+}
+
 static void zx_dma_free_desc(struct virt_dma_desc *vd)
 {
 	struct zx_dma_desc_sw *ds =
@@ -744,9 +816,12 @@ static int zx_dma_probe(struct platform_device *op)
 	d->slave.device_tx_status = zx_dma_tx_status;
 	d->slave.device_prep_dma_memcpy = zx_dma_prep_memcpy;
 	d->slave.device_prep_slave_sg = zx_dma_prep_slave_sg;
+	d->slave.device_prep_dma_cyclic = zx_dma_prep_dma_cyclic;
 	d->slave.device_issue_pending = zx_dma_issue_pending;
 	d->slave.device_config = zx_dma_config;
 	d->slave.device_terminate_all = zx_dma_terminate_all;
+	d->slave.device_pause = zx_dma_transfer_pause;
+	d->slave.device_resume = zx_dma_transfer_resume;
 	d->slave.copy_align = DMA_ALIGN;
 	d->slave.src_addr_widths = ZX_DMA_BUSWIDTHS;
 	d->slave.dst_addr_widths = ZX_DMA_BUSWIDTHS;
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux PCI]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux