From: Ville Syrjälä <ville.syrj...@linux.intel.com>

If we have only a single active pipe and the cdclk change only requires
the cd2x divider to be updated bxt+ can do the update with forcing a full
modeset on the pipe. Try to hook that up.

Signed-off-by: Ville Syrjälä <ville.syrj...@linux.intel.com>
Signed-off-by: Abhay Kumar <abhay.ku...@intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h      |   3 +-
 drivers/gpu/drm/i915/i915_reg.h      |   3 +-
 drivers/gpu/drm/i915/intel_cdclk.c   | 105 +++++++++++++++++++++++++----------
 drivers/gpu/drm/i915/intel_display.c |  20 ++++++-
 drivers/gpu/drm/i915/intel_drv.h     |   9 ++-
 5 files changed, 105 insertions(+), 35 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index e5b9d3c77139..1f0a6427e76c 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -408,7 +408,8 @@ struct drm_i915_display_funcs {
        void (*get_cdclk)(struct drm_i915_private *dev_priv,
                          struct intel_cdclk_state *cdclk_state);
        void (*set_cdclk)(struct drm_i915_private *dev_priv,
-                         const struct intel_cdclk_state *cdclk_state);
+                         const struct intel_cdclk_state *cdclk_state,
+                         enum pipe pipe);
        int (*get_fifo_size)(struct drm_i915_private *dev_priv,
                             enum i9xx_plane_id i9xx_plane);
        int (*compute_pipe_wm)(struct intel_crtc_state *cstate);
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 8534f88a60f6..7702cec70b0d 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -9262,7 +9262,8 @@ enum skl_power_gate {
 #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  ICL_CDCLK_CD2X_PIPE_NONE      (7 << 19)
+#define  ICL_CDCLK_CD2X_PIPE(pipe)     ((pipe) << 19)
+#define  ICL_CDCLK_CD2X_PIPE_NONE      ICL_CDCLK_CD2X_PIPE(7)
 #define  BXT_CDCLK_SSA_PRECHARGE_ENABLE        (1 << 16)
 #define  CDCLK_FREQ_DECIMAL_MASK       (0x7ff)
 
diff --git a/drivers/gpu/drm/i915/intel_cdclk.c 
b/drivers/gpu/drm/i915/intel_cdclk.c
index 29075c763428..1955f6aa54e1 100644
--- a/drivers/gpu/drm/i915/intel_cdclk.c
+++ b/drivers/gpu/drm/i915/intel_cdclk.c
@@ -516,7 +516,8 @@ static void vlv_program_pfi_credits(struct drm_i915_private 
*dev_priv)
 }
 
 static void vlv_set_cdclk(struct drm_i915_private *dev_priv,
-                         const struct intel_cdclk_state *cdclk_state)
+                         const struct intel_cdclk_state *cdclk_state,
+                         enum pipe pipe)
 {
        int cdclk = cdclk_state->cdclk;
        u32 val, cmd = cdclk_state->voltage_level;
@@ -597,7 +598,8 @@ static void vlv_set_cdclk(struct drm_i915_private *dev_priv,
 }
 
 static void chv_set_cdclk(struct drm_i915_private *dev_priv,
-                         const struct intel_cdclk_state *cdclk_state)
+                         const struct intel_cdclk_state *cdclk_state,
+                         enum pipe pipe)
 {
        int cdclk = cdclk_state->cdclk;
        u32 val, cmd = cdclk_state->voltage_level;
@@ -695,7 +697,8 @@ static void bdw_get_cdclk(struct drm_i915_private *dev_priv,
 }
 
 static void bdw_set_cdclk(struct drm_i915_private *dev_priv,
-                         const struct intel_cdclk_state *cdclk_state)
+                         const struct intel_cdclk_state *cdclk_state,
+                         enum pipe pipe)
 {
        int cdclk = cdclk_state->cdclk;
        uint32_t val;
@@ -985,7 +988,8 @@ static void skl_dpll0_disable(struct drm_i915_private 
*dev_priv)
 }
 
 static void skl_set_cdclk(struct drm_i915_private *dev_priv,
-                         const struct intel_cdclk_state *cdclk_state)
+                         const struct intel_cdclk_state *cdclk_state,
+                         enum pipe pipe)
 {
        int cdclk = cdclk_state->cdclk;
        int vco = cdclk_state->vco;
@@ -1156,7 +1160,7 @@ void skl_init_cdclk(struct drm_i915_private *dev_priv)
        cdclk_state.cdclk = skl_calc_cdclk(0, cdclk_state.vco);
        cdclk_state.voltage_level = skl_calc_voltage_level(cdclk_state.cdclk);
 
-       skl_set_cdclk(dev_priv, &cdclk_state);
+       skl_set_cdclk(dev_priv, &cdclk_state, INVALID_PIPE);
 }
 
 /**
@@ -1174,7 +1178,7 @@ void skl_uninit_cdclk(struct drm_i915_private *dev_priv)
        cdclk_state.vco = 0;
        cdclk_state.voltage_level = skl_calc_voltage_level(cdclk_state.cdclk);
 
-       skl_set_cdclk(dev_priv, &cdclk_state);
+       skl_set_cdclk(dev_priv, &cdclk_state, INVALID_PIPE);
 }
 
 static int bxt_calc_cdclk(int min_cdclk)
@@ -1353,7 +1357,8 @@ static void bxt_de_pll_enable(struct drm_i915_private 
*dev_priv, int vco)
 }
 
 static void bxt_set_cdclk(struct drm_i915_private *dev_priv,
-                         const struct intel_cdclk_state *cdclk_state)
+                         const struct intel_cdclk_state *cdclk_state,
+                         enum pipe pipe)
 {
        int cdclk = cdclk_state->cdclk;
        int vco = cdclk_state->vco;
@@ -1406,11 +1411,10 @@ static void bxt_set_cdclk(struct drm_i915_private 
*dev_priv,
                bxt_de_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;
+       if (pipe == INVALID_PIPE)
+               val |= BXT_CDCLK_CD2X_PIPE_NONE;
+       else
+               val |= BXT_CDCLK_CD2X_PIPE(pipe);
        /*
         * Disable SSA Precharge when CD clock frequency < 500 MHz,
         * enable otherwise.
@@ -1523,7 +1527,7 @@ void bxt_init_cdclk(struct drm_i915_private *dev_priv)
        }
        cdclk_state.voltage_level = bxt_calc_voltage_level(cdclk_state.cdclk);
 
-       bxt_set_cdclk(dev_priv, &cdclk_state);
+       bxt_set_cdclk(dev_priv, &cdclk_state, INVALID_PIPE);
 }
 
 /**
@@ -1541,7 +1545,7 @@ void bxt_uninit_cdclk(struct drm_i915_private *dev_priv)
        cdclk_state.vco = 0;
        cdclk_state.voltage_level = bxt_calc_voltage_level(cdclk_state.cdclk);
 
-       bxt_set_cdclk(dev_priv, &cdclk_state);
+       bxt_set_cdclk(dev_priv, &cdclk_state, INVALID_PIPE);
 }
 
 static int cnl_calc_cdclk(int min_cdclk)
@@ -1661,7 +1665,8 @@ static void cnl_cdclk_pll_enable(struct drm_i915_private 
*dev_priv, int vco)
 }
 
 static void cnl_set_cdclk(struct drm_i915_private *dev_priv,
-                         const struct intel_cdclk_state *cdclk_state)
+                         const struct intel_cdclk_state *cdclk_state,
+                         enum pipe pipe)
 {
        int cdclk = cdclk_state->cdclk;
        int vco = cdclk_state->vco;
@@ -1702,11 +1707,10 @@ static void cnl_set_cdclk(struct drm_i915_private 
*dev_priv,
                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;
+       if (pipe == INVALID_PIPE)
+               val |= BXT_CDCLK_CD2X_PIPE_NONE;
+       else
+               val |= BXT_CDCLK_CD2X_PIPE(pipe);
        I915_WRITE(CDCLK_CTL, val);
 
        /* inform PCU of the change */
@@ -1845,10 +1849,12 @@ static int icl_calc_cdclk_pll_vco(struct 
drm_i915_private *dev_priv, int cdclk)
 }
 
 static void icl_set_cdclk(struct drm_i915_private *dev_priv,
-                         const struct intel_cdclk_state *cdclk_state)
+                         const struct intel_cdclk_state *cdclk_state,
+                         enum pipe pipe)
 {
        unsigned int cdclk = cdclk_state->cdclk;
        unsigned int vco = cdclk_state->vco;
+       u32 val;
        int ret;
 
        mutex_lock(&dev_priv->pcu_lock);
@@ -1870,8 +1876,12 @@ static void icl_set_cdclk(struct drm_i915_private 
*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));
+       val = skl_cdclk_decimal(cdclk);
+       if (pipe == INVALID_PIPE)
+               val |= ICL_CDCLK_CD2X_PIPE_NONE;
+       else
+               val |= ICL_CDCLK_CD2X_PIPE(pipe);
+       I915_WRITE(CDCLK_CTL, val);
 
        mutex_lock(&dev_priv->pcu_lock);
        sandybridge_pcode_write(dev_priv, SKL_PCODE_CDCLK_CONTROL,
@@ -2000,7 +2010,7 @@ void icl_init_cdclk(struct drm_i915_private *dev_priv)
        sanitized_state.voltage_level =
                                icl_calc_voltage_level(sanitized_state.cdclk);
 
-       icl_set_cdclk(dev_priv, &sanitized_state);
+       icl_set_cdclk(dev_priv, &sanitized_state, INVALID_PIPE);
 }
 
 /**
@@ -2018,7 +2028,7 @@ void icl_uninit_cdclk(struct drm_i915_private *dev_priv)
        cdclk_state.vco = 0;
        cdclk_state.voltage_level = icl_calc_voltage_level(cdclk_state.cdclk);
 
-       icl_set_cdclk(dev_priv, &cdclk_state);
+       icl_set_cdclk(dev_priv, &cdclk_state, INVALID_PIPE);
 }
 
 /**
@@ -2046,7 +2056,7 @@ void cnl_init_cdclk(struct drm_i915_private *dev_priv)
        cdclk_state.vco = cnl_cdclk_pll_vco(dev_priv, cdclk_state.cdclk);
        cdclk_state.voltage_level = cnl_calc_voltage_level(cdclk_state.cdclk);
 
-       cnl_set_cdclk(dev_priv, &cdclk_state);
+       cnl_set_cdclk(dev_priv, &cdclk_state, INVALID_PIPE);
 }
 
 /**
@@ -2064,7 +2074,7 @@ void cnl_uninit_cdclk(struct drm_i915_private *dev_priv)
        cdclk_state.vco = 0;
        cdclk_state.voltage_level = cnl_calc_voltage_level(cdclk_state.cdclk);
 
-       cnl_set_cdclk(dev_priv, &cdclk_state);
+       cnl_set_cdclk(dev_priv, &cdclk_state, INVALID_PIPE);
 }
 
 /**
@@ -2084,6 +2094,41 @@ bool intel_cdclk_needs_modeset(const struct 
intel_cdclk_state *a,
 }
 
 /**
+ * intel_cdclk_needs_cd2x_update - Determine if two CDCLK states require a 
cd2x divider update
+ * @a: first CDCLK state
+ * @b: second CDCLK state
+ *
+ * Returns:
+ * True if the CDCLK states require just a cd2x divider update, false if not.
+ */
+bool intel_cdclk_needs_cd2x_update(struct drm_i915_private *dev_priv,
+                                  const struct intel_cdclk_state *a,
+                                  const struct intel_cdclk_state *b)
+{
+       /* Older hw doesn't have the capability */
+       if (INTEL_GEN(dev_priv) < 10 && !IS_GEN9_LP(dev_priv))
+               return false;
+
+       return a->cdclk != b->cdclk &&
+               a->vco == b->vco &&
+               a->ref == b->ref;
+}
+
+/**
+ * intel_cdclk_can_skip_modeset - Determine if two CDCLK states can a modeset 
on all pipes
+ * @a: first CDCLK state
+ * @b: second CDCLK state
+ *
+ * Returns:
+ * True if the CDCLK states require pipes to be off during reprogramming, 
false if not.
+ */
+bool intel_cdclk_can_skip_modeset(const struct intel_cdclk_state *a,
+                                 const struct intel_cdclk_state *b)
+{
+       return a->vco == b->vco && a->ref == b->ref;
+}
+
+/**
  * intel_cdclk_changed - Determine if two CDCLK states are different
  * @a: first CDCLK state
  * @b: second CDCLK state
@@ -2111,12 +2156,14 @@ void intel_dump_cdclk_state(const struct 
intel_cdclk_state *cdclk_state,
  * intel_set_cdclk - Push the CDCLK state to the hardware
  * @dev_priv: i915 device
  * @cdclk_state: new CDCLK state
+ * @pipe: pipe with which to synchronize the update
  *
  * Program the hardware based on the passed in CDCLK state,
  * if necessary.
  */
 void intel_set_cdclk(struct drm_i915_private *dev_priv,
-                    const struct intel_cdclk_state *cdclk_state)
+                    const struct intel_cdclk_state *cdclk_state,
+                    enum pipe pipe)
 {
        if (!intel_cdclk_changed(&dev_priv->cdclk.hw, cdclk_state))
                return;
@@ -2126,7 +2173,7 @@ void intel_set_cdclk(struct drm_i915_private *dev_priv,
 
        intel_dump_cdclk_state(cdclk_state, "Changing CDCLK to");
 
-       dev_priv->display.set_cdclk(dev_priv, cdclk_state);
+       dev_priv->display.set_cdclk(dev_priv, cdclk_state, pipe);
 
        if (WARN(intel_cdclk_changed(&dev_priv->cdclk.hw, cdclk_state),
                 "cdclk state doesn't match!\n")) {
diff --git a/drivers/gpu/drm/i915/intel_display.c 
b/drivers/gpu/drm/i915/intel_display.c
index b0b6e1e9a294..5a67fb39300a 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -12217,6 +12217,7 @@ static int intel_modeset_checks(struct drm_atomic_state 
*state)
        intel_state->active_crtcs = dev_priv->active_crtcs;
        intel_state->cdclk.logical = dev_priv->cdclk.logical;
        intel_state->cdclk.actual = dev_priv->cdclk.actual;
+       intel_state->cdclk.pipe = INVALID_PIPE;
 
        for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, 
new_crtc_state, i) {
                if (new_crtc_state->active)
@@ -12252,12 +12253,24 @@ static int intel_modeset_checks(struct 
drm_atomic_state *state)
                                return ret;
                }
 
+
                /* All pipes must be switched off while we change the cdclk. */
-               if (intel_cdclk_needs_modeset(&dev_priv->cdclk.actual,
-                                             &intel_state->cdclk.actual)) {
+               if (is_power_of_2(intel_state->active_crtcs) &&
+                   intel_cdclk_needs_cd2x_update(dev_priv,
+                                                 &dev_priv->cdclk.actual,
+                                                 &intel_state->cdclk.actual)) {
+                       ret = intel_lock_all_pipes(state);
+                       if (ret < 0)
+                               return ret;
+
+                       intel_state->cdclk.pipe = 
ilog2(intel_state->active_crtcs);
+               } else if (intel_cdclk_needs_modeset(&dev_priv->cdclk.actual,
+                                                    
&intel_state->cdclk.actual)) {
                        ret = intel_modeset_all_pipes(state);
                        if (ret < 0)
                                return ret;
+
+                       intel_state->cdclk.pipe = INVALID_PIPE;
                }
 
                DRM_DEBUG_KMS("New cdclk calculated to be logical %u kHz, 
actual %u kHz\n",
@@ -12650,7 +12663,8 @@ static void intel_atomic_commit_tail(struct 
drm_atomic_state *state)
        if (intel_state->modeset) {
                drm_atomic_helper_update_legacy_modeset_state(state->dev, 
state);
 
-               intel_set_cdclk(dev_priv, &dev_priv->cdclk.actual);
+               intel_set_cdclk(dev_priv, &dev_priv->cdclk.actual,
+                               intel_state->cdclk.pipe);
 
                /*
                 * SKL workaround: bspec recommends we disable the SAGV when we
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 843eefaa0f0c..e5ffe42084a5 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -456,6 +456,9 @@ struct intel_atomic_state {
                 * state only when all crtc's are DPMS off.
                 */
                struct intel_cdclk_state actual;
+
+               /* pipe to which cd2x update is synchronized */
+               enum pipe pipe;
        } cdclk;
 
        bool dpll_set, modeset;
@@ -1464,12 +1467,16 @@ 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);
 void intel_update_rawclk(struct drm_i915_private *dev_priv);
+bool intel_cdclk_needs_cd2x_update(struct drm_i915_private *dev_priv,
+                                  const struct intel_cdclk_state *a,
+                                  const struct intel_cdclk_state *b);
 bool intel_cdclk_needs_modeset(const struct intel_cdclk_state *a,
                               const struct intel_cdclk_state *b);
 bool intel_cdclk_changed(const struct intel_cdclk_state *a,
                         const struct intel_cdclk_state *b);
 void intel_set_cdclk(struct drm_i915_private *dev_priv,
-                    const struct intel_cdclk_state *cdclk_state);
+                    const struct intel_cdclk_state *cdclk_state,
+                    enum pipe pipe);
 void intel_dump_cdclk_state(const struct intel_cdclk_state *cdclk_state,
                            const char *context);
 
-- 
2.7.4

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to