--- /dev/null
+From ecc9ab4e924b7eb9e2c4a668162aaa1d9d60d08c Mon Sep 17 00:00:00 2001
+From: Alex Deucher <alexander.deucher@amd.com>
+Date: Wed, 13 Nov 2024 12:21:18 -0500
+Subject: drm/amdgpu/vcn4.0.5: split code along instances
+
+From: Alex Deucher <alexander.deucher@amd.com>
+
+commit ecc9ab4e924b7eb9e2c4a668162aaa1d9d60d08c upstream.
+
+Split the code on a per instance basis. This will allow
+us to use the per instance functions in the future to
+handle more things per instance.
+
+v2: squash in fix for stop() from Boyuan
+
+Reviewed-by: Boyuan Zhang <Boyuan.Zhang@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Tested-by: Eric Naim <dnaim@cachyos.org>
+Signed-off-by: Eric Naim <dnaim@cachyos.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c | 462 +++++++++++++++-----------------
+ 1 file changed, 229 insertions(+), 233 deletions(-)
+
+--- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c
++++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c
+@@ -991,184 +991,179 @@ static int vcn_v4_0_5_start_dpg_mode(str
+ * vcn_v4_0_5_start - VCN start
+ *
+ * @adev: amdgpu_device pointer
++ * @i: instance to start
+ *
+ * Start VCN block
+ */
+-static int vcn_v4_0_5_start(struct amdgpu_device *adev)
++static int vcn_v4_0_5_start(struct amdgpu_device *adev, int i)
+ {
+ volatile struct amdgpu_vcn4_fw_shared *fw_shared;
+ struct amdgpu_ring *ring;
+ uint32_t tmp;
+- int i, j, k, r;
++ int j, k, r;
+
+- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
+- if (adev->pm.dpm_enabled)
+- amdgpu_dpm_enable_vcn(adev, true, i);
+- }
+-
+- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
+- if (adev->vcn.harvest_config & (1 << i))
+- continue;
++ if (adev->vcn.harvest_config & (1 << i))
++ return 0;
+
+- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
++ if (adev->pm.dpm_enabled)
++ amdgpu_dpm_enable_vcn(adev, true, i);
+
+- if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) {
+- r = vcn_v4_0_5_start_dpg_mode(adev, i, adev->vcn.indirect_sram);
+- continue;
+- }
++ fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
+
+- /* disable VCN power gating */
+- vcn_v4_0_5_disable_static_power_gating(adev, i);
++ if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG)
++ return vcn_v4_0_5_start_dpg_mode(adev, i, adev->vcn.indirect_sram);
+
+- /* set VCN status busy */
+- tmp = RREG32_SOC15(VCN, i, regUVD_STATUS) | UVD_STATUS__UVD_BUSY;
+- WREG32_SOC15(VCN, i, regUVD_STATUS, tmp);
+-
+- /*SW clock gating */
+- vcn_v4_0_5_disable_clock_gating(adev, i);
+-
+- /* enable VCPU clock */
+- WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_VCPU_CNTL),
+- UVD_VCPU_CNTL__CLK_EN_MASK, ~UVD_VCPU_CNTL__CLK_EN_MASK);
+-
+- /* disable master interrupt */
+- WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_MASTINT_EN), 0,
+- ~UVD_MASTINT_EN__VCPU_EN_MASK);
+-
+- /* enable LMI MC and UMC channels */
+- WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_LMI_CTRL2), 0,
+- ~UVD_LMI_CTRL2__STALL_ARB_UMC_MASK);
+-
+- tmp = RREG32_SOC15(VCN, i, regUVD_SOFT_RESET);
+- tmp &= ~UVD_SOFT_RESET__LMI_SOFT_RESET_MASK;
+- tmp &= ~UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK;
+- WREG32_SOC15(VCN, i, regUVD_SOFT_RESET, tmp);
+-
+- /* setup regUVD_LMI_CTRL */
+- tmp = RREG32_SOC15(VCN, i, regUVD_LMI_CTRL);
+- WREG32_SOC15(VCN, i, regUVD_LMI_CTRL, tmp |
+- UVD_LMI_CTRL__WRITE_CLEAN_TIMER_EN_MASK |
+- UVD_LMI_CTRL__MASK_MC_URGENT_MASK |
+- UVD_LMI_CTRL__DATA_COHERENCY_EN_MASK |
+- UVD_LMI_CTRL__VCPU_DATA_COHERENCY_EN_MASK);
+-
+- /* setup regUVD_MPC_CNTL */
+- tmp = RREG32_SOC15(VCN, i, regUVD_MPC_CNTL);
+- tmp &= ~UVD_MPC_CNTL__REPLACEMENT_MODE_MASK;
+- tmp |= 0x2 << UVD_MPC_CNTL__REPLACEMENT_MODE__SHIFT;
+- WREG32_SOC15(VCN, i, regUVD_MPC_CNTL, tmp);
+-
+- /* setup UVD_MPC_SET_MUXA0 */
+- WREG32_SOC15(VCN, i, regUVD_MPC_SET_MUXA0,
+- ((0x1 << UVD_MPC_SET_MUXA0__VARA_1__SHIFT) |
+- (0x2 << UVD_MPC_SET_MUXA0__VARA_2__SHIFT) |
+- (0x3 << UVD_MPC_SET_MUXA0__VARA_3__SHIFT) |
+- (0x4 << UVD_MPC_SET_MUXA0__VARA_4__SHIFT)));
+-
+- /* setup UVD_MPC_SET_MUXB0 */
+- WREG32_SOC15(VCN, i, regUVD_MPC_SET_MUXB0,
+- ((0x1 << UVD_MPC_SET_MUXB0__VARB_1__SHIFT) |
+- (0x2 << UVD_MPC_SET_MUXB0__VARB_2__SHIFT) |
+- (0x3 << UVD_MPC_SET_MUXB0__VARB_3__SHIFT) |
+- (0x4 << UVD_MPC_SET_MUXB0__VARB_4__SHIFT)));
+-
+- /* setup UVD_MPC_SET_MUX */
+- WREG32_SOC15(VCN, i, regUVD_MPC_SET_MUX,
+- ((0x0 << UVD_MPC_SET_MUX__SET_0__SHIFT) |
+- (0x1 << UVD_MPC_SET_MUX__SET_1__SHIFT) |
+- (0x2 << UVD_MPC_SET_MUX__SET_2__SHIFT)));
+-
+- vcn_v4_0_5_mc_resume(adev, i);
+-
+- /* VCN global tiling registers */
+- WREG32_SOC15(VCN, i, regUVD_GFX10_ADDR_CONFIG,
+- adev->gfx.config.gb_addr_config);
+-
+- /* unblock VCPU register access */
+- WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_RB_ARB_CTRL), 0,
+- ~UVD_RB_ARB_CTRL__VCPU_DIS_MASK);
+-
+- /* release VCPU reset to boot */
+- WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_VCPU_CNTL), 0,
+- ~UVD_VCPU_CNTL__BLK_RST_MASK);
+-
+- for (j = 0; j < 10; ++j) {
+- uint32_t status;
+-
+- for (k = 0; k < 100; ++k) {
+- status = RREG32_SOC15(VCN, i, regUVD_STATUS);
+- if (status & 2)
+- break;
+- mdelay(10);
+- if (amdgpu_emu_mode == 1)
+- msleep(1);
+- }
++ /* disable VCN power gating */
++ vcn_v4_0_5_disable_static_power_gating(adev, i);
++
++ /* set VCN status busy */
++ tmp = RREG32_SOC15(VCN, i, regUVD_STATUS) | UVD_STATUS__UVD_BUSY;
++ WREG32_SOC15(VCN, i, regUVD_STATUS, tmp);
++
++ /* SW clock gating */
++ vcn_v4_0_5_disable_clock_gating(adev, i);
++
++ /* enable VCPU clock */
++ WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_VCPU_CNTL),
++ UVD_VCPU_CNTL__CLK_EN_MASK, ~UVD_VCPU_CNTL__CLK_EN_MASK);
++
++ /* disable master interrupt */
++ WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_MASTINT_EN), 0,
++ ~UVD_MASTINT_EN__VCPU_EN_MASK);
++
++ /* enable LMI MC and UMC channels */
++ WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_LMI_CTRL2), 0,
++ ~UVD_LMI_CTRL2__STALL_ARB_UMC_MASK);
++
++ tmp = RREG32_SOC15(VCN, i, regUVD_SOFT_RESET);
++ tmp &= ~UVD_SOFT_RESET__LMI_SOFT_RESET_MASK;
++ tmp &= ~UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK;
++ WREG32_SOC15(VCN, i, regUVD_SOFT_RESET, tmp);
++
++ /* setup regUVD_LMI_CTRL */
++ tmp = RREG32_SOC15(VCN, i, regUVD_LMI_CTRL);
++ WREG32_SOC15(VCN, i, regUVD_LMI_CTRL, tmp |
++ UVD_LMI_CTRL__WRITE_CLEAN_TIMER_EN_MASK |
++ UVD_LMI_CTRL__MASK_MC_URGENT_MASK |
++ UVD_LMI_CTRL__DATA_COHERENCY_EN_MASK |
++ UVD_LMI_CTRL__VCPU_DATA_COHERENCY_EN_MASK);
++
++ /* setup regUVD_MPC_CNTL */
++ tmp = RREG32_SOC15(VCN, i, regUVD_MPC_CNTL);
++ tmp &= ~UVD_MPC_CNTL__REPLACEMENT_MODE_MASK;
++ tmp |= 0x2 << UVD_MPC_CNTL__REPLACEMENT_MODE__SHIFT;
++ WREG32_SOC15(VCN, i, regUVD_MPC_CNTL, tmp);
++
++ /* setup UVD_MPC_SET_MUXA0 */
++ WREG32_SOC15(VCN, i, regUVD_MPC_SET_MUXA0,
++ ((0x1 << UVD_MPC_SET_MUXA0__VARA_1__SHIFT) |
++ (0x2 << UVD_MPC_SET_MUXA0__VARA_2__SHIFT) |
++ (0x3 << UVD_MPC_SET_MUXA0__VARA_3__SHIFT) |
++ (0x4 << UVD_MPC_SET_MUXA0__VARA_4__SHIFT)));
++
++ /* setup UVD_MPC_SET_MUXB0 */
++ WREG32_SOC15(VCN, i, regUVD_MPC_SET_MUXB0,
++ ((0x1 << UVD_MPC_SET_MUXB0__VARB_1__SHIFT) |
++ (0x2 << UVD_MPC_SET_MUXB0__VARB_2__SHIFT) |
++ (0x3 << UVD_MPC_SET_MUXB0__VARB_3__SHIFT) |
++ (0x4 << UVD_MPC_SET_MUXB0__VARB_4__SHIFT)));
++
++ /* setup UVD_MPC_SET_MUX */
++ WREG32_SOC15(VCN, i, regUVD_MPC_SET_MUX,
++ ((0x0 << UVD_MPC_SET_MUX__SET_0__SHIFT) |
++ (0x1 << UVD_MPC_SET_MUX__SET_1__SHIFT) |
++ (0x2 << UVD_MPC_SET_MUX__SET_2__SHIFT)));
++
++ vcn_v4_0_5_mc_resume(adev, i);
++
++ /* VCN global tiling registers */
++ WREG32_SOC15(VCN, i, regUVD_GFX10_ADDR_CONFIG,
++ adev->gfx.config.gb_addr_config);
++
++ /* unblock VCPU register access */
++ WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_RB_ARB_CTRL), 0,
++ ~UVD_RB_ARB_CTRL__VCPU_DIS_MASK);
++
++ /* release VCPU reset to boot */
++ WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_VCPU_CNTL), 0,
++ ~UVD_VCPU_CNTL__BLK_RST_MASK);
++
++ for (j = 0; j < 10; ++j) {
++ uint32_t status;
++
++ for (k = 0; k < 100; ++k) {
++ status = RREG32_SOC15(VCN, i, regUVD_STATUS);
++ if (status & 2)
++ break;
++ mdelay(10);
++ if (amdgpu_emu_mode == 1)
++ msleep(1);
++ }
+
+- if (amdgpu_emu_mode == 1) {
+- r = -1;
+- if (status & 2) {
+- r = 0;
+- break;
+- }
+- } else {
++ if (amdgpu_emu_mode == 1) {
++ r = -1;
++ if (status & 2) {
+ r = 0;
+- if (status & 2)
+- break;
+-
+- dev_err(adev->dev,
+- "VCN[%d] is not responding, trying to reset VCPU!!!\n", i);
+- WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_VCPU_CNTL),
+- UVD_VCPU_CNTL__BLK_RST_MASK,
+- ~UVD_VCPU_CNTL__BLK_RST_MASK);
+- mdelay(10);
+- WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_VCPU_CNTL), 0,
+- ~UVD_VCPU_CNTL__BLK_RST_MASK);
+-
+- mdelay(10);
+- r = -1;
++ break;
+ }
+- }
++ } else {
++ r = 0;
++ if (status & 2)
++ break;
++
++ dev_err(adev->dev,
++ "VCN[%d] is not responding, trying to reset VCPU!!!\n", i);
++ WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_VCPU_CNTL),
++ UVD_VCPU_CNTL__BLK_RST_MASK,
++ ~UVD_VCPU_CNTL__BLK_RST_MASK);
++ mdelay(10);
++ WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_VCPU_CNTL), 0,
++ ~UVD_VCPU_CNTL__BLK_RST_MASK);
+
+- if (r) {
+- dev_err(adev->dev, "VCN[%d] is not responding, giving up!!!\n", i);
+- return r;
++ mdelay(10);
++ r = -1;
+ }
++ }
+
+- /* enable master interrupt */
+- WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_MASTINT_EN),
+- UVD_MASTINT_EN__VCPU_EN_MASK,
+- ~UVD_MASTINT_EN__VCPU_EN_MASK);
+-
+- /* clear the busy bit of VCN_STATUS */
+- WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_STATUS), 0,
+- ~(2 << UVD_STATUS__VCPU_REPORT__SHIFT));
+-
+- ring = &adev->vcn.inst[i].ring_enc[0];
+- WREG32_SOC15(VCN, i, regVCN_RB1_DB_CTRL,
+- ring->doorbell_index << VCN_RB1_DB_CTRL__OFFSET__SHIFT |
+- VCN_RB1_DB_CTRL__EN_MASK);
+-
+- WREG32_SOC15(VCN, i, regUVD_RB_BASE_LO, ring->gpu_addr);
+- WREG32_SOC15(VCN, i, regUVD_RB_BASE_HI, upper_32_bits(ring->gpu_addr));
+- WREG32_SOC15(VCN, i, regUVD_RB_SIZE, ring->ring_size / 4);
+-
+- tmp = RREG32_SOC15(VCN, i, regVCN_RB_ENABLE);
+- tmp &= ~(VCN_RB_ENABLE__RB1_EN_MASK);
+- WREG32_SOC15(VCN, i, regVCN_RB_ENABLE, tmp);
+- fw_shared->sq.queue_mode |= FW_QUEUE_RING_RESET;
+- WREG32_SOC15(VCN, i, regUVD_RB_RPTR, 0);
+- WREG32_SOC15(VCN, i, regUVD_RB_WPTR, 0);
+-
+- tmp = RREG32_SOC15(VCN, i, regUVD_RB_RPTR);
+- WREG32_SOC15(VCN, i, regUVD_RB_WPTR, tmp);
+- ring->wptr = RREG32_SOC15(VCN, i, regUVD_RB_WPTR);
+-
+- tmp = RREG32_SOC15(VCN, i, regVCN_RB_ENABLE);
+- tmp |= VCN_RB_ENABLE__RB1_EN_MASK;
+- WREG32_SOC15(VCN, i, regVCN_RB_ENABLE, tmp);
+- fw_shared->sq.queue_mode &= ~(FW_QUEUE_RING_RESET | FW_QUEUE_DPG_HOLD_OFF);
++ if (r) {
++ dev_err(adev->dev, "VCN[%d] is not responding, giving up!!!\n", i);
++ return r;
+ }
+
++ /* enable master interrupt */
++ WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_MASTINT_EN),
++ UVD_MASTINT_EN__VCPU_EN_MASK,
++ ~UVD_MASTINT_EN__VCPU_EN_MASK);
++
++ /* clear the busy bit of VCN_STATUS */
++ WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_STATUS), 0,
++ ~(2 << UVD_STATUS__VCPU_REPORT__SHIFT));
++
++ ring = &adev->vcn.inst[i].ring_enc[0];
++ WREG32_SOC15(VCN, i, regVCN_RB1_DB_CTRL,
++ ring->doorbell_index << VCN_RB1_DB_CTRL__OFFSET__SHIFT |
++ VCN_RB1_DB_CTRL__EN_MASK);
++
++ WREG32_SOC15(VCN, i, regUVD_RB_BASE_LO, ring->gpu_addr);
++ WREG32_SOC15(VCN, i, regUVD_RB_BASE_HI, upper_32_bits(ring->gpu_addr));
++ WREG32_SOC15(VCN, i, regUVD_RB_SIZE, ring->ring_size / 4);
++
++ tmp = RREG32_SOC15(VCN, i, regVCN_RB_ENABLE);
++ tmp &= ~(VCN_RB_ENABLE__RB1_EN_MASK);
++ WREG32_SOC15(VCN, i, regVCN_RB_ENABLE, tmp);
++ fw_shared->sq.queue_mode |= FW_QUEUE_RING_RESET;
++ WREG32_SOC15(VCN, i, regUVD_RB_RPTR, 0);
++ WREG32_SOC15(VCN, i, regUVD_RB_WPTR, 0);
++
++ tmp = RREG32_SOC15(VCN, i, regUVD_RB_RPTR);
++ WREG32_SOC15(VCN, i, regUVD_RB_WPTR, tmp);
++ ring->wptr = RREG32_SOC15(VCN, i, regUVD_RB_WPTR);
++
++ tmp = RREG32_SOC15(VCN, i, regVCN_RB_ENABLE);
++ tmp |= VCN_RB_ENABLE__RB1_EN_MASK;
++ WREG32_SOC15(VCN, i, regVCN_RB_ENABLE, tmp);
++ fw_shared->sq.queue_mode &= ~(FW_QUEUE_RING_RESET | FW_QUEUE_DPG_HOLD_OFF);
++
+ return 0;
+ }
+
+@@ -1204,88 +1199,87 @@ static void vcn_v4_0_5_stop_dpg_mode(str
+ * vcn_v4_0_5_stop - VCN stop
+ *
+ * @adev: amdgpu_device pointer
++ * @i: instance to stop
+ *
+ * Stop VCN block
+ */
+-static int vcn_v4_0_5_stop(struct amdgpu_device *adev)
++static int vcn_v4_0_5_stop(struct amdgpu_device *adev, int i)
+ {
+ volatile struct amdgpu_vcn4_fw_shared *fw_shared;
+ uint32_t tmp;
+- int i, r = 0;
++ int r = 0;
+
+- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
+- if (adev->vcn.harvest_config & (1 << i))
+- continue;
+-
+- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
+- fw_shared->sq.queue_mode |= FW_QUEUE_DPG_HOLD_OFF;
+-
+- if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) {
+- vcn_v4_0_5_stop_dpg_mode(adev, i);
+- continue;
+- }
+-
+- /* wait for vcn idle */
+- r = SOC15_WAIT_ON_RREG(VCN, i, regUVD_STATUS, UVD_STATUS__IDLE, 0x7);
+- if (r)
+- return r;
+-
+- tmp = UVD_LMI_STATUS__VCPU_LMI_WRITE_CLEAN_MASK |
+- UVD_LMI_STATUS__READ_CLEAN_MASK |
+- UVD_LMI_STATUS__WRITE_CLEAN_MASK |
+- UVD_LMI_STATUS__WRITE_CLEAN_RAW_MASK;
+- r = SOC15_WAIT_ON_RREG(VCN, i, regUVD_LMI_STATUS, tmp, tmp);
+- if (r)
+- return r;
+-
+- /* disable LMI UMC channel */
+- tmp = RREG32_SOC15(VCN, i, regUVD_LMI_CTRL2);
+- tmp |= UVD_LMI_CTRL2__STALL_ARB_UMC_MASK;
+- WREG32_SOC15(VCN, i, regUVD_LMI_CTRL2, tmp);
+- tmp = UVD_LMI_STATUS__UMC_READ_CLEAN_RAW_MASK |
+- UVD_LMI_STATUS__UMC_WRITE_CLEAN_RAW_MASK;
+- r = SOC15_WAIT_ON_RREG(VCN, i, regUVD_LMI_STATUS, tmp, tmp);
+- if (r)
+- return r;
+-
+- /* block VCPU register access */
+- WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_RB_ARB_CTRL),
+- UVD_RB_ARB_CTRL__VCPU_DIS_MASK,
+- ~UVD_RB_ARB_CTRL__VCPU_DIS_MASK);
+-
+- /* reset VCPU */
+- WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_VCPU_CNTL),
+- UVD_VCPU_CNTL__BLK_RST_MASK,
+- ~UVD_VCPU_CNTL__BLK_RST_MASK);
+-
+- /* disable VCPU clock */
+- WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_VCPU_CNTL), 0,
+- ~(UVD_VCPU_CNTL__CLK_EN_MASK));
+-
+- /* apply soft reset */
+- tmp = RREG32_SOC15(VCN, i, regUVD_SOFT_RESET);
+- tmp |= UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK;
+- WREG32_SOC15(VCN, i, regUVD_SOFT_RESET, tmp);
+- tmp = RREG32_SOC15(VCN, i, regUVD_SOFT_RESET);
+- tmp |= UVD_SOFT_RESET__LMI_SOFT_RESET_MASK;
+- WREG32_SOC15(VCN, i, regUVD_SOFT_RESET, tmp);
+-
+- /* clear status */
+- WREG32_SOC15(VCN, i, regUVD_STATUS, 0);
++ if (adev->vcn.harvest_config & (1 << i))
++ return 0;
+
+- /* apply HW clock gating */
+- vcn_v4_0_5_enable_clock_gating(adev, i);
++ fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
++ fw_shared->sq.queue_mode |= FW_QUEUE_DPG_HOLD_OFF;
+
+- /* enable VCN power gating */
+- vcn_v4_0_5_enable_static_power_gating(adev, i);
++ if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) {
++ vcn_v4_0_5_stop_dpg_mode(adev, i);
++ r = 0;
++ goto done;
+ }
+
+- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
+- if (adev->pm.dpm_enabled)
+- amdgpu_dpm_enable_vcn(adev, false, i);
+- }
++ /* wait for vcn idle */
++ r = SOC15_WAIT_ON_RREG(VCN, i, regUVD_STATUS, UVD_STATUS__IDLE, 0x7);
++ if (r)
++ goto done;
++
++ tmp = UVD_LMI_STATUS__VCPU_LMI_WRITE_CLEAN_MASK |
++ UVD_LMI_STATUS__READ_CLEAN_MASK |
++ UVD_LMI_STATUS__WRITE_CLEAN_MASK |
++ UVD_LMI_STATUS__WRITE_CLEAN_RAW_MASK;
++ r = SOC15_WAIT_ON_RREG(VCN, i, regUVD_LMI_STATUS, tmp, tmp);
++ if (r)
++ goto done;
++
++ /* disable LMI UMC channel */
++ tmp = RREG32_SOC15(VCN, i, regUVD_LMI_CTRL2);
++ tmp |= UVD_LMI_CTRL2__STALL_ARB_UMC_MASK;
++ WREG32_SOC15(VCN, i, regUVD_LMI_CTRL2, tmp);
++ tmp = UVD_LMI_STATUS__UMC_READ_CLEAN_RAW_MASK |
++ UVD_LMI_STATUS__UMC_WRITE_CLEAN_RAW_MASK;
++ r = SOC15_WAIT_ON_RREG(VCN, i, regUVD_LMI_STATUS, tmp, tmp);
++ if (r)
++ goto done;
++
++ /* block VCPU register access */
++ WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_RB_ARB_CTRL),
++ UVD_RB_ARB_CTRL__VCPU_DIS_MASK,
++ ~UVD_RB_ARB_CTRL__VCPU_DIS_MASK);
++
++ /* reset VCPU */
++ WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_VCPU_CNTL),
++ UVD_VCPU_CNTL__BLK_RST_MASK,
++ ~UVD_VCPU_CNTL__BLK_RST_MASK);
++
++ /* disable VCPU clock */
++ WREG32_P(SOC15_REG_OFFSET(VCN, i, regUVD_VCPU_CNTL), 0,
++ ~(UVD_VCPU_CNTL__CLK_EN_MASK));
++
++ /* apply soft reset */
++ tmp = RREG32_SOC15(VCN, i, regUVD_SOFT_RESET);
++ tmp |= UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK;
++ WREG32_SOC15(VCN, i, regUVD_SOFT_RESET, tmp);
++ tmp = RREG32_SOC15(VCN, i, regUVD_SOFT_RESET);
++ tmp |= UVD_SOFT_RESET__LMI_SOFT_RESET_MASK;
++ WREG32_SOC15(VCN, i, regUVD_SOFT_RESET, tmp);
++
++ /* clear status */
++ WREG32_SOC15(VCN, i, regUVD_STATUS, 0);
++
++ /* apply HW clock gating */
++ vcn_v4_0_5_enable_clock_gating(adev, i);
++
++ /* enable VCN power gating */
++ vcn_v4_0_5_enable_static_power_gating(adev, i);
++
++done:
++ if (adev->pm.dpm_enabled)
++ amdgpu_dpm_enable_vcn(adev, false, i);
+
+- return 0;
++ return r;
+ }
+
+ /**
+@@ -1537,15 +1531,17 @@ static int vcn_v4_0_5_set_powergating_st
+ enum amd_powergating_state state)
+ {
+ struct amdgpu_device *adev = ip_block->adev;
+- int ret;
++ int ret = 0, i;
+
+ if (state == adev->vcn.cur_state)
+ return 0;
+
+- if (state == AMD_PG_STATE_GATE)
+- ret = vcn_v4_0_5_stop(adev);
+- else
+- ret = vcn_v4_0_5_start(adev);
++ for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
++ if (state == AMD_PG_STATE_GATE)
++ ret |= vcn_v4_0_5_stop(adev, i);
++ else
++ ret |= vcn_v4_0_5_start(adev, i);
++ }
+
+ if (!ret)
+ adev->vcn.cur_state = state;