drm/radeon/kms/pm: rework power management

Add two new sysfs attributes:
- dynpm
- power_state

Echoing 0/1 to dynpm disables/enables dynamic power management.
The driver scales the sclk dynamically based on the number of
queued fences.  dynpm only scales sclk dynamically in single head
mode.

Echoing x.y to power_state selects a static power state (x) and clock
mode (y).  This allows you to statically select a power state and clock
mode.  Selecting a static clock mode will disable dynpm.

Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index 38f75f5..4691309 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -247,7 +247,7 @@
 		 pcie_lanes);
 }
 
-void r600_set_power_state(struct radeon_device *rdev)
+void r600_set_power_state(struct radeon_device *rdev, bool static_switch)
 {
 	u32 sclk, mclk;
 
@@ -266,37 +266,52 @@
 			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;
 
-		/* set pcie lanes */
-		/* TODO */
+		/* voltage, pcie lanes, etc.*/
+		radeon_pm_misc(rdev);
 
-		/* set voltage */
-		/* TODO */
-
-		/* set engine clock */
-		if (sclk != rdev->pm.current_sclk) {
-			radeon_sync_with_vblank(rdev);
-			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);
-		}
+		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);
+			}
+#if 0
+			/* 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);
+			}
+#endif
+			radeon_pm_finish(rdev);
+		} else {
+			/* set engine clock */
+			if (sclk != rdev->pm.current_sclk) {
+				radeon_sync_with_vblank(rdev);
+				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);
+			}
 
 #if 0
-		/* set memory clock */
-		if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) {
-			radeon_sync_with_vblank(rdev);
-			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);
-		}
+			/* set memory clock */
+			if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) {
+				radeon_sync_with_vblank(rdev);
+				radeon_pm_debug_check_in_vbl(rdev, false);
+				radeon_pm_prepare(rdev);
+				radeon_set_memory_clock(rdev, mclk);
+				radeon_pm_finish(rdev);
+				radeon_pm_debug_check_in_vbl(rdev, true);
+				rdev->pm.current_mclk = mclk;
+				DRM_INFO("Setting: m: %d\n", mclk);
+			}
 #endif
+		}
 
 		rdev->pm.current_power_state_index = rdev->pm.requested_power_state_index;
 		rdev->pm.current_clock_mode_index = rdev->pm.requested_clock_mode_index;