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.

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