if (wait_for(I915_READ(LCPLL1_CTL) & LCPLL_PLL_LOCK, 5))
                DRM_ERROR("DPLL0 not locked\n");
+
+       dev_priv->skl_vco_freq = vco;
 }
 
 static void
        I915_WRITE(LCPLL1_CTL, I915_READ(LCPLL1_CTL) & ~LCPLL_PLL_ENABLE);
        if (wait_for(!(I915_READ(LCPLL1_CTL) & LCPLL_PLL_LOCK), 1))
                DRM_ERROR("Couldn't disable DPLL0\n");
+
+       dev_priv->skl_vco_freq = 0;
 }
 
 static bool skl_cdclk_pcu_ready(struct drm_i915_private *dev_priv)
        return false;
 }
 
-static void skl_set_cdclk(struct drm_i915_private *dev_priv, int cdclk)
+static void skl_set_cdclk(struct drm_i915_private *dev_priv, int cdclk, int vco)
 {
        struct drm_device *dev = dev_priv->dev;
        u32 freq_select, pcu_ack;
 
-       DRM_DEBUG_DRIVER("Changing CDCLK to %dKHz\n", cdclk);
+       WARN_ON((cdclk == 24000) != (vco == 0));
+
+       DRM_DEBUG_DRIVER("Changing CDCLK to %d kHz (VCO %d MHz)\n", cdclk, vco);
 
        if (!skl_cdclk_wait_for_pcu_ready(dev_priv)) {
                DRM_ERROR("failed to inform PCU about cdclk change\n");
                break;
        }
 
+       if (dev_priv->skl_vco_freq != 0 &&
+           dev_priv->skl_vco_freq != vco)
+               skl_dpll0_disable(dev_priv);
+
+       if (dev_priv->skl_vco_freq != vco)
+               skl_dpll0_enable(dev_priv, vco);
+
        I915_WRITE(CDCLK_CTL, freq_select | skl_cdclk_decimal(cdclk));
        POSTING_READ(CDCLK_CTL);
 
        if (I915_READ(DBUF_CTL) & DBUF_POWER_STATE)
                DRM_ERROR("DBuf power disable timeout\n");
 
-       skl_dpll0_disable(dev_priv);
+       skl_set_cdclk(dev_priv, 24000, 0);
 }
 
 void skl_init_cdclk(struct drm_i915_private *dev_priv)
 {
-       unsigned int cdclk;
-
        /* DPLL0 not enabled (happens on early BIOS versions) */
-       if (!(I915_READ(LCPLL1_CTL) & LCPLL_PLL_ENABLE)) {
-               /* enable DPLL0 */
-               if (dev_priv->skl_vco_freq != 8640)
-                       dev_priv->skl_vco_freq = 8100;
-               skl_dpll0_enable(dev_priv, dev_priv->skl_vco_freq);
-               cdclk = skl_calc_cdclk(0, dev_priv->skl_vco_freq);
-       } else {
-               cdclk = dev_priv->cdclk_freq;
-       }
+       if (dev_priv->skl_vco_freq == 0) {
+               int cdclk, vco;
 
-       /* set CDCLK to the lowest frequency, Modeset follows */
-       skl_set_cdclk(dev_priv, cdclk);
+               /* set CDCLK to the lowest frequency, Modeset follows */
+               vco = 8100;
+               cdclk = skl_calc_cdclk(0, vco);
+
+               skl_set_cdclk(dev_priv, cdclk, vco);
+       }
 
        /* enable DBUF power */
        I915_WRITE(DBUF_CTL, I915_READ(DBUF_CTL) | DBUF_POWER_REQUEST);
 
 static void skl_modeset_commit_cdclk(struct drm_atomic_state *old_state)
 {
-       struct drm_device *dev = old_state->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       unsigned int req_cdclk = to_intel_atomic_state(old_state)->dev_cdclk;
-
-       /*
-        * FIXME disable/enable PLL should wrap set_cdclk()
-        */
-       skl_set_cdclk(dev_priv, req_cdclk);
+       struct drm_i915_private *dev_priv = to_i915(old_state->dev);
+       struct intel_atomic_state *intel_state = to_intel_atomic_state(old_state);
+       unsigned int req_cdclk = intel_state->dev_cdclk;
+       unsigned int req_vco = intel_state->cdclk_pll_vco;
 
-       dev_priv->skl_vco_freq = to_intel_atomic_state(old_state)->cdclk_pll_vco;
+       skl_set_cdclk(dev_priv, req_cdclk, req_vco);
 }
 
 static int haswell_crtc_compute_clock(struct intel_crtc *crtc,