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 182 kB image not shown  

Quelle  vega10_hwmgr.c   Sprache: C

 
/*
 * Copyright 2016 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/pci.h>
#include <linux/slab.h>

#include "hwmgr.h"
#include "amd_powerplay.h"
#include "hardwaremanager.h"
#include "ppatomfwctrl.h"
#include "atomfirmware.h"
#include "cgs_common.h"
#include "vega10_powertune.h"
#include "smu9.h"
#include "smu9_driver_if.h"
#include "vega10_inc.h"
#include "soc15_common.h"
#include "pppcielanes.h"
#include "vega10_hwmgr.h"
#include "vega10_smumgr.h"
#include "vega10_processpptables.h"
#include "vega10_pptable.h"
#include "vega10_thermal.h"
#include "pp_debug.h"
#include "amd_pcie_helpers.h"
#include "ppinterrupt.h"
#include "pp_overdriver.h"
#include "pp_thermal.h"
#include "vega10_baco.h"

#include "smuio/smuio_9_0_offset.h"
#include "smuio/smuio_9_0_sh_mask.h"

#define smnPCIE_LC_SPEED_CNTL   0x11140290
#define smnPCIE_LC_LINK_WIDTH_CNTL  0x11140288

#define HBM_MEMORY_CHANNEL_WIDTH    128

static const uint32_t channel_number[] = {1, 2, 0, 4, 0, 8, 0, 16, 2};

#define mmDF_CS_AON0_DramBaseAddress0                                                                  0x0044
#define mmDF_CS_AON0_DramBaseAddress0_BASE_IDX                                                         0

//DF_CS_AON0_DramBaseAddress0
#define DF_CS_AON0_DramBaseAddress0__AddrRngVal__SHIFT                                                        0x0
#define DF_CS_AON0_DramBaseAddress0__LgcyMmioHoleEn__SHIFT                                                    0x1
#define DF_CS_AON0_DramBaseAddress0__IntLvNumChan__SHIFT                                                      0x4
#define DF_CS_AON0_DramBaseAddress0__IntLvAddrSel__SHIFT                                                      0x8
#define DF_CS_AON0_DramBaseAddress0__DramBaseAddr__SHIFT                                                      0xc
#define DF_CS_AON0_DramBaseAddress0__AddrRngVal_MASK                                                          0x00000001L
#define DF_CS_AON0_DramBaseAddress0__LgcyMmioHoleEn_MASK                                                      0x00000002L
#define DF_CS_AON0_DramBaseAddress0__IntLvNumChan_MASK                                                        0x000000F0L
#define DF_CS_AON0_DramBaseAddress0__IntLvAddrSel_MASK                                                        0x00000700L
#define DF_CS_AON0_DramBaseAddress0__DramBaseAddr_MASK                                                        0xFFFFF000L

typedef enum {
 CLK_SMNCLK = 0,
 CLK_SOCCLK,
 CLK_MP0CLK,
 CLK_MP1CLK,
 CLK_LCLK,
 CLK_DCEFCLK,
 CLK_VCLK,
 CLK_DCLK,
 CLK_ECLK,
 CLK_UCLK,
 CLK_GFXCLK,
 CLK_COUNT,
} CLOCK_ID_e;

static const ULONG PhwVega10_Magic = (ULONG)(PHM_VIslands_Magic);

static struct vega10_power_state *cast_phw_vega10_power_state(
      struct pp_hw_power_state *hw_ps)
{
 PP_ASSERT_WITH_CODE((PhwVega10_Magic == hw_ps->magic),
    "Invalid Powerstate Type!",
     return NULL;);

 return (struct vega10_power_state *)hw_ps;
}

static const struct vega10_power_state *cast_const_phw_vega10_power_state(
     const struct pp_hw_power_state *hw_ps)
{
 PP_ASSERT_WITH_CODE((PhwVega10_Magic == hw_ps->magic),
    "Invalid Powerstate Type!",
     return NULL;);

 return (const struct vega10_power_state *)hw_ps;
}

static void vega10_set_default_registry_data(struct pp_hwmgr *hwmgr)
{
 struct vega10_hwmgr *data = hwmgr->backend;

 data->registry_data.sclk_dpm_key_disabled =
   hwmgr->feature_mask & PP_SCLK_DPM_MASK ? false : true;
 data->registry_data.socclk_dpm_key_disabled =
   hwmgr->feature_mask & PP_SOCCLK_DPM_MASK ? false : true;
 data->registry_data.mclk_dpm_key_disabled =
   hwmgr->feature_mask & PP_MCLK_DPM_MASK ? false : true;
 data->registry_data.pcie_dpm_key_disabled =
   hwmgr->feature_mask & PP_PCIE_DPM_MASK ? false : true;

 data->registry_data.dcefclk_dpm_key_disabled =
   hwmgr->feature_mask & PP_DCEFCLK_DPM_MASK ? false : true;

 if (hwmgr->feature_mask & PP_POWER_CONTAINMENT_MASK) {
  data->registry_data.power_containment_support = 1;
  data->registry_data.enable_pkg_pwr_tracking_feature = 1;
  data->registry_data.enable_tdc_limit_feature = 1;
 }

 data->registry_data.clock_stretcher_support =
   hwmgr->feature_mask & PP_CLOCK_STRETCH_MASK ? true : false;

 data->registry_data.ulv_support =
   hwmgr->feature_mask & PP_ULV_MASK ? true : false;

 data->registry_data.sclk_deep_sleep_support =
   hwmgr->feature_mask & PP_SCLK_DEEP_SLEEP_MASK ? true : false;

 data->registry_data.disable_water_mark = 0;

 data->registry_data.fan_control_support = 1;
 data->registry_data.thermal_support = 1;
 data->registry_data.fw_ctf_enabled = 1;

 data->registry_data.avfs_support =
  hwmgr->feature_mask & PP_AVFS_MASK ? true : false;
 data->registry_data.led_dpm_enabled = 1;

 data->registry_data.vr0hot_enabled = 1;
 data->registry_data.vr1hot_enabled = 1;
 data->registry_data.regulator_hot_gpio_support = 1;

 data->registry_data.didt_support = 1;
 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->display_voltage_mode = PPVEGA10_VEGA10DISPLAYVOLTAGEMODE_DFLT;
 data->dcef_clk_quad_eqn_a = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
 data->dcef_clk_quad_eqn_b = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
 data->dcef_clk_quad_eqn_c = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
 data->disp_clk_quad_eqn_a = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
 data->disp_clk_quad_eqn_b = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
 data->disp_clk_quad_eqn_c = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
 data->pixel_clk_quad_eqn_a = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
 data->pixel_clk_quad_eqn_b = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
 data->pixel_clk_quad_eqn_c = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
 data->phy_clk_quad_eqn_a = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
 data->phy_clk_quad_eqn_b = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
 data->phy_clk_quad_eqn_c = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;

 data->gfxclk_average_alpha = PPVEGA10_VEGA10GFXCLKAVERAGEALPHA_DFLT;
 data->socclk_average_alpha = PPVEGA10_VEGA10SOCCLKAVERAGEALPHA_DFLT;
 data->uclk_average_alpha = PPVEGA10_VEGA10UCLKCLKAVERAGEALPHA_DFLT;
 data->gfx_activity_average_alpha = PPVEGA10_VEGA10GFXACTIVITYAVERAGEALPHA_DFLT;
}

static int vega10_set_features_platform_caps(struct pp_hwmgr *hwmgr)
{
 struct vega10_hwmgr *data = hwmgr->backend;
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)hwmgr->pptable;
 struct amdgpu_device *adev = hwmgr->adev;

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

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

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

 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);

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

 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);

 /* 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);
 }

 if (data->registry_data.power_containment_support)
  phm_cap_set(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_PowerContainment);
 phm_cap_set(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_CAC);

 if (table_info->tdp_table->usClockStretchAmount &&
   data->registry_data.clock_stretcher_support)
  phm_cap_set(hwmgr->platform_descriptor.platformCaps,
    PHM_PlatformCaps_ClockStretcher);

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

 phm_cap_set(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_UVDDPM);
 phm_cap_set(hwmgr->platform_descriptor.platformCaps,
   PHM_PlatformCaps_VCEDPM);

 return 0;
}

static int vega10_odn_initial_default_setting(struct pp_hwmgr *hwmgr)
{
 struct vega10_hwmgr *data = hwmgr->backend;
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)(hwmgr->pptable);
 struct vega10_odn_dpm_table *odn_table = &(data->odn_dpm_table);
 struct vega10_odn_vddc_lookup_table *od_lookup_table;
 struct phm_ppt_v1_voltage_lookup_table *vddc_lookup_table;
 struct phm_ppt_v1_clock_voltage_dependency_table *dep_table[3];
 struct phm_ppt_v1_clock_voltage_dependency_table *od_table[3];
 struct pp_atomfwctrl_avfs_parameters avfs_params = {0};
 uint32_t i;
 int result;

 result = pp_atomfwctrl_get_avfs_information(hwmgr, &avfs_params);
 if (!result) {
  data->odn_dpm_table.max_vddc = avfs_params.ulMaxVddc;
  data->odn_dpm_table.min_vddc = avfs_params.ulMinVddc;
 }

 od_lookup_table = &odn_table->vddc_lookup_table;
 vddc_lookup_table = table_info->vddc_lookup_table;

 for (i = 0; i < vddc_lookup_table->count; i++)
  od_lookup_table->entries[i].us_vdd = vddc_lookup_table->entries[i].us_vdd;

 od_lookup_table->count = vddc_lookup_table->count;

 dep_table[0] = table_info->vdd_dep_on_sclk;
 dep_table[1] = table_info->vdd_dep_on_mclk;
 dep_table[2] = table_info->vdd_dep_on_socclk;
 od_table[0] = (struct phm_ppt_v1_clock_voltage_dependency_table *)&odn_table->vdd_dep_on_sclk;
 od_table[1] = (struct phm_ppt_v1_clock_voltage_dependency_table *)&odn_table->vdd_dep_on_mclk;
 od_table[2] = (struct phm_ppt_v1_clock_voltage_dependency_table *)&odn_table->vdd_dep_on_socclk;

 for (i = 0; i < 3; i++)
  smu_get_voltage_dependency_table_ppt_v1(dep_table[i], od_table[i]);

 if (odn_table->max_vddc == 0 || odn_table->max_vddc > 2000)
  odn_table->max_vddc = dep_table[0]->entries[dep_table[0]->count - 1].vddc;
 if (odn_table->min_vddc == 0 || odn_table->min_vddc > 2000)
  odn_table->min_vddc = dep_table[0]->entries[0].vddc;

 i = od_table[2]->count - 1;
 od_table[2]->entries[i].clk = hwmgr->platform_descriptor.overdriveLimit.memoryClock > od_table[2]->entries[i].clk ?
     hwmgr->platform_descriptor.overdriveLimit.memoryClock :
     od_table[2]->entries[i].clk;
 od_table[2]->entries[i].vddc = odn_table->max_vddc > od_table[2]->entries[i].vddc ?
     odn_table->max_vddc :
     od_table[2]->entries[i].vddc;

 return 0;
}

static int vega10_init_dpm_defaults(struct pp_hwmgr *hwmgr)
{
 struct vega10_hwmgr *data = hwmgr->backend;
 uint32_t sub_vendor_id, hw_revision;
 uint32_t top32, bottom32;
 struct amdgpu_device *adev = hwmgr->adev;
 int ret, i;

 vega10_initialize_power_tune_defaults(hwmgr);

 for (i = 0; i < GNLD_FEATURES_MAX; i++) {
  data->smu_features[i].smu_feature_id = 0xffff;
  data->smu_features[i].smu_feature_bitmap = 1 << i;
  data->smu_features[i].enabled = false;
  data->smu_features[i].supported = false;
 }

 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_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_ULV].smu_feature_id =
   FEATURE_ULV_BIT;
 data->smu_features[GNLD_AVFS].smu_feature_id =
   FEATURE_AVFS_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_ACG].smu_feature_id = FEATURE_ACG_BIT;
 data->smu_features[GNLD_DIDT].smu_feature_id = FEATURE_GFX_EDC_BIT;
 data->smu_features[GNLD_PCC_LIMIT].smu_feature_id = FEATURE_PCC_LIMIT_CONTROL_BIT;

 if (!data->registry_data.prefetcher_dpm_key_disabled)
  data->smu_features[GNLD_DPM_PREFETCHER].supported = true;

 if (!data->registry_data.sclk_dpm_key_disabled)
  data->smu_features[GNLD_DPM_GFXCLK].supported = true;

 if (!data->registry_data.mclk_dpm_key_disabled)
  data->smu_features[GNLD_DPM_UCLK].supported = true;

 if (!data->registry_data.socclk_dpm_key_disabled)
  data->smu_features[GNLD_DPM_SOCCLK].supported = true;

 if (PP_CAP(PHM_PlatformCaps_UVDDPM))
  data->smu_features[GNLD_DPM_UVD].supported = true;

 if (PP_CAP(PHM_PlatformCaps_VCEDPM))
  data->smu_features[GNLD_DPM_VCE].supported = true;

 data->smu_features[GNLD_DPM_LINK].supported = true;

 if (!data->registry_data.dcefclk_dpm_key_disabled)
  data->smu_features[GNLD_DPM_DCEFCLK].supported = true;

 if (PP_CAP(PHM_PlatformCaps_SclkDeepSleep) &&
     data->registry_data.sclk_deep_sleep_support) {
  data->smu_features[GNLD_DS_GFXCLK].supported = true;
  data->smu_features[GNLD_DS_SOCCLK].supported = true;
  data->smu_features[GNLD_DS_LCLK].supported = true;
  data->smu_features[GNLD_DS_DCEFCLK].supported = true;
 }

 if (data->registry_data.enable_pkg_pwr_tracking_feature)
  data->smu_features[GNLD_PPT].supported = true;

 if (data->registry_data.enable_tdc_limit_feature)
  data->smu_features[GNLD_TDC].supported = true;

 if (data->registry_data.thermal_support)
  data->smu_features[GNLD_THERMAL].supported = true;

 if (data->registry_data.fan_control_support)
  data->smu_features[GNLD_FAN_CONTROL].supported = true;

 if (data->registry_data.fw_ctf_enabled)
  data->smu_features[GNLD_FW_CTF].supported = true;

 if (data->registry_data.avfs_support)
  data->smu_features[GNLD_AVFS].supported = true;

 if (data->registry_data.led_dpm_enabled)
  data->smu_features[GNLD_LED_DISPLAY].supported = true;

 if (data->registry_data.vr1hot_enabled)
  data->smu_features[GNLD_VR1HOT].supported = true;

 if (data->registry_data.vr0hot_enabled)
  data->smu_features[GNLD_VR0HOT].supported = true;

 ret = smum_send_msg_to_smc(hwmgr,
   PPSMC_MSG_GetSmuVersion,
   &hwmgr->smu_version);
 if (ret)
  return ret;

  /* ACG firmware has major version 5 */
 if ((hwmgr->smu_version & 0xff000000) == 0x5000000)
  data->smu_features[GNLD_ACG].supported = true;
 if (data->registry_data.didt_support)
  data->smu_features[GNLD_DIDT].supported = true;

 hw_revision = adev->pdev->revision;
 sub_vendor_id = adev->pdev->subsystem_vendor;

 if ((hwmgr->chip_id == 0x6862 ||
  hwmgr->chip_id == 0x6861 ||
  hwmgr->chip_id == 0x6868) &&
  (hw_revision == 0) &&
  (sub_vendor_id != 0x1002))
  data->smu_features[GNLD_PCC_LIMIT].supported = 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;
}

#ifdef PPLIB_VEGA10_EVV_SUPPORT
static int vega10_get_socclk_for_voltage_evv(struct pp_hwmgr *hwmgr,
 phm_ppt_v1_voltage_lookup_table *lookup_table,
 uint16_t virtual_voltage_id, int32_t *socclk)
{
 uint8_t entry_id;
 uint8_t voltage_id;
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)(hwmgr->pptable);

 PP_ASSERT_WITH_CODE(lookup_table->count != 0,
   "Lookup table is empty",
   return -EINVAL);

 /* search for leakage voltage ID 0xff01 ~ 0xff08 and sclk */
 for (entry_id = 0; entry_id < table_info->vdd_dep_on_sclk->count; entry_id++) {
  voltage_id = table_info->vdd_dep_on_socclk->entries[entry_id].vddInd;
  if (lookup_table->entries[voltage_id].us_vdd == virtual_voltage_id)
   break;
 }

 PP_ASSERT_WITH_CODE(entry_id < table_info->vdd_dep_on_socclk->count,
   "Can't find requested voltage id in vdd_dep_on_socclk table!",
   return -EINVAL);

 *socclk = table_info->vdd_dep_on_socclk->entries[entry_id].clk;

 return 0;
}

#define ATOM_VIRTUAL_VOLTAGE_ID0             0xff01
/**
 * vega10_get_evv_voltages - Get Leakage VDDC based on leakage ID.
 *
 * @hwmgr:  the address of the powerplay hardware manager.
 * return:  always 0.
 */

static int vega10_get_evv_voltages(struct pp_hwmgr *hwmgr)
{
 struct vega10_hwmgr *data = hwmgr->backend;
 uint16_t vv_id;
 uint32_t vddc = 0;
 uint16_t i, j;
 uint32_t sclk = 0;
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)hwmgr->pptable;
 struct phm_ppt_v1_clock_voltage_dependency_table *socclk_table =
   table_info->vdd_dep_on_socclk;
 int result;

 for (i = 0; i < VEGA10_MAX_LEAKAGE_COUNT; i++) {
  vv_id = ATOM_VIRTUAL_VOLTAGE_ID0 + i;

  if (!vega10_get_socclk_for_voltage_evv(hwmgr,
    table_info->vddc_lookup_table, vv_id, &sclk)) {
   if (PP_CAP(PHM_PlatformCaps_ClockStretcher)) {
    for (j = 1; j < socclk_table->count; j++) {
     if (socclk_table->entries[j].clk == sclk &&
       socclk_table->entries[j].cks_enable == 0) {
      sclk += 5000;
      break;
     }
    }
   }

   PP_ASSERT_WITH_CODE(!atomctrl_get_voltage_evv_on_sclk_ai(hwmgr,
     VOLTAGE_TYPE_VDDC, sclk, vv_id, &vddc),
     "Error retrieving EVV voltage value!",
     continue);


   /* need to make sure vddc is less than 2v or else, it could burn the ASIC. */
   PP_ASSERT_WITH_CODE((vddc < 2000 && vddc != 0),
     "Invalid VDDC value", result = -EINVAL;);

   /* the voltage should not be zero nor equal to leakage ID */
   if (vddc != 0 && vddc != vv_id) {
    data->vddc_leakage.actual_voltage[data->vddc_leakage.count] = (uint16_t)(vddc/100);
    data->vddc_leakage.leakage_id[data->vddc_leakage.count] = vv_id;
    data->vddc_leakage.count++;
   }
  }
 }

 return 0;
}

/**
 * vega10_patch_with_vdd_leakage - Change virtual leakage voltage to actual value.
 *
 * @hwmgr:         the address of the powerplay hardware manager.
 * @voltage:       pointer to changing voltage
 * @leakage_table: pointer to leakage table
 */

static void vega10_patch_with_vdd_leakage(struct pp_hwmgr *hwmgr,
  uint16_t *voltage, struct vega10_leakage_voltage *leakage_table)
{
 uint32_t index;

 /* search for leakage voltage ID 0xff01 ~ 0xff08 */
 for (index = 0; index < leakage_table->count; index++) {
  /* if this voltage matches a leakage voltage ID */
  /* patch with actual leakage voltage */
  if (leakage_table->leakage_id[index] == *voltage) {
   *voltage = leakage_table->actual_voltage[index];
   break;
  }
 }

 if (*voltage > ATOM_VIRTUAL_VOLTAGE_ID0)
  pr_info("Voltage value looks like a Leakage ID but it's not patched\n");
}

/**
 * vega10_patch_lookup_table_with_leakage - Patch voltage lookup table by EVV leakages.
 *
 * @hwmgr:         the address of the powerplay hardware manager.
 * @lookup_table:  pointer to voltage lookup table
 * @leakage_table: pointer to leakage table
 * return:         always 0
 */

static int vega10_patch_lookup_table_with_leakage(struct pp_hwmgr *hwmgr,
  phm_ppt_v1_voltage_lookup_table *lookup_table,
  struct vega10_leakage_voltage *leakage_table)
{
 uint32_t i;

 for (i = 0; i < lookup_table->count; i++)
  vega10_patch_with_vdd_leakage(hwmgr,
    &lookup_table->entries[i].us_vdd, leakage_table);

 return 0;
}

static int vega10_patch_clock_voltage_limits_with_vddc_leakage(
  struct pp_hwmgr *hwmgr, struct vega10_leakage_voltage *leakage_table,
  uint16_t *vddc)
{
 vega10_patch_with_vdd_leakage(hwmgr, (uint16_t *)vddc, leakage_table);

 return 0;
}
#endif

static int vega10_patch_voltage_dependency_tables_with_lookup_table(
  struct pp_hwmgr *hwmgr)
{
 uint8_t entry_id, voltage_id;
 unsigned i;
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)(hwmgr->pptable);
 struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
   table_info->mm_dep_table;
 struct phm_ppt_v1_clock_voltage_dependency_table *mclk_table =
   table_info->vdd_dep_on_mclk;

 for (i = 0; i < 6; i++) {
  struct phm_ppt_v1_clock_voltage_dependency_table *vdt;
  switch (i) {
   case 0: vdt = table_info->vdd_dep_on_socclk; break;
   case 1: vdt = table_info->vdd_dep_on_sclk; break;
   case 2: vdt = table_info->vdd_dep_on_dcefclk; break;
   case 3: vdt = table_info->vdd_dep_on_pixclk; break;
   case 4: vdt = table_info->vdd_dep_on_dispclk; break;
   case 5: vdt = table_info->vdd_dep_on_phyclk; break;
  }

  for (entry_id = 0; entry_id < vdt->count; entry_id++) {
   voltage_id = vdt->entries[entry_id].vddInd;
   vdt->entries[entry_id].vddc =
     table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
  }
 }

 for (entry_id = 0; entry_id < mm_table->count; ++entry_id) {
  voltage_id = mm_table->entries[entry_id].vddcInd;
  mm_table->entries[entry_id].vddc =
   table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
 }

 for (entry_id = 0; entry_id < mclk_table->count; ++entry_id) {
  voltage_id = mclk_table->entries[entry_id].vddInd;
  mclk_table->entries[entry_id].vddc =
    table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
  voltage_id = mclk_table->entries[entry_id].vddciInd;
  mclk_table->entries[entry_id].vddci =
    table_info->vddci_lookup_table->entries[voltage_id].us_vdd;
  voltage_id = mclk_table->entries[entry_id].mvddInd;
  mclk_table->entries[entry_id].mvdd =
    table_info->vddmem_lookup_table->entries[voltage_id].us_vdd;
 }


 return 0;

}

static int vega10_sort_lookup_table(struct pp_hwmgr *hwmgr,
  struct phm_ppt_v1_voltage_lookup_table *lookup_table)
{
 uint32_t table_size, i, j;

 PP_ASSERT_WITH_CODE(lookup_table && lookup_table->count,
  "Lookup table is empty"return -EINVAL);

 table_size = lookup_table->count;

 /* Sorting voltages */
 for (i = 0; i < table_size - 1; i++) {
  for (j = i + 1; j > 0; j--) {
   if (lookup_table->entries[j].us_vdd <
     lookup_table->entries[j - 1].us_vdd) {
    swap(lookup_table->entries[j - 1],
         lookup_table->entries[j]);
   }
  }
 }

 return 0;
}

static int vega10_complete_dependency_tables(struct pp_hwmgr *hwmgr)
{
 int result = 0;
 int tmp_result;
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)(hwmgr->pptable);
#ifdef PPLIB_VEGA10_EVV_SUPPORT
 struct vega10_hwmgr *data = hwmgr->backend;

 tmp_result = vega10_patch_lookup_table_with_leakage(hwmgr,
   table_info->vddc_lookup_table, &(data->vddc_leakage));
 if (tmp_result)
  result = tmp_result;

 tmp_result = vega10_patch_clock_voltage_limits_with_vddc_leakage(hwmgr,
   &(data->vddc_leakage), &table_info->max_clock_voltage_on_dc.vddc);
 if (tmp_result)
  result = tmp_result;
#endif

 tmp_result = vega10_patch_voltage_dependency_tables_with_lookup_table(hwmgr);
 if (tmp_result)
  result = tmp_result;

 tmp_result = vega10_sort_lookup_table(hwmgr, table_info->vddc_lookup_table);
 if (tmp_result)
  result = tmp_result;

 return result;
}

static int vega10_set_private_data_based_on_pptable(struct pp_hwmgr *hwmgr)
{
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)(hwmgr->pptable);
 struct phm_ppt_v1_clock_voltage_dependency_table *allowed_sclk_vdd_table =
   table_info->vdd_dep_on_socclk;
 struct phm_ppt_v1_clock_voltage_dependency_table *allowed_mclk_vdd_table =
   table_info->vdd_dep_on_mclk;

 PP_ASSERT_WITH_CODE(allowed_sclk_vdd_table,
  "VDD dependency on SCLK table is missing. This table is mandatory"return -EINVAL);
 PP_ASSERT_WITH_CODE(allowed_sclk_vdd_table->count >= 1,
  "VDD dependency on SCLK table is empty. This table is mandatory"return -EINVAL);

 PP_ASSERT_WITH_CODE(allowed_mclk_vdd_table,
  "VDD dependency on MCLK table is missing. This table is mandatory"return -EINVAL);
 PP_ASSERT_WITH_CODE(allowed_mclk_vdd_table->count >= 1,
  "VDD dependency on MCLK table is empty. This table is mandatory"return -EINVAL);

 table_info->max_clock_voltage_on_ac.sclk =
  allowed_sclk_vdd_table->entries[allowed_sclk_vdd_table->count - 1].clk;
 table_info->max_clock_voltage_on_ac.mclk =
  allowed_mclk_vdd_table->entries[allowed_mclk_vdd_table->count - 1].clk;
 table_info->max_clock_voltage_on_ac.vddc =
  allowed_sclk_vdd_table->entries[allowed_sclk_vdd_table->count - 1].vddc;
 table_info->max_clock_voltage_on_ac.vddci =
  allowed_mclk_vdd_table->entries[allowed_mclk_vdd_table->count - 1].vddci;

 hwmgr->dyn_state.max_clock_voltage_on_ac.sclk =
  table_info->max_clock_voltage_on_ac.sclk;
 hwmgr->dyn_state.max_clock_voltage_on_ac.mclk =
  table_info->max_clock_voltage_on_ac.mclk;
 hwmgr->dyn_state.max_clock_voltage_on_ac.vddc =
  table_info->max_clock_voltage_on_ac.vddc;
 hwmgr->dyn_state.max_clock_voltage_on_ac.vddci =
  table_info->max_clock_voltage_on_ac.vddci;

 return 0;
}

static int vega10_hwmgr_backend_fini(struct pp_hwmgr *hwmgr)
{
 kfree(hwmgr->dyn_state.vddc_dep_on_dal_pwrl);
 hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL;

 kfree(hwmgr->backend);
 hwmgr->backend = NULL;

 return 0;
}

static int vega10_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
{
 int result = 0;
 struct vega10_hwmgr *data;
 uint32_t config_telemetry = 0;
 struct pp_atomfwctrl_voltage_table vol_table;
 struct amdgpu_device *adev = hwmgr->adev;

 data = kzalloc(sizeof(struct vega10_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;

 vega10_set_default_registry_data(hwmgr);
 data->disable_dpm_mask = 0xff;

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

 /* VDDCR_SOC */
 if (pp_atomfwctrl_is_voltage_controlled_by_gpio_v4(hwmgr,
   VOLTAGE_TYPE_VDDC, VOLTAGE_OBJ_SVID2)) {
  if (!pp_atomfwctrl_get_voltage_table_v4(hwmgr,
    VOLTAGE_TYPE_VDDC, VOLTAGE_OBJ_SVID2,
    &vol_table)) {
   config_telemetry = ((vol_table.telemetry_slope << 8) & 0xff00) |
     (vol_table.telemetry_offset & 0xff);
   data->vddc_control = VEGA10_VOLTAGE_CONTROL_BY_SVID2;
  }
 } else {
  kfree(hwmgr->backend);
  hwmgr->backend = NULL;
  PP_ASSERT_WITH_CODE(false,
    "VDDCR_SOC is not SVID2!",
    return -1);
 }

 /* MVDDC */
 if (pp_atomfwctrl_is_voltage_controlled_by_gpio_v4(hwmgr,
   VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_SVID2)) {
  if (!pp_atomfwctrl_get_voltage_table_v4(hwmgr,
    VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_SVID2,
    &vol_table)) {
   config_telemetry |=
     ((vol_table.telemetry_slope << 24) & 0xff000000) |
     ((vol_table.telemetry_offset << 16) & 0xff0000);
   data->mvdd_control = VEGA10_VOLTAGE_CONTROL_BY_SVID2;
  }
 }

  /* VDDCI_MEM */
 if (PP_CAP(PHM_PlatformCaps_ControlVDDCI)) {
  if (pp_atomfwctrl_is_voltage_controlled_by_gpio_v4(hwmgr,
    VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_GPIO_LUT))
   data->vddci_control = VEGA10_VOLTAGE_CONTROL_BY_GPIO;
 }

 data->config_telemetry = config_telemetry;

 vega10_set_features_platform_caps(hwmgr);

 result = vega10_init_dpm_defaults(hwmgr);
 if (result)
  return result;

#ifdef PPLIB_VEGA10_EVV_SUPPORT
 /* Get leakage voltage based on leakage ID. */
 PP_ASSERT_WITH_CODE(!vega10_get_evv_voltages(hwmgr),
   "Get EVV Voltage Failed. Abort Driver loading!",
   return -1);
#endif

 /* Patch our voltage dependency table with actual leakage voltage
 * We need to perform leakage translation before it's used by other functions
 */

 vega10_complete_dependency_tables(hwmgr);

 /* Parse pptable data read from VBIOS */
 vega10_set_private_data_based_on_pptable(hwmgr);

 data->is_tlu_enabled = false;

 hwmgr->platform_descriptor.hardwareActivityPerformanceLevels =
   VEGA10_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;
 if (!hwmgr->not_vf)
  return result;

 /* Setup default Overdrive Fan control settings */
 data->odn_fan_table.target_fan_speed =
   hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanRPM;
 data->odn_fan_table.target_temperature =
   hwmgr->thermal_controller.
   advanceFanControlParameters.ucTargetTemperature;
 data->odn_fan_table.min_performance_clock =
   hwmgr->thermal_controller.advanceFanControlParameters.
   ulMinFanSCLKAcousticLimit;
 data->odn_fan_table.min_fan_limit =
   hwmgr->thermal_controller.
   advanceFanControlParameters.usFanPWMMinLimit *
   hwmgr->thermal_controller.fanInfo.ulMaxRPM / 100;

 data->mem_channels = (RREG32_SOC15(DF, 0, mmDF_CS_AON0_DramBaseAddress0) &
   DF_CS_AON0_DramBaseAddress0__IntLvNumChan_MASK) >>
   DF_CS_AON0_DramBaseAddress0__IntLvNumChan__SHIFT;
 PP_ASSERT_WITH_CODE(data->mem_channels < ARRAY_SIZE(channel_number),
   "Mem Channel Index Exceeded maximum!",
   return -EINVAL);

 return result;
}

static int vega10_init_sclk_threshold(struct pp_hwmgr *hwmgr)
{
 struct vega10_hwmgr *data = hwmgr->backend;

 data->low_sclk_interrupt_threshold = 0;

 return 0;
}

static int vega10_setup_dpm_led_config(struct pp_hwmgr *hwmgr)
{
 struct vega10_hwmgr *data = hwmgr->backend;
 PPTable_t *pp_table = &(data->smc_state_table.pp_table);

 struct pp_atomfwctrl_voltage_table table;
 uint8_t i, j;
 uint32_t mask = 0;
 uint32_t tmp;
 int32_t ret = 0;

 ret = pp_atomfwctrl_get_voltage_table_v4(hwmgr, VOLTAGE_TYPE_LEDDPM,
      VOLTAGE_OBJ_GPIO_LUT, &table);

 if (!ret) {
  tmp = table.mask_low;
  for (i = 0, j = 0; i < 32; i++) {
   if (tmp & 1) {
    mask |= (uint32_t)(i << (8 * j));
    if (++j >= 3)
     break;
   }
   tmp >>= 1;
  }
 }

 pp_table->LedPin0 = (uint8_t)(mask & 0xff);
 pp_table->LedPin1 = (uint8_t)((mask >> 8) & 0xff);
 pp_table->LedPin2 = (uint8_t)((mask >> 16) & 0xff);
 return 0;
}

static int vega10_setup_asic_task(struct pp_hwmgr *hwmgr)
{
 if (!hwmgr->not_vf)
  return 0;

 PP_ASSERT_WITH_CODE(!vega10_init_sclk_threshold(hwmgr),
   "Failed to init sclk threshold!",
   return -EINVAL);

 PP_ASSERT_WITH_CODE(!vega10_setup_dpm_led_config(hwmgr),
   "Failed to set up led dpm config!",
   return -EINVAL);

 smum_send_msg_to_smc_with_parameter(hwmgr,
    PPSMC_MSG_NumOfDisplays,
    0,
    NULL);

 return 0;
}

/**
 * vega10_trim_voltage_table - Remove repeated voltage values and create table with unique values.
 *
 * @hwmgr:      the address of the powerplay hardware manager.
 * @vol_table:  the pointer to changing voltage table
 * return:      0 in success
 */

static int vega10_trim_voltage_table(struct pp_hwmgr *hwmgr,
  struct pp_atomfwctrl_voltage_table *vol_table)
{
 uint32_t i, j;
 uint16_t vvalue;
 bool found = false;
 struct pp_atomfwctrl_voltage_table *table;

 PP_ASSERT_WITH_CODE(vol_table,
   "Voltage Table empty."return -EINVAL);
 table = kzalloc(sizeof(struct pp_atomfwctrl_voltage_table),
   GFP_KERNEL);

 if (!table)
  return -ENOMEM;

 table->mask_low = vol_table->mask_low;
 table->phase_delay = vol_table->phase_delay;

 for (i = 0; i < vol_table->count; i++) {
  vvalue = vol_table->entries[i].value;
  found = false;

  for (j = 0; j < table->count; j++) {
   if (vvalue == table->entries[j].value) {
    found = true;
    break;
   }
  }

  if (!found) {
   table->entries[table->count].value = vvalue;
   table->entries[table->count].smio_low =
     vol_table->entries[i].smio_low;
   table->count++;
  }
 }

 memcpy(vol_table, table, sizeof(struct pp_atomfwctrl_voltage_table));
 kfree(table);

 return 0;
}

static int vega10_get_mvdd_voltage_table(struct pp_hwmgr *hwmgr,
  phm_ppt_v1_clock_voltage_dependency_table *dep_table,
  struct pp_atomfwctrl_voltage_table *vol_table)
{
 int i;

 PP_ASSERT_WITH_CODE(dep_table->count,
   "Voltage Dependency Table empty.",
   return -EINVAL);

 vol_table->mask_low = 0;
 vol_table->phase_delay = 0;
 vol_table->count = dep_table->count;

 for (i = 0; i < vol_table->count; i++) {
  vol_table->entries[i].value = dep_table->entries[i].mvdd;
  vol_table->entries[i].smio_low = 0;
 }

 PP_ASSERT_WITH_CODE(!vega10_trim_voltage_table(hwmgr,
   vol_table),
   "Failed to trim MVDD Table!",
   return -1);

 return 0;
}

static int vega10_get_vddci_voltage_table(struct pp_hwmgr *hwmgr,
  phm_ppt_v1_clock_voltage_dependency_table *dep_table,
  struct pp_atomfwctrl_voltage_table *vol_table)
{
 uint32_t i;

 PP_ASSERT_WITH_CODE(dep_table->count,
   "Voltage Dependency Table empty.",
   return -EINVAL);

 vol_table->mask_low = 0;
 vol_table->phase_delay = 0;
 vol_table->count = dep_table->count;

 for (i = 0; i < dep_table->count; i++) {
  vol_table->entries[i].value = dep_table->entries[i].vddci;
  vol_table->entries[i].smio_low = 0;
 }

 PP_ASSERT_WITH_CODE(!vega10_trim_voltage_table(hwmgr, vol_table),
   "Failed to trim VDDCI table.",
   return -1);

 return 0;
}

static int vega10_get_vdd_voltage_table(struct pp_hwmgr *hwmgr,
  phm_ppt_v1_clock_voltage_dependency_table *dep_table,
  struct pp_atomfwctrl_voltage_table *vol_table)
{
 int i;

 PP_ASSERT_WITH_CODE(dep_table->count,
   "Voltage Dependency Table empty.",
   return -EINVAL);

 vol_table->mask_low = 0;
 vol_table->phase_delay = 0;
 vol_table->count = dep_table->count;

 for (i = 0; i < vol_table->count; i++) {
  vol_table->entries[i].value = dep_table->entries[i].vddc;
  vol_table->entries[i].smio_low = 0;
 }

 return 0;
}

/* ---- Voltage Tables ----
 * If the voltage table would be bigger than
 * what will fit into the state table on
 * the SMC keep only the higher entries.
 */

static void vega10_trim_voltage_table_to_fit_state_table(
  struct pp_hwmgr *hwmgr,
  uint32_t max_vol_steps,
  struct pp_atomfwctrl_voltage_table *vol_table)
{
 unsigned int i, diff;

 if (vol_table->count <= max_vol_steps)
  return;

 diff = vol_table->count - max_vol_steps;

 for (i = 0; i < max_vol_steps; i++)
  vol_table->entries[i] = vol_table->entries[i + diff];

 vol_table->count = max_vol_steps;
}

/**
 * vega10_construct_voltage_tables - Create Voltage Tables.
 *
 * @hwmgr:  the address of the powerplay hardware manager.
 * return:  always 0
 */

static int vega10_construct_voltage_tables(struct pp_hwmgr *hwmgr)
{
 struct vega10_hwmgr *data = hwmgr->backend;
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)hwmgr->pptable;
 int result;

 if (data->mvdd_control == VEGA10_VOLTAGE_CONTROL_BY_SVID2 ||
   data->mvdd_control == VEGA10_VOLTAGE_CONTROL_NONE) {
  result = vega10_get_mvdd_voltage_table(hwmgr,
    table_info->vdd_dep_on_mclk,
    &(data->mvdd_voltage_table));
  PP_ASSERT_WITH_CODE(!result,
    "Failed to retrieve MVDDC table!",
    return result);
 }

 if (data->vddci_control == VEGA10_VOLTAGE_CONTROL_NONE) {
  result = vega10_get_vddci_voltage_table(hwmgr,
    table_info->vdd_dep_on_mclk,
    &(data->vddci_voltage_table));
  PP_ASSERT_WITH_CODE(!result,
    "Failed to retrieve VDDCI_MEM table!",
    return result);
 }

 if (data->vddc_control == VEGA10_VOLTAGE_CONTROL_BY_SVID2 ||
   data->vddc_control == VEGA10_VOLTAGE_CONTROL_NONE) {
  result = vega10_get_vdd_voltage_table(hwmgr,
    table_info->vdd_dep_on_sclk,
    &(data->vddc_voltage_table));
  PP_ASSERT_WITH_CODE(!result,
    "Failed to retrieve VDDCR_SOC table!",
    return result);
 }

 PP_ASSERT_WITH_CODE(data->vddc_voltage_table.count <= 16,
   "Too many voltage values for VDDC. Trimming to fit state table.",
   vega10_trim_voltage_table_to_fit_state_table(hwmgr,
     16, &(data->vddc_voltage_table)));

 PP_ASSERT_WITH_CODE(data->vddci_voltage_table.count <= 16,
   "Too many voltage values for VDDCI. Trimming to fit state table.",
   vega10_trim_voltage_table_to_fit_state_table(hwmgr,
     16, &(data->vddci_voltage_table)));

 PP_ASSERT_WITH_CODE(data->mvdd_voltage_table.count <= 16,
   "Too many voltage values for MVDD. Trimming to fit state table.",
   vega10_trim_voltage_table_to_fit_state_table(hwmgr,
     16, &(data->mvdd_voltage_table)));


 return 0;
}

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

static void vega10_init_dpm_state(struct vega10_dpm_state *dpm_state)
{
 dpm_state->soft_min_level = 0xff;
 dpm_state->soft_max_level = 0xff;
 dpm_state->hard_min_level = 0xff;
 dpm_state->hard_max_level = 0xff;
}

static void vega10_setup_default_single_dpm_table(struct pp_hwmgr *hwmgr,
  struct vega10_single_dpm_table *dpm_table,
  struct phm_ppt_v1_clock_voltage_dependency_table *dep_table)
{
 int i;

 dpm_table->count = 0;

 for (i = 0; i < dep_table->count; i++) {
  if (i == 0 || dpm_table->dpm_levels[dpm_table->count - 1].value <=
    dep_table->entries[i].clk) {
   dpm_table->dpm_levels[dpm_table->count].value =
     dep_table->entries[i].clk;
   dpm_table->dpm_levels[dpm_table->count].enabled = true;
   dpm_table->count++;
  }
 }
}
static int vega10_setup_default_pcie_table(struct pp_hwmgr *hwmgr)
{
 struct vega10_hwmgr *data = hwmgr->backend;
 struct vega10_pcie_table *pcie_table = &(data->dpm_table.pcie_table);
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)(hwmgr->pptable);
 struct phm_ppt_v1_pcie_table *bios_pcie_table =
   table_info->pcie_table;
 uint32_t i;

 PP_ASSERT_WITH_CODE(bios_pcie_table->count,
   "Incorrect number of PCIE States from VBIOS!",
   return -1);

 for (i = 0; i < NUM_LINK_LEVELS; i++) {
  if (data->registry_data.pcieSpeedOverride)
   pcie_table->pcie_gen[i] =
     data->registry_data.pcieSpeedOverride;
  else
   pcie_table->pcie_gen[i] =
     bios_pcie_table->entries[i].gen_speed;

  if (data->registry_data.pcieLaneOverride)
   pcie_table->pcie_lane[i] = (uint8_t)encode_pcie_lane_width(
     data->registry_data.pcieLaneOverride);
  else
   pcie_table->pcie_lane[i] = (uint8_t)encode_pcie_lane_width(
       bios_pcie_table->entries[i].lane_width);
  if (data->registry_data.pcieClockOverride)
   pcie_table->lclk[i] =
     data->registry_data.pcieClockOverride;
  else
   pcie_table->lclk[i] =
     bios_pcie_table->entries[i].pcie_sclk;
 }

 pcie_table->count = NUM_LINK_LEVELS;

 return 0;
}

/*
 * 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 vega10_setup_default_dpm_tables(struct pp_hwmgr *hwmgr)
{
 struct vega10_hwmgr *data = hwmgr->backend;
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)(hwmgr->pptable);
 struct vega10_single_dpm_table *dpm_table;
 uint32_t i;

 struct phm_ppt_v1_clock_voltage_dependency_table *dep_soc_table =
   table_info->vdd_dep_on_socclk;
 struct phm_ppt_v1_clock_voltage_dependency_table *dep_gfx_table =
   table_info->vdd_dep_on_sclk;
 struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table =
   table_info->vdd_dep_on_mclk;
 struct phm_ppt_v1_mm_clock_voltage_dependency_table *dep_mm_table =
   table_info->mm_dep_table;
 struct phm_ppt_v1_clock_voltage_dependency_table *dep_dcef_table =
   table_info->vdd_dep_on_dcefclk;
 struct phm_ppt_v1_clock_voltage_dependency_table *dep_pix_table =
   table_info->vdd_dep_on_pixclk;
 struct phm_ppt_v1_clock_voltage_dependency_table *dep_disp_table =
   table_info->vdd_dep_on_dispclk;
 struct phm_ppt_v1_clock_voltage_dependency_table *dep_phy_table =
   table_info->vdd_dep_on_phyclk;

 PP_ASSERT_WITH_CODE(dep_soc_table,
   "SOCCLK dependency table is missing. This table is mandatory",
   return -EINVAL);
 PP_ASSERT_WITH_CODE(dep_soc_table->count >= 1,
   "SOCCLK dependency table is empty. This table is mandatory",
   return -EINVAL);

 PP_ASSERT_WITH_CODE(dep_gfx_table,
   "GFXCLK dependency table is missing. This table is mandatory",
   return -EINVAL);
 PP_ASSERT_WITH_CODE(dep_gfx_table->count >= 1,
   "GFXCLK dependency table is empty. This table is mandatory",
   return -EINVAL);

 PP_ASSERT_WITH_CODE(dep_mclk_table,
   "MCLK dependency table is missing. This table is mandatory",
   return -EINVAL);
 PP_ASSERT_WITH_CODE(dep_mclk_table->count >= 1,
   "MCLK dependency table has to have is missing. This table is mandatory",
   return -EINVAL);

 /* Initialize Sclk DPM table based on allow Sclk values */
 dpm_table = &(data->dpm_table.soc_table);
 vega10_setup_default_single_dpm_table(hwmgr,
   dpm_table,
   dep_soc_table);

 vega10_init_dpm_state(&(dpm_table->dpm_state));

 dpm_table = &(data->dpm_table.gfx_table);
 vega10_setup_default_single_dpm_table(hwmgr,
   dpm_table,
   dep_gfx_table);
 if (hwmgr->platform_descriptor.overdriveLimit.engineClock == 0)
  hwmgr->platform_descriptor.overdriveLimit.engineClock =
     dpm_table->dpm_levels[dpm_table->count-1].value;
 vega10_init_dpm_state(&(dpm_table->dpm_state));

 /* Initialize Mclk DPM table based on allow Mclk values */
 data->dpm_table.mem_table.count = 0;
 dpm_table = &(data->dpm_table.mem_table);
 vega10_setup_default_single_dpm_table(hwmgr,
   dpm_table,
   dep_mclk_table);
 if (hwmgr->platform_descriptor.overdriveLimit.memoryClock == 0)
  hwmgr->platform_descriptor.overdriveLimit.memoryClock =
     dpm_table->dpm_levels[dpm_table->count-1].value;
 vega10_init_dpm_state(&(dpm_table->dpm_state));

 data->dpm_table.eclk_table.count = 0;
 dpm_table = &(data->dpm_table.eclk_table);
 for (i = 0; i < dep_mm_table->count; i++) {
  if (i == 0 || dpm_table->dpm_levels
    [dpm_table->count - 1].value <=
      dep_mm_table->entries[i].eclk) {
   dpm_table->dpm_levels[dpm_table->count].value =
     dep_mm_table->entries[i].eclk;
   dpm_table->dpm_levels[dpm_table->count].enabled = i == 0;
   dpm_table->count++;
  }
 }
 vega10_init_dpm_state(&(dpm_table->dpm_state));

 data->dpm_table.vclk_table.count = 0;
 data->dpm_table.dclk_table.count = 0;
 dpm_table = &(data->dpm_table.vclk_table);
 for (i = 0; i < dep_mm_table->count; i++) {
  if (i == 0 || dpm_table->dpm_levels
    [dpm_table->count - 1].value <=
      dep_mm_table->entries[i].vclk) {
   dpm_table->dpm_levels[dpm_table->count].value =
     dep_mm_table->entries[i].vclk;
   dpm_table->dpm_levels[dpm_table->count].enabled = i == 0;
   dpm_table->count++;
  }
 }
 vega10_init_dpm_state(&(dpm_table->dpm_state));

 dpm_table = &(data->dpm_table.dclk_table);
 for (i = 0; i < dep_mm_table->count; i++) {
  if (i == 0 || dpm_table->dpm_levels
    [dpm_table->count - 1].value <=
      dep_mm_table->entries[i].dclk) {
   dpm_table->dpm_levels[dpm_table->count].value =
     dep_mm_table->entries[i].dclk;
   dpm_table->dpm_levels[dpm_table->count].enabled = i == 0;
   dpm_table->count++;
  }
 }
 vega10_init_dpm_state(&(dpm_table->dpm_state));

 /* Assume there is no headless Vega10 for now */
 dpm_table = &(data->dpm_table.dcef_table);
 vega10_setup_default_single_dpm_table(hwmgr,
   dpm_table,
   dep_dcef_table);

 vega10_init_dpm_state(&(dpm_table->dpm_state));

 dpm_table = &(data->dpm_table.pixel_table);
 vega10_setup_default_single_dpm_table(hwmgr,
   dpm_table,
   dep_pix_table);

 vega10_init_dpm_state(&(dpm_table->dpm_state));

 dpm_table = &(data->dpm_table.display_table);
 vega10_setup_default_single_dpm_table(hwmgr,
   dpm_table,
   dep_disp_table);

 vega10_init_dpm_state(&(dpm_table->dpm_state));

 dpm_table = &(data->dpm_table.phy_table);
 vega10_setup_default_single_dpm_table(hwmgr,
   dpm_table,
   dep_phy_table);

 vega10_init_dpm_state(&(dpm_table->dpm_state));

 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));

 return 0;
}

/*
 * vega10_populate_ulv_state
 * Function to provide parameters for Utral Low Voltage state to SMC.
 *
 * @hwmgr: - the address of the hardware manager.
 * return:   Always 0.
 */

static int vega10_populate_ulv_state(struct pp_hwmgr *hwmgr)
{
 struct vega10_hwmgr *data = hwmgr->backend;
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)(hwmgr->pptable);

 data->smc_state_table.pp_table.UlvOffsetVid =
   (uint8_t)table_info->us_ulv_voltage_offset;

 data->smc_state_table.pp_table.UlvSmnclkDid =
   (uint8_t)(table_info->us_ulv_smnclk_did);
 data->smc_state_table.pp_table.UlvMp1clkDid =
   (uint8_t)(table_info->us_ulv_mp1clk_did);
 data->smc_state_table.pp_table.UlvGfxclkBypass =
   (uint8_t)(table_info->us_ulv_gfxclk_bypass);
 data->smc_state_table.pp_table.UlvPhaseSheddingPsi0 =
   (uint8_t)(data->vddc_voltage_table.psi0_enable);
 data->smc_state_table.pp_table.UlvPhaseSheddingPsi1 =
   (uint8_t)(data->vddc_voltage_table.psi1_enable);

 return 0;
}

static int vega10_populate_single_lclk_level(struct pp_hwmgr *hwmgr,
  uint32_t lclock, uint8_t *curr_lclk_did)
{
 struct pp_atomfwctrl_clock_dividers_soc15 dividers;

 PP_ASSERT_WITH_CODE(!pp_atomfwctrl_get_gpu_pll_dividers_vega10(
   hwmgr,
   COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
   lclock, ÷rs),
   "Failed to get LCLK clock settings from VBIOS!",
   return -1);

 *curr_lclk_did = dividers.ulDid;

 return 0;
}

static int vega10_override_pcie_parameters(struct pp_hwmgr *hwmgr)
{
 struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
 struct vega10_hwmgr *data =
   (struct vega10_hwmgr *)(hwmgr->backend);
 uint32_t pcie_gen = 0, pcie_width = 0;
 PPTable_t *pp_table = &(data->smc_state_table.pp_table);
 int i;

 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;

 for (i = 0; i < NUM_LINK_LEVELS; i++) {
  if (pp_table->PcieGenSpeed[i] > pcie_gen)
   pp_table->PcieGenSpeed[i] = pcie_gen;

  if (pp_table->PcieLaneCount[i] > pcie_width)
   pp_table->PcieLaneCount[i] = pcie_width;
 }

 if (data->registry_data.pcie_dpm_key_disabled) {
  for (i = 0; i < NUM_LINK_LEVELS; i++) {
   pp_table->PcieGenSpeed[i] = pcie_gen;
   pp_table->PcieLaneCount[i] = pcie_width;
  }
 }

 return 0;
}

static int vega10_populate_smc_link_levels(struct pp_hwmgr *hwmgr)
{
 int result = -1;
 struct vega10_hwmgr *data = hwmgr->backend;
 PPTable_t *pp_table = &(data->smc_state_table.pp_table);
 struct vega10_pcie_table *pcie_table =
   &(data->dpm_table.pcie_table);
 uint32_t i, j;

 for (i = 0; i < pcie_table->count; i++) {
  pp_table->PcieGenSpeed[i] = pcie_table->pcie_gen[i];
  pp_table->PcieLaneCount[i] = pcie_table->pcie_lane[i];

  result = vega10_populate_single_lclk_level(hwmgr,
    pcie_table->lclk[i], &(pp_table->LclkDid[i]));
  if (result) {
   pr_info("Populate LClock Level %d Failed!\n", i);
   return result;
  }
 }

 j = i - 1;
 while (i < NUM_LINK_LEVELS) {
  pp_table->PcieGenSpeed[i] = pcie_table->pcie_gen[j];
  pp_table->PcieLaneCount[i] = pcie_table->pcie_lane[j];

  result = vega10_populate_single_lclk_level(hwmgr,
    pcie_table->lclk[j], &(pp_table->LclkDid[i]));
  if (result) {
   pr_info("Populate LClock Level %d Failed!\n", i);
   return result;
  }
  i++;
 }

 return result;
}

/**
 * vega10_populate_single_gfx_level - Populates single SMC GFXSCLK structure
 *                                    using the provided engine clock
 *
 * @hwmgr:      the address of the hardware manager
 * @gfx_clock:  the GFX clock to use to populate the structure.
 * @current_gfxclk_level:  location in PPTable for the SMC GFXCLK structure.
 * @acg_freq:   ACG frequenty to return (MHz)
 */

static int vega10_populate_single_gfx_level(struct pp_hwmgr *hwmgr,
  uint32_t gfx_clock, PllSetting_t *current_gfxclk_level,
  uint32_t *acg_freq)
{
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)(hwmgr->pptable);
 struct phm_ppt_v1_clock_voltage_dependency_table *dep_on_sclk;
 struct vega10_hwmgr *data = hwmgr->backend;
 struct pp_atomfwctrl_clock_dividers_soc15 dividers;
 uint32_t gfx_max_clock =
   hwmgr->platform_descriptor.overdriveLimit.engineClock;
 uint32_t i = 0;

 if (hwmgr->od_enabled)
  dep_on_sclk = (struct phm_ppt_v1_clock_voltage_dependency_table *)
      &(data->odn_dpm_table.vdd_dep_on_sclk);
 else
  dep_on_sclk = table_info->vdd_dep_on_sclk;

 PP_ASSERT_WITH_CODE(dep_on_sclk,
   "Invalid SOC_VDD-GFX_CLK Dependency Table!",
   return -EINVAL);

 if (data->need_update_dpm_table & DPMTABLE_OD_UPDATE_SCLK)
  gfx_clock = gfx_clock > gfx_max_clock ? gfx_max_clock : gfx_clock;
 else {
  for (i = 0; i < dep_on_sclk->count; i++) {
   if (dep_on_sclk->entries[i].clk == gfx_clock)
    break;
  }
  PP_ASSERT_WITH_CODE(dep_on_sclk->count > i,
    "Cannot find gfx_clk in SOC_VDD-GFX_CLK!",
    return -EINVAL);
 }

 PP_ASSERT_WITH_CODE(!pp_atomfwctrl_get_gpu_pll_dividers_vega10(hwmgr,
   COMPUTE_GPUCLK_INPUT_FLAG_GFXCLK,
   gfx_clock, ÷rs),
   "Failed to get GFX Clock settings from VBIOS!",
   return -EINVAL);

 /* Feedback Multiplier: bit 0:8 int, bit 15:12 post_div, bit 31:16 frac */
 current_gfxclk_level->FbMult =
   cpu_to_le32(dividers.ulPll_fb_mult);
 /* Spread FB Multiplier bit: bit 0:8 int, bit 31:16 frac */
 current_gfxclk_level->SsOn = dividers.ucPll_ss_enable;
 current_gfxclk_level->SsFbMult =
   cpu_to_le32(dividers.ulPll_ss_fbsmult);
 current_gfxclk_level->SsSlewFrac =
   cpu_to_le16(dividers.usPll_ss_slew_frac);
 current_gfxclk_level->Did = (uint8_t)(dividers.ulDid);

 *acg_freq = gfx_clock / 100; /* 100 Khz to Mhz conversion */

 return 0;
}

/**
 * vega10_populate_single_soc_level - Populates single SMC SOCCLK structure
 *                                    using the provided clock.
 *
 * @hwmgr:     the address of the hardware manager.
 * @soc_clock: the SOC clock to use to populate the structure.
 * @current_soc_did:   DFS divider to pass back to caller
 * @current_vol_index: index of current VDD to pass back to caller
 * return:      0 on success
 */

static int vega10_populate_single_soc_level(struct pp_hwmgr *hwmgr,
  uint32_t soc_clock, uint8_t *current_soc_did,
  uint8_t *current_vol_index)
{
 struct vega10_hwmgr *data = hwmgr->backend;
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)(hwmgr->pptable);
 struct phm_ppt_v1_clock_voltage_dependency_table *dep_on_soc;
 struct pp_atomfwctrl_clock_dividers_soc15 dividers;
 uint32_t i;

 if (hwmgr->od_enabled) {
  dep_on_soc = (struct phm_ppt_v1_clock_voltage_dependency_table *)
      &data->odn_dpm_table.vdd_dep_on_socclk;
  for (i = 0; i < dep_on_soc->count; i++) {
   if (dep_on_soc->entries[i].clk >= soc_clock)
    break;
  }
 } else {
  dep_on_soc = table_info->vdd_dep_on_socclk;
  for (i = 0; i < dep_on_soc->count; i++) {
   if (dep_on_soc->entries[i].clk == soc_clock)
    break;
  }
 }

 PP_ASSERT_WITH_CODE(dep_on_soc->count > i,
   "Cannot find SOC_CLK in SOC_VDD-SOC_CLK Dependency Table",
   return -EINVAL);

 PP_ASSERT_WITH_CODE(!pp_atomfwctrl_get_gpu_pll_dividers_vega10(hwmgr,
   COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
   soc_clock, ÷rs),
   "Failed to get SOC Clock settings from VBIOS!",
   return -EINVAL);

 *current_soc_did = (uint8_t)dividers.ulDid;
 *current_vol_index = (uint8_t)(dep_on_soc->entries[i].vddInd);
 return 0;
}

/**
 * vega10_populate_all_graphic_levels - Populates all SMC SCLK levels' structure
 *                                      based on the trimmed allowed dpm engine clock states
 *
 * @hwmgr:      the address of the hardware manager
 */

static int vega10_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
{
 struct vega10_hwmgr *data = hwmgr->backend;
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)(hwmgr->pptable);
 PPTable_t *pp_table = &(data->smc_state_table.pp_table);
 struct vega10_single_dpm_table *dpm_table = &(data->dpm_table.gfx_table);
 int result = 0;
 uint32_t i, j;

 for (i = 0; i < dpm_table->count; i++) {
  result = vega10_populate_single_gfx_level(hwmgr,
    dpm_table->dpm_levels[i].value,
    &(pp_table->GfxclkLevel[i]),
    &(pp_table->AcgFreqTable[i]));
  if (result)
   return result;
 }

 j = i - 1;
 while (i < NUM_GFXCLK_DPM_LEVELS) {
  result = vega10_populate_single_gfx_level(hwmgr,
    dpm_table->dpm_levels[j].value,
    &(pp_table->GfxclkLevel[i]),
    &(pp_table->AcgFreqTable[i]));
  if (result)
   return result;
  i++;
 }

 pp_table->GfxclkSlewRate =
   cpu_to_le16(table_info->us_gfxclk_slew_rate);

 dpm_table = &(data->dpm_table.soc_table);
 for (i = 0; i < dpm_table->count; i++) {
  result = vega10_populate_single_soc_level(hwmgr,
    dpm_table->dpm_levels[i].value,
    &(pp_table->SocclkDid[i]),
    &(pp_table->SocDpmVoltageIndex[i]));
  if (result)
   return result;
 }

 j = i - 1;
 while (i < NUM_SOCCLK_DPM_LEVELS) {
  result = vega10_populate_single_soc_level(hwmgr,
    dpm_table->dpm_levels[j].value,
    &(pp_table->SocclkDid[i]),
    &(pp_table->SocDpmVoltageIndex[i]));
  if (result)
   return result;
  i++;
 }

 return result;
}

static void vega10_populate_vddc_soc_levels(struct pp_hwmgr *hwmgr)
{
 struct vega10_hwmgr *data = hwmgr->backend;
 PPTable_t *pp_table = &(data->smc_state_table.pp_table);
 struct phm_ppt_v2_information *table_info = hwmgr->pptable;
 struct phm_ppt_v1_voltage_lookup_table *vddc_lookup_table;

 uint8_t soc_vid = 0;
 uint32_t i, max_vddc_level;

 if (hwmgr->od_enabled)
  vddc_lookup_table = (struct phm_ppt_v1_voltage_lookup_table *)&data->odn_dpm_table.vddc_lookup_table;
 else
  vddc_lookup_table = table_info->vddc_lookup_table;

 max_vddc_level = vddc_lookup_table->count;
 for (i = 0; i < max_vddc_level; i++) {
  soc_vid = (uint8_t)convert_to_vid(vddc_lookup_table->entries[i].us_vdd);
  pp_table->SocVid[i] = soc_vid;
 }
 while (i < MAX_REGULAR_DPM_NUMBER) {
  pp_table->SocVid[i] = soc_vid;
  i++;
 }
}

/*
 * Populates single SMC GFXCLK structure using the provided clock.
 *
 * @hwmgr:     the address of the hardware manager.
 * @mem_clock: the memory clock to use to populate the structure.
 * return:     0 on success..
 */

static int vega10_populate_single_memory_level(struct pp_hwmgr *hwmgr,
  uint32_t mem_clock, uint8_t *current_mem_vid,
  PllSetting_t *current_memclk_level, uint8_t *current_mem_soc_vind)
{
 struct vega10_hwmgr *data = hwmgr->backend;
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)(hwmgr->pptable);
 struct phm_ppt_v1_clock_voltage_dependency_table *dep_on_mclk;
 struct pp_atomfwctrl_clock_dividers_soc15 dividers;
 uint32_t mem_max_clock =
   hwmgr->platform_descriptor.overdriveLimit.memoryClock;
 uint32_t i = 0;

 if (hwmgr->od_enabled)
  dep_on_mclk = (struct phm_ppt_v1_clock_voltage_dependency_table *)
     &data->odn_dpm_table.vdd_dep_on_mclk;
 else
  dep_on_mclk = table_info->vdd_dep_on_mclk;

 PP_ASSERT_WITH_CODE(dep_on_mclk,
   "Invalid SOC_VDD-UCLK Dependency Table!",
   return -EINVAL);

 if (data->need_update_dpm_table & DPMTABLE_OD_UPDATE_MCLK) {
  mem_clock = mem_clock > mem_max_clock ? mem_max_clock : mem_clock;
 } else {
  for (i = 0; i < dep_on_mclk->count; i++) {
   if (dep_on_mclk->entries[i].clk == mem_clock)
    break;
  }
  PP_ASSERT_WITH_CODE(dep_on_mclk->count > i,
    "Cannot find UCLK in SOC_VDD-UCLK Dependency Table!",
    return -EINVAL);
 }

 PP_ASSERT_WITH_CODE(!pp_atomfwctrl_get_gpu_pll_dividers_vega10(
   hwmgr, COMPUTE_GPUCLK_INPUT_FLAG_UCLK, mem_clock, ÷rs),
   "Failed to get UCLK settings from VBIOS!",
   return -1);

 *current_mem_vid =
   (uint8_t)(convert_to_vid(dep_on_mclk->entries[i].mvdd));
 *current_mem_soc_vind =
   (uint8_t)(dep_on_mclk->entries[i].vddInd);
 current_memclk_level->FbMult = cpu_to_le32(dividers.ulPll_fb_mult);
 current_memclk_level->Did = (uint8_t)(dividers.ulDid);

 PP_ASSERT_WITH_CODE(current_memclk_level->Did >= 1,
   "Invalid Divider ID!",
   return -EINVAL);

 return 0;
}

/**
 * vega10_populate_all_memory_levels - Populates all SMC MCLK levels' structure
 *                                     based on the trimmed allowed dpm memory clock states.
 *
 * @hwmgr:  the address of the hardware manager.
 * return:   PP_Result_OK on success.
 */

static int vega10_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
{
 struct vega10_hwmgr *data = hwmgr->backend;
 PPTable_t *pp_table = &(data->smc_state_table.pp_table);
 struct vega10_single_dpm_table *dpm_table =
   &(data->dpm_table.mem_table);
 int result = 0;
 uint32_t i, j;

 for (i = 0; i < dpm_table->count; i++) {
  result = vega10_populate_single_memory_level(hwmgr,
    dpm_table->dpm_levels[i].value,
    &(pp_table->MemVid[i]),
    &(pp_table->UclkLevel[i]),
    &(pp_table->MemSocVoltageIndex[i]));
  if (result)
   return result;
 }

 j = i - 1;
 while (i < NUM_UCLK_DPM_LEVELS) {
  result = vega10_populate_single_memory_level(hwmgr,
    dpm_table->dpm_levels[j].value,
    &(pp_table->MemVid[i]),
    &(pp_table->UclkLevel[i]),
    &(pp_table->MemSocVoltageIndex[i]));
  if (result)
   return result;
  i++;
 }

 pp_table->NumMemoryChannels = (uint16_t)(data->mem_channels);
 pp_table->MemoryChannelWidth =
   (uint16_t)(HBM_MEMORY_CHANNEL_WIDTH *
     channel_number[data->mem_channels]);

 pp_table->LowestUclkReservedForUlv =
   (uint8_t)(data->lowest_uclk_reserved_for_ulv);

 return result;
}

static int vega10_populate_single_display_type(struct pp_hwmgr *hwmgr,
  DSPCLK_e disp_clock)
{
 struct vega10_hwmgr *data = hwmgr->backend;
 PPTable_t *pp_table = &(data->smc_state_table.pp_table);
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)
   (hwmgr->pptable);
 struct phm_ppt_v1_clock_voltage_dependency_table *dep_table;
 uint32_t i;
 uint16_t clk = 0, vddc = 0;
 uint8_t vid = 0;

 switch (disp_clock) {
 case DSPCLK_DCEFCLK:
  dep_table = table_info->vdd_dep_on_dcefclk;
  break;
 case DSPCLK_DISPCLK:
  dep_table = table_info->vdd_dep_on_dispclk;
  break;
 case DSPCLK_PIXCLK:
  dep_table = table_info->vdd_dep_on_pixclk;
  break;
 case DSPCLK_PHYCLK:
  dep_table = table_info->vdd_dep_on_phyclk;
  break;
 default:
  return -1;
 }

 PP_ASSERT_WITH_CODE(dep_table->count <= NUM_DSPCLK_LEVELS,
   "Number Of Entries Exceeded maximum!",
   return -1);

 for (i = 0; i < dep_table->count; i++) {
  clk = (uint16_t)(dep_table->entries[i].clk / 100);
  vddc = table_info->vddc_lookup_table->
    entries[dep_table->entries[i].vddInd].us_vdd;
  vid = (uint8_t)convert_to_vid(vddc);
  pp_table->DisplayClockTable[disp_clock][i].Freq =
    cpu_to_le16(clk);
  pp_table->DisplayClockTable[disp_clock][i].Vid =
    cpu_to_le16(vid);
 }

 while (i < NUM_DSPCLK_LEVELS) {
  pp_table->DisplayClockTable[disp_clock][i].Freq =
    cpu_to_le16(clk);
  pp_table->DisplayClockTable[disp_clock][i].Vid =
    cpu_to_le16(vid);
  i++;
 }

 return 0;
}

static int vega10_populate_all_display_clock_levels(struct pp_hwmgr *hwmgr)
{
 uint32_t i;

 for (i = 0; i < DSPCLK_COUNT; i++) {
  PP_ASSERT_WITH_CODE(!vega10_populate_single_display_type(hwmgr, i),
    "Failed to populate Clock in DisplayClockTable!",
    return -1);
 }

 return 0;
}

static int vega10_populate_single_eclock_level(struct pp_hwmgr *hwmgr,
  uint32_t eclock, uint8_t *current_eclk_did,
  uint8_t *current_soc_vol)
{
 struct phm_ppt_v2_information *table_info =
   (struct phm_ppt_v2_information *)(hwmgr->pptable);
 struct phm_ppt_v1_mm_clock_voltage_dependency_table *dep_table =
   table_info->mm_dep_table;
 struct pp_atomfwctrl_clock_dividers_soc15 dividers;
 uint32_t i;

 PP_ASSERT_WITH_CODE(!pp_atomfwctrl_get_gpu_pll_dividers_vega10(hwmgr,
   COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
   eclock, ÷rs),
   "Failed to get ECLK clock settings from VBIOS!",
   return -1);

 *current_eclk_did = (uint8_t)dividers.ulDid;

 for (i = 0; i < dep_table->count; i++) {
  if (dep_table->entries[i].eclk == eclock)
--> --------------------

--> maximum size reached

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

Messung V0.5
C=94 H=89 G=91

¤ Dauer der Verarbeitung: 0.28 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.