From 46c774f692d5c79c8b2fbff586ad8ffa10c9700d Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 26 Feb 2014 16:15:06 +0000 Subject: [PATCH] drm/i915/sdvo: Fix LVDS connector status detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is an ordering issue currently in detecting the LVDS panel as the determination depends upon whether there is a mode associated with the connector. However, we don't associate modes with the connector until we query get_modes() which is only done for a connected output. The danger is that we then rely on the EDID for detecting the output, which can be confused by attaching a VGA monitor and we erroneously associate the EDID from the VGA output for the LVDS panel. In order to provide accurate output detection, we need to query the LVDS fixed-mode during detection and remove it if the SDVO output status reports that it is ever decoupled. Reported-by: Aitor Garcia Reported-by: Wilhelm Hagemeister Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=63172 Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=71151 Signed-off-by: Chris Wilson Cc: Ville Syrjälä --- drivers/gpu/drm/i915/intel_sdvo.c | 49 ++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 825853d82a4d..3f48be0b20b8 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -52,6 +52,8 @@ #define IS_DIGITAL(c) (c->output_flag & (SDVO_TMDS_MASK | SDVO_LVDS_MASK)) +static void intel_sdvo_get_lvds_modes(struct drm_connector *connector); + static const char *tv_format_names[] = { "NTSC_M" , "NTSC_J" , "NTSC_443", "PAL_B" , "PAL_D" , "PAL_G" , @@ -773,9 +775,9 @@ intel_sdvo_create_preferred_input_timing(struct intel_sdvo *intel_sdvo, args.height = height; args.interlace = 0; - if (intel_sdvo->is_lvds && - (intel_sdvo->sdvo_lvds_fixed_mode->hdisplay != width || - intel_sdvo->sdvo_lvds_fixed_mode->vdisplay != height)) + if (intel_sdvo->sdvo_lvds_fixed_mode && + (intel_sdvo->sdvo_lvds_fixed_mode->hdisplay != width || + intel_sdvo->sdvo_lvds_fixed_mode->vdisplay != height)) args.scaled = 1; return intel_sdvo_set_value(intel_sdvo, @@ -1210,7 +1212,7 @@ static void intel_sdvo_mode_set(struct intel_encoder *intel_encoder) return; /* lvds has a special fixed output timing. */ - if (intel_sdvo->is_lvds) + if (intel_sdvo->sdvo_lvds_fixed_mode) intel_sdvo_get_dtd_from_mode(&output_dtd, intel_sdvo->sdvo_lvds_fixed_mode); else @@ -1551,7 +1553,7 @@ intel_sdvo_mode_valid(struct drm_connector *connector, if (intel_sdvo->pixel_clock_max < mode->clock) return MODE_CLOCK_HIGH; - if (intel_sdvo->is_lvds) { + if (intel_sdvo->sdvo_lvds_fixed_mode) { if (mode->hdisplay > intel_sdvo->sdvo_lvds_fixed_mode->hdisplay) return MODE_PANEL; @@ -1711,6 +1713,17 @@ intel_sdvo_tmds_sink_detect(struct drm_connector *connector) return status; } +static enum drm_connector_status +intel_sdvo_lvds_detect(struct drm_connector *connector) +{ + struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector); + + intel_sdvo_get_lvds_modes(connector); + return (intel_sdvo->sdvo_lvds_fixed_mode ? + connector_status_connected : + connector_status_disconnected); +} + static bool intel_sdvo_connector_matches_edid(struct intel_sdvo_connector *sdvo, struct edid *edid) @@ -1743,11 +1756,13 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) response & 0xff, response >> 8, intel_sdvo_connector->output_flag); - if (response == 0) - return connector_status_disconnected; - intel_sdvo->attached_output = response; + /* Discard the fixed mode, LVDS will reattach during detect */ + if (intel_sdvo->sdvo_lvds_fixed_mode != NULL) + drm_mode_destroy(connector->dev, + intel_sdvo->sdvo_lvds_fixed_mode); + intel_sdvo->has_hdmi_monitor = false; intel_sdvo->has_hdmi_audio = false; intel_sdvo->rgb_quant_range_selectable = false; @@ -1756,6 +1771,8 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) ret = connector_status_disconnected; else if (IS_TMDS(intel_sdvo_connector)) ret = intel_sdvo_tmds_sink_detect(connector); + else if (IS_LVDS(intel_sdvo_connector)) + ret = intel_sdvo_lvds_detect(connector); else { struct edid *edid; @@ -1783,7 +1800,7 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) if (response & SDVO_TV_MASK) intel_sdvo->is_tv = true; if (response & SDVO_LVDS_MASK) - intel_sdvo->is_lvds = intel_sdvo->sdvo_lvds_fixed_mode != NULL; + intel_sdvo->is_lvds = true; } return ret; @@ -1953,13 +1970,13 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector) */ intel_ddc_get_modes(connector, &intel_sdvo->ddc); - list_for_each_entry(newmode, &connector->probed_modes, head) { - if (newmode->type & DRM_MODE_TYPE_PREFERRED) { - intel_sdvo->sdvo_lvds_fixed_mode = - drm_mode_duplicate(connector->dev, newmode); - - intel_sdvo->is_lvds = true; - break; + if (intel_sdvo->sdvo_lvds_fixed_mode == NULL) { + list_for_each_entry(newmode, &connector->probed_modes, head) { + if (newmode->type & DRM_MODE_TYPE_PREFERRED) { + intel_sdvo->sdvo_lvds_fixed_mode = + drm_mode_duplicate(connector->dev, newmode); + break; + } } } } -- 1.9.0