diff options
Diffstat (limited to 'common/recipes-kernel/linux/files/0695-drm-amd-powerplay-add-CG-and-PG-support-for-carrizo.patch')
-rw-r--r-- | common/recipes-kernel/linux/files/0695-drm-amd-powerplay-add-CG-and-PG-support-for-carrizo.patch | 1366 |
1 files changed, 1366 insertions, 0 deletions
diff --git a/common/recipes-kernel/linux/files/0695-drm-amd-powerplay-add-CG-and-PG-support-for-carrizo.patch b/common/recipes-kernel/linux/files/0695-drm-amd-powerplay-add-CG-and-PG-support-for-carrizo.patch new file mode 100644 index 00000000..e8707cd6 --- /dev/null +++ b/common/recipes-kernel/linux/files/0695-drm-amd-powerplay-add-CG-and-PG-support-for-carrizo.patch @@ -0,0 +1,1366 @@ +From 581a35785664cdae19a1c61ad360f662fba60cfc Mon Sep 17 00:00:00 2001 +From: Rex Zhu <Rex.Zhu@amd.com> +Date: Wed, 23 Sep 2015 15:14:38 +0800 +Subject: [PATCH 0695/1050] drm/amd/powerplay: add CG and PG support for + carrizo + +This adds clock and powergating support for CZ. + +v2: squash in fixes + +Signed-off-by: Rex Zhu <Rex.Zhu@amd.com> +Reviewed-by: Jammy Zhou <Jammy.Zhou@amd.com> +Reviewed-by: Alex Deucher <alexander.deucher@amd.com> +--- + drivers/gpu/drm/amd/powerplay/hwmgr/Makefile | 3 +- + .../drm/amd/powerplay/hwmgr/cz_clockpowergating.c | 252 ++++++++++ + .../drm/amd/powerplay/hwmgr/cz_clockpowergating.h | 37 ++ + drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c | 521 ++++++++++++++++++++- + drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.h | 12 +- + .../gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c | 89 +++- + drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c | 10 + + .../gpu/drm/amd/powerplay/inc/hardwaremanager.h | 53 ++- + drivers/gpu/drm/amd/powerplay/inc/hwmgr.h | 46 +- + drivers/gpu/drm/amd/powerplay/inc/pp_asicblocks.h | 47 ++ + 10 files changed, 1058 insertions(+), 12 deletions(-) + create mode 100644 drivers/gpu/drm/amd/powerplay/hwmgr/cz_clockpowergating.c + create mode 100644 drivers/gpu/drm/amd/powerplay/hwmgr/cz_clockpowergating.h + create mode 100644 drivers/gpu/drm/amd/powerplay/inc/pp_asicblocks.h + +diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/Makefile b/drivers/gpu/drm/amd/powerplay/hwmgr/Makefile +index 22d383e..46cc494 100644 +--- a/drivers/gpu/drm/amd/powerplay/hwmgr/Makefile ++++ b/drivers/gpu/drm/amd/powerplay/hwmgr/Makefile +@@ -3,7 +3,8 @@ + # It provides the hardware management services for the driver. + + HARDWARE_MGR = hwmgr.o processpptables.o functiontables.o \ +- hardwaremanager.o pp_acpi.o cz_hwmgr.o ++ hardwaremanager.o pp_acpi.o cz_hwmgr.o \ ++ cz_clockpowergating.o + + AMD_PP_HWMGR = $(addprefix $(AMD_PP_PATH)/hwmgr/,$(HARDWARE_MGR)) + +diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_clockpowergating.c b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_clockpowergating.c +new file mode 100644 +index 0000000..ad77008 +--- /dev/null ++++ b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_clockpowergating.c +@@ -0,0 +1,252 @@ ++/* ++ * Copyright 2015 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 "hwmgr.h" ++#include "cz_clockpowergating.h" ++#include "cz_ppsmc.h" ++ ++/* PhyID -> Status Mapping in DDI_PHY_GEN_STATUS ++ 0 GFX0L (3:0), (27:24), ++ 1 GFX0H (7:4), (31:28), ++ 2 GFX1L (3:0), (19:16), ++ 3 GFX1H (7:4), (23:20), ++ 4 DDIL (3:0), (11: 8), ++ 5 DDIH (7:4), (15:12), ++ 6 DDI2L (3:0), ( 3: 0), ++ 7 DDI2H (7:4), ( 7: 4), ++*/ ++#define DDI_PHY_GEN_STATUS_VAL(phyID) (1 << ((3 - ((phyID & 0x07)/2))*8 + (phyID & 0x01)*4)) ++#define IS_PHY_ID_USED_BY_PLL(PhyID) (((0xF3 & (1 << PhyID)) & 0xFF) ? true : false) ++ ++ ++int cz_phm_set_asic_block_gating(struct pp_hwmgr *hwmgr, enum PHM_AsicBlock block, enum PHM_ClockGateSetting gating) ++{ ++ int ret = 0; ++ ++ switch (block) { ++ case PHM_AsicBlock_UVD_MVC: ++ case PHM_AsicBlock_UVD: ++ case PHM_AsicBlock_UVD_HD: ++ case PHM_AsicBlock_UVD_SD: ++ if (gating == PHM_ClockGateSetting_StaticOff) ++ ret = cz_dpm_powerdown_uvd(hwmgr); ++ else ++ ret = cz_dpm_powerup_uvd(hwmgr); ++ break; ++ case PHM_AsicBlock_GFX: ++ default: ++ break; ++ } ++ ++ return ret; ++} ++ ++ ++bool cz_phm_is_safe_for_asic_block(struct pp_hwmgr *hwmgr, const struct pp_hw_power_state *state, enum PHM_AsicBlock block) ++{ ++ return true; ++} ++ ++ ++int cz_phm_enable_disable_gfx_power_gating(struct pp_hwmgr *hwmgr, bool enable) ++{ ++ return 0; ++} ++ ++int cz_phm_smu_power_up_down_pcie(struct pp_hwmgr *hwmgr, uint32_t target, bool up, uint32_t args) ++{ ++ /* TODO */ ++ return 0; ++} ++ ++int cz_phm_initialize_display_phy_access(struct pp_hwmgr *hwmgr, bool initialize, bool accesshw) ++{ ++ /* TODO */ ++ return 0; ++} ++ ++int cz_phm_get_display_phy_access_info(struct pp_hwmgr *hwmgr) ++{ ++ /* TODO */ ++ return 0; ++} ++ ++int cz_phm_gate_unused_display_phys(struct pp_hwmgr *hwmgr) ++{ ++ /* TODO */ ++ return 0; ++} ++ ++int cz_phm_ungate_all_display_phys(struct pp_hwmgr *hwmgr) ++{ ++ /* TODO */ ++ return 0; ++} ++ ++static int cz_tf_uvd_power_gating_initialize(struct pp_hwmgr *hwmgr, void *pInput, void *pOutput, void *pStorage, int Result) ++{ ++ return 0; ++} ++ ++static int cz_tf_vce_power_gating_initialize(struct pp_hwmgr *hwmgr, void *pInput, void *pOutput, void *pStorage, int Result) ++{ ++ return 0; ++} ++ ++int cz_enable_disable_uvd_dpm(struct pp_hwmgr *hwmgr, bool enable) ++{ ++ struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend); ++ uint32_t dpm_features = 0; ++ ++ if (enable && ++ phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, ++ PHM_PlatformCaps_UVDDPM)) { ++ cz_hwmgr->dpm_flags |= DPMFlags_UVD_Enabled; ++ dpm_features |= UVD_DPM_MASK; ++ smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, ++ PPSMC_MSG_EnableAllSmuFeatures, dpm_features); ++ } else { ++ dpm_features |= UVD_DPM_MASK; ++ cz_hwmgr->dpm_flags &= ~DPMFlags_UVD_Enabled; ++ smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, ++ PPSMC_MSG_DisableAllSmuFeatures, dpm_features); ++ } ++ return 0; ++} ++ ++int cz_enable_disable_vce_dpm(struct pp_hwmgr *hwmgr, bool enable) ++{ ++ struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend); ++ uint32_t dpm_features = 0; ++ ++ if (enable && phm_cap_enabled( ++ hwmgr->platform_descriptor.platformCaps, ++ PHM_PlatformCaps_VCEDPM)) { ++ cz_hwmgr->dpm_flags |= DPMFlags_VCE_Enabled; ++ dpm_features |= VCE_DPM_MASK; ++ smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, ++ PPSMC_MSG_EnableAllSmuFeatures, dpm_features); ++ } else { ++ dpm_features |= VCE_DPM_MASK; ++ cz_hwmgr->dpm_flags &= ~DPMFlags_VCE_Enabled; ++ smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, ++ PPSMC_MSG_DisableAllSmuFeatures, dpm_features); ++ } ++ ++ return 0; ++} ++ ++ ++int cz_dpm_powergate_uvd(struct pp_hwmgr *hwmgr, bool bgate) ++{ ++ struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend); ++ ++ if (cz_hwmgr->uvd_power_gated == bgate) ++ return 0; ++ ++ cz_hwmgr->uvd_power_gated = bgate; ++ ++ if (bgate) { ++ cgs_set_clockgating_state(hwmgr->device, ++ AMD_IP_BLOCK_TYPE_UVD, ++ AMD_CG_STATE_UNGATE); ++ cgs_set_powergating_state(hwmgr->device, ++ AMD_IP_BLOCK_TYPE_UVD, ++ AMD_PG_STATE_GATE); ++ cz_dpm_update_uvd_dpm(hwmgr, true); ++ cz_dpm_powerdown_uvd(hwmgr); ++ } else { ++ cz_dpm_powerup_uvd(hwmgr); ++ cgs_set_clockgating_state(hwmgr->device, ++ AMD_IP_BLOCK_TYPE_UVD, ++ AMD_PG_STATE_GATE); ++ cgs_set_powergating_state(hwmgr->device, ++ AMD_IP_BLOCK_TYPE_UVD, ++ AMD_CG_STATE_UNGATE); ++ cz_dpm_update_uvd_dpm(hwmgr, false); ++ } ++ ++ return 0; ++} ++ ++int cz_dpm_powergate_vce(struct pp_hwmgr *hwmgr, bool bgate) ++{ ++ struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend); ++ ++ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, ++ PHM_PlatformCaps_VCEPowerGating)) { ++ if (cz_hwmgr->vce_power_gated != bgate) { ++ if (bgate) { ++ cgs_set_clockgating_state( ++ hwmgr->device, ++ AMD_IP_BLOCK_TYPE_VCE, ++ AMD_CG_STATE_UNGATE); ++ cgs_set_powergating_state( ++ hwmgr->device, ++ AMD_IP_BLOCK_TYPE_VCE, ++ AMD_PG_STATE_GATE); ++ cz_enable_disable_vce_dpm(hwmgr, false); ++ /* TODO: to figure out why vce can't be poweroff*/ ++ cz_hwmgr->vce_power_gated = true; ++ } else { ++ cz_dpm_powerup_vce(hwmgr); ++ cz_hwmgr->vce_power_gated = false; ++ cgs_set_clockgating_state( ++ hwmgr->device, ++ AMD_IP_BLOCK_TYPE_VCE, ++ AMD_PG_STATE_GATE); ++ cgs_set_powergating_state( ++ hwmgr->device, ++ AMD_IP_BLOCK_TYPE_VCE, ++ AMD_CG_STATE_UNGATE); ++ cz_dpm_update_vce_dpm(hwmgr); ++ cz_enable_disable_vce_dpm(hwmgr, true); ++ return 0; ++ } ++ } ++ } else { ++ cz_dpm_update_vce_dpm(hwmgr); ++ cz_enable_disable_vce_dpm(hwmgr, true); ++ return 0; ++ } ++ ++ if (!cz_hwmgr->vce_power_gated) ++ cz_dpm_update_vce_dpm(hwmgr); ++ ++ return 0; ++} ++ ++ ++static struct phm_master_table_item cz_enable_clock_power_gatings_list[] = { ++ /*we don't need an exit table here, because there is only D3 cold on Kv*/ ++ { phm_cf_want_uvd_power_gating, cz_tf_uvd_power_gating_initialize }, ++ { phm_cf_want_vce_power_gating, cz_tf_vce_power_gating_initialize }, ++ /* to do { NULL, cz_tf_xdma_power_gating_enable }, */ ++ { NULL, NULL } ++}; ++ ++struct phm_master_table_header cz_phm_enable_clock_power_gatings_master = { ++ 0, ++ PHM_MasterTableFlag_None, ++ cz_enable_clock_power_gatings_list ++}; +diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_clockpowergating.h b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_clockpowergating.h +new file mode 100644 +index 0000000..bbbc057 +--- /dev/null ++++ b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_clockpowergating.h +@@ -0,0 +1,37 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ */ ++ ++#ifndef _CZ_CLOCK_POWER_GATING_H_ ++#define _CZ_CLOCK_POWER_GATING_H_ ++ ++#include "cz_hwmgr.h" ++#include "pp_asicblocks.h" ++ ++extern int cz_phm_set_asic_block_gating(struct pp_hwmgr *hwmgr, enum PHM_AsicBlock block, enum PHM_ClockGateSetting gating); ++extern struct phm_master_table_header cz_phm_enable_clock_power_gatings_master; ++extern struct phm_master_table_header cz_phm_disable_clock_power_gatings_master; ++extern int cz_dpm_powergate_vce(struct pp_hwmgr *hwmgr, bool bgate); ++extern int cz_dpm_powergate_uvd(struct pp_hwmgr *hwmgr, bool bgate); ++extern int cz_enable_disable_vce_dpm(struct pp_hwmgr *hwmgr, bool enable); ++extern int cz_enable_disable_uvd_dpm(struct pp_hwmgr *hwmgr, bool enable); ++#endif /* _CZ_CLOCK_POWER_GATING_H_ */ +diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c +index 0c49505..e187b3f 100644 +--- a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c ++++ b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c +@@ -28,12 +28,23 @@ + #include "processpptables.h" + #include "cgs_common.h" + #include "smu/smu_8_0_d.h" ++#include "smu8_fusion.h" ++#include "smu/smu_8_0_sh_mask.h" + #include "smumgr.h" + #include "hwmgr.h" + #include "hardwaremanager.h" + #include "cz_ppsmc.h" + #include "cz_hwmgr.h" + #include "power_state.h" ++#include "cz_clockpowergating.h" ++ ++ ++#define ixSMUSVI_NB_CURRENTVID 0xD8230044 ++#define CURRENT_NB_VID_MASK 0xff000000 ++#define CURRENT_NB_VID__SHIFT 24 ++#define ixSMUSVI_GFX_CURRENTVID 0xD8230048 ++#define CURRENT_GFX_VID_MASK 0xff000000 ++#define CURRENT_GFX_VID__SHIFT 24 + + static const unsigned long PhwCz_Magic = (unsigned long) PHM_Cz_Magic; + +@@ -45,6 +56,46 @@ static struct cz_power_state *cast_PhwCzPowerState(struct pp_hw_power_state *hw_ + return (struct cz_power_state *)hw_ps; + } + ++static const struct cz_power_state *cast_const_PhwCzPowerState( ++ const struct pp_hw_power_state *hw_ps) ++{ ++ if (PhwCz_Magic != hw_ps->magic) ++ return NULL; ++ ++ return (struct cz_power_state *)hw_ps; ++} ++ ++uint32_t cz_get_eclk_level(struct pp_hwmgr *hwmgr, ++ uint32_t clock, uint32_t msg) ++{ ++ int i = 0; ++ struct phm_vce_clock_voltage_dependency_table *ptable = ++ hwmgr->dyn_state.vce_clocl_voltage_dependency_table; ++ ++ switch (msg) { ++ case PPSMC_MSG_SetEclkSoftMin: ++ case PPSMC_MSG_SetEclkHardMin: ++ for (i = 0; i < (int)ptable->count; i++) { ++ if (clock <= ptable->entries[i].ecclk) ++ break; ++ } ++ break; ++ ++ case PPSMC_MSG_SetEclkSoftMax: ++ case PPSMC_MSG_SetEclkHardMax: ++ for (i = ptable->count - 1; i >= 0; i--) { ++ if (clock >= ptable->entries[i].ecclk) ++ break; ++ } ++ break; ++ ++ default: ++ break; ++ } ++ ++ return i; ++} ++ + static uint32_t cz_get_sclk_level(struct pp_hwmgr *hwmgr, + uint32_t clock, uint32_t msg) + { +@@ -75,6 +126,37 @@ static uint32_t cz_get_sclk_level(struct pp_hwmgr *hwmgr, + return i; + } + ++static uint32_t cz_get_uvd_level(struct pp_hwmgr *hwmgr, ++ uint32_t clock, uint32_t msg) ++{ ++ int i = 0; ++ struct phm_uvd_clock_voltage_dependency_table *ptable = ++ hwmgr->dyn_state.uvd_clocl_voltage_dependency_table; ++ ++ switch (msg) { ++ case PPSMC_MSG_SetUvdSoftMin: ++ case PPSMC_MSG_SetUvdHardMin: ++ for (i = 0; i < (int)ptable->count; i++) { ++ if (clock <= ptable->entries[i].vclk) ++ break; ++ } ++ break; ++ ++ case PPSMC_MSG_SetUvdSoftMax: ++ case PPSMC_MSG_SetUvdHardMax: ++ for (i = ptable->count - 1; i >= 0; i--) { ++ if (clock >= ptable->entries[i].vclk) ++ break; ++ } ++ break; ++ ++ default: ++ break; ++ } ++ ++ return i; ++} ++ + static uint32_t cz_get_max_sclk_level(struct pp_hwmgr *hwmgr) + { + struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend); +@@ -504,6 +586,175 @@ static int cz_tf_init_sclk_threshold(struct pp_hwmgr *hwmgr, void *input, + + return 0; + } ++static int cz_tf_update_sclk_limit(struct pp_hwmgr *hwmgr, ++ void *input, void *output, ++ void *storage, int result) ++{ ++ struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend); ++ struct phm_clock_voltage_dependency_table *table = ++ hwmgr->dyn_state.vddc_dependency_on_sclk; ++ ++ unsigned long clock = 0; ++ unsigned long level; ++ unsigned long stable_pstate_sclk; ++ struct PP_Clocks clocks; ++ unsigned long percentage; ++ ++ cz_hwmgr->sclk_dpm.soft_min_clk = table->entries[0].clk; ++ level = cz_get_max_sclk_level(hwmgr) - 1; ++ ++ if (level < table->count) ++ cz_hwmgr->sclk_dpm.soft_max_clk = table->entries[level].clk; ++ else ++ cz_hwmgr->sclk_dpm.soft_max_clk = table->entries[table->count - 1].clk; ++ ++ /*PECI_GetMinClockSettings(pHwMgr->pPECI, &clocks);*/ ++ clock = clocks.engineClock; ++ ++ if (cz_hwmgr->sclk_dpm.hard_min_clk != clock) { ++ cz_hwmgr->sclk_dpm.hard_min_clk = clock; ++ ++ smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, ++ PPSMC_MSG_SetSclkHardMin, ++ cz_get_sclk_level(hwmgr, ++ cz_hwmgr->sclk_dpm.hard_min_clk, ++ PPSMC_MSG_SetSclkHardMin)); ++ } ++ ++ clock = cz_hwmgr->sclk_dpm.soft_min_clk; ++ ++ /* update minimum clocks for Stable P-State feature */ ++ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, ++ PHM_PlatformCaps_StablePState)) { ++ percentage = 75; ++ /*Sclk - calculate sclk value based on percentage and find FLOOR sclk from VddcDependencyOnSCLK table */ ++ stable_pstate_sclk = (hwmgr->dyn_state.max_clock_voltage_on_ac.mclk * ++ percentage) / 100; ++ ++ if (clock < stable_pstate_sclk) ++ clock = stable_pstate_sclk; ++ } else { ++ if (clock < hwmgr->gfx_arbiter.sclk) ++ clock = hwmgr->gfx_arbiter.sclk; ++ } ++ ++ if (cz_hwmgr->sclk_dpm.soft_min_clk != clock) { ++ cz_hwmgr->sclk_dpm.soft_min_clk = clock; ++ smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, ++ PPSMC_MSG_SetSclkSoftMin, ++ cz_get_sclk_level(hwmgr, ++ cz_hwmgr->sclk_dpm.soft_min_clk, ++ PPSMC_MSG_SetSclkSoftMin)); ++ } ++ ++ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, ++ PHM_PlatformCaps_StablePState) && ++ cz_hwmgr->sclk_dpm.soft_max_clk != clock) { ++ cz_hwmgr->sclk_dpm.soft_max_clk = clock; ++ smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, ++ PPSMC_MSG_SetSclkSoftMax, ++ cz_get_sclk_level(hwmgr, ++ cz_hwmgr->sclk_dpm.soft_max_clk, ++ PPSMC_MSG_SetSclkSoftMax)); ++ } ++ ++ return 0; ++} ++ ++static int cz_tf_set_deep_sleep_sclk_threshold(struct pp_hwmgr *hwmgr, ++ void *input, void *output, ++ void *storage, int result) ++{ ++ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, ++ PHM_PlatformCaps_SclkDeepSleep)) { ++ /* TO DO get from dal PECI_GetMinClockSettings(pHwMgr->pPECI, &clocks); */ ++ smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, ++ PPSMC_MSG_SetMinDeepSleepSclk, ++ CZ_MIN_DEEP_SLEEP_SCLK); ++ } ++ ++ return 0; ++} ++ ++static int cz_tf_set_watermark_threshold(struct pp_hwmgr *hwmgr, ++ void *input, void *output, ++ void *storage, int result) ++{ ++ struct cz_hwmgr *cz_hwmgr = ++ (struct cz_hwmgr *)(hwmgr->backend); ++ ++ smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, ++ PPSMC_MSG_SetWatermarkFrequency, ++ cz_hwmgr->sclk_dpm.soft_max_clk); ++ ++ return 0; ++} ++ ++static int cz_tf_set_enabled_levels(struct pp_hwmgr *hwmgr, ++ void *input, void *output, ++ void *storage, int result) ++{ ++ return 0; ++} ++ ++static int cz_tf_enable_nb_dpm(struct pp_hwmgr *hwmgr, ++ void *input, void *output, ++ void *storage, int result) ++{ ++ int ret = 0; ++ struct cz_hwmgr *cz_hwmgr = ++ (struct cz_hwmgr *)(hwmgr->backend); ++ unsigned long dpm_features = 0; ++ ++ if (!cz_hwmgr->is_nb_dpm_enabled && ++ cz_hwmgr->is_nb_dpm_enabled_by_driver) { /* also depend on dal NBPStateDisableRequired */ ++ dpm_features |= NB_DPM_MASK; ++ ret = smum_send_msg_to_smc_with_parameter( ++ hwmgr->smumgr, ++ PPSMC_MSG_EnableAllSmuFeatures, ++ dpm_features); ++ if (ret == 0) ++ cz_hwmgr->is_nb_dpm_enabled = true; ++ } ++ return ret; ++} ++ ++static int cz_tf_update_low_mem_pstate(struct pp_hwmgr *hwmgr, ++ void *input, void *output, ++ void *storage, int result) ++{ ++ ++ struct cz_hwmgr *cz_hwmgr = ++ (struct cz_hwmgr *)(hwmgr->backend); ++ const struct phm_set_power_state_input *states = (struct phm_set_power_state_input *)input; ++ const struct cz_power_state *pnew_state = cast_const_PhwCzPowerState(states->pnew_state); ++ ++ if (cz_hwmgr->sys_info.nb_dpm_enable) { ++ if (pnew_state->action == FORCE_HIGH) ++ smum_send_msg_to_smc(hwmgr->smumgr, ++ PPSMC_MSG_DisableLowMemoryPstate); ++ else ++ smum_send_msg_to_smc(hwmgr->smumgr, ++ PPSMC_MSG_EnableLowMemoryPstate); ++ } ++ return 0; ++} ++ ++static struct phm_master_table_item cz_set_power_state_list[] = { ++ {NULL, cz_tf_update_sclk_limit}, ++ {NULL, cz_tf_set_deep_sleep_sclk_threshold}, ++ {NULL, cz_tf_set_watermark_threshold}, ++ {NULL, cz_tf_set_enabled_levels}, ++ {NULL, cz_tf_enable_nb_dpm}, ++ {NULL, cz_tf_update_low_mem_pstate}, ++ {NULL, NULL} ++}; ++ ++static struct phm_master_table_header cz_set_power_state_master = { ++ 0, ++ PHM_MasterTableFlag_None, ++ cz_set_power_state_list ++}; + + static struct phm_master_table_item cz_setup_asic_list[] = { + {NULL, cz_tf_reset_active_process_mask}, +@@ -649,6 +900,56 @@ static struct phm_master_table_header cz_enable_dpm_master = { + cz_enable_dpm_list + }; + ++static int cz_apply_state_adjust_rules(struct pp_hwmgr *hwmgr, ++ struct pp_power_state *prequest_ps, ++ const struct pp_power_state *pcurrent_ps) ++{ ++ struct cz_power_state *cz_ps = ++ cast_PhwCzPowerState(&prequest_ps->hardware); ++ ++ const struct cz_power_state *cz_current_ps = ++ cast_const_PhwCzPowerState(&pcurrent_ps->hardware); ++ ++ struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend); ++ struct PP_Clocks clocks; ++ bool force_high; ++ unsigned long num_of_active_displays = 4; ++ ++ cz_ps->evclk = hwmgr->vce_arbiter.evclk; ++ cz_ps->ecclk = hwmgr->vce_arbiter.ecclk; ++ ++ cz_ps->need_dfs_bypass = true; ++ ++ cz_hwmgr->video_start = (hwmgr->uvd_arbiter.vclk != 0 || hwmgr->uvd_arbiter.dclk != 0 || ++ hwmgr->vce_arbiter.evclk != 0 || hwmgr->vce_arbiter.ecclk != 0); ++ ++ cz_hwmgr->battery_state = (PP_StateUILabel_Battery == prequest_ps->classification.ui_label); ++ ++ /* to do PECI_GetMinClockSettings(pHwMgr->pPECI, &clocks); */ ++ /* PECI_GetNumberOfActiveDisplays(pHwMgr->pPECI, &numOfActiveDisplays); */ ++ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_StablePState)) ++ clocks.memoryClock = hwmgr->dyn_state.max_clock_voltage_on_ac.mclk; ++ else ++ clocks.memoryClock = 0; ++ ++ if (clocks.memoryClock < hwmgr->gfx_arbiter.mclk) ++ clocks.memoryClock = hwmgr->gfx_arbiter.mclk; ++ ++ force_high = (clocks.memoryClock > cz_hwmgr->sys_info.nbp_memory_clock[CZ_NUM_NBPMEMORYCLOCK - 1]) ++ || (num_of_active_displays >= 3); ++ ++ cz_ps->action = cz_current_ps->action; ++ ++ if ((force_high == false) && (cz_ps->action == FORCE_HIGH)) ++ cz_ps->action = CANCEL_FORCE_HIGH; ++ else if ((force_high == true) && (cz_ps->action != FORCE_HIGH)) ++ cz_ps->action = FORCE_HIGH; ++ else ++ cz_ps->action = DO_NOTHING; ++ ++ return 0; ++} ++ + static int cz_hwmgr_backend_init(struct pp_hwmgr *hwmgr) + { + int result = 0; +@@ -676,10 +977,28 @@ static int cz_hwmgr_backend_init(struct pp_hwmgr *hwmgr) + + result = phm_construct_table(hwmgr, &cz_disable_dpm_master, + &(hwmgr->disable_dynamic_state_management)); +- ++ if (result != 0) { ++ printk(KERN_ERR "[ powerplay ] Fail to disable_dynamic_state\n"); ++ return result; ++ } + result = phm_construct_table(hwmgr, &cz_enable_dpm_master, + &(hwmgr->enable_dynamic_state_management)); ++ if (result != 0) { ++ printk(KERN_ERR "[ powerplay ] Fail to enable_dynamic_state\n"); ++ return result; ++ } ++ result = phm_construct_table(hwmgr, &cz_set_power_state_master, ++ &(hwmgr->set_power_state)); ++ if (result != 0) { ++ printk(KERN_ERR "[ powerplay ] Fail to construct set_power_state\n"); ++ return result; ++ } + ++ result = phm_construct_table(hwmgr, &cz_phm_enable_clock_power_gatings_master, &(hwmgr->enable_clock_power_gatings)); ++ if (result != 0) { ++ printk(KERN_ERR "[ powerplay ] Fail to construct enable_clock_power_gatings\n"); ++ return result; ++ } + return result; + } + +@@ -793,6 +1112,138 @@ static int cz_dpm_force_dpm_level(struct pp_hwmgr *hwmgr, + return ret; + } + ++int cz_dpm_powerdown_uvd(struct pp_hwmgr *hwmgr) ++{ ++ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, ++ PHM_PlatformCaps_UVDPowerGating)) ++ return smum_send_msg_to_smc(hwmgr->smumgr, ++ PPSMC_MSG_UVDPowerOFF); ++ return 0; ++} ++ ++int cz_dpm_powerup_uvd(struct pp_hwmgr *hwmgr) ++{ ++ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, ++ PHM_PlatformCaps_UVDPowerGating)) { ++ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, ++ PHM_PlatformCaps_UVDDynamicPowerGating)) { ++ return smum_send_msg_to_smc_with_parameter( ++ hwmgr->smumgr, ++ PPSMC_MSG_UVDPowerON, 1); ++ } else { ++ return smum_send_msg_to_smc_with_parameter( ++ hwmgr->smumgr, ++ PPSMC_MSG_UVDPowerON, 0); ++ } ++ } ++ ++ return 0; ++} ++ ++int cz_dpm_update_uvd_dpm(struct pp_hwmgr *hwmgr, bool bgate) ++{ ++ struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend); ++ struct phm_uvd_clock_voltage_dependency_table *ptable = ++ hwmgr->dyn_state.uvd_clocl_voltage_dependency_table; ++ ++ if (!bgate) { ++ /* Stable Pstate is enabled and we need to set the UVD DPM to highest level */ ++ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, ++ PHM_PlatformCaps_StablePState)) { ++ cz_hwmgr->uvd_dpm.hard_min_clk = ++ ptable->entries[ptable->count - 1].vclk; ++ ++ smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, ++ PPSMC_MSG_SetUvdHardMin, ++ cz_get_uvd_level(hwmgr, ++ cz_hwmgr->uvd_dpm.hard_min_clk, ++ PPSMC_MSG_SetUvdHardMin)); ++ ++ cz_enable_disable_uvd_dpm(hwmgr, true); ++ } else ++ cz_enable_disable_uvd_dpm(hwmgr, true); ++ } else ++ cz_enable_disable_uvd_dpm(hwmgr, false); ++ ++ return 0; ++} ++ ++int cz_dpm_update_vce_dpm(struct pp_hwmgr *hwmgr) ++{ ++ struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend); ++ struct phm_vce_clock_voltage_dependency_table *ptable = ++ hwmgr->dyn_state.vce_clocl_voltage_dependency_table; ++ ++ /* Stable Pstate is enabled and we need to set the VCE DPM to highest level */ ++ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, ++ PHM_PlatformCaps_StablePState)) { ++ cz_hwmgr->vce_dpm.hard_min_clk = ++ ptable->entries[ptable->count - 1].ecclk; ++ ++ smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, ++ PPSMC_MSG_SetEclkHardMin, ++ cz_get_eclk_level(hwmgr, ++ cz_hwmgr->vce_dpm.hard_min_clk, ++ PPSMC_MSG_SetEclkHardMin)); ++ } else { ++ /*EPR# 419220 -HW limitation to to */ ++ cz_hwmgr->vce_dpm.hard_min_clk = hwmgr->vce_arbiter.ecclk; ++ smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, ++ PPSMC_MSG_SetEclkHardMin, ++ cz_get_eclk_level(hwmgr, ++ cz_hwmgr->vce_dpm.hard_min_clk, ++ PPSMC_MSG_SetEclkHardMin)); ++ ++ } ++ return 0; ++} ++ ++int cz_dpm_powerdown_vce(struct pp_hwmgr *hwmgr) ++{ ++ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, ++ PHM_PlatformCaps_VCEPowerGating)) ++ return smum_send_msg_to_smc(hwmgr->smumgr, ++ PPSMC_MSG_VCEPowerOFF); ++ return 0; ++} ++ ++int cz_dpm_powerup_vce(struct pp_hwmgr *hwmgr) ++{ ++ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, ++ PHM_PlatformCaps_VCEPowerGating)) ++ return smum_send_msg_to_smc(hwmgr->smumgr, ++ PPSMC_MSG_VCEPowerON); ++ return 0; ++} ++ ++static int cz_dpm_get_mclk(struct pp_hwmgr *hwmgr, bool low) ++{ ++ struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend); ++ ++ return cz_hwmgr->sys_info.bootup_uma_clock; ++} ++ ++static int cz_dpm_get_sclk(struct pp_hwmgr *hwmgr, bool low) ++{ ++ struct pp_power_state *ps; ++ struct cz_power_state *cz_ps; ++ ++ if (hwmgr == NULL) ++ return -EINVAL; ++ ++ ps = hwmgr->request_ps; ++ ++ if (ps == NULL) ++ return -EINVAL; ++ ++ cz_ps = cast_PhwCzPowerState(&ps->hardware); ++ ++ if (low) ++ return cz_ps->levels[0].engineClock; ++ else ++ return cz_ps->levels[cz_ps->level-1].engineClock; ++} ++ + static int cz_dpm_patch_boot_state(struct pp_hwmgr *hwmgr, + struct pp_hw_power_state *hw_ps) + { +@@ -871,15 +1322,83 @@ int cz_get_power_state_size(struct pp_hwmgr *hwmgr) + return sizeof(struct cz_power_state); + } + ++static void ++cz_print_current_perforce_level(struct pp_hwmgr *hwmgr, struct seq_file *m) ++{ ++ struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend); ++ ++ struct phm_clock_voltage_dependency_table *table = ++ hwmgr->dyn_state.vddc_dependency_on_sclk; ++ ++ struct phm_vce_clock_voltage_dependency_table *vce_table = ++ hwmgr->dyn_state.vce_clocl_voltage_dependency_table; ++ ++ struct phm_uvd_clock_voltage_dependency_table *uvd_table = ++ hwmgr->dyn_state.uvd_clocl_voltage_dependency_table; ++ ++ uint32_t sclk_index = PHM_GET_FIELD(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixTARGET_AND_CURRENT_PROFILE_INDEX), ++ TARGET_AND_CURRENT_PROFILE_INDEX, CURR_SCLK_INDEX); ++ uint32_t uvd_index = PHM_GET_FIELD(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixTARGET_AND_CURRENT_PROFILE_INDEX_2), ++ TARGET_AND_CURRENT_PROFILE_INDEX_2, CURR_UVD_INDEX); ++ uint32_t vce_index = PHM_GET_FIELD(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixTARGET_AND_CURRENT_PROFILE_INDEX_2), ++ TARGET_AND_CURRENT_PROFILE_INDEX_2, CURR_VCE_INDEX); ++ ++ uint32_t sclk, vclk, dclk, ecclk, tmp; ++ uint16_t vddnb, vddgfx; ++ ++ if (sclk_index >= NUM_SCLK_LEVELS) { ++ seq_printf(m, "\n invalid sclk dpm profile %d\n", sclk_index); ++ } else { ++ sclk = table->entries[sclk_index].clk; ++ seq_printf(m, "\n index: %u sclk: %u MHz\n", sclk_index, sclk/100); ++ } ++ ++ tmp = (cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixSMUSVI_NB_CURRENTVID) & ++ CURRENT_NB_VID_MASK) >> CURRENT_NB_VID__SHIFT; ++ vddnb = cz_convert_8Bit_index_to_voltage(hwmgr, tmp); ++ tmp = (cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixSMUSVI_GFX_CURRENTVID) & ++ CURRENT_GFX_VID_MASK) >> CURRENT_GFX_VID__SHIFT; ++ vddgfx = cz_convert_8Bit_index_to_voltage(hwmgr, (u16)tmp); ++ seq_printf(m, "\n vddnb: %u vddgfx: %u\n", vddnb, vddgfx); ++ ++ seq_printf(m, "\n uvd %sabled\n", cz_hwmgr->uvd_power_gated ? "dis" : "en"); ++ if (!cz_hwmgr->uvd_power_gated) { ++ if (uvd_index >= CZ_MAX_HARDWARE_POWERLEVELS) { ++ seq_printf(m, "\n invalid uvd dpm level %d\n", uvd_index); ++ } else { ++ vclk = uvd_table->entries[uvd_index].vclk; ++ dclk = uvd_table->entries[uvd_index].dclk; ++ seq_printf(m, "\n index: %u uvd vclk: %u MHz dclk: %u MHz\n", uvd_index, vclk/100, dclk/100); ++ } ++ } ++ ++ seq_printf(m, "\n vce %sabled\n", cz_hwmgr->vce_power_gated ? "dis" : "en"); ++ if (!cz_hwmgr->vce_power_gated) { ++ if (vce_index >= CZ_MAX_HARDWARE_POWERLEVELS) { ++ seq_printf(m, "\n invalid vce dpm level %d\n", vce_index); ++ } else { ++ ecclk = vce_table->entries[vce_index].ecclk; ++ seq_printf(m, "\n index: %u vce ecclk: %u MHz\n", vce_index, ecclk/100); ++ } ++ } ++} ++ + static const struct pp_hwmgr_func cz_hwmgr_funcs = { + .backend_init = cz_hwmgr_backend_init, + .backend_fini = cz_hwmgr_backend_fini, + .asic_setup = NULL, ++ .apply_state_adjust_rules = cz_apply_state_adjust_rules, + .force_dpm_level = cz_dpm_force_dpm_level, + .get_power_state_size = cz_get_power_state_size, ++ .powerdown_uvd = cz_dpm_powerdown_uvd, ++ .powergate_uvd = cz_dpm_powergate_uvd, ++ .powergate_vce = cz_dpm_powergate_vce, ++ .get_mclk = cz_dpm_get_mclk, ++ .get_sclk = cz_dpm_get_sclk, + .patch_boot_state = cz_dpm_patch_boot_state, + .get_pp_table_entry = cz_dpm_get_pp_table_entry, + .get_num_of_pp_table_entries = cz_dpm_get_num_of_pp_table_entries, ++ .print_current_perforce_level = cz_print_current_perforce_level, + }; + + int cz_hwmgr_init(struct pp_hwmgr *hwmgr) +diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.h b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.h +index 05849fd..70b0e51 100644 +--- a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.h ++++ b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.h +@@ -32,6 +32,7 @@ + #define CZ_AT_DFLT 30 + #define CZ_MAX_HARDWARE_POWERLEVELS 8 + #define PPCZ_VOTINGRIGHTSCLIENTS_DFLT0 0x3FFFC102 ++#define CZ_MIN_DEEP_SLEEP_SCLK 800 + + /* Carrizo device IDs */ + #define DEVICE_ID_CZ_9870 0x9870 +@@ -198,6 +199,9 @@ struct cz_hwmgr { + struct cz_sys_info sys_info; + + struct cz_power_level boot_power_level; ++ struct cz_power_state *cz_current_ps; ++ struct cz_power_state *cz_requested_ps; ++ + uint32_t mgcg_cgtt_local0; + uint32_t mgcg_cgtt_local1; + +@@ -299,11 +303,15 @@ struct cz_hwmgr { + + uint32_t max_sclk_level; + uint32_t num_of_clk_entries; +- struct cz_power_state *cz_ps; + }; + + struct pp_hwmgr; + + int cz_hwmgr_init(struct pp_hwmgr *hwmgr); +- ++int cz_dpm_powerdown_uvd(struct pp_hwmgr *hwmgr); ++int cz_dpm_powerup_uvd(struct pp_hwmgr *hwmgr); ++int cz_dpm_powerdown_vce(struct pp_hwmgr *hwmgr); ++int cz_dpm_powerup_vce(struct pp_hwmgr *hwmgr); ++int cz_dpm_update_uvd_dpm(struct pp_hwmgr *hwmgr, bool bgate); ++int cz_dpm_update_vce_dpm(struct pp_hwmgr *hwmgr); + #endif /* _CZ_HWMGR_H_ */ +diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c b/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c +index 7317e43..aec9f6d 100644 +--- a/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c ++++ b/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c +@@ -23,6 +23,7 @@ + #include <linux/errno.h> + #include "hwmgr.h" + #include "hardwaremanager.h" ++#include "power_state.h" + #include "pp_acpi.h" + #include "amd_acpi.h" + +@@ -55,6 +56,17 @@ void phm_init_dynamic_caps(struct pp_hwmgr *hwmgr) + phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_PCIEPerformanceRequest); + } + ++bool phm_is_hw_access_blocked(struct pp_hwmgr *hwmgr) ++{ ++ return hwmgr->block_hw_access; ++} ++ ++int phm_block_hw_access(struct pp_hwmgr *hwmgr, bool block) ++{ ++ hwmgr->block_hw_access = block; ++ return 0; ++} ++ + int phm_setup_asic(struct pp_hwmgr *hwmgr) + { + if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, +@@ -62,13 +74,33 @@ int phm_setup_asic(struct pp_hwmgr *hwmgr) + if (NULL != hwmgr->hwmgr_func->asic_setup) + return hwmgr->hwmgr_func->asic_setup(hwmgr); + } else { +- return phm_dispatch_table (hwmgr, &(hwmgr->setup_asic), ++ return phm_dispatch_table(hwmgr, &(hwmgr->setup_asic), + NULL, NULL); + } + + return 0; + } + ++int phm_set_power_state(struct pp_hwmgr *hwmgr, ++ const struct pp_hw_power_state *pcurrent_state, ++ const struct pp_hw_power_state *pnew_power_state) ++{ ++ struct phm_set_power_state_input states; ++ ++ states.pcurrent_state = pcurrent_state; ++ states.pnew_state = pnew_power_state; ++ ++ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, ++ PHM_PlatformCaps_TablelessHardwareInterface)) { ++ if (NULL != hwmgr->hwmgr_func->power_state_set) ++ return hwmgr->hwmgr_func->power_state_set(hwmgr, &states); ++ } else { ++ return phm_dispatch_table(hwmgr, &(hwmgr->set_power_state), &states, NULL); ++ } ++ ++ return 0; ++} ++ + int phm_enable_dynamic_state_management(struct pp_hwmgr *hwmgr) + { + if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, +@@ -76,9 +108,62 @@ int phm_enable_dynamic_state_management(struct pp_hwmgr *hwmgr) + if (NULL != hwmgr->hwmgr_func->dynamic_state_management_enable) + return hwmgr->hwmgr_func->dynamic_state_management_enable(hwmgr); + } else { +- return phm_dispatch_table (hwmgr, ++ return phm_dispatch_table(hwmgr, + &(hwmgr->enable_dynamic_state_management), + NULL, NULL); + } + return 0; + } ++ ++int phm_force_dpm_levels(struct pp_hwmgr *hwmgr, enum amd_dpm_forced_level level) ++{ ++ if (hwmgr->hwmgr_func->force_dpm_level != NULL) ++ return hwmgr->hwmgr_func->force_dpm_level(hwmgr, level); ++ ++ return 0; ++} ++ ++int phm_apply_state_adjust_rules(struct pp_hwmgr *hwmgr, ++ struct pp_power_state *adjusted_ps, ++ const struct pp_power_state *current_ps) ++{ ++ if (hwmgr->hwmgr_func->apply_state_adjust_rules != NULL) ++ return hwmgr->hwmgr_func->apply_state_adjust_rules( ++ hwmgr, ++ adjusted_ps, ++ current_ps); ++ return 0; ++} ++ ++int phm_powerdown_uvd(struct pp_hwmgr *hwmgr) ++{ ++ if (hwmgr->hwmgr_func->powerdown_uvd != NULL) ++ return hwmgr->hwmgr_func->powerdown_uvd(hwmgr); ++ return 0; ++} ++ ++int phm_powergate_uvd(struct pp_hwmgr *hwmgr, bool gate) ++{ ++ if (hwmgr->hwmgr_func->powergate_uvd != NULL) ++ return hwmgr->hwmgr_func->powergate_uvd(hwmgr, gate); ++ return 0; ++} ++ ++int phm_powergate_vce(struct pp_hwmgr *hwmgr, bool gate) ++{ ++ if (hwmgr->hwmgr_func->powergate_vce != NULL) ++ return hwmgr->hwmgr_func->powergate_vce(hwmgr, gate); ++ return 0; ++} ++ ++int phm_enable_clock_power_gatings(struct pp_hwmgr *hwmgr) ++{ ++ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, ++ PHM_PlatformCaps_TablelessHardwareInterface)) { ++ if (NULL != hwmgr->hwmgr_func->enable_clock_power_gating) ++ return hwmgr->hwmgr_func->enable_clock_power_gating(hwmgr); ++ } else { ++ return phm_dispatch_table(hwmgr, &(hwmgr->enable_clock_power_gatings), NULL, NULL); ++ } ++ return 0; ++} +diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c +index e26df90..5d1ba90 100644 +--- a/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c ++++ b/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c +@@ -201,3 +201,13 @@ void phm_wait_for_indirect_register_unequal(struct pp_hwmgr *hwmgr, + phm_wait_for_register_unequal(hwmgr, indirect_port + 1, + value, mask); + } ++ ++bool phm_cf_want_uvd_power_gating(struct pp_hwmgr *hwmgr) ++{ ++ return phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_UVDPowerGating); ++} ++ ++bool phm_cf_want_vce_power_gating(struct pp_hwmgr *hwmgr) ++{ ++ return phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_VCEPowerGating); ++} +diff --git a/drivers/gpu/drm/amd/powerplay/inc/hardwaremanager.h b/drivers/gpu/drm/amd/powerplay/inc/hardwaremanager.h +index 26e1256..a69b379 100644 +--- a/drivers/gpu/drm/amd/powerplay/inc/hardwaremanager.h ++++ b/drivers/gpu/drm/amd/powerplay/inc/hardwaremanager.h +@@ -23,7 +23,12 @@ + #ifndef _HARDWARE_MANAGER_H_ + #define _HARDWARE_MANAGER_H_ + ++ ++ + struct pp_hwmgr; ++struct pp_hw_power_state; ++struct pp_power_state; ++enum amd_dpm_forced_level; + + /* Automatic Power State Throttling */ + enum PHM_AutoThrottleSource +@@ -206,6 +211,24 @@ struct pp_hw_descriptor { + uint32_t hw_caps[PHM_MAX_NUM_CAPS_ULONG_ENTRIES]; + }; + ++enum PHM_PerformanceLevelDesignation { ++ PHM_PerformanceLevelDesignation_Activity, ++ PHM_PerformanceLevelDesignation_PowerContainment ++}; ++ ++typedef enum PHM_PerformanceLevelDesignation PHM_PerformanceLevelDesignation; ++ ++struct PHM_PerformanceLevel { ++ uint32_t coreClock; ++ uint32_t memory_clock; ++ uint32_t vddc; ++ uint32_t vddci; ++ uint32_t nonLocalMemoryFreq; ++ uint32_t nonLocalMemoryWidth; ++}; ++ ++typedef struct PHM_PerformanceLevel PHM_PerformanceLevel; ++ + /* Function for setting a platform cap */ + static inline void phm_cap_set(uint32_t *caps, + enum phm_platform_caps c) +@@ -226,6 +249,20 @@ static inline bool phm_cap_enabled(const uint32_t *caps, enum phm_platform_caps + (1UL << (c & (PHM_MAX_NUM_CAPS_BITS_PER_FIELD - 1))))); + } + ++#define PP_PCIEGenInvalid 0xffff ++enum PP_PCIEGen { ++ PP_PCIEGen1 = 0, /* PCIE 1.0 - Transfer rate of 2.5 GT/s */ ++ PP_PCIEGen2, /*PCIE 2.0 - Transfer rate of 5.0 GT/s */ ++ PP_PCIEGen3 /*PCIE 3.0 - Transfer rate of 8.0 GT/s */ ++}; ++ ++typedef enum PP_PCIEGen PP_PCIEGen; ++ ++#define PP_Min_PCIEGen PP_PCIEGen1 ++#define PP_Max_PCIEGen PP_PCIEGen3 ++#define PP_Min_PCIELane 1 ++#define PP_Max_PCIELane 32 ++ + enum phm_clock_Type { + PHM_DispClock = 1, + PHM_SClock, +@@ -273,8 +310,22 @@ struct phm_clocks { + uint32_t num_of_entries; + uint32_t clock[MAX_NUM_CLOCKS]; + }; +- ++extern int phm_enable_clock_power_gatings(struct pp_hwmgr *hwmgr); ++extern int phm_powergate_uvd(struct pp_hwmgr *hwmgr, bool gate); ++extern int phm_powergate_vce(struct pp_hwmgr *hwmgr, bool gate); ++extern int phm_powerdown_uvd(struct pp_hwmgr *hwmgr); + extern int phm_setup_asic(struct pp_hwmgr *hwmgr); + extern int phm_enable_dynamic_state_management(struct pp_hwmgr *hwmgr); + extern void phm_init_dynamic_caps(struct pp_hwmgr *hwmgr); ++extern bool phm_is_hw_access_blocked(struct pp_hwmgr *hwmgr); ++extern int phm_block_hw_access(struct pp_hwmgr *hwmgr, bool block); ++extern int phm_set_power_state(struct pp_hwmgr *hwmgr, ++ const struct pp_hw_power_state *pcurrent_state, ++ const struct pp_hw_power_state *pnew_power_state); ++ ++extern int phm_apply_state_adjust_rules(struct pp_hwmgr *hwmgr, ++ struct pp_power_state *adjusted_ps, ++ const struct pp_power_state *current_ps); ++ ++extern int phm_force_dpm_levels(struct pp_hwmgr *hwmgr, enum amd_dpm_forced_level level); + #endif /* _HARDWARE_MANAGER_H_ */ +diff --git a/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h +index 07fba41..18b5ab1 100644 +--- a/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h ++++ b/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h +@@ -23,6 +23,7 @@ + #ifndef _HWMGR_H_ + #define _HWMGR_H_ + ++#include <linux/seq_file.h> + #include "amd_powerplay.h" + #include "pp_instance.h" + #include "hardwaremanager.h" +@@ -85,6 +86,11 @@ typedef int (*phm_table_function)(struct pp_hwmgr *hwmgr, void *input, + + typedef bool (*phm_check_function)(struct pp_hwmgr *hwmgr); + ++struct phm_set_power_state_input { ++ const struct pp_hw_power_state *pcurrent_state; ++ const struct pp_hw_power_state *pnew_state; ++}; ++ + struct phm_acp_arbiter { + uint32_t acpclk; + }; +@@ -252,11 +258,34 @@ struct pp_hwmgr_func { + int (*backend_fini)(struct pp_hwmgr *hw_mgr); + int (*asic_setup)(struct pp_hwmgr *hw_mgr); + int (*get_power_state_size)(struct pp_hwmgr *hw_mgr); +- int (*force_dpm_level)(struct pp_hwmgr *hw_mgr, enum amd_dpm_forced_level level); +- int (*dynamic_state_management_enable)(struct pp_hwmgr *hw_mgr); +- int (*patch_boot_state)(struct pp_hwmgr *hwmgr, struct pp_hw_power_state *hw_ps); +- int (*get_pp_table_entry)(struct pp_hwmgr *hwmgr, unsigned long, struct pp_power_state *); ++ ++ int (*apply_state_adjust_rules)(struct pp_hwmgr *hwmgr, ++ struct pp_power_state *prequest_ps, ++ const struct pp_power_state *pcurrent_ps); ++ ++ int (*force_dpm_level)(struct pp_hwmgr *hw_mgr, ++ enum amd_dpm_forced_level level); ++ ++ int (*dynamic_state_management_enable)( ++ struct pp_hwmgr *hw_mgr); ++ ++ int (*patch_boot_state)(struct pp_hwmgr *hwmgr, ++ struct pp_hw_power_state *hw_ps); ++ ++ int (*get_pp_table_entry)(struct pp_hwmgr *hwmgr, ++ unsigned long, struct pp_power_state *); ++ + int (*get_num_of_pp_table_entries)(struct pp_hwmgr *hwmgr); ++ int (*powerdown_uvd)(struct pp_hwmgr *hwmgr); ++ int (*powergate_vce)(struct pp_hwmgr *hwmgr, bool bgate); ++ int (*powergate_uvd)(struct pp_hwmgr *hwmgr, bool bgate); ++ int (*get_mclk)(struct pp_hwmgr *hwmgr, bool low); ++ int (*get_sclk)(struct pp_hwmgr *hwmgr, bool low); ++ int (*power_state_set)(struct pp_hwmgr *hwmgr, ++ const void *state); ++ void (*print_current_perforce_level)(struct pp_hwmgr *hwmgr, ++ struct seq_file *m); ++ int (*enable_clock_power_gating)(struct pp_hwmgr *hwmgr); + }; + + struct pp_table_func { +@@ -416,7 +445,7 @@ struct pp_hwmgr { + struct pp_smumgr *smumgr; + const void *soft_pp_table; + enum amd_dpm_forced_level dpm_level; +- ++ bool block_hw_access; + struct phm_gfx_arbiter gfx_arbiter; + struct phm_acp_arbiter acp_arbiter; + struct phm_uvd_arbiter uvd_arbiter; +@@ -430,6 +459,8 @@ struct pp_hwmgr { + struct phm_runtime_table_header setup_asic; + struct phm_runtime_table_header disable_dynamic_state_management; + struct phm_runtime_table_header enable_dynamic_state_management; ++ struct phm_runtime_table_header set_power_state; ++ struct phm_runtime_table_header enable_clock_power_gatings; + const struct pp_hwmgr_func *hwmgr_func; + const struct pp_table_func *pptable_func; + struct pp_power_state *ps; +@@ -471,6 +502,11 @@ extern void phm_wait_for_indirect_register_unequal( + uint32_t value, + uint32_t mask); + ++bool phm_cf_want_uvd_power_gating(struct pp_hwmgr *hwmgr); ++bool phm_cf_want_vce_power_gating(struct pp_hwmgr *hwmgr); ++ ++#define PHM_ENTIRE_REGISTER_MASK 0xFFFFFFFFU ++ + #define PHM_FIELD_SHIFT(reg, field) reg##__##field##__SHIFT + #define PHM_FIELD_MASK(reg, field) reg##__##field##_MASK + +diff --git a/drivers/gpu/drm/amd/powerplay/inc/pp_asicblocks.h b/drivers/gpu/drm/amd/powerplay/inc/pp_asicblocks.h +new file mode 100644 +index 0000000..0c1593e +--- /dev/null ++++ b/drivers/gpu/drm/amd/powerplay/inc/pp_asicblocks.h +@@ -0,0 +1,47 @@ ++/* ++ * Copyright 2015 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ */ ++#ifndef PP_ASICBLOCKS_H ++#define PP_ASICBLOCKS_H ++ ++ ++enum PHM_AsicBlock { ++ PHM_AsicBlock_GFX, ++ PHM_AsicBlock_UVD_MVC, ++ PHM_AsicBlock_UVD, ++ PHM_AsicBlock_UVD_HD, ++ PHM_AsicBlock_UVD_SD, ++ PHM_AsicBlock_Count ++}; ++ ++enum PHM_ClockGateSetting { ++ PHM_ClockGateSetting_StaticOn, ++ PHM_ClockGateSetting_StaticOff, ++ PHM_ClockGateSetting_Dynamic ++}; ++ ++struct phm_asic_blocks { ++ bool gfx : 1; ++ bool uvd : 1; ++}; ++ ++#endif +-- +1.9.1 + |