summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/dma.c98
-rw-r--r--arch/arm/mach-tegra/include/mach/dma.h6
2 files changed, 57 insertions, 47 deletions
diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c
index 2e00d8234d2d..68a7876f82f6 100644
--- a/arch/arm/mach-tegra/dma.c
+++ b/arch/arm/mach-tegra/dma.c
@@ -127,6 +127,8 @@ struct tegra_dma_channel {
void __iomem *addr;
int mode;
int irq;
+ dma_callback callback;
+ struct tegra_dma_req *cb_req;
};
#define NV_DMA_MAX_CHANNELS 32
@@ -717,13 +719,12 @@ static void tegra_dma_update_hw(struct tegra_dma_channel *ch,
req->status = TEGRA_DMA_REQ_INFLIGHT;
}
-static bool handle_oneshot_dma(struct tegra_dma_channel *ch,
- unsigned long *irq_flags)
+static void handle_oneshot_dma(struct tegra_dma_channel *ch)
{
struct tegra_dma_req *req;
if (list_empty(&ch->list))
- return true;
+ return;
req = list_entry(ch->list.next, typeof(*req), node);
if (req) {
@@ -731,36 +732,28 @@ static bool handle_oneshot_dma(struct tegra_dma_channel *ch,
req->bytes_transferred = req->size;
req->status = TEGRA_DMA_REQ_SUCCESS;
- spin_unlock_irqrestore(&ch->lock, *irq_flags);
- /* Callback should be called without any lock */
- pr_debug("%s: transferred %d bytes\n", __func__,
- req->bytes_transferred);
- req->complete(req);
- spin_lock_irqsave(&ch->lock, *irq_flags);
+ ch->callback = req->complete;
+ ch->cb_req = req;
}
if (!list_empty(&ch->list)) {
req = list_entry(ch->list.next, typeof(*req), node);
- /* the complete function we just called may have enqueued
- another req, in which case dma has already started */
- if (req->status != TEGRA_DMA_REQ_INFLIGHT)
- tegra_dma_update_hw(ch, req);
+ tegra_dma_update_hw(ch, req);
}
- return true;
+ return;
}
-static bool handle_continuous_dbl_dma(struct tegra_dma_channel *ch,
- unsigned long *irq_flags)
+static void handle_continuous_dbl_dma(struct tegra_dma_channel *ch)
{
struct tegra_dma_req *req;
struct tegra_dma_req *next_req;
if (list_empty(&ch->list))
- return true;
+ return;
req = list_entry(ch->list.next, typeof(*req), node);
if (!req)
- return true;
+ return;
if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_EMPTY) {
bool is_dma_ping_complete;
@@ -786,10 +779,9 @@ static bool handle_continuous_dbl_dma(struct tegra_dma_channel *ch,
list_del(&req->node);
- /* DMA lock is NOT held when callbak is called */
- spin_unlock_irqrestore(&ch->lock, *irq_flags);
- req->complete(req);
- return false;
+ ch->callback = req->complete;
+ ch->cb_req = req;
+ return;
}
/* Load the next request into the hardware, if available */
if (!list_is_last(&req->node, &ch->list)) {
@@ -799,11 +791,10 @@ static bool handle_continuous_dbl_dma(struct tegra_dma_channel *ch,
}
req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL;
req->bytes_transferred = req->size >> 1;
- /* DMA lock is NOT held when callback is called */
- spin_unlock_irqrestore(&ch->lock, *irq_flags);
- if (likely(req->threshold))
- req->threshold(req);
- return false;
+
+ ch->callback = req->threshold;
+ ch->cb_req = req;
+ return;
}
if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL) {
@@ -832,19 +823,17 @@ static bool handle_continuous_dbl_dma(struct tegra_dma_channel *ch,
list_del(&req->node);
- /* DMA lock is NOT held when callbak is called */
- spin_unlock_irqrestore(&ch->lock, *irq_flags);
- req->complete(req);
- return false;
+ ch->callback = req->complete;
+ ch->cb_req = req;
+ return;
}
tegra_dma_stop(ch);
/* Dma should be stop much earlier */
BUG();
- return true;
+ return;
}
-static bool handle_continuous_sngl_dma(struct tegra_dma_channel *ch,
- unsigned long *irq_flags)
+static void handle_continuous_sngl_dma(struct tegra_dma_channel *ch)
{
struct tegra_dma_req *req;
struct tegra_dma_req *next_req;
@@ -853,14 +842,14 @@ static bool handle_continuous_sngl_dma(struct tegra_dma_channel *ch,
if (list_empty(&ch->list)) {
tegra_dma_stop(ch);
pr_err("%s: No requests in the list.\n", __func__);
- return true;
+ return;
}
req = list_entry(ch->list.next, typeof(*req), node);
if (!req || (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_FULL)) {
tegra_dma_stop(ch);
pr_err("%s: DMA complete irq without corresponding req\n",
__func__);
- return true;
+ return;
}
/* Handle the case when buffer is completely full */
@@ -888,9 +877,10 @@ static bool handle_continuous_sngl_dma(struct tegra_dma_channel *ch,
}
}
list_del(&req->node);
- spin_unlock_irqrestore(&ch->lock, *irq_flags);
- req->complete(req);
- return false;
+
+ ch->callback = req->complete;
+ ch->cb_req = req;
+ return;
}
static irqreturn_t dma_isr(int irq, void *data)
@@ -898,27 +888,45 @@ static irqreturn_t dma_isr(int irq, void *data)
struct tegra_dma_channel *ch = data;
unsigned long irq_flags;
unsigned long status;
- bool unlock = true;
+ dma_callback callback = NULL;
+ struct tegra_dma_req *cb_req = NULL;
spin_lock_irqsave(&ch->lock, irq_flags);
+ /*
+ * Calbacks should be set and cleared while holding the spinlock,
+ * never left set
+ */
+ if (ch->callback || ch->cb_req)
+ pr_err("%s():"
+ "Channel %d callbacks are not initialized properly\n",
+ __func__, ch->id);
+ BUG_ON(ch->callback || ch->cb_req);
+
status = readl(ch->addr + APB_DMA_CHAN_STA);
if (status & STA_ISE_EOC) {
/* Clear dma int status */
writel(status, ch->addr + APB_DMA_CHAN_STA);
if (ch->mode & TEGRA_DMA_MODE_ONESHOT)
- unlock = handle_oneshot_dma(ch, &irq_flags);
+ handle_oneshot_dma(ch);
else if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS_DOUBLE)
- unlock = handle_continuous_dbl_dma(ch, &irq_flags);
+ handle_continuous_dbl_dma(ch);
else if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS_SINGLE)
- unlock = handle_continuous_sngl_dma(ch, &irq_flags);
+ handle_continuous_sngl_dma(ch);
else
pr_err("Bad channel mode for DMA ISR to handle\n");
+ callback = ch->callback;
+ cb_req = ch->cb_req;
+ ch->callback = NULL;
+ ch->cb_req = NULL;
} else
pr_info("Interrupt is already handled %d\n", ch->id);
- if (unlock)
- spin_unlock_irqrestore(&ch->lock, irq_flags);
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+
+ /* Call callback function to notify client if it is there */
+ if (callback)
+ callback(cb_req);
return IRQ_HANDLED;
}
diff --git a/arch/arm/mach-tegra/include/mach/dma.h b/arch/arm/mach-tegra/include/mach/dma.h
index 920381f836c2..580d50fd1565 100644
--- a/arch/arm/mach-tegra/include/mach/dma.h
+++ b/arch/arm/mach-tegra/include/mach/dma.h
@@ -86,6 +86,8 @@ enum tegra_dma_req_buff_status {
TEGRA_DMA_REQ_BUF_STATUS_FULL,
};
+typedef void (*dma_callback)(struct tegra_dma_req *req);
+
struct tegra_dma_req {
struct list_head node;
unsigned int modid;
@@ -99,7 +101,7 @@ struct tegra_dma_req {
* no DMA requests queued up, then it will STOP the DMA. It there are
* more requests in the DMA, then it will queue the next request.
*/
- void (*complete)(struct tegra_dma_req *req);
+ dma_callback complete;
/* This is a called from the DMA ISR context when the DMA is still in
* progress and is actively filling same buffer.
@@ -118,7 +120,7 @@ struct tegra_dma_req {
* callback to program the next buffer.
*
*/
- void (*threshold)(struct tegra_dma_req *req);
+ dma_callback threshold;
/* 1 to copy to memory.
* 0 to copy from the memory to device FIFO */