diff options
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/cz_dpm.c')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/cz_dpm.c | 273 |
1 files changed, 272 insertions, 1 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/cz_dpm.c b/drivers/gpu/drm/amd/amdgpu/cz_dpm.c index 8035d4d6a4f5..4dd17f2dd905 100644 --- a/drivers/gpu/drm/amd/amdgpu/cz_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/cz_dpm.c @@ -1078,6 +1078,37 @@ static uint32_t cz_get_eclk_level(struct amdgpu_device *adev, return i; } +static uint32_t cz_get_uvd_level(struct amdgpu_device *adev, + uint32_t clock, uint16_t msg) +{ + int i = 0; + struct amdgpu_uvd_clock_voltage_dependency_table *table = + &adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table; + + switch (msg) { + case PPSMC_MSG_SetUvdSoftMin: + case PPSMC_MSG_SetUvdHardMin: + for (i = 0; i < table->count; i++) + if (clock <= table->entries[i].vclk) + break; + if (i == table->count) + i = table->count - 1; + break; + case PPSMC_MSG_SetUvdSoftMax: + case PPSMC_MSG_SetUvdHardMax: + for (i = table->count - 1; i >= 0; i--) + if (clock >= table->entries[i].vclk) + break; + if (i < 0) + i = 0; + break; + default: + break; + } + + return i; +} + static int cz_program_bootup_state(struct amdgpu_device *adev) { struct cz_power_info *pi = cz_get_pi(adev); @@ -1739,6 +1770,200 @@ static int cz_dpm_unforce_dpm_levels(struct amdgpu_device *adev) return 0; } +static int cz_dpm_uvd_force_highest(struct amdgpu_device *adev) +{ + struct cz_power_info *pi = cz_get_pi(adev); + int ret = 0; + + if (pi->uvd_dpm.soft_min_clk != pi->uvd_dpm.soft_max_clk) { + pi->uvd_dpm.soft_min_clk = + pi->uvd_dpm.soft_max_clk; + ret = cz_send_msg_to_smc_with_parameter(adev, + PPSMC_MSG_SetUvdSoftMin, + cz_get_uvd_level(adev, + pi->uvd_dpm.soft_min_clk, + PPSMC_MSG_SetUvdSoftMin)); + if (ret) + return ret; + } + + return ret; +} + +static int cz_dpm_uvd_force_lowest(struct amdgpu_device *adev) +{ + struct cz_power_info *pi = cz_get_pi(adev); + int ret = 0; + + if (pi->uvd_dpm.soft_max_clk != pi->uvd_dpm.soft_min_clk) { + pi->uvd_dpm.soft_max_clk = pi->uvd_dpm.soft_min_clk; + ret = cz_send_msg_to_smc_with_parameter(adev, + PPSMC_MSG_SetUvdSoftMax, + cz_get_uvd_level(adev, + pi->uvd_dpm.soft_max_clk, + PPSMC_MSG_SetUvdSoftMax)); + if (ret) + return ret; + } + + return ret; +} + +static uint32_t cz_dpm_get_max_uvd_level(struct amdgpu_device *adev) +{ + struct cz_power_info *pi = cz_get_pi(adev); + + if (!pi->max_uvd_level) { + cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxUvdLevel); + pi->max_uvd_level = cz_get_argument(adev) + 1; + } + + if (pi->max_uvd_level > CZ_MAX_HARDWARE_POWERLEVELS) { + DRM_ERROR("Invalid max uvd level!\n"); + return -EINVAL; + } + + return pi->max_uvd_level; +} + +static int cz_dpm_unforce_uvd_dpm_levels(struct amdgpu_device *adev) +{ + struct cz_power_info *pi = cz_get_pi(adev); + struct amdgpu_uvd_clock_voltage_dependency_table *dep_table = + &adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table; + uint32_t level = 0; + int ret = 0; + + pi->uvd_dpm.soft_min_clk = dep_table->entries[0].vclk; + level = cz_dpm_get_max_uvd_level(adev) - 1; + if (level < dep_table->count) + pi->uvd_dpm.soft_max_clk = dep_table->entries[level].vclk; + else + pi->uvd_dpm.soft_max_clk = + dep_table->entries[dep_table->count - 1].vclk; + + /* get min/max sclk soft value + * notify SMU to execute */ + ret = cz_send_msg_to_smc_with_parameter(adev, + PPSMC_MSG_SetUvdSoftMin, + cz_get_uvd_level(adev, + pi->uvd_dpm.soft_min_clk, + PPSMC_MSG_SetUvdSoftMin)); + if (ret) + return ret; + + ret = cz_send_msg_to_smc_with_parameter(adev, + PPSMC_MSG_SetUvdSoftMax, + cz_get_uvd_level(adev, + pi->uvd_dpm.soft_max_clk, + PPSMC_MSG_SetUvdSoftMax)); + if (ret) + return ret; + + DRM_DEBUG("DPM uvd unforce state min=%d, max=%d.\n", + pi->uvd_dpm.soft_min_clk, + pi->uvd_dpm.soft_max_clk); + + return 0; +} + +static int cz_dpm_vce_force_highest(struct amdgpu_device *adev) +{ + struct cz_power_info *pi = cz_get_pi(adev); + int ret = 0; + + if (pi->vce_dpm.soft_min_clk != pi->vce_dpm.soft_max_clk) { + pi->vce_dpm.soft_min_clk = + pi->vce_dpm.soft_max_clk; + ret = cz_send_msg_to_smc_with_parameter(adev, + PPSMC_MSG_SetEclkSoftMin, + cz_get_eclk_level(adev, + pi->vce_dpm.soft_min_clk, + PPSMC_MSG_SetEclkSoftMin)); + if (ret) + return ret; + } + + return ret; +} + +static int cz_dpm_vce_force_lowest(struct amdgpu_device *adev) +{ + struct cz_power_info *pi = cz_get_pi(adev); + int ret = 0; + + if (pi->vce_dpm.soft_max_clk != pi->vce_dpm.soft_min_clk) { + pi->vce_dpm.soft_max_clk = pi->vce_dpm.soft_min_clk; + ret = cz_send_msg_to_smc_with_parameter(adev, + PPSMC_MSG_SetEclkSoftMax, + cz_get_uvd_level(adev, + pi->vce_dpm.soft_max_clk, + PPSMC_MSG_SetEclkSoftMax)); + if (ret) + return ret; + } + + return ret; +} + +static uint32_t cz_dpm_get_max_vce_level(struct amdgpu_device *adev) +{ + struct cz_power_info *pi = cz_get_pi(adev); + + if (!pi->max_vce_level) { + cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxEclkLevel); + pi->max_vce_level = cz_get_argument(adev) + 1; + } + + if (pi->max_vce_level > CZ_MAX_HARDWARE_POWERLEVELS) { + DRM_ERROR("Invalid max vce level!\n"); + return -EINVAL; + } + + return pi->max_vce_level; +} + +static int cz_dpm_unforce_vce_dpm_levels(struct amdgpu_device *adev) +{ + struct cz_power_info *pi = cz_get_pi(adev); + struct amdgpu_vce_clock_voltage_dependency_table *dep_table = + &adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table; + uint32_t level = 0; + int ret = 0; + + pi->vce_dpm.soft_min_clk = dep_table->entries[0].ecclk; + level = cz_dpm_get_max_vce_level(adev) - 1; + if (level < dep_table->count) + pi->vce_dpm.soft_max_clk = dep_table->entries[level].ecclk; + else + pi->vce_dpm.soft_max_clk = + dep_table->entries[dep_table->count - 1].ecclk; + + /* get min/max sclk soft value + * notify SMU to execute */ + ret = cz_send_msg_to_smc_with_parameter(adev, + PPSMC_MSG_SetEclkSoftMin, + cz_get_eclk_level(adev, + pi->vce_dpm.soft_min_clk, + PPSMC_MSG_SetEclkSoftMin)); + if (ret) + return ret; + + ret = cz_send_msg_to_smc_with_parameter(adev, + PPSMC_MSG_SetEclkSoftMax, + cz_get_eclk_level(adev, + pi->vce_dpm.soft_max_clk, + PPSMC_MSG_SetEclkSoftMax)); + if (ret) + return ret; + + DRM_DEBUG("DPM vce unforce state min=%d, max=%d.\n", + pi->vce_dpm.soft_min_clk, + pi->vce_dpm.soft_max_clk); + + return 0; +} + static int cz_dpm_force_dpm_level(struct amdgpu_device *adev, enum amdgpu_dpm_forced_level level) { @@ -1746,25 +1971,70 @@ static int cz_dpm_force_dpm_level(struct amdgpu_device *adev, switch (level) { case AMDGPU_DPM_FORCED_LEVEL_HIGH: + /* sclk */ ret = cz_dpm_unforce_dpm_levels(adev); if (ret) return ret; ret = cz_dpm_force_highest(adev); if (ret) return ret; + + /* uvd */ + ret = cz_dpm_unforce_uvd_dpm_levels(adev); + if (ret) + return ret; + ret = cz_dpm_uvd_force_highest(adev); + if (ret) + return ret; + + /* vce */ + ret = cz_dpm_unforce_vce_dpm_levels(adev); + if (ret) + return ret; + ret = cz_dpm_vce_force_highest(adev); + if (ret) + return ret; break; case AMDGPU_DPM_FORCED_LEVEL_LOW: + /* sclk */ ret = cz_dpm_unforce_dpm_levels(adev); if (ret) return ret; ret = cz_dpm_force_lowest(adev); if (ret) return ret; + + /* uvd */ + ret = cz_dpm_unforce_uvd_dpm_levels(adev); + if (ret) + return ret; + ret = cz_dpm_uvd_force_lowest(adev); + if (ret) + return ret; + + /* vce */ + ret = cz_dpm_unforce_vce_dpm_levels(adev); + if (ret) + return ret; + ret = cz_dpm_vce_force_lowest(adev); + if (ret) + return ret; break; case AMDGPU_DPM_FORCED_LEVEL_AUTO: + /* sclk */ ret = cz_dpm_unforce_dpm_levels(adev); if (ret) return ret; + + /* uvd */ + ret = cz_dpm_unforce_uvd_dpm_levels(adev); + if (ret) + return ret; + + /* vce */ + ret = cz_dpm_unforce_vce_dpm_levels(adev); + if (ret) + return ret; break; default: break; @@ -1905,7 +2175,8 @@ static int cz_update_vce_dpm(struct amdgpu_device *adev) pi->vce_dpm.hard_min_clk = table->entries[table->count-1].ecclk; } else { /* non-stable p-state cases. without vce.Arbiter.EcclkHardMin */ - pi->vce_dpm.hard_min_clk = table->entries[0].ecclk; + /* leave it as set by user */ + /*pi->vce_dpm.hard_min_clk = table->entries[0].ecclk;*/ } cz_send_msg_to_smc_with_parameter(adev, |