drm/amdgpu/cz: add code to enable forcing UVD clocks
authorAlex Deucher <alexander.deucher@amd.com>
Fri, 18 Dec 2015 16:06:42 +0000 (11:06 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Wed, 13 Jan 2016 17:11:30 +0000 (12:11 -0500)
UVD DPM works similarly to SCLK DPM.  Add a similar interface
for UVD for forcing the UVD clocks.

Reviewed-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/amdgpu/cz_dpm.c
drivers/gpu/drm/amd/amdgpu/cz_dpm.h

index 8035d4d6a4f57f9ff880d8fe1895285734a9bb2b..5ccea9f9c634262a0e85874c50bb6c2e2996fc29 100644 (file)
@@ -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,104 @@ 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_force_dpm_level(struct amdgpu_device *adev,
                                  enum amdgpu_dpm_forced_level level)
 {
index 99e1afc896294c90c13f7e3ed0cc2ceff2aae1d7..6a6a6fe7967f2752f35438ddc84b15219638dac9 100644 (file)
@@ -183,6 +183,7 @@ struct cz_power_info {
        uint32_t voltage_drop_threshold;
        uint32_t gfx_pg_threshold;
        uint32_t max_sclk_level;
+       uint32_t max_uvd_level;
        /* flags */
        bool didt_enabled;
        bool video_start;