]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
drm/i915/cnl: Implement CNL display init/unit sequence
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Fri, 9 Jun 2017 22:26:00 +0000 (15:26 -0700)
committerRodrigo Vivi <rodrigo.vivi@intel.com>
Mon, 12 Jun 2017 16:41:01 +0000 (09:41 -0700)
Implement the CNL display init/uninit sequence as outlined in Bspec.

Quite similar to SKL/BXT. The main complicaiton is probably the extra
procmon setup we must do based on the process/voltage information we
can read out from some register.

v2: s/skl_dbuf/gen9_dbuf/ to follow upstream
    bxt needed a cdclk sanitize step, so let's add it for cnl too
v3: s/CHICKEN_MISC_1/CHICKEN_MISC_2/ (Ander)
v4: Rebased by Rodrigo after Ville's cdclk rework
v5: Removed unecessary Aux IO forced enable/disable, Fix DW10 setup
    Fix procpon Mask. (Credits-to Paulo and Clint)
    Remove A0 workaround.
v6: Rebased on top of recent code (Rodrigo).
v7: Respect the order of sanitize_ after set_
    (Done by Rodrigo, Requested by Ville)
v8: Commit message updated to matvh v5 changes besides
    Remove unused DW8 and an extra blank line. (all noticed
    by Imre).
v9: Remove __attribute__((unused)) added on latest version
    of drm/i915/cnl: Implement .set_cdclk() for CNL.

Cc: Paulo Zanoni <paulo.r.zanoni@intel.com>
Cc: Clint Taylor <clinton.a.taylor@intel.com>
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>
Link: http://patchwork.freedesktop.org/patch/msgid/1497047175-27250-3-git-send-email-rodrigo.vivi@intel.com
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_cdclk.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_runtime_pm.c

index ac3df675b4f370026d68d5dbcfd988ca7da9044a..539e44e88e01bff694c4c66f8e9af2b209a9b00c 100644 (file)
@@ -1661,6 +1661,9 @@ enum skl_disp_power_wells {
 #define   PHY_RESERVED                 (1 << 7)
 #define BXT_PORT_CL1CM_DW0(phy)                _BXT_PHY((phy), _PORT_CL1CM_DW0_BC)
 
+#define CNL_PORT_CL1CM_DW5             _MMIO(0x162014)
+#define   CL_POWER_DOWN_ENABLE         (1 << 4)
+
 #define _PORT_CL1CM_DW9_A              0x162024
 #define _PORT_CL1CM_DW9_BC             0x6C024
 #define   IREF0RC_OFFSET_SHIFT         8
@@ -1693,6 +1696,23 @@ enum skl_disp_power_wells {
 #define BXT_PORT_CL2CM_DW6(phy)                _BXT_PHY((phy), _PORT_CL2CM_DW6_BC)
 #define   DW6_OLDO_DYN_PWR_DOWN_EN     (1 << 28)
 
+#define CNL_PORT_COMP_DW0              _MMIO(0x162100)
+#define   COMP_INIT                    (1 << 31)
+#define CNL_PORT_COMP_DW1              _MMIO(0x162104)
+#define CNL_PORT_COMP_DW3              _MMIO(0x16210c)
+#define   PROCESS_INFO_DOT_0           (0 << 26)
+#define   PROCESS_INFO_DOT_1           (1 << 26)
+#define   PROCESS_INFO_DOT_4           (2 << 26)
+#define   PROCESS_INFO_MASK            (7 << 26)
+#define   PROCESS_INFO_SHIFT           26
+#define   VOLTAGE_INFO_0_85V           (0 << 24)
+#define   VOLTAGE_INFO_0_95V           (1 << 24)
+#define   VOLTAGE_INFO_1_05V           (2 << 24)
+#define   VOLTAGE_INFO_MASK            (3 << 24)
+#define   VOLTAGE_INFO_SHIFT           24
+#define CNL_PORT_COMP_DW9              _MMIO(0x162124)
+#define CNL_PORT_COMP_DW10             _MMIO(0x162128)
+
 /* BXT PHY Ref registers */
 #define _PORT_REF_DW3_A                        0x16218C
 #define _PORT_REF_DW3_BC               0x6C18C
@@ -6510,6 +6530,9 @@ enum {
 #define  GLK_CL1_PWR_DOWN      (1 << 11)
 #define  GLK_CL2_PWR_DOWN      (1 << 12)
 
+#define CHICKEN_MISC_2         _MMIO(0x42084)
+#define  COMP_PWR_DOWN         (1 << 23)
+
 #define _CHICKEN_PIPESL_1_A    0x420b0
 #define _CHICKEN_PIPESL_1_B    0x420b4
 #define  HSW_FBCQ_DIS                  (1 << 22)
index 0e892b8c84af340ca21f651cad10df61f465603e..35a1432bc90bd21e3b7cb83e4e2809cddd0d1aca 100644 (file)
@@ -1485,7 +1485,6 @@ static void cnl_cdclk_pll_enable(struct drm_i915_private *dev_priv, int vco)
        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)
 {
@@ -1558,6 +1557,113 @@ static void cnl_set_cdclk(struct drm_i915_private *dev_priv,
        intel_update_cdclk(dev_priv);
 }
 
+static int cnl_cdclk_pll_vco(struct drm_i915_private *dev_priv, int cdclk)
+{
+       int ratio;
+
+       if (cdclk == dev_priv->cdclk.hw.ref)
+               return 0;
+
+       switch (cdclk) {
+       default:
+               MISSING_CASE(cdclk);
+       case 168000:
+       case 336000:
+               ratio = dev_priv->cdclk.hw.ref == 19200 ? 35 : 28;
+               break;
+       case 528000:
+               ratio = dev_priv->cdclk.hw.ref == 19200 ? 55 : 44;
+               break;
+       }
+
+       return dev_priv->cdclk.hw.ref * ratio;
+}
+
+static void cnl_sanitize_cdclk(struct drm_i915_private *dev_priv)
+{
+       u32 cdctl, expected;
+
+       intel_update_cdclk(dev_priv);
+
+       if (dev_priv->cdclk.hw.vco == 0 ||
+           dev_priv->cdclk.hw.cdclk == dev_priv->cdclk.hw.ref)
+               goto sanitize;
+
+       /* DPLL okay; verify the cdclock
+        *
+        * Some BIOS versions leave an incorrect decimal frequency value and
+        * set reserved MBZ bits in CDCLK_CTL at least during exiting from S4,
+        * so sanitize this register.
+        */
+       cdctl = I915_READ(CDCLK_CTL);
+       /*
+        * Let's ignore the pipe field, since BIOS could have configured the
+        * dividers both synching to an active pipe, or asynchronously
+        * (PIPE_NONE).
+        */
+       cdctl &= ~BXT_CDCLK_CD2X_PIPE_NONE;
+
+       expected = (cdctl & BXT_CDCLK_CD2X_DIV_SEL_MASK) |
+                  skl_cdclk_decimal(dev_priv->cdclk.hw.cdclk);
+
+       if (cdctl == expected)
+               /* All well; nothing to sanitize */
+               return;
+
+sanitize:
+       DRM_DEBUG_KMS("Sanitizing cdclk programmed by pre-os\n");
+
+       /* force cdclk programming */
+       dev_priv->cdclk.hw.cdclk = 0;
+
+       /* force full PLL disable + enable */
+       dev_priv->cdclk.hw.vco = -1;
+}
+
+/**
+ * cnl_init_cdclk - Initialize CDCLK on CNL
+ * @dev_priv: i915 device
+ *
+ * Initialize CDCLK for CNL. 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 cnl_init_cdclk(struct drm_i915_private *dev_priv)
+{
+       struct intel_cdclk_state cdclk_state;
+
+       cnl_sanitize_cdclk(dev_priv);
+
+       if (dev_priv->cdclk.hw.cdclk != 0 &&
+           dev_priv->cdclk.hw.vco != 0)
+               return;
+
+       cdclk_state = dev_priv->cdclk.hw;
+
+       cdclk_state.cdclk = 168000;
+       cdclk_state.vco = cnl_cdclk_pll_vco(dev_priv, cdclk_state.cdclk);
+
+       cnl_set_cdclk(dev_priv, &cdclk_state);
+}
+
+/**
+ * cnl_uninit_cdclk - Uninitialize CDCLK on CNL
+ * @dev_priv: i915 device
+ *
+ * Uninitialize CDCLK for CNL. This is done only
+ * during the display core uninitialization sequence.
+ */
+void cnl_uninit_cdclk(struct drm_i915_private *dev_priv)
+{
+       struct intel_cdclk_state cdclk_state = dev_priv->cdclk.hw;
+
+       cdclk_state.cdclk = cdclk_state.ref;
+       cdclk_state.vco = 0;
+
+       cnl_set_cdclk(dev_priv, &cdclk_state);
+}
+
 /**
  * intel_cdclk_state_compare - Determine if two CDCLK states differ
  * @a: first CDCLK state
index 2ad5fa337ed1f5a42368150a213403b16b5e0401..2bc3326f40680ddc888d56d845745ecf7786b506 100644 (file)
@@ -1318,6 +1318,8 @@ void intel_audio_deinit(struct drm_i915_private *dev_priv);
 /* intel_cdclk.c */
 void skl_init_cdclk(struct drm_i915_private *dev_priv);
 void skl_uninit_cdclk(struct drm_i915_private *dev_priv);
+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 intel_init_cdclk_hooks(struct drm_i915_private *dev_priv);
index 8a6f287d225b181159919801cd93b8e18fded434..436ec7a7b8431bf4de56d146d2bfe6343efa4fb4 100644 (file)
@@ -2696,6 +2696,111 @@ void bxt_display_core_uninit(struct drm_i915_private *dev_priv)
        mutex_unlock(&power_domains->lock);
 }
 
+#define CNL_PROCMON_IDX(val) \
+       (((val) & (PROCESS_INFO_MASK | VOLTAGE_INFO_MASK)) >> VOLTAGE_INFO_SHIFT)
+#define NUM_CNL_PROCMON \
+       (CNL_PROCMON_IDX(VOLTAGE_INFO_MASK | PROCESS_INFO_MASK) + 1)
+
+static const struct cnl_procmon {
+       u32 dw1, dw9, dw10;
+} cnl_procmon_values[NUM_CNL_PROCMON] = {
+       [CNL_PROCMON_IDX(VOLTAGE_INFO_0_85V | PROCESS_INFO_DOT_0)] =
+               { .dw1 = 0x00 << 16, .dw9 = 0x62AB67BB, .dw10 = 0x51914F96, },
+       [CNL_PROCMON_IDX(VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_0)] =
+               { .dw1 = 0x00 << 16, .dw9 = 0x86E172C7, .dw10 = 0x77CA5EAB, },
+       [CNL_PROCMON_IDX(VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_1)] =
+               { .dw1 = 0x00 << 16, .dw9 = 0x93F87FE1, .dw10 = 0x8AE871C5, },
+       [CNL_PROCMON_IDX(VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_0)] =
+               { .dw1 = 0x00 << 16, .dw9 = 0x98FA82DD, .dw10 = 0x89E46DC1, },
+       [CNL_PROCMON_IDX(VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_1)] =
+               { .dw1 = 0x44 << 16, .dw9 = 0x9A00AB25, .dw10 = 0x8AE38FF1, },
+};
+
+static void cnl_display_core_init(struct drm_i915_private *dev_priv, bool resume)
+{
+       struct i915_power_domains *power_domains = &dev_priv->power_domains;
+       const struct cnl_procmon *procmon;
+       struct i915_power_well *well;
+       u32 val;
+
+       gen9_set_dc_state(dev_priv, DC_STATE_DISABLE);
+
+       /* 1. Enable PCH Reset Handshake */
+       val = I915_READ(HSW_NDE_RSTWRN_OPT);
+       val |= RESET_PCH_HANDSHAKE_ENABLE;
+       I915_WRITE(HSW_NDE_RSTWRN_OPT, val);
+
+       /* 2. Enable Comp */
+       val = I915_READ(CHICKEN_MISC_2);
+       val &= ~COMP_PWR_DOWN;
+       I915_WRITE(CHICKEN_MISC_2, val);
+
+       val = I915_READ(CNL_PORT_COMP_DW3);
+       procmon = &cnl_procmon_values[CNL_PROCMON_IDX(val)];
+
+       WARN_ON(procmon->dw10 == 0);
+
+       val = I915_READ(CNL_PORT_COMP_DW1);
+       val &= ~((0xff << 16) | 0xff);
+       val |= procmon->dw1;
+       I915_WRITE(CNL_PORT_COMP_DW1, val);
+
+       I915_WRITE(CNL_PORT_COMP_DW9, procmon->dw9);
+       I915_WRITE(CNL_PORT_COMP_DW10, procmon->dw10);
+
+       val = I915_READ(CNL_PORT_COMP_DW0);
+       val |= COMP_INIT;
+       I915_WRITE(CNL_PORT_COMP_DW0, val);
+
+       /* 3. */
+       val = I915_READ(CNL_PORT_CL1CM_DW5);
+       val |= CL_POWER_DOWN_ENABLE;
+       I915_WRITE(CNL_PORT_CL1CM_DW5, val);
+
+       /* 4. Enable Power Well 1 (PG1) and Aux IO Power */
+       mutex_lock(&power_domains->lock);
+       well = lookup_power_well(dev_priv, SKL_DISP_PW_1);
+       intel_power_well_enable(dev_priv, well);
+       mutex_unlock(&power_domains->lock);
+
+       /* 5. Enable CD clock */
+       cnl_init_cdclk(dev_priv);
+
+       /* 6. Enable DBUF */
+       gen9_dbuf_enable(dev_priv);
+}
+
+#undef CNL_PROCMON_IDX
+#undef NUM_CNL_PROCMON
+
+static void cnl_display_core_uninit(struct drm_i915_private *dev_priv)
+{
+       struct i915_power_domains *power_domains = &dev_priv->power_domains;
+       struct i915_power_well *well;
+       u32 val;
+
+       gen9_set_dc_state(dev_priv, DC_STATE_DISABLE);
+
+       /* 1. Disable all display engine functions -> aready done */
+
+       /* 2. Disable DBUF */
+       gen9_dbuf_disable(dev_priv);
+
+       /* 3. Disable CD clock */
+       cnl_uninit_cdclk(dev_priv);
+
+       /* 4. Disable Power Well 1 (PG1) and Aux IO Power */
+       mutex_lock(&power_domains->lock);
+       well = lookup_power_well(dev_priv, SKL_DISP_PW_1);
+       intel_power_well_disable(dev_priv, well);
+       mutex_unlock(&power_domains->lock);
+
+       /* 5. Disable Comp */
+       val = I915_READ(CHICKEN_MISC_2);
+       val |= COMP_PWR_DOWN;
+       I915_WRITE(CHICKEN_MISC_2, val);
+}
+
 static void chv_phy_control_init(struct drm_i915_private *dev_priv)
 {
        struct i915_power_well *cmn_bc =
@@ -2828,7 +2933,9 @@ void intel_power_domains_init_hw(struct drm_i915_private *dev_priv, bool resume)
 
        power_domains->initializing = true;
 
-       if (IS_GEN9_BC(dev_priv)) {
+       if (IS_CANNONLAKE(dev_priv)) {
+               cnl_display_core_init(dev_priv, resume);
+       } else if (IS_GEN9_BC(dev_priv)) {
                skl_display_core_init(dev_priv, resume);
        } else if (IS_GEN9_LP(dev_priv)) {
                bxt_display_core_init(dev_priv, resume);
@@ -2867,7 +2974,9 @@ void intel_power_domains_suspend(struct drm_i915_private *dev_priv)
        if (!i915.disable_power_well)
                intel_display_power_put(dev_priv, POWER_DOMAIN_INIT);
 
-       if (IS_GEN9_BC(dev_priv))
+       if (IS_CANNONLAKE(dev_priv))
+               cnl_display_core_uninit(dev_priv);
+       else if (IS_GEN9_BC(dev_priv))
                skl_display_core_uninit(dev_priv);
        else if (IS_GEN9_LP(dev_priv))
                bxt_display_core_uninit(dev_priv);