drm/i915/icl: Sanitize DDI port clock gating for DSI ports
authorImre Deak <imre.deak@intel.com>
Thu, 29 Nov 2018 14:12:17 +0000 (16:12 +0200)
committerJani Nikula <jani.nikula@intel.com>
Mon, 3 Dec 2018 13:52:47 +0000 (15:52 +0200)
The requirement for the DDI port clock gating for a port in DSI mode is
the opposite wrt. the case when the port is in DDI mode: the clock
should be gated when the port is active and ungated when the port is
inactive. Note that we cannot simply keep the DDI clock gated when the
port will be only used in DSI mode: it must be gated/ungated at a
specific spot in the DSI enable/disable sequence.

Ensure the above for all ports of a DSI encoder, also adding a sanity
check that we haven't registered another encoder using the same port
(VBT should never allow this to happen).

Cc: Madhav Chauhan <madhav.chauhan@intel.com>
Cc: Vandita Kulkarni <vandita.kulkarni@intel.com>
Cc: Jani Nikula <jani.nikula@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Clint Taylor <clinton.a.taylor@intel.com>
Reviewed-by: Jani Nikula <jani.nikula@intel.com>
Signed-off-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Jani Nikula <jani.nikula@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/ceb14d5a68e8e23988d923d4290a4f981789e616.1543500285.git.jani.nikula@intel.com
drivers/gpu/drm/i915/intel_ddi.c
drivers/gpu/drm/i915/intel_dsi.h

index d37ac95bc23d1389bd66769253d59f9321f58368..6308d0f151e128e20342f3369e240ef9aff555a1 100644 (file)
@@ -28,6 +28,7 @@
 #include <drm/drm_scdc_helper.h>
 #include "i915_drv.h"
 #include "intel_drv.h"
+#include "intel_dsi.h"
 
 struct ddi_buf_trans {
        u32 trans1;     /* balance leg enable, de-emph level */
@@ -2836,8 +2837,9 @@ void icl_sanitize_encoder_pll_mapping(struct intel_encoder *encoder)
 {
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        u32 val;
-       enum port port = encoder->port;
-       bool clk_enabled;
+       enum port port;
+       u32 port_mask;
+       bool ddi_clk_needed;
 
        /*
         * In case of DP MST, we sanitize the primary encoder only, not the
@@ -2846,9 +2848,6 @@ void icl_sanitize_encoder_pll_mapping(struct intel_encoder *encoder)
        if (encoder->type == INTEL_OUTPUT_DP_MST)
                return;
 
-       val = I915_READ(DPCLKA_CFGCR0_ICL);
-       clk_enabled = !(val & icl_dpclka_cfgcr0_clk_off(dev_priv, port));
-
        if (!encoder->base.crtc && intel_encoder_is_dp(encoder)) {
                u8 pipe_mask;
                bool is_mst;
@@ -2862,20 +2861,52 @@ void icl_sanitize_encoder_pll_mapping(struct intel_encoder *encoder)
                        return;
        }
 
-       if (clk_enabled == !!encoder->base.crtc)
-               return;
+       port_mask = BIT(encoder->port);
+       ddi_clk_needed = encoder->base.crtc;
 
-       /*
-        * Punt on the case now where clock is disabled, but the encoder is
-        * enabled, something else is really broken then.
-        */
-       if (WARN_ON(!clk_enabled))
-               return;
+       if (encoder->type == INTEL_OUTPUT_DSI) {
+               struct intel_encoder *other_encoder;
 
-       DRM_NOTE("Port %c is disabled but it has a mapped PLL, unmap it\n",
-                port_name(port));
-       val |= icl_dpclka_cfgcr0_clk_off(dev_priv, port);
-       I915_WRITE(DPCLKA_CFGCR0_ICL, val);
+               port_mask = intel_dsi_encoder_ports(encoder);
+               /*
+                * Sanity check that we haven't incorrectly registered another
+                * encoder using any of the ports of this DSI encoder.
+                */
+               for_each_intel_encoder(&dev_priv->drm, other_encoder) {
+                       if (other_encoder == encoder)
+                               continue;
+
+                       if (WARN_ON(port_mask & BIT(other_encoder->port)))
+                               return;
+               }
+               /*
+                * DSI ports should have their DDI clock ungated when disabled
+                * and gated when enabled.
+                */
+               ddi_clk_needed = !encoder->base.crtc;
+       }
+
+       val = I915_READ(DPCLKA_CFGCR0_ICL);
+       for_each_port_masked(port, port_mask) {
+               bool ddi_clk_ungated = !(val &
+                                        icl_dpclka_cfgcr0_clk_off(dev_priv,
+                                                                  port));
+
+               if (ddi_clk_needed == ddi_clk_ungated)
+                       continue;
+
+               /*
+                * Punt on the case now where clock is gated, but it would
+                * be needed by the port. Something else is really broken then.
+                */
+               if (WARN_ON(ddi_clk_needed))
+                       continue;
+
+               DRM_NOTE("Port %c is disabled/in DSI mode with an ungated DDI clock, gate it\n",
+                        port_name(port));
+               val |= icl_dpclka_cfgcr0_clk_off(dev_priv, port);
+               I915_WRITE(DPCLKA_CFGCR0_ICL, val);
+       }
 }
 
 static void intel_ddi_clk_select(struct intel_encoder *encoder,
index ee93137f4433d3c3b7a482d7ea428f6a92fb602d..d968f1f13e0923b7793d5ed944bc9a1fc5049c23 100644 (file)
@@ -146,6 +146,11 @@ static inline bool is_cmd_mode(struct intel_dsi *intel_dsi)
        return intel_dsi->operation_mode == INTEL_DSI_COMMAND_MODE;
 }
 
+static inline u16 intel_dsi_encoder_ports(struct intel_encoder *encoder)
+{
+       return enc_to_intel_dsi(&encoder->base)->ports;
+}
+
 /* intel_dsi.c */
 int intel_dsi_bitrate(const struct intel_dsi *intel_dsi);
 int intel_dsi_tlpx_ns(const struct intel_dsi *intel_dsi);