aboutsummaryrefslogtreecommitdiffstats
path: root/common/recipes-kernel/linux/files/0695-drm-amd-powerplay-add-CG-and-PG-support-for-carrizo.patch
diff options
context:
space:
mode:
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.patch1366
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
+