drm/amd/powerplay: support retrieving and adjusting dcefclock power levels V2
authorEvan Quan <evan.quan@amd.com>
Mon, 14 Jan 2019 09:37:26 +0000 (17:37 +0800)
committerAlex Deucher <alexander.deucher@amd.com>
Fri, 25 Jan 2019 21:15:34 +0000 (16:15 -0500)
User can use "pp_dpm_dcefclk" to retrieve and adjust dcefclock power
levels.

V2: expose this interface for Vega10 and later ASICs only

Signed-off-by: Evan Quan <evan.quan@amd.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c
drivers/gpu/drm/amd/include/kgd_pp_interface.h
drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c

index 23eb6f6..4099c6b 100644 (file)
@@ -680,13 +680,16 @@ static ssize_t amdgpu_get_ppfeature_status(struct device *dev,
 }
 
 /**
- * DOC: pp_dpm_sclk pp_dpm_mclk pp_dpm_socclk pp_dpm_fclk pp_dpm_pcie
+ * DOC: pp_dpm_sclk pp_dpm_mclk pp_dpm_socclk pp_dpm_fclk pp_dpm_dcefclk
+ * pp_dpm_pcie
  *
  * The amdgpu driver provides a sysfs API for adjusting what power levels
  * are enabled for a given power state.  The files pp_dpm_sclk, pp_dpm_mclk,
- * pp_dpm_socclk, pp_dpm_fclk and pp_dpm_pcie are used for this.
+ * pp_dpm_socclk, pp_dpm_fclk, pp_dpm_dcefclk and pp_dpm_pcie are used for
+ * this.
  *
- * pp_dpm_socclk interface is only available for Vega10 and later ASICs.
+ * pp_dpm_socclk and pp_dpm_dcefclk interfaces are only available for
+ * Vega10 and later ASICs.
  * pp_dpm_fclk interface is only available for Vega20 and later ASICs.
  *
  * Reading back the files will show you the available power levels within
@@ -697,6 +700,8 @@ static ssize_t amdgpu_get_ppfeature_status(struct device *dev,
  * Secondly,Enter a new value for each level by inputing a string that
  * contains " echo xx xx xx > pp_dpm_sclk/mclk/pcie"
  * E.g., echo 4 5 6 to > pp_dpm_sclk will enable sclk levels 4, 5, and 6.
+ *
+ * NOTE: change to the dcefclk max dpm level is not supported now
  */
 
 static ssize_t amdgpu_get_pp_dpm_sclk(struct device *dev,
@@ -879,6 +884,42 @@ static ssize_t amdgpu_set_pp_dpm_fclk(struct device *dev,
        return count;
 }
 
+static ssize_t amdgpu_get_pp_dpm_dcefclk(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct drm_device *ddev = dev_get_drvdata(dev);
+       struct amdgpu_device *adev = ddev->dev_private;
+
+       if (adev->powerplay.pp_funcs->print_clock_levels)
+               return amdgpu_dpm_print_clock_levels(adev, PP_DCEFCLK, buf);
+       else
+               return snprintf(buf, PAGE_SIZE, "\n");
+}
+
+static ssize_t amdgpu_set_pp_dpm_dcefclk(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf,
+               size_t count)
+{
+       struct drm_device *ddev = dev_get_drvdata(dev);
+       struct amdgpu_device *adev = ddev->dev_private;
+       int ret;
+       uint32_t mask = 0;
+
+       ret = amdgpu_read_mask(buf, count, &mask);
+       if (ret)
+               return ret;
+
+       if (adev->powerplay.pp_funcs->force_clock_level)
+               ret = amdgpu_dpm_force_clock_level(adev, PP_DCEFCLK, mask);
+
+       if (ret)
+               return -EINVAL;
+
+       return count;
+}
+
 static ssize_t amdgpu_get_pp_dpm_pcie(struct device *dev,
                struct device_attribute *attr,
                char *buf)
@@ -1168,6 +1209,9 @@ static DEVICE_ATTR(pp_dpm_socclk, S_IRUGO | S_IWUSR,
 static DEVICE_ATTR(pp_dpm_fclk, S_IRUGO | S_IWUSR,
                amdgpu_get_pp_dpm_fclk,
                amdgpu_set_pp_dpm_fclk);
+static DEVICE_ATTR(pp_dpm_dcefclk, S_IRUGO | S_IWUSR,
+               amdgpu_get_pp_dpm_dcefclk,
+               amdgpu_set_pp_dpm_dcefclk);
 static DEVICE_ATTR(pp_dpm_pcie, S_IRUGO | S_IWUSR,
                amdgpu_get_pp_dpm_pcie,
                amdgpu_set_pp_dpm_pcie);
@@ -2333,6 +2377,11 @@ int amdgpu_pm_sysfs_init(struct amdgpu_device *adev)
                        DRM_ERROR("failed to create device file pp_dpm_socclk\n");
                        return ret;
                }
+               ret = device_create_file(adev->dev, &dev_attr_pp_dpm_dcefclk);
+               if (ret) {
+                       DRM_ERROR("failed to create device file pp_dpm_dcefclk\n");
+                       return ret;
+               }
        }
        if (adev->asic_type >= CHIP_VEGA20) {
                ret = device_create_file(adev->dev, &dev_attr_pp_dpm_fclk);
@@ -2428,8 +2477,10 @@ void amdgpu_pm_sysfs_fini(struct amdgpu_device *adev)
 
        device_remove_file(adev->dev, &dev_attr_pp_dpm_sclk);
        device_remove_file(adev->dev, &dev_attr_pp_dpm_mclk);
-       if (adev->asic_type >= CHIP_VEGA10)
+       if (adev->asic_type >= CHIP_VEGA10) {
                device_remove_file(adev->dev, &dev_attr_pp_dpm_socclk);
+               device_remove_file(adev->dev, &dev_attr_pp_dpm_dcefclk);
+       }
        device_remove_file(adev->dev, &dev_attr_pp_dpm_pcie);
        if (adev->asic_type >= CHIP_VEGA20)
                device_remove_file(adev->dev, &dev_attr_pp_dpm_fclk);
index f82de14..2b579ba 100644 (file)
@@ -94,6 +94,7 @@ enum pp_clock_type {
        PP_PCIE,
        PP_SOCCLK,
        PP_FCLK,
+       PP_DCEFCLK,
        OD_SCLK,
        OD_MCLK,
        OD_VDDC_CURVE,
index 4e7399c..8c1fa98 100644 (file)
@@ -1747,6 +1747,17 @@ static int vega20_upload_dpm_min_level(struct pp_hwmgr *hwmgr, uint32_t feature_
                                        return ret);
        }
 
+       if (data->smu_features[GNLD_DPM_DCEFCLK].enabled &&
+          (feature_mask & FEATURE_DPM_DCEFCLK_MASK)) {
+               min_freq = data->dpm_table.dcef_table.dpm_state.hard_min_level;
+
+               PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+                                       hwmgr, PPSMC_MSG_SetHardMinByFreq,
+                                       (PPCLK_DCEFCLK << 16) | (min_freq & 0xffff))),
+                                       "Failed to set hard min dcefclk!",
+                                       return ret);
+       }
+
        return ret;
 }
 
@@ -2259,7 +2270,7 @@ static int vega20_force_clock_level(struct pp_hwmgr *hwmgr,
                enum pp_clock_type type, uint32_t mask)
 {
        struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
-       uint32_t soft_min_level, soft_max_level;
+       uint32_t soft_min_level, soft_max_level, hard_min_level;
        int ret = 0;
 
        switch (type) {
@@ -2374,6 +2385,28 @@ static int vega20_force_clock_level(struct pp_hwmgr *hwmgr,
 
                break;
 
+       case PP_DCEFCLK:
+               hard_min_level = mask ? (ffs(mask) - 1) : 0;
+
+               if (hard_min_level >= data->dpm_table.dcef_table.count) {
+                       pr_err("Clock level specified %d is over max allowed %d\n",
+                                       hard_min_level,
+                                       data->dpm_table.dcef_table.count - 1);
+                       return -EINVAL;
+               }
+
+               data->dpm_table.dcef_table.dpm_state.hard_min_level =
+                       data->dpm_table.dcef_table.dpm_levels[hard_min_level].value;
+
+               ret = vega20_upload_dpm_min_level(hwmgr, FEATURE_DPM_DCEFCLK_MASK);
+               PP_ASSERT_WITH_CODE(!ret,
+                       "Failed to upload boot level to lowest!",
+                       return ret);
+
+               //TODO: Setting DCEFCLK max dpm level is not supported
+
+               break;
+
        case PP_PCIE:
                soft_min_level = mask ? (ffs(mask) - 1) : 0;
                soft_max_level = mask ? (fls(mask) - 1) : 0;
@@ -3040,6 +3073,23 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr,
                                fclk_dpm_table->dpm_levels[i].value == (now / 100) ? "*" : "");
                break;
 
+       case PP_DCEFCLK:
+               ret = vega20_get_current_clk_freq(hwmgr, PPCLK_DCEFCLK, &now);
+               PP_ASSERT_WITH_CODE(!ret,
+                               "Attempt to get current dcefclk freq Failed!",
+                               return ret);
+
+               ret = vega20_get_dcefclocks(hwmgr, &clocks);
+               PP_ASSERT_WITH_CODE(!ret,
+                               "Attempt to get dcefclk levels Failed!",
+                               return ret);
+
+               for (i = 0; i < clocks.num_levels; i++)
+                       size += sprintf(buf + size, "%d: %uMhz %s\n",
+                               i, clocks.data[i].clocks_in_khz / 1000,
+                               (clocks.data[i].clocks_in_khz == now * 10) ? "*" : "");
+               break;
+
        case PP_PCIE:
                gen_speed = (RREG32_PCIE(smnPCIE_LC_SPEED_CNTL) &
                             PSWUSP0_PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE_MASK)