drm/radeon/kms/pm: add support for no display power states

The lowest power states often cause display problems, so only enable
them when all displays are off.

Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index 1ee7fc9..0dfa508 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -58,7 +58,7 @@
 {
 	int i;
 
-	if (!static_switch)
+	if (rdev->pm.state != PM_STATE_DISABLED)
 		radeon_get_power_state(rdev, rdev->pm.planned_action);
 
 	mutex_lock(&rdev->ddev->struct_mutex);
@@ -147,8 +147,11 @@
 	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 > 1) &&
-		    (rdev->pm.power_state[ps].flags & RADEON_PM_SINGLE_DISPLAY_ONLY)) {
+		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 */
@@ -248,7 +251,7 @@
 			 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_SINGLE_DISPLAY_ONLY)
+		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++) {
@@ -261,6 +264,8 @@
 					 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");
 		}
 	}
 }
@@ -318,7 +323,7 @@
 		/* reset default clocks */
 		rdev->pm.state = PM_STATE_DISABLED;
 		rdev->pm.planned_action = PM_ACTION_DEFAULT;
-		radeon_pm_set_clocks(rdev, false);
+		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)) {
@@ -342,9 +347,6 @@
 	struct drm_crtc *crtc;
 	struct radeon_crtc *radeon_crtc;
 
-	if (rdev->pm.state == PM_STATE_DISABLED)
-		return;
-
 	mutex_lock(&rdev->pm.mutex);
 
 	rdev->pm.active_crtcs = 0;
@@ -358,13 +360,22 @@
 		}
 	}
 
+	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_UPCLOCK;
-			radeon_pm_set_clocks(rdev, false);
+			rdev->pm.planned_action = PM_ACTION_DEFAULT;
+			radeon_pm_set_clocks(rdev, true);
 
 			DRM_DEBUG("radeon: dynamic power management deactivated\n");
 		}
@@ -374,7 +385,7 @@
 		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, false);
+			radeon_pm_set_clocks(rdev, true);
 
 			queue_delayed_work(rdev->wq, &rdev->pm.idle_work,
 				msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
@@ -390,7 +401,7 @@
 
 			rdev->pm.state = PM_STATE_MINIMUM;
 			rdev->pm.planned_action = PM_ACTION_MINIMUM;
-			radeon_pm_set_clocks(rdev, false);
+			radeon_pm_set_clocks(rdev, true);
 		}
 	}
 
@@ -526,6 +537,9 @@
 			}
 		}
 
+		/* 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);