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/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: