Move the CG state into the adev.uvd structure to allow for parallel UVD instances. Also prevent uvd start if in the PG state (will be enhanced later to support powerplay). Also avoid setting CG state in start since DPM will call set_clockgating_state anyways. Adds static PG to the DPM implementation. Finally, added a mutex around PG transistions so that the userspace can poll TILE/PGFSM registers without a race condition on the FW state. Signed-off-by: Tom St Denis <tom.stdenis at amd.com> --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 2 + drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 14 ++++++- drivers/gpu/drm/amd/amdgpu/cz_dpm.c | 60 +++++++++++++++++++++++++++--- drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c | 38 ++++++++++--------- 4 files changed, 88 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index d406ec7499ad..d0460ea2f85b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -1690,6 +1690,8 @@ struct amdgpu_uvd { bool address_64_bit; struct amd_sched_entity entity; uint32_t srbm_soft_reset; + int cg_state, pg_state; + struct mutex pg_lock; }; /* diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index eab931a58d06..b43a851295fe 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -2335,22 +2335,26 @@ static ssize_t amdgpu_debugfs_regs_read(struct file *f, char __user *buf, struct amdgpu_device *adev = f->f_inode->i_private; ssize_t result = 0; int r; - bool use_bank; + bool use_pg_lock, use_bank; unsigned instance_bank, sh_bank, se_bank; if (size & 0x3 || *pos & 0x3) return -EINVAL; + /* are we reading UVD registers for which a PG lock is necessary? */ + use_pg_lock = (*pos >> 23) & 1; + if (*pos & (1ULL << 62)) { se_bank = (*pos >> 24) & 0x3FF; sh_bank = (*pos >> 34) & 0x3FF; instance_bank = (*pos >> 44) & 0x3FF; use_bank = 1; - *pos &= 0xFFFFFF; } else { use_bank = 0; } + *pos &= 0x3FFFF; + if (use_bank) { if (sh_bank >= adev->gfx.config.max_sh_per_se || se_bank >= adev->gfx.config.max_shader_engines) @@ -2360,6 +2364,9 @@ static ssize_t amdgpu_debugfs_regs_read(struct file *f, char __user *buf, sh_bank, instance_bank); } + if (use_pg_lock) + mutex_lock(&adev->uvd.pg_lock); + while (size) { uint32_t value; @@ -2385,6 +2392,9 @@ end: mutex_unlock(&adev->grbm_idx_mutex); } + if (use_pg_lock) + mutex_unlock(&adev->uvd.pg_lock); + return result; } diff --git a/drivers/gpu/drm/amd/amdgpu/cz_dpm.c b/drivers/gpu/drm/amd/amdgpu/cz_dpm.c index 8ba07e79d4cb..e79307b7b526 100644 --- a/drivers/gpu/drm/amd/amdgpu/cz_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/cz_dpm.c @@ -41,6 +41,8 @@ #include "gmc/gmc_8_1_d.h" #include "bif/bif_5_1_d.h" #include "gfx_v8_0.h" +#include "uvd/uvd_6_0_d.h" +#include "uvd/uvd_6_0_sh_mask.h" static void cz_dpm_powergate_uvd(struct amdgpu_device *adev, bool gate); static void cz_dpm_powergate_vce(struct amdgpu_device *adev, bool gate); @@ -2101,6 +2103,18 @@ static void cz_dpm_powergate_uvd(struct amdgpu_device *adev, bool gate) if (pi->uvd_power_gated == gate) return; + mutex_lock(&adev->uvd.pg_lock); + + if (pi->caps_uvd_pg) { + if (pi->uvd_dynamic_pg) + WREG32(mmUVD_POWER_STATUS, + UVD_POWER_STATUS__UVD_PG_EN_MASK | + UVD_POWER_STATUS__UVD_PG_MODE_MASK); + else + WREG32(mmUVD_POWER_STATUS, + UVD_POWER_STATUS__UVD_PG_EN_MASK); + } + pi->uvd_power_gated = gate; if (gate) { @@ -2108,32 +2122,66 @@ static void cz_dpm_powergate_uvd(struct amdgpu_device *adev, bool gate) /* disable clockgating so we can properly shut down the block */ ret = amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_UVD, AMD_CG_STATE_UNGATE); + if (ret) { + DRM_ERROR("UVD DPM Power Gating failed to set clockgating state\n"); + goto end; + } + /* shutdown the UVD block */ ret = amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD, AMD_PG_STATE_GATE); - /* XXX: check for errors */ + + if (ret) { + DRM_ERROR("UVD DPM Power Gating failed to set powergating state\n"); + goto end; + } } cz_update_uvd_dpm(adev, gate); - if (pi->caps_uvd_pg) + if (pi->caps_uvd_pg) { /* power off the UVD block */ - cz_send_msg_to_smc(adev, PPSMC_MSG_UVDPowerOFF); + ret = cz_send_msg_to_smc(adev, PPSMC_MSG_UVDPowerOFF); + if (ret) { + DRM_ERROR("UVD DPM Power Gating failed to send SMU PowerOFF message\n"); + goto end; + } + adev->uvd.pg_state = gate; + } } else { if (pi->caps_uvd_pg) { /* power on the UVD block */ if (pi->uvd_dynamic_pg) - cz_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_UVDPowerON, 1); + ret = cz_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_UVDPowerON, 1); else - cz_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_UVDPowerON, 0); + ret = cz_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_UVDPowerON, 0); + + adev->uvd.pg_state = gate; + + if (ret) { + DRM_ERROR("UVD DPM Power Gating Failed to send SMU PowerON message\n"); + goto end; + } + /* re-init the UVD block */ ret = amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD, AMD_PG_STATE_UNGATE); + + if (ret) { + DRM_ERROR("UVD DPM Power Gating Failed to set powergating state\n"); + goto end; + } + /* enable clockgating. hw will dynamically gate/ungate clocks on the fly */ ret = amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_UVD, AMD_CG_STATE_GATE); - /* XXX: check for errors */ + if (ret) { + DRM_ERROR("UVD DPM Power Gating Failed to set clockgating state\n"); + goto end; + } } cz_update_uvd_dpm(adev, gate); } +end: + mutex_unlock(&adev->uvd.pg_lock); } static int cz_enable_vce_dpm(struct amdgpu_device *adev, bool enable) diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c index 4fa50918e886..422d5300b92e 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c @@ -101,6 +101,8 @@ static int uvd_v6_0_sw_init(void *handle) int r; struct amdgpu_device *adev = (struct amdgpu_device *)handle; + mutex_init(&adev->uvd.pg_lock); + /* UVD TRAP */ r = amdgpu_irq_add_id(adev, 124, &adev->uvd.irq); if (r) @@ -387,6 +389,13 @@ static int uvd_v6_0_start(struct amdgpu_device *adev) uint32_t mp_swap_cntl; int i, j, r; + /* is power gated? then we can't start (TODO: re-enable power) */ + if (adev->uvd.pg_state) + return -EINVAL; + + /* set CG state to -1 for unset */ + adev->uvd.cg_state = -1; + /* disable DPG */ WREG32_P(mmUVD_POWER_STATUS, 0, ~UVD_POWER_STATUS__UVD_PG_MODE_MASK); @@ -396,15 +405,10 @@ static int uvd_v6_0_start(struct amdgpu_device *adev) uvd_v6_0_mc_resume(adev); - /* Set dynamic clock gating in S/W control mode */ - if (adev->cg_flags & AMD_CG_SUPPORT_UVD_MGCG) { - uvd_v6_0_set_sw_clock_gating(adev); - } else { - /* disable clock gating */ - uint32_t data = RREG32(mmUVD_CGC_CTRL); - data &= ~UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK; - WREG32(mmUVD_CGC_CTRL, data); - } + /* disable clock gating */ + tmp = RREG32(mmUVD_CGC_CTRL); + tmp &= ~UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK; + WREG32(mmUVD_CGC_CTRL, tmp); /* disable interupt */ WREG32_P(mmUVD_MASTINT_EN, 0, ~UVD_MASTINT_EN__VCPU_EN_MASK); @@ -964,21 +968,19 @@ static int uvd_v6_0_set_clockgating_state(void *handle, enum amd_clockgating_state state) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; - bool enable = (state == AMD_CG_STATE_GATE) ? true : false; - static int curstate = -1; - - if (adev->asic_type == CHIP_FIJI || - adev->asic_type == CHIP_POLARIS10) - uvd_v6_set_bypass_mode(adev, enable); if (!(adev->cg_flags & AMD_CG_SUPPORT_UVD_MGCG)) return 0; - if (curstate == state) + if (adev->uvd.cg_state == state) return 0; - curstate = state; - if (enable) { + if (adev->asic_type == CHIP_FIJI || + adev->asic_type == CHIP_POLARIS10) + uvd_v6_set_bypass_mode(adev, state == AMD_CG_STATE_GATE ? true : false); + + adev->uvd.cg_state = state; + if (state == AMD_CG_STATE_GATE) { /* disable HW gating and enable Sw gating */ uvd_v6_0_set_sw_clock_gating(adev); } else { -- 2.9.2