drm/radeon/kms: add initial radeon tv-out support.

This ports the tv-out code from the DDX to KMS.

adds a radeon.tv module option, radeon.tv=0 to disable tv

Signed-off-by: Dave Airlie <airlied@redhat.com>
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile
index 6fb8429..c5db0c4 100644
--- a/drivers/gpu/drm/radeon/Makefile
+++ b/drivers/gpu/drm/radeon/Makefile
@@ -47,7 +47,7 @@
 	radeon_clocks.o radeon_fb.o radeon_gem.o radeon_ring.o radeon_irq_kms.o \
 	radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \
 	rs400.o rs600.o rs690.o rv515.o r520.o r600.o rs780.o rv770.o \
-	radeon_test.o r200.o
+	radeon_test.o r200.o radeon_legacy_tv.o
 
 radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
 
diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h
index cf67928..5d40208 100644
--- a/drivers/gpu/drm/radeon/atombios.h
+++ b/drivers/gpu/drm/radeon/atombios.h
@@ -2374,6 +2374,17 @@
 	ATOM_MODE_TIMING aModeTimings[MAX_SUPPORTED_TV_TIMING];
 } ATOM_ANALOG_TV_INFO;
 
+#define MAX_SUPPORTED_TV_TIMING_V1_2    3
+
+typedef struct _ATOM_ANALOG_TV_INFO_V1_2 {
+	ATOM_COMMON_TABLE_HEADER sHeader;
+	UCHAR                    ucTV_SupportedStandard;
+	UCHAR                    ucTV_BootUpDefaultStandard;
+	UCHAR                    ucExt_TV_ASIC_ID;
+	UCHAR                    ucExt_TV_ASIC_SlaveAddr;
+	ATOM_DTD_FORMAT          aModeTimings[MAX_SUPPORTED_TV_TIMING];
+} ATOM_ANALOG_TV_INFO_V1_2;
+
 /**************************************************************************/
 /*  VRAM usage and their defintions */
 
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index 74d034f..8e31e99 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -31,6 +31,10 @@
 #include "atom.h"
 #include "atom-bits.h"
 
+/* evil but including atombios.h is much worse */
+bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index,
+				SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION *crtc_timing,
+				int32_t *pixel_clock);
 static void atombios_overscan_setup(struct drm_crtc *crtc,
 				    struct drm_display_mode *mode,
 				    struct drm_display_mode *adjusted_mode)
@@ -89,17 +93,32 @@
 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
 	ENABLE_SCALER_PS_ALLOCATION args;
 	int index = GetIndexIntoMasterTable(COMMAND, EnableScaler);
+
 	/* fixme - fill in enc_priv for atom dac */
 	enum radeon_tv_std tv_std = TV_STD_NTSC;
+	bool is_tv = false, is_cv = false;
+	struct drm_encoder *encoder;
 
 	if (!ASIC_IS_AVIVO(rdev) && radeon_crtc->crtc_id)
 		return;
 
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		/* find tv std */
+		if (encoder->crtc == crtc) {
+			struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+			if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) {
+				struct radeon_encoder_atom_dac *tv_dac = radeon_encoder->enc_priv;
+				tv_std = tv_dac->tv_std;
+				is_tv = true;
+			}
+		}
+	}
+
 	memset(&args, 0, sizeof(args));
 
 	args.ucScaler = radeon_crtc->crtc_id;
 
-	if (radeon_crtc->devices & (ATOM_DEVICE_TV_SUPPORT)) {
+	if (is_tv) {
 		switch (tv_std) {
 		case TV_STD_NTSC:
 		default:
@@ -128,7 +147,7 @@
 			break;
 		}
 		args.ucEnable = SCALER_ENABLE_MULTITAP_MODE;
-	} else if (radeon_crtc->devices & (ATOM_DEVICE_CV_SUPPORT)) {
+	} else if (is_cv) {
 		args.ucTVStandard = ATOM_TV_CV;
 		args.ucEnable = SCALER_ENABLE_MULTITAP_MODE;
 	} else {
@@ -151,9 +170,9 @@
 		}
 	}
 	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
-	if (radeon_crtc->devices & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT)
-	    && rdev->family >= CHIP_RV515 && rdev->family <= CHIP_RV570) {
-		atom_rv515_force_tv_scaler(rdev);
+	if ((is_tv || is_cv)
+	    && rdev->family >= CHIP_RV515 && rdev->family <= CHIP_R580) {
+		atom_rv515_force_tv_scaler(rdev, radeon_crtc);
 	}
 }
 
@@ -551,42 +570,68 @@
 	struct radeon_device *rdev = dev->dev_private;
 	struct drm_encoder *encoder;
 	SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION crtc_timing;
+	int need_tv_timings = 0;
+	bool ret;
 
 	/* TODO color tiling */
 	memset(&crtc_timing, 0, sizeof(crtc_timing));
 
-	/* TODO tv */
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		/* find tv std */
+		if (encoder->crtc == crtc) {
+			struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
 
+			if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) {
+				struct radeon_encoder_atom_dac *tv_dac = radeon_encoder->enc_priv;
+				if (tv_dac) {
+					if (tv_dac->tv_std == TV_STD_NTSC ||
+					    tv_dac->tv_std == TV_STD_NTSC_J ||
+					    tv_dac->tv_std == TV_STD_PAL_M)
+						need_tv_timings = 1;
+					else
+						need_tv_timings = 2;
+					break;
+				}
+			}
+		}
 	}
 
 	crtc_timing.ucCRTC = radeon_crtc->crtc_id;
-	crtc_timing.usH_Total = adjusted_mode->crtc_htotal;
-	crtc_timing.usH_Disp = adjusted_mode->crtc_hdisplay;
-	crtc_timing.usH_SyncStart = adjusted_mode->crtc_hsync_start;
-	crtc_timing.usH_SyncWidth =
-	    adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
+	if (need_tv_timings) {
+		ret = radeon_atom_get_tv_timings(rdev, need_tv_timings - 1,
+						 &crtc_timing, &adjusted_mode->clock);
+		if (ret == false)
+			need_tv_timings = 0;
+	}
 
-	crtc_timing.usV_Total = adjusted_mode->crtc_vtotal;
-	crtc_timing.usV_Disp = adjusted_mode->crtc_vdisplay;
-	crtc_timing.usV_SyncStart = adjusted_mode->crtc_vsync_start;
-	crtc_timing.usV_SyncWidth =
-	    adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start;
+	if (!need_tv_timings) {
+		crtc_timing.usH_Total = adjusted_mode->crtc_htotal;
+		crtc_timing.usH_Disp = adjusted_mode->crtc_hdisplay;
+		crtc_timing.usH_SyncStart = adjusted_mode->crtc_hsync_start;
+		crtc_timing.usH_SyncWidth =
+			adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
 
-	if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
-		crtc_timing.susModeMiscInfo.usAccess |= ATOM_VSYNC_POLARITY;
+		crtc_timing.usV_Total = adjusted_mode->crtc_vtotal;
+		crtc_timing.usV_Disp = adjusted_mode->crtc_vdisplay;
+		crtc_timing.usV_SyncStart = adjusted_mode->crtc_vsync_start;
+		crtc_timing.usV_SyncWidth =
+			adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start;
 
-	if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
-		crtc_timing.susModeMiscInfo.usAccess |= ATOM_HSYNC_POLARITY;
+		if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
+			crtc_timing.susModeMiscInfo.usAccess |= ATOM_VSYNC_POLARITY;
 
-	if (adjusted_mode->flags & DRM_MODE_FLAG_CSYNC)
-		crtc_timing.susModeMiscInfo.usAccess |= ATOM_COMPOSITESYNC;
+		if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
+			crtc_timing.susModeMiscInfo.usAccess |= ATOM_HSYNC_POLARITY;
 
-	if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)
-		crtc_timing.susModeMiscInfo.usAccess |= ATOM_INTERLACE;
+		if (adjusted_mode->flags & DRM_MODE_FLAG_CSYNC)
+			crtc_timing.susModeMiscInfo.usAccess |= ATOM_COMPOSITESYNC;
 
-	if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
-		crtc_timing.susModeMiscInfo.usAccess |= ATOM_DOUBLE_CLOCK_MODE;
+		if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)
+			crtc_timing.susModeMiscInfo.usAccess |= ATOM_INTERLACE;
+
+		if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
+			crtc_timing.susModeMiscInfo.usAccess |= ATOM_DOUBLE_CLOCK_MODE;
+	}
 
 	atombios_crtc_set_pll(crtc, adjusted_mode);
 	atombios_crtc_set_timing(crtc, &crtc_timing);
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 6c35c3c..e47f2fc 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -66,6 +66,7 @@
 extern int radeon_benchmarking;
 extern int radeon_testing;
 extern int radeon_connector_table;
+extern int radeon_tv;
 
 /*
  * Copy from radeon_drv.h so we don't have to include both and have conflicting
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index fcfe5c0..bba9b4b 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -471,11 +471,6 @@
 			continue;
 		}
 
-		if (i == ATOM_DEVICE_TV1_INDEX) {
-			DRM_DEBUG("Skipping TV Out\n");
-			continue;
-		}
-
 		bios_connectors[i].connector_type =
 		    supported_devices_connector_convert[ci.sucConnectorInfo.
 							sbfAccess.
@@ -858,6 +853,72 @@
 	return p_dac;
 }
 
+bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index,
+				SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION *crtc_timing,
+				int32_t *pixel_clock)
+{
+	struct radeon_mode_info *mode_info = &rdev->mode_info;
+	ATOM_ANALOG_TV_INFO *tv_info;
+	ATOM_ANALOG_TV_INFO_V1_2 *tv_info_v1_2;
+	ATOM_DTD_FORMAT *dtd_timings;
+	int data_index = GetIndexIntoMasterTable(DATA, AnalogTV_Info);
+	u8 frev, crev;
+	uint16_t data_offset;
+
+	atom_parse_data_header(mode_info->atom_context, data_index, NULL, &frev, &crev, &data_offset);
+
+	switch (crev) {
+	case 1:
+		tv_info = (ATOM_ANALOG_TV_INFO *)(mode_info->atom_context->bios + data_offset);
+		if (index > MAX_SUPPORTED_TV_TIMING)
+			return false;
+
+		crtc_timing->usH_Total = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_Total);
+		crtc_timing->usH_Disp = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_Disp);
+		crtc_timing->usH_SyncStart = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_SyncStart);
+		crtc_timing->usH_SyncWidth = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_SyncWidth);
+
+		crtc_timing->usV_Total = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_Total);
+		crtc_timing->usV_Disp = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_Disp);
+		crtc_timing->usV_SyncStart = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_SyncStart);
+		crtc_timing->usV_SyncWidth = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_SyncWidth);
+
+		crtc_timing->susModeMiscInfo = tv_info->aModeTimings[index].susModeMiscInfo;
+
+		crtc_timing->ucOverscanRight = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_OverscanRight);
+		crtc_timing->ucOverscanLeft = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_OverscanLeft);
+		crtc_timing->ucOverscanBottom = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_OverscanBottom);
+		crtc_timing->ucOverscanTop = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_OverscanTop);
+		*pixel_clock = le16_to_cpu(tv_info->aModeTimings[index].usPixelClock) * 10;
+
+		if (index == 1) {
+			/* PAL timings appear to have wrong values for totals */
+			crtc_timing->usH_Total -= 1;
+			crtc_timing->usV_Total -= 1;
+		}
+		break;
+	case 2:
+		tv_info_v1_2 = (ATOM_ANALOG_TV_INFO_V1_2 *)(mode_info->atom_context->bios + data_offset);
+		if (index > MAX_SUPPORTED_TV_TIMING_V1_2)
+			return false;
+
+		dtd_timings = &tv_info_v1_2->aModeTimings[index];
+		crtc_timing->usH_Total = le16_to_cpu(dtd_timings->usHActive) + le16_to_cpu(dtd_timings->usHBlanking_Time);
+		crtc_timing->usH_Disp = le16_to_cpu(dtd_timings->usHActive);
+		crtc_timing->usH_SyncStart = le16_to_cpu(dtd_timings->usHActive) + le16_to_cpu(dtd_timings->usHSyncOffset);
+		crtc_timing->usH_SyncWidth = le16_to_cpu(dtd_timings->usHSyncWidth);
+		crtc_timing->usV_Total = le16_to_cpu(dtd_timings->usVActive) + le16_to_cpu(dtd_timings->usVBlanking_Time);
+		crtc_timing->usV_Disp = le16_to_cpu(dtd_timings->usVActive);
+		crtc_timing->usV_SyncStart = le16_to_cpu(dtd_timings->usVActive) + le16_to_cpu(dtd_timings->usVSyncOffset);
+		crtc_timing->usV_SyncWidth = le16_to_cpu(dtd_timings->usVSyncWidth);
+
+		crtc_timing->susModeMiscInfo.usAccess = le16_to_cpu(dtd_timings->susModeMiscInfo.usAccess);
+		*pixel_clock = le16_to_cpu(dtd_timings->usPixClk) * 10;
+		break;
+	}
+	return true;
+}
+
 struct radeon_encoder_tv_dac *
 radeon_atombios_get_tv_dac_info(struct radeon_encoder *encoder)
 {
@@ -948,10 +1009,10 @@
 	uint32_t bios_2_scratch, bios_6_scratch;
 
 	if (rdev->family >= CHIP_R600) {
-		bios_2_scratch = RREG32(R600_BIOS_0_SCRATCH);
+		bios_2_scratch = RREG32(R600_BIOS_2_SCRATCH);
 		bios_6_scratch = RREG32(R600_BIOS_6_SCRATCH);
 	} else {
-		bios_2_scratch = RREG32(RADEON_BIOS_0_SCRATCH);
+		bios_2_scratch = RREG32(RADEON_BIOS_2_SCRATCH);
 		bios_6_scratch = RREG32(RADEON_BIOS_6_SCRATCH);
 	}
 
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 70ede6a..6a2b029 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -94,6 +94,54 @@
 	return NULL;
 }
 
+
+/*
+ * radeon_connector_analog_encoder_conflict_solve
+ * - search for other connectors sharing this encoder
+ *   if priority is true, then set them disconnected if this is connected
+ *   if priority is false, set us disconnected if they are connected
+ */
+static enum drm_connector_status
+radeon_connector_analog_encoder_conflict_solve(struct drm_connector *connector,
+					       struct drm_encoder *encoder,
+					       enum drm_connector_status current_status,
+					       bool priority)
+{
+	struct drm_device *dev = connector->dev;
+	struct drm_connector *conflict;
+	int i;
+
+	list_for_each_entry(conflict, &dev->mode_config.connector_list, head) {
+		if (conflict == connector)
+			continue;
+
+		for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+			if (conflict->encoder_ids[i] == 0)
+				break;
+
+			/* if the IDs match */
+			if (conflict->encoder_ids[i] == encoder->base.id) {
+				if (conflict->status != connector_status_connected)
+					continue;
+
+				if (priority == true) {
+					DRM_INFO("1: conflicting encoders switching off %s\n", drm_get_connector_name(conflict));
+					DRM_INFO("in favor of %s\n", drm_get_connector_name(connector));
+					conflict->status = connector_status_disconnected;
+					radeon_connector_update_scratch_regs(conflict, connector_status_disconnected);
+				} else {
+					DRM_INFO("2: conflicting encoders switching off %s\n", drm_get_connector_name(connector));
+					DRM_INFO("in favor of %s\n", drm_get_connector_name(conflict));
+					current_status = connector_status_disconnected;
+				}
+				break;
+			}
+		}
+	}
+	return current_status;
+
+}
+
 static struct drm_display_mode *radeon_fp_native_mode(struct drm_encoder *encoder)
 {
 	struct drm_device *dev = encoder->dev;
@@ -213,7 +261,6 @@
 static int radeon_vga_mode_valid(struct drm_connector *connector,
 				  struct drm_display_mode *mode)
 {
-
 	return MODE_OK;
 }
 
@@ -225,22 +272,22 @@
 	bool dret;
 	enum drm_connector_status ret = connector_status_disconnected;
 
+	encoder = radeon_best_single_encoder(connector);
+	if (!encoder)
+		ret = connector_status_disconnected;
+
 	radeon_i2c_do_lock(radeon_connector, 1);
 	dret = radeon_ddc_probe(radeon_connector);
 	radeon_i2c_do_lock(radeon_connector, 0);
 	if (dret)
 		ret = connector_status_connected;
 	else {
-		/* if EDID fails to a load detect */
-		encoder = radeon_best_single_encoder(connector);
-		if (!encoder)
-			ret = connector_status_disconnected;
-		else {
-			encoder_funcs = encoder->helper_private;
-			ret = encoder_funcs->detect(encoder, connector);
-		}
+		encoder_funcs = encoder->helper_private;
+		ret = encoder_funcs->detect(encoder, connector);
 	}
 
+	if (ret == connector_status_connected)
+		ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true);
 	radeon_connector_update_scratch_regs(connector, ret);
 	return ret;
 }
@@ -259,21 +306,87 @@
 	.set_property = radeon_connector_set_property,
 };
 
+static struct drm_display_mode tv_fixed_mode = {
+	DRM_MODE("800x600", DRM_MODE_TYPE_DEFAULT, 38250, 800, 832,
+		 912, 1024, 0, 600, 603, 607, 624, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC),
+};
+
+static int radeon_tv_get_modes(struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct drm_display_mode *tv_mode;
+
+	tv_mode = drm_mode_duplicate(dev, &tv_fixed_mode);
+	tv_mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+	drm_mode_probed_add(connector, tv_mode);
+
+	return 1;
+}
+
+static int radeon_tv_mode_valid(struct drm_connector *connector,
+				struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static enum drm_connector_status radeon_tv_detect(struct drm_connector *connector)
+{
+	struct drm_encoder *encoder;
+	struct drm_encoder_helper_funcs *encoder_funcs;
+	int ret;
+
+	encoder = radeon_best_single_encoder(connector);
+	if (!encoder)
+		ret = connector_status_disconnected;
+	else {
+		encoder_funcs = encoder->helper_private;
+		ret = encoder_funcs->detect(encoder, connector);
+	}
+	if (ret == connector_status_connected)
+		ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, false);
+	radeon_connector_update_scratch_regs(connector, ret);
+	return ret;
+}
+
+struct drm_connector_helper_funcs radeon_tv_connector_helper_funcs = {
+	.get_modes = radeon_tv_get_modes,
+	.mode_valid = radeon_tv_mode_valid,
+	.best_encoder = radeon_best_single_encoder,
+};
+
+struct drm_connector_funcs radeon_tv_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = radeon_tv_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = radeon_connector_destroy,
+	.set_property = radeon_connector_set_property,
+};
+
 static int radeon_dvi_get_modes(struct drm_connector *connector)
 {
 	struct radeon_connector *radeon_connector = to_radeon_connector(connector);
 	int ret;
 
 	ret = radeon_ddc_get_modes(radeon_connector);
-	/* reset scratch regs here since radeon_dvi_detect doesn't check digital bit */
-	radeon_connector_update_scratch_regs(connector, connector_status_connected);
 	return ret;
 }
 
+/*
+ * DVI is complicated
+ * Do a DDC probe, if DDC probe passes, get the full EDID so
+ * we can do analog/digital monitor detection at this point.
+ * If the monitor is an analog monitor or we got no DDC,
+ * we need to find the DAC encoder object for this connector.
+ * If we got no DDC, we do load detection on the DAC encoder object.
+ * If we got analog DDC or load detection passes on the DAC encoder
+ * we have to check if this analog encoder is shared with anyone else (TV)
+ * if its shared we have to set the other connector to disconnected.
+ */
 static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connector)
 {
 	struct radeon_connector *radeon_connector = to_radeon_connector(connector);
-	struct drm_encoder *encoder;
+	struct drm_encoder *encoder = NULL;
 	struct drm_encoder_helper_funcs *encoder_funcs;
 	struct drm_mode_object *obj;
 	int i;
@@ -283,32 +396,58 @@
 	radeon_i2c_do_lock(radeon_connector, 1);
 	dret = radeon_ddc_probe(radeon_connector);
 	radeon_i2c_do_lock(radeon_connector, 0);
-	if (dret)
-		ret = connector_status_connected;
-	else {
-		for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
-			if (connector->encoder_ids[i] == 0)
-				break;
+	if (dret) {
+		radeon_i2c_do_lock(radeon_connector, 1);
+		radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
+		radeon_i2c_do_lock(radeon_connector, 0);
 
-			obj = drm_mode_object_find(connector->dev,
-						   connector->encoder_ids[i],
-						   DRM_MODE_OBJECT_ENCODER);
-			if (!obj)
-				continue;
+		if (!radeon_connector->edid) {
+			DRM_ERROR("DDC responded but not EDID found for %s\n",
+				  drm_get_connector_name(connector));
+		} else {
+			radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL);
 
-			encoder = obj_to_encoder(obj);
-
-			encoder_funcs = encoder->helper_private;
-			if (encoder_funcs->detect) {
-				ret = encoder_funcs->detect(encoder, connector);
-				if (ret == connector_status_connected) {
-					radeon_connector->use_digital = 0;
-					break;
-				}
-			}
+			/* if this isn't a digital monitor
+			   then we need to make sure we don't have any
+			   TV conflicts */
+			ret = connector_status_connected;
 		}
 	}
 
+	if ((ret == connector_status_connected) && (radeon_connector->use_digital == true))
+		goto out;
+
+	/* find analog encoder */
+	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+		if (connector->encoder_ids[i] == 0)
+			break;
+
+		obj = drm_mode_object_find(connector->dev,
+					   connector->encoder_ids[i],
+					   DRM_MODE_OBJECT_ENCODER);
+		if (!obj)
+			continue;
+
+		encoder = obj_to_encoder(obj);
+
+		encoder_funcs = encoder->helper_private;
+		if (encoder_funcs->detect) {
+			if (ret != connector_status_connected) {
+				ret = encoder_funcs->detect(encoder, connector);
+				if (ret == connector_status_connected) {
+					radeon_connector->use_digital = false;
+				}
+			}
+			break;
+		}
+	}
+
+	if ((ret == connector_status_connected) && (radeon_connector->use_digital == false) &&
+	    encoder) {
+		ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true);
+	}
+
+out:
 	/* updated in get modes as well since we need to know if it's analog or digital */
 	radeon_connector_update_scratch_regs(connector, ret);
 	return ret;
@@ -332,7 +471,7 @@
 
 		encoder = obj_to_encoder(obj);
 
-		if (radeon_connector->use_digital) {
+		if (radeon_connector->use_digital == true) {
 			if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS)
 				return encoder;
 		} else {
@@ -385,10 +524,7 @@
 	uint32_t subpixel_order = SubPixelNone;
 
 	/* fixme - tv/cv/din */
-	if ((connector_type == DRM_MODE_CONNECTOR_Unknown) ||
-	    (connector_type == DRM_MODE_CONNECTOR_SVIDEO) ||
-	    (connector_type == DRM_MODE_CONNECTOR_Composite) ||
-	    (connector_type == DRM_MODE_CONNECTOR_9PinDIN))
+	if (connector_type == DRM_MODE_CONNECTOR_Unknown)
 		return;
 
 	/* see if we already added it */
@@ -480,6 +616,10 @@
 	case DRM_MODE_CONNECTOR_SVIDEO:
 	case DRM_MODE_CONNECTOR_Composite:
 	case DRM_MODE_CONNECTOR_9PinDIN:
+		if (radeon_tv == 1) {
+			drm_connector_init(dev, &radeon_connector->base, &radeon_tv_connector_funcs, connector_type);
+			drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs);
+		}
 		break;
 	case DRM_MODE_CONNECTOR_LVDS:
 		radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL);
@@ -522,10 +662,7 @@
 	uint32_t subpixel_order = SubPixelNone;
 
 	/* fixme - tv/cv/din */
-	if ((connector_type == DRM_MODE_CONNECTOR_Unknown) ||
-	    (connector_type == DRM_MODE_CONNECTOR_SVIDEO) ||
-	    (connector_type == DRM_MODE_CONNECTOR_Composite) ||
-	    (connector_type == DRM_MODE_CONNECTOR_9PinDIN))
+	if (connector_type == DRM_MODE_CONNECTOR_Unknown)
 		return;
 
 	/* see if we already added it */
@@ -578,6 +715,10 @@
 	case DRM_MODE_CONNECTOR_SVIDEO:
 	case DRM_MODE_CONNECTOR_Composite:
 	case DRM_MODE_CONNECTOR_9PinDIN:
+		if (radeon_tv == 1) {
+			drm_connector_init(dev, &radeon_connector->base, &radeon_tv_connector_funcs, connector_type);
+			drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs);
+		}
 		break;
 	case DRM_MODE_CONNECTOR_LVDS:
 		drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type);
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index af03560..9d817a6 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -312,7 +312,7 @@
 	}
 }
 
-bool radeon_setup_enc_conn(struct drm_device *dev)
+static bool radeon_setup_enc_conn(struct drm_device *dev)
 {
 	struct radeon_device *rdev = dev->dev_private;
 	struct drm_connector *drm_connector;
@@ -346,9 +346,13 @@
 
 	if (!radeon_connector->ddc_bus)
 		return -1;
-	radeon_i2c_do_lock(radeon_connector, 1);
-	edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
-	radeon_i2c_do_lock(radeon_connector, 0);
+	if (!radeon_connector->edid) {
+		radeon_i2c_do_lock(radeon_connector, 1);
+		edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
+		radeon_i2c_do_lock(radeon_connector, 0);
+	} else
+		edid = radeon_connector->edid;
+
 	if (edid) {
 		/* update digital bits here */
 		if (edid->input & DRM_EDID_INPUT_DIGITAL)
@@ -677,7 +681,6 @@
 			continue;
 		if (first) {
 			radeon_crtc->rmx_type = radeon_encoder->rmx_type;
-			radeon_crtc->devices = radeon_encoder->devices;
 			memcpy(&radeon_crtc->native_mode,
 				&radeon_encoder->native_mode,
 				sizeof(struct radeon_native_mode));
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 0bd5879..133e975 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -91,6 +91,7 @@
 int radeon_benchmarking = 0;
 int radeon_testing = 0;
 int radeon_connector_table = 0;
+int radeon_tv = 1;
 #endif
 
 MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
@@ -123,6 +124,9 @@
 
 MODULE_PARM_DESC(connector_table, "Force connector table");
 module_param_named(connector_table, radeon_connector_table, int, 0444);
+
+MODULE_PARM_DESC(tv, "TV enable (0 = disable)");
+module_param_named(tv, radeon_tv, int, 0444);
 #endif
 
 static int radeon_suspend(struct drm_device *dev, pm_message_t state)
diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c
index 0a92706..5c4ede7c 100644
--- a/drivers/gpu/drm/radeon/radeon_encoders.c
+++ b/drivers/gpu/drm/radeon/radeon_encoders.c
@@ -126,6 +126,23 @@
 	}
 }
 
+void radeon_encoder_set_active_device(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+	struct drm_connector *connector;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		if (connector->encoder == encoder) {
+			struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+			radeon_encoder->active_device = radeon_encoder->devices & radeon_connector->devices;
+			DRM_INFO("setting active device to %08x from %08x %08x for encoder %d\n",
+				 radeon_encoder->active_device, radeon_encoder->devices,
+				 radeon_connector->devices, encoder->encoder_type);
+		}
+	}
+}
+
 static struct drm_connector *
 radeon_get_connector_for_encoder(struct drm_encoder *encoder)
 {
@@ -244,9 +261,9 @@
 
 	args.ucAction = action;
 
-	if (radeon_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT))
+	if (radeon_encoder->active_device & (ATOM_DEVICE_CRT_SUPPORT))
 		args.ucDacStandard = ATOM_DAC1_PS2;
-	else if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT))
+	else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
 		args.ucDacStandard = ATOM_DAC1_CV;
 	else {
 		switch (tv_std) {
@@ -288,7 +305,7 @@
 
 	args.sTVEncoder.ucAction = action;
 
-	if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT))
+	if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
 		args.sTVEncoder.ucTvStandard = ATOM_TV_CV;
 	else {
 		switch (tv_std) {
@@ -825,10 +842,10 @@
 
 	/* XXX: fix up scratch reg handling */
 	temp = RREG32(reg);
-	if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT))
+	if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
 		WREG32(reg, (ATOM_S3_TV1_ACTIVE |
 			     (radeon_crtc->crtc_id << 18)));
-	else if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT))
+	else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
 		WREG32(reg, (ATOM_S3_CV_ACTIVE | (radeon_crtc->crtc_id << 24)));
 	else
 		WREG32(reg, 0);
@@ -851,9 +868,19 @@
 	DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION args;
 	int index = 0;
 	bool is_dig = false;
+	int devices;
 
 	memset(&args, 0, sizeof(args));
 
+	/* on DPMS off we have no idea if active device is meaningful */
+	if (mode != DRM_MODE_DPMS_ON && !radeon_encoder->active_device)
+		devices = radeon_encoder->devices;
+	else
+		devices = radeon_encoder->active_device;
+
+	DRM_INFO("encoder dpms %d to mode %d, devices %08x, active_devices %08x\n",
+		 radeon_encoder->encoder_id, mode, radeon_encoder->devices,
+		radeon_encoder->active_device);
 	switch (radeon_encoder->encoder_id) {
 	case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
 	case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
@@ -881,18 +908,18 @@
 		break;
 	case ENCODER_OBJECT_ID_INTERNAL_DAC1:
 	case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
-		if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT))
+		if (devices & (ATOM_DEVICE_TV_SUPPORT))
 			index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl);
-		else if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT))
+		else if (devices & (ATOM_DEVICE_CV_SUPPORT))
 			index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl);
 		else
 			index = GetIndexIntoMasterTable(COMMAND, DAC1OutputControl);
 		break;
 	case ENCODER_OBJECT_ID_INTERNAL_DAC2:
 	case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
-		if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT))
+		if (devices & (ATOM_DEVICE_TV_SUPPORT))
 			index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl);
-		else if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT))
+		else if (devices & (ATOM_DEVICE_CV_SUPPORT))
 			index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl);
 		else
 			index = GetIndexIntoMasterTable(COMMAND, DAC2OutputControl);
@@ -979,18 +1006,18 @@
 				break;
 			case ENCODER_OBJECT_ID_INTERNAL_DAC1:
 			case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
-				if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT))
+				if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
 					args.v1.ucDevice = ATOM_DEVICE_TV1_INDEX;
-				else if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT))
+				else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
 					args.v1.ucDevice = ATOM_DEVICE_CV_INDEX;
 				else
 					args.v1.ucDevice = ATOM_DEVICE_CRT1_INDEX;
 				break;
 			case ENCODER_OBJECT_ID_INTERNAL_DAC2:
 			case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
-				if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT))
+				if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
 					args.v1.ucDevice = ATOM_DEVICE_TV1_INDEX;
-				else if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT))
+				else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
 					args.v1.ucDevice = ATOM_DEVICE_CV_INDEX;
 				else
 					args.v1.ucDevice = ATOM_DEVICE_CRT2_INDEX;
@@ -1019,17 +1046,17 @@
 				args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
 				break;
 			case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
-				if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT))
+				if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
 					args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
-				else if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT))
+				else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
 					args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
 				else
 					args.v2.ucEncoderID = ASIC_INT_DAC1_ENCODER_ID;
 				break;
 			case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
-				if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT))
+				if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
 					args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
-				else if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT))
+				else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
 					args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
 				else
 					args.v2.ucEncoderID = ASIC_INT_DAC2_ENCODER_ID;
@@ -1097,7 +1124,7 @@
 	atombios_set_encoder_crtc_source(encoder);
 
 	if (ASIC_IS_AVIVO(rdev)) {
-		if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT))
+		if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT))
 			atombios_yuv_setup(encoder, true);
 		else
 			atombios_yuv_setup(encoder, false);
@@ -1135,7 +1162,7 @@
 	case ENCODER_OBJECT_ID_INTERNAL_DAC2:
 	case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
 		atombios_dac_setup(encoder, ATOM_ENABLE);
-		if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT))
+		if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT))
 			atombios_tv_setup(encoder, ATOM_ENABLE);
 		break;
 	}
@@ -1143,11 +1170,12 @@
 }
 
 static bool
-atombios_dac_load_detect(struct drm_encoder *encoder)
+atombios_dac_load_detect(struct drm_encoder *encoder, struct drm_connector *connector)
 {
 	struct drm_device *dev = encoder->dev;
 	struct radeon_device *rdev = dev->dev_private;
 	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+	struct radeon_connector *radeon_connector = to_radeon_connector(connector);
 
 	if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT |
 				       ATOM_DEVICE_CV_SUPPORT |
@@ -1168,15 +1196,15 @@
 		else
 			args.sDacload.ucDacType = ATOM_DAC_B;
 
-		if (radeon_encoder->devices & ATOM_DEVICE_CRT1_SUPPORT)
+		if (radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT)
 			args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT1_SUPPORT);
-		else if (radeon_encoder->devices & ATOM_DEVICE_CRT2_SUPPORT)
+		else if (radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT)
 			args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT2_SUPPORT);
-		else if (radeon_encoder->devices & ATOM_DEVICE_CV_SUPPORT) {
+		else if (radeon_connector->devices & ATOM_DEVICE_CV_SUPPORT) {
 			args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CV_SUPPORT);
 			if (crev >= 3)
 				args.sDacload.ucMisc = DAC_LOAD_MISC_YPrPb;
-		} else if (radeon_encoder->devices & ATOM_DEVICE_TV1_SUPPORT) {
+		} else if (radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT) {
 			args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_TV1_SUPPORT);
 			if (crev >= 3)
 				args.sDacload.ucMisc = DAC_LOAD_MISC_YPrPb;
@@ -1195,9 +1223,10 @@
 	struct drm_device *dev = encoder->dev;
 	struct radeon_device *rdev = dev->dev_private;
 	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+	struct radeon_connector *radeon_connector = to_radeon_connector(connector);
 	uint32_t bios_0_scratch;
 
-	if (!atombios_dac_load_detect(encoder)) {
+	if (!atombios_dac_load_detect(encoder, connector)) {
 		DRM_DEBUG("detect returned false \n");
 		return connector_status_unknown;
 	}
@@ -1207,17 +1236,20 @@
 	else
 		bios_0_scratch = RREG32(RADEON_BIOS_0_SCRATCH);
 
-	DRM_DEBUG("Bios 0 scratch %x\n", bios_0_scratch);
-	if (radeon_encoder->devices & ATOM_DEVICE_CRT1_SUPPORT) {
+	DRM_DEBUG("Bios 0 scratch %x %08x\n", bios_0_scratch, radeon_encoder->devices);
+	if (radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT) {
 		if (bios_0_scratch & ATOM_S0_CRT1_MASK)
 			return connector_status_connected;
-	} else if (radeon_encoder->devices & ATOM_DEVICE_CRT2_SUPPORT) {
+	}
+	if (radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT) {
 		if (bios_0_scratch & ATOM_S0_CRT2_MASK)
 			return connector_status_connected;
-	} else if (radeon_encoder->devices & ATOM_DEVICE_CV_SUPPORT) {
+	}
+	if (radeon_connector->devices & ATOM_DEVICE_CV_SUPPORT) {
 		if (bios_0_scratch & (ATOM_S0_CV_MASK|ATOM_S0_CV_MASK_A))
 			return connector_status_connected;
-	} else if (radeon_encoder->devices & ATOM_DEVICE_TV1_SUPPORT) {
+	}
+	if (radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT) {
 		if (bios_0_scratch & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A))
 			return connector_status_connected; /* CTV */
 		else if (bios_0_scratch & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A))
@@ -1230,6 +1262,8 @@
 {
 	radeon_atom_output_lock(encoder, true);
 	radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+
+	radeon_encoder_set_active_device(encoder);
 }
 
 static void radeon_atom_encoder_commit(struct drm_encoder *encoder)
@@ -1238,12 +1272,21 @@
 	radeon_atom_output_lock(encoder, false);
 }
 
+static void radeon_atom_encoder_disable(struct drm_encoder *encoder)
+{
+	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+	radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+	DRM_INFO("setting active device to 0 for encoder %d\n", encoder->encoder_type);
+	radeon_encoder->active_device = 0;
+}
+
 static const struct drm_encoder_helper_funcs radeon_atom_dig_helper_funcs = {
 	.dpms = radeon_atom_encoder_dpms,
 	.mode_fixup = radeon_atom_mode_fixup,
 	.prepare = radeon_atom_encoder_prepare,
 	.mode_set = radeon_atom_encoder_mode_set,
 	.commit = radeon_atom_encoder_commit,
+	.disable = radeon_atom_encoder_disable,
 	/* no detect for TMDS/LVDS yet */
 };
 
@@ -1268,6 +1311,18 @@
 	.destroy = radeon_enc_destroy,
 };
 
+struct radeon_encoder_atom_dac *
+radeon_atombios_set_dac_info(struct radeon_encoder *radeon_encoder)
+{
+	struct radeon_encoder_atom_dac *dac = kzalloc(sizeof(struct radeon_encoder_atom_dac), GFP_KERNEL);
+
+	if (!dac)
+		return NULL;
+
+	dac->tv_std = TV_STD_NTSC;
+	return dac;
+}
+
 struct radeon_encoder_atom_dig *
 radeon_atombios_set_dig_info(struct radeon_encoder *radeon_encoder)
 {
@@ -1336,6 +1391,7 @@
 	case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
 	case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
 		drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TVDAC);
+		radeon_encoder->enc_priv = radeon_atombios_set_dac_info(radeon_encoder);
 		drm_encoder_helper_add(encoder, &radeon_atom_dac_helper_funcs);
 		break;
 	case ENCODER_OBJECT_ID_INTERNAL_DVO1:
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
index 0da72f1..0d29d15 100644
--- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
+++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
@@ -28,6 +28,7 @@
 #include <drm/radeon_drm.h>
 #include "radeon_fixed.h"
 #include "radeon.h"
+#include "atom.h"
 
 static void radeon_legacy_rmx_mode_set(struct drm_crtc *crtc,
 				       struct drm_display_mode *mode,
@@ -501,6 +502,7 @@
 	struct drm_device *dev = crtc->dev;
 	struct radeon_device *rdev = dev->dev_private;
 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+	struct drm_encoder *encoder;
 	int format;
 	int hsync_start;
 	int hsync_wid;
@@ -509,8 +511,19 @@
 	uint32_t crtc_h_sync_strt_wid;
 	uint32_t crtc_v_total_disp;
 	uint32_t crtc_v_sync_strt_wid;
+	bool is_tv = false;
 
 	DRM_DEBUG("\n");
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		if (encoder->crtc == crtc) {
+			struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+			if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) {
+				is_tv = true;
+				DRM_INFO("crtc %d is connected to a TV\n", radeon_crtc->crtc_id);
+				break;
+			}
+		}
+	}
 
 	switch (crtc->fb->bits_per_pixel) {
 	case 15:      /*  555 */
@@ -642,6 +655,11 @@
 		WREG32(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl);
 	}
 
+	if (is_tv)
+		radeon_legacy_tv_adjust_crtc_reg(encoder, &crtc_h_total_disp,
+						 &crtc_h_sync_strt_wid, &crtc_v_total_disp,
+						 &crtc_v_sync_strt_wid);
+
 	WREG32(RADEON_CRTC_H_TOTAL_DISP + radeon_crtc->crtc_offset, crtc_h_total_disp);
 	WREG32(RADEON_CRTC_H_SYNC_STRT_WID + radeon_crtc->crtc_offset, crtc_h_sync_strt_wid);
 	WREG32(RADEON_CRTC_V_TOTAL_DISP + radeon_crtc->crtc_offset, crtc_v_total_disp);
@@ -668,7 +686,7 @@
 	uint32_t pll_ref_div = 0;
 	uint32_t pll_fb_post_div = 0;
 	uint32_t htotal_cntl = 0;
-
+	bool is_tv = false;
 	struct radeon_pll *pll;
 
 	struct {
@@ -703,6 +721,13 @@
 
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 		if (encoder->crtc == crtc) {
+			struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+
+			if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) {
+				is_tv = true;
+				break;
+			}
+
 			if (encoder->encoder_type != DRM_MODE_ENCODER_DAC)
 				pll_flags |= RADEON_PLL_NO_ODD_POST_DIV;
 			if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) {
@@ -766,6 +791,12 @@
 					  ~(RADEON_PIX2CLK_SRC_SEL_MASK)) |
 					 RADEON_PIX2CLK_SRC_SEL_P2PLLCLK);
 
+		if (is_tv) {
+			radeon_legacy_tv_adjust_pll2(encoder, &htotal_cntl,
+						     &pll_ref_div, &pll_fb_post_div,
+						     &pixclks_cntl);
+		}
+
 		WREG32_PLL_P(RADEON_PIXCLKS_CNTL,
 			     RADEON_PIX2CLK_SRC_SEL_CPUCLK,
 			     ~(RADEON_PIX2CLK_SRC_SEL_MASK));
@@ -820,6 +851,15 @@
 
 		WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl);
 	} else {
+		uint32_t pixclks_cntl;
+
+
+		if (is_tv) {
+			pixclks_cntl = RREG32_PLL(RADEON_PIXCLKS_CNTL);
+			radeon_legacy_tv_adjust_pll1(encoder, &htotal_cntl, &pll_ref_div,
+						     &pll_fb_post_div, &pixclks_cntl);
+		}
+
 		if (rdev->flags & RADEON_IS_MOBILITY) {
 			/* A temporal workaround for the occational blanking on certain laptop panels.
 			   This appears to related to the PLL divider registers (fail to lock?).
@@ -914,6 +954,8 @@
 			     RADEON_VCLK_SRC_SEL_PPLLCLK,
 			     ~(RADEON_VCLK_SRC_SEL_MASK));
 
+		if (is_tv)
+			WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl);
 	}
 }
 
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c
index 9322675..0aaafcd 100644
--- a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c
+++ b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c
@@ -29,6 +29,15 @@
 #include "radeon.h"
 #include "atom.h"
 
+static void radeon_legacy_encoder_disable(struct drm_encoder *encoder)
+{
+	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+	struct drm_encoder_helper_funcs *encoder_funcs;
+
+	encoder_funcs = encoder->helper_private;
+	encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
+	radeon_encoder->active_device = 0;
+}
 
 static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode)
 {
@@ -98,6 +107,8 @@
 	else
 		radeon_combios_output_lock(encoder, true);
 	radeon_legacy_lvds_dpms(encoder, DRM_MODE_DPMS_OFF);
+
+	radeon_encoder_set_active_device(encoder);
 }
 
 static void radeon_legacy_lvds_commit(struct drm_encoder *encoder)
@@ -195,6 +206,7 @@
 	.prepare = radeon_legacy_lvds_prepare,
 	.mode_set = radeon_legacy_lvds_mode_set,
 	.commit = radeon_legacy_lvds_commit,
+	.disable = radeon_legacy_encoder_disable,
 };
 
 
@@ -260,6 +272,7 @@
 	else
 		radeon_combios_output_lock(encoder, true);
 	radeon_legacy_primary_dac_dpms(encoder, DRM_MODE_DPMS_OFF);
+	radeon_encoder_set_active_device(encoder);
 }
 
 static void radeon_legacy_primary_dac_commit(struct drm_encoder *encoder)
@@ -402,6 +415,7 @@
 	.mode_set = radeon_legacy_primary_dac_mode_set,
 	.commit = radeon_legacy_primary_dac_commit,
 	.detect = radeon_legacy_primary_dac_detect,
+	.disable = radeon_legacy_encoder_disable,
 };
 
 
@@ -454,6 +468,7 @@
 	else
 		radeon_combios_output_lock(encoder, true);
 	radeon_legacy_tmds_int_dpms(encoder, DRM_MODE_DPMS_OFF);
+	radeon_encoder_set_active_device(encoder);
 }
 
 static void radeon_legacy_tmds_int_commit(struct drm_encoder *encoder)
@@ -566,6 +581,7 @@
 	.prepare = radeon_legacy_tmds_int_prepare,
 	.mode_set = radeon_legacy_tmds_int_mode_set,
 	.commit = radeon_legacy_tmds_int_commit,
+	.disable = radeon_legacy_encoder_disable,
 };
 
 
@@ -620,6 +636,7 @@
 	else
 		radeon_combios_output_lock(encoder, true);
 	radeon_legacy_tmds_ext_dpms(encoder, DRM_MODE_DPMS_OFF);
+	radeon_encoder_set_active_device(encoder);
 }
 
 static void radeon_legacy_tmds_ext_commit(struct drm_encoder *encoder)
@@ -706,6 +723,7 @@
 	.prepare = radeon_legacy_tmds_ext_prepare,
 	.mode_set = radeon_legacy_tmds_ext_mode_set,
 	.commit = radeon_legacy_tmds_ext_commit,
+	.disable = radeon_legacy_encoder_disable,
 };
 
 
@@ -727,17 +745,21 @@
 {
 	struct drm_device *dev = encoder->dev;
 	struct radeon_device *rdev = dev->dev_private;
+	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
 	uint32_t fp2_gen_cntl = 0, crtc2_gen_cntl = 0, tv_dac_cntl = 0;
-	/* uint32_t tv_master_cntl = 0; */
-
+	uint32_t tv_master_cntl = 0;
+	bool is_tv;
 	DRM_DEBUG("\n");
 
+	is_tv = radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT ? true : false;
+
 	if (rdev->family == CHIP_R200)
 		fp2_gen_cntl = RREG32(RADEON_FP2_GEN_CNTL);
 	else {
-		crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL);
-		/*  FIXME TV */
-		/* tv_master_cntl = RREG32(RADEON_TV_MASTER_CNTL); */
+		if (is_tv)
+			tv_master_cntl = RREG32(RADEON_TV_MASTER_CNTL);
+		else
+			crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL);
 		tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL);
 	}
 
@@ -746,20 +768,23 @@
 		if (rdev->family == CHIP_R200) {
 			fp2_gen_cntl |= (RADEON_FP2_ON | RADEON_FP2_DVO_EN);
 		} else {
-			crtc2_gen_cntl |= RADEON_CRTC2_CRT2_ON;
-			/* tv_master_cntl |= RADEON_TV_ON; */
+			if (is_tv)
+				tv_master_cntl |= RADEON_TV_ON;
+			else
+				crtc2_gen_cntl |= RADEON_CRTC2_CRT2_ON;
+
 			if (rdev->family == CHIP_R420 ||
-					rdev->family == CHIP_R423 ||
-					rdev->family == CHIP_RV410)
+			    rdev->family == CHIP_R423 ||
+			    rdev->family == CHIP_RV410)
 				tv_dac_cntl &= ~(R420_TV_DAC_RDACPD |
-						R420_TV_DAC_GDACPD |
-						R420_TV_DAC_BDACPD |
-						RADEON_TV_DAC_BGSLEEP);
+						 R420_TV_DAC_GDACPD |
+						 R420_TV_DAC_BDACPD |
+						 RADEON_TV_DAC_BGSLEEP);
 			else
 				tv_dac_cntl &= ~(RADEON_TV_DAC_RDACPD |
-						RADEON_TV_DAC_GDACPD |
-						RADEON_TV_DAC_BDACPD |
-						RADEON_TV_DAC_BGSLEEP);
+						 RADEON_TV_DAC_GDACPD |
+						 RADEON_TV_DAC_BDACPD |
+						 RADEON_TV_DAC_BGSLEEP);
 		}
 		break;
 	case DRM_MODE_DPMS_STANDBY:
@@ -768,8 +793,11 @@
 		if (rdev->family == CHIP_R200)
 			fp2_gen_cntl &= ~(RADEON_FP2_ON | RADEON_FP2_DVO_EN);
 		else {
-			crtc2_gen_cntl &= ~RADEON_CRTC2_CRT2_ON;
-			/* tv_master_cntl &= ~RADEON_TV_ON; */
+			if (is_tv)
+				tv_master_cntl &= ~RADEON_TV_ON;
+			else
+				crtc2_gen_cntl &= ~RADEON_CRTC2_CRT2_ON;
+
 			if (rdev->family == CHIP_R420 ||
 					rdev->family == CHIP_R423 ||
 					rdev->family == CHIP_RV410)
@@ -789,8 +817,10 @@
 	if (rdev->family == CHIP_R200) {
 		WREG32(RADEON_FP2_GEN_CNTL, fp2_gen_cntl);
 	} else {
-		WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl);
-		/* WREG32(RADEON_TV_MASTER_CNTL, tv_master_cntl); */
+		if (is_tv)
+			WREG32(RADEON_TV_MASTER_CNTL, tv_master_cntl);
+		else
+			WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl);
 		WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl);
 	}
 
@@ -809,6 +839,7 @@
 	else
 		radeon_combios_output_lock(encoder, true);
 	radeon_legacy_tv_dac_dpms(encoder, DRM_MODE_DPMS_OFF);
+	radeon_encoder_set_active_device(encoder);
 }
 
 static void radeon_legacy_tv_dac_commit(struct drm_encoder *encoder)
@@ -831,11 +862,15 @@
 	struct radeon_device *rdev = dev->dev_private;
 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
 	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+	struct radeon_encoder_tv_dac *tv_dac = radeon_encoder->enc_priv;
 	uint32_t tv_dac_cntl, gpiopad_a = 0, dac2_cntl, disp_output_cntl = 0;
-	uint32_t disp_hw_debug = 0, fp2_gen_cntl = 0;
+	uint32_t disp_hw_debug = 0, fp2_gen_cntl = 0, disp_tv_out_cntl = 0;
+	bool is_tv = false;
 
 	DRM_DEBUG("\n");
 
+	is_tv = radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT ? true : false;
+
 	if (rdev->family != CHIP_R200) {
 		tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL);
 		if (rdev->family == CHIP_R420 ||
@@ -858,7 +893,7 @@
 		}
 
 		/*  FIXME TV */
-		if (radeon_encoder->enc_priv) {
+		if (tv_dac) {
 			struct radeon_encoder_tv_dac *tv_dac = radeon_encoder->enc_priv;
 			tv_dac_cntl |= (RADEON_TV_DAC_NBLANK |
 					RADEON_TV_DAC_NHOLD |
@@ -875,44 +910,93 @@
 	if (ASIC_IS_R300(rdev)) {
 		gpiopad_a = RREG32(RADEON_GPIOPAD_A) | 1;
 		disp_output_cntl = RREG32(RADEON_DISP_OUTPUT_CNTL);
-	} else if (rdev->family == CHIP_R200)
-		fp2_gen_cntl = RREG32(RADEON_FP2_GEN_CNTL);
+	}
+
+	if (rdev->family == CHIP_R200 || ASIC_IS_R300(rdev))
+		disp_tv_out_cntl = RREG32(RADEON_DISP_TV_OUT_CNTL);
 	else
 		disp_hw_debug = RREG32(RADEON_DISP_HW_DEBUG);
 
-	dac2_cntl = RREG32(RADEON_DAC_CNTL2) | RADEON_DAC2_DAC2_CLK_SEL;
+	if (rdev->family == CHIP_R200)
+		fp2_gen_cntl = RREG32(RADEON_FP2_GEN_CNTL);
 
-	if (radeon_crtc->crtc_id == 0) {
-		if (ASIC_IS_R300(rdev)) {
-			disp_output_cntl &= ~RADEON_DISP_TVDAC_SOURCE_MASK;
-			disp_output_cntl |= RADEON_DISP_TVDAC_SOURCE_CRTC;
-		} else if (rdev->family == CHIP_R200) {
-			fp2_gen_cntl &= ~(R200_FP2_SOURCE_SEL_MASK |
-					  RADEON_FP2_DVO_RATE_SEL_SDR);
-		} else
-			disp_hw_debug |= RADEON_CRT2_DISP1_SEL;
+	if (is_tv) {
+		uint32_t dac_cntl;
+
+		dac_cntl = RREG32(RADEON_DAC_CNTL);
+		dac_cntl &= ~RADEON_DAC_TVO_EN;
+		WREG32(RADEON_DAC_CNTL, dac_cntl);
+
+		if (ASIC_IS_R300(rdev))
+			gpiopad_a = RREG32(RADEON_GPIOPAD_A) & ~1;
+
+		dac2_cntl = RREG32(RADEON_DAC_CNTL2) & ~RADEON_DAC2_DAC2_CLK_SEL;
+		if (radeon_crtc->crtc_id == 0) {
+			if (ASIC_IS_R300(rdev)) {
+				disp_output_cntl &= ~RADEON_DISP_TVDAC_SOURCE_MASK;
+				disp_output_cntl |= (RADEON_DISP_TVDAC_SOURCE_CRTC |
+						     RADEON_DISP_TV_SOURCE_CRTC);
+			}
+			if (rdev->family >= CHIP_R200) {
+				disp_tv_out_cntl &= ~RADEON_DISP_TV_PATH_SRC_CRTC2;
+			} else {
+				disp_hw_debug |= RADEON_CRT2_DISP1_SEL;
+			}
+		} else {
+			if (ASIC_IS_R300(rdev)) {
+				disp_output_cntl &= ~RADEON_DISP_TVDAC_SOURCE_MASK;
+				disp_output_cntl |= RADEON_DISP_TV_SOURCE_CRTC;
+			}
+			if (rdev->family >= CHIP_R200) {
+				disp_tv_out_cntl |= RADEON_DISP_TV_PATH_SRC_CRTC2;
+			} else {
+				disp_hw_debug &= ~RADEON_CRT2_DISP1_SEL;
+			}
+		}
+		WREG32(RADEON_DAC_CNTL2, dac2_cntl);
 	} else {
-		if (ASIC_IS_R300(rdev)) {
-			disp_output_cntl &= ~RADEON_DISP_TVDAC_SOURCE_MASK;
-			disp_output_cntl |= RADEON_DISP_TVDAC_SOURCE_CRTC2;
-		} else if (rdev->family == CHIP_R200) {
-			fp2_gen_cntl &= ~(R200_FP2_SOURCE_SEL_MASK |
-					  RADEON_FP2_DVO_RATE_SEL_SDR);
-			fp2_gen_cntl |= R200_FP2_SOURCE_SEL_CRTC2;
-		} else
-			disp_hw_debug &= ~RADEON_CRT2_DISP1_SEL;
-	}
 
-	WREG32(RADEON_DAC_CNTL2, dac2_cntl);
+		dac2_cntl = RREG32(RADEON_DAC_CNTL2) | RADEON_DAC2_DAC2_CLK_SEL;
+
+		if (radeon_crtc->crtc_id == 0) {
+			if (ASIC_IS_R300(rdev)) {
+				disp_output_cntl &= ~RADEON_DISP_TVDAC_SOURCE_MASK;
+				disp_output_cntl |= RADEON_DISP_TVDAC_SOURCE_CRTC;
+			} else if (rdev->family == CHIP_R200) {
+				fp2_gen_cntl &= ~(R200_FP2_SOURCE_SEL_MASK |
+						  RADEON_FP2_DVO_RATE_SEL_SDR);
+			} else
+				disp_hw_debug |= RADEON_CRT2_DISP1_SEL;
+		} else {
+			if (ASIC_IS_R300(rdev)) {
+				disp_output_cntl &= ~RADEON_DISP_TVDAC_SOURCE_MASK;
+				disp_output_cntl |= RADEON_DISP_TVDAC_SOURCE_CRTC2;
+			} else if (rdev->family == CHIP_R200) {
+				fp2_gen_cntl &= ~(R200_FP2_SOURCE_SEL_MASK |
+						  RADEON_FP2_DVO_RATE_SEL_SDR);
+				fp2_gen_cntl |= R200_FP2_SOURCE_SEL_CRTC2;
+			} else
+				disp_hw_debug &= ~RADEON_CRT2_DISP1_SEL;
+		}
+		WREG32(RADEON_DAC_CNTL2, dac2_cntl);
+	}
 
 	if (ASIC_IS_R300(rdev)) {
 		WREG32_P(RADEON_GPIOPAD_A, gpiopad_a, ~1);
-		WREG32(RADEON_DISP_TV_OUT_CNTL, disp_output_cntl);
-	} else if (rdev->family == CHIP_R200)
-		WREG32(RADEON_FP2_GEN_CNTL, fp2_gen_cntl);
+		WREG32(RADEON_DISP_OUTPUT_CNTL, disp_output_cntl);
+	}
+
+	if (rdev->family >= CHIP_R200)
+		WREG32(RADEON_DISP_TV_OUT_CNTL, disp_tv_out_cntl);
 	else
 		WREG32(RADEON_DISP_HW_DEBUG, disp_hw_debug);
 
+	if (rdev->family == CHIP_R200)
+		WREG32(RADEON_FP2_GEN_CNTL, fp2_gen_cntl);
+
+	if (is_tv)
+		radeon_legacy_tv_mode_set(encoder, mode, adjusted_mode);
+
 	if (rdev->is_atom_bios)
 		radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id);
 	else
@@ -920,6 +1004,141 @@
 
 }
 
+static bool r300_legacy_tv_detect(struct drm_encoder *encoder,
+				  struct drm_connector *connector)
+{
+	struct drm_device *dev = encoder->dev;
+	struct radeon_device *rdev = dev->dev_private;
+	uint32_t crtc2_gen_cntl, tv_dac_cntl, dac_cntl2, dac_ext_cntl;
+	uint32_t disp_output_cntl, gpiopad_a, tmp;
+	bool found = false;
+
+	/* save regs needed */
+	gpiopad_a = RREG32(RADEON_GPIOPAD_A);
+	dac_cntl2 = RREG32(RADEON_DAC_CNTL2);
+	crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL);
+	dac_ext_cntl = RREG32(RADEON_DAC_EXT_CNTL);
+	tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL);
+	disp_output_cntl = RREG32(RADEON_DISP_OUTPUT_CNTL);
+
+	WREG32_P(RADEON_GPIOPAD_A, 0, ~1);
+
+	WREG32(RADEON_DAC_CNTL2, RADEON_DAC2_DAC2_CLK_SEL);
+
+	WREG32(RADEON_CRTC2_GEN_CNTL,
+	       RADEON_CRTC2_CRT2_ON | RADEON_CRTC2_VSYNC_TRISTAT);
+
+	tmp = disp_output_cntl & ~RADEON_DISP_TVDAC_SOURCE_MASK;
+	tmp |= RADEON_DISP_TVDAC_SOURCE_CRTC2;
+	WREG32(RADEON_DISP_OUTPUT_CNTL, tmp);
+
+	WREG32(RADEON_DAC_EXT_CNTL,
+	       RADEON_DAC2_FORCE_BLANK_OFF_EN |
+	       RADEON_DAC2_FORCE_DATA_EN |
+	       RADEON_DAC_FORCE_DATA_SEL_RGB |
+	       (0xec << RADEON_DAC_FORCE_DATA_SHIFT));
+
+	WREG32(RADEON_TV_DAC_CNTL,
+	       RADEON_TV_DAC_STD_NTSC |
+	       (8 << RADEON_TV_DAC_BGADJ_SHIFT) |
+	       (6 << RADEON_TV_DAC_DACADJ_SHIFT));
+
+	RREG32(RADEON_TV_DAC_CNTL);
+	mdelay(4);
+
+	WREG32(RADEON_TV_DAC_CNTL,
+	       RADEON_TV_DAC_NBLANK |
+	       RADEON_TV_DAC_NHOLD |
+	       RADEON_TV_MONITOR_DETECT_EN |
+	       RADEON_TV_DAC_STD_NTSC |
+	       (8 << RADEON_TV_DAC_BGADJ_SHIFT) |
+	       (6 << RADEON_TV_DAC_DACADJ_SHIFT));
+
+	RREG32(RADEON_TV_DAC_CNTL);
+	mdelay(6);
+
+	tmp = RREG32(RADEON_TV_DAC_CNTL);
+	if ((tmp & RADEON_TV_DAC_GDACDET) != 0) {
+		found = true;
+		DRM_DEBUG("S-video TV connection detected\n");
+	} else if ((tmp & RADEON_TV_DAC_BDACDET) != 0) {
+		found = true;
+		DRM_DEBUG("Composite TV connection detected\n");
+	}
+
+	WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl);
+	WREG32(RADEON_DAC_EXT_CNTL, dac_ext_cntl);
+	WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl);
+	WREG32(RADEON_DISP_OUTPUT_CNTL, disp_output_cntl);
+	WREG32(RADEON_DAC_CNTL2, dac_cntl2);
+	WREG32_P(RADEON_GPIOPAD_A, gpiopad_a, ~1);
+	return found;
+}
+
+static bool radeon_legacy_tv_detect(struct drm_encoder *encoder,
+				    struct drm_connector *connector)
+{
+	struct drm_device *dev = encoder->dev;
+	struct radeon_device *rdev = dev->dev_private;
+	uint32_t tv_dac_cntl, dac_cntl2;
+	uint32_t config_cntl, tv_pre_dac_mux_cntl, tv_master_cntl, tmp;
+	bool found = false;
+
+	if (ASIC_IS_R300(rdev))
+		return r300_legacy_tv_detect(encoder, connector);
+
+	dac_cntl2 = RREG32(RADEON_DAC_CNTL2);
+	tv_master_cntl = RREG32(RADEON_TV_MASTER_CNTL);
+	tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL);
+	config_cntl = RREG32(RADEON_CONFIG_CNTL);
+	tv_pre_dac_mux_cntl = RREG32(RADEON_TV_PRE_DAC_MUX_CNTL);
+
+	tmp = dac_cntl2 & ~RADEON_DAC2_DAC2_CLK_SEL;
+	WREG32(RADEON_DAC_CNTL2, tmp);
+
+	tmp = tv_master_cntl | RADEON_TV_ON;
+	tmp &= ~(RADEON_TV_ASYNC_RST |
+		 RADEON_RESTART_PHASE_FIX |
+		 RADEON_CRT_FIFO_CE_EN |
+		 RADEON_TV_FIFO_CE_EN |
+		 RADEON_RE_SYNC_NOW_SEL_MASK);
+	tmp |= RADEON_TV_FIFO_ASYNC_RST | RADEON_CRT_ASYNC_RST;
+	WREG32(RADEON_TV_MASTER_CNTL, tmp);
+
+	tmp = RADEON_TV_DAC_NBLANK | RADEON_TV_DAC_NHOLD |
+		RADEON_TV_MONITOR_DETECT_EN | RADEON_TV_DAC_STD_NTSC |
+		(8 << RADEON_TV_DAC_BGADJ_SHIFT);
+
+	if (config_cntl & RADEON_CFG_ATI_REV_ID_MASK)
+		tmp |= (4 << RADEON_TV_DAC_DACADJ_SHIFT);
+	else
+		tmp |= (8 << RADEON_TV_DAC_DACADJ_SHIFT);
+	WREG32(RADEON_TV_DAC_CNTL, tmp);
+
+	tmp = RADEON_C_GRN_EN | RADEON_CMP_BLU_EN |
+		RADEON_RED_MX_FORCE_DAC_DATA |
+		RADEON_GRN_MX_FORCE_DAC_DATA |
+		RADEON_BLU_MX_FORCE_DAC_DATA |
+		(0x109 << RADEON_TV_FORCE_DAC_DATA_SHIFT);
+	WREG32(RADEON_TV_PRE_DAC_MUX_CNTL, tmp);
+
+	mdelay(3);
+	tmp = RREG32(RADEON_TV_DAC_CNTL);
+	if (tmp & RADEON_TV_DAC_GDACDET) {
+		found = true;
+		DRM_DEBUG("S-video TV connection detected\n");
+	} else if ((tmp & RADEON_TV_DAC_BDACDET) != 0) {
+		found = true;
+		DRM_DEBUG("Composite TV connection detected\n");
+	}
+
+	WREG32(RADEON_TV_PRE_DAC_MUX_CNTL, tv_pre_dac_mux_cntl);
+	WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl);
+	WREG32(RADEON_TV_MASTER_CNTL, tv_master_cntl);
+	WREG32(RADEON_DAC_CNTL2, dac_cntl2);
+	return found;
+}
+
 static enum drm_connector_status radeon_legacy_tv_dac_detect(struct drm_encoder *encoder,
 							     struct drm_connector *connector)
 {
@@ -928,9 +1147,29 @@
 	uint32_t crtc2_gen_cntl, tv_dac_cntl, dac_cntl2, dac_ext_cntl;
 	uint32_t disp_hw_debug, disp_output_cntl, gpiopad_a, pixclks_cntl, tmp;
 	enum drm_connector_status found = connector_status_disconnected;
+	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+	struct radeon_encoder_tv_dac *tv_dac = radeon_encoder->enc_priv;
 	bool color = true;
 
-	/*  FIXME tv */
+	if (connector->connector_type == DRM_MODE_CONNECTOR_SVIDEO ||
+	    connector->connector_type == DRM_MODE_CONNECTOR_Composite ||
+	    connector->connector_type == DRM_MODE_CONNECTOR_9PinDIN) {
+		bool tv_detect;
+
+		if (radeon_encoder->active_device && !(radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT))
+			return connector_status_disconnected;
+
+		tv_detect = radeon_legacy_tv_detect(encoder, connector);
+		if (tv_detect && tv_dac)
+			found = connector_status_connected;
+		return found;
+	}
+
+	/* don't probe if the encoder is being used for something else not CRT related */
+	if (radeon_encoder->active_device && !(radeon_encoder->active_device & ATOM_DEVICE_CRT_SUPPORT)) {
+		DRM_INFO("not detecting due to %08x\n", radeon_encoder->active_device);
+		return connector_status_disconnected;
+	}
 
 	/* save the regs we need */
 	pixclks_cntl = RREG32_PLL(RADEON_PIXCLKS_CNTL);
@@ -1013,8 +1252,7 @@
 	}
 	WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl);
 
-	/* return found; */
-	return connector_status_disconnected;
+	return found;
 
 }
 
@@ -1025,6 +1263,7 @@
 	.mode_set = radeon_legacy_tv_dac_mode_set,
 	.commit = radeon_legacy_tv_dac_commit,
 	.detect = radeon_legacy_tv_dac_detect,
+	.disable = radeon_legacy_encoder_disable,
 };
 
 
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_tv.c b/drivers/gpu/drm/radeon/radeon_legacy_tv.c
new file mode 100644
index 0000000..3a12bb0
--- /dev/null
+++ b/drivers/gpu/drm/radeon/radeon_legacy_tv.c
@@ -0,0 +1,904 @@
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+#include "radeon.h"
+
+/*
+ * Integrated TV out support based on the GATOS code by
+ * Federico Ulivi <fulivi@lycos.com>
+ */
+
+
+/*
+ * Limits of h/v positions (hPos & vPos)
+ */
+#define MAX_H_POSITION 5 /* Range: [-5..5], negative is on the left, 0 is default, positive is on the right */
+#define MAX_V_POSITION 5 /* Range: [-5..5], negative is up, 0 is default, positive is down */
+
+/*
+ * Unit for hPos (in TV clock periods)
+ */
+#define H_POS_UNIT 10
+
+/*
+ * Indexes in h. code timing table for horizontal line position adjustment
+ */
+#define H_TABLE_POS1 6
+#define H_TABLE_POS2 8
+
+/*
+ * Limits of hor. size (hSize)
+ */
+#define MAX_H_SIZE 5 /* Range: [-5..5], negative is smaller, positive is larger */
+
+/* tv standard constants */
+#define NTSC_TV_CLOCK_T 233
+#define NTSC_TV_VFTOTAL 1
+#define NTSC_TV_LINES_PER_FRAME 525
+#define NTSC_TV_ZERO_H_SIZE 479166
+#define NTSC_TV_H_SIZE_UNIT 9478
+
+#define PAL_TV_CLOCK_T 188
+#define PAL_TV_VFTOTAL 3
+#define PAL_TV_LINES_PER_FRAME 625
+#define PAL_TV_ZERO_H_SIZE 473200
+#define PAL_TV_H_SIZE_UNIT 9360
+
+/* tv pll setting for 27 mhz ref clk */
+#define NTSC_TV_PLL_M_27 22
+#define NTSC_TV_PLL_N_27 175
+#define NTSC_TV_PLL_P_27 5
+
+#define PAL_TV_PLL_M_27 113
+#define PAL_TV_PLL_N_27 668
+#define PAL_TV_PLL_P_27 3
+
+/* tv pll setting for 14 mhz ref clk */
+#define NTSC_TV_PLL_M_14 33
+#define NTSC_TV_PLL_N_14 693
+#define NTSC_TV_PLL_P_14 7
+
+#define VERT_LEAD_IN_LINES 2
+#define FRAC_BITS 0xe
+#define FRAC_MASK 0x3fff
+
+struct radeon_tv_mode_constants {
+	uint16_t hor_resolution;
+	uint16_t ver_resolution;
+	enum radeon_tv_std standard;
+	uint16_t hor_total;
+	uint16_t ver_total;
+	uint16_t hor_start;
+	uint16_t hor_syncstart;
+	uint16_t ver_syncstart;
+	unsigned def_restart;
+	uint16_t crtcPLL_N;
+	uint8_t  crtcPLL_M;
+	uint8_t  crtcPLL_post_div;
+	unsigned pix_to_tv;
+};
+
+static const uint16_t hor_timing_NTSC[] = {
+	0x0007,
+	0x003f,
+	0x0263,
+	0x0a24,
+	0x2a6b,
+	0x0a36,
+	0x126d, /* H_TABLE_POS1 */
+	0x1bfe,
+	0x1a8f, /* H_TABLE_POS2 */
+	0x1ec7,
+	0x3863,
+	0x1bfe,
+	0x1bfe,
+	0x1a2a,
+	0x1e95,
+	0x0e31,
+	0x201b,
+	0
+};
+
+static const uint16_t vert_timing_NTSC[] = {
+	0x2001,
+	0x200d,
+	0x1006,
+	0x0c06,
+	0x1006,
+	0x1818,
+	0x21e3,
+	0x1006,
+	0x0c06,
+	0x1006,
+	0x1817,
+	0x21d4,
+	0x0002,
+	0
+};
+
+static const uint16_t hor_timing_PAL[] = {
+	0x0007,
+	0x0058,
+	0x027c,
+	0x0a31,
+	0x2a77,
+	0x0a95,
+	0x124f, /* H_TABLE_POS1 */
+	0x1bfe,
+	0x1b22, /* H_TABLE_POS2 */
+	0x1ef9,
+	0x387c,
+	0x1bfe,
+	0x1bfe,
+	0x1b31,
+	0x1eb5,
+	0x0e43,
+	0x201b,
+	0
+};
+
+static const uint16_t vert_timing_PAL[] = 	{
+	0x2001,
+	0x200c,
+	0x1005,
+	0x0c05,
+	0x1005,
+	0x1401,
+	0x1821,
+	0x2240,
+	0x1005,
+	0x0c05,
+	0x1005,
+	0x1401,
+	0x1822,
+	0x2230,
+	0x0002,
+	0
+};
+
+/**********************************************************************
+ *
+ * availableModes
+ *
+ * Table of all allowed modes for tv output
+ *
+ **********************************************************************/
+static const struct radeon_tv_mode_constants available_tv_modes[] = {
+	{   /* NTSC timing for 27 Mhz ref clk */
+		800,                /* horResolution */
+		600,                /* verResolution */
+		TV_STD_NTSC,        /* standard */
+		990,                /* horTotal */
+		740,                /* verTotal */
+		813,                /* horStart */
+		824,                /* horSyncStart */
+		632,                /* verSyncStart */
+		625592,             /* defRestart */
+		592,                /* crtcPLL_N */
+		91,                 /* crtcPLL_M */
+		4,                  /* crtcPLL_postDiv */
+		1022,               /* pixToTV */
+	},
+	{   /* PAL timing for 27 Mhz ref clk */
+		800,               /* horResolution */
+		600,               /* verResolution */
+		TV_STD_PAL,        /* standard */
+		1144,              /* horTotal */
+		706,               /* verTotal */
+		812,               /* horStart */
+		824,               /* horSyncStart */
+		669,               /* verSyncStart */
+		696700,            /* defRestart */
+		1382,              /* crtcPLL_N */
+		231,               /* crtcPLL_M */
+		4,                 /* crtcPLL_postDiv */
+		759,               /* pixToTV */
+	},
+	{   /* NTSC timing for 14 Mhz ref clk */
+		800,                /* horResolution */
+		600,                /* verResolution */
+		TV_STD_NTSC,        /* standard */
+		1018,               /* horTotal */
+		727,                /* verTotal */
+		813,                /* horStart */
+		840,                /* horSyncStart */
+		633,                /* verSyncStart */
+		630627,             /* defRestart */
+		347,                /* crtcPLL_N */
+		14,                 /* crtcPLL_M */
+			8,                  /* crtcPLL_postDiv */
+		1022,               /* pixToTV */
+	},
+};
+
+#define N_AVAILABLE_MODES ARRAY_SIZE(available_tv_modes)
+
+static const struct radeon_tv_mode_constants *radeon_legacy_tv_get_std_mode(struct radeon_encoder *radeon_encoder,
+									    uint16_t *pll_ref_freq)
+{
+	struct drm_device *dev = radeon_encoder->base.dev;
+	struct radeon_device *rdev = dev->dev_private;
+	struct radeon_crtc *radeon_crtc;
+	struct radeon_encoder_tv_dac *tv_dac = radeon_encoder->enc_priv;
+	const struct radeon_tv_mode_constants *const_ptr;
+	struct radeon_pll *pll;
+
+	radeon_crtc = to_radeon_crtc(radeon_encoder->base.crtc);
+	if (radeon_crtc->crtc_id == 1)
+		pll = &rdev->clock.p2pll;
+	else
+		pll = &rdev->clock.p1pll;
+
+	if (pll_ref_freq)
+		*pll_ref_freq = pll->reference_freq;
+
+	if (tv_dac->tv_std == TV_STD_NTSC ||
+	    tv_dac->tv_std == TV_STD_NTSC_J ||
+	    tv_dac->tv_std == TV_STD_PAL_M) {
+		if (pll->reference_freq == 2700)
+			const_ptr = &available_tv_modes[0];
+		else
+			const_ptr = &available_tv_modes[2];
+	} else {
+		if (pll->reference_freq == 2700)
+			const_ptr = &available_tv_modes[1];
+		else
+			const_ptr = &available_tv_modes[1]; /* FIX ME */
+	}
+	return const_ptr;
+}
+
+static long YCOEF_value[5] = { 2, 2, 0, 4, 0 };
+static long YCOEF_EN_value[5] = { 1, 1, 0, 1, 0 };
+static long SLOPE_value[5] = { 1, 2, 2, 4, 8 };
+static long SLOPE_limit[5] = { 6, 5, 4, 3, 2 };
+
+static void radeon_wait_pll_lock(struct drm_encoder *encoder, unsigned n_tests,
+				 unsigned n_wait_loops, unsigned cnt_threshold)
+{
+	struct drm_device *dev = encoder->dev;
+	struct radeon_device *rdev = dev->dev_private;
+	uint32_t save_pll_test;
+	unsigned int i, j;
+
+	WREG32(RADEON_TEST_DEBUG_MUX, (RREG32(RADEON_TEST_DEBUG_MUX) & 0xffff60ff) | 0x100);
+	save_pll_test = RREG32_PLL(RADEON_PLL_TEST_CNTL);
+	WREG32_PLL(RADEON_PLL_TEST_CNTL, save_pll_test & ~RADEON_PLL_MASK_READ_B);
+
+	WREG8(RADEON_CLOCK_CNTL_INDEX, RADEON_PLL_TEST_CNTL);
+	for (i = 0; i < n_tests; i++) {
+		WREG8(RADEON_CLOCK_CNTL_DATA + 3, 0);
+		for (j = 0; j < n_wait_loops; j++)
+			if (RREG8(RADEON_CLOCK_CNTL_DATA + 3) >= cnt_threshold)
+				break;
+	}
+	WREG32_PLL(RADEON_PLL_TEST_CNTL, save_pll_test);
+	WREG32(RADEON_TEST_DEBUG_MUX, RREG32(RADEON_TEST_DEBUG_MUX) & 0xffffe0ff);
+}
+
+
+static void radeon_legacy_tv_write_fifo(struct radeon_encoder *radeon_encoder,
+					uint16_t addr, uint32_t value)
+{
+	struct drm_device *dev = radeon_encoder->base.dev;
+	struct radeon_device *rdev = dev->dev_private;
+	uint32_t tmp;
+	int i = 0;
+
+	WREG32(RADEON_TV_HOST_WRITE_DATA, value);
+
+	WREG32(RADEON_TV_HOST_RD_WT_CNTL, addr);
+	WREG32(RADEON_TV_HOST_RD_WT_CNTL, addr | RADEON_HOST_FIFO_WT);
+
+	do {
+		tmp = RREG32(RADEON_TV_HOST_RD_WT_CNTL);
+		if ((tmp & RADEON_HOST_FIFO_WT_ACK) == 0)
+			break;
+		i++;
+	} while (i < 10000);
+	WREG32(RADEON_TV_HOST_RD_WT_CNTL, 0);
+}
+
+#if 0 /* included for completeness */
+static uint32_t radeon_legacy_tv_read_fifo(struct radeon_encoder *radeon_encoder, uint16_t addr)
+{
+	struct drm_device *dev = radeon_encoder->base.dev;
+	struct radeon_device *rdev = dev->dev_private;
+	uint32_t tmp;
+	int i = 0;
+
+	WREG32(RADEON_TV_HOST_RD_WT_CNTL, addr);
+	WREG32(RADEON_TV_HOST_RD_WT_CNTL, addr | RADEON_HOST_FIFO_RD);
+
+	do {
+		tmp = RREG32(RADEON_TV_HOST_RD_WT_CNTL);
+		if ((tmp & RADEON_HOST_FIFO_RD_ACK) == 0)
+			break;
+		i++;
+	} while (i < 10000);
+	WREG32(RADEON_TV_HOST_RD_WT_CNTL, 0);
+	return RREG32(RADEON_TV_HOST_READ_DATA);
+}
+#endif
+
+static uint16_t radeon_get_htiming_tables_addr(uint32_t tv_uv_adr)
+{
+	uint16_t h_table;
+
+	switch ((tv_uv_adr & RADEON_HCODE_TABLE_SEL_MASK) >> RADEON_HCODE_TABLE_SEL_SHIFT) {
+	case 0:
+		h_table = RADEON_TV_MAX_FIFO_ADDR_INTERNAL;
+		break;
+	case 1:
+		h_table = ((tv_uv_adr & RADEON_TABLE1_BOT_ADR_MASK) >> RADEON_TABLE1_BOT_ADR_SHIFT) * 2;
+		break;
+	case 2:
+		h_table = ((tv_uv_adr & RADEON_TABLE3_TOP_ADR_MASK) >> RADEON_TABLE3_TOP_ADR_SHIFT) * 2;
+		break;
+	default:
+		h_table = 0;
+		break;
+	}
+	return h_table;
+}
+
+static uint16_t radeon_get_vtiming_tables_addr(uint32_t tv_uv_adr)
+{
+	uint16_t v_table;
+
+	switch ((tv_uv_adr & RADEON_VCODE_TABLE_SEL_MASK) >> RADEON_VCODE_TABLE_SEL_SHIFT) {
+	case 0:
+		v_table = ((tv_uv_adr & RADEON_MAX_UV_ADR_MASK) >> RADEON_MAX_UV_ADR_SHIFT) * 2 + 1;
+		break;
+	case 1:
+		v_table = ((tv_uv_adr & RADEON_TABLE1_BOT_ADR_MASK) >> RADEON_TABLE1_BOT_ADR_SHIFT) * 2 + 1;
+		break;
+	case 2:
+		v_table = ((tv_uv_adr & RADEON_TABLE3_TOP_ADR_MASK) >> RADEON_TABLE3_TOP_ADR_SHIFT) * 2 + 1;
+		break;
+	default:
+		v_table = 0;
+		break;
+	}
+	return v_table;
+}
+
+static void radeon_restore_tv_timing_tables(struct radeon_encoder *radeon_encoder)
+{
+	struct drm_device *dev = radeon_encoder->base.dev;
+	struct radeon_device *rdev = dev->dev_private;
+	struct radeon_encoder_tv_dac *tv_dac = radeon_encoder->enc_priv;
+	uint16_t h_table, v_table;
+	uint32_t tmp;
+	int i;
+
+	WREG32(RADEON_TV_UV_ADR, tv_dac->tv.tv_uv_adr);
+	h_table = radeon_get_htiming_tables_addr(tv_dac->tv.tv_uv_adr);
+	v_table = radeon_get_vtiming_tables_addr(tv_dac->tv.tv_uv_adr);
+
+	for (i = 0; i < MAX_H_CODE_TIMING_LEN; i += 2, h_table--) {
+		tmp = ((uint32_t)tv_dac->tv.h_code_timing[i] << 14) | ((uint32_t)tv_dac->tv.h_code_timing[i+1]);
+		radeon_legacy_tv_write_fifo(radeon_encoder, h_table, tmp);
+		if (tv_dac->tv.h_code_timing[i] == 0 || tv_dac->tv.h_code_timing[i + 1] == 0)
+			break;
+	}
+	for (i = 0; i < MAX_V_CODE_TIMING_LEN; i += 2, v_table++) {
+		tmp = ((uint32_t)tv_dac->tv.v_code_timing[i+1] << 14) | ((uint32_t)tv_dac->tv.v_code_timing[i]);
+		radeon_legacy_tv_write_fifo(radeon_encoder, v_table, tmp);
+		if (tv_dac->tv.v_code_timing[i] == 0 || tv_dac->tv.v_code_timing[i + 1] == 0)
+			break;
+	}
+}
+
+static void radeon_legacy_write_tv_restarts(struct radeon_encoder *radeon_encoder)
+{
+	struct drm_device *dev = radeon_encoder->base.dev;
+	struct radeon_device *rdev = dev->dev_private;
+	struct radeon_encoder_tv_dac *tv_dac = radeon_encoder->enc_priv;
+	WREG32(RADEON_TV_FRESTART, tv_dac->tv.frestart);
+	WREG32(RADEON_TV_HRESTART, tv_dac->tv.hrestart);
+	WREG32(RADEON_TV_VRESTART, tv_dac->tv.vrestart);
+}
+
+static bool radeon_legacy_tv_init_restarts(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct radeon_device *rdev = dev->dev_private;
+	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+	struct radeon_encoder_tv_dac *tv_dac = radeon_encoder->enc_priv;
+	struct radeon_crtc *radeon_crtc;
+	int restart;
+	unsigned int h_total, v_total, f_total;
+	int v_offset, h_offset;
+	u16 p1, p2, h_inc;
+	bool h_changed;
+	const struct radeon_tv_mode_constants *const_ptr;
+	struct radeon_pll *pll;
+
+	radeon_crtc = to_radeon_crtc(radeon_encoder->base.crtc);
+	if (radeon_crtc->crtc_id == 1)
+		pll = &rdev->clock.p2pll;
+	else
+		pll = &rdev->clock.p1pll;
+
+	const_ptr = radeon_legacy_tv_get_std_mode(radeon_encoder, NULL);
+	if (!const_ptr)
+		return false;
+
+	h_total = const_ptr->hor_total;
+	v_total = const_ptr->ver_total;
+
+	if (tv_dac->tv_std == TV_STD_NTSC ||
+	    tv_dac->tv_std == TV_STD_NTSC_J ||
+	    tv_dac->tv_std == TV_STD_PAL_M ||
+	    tv_dac->tv_std == TV_STD_PAL_60)
+		f_total = NTSC_TV_VFTOTAL + 1;
+	else
+		f_total = PAL_TV_VFTOTAL + 1;
+
+	/* adjust positions 1&2 in hor. cod timing table */
+	h_offset = tv_dac->h_pos * H_POS_UNIT;
+
+	if (tv_dac->tv_std == TV_STD_NTSC ||
+	    tv_dac->tv_std == TV_STD_NTSC_J ||
+	    tv_dac->tv_std == TV_STD_PAL_M) {
+		h_offset -= 50;
+		p1 = hor_timing_NTSC[H_TABLE_POS1];
+		p2 = hor_timing_NTSC[H_TABLE_POS2];
+	} else {
+		p1 = hor_timing_PAL[H_TABLE_POS1];
+		p2 = hor_timing_PAL[H_TABLE_POS2];
+	}
+
+	p1 = (u16)((int)p1 + h_offset);
+	p2 = (u16)((int)p2 - h_offset);
+
+	h_changed = (p1 != tv_dac->tv.h_code_timing[H_TABLE_POS1] ||
+		     p2 != tv_dac->tv.h_code_timing[H_TABLE_POS2]);
+
+	tv_dac->tv.h_code_timing[H_TABLE_POS1] = p1;
+	tv_dac->tv.h_code_timing[H_TABLE_POS2] = p2;
+
+	/* Convert hOffset from n. of TV clock periods to n. of CRTC clock periods (CRTC pixels) */
+	h_offset = (h_offset * (int)(const_ptr->pix_to_tv)) / 1000;
+
+	/* adjust restart */
+	restart = const_ptr->def_restart;
+
+	/*
+	 * convert v_pos TV lines to n. of CRTC pixels
+	 */
+	if (tv_dac->tv_std == TV_STD_NTSC ||
+	    tv_dac->tv_std == TV_STD_NTSC_J ||
+	    tv_dac->tv_std == TV_STD_PAL_M ||
+	    tv_dac->tv_std == TV_STD_PAL_60)
+		v_offset = ((int)(v_total * h_total) * 2 * tv_dac->v_pos) / (int)(NTSC_TV_LINES_PER_FRAME);
+	else
+		v_offset = ((int)(v_total * h_total) * 2 * tv_dac->v_pos) / (int)(PAL_TV_LINES_PER_FRAME);
+
+	restart -= v_offset + h_offset;
+
+	DRM_DEBUG("compute_restarts: def = %u h = %d v = %d, p1 = %04x, p2 = %04x, restart = %d\n",
+		  const_ptr->def_restart, tv_dac->h_pos, tv_dac->v_pos, p1, p2, restart);
+
+	tv_dac->tv.hrestart = restart % h_total;
+	restart /= h_total;
+	tv_dac->tv.vrestart = restart % v_total;
+	restart /= v_total;
+	tv_dac->tv.frestart = restart % f_total;
+
+	DRM_DEBUG("compute_restart: F/H/V=%u,%u,%u\n",
+		  (unsigned)tv_dac->tv.frestart,
+		  (unsigned)tv_dac->tv.vrestart,
+		  (unsigned)tv_dac->tv.hrestart);
+
+	/* compute h_inc from hsize */
+	if (tv_dac->tv_std == TV_STD_NTSC ||
+	    tv_dac->tv_std == TV_STD_NTSC_J ||
+	    tv_dac->tv_std == TV_STD_PAL_M)
+		h_inc = (u16)((int)(const_ptr->hor_resolution * 4096 * NTSC_TV_CLOCK_T) /
+			      (tv_dac->h_size * (int)(NTSC_TV_H_SIZE_UNIT) + (int)(NTSC_TV_ZERO_H_SIZE)));
+	else
+		h_inc = (u16)((int)(const_ptr->hor_resolution * 4096 * PAL_TV_CLOCK_T) /
+			      (tv_dac->h_size * (int)(PAL_TV_H_SIZE_UNIT) + (int)(PAL_TV_ZERO_H_SIZE)));
+
+	tv_dac->tv.timing_cntl = (tv_dac->tv.timing_cntl & ~RADEON_H_INC_MASK) |
+		((u32)h_inc << RADEON_H_INC_SHIFT);
+
+	DRM_DEBUG("compute_restart: h_size = %d h_inc = %d\n", tv_dac->h_size, h_inc);
+
+	return h_changed;
+}
+
+void radeon_legacy_tv_mode_set(struct drm_encoder *encoder,
+			       struct drm_display_mode *mode,
+			       struct drm_display_mode *adjusted_mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct radeon_device *rdev = dev->dev_private;
+	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+	struct radeon_encoder_tv_dac *tv_dac = radeon_encoder->enc_priv;
+	const struct radeon_tv_mode_constants *const_ptr;
+	struct radeon_crtc *radeon_crtc;
+	int i;
+	uint16_t pll_ref_freq;
+	uint32_t vert_space, flicker_removal, tmp;
+	uint32_t tv_master_cntl, tv_rgb_cntl, tv_dac_cntl;
+	uint32_t tv_modulator_cntl1, tv_modulator_cntl2;
+	uint32_t tv_vscaler_cntl1, tv_vscaler_cntl2;
+	uint32_t tv_pll_cntl, tv_pll_cntl1, tv_ftotal;
+	uint32_t tv_y_fall_cntl, tv_y_rise_cntl, tv_y_saw_tooth_cntl;
+	uint32_t m, n, p;
+	const uint16_t *hor_timing;
+	const uint16_t *vert_timing;
+
+	const_ptr = radeon_legacy_tv_get_std_mode(radeon_encoder, &pll_ref_freq);
+	if (!const_ptr)
+		return;
+
+	radeon_crtc = to_radeon_crtc(encoder->crtc);
+
+	tv_master_cntl = (RADEON_VIN_ASYNC_RST |
+			  RADEON_CRT_FIFO_CE_EN |
+			  RADEON_TV_FIFO_CE_EN |
+			  RADEON_TV_ON);
+
+	if (!ASIC_IS_R300(rdev))
+		tv_master_cntl |= RADEON_TVCLK_ALWAYS_ONb;
+
+	if (tv_dac->tv_std == TV_STD_NTSC ||
+	    tv_dac->tv_std == TV_STD_NTSC_J)
+		tv_master_cntl |= RADEON_RESTART_PHASE_FIX;
+
+	tv_modulator_cntl1 = (RADEON_SLEW_RATE_LIMIT |
+			      RADEON_SYNC_TIP_LEVEL |
+			      RADEON_YFLT_EN |
+			      RADEON_UVFLT_EN |
+			      (6 << RADEON_CY_FILT_BLEND_SHIFT));
+
+	if (tv_dac->tv_std == TV_STD_NTSC ||
+	    tv_dac->tv_std == TV_STD_NTSC_J) {
+		tv_modulator_cntl1 |= (0x46 << RADEON_SET_UP_LEVEL_SHIFT) |
+			(0x3b << RADEON_BLANK_LEVEL_SHIFT);
+		tv_modulator_cntl2 = (-111 & RADEON_TV_U_BURST_LEVEL_MASK) |
+			((0 & RADEON_TV_V_BURST_LEVEL_MASK) << RADEON_TV_V_BURST_LEVEL_SHIFT);
+	} else if (tv_dac->tv_std == TV_STD_SCART_PAL) {
+		tv_modulator_cntl1 |= RADEON_ALT_PHASE_EN;
+		tv_modulator_cntl2 = (0 & RADEON_TV_U_BURST_LEVEL_MASK) |
+			((0 & RADEON_TV_V_BURST_LEVEL_MASK) << RADEON_TV_V_BURST_LEVEL_SHIFT);
+	} else {
+		tv_modulator_cntl1 |= RADEON_ALT_PHASE_EN |
+			(0x3b << RADEON_SET_UP_LEVEL_SHIFT) |
+			(0x3b << RADEON_BLANK_LEVEL_SHIFT);
+		tv_modulator_cntl2 = (-78 & RADEON_TV_U_BURST_LEVEL_MASK) |
+			((62 & RADEON_TV_V_BURST_LEVEL_MASK) << RADEON_TV_V_BURST_LEVEL_SHIFT);
+	}
+
+
+	tv_rgb_cntl = (RADEON_RGB_DITHER_EN
+		       | RADEON_TVOUT_SCALE_EN
+		       | (0x0b << RADEON_UVRAM_READ_MARGIN_SHIFT)
+		       | (0x07 << RADEON_FIFORAM_FFMACRO_READ_MARGIN_SHIFT)
+		       | RADEON_RGB_ATTEN_SEL(0x3)
+		       | RADEON_RGB_ATTEN_VAL(0xc));
+
+	if (radeon_crtc->crtc_id == 1)
+		tv_rgb_cntl |= RADEON_RGB_SRC_SEL_CRTC2;
+	else {
+		if (radeon_crtc->rmx_type != RMX_OFF)
+			tv_rgb_cntl |= RADEON_RGB_SRC_SEL_RMX;
+		else
+			tv_rgb_cntl |= RADEON_RGB_SRC_SEL_CRTC1;
+	}
+
+	if (tv_dac->tv_std == TV_STD_NTSC ||
+	    tv_dac->tv_std == TV_STD_NTSC_J ||
+	    tv_dac->tv_std == TV_STD_PAL_M ||
+	    tv_dac->tv_std == TV_STD_PAL_60)
+		vert_space = const_ptr->ver_total * 2 * 10000 / NTSC_TV_LINES_PER_FRAME;
+	else
+		vert_space = const_ptr->ver_total * 2 * 10000 / PAL_TV_LINES_PER_FRAME;
+
+	tmp = RREG32(RADEON_TV_VSCALER_CNTL1);
+	tmp &= 0xe3ff0000;
+	tmp |= (vert_space * (1 << FRAC_BITS) / 10000);
+	tv_vscaler_cntl1 = tmp;
+
+	if (pll_ref_freq == 2700)
+		tv_vscaler_cntl1 |= RADEON_RESTART_FIELD;
+
+	if (const_ptr->hor_resolution == 1024)
+		tv_vscaler_cntl1 |= (4 << RADEON_Y_DEL_W_SIG_SHIFT);
+	else
+		tv_vscaler_cntl1 |= (2 << RADEON_Y_DEL_W_SIG_SHIFT);
+
+	/* scale up for int divide */
+	tmp = const_ptr->ver_total * 2 * 1000;
+	if (tv_dac->tv_std == TV_STD_NTSC ||
+	    tv_dac->tv_std == TV_STD_NTSC_J ||
+	    tv_dac->tv_std == TV_STD_PAL_M ||
+	    tv_dac->tv_std == TV_STD_PAL_60) {
+		tmp /= NTSC_TV_LINES_PER_FRAME;
+	} else {
+		tmp /= PAL_TV_LINES_PER_FRAME;
+	}
+	flicker_removal = (tmp + 500) / 1000;
+
+	if (flicker_removal < 3)
+		flicker_removal = 3;
+	for (i = 0; i < 6; ++i) {
+		if (flicker_removal == SLOPE_limit[i])
+			break;
+	}
+
+	tv_y_saw_tooth_cntl = (vert_space * SLOPE_value[i] * (1 << (FRAC_BITS - 1)) +
+				5001) / 10000 / 8 | ((SLOPE_value[i] *
+				(1 << (FRAC_BITS - 1)) / 8) << 16);
+	tv_y_fall_cntl =
+		(YCOEF_EN_value[i] << 17) | ((YCOEF_value[i] * (1 << 8) / 8) << 24) |
+		RADEON_Y_FALL_PING_PONG | (272 * SLOPE_value[i] / 8) * (1 << (FRAC_BITS - 1)) /
+		1024;
+	tv_y_rise_cntl = RADEON_Y_RISE_PING_PONG|
+		(flicker_removal * 1024 - 272) * SLOPE_value[i] / 8 * (1 << (FRAC_BITS - 1)) / 1024;
+
+	tv_vscaler_cntl2 = RREG32(RADEON_TV_VSCALER_CNTL2) & 0x00fffff0;
+	tv_vscaler_cntl2 |= (0x10 << 24) |
+		RADEON_DITHER_MODE |
+		RADEON_Y_OUTPUT_DITHER_EN |
+		RADEON_UV_OUTPUT_DITHER_EN |
+		RADEON_UV_TO_BUF_DITHER_EN;
+
+	tmp = (tv_vscaler_cntl1 >> RADEON_UV_INC_SHIFT) & RADEON_UV_INC_MASK;
+	tmp = ((16384 * 256 * 10) / tmp + 5) / 10;
+	tmp = (tmp << RADEON_UV_OUTPUT_POST_SCALE_SHIFT) | 0x000b0000;
+	tv_dac->tv.timing_cntl = tmp;
+
+	if (tv_dac->tv_std == TV_STD_NTSC ||
+	    tv_dac->tv_std == TV_STD_NTSC_J ||
+	    tv_dac->tv_std == TV_STD_PAL_M ||
+	    tv_dac->tv_std == TV_STD_PAL_60)
+		tv_dac_cntl = tv_dac->ntsc_tvdac_adj;
+	else
+		tv_dac_cntl = tv_dac->pal_tvdac_adj;
+
+	tv_dac_cntl |= RADEON_TV_DAC_NBLANK | RADEON_TV_DAC_NHOLD;
+
+	if (tv_dac->tv_std == TV_STD_NTSC ||
+	    tv_dac->tv_std == TV_STD_NTSC_J)
+		tv_dac_cntl |= RADEON_TV_DAC_STD_NTSC;
+	else
+		tv_dac_cntl |= RADEON_TV_DAC_STD_PAL;
+
+	if (tv_dac->tv_std == TV_STD_NTSC ||
+	    tv_dac->tv_std == TV_STD_NTSC_J) {
+		if (pll_ref_freq == 2700) {
+			m = NTSC_TV_PLL_M_27;
+			n = NTSC_TV_PLL_N_27;
+			p = NTSC_TV_PLL_P_27;
+		} else {
+			m = NTSC_TV_PLL_M_14;
+			n = NTSC_TV_PLL_N_14;
+			p = NTSC_TV_PLL_P_14;
+		}
+	} else {
+		if (pll_ref_freq == 2700) {
+			m = PAL_TV_PLL_M_27;
+			n = PAL_TV_PLL_N_27;
+			p = PAL_TV_PLL_P_27;
+		} else {
+			m = PAL_TV_PLL_M_27;
+			n = PAL_TV_PLL_N_27;
+			p = PAL_TV_PLL_P_27;
+		}
+	}
+
+	tv_pll_cntl = (m & RADEON_TV_M0LO_MASK) |
+		(((m >> 8) & RADEON_TV_M0HI_MASK) << RADEON_TV_M0HI_SHIFT) |
+		((n & RADEON_TV_N0LO_MASK) << RADEON_TV_N0LO_SHIFT) |
+		(((n >> 9) & RADEON_TV_N0HI_MASK) << RADEON_TV_N0HI_SHIFT) |
+		((p & RADEON_TV_P_MASK) << RADEON_TV_P_SHIFT);
+
+	tv_pll_cntl1 = (((4 & RADEON_TVPCP_MASK) << RADEON_TVPCP_SHIFT) |
+			((4 & RADEON_TVPVG_MASK) << RADEON_TVPVG_SHIFT) |
+			((1 & RADEON_TVPDC_MASK) << RADEON_TVPDC_SHIFT) |
+			RADEON_TVCLK_SRC_SEL_TVPLL |
+			RADEON_TVPLL_TEST_DIS);
+
+	tv_dac->tv.tv_uv_adr = 0xc8;
+
+	if (tv_dac->tv_std == TV_STD_NTSC ||
+	    tv_dac->tv_std == TV_STD_NTSC_J ||
+	    tv_dac->tv_std == TV_STD_PAL_M ||
+	    tv_dac->tv_std == TV_STD_PAL_60) {
+		tv_ftotal = NTSC_TV_VFTOTAL;
+		hor_timing = hor_timing_NTSC;
+		vert_timing = vert_timing_NTSC;
+	} else {
+		hor_timing = hor_timing_PAL;
+		vert_timing = vert_timing_PAL;
+		tv_ftotal = PAL_TV_VFTOTAL;
+	}
+
+	for (i = 0; i < MAX_H_CODE_TIMING_LEN; i++) {
+		if ((tv_dac->tv.h_code_timing[i] = hor_timing[i]) == 0)
+			break;
+	}
+
+	for (i = 0; i < MAX_V_CODE_TIMING_LEN; i++) {
+		if ((tv_dac->tv.v_code_timing[i] = vert_timing[i]) == 0)
+			break;
+	}
+
+	radeon_legacy_tv_init_restarts(encoder);
+
+	/* play with DAC_CNTL */
+	/* play with GPIOPAD_A */
+	/* DISP_OUTPUT_CNTL */
+	/* use reference freq */
+
+	/* program the TV registers */
+	WREG32(RADEON_TV_MASTER_CNTL, (tv_master_cntl | RADEON_TV_ASYNC_RST |
+				       RADEON_CRT_ASYNC_RST | RADEON_TV_FIFO_ASYNC_RST));
+
+	tmp = RREG32(RADEON_TV_DAC_CNTL);
+	tmp &= ~RADEON_TV_DAC_NBLANK;
+	tmp |= RADEON_TV_DAC_BGSLEEP |
+		RADEON_TV_DAC_RDACPD |
+		RADEON_TV_DAC_GDACPD |
+		RADEON_TV_DAC_BDACPD;
+	WREG32(RADEON_TV_DAC_CNTL, tmp);
+
+	/* TV PLL */
+	WREG32_PLL_P(RADEON_TV_PLL_CNTL1, 0, ~RADEON_TVCLK_SRC_SEL_TVPLL);
+	WREG32_PLL(RADEON_TV_PLL_CNTL, tv_pll_cntl);
+	WREG32_PLL_P(RADEON_TV_PLL_CNTL1, RADEON_TVPLL_RESET, ~RADEON_TVPLL_RESET);
+
+	radeon_wait_pll_lock(encoder, 200, 800, 135);
+
+	WREG32_PLL_P(RADEON_TV_PLL_CNTL1, 0, ~RADEON_TVPLL_RESET);
+
+	radeon_wait_pll_lock(encoder, 300, 160, 27);
+	radeon_wait_pll_lock(encoder, 200, 800, 135);
+
+	WREG32_PLL_P(RADEON_TV_PLL_CNTL1, 0, ~0xf);
+	WREG32_PLL_P(RADEON_TV_PLL_CNTL1, RADEON_TVCLK_SRC_SEL_TVPLL, ~RADEON_TVCLK_SRC_SEL_TVPLL);
+
+	WREG32_PLL_P(RADEON_TV_PLL_CNTL1, (1 << RADEON_TVPDC_SHIFT), ~RADEON_TVPDC_MASK);
+	WREG32_PLL_P(RADEON_TV_PLL_CNTL1, 0, ~RADEON_TVPLL_SLEEP);
+
+	/* TV HV */
+	WREG32(RADEON_TV_RGB_CNTL, tv_rgb_cntl);
+	WREG32(RADEON_TV_HTOTAL, const_ptr->hor_total - 1);
+	WREG32(RADEON_TV_HDISP, const_ptr->hor_resolution - 1);
+	WREG32(RADEON_TV_HSTART, const_ptr->hor_start);
+
+	WREG32(RADEON_TV_VTOTAL, const_ptr->ver_total - 1);
+	WREG32(RADEON_TV_VDISP, const_ptr->ver_resolution - 1);
+	WREG32(RADEON_TV_FTOTAL, tv_ftotal);
+	WREG32(RADEON_TV_VSCALER_CNTL1, tv_vscaler_cntl1);
+	WREG32(RADEON_TV_VSCALER_CNTL2, tv_vscaler_cntl2);
+
+	WREG32(RADEON_TV_Y_FALL_CNTL, tv_y_fall_cntl);
+	WREG32(RADEON_TV_Y_RISE_CNTL, tv_y_rise_cntl);
+	WREG32(RADEON_TV_Y_SAW_TOOTH_CNTL, tv_y_saw_tooth_cntl);
+
+	WREG32(RADEON_TV_MASTER_CNTL, (tv_master_cntl | RADEON_TV_ASYNC_RST |
+				       RADEON_CRT_ASYNC_RST));
+
+	/* TV restarts */
+	radeon_legacy_write_tv_restarts(radeon_encoder);
+
+	/* tv timings */
+	radeon_restore_tv_timing_tables(radeon_encoder);
+
+	WREG32(RADEON_TV_MASTER_CNTL, (tv_master_cntl | RADEON_TV_ASYNC_RST));
+
+	/* tv std */
+	WREG32(RADEON_TV_SYNC_CNTL, (RADEON_SYNC_PUB | RADEON_TV_SYNC_IO_DRIVE));
+	WREG32(RADEON_TV_TIMING_CNTL, tv_dac->tv.timing_cntl);
+	WREG32(RADEON_TV_MODULATOR_CNTL1, tv_modulator_cntl1);
+	WREG32(RADEON_TV_MODULATOR_CNTL2, tv_modulator_cntl2);
+	WREG32(RADEON_TV_PRE_DAC_MUX_CNTL, (RADEON_Y_RED_EN |
+					    RADEON_C_GRN_EN |
+					    RADEON_CMP_BLU_EN |
+					    RADEON_DAC_DITHER_EN));
+
+	WREG32(RADEON_TV_CRC_CNTL, 0);
+
+	WREG32(RADEON_TV_MASTER_CNTL, tv_master_cntl);
+
+	WREG32(RADEON_TV_GAIN_LIMIT_SETTINGS, ((0x17f << RADEON_UV_GAIN_LIMIT_SHIFT) |
+					       (0x5ff << RADEON_Y_GAIN_LIMIT_SHIFT)));
+	WREG32(RADEON_TV_LINEAR_GAIN_SETTINGS, ((0x100 << RADEON_UV_GAIN_SHIFT) |
+						(0x100 << RADEON_Y_GAIN_SHIFT)));
+
+	WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl);
+
+}
+
+void radeon_legacy_tv_adjust_crtc_reg(struct drm_encoder *encoder,
+				      uint32_t *h_total_disp, uint32_t *h_sync_strt_wid,
+				      uint32_t *v_total_disp, uint32_t *v_sync_strt_wid)
+{
+	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+	const struct radeon_tv_mode_constants *const_ptr;
+	uint32_t tmp;
+
+	const_ptr = radeon_legacy_tv_get_std_mode(radeon_encoder, NULL);
+	if (!const_ptr)
+		return;
+
+	*h_total_disp = (((const_ptr->hor_resolution / 8) - 1) << RADEON_CRTC_H_DISP_SHIFT) |
+		(((const_ptr->hor_total / 8) - 1) << RADEON_CRTC_H_TOTAL_SHIFT);
+
+	tmp = *h_sync_strt_wid;
+	tmp &= ~(RADEON_CRTC_H_SYNC_STRT_PIX | RADEON_CRTC_H_SYNC_STRT_CHAR);
+	tmp |= (((const_ptr->hor_syncstart / 8) - 1) << RADEON_CRTC_H_SYNC_STRT_CHAR_SHIFT) |
+		(const_ptr->hor_syncstart & 7);
+	*h_sync_strt_wid = tmp;
+
+	*v_total_disp = ((const_ptr->ver_resolution - 1) << RADEON_CRTC_V_DISP_SHIFT) |
+		((const_ptr->ver_total - 1) << RADEON_CRTC_V_TOTAL_SHIFT);
+
+	tmp = *v_sync_strt_wid;
+	tmp &= ~RADEON_CRTC_V_SYNC_STRT;
+	tmp |= ((const_ptr->ver_syncstart - 1) << RADEON_CRTC_V_SYNC_STRT_SHIFT);
+	*v_sync_strt_wid = tmp;
+}
+
+static inline int get_post_div(int value)
+{
+	int post_div;
+	switch (value) {
+	case 1: post_div = 0; break;
+	case 2: post_div = 1; break;
+	case 3: post_div = 4; break;
+	case 4: post_div = 2; break;
+	case 6: post_div = 6; break;
+	case 8: post_div = 3; break;
+	case 12: post_div = 7; break;
+	case 16:
+	default: post_div = 5; break;
+	}
+	return post_div;
+}
+
+void radeon_legacy_tv_adjust_pll1(struct drm_encoder *encoder,
+				  uint32_t *htotal_cntl, uint32_t *ppll_ref_div,
+				  uint32_t *ppll_div_3, uint32_t *pixclks_cntl)
+{
+	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+	const struct radeon_tv_mode_constants *const_ptr;
+
+	const_ptr = radeon_legacy_tv_get_std_mode(radeon_encoder, NULL);
+	if (!const_ptr)
+		return;
+
+	*htotal_cntl = (const_ptr->hor_total & 0x7) | RADEON_HTOT_CNTL_VGA_EN;
+
+	*ppll_ref_div = const_ptr->crtcPLL_M;
+
+	*ppll_div_3 = (const_ptr->crtcPLL_N & 0x7ff) | (get_post_div(const_ptr->crtcPLL_post_div) << 16);
+	*pixclks_cntl &= ~(RADEON_PIX2CLK_SRC_SEL_MASK | RADEON_PIXCLK_TV_SRC_SEL);
+	*pixclks_cntl |= RADEON_PIX2CLK_SRC_SEL_P2PLLCLK;
+}
+
+void radeon_legacy_tv_adjust_pll2(struct drm_encoder *encoder,
+				  uint32_t *htotal2_cntl, uint32_t *p2pll_ref_div,
+				  uint32_t *p2pll_div_0, uint32_t *pixclks_cntl)
+{
+	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+	const struct radeon_tv_mode_constants *const_ptr;
+
+	const_ptr = radeon_legacy_tv_get_std_mode(radeon_encoder, NULL);
+	if (!const_ptr)
+		return;
+
+	*htotal2_cntl = (const_ptr->hor_total & 0x7);
+
+	*p2pll_ref_div = const_ptr->crtcPLL_M;
+
+	*p2pll_div_0 = (const_ptr->crtcPLL_N & 0x7ff) | (get_post_div(const_ptr->crtcPLL_post_div) << 16);
+	*pixclks_cntl &= ~RADEON_PIX2CLK_SRC_SEL_MASK;
+	*pixclks_cntl |= RADEON_PIX2CLK_SRC_SEL_P2PLLCLK | RADEON_PIXCLK_TV_SRC_SEL;
+}
+
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 20e9509..523d6cb 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -188,6 +188,21 @@
 	uint32_t flags;
 };
 
+#define MAX_H_CODE_TIMING_LEN 32
+#define MAX_V_CODE_TIMING_LEN 32
+
+/* need to store these as reading
+   back code tables is excessive */
+struct radeon_tv_regs {
+	uint32_t tv_uv_adr;
+	uint32_t timing_cntl;
+	uint32_t hrestart;
+	uint32_t vrestart;
+	uint32_t frestart;
+	uint16_t h_code_timing[MAX_H_CODE_TIMING_LEN];
+	uint16_t v_code_timing[MAX_V_CODE_TIMING_LEN];
+};
+
 struct radeon_crtc {
 	struct drm_crtc base;
 	int crtc_id;
@@ -202,7 +217,6 @@
 	uint32_t legacy_display_base_addr;
 	uint32_t legacy_cursor_offset;
 	enum radeon_rmx_type rmx_type;
-	uint32_t devices;
 	fixed20_12 vsc;
 	fixed20_12 hsc;
 	struct radeon_native_mode native_mode;
@@ -234,7 +248,13 @@
 	uint32_t ntsc_tvdac_adj;
 	uint32_t pal_tvdac_adj;
 
+	int               h_pos;
+	int               v_pos;
+	int               h_size;
+	int               supported_tv_stds;
+	bool              tv_on;
 	enum radeon_tv_std tv_std;
+	struct radeon_tv_regs tv;
 };
 
 struct radeon_encoder_int_tmds {
@@ -253,10 +273,15 @@
 	struct radeon_native_mode native_mode;
 };
 
+struct radeon_encoder_atom_dac {
+	enum radeon_tv_std tv_std;
+};
+
 struct radeon_encoder {
 	struct drm_encoder base;
 	uint32_t encoder_id;
 	uint32_t devices;
+	uint32_t active_device;
 	uint32_t flags;
 	uint32_t pixel_clock;
 	enum radeon_rmx_type rmx_type;
@@ -274,7 +299,10 @@
 	uint32_t connector_id;
 	uint32_t devices;
 	struct radeon_i2c_chan *ddc_bus;
-	int use_digital;
+	bool use_digital;
+	/* we need to mind the EDID between detect
+	   and get modes due to analog/digital/tvencoder */
+	struct edid *edid;
 	void *con_priv;
 };
 
@@ -308,6 +336,7 @@
 struct drm_encoder *radeon_encoder_legacy_tmds_ext_add(struct drm_device *dev, int bios_index);
 extern void atombios_external_tmds_setup(struct drm_encoder *encoder, int action);
 extern int atombios_get_encoder_mode(struct drm_encoder *encoder);
+extern void radeon_encoder_set_active_device(struct drm_encoder *encoder);
 
 extern void radeon_crtc_load_lut(struct drm_crtc *crtc);
 extern int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
@@ -394,6 +423,19 @@
 bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
 					struct drm_display_mode *mode,
 					struct drm_display_mode *adjusted_mode);
-void atom_rv515_force_tv_scaler(struct radeon_device *rdev);
+void atom_rv515_force_tv_scaler(struct radeon_device *rdev, struct radeon_crtc *radeon_crtc);
 
+/* legacy tv */
+void radeon_legacy_tv_adjust_crtc_reg(struct drm_encoder *encoder,
+				      uint32_t *h_total_disp, uint32_t *h_sync_strt_wid,
+				      uint32_t *v_total_disp, uint32_t *v_sync_strt_wid);
+void radeon_legacy_tv_adjust_pll1(struct drm_encoder *encoder,
+				  uint32_t *htotal_cntl, uint32_t *ppll_ref_div,
+				  uint32_t *ppll_div_3, uint32_t *pixclks_cntl);
+void radeon_legacy_tv_adjust_pll2(struct drm_encoder *encoder,
+				  uint32_t *htotal2_cntl, uint32_t *p2pll_ref_div,
+				  uint32_t *p2pll_div_0, uint32_t *pixclks_cntl);
+void radeon_legacy_tv_mode_set(struct drm_encoder *encoder,
+			       struct drm_display_mode *mode,
+			       struct drm_display_mode *adjusted_mode);
 #endif
diff --git a/drivers/gpu/drm/radeon/radeon_reg.h b/drivers/gpu/drm/radeon/radeon_reg.h
index 404b39b..28be2f1 100644
--- a/drivers/gpu/drm/radeon/radeon_reg.h
+++ b/drivers/gpu/drm/radeon/radeon_reg.h
@@ -3462,7 +3462,9 @@
 #       define RADEON_RGB_CONVERT_BY_PASS	  (1 << 10)
 #       define RADEON_UVRAM_READ_MARGIN_SHIFT	  16
 #       define RADEON_FIFORAM_FFMACRO_READ_MARGIN_SHIFT	  20
-#	define RADEON_TVOUT_SCALE_EN		  (1 << 26)
+#       define RADEON_RGB_ATTEN_SEL(x)            ((x) << 24)
+#       define RADEON_TVOUT_SCALE_EN              (1 << 26)
+#       define RADEON_RGB_ATTEN_VAL(x)            ((x) << 28)
 #define RADEON_TV_SYNC_CNTL                          0x0808
 #       define RADEON_SYNC_OE                     (1 <<  0)
 #       define RADEON_SYNC_OUT                    (1 <<  1)
diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c
index 60a194f..97965c4 100644
--- a/drivers/gpu/drm/radeon/rv515.c
+++ b/drivers/gpu/drm/radeon/rv515.c
@@ -475,232 +475,234 @@
 	return 0;
 }
 
-void atom_rv515_force_tv_scaler(struct radeon_device *rdev)
+void atom_rv515_force_tv_scaler(struct radeon_device *rdev, struct radeon_crtc *crtc)
 {
+	int index_reg = 0x6578 + crtc->crtc_offset;
+	int data_reg = 0x657c + crtc->crtc_offset;
 
-	WREG32(0x659C, 0x0);
-	WREG32(0x6594, 0x705);
-	WREG32(0x65A4, 0x10001);
-	WREG32(0x65D8, 0x0);
-	WREG32(0x65B0, 0x0);
-	WREG32(0x65C0, 0x0);
-	WREG32(0x65D4, 0x0);
-	WREG32(0x6578, 0x0);
-	WREG32(0x657C, 0x841880A8);
-	WREG32(0x6578, 0x1);
-	WREG32(0x657C, 0x84208680);
-	WREG32(0x6578, 0x2);
-	WREG32(0x657C, 0xBFF880B0);
-	WREG32(0x6578, 0x100);
-	WREG32(0x657C, 0x83D88088);
-	WREG32(0x6578, 0x101);
-	WREG32(0x657C, 0x84608680);
-	WREG32(0x6578, 0x102);
-	WREG32(0x657C, 0xBFF080D0);
-	WREG32(0x6578, 0x200);
-	WREG32(0x657C, 0x83988068);
-	WREG32(0x6578, 0x201);
-	WREG32(0x657C, 0x84A08680);
-	WREG32(0x6578, 0x202);
-	WREG32(0x657C, 0xBFF080F8);
-	WREG32(0x6578, 0x300);
-	WREG32(0x657C, 0x83588058);
-	WREG32(0x6578, 0x301);
-	WREG32(0x657C, 0x84E08660);
-	WREG32(0x6578, 0x302);
-	WREG32(0x657C, 0xBFF88120);
-	WREG32(0x6578, 0x400);
-	WREG32(0x657C, 0x83188040);
-	WREG32(0x6578, 0x401);
-	WREG32(0x657C, 0x85008660);
-	WREG32(0x6578, 0x402);
-	WREG32(0x657C, 0xBFF88150);
-	WREG32(0x6578, 0x500);
-	WREG32(0x657C, 0x82D88030);
-	WREG32(0x6578, 0x501);
-	WREG32(0x657C, 0x85408640);
-	WREG32(0x6578, 0x502);
-	WREG32(0x657C, 0xBFF88180);
-	WREG32(0x6578, 0x600);
-	WREG32(0x657C, 0x82A08018);
-	WREG32(0x6578, 0x601);
-	WREG32(0x657C, 0x85808620);
-	WREG32(0x6578, 0x602);
-	WREG32(0x657C, 0xBFF081B8);
-	WREG32(0x6578, 0x700);
-	WREG32(0x657C, 0x82608010);
-	WREG32(0x6578, 0x701);
-	WREG32(0x657C, 0x85A08600);
-	WREG32(0x6578, 0x702);
-	WREG32(0x657C, 0x800081F0);
-	WREG32(0x6578, 0x800);
-	WREG32(0x657C, 0x8228BFF8);
-	WREG32(0x6578, 0x801);
-	WREG32(0x657C, 0x85E085E0);
-	WREG32(0x6578, 0x802);
-	WREG32(0x657C, 0xBFF88228);
-	WREG32(0x6578, 0x10000);
-	WREG32(0x657C, 0x82A8BF00);
-	WREG32(0x6578, 0x10001);
-	WREG32(0x657C, 0x82A08CC0);
-	WREG32(0x6578, 0x10002);
-	WREG32(0x657C, 0x8008BEF8);
-	WREG32(0x6578, 0x10100);
-	WREG32(0x657C, 0x81F0BF28);
-	WREG32(0x6578, 0x10101);
-	WREG32(0x657C, 0x83608CA0);
-	WREG32(0x6578, 0x10102);
-	WREG32(0x657C, 0x8018BED0);
-	WREG32(0x6578, 0x10200);
-	WREG32(0x657C, 0x8148BF38);
-	WREG32(0x6578, 0x10201);
-	WREG32(0x657C, 0x84408C80);
-	WREG32(0x6578, 0x10202);
-	WREG32(0x657C, 0x8008BEB8);
-	WREG32(0x6578, 0x10300);
-	WREG32(0x657C, 0x80B0BF78);
-	WREG32(0x6578, 0x10301);
-	WREG32(0x657C, 0x85008C20);
-	WREG32(0x6578, 0x10302);
-	WREG32(0x657C, 0x8020BEA0);
-	WREG32(0x6578, 0x10400);
-	WREG32(0x657C, 0x8028BF90);
-	WREG32(0x6578, 0x10401);
-	WREG32(0x657C, 0x85E08BC0);
-	WREG32(0x6578, 0x10402);
-	WREG32(0x657C, 0x8018BE90);
-	WREG32(0x6578, 0x10500);
-	WREG32(0x657C, 0xBFB8BFB0);
-	WREG32(0x6578, 0x10501);
-	WREG32(0x657C, 0x86C08B40);
-	WREG32(0x6578, 0x10502);
-	WREG32(0x657C, 0x8010BE90);
-	WREG32(0x6578, 0x10600);
-	WREG32(0x657C, 0xBF58BFC8);
-	WREG32(0x6578, 0x10601);
-	WREG32(0x657C, 0x87A08AA0);
-	WREG32(0x6578, 0x10602);
-	WREG32(0x657C, 0x8010BE98);
-	WREG32(0x6578, 0x10700);
-	WREG32(0x657C, 0xBF10BFF0);
-	WREG32(0x6578, 0x10701);
-	WREG32(0x657C, 0x886089E0);
-	WREG32(0x6578, 0x10702);
-	WREG32(0x657C, 0x8018BEB0);
-	WREG32(0x6578, 0x10800);
-	WREG32(0x657C, 0xBED8BFE8);
-	WREG32(0x6578, 0x10801);
-	WREG32(0x657C, 0x89408940);
-	WREG32(0x6578, 0x10802);
-	WREG32(0x657C, 0xBFE8BED8);
-	WREG32(0x6578, 0x20000);
-	WREG32(0x657C, 0x80008000);
-	WREG32(0x6578, 0x20001);
-	WREG32(0x657C, 0x90008000);
-	WREG32(0x6578, 0x20002);
-	WREG32(0x657C, 0x80008000);
-	WREG32(0x6578, 0x20003);
-	WREG32(0x657C, 0x80008000);
-	WREG32(0x6578, 0x20100);
-	WREG32(0x657C, 0x80108000);
-	WREG32(0x6578, 0x20101);
-	WREG32(0x657C, 0x8FE0BF70);
-	WREG32(0x6578, 0x20102);
-	WREG32(0x657C, 0xBFE880C0);
-	WREG32(0x6578, 0x20103);
-	WREG32(0x657C, 0x80008000);
-	WREG32(0x6578, 0x20200);
-	WREG32(0x657C, 0x8018BFF8);
-	WREG32(0x6578, 0x20201);
-	WREG32(0x657C, 0x8F80BF08);
-	WREG32(0x6578, 0x20202);
-	WREG32(0x657C, 0xBFD081A0);
-	WREG32(0x6578, 0x20203);
-	WREG32(0x657C, 0xBFF88000);
-	WREG32(0x6578, 0x20300);
-	WREG32(0x657C, 0x80188000);
-	WREG32(0x6578, 0x20301);
-	WREG32(0x657C, 0x8EE0BEC0);
-	WREG32(0x6578, 0x20302);
-	WREG32(0x657C, 0xBFB082A0);
-	WREG32(0x6578, 0x20303);
-	WREG32(0x657C, 0x80008000);
-	WREG32(0x6578, 0x20400);
-	WREG32(0x657C, 0x80188000);
-	WREG32(0x6578, 0x20401);
-	WREG32(0x657C, 0x8E00BEA0);
-	WREG32(0x6578, 0x20402);
-	WREG32(0x657C, 0xBF8883C0);
-	WREG32(0x6578, 0x20403);
-	WREG32(0x657C, 0x80008000);
-	WREG32(0x6578, 0x20500);
-	WREG32(0x657C, 0x80188000);
-	WREG32(0x6578, 0x20501);
-	WREG32(0x657C, 0x8D00BE90);
-	WREG32(0x6578, 0x20502);
-	WREG32(0x657C, 0xBF588500);
-	WREG32(0x6578, 0x20503);
-	WREG32(0x657C, 0x80008008);
-	WREG32(0x6578, 0x20600);
-	WREG32(0x657C, 0x80188000);
-	WREG32(0x6578, 0x20601);
-	WREG32(0x657C, 0x8BC0BE98);
-	WREG32(0x6578, 0x20602);
-	WREG32(0x657C, 0xBF308660);
-	WREG32(0x6578, 0x20603);
-	WREG32(0x657C, 0x80008008);
-	WREG32(0x6578, 0x20700);
-	WREG32(0x657C, 0x80108000);
-	WREG32(0x6578, 0x20701);
-	WREG32(0x657C, 0x8A80BEB0);
-	WREG32(0x6578, 0x20702);
-	WREG32(0x657C, 0xBF0087C0);
-	WREG32(0x6578, 0x20703);
-	WREG32(0x657C, 0x80008008);
-	WREG32(0x6578, 0x20800);
-	WREG32(0x657C, 0x80108000);
-	WREG32(0x6578, 0x20801);
-	WREG32(0x657C, 0x8920BED0);
-	WREG32(0x6578, 0x20802);
-	WREG32(0x657C, 0xBED08920);
-	WREG32(0x6578, 0x20803);
-	WREG32(0x657C, 0x80008010);
-	WREG32(0x6578, 0x30000);
-	WREG32(0x657C, 0x90008000);
-	WREG32(0x6578, 0x30001);
-	WREG32(0x657C, 0x80008000);
-	WREG32(0x6578, 0x30100);
-	WREG32(0x657C, 0x8FE0BF90);
-	WREG32(0x6578, 0x30101);
-	WREG32(0x657C, 0xBFF880A0);
-	WREG32(0x6578, 0x30200);
-	WREG32(0x657C, 0x8F60BF40);
-	WREG32(0x6578, 0x30201);
-	WREG32(0x657C, 0xBFE88180);
-	WREG32(0x6578, 0x30300);
-	WREG32(0x657C, 0x8EC0BF00);
-	WREG32(0x6578, 0x30301);
-	WREG32(0x657C, 0xBFC88280);
-	WREG32(0x6578, 0x30400);
-	WREG32(0x657C, 0x8DE0BEE0);
-	WREG32(0x6578, 0x30401);
-	WREG32(0x657C, 0xBFA083A0);
-	WREG32(0x6578, 0x30500);
-	WREG32(0x657C, 0x8CE0BED0);
-	WREG32(0x6578, 0x30501);
-	WREG32(0x657C, 0xBF7884E0);
-	WREG32(0x6578, 0x30600);
-	WREG32(0x657C, 0x8BA0BED8);
-	WREG32(0x6578, 0x30601);
-	WREG32(0x657C, 0xBF508640);
-	WREG32(0x6578, 0x30700);
-	WREG32(0x657C, 0x8A60BEE8);
-	WREG32(0x6578, 0x30701);
-	WREG32(0x657C, 0xBF2087A0);
-	WREG32(0x6578, 0x30800);
-	WREG32(0x657C, 0x8900BF00);
-	WREG32(0x6578, 0x30801);
-	WREG32(0x657C, 0xBF008900);
+	WREG32(0x659C + crtc->crtc_offset, 0x0);
+	WREG32(0x6594 + crtc->crtc_offset, 0x705);
+	WREG32(0x65A4 + crtc->crtc_offset, 0x10001);
+	WREG32(0x65D8 + crtc->crtc_offset, 0x0);
+	WREG32(0x65B0 + crtc->crtc_offset, 0x0);
+	WREG32(0x65C0 + crtc->crtc_offset, 0x0);
+	WREG32(0x65D4 + crtc->crtc_offset, 0x0);
+	WREG32(index_reg, 0x0);
+	WREG32(data_reg, 0x841880A8);
+	WREG32(index_reg, 0x1);
+	WREG32(data_reg, 0x84208680);
+	WREG32(index_reg, 0x2);
+	WREG32(data_reg, 0xBFF880B0);
+	WREG32(index_reg, 0x100);
+	WREG32(data_reg, 0x83D88088);
+	WREG32(index_reg, 0x101);
+	WREG32(data_reg, 0x84608680);
+	WREG32(index_reg, 0x102);
+	WREG32(data_reg, 0xBFF080D0);
+	WREG32(index_reg, 0x200);
+	WREG32(data_reg, 0x83988068);
+	WREG32(index_reg, 0x201);
+	WREG32(data_reg, 0x84A08680);
+	WREG32(index_reg, 0x202);
+	WREG32(data_reg, 0xBFF080F8);
+	WREG32(index_reg, 0x300);
+	WREG32(data_reg, 0x83588058);
+	WREG32(index_reg, 0x301);
+	WREG32(data_reg, 0x84E08660);
+	WREG32(index_reg, 0x302);
+	WREG32(data_reg, 0xBFF88120);
+	WREG32(index_reg, 0x400);
+	WREG32(data_reg, 0x83188040);
+	WREG32(index_reg, 0x401);
+	WREG32(data_reg, 0x85008660);
+	WREG32(index_reg, 0x402);
+	WREG32(data_reg, 0xBFF88150);
+	WREG32(index_reg, 0x500);
+	WREG32(data_reg, 0x82D88030);
+	WREG32(index_reg, 0x501);
+	WREG32(data_reg, 0x85408640);
+	WREG32(index_reg, 0x502);
+	WREG32(data_reg, 0xBFF88180);
+	WREG32(index_reg, 0x600);
+	WREG32(data_reg, 0x82A08018);
+	WREG32(index_reg, 0x601);
+	WREG32(data_reg, 0x85808620);
+	WREG32(index_reg, 0x602);
+	WREG32(data_reg, 0xBFF081B8);
+	WREG32(index_reg, 0x700);
+	WREG32(data_reg, 0x82608010);
+	WREG32(index_reg, 0x701);
+	WREG32(data_reg, 0x85A08600);
+	WREG32(index_reg, 0x702);
+	WREG32(data_reg, 0x800081F0);
+	WREG32(index_reg, 0x800);
+	WREG32(data_reg, 0x8228BFF8);
+	WREG32(index_reg, 0x801);
+	WREG32(data_reg, 0x85E085E0);
+	WREG32(index_reg, 0x802);
+	WREG32(data_reg, 0xBFF88228);
+	WREG32(index_reg, 0x10000);
+	WREG32(data_reg, 0x82A8BF00);
+	WREG32(index_reg, 0x10001);
+	WREG32(data_reg, 0x82A08CC0);
+	WREG32(index_reg, 0x10002);
+	WREG32(data_reg, 0x8008BEF8);
+	WREG32(index_reg, 0x10100);
+	WREG32(data_reg, 0x81F0BF28);
+	WREG32(index_reg, 0x10101);
+	WREG32(data_reg, 0x83608CA0);
+	WREG32(index_reg, 0x10102);
+	WREG32(data_reg, 0x8018BED0);
+	WREG32(index_reg, 0x10200);
+	WREG32(data_reg, 0x8148BF38);
+	WREG32(index_reg, 0x10201);
+	WREG32(data_reg, 0x84408C80);
+	WREG32(index_reg, 0x10202);
+	WREG32(data_reg, 0x8008BEB8);
+	WREG32(index_reg, 0x10300);
+	WREG32(data_reg, 0x80B0BF78);
+	WREG32(index_reg, 0x10301);
+	WREG32(data_reg, 0x85008C20);
+	WREG32(index_reg, 0x10302);
+	WREG32(data_reg, 0x8020BEA0);
+	WREG32(index_reg, 0x10400);
+	WREG32(data_reg, 0x8028BF90);
+	WREG32(index_reg, 0x10401);
+	WREG32(data_reg, 0x85E08BC0);
+	WREG32(index_reg, 0x10402);
+	WREG32(data_reg, 0x8018BE90);
+	WREG32(index_reg, 0x10500);
+	WREG32(data_reg, 0xBFB8BFB0);
+	WREG32(index_reg, 0x10501);
+	WREG32(data_reg, 0x86C08B40);
+	WREG32(index_reg, 0x10502);
+	WREG32(data_reg, 0x8010BE90);
+	WREG32(index_reg, 0x10600);
+	WREG32(data_reg, 0xBF58BFC8);
+	WREG32(index_reg, 0x10601);
+	WREG32(data_reg, 0x87A08AA0);
+	WREG32(index_reg, 0x10602);
+	WREG32(data_reg, 0x8010BE98);
+	WREG32(index_reg, 0x10700);
+	WREG32(data_reg, 0xBF10BFF0);
+	WREG32(index_reg, 0x10701);
+	WREG32(data_reg, 0x886089E0);
+	WREG32(index_reg, 0x10702);
+	WREG32(data_reg, 0x8018BEB0);
+	WREG32(index_reg, 0x10800);
+	WREG32(data_reg, 0xBED8BFE8);
+	WREG32(index_reg, 0x10801);
+	WREG32(data_reg, 0x89408940);
+	WREG32(index_reg, 0x10802);
+	WREG32(data_reg, 0xBFE8BED8);
+	WREG32(index_reg, 0x20000);
+	WREG32(data_reg, 0x80008000);
+	WREG32(index_reg, 0x20001);
+	WREG32(data_reg, 0x90008000);
+	WREG32(index_reg, 0x20002);
+	WREG32(data_reg, 0x80008000);
+	WREG32(index_reg, 0x20003);
+	WREG32(data_reg, 0x80008000);
+	WREG32(index_reg, 0x20100);
+	WREG32(data_reg, 0x80108000);
+	WREG32(index_reg, 0x20101);
+	WREG32(data_reg, 0x8FE0BF70);
+	WREG32(index_reg, 0x20102);
+	WREG32(data_reg, 0xBFE880C0);
+	WREG32(index_reg, 0x20103);
+	WREG32(data_reg, 0x80008000);
+	WREG32(index_reg, 0x20200);
+	WREG32(data_reg, 0x8018BFF8);
+	WREG32(index_reg, 0x20201);
+	WREG32(data_reg, 0x8F80BF08);
+	WREG32(index_reg, 0x20202);
+	WREG32(data_reg, 0xBFD081A0);
+	WREG32(index_reg, 0x20203);
+	WREG32(data_reg, 0xBFF88000);
+	WREG32(index_reg, 0x20300);
+	WREG32(data_reg, 0x80188000);
+	WREG32(index_reg, 0x20301);
+	WREG32(data_reg, 0x8EE0BEC0);
+	WREG32(index_reg, 0x20302);
+	WREG32(data_reg, 0xBFB082A0);
+	WREG32(index_reg, 0x20303);
+	WREG32(data_reg, 0x80008000);
+	WREG32(index_reg, 0x20400);
+	WREG32(data_reg, 0x80188000);
+	WREG32(index_reg, 0x20401);
+	WREG32(data_reg, 0x8E00BEA0);
+	WREG32(index_reg, 0x20402);
+	WREG32(data_reg, 0xBF8883C0);
+	WREG32(index_reg, 0x20403);
+	WREG32(data_reg, 0x80008000);
+	WREG32(index_reg, 0x20500);
+	WREG32(data_reg, 0x80188000);
+	WREG32(index_reg, 0x20501);
+	WREG32(data_reg, 0x8D00BE90);
+	WREG32(index_reg, 0x20502);
+	WREG32(data_reg, 0xBF588500);
+	WREG32(index_reg, 0x20503);
+	WREG32(data_reg, 0x80008008);
+	WREG32(index_reg, 0x20600);
+	WREG32(data_reg, 0x80188000);
+	WREG32(index_reg, 0x20601);
+	WREG32(data_reg, 0x8BC0BE98);
+	WREG32(index_reg, 0x20602);
+	WREG32(data_reg, 0xBF308660);
+	WREG32(index_reg, 0x20603);
+	WREG32(data_reg, 0x80008008);
+	WREG32(index_reg, 0x20700);
+	WREG32(data_reg, 0x80108000);
+	WREG32(index_reg, 0x20701);
+	WREG32(data_reg, 0x8A80BEB0);
+	WREG32(index_reg, 0x20702);
+	WREG32(data_reg, 0xBF0087C0);
+	WREG32(index_reg, 0x20703);
+	WREG32(data_reg, 0x80008008);
+	WREG32(index_reg, 0x20800);
+	WREG32(data_reg, 0x80108000);
+	WREG32(index_reg, 0x20801);
+	WREG32(data_reg, 0x8920BED0);
+	WREG32(index_reg, 0x20802);
+	WREG32(data_reg, 0xBED08920);
+	WREG32(index_reg, 0x20803);
+	WREG32(data_reg, 0x80008010);
+	WREG32(index_reg, 0x30000);
+	WREG32(data_reg, 0x90008000);
+	WREG32(index_reg, 0x30001);
+	WREG32(data_reg, 0x80008000);
+	WREG32(index_reg, 0x30100);
+	WREG32(data_reg, 0x8FE0BF90);
+	WREG32(index_reg, 0x30101);
+	WREG32(data_reg, 0xBFF880A0);
+	WREG32(index_reg, 0x30200);
+	WREG32(data_reg, 0x8F60BF40);
+	WREG32(index_reg, 0x30201);
+	WREG32(data_reg, 0xBFE88180);
+	WREG32(index_reg, 0x30300);
+	WREG32(data_reg, 0x8EC0BF00);
+	WREG32(index_reg, 0x30301);
+	WREG32(data_reg, 0xBFC88280);
+	WREG32(index_reg, 0x30400);
+	WREG32(data_reg, 0x8DE0BEE0);
+	WREG32(index_reg, 0x30401);
+	WREG32(data_reg, 0xBFA083A0);
+	WREG32(index_reg, 0x30500);
+	WREG32(data_reg, 0x8CE0BED0);
+	WREG32(index_reg, 0x30501);
+	WREG32(data_reg, 0xBF7884E0);
+	WREG32(index_reg, 0x30600);
+	WREG32(data_reg, 0x8BA0BED8);
+	WREG32(index_reg, 0x30601);
+	WREG32(data_reg, 0xBF508640);
+	WREG32(index_reg, 0x30700);
+	WREG32(data_reg, 0x8A60BEE8);
+	WREG32(index_reg, 0x30701);
+	WREG32(data_reg, 0xBF2087A0);
+	WREG32(index_reg, 0x30800);
+	WREG32(data_reg, 0x8900BF00);
+	WREG32(index_reg, 0x30801);
+	WREG32(data_reg, 0xBF008900);
 }
 
 struct rv515_watermark {