aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/amd/powerplay
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/amd/powerplay')
-rw-r--r--drivers/gpu/drm/amd/powerplay/Makefile2
-rw-r--r--drivers/gpu/drm/amd/powerplay/amd_powerplay.c10
-rw-r--r--drivers/gpu/drm/amd/powerplay/amdgpu_smu.c1253
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/Makefile3
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c2
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/smu10_hwmgr.c127
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c32
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/smu9_baco.c66
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/smu9_baco.h31
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega10_baco.c39
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega10_baco.h5
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c39
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega12_baco.c119
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega12_baco.h29
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c5
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega12_inc.h2
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega20_baco.c12
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega20_baco.h1
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c53
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.h2
-rw-r--r--drivers/gpu/drm/amd/powerplay/inc/amdgpu_smu.h770
-rw-r--r--drivers/gpu/drm/amd/powerplay/inc/rv_ppsmc.h1
-rw-r--r--drivers/gpu/drm/amd/powerplay/inc/smu10.h14
-rw-r--r--drivers/gpu/drm/amd/powerplay/inc/smu_v11_0.h89
-rw-r--r--drivers/gpu/drm/amd/powerplay/inc/smu_v11_0_ppsmc.h128
-rw-r--r--drivers/gpu/drm/amd/powerplay/inc/smu_v11_0_pptable.h147
-rw-r--r--drivers/gpu/drm/amd/powerplay/inc/vega20_ppsmc.h3
-rw-r--r--drivers/gpu/drm/amd/powerplay/smu_v11_0.c1977
-rw-r--r--drivers/gpu/drm/amd/powerplay/smumgr/smu10_smumgr.c4
-rw-r--r--drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c20
-rw-r--r--drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.h1
-rw-r--r--drivers/gpu/drm/amd/powerplay/vega20_ppt.c2413
-rw-r--r--drivers/gpu/drm/amd/powerplay/vega20_ppt.h129
33 files changed, 7415 insertions, 113 deletions
diff --git a/drivers/gpu/drm/amd/powerplay/Makefile b/drivers/gpu/drm/amd/powerplay/Makefile
index 231785a9e24c..ec87b3430d12 100644
--- a/drivers/gpu/drm/amd/powerplay/Makefile
+++ b/drivers/gpu/drm/amd/powerplay/Makefile
@@ -35,7 +35,7 @@ AMD_POWERPLAY = $(addsuffix /Makefile,$(addprefix $(FULL_AMD_PATH)/powerplay/,$(
include $(AMD_POWERPLAY)
-POWER_MGR = amd_powerplay.o
+POWER_MGR = amd_powerplay.o amdgpu_smu.o smu_v11_0.o vega20_ppt.o
AMD_PP_POWER = $(addprefix $(AMD_PP_PATH)/,$(POWER_MGR))
diff --git a/drivers/gpu/drm/amd/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/powerplay/amd_powerplay.c
index 3f73f7cd18b9..bea1587d352d 100644
--- a/drivers/gpu/drm/amd/powerplay/amd_powerplay.c
+++ b/drivers/gpu/drm/amd/powerplay/amd_powerplay.c
@@ -53,7 +53,7 @@ static int amd_powerplay_create(struct amdgpu_device *adev)
mutex_init(&hwmgr->smu_lock);
hwmgr->chip_family = adev->family;
hwmgr->chip_id = adev->asic_type;
- hwmgr->feature_mask = adev->powerplay.pp_feature;
+ hwmgr->feature_mask = adev->pm.pp_feature;
hwmgr->display_config = &adev->pm.pm_display_cfg;
adev->powerplay.pp_handle = hwmgr;
adev->powerplay.pp_funcs = &pp_dpm_funcs;
@@ -1304,7 +1304,7 @@ static int pp_notify_smu_enable_pwe(void *handle)
if (hwmgr->hwmgr_func->smus_notify_pwe == NULL) {
pr_info_ratelimited("%s was not implemented.\n", __func__);
- return -EINVAL;;
+ return -EINVAL;
}
mutex_lock(&hwmgr->smu_lock);
@@ -1341,7 +1341,7 @@ static int pp_set_min_deep_sleep_dcefclk(void *handle, uint32_t clock)
if (hwmgr->hwmgr_func->set_min_deep_sleep_dcefclk == NULL) {
pr_debug("%s was not implemented.\n", __func__);
- return -EINVAL;;
+ return -EINVAL;
}
mutex_lock(&hwmgr->smu_lock);
@@ -1360,7 +1360,7 @@ static int pp_set_hard_min_dcefclk_by_freq(void *handle, uint32_t clock)
if (hwmgr->hwmgr_func->set_hard_min_dcefclk_by_freq == NULL) {
pr_debug("%s was not implemented.\n", __func__);
- return -EINVAL;;
+ return -EINVAL;
}
mutex_lock(&hwmgr->smu_lock);
@@ -1379,7 +1379,7 @@ static int pp_set_hard_min_fclk_by_freq(void *handle, uint32_t clock)
if (hwmgr->hwmgr_func->set_hard_min_fclk_by_freq == NULL) {
pr_debug("%s was not implemented.\n", __func__);
- return -EINVAL;;
+ return -EINVAL;
}
mutex_lock(&hwmgr->smu_lock);
diff --git a/drivers/gpu/drm/amd/powerplay/amdgpu_smu.c b/drivers/gpu/drm/amd/powerplay/amdgpu_smu.c
new file mode 100644
index 000000000000..eec329ab6037
--- /dev/null
+++ b/drivers/gpu/drm/amd/powerplay/amdgpu_smu.c
@@ -0,0 +1,1253 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "pp_debug.h"
+#include <linux/firmware.h>
+#include <drm/drmP.h>
+#include "amdgpu.h"
+#include "amdgpu_smu.h"
+#include "soc15_common.h"
+#include "smu_v11_0.h"
+#include "atom.h"
+#include "amd_pcie.h"
+
+int smu_dpm_set_power_gate(struct smu_context *smu, uint32_t block_type,
+ bool gate)
+{
+ int ret = 0;
+
+ switch (block_type) {
+ case AMD_IP_BLOCK_TYPE_UVD:
+ ret = smu_dpm_set_uvd_enable(smu, gate);
+ break;
+ case AMD_IP_BLOCK_TYPE_VCE:
+ ret = smu_dpm_set_vce_enable(smu, gate);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+enum amd_pm_state_type smu_get_current_power_state(struct smu_context *smu)
+{
+ /* not support power state */
+ return POWER_STATE_TYPE_DEFAULT;
+}
+
+int smu_get_power_num_states(struct smu_context *smu,
+ struct pp_states_info *state_info)
+{
+ if (!state_info)
+ return -EINVAL;
+
+ /* not support power state */
+ memset(state_info, 0, sizeof(struct pp_states_info));
+ state_info->nums = 0;
+
+ return 0;
+}
+
+int smu_common_read_sensor(struct smu_context *smu, enum amd_pp_sensors sensor,
+ void *data, uint32_t *size)
+{
+ int ret = 0;
+
+ switch (sensor) {
+ case AMDGPU_PP_SENSOR_STABLE_PSTATE_SCLK:
+ *((uint32_t *)data) = smu->pstate_sclk;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_STABLE_PSTATE_MCLK:
+ *((uint32_t *)data) = smu->pstate_mclk;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_ENABLED_SMC_FEATURES_MASK:
+ ret = smu_feature_get_enabled_mask(smu, (uint32_t *)data, 2);
+ *size = 8;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret)
+ *size = 0;
+
+ return ret;
+}
+
+int smu_update_table_with_arg(struct smu_context *smu, uint16_t table_id, uint16_t exarg,
+ void *table_data, bool drv2smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *table = NULL;
+ int ret = 0;
+ uint32_t table_index;
+
+ if (!table_data || table_id >= smu_table->table_count)
+ return -EINVAL;
+
+ table_index = (exarg << 16) | table_id;
+
+ table = &smu_table->tables[table_id];
+
+ if (drv2smu)
+ memcpy(table->cpu_addr, table_data, table->size);
+
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_SetDriverDramAddrHigh,
+ upper_32_bits(table->mc_address));
+ if (ret)
+ return ret;
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_SetDriverDramAddrLow,
+ lower_32_bits(table->mc_address));
+ if (ret)
+ return ret;
+ ret = smu_send_smc_msg_with_param(smu, drv2smu ?
+ SMU_MSG_TransferTableDram2Smu :
+ SMU_MSG_TransferTableSmu2Dram,
+ table_index);
+ if (ret)
+ return ret;
+
+ if (!drv2smu)
+ memcpy(table_data, table->cpu_addr, table->size);
+
+ return ret;
+}
+
+bool is_support_sw_smu(struct amdgpu_device *adev)
+{
+ if (amdgpu_dpm != 1)
+ return false;
+
+ if (adev->asic_type >= CHIP_VEGA20 && adev->asic_type != CHIP_RAVEN)
+ return true;
+
+ return false;
+}
+
+int smu_sys_get_pp_table(struct smu_context *smu, void **table)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+
+ if (!smu_table->power_play_table && !smu_table->hardcode_pptable)
+ return -EINVAL;
+
+ if (smu_table->hardcode_pptable)
+ *table = smu_table->hardcode_pptable;
+ else
+ *table = smu_table->power_play_table;
+
+ return smu_table->power_play_table_size;
+}
+
+int smu_sys_set_pp_table(struct smu_context *smu, void *buf, size_t size)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ ATOM_COMMON_TABLE_HEADER *header = (ATOM_COMMON_TABLE_HEADER *)buf;
+ int ret = 0;
+
+ if (header->usStructureSize != size) {
+ pr_err("pp table size not matched !\n");
+ return -EIO;
+ }
+
+ mutex_lock(&smu->mutex);
+ if (!smu_table->hardcode_pptable)
+ smu_table->hardcode_pptable = kzalloc(size, GFP_KERNEL);
+ if (!smu_table->hardcode_pptable) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ memcpy(smu_table->hardcode_pptable, buf, size);
+ smu_table->power_play_table = smu_table->hardcode_pptable;
+ smu_table->power_play_table_size = size;
+ mutex_unlock(&smu->mutex);
+
+ ret = smu_reset(smu);
+ if (ret)
+ pr_info("smu reset failed, ret = %d\n", ret);
+
+ return ret;
+
+failed:
+ mutex_unlock(&smu->mutex);
+ return ret;
+}
+
+int smu_feature_init_dpm(struct smu_context *smu)
+{
+ struct smu_feature *feature = &smu->smu_feature;
+ int ret = 0;
+ uint32_t unallowed_feature_mask[SMU_FEATURE_MAX/32];
+
+ mutex_lock(&feature->mutex);
+ bitmap_fill(feature->allowed, SMU_FEATURE_MAX);
+ mutex_unlock(&feature->mutex);
+
+ ret = smu_get_unallowed_feature_mask(smu, unallowed_feature_mask,
+ SMU_FEATURE_MAX/32);
+ if (ret)
+ return ret;
+
+ mutex_lock(&feature->mutex);
+ bitmap_andnot(feature->allowed, feature->allowed,
+ (unsigned long *)unallowed_feature_mask,
+ feature->feature_num);
+ mutex_unlock(&feature->mutex);
+
+ return ret;
+}
+
+int smu_feature_is_enabled(struct smu_context *smu, int feature_id)
+{
+ struct smu_feature *feature = &smu->smu_feature;
+ int ret = 0;
+
+ WARN_ON(feature_id > feature->feature_num);
+
+ mutex_lock(&feature->mutex);
+ ret = test_bit(feature_id, feature->enabled);
+ mutex_unlock(&feature->mutex);
+
+ return ret;
+}
+
+int smu_feature_set_enabled(struct smu_context *smu, int feature_id, bool enable)
+{
+ struct smu_feature *feature = &smu->smu_feature;
+ int ret = 0;
+
+ WARN_ON(feature_id > feature->feature_num);
+
+ mutex_lock(&feature->mutex);
+ ret = smu_feature_update_enable_state(smu, feature_id, enable);
+ if (ret)
+ goto failed;
+
+ if (enable)
+ test_and_set_bit(feature_id, feature->enabled);
+ else
+ test_and_clear_bit(feature_id, feature->enabled);
+
+failed:
+ mutex_unlock(&feature->mutex);
+
+ return ret;
+}
+
+int smu_feature_is_supported(struct smu_context *smu, int feature_id)
+{
+ struct smu_feature *feature = &smu->smu_feature;
+ int ret = 0;
+
+ WARN_ON(feature_id > feature->feature_num);
+
+ mutex_lock(&feature->mutex);
+ ret = test_bit(feature_id, feature->supported);
+ mutex_unlock(&feature->mutex);
+
+ return ret;
+}
+
+int smu_feature_set_supported(struct smu_context *smu, int feature_id,
+ bool enable)
+{
+ struct smu_feature *feature = &smu->smu_feature;
+ int ret = 0;
+
+ WARN_ON(feature_id > feature->feature_num);
+
+ mutex_lock(&feature->mutex);
+ if (enable)
+ test_and_set_bit(feature_id, feature->supported);
+ else
+ test_and_clear_bit(feature_id, feature->supported);
+ mutex_unlock(&feature->mutex);
+
+ return ret;
+}
+
+static int smu_set_funcs(struct amdgpu_device *adev)
+{
+ struct smu_context *smu = &adev->smu;
+
+ switch (adev->asic_type) {
+ case CHIP_VEGA20:
+ adev->pm.pp_feature &= ~PP_GFXOFF_MASK;
+ if (adev->pm.pp_feature & PP_OVERDRIVE_MASK)
+ smu->od_enabled = true;
+ smu_v11_0_set_smu_funcs(smu);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smu_early_init(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct smu_context *smu = &adev->smu;
+
+ smu->adev = adev;
+ mutex_init(&smu->mutex);
+
+ return smu_set_funcs(adev);
+}
+
+static int smu_late_init(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct smu_context *smu = &adev->smu;
+ mutex_lock(&smu->mutex);
+ smu_handle_task(&adev->smu,
+ smu->smu_dpm.dpm_level,
+ AMD_PP_TASK_COMPLETE_INIT);
+ mutex_unlock(&smu->mutex);
+
+ return 0;
+}
+
+int smu_get_atom_data_table(struct smu_context *smu, uint32_t table,
+ uint16_t *size, uint8_t *frev, uint8_t *crev,
+ uint8_t **addr)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint16_t data_start;
+
+ if (!amdgpu_atom_parse_data_header(adev->mode_info.atom_context, table,
+ size, frev, crev, &data_start))
+ return -EINVAL;
+
+ *addr = (uint8_t *)adev->mode_info.atom_context->bios + data_start;
+
+ return 0;
+}
+
+static int smu_initialize_pptable(struct smu_context *smu)
+{
+ /* TODO */
+ return 0;
+}
+
+static int smu_smc_table_sw_init(struct smu_context *smu)
+{
+ int ret;
+
+ ret = smu_initialize_pptable(smu);
+ if (ret) {
+ pr_err("Failed to init smu_initialize_pptable!\n");
+ return ret;
+ }
+
+ /**
+ * Create smu_table structure, and init smc tables such as
+ * TABLE_PPTABLE, TABLE_WATERMARKS, TABLE_SMU_METRICS, and etc.
+ */
+ ret = smu_init_smc_tables(smu);
+ if (ret) {
+ pr_err("Failed to init smc tables!\n");
+ return ret;
+ }
+
+ /**
+ * Create smu_power_context structure, and allocate smu_dpm_context and
+ * context size to fill the smu_power_context data.
+ */
+ ret = smu_init_power(smu);
+ if (ret) {
+ pr_err("Failed to init smu_init_power!\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int smu_smc_table_sw_fini(struct smu_context *smu)
+{
+ int ret;
+
+ ret = smu_fini_smc_tables(smu);
+ if (ret) {
+ pr_err("Failed to smu_fini_smc_tables!\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int smu_sw_init(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct smu_context *smu = &adev->smu;
+ int ret;
+
+ if (!is_support_sw_smu(adev))
+ return -EINVAL;
+
+ smu->pool_size = adev->pm.smu_prv_buffer_size;
+ smu->smu_feature.feature_num = SMU_FEATURE_MAX;
+ mutex_init(&smu->smu_feature.mutex);
+ bitmap_zero(smu->smu_feature.supported, SMU_FEATURE_MAX);
+ bitmap_zero(smu->smu_feature.enabled, SMU_FEATURE_MAX);
+ bitmap_zero(smu->smu_feature.allowed, SMU_FEATURE_MAX);
+ smu->watermarks_bitmap = 0;
+ smu->power_profile_mode = PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT;
+ smu->default_power_profile_mode = PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT;
+
+ smu->workload_mask = 1 << smu->workload_prority[PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT];
+ smu->workload_prority[PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT] = 0;
+ smu->workload_prority[PP_SMC_POWER_PROFILE_FULLSCREEN3D] = 1;
+ smu->workload_prority[PP_SMC_POWER_PROFILE_POWERSAVING] = 2;
+ smu->workload_prority[PP_SMC_POWER_PROFILE_VIDEO] = 3;
+ smu->workload_prority[PP_SMC_POWER_PROFILE_VR] = 4;
+ smu->workload_prority[PP_SMC_POWER_PROFILE_COMPUTE] = 5;
+ smu->workload_prority[PP_SMC_POWER_PROFILE_CUSTOM] = 6;
+
+ smu->workload_setting[0] = PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT;
+ smu->workload_setting[1] = PP_SMC_POWER_PROFILE_FULLSCREEN3D;
+ smu->workload_setting[2] = PP_SMC_POWER_PROFILE_POWERSAVING;
+ smu->workload_setting[3] = PP_SMC_POWER_PROFILE_VIDEO;
+ smu->workload_setting[4] = PP_SMC_POWER_PROFILE_VR;
+ smu->workload_setting[5] = PP_SMC_POWER_PROFILE_COMPUTE;
+ smu->workload_setting[6] = PP_SMC_POWER_PROFILE_CUSTOM;
+ smu->display_config = &adev->pm.pm_display_cfg;
+
+ smu->smu_dpm.dpm_level = AMD_DPM_FORCED_LEVEL_AUTO;
+ smu->smu_dpm.requested_dpm_level = AMD_DPM_FORCED_LEVEL_AUTO;
+ ret = smu_init_microcode(smu);
+ if (ret) {
+ pr_err("Failed to load smu firmware!\n");
+ return ret;
+ }
+
+ ret = smu_smc_table_sw_init(smu);
+ if (ret) {
+ pr_err("Failed to sw init smc table!\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int smu_sw_fini(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct smu_context *smu = &adev->smu;
+ int ret;
+
+ if (!is_support_sw_smu(adev))
+ return -EINVAL;
+
+ ret = smu_smc_table_sw_fini(smu);
+ if (ret) {
+ pr_err("Failed to sw fini smc table!\n");
+ return ret;
+ }
+
+ ret = smu_fini_power(smu);
+ if (ret) {
+ pr_err("Failed to init smu_fini_power!\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int smu_init_fb_allocations(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *tables = smu_table->tables;
+ uint32_t table_count = smu_table->table_count;
+ uint32_t i = 0;
+ int32_t ret = 0;
+
+ if (table_count <= 0)
+ return -EINVAL;
+
+ for (i = 0 ; i < table_count; i++) {
+ if (tables[i].size == 0)
+ continue;
+ ret = amdgpu_bo_create_kernel(adev,
+ tables[i].size,
+ tables[i].align,
+ tables[i].domain,
+ &tables[i].bo,
+ &tables[i].mc_address,
+ &tables[i].cpu_addr);
+ if (ret)
+ goto failed;
+ }
+
+ return 0;
+failed:
+ for (; i > 0; i--) {
+ if (tables[i].size == 0)
+ continue;
+ amdgpu_bo_free_kernel(&tables[i].bo,
+ &tables[i].mc_address,
+ &tables[i].cpu_addr);
+
+ }
+ return ret;
+}
+
+static int smu_fini_fb_allocations(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *tables = smu_table->tables;
+ uint32_t table_count = smu_table->table_count;
+ uint32_t i = 0;
+
+ if (table_count == 0 || tables == NULL)
+ return 0;
+
+ for (i = 0 ; i < table_count; i++) {
+ if (tables[i].size == 0)
+ continue;
+ amdgpu_bo_free_kernel(&tables[i].bo,
+ &tables[i].mc_address,
+ &tables[i].cpu_addr);
+ }
+
+ return 0;
+}
+
+static int smu_override_pcie_parameters(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t pcie_gen = 0, pcie_width = 0, smu_pcie_arg;
+ int ret;
+
+ if (adev->pm.pcie_gen_mask & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN4)
+ pcie_gen = 3;
+ else if (adev->pm.pcie_gen_mask & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN3)
+ pcie_gen = 2;
+ else if (adev->pm.pcie_gen_mask & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN2)
+ pcie_gen = 1;
+ else if (adev->pm.pcie_gen_mask & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN1)
+ pcie_gen = 0;
+
+ /* Bit 31:16: LCLK DPM level. 0 is DPM0, and 1 is DPM1
+ * Bit 15:8: PCIE GEN, 0 to 3 corresponds to GEN1 to GEN4
+ * Bit 7:0: PCIE lane width, 1 to 7 corresponds is x1 to x32
+ */
+ if (adev->pm.pcie_mlw_mask & CAIL_PCIE_LINK_WIDTH_SUPPORT_X16)
+ pcie_width = 6;
+ else if (adev->pm.pcie_mlw_mask & CAIL_PCIE_LINK_WIDTH_SUPPORT_X12)
+ pcie_width = 5;
+ else if (adev->pm.pcie_mlw_mask & CAIL_PCIE_LINK_WIDTH_SUPPORT_X8)
+ pcie_width = 4;
+ else if (adev->pm.pcie_mlw_mask & CAIL_PCIE_LINK_WIDTH_SUPPORT_X4)
+ pcie_width = 3;
+ else if (adev->pm.pcie_mlw_mask & CAIL_PCIE_LINK_WIDTH_SUPPORT_X2)
+ pcie_width = 2;
+ else if (adev->pm.pcie_mlw_mask & CAIL_PCIE_LINK_WIDTH_SUPPORT_X1)
+ pcie_width = 1;
+
+ smu_pcie_arg = (1 << 16) | (pcie_gen << 8) | pcie_width;
+ ret = smu_send_smc_msg_with_param(smu,
+ SMU_MSG_OverridePcieParameters,
+ smu_pcie_arg);
+ if (ret)
+ pr_err("[%s] Attempt to override pcie params failed!\n", __func__);
+ return ret;
+}
+
+static int smu_smc_table_hw_init(struct smu_context *smu,
+ bool initialize)
+{
+ struct amdgpu_device *adev = smu->adev;
+ int ret;
+
+ if (smu_is_dpm_running(smu) && adev->in_suspend) {
+ pr_info("dpm has been enabled\n");
+ return 0;
+ }
+
+ ret = smu_init_display(smu);
+ if (ret)
+ return ret;
+
+ if (initialize) {
+ ret = smu_read_pptable_from_vbios(smu);
+ if (ret)
+ return ret;
+
+ /* get boot_values from vbios to set revision, gfxclk, and etc. */
+ ret = smu_get_vbios_bootup_values(smu);
+ if (ret)
+ return ret;
+
+ ret = smu_get_clk_info_from_vbios(smu);
+ if (ret)
+ return ret;
+
+ /*
+ * check if the format_revision in vbios is up to pptable header
+ * version, and the structure size is not 0.
+ */
+ ret = smu_get_clk_info_from_vbios(smu);
+ if (ret)
+ return ret;
+
+ ret = smu_check_pptable(smu);
+ if (ret)
+ return ret;
+
+ /*
+ * allocate vram bos to store smc table contents.
+ */
+ ret = smu_init_fb_allocations(smu);
+ if (ret)
+ return ret;
+
+ /*
+ * Parse pptable format and fill PPTable_t smc_pptable to
+ * smu_table_context structure. And read the smc_dpm_table from vbios,
+ * then fill it into smc_pptable.
+ */
+ ret = smu_parse_pptable(smu);
+ if (ret)
+ return ret;
+
+ /*
+ * Send msg GetDriverIfVersion to check if the return value is equal
+ * with DRIVER_IF_VERSION of smc header.
+ */
+ ret = smu_check_fw_version(smu);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Copy pptable bo in the vram to smc with SMU MSGs such as
+ * SetDriverDramAddr and TransferTableDram2Smu.
+ */
+ ret = smu_write_pptable(smu);
+ if (ret)
+ return ret;
+
+ /* issue RunAfllBtc msg */
+ ret = smu_run_afll_btc(smu);
+ if (ret)
+ return ret;
+
+ ret = smu_feature_set_allowed_mask(smu);
+ if (ret)
+ return ret;
+
+ ret = smu_system_features_control(smu, true);
+ if (ret)
+ return ret;
+
+ ret = smu_override_pcie_parameters(smu);
+ if (ret)
+ return ret;
+
+ ret = smu_notify_display_change(smu);
+ if (ret)
+ return ret;
+
+ /*
+ * Set min deep sleep dce fclk with bootup value from vbios via
+ * SetMinDeepSleepDcefclk MSG.
+ */
+ ret = smu_set_min_dcef_deep_sleep(smu);
+ if (ret)
+ return ret;
+
+ /*
+ * Set initialized values (get from vbios) to dpm tables context such as
+ * gfxclk, memclk, dcefclk, and etc. And enable the DPM feature for each
+ * type of clks.
+ */
+ if (initialize) {
+ ret = smu_populate_smc_pptable(smu);
+ if (ret)
+ return ret;
+
+ ret = smu_init_max_sustainable_clocks(smu);
+ if (ret)
+ return ret;
+ }
+
+ ret = smu_set_od8_default_settings(smu, initialize);
+ if (ret)
+ return ret;
+
+ if (initialize) {
+ ret = smu_populate_umd_state_clk(smu);
+ if (ret)
+ return ret;
+
+ ret = smu_get_power_limit(smu, &smu->default_power_limit, false);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Set PMSTATUSLOG table bo address with SetToolsDramAddr MSG for tools.
+ */
+ ret = smu_set_tool_table_location(smu);
+
+ return ret;
+}
+
+/**
+ * smu_alloc_memory_pool - allocate memory pool in the system memory
+ *
+ * @smu: amdgpu_device pointer
+ *
+ * This memory pool will be used for SMC use and msg SetSystemVirtualDramAddr
+ * and DramLogSetDramAddr can notify it changed.
+ *
+ * Returns 0 on success, error on failure.
+ */
+static int smu_alloc_memory_pool(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *memory_pool = &smu_table->memory_pool;
+ uint64_t pool_size = smu->pool_size;
+ int ret = 0;
+
+ if (pool_size == SMU_MEMORY_POOL_SIZE_ZERO)
+ return ret;
+
+ memory_pool->size = pool_size;
+ memory_pool->align = PAGE_SIZE;
+ memory_pool->domain = AMDGPU_GEM_DOMAIN_GTT;
+
+ switch (pool_size) {
+ case SMU_MEMORY_POOL_SIZE_256_MB:
+ case SMU_MEMORY_POOL_SIZE_512_MB:
+ case SMU_MEMORY_POOL_SIZE_1_GB:
+ case SMU_MEMORY_POOL_SIZE_2_GB:
+ ret = amdgpu_bo_create_kernel(adev,
+ memory_pool->size,
+ memory_pool->align,
+ memory_pool->domain,
+ &memory_pool->bo,
+ &memory_pool->mc_address,
+ &memory_pool->cpu_addr);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int smu_free_memory_pool(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *memory_pool = &smu_table->memory_pool;
+ int ret = 0;
+
+ if (memory_pool->size == SMU_MEMORY_POOL_SIZE_ZERO)
+ return ret;
+
+ amdgpu_bo_free_kernel(&memory_pool->bo,
+ &memory_pool->mc_address,
+ &memory_pool->cpu_addr);
+
+ memset(memory_pool, 0, sizeof(struct smu_table));
+
+ return ret;
+}
+
+static int smu_hw_init(void *handle)
+{
+ int ret;
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct smu_context *smu = &adev->smu;
+
+ if (!is_support_sw_smu(adev))
+ return -EINVAL;
+
+ if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) {
+ ret = smu_load_microcode(smu);
+ if (ret)
+ return ret;
+ }
+
+ ret = smu_check_fw_status(smu);
+ if (ret) {
+ pr_err("SMC firmware status is not correct\n");
+ return ret;
+ }
+
+ mutex_lock(&smu->mutex);
+
+ ret = smu_feature_init_dpm(smu);
+ if (ret)
+ goto failed;
+
+ ret = smu_smc_table_hw_init(smu, true);
+ if (ret)
+ goto failed;
+
+ ret = smu_alloc_memory_pool(smu);
+ if (ret)
+ goto failed;
+
+ /*
+ * Use msg SetSystemVirtualDramAddr and DramLogSetDramAddr can notify
+ * pool location.
+ */
+ ret = smu_notify_memory_pool_location(smu);
+ if (ret)
+ goto failed;
+
+ ret = smu_start_thermal_control(smu);
+ if (ret)
+ goto failed;
+
+ mutex_unlock(&smu->mutex);
+
+ adev->pm.dpm_enabled = true;
+
+ pr_info("SMU is initialized successfully!\n");
+
+ return 0;
+
+failed:
+ mutex_unlock(&smu->mutex);
+ return ret;
+}
+
+static int smu_hw_fini(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct smu_context *smu = &adev->smu;
+ struct smu_table_context *table_context = &smu->smu_table;
+ int ret = 0;
+
+ if (!is_support_sw_smu(adev))
+ return -EINVAL;
+
+ kfree(table_context->driver_pptable);
+ table_context->driver_pptable = NULL;
+
+ kfree(table_context->max_sustainable_clocks);
+ table_context->max_sustainable_clocks = NULL;
+
+ kfree(table_context->od_feature_capabilities);
+ table_context->od_feature_capabilities = NULL;
+
+ kfree(table_context->od_settings_max);
+ table_context->od_settings_max = NULL;
+
+ kfree(table_context->od_settings_min);
+ table_context->od_settings_min = NULL;
+
+ kfree(table_context->overdrive_table);
+ table_context->overdrive_table = NULL;
+
+ kfree(table_context->od8_settings);
+ table_context->od8_settings = NULL;
+
+ ret = smu_fini_fb_allocations(smu);
+ if (ret)
+ return ret;
+
+ ret = smu_free_memory_pool(smu);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int smu_reset(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ int ret = 0;
+
+ ret = smu_hw_fini(adev);
+ if (ret)
+ return ret;
+
+ ret = smu_hw_init(adev);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static int smu_suspend(void *handle)
+{
+ int ret;
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct smu_context *smu = &adev->smu;
+
+ if (!is_support_sw_smu(adev))
+ return -EINVAL;
+
+ ret = smu_system_features_control(smu, false);
+ if (ret)
+ return ret;
+
+ smu->watermarks_bitmap &= ~(WATERMARKS_LOADED);
+
+ return 0;
+}
+
+static int smu_resume(void *handle)
+{
+ int ret;
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct smu_context *smu = &adev->smu;
+
+ if (!is_support_sw_smu(adev))
+ return -EINVAL;
+
+ pr_info("SMU is resuming...\n");
+
+ mutex_lock(&smu->mutex);
+
+ ret = smu_smc_table_hw_init(smu, false);
+ if (ret)
+ goto failed;
+
+ ret = smu_start_thermal_control(smu);
+ if (ret)
+ goto failed;
+
+ mutex_unlock(&smu->mutex);
+
+ pr_info("SMU is resumed successfully!\n");
+
+ return 0;
+failed:
+ mutex_unlock(&smu->mutex);
+ return ret;
+}
+
+int smu_display_configuration_change(struct smu_context *smu,
+ const struct amd_pp_display_configuration *display_config)
+{
+ int index = 0;
+ int num_of_active_display = 0;
+
+ if (!is_support_sw_smu(smu->adev))
+ return -EINVAL;
+
+ if (!display_config)
+ return -EINVAL;
+
+ mutex_lock(&smu->mutex);
+
+ smu_set_deep_sleep_dcefclk(smu,
+ display_config->min_dcef_deep_sleep_set_clk / 100);
+
+ for (index = 0; index < display_config->num_path_including_non_display; index++) {
+ if (display_config->displays[index].controller_id != 0)
+ num_of_active_display++;
+ }
+
+ smu_set_active_display_count(smu, num_of_active_display);
+
+ smu_store_cc6_data(smu, display_config->cpu_pstate_separation_time,
+ display_config->cpu_cc6_disable,
+ display_config->cpu_pstate_disable,
+ display_config->nb_pstate_switch_disable);
+
+ mutex_unlock(&smu->mutex);
+
+ return 0;
+}
+
+static int smu_get_clock_info(struct smu_context *smu,
+ struct smu_clock_info *clk_info,
+ enum smu_perf_level_designation designation)
+{
+ int ret;
+ struct smu_performance_level level = {0};
+
+ if (!clk_info)
+ return -EINVAL;
+
+ ret = smu_get_perf_level(smu, PERF_LEVEL_ACTIVITY, &level);
+ if (ret)
+ return -EINVAL;
+
+ clk_info->min_mem_clk = level.memory_clock;
+ clk_info->min_eng_clk = level.core_clock;
+ clk_info->min_bus_bandwidth = level.non_local_mem_freq * level.non_local_mem_width;
+
+ ret = smu_get_perf_level(smu, designation, &level);
+ if (ret)
+ return -EINVAL;
+
+ clk_info->min_mem_clk = level.memory_clock;
+ clk_info->min_eng_clk = level.core_clock;
+ clk_info->min_bus_bandwidth = level.non_local_mem_freq * level.non_local_mem_width;
+
+ return 0;
+}
+
+int smu_get_current_clocks(struct smu_context *smu,
+ struct amd_pp_clock_info *clocks)
+{
+ struct amd_pp_simple_clock_info simple_clocks = {0};
+ struct smu_clock_info hw_clocks;
+ int ret = 0;
+
+ if (!is_support_sw_smu(smu->adev))
+ return -EINVAL;
+
+ mutex_lock(&smu->mutex);
+
+ smu_get_dal_power_level(smu, &simple_clocks);
+
+ if (smu->support_power_containment)
+ ret = smu_get_clock_info(smu, &hw_clocks,
+ PERF_LEVEL_POWER_CONTAINMENT);
+ else
+ ret = smu_get_clock_info(smu, &hw_clocks, PERF_LEVEL_ACTIVITY);
+
+ if (ret) {
+ pr_err("Error in smu_get_clock_info\n");
+ goto failed;
+ }
+
+ clocks->min_engine_clock = hw_clocks.min_eng_clk;
+ clocks->max_engine_clock = hw_clocks.max_eng_clk;
+ clocks->min_memory_clock = hw_clocks.min_mem_clk;
+ clocks->max_memory_clock = hw_clocks.max_mem_clk;
+ clocks->min_bus_bandwidth = hw_clocks.min_bus_bandwidth;
+ clocks->max_bus_bandwidth = hw_clocks.max_bus_bandwidth;
+ clocks->max_engine_clock_in_sr = hw_clocks.max_eng_clk;
+ clocks->min_engine_clock_in_sr = hw_clocks.min_eng_clk;
+
+ if (simple_clocks.level == 0)
+ clocks->max_clocks_state = PP_DAL_POWERLEVEL_7;
+ else
+ clocks->max_clocks_state = simple_clocks.level;
+
+ if (!smu_get_current_shallow_sleep_clocks(smu, &hw_clocks)) {
+ clocks->max_engine_clock_in_sr = hw_clocks.max_eng_clk;
+ clocks->min_engine_clock_in_sr = hw_clocks.min_eng_clk;
+ }
+
+failed:
+ mutex_unlock(&smu->mutex);
+ return ret;
+}
+
+static int smu_set_clockgating_state(void *handle,
+ enum amd_clockgating_state state)
+{
+ return 0;
+}
+
+static int smu_set_powergating_state(void *handle,
+ enum amd_powergating_state state)
+{
+ return 0;
+}
+
+static int smu_enable_umd_pstate(void *handle,
+ enum amd_dpm_forced_level *level)
+{
+ uint32_t profile_mode_mask = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD |
+ AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK |
+ AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK |
+ AMD_DPM_FORCED_LEVEL_PROFILE_PEAK;
+
+ struct smu_context *smu = (struct smu_context*)(handle);
+ struct smu_dpm_context *smu_dpm_ctx = &(smu->smu_dpm);
+ if (!smu_dpm_ctx->dpm_context)
+ return -EINVAL;
+
+ if (!(smu_dpm_ctx->dpm_level & profile_mode_mask)) {
+ /* enter umd pstate, save current level, disable gfx cg*/
+ if (*level & profile_mode_mask) {
+ smu_dpm_ctx->saved_dpm_level = smu_dpm_ctx->dpm_level;
+ smu_dpm_ctx->enable_umd_pstate = true;
+ amdgpu_device_ip_set_clockgating_state(smu->adev,
+ AMD_IP_BLOCK_TYPE_GFX,
+ AMD_CG_STATE_UNGATE);
+ amdgpu_device_ip_set_powergating_state(smu->adev,
+ AMD_IP_BLOCK_TYPE_GFX,
+ AMD_PG_STATE_UNGATE);
+ }
+ } else {
+ /* exit umd pstate, restore level, enable gfx cg*/
+ if (!(*level & profile_mode_mask)) {
+ if (*level == AMD_DPM_FORCED_LEVEL_PROFILE_EXIT)
+ *level = smu_dpm_ctx->saved_dpm_level;
+ smu_dpm_ctx->enable_umd_pstate = false;
+ amdgpu_device_ip_set_clockgating_state(smu->adev,
+ AMD_IP_BLOCK_TYPE_GFX,
+ AMD_CG_STATE_GATE);
+ amdgpu_device_ip_set_powergating_state(smu->adev,
+ AMD_IP_BLOCK_TYPE_GFX,
+ AMD_PG_STATE_GATE);
+ }
+ }
+
+ return 0;
+}
+
+int smu_adjust_power_state_dynamic(struct smu_context *smu,
+ enum amd_dpm_forced_level level,
+ bool skip_display_settings)
+{
+ int ret = 0;
+ int index = 0;
+ uint32_t sclk_mask, mclk_mask, soc_mask;
+ long workload;
+ struct smu_dpm_context *smu_dpm_ctx = &(smu->smu_dpm);
+
+ if (!skip_display_settings) {
+ ret = smu_display_config_changed(smu);
+ if (ret) {
+ pr_err("Failed to change display config!");
+ return ret;
+ }
+ }
+
+ ret = smu_apply_clocks_adjust_rules(smu);
+ if (ret) {
+ pr_err("Failed to apply clocks adjust rules!");
+ return ret;
+ }
+
+ if (!skip_display_settings) {
+ ret = smu_notify_smc_dispaly_config(smu);
+ if (ret) {
+ pr_err("Failed to notify smc display config!");
+ return ret;
+ }
+ }
+
+ if (smu_dpm_ctx->dpm_level != level) {
+ switch (level) {
+ case AMD_DPM_FORCED_LEVEL_HIGH:
+ ret = smu_force_dpm_limit_value(smu, true);
+ break;
+ case AMD_DPM_FORCED_LEVEL_LOW:
+ ret = smu_force_dpm_limit_value(smu, false);
+ break;
+
+ case AMD_DPM_FORCED_LEVEL_AUTO:
+ ret = smu_unforce_dpm_levels(smu);
+ break;
+
+ case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+ ret = smu_get_profiling_clk_mask(smu, level,
+ &sclk_mask,
+ &mclk_mask,
+ &soc_mask);
+ if (ret)
+ return ret;
+ smu_force_clk_levels(smu, PP_SCLK, 1 << sclk_mask);
+ smu_force_clk_levels(smu, PP_MCLK, 1 << mclk_mask);
+ break;
+
+ case AMD_DPM_FORCED_LEVEL_MANUAL:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_EXIT:
+ default:
+ break;
+ }
+
+ if (!ret)
+ smu_dpm_ctx->dpm_level = level;
+ }
+
+ if (smu_dpm_ctx->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL) {
+ index = fls(smu->workload_mask);
+ index = index > 0 && index <= WORKLOAD_POLICY_MAX ? index - 1 : 0;
+ workload = smu->workload_setting[index];
+
+ if (smu->power_profile_mode != workload)
+ smu_set_power_profile_mode(smu, &workload, 0);
+ }
+
+ return ret;
+}
+
+int smu_handle_task(struct smu_context *smu,
+ enum amd_dpm_forced_level level,
+ enum amd_pp_task task_id)
+{
+ int ret = 0;
+
+ switch (task_id) {
+ case AMD_PP_TASK_DISPLAY_CONFIG_CHANGE:
+ ret = smu_pre_display_config_changed(smu);
+ if (ret)
+ return ret;
+ ret = smu_set_cpu_power_state(smu);
+ if (ret)
+ return ret;
+ ret = smu_adjust_power_state_dynamic(smu, level, false);
+ break;
+ case AMD_PP_TASK_COMPLETE_INIT:
+ case AMD_PP_TASK_READJUST_POWER_STATE:
+ ret = smu_adjust_power_state_dynamic(smu, level, true);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+const struct amd_ip_funcs smu_ip_funcs = {
+ .name = "smu",
+ .early_init = smu_early_init,
+ .late_init = smu_late_init,
+ .sw_init = smu_sw_init,
+ .sw_fini = smu_sw_fini,
+ .hw_init = smu_hw_init,
+ .hw_fini = smu_hw_fini,
+ .suspend = smu_suspend,
+ .resume = smu_resume,
+ .is_idle = NULL,
+ .check_soft_reset = NULL,
+ .wait_for_idle = NULL,
+ .soft_reset = NULL,
+ .set_clockgating_state = smu_set_clockgating_state,
+ .set_powergating_state = smu_set_powergating_state,
+ .enable_umd_pstate = smu_enable_umd_pstate,
+};
+
+const struct amdgpu_ip_block_version smu_v11_0_ip_block =
+{
+ .type = AMD_IP_BLOCK_TYPE_SMC,
+ .major = 11,
+ .minor = 0,
+ .rev = 0,
+ .funcs = &smu_ip_funcs,
+};
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/Makefile b/drivers/gpu/drm/amd/powerplay/hwmgr/Makefile
index 0b3c6d1d52e4..cc63705920dc 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/Makefile
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/Makefile
@@ -35,7 +35,8 @@ HARDWARE_MGR = hwmgr.o processpptables.o \
vega12_thermal.o \
pp_overdriver.o smu_helper.o \
vega20_processpptables.o vega20_hwmgr.o vega20_powertune.o \
- vega20_thermal.o common_baco.o vega10_baco.o vega20_baco.o
+ vega20_thermal.o common_baco.o vega10_baco.o vega20_baco.o \
+ vega12_baco.o smu9_baco.o
AMD_PP_HWMGR = $(addprefix $(AMD_PP_PATH)/hwmgr/,$(HARDWARE_MGR))
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c b/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c
index c1c51c115e57..70f7f47a2fcf 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c
@@ -76,7 +76,7 @@ int phm_set_power_state(struct pp_hwmgr *hwmgr,
int phm_enable_dynamic_state_management(struct pp_hwmgr *hwmgr)
{
struct amdgpu_device *adev = NULL;
- int ret = -EINVAL;;
+ int ret = -EINVAL;
PHM_FUNC_CHECK(hwmgr);
adev = hwmgr->adev;
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu10_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu10_hwmgr.c
index 0ad8fe4a6277..9a595f7525e6 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu10_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu10_hwmgr.c
@@ -35,6 +35,7 @@
#include "smu10_hwmgr.h"
#include "power_state.h"
#include "soc15_common.h"
+#include "smu10.h"
#define SMU10_MAX_DEEPSLEEP_DIVIDER_ID 5
#define SMU10_MINIMUM_ENGINE_CLOCK 800 /* 8Mhz, the low boundary of engine clock allowed on this chip */
@@ -114,11 +115,6 @@ static int smu10_initialize_dpm_defaults(struct pp_hwmgr *hwmgr)
smu10_data->num_active_display = 0;
smu10_data->deep_sleep_dcefclk = 0;
- if (hwmgr->feature_mask & PP_GFXOFF_MASK)
- smu10_data->gfx_off_controled_by_driver = true;
- else
- smu10_data->gfx_off_controled_by_driver = false;
-
phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
PHM_PlatformCaps_SclkDeepSleep);
@@ -209,18 +205,13 @@ static int smu10_set_clock_limit(struct pp_hwmgr *hwmgr, const void *input)
return 0;
}
-static inline uint32_t convert_10k_to_mhz(uint32_t clock)
-{
- return (clock + 99) / 100;
-}
-
static int smu10_set_min_deep_sleep_dcefclk(struct pp_hwmgr *hwmgr, uint32_t clock)
{
struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
if (smu10_data->need_min_deep_sleep_dcefclk &&
- smu10_data->deep_sleep_dcefclk != convert_10k_to_mhz(clock)) {
- smu10_data->deep_sleep_dcefclk = convert_10k_to_mhz(clock);
+ smu10_data->deep_sleep_dcefclk != clock) {
+ smu10_data->deep_sleep_dcefclk = clock;
smum_send_msg_to_smc_with_parameter(hwmgr,
PPSMC_MSG_SetMinDeepSleepDcefclk,
smu10_data->deep_sleep_dcefclk);
@@ -233,8 +224,8 @@ static int smu10_set_hard_min_dcefclk_by_freq(struct pp_hwmgr *hwmgr, uint32_t c
struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
if (smu10_data->dcf_actual_hard_min_freq &&
- smu10_data->dcf_actual_hard_min_freq != convert_10k_to_mhz(clock)) {
- smu10_data->dcf_actual_hard_min_freq = convert_10k_to_mhz(clock);
+ smu10_data->dcf_actual_hard_min_freq != clock) {
+ smu10_data->dcf_actual_hard_min_freq = clock;
smum_send_msg_to_smc_with_parameter(hwmgr,
PPSMC_MSG_SetHardMinDcefclkByFreq,
smu10_data->dcf_actual_hard_min_freq);
@@ -247,8 +238,8 @@ static int smu10_set_hard_min_fclk_by_freq(struct pp_hwmgr *hwmgr, uint32_t cloc
struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
if (smu10_data->f_actual_hard_min_freq &&
- smu10_data->f_actual_hard_min_freq != convert_10k_to_mhz(clock)) {
- smu10_data->f_actual_hard_min_freq = convert_10k_to_mhz(clock);
+ smu10_data->f_actual_hard_min_freq != clock) {
+ smu10_data->f_actual_hard_min_freq = clock;
smum_send_msg_to_smc_with_parameter(hwmgr,
PPSMC_MSG_SetHardMinFclkByFreq,
smu10_data->f_actual_hard_min_freq);
@@ -330,9 +321,9 @@ static bool smu10_is_gfx_on(struct pp_hwmgr *hwmgr)
static int smu10_disable_gfx_off(struct pp_hwmgr *hwmgr)
{
- struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+ struct amdgpu_device *adev = hwmgr->adev;
- if (smu10_data->gfx_off_controled_by_driver) {
+ if (adev->pm.pp_feature & PP_GFXOFF_MASK) {
smum_send_msg_to_smc(hwmgr, PPSMC_MSG_DisableGfxOff);
/* confirm gfx is back to "on" state */
@@ -350,9 +341,9 @@ static int smu10_disable_dpm_tasks(struct pp_hwmgr *hwmgr)
static int smu10_enable_gfx_off(struct pp_hwmgr *hwmgr)
{
- struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+ struct amdgpu_device *adev = hwmgr->adev;
- if (smu10_data->gfx_off_controled_by_driver)
+ if (adev->pm.pp_feature & PP_GFXOFF_MASK)
smum_send_msg_to_smc(hwmgr, PPSMC_MSG_EnableGfxOff);
return 0;
@@ -577,7 +568,6 @@ static int smu10_dpm_force_dpm_level(struct pp_hwmgr *hwmgr,
enum amd_dpm_forced_level level)
{
struct smu10_hwmgr *data = hwmgr->backend;
- struct amdgpu_device *adev = hwmgr->adev;
uint32_t min_sclk = hwmgr->display_config->min_core_set_clock;
uint32_t min_mclk = hwmgr->display_config->min_mem_set_clock/100;
@@ -586,11 +576,6 @@ static int smu10_dpm_force_dpm_level(struct pp_hwmgr *hwmgr,
return 0;
}
- /* Disable UMDPSTATE support on rv2 temporarily */
- if ((adev->asic_type == CHIP_RAVEN) &&
- (adev->rev_id >= 8))
- return 0;
-
if (min_sclk < data->gfx_min_freq_limit)
min_sclk = data->gfx_min_freq_limit;
@@ -1205,6 +1190,94 @@ static void smu10_powergate_vcn(struct pp_hwmgr *hwmgr, bool bgate)
}
}
+static int conv_power_profile_to_pplib_workload(int power_profile)
+{
+ int pplib_workload = 0;
+
+ switch (power_profile) {
+ case PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT:
+ pplib_workload = WORKLOAD_DEFAULT_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_FULLSCREEN3D:
+ pplib_workload = WORKLOAD_PPLIB_FULL_SCREEN_3D_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_POWERSAVING:
+ pplib_workload = WORKLOAD_PPLIB_POWER_SAVING_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_VIDEO:
+ pplib_workload = WORKLOAD_PPLIB_VIDEO_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_VR:
+ pplib_workload = WORKLOAD_PPLIB_VR_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_COMPUTE:
+ pplib_workload = WORKLOAD_PPLIB_COMPUTE_BIT;
+ break;
+ }
+
+ return pplib_workload;
+}
+
+static int smu10_get_power_profile_mode(struct pp_hwmgr *hwmgr, char *buf)
+{
+ uint32_t i, size = 0;
+ static const uint8_t
+ profile_mode_setting[6][4] = {{70, 60, 0, 0,},
+ {70, 60, 1, 3,},
+ {90, 60, 0, 0,},
+ {70, 60, 0, 0,},
+ {70, 90, 0, 0,},
+ {30, 60, 0, 6,},
+ };
+ static const char *profile_name[6] = {
+ "BOOTUP_DEFAULT",
+ "3D_FULL_SCREEN",
+ "POWER_SAVING",
+ "VIDEO",
+ "VR",
+ "COMPUTE"};
+ static const char *title[6] = {"NUM",
+ "MODE_NAME",
+ "BUSY_SET_POINT",
+ "FPS",
+ "USE_RLC_BUSY",
+ "MIN_ACTIVE_LEVEL"};
+
+ if (!buf)
+ return -EINVAL;
+
+ size += sprintf(buf + size, "%s %16s %s %s %s %s\n",title[0],
+ title[1], title[2], title[3], title[4], title[5]);
+
+ for (i = 0; i <= PP_SMC_POWER_PROFILE_COMPUTE; i++)
+ size += sprintf(buf + size, "%3d %14s%s: %14d %3d %10d %14d\n",
+ i, profile_name[i], (i == hwmgr->power_profile_mode) ? "*" : " ",
+ profile_mode_setting[i][0], profile_mode_setting[i][1],
+ profile_mode_setting[i][2], profile_mode_setting[i][3]);
+
+ return size;
+}
+
+static int smu10_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, uint32_t size)
+{
+ int workload_type = 0;
+
+ if (input[size] > PP_SMC_POWER_PROFILE_COMPUTE) {
+ pr_err("Invalid power profile mode %ld\n", input[size]);
+ return -EINVAL;
+ }
+ hwmgr->power_profile_mode = input[size];
+
+ /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
+ workload_type =
+ conv_power_profile_to_pplib_workload(hwmgr->power_profile_mode);
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_ActiveProcessNotify,
+ 1 << workload_type);
+
+ return 0;
+}
+
+
static const struct pp_hwmgr_func smu10_hwmgr_funcs = {
.backend_init = smu10_hwmgr_backend_init,
.backend_fini = smu10_hwmgr_backend_fini,
@@ -1246,6 +1319,8 @@ static const struct pp_hwmgr_func smu10_hwmgr_funcs = {
.powergate_sdma = smu10_powergate_sdma,
.set_hard_min_dcefclk_by_freq = smu10_set_hard_min_dcefclk_by_freq,
.set_hard_min_fclk_by_freq = smu10_set_hard_min_fclk_by_freq,
+ .get_power_profile_mode = smu10_get_power_profile_mode,
+ .set_power_profile_mode = smu10_set_power_profile_mode,
};
int smu10_init_function_pointers(struct pp_hwmgr *hwmgr)
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
index 83d3d935f3ac..048757e8f494 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
@@ -77,7 +77,7 @@
#define PCIE_BUS_CLK 10000
#define TCLK (PCIE_BUS_CLK / 10)
-static const struct profile_mode_setting smu7_profiling[7] =
+static struct profile_mode_setting smu7_profiling[7] =
{{0, 0, 0, 0, 0, 0, 0, 0},
{1, 0, 100, 30, 1, 0, 100, 10},
{1, 10, 0, 30, 0, 0, 0, 0},
@@ -4984,17 +4984,27 @@ static int smu7_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, uint
mode = input[size];
switch (mode) {
case PP_SMC_POWER_PROFILE_CUSTOM:
- if (size < 8)
+ if (size < 8 && size != 0)
return -EINVAL;
-
- tmp.bupdate_sclk = input[0];
- tmp.sclk_up_hyst = input[1];
- tmp.sclk_down_hyst = input[2];
- tmp.sclk_activity = input[3];
- tmp.bupdate_mclk = input[4];
- tmp.mclk_up_hyst = input[5];
- tmp.mclk_down_hyst = input[6];
- tmp.mclk_activity = input[7];
+ /* If only CUSTOM is passed in, use the saved values. Check
+ * that we actually have a CUSTOM profile by ensuring that
+ * the "use sclk" or the "use mclk" bits are set
+ */
+ tmp = smu7_profiling[PP_SMC_POWER_PROFILE_CUSTOM];
+ if (size == 0) {
+ if (tmp.bupdate_sclk == 0 && tmp.bupdate_mclk == 0)
+ return -EINVAL;
+ } else {
+ tmp.bupdate_sclk = input[0];
+ tmp.sclk_up_hyst = input[1];
+ tmp.sclk_down_hyst = input[2];
+ tmp.sclk_activity = input[3];
+ tmp.bupdate_mclk = input[4];
+ tmp.mclk_up_hyst = input[5];
+ tmp.mclk_down_hyst = input[6];
+ tmp.mclk_activity = input[7];
+ smu7_profiling[PP_SMC_POWER_PROFILE_CUSTOM] = tmp;
+ }
if (!smum_update_dpm_settings(hwmgr, &tmp)) {
memcpy(&data->current_profile_setting, &tmp, sizeof(struct profile_mode_setting));
hwmgr->power_profile_mode = mode;
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu9_baco.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu9_baco.c
new file mode 100644
index 000000000000..de0a37f7c632
--- /dev/null
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu9_baco.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "amdgpu.h"
+#include "soc15.h"
+#include "soc15_hw_ip.h"
+#include "vega10_ip_offset.h"
+#include "soc15_common.h"
+#include "vega10_inc.h"
+#include "smu9_baco.h"
+
+int smu9_baco_get_capability(struct pp_hwmgr *hwmgr, bool *cap)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+ uint32_t reg, data;
+
+ *cap = false;
+ if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_BACO))
+ return 0;
+
+ WREG32(0x12074, 0xFFF0003B);
+ data = RREG32(0x12075);
+
+ if (data == 0x1) {
+ reg = RREG32_SOC15(NBIF, 0, mmRCC_BIF_STRAP0);
+
+ if (reg & RCC_BIF_STRAP0__STRAP_PX_CAPABLE_MASK)
+ *cap = true;
+ }
+
+ return 0;
+}
+
+int smu9_baco_get_state(struct pp_hwmgr *hwmgr, enum BACO_STATE *state)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+ uint32_t reg;
+
+ reg = RREG32_SOC15(NBIF, 0, mmBACO_CNTL);
+
+ if (reg & BACO_CNTL__BACO_MODE_MASK)
+ /* gfx has already entered BACO state */
+ *state = BACO_STATE_IN;
+ else
+ *state = BACO_STATE_OUT;
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu9_baco.h b/drivers/gpu/drm/amd/powerplay/hwmgr/smu9_baco.h
new file mode 100644
index 000000000000..84e90f801ac3
--- /dev/null
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu9_baco.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#ifndef __SMU9_BACO_H__
+#define __SMU9_BACO_H__
+#include "hwmgr.h"
+#include "common_baco.h"
+
+extern int smu9_baco_get_capability(struct pp_hwmgr *hwmgr, bool *cap);
+extern int smu9_baco_get_state(struct pp_hwmgr *hwmgr, enum BACO_STATE *state);
+
+#endif
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_baco.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_baco.c
index 7337be5602e4..d168af4a4d78 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_baco.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_baco.c
@@ -85,48 +85,11 @@ static const struct soc15_baco_cmd_entry clean_baco_tbl[] =
{CMD_WRITE, SOC15_REG_ENTRY(NBIF, 0, mmBIOS_SCRATCH_7), 0, 0, 0, 0},
};
-int vega10_baco_get_capability(struct pp_hwmgr *hwmgr, bool *cap)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
- uint32_t reg, data;
-
- *cap = false;
- if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_BACO))
- return 0;
-
- WREG32(0x12074, 0xFFF0003B);
- data = RREG32(0x12075);
-
- if (data == 0x1) {
- reg = RREG32_SOC15(NBIF, 0, mmRCC_BIF_STRAP0);
-
- if (reg & RCC_BIF_STRAP0__STRAP_PX_CAPABLE_MASK)
- *cap = true;
- }
-
- return 0;
-}
-
-int vega10_baco_get_state(struct pp_hwmgr *hwmgr, enum BACO_STATE *state)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
- uint32_t reg;
-
- reg = RREG32_SOC15(NBIF, 0, mmBACO_CNTL);
-
- if (reg & BACO_CNTL__BACO_MODE_MASK)
- /* gfx has already entered BACO state */
- *state = BACO_STATE_IN;
- else
- *state = BACO_STATE_OUT;
- return 0;
-}
-
int vega10_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state)
{
enum BACO_STATE cur_state;
- vega10_baco_get_state(hwmgr, &cur_state);
+ smu9_baco_get_state(hwmgr, &cur_state);
if (cur_state == state)
/* aisc already in the target state */
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_baco.h b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_baco.h
index f7a3ffa744b3..96d793f026a5 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_baco.h
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_baco.h
@@ -22,11 +22,8 @@
*/
#ifndef __VEGA10_BACO_H__
#define __VEGA10_BACO_H__
-#include "hwmgr.h"
-#include "common_baco.h"
+#include "smu9_baco.h"
-extern int vega10_baco_get_capability(struct pp_hwmgr *hwmgr, bool *cap);
-extern int vega10_baco_get_state(struct pp_hwmgr *hwmgr, enum BACO_STATE *state);
extern int vega10_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state);
#endif
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c
index 5c4f701939ea..384c37875cd0 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c
@@ -1427,6 +1427,15 @@ static int vega10_setup_default_dpm_tables(struct pp_hwmgr *hwmgr)
vega10_setup_default_pcie_table(hwmgr);
+ /* Zero out the saved copy of the CUSTOM profile
+ * This will be checked when trying to set the profile
+ * and will require that new values be passed in
+ */
+ data->custom_profile_mode[0] = 0;
+ data->custom_profile_mode[1] = 0;
+ data->custom_profile_mode[2] = 0;
+ data->custom_profile_mode[3] = 0;
+
/* save a copy of the default DPM table */
memcpy(&(data->golden_dpm_table), &(data->dpm_table),
sizeof(struct vega10_dpm_table));
@@ -4904,16 +4913,23 @@ static int vega10_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, ui
uint8_t FPS;
uint8_t use_rlc_busy;
uint8_t min_active_level;
+ uint32_t power_profile_mode = input[size];
- hwmgr->power_profile_mode = input[size];
-
- smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_SetWorkloadMask,
- 1<<hwmgr->power_profile_mode);
-
- if (hwmgr->power_profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) {
- if (size == 0 || size > 4)
+ if (power_profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) {
+ if (size != 0 && size != 4)
return -EINVAL;
+ /* If size = 0 and the CUSTOM profile has been set already
+ * then just apply the profile. The copy stored in the hwmgr
+ * is zeroed out on init
+ */
+ if (size == 0) {
+ if (data->custom_profile_mode[0] != 0)
+ goto out;
+ else
+ return -EINVAL;
+ }
+
data->custom_profile_mode[0] = busy_set_point = input[0];
data->custom_profile_mode[1] = FPS = input[1];
data->custom_profile_mode[2] = use_rlc_busy = input[2];
@@ -4924,6 +4940,11 @@ static int vega10_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, ui
use_rlc_busy << 16 | min_active_level<<24);
}
+out:
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_SetWorkloadMask,
+ 1 << power_profile_mode);
+ hwmgr->power_profile_mode = power_profile_mode;
+
return 0;
}
@@ -5170,8 +5191,8 @@ static const struct pp_hwmgr_func vega10_hwmgr_funcs = {
.set_power_limit = vega10_set_power_limit,
.odn_edit_dpm_table = vega10_odn_edit_dpm_table,
.get_performance_level = vega10_get_performance_level,
- .get_asic_baco_capability = vega10_baco_get_capability,
- .get_asic_baco_state = vega10_baco_get_state,
+ .get_asic_baco_capability = smu9_baco_get_capability,
+ .get_asic_baco_state = smu9_baco_get_state,
.set_asic_baco_state = vega10_baco_set_state,
.enable_mgpu_fan_boost = vega10_enable_mgpu_fan_boost,
.get_ppfeature_status = vega10_get_ppfeature_status,
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_baco.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_baco.c
new file mode 100644
index 000000000000..9d8ca94a8f0c
--- /dev/null
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_baco.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "amdgpu.h"
+#include "soc15.h"
+#include "soc15_hw_ip.h"
+#include "vega10_ip_offset.h"
+#include "soc15_common.h"
+#include "vega12_inc.h"
+#include "vega12_ppsmc.h"
+#include "vega12_baco.h"
+
+static const struct soc15_baco_cmd_entry pre_baco_tbl[] =
+{
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmBIF_DOORBELL_CNTL_BASE_IDX, mmBIF_DOORBELL_CNTL, BIF_DOORBELL_CNTL__DOORBELL_MONITOR_EN_MASK, BIF_DOORBELL_CNTL__DOORBELL_MONITOR_EN__SHIFT, 0, 0 },
+ { CMD_WRITE, NBIF_HWID, 0, mmBIF_FB_EN_BASE_IDX, mmBIF_FB_EN, 0, 0, 0, 0 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_DSTATE_BYPASS_MASK, BACO_CNTL__BACO_DSTATE_BYPASS__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_RST_INTR_MASK_MASK, BACO_CNTL__BACO_RST_INTR_MASK__SHIFT, 0, 1 }
+};
+
+static const struct soc15_baco_cmd_entry enter_baco_tbl[] =
+{
+ { CMD_WAITFOR, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__SOC_DOMAIN_IDLE_MASK, THM_BACO_CNTL__SOC_DOMAIN_IDLE__SHIFT, 0xffffffff, 0x80000000 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_EN_MASK, BACO_CNTL__BACO_EN__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_BIF_LCLK_SWITCH_MASK, BACO_CNTL__BACO_BIF_LCLK_SWITCH__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_DUMMY_EN_MASK, BACO_CNTL__BACO_DUMMY_EN__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_SOC_VDCI_RESET_MASK, THM_BACO_CNTL__BACO_SOC_VDCI_RESET__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_SMNCLK_MUX_MASK, THM_BACO_CNTL__BACO_SMNCLK_MUX__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_ISO_EN_MASK, THM_BACO_CNTL__BACO_ISO_EN__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_AEB_ISO_EN_MASK, THM_BACO_CNTL__BACO_AEB_ISO_EN__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_ANA_ISO_EN_MASK, THM_BACO_CNTL__BACO_ANA_ISO_EN__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_SOC_REFCLK_OFF_MASK, THM_BACO_CNTL__BACO_SOC_REFCLK_OFF__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, BACO_CNTL__BACO_POWER_OFF__SHIFT, 0, 1 },
+ { CMD_DELAY_MS, 0, 0, 0, 5, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_RESET_EN_MASK, THM_BACO_CNTL__BACO_RESET_EN__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_PWROKRAW_CNTL_MASK, THM_BACO_CNTL__BACO_PWROKRAW_CNTL__SHIFT, 0, 0 },
+ { CMD_WAITFOR, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_MODE_MASK, BACO_CNTL__BACO_MODE__SHIFT, 0xffffffff, 0x100 }
+};
+
+static const struct soc15_baco_cmd_entry exit_baco_tbl[] =
+{
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, BACO_CNTL__BACO_POWER_OFF__SHIFT, 0, 0 },
+ { CMD_DELAY_MS, 0, 0, 0, 0, 0, 0, 10, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_SOC_REFCLK_OFF_MASK, THM_BACO_CNTL__BACO_SOC_REFCLK_OFF__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_ANA_ISO_EN_MASK, THM_BACO_CNTL__BACO_ANA_ISO_EN__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_AEB_ISO_EN_MASK, THM_BACO_CNTL__BACO_AEB_ISO_EN__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_ISO_EN_MASK, THM_BACO_CNTL__BACO_ISO_EN__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_PWROKRAW_CNTL_MASK, THM_BACO_CNTL__BACO_PWROKRAW_CNTL__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_SMNCLK_MUX_MASK, THM_BACO_CNTL__BACO_SMNCLK_MUX__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_SOC_VDCI_RESET_MASK, THM_BACO_CNTL__BACO_SOC_VDCI_RESET__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_EXIT_MASK, THM_BACO_CNTL__BACO_EXIT__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_RESET_EN_MASK, THM_BACO_CNTL__BACO_RESET_EN__SHIFT, 0, 0 },
+ { CMD_WAITFOR, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_EXIT_MASK, 0, 0xffffffff, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_SB_AXI_FENCE_MASK, THM_BACO_CNTL__BACO_SB_AXI_FENCE__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_DUMMY_EN_MASK, BACO_CNTL__BACO_DUMMY_EN__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_BIF_LCLK_SWITCH_MASK, BACO_CNTL__BACO_BIF_LCLK_SWITCH__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_EN_MASK, BACO_CNTL__BACO_EN__SHIFT, 0, 0 },
+ { CMD_WAITFOR, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_MODE_MASK, 0, 0xffffffff, 0 }
+};
+
+static const struct soc15_baco_cmd_entry clean_baco_tbl[] =
+{
+ { CMD_WRITE, NBIF_HWID, 0, mmBIOS_SCRATCH_6_BASE_IDX, mmBIOS_SCRATCH_6, 0, 0, 0, 0 },
+ { CMD_WRITE, NBIF_HWID, 0, mmBIOS_SCRATCH_7_BASE_IDX, mmBIOS_SCRATCH_7, 0, 0, 0, 0 }
+};
+
+int vega12_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state)
+{
+ enum BACO_STATE cur_state;
+
+ smu9_baco_get_state(hwmgr, &cur_state);
+
+ if (cur_state == state)
+ /* aisc already in the target state */
+ return 0;
+
+ if (state == BACO_STATE_IN) {
+ if (soc15_baco_program_registers(hwmgr, pre_baco_tbl,
+ ARRAY_SIZE(pre_baco_tbl))) {
+ if (smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_EnterBaco, 0))
+ return -EINVAL;
+
+ if (soc15_baco_program_registers(hwmgr, enter_baco_tbl,
+ ARRAY_SIZE(enter_baco_tbl)))
+ return 0;
+ }
+ } else if (state == BACO_STATE_OUT) {
+ /* HW requires at least 20ms between regulator off and on */
+ msleep(20);
+ /* Execute Hardware BACO exit sequence */
+ if (soc15_baco_program_registers(hwmgr, exit_baco_tbl,
+ ARRAY_SIZE(exit_baco_tbl))) {
+ if (soc15_baco_program_registers(hwmgr, clean_baco_tbl,
+ ARRAY_SIZE(clean_baco_tbl)))
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_baco.h b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_baco.h
new file mode 100644
index 000000000000..57b72e5a95ae
--- /dev/null
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_baco.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#ifndef __VEGA12_BACO_H__
+#define __VEGA12_BACO_H__
+#include "smu9_baco.h"
+
+extern int vega12_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state);
+
+#endif
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c
index bdb48e94eff6..707cd4b0357f 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c
@@ -45,6 +45,7 @@
#include "ppinterrupt.h"
#include "pp_overdriver.h"
#include "pp_thermal.h"
+#include "vega12_baco.h"
static int vega12_force_clock_level(struct pp_hwmgr *hwmgr,
@@ -2626,8 +2627,12 @@ static const struct pp_hwmgr_func vega12_hwmgr_funcs = {
.start_thermal_controller = vega12_start_thermal_controller,
.powergate_gfx = vega12_gfx_off_control,
.get_performance_level = vega12_get_performance_level,
+ .get_asic_baco_capability = smu9_baco_get_capability,
+ .get_asic_baco_state = smu9_baco_get_state,
+ .set_asic_baco_state = vega12_baco_set_state,
.get_ppfeature_status = vega12_get_ppfeature_status,
.set_ppfeature_status = vega12_set_ppfeature_status,
+
};
int vega12_hwmgr_init(struct pp_hwmgr *hwmgr)
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_inc.h b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_inc.h
index 30b278c50222..e6d9e84059e1 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_inc.h
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_inc.h
@@ -35,5 +35,7 @@
#include "asic_reg/gc/gc_9_2_1_sh_mask.h"
#include "asic_reg/nbio/nbio_6_1_offset.h"
+#include "asic_reg/nbio/nbio_6_1_offset.h"
+#include "asic_reg/nbio/nbio_6_1_sh_mask.h"
#endif
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_baco.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_baco.c
index 5e8602a79b1c..df6ff9252401 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_baco.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_baco.c
@@ -27,6 +27,7 @@
#include "vega20_inc.h"
#include "vega20_ppsmc.h"
#include "vega20_baco.h"
+#include "vega20_smumgr.h"
@@ -101,3 +102,14 @@ int vega20_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state)
return 0;
}
+
+int vega20_baco_apply_vdci_flush_workaround(struct pp_hwmgr *hwmgr)
+{
+ int ret = 0;
+
+ ret = vega20_set_pptable_driver_address(hwmgr);
+ if (ret)
+ return ret;
+
+ return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_BacoWorkAroundFlushVDCI);
+}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_baco.h b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_baco.h
index 51c7f8392925..f06471e712dc 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_baco.h
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_baco.h
@@ -28,5 +28,6 @@
extern int vega20_baco_get_capability(struct pp_hwmgr *hwmgr, bool *cap);
extern int vega20_baco_get_state(struct pp_hwmgr *hwmgr, enum BACO_STATE *state);
extern int vega20_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state);
+extern int vega20_baco_apply_vdci_flush_workaround(struct pp_hwmgr *hwmgr);
#endif
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c
index 23b5b94a4939..9b9f87b84910 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c
@@ -434,6 +434,7 @@ static int vega20_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
hwmgr->platform_descriptor.clockStep.memoryClock = 500;
data->total_active_cus = adev->gfx.cu_info.number;
+ data->is_custom_profile_set = false;
return 0;
}
@@ -450,6 +451,7 @@ static int vega20_init_sclk_threshold(struct pp_hwmgr *hwmgr)
static int vega20_setup_asic_task(struct pp_hwmgr *hwmgr)
{
+ struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
int ret = 0;
ret = vega20_init_sclk_threshold(hwmgr);
@@ -457,7 +459,15 @@ static int vega20_setup_asic_task(struct pp_hwmgr *hwmgr)
"Failed to init sclk threshold!",
return ret);
- return 0;
+ if (adev->in_baco_reset) {
+ adev->in_baco_reset = 0;
+
+ ret = vega20_baco_apply_vdci_flush_workaround(hwmgr);
+ if (ret)
+ pr_err("Failed to apply vega20 baco workaround!\n");
+ }
+
+ return ret;
}
/*
@@ -3450,7 +3460,18 @@ static void vega20_power_gate_vce(struct pp_hwmgr *hwmgr, bool bgate)
return ;
data->vce_power_gated = bgate;
- vega20_enable_disable_vce_dpm(hwmgr, !bgate);
+ if (bgate) {
+ vega20_enable_disable_vce_dpm(hwmgr, !bgate);
+ amdgpu_device_ip_set_powergating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_VCE,
+ AMD_PG_STATE_GATE);
+ } else {
+ amdgpu_device_ip_set_powergating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_VCE,
+ AMD_PG_STATE_UNGATE);
+ vega20_enable_disable_vce_dpm(hwmgr, !bgate);
+ }
+
}
static void vega20_power_gate_uvd(struct pp_hwmgr *hwmgr, bool bgate)
@@ -3826,16 +3847,19 @@ static int vega20_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, ui
{
DpmActivityMonitorCoeffInt_t activity_monitor;
int workload_type, result = 0;
+ uint32_t power_profile_mode = input[size];
- hwmgr->power_profile_mode = input[size];
-
- if (hwmgr->power_profile_mode > PP_SMC_POWER_PROFILE_CUSTOM) {
- pr_err("Invalid power profile mode %d\n", hwmgr->power_profile_mode);
+ if (power_profile_mode > PP_SMC_POWER_PROFILE_CUSTOM) {
+ pr_err("Invalid power profile mode %d\n", power_profile_mode);
return -EINVAL;
}
- if (hwmgr->power_profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) {
- if (size < 10)
+ if (power_profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) {
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ if (size == 0 && !data->is_custom_profile_set)
+ return -EINVAL;
+ if (size < 10 && size != 0)
return -EINVAL;
result = vega20_get_activity_monitor_coeff(hwmgr,
@@ -3845,6 +3869,13 @@ static int vega20_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, ui
"[SetPowerProfile] Failed to get activity monitor!",
return result);
+ /* If size==0, then we want to apply the already-configured
+ * CUSTOM profile again. Just apply it, since we checked its
+ * validity above
+ */
+ if (size == 0)
+ goto out;
+
switch (input[0]) {
case 0: /* Gfxclk */
activity_monitor.Gfx_FPS = input[1];
@@ -3895,17 +3926,21 @@ static int vega20_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, ui
result = vega20_set_activity_monitor_coeff(hwmgr,
(uint8_t *)(&activity_monitor),
WORKLOAD_PPLIB_CUSTOM_BIT);
+ data->is_custom_profile_set = true;
PP_ASSERT_WITH_CODE(!result,
"[SetPowerProfile] Failed to set activity monitor!",
return result);
}
+out:
/* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
workload_type =
- conv_power_profile_to_pplib_workload(hwmgr->power_profile_mode);
+ conv_power_profile_to_pplib_workload(power_profile_mode);
smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_SetWorkloadMask,
1 << workload_type);
+ hwmgr->power_profile_mode = power_profile_mode;
+
return 0;
}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.h b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.h
index ac2a3118a0ae..2c3125f82b24 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.h
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.h
@@ -531,6 +531,8 @@ struct vega20_hwmgr {
bool pcie_parameters_override;
uint32_t pcie_gen_level1;
uint32_t pcie_width_level1;
+
+ bool is_custom_profile_set;
};
#define VEGA20_DPM2_NEAR_TDP_DEC 10
diff --git a/drivers/gpu/drm/amd/powerplay/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/powerplay/inc/amdgpu_smu.h
new file mode 100644
index 000000000000..c8b168b3413b
--- /dev/null
+++ b/drivers/gpu/drm/amd/powerplay/inc/amdgpu_smu.h
@@ -0,0 +1,770 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __AMDGPU_SMU_H__
+#define __AMDGPU_SMU_H__
+
+#include "amdgpu.h"
+#include "kgd_pp_interface.h"
+#include "dm_pp_interface.h"
+
+struct smu_hw_power_state {
+ unsigned int magic;
+};
+
+struct smu_power_state;
+
+enum smu_state_ui_label {
+ SMU_STATE_UI_LABEL_NONE,
+ SMU_STATE_UI_LABEL_BATTERY,
+ SMU_STATE_UI_TABEL_MIDDLE_LOW,
+ SMU_STATE_UI_LABEL_BALLANCED,
+ SMU_STATE_UI_LABEL_MIDDLE_HIGHT,
+ SMU_STATE_UI_LABEL_PERFORMANCE,
+ SMU_STATE_UI_LABEL_BACO,
+};
+
+enum smu_state_classification_flag {
+ SMU_STATE_CLASSIFICATION_FLAG_BOOT = 0x0001,
+ SMU_STATE_CLASSIFICATION_FLAG_THERMAL = 0x0002,
+ SMU_STATE_CLASSIFICATIN_FLAG_LIMITED_POWER_SOURCE = 0x0004,
+ SMU_STATE_CLASSIFICATION_FLAG_RESET = 0x0008,
+ SMU_STATE_CLASSIFICATION_FLAG_FORCED = 0x0010,
+ SMU_STATE_CLASSIFICATION_FLAG_USER_3D_PERFORMANCE = 0x0020,
+ SMU_STATE_CLASSIFICATION_FLAG_USER_2D_PERFORMANCE = 0x0040,
+ SMU_STATE_CLASSIFICATION_FLAG_3D_PERFORMANCE = 0x0080,
+ SMU_STATE_CLASSIFICATION_FLAG_AC_OVERDIRVER_TEMPLATE = 0x0100,
+ SMU_STATE_CLASSIFICATION_FLAG_UVD = 0x0200,
+ SMU_STATE_CLASSIFICATION_FLAG_3D_PERFORMANCE_LOW = 0x0400,
+ SMU_STATE_CLASSIFICATION_FLAG_ACPI = 0x0800,
+ SMU_STATE_CLASSIFICATION_FLAG_HD2 = 0x1000,
+ SMU_STATE_CLASSIFICATION_FLAG_UVD_HD = 0x2000,
+ SMU_STATE_CLASSIFICATION_FLAG_UVD_SD = 0x4000,
+ SMU_STATE_CLASSIFICATION_FLAG_USER_DC_PERFORMANCE = 0x8000,
+ SMU_STATE_CLASSIFICATION_FLAG_DC_OVERDIRVER_TEMPLATE = 0x10000,
+ SMU_STATE_CLASSIFICATION_FLAG_BACO = 0x20000,
+ SMU_STATE_CLASSIFICATIN_FLAG_LIMITED_POWER_SOURCE2 = 0x40000,
+ SMU_STATE_CLASSIFICATION_FLAG_ULV = 0x80000,
+ SMU_STATE_CLASSIFICATION_FLAG_UVD_MVC = 0x100000,
+};
+
+struct smu_state_classification_block {
+ enum smu_state_ui_label ui_label;
+ enum smu_state_classification_flag flags;
+ int bios_index;
+ bool temporary_state;
+ bool to_be_deleted;
+};
+
+struct smu_state_pcie_block {
+ unsigned int lanes;
+};
+
+enum smu_refreshrate_source {
+ SMU_REFRESHRATE_SOURCE_EDID,
+ SMU_REFRESHRATE_SOURCE_EXPLICIT
+};
+
+struct smu_state_display_block {
+ bool disable_frame_modulation;
+ bool limit_refreshrate;
+ enum smu_refreshrate_source refreshrate_source;
+ int explicit_refreshrate;
+ int edid_refreshrate_index;
+ bool enable_vari_bright;
+};
+
+struct smu_state_memroy_block {
+ bool dll_off;
+ uint8_t m3arb;
+ uint8_t unused[3];
+};
+
+struct smu_state_software_algorithm_block {
+ bool disable_load_balancing;
+ bool enable_sleep_for_timestamps;
+};
+
+struct smu_temperature_range {
+ int min;
+ int max;
+};
+
+struct smu_state_validation_block {
+ bool single_display_only;
+ bool disallow_on_dc;
+ uint8_t supported_power_levels;
+};
+
+struct smu_uvd_clocks {
+ uint32_t vclk;
+ uint32_t dclk;
+};
+
+/**
+* Structure to hold a SMU Power State.
+*/
+struct smu_power_state {
+ uint32_t id;
+ struct list_head ordered_list;
+ struct list_head all_states_list;
+
+ struct smu_state_classification_block classification;
+ struct smu_state_validation_block validation;
+ struct smu_state_pcie_block pcie;
+ struct smu_state_display_block display;
+ struct smu_state_memroy_block memory;
+ struct smu_temperature_range temperatures;
+ struct smu_state_software_algorithm_block software;
+ struct smu_uvd_clocks uvd_clocks;
+ struct smu_hw_power_state hardware;
+};
+
+enum smu_message_type
+{
+ SMU_MSG_TestMessage = 0,
+ SMU_MSG_GetSmuVersion,
+ SMU_MSG_GetDriverIfVersion,
+ SMU_MSG_SetAllowedFeaturesMaskLow,
+ SMU_MSG_SetAllowedFeaturesMaskHigh,
+ SMU_MSG_EnableAllSmuFeatures,
+ SMU_MSG_DisableAllSmuFeatures,
+ SMU_MSG_EnableSmuFeaturesLow,
+ SMU_MSG_EnableSmuFeaturesHigh,
+ SMU_MSG_DisableSmuFeaturesLow,
+ SMU_MSG_DisableSmuFeaturesHigh,
+ SMU_MSG_GetEnabledSmuFeaturesLow,
+ SMU_MSG_GetEnabledSmuFeaturesHigh,
+ SMU_MSG_SetWorkloadMask,
+ SMU_MSG_SetPptLimit,
+ SMU_MSG_SetDriverDramAddrHigh,
+ SMU_MSG_SetDriverDramAddrLow,
+ SMU_MSG_SetToolsDramAddrHigh,
+ SMU_MSG_SetToolsDramAddrLow,
+ SMU_MSG_TransferTableSmu2Dram,
+ SMU_MSG_TransferTableDram2Smu,
+ SMU_MSG_UseDefaultPPTable,
+ SMU_MSG_UseBackupPPTable,
+ SMU_MSG_RunBtc,
+ SMU_MSG_RequestI2CBus,
+ SMU_MSG_ReleaseI2CBus,
+ SMU_MSG_SetFloorSocVoltage,
+ SMU_MSG_SoftReset,
+ SMU_MSG_StartBacoMonitor,
+ SMU_MSG_CancelBacoMonitor,
+ SMU_MSG_EnterBaco,
+ SMU_MSG_SetSoftMinByFreq,
+ SMU_MSG_SetSoftMaxByFreq,
+ SMU_MSG_SetHardMinByFreq,
+ SMU_MSG_SetHardMaxByFreq,
+ SMU_MSG_GetMinDpmFreq,
+ SMU_MSG_GetMaxDpmFreq,
+ SMU_MSG_GetDpmFreqByIndex,
+ SMU_MSG_GetDpmClockFreq,
+ SMU_MSG_GetSsVoltageByDpm,
+ SMU_MSG_SetMemoryChannelConfig,
+ SMU_MSG_SetGeminiMode,
+ SMU_MSG_SetGeminiApertureHigh,
+ SMU_MSG_SetGeminiApertureLow,
+ SMU_MSG_SetMinLinkDpmByIndex,
+ SMU_MSG_OverridePcieParameters,
+ SMU_MSG_OverDriveSetPercentage,
+ SMU_MSG_SetMinDeepSleepDcefclk,
+ SMU_MSG_ReenableAcDcInterrupt,
+ SMU_MSG_NotifyPowerSource,
+ SMU_MSG_SetUclkFastSwitch,
+ SMU_MSG_SetUclkDownHyst,
+ SMU_MSG_GfxDeviceDriverReset,
+ SMU_MSG_GetCurrentRpm,
+ SMU_MSG_SetVideoFps,
+ SMU_MSG_SetTjMax,
+ SMU_MSG_SetFanTemperatureTarget,
+ SMU_MSG_PrepareMp1ForUnload,
+ SMU_MSG_DramLogSetDramAddrHigh,
+ SMU_MSG_DramLogSetDramAddrLow,
+ SMU_MSG_DramLogSetDramSize,
+ SMU_MSG_SetFanMaxRpm,
+ SMU_MSG_SetFanMinPwm,
+ SMU_MSG_ConfigureGfxDidt,
+ SMU_MSG_NumOfDisplays,
+ SMU_MSG_RemoveMargins,
+ SMU_MSG_ReadSerialNumTop32,
+ SMU_MSG_ReadSerialNumBottom32,
+ SMU_MSG_SetSystemVirtualDramAddrHigh,
+ SMU_MSG_SetSystemVirtualDramAddrLow,
+ SMU_MSG_WaflTest,
+ SMU_MSG_SetFclkGfxClkRatio,
+ SMU_MSG_AllowGfxOff,
+ SMU_MSG_DisallowGfxOff,
+ SMU_MSG_GetPptLimit,
+ SMU_MSG_GetDcModeMaxDpmFreq,
+ SMU_MSG_GetDebugData,
+ SMU_MSG_SetXgmiMode,
+ SMU_MSG_RunAfllBtc,
+ SMU_MSG_ExitBaco,
+ SMU_MSG_PrepareMp1ForReset,
+ SMU_MSG_PrepareMp1ForShutdown,
+ SMU_MSG_SetMGpuFanBoostLimitRpm,
+ SMU_MSG_GetAVFSVoltageByDpm,
+ SMU_MSG_MAX_COUNT,
+};
+
+enum smu_memory_pool_size
+{
+ SMU_MEMORY_POOL_SIZE_ZERO = 0,
+ SMU_MEMORY_POOL_SIZE_256_MB = 0x10000000,
+ SMU_MEMORY_POOL_SIZE_512_MB = 0x20000000,
+ SMU_MEMORY_POOL_SIZE_1_GB = 0x40000000,
+ SMU_MEMORY_POOL_SIZE_2_GB = 0x80000000,
+};
+
+#define SMU_TABLE_INIT(tables, table_id, s, a, d) \
+ do { \
+ tables[table_id].size = s; \
+ tables[table_id].align = a; \
+ tables[table_id].domain = d; \
+ } while (0)
+
+struct smu_table {
+ uint64_t size;
+ uint32_t align;
+ uint8_t domain;
+ uint64_t mc_address;
+ void *cpu_addr;
+ struct amdgpu_bo *bo;
+};
+
+enum smu_perf_level_designation {
+ PERF_LEVEL_ACTIVITY,
+ PERF_LEVEL_POWER_CONTAINMENT,
+};
+
+struct smu_performance_level {
+ uint32_t core_clock;
+ uint32_t memory_clock;
+ uint32_t vddc;
+ uint32_t vddci;
+ uint32_t non_local_mem_freq;
+ uint32_t non_local_mem_width;
+};
+
+struct smu_clock_info {
+ uint32_t min_mem_clk;
+ uint32_t max_mem_clk;
+ uint32_t min_eng_clk;
+ uint32_t max_eng_clk;
+ uint32_t min_bus_bandwidth;
+ uint32_t max_bus_bandwidth;
+};
+
+struct smu_bios_boot_up_values
+{
+ uint32_t revision;
+ uint32_t gfxclk;
+ uint32_t uclk;
+ uint32_t socclk;
+ uint32_t dcefclk;
+ uint32_t eclk;
+ uint32_t vclk;
+ uint32_t dclk;
+ uint16_t vddc;
+ uint16_t vddci;
+ uint16_t mvddc;
+ uint16_t vdd_gfx;
+ uint8_t cooling_id;
+ uint32_t pp_table_id;
+};
+
+struct smu_table_context
+{
+ void *power_play_table;
+ uint32_t power_play_table_size;
+ void *hardcode_pptable;
+
+ void *max_sustainable_clocks;
+ struct smu_bios_boot_up_values boot_values;
+ void *driver_pptable;
+ struct smu_table *tables;
+ uint32_t table_count;
+ struct smu_table memory_pool;
+ uint16_t software_shutdown_temp;
+ uint8_t thermal_controller_type;
+ uint16_t TDPODLimit;
+
+ uint8_t *od_feature_capabilities;
+ uint32_t *od_settings_max;
+ uint32_t *od_settings_min;
+ void *overdrive_table;
+ void *od8_settings;
+ bool od_gfxclk_update;
+ bool od_memclk_update;
+};
+
+struct smu_dpm_context {
+ uint32_t dpm_context_size;
+ void *dpm_context;
+ void *golden_dpm_context;
+ bool enable_umd_pstate;
+ enum amd_dpm_forced_level dpm_level;
+ enum amd_dpm_forced_level saved_dpm_level;
+ enum amd_dpm_forced_level requested_dpm_level;
+ struct smu_power_state *dpm_request_power_state;
+ struct smu_power_state *dpm_current_power_state;
+ struct mclock_latency_table *mclk_latency_table;
+};
+
+struct smu_power_context {
+ void *power_context;
+ uint32_t power_context_size;
+};
+
+
+#define SMU_FEATURE_MAX (64)
+struct smu_feature
+{
+ uint32_t feature_num;
+ DECLARE_BITMAP(supported, SMU_FEATURE_MAX);
+ DECLARE_BITMAP(allowed, SMU_FEATURE_MAX);
+ DECLARE_BITMAP(enabled, SMU_FEATURE_MAX);
+ struct mutex mutex;
+};
+
+struct smu_clocks {
+ uint32_t engine_clock;
+ uint32_t memory_clock;
+ uint32_t bus_bandwidth;
+ uint32_t engine_clock_in_sr;
+ uint32_t dcef_clock;
+ uint32_t dcef_clock_in_sr;
+};
+
+#define MAX_REGULAR_DPM_NUM 16
+struct mclk_latency_entries {
+ uint32_t frequency;
+ uint32_t latency;
+};
+struct mclock_latency_table {
+ uint32_t count;
+ struct mclk_latency_entries entries[MAX_REGULAR_DPM_NUM];
+};
+
+#define WORKLOAD_POLICY_MAX 7
+struct smu_context
+{
+ struct amdgpu_device *adev;
+
+ const struct smu_funcs *funcs;
+ const struct pptable_funcs *ppt_funcs;
+ struct mutex mutex;
+ uint64_t pool_size;
+
+ struct smu_table_context smu_table;
+ struct smu_dpm_context smu_dpm;
+ struct smu_power_context smu_power;
+ struct smu_feature smu_feature;
+ struct amd_pp_display_configuration *display_config;
+
+ uint32_t pstate_sclk;
+ uint32_t pstate_mclk;
+
+ bool od_enabled;
+ uint32_t power_limit;
+ uint32_t default_power_limit;
+
+ bool support_power_containment;
+ bool disable_watermark;
+
+#define WATERMARKS_EXIST (1 << 0)
+#define WATERMARKS_LOADED (1 << 1)
+ uint32_t watermarks_bitmap;
+
+ uint32_t workload_mask;
+ uint32_t workload_prority[WORKLOAD_POLICY_MAX];
+ uint32_t workload_setting[WORKLOAD_POLICY_MAX];
+ uint32_t power_profile_mode;
+ uint32_t default_power_profile_mode;
+
+ uint32_t smc_if_version;
+};
+
+struct pptable_funcs {
+ int (*alloc_dpm_context)(struct smu_context *smu);
+ int (*store_powerplay_table)(struct smu_context *smu);
+ int (*check_powerplay_table)(struct smu_context *smu);
+ int (*append_powerplay_table)(struct smu_context *smu);
+ int (*get_smu_msg_index)(struct smu_context *smu, uint32_t index);
+ int (*run_afll_btc)(struct smu_context *smu);
+ int (*get_unallowed_feature_mask)(struct smu_context *smu, uint32_t *feature_mask, uint32_t num);
+ enum amd_pm_state_type (*get_current_power_state)(struct smu_context *smu);
+ int (*set_default_dpm_table)(struct smu_context *smu);
+ int (*set_power_state)(struct smu_context *smu);
+ int (*populate_umd_state_clk)(struct smu_context *smu);
+ int (*print_clk_levels)(struct smu_context *smu, enum pp_clock_type type, char *buf);
+ int (*force_clk_levels)(struct smu_context *smu, enum pp_clock_type type, uint32_t mask);
+ int (*set_default_od8_settings)(struct smu_context *smu);
+ int (*update_specified_od8_value)(struct smu_context *smu,
+ uint32_t index,
+ uint32_t value);
+ int (*get_od_percentage)(struct smu_context *smu, enum pp_clock_type type);
+ int (*set_od_percentage)(struct smu_context *smu,
+ enum pp_clock_type type,
+ uint32_t value);
+ int (*od_edit_dpm_table)(struct smu_context *smu,
+ enum PP_OD_DPM_TABLE_COMMAND type,
+ long *input, uint32_t size);
+ int (*get_clock_by_type_with_latency)(struct smu_context *smu,
+ enum amd_pp_clock_type type,
+ struct
+ pp_clock_levels_with_latency
+ *clocks);
+ int (*get_clock_by_type_with_voltage)(struct smu_context *smu,
+ enum amd_pp_clock_type type,
+ struct
+ pp_clock_levels_with_voltage
+ *clocks);
+ int (*get_power_profile_mode)(struct smu_context *smu, char *buf);
+ int (*set_power_profile_mode)(struct smu_context *smu, long *input, uint32_t size);
+ enum amd_dpm_forced_level (*get_performance_level)(struct smu_context *smu);
+ int (*force_performance_level)(struct smu_context *smu, enum amd_dpm_forced_level level);
+ int (*pre_display_config_changed)(struct smu_context *smu);
+ int (*display_config_changed)(struct smu_context *smu);
+ int (*apply_clocks_adjust_rules)(struct smu_context *smu);
+ int (*notify_smc_dispaly_config)(struct smu_context *smu);
+ int (*force_dpm_limit_value)(struct smu_context *smu, bool highest);
+ int (*unforce_dpm_levels)(struct smu_context *smu);
+ int (*upload_dpm_level)(struct smu_context *smu, bool max,
+ uint32_t feature_mask);
+ int (*get_profiling_clk_mask)(struct smu_context *smu,
+ enum amd_dpm_forced_level level,
+ uint32_t *sclk_mask,
+ uint32_t *mclk_mask,
+ uint32_t *soc_mask);
+ int (*set_cpu_power_state)(struct smu_context *smu);
+};
+
+struct smu_funcs
+{
+ int (*init_microcode)(struct smu_context *smu);
+ int (*init_smc_tables)(struct smu_context *smu);
+ int (*fini_smc_tables)(struct smu_context *smu);
+ int (*init_power)(struct smu_context *smu);
+ int (*fini_power)(struct smu_context *smu);
+ int (*load_microcode)(struct smu_context *smu);
+ int (*check_fw_status)(struct smu_context *smu);
+ int (*read_pptable_from_vbios)(struct smu_context *smu);
+ int (*get_vbios_bootup_values)(struct smu_context *smu);
+ int (*get_clk_info_from_vbios)(struct smu_context *smu);
+ int (*check_pptable)(struct smu_context *smu);
+ int (*parse_pptable)(struct smu_context *smu);
+ int (*populate_smc_pptable)(struct smu_context *smu);
+ int (*check_fw_version)(struct smu_context *smu);
+ int (*write_pptable)(struct smu_context *smu);
+ int (*set_min_dcef_deep_sleep)(struct smu_context *smu);
+ int (*set_tool_table_location)(struct smu_context *smu);
+ int (*notify_memory_pool_location)(struct smu_context *smu);
+ int (*write_watermarks_table)(struct smu_context *smu);
+ int (*set_last_dcef_min_deep_sleep_clk)(struct smu_context *smu);
+ int (*system_features_control)(struct smu_context *smu, bool en);
+ int (*send_smc_msg)(struct smu_context *smu, uint16_t msg);
+ int (*send_smc_msg_with_param)(struct smu_context *smu, uint16_t msg, uint32_t param);
+ int (*read_smc_arg)(struct smu_context *smu, uint32_t *arg);
+ int (*init_display)(struct smu_context *smu);
+ int (*set_allowed_mask)(struct smu_context *smu);
+ int (*get_enabled_mask)(struct smu_context *smu, uint32_t *feature_mask, uint32_t num);
+ bool (*is_dpm_running)(struct smu_context *smu);
+ int (*update_feature_enable_state)(struct smu_context *smu, uint32_t feature_id, bool enabled);
+ int (*notify_display_change)(struct smu_context *smu);
+ int (*get_power_limit)(struct smu_context *smu, uint32_t *limit, bool def);
+ int (*set_power_limit)(struct smu_context *smu, uint32_t n);
+ int (*get_current_clk_freq)(struct smu_context *smu, uint32_t clk_id, uint32_t *value);
+ int (*init_max_sustainable_clocks)(struct smu_context *smu);
+ int (*start_thermal_control)(struct smu_context *smu);
+ int (*read_sensor)(struct smu_context *smu, enum amd_pp_sensors sensor,
+ void *data, uint32_t *size);
+ int (*set_deep_sleep_dcefclk)(struct smu_context *smu, uint32_t clk);
+ int (*set_active_display_count)(struct smu_context *smu, uint32_t count);
+ int (*store_cc6_data)(struct smu_context *smu, uint32_t separation_time,
+ bool cc6_disable, bool pstate_disable,
+ bool pstate_switch_disable);
+ int (*get_clock_by_type)(struct smu_context *smu,
+ enum amd_pp_clock_type type,
+ struct amd_pp_clocks *clocks);
+ int (*get_max_high_clocks)(struct smu_context *smu,
+ struct amd_pp_simple_clock_info *clocks);
+ int (*display_clock_voltage_request)(struct smu_context *smu, struct
+ pp_display_clock_request
+ *clock_req);
+ int (*get_dal_power_level)(struct smu_context *smu,
+ struct amd_pp_simple_clock_info *clocks);
+ int (*get_perf_level)(struct smu_context *smu,
+ enum smu_perf_level_designation designation,
+ struct smu_performance_level *level);
+ int (*get_current_shallow_sleep_clocks)(struct smu_context *smu,
+ struct smu_clock_info *clocks);
+ int (*notify_smu_enable_pwe)(struct smu_context *smu);
+ int (*set_watermarks_for_clock_ranges)(struct smu_context *smu,
+ struct dm_pp_wm_sets_with_clock_ranges_soc15 *clock_ranges);
+ int (*set_od8_default_settings)(struct smu_context *smu,
+ bool initialize);
+ int (*conv_power_profile_to_pplib_workload)(int power_profile);
+ int (*get_power_profile_mode)(struct smu_context *smu, char *buf);
+ int (*set_power_profile_mode)(struct smu_context *smu, long *input, uint32_t size);
+ int (*update_od8_settings)(struct smu_context *smu,
+ uint32_t index,
+ uint32_t value);
+ int (*dpm_set_uvd_enable)(struct smu_context *smu, bool enable);
+ int (*dpm_set_vce_enable)(struct smu_context *smu, bool enable);
+ uint32_t (*get_sclk)(struct smu_context *smu, bool low);
+ uint32_t (*get_mclk)(struct smu_context *smu, bool low);
+ int (*get_current_rpm)(struct smu_context *smu, uint32_t *speed);
+ uint32_t (*get_fan_control_mode)(struct smu_context *smu);
+ int (*set_fan_control_mode)(struct smu_context *smu, uint32_t mode);
+ int (*get_fan_speed_percent)(struct smu_context *smu, uint32_t *speed);
+ int (*set_fan_speed_percent)(struct smu_context *smu, uint32_t speed);
+ int (*set_fan_speed_rpm)(struct smu_context *smu, uint32_t speed);
+ int (*set_xgmi_pstate)(struct smu_context *smu, uint32_t pstate);
+
+};
+
+#define smu_init_microcode(smu) \
+ ((smu)->funcs->init_microcode ? (smu)->funcs->init_microcode((smu)) : 0)
+#define smu_init_smc_tables(smu) \
+ ((smu)->funcs->init_smc_tables ? (smu)->funcs->init_smc_tables((smu)) : 0)
+#define smu_fini_smc_tables(smu) \
+ ((smu)->funcs->fini_smc_tables ? (smu)->funcs->fini_smc_tables((smu)) : 0)
+#define smu_init_power(smu) \
+ ((smu)->funcs->init_power ? (smu)->funcs->init_power((smu)) : 0)
+#define smu_fini_power(smu) \
+ ((smu)->funcs->fini_power ? (smu)->funcs->fini_power((smu)) : 0)
+#define smu_load_microcode(smu) \
+ ((smu)->funcs->load_microcode ? (smu)->funcs->load_microcode((smu)) : 0)
+#define smu_check_fw_status(smu) \
+ ((smu)->funcs->check_fw_status ? (smu)->funcs->check_fw_status((smu)) : 0)
+#define smu_read_pptable_from_vbios(smu) \
+ ((smu)->funcs->read_pptable_from_vbios ? (smu)->funcs->read_pptable_from_vbios((smu)) : 0)
+#define smu_get_vbios_bootup_values(smu) \
+ ((smu)->funcs->get_vbios_bootup_values ? (smu)->funcs->get_vbios_bootup_values((smu)) : 0)
+#define smu_get_clk_info_from_vbios(smu) \
+ ((smu)->funcs->get_clk_info_from_vbios ? (smu)->funcs->get_clk_info_from_vbios((smu)) : 0)
+#define smu_check_pptable(smu) \
+ ((smu)->funcs->check_pptable ? (smu)->funcs->check_pptable((smu)) : 0)
+#define smu_parse_pptable(smu) \
+ ((smu)->funcs->parse_pptable ? (smu)->funcs->parse_pptable((smu)) : 0)
+#define smu_populate_smc_pptable(smu) \
+ ((smu)->funcs->populate_smc_pptable ? (smu)->funcs->populate_smc_pptable((smu)) : 0)
+#define smu_check_fw_version(smu) \
+ ((smu)->funcs->check_fw_version ? (smu)->funcs->check_fw_version((smu)) : 0)
+#define smu_write_pptable(smu) \
+ ((smu)->funcs->write_pptable ? (smu)->funcs->write_pptable((smu)) : 0)
+#define smu_set_min_dcef_deep_sleep(smu) \
+ ((smu)->funcs->set_min_dcef_deep_sleep ? (smu)->funcs->set_min_dcef_deep_sleep((smu)) : 0)
+#define smu_set_tool_table_location(smu) \
+ ((smu)->funcs->set_tool_table_location ? (smu)->funcs->set_tool_table_location((smu)) : 0)
+#define smu_notify_memory_pool_location(smu) \
+ ((smu)->funcs->notify_memory_pool_location ? (smu)->funcs->notify_memory_pool_location((smu)) : 0)
+#define smu_write_watermarks_table(smu) \
+ ((smu)->funcs->write_watermarks_table ? (smu)->funcs->write_watermarks_table((smu)) : 0)
+#define smu_set_last_dcef_min_deep_sleep_clk(smu) \
+ ((smu)->funcs->set_last_dcef_min_deep_sleep_clk ? (smu)->funcs->set_last_dcef_min_deep_sleep_clk((smu)) : 0)
+#define smu_system_features_control(smu, en) \
+ ((smu)->funcs->system_features_control ? (smu)->funcs->system_features_control((smu), (en)) : 0)
+#define smu_init_max_sustainable_clocks(smu) \
+ ((smu)->funcs->init_max_sustainable_clocks ? (smu)->funcs->init_max_sustainable_clocks((smu)) : 0)
+#define smu_set_od8_default_settings(smu, initialize) \
+ ((smu)->funcs->set_od8_default_settings ? (smu)->funcs->set_od8_default_settings((smu), (initialize)) : 0)
+#define smu_update_od8_settings(smu, index, value) \
+ ((smu)->funcs->update_od8_settings ? (smu)->funcs->update_od8_settings((smu), (index), (value)) : 0)
+#define smu_get_current_rpm(smu, speed) \
+ ((smu)->funcs->get_current_rpm ? (smu)->funcs->get_current_rpm((smu), (speed)) : 0)
+#define smu_set_fan_speed_rpm(smu, speed) \
+ ((smu)->funcs->set_fan_speed_rpm ? (smu)->funcs->set_fan_speed_rpm((smu), (speed)) : 0)
+#define smu_send_smc_msg(smu, msg) \
+ ((smu)->funcs->send_smc_msg? (smu)->funcs->send_smc_msg((smu), (msg)) : 0)
+#define smu_send_smc_msg_with_param(smu, msg, param) \
+ ((smu)->funcs->send_smc_msg_with_param? (smu)->funcs->send_smc_msg_with_param((smu), (msg), (param)) : 0)
+#define smu_read_smc_arg(smu, arg) \
+ ((smu)->funcs->read_smc_arg? (smu)->funcs->read_smc_arg((smu), (arg)) : 0)
+#define smu_alloc_dpm_context(smu) \
+ ((smu)->ppt_funcs->alloc_dpm_context ? (smu)->ppt_funcs->alloc_dpm_context((smu)) : 0)
+#define smu_init_display(smu) \
+ ((smu)->funcs->init_display ? (smu)->funcs->init_display((smu)) : 0)
+#define smu_feature_set_allowed_mask(smu) \
+ ((smu)->funcs->set_allowed_mask? (smu)->funcs->set_allowed_mask((smu)) : 0)
+#define smu_feature_get_enabled_mask(smu, mask, num) \
+ ((smu)->funcs->get_enabled_mask? (smu)->funcs->get_enabled_mask((smu), (mask), (num)) : 0)
+#define smu_is_dpm_running(smu) \
+ ((smu)->funcs->is_dpm_running ? (smu)->funcs->is_dpm_running((smu)) : 0)
+#define smu_feature_update_enable_state(smu, feature_id, enabled) \
+ ((smu)->funcs->update_feature_enable_state? (smu)->funcs->update_feature_enable_state((smu), (feature_id), (enabled)) : 0)
+#define smu_notify_display_change(smu) \
+ ((smu)->funcs->notify_display_change? (smu)->funcs->notify_display_change((smu)) : 0)
+#define smu_store_powerplay_table(smu) \
+ ((smu)->ppt_funcs->store_powerplay_table ? (smu)->ppt_funcs->store_powerplay_table((smu)) : 0)
+#define smu_check_powerplay_table(smu) \
+ ((smu)->ppt_funcs->check_powerplay_table ? (smu)->ppt_funcs->check_powerplay_table((smu)) : 0)
+#define smu_append_powerplay_table(smu) \
+ ((smu)->ppt_funcs->append_powerplay_table ? (smu)->ppt_funcs->append_powerplay_table((smu)) : 0)
+#define smu_set_default_dpm_table(smu) \
+ ((smu)->ppt_funcs->set_default_dpm_table ? (smu)->ppt_funcs->set_default_dpm_table((smu)) : 0)
+#define smu_populate_umd_state_clk(smu) \
+ ((smu)->ppt_funcs->populate_umd_state_clk ? (smu)->ppt_funcs->populate_umd_state_clk((smu)) : 0)
+#define smu_set_default_od8_settings(smu) \
+ ((smu)->ppt_funcs->set_default_od8_settings ? (smu)->ppt_funcs->set_default_od8_settings((smu)) : 0)
+#define smu_update_specified_od8_value(smu, index, value) \
+ ((smu)->ppt_funcs->update_specified_od8_value ? (smu)->ppt_funcs->update_specified_od8_value((smu), (index), (value)) : 0)
+#define smu_get_power_limit(smu, limit, def) \
+ ((smu)->funcs->get_power_limit ? (smu)->funcs->get_power_limit((smu), (limit), (def)) : 0)
+#define smu_set_power_limit(smu, limit) \
+ ((smu)->funcs->set_power_limit ? (smu)->funcs->set_power_limit((smu), (limit)) : 0)
+#define smu_get_current_clk_freq(smu, clk_id, value) \
+ ((smu)->funcs->get_current_clk_freq? (smu)->funcs->get_current_clk_freq((smu), (clk_id), (value)) : 0)
+#define smu_print_clk_levels(smu, type, buf) \
+ ((smu)->ppt_funcs->print_clk_levels ? (smu)->ppt_funcs->print_clk_levels((smu), (type), (buf)) : 0)
+#define smu_force_clk_levels(smu, type, level) \
+ ((smu)->ppt_funcs->force_clk_levels ? (smu)->ppt_funcs->force_clk_levels((smu), (type), (level)) : 0)
+#define smu_get_od_percentage(smu, type) \
+ ((smu)->ppt_funcs->get_od_percentage ? (smu)->ppt_funcs->get_od_percentage((smu), (type)) : 0)
+#define smu_set_od_percentage(smu, type, value) \
+ ((smu)->ppt_funcs->set_od_percentage ? (smu)->ppt_funcs->set_od_percentage((smu), (type), (value)) : 0)
+#define smu_od_edit_dpm_table(smu, type, input, size) \
+ ((smu)->ppt_funcs->od_edit_dpm_table ? (smu)->ppt_funcs->od_edit_dpm_table((smu), (type), (input), (size)) : 0)
+#define smu_start_thermal_control(smu) \
+ ((smu)->funcs->start_thermal_control? (smu)->funcs->start_thermal_control((smu)) : 0)
+#define smu_read_sensor(smu, sensor, data, size) \
+ ((smu)->funcs->read_sensor? (smu)->funcs->read_sensor((smu), (sensor), (data), (size)) : 0)
+#define smu_get_power_profile_mode(smu, buf) \
+ ((smu)->funcs->get_power_profile_mode ? (smu)->funcs->get_power_profile_mode((smu), buf) : 0)
+#define smu_set_power_profile_mode(smu, param, param_size) \
+ ((smu)->funcs->set_power_profile_mode ? (smu)->funcs->set_power_profile_mode((smu), (param), (param_size)) : 0)
+#define smu_get_performance_level(smu) \
+ ((smu)->ppt_funcs->get_performance_level ? (smu)->ppt_funcs->get_performance_level((smu)) : 0)
+#define smu_force_performance_level(smu, level) \
+ ((smu)->ppt_funcs->force_performance_level ? (smu)->ppt_funcs->force_performance_level((smu), (level)) : 0)
+#define smu_pre_display_config_changed(smu) \
+ ((smu)->ppt_funcs->pre_display_config_changed ? (smu)->ppt_funcs->pre_display_config_changed((smu)) : 0)
+#define smu_display_config_changed(smu) \
+ ((smu)->ppt_funcs->display_config_changed ? (smu)->ppt_funcs->display_config_changed((smu)) : 0)
+#define smu_apply_clocks_adjust_rules(smu) \
+ ((smu)->ppt_funcs->apply_clocks_adjust_rules ? (smu)->ppt_funcs->apply_clocks_adjust_rules((smu)) : 0)
+#define smu_notify_smc_dispaly_config(smu) \
+ ((smu)->ppt_funcs->notify_smc_dispaly_config ? (smu)->ppt_funcs->notify_smc_dispaly_config((smu)) : 0)
+#define smu_force_dpm_limit_value(smu, highest) \
+ ((smu)->ppt_funcs->force_dpm_limit_value ? (smu)->ppt_funcs->force_dpm_limit_value((smu), (highest)) : 0)
+#define smu_unforce_dpm_levels(smu) \
+ ((smu)->ppt_funcs->unforce_dpm_levels ? (smu)->ppt_funcs->unforce_dpm_levels((smu)) : 0)
+#define smu_upload_dpm_level(smu, max, feature_mask) \
+ ((smu)->ppt_funcs->upload_dpm_level ? (smu)->ppt_funcs->upload_dpm_level((smu), (max), (feature_mask)) : 0)
+#define smu_get_profiling_clk_mask(smu, level, sclk_mask, mclk_mask, soc_mask) \
+ ((smu)->ppt_funcs->get_profiling_clk_mask ? (smu)->ppt_funcs->get_profiling_clk_mask((smu), (level), (sclk_mask), (mclk_mask), (soc_mask)) : 0)
+#define smu_set_cpu_power_state(smu) \
+ ((smu)->ppt_funcs->set_cpu_power_state ? (smu)->ppt_funcs->set_cpu_power_state((smu)) : 0)
+#define smu_get_fan_control_mode(smu) \
+ ((smu)->funcs->get_fan_control_mode ? (smu)->funcs->get_fan_control_mode((smu)) : 0)
+#define smu_set_fan_control_mode(smu, value) \
+ ((smu)->funcs->set_fan_control_mode ? (smu)->funcs->set_fan_control_mode((smu), (value)) : 0)
+#define smu_get_fan_speed_percent(smu, speed) \
+ ((smu)->funcs->get_fan_speed_percent ? (smu)->funcs->get_fan_speed_percent((smu), (speed)) : 0)
+#define smu_set_fan_speed_percent(smu, speed) \
+ ((smu)->funcs->set_fan_speed_percent ? (smu)->funcs->set_fan_speed_percent((smu), (speed)) : 0)
+
+#define smu_msg_get_index(smu, msg) \
+ ((smu)->ppt_funcs? ((smu)->ppt_funcs->get_smu_msg_index? (smu)->ppt_funcs->get_smu_msg_index((smu), (msg)) : -EINVAL) : -EINVAL)
+#define smu_run_afll_btc(smu) \
+ ((smu)->ppt_funcs? ((smu)->ppt_funcs->run_afll_btc? (smu)->ppt_funcs->run_afll_btc((smu)) : 0) : 0)
+#define smu_get_unallowed_feature_mask(smu, feature_mask, num) \
+ ((smu)->ppt_funcs? ((smu)->ppt_funcs->get_unallowed_feature_mask? (smu)->ppt_funcs->get_unallowed_feature_mask((smu), (feature_mask), (num)) : 0) : 0)
+#define smu_set_deep_sleep_dcefclk(smu, clk) \
+ ((smu)->funcs->set_deep_sleep_dcefclk ? (smu)->funcs->set_deep_sleep_dcefclk((smu), (clk)) : 0)
+#define smu_set_active_display_count(smu, count) \
+ ((smu)->funcs->set_active_display_count ? (smu)->funcs->set_active_display_count((smu), (count)) : 0)
+#define smu_store_cc6_data(smu, st, cc6_dis, pst_dis, pst_sw_dis) \
+ ((smu)->funcs->store_cc6_data ? (smu)->funcs->store_cc6_data((smu), (st), (cc6_dis), (pst_dis), (pst_sw_dis)) : 0)
+#define smu_get_clock_by_type(smu, type, clocks) \
+ ((smu)->funcs->get_clock_by_type ? (smu)->funcs->get_clock_by_type((smu), (type), (clocks)) : 0)
+#define smu_get_max_high_clocks(smu, clocks) \
+ ((smu)->funcs->get_max_high_clocks ? (smu)->funcs->get_max_high_clocks((smu), (clocks)) : 0)
+#define smu_get_clock_by_type_with_latency(smu, type, clocks) \
+ ((smu)->ppt_funcs->get_clock_by_type_with_latency ? (smu)->ppt_funcs->get_clock_by_type_with_latency((smu), (type), (clocks)) : 0)
+#define smu_get_clock_by_type_with_voltage(smu, type, clocks) \
+ ((smu)->ppt_funcs->get_clock_by_type_with_voltage ? (smu)->ppt_funcs->get_clock_by_type_with_voltage((smu), (type), (clocks)) : 0)
+#define smu_display_clock_voltage_request(smu, clock_req) \
+ ((smu)->funcs->display_clock_voltage_request ? (smu)->funcs->display_clock_voltage_request((smu), (clock_req)) : 0)
+#define smu_get_dal_power_level(smu, clocks) \
+ ((smu)->funcs->get_dal_power_level ? (smu)->funcs->get_dal_power_level((smu), (clocks)) : 0)
+#define smu_get_perf_level(smu, designation, level) \
+ ((smu)->funcs->get_perf_level ? (smu)->funcs->get_perf_level((smu), (designation), (level)) : 0)
+#define smu_get_current_shallow_sleep_clocks(smu, clocks) \
+ ((smu)->funcs->get_current_shallow_sleep_clocks ? (smu)->funcs->get_current_shallow_sleep_clocks((smu), (clocks)) : 0)
+#define smu_notify_smu_enable_pwe(smu) \
+ ((smu)->funcs->notify_smu_enable_pwe ? (smu)->funcs->notify_smu_enable_pwe((smu)) : 0)
+#define smu_set_watermarks_for_clock_ranges(smu, clock_ranges) \
+ ((smu)->funcs->set_watermarks_for_clock_ranges ? (smu)->funcs->set_watermarks_for_clock_ranges((smu), (clock_ranges)) : 0)
+#define smu_dpm_set_uvd_enable(smu, enable) \
+ ((smu)->funcs->dpm_set_uvd_enable ? (smu)->funcs->dpm_set_uvd_enable((smu), (enable)) : 0)
+#define smu_dpm_set_vce_enable(smu, enable) \
+ ((smu)->funcs->dpm_set_vce_enable ? (smu)->funcs->dpm_set_vce_enable((smu), (enable)) : 0)
+#define smu_get_sclk(smu, low) \
+ ((smu)->funcs->get_sclk ? (smu)->funcs->get_sclk((smu), (low)) : 0)
+#define smu_get_mclk(smu, low) \
+ ((smu)->funcs->get_mclk ? (smu)->funcs->get_mclk((smu), (low)) : 0)
+#define smu_set_xgmi_pstate(smu, pstate) \
+ ((smu)->funcs->set_xgmi_pstate ? (smu)->funcs->set_xgmi_pstate((smu), (pstate)) : 0)
+
+
+extern int smu_get_atom_data_table(struct smu_context *smu, uint32_t table,
+ uint16_t *size, uint8_t *frev, uint8_t *crev,
+ uint8_t **addr);
+
+extern const struct amd_ip_funcs smu_ip_funcs;
+
+extern const struct amdgpu_ip_block_version smu_v11_0_ip_block;
+extern int smu_feature_init_dpm(struct smu_context *smu);
+
+extern int smu_feature_is_enabled(struct smu_context *smu, int feature_id);
+extern int smu_feature_set_enabled(struct smu_context *smu, int feature_id, bool enable);
+extern int smu_feature_is_supported(struct smu_context *smu, int feature_id);
+extern int smu_feature_set_supported(struct smu_context *smu, int feature_id, bool enable);
+
+int smu_update_table_with_arg(struct smu_context *smu, uint16_t table_id, uint16_t exarg,
+ void *table_data, bool drv2smu);
+#define smu_update_table(smu, table_id, table_data, drv2smu) \
+ smu_update_table_with_arg((smu), (table_id), 0, (table_data), (drv2smu))
+
+bool is_support_sw_smu(struct amdgpu_device *adev);
+int smu_reset(struct smu_context *smu);
+int smu_common_read_sensor(struct smu_context *smu, enum amd_pp_sensors sensor,
+ void *data, uint32_t *size);
+int smu_sys_get_pp_table(struct smu_context *smu, void **table);
+int smu_sys_set_pp_table(struct smu_context *smu, void *buf, size_t size);
+int smu_get_power_num_states(struct smu_context *smu, struct pp_states_info *state_info);
+enum amd_pm_state_type smu_get_current_power_state(struct smu_context *smu);
+
+/* smu to display interface */
+extern int smu_display_configuration_change(struct smu_context *smu, const
+ struct amd_pp_display_configuration
+ *display_config);
+extern int smu_get_current_clocks(struct smu_context *smu,
+ struct amd_pp_clock_info *clocks);
+extern int smu_dpm_set_power_gate(struct smu_context *smu,uint32_t block_type, bool gate);
+extern int smu_handle_task(struct smu_context *smu,
+ enum amd_dpm_forced_level level,
+ enum amd_pp_task task_id);
+#endif
diff --git a/drivers/gpu/drm/amd/powerplay/inc/rv_ppsmc.h b/drivers/gpu/drm/amd/powerplay/inc/rv_ppsmc.h
index a2991fa2e6f8..90879e4092a3 100644
--- a/drivers/gpu/drm/amd/powerplay/inc/rv_ppsmc.h
+++ b/drivers/gpu/drm/amd/powerplay/inc/rv_ppsmc.h
@@ -85,7 +85,6 @@
#define PPSMC_MSG_SetRccPfcPmeRestoreRegister 0x36
#define PPSMC_Message_Count 0x37
-
typedef uint16_t PPSMC_Result;
typedef int PPSMC_Msg;
diff --git a/drivers/gpu/drm/amd/powerplay/inc/smu10.h b/drivers/gpu/drm/amd/powerplay/inc/smu10.h
index 9e837a5014c5..b96520528240 100644
--- a/drivers/gpu/drm/amd/powerplay/inc/smu10.h
+++ b/drivers/gpu/drm/amd/powerplay/inc/smu10.h
@@ -136,12 +136,14 @@
#define FEATURE_CORE_CSTATES_MASK (1 << FEATURE_CORE_CSTATES_BIT)
/* Workload bits */
-#define WORKLOAD_PPLIB_FULL_SCREEN_3D_BIT 0
-#define WORKLOAD_PPLIB_VIDEO_BIT 2
-#define WORKLOAD_PPLIB_VR_BIT 3
-#define WORKLOAD_PPLIB_COMPUTE_BIT 4
-#define WORKLOAD_PPLIB_CUSTOM_BIT 5
-#define WORKLOAD_PPLIB_COUNT 6
+#define WORKLOAD_DEFAULT_BIT 0
+#define WORKLOAD_PPLIB_FULL_SCREEN_3D_BIT 1
+#define WORKLOAD_PPLIB_POWER_SAVING_BIT 2
+#define WORKLOAD_PPLIB_VIDEO_BIT 3
+#define WORKLOAD_PPLIB_VR_BIT 4
+#define WORKLOAD_PPLIB_COMPUTE_BIT 5
+#define WORKLOAD_PPLIB_CUSTOM_BIT 6
+#define WORKLOAD_PPLIB_COUNT 7
typedef struct {
/* MP1_EXT_SCRATCH0 */
diff --git a/drivers/gpu/drm/amd/powerplay/inc/smu_v11_0.h b/drivers/gpu/drm/amd/powerplay/inc/smu_v11_0.h
new file mode 100644
index 000000000000..aa8d81f4111e
--- /dev/null
+++ b/drivers/gpu/drm/amd/powerplay/inc/smu_v11_0.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#ifndef __SMU_V11_0_H__
+#define __SMU_V11_0_H__
+
+#include "amdgpu_smu.h"
+
+/* MP Apertures */
+#define MP0_Public 0x03800000
+#define MP0_SRAM 0x03900000
+#define MP1_Public 0x03b00000
+#define MP1_SRAM 0x03c00004
+
+/* address block */
+#define smnMP1_FIRMWARE_FLAGS 0x3010024
+#define smnMP0_FW_INTF 0x30101c0
+#define smnMP1_PUB_CTRL 0x3010b14
+
+struct smu_11_0_max_sustainable_clocks {
+ uint32_t display_clock;
+ uint32_t phy_clock;
+ uint32_t pixel_clock;
+ uint32_t uclock;
+ uint32_t dcef_clock;
+ uint32_t soc_clock;
+};
+
+struct smu_11_0_dpm_table {
+ uint32_t min; /* MHz */
+ uint32_t max; /* MHz */
+};
+
+struct smu_11_0_dpm_tables {
+ struct smu_11_0_dpm_table soc_table;
+ struct smu_11_0_dpm_table gfx_table;
+ struct smu_11_0_dpm_table uclk_table;
+ struct smu_11_0_dpm_table eclk_table;
+ struct smu_11_0_dpm_table vclk_table;
+ struct smu_11_0_dpm_table dclk_table;
+ struct smu_11_0_dpm_table dcef_table;
+ struct smu_11_0_dpm_table pixel_table;
+ struct smu_11_0_dpm_table display_table;
+ struct smu_11_0_dpm_table phy_table;
+ struct smu_11_0_dpm_table fclk_table;
+};
+
+struct smu_11_0_dpm_context {
+ struct smu_11_0_dpm_tables dpm_tables;
+ uint32_t workload_policy_mask;
+ uint32_t dcef_min_ds_clk;
+};
+
+enum smu_11_0_power_state {
+ SMU_11_0_POWER_STATE__D0 = 0,
+ SMU_11_0_POWER_STATE__D1,
+ SMU_11_0_POWER_STATE__D3, /* Sleep*/
+ SMU_11_0_POWER_STATE__D4, /* Hibernate*/
+ SMU_11_0_POWER_STATE__D5, /* Power off*/
+};
+
+struct smu_11_0_power_context {
+ uint32_t power_source;
+ uint8_t in_power_limit_boost_mode;
+ enum smu_11_0_power_state power_state;
+};
+
+void smu_v11_0_set_smu_funcs(struct smu_context *smu);
+
+#endif
diff --git a/drivers/gpu/drm/amd/powerplay/inc/smu_v11_0_ppsmc.h b/drivers/gpu/drm/amd/powerplay/inc/smu_v11_0_ppsmc.h
new file mode 100644
index 000000000000..f466f624ad32
--- /dev/null
+++ b/drivers/gpu/drm/amd/powerplay/inc/smu_v11_0_ppsmc.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef SMU_V11_0_PPSMC_H
+#define SMU_V11_0_PPSMC_H
+
+// SMU Response Codes:
+#define PPSMC_Result_OK 0x1
+#define PPSMC_Result_Failed 0xFF
+#define PPSMC_Result_UnknownCmd 0xFE
+#define PPSMC_Result_CmdRejectedPrereq 0xFD
+#define PPSMC_Result_CmdRejectedBusy 0xFC
+
+// Message Definitions:
+// BASIC
+#define PPSMC_MSG_TestMessage 0x1
+#define PPSMC_MSG_GetSmuVersion 0x2
+#define PPSMC_MSG_GetDriverIfVersion 0x3
+#define PPSMC_MSG_SetAllowedFeaturesMaskLow 0x4
+#define PPSMC_MSG_SetAllowedFeaturesMaskHigh 0x5
+#define PPSMC_MSG_EnableAllSmuFeatures 0x6
+#define PPSMC_MSG_DisableAllSmuFeatures 0x7
+#define PPSMC_MSG_EnableSmuFeaturesLow 0x8
+#define PPSMC_MSG_EnableSmuFeaturesHigh 0x9
+#define PPSMC_MSG_DisableSmuFeaturesLow 0xA
+#define PPSMC_MSG_DisableSmuFeaturesHigh 0xB
+#define PPSMC_MSG_GetEnabledSmuFeaturesLow 0xC
+#define PPSMC_MSG_GetEnabledSmuFeaturesHigh 0xD
+#define PPSMC_MSG_SetDriverDramAddrHigh 0xE
+#define PPSMC_MSG_SetDriverDramAddrLow 0xF
+#define PPSMC_MSG_SetToolsDramAddrHigh 0x10
+#define PPSMC_MSG_SetToolsDramAddrLow 0x11
+#define PPSMC_MSG_TransferTableSmu2Dram 0x12
+#define PPSMC_MSG_TransferTableDram2Smu 0x13
+#define PPSMC_MSG_UseDefaultPPTable 0x14
+#define PPSMC_MSG_UseBackupPPTable 0x15
+#define PPSMC_MSG_SetSystemVirtualDramAddrHigh 0x16
+#define PPSMC_MSG_SetSystemVirtualDramAddrLow 0x17
+
+//BACO/BAMACO/BOMACO
+#define PPSMC_MSG_EnterBaco 0x18
+#define PPSMC_MSG_ExitBaco 0x19
+
+//DPM
+#define PPSMC_MSG_SetSoftMinByFreq 0x1A
+#define PPSMC_MSG_SetSoftMaxByFreq 0x1B
+#define PPSMC_MSG_SetHardMinByFreq 0x1C
+#define PPSMC_MSG_SetHardMaxByFreq 0x1D
+#define PPSMC_MSG_GetMinDpmFreq 0x1E
+#define PPSMC_MSG_GetMaxDpmFreq 0x1F
+#define PPSMC_MSG_GetDpmFreqByIndex 0x20
+#define PPSMC_MSG_OverridePcieParameters 0x21
+#define PPSMC_MSG_SetMinDeepSleepDcefclk 0x22
+#define PPSMC_MSG_SetWorkloadMask 0x23
+#define PPSMC_MSG_SetUclkFastSwitch 0x24
+#define PPSMC_MSG_GetAvfsVoltageByDpm 0x25
+#define PPSMC_MSG_SetVideoFps 0x26
+#define PPSMC_MSG_GetDcModeMaxDpmFreq 0x27
+
+//Power Gating
+#define PPSMC_MSG_AllowGfxOff 0x28
+#define PPSMC_MSG_DisallowGfxOff 0x29
+#define PPSMC_MSG_PowerUpVcn 0x2A
+#define PPSMC_MSG_PowerDownVcn 0x2B
+#define PPSMC_MSG_PowerUpJpeg 0x2C
+#define PPSMC_MSG_PowerDownJpeg 0x2D
+//reserve 0x2A to 0x2F for PG harvesting TBD
+
+//I2C Interface
+#define PPSMC_RequestI2cTransaction 0x30
+
+//Resets
+#define PPSMC_MSG_SoftReset 0x31 //FIXME Need confirmation from driver
+#define PPSMC_MSG_PrepareMp1ForUnload 0x32
+#define PPSMC_MSG_PrepareMp1ForReset 0x33
+#define PPSMC_MSG_PrepareMp1ForShutdown 0x34
+
+//ACDC Power Source
+#define PPSMC_MSG_SetPptLimit 0x35
+#define PPSMC_MSG_GetPptLimit 0x36
+#define PPSMC_MSG_ReenableAcDcInterrupt 0x37
+#define PPSMC_MSG_NotifyPowerSource 0x38
+//#define PPSMC_MSG_GfxDeviceDriverReset 0x39 //FIXME mode1 and 2 resets will go directly go PSP
+
+//BTC
+#define PPSMC_MSG_RunBtc 0x3A
+
+//Debug
+#define PPSMC_MSG_DramLogSetDramAddrHigh 0x3B
+#define PPSMC_MSG_DramLogSetDramAddrLow 0x3C
+#define PPSMC_MSG_DramLogSetDramSize 0x3D
+#define PPSMC_MSG_GetDebugData 0x3E
+
+//Others
+#define PPSMC_MSG_ConfigureGfxDidt 0x3F
+#define PPSMC_MSG_NumOfDisplays 0x40
+
+#define PPSMC_MSG_SetMemoryChannelConfig 0x41
+#define PPSMC_MSG_SetGeminiMode 0x42
+#define PPSMC_MSG_SetGeminiApertureHigh 0x43
+#define PPSMC_MSG_SetGeminiApertureLow 0x44
+
+#define PPSMC_Message_Count 0x45
+
+typedef uint32_t PPSMC_Result;
+typedef uint32_t PPSMC_Msg;
+
+#endif
diff --git a/drivers/gpu/drm/amd/powerplay/inc/smu_v11_0_pptable.h b/drivers/gpu/drm/amd/powerplay/inc/smu_v11_0_pptable.h
new file mode 100644
index 000000000000..92c65b80bde2
--- /dev/null
+++ b/drivers/gpu/drm/amd/powerplay/inc/smu_v11_0_pptable.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef SMU_11_0_PPTABLE_H
+#define SMU_11_0_PPTABLE_H
+
+
+#define SMU_11_0_TABLE_FORMAT_REVISION 12
+
+//// POWERPLAYTABLE::ulPlatformCaps
+#define SMU_11_0_PP_PLATFORM_CAP_POWERPLAY 0x1
+#define SMU_11_0_PP_PLATFORM_CAP_SBIOSPOWERSOURCE 0x2
+#define SMU_11_0_PP_PLATFORM_CAP_HARDWAREDC 0x4
+#define SMU_11_0_PP_PLATFORM_CAP_BACO 0x8
+#define SMU_11_0_PP_PLATFORM_CAP_MACO 0x10
+#define SMU_11_0_PP_PLATFORM_CAP_SHADOWPSTATE 0x20
+
+// SMU_11_0_PP_THERMALCONTROLLER - Thermal Controller Type
+#define SMU_11_0_PP_THERMALCONTROLLER_NONE 0
+
+#define SMU_11_0_PP_OVERDRIVE_VERSION 0x0800
+#define SMU_11_0_PP_POWERSAVINGCLOCK_VERSION 0x0100
+
+enum SMU_11_0_ODFEATURE_ID {
+ SMU_11_0_ODFEATURE_GFXCLK_LIMITS = 1 << 0, //GFXCLK Limit feature
+ SMU_11_0_ODFEATURE_GFXCLK_CURVE = 1 << 1, //GFXCLK Curve feature
+ SMU_11_0_ODFEATURE_UCLK_MAX = 1 << 2, //UCLK Limit feature
+ SMU_11_0_ODFEATURE_POWER_LIMIT = 1 << 3, //Power Limit feature
+ SMU_11_0_ODFEATURE_FAN_ACOUSTIC_LIMIT = 1 << 4, //Fan Acoustic RPM feature
+ SMU_11_0_ODFEATURE_FAN_SPEED_MIN = 1 << 5, //Minimum Fan Speed feature
+ SMU_11_0_ODFEATURE_TEMPERATURE_FAN = 1 << 6, //Fan Target Temperature Limit feature
+ SMU_11_0_ODFEATURE_TEMPERATURE_SYSTEM = 1 << 7, //Operating Temperature Limit feature
+ SMU_11_0_ODFEATURE_MEMORY_TIMING_TUNE = 1 << 8, //AC Timing Tuning feature
+ SMU_11_0_ODFEATURE_FAN_ZERO_RPM_CONTROL = 1 << 9, //Zero RPM feature
+ SMU_11_0_ODFEATURE_AUTO_UV_ENGINE = 1 << 10, //Auto Under Volt GFXCLK feature
+ SMU_11_0_ODFEATURE_AUTO_OC_ENGINE = 1 << 11, //Auto Over Clock GFXCLK feature
+ SMU_11_0_ODFEATURE_AUTO_OC_MEMORY = 1 << 12, //Auto Over Clock MCLK feature
+ SMU_11_0_ODFEATURE_FAN_CURVE = 1 << 13, //VICTOR TODO
+ SMU_11_0_ODFEATURE_COUNT = 14,
+};
+#define SMU_11_0_MAX_ODFEATURE 32 //Maximum Number of OD Features
+
+enum SMU_11_0_ODSETTING_ID {
+ SMU_11_0_ODSETTING_GFXCLKFMAX = 0,
+ SMU_11_0_ODSETTING_GFXCLKFMIN,
+ SMU_11_0_ODSETTING_VDDGFXCURVEFREQ_P1,
+ SMU_11_0_ODSETTING_VDDGFXCURVEVOLTAGE_P1,
+ SMU_11_0_ODSETTING_VDDGFXCURVEFREQ_P2,
+ SMU_11_0_ODSETTING_VDDGFXCURVEVOLTAGE_P2,
+ SMU_11_0_ODSETTING_VDDGFXCURVEFREQ_P3,
+ SMU_11_0_ODSETTING_VDDGFXCURVEVOLTAGE_P3,
+ SMU_11_0_ODSETTING_UCLKFMAX,
+ SMU_11_0_ODSETTING_POWERPERCENTAGE,
+ SMU_11_0_ODSETTING_FANRPMMIN,
+ SMU_11_0_ODSETTING_FANRPMACOUSTICLIMIT,
+ SMU_11_0_ODSETTING_FANTARGETTEMPERATURE,
+ SMU_11_0_ODSETTING_OPERATINGTEMPMAX,
+ SMU_11_0_ODSETTING_ACTIMING,
+ SMU_11_0_ODSETTING_FAN_ZERO_RPM_CONTROL,
+ SMU_11_0_ODSETTING_AUTOUVENGINE,
+ SMU_11_0_ODSETTING_AUTOOCENGINE,
+ SMU_11_0_ODSETTING_AUTOOCMEMORY,
+ SMU_11_0_ODSETTING_COUNT,
+};
+#define SMU_11_0_MAX_ODSETTING 32 //Maximum Number of ODSettings
+
+struct smu_11_0_overdrive_table
+{
+ uint8_t revision; //Revision = SMU_11_0_PP_OVERDRIVE_VERSION
+ uint8_t reserve[3]; //Zero filled field reserved for future use
+ uint32_t feature_count; //Total number of supported features
+ uint32_t setting_count; //Total number of supported settings
+ uint8_t cap[SMU_11_0_MAX_ODFEATURE]; //OD feature support flags
+ uint32_t max[SMU_11_0_MAX_ODSETTING]; //default maximum settings
+ uint32_t min[SMU_11_0_MAX_ODSETTING]; //default minimum settings
+} __attribute__((packed));
+
+enum SMU_11_0_PPCLOCK_ID {
+ SMU_11_0_PPCLOCK_GFXCLK = 0,
+ SMU_11_0_PPCLOCK_VCLK,
+ SMU_11_0_PPCLOCK_DCLK,
+ SMU_11_0_PPCLOCK_ECLK,
+ SMU_11_0_PPCLOCK_SOCCLK,
+ SMU_11_0_PPCLOCK_UCLK,
+ SMU_11_0_PPCLOCK_DCEFCLK,
+ SMU_11_0_PPCLOCK_DISPCLK,
+ SMU_11_0_PPCLOCK_PIXCLK,
+ SMU_11_0_PPCLOCK_PHYCLK,
+ SMU_11_0_PPCLOCK_COUNT,
+};
+#define SMU_11_0_MAX_PPCLOCK 16 //Maximum Number of PP Clocks
+
+struct smu_11_0_power_saving_clock_table
+{
+ uint8_t revision; //Revision = SMU_11_0_PP_POWERSAVINGCLOCK_VERSION
+ uint8_t reserve[3]; //Zero filled field reserved for future use
+ uint32_t count; //power_saving_clock_count = SMU_11_0_PPCLOCK_COUNT
+ uint32_t max[SMU_11_0_MAX_PPCLOCK]; //PowerSavingClock Mode Clock Maximum array In MHz
+ uint32_t min[SMU_11_0_MAX_PPCLOCK]; //PowerSavingClock Mode Clock Minimum array In MHz
+} __attribute__((packed));
+
+struct smu_11_0_powerplay_table
+{
+ struct atom_common_table_header header;
+ uint8_t table_revision;
+ uint32_t table_size; //Driver portion table size. The offset to smc_pptable including header size
+ uint32_t golden_pp_id;
+ uint32_t golden_revision;
+ uint16_t format_id;
+ uint32_t platform_caps; //POWERPLAYABLE::ulPlatformCaps
+
+ uint8_t thermal_controller_type; //one of SMU_11_0_PP_THERMALCONTROLLER
+
+ uint16_t small_power_limit1;
+ uint16_t small_power_limit2;
+ uint16_t boost_power_limit;
+ uint16_t od_turbo_power_limit; //Power limit setting for Turbo mode in Performance UI Tuning.
+ uint16_t od_power_save_power_limit; //Power limit setting for PowerSave/Optimal mode in Performance UI Tuning.
+ uint16_t software_shutdown_temp;
+
+ uint16_t reserve[6]; //Zero filled field reserved for future use
+
+ struct smu_11_0_power_saving_clock_table power_saving_clock;
+ struct smu_11_0_overdrive_table overdrive_table;
+
+ PPTable_t smc_pptable; //PPTable_t in smu11_driver_if.h
+} __attribute__((packed));
+
+#endif
diff --git a/drivers/gpu/drm/amd/powerplay/inc/vega20_ppsmc.h b/drivers/gpu/drm/amd/powerplay/inc/vega20_ppsmc.h
index 4f63a736ea0e..a0883038f3c3 100644
--- a/drivers/gpu/drm/amd/powerplay/inc/vega20_ppsmc.h
+++ b/drivers/gpu/drm/amd/powerplay/inc/vega20_ppsmc.h
@@ -119,7 +119,8 @@
#define PPSMC_MSG_PrepareMp1ForShutdown 0x5A
#define PPSMC_MSG_SetMGpuFanBoostLimitRpm 0x5D
#define PPSMC_MSG_GetAVFSVoltageByDpm 0x5F
-#define PPSMC_Message_Count 0x60
+#define PPSMC_MSG_BacoWorkAroundFlushVDCI 0x60
+#define PPSMC_Message_Count 0x61
typedef uint32_t PPSMC_Result;
typedef uint32_t PPSMC_Msg;
diff --git a/drivers/gpu/drm/amd/powerplay/smu_v11_0.c b/drivers/gpu/drm/amd/powerplay/smu_v11_0.c
new file mode 100644
index 000000000000..92903a4cc4d8
--- /dev/null
+++ b/drivers/gpu/drm/amd/powerplay/smu_v11_0.c
@@ -0,0 +1,1977 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "pp_debug.h"
+#include <linux/firmware.h>
+#include "amdgpu.h"
+#include "amdgpu_smu.h"
+#include "atomfirmware.h"
+#include "amdgpu_atomfirmware.h"
+#include "smu_v11_0.h"
+#include "smu11_driver_if.h"
+#include "soc15_common.h"
+#include "atom.h"
+#include "vega20_ppt.h"
+#include "pp_thermal.h"
+
+#include "asic_reg/thm/thm_11_0_2_offset.h"
+#include "asic_reg/thm/thm_11_0_2_sh_mask.h"
+#include "asic_reg/mp/mp_9_0_offset.h"
+#include "asic_reg/mp/mp_9_0_sh_mask.h"
+#include "asic_reg/nbio/nbio_7_4_offset.h"
+#include "asic_reg/smuio/smuio_9_0_offset.h"
+#include "asic_reg/smuio/smuio_9_0_sh_mask.h"
+
+MODULE_FIRMWARE("amdgpu/vega20_smc.bin");
+
+#define SMU11_TOOL_SIZE 0x19000
+#define SMU11_THERMAL_MINIMUM_ALERT_TEMP 0
+#define SMU11_THERMAL_MAXIMUM_ALERT_TEMP 255
+
+#define SMU11_TEMPERATURE_UNITS_PER_CENTIGRADES 1000
+#define SMU11_VOLTAGE_SCALE 4
+
+#define SMC_DPM_FEATURE (FEATURE_DPM_PREFETCHER_MASK | \
+ FEATURE_DPM_GFXCLK_MASK | \
+ FEATURE_DPM_UCLK_MASK | \
+ FEATURE_DPM_SOCCLK_MASK | \
+ FEATURE_DPM_UVD_MASK | \
+ FEATURE_DPM_VCE_MASK | \
+ FEATURE_DPM_MP0CLK_MASK | \
+ FEATURE_DPM_LINK_MASK | \
+ FEATURE_DPM_DCEFCLK_MASK)
+
+static int smu_v11_0_send_msg_without_waiting(struct smu_context *smu,
+ uint16_t msg)
+{
+ struct amdgpu_device *adev = smu->adev;
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_66, msg);
+ return 0;
+}
+
+static int smu_v11_0_read_arg(struct smu_context *smu, uint32_t *arg)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ *arg = RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_82);
+ return 0;
+}
+
+static int smu_v11_0_wait_for_response(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t cur_value, i;
+
+ for (i = 0; i < adev->usec_timeout; i++) {
+ cur_value = RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90);
+ if ((cur_value & MP1_C2PMSG_90__CONTENT_MASK) != 0)
+ break;
+ udelay(1);
+ }
+
+ /* timeout means wrong logic */
+ if (i == adev->usec_timeout)
+ return -ETIME;
+
+ return RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90) == 0x1 ? 0 : -EIO;
+}
+
+static int smu_v11_0_send_msg(struct smu_context *smu, uint16_t msg)
+{
+ struct amdgpu_device *adev = smu->adev;
+ int ret = 0, index = 0;
+
+ index = smu_msg_get_index(smu, msg);
+ if (index < 0)
+ return index;
+
+ smu_v11_0_wait_for_response(smu);
+
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90, 0);
+
+ smu_v11_0_send_msg_without_waiting(smu, (uint16_t)index);
+
+ ret = smu_v11_0_wait_for_response(smu);
+
+ if (ret)
+ pr_err("Failed to send message 0x%x, response 0x%x\n", index,
+ ret);
+
+ return ret;
+
+}
+
+static int
+smu_v11_0_send_msg_with_param(struct smu_context *smu, uint16_t msg,
+ uint32_t param)
+{
+
+ struct amdgpu_device *adev = smu->adev;
+ int ret = 0, index = 0;
+
+ index = smu_msg_get_index(smu, msg);
+ if (index < 0)
+ return index;
+
+ ret = smu_v11_0_wait_for_response(smu);
+ if (ret)
+ pr_err("Failed to send message 0x%x, response 0x%x, param 0x%x\n",
+ index, ret, param);
+
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90, 0);
+
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_82, param);
+
+ smu_v11_0_send_msg_without_waiting(smu, (uint16_t)index);
+
+ ret = smu_v11_0_wait_for_response(smu);
+ if (ret)
+ pr_err("Failed to send message 0x%x, response 0x%x param 0x%x\n",
+ index, ret, param);
+
+ return ret;
+}
+
+static int smu_v11_0_init_microcode(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ const char *chip_name;
+ char fw_name[30];
+ int err = 0;
+ const struct smc_firmware_header_v1_0 *hdr;
+ const struct common_firmware_header *header;
+ struct amdgpu_firmware_info *ucode = NULL;
+
+ switch (adev->asic_type) {
+ case CHIP_VEGA20:
+ chip_name = "vega20";
+ break;
+ default:
+ BUG();
+ }
+
+ snprintf(fw_name, sizeof(fw_name), "amdgpu/%s_smc.bin", chip_name);
+
+ err = request_firmware(&adev->pm.fw, fw_name, adev->dev);
+ if (err)
+ goto out;
+ err = amdgpu_ucode_validate(adev->pm.fw);
+ if (err)
+ goto out;
+
+ hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data;
+ amdgpu_ucode_print_smc_hdr(&hdr->header);
+ adev->pm.fw_version = le32_to_cpu(hdr->header.ucode_version);
+
+ if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
+ ucode = &adev->firmware.ucode[AMDGPU_UCODE_ID_SMC];
+ ucode->ucode_id = AMDGPU_UCODE_ID_SMC;
+ ucode->fw = adev->pm.fw;
+ header = (const struct common_firmware_header *)ucode->fw->data;
+ adev->firmware.fw_size +=
+ ALIGN(le32_to_cpu(header->ucode_size_bytes), PAGE_SIZE);
+ }
+
+out:
+ if (err) {
+ DRM_ERROR("smu_v11_0: Failed to load firmware \"%s\"\n",
+ fw_name);
+ release_firmware(adev->pm.fw);
+ adev->pm.fw = NULL;
+ }
+ return err;
+}
+
+static int smu_v11_0_load_microcode(struct smu_context *smu)
+{
+ return 0;
+}
+
+static int smu_v11_0_check_fw_status(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t mp1_fw_flags;
+
+ mp1_fw_flags = RREG32_PCIE(MP1_Public |
+ (smnMP1_FIRMWARE_FLAGS & 0xffffffff));
+
+ if ((mp1_fw_flags & MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED_MASK) >>
+ MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED__SHIFT)
+ return 0;
+
+ return -EIO;
+}
+
+static int smu_v11_0_check_fw_version(struct smu_context *smu)
+{
+ uint32_t smu_version = 0xff;
+ int ret = 0;
+
+ ret = smu_send_smc_msg(smu, SMU_MSG_GetDriverIfVersion);
+ if (ret)
+ goto err;
+
+ ret = smu_read_smc_arg(smu, &smu_version);
+ if (ret)
+ goto err;
+
+ if (smu_version != smu->smc_if_version)
+ ret = -EINVAL;
+err:
+ return ret;
+}
+
+static int smu_v11_0_read_pptable_from_vbios(struct smu_context *smu)
+{
+ int ret, index;
+ uint16_t size;
+ uint8_t frev, crev;
+ void *table;
+
+ index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1,
+ powerplayinfo);
+
+ ret = smu_get_atom_data_table(smu, index, &size, &frev, &crev,
+ (uint8_t **)&table);
+ if (ret)
+ return ret;
+
+ if (!smu->smu_table.power_play_table)
+ smu->smu_table.power_play_table = table;
+ if (!smu->smu_table.power_play_table_size)
+ smu->smu_table.power_play_table_size = size;
+
+ return 0;
+}
+
+static int smu_v11_0_init_dpm_context(struct smu_context *smu)
+{
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+
+ if (smu_dpm->dpm_context || smu_dpm->dpm_context_size != 0)
+ return -EINVAL;
+
+ return smu_alloc_dpm_context(smu);
+}
+
+static int smu_v11_0_fini_dpm_context(struct smu_context *smu)
+{
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+
+ if (!smu_dpm->dpm_context || smu_dpm->dpm_context_size == 0)
+ return -EINVAL;
+
+ kfree(smu_dpm->dpm_context);
+ kfree(smu_dpm->golden_dpm_context);
+ kfree(smu_dpm->dpm_current_power_state);
+ kfree(smu_dpm->dpm_request_power_state);
+ smu_dpm->dpm_context = NULL;
+ smu_dpm->golden_dpm_context = NULL;
+ smu_dpm->dpm_context_size = 0;
+ smu_dpm->dpm_current_power_state = NULL;
+ smu_dpm->dpm_request_power_state = NULL;
+
+ return 0;
+}
+
+static int smu_v11_0_init_smc_tables(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *tables = NULL;
+ int ret = 0;
+
+ if (smu_table->tables || smu_table->table_count != 0)
+ return -EINVAL;
+
+ tables = kcalloc(TABLE_COUNT, sizeof(struct smu_table), GFP_KERNEL);
+ if (!tables)
+ return -ENOMEM;
+
+ smu_table->tables = tables;
+ smu_table->table_count = TABLE_COUNT;
+
+ SMU_TABLE_INIT(tables, TABLE_PPTABLE, sizeof(PPTable_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, TABLE_WATERMARKS, sizeof(Watermarks_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, TABLE_SMU_METRICS, sizeof(SmuMetrics_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, TABLE_OVERDRIVE, sizeof(OverDriveTable_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, TABLE_PMSTATUSLOG, SMU11_TOOL_SIZE, PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, TABLE_ACTIVITY_MONITOR_COEFF,
+ sizeof(DpmActivityMonitorCoeffInt_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM);
+
+ ret = smu_v11_0_init_dpm_context(smu);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int smu_v11_0_fini_smc_tables(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ int ret = 0;
+
+ if (!smu_table->tables || smu_table->table_count == 0)
+ return -EINVAL;
+
+ kfree(smu_table->tables);
+ smu_table->tables = NULL;
+ smu_table->table_count = 0;
+
+ ret = smu_v11_0_fini_dpm_context(smu);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+static int smu_v11_0_init_power(struct smu_context *smu)
+{
+ struct smu_power_context *smu_power = &smu->smu_power;
+
+ if (smu_power->power_context || smu_power->power_context_size != 0)
+ return -EINVAL;
+
+ smu_power->power_context = kzalloc(sizeof(struct smu_11_0_dpm_context),
+ GFP_KERNEL);
+ if (!smu_power->power_context)
+ return -ENOMEM;
+ smu_power->power_context_size = sizeof(struct smu_11_0_dpm_context);
+
+ return 0;
+}
+
+static int smu_v11_0_fini_power(struct smu_context *smu)
+{
+ struct smu_power_context *smu_power = &smu->smu_power;
+
+ if (!smu_power->power_context || smu_power->power_context_size == 0)
+ return -EINVAL;
+
+ kfree(smu_power->power_context);
+ smu_power->power_context = NULL;
+ smu_power->power_context_size = 0;
+
+ return 0;
+}
+
+int smu_v11_0_get_vbios_bootup_values(struct smu_context *smu)
+{
+ int ret, index;
+ uint16_t size;
+ uint8_t frev, crev;
+ struct atom_common_table_header *header;
+ struct atom_firmware_info_v3_3 *v_3_3;
+ struct atom_firmware_info_v3_1 *v_3_1;
+
+ index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1,
+ firmwareinfo);
+
+ ret = smu_get_atom_data_table(smu, index, &size, &frev, &crev,
+ (uint8_t **)&header);
+ if (ret)
+ return ret;
+
+ if (header->format_revision != 3) {
+ pr_err("unknown atom_firmware_info version! for smu11\n");
+ return -EINVAL;
+ }
+
+ switch (header->content_revision) {
+ case 0:
+ case 1:
+ case 2:
+ v_3_1 = (struct atom_firmware_info_v3_1 *)header;
+ smu->smu_table.boot_values.revision = v_3_1->firmware_revision;
+ smu->smu_table.boot_values.gfxclk = v_3_1->bootup_sclk_in10khz;
+ smu->smu_table.boot_values.uclk = v_3_1->bootup_mclk_in10khz;
+ smu->smu_table.boot_values.socclk = 0;
+ smu->smu_table.boot_values.dcefclk = 0;
+ smu->smu_table.boot_values.vddc = v_3_1->bootup_vddc_mv;
+ smu->smu_table.boot_values.vddci = v_3_1->bootup_vddci_mv;
+ smu->smu_table.boot_values.mvddc = v_3_1->bootup_mvddc_mv;
+ smu->smu_table.boot_values.vdd_gfx = v_3_1->bootup_vddgfx_mv;
+ smu->smu_table.boot_values.cooling_id = v_3_1->coolingsolution_id;
+ smu->smu_table.boot_values.pp_table_id = 0;
+ break;
+ case 3:
+ default:
+ v_3_3 = (struct atom_firmware_info_v3_3 *)header;
+ smu->smu_table.boot_values.revision = v_3_3->firmware_revision;
+ smu->smu_table.boot_values.gfxclk = v_3_3->bootup_sclk_in10khz;
+ smu->smu_table.boot_values.uclk = v_3_3->bootup_mclk_in10khz;
+ smu->smu_table.boot_values.socclk = 0;
+ smu->smu_table.boot_values.dcefclk = 0;
+ smu->smu_table.boot_values.vddc = v_3_3->bootup_vddc_mv;
+ smu->smu_table.boot_values.vddci = v_3_3->bootup_vddci_mv;
+ smu->smu_table.boot_values.mvddc = v_3_3->bootup_mvddc_mv;
+ smu->smu_table.boot_values.vdd_gfx = v_3_3->bootup_vddgfx_mv;
+ smu->smu_table.boot_values.cooling_id = v_3_3->coolingsolution_id;
+ smu->smu_table.boot_values.pp_table_id = v_3_3->pplib_pptable_id;
+ }
+
+ return 0;
+}
+
+static int smu_v11_0_get_clk_info_from_vbios(struct smu_context *smu)
+{
+ int ret, index;
+ struct amdgpu_device *adev = smu->adev;
+ struct atom_get_smu_clock_info_parameters_v3_1 input = {0};
+ struct atom_get_smu_clock_info_output_parameters_v3_1 *output;
+
+ input.clk_id = SMU11_SYSPLL0_SOCCLK_ID;
+ input.command = GET_SMU_CLOCK_INFO_V3_1_GET_CLOCK_FREQ;
+ index = get_index_into_master_table(atom_master_list_of_command_functions_v2_1,
+ getsmuclockinfo);
+
+ ret = amdgpu_atom_execute_table(adev->mode_info.atom_context, index,
+ (uint32_t *)&input);
+ if (ret)
+ return -EINVAL;
+
+ output = (struct atom_get_smu_clock_info_output_parameters_v3_1 *)&input;
+ smu->smu_table.boot_values.socclk = le32_to_cpu(output->atom_smu_outputclkfreq.smu_clock_freq_hz) / 10000;
+
+ memset(&input, 0, sizeof(input));
+ input.clk_id = SMU11_SYSPLL0_DCEFCLK_ID;
+ input.command = GET_SMU_CLOCK_INFO_V3_1_GET_CLOCK_FREQ;
+ index = get_index_into_master_table(atom_master_list_of_command_functions_v2_1,
+ getsmuclockinfo);
+
+ ret = amdgpu_atom_execute_table(adev->mode_info.atom_context, index,
+ (uint32_t *)&input);
+ if (ret)
+ return -EINVAL;
+
+ output = (struct atom_get_smu_clock_info_output_parameters_v3_1 *)&input;
+ smu->smu_table.boot_values.dcefclk = le32_to_cpu(output->atom_smu_outputclkfreq.smu_clock_freq_hz) / 10000;
+
+ memset(&input, 0, sizeof(input));
+ input.clk_id = SMU11_SYSPLL0_ECLK_ID;
+ input.command = GET_SMU_CLOCK_INFO_V3_1_GET_CLOCK_FREQ;
+ index = get_index_into_master_table(atom_master_list_of_command_functions_v2_1,
+ getsmuclockinfo);
+
+ ret = amdgpu_atom_execute_table(adev->mode_info.atom_context, index,
+ (uint32_t *)&input);
+ if (ret)
+ return -EINVAL;
+
+ output = (struct atom_get_smu_clock_info_output_parameters_v3_1 *)&input;
+ smu->smu_table.boot_values.eclk = le32_to_cpu(output->atom_smu_outputclkfreq.smu_clock_freq_hz) / 10000;
+
+ memset(&input, 0, sizeof(input));
+ input.clk_id = SMU11_SYSPLL0_VCLK_ID;
+ input.command = GET_SMU_CLOCK_INFO_V3_1_GET_CLOCK_FREQ;
+ index = get_index_into_master_table(atom_master_list_of_command_functions_v2_1,
+ getsmuclockinfo);
+
+ ret = amdgpu_atom_execute_table(adev->mode_info.atom_context, index,
+ (uint32_t *)&input);
+ if (ret)
+ return -EINVAL;
+
+ output = (struct atom_get_smu_clock_info_output_parameters_v3_1 *)&input;
+ smu->smu_table.boot_values.vclk = le32_to_cpu(output->atom_smu_outputclkfreq.smu_clock_freq_hz) / 10000;
+
+ memset(&input, 0, sizeof(input));
+ input.clk_id = SMU11_SYSPLL0_DCLK_ID;
+ input.command = GET_SMU_CLOCK_INFO_V3_1_GET_CLOCK_FREQ;
+ index = get_index_into_master_table(atom_master_list_of_command_functions_v2_1,
+ getsmuclockinfo);
+
+ ret = amdgpu_atom_execute_table(adev->mode_info.atom_context, index,
+ (uint32_t *)&input);
+ if (ret)
+ return -EINVAL;
+
+ output = (struct atom_get_smu_clock_info_output_parameters_v3_1 *)&input;
+ smu->smu_table.boot_values.dclk = le32_to_cpu(output->atom_smu_outputclkfreq.smu_clock_freq_hz) / 10000;
+
+ return 0;
+}
+
+static int smu_v11_0_notify_memory_pool_location(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *memory_pool = &smu_table->memory_pool;
+ int ret = 0;
+ uint64_t address;
+ uint32_t address_low, address_high;
+
+ if (memory_pool->size == 0 || memory_pool->cpu_addr == NULL)
+ return ret;
+
+ address = (uintptr_t)memory_pool->cpu_addr;
+ address_high = (uint32_t)upper_32_bits(address);
+ address_low = (uint32_t)lower_32_bits(address);
+
+ ret = smu_send_smc_msg_with_param(smu,
+ SMU_MSG_SetSystemVirtualDramAddrHigh,
+ address_high);
+ if (ret)
+ return ret;
+ ret = smu_send_smc_msg_with_param(smu,
+ SMU_MSG_SetSystemVirtualDramAddrLow,
+ address_low);
+ if (ret)
+ return ret;
+
+ address = memory_pool->mc_address;
+ address_high = (uint32_t)upper_32_bits(address);
+ address_low = (uint32_t)lower_32_bits(address);
+
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_DramLogSetDramAddrHigh,
+ address_high);
+ if (ret)
+ return ret;
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_DramLogSetDramAddrLow,
+ address_low);
+ if (ret)
+ return ret;
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_DramLogSetDramSize,
+ (uint32_t)memory_pool->size);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static int smu_v11_0_check_pptable(struct smu_context *smu)
+{
+ int ret;
+
+ ret = smu_check_powerplay_table(smu);
+ return ret;
+}
+
+static int smu_v11_0_parse_pptable(struct smu_context *smu)
+{
+ int ret;
+
+ struct smu_table_context *table_context = &smu->smu_table;
+
+ if (table_context->driver_pptable)
+ return -EINVAL;
+
+ table_context->driver_pptable = kzalloc(sizeof(PPTable_t), GFP_KERNEL);
+
+ if (!table_context->driver_pptable)
+ return -ENOMEM;
+
+ ret = smu_store_powerplay_table(smu);
+ if (ret)
+ return -EINVAL;
+
+ ret = smu_append_powerplay_table(smu);
+
+ return ret;
+}
+
+static int smu_v11_0_populate_smc_pptable(struct smu_context *smu)
+{
+ int ret;
+
+ ret = smu_set_default_dpm_table(smu);
+
+ return ret;
+}
+
+static int smu_v11_0_write_pptable(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ int ret = 0;
+
+ ret = smu_update_table(smu, TABLE_PPTABLE, table_context->driver_pptable, true);
+
+ return ret;
+}
+
+static int smu_v11_0_write_watermarks_table(struct smu_context *smu)
+{
+ return smu_update_table(smu, TABLE_WATERMARKS,
+ smu->smu_table.tables[TABLE_WATERMARKS].cpu_addr, true);
+}
+
+static int smu_v11_0_set_deep_sleep_dcefclk(struct smu_context *smu, uint32_t clk)
+{
+ int ret;
+
+ ret = smu_send_smc_msg_with_param(smu,
+ SMU_MSG_SetMinDeepSleepDcefclk, clk);
+ if (ret)
+ pr_err("SMU11 attempt to set divider for DCEFCLK Failed!");
+
+ return ret;
+}
+
+static int smu_v11_0_set_min_dcef_deep_sleep(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+
+ if (!table_context)
+ return -EINVAL;
+
+ return smu_set_deep_sleep_dcefclk(smu,
+ table_context->boot_values.dcefclk / 100);
+}
+
+static int smu_v11_0_set_tool_table_location(struct smu_context *smu)
+{
+ int ret = 0;
+ struct smu_table *tool_table = &smu->smu_table.tables[TABLE_PMSTATUSLOG];
+
+ if (tool_table->mc_address) {
+ ret = smu_send_smc_msg_with_param(smu,
+ SMU_MSG_SetToolsDramAddrHigh,
+ upper_32_bits(tool_table->mc_address));
+ if (!ret)
+ ret = smu_send_smc_msg_with_param(smu,
+ SMU_MSG_SetToolsDramAddrLow,
+ lower_32_bits(tool_table->mc_address));
+ }
+
+ return ret;
+}
+
+static int smu_v11_0_init_display(struct smu_context *smu)
+{
+ int ret = 0;
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_NumOfDisplays, 0);
+ return ret;
+}
+
+static int smu_v11_0_update_feature_enable_state(struct smu_context *smu, uint32_t feature_id, bool enabled)
+{
+ uint32_t feature_low = 0, feature_high = 0;
+ int ret = 0;
+
+ if (feature_id >= 0 && feature_id < 31)
+ feature_low = (1 << feature_id);
+ else if (feature_id > 31 && feature_id < 63)
+ feature_high = (1 << feature_id);
+ else
+ return -EINVAL;
+
+ if (enabled) {
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_EnableSmuFeaturesLow,
+ feature_low);
+ if (ret)
+ return ret;
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_EnableSmuFeaturesHigh,
+ feature_high);
+ if (ret)
+ return ret;
+
+ } else {
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_DisableSmuFeaturesLow,
+ feature_low);
+ if (ret)
+ return ret;
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_DisableSmuFeaturesHigh,
+ feature_high);
+ if (ret)
+ return ret;
+
+ }
+
+ return ret;
+}
+
+static int smu_v11_0_set_allowed_mask(struct smu_context *smu)
+{
+ struct smu_feature *feature = &smu->smu_feature;
+ int ret = 0;
+ uint32_t feature_mask[2];
+
+ mutex_lock(&feature->mutex);
+ if (bitmap_empty(feature->allowed, SMU_FEATURE_MAX) || feature->feature_num < 64)
+ goto failed;
+
+ bitmap_copy((unsigned long *)feature_mask, feature->allowed, 64);
+
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_SetAllowedFeaturesMaskHigh,
+ feature_mask[1]);
+ if (ret)
+ goto failed;
+
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_SetAllowedFeaturesMaskLow,
+ feature_mask[0]);
+ if (ret)
+ goto failed;
+
+failed:
+ mutex_unlock(&feature->mutex);
+ return ret;
+}
+
+static int smu_v11_0_get_enabled_mask(struct smu_context *smu,
+ uint32_t *feature_mask, uint32_t num)
+{
+ uint32_t feature_mask_high = 0, feature_mask_low = 0;
+ int ret = 0;
+
+ if (!feature_mask || num < 2)
+ return -EINVAL;
+
+ ret = smu_send_smc_msg(smu, SMU_MSG_GetEnabledSmuFeaturesHigh);
+ if (ret)
+ return ret;
+ ret = smu_read_smc_arg(smu, &feature_mask_high);
+ if (ret)
+ return ret;
+
+ ret = smu_send_smc_msg(smu, SMU_MSG_GetEnabledSmuFeaturesLow);
+ if (ret)
+ return ret;
+ ret = smu_read_smc_arg(smu, &feature_mask_low);
+ if (ret)
+ return ret;
+
+ feature_mask[0] = feature_mask_low;
+ feature_mask[1] = feature_mask_high;
+
+ return ret;
+}
+
+static bool smu_v11_0_is_dpm_running(struct smu_context *smu)
+{
+ int ret = 0;
+ uint32_t feature_mask[2];
+ unsigned long feature_enabled;
+ ret = smu_v11_0_get_enabled_mask(smu, feature_mask, 2);
+ feature_enabled = (unsigned long)((uint64_t)feature_mask[0] |
+ ((uint64_t)feature_mask[1] << 32));
+ return !!(feature_enabled & SMC_DPM_FEATURE);
+}
+
+static int smu_v11_0_system_features_control(struct smu_context *smu,
+ bool en)
+{
+ struct smu_feature *feature = &smu->smu_feature;
+ uint32_t feature_mask[2];
+ int ret = 0;
+
+ ret = smu_send_smc_msg(smu, (en ? SMU_MSG_EnableAllSmuFeatures :
+ SMU_MSG_DisableAllSmuFeatures));
+ if (ret)
+ return ret;
+ ret = smu_feature_get_enabled_mask(smu, feature_mask, 2);
+ if (ret)
+ return ret;
+
+ bitmap_copy(feature->enabled, (unsigned long *)&feature_mask,
+ feature->feature_num);
+ bitmap_copy(feature->supported, (unsigned long *)&feature_mask,
+ feature->feature_num);
+
+ return ret;
+}
+
+static int smu_v11_0_notify_display_change(struct smu_context *smu)
+{
+ int ret = 0;
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_UCLK_BIT))
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_SetUclkFastSwitch, 1);
+
+ return ret;
+}
+
+static int
+smu_v11_0_get_max_sustainable_clock(struct smu_context *smu, uint32_t *clock,
+ PPCLK_e clock_select)
+{
+ int ret = 0;
+
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_GetDcModeMaxDpmFreq,
+ clock_select << 16);
+ if (ret) {
+ pr_err("[GetMaxSustainableClock] Failed to get max DC clock from SMC!");
+ return ret;
+ }
+
+ ret = smu_read_smc_arg(smu, clock);
+ if (ret)
+ return ret;
+
+ if (*clock != 0)
+ return 0;
+
+ /* if DC limit is zero, return AC limit */
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_GetMaxDpmFreq,
+ clock_select << 16);
+ if (ret) {
+ pr_err("[GetMaxSustainableClock] failed to get max AC clock from SMC!");
+ return ret;
+ }
+
+ ret = smu_read_smc_arg(smu, clock);
+
+ return ret;
+}
+
+static int smu_v11_0_init_max_sustainable_clocks(struct smu_context *smu)
+{
+ struct smu_11_0_max_sustainable_clocks *max_sustainable_clocks;
+ int ret = 0;
+
+ max_sustainable_clocks = kzalloc(sizeof(struct smu_11_0_max_sustainable_clocks),
+ GFP_KERNEL);
+ smu->smu_table.max_sustainable_clocks = (void *)max_sustainable_clocks;
+
+ max_sustainable_clocks->uclock = smu->smu_table.boot_values.uclk / 100;
+ max_sustainable_clocks->soc_clock = smu->smu_table.boot_values.socclk / 100;
+ max_sustainable_clocks->dcef_clock = smu->smu_table.boot_values.dcefclk / 100;
+ max_sustainable_clocks->display_clock = 0xFFFFFFFF;
+ max_sustainable_clocks->phy_clock = 0xFFFFFFFF;
+ max_sustainable_clocks->pixel_clock = 0xFFFFFFFF;
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_UCLK_BIT)) {
+ ret = smu_v11_0_get_max_sustainable_clock(smu,
+ &(max_sustainable_clocks->uclock),
+ PPCLK_UCLK);
+ if (ret) {
+ pr_err("[%s] failed to get max UCLK from SMC!",
+ __func__);
+ return ret;
+ }
+ }
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_SOCCLK_BIT)) {
+ ret = smu_v11_0_get_max_sustainable_clock(smu,
+ &(max_sustainable_clocks->soc_clock),
+ PPCLK_SOCCLK);
+ if (ret) {
+ pr_err("[%s] failed to get max SOCCLK from SMC!",
+ __func__);
+ return ret;
+ }
+ }
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_DCEFCLK_BIT)) {
+ ret = smu_v11_0_get_max_sustainable_clock(smu,
+ &(max_sustainable_clocks->dcef_clock),
+ PPCLK_DCEFCLK);
+ if (ret) {
+ pr_err("[%s] failed to get max DCEFCLK from SMC!",
+ __func__);
+ return ret;
+ }
+
+ ret = smu_v11_0_get_max_sustainable_clock(smu,
+ &(max_sustainable_clocks->display_clock),
+ PPCLK_DISPCLK);
+ if (ret) {
+ pr_err("[%s] failed to get max DISPCLK from SMC!",
+ __func__);
+ return ret;
+ }
+ ret = smu_v11_0_get_max_sustainable_clock(smu,
+ &(max_sustainable_clocks->phy_clock),
+ PPCLK_PHYCLK);
+ if (ret) {
+ pr_err("[%s] failed to get max PHYCLK from SMC!",
+ __func__);
+ return ret;
+ }
+ ret = smu_v11_0_get_max_sustainable_clock(smu,
+ &(max_sustainable_clocks->pixel_clock),
+ PPCLK_PIXCLK);
+ if (ret) {
+ pr_err("[%s] failed to get max PIXCLK from SMC!",
+ __func__);
+ return ret;
+ }
+ }
+
+ if (max_sustainable_clocks->soc_clock < max_sustainable_clocks->uclock)
+ max_sustainable_clocks->uclock = max_sustainable_clocks->soc_clock;
+
+ return 0;
+}
+
+static int smu_v11_0_get_power_limit(struct smu_context *smu,
+ uint32_t *limit,
+ bool get_default)
+{
+ int ret = 0;
+
+ if (get_default) {
+ mutex_lock(&smu->mutex);
+ *limit = smu->default_power_limit;
+ if (smu->od_enabled) {
+ *limit *= (100 + smu->smu_table.TDPODLimit);
+ *limit /= 100;
+ }
+ mutex_unlock(&smu->mutex);
+ } else {
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_GetPptLimit,
+ POWER_SOURCE_AC << 16);
+ if (ret) {
+ pr_err("[%s] get PPT limit failed!", __func__);
+ return ret;
+ }
+ smu_read_smc_arg(smu, limit);
+ smu->power_limit = *limit;
+ }
+
+ return ret;
+}
+
+static int smu_v11_0_set_power_limit(struct smu_context *smu, uint32_t n)
+{
+ uint32_t max_power_limit;
+ int ret = 0;
+
+ if (n == 0)
+ n = smu->default_power_limit;
+
+ max_power_limit = smu->default_power_limit;
+
+ if (smu->od_enabled) {
+ max_power_limit *= (100 + smu->smu_table.TDPODLimit);
+ max_power_limit /= 100;
+ }
+
+ if (smu_feature_is_enabled(smu, FEATURE_PPT_BIT))
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_SetPptLimit, n);
+ if (ret) {
+ pr_err("[%s] Set power limit Failed!", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int smu_v11_0_get_current_clk_freq(struct smu_context *smu, uint32_t clk_id, uint32_t *value)
+{
+ int ret = 0;
+ uint32_t freq;
+
+ if (clk_id >= PPCLK_COUNT || !value)
+ return -EINVAL;
+
+ ret = smu_send_smc_msg_with_param(smu,
+ SMU_MSG_GetDpmClockFreq, (clk_id << 16));
+ if (ret)
+ return ret;
+
+ ret = smu_read_smc_arg(smu, &freq);
+ if (ret)
+ return ret;
+
+ freq *= 100;
+ *value = freq;
+
+ return ret;
+}
+
+static int smu_v11_0_get_thermal_range(struct smu_context *smu,
+ struct PP_TemperatureRange *range)
+{
+ memcpy(range, &SMU7ThermalWithDelayPolicy[0], sizeof(struct PP_TemperatureRange));
+
+ range->max = smu->smu_table.software_shutdown_temp *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+ return 0;
+}
+
+static int smu_v11_0_set_thermal_range(struct smu_context *smu,
+ struct PP_TemperatureRange *range)
+{
+ struct amdgpu_device *adev = smu->adev;
+ int low = SMU11_THERMAL_MINIMUM_ALERT_TEMP *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ int high = SMU11_THERMAL_MAXIMUM_ALERT_TEMP *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ uint32_t val;
+
+ if (low < range->min)
+ low = range->min;
+ if (high > range->max)
+ high = range->max;
+
+ if (low > high)
+ return -EINVAL;
+
+ val = RREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_CTRL);
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, MAX_IH_CREDIT, 5);
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, THERM_IH_HW_ENA, 1);
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, DIG_THERM_INTH, (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, DIG_THERM_INTL, (low / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
+ val = val & (~THM_THERMAL_INT_CTRL__THERM_TRIGGER_MASK_MASK);
+
+ WREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_CTRL, val);
+
+ return 0;
+}
+
+static int smu_v11_0_enable_thermal_alert(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t val = 0;
+
+ val |= (1 << THM_THERMAL_INT_ENA__THERM_INTH_CLR__SHIFT);
+ val |= (1 << THM_THERMAL_INT_ENA__THERM_INTL_CLR__SHIFT);
+ val |= (1 << THM_THERMAL_INT_ENA__THERM_TRIGGER_CLR__SHIFT);
+
+ WREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_ENA, val);
+
+ return 0;
+}
+
+static int smu_v11_0_set_thermal_fan_table(struct smu_context *smu)
+{
+ int ret;
+ struct smu_table_context *table_context = &smu->smu_table;
+ PPTable_t *pptable = table_context->driver_pptable;
+
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_SetFanTemperatureTarget,
+ (uint32_t)pptable->FanTargetTemperature);
+
+ return ret;
+}
+
+static int smu_v11_0_start_thermal_control(struct smu_context *smu)
+{
+ int ret = 0;
+ struct PP_TemperatureRange range;
+ struct amdgpu_device *adev = smu->adev;
+
+ smu_v11_0_get_thermal_range(smu, &range);
+
+ if (smu->smu_table.thermal_controller_type) {
+ ret = smu_v11_0_set_thermal_range(smu, &range);
+ if (ret)
+ return ret;
+
+ ret = smu_v11_0_enable_thermal_alert(smu);
+ if (ret)
+ return ret;
+ ret = smu_v11_0_set_thermal_fan_table(smu);
+ if (ret)
+ return ret;
+ }
+
+ adev->pm.dpm.thermal.min_temp = range.min;
+ adev->pm.dpm.thermal.max_temp = range.max;
+
+ return ret;
+}
+
+static int smu_v11_0_get_current_activity_percent(struct smu_context *smu,
+ uint32_t *value)
+{
+ int ret = 0;
+ SmuMetrics_t metrics;
+
+ if (!value)
+ return -EINVAL;
+
+ ret = smu_update_table(smu, TABLE_SMU_METRICS, (void *)&metrics, false);
+ if (ret)
+ return ret;
+
+ *value = metrics.AverageGfxActivity;
+
+ return 0;
+}
+
+static int smu_v11_0_thermal_get_temperature(struct smu_context *smu, uint32_t *value)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t temp = 0;
+
+ if (!value)
+ return -EINVAL;
+
+ temp = RREG32_SOC15(THM, 0, mmCG_MULT_THERMAL_STATUS);
+ temp = (temp & CG_MULT_THERMAL_STATUS__CTF_TEMP_MASK) >>
+ CG_MULT_THERMAL_STATUS__CTF_TEMP__SHIFT;
+
+ temp = temp & 0x1ff;
+ temp *= SMU11_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+ *value = temp;
+
+ return 0;
+}
+
+static int smu_v11_0_get_gpu_power(struct smu_context *smu, uint32_t *value)
+{
+ int ret = 0;
+ SmuMetrics_t metrics;
+
+ if (!value)
+ return -EINVAL;
+
+ ret = smu_update_table(smu, TABLE_SMU_METRICS, (void *)&metrics, false);
+ if (ret)
+ return ret;
+
+ *value = metrics.CurrSocketPower << 8;
+
+ return 0;
+}
+
+static uint16_t convert_to_vddc(uint8_t vid)
+{
+ return (uint16_t) ((6200 - (vid * 25)) / SMU11_VOLTAGE_SCALE);
+}
+
+static int smu_v11_0_get_gfx_vdd(struct smu_context *smu, uint32_t *value)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t vdd = 0, val_vid = 0;
+
+ if (!value)
+ return -EINVAL;
+ val_vid = (RREG32_SOC15(SMUIO, 0, mmSMUSVI0_TEL_PLANE0) &
+ SMUSVI0_TEL_PLANE0__SVI0_PLANE0_VDDCOR_MASK) >>
+ SMUSVI0_TEL_PLANE0__SVI0_PLANE0_VDDCOR__SHIFT;
+
+ vdd = (uint32_t)convert_to_vddc((uint8_t)val_vid);
+
+ *value = vdd;
+
+ return 0;
+
+}
+
+static int smu_v11_0_read_sensor(struct smu_context *smu,
+ enum amd_pp_sensors sensor,
+ void *data, uint32_t *size)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ PPTable_t *pptable = table_context->driver_pptable;
+ int ret = 0;
+ switch (sensor) {
+ case AMDGPU_PP_SENSOR_GPU_LOAD:
+ ret = smu_v11_0_get_current_activity_percent(smu,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_MCLK:
+ ret = smu_get_current_clk_freq(smu, PPCLK_UCLK, (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_SCLK:
+ ret = smu_get_current_clk_freq(smu, PPCLK_GFXCLK, (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_TEMP:
+ ret = smu_v11_0_thermal_get_temperature(smu, (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_POWER:
+ ret = smu_v11_0_get_gpu_power(smu, (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_VDDGFX:
+ ret = smu_v11_0_get_gfx_vdd(smu, (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_UVD_POWER:
+ *(uint32_t *)data = smu_feature_is_enabled(smu, FEATURE_DPM_UVD_BIT) ? 1 : 0;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_VCE_POWER:
+ *(uint32_t *)data = smu_feature_is_enabled(smu, FEATURE_DPM_VCE_BIT) ? 1 : 0;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_MIN_FAN_RPM:
+ *(uint32_t *)data = 0;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_MAX_FAN_RPM:
+ *(uint32_t *)data = pptable->FanMaximumRpm;
+ *size = 4;
+ break;
+ default:
+ ret = smu_common_read_sensor(smu, sensor, data, size);
+ break;
+ }
+
+ if (ret)
+ *size = 0;
+
+ return ret;
+}
+
+static int
+smu_v11_0_display_clock_voltage_request(struct smu_context *smu,
+ struct pp_display_clock_request
+ *clock_req)
+{
+ enum amd_pp_clock_type clk_type = clock_req->clock_type;
+ int ret = 0;
+ PPCLK_e clk_select = 0;
+ uint32_t clk_freq = clock_req->clock_freq_in_khz / 1000;
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_DCEFCLK_BIT)) {
+ switch (clk_type) {
+ case amd_pp_dcef_clock:
+ clk_select = PPCLK_DCEFCLK;
+ break;
+ case amd_pp_disp_clock:
+ clk_select = PPCLK_DISPCLK;
+ break;
+ case amd_pp_pixel_clock:
+ clk_select = PPCLK_PIXCLK;
+ break;
+ case amd_pp_phy_clock:
+ clk_select = PPCLK_PHYCLK;
+ break;
+ default:
+ pr_info("[%s] Invalid Clock Type!", __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret)
+ goto failed;
+
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_SetHardMinByFreq,
+ (clk_select << 16) | clk_freq);
+ }
+
+failed:
+ return ret;
+}
+
+static int smu_v11_0_set_watermarks_table(struct smu_context *smu,
+ Watermarks_t *table, struct
+ dm_pp_wm_sets_with_clock_ranges_soc15
+ *clock_ranges)
+{
+ int i;
+
+ if (!table || !clock_ranges)
+ return -EINVAL;
+
+ if (clock_ranges->num_wm_dmif_sets > 4 ||
+ clock_ranges->num_wm_mcif_sets > 4)
+ return -EINVAL;
+
+ for (i = 0; i < clock_ranges->num_wm_dmif_sets; i++) {
+ table->WatermarkRow[1][i].MinClock =
+ cpu_to_le16((uint16_t)
+ (clock_ranges->wm_dmif_clocks_ranges[i].wm_min_dcfclk_clk_in_khz /
+ 1000));
+ table->WatermarkRow[1][i].MaxClock =
+ cpu_to_le16((uint16_t)
+ (clock_ranges->wm_dmif_clocks_ranges[i].wm_max_dcfclk_clk_in_khz /
+ 1000));
+ table->WatermarkRow[1][i].MinUclk =
+ cpu_to_le16((uint16_t)
+ (clock_ranges->wm_dmif_clocks_ranges[i].wm_min_mem_clk_in_khz /
+ 1000));
+ table->WatermarkRow[1][i].MaxUclk =
+ cpu_to_le16((uint16_t)
+ (clock_ranges->wm_dmif_clocks_ranges[i].wm_max_mem_clk_in_khz /
+ 1000));
+ table->WatermarkRow[1][i].WmSetting = (uint8_t)
+ clock_ranges->wm_dmif_clocks_ranges[i].wm_set_id;
+ }
+
+ for (i = 0; i < clock_ranges->num_wm_mcif_sets; i++) {
+ table->WatermarkRow[0][i].MinClock =
+ cpu_to_le16((uint16_t)
+ (clock_ranges->wm_mcif_clocks_ranges[i].wm_min_socclk_clk_in_khz /
+ 1000));
+ table->WatermarkRow[0][i].MaxClock =
+ cpu_to_le16((uint16_t)
+ (clock_ranges->wm_mcif_clocks_ranges[i].wm_max_socclk_clk_in_khz /
+ 1000));
+ table->WatermarkRow[0][i].MinUclk =
+ cpu_to_le16((uint16_t)
+ (clock_ranges->wm_mcif_clocks_ranges[i].wm_min_mem_clk_in_khz /
+ 1000));
+ table->WatermarkRow[0][i].MaxUclk =
+ cpu_to_le16((uint16_t)
+ (clock_ranges->wm_mcif_clocks_ranges[i].wm_max_mem_clk_in_khz /
+ 1000));
+ table->WatermarkRow[0][i].WmSetting = (uint8_t)
+ clock_ranges->wm_mcif_clocks_ranges[i].wm_set_id;
+ }
+
+ return 0;
+}
+
+static int
+smu_v11_0_set_watermarks_for_clock_ranges(struct smu_context *smu, struct
+ dm_pp_wm_sets_with_clock_ranges_soc15
+ *clock_ranges)
+{
+ int ret = 0;
+ struct smu_table *watermarks = &smu->smu_table.tables[TABLE_WATERMARKS];
+ Watermarks_t *table = watermarks->cpu_addr;
+
+ if (!smu->disable_watermark &&
+ smu_feature_is_enabled(smu, FEATURE_DPM_DCEFCLK_BIT) &&
+ smu_feature_is_enabled(smu, FEATURE_DPM_SOCCLK_BIT)) {
+ smu_v11_0_set_watermarks_table(smu, table, clock_ranges);
+ smu->watermarks_bitmap |= WATERMARKS_EXIST;
+ smu->watermarks_bitmap &= ~WATERMARKS_LOADED;
+ }
+
+ return ret;
+}
+
+static int smu_v11_0_get_clock_ranges(struct smu_context *smu,
+ uint32_t *clock,
+ PPCLK_e clock_select,
+ bool max)
+{
+ int ret;
+ *clock = 0;
+ if (max) {
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_GetMaxDpmFreq,
+ (clock_select << 16));
+ if (ret) {
+ pr_err("[GetClockRanges] Failed to get max clock from SMC!\n");
+ return ret;
+ }
+ smu_read_smc_arg(smu, clock);
+ } else {
+ ret = smu_send_smc_msg_with_param(smu, SMU_MSG_GetMinDpmFreq,
+ (clock_select << 16));
+ if (ret) {
+ pr_err("[GetClockRanges] Failed to get min clock from SMC!\n");
+ return ret;
+ }
+ smu_read_smc_arg(smu, clock);
+ }
+
+ return 0;
+}
+
+static uint32_t smu_v11_0_dpm_get_sclk(struct smu_context *smu, bool low)
+{
+ uint32_t gfx_clk;
+ int ret;
+
+ if (!smu_feature_is_enabled(smu, FEATURE_DPM_GFXCLK_BIT)) {
+ pr_err("[GetSclks]: gfxclk dpm not enabled!\n");
+ return -EPERM;
+ }
+
+ if (low) {
+ ret = smu_v11_0_get_clock_ranges(smu, &gfx_clk, PPCLK_GFXCLK, false);
+ if (ret) {
+ pr_err("[GetSclks]: fail to get min PPCLK_GFXCLK\n");
+ return ret;
+ }
+ } else {
+ ret = smu_v11_0_get_clock_ranges(smu, &gfx_clk, PPCLK_GFXCLK, true);
+ if (ret) {
+ pr_err("[GetSclks]: fail to get max PPCLK_GFXCLK\n");
+ return ret;
+ }
+ }
+
+ return (gfx_clk * 100);
+}
+
+static uint32_t smu_v11_0_dpm_get_mclk(struct smu_context *smu, bool low)
+{
+ uint32_t mem_clk;
+ int ret;
+
+ if (!smu_feature_is_enabled(smu, FEATURE_DPM_UCLK_BIT)) {
+ pr_err("[GetMclks]: memclk dpm not enabled!\n");
+ return -EPERM;
+ }
+
+ if (low) {
+ ret = smu_v11_0_get_clock_ranges(smu, &mem_clk, PPCLK_UCLK, false);
+ if (ret) {
+ pr_err("[GetMclks]: fail to get min PPCLK_UCLK\n");
+ return ret;
+ }
+ } else {
+ ret = smu_v11_0_get_clock_ranges(smu, &mem_clk, PPCLK_GFXCLK, true);
+ if (ret) {
+ pr_err("[GetMclks]: fail to get max PPCLK_UCLK\n");
+ return ret;
+ }
+ }
+
+ return (mem_clk * 100);
+}
+
+static int smu_v11_0_set_od8_default_settings(struct smu_context *smu,
+ bool initialize)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ int ret;
+
+ if (initialize) {
+ if (table_context->overdrive_table)
+ return -EINVAL;
+
+ table_context->overdrive_table = kzalloc(sizeof(OverDriveTable_t), GFP_KERNEL);
+
+ if (!table_context->overdrive_table)
+ return -ENOMEM;
+
+ ret = smu_update_table(smu, TABLE_OVERDRIVE, table_context->overdrive_table, false);
+ if (ret) {
+ pr_err("Failed to export over drive table!\n");
+ return ret;
+ }
+
+ smu_set_default_od8_settings(smu);
+ }
+
+ ret = smu_update_table(smu, TABLE_OVERDRIVE, table_context->overdrive_table, true);
+ if (ret) {
+ pr_err("Failed to import over drive table!\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int smu_v11_0_conv_power_profile_to_pplib_workload(int power_profile)
+{
+ int pplib_workload = 0;
+
+ switch (power_profile) {
+ case PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT:
+ pplib_workload = WORKLOAD_DEFAULT_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_FULLSCREEN3D:
+ pplib_workload = WORKLOAD_PPLIB_FULL_SCREEN_3D_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_POWERSAVING:
+ pplib_workload = WORKLOAD_PPLIB_POWER_SAVING_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_VIDEO:
+ pplib_workload = WORKLOAD_PPLIB_VIDEO_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_VR:
+ pplib_workload = WORKLOAD_PPLIB_VR_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_COMPUTE:
+ pplib_workload = WORKLOAD_PPLIB_COMPUTE_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_CUSTOM:
+ pplib_workload = WORKLOAD_PPLIB_CUSTOM_BIT;
+ break;
+ }
+
+ return pplib_workload;
+}
+
+static int smu_v11_0_get_power_profile_mode(struct smu_context *smu, char *buf)
+{
+ DpmActivityMonitorCoeffInt_t activity_monitor;
+ uint32_t i, size = 0;
+ uint16_t workload_type = 0;
+ static const char *profile_name[] = {
+ "BOOTUP_DEFAULT",
+ "3D_FULL_SCREEN",
+ "POWER_SAVING",
+ "VIDEO",
+ "VR",
+ "COMPUTE",
+ "CUSTOM"};
+ static const char *title[] = {
+ "PROFILE_INDEX(NAME)",
+ "CLOCK_TYPE(NAME)",
+ "FPS",
+ "UseRlcBusy",
+ "MinActiveFreqType",
+ "MinActiveFreq",
+ "BoosterFreqType",
+ "BoosterFreq",
+ "PD_Data_limit_c",
+ "PD_Data_error_coeff",
+ "PD_Data_error_rate_coeff"};
+ int result = 0;
+
+ if (!buf)
+ return -EINVAL;
+
+ size += sprintf(buf + size, "%16s %s %s %s %s %s %s %s %s %s %s\n",
+ title[0], title[1], title[2], title[3], title[4], title[5],
+ title[6], title[7], title[8], title[9], title[10]);
+
+ for (i = 0; i <= PP_SMC_POWER_PROFILE_CUSTOM; i++) {
+ /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
+ workload_type = smu_v11_0_conv_power_profile_to_pplib_workload(i);
+ result = smu_update_table_with_arg(smu, TABLE_ACTIVITY_MONITOR_COEFF,
+ workload_type, &activity_monitor, false);
+ if (result) {
+ pr_err("[%s] Failed to get activity monitor!", __func__);
+ return result;
+ }
+
+ size += sprintf(buf + size, "%2d %14s%s:\n",
+ i, profile_name[i], (i == smu->power_profile_mode) ? "*" : " ");
+
+ size += sprintf(buf + size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d %7d\n",
+ " ",
+ 0,
+ "GFXCLK",
+ activity_monitor.Gfx_FPS,
+ activity_monitor.Gfx_UseRlcBusy,
+ activity_monitor.Gfx_MinActiveFreqType,
+ activity_monitor.Gfx_MinActiveFreq,
+ activity_monitor.Gfx_BoosterFreqType,
+ activity_monitor.Gfx_BoosterFreq,
+ activity_monitor.Gfx_PD_Data_limit_c,
+ activity_monitor.Gfx_PD_Data_error_coeff,
+ activity_monitor.Gfx_PD_Data_error_rate_coeff);
+
+ size += sprintf(buf + size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d %7d\n",
+ " ",
+ 1,
+ "SOCCLK",
+ activity_monitor.Soc_FPS,
+ activity_monitor.Soc_UseRlcBusy,
+ activity_monitor.Soc_MinActiveFreqType,
+ activity_monitor.Soc_MinActiveFreq,
+ activity_monitor.Soc_BoosterFreqType,
+ activity_monitor.Soc_BoosterFreq,
+ activity_monitor.Soc_PD_Data_limit_c,
+ activity_monitor.Soc_PD_Data_error_coeff,
+ activity_monitor.Soc_PD_Data_error_rate_coeff);
+
+ size += sprintf(buf + size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d %7d\n",
+ " ",
+ 2,
+ "UCLK",
+ activity_monitor.Mem_FPS,
+ activity_monitor.Mem_UseRlcBusy,
+ activity_monitor.Mem_MinActiveFreqType,
+ activity_monitor.Mem_MinActiveFreq,
+ activity_monitor.Mem_BoosterFreqType,
+ activity_monitor.Mem_BoosterFreq,
+ activity_monitor.Mem_PD_Data_limit_c,
+ activity_monitor.Mem_PD_Data_error_coeff,
+ activity_monitor.Mem_PD_Data_error_rate_coeff);
+
+ size += sprintf(buf + size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d %7d\n",
+ " ",
+ 3,
+ "FCLK",
+ activity_monitor.Fclk_FPS,
+ activity_monitor.Fclk_UseRlcBusy,
+ activity_monitor.Fclk_MinActiveFreqType,
+ activity_monitor.Fclk_MinActiveFreq,
+ activity_monitor.Fclk_BoosterFreqType,
+ activity_monitor.Fclk_BoosterFreq,
+ activity_monitor.Fclk_PD_Data_limit_c,
+ activity_monitor.Fclk_PD_Data_error_coeff,
+ activity_monitor.Fclk_PD_Data_error_rate_coeff);
+ }
+
+ return size;
+}
+
+static int smu_v11_0_set_power_profile_mode(struct smu_context *smu, long *input, uint32_t size)
+{
+ DpmActivityMonitorCoeffInt_t activity_monitor;
+ int workload_type = 0, ret = 0;
+
+ smu->power_profile_mode = input[size];
+
+ if (smu->power_profile_mode > PP_SMC_POWER_PROFILE_CUSTOM) {
+ pr_err("Invalid power profile mode %d\n", smu->power_profile_mode);
+ return -EINVAL;
+ }
+
+ if (smu->power_profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) {
+ ret = smu_update_table_with_arg(smu, TABLE_ACTIVITY_MONITOR_COEFF,
+ WORKLOAD_PPLIB_CUSTOM_BIT, &activity_monitor, false);
+ if (ret) {
+ pr_err("[%s] Failed to get activity monitor!", __func__);
+ return ret;
+ }
+
+ switch (input[0]) {
+ case 0: /* Gfxclk */
+ activity_monitor.Gfx_FPS = input[1];
+ activity_monitor.Gfx_UseRlcBusy = input[2];
+ activity_monitor.Gfx_MinActiveFreqType = input[3];
+ activity_monitor.Gfx_MinActiveFreq = input[4];
+ activity_monitor.Gfx_BoosterFreqType = input[5];
+ activity_monitor.Gfx_BoosterFreq = input[6];
+ activity_monitor.Gfx_PD_Data_limit_c = input[7];
+ activity_monitor.Gfx_PD_Data_error_coeff = input[8];
+ activity_monitor.Gfx_PD_Data_error_rate_coeff = input[9];
+ break;
+ case 1: /* Socclk */
+ activity_monitor.Soc_FPS = input[1];
+ activity_monitor.Soc_UseRlcBusy = input[2];
+ activity_monitor.Soc_MinActiveFreqType = input[3];
+ activity_monitor.Soc_MinActiveFreq = input[4];
+ activity_monitor.Soc_BoosterFreqType = input[5];
+ activity_monitor.Soc_BoosterFreq = input[6];
+ activity_monitor.Soc_PD_Data_limit_c = input[7];
+ activity_monitor.Soc_PD_Data_error_coeff = input[8];
+ activity_monitor.Soc_PD_Data_error_rate_coeff = input[9];
+ break;
+ case 2: /* Uclk */
+ activity_monitor.Mem_FPS = input[1];
+ activity_monitor.Mem_UseRlcBusy = input[2];
+ activity_monitor.Mem_MinActiveFreqType = input[3];
+ activity_monitor.Mem_MinActiveFreq = input[4];
+ activity_monitor.Mem_BoosterFreqType = input[5];
+ activity_monitor.Mem_BoosterFreq = input[6];
+ activity_monitor.Mem_PD_Data_limit_c = input[7];
+ activity_monitor.Mem_PD_Data_error_coeff = input[8];
+ activity_monitor.Mem_PD_Data_error_rate_coeff = input[9];
+ break;
+ case 3: /* Fclk */
+ activity_monitor.Fclk_FPS = input[1];
+ activity_monitor.Fclk_UseRlcBusy = input[2];
+ activity_monitor.Fclk_MinActiveFreqType = input[3];
+ activity_monitor.Fclk_MinActiveFreq = input[4];
+ activity_monitor.Fclk_BoosterFreqType = input[5];
+ activity_monitor.Fclk_BoosterFreq = input[6];
+ activity_monitor.Fclk_PD_Data_limit_c = input[7];
+ activity_monitor.Fclk_PD_Data_error_coeff = input[8];
+ activity_monitor.Fclk_PD_Data_error_rate_coeff = input[9];
+ break;
+ }
+
+ ret = smu_update_table_with_arg(smu, TABLE_ACTIVITY_MONITOR_COEFF,
+ WORKLOAD_PPLIB_COMPUTE_BIT, &activity_monitor, true);
+ if (ret) {
+ pr_err("[%s] Failed to set activity monitor!", __func__);
+ return ret;
+ }
+ }
+
+ /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
+ workload_type =
+ smu_v11_0_conv_power_profile_to_pplib_workload(smu->power_profile_mode);
+ smu_send_smc_msg_with_param(smu, SMU_MSG_SetWorkloadMask,
+ 1 << workload_type);
+
+ return ret;
+}
+
+static int smu_v11_0_update_od8_settings(struct smu_context *smu,
+ uint32_t index,
+ uint32_t value)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ int ret;
+
+ ret = smu_update_table(smu, TABLE_OVERDRIVE,
+ table_context->overdrive_table, false);
+ if (ret) {
+ pr_err("Failed to export over drive table!\n");
+ return ret;
+ }
+
+ smu_update_specified_od8_value(smu, index, value);
+
+ ret = smu_update_table(smu, TABLE_OVERDRIVE,
+ table_context->overdrive_table, true);
+ if (ret) {
+ pr_err("Failed to import over drive table!\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int smu_v11_0_dpm_set_uvd_enable(struct smu_context *smu, bool enable)
+{
+ if (!smu_feature_is_supported(smu, FEATURE_DPM_VCE_BIT))
+ return 0;
+
+ if (enable == smu_feature_is_enabled(smu, FEATURE_DPM_VCE_BIT))
+ return 0;
+
+ return smu_feature_set_enabled(smu, FEATURE_DPM_VCE_BIT, enable);
+}
+
+static int smu_v11_0_dpm_set_vce_enable(struct smu_context *smu, bool enable)
+{
+ if (!smu_feature_is_supported(smu, FEATURE_DPM_UVD_BIT))
+ return 0;
+
+ if (enable == smu_feature_is_enabled(smu, FEATURE_DPM_UVD_BIT))
+ return 0;
+
+ return smu_feature_set_enabled(smu, FEATURE_DPM_UVD_BIT, enable);
+}
+
+static int smu_v11_0_get_current_rpm(struct smu_context *smu,
+ uint32_t *current_rpm)
+{
+ int ret;
+
+ ret = smu_send_smc_msg(smu, SMU_MSG_GetCurrentRpm);
+
+ if (ret) {
+ pr_err("Attempt to get current RPM from SMC Failed!\n");
+ return ret;
+ }
+
+ smu_read_smc_arg(smu, current_rpm);
+
+ return 0;
+}
+
+static uint32_t
+smu_v11_0_get_fan_control_mode(struct smu_context *smu)
+{
+ if (!smu_feature_is_enabled(smu, FEATURE_FAN_CONTROL_BIT))
+ return AMD_FAN_CTRL_MANUAL;
+ else
+ return AMD_FAN_CTRL_AUTO;
+}
+
+static int
+smu_v11_0_get_fan_speed_percent(struct smu_context *smu,
+ uint32_t *speed)
+{
+ int ret = 0;
+ uint32_t percent = 0;
+ uint32_t current_rpm;
+ PPTable_t *pptable = smu->smu_table.driver_pptable;
+
+ ret = smu_v11_0_get_current_rpm(smu, &current_rpm);
+ percent = current_rpm * 100 / pptable->FanMaximumRpm;
+ *speed = percent > 100 ? 100 : percent;
+
+ return ret;
+}
+
+static int
+smu_v11_0_smc_fan_control(struct smu_context *smu, bool start)
+{
+ int ret = 0;
+
+ if (smu_feature_is_supported(smu, FEATURE_FAN_CONTROL_BIT))
+ return 0;
+
+ ret = smu_feature_set_enabled(smu, FEATURE_FAN_CONTROL_BIT, start);
+ if (ret)
+ pr_err("[%s]%s smc FAN CONTROL feature failed!",
+ __func__, (start ? "Start" : "Stop"));
+
+ return ret;
+}
+
+static int
+smu_v11_0_set_fan_static_mode(struct smu_context *smu, uint32_t mode)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ WREG32_SOC15(THM, 0, mmCG_FDO_CTRL2,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL2),
+ CG_FDO_CTRL2, TMIN, 0));
+ WREG32_SOC15(THM, 0, mmCG_FDO_CTRL2,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL2),
+ CG_FDO_CTRL2, FDO_PWM_MODE, mode));
+
+ return 0;
+}
+
+static int
+smu_v11_0_set_fan_speed_percent(struct smu_context *smu, uint32_t speed)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t duty100;
+ uint32_t duty;
+ uint64_t tmp64;
+ bool stop = 0;
+
+ if (speed > 100)
+ speed = 100;
+
+ if (smu_v11_0_smc_fan_control(smu, stop))
+ return -EINVAL;
+ duty100 = REG_GET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL1),
+ CG_FDO_CTRL1, FMAX_DUTY100);
+ if (!duty100)
+ return -EINVAL;
+
+ tmp64 = (uint64_t)speed * duty100;
+ do_div(tmp64, 100);
+ duty = (uint32_t)tmp64;
+
+ WREG32_SOC15(THM, 0, mmCG_FDO_CTRL0,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL0),
+ CG_FDO_CTRL0, FDO_STATIC_DUTY, duty));
+
+ return smu_v11_0_set_fan_static_mode(smu, FDO_PWM_MODE_STATIC);
+}
+
+static int
+smu_v11_0_set_fan_control_mode(struct smu_context *smu,
+ uint32_t mode)
+{
+ int ret = 0;
+ bool start = 1;
+ bool stop = 0;
+
+ switch (mode) {
+ case AMD_FAN_CTRL_NONE:
+ ret = smu_v11_0_set_fan_speed_percent(smu, 100);
+ break;
+ case AMD_FAN_CTRL_MANUAL:
+ ret = smu_v11_0_smc_fan_control(smu, stop);
+ break;
+ case AMD_FAN_CTRL_AUTO:
+ ret = smu_v11_0_smc_fan_control(smu, start);
+ break;
+ default:
+ break;
+ }
+
+ if (ret) {
+ pr_err("[%s]Set fan control mode failed!", __func__);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int smu_v11_0_set_fan_speed_rpm(struct smu_context *smu,
+ uint32_t speed)
+{
+ struct amdgpu_device *adev = smu->adev;
+ int ret;
+ uint32_t tach_period, crystal_clock_freq;
+ bool stop = 0;
+
+ if (!speed)
+ return -EINVAL;
+
+ mutex_lock(&(smu->mutex));
+ ret = smu_v11_0_smc_fan_control(smu, stop);
+ if (ret)
+ goto set_fan_speed_rpm_failed;
+
+ crystal_clock_freq = amdgpu_asic_get_xclk(adev);
+ tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed);
+ WREG32_SOC15(THM, 0, mmCG_TACH_CTRL,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_TACH_CTRL),
+ CG_TACH_CTRL, TARGET_PERIOD,
+ tach_period));
+
+ ret = smu_v11_0_set_fan_static_mode(smu, FDO_PWM_MODE_STATIC_RPM);
+
+set_fan_speed_rpm_failed:
+ mutex_unlock(&(smu->mutex));
+ return ret;
+}
+
+static int smu_v11_0_set_xgmi_pstate(struct smu_context *smu,
+ uint32_t pstate)
+{
+ int ret = 0;
+ mutex_lock(&(smu->mutex));
+ ret = smu_send_smc_msg_with_param(smu,
+ SMU_MSG_SetXgmiMode,
+ pstate ? XGMI_STATE_D0 : XGMI_STATE_D3);
+ mutex_unlock(&(smu->mutex));
+ return ret;
+}
+
+static const struct smu_funcs smu_v11_0_funcs = {
+ .init_microcode = smu_v11_0_init_microcode,
+ .load_microcode = smu_v11_0_load_microcode,
+ .check_fw_status = smu_v11_0_check_fw_status,
+ .check_fw_version = smu_v11_0_check_fw_version,
+ .send_smc_msg = smu_v11_0_send_msg,
+ .send_smc_msg_with_param = smu_v11_0_send_msg_with_param,
+ .read_smc_arg = smu_v11_0_read_arg,
+ .read_pptable_from_vbios = smu_v11_0_read_pptable_from_vbios,
+ .init_smc_tables = smu_v11_0_init_smc_tables,
+ .fini_smc_tables = smu_v11_0_fini_smc_tables,
+ .init_power = smu_v11_0_init_power,
+ .fini_power = smu_v11_0_fini_power,
+ .get_vbios_bootup_values = smu_v11_0_get_vbios_bootup_values,
+ .get_clk_info_from_vbios = smu_v11_0_get_clk_info_from_vbios,
+ .notify_memory_pool_location = smu_v11_0_notify_memory_pool_location,
+ .check_pptable = smu_v11_0_check_pptable,
+ .parse_pptable = smu_v11_0_parse_pptable,
+ .populate_smc_pptable = smu_v11_0_populate_smc_pptable,
+ .write_pptable = smu_v11_0_write_pptable,
+ .write_watermarks_table = smu_v11_0_write_watermarks_table,
+ .set_min_dcef_deep_sleep = smu_v11_0_set_min_dcef_deep_sleep,
+ .set_tool_table_location = smu_v11_0_set_tool_table_location,
+ .init_display = smu_v11_0_init_display,
+ .set_allowed_mask = smu_v11_0_set_allowed_mask,
+ .get_enabled_mask = smu_v11_0_get_enabled_mask,
+ .is_dpm_running = smu_v11_0_is_dpm_running,
+ .system_features_control = smu_v11_0_system_features_control,
+ .update_feature_enable_state = smu_v11_0_update_feature_enable_state,
+ .notify_display_change = smu_v11_0_notify_display_change,
+ .get_power_limit = smu_v11_0_get_power_limit,
+ .set_power_limit = smu_v11_0_set_power_limit,
+ .get_current_clk_freq = smu_v11_0_get_current_clk_freq,
+ .init_max_sustainable_clocks = smu_v11_0_init_max_sustainable_clocks,
+ .start_thermal_control = smu_v11_0_start_thermal_control,
+ .read_sensor = smu_v11_0_read_sensor,
+ .set_deep_sleep_dcefclk = smu_v11_0_set_deep_sleep_dcefclk,
+ .display_clock_voltage_request = smu_v11_0_display_clock_voltage_request,
+ .set_watermarks_for_clock_ranges = smu_v11_0_set_watermarks_for_clock_ranges,
+ .get_sclk = smu_v11_0_dpm_get_sclk,
+ .get_mclk = smu_v11_0_dpm_get_mclk,
+ .set_od8_default_settings = smu_v11_0_set_od8_default_settings,
+ .conv_power_profile_to_pplib_workload = smu_v11_0_conv_power_profile_to_pplib_workload,
+ .get_power_profile_mode = smu_v11_0_get_power_profile_mode,
+ .set_power_profile_mode = smu_v11_0_set_power_profile_mode,
+ .update_od8_settings = smu_v11_0_update_od8_settings,
+ .dpm_set_uvd_enable = smu_v11_0_dpm_set_uvd_enable,
+ .dpm_set_vce_enable = smu_v11_0_dpm_set_vce_enable,
+ .get_current_rpm = smu_v11_0_get_current_rpm,
+ .get_fan_control_mode = smu_v11_0_get_fan_control_mode,
+ .set_fan_control_mode = smu_v11_0_set_fan_control_mode,
+ .get_fan_speed_percent = smu_v11_0_get_fan_speed_percent,
+ .set_fan_speed_percent = smu_v11_0_set_fan_speed_percent,
+ .set_fan_speed_rpm = smu_v11_0_set_fan_speed_rpm,
+ .set_xgmi_pstate = smu_v11_0_set_xgmi_pstate,
+};
+
+void smu_v11_0_set_smu_funcs(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ smu->funcs = &smu_v11_0_funcs;
+ switch (adev->asic_type) {
+ case CHIP_VEGA20:
+ vega20_set_ppt_funcs(smu);
+ break;
+ default:
+ pr_warn("Unknown asic for smu11\n");
+ }
+}
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/smu10_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/smu10_smumgr.c
index d111dd4e03d7..6d11076a79ba 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/smu10_smumgr.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/smu10_smumgr.c
@@ -212,6 +212,10 @@ static int smu10_start_smu(struct pp_hwmgr *hwmgr)
hwmgr->smu_version = smu10_read_arg_from_smc(hwmgr);
adev->pm.fw_version = hwmgr->smu_version >> 8;
+ if (adev->rev_id < 0x8 && adev->pdev->device != 0x15d8 &&
+ adev->pm.fw_version < 0x1e45)
+ adev->pm.pp_feature &= ~PP_GFXOFF_MASK;
+
if (smu10_verify_smc_interface(hwmgr))
return -EINVAL;
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c
index ba00744c3413..f301a73f6df1 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c
@@ -367,6 +367,26 @@ static int vega20_set_tools_address(struct pp_hwmgr *hwmgr)
return ret;
}
+int vega20_set_pptable_driver_address(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_smumgr *priv =
+ (struct vega20_smumgr *)(hwmgr->smu_backend);
+ int ret = 0;
+
+ PP_ASSERT_WITH_CODE((ret = vega20_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrHigh,
+ upper_32_bits(priv->smu_tables.entry[TABLE_PPTABLE].mc_addr))) == 0,
+ "[SetPPtabeDriverAddress] Attempt to Set Dram Addr High Failed!",
+ return ret);
+ PP_ASSERT_WITH_CODE((ret = vega20_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrLow,
+ lower_32_bits(priv->smu_tables.entry[TABLE_PPTABLE].mc_addr))) == 0,
+ "[SetPPtabeDriverAddress] Attempt to Set Dram Addr Low Failed!",
+ return ret);
+
+ return ret;
+}
+
static int vega20_smu_init(struct pp_hwmgr *hwmgr)
{
struct vega20_smumgr *priv;
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.h b/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.h
index 77349c3f0162..ec953ab13e87 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.h
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.h
@@ -55,6 +55,7 @@ int vega20_set_activity_monitor_coeff(struct pp_hwmgr *hwmgr,
uint8_t *table, uint16_t workload_type);
int vega20_get_activity_monitor_coeff(struct pp_hwmgr *hwmgr,
uint8_t *table, uint16_t workload_type);
+int vega20_set_pptable_driver_address(struct pp_hwmgr *hwmgr);
#endif
diff --git a/drivers/gpu/drm/amd/powerplay/vega20_ppt.c b/drivers/gpu/drm/amd/powerplay/vega20_ppt.c
new file mode 100644
index 000000000000..8fafcbdb1dfd
--- /dev/null
+++ b/drivers/gpu/drm/amd/powerplay/vega20_ppt.c
@@ -0,0 +1,2413 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "pp_debug.h"
+#include <linux/firmware.h>
+#include "amdgpu.h"
+#include "amdgpu_smu.h"
+#include "atomfirmware.h"
+#include "amdgpu_atomfirmware.h"
+#include "smu_v11_0.h"
+#include "smu11_driver_if.h"
+#include "soc15_common.h"
+#include "atom.h"
+#include "power_state.h"
+#include "vega20_ppt.h"
+#include "vega20_pptable.h"
+#include "vega20_ppsmc.h"
+#include "nbio/nbio_7_4_sh_mask.h"
+
+#define smnPCIE_LC_SPEED_CNTL 0x11140290
+#define smnPCIE_LC_LINK_WIDTH_CNTL 0x11140288
+
+#define MSG_MAP(msg) \
+ [SMU_MSG_##msg] = PPSMC_MSG_##msg
+
+static int vega20_message_map[SMU_MSG_MAX_COUNT] = {
+ MSG_MAP(TestMessage),
+ MSG_MAP(GetSmuVersion),
+ MSG_MAP(GetDriverIfVersion),
+ MSG_MAP(SetAllowedFeaturesMaskLow),
+ MSG_MAP(SetAllowedFeaturesMaskHigh),
+ MSG_MAP(EnableAllSmuFeatures),
+ MSG_MAP(DisableAllSmuFeatures),
+ MSG_MAP(EnableSmuFeaturesLow),
+ MSG_MAP(EnableSmuFeaturesHigh),
+ MSG_MAP(DisableSmuFeaturesLow),
+ MSG_MAP(DisableSmuFeaturesHigh),
+ MSG_MAP(GetEnabledSmuFeaturesLow),
+ MSG_MAP(GetEnabledSmuFeaturesHigh),
+ MSG_MAP(SetWorkloadMask),
+ MSG_MAP(SetPptLimit),
+ MSG_MAP(SetDriverDramAddrHigh),
+ MSG_MAP(SetDriverDramAddrLow),
+ MSG_MAP(SetToolsDramAddrHigh),
+ MSG_MAP(SetToolsDramAddrLow),
+ MSG_MAP(TransferTableSmu2Dram),
+ MSG_MAP(TransferTableDram2Smu),
+ MSG_MAP(UseDefaultPPTable),
+ MSG_MAP(UseBackupPPTable),
+ MSG_MAP(RunBtc),
+ MSG_MAP(RequestI2CBus),
+ MSG_MAP(ReleaseI2CBus),
+ MSG_MAP(SetFloorSocVoltage),
+ MSG_MAP(SoftReset),
+ MSG_MAP(StartBacoMonitor),
+ MSG_MAP(CancelBacoMonitor),
+ MSG_MAP(EnterBaco),
+ MSG_MAP(SetSoftMinByFreq),
+ MSG_MAP(SetSoftMaxByFreq),
+ MSG_MAP(SetHardMinByFreq),
+ MSG_MAP(SetHardMaxByFreq),
+ MSG_MAP(GetMinDpmFreq),
+ MSG_MAP(GetMaxDpmFreq),
+ MSG_MAP(GetDpmFreqByIndex),
+ MSG_MAP(GetDpmClockFreq),
+ MSG_MAP(GetSsVoltageByDpm),
+ MSG_MAP(SetMemoryChannelConfig),
+ MSG_MAP(SetGeminiMode),
+ MSG_MAP(SetGeminiApertureHigh),
+ MSG_MAP(SetGeminiApertureLow),
+ MSG_MAP(SetMinLinkDpmByIndex),
+ MSG_MAP(OverridePcieParameters),
+ MSG_MAP(OverDriveSetPercentage),
+ MSG_MAP(SetMinDeepSleepDcefclk),
+ MSG_MAP(ReenableAcDcInterrupt),
+ MSG_MAP(NotifyPowerSource),
+ MSG_MAP(SetUclkFastSwitch),
+ MSG_MAP(SetUclkDownHyst),
+ MSG_MAP(GetCurrentRpm),
+ MSG_MAP(SetVideoFps),
+ MSG_MAP(SetTjMax),
+ MSG_MAP(SetFanTemperatureTarget),
+ MSG_MAP(PrepareMp1ForUnload),
+ MSG_MAP(DramLogSetDramAddrHigh),
+ MSG_MAP(DramLogSetDramAddrLow),
+ MSG_MAP(DramLogSetDramSize),
+ MSG_MAP(SetFanMaxRpm),
+ MSG_MAP(SetFanMinPwm),
+ MSG_MAP(ConfigureGfxDidt),
+ MSG_MAP(NumOfDisplays),
+ MSG_MAP(RemoveMargins),
+ MSG_MAP(ReadSerialNumTop32),
+ MSG_MAP(ReadSerialNumBottom32),
+ MSG_MAP(SetSystemVirtualDramAddrHigh),
+ MSG_MAP(SetSystemVirtualDramAddrLow),
+ MSG_MAP(WaflTest),
+ MSG_MAP(SetFclkGfxClkRatio),
+ MSG_MAP(AllowGfxOff),
+ MSG_MAP(DisallowGfxOff),
+ MSG_MAP(GetPptLimit),
+ MSG_MAP(GetDcModeMaxDpmFreq),
+ MSG_MAP(GetDebugData),
+ MSG_MAP(SetXgmiMode),
+ MSG_MAP(RunAfllBtc),
+ MSG_MAP(ExitBaco),
+ MSG_MAP(PrepareMp1ForReset),
+ MSG_MAP(PrepareMp1ForShutdown),
+ MSG_MAP(SetMGpuFanBoostLimitRpm),
+ MSG_MAP(GetAVFSVoltageByDpm),
+};
+
+static int vega20_get_smu_msg_index(struct smu_context *smc, uint32_t index)
+{
+ int val;
+
+ if (index >= SMU_MSG_MAX_COUNT)
+ return -EINVAL;
+
+ val = vega20_message_map[index];
+ if (val > PPSMC_Message_Count)
+ return -EINVAL;
+
+ return val;
+}
+
+static int vega20_allocate_dpm_context(struct smu_context *smu)
+{
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+
+ if (smu_dpm->dpm_context)
+ return -EINVAL;
+
+ smu_dpm->dpm_context = kzalloc(sizeof(struct vega20_dpm_table),
+ GFP_KERNEL);
+ if (!smu_dpm->dpm_context)
+ return -ENOMEM;
+
+ if (smu_dpm->golden_dpm_context)
+ return -EINVAL;
+
+ smu_dpm->golden_dpm_context = kzalloc(sizeof(struct vega20_dpm_table),
+ GFP_KERNEL);
+ if (!smu_dpm->golden_dpm_context)
+ return -ENOMEM;
+
+ smu_dpm->dpm_context_size = sizeof(struct vega20_dpm_table);
+
+ smu_dpm->dpm_current_power_state = kzalloc(sizeof(struct smu_power_state),
+ GFP_KERNEL);
+ if (!smu_dpm->dpm_current_power_state)
+ return -ENOMEM;
+
+ smu_dpm->dpm_request_power_state = kzalloc(sizeof(struct smu_power_state),
+ GFP_KERNEL);
+ if (!smu_dpm->dpm_request_power_state)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int vega20_setup_od8_information(struct smu_context *smu)
+{
+ ATOM_Vega20_POWERPLAYTABLE *powerplay_table = NULL;
+ struct smu_table_context *table_context = &smu->smu_table;
+
+ uint32_t od_feature_count, od_feature_array_size,
+ od_setting_count, od_setting_array_size;
+
+ if (!table_context->power_play_table)
+ return -EINVAL;
+
+ powerplay_table = table_context->power_play_table;
+
+ if (powerplay_table->OverDrive8Table.ucODTableRevision == 1) {
+ /* Setup correct ODFeatureCount, and store ODFeatureArray from
+ * powerplay table to od_feature_capabilities */
+ od_feature_count =
+ (le32_to_cpu(powerplay_table->OverDrive8Table.ODFeatureCount) >
+ ATOM_VEGA20_ODFEATURE_COUNT) ?
+ ATOM_VEGA20_ODFEATURE_COUNT :
+ le32_to_cpu(powerplay_table->OverDrive8Table.ODFeatureCount);
+
+ od_feature_array_size = sizeof(uint8_t) * od_feature_count;
+
+ if (table_context->od_feature_capabilities)
+ return -EINVAL;
+
+ table_context->od_feature_capabilities = kmemdup(&powerplay_table->OverDrive8Table.ODFeatureCapabilities,
+ od_feature_array_size,
+ GFP_KERNEL);
+ if (!table_context->od_feature_capabilities)
+ return -ENOMEM;
+
+ /* Setup correct ODSettingCount, and store ODSettingArray from
+ * powerplay table to od_settings_max and od_setting_min */
+ od_setting_count =
+ (le32_to_cpu(powerplay_table->OverDrive8Table.ODSettingCount) >
+ ATOM_VEGA20_ODSETTING_COUNT) ?
+ ATOM_VEGA20_ODSETTING_COUNT :
+ le32_to_cpu(powerplay_table->OverDrive8Table.ODSettingCount);
+
+ od_setting_array_size = sizeof(uint32_t) * od_setting_count;
+
+ if (table_context->od_settings_max)
+ return -EINVAL;
+
+ table_context->od_settings_max = kmemdup(&powerplay_table->OverDrive8Table.ODSettingsMax,
+ od_setting_array_size,
+ GFP_KERNEL);
+
+ if (!table_context->od_settings_max) {
+ kfree(table_context->od_feature_capabilities);
+ table_context->od_feature_capabilities = NULL;
+ return -ENOMEM;
+ }
+
+ if (table_context->od_settings_min)
+ return -EINVAL;
+
+ table_context->od_settings_min = kmemdup(&powerplay_table->OverDrive8Table.ODSettingsMin,
+ od_setting_array_size,
+ GFP_KERNEL);
+
+ if (!table_context->od_settings_min) {
+ kfree(table_context->od_feature_capabilities);
+ table_context->od_feature_capabilities = NULL;
+ kfree(table_context->od_settings_max);
+ table_context->od_settings_max = NULL;
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static int vega20_store_powerplay_table(struct smu_context *smu)
+{
+ ATOM_Vega20_POWERPLAYTABLE *powerplay_table = NULL;
+ struct smu_table_context *table_context = &smu->smu_table;
+ int ret;
+
+ if (!table_context->power_play_table)
+ return -EINVAL;
+
+ powerplay_table = table_context->power_play_table;
+
+ memcpy(table_context->driver_pptable, &powerplay_table->smcPPTable,
+ sizeof(PPTable_t));
+
+ table_context->software_shutdown_temp = powerplay_table->usSoftwareShutdownTemp;
+ table_context->thermal_controller_type = powerplay_table->ucThermalControllerType;
+ table_context->TDPODLimit = le32_to_cpu(powerplay_table->OverDrive8Table.ODSettingsMax[ATOM_VEGA20_ODSETTING_POWERPERCENTAGE]);
+
+ ret = vega20_setup_od8_information(smu);
+
+ return ret;
+}
+
+static int vega20_append_powerplay_table(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ PPTable_t *smc_pptable = table_context->driver_pptable;
+ struct atom_smc_dpm_info_v4_4 *smc_dpm_table;
+ int index, i, ret;
+
+ index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1,
+ smc_dpm_info);
+
+ ret = smu_get_atom_data_table(smu, index, NULL, NULL, NULL,
+ (uint8_t **)&smc_dpm_table);
+ if (ret)
+ return ret;
+
+ smc_pptable->MaxVoltageStepGfx = smc_dpm_table->maxvoltagestepgfx;
+ smc_pptable->MaxVoltageStepSoc = smc_dpm_table->maxvoltagestepsoc;
+
+ smc_pptable->VddGfxVrMapping = smc_dpm_table->vddgfxvrmapping;
+ smc_pptable->VddSocVrMapping = smc_dpm_table->vddsocvrmapping;
+ smc_pptable->VddMem0VrMapping = smc_dpm_table->vddmem0vrmapping;
+ smc_pptable->VddMem1VrMapping = smc_dpm_table->vddmem1vrmapping;
+
+ smc_pptable->GfxUlvPhaseSheddingMask = smc_dpm_table->gfxulvphasesheddingmask;
+ smc_pptable->SocUlvPhaseSheddingMask = smc_dpm_table->soculvphasesheddingmask;
+ smc_pptable->ExternalSensorPresent = smc_dpm_table->externalsensorpresent;
+
+ smc_pptable->GfxMaxCurrent = smc_dpm_table->gfxmaxcurrent;
+ smc_pptable->GfxOffset = smc_dpm_table->gfxoffset;
+ smc_pptable->Padding_TelemetryGfx = smc_dpm_table->padding_telemetrygfx;
+
+ smc_pptable->SocMaxCurrent = smc_dpm_table->socmaxcurrent;
+ smc_pptable->SocOffset = smc_dpm_table->socoffset;
+ smc_pptable->Padding_TelemetrySoc = smc_dpm_table->padding_telemetrysoc;
+
+ smc_pptable->Mem0MaxCurrent = smc_dpm_table->mem0maxcurrent;
+ smc_pptable->Mem0Offset = smc_dpm_table->mem0offset;
+ smc_pptable->Padding_TelemetryMem0 = smc_dpm_table->padding_telemetrymem0;
+
+ smc_pptable->Mem1MaxCurrent = smc_dpm_table->mem1maxcurrent;
+ smc_pptable->Mem1Offset = smc_dpm_table->mem1offset;
+ smc_pptable->Padding_TelemetryMem1 = smc_dpm_table->padding_telemetrymem1;
+
+ smc_pptable->AcDcGpio = smc_dpm_table->acdcgpio;
+ smc_pptable->AcDcPolarity = smc_dpm_table->acdcpolarity;
+ smc_pptable->VR0HotGpio = smc_dpm_table->vr0hotgpio;
+ smc_pptable->VR0HotPolarity = smc_dpm_table->vr0hotpolarity;
+
+ smc_pptable->VR1HotGpio = smc_dpm_table->vr1hotgpio;
+ smc_pptable->VR1HotPolarity = smc_dpm_table->vr1hotpolarity;
+ smc_pptable->Padding1 = smc_dpm_table->padding1;
+ smc_pptable->Padding2 = smc_dpm_table->padding2;
+
+ smc_pptable->LedPin0 = smc_dpm_table->ledpin0;
+ smc_pptable->LedPin1 = smc_dpm_table->ledpin1;
+ smc_pptable->LedPin2 = smc_dpm_table->ledpin2;
+
+ smc_pptable->PllGfxclkSpreadEnabled = smc_dpm_table->pllgfxclkspreadenabled;
+ smc_pptable->PllGfxclkSpreadPercent = smc_dpm_table->pllgfxclkspreadpercent;
+ smc_pptable->PllGfxclkSpreadFreq = smc_dpm_table->pllgfxclkspreadfreq;
+
+ smc_pptable->UclkSpreadEnabled = 0;
+ smc_pptable->UclkSpreadPercent = smc_dpm_table->uclkspreadpercent;
+ smc_pptable->UclkSpreadFreq = smc_dpm_table->uclkspreadfreq;
+
+ smc_pptable->FclkSpreadEnabled = smc_dpm_table->fclkspreadenabled;
+ smc_pptable->FclkSpreadPercent = smc_dpm_table->fclkspreadpercent;
+ smc_pptable->FclkSpreadFreq = smc_dpm_table->fclkspreadfreq;
+
+ smc_pptable->FllGfxclkSpreadEnabled = smc_dpm_table->fllgfxclkspreadenabled;
+ smc_pptable->FllGfxclkSpreadPercent = smc_dpm_table->fllgfxclkspreadpercent;
+ smc_pptable->FllGfxclkSpreadFreq = smc_dpm_table->fllgfxclkspreadfreq;
+
+ for (i = 0; i < I2C_CONTROLLER_NAME_COUNT; i++) {
+ smc_pptable->I2cControllers[i].Enabled =
+ smc_dpm_table->i2ccontrollers[i].enabled;
+ smc_pptable->I2cControllers[i].SlaveAddress =
+ smc_dpm_table->i2ccontrollers[i].slaveaddress;
+ smc_pptable->I2cControllers[i].ControllerPort =
+ smc_dpm_table->i2ccontrollers[i].controllerport;
+ smc_pptable->I2cControllers[i].ThermalThrottler =
+ smc_dpm_table->i2ccontrollers[i].thermalthrottler;
+ smc_pptable->I2cControllers[i].I2cProtocol =
+ smc_dpm_table->i2ccontrollers[i].i2cprotocol;
+ smc_pptable->I2cControllers[i].I2cSpeed =
+ smc_dpm_table->i2ccontrollers[i].i2cspeed;
+ }
+
+ return 0;
+}
+
+static int vega20_check_powerplay_table(struct smu_context *smu)
+{
+ ATOM_Vega20_POWERPLAYTABLE *powerplay_table = NULL;
+ struct smu_table_context *table_context = &smu->smu_table;
+
+ powerplay_table = table_context->power_play_table;
+
+ if (powerplay_table->sHeader.format_revision < ATOM_VEGA20_TABLE_REVISION_VEGA20) {
+ pr_err("Unsupported PPTable format!");
+ return -EINVAL;
+ }
+
+ if (!powerplay_table->sHeader.structuresize) {
+ pr_err("Invalid PowerPlay Table!");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vega20_run_btc_afll(struct smu_context *smu)
+{
+ return smu_send_smc_msg(smu, SMU_MSG_RunAfllBtc);
+}
+
+static int
+vega20_get_unallowed_feature_mask(struct smu_context *smu,
+ uint32_t *feature_mask, uint32_t num)
+{
+ if (num > 2)
+ return -EINVAL;
+
+ feature_mask[0] = 0xE0041C00;
+ feature_mask[1] = 0xFFFFFFFE; /* bit32~bit63 is Unsupported */
+
+ return 0;
+}
+
+static enum
+amd_pm_state_type vega20_get_current_power_state(struct smu_context *smu)
+{
+ enum amd_pm_state_type pm_type;
+ struct smu_dpm_context *smu_dpm_ctx = &(smu->smu_dpm);
+
+ if (!smu_dpm_ctx->dpm_context ||
+ !smu_dpm_ctx->dpm_current_power_state)
+ return -EINVAL;
+
+ mutex_lock(&(smu->mutex));
+ switch (smu_dpm_ctx->dpm_current_power_state->classification.ui_label) {
+ case SMU_STATE_UI_LABEL_BATTERY:
+ pm_type = POWER_STATE_TYPE_BATTERY;
+ break;
+ case SMU_STATE_UI_LABEL_BALLANCED:
+ pm_type = POWER_STATE_TYPE_BALANCED;
+ break;
+ case SMU_STATE_UI_LABEL_PERFORMANCE:
+ pm_type = POWER_STATE_TYPE_PERFORMANCE;
+ break;
+ default:
+ if (smu_dpm_ctx->dpm_current_power_state->classification.flags & SMU_STATE_CLASSIFICATION_FLAG_BOOT)
+ pm_type = POWER_STATE_TYPE_INTERNAL_BOOT;
+ else
+ pm_type = POWER_STATE_TYPE_DEFAULT;
+ break;
+ }
+ mutex_unlock(&(smu->mutex));
+
+ return pm_type;
+}
+
+static int
+vega20_set_single_dpm_table(struct smu_context *smu,
+ struct vega20_single_dpm_table *single_dpm_table,
+ PPCLK_e clk_id)
+{
+ int ret = 0;
+ uint32_t i, num_of_levels = 0, clk;
+
+ ret = smu_send_smc_msg_with_param(smu,
+ SMU_MSG_GetDpmFreqByIndex,
+ (clk_id << 16 | 0xFF));
+ if (ret) {
+ pr_err("[GetNumOfDpmLevel] failed to get dpm levels!");
+ return ret;
+ }
+
+ smu_read_smc_arg(smu, &num_of_levels);
+ if (!num_of_levels) {
+ pr_err("[GetNumOfDpmLevel] number of clk levels is invalid!");
+ return -EINVAL;
+ }
+
+ single_dpm_table->count = num_of_levels;
+
+ for (i = 0; i < num_of_levels; i++) {
+ ret = smu_send_smc_msg_with_param(smu,
+ SMU_MSG_GetDpmFreqByIndex,
+ (clk_id << 16 | i));
+ if (ret) {
+ pr_err("[GetDpmFreqByIndex] failed to get dpm freq by index!");
+ return ret;
+ }
+ smu_read_smc_arg(smu, &clk);
+ if (!clk) {
+ pr_err("[GetDpmFreqByIndex] clk value is invalid!");
+ return -EINVAL;
+ }
+ single_dpm_table->dpm_levels[i].value = clk;
+ single_dpm_table->dpm_levels[i].enabled = true;
+ }
+ return 0;
+}
+
+static void vega20_init_single_dpm_state(struct vega20_dpm_state *dpm_state)
+{
+ dpm_state->soft_min_level = 0x0;
+ dpm_state->soft_max_level = 0xffff;
+ dpm_state->hard_min_level = 0x0;
+ dpm_state->hard_max_level = 0xffff;
+}
+
+static int vega20_set_default_dpm_table(struct smu_context *smu)
+{
+ int ret;
+
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+ struct vega20_dpm_table *dpm_table = NULL;
+ struct vega20_single_dpm_table *single_dpm_table;
+
+ dpm_table = smu_dpm->dpm_context;
+
+ /* socclk */
+ single_dpm_table = &(dpm_table->soc_table);
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_SOCCLK_BIT)) {
+ ret = vega20_set_single_dpm_table(smu, single_dpm_table,
+ PPCLK_SOCCLK);
+ if (ret) {
+ pr_err("[SetupDefaultDpmTable] failed to get socclk dpm levels!");
+ return ret;
+ }
+ } else {
+ single_dpm_table->count = 1;
+ single_dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.socclk / 100;
+ }
+ vega20_init_single_dpm_state(&(single_dpm_table->dpm_state));
+
+ /* gfxclk */
+ single_dpm_table = &(dpm_table->gfx_table);
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_GFXCLK_BIT)) {
+ ret = vega20_set_single_dpm_table(smu, single_dpm_table,
+ PPCLK_GFXCLK);
+ if (ret) {
+ pr_err("[SetupDefaultDpmTable] failed to get gfxclk dpm levels!");
+ return ret;
+ }
+ } else {
+ single_dpm_table->count = 1;
+ single_dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.gfxclk / 100;
+ }
+ vega20_init_single_dpm_state(&(single_dpm_table->dpm_state));
+
+ /* memclk */
+ single_dpm_table = &(dpm_table->mem_table);
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_UCLK_BIT)) {
+ ret = vega20_set_single_dpm_table(smu, single_dpm_table,
+ PPCLK_UCLK);
+ if (ret) {
+ pr_err("[SetupDefaultDpmTable] failed to get memclk dpm levels!");
+ return ret;
+ }
+ } else {
+ single_dpm_table->count = 1;
+ single_dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.uclk / 100;
+ }
+ vega20_init_single_dpm_state(&(single_dpm_table->dpm_state));
+
+ /* eclk */
+ single_dpm_table = &(dpm_table->eclk_table);
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_VCE_BIT)) {
+ ret = vega20_set_single_dpm_table(smu, single_dpm_table, PPCLK_ECLK);
+ if (ret) {
+ pr_err("[SetupDefaultDpmTable] failed to get eclk dpm levels!");
+ return ret;
+ }
+ } else {
+ single_dpm_table->count = 1;
+ single_dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.eclk / 100;
+ }
+ vega20_init_single_dpm_state(&(single_dpm_table->dpm_state));
+
+ /* vclk */
+ single_dpm_table = &(dpm_table->vclk_table);
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_UVD_BIT)) {
+ ret = vega20_set_single_dpm_table(smu, single_dpm_table, PPCLK_VCLK);
+ if (ret) {
+ pr_err("[SetupDefaultDpmTable] failed to get vclk dpm levels!");
+ return ret;
+ }
+ } else {
+ single_dpm_table->count = 1;
+ single_dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.vclk / 100;
+ }
+ vega20_init_single_dpm_state(&(single_dpm_table->dpm_state));
+
+ /* dclk */
+ single_dpm_table = &(dpm_table->dclk_table);
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_UVD_BIT)) {
+ ret = vega20_set_single_dpm_table(smu, single_dpm_table, PPCLK_DCLK);
+ if (ret) {
+ pr_err("[SetupDefaultDpmTable] failed to get dclk dpm levels!");
+ return ret;
+ }
+ } else {
+ single_dpm_table->count = 1;
+ single_dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dclk / 100;
+ }
+ vega20_init_single_dpm_state(&(single_dpm_table->dpm_state));
+
+ /* dcefclk */
+ single_dpm_table = &(dpm_table->dcef_table);
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_DCEFCLK_BIT)) {
+ ret = vega20_set_single_dpm_table(smu, single_dpm_table,
+ PPCLK_DCEFCLK);
+ if (ret) {
+ pr_err("[SetupDefaultDpmTable] failed to get dcefclk dpm levels!");
+ return ret;
+ }
+ } else {
+ single_dpm_table->count = 1;
+ single_dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dcefclk / 100;
+ }
+ vega20_init_single_dpm_state(&(single_dpm_table->dpm_state));
+
+ /* pixclk */
+ single_dpm_table = &(dpm_table->pixel_table);
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_DCEFCLK_BIT)) {
+ ret = vega20_set_single_dpm_table(smu, single_dpm_table,
+ PPCLK_PIXCLK);
+ if (ret) {
+ pr_err("[SetupDefaultDpmTable] failed to get pixclk dpm levels!");
+ return ret;
+ }
+ } else {
+ single_dpm_table->count = 0;
+ }
+ vega20_init_single_dpm_state(&(single_dpm_table->dpm_state));
+
+ /* dispclk */
+ single_dpm_table = &(dpm_table->display_table);
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_DCEFCLK_BIT)) {
+ ret = vega20_set_single_dpm_table(smu, single_dpm_table,
+ PPCLK_DISPCLK);
+ if (ret) {
+ pr_err("[SetupDefaultDpmTable] failed to get dispclk dpm levels!");
+ return ret;
+ }
+ } else {
+ single_dpm_table->count = 0;
+ }
+ vega20_init_single_dpm_state(&(single_dpm_table->dpm_state));
+
+ /* phyclk */
+ single_dpm_table = &(dpm_table->phy_table);
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_DCEFCLK_BIT)) {
+ ret = vega20_set_single_dpm_table(smu, single_dpm_table,
+ PPCLK_PHYCLK);
+ if (ret) {
+ pr_err("[SetupDefaultDpmTable] failed to get phyclk dpm levels!");
+ return ret;
+ }
+ } else {
+ single_dpm_table->count = 0;
+ }
+ vega20_init_single_dpm_state(&(single_dpm_table->dpm_state));
+
+ /* fclk */
+ single_dpm_table = &(dpm_table->fclk_table);
+
+ if (smu_feature_is_enabled(smu,FEATURE_DPM_FCLK_BIT)) {
+ ret = vega20_set_single_dpm_table(smu, single_dpm_table,
+ PPCLK_FCLK);
+ if (ret) {
+ pr_err("[SetupDefaultDpmTable] failed to get fclk dpm levels!");
+ return ret;
+ }
+ } else {
+ single_dpm_table->count = 0;
+ }
+ vega20_init_single_dpm_state(&(single_dpm_table->dpm_state));
+
+ memcpy(smu_dpm->golden_dpm_context, dpm_table,
+ sizeof(struct vega20_dpm_table));
+
+ return 0;
+}
+
+static int vega20_populate_umd_state_clk(struct smu_context *smu)
+{
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+ struct vega20_dpm_table *dpm_table = NULL;
+ struct vega20_single_dpm_table *gfx_table = NULL;
+ struct vega20_single_dpm_table *mem_table = NULL;
+
+ dpm_table = smu_dpm->dpm_context;
+ gfx_table = &(dpm_table->gfx_table);
+ mem_table = &(dpm_table->mem_table);
+
+ smu->pstate_sclk = gfx_table->dpm_levels[0].value;
+ smu->pstate_mclk = mem_table->dpm_levels[0].value;
+
+ if (gfx_table->count > VEGA20_UMD_PSTATE_GFXCLK_LEVEL &&
+ mem_table->count > VEGA20_UMD_PSTATE_MCLK_LEVEL) {
+ smu->pstate_sclk = gfx_table->dpm_levels[VEGA20_UMD_PSTATE_GFXCLK_LEVEL].value;
+ smu->pstate_mclk = mem_table->dpm_levels[VEGA20_UMD_PSTATE_MCLK_LEVEL].value;
+ }
+
+ smu->pstate_sclk = smu->pstate_sclk * 100;
+ smu->pstate_mclk = smu->pstate_mclk * 100;
+
+ return 0;
+}
+
+static int vega20_get_clk_table(struct smu_context *smu,
+ struct pp_clock_levels_with_latency *clocks,
+ struct vega20_single_dpm_table *dpm_table)
+{
+ int i, count;
+
+ count = (dpm_table->count > MAX_NUM_CLOCKS) ? MAX_NUM_CLOCKS : dpm_table->count;
+ clocks->num_levels = count;
+
+ for (i = 0; i < count; i++) {
+ clocks->data[i].clocks_in_khz =
+ dpm_table->dpm_levels[i].value * 1000;
+ clocks->data[i].latency_in_us = 0;
+ }
+
+ return 0;
+}
+
+static int vega20_print_clk_levels(struct smu_context *smu,
+ enum pp_clock_type type, char *buf)
+{
+ int i, now, size = 0;
+ int ret = 0;
+ uint32_t gen_speed, lane_width;
+ struct amdgpu_device *adev = smu->adev;
+ struct pp_clock_levels_with_latency clocks;
+ struct vega20_single_dpm_table *single_dpm_table;
+ struct smu_table_context *table_context = &smu->smu_table;
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+ struct vega20_dpm_table *dpm_table = NULL;
+ struct vega20_od8_settings *od8_settings =
+ (struct vega20_od8_settings *)table_context->od8_settings;
+ OverDriveTable_t *od_table =
+ (OverDriveTable_t *)(table_context->overdrive_table);
+ PPTable_t *pptable = (PPTable_t *)table_context->driver_pptable;
+
+ dpm_table = smu_dpm->dpm_context;
+
+ switch (type) {
+ case PP_SCLK:
+ ret = smu_get_current_clk_freq(smu, PPCLK_GFXCLK, &now);
+ if (ret) {
+ pr_err("Attempt to get current gfx clk Failed!");
+ return ret;
+ }
+
+ single_dpm_table = &(dpm_table->gfx_table);
+ ret = vega20_get_clk_table(smu, &clocks, single_dpm_table);
+ if (ret) {
+ pr_err("Attempt to get gfx clk 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_MCLK:
+ ret = smu_get_current_clk_freq(smu, PPCLK_UCLK, &now);
+ if (ret) {
+ pr_err("Attempt to get current mclk Failed!");
+ return ret;
+ }
+
+ single_dpm_table = &(dpm_table->mem_table);
+ ret = vega20_get_clk_table(smu, &clocks, single_dpm_table);
+ if (ret) {
+ pr_err("Attempt to get memory clk 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_SOCCLK:
+ ret = smu_get_current_clk_freq(smu, PPCLK_SOCCLK, &now);
+ if (ret) {
+ pr_err("Attempt to get current socclk Failed!");
+ return ret;
+ }
+
+ single_dpm_table = &(dpm_table->soc_table);
+ ret = vega20_get_clk_table(smu, &clocks, single_dpm_table);
+ if (ret) {
+ pr_err("Attempt to get socclk 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_FCLK:
+ ret = smu_get_current_clk_freq(smu, PPCLK_FCLK, &now);
+ if (ret) {
+ pr_err("Attempt to get current fclk Failed!");
+ return ret;
+ }
+
+ single_dpm_table = &(dpm_table->fclk_table);
+ for (i = 0; i < single_dpm_table->count; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, single_dpm_table->dpm_levels[i].value,
+ (single_dpm_table->dpm_levels[i].value == now / 100)
+ ? "*" : "");
+ break;
+
+ case PP_DCEFCLK:
+ ret = smu_get_current_clk_freq(smu, PPCLK_DCEFCLK, &now);
+ if (ret) {
+ pr_err("Attempt to get current dcefclk Failed!");
+ return ret;
+ }
+
+ single_dpm_table = &(dpm_table->dcef_table);
+ ret = vega20_get_clk_table(smu, &clocks, single_dpm_table);
+ if (ret) {
+ pr_err("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)
+ >> PSWUSP0_PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE__SHIFT;
+ lane_width = (RREG32_PCIE(smnPCIE_LC_LINK_WIDTH_CNTL) &
+ PCIE_LC_LINK_WIDTH_CNTL__LC_LINK_WIDTH_RD_MASK)
+ >> PCIE_LC_LINK_WIDTH_CNTL__LC_LINK_WIDTH_RD__SHIFT;
+ for (i = 0; i < NUM_LINK_LEVELS; i++)
+ size += sprintf(buf + size, "%d: %s %s %dMhz %s\n", i,
+ (pptable->PcieGenSpeed[i] == 0) ? "2.5GT/s," :
+ (pptable->PcieGenSpeed[i] == 1) ? "5.0GT/s," :
+ (pptable->PcieGenSpeed[i] == 2) ? "8.0GT/s," :
+ (pptable->PcieGenSpeed[i] == 3) ? "16.0GT/s," : "",
+ (pptable->PcieLaneCount[i] == 1) ? "x1" :
+ (pptable->PcieLaneCount[i] == 2) ? "x2" :
+ (pptable->PcieLaneCount[i] == 3) ? "x4" :
+ (pptable->PcieLaneCount[i] == 4) ? "x8" :
+ (pptable->PcieLaneCount[i] == 5) ? "x12" :
+ (pptable->PcieLaneCount[i] == 6) ? "x16" : "",
+ pptable->LclkFreq[i],
+ (gen_speed == pptable->PcieGenSpeed[i]) &&
+ (lane_width == pptable->PcieLaneCount[i]) ?
+ "*" : "");
+ break;
+
+ case OD_SCLK:
+ if (od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMIN].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].feature_id) {
+ size = sprintf(buf, "%s:\n", "OD_SCLK");
+ size += sprintf(buf + size, "0: %10uMhz\n",
+ od_table->GfxclkFmin);
+ size += sprintf(buf + size, "1: %10uMhz\n",
+ od_table->GfxclkFmax);
+ }
+
+ break;
+
+ case OD_MCLK:
+ if (od8_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].feature_id) {
+ size = sprintf(buf, "%s:\n", "OD_MCLK");
+ size += sprintf(buf + size, "1: %10uMhz\n",
+ od_table->UclkFmax);
+ }
+
+ break;
+
+ case OD_VDDC_CURVE:
+ if (od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ1].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ2].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ3].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].feature_id) {
+ size = sprintf(buf, "%s:\n", "OD_VDDC_CURVE");
+ size += sprintf(buf + size, "0: %10uMhz %10dmV\n",
+ od_table->GfxclkFreq1,
+ od_table->GfxclkVolt1 / VOLTAGE_SCALE);
+ size += sprintf(buf + size, "1: %10uMhz %10dmV\n",
+ od_table->GfxclkFreq2,
+ od_table->GfxclkVolt2 / VOLTAGE_SCALE);
+ size += sprintf(buf + size, "2: %10uMhz %10dmV\n",
+ od_table->GfxclkFreq3,
+ od_table->GfxclkVolt3 / VOLTAGE_SCALE);
+ }
+
+ break;
+
+ case OD_RANGE:
+ size = sprintf(buf, "%s:\n", "OD_RANGE");
+
+ if (od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMIN].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].feature_id) {
+ size += sprintf(buf + size, "SCLK: %7uMhz %10uMhz\n",
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMIN].min_value,
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].max_value);
+ }
+
+ if (od8_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].feature_id) {
+ single_dpm_table = &(dpm_table->mem_table);
+ ret = vega20_get_clk_table(smu, &clocks, single_dpm_table);
+ if (ret) {
+ pr_err("Attempt to get memory clk levels Failed!");
+ return ret;
+ }
+
+ size += sprintf(buf + size, "MCLK: %7uMhz %10uMhz\n",
+ clocks.data[0].clocks_in_khz / 1000,
+ od8_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].max_value);
+ }
+
+ if (od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ1].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ2].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ3].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].feature_id) {
+ size += sprintf(buf + size, "VDDC_CURVE_SCLK[0]: %7uMhz %10uMhz\n",
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ1].min_value,
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ1].max_value);
+ size += sprintf(buf + size, "VDDC_CURVE_VOLT[0]: %7dmV %11dmV\n",
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].min_value,
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].max_value);
+ size += sprintf(buf + size, "VDDC_CURVE_SCLK[1]: %7uMhz %10uMhz\n",
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ2].min_value,
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ2].max_value);
+ size += sprintf(buf + size, "VDDC_CURVE_VOLT[1]: %7dmV %11dmV\n",
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].min_value,
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].max_value);
+ size += sprintf(buf + size, "VDDC_CURVE_SCLK[2]: %7uMhz %10uMhz\n",
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ3].min_value,
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ3].max_value);
+ size += sprintf(buf + size, "VDDC_CURVE_VOLT[2]: %7dmV %11dmV\n",
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].min_value,
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].max_value);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ return size;
+}
+
+static int vega20_upload_dpm_level(struct smu_context *smu, bool max,
+ uint32_t feature_mask)
+{
+ struct vega20_dpm_table *dpm_table;
+ struct vega20_single_dpm_table *single_dpm_table;
+ uint32_t freq;
+ int ret = 0;
+
+ dpm_table = smu->smu_dpm.dpm_context;
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_GFXCLK_BIT) &&
+ (feature_mask & FEATURE_DPM_GFXCLK_MASK)) {
+ single_dpm_table = &(dpm_table->gfx_table);
+ freq = max ? single_dpm_table->dpm_state.soft_max_level :
+ single_dpm_table->dpm_state.soft_min_level;
+ ret = smu_send_smc_msg_with_param(smu,
+ (max ? SMU_MSG_SetSoftMaxByFreq : SMU_MSG_SetSoftMinByFreq),
+ (PPCLK_GFXCLK << 16) | (freq & 0xffff));
+ if (ret) {
+ pr_err("Failed to set soft %s gfxclk !\n",
+ max ? "max" : "min");
+ return ret;
+ }
+ }
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_UCLK_BIT) &&
+ (feature_mask & FEATURE_DPM_UCLK_MASK)) {
+ single_dpm_table = &(dpm_table->mem_table);
+ freq = max ? single_dpm_table->dpm_state.soft_max_level :
+ single_dpm_table->dpm_state.soft_min_level;
+ ret = smu_send_smc_msg_with_param(smu,
+ (max ? SMU_MSG_SetSoftMaxByFreq : SMU_MSG_SetSoftMinByFreq),
+ (PPCLK_UCLK << 16) | (freq & 0xffff));
+ if (ret) {
+ pr_err("Failed to set soft %s memclk !\n",
+ max ? "max" : "min");
+ return ret;
+ }
+ }
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_SOCCLK_BIT) &&
+ (feature_mask & FEATURE_DPM_SOCCLK_MASK)) {
+ single_dpm_table = &(dpm_table->soc_table);
+ freq = max ? single_dpm_table->dpm_state.soft_max_level :
+ single_dpm_table->dpm_state.soft_min_level;
+ ret = smu_send_smc_msg_with_param(smu,
+ (max ? SMU_MSG_SetSoftMaxByFreq : SMU_MSG_SetSoftMinByFreq),
+ (PPCLK_SOCCLK << 16) | (freq & 0xffff));
+ if (ret) {
+ pr_err("Failed to set soft %s socclk !\n",
+ max ? "max" : "min");
+ return ret;
+ }
+ }
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_FCLK_BIT) &&
+ (feature_mask & FEATURE_DPM_FCLK_MASK)) {
+ single_dpm_table = &(dpm_table->fclk_table);
+ freq = max ? single_dpm_table->dpm_state.soft_max_level :
+ single_dpm_table->dpm_state.soft_min_level;
+ ret = smu_send_smc_msg_with_param(smu,
+ (max ? SMU_MSG_SetSoftMaxByFreq : SMU_MSG_SetSoftMinByFreq),
+ (PPCLK_FCLK << 16) | (freq & 0xffff));
+ if (ret) {
+ pr_err("Failed to set soft %s fclk !\n",
+ max ? "max" : "min");
+ return ret;
+ }
+ }
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_DCEFCLK_BIT) &&
+ (feature_mask & FEATURE_DPM_DCEFCLK_MASK)) {
+ single_dpm_table = &(dpm_table->dcef_table);
+ freq = single_dpm_table->dpm_state.hard_min_level;
+ if (!max) {
+ ret = smu_send_smc_msg_with_param(smu,
+ SMU_MSG_SetHardMinByFreq,
+ (PPCLK_DCEFCLK << 16) | (freq & 0xffff));
+ if (ret) {
+ pr_err("Failed to set hard min dcefclk !\n");
+ return ret;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int vega20_force_clk_levels(struct smu_context *smu,
+ enum pp_clock_type type, uint32_t mask)
+{
+ struct vega20_dpm_table *dpm_table;
+ struct vega20_single_dpm_table *single_dpm_table;
+ uint32_t soft_min_level, soft_max_level, hard_min_level;
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+ int ret = 0;
+
+ if (smu_dpm->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL) {
+ pr_info("force clock level is for dpm manual mode only.\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&(smu->mutex));
+
+ soft_min_level = mask ? (ffs(mask) - 1) : 0;
+ soft_max_level = mask ? (fls(mask) - 1) : 0;
+
+ dpm_table = smu->smu_dpm.dpm_context;
+
+ switch (type) {
+ case PP_SCLK:
+ single_dpm_table = &(dpm_table->gfx_table);
+
+ if (soft_max_level >= single_dpm_table->count) {
+ pr_err("Clock level specified %d is over max allowed %d\n",
+ soft_max_level, single_dpm_table->count - 1);
+ ret = -EINVAL;
+ break;
+ }
+
+ single_dpm_table->dpm_state.soft_min_level =
+ single_dpm_table->dpm_levels[soft_min_level].value;
+ single_dpm_table->dpm_state.soft_max_level =
+ single_dpm_table->dpm_levels[soft_max_level].value;
+
+ ret = vega20_upload_dpm_level(smu, false, FEATURE_DPM_GFXCLK_MASK);
+ if (ret) {
+ pr_err("Failed to upload boot level to lowest!\n");
+ break;
+ }
+
+ ret = vega20_upload_dpm_level(smu, true, FEATURE_DPM_GFXCLK_MASK);
+ if (ret)
+ pr_err("Failed to upload dpm max level to highest!\n");
+
+ break;
+
+ case PP_MCLK:
+ single_dpm_table = &(dpm_table->mem_table);
+
+ if (soft_max_level >= single_dpm_table->count) {
+ pr_err("Clock level specified %d is over max allowed %d\n",
+ soft_max_level, single_dpm_table->count - 1);
+ ret = -EINVAL;
+ break;
+ }
+
+ single_dpm_table->dpm_state.soft_min_level =
+ single_dpm_table->dpm_levels[soft_min_level].value;
+ single_dpm_table->dpm_state.soft_max_level =
+ single_dpm_table->dpm_levels[soft_max_level].value;
+
+ ret = vega20_upload_dpm_level(smu, false, FEATURE_DPM_UCLK_MASK);
+ if (ret) {
+ pr_err("Failed to upload boot level to lowest!\n");
+ break;
+ }
+
+ ret = vega20_upload_dpm_level(smu, true, FEATURE_DPM_UCLK_MASK);
+ if (ret)
+ pr_err("Failed to upload dpm max level to highest!\n");
+
+ break;
+
+ case PP_SOCCLK:
+ single_dpm_table = &(dpm_table->soc_table);
+
+ if (soft_max_level >= single_dpm_table->count) {
+ pr_err("Clock level specified %d is over max allowed %d\n",
+ soft_max_level, single_dpm_table->count - 1);
+ ret = -EINVAL;
+ break;
+ }
+
+ single_dpm_table->dpm_state.soft_min_level =
+ single_dpm_table->dpm_levels[soft_min_level].value;
+ single_dpm_table->dpm_state.soft_max_level =
+ single_dpm_table->dpm_levels[soft_max_level].value;
+
+ ret = vega20_upload_dpm_level(smu, false, FEATURE_DPM_SOCCLK_MASK);
+ if (ret) {
+ pr_err("Failed to upload boot level to lowest!\n");
+ break;
+ }
+
+ ret = vega20_upload_dpm_level(smu, true, FEATURE_DPM_SOCCLK_MASK);
+ if (ret)
+ pr_err("Failed to upload dpm max level to highest!\n");
+
+ break;
+
+ case PP_FCLK:
+ single_dpm_table = &(dpm_table->fclk_table);
+
+ if (soft_max_level >= single_dpm_table->count) {
+ pr_err("Clock level specified %d is over max allowed %d\n",
+ soft_max_level, single_dpm_table->count - 1);
+ ret = -EINVAL;
+ break;
+ }
+
+ single_dpm_table->dpm_state.soft_min_level =
+ single_dpm_table->dpm_levels[soft_min_level].value;
+ single_dpm_table->dpm_state.soft_max_level =
+ single_dpm_table->dpm_levels[soft_max_level].value;
+
+ ret = vega20_upload_dpm_level(smu, false, FEATURE_DPM_FCLK_MASK);
+ if (ret) {
+ pr_err("Failed to upload boot level to lowest!\n");
+ break;
+ }
+
+ ret = vega20_upload_dpm_level(smu, true, FEATURE_DPM_FCLK_MASK);
+ if (ret)
+ pr_err("Failed to upload dpm max level to highest!\n");
+
+ break;
+
+ case PP_DCEFCLK:
+ hard_min_level = soft_min_level;
+ single_dpm_table = &(dpm_table->dcef_table);
+
+ if (hard_min_level >= single_dpm_table->count) {
+ pr_err("Clock level specified %d is over max allowed %d\n",
+ hard_min_level, single_dpm_table->count - 1);
+ ret = -EINVAL;
+ break;
+ }
+
+ single_dpm_table->dpm_state.hard_min_level =
+ single_dpm_table->dpm_levels[hard_min_level].value;
+
+ ret = vega20_upload_dpm_level(smu, false, FEATURE_DPM_DCEFCLK_MASK);
+ if (ret)
+ pr_err("Failed to upload boot level to lowest!\n");
+
+ break;
+
+ case PP_PCIE:
+ if (soft_min_level >= NUM_LINK_LEVELS ||
+ soft_max_level >= NUM_LINK_LEVELS) {
+ ret = -EINVAL;
+ break;
+ }
+
+ ret = smu_send_smc_msg_with_param(smu,
+ SMU_MSG_SetMinLinkDpmByIndex, soft_min_level);
+ if (ret)
+ pr_err("Failed to set min link dpm level!\n");
+
+ break;
+
+ default:
+ break;
+ }
+
+ mutex_unlock(&(smu->mutex));
+ return ret;
+}
+
+static int vega20_get_clock_by_type_with_latency(struct smu_context *smu,
+ enum amd_pp_clock_type type,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ int ret;
+ struct vega20_single_dpm_table *single_dpm_table;
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+ struct vega20_dpm_table *dpm_table = NULL;
+
+ dpm_table = smu_dpm->dpm_context;
+
+ mutex_lock(&smu->mutex);
+
+ switch (type) {
+ case amd_pp_sys_clock:
+ single_dpm_table = &(dpm_table->gfx_table);
+ ret = vega20_get_clk_table(smu, clocks, single_dpm_table);
+ break;
+ case amd_pp_mem_clock:
+ single_dpm_table = &(dpm_table->mem_table);
+ ret = vega20_get_clk_table(smu, clocks, single_dpm_table);
+ break;
+ case amd_pp_dcef_clock:
+ single_dpm_table = &(dpm_table->dcef_table);
+ ret = vega20_get_clk_table(smu, clocks, single_dpm_table);
+ break;
+ case amd_pp_soc_clock:
+ single_dpm_table = &(dpm_table->soc_table);
+ ret = vega20_get_clk_table(smu, clocks, single_dpm_table);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&smu->mutex);
+ return ret;
+}
+
+static int vega20_overdrive_get_gfx_clk_base_voltage(struct smu_context *smu,
+ uint32_t *voltage,
+ uint32_t freq)
+{
+ int ret;
+
+ ret = smu_send_smc_msg_with_param(smu,
+ SMU_MSG_GetAVFSVoltageByDpm,
+ ((AVFS_CURVE << 24) | (OD8_HOTCURVE_TEMPERATURE << 16) | freq));
+ if (ret) {
+ pr_err("[GetBaseVoltage] failed to get GFXCLK AVFS voltage from SMU!");
+ return ret;
+ }
+
+ smu_read_smc_arg(smu, voltage);
+ *voltage = *voltage / VOLTAGE_SCALE;
+
+ return 0;
+}
+
+static int vega20_set_default_od8_setttings(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ OverDriveTable_t *od_table = (OverDriveTable_t *)(table_context->overdrive_table);
+ struct vega20_od8_settings *od8_settings = NULL;
+ PPTable_t *smc_pptable = table_context->driver_pptable;
+ int i, ret;
+
+ if (table_context->od8_settings)
+ return -EINVAL;
+
+ table_context->od8_settings = kzalloc(sizeof(struct vega20_od8_settings), GFP_KERNEL);
+
+ if (!table_context->od8_settings)
+ return -ENOMEM;
+
+ memset(table_context->od8_settings, 0, sizeof(struct vega20_od8_settings));
+ od8_settings = (struct vega20_od8_settings *)table_context->od8_settings;
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_SOCCLK_BIT)) {
+ if (table_context->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_GFXCLK_LIMITS] &&
+ table_context->od_settings_max[OD8_SETTING_GFXCLK_FMAX] > 0 &&
+ table_context->od_settings_min[OD8_SETTING_GFXCLK_FMIN] > 0 &&
+ (table_context->od_settings_max[OD8_SETTING_GFXCLK_FMAX] >=
+ table_context->od_settings_min[OD8_SETTING_GFXCLK_FMIN])) {
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMIN].feature_id =
+ OD8_GFXCLK_LIMITS;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].feature_id =
+ OD8_GFXCLK_LIMITS;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMIN].default_value =
+ od_table->GfxclkFmin;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].default_value =
+ od_table->GfxclkFmax;
+ }
+
+ if (table_context->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_GFXCLK_CURVE] &&
+ (table_context->od_settings_min[OD8_SETTING_GFXCLK_VOLTAGE1] >=
+ smc_pptable->MinVoltageGfx / VOLTAGE_SCALE) &&
+ (table_context->od_settings_max[OD8_SETTING_GFXCLK_VOLTAGE3] <=
+ smc_pptable->MaxVoltageGfx / VOLTAGE_SCALE) &&
+ (table_context->od_settings_min[OD8_SETTING_GFXCLK_VOLTAGE1] <=
+ table_context->od_settings_max[OD8_SETTING_GFXCLK_VOLTAGE3])) {
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ1].feature_id =
+ OD8_GFXCLK_CURVE;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].feature_id =
+ OD8_GFXCLK_CURVE;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ2].feature_id =
+ OD8_GFXCLK_CURVE;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].feature_id =
+ OD8_GFXCLK_CURVE;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ3].feature_id =
+ OD8_GFXCLK_CURVE;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].feature_id =
+ OD8_GFXCLK_CURVE;
+
+ od_table->GfxclkFreq1 = od_table->GfxclkFmin;
+ od_table->GfxclkFreq2 = (od_table->GfxclkFmin + od_table->GfxclkFmax) / 2;
+ od_table->GfxclkFreq3 = od_table->GfxclkFmax;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ1].default_value =
+ od_table->GfxclkFreq1;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ2].default_value =
+ od_table->GfxclkFreq2;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ3].default_value =
+ od_table->GfxclkFreq3;
+
+ ret = vega20_overdrive_get_gfx_clk_base_voltage(smu,
+ &od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].default_value,
+ od_table->GfxclkFreq1);
+ if (ret)
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].default_value = 0;
+ od_table->GfxclkVolt1 =
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].default_value
+ * VOLTAGE_SCALE;
+ ret = vega20_overdrive_get_gfx_clk_base_voltage(smu,
+ &od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].default_value,
+ od_table->GfxclkFreq2);
+ if (ret)
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].default_value = 0;
+ od_table->GfxclkVolt2 =
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].default_value
+ * VOLTAGE_SCALE;
+ ret = vega20_overdrive_get_gfx_clk_base_voltage(smu,
+ &od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].default_value,
+ od_table->GfxclkFreq3);
+ if (ret)
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].default_value = 0;
+ od_table->GfxclkVolt3 =
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].default_value
+ * VOLTAGE_SCALE;
+ }
+ }
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_UCLK_BIT)) {
+ if (table_context->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_UCLK_MAX] &&
+ table_context->od_settings_min[OD8_SETTING_UCLK_FMAX] > 0 &&
+ table_context->od_settings_max[OD8_SETTING_UCLK_FMAX] > 0 &&
+ (table_context->od_settings_max[OD8_SETTING_UCLK_FMAX] >=
+ table_context->od_settings_min[OD8_SETTING_UCLK_FMAX])) {
+ od8_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].feature_id =
+ OD8_UCLK_MAX;
+ od8_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].default_value =
+ od_table->UclkFmax;
+ }
+ }
+
+ if (table_context->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_POWER_LIMIT] &&
+ table_context->od_settings_min[OD8_SETTING_POWER_PERCENTAGE] > 0 &&
+ table_context->od_settings_min[OD8_SETTING_POWER_PERCENTAGE] <= 100 &&
+ table_context->od_settings_max[OD8_SETTING_POWER_PERCENTAGE] > 0 &&
+ table_context->od_settings_max[OD8_SETTING_POWER_PERCENTAGE] <= 100) {
+ od8_settings->od8_settings_array[OD8_SETTING_POWER_PERCENTAGE].feature_id =
+ OD8_POWER_LIMIT;
+ od8_settings->od8_settings_array[OD8_SETTING_POWER_PERCENTAGE].default_value =
+ od_table->OverDrivePct;
+ }
+
+ if (smu_feature_is_enabled(smu, FEATURE_FAN_CONTROL_BIT)) {
+ if (table_context->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_FAN_ACOUSTIC_LIMIT] &&
+ table_context->od_settings_min[OD8_SETTING_FAN_ACOUSTIC_LIMIT] > 0 &&
+ table_context->od_settings_max[OD8_SETTING_FAN_ACOUSTIC_LIMIT] > 0 &&
+ (table_context->od_settings_max[OD8_SETTING_FAN_ACOUSTIC_LIMIT] >=
+ table_context->od_settings_min[OD8_SETTING_FAN_ACOUSTIC_LIMIT])) {
+ od8_settings->od8_settings_array[OD8_SETTING_FAN_ACOUSTIC_LIMIT].feature_id =
+ OD8_ACOUSTIC_LIMIT_SCLK;
+ od8_settings->od8_settings_array[OD8_SETTING_FAN_ACOUSTIC_LIMIT].default_value =
+ od_table->FanMaximumRpm;
+ }
+
+ if (table_context->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_FAN_SPEED_MIN] &&
+ table_context->od_settings_min[OD8_SETTING_FAN_MIN_SPEED] > 0 &&
+ table_context->od_settings_max[OD8_SETTING_FAN_MIN_SPEED] > 0 &&
+ (table_context->od_settings_max[OD8_SETTING_FAN_MIN_SPEED] >=
+ table_context->od_settings_min[OD8_SETTING_FAN_MIN_SPEED])) {
+ od8_settings->od8_settings_array[OD8_SETTING_FAN_MIN_SPEED].feature_id =
+ OD8_FAN_SPEED_MIN;
+ od8_settings->od8_settings_array[OD8_SETTING_FAN_MIN_SPEED].default_value =
+ od_table->FanMinimumPwm * smc_pptable->FanMaximumRpm / 100;
+ }
+ }
+
+ if (smu_feature_is_enabled(smu, FEATURE_THERMAL_BIT)) {
+ if (table_context->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_TEMPERATURE_FAN] &&
+ table_context->od_settings_min[OD8_SETTING_FAN_TARGET_TEMP] > 0 &&
+ table_context->od_settings_max[OD8_SETTING_FAN_TARGET_TEMP] > 0 &&
+ (table_context->od_settings_max[OD8_SETTING_FAN_TARGET_TEMP] >=
+ table_context->od_settings_min[OD8_SETTING_FAN_TARGET_TEMP])) {
+ od8_settings->od8_settings_array[OD8_SETTING_FAN_TARGET_TEMP].feature_id =
+ OD8_TEMPERATURE_FAN;
+ od8_settings->od8_settings_array[OD8_SETTING_FAN_TARGET_TEMP].default_value =
+ od_table->FanTargetTemperature;
+ }
+
+ if (table_context->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_TEMPERATURE_SYSTEM] &&
+ table_context->od_settings_min[OD8_SETTING_OPERATING_TEMP_MAX] > 0 &&
+ table_context->od_settings_max[OD8_SETTING_OPERATING_TEMP_MAX] > 0 &&
+ (table_context->od_settings_max[OD8_SETTING_OPERATING_TEMP_MAX] >=
+ table_context->od_settings_min[OD8_SETTING_OPERATING_TEMP_MAX])) {
+ od8_settings->od8_settings_array[OD8_SETTING_OPERATING_TEMP_MAX].feature_id =
+ OD8_TEMPERATURE_SYSTEM;
+ od8_settings->od8_settings_array[OD8_SETTING_OPERATING_TEMP_MAX].default_value =
+ od_table->MaxOpTemp;
+ }
+ }
+
+ for (i = 0; i < OD8_SETTING_COUNT; i++) {
+ if (od8_settings->od8_settings_array[i].feature_id) {
+ od8_settings->od8_settings_array[i].min_value =
+ table_context->od_settings_min[i];
+ od8_settings->od8_settings_array[i].max_value =
+ table_context->od_settings_max[i];
+ od8_settings->od8_settings_array[i].current_value =
+ od8_settings->od8_settings_array[i].default_value;
+ } else {
+ od8_settings->od8_settings_array[i].min_value = 0;
+ od8_settings->od8_settings_array[i].max_value = 0;
+ od8_settings->od8_settings_array[i].current_value = 0;
+ }
+ }
+
+ return 0;
+}
+
+static int vega20_get_od_percentage(struct smu_context *smu,
+ enum pp_clock_type type)
+{
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+ struct vega20_dpm_table *dpm_table = NULL;
+ struct vega20_dpm_table *golden_table = NULL;
+ struct vega20_single_dpm_table *single_dpm_table;
+ struct vega20_single_dpm_table *golden_dpm_table;
+ int value, golden_value;
+
+ dpm_table = smu_dpm->dpm_context;
+ golden_table = smu_dpm->golden_dpm_context;
+
+ switch (type) {
+ case OD_SCLK:
+ single_dpm_table = &(dpm_table->gfx_table);
+ golden_dpm_table = &(golden_table->gfx_table);
+ break;
+ case OD_MCLK:
+ single_dpm_table = &(dpm_table->mem_table);
+ golden_dpm_table = &(golden_table->mem_table);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ value = single_dpm_table->dpm_levels[single_dpm_table->count - 1].value;
+ golden_value = golden_dpm_table->dpm_levels[golden_dpm_table->count - 1].value;
+
+ value -= golden_value;
+ value = DIV_ROUND_UP(value * 100, golden_value);
+
+ return value;
+}
+
+static int
+vega20_get_profiling_clk_mask(struct smu_context *smu,
+ enum amd_dpm_forced_level level,
+ uint32_t *sclk_mask,
+ uint32_t *mclk_mask,
+ uint32_t *soc_mask)
+{
+ struct vega20_dpm_table *dpm_table = (struct vega20_dpm_table *)smu->smu_dpm.dpm_context;
+ struct vega20_single_dpm_table *gfx_dpm_table;
+ struct vega20_single_dpm_table *mem_dpm_table;
+ struct vega20_single_dpm_table *soc_dpm_table;
+
+ if (!smu->smu_dpm.dpm_context)
+ return -EINVAL;
+
+ gfx_dpm_table = &dpm_table->gfx_table;
+ mem_dpm_table = &dpm_table->mem_table;
+ soc_dpm_table = &dpm_table->soc_table;
+
+ *sclk_mask = 0;
+ *mclk_mask = 0;
+ *soc_mask = 0;
+
+ if (gfx_dpm_table->count > VEGA20_UMD_PSTATE_GFXCLK_LEVEL &&
+ mem_dpm_table->count > VEGA20_UMD_PSTATE_MCLK_LEVEL &&
+ soc_dpm_table->count > VEGA20_UMD_PSTATE_SOCCLK_LEVEL) {
+ *sclk_mask = VEGA20_UMD_PSTATE_GFXCLK_LEVEL;
+ *mclk_mask = VEGA20_UMD_PSTATE_MCLK_LEVEL;
+ *soc_mask = VEGA20_UMD_PSTATE_SOCCLK_LEVEL;
+ }
+
+ if (level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK) {
+ *sclk_mask = 0;
+ } else if (level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK) {
+ *mclk_mask = 0;
+ } else if (level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ *sclk_mask = gfx_dpm_table->count - 1;
+ *mclk_mask = mem_dpm_table->count - 1;
+ *soc_mask = soc_dpm_table->count - 1;
+ }
+
+ return 0;
+}
+
+static int
+vega20_set_uclk_to_highest_dpm_level(struct smu_context *smu,
+ struct vega20_single_dpm_table *dpm_table)
+{
+ int ret = 0;
+ struct smu_dpm_context *smu_dpm_ctx = &(smu->smu_dpm);
+ if (!smu_dpm_ctx->dpm_context)
+ return -EINVAL;
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_UCLK_BIT)) {
+ if (dpm_table->count <= 0) {
+ pr_err("[%s] Dpm table has no entry!", __func__);
+ return -EINVAL;
+ }
+
+ if (dpm_table->count > NUM_UCLK_DPM_LEVELS) {
+ pr_err("[%s] Dpm table has too many entries!", __func__);
+ return -EINVAL;
+ }
+
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ ret = smu_send_smc_msg_with_param(smu,
+ SMU_MSG_SetHardMinByFreq,
+ (PPCLK_UCLK << 16) | dpm_table->dpm_state.hard_min_level);
+ if (ret) {
+ pr_err("[%s] Set hard min uclk failed!", __func__);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int vega20_pre_display_config_changed(struct smu_context *smu)
+{
+ int ret = 0;
+ struct vega20_dpm_table *dpm_table = smu->smu_dpm.dpm_context;
+
+ if (!smu->smu_dpm.dpm_context)
+ return -EINVAL;
+
+ smu_send_smc_msg_with_param(smu, SMU_MSG_NumOfDisplays, 0);
+ ret = vega20_set_uclk_to_highest_dpm_level(smu,
+ &dpm_table->mem_table);
+ if (ret)
+ pr_err("Failed to set uclk to highest dpm level");
+ return ret;
+}
+
+static int vega20_display_config_changed(struct smu_context *smu)
+{
+ int ret = 0;
+
+ if (!smu->funcs)
+ return -EINVAL;
+
+ if (!smu->smu_dpm.dpm_context ||
+ !smu->smu_table.tables ||
+ !smu->smu_table.tables[TABLE_WATERMARKS].cpu_addr)
+ return -EINVAL;
+
+ if ((smu->watermarks_bitmap & WATERMARKS_EXIST) &&
+ !(smu->watermarks_bitmap & WATERMARKS_LOADED)) {
+ ret = smu->funcs->write_watermarks_table(smu);
+ if (ret) {
+ pr_err("Failed to update WMTABLE!");
+ return ret;
+ }
+ smu->watermarks_bitmap |= WATERMARKS_LOADED;
+ }
+
+ if ((smu->watermarks_bitmap & WATERMARKS_EXIST) &&
+ smu_feature_is_supported(smu, FEATURE_DPM_DCEFCLK_BIT) &&
+ smu_feature_is_supported(smu, FEATURE_DPM_SOCCLK_BIT)) {
+ smu_send_smc_msg_with_param(smu,
+ SMU_MSG_NumOfDisplays,
+ smu->display_config->num_display);
+ }
+
+ return ret;
+}
+
+static int vega20_apply_clocks_adjust_rules(struct smu_context *smu)
+{
+ struct smu_dpm_context *smu_dpm_ctx = &(smu->smu_dpm);
+ struct vega20_dpm_table *dpm_ctx = (struct vega20_dpm_table *)(smu_dpm_ctx->dpm_context);
+ struct vega20_single_dpm_table *dpm_table;
+ bool vblank_too_short = false;
+ bool disable_mclk_switching;
+ uint32_t i, latency;
+
+ disable_mclk_switching = ((1 < smu->display_config->num_display) &&
+ !smu->display_config->multi_monitor_in_sync) || vblank_too_short;
+ latency = smu->display_config->dce_tolerable_mclk_in_active_latency;
+
+ /* gfxclk */
+ dpm_table = &(dpm_ctx->gfx_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
+ if (VEGA20_UMD_PSTATE_GFXCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_GFXCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_GFXCLK_LEVEL].value;
+ }
+
+ if (smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[0].value;
+ }
+
+ if (smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+
+ /* memclk */
+ dpm_table = &(dpm_ctx->mem_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
+ if (VEGA20_UMD_PSTATE_MCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_MCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_MCLK_LEVEL].value;
+ }
+
+ if (smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[0].value;
+ }
+
+ if (smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+
+ /* honour DAL's UCLK Hardmin */
+ if (dpm_table->dpm_state.hard_min_level < (smu->display_config->min_mem_set_clock / 100))
+ dpm_table->dpm_state.hard_min_level = smu->display_config->min_mem_set_clock / 100;
+
+ /* Hardmin is dependent on displayconfig */
+ if (disable_mclk_switching) {
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ for (i = 0; i < smu_dpm_ctx->mclk_latency_table->count - 1; i++) {
+ if (smu_dpm_ctx->mclk_latency_table->entries[i].latency <= latency) {
+ if (dpm_table->dpm_levels[i].value >= (smu->display_config->min_mem_set_clock / 100)) {
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[i].value;
+ break;
+ }
+ }
+ }
+ }
+
+ if (smu->display_config->nb_pstate_switch_disable)
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
+ /* vclk */
+ dpm_table = &(dpm_ctx->vclk_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
+ if (VEGA20_UMD_PSTATE_UVDCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_UVDCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_UVDCLK_LEVEL].value;
+ }
+
+ if (smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+
+ /* dclk */
+ dpm_table = &(dpm_ctx->dclk_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
+ if (VEGA20_UMD_PSTATE_UVDCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_UVDCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_UVDCLK_LEVEL].value;
+ }
+
+ if (smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+
+ /* socclk */
+ dpm_table = &(dpm_ctx->soc_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
+ if (VEGA20_UMD_PSTATE_SOCCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_SOCCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_SOCCLK_LEVEL].value;
+ }
+
+ if (smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+
+ /* eclk */
+ dpm_table = &(dpm_ctx->eclk_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
+ if (VEGA20_UMD_PSTATE_VCEMCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_VCEMCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_VCEMCLK_LEVEL].value;
+ }
+
+ if (smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+ return 0;
+}
+
+static int
+vega20_notify_smc_dispaly_config(struct smu_context *smu)
+{
+ struct vega20_dpm_table *dpm_table = smu->smu_dpm.dpm_context;
+ struct vega20_single_dpm_table *memtable = &dpm_table->mem_table;
+ struct smu_clocks min_clocks = {0};
+ struct pp_display_clock_request clock_req;
+ int ret = 0;
+
+ min_clocks.dcef_clock = smu->display_config->min_dcef_set_clk;
+ min_clocks.dcef_clock_in_sr = smu->display_config->min_dcef_deep_sleep_set_clk;
+ min_clocks.memory_clock = smu->display_config->min_mem_set_clock;
+
+ if (smu_feature_is_supported(smu, FEATURE_DPM_DCEFCLK_BIT)) {
+ clock_req.clock_type = amd_pp_dcef_clock;
+ clock_req.clock_freq_in_khz = min_clocks.dcef_clock * 10;
+ if (!smu->funcs->display_clock_voltage_request(smu, &clock_req)) {
+ if (smu_feature_is_supported(smu, FEATURE_DS_DCEFCLK_BIT)) {
+ ret = smu_send_smc_msg_with_param(smu,
+ SMU_MSG_SetMinDeepSleepDcefclk,
+ min_clocks.dcef_clock_in_sr/100);
+ if (ret) {
+ pr_err("Attempt to set divider for DCEFCLK Failed!");
+ return ret;
+ }
+ }
+ } else {
+ pr_info("Attempt to set Hard Min for DCEFCLK Failed!");
+ }
+ }
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_UCLK_BIT)) {
+ memtable->dpm_state.hard_min_level = min_clocks.memory_clock/100;
+ ret = smu_send_smc_msg_with_param(smu,
+ SMU_MSG_SetHardMinByFreq,
+ (PPCLK_UCLK << 16) | memtable->dpm_state.hard_min_level);
+ if (ret) {
+ pr_err("[%s] Set hard min uclk failed!", __func__);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static uint32_t vega20_find_lowest_dpm_level(struct vega20_single_dpm_table *table)
+{
+ uint32_t i;
+
+ for (i = 0; i < table->count; i++) {
+ if (table->dpm_levels[i].enabled)
+ break;
+ }
+ if (i >= table->count) {
+ i = 0;
+ table->dpm_levels[i].enabled = true;
+ }
+
+ return i;
+}
+
+static uint32_t vega20_find_highest_dpm_level(struct vega20_single_dpm_table *table)
+{
+ int i = 0;
+
+ if (!table) {
+ pr_err("[%s] DPM Table does not exist!", __func__);
+ return 0;
+ }
+ if (table->count <= 0) {
+ pr_err("[%s] DPM Table has no entry!", __func__);
+ return 0;
+ }
+ if (table->count > MAX_REGULAR_DPM_NUMBER) {
+ pr_err("[%s] DPM Table has too many entries!", __func__);
+ return MAX_REGULAR_DPM_NUMBER - 1;
+ }
+
+ for (i = table->count - 1; i >= 0; i--) {
+ if (table->dpm_levels[i].enabled)
+ break;
+ }
+ if (i < 0) {
+ i = 0;
+ table->dpm_levels[i].enabled = true;
+ }
+
+ return i;
+}
+
+static int vega20_force_dpm_limit_value(struct smu_context *smu, bool highest)
+{
+ uint32_t soft_level;
+ int ret = 0;
+ struct vega20_dpm_table *dpm_table =
+ (struct vega20_dpm_table *)smu->smu_dpm.dpm_context;
+
+ if (highest)
+ soft_level = vega20_find_highest_dpm_level(&(dpm_table->gfx_table));
+ else
+ soft_level = vega20_find_lowest_dpm_level(&(dpm_table->gfx_table));
+
+ dpm_table->gfx_table.dpm_state.soft_min_level =
+ dpm_table->gfx_table.dpm_state.soft_max_level =
+ dpm_table->gfx_table.dpm_levels[soft_level].value;
+
+ if (highest)
+ soft_level = vega20_find_highest_dpm_level(&(dpm_table->mem_table));
+ else
+ soft_level = vega20_find_lowest_dpm_level(&(dpm_table->mem_table));
+
+ dpm_table->mem_table.dpm_state.soft_min_level =
+ dpm_table->mem_table.dpm_state.soft_max_level =
+ dpm_table->mem_table.dpm_levels[soft_level].value;
+
+ if (highest)
+ soft_level = vega20_find_highest_dpm_level(&(dpm_table->soc_table));
+ else
+ soft_level = vega20_find_lowest_dpm_level(&(dpm_table->soc_table));
+
+ dpm_table->soc_table.dpm_state.soft_min_level =
+ dpm_table->soc_table.dpm_state.soft_max_level =
+ dpm_table->soc_table.dpm_levels[soft_level].value;
+
+ ret = vega20_upload_dpm_level(smu, false, 0xFFFFFFFF);
+ if (ret) {
+ pr_err("Failed to upload boot level to %s!\n",
+ highest ? "highest" : "lowest");
+ return ret;
+ }
+
+ ret = vega20_upload_dpm_level(smu, true, 0xFFFFFFFF);
+ if (ret) {
+ pr_err("Failed to upload dpm max level to %s!\n!",
+ highest ? "highest" : "lowest");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int vega20_unforce_dpm_levels(struct smu_context *smu)
+{
+ uint32_t soft_min_level, soft_max_level;
+ int ret = 0;
+ struct vega20_dpm_table *dpm_table =
+ (struct vega20_dpm_table *)smu->smu_dpm.dpm_context;
+
+ soft_min_level = vega20_find_lowest_dpm_level(&(dpm_table->gfx_table));
+ soft_max_level = vega20_find_highest_dpm_level(&(dpm_table->gfx_table));
+ dpm_table->gfx_table.dpm_state.soft_min_level =
+ dpm_table->gfx_table.dpm_levels[soft_min_level].value;
+ dpm_table->gfx_table.dpm_state.soft_max_level =
+ dpm_table->gfx_table.dpm_levels[soft_max_level].value;
+
+ soft_min_level = vega20_find_lowest_dpm_level(&(dpm_table->mem_table));
+ soft_max_level = vega20_find_highest_dpm_level(&(dpm_table->mem_table));
+ dpm_table->mem_table.dpm_state.soft_min_level =
+ dpm_table->gfx_table.dpm_levels[soft_min_level].value;
+ dpm_table->mem_table.dpm_state.soft_max_level =
+ dpm_table->gfx_table.dpm_levels[soft_max_level].value;
+
+ soft_min_level = vega20_find_lowest_dpm_level(&(dpm_table->soc_table));
+ soft_max_level = vega20_find_highest_dpm_level(&(dpm_table->soc_table));
+ dpm_table->soc_table.dpm_state.soft_min_level =
+ dpm_table->soc_table.dpm_levels[soft_min_level].value;
+ dpm_table->soc_table.dpm_state.soft_max_level =
+ dpm_table->soc_table.dpm_levels[soft_max_level].value;
+
+ ret = smu_upload_dpm_level(smu, false, 0xFFFFFFFF);
+ if (ret) {
+ pr_err("Failed to upload DPM Bootup Levels!");
+ return ret;
+ }
+
+ ret = smu_upload_dpm_level(smu, true, 0xFFFFFFFF);
+ if (ret) {
+ pr_err("Failed to upload DPM Max Levels!");
+ return ret;
+ }
+
+ return ret;
+}
+
+static enum amd_dpm_forced_level vega20_get_performance_level(struct smu_context *smu)
+{
+ struct smu_dpm_context *smu_dpm_ctx = &(smu->smu_dpm);
+ if (!smu_dpm_ctx->dpm_context)
+ return -EINVAL;
+
+ if (smu_dpm_ctx->dpm_level != smu_dpm_ctx->saved_dpm_level) {
+ mutex_lock(&(smu->mutex));
+ smu_dpm_ctx->saved_dpm_level = smu_dpm_ctx->dpm_level;
+ mutex_unlock(&(smu->mutex));
+ }
+ return smu_dpm_ctx->dpm_level;
+}
+
+static int
+vega20_force_performance_level(struct smu_context *smu, enum amd_dpm_forced_level level)
+{
+ int ret = 0;
+ int i;
+ struct smu_dpm_context *smu_dpm_ctx = &(smu->smu_dpm);
+
+ if (!smu_dpm_ctx->dpm_context)
+ return -EINVAL;
+
+ for (i = 0; i < smu->adev->num_ip_blocks; i++) {
+ if (smu->adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_SMC)
+ break;
+ }
+
+ mutex_lock(&smu->mutex);
+
+ smu->adev->ip_blocks[i].version->funcs->enable_umd_pstate(smu, &level);
+ ret = smu_handle_task(smu, level,
+ AMD_PP_TASK_READJUST_POWER_STATE);
+
+ mutex_unlock(&smu->mutex);
+
+ return ret;
+}
+
+static int vega20_update_specified_od8_value(struct smu_context *smu,
+ uint32_t index,
+ uint32_t value)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ OverDriveTable_t *od_table =
+ (OverDriveTable_t *)(table_context->overdrive_table);
+ struct vega20_od8_settings *od8_settings =
+ (struct vega20_od8_settings *)table_context->od8_settings;
+
+ switch (index) {
+ case OD8_SETTING_GFXCLK_FMIN:
+ od_table->GfxclkFmin = (uint16_t)value;
+ break;
+
+ case OD8_SETTING_GFXCLK_FMAX:
+ if (value < od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].min_value ||
+ value > od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].max_value)
+ return -EINVAL;
+ od_table->GfxclkFmax = (uint16_t)value;
+ break;
+
+ case OD8_SETTING_GFXCLK_FREQ1:
+ od_table->GfxclkFreq1 = (uint16_t)value;
+ break;
+
+ case OD8_SETTING_GFXCLK_VOLTAGE1:
+ od_table->GfxclkVolt1 = (uint16_t)value;
+ break;
+
+ case OD8_SETTING_GFXCLK_FREQ2:
+ od_table->GfxclkFreq2 = (uint16_t)value;
+ break;
+
+ case OD8_SETTING_GFXCLK_VOLTAGE2:
+ od_table->GfxclkVolt2 = (uint16_t)value;
+ break;
+
+ case OD8_SETTING_GFXCLK_FREQ3:
+ od_table->GfxclkFreq3 = (uint16_t)value;
+ break;
+
+ case OD8_SETTING_GFXCLK_VOLTAGE3:
+ od_table->GfxclkVolt3 = (uint16_t)value;
+ break;
+
+ case OD8_SETTING_UCLK_FMAX:
+ if (value < od8_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].min_value ||
+ value > od8_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].max_value)
+ return -EINVAL;
+ od_table->UclkFmax = (uint16_t)value;
+ break;
+
+ case OD8_SETTING_POWER_PERCENTAGE:
+ od_table->OverDrivePct = (int16_t)value;
+ break;
+
+ case OD8_SETTING_FAN_ACOUSTIC_LIMIT:
+ od_table->FanMaximumRpm = (uint16_t)value;
+ break;
+
+ case OD8_SETTING_FAN_MIN_SPEED:
+ od_table->FanMinimumPwm = (uint16_t)value;
+ break;
+
+ case OD8_SETTING_FAN_TARGET_TEMP:
+ od_table->FanTargetTemperature = (uint16_t)value;
+ break;
+
+ case OD8_SETTING_OPERATING_TEMP_MAX:
+ od_table->MaxOpTemp = (uint16_t)value;
+ break;
+ }
+
+ return 0;
+}
+
+static int vega20_set_od_percentage(struct smu_context *smu,
+ enum pp_clock_type type,
+ uint32_t value)
+{
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+ struct vega20_dpm_table *dpm_table = NULL;
+ struct vega20_dpm_table *golden_table = NULL;
+ struct vega20_single_dpm_table *single_dpm_table;
+ struct vega20_single_dpm_table *golden_dpm_table;
+ uint32_t od_clk, index;
+ int ret = 0;
+ int feature_enabled;
+ PPCLK_e clk_id;
+
+ mutex_lock(&(smu->mutex));
+
+ dpm_table = smu_dpm->dpm_context;
+ golden_table = smu_dpm->golden_dpm_context;
+
+ switch (type) {
+ case OD_SCLK:
+ single_dpm_table = &(dpm_table->gfx_table);
+ golden_dpm_table = &(golden_table->gfx_table);
+ feature_enabled = smu_feature_is_enabled(smu, FEATURE_DPM_GFXCLK_BIT);
+ clk_id = PPCLK_GFXCLK;
+ index = OD8_SETTING_GFXCLK_FMAX;
+ break;
+ case OD_MCLK:
+ single_dpm_table = &(dpm_table->mem_table);
+ golden_dpm_table = &(golden_table->mem_table);
+ feature_enabled = smu_feature_is_enabled(smu, FEATURE_DPM_UCLK_BIT);
+ clk_id = PPCLK_UCLK;
+ index = OD8_SETTING_UCLK_FMAX;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret)
+ goto set_od_failed;
+
+ od_clk = golden_dpm_table->dpm_levels[golden_dpm_table->count - 1].value * value;
+ od_clk /= 100;
+ od_clk += golden_dpm_table->dpm_levels[golden_dpm_table->count - 1].value;
+
+ ret = smu_update_od8_settings(smu, index, od_clk);
+ if (ret) {
+ pr_err("[Setoverdrive] failed to set od clk!\n");
+ goto set_od_failed;
+ }
+
+ if (feature_enabled) {
+ ret = vega20_set_single_dpm_table(smu, single_dpm_table,
+ clk_id);
+ if (ret) {
+ pr_err("[Setoverdrive] failed to refresh dpm table!\n");
+ goto set_od_failed;
+ }
+ } else {
+ single_dpm_table->count = 1;
+ single_dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.gfxclk / 100;
+ }
+
+ ret = smu_handle_task(smu, smu_dpm->dpm_level,
+ AMD_PP_TASK_READJUST_POWER_STATE);
+
+set_od_failed:
+ mutex_unlock(&(smu->mutex));
+
+ return ret;
+}
+
+static int vega20_odn_edit_dpm_table(struct smu_context *smu,
+ enum PP_OD_DPM_TABLE_COMMAND type,
+ long *input, uint32_t size)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ OverDriveTable_t *od_table =
+ (OverDriveTable_t *)(table_context->overdrive_table);
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+ struct vega20_dpm_table *dpm_table = NULL;
+ struct vega20_single_dpm_table *single_dpm_table;
+ struct vega20_od8_settings *od8_settings =
+ (struct vega20_od8_settings *)table_context->od8_settings;
+ struct pp_clock_levels_with_latency clocks;
+ int32_t input_index, input_clk, input_vol, i;
+ int od8_id;
+ int ret = 0;
+
+ dpm_table = smu_dpm->dpm_context;
+
+ if (!input) {
+ pr_warn("NULL user input for clock and voltage\n");
+ return -EINVAL;
+ }
+
+ switch (type) {
+ case PP_OD_EDIT_SCLK_VDDC_TABLE:
+ if (!(od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMIN].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].feature_id)) {
+ pr_info("Sclk min/max frequency overdrive not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ for (i = 0; i < size; i += 2) {
+ if (i + 2 > size) {
+ pr_info("invalid number of input parameters %d\n", size);
+ return -EINVAL;
+ }
+
+ input_index = input[i];
+ input_clk = input[i + 1];
+
+ if (input_index != 0 && input_index != 1) {
+ pr_info("Invalid index %d\n", input_index);
+ pr_info("Support min/max sclk frequency settingonly which index by 0/1\n");
+ return -EINVAL;
+ }
+
+ if (input_clk < od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMIN].min_value ||
+ input_clk > od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].max_value) {
+ pr_info("clock freq %d is not within allowed range [%d - %d]\n",
+ input_clk,
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMIN].min_value,
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].max_value);
+ return -EINVAL;
+ }
+
+ if (input_index == 0 && od_table->GfxclkFmin != input_clk) {
+ od_table->GfxclkFmin = input_clk;
+ table_context->od_gfxclk_update = true;
+ } else if (input_index == 1 && od_table->GfxclkFmax != input_clk) {
+ od_table->GfxclkFmax = input_clk;
+ table_context->od_gfxclk_update = true;
+ }
+ }
+
+ break;
+
+ case PP_OD_EDIT_MCLK_VDDC_TABLE:
+ if (!od8_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].feature_id) {
+ pr_info("Mclk max frequency overdrive not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ single_dpm_table = &(dpm_table->mem_table);
+ ret = vega20_get_clk_table(smu, &clocks, single_dpm_table);
+ if (ret) {
+ pr_err("Attempt to get memory clk levels Failed!");
+ return ret;
+ }
+
+ for (i = 0; i < size; i += 2) {
+ if (i + 2 > size) {
+ pr_info("invalid number of input parameters %d\n",
+ size);
+ return -EINVAL;
+ }
+
+ input_index = input[i];
+ input_clk = input[i + 1];
+
+ if (input_index != 1) {
+ pr_info("Invalid index %d\n", input_index);
+ pr_info("Support max Mclk frequency setting only which index by 1\n");
+ return -EINVAL;
+ }
+
+ if (input_clk < clocks.data[0].clocks_in_khz / 1000 ||
+ input_clk > od8_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].max_value) {
+ pr_info("clock freq %d is not within allowed range [%d - %d]\n",
+ input_clk,
+ clocks.data[0].clocks_in_khz / 1000,
+ od8_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].max_value);
+ return -EINVAL;
+ }
+
+ if (input_index == 1 && od_table->UclkFmax != input_clk) {
+ table_context->od_gfxclk_update = true;
+ od_table->UclkFmax = input_clk;
+ }
+ }
+
+ break;
+
+ case PP_OD_EDIT_VDDC_CURVE:
+ if (!(od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ1].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ2].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ3].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].feature_id &&
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].feature_id)) {
+ pr_info("Voltage curve calibrate not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ for (i = 0; i < size; i += 3) {
+ if (i + 3 > size) {
+ pr_info("invalid number of input parameters %d\n",
+ size);
+ return -EINVAL;
+ }
+
+ input_index = input[i];
+ input_clk = input[i + 1];
+ input_vol = input[i + 2];
+
+ if (input_index > 2) {
+ pr_info("Setting for point %d is not supported\n",
+ input_index + 1);
+ pr_info("Three supported points index by 0, 1, 2\n");
+ return -EINVAL;
+ }
+
+ od8_id = OD8_SETTING_GFXCLK_FREQ1 + 2 * input_index;
+ if (input_clk < od8_settings->od8_settings_array[od8_id].min_value ||
+ input_clk > od8_settings->od8_settings_array[od8_id].max_value) {
+ pr_info("clock freq %d is not within allowed range [%d - %d]\n",
+ input_clk,
+ od8_settings->od8_settings_array[od8_id].min_value,
+ od8_settings->od8_settings_array[od8_id].max_value);
+ return -EINVAL;
+ }
+
+ od8_id = OD8_SETTING_GFXCLK_VOLTAGE1 + 2 * input_index;
+ if (input_vol < od8_settings->od8_settings_array[od8_id].min_value ||
+ input_vol > od8_settings->od8_settings_array[od8_id].max_value) {
+ pr_info("clock voltage %d is not within allowed range [%d- %d]\n",
+ input_vol,
+ od8_settings->od8_settings_array[od8_id].min_value,
+ od8_settings->od8_settings_array[od8_id].max_value);
+ return -EINVAL;
+ }
+
+ switch (input_index) {
+ case 0:
+ od_table->GfxclkFreq1 = input_clk;
+ od_table->GfxclkVolt1 = input_vol * VOLTAGE_SCALE;
+ break;
+ case 1:
+ od_table->GfxclkFreq2 = input_clk;
+ od_table->GfxclkVolt2 = input_vol * VOLTAGE_SCALE;
+ break;
+ case 2:
+ od_table->GfxclkFreq3 = input_clk;
+ od_table->GfxclkVolt3 = input_vol * VOLTAGE_SCALE;
+ break;
+ }
+ }
+
+ break;
+
+ case PP_OD_RESTORE_DEFAULT_TABLE:
+ ret = smu_update_table(smu, TABLE_OVERDRIVE, table_context->overdrive_table, false);
+ if (ret) {
+ pr_err("Failed to export over drive table!\n");
+ return ret;
+ }
+
+ break;
+
+ case PP_OD_COMMIT_DPM_TABLE:
+ ret = smu_update_table(smu, TABLE_OVERDRIVE, table_context->overdrive_table, true);
+ if (ret) {
+ pr_err("Failed to import over drive table!\n");
+ return ret;
+ }
+
+ /* retrieve updated gfxclk table */
+ if (table_context->od_gfxclk_update) {
+ table_context->od_gfxclk_update = false;
+ single_dpm_table = &(dpm_table->gfx_table);
+
+ if (smu_feature_is_enabled(smu, FEATURE_DPM_GFXCLK_BIT)) {
+ ret = vega20_set_single_dpm_table(smu, single_dpm_table,
+ PPCLK_GFXCLK);
+ if (ret) {
+ pr_err("[Setoverdrive] failed to refresh dpm table!\n");
+ return ret;
+ }
+ } else {
+ single_dpm_table->count = 1;
+ single_dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.gfxclk / 100;
+ }
+ }
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (type == PP_OD_COMMIT_DPM_TABLE) {
+ mutex_lock(&(smu->mutex));
+ ret = smu_handle_task(smu, smu_dpm->dpm_level,
+ AMD_PP_TASK_READJUST_POWER_STATE);
+ mutex_unlock(&(smu->mutex));
+ }
+
+ return ret;
+}
+
+static const struct pptable_funcs vega20_ppt_funcs = {
+ .alloc_dpm_context = vega20_allocate_dpm_context,
+ .store_powerplay_table = vega20_store_powerplay_table,
+ .check_powerplay_table = vega20_check_powerplay_table,
+ .append_powerplay_table = vega20_append_powerplay_table,
+ .get_smu_msg_index = vega20_get_smu_msg_index,
+ .run_afll_btc = vega20_run_btc_afll,
+ .get_unallowed_feature_mask = vega20_get_unallowed_feature_mask,
+ .get_current_power_state = vega20_get_current_power_state,
+ .set_default_dpm_table = vega20_set_default_dpm_table,
+ .set_power_state = NULL,
+ .populate_umd_state_clk = vega20_populate_umd_state_clk,
+ .print_clk_levels = vega20_print_clk_levels,
+ .force_clk_levels = vega20_force_clk_levels,
+ .get_clock_by_type_with_latency = vega20_get_clock_by_type_with_latency,
+ .set_default_od8_settings = vega20_set_default_od8_setttings,
+ .get_od_percentage = vega20_get_od_percentage,
+ .get_performance_level = vega20_get_performance_level,
+ .force_performance_level = vega20_force_performance_level,
+ .update_specified_od8_value = vega20_update_specified_od8_value,
+ .set_od_percentage = vega20_set_od_percentage,
+ .od_edit_dpm_table = vega20_odn_edit_dpm_table,
+ .pre_display_config_changed = vega20_pre_display_config_changed,
+ .display_config_changed = vega20_display_config_changed,
+ .apply_clocks_adjust_rules = vega20_apply_clocks_adjust_rules,
+ .notify_smc_dispaly_config = vega20_notify_smc_dispaly_config,
+ .force_dpm_limit_value = vega20_force_dpm_limit_value,
+ .unforce_dpm_levels = vega20_unforce_dpm_levels,
+ .upload_dpm_level = vega20_upload_dpm_level,
+ .get_profiling_clk_mask = vega20_get_profiling_clk_mask,
+};
+
+void vega20_set_ppt_funcs(struct smu_context *smu)
+{
+ smu->ppt_funcs = &vega20_ppt_funcs;
+ smu->smc_if_version = SMU11_DRIVER_IF_VERSION;
+}
diff --git a/drivers/gpu/drm/amd/powerplay/vega20_ppt.h b/drivers/gpu/drm/amd/powerplay/vega20_ppt.h
new file mode 100644
index 000000000000..5a0d2af63173
--- /dev/null
+++ b/drivers/gpu/drm/amd/powerplay/vega20_ppt.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#ifndef __VEGA20_PPT_H__
+#define __VEGA20_PPT_H__
+
+#define VEGA20_UMD_PSTATE_GFXCLK_LEVEL 0x3
+#define VEGA20_UMD_PSTATE_SOCCLK_LEVEL 0x3
+#define VEGA20_UMD_PSTATE_MCLK_LEVEL 0x2
+#define VEGA20_UMD_PSTATE_UVDCLK_LEVEL 0x3
+#define VEGA20_UMD_PSTATE_VCEMCLK_LEVEL 0x3
+
+#define MAX_REGULAR_DPM_NUMBER 16
+#define MAX_PCIE_CONF 2
+
+#define VOLTAGE_SCALE 4
+#define AVFS_CURVE 0
+#define OD8_HOTCURVE_TEMPERATURE 85
+
+struct vega20_dpm_level {
+ bool enabled;
+ uint32_t value;
+ uint32_t param1;
+};
+
+struct vega20_dpm_state {
+ uint32_t soft_min_level;
+ uint32_t soft_max_level;
+ uint32_t hard_min_level;
+ uint32_t hard_max_level;
+};
+
+struct vega20_single_dpm_table {
+ uint32_t count;
+ struct vega20_dpm_state dpm_state;
+ struct vega20_dpm_level dpm_levels[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct vega20_pcie_table {
+ uint16_t count;
+ uint8_t pcie_gen[MAX_PCIE_CONF];
+ uint8_t pcie_lane[MAX_PCIE_CONF];
+ uint32_t lclk[MAX_PCIE_CONF];
+};
+
+struct vega20_dpm_table {
+ struct vega20_single_dpm_table soc_table;
+ struct vega20_single_dpm_table gfx_table;
+ struct vega20_single_dpm_table mem_table;
+ struct vega20_single_dpm_table eclk_table;
+ struct vega20_single_dpm_table vclk_table;
+ struct vega20_single_dpm_table dclk_table;
+ struct vega20_single_dpm_table dcef_table;
+ struct vega20_single_dpm_table pixel_table;
+ struct vega20_single_dpm_table display_table;
+ struct vega20_single_dpm_table phy_table;
+ struct vega20_single_dpm_table fclk_table;
+ struct vega20_pcie_table pcie_table;
+};
+
+enum OD8_FEATURE_ID
+{
+ OD8_GFXCLK_LIMITS = 1 << 0,
+ OD8_GFXCLK_CURVE = 1 << 1,
+ OD8_UCLK_MAX = 1 << 2,
+ OD8_POWER_LIMIT = 1 << 3,
+ OD8_ACOUSTIC_LIMIT_SCLK = 1 << 4, //FanMaximumRpm
+ OD8_FAN_SPEED_MIN = 1 << 5, //FanMinimumPwm
+ OD8_TEMPERATURE_FAN = 1 << 6, //FanTargetTemperature
+ OD8_TEMPERATURE_SYSTEM = 1 << 7, //MaxOpTemp
+ OD8_MEMORY_TIMING_TUNE = 1 << 8,
+ OD8_FAN_ZERO_RPM_CONTROL = 1 << 9
+};
+
+enum OD8_SETTING_ID
+{
+ OD8_SETTING_GFXCLK_FMIN = 0,
+ OD8_SETTING_GFXCLK_FMAX,
+ OD8_SETTING_GFXCLK_FREQ1,
+ OD8_SETTING_GFXCLK_VOLTAGE1,
+ OD8_SETTING_GFXCLK_FREQ2,
+ OD8_SETTING_GFXCLK_VOLTAGE2,
+ OD8_SETTING_GFXCLK_FREQ3,
+ OD8_SETTING_GFXCLK_VOLTAGE3,
+ OD8_SETTING_UCLK_FMAX,
+ OD8_SETTING_POWER_PERCENTAGE,
+ OD8_SETTING_FAN_ACOUSTIC_LIMIT,
+ OD8_SETTING_FAN_MIN_SPEED,
+ OD8_SETTING_FAN_TARGET_TEMP,
+ OD8_SETTING_OPERATING_TEMP_MAX,
+ OD8_SETTING_AC_TIMING,
+ OD8_SETTING_FAN_ZERO_RPM_CONTROL,
+ OD8_SETTING_COUNT
+};
+
+struct vega20_od8_single_setting {
+ uint32_t feature_id;
+ int32_t min_value;
+ int32_t max_value;
+ int32_t current_value;
+ int32_t default_value;
+};
+
+struct vega20_od8_settings {
+ struct vega20_od8_single_setting od8_settings_array[OD8_SETTING_COUNT];
+};
+
+extern void vega20_set_ppt_funcs(struct smu_context *smu);
+
+#endif