| From 0011c8cf299169459cd6d80bd1ea4eae047627b1 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 2 May 2019 18:25:17 +0530 |
| Subject: dmaengine: tegra210-adma: restore channel status |
| |
| From: Sameer Pujar <spujar@nvidia.com> |
| |
| [ Upstream commit f33e7bb3eb922618612a90f0a828c790e8880773 ] |
| |
| Status of ADMA channel registers is not saved and restored during system |
| suspend. During active playback if system enters suspend, this results in |
| wrong state of channel registers during system resume and playback fails |
| to resume properly. Fix this by saving following channel registers in |
| runtime suspend and restore during runtime resume. |
| * ADMA_CH_LOWER_SRC_ADDR |
| * ADMA_CH_LOWER_TRG_ADDR |
| * ADMA_CH_FIFO_CTRL |
| * ADMA_CH_CONFIG |
| * ADMA_CH_CTRL |
| * ADMA_CH_CMD |
| * ADMA_CH_TC |
| Runtime PM calls will be inovked during system resume path if a playback |
| or capture needs to be resumed. Hence above changes work fine for system |
| suspend case. |
| |
| Fixes: f46b195799b5 ("dmaengine: tegra-adma: Add support for Tegra210 ADMA") |
| Signed-off-by: Sameer Pujar <spujar@nvidia.com> |
| Reviewed-by: Jon Hunter <jonathanh@nvidia.com> |
| Signed-off-by: Vinod Koul <vkoul@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/dma/tegra210-adma.c | 46 ++++++++++++++++++++++++++++++++++++- |
| 1 file changed, 45 insertions(+), 1 deletion(-) |
| |
| diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c |
| index e9e46a5207452..8c3cab463354f 100644 |
| --- a/drivers/dma/tegra210-adma.c |
| +++ b/drivers/dma/tegra210-adma.c |
| @@ -98,6 +98,7 @@ struct tegra_adma_chan_regs { |
| unsigned int src_addr; |
| unsigned int trg_addr; |
| unsigned int fifo_ctrl; |
| + unsigned int cmd; |
| unsigned int tc; |
| }; |
| |
| @@ -127,6 +128,7 @@ struct tegra_adma_chan { |
| enum dma_transfer_direction sreq_dir; |
| unsigned int sreq_index; |
| bool sreq_reserved; |
| + struct tegra_adma_chan_regs ch_regs; |
| |
| /* Transfer count and position info */ |
| unsigned int tx_buf_count; |
| @@ -635,8 +637,30 @@ static struct dma_chan *tegra_dma_of_xlate(struct of_phandle_args *dma_spec, |
| static int tegra_adma_runtime_suspend(struct device *dev) |
| { |
| struct tegra_adma *tdma = dev_get_drvdata(dev); |
| + struct tegra_adma_chan_regs *ch_reg; |
| + struct tegra_adma_chan *tdc; |
| + int i; |
| |
| tdma->global_cmd = tdma_read(tdma, ADMA_GLOBAL_CMD); |
| + if (!tdma->global_cmd) |
| + goto clk_disable; |
| + |
| + for (i = 0; i < tdma->nr_channels; i++) { |
| + tdc = &tdma->channels[i]; |
| + ch_reg = &tdc->ch_regs; |
| + ch_reg->cmd = tdma_ch_read(tdc, ADMA_CH_CMD); |
| + /* skip if channel is not active */ |
| + if (!ch_reg->cmd) |
| + continue; |
| + ch_reg->tc = tdma_ch_read(tdc, ADMA_CH_TC); |
| + ch_reg->src_addr = tdma_ch_read(tdc, ADMA_CH_LOWER_SRC_ADDR); |
| + ch_reg->trg_addr = tdma_ch_read(tdc, ADMA_CH_LOWER_TRG_ADDR); |
| + ch_reg->ctrl = tdma_ch_read(tdc, ADMA_CH_CTRL); |
| + ch_reg->fifo_ctrl = tdma_ch_read(tdc, ADMA_CH_FIFO_CTRL); |
| + ch_reg->config = tdma_ch_read(tdc, ADMA_CH_CONFIG); |
| + } |
| + |
| +clk_disable: |
| clk_disable_unprepare(tdma->ahub_clk); |
| |
| return 0; |
| @@ -645,7 +669,9 @@ static int tegra_adma_runtime_suspend(struct device *dev) |
| static int tegra_adma_runtime_resume(struct device *dev) |
| { |
| struct tegra_adma *tdma = dev_get_drvdata(dev); |
| - int ret; |
| + struct tegra_adma_chan_regs *ch_reg; |
| + struct tegra_adma_chan *tdc; |
| + int ret, i; |
| |
| ret = clk_prepare_enable(tdma->ahub_clk); |
| if (ret) { |
| @@ -654,6 +680,24 @@ static int tegra_adma_runtime_resume(struct device *dev) |
| } |
| tdma_write(tdma, ADMA_GLOBAL_CMD, tdma->global_cmd); |
| |
| + if (!tdma->global_cmd) |
| + return 0; |
| + |
| + for (i = 0; i < tdma->nr_channels; i++) { |
| + tdc = &tdma->channels[i]; |
| + ch_reg = &tdc->ch_regs; |
| + /* skip if channel was not active earlier */ |
| + if (!ch_reg->cmd) |
| + continue; |
| + tdma_ch_write(tdc, ADMA_CH_TC, ch_reg->tc); |
| + tdma_ch_write(tdc, ADMA_CH_LOWER_SRC_ADDR, ch_reg->src_addr); |
| + tdma_ch_write(tdc, ADMA_CH_LOWER_TRG_ADDR, ch_reg->trg_addr); |
| + tdma_ch_write(tdc, ADMA_CH_CTRL, ch_reg->ctrl); |
| + tdma_ch_write(tdc, ADMA_CH_FIFO_CTRL, ch_reg->fifo_ctrl); |
| + tdma_ch_write(tdc, ADMA_CH_CONFIG, ch_reg->config); |
| + tdma_ch_write(tdc, ADMA_CH_CMD, ch_reg->cmd); |
| + } |
| + |
| return 0; |
| } |
| |
| -- |
| 2.20.1 |
| |