/* prototypes */
 static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx);
+static void atc_issue_pending(struct dma_chan *chan);
 
 
 /*----------------------------------------------------------------------*/
        vdbg_dump_regs(atchan);
 }
 
+/*
+ * atc_get_current_descriptors -
+ * locate the descriptor which equal to physical address in DSCR
+ * @atchan: the channel we want to start
+ * @dscr_addr: physical descriptor address in DSCR
+ */
+static struct at_desc *atc_get_current_descriptors(struct at_dma_chan *atchan,
+                                                       u32 dscr_addr)
+{
+       struct at_desc  *desc, *_desc, *child, *desc_cur = NULL;
+
+       list_for_each_entry_safe(desc, _desc, &atchan->active_list, desc_node) {
+               if (desc->lli.dscr == dscr_addr) {
+                       desc_cur = desc;
+                       break;
+               }
+
+               list_for_each_entry(child, &desc->tx_list, desc_node) {
+                       if (child->lli.dscr == dscr_addr) {
+                               desc_cur = child;
+                               break;
+                       }
+               }
+       }
+
+       return desc_cur;
+}
+
+/*
+ * atc_get_bytes_left -
+ * Get the number of bytes residue in dma buffer,
+ * @chan: the channel we want to start
+ */
+static int atc_get_bytes_left(struct dma_chan *chan)
+{
+       struct at_dma_chan      *atchan = to_at_dma_chan(chan);
+       struct at_dma           *atdma = to_at_dma(chan->device);
+       int     chan_id = atchan->chan_common.chan_id;
+       struct at_desc *desc_first = atc_first_active(atchan);
+       struct at_desc *desc_cur;
+       int ret = 0, count = 0;
+
+       /*
+        * Initialize necessary values in the first time.
+        * remain_desc record remain desc length.
+        */
+       if (atchan->remain_desc == 0)
+               /* First descriptor embedds the transaction length */
+               atchan->remain_desc = desc_first->len;
+
+       /*
+        * This happens when current descriptor transfer complete.
+        * The residual buffer size should reduce current descriptor length.
+        */
+       if (unlikely(test_bit(ATC_IS_BTC, &atchan->status))) {
+               clear_bit(ATC_IS_BTC, &atchan->status);
+               desc_cur = atc_get_current_descriptors(atchan,
+                                               channel_readl(atchan, DSCR));
+               if (!desc_cur) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+               atchan->remain_desc -= (desc_cur->lli.ctrla & ATC_BTSIZE_MAX)
+                                               << (desc_first->tx_width);
+               if (atchan->remain_desc < 0) {
+                       ret = -EINVAL;
+                       goto out;
+               } else
+                       ret = atchan->remain_desc;
+       } else {
+               /*
+                * Get residual bytes when current
+                * descriptor transfer in progress.
+                */
+               count = (channel_readl(atchan, CTRLA) & ATC_BTSIZE_MAX)
+                               << (desc_first->tx_width);
+               ret = atchan->remain_desc - count;
+       }
+       /*
+        * Check fifo empty.
+        */
+       if (!(dma_readl(atdma, CHSR) & AT_DMA_EMPT(chan_id)))
+               atc_issue_pending(chan);
+
+out:
+       return ret;
+}
+
 /**
  * atc_chain_complete - finish work for one transaction chain
  * @atchan: channel we work on
                                        /* Give information to tasklet */
                                        set_bit(ATC_IS_ERROR, &atchan->status);
                                }
+                               if (pending & AT_DMA_BTC(i))
+                                       set_bit(ATC_IS_BTC, &atchan->status);
                                tasklet_schedule(&atchan->tasklet);
                                ret = IRQ_HANDLED;
                        }
                struct dma_tx_state *txstate)
 {
        struct at_dma_chan      *atchan = to_at_dma_chan(chan);
-       dma_cookie_t            last_used;
-       dma_cookie_t            last_complete;
        unsigned long           flags;
        enum dma_status         ret;
-
-       spin_lock_irqsave(&atchan->lock, flags);
+       int bytes = 0;
 
        ret = dma_cookie_status(chan, cookie, txstate);
-       if (ret != DMA_SUCCESS) {
-               atc_cleanup_descriptors(atchan);
+       if (ret == DMA_SUCCESS)
+               return ret;
+       /*
+        * There's no point calculating the residue if there's
+        * no txstate to store the value.
+        */
+       if (!txstate)
+               return DMA_ERROR;
 
-               ret = dma_cookie_status(chan, cookie, txstate);
-       }
+       spin_lock_irqsave(&atchan->lock, flags);
 
-       last_complete = chan->completed_cookie;
-       last_used = chan->cookie;
+       /*  Get number of bytes left in the active transactions */
+       bytes = atc_get_bytes_left(chan);
 
        spin_unlock_irqrestore(&atchan->lock, flags);
 
-       if (ret != DMA_SUCCESS)
-               dma_set_residue(txstate, atc_first_active(atchan)->len);
-
-       if (atc_chan_is_paused(atchan))
-               ret = DMA_PAUSED;
+       if (unlikely(bytes < 0)) {
+               dev_vdbg(chan2dev(chan), "get residual bytes error\n");
+               return DMA_ERROR;
+       } else
+               dma_set_residue(txstate, bytes);
 
-       dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d (d%d, u%d)\n",
-                ret, cookie, last_complete ? last_complete : 0,
-                last_used ? last_used : 0);
+       dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d residue = %d\n",
+                ret, cookie, bytes);
 
        return ret;
 }
 
        spin_lock_irqsave(&atchan->lock, flags);
        atchan->descs_allocated = i;
+       atchan->remain_desc = 0;
        list_splice(&tmp_list, &atchan->free_list);
        dma_cookie_init(chan);
        spin_unlock_irqrestore(&atchan->lock, flags);
        list_splice_init(&atchan->free_list, &list);
        atchan->descs_allocated = 0;
        atchan->status = 0;
+       atchan->remain_desc = 0;
 
        dev_vdbg(chan2dev(chan), "free_chan_resources: done\n");
 }