drm/i915: Allow planes to declare their minimum acceptable cdclk
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Tue, 15 Oct 2019 19:30:26 +0000 (22:30 +0300)
committerVille Syrjälä <ville.syrjala@linux.intel.com>
Thu, 24 Oct 2019 18:22:25 +0000 (21:22 +0300)
Various pixel formats and plane scaling impose additional constraints
on the cdclk frequency. Provide a new plane->min_cdclk() hook that
will be used to compute the minimum acceptable cdclk frequency for
each plane.

Annoyingly on some platforms the numer of active planes affects
this calculation so we must also toss in more planes into the
state when the number of active planes changes.

The sequence of state computation must also be changed:
1. check_plane() (updates plane's visibility etc.)
2. figure out if more planes now require update min_cdclk
   computaion
3. calculate the new min cdclk for each plane in the state
4. if the minimum of any plane now exceeds the current
   logical cdclk we recompute the cdclk
4. during cdclk computation take the planes' min_cdclk into
   accoutn
5. follow the normal cdclk programming to change the
   cdclk frequency. This may now require a modeset (except
   on bxt/glk in some cases), which either succeeds or
   fails depending on whether userspace has given
   us permission to perform a modeset or not.

v2: Fix plane id check in intel_crtc_add_planes_to_state()
    Only print the debug message when cdclk needs bumping
    Use dev_priv->cdclk... as the old state explicitly

Reviewed-by: Juha-Pekka Heikkila <juhapekka.heikkila@gmail.com>
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191015193035.25982-5-ville.syrjala@linux.intel.com
drivers/gpu/drm/i915/display/intel_atomic_plane.c
drivers/gpu/drm/i915/display/intel_atomic_plane.h
drivers/gpu/drm/i915/display/intel_cdclk.c
drivers/gpu/drm/i915/display/intel_display.c
drivers/gpu/drm/i915/display/intel_display_types.h
drivers/gpu/drm/i915/display/intel_sprite.c
drivers/gpu/drm/i915/display/intel_sprite.h

index a6cff5a160fb8d9c5d479988abda9feb061e7c18..98f557a9f8eefbc191bfe13b54683077f30e740e 100644 (file)
@@ -138,6 +138,44 @@ unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state,
        return cpp * crtc_state->pixel_rate;
 }
 
+bool intel_plane_calc_min_cdclk(struct intel_atomic_state *state,
+                               struct intel_plane *plane)
+{
+       struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
+       const struct intel_plane_state *plane_state =
+               intel_atomic_get_new_plane_state(state, plane);
+       struct intel_crtc *crtc = to_intel_crtc(plane_state->base.crtc);
+       struct intel_crtc_state *crtc_state;
+
+       if (!plane_state->base.visible || !plane->min_cdclk)
+               return false;
+
+       crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
+
+       crtc_state->min_cdclk[plane->id] =
+               plane->min_cdclk(crtc_state, plane_state);
+
+       /*
+        * Does the cdclk need to be bumbed up?
+        *
+        * Note: we obviously need to be called before the new
+        * cdclk frequency is calculated so state->cdclk.logical
+        * hasn't been populated yet. Hence we look at the old
+        * cdclk state under dev_priv->cdclk.logical. This is
+        * safe as long we hold at least one crtc mutex (which
+        * must be true since we have crtc_state).
+        */
+       if (crtc_state->min_cdclk[plane->id] > dev_priv->cdclk.logical.cdclk) {
+               DRM_DEBUG_KMS("[PLANE:%d:%s] min_cdclk (%d kHz) > logical cdclk (%d kHz)\n",
+                             plane->base.base.id, plane->base.name,
+                             crtc_state->min_cdclk[plane->id],
+                             dev_priv->cdclk.logical.cdclk);
+               return true;
+       }
+
+       return false;
+}
+
 int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_state,
                                        struct intel_crtc_state *new_crtc_state,
                                        const struct intel_plane_state *old_plane_state,
@@ -151,6 +189,7 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_
        new_crtc_state->nv12_planes &= ~BIT(plane->id);
        new_crtc_state->c8_planes &= ~BIT(plane->id);
        new_crtc_state->data_rate[plane->id] = 0;
+       new_crtc_state->min_cdclk[plane->id] = 0;
        new_plane_state->base.visible = false;
 
        if (!new_plane_state->base.crtc && !old_plane_state->base.crtc)
index dc85af02e9b7e09edd384c5d23405bf8c53e4d52..e61e9a82aadfd1960010e823ca29ee9320e7c2da 100644 (file)
@@ -47,5 +47,7 @@ int intel_plane_atomic_calc_changes(const struct intel_crtc_state *old_crtc_stat
                                    struct intel_crtc_state *crtc_state,
                                    const struct intel_plane_state *old_plane_state,
                                    struct intel_plane_state *plane_state);
+bool intel_plane_calc_min_cdclk(struct intel_atomic_state *state,
+                               struct intel_plane *plane);
 
 #endif /* __INTEL_ATOMIC_PLANE_H__ */
index 6c17a3bbf8669b7789aee34b7d9ee61f08fd5aa8..0caef2592a7e2aec8fdac9f046fd3c4317ca590c 100644 (file)
@@ -1918,6 +1918,19 @@ static int intel_pixel_rate_to_cdclk(const struct intel_crtc_state *crtc_state)
                return DIV_ROUND_UP(pixel_rate * 100, 90);
 }
 
+static int intel_planes_min_cdclk(const struct intel_crtc_state *crtc_state)
+{
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       struct intel_plane *plane;
+       int min_cdclk = 0;
+
+       for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane)
+               min_cdclk = max(crtc_state->min_cdclk[plane->id], min_cdclk);
+
+       return min_cdclk;
+}
+
 int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_state)
 {
        struct drm_i915_private *dev_priv =
@@ -1986,6 +1999,9 @@ int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_state)
            IS_GEMINILAKE(dev_priv))
                min_cdclk = max(158400, min_cdclk);
 
+       /* Account for additional needs from the planes */
+       min_cdclk = max(intel_planes_min_cdclk(crtc_state), min_cdclk);
+
        if (min_cdclk > dev_priv->max_cdclk_freq) {
                DRM_DEBUG_KMS("required cdclk (%d kHz) exceeds max (%d kHz)\n",
                              min_cdclk, dev_priv->max_cdclk_freq);
index dea81199558478d59db4e9b9aabedb9d239281f4..1fac989611835b6cd30086e50eb1ca76f7f509a0 100644 (file)
@@ -3154,6 +3154,7 @@ static void intel_plane_disable_noatomic(struct intel_crtc *crtc,
        intel_set_plane_visible(crtc_state, plane_state, false);
        fixup_active_planes(crtc_state);
        crtc_state->data_rate[plane->id] = 0;
+       crtc_state->min_cdclk[plane->id] = 0;
 
        if (plane->id == PLANE_PRIMARY)
                intel_pre_disable_primary_noatomic(&crtc->base);
@@ -3577,6 +3578,53 @@ int skl_check_plane_surface(struct intel_plane_state *plane_state)
        return 0;
 }
 
+static void i9xx_plane_ratio(const struct intel_crtc_state *crtc_state,
+                            const struct intel_plane_state *plane_state,
+                            unsigned int *num, unsigned int *den)
+{
+       const struct drm_framebuffer *fb = plane_state->base.fb;
+       unsigned int cpp = fb->format->cpp[0];
+
+       /*
+        * g4x bspec says 64bpp pixel rate can't exceed 80%
+        * of cdclk when the sprite plane is enabled on the
+        * same pipe. ilk/snb bspec says 64bpp pixel rate is
+        * never allowed to exceed 80% of cdclk. Let's just go
+        * with the ilk/snb limit always.
+        */
+       if (cpp == 8) {
+               *num = 10;
+               *den = 8;
+       } else {
+               *num = 1;
+               *den = 1;
+       }
+}
+
+static int i9xx_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
+                               const struct intel_plane_state *plane_state)
+{
+       unsigned int pixel_rate;
+       unsigned int num, den;
+
+       /*
+        * Note that crtc_state->pixel_rate accounts for both
+        * horizontal and vertical panel fitter downscaling factors.
+        * Pre-HSW bspec tells us to only consider the horizontal
+        * downscaling factor here. We ignore that and just consider
+        * both for simplicity.
+        */
+       pixel_rate = crtc_state->pixel_rate;
+
+       i9xx_plane_ratio(crtc_state, plane_state, &num, &den);
+
+       /* two pixels per clock with double wide pipe */
+       if (crtc_state->double_wide)
+               den *= 2;
+
+       return DIV_ROUND_UP(pixel_rate * num, den);
+}
+
 unsigned int
 i9xx_plane_max_stride(struct intel_plane *plane,
                      u32 pixel_format, u64 modifier,
@@ -11706,6 +11754,7 @@ int intel_plane_atomic_calc_changes(const struct intel_crtc_state *old_crtc_stat
                plane_state->base.visible = visible = false;
                crtc_state->active_planes &= ~BIT(plane->id);
                crtc_state->data_rate[plane->id] = 0;
+               crtc_state->min_cdclk[plane->id] = 0;
        }
 
        if (!was_visible && !visible)
@@ -12072,9 +12121,6 @@ static int intel_crtc_atomic_check(struct intel_atomic_state *state,
        if (INTEL_GEN(dev_priv) >= 9) {
                if (mode_changed || crtc_state->update_pipe)
                        ret = skl_update_scaler_crtc(crtc_state);
-
-               if (!ret)
-                       ret = icl_check_nv12_planes(crtc_state);
                if (!ret)
                        ret = skl_check_pipe_max_pixel_rate(crtc, crtc_state);
                if (!ret)
@@ -13796,12 +13842,49 @@ static void intel_crtc_check_fastset(const struct intel_crtc_state *old_crtc_sta
        new_crtc_state->has_drrs = old_crtc_state->has_drrs;
 }
 
-static int intel_atomic_check_planes(struct intel_atomic_state *state)
+static int intel_crtc_add_planes_to_state(struct intel_atomic_state *state,
+                                         struct intel_crtc *crtc,
+                                         u8 plane_ids_mask)
 {
+       struct drm_i915_private *dev_priv = to_i915(state->base.dev);
+       struct intel_plane *plane;
+
+       for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) {
+               struct intel_plane_state *plane_state;
+
+               if ((plane_ids_mask & BIT(plane->id)) == 0)
+                       continue;
+
+               plane_state = intel_atomic_get_plane_state(state, plane);
+               if (IS_ERR(plane_state))
+                       return PTR_ERR(plane_state);
+       }
+
+       return 0;
+}
+
+static bool active_planes_affects_min_cdclk(struct drm_i915_private *dev_priv)
+{
+       /* See {hsw,vlv,ivb}_plane_ratio() */
+       return IS_BROADWELL(dev_priv) || IS_HASWELL(dev_priv) ||
+               IS_CHERRYVIEW(dev_priv) || IS_VALLEYVIEW(dev_priv) ||
+               IS_IVYBRIDGE(dev_priv);
+}
+
+static int intel_atomic_check_planes(struct intel_atomic_state *state,
+                                    bool *need_modeset)
+{
+       struct drm_i915_private *dev_priv = to_i915(state->base.dev);
+       struct intel_crtc_state *old_crtc_state, *new_crtc_state;
        struct intel_plane_state *plane_state;
        struct intel_plane *plane;
+       struct intel_crtc *crtc;
        int i, ret;
 
+       ret = icl_add_linked_planes(state);
+       if (ret)
+               return ret;
+
        for_each_new_intel_plane_in_state(state, plane, plane_state, i) {
                ret = intel_plane_atomic_check(state, plane);
                if (ret) {
@@ -13811,6 +13894,41 @@ static int intel_atomic_check_planes(struct intel_atomic_state *state)
                }
        }
 
+       for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
+                                           new_crtc_state, i) {
+               u8 old_active_planes, new_active_planes;
+
+               ret = icl_check_nv12_planes(new_crtc_state);
+               if (ret)
+                       return ret;
+
+               /*
+                * On some platforms the number of active planes affects
+                * the planes' minimum cdclk calculation. Add such planes
+                * to the state before we compute the minimum cdclk.
+                */
+               if (!active_planes_affects_min_cdclk(dev_priv))
+                       continue;
+
+               old_active_planes = old_crtc_state->active_planes & ~BIT(PLANE_CURSOR);
+               new_active_planes = new_crtc_state->active_planes & ~BIT(PLANE_CURSOR);
+
+               if (hweight8(old_active_planes) == hweight8(new_active_planes))
+                       continue;
+
+               ret = intel_crtc_add_planes_to_state(state, crtc, new_active_planes);
+               if (ret)
+                       return ret;
+       }
+
+       /*
+        * active_planes bitmask has been updated, and potentially
+        * affected planes are part of the state. We can now
+        * compute the minimum cdclk for each plane.
+        */
+       for_each_new_intel_plane_in_state(state, plane, plane_state, i)
+               *need_modeset |= intel_plane_calc_min_cdclk(state, plane);
+
        return 0;
 }
 
@@ -13891,6 +14009,10 @@ static int intel_atomic_check(struct drm_device *dev,
 
        any_ms |= state->cdclk.force_min_cdclk_changed;
 
+       ret = intel_atomic_check_planes(state, &any_ms);
+       if (ret)
+               goto fail;
+
        if (any_ms) {
                ret = intel_modeset_checks(state);
                if (ret)
@@ -13899,14 +14021,6 @@ static int intel_atomic_check(struct drm_device *dev,
                state->cdclk.logical = dev_priv->cdclk.logical;
        }
 
-       ret = icl_add_linked_planes(state);
-       if (ret)
-               goto fail;
-
-       ret = intel_atomic_check_planes(state);
-       if (ret)
-               goto fail;
-
        ret = intel_atomic_check_crtcs(state);
        if (ret)
                goto fail;
@@ -15355,6 +15469,15 @@ intel_primary_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe)
                plane->get_hw_state = i9xx_plane_get_hw_state;
                plane->check_plane = i9xx_plane_check;
 
+               if (IS_BROADWELL(dev_priv) || IS_HASWELL(dev_priv))
+                       plane->min_cdclk = hsw_plane_min_cdclk;
+               else if (IS_IVYBRIDGE(dev_priv))
+                       plane->min_cdclk = ivb_plane_min_cdclk;
+               else if (IS_CHERRYVIEW(dev_priv) || IS_VALLEYVIEW(dev_priv))
+                       plane->min_cdclk = vlv_plane_min_cdclk;
+               else
+                       plane->min_cdclk = i9xx_plane_min_cdclk;
+
                plane_funcs = &i965_plane_funcs;
        } else {
                formats = i8xx_primary_formats;
@@ -15366,6 +15489,7 @@ intel_primary_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe)
                plane->disable_plane = i9xx_disable_plane;
                plane->get_hw_state = i9xx_plane_get_hw_state;
                plane->check_plane = i9xx_plane_check;
+               plane->min_cdclk = i9xx_plane_min_cdclk;
 
                plane_funcs = &i8xx_plane_funcs;
        }
@@ -17284,17 +17408,9 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
 
                        intel_crtc_compute_pixel_rate(crtc_state);
 
-                       min_cdclk = intel_crtc_compute_min_cdclk(crtc_state);
-                       if (WARN_ON(min_cdclk < 0))
-                               min_cdclk = 0;
-
                        intel_crtc_update_active_timings(crtc_state);
                }
 
-               dev_priv->min_cdclk[crtc->pipe] = min_cdclk;
-               dev_priv->min_voltage_level[crtc->pipe] =
-                       crtc_state->min_voltage_level;
-
                for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) {
                        const struct intel_plane_state *plane_state =
                                to_intel_plane_state(plane->base.state);
@@ -17306,8 +17422,34 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
                        if (plane_state->base.visible)
                                crtc_state->data_rate[plane->id] =
                                        4 * crtc_state->pixel_rate;
+                       /*
+                        * FIXME don't have the fb yet, so can't
+                        * use plane->min_cdclk() :(
+                        */
+                       if (plane_state->base.visible && plane->min_cdclk) {
+                               if (crtc_state->double_wide ||
+                                   INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv))
+                                       crtc_state->min_cdclk[plane->id] =
+                                               DIV_ROUND_UP(crtc_state->pixel_rate, 2);
+                               else
+                                       crtc_state->min_cdclk[plane->id] =
+                                               crtc_state->pixel_rate;
+                       }
+                       DRM_DEBUG_KMS("[PLANE:%d:%s] min_cdclk %d kHz\n",
+                                     plane->base.base.id, plane->base.name,
+                                     crtc_state->min_cdclk[plane->id]);
+               }
+
+               if (crtc_state->base.active) {
+                       min_cdclk = intel_crtc_compute_min_cdclk(crtc_state);
+                       if (WARN_ON(min_cdclk < 0))
+                               min_cdclk = 0;
                }
 
+               dev_priv->min_cdclk[crtc->pipe] = min_cdclk;
+               dev_priv->min_voltage_level[crtc->pipe] =
+                       crtc_state->min_voltage_level;
+
                intel_bw_crtc_update(bw_state, crtc_state);
 
                intel_pipe_config_sanity_check(dev_priv, crtc_state);
index d18208e590b0c9372a45b40298a11e54826ad42e..40184e823c842066b0ef8bd5cad6eb09fa9efb1d 100644 (file)
@@ -940,6 +940,8 @@ struct intel_crtc_state {
 
        struct intel_crtc_wm_state wm;
 
+       int min_cdclk[I915_MAX_PLANES];
+
        u32 data_rate[I915_MAX_PLANES];
 
        /* Gamma mode programmed on the pipe */
@@ -1085,6 +1087,8 @@ struct intel_plane {
        bool (*get_hw_state)(struct intel_plane *plane, enum pipe *pipe);
        int (*check_plane)(struct intel_crtc_state *crtc_state,
                           struct intel_plane_state *plane_state);
+       int (*min_cdclk)(const struct intel_crtc_state *crtc_state,
+                        const struct intel_plane_state *plane_state);
 };
 
 struct intel_watermark_params {
index 5ae12ab3c5b70df92f84021c072ddef9d8865995..469f79b01114e8880bbac209d3f628a44eb2def1 100644 (file)
@@ -322,6 +322,55 @@ bool icl_is_hdr_plane(struct drm_i915_private *dev_priv, enum plane_id plane_id)
                icl_hdr_plane_mask() & BIT(plane_id);
 }
 
+static void
+skl_plane_ratio(const struct intel_crtc_state *crtc_state,
+               const struct intel_plane_state *plane_state,
+               unsigned int *num, unsigned int *den)
+{
+       struct drm_i915_private *dev_priv = to_i915(plane_state->base.plane->dev);
+       const struct drm_framebuffer *fb = plane_state->base.fb;
+
+       if (fb->format->cpp[0] == 8) {
+               if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv)) {
+                       *num = 10;
+                       *den = 8;
+               } else {
+                       *num = 9;
+                       *den = 8;
+               }
+       } else {
+               *num = 1;
+               *den = 1;
+       }
+}
+
+static int skl_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
+                              const struct intel_plane_state *plane_state)
+{
+       struct drm_i915_private *dev_priv = to_i915(plane_state->base.plane->dev);
+       unsigned int pixel_rate = crtc_state->pixel_rate;
+       unsigned int src_w, src_h, dst_w, dst_h;
+       unsigned int num, den;
+
+       skl_plane_ratio(crtc_state, plane_state, &num, &den);
+
+       /* two pixels per clock on glk+ */
+       if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv))
+               den *= 2;
+
+       src_w = drm_rect_width(&plane_state->base.src) >> 16;
+       src_h = drm_rect_height(&plane_state->base.src) >> 16;
+       dst_w = drm_rect_width(&plane_state->base.dst);
+       dst_h = drm_rect_height(&plane_state->base.dst);
+
+       /* Downscaling limits the maximum pixel rate */
+       dst_w = min(src_w, dst_w);
+       dst_h = min(src_h, dst_h);
+
+       return DIV64_U64_ROUND_UP(mul_u32_u32(pixel_rate * num, src_w * src_h),
+                                 mul_u32_u32(den, dst_w * dst_h));
+}
+
 static unsigned int
 skl_plane_max_stride(struct intel_plane *plane,
                     u32 pixel_format, u64 modifier,
@@ -811,6 +860,85 @@ vlv_update_clrc(const struct intel_plane_state *plane_state)
                      SP_SH_SIN(sh_sin) | SP_SH_COS(sh_cos));
 }
 
+static void
+vlv_plane_ratio(const struct intel_crtc_state *crtc_state,
+               const struct intel_plane_state *plane_state,
+               unsigned int *num, unsigned int *den)
+{
+       u8 active_planes = crtc_state->active_planes & ~BIT(PLANE_CURSOR);
+       const struct drm_framebuffer *fb = plane_state->base.fb;
+       unsigned int cpp = fb->format->cpp[0];
+
+       /*
+        * VLV bspec only considers cases where all three planes are
+        * enabled, and cases where the primary and one sprite is enabled.
+        * Let's assume the case with just two sprites enabled also
+        * maps to the latter case.
+        */
+       if (hweight8(active_planes) == 3) {
+               switch (cpp) {
+               case 8:
+                       *num = 11;
+                       *den = 8;
+                       break;
+               case 4:
+                       *num = 18;
+                       *den = 16;
+                       break;
+               default:
+                       *num = 1;
+                       *den = 1;
+                       break;
+               }
+       } else if (hweight8(active_planes) == 2) {
+               switch (cpp) {
+               case 8:
+                       *num = 10;
+                       *den = 8;
+                       break;
+               case 4:
+                       *num = 17;
+                       *den = 16;
+                       break;
+               default:
+                       *num = 1;
+                       *den = 1;
+                       break;
+               }
+       } else {
+               switch (cpp) {
+               case 8:
+                       *num = 10;
+                       *den = 8;
+                       break;
+               default:
+                       *num = 1;
+                       *den = 1;
+                       break;
+               }
+       }
+}
+
+int vlv_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
+                       const struct intel_plane_state *plane_state)
+{
+       unsigned int pixel_rate;
+       unsigned int num, den;
+
+       /*
+        * Note that crtc_state->pixel_rate accounts for both
+        * horizontal and vertical panel fitter downscaling factors.
+        * Pre-HSW bspec tells us to only consider the horizontal
+        * downscaling factor here. We ignore that and just consider
+        * both for simplicity.
+        */
+       pixel_rate = crtc_state->pixel_rate;
+
+       vlv_plane_ratio(crtc_state, plane_state, &num, &den);
+
+       return DIV_ROUND_UP(pixel_rate * num, den);
+}
+
 static u32 vlv_sprite_ctl_crtc(const struct intel_crtc_state *crtc_state)
 {
        u32 sprctl = 0;
@@ -1017,6 +1145,164 @@ vlv_plane_get_hw_state(struct intel_plane *plane,
        return ret;
 }
 
+static void ivb_plane_ratio(const struct intel_crtc_state *crtc_state,
+                           const struct intel_plane_state *plane_state,
+                           unsigned int *num, unsigned int *den)
+{
+       u8 active_planes = crtc_state->active_planes & ~BIT(PLANE_CURSOR);
+       const struct drm_framebuffer *fb = plane_state->base.fb;
+       unsigned int cpp = fb->format->cpp[0];
+
+       if (hweight8(active_planes) == 2) {
+               switch (cpp) {
+               case 8:
+                       *num = 10;
+                       *den = 8;
+                       break;
+               case 4:
+                       *num = 17;
+                       *den = 16;
+                       break;
+               default:
+                       *num = 1;
+                       *den = 1;
+                       break;
+               }
+       } else {
+               switch (cpp) {
+               case 8:
+                       *num = 9;
+                       *den = 8;
+                       break;
+               default:
+                       *num = 1;
+                       *den = 1;
+                       break;
+               }
+       }
+}
+
+static void ivb_plane_ratio_scaling(const struct intel_crtc_state *crtc_state,
+                                   const struct intel_plane_state *plane_state,
+                                   unsigned int *num, unsigned int *den)
+{
+       const struct drm_framebuffer *fb = plane_state->base.fb;
+       unsigned int cpp = fb->format->cpp[0];
+
+       switch (cpp) {
+       case 8:
+               *num = 12;
+               *den = 8;
+               break;
+       case 4:
+               *num = 19;
+               *den = 16;
+               break;
+       case 2:
+               *num = 33;
+               *den = 32;
+               break;
+       default:
+               *num = 1;
+               *den = 1;
+               break;
+       }
+}
+
+int ivb_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
+                       const struct intel_plane_state *plane_state)
+{
+       unsigned int pixel_rate;
+       unsigned int num, den;
+
+       /*
+        * Note that crtc_state->pixel_rate accounts for both
+        * horizontal and vertical panel fitter downscaling factors.
+        * Pre-HSW bspec tells us to only consider the horizontal
+        * downscaling factor here. We ignore that and just consider
+        * both for simplicity.
+        */
+       pixel_rate = crtc_state->pixel_rate;
+
+       ivb_plane_ratio(crtc_state, plane_state, &num, &den);
+
+       return DIV_ROUND_UP(pixel_rate * num, den);
+}
+
+static int ivb_sprite_min_cdclk(const struct intel_crtc_state *crtc_state,
+                               const struct intel_plane_state *plane_state)
+{
+       unsigned int src_w, dst_w, pixel_rate;
+       unsigned int num, den;
+
+       /*
+        * Note that crtc_state->pixel_rate accounts for both
+        * horizontal and vertical panel fitter downscaling factors.
+        * Pre-HSW bspec tells us to only consider the horizontal
+        * downscaling factor here. We ignore that and just consider
+        * both for simplicity.
+        */
+       pixel_rate = crtc_state->pixel_rate;
+
+       src_w = drm_rect_width(&plane_state->base.src) >> 16;
+       dst_w = drm_rect_width(&plane_state->base.dst);
+
+       if (src_w != dst_w)
+               ivb_plane_ratio_scaling(crtc_state, plane_state, &num, &den);
+       else
+               ivb_plane_ratio(crtc_state, plane_state, &num, &den);
+
+       /* Horizontal downscaling limits the maximum pixel rate */
+       dst_w = min(src_w, dst_w);
+
+       return DIV_ROUND_UP_ULL(mul_u32_u32(pixel_rate, num * src_w),
+                               den * dst_w);
+}
+
+static void hsw_plane_ratio(const struct intel_crtc_state *crtc_state,
+                           const struct intel_plane_state *plane_state,
+                           unsigned int *num, unsigned int *den)
+{
+       u8 active_planes = crtc_state->active_planes & ~BIT(PLANE_CURSOR);
+       const struct drm_framebuffer *fb = plane_state->base.fb;
+       unsigned int cpp = fb->format->cpp[0];
+
+       if (hweight8(active_planes) == 2) {
+               switch (cpp) {
+               case 8:
+                       *num = 10;
+                       *den = 8;
+                       break;
+               default:
+                       *num = 1;
+                       *den = 1;
+                       break;
+               }
+       } else {
+               switch (cpp) {
+               case 8:
+                       *num = 9;
+                       *den = 8;
+                       break;
+               default:
+                       *num = 1;
+                       *den = 1;
+                       break;
+               }
+       }
+}
+
+int hsw_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
+                       const struct intel_plane_state *plane_state)
+{
+       unsigned int pixel_rate = crtc_state->pixel_rate;
+       unsigned int num, den;
+
+       hsw_plane_ratio(crtc_state, plane_state, &num, &den);
+
+       return DIV_ROUND_UP(pixel_rate * num, den);
+}
+
 static u32 ivb_sprite_ctl_crtc(const struct intel_crtc_state *crtc_state)
 {
        u32 sprctl = 0;
@@ -1243,6 +1529,53 @@ ivb_plane_get_hw_state(struct intel_plane *plane,
        return ret;
 }
 
+static int g4x_sprite_min_cdclk(const struct intel_crtc_state *crtc_state,
+                               const struct intel_plane_state *plane_state)
+{
+       const struct drm_framebuffer *fb = plane_state->base.fb;
+       unsigned int hscale, pixel_rate;
+       unsigned int limit, decimate;
+
+       /*
+        * Note that crtc_state->pixel_rate accounts for both
+        * horizontal and vertical panel fitter downscaling factors.
+        * Pre-HSW bspec tells us to only consider the horizontal
+        * downscaling factor here. We ignore that and just consider
+        * both for simplicity.
+        */
+       pixel_rate = crtc_state->pixel_rate;
+
+       /* Horizontal downscaling limits the maximum pixel rate */
+       hscale = drm_rect_calc_hscale(&plane_state->base.src,
+                                     &plane_state->base.dst,
+                                     0, INT_MAX);
+       if (hscale < 0x10000)
+               return pixel_rate;
+
+       /* Decimation steps at 2x,4x,8x,16x */
+       decimate = ilog2(hscale >> 16);
+       hscale >>= decimate;
+
+       /* Starting limit is 90% of cdclk */
+       limit = 9;
+
+       /* -10% per decimation step */
+       limit -= decimate;
+
+       /* -10% for RGB */
+       if (fb->format->cpp[0] >= 4)
+               limit--; /* -10% for RGB */
+
+       /*
+        * We should also do -10% if sprite scaling is enabled
+        * on the other pipe, but we can't really check for that,
+        * so we ignore it.
+        */
+
+       return DIV_ROUND_UP_ULL(mul_u32_u32(pixel_rate, 10 * hscale),
+                               limit << 16);
+}
+
 static unsigned int
 g4x_sprite_max_stride(struct intel_plane *plane,
                      u32 pixel_format, u64 modifier,
@@ -2511,6 +2844,7 @@ skl_universal_plane_create(struct drm_i915_private *dev_priv,
        plane->disable_plane = skl_disable_plane;
        plane->get_hw_state = skl_plane_get_hw_state;
        plane->check_plane = skl_plane_check;
+       plane->min_cdclk = skl_plane_min_cdclk;
        if (icl_is_nv12_y_plane(plane_id))
                plane->update_slave = icl_update_slave;
 
@@ -2618,6 +2952,7 @@ intel_sprite_plane_create(struct drm_i915_private *dev_priv,
                plane->disable_plane = vlv_disable_plane;
                plane->get_hw_state = vlv_plane_get_hw_state;
                plane->check_plane = vlv_sprite_check;
+               plane->min_cdclk = vlv_plane_min_cdclk;
 
                formats = vlv_plane_formats;
                num_formats = ARRAY_SIZE(vlv_plane_formats);
@@ -2631,6 +2966,11 @@ intel_sprite_plane_create(struct drm_i915_private *dev_priv,
                plane->get_hw_state = ivb_plane_get_hw_state;
                plane->check_plane = g4x_sprite_check;
 
+               if (IS_BROADWELL(dev_priv) || IS_HASWELL(dev_priv))
+                       plane->min_cdclk = hsw_plane_min_cdclk;
+               else
+                       plane->min_cdclk = ivb_sprite_min_cdclk;
+
                formats = snb_plane_formats;
                num_formats = ARRAY_SIZE(snb_plane_formats);
                modifiers = i9xx_plane_format_modifiers;
@@ -2642,6 +2982,7 @@ intel_sprite_plane_create(struct drm_i915_private *dev_priv,
                plane->disable_plane = g4x_disable_plane;
                plane->get_hw_state = g4x_plane_get_hw_state;
                plane->check_plane = g4x_sprite_check;
+               plane->min_cdclk = g4x_sprite_min_cdclk;
 
                modifiers = i9xx_plane_format_modifiers;
                if (IS_GEN(dev_priv, 6)) {
index 229336214f6857b290fa13c3bbb3621346cba0ef..5eeaa92420d1071d3c41d3ce7568aea70389283c 100644 (file)
@@ -49,4 +49,11 @@ static inline u8 icl_hdr_plane_mask(void)
 
 bool icl_is_hdr_plane(struct drm_i915_private *dev_priv, enum plane_id plane_id);
 
+int ivb_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
+                       const struct intel_plane_state *plane_state);
+int hsw_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
+                       const struct intel_plane_state *plane_state);
+int vlv_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
+                       const struct intel_plane_state *plane_state);
+
 #endif /* __INTEL_SPRITE_H__ */