drm/radeon/kms/atom: add crtc disable function

Disables the crts as per dpms and also disables the ppll
associated with the crtc.  This should save additional power.

Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index 8c2d647..a22d5a3 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -669,6 +669,90 @@
 	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 }
 
+static void atombios_crtc_program_pll(struct drm_crtc *crtc,
+				      int crtc_id,
+				      int pll_id,
+				      u32 encoder_mode,
+				      u32 encoder_id,
+				      u32 clock,
+				      u32 ref_div,
+				      u32 fb_div,
+				      u32 frac_fb_div,
+				      u32 post_div)
+{
+	struct drm_device *dev = crtc->dev;
+	struct radeon_device *rdev = dev->dev_private;
+	u8 frev, crev;
+	int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock);
+	union set_pixel_clock args;
+
+	memset(&args, 0, sizeof(args));
+
+	if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev,
+				   &crev))
+		return;
+
+	switch (frev) {
+	case 1:
+		switch (crev) {
+		case 1:
+			if (clock == ATOM_DISABLE)
+				return;
+			args.v1.usPixelClock = cpu_to_le16(clock / 10);
+			args.v1.usRefDiv = cpu_to_le16(ref_div);
+			args.v1.usFbDiv = cpu_to_le16(fb_div);
+			args.v1.ucFracFbDiv = frac_fb_div;
+			args.v1.ucPostDiv = post_div;
+			args.v1.ucPpll = pll_id;
+			args.v1.ucCRTC = crtc_id;
+			args.v1.ucRefDivSrc = 1;
+			break;
+		case 2:
+			args.v2.usPixelClock = cpu_to_le16(clock / 10);
+			args.v2.usRefDiv = cpu_to_le16(ref_div);
+			args.v2.usFbDiv = cpu_to_le16(fb_div);
+			args.v2.ucFracFbDiv = frac_fb_div;
+			args.v2.ucPostDiv = post_div;
+			args.v2.ucPpll = pll_id;
+			args.v2.ucCRTC = crtc_id;
+			args.v2.ucRefDivSrc = 1;
+			break;
+		case 3:
+			args.v3.usPixelClock = cpu_to_le16(clock / 10);
+			args.v3.usRefDiv = cpu_to_le16(ref_div);
+			args.v3.usFbDiv = cpu_to_le16(fb_div);
+			args.v3.ucFracFbDiv = frac_fb_div;
+			args.v3.ucPostDiv = post_div;
+			args.v3.ucPpll = pll_id;
+			args.v3.ucMiscInfo = (pll_id << 2);
+			args.v3.ucTransmitterId = encoder_id;
+			args.v3.ucEncoderMode = encoder_mode;
+			break;
+		case 5:
+			args.v5.ucCRTC = crtc_id;
+			args.v5.usPixelClock = cpu_to_le16(clock / 10);
+			args.v5.ucRefDiv = ref_div;
+			args.v5.usFbDiv = cpu_to_le16(fb_div);
+			args.v5.ulFbDivDecFrac = cpu_to_le32(frac_fb_div * 100000);
+			args.v5.ucPostDiv = post_div;
+			args.v5.ucMiscInfo = 0; /* HDMI depth, etc. */
+			args.v5.ucTransmitterID = encoder_id;
+			args.v5.ucEncoderMode = encoder_mode;
+			args.v5.ucPpll = pll_id;
+			break;
+		default:
+			DRM_ERROR("Unknown table version %d %d\n", frev, crev);
+			return;
+		}
+		break;
+	default:
+		DRM_ERROR("Unknown table version %d %d\n", frev, crev);
+		return;
+	}
+
+	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+}
+
 static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
 {
 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
@@ -676,17 +760,12 @@
 	struct radeon_device *rdev = dev->dev_private;
 	struct drm_encoder *encoder = NULL;
 	struct radeon_encoder *radeon_encoder = NULL;
-	u8 frev, crev;
-	int index;
-	union set_pixel_clock args;
 	u32 pll_clock = mode->clock;
 	u32 ref_div = 0, fb_div = 0, frac_fb_div = 0, post_div = 0;
 	struct radeon_pll *pll;
 	u32 adjusted_clock;
 	int encoder_mode = 0;
 
-	memset(&args, 0, sizeof(args));
-
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 		if (encoder->crtc == crtc) {
 			radeon_encoder = to_radeon_encoder(encoder);
@@ -718,68 +797,10 @@
 	radeon_compute_pll(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
 			   &ref_div, &post_div);
 
-	index = GetIndexIntoMasterTable(COMMAND, SetPixelClock);
-	if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev,
-				   &crev))
-		return;
+	atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id,
+				  encoder_mode, radeon_encoder->encoder_id, mode->clock,
+				  ref_div, fb_div, frac_fb_div, post_div);
 
-	switch (frev) {
-	case 1:
-		switch (crev) {
-		case 1:
-			args.v1.usPixelClock = cpu_to_le16(mode->clock / 10);
-			args.v1.usRefDiv = cpu_to_le16(ref_div);
-			args.v1.usFbDiv = cpu_to_le16(fb_div);
-			args.v1.ucFracFbDiv = frac_fb_div;
-			args.v1.ucPostDiv = post_div;
-			args.v1.ucPpll = radeon_crtc->pll_id;
-			args.v1.ucCRTC = radeon_crtc->crtc_id;
-			args.v1.ucRefDivSrc = 1;
-			break;
-		case 2:
-			args.v2.usPixelClock = cpu_to_le16(mode->clock / 10);
-			args.v2.usRefDiv = cpu_to_le16(ref_div);
-			args.v2.usFbDiv = cpu_to_le16(fb_div);
-			args.v2.ucFracFbDiv = frac_fb_div;
-			args.v2.ucPostDiv = post_div;
-			args.v2.ucPpll = radeon_crtc->pll_id;
-			args.v2.ucCRTC = radeon_crtc->crtc_id;
-			args.v2.ucRefDivSrc = 1;
-			break;
-		case 3:
-			args.v3.usPixelClock = cpu_to_le16(mode->clock / 10);
-			args.v3.usRefDiv = cpu_to_le16(ref_div);
-			args.v3.usFbDiv = cpu_to_le16(fb_div);
-			args.v3.ucFracFbDiv = frac_fb_div;
-			args.v3.ucPostDiv = post_div;
-			args.v3.ucPpll = radeon_crtc->pll_id;
-			args.v3.ucMiscInfo = (radeon_crtc->pll_id << 2);
-			args.v3.ucTransmitterId = radeon_encoder->encoder_id;
-			args.v3.ucEncoderMode = encoder_mode;
-			break;
-		case 5:
-			args.v5.ucCRTC = radeon_crtc->crtc_id;
-			args.v5.usPixelClock = cpu_to_le16(mode->clock / 10);
-			args.v5.ucRefDiv = ref_div;
-			args.v5.usFbDiv = cpu_to_le16(fb_div);
-			args.v5.ulFbDivDecFrac = cpu_to_le32(frac_fb_div * 100000);
-			args.v5.ucPostDiv = post_div;
-			args.v5.ucMiscInfo = 0; /* HDMI depth, etc. */
-			args.v5.ucTransmitterID = radeon_encoder->encoder_id;
-			args.v5.ucEncoderMode = encoder_mode;
-			args.v5.ucPpll = radeon_crtc->pll_id;
-			break;
-		default:
-			DRM_ERROR("Unknown table version %d %d\n", frev, crev);
-			return;
-		}
-		break;
-	default:
-		DRM_ERROR("Unknown table version %d %d\n", frev, crev);
-		return;
-	}
-
-	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 }
 
 static int evergreen_crtc_set_base(struct drm_crtc *crtc, int x, int y,
@@ -1191,6 +1212,24 @@
 	atombios_lock_crtc(crtc, ATOM_DISABLE);
 }
 
+static void atombios_crtc_disable(struct drm_crtc *crtc)
+{
+	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+	atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+
+	switch (radeon_crtc->pll_id) {
+	case ATOM_PPLL1:
+	case ATOM_PPLL2:
+		/* disable the ppll */
+		atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id,
+					  0, 0, ATOM_DISABLE, 0, 0, 0, 0);
+		break;
+	default:
+		break;
+	}
+	radeon_crtc->pll_id = -1;
+}
+
 static const struct drm_crtc_helper_funcs atombios_helper_funcs = {
 	.dpms = atombios_crtc_dpms,
 	.mode_fixup = atombios_crtc_mode_fixup,
@@ -1199,6 +1238,7 @@
 	.prepare = atombios_crtc_prepare,
 	.commit = atombios_crtc_commit,
 	.load_lut = radeon_crtc_load_lut,
+	.disable = atombios_crtc_disable,
 };
 
 void radeon_atombios_init_crtc(struct drm_device *dev,