drm/i915/hdmi: Turn DP++ TMDS output buffers back on in encoder->shutdown()
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Fri, 29 Oct 2021 19:18:02 +0000 (22:18 +0300)
committerVille Syrjälä <ville.syrjala@linux.intel.com>
Mon, 1 Nov 2021 12:15:18 +0000 (14:15 +0200)
Looks like our VBIOS/GOP generally fail to turn the DP dual mode adater
TMDS output buffers back on after a reboot. This leads to a black screen
after reboot if we turned the TMDS output buffers off prior to reboot.
And if i915 decides to do a fastboot the black screen will persist even
after i915 takes over.

Apparently this has been a problem ever since commit b2ccb822d376 ("drm/i915:
Enable/disable TMDS output buffers in DP++ adaptor as needed") if one
rebooted while the display was turned off. And things became worse with
commit fe0f1e3bfdfe ("drm/i915: Shut down displays gracefully on reboot")
since now we always turn the display off before a reboot.

This was reported on a RKL, but I confirmed the same behaviour on my
SNB as well. So looks pretty universal.

Let's fix this by explicitly turning the TMDS output buffers back on
in the encoder->shutdown() hook. Note that this gets called after irqs
have been disabled, so the i2c communication with the DP dual mode
adapter has to be performed via polling (which the gmbus code is
perfectly happy to do for us).

We also need a bit of care in handling DDI encoders which may or may
not be set up for HDMI output. Specifically ddc_pin will not be
populated for a DP only DDI encoder, in which case we don't want to
call intel_gmbus_get_adapter(). We can handle that by simply doing
the dual mode adapter type check before calling
intel_gmbus_get_adapter().

Cc: <stable@vger.kernel.org> # v5.11+
Fixes: fe0f1e3bfdfe ("drm/i915: Shut down displays gracefully on reboot")
Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/4371
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20211029191802.18448-2-ville.syrjala@linux.intel.com
Reviewed-by: Stanislav Lisovskiy <stanislav.lisovskiy@intel.com>
drivers/gpu/drm/i915/display/g4x_hdmi.c
drivers/gpu/drm/i915/display/intel_ddi.c
drivers/gpu/drm/i915/display/intel_hdmi.c
drivers/gpu/drm/i915/display/intel_hdmi.h

index 88c427f3c3467b19838f3a5b64898013ed214c14..f5b4dd5b42757a74dd05674c953bb98acb69cecd 100644 (file)
@@ -584,6 +584,7 @@ void g4x_hdmi_init(struct drm_i915_private *dev_priv,
                else
                        intel_encoder->enable = g4x_enable_hdmi;
        }
+       intel_encoder->shutdown = intel_hdmi_encoder_shutdown;
 
        intel_encoder->type = INTEL_OUTPUT_HDMI;
        intel_encoder->power_domain = intel_port_to_power_domain(port);
index 9fb99b09fff8bf2a1c4e9fdc7f375a8f0190e698..5ef2882727e194317f7fc67ab404efc51544d1e8 100644 (file)
@@ -4312,6 +4312,7 @@ static void intel_ddi_encoder_shutdown(struct intel_encoder *encoder)
        enum phy phy = intel_port_to_phy(i915, encoder->port);
 
        intel_dp_encoder_shutdown(encoder);
+       intel_hdmi_encoder_shutdown(encoder);
 
        if (!intel_phy_is_tc(i915, phy))
                return;
index 7e6af959bf8384015252fb81cd32a89c5840ef9f..3b5b9e7b05b7b0d24c73dd99d1cc5d9b54242adf 100644 (file)
@@ -1246,12 +1246,13 @@ static void hsw_set_infoframes(struct intel_encoder *encoder,
 void intel_dp_dual_mode_set_tmds_output(struct intel_hdmi *hdmi, bool enable)
 {
        struct drm_i915_private *dev_priv = intel_hdmi_to_i915(hdmi);
-       struct i2c_adapter *adapter =
-               intel_gmbus_get_adapter(dev_priv, hdmi->ddc_bus);
+       struct i2c_adapter *adapter;
 
        if (hdmi->dp_dual_mode.type < DRM_DP_DUAL_MODE_TYPE2_DVI)
                return;
 
+       adapter = intel_gmbus_get_adapter(dev_priv, hdmi->ddc_bus);
+
        drm_dbg_kms(&dev_priv->drm, "%s DP dual mode adaptor TMDS output\n",
                    enable ? "Enabling" : "Disabling");
 
@@ -2285,6 +2286,17 @@ int intel_hdmi_compute_config(struct intel_encoder *encoder,
        return 0;
 }
 
+void intel_hdmi_encoder_shutdown(struct intel_encoder *encoder)
+{
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+
+       /*
+        * Give a hand to buggy BIOSen which forget to turn
+        * the TMDS output buffers back on after a reboot.
+        */
+       intel_dp_dual_mode_set_tmds_output(intel_hdmi, true);
+}
+
 static void
 intel_hdmi_unset_edid(struct drm_connector *connector)
 {
index b43a180d007e015f19986cf79ff3ad6553232dbd..2bf440eb400ab1bef70af51556b78dc11ee44cfb 100644 (file)
@@ -28,6 +28,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *dig_port,
 int intel_hdmi_compute_config(struct intel_encoder *encoder,
                              struct intel_crtc_state *pipe_config,
                              struct drm_connector_state *conn_state);
+void intel_hdmi_encoder_shutdown(struct intel_encoder *encoder);
 bool intel_hdmi_handle_sink_scrambling(struct intel_encoder *encoder,
                                       struct drm_connector *connector,
                                       bool high_tmds_clock_ratio,