From: Ville Syrjälä Date: Wed, 13 Dec 2023 10:25:15 +0000 (+0200) Subject: drm/i915: Extract intel_vblank_evade() X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=b5ad7ce024b3a866b3d510f121cfa5c1b3610adf;p=linux.git drm/i915: Extract intel_vblank_evade() Pull the core vblank evasion loop into its own function, so that we can reuse it elsewhere later. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20231213102519.13500-6-ville.syrjala@linux.intel.com Reviewed-by: Uma Shankar --- diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c index 92cfb7c8eadb0..26a07b2219bf8 100644 --- a/drivers/gpu/drm/i915/display/intel_crtc.c +++ b/drivers/gpu/drm/i915/display/intel_crtc.c @@ -472,6 +472,7 @@ static int intel_mode_vblank_start(const struct drm_display_mode *mode) } struct intel_vblank_evade_ctx { + struct intel_crtc *crtc; int min, max, vblank_start; bool need_vlv_dsi_wa; }; @@ -485,6 +486,8 @@ static void intel_vblank_evade_init(const struct intel_crtc_state *old_crtc_stat const struct intel_crtc_state *crtc_state; const struct drm_display_mode *adjusted_mode; + evade->crtc = crtc; + evade->need_vlv_dsi_wa = (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915)) && intel_crtc_has_type(new_crtc_state, INTEL_OUTPUT_DSI); @@ -531,6 +534,65 @@ static void intel_vblank_evade_init(const struct intel_crtc_state *old_crtc_stat evade->min -= adjusted_mode->crtc_vblank_start - adjusted_mode->crtc_vdisplay; } +/* must be called with vblank interrupt already enabled! */ +static int intel_vblank_evade(struct intel_vblank_evade_ctx *evade) +{ + struct intel_crtc *crtc = evade->crtc; + struct drm_i915_private *i915 = to_i915(crtc->base.dev); + long timeout = msecs_to_jiffies_timeout(1); + wait_queue_head_t *wq = drm_crtc_vblank_waitqueue(&crtc->base); + DEFINE_WAIT(wait); + int scanline; + + for (;;) { + /* + * prepare_to_wait() has a memory barrier, which guarantees + * other CPUs can see the task state update by the time we + * read the scanline. + */ + prepare_to_wait(wq, &wait, TASK_UNINTERRUPTIBLE); + + scanline = intel_get_crtc_scanline(crtc); + if (scanline < evade->min || scanline > evade->max) + break; + + if (!timeout) { + drm_err(&i915->drm, + "Potential atomic update failure on pipe %c\n", + pipe_name(crtc->pipe)); + break; + } + + local_irq_enable(); + + timeout = schedule_timeout(timeout); + + local_irq_disable(); + } + + finish_wait(wq, &wait); + + /* + * On VLV/CHV DSI the scanline counter would appear to + * increment approx. 1/3 of a scanline before start of vblank. + * The registers still get latched at start of vblank however. + * This means we must not write any registers on the first + * line of vblank (since not the whole line is actually in + * vblank). And unfortunately we can't use the interrupt to + * wait here since it will fire too soon. We could use the + * frame start interrupt instead since it will fire after the + * critical scanline, but that would require more changes + * in the interrupt code. So for now we'll just do the nasty + * thing and poll for the bad scanline to pass us by. + * + * FIXME figure out if BXT+ DSI suffers from this as well + */ + while (evade->need_vlv_dsi_wa && scanline == evade->vblank_start) + scanline = intel_get_crtc_scanline(crtc); + + return scanline; +} + /** * intel_pipe_update_start() - start update of a set of display registers * @state: the atomic state @@ -552,11 +614,8 @@ void intel_pipe_update_start(struct intel_atomic_state *state, intel_atomic_get_old_crtc_state(state, crtc); struct intel_crtc_state *new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc); - long timeout = msecs_to_jiffies_timeout(1); - int scanline; - wait_queue_head_t *wq = drm_crtc_vblank_waitqueue(&crtc->base); struct intel_vblank_evade_ctx evade; - DEFINE_WAIT(wait); + int scanline; intel_psr_lock(new_crtc_state); @@ -593,51 +652,7 @@ void intel_pipe_update_start(struct intel_atomic_state *state, crtc->debug.max_vbl = evade.max; trace_intel_pipe_update_start(crtc); - for (;;) { - /* - * prepare_to_wait() has a memory barrier, which guarantees - * other CPUs can see the task state update by the time we - * read the scanline. - */ - prepare_to_wait(wq, &wait, TASK_UNINTERRUPTIBLE); - - scanline = intel_get_crtc_scanline(crtc); - if (scanline < evade.min || scanline > evade.max) - break; - - if (!timeout) { - drm_err(&dev_priv->drm, - "Potential atomic update failure on pipe %c\n", - pipe_name(crtc->pipe)); - break; - } - - local_irq_enable(); - - timeout = schedule_timeout(timeout); - - local_irq_disable(); - } - - finish_wait(wq, &wait); - - /* - * On VLV/CHV DSI the scanline counter would appear to - * increment approx. 1/3 of a scanline before start of vblank. - * The registers still get latched at start of vblank however. - * This means we must not write any registers on the first - * line of vblank (since not the whole line is actually in - * vblank). And unfortunately we can't use the interrupt to - * wait here since it will fire too soon. We could use the - * frame start interrupt instead since it will fire after the - * critical scanline, but that would require more changes - * in the interrupt code. So for now we'll just do the nasty - * thing and poll for the bad scanline to pass us by. - * - * FIXME figure out if BXT+ DSI suffers from this as well - */ - while (evade.need_vlv_dsi_wa && scanline == evade.vblank_start) - scanline = intel_get_crtc_scanline(crtc); + scanline = intel_vblank_evade(&evade); drm_crtc_vblank_put(&crtc->base);