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,
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)
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;
}
dev_priv->max_cdclk_freq));
}
- if (min_cdclk > dev_priv->max_cdclk_freq) {
- drm_dbg_kms(&dev_priv->drm,
- "required cdclk (%d kHz) exceeds max (%d kHz)\n",
- min_cdclk, dev_priv->max_cdclk_freq);
- return -EINVAL;
- }
-
return min_cdclk;
}
{
struct intel_atomic_state *state = cdclk_state->base.state;
struct drm_i915_private *dev_priv = to_i915(state->base.dev);
- struct intel_bw_state *bw_state = NULL;
+ const struct intel_bw_state *bw_state;
struct intel_crtc *crtc;
struct intel_crtc_state *crtc_state;
int min_cdclk, i;
if (min_cdclk < 0)
return min_cdclk;
- bw_state = intel_atomic_get_bw_state(state);
- if (IS_ERR(bw_state))
- return PTR_ERR(bw_state);
-
if (cdclk_state->min_cdclk[crtc->pipe] == min_cdclk)
continue;
return ret;
}
- min_cdclk = cdclk_state->force_min_cdclk;
- for_each_pipe(dev_priv, pipe) {
- min_cdclk = max(cdclk_state->min_cdclk[pipe], min_cdclk);
+ bw_state = intel_atomic_get_new_bw_state(state);
+ if (bw_state) {
+ min_cdclk = intel_bw_min_cdclk(dev_priv, bw_state);
- if (!bw_state)
- continue;
+ if (cdclk_state->bw_min_cdclk != min_cdclk) {
+ int ret;
+
+ cdclk_state->bw_min_cdclk = min_cdclk;
+
+ ret = intel_atomic_lock_global_state(&cdclk_state->base);
+ if (ret)
+ return ret;
+ }
+ }
- min_cdclk = max(bw_state->min_cdclk, min_cdclk);
+ min_cdclk = max(cdclk_state->force_min_cdclk,
+ cdclk_state->bw_min_cdclk);
+ for_each_pipe(dev_priv, pipe)
+ min_cdclk = max(cdclk_state->min_cdclk[pipe], min_cdclk);
+
+ if (min_cdclk > dev_priv->max_cdclk_freq) {
+ drm_dbg_kms(&dev_priv->drm,
+ "required cdclk (%d kHz) exceeds max (%d kHz)\n",
+ min_cdclk, dev_priv->max_cdclk_freq);
+ return -EINVAL;
}
return min_cdclk;
int intel_cdclk_atomic_check(struct intel_atomic_state *state,
bool *need_cdclk_calc)
{
- struct drm_i915_private *i915 = to_i915(state->base.dev);
const struct intel_cdclk_state *old_cdclk_state;
const struct intel_cdclk_state *new_cdclk_state;
struct intel_plane_state *plane_state;
- struct intel_bw_state *new_bw_state;
struct intel_plane *plane;
- int min_cdclk = 0;
- enum pipe pipe;
int ret;
int i;
return ret;
}
+ ret = intel_bw_calc_min_cdclk(state, need_cdclk_calc);
+ if (ret)
+ return ret;
+
old_cdclk_state = intel_atomic_get_old_cdclk_state(state);
new_cdclk_state = intel_atomic_get_new_cdclk_state(state);
old_cdclk_state->force_min_cdclk != new_cdclk_state->force_min_cdclk)
*need_cdclk_calc = true;
- ret = intel_bw_calc_min_cdclk(state);
- if (ret)
- return ret;
-
- new_bw_state = intel_atomic_get_new_bw_state(state);
-
- if (!new_cdclk_state || !new_bw_state)
- return 0;
-
- for_each_pipe(i915, pipe) {
- min_cdclk = max(new_cdclk_state->min_cdclk[pipe], min_cdclk);
-
- /* Currently do this change only if we need to increase */
- if (new_bw_state->min_cdclk > min_cdclk)
- *need_cdclk_calc = true;
- }
-
return 0;
}