Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/async_tx
[pandora-kernel.git] / drivers / dma / at_hdmac.c
index f52c9e3..36144f8 100644 (file)
@@ -190,7 +190,7 @@ static void atc_desc_chain(struct at_desc **first, struct at_desc **prev,
 /**
  * atc_assign_cookie - compute and assign new cookie
  * @atchan: channel we work on
- * @desc: descriptor to asign cookie for
+ * @desc: descriptor to assign cookie for
  *
  * Called with atchan->lock held and bh disabled
  */
@@ -508,7 +508,8 @@ static irqreturn_t at_dma_interrupt(int irq, void *dev_id)
                        if (pending & (AT_DMA_BTC(i) | AT_DMA_ERR(i))) {
                                if (pending & AT_DMA_ERR(i)) {
                                        /* Disable channel on AHB error */
-                                       dma_writel(atdma, CHDR, atchan->mask);
+                                       dma_writel(atdma, CHDR,
+                                               AT_DMA_RES(i) | atchan->mask);
                                        /* Give information to tasklet */
                                        set_bit(ATC_IS_ERROR, &atchan->status);
                                }
@@ -625,16 +626,7 @@ atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
 
                desc->txd.cookie = 0;
 
-               if (!first) {
-                       first = desc;
-               } else {
-                       /* inform the HW lli about chaining */
-                       prev->lli.dscr = desc->txd.phys;
-                       /* insert the link descriptor to the LD ring */
-                       list_add_tail(&desc->desc_node,
-                                       &first->tx_list);
-               }
-               prev = desc;
+               atc_desc_chain(&first, &prev, desc);
        }
 
        /* First descriptor of the chain embedds additional information */
@@ -725,16 +717,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                                        | len >> mem_width;
                        desc->lli.ctrlb = ctrlb;
 
-                       if (!first) {
-                               first = desc;
-                       } else {
-                               /* inform the HW lli about chaining */
-                               prev->lli.dscr = desc->txd.phys;
-                               /* insert the link descriptor to the LD ring */
-                               list_add_tail(&desc->desc_node,
-                                               &first->tx_list);
-                       }
-                       prev = desc;
+                       atc_desc_chain(&first, &prev, desc);
                        total_len += len;
                }
                break;
@@ -768,16 +751,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                                        | len >> reg_width;
                        desc->lli.ctrlb = ctrlb;
 
-                       if (!first) {
-                               first = desc;
-                       } else {
-                               /* inform the HW lli about chaining */
-                               prev->lli.dscr = desc->txd.phys;
-                               /* insert the link descriptor to the LD ring */
-                               list_add_tail(&desc->desc_node,
-                                               &first->tx_list);
-                       }
-                       prev = desc;
+                       atc_desc_chain(&first, &prev, desc);
                        total_len += len;
                }
                break;
@@ -952,39 +926,62 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
 {
        struct at_dma_chan      *atchan = to_at_dma_chan(chan);
        struct at_dma           *atdma = to_at_dma(chan->device);
-       struct at_desc          *desc, *_desc;
+       int                     chan_id = atchan->chan_common.chan_id;
+
        LIST_HEAD(list);
 
-       /* Only supports DMA_TERMINATE_ALL */
-       if (cmd != DMA_TERMINATE_ALL)
-               return -ENXIO;
+       dev_vdbg(chan2dev(chan), "atc_control (%d)\n", cmd);
 
-       /*
-        * This is only called when something went wrong elsewhere, so
-        * we don't really care about the data. Just disable the
-        * channel. We still have to poll the channel enable bit due
-        * to AHB/HSB limitations.
-        */
-       spin_lock_bh(&atchan->lock);
+       if (cmd == DMA_PAUSE) {
+               spin_lock_bh(&atchan->lock);
 
-       dma_writel(atdma, CHDR, atchan->mask);
+               dma_writel(atdma, CHER, AT_DMA_SUSP(chan_id));
+               set_bit(ATC_IS_PAUSED, &atchan->status);
 
-       /* confirm that this channel is disabled */
-       while (dma_readl(atdma, CHSR) & atchan->mask)
-               cpu_relax();
+               spin_unlock_bh(&atchan->lock);
+       } else if (cmd == DMA_RESUME) {
+               if (!test_bit(ATC_IS_PAUSED, &atchan->status))
+                       return 0;
 
-       /* active_list entries will end up before queued entries */
-       list_splice_init(&atchan->queue, &list);
-       list_splice_init(&atchan->active_list, &list);
+               spin_lock_bh(&atchan->lock);
 
-       /* Flush all pending and queued descriptors */
-       list_for_each_entry_safe(desc, _desc, &list, desc_node)
-               atc_chain_complete(atchan, desc);
+               dma_writel(atdma, CHDR, AT_DMA_RES(chan_id));
+               clear_bit(ATC_IS_PAUSED, &atchan->status);
 
-       /* if channel dedicated to cyclic operations, free it */
-       clear_bit(ATC_IS_CYCLIC, &atchan->status);
+               spin_unlock_bh(&atchan->lock);
+       } else if (cmd == DMA_TERMINATE_ALL) {
+               struct at_desc  *desc, *_desc;
+               /*
+                * This is only called when something went wrong elsewhere, so
+                * we don't really care about the data. Just disable the
+                * channel. We still have to poll the channel enable bit due
+                * to AHB/HSB limitations.
+                */
+               spin_lock_bh(&atchan->lock);
 
-       spin_unlock_bh(&atchan->lock);
+               /* disabling channel: must also remove suspend state */
+               dma_writel(atdma, CHDR, AT_DMA_RES(chan_id) | atchan->mask);
+
+               /* confirm that this channel is disabled */
+               while (dma_readl(atdma, CHSR) & atchan->mask)
+                       cpu_relax();
+
+               /* active_list entries will end up before queued entries */
+               list_splice_init(&atchan->queue, &list);
+               list_splice_init(&atchan->active_list, &list);
+
+               /* Flush all pending and queued descriptors */
+               list_for_each_entry_safe(desc, _desc, &list, desc_node)
+                       atc_chain_complete(atchan, desc);
+
+               clear_bit(ATC_IS_PAUSED, &atchan->status);
+               /* if channel dedicated to cyclic operations, free it */
+               clear_bit(ATC_IS_CYCLIC, &atchan->status);
+
+               spin_unlock_bh(&atchan->lock);
+       } else {
+               return -ENXIO;
+       }
 
        return 0;
 }
@@ -1026,9 +1023,17 @@ atc_tx_status(struct dma_chan *chan,
 
        spin_unlock_bh(&atchan->lock);
 
-       dma_set_tx_state(txstate, last_complete, last_used, 0);
-       dev_vdbg(chan2dev(chan), "tx_status: %d (d%d, u%d)\n",
-                cookie, last_complete ? last_complete : 0,
+       if (ret != DMA_SUCCESS)
+               dma_set_tx_state(txstate, last_complete, last_used,
+                       atc_first_active(atchan)->len);
+       else
+               dma_set_tx_state(txstate, last_complete, last_used, 0);
+
+       if (test_bit(ATC_IS_PAUSED, &atchan->status))
+               ret = DMA_PAUSED;
+
+       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);
 
        return ret;