summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/display/intel_bw.c
diff options
context:
space:
mode:
authorVille Syrjälä <ville.syrjala@linux.intel.com>2022-03-03 21:12:06 +0200
committerVille Syrjälä <ville.syrjala@linux.intel.com>2022-03-21 17:56:41 +0200
commit5ac860cc52540df8bca27e0bb25b6744df67e8f0 (patch)
treefd92e86502a091bee4f7ab63c9b9c3007012336e /drivers/gpu/drm/i915/display/intel_bw.c
parent6731eb046cf71e30a79e60bae8ed31f5450928fa (diff)
downloadlinux-5ac860cc52540df8bca27e0bb25b6744df67e8f0.tar.gz
linux-5ac860cc52540df8bca27e0bb25b6744df67e8f0.tar.bz2
linux-5ac860cc52540df8bca27e0bb25b6744df67e8f0.zip
drm/i915: Fix DBUF bandwidth vs. cdclk handling
Make the dbuf bandwidth min cdclk calculations match the spec more closely. Supposedly the arbiter can only guarantee an equal share of the total bandwidth of the slice to each active plane on that slice. So we take the max bandwidth of any of the planes on each slice and multiply that by the number of active planes on the slice to get a worst case estimate on how much bandwidth we require. Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20220303191207.27931-9-ville.syrjala@linux.intel.com Reviewed-by: Stanislav Lisovskiy <stanislav.lisovskiy@intel.com>
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_bw.c')
-rw-r--r--drivers/gpu/drm/i915/display/intel_bw.c157
1 files changed, 111 insertions, 46 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c
index a0a69d8db132..307a5a1c7788 100644
--- a/drivers/gpu/drm/i915/display/intel_bw.c
+++ b/drivers/gpu/drm/i915/display/intel_bw.c
@@ -692,12 +692,34 @@ static bool intel_bw_state_changed(struct drm_i915_private *i915,
enum dbuf_slice slice;
for_each_dbuf_slice(i915, slice) {
- if (old_crtc_bw->used_bw[slice] != new_crtc_bw->used_bw[slice])
+ if (old_crtc_bw->max_bw[slice] != new_crtc_bw->max_bw[slice] ||
+ old_crtc_bw->active_planes[slice] != new_crtc_bw->active_planes[slice])
return true;
}
}
- return old_bw_state->min_cdclk != new_bw_state->min_cdclk;
+ return false;
+}
+
+static void skl_plane_calc_dbuf_bw(struct intel_bw_state *bw_state,
+ struct intel_crtc *crtc,
+ enum plane_id plane_id,
+ const struct skl_ddb_entry *ddb,
+ unsigned int data_rate)
+{
+ struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+ struct intel_dbuf_bw *crtc_bw = &bw_state->dbuf_bw[crtc->pipe];
+ unsigned int dbuf_mask = skl_ddb_dbuf_slice_mask(i915, 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(i915, slice, dbuf_mask) {
+ crtc_bw->max_bw[slice] = max(crtc_bw->max_bw[slice], data_rate);
+ crtc_bw->active_planes[slice] |= BIT(plane_id);
+ }
}
static void skl_crtc_calc_dbuf_bw(struct intel_bw_state *bw_state,
@@ -708,46 +730,77 @@ static void skl_crtc_calc_dbuf_bw(struct intel_bw_state *bw_state,
struct intel_dbuf_bw *crtc_bw = &bw_state->dbuf_bw[crtc->pipe];
enum plane_id plane_id;
- memset(&crtc_bw->used_bw, 0, sizeof(crtc_bw->used_bw));
+ memset(crtc_bw, 0, sizeof(*crtc_bw));
if (!crtc_state->hw.active)
return;
for_each_plane_id_on_crtc(crtc, plane_id) {
- const struct skl_ddb_entry *ddb =
- &crtc_state->wm.skl.plane_ddb[plane_id];
- unsigned int data_rate = crtc_state->data_rate[plane_id];
- unsigned int dbuf_mask = skl_ddb_dbuf_slice_mask(i915, ddb);
- enum dbuf_slice slice;
+ /*
+ * We assume cursors are small enough
+ * to not cause bandwidth problems.
+ */
+ if (plane_id == PLANE_CURSOR)
+ continue;
+
+ skl_plane_calc_dbuf_bw(bw_state, crtc, plane_id,
+ &crtc_state->wm.skl.plane_ddb[plane_id],
+ crtc_state->data_rate[plane_id]);
- for_each_dbuf_slice_in_mask(i915, slice, dbuf_mask)
- crtc_bw->used_bw[slice] += data_rate;
+ if (DISPLAY_VER(i915) < 11)
+ skl_plane_calc_dbuf_bw(bw_state, crtc, plane_id,
+ &crtc_state->wm.skl.plane_ddb_y[plane_id],
+ crtc_state->data_rate[plane_id]);
}
+}
- if (DISPLAY_VER(i915) >= 11)
- return;
+/* "Maximum Data Buffer Bandwidth" */
+static int
+intel_bw_dbuf_min_cdclk(struct drm_i915_private *i915,
+ const struct intel_bw_state *bw_state)
+{
+ unsigned int total_max_bw = 0;
+ enum dbuf_slice slice;
- for_each_plane_id_on_crtc(crtc, plane_id) {
- const struct skl_ddb_entry *ddb =
- &crtc_state->wm.skl.plane_ddb_y[plane_id];
- unsigned int data_rate = crtc_state->data_rate_y[plane_id];
- unsigned int dbuf_mask = skl_ddb_dbuf_slice_mask(i915, ddb);
- enum dbuf_slice slice;
+ for_each_dbuf_slice(i915, 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(i915, pipe) {
+ const struct intel_dbuf_bw *crtc_bw = &bw_state->dbuf_bw[pipe];
+
+ max_bw = max(crtc_bw->max_bw[slice], max_bw);
+ num_active_planes += hweight8(crtc_bw->active_planes[slice]);
+ }
+ max_bw *= num_active_planes;
- for_each_dbuf_slice_in_mask(i915, slice, dbuf_mask)
- crtc_bw->used_bw[slice] += data_rate;
+ total_max_bw = max(total_max_bw, max_bw);
}
+
+ return DIV_ROUND_UP(total_max_bw, 64);
+}
+
+int intel_bw_min_cdclk(struct drm_i915_private *i915,
+ const struct intel_bw_state *bw_state)
+{
+ return intel_bw_dbuf_min_cdclk(i915, bw_state);
}
-int intel_bw_calc_min_cdclk(struct intel_atomic_state *state)
+int intel_bw_calc_min_cdclk(struct intel_atomic_state *state,
+ bool *need_cdclk_calc)
{
struct drm_i915_private *dev_priv = to_i915(state->base.dev);
struct intel_bw_state *new_bw_state = NULL;
- struct intel_bw_state *old_bw_state = NULL;
+ const struct intel_bw_state *old_bw_state = NULL;
+ const struct intel_cdclk_state *cdclk_state;
const struct intel_crtc_state *crtc_state;
+ int old_min_cdclk, new_min_cdclk;
struct intel_crtc *crtc;
- int max_bw = 0;
- enum pipe pipe;
int i;
if (DISPLAY_VER(dev_priv) < 9)
@@ -766,34 +819,46 @@ int intel_bw_calc_min_cdclk(struct intel_atomic_state *state)
if (!old_bw_state)
return 0;
- for_each_pipe(dev_priv, pipe) {
- struct intel_dbuf_bw *crtc_bw;
- enum dbuf_slice slice;
-
- crtc_bw = &new_bw_state->dbuf_bw[pipe];
-
- for_each_dbuf_slice(dev_priv, slice) {
- /*
- * Current experimental observations show that contrary
- * to BSpec we get underruns once we exceed 64 * CDCLK
- * for slices in total.
- * As a temporary measure in order not to keep CDCLK
- * bumped up all the time we calculate CDCLK according
- * to this formula for overall bw consumed by slices.
- */
- max_bw += crtc_bw->used_bw[slice];
- }
- }
-
- new_bw_state->min_cdclk = DIV_ROUND_UP(max_bw, 64);
-
if (intel_bw_state_changed(dev_priv, old_bw_state, new_bw_state)) {
int ret = intel_atomic_lock_global_state(&new_bw_state->base);
-
if (ret)
return ret;
}
+ old_min_cdclk = intel_bw_min_cdclk(dev_priv, old_bw_state);
+ new_min_cdclk = intel_bw_min_cdclk(dev_priv, new_bw_state);
+
+ /*
+ * No need to check against the cdclk state if
+ * the min cdclk for the dbuf doesn't increase.
+ *
+ * Ie. we only ever increase the cdclk due to dbuf
+ * requirements. This can reduce back and forth
+ * display blinking due to constant cdclk changes.
+ */
+ if (new_min_cdclk <= old_min_cdclk)
+ return 0;
+
+ cdclk_state = intel_atomic_get_cdclk_state(state);
+ if (IS_ERR(cdclk_state))
+ return PTR_ERR(cdclk_state);
+
+ /*
+ * No need to recalculate the cdclk state if
+ * the min cdclk for the dbuf doesn't increase.
+ *
+ * Ie. we only ever increase the cdclk due to dbuf
+ * requirements. This can reduce back and forth
+ * display blinking due to constant cdclk changes.
+ */
+ if (new_min_cdclk <= cdclk_state->bw_min_cdclk)
+ return 0;
+
+ drm_dbg_kms(&dev_priv->drm,
+ "new bandwidth min cdclk (%d kHz) > old min cdclk (%d kHz)\n",
+ new_min_cdclk, cdclk_state->bw_min_cdclk);
+ *need_cdclk_calc = true;
+
return 0;
}