diff mbox

[29/67] drm/i915/cnl: Implement .set_cdclk() for CNL

Message ID 1491506163-14587-29-git-send-email-rodrigo.vivi@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Rodrigo Vivi April 6, 2017, 7:15 p.m. UTC
From: Ville Syrjälä <ville.syrjala@linux.intel.com>

Add support for changing the cdclk frequency on CNL. Again, quite
similar to BXT, but there are some annoying differences which means
trying to share more code might not be feasible:
* PLL ratio now lives in the PLL enable register
* pcode came from SKL, not from BXT

We support three cdclk frequencies: 168,336,528 Mhz. The first two
use the same PLL frequency, the last one uses a different one meaning
we once again may need to toggle the PLL off and on when changing
cdclk.

v2: Rebased by Rodrigo on top of Ville's cdclk rework.
v3: Respect order of set_ bellow get_ (Ville)

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
---
 drivers/gpu/drm/i915/intel_cdclk.c | 105 +++++++++++++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)

Comments

Imre Deak June 5, 2017, 1:11 p.m. UTC | #1
On Thu, Apr 06, 2017 at 12:15:25PM -0700, Rodrigo Vivi wrote:
> From: Ville Syrjälä <ville.syrjala@linux.intel.com>
> 
> Add support for changing the cdclk frequency on CNL. Again, quite
> similar to BXT, but there are some annoying differences which means
> trying to share more code might not be feasible:
> * PLL ratio now lives in the PLL enable register
> * pcode came from SKL, not from BXT
> 
> We support three cdclk frequencies: 168,336,528 Mhz. The first two
> use the same PLL frequency, the last one uses a different one meaning
> we once again may need to toggle the PLL off and on when changing
> cdclk.
> 
> v2: Rebased by Rodrigo on top of Ville's cdclk rework.
> v3: Respect order of set_ bellow get_ (Ville)
> 
> Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>

Reviewed-by: Imre Deak <imre.deak@intel.com>

> ---
>  drivers/gpu/drm/i915/intel_cdclk.c | 105 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 105 insertions(+)
> 
> diff --git a/drivers/gpu/drm/i915/intel_cdclk.c b/drivers/gpu/drm/i915/intel_cdclk.c
> index a4e2bd5..bee4394 100644
> --- a/drivers/gpu/drm/i915/intel_cdclk.c
> +++ b/drivers/gpu/drm/i915/intel_cdclk.c
> @@ -1450,6 +1450,111 @@ static void cnl_get_cdclk(struct drm_i915_private *dev_priv,
>  	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;
> +}
> +
> +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
>   * @a: first CDCLK state
> -- 
> 1.9.1
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/intel_cdclk.c b/drivers/gpu/drm/i915/intel_cdclk.c
index a4e2bd5..bee4394 100644
--- a/drivers/gpu/drm/i915/intel_cdclk.c
+++ b/drivers/gpu/drm/i915/intel_cdclk.c
@@ -1450,6 +1450,111 @@  static void cnl_get_cdclk(struct drm_i915_private *dev_priv,
 	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;
+}
+
+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
  * @a: first CDCLK state