> 31.01.2022 12:09, Akhil R пишет: > >> В Mon, 31 Jan 2022 04:25:14 +0000 > >> Akhil R <akhilrajeev@xxxxxxxxxx> пишет: > >> > >>>> 30.01.2022 19:34, Akhil R пишет: > >>>>>> 29.01.2022 19:40, Akhil R пишет: > >>>>>>> +static int tegra_dma_device_pause(struct dma_chan *dc) { > >>>>>>> + struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); > >>>>>>> + unsigned long wcount, flags; > >>>>>>> + int ret = 0; > >>>>>>> + > >>>>>>> + if (!tdc->tdma->chip_data->hw_support_pause) > >>>>>>> + return 0; > >>>>>> > >>>>>> It's wrong to return zero if pause unsupported, please see what > >>>>>> dmaengine_pause() returns. > >>>>>> > >>>>>>> + > >>>>>>> + spin_lock_irqsave(&tdc->vc.lock, flags); > >>>>>>> + if (!tdc->dma_desc) > >>>>>>> + goto out; > >>>>>>> + > >>>>>>> + ret = tegra_dma_pause(tdc); > >>>>>>> + if (ret) { > >>>>>>> + dev_err(tdc2dev(tdc), "DMA pause timed out\n"); > >>>>>>> + goto out; > >>>>>>> + } > >>>>>>> + > >>>>>>> + wcount = tdc_read(tdc, TEGRA_GPCDMA_CHAN_XFER_COUNT); > >>>>>>> + tdc->dma_desc->bytes_xfer += > >>>>>>> + tdc->dma_desc->bytes_req - (wcount * 4); > >>>>>> > >>>>>> Why transfer is accumulated? > >>>>>> > >>>>>> Why do you need to update xfer size at all on pause? > >>>>> > >>>>> I will verify the calculation. This looks correct only for single > >>>>> sg transaction. > >>>>> > >>>>> Updating xfer_size is added to support drivers which pause the > >>>>> transaction and read the status before terminating. > >>>>> Eg. > >>>> > >>>> Why you couldn't update the status in tegra_dma_terminate_all()? > >>> Is it useful to update the status in terminate_all()? I assume the > >>> descriptor Is freed in vchan_dma_desc_free_list() or am I getting it > >>> wrong? > >> > >> Yes, it's not useful. Then you only need to fix the tx_status() and > >> don't touch dma_desc on pause. > > If the bytes_xfer is updated in tx_status(), I suppose > > DMA_RESIDUE_GRANULARITY_BURST can be kept as is, correct? > > You don't need to update bytes_xfer in tx_status(). Please see Tegra20 > DMA driver for the example, which supports GRANULARITY_BURST properly. Does the below method look good? bytes_xfer is updated on every ISR and in tx_status(), the wcount is read to calculate the intermittent value. If the transfer get complete in between, use wcount as 0 to add sg_req.len to bytes_xfer static int tegra_dma_get_residual(struct tegra_dma_channel *tdc) { unsigned long wcount = 0, status; unsigned int bytes_xfer, residual; struct tegra_dma_desc *dma_desc = tdc->dma_desc; struct tegra_dma_sg_req *sg_req = dma_desc->sg_req; /* * Do not read from CHAN_XFER_COUNT if EOC bit is set * as the transfer would have already completed and * the register could have updated for next transfer * in case of cyclic transfers. */ status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS); if (!(status & TEGRA_GPCDMA_STATUS_ISE_EOC)) wcount = tdc_read(tdc, TEGRA_GPCDMA_CHAN_XFER_COUNT); bytes_xfer = dma_desc->bytes_xfer + sg_req[dma_desc->sg_idx - 1].len - (wcount * 4); residual = dma_desc->bytes_req - (bytes_xfer % dma_desc->bytes_req); return residual; } static enum dma_status tegra_dma_tx_status( ) { .... spin_lock_irqsave(&tdc->vc.lock, flags); ... } else if (tdc->dma_desc && tdc->dma_desc->vd.tx.cookie == cookie) { residual = tegra_dma_get_residual(tdc); dma_set_residue(txstate, residual); } else { dev_err(tdc2dev(tdc), "cookie %d is not found\n", cookie); } spin_unlock_irqrestore(&tdc->vc.lock, flags); return ret; }