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))
(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 */
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,
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,
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) {
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)
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)
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,
.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,
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 {
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 {
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);