diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_sdvo.c')
| -rw-r--r-- | drivers/gpu/drm/i915/intel_sdvo.c | 252 | 
1 files changed, 171 insertions, 81 deletions
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index abef69c8a49..91238f0c39a 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -31,6 +31,7 @@  #include "drm.h"  #include "drm_crtc.h"  #include "intel_drv.h" +#include "drm_edid.h"  #include "i915_drm.h"  #include "i915_drv.h"  #include "intel_sdvo_regs.h" @@ -54,6 +55,12 @@ struct intel_sdvo_priv {  	/* Pixel clock limitations reported by the SDVO device, in kHz */  	int pixel_clock_min, pixel_clock_max; +	/* +	* For multiple function SDVO device, +	* this is for current attached outputs. +	*/ +	uint16_t attached_output; +  	/**  	 * This is set if we're going to treat the device as TV-out.  	 * @@ -113,6 +120,9 @@ struct intel_sdvo_priv {  	u32 save_SDVOX;  }; +static bool +intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags); +  /**   * Writes the SDVOB or SDVOC with the given value, but always writes both   * SDVOB and SDVOC to work around apparent hardware issues (according to @@ -1434,41 +1444,96 @@ void intel_sdvo_set_hotplug(struct drm_connector *connector, int on)  	intel_sdvo_read_response(intel_output, &response, 2);  } -static void -intel_sdvo_hdmi_sink_detect(struct drm_connector *connector) +static bool +intel_sdvo_multifunc_encoder(struct intel_output *intel_output) +{ +	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; +	int caps = 0; + +	if (sdvo_priv->caps.output_flags & +		(SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)) +		caps++; +	if (sdvo_priv->caps.output_flags & +		(SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1)) +		caps++; +	if (sdvo_priv->caps.output_flags & +		(SDVO_OUTPUT_SVID0 | SDVO_OUTPUT_SVID0)) +		caps++; +	if (sdvo_priv->caps.output_flags & +		(SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_CVBS1)) +		caps++; +	if (sdvo_priv->caps.output_flags & +		(SDVO_OUTPUT_YPRPB0 | SDVO_OUTPUT_YPRPB1)) +		caps++; + +	if (sdvo_priv->caps.output_flags & +		(SDVO_OUTPUT_SCART0 | SDVO_OUTPUT_SCART1)) +		caps++; + +	if (sdvo_priv->caps.output_flags & +		(SDVO_OUTPUT_LVDS0 | SDVO_OUTPUT_LVDS1)) +		caps++; + +	return (caps > 1); +} + +enum drm_connector_status +intel_sdvo_hdmi_sink_detect(struct drm_connector *connector, u16 response)  {  	struct intel_output *intel_output = to_intel_output(connector);  	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; +	enum drm_connector_status status = connector_status_connected;  	struct edid *edid = NULL;  	edid = drm_get_edid(&intel_output->base,  			    intel_output->ddc_bus);  	if (edid != NULL) { -		sdvo_priv->is_hdmi = drm_detect_hdmi_monitor(edid); +		/* Don't report the output as connected if it's a DVI-I +		 * connector with a non-digital EDID coming out. +		 */ +		if (response & (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)) { +			if (edid->input & DRM_EDID_INPUT_DIGITAL) +				sdvo_priv->is_hdmi = +					drm_detect_hdmi_monitor(edid); +			else +				status = connector_status_disconnected; +		} +  		kfree(edid);  		intel_output->base.display_info.raw_edid = NULL; -	} + +	} else if (response & (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)) +		status = connector_status_disconnected; + +	return status;  }  static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connector)  { -	u8 response[2]; +	uint16_t response;  	u8 status;  	struct intel_output *intel_output = to_intel_output(connector); +	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;  	intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0);  	status = intel_sdvo_read_response(intel_output, &response, 2); -	DRM_DEBUG_KMS("SDVO response %d %d\n", response[0], response[1]); +	DRM_DEBUG_KMS("SDVO response %d %d\n", response & 0xff, response >> 8);  	if (status != SDVO_CMD_STATUS_SUCCESS)  		return connector_status_unknown; -	if ((response[0] != 0) || (response[1] != 0)) { -		intel_sdvo_hdmi_sink_detect(connector); -		return connector_status_connected; -	} else +	if (response == 0)  		return connector_status_disconnected; + +	if (intel_sdvo_multifunc_encoder(intel_output) && +		sdvo_priv->attached_output != response) { +		if (sdvo_priv->controlled_output != response && +			intel_sdvo_output_setup(intel_output, response) != true) +			return connector_status_unknown; +		sdvo_priv->attached_output = response; +	} +	return intel_sdvo_hdmi_sink_detect(connector, response);  }  static void intel_sdvo_get_ddc_modes(struct drm_connector *connector) @@ -1865,16 +1930,100 @@ intel_sdvo_get_slave_addr(struct drm_device *dev, int output_device)  		return 0x72;  } +static bool +intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags) +{ +	struct drm_connector *connector = &intel_output->base; +	struct drm_encoder *encoder = &intel_output->enc; +	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; +	bool ret = true, registered = false; + +	sdvo_priv->is_tv = false; +	intel_output->needs_tv_clock = false; +	sdvo_priv->is_lvds = false; + +	if (device_is_registered(&connector->kdev)) { +		drm_sysfs_connector_remove(connector); +		registered = true; +	} + +	if (flags & +	    (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)) { +		if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0) +			sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS0; +		else +			sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS1; + +		encoder->encoder_type = DRM_MODE_ENCODER_TMDS; +		connector->connector_type = DRM_MODE_CONNECTOR_DVID; + +		if (intel_sdvo_get_supp_encode(intel_output, +					       &sdvo_priv->encode) && +		    intel_sdvo_get_digital_encoding_mode(intel_output) && +		    sdvo_priv->is_hdmi) { +			/* enable hdmi encoding mode if supported */ +			intel_sdvo_set_encode(intel_output, SDVO_ENCODE_HDMI); +			intel_sdvo_set_colorimetry(intel_output, +						   SDVO_COLORIMETRY_RGB256); +			connector->connector_type = DRM_MODE_CONNECTOR_HDMIA; +		} +	} else if (flags & SDVO_OUTPUT_SVID0) { + +		sdvo_priv->controlled_output = SDVO_OUTPUT_SVID0; +		encoder->encoder_type = DRM_MODE_ENCODER_TVDAC; +		connector->connector_type = DRM_MODE_CONNECTOR_SVIDEO; +		sdvo_priv->is_tv = true; +		intel_output->needs_tv_clock = true; +	} else if (flags & SDVO_OUTPUT_RGB0) { + +		sdvo_priv->controlled_output = SDVO_OUTPUT_RGB0; +		encoder->encoder_type = DRM_MODE_ENCODER_DAC; +		connector->connector_type = DRM_MODE_CONNECTOR_VGA; +	} else if (flags & SDVO_OUTPUT_RGB1) { + +		sdvo_priv->controlled_output = SDVO_OUTPUT_RGB1; +		encoder->encoder_type = DRM_MODE_ENCODER_DAC; +		connector->connector_type = DRM_MODE_CONNECTOR_VGA; +	} else if (flags & SDVO_OUTPUT_LVDS0) { + +		sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS0; +		encoder->encoder_type = DRM_MODE_ENCODER_LVDS; +		connector->connector_type = DRM_MODE_CONNECTOR_LVDS; +		sdvo_priv->is_lvds = true; +	} else if (flags & SDVO_OUTPUT_LVDS1) { + +		sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS1; +		encoder->encoder_type = DRM_MODE_ENCODER_LVDS; +		connector->connector_type = DRM_MODE_CONNECTOR_LVDS; +		sdvo_priv->is_lvds = true; +	} else { + +		unsigned char bytes[2]; + +		sdvo_priv->controlled_output = 0; +		memcpy(bytes, &sdvo_priv->caps.output_flags, 2); +		DRM_DEBUG_KMS("%s: Unknown SDVO output type (0x%02x%02x)\n", +			      SDVO_NAME(sdvo_priv), +			      bytes[0], bytes[1]); +		ret = false; +	} + +	if (ret && registered) +		ret = drm_sysfs_connector_add(connector) == 0 ? true : false; + + +	return ret; + +} +  bool intel_sdvo_init(struct drm_device *dev, int output_device)  {  	struct drm_connector *connector;  	struct intel_output *intel_output;  	struct intel_sdvo_priv *sdvo_priv; -	int connector_type;  	u8 ch[0x40];  	int i; -	int encoder_type;  	intel_output = kcalloc(sizeof(struct intel_output)+sizeof(struct intel_sdvo_priv), 1, GFP_KERNEL);  	if (!intel_output) { @@ -1923,87 +2072,28 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)  	intel_output->ddc_bus->algo = &intel_sdvo_i2c_bit_algo;  	/* In defaut case sdvo lvds is false */ -	sdvo_priv->is_lvds = false;  	intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps); -	if (sdvo_priv->caps.output_flags & -	    (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)) { -		if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0) -			sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS0; -		else -			sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS1; - -		encoder_type = DRM_MODE_ENCODER_TMDS; -		connector_type = DRM_MODE_CONNECTOR_DVID; - -		if (intel_sdvo_get_supp_encode(intel_output, -					       &sdvo_priv->encode) && -		    intel_sdvo_get_digital_encoding_mode(intel_output) && -		    sdvo_priv->is_hdmi) { -			/* enable hdmi encoding mode if supported */ -			intel_sdvo_set_encode(intel_output, SDVO_ENCODE_HDMI); -			intel_sdvo_set_colorimetry(intel_output, -						   SDVO_COLORIMETRY_RGB256); -			connector_type = DRM_MODE_CONNECTOR_HDMIA; -		} -	} -	else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_SVID0) -	{ -		sdvo_priv->controlled_output = SDVO_OUTPUT_SVID0; -		encoder_type = DRM_MODE_ENCODER_TVDAC; -		connector_type = DRM_MODE_CONNECTOR_SVIDEO; -		sdvo_priv->is_tv = true; -		intel_output->needs_tv_clock = true; -	} -	else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0) -	{ -		sdvo_priv->controlled_output = SDVO_OUTPUT_RGB0; -		encoder_type = DRM_MODE_ENCODER_DAC; -		connector_type = DRM_MODE_CONNECTOR_VGA; -	} -	else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1) -	{ -		sdvo_priv->controlled_output = SDVO_OUTPUT_RGB1; -		encoder_type = DRM_MODE_ENCODER_DAC; -		connector_type = DRM_MODE_CONNECTOR_VGA; -	} -	else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS0) -	{ -		sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS0; -		encoder_type = DRM_MODE_ENCODER_LVDS; -		connector_type = DRM_MODE_CONNECTOR_LVDS; -		sdvo_priv->is_lvds = true; -	} -	else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS1) -	{ -		sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS1; -		encoder_type = DRM_MODE_ENCODER_LVDS; -		connector_type = DRM_MODE_CONNECTOR_LVDS; -		sdvo_priv->is_lvds = true; -	} -	else -	{ -		unsigned char bytes[2]; - -		sdvo_priv->controlled_output = 0; -		memcpy (bytes, &sdvo_priv->caps.output_flags, 2); -		DRM_DEBUG_KMS("%s: Unknown SDVO output type (0x%02x%02x)\n", -				  SDVO_NAME(sdvo_priv), -				  bytes[0], bytes[1]); -		encoder_type = DRM_MODE_ENCODER_NONE; -		connector_type = DRM_MODE_CONNECTOR_Unknown; +	if (intel_sdvo_output_setup(intel_output, +				    sdvo_priv->caps.output_flags) != true) { +		DRM_DEBUG_KMS("SDVO output failed to setup on SDVO%c\n", +			  output_device == SDVOB ? 'B' : 'C');  		goto err_i2c;  	} +  	connector = &intel_output->base;  	drm_connector_init(dev, connector, &intel_sdvo_connector_funcs, -			   connector_type); +			   connector->connector_type); +  	drm_connector_helper_add(connector, &intel_sdvo_connector_helper_funcs);  	connector->interlace_allowed = 0;  	connector->doublescan_allowed = 0;  	connector->display_info.subpixel_order = SubPixelHorizontalRGB; -	drm_encoder_init(dev, &intel_output->enc, &intel_sdvo_enc_funcs, encoder_type); +	drm_encoder_init(dev, &intel_output->enc, +			&intel_sdvo_enc_funcs, intel_output->enc.encoder_type); +  	drm_encoder_helper_add(&intel_output->enc, &intel_sdvo_helper_funcs);  	drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);  |