drm/i915/cnl: Initialize PLLs
authorRodrigo Vivi <rodrigo.vivi@intel.com>
Fri, 9 Jun 2017 22:26:04 +0000 (15:26 -0700)
committerRodrigo Vivi <rodrigo.vivi@intel.com>
Mon, 12 Jun 2017 16:42:18 +0000 (09:42 -0700)
Although CNL follows PLL initialization more like Skylake
than Broxton we have a completely different initialization
sequence and registers used.

One big difference from SKL is that CDCLK PLL is now
exclusive (ADPLL) and for DDIs and MIPI we need to use
DFGPLLs 0, 1 or 2.

v2: Accept all Ander's suggestions and fixes:
    - Registers and bits names prefix
    - Group pll functions
    - bits masks fixes
    - remove read and modify on cfgcr1
    - fix cfgcr0 setup
v3: Set SSC_ENABLE for DP.
    Fix HDMI_MODE cfgcr0.
    Avoid touch cfgcr0 on DP.
    Add missed else on dpll_mgr definition so we use cnl one, not hsw.
v3: Centra freq should be always set to default and change bits
    definitions to (1 << 1) instead of (1<<1). (by Paulo)
v4: Rebased.

Cc: Paulo Zanoni <paulo.r.zanoni@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Kahola, Mika <mika.kahola@intel.com>
Reviewed-by: Ander Conselvan De Oliveira <ander.conselvan.de.oliveira@intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1497047175-27250-7-git-send-email-rodrigo.vivi@intel.com
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_dpll_mgr.c
drivers/gpu/drm/i915/intel_dpll_mgr.h

index f9e329ada437c23a1491aa492094a658a532d982..9421915cc0f508ed58140597ef289ebff11f5753 100644 (file)
@@ -60,6 +60,8 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
 #define _MMIO_PORT(port, a, b) _MMIO(_PORT(port, a, b))
 #define _MMIO_PIPE3(pipe, a, b, c) _MMIO(_PICK(pipe, a, b, c))
 #define _MMIO_PORT3(pipe, a, b, c) _MMIO(_PICK(pipe, a, b, c))
+#define _PLL(pll, a, b) ((a) + (pll)*((b)-(a)))
+#define _MMIO_PLL(pll, a, b) _MMIO(_PLL(pll, a, b))
 #define _PHY3(phy, ...) _PICK(phy, __VA_ARGS__)
 #define _MMIO_PHY3(phy, a, b, c) _MMIO(_PHY3(phy, a, b, c))
 
@@ -8143,6 +8145,52 @@ enum {
 #define  DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(port) ((port)*2)
 #define  DPCLKA_CFGCR0_DDI_CLK_SEL(pll, port)  ((pll) << ((port)*2))
 
+/* CNL PLL */
+#define DPLL0_ENABLE           0x46010
+#define DPLL1_ENABLE           0x46014
+#define  PLL_ENABLE            (1 << 31)
+#define  PLL_LOCK              (1 << 30)
+#define  PLL_POWER_ENABLE      (1 << 27)
+#define  PLL_POWER_STATE       (1 << 26)
+#define CNL_DPLL_ENABLE(pll)   _MMIO_PLL(pll, DPLL0_ENABLE, DPLL1_ENABLE)
+
+#define _CNL_DPLL0_CFGCR0              0x6C000
+#define _CNL_DPLL1_CFGCR0              0x6C080
+#define  DPLL_CFGCR0_HDMI_MODE         (1 << 30)
+#define  DPLL_CFGCR0_SSC_ENABLE                (1 << 29)
+#define  DPLL_CFGCR0_LINK_RATE_MASK    (0xf << 25)
+#define  DPLL_CFGCR0_LINK_RATE_2700    (0 << 25)
+#define  DPLL_CFGCR0_LINK_RATE_1350    (1 << 25)
+#define  DPLL_CFGCR0_LINK_RATE_810     (2 << 25)
+#define  DPLL_CFGCR0_LINK_RATE_1620    (3 << 25)
+#define  DPLL_CFGCR0_LINK_RATE_1080    (4 << 25)
+#define  DPLL_CFGCR0_LINK_RATE_2160    (5 << 25)
+#define  DPLL_CFGCR0_LINK_RATE_3240    (6 << 25)
+#define  DPLL_CFGCR0_LINK_RATE_4050    (7 << 25)
+#define  DPLL_CFGCR0_DCO_FRACTION_MASK (0x7fff << 10)
+#define  DPLL_CFGCR0_DCO_FRACTION(x)   ((x) << 10)
+#define  DPLL_CFGCR0_DCO_INTEGER_MASK  (0x3ff)
+#define CNL_DPLL_CFGCR0(pll)           _MMIO_PLL(pll, _CNL_DPLL0_CFGCR0, _CNL_DPLL1_CFGCR0)
+
+#define _CNL_DPLL0_CFGCR1              0x6C004
+#define _CNL_DPLL1_CFGCR1              0x6C084
+#define  DPLL_CFGCR1_QDIV_RATIO_MASK   (0xff << 10)
+#define  DPLL_CFGCR1_QDIV_RATIO(x)     ((x) << 10)
+#define  DPLL_CFGCR1_QDIV_MODE(x)      ((x) << 9)
+#define  DPLL_CFGCR1_KDIV_MASK         (7 << 6)
+#define  DPLL_CFGCR1_KDIV(x)           ((x) << 6)
+#define  DPLL_CFGCR1_KDIV_1            (1 << 6)
+#define  DPLL_CFGCR1_KDIV_2            (2 << 6)
+#define  DPLL_CFGCR1_KDIV_4            (4 << 6)
+#define  DPLL_CFGCR1_PDIV_MASK         (0xf << 2)
+#define  DPLL_CFGCR1_PDIV(x)           ((x) << 2)
+#define  DPLL_CFGCR1_PDIV_2            (1 << 2)
+#define  DPLL_CFGCR1_PDIV_3            (2 << 2)
+#define  DPLL_CFGCR1_PDIV_5            (4 << 2)
+#define  DPLL_CFGCR1_PDIV_7            (8 << 2)
+#define  DPLL_CFGCR1_CENTRAL_FREQ      (3 << 0)
+#define CNL_DPLL_CFGCR1(pll)           _MMIO_PLL(pll, _CNL_DPLL0_CFGCR1, _CNL_DPLL1_CFGCR1)
+
 /* BXT display engine PLL */
 #define BXT_DE_PLL_CTL                 _MMIO(0x6d000)
 #define   BXT_DE_PLL_RATIO(x)          (x)     /* {60,65,100} * 19.2MHz */
index b4de632f11587000778908a74969fd34f89fdfe1..903c38dc683a4c600763a04c5b392177ee2628a9 100644 (file)
@@ -1321,7 +1321,6 @@ static bool skl_ddi_hdmi_pll_dividers(struct intel_crtc *crtc,
        return true;
 }
 
-
 static bool
 skl_ddi_dp_set_dpll_hw_state(int clock,
                             struct intel_dpll_hw_state *dpll_hw_state)
@@ -1967,6 +1966,301 @@ static const struct intel_dpll_mgr bxt_pll_mgr = {
        .dump_hw_state = bxt_dump_hw_state,
 };
 
+static void cnl_ddi_pll_enable(struct drm_i915_private *dev_priv,
+                              struct intel_shared_dpll *pll)
+{
+       uint32_t val;
+
+       /* 1. Enable DPLL power in DPLL_ENABLE. */
+       val = I915_READ(CNL_DPLL_ENABLE(pll->id));
+       val |= PLL_POWER_ENABLE;
+       I915_WRITE(CNL_DPLL_ENABLE(pll->id), val);
+
+       /* 2. Wait for DPLL power state enabled in DPLL_ENABLE. */
+       if (intel_wait_for_register(dev_priv,
+                                   CNL_DPLL_ENABLE(pll->id),
+                                   PLL_POWER_STATE,
+                                   PLL_POWER_STATE,
+                                   5))
+               DRM_ERROR("PLL %d Power not enabled\n", pll->id);
+
+       /*
+        * 3. Configure DPLL_CFGCR0 to set SSC enable/disable,
+        * select DP mode, and set DP link rate.
+        */
+       val = pll->state.hw_state.cfgcr0;
+       I915_WRITE(CNL_DPLL_CFGCR0(pll->id), val);
+
+       /* 4. Reab back to ensure writes completed */
+       POSTING_READ(CNL_DPLL_CFGCR0(pll->id));
+
+       /* 3. Configure DPLL_CFGCR0 */
+       /* Avoid touch CFGCR1 if HDMI mode is not enabled */
+       if (pll->state.hw_state.cfgcr0 & DPLL_CTRL1_HDMI_MODE(pll->id)) {
+               val = pll->state.hw_state.cfgcr1;
+               I915_WRITE(CNL_DPLL_CFGCR1(pll->id), val);
+               /* 4. Reab back to ensure writes completed */
+               POSTING_READ(CNL_DPLL_CFGCR1(pll->id));
+       }
+
+       /*
+        * 5. If the frequency will result in a change to the voltage
+        * requirement, follow the Display Voltage Frequency Switching
+        * Sequence Before Frequency Change
+        *
+        * FIXME: (DVFS) is used to adjust the display voltage to match the
+        * display clock frequencies
+        */
+
+       /* 6. Enable DPLL in DPLL_ENABLE. */
+       val = I915_READ(CNL_DPLL_ENABLE(pll->id));
+       val |= PLL_ENABLE;
+       I915_WRITE(CNL_DPLL_ENABLE(pll->id), val);
+
+       /* 7. Wait for PLL lock status in DPLL_ENABLE. */
+       if (intel_wait_for_register(dev_priv,
+                                   CNL_DPLL_ENABLE(pll->id),
+                                   PLL_LOCK,
+                                   PLL_LOCK,
+                                   5))
+               DRM_ERROR("PLL %d not locked\n", pll->id);
+
+       /*
+        * 8. If the frequency will result in a change to the voltage
+        * requirement, follow the Display Voltage Frequency Switching
+        * Sequence After Frequency Change
+        *
+        * FIXME: (DVFS) is used to adjust the display voltage to match the
+        * display clock frequencies
+        */
+
+       /*
+        * 9. turn on the clock for the DDI and map the DPLL to the DDI
+        * Done at intel_ddi_clk_select
+        */
+}
+
+static void cnl_ddi_pll_disable(struct drm_i915_private *dev_priv,
+                               struct intel_shared_dpll *pll)
+{
+       uint32_t val;
+
+       /*
+        * 1. Configure DPCLKA_CFGCR0 to turn off the clock for the DDI.
+        * Done at intel_ddi_post_disable
+        */
+
+       /*
+        * 2. If the frequency will result in a change to the voltage
+        * requirement, follow the Display Voltage Frequency Switching
+        * Sequence Before Frequency Change
+        *
+        * FIXME: (DVFS) is used to adjust the display voltage to match the
+        * display clock frequencies
+        */
+
+       /* 3. Disable DPLL through DPLL_ENABLE. */
+       val = I915_READ(CNL_DPLL_ENABLE(pll->id));
+       val &= ~PLL_ENABLE;
+       I915_WRITE(CNL_DPLL_ENABLE(pll->id), val);
+
+       /* 4. Wait for PLL not locked status in DPLL_ENABLE. */
+       if (intel_wait_for_register(dev_priv,
+                                   CNL_DPLL_ENABLE(pll->id),
+                                   PLL_LOCK,
+                                   0,
+                                   5))
+               DRM_ERROR("PLL %d locked\n", pll->id);
+
+       /*
+        * 5. If the frequency will result in a change to the voltage
+        * requirement, follow the Display Voltage Frequency Switching
+        * Sequence After Frequency Change
+        *
+        * FIXME: (DVFS) is used to adjust the display voltage to match the
+        * display clock frequencies
+        */
+
+       /* 6. Disable DPLL power in DPLL_ENABLE. */
+       val = I915_READ(CNL_DPLL_ENABLE(pll->id));
+       val &= ~PLL_POWER_ENABLE;
+       I915_WRITE(CNL_DPLL_ENABLE(pll->id), val);
+
+       /* 7. Wait for DPLL power state disabled in DPLL_ENABLE. */
+       if (intel_wait_for_register(dev_priv,
+                                   CNL_DPLL_ENABLE(pll->id),
+                                   PLL_POWER_STATE,
+                                   0,
+                                   5))
+               DRM_ERROR("PLL %d Power not disabled\n", pll->id);
+}
+
+static bool cnl_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv,
+                                    struct intel_shared_dpll *pll,
+                                    struct intel_dpll_hw_state *hw_state)
+{
+       uint32_t val;
+       bool ret;
+
+       if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS))
+               return false;
+
+       ret = false;
+
+       val = I915_READ(CNL_DPLL_ENABLE(pll->id));
+       if (!(val & PLL_ENABLE))
+               goto out;
+
+       val = I915_READ(CNL_DPLL_CFGCR0(pll->id));
+       hw_state->cfgcr0 = val;
+
+       /* avoid reading back stale values if HDMI mode is not enabled */
+       if (val & DPLL_CFGCR0_HDMI_MODE) {
+               hw_state->cfgcr1 = I915_READ(CNL_DPLL_CFGCR1(pll->id));
+       }
+       ret = true;
+
+out:
+       intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS);
+
+       return ret;
+}
+
+static bool cnl_ddi_hdmi_pll_dividers(struct intel_crtc *crtc,
+                                     struct intel_crtc_state *crtc_state,
+                                     int clock)
+{
+       uint32_t cfgcr0, cfgcr1;
+       struct skl_wrpll_params wrpll_params = { 0, };
+
+       cfgcr0 = DPLL_CFGCR0_HDMI_MODE;
+
+       /* FIXME: Proper wrpll calculation done in a following patch */
+       return false;
+
+       cfgcr0 |= DPLL_CFGCR0_DCO_FRACTION(wrpll_params.dco_fraction) |
+               wrpll_params.dco_integer;
+
+       cfgcr1 = DPLL_CFGCR1_QDIV_RATIO(wrpll_params.qdiv_ratio) |
+               DPLL_CFGCR1_QDIV_MODE(wrpll_params.qdiv_mode) |
+               DPLL_CFGCR1_KDIV(wrpll_params.kdiv) |
+               DPLL_CFGCR1_PDIV(wrpll_params.pdiv) |
+               wrpll_params.central_freq |
+               DPLL_CFGCR1_CENTRAL_FREQ;
+
+       memset(&crtc_state->dpll_hw_state, 0,
+              sizeof(crtc_state->dpll_hw_state));
+
+       crtc_state->dpll_hw_state.cfgcr0 = cfgcr0;
+       crtc_state->dpll_hw_state.cfgcr1 = cfgcr1;
+       return true;
+}
+
+bool cnl_ddi_dp_set_dpll_hw_state(int clock,
+                                 struct intel_dpll_hw_state *dpll_hw_state)
+{
+       uint32_t cfgcr0;
+
+       cfgcr0 = DPLL_CFGCR0_SSC_ENABLE;
+
+       switch (clock / 2) {
+       case 81000:
+               cfgcr0 |= DPLL_CFGCR0_LINK_RATE_810;
+               break;
+       case 135000:
+               cfgcr0 |= DPLL_CFGCR0_LINK_RATE_1350;
+               break;
+       case 270000:
+               cfgcr0 |= DPLL_CFGCR0_LINK_RATE_2700;
+               break;
+               /* eDP 1.4 rates */
+       case 162000:
+               cfgcr0 |= DPLL_CFGCR0_LINK_RATE_1620;
+               break;
+       case 108000:
+               cfgcr0 |= DPLL_CFGCR0_LINK_RATE_1080;
+               break;
+       case 216000:
+               cfgcr0 |= DPLL_CFGCR0_LINK_RATE_2160;
+               break;
+       case 324000:
+               /* Some SKUs may require elevated I/O voltage to support this */
+               cfgcr0 |= DPLL_CFGCR0_LINK_RATE_3240;
+               break;
+       case 405000:
+               /* Some SKUs may require elevated I/O voltage to support this */
+               cfgcr0 |= DPLL_CFGCR0_LINK_RATE_4050;
+               break;
+       }
+
+       dpll_hw_state->cfgcr0 = cfgcr0;
+       return true;
+}
+
+static struct intel_shared_dpll *
+cnl_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state,
+            struct intel_encoder *encoder)
+{
+       struct intel_shared_dpll *pll;
+       int clock = crtc_state->port_clock;
+       bool bret;
+       struct intel_dpll_hw_state dpll_hw_state;
+
+       memset(&dpll_hw_state, 0, sizeof(dpll_hw_state));
+
+       if (encoder->type == INTEL_OUTPUT_HDMI) {
+               bret = cnl_ddi_hdmi_pll_dividers(crtc, crtc_state, clock);
+               if (!bret) {
+                       DRM_DEBUG_KMS("Could not get HDMI pll dividers.\n");
+                       return NULL;
+               }
+       } else if (encoder->type == INTEL_OUTPUT_DP ||
+                  encoder->type == INTEL_OUTPUT_DP_MST ||
+                  encoder->type == INTEL_OUTPUT_EDP) {
+               bret = cnl_ddi_dp_set_dpll_hw_state(clock, &dpll_hw_state);
+               if (!bret) {
+                       DRM_DEBUG_KMS("Could not set DP dpll HW state.\n");
+                       return NULL;
+               }
+               crtc_state->dpll_hw_state = dpll_hw_state;
+       } else {
+               DRM_DEBUG_KMS("Skip DPLL setup for encoder %d\n",
+                             encoder->type);
+               return NULL;
+       }
+
+       pll = intel_find_shared_dpll(crtc, crtc_state,
+                                    DPLL_ID_SKL_DPLL0,
+                                    DPLL_ID_SKL_DPLL2);
+       if (!pll) {
+               DRM_DEBUG_KMS("No PLL selected\n");
+               return NULL;
+       }
+
+       intel_reference_shared_dpll(pll, crtc_state);
+
+       return pll;
+}
+
+static const struct intel_shared_dpll_funcs cnl_ddi_pll_funcs = {
+       .enable = cnl_ddi_pll_enable,
+       .disable = cnl_ddi_pll_disable,
+       .get_hw_state = cnl_ddi_pll_get_hw_state,
+};
+
+static const struct dpll_info cnl_plls[] = {
+       { "DPLL 0", DPLL_ID_SKL_DPLL0, &cnl_ddi_pll_funcs, 0 },
+       { "DPLL 1", DPLL_ID_SKL_DPLL1, &cnl_ddi_pll_funcs, 0 },
+       { "DPLL 2", DPLL_ID_SKL_DPLL2, &cnl_ddi_pll_funcs, 0 },
+       { NULL, -1, NULL, },
+};
+
+static const struct intel_dpll_mgr cnl_pll_mgr = {
+       .dpll_info = cnl_plls,
+       .get_dpll = cnl_get_dpll,
+       .dump_hw_state = skl_dump_hw_state,
+};
+
 /**
  * intel_shared_dpll_init - Initialize shared DPLLs
  * @dev: drm device
@@ -1980,7 +2274,9 @@ void intel_shared_dpll_init(struct drm_device *dev)
        const struct dpll_info *dpll_info;
        int i;
 
-       if (IS_GEN9_BC(dev_priv))
+       if (IS_CANNONLAKE(dev_priv))
+               dpll_mgr = &cnl_pll_mgr;
+       else if (IS_GEN9_BC(dev_priv))
                dpll_mgr = &skl_pll_mgr;
        else if (IS_GEN9_LP(dev_priv))
                dpll_mgr = &bxt_pll_mgr;
index f8d13a947c13e1c98361c8cabdb0b4fdb19c7329..f24ccf443d251538fa34bd293412e603786caad6 100644 (file)
@@ -128,6 +128,10 @@ struct intel_dpll_hw_state {
        /* HDMI only, 0 when used for DP */
        uint32_t cfgcr1, cfgcr2;
 
+       /* cnl */
+       uint32_t cfgcr0;
+       /* CNL also uses cfgcr1 */
+
        /* bxt */
        uint32_t ebb0, ebb4, pll0, pll1, pll2, pll3, pll6, pll8, pll9, pll10,
                 pcsdw12;