drm/i915: Enable/disable TMDS output buffers in DP++ adaptor as needed
[deliverable/linux.git] / drivers / gpu / drm / i915 / intel_hdmi.c
index a0d8daed24701cf279ec6a452538eb814e092022..75cbaed3687592e200d2223562c5694be23bf77f 100644 (file)
@@ -638,7 +638,7 @@ static bool intel_hdmi_set_gcp_infoframe(struct drm_encoder *encoder)
                reg = HSW_TVIDEO_DIP_GCP(crtc->config->cpu_transcoder);
        else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
                reg = VLV_TVIDEO_DIP_GCP(crtc->pipe);
-       else if (HAS_PCH_SPLIT(dev_priv->dev))
+       else if (HAS_PCH_SPLIT(dev_priv))
                reg = TVIDEO_DIP_GCP(crtc->pipe);
        else
                return false;
@@ -836,6 +836,22 @@ static void hsw_set_infoframes(struct drm_encoder *encoder,
        intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
 }
 
+void intel_dp_dual_mode_set_tmds_output(struct intel_hdmi *hdmi, bool enable)
+{
+       struct drm_i915_private *dev_priv = to_i915(intel_hdmi_to_dev(hdmi));
+       struct i2c_adapter *adapter =
+               intel_gmbus_get_adapter(dev_priv, hdmi->ddc_bus);
+
+       if (hdmi->dp_dual_mode.type < DRM_DP_DUAL_MODE_TYPE2_DVI)
+               return;
+
+       DRM_DEBUG_KMS("%s DP dual mode adaptor TMDS output\n",
+                     enable ? "Enabling" : "Disabling");
+
+       drm_dp_dual_mode_set_tmds_output(hdmi->dp_dual_mode.type,
+                                        adapter, enable);
+}
+
 static void intel_hdmi_prepare(struct intel_encoder *encoder)
 {
        struct drm_device *dev = encoder->base.dev;
@@ -845,6 +861,8 @@ static void intel_hdmi_prepare(struct intel_encoder *encoder)
        const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode;
        u32 hdmi_val;
 
+       intel_dp_dual_mode_set_tmds_output(intel_hdmi, true);
+
        hdmi_val = SDVO_ENCODING_HDMI;
        if (!HAS_PCH_SPLIT(dev) && crtc->config->limited_color_range)
                hdmi_val |= HDMI_COLOR_RANGE_16_235;
@@ -952,9 +970,6 @@ static void intel_hdmi_get_config(struct intel_encoder *encoder,
        if (pipe_config->pixel_multiplier)
                dotclock /= pipe_config->pixel_multiplier;
 
-       if (HAS_PCH_SPLIT(dev_priv->dev))
-               ironlake_check_encoder_dotclock(pipe_config, dotclock);
-
        pipe_config->base.adjusted_mode.crtc_clock = dotclock;
 }
 
@@ -1143,6 +1158,8 @@ static void intel_disable_hdmi(struct intel_encoder *encoder)
        }
 
        intel_hdmi->set_infoframes(&encoder->base, false, NULL);
+
+       intel_dp_dual_mode_set_tmds_output(intel_hdmi, false);
 }
 
 static void g4x_disable_hdmi(struct intel_encoder *encoder)
@@ -1168,27 +1185,42 @@ static void pch_post_disable_hdmi(struct intel_encoder *encoder)
        intel_disable_hdmi(encoder);
 }
 
-static int hdmi_port_clock_limit(struct intel_hdmi *hdmi, bool respect_dvi_limit)
+static int intel_hdmi_source_max_tmds_clock(struct drm_i915_private *dev_priv)
 {
-       struct drm_device *dev = intel_hdmi_to_dev(hdmi);
-
-       if ((respect_dvi_limit && !hdmi->has_hdmi_sink) || IS_G4X(dev))
+       if (IS_G4X(dev_priv))
                return 165000;
-       else if (IS_HASWELL(dev) || INTEL_INFO(dev)->gen >= 8)
+       else if (IS_HASWELL(dev_priv) || INTEL_INFO(dev_priv)->gen >= 8)
                return 300000;
        else
                return 225000;
 }
 
+static int hdmi_port_clock_limit(struct intel_hdmi *hdmi,
+                                bool respect_downstream_limits)
+{
+       struct drm_device *dev = intel_hdmi_to_dev(hdmi);
+       int max_tmds_clock = intel_hdmi_source_max_tmds_clock(to_i915(dev));
+
+       if (respect_downstream_limits) {
+               if (hdmi->dp_dual_mode.max_tmds_clock)
+                       max_tmds_clock = min(max_tmds_clock,
+                                            hdmi->dp_dual_mode.max_tmds_clock);
+               if (!hdmi->has_hdmi_sink)
+                       max_tmds_clock = min(max_tmds_clock, 165000);
+       }
+
+       return max_tmds_clock;
+}
+
 static enum drm_mode_status
 hdmi_port_clock_valid(struct intel_hdmi *hdmi,
-                     int clock, bool respect_dvi_limit)
+                     int clock, bool respect_downstream_limits)
 {
        struct drm_device *dev = intel_hdmi_to_dev(hdmi);
 
        if (clock < 25000)
                return MODE_CLOCK_LOW;
-       if (clock > hdmi_port_clock_limit(hdmi, respect_dvi_limit))
+       if (clock > hdmi_port_clock_limit(hdmi, respect_downstream_limits))
                return MODE_CLOCK_HIGH;
 
        /* BXT DPLL can't generate 223-240 MHz */
@@ -1312,7 +1344,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
         * within limits.
         */
        if (pipe_config->pipe_bpp > 8*3 && pipe_config->has_hdmi_sink &&
-           hdmi_port_clock_valid(intel_hdmi, clock_12bpc, false) == MODE_OK &&
+           hdmi_port_clock_valid(intel_hdmi, clock_12bpc, true) == MODE_OK &&
            hdmi_12bpc_possible(pipe_config)) {
                DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n");
                desired_bpp = 12*3;
@@ -1352,10 +1384,35 @@ intel_hdmi_unset_edid(struct drm_connector *connector)
        intel_hdmi->has_audio = false;
        intel_hdmi->rgb_quant_range_selectable = false;
 
+       intel_hdmi->dp_dual_mode.type = DRM_DP_DUAL_MODE_NONE;
+       intel_hdmi->dp_dual_mode.max_tmds_clock = 0;
+
        kfree(to_intel_connector(connector)->detect_edid);
        to_intel_connector(connector)->detect_edid = NULL;
 }
 
+static void
+intel_hdmi_dp_dual_mode_detect(struct drm_connector *connector)
+{
+       struct drm_i915_private *dev_priv = to_i915(connector->dev);
+       struct intel_hdmi *hdmi = intel_attached_hdmi(connector);
+       struct i2c_adapter *adapter =
+               intel_gmbus_get_adapter(dev_priv, hdmi->ddc_bus);
+       enum drm_dp_dual_mode_type type = drm_dp_dual_mode_detect(adapter);
+
+       if (type == DRM_DP_DUAL_MODE_NONE ||
+           type == DRM_DP_DUAL_MODE_UNKNOWN)
+               return;
+
+       hdmi->dp_dual_mode.type = type;
+       hdmi->dp_dual_mode.max_tmds_clock =
+               drm_dp_dual_mode_max_tmds_clock(type, adapter);
+
+       DRM_DEBUG_KMS("DP dual mode adaptor (%s) detected (max TMDS clock: %d kHz)\n",
+                     drm_dp_get_dual_mode_type_name(type),
+                     hdmi->dp_dual_mode.max_tmds_clock);
+}
+
 static bool
 intel_hdmi_set_edid(struct drm_connector *connector, bool force)
 {
@@ -1371,6 +1428,8 @@ intel_hdmi_set_edid(struct drm_connector *connector, bool force)
                                    intel_gmbus_get_adapter(dev_priv,
                                    intel_hdmi->ddc_bus));
 
+               intel_hdmi_dp_dual_mode_detect(connector);
+
                intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
        }
 
@@ -1415,8 +1474,16 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
                                hdmi_to_dig_port(intel_hdmi));
        }
 
-       if (!live_status)
-               DRM_DEBUG_KMS("Live status not up!");
+       if (!live_status) {
+               DRM_DEBUG_KMS("HDMI live status down\n");
+               /*
+                * Live status register is not reliable on all intel platforms.
+                * So consider live_status only for certain platforms, for
+                * others, read EDID to determine presence of sink.
+                */
+               if (INTEL_INFO(dev_priv)->gen < 7 || IS_IVYBRIDGE(dev_priv))
+                       live_status = true;
+       }
 
        intel_hdmi_unset_edid(connector);
 
This page took 0.026296 seconds and 5 git commands to generate.