drm/i915: fix up pch pll handling in ->mode_set
authorDaniel Vetter <daniel.vetter@ffwll.ch>
Wed, 5 Jun 2013 11:34:03 +0000 (13:34 +0200)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Mon, 10 Jun 2013 17:44:40 +0000 (19:44 +0200)
We ->mode_set is called we can't just blindly reuse an existing pll
since that might be shared with a different, still active pch output.

v2: Only update the pll settings when the pch pll is know to be
unused, otherwise we can wreak havoc with a running pipe. Which in the
case of DP will likely result in a black screen due to loss of link
lock.

v3: Tighten up the asserts a bit more, especially make sure that the
pch pll is still enabled when we try to disable it. This would have
caught the bug fixed in this patch.

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

index 6b37b75..1d0aa24 100644 (file)
@@ -1427,7 +1427,8 @@ static void ironlake_enable_pch_pll(struct intel_crtc *intel_crtc)
        /* PCH refclock must be enabled first */
        assert_pch_refclk_enabled(dev_priv);
 
-       if (pll->active++ && pll->on) {
+       if (pll->active++) {
+               WARN_ON(!pll->on);
                assert_pch_pll_enabled(dev_priv, pll, NULL);
                return;
        }
@@ -1468,10 +1469,9 @@ static void intel_disable_pch_pll(struct intel_crtc *intel_crtc)
                return;
        }
 
-       if (--pll->active) {
-               assert_pch_pll_enabled(dev_priv, pll, NULL);
+       assert_pch_pll_enabled(dev_priv, pll, NULL);
+       if (--pll->active)
                return;
-       }
 
        DRM_DEBUG_KMS("disabling PCH PLL %x\n", pll->pll_reg);
 
@@ -3081,9 +3081,9 @@ static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u3
 
        pll = intel_crtc->pch_pll;
        if (pll) {
-               DRM_DEBUG_KMS("CRTC:%d reusing existing PCH PLL %x\n",
+               DRM_DEBUG_KMS("CRTC:%d dropping existing PCH PLL %x\n",
                              intel_crtc->base.base.id, pll->pll_reg);
-               goto prepare;
+               intel_put_pch_pll(intel_crtc);
        }
 
        if (HAS_PCH_IBX(dev_priv->dev)) {
@@ -3128,19 +3128,22 @@ static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u3
 
 found:
        intel_crtc->pch_pll = pll;
-       pll->refcount++;
        DRM_DEBUG_DRIVER("using pll %d for pipe %c\n", i, pipe_name(intel_crtc->pipe));
-prepare: /* separate function? */
-       DRM_DEBUG_DRIVER("switching PLL %x off\n", pll->pll_reg);
+       if (pll->active == 0) {
+               DRM_DEBUG_DRIVER("setting up pll %d\n", i);
+               WARN_ON(pll->on);
+               assert_pch_pll_disabled(dev_priv, pll, NULL);
 
-       /* Wait for the clocks to stabilize before rewriting the regs */
-       I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE);
-       POSTING_READ(pll->pll_reg);
-       udelay(150);
+               /* Wait for the clocks to stabilize before rewriting the regs */
+               I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE);
+               POSTING_READ(pll->pll_reg);
+               udelay(150);
+
+               I915_WRITE(pll->fp0_reg, fp);
+               I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE);
+       }
+       pll->refcount++;
 
-       I915_WRITE(pll->fp0_reg, fp);
-       I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE);
-       pll->on = false;
        return pll;
 }