diff options
-rw-r--r-- | drivers/gpu/drm/i915/display/skl_watermark.c | 118 |
1 files changed, 116 insertions, 2 deletions
diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index a601747cae55..50a9a6adbe32 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -12,6 +12,7 @@ #include "intel_atomic.h" #include "intel_atomic_plane.h" #include "intel_bw.h" +#include "intel_crtc.h" #include "intel_de.h" #include "intel_display.h" #include "intel_display_power.h" @@ -720,7 +721,7 @@ static unsigned int skl_wm_latency(struct drm_i915_private *i915, int level, skl_watermark_ipc_enabled(i915)) latency += 4; - if (skl_needs_memory_bw_wa(i915) && wp->x_tiled) + if (skl_needs_memory_bw_wa(i915) && wp && wp->x_tiled) latency += 15; return latency; @@ -2200,6 +2201,119 @@ static int icl_build_plane_wm(struct intel_crtc_state *crtc_state, return 0; } +static bool +skl_is_vblank_too_short(const struct intel_crtc_state *crtc_state, + int wm0_lines, int latency) +{ + const struct drm_display_mode *adjusted_mode = + &crtc_state->hw.adjusted_mode; + + /* FIXME missing scaler and DSC pre-fill time */ + return crtc_state->framestart_delay + + intel_usecs_to_scanlines(adjusted_mode, latency) + + wm0_lines > + adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vblank_start; +} + +static int skl_max_wm0_lines(const struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + enum plane_id plane_id; + int wm0_lines = 0; + + for_each_plane_id_on_crtc(crtc, plane_id) { + const struct skl_plane_wm *wm = &crtc_state->wm.skl.optimal.planes[plane_id]; + + /* FIXME what about !skl_wm_has_lines() platforms? */ + wm0_lines = max_t(int, wm0_lines, wm->wm[0].lines); + } + + return wm0_lines; +} + +static int skl_max_wm_level_for_vblank(struct intel_crtc_state *crtc_state, + int wm0_lines) +{ + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + struct drm_i915_private *i915 = to_i915(crtc->base.dev); + int level; + + for (level = i915->display.wm.num_levels - 1; level >= 0; level--) { + int latency; + + /* FIXME should we care about the latency w/a's? */ + latency = skl_wm_latency(i915, level, NULL); + if (latency == 0) + continue; + + /* FIXME is it correct to use 0 latency for wm0 here? */ + if (level == 0) + latency = 0; + + if (!skl_is_vblank_too_short(crtc_state, wm0_lines, latency)) + return level; + } + + return -EINVAL; +} + +static int skl_wm_check_vblank(struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + struct drm_i915_private *i915 = to_i915(crtc->base.dev); + int wm0_lines, level; + + if (!crtc_state->hw.active) + return 0; + + wm0_lines = skl_max_wm0_lines(crtc_state); + + level = skl_max_wm_level_for_vblank(crtc_state, wm0_lines); + if (level < 0) + return level; + + /* + * FIXME PSR needs to toggle LATENCY_REPORTING_REMOVED_PIPE_* + * based on whether we're limited by the vblank duration. + * + * FIXME also related to skl+ w/a 1136 (also unimplemented as of + * now) perhaps? + */ + + for (level++; level < i915->display.wm.num_levels; level++) { + enum plane_id plane_id; + + for_each_plane_id_on_crtc(crtc, plane_id) { + struct skl_plane_wm *wm = + &crtc_state->wm.skl.optimal.planes[plane_id]; + + /* + * FIXME just clear enable or flag the entire + * thing as bad via min_ddb_alloc=U16_MAX? + */ + wm->wm[level].enable = false; + wm->uv_wm[level].enable = false; + } + } + + if (DISPLAY_VER(i915) >= 12 && + i915->display.sagv.block_time_us && + skl_is_vblank_too_short(crtc_state, wm0_lines, + i915->display.sagv.block_time_us)) { + enum plane_id plane_id; + + for_each_plane_id_on_crtc(crtc, plane_id) { + struct skl_plane_wm *wm = + &crtc_state->wm.skl.optimal.planes[plane_id]; + + wm->sagv.wm0.enable = false; + wm->sagv.trans_wm.enable = false; + } + } + + return 0; +} + static int skl_build_pipe_wm(struct intel_atomic_state *state, struct intel_crtc *crtc) { @@ -2229,7 +2343,7 @@ static int skl_build_pipe_wm(struct intel_atomic_state *state, crtc_state->wm.skl.optimal = crtc_state->wm.skl.raw; - return 0; + return skl_wm_check_vblank(crtc_state); } static void skl_ddb_entry_write(struct drm_i915_private *i915, |