|
@@ -1452,6 +1452,112 @@ static void cnl_get_cdclk(struct drm_i915_private *dev_priv,
|
|
cdclk_state->cdclk = DIV_ROUND_CLOSEST(cdclk_state->vco, div);
|
|
cdclk_state->cdclk = DIV_ROUND_CLOSEST(cdclk_state->vco, div);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void cnl_cdclk_pll_disable(struct drm_i915_private *dev_priv)
|
|
|
|
+{
|
|
|
|
+ u32 val;
|
|
|
|
+
|
|
|
|
+ val = I915_READ(BXT_DE_PLL_ENABLE);
|
|
|
|
+ val &= ~BXT_DE_PLL_PLL_ENABLE;
|
|
|
|
+ I915_WRITE(BXT_DE_PLL_ENABLE, val);
|
|
|
|
+
|
|
|
|
+ /* Timeout 200us */
|
|
|
|
+ if (wait_for((I915_READ(BXT_DE_PLL_ENABLE) & BXT_DE_PLL_LOCK) == 0, 1))
|
|
|
|
+ DRM_ERROR("timout waiting for CDCLK PLL unlock\n");
|
|
|
|
+
|
|
|
|
+ dev_priv->cdclk.hw.vco = 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void cnl_cdclk_pll_enable(struct drm_i915_private *dev_priv, int vco)
|
|
|
|
+{
|
|
|
|
+ int ratio = DIV_ROUND_CLOSEST(vco, dev_priv->cdclk.hw.ref);
|
|
|
|
+ u32 val;
|
|
|
|
+
|
|
|
|
+ val = CNL_CDCLK_PLL_RATIO(ratio);
|
|
|
|
+ I915_WRITE(BXT_DE_PLL_ENABLE, val);
|
|
|
|
+
|
|
|
|
+ val |= BXT_DE_PLL_PLL_ENABLE;
|
|
|
|
+ I915_WRITE(BXT_DE_PLL_ENABLE, val);
|
|
|
|
+
|
|
|
|
+ /* Timeout 200us */
|
|
|
|
+ if (wait_for((I915_READ(BXT_DE_PLL_ENABLE) & BXT_DE_PLL_LOCK) != 0, 1))
|
|
|
|
+ DRM_ERROR("timout waiting for CDCLK PLL lock\n");
|
|
|
|
+
|
|
|
|
+ dev_priv->cdclk.hw.vco = vco;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+__attribute__((unused))
|
|
|
|
+static void cnl_set_cdclk(struct drm_i915_private *dev_priv,
|
|
|
|
+ const struct intel_cdclk_state *cdclk_state)
|
|
|
|
+{
|
|
|
|
+ int cdclk = cdclk_state->cdclk;
|
|
|
|
+ int vco = cdclk_state->vco;
|
|
|
|
+ u32 val, divider, pcu_ack;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&dev_priv->rps.hw_lock);
|
|
|
|
+ ret = skl_pcode_request(dev_priv, SKL_PCODE_CDCLK_CONTROL,
|
|
|
|
+ SKL_CDCLK_PREPARE_FOR_CHANGE,
|
|
|
|
+ SKL_CDCLK_READY_FOR_CHANGE,
|
|
|
|
+ SKL_CDCLK_READY_FOR_CHANGE, 3);
|
|
|
|
+ mutex_unlock(&dev_priv->rps.hw_lock);
|
|
|
|
+ if (ret) {
|
|
|
|
+ DRM_ERROR("Failed to inform PCU about cdclk change (%d)\n",
|
|
|
|
+ ret);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* cdclk = vco / 2 / div{1,2} */
|
|
|
|
+ switch (DIV_ROUND_CLOSEST(vco, cdclk)) {
|
|
|
|
+ case 4:
|
|
|
|
+ divider = BXT_CDCLK_CD2X_DIV_SEL_2;
|
|
|
|
+ break;
|
|
|
|
+ case 2:
|
|
|
|
+ divider = BXT_CDCLK_CD2X_DIV_SEL_1;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ WARN_ON(cdclk != dev_priv->cdclk.hw.ref);
|
|
|
|
+ WARN_ON(vco != 0);
|
|
|
|
+
|
|
|
|
+ divider = BXT_CDCLK_CD2X_DIV_SEL_1;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (cdclk) {
|
|
|
|
+ case 528000:
|
|
|
|
+ pcu_ack = 2;
|
|
|
|
+ break;
|
|
|
|
+ case 336000:
|
|
|
|
+ pcu_ack = 1;
|
|
|
|
+ break;
|
|
|
|
+ case 168000:
|
|
|
|
+ default:
|
|
|
|
+ pcu_ack = 0;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dev_priv->cdclk.hw.vco != 0 &&
|
|
|
|
+ dev_priv->cdclk.hw.vco != vco)
|
|
|
|
+ cnl_cdclk_pll_disable(dev_priv);
|
|
|
|
+
|
|
|
|
+ if (dev_priv->cdclk.hw.vco != vco)
|
|
|
|
+ cnl_cdclk_pll_enable(dev_priv, vco);
|
|
|
|
+
|
|
|
|
+ val = divider | skl_cdclk_decimal(cdclk);
|
|
|
|
+ /*
|
|
|
|
+ * FIXME if only the cd2x divider needs changing, it could be done
|
|
|
|
+ * without shutting off the pipe (if only one pipe is active).
|
|
|
|
+ */
|
|
|
|
+ val |= BXT_CDCLK_CD2X_PIPE_NONE;
|
|
|
|
+ I915_WRITE(CDCLK_CTL, val);
|
|
|
|
+
|
|
|
|
+ /* inform PCU of the change */
|
|
|
|
+ mutex_lock(&dev_priv->rps.hw_lock);
|
|
|
|
+ sandybridge_pcode_write(dev_priv, SKL_PCODE_CDCLK_CONTROL, pcu_ack);
|
|
|
|
+ mutex_unlock(&dev_priv->rps.hw_lock);
|
|
|
|
+
|
|
|
|
+ intel_update_cdclk(dev_priv);
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* intel_cdclk_state_compare - Determine if two CDCLK states differ
|
|
* intel_cdclk_state_compare - Determine if two CDCLK states differ
|
|
* @a: first CDCLK state
|
|
* @a: first CDCLK state
|