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

Quelle  sienna_cichlid_ppt.c   Sprache: C

 
/*
 * Copyright 2019 Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 */


#define SWSMU_CODE_LAYER_L2

#include <linux/firmware.h>
#include <linux/pci.h>
#include <linux/i2c.h>
#include "amdgpu.h"
#include "amdgpu_dpm.h"
#include "amdgpu_smu.h"
#include "atomfirmware.h"
#include "amdgpu_atomfirmware.h"
#include "amdgpu_atombios.h"
#include "smu_v11_0.h"
#include "smu11_driver_if_sienna_cichlid.h"
#include "soc15_common.h"
#include "atom.h"
#include "sienna_cichlid_ppt.h"
#include "smu_v11_0_7_pptable.h"
#include "smu_v11_0_7_ppsmc.h"
#include "nbio/nbio_2_3_offset.h"
#include "nbio/nbio_2_3_sh_mask.h"
#include "thm/thm_11_0_2_offset.h"
#include "thm/thm_11_0_2_sh_mask.h"
#include "mp/mp_11_0_offset.h"
#include "mp/mp_11_0_sh_mask.h"

#include "asic_reg/mp/mp_11_0_sh_mask.h"
#include "amdgpu_ras.h"
#include "smu_cmn.h"

/*
 * DO NOT use these for err/warn/info/debug messages.
 * Use dev_err, dev_warn, dev_info and dev_dbg instead.
 * They are more MGPU friendly.
 */

#undef pr_err
#undef pr_warn
#undef pr_info
#undef pr_debug

#define FEATURE_MASK(feature) (1ULL << feature)
#define SMC_DPM_FEATURE ( \
 FEATURE_MASK(FEATURE_DPM_PREFETCHER_BIT) | \
 FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT)     | \
 FEATURE_MASK(FEATURE_DPM_UCLK_BIT)  | \
 FEATURE_MASK(FEATURE_DPM_LINK_BIT)       | \
 FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT)     | \
 FEATURE_MASK(FEATURE_DPM_FCLK_BIT)  | \
 FEATURE_MASK(FEATURE_DPM_DCEFCLK_BIT)  | \
 FEATURE_MASK(FEATURE_DPM_MP0CLK_BIT))

#define SMU_11_0_7_GFX_BUSY_THRESHOLD 15

#define GET_PPTABLE_MEMBER(field, member)                                    \
 do {                                                                 \
  if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) ==             \
      IP_VERSION(11, 0, 13))                                   \
   (*member) = (smu->smu_table.driver_pptable +         \
         offsetof(PPTable_beige_goby_t, field)); \
  else                                                         \
   (*member) = (smu->smu_table.driver_pptable +         \
         offsetof(PPTable_t, field));            \
 } while (0)

/* STB FIFO depth is in 64bit units */
#define SIENNA_CICHLID_STB_DEPTH_UNIT_BYTES 8

/*
 * SMU support ECCTABLE since version 58.70.0,
 * use this to check whether ECCTABLE feature is supported.
 */

#define SUPPORT_ECCTABLE_SMU_VERSION 0x003a4600

static int get_table_size(struct smu_context *smu)
{
 if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(11, 0, 13))
  return sizeof(PPTable_beige_goby_t);
 else
  return sizeof(PPTable_t);
}

static struct cmn2asic_msg_mapping sienna_cichlid_message_map[SMU_MSG_MAX_COUNT] = {
 MSG_MAP(TestMessage,   PPSMC_MSG_TestMessage,                 1),
 MSG_MAP(GetSmuVersion,   PPSMC_MSG_GetSmuVersion,               1),
 MSG_MAP(GetDriverIfVersion,  PPSMC_MSG_GetDriverIfVersion,          1),
 MSG_MAP(SetAllowedFeaturesMaskLow, PPSMC_MSG_SetAllowedFeaturesMaskLow,   0),
 MSG_MAP(SetAllowedFeaturesMaskHigh, PPSMC_MSG_SetAllowedFeaturesMaskHigh,  0),
 MSG_MAP(EnableAllSmuFeatures,  PPSMC_MSG_EnableAllSmuFeatures,        0),
 MSG_MAP(DisableAllSmuFeatures,  PPSMC_MSG_DisableAllSmuFeatures,       0),
 MSG_MAP(EnableSmuFeaturesLow,  PPSMC_MSG_EnableSmuFeaturesLow,        1),
 MSG_MAP(EnableSmuFeaturesHigh,  PPSMC_MSG_EnableSmuFeaturesHigh,       1),
 MSG_MAP(DisableSmuFeaturesLow,  PPSMC_MSG_DisableSmuFeaturesLow,       1),
 MSG_MAP(DisableSmuFeaturesHigh,  PPSMC_MSG_DisableSmuFeaturesHigh,      1),
 MSG_MAP(GetEnabledSmuFeaturesLow,       PPSMC_MSG_GetRunningSmuFeaturesLow,    1),
 MSG_MAP(GetEnabledSmuFeaturesHigh, PPSMC_MSG_GetRunningSmuFeaturesHigh,   1),
 MSG_MAP(SetWorkloadMask,  PPSMC_MSG_SetWorkloadMask,             1),
 MSG_MAP(SetPptLimit,   PPSMC_MSG_SetPptLimit,                 0),
 MSG_MAP(SetDriverDramAddrHigh,  PPSMC_MSG_SetDriverDramAddrHigh,       1),
 MSG_MAP(SetDriverDramAddrLow,  PPSMC_MSG_SetDriverDramAddrLow,        1),
 MSG_MAP(SetToolsDramAddrHigh,  PPSMC_MSG_SetToolsDramAddrHigh,        0),
 MSG_MAP(SetToolsDramAddrLow,  PPSMC_MSG_SetToolsDramAddrLow,         0),
 MSG_MAP(TransferTableSmu2Dram,  PPSMC_MSG_TransferTableSmu2Dram,       1),
 MSG_MAP(TransferTableDram2Smu,  PPSMC_MSG_TransferTableDram2Smu,       0),
 MSG_MAP(UseDefaultPPTable,  PPSMC_MSG_UseDefaultPPTable,           0),
 MSG_MAP(RunDcBtc,   PPSMC_MSG_RunDcBtc,                    0),
 MSG_MAP(EnterBaco,   PPSMC_MSG_EnterBaco,                   0),
 MSG_MAP(SetSoftMinByFreq,  PPSMC_MSG_SetSoftMinByFreq,            1),
 MSG_MAP(SetSoftMaxByFreq,  PPSMC_MSG_SetSoftMaxByFreq,            1),
 MSG_MAP(SetHardMinByFreq,  PPSMC_MSG_SetHardMinByFreq,            1),
 MSG_MAP(SetHardMaxByFreq,  PPSMC_MSG_SetHardMaxByFreq,            0),
 MSG_MAP(GetMinDpmFreq,   PPSMC_MSG_GetMinDpmFreq,               1),
 MSG_MAP(GetMaxDpmFreq,   PPSMC_MSG_GetMaxDpmFreq,               1),
 MSG_MAP(GetDpmFreqByIndex,  PPSMC_MSG_GetDpmFreqByIndex,           1),
 MSG_MAP(SetGeminiMode,   PPSMC_MSG_SetGeminiMode,               0),
 MSG_MAP(SetGeminiApertureHigh,  PPSMC_MSG_SetGeminiApertureHigh,       0),
 MSG_MAP(SetGeminiApertureLow,  PPSMC_MSG_SetGeminiApertureLow,        0),
 MSG_MAP(OverridePcieParameters,  PPSMC_MSG_OverridePcieParameters,      0),
 MSG_MAP(ReenableAcDcInterrupt,  PPSMC_MSG_ReenableAcDcInterrupt,       0),
 MSG_MAP(NotifyPowerSource,  PPSMC_MSG_NotifyPowerSource,           0),
 MSG_MAP(SetUclkFastSwitch,  PPSMC_MSG_SetUclkFastSwitch,           0),
 MSG_MAP(SetVideoFps,   PPSMC_MSG_SetVideoFps,                 0),
 MSG_MAP(PrepareMp1ForUnload,  PPSMC_MSG_PrepareMp1ForUnload,         1),
 MSG_MAP(AllowGfxOff,   PPSMC_MSG_AllowGfxOff,                 0),
 MSG_MAP(DisallowGfxOff,   PPSMC_MSG_DisallowGfxOff,              0),
 MSG_MAP(GetPptLimit,   PPSMC_MSG_GetPptLimit,                 0),
 MSG_MAP(GetDcModeMaxDpmFreq,  PPSMC_MSG_GetDcModeMaxDpmFreq,         1),
 MSG_MAP(ExitBaco,   PPSMC_MSG_ExitBaco,                    0),
 MSG_MAP(PowerUpVcn,   PPSMC_MSG_PowerUpVcn,                  0),
 MSG_MAP(PowerDownVcn,   PPSMC_MSG_PowerDownVcn,                0),
 MSG_MAP(PowerUpJpeg,   PPSMC_MSG_PowerUpJpeg,                 0),
 MSG_MAP(PowerDownJpeg,   PPSMC_MSG_PowerDownJpeg,               0),
 MSG_MAP(BacoAudioD3PME,   PPSMC_MSG_BacoAudioD3PME,              0),
 MSG_MAP(ArmD3,    PPSMC_MSG_ArmD3,                       0),
 MSG_MAP(Mode1Reset,                     PPSMC_MSG_Mode1Reset,         0),
 MSG_MAP(SetMGpuFanBoostLimitRpm, PPSMC_MSG_SetMGpuFanBoostLimitRpm,     0),
 MSG_MAP(SetGpoFeaturePMask,  PPSMC_MSG_SetGpoFeaturePMask,          0),
 MSG_MAP(DisallowGpo,   PPSMC_MSG_DisallowGpo,                 0),
 MSG_MAP(Enable2ndUSB20Port,  PPSMC_MSG_Enable2ndUSB20Port,          0),
 MSG_MAP(DriverMode2Reset,  PPSMC_MSG_DriverMode2Reset,        0),
};

static struct cmn2asic_mapping sienna_cichlid_clk_map[SMU_CLK_COUNT] = {
 CLK_MAP(GFXCLK,  PPCLK_GFXCLK),
 CLK_MAP(SCLK,  PPCLK_GFXCLK),
 CLK_MAP(SOCCLK,  PPCLK_SOCCLK),
 CLK_MAP(FCLK,  PPCLK_FCLK),
 CLK_MAP(UCLK,  PPCLK_UCLK),
 CLK_MAP(MCLK,  PPCLK_UCLK),
 CLK_MAP(DCLK,  PPCLK_DCLK_0),
 CLK_MAP(DCLK1,  PPCLK_DCLK_1),
 CLK_MAP(VCLK,  PPCLK_VCLK_0),
 CLK_MAP(VCLK1,  PPCLK_VCLK_1),
 CLK_MAP(DCEFCLK, PPCLK_DCEFCLK),
 CLK_MAP(DISPCLK, PPCLK_DISPCLK),
 CLK_MAP(PIXCLK,  PPCLK_PIXCLK),
 CLK_MAP(PHYCLK,  PPCLK_PHYCLK),
};

static struct cmn2asic_mapping sienna_cichlid_feature_mask_map[SMU_FEATURE_COUNT] = {
 FEA_MAP(DPM_PREFETCHER),
 FEA_MAP(DPM_GFXCLK),
 FEA_MAP(DPM_GFX_GPO),
 FEA_MAP(DPM_UCLK),
 FEA_MAP(DPM_FCLK),
 FEA_MAP(DPM_SOCCLK),
 FEA_MAP(DPM_MP0CLK),
 FEA_MAP(DPM_LINK),
 FEA_MAP(DPM_DCEFCLK),
 FEA_MAP(DPM_XGMI),
 FEA_MAP(MEM_VDDCI_SCALING),
 FEA_MAP(MEM_MVDD_SCALING),
 FEA_MAP(DS_GFXCLK),
 FEA_MAP(DS_SOCCLK),
 FEA_MAP(DS_FCLK),
 FEA_MAP(DS_LCLK),
 FEA_MAP(DS_DCEFCLK),
 FEA_MAP(DS_UCLK),
 FEA_MAP(GFX_ULV),
 FEA_MAP(FW_DSTATE),
 FEA_MAP(GFXOFF),
 FEA_MAP(BACO),
 FEA_MAP(MM_DPM_PG),
 FEA_MAP(RSMU_SMN_CG),
 FEA_MAP(PPT),
 FEA_MAP(TDC),
 FEA_MAP(APCC_PLUS),
 FEA_MAP(GTHR),
 FEA_MAP(ACDC),
 FEA_MAP(VR0HOT),
 FEA_MAP(VR1HOT),
 FEA_MAP(FW_CTF),
 FEA_MAP(FAN_CONTROL),
 FEA_MAP(THERMAL),
 FEA_MAP(GFX_DCS),
 FEA_MAP(RM),
 FEA_MAP(LED_DISPLAY),
 FEA_MAP(GFX_SS),
 FEA_MAP(OUT_OF_BAND_MONITOR),
 FEA_MAP(TEMP_DEPENDENT_VMIN),
 FEA_MAP(MMHUB_PG),
 FEA_MAP(ATHUB_PG),
 FEA_MAP(APCC_DFLL),
};

static struct cmn2asic_mapping sienna_cichlid_table_map[SMU_TABLE_COUNT] = {
 TAB_MAP(PPTABLE),
 TAB_MAP(WATERMARKS),
 TAB_MAP(AVFS_PSM_DEBUG),
 TAB_MAP(AVFS_FUSE_OVERRIDE),
 TAB_MAP(PMSTATUSLOG),
 TAB_MAP(SMU_METRICS),
 TAB_MAP(DRIVER_SMU_CONFIG),
 TAB_MAP(ACTIVITY_MONITOR_COEFF),
 TAB_MAP(OVERDRIVE),
 TAB_MAP(I2C_COMMANDS),
 TAB_MAP(PACE),
 TAB_MAP(ECCINFO),
};

static struct cmn2asic_mapping sienna_cichlid_pwr_src_map[SMU_POWER_SOURCE_COUNT] = {
 PWR_MAP(AC),
 PWR_MAP(DC),
};

static struct cmn2asic_mapping sienna_cichlid_workload_map[PP_SMC_POWER_PROFILE_COUNT] = {
 WORKLOAD_MAP(PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT, WORKLOAD_PPLIB_DEFAULT_BIT),
 WORKLOAD_MAP(PP_SMC_POWER_PROFILE_FULLSCREEN3D,  WORKLOAD_PPLIB_FULL_SCREEN_3D_BIT),
 WORKLOAD_MAP(PP_SMC_POWER_PROFILE_POWERSAVING,  WORKLOAD_PPLIB_POWER_SAVING_BIT),
 WORKLOAD_MAP(PP_SMC_POWER_PROFILE_VIDEO,  WORKLOAD_PPLIB_VIDEO_BIT),
 WORKLOAD_MAP(PP_SMC_POWER_PROFILE_VR,   WORKLOAD_PPLIB_VR_BIT),
 WORKLOAD_MAP(PP_SMC_POWER_PROFILE_COMPUTE,  WORKLOAD_PPLIB_COMPUTE_BIT),
 WORKLOAD_MAP(PP_SMC_POWER_PROFILE_CUSTOM,  WORKLOAD_PPLIB_CUSTOM_BIT),
};

static const uint8_t sienna_cichlid_throttler_map[] = {
 [THROTTLER_TEMP_EDGE_BIT] = (SMU_THROTTLER_TEMP_EDGE_BIT),
 [THROTTLER_TEMP_HOTSPOT_BIT] = (SMU_THROTTLER_TEMP_HOTSPOT_BIT),
 [THROTTLER_TEMP_MEM_BIT] = (SMU_THROTTLER_TEMP_MEM_BIT),
 [THROTTLER_TEMP_VR_GFX_BIT] = (SMU_THROTTLER_TEMP_VR_GFX_BIT),
 [THROTTLER_TEMP_VR_MEM0_BIT] = (SMU_THROTTLER_TEMP_VR_MEM0_BIT),
 [THROTTLER_TEMP_VR_MEM1_BIT] = (SMU_THROTTLER_TEMP_VR_MEM1_BIT),
 [THROTTLER_TEMP_VR_SOC_BIT] = (SMU_THROTTLER_TEMP_VR_SOC_BIT),
 [THROTTLER_TEMP_LIQUID0_BIT] = (SMU_THROTTLER_TEMP_LIQUID0_BIT),
 [THROTTLER_TEMP_LIQUID1_BIT] = (SMU_THROTTLER_TEMP_LIQUID1_BIT),
 [THROTTLER_TDC_GFX_BIT]  = (SMU_THROTTLER_TDC_GFX_BIT),
 [THROTTLER_TDC_SOC_BIT]  = (SMU_THROTTLER_TDC_SOC_BIT),
 [THROTTLER_PPT0_BIT]  = (SMU_THROTTLER_PPT0_BIT),
 [THROTTLER_PPT1_BIT]  = (SMU_THROTTLER_PPT1_BIT),
 [THROTTLER_PPT2_BIT]  = (SMU_THROTTLER_PPT2_BIT),
 [THROTTLER_PPT3_BIT]  = (SMU_THROTTLER_PPT3_BIT),
 [THROTTLER_FIT_BIT]  = (SMU_THROTTLER_FIT_BIT),
 [THROTTLER_PPM_BIT]  = (SMU_THROTTLER_PPM_BIT),
 [THROTTLER_APCC_BIT]  = (SMU_THROTTLER_APCC_BIT),
};

static int
sienna_cichlid_get_allowed_feature_mask(struct smu_context *smu,
      uint32_t *feature_mask, uint32_t num)
{
 struct amdgpu_device *adev = smu->adev;

 if (num > 2)
  return -EINVAL;

 memset(feature_mask, 0, sizeof(uint32_t) * num);

 *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_PREFETCHER_BIT)
    | FEATURE_MASK(FEATURE_DPM_FCLK_BIT)
    | FEATURE_MASK(FEATURE_DPM_MP0CLK_BIT)
    | FEATURE_MASK(FEATURE_DS_SOCCLK_BIT)
    | FEATURE_MASK(FEATURE_DS_DCEFCLK_BIT)
    | FEATURE_MASK(FEATURE_DS_FCLK_BIT)
    | FEATURE_MASK(FEATURE_DS_UCLK_BIT)
    | FEATURE_MASK(FEATURE_FW_DSTATE_BIT)
    | FEATURE_MASK(FEATURE_DF_CSTATE_BIT)
    | FEATURE_MASK(FEATURE_RSMU_SMN_CG_BIT)
    | FEATURE_MASK(FEATURE_GFX_SS_BIT)
    | FEATURE_MASK(FEATURE_VR0HOT_BIT)
    | FEATURE_MASK(FEATURE_PPT_BIT)
    | FEATURE_MASK(FEATURE_TDC_BIT)
    | FEATURE_MASK(FEATURE_BACO_BIT)
    | FEATURE_MASK(FEATURE_APCC_DFLL_BIT)
    | FEATURE_MASK(FEATURE_FW_CTF_BIT)
    | FEATURE_MASK(FEATURE_FAN_CONTROL_BIT)
    | FEATURE_MASK(FEATURE_THERMAL_BIT)
    | FEATURE_MASK(FEATURE_OUT_OF_BAND_MONITOR_BIT);

 if (adev->pm.pp_feature & PP_SCLK_DPM_MASK) {
  *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT);
  *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_GFX_GPO_BIT);
 }

 if ((adev->pm.pp_feature & PP_GFX_DCS_MASK) &&
     (amdgpu_ip_version(adev, MP1_HWIP, 0) > IP_VERSION(11, 0, 7)) &&
     !(adev->flags & AMD_IS_APU))
  *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_GFX_DCS_BIT);

 if (adev->pm.pp_feature & PP_MCLK_DPM_MASK)
  *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_UCLK_BIT)
     | FEATURE_MASK(FEATURE_MEM_VDDCI_SCALING_BIT)
     | FEATURE_MASK(FEATURE_MEM_MVDD_SCALING_BIT);

 if (adev->pm.pp_feature & PP_PCIE_DPM_MASK)
  *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_LINK_BIT);

 if (adev->pm.pp_feature & PP_DCEFCLK_DPM_MASK)
  *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_DCEFCLK_BIT);

 if (adev->pm.pp_feature & PP_SOCCLK_DPM_MASK)
  *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT);

 if (adev->pm.pp_feature & PP_ULV_MASK)
  *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_GFX_ULV_BIT);

 if (adev->pm.pp_feature & PP_SCLK_DEEP_SLEEP_MASK)
  *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DS_GFXCLK_BIT);

 if (adev->pm.pp_feature & PP_GFXOFF_MASK)
  *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_GFXOFF_BIT);

 if (smu->adev->pg_flags & AMD_PG_SUPPORT_ATHUB)
  *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_ATHUB_PG_BIT);

 if (smu->adev->pg_flags & AMD_PG_SUPPORT_MMHUB)
  *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_MMHUB_PG_BIT);

 if (smu->adev->pg_flags & AMD_PG_SUPPORT_VCN ||
     smu->adev->pg_flags & AMD_PG_SUPPORT_JPEG)
  *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_MM_DPM_PG_BIT);

 if (smu->dc_controlled_by_gpio)
       *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_ACDC_BIT);

 if (amdgpu_device_should_use_aspm(adev))
  *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DS_LCLK_BIT);

 return 0;
}

static void sienna_cichlid_check_bxco_support(struct smu_context *smu)
{
 struct smu_table_context *table_context = &smu->smu_table;
 struct smu_11_0_7_powerplay_table *powerplay_table =
  table_context->power_play_table;
 struct smu_baco_context *smu_baco = &smu->smu_baco;
 struct amdgpu_device *adev = smu->adev;
 uint32_t val;

 if (powerplay_table->platform_caps & SMU_11_0_7_PP_PLATFORM_CAP_BACO) {
  val = RREG32_SOC15(NBIO, 0, mmRCC_BIF_STRAP0);
  smu_baco->platform_support =
   (val & RCC_BIF_STRAP0__STRAP_PX_CAPABLE_MASK) ? true :
         false;

  /*
 * Disable BACO entry/exit completely on below SKUs to
 * avoid hardware intermittent failures.
 */

  if (((adev->pdev->device == 0x73A1) &&
      (adev->pdev->revision == 0x00)) ||
      ((adev->pdev->device == 0x73BF) &&
      (adev->pdev->revision == 0xCF)) ||
      ((adev->pdev->device == 0x7422) &&
      (adev->pdev->revision == 0x00)) ||
      ((adev->pdev->device == 0x73A3) &&
      (adev->pdev->revision == 0x00)) ||
      ((adev->pdev->device == 0x73E3) &&
      (adev->pdev->revision == 0x00)))
   smu_baco->platform_support = false;

 }
}

static void sienna_cichlid_check_fan_support(struct smu_context *smu)
{
 struct smu_table_context *table_context = &smu->smu_table;
 PPTable_t *pptable = table_context->driver_pptable;
 uint64_t features = *(uint64_t *) pptable->FeaturesToRun;

 /* Fan control is not possible if PPTable has it disabled */
 smu->adev->pm.no_fan =
  !(features & (1ULL << FEATURE_FAN_CONTROL_BIT));
 if (smu->adev->pm.no_fan)
  dev_info_once(smu->adev->dev,
         "PMFW based fan control disabled");
}

static int sienna_cichlid_check_powerplay_table(struct smu_context *smu)
{
 struct smu_table_context *table_context = &smu->smu_table;
 struct smu_11_0_7_powerplay_table *powerplay_table =
  table_context->power_play_table;

 if (powerplay_table->platform_caps & SMU_11_0_7_PP_PLATFORM_CAP_HARDWAREDC)
  smu->dc_controlled_by_gpio = true;

 sienna_cichlid_check_bxco_support(smu);
 sienna_cichlid_check_fan_support(smu);

 table_context->thermal_controller_type =
  powerplay_table->thermal_controller_type;

 /*
 * Instead of having its own buffer space and get overdrive_table copied,
 * smu->od_settings just points to the actual overdrive_table
 */

 smu->od_settings = &powerplay_table->overdrive_table;

 return 0;
}

static int sienna_cichlid_append_powerplay_table(struct smu_context *smu)
{
 struct atom_smc_dpm_info_v4_9 *smc_dpm_table;
 int index, ret;
 PPTable_beige_goby_t *ppt_beige_goby;
 PPTable_t *ppt;

 if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(11, 0, 13))
  ppt_beige_goby = smu->smu_table.driver_pptable;
 else
  ppt = smu->smu_table.driver_pptable;

 index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1,
         smc_dpm_info);

 ret = amdgpu_atombios_get_data_table(smu->adev, index, NULL, NULL, NULL,
          (uint8_t **)&smc_dpm_table);
 if (ret)
  return ret;

 if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(11, 0, 13))
  smu_memcpy_trailing(ppt_beige_goby, I2cControllers, BoardReserved,
        smc_dpm_table, I2cControllers);
 else
  smu_memcpy_trailing(ppt, I2cControllers, BoardReserved,
        smc_dpm_table, I2cControllers);

 return 0;
}

static int sienna_cichlid_store_powerplay_table(struct smu_context *smu)
{
 struct smu_table_context *table_context = &smu->smu_table;
 struct smu_11_0_7_powerplay_table *powerplay_table =
  table_context->power_play_table;
 int table_size;

 table_size = get_table_size(smu);
 memcpy(table_context->driver_pptable, &powerplay_table->smc_pptable,
        table_size);

 return 0;
}

static int sienna_cichlid_patch_pptable_quirk(struct smu_context *smu)
{
 struct amdgpu_device *adev = smu->adev;
 uint32_t *board_reserved;
 uint16_t *freq_table_gfx;
 uint32_t i;

 /* Fix some OEM SKU specific stability issues */
 GET_PPTABLE_MEMBER(BoardReserved, &board_reserved);
 if ((adev->pdev->device == 0x73DF) &&
     (adev->pdev->revision == 0XC3) &&
     (adev->pdev->subsystem_device == 0x16C2) &&
     (adev->pdev->subsystem_vendor == 0x1043))
  board_reserved[0] = 1387;

 GET_PPTABLE_MEMBER(FreqTableGfx, &freq_table_gfx);
 if ((adev->pdev->device == 0x73DF) &&
     (adev->pdev->revision == 0XC3) &&
     ((adev->pdev->subsystem_device == 0x16C2) ||
     (adev->pdev->subsystem_device == 0x133C)) &&
     (adev->pdev->subsystem_vendor == 0x1043)) {
  for (i = 0; i < NUM_GFXCLK_DPM_LEVELS; i++) {
   if (freq_table_gfx[i] > 2500)
    freq_table_gfx[i] = 2500;
  }
 }

 return 0;
}

static int sienna_cichlid_setup_pptable(struct smu_context *smu)
{
 int ret = 0;

 ret = smu_v11_0_setup_pptable(smu);
 if (ret)
  return ret;

 ret = sienna_cichlid_store_powerplay_table(smu);
 if (ret)
  return ret;

 ret = sienna_cichlid_append_powerplay_table(smu);
 if (ret)
  return ret;

 ret = sienna_cichlid_check_powerplay_table(smu);
 if (ret)
  return ret;

 return sienna_cichlid_patch_pptable_quirk(smu);
}

static int sienna_cichlid_tables_init(struct smu_context *smu)
{
 struct smu_table_context *smu_table = &smu->smu_table;
 struct smu_table *tables = smu_table->tables;
 int table_size;

 table_size = get_table_size(smu);
 SMU_TABLE_INIT(tables, SMU_TABLE_PPTABLE, table_size,
          PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
 SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t),
         PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
 SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetricsExternal_t),
         PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
 SMU_TABLE_INIT(tables, SMU_TABLE_I2C_COMMANDS, sizeof(SwI2cRequest_t),
         PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
 SMU_TABLE_INIT(tables, SMU_TABLE_OVERDRIVE, sizeof(OverDriveTable_t),
         PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
 SMU_TABLE_INIT(tables, SMU_TABLE_PMSTATUSLOG, SMU11_TOOL_SIZE,
         PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
 SMU_TABLE_INIT(tables, SMU_TABLE_ACTIVITY_MONITOR_COEFF,
         sizeof(DpmActivityMonitorCoeffIntExternal_t), PAGE_SIZE,
                AMDGPU_GEM_DOMAIN_VRAM);
 SMU_TABLE_INIT(tables, SMU_TABLE_ECCINFO, sizeof(EccInfoTable_t),
   PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
 SMU_TABLE_INIT(tables, SMU_TABLE_DRIVER_SMU_CONFIG, sizeof(DriverSmuConfigExternal_t),
         PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);

 smu_table->metrics_table = kzalloc(sizeof(SmuMetricsExternal_t), GFP_KERNEL);
 if (!smu_table->metrics_table)
  goto err0_out;
 smu_table->metrics_time = 0;

 smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v1_3);
 smu_table->gpu_metrics_table = kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL);
 if (!smu_table->gpu_metrics_table)
  goto err1_out;

 smu_table->watermarks_table = kzalloc(sizeof(Watermarks_t), GFP_KERNEL);
 if (!smu_table->watermarks_table)
  goto err2_out;

 smu_table->ecc_table = kzalloc(tables[SMU_TABLE_ECCINFO].size, GFP_KERNEL);
 if (!smu_table->ecc_table)
  goto err3_out;

 smu_table->driver_smu_config_table =
  kzalloc(tables[SMU_TABLE_DRIVER_SMU_CONFIG].size, GFP_KERNEL);
 if (!smu_table->driver_smu_config_table)
  goto err4_out;

 return 0;

err4_out:
 kfree(smu_table->ecc_table);
err3_out:
 kfree(smu_table->watermarks_table);
err2_out:
 kfree(smu_table->gpu_metrics_table);
err1_out:
 kfree(smu_table->metrics_table);
err0_out:
 return -ENOMEM;
}

static uint32_t sienna_cichlid_get_throttler_status_locked(struct smu_context *smu,
          bool use_metrics_v3,
          bool use_metrics_v2)
{
 struct smu_table_context *smu_table= &smu->smu_table;
 SmuMetricsExternal_t *metrics_ext =
  (SmuMetricsExternal_t *)(smu_table->metrics_table);
 uint32_t throttler_status = 0;
 int i;

 if (use_metrics_v3) {
  for (i = 0; i < THROTTLER_COUNT; i++)
   throttler_status |=
    (metrics_ext->SmuMetrics_V3.ThrottlingPercentage[i] ? 1U << i : 0);
 } else if (use_metrics_v2) {
  for (i = 0; i < THROTTLER_COUNT; i++)
   throttler_status |=
    (metrics_ext->SmuMetrics_V2.ThrottlingPercentage[i] ? 1U << i : 0);
 } else {
  throttler_status = metrics_ext->SmuMetrics.ThrottlerStatus;
 }

 return throttler_status;
}

static bool sienna_cichlid_is_od_feature_supported(struct smu_11_0_7_overdrive_table *od_table,
         enum SMU_11_0_7_ODFEATURE_CAP cap)
{
 return od_table->cap[cap];
}

static int sienna_cichlid_get_power_limit(struct smu_context *smu,
       uint32_t *current_power_limit,
       uint32_t *default_power_limit,
       uint32_t *max_power_limit,
       uint32_t *min_power_limit)
{
 struct smu_11_0_7_powerplay_table *powerplay_table =
  (struct smu_11_0_7_powerplay_table *)smu->smu_table.power_play_table;
 struct smu_11_0_7_overdrive_table *od_settings = smu->od_settings;
 uint32_t power_limit, od_percent_upper = 0, od_percent_lower = 0;
 uint16_t *table_member;

 GET_PPTABLE_MEMBER(SocketPowerLimitAc, &table_member);

 if (smu_v11_0_get_current_power_limit(smu, &power_limit)) {
  power_limit =
   table_member[PPT_THROTTLER_PPT0];
 }

 if (current_power_limit)
  *current_power_limit = power_limit;
 if (default_power_limit)
  *default_power_limit = power_limit;

 if (powerplay_table) {
  if (smu->od_enabled &&
    sienna_cichlid_is_od_feature_supported(od_settings, SMU_11_0_7_ODCAP_POWER_LIMIT)) {
   od_percent_upper = le32_to_cpu(powerplay_table->overdrive_table.max[SMU_11_0_7_ODSETTING_POWERPERCENTAGE]);
   od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[SMU_11_0_7_ODSETTING_POWERPERCENTAGE]);
  } else if ((sienna_cichlid_is_od_feature_supported(od_settings, SMU_11_0_7_ODCAP_POWER_LIMIT))) {
   od_percent_upper = 0;
   od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[SMU_11_0_7_ODSETTING_POWERPERCENTAGE]);
  }
 }

 dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n",
     od_percent_upper, od_percent_lower, power_limit);

 if (max_power_limit) {
  *max_power_limit = power_limit * (100 + od_percent_upper);
  *max_power_limit /= 100;
 }

 if (min_power_limit) {
  *min_power_limit = power_limit * (100 - od_percent_lower);
  *min_power_limit /= 100;
 }
 return 0;
}

static void sienna_cichlid_get_smartshift_power_percentage(struct smu_context *smu,
     uint32_t *apu_percent,
     uint32_t *dgpu_percent)
{
 struct smu_table_context *smu_table = &smu->smu_table;
 SmuMetrics_V4_t *metrics_v4 =
  &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics_V4);
 uint16_t powerRatio = 0;
 uint16_t apu_power_limit = 0;
 uint16_t dgpu_power_limit = 0;
 uint32_t apu_boost = 0;
 uint32_t dgpu_boost = 0;
 uint32_t cur_power_limit;

 if (metrics_v4->ApuSTAPMSmartShiftLimit != 0) {
  sienna_cichlid_get_power_limit(smu, &cur_power_limit, NULL, NULL, NULL);
  apu_power_limit = metrics_v4->ApuSTAPMLimit;
  dgpu_power_limit = cur_power_limit;
  powerRatio = (((apu_power_limit +
        dgpu_power_limit) * 100) /
        metrics_v4->ApuSTAPMSmartShiftLimit);
  if (powerRatio > 100) {
   apu_power_limit = (apu_power_limit * 100) /
          powerRatio;
   dgpu_power_limit = (dgpu_power_limit * 100) /
           powerRatio;
  }
  if (metrics_v4->AverageApuSocketPower > apu_power_limit &&
    apu_power_limit != 0) {
   apu_boost = ((metrics_v4->AverageApuSocketPower -
       apu_power_limit) * 100) /
       apu_power_limit;
   if (apu_boost > 100)
    apu_boost = 100;
  }

  if (metrics_v4->AverageSocketPower > dgpu_power_limit &&
    dgpu_power_limit != 0) {
   dgpu_boost = ((metrics_v4->AverageSocketPower -
        dgpu_power_limit) * 100) /
        dgpu_power_limit;
   if (dgpu_boost > 100)
    dgpu_boost = 100;
  }

  if (dgpu_boost >= apu_boost)
   apu_boost = 0;
  else
   dgpu_boost = 0;
 }
 *apu_percent = apu_boost;
 *dgpu_percent = dgpu_boost;
}

static int sienna_cichlid_get_smu_metrics_data(struct smu_context *smu,
            MetricsMember_t member,
            uint32_t *value)
{
 struct smu_table_context *smu_table= &smu->smu_table;
 SmuMetrics_t *metrics =
  &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics);
 SmuMetrics_V2_t *metrics_v2 =
  &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics_V2);
 SmuMetrics_V3_t *metrics_v3 =
  &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics_V3);
 bool use_metrics_v2 = false;
 bool use_metrics_v3 = false;
 uint16_t average_gfx_activity;
 int ret = 0;
 uint32_t apu_percent = 0;
 uint32_t dgpu_percent = 0;

 switch (amdgpu_ip_version(smu->adev, MP1_HWIP, 0)) {
 case IP_VERSION(11, 0, 7):
  if (smu->smc_fw_version >= 0x3A4900)
   use_metrics_v3 = true;
  else if (smu->smc_fw_version >= 0x3A4300)
   use_metrics_v2 = true;
  break;
 case IP_VERSION(11, 0, 11):
  if (smu->smc_fw_version >= 0x412D00)
   use_metrics_v2 = true;
  break;
 case IP_VERSION(11, 0, 12):
  if (smu->smc_fw_version >= 0x3B2300)
   use_metrics_v2 = true;
  break;
 case IP_VERSION(11, 0, 13):
  if (smu->smc_fw_version >= 0x491100)
   use_metrics_v2 = true;
  break;
 default:
  break;
 }

 ret = smu_cmn_get_metrics_table(smu,
     NULL,
     false);
 if (ret)
  return ret;

 switch (member) {
 case METRICS_CURR_GFXCLK:
  *value = use_metrics_v3 ? metrics_v3->CurrClock[PPCLK_GFXCLK] :
   use_metrics_v2 ? metrics_v2->CurrClock[PPCLK_GFXCLK] :
   metrics->CurrClock[PPCLK_GFXCLK];
  break;
 case METRICS_CURR_SOCCLK:
  *value = use_metrics_v3 ? metrics_v3->CurrClock[PPCLK_SOCCLK] :
   use_metrics_v2 ? metrics_v2->CurrClock[PPCLK_SOCCLK] :
   metrics->CurrClock[PPCLK_SOCCLK];
  break;
 case METRICS_CURR_UCLK:
  *value = use_metrics_v3 ? metrics_v3->CurrClock[PPCLK_UCLK] :
   use_metrics_v2 ? metrics_v2->CurrClock[PPCLK_UCLK] :
   metrics->CurrClock[PPCLK_UCLK];
  break;
 case METRICS_CURR_VCLK:
  *value = use_metrics_v3 ? metrics_v3->CurrClock[PPCLK_VCLK_0] :
   use_metrics_v2 ? metrics_v2->CurrClock[PPCLK_VCLK_0] :
   metrics->CurrClock[PPCLK_VCLK_0];
  break;
 case METRICS_CURR_VCLK1:
  *value = use_metrics_v3 ? metrics_v3->CurrClock[PPCLK_VCLK_1] :
   use_metrics_v2 ? metrics_v2->CurrClock[PPCLK_VCLK_1] :
   metrics->CurrClock[PPCLK_VCLK_1];
  break;
 case METRICS_CURR_DCLK:
  *value = use_metrics_v3 ? metrics_v3->CurrClock[PPCLK_DCLK_0] :
   use_metrics_v2 ? metrics_v2->CurrClock[PPCLK_DCLK_0] :
   metrics->CurrClock[PPCLK_DCLK_0];
  break;
 case METRICS_CURR_DCLK1:
  *value = use_metrics_v3 ? metrics_v3->CurrClock[PPCLK_DCLK_1] :
   use_metrics_v2 ? metrics_v2->CurrClock[PPCLK_DCLK_1] :
   metrics->CurrClock[PPCLK_DCLK_1];
  break;
 case METRICS_CURR_DCEFCLK:
  *value = use_metrics_v3 ? metrics_v3->CurrClock[PPCLK_DCEFCLK] :
   use_metrics_v2 ? metrics_v2->CurrClock[PPCLK_DCEFCLK] :
   metrics->CurrClock[PPCLK_DCEFCLK];
  break;
 case METRICS_CURR_FCLK:
  *value = use_metrics_v3 ? metrics_v3->CurrClock[PPCLK_FCLK] :
   use_metrics_v2 ? metrics_v2->CurrClock[PPCLK_FCLK] :
   metrics->CurrClock[PPCLK_FCLK];
  break;
 case METRICS_AVERAGE_GFXCLK:
  average_gfx_activity = use_metrics_v3 ? metrics_v3->AverageGfxActivity :
   use_metrics_v2 ? metrics_v2->AverageGfxActivity :
   metrics->AverageGfxActivity;
  if (average_gfx_activity <= SMU_11_0_7_GFX_BUSY_THRESHOLD)
   *value = use_metrics_v3 ? metrics_v3->AverageGfxclkFrequencyPostDs :
    use_metrics_v2 ? metrics_v2->AverageGfxclkFrequencyPostDs :
    metrics->AverageGfxclkFrequencyPostDs;
  else
   *value = use_metrics_v3 ? metrics_v3->AverageGfxclkFrequencyPreDs :
    use_metrics_v2 ? metrics_v2->AverageGfxclkFrequencyPreDs :
    metrics->AverageGfxclkFrequencyPreDs;
  break;
 case METRICS_AVERAGE_FCLK:
  *value = use_metrics_v3 ? metrics_v3->AverageFclkFrequencyPostDs :
   use_metrics_v2 ? metrics_v2->AverageFclkFrequencyPostDs :
   metrics->AverageFclkFrequencyPostDs;
  break;
 case METRICS_AVERAGE_UCLK:
  *value = use_metrics_v3 ? metrics_v3->AverageUclkFrequencyPostDs :
   use_metrics_v2 ? metrics_v2->AverageUclkFrequencyPostDs :
   metrics->AverageUclkFrequencyPostDs;
  break;
 case METRICS_AVERAGE_GFXACTIVITY:
  *value = use_metrics_v3 ? metrics_v3->AverageGfxActivity :
   use_metrics_v2 ? metrics_v2->AverageGfxActivity :
   metrics->AverageGfxActivity;
  break;
 case METRICS_AVERAGE_MEMACTIVITY:
  *value = use_metrics_v3 ? metrics_v3->AverageUclkActivity :
   use_metrics_v2 ? metrics_v2->AverageUclkActivity :
   metrics->AverageUclkActivity;
  break;
 case METRICS_AVERAGE_SOCKETPOWER:
  *value = use_metrics_v3 ? metrics_v3->AverageSocketPower << 8 :
   use_metrics_v2 ? metrics_v2->AverageSocketPower << 8 :
   metrics->AverageSocketPower << 8;
  break;
 case METRICS_TEMPERATURE_EDGE:
  *value = (use_metrics_v3 ? metrics_v3->TemperatureEdge :
   use_metrics_v2 ? metrics_v2->TemperatureEdge :
   metrics->TemperatureEdge) * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
  break;
 case METRICS_TEMPERATURE_HOTSPOT:
  *value = (use_metrics_v3 ? metrics_v3->TemperatureHotspot :
   use_metrics_v2 ? metrics_v2->TemperatureHotspot :
   metrics->TemperatureHotspot) * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
  break;
 case METRICS_TEMPERATURE_MEM:
  *value = (use_metrics_v3 ? metrics_v3->TemperatureMem :
   use_metrics_v2 ? metrics_v2->TemperatureMem :
   metrics->TemperatureMem) * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
  break;
 case METRICS_TEMPERATURE_VRGFX:
  *value = (use_metrics_v3 ? metrics_v3->TemperatureVrGfx :
   use_metrics_v2 ? metrics_v2->TemperatureVrGfx :
   metrics->TemperatureVrGfx) * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
  break;
 case METRICS_TEMPERATURE_VRSOC:
  *value = (use_metrics_v3 ? metrics_v3->TemperatureVrSoc :
   use_metrics_v2 ? metrics_v2->TemperatureVrSoc :
   metrics->TemperatureVrSoc) * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
  break;
 case METRICS_THROTTLER_STATUS:
  *value = sienna_cichlid_get_throttler_status_locked(smu, use_metrics_v3, use_metrics_v2);
  break;
 case METRICS_CURR_FANSPEED:
  *value = use_metrics_v3 ? metrics_v3->CurrFanSpeed :
   use_metrics_v2 ? metrics_v2->CurrFanSpeed : metrics->CurrFanSpeed;
  break;
 case METRICS_UNIQUE_ID_UPPER32:
  /* Only supported in 0x3A5300+, metrics_v3 requires 0x3A4900+ */
  *value = use_metrics_v3 ? metrics_v3->PublicSerialNumUpper32 : 0;
  break;
 case METRICS_UNIQUE_ID_LOWER32:
  /* Only supported in 0x3A5300+, metrics_v3 requires 0x3A4900+ */
  *value = use_metrics_v3 ? metrics_v3->PublicSerialNumLower32 : 0;
  break;
 case METRICS_SS_APU_SHARE:
  sienna_cichlid_get_smartshift_power_percentage(smu, &apu_percent, &dgpu_percent);
  *value = apu_percent;
  break;
 case METRICS_SS_DGPU_SHARE:
  sienna_cichlid_get_smartshift_power_percentage(smu, &apu_percent, &dgpu_percent);
  *value = dgpu_percent;
  break;

 default:
  *value = UINT_MAX;
  break;
 }

 return ret;

}

static int sienna_cichlid_allocate_dpm_context(struct smu_context *smu)
{
 struct smu_dpm_context *smu_dpm = &smu->smu_dpm;

 smu_dpm->dpm_context = kzalloc(sizeof(struct smu_11_0_dpm_context),
           GFP_KERNEL);
 if (!smu_dpm->dpm_context)
  return -ENOMEM;

 smu_dpm->dpm_context_size = sizeof(struct smu_11_0_dpm_context);

 return 0;
}

static void sienna_cichlid_stb_init(struct smu_context *smu);

static int sienna_cichlid_init_smc_tables(struct smu_context *smu)
{
 struct amdgpu_device *adev = smu->adev;
 int ret = 0;

 ret = sienna_cichlid_tables_init(smu);
 if (ret)
  return ret;

 ret = sienna_cichlid_allocate_dpm_context(smu);
 if (ret)
  return ret;

 if (!amdgpu_sriov_vf(adev))
  sienna_cichlid_stb_init(smu);

 return smu_v11_0_init_smc_tables(smu);
}

static int sienna_cichlid_set_default_dpm_table(struct smu_context *smu)
{
 struct smu_11_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
 struct smu_11_0_dpm_table *dpm_table;
 struct amdgpu_device *adev = smu->adev;
 int i, ret = 0;
 DpmDescriptor_t *table_member;

 /* socclk dpm table setup */
 dpm_table = &dpm_context->dpm_tables.soc_table;
 GET_PPTABLE_MEMBER(DpmDescriptor, &table_member);
 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_SOCCLK_BIT)) {
  ret = smu_v11_0_set_single_dpm_table(smu,
           SMU_SOCCLK,
           dpm_table);
  if (ret)
   return ret;
  dpm_table->is_fine_grained =
   !table_member[PPCLK_SOCCLK].SnapToDiscrete;
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.socclk / 100;
  dpm_table->dpm_levels[0].enabled = true;
  dpm_table->min = dpm_table->dpm_levels[0].value;
  dpm_table->max = dpm_table->dpm_levels[0].value;
 }

 /* gfxclk dpm table setup */
 dpm_table = &dpm_context->dpm_tables.gfx_table;
 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_GFXCLK_BIT)) {
  ret = smu_v11_0_set_single_dpm_table(smu,
           SMU_GFXCLK,
           dpm_table);
  if (ret)
   return ret;
  dpm_table->is_fine_grained =
   !table_member[PPCLK_GFXCLK].SnapToDiscrete;
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.gfxclk / 100;
  dpm_table->dpm_levels[0].enabled = true;
  dpm_table->min = dpm_table->dpm_levels[0].value;
  dpm_table->max = dpm_table->dpm_levels[0].value;
 }

 /* uclk dpm table setup */
 dpm_table = &dpm_context->dpm_tables.uclk_table;
 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT)) {
  ret = smu_v11_0_set_single_dpm_table(smu,
           SMU_UCLK,
           dpm_table);
  if (ret)
   return ret;
  dpm_table->is_fine_grained =
   !table_member[PPCLK_UCLK].SnapToDiscrete;
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.uclk / 100;
  dpm_table->dpm_levels[0].enabled = true;
  dpm_table->min = dpm_table->dpm_levels[0].value;
  dpm_table->max = dpm_table->dpm_levels[0].value;
 }

 /* fclk dpm table setup */
 dpm_table = &dpm_context->dpm_tables.fclk_table;
 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_FCLK_BIT)) {
  ret = smu_v11_0_set_single_dpm_table(smu,
           SMU_FCLK,
           dpm_table);
  if (ret)
   return ret;
  dpm_table->is_fine_grained =
   !table_member[PPCLK_FCLK].SnapToDiscrete;
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.fclk / 100;
  dpm_table->dpm_levels[0].enabled = true;
  dpm_table->min = dpm_table->dpm_levels[0].value;
  dpm_table->max = dpm_table->dpm_levels[0].value;
 }

 /* vclk0/1 dpm table setup */
 for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
  if (adev->vcn.harvest_config & (1 << i))
   continue;

  dpm_table = &dpm_context->dpm_tables.vclk_table;
  if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_MM_DPM_PG_BIT)) {
   ret = smu_v11_0_set_single_dpm_table(smu,
            i ? SMU_VCLK1 : SMU_VCLK,
            dpm_table);
   if (ret)
    return ret;
   dpm_table->is_fine_grained =
    !table_member[i ? PPCLK_VCLK_1 : PPCLK_VCLK_0].SnapToDiscrete;
  } else {
   dpm_table->count = 1;
   dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.vclk / 100;
   dpm_table->dpm_levels[0].enabled = true;
   dpm_table->min = dpm_table->dpm_levels[0].value;
   dpm_table->max = dpm_table->dpm_levels[0].value;
  }
 }

 /* dclk0/1 dpm table setup */
 for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
  if (adev->vcn.harvest_config & (1 << i))
   continue;
  dpm_table = &dpm_context->dpm_tables.dclk_table;
  if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_MM_DPM_PG_BIT)) {
   ret = smu_v11_0_set_single_dpm_table(smu,
            i ? SMU_DCLK1 : SMU_DCLK,
            dpm_table);
   if (ret)
    return ret;
   dpm_table->is_fine_grained =
    !table_member[i ? PPCLK_DCLK_1 : PPCLK_DCLK_0].SnapToDiscrete;
  } else {
   dpm_table->count = 1;
   dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dclk / 100;
   dpm_table->dpm_levels[0].enabled = true;
   dpm_table->min = dpm_table->dpm_levels[0].value;
   dpm_table->max = dpm_table->dpm_levels[0].value;
  }
 }

 /* dcefclk dpm table setup */
 dpm_table = &dpm_context->dpm_tables.dcef_table;
 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCEFCLK_BIT)) {
  ret = smu_v11_0_set_single_dpm_table(smu,
           SMU_DCEFCLK,
           dpm_table);
  if (ret)
   return ret;
  dpm_table->is_fine_grained =
   !table_member[PPCLK_DCEFCLK].SnapToDiscrete;
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dcefclk / 100;
  dpm_table->dpm_levels[0].enabled = true;
  dpm_table->min = dpm_table->dpm_levels[0].value;
  dpm_table->max = dpm_table->dpm_levels[0].value;
 }

 /* pixelclk dpm table setup */
 dpm_table = &dpm_context->dpm_tables.pixel_table;
 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCEFCLK_BIT)) {
  ret = smu_v11_0_set_single_dpm_table(smu,
           SMU_PIXCLK,
           dpm_table);
  if (ret)
   return ret;
  dpm_table->is_fine_grained =
   !table_member[PPCLK_PIXCLK].SnapToDiscrete;
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dcefclk / 100;
  dpm_table->dpm_levels[0].enabled = true;
  dpm_table->min = dpm_table->dpm_levels[0].value;
  dpm_table->max = dpm_table->dpm_levels[0].value;
 }

 /* displayclk dpm table setup */
 dpm_table = &dpm_context->dpm_tables.display_table;
 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCEFCLK_BIT)) {
  ret = smu_v11_0_set_single_dpm_table(smu,
           SMU_DISPCLK,
           dpm_table);
  if (ret)
   return ret;
  dpm_table->is_fine_grained =
   !table_member[PPCLK_DISPCLK].SnapToDiscrete;
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dcefclk / 100;
  dpm_table->dpm_levels[0].enabled = true;
  dpm_table->min = dpm_table->dpm_levels[0].value;
  dpm_table->max = dpm_table->dpm_levels[0].value;
 }

 /* phyclk dpm table setup */
 dpm_table = &dpm_context->dpm_tables.phy_table;
 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCEFCLK_BIT)) {
  ret = smu_v11_0_set_single_dpm_table(smu,
           SMU_PHYCLK,
           dpm_table);
  if (ret)
   return ret;
  dpm_table->is_fine_grained =
   !table_member[PPCLK_PHYCLK].SnapToDiscrete;
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dcefclk / 100;
  dpm_table->dpm_levels[0].enabled = true;
  dpm_table->min = dpm_table->dpm_levels[0].value;
  dpm_table->max = dpm_table->dpm_levels[0].value;
 }

 return 0;
}

static int sienna_cichlid_dpm_set_vcn_enable(struct smu_context *smu,
           bool enable,
           int inst)
{
 struct amdgpu_device *adev = smu->adev;
 int ret = 0;

 if (adev->vcn.harvest_config & (1 << inst))
  return ret;
 /* vcn dpm on is a prerequisite for vcn power gate messages */
 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_MM_DPM_PG_BIT)) {
  ret = smu_cmn_send_smc_msg_with_param(smu, enable ?
            SMU_MSG_PowerUpVcn : SMU_MSG_PowerDownVcn,
            0x10000 * inst, NULL);
 }

 return ret;
}

static int sienna_cichlid_dpm_set_jpeg_enable(struct smu_context *smu, bool enable)
{
 int ret = 0;

 if (enable) {
  if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_MM_DPM_PG_BIT)) {
   ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_PowerUpJpeg, 0, NULL);
   if (ret)
    return ret;
  }
 } else {
  if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_MM_DPM_PG_BIT)) {
   ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_PowerDownJpeg, 0, NULL);
   if (ret)
    return ret;
  }
 }

 return ret;
}

static int sienna_cichlid_get_current_clk_freq_by_table(struct smu_context *smu,
           enum smu_clk_type clk_type,
           uint32_t *value)
{
 MetricsMember_t member_type;
 int clk_id = 0;

 clk_id = smu_cmn_to_asic_specific_index(smu,
      CMN2ASIC_MAPPING_CLK,
      clk_type);
 if (clk_id < 0)
  return clk_id;

 switch (clk_id) {
 case PPCLK_GFXCLK:
  member_type = METRICS_CURR_GFXCLK;
  break;
 case PPCLK_UCLK:
  member_type = METRICS_CURR_UCLK;
  break;
 case PPCLK_SOCCLK:
  member_type = METRICS_CURR_SOCCLK;
  break;
 case PPCLK_FCLK:
  member_type = METRICS_CURR_FCLK;
  break;
 case PPCLK_VCLK_0:
  member_type = METRICS_CURR_VCLK;
  break;
 case PPCLK_VCLK_1:
  member_type = METRICS_CURR_VCLK1;
  break;
 case PPCLK_DCLK_0:
  member_type = METRICS_CURR_DCLK;
  break;
 case PPCLK_DCLK_1:
  member_type = METRICS_CURR_DCLK1;
  break;
 case PPCLK_DCEFCLK:
  member_type = METRICS_CURR_DCEFCLK;
  break;
 default:
  return -EINVAL;
 }

 return sienna_cichlid_get_smu_metrics_data(smu,
         member_type,
         value);

}

static bool sienna_cichlid_is_support_fine_grained_dpm(struct smu_context *smu, enum smu_clk_type clk_type)
{
 DpmDescriptor_t *dpm_desc = NULL;
 DpmDescriptor_t *table_member;
 uint32_t clk_index = 0;

 GET_PPTABLE_MEMBER(DpmDescriptor, &table_member);
 clk_index = smu_cmn_to_asic_specific_index(smu,
         CMN2ASIC_MAPPING_CLK,
         clk_type);
 dpm_desc = &table_member[clk_index];

 /* 0 - Fine grained DPM, 1 - Discrete DPM */
 return dpm_desc->SnapToDiscrete == 0;
}

static void sienna_cichlid_get_od_setting_range(struct smu_11_0_7_overdrive_table *od_table,
      enum SMU_11_0_7_ODSETTING_ID setting,
      uint32_t *min, uint32_t *max)
{
 if (min)
  *min = od_table->min[setting];
 if (max)
  *max = od_table->max[setting];
}

static int sienna_cichlid_print_clk_levels(struct smu_context *smu,
   enum smu_clk_type clk_type, char *buf)
{
 struct amdgpu_device *adev = smu->adev;
 struct smu_table_context *table_context = &smu->smu_table;
 struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
 struct smu_11_0_dpm_context *dpm_context = smu_dpm->dpm_context;
 uint16_t *table_member;

 struct smu_11_0_7_overdrive_table *od_settings = smu->od_settings;
 OverDriveTable_t *od_table =
  (OverDriveTable_t *)table_context->overdrive_table;
 int i, size = 0, ret = 0;
 uint32_t cur_value = 0, value = 0, count = 0;
 uint32_t freq_values[3] = {0};
 uint32_t mark_index = 0;
 uint32_t gen_speed, lane_width;
 uint32_t min_value, max_value;

 smu_cmn_get_sysfs_buf(&buf, &size);

 switch (clk_type) {
 case SMU_GFXCLK:
 case SMU_SCLK:
 case SMU_SOCCLK:
 case SMU_MCLK:
 case SMU_UCLK:
 case SMU_FCLK:
 case SMU_VCLK:
 case SMU_VCLK1:
 case SMU_DCLK:
 case SMU_DCLK1:
 case SMU_DCEFCLK:
  ret = sienna_cichlid_get_current_clk_freq_by_table(smu, clk_type, &cur_value);
  if (ret)
   goto print_clk_out;

  ret = smu_v11_0_get_dpm_level_count(smu, clk_type, &count);
  if (ret)
   goto print_clk_out;

  if (!sienna_cichlid_is_support_fine_grained_dpm(smu, clk_type)) {
   for (i = 0; i < count; i++) {
    ret = smu_v11_0_get_dpm_freq_by_index(smu, clk_type, i, &value);
    if (ret)
     goto print_clk_out;

    size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n", i, value,
      cur_value == value ? "*" : "");
   }
  } else {
   ret = smu_v11_0_get_dpm_freq_by_index(smu, clk_type, 0, &freq_values[0]);
   if (ret)
    goto print_clk_out;
   ret = smu_v11_0_get_dpm_freq_by_index(smu, clk_type, count - 1, &freq_values[2]);
   if (ret)
    goto print_clk_out;

   freq_values[1] = cur_value;
   mark_index = cur_value == freq_values[0] ? 0 :
         cur_value == freq_values[2] ? 2 : 1;

   count = 3;
   if (mark_index != 1) {
    count = 2;
    freq_values[1] = freq_values[2];
   }

   for (i = 0; i < count; i++) {
    size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n", i, freq_values[i],
      cur_value  == freq_values[i] ? "*" : "");
   }

  }
  break;
 case SMU_PCIE:
  gen_speed = smu_v11_0_get_current_pcie_link_speed_level(smu);
  lane_width = smu_v11_0_get_current_pcie_link_width_level(smu);
  GET_PPTABLE_MEMBER(LclkFreq, &table_member);
  for (i = 0; i < NUM_LINK_LEVELS; i++)
   size += sysfs_emit_at(buf, size, "%d: %s %s %dMhz %s\n", i,
     (dpm_context->dpm_tables.pcie_table.pcie_gen[i] == 0) ? "2.5GT/s," :
     (dpm_context->dpm_tables.pcie_table.pcie_gen[i] == 1) ? "5.0GT/s," :
     (dpm_context->dpm_tables.pcie_table.pcie_gen[i] == 2) ? "8.0GT/s," :
     (dpm_context->dpm_tables.pcie_table.pcie_gen[i] == 3) ? "16.0GT/s," : "",
     (dpm_context->dpm_tables.pcie_table.pcie_lane[i] == 1) ? "x1" :
     (dpm_context->dpm_tables.pcie_table.pcie_lane[i] == 2) ? "x2" :
     (dpm_context->dpm_tables.pcie_table.pcie_lane[i] == 3) ? "x4" :
     (dpm_context->dpm_tables.pcie_table.pcie_lane[i] == 4) ? "x8" :
     (dpm_context->dpm_tables.pcie_table.pcie_lane[i] == 5) ? "x12" :
     (dpm_context->dpm_tables.pcie_table.pcie_lane[i] == 6) ? "x16" : "",
     table_member[i],
     (gen_speed == dpm_context->dpm_tables.pcie_table.pcie_gen[i]) &&
     (lane_width == dpm_context->dpm_tables.pcie_table.pcie_lane[i]) ?
     "*" : "");
  break;
 case SMU_OD_SCLK:
  if (!smu->od_enabled || !od_table || !od_settings)
   break;

  if (!sienna_cichlid_is_od_feature_supported(od_settings, SMU_11_0_7_ODCAP_GFXCLK_LIMITS))
   break;

  size += sysfs_emit_at(buf, size, "OD_SCLK:\n");
  size += sysfs_emit_at(buf, size, "0: %uMhz\n1: %uMhz\n", od_table->GfxclkFmin, od_table->GfxclkFmax);
  break;

 case SMU_OD_MCLK:
  if (!smu->od_enabled || !od_table || !od_settings)
   break;

  if (!sienna_cichlid_is_od_feature_supported(od_settings, SMU_11_0_7_ODCAP_UCLK_LIMITS))
   break;

  size += sysfs_emit_at(buf, size, "OD_MCLK:\n");
  size += sysfs_emit_at(buf, size, "0: %uMhz\n1: %uMHz\n", od_table->UclkFmin, od_table->UclkFmax);
  break;

 case SMU_OD_VDDGFX_OFFSET:
  if (!smu->od_enabled || !od_table || !od_settings)
   break;

  /*
 * OD GFX Voltage Offset functionality is supported only by 58.41.0
 * and onwards SMU firmwares.
 */

  if ((amdgpu_ip_version(adev, MP1_HWIP, 0) ==
       IP_VERSION(11, 0, 7)) &&
      (smu->smc_fw_version < 0x003a2900))
   break;

  size += sysfs_emit_at(buf, size, "OD_VDDGFX_OFFSET:\n");
  size += sysfs_emit_at(buf, size, "%dmV\n", od_table->VddGfxOffset);
  break;

 case SMU_OD_RANGE:
  if (!smu->od_enabled || !od_table || !od_settings)
   break;

  size += sysfs_emit_at(buf, size, "%s:\n""OD_RANGE");

  if (sienna_cichlid_is_od_feature_supported(od_settings, SMU_11_0_7_ODCAP_GFXCLK_LIMITS)) {
   sienna_cichlid_get_od_setting_range(od_settings, SMU_11_0_7_ODSETTING_GFXCLKFMIN,
           &min_value, NULL);
   sienna_cichlid_get_od_setting_range(od_settings, SMU_11_0_7_ODSETTING_GFXCLKFMAX,
           NULL, &max_value);
   size += sysfs_emit_at(buf, size, "SCLK: %7uMhz %10uMhz\n",
     min_value, max_value);
  }

  if (sienna_cichlid_is_od_feature_supported(od_settings, SMU_11_0_7_ODCAP_UCLK_LIMITS)) {
   sienna_cichlid_get_od_setting_range(od_settings, SMU_11_0_7_ODSETTING_UCLKFMIN,
           &min_value, NULL);
   sienna_cichlid_get_od_setting_range(od_settings, SMU_11_0_7_ODSETTING_UCLKFMAX,
           NULL, &max_value);
   size += sysfs_emit_at(buf, size, "MCLK: %7uMhz %10uMhz\n",
     min_value, max_value);
  }
  break;

 default:
  break;
 }

print_clk_out:
 return size;
}

static int sienna_cichlid_force_clk_levels(struct smu_context *smu,
       enum smu_clk_type clk_type, uint32_t mask)
{
 int ret = 0;
 uint32_t soft_min_level = 0, soft_max_level = 0, min_freq = 0, max_freq = 0;

 soft_min_level = mask ? (ffs(mask) - 1) : 0;
 soft_max_level = mask ? (fls(mask) - 1) : 0;

 switch (clk_type) {
 case SMU_GFXCLK:
 case SMU_SCLK:
 case SMU_SOCCLK:
 case SMU_MCLK:
 case SMU_UCLK:
 case SMU_FCLK:
  /* There is only 2 levels for fine grained DPM */
  if (sienna_cichlid_is_support_fine_grained_dpm(smu, clk_type)) {
   soft_max_level = (soft_max_level >= 1 ? 1 : 0);
   soft_min_level = (soft_min_level >= 1 ? 1 : 0);
  }

  ret = smu_v11_0_get_dpm_freq_by_index(smu, clk_type, soft_min_level, &min_freq);
  if (ret)
   goto forec_level_out;

  ret = smu_v11_0_get_dpm_freq_by_index(smu, clk_type, soft_max_level, &max_freq);
  if (ret)
   goto forec_level_out;

  ret = smu_v11_0_set_soft_freq_limited_range(smu, clk_type, min_freq, max_freq, false);
  if (ret)
   goto forec_level_out;
  break;
 case SMU_DCEFCLK:
  dev_info(smu->adev->dev,"Setting DCEFCLK min/max dpm level is not supported!\n");
  break;
 default:
  break;
 }

forec_level_out:
 return 0;
}

static int sienna_cichlid_populate_umd_state_clk(struct smu_context *smu)
{
 struct smu_11_0_dpm_context *dpm_context =
    smu->smu_dpm.dpm_context;
 struct smu_11_0_dpm_table *gfx_table =
    &dpm_context->dpm_tables.gfx_table;
 struct smu_11_0_dpm_table *mem_table =
    &dpm_context->dpm_tables.uclk_table;
 struct smu_11_0_dpm_table *soc_table =
    &dpm_context->dpm_tables.soc_table;
 struct smu_umd_pstate_table *pstate_table =
    &smu->pstate_table;
 struct amdgpu_device *adev = smu->adev;

 pstate_table->gfxclk_pstate.min = gfx_table->min;
 pstate_table->gfxclk_pstate.peak = gfx_table->max;

 pstate_table->uclk_pstate.min = mem_table->min;
 pstate_table->uclk_pstate.peak = mem_table->max;

 pstate_table->socclk_pstate.min = soc_table->min;
 pstate_table->socclk_pstate.peak = soc_table->max;

 switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) {
 case IP_VERSION(11, 0, 7):
 case IP_VERSION(11, 0, 11):
  pstate_table->gfxclk_pstate.standard = SIENNA_CICHLID_UMD_PSTATE_PROFILING_GFXCLK;
  pstate_table->uclk_pstate.standard = SIENNA_CICHLID_UMD_PSTATE_PROFILING_MEMCLK;
  pstate_table->socclk_pstate.standard = SIENNA_CICHLID_UMD_PSTATE_PROFILING_SOCCLK;
  break;
 case IP_VERSION(11, 0, 12):
  pstate_table->gfxclk_pstate.standard = DIMGREY_CAVEFISH_UMD_PSTATE_PROFILING_GFXCLK;
  pstate_table->uclk_pstate.standard = DIMGREY_CAVEFISH_UMD_PSTATE_PROFILING_MEMCLK;
  pstate_table->socclk_pstate.standard = DIMGREY_CAVEFISH_UMD_PSTATE_PROFILING_SOCCLK;
  break;
 case IP_VERSION(11, 0, 13):
  pstate_table->gfxclk_pstate.standard = BEIGE_GOBY_UMD_PSTATE_PROFILING_GFXCLK;
  pstate_table->uclk_pstate.standard = BEIGE_GOBY_UMD_PSTATE_PROFILING_MEMCLK;
  pstate_table->socclk_pstate.standard = BEIGE_GOBY_UMD_PSTATE_PROFILING_SOCCLK;
  break;
 default:
  break;
 }

 return 0;
}

static int sienna_cichlid_pre_display_config_changed(struct smu_context *smu)
{
 int ret = 0;
 uint32_t max_freq = 0;

 /* Sienna_Cichlid do not support to change display num currently */
 return 0;
#if 0
 ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_NumOfDisplays, 0, NULL);
 if (ret)
  return ret;
#endif

 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT)) {
  ret = smu_v11_0_get_dpm_ultimate_freq(smu, SMU_UCLK, NULL, &max_freq);
  if (ret)
   return ret;
  ret = smu_v11_0_set_hard_freq_limited_range(smu, SMU_UCLK, 0, max_freq);
  if (ret)
   return ret;
 }

 return ret;
}

static int sienna_cichlid_display_config_changed(struct smu_context *smu)
{
 int ret = 0;

 if ((smu->watermarks_bitmap & WATERMARKS_EXIST) &&
     smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCEFCLK_BIT) &&
     smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_SOCCLK_BIT)) {
#if 0
  ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_NumOfDisplays,
        smu->display_config->num_display,
        NULL);
#endif
  if (ret)
   return ret;
 }

 return ret;
}

static bool sienna_cichlid_is_dpm_running(struct smu_context *smu)
{
 int ret = 0;
 uint64_t feature_enabled;

 ret = smu_cmn_get_enabled_mask(smu, &feature_enabled);
 if (ret)
  return false;

 return !!(feature_enabled & SMC_DPM_FEATURE);
}

static int sienna_cichlid_get_fan_speed_rpm(struct smu_context *smu,
         uint32_t *speed)
{
 if (!speed)
  return -EINVAL;

 /*
 * For Sienna_Cichlid and later, the fan speed(rpm) reported
 * by pmfw is always trustable(even when the fan control feature
 * disabled or 0 RPM kicked in).
 */

 return sienna_cichlid_get_smu_metrics_data(smu,
         METRICS_CURR_FANSPEED,
         speed);
}

static int sienna_cichlid_get_fan_parameters(struct smu_context *smu)
{
 uint16_t *table_member;

 GET_PPTABLE_MEMBER(FanMaximumRpm, &table_member);
 smu->fan_max_rpm = *table_member;

 return 0;
}

static int sienna_cichlid_get_power_profile_mode(struct smu_context *smu, char *buf)
{
 DpmActivityMonitorCoeffIntExternal_t activity_monitor_external;
 DpmActivityMonitorCoeffInt_t *activity_monitor =
  &(activity_monitor_external.DpmActivityMonitorCoeffInt);
 uint32_t i, size = 0;
 int16_t workload_type = 0;
 static const char *title[] = {
   "PROFILE_INDEX(NAME)",
   "CLOCK_TYPE(NAME)",
   "FPS",
   "MinFreqType",
   "MinActiveFreqType",
   "MinActiveFreq",
   "BoosterFreqType",
   "BoosterFreq",
   "PD_Data_limit_c",
   "PD_Data_error_coeff",
   "PD_Data_error_rate_coeff"};
 int result = 0;

 if (!buf)
  return -EINVAL;

 size += sysfs_emit_at(buf, size, "%16s %s %s %s %s %s %s %s %s %s %s\n",
   title[0], title[1], title[2], title[3], title[4], title[5],
   title[6], title[7], title[8], title[9], title[10]);

 for (i = 0; i <= PP_SMC_POWER_PROFILE_CUSTOM; i++) {
  /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
  workload_type = smu_cmn_to_asic_specific_index(smu,
              CMN2ASIC_MAPPING_WORKLOAD,
              i);
  if (workload_type < 0)
   return -EINVAL;

  result = smu_cmn_update_table(smu,
       SMU_TABLE_ACTIVITY_MONITOR_COEFF, workload_type,
       (void *)(&activity_monitor_external), false);
  if (result) {
   dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
   return result;
  }

  size += sysfs_emit_at(buf, size, "%2d %14s%s:\n",
   i, amdgpu_pp_profile_name[i], (i == smu->power_profile_mode) ? "*" : " ");

  size += sysfs_emit_at(buf, size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d %7d\n",
   " ",
   0,
   "GFXCLK",
   activity_monitor->Gfx_FPS,
   activity_monitor->Gfx_MinFreqStep,
   activity_monitor->Gfx_MinActiveFreqType,
   activity_monitor->Gfx_MinActiveFreq,
   activity_monitor->Gfx_BoosterFreqType,
   activity_monitor->Gfx_BoosterFreq,
   activity_monitor->Gfx_PD_Data_limit_c,
   activity_monitor->Gfx_PD_Data_error_coeff,
   activity_monitor->Gfx_PD_Data_error_rate_coeff);

  size += sysfs_emit_at(buf, size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d %7d\n",
   " ",
   1,
   "SOCCLK",
   activity_monitor->Fclk_FPS,
   activity_monitor->Fclk_MinFreqStep,
   activity_monitor->Fclk_MinActiveFreqType,
   activity_monitor->Fclk_MinActiveFreq,
   activity_monitor->Fclk_BoosterFreqType,
   activity_monitor->Fclk_BoosterFreq,
   activity_monitor->Fclk_PD_Data_limit_c,
   activity_monitor->Fclk_PD_Data_error_coeff,
   activity_monitor->Fclk_PD_Data_error_rate_coeff);

  size += sysfs_emit_at(buf, size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d %7d\n",
   " ",
   2,
   "MEMCLK",
   activity_monitor->Mem_FPS,
   activity_monitor->Mem_MinFreqStep,
   activity_monitor->Mem_MinActiveFreqType,
   activity_monitor->Mem_MinActiveFreq,
   activity_monitor->Mem_BoosterFreqType,
   activity_monitor->Mem_BoosterFreq,
   activity_monitor->Mem_PD_Data_limit_c,
   activity_monitor->Mem_PD_Data_error_coeff,
   activity_monitor->Mem_PD_Data_error_rate_coeff);
 }

 return size;
}

#define SIENNA_CICHLID_CUSTOM_PARAMS_COUNT 10
#define SIENNA_CICHLID_CUSTOM_PARAMS_CLOCK_COUNT 3
#define SIENNA_CICHLID_CUSTOM_PARAMS_SIZE (SIENNA_CICHLID_CUSTOM_PARAMS_CLOCK_COUNT * SIENNA_CICHLID_CUSTOM_PARAMS_COUNT * sizeof(long))

static int sienna_cichlid_set_power_profile_mode_coeff(struct smu_context *smu,
             long *input)
{

 DpmActivityMonitorCoeffIntExternal_t activity_monitor_external;
 DpmActivityMonitorCoeffInt_t *activity_monitor =
  &(activity_monitor_external.DpmActivityMonitorCoeffInt);
 int ret, idx;

 ret = smu_cmn_update_table(smu,
       SMU_TABLE_ACTIVITY_MONITOR_COEFF, WORKLOAD_PPLIB_CUSTOM_BIT,
       (void *)(&activity_monitor_external), false);
 if (ret) {
  dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
  return ret;
 }

 idx = 0 * SIENNA_CICHLID_CUSTOM_PARAMS_COUNT;
 if (input[idx]) {
  /* Gfxclk */
  activity_monitor->Gfx_FPS = input[idx + 1];
  activity_monitor->Gfx_MinFreqStep = input[idx + 2];
  activity_monitor->Gfx_MinActiveFreqType = input[idx + 3];
  activity_monitor->Gfx_MinActiveFreq = input[idx + 4];
  activity_monitor->Gfx_BoosterFreqType = input[idx + 5];
  activity_monitor->Gfx_BoosterFreq = input[idx + 6];
  activity_monitor->Gfx_PD_Data_limit_c = input[idx + 7];
  activity_monitor->Gfx_PD_Data_error_coeff = input[idx + 8];
  activity_monitor->Gfx_PD_Data_error_rate_coeff = input[idx + 9];
 }
 idx = 1 * SIENNA_CICHLID_CUSTOM_PARAMS_COUNT;
 if (input[idx]) {
  /* Socclk */
  activity_monitor->Fclk_FPS = input[idx + 1];
  activity_monitor->Fclk_MinFreqStep = input[idx + 2];
  activity_monitor->Fclk_MinActiveFreqType = input[idx + 3];
  activity_monitor->Fclk_MinActiveFreq = input[idx + 4];
  activity_monitor->Fclk_BoosterFreqType = input[idx + 5];
  activity_monitor->Fclk_BoosterFreq = input[idx + 6];
  activity_monitor->Fclk_PD_Data_limit_c = input[idx + 7];
  activity_monitor->Fclk_PD_Data_error_coeff = input[idx + 8];
  activity_monitor->Fclk_PD_Data_error_rate_coeff = input[idx + 9];
 }
 idx = 2 * SIENNA_CICHLID_CUSTOM_PARAMS_COUNT;
 if (input[idx]) {
  /* Memclk */
  activity_monitor->Mem_FPS = input[idx + 1];
  activity_monitor->Mem_MinFreqStep = input[idx + 2];
  activity_monitor->Mem_MinActiveFreqType = input[idx + 3];
  activity_monitor->Mem_MinActiveFreq = input[idx + 4];
  activity_monitor->Mem_BoosterFreqType = input[idx + 5];
  activity_monitor->Mem_BoosterFreq = input[idx + 6];
  activity_monitor->Mem_PD_Data_limit_c = input[idx + 7];
  activity_monitor->Mem_PD_Data_error_coeff = input[idx + 8];
  activity_monitor->Mem_PD_Data_error_rate_coeff = input[idx + 9];
 }

 ret = smu_cmn_update_table(smu,
       SMU_TABLE_ACTIVITY_MONITOR_COEFF, WORKLOAD_PPLIB_CUSTOM_BIT,
       (void *)(&activity_monitor_external), true);
 if (ret) {
  dev_err(smu->adev->dev, "[%s] Failed to set activity monitor!", __func__);
  return ret;
 }

 return ret;
}

static int sienna_cichlid_set_power_profile_mode(struct smu_context *smu,
       u32 workload_mask,
       long *custom_params,
       u32 custom_params_max_idx)
{
 u32 backend_workload_mask = 0;
 int ret, idx = -1, i;

 smu_cmn_get_backend_workload_mask(smu, workload_mask,
       &backend_workload_mask);

 if (workload_mask & (1 << PP_SMC_POWER_PROFILE_CUSTOM)) {
  if (!smu->custom_profile_params) {
   smu->custom_profile_params =
    kzalloc(SIENNA_CICHLID_CUSTOM_PARAMS_SIZE, GFP_KERNEL);
   if (!smu->custom_profile_params)
    return -ENOMEM;
  }
  if (custom_params && custom_params_max_idx) {
   if (custom_params_max_idx != SIENNA_CICHLID_CUSTOM_PARAMS_COUNT)
    return -EINVAL;
   if (custom_params[0] >= SIENNA_CICHLID_CUSTOM_PARAMS_CLOCK_COUNT)
    return -EINVAL;
   idx = custom_params[0] * SIENNA_CICHLID_CUSTOM_PARAMS_COUNT;
   smu->custom_profile_params[idx] = 1;
   for (i = 1; i < custom_params_max_idx; i++)
    smu->custom_profile_params[idx + i] = custom_params[i];
  }
  ret = sienna_cichlid_set_power_profile_mode_coeff(smu,
          smu->custom_profile_params);
  if (ret) {
   if (idx != -1)
    smu->custom_profile_params[idx] = 0;
   return ret;
  }
 } else if (smu->custom_profile_params) {
  memset(smu->custom_profile_params, 0, SIENNA_CICHLID_CUSTOM_PARAMS_SIZE);
 }

 ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetWorkloadMask,
           backend_workload_mask, NULL);
 if (ret) {
  dev_err(smu->adev->dev, "Failed to set workload mask 0x%08x\n",
   workload_mask);
  if (idx != -1)
   smu->custom_profile_params[idx] = 0;
  return ret;
 }

 return ret;
}

static int sienna_cichlid_notify_smc_display_config(struct smu_context *smu)
{
 struct smu_clocks min_clocks = {0};
 struct pp_display_clock_request clock_req;
 int ret = 0;

 min_clocks.dcef_clock = smu->display_config->min_dcef_set_clk;
 min_clocks.dcef_clock_in_sr = smu->display_config->min_dcef_deep_sleep_set_clk;
 min_clocks.memory_clock = smu->display_config->min_mem_set_clock;

 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCEFCLK_BIT)) {
  clock_req.clock_type = amd_pp_dcef_clock;
  clock_req.clock_freq_in_khz = min_clocks.dcef_clock * 10;

  ret = smu_v11_0_display_clock_voltage_request(smu, &clock_req);
  if (!ret) {
   if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DS_DCEFCLK_BIT)) {
    ret = smu_cmn_send_smc_msg_with_param(smu,
          SMU_MSG_SetMinDeepSleepDcefclk,
          min_clocks.dcef_clock_in_sr/100,
          NULL);
    if (ret) {
     dev_err(smu->adev->dev, "Attempt to set divider for DCEFCLK Failed!");
     return ret;
    }
   }
  } else {
   dev_info(smu->adev->dev, "Attempt to set Hard Min for DCEFCLK Failed!");
  }
 }

 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT)) {
  ret = smu_v11_0_set_hard_freq_limited_range(smu, SMU_UCLK, min_clocks.memory_clock/100, 0);
  if (ret) {
   dev_err(smu->adev->dev, "[%s] Set hard min uclk failed!", __func__);
   return ret;
  }
 }

 return 0;
}

static int sienna_cichlid_set_watermarks_table(struct smu_context *smu,
            struct pp_smu_wm_range_sets *clock_ranges)
{
 Watermarks_t *table = smu->smu_table.watermarks_table;
 int ret = 0;
 int i;

 if (clock_ranges) {
  if (clock_ranges->num_reader_wm_sets > NUM_WM_RANGES ||
      clock_ranges->num_writer_wm_sets > NUM_WM_RANGES)
   return -EINVAL;

  for (i = 0; i < clock_ranges->num_reader_wm_sets; i++) {
   table->WatermarkRow[WM_DCEFCLK][i].MinClock =
    clock_ranges->reader_wm_sets[i].min_drain_clk_mhz;
   table->WatermarkRow[WM_DCEFCLK][i].MaxClock =
    clock_ranges->reader_wm_sets[i].max_drain_clk_mhz;
   table->WatermarkRow[WM_DCEFCLK][i].MinUclk =
    clock_ranges->reader_wm_sets[i].min_fill_clk_mhz;
   table->WatermarkRow[WM_DCEFCLK][i].MaxUclk =
    clock_ranges->reader_wm_sets[i].max_fill_clk_mhz;

   table->WatermarkRow[WM_DCEFCLK][i].WmSetting =
    clock_ranges->reader_wm_sets[i].wm_inst;
  }

  for (i = 0; i < clock_ranges->num_writer_wm_sets; i++) {
   table->WatermarkRow[WM_SOCCLK][i].MinClock =
    clock_ranges->writer_wm_sets[i].min_fill_clk_mhz;
   table->WatermarkRow[WM_SOCCLK][i].MaxClock =
    clock_ranges->writer_wm_sets[i].max_fill_clk_mhz;
   table->WatermarkRow[WM_SOCCLK][i].MinUclk =
    clock_ranges->writer_wm_sets[i].min_drain_clk_mhz;
   table->WatermarkRow[WM_SOCCLK][i].MaxUclk =
    clock_ranges->writer_wm_sets[i].max_drain_clk_mhz;

   table->WatermarkRow[WM_SOCCLK][i].WmSetting =
    clock_ranges->writer_wm_sets[i].wm_inst;
  }

  smu->watermarks_bitmap |= WATERMARKS_EXIST;
 }

 if ((smu->watermarks_bitmap & WATERMARKS_EXIST) &&
      !(smu->watermarks_bitmap & WATERMARKS_LOADED)) {
  ret = smu_cmn_write_watermarks_table(smu);
  if (ret) {
   dev_err(smu->adev->dev, "Failed to update WMTABLE!");
   return ret;
  }
  smu->watermarks_bitmap |= WATERMARKS_LOADED;
 }

 return 0;
}

static int sienna_cichlid_read_sensor(struct smu_context *smu,
     enum amd_pp_sensors sensor,
     void *data, uint32_t *size)
{
 int ret = 0;
 uint16_t *temp;
 struct amdgpu_device *adev = smu->adev;

 if(!data || !size)
  return -EINVAL;

 switch (sensor) {
 case AMDGPU_PP_SENSOR_MAX_FAN_RPM:
  GET_PPTABLE_MEMBER(FanMaximumRpm, &temp);
  *(uint16_t *)data = *temp;
  *size = 4;
  break;
 case AMDGPU_PP_SENSOR_MEM_LOAD:
  ret = sienna_cichlid_get_smu_metrics_data(smu,
         METRICS_AVERAGE_MEMACTIVITY,
         (uint32_t *)data);
  *size = 4;
  break;
 case AMDGPU_PP_SENSOR_GPU_LOAD:
  ret = sienna_cichlid_get_smu_metrics_data(smu,
         METRICS_AVERAGE_GFXACTIVITY,
         (uint32_t *)data);
  *size = 4;
  break;
 case AMDGPU_PP_SENSOR_GPU_AVG_POWER:
  ret = sienna_cichlid_get_smu_metrics_data(smu,
         METRICS_AVERAGE_SOCKETPOWER,
         (uint32_t *)data);
  *size = 4;
  break;
 case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
  ret = sienna_cichlid_get_smu_metrics_data(smu,
         METRICS_TEMPERATURE_HOTSPOT,
         (uint32_t *)data);
  *size = 4;
  break;
 case AMDGPU_PP_SENSOR_EDGE_TEMP:
  ret = sienna_cichlid_get_smu_metrics_data(smu,
--> --------------------

--> maximum size reached

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

Messung V0.5
C=95 H=91 G=92

¤ Dauer der Verarbeitung: 0.20 Sekunden  (vorverarbeitet)  ¤

*© 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.