[PATCH] drm/amd/powerplay: expose vega20 OD features
Evan Quan
evan.quan at amd.com
Fri Aug 24 07:43:49 UTC 2018
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
More information about the amd-gfx
mailing list