All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alex Deucher <alexdeucher@gmail.com>
To: airlied@gmail.com, dri-devel@lists.freedesktop.org
Cc: mjg59@srcf.ucam.org
Subject: [PATCH 8/9] drm/radeon/kms/pm: rework power management
Date: Fri,  7 May 2010 17:16:19 -0400	[thread overview]
Message-ID: <1273266980-20982-9-git-send-email-alexdeucher@gmail.com> (raw)
In-Reply-To: <1273266980-20982-8-git-send-email-alexdeucher@gmail.com>

- Separate dynpm and profile based power management methods.  You can select the pm method
  by echoing the selected method ("dynpm" or "profile") to power_method in sysfs.
- Expose basic 4 profile in profile method
  "default" - default clocks
  "auto" - select between low and high based on ac/dc state
  "low" - DC, low power mode
  "high" - AC, performance mode
  The current base profile is "default", but it should switched to "auto" once we've tested
  on more systems.  Switching the state is a matter of echoing the requested profile to
  power_profile in sysfs.  The lowest power states are selected automatically when dpms turns
  the monitors off in all states but default.
- Remove dynamic fence-based reclocking for the moment.  We can revisit this later once we
  have basic pm in.
- Move pm init/fini to modesetting path.  pm is tightly coupled with display state.  Make sure
  display side is initialized before pm.
- Add pm suspend/resume functions to make sure pm state is properly reinitialized on resume.
- Remove dynpm module option.  It's now selectable via sysfs.

Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
---
 drivers/gpu/drm/radeon/Kconfig          |    1 +
 drivers/gpu/drm/radeon/evergreen.c      |    3 -
 drivers/gpu/drm/radeon/r100.c           |  128 ++-----
 drivers/gpu/drm/radeon/r300.c           |    3 -
 drivers/gpu/drm/radeon/r420.c           |   32 ++-
 drivers/gpu/drm/radeon/r520.c           |    2 -
 drivers/gpu/drm/radeon/r600.c           |  332 ++++++++++++-----
 drivers/gpu/drm/radeon/radeon.h         |   87 +++--
 drivers/gpu/drm/radeon/radeon_asic.c    |   56 ++--
 drivers/gpu/drm/radeon/radeon_asic.h    |   12 +-
 drivers/gpu/drm/radeon/radeon_device.c  |    2 +
 drivers/gpu/drm/radeon/radeon_display.c |    4 +
 drivers/gpu/drm/radeon/radeon_drv.c     |    4 -
 drivers/gpu/drm/radeon/radeon_pm.c      |  628 +++++++++++++++++++------------
 drivers/gpu/drm/radeon/rs400.c          |    3 -
 drivers/gpu/drm/radeon/rs600.c          |    3 -
 drivers/gpu/drm/radeon/rs690.c          |    3 -
 drivers/gpu/drm/radeon/rv515.c          |    3 -
 drivers/gpu/drm/radeon/rv770.c          |    3 -
 19 files changed, 790 insertions(+), 519 deletions(-)

diff --git a/drivers/gpu/drm/radeon/Kconfig b/drivers/gpu/drm/radeon/Kconfig
index 1c02d23..80c5b3e 100644
--- a/drivers/gpu/drm/radeon/Kconfig
+++ b/drivers/gpu/drm/radeon/Kconfig
@@ -1,6 +1,7 @@
 config DRM_RADEON_KMS
 	bool "Enable modesetting on radeon by default - NEW DRIVER"
 	depends on DRM_RADEON
+	depends on POWER_SUPPLY
 	help
 	  Choose this option if you want kernel modesetting enabled by default.
 
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 8d86d05..8c8e4d3 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -2115,8 +2115,6 @@ int evergreen_init(struct radeon_device *rdev)
 	r = radeon_clocks_init(rdev);
 	if (r)
 		return r;
-	/* Initialize power management */
-	radeon_pm_init(rdev);
 	/* Fence driver */
 	r = radeon_fence_driver_init(rdev);
 	if (r)
@@ -2178,7 +2176,6 @@ int evergreen_init(struct radeon_device *rdev)
 
 void evergreen_fini(struct radeon_device *rdev)
 {
-	radeon_pm_fini(rdev);
 	/*r600_blit_fini(rdev);*/
 	r700_cp_fini(rdev);
 	r600_wb_fini(rdev);
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index 4161a35..4c5d21b 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -68,22 +68,21 @@ MODULE_FIRMWARE(FIRMWARE_R520);
  * r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280
  */
 
-void r100_get_power_state(struct radeon_device *rdev,
-			  enum radeon_pm_action action)
+void r100_pm_get_dynpm_state(struct radeon_device *rdev)
 {
 	int i;
-	rdev->pm.can_upclock = true;
-	rdev->pm.can_downclock = true;
+	rdev->pm.dynpm_can_upclock = true;
+	rdev->pm.dynpm_can_downclock = true;
 
-	switch (action) {
-	case PM_ACTION_MINIMUM:
+	switch (rdev->pm.dynpm_planned_action) {
+	case DYNPM_ACTION_MINIMUM:
 		rdev->pm.requested_power_state_index = 0;
-		rdev->pm.can_downclock = false;
+		rdev->pm.dynpm_can_downclock = false;
 		break;
-	case PM_ACTION_DOWNCLOCK:
+	case DYNPM_ACTION_DOWNCLOCK:
 		if (rdev->pm.current_power_state_index == 0) {
 			rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index;
-			rdev->pm.can_downclock = false;
+			rdev->pm.dynpm_can_downclock = false;
 		} else {
 			if (rdev->pm.active_crtc_count > 1) {
 				for (i = 0; i < rdev->pm.num_power_states; i++) {
@@ -108,10 +107,10 @@ void r100_get_power_state(struct radeon_device *rdev,
 			rdev->pm.requested_power_state_index++;
 		}
 		break;
-	case PM_ACTION_UPCLOCK:
+	case DYNPM_ACTION_UPCLOCK:
 		if (rdev->pm.current_power_state_index == (rdev->pm.num_power_states - 1)) {
 			rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index;
-			rdev->pm.can_upclock = false;
+			rdev->pm.dynpm_can_upclock = false;
 		} else {
 			if (rdev->pm.active_crtc_count > 1) {
 				for (i = (rdev->pm.num_power_states - 1); i >= 0; i--) {
@@ -130,11 +129,11 @@ void r100_get_power_state(struct radeon_device *rdev,
 					rdev->pm.current_power_state_index + 1;
 		}
 		break;
-	case PM_ACTION_DEFAULT:
+	case DYNPM_ACTION_DEFAULT:
 		rdev->pm.requested_power_state_index = rdev->pm.default_power_state_index;
-		rdev->pm.can_upclock = false;
+		rdev->pm.dynpm_can_upclock = false;
 		break;
-	case PM_ACTION_NONE:
+	case DYNPM_ACTION_NONE:
 	default:
 		DRM_ERROR("Requested mode for not defined action\n");
 		return;
@@ -151,77 +150,33 @@ void r100_get_power_state(struct radeon_device *rdev,
 		 pcie_lanes);
 }
 
-void r100_set_power_state(struct radeon_device *rdev, bool static_switch)
-{
-	u32 sclk, mclk;
-
-	if (rdev->pm.current_power_state_index == rdev->pm.requested_power_state_index)
-		return;
-
-	if (radeon_gui_idle(rdev)) {
-
-		sclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
-			clock_info[rdev->pm.requested_clock_mode_index].sclk;
-		if (sclk > rdev->clock.default_sclk)
-			sclk = rdev->clock.default_sclk;
-
-		mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
-			clock_info[rdev->pm.requested_clock_mode_index].mclk;
-		if (mclk > rdev->clock.default_mclk)
-			mclk = rdev->clock.default_mclk;
-		/* don't change the mclk with multiple crtcs */
-		if (rdev->pm.active_crtc_count > 1)
-			mclk = rdev->clock.default_mclk;
-
-		/* voltage, pcie lanes, etc.*/
-		radeon_pm_misc(rdev);
-
-		if (static_switch) {
-			radeon_pm_prepare(rdev);
-			/* set engine clock */
-			if (sclk != rdev->pm.current_sclk) {
-				radeon_set_engine_clock(rdev, sclk);
-				rdev->pm.current_sclk = sclk;
-				DRM_INFO("Setting: e: %d\n", sclk);
-			}
-			/* set memory clock */
-			if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) {
-				radeon_set_memory_clock(rdev, mclk);
-				rdev->pm.current_mclk = mclk;
-				DRM_INFO("Setting: m: %d\n", mclk);
-			}
-			radeon_pm_finish(rdev);
-		} else {
-			radeon_sync_with_vblank(rdev);
-
-			if (!radeon_pm_in_vbl(rdev))
-				return;
-
-			radeon_pm_prepare(rdev);
-			/* set engine clock */
-			if (sclk != rdev->pm.current_sclk) {
-				radeon_pm_debug_check_in_vbl(rdev, false);
-				radeon_set_engine_clock(rdev, sclk);
-				radeon_pm_debug_check_in_vbl(rdev, true);
-				rdev->pm.current_sclk = sclk;
-				DRM_INFO("Setting: e: %d\n", sclk);
-			}
-
-			/* set memory clock */
-			if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) {
-				radeon_pm_debug_check_in_vbl(rdev, false);
-				radeon_set_memory_clock(rdev, mclk);
-				radeon_pm_debug_check_in_vbl(rdev, true);
-				rdev->pm.current_mclk = mclk;
-				DRM_INFO("Setting: m: %d\n", mclk);
-			}
-			radeon_pm_finish(rdev);
-		}
-
-		rdev->pm.current_power_state_index = rdev->pm.requested_power_state_index;
-		rdev->pm.current_clock_mode_index = rdev->pm.requested_clock_mode_index;
-	} else
-		DRM_INFO("pm: GUI not idle!!!\n");
+void r100_pm_init_profile(struct radeon_device *rdev)
+{
+	/* default */
+	rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+	rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+	rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 0;
+	/* low sh */
+	rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0;
+	/* high sh */
+	rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+	rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 0;
+	/* low mh */
+	rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+	rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0;
+	/* high mh */
+	rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+	rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 0;
 }
 
 void r100_pm_misc(struct radeon_device *rdev)
@@ -3815,7 +3770,6 @@ int r100_suspend(struct radeon_device *rdev)
 
 void r100_fini(struct radeon_device *rdev)
 {
-	radeon_pm_fini(rdev);
 	r100_cp_fini(rdev);
 	r100_wb_fini(rdev);
 	r100_ib_fini(rdev);
@@ -3871,8 +3825,6 @@ int r100_init(struct radeon_device *rdev)
 	r100_errata(rdev);
 	/* Initialize clocks */
 	radeon_get_clock_info(rdev->ddev);
-	/* Initialize power management */
-	radeon_pm_init(rdev);
 	/* initialize AGP */
 	if (rdev->flags & RADEON_IS_AGP) {
 		r = radeon_agp_init(rdev);
diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c
index 5d622cb..5c54db5 100644
--- a/drivers/gpu/drm/radeon/r300.c
+++ b/drivers/gpu/drm/radeon/r300.c
@@ -1345,7 +1345,6 @@ int r300_suspend(struct radeon_device *rdev)
 
 void r300_fini(struct radeon_device *rdev)
 {
-	radeon_pm_fini(rdev);
 	r100_cp_fini(rdev);
 	r100_wb_fini(rdev);
 	r100_ib_fini(rdev);
@@ -1401,8 +1400,6 @@ int r300_init(struct radeon_device *rdev)
 	r300_errata(rdev);
 	/* Initialize clocks */
 	radeon_get_clock_info(rdev->ddev);
-	/* Initialize power management */
-	radeon_pm_init(rdev);
 	/* initialize AGP */
 	if (rdev->flags & RADEON_IS_AGP) {
 		r = radeon_agp_init(rdev);
diff --git a/drivers/gpu/drm/radeon/r420.c b/drivers/gpu/drm/radeon/r420.c
index 3759d83..87c0e38 100644
--- a/drivers/gpu/drm/radeon/r420.c
+++ b/drivers/gpu/drm/radeon/r420.c
@@ -36,6 +36,35 @@
 #include "r420d.h"
 #include "r420_reg_safe.h"
 
+void r420_pm_init_profile(struct radeon_device *rdev)
+{
+	/* default */
+	rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+	rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+	rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 0;
+	/* low sh */
+	rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = 1;
+	rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0;
+	/* high sh */
+	rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+	rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 0;
+	/* low mh */
+	rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+	rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0;
+	/* high mh */
+	rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+	rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0;
+	rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 0;
+}
+
 static void r420_set_reg_safe(struct radeon_device *rdev)
 {
 	rdev->config.r300.reg_safe_bm = r420_reg_safe_bm;
@@ -268,7 +297,6 @@ int r420_suspend(struct radeon_device *rdev)
 
 void r420_fini(struct radeon_device *rdev)
 {
-	radeon_pm_fini(rdev);
 	r100_cp_fini(rdev);
 	r100_wb_fini(rdev);
 	r100_ib_fini(rdev);
@@ -328,8 +356,6 @@ int r420_init(struct radeon_device *rdev)
 
 	/* Initialize clocks */
 	radeon_get_clock_info(rdev->ddev);
-	/* Initialize power management */
-	radeon_pm_init(rdev);
 	/* initialize AGP */
 	if (rdev->flags & RADEON_IS_AGP) {
 		r = radeon_agp_init(rdev);
diff --git a/drivers/gpu/drm/radeon/r520.c b/drivers/gpu/drm/radeon/r520.c
index 870111e..34330df 100644
--- a/drivers/gpu/drm/radeon/r520.c
+++ b/drivers/gpu/drm/radeon/r520.c
@@ -261,8 +261,6 @@ int r520_init(struct radeon_device *rdev)
 	}
 	/* Initialize clocks */
 	radeon_get_clock_info(rdev->ddev);
-	/* Initialize power management */
-	radeon_pm_init(rdev);
 	/* initialize AGP */
 	if (rdev->flags & RADEON_IS_AGP) {
 		r = radeon_agp_init(rdev);
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index 08a328c..618d76d 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -92,13 +92,12 @@ void r600_gpu_init(struct radeon_device *rdev);
 void r600_fini(struct radeon_device *rdev);
 void r600_irq_disable(struct radeon_device *rdev);
 
-void r600_get_power_state(struct radeon_device *rdev,
-			  enum radeon_pm_action action)
+void r600_pm_get_dynpm_state(struct radeon_device *rdev)
 {
 	int i;
 
-	rdev->pm.can_upclock = true;
-	rdev->pm.can_downclock = true;
+	rdev->pm.dynpm_can_upclock = true;
+	rdev->pm.dynpm_can_downclock = true;
 
 	/* power state array is low to high, default is first */
 	if ((rdev->flags & RADEON_IS_IGP) || (rdev->family == CHIP_R600)) {
@@ -107,16 +106,16 @@ void r600_get_power_state(struct radeon_device *rdev,
 		if (rdev->pm.num_power_states > 2)
 			min_power_state_index = 1;
 
-		switch (action) {
-		case PM_ACTION_MINIMUM:
+		switch (rdev->pm.dynpm_planned_action) {
+		case DYNPM_ACTION_MINIMUM:
 			rdev->pm.requested_power_state_index = min_power_state_index;
 			rdev->pm.requested_clock_mode_index = 0;
-			rdev->pm.can_downclock = false;
+			rdev->pm.dynpm_can_downclock = false;
 			break;
-		case PM_ACTION_DOWNCLOCK:
+		case DYNPM_ACTION_DOWNCLOCK:
 			if (rdev->pm.current_power_state_index == min_power_state_index) {
 				rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index;
-				rdev->pm.can_downclock = false;
+				rdev->pm.dynpm_can_downclock = false;
 			} else {
 				if (rdev->pm.active_crtc_count > 1) {
 					for (i = 0; i < rdev->pm.num_power_states; i++) {
@@ -144,10 +143,10 @@ void r600_get_power_state(struct radeon_device *rdev,
 				rdev->pm.requested_power_state_index++;
 			}
 			break;
-		case PM_ACTION_UPCLOCK:
+		case DYNPM_ACTION_UPCLOCK:
 			if (rdev->pm.current_power_state_index == (rdev->pm.num_power_states - 1)) {
 				rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index;
-				rdev->pm.can_upclock = false;
+				rdev->pm.dynpm_can_upclock = false;
 			} else {
 				if (rdev->pm.active_crtc_count > 1) {
 					for (i = (rdev->pm.num_power_states - 1); i >= 0; i--) {
@@ -168,12 +167,12 @@ void r600_get_power_state(struct radeon_device *rdev,
 			}
 			rdev->pm.requested_clock_mode_index = 0;
 			break;
-		case PM_ACTION_DEFAULT:
+		case DYNPM_ACTION_DEFAULT:
 			rdev->pm.requested_power_state_index = rdev->pm.default_power_state_index;
 			rdev->pm.requested_clock_mode_index = 0;
-			rdev->pm.can_upclock = false;
+			rdev->pm.dynpm_can_upclock = false;
 			break;
-		case PM_ACTION_NONE:
+		case DYNPM_ACTION_NONE:
 		default:
 			DRM_ERROR("Requested mode for not defined action\n");
 			return;
@@ -200,22 +199,22 @@ void r600_get_power_state(struct radeon_device *rdev,
 		} else
 			rdev->pm.requested_power_state_index = 1;
 
-		switch (action) {
-		case PM_ACTION_MINIMUM:
+		switch (rdev->pm.dynpm_planned_action) {
+		case DYNPM_ACTION_MINIMUM:
 			rdev->pm.requested_clock_mode_index = 0;
-			rdev->pm.can_downclock = false;
+			rdev->pm.dynpm_can_downclock = false;
 			break;
-		case PM_ACTION_DOWNCLOCK:
+		case DYNPM_ACTION_DOWNCLOCK:
 			if (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index) {
 				if (rdev->pm.current_clock_mode_index == 0) {
 					rdev->pm.requested_clock_mode_index = 0;
-					rdev->pm.can_downclock = false;
+					rdev->pm.dynpm_can_downclock = false;
 				} else
 					rdev->pm.requested_clock_mode_index =
 						rdev->pm.current_clock_mode_index - 1;
 			} else {
 				rdev->pm.requested_clock_mode_index = 0;
-				rdev->pm.can_downclock = false;
+				rdev->pm.dynpm_can_downclock = false;
 			}
 			/* don't use the power state if crtcs are active and no display flag is set */
 			if ((rdev->pm.active_crtc_count > 0) &&
@@ -225,27 +224,27 @@ void r600_get_power_state(struct radeon_device *rdev,
 				rdev->pm.requested_clock_mode_index++;
 			}
 			break;
-		case PM_ACTION_UPCLOCK:
+		case DYNPM_ACTION_UPCLOCK:
 			if (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index) {
 				if (rdev->pm.current_clock_mode_index ==
 				    (rdev->pm.power_state[rdev->pm.requested_power_state_index].num_clock_modes - 1)) {
 					rdev->pm.requested_clock_mode_index = rdev->pm.current_clock_mode_index;
-					rdev->pm.can_upclock = false;
+					rdev->pm.dynpm_can_upclock = false;
 				} else
 					rdev->pm.requested_clock_mode_index =
 						rdev->pm.current_clock_mode_index + 1;
 			} else {
 				rdev->pm.requested_clock_mode_index =
 					rdev->pm.power_state[rdev->pm.requested_power_state_index].num_clock_modes - 1;
-				rdev->pm.can_upclock = false;
+				rdev->pm.dynpm_can_upclock = false;
 			}
 			break;
-		case PM_ACTION_DEFAULT:
+		case DYNPM_ACTION_DEFAULT:
 			rdev->pm.requested_power_state_index = rdev->pm.default_power_state_index;
 			rdev->pm.requested_clock_mode_index = 0;
-			rdev->pm.can_upclock = false;
+			rdev->pm.dynpm_can_upclock = false;
 			break;
-		case PM_ACTION_NONE:
+		case DYNPM_ACTION_NONE:
 		default:
 			DRM_ERROR("Requested mode for not defined action\n");
 			return;
@@ -261,73 +260,225 @@ void r600_get_power_state(struct radeon_device *rdev,
 		 pcie_lanes);
 }
 
-void r600_set_power_state(struct radeon_device *rdev, bool static_switch)
+static int r600_pm_get_type_index(struct radeon_device *rdev,
+				  enum radeon_pm_state_type ps_type,
+				  int instance)
 {
-	u32 sclk, mclk;
-
-	if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) &&
-	    (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index))
-		return;
-
-	if (radeon_gui_idle(rdev)) {
-		sclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
-			clock_info[rdev->pm.requested_clock_mode_index].sclk;
-		if (sclk > rdev->clock.default_sclk)
-			sclk = rdev->clock.default_sclk;
-
-		mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
-			clock_info[rdev->pm.requested_clock_mode_index].mclk;
-		if (mclk > rdev->clock.default_mclk)
-			mclk = rdev->clock.default_mclk;
-
-		/* voltage, pcie lanes, etc.*/
-		radeon_pm_misc(rdev);
+	int i;
+	int found_instance = -1;
 
-		if (static_switch) {
-			radeon_pm_prepare(rdev);
-			/* set engine clock */
-			if (sclk != rdev->pm.current_sclk) {
-				radeon_set_engine_clock(rdev, sclk);
-				rdev->pm.current_sclk = sclk;
-				DRM_INFO("Setting: e: %d\n", sclk);
-			}
-			/* set memory clock */
-			if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) {
-				radeon_set_memory_clock(rdev, mclk);
-				rdev->pm.current_mclk = mclk;
-				DRM_INFO("Setting: m: %d\n", mclk);
-			}
-			radeon_pm_finish(rdev);
+	for (i = 0; i < rdev->pm.num_power_states; i++) {
+		if (rdev->pm.power_state[i].type == ps_type) {
+			found_instance++;
+			if (found_instance == instance)
+				return i;
+		}
+	}
+	/* return default if no match */
+	return rdev->pm.default_power_state_index;
+}
+
+void rs780_pm_init_profile(struct radeon_device *rdev)
+{
+	if (rdev->pm.num_power_states == 2) {
+		/* default */
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 0;
+		/* low sh */
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0;
+		/* high sh */
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = 1;
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 0;
+		/* low mh */
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0;
+		/* high mh */
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = 1;
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 0;
+	} else if (rdev->pm.num_power_states == 3) {
+		/* default */
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 0;
+		/* low sh */
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = 1;
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = 1;
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0;
+		/* high sh */
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = 1;
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = 2;
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 0;
+		/* low mh */
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = 1;
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = 1;
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0;
+		/* high mh */
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = 1;
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = 2;
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 0;
+	} else {
+		/* default */
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 0;
+		/* low sh */
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = 2;
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = 2;
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0;
+		/* high sh */
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = 2;
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = 3;
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 0;
+		/* low mh */
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = 2;
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0;
+		/* high mh */
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = 2;
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = 3;
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 0;
+	}
+}
+
+void r600_pm_init_profile(struct radeon_device *rdev)
+{
+	if (rdev->family == CHIP_R600) {
+		/* XXX */
+		/* default */
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 2;
+		/* low sh */
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 2;
+		/* high sh */
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 2;
+		/* low mh */
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 2;
+		/* high mh */
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 2;
+	} else if (rdev->flags & RADEON_IS_MOBILITY) {
+		/* default */
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 2;
+		/* low sh */
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx =
+			r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0);
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx =
+			r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0);
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 2;
+		/* high sh */
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx =
+			r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0);
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx =
+			r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0);
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 2;
+		/* low mh */
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx =
+			r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 1);
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx =
+			r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 1);
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 2;
+		/* high mh */
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx =
+			r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1);
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx =
+			r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1);
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0;
+		rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 2;
+	} else {
+		if (rdev->pm.num_power_states < 4) {
+			/* default */
+			rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+			rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+			rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0;
+			rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 2;
+			/* low sh */
+			rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+			rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+			rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0;
+			rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 2;
+			/* high sh */
+			rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+			rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+			rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0;
+			rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 2;
+			/* low mh */
+			rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+			rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+			rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0;
+			rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 2;
+			/* high mh */
+			rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+			rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+			rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0;
+			rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 2;
 		} else {
-			radeon_sync_with_vblank(rdev);
-
-			if (!radeon_pm_in_vbl(rdev))
-				return;
-
-			radeon_pm_prepare(rdev);
-			if (sclk != rdev->pm.current_sclk) {
-				radeon_pm_debug_check_in_vbl(rdev, false);
-				radeon_set_engine_clock(rdev, sclk);
-				radeon_pm_debug_check_in_vbl(rdev, true);
-				rdev->pm.current_sclk = sclk;
-				DRM_INFO("Setting: e: %d\n", sclk);
-			}
-
-			/* set memory clock */
-			if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) {
-				radeon_pm_debug_check_in_vbl(rdev, false);
-				radeon_set_memory_clock(rdev, mclk);
-				radeon_pm_debug_check_in_vbl(rdev, true);
-				rdev->pm.current_mclk = mclk;
-				DRM_INFO("Setting: m: %d\n", mclk);
-			}
-			radeon_pm_finish(rdev);
+			/* default */
+			rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
+			rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
+			rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0;
+			rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 2;
+			/* low sh */
+			rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = 1;
+			rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = 1;
+			rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0;
+			rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 2;
+			/* high sh */
+			rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = 1;
+			rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = 1;
+			rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0;
+			rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 2;
+			/* low mh */
+			rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = 3;
+			rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = 3;
+			rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0;
+			rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 2;
+			/* high mh */
+			rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = 3;
+			rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = 3;
+			rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0;
+			rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 2;
 		}
-
-		rdev->pm.current_power_state_index = rdev->pm.requested_power_state_index;
-		rdev->pm.current_clock_mode_index = rdev->pm.requested_clock_mode_index;
-	} else
-		DRM_INFO("GUI not idle!!!\n");
+	}
 }
 
 void r600_pm_misc(struct radeon_device *rdev)
@@ -2320,8 +2471,6 @@ int r600_init(struct radeon_device *rdev)
 	r = radeon_clocks_init(rdev);
 	if (r)
 		return r;
-	/* Initialize power management */
-	radeon_pm_init(rdev);
 	/* Fence driver */
 	r = radeon_fence_driver_init(rdev);
 	if (r)
@@ -2386,7 +2535,6 @@ int r600_init(struct radeon_device *rdev)
 
 void r600_fini(struct radeon_device *rdev)
 {
-	radeon_pm_fini(rdev);
 	r600_audio_fini(rdev);
 	r600_blit_fini(rdev);
 	r600_cp_fini(rdev);
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 480a83f..5c9ce2b 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -89,7 +89,6 @@ extern int radeon_testing;
 extern int radeon_connector_table;
 extern int radeon_tv;
 extern int radeon_new_pll;
-extern int radeon_dynpm;
 extern int radeon_audio;
 extern int radeon_disp_priority;
 extern int radeon_hw_i2c;
@@ -173,11 +172,10 @@ struct radeon_clock {
 int radeon_pm_init(struct radeon_device *rdev);
 void radeon_pm_fini(struct radeon_device *rdev);
 void radeon_pm_compute_clocks(struct radeon_device *rdev);
+void radeon_pm_suspend(struct radeon_device *rdev);
+void radeon_pm_resume(struct radeon_device *rdev);
 void radeon_combios_get_power_modes(struct radeon_device *rdev);
 void radeon_atombios_get_power_modes(struct radeon_device *rdev);
-bool radeon_pm_in_vbl(struct radeon_device *rdev);
-bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish);
-void radeon_sync_with_vblank(struct radeon_device *rdev);
 
 /*
  * Fences.
@@ -608,18 +606,24 @@ struct radeon_wb {
  * Equation between gpu/memory clock and available bandwidth is hw dependent
  * (type of memory, bus size, efficiency, ...)
  */
-enum radeon_pm_state {
-	PM_STATE_DISABLED,
-	PM_STATE_MINIMUM,
-	PM_STATE_PAUSED,
-	PM_STATE_ACTIVE
+
+enum radeon_pm_method {
+	PM_METHOD_PROFILE,
+	PM_METHOD_DYNPM,
+};
+
+enum radeon_dynpm_state {
+	DYNPM_STATE_DISABLED,
+	DYNPM_STATE_MINIMUM,
+	DYNPM_STATE_PAUSED,
+	DYNPM_STATE_ACTIVE
 };
-enum radeon_pm_action {
-	PM_ACTION_NONE,
-	PM_ACTION_MINIMUM,
-	PM_ACTION_DOWNCLOCK,
-	PM_ACTION_UPCLOCK,
-	PM_ACTION_DEFAULT
+enum radeon_dynpm_action {
+	DYNPM_ACTION_NONE,
+	DYNPM_ACTION_MINIMUM,
+	DYNPM_ACTION_DOWNCLOCK,
+	DYNPM_ACTION_UPCLOCK,
+	DYNPM_ACTION_DEFAULT
 };
 
 enum radeon_voltage_type {
@@ -637,11 +641,25 @@ enum radeon_pm_state_type {
 	POWER_STATE_TYPE_PERFORMANCE,
 };
 
-enum radeon_pm_clock_mode_type {
-	POWER_MODE_TYPE_DEFAULT,
-	POWER_MODE_TYPE_LOW,
-	POWER_MODE_TYPE_MID,
-	POWER_MODE_TYPE_HIGH,
+enum radeon_pm_profile_type {
+	PM_PROFILE_DEFAULT,
+	PM_PROFILE_AUTO,
+	PM_PROFILE_LOW,
+	PM_PROFILE_HIGH,
+};
+
+#define PM_PROFILE_DEFAULT_IDX 0
+#define PM_PROFILE_LOW_SH_IDX  1
+#define PM_PROFILE_HIGH_SH_IDX 2
+#define PM_PROFILE_LOW_MH_IDX  3
+#define PM_PROFILE_HIGH_MH_IDX 4
+#define PM_PROFILE_MAX         5
+
+struct radeon_pm_profile {
+	int dpms_off_ps_idx;
+	int dpms_on_ps_idx;
+	int dpms_off_cm_idx;
+	int dpms_on_cm_idx;
 };
 
 struct radeon_voltage {
@@ -696,12 +714,6 @@ struct radeon_power_state {
 
 struct radeon_pm {
 	struct mutex		mutex;
-	struct delayed_work	idle_work;
-	enum radeon_pm_state	state;
-	enum radeon_pm_action	planned_action;
-	unsigned long		action_timeout;
-	bool                    can_upclock;
-	bool                    can_downclock;
 	u32			active_crtcs;
 	int			active_crtc_count;
 	int			req_vblank;
@@ -731,6 +743,19 @@ struct radeon_pm {
 	u32                     current_sclk;
 	u32                     current_mclk;
 	struct radeon_i2c_chan *i2c_bus;
+	/* selected pm method */
+	enum radeon_pm_method     pm_method;
+	/* dynpm power management */
+	struct delayed_work	dynpm_idle_work;
+	enum radeon_dynpm_state	dynpm_state;
+	enum radeon_dynpm_action	dynpm_planned_action;
+	unsigned long		dynpm_action_timeout;
+	bool                    dynpm_can_upclock;
+	bool                    dynpm_can_downclock;
+	/* profile-based power management */
+	enum radeon_pm_profile_type profile;
+	int                     profile_index;
+	struct radeon_pm_profile profiles[PM_PROFILE_MAX];
 };
 
 
@@ -819,11 +844,12 @@ struct radeon_asic {
 	 */
 	void (*ioctl_wait_idle)(struct radeon_device *rdev, struct radeon_bo *bo);
 	bool (*gui_idle)(struct radeon_device *rdev);
-	void (*get_power_state)(struct radeon_device *rdev, enum radeon_pm_action action);
-	void (*set_power_state)(struct radeon_device *rdev, bool static_switch);
+	/* power management */
 	void (*pm_misc)(struct radeon_device *rdev);
 	void (*pm_prepare)(struct radeon_device *rdev);
 	void (*pm_finish)(struct radeon_device *rdev);
+	void (*pm_init_profile)(struct radeon_device *rdev);
+	void (*pm_get_dynpm_state)(struct radeon_device *rdev);
 };
 
 /*
@@ -1041,6 +1067,7 @@ struct radeon_device {
 	uint8_t			audio_category_code;
 
 	bool powered_down;
+	struct notifier_block acpi_nb;
 };
 
 int radeon_device_init(struct radeon_device *rdev,
@@ -1232,11 +1259,11 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v)
 #define radeon_hpd_sense(rdev, hpd) (rdev)->asic->hpd_sense((rdev), (hpd))
 #define radeon_hpd_set_polarity(rdev, hpd) (rdev)->asic->hpd_set_polarity((rdev), (hpd))
 #define radeon_gui_idle(rdev) (rdev)->asic->gui_idle((rdev))
-#define radeon_get_power_state(rdev, a) (rdev)->asic->get_power_state((rdev), (a))
-#define radeon_set_power_state(rdev, s) (rdev)->asic->set_power_state((rdev), (s))
 #define radeon_pm_misc(rdev) (rdev)->asic->pm_misc((rdev))
 #define radeon_pm_prepare(rdev) (rdev)->asic->pm_prepare((rdev))
 #define radeon_pm_finish(rdev) (rdev)->asic->pm_finish((rdev))
+#define radeon_pm_init_profile(rdev) (rdev)->asic->pm_init_profile((rdev))
+#define radeon_pm_get_dynpm_state(rdev) (rdev)->asic->pm_get_dynpm_state((rdev))
 
 /* Common functions */
 /* AGP */
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c
index 1e6f17b..e57df08 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.c
+++ b/drivers/gpu/drm/radeon/radeon_asic.c
@@ -166,11 +166,11 @@ static struct radeon_asic r100_asic = {
 	.hpd_set_polarity = &r100_hpd_set_polarity,
 	.ioctl_wait_idle = NULL,
 	.gui_idle = &r100_gui_idle,
-	.get_power_state = &r100_get_power_state,
-	.set_power_state = &r100_set_power_state,
 	.pm_misc = &r100_pm_misc,
 	.pm_prepare = &r100_pm_prepare,
 	.pm_finish = &r100_pm_finish,
+	.pm_init_profile = &r100_pm_init_profile,
+	.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
 };
 
 static struct radeon_asic r200_asic = {
@@ -210,11 +210,11 @@ static struct radeon_asic r200_asic = {
 	.hpd_set_polarity = &r100_hpd_set_polarity,
 	.ioctl_wait_idle = NULL,
 	.gui_idle = &r100_gui_idle,
-	.get_power_state = &r100_get_power_state,
-	.set_power_state = &r100_set_power_state,
 	.pm_misc = &r100_pm_misc,
 	.pm_prepare = &r100_pm_prepare,
 	.pm_finish = &r100_pm_finish,
+	.pm_init_profile = &r100_pm_init_profile,
+	.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
 };
 
 static struct radeon_asic r300_asic = {
@@ -255,11 +255,11 @@ static struct radeon_asic r300_asic = {
 	.hpd_set_polarity = &r100_hpd_set_polarity,
 	.ioctl_wait_idle = NULL,
 	.gui_idle = &r100_gui_idle,
-	.get_power_state = &r100_get_power_state,
-	.set_power_state = &r100_set_power_state,
 	.pm_misc = &r100_pm_misc,
 	.pm_prepare = &r100_pm_prepare,
 	.pm_finish = &r100_pm_finish,
+	.pm_init_profile = &r100_pm_init_profile,
+	.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
 };
 
 static struct radeon_asic r300_asic_pcie = {
@@ -299,11 +299,11 @@ static struct radeon_asic r300_asic_pcie = {
 	.hpd_set_polarity = &r100_hpd_set_polarity,
 	.ioctl_wait_idle = NULL,
 	.gui_idle = &r100_gui_idle,
-	.get_power_state = &r100_get_power_state,
-	.set_power_state = &r100_set_power_state,
 	.pm_misc = &r100_pm_misc,
 	.pm_prepare = &r100_pm_prepare,
 	.pm_finish = &r100_pm_finish,
+	.pm_init_profile = &r100_pm_init_profile,
+	.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
 };
 
 static struct radeon_asic r420_asic = {
@@ -344,11 +344,11 @@ static struct radeon_asic r420_asic = {
 	.hpd_set_polarity = &r100_hpd_set_polarity,
 	.ioctl_wait_idle = NULL,
 	.gui_idle = &r100_gui_idle,
-	.get_power_state = &r100_get_power_state,
-	.set_power_state = &r100_set_power_state,
 	.pm_misc = &r100_pm_misc,
 	.pm_prepare = &r100_pm_prepare,
 	.pm_finish = &r100_pm_finish,
+	.pm_init_profile = &r420_pm_init_profile,
+	.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
 };
 
 static struct radeon_asic rs400_asic = {
@@ -389,11 +389,11 @@ static struct radeon_asic rs400_asic = {
 	.hpd_set_polarity = &r100_hpd_set_polarity,
 	.ioctl_wait_idle = NULL,
 	.gui_idle = &r100_gui_idle,
-	.get_power_state = &r100_get_power_state,
-	.set_power_state = &r100_set_power_state,
 	.pm_misc = &r100_pm_misc,
 	.pm_prepare = &r100_pm_prepare,
 	.pm_finish = &r100_pm_finish,
+	.pm_init_profile = &r100_pm_init_profile,
+	.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
 };
 
 static struct radeon_asic rs600_asic = {
@@ -434,11 +434,11 @@ static struct radeon_asic rs600_asic = {
 	.hpd_set_polarity = &rs600_hpd_set_polarity,
 	.ioctl_wait_idle = NULL,
 	.gui_idle = &r100_gui_idle,
-	.get_power_state = &r100_get_power_state,
-	.set_power_state = &r100_set_power_state,
 	.pm_misc = &rs600_pm_misc,
 	.pm_prepare = &rs600_pm_prepare,
 	.pm_finish = &rs600_pm_finish,
+	.pm_init_profile = &r420_pm_init_profile,
+	.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
 };
 
 static struct radeon_asic rs690_asic = {
@@ -479,11 +479,11 @@ static struct radeon_asic rs690_asic = {
 	.hpd_set_polarity = &rs600_hpd_set_polarity,
 	.ioctl_wait_idle = NULL,
 	.gui_idle = &r100_gui_idle,
-	.get_power_state = &r100_get_power_state,
-	.set_power_state = &r100_set_power_state,
 	.pm_misc = &rs600_pm_misc,
 	.pm_prepare = &rs600_pm_prepare,
 	.pm_finish = &rs600_pm_finish,
+	.pm_init_profile = &r420_pm_init_profile,
+	.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
 };
 
 static struct radeon_asic rv515_asic = {
@@ -524,11 +524,11 @@ static struct radeon_asic rv515_asic = {
 	.hpd_set_polarity = &rs600_hpd_set_polarity,
 	.ioctl_wait_idle = NULL,
 	.gui_idle = &r100_gui_idle,
-	.get_power_state = &r100_get_power_state,
-	.set_power_state = &r100_set_power_state,
 	.pm_misc = &rs600_pm_misc,
 	.pm_prepare = &rs600_pm_prepare,
 	.pm_finish = &rs600_pm_finish,
+	.pm_init_profile = &r420_pm_init_profile,
+	.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
 };
 
 static struct radeon_asic r520_asic = {
@@ -569,11 +569,11 @@ static struct radeon_asic r520_asic = {
 	.hpd_set_polarity = &rs600_hpd_set_polarity,
 	.ioctl_wait_idle = NULL,
 	.gui_idle = &r100_gui_idle,
-	.get_power_state = &r100_get_power_state,
-	.set_power_state = &r100_set_power_state,
 	.pm_misc = &rs600_pm_misc,
 	.pm_prepare = &rs600_pm_prepare,
 	.pm_finish = &rs600_pm_finish,
+	.pm_init_profile = &r420_pm_init_profile,
+	.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
 };
 
 static struct radeon_asic r600_asic = {
@@ -613,11 +613,11 @@ static struct radeon_asic r600_asic = {
 	.hpd_set_polarity = &r600_hpd_set_polarity,
 	.ioctl_wait_idle = r600_ioctl_wait_idle,
 	.gui_idle = &r600_gui_idle,
-	.get_power_state = &r600_get_power_state,
-	.set_power_state = &r600_set_power_state,
 	.pm_misc = &r600_pm_misc,
 	.pm_prepare = &rs600_pm_prepare,
 	.pm_finish = &rs600_pm_finish,
+	.pm_init_profile = &r600_pm_init_profile,
+	.pm_get_dynpm_state = &r600_pm_get_dynpm_state,
 };
 
 static struct radeon_asic rs780_asic = {
@@ -657,11 +657,11 @@ static struct radeon_asic rs780_asic = {
 	.hpd_set_polarity = &r600_hpd_set_polarity,
 	.ioctl_wait_idle = r600_ioctl_wait_idle,
 	.gui_idle = &r600_gui_idle,
-	.get_power_state = &r600_get_power_state,
-	.set_power_state = &r600_set_power_state,
 	.pm_misc = &r600_pm_misc,
 	.pm_prepare = &rs600_pm_prepare,
 	.pm_finish = &rs600_pm_finish,
+	.pm_init_profile = &rs780_pm_init_profile,
+	.pm_get_dynpm_state = &r600_pm_get_dynpm_state,
 };
 
 static struct radeon_asic rv770_asic = {
@@ -701,11 +701,11 @@ static struct radeon_asic rv770_asic = {
 	.hpd_set_polarity = &r600_hpd_set_polarity,
 	.ioctl_wait_idle = r600_ioctl_wait_idle,
 	.gui_idle = &r600_gui_idle,
-	.get_power_state = &r600_get_power_state,
-	.set_power_state = &r600_set_power_state,
 	.pm_misc = &rv770_pm_misc,
 	.pm_prepare = &rs600_pm_prepare,
 	.pm_finish = &rs600_pm_finish,
+	.pm_init_profile = &r600_pm_init_profile,
+	.pm_get_dynpm_state = &r600_pm_get_dynpm_state,
 };
 
 static struct radeon_asic evergreen_asic = {
@@ -743,11 +743,11 @@ static struct radeon_asic evergreen_asic = {
 	.hpd_sense = &evergreen_hpd_sense,
 	.hpd_set_polarity = &evergreen_hpd_set_polarity,
 	.gui_idle = &r600_gui_idle,
-	.get_power_state = &r600_get_power_state,
-	.set_power_state = &r600_set_power_state,
 	.pm_misc = &evergreen_pm_misc,
 	.pm_prepare = &evergreen_pm_prepare,
 	.pm_finish = &evergreen_pm_finish,
+	.pm_init_profile = &r600_pm_init_profile,
+	.pm_get_dynpm_state = &r600_pm_get_dynpm_state,
 };
 
 int radeon_asic_init(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
index 8a12786..5c40a3d 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.h
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
@@ -127,12 +127,11 @@ void r100_enable_bm(struct radeon_device *rdev);
 void r100_set_common_regs(struct radeon_device *rdev);
 void r100_bm_disable(struct radeon_device *rdev);
 extern bool r100_gui_idle(struct radeon_device *rdev);
-extern void r100_set_power_state(struct radeon_device *rdev, bool static_switch);
-extern void r100_get_power_state(struct radeon_device *rdev,
-				 enum radeon_pm_action action);
 extern void r100_pm_misc(struct radeon_device *rdev);
 extern void r100_pm_prepare(struct radeon_device *rdev);
 extern void r100_pm_finish(struct radeon_device *rdev);
+extern void r100_pm_init_profile(struct radeon_device *rdev);
+extern void r100_pm_get_dynpm_state(struct radeon_device *rdev);
 
 /*
  * r200,rv250,rs300,rv280
@@ -170,6 +169,7 @@ extern int r420_init(struct radeon_device *rdev);
 extern void r420_fini(struct radeon_device *rdev);
 extern int r420_suspend(struct radeon_device *rdev);
 extern int r420_resume(struct radeon_device *rdev);
+extern void r420_pm_init_profile(struct radeon_device *rdev);
 
 /*
  * rs400,rs480
@@ -281,10 +281,10 @@ void r600_hpd_set_polarity(struct radeon_device *rdev,
 			   enum radeon_hpd_id hpd);
 extern void r600_ioctl_wait_idle(struct radeon_device *rdev, struct radeon_bo *bo);
 extern bool r600_gui_idle(struct radeon_device *rdev);
-extern void r600_set_power_state(struct radeon_device *rdev, bool static_switch);
-extern void r600_get_power_state(struct radeon_device *rdev,
-				 enum radeon_pm_action action);
 extern void r600_pm_misc(struct radeon_device *rdev);
+extern void r600_pm_init_profile(struct radeon_device *rdev);
+extern void rs780_pm_init_profile(struct radeon_device *rdev);
+extern void r600_pm_get_dynpm_state(struct radeon_device *rdev);
 
 /*
  * rv770,rv730,rv710,rv740
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index e249da8..a20b612 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -748,6 +748,7 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
 
 	radeon_save_bios_scratch_regs(rdev);
 
+	radeon_pm_suspend(rdev);
 	radeon_suspend(rdev);
 	radeon_hpd_fini(rdev);
 	/* evict remaining vram memory */
@@ -783,6 +784,7 @@ int radeon_resume_kms(struct drm_device *dev)
 	/* resume AGP if in use */
 	radeon_agp_resume(rdev);
 	radeon_resume(rdev);
+	radeon_pm_resume(rdev);
 	radeon_restore_bios_scratch_regs(rdev);
 	radeon_fbdev_set_suspend(rdev, 0);
 	release_console_sem();
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index 25d20bc..bd44f88 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -1030,6 +1030,9 @@ int radeon_modeset_init(struct radeon_device *rdev)
 	/* initialize hpd */
 	radeon_hpd_init(rdev);
 
+	/* Initialize power management */
+	radeon_pm_init(rdev);
+
 	radeon_fbdev_init(rdev);
 	return 0;
 }
@@ -1038,6 +1041,7 @@ void radeon_modeset_fini(struct radeon_device *rdev)
 {
 	radeon_fbdev_fini(rdev);
 	kfree(rdev->mode_info.bios_hardcoded_edid);
+	radeon_pm_fini(rdev);
 
 	if (rdev->mode_info.mode_config_initialized) {
 		radeon_hpd_fini(rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 4b05563..ff7baad 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -91,7 +91,6 @@ int radeon_testing = 0;
 int radeon_connector_table = 0;
 int radeon_tv = 1;
 int radeon_new_pll = -1;
-int radeon_dynpm = -1;
 int radeon_audio = 1;
 int radeon_disp_priority = 0;
 int radeon_hw_i2c = 0;
@@ -132,9 +131,6 @@ module_param_named(tv, radeon_tv, int, 0444);
 MODULE_PARM_DESC(new_pll, "Select new PLL code");
 module_param_named(new_pll, radeon_new_pll, int, 0444);
 
-MODULE_PARM_DESC(dynpm, "Disable/Enable dynamic power management (1 = enable)");
-module_param_named(dynpm, radeon_dynpm, int, 0444);
-
 MODULE_PARM_DESC(audio, "Audio enable (0 = disable)");
 module_param_named(audio, radeon_audio, int, 0444);
 
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index 0dfa508..1827317 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -23,14 +23,98 @@
 #include "drmP.h"
 #include "radeon.h"
 #include "avivod.h"
+#ifdef CONFIG_ACPI
+#include <linux/acpi.h>
+#endif
+#include <linux/power_supply.h>
 
 #define RADEON_IDLE_LOOP_MS 100
 #define RADEON_RECLOCK_DELAY_MS 200
 #define RADEON_WAIT_VBLANK_TIMEOUT 200
 #define RADEON_WAIT_IDLE_TIMEOUT 200
 
-static void radeon_pm_idle_work_handler(struct work_struct *work);
+static void radeon_dynpm_idle_work_handler(struct work_struct *work);
 static int radeon_debugfs_pm_init(struct radeon_device *rdev);
+static bool radeon_pm_in_vbl(struct radeon_device *rdev);
+static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish);
+static void radeon_pm_update_profile(struct radeon_device *rdev);
+static void radeon_pm_set_clocks(struct radeon_device *rdev);
+
+#define ACPI_AC_CLASS           "ac_adapter"
+
+#ifdef CONFIG_ACPI
+static int radeon_acpi_event(struct notifier_block *nb,
+			     unsigned long val,
+			     void *data)
+{
+	struct radeon_device *rdev = container_of(nb, struct radeon_device, acpi_nb);
+	struct acpi_bus_event *entry = (struct acpi_bus_event *)data;
+
+	if (strcmp(entry->device_class, ACPI_AC_CLASS) == 0) {
+		if (power_supply_is_system_supplied() > 0)
+			DRM_INFO("pm: AC\n");
+		else
+			DRM_INFO("pm: DC\n");
+
+		if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
+			if (rdev->pm.profile == PM_PROFILE_AUTO) {
+				mutex_lock(&rdev->pm.mutex);
+				radeon_pm_update_profile(rdev);
+				radeon_pm_set_clocks(rdev);
+				mutex_unlock(&rdev->pm.mutex);
+			}
+		}
+	}
+
+	return NOTIFY_OK;
+}
+#endif
+
+static void radeon_pm_update_profile(struct radeon_device *rdev)
+{
+	switch (rdev->pm.profile) {
+	case PM_PROFILE_DEFAULT:
+		rdev->pm.profile_index = PM_PROFILE_DEFAULT_IDX;
+		break;
+	case PM_PROFILE_AUTO:
+		if (power_supply_is_system_supplied() > 0) {
+			if (rdev->pm.active_crtc_count > 1)
+				rdev->pm.profile_index = PM_PROFILE_HIGH_MH_IDX;
+			else
+				rdev->pm.profile_index = PM_PROFILE_HIGH_SH_IDX;
+		} else {
+			if (rdev->pm.active_crtc_count > 1)
+				rdev->pm.profile_index = PM_PROFILE_LOW_MH_IDX;
+			else
+				rdev->pm.profile_index = PM_PROFILE_LOW_SH_IDX;
+		}
+		break;
+	case PM_PROFILE_LOW:
+		if (rdev->pm.active_crtc_count > 1)
+			rdev->pm.profile_index = PM_PROFILE_LOW_MH_IDX;
+		else
+			rdev->pm.profile_index = PM_PROFILE_LOW_SH_IDX;
+		break;
+	case PM_PROFILE_HIGH:
+		if (rdev->pm.active_crtc_count > 1)
+			rdev->pm.profile_index = PM_PROFILE_HIGH_MH_IDX;
+		else
+			rdev->pm.profile_index = PM_PROFILE_HIGH_SH_IDX;
+		break;
+	}
+
+	if (rdev->pm.active_crtc_count == 0) {
+		rdev->pm.requested_power_state_index =
+			rdev->pm.profiles[rdev->pm.profile_index].dpms_off_ps_idx;
+		rdev->pm.requested_clock_mode_index =
+			rdev->pm.profiles[rdev->pm.profile_index].dpms_off_cm_idx;
+	} else {
+		rdev->pm.requested_power_state_index =
+			rdev->pm.profiles[rdev->pm.profile_index].dpms_on_ps_idx;
+		rdev->pm.requested_clock_mode_index =
+			rdev->pm.profiles[rdev->pm.profile_index].dpms_on_cm_idx;
+	}
+}
 
 static void radeon_unmap_vram_bos(struct radeon_device *rdev)
 {
@@ -54,12 +138,93 @@ static void radeon_unmap_vram_bos(struct radeon_device *rdev)
 		ttm_bo_unmap_virtual(&rdev->r600_blit.shader_obj->tbo);
 }
 
-static void radeon_pm_set_clocks(struct radeon_device *rdev, int static_switch)
+static void radeon_sync_with_vblank(struct radeon_device *rdev)
 {
-	int i;
+	if (rdev->pm.active_crtcs) {
+		rdev->pm.vblank_sync = false;
+		wait_event_timeout(
+			rdev->irq.vblank_queue, rdev->pm.vblank_sync,
+			msecs_to_jiffies(RADEON_WAIT_VBLANK_TIMEOUT));
+	}
+}
+
+static void radeon_set_power_state(struct radeon_device *rdev)
+{
+	u32 sclk, mclk;
+
+	if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) &&
+	    (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index))
+		return;
+
+	if (radeon_gui_idle(rdev)) {
+		sclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
+			clock_info[rdev->pm.requested_clock_mode_index].sclk;
+		if (sclk > rdev->clock.default_sclk)
+			sclk = rdev->clock.default_sclk;
+
+		mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
+			clock_info[rdev->pm.requested_clock_mode_index].mclk;
+		if (mclk > rdev->clock.default_mclk)
+			mclk = rdev->clock.default_mclk;
+
+		/* voltage, pcie lanes, etc.*/
+		radeon_pm_misc(rdev);
+
+		if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
+			radeon_sync_with_vblank(rdev);
+
+			if (!radeon_pm_in_vbl(rdev))
+				return;
+
+			radeon_pm_prepare(rdev);
+			/* set engine clock */
+			if (sclk != rdev->pm.current_sclk) {
+				radeon_pm_debug_check_in_vbl(rdev, false);
+				radeon_set_engine_clock(rdev, sclk);
+				radeon_pm_debug_check_in_vbl(rdev, true);
+				rdev->pm.current_sclk = sclk;
+				DRM_INFO("Setting: e: %d\n", sclk);
+			}
+
+			/* set memory clock */
+			if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) {
+				radeon_pm_debug_check_in_vbl(rdev, false);
+				radeon_set_memory_clock(rdev, mclk);
+				radeon_pm_debug_check_in_vbl(rdev, true);
+				rdev->pm.current_mclk = mclk;
+				DRM_INFO("Setting: m: %d\n", mclk);
+			}
+			radeon_pm_finish(rdev);
+		} else {
+			/* set engine clock */
+			if (sclk != rdev->pm.current_sclk) {
+				radeon_sync_with_vblank(rdev);
+				radeon_pm_prepare(rdev);
+				radeon_set_engine_clock(rdev, sclk);
+				radeon_pm_finish(rdev);
+				rdev->pm.current_sclk = sclk;
+				DRM_INFO("Setting: e: %d\n", sclk);
+			}
+			/* set memory clock */
+			if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) {
+				radeon_sync_with_vblank(rdev);
+				radeon_pm_prepare(rdev);
+				radeon_set_memory_clock(rdev, mclk);
+				radeon_pm_finish(rdev);
+				rdev->pm.current_mclk = mclk;
+				DRM_INFO("Setting: m: %d\n", mclk);
+			}
+		}
 
-	if (rdev->pm.state != PM_STATE_DISABLED)
-		radeon_get_power_state(rdev, rdev->pm.planned_action);
+		rdev->pm.current_power_state_index = rdev->pm.requested_power_state_index;
+		rdev->pm.current_clock_mode_index = rdev->pm.requested_clock_mode_index;
+	} else
+		DRM_INFO("pm: GUI not idle!!!\n");
+}
+
+static void radeon_pm_set_clocks(struct radeon_device *rdev)
+{
+	int i;
 
 	mutex_lock(&rdev->ddev->struct_mutex);
 	mutex_lock(&rdev->vram_mutex);
@@ -67,27 +232,31 @@ static void radeon_pm_set_clocks(struct radeon_device *rdev, int static_switch)
 
 	/* gui idle int has issues on older chips it seems */
 	if (rdev->family >= CHIP_R600) {
-		/* wait for GPU idle */
-		rdev->pm.gui_idle = false;
-		rdev->irq.gui_idle = true;
-		radeon_irq_set(rdev);
-		wait_event_interruptible_timeout(
-			rdev->irq.idle_queue, rdev->pm.gui_idle,
-			msecs_to_jiffies(RADEON_WAIT_IDLE_TIMEOUT));
-		rdev->irq.gui_idle = false;
-		radeon_irq_set(rdev);
+		if (rdev->irq.installed) {
+			/* wait for GPU idle */
+			rdev->pm.gui_idle = false;
+			rdev->irq.gui_idle = true;
+			radeon_irq_set(rdev);
+			wait_event_interruptible_timeout(
+				rdev->irq.idle_queue, rdev->pm.gui_idle,
+				msecs_to_jiffies(RADEON_WAIT_IDLE_TIMEOUT));
+			rdev->irq.gui_idle = false;
+			radeon_irq_set(rdev);
+		}
 	} else {
-		struct radeon_fence *fence;
-		radeon_ring_alloc(rdev, 64);
-		radeon_fence_create(rdev, &fence);
-		radeon_fence_emit(rdev, fence);
-		radeon_ring_commit(rdev);
-		radeon_fence_wait(fence, false);
-		radeon_fence_unref(&fence);
+		if (rdev->cp.ready) {
+			struct radeon_fence *fence;
+			radeon_ring_alloc(rdev, 64);
+			radeon_fence_create(rdev, &fence);
+			radeon_fence_emit(rdev, fence);
+			radeon_ring_commit(rdev);
+			radeon_fence_wait(fence, false);
+			radeon_fence_unref(&fence);
+		}
 	}
 	radeon_unmap_vram_bos(rdev);
 
-	if (!static_switch) {
+	if (rdev->irq.installed) {
 		for (i = 0; i < rdev->num_crtc; i++) {
 			if (rdev->pm.active_crtcs & (1 << i)) {
 				rdev->pm.req_vblank |= (1 << i);
@@ -96,9 +265,9 @@ static void radeon_pm_set_clocks(struct radeon_device *rdev, int static_switch)
 		}
 	}
 
-	radeon_set_power_state(rdev, static_switch);
+	radeon_set_power_state(rdev);
 
-	if (!static_switch) {
+	if (rdev->irq.installed) {
 		for (i = 0; i < rdev->num_crtc; i++) {
 			if (rdev->pm.req_vblank & (1 << i)) {
 				rdev->pm.req_vblank &= ~(1 << i);
@@ -112,230 +281,195 @@ static void radeon_pm_set_clocks(struct radeon_device *rdev, int static_switch)
 	if (rdev->pm.active_crtc_count)
 		radeon_bandwidth_update(rdev);
 
-	rdev->pm.planned_action = PM_ACTION_NONE;
+	rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
 
 	mutex_unlock(&rdev->cp.mutex);
 	mutex_unlock(&rdev->vram_mutex);
 	mutex_unlock(&rdev->ddev->struct_mutex);
 }
 
-static ssize_t radeon_get_power_state_static(struct device *dev,
-					     struct device_attribute *attr,
-					     char *buf)
+static ssize_t radeon_get_pm_profile(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;
+	int cp = rdev->pm.profile;
 
-	return snprintf(buf, PAGE_SIZE, "%d.%d\n", rdev->pm.current_power_state_index,
-			rdev->pm.current_clock_mode_index);
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			(cp == PM_PROFILE_AUTO) ? "auto" :
+			(cp == PM_PROFILE_LOW) ? "low" :
+			(cp == PM_PROFILE_HIGH) ? "high" : "default");
 }
 
-static ssize_t radeon_set_power_state_static(struct device *dev,
-					     struct device_attribute *attr,
-					     const char *buf,
-					     size_t count)
+static ssize_t radeon_set_pm_profile(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;
-	int ps, cm;
-
-	if (sscanf(buf, "%u.%u", &ps, &cm) != 2) {
-		DRM_ERROR("Invalid power state!\n");
-		return count;
-	}
 
 	mutex_lock(&rdev->pm.mutex);
-	if ((ps >= 0) && (ps < rdev->pm.num_power_states) &&
-	    (cm >= 0) && (cm < rdev->pm.power_state[ps].num_clock_modes)) {
-		if ((rdev->pm.active_crtc_count > 0) &&
-		    (rdev->pm.power_state[ps].clock_info[cm].flags & RADEON_PM_MODE_NO_DISPLAY)) {
-			DRM_ERROR("Invalid power state for display: %d.%d\n", ps, cm);
-		} else if ((rdev->pm.active_crtc_count > 1) &&
-			   (rdev->pm.power_state[ps].flags & RADEON_PM_STATE_SINGLE_DISPLAY_ONLY)) {
-			DRM_ERROR("Invalid power state for multi-head: %d.%d\n", ps, cm);
-		} else {
-			/* disable dynpm */
-			rdev->pm.state = PM_STATE_DISABLED;
-			rdev->pm.planned_action = PM_ACTION_NONE;
-			rdev->pm.requested_power_state_index = ps;
-			rdev->pm.requested_clock_mode_index = cm;
-			radeon_pm_set_clocks(rdev, true);
+	if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
+		if (strncmp("default", buf, strlen("default")) == 0)
+			rdev->pm.profile = PM_PROFILE_DEFAULT;
+		else if (strncmp("auto", buf, strlen("auto")) == 0)
+			rdev->pm.profile = PM_PROFILE_AUTO;
+		else if (strncmp("low", buf, strlen("low")) == 0)
+			rdev->pm.profile = PM_PROFILE_LOW;
+		else if (strncmp("high", buf, strlen("high")) == 0)
+			rdev->pm.profile = PM_PROFILE_HIGH;
+		else {
+			DRM_ERROR("invalid power profile!\n");
+			goto fail;
 		}
-	} else
-		DRM_ERROR("Invalid power state: %d.%d\n\n", ps, cm);
+		radeon_pm_update_profile(rdev);
+		radeon_pm_set_clocks(rdev);
+	}
+fail:
 	mutex_unlock(&rdev->pm.mutex);
 
 	return count;
 }
 
-static ssize_t radeon_get_dynpm(struct device *dev,
-				struct device_attribute *attr,
-				char *buf)
+static ssize_t radeon_get_pm_method(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;
+	int pm = rdev->pm.pm_method;
 
 	return snprintf(buf, PAGE_SIZE, "%s\n",
-			(rdev->pm.state == PM_STATE_DISABLED) ? "disabled" : "enabled");
+			(pm == PM_METHOD_DYNPM) ? "dynpm" : "profile");
 }
 
-static ssize_t radeon_set_dynpm(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf,
-				size_t count)
+static ssize_t radeon_set_pm_method(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;
-	int tmp = simple_strtoul(buf, NULL, 10);
 
-	if (tmp == 0) {
-		/* update power mode info */
-		radeon_pm_compute_clocks(rdev);
-		/* disable dynpm */
+
+	if (strncmp("dynpm", buf, strlen("dynpm")) == 0) {
 		mutex_lock(&rdev->pm.mutex);
-		rdev->pm.state = PM_STATE_DISABLED;
-		rdev->pm.planned_action = PM_ACTION_NONE;
+		rdev->pm.pm_method = PM_METHOD_DYNPM;
+		rdev->pm.dynpm_state = DYNPM_STATE_PAUSED;
+		rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT;
 		mutex_unlock(&rdev->pm.mutex);
-		DRM_INFO("radeon: dynamic power management disabled\n");
-	} else if (tmp == 1) {
-		if (rdev->pm.num_power_states > 1) {
-			/* enable dynpm */
-			mutex_lock(&rdev->pm.mutex);
-			rdev->pm.state = PM_STATE_PAUSED;
-			rdev->pm.planned_action = PM_ACTION_DEFAULT;
-			radeon_get_power_state(rdev, rdev->pm.planned_action);
-			mutex_unlock(&rdev->pm.mutex);
-			/* update power mode info */
-			radeon_pm_compute_clocks(rdev);
-			DRM_INFO("radeon: dynamic power management enabled\n");
-		} else
-			DRM_ERROR("dynpm not valid on this system\n");
-	} else
-		DRM_ERROR("Invalid setting: %d\n", tmp);
-
+	} else if (strncmp("profile", buf, strlen("profile")) == 0) {
+		mutex_lock(&rdev->pm.mutex);
+		rdev->pm.pm_method = PM_METHOD_PROFILE;
+		/* disable dynpm */
+		rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
+		rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
+		cancel_delayed_work(&rdev->pm.dynpm_idle_work);
+		mutex_unlock(&rdev->pm.mutex);
+	} else {
+		DRM_ERROR("invalid power method!\n");
+		goto fail;
+	}
+	radeon_pm_compute_clocks(rdev);
+fail:
 	return count;
 }
 
-static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, radeon_get_power_state_static, radeon_set_power_state_static);
-static DEVICE_ATTR(dynpm, S_IRUGO | S_IWUSR, radeon_get_dynpm, radeon_set_dynpm);
-
+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 const char *pm_state_names[4] = {
-	"PM_STATE_DISABLED",
-	"PM_STATE_MINIMUM",
-	"PM_STATE_PAUSED",
-	"PM_STATE_ACTIVE"
-};
-
-static const char *pm_state_types[5] = {
-	"",
-	"Powersave",
-	"Battery",
-	"Balanced",
-	"Performance",
-};
-
-static void radeon_print_power_mode_info(struct radeon_device *rdev)
+void radeon_pm_suspend(struct radeon_device *rdev)
 {
-	int i, j;
-	bool is_default;
-
-	DRM_INFO("%d Power State(s)\n", rdev->pm.num_power_states);
-	for (i = 0; i < rdev->pm.num_power_states; i++) {
-		if (rdev->pm.default_power_state_index == i)
-			is_default = true;
-		else
-			is_default = false;
-		DRM_INFO("State %d %s %s\n", i,
-			 pm_state_types[rdev->pm.power_state[i].type],
-			 is_default ? "(default)" : "");
-		if ((rdev->flags & RADEON_IS_PCIE) && !(rdev->flags & RADEON_IS_IGP))
-			DRM_INFO("\t%d PCIE Lanes\n", rdev->pm.power_state[i].pcie_lanes);
-		if (rdev->pm.power_state[i].flags & RADEON_PM_STATE_SINGLE_DISPLAY_ONLY)
-			DRM_INFO("\tSingle display only\n");
-		DRM_INFO("\t%d Clock Mode(s)\n", rdev->pm.power_state[i].num_clock_modes);
-		for (j = 0; j < rdev->pm.power_state[i].num_clock_modes; j++) {
-			if (rdev->flags & RADEON_IS_IGP)
-				DRM_INFO("\t\t%d engine: %d\n",
-					 j,
-					 rdev->pm.power_state[i].clock_info[j].sclk * 10);
-			else
-				DRM_INFO("\t\t%d engine/memory: %d/%d\n",
-					 j,
-					 rdev->pm.power_state[i].clock_info[j].sclk * 10,
-					 rdev->pm.power_state[i].clock_info[j].mclk * 10);
-			if (rdev->pm.power_state[i].clock_info[j].flags & RADEON_PM_MODE_NO_DISPLAY)
-				DRM_INFO("\t\tNo display only\n");
-		}
-	}
+	mutex_lock(&rdev->pm.mutex);
+	cancel_delayed_work(&rdev->pm.dynpm_idle_work);
+	rdev->pm.current_power_state_index = -1;
+	rdev->pm.current_clock_mode_index = -1;
+	rdev->pm.current_sclk = 0;
+	rdev->pm.current_mclk = 0;
+	mutex_unlock(&rdev->pm.mutex);
 }
 
-void radeon_sync_with_vblank(struct radeon_device *rdev)
+void radeon_pm_resume(struct radeon_device *rdev)
 {
-	if (rdev->pm.active_crtcs) {
-		rdev->pm.vblank_sync = false;
-		wait_event_timeout(
-			rdev->irq.vblank_queue, rdev->pm.vblank_sync,
-			msecs_to_jiffies(RADEON_WAIT_VBLANK_TIMEOUT));
-	}
+	radeon_pm_compute_clocks(rdev);
 }
 
 int radeon_pm_init(struct radeon_device *rdev)
 {
-	rdev->pm.state = PM_STATE_DISABLED;
-	rdev->pm.planned_action = PM_ACTION_NONE;
-	rdev->pm.can_upclock = true;
-	rdev->pm.can_downclock = true;
+	/* default to profile method */
+	rdev->pm.pm_method = PM_METHOD_PROFILE;
+	rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
+	rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
+	rdev->pm.dynpm_can_upclock = true;
+	rdev->pm.dynpm_can_downclock = true;
+	rdev->pm.current_sclk = 0;
+	rdev->pm.current_mclk = 0;
 
 	if (rdev->bios) {
 		if (rdev->is_atom_bios)
 			radeon_atombios_get_power_modes(rdev);
 		else
 			radeon_combios_get_power_modes(rdev);
-		radeon_print_power_mode_info(rdev);
+		radeon_pm_init_profile(rdev);
+		rdev->pm.current_power_state_index = -1;
+		rdev->pm.current_clock_mode_index = -1;
 	}
 
-	if (radeon_debugfs_pm_init(rdev)) {
-		DRM_ERROR("Failed to register debugfs file for PM!\n");
-	}
+	if (rdev->pm.num_power_states > 1) {
+		if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
+			mutex_lock(&rdev->pm.mutex);
+			rdev->pm.profile = PM_PROFILE_DEFAULT;
+			radeon_pm_update_profile(rdev);
+			radeon_pm_set_clocks(rdev);
+			mutex_unlock(&rdev->pm.mutex);
+		}
 
-	/* where's the best place to put this? */
-	device_create_file(rdev->dev, &dev_attr_power_state);
-	device_create_file(rdev->dev, &dev_attr_dynpm);
+		/* where's the best place to put these? */
+		device_create_file(rdev->dev, &dev_attr_power_profile);
+		device_create_file(rdev->dev, &dev_attr_power_method);
 
-	INIT_DELAYED_WORK(&rdev->pm.idle_work, radeon_pm_idle_work_handler);
+#ifdef CONFIG_ACPI
+		rdev->acpi_nb.notifier_call = radeon_acpi_event;
+		register_acpi_notifier(&rdev->acpi_nb);
+#endif
+		INIT_DELAYED_WORK(&rdev->pm.dynpm_idle_work, radeon_dynpm_idle_work_handler);
 
-	if ((radeon_dynpm != -1 && radeon_dynpm) && (rdev->pm.num_power_states > 1)) {
-		rdev->pm.state = PM_STATE_PAUSED;
-		DRM_INFO("radeon: dynamic power management enabled\n");
-	}
+		if (radeon_debugfs_pm_init(rdev)) {
+			DRM_ERROR("Failed to register debugfs file for PM!\n");
+		}
 
-	DRM_INFO("radeon: power management initialized\n");
+		DRM_INFO("radeon: power management initialized\n");
+	}
 
 	return 0;
 }
 
 void radeon_pm_fini(struct radeon_device *rdev)
 {
-	if (rdev->pm.state != PM_STATE_DISABLED) {
-		/* cancel work */
-		cancel_delayed_work_sync(&rdev->pm.idle_work);
-		/* reset default clocks */
-		rdev->pm.state = PM_STATE_DISABLED;
-		rdev->pm.planned_action = PM_ACTION_DEFAULT;
-		radeon_pm_set_clocks(rdev, true);
-	} else if ((rdev->pm.current_power_state_index !=
-		    rdev->pm.default_power_state_index) ||
-		   (rdev->pm.current_clock_mode_index != 0)) {
-		rdev->pm.requested_power_state_index = rdev->pm.default_power_state_index;
-		rdev->pm.requested_clock_mode_index = 0;
+	if (rdev->pm.num_power_states > 1) {
 		mutex_lock(&rdev->pm.mutex);
-		radeon_pm_set_clocks(rdev, true);
+		if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
+			rdev->pm.profile = PM_PROFILE_DEFAULT;
+			radeon_pm_update_profile(rdev);
+			radeon_pm_set_clocks(rdev);
+		} else if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
+			/* cancel work */
+			cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work);
+			/* reset default clocks */
+			rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
+			rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT;
+			radeon_pm_set_clocks(rdev);
+		}
 		mutex_unlock(&rdev->pm.mutex);
-	}
 
-	device_remove_file(rdev->dev, &dev_attr_power_state);
-	device_remove_file(rdev->dev, &dev_attr_dynpm);
+		device_remove_file(rdev->dev, &dev_attr_power_profile);
+		device_remove_file(rdev->dev, &dev_attr_power_method);
+#ifdef CONFIG_ACPI
+		unregister_acpi_notifier(&rdev->acpi_nb);
+#endif
+	}
 
 	if (rdev->pm.i2c_bus)
 		radeon_i2c_destroy(rdev->pm.i2c_bus);
@@ -347,6 +481,9 @@ void radeon_pm_compute_clocks(struct radeon_device *rdev)
 	struct drm_crtc *crtc;
 	struct radeon_crtc *radeon_crtc;
 
+	if (rdev->pm.num_power_states < 2)
+		return;
+
 	mutex_lock(&rdev->pm.mutex);
 
 	rdev->pm.active_crtcs = 0;
@@ -360,55 +497,56 @@ void radeon_pm_compute_clocks(struct radeon_device *rdev)
 		}
 	}
 
-	if (rdev->pm.state == PM_STATE_DISABLED) {
-		mutex_unlock(&rdev->pm.mutex);
-		return;
-	}
-
-	/* Note, radeon_pm_set_clocks is called with static_switch set
-	 * to true since we always want to statically set the clocks,
-	 * not wait for vbl.
-	 */
-	if (rdev->pm.active_crtc_count > 1) {
-		if (rdev->pm.state == PM_STATE_ACTIVE) {
-			cancel_delayed_work(&rdev->pm.idle_work);
-
-			rdev->pm.state = PM_STATE_PAUSED;
-			rdev->pm.planned_action = PM_ACTION_DEFAULT;
-			radeon_pm_set_clocks(rdev, true);
-
-			DRM_DEBUG("radeon: dynamic power management deactivated\n");
-		}
-	} else if (rdev->pm.active_crtc_count == 1) {
-		/* TODO: Increase clocks if needed for current mode */
-
-		if (rdev->pm.state == PM_STATE_MINIMUM) {
-			rdev->pm.state = PM_STATE_ACTIVE;
-			rdev->pm.planned_action = PM_ACTION_UPCLOCK;
-			radeon_pm_set_clocks(rdev, true);
-
-			queue_delayed_work(rdev->wq, &rdev->pm.idle_work,
-				msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
-		} else if (rdev->pm.state == PM_STATE_PAUSED) {
-			rdev->pm.state = PM_STATE_ACTIVE;
-			queue_delayed_work(rdev->wq, &rdev->pm.idle_work,
-				msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
-			DRM_DEBUG("radeon: dynamic power management activated\n");
-		}
-	} else { /* count == 0 */
-		if (rdev->pm.state != PM_STATE_MINIMUM) {
-			cancel_delayed_work(&rdev->pm.idle_work);
-
-			rdev->pm.state = PM_STATE_MINIMUM;
-			rdev->pm.planned_action = PM_ACTION_MINIMUM;
-			radeon_pm_set_clocks(rdev, true);
+	if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
+		radeon_pm_update_profile(rdev);
+		radeon_pm_set_clocks(rdev);
+	} else if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
+		if (rdev->pm.dynpm_state != DYNPM_STATE_DISABLED) {
+			if (rdev->pm.active_crtc_count > 1) {
+				if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) {
+					cancel_delayed_work(&rdev->pm.dynpm_idle_work);
+
+					rdev->pm.dynpm_state = DYNPM_STATE_PAUSED;
+					rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT;
+					radeon_pm_get_dynpm_state(rdev);
+					radeon_pm_set_clocks(rdev);
+
+					DRM_DEBUG("radeon: dynamic power management deactivated\n");
+				}
+			} else if (rdev->pm.active_crtc_count == 1) {
+				/* TODO: Increase clocks if needed for current mode */
+
+				if (rdev->pm.dynpm_state == DYNPM_STATE_MINIMUM) {
+					rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
+					rdev->pm.dynpm_planned_action = DYNPM_ACTION_UPCLOCK;
+					radeon_pm_get_dynpm_state(rdev);
+					radeon_pm_set_clocks(rdev);
+
+					queue_delayed_work(rdev->wq, &rdev->pm.dynpm_idle_work,
+							   msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
+				} else if (rdev->pm.dynpm_state == DYNPM_STATE_PAUSED) {
+					rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
+					queue_delayed_work(rdev->wq, &rdev->pm.dynpm_idle_work,
+							   msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
+					DRM_DEBUG("radeon: dynamic power management activated\n");
+				}
+			} else { /* count == 0 */
+				if (rdev->pm.dynpm_state != DYNPM_STATE_MINIMUM) {
+					cancel_delayed_work(&rdev->pm.dynpm_idle_work);
+
+					rdev->pm.dynpm_state = DYNPM_STATE_MINIMUM;
+					rdev->pm.dynpm_planned_action = DYNPM_ACTION_MINIMUM;
+					radeon_pm_get_dynpm_state(rdev);
+					radeon_pm_set_clocks(rdev);
+				}
+			}
 		}
 	}
 
 	mutex_unlock(&rdev->pm.mutex);
 }
 
-bool radeon_pm_in_vbl(struct radeon_device *rdev)
+static bool radeon_pm_in_vbl(struct radeon_device *rdev)
 {
 	u32 stat_crtc = 0, vbl = 0, position = 0;
 	bool in_vbl = true;
@@ -480,7 +618,7 @@ bool radeon_pm_in_vbl(struct radeon_device *rdev)
 	return in_vbl;
 }
 
-bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish)
+static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish)
 {
 	u32 stat_crtc = 0;
 	bool in_vbl = radeon_pm_in_vbl(rdev);
@@ -491,16 +629,16 @@ bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish)
 	return in_vbl;
 }
 
-static void radeon_pm_idle_work_handler(struct work_struct *work)
+static void radeon_dynpm_idle_work_handler(struct work_struct *work)
 {
 	struct radeon_device *rdev;
 	int resched;
 	rdev = container_of(work, struct radeon_device,
-				pm.idle_work.work);
+				pm.dynpm_idle_work.work);
 
 	resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev);
 	mutex_lock(&rdev->pm.mutex);
-	if (rdev->pm.state == PM_STATE_ACTIVE) {
+	if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) {
 		unsigned long irq_flags;
 		int not_processed = 0;
 
@@ -516,23 +654,23 @@ static void radeon_pm_idle_work_handler(struct work_struct *work)
 		read_unlock_irqrestore(&rdev->fence_drv.lock, irq_flags);
 
 		if (not_processed >= 3) { /* should upclock */
-			if (rdev->pm.planned_action == PM_ACTION_DOWNCLOCK) {
-				rdev->pm.planned_action = PM_ACTION_NONE;
-			} else if (rdev->pm.planned_action == PM_ACTION_NONE &&
-				   rdev->pm.can_upclock) {
-				rdev->pm.planned_action =
-					PM_ACTION_UPCLOCK;
-				rdev->pm.action_timeout = jiffies +
+			if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_DOWNCLOCK) {
+				rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
+			} else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE &&
+				   rdev->pm.dynpm_can_upclock) {
+				rdev->pm.dynpm_planned_action =
+					DYNPM_ACTION_UPCLOCK;
+				rdev->pm.dynpm_action_timeout = jiffies +
 				msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS);
 			}
 		} else if (not_processed == 0) { /* should downclock */
-			if (rdev->pm.planned_action == PM_ACTION_UPCLOCK) {
-				rdev->pm.planned_action = PM_ACTION_NONE;
-			} else if (rdev->pm.planned_action == PM_ACTION_NONE &&
-				   rdev->pm.can_downclock) {
-				rdev->pm.planned_action =
-					PM_ACTION_DOWNCLOCK;
-				rdev->pm.action_timeout = jiffies +
+			if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_UPCLOCK) {
+				rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
+			} else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE &&
+				   rdev->pm.dynpm_can_downclock) {
+				rdev->pm.dynpm_planned_action =
+					DYNPM_ACTION_DOWNCLOCK;
+				rdev->pm.dynpm_action_timeout = jiffies +
 				msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS);
 			}
 		}
@@ -540,15 +678,16 @@ static void radeon_pm_idle_work_handler(struct work_struct *work)
 		/* Note, radeon_pm_set_clocks is called with static_switch set
 		 * to false since we want to wait for vbl to avoid flicker.
 		 */
-		if (rdev->pm.planned_action != PM_ACTION_NONE &&
-		    jiffies > rdev->pm.action_timeout) {
-			radeon_pm_set_clocks(rdev, false);
+		if (rdev->pm.dynpm_planned_action != DYNPM_ACTION_NONE &&
+		    jiffies > rdev->pm.dynpm_action_timeout) {
+			radeon_pm_get_dynpm_state(rdev);
+			radeon_pm_set_clocks(rdev);
 		}
 	}
 	mutex_unlock(&rdev->pm.mutex);
 	ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched);
 
-	queue_delayed_work(rdev->wq, &rdev->pm.idle_work,
+	queue_delayed_work(rdev->wq, &rdev->pm.dynpm_idle_work,
 					msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
 }
 
@@ -563,7 +702,6 @@ static int radeon_debugfs_pm_info(struct seq_file *m, void *data)
 	struct drm_device *dev = node->minor->dev;
 	struct radeon_device *rdev = dev->dev_private;
 
-	seq_printf(m, "state: %s\n", pm_state_names[rdev->pm.state]);
 	seq_printf(m, "default engine clock: %u0 kHz\n", rdev->clock.default_sclk);
 	seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev));
 	seq_printf(m, "default memory clock: %u0 kHz\n", rdev->clock.default_mclk);
diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c
index dc76fe7..9e4240b 100644
--- a/drivers/gpu/drm/radeon/rs400.c
+++ b/drivers/gpu/drm/radeon/rs400.c
@@ -456,7 +456,6 @@ int rs400_suspend(struct radeon_device *rdev)
 
 void rs400_fini(struct radeon_device *rdev)
 {
-	radeon_pm_fini(rdev);
 	r100_cp_fini(rdev);
 	r100_wb_fini(rdev);
 	r100_ib_fini(rdev);
@@ -507,8 +506,6 @@ int rs400_init(struct radeon_device *rdev)
 
 	/* Initialize clocks */
 	radeon_get_clock_info(rdev->ddev);
-	/* Initialize power management */
-	radeon_pm_init(rdev);
 	/* initialize memory controller */
 	rs400_mc_init(rdev);
 	/* Fence driver */
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
index 8e0c460..e8c68e9 100644
--- a/drivers/gpu/drm/radeon/rs600.c
+++ b/drivers/gpu/drm/radeon/rs600.c
@@ -846,7 +846,6 @@ int rs600_suspend(struct radeon_device *rdev)
 
 void rs600_fini(struct radeon_device *rdev)
 {
-	radeon_pm_fini(rdev);
 	r100_cp_fini(rdev);
 	r100_wb_fini(rdev);
 	r100_ib_fini(rdev);
@@ -896,8 +895,6 @@ int rs600_init(struct radeon_device *rdev)
 
 	/* Initialize clocks */
 	radeon_get_clock_info(rdev->ddev);
-	/* Initialize power management */
-	radeon_pm_init(rdev);
 	/* initialize memory controller */
 	rs600_mc_init(rdev);
 	rs600_debugfs(rdev);
diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c
index e8edfe6..bcc3319 100644
--- a/drivers/gpu/drm/radeon/rs690.c
+++ b/drivers/gpu/drm/radeon/rs690.c
@@ -676,7 +676,6 @@ int rs690_suspend(struct radeon_device *rdev)
 
 void rs690_fini(struct radeon_device *rdev)
 {
-	radeon_pm_fini(rdev);
 	r100_cp_fini(rdev);
 	r100_wb_fini(rdev);
 	r100_ib_fini(rdev);
@@ -727,8 +726,6 @@ int rs690_init(struct radeon_device *rdev)
 
 	/* Initialize clocks */
 	radeon_get_clock_info(rdev->ddev);
-	/* Initialize power management */
-	radeon_pm_init(rdev);
 	/* initialize memory controller */
 	rs690_mc_init(rdev);
 	rv515_debugfs(rdev);
diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c
index 2009f4b..7d9a7b0 100644
--- a/drivers/gpu/drm/radeon/rv515.c
+++ b/drivers/gpu/drm/radeon/rv515.c
@@ -445,7 +445,6 @@ void rv515_set_safe_registers(struct radeon_device *rdev)
 
 void rv515_fini(struct radeon_device *rdev)
 {
-	radeon_pm_fini(rdev);
 	r100_cp_fini(rdev);
 	r100_wb_fini(rdev);
 	r100_ib_fini(rdev);
@@ -494,8 +493,6 @@ int rv515_init(struct radeon_device *rdev)
 		return -EINVAL;
 	/* Initialize clocks */
 	radeon_get_clock_info(rdev->ddev);
-	/* Initialize power management */
-	radeon_pm_init(rdev);
 	/* initialize AGP */
 	if (rdev->flags & RADEON_IS_AGP) {
 		r = radeon_agp_init(rdev);
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c
index 7c55182..253f24a 100644
--- a/drivers/gpu/drm/radeon/rv770.c
+++ b/drivers/gpu/drm/radeon/rv770.c
@@ -1091,8 +1091,6 @@ int rv770_init(struct radeon_device *rdev)
 	r = radeon_clocks_init(rdev);
 	if (r)
 		return r;
-	/* Initialize power management */
-	radeon_pm_init(rdev);
 	/* Fence driver */
 	r = radeon_fence_driver_init(rdev);
 	if (r)
@@ -1161,7 +1159,6 @@ int rv770_init(struct radeon_device *rdev)
 
 void rv770_fini(struct radeon_device *rdev)
 {
-	radeon_pm_fini(rdev);
 	r600_blit_fini(rdev);
 	r700_cp_fini(rdev);
 	r600_wb_fini(rdev);
-- 
1.5.6.3

  reply	other threads:[~2010-05-07 21:17 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-05-07 21:16 [PATCH 0/9] drm/radeon/kms: update pm code Alex Deucher
2010-05-07 21:16 ` [PATCH 1/9] drm/radeon/kms: enable misc pm power state features on r5xx, rs6xx Alex Deucher
2010-05-07 21:16   ` [PATCH 2/9] drm/radeon/kms: enable misc pm power state features on r1xx-r4xx Alex Deucher
2010-05-07 21:16     ` [PATCH 3/9] drm/radeon/kms: re-enable gui idle interrupts on r6xx+ Alex Deucher
2010-05-07 21:16       ` [PATCH 4/9] radeon: Split out ring locking and allocation Alex Deucher
2010-05-07 21:16         ` [PATCH 5/9] radeon: Use fences to gate entry to reclocking on <r600 Alex Deucher
2010-05-07 21:16           ` [PATCH 6/9] drm/radeon/kms: fix lock ordering in ring, ib handling Alex Deucher
2010-05-07 21:16             ` [PATCH 7/9] drm/radeon/kms/pm: add support for no display power states Alex Deucher
2010-05-07 21:16               ` Alex Deucher [this message]
2010-05-07 21:16                 ` [PATCH 9/9] drm/radeon/kms/pm: make pm spam debug only Alex Deucher
2010-05-07 21:23 ` [PATCH 0/9] drm/radeon/kms: update pm code Matthew Garrett
2010-05-07 22:24   ` Alex Deucher
2010-05-08 11:14 ` Klaus Doblmann B.A.
2010-05-08 15:29   ` Alex Deucher
2010-05-08 18:46     ` Klaus Doblmann B.A.
2010-05-10 16:04 ` Andy Furniss
2010-05-10 16:47   ` Alex Deucher
2010-05-10 17:25     ` Andy Furniss
2010-05-10 17:28       ` Rafał Miłecki

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1273266980-20982-9-git-send-email-alexdeucher@gmail.com \
    --to=alexdeucher@gmail.com \
    --cc=airlied@gmail.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=mjg59@srcf.ucam.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.