diff options
Diffstat (limited to 'arch/arm/mach-tegra/dma.c')
-rw-r--r-- | arch/arm/mach-tegra/dma.c | 98 |
1 files changed, 61 insertions, 37 deletions
diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c index f882d4293bec..35499916e2bd 100644 --- a/arch/arm/mach-tegra/dma.c +++ b/arch/arm/mach-tegra/dma.c @@ -237,6 +237,62 @@ static inline unsigned int get_req_xfer_word_count( return req->size >> 2; } +static int get_current_xferred_count(struct tegra_dma_channel *ch, + struct tegra_dma_req *req, unsigned long status) +{ + int req_transfer_count; + req_transfer_count = get_req_xfer_word_count(ch, req) << 2; + return req_transfer_count - ((status & STA_COUNT_MASK) + 4); +} + +static void tegra_dma_abort_req(struct tegra_dma_channel *ch, + struct tegra_dma_req *req, const char *warn_msg) +{ + unsigned long status = readl(ch->addr + APB_DMA_CHAN_STA); + + /* + * Check if interrupt is pending. + * This api is called from isr and hence need not to call + * isr handle again, just update the byte_transferred. + */ + if (status & STA_ISE_EOC) + req->bytes_transferred += get_req_xfer_word_count(ch, req) << 2; + tegra_dma_stop(ch); + + req->bytes_transferred += get_current_xferred_count(ch, req, status); + req->status = -TEGRA_DMA_REQ_ERROR_STOPPED; + if (warn_msg) + WARN(1, KERN_WARNING "%s\n", warn_msg); + start_head_req(ch); +} + +static void handle_continuous_head_request(struct tegra_dma_channel *ch, + struct tegra_dma_req *last_req) +{ + struct tegra_dma_req *hreq = NULL; + + if (list_empty(&ch->list)) { + tegra_dma_abort_req(ch, last_req, NULL); + return; + } + + /* + * Check that head req on list should be in flight. + * If it is not in flight then request came late + * and so need to abort dma and start next request + * immediately. + */ + hreq = list_entry(ch->list.next, typeof(*hreq), node); + if (hreq->status != TEGRA_DMA_REQ_INFLIGHT) { + tegra_dma_abort_req(ch, last_req, "Req was not queued on time"); + return; + } + + /* Configure next request in single buffer mode */ + if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS_SINGLE) + configure_next_req(ch, hreq); +} + static unsigned int get_channel_status(struct tegra_dma_channel *ch, struct tegra_dma_req *req, bool is_stop_dma) { @@ -807,17 +863,14 @@ static void handle_continuous_dbl_dma(struct tegra_dma_channel *ch) * We should not land here if queue mechanism * with system latency are properly configured. */ - WARN_ON(1); - req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_FULL; req->bytes_transferred += req->size; - req->status = TEGRA_DMA_REQ_SUCCESS; - tegra_dma_stop(ch); list_del(&req->node); ch->callback = req->complete; ch->cb_req = req; - start_head_req(ch); + tegra_dma_abort_req(ch, req, + "Dma becomes out of sync for ping-pong buffer"); return; } @@ -846,24 +899,10 @@ static void handle_continuous_dbl_dma(struct tegra_dma_channel *ch) ch->callback = req->complete; ch->cb_req = req; - if (list_empty(&ch->list)) { - tegra_dma_stop(ch); - } else { - /* Check that next req on list should be in flight */ - req = list_entry(ch->list.next, typeof(*req), node); - if (req->status != TEGRA_DMA_REQ_INFLIGHT) { - /* - * We should not land here if queue mechanism - * with system latency are properly configured. - */ - WARN_ON(1); - tegra_dma_stop(ch); - start_head_req(ch); - } - } + handle_continuous_head_request(ch, req); return; } - tegra_dma_stop(ch); + tegra_dma_abort_req(ch, req, "Dma status is not on sync\n"); /* Dma should be stop much earlier */ BUG(); return; @@ -872,7 +911,6 @@ static void handle_continuous_dbl_dma(struct tegra_dma_channel *ch) static void handle_continuous_sngl_dma(struct tegra_dma_channel *ch) { struct tegra_dma_req *req; - struct tegra_dma_req *nreq; req = list_entry(ch->list.next, typeof(*req), node); if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_FULL) { @@ -892,21 +930,7 @@ static void handle_continuous_sngl_dma(struct tegra_dma_channel *ch) ch->callback = req->complete; ch->cb_req = req; - if (list_empty(&ch->list)) { - pr_debug("%s: stop\n", __func__); - tegra_dma_stop(ch); - } else { - /* Head req should be in flight */ - nreq = list_entry(ch->list.next, typeof(*nreq), node); - if (nreq->status != TEGRA_DMA_REQ_INFLIGHT) { - tegra_dma_stop(ch); - WARN_ON(1); - start_head_req(ch); - } else { - /* Configure next req */ - configure_next_req(ch, nreq); - } - } + handle_continuous_head_request(ch, req); return; } |