summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2013-07-09 10:49:39 +1000
committerDave Airlie <airlied@redhat.com>2013-07-09 10:49:39 +1000
commit774d8e34e46506222bb5e2888e3ef42b2775715f (patch)
tree22b62340c42a82262d91078b19a2f5f3bfd12099
parentfd0932d7f80146f115671a165461c6f877620abb (diff)
parentf4dec31861e938267d41a962f145edc7d81c8e92 (diff)
Merge branch 'drm-next-3.11' of git://people.freedesktop.org/~agd5f/linux into drm-next
A few more DPM fixes based on user testing. * 'drm-next-3.11' of git://people.freedesktop.org/~agd5f/linux: drm/radeon/dpm: implement vblank_too_short callback for si drm/radeon/dpm: implement vblank_too_short callback for cayman drm/radeon/dpm: implement vblank_too_short callback for btc drm/radeon/dpm: implement vblank_too_short callback for evergreen drm/radeon/dpm: implement vblank_too_short callback for 7xx drm/radeon/dpm: add checks against vblank time drm/radeon/dpm: add helper to calculate vblank time drm/radeon: remove stray line in old pm code drm/radeon/dpm: fix display_gap programming on rv7xx drm/radeon/dpm: implement force performance level for TN drm/radeon/dpm: implement force performance level for ON/LN drm/radeon/dpm: implement force performance level for SI drm/radeon/dpm: implement force performance level for cayman drm/radeon/dpm: implement force performance levels for 7xx/eg/btc drm/radeon/dpm: add infrastructure to force performance levels drm/radeon: fix surface setup on r1xx drm/radeon: add support for 3d perf states on older asics drm/radeon: set default clocks for SI when DPM is disabled
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c3
-rw-r--r--drivers/gpu/drm/radeon/btc_dpm.c20
-rw-r--r--drivers/gpu/drm/radeon/cypress_dpm.c17
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c8
-rw-r--r--drivers/gpu/drm/radeon/ni_dpm.c54
-rw-r--r--drivers/gpu/drm/radeon/ni_dpm.h2
-rw-r--r--drivers/gpu/drm/radeon/ppsmc.h3
-rw-r--r--drivers/gpu/drm/radeon/r100.c11
-rw-r--r--drivers/gpu/drm/radeon/r600_dpm.c24
-rw-r--r--drivers/gpu/drm/radeon/r600_dpm.h1
-rw-r--r--drivers/gpu/drm/radeon/radeon.h13
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.c12
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.h14
-rw-r--r--drivers/gpu/drm/radeon/radeon_atombios.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h1
-rw-r--r--drivers/gpu/drm/radeon/radeon_pm.c92
-rw-r--r--drivers/gpu/drm/radeon/rv770_dpm.c44
-rw-r--r--drivers/gpu/drm/radeon/rv770_dpm.h3
-rw-r--r--drivers/gpu/drm/radeon/si_dpm.c45
-rw-r--r--drivers/gpu/drm/radeon/sumo_dpm.c44
-rw-r--r--drivers/gpu/drm/radeon/trinity_dpm.c32
-rw-r--r--drivers/gpu/drm/radeon/trinity_dpm.h1
-rw-r--r--drivers/gpu/drm/radeon/trinity_smc.c7
23 files changed, 401 insertions, 54 deletions
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index c7ad4b930850..b9d3b43f19c0 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -1841,6 +1841,9 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc,
atombios_crtc_set_base(crtc, x, y, old_fb);
atombios_overscan_setup(crtc, mode, adjusted_mode);
atombios_scaler_setup(crtc);
+ /* update the hw version fpr dpm */
+ radeon_crtc->hw_mode = *adjusted_mode;
+
return 0;
}
diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c
index f072660c7665..0bfd55e08820 100644
--- a/drivers/gpu/drm/radeon/btc_dpm.c
+++ b/drivers/gpu/drm/radeon/btc_dpm.c
@@ -2059,6 +2059,19 @@ static void btc_init_stutter_mode(struct radeon_device *rdev)
}
}
+bool btc_dpm_vblank_too_short(struct radeon_device *rdev)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+ u32 vblank_time = r600_dpm_get_vblank_time(rdev);
+ u32 switch_limit = pi->mem_gddr5 ? 450 : 100;
+
+ if (vblank_time < switch_limit)
+ return true;
+ else
+ return false;
+
+}
+
static void btc_apply_state_adjust_rules(struct radeon_device *rdev,
struct radeon_ps *rps)
{
@@ -2068,7 +2081,8 @@ static void btc_apply_state_adjust_rules(struct radeon_device *rdev,
u32 mclk, sclk;
u16 vddc, vddci;
- if (rdev->pm.dpm.new_active_crtc_count > 1)
+ if ((rdev->pm.dpm.new_active_crtc_count > 1) ||
+ btc_dpm_vblank_too_short(rdev))
disable_mclk_switching = true;
else
disable_mclk_switching = false;
@@ -2326,9 +2340,9 @@ int btc_dpm_set_power_state(struct radeon_device *rdev)
return ret;
}
- ret = rv770_unrestrict_performance_levels_after_switch(rdev);
+ ret = rv770_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO);
if (ret) {
- DRM_ERROR("rv770_unrestrict_performance_levels_after_switch failed\n");
+ DRM_ERROR("rv770_dpm_force_performance_level failed\n");
return ret;
}
diff --git a/drivers/gpu/drm/radeon/cypress_dpm.c b/drivers/gpu/drm/radeon/cypress_dpm.c
index 5ada922e5cec..9bcdd174780f 100644
--- a/drivers/gpu/drm/radeon/cypress_dpm.c
+++ b/drivers/gpu/drm/radeon/cypress_dpm.c
@@ -2014,9 +2014,9 @@ int cypress_dpm_set_power_state(struct radeon_device *rdev)
if (eg_pi->pcie_performance_request)
cypress_notify_link_speed_change_after_state_change(rdev, new_ps, old_ps);
- ret = rv770_unrestrict_performance_levels_after_switch(rdev);
+ ret = rv770_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO);
if (ret) {
- DRM_ERROR("rv770_unrestrict_performance_levels_after_switch failed\n");
+ DRM_ERROR("rv770_dpm_force_performance_level failed\n");
return ret;
}
@@ -2174,3 +2174,16 @@ void cypress_dpm_fini(struct radeon_device *rdev)
kfree(rdev->pm.dpm.ps);
kfree(rdev->pm.dpm.priv);
}
+
+bool cypress_dpm_vblank_too_short(struct radeon_device *rdev)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+ u32 vblank_time = r600_dpm_get_vblank_time(rdev);
+ u32 switch_limit = pi->mem_gddr5 ? 450 : 300;
+
+ if (vblank_time < switch_limit)
+ return true;
+ else
+ return false;
+
+}
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 2e1de4fd2975..e49059dc9b8f 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -1504,8 +1504,8 @@ void evergreen_pm_misc(struct radeon_device *rdev)
struct radeon_voltage *voltage = &ps->clock_info[req_cm_idx].voltage;
if (voltage->type == VOLTAGE_SW) {
- /* 0xff01 is a flag rather then an actual voltage */
- if (voltage->voltage == 0xff01)
+ /* 0xff0x are flags rather then an actual voltage */
+ if ((voltage->voltage & 0xff00) == 0xff00)
return;
if (voltage->voltage && (voltage->voltage != rdev->pm.current_vddc)) {
radeon_atom_set_voltage(rdev, voltage->voltage, SET_VOLTAGE_TYPE_ASIC_VDDC);
@@ -1525,8 +1525,8 @@ void evergreen_pm_misc(struct radeon_device *rdev)
voltage = &rdev->pm.power_state[req_ps_idx].
clock_info[rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx].voltage;
- /* 0xff01 is a flag rather then an actual voltage */
- if (voltage->vddci == 0xff01)
+ /* 0xff0x are flags rather then an actual voltage */
+ if ((voltage->vddci & 0xff00) == 0xff00)
return;
if (voltage->vddci && (voltage->vddci != rdev->pm.current_vddci)) {
radeon_atom_set_voltage(rdev, voltage->vddci, SET_VOLTAGE_TYPE_ASIC_VDDCI);
diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c
index a4cb99c2da85..559cf24d51af 100644
--- a/drivers/gpu/drm/radeon/ni_dpm.c
+++ b/drivers/gpu/drm/radeon/ni_dpm.c
@@ -765,6 +765,19 @@ static void ni_calculate_leakage_for_v_and_t(struct radeon_device *rdev,
ni_calculate_leakage_for_v_and_t_formula(coeff, v, t, i_leakage, leakage);
}
+bool ni_dpm_vblank_too_short(struct radeon_device *rdev)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+ u32 vblank_time = r600_dpm_get_vblank_time(rdev);
+ u32 switch_limit = pi->mem_gddr5 ? 450 : 300;
+
+ if (vblank_time < switch_limit)
+ return true;
+ else
+ return false;
+
+}
+
static void ni_apply_state_adjust_rules(struct radeon_device *rdev,
struct radeon_ps *rps)
{
@@ -775,7 +788,8 @@ static void ni_apply_state_adjust_rules(struct radeon_device *rdev,
u16 vddc, vddci;
int i;
- if (rdev->pm.dpm.new_active_crtc_count > 1)
+ if ((rdev->pm.dpm.new_active_crtc_count > 1) ||
+ ni_dpm_vblank_too_short(rdev))
disable_mclk_switching = true;
else
disable_mclk_switching = false;
@@ -1037,13 +1051,37 @@ static int ni_restrict_performance_levels_before_switch(struct radeon_device *rd
0 : -EINVAL;
}
-static int ni_unrestrict_performance_levels_after_switch(struct radeon_device *rdev)
+int ni_dpm_force_performance_level(struct radeon_device *rdev,
+ enum radeon_dpm_forced_level level)
{
- if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK)
- return -EINVAL;
+ struct radeon_ps *rps = rdev->pm.dpm.current_ps;
+ struct ni_ps *ps = ni_get_ps(rps);
+ u32 levels;
- return (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) == PPSMC_Result_OK) ?
- 0 : -EINVAL;
+ if (level == RADEON_DPM_FORCED_LEVEL_HIGH) {
+ if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) != PPSMC_Result_OK)
+ return -EINVAL;
+
+ if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 1) != PPSMC_Result_OK)
+ return -EINVAL;
+ } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) {
+ if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK)
+ return -EINVAL;
+
+ levels = ps->performance_level_count - 1;
+ if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, levels) != PPSMC_Result_OK)
+ return -EINVAL;
+ } else if (level == RADEON_DPM_FORCED_LEVEL_AUTO) {
+ if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK)
+ return -EINVAL;
+
+ if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) != PPSMC_Result_OK)
+ return -EINVAL;
+ }
+
+ rdev->pm.dpm.forced_level = level;
+
+ return 0;
}
static void ni_stop_smc(struct radeon_device *rdev)
@@ -3831,9 +3869,9 @@ int ni_dpm_set_power_state(struct radeon_device *rdev)
return ret;
}
- ret = ni_unrestrict_performance_levels_after_switch(rdev);
+ ret = ni_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO);
if (ret) {
- DRM_ERROR("ni_unrestrict_performance_levels_after_switch failed\n");
+ DRM_ERROR("ni_dpm_force_performance_level failed\n");
return ret;
}
diff --git a/drivers/gpu/drm/radeon/ni_dpm.h b/drivers/gpu/drm/radeon/ni_dpm.h
index ac1c7abf2c67..6bbee9180909 100644
--- a/drivers/gpu/drm/radeon/ni_dpm.h
+++ b/drivers/gpu/drm/radeon/ni_dpm.h
@@ -245,4 +245,6 @@ void ni_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev,
struct radeon_ps *new_ps,
struct radeon_ps *old_ps);
+bool ni_dpm_vblank_too_short(struct radeon_device *rdev);
+
#endif
diff --git a/drivers/gpu/drm/radeon/ppsmc.h b/drivers/gpu/drm/radeon/ppsmc.h
index 8fb1113a8fd7..b5564a3645d2 100644
--- a/drivers/gpu/drm/radeon/ppsmc.h
+++ b/drivers/gpu/drm/radeon/ppsmc.h
@@ -71,6 +71,8 @@ typedef uint8_t PPSMC_Result;
#define PPSMC_MSG_SwitchToSwState ((uint8_t)0x20)
#define PPSMC_MSG_SwitchToInitialState ((uint8_t)0x40)
#define PPSMC_MSG_NoForcedLevel ((uint8_t)0x41)
+#define PPSMC_MSG_ForceHigh ((uint8_t)0x42)
+#define PPSMC_MSG_ForceMediumOrHigh ((uint8_t)0x43)
#define PPSMC_MSG_SwitchToMinimumPower ((uint8_t)0x51)
#define PPSMC_MSG_ResumeFromMinimumPower ((uint8_t)0x52)
#define PPSMC_MSG_EnableCac ((uint8_t)0x53)
@@ -101,6 +103,7 @@ typedef uint8_t PPSMC_Result;
#define PPSMC_MSG_DPM_Config ((uint32_t) 0x102)
#define PPSMC_MSG_DPM_ForceState ((uint32_t) 0x104)
#define PPSMC_MSG_PG_SIMD_Config ((uint32_t) 0x108)
+#define PPSMC_MSG_DPM_N_LevelsDisabled ((uint32_t) 0x112)
#define PPSMC_MSG_DCE_RemoveVoltageAdjustment ((uint32_t) 0x11d)
#define PPSMC_MSG_DCE_AllowVoltageAdjustment ((uint32_t) 0x11e)
#define PPSMC_MSG_UVD_DPM_Config ((uint32_t) 0x124)
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index d0314ecbd7c1..c9affefd79f6 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -3077,6 +3077,10 @@ int r100_set_surface_reg(struct radeon_device *rdev, int reg,
flags |= RADEON_SURF_TILE_COLOR_BOTH;
if (tiling_flags & RADEON_TILING_MACRO)
flags |= RADEON_SURF_TILE_COLOR_MACRO;
+ /* setting pitch to 0 disables tiling */
+ if ((tiling_flags & (RADEON_TILING_MACRO|RADEON_TILING_MICRO))
+ == 0)
+ pitch = 0;
} else if (rdev->family <= CHIP_RV280) {
if (tiling_flags & (RADEON_TILING_MACRO))
flags |= R200_SURF_TILE_COLOR_MACRO;
@@ -3094,13 +3098,6 @@ int r100_set_surface_reg(struct radeon_device *rdev, int reg,
if (tiling_flags & RADEON_TILING_SWAP_32BIT)
flags |= RADEON_SURF_AP0_SWP_32BPP | RADEON_SURF_AP1_SWP_32BPP;
- /* when we aren't tiling the pitch seems to needs to be furtherdivided down. - tested on power5 + rn50 server */
- if (tiling_flags & (RADEON_TILING_SWAP_16BIT | RADEON_TILING_SWAP_32BIT)) {
- if (!(tiling_flags & (RADEON_TILING_MACRO | RADEON_TILING_MICRO)))
- if (ASIC_IS_RN50(rdev))
- pitch /= 16;
- }
-
/* r100/r200 divide by 16 */
if (rdev->family < CHIP_R300)
flags |= pitch / 16;
diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c
index 76368c04f809..b88f54b134ab 100644
--- a/drivers/gpu/drm/radeon/r600_dpm.c
+++ b/drivers/gpu/drm/radeon/r600_dpm.c
@@ -150,6 +150,30 @@ void r600_dpm_print_ps_status(struct radeon_device *rdev,
printk("\n");
}
+u32 r600_dpm_get_vblank_time(struct radeon_device *rdev)
+{
+ struct drm_device *dev = rdev->ddev;
+ struct drm_crtc *crtc;
+ struct radeon_crtc *radeon_crtc;
+ u32 line_time_us, vblank_lines;
+ u32 vblank_time_us = 0xffffffff; /* if the displays are off, vblank time is max */
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ radeon_crtc = to_radeon_crtc(crtc);
+ if (crtc->enabled && radeon_crtc->enabled && radeon_crtc->hw_mode.clock) {
+ line_time_us = (radeon_crtc->hw_mode.crtc_htotal * 1000) /
+ radeon_crtc->hw_mode.clock;
+ vblank_lines = radeon_crtc->hw_mode.crtc_vblank_end -
+ radeon_crtc->hw_mode.crtc_vdisplay +
+ (radeon_crtc->v_border * 2);
+ vblank_time_us = vblank_lines * line_time_us;
+ break;
+ }
+ }
+
+ return vblank_time_us;
+}
+
void r600_calculate_u_and_p(u32 i, u32 r_c, u32 p_b,
u32 *p, u32 *u)
{
diff --git a/drivers/gpu/drm/radeon/r600_dpm.h b/drivers/gpu/drm/radeon/r600_dpm.h
index a95ab214289b..7c822d9ae53d 100644
--- a/drivers/gpu/drm/radeon/r600_dpm.h
+++ b/drivers/gpu/drm/radeon/r600_dpm.h
@@ -129,6 +129,7 @@ void r600_dpm_print_class_info(u32 class, u32 class2);
void r600_dpm_print_cap_info(u32 caps);
void r600_dpm_print_ps_status(struct radeon_device *rdev,
struct radeon_ps *rps);
+u32 r600_dpm_get_vblank_time(struct radeon_device *rdev);
bool r600_is_uvd_state(u32 class, u32 class2);
void r600_calculate_u_and_p(u32 i, u32 r_c, u32 p_b,
u32 *p, u32 *u);
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index f51807f04f65..9b7025d02cd0 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -1100,6 +1100,7 @@ enum radeon_pm_state_type {
POWER_STATE_TYPE_INTERNAL_THERMAL,
POWER_STATE_TYPE_INTERNAL_ACPI,
POWER_STATE_TYPE_INTERNAL_ULV,
+ POWER_STATE_TYPE_INTERNAL_3DPERF,
};
enum radeon_pm_profile_type {
@@ -1334,6 +1335,12 @@ enum radeon_pcie_gen {
RADEON_PCIE_GEN_INVALID = 0xffff
};
+enum radeon_dpm_forced_level {
+ RADEON_DPM_FORCED_LEVEL_AUTO = 0,
+ RADEON_DPM_FORCED_LEVEL_LOW = 1,
+ RADEON_DPM_FORCED_LEVEL_HIGH = 2,
+};
+
struct radeon_dpm {
struct radeon_ps *ps;
/* number of valid power states */
@@ -1373,6 +1380,8 @@ struct radeon_dpm {
bool uvd_active;
/* thermal handling */
struct radeon_dpm_thermal thermal;
+ /* forced levels */
+ enum radeon_dpm_forced_level forced_level;
};
void radeon_dpm_enable_power_state(struct radeon_device *rdev,
@@ -1668,6 +1677,8 @@ struct radeon_asic {
u32 (*get_mclk)(struct radeon_device *rdev, bool low);
void (*print_power_state)(struct radeon_device *rdev, struct radeon_ps *ps);
void (*debugfs_print_current_performance_level)(struct radeon_device *rdev, struct seq_file *m);
+ int (*force_performance_level)(struct radeon_device *rdev, enum radeon_dpm_forced_level level);
+ bool (*vblank_too_short)(struct radeon_device *rdev);
} dpm;
/* pageflipping */
struct {
@@ -2435,6 +2446,8 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v);
#define radeon_dpm_get_mclk(rdev, l) rdev->asic->dpm.get_mclk((rdev), (l))
#define radeon_dpm_print_power_state(rdev, ps) rdev->asic->dpm.print_power_state((rdev), (ps))
#define radeon_dpm_debugfs_print_current_performance_level(rdev, m) rdev->asic->dpm.debugfs_print_current_performance_level((rdev), (m))
+#define radeon_dpm_force_performance_level(rdev, l) rdev->asic->dpm.force_performance_level((rdev), (l))
+#define radeon_dpm_vblank_too_short(rdev) rdev->asic->dpm.vblank_too_short((rdev))
/* Common functions */
/* AGP */
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c
index a5b244dc50ca..097077499cc6 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.c
+++ b/drivers/gpu/drm/radeon/radeon_asic.c
@@ -1393,6 +1393,8 @@ static struct radeon_asic rv770_asic = {
.get_mclk = &rv770_dpm_get_mclk,
.print_power_state = &rv770_dpm_print_power_state,
.debugfs_print_current_performance_level = &rv770_dpm_debugfs_print_current_performance_level,
+ .force_performance_level = &rv770_dpm_force_performance_level,
+ .vblank_too_short = &rv770_dpm_vblank_too_short,
},
.pflip = {
.pre_page_flip = &rs600_pre_page_flip,
@@ -1516,6 +1518,8 @@ static struct radeon_asic evergreen_asic = {
.get_mclk = &rv770_dpm_get_mclk,
.print_power_state = &rv770_dpm_print_power_state,
.debugfs_print_current_performance_level = &rv770_dpm_debugfs_print_current_performance_level,
+ .force_performance_level = &rv770_dpm_force_performance_level,
+ .vblank_too_short = &cypress_dpm_vblank_too_short,
},
.pflip = {
.pre_page_flip = &evergreen_pre_page_flip,
@@ -1639,6 +1643,7 @@ static struct radeon_asic sumo_asic = {
.get_mclk = &sumo_dpm_get_mclk,
.print_power_state = &sumo_dpm_print_power_state,
.debugfs_print_current_performance_level = &sumo_dpm_debugfs_print_current_performance_level,
+ .force_performance_level = &sumo_dpm_force_performance_level,
},
.pflip = {
.pre_page_flip = &evergreen_pre_page_flip,
@@ -1762,6 +1767,8 @@ static struct radeon_asic btc_asic = {
.get_mclk = &btc_dpm_get_mclk,
.print_power_state = &rv770_dpm_print_power_state,
.debugfs_print_current_performance_level = &rv770_dpm_debugfs_print_current_performance_level,
+ .force_performance_level = &rv770_dpm_force_performance_level,
+ .vblank_too_short = &btc_dpm_vblank_too_short,
},
.pflip = {
.pre_page_flip = &evergreen_pre_page_flip,
@@ -1937,6 +1944,8 @@ static struct radeon_asic cayman_asic = {
.get_mclk = &ni_dpm_get_mclk,
.print_power_state = &ni_dpm_print_power_state,
.debugfs_print_current_performance_level = &ni_dpm_debugfs_print_current_performance_level,
+ .force_performance_level = &ni_dpm_force_performance_level,
+ .vblank_too_short = &ni_dpm_vblank_too_short,
},
.pflip = {
.pre_page_flip = &evergreen_pre_page_flip,
@@ -2110,6 +2119,7 @@ static struct radeon_asic trinity_asic = {
.get_mclk = &trinity_dpm_get_mclk,
.print_power_state = &trinity_dpm_print_power_state,
.debugfs_print_current_performance_level = &trinity_dpm_debugfs_print_current_performance_level,
+ .force_performance_level = &trinity_dpm_force_performance_level,
},
.pflip = {
.pre_page_flip = &evergreen_pre_page_flip,
@@ -2283,6 +2293,8 @@ static struct radeon_asic si_asic = {
.get_mclk = &ni_dpm_get_mclk,
.print_power_state = &ni_dpm_print_power_state,
.debugfs_print_current_performance_level = &si_dpm_debugfs_print_current_performance_level,
+ .force_performance_level = &si_dpm_force_performance_level,
+ .vblank_too_short = &ni_dpm_vblank_too_short,
},
.pflip = {
.pre_page_flip = &evergreen_pre_page_flip,
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
index 6822c7aeacaa..45d0693cddd5 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.h
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
@@ -478,6 +478,9 @@ void rv770_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *ps);
void rv770_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
struct seq_file *m);
+int rv770_dpm_force_performance_level(struct radeon_device *rdev,
+ enum radeon_dpm_forced_level level);
+bool rv770_dpm_vblank_too_short(struct radeon_device *rdev);
/*
* evergreen
@@ -542,6 +545,7 @@ void cypress_dpm_disable(struct radeon_device *rdev);
int cypress_dpm_set_power_state(struct radeon_device *rdev);
void cypress_dpm_display_configuration_changed(struct radeon_device *rdev);
void cypress_dpm_fini(struct radeon_device *rdev);
+bool cypress_dpm_vblank_too_short(struct radeon_device *rdev);
int btc_dpm_init(struct radeon_device *rdev);
void btc_dpm_setup_asic(struct radeon_device *rdev);
int btc_dpm_enable(struct radeon_device *rdev);
@@ -552,6 +556,7 @@ void btc_dpm_post_set_power_state(struct radeon_device *rdev);
void btc_dpm_fini(struct radeon_device *rdev);
u32 btc_dpm_get_sclk(struct radeon_device *rdev, bool low);
u32 btc_dpm_get_mclk(struct radeon_device *rdev, bool low);
+bool btc_dpm_vblank_too_short(struct radeon_device *rdev);
int sumo_dpm_init(struct radeon_device *rdev);
int sumo_dpm_enable(struct radeon_device *rdev);
void sumo_dpm_disable(struct radeon_device *rdev);
@@ -567,6 +572,8 @@ void sumo_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *ps);
void sumo_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
struct seq_file *m);
+int sumo_dpm_force_performance_level(struct radeon_device *rdev,
+ enum radeon_dpm_forced_level level);
/*
* cayman
@@ -615,6 +622,9 @@ void ni_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *ps);
void ni_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
struct seq_file *m);
+int ni_dpm_force_performance_level(struct radeon_device *rdev,
+ enum radeon_dpm_forced_level level);
+bool ni_dpm_vblank_too_short(struct radeon_device *rdev);
int trinity_dpm_init(struct radeon_device *rdev);
int trinity_dpm_enable(struct radeon_device *rdev);
void trinity_dpm_disable(struct radeon_device *rdev);
@@ -630,6 +640,8 @@ void trinity_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *ps);
void trinity_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
struct seq_file *m);
+int trinity_dpm_force_performance_level(struct radeon_device *rdev,
+ enum radeon_dpm_forced_level level);
/* DCE6 - SI */
void dce6_bandwidth_update(struct radeon_device *rdev);
@@ -679,6 +691,8 @@ void si_dpm_fini(struct radeon_device *rdev);
void si_dpm_display_configuration_changed(struct radeon_device *rdev);
void si_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
struct seq_file *m);
+int si_dpm_force_performance_level(struct radeon_device *rdev,
+ enum radeon_dpm_forced_level level);
/* DCE8 - CIK */
void dce8_bandwidth_update(struct radeon_device *rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index b1777d10d0b5..fbdaff55556b 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -2441,6 +2441,10 @@ static bool radeon_atombios_parse_pplib_clock_info(struct radeon_device *rdev,
case ATOM_VIRTUAL_VOLTAGE_ID1:
case ATOM_VIRTUAL_VOLTAGE_ID2:
case ATOM_VIRTUAL_VOLTAGE_ID3:
+ case ATOM_VIRTUAL_VOLTAGE_ID4:
+ case ATOM_VIRTUAL_VOLTAGE_ID5:
+ case ATOM_VIRTUAL_VOLTAGE_ID6:
+ case ATOM_VIRTUAL_VOLTAGE_ID7:
if (radeon_atom_get_max_vddc(rdev, VOLTAGE_TYPE_VDDC,
rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage,
&vddc) == 0)
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index b568cb19a7fa..8296632a4235 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -335,6 +335,7 @@ struct radeon_crtc {
u32 line_time;
u32 wm_low;
u32 wm_high;
+ struct drm_display_mode hw_mode;
};
struct radeon_encoder_primary_dac {
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index ebbdb477745a..f374c467aaca 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -468,9 +468,57 @@ fail:
return count;
}
+static ssize_t radeon_get_dpm_forced_performance_level(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
+ struct radeon_device *rdev = ddev->dev_private;
+ enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level;
+
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ (level == RADEON_DPM_FORCED_LEVEL_AUTO) ? "auto" :
+ (level == RADEON_DPM_FORCED_LEVEL_LOW) ? "low" : "high");
+}
+
+static ssize_t radeon_set_dpm_forced_performance_level(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
+ struct radeon_device *rdev = ddev->dev_private;
+ enum radeon_dpm_forced_level level;
+ int ret = 0;
+
+ mutex_lock(&rdev->pm.mutex);
+ if (strncmp("low", buf, strlen("low")) == 0) {
+ level = RADEON_DPM_FORCED_LEVEL_LOW;
+ } else if (strncmp("high", buf, strlen("high")) == 0) {
+ level = RADEON_DPM_FORCED_LEVEL_HIGH;
+ } else if (strncmp("auto", buf, strlen("auto")) == 0) {
+ level = RADEON_DPM_FORCED_LEVEL_AUTO;
+ } else {
+ mutex_unlock(&rdev->pm.mutex);
+ count = -EINVAL;
+ goto fail;
+ }
+ if (rdev->asic->dpm.force_performance_level) {
+ ret = radeon_dpm_force_performance_level(rdev, level);
+ if (ret)
+ count = -EINVAL;
+ }
+ mutex_unlock(&rdev->pm.mutex);
+fail:
+ return count;
+}
+
static DEVICE_ATTR(power_profile, S_IRUGO | S_IWUSR, radeon_get_pm_profile, radeon_set_pm_profile);
static DEVICE_ATTR(power_method, S_IRUGO | S_IWUSR, radeon_get_pm_method, radeon_set_pm_method);
static DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, radeon_get_dpm_state, radeon_set_dpm_state);
+static DEVICE_ATTR(power_dpm_force_performance_level, S_IRUGO | S_IWUSR,
+ radeon_get_dpm_forced_performance_level,
+ radeon_set_dpm_forced_performance_level);
static ssize_t radeon_hwmon_show_temp(struct device *dev,
struct device_attribute *attr,
@@ -585,12 +633,25 @@ static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev,
int i;
struct radeon_ps *ps;
u32 ui_class;
+ bool single_display = (rdev->pm.dpm.new_active_crtc_count < 2) ?
+ true : false;
-restart_search:
+ /* check if the vblank period is too short to adjust the mclk */
+ if (single_display && rdev->asic->dpm.vblank_too_short) {
+ if (radeon_dpm_vblank_too_short(rdev))
+ single_display = false;
+ }
+
+ /* certain older asics have a separare 3D performance state,
+ * so try that first if the user selected performance
+ */
+ if (dpm_state == POWER_STATE_TYPE_PERFORMANCE)
+ dpm_state = POWER_STATE_TYPE_INTERNAL_3DPERF;
/* balanced states don't exist at the moment */
if (dpm_state == POWER_STATE_TYPE_BALANCED)
dpm_state = POWER_STATE_TYPE_PERFORMANCE;
+restart_search:
/* Pick the best power state based on current conditions */
for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
ps = &rdev->pm.dpm.ps[i];
@@ -600,7 +661,7 @@ restart_search:
case POWER_STATE_TYPE_BATTERY:
if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) {
if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
- if (rdev->pm.dpm.new_active_crtc_count < 2)
+ if (single_display)
return ps;
} else
return ps;
@@ -609,7 +670,7 @@ restart_search:
case POWER_STATE_TYPE_BALANCED:
if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BALANCED) {
if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
- if (rdev->pm.dpm.new_active_crtc_count < 2)
+ if (single_display)
return ps;
} else
return ps;
@@ -618,7 +679,7 @@ restart_search:
case POWER_STATE_TYPE_PERFORMANCE:
if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) {
if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
- if (rdev->pm.dpm.new_active_crtc_count < 2)
+ if (single_display)
return ps;
} else
return ps;
@@ -657,6 +718,10 @@ restart_search:
if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV)
return ps;
break;
+ case POWER_STATE_TYPE_INTERNAL_3DPERF:
+ if (ps->class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE)
+ return ps;
+ break;
default:
break;
}
@@ -675,6 +740,8 @@ restart_search:
dpm_state = POWER_STATE_TYPE_BATTERY;
goto restart_search;
case POWER_STATE_TYPE_BATTERY:
+ case POWER_STATE_TYPE_BALANCED:
+ case POWER_STATE_TYPE_INTERNAL_3DPERF:
dpm_state = POWER_STATE_TYPE_PERFORMANCE;
goto restart_search;
default:
@@ -852,7 +919,7 @@ static void radeon_pm_resume_old(struct radeon_device *rdev)
{
/* set up the default clocks if the MC ucode is loaded */
if ((rdev->family >= CHIP_BARTS) &&
- (rdev->family <= CHIP_CAYMAN) &&
+ (rdev->family <= CHIP_HAINAN) &&
rdev->mc_fw) {
if (rdev->pm.default_vddc)
radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
@@ -896,7 +963,7 @@ static void radeon_pm_resume_dpm(struct radeon_device *rdev)
if (ret) {
DRM_ERROR("radeon: dpm resume failed\n");
if ((rdev->family >= CHIP_BARTS) &&
- (rdev->family <= CHIP_CAYMAN) &&
+ (rdev->family <= CHIP_HAINAN) &&
rdev->mc_fw) {
if (rdev->pm.default_vddc)
radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
@@ -947,7 +1014,7 @@ static int radeon_pm_init_old(struct radeon_device *rdev)
radeon_pm_init_profile(rdev);
/* set up the default clocks if the MC ucode is loaded */
if ((rdev->family >= CHIP_BARTS) &&
- (rdev->family <= CHIP_CAYMAN) &&
+ (rdev->family <= CHIP_HAINAN) &&
rdev->mc_fw) {
if (rdev->pm.default_vddc)
radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
@@ -1003,8 +1070,8 @@ static int radeon_pm_init_dpm(struct radeon_device *rdev)
int ret;
/* default to performance state */
- rdev->pm.dpm.state = POWER_STATE_TYPE_PERFORMANCE;
- rdev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE;
+ rdev->pm.dpm.state = POWER_STATE_TYPE_BALANCED;
+ rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED;
rdev->pm.default_sclk = rdev->clock.default_sclk;
rdev->pm.default_mclk = rdev->clock.default_mclk;
rdev->pm.current_sclk = rdev->clock.default_sclk;
@@ -1032,7 +1099,7 @@ static int radeon_pm_init_dpm(struct radeon_device *rdev)
if (ret) {
rdev->pm.dpm_enabled = false;
if ((rdev->family >= CHIP_BARTS) &&
- (rdev->family <= CHIP_CAYMAN) &&
+ (rdev->family <= CHIP_HAINAN) &&
rdev->mc_fw) {
if (rdev->pm.default_vddc)
radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
@@ -1055,6 +1122,9 @@ static int radeon_pm_init_dpm(struct radeon_device *rdev)
ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state);
if (ret)
DRM_ERROR("failed to create device file for dpm state\n");
+ ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level);
+ if (ret)
+ DRM_ERROR("failed to create device file for dpm state\n");
/* XXX: these are noops for dpm but are here for backwards compat */
ret = device_create_file(rdev->dev, &dev_attr_power_profile);
if (ret)
@@ -1159,6 +1229,7 @@ static void radeon_pm_fini_dpm(struct radeon_device *rdev)
mutex_unlock(&rdev->pm.mutex);
device_remove_file(rdev->dev, &dev_attr_power_dpm_state);
+ device_remove_file(rdev->dev, &dev_attr_power_dpm_force_performance_level);
/* XXX backwards compat */
device_remove_file(rdev->dev, &dev_attr_power_profile);
device_remove_file(rdev->dev, &dev_attr_power_method);
@@ -1188,7 +1259,6 @@ static void radeon_pm_compute_clocks_old(struct radeon_device *rdev)
if (rdev->pm.num_power_states < 2)
return;
- INIT_WORK(&rdev->pm.dpm.thermal.work, radeon_dpm_thermal_work_handler);
mutex_lock(&rdev->pm.mutex);
rdev->pm.active_crtcs = 0;
diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c
index 9af464d48eaa..d914e04ea39a 100644
--- a/drivers/gpu/drm/radeon/rv770_dpm.c
+++ b/drivers/gpu/drm/radeon/rv770_dpm.c
@@ -1341,10 +1341,10 @@ static void rv770_program_display_gap(struct radeon_device *rdev)
u32 tmp = RREG32(CG_DISPLAY_GAP_CNTL);
tmp &= ~(DISP1_GAP_MCHG_MASK | DISP2_GAP_MCHG_MASK);
- if (RREG32(AVIVO_D1CRTC_CONTROL) & AVIVO_CRTC_EN) {
+ if (rdev->pm.dpm.new_active_crtcs & 1) {
tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK);
tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE);
- } else if (RREG32(AVIVO_D2CRTC_CONTROL) & AVIVO_CRTC_EN) {
+ } else if (rdev->pm.dpm.new_active_crtcs & 2) {
tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE);
tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK);
} else {
@@ -1471,14 +1471,30 @@ int rv770_restrict_performance_levels_before_switch(struct radeon_device *rdev)
return 0;
}
-int rv770_unrestrict_performance_levels_after_switch(struct radeon_device *rdev)
+int rv770_dpm_force_performance_level(struct radeon_device *rdev,
+ enum radeon_dpm_forced_level level)
{
- if (rv770_send_msg_to_smc(rdev, (PPSMC_Msg)(PPSMC_MSG_NoForcedLevel)) != PPSMC_Result_OK)
- return -EINVAL;
+ PPSMC_Msg msg;
+
+ if (level == RADEON_DPM_FORCED_LEVEL_HIGH) {
+ if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_ZeroLevelsDisabled) != PPSMC_Result_OK)
+ return -EINVAL;
+ msg = PPSMC_MSG_ForceHigh;
+ } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) {
+ if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_NoForcedLevel) != PPSMC_Result_OK)
+ return -EINVAL;
+ msg = (PPSMC_Msg)(PPSMC_MSG_TwoLevelsDisabled);
+ } else {
+ if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_NoForcedLevel) != PPSMC_Result_OK)
+ return -EINVAL;
+ msg = (PPSMC_Msg)(PPSMC_MSG_ZeroLevelsDisabled);
+ }
- if (rv770_send_msg_to_smc(rdev, (PPSMC_Msg)(PPSMC_MSG_ZeroLevelsDisabled)) != PPSMC_Result_OK)
+ if (rv770_send_msg_to_smc(rdev, msg) != PPSMC_Result_OK)
return -EINVAL;
+ rdev->pm.dpm.forced_level = level;
+
return 0;
}
@@ -2047,9 +2063,10 @@ int rv770_dpm_set_power_state(struct radeon_device *rdev)
if (pi->dcodt)
rv770_program_dcodt_after_state_switch(rdev, new_ps, old_ps);
rv770_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
- ret = rv770_unrestrict_performance_levels_after_switch(rdev);
+
+ ret = rv770_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO);
if (ret) {
- DRM_ERROR("rv770_unrestrict_performance_levels_after_switch failed\n");
+ DRM_ERROR("rv770_dpm_force_performance_level failed\n");
return ret;
}
@@ -2491,3 +2508,14 @@ u32 rv770_dpm_get_mclk(struct radeon_device *rdev, bool low)
else
return requested_state->high.mclk;
}
+
+bool rv770_dpm_vblank_too_short(struct radeon_device *rdev)
+{
+ u32 vblank_time = r600_dpm_get_vblank_time(rdev);
+
+ if (vblank_time < 300)
+ return true;
+ else
+ return false;
+
+}
diff --git a/drivers/gpu/drm/radeon/rv770_dpm.h b/drivers/gpu/drm/radeon/rv770_dpm.h
index f1e1fcf7f622..96b1b2a62a8a 100644
--- a/drivers/gpu/drm/radeon/rv770_dpm.h
+++ b/drivers/gpu/drm/radeon/rv770_dpm.h
@@ -262,7 +262,8 @@ void rv770_stop_dpm(struct radeon_device *rdev);
void r7xx_stop_smc(struct radeon_device *rdev);
void rv770_reset_smio_status(struct radeon_device *rdev);
int rv770_restrict_performance_levels_before_switch(struct radeon_device *rdev);
-int rv770_unrestrict_performance_levels_after_switch(struct radeon_device *rdev);
+int rv770_dpm_force_performance_level(struct radeon_device *rdev,
+ enum radeon_dpm_forced_level level);
int rv770_halt_smc(struct radeon_device *rdev);
int rv770_resume_smc(struct radeon_device *rdev);
int rv770_set_sw_state(struct radeon_device *rdev);
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
index a7e97cd05e96..73aaa2e4c312 100644
--- a/drivers/gpu/drm/radeon/si_dpm.c
+++ b/drivers/gpu/drm/radeon/si_dpm.c
@@ -2906,7 +2906,8 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev,
u16 vddc, vddci;
int i;
- if (rdev->pm.dpm.new_active_crtc_count > 1)
+ if ((rdev->pm.dpm.new_active_crtc_count > 1) ||
+ ni_dpm_vblank_too_short(rdev))
disable_mclk_switching = true;
else
disable_mclk_switching = false;
@@ -3231,16 +3232,38 @@ static int si_restrict_performance_levels_before_switch(struct radeon_device *rd
0 : -EINVAL;
}
-#if 0
-static int si_unrestrict_performance_levels_after_switch(struct radeon_device *rdev)
+int si_dpm_force_performance_level(struct radeon_device *rdev,
+ enum radeon_dpm_forced_level level)
{
- if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK)
- return -EINVAL;
+ struct radeon_ps *rps = rdev->pm.dpm.current_ps;
+ struct ni_ps *ps = ni_get_ps(rps);
+ u32 levels;
- return (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) == PPSMC_Result_OK) ?
- 0 : -EINVAL;
+ if (level == RADEON_DPM_FORCED_LEVEL_HIGH) {
+ if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) != PPSMC_Result_OK)
+ return -EINVAL;
+
+ if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 1) != PPSMC_Result_OK)
+ return -EINVAL;
+ } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) {
+ if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK)
+ return -EINVAL;
+
+ levels = ps->performance_level_count - 1;
+ if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, levels) != PPSMC_Result_OK)
+ return -EINVAL;
+ } else if (level == RADEON_DPM_FORCED_LEVEL_AUTO) {
+ if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK)
+ return -EINVAL;
+
+ if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) != PPSMC_Result_OK)
+ return -EINVAL;
+ }
+
+ rdev->pm.dpm.forced_level = level;
+
+ return 0;
}
-#endif
static int si_set_boot_state(struct radeon_device *rdev)
{
@@ -5992,11 +6015,13 @@ int si_dpm_set_power_state(struct radeon_device *rdev)
#if 0
/* XXX */
- ret = si_unrestrict_performance_levels_after_switch(rdev);
+ ret = si_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO);
if (ret) {
- DRM_ERROR("si_unrestrict_performance_levels_after_switch failed\n");
+ DRM_ERROR("si_dpm_force_performance_level failed\n");
return ret;
}
+#else
+ rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO;
#endif
return 0;
diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c
index dc599060a9a4..11b6b9924f1b 100644
--- a/drivers/gpu/drm/radeon/sumo_dpm.c
+++ b/drivers/gpu/drm/radeon/sumo_dpm.c
@@ -1319,6 +1319,8 @@ int sumo_dpm_set_power_state(struct radeon_device *rdev)
if (pi->enable_dpm)
sumo_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
+ rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO;
+
return 0;
}
@@ -1830,3 +1832,45 @@ u32 sumo_dpm_get_mclk(struct radeon_device *rdev, bool low)
return pi->sys_info.bootup_uma_clk;
}
+
+int sumo_dpm_force_performance_level(struct radeon_device *rdev,
+ enum radeon_dpm_forced_level level)
+{
+ struct sumo_power_info *pi = sumo_get_pi(rdev);
+ struct radeon_ps *rps = &pi->current_rps;
+ struct sumo_ps *ps = sumo_get_ps(rps);
+ int i;
+
+ if (ps->num_levels <= 1)
+ return 0;
+
+ if (level == RADEON_DPM_FORCED_LEVEL_HIGH) {
+ sumo_power_level_enable(rdev, ps->num_levels - 1, true);
+ sumo_set_forced_level(rdev, ps->num_levels - 1);
+ sumo_set_forced_mode_enabled(rdev);
+ for (i = 0; i < ps->num_levels - 1; i++) {
+ sumo_power_level_enable(rdev, i, false);
+ }
+ sumo_set_forced_mode(rdev, false);
+ sumo_set_forced_mode_enabled(rdev);
+ sumo_set_forced_mode(rdev, false);
+ } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) {
+ sumo_power_level_enable(rdev, 0, true);
+ sumo_set_forced_level(rdev, 0);
+ sumo_set_forced_mode_enabled(rdev);
+ for (i = 1; i < ps->num_levels; i++) {
+ sumo_power_level_enable(rdev, i, false);
+ }
+ sumo_set_forced_mode(rdev, false);
+ sumo_set_forced_mode_enabled(rdev);
+ sumo_set_forced_mode(rdev, false);
+ } else {
+ for (i = 0; i < ps->num_levels; i++) {
+ sumo_power_level_enable(rdev, i, true);
+ }
+ }
+
+ rdev->pm.dpm.forced_level = level;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c
index 8a32bcc6bbb5..a1eb5f59939f 100644
--- a/drivers/gpu/drm/radeon/trinity_dpm.c
+++ b/drivers/gpu/drm/radeon/trinity_dpm.c
@@ -1158,6 +1158,37 @@ static void trinity_setup_nbp_sim(struct radeon_device *rdev,
}
}
+int trinity_dpm_force_performance_level(struct radeon_device *rdev,
+ enum radeon_dpm_forced_level level)
+{
+ struct trinity_power_info *pi = trinity_get_pi(rdev);
+ struct radeon_ps *rps = &pi->current_rps;
+ struct trinity_ps *ps = trinity_get_ps(rps);
+ int i, ret;
+
+ if (ps->num_levels <= 1)
+ return 0;
+
+ if (level == RADEON_DPM_FORCED_LEVEL_HIGH) {
+ /* not supported by the hw */
+ return -EINVAL;
+ } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) {
+ ret = trinity_dpm_n_levels_disabled(rdev, ps->num_levels - 1);
+ if (ret)
+ return ret;
+ } else {
+ for (i = 0; i < ps->num_levels; i++) {
+ ret = trinity_dpm_n_levels_disabled(rdev, 0);
+ if (ret)
+ return ret;
+ }
+ }
+
+ rdev->pm.dpm.forced_level = level;
+
+ return 0;
+}
+
int trinity_dpm_pre_set_power_state(struct radeon_device *rdev)
{
struct trinity_power_info *pi = trinity_get_pi(rdev);
@@ -1190,6 +1221,7 @@ int trinity_dpm_set_power_state(struct radeon_device *rdev)
trinity_force_level_0(rdev);
trinity_unforce_levels(rdev);
trinity_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
+ rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO;
}
trinity_release_mutex(rdev);
diff --git a/drivers/gpu/drm/radeon/trinity_dpm.h b/drivers/gpu/drm/radeon/trinity_dpm.h
index c621b843aab5..e82df071f8b3 100644
--- a/drivers/gpu/drm/radeon/trinity_dpm.h
+++ b/drivers/gpu/drm/radeon/trinity_dpm.h
@@ -121,6 +121,7 @@ struct trinity_power_info {
int trinity_dpm_config(struct radeon_device *rdev, bool enable);
int trinity_uvd_dpm_config(struct radeon_device *rdev);
int trinity_dpm_force_state(struct radeon_device *rdev, u32 n);
+int trinity_dpm_n_levels_disabled(struct radeon_device *rdev, u32 n);
int trinity_dpm_no_forced_level(struct radeon_device *rdev);
int trinity_dce_enable_voltage_adjustment(struct radeon_device *rdev,
bool enable);
diff --git a/drivers/gpu/drm/radeon/trinity_smc.c b/drivers/gpu/drm/radeon/trinity_smc.c
index 85f86a29513c..a42d89f1830c 100644
--- a/drivers/gpu/drm/radeon/trinity_smc.c
+++ b/drivers/gpu/drm/radeon/trinity_smc.c
@@ -73,6 +73,13 @@ int trinity_dpm_force_state(struct radeon_device *rdev, u32 n)
return trinity_notify_message_to_smu(rdev, PPSMC_MSG_DPM_ForceState);
}
+int trinity_dpm_n_levels_disabled(struct radeon_device *rdev, u32 n)
+{
+ WREG32_SMC(SMU_SCRATCH0, n);
+
+ return trinity_notify_message_to_smu(rdev, PPSMC_MSG_DPM_N_LevelsDisabled);
+}
+
int trinity_uvd_dpm_config(struct radeon_device *rdev)
{
return trinity_notify_message_to_smu(rdev, PPSMC_MSG_UVD_DPM_Config);