drm/i915: Kick the power sequencer before AUX transactions
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Thu, 16 Oct 2014 18:29:42 +0000 (21:29 +0300)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Tue, 4 Nov 2014 22:22:02 +0000 (23:22 +0100)
When we pick a new power sequencer for the port but we're not doing a
full modeset, the power sequencer may have locked on to another port (or
no port). So kick it a bit to make sure it controls the port we want.

Again just like when we attempt to actually enable the DP port, we
must first write the port register with the approriate value except
the enable bit, and then we must enable the port to make the power
sequencer happy. In this case since we don't want the port actually
enabled we just toggle it on and immediately back off. Going forward
the power sequencer will keep working on that specific port until again
moved to another port.

v2: Refine the kick procedure

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/intel_dp.c

index 8233557..4f7df37 100644 (file)
@@ -321,6 +321,52 @@ static void pps_unlock(struct intel_dp *intel_dp)
        intel_display_power_put(dev_priv, power_domain);
 }
 
+static void
+vlv_power_sequencer_kick(struct intel_dp *intel_dp)
+{
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum pipe pipe = intel_dp->pps_pipe;
+       uint32_t DP;
+
+       if (WARN(I915_READ(intel_dp->output_reg) & DP_PORT_EN,
+                "skipping pipe %c power seqeuncer kick due to port %c being active\n",
+                pipe_name(pipe), port_name(intel_dig_port->port)))
+               return;
+
+       DRM_DEBUG_KMS("kicking pipe %c power sequencer for port %c\n",
+                     pipe_name(pipe), port_name(intel_dig_port->port));
+
+       /* Preserve the BIOS-computed detected bit. This is
+        * supposed to be read-only.
+        */
+       DP = I915_READ(intel_dp->output_reg) & DP_DETECTED;
+       DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
+       DP |= DP_PORT_WIDTH(1);
+       DP |= DP_LINK_TRAIN_PAT_1;
+
+       if (IS_CHERRYVIEW(dev))
+               DP |= DP_PIPE_SELECT_CHV(pipe);
+       else if (pipe == PIPE_B)
+               DP |= DP_PIPEB_SELECT;
+
+       /*
+        * Similar magic as in intel_dp_enable_port().
+        * We _must_ do this port enable + disable trick
+        * to make this power seqeuencer lock onto the port.
+        * Otherwise even VDD force bit won't work.
+        */
+       I915_WRITE(intel_dp->output_reg, DP);
+       POSTING_READ(intel_dp->output_reg);
+
+       I915_WRITE(intel_dp->output_reg, DP | DP_PORT_EN);
+       POSTING_READ(intel_dp->output_reg);
+
+       I915_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN);
+       POSTING_READ(intel_dp->output_reg);
+}
+
 static enum pipe
 vlv_power_sequencer_pipe(struct intel_dp *intel_dp)
 {
@@ -369,6 +415,12 @@ vlv_power_sequencer_pipe(struct intel_dp *intel_dp)
        intel_dp_init_panel_power_sequencer(dev, intel_dp);
        intel_dp_init_panel_power_sequencer_registers(dev, intel_dp);
 
+       /*
+        * Even vdd force doesn't work until we've made
+        * the power sequencer lock in on the port.
+        */
+       vlv_power_sequencer_kick(intel_dp);
+
        return intel_dp->pps_pipe;
 }