Vega20 simplifies the OD logics and it can not fit old OD interfaces. Thus we design new OD interfaces for vega20. Change-Id: I888faec46a81287ae24f452ce16b42c1f6d06d7d Signed-off-by: Evan Quan <evan.quan at amd.com> --- drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h | 8 + drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c | 125 ++++++++++++ .../gpu/drm/amd/include/kgd_pp_interface.h | 2 + drivers/gpu/drm/amd/powerplay/amd_powerplay.c | 37 ++++ .../drm/amd/powerplay/hwmgr/vega20_hwmgr.c | 191 +++++++++++++++++- drivers/gpu/drm/amd/powerplay/inc/hwmgr.h | 2 + 6 files changed, 362 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h index ff24e1cc5b65..84b3e6f87abf 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h @@ -357,6 +357,14 @@ enum amdgpu_pcie_gen { ((adev)->powerplay.pp_funcs->odn_edit_dpm_table(\ (adev)->powerplay.pp_handle, type, parameter, size)) +#define amdgpu_dpm_get_od8_settings(adev, buf) \ + ((adev)->powerplay.pp_funcs->get_od8_settings(\ + (adev)->powerplay.pp_handle, buf)) + +#define amdgpu_dpm_set_od8_settings(adev, parameter, size) \ + ((adev)->powerplay.pp_funcs->set_od8_settings(\ + (adev)->powerplay.pp_handle, parameter, size)) + struct amdgpu_dpm { struct amdgpu_ps *ps; /* number of valid power states */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c index daa55fb06171..94cd7c503372 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c @@ -934,6 +934,121 @@ static ssize_t amdgpu_get_busy_percent(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", value); } +/** + * DOC: pp_od8_settings + * + * The amdgpu driver provides a sysfs API for adjusting the clocks, voltages, + * power limit, fan speed and temperature. The pp_od8_settings is used for + * this. + * + * Reading the file will display: + * + * - a name list of the features that are able to be adjusted + * + * - the mininum and maximum allowed value for each supported + * feature labeled in format of "[mininum - maximum]" + * + * - the current value for each supported feature labeled after + * ":" + * + * To manually adjust these settings: + * + * - write a string that contains the new value for each supported + * feature. For those which do not need to be changed, just enter + * their old value + * + * - make sure the new value is within the allowed ranges between + * the minimum and maximum + * + * All the possible supported features: + * + * - Gfx clock + * The min and max frequency can be specified by GfxclkFmin + * and GfxclkFmax(both in Mhz). Intermediate levels are + * stretched/shrunk according to ratio in original table. + * + * - Gfx voltage + * With three pairs of gfx clk(in Mhz) and offset voltage(in mV) + * combinations specified, smu fw can calibrate the voltage + * curve automatically. + * - GfxclkFreq1 <-> GfxclkOffsetVolt1 + * - GfxclkFreq2 <-> GfxclkOffsetVolt2 + * - GfxclkFreq3 <-> GfxclkOffsetVolt3 + * + * - Uclk + * The max memory clock can be specified by UclkFmax(in Mhz). + * + * - Power limit + * The power limit can be increased or descreased by + * OverDrivePct(in percent). + * + * - Fan spped + * The max fan speed can be set by FanMaxinumRpm and the + * min fan speed by FanMinimumPwm. Also zero rpm enablement + * is specified by FanZeroRpmEnable. + * + * - Temperature + * The fan target temperature can be set by FanTargetTemperature + * and max Tj temperature by MaxOpTemp. + * + */ + +static ssize_t amdgpu_get_pp_od8_settings(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->get_od8_settings) + return amdgpu_dpm_get_od8_settings(adev, buf); + + return snprintf(buf, PAGE_SIZE, "\n"); +} + +static ssize_t amdgpu_set_pp_od8_settings(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; + char *sub_str, *tmp_str, buf_cpy[128]; + const char delimiter[3] = {' ', '\n', '\0'}; + uint32_t parameter_size = 0; + long parameter[64]; + int ret = 0xff; + + if (count >= sizeof(buf_cpy) / sizeof(buf_cpy[0])) + return -EINVAL; + + memcpy(buf_cpy, buf, count); + buf_cpy[count] = '\0'; + + tmp_str = buf_cpy; + + while (isspace(*tmp_str)) tmp_str++; + + while (tmp_str[0]) { + sub_str = strsep(&tmp_str, delimiter); + ret = kstrtol(sub_str, 0, ¶meter[parameter_size]); + if (ret) + return -EINVAL; + parameter_size++; + + while (isspace(*tmp_str)) + tmp_str++; + } + + if (adev->powerplay.pp_funcs->set_od8_settings) + ret = amdgpu_dpm_set_od8_settings(adev, parameter, parameter_size); + + if (ret) + return -EINVAL; + + return count; +} + 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, @@ -969,6 +1084,9 @@ static DEVICE_ATTR(pp_od_clk_voltage, S_IRUGO | S_IWUSR, amdgpu_set_pp_od_clk_voltage); static DEVICE_ATTR(gpu_busy_percent, S_IRUGO, amdgpu_get_busy_percent, NULL); +static DEVICE_ATTR(pp_od8_settings, S_IRUGO | S_IWUSR, + amdgpu_get_pp_od8_settings, + amdgpu_set_pp_od8_settings); static ssize_t amdgpu_hwmon_show_temp(struct device *dev, struct device_attribute *attr, @@ -1879,6 +1997,13 @@ int amdgpu_pm_sysfs_init(struct amdgpu_device *adev) "gpu_busy_level\n"); return ret; } + ret = device_create_file(adev->dev, + &dev_attr_pp_od8_settings); + if (ret) { + DRM_ERROR("failed to create device file " + "pp_od8_settings\n"); + return ret; + } ret = amdgpu_debugfs_pm_init(adev); if (ret) { DRM_ERROR("Failed to register debugfs file for dpm!\n"); diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h b/drivers/gpu/drm/amd/include/kgd_pp_interface.h index 6a41b81c7325..e23746ba53bf 100644 --- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h @@ -269,6 +269,8 @@ struct amd_pm_funcs { int (*get_display_mode_validation_clocks)(void *handle, struct amd_pp_simple_clock_info *clocks); int (*notify_smu_enable_pwe)(void *handle); + int (*get_od8_settings)(void *handle, char *buf); + int (*set_od8_settings)(void *handle, long *input, uint32_t size); }; #endif diff --git a/drivers/gpu/drm/amd/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/powerplay/amd_powerplay.c index da4ebff5b74d..4bcd06306698 100644 --- a/drivers/gpu/drm/amd/powerplay/amd_powerplay.c +++ b/drivers/gpu/drm/amd/powerplay/amd_powerplay.c @@ -883,6 +883,41 @@ static int pp_odn_edit_dpm_table(void *handle, uint32_t type, long *input, uint3 return hwmgr->hwmgr_func->odn_edit_dpm_table(hwmgr, type, input, size); } +static int pp_get_od8_settings(void *handle, char *buf) +{ + struct pp_hwmgr *hwmgr = handle; + + if (!hwmgr || !hwmgr->pm_en || !buf) + return -EINVAL; + + if (hwmgr->hwmgr_func->get_od8_settings == NULL) { + pr_info("%s was not implemented.\n", __func__); + return snprintf(buf, PAGE_SIZE, "\n"); + } + + return hwmgr->hwmgr_func->get_od8_settings(hwmgr, buf); +} + +static int pp_set_od8_settings(void *handle, long *input, uint32_t size) +{ + struct pp_hwmgr *hwmgr = handle; + int ret = -EINVAL; + + if (!hwmgr || !hwmgr->pm_en) + return ret; + + if (hwmgr->hwmgr_func->set_od8_settings == NULL) { + pr_info("%s was not implemented.\n", __func__); + return ret; + } + + mutex_lock(&hwmgr->smu_lock); + ret = hwmgr->hwmgr_func->set_od8_settings(hwmgr, input, size); + mutex_unlock(&hwmgr->smu_lock); + + return ret; +} + static int pp_dpm_switch_power_profile(void *handle, enum PP_SMC_POWER_PROFILE type, bool en) { @@ -1272,6 +1307,8 @@ static const struct amd_pm_funcs pp_dpm_funcs = { .get_power_profile_mode = pp_get_power_profile_mode, .set_power_profile_mode = pp_set_power_profile_mode, .odn_edit_dpm_table = pp_odn_edit_dpm_table, + .get_od8_settings = pp_get_od8_settings, + .set_od8_settings = pp_set_od8_settings, .set_power_limit = pp_set_power_limit, .get_power_limit = pp_get_power_limit, /* export to DC */ diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c index fb32b28afa66..ececa2f7fe5f 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c @@ -1099,7 +1099,188 @@ static int vega20_od8_initialize_default_settings( return 0; } -static int vega20_od8_set_settings( +static int vega20_get_od8_settings( + struct pp_hwmgr *hwmgr, + char *buf) +{ + struct vega20_hwmgr *data = + (struct vega20_hwmgr *)(hwmgr->backend); + struct vega20_od8_single_setting *od8_settings = + data->od8_settings.od8_settings_array; + OverDriveTable_t od_table; + uint32_t size = 0; + int ret = 0; + + if (!buf) + return -EINVAL; + + ret = vega20_copy_table_from_smc(hwmgr, + (uint8_t *)(&od_table), + TABLE_OVERDRIVE); + PP_ASSERT_WITH_CODE(!ret, + "Failed to export over drive table!", + return ret); + + if (od8_settings[OD8_SETTING_GFXCLK_FMIN].feature_id) + size += sprintf(buf + size, "%22s[%d - %d]: %u\n", "GfxclkFmin", + od8_settings[OD8_SETTING_GFXCLK_FMIN].min_value, + od8_settings[OD8_SETTING_GFXCLK_FMIN].max_value, + od_table.GfxclkFmin); + + if (od8_settings[OD8_SETTING_GFXCLK_FMAX].feature_id) + size += sprintf(buf + size, "%22s[%d - %d]: %u\n", "GfxclkFmax", + od8_settings[OD8_SETTING_GFXCLK_FMAX].min_value, + od8_settings[OD8_SETTING_GFXCLK_FMAX].max_value, + od_table.GfxclkFmax); + + if (od8_settings[OD8_SETTING_GFXCLK_FREQ1].feature_id) + size += sprintf(buf + size, "%22s[%d - %d]: %u\n", "GfxclkFreq1", + od8_settings[OD8_SETTING_GFXCLK_FREQ1].min_value, + od8_settings[OD8_SETTING_GFXCLK_FREQ1].max_value, + od_table.GfxclkFreq1); + + if (od8_settings[OD8_SETTING_GFXCLK_VOLTAGE1].feature_id) + size += sprintf(buf + size, "%22s[%d - %d]: %u\n", "GfxclkOffsetVolt1", + od8_settings[OD8_SETTING_GFXCLK_VOLTAGE1].min_value, + od8_settings[OD8_SETTING_GFXCLK_VOLTAGE1].max_value, + od_table.GfxclkOffsetVolt1); + + if (od8_settings[OD8_SETTING_GFXCLK_FREQ2].feature_id) + size += sprintf(buf + size, "%22s[%d - %d]: %u\n", "GfxclkFreq2", + od8_settings[OD8_SETTING_GFXCLK_FREQ2].min_value, + od8_settings[OD8_SETTING_GFXCLK_FREQ2].max_value, + od_table.GfxclkFreq2); + + if (od8_settings[OD8_SETTING_GFXCLK_VOLTAGE2].feature_id) + size += sprintf(buf + size, "%22s[%d - %d]: %u\n", "GfxclkOffsetVolt2", + od8_settings[OD8_SETTING_GFXCLK_VOLTAGE2].min_value, + od8_settings[OD8_SETTING_GFXCLK_VOLTAGE2].max_value, + od_table.GfxclkOffsetVolt2); + + if (od8_settings[OD8_SETTING_GFXCLK_FREQ3].feature_id) + size += sprintf(buf + size, "%22s[%d - %d]: %u\n", "GfxclkFreq3", + od8_settings[OD8_SETTING_GFXCLK_FREQ3].min_value, + od8_settings[OD8_SETTING_GFXCLK_FREQ3].max_value, + od_table.GfxclkFreq3); + + if (od8_settings[OD8_SETTING_GFXCLK_VOLTAGE3].feature_id) + size += sprintf(buf + size, "%22s[%d - %d]: %u\n", "GfxclkOffsetVolt3", + od8_settings[OD8_SETTING_GFXCLK_VOLTAGE3].min_value, + od8_settings[OD8_SETTING_GFXCLK_VOLTAGE3].max_value, + od_table.GfxclkOffsetVolt3); + + if (od8_settings[OD8_SETTING_UCLK_FMAX].feature_id) + size += sprintf(buf + size, "%22s[%d - %d]: %u\n", "UclkFmax", + od8_settings[OD8_SETTING_UCLK_FMAX].min_value, + od8_settings[OD8_SETTING_UCLK_FMAX].max_value, + od_table.UclkFmax); + + if (od8_settings[OD8_SETTING_POWER_PERCENTAGE].feature_id) + size += sprintf(buf + size, "%22s[%d - %d]: %u\n", "OverDrivePct", + od8_settings[OD8_SETTING_POWER_PERCENTAGE].min_value, + od8_settings[OD8_SETTING_POWER_PERCENTAGE].max_value, + od_table.OverDrivePct); + + if (od8_settings[OD8_SETTING_FAN_ACOUSTIC_LIMIT].feature_id) + size += sprintf(buf + size, "%22s[%d - %d]: %u\n", "FanMaximumRpm", + od8_settings[OD8_SETTING_FAN_ACOUSTIC_LIMIT].min_value, + od8_settings[OD8_SETTING_FAN_ACOUSTIC_LIMIT].max_value, + od_table.FanMaximumRpm); + + if (od8_settings[OD8_SETTING_FAN_MIN_SPEED].feature_id) + size += sprintf(buf + size, "%22s[%d - %d]: %u\n", "FanMinimumPwm", + od8_settings[OD8_SETTING_FAN_MIN_SPEED].min_value, + od8_settings[OD8_SETTING_FAN_MIN_SPEED].max_value, + od_table.FanMinimumPwm); + + if (od8_settings[OD8_SETTING_FAN_TARGET_TEMP].feature_id) + size += sprintf(buf + size, "%22s[%d - %d]: %u\n", "FanTargetTemperautre", + od8_settings[OD8_SETTING_FAN_TARGET_TEMP].min_value, + od8_settings[OD8_SETTING_FAN_TARGET_TEMP].max_value, + od_table.FanTargetTemperature); + + if (od8_settings[OD8_SETTING_OPERATING_TEMP_MAX].feature_id) + size += sprintf(buf + size, "%22s[%d - %d]: %u\n", "MaxOpTemp", + od8_settings[OD8_SETTING_OPERATING_TEMP_MAX].min_value, + od8_settings[OD8_SETTING_OPERATING_TEMP_MAX].max_value, + od_table.MaxOpTemp); + + if (od8_settings[OD8_SETTING_FAN_ZERO_RPM_CONTROL].feature_id) + size += sprintf(buf + size, "%22s[%d - %d]: %u\n", "FanZeroRpmEnable", + od8_settings[OD8_SETTING_FAN_ZERO_RPM_CONTROL].min_value, + od8_settings[OD8_SETTING_FAN_ZERO_RPM_CONTROL].max_value, + od_table.FanZeroRpmEnable); + + return size; +} + +static int vega20_set_od8_settings( + struct pp_hwmgr *hwmgr, + long *input, + uint32_t size) +{ + struct vega20_hwmgr *data = + (struct vega20_hwmgr *)(hwmgr->backend); + struct vega20_od8_single_setting *od8_settings = + data->od8_settings.od8_settings_array; + OverDriveTable_t od_table; + uint16_t *buf = (uint16_t *)(&od_table); + uint32_t i, j = 0, k; + int ret = 0; + + /* retrieve current overdrive settings */ + ret = vega20_copy_table_from_smc(hwmgr, + (uint8_t *)(&od_table), + TABLE_OVERDRIVE); + PP_ASSERT_WITH_CODE(!ret, + "Failed to export over drive table!", + return ret); + + for (i = 0; + i < (sizeof(OverDriveTable_t) / sizeof(uint16_t)) - 1; + i++) { + /* overdrive table supports no dram timing setting */ + k = (i == OD8_SETTING_AC_TIMING) ? + OD8_SETTING_FAN_ZERO_RPM_CONTROL : i; + /* + * Not all OD settings are supported and exported + * to user. + * So, updated only those which are supported and + * exported to user. + */ + if (od8_settings[k].feature_id) { + if (input[j] < od8_settings[k].min_value || + input[j] > od8_settings[k].max_value) + return -EINVAL; + + buf[i] = input[j]; + if (j++ >= size - 1) + break; + } + } + + /* enable the new overdrive settings */ + ret = vega20_copy_table_to_smc(hwmgr, + (uint8_t *)(&od_table), + TABLE_OVERDRIVE); + PP_ASSERT_WITH_CODE(!ret, + "Failed to import over drive table!", + return ret); + + /* update the current_value of overdrive settings */ + for (i = 0; + i < (sizeof(OverDriveTable_t) / sizeof(uint16_t)) - 1; + i++) { + k = (i == OD8_SETTING_AC_TIMING) ? + OD8_SETTING_FAN_ZERO_RPM_CONTROL : i; + if (od8_settings[k].feature_id) + od8_settings[k].current_value = buf[i]; + } + + return 0; +} + +static int vega20_od8_set_single_setting( struct pp_hwmgr *hwmgr, uint32_t index, uint32_t value) @@ -1198,7 +1379,7 @@ static int vega20_set_sclk_od( do_div(od_sclk, 100); od_sclk += golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value; - ret = vega20_od8_set_settings(hwmgr, OD8_SETTING_GFXCLK_FMAX, od_sclk); + ret = vega20_od8_set_single_setting(hwmgr, OD8_SETTING_GFXCLK_FMAX, od_sclk); PP_ASSERT_WITH_CODE(!ret, "[SetSclkOD] failed to set od gfxclk!", return ret); @@ -1245,7 +1426,7 @@ static int vega20_set_mclk_od( do_div(od_mclk, 100); od_mclk += golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value; - ret = vega20_od8_set_settings(hwmgr, OD8_SETTING_UCLK_FMAX, od_mclk); + ret = vega20_od8_set_single_setting(hwmgr, OD8_SETTING_UCLK_FMAX, od_mclk); PP_ASSERT_WITH_CODE(!ret, "[SetMclkOD] failed to set od memclk!", return ret); @@ -2966,6 +3147,10 @@ static const struct pp_hwmgr_func vega20_hwmgr_funcs = { vega20_get_power_profile_mode, .set_power_profile_mode = vega20_set_power_profile_mode, + .get_od8_settings = + vega20_get_od8_settings, + .set_od8_settings = + vega20_set_od8_settings, /* od related */ .set_power_limit = vega20_set_power_limit, diff --git a/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h index a6d92128b19c..285af1728e2e 100644 --- a/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h +++ b/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h @@ -328,6 +328,8 @@ struct pp_hwmgr_func { int (*set_power_limit)(struct pp_hwmgr *hwmgr, uint32_t n); int (*powergate_mmhub)(struct pp_hwmgr *hwmgr); int (*smus_notify_pwe)(struct pp_hwmgr *hwmgr); + int (*get_od8_settings)(struct pp_hwmgr *hwmgr, char *buf); + int (*set_od8_settings)(struct pp_hwmgr *hwmgr, long *input, uint32_t size); }; struct pp_table_func { -- 2.18.0