Merge tag 'core-core-2020-01-28' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / dma / imx-sdma.c
index c27e206..066b21a 100644 (file)
@@ -760,12 +760,8 @@ static void sdma_start_desc(struct sdma_channel *sdmac)
                return;
        }
        sdmac->desc = desc = to_sdma_desc(&vd->tx);
-       /*
-        * Do not delete the node in desc_issued list in cyclic mode, otherwise
-        * the desc allocated will never be freed in vchan_dma_desc_free_list
-        */
-       if (!(sdmac->flags & IMX_DMA_SG_LOOP))
-               list_del(&vd->node);
+
+       list_del(&vd->node);
 
        sdma->channel_control[channel].base_bd_ptr = desc->bd_phys;
        sdma->channel_control[channel].current_bd_ptr = desc->bd_phys;
@@ -1071,20 +1067,27 @@ static void sdma_channel_terminate_work(struct work_struct *work)
 
        spin_lock_irqsave(&sdmac->vc.lock, flags);
        vchan_get_all_descriptors(&sdmac->vc, &head);
-       sdmac->desc = NULL;
        spin_unlock_irqrestore(&sdmac->vc.lock, flags);
        vchan_dma_desc_free_list(&sdmac->vc, &head);
        sdmac->context_loaded = false;
 }
 
-static int sdma_disable_channel_async(struct dma_chan *chan)
+static int sdma_terminate_all(struct dma_chan *chan)
 {
        struct sdma_channel *sdmac = to_sdma_chan(chan);
+       unsigned long flags;
+
+       spin_lock_irqsave(&sdmac->vc.lock, flags);
 
        sdma_disable_channel(chan);
 
-       if (sdmac->desc)
+       if (sdmac->desc) {
+               vchan_terminate_vdesc(&sdmac->desc->vd);
+               sdmac->desc = NULL;
                schedule_work(&sdmac->terminate_worker);
+       }
+
+       spin_unlock_irqrestore(&sdmac->vc.lock, flags);
 
        return 0;
 }
@@ -1324,7 +1327,7 @@ static void sdma_free_chan_resources(struct dma_chan *chan)
        struct sdma_channel *sdmac = to_sdma_chan(chan);
        struct sdma_engine *sdma = sdmac->sdma;
 
-       sdma_disable_channel_async(chan);
+       sdma_terminate_all(chan);
 
        sdma_channel_synchronize(chan);
 
@@ -1648,7 +1651,7 @@ static enum dma_status sdma_tx_status(struct dma_chan *chan,
                                      struct dma_tx_state *txstate)
 {
        struct sdma_channel *sdmac = to_sdma_chan(chan);
-       struct sdma_desc *desc;
+       struct sdma_desc *desc = NULL;
        u32 residue;
        struct virt_dma_desc *vd;
        enum dma_status ret;
@@ -1659,19 +1662,23 @@ static enum dma_status sdma_tx_status(struct dma_chan *chan,
                return ret;
 
        spin_lock_irqsave(&sdmac->vc.lock, flags);
+
        vd = vchan_find_desc(&sdmac->vc, cookie);
-       if (vd) {
+       if (vd)
                desc = to_sdma_desc(&vd->tx);
+       else if (sdmac->desc && sdmac->desc->vd.tx.cookie == cookie)
+               desc = sdmac->desc;
+
+       if (desc) {
                if (sdmac->flags & IMX_DMA_SG_LOOP)
                        residue = (desc->num_bd - desc->buf_ptail) *
                                desc->period_len - desc->chn_real_count;
                else
                        residue = desc->chn_count - desc->chn_real_count;
-       } else if (sdmac->desc && sdmac->desc->vd.tx.cookie == cookie) {
-               residue = sdmac->desc->chn_count - sdmac->desc->chn_real_count;
        } else {
                residue = 0;
        }
+
        spin_unlock_irqrestore(&sdmac->vc.lock, flags);
 
        dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie,
@@ -2103,7 +2110,7 @@ static int sdma_probe(struct platform_device *pdev)
        sdma->dma_device.device_prep_slave_sg = sdma_prep_slave_sg;
        sdma->dma_device.device_prep_dma_cyclic = sdma_prep_dma_cyclic;
        sdma->dma_device.device_config = sdma_config;
-       sdma->dma_device.device_terminate_all = sdma_disable_channel_async;
+       sdma->dma_device.device_terminate_all = sdma_terminate_all;
        sdma->dma_device.device_synchronize = sdma_channel_synchronize;
        sdma->dma_device.src_addr_widths = SDMA_DMA_BUSWIDTHS;
        sdma->dma_device.dst_addr_widths = SDMA_DMA_BUSWIDTHS;