> -----Original Message-----
> From: Intel-gfx <[email protected]> On Behalf Of Ville 
> Syrjala
> Sent: Monday, 13 October 2025 23.12
> To: [email protected]
> Cc: [email protected]
> Subject: [PATCH 1/9] drm/i915/bw: Untangle dbuf bw from the sagv/mem bw stuff
> 
> From: Ville Syrjälä <[email protected]>
> 
> Currently intel_bw.c contains basically three completely independent
> parts:
> - SAGV/memory bandwidth handling
> - DBuf bandwidth handling
> - "Maximum pipe read bandwidth" calculation, which is some kind
>   of internal per-pipe bandwidth limit.
> 
> Carve out the DBuf bandwdith handling into a separate file since there is no 
> actual dependency between it and the rest of
> intel_bw.c.
> 

Reviewed-by: Mika Kahola <[email protected]>

> Signed-off-by: Ville Syrjälä <[email protected]>
> ---
>  drivers/gpu/drm/i915/Makefile                 |   1 +
>  drivers/gpu/drm/i915/display/intel_bw.c       | 191 ------------
>  drivers/gpu/drm/i915/display/intel_bw.h       |   4 -
>  drivers/gpu/drm/i915/display/intel_cdclk.c    |  32 +-
>  drivers/gpu/drm/i915/display/intel_cdclk.h    |   7 +-
>  drivers/gpu/drm/i915/display/intel_dbuf_bw.c  | 295 ++++++++++++++++++  
> drivers/gpu/drm/i915/display/intel_dbuf_bw.h  |
> 37 +++
>  .../gpu/drm/i915/display/intel_display_core.h |   4 +
>  .../drm/i915/display/intel_display_driver.c   |   5 +
>  .../drm/i915/display/intel_modeset_setup.c    |   3 +
>  drivers/gpu/drm/xe/Makefile                   |   1 +
>  11 files changed, 363 insertions(+), 217 deletions(-)  create mode 100644 
> drivers/gpu/drm/i915/display/intel_dbuf_bw.c
>  create mode 100644 drivers/gpu/drm/i915/display/intel_dbuf_bw.h
> 
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile 
> index 6d7800e25e55..dbdf88b42919 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -241,6 +241,7 @@ i915-y += \
>       display/intel_crtc.o \
>       display/intel_crtc_state_dump.o \
>       display/intel_cursor.o \
> +     display/intel_dbuf_bw.o \
>       display/intel_display.o \
>       display/intel_display_conversion.o \
>       display/intel_display_driver.o \
> diff --git a/drivers/gpu/drm/i915/display/intel_bw.c 
> b/drivers/gpu/drm/i915/display/intel_bw.c
> index b53bcb693e79..a4d16711d336 100644
> --- a/drivers/gpu/drm/i915/display/intel_bw.c
> +++ b/drivers/gpu/drm/i915/display/intel_bw.c
> @@ -3,16 +3,12 @@
>   * Copyright © 2019 Intel Corporation
>   */
> 
> -#include <drm/drm_atomic_state_helper.h>
> -
>  #include "soc/intel_dram.h"
> 
>  #include "i915_drv.h"
>  #include "i915_reg.h"
>  #include "i915_utils.h"
> -#include "intel_atomic.h"
>  #include "intel_bw.h"
> -#include "intel_cdclk.h"
>  #include "intel_crtc.h"
>  #include "intel_display_core.h"
>  #include "intel_display_regs.h"
> @@ -22,14 +18,8 @@
>  #include "intel_uncore.h"
>  #include "skl_watermark.h"
> 
> -struct intel_dbuf_bw {
> -     unsigned int max_bw[I915_MAX_DBUF_SLICES];
> -     u8 active_planes[I915_MAX_DBUF_SLICES];
> -};
> -
>  struct intel_bw_state {
>       struct intel_global_state base;
> -     struct intel_dbuf_bw dbuf_bw[I915_MAX_PIPES];
> 
>       /*
>        * Contains a bit mask, used to determine, whether correspondent @@ 
> -1264,184 +1254,6 @@ static int
> intel_bw_check_qgv_points(struct intel_display *display,
>                                          old_bw_state, new_bw_state);
>  }
> 
> -static bool intel_dbuf_bw_changed(struct intel_display *display,
> -                               const struct intel_dbuf_bw *old_dbuf_bw,
> -                               const struct intel_dbuf_bw *new_dbuf_bw)
> -{
> -     enum dbuf_slice slice;
> -
> -     for_each_dbuf_slice(display, slice) {
> -             if (old_dbuf_bw->max_bw[slice] != new_dbuf_bw->max_bw[slice] ||
> -                 old_dbuf_bw->active_planes[slice] != 
> new_dbuf_bw->active_planes[slice])
> -                     return true;
> -     }
> -
> -     return false;
> -}
> -
> -static bool intel_bw_state_changed(struct intel_display *display,
> -                                const struct intel_bw_state *old_bw_state,
> -                                const struct intel_bw_state *new_bw_state)
> -{
> -     enum pipe pipe;
> -
> -     for_each_pipe(display, pipe) {
> -             const struct intel_dbuf_bw *old_dbuf_bw =
> -                     &old_bw_state->dbuf_bw[pipe];
> -             const struct intel_dbuf_bw *new_dbuf_bw =
> -                     &new_bw_state->dbuf_bw[pipe];
> -
> -             if (intel_dbuf_bw_changed(display, old_dbuf_bw, new_dbuf_bw))
> -                     return true;
> -     }
> -
> -     return false;
> -}
> -
> -static void skl_plane_calc_dbuf_bw(struct intel_dbuf_bw *dbuf_bw,
> -                                struct intel_crtc *crtc,
> -                                enum plane_id plane_id,
> -                                const struct skl_ddb_entry *ddb,
> -                                unsigned int data_rate)
> -{
> -     struct intel_display *display = to_intel_display(crtc);
> -     unsigned int dbuf_mask = skl_ddb_dbuf_slice_mask(display, ddb);
> -     enum dbuf_slice slice;
> -
> -     /*
> -      * The arbiter can only really guarantee an
> -      * equal share of the total bw to each plane.
> -      */
> -     for_each_dbuf_slice_in_mask(display, slice, dbuf_mask) {
> -             dbuf_bw->max_bw[slice] = max(dbuf_bw->max_bw[slice], data_rate);
> -             dbuf_bw->active_planes[slice] |= BIT(plane_id);
> -     }
> -}
> -
> -static void skl_crtc_calc_dbuf_bw(struct intel_dbuf_bw *dbuf_bw,
> -                               const struct intel_crtc_state *crtc_state)
> -{
> -     struct intel_display *display = to_intel_display(crtc_state);
> -     struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
> -     enum plane_id plane_id;
> -
> -     memset(dbuf_bw, 0, sizeof(*dbuf_bw));
> -
> -     if (!crtc_state->hw.active)
> -             return;
> -
> -     for_each_plane_id_on_crtc(crtc, plane_id) {
> -             /*
> -              * We assume cursors are small enough
> -              * to not cause bandwidth problems.
> -              */
> -             if (plane_id == PLANE_CURSOR)
> -                     continue;
> -
> -             skl_plane_calc_dbuf_bw(dbuf_bw, crtc, plane_id,
> -                                    &crtc_state->wm.skl.plane_ddb[plane_id],
> -                                    crtc_state->data_rate[plane_id]);
> -
> -             if (DISPLAY_VER(display) < 11)
> -                     skl_plane_calc_dbuf_bw(dbuf_bw, crtc, plane_id,
> -                                            
> &crtc_state->wm.skl.plane_ddb_y[plane_id],
> -                                            crtc_state->data_rate[plane_id]);
> -     }
> -}
> -
> -/* "Maximum Data Buffer Bandwidth" */
> -static int
> -intel_bw_dbuf_min_cdclk(struct intel_display *display,
> -                     const struct intel_bw_state *bw_state)
> -{
> -     unsigned int total_max_bw = 0;
> -     enum dbuf_slice slice;
> -
> -     for_each_dbuf_slice(display, slice) {
> -             int num_active_planes = 0;
> -             unsigned int max_bw = 0;
> -             enum pipe pipe;
> -
> -             /*
> -              * The arbiter can only really guarantee an
> -              * equal share of the total bw to each plane.
> -              */
> -             for_each_pipe(display, pipe) {
> -                     const struct intel_dbuf_bw *dbuf_bw = 
> &bw_state->dbuf_bw[pipe];
> -
> -                     max_bw = max(dbuf_bw->max_bw[slice], max_bw);
> -                     num_active_planes += 
> hweight8(dbuf_bw->active_planes[slice]);
> -             }
> -             max_bw *= num_active_planes;
> -
> -             total_max_bw = max(total_max_bw, max_bw);
> -     }
> -
> -     return DIV_ROUND_UP(total_max_bw, 64);
> -}
> -
> -int intel_bw_min_cdclk(struct intel_display *display,
> -                    const struct intel_bw_state *bw_state)
> -{
> -     int min_cdclk;
> -
> -     min_cdclk = intel_bw_dbuf_min_cdclk(display, bw_state);
> -
> -     return min_cdclk;
> -}
> -
> -int intel_bw_calc_min_cdclk(struct intel_atomic_state *state,
> -                         bool *need_cdclk_calc)
> -{
> -     struct intel_display *display = to_intel_display(state);
> -     struct intel_bw_state *new_bw_state = NULL;
> -     const struct intel_bw_state *old_bw_state = NULL;
> -     const struct intel_crtc_state *old_crtc_state;
> -     const struct intel_crtc_state *new_crtc_state;
> -     struct intel_crtc *crtc;
> -     int ret, i;
> -
> -     if (DISPLAY_VER(display) < 9)
> -             return 0;
> -
> -     for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
> -                                         new_crtc_state, i) {
> -             struct intel_dbuf_bw old_dbuf_bw, new_dbuf_bw;
> -
> -             skl_crtc_calc_dbuf_bw(&old_dbuf_bw, old_crtc_state);
> -             skl_crtc_calc_dbuf_bw(&new_dbuf_bw, new_crtc_state);
> -
> -             if (!intel_dbuf_bw_changed(display, &old_dbuf_bw, &new_dbuf_bw))
> -                     continue;
> -
> -             new_bw_state = intel_atomic_get_bw_state(state);
> -             if (IS_ERR(new_bw_state))
> -                     return PTR_ERR(new_bw_state);
> -
> -             old_bw_state = intel_atomic_get_old_bw_state(state);
> -
> -             new_bw_state->dbuf_bw[crtc->pipe] = new_dbuf_bw;
> -     }
> -
> -     if (!old_bw_state)
> -             return 0;
> -
> -     if (intel_bw_state_changed(display, old_bw_state, new_bw_state)) {
> -             int ret = intel_atomic_lock_global_state(&new_bw_state->base);
> -             if (ret)
> -                     return ret;
> -     }
> -
> -     ret = intel_cdclk_update_bw_min_cdclk(state,
> -                                           intel_bw_min_cdclk(display, 
> old_bw_state),
> -                                           intel_bw_min_cdclk(display, 
> new_bw_state),
> -                                           need_cdclk_calc);
> -     if (ret)
> -             return ret;
> -
> -     return 0;
> -}
> -
>  static int intel_bw_check_data_rate(struct intel_atomic_state *state, bool 
> *changed)  {
>       struct intel_display *display = to_intel_display(state); @@ -1647,8 
> +1459,6 @@ void intel_bw_update_hw_state(struct
> intel_display *display)
>               if (DISPLAY_VER(display) >= 11)
>                       intel_bw_crtc_update(bw_state, crtc_state);
> 
> -             skl_crtc_calc_dbuf_bw(&bw_state->dbuf_bw[pipe], crtc_state);
> -
>               /* initially SAGV has been forced off */
>               bw_state->pipe_sagv_reject |= BIT(pipe);
>       }
> @@ -1666,7 +1476,6 @@ void intel_bw_crtc_disable_noatomic(struct intel_crtc 
> *crtc)
> 
>       bw_state->data_rate[pipe] = 0;
>       bw_state->num_active_planes[pipe] = 0;
> -     memset(&bw_state->dbuf_bw[pipe], 0, sizeof(bw_state->dbuf_bw[pipe]));
>  }
> 
>  static struct intel_global_state *
> diff --git a/drivers/gpu/drm/i915/display/intel_bw.h 
> b/drivers/gpu/drm/i915/display/intel_bw.h
> index 4bb3a637b295..051e163f2f15 100644
> --- a/drivers/gpu/drm/i915/display/intel_bw.h
> +++ b/drivers/gpu/drm/i915/display/intel_bw.h
> @@ -30,10 +30,6 @@ void intel_bw_init_hw(struct intel_display *display);  int 
> intel_bw_init(struct intel_display *display);  int
> intel_bw_atomic_check(struct intel_atomic_state *state);  int 
> intel_bw_crtc_min_cdclk(const struct intel_crtc_state *crtc_state);
> -int intel_bw_calc_min_cdclk(struct intel_atomic_state *state,
> -                         bool *need_cdclk_calc);
> -int intel_bw_min_cdclk(struct intel_display *display,
> -                    const struct intel_bw_state *bw_state);
>  void intel_bw_update_hw_state(struct intel_display *display);  void 
> intel_bw_crtc_disable_noatomic(struct intel_crtc *crtc);
> 
> diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c 
> b/drivers/gpu/drm/i915/display/intel_cdclk.c
> index f2e092f89ddd..23b9e100d824 100644
> --- a/drivers/gpu/drm/i915/display/intel_cdclk.c
> +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c
> @@ -38,6 +38,7 @@
>  #include "intel_bw.h"
>  #include "intel_cdclk.h"
>  #include "intel_crtc.h"
> +#include "intel_dbuf_bw.h"
>  #include "intel_de.h"
>  #include "intel_display_regs.h"
>  #include "intel_display_types.h"
> @@ -133,8 +134,8 @@ struct intel_cdclk_state {
>        */
>       struct intel_cdclk_config actual;
> 
> -     /* minimum acceptable cdclk to satisfy bandwidth requirements */
> -     int bw_min_cdclk;
> +     /* minimum acceptable cdclk to satisfy DBUF bandwidth requirements */
> +     int dbuf_bw_min_cdclk;
>       /* minimum acceptable cdclk for each pipe */
>       int min_cdclk[I915_MAX_PIPES];
>       /* minimum acceptable voltage level for each pipe */ @@ -2891,9 +2892,9 
> @@ static int
> intel_cdclk_update_crtc_min_cdclk(struct intel_atomic_state *state,
>       return 0;
>  }
> 
> -int intel_cdclk_update_bw_min_cdclk(struct intel_atomic_state *state,
> -                                 int old_min_cdclk, int new_min_cdclk,
> -                                 bool *need_cdclk_calc)
> +int intel_cdclk_update_dbuf_bw_min_cdclk(struct intel_atomic_state *state,
> +                                      int old_min_cdclk, int new_min_cdclk,
> +                                      bool *need_cdclk_calc)
>  {
>       struct intel_display *display = to_intel_display(state);
>       struct intel_cdclk_state *cdclk_state; @@ -2910,7 +2911,7 @@ int 
> intel_cdclk_update_bw_min_cdclk(struct
> intel_atomic_state *state,
>       if (IS_ERR(cdclk_state))
>               return PTR_ERR(cdclk_state);
> 
> -     old_min_cdclk = cdclk_state->bw_min_cdclk;
> +     old_min_cdclk = cdclk_state->dbuf_bw_min_cdclk;
> 
>       if (new_min_cdclk == old_min_cdclk)
>               return 0;
> @@ -2918,7 +2919,7 @@ int intel_cdclk_update_bw_min_cdclk(struct 
> intel_atomic_state *state,
>       if (!allow_cdclk_decrease && new_min_cdclk < old_min_cdclk)
>               return 0;
> 
> -     cdclk_state->bw_min_cdclk = new_min_cdclk;
> +     cdclk_state->dbuf_bw_min_cdclk = new_min_cdclk;
> 
>       ret = intel_atomic_lock_global_state(&cdclk_state->base);
>       if (ret)
> @@ -2927,7 +2928,7 @@ int intel_cdclk_update_bw_min_cdclk(struct 
> intel_atomic_state *state,
>       *need_cdclk_calc = true;
> 
>       drm_dbg_kms(display->drm,
> -                 "bandwidth min cdclk: %d kHz -> %d kHz\n",
> +                 "dbuf bandwidth min cdclk: %d kHz -> %d kHz\n",
>                   old_min_cdclk, new_min_cdclk);
> 
>       return 0;
> @@ -2950,7 +2951,7 @@ static int intel_compute_min_cdclk(struct 
> intel_atomic_state *state)
>       int min_cdclk;
> 
>       min_cdclk = cdclk_state->force_min_cdclk;
> -     min_cdclk = max(min_cdclk, cdclk_state->bw_min_cdclk);
> +     min_cdclk = max(min_cdclk, cdclk_state->dbuf_bw_min_cdclk);
>       for_each_pipe(display, pipe)
>               min_cdclk = max(min_cdclk, cdclk_state->min_cdclk[pipe]);
> 
> @@ -3476,7 +3477,7 @@ int intel_cdclk_atomic_check(struct intel_atomic_state 
> *state)
>       if (ret)
>               return ret;
> 
> -     ret = intel_bw_calc_min_cdclk(state, &need_cdclk_calc);
> +     ret = intel_dbuf_bw_calc_min_cdclk(state, &need_cdclk_calc);
>       if (ret)
>               return ret;
> 
> @@ -3503,8 +3504,8 @@ int intel_cdclk_atomic_check(struct intel_atomic_state 
> *state)
> 
>  void intel_cdclk_update_hw_state(struct intel_display *display)  {
> -     const struct intel_bw_state *bw_state =
> -             to_intel_bw_state(display->bw.obj.state);
> +     const struct intel_dbuf_bw_state *dbuf_bw_state =
> +             to_intel_dbuf_bw_state(display->dbuf_bw.obj.state);
>       struct intel_cdclk_state *cdclk_state =
>               to_intel_cdclk_state(display->cdclk.obj.state);
>       struct intel_crtc *crtc;
> @@ -3526,7 +3527,7 @@ void intel_cdclk_update_hw_state(struct intel_display 
> *display)
>               cdclk_state->min_voltage_level[pipe] = 
> crtc_state->min_voltage_level;
>       }
> 
> -     cdclk_state->bw_min_cdclk = intel_bw_min_cdclk(display, bw_state);
> +     cdclk_state->dbuf_bw_min_cdclk = intel_dbuf_bw_min_cdclk(display,
> +dbuf_bw_state);
>  }
> 
>  void intel_cdclk_crtc_disable_noatomic(struct intel_crtc *crtc) @@ -4020,11 
> +4021,6 @@ int intel_cdclk_min_cdclk(const struct
> intel_cdclk_state *cdclk_state, enum pipe
>       return cdclk_state->min_cdclk[pipe];
>  }
> 
> -int intel_cdclk_bw_min_cdclk(const struct intel_cdclk_state *cdclk_state) -{
> -     return cdclk_state->bw_min_cdclk;
> -}
> -
>  bool intel_cdclk_pmdemand_needs_update(struct intel_atomic_state *state)  {
>       const struct intel_cdclk_state *new_cdclk_state, *old_cdclk_state; diff 
> --git a/drivers/gpu/drm/i915/display/intel_cdclk.h
> b/drivers/gpu/drm/i915/display/intel_cdclk.h
> index 72963f6f399a..d9d7a8b3a48a 100644
> --- a/drivers/gpu/drm/i915/display/intel_cdclk.h
> +++ b/drivers/gpu/drm/i915/display/intel_cdclk.h
> @@ -46,9 +46,9 @@ struct intel_cdclk_state *  
> intel_atomic_get_cdclk_state(struct intel_atomic_state *state);  void
> intel_cdclk_update_hw_state(struct intel_display *display);  void 
> intel_cdclk_crtc_disable_noatomic(struct intel_crtc *crtc); -int
> intel_cdclk_update_bw_min_cdclk(struct intel_atomic_state *state,
> -                                 int old_min_cdclk, int new_min_cdclk,
> -                                 bool *need_cdclk_calc);
> +int intel_cdclk_update_dbuf_bw_min_cdclk(struct intel_atomic_state *state,
> +                                      int old_min_cdclk, int new_min_cdclk,
> +                                      bool *need_cdclk_calc);
> 
>  #define to_intel_cdclk_state(global_state) \
>       container_of_const((global_state), struct intel_cdclk_state, base) @@ 
> -65,7 +65,6 @@ int intel_cdclk_logical(const struct
> intel_cdclk_state *cdclk_state);  int intel_cdclk_actual(const struct 
> intel_cdclk_state *cdclk_state);  int
> intel_cdclk_actual_voltage_level(const struct intel_cdclk_state 
> *cdclk_state);  int intel_cdclk_min_cdclk(const struct
> intel_cdclk_state *cdclk_state, enum pipe pipe); -int 
> intel_cdclk_bw_min_cdclk(const struct intel_cdclk_state *cdclk_state);  bool
> intel_cdclk_pmdemand_needs_update(struct intel_atomic_state *state);  void 
> intel_cdclk_force_min_cdclk(struct
> intel_cdclk_state *cdclk_state, int force_min_cdclk);  void 
> intel_cdclk_read_hw(struct intel_display *display); diff --git
> a/drivers/gpu/drm/i915/display/intel_dbuf_bw.c 
> b/drivers/gpu/drm/i915/display/intel_dbuf_bw.c
> new file mode 100644
> index 000000000000..8b8894c37f63
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/display/intel_dbuf_bw.c
> @@ -0,0 +1,295 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2025 Intel Corporation
> + */
> +
> +#include <drm/drm_print.h>
> +
> +#include "intel_dbuf_bw.h"
> +#include "intel_display_core.h"
> +#include "intel_display_types.h"
> +#include "skl_watermark.h"
> +
> +struct intel_dbuf_bw {
> +     unsigned int max_bw[I915_MAX_DBUF_SLICES];
> +     u8 active_planes[I915_MAX_DBUF_SLICES];
> +};
> +
> +struct intel_dbuf_bw_state {
> +     struct intel_global_state base;
> +     struct intel_dbuf_bw dbuf_bw[I915_MAX_PIPES]; };
> +
> +struct intel_dbuf_bw_state *to_intel_dbuf_bw_state(struct
> +intel_global_state *obj_state) {
> +     return container_of(obj_state, struct intel_dbuf_bw_state, base); }
> +
> +struct intel_dbuf_bw_state *
> +intel_atomic_get_old_dbuf_bw_state(struct intel_atomic_state *state) {
> +     struct intel_display *display = to_intel_display(state);
> +     struct intel_global_state *dbuf_bw_state;
> +
> +     dbuf_bw_state = intel_atomic_get_old_global_obj_state(state,
> +&display->dbuf_bw.obj);
> +
> +     return to_intel_dbuf_bw_state(dbuf_bw_state);
> +}
> +
> +struct intel_dbuf_bw_state *
> +intel_atomic_get_new_dbuf_bw_state(struct intel_atomic_state *state) {
> +     struct intel_display *display = to_intel_display(state);
> +     struct intel_global_state *dbuf_bw_state;
> +
> +     dbuf_bw_state = intel_atomic_get_new_global_obj_state(state,
> +&display->dbuf_bw.obj);
> +
> +     return to_intel_dbuf_bw_state(dbuf_bw_state);
> +}
> +
> +struct intel_dbuf_bw_state *
> +intel_atomic_get_dbuf_bw_state(struct intel_atomic_state *state) {
> +     struct intel_display *display = to_intel_display(state);
> +     struct intel_global_state *dbuf_bw_state;
> +
> +     dbuf_bw_state = intel_atomic_get_global_obj_state(state, 
> &display->dbuf_bw.obj);
> +     if (IS_ERR(dbuf_bw_state))
> +             return ERR_CAST(dbuf_bw_state);
> +
> +     return to_intel_dbuf_bw_state(dbuf_bw_state);
> +}
> +
> +static bool intel_dbuf_bw_changed(struct intel_display *display,
> +                               const struct intel_dbuf_bw *old_dbuf_bw,
> +                               const struct intel_dbuf_bw *new_dbuf_bw) {
> +     enum dbuf_slice slice;
> +
> +     for_each_dbuf_slice(display, slice) {
> +             if (old_dbuf_bw->max_bw[slice] != new_dbuf_bw->max_bw[slice] ||
> +                 old_dbuf_bw->active_planes[slice] != 
> new_dbuf_bw->active_planes[slice])
> +                     return true;
> +     }
> +
> +     return false;
> +}
> +
> +static bool intel_dbuf_bw_state_changed(struct intel_display *display,
> +                                     const struct intel_dbuf_bw_state 
> *old_dbuf_bw_state,
> +                                     const struct intel_dbuf_bw_state 
> *new_dbuf_bw_state) {
> +     enum pipe pipe;
> +
> +     for_each_pipe(display, pipe) {
> +             const struct intel_dbuf_bw *old_dbuf_bw =
> +                     &old_dbuf_bw_state->dbuf_bw[pipe];
> +             const struct intel_dbuf_bw *new_dbuf_bw =
> +                     &new_dbuf_bw_state->dbuf_bw[pipe];
> +
> +             if (intel_dbuf_bw_changed(display, old_dbuf_bw, new_dbuf_bw))
> +                     return true;
> +     }
> +
> +     return false;
> +}
> +
> +static void skl_plane_calc_dbuf_bw(struct intel_dbuf_bw *dbuf_bw,
> +                                struct intel_crtc *crtc,
> +                                enum plane_id plane_id,
> +                                const struct skl_ddb_entry *ddb,
> +                                unsigned int data_rate)
> +{
> +     struct intel_display *display = to_intel_display(crtc);
> +     unsigned int dbuf_mask = skl_ddb_dbuf_slice_mask(display, ddb);
> +     enum dbuf_slice slice;
> +
> +     /*
> +      * The arbiter can only really guarantee an
> +      * equal share of the total bw to each plane.
> +      */
> +     for_each_dbuf_slice_in_mask(display, slice, dbuf_mask) {
> +             dbuf_bw->max_bw[slice] = max(dbuf_bw->max_bw[slice], data_rate);
> +             dbuf_bw->active_planes[slice] |= BIT(plane_id);
> +     }
> +}
> +
> +static void skl_crtc_calc_dbuf_bw(struct intel_dbuf_bw *dbuf_bw,
> +                               const struct intel_crtc_state *crtc_state) {
> +     struct intel_display *display = to_intel_display(crtc_state);
> +     struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
> +     enum plane_id plane_id;
> +
> +     memset(dbuf_bw, 0, sizeof(*dbuf_bw));
> +
> +     if (!crtc_state->hw.active)
> +             return;
> +
> +     for_each_plane_id_on_crtc(crtc, plane_id) {
> +             /*
> +              * We assume cursors are small enough
> +              * to not cause bandwidth problems.
> +              */
> +             if (plane_id == PLANE_CURSOR)
> +                     continue;
> +
> +             skl_plane_calc_dbuf_bw(dbuf_bw, crtc, plane_id,
> +                                    &crtc_state->wm.skl.plane_ddb[plane_id],
> +                                    crtc_state->data_rate[plane_id]);
> +
> +             if (DISPLAY_VER(display) < 11)
> +                     skl_plane_calc_dbuf_bw(dbuf_bw, crtc, plane_id,
> +                                            
> &crtc_state->wm.skl.plane_ddb_y[plane_id],
> +                                            crtc_state->data_rate[plane_id]);
> +     }
> +}
> +
> +/* "Maximum Data Buffer Bandwidth" */
> +int intel_dbuf_bw_min_cdclk(struct intel_display *display,
> +                         const struct intel_dbuf_bw_state *dbuf_bw_state) {
> +     unsigned int total_max_bw = 0;
> +     enum dbuf_slice slice;
> +
> +     for_each_dbuf_slice(display, slice) {
> +             int num_active_planes = 0;
> +             unsigned int max_bw = 0;
> +             enum pipe pipe;
> +
> +             /*
> +              * The arbiter can only really guarantee an
> +              * equal share of the total bw to each plane.
> +              */
> +             for_each_pipe(display, pipe) {
> +                     const struct intel_dbuf_bw *dbuf_bw = 
> &dbuf_bw_state->dbuf_bw[pipe];
> +
> +                     max_bw = max(dbuf_bw->max_bw[slice], max_bw);
> +                     num_active_planes += 
> hweight8(dbuf_bw->active_planes[slice]);
> +             }
> +             max_bw *= num_active_planes;
> +
> +             total_max_bw = max(total_max_bw, max_bw);
> +     }
> +
> +     return DIV_ROUND_UP(total_max_bw, 64); }
> +
> +int intel_dbuf_bw_calc_min_cdclk(struct intel_atomic_state *state,
> +                              bool *need_cdclk_calc)
> +{
> +     struct intel_display *display = to_intel_display(state);
> +     struct intel_dbuf_bw_state *new_dbuf_bw_state = NULL;
> +     const struct intel_dbuf_bw_state *old_dbuf_bw_state = NULL;
> +     const struct intel_crtc_state *old_crtc_state;
> +     const struct intel_crtc_state *new_crtc_state;
> +     struct intel_crtc *crtc;
> +     int ret, i;
> +
> +     if (DISPLAY_VER(display) < 9)
> +             return 0;
> +
> +     for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
> +                                         new_crtc_state, i) {
> +             struct intel_dbuf_bw old_dbuf_bw, new_dbuf_bw;
> +
> +             skl_crtc_calc_dbuf_bw(&old_dbuf_bw, old_crtc_state);
> +             skl_crtc_calc_dbuf_bw(&new_dbuf_bw, new_crtc_state);
> +
> +             if (!intel_dbuf_bw_changed(display, &old_dbuf_bw, &new_dbuf_bw))
> +                     continue;
> +
> +             new_dbuf_bw_state = intel_atomic_get_dbuf_bw_state(state);
> +             if (IS_ERR(new_dbuf_bw_state))
> +                     return PTR_ERR(new_dbuf_bw_state);
> +
> +             old_dbuf_bw_state = intel_atomic_get_old_dbuf_bw_state(state);
> +
> +             new_dbuf_bw_state->dbuf_bw[crtc->pipe] = new_dbuf_bw;
> +     }
> +
> +     if (!old_dbuf_bw_state)
> +             return 0;
> +
> +     if (intel_dbuf_bw_state_changed(display, old_dbuf_bw_state, 
> new_dbuf_bw_state)) {
> +             ret = intel_atomic_lock_global_state(&new_dbuf_bw_state->base);
> +             if (ret)
> +                     return ret;
> +     }
> +
> +     ret = intel_cdclk_update_dbuf_bw_min_cdclk(state,
> +                                                
> intel_dbuf_bw_min_cdclk(display, old_dbuf_bw_state),
> +                                                
> intel_dbuf_bw_min_cdclk(display, new_dbuf_bw_state),
> +                                                need_cdclk_calc);
> +     if (ret)
> +             return ret;
> +
> +     return 0;
> +}
> +
> +void intel_dbuf_bw_update_hw_state(struct intel_display *display) {
> +     struct intel_dbuf_bw_state *dbuf_bw_state =
> +             to_intel_dbuf_bw_state(display->dbuf_bw.obj.state);
> +     struct intel_crtc *crtc;
> +
> +     if (DISPLAY_VER(display) < 9)
> +             return;
> +
> +     for_each_intel_crtc(display->drm, crtc) {
> +             const struct intel_crtc_state *crtc_state =
> +                     to_intel_crtc_state(crtc->base.state);
> +
> +             skl_crtc_calc_dbuf_bw(&dbuf_bw_state->dbuf_bw[crtc->pipe], 
> crtc_state);
> +     }
> +}
> +
> +void intel_dbuf_bw_crtc_disable_noatomic(struct intel_crtc *crtc) {
> +     struct intel_display *display = to_intel_display(crtc);
> +     struct intel_dbuf_bw_state *dbuf_bw_state =
> +             to_intel_dbuf_bw_state(display->dbuf_bw.obj.state);
> +     enum pipe pipe = crtc->pipe;
> +
> +     if (DISPLAY_VER(display) < 9)
> +             return;
> +
> +     memset(&dbuf_bw_state->dbuf_bw[pipe], 0,
> +sizeof(dbuf_bw_state->dbuf_bw[pipe]));
> +}
> +
> +static struct intel_global_state *
> +intel_dbuf_bw_duplicate_state(struct intel_global_obj *obj) {
> +     struct intel_dbuf_bw_state *state;
> +
> +     state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
> +     if (!state)
> +             return NULL;
> +
> +     return &state->base;
> +}
> +
> +static void intel_dbuf_bw_destroy_state(struct intel_global_obj *obj,
> +                                     struct intel_global_state *state)
> +{
> +     kfree(state);
> +}
> +
> +static const struct intel_global_state_funcs intel_dbuf_bw_funcs = {
> +     .atomic_duplicate_state = intel_dbuf_bw_duplicate_state,
> +     .atomic_destroy_state = intel_dbuf_bw_destroy_state, };
> +
> +int intel_dbuf_bw_init(struct intel_display *display) {
> +     struct intel_dbuf_bw_state *state;
> +
> +     state = kzalloc(sizeof(*state), GFP_KERNEL);
> +     if (!state)
> +             return -ENOMEM;
> +
> +     intel_atomic_global_obj_init(display, &display->dbuf_bw.obj,
> +                                  &state->base, &intel_dbuf_bw_funcs);
> +
> +     return 0;
> +}
> diff --git a/drivers/gpu/drm/i915/display/intel_dbuf_bw.h 
> b/drivers/gpu/drm/i915/display/intel_dbuf_bw.h
> new file mode 100644
> index 000000000000..61875b9d5969
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/display/intel_dbuf_bw.h
> @@ -0,0 +1,37 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2025 Intel Corporation
> + */
> +
> +#ifndef __INTEL_DBUF_BW_H__
> +#define __INTEL_DBUF_BW_H__
> +
> +#include <drm/drm_atomic.h>
> +
> +struct intel_atomic_state;
> +struct intel_dbuf_bw_state;
> +struct intel_crtc;
> +struct intel_display;
> +struct intel_global_state;
> +
> +struct intel_dbuf_bw_state *
> +to_intel_dbuf_bw_state(struct intel_global_state *obj_state);
> +
> +struct intel_dbuf_bw_state *
> +intel_atomic_get_old_dbuf_bw_state(struct intel_atomic_state *state);
> +
> +struct intel_dbuf_bw_state *
> +intel_atomic_get_new_dbuf_bw_state(struct intel_atomic_state *state);
> +
> +struct intel_dbuf_bw_state *
> +intel_atomic_get_dbuf_bw_state(struct intel_atomic_state *state);
> +
> +int intel_dbuf_bw_init(struct intel_display *display); int
> +intel_dbuf_bw_calc_min_cdclk(struct intel_atomic_state *state,
> +                              bool *need_cdclk_calc);
> +int intel_dbuf_bw_min_cdclk(struct intel_display *display,
> +                         const struct intel_dbuf_bw_state *dbuf_bw_state); 
> void
> +intel_dbuf_bw_update_hw_state(struct intel_display *display); void
> +intel_dbuf_bw_crtc_disable_noatomic(struct intel_crtc *crtc);
> +
> +#endif /* __INTEL_DBUF_BW_H__ */
> diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h 
> b/drivers/gpu/drm/i915/display/intel_display_core.h
> index df4da52cbdb3..32664098b407 100644
> --- a/drivers/gpu/drm/i915/display/intel_display_core.h
> +++ b/drivers/gpu/drm/i915/display/intel_display_core.h
> @@ -369,6 +369,10 @@ struct intel_display {
>               struct intel_global_obj obj;
>       } dbuf;
> 
> +     struct {
> +             struct intel_global_obj obj;
> +     } dbuf_bw;
> +
>       struct {
>               /*
>                * dkl.phy_lock protects against concurrent access of the diff 
> --git
> a/drivers/gpu/drm/i915/display/intel_display_driver.c 
> b/drivers/gpu/drm/i915/display/intel_display_driver.c
> index f84a0b26b7a6..38672d2896e3 100644
> --- a/drivers/gpu/drm/i915/display/intel_display_driver.c
> +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c
> @@ -28,6 +28,7 @@
>  #include "intel_cdclk.h"
>  #include "intel_color.h"
>  #include "intel_crtc.h"
> +#include "intel_dbuf_bw.h"
>  #include "intel_display_core.h"
>  #include "intel_display_debugfs.h"
>  #include "intel_display_driver.h"
> @@ -285,6 +286,10 @@ int intel_display_driver_probe_noirq(struct 
> intel_display *display)
>       if (ret)
>               goto cleanup_wq_unordered;
> 
> +     ret = intel_dbuf_bw_init(display);
> +     if (ret)
> +             goto cleanup_wq_unordered;
> +
>       ret = intel_bw_init(display);
>       if (ret)
>               goto cleanup_wq_unordered;
> diff --git a/drivers/gpu/drm/i915/display/intel_modeset_setup.c 
> b/drivers/gpu/drm/i915/display/intel_modeset_setup.c
> index 8415f3d703ed..deb877b2aebd 100644
> --- a/drivers/gpu/drm/i915/display/intel_modeset_setup.c
> +++ b/drivers/gpu/drm/i915/display/intel_modeset_setup.c
> @@ -19,6 +19,7 @@
>  #include "intel_color.h"
>  #include "intel_crtc.h"
>  #include "intel_crtc_state_dump.h"
> +#include "intel_dbuf_bw.h"
>  #include "intel_ddi.h"
>  #include "intel_de.h"
>  #include "intel_display.h"
> @@ -176,6 +177,7 @@ static void intel_crtc_disable_noatomic_complete(struct 
> intel_crtc *crtc)
>       intel_cdclk_crtc_disable_noatomic(crtc);
>       skl_wm_crtc_disable_noatomic(crtc);
>       intel_bw_crtc_disable_noatomic(crtc);
> +     intel_dbuf_bw_crtc_disable_noatomic(crtc);
> 
>       intel_pmdemand_update_port_clock(display, pmdemand_state, pipe, 0);  } 
> @@ -872,6 +874,7 @@ static void
> intel_modeset_readout_hw_state(struct intel_display *display)
>               intel_wm_get_hw_state(display);
> 
>       intel_bw_update_hw_state(display);
> +     intel_dbuf_bw_update_hw_state(display);
>       intel_cdclk_update_hw_state(display);
> 
>       intel_pmdemand_init_pmdemand_params(display, pmdemand_state); diff 
> --git a/drivers/gpu/drm/xe/Makefile
> b/drivers/gpu/drm/xe/Makefile index 84321fad3265..88ba3d32802d 100644
> --- a/drivers/gpu/drm/xe/Makefile
> +++ b/drivers/gpu/drm/xe/Makefile
> @@ -243,6 +243,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
>       i915-display/intel_crtc_state_dump.o \
>       i915-display/intel_cursor.o \
>       i915-display/intel_cx0_phy.o \
> +     i915-display/intel_dbuf_bw.o \
>       i915-display/intel_ddi.o \
>       i915-display/intel_ddi_buf_trans.o \
>       i915-display/intel_display.o \
> --
> 2.49.1

Reply via email to