drm/i915/icl: add the main CDCLK functions
authorPaulo Zanoni <paulo.r.zanoni@intel.com>
Tue, 6 Feb 2018 19:33:46 +0000 (17:33 -0200)
committerPaulo Zanoni <paulo.r.zanoni@intel.com>
Tue, 13 Feb 2018 12:16:04 +0000 (10:16 -0200)
This commit adds the basic CDCLK functions, but it's still missing
pieces of the display initialization sequence.

v2:
 - Implement the voltage levels.
 - Rebase.
v3:
 - Adjust to the new "bypass" clock (Imre).
 - Call intel_dump_cdclk_state() too.
 - Rename a variable to avoid confusion.
 - Simplify the DVFS part.
v4:
 - Remove wrong bit definition (James).
 - Also drive-by fix the coding style for the register definition we
   touched.
v5:
 - Comment style (checkpatch).

Cc: James Ausmus <james.ausmus@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Reviewed-by: James Ausmus <james.ausmus@intel.com>
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180206193346.18272-1-paulo.r.zanoni@intel.com
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_cdclk.c
drivers/gpu/drm/i915/intel_drv.h

index 4551f17..76542f7 100644 (file)
@@ -7181,8 +7181,12 @@ enum {
 #define SKL_DFSM_PIPE_B_DISABLE                (1 << 21)
 #define SKL_DFSM_PIPE_C_DISABLE                (1 << 28)
 
-#define SKL_DSSM                       _MMIO(0x51004)
-#define CNL_DSSM_CDCLK_PLL_REFCLK_24MHz        (1 << 31)
+#define SKL_DSSM                               _MMIO(0x51004)
+#define CNL_DSSM_CDCLK_PLL_REFCLK_24MHz                (1 << 31)
+#define ICL_DSSM_CDCLK_PLL_REFCLK_MASK         (7 << 29)
+#define ICL_DSSM_CDCLK_PLL_REFCLK_24MHz                (0 << 29)
+#define ICL_DSSM_CDCLK_PLL_REFCLK_19_2MHz      (1 << 29)
+#define ICL_DSSM_CDCLK_PLL_REFCLK_38_4MHz      (2 << 29)
 
 #define GEN7_FF_SLICE_CS_CHICKEN1      _MMIO(0x20e0)
 #define   GEN9_FFSC_PERCTX_PREEMPT_CTRL        (1<<14)
@@ -8731,20 +8735,21 @@ enum skl_power_gate {
 
 /* CDCLK_CTL */
 #define CDCLK_CTL                      _MMIO(0x46000)
-#define  CDCLK_FREQ_SEL_MASK           (3<<26)
-#define  CDCLK_FREQ_450_432            (0<<26)
-#define  CDCLK_FREQ_540                        (1<<26)
-#define  CDCLK_FREQ_337_308            (2<<26)
-#define  CDCLK_FREQ_675_617            (3<<26)
-#define  BXT_CDCLK_CD2X_DIV_SEL_MASK   (3<<22)
-#define  BXT_CDCLK_CD2X_DIV_SEL_1      (0<<22)
-#define  BXT_CDCLK_CD2X_DIV_SEL_1_5    (1<<22)
-#define  BXT_CDCLK_CD2X_DIV_SEL_2      (2<<22)
-#define  BXT_CDCLK_CD2X_DIV_SEL_4      (3<<22)
-#define  BXT_CDCLK_CD2X_PIPE(pipe)     ((pipe)<<20)
-#define  CDCLK_DIVMUX_CD_OVERRIDE      (1<<19)
+#define  CDCLK_FREQ_SEL_MASK           (3 << 26)
+#define  CDCLK_FREQ_450_432            (0 << 26)
+#define  CDCLK_FREQ_540                        (1 << 26)
+#define  CDCLK_FREQ_337_308            (2 << 26)
+#define  CDCLK_FREQ_675_617            (3 << 26)
+#define  BXT_CDCLK_CD2X_DIV_SEL_MASK   (3 << 22)
+#define  BXT_CDCLK_CD2X_DIV_SEL_1      (0 << 22)
+#define  BXT_CDCLK_CD2X_DIV_SEL_1_5    (1 << 22)
+#define  BXT_CDCLK_CD2X_DIV_SEL_2      (2 << 22)
+#define  BXT_CDCLK_CD2X_DIV_SEL_4      (3 << 22)
+#define  BXT_CDCLK_CD2X_PIPE(pipe)     ((pipe) << 20)
+#define  CDCLK_DIVMUX_CD_OVERRIDE      (1 << 19)
 #define  BXT_CDCLK_CD2X_PIPE_NONE      BXT_CDCLK_CD2X_PIPE(3)
-#define  BXT_CDCLK_SSA_PRECHARGE_ENABLE        (1<<16)
+#define  ICL_CDCLK_CD2X_PIPE_NONE      (7 << 19)
+#define  BXT_CDCLK_SSA_PRECHARGE_ENABLE        (1 << 16)
 #define  CDCLK_FREQ_DECIMAL_MASK       (0x7ff)
 
 /* LCPLL_CTL */
index aab6d15..dc7db8a 100644 (file)
@@ -1778,6 +1778,199 @@ sanitize:
        dev_priv->cdclk.hw.vco = -1;
 }
 
+static int icl_calc_cdclk(int min_cdclk, unsigned int ref)
+{
+       int ranges_24[] = { 312000, 552000, 648000 };
+       int ranges_19_38[] = { 307200, 556800, 652800 };
+       int *ranges;
+
+       switch (ref) {
+       default:
+               MISSING_CASE(ref);
+       case 24000:
+               ranges = ranges_24;
+               break;
+       case 19200:
+       case 38400:
+               ranges = ranges_19_38;
+               break;
+       }
+
+       if (min_cdclk > ranges[1])
+               return ranges[2];
+       else if (min_cdclk > ranges[0])
+               return ranges[1];
+       else
+               return ranges[0];
+}
+
+static int icl_calc_cdclk_pll_vco(struct drm_i915_private *dev_priv, int cdclk)
+{
+       int ratio;
+
+       if (cdclk == dev_priv->cdclk.hw.bypass)
+               return 0;
+
+       switch (cdclk) {
+       default:
+               MISSING_CASE(cdclk);
+       case 307200:
+       case 556800:
+       case 652800:
+               WARN_ON(dev_priv->cdclk.hw.ref != 19200 &&
+                       dev_priv->cdclk.hw.ref != 38400);
+               break;
+       case 312000:
+       case 552000:
+       case 648000:
+               WARN_ON(dev_priv->cdclk.hw.ref != 24000);
+       }
+
+       ratio = cdclk / (dev_priv->cdclk.hw.ref / 2);
+
+       return dev_priv->cdclk.hw.ref * ratio;
+}
+
+static void icl_set_cdclk(struct drm_i915_private *dev_priv,
+                         const struct intel_cdclk_state *cdclk_state)
+{
+       unsigned int cdclk = cdclk_state->cdclk;
+       unsigned int vco = cdclk_state->vco;
+       int ret;
+
+       mutex_lock(&dev_priv->pcu_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->pcu_lock);
+       if (ret) {
+               DRM_ERROR("Failed to inform PCU about cdclk change (%d)\n",
+                         ret);
+               return;
+       }
+
+       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);
+
+       I915_WRITE(CDCLK_CTL, ICL_CDCLK_CD2X_PIPE_NONE |
+                             skl_cdclk_decimal(cdclk));
+
+       mutex_lock(&dev_priv->pcu_lock);
+       /* TODO: add proper DVFS support. */
+       sandybridge_pcode_write(dev_priv, SKL_PCODE_CDCLK_CONTROL, 2);
+       mutex_unlock(&dev_priv->pcu_lock);
+
+       intel_update_cdclk(dev_priv);
+}
+
+static void icl_get_cdclk(struct drm_i915_private *dev_priv,
+                         struct intel_cdclk_state *cdclk_state)
+{
+       u32 val;
+
+       cdclk_state->bypass = 50000;
+
+       val = I915_READ(SKL_DSSM);
+       switch (val & ICL_DSSM_CDCLK_PLL_REFCLK_MASK) {
+       default:
+               MISSING_CASE(val);
+       case ICL_DSSM_CDCLK_PLL_REFCLK_24MHz:
+               cdclk_state->ref = 24000;
+               break;
+       case ICL_DSSM_CDCLK_PLL_REFCLK_19_2MHz:
+               cdclk_state->ref = 19200;
+               break;
+       case ICL_DSSM_CDCLK_PLL_REFCLK_38_4MHz:
+               cdclk_state->ref = 38400;
+               break;
+       }
+
+       val = I915_READ(BXT_DE_PLL_ENABLE);
+       if ((val & BXT_DE_PLL_PLL_ENABLE) == 0 ||
+           (val & BXT_DE_PLL_LOCK) == 0) {
+               /*
+                * CDCLK PLL is disabled, the VCO/ratio doesn't matter, but
+                * setting it to zero is a way to signal that.
+                */
+               cdclk_state->vco = 0;
+               cdclk_state->cdclk = cdclk_state->bypass;
+               return;
+       }
+
+       cdclk_state->vco = (val & BXT_DE_PLL_RATIO_MASK) * cdclk_state->ref;
+
+       val = I915_READ(CDCLK_CTL);
+       WARN_ON((val & BXT_CDCLK_CD2X_DIV_SEL_MASK) != 0);
+
+       cdclk_state->cdclk = cdclk_state->vco / 2;
+}
+
+/**
+ * icl_init_cdclk - Initialize CDCLK on ICL
+ * @dev_priv: i915 device
+ *
+ * Initialize CDCLK for ICL. This consists mainly of initializing
+ * dev_priv->cdclk.hw and sanitizing the state of the hardware if needed. This
+ * is generally done only during the display core initialization sequence, after
+ * which the DMC will take care of turning CDCLK off/on as needed.
+ */
+void icl_init_cdclk(struct drm_i915_private *dev_priv)
+{
+       struct intel_cdclk_state sanitized_state;
+       u32 val;
+
+       /* This sets dev_priv->cdclk.hw. */
+       intel_update_cdclk(dev_priv);
+       intel_dump_cdclk_state(&dev_priv->cdclk.hw, "Current CDCLK");
+
+       /* This means CDCLK disabled. */
+       if (dev_priv->cdclk.hw.cdclk == dev_priv->cdclk.hw.bypass)
+               goto sanitize;
+
+       val = I915_READ(CDCLK_CTL);
+
+       if ((val & BXT_CDCLK_CD2X_DIV_SEL_MASK) != 0)
+               goto sanitize;
+
+       if ((val & CDCLK_FREQ_DECIMAL_MASK) !=
+           skl_cdclk_decimal(dev_priv->cdclk.hw.cdclk))
+               goto sanitize;
+
+       return;
+
+sanitize:
+       DRM_DEBUG_KMS("Sanitizing cdclk programmed by pre-os\n");
+
+       sanitized_state.ref = dev_priv->cdclk.hw.ref;
+       sanitized_state.cdclk = icl_calc_cdclk(0, sanitized_state.ref);
+       sanitized_state.vco = icl_calc_cdclk_pll_vco(dev_priv,
+                                                    sanitized_state.cdclk);
+
+       icl_set_cdclk(dev_priv, &sanitized_state);
+}
+
+/**
+ * icl_uninit_cdclk - Uninitialize CDCLK on ICL
+ * @dev_priv: i915 device
+ *
+ * Uninitialize CDCLK for ICL. This is done only during the display core
+ * uninitialization sequence.
+ */
+void icl_uninit_cdclk(struct drm_i915_private *dev_priv)
+{
+       struct intel_cdclk_state cdclk_state = dev_priv->cdclk.hw;
+
+       cdclk_state.cdclk = cdclk_state.bypass;
+       cdclk_state.vco = 0;
+
+       icl_set_cdclk(dev_priv, &cdclk_state);
+}
+
 /**
  * cnl_init_cdclk - Initialize CDCLK on CNL
  * @dev_priv: i915 device
@@ -2216,6 +2409,36 @@ static int cnl_modeset_calc_cdclk(struct drm_atomic_state *state)
        return 0;
 }
 
+static int icl_modeset_calc_cdclk(struct drm_atomic_state *state)
+{
+       struct drm_i915_private *dev_priv = to_i915(state->dev);
+       struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
+       unsigned int ref = intel_state->cdclk.logical.ref;
+       int min_cdclk, cdclk, vco;
+
+       min_cdclk = intel_compute_min_cdclk(state);
+       if (min_cdclk < 0)
+               return min_cdclk;
+
+       cdclk = icl_calc_cdclk(min_cdclk, ref);
+       vco = icl_calc_cdclk_pll_vco(dev_priv, cdclk);
+
+       intel_state->cdclk.logical.vco = vco;
+       intel_state->cdclk.logical.cdclk = cdclk;
+
+       if (!intel_state->active_crtcs) {
+               cdclk = icl_calc_cdclk(0, ref);
+               vco = icl_calc_cdclk_pll_vco(dev_priv, cdclk);
+
+               intel_state->cdclk.actual.vco = vco;
+               intel_state->cdclk.actual.cdclk = cdclk;
+       } else {
+               intel_state->cdclk.actual = intel_state->cdclk.logical;
+       }
+
+       return 0;
+}
+
 static int intel_compute_max_dotclk(struct drm_i915_private *dev_priv)
 {
        int max_cdclk_freq = dev_priv->max_cdclk_freq;
@@ -2249,7 +2472,12 @@ static int intel_compute_max_dotclk(struct drm_i915_private *dev_priv)
  */
 void intel_update_max_cdclk(struct drm_i915_private *dev_priv)
 {
-       if (IS_CANNONLAKE(dev_priv)) {
+       if (IS_ICELAKE(dev_priv)) {
+               if (dev_priv->cdclk.hw.ref == 24000)
+                       dev_priv->max_cdclk_freq = 648000;
+               else
+                       dev_priv->max_cdclk_freq = 652800;
+       } else if (IS_CANNONLAKE(dev_priv)) {
                dev_priv->max_cdclk_freq = 528000;
        } else if (IS_GEN9_BC(dev_priv)) {
                u32 limit = I915_READ(SKL_DFSM) & SKL_DFSM_CDCLK_LIMIT_MASK;
@@ -2473,9 +2701,14 @@ void intel_init_cdclk_hooks(struct drm_i915_private *dev_priv)
                dev_priv->display.set_cdclk = cnl_set_cdclk;
                dev_priv->display.modeset_calc_cdclk =
                        cnl_modeset_calc_cdclk;
+       } else if (IS_ICELAKE(dev_priv)) {
+               dev_priv->display.set_cdclk = icl_set_cdclk;
+               dev_priv->display.modeset_calc_cdclk = icl_modeset_calc_cdclk;
        }
 
-       if (IS_CANNONLAKE(dev_priv))
+       if (IS_ICELAKE(dev_priv))
+               dev_priv->display.get_cdclk = icl_get_cdclk;
+       else if (IS_CANNONLAKE(dev_priv))
                dev_priv->display.get_cdclk = cnl_get_cdclk;
        else if (IS_GEN9_BC(dev_priv))
                dev_priv->display.get_cdclk = skl_get_cdclk;
index f894e17..5853d92 100644 (file)
@@ -1323,6 +1323,8 @@ void cnl_init_cdclk(struct drm_i915_private *dev_priv);
 void cnl_uninit_cdclk(struct drm_i915_private *dev_priv);
 void bxt_init_cdclk(struct drm_i915_private *dev_priv);
 void bxt_uninit_cdclk(struct drm_i915_private *dev_priv);
+void icl_init_cdclk(struct drm_i915_private *dev_priv);
+void icl_uninit_cdclk(struct drm_i915_private *dev_priv);
 void intel_init_cdclk_hooks(struct drm_i915_private *dev_priv);
 void intel_update_max_cdclk(struct drm_i915_private *dev_priv);
 void intel_update_cdclk(struct drm_i915_private *dev_priv);