]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
drm/i915: Skip modeset for cdclk changes if possible
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Mon, 10 Jun 2019 07:14:00 +0000 (09:14 +0200)
committerKleber Sacilotto de Souza <kleber.souza@canonical.com>
Tue, 2 Jul 2019 12:18:49 +0000 (14:18 +0200)
BugLink: https://launchpad.net/bugs/1826868
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.

v2:
- Wait for vblank after an optimized CDCLK change.
- Avoid optimization if the pipe needs a modeset (or was disabled).
- Split CDCLK change to a pre/post plane update step.
v3:
- Use correct version of CDCLK state as old state. (Ville)
- Remove unused intel_cdclk_can_skip_modeset()
v4:
- For consistency call intel_set_cdclk_post_plane_update() only during
  modesets (and not fastsets).
v5:
- Remove the logic to update the CD2X divider on-the-fly on ICL, since
  only a divider of 1 is supported there. Clint also noticed that the
  pipe select bits in CDCLK_CTL are oddly defined on ICL, it's not clear
  yet whether that's only an error in the specification.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Abhay Kumar <abhay.kumar@intel.com>
Tested-by: Abhay Kumar <abhay.kumar@intel.com>
Signed-off-by: Imre Deak <imre.deak@intel.com>
Reviewed-by: Clint Taylor <Clinton.A.Taylor@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190327101321.3095-1-imre.deak@intel.com
(backported from commit 59f9e9cab3a1e6762fb707d0d829b982930f1349)
Signed-off-by: Hui Wang <hui.wang@canonical.com>
Acked-by: AceLan Kao <acelan.kao@canonical.com>
Acked-by: Stefan Bader <stefan.bader@canonical.com>
Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/intel_cdclk.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_drv.h

index eaba810a928e8d19004a1a56f1aac21a4f152e89..875044737f74474abdbb8b60fe24793a5a9c4e45 100644 (file)
@@ -698,7 +698,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, int plane);
        int (*compute_pipe_wm)(struct intel_crtc_state *cstate);
        int (*compute_intermediate_wm)(struct drm_device *dev,
index fd41b7296990f57ee00d76dc37ec2f9e5f6f522e..837a24f4b0e116cfed94c91a0b91f2424e1e82ef 100644 (file)
@@ -483,7 +483,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;
@@ -559,7 +560,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;
@@ -642,7 +644,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, data;
@@ -913,7 +916,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;
@@ -1073,7 +1077,7 @@ void skl_init_cdclk(struct drm_i915_private *dev_priv)
                cdclk_state.vco = 8100000;
        cdclk_state.cdclk = skl_calc_cdclk(0, cdclk_state.vco);
 
-       skl_set_cdclk(dev_priv, &cdclk_state);
+       skl_set_cdclk(dev_priv, &cdclk_state, INVALID_PIPE);
 }
 
 /**
@@ -1090,7 +1094,7 @@ void skl_uninit_cdclk(struct drm_i915_private *dev_priv)
        cdclk_state.cdclk = cdclk_state.ref;
        cdclk_state.vco = 0;
 
-       skl_set_cdclk(dev_priv, &cdclk_state);
+       skl_set_cdclk(dev_priv, &cdclk_state, INVALID_PIPE);
 }
 
 static int bxt_calc_cdclk(int min_cdclk)
@@ -1254,7 +1258,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;
@@ -1309,11 +1314,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.
@@ -1322,6 +1326,9 @@ static void bxt_set_cdclk(struct drm_i915_private *dev_priv,
                val |= BXT_CDCLK_SSA_PRECHARGE_ENABLE;
        I915_WRITE(CDCLK_CTL, val);
 
+       if (pipe != INVALID_PIPE)
+               intel_wait_for_vblank(dev_priv, pipe);
+
        mutex_lock(&dev_priv->pcu_lock);
        /*
         * The timeout isn't specified, the 2ms used here is based on
@@ -1424,7 +1431,7 @@ void bxt_init_cdclk(struct drm_i915_private *dev_priv)
                cdclk_state.vco = bxt_de_pll_vco(dev_priv, cdclk_state.cdclk);
        }
 
-       bxt_set_cdclk(dev_priv, &cdclk_state);
+       bxt_set_cdclk(dev_priv, &cdclk_state, INVALID_PIPE);
 }
 
 /**
@@ -1441,7 +1448,7 @@ void bxt_uninit_cdclk(struct drm_i915_private *dev_priv)
        cdclk_state.cdclk = cdclk_state.ref;
        cdclk_state.vco = 0;
 
-       bxt_set_cdclk(dev_priv, &cdclk_state);
+       bxt_set_cdclk(dev_priv, &cdclk_state, INVALID_PIPE);
 }
 
 static int cnl_calc_cdclk(int min_cdclk)
@@ -1540,7 +1547,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;
@@ -1596,13 +1604,15 @@ 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);
 
+       if (pipe != INVALID_PIPE)
+               intel_wait_for_vblank(dev_priv, pipe);
+
        /* inform PCU of the change */
        mutex_lock(&dev_priv->pcu_lock);
        sandybridge_pcode_write(dev_priv, SKL_PCODE_CDCLK_CONTROL, pcu_ack);
@@ -1698,7 +1708,7 @@ void cnl_init_cdclk(struct drm_i915_private *dev_priv)
        cdclk_state.cdclk = cnl_calc_cdclk(0);
        cdclk_state.vco = cnl_cdclk_pll_vco(dev_priv, cdclk_state.cdclk);
 
-       cnl_set_cdclk(dev_priv, &cdclk_state);
+       cnl_set_cdclk(dev_priv, &cdclk_state, INVALID_PIPE);
 }
 
 /**
@@ -1715,7 +1725,7 @@ void cnl_uninit_cdclk(struct drm_i915_private *dev_priv)
        cdclk_state.cdclk = cdclk_state.ref;
        cdclk_state.vco = 0;
 
-       cnl_set_cdclk(dev_priv, &cdclk_state);
+       cnl_set_cdclk(dev_priv, &cdclk_state, INVALID_PIPE);
 }
 
 /**
@@ -1732,6 +1742,27 @@ bool intel_cdclk_state_compare(const struct intel_cdclk_state *a,
        return memcmp(a, b, sizeof(*a)) == 0;
 }
 
+/**
+ * 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_swap_state - make atomic CDCLK configuration effective
  * @state: atomic state
@@ -1756,12 +1787,14 @@ void intel_cdclk_swap_state(struct intel_atomic_state *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)
+static void intel_set_cdclk(struct drm_i915_private *dev_priv,
+                           const struct intel_cdclk_state *cdclk_state,
+                           enum pipe pipe)
 {
        if (intel_cdclk_state_compare(&dev_priv->cdclk.hw, cdclk_state))
                return;
@@ -1773,7 +1806,47 @@ void intel_set_cdclk(struct drm_i915_private *dev_priv,
                         cdclk_state->cdclk, cdclk_state->vco,
                         cdclk_state->ref);
 
-       dev_priv->display.set_cdclk(dev_priv, cdclk_state);
+       dev_priv->display.set_cdclk(dev_priv, cdclk_state, pipe);
+}
+
+/**
+ * intel_set_cdclk_pre_plane_update - Push the CDCLK state to the hardware
+ * @dev_priv: i915 device
+ * @old_state: old CDCLK state
+ * @new_state: new CDCLK state
+ * @pipe: pipe with which to synchronize the update
+ *
+ * Program the hardware before updating the HW plane state based on the passed
+ * in CDCLK state, if necessary.
+ */
+void
+intel_set_cdclk_pre_plane_update(struct drm_i915_private *dev_priv,
+                                const struct intel_cdclk_state *old_state,
+                                const struct intel_cdclk_state *new_state,
+                                enum pipe pipe)
+{
+       if (pipe == INVALID_PIPE || old_state->cdclk <= new_state->cdclk)
+               intel_set_cdclk(dev_priv, new_state, pipe);
+}
+
+/**
+ * intel_set_cdclk_post_plane_update - Push the CDCLK state to the hardware
+ * @dev_priv: i915 device
+ * @old_state: old CDCLK state
+ * @new_state: new CDCLK state
+ * @pipe: pipe with which to synchronize the update
+ *
+ * Program the hardware after updating the HW plane state based on the passed
+ * in CDCLK state, if necessary.
+ */
+void
+intel_set_cdclk_post_plane_update(struct drm_i915_private *dev_priv,
+                                 const struct intel_cdclk_state *old_state,
+                                 const struct intel_cdclk_state *new_state,
+                                 enum pipe pipe)
+{
+       if (pipe != INVALID_PIPE && old_state->cdclk > new_state->cdclk)
+               intel_set_cdclk(dev_priv, new_state, pipe);
 }
 
 static int intel_pixel_rate_to_cdclk(struct drm_i915_private *dev_priv,
index 113e13af61e33e7b363b31aefe5ff3a218963795..939233362f7b8d7451de2a072027e601c498ca89 100644 (file)
@@ -11934,6 +11934,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)
@@ -11953,6 +11954,8 @@ static int intel_modeset_checks(struct drm_atomic_state *state)
         * adjusted_mode bits in the crtc directly.
         */
        if (dev_priv->display.modeset_calc_cdclk) {
+               enum pipe pipe;
+
                ret = dev_priv->display.modeset_calc_cdclk(state);
                if (ret < 0)
                        return ret;
@@ -11969,12 +11972,36 @@ static int intel_modeset_checks(struct drm_atomic_state *state)
                                return ret;
                }
 
+               if (is_power_of_2(intel_state->active_crtcs)) {
+                       struct drm_crtc *crtc;
+                       struct drm_crtc_state *crtc_state;
+
+                       pipe = ilog2(intel_state->active_crtcs);
+                       crtc = &intel_get_crtc_for_pipe(dev_priv, pipe)->base;
+                       crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+                       if (crtc_state && needs_modeset(crtc_state))
+                               pipe = INVALID_PIPE;
+               } else {
+                       pipe = INVALID_PIPE;
+               }
+
                /* All pipes must be switched off while we change the cdclk. */
-               if (!intel_cdclk_state_compare(&dev_priv->cdclk.actual,
+               if (pipe != INVALID_PIPE &&
+                   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 = pipe;
+               } else if (!intel_cdclk_state_compare(&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",
@@ -12336,7 +12363,10 @@ 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_pre_plane_update(dev_priv,
+                                                &intel_state->cdclk.actual,
+                                                &dev_priv->cdclk.actual,
+                                                intel_state->cdclk.pipe);
 
                /*
                 * SKL workaround: bspec recommends we disable the SAGV when we
@@ -12365,6 +12395,12 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
        /* Now enable the clocks, plane, pipe, and connectors that we set up. */
        dev_priv->display.update_crtcs(state);
 
+       if (intel_state->modeset)
+               intel_set_cdclk_post_plane_update(dev_priv,
+                                                 &intel_state->cdclk.actual,
+                                                 &dev_priv->cdclk.actual,
+                                                 intel_state->cdclk.pipe);
+
        /* FIXME: We should call drm_atomic_helper_commit_hw_done() here
         * already, but still need the state for the delayed optimization. To
         * fix this:
index 83d3724197bd7fcf4c82046394babd84ccb37e86..a7459a17d2d453e5ea55a3bcbe8b0b1f65ddcc1e 100644 (file)
@@ -371,6 +371,8 @@ struct intel_atomic_state {
 
                int force_min_cdclk;
                bool force_min_cdclk_changed;
+               /* pipe to which cd2x update is synchronized */
+               enum pipe pipe;
        } cdclk;
 
        bool dpll_set, modeset;
@@ -1326,10 +1328,20 @@ void intel_update_cdclk(struct drm_i915_private *dev_priv);
 void intel_update_rawclk(struct drm_i915_private *dev_priv);
 bool intel_cdclk_state_compare(const struct intel_cdclk_state *a,
                               const struct intel_cdclk_state *b);
+bool intel_cdclk_needs_cd2x_update(struct drm_i915_private *dev_priv,
+                                  const struct intel_cdclk_state *a,
+                                  const struct intel_cdclk_state *b);
 void intel_cdclk_swap_state(struct intel_atomic_state *state);
-void intel_set_cdclk(struct drm_i915_private *dev_priv,
-                    const struct intel_cdclk_state *cdclk_state);
-
+void
+intel_set_cdclk_pre_plane_update(struct drm_i915_private *dev_priv,
+                                const struct intel_cdclk_state *old_state,
+                                const struct intel_cdclk_state *new_state,
+                                enum pipe pipe);
+void
+intel_set_cdclk_post_plane_update(struct drm_i915_private *dev_priv,
+                                 const struct intel_cdclk_state *old_state,
+                                 const struct intel_cdclk_state *new_state,
+                                 enum pipe pipe);
 /* intel_display.c */
 void i830_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe);
 void i830_disable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe);