drm/amd/amdgpu: add power profile sysfs entry
authorEric Huang <JinHuiEric.Huang@amd.com>
Mon, 12 Sep 2016 20:17:44 +0000 (16:17 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Thu, 30 Mar 2017 03:52:48 +0000 (23:52 -0400)
Add the sysfs entries pp_gfx_power_profile and
pp_compute_power_profile which give user a way to set
power profile through parameters minimum sclk, minimum mclk,
activity threshold, up hysteresis and down hysteresis only
when the entry power_dpm_force_performance_level is in
default value "auto". It is read and write. Example:

echo 500 800 20 0 5 > /sys/class/drm/card0/device/pp_*_power_profile

cat /sys/class/drm/card0/device/pp_*_power_profile
500 800 20 0 5

Note: first parameter is sclk in MHz, second is mclk in MHz,
third is activity threshold in percentage, fourth is up hysteresis
in ms and fifth is down hysteresis in ms.

echo set > /sys/class/drm/card0/device/pp_*_power_profile
To set power profile state if it exists.

echo reset > /sys/class/drm/card0/device/pp_*_power_profile
To restore default state and clear previous setting.

Signed-off-by: Eric Huang <JinHuiEric.Huang@amd.com>
Acked-by: Rex Zhu <Rex.Zhu@amd.com>
Acked-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h
drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c
drivers/gpu/drm/amd/include/amd_shared.h
drivers/gpu/drm/amd/powerplay/amd_powerplay.c
drivers/gpu/drm/amd/powerplay/inc/amd_powerplay.h
drivers/gpu/drm/amd/powerplay/inc/hwmgr.h

index fa2b5568142288fa372f39e33e2b1f9fbd75b161..5f09893c96745bbc63fcac371cf9fff73d92d45b 100644 (file)
@@ -272,6 +272,14 @@ struct amdgpu_dpm_funcs {
                                bool *equal);
 
        struct amd_vce_state* (*get_vce_clock_state)(struct amdgpu_device *adev, unsigned idx);
+       int (*reset_power_profile_state)(struct amdgpu_device *adev,
+                       struct amd_pp_profile *request);
+       int (*get_power_profile_state)(struct amdgpu_device *adev,
+                       struct amd_pp_profile *query);
+       int (*set_power_profile_state)(struct amdgpu_device *adev,
+                       struct amd_pp_profile *request);
+       int (*switch_power_profile)(struct amdgpu_device *adev,
+                       enum amd_pp_profile_type type);
 };
 
 #define amdgpu_dpm_pre_set_power_state(adev) (adev)->pm.funcs->pre_set_power_state((adev))
@@ -388,6 +396,22 @@ struct amdgpu_dpm_funcs {
        (adev)->powerplay.pp_funcs->get_performance_level((adev)->powerplay.pp_handle) : \
        (adev)->pm.dpm.forced_level)
 
+#define amdgpu_dpm_reset_power_profile_state(adev, request) \
+       ((adev)->powerplay.pp_funcs->reset_power_profile_state(\
+                       (adev)->powerplay.pp_handle, request))
+
+#define amdgpu_dpm_get_power_profile_state(adev, query) \
+       ((adev)->powerplay.pp_funcs->get_power_profile_state(\
+                       (adev)->powerplay.pp_handle, query))
+
+#define amdgpu_dpm_set_power_profile_state(adev, request) \
+       ((adev)->powerplay.pp_funcs->set_power_profile_state(\
+                       (adev)->powerplay.pp_handle, request))
+
+#define amdgpu_dpm_switch_power_profile(adev, type) \
+       ((adev)->powerplay.pp_funcs->switch_power_profile(\
+                       (adev)->powerplay.pp_handle, type))
+
 struct amdgpu_dpm {
        struct amdgpu_ps        *ps;
        /* number of valid power states */
index 346e80a7119b32e2091f2f73958d4e345874b602..fd45212c4b98a64a3d13be09ad9e2b1a5191ea6d 100644 (file)
@@ -610,6 +610,174 @@ fail:
        return count;
 }
 
+static ssize_t amdgpu_get_pp_power_profile(struct device *dev,
+               char *buf, struct amd_pp_profile *query)
+{
+       struct drm_device *ddev = dev_get_drvdata(dev);
+       struct amdgpu_device *adev = ddev->dev_private;
+       int ret = 0;
+
+       if (adev->pp_enabled)
+               ret = amdgpu_dpm_get_power_profile_state(
+                               adev, query);
+       else if (adev->pm.funcs->get_power_profile_state)
+               ret = adev->pm.funcs->get_power_profile_state(
+                               adev, query);
+
+       if (ret)
+               return ret;
+
+       return snprintf(buf, PAGE_SIZE,
+                       "%d %d %d %d %d\n",
+                       query->min_sclk / 100,
+                       query->min_mclk / 100,
+                       query->activity_threshold,
+                       query->up_hyst,
+                       query->down_hyst);
+}
+
+static ssize_t amdgpu_get_pp_gfx_power_profile(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct amd_pp_profile query = {0};
+
+       query.type = AMD_PP_GFX_PROFILE;
+
+       return amdgpu_get_pp_power_profile(dev, buf, &query);
+}
+
+static ssize_t amdgpu_get_pp_compute_power_profile(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct amd_pp_profile query = {0};
+
+       query.type = AMD_PP_COMPUTE_PROFILE;
+
+       return amdgpu_get_pp_power_profile(dev, buf, &query);
+}
+
+static ssize_t amdgpu_set_pp_power_profile(struct device *dev,
+               const char *buf,
+               size_t count,
+               struct amd_pp_profile *request)
+{
+       struct drm_device *ddev = dev_get_drvdata(dev);
+       struct amdgpu_device *adev = ddev->dev_private;
+       uint32_t loop = 0;
+       char *sub_str, buf_cpy[128], *tmp_str;
+       const char delimiter[3] = {' ', '\n', '\0'};
+       long int value;
+       int ret = 0;
+
+       if (strncmp("reset", buf, strlen("reset")) == 0) {
+               if (adev->pp_enabled)
+                       ret = amdgpu_dpm_reset_power_profile_state(
+                                       adev, request);
+               else if (adev->pm.funcs->reset_power_profile_state)
+                       ret = adev->pm.funcs->reset_power_profile_state(
+                                       adev, request);
+               if (ret) {
+                       count = -EINVAL;
+                       goto fail;
+               }
+               return count;
+       }
+
+       if (strncmp("set", buf, strlen("set")) == 0) {
+               if (adev->pp_enabled)
+                       ret = amdgpu_dpm_set_power_profile_state(
+                                       adev, request);
+               else if (adev->pm.funcs->set_power_profile_state)
+                       ret = adev->pm.funcs->set_power_profile_state(
+                                       adev, request);
+               if (ret) {
+                       count = -EINVAL;
+                       goto fail;
+               }
+               return count;
+       }
+
+       if (count + 1 >= 128) {
+               count = -EINVAL;
+               goto fail;
+       }
+
+       memcpy(buf_cpy, buf, count + 1);
+       tmp_str = buf_cpy;
+
+       while (tmp_str[0]) {
+               sub_str = strsep(&tmp_str, delimiter);
+               ret = kstrtol(sub_str, 0, &value);
+               if (ret) {
+                       count = -EINVAL;
+                       goto fail;
+               }
+
+               switch (loop) {
+               case 0:
+                       /* input unit MHz convert to dpm table unit 10KHz*/
+                       request->min_sclk = (uint32_t)value * 100;
+                       break;
+               case 1:
+                       /* input unit MHz convert to dpm table unit 10KHz*/
+                       request->min_mclk = (uint32_t)value * 100;
+                       break;
+               case 2:
+                       request->activity_threshold = (uint16_t)value;
+                       break;
+               case 3:
+                       request->up_hyst = (uint8_t)value;
+                       break;
+               case 4:
+                       request->down_hyst = (uint8_t)value;
+                       break;
+               default:
+                       break;
+               }
+
+               loop++;
+       }
+
+       if (adev->pp_enabled)
+               ret = amdgpu_dpm_set_power_profile_state(
+                               adev, request);
+       else if (adev->pm.funcs->set_power_profile_state)
+               ret = adev->pm.funcs->set_power_profile_state(
+                               adev, request);
+
+       if (ret)
+               count = -EINVAL;
+
+fail:
+       return count;
+}
+
+static ssize_t amdgpu_set_pp_gfx_power_profile(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf,
+               size_t count)
+{
+       struct amd_pp_profile request = {0};
+
+       request.type = AMD_PP_GFX_PROFILE;
+
+       return amdgpu_set_pp_power_profile(dev, buf, count, &request);
+}
+
+static ssize_t amdgpu_set_pp_compute_power_profile(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf,
+               size_t count)
+{
+       struct amd_pp_profile request = {0};
+
+       request.type = AMD_PP_COMPUTE_PROFILE;
+
+       return amdgpu_set_pp_power_profile(dev, buf, count, &request);
+}
+
 static DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, amdgpu_get_dpm_state, amdgpu_set_dpm_state);
 static DEVICE_ATTR(power_dpm_force_performance_level, S_IRUGO | S_IWUSR,
                   amdgpu_get_dpm_forced_performance_level,
@@ -637,6 +805,12 @@ static DEVICE_ATTR(pp_sclk_od, S_IRUGO | S_IWUSR,
 static DEVICE_ATTR(pp_mclk_od, S_IRUGO | S_IWUSR,
                amdgpu_get_pp_mclk_od,
                amdgpu_set_pp_mclk_od);
+static DEVICE_ATTR(pp_gfx_power_profile, S_IRUGO | S_IWUSR,
+               amdgpu_get_pp_gfx_power_profile,
+               amdgpu_set_pp_gfx_power_profile);
+static DEVICE_ATTR(pp_compute_power_profile, S_IRUGO | S_IWUSR,
+               amdgpu_get_pp_compute_power_profile,
+               amdgpu_set_pp_compute_power_profile);
 
 static ssize_t amdgpu_hwmon_show_temp(struct device *dev,
                                      struct device_attribute *attr,
@@ -1255,6 +1429,20 @@ int amdgpu_pm_sysfs_init(struct amdgpu_device *adev)
                DRM_ERROR("failed to create device file pp_mclk_od\n");
                return ret;
        }
+       ret = device_create_file(adev->dev,
+                       &dev_attr_pp_gfx_power_profile);
+       if (ret) {
+               DRM_ERROR("failed to create device file "
+                               "pp_gfx_power_profile\n");
+               return ret;
+       }
+       ret = device_create_file(adev->dev,
+                       &dev_attr_pp_compute_power_profile);
+       if (ret) {
+               DRM_ERROR("failed to create device file "
+                               "pp_compute_power_profile\n");
+               return ret;
+       }
 
        ret = amdgpu_debugfs_pm_init(adev);
        if (ret) {
@@ -1284,6 +1472,10 @@ void amdgpu_pm_sysfs_fini(struct amdgpu_device *adev)
        device_remove_file(adev->dev, &dev_attr_pp_dpm_pcie);
        device_remove_file(adev->dev, &dev_attr_pp_sclk_od);
        device_remove_file(adev->dev, &dev_attr_pp_mclk_od);
+       device_remove_file(adev->dev,
+                       &dev_attr_pp_gfx_power_profile);
+       device_remove_file(adev->dev,
+                       &dev_attr_pp_compute_power_profile);
 }
 
 void amdgpu_pm_compute_clocks(struct amdgpu_device *adev)
index 43f45adeccd1a1aecfba1471fa749f868455b57a..f7425c384a3fd3f142691224c4087ac166216225 100644 (file)
@@ -120,6 +120,20 @@ enum amd_vce_level {
        AMD_VCE_LEVEL_DC_GP_HIGH = 5, /* DC, general purpose queue, 1080 >= res > 720 */
 };
 
+enum amd_pp_profile_type {
+       AMD_PP_GFX_PROFILE,
+       AMD_PP_COMPUTE_PROFILE,
+};
+
+struct amd_pp_profile {
+       enum amd_pp_profile_type type;
+       uint32_t min_sclk;
+       uint32_t min_mclk;
+       uint16_t activity_threshold;
+       uint8_t up_hyst;
+       uint8_t down_hyst;
+};
+
 /* CG flags */
 #define AMD_CG_SUPPORT_GFX_MGCG                        (1 << 0)
 #define AMD_CG_SUPPORT_GFX_MGLS                        (1 << 1)
index e9cf207b9dcf3ba5d89c4385a5956826875543f9..8074386da36f83c4d81daf14e3c49b0109dbd1dd 100644 (file)
@@ -917,10 +917,140 @@ pp_dpm_get_vce_clock_state(void *handle, unsigned idx)
 
        if (hwmgr && idx < hwmgr->num_vce_state_tables)
                return &hwmgr->vce_states[idx];
-
        return NULL;
 }
 
+static int pp_dpm_reset_power_profile_state(void *handle,
+               struct amd_pp_profile *request)
+{
+       struct pp_hwmgr *hwmgr;
+       struct pp_instance *pp_handle = (struct pp_instance *)handle;
+
+       if (!request || pp_check(pp_handle))
+               return -EINVAL;
+
+       hwmgr = pp_handle->hwmgr;
+
+       if (hwmgr->hwmgr_func->set_power_profile_state == NULL) {
+               pr_info("%s was not implemented.\n", __func__);
+               return 0;
+       }
+
+       if (request->type == AMD_PP_GFX_PROFILE) {
+               hwmgr->gfx_power_profile = hwmgr->default_gfx_power_profile;
+               return hwmgr->hwmgr_func->set_power_profile_state(hwmgr,
+                               &hwmgr->gfx_power_profile);
+       } else if (request->type == AMD_PP_COMPUTE_PROFILE) {
+               hwmgr->compute_power_profile =
+                               hwmgr->default_compute_power_profile;
+               return hwmgr->hwmgr_func->set_power_profile_state(hwmgr,
+                               &hwmgr->compute_power_profile);
+       } else
+               return -EINVAL;
+}
+
+static int pp_dpm_get_power_profile_state(void *handle,
+               struct amd_pp_profile *query)
+{
+       struct pp_hwmgr *hwmgr;
+       struct pp_instance *pp_handle = (struct pp_instance *)handle;
+
+       if (!query || pp_check(pp_handle))
+               return -EINVAL;
+
+       hwmgr = pp_handle->hwmgr;
+
+       if (query->type == AMD_PP_GFX_PROFILE)
+               memcpy(query, &hwmgr->gfx_power_profile,
+                               sizeof(struct amd_pp_profile));
+       else if (query->type == AMD_PP_COMPUTE_PROFILE)
+               memcpy(query, &hwmgr->compute_power_profile,
+                               sizeof(struct amd_pp_profile));
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static int pp_dpm_set_power_profile_state(void *handle,
+               struct amd_pp_profile *request)
+{
+       struct pp_hwmgr *hwmgr;
+       struct pp_instance *pp_handle = (struct pp_instance *)handle;
+       int ret = -1;
+
+       if (!request || pp_check(pp_handle))
+               return -EINVAL;
+
+       hwmgr = pp_handle->hwmgr;
+
+       if (hwmgr->hwmgr_func->set_power_profile_state == NULL) {
+               pr_info("%s was not implemented.\n", __func__);
+               return 0;
+       }
+
+       if (request->min_sclk ||
+               request->min_mclk ||
+               request->activity_threshold ||
+               request->up_hyst ||
+               request->down_hyst) {
+               if (request->type == AMD_PP_GFX_PROFILE)
+                       memcpy(&hwmgr->gfx_power_profile, request,
+                                       sizeof(struct amd_pp_profile));
+               else if (request->type == AMD_PP_COMPUTE_PROFILE)
+                       memcpy(&hwmgr->compute_power_profile, request,
+                                       sizeof(struct amd_pp_profile));
+               else
+                       return -EINVAL;
+
+               if (request->type == hwmgr->current_power_profile)
+                       ret = hwmgr->hwmgr_func->set_power_profile_state(
+                                       hwmgr,
+                                       request);
+       } else {
+               /* set power profile if it exists */
+               switch (request->type) {
+               case AMD_PP_GFX_PROFILE:
+                       ret = hwmgr->hwmgr_func->set_power_profile_state(
+                                       hwmgr,
+                                       &hwmgr->gfx_power_profile);
+                       break;
+               case AMD_PP_COMPUTE_PROFILE:
+                       ret = hwmgr->hwmgr_func->set_power_profile_state(
+                                       hwmgr,
+                                       &hwmgr->compute_power_profile);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       if (!ret)
+               hwmgr->current_power_profile = request->type;
+
+       return 0;
+}
+
+static int pp_dpm_switch_power_profile(void *handle,
+               enum amd_pp_profile_type type)
+{
+       struct pp_hwmgr *hwmgr;
+       struct amd_pp_profile request = {0};
+       struct pp_instance *pp_handle = (struct pp_instance *)handle;
+
+       if (pp_check(pp_handle))
+               return -EINVAL;
+
+       hwmgr = pp_handle->hwmgr;
+
+       if (hwmgr->current_power_profile != type) {
+               request.type = type;
+               pp_dpm_set_power_profile_state(handle, &request);
+       }
+
+       return 0;
+}
+
 const struct amd_powerplay_funcs pp_dpm_funcs = {
        .get_temperature = pp_dpm_get_temperature,
        .load_firmware = pp_dpm_load_fw,
@@ -949,6 +1079,10 @@ const struct amd_powerplay_funcs pp_dpm_funcs = {
        .set_mclk_od = pp_dpm_set_mclk_od,
        .read_sensor = pp_dpm_read_sensor,
        .get_vce_clock_state = pp_dpm_get_vce_clock_state,
+       .reset_power_profile_state = pp_dpm_reset_power_profile_state,
+       .get_power_profile_state = pp_dpm_get_power_profile_state,
+       .set_power_profile_state = pp_dpm_set_power_profile_state,
+       .switch_power_profile = pp_dpm_switch_power_profile,
 };
 
 int amd_powerplay_create(struct amd_pp_init *pp_init,
index 6dd5f0e9ef87c6e55307d61ee1c8f9f787d9a205..4b4f5ff2f039351c27d68cc28bc382b75d50e0c4 100644 (file)
@@ -361,6 +361,14 @@ struct amd_powerplay_funcs {
        int (*set_mclk_od)(void *handle, uint32_t value);
        int (*read_sensor)(void *handle, int idx, int32_t *value);
        struct amd_vce_state* (*get_vce_clock_state)(void *handle, unsigned idx);
+       int (*reset_power_profile_state)(void *handle,
+                       struct amd_pp_profile *request);
+       int (*get_power_profile_state)(void *handle,
+                       struct amd_pp_profile *query);
+       int (*set_power_profile_state)(void *handle,
+                       struct amd_pp_profile *request);
+       int (*switch_power_profile)(void *handle,
+                       enum amd_pp_profile_type type);
 };
 
 struct amd_powerplay {
index 7275a29293eb4e0d3422c0903e8f6cbc7273ac6d..768f81f365cac09cda1a2e05d5474bf3216afa23 100644 (file)
@@ -358,6 +358,8 @@ struct pp_hwmgr_func {
        int (*read_sensor)(struct pp_hwmgr *hwmgr, int idx, int32_t *value);
        int (*request_firmware)(struct pp_hwmgr *hwmgr);
        int (*release_firmware)(struct pp_hwmgr *hwmgr);
+       int (*set_power_profile_state)(struct pp_hwmgr *hwmgr,
+                       struct amd_pp_profile *request);
 };
 
 struct pp_table_func {
@@ -650,6 +652,13 @@ struct pp_hwmgr {
        struct pp_power_state    *uvd_ps;
        struct amd_pp_display_configuration display_config;
        uint32_t feature_mask;
+
+       /* power profile */
+       struct amd_pp_profile gfx_power_profile;
+       struct amd_pp_profile compute_power_profile;
+       struct amd_pp_profile default_gfx_power_profile;
+       struct amd_pp_profile default_compute_power_profile;
+       enum amd_pp_profile_type current_power_profile;
 };
 
 extern int hwmgr_early_init(struct pp_instance *handle);