* [PATCH V1] ARM: tegra: Pause DMA when reading transfer count
@ 2012-01-10 6:05 ` Laxman Dewangan
0 siblings, 0 replies; 8+ messages in thread
From: Laxman Dewangan @ 2012-01-10 6:05 UTC (permalink / raw)
To: ccross-z5hGa2qSFaRBDgjK7y7TUQ, olof-nZhT3qVonbNeoWH0uzbU5w,
swarren-DDmLM1+adcrQT0dZR+AlfA, linux-lFZ/pmaqli7XmaaqVzeoHQ
Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
ldewangan-DDmLM1+adcrQT0dZR+AlfA
From: Laxman Dewangan <ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
In order to read an accurate channel transfer count
from the APB DMA engine, the DMA controller must be
paused first.
Signed-off-by: Laxman Dewangan <ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
arch/arm/mach-tegra/dma.c | 116 ++++++++++++++++++++++++++++----------------
1 files changed, 74 insertions(+), 42 deletions(-)
diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c
index c0cf967..c3529cf 100644
--- a/arch/arm/mach-tegra/dma.c
+++ b/arch/arm/mach-tegra/dma.c
@@ -133,6 +133,7 @@ struct tegra_dma_channel {
static bool tegra_dma_initialized;
static DEFINE_MUTEX(tegra_dma_lock);
+static DEFINE_SPINLOCK(enable_lock);
static DECLARE_BITMAP(channel_usage, NV_DMA_MAX_CHANNELS);
static struct tegra_dma_channel dma_channels[NV_DMA_MAX_CHANNELS];
@@ -198,18 +199,82 @@ static int tegra_dma_cancel(struct tegra_dma_channel *ch)
return 0;
}
+static unsigned int get_channel_status(struct tegra_dma_channel *ch,
+ struct tegra_dma_req *req, bool is_stop_dma)
+{
+ void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE);
+ unsigned int status;
+
+ if (is_stop_dma) {
+ /*
+ * STOP the DMA and get the transfer count.
+ * Getting the transfer count is tricky.
+ * - Globally disable DMA on all channels
+ * - Read the channel's status register to know the number
+ * of pending bytes to be transfered.
+ * - Stop the dma channel
+ * - Globally re-enable DMA to resume other transfers
+ */
+ spin_lock(&enable_lock);
+ writel(0, addr + APB_DMA_GEN);
+ udelay(20);
+ status = readl(ch->addr + APB_DMA_CHAN_STA);
+ tegra_dma_stop(ch);
+ writel(GEN_ENABLE, addr + APB_DMA_GEN);
+ spin_unlock(&enable_lock);
+ if (status & STA_ISE_EOC) {
+ pr_err("Got Dma Int here clearing");
+ writel(status, ch->addr + APB_DMA_CHAN_STA);
+ }
+ req->status = TEGRA_DMA_REQ_ERROR_ABORTED;
+ } else {
+ status = readl(ch->addr + APB_DMA_CHAN_STA);
+ }
+ return status;
+}
+
+/* should be called with the channel lock held */
+static unsigned int dma_active_count(struct tegra_dma_channel *ch,
+ struct tegra_dma_req *req, unsigned int status)
+{
+ unsigned int to_transfer;
+ unsigned int req_transfer_count;
+ unsigned int bytes_transferred;
+
+ to_transfer = ((status & STA_COUNT_MASK) >> STA_COUNT_SHIFT) + 1;
+ req_transfer_count = ch->req_transfer_count + 1;
+ bytes_transferred = req_transfer_count;
+ if (status & STA_BUSY)
+ bytes_transferred -= to_transfer;
+ /*
+ * In continuous transfer mode, DMA only tracks the count of the
+ * half DMA buffer. So, if the DMA already finished half the DMA
+ * then add the half buffer to the completed count.
+ */
+ if (ch->mode & TEGRA_DMA_MODE_CONTINOUS) {
+ if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL)
+ bytes_transferred += req_transfer_count;
+ if (status & STA_ISE_EOC)
+ bytes_transferred += req_transfer_count;
+ }
+ bytes_transferred *= 4;
+ return bytes_transferred;
+}
+
int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
struct tegra_dma_req *_req)
{
- unsigned int csr;
unsigned int status;
struct tegra_dma_req *req = NULL;
int found = 0;
unsigned long irq_flags;
- int to_transfer;
- int req_transfer_count;
+ int stop = 0;
spin_lock_irqsave(&ch->lock, irq_flags);
+
+ if (list_entry(ch->list.next, struct tegra_dma_req, node) == _req)
+ stop = 1;
+
list_for_each_entry(req, &ch->list, node) {
if (req == _req) {
list_del(&req->node);
@@ -222,47 +287,12 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
return 0;
}
- /* STOP the DMA and get the transfer count.
- * Getting the transfer count is tricky.
- * - Change the source selector to invalid to stop the DMA from
- * FIFO to memory.
- * - Read the status register to know the number of pending
- * bytes to be transferred.
- * - Finally stop or program the DMA to the next buffer in the
- * list.
- */
- csr = readl(ch->addr + APB_DMA_CHAN_CSR);
- csr &= ~CSR_REQ_SEL_MASK;
- csr |= CSR_REQ_SEL_INVALID;
- writel(csr, ch->addr + APB_DMA_CHAN_CSR);
-
- /* Get the transfer count */
- status = readl(ch->addr + APB_DMA_CHAN_STA);
- to_transfer = (status & STA_COUNT_MASK) >> STA_COUNT_SHIFT;
- req_transfer_count = ch->req_transfer_count;
- req_transfer_count += 1;
- to_transfer += 1;
+ if (!stop)
+ goto skip_stop_dma;
- req->bytes_transferred = req_transfer_count;
+ status = get_channel_status(ch, req, true);
+ req->bytes_transferred = dma_active_count(ch, req, status);
- if (status & STA_BUSY)
- req->bytes_transferred -= to_transfer;
-
- /* In continuous transfer mode, DMA only tracks the count of the
- * half DMA buffer. So, if the DMA already finished half the DMA
- * then add the half buffer to the completed count.
- *
- * FIXME: There can be a race here. What if the req to
- * dequue happens at the same time as the DMA just moved to
- * the new buffer and SW didn't yet received the interrupt?
- */
- if (ch->mode & TEGRA_DMA_MODE_CONTINOUS)
- if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL)
- req->bytes_transferred += req_transfer_count;
-
- req->bytes_transferred *= 4;
-
- tegra_dma_stop(ch);
if (!list_empty(&ch->list)) {
/* if the list is not empty, queue the next request */
struct tegra_dma_req *next_req;
@@ -270,6 +300,8 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
typeof(*next_req), node);
tegra_dma_update_hw(ch, next_req);
}
+
+skip_stop_dma:
req->status = -TEGRA_DMA_REQ_ERROR_ABORTED;
spin_unlock_irqrestore(&ch->lock, irq_flags);
--
1.7.1.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH V1] ARM: tegra: Pause DMA when reading transfer count
@ 2012-01-10 6:05 ` Laxman Dewangan
0 siblings, 0 replies; 8+ messages in thread
From: Laxman Dewangan @ 2012-01-10 6:05 UTC (permalink / raw)
To: ccross, olof, swarren, linux
Cc: linux-tegra, linux-arm-kernel, linux-kernel, ldewangan
From: Laxman Dewangan <ldewangan@nvidia.com>
In order to read an accurate channel transfer count
from the APB DMA engine, the DMA controller must be
paused first.
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
---
arch/arm/mach-tegra/dma.c | 116 ++++++++++++++++++++++++++++----------------
1 files changed, 74 insertions(+), 42 deletions(-)
diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c
index c0cf967..c3529cf 100644
--- a/arch/arm/mach-tegra/dma.c
+++ b/arch/arm/mach-tegra/dma.c
@@ -133,6 +133,7 @@ struct tegra_dma_channel {
static bool tegra_dma_initialized;
static DEFINE_MUTEX(tegra_dma_lock);
+static DEFINE_SPINLOCK(enable_lock);
static DECLARE_BITMAP(channel_usage, NV_DMA_MAX_CHANNELS);
static struct tegra_dma_channel dma_channels[NV_DMA_MAX_CHANNELS];
@@ -198,18 +199,82 @@ static int tegra_dma_cancel(struct tegra_dma_channel *ch)
return 0;
}
+static unsigned int get_channel_status(struct tegra_dma_channel *ch,
+ struct tegra_dma_req *req, bool is_stop_dma)
+{
+ void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE);
+ unsigned int status;
+
+ if (is_stop_dma) {
+ /*
+ * STOP the DMA and get the transfer count.
+ * Getting the transfer count is tricky.
+ * - Globally disable DMA on all channels
+ * - Read the channel's status register to know the number
+ * of pending bytes to be transfered.
+ * - Stop the dma channel
+ * - Globally re-enable DMA to resume other transfers
+ */
+ spin_lock(&enable_lock);
+ writel(0, addr + APB_DMA_GEN);
+ udelay(20);
+ status = readl(ch->addr + APB_DMA_CHAN_STA);
+ tegra_dma_stop(ch);
+ writel(GEN_ENABLE, addr + APB_DMA_GEN);
+ spin_unlock(&enable_lock);
+ if (status & STA_ISE_EOC) {
+ pr_err("Got Dma Int here clearing");
+ writel(status, ch->addr + APB_DMA_CHAN_STA);
+ }
+ req->status = TEGRA_DMA_REQ_ERROR_ABORTED;
+ } else {
+ status = readl(ch->addr + APB_DMA_CHAN_STA);
+ }
+ return status;
+}
+
+/* should be called with the channel lock held */
+static unsigned int dma_active_count(struct tegra_dma_channel *ch,
+ struct tegra_dma_req *req, unsigned int status)
+{
+ unsigned int to_transfer;
+ unsigned int req_transfer_count;
+ unsigned int bytes_transferred;
+
+ to_transfer = ((status & STA_COUNT_MASK) >> STA_COUNT_SHIFT) + 1;
+ req_transfer_count = ch->req_transfer_count + 1;
+ bytes_transferred = req_transfer_count;
+ if (status & STA_BUSY)
+ bytes_transferred -= to_transfer;
+ /*
+ * In continuous transfer mode, DMA only tracks the count of the
+ * half DMA buffer. So, if the DMA already finished half the DMA
+ * then add the half buffer to the completed count.
+ */
+ if (ch->mode & TEGRA_DMA_MODE_CONTINOUS) {
+ if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL)
+ bytes_transferred += req_transfer_count;
+ if (status & STA_ISE_EOC)
+ bytes_transferred += req_transfer_count;
+ }
+ bytes_transferred *= 4;
+ return bytes_transferred;
+}
+
int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
struct tegra_dma_req *_req)
{
- unsigned int csr;
unsigned int status;
struct tegra_dma_req *req = NULL;
int found = 0;
unsigned long irq_flags;
- int to_transfer;
- int req_transfer_count;
+ int stop = 0;
spin_lock_irqsave(&ch->lock, irq_flags);
+
+ if (list_entry(ch->list.next, struct tegra_dma_req, node) == _req)
+ stop = 1;
+
list_for_each_entry(req, &ch->list, node) {
if (req == _req) {
list_del(&req->node);
@@ -222,47 +287,12 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
return 0;
}
- /* STOP the DMA and get the transfer count.
- * Getting the transfer count is tricky.
- * - Change the source selector to invalid to stop the DMA from
- * FIFO to memory.
- * - Read the status register to know the number of pending
- * bytes to be transferred.
- * - Finally stop or program the DMA to the next buffer in the
- * list.
- */
- csr = readl(ch->addr + APB_DMA_CHAN_CSR);
- csr &= ~CSR_REQ_SEL_MASK;
- csr |= CSR_REQ_SEL_INVALID;
- writel(csr, ch->addr + APB_DMA_CHAN_CSR);
-
- /* Get the transfer count */
- status = readl(ch->addr + APB_DMA_CHAN_STA);
- to_transfer = (status & STA_COUNT_MASK) >> STA_COUNT_SHIFT;
- req_transfer_count = ch->req_transfer_count;
- req_transfer_count += 1;
- to_transfer += 1;
+ if (!stop)
+ goto skip_stop_dma;
- req->bytes_transferred = req_transfer_count;
+ status = get_channel_status(ch, req, true);
+ req->bytes_transferred = dma_active_count(ch, req, status);
- if (status & STA_BUSY)
- req->bytes_transferred -= to_transfer;
-
- /* In continuous transfer mode, DMA only tracks the count of the
- * half DMA buffer. So, if the DMA already finished half the DMA
- * then add the half buffer to the completed count.
- *
- * FIXME: There can be a race here. What if the req to
- * dequue happens at the same time as the DMA just moved to
- * the new buffer and SW didn't yet received the interrupt?
- */
- if (ch->mode & TEGRA_DMA_MODE_CONTINOUS)
- if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL)
- req->bytes_transferred += req_transfer_count;
-
- req->bytes_transferred *= 4;
-
- tegra_dma_stop(ch);
if (!list_empty(&ch->list)) {
/* if the list is not empty, queue the next request */
struct tegra_dma_req *next_req;
@@ -270,6 +300,8 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
typeof(*next_req), node);
tegra_dma_update_hw(ch, next_req);
}
+
+skip_stop_dma:
req->status = -TEGRA_DMA_REQ_ERROR_ABORTED;
spin_unlock_irqrestore(&ch->lock, irq_flags);
--
1.7.1.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* RE: [PATCH V1] ARM: tegra: Pause DMA when reading transfer count
2012-01-10 6:05 ` Laxman Dewangan
(?)
@ 2012-01-11 20:48 ` Stephen Warren
-1 siblings, 0 replies; 8+ messages in thread
From: Stephen Warren @ 2012-01-11 20:48 UTC (permalink / raw)
To: Laxman Dewangan, ccross-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org,
olof-nZhT3qVonbNeoWH0uzbU5w@public.gmane.org,
linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org
Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Laxman Dewangan
Laxman Dewangan wrote at Monday, January 09, 2012 11:05 PM:
> In order to read an accurate channel transfer count
> from the APB DMA engine, the DMA controller must be
> paused first.
>
> Signed-off-by: Laxman Dewangan <ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Acked-by: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Tested-by: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
(Audio playback on Harmony booted using device tree works fine in my
brief testing)
--
nvpublic
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH V1] ARM: tegra: Pause DMA when reading transfer count
@ 2012-01-11 20:48 ` Stephen Warren
0 siblings, 0 replies; 8+ messages in thread
From: Stephen Warren @ 2012-01-11 20:48 UTC (permalink / raw)
To: linux-arm-kernel
Laxman Dewangan wrote at Monday, January 09, 2012 11:05 PM:
> In order to read an accurate channel transfer count
> from the APB DMA engine, the DMA controller must be
> paused first.
>
> Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Acked-by: Stephen Warren <swarren@nvidia.com>
Tested-by: Stephen Warren <swarren@nvidia.com>
(Audio playback on Harmony booted using device tree works fine in my
brief testing)
--
nvpublic
^ permalink raw reply [flat|nested] 8+ messages in thread
* RE: [PATCH V1] ARM: tegra: Pause DMA when reading transfer count
@ 2012-01-11 20:48 ` Stephen Warren
0 siblings, 0 replies; 8+ messages in thread
From: Stephen Warren @ 2012-01-11 20:48 UTC (permalink / raw)
To: Laxman Dewangan, ccross@android.com, olof@lixom.net,
linux@arm.linux.org.uk
Cc: linux-tegra@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Laxman Dewangan
Laxman Dewangan wrote at Monday, January 09, 2012 11:05 PM:
> In order to read an accurate channel transfer count
> from the APB DMA engine, the DMA controller must be
> paused first.
>
> Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Acked-by: Stephen Warren <swarren@nvidia.com>
Tested-by: Stephen Warren <swarren@nvidia.com>
(Audio playback on Harmony booted using device tree works fine in my
brief testing)
--
nvpublic
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH V1] ARM: tegra: Pause DMA when reading transfer count
2012-01-11 20:48 ` Stephen Warren
(?)
@ 2012-01-19 3:48 ` Olof Johansson
-1 siblings, 0 replies; 8+ messages in thread
From: Olof Johansson @ 2012-01-19 3:48 UTC (permalink / raw)
To: Stephen Warren
Cc: Laxman Dewangan, ccross-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org,
linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org,
linux-tegra-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Laxman Dewangan
On Wed, Jan 11, 2012 at 12:48:26PM -0800, Stephen Warren wrote:
> Laxman Dewangan wrote at Monday, January 09, 2012 11:05 PM:
> > In order to read an accurate channel transfer count
> > from the APB DMA engine, the DMA controller must be
> > paused first.
> >
> > Signed-off-by: Laxman Dewangan <ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>
> Acked-by: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> Tested-by: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>
> (Audio playback on Harmony booted using device tree works fine in my
> brief testing)
Thanks, applied.
-Olof
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH V1] ARM: tegra: Pause DMA when reading transfer count
@ 2012-01-19 3:48 ` Olof Johansson
0 siblings, 0 replies; 8+ messages in thread
From: Olof Johansson @ 2012-01-19 3:48 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, Jan 11, 2012 at 12:48:26PM -0800, Stephen Warren wrote:
> Laxman Dewangan wrote at Monday, January 09, 2012 11:05 PM:
> > In order to read an accurate channel transfer count
> > from the APB DMA engine, the DMA controller must be
> > paused first.
> >
> > Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
>
> Acked-by: Stephen Warren <swarren@nvidia.com>
> Tested-by: Stephen Warren <swarren@nvidia.com>
>
> (Audio playback on Harmony booted using device tree works fine in my
> brief testing)
Thanks, applied.
-Olof
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH V1] ARM: tegra: Pause DMA when reading transfer count
@ 2012-01-19 3:48 ` Olof Johansson
0 siblings, 0 replies; 8+ messages in thread
From: Olof Johansson @ 2012-01-19 3:48 UTC (permalink / raw)
To: Stephen Warren
Cc: Laxman Dewangan, ccross@android.com, linux@arm.linux.org.uk,
linux-tegra@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Laxman Dewangan
On Wed, Jan 11, 2012 at 12:48:26PM -0800, Stephen Warren wrote:
> Laxman Dewangan wrote at Monday, January 09, 2012 11:05 PM:
> > In order to read an accurate channel transfer count
> > from the APB DMA engine, the DMA controller must be
> > paused first.
> >
> > Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
>
> Acked-by: Stephen Warren <swarren@nvidia.com>
> Tested-by: Stephen Warren <swarren@nvidia.com>
>
> (Audio playback on Harmony booted using device tree works fine in my
> brief testing)
Thanks, applied.
-Olof
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2012-01-19 3:48 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-01-10 6:05 [PATCH V1] ARM: tegra: Pause DMA when reading transfer count Laxman Dewangan
2012-01-10 6:05 ` Laxman Dewangan
[not found] ` <1326175511-28642-1-git-send-email-ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2012-01-11 20:48 ` Stephen Warren
2012-01-11 20:48 ` Stephen Warren
2012-01-11 20:48 ` Stephen Warren
[not found] ` <74CDBE0F657A3D45AFBB94109FB122FF177EE3A77E-C7FfzLzN0UxDw2glCA4ptUEOCMrvLtNR@public.gmane.org>
2012-01-19 3:48 ` Olof Johansson
2012-01-19 3:48 ` Olof Johansson
2012-01-19 3:48 ` Olof Johansson
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.