summaryrefslogtreecommitdiff
path: root/drivers/dma
diff options
context:
space:
mode:
authorHuang Chao <chao7.huang@samsung.com>2014-06-24 13:01:46 +0800
committerChanho Park <chanho61.park@samsung.com>2014-11-18 11:59:55 +0900
commit3fcbdb162c1081f2641981f54b2938a61e23f456 (patch)
treeafdf077474b9dd718853a50b337983ed5037b9db /drivers/dma
parent2fc5a29f7e7504541807ef18dfbe6a6dbb6dea64 (diff)
downloadlinux-3.10-3fcbdb162c1081f2641981f54b2938a61e23f456.tar.gz
linux-3.10-3fcbdb162c1081f2641981f54b2938a61e23f456.tar.bz2
linux-3.10-3fcbdb162c1081f2641981f54b2938a61e23f456.zip
dma: pl330: Implement the DMA channel PAUSE/RESUME interfaces
This patch implements the DMA_PAUSE/DMA_RESUME interfaces in pl330 DMAC driver, which will call the dma pause/resume operations to set the dma channel status respectively. When DMA_PAUSE is called from dma subsystem during audio playback, the DMAC should halt the transfer execution of the channel thread and wait for the resume event occurs, which is specified by the event number in INTEN register. The channel thread is stalled by the DMAWFE instruction. After that, when DMA_RESUME is called, which means that the resume event occurs, this patch will signal an interrupt to send the DMASEV instruction and restart the channel thread, and then the channel thread moves to the Executing state and the DMAC clears the event. Change-Id: I5d990f0efb31b7e37673a060fdcbc9002b6ea488 Signed-off-by: Huang Chao <chao7.huang@samsung.com>
Diffstat (limited to 'drivers/dma')
-rw-r--r--drivers/dma/pl330.c61
1 files changed, 59 insertions, 2 deletions
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index cd132f3d06a..d7da512f96f 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -1239,6 +1239,49 @@ static bool _start(struct pl330_thread *thrd)
}
}
+static bool _pause(struct pl330_thread *thrd)
+{
+ void __iomem *regs = thrd->dmac->pinfo->base;
+ u8 insn[6] = {0, 0, 0, 0, 0, 0};
+
+ if (_state(thrd) == PL330_STATE_FAULT_COMPLETING)
+ UNTIL(thrd, PL330_STATE_FAULTING | PL330_STATE_KILLING);
+
+ /* Return false if dma channel has fault */
+ if (_state(thrd) == PL330_STATE_COMPLETING ||
+ _state(thrd) == PL330_STATE_KILLING ||
+ _state(thrd) == PL330_STATE_FAULTING)
+ return false;
+
+ _emit_WFE(0, insn, thrd->ev, 1);
+
+ /* Stop generating interrupts for SEV */
+ writel(readl(regs + INTEN) & ~(1 << thrd->ev), regs + INTEN);
+
+ _execute_DBGINSN(thrd, insn, is_manager(thrd));
+
+ return true;
+}
+
+static bool _resume(struct pl330_thread *thrd)
+{
+ void __iomem *regs = thrd->dmac->pinfo->base;
+
+ if (_state(thrd) == PL330_STATE_FAULT_COMPLETING)
+ UNTIL(thrd, PL330_STATE_FAULTING | PL330_STATE_KILLING);
+
+ /* Return false if dma channel has fault */
+ if (_state(thrd) == PL330_STATE_COMPLETING ||
+ _state(thrd) == PL330_STATE_KILLING ||
+ _state(thrd) == PL330_STATE_FAULTING)
+ return false;
+
+ /* Start generating interrupts for SEV */
+ writel(readl(regs + INTEN) | (1 << thrd->ev), regs + INTEN);
+
+ return true;
+}
+
static inline int _ldst_memtomem(unsigned dry_run, u8 buf[],
const struct _xfer_spec *pxs, int cyc)
{
@@ -1824,6 +1867,16 @@ static int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op)
ret = -EIO;
break;
+ case PL330_OP_PAUSE:
+ if ((active != -1) && !_pause(thrd))
+ ret = -EIO;
+ break;
+
+ case PL330_OP_RESUME:
+ if ((active != -1) && !_resume(thrd))
+ ret = -EIO;
+ break;
+
default:
ret = -EINVAL;
}
@@ -2421,10 +2474,14 @@ static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned
}
break;
case DMA_PAUSE:
- /* TODO: set up dma channel and config register if any */
+ spin_lock_irqsave(&pch->lock, flags);
+ pl330_chan_ctrl(pch->pl330_chid, PL330_OP_PAUSE);
+ spin_unlock_irqrestore(&pch->lock, flags);
break;
case DMA_RESUME:
- /* TODO: set up dma channel and config register if any */
+ spin_lock_irqsave(&pch->lock, flags);
+ pl330_chan_ctrl(pch->pl330_chid, PL330_OP_RESUME);
+ spin_unlock_irqrestore(&pch->lock, flags);
break;
default:
dev_err(pch->dmac->pif.dev, "Not supported command.\n");