Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/gpu/drm/amd/pm/powerplay/hwmgr/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 141 kB image not shown  

Quelle  vega20_hwmgr.c   Sprache: C

 
/*
 * 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 <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>

#include "hwmgr.h"
#include "amd_powerplay.h"
#include "vega20_smumgr.h"
#include "hardwaremanager.h"
#include "ppatomfwctrl.h"
#include "atomfirmware.h"
#include "cgs_common.h"
#include "vega20_powertune.h"
#include "vega20_inc.h"
#include "pppcielanes.h"
#include "vega20_hwmgr.h"
#include "vega20_processpptables.h"
#include "vega20_pptable.h"
#include "vega20_thermal.h"
#include "vega20_ppsmc.h"
#include "pp_debug.h"
#include "amd_pcie_helpers.h"
#include "ppinterrupt.h"
#include "pp_overdriver.h"
#include "pp_thermal.h"
#include "soc15_common.h"
#include "vega20_baco.h"
#include "smuio/smuio_9_0_offset.h"
#include "smuio/smuio_9_0_sh_mask.h"
#include "nbio/nbio_7_4_sh_mask.h"

#define smnPCIE_LC_SPEED_CNTL   0x11140290
#define smnPCIE_LC_LINK_WIDTH_CNTL  0x11140288

#define LINK_WIDTH_MAX    6
#define LINK_SPEED_MAX    3
static const int link_width[] = {0, 1, 2, 4, 8, 12, 16};
static const int link_speed[] = {25, 50, 80, 160};

static void vega20_set_default_registry_data(struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data =
   (struct vega20_hwmgr *)(hwmgr->backend);

 data->gfxclk_average_alpha = PPVEGA20_VEGA20GFXCLKAVERAGEALPHA_DFLT;
 data->socclk_average_alpha = PPVEGA20_VEGA20SOCCLKAVERAGEALPHA_DFLT;
 data->uclk_average_alpha = PPVEGA20_VEGA20UCLKCLKAVERAGEALPHA_DFLT;
 data->gfx_activity_average_alpha = PPVEGA20_VEGA20GFXACTIVITYAVERAGEALPHA_DFLT;
 data->lowest_uclk_reserved_for_ulv = PPVEGA20_VEGA20LOWESTUCLKRESERVEDFORULV_DFLT;

 data->display_voltage_mode = PPVEGA20_VEGA20DISPLAYVOLTAGEMODE_DFLT;
 data->dcef_clk_quad_eqn_a = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
 data->dcef_clk_quad_eqn_b = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
 data->dcef_clk_quad_eqn_c = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
 data->disp_clk_quad_eqn_a = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
 data->disp_clk_quad_eqn_b = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
 data->disp_clk_quad_eqn_c = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
 data->pixel_clk_quad_eqn_a = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
 data->pixel_clk_quad_eqn_b = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
 data->pixel_clk_quad_eqn_c = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
 data->phy_clk_quad_eqn_a = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
 data->phy_clk_quad_eqn_b = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
 data->phy_clk_quad_eqn_c = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;

 /*
 * Disable the following features for now:
 *   GFXCLK DS
 *   SOCLK DS
 *   LCLK DS
 *   DCEFCLK DS
 *   FCLK DS
 *   MP1CLK DS
 *   MP0CLK DS
 */

 data->registry_data.disallowed_features = 0xE0041C00;
 /* ECC feature should be disabled on old SMUs */
 smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetSmuVersion, &hwmgr->smu_version);
 if (hwmgr->smu_version < 0x282100)
  data->registry_data.disallowed_features |= FEATURE_ECC_MASK;

 if (!(hwmgr->feature_mask & PP_PCIE_DPM_MASK))
  data->registry_data.disallowed_features |= FEATURE_DPM_LINK_MASK;

 if (!(hwmgr->feature_mask & PP_SCLK_DPM_MASK))
  data->registry_data.disallowed_features |= FEATURE_DPM_GFXCLK_MASK;

 if (!(hwmgr->feature_mask & PP_SOCCLK_DPM_MASK))
  data->registry_data.disallowed_features |= FEATURE_DPM_SOCCLK_MASK;

 if (!(hwmgr->feature_mask & PP_MCLK_DPM_MASK))
  data->registry_data.disallowed_features |= FEATURE_DPM_UCLK_MASK;

 if (!(hwmgr->feature_mask & PP_DCEFCLK_DPM_MASK))
  data->registry_data.disallowed_features |= FEATURE_DPM_DCEFCLK_MASK;

 if (!(hwmgr->feature_mask & PP_ULV_MASK))
  data->registry_data.disallowed_features |= FEATURE_ULV_MASK;

 if (!(hwmgr->feature_mask & PP_SCLK_DEEP_SLEEP_MASK))
  data->registry_data.disallowed_features |= FEATURE_DS_GFXCLK_MASK;

 data->registry_data.od_state_in_dc_support = 0;
 data->registry_data.thermal_support = 1;
 data->registry_data.skip_baco_hardware = 0;

 data->registry_data.log_avfs_param = 0;
 data->registry_data.sclk_throttle_low_notification = 1;
 data->registry_data.force_dpm_high = 0;
 data->registry_data.stable_pstate_sclk_dpm_percentage = 75;

 data->registry_data.didt_support = 0;
 if (data->registry_data.didt_support) {
  data->registry_data.didt_mode = 6;
  data->registry_data.sq_ramping_support = 1;
  data->registry_data.db_ramping_support = 0;
  data->registry_data.td_ramping_support = 0;
  data->registry_data.tcp_ramping_support = 0;
  data->registry_data.dbr_ramping_support = 0;
  data->registry_data.edc_didt_support = 1;
  data->registry_data.gc_didt_support = 0;
  data->registry_data.psm_didt_support = 0;
 }

 data->registry_data.pcie_lane_override = 0xff;
 data->registry_data.pcie_speed_override = 0xff;
 data->registry_data.pcie_clock_override = 0xffffffff;
 data->registry_data.regulator_hot_gpio_support = 1;
 data->registry_data.ac_dc_switch_gpio_support = 0;
 data->registry_data.quick_transition_support = 0;
 data->registry_data.zrpm_start_temp = 0xffff;
 data->registry_data.zrpm_stop_temp = 0xffff;
 data->registry_data.od8_feature_enable = 1;
 data->registry_data.disable_water_mark = 0;
 data->registry_data.disable_pp_tuning = 0;
 data->registry_data.disable_xlpp_tuning = 0;
 data->registry_data.disable_workload_policy = 0;
 data->registry_data.perf_ui_tuning_profile_turbo = 0x19190F0F;
 data->registry_data.perf_ui_tuning_profile_powerSave = 0x19191919;
 data->registry_data.perf_ui_tuning_profile_xl = 0x00000F0A;
 data->registry_data.force_workload_policy_mask = 0;
 data->registry_data.disable_3d_fs_detection = 0;
 data->registry_data.fps_support = 1;
 data->registry_data.disable_auto_wattman = 1;
 data->registry_data.auto_wattman_debug = 0;
 data->registry_data.auto_wattman_sample_period = 100;
 data->registry_data.fclk_gfxclk_ratio = 0;
 data->registry_data.auto_wattman_threshold = 50;
 data->registry_data.gfxoff_controlled_by_driver = 1;
 data->gfxoff_allowed = false;
 data->counter_gfxoff = 0;
 data->registry_data.pcie_dpm_key_disabled = !(hwmgr->feature_mask & PP_PCIE_DPM_MASK);
}

static int vega20_set_features_platform_caps(struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data =
   (struct vega20_hwmgr *)(hwmgr->backend);
 struct amdgpu_device *adev = hwmgr->adev;

 if (data->vddci_control == VEGA20_VOLTAGE_CONTROL_NONE)
  phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_ControlVDDCI);

 phm_cap_set(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_TablelessHardwareInterface);

 phm_cap_set(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_BACO);

 phm_cap_set(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_EnableSMU7ThermalManagement);

 if (adev->pg_flags & AMD_PG_SUPPORT_UVD)
  phm_cap_set(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_UVDPowerGating);

 if (adev->pg_flags & AMD_PG_SUPPORT_VCE)
  phm_cap_set(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_VCEPowerGating);

 phm_cap_set(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_UnTabledHardwareInterface);

 if (data->registry_data.od8_feature_enable)
  phm_cap_set(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_OD8inACSupport);

 phm_cap_set(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_ActivityReporting);
 phm_cap_set(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_FanSpeedInTableIsRPM);

 if (data->registry_data.od_state_in_dc_support) {
  if (data->registry_data.od8_feature_enable)
   phm_cap_set(hwmgr->platform_descriptor.platformCaps,
     PHM_PlatformCaps_OD8inDCSupport);
 }

 if (data->registry_data.thermal_support &&
     data->registry_data.fuzzy_fan_control_support &&
     hwmgr->thermal_controller.advanceFanControlParameters.usTMax)
  phm_cap_set(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_ODFuzzyFanControlSupport);

 phm_cap_set(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_DynamicPowerManagement);
 phm_cap_set(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_SMC);
 phm_cap_set(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_ThermalPolicyDelay);

 if (data->registry_data.force_dpm_high)
  phm_cap_set(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_ExclusiveModeAlwaysHigh);

 phm_cap_set(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_DynamicUVDState);

 if (data->registry_data.sclk_throttle_low_notification)
  phm_cap_set(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_SclkThrottleLowNotification);

 /* power tune caps */
 /* assume disabled */
 phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_PowerContainment);
 phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_DiDtSupport);
 phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_SQRamping);
 phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_DBRamping);
 phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_TDRamping);
 phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_TCPRamping);
 phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_DBRRamping);
 phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_DiDtEDCEnable);
 phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_GCEDC);
 phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_PSM);

 if (data->registry_data.didt_support) {
  phm_cap_set(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_DiDtSupport);
  if (data->registry_data.sq_ramping_support)
   phm_cap_set(hwmgr->platform_descriptor.platformCaps,
     PHM_PlatformCaps_SQRamping);
  if (data->registry_data.db_ramping_support)
   phm_cap_set(hwmgr->platform_descriptor.platformCaps,
     PHM_PlatformCaps_DBRamping);
  if (data->registry_data.td_ramping_support)
   phm_cap_set(hwmgr->platform_descriptor.platformCaps,
     PHM_PlatformCaps_TDRamping);
  if (data->registry_data.tcp_ramping_support)
   phm_cap_set(hwmgr->platform_descriptor.platformCaps,
     PHM_PlatformCaps_TCPRamping);
  if (data->registry_data.dbr_ramping_support)
   phm_cap_set(hwmgr->platform_descriptor.platformCaps,
     PHM_PlatformCaps_DBRRamping);
  if (data->registry_data.edc_didt_support)
   phm_cap_set(hwmgr->platform_descriptor.platformCaps,
     PHM_PlatformCaps_DiDtEDCEnable);
  if (data->registry_data.gc_didt_support)
   phm_cap_set(hwmgr->platform_descriptor.platformCaps,
     PHM_PlatformCaps_GCEDC);
  if (data->registry_data.psm_didt_support)
   phm_cap_set(hwmgr->platform_descriptor.platformCaps,
     PHM_PlatformCaps_PSM);
 }

 phm_cap_set(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_RegulatorHot);

 if (data->registry_data.ac_dc_switch_gpio_support) {
  phm_cap_set(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_AutomaticDCTransition);
  phm_cap_set(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_SMCtoPPLIBAcdcGpioScheme);
 }

 if (data->registry_data.quick_transition_support) {
  phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_AutomaticDCTransition);
  phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_SMCtoPPLIBAcdcGpioScheme);
  phm_cap_set(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_Falcon_QuickTransition);
 }

 if (data->lowest_uclk_reserved_for_ulv != PPVEGA20_VEGA20LOWESTUCLKRESERVEDFORULV_DFLT) {
  phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_LowestUclkReservedForUlv);
  if (data->lowest_uclk_reserved_for_ulv == 1)
   phm_cap_set(hwmgr->platform_descriptor.platformCaps,
     PHM_PlatformCaps_LowestUclkReservedForUlv);
 }

 if (data->registry_data.custom_fan_support)
  phm_cap_set(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_CustomFanControlSupport);

 return 0;
}

static int vega20_init_dpm_defaults(struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
 struct amdgpu_device *adev = hwmgr->adev;
 uint32_t top32, bottom32;
 int i, ret;

 data->smu_features[GNLD_DPM_PREFETCHER].smu_feature_id =
   FEATURE_DPM_PREFETCHER_BIT;
 data->smu_features[GNLD_DPM_GFXCLK].smu_feature_id =
   FEATURE_DPM_GFXCLK_BIT;
 data->smu_features[GNLD_DPM_UCLK].smu_feature_id =
   FEATURE_DPM_UCLK_BIT;
 data->smu_features[GNLD_DPM_SOCCLK].smu_feature_id =
   FEATURE_DPM_SOCCLK_BIT;
 data->smu_features[GNLD_DPM_UVD].smu_feature_id =
   FEATURE_DPM_UVD_BIT;
 data->smu_features[GNLD_DPM_VCE].smu_feature_id =
   FEATURE_DPM_VCE_BIT;
 data->smu_features[GNLD_ULV].smu_feature_id =
   FEATURE_ULV_BIT;
 data->smu_features[GNLD_DPM_MP0CLK].smu_feature_id =
   FEATURE_DPM_MP0CLK_BIT;
 data->smu_features[GNLD_DPM_LINK].smu_feature_id =
   FEATURE_DPM_LINK_BIT;
 data->smu_features[GNLD_DPM_DCEFCLK].smu_feature_id =
   FEATURE_DPM_DCEFCLK_BIT;
 data->smu_features[GNLD_DS_GFXCLK].smu_feature_id =
   FEATURE_DS_GFXCLK_BIT;
 data->smu_features[GNLD_DS_SOCCLK].smu_feature_id =
   FEATURE_DS_SOCCLK_BIT;
 data->smu_features[GNLD_DS_LCLK].smu_feature_id =
   FEATURE_DS_LCLK_BIT;
 data->smu_features[GNLD_PPT].smu_feature_id =
   FEATURE_PPT_BIT;
 data->smu_features[GNLD_TDC].smu_feature_id =
   FEATURE_TDC_BIT;
 data->smu_features[GNLD_THERMAL].smu_feature_id =
   FEATURE_THERMAL_BIT;
 data->smu_features[GNLD_GFX_PER_CU_CG].smu_feature_id =
   FEATURE_GFX_PER_CU_CG_BIT;
 data->smu_features[GNLD_RM].smu_feature_id =
   FEATURE_RM_BIT;
 data->smu_features[GNLD_DS_DCEFCLK].smu_feature_id =
   FEATURE_DS_DCEFCLK_BIT;
 data->smu_features[GNLD_ACDC].smu_feature_id =
   FEATURE_ACDC_BIT;
 data->smu_features[GNLD_VR0HOT].smu_feature_id =
   FEATURE_VR0HOT_BIT;
 data->smu_features[GNLD_VR1HOT].smu_feature_id =
   FEATURE_VR1HOT_BIT;
 data->smu_features[GNLD_FW_CTF].smu_feature_id =
   FEATURE_FW_CTF_BIT;
 data->smu_features[GNLD_LED_DISPLAY].smu_feature_id =
   FEATURE_LED_DISPLAY_BIT;
 data->smu_features[GNLD_FAN_CONTROL].smu_feature_id =
   FEATURE_FAN_CONTROL_BIT;
 data->smu_features[GNLD_DIDT].smu_feature_id = FEATURE_GFX_EDC_BIT;
 data->smu_features[GNLD_GFXOFF].smu_feature_id = FEATURE_GFXOFF_BIT;
 data->smu_features[GNLD_CG].smu_feature_id = FEATURE_CG_BIT;
 data->smu_features[GNLD_DPM_FCLK].smu_feature_id = FEATURE_DPM_FCLK_BIT;
 data->smu_features[GNLD_DS_FCLK].smu_feature_id = FEATURE_DS_FCLK_BIT;
 data->smu_features[GNLD_DS_MP1CLK].smu_feature_id = FEATURE_DS_MP1CLK_BIT;
 data->smu_features[GNLD_DS_MP0CLK].smu_feature_id = FEATURE_DS_MP0CLK_BIT;
 data->smu_features[GNLD_XGMI].smu_feature_id = FEATURE_XGMI_BIT;
 data->smu_features[GNLD_ECC].smu_feature_id = FEATURE_ECC_BIT;

 for (i = 0; i < GNLD_FEATURES_MAX; i++) {
  data->smu_features[i].smu_feature_bitmap =
   (uint64_t)(1ULL << data->smu_features[i].smu_feature_id);
  data->smu_features[i].allowed =
   ((data->registry_data.disallowed_features >> i) & 1) ?
   false : true;
 }

 /* Get the SN to turn into a Unique ID */
 ret = smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ReadSerialNumTop32, &top32);
 if (ret)
  return ret;

 ret = smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ReadSerialNumBottom32, &bottom32);
 if (ret)
  return ret;

 adev->unique_id = ((uint64_t)bottom32 << 32) | top32;

 return 0;
}

static int vega20_set_private_data_based_on_pptable(struct pp_hwmgr *hwmgr)
{
 return 0;
}

static int vega20_hwmgr_backend_fini(struct pp_hwmgr *hwmgr)
{
 kfree(hwmgr->backend);
 hwmgr->backend = NULL;

 return 0;
}

static int vega20_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data;
 struct amdgpu_device *adev = hwmgr->adev;
 int result;

 data = kzalloc(sizeof(struct vega20_hwmgr), GFP_KERNEL);
 if (data == NULL)
  return -ENOMEM;

 hwmgr->backend = data;

 hwmgr->workload_mask = 1 << hwmgr->workload_prority[PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT];
 hwmgr->power_profile_mode = PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT;
 hwmgr->default_power_profile_mode = PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT;

 vega20_set_default_registry_data(hwmgr);

 data->disable_dpm_mask = 0xff;

 /* need to set voltage control types before EVV patching */
 data->vddc_control = VEGA20_VOLTAGE_CONTROL_NONE;
 data->mvdd_control = VEGA20_VOLTAGE_CONTROL_NONE;
 data->vddci_control = VEGA20_VOLTAGE_CONTROL_NONE;

 data->water_marks_bitmap = 0;
 data->avfs_exist = false;

 vega20_set_features_platform_caps(hwmgr);

 result = vega20_init_dpm_defaults(hwmgr);
 if (result) {
  pr_err("%s failed\n", __func__);
  return result;
 }
 /* Parse pptable data read from VBIOS */
 vega20_set_private_data_based_on_pptable(hwmgr);

 data->is_tlu_enabled = false;

 hwmgr->platform_descriptor.hardwareActivityPerformanceLevels =
   VEGA20_MAX_HARDWARE_POWERLEVELS;
 hwmgr->platform_descriptor.hardwarePerformanceLevels = 2;
 hwmgr->platform_descriptor.minimumClocksReductionPercentage = 50;

 hwmgr->platform_descriptor.vbiosInterruptId = 0x20000400; /* IRQ_SOURCE1_SW_INT */
 /* The true clock step depends on the frequency, typically 4.5 or 9 MHz. Here we use 5. */
 hwmgr->platform_descriptor.clockStep.engineClock = 500;
 hwmgr->platform_descriptor.clockStep.memoryClock = 500;

 data->total_active_cus = adev->gfx.cu_info.number;
 data->is_custom_profile_set = false;

 return 0;
}

static int vega20_init_sclk_threshold(struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data =
   (struct vega20_hwmgr *)(hwmgr->backend);

 data->low_sclk_interrupt_threshold = 0;

 return 0;
}

static int vega20_setup_asic_task(struct pp_hwmgr *hwmgr)
{
 struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
 int ret = 0;
 bool use_baco = (amdgpu_in_reset(adev) &&
    (amdgpu_asic_reset_method(adev) == AMD_RESET_METHOD_BACO)) ||
  (adev->in_runpm && amdgpu_asic_supports_baco(adev));

 ret = vega20_init_sclk_threshold(hwmgr);
 PP_ASSERT_WITH_CODE(!ret,
   "Failed to init sclk threshold!",
   return ret);

 if (use_baco) {
  ret = vega20_baco_apply_vdci_flush_workaround(hwmgr);
  if (ret)
   pr_err("Failed to apply vega20 baco workaround!\n");
 }

 return ret;
}

/*
 * @fn vega20_init_dpm_state
 * @brief Function to initialize all Soft Min/Max and Hard Min/Max to 0xff.
 *
 * @param    dpm_state - the address of the DPM Table to initiailize.
 * @return   None.
 */

static void vega20_init_dpm_state(struct vega20_dpm_state *dpm_state)
{
 dpm_state->soft_min_level = 0x0;
 dpm_state->soft_max_level = VG20_CLOCK_MAX_DEFAULT;
 dpm_state->hard_min_level = 0x0;
 dpm_state->hard_max_level = VG20_CLOCK_MAX_DEFAULT;
}

static int vega20_get_number_of_dpm_level(struct pp_hwmgr *hwmgr,
  PPCLK_e clk_id, uint32_t *num_of_levels)
{
 int ret = 0;

 ret = smum_send_msg_to_smc_with_parameter(hwmgr,
   PPSMC_MSG_GetDpmFreqByIndex,
   (clk_id << 16 | 0xFF),
   num_of_levels);
 PP_ASSERT_WITH_CODE(!ret,
   "[GetNumOfDpmLevel] failed to get dpm levels!",
   return ret);

 return ret;
}

static int vega20_get_dpm_frequency_by_index(struct pp_hwmgr *hwmgr,
  PPCLK_e clk_id, uint32_t index, uint32_t *clk)
{
 int ret = 0;

 ret = smum_send_msg_to_smc_with_parameter(hwmgr,
   PPSMC_MSG_GetDpmFreqByIndex,
   (clk_id << 16 | index),
   clk);
 PP_ASSERT_WITH_CODE(!ret,
   "[GetDpmFreqByIndex] failed to get dpm freq by index!",
   return ret);

 return ret;
}

static int vega20_setup_single_dpm_table(struct pp_hwmgr *hwmgr,
  struct vega20_single_dpm_table *dpm_table, PPCLK_e clk_id)
{
 int ret = 0;
 uint32_t i, num_of_levels, clk;

 ret = vega20_get_number_of_dpm_level(hwmgr, clk_id, &num_of_levels);
 PP_ASSERT_WITH_CODE(!ret,
   "[SetupSingleDpmTable] failed to get clk levels!",
   return ret);

 dpm_table->count = num_of_levels;

 for (i = 0; i < num_of_levels; i++) {
  ret = vega20_get_dpm_frequency_by_index(hwmgr, clk_id, i, &clk);
  PP_ASSERT_WITH_CODE(!ret,
   "[SetupSingleDpmTable] failed to get clk of specific level!",
   return ret);
  dpm_table->dpm_levels[i].value = clk;
  dpm_table->dpm_levels[i].enabled = true;
 }

 return ret;
}

static int vega20_setup_gfxclk_dpm_table(struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data =
   (struct vega20_hwmgr *)(hwmgr->backend);
 struct vega20_single_dpm_table *dpm_table;
 int ret = 0;

 dpm_table = &(data->dpm_table.gfx_table);
 if (data->smu_features[GNLD_DPM_GFXCLK].enabled) {
  ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_GFXCLK);
  PP_ASSERT_WITH_CODE(!ret,
    "[SetupDefaultDpmTable] failed to get gfxclk dpm levels!",
    return ret);
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = data->vbios_boot_state.gfx_clock / 100;
 }

 return ret;
}

static int vega20_setup_memclk_dpm_table(struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data =
   (struct vega20_hwmgr *)(hwmgr->backend);
 struct vega20_single_dpm_table *dpm_table;
 int ret = 0;

 dpm_table = &(data->dpm_table.mem_table);
 if (data->smu_features[GNLD_DPM_UCLK].enabled) {
  ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_UCLK);
  PP_ASSERT_WITH_CODE(!ret,
    "[SetupDefaultDpmTable] failed to get memclk dpm levels!",
    return ret);
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = data->vbios_boot_state.mem_clock / 100;
 }

 return ret;
}

/*
 * This function is to initialize all DPM state tables
 * for SMU based on the dependency table.
 * Dynamic state patching function will then trim these
 * state tables to the allowed range based
 * on the power policy or external client requests,
 * such as UVD request, etc.
 */

static int vega20_setup_default_dpm_tables(struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data =
   (struct vega20_hwmgr *)(hwmgr->backend);
 struct vega20_single_dpm_table *dpm_table;
 int ret = 0;

 memset(&data->dpm_table, 0, sizeof(data->dpm_table));

 /* socclk */
 dpm_table = &(data->dpm_table.soc_table);
 if (data->smu_features[GNLD_DPM_SOCCLK].enabled) {
  ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_SOCCLK);
  PP_ASSERT_WITH_CODE(!ret,
    "[SetupDefaultDpmTable] failed to get socclk dpm levels!",
    return ret);
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = data->vbios_boot_state.soc_clock / 100;
 }
 vega20_init_dpm_state(&(dpm_table->dpm_state));

 /* gfxclk */
 dpm_table = &(data->dpm_table.gfx_table);
 ret = vega20_setup_gfxclk_dpm_table(hwmgr);
 if (ret)
  return ret;
 vega20_init_dpm_state(&(dpm_table->dpm_state));

 /* memclk */
 dpm_table = &(data->dpm_table.mem_table);
 ret = vega20_setup_memclk_dpm_table(hwmgr);
 if (ret)
  return ret;
 vega20_init_dpm_state(&(dpm_table->dpm_state));

 /* eclk */
 dpm_table = &(data->dpm_table.eclk_table);
 if (data->smu_features[GNLD_DPM_VCE].enabled) {
  ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_ECLK);
  PP_ASSERT_WITH_CODE(!ret,
    "[SetupDefaultDpmTable] failed to get eclk dpm levels!",
    return ret);
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = data->vbios_boot_state.eclock / 100;
 }
 vega20_init_dpm_state(&(dpm_table->dpm_state));

 /* vclk */
 dpm_table = &(data->dpm_table.vclk_table);
 if (data->smu_features[GNLD_DPM_UVD].enabled) {
  ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_VCLK);
  PP_ASSERT_WITH_CODE(!ret,
    "[SetupDefaultDpmTable] failed to get vclk dpm levels!",
    return ret);
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = data->vbios_boot_state.vclock / 100;
 }
 vega20_init_dpm_state(&(dpm_table->dpm_state));

 /* dclk */
 dpm_table = &(data->dpm_table.dclk_table);
 if (data->smu_features[GNLD_DPM_UVD].enabled) {
  ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_DCLK);
  PP_ASSERT_WITH_CODE(!ret,
    "[SetupDefaultDpmTable] failed to get dclk dpm levels!",
    return ret);
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = data->vbios_boot_state.dclock / 100;
 }
 vega20_init_dpm_state(&(dpm_table->dpm_state));

 /* dcefclk */
 dpm_table = &(data->dpm_table.dcef_table);
 if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
  ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_DCEFCLK);
  PP_ASSERT_WITH_CODE(!ret,
    "[SetupDefaultDpmTable] failed to get dcefclk dpm levels!",
    return ret);
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = data->vbios_boot_state.dcef_clock / 100;
 }
 vega20_init_dpm_state(&(dpm_table->dpm_state));

 /* pixclk */
 dpm_table = &(data->dpm_table.pixel_table);
 if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
  ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_PIXCLK);
  PP_ASSERT_WITH_CODE(!ret,
    "[SetupDefaultDpmTable] failed to get pixclk dpm levels!",
    return ret);
 } else
  dpm_table->count = 0;
 vega20_init_dpm_state(&(dpm_table->dpm_state));

 /* dispclk */
 dpm_table = &(data->dpm_table.display_table);
 if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
  ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_DISPCLK);
  PP_ASSERT_WITH_CODE(!ret,
    "[SetupDefaultDpmTable] failed to get dispclk dpm levels!",
    return ret);
 } else
  dpm_table->count = 0;
 vega20_init_dpm_state(&(dpm_table->dpm_state));

 /* phyclk */
 dpm_table = &(data->dpm_table.phy_table);
 if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
  ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_PHYCLK);
  PP_ASSERT_WITH_CODE(!ret,
    "[SetupDefaultDpmTable] failed to get phyclk dpm levels!",
    return ret);
 } else
  dpm_table->count = 0;
 vega20_init_dpm_state(&(dpm_table->dpm_state));

 /* fclk */
 dpm_table = &(data->dpm_table.fclk_table);
 if (data->smu_features[GNLD_DPM_FCLK].enabled) {
  ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_FCLK);
  PP_ASSERT_WITH_CODE(!ret,
    "[SetupDefaultDpmTable] failed to get fclk dpm levels!",
    return ret);
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = data->vbios_boot_state.fclock / 100;
 }
 vega20_init_dpm_state(&(dpm_table->dpm_state));

 /* save a copy of the default DPM table */
 memcpy(&(data->golden_dpm_table), &(data->dpm_table),
   sizeof(struct vega20_dpm_table));

 return 0;
}

/**
 * vega20_init_smc_table - Initializes the SMC table and uploads it
 *
 * @hwmgr:  the address of the powerplay hardware manager.
 * return:  always 0
 */

static int vega20_init_smc_table(struct pp_hwmgr *hwmgr)
{
 int result;
 struct vega20_hwmgr *data =
   (struct vega20_hwmgr *)(hwmgr->backend);
 PPTable_t *pp_table = &(data->smc_state_table.pp_table);
 struct pp_atomfwctrl_bios_boot_up_values boot_up_values;
 struct phm_ppt_v3_information *pptable_information =
  (struct phm_ppt_v3_information *)hwmgr->pptable;

 result = pp_atomfwctrl_get_vbios_bootup_values(hwmgr, &boot_up_values);
 PP_ASSERT_WITH_CODE(!result,
   "[InitSMCTable] Failed to get vbios bootup values!",
   return result);

 data->vbios_boot_state.vddc     = boot_up_values.usVddc;
 data->vbios_boot_state.vddci    = boot_up_values.usVddci;
 data->vbios_boot_state.mvddc    = boot_up_values.usMvddc;
 data->vbios_boot_state.gfx_clock = boot_up_values.ulGfxClk;
 data->vbios_boot_state.mem_clock = boot_up_values.ulUClk;
 data->vbios_boot_state.soc_clock = boot_up_values.ulSocClk;
 data->vbios_boot_state.dcef_clock = boot_up_values.ulDCEFClk;
 data->vbios_boot_state.eclock = boot_up_values.ulEClk;
 data->vbios_boot_state.vclock = boot_up_values.ulVClk;
 data->vbios_boot_state.dclock = boot_up_values.ulDClk;
 data->vbios_boot_state.fclock = boot_up_values.ulFClk;
 data->vbios_boot_state.uc_cooling_id = boot_up_values.ucCoolingID;

 smum_send_msg_to_smc_with_parameter(hwmgr,
   PPSMC_MSG_SetMinDeepSleepDcefclk,
  (uint32_t)(data->vbios_boot_state.dcef_clock / 100),
   NULL);

 memcpy(pp_table, pptable_information->smc_pptable, sizeof(PPTable_t));

 result = smum_smc_table_manager(hwmgr,
     (uint8_t *)pp_table, TABLE_PPTABLE, false);
 PP_ASSERT_WITH_CODE(!result,
   "[InitSMCTable] Failed to upload PPtable!",
   return result);

 return 0;
}

/*
 * Override PCIe link speed and link width for DPM Level 1. PPTable entries
 * reflect the ASIC capabilities and not the system capabilities. For e.g.
 * Vega20 board in a PCI Gen3 system. In this case, when SMU's tries to switch
 * to DPM1, it fails as system doesn't support Gen4.
 */

static int vega20_override_pcie_parameters(struct pp_hwmgr *hwmgr)
{
 struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
 struct vega20_hwmgr *data =
   (struct vega20_hwmgr *)(hwmgr->backend);
 uint32_t pcie_gen = 0, pcie_width = 0, smu_pcie_arg, pcie_gen_arg, pcie_width_arg;
 PPTable_t *pp_table = &(data->smc_state_table.pp_table);
 int i;
 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;

 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;

 /* 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
 */

 for (i = 0; i < NUM_LINK_LEVELS; i++) {
  pcie_gen_arg = (pp_table->PcieGenSpeed[i] > pcie_gen) ? pcie_gen :
   pp_table->PcieGenSpeed[i];
  pcie_width_arg = (pp_table->PcieLaneCount[i] > pcie_width) ? pcie_width :
   pp_table->PcieLaneCount[i];

  if (pcie_gen_arg != pp_table->PcieGenSpeed[i] || pcie_width_arg !=
      pp_table->PcieLaneCount[i]) {
   smu_pcie_arg = (i << 16) | (pcie_gen_arg << 8) | pcie_width_arg;
   ret = smum_send_msg_to_smc_with_parameter(hwmgr,
    PPSMC_MSG_OverridePcieParameters, smu_pcie_arg,
    NULL);
   PP_ASSERT_WITH_CODE(!ret,
    "[OverridePcieParameters] Attempt to override pcie params failed!",
    return ret);
  }

  /* update the pptable */
  pp_table->PcieGenSpeed[i] = pcie_gen_arg;
  pp_table->PcieLaneCount[i] = pcie_width_arg;
 }

 /* override to the highest if it's disabled from ppfeaturmask */
 if (data->registry_data.pcie_dpm_key_disabled) {
  for (i = 0; i < NUM_LINK_LEVELS; i++) {
   smu_pcie_arg = (i << 16) | (pcie_gen << 8) | pcie_width;
   ret = smum_send_msg_to_smc_with_parameter(hwmgr,
    PPSMC_MSG_OverridePcieParameters, smu_pcie_arg,
    NULL);
   PP_ASSERT_WITH_CODE(!ret,
    "[OverridePcieParameters] Attempt to override pcie params failed!",
    return ret);

   pp_table->PcieGenSpeed[i] = pcie_gen;
   pp_table->PcieLaneCount[i] = pcie_width;
  }
  ret = vega20_enable_smc_features(hwmgr,
    false,
    data->smu_features[GNLD_DPM_LINK].smu_feature_bitmap);
  PP_ASSERT_WITH_CODE(!ret,
    "Attempt to Disable DPM LINK Failed!",
    return ret);
  data->smu_features[GNLD_DPM_LINK].enabled = false;
  data->smu_features[GNLD_DPM_LINK].supported = false;
 }

 return 0;
}

static int vega20_set_allowed_featuresmask(struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data =
   (struct vega20_hwmgr *)(hwmgr->backend);
 uint32_t allowed_features_low = 0, allowed_features_high = 0;
 int i;
 int ret = 0;

 for (i = 0; i < GNLD_FEATURES_MAX; i++)
  if (data->smu_features[i].allowed)
   data->smu_features[i].smu_feature_id > 31 ?
    (allowed_features_high |=
     ((data->smu_features[i].smu_feature_bitmap >> SMU_FEATURES_HIGH_SHIFT)
      & 0xFFFFFFFF)) :
    (allowed_features_low |=
     ((data->smu_features[i].smu_feature_bitmap >> SMU_FEATURES_LOW_SHIFT)
      & 0xFFFFFFFF));

 ret = smum_send_msg_to_smc_with_parameter(hwmgr,
  PPSMC_MSG_SetAllowedFeaturesMaskHigh, allowed_features_high, NULL);
 PP_ASSERT_WITH_CODE(!ret,
  "[SetAllowedFeaturesMask] Attempt to set allowed features mask(high) failed!",
  return ret);

 ret = smum_send_msg_to_smc_with_parameter(hwmgr,
  PPSMC_MSG_SetAllowedFeaturesMaskLow, allowed_features_low, NULL);
 PP_ASSERT_WITH_CODE(!ret,
  "[SetAllowedFeaturesMask] Attempt to set allowed features mask (low) failed!",
  return ret);

 return 0;
}

static int vega20_run_btc(struct pp_hwmgr *hwmgr)
{
 return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_RunBtc, NULL);
}

static int vega20_run_btc_afll(struct pp_hwmgr *hwmgr)
{
 return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_RunAfllBtc, NULL);
}

static int vega20_enable_all_smu_features(struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data =
   (struct vega20_hwmgr *)(hwmgr->backend);
 uint64_t features_enabled;
 int i;
 bool enabled;
 int ret = 0;

 PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc(hwmgr,
   PPSMC_MSG_EnableAllSmuFeatures,
   NULL)) == 0,
   "[EnableAllSMUFeatures] Failed to enable all smu features!",
   return ret);

 ret = vega20_get_enabled_smc_features(hwmgr, &features_enabled);
 PP_ASSERT_WITH_CODE(!ret,
   "[EnableAllSmuFeatures] Failed to get enabled smc features!",
   return ret);

 for (i = 0; i < GNLD_FEATURES_MAX; i++) {
  enabled = (features_enabled & data->smu_features[i].smu_feature_bitmap) ?
   true : false;
  data->smu_features[i].enabled = enabled;
  data->smu_features[i].supported = enabled;

#if 0
  if (data->smu_features[i].allowed && !enabled)
   pr_info("[EnableAllSMUFeatures] feature %d is expected enabled!", i);
  else if (!data->smu_features[i].allowed && enabled)
   pr_info("[EnableAllSMUFeatures] feature %d is expected disabled!", i);
#endif
 }

 return 0;
}

static int vega20_notify_smc_display_change(struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);

 if (data->smu_features[GNLD_DPM_UCLK].enabled)
  return smum_send_msg_to_smc_with_parameter(hwmgr,
   PPSMC_MSG_SetUclkFastSwitch,
   1,
   NULL);

 return 0;
}

static int vega20_send_clock_ratio(struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data =
   (struct vega20_hwmgr *)(hwmgr->backend);

 return smum_send_msg_to_smc_with_parameter(hwmgr,
   PPSMC_MSG_SetFclkGfxClkRatio,
   data->registry_data.fclk_gfxclk_ratio,
   NULL);
}

static int vega20_disable_all_smu_features(struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data =
   (struct vega20_hwmgr *)(hwmgr->backend);
 int i, ret = 0;

 PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc(hwmgr,
   PPSMC_MSG_DisableAllSmuFeatures,
   NULL)) == 0,
   "[DisableAllSMUFeatures] Failed to disable all smu features!",
   return ret);

 for (i = 0; i < GNLD_FEATURES_MAX; i++)
  data->smu_features[i].enabled = 0;

 return 0;
}

static int vega20_od8_set_feature_capabilities(
  struct pp_hwmgr *hwmgr)
{
 struct phm_ppt_v3_information *pptable_information =
  (struct phm_ppt_v3_information *)hwmgr->pptable;
 struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
 PPTable_t *pp_table = &(data->smc_state_table.pp_table);
 struct vega20_od8_settings *od_settings = &(data->od8_settings);

 od_settings->overdrive8_capabilities = 0;

 if (data->smu_features[GNLD_DPM_GFXCLK].enabled) {
  if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_GFXCLK_LIMITS] &&
      pptable_information->od_settings_max[OD8_SETTING_GFXCLK_FMAX] > 0 &&
      pptable_information->od_settings_min[OD8_SETTING_GFXCLK_FMIN] > 0 &&
      (pptable_information->od_settings_max[OD8_SETTING_GFXCLK_FMAX] >=
      pptable_information->od_settings_min[OD8_SETTING_GFXCLK_FMIN]))
   od_settings->overdrive8_capabilities |= OD8_GFXCLK_LIMITS;

  if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_GFXCLK_CURVE] &&
      (pptable_information->od_settings_min[OD8_SETTING_GFXCLK_VOLTAGE1] >=
       pp_table->MinVoltageGfx / VOLTAGE_SCALE) &&
      (pptable_information->od_settings_max[OD8_SETTING_GFXCLK_VOLTAGE3] <=
       pp_table->MaxVoltageGfx / VOLTAGE_SCALE) &&
      (pptable_information->od_settings_max[OD8_SETTING_GFXCLK_VOLTAGE3] >=
       pptable_information->od_settings_min[OD8_SETTING_GFXCLK_VOLTAGE1]))
   od_settings->overdrive8_capabilities |= OD8_GFXCLK_CURVE;
 }

 if (data->smu_features[GNLD_DPM_UCLK].enabled) {
  pptable_information->od_settings_min[OD8_SETTING_UCLK_FMAX] =
   data->dpm_table.mem_table.dpm_levels[data->dpm_table.mem_table.count - 2].value;
  if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_UCLK_MAX] &&
      pptable_information->od_settings_min[OD8_SETTING_UCLK_FMAX] > 0 &&
      pptable_information->od_settings_max[OD8_SETTING_UCLK_FMAX] > 0 &&
      (pptable_information->od_settings_max[OD8_SETTING_UCLK_FMAX] >=
      pptable_information->od_settings_min[OD8_SETTING_UCLK_FMAX]))
   od_settings->overdrive8_capabilities |= OD8_UCLK_MAX;
 }

 if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_POWER_LIMIT] &&
     pptable_information->od_settings_max[OD8_SETTING_POWER_PERCENTAGE] > 0 &&
     pptable_information->od_settings_max[OD8_SETTING_POWER_PERCENTAGE] <= 100 &&
     pptable_information->od_settings_min[OD8_SETTING_POWER_PERCENTAGE] > 0 &&
     pptable_information->od_settings_min[OD8_SETTING_POWER_PERCENTAGE] <= 100)
  od_settings->overdrive8_capabilities |= OD8_POWER_LIMIT;

 if (data->smu_features[GNLD_FAN_CONTROL].enabled) {
  if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_FAN_ACOUSTIC_LIMIT] &&
      pptable_information->od_settings_min[OD8_SETTING_FAN_ACOUSTIC_LIMIT] > 0 &&
      pptable_information->od_settings_max[OD8_SETTING_FAN_ACOUSTIC_LIMIT] > 0 &&
      (pptable_information->od_settings_max[OD8_SETTING_FAN_ACOUSTIC_LIMIT] >=
       pptable_information->od_settings_min[OD8_SETTING_FAN_ACOUSTIC_LIMIT]))
   od_settings->overdrive8_capabilities |= OD8_ACOUSTIC_LIMIT_SCLK;

  if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_FAN_SPEED_MIN] &&
      (pptable_information->od_settings_min[OD8_SETTING_FAN_MIN_SPEED] >=
      (pp_table->FanPwmMin * pp_table->FanMaximumRpm / 100)) &&
      pptable_information->od_settings_max[OD8_SETTING_FAN_MIN_SPEED] > 0 &&
      (pptable_information->od_settings_max[OD8_SETTING_FAN_MIN_SPEED] >=
       pptable_information->od_settings_min[OD8_SETTING_FAN_MIN_SPEED]))
   od_settings->overdrive8_capabilities |= OD8_FAN_SPEED_MIN;
 }

 if (data->smu_features[GNLD_THERMAL].enabled) {
  if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_TEMPERATURE_FAN] &&
      pptable_information->od_settings_max[OD8_SETTING_FAN_TARGET_TEMP] > 0 &&
      pptable_information->od_settings_min[OD8_SETTING_FAN_TARGET_TEMP] > 0 &&
      (pptable_information->od_settings_max[OD8_SETTING_FAN_TARGET_TEMP] >=
       pptable_information->od_settings_min[OD8_SETTING_FAN_TARGET_TEMP]))
   od_settings->overdrive8_capabilities |= OD8_TEMPERATURE_FAN;

  if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_TEMPERATURE_SYSTEM] &&
      pptable_information->od_settings_max[OD8_SETTING_OPERATING_TEMP_MAX] > 0 &&
      pptable_information->od_settings_min[OD8_SETTING_OPERATING_TEMP_MAX] > 0 &&
      (pptable_information->od_settings_max[OD8_SETTING_OPERATING_TEMP_MAX] >=
       pptable_information->od_settings_min[OD8_SETTING_OPERATING_TEMP_MAX]))
   od_settings->overdrive8_capabilities |= OD8_TEMPERATURE_SYSTEM;
 }

 if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_MEMORY_TIMING_TUNE])
  od_settings->overdrive8_capabilities |= OD8_MEMORY_TIMING_TUNE;

 if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_FAN_ZERO_RPM_CONTROL] &&
     pp_table->FanZeroRpmEnable)
  od_settings->overdrive8_capabilities |= OD8_FAN_ZERO_RPM_CONTROL;

 if (!od_settings->overdrive8_capabilities)
  hwmgr->od_enabled = false;

 return 0;
}

static int vega20_od8_set_feature_id(
  struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
 struct vega20_od8_settings *od_settings = &(data->od8_settings);

 if (od_settings->overdrive8_capabilities & OD8_GFXCLK_LIMITS) {
  od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMIN].feature_id =
   OD8_GFXCLK_LIMITS;
  od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].feature_id =
   OD8_GFXCLK_LIMITS;
 } else {
  od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMIN].feature_id =
   0;
  od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].feature_id =
   0;
 }

 if (od_settings->overdrive8_capabilities & OD8_GFXCLK_CURVE) {
  od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ1].feature_id =
   OD8_GFXCLK_CURVE;
  od_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].feature_id =
   OD8_GFXCLK_CURVE;
  od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ2].feature_id =
   OD8_GFXCLK_CURVE;
  od_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].feature_id =
   OD8_GFXCLK_CURVE;
  od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ3].feature_id =
   OD8_GFXCLK_CURVE;
  od_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].feature_id =
   OD8_GFXCLK_CURVE;
 } else {
  od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ1].feature_id =
   0;
  od_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].feature_id =
   0;
  od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ2].feature_id =
   0;
  od_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].feature_id =
   0;
  od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ3].feature_id =
   0;
  od_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].feature_id =
   0;
 }

 if (od_settings->overdrive8_capabilities & OD8_UCLK_MAX)
  od_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].feature_id = OD8_UCLK_MAX;
 else
  od_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].feature_id = 0;

 if (od_settings->overdrive8_capabilities & OD8_POWER_LIMIT)
  od_settings->od8_settings_array[OD8_SETTING_POWER_PERCENTAGE].feature_id = OD8_POWER_LIMIT;
 else
  od_settings->od8_settings_array[OD8_SETTING_POWER_PERCENTAGE].feature_id = 0;

 if (od_settings->overdrive8_capabilities & OD8_ACOUSTIC_LIMIT_SCLK)
  od_settings->od8_settings_array[OD8_SETTING_FAN_ACOUSTIC_LIMIT].feature_id =
   OD8_ACOUSTIC_LIMIT_SCLK;
 else
  od_settings->od8_settings_array[OD8_SETTING_FAN_ACOUSTIC_LIMIT].feature_id =
   0;

 if (od_settings->overdrive8_capabilities & OD8_FAN_SPEED_MIN)
  od_settings->od8_settings_array[OD8_SETTING_FAN_MIN_SPEED].feature_id =
   OD8_FAN_SPEED_MIN;
 else
  od_settings->od8_settings_array[OD8_SETTING_FAN_MIN_SPEED].feature_id =
   0;

 if (od_settings->overdrive8_capabilities & OD8_TEMPERATURE_FAN)
  od_settings->od8_settings_array[OD8_SETTING_FAN_TARGET_TEMP].feature_id =
   OD8_TEMPERATURE_FAN;
 else
  od_settings->od8_settings_array[OD8_SETTING_FAN_TARGET_TEMP].feature_id =
   0;

 if (od_settings->overdrive8_capabilities & OD8_TEMPERATURE_SYSTEM)
  od_settings->od8_settings_array[OD8_SETTING_OPERATING_TEMP_MAX].feature_id =
   OD8_TEMPERATURE_SYSTEM;
 else
  od_settings->od8_settings_array[OD8_SETTING_OPERATING_TEMP_MAX].feature_id =
   0;

 return 0;
}

static int vega20_od8_get_gfx_clock_base_voltage(
  struct pp_hwmgr *hwmgr,
  uint32_t *voltage,
  uint32_t freq)
{
 int ret = 0;

 ret = smum_send_msg_to_smc_with_parameter(hwmgr,
   PPSMC_MSG_GetAVFSVoltageByDpm,
   ((AVFS_CURVE << 24) | (OD8_HOTCURVE_TEMPERATURE << 16) | freq),
   voltage);
 PP_ASSERT_WITH_CODE(!ret,
   "[GetBaseVoltage] failed to get GFXCLK AVFS voltage from SMU!",
   return ret);

 *voltage = *voltage / VOLTAGE_SCALE;

 return 0;
}

static int vega20_od8_initialize_default_settings(
  struct pp_hwmgr *hwmgr)
{
 struct phm_ppt_v3_information *pptable_information =
  (struct phm_ppt_v3_information *)hwmgr->pptable;
 struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
 struct vega20_od8_settings *od8_settings = &(data->od8_settings);
 OverDriveTable_t *od_table = &(data->smc_state_table.overdrive_table);
 int i, ret = 0;

 /* Set Feature Capabilities */
 vega20_od8_set_feature_capabilities(hwmgr);

 /* Map FeatureID to individual settings */
 vega20_od8_set_feature_id(hwmgr);

 /* Set default values */
 ret = smum_smc_table_manager(hwmgr, (uint8_t *)od_table, TABLE_OVERDRIVE, true);
 PP_ASSERT_WITH_CODE(!ret,
   "Failed to export over drive table!",
   return ret);

 if (od8_settings->overdrive8_capabilities & 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;
 } else {
  od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMIN].default_value =
   0;
  od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].default_value =
   0;
 }

 if (od8_settings->overdrive8_capabilities & OD8_GFXCLK_CURVE) {
  od_table->GfxclkFreq1 = od_table->GfxclkFmin;
  od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ1].default_value =
   od_table->GfxclkFreq1;

  od_table->GfxclkFreq3 = od_table->GfxclkFmax;
  od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ3].default_value =
   od_table->GfxclkFreq3;

  od_table->GfxclkFreq2 = (od_table->GfxclkFreq1 + od_table->GfxclkFreq3) / 2;
  od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ2].default_value =
   od_table->GfxclkFreq2;

  PP_ASSERT_WITH_CODE(!vega20_od8_get_gfx_clock_base_voltage(hwmgr,
       &(od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].default_value),
         od_table->GfxclkFreq1),
    "[PhwVega20_OD8_InitializeDefaultSettings] Failed to get Base clock voltage from SMU!",
    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;

  PP_ASSERT_WITH_CODE(!vega20_od8_get_gfx_clock_base_voltage(hwmgr,
       &(od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].default_value),
         od_table->GfxclkFreq2),
    "[PhwVega20_OD8_InitializeDefaultSettings] Failed to get Base clock voltage from SMU!",
    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;

  PP_ASSERT_WITH_CODE(!vega20_od8_get_gfx_clock_base_voltage(hwmgr,
       &(od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].default_value),
         od_table->GfxclkFreq3),
    "[PhwVega20_OD8_InitializeDefaultSettings] Failed to get Base clock voltage from SMU!",
    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;
 } else {
  od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ1].default_value =
   0;
  od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].default_value =
   0;
  od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ2].default_value =
   0;
  od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].default_value =
   0;
  od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ3].default_value =
   0;
  od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].default_value =
   0;
 }

 if (od8_settings->overdrive8_capabilities & OD8_UCLK_MAX)
  od8_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].default_value =
   od_table->UclkFmax;
 else
  od8_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].default_value =
   0;

 if (od8_settings->overdrive8_capabilities & OD8_POWER_LIMIT)
  od8_settings->od8_settings_array[OD8_SETTING_POWER_PERCENTAGE].default_value =
   od_table->OverDrivePct;
 else
  od8_settings->od8_settings_array[OD8_SETTING_POWER_PERCENTAGE].default_value =
   0;

 if (od8_settings->overdrive8_capabilities & OD8_ACOUSTIC_LIMIT_SCLK)
  od8_settings->od8_settings_array[OD8_SETTING_FAN_ACOUSTIC_LIMIT].default_value =
   od_table->FanMaximumRpm;
 else
  od8_settings->od8_settings_array[OD8_SETTING_FAN_ACOUSTIC_LIMIT].default_value =
   0;

 if (od8_settings->overdrive8_capabilities & OD8_FAN_SPEED_MIN)
  od8_settings->od8_settings_array[OD8_SETTING_FAN_MIN_SPEED].default_value =
   od_table->FanMinimumPwm * data->smc_state_table.pp_table.FanMaximumRpm / 100;
 else
  od8_settings->od8_settings_array[OD8_SETTING_FAN_MIN_SPEED].default_value =
   0;

 if (od8_settings->overdrive8_capabilities & OD8_TEMPERATURE_FAN)
  od8_settings->od8_settings_array[OD8_SETTING_FAN_TARGET_TEMP].default_value =
   od_table->FanTargetTemperature;
 else
  od8_settings->od8_settings_array[OD8_SETTING_FAN_TARGET_TEMP].default_value =
   0;

 if (od8_settings->overdrive8_capabilities & OD8_TEMPERATURE_SYSTEM)
  od8_settings->od8_settings_array[OD8_SETTING_OPERATING_TEMP_MAX].default_value =
   od_table->MaxOpTemp;
 else
  od8_settings->od8_settings_array[OD8_SETTING_OPERATING_TEMP_MAX].default_value =
   0;

 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 =
    pptable_information->od_settings_min[i];
   od8_settings->od8_settings_array[i].max_value =
    pptable_information->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;
  }
 }

 ret = smum_smc_table_manager(hwmgr, (uint8_t *)od_table, TABLE_OVERDRIVE, false);
 PP_ASSERT_WITH_CODE(!ret,
   "Failed to import over drive table!",
   return ret);

 return 0;
}

static int vega20_od8_set_settings(
  struct pp_hwmgr *hwmgr,
  uint32_t index,
  uint32_t value)
{
 OverDriveTable_t od_table;
 int ret = 0;
 struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
 struct vega20_od8_single_setting *od8_settings =
   data->od8_settings.od8_settings_array;

 ret = smum_smc_table_manager(hwmgr, (uint8_t *)(&od_table), TABLE_OVERDRIVE, true);
 PP_ASSERT_WITH_CODE(!ret,
   "Failed to export over drive table!",
   return ret);

 switch (index) {
 case OD8_SETTING_GFXCLK_FMIN:
  od_table.GfxclkFmin = (uint16_t)value;
  break;
 case OD8_SETTING_GFXCLK_FMAX:
  if (value < od8_settings[OD8_SETTING_GFXCLK_FMAX].min_value ||
      value > od8_settings[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_SETTING_UCLK_FMAX].min_value ||
      value > od8_settings[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;
 }

 ret = smum_smc_table_manager(hwmgr, (uint8_t *)(&od_table), TABLE_OVERDRIVE, false);
 PP_ASSERT_WITH_CODE(!ret,
   "Failed to import over drive table!",
   return ret);

 return 0;
}

static int vega20_get_sclk_od(
  struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data = hwmgr->backend;
 struct vega20_single_dpm_table *sclk_table =
   &(data->dpm_table.gfx_table);
 struct vega20_single_dpm_table *golden_sclk_table =
   &(data->golden_dpm_table.gfx_table);
 int value = sclk_table->dpm_levels[sclk_table->count - 1].value;
 int golden_value = golden_sclk_table->dpm_levels
   [golden_sclk_table->count - 1].value;

 /* od percentage */
 value -= golden_value;
 value = DIV_ROUND_UP(value * 100, golden_value);

 return value;
}

static int vega20_set_sclk_od(
  struct pp_hwmgr *hwmgr, uint32_t value)
{
 struct vega20_hwmgr *data = hwmgr->backend;
 struct vega20_single_dpm_table *golden_sclk_table =
   &(data->golden_dpm_table.gfx_table);
 uint32_t od_sclk;
 int ret = 0;

 od_sclk = golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value * value;
 od_sclk /= 100;
 od_sclk += golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value;

 ret = vega20_od8_set_settings(hwmgr, OD8_SETTING_GFXCLK_FMAX, od_sclk);
 PP_ASSERT_WITH_CODE(!ret,
   "[SetSclkOD] failed to set od gfxclk!",
   return ret);

 /* retrieve updated gfxclk table */
 ret = vega20_setup_gfxclk_dpm_table(hwmgr);
 PP_ASSERT_WITH_CODE(!ret,
   "[SetSclkOD] failed to refresh gfxclk table!",
   return ret);

 return 0;
}

static int vega20_get_mclk_od(
  struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data = hwmgr->backend;
 struct vega20_single_dpm_table *mclk_table =
   &(data->dpm_table.mem_table);
 struct vega20_single_dpm_table *golden_mclk_table =
   &(data->golden_dpm_table.mem_table);
 int value = mclk_table->dpm_levels[mclk_table->count - 1].value;
 int golden_value = golden_mclk_table->dpm_levels
   [golden_mclk_table->count - 1].value;

 /* od percentage */
 value -= golden_value;
 value = DIV_ROUND_UP(value * 100, golden_value);

 return value;
}

static int vega20_set_mclk_od(
  struct pp_hwmgr *hwmgr, uint32_t value)
{
 struct vega20_hwmgr *data = hwmgr->backend;
 struct vega20_single_dpm_table *golden_mclk_table =
   &(data->golden_dpm_table.mem_table);
 uint32_t od_mclk;
 int ret = 0;

 od_mclk = golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value * value;
 od_mclk /= 100;
 od_mclk += golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value;

 ret = vega20_od8_set_settings(hwmgr, OD8_SETTING_UCLK_FMAX, od_mclk);
 PP_ASSERT_WITH_CODE(!ret,
   "[SetMclkOD] failed to set od memclk!",
   return ret);

 /* retrieve updated memclk table */
 ret = vega20_setup_memclk_dpm_table(hwmgr);
 PP_ASSERT_WITH_CODE(!ret,
   "[SetMclkOD] failed to refresh memclk table!",
   return ret);

 return 0;
}

static void vega20_populate_umdpstate_clocks(struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
 struct vega20_single_dpm_table *gfx_table = &(data->dpm_table.gfx_table);
 struct vega20_single_dpm_table *mem_table = &(data->dpm_table.mem_table);

 if (gfx_table->count > VEGA20_UMD_PSTATE_GFXCLK_LEVEL &&
     mem_table->count > VEGA20_UMD_PSTATE_MCLK_LEVEL) {
  hwmgr->pstate_sclk = gfx_table->dpm_levels[VEGA20_UMD_PSTATE_GFXCLK_LEVEL].value;
  hwmgr->pstate_mclk = mem_table->dpm_levels[VEGA20_UMD_PSTATE_MCLK_LEVEL].value;
 } else {
  hwmgr->pstate_sclk = gfx_table->dpm_levels[0].value;
  hwmgr->pstate_mclk = mem_table->dpm_levels[0].value;
 }

 hwmgr->pstate_sclk_peak = gfx_table->dpm_levels[gfx_table->count - 1].value;
 hwmgr->pstate_mclk_peak = mem_table->dpm_levels[mem_table->count - 1].value;
}

static int vega20_get_max_sustainable_clock(struct pp_hwmgr *hwmgr,
  PP_Clock *clock, PPCLK_e clock_select)
{
 int ret = 0;

 PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
   PPSMC_MSG_GetDcModeMaxDpmFreq,
   (clock_select << 16),
   clock)) == 0,
   "[GetMaxSustainableClock] Failed to get max DC clock from SMC!",
   return ret);

 /* if DC limit is zero, return AC limit */
 if (*clock == 0) {
  PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
   PPSMC_MSG_GetMaxDpmFreq,
   (clock_select << 16),
   clock)) == 0,
   "[GetMaxSustainableClock] failed to get max AC clock from SMC!",
   return ret);
 }

 return 0;
}

static int vega20_init_max_sustainable_clocks(struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data =
  (struct vega20_hwmgr *)(hwmgr->backend);
 struct vega20_max_sustainable_clocks *max_sustainable_clocks =
  &(data->max_sustainable_clocks);
 int ret = 0;

 max_sustainable_clocks->uclock = data->vbios_boot_state.mem_clock / 100;
 max_sustainable_clocks->soc_clock = data->vbios_boot_state.soc_clock / 100;
 max_sustainable_clocks->dcef_clock = data->vbios_boot_state.dcef_clock / 100;
 max_sustainable_clocks->display_clock = 0xFFFFFFFF;
 max_sustainable_clocks->phy_clock = 0xFFFFFFFF;
 max_sustainable_clocks->pixel_clock = 0xFFFFFFFF;

 if (data->smu_features[GNLD_DPM_UCLK].enabled)
  PP_ASSERT_WITH_CODE((ret = vega20_get_max_sustainable_clock(hwmgr,
    &(max_sustainable_clocks->uclock),
    PPCLK_UCLK)) == 0,
    "[InitMaxSustainableClocks] failed to get max UCLK from SMC!",
    return ret);

 if (data->smu_features[GNLD_DPM_SOCCLK].enabled)
  PP_ASSERT_WITH_CODE((ret = vega20_get_max_sustainable_clock(hwmgr,
    &(max_sustainable_clocks->soc_clock),
    PPCLK_SOCCLK)) == 0,
    "[InitMaxSustainableClocks] failed to get max SOCCLK from SMC!",
    return ret);

 if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
  PP_ASSERT_WITH_CODE((ret = vega20_get_max_sustainable_clock(hwmgr,
    &(max_sustainable_clocks->dcef_clock),
    PPCLK_DCEFCLK)) == 0,
    "[InitMaxSustainableClocks] failed to get max DCEFCLK from SMC!",
    return ret);
  PP_ASSERT_WITH_CODE((ret = vega20_get_max_sustainable_clock(hwmgr,
    &(max_sustainable_clocks->display_clock),
    PPCLK_DISPCLK)) == 0,
    "[InitMaxSustainableClocks] failed to get max DISPCLK from SMC!",
    return ret);
  PP_ASSERT_WITH_CODE((ret = vega20_get_max_sustainable_clock(hwmgr,
    &(max_sustainable_clocks->phy_clock),
    PPCLK_PHYCLK)) == 0,
    "[InitMaxSustainableClocks] failed to get max PHYCLK from SMC!",
    return ret);
  PP_ASSERT_WITH_CODE((ret = vega20_get_max_sustainable_clock(hwmgr,
    &(max_sustainable_clocks->pixel_clock),
    PPCLK_PIXCLK)) == 0,
    "[InitMaxSustainableClocks] failed to get max PIXCLK from SMC!",
    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 vega20_enable_mgpu_fan_boost(struct pp_hwmgr *hwmgr)
{
 int result;

 result = smum_send_msg_to_smc(hwmgr,
  PPSMC_MSG_SetMGpuFanBoostLimitRpm,
  NULL);
 PP_ASSERT_WITH_CODE(!result,
   "[EnableMgpuFan] Failed to enable mgpu fan boost!",
   return result);

 return 0;
}

static void vega20_init_powergate_state(struct pp_hwmgr *hwmgr)
{
 struct vega20_hwmgr *data =
  (struct vega20_hwmgr *)(hwmgr->backend);

 data->uvd_power_gated = true;
 data->vce_power_gated = true;
}

static int vega20_enable_dpm_tasks(struct pp_hwmgr *hwmgr)
{
 int result = 0;

 smum_send_msg_to_smc_with_parameter(hwmgr,
   PPSMC_MSG_NumOfDisplays, 0, NULL);

 result = vega20_set_allowed_featuresmask(hwmgr);
 PP_ASSERT_WITH_CODE(!result,
   "[EnableDPMTasks] Failed to set allowed featuresmask!\n",
   return result);

 result = vega20_init_smc_table(hwmgr);
 PP_ASSERT_WITH_CODE(!result,
   "[EnableDPMTasks] Failed to initialize SMC table!",
   return result);

 result = vega20_run_btc(hwmgr);
 PP_ASSERT_WITH_CODE(!result,
   "[EnableDPMTasks] Failed to run btc!",
   return result);

 result = vega20_run_btc_afll(hwmgr);
 PP_ASSERT_WITH_CODE(!result,
   "[EnableDPMTasks] Failed to run btc afll!",
   return result);

 result = vega20_enable_all_smu_features(hwmgr);
 PP_ASSERT_WITH_CODE(!result,
   "[EnableDPMTasks] Failed to enable all smu features!",
   return result);

 result = vega20_override_pcie_parameters(hwmgr);
 PP_ASSERT_WITH_CODE(!result,
   "[EnableDPMTasks] Failed to override pcie parameters!",
   return result);

 result = vega20_notify_smc_display_change(hwmgr);
 PP_ASSERT_WITH_CODE(!result,
   "[EnableDPMTasks] Failed to notify smc display change!",
   return result);

 result = vega20_send_clock_ratio(hwmgr);
 PP_ASSERT_WITH_CODE(!result,
   "[EnableDPMTasks] Failed to send clock ratio!",
   return result);

 /* Initialize UVD/VCE powergating state */
 vega20_init_powergate_state(hwmgr);

 result = vega20_setup_default_dpm_tables(hwmgr);
 PP_ASSERT_WITH_CODE(!result,
   "[EnableDPMTasks] Failed to setup default DPM tables!",
   return result);

 result = vega20_init_max_sustainable_clocks(hwmgr);
 PP_ASSERT_WITH_CODE(!result,
   "[EnableDPMTasks] Failed to get maximum sustainable clocks!",
   return result);

 result = vega20_power_control_set_level(hwmgr);
 PP_ASSERT_WITH_CODE(!result,
   "[EnableDPMTasks] Failed to power control set level!",
   return result);

 result = vega20_od8_initialize_default_settings(hwmgr);
 PP_ASSERT_WITH_CODE(!result,
   "[EnableDPMTasks] Failed to initialize odn settings!",
   return result);

 vega20_populate_umdpstate_clocks(hwmgr);

 result = smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetPptLimit,
   POWER_SOURCE_AC << 16, &hwmgr->default_power_limit);
 PP_ASSERT_WITH_CODE(!result,
   "[GetPptLimit] get default PPT limit failed!",
   return result);
 hwmgr->power_limit =
  hwmgr->default_power_limit;

 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;

 PP_ASSERT_WITH_CODE(table != NULL,
   "[FindHighestDPMLevel] DPM Table does not exist!",
   return 0);
 PP_ASSERT_WITH_CODE(table->count > 0,
   "[FindHighestDPMLevel] DPM Table has no entry!",
   return 0);
 PP_ASSERT_WITH_CODE(table->count <= MAX_REGULAR_DPM_NUMBER,
   "[FindHighestDPMLevel] DPM Table has too many entries!",
   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_upload_dpm_min_level(struct pp_hwmgr *hwmgr, uint32_t feature_mask)
{
 struct vega20_hwmgr *data =
   (struct vega20_hwmgr *)(hwmgr->backend);
 uint32_t min_freq;
 int ret = 0;

 if (data->smu_features[GNLD_DPM_GFXCLK].enabled &&
    (feature_mask & FEATURE_DPM_GFXCLK_MASK)) {
  min_freq = data->dpm_table.gfx_table.dpm_state.soft_min_level;
  PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
     hwmgr, PPSMC_MSG_SetSoftMinByFreq,
     (PPCLK_GFXCLK << 16) | (min_freq & 0xffff),
     NULL)),
     "Failed to set soft min gfxclk !",
     return ret);
 }

 if (data->smu_features[GNLD_DPM_UCLK].enabled &&
    (feature_mask & FEATURE_DPM_UCLK_MASK)) {
  min_freq = data->dpm_table.mem_table.dpm_state.soft_min_level;
  PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
     hwmgr, PPSMC_MSG_SetSoftMinByFreq,
     (PPCLK_UCLK << 16) | (min_freq & 0xffff),
     NULL)),
     "Failed to set soft min memclk !",
     return ret);
 }

 if (data->smu_features[GNLD_DPM_UVD].enabled &&
    (feature_mask & FEATURE_DPM_UVD_MASK)) {
  min_freq = data->dpm_table.vclk_table.dpm_state.soft_min_level;

  PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
     hwmgr, PPSMC_MSG_SetSoftMinByFreq,
     (PPCLK_VCLK << 16) | (min_freq & 0xffff),
     NULL)),
     "Failed to set soft min vclk!",
     return ret);

  min_freq = data->dpm_table.dclk_table.dpm_state.soft_min_level;

  PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
     hwmgr, PPSMC_MSG_SetSoftMinByFreq,
     (PPCLK_DCLK << 16) | (min_freq & 0xffff),
     NULL)),
     "Failed to set soft min dclk!",
     return ret);
 }

 if (data->smu_features[GNLD_DPM_VCE].enabled &&
    (feature_mask & FEATURE_DPM_VCE_MASK)) {
  min_freq = data->dpm_table.eclk_table.dpm_state.soft_min_level;

  PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
     hwmgr, PPSMC_MSG_SetSoftMinByFreq,
     (PPCLK_ECLK << 16) | (min_freq & 0xffff),
     NULL)),
     "Failed to set soft min eclk!",
     return ret);
 }

 if (data->smu_features[GNLD_DPM_SOCCLK].enabled &&
    (feature_mask & FEATURE_DPM_SOCCLK_MASK)) {
  min_freq = data->dpm_table.soc_table.dpm_state.soft_min_level;

  PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
     hwmgr, PPSMC_MSG_SetSoftMinByFreq,
     (PPCLK_SOCCLK << 16) | (min_freq & 0xffff),
     NULL)),
     "Failed to set soft min socclk!",
     return ret);
 }

 if (data->smu_features[GNLD_DPM_FCLK].enabled &&
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=94 G=95

¤ Dauer der Verarbeitung: 0.17 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.