From: "Ville Syrjälä" <ville.syrjala@linux.intel.com>
To: "Nautiyal, Ankit K" <ankit.k.nautiyal@intel.com>
Cc: intel-gfx@lists.freedesktop.org, intel-xe@lists.freedesktop.org,
Mitul Golani <mitulkumar.ajitkumar.golani@intel.com>
Subject: Re: [PATCH 12/14] drm/i915/vrr: Use static guardband to support seamless LRR switching
Date: Wed, 10 Sep 2025 15:30:35 +0300 [thread overview]
Message-ID: <aMFva97JaRWxZUKn@intel.com> (raw)
In-Reply-To: <39f9b204-817a-4911-837a-7f5be94d2a0f@intel.com>
On Tue, Sep 09, 2025 at 08:32:11PM +0530, Nautiyal, Ankit K wrote:
>
> On 9/8/2025 10:13 PM, Ville Syrjälä wrote:
> > On Sun, Sep 07, 2025 at 01:02:39PM +0530, Ankit Nautiyal wrote:
> >> In the current VRR implementation, vrr.vmin and vrr.guardband are set such
> >> that they do not need to change when switching from fixed refresh rate to
> >> variable refresh rate. Specifically, vrr.guardband is always set to match
> >> the vblank length. This approach works for most cases, but not for LRR,
> >> where the guardband would need to change while the VRR timing generator is
> >> still active.
> >>
> >> With the VRR TG always active, live updates to guardband are unsafe and not
> >> recommended. To ensure hardware safety, guardband was moved out of the
> >> !fastset block, meaning any change now requires a full modeset.
> >> This breaks seamless LRR switching, which was previously supported.
> >>
> >> Since the problem arises from guardband being matched to the vblank length,
> >> solution is to use a minimal, sufficient static value, instead. So we use a
> >> static guardband defined during mode-set that fits within the smallest
> >> expected vblank and remains unchanged in case of features like LRR where
> >> vtotal changes. To compute this minimum guardband we take into account
> >> latencies/delays due to different features as mentioned in the Bspec.
> >>
> >> v2:
> >> -Use helpers for dsc/scaler prefill latencies. (Mitul)
> >> -Account for pkgc latency and take max of pkgc and sagv latencies.
> >> v3: Use new helper for PSR2/Panel Replay latency.
> >> v4: Avoid re-setting the Vmin/Flipline for optimized guardband.
> >>
> >> Bspec: 70151
> >> Signed-off-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
> >> Reviewed-by: Mitul Golani <mitulkumar.ajitkumar.golani@intel.com> (#v3)
> >> ---
> >> drivers/gpu/drm/i915/display/intel_display.c | 2 +-
> >> drivers/gpu/drm/i915/display/intel_vrr.c | 127 ++++++++++++++++++-
> >> drivers/gpu/drm/i915/display/intel_vrr.h | 3 +-
> >> 3 files changed, 128 insertions(+), 4 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
> >> index fb072275b1c7..3fa94675d5e1 100644
> >> --- a/drivers/gpu/drm/i915/display/intel_display.c
> >> +++ b/drivers/gpu/drm/i915/display/intel_display.c
> >> @@ -4902,7 +4902,6 @@ intel_modeset_pipe_config_late(struct intel_atomic_state *state,
> >> struct drm_connector *connector;
> >> int i;
> >>
> >> - intel_vrr_compute_config_late(crtc_state);
> >>
> >> for_each_new_connector_in_state(&state->base, connector,
> >> conn_state, i) {
> >> @@ -4914,6 +4913,7 @@ intel_modeset_pipe_config_late(struct intel_atomic_state *state,
> >> !encoder->compute_config_late)
> >> continue;
> >>
> >> + intel_vrr_compute_config_late(crtc_state, conn_state);
> >> ret = encoder->compute_config_late(encoder, crtc_state,
> >> conn_state);
> >> if (ret)
> >> diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c
> >> index 855974174afd..fff684eb2514 100644
> >> --- a/drivers/gpu/drm/i915/display/intel_vrr.c
> >> +++ b/drivers/gpu/drm/i915/display/intel_vrr.c
> >> @@ -6,12 +6,15 @@
> >>
> >> #include <drm/drm_print.h>
> >>
> >> +#include "intel_alpm.h"
> >> #include "intel_de.h"
> >> #include "intel_display_regs.h"
> >> #include "intel_display_types.h"
> >> #include "intel_dp.h"
> >> #include "intel_vrr.h"
> >> #include "intel_vrr_regs.h"
> >> +#include "skl_scaler.h"
> >> +#include "skl_watermark.h"
> >>
> >> #define FIXED_POINT_PRECISION 100
> >> #define CMRR_PRECISION_TOLERANCE 10
> >> @@ -413,15 +416,135 @@ intel_vrr_compute_config(struct intel_crtc_state *crtc_state,
> >> }
> >> }
> >>
> >> -void intel_vrr_compute_config_late(struct intel_crtc_state *crtc_state)
> >> +static
> >> +int scaler_prefill_latency(struct intel_crtc_state *crtc_state, int linetime_us)
> >> +{
> >> + int chroma_downscaling_factor = skl_scaler_chroma_downscale_factor(crtc_state);
> >> + u64 hscale_k, vscale_k;
> >> + int cdclk_adjustment;
> >> + int num_scaler_users;
> >> +
> >> + /*
> >> + * Assuming:
> >> + * Both scaler enabled.
> >> + * scaler 1 downscaling factor as 2 x 2 (Horiz x Vert)
> >> + * scaler 2 downscaling factor as 2 x 1 (Horiz x Vert)
> >> + * Cdclk Adjustment : 1
> >> + */
> >> + num_scaler_users = 2;
> >> + hscale_k = 2 * 1000;
> >> + vscale_k = 2 * 1000;
> >> + cdclk_adjustment = 1;
> >> +
> >> + return intel_vrr_guardband_scaler_latency(num_scaler_users, hscale_k, vscale_k,
> >> + chroma_downscaling_factor,
> >> + cdclk_adjustment,
> >> + linetime_us);
> >> +}
> >> +
> >> +static
> >> +int dsc_prefill_latency(struct intel_crtc_state *crtc_state, int linetime_us)
> >> +{
> >> +#define MAX_SCALERS 2
> >> + int chroma_downscaling_factor = skl_scaler_chroma_downscale_factor(crtc_state);
> >> + u64 hscale_k[MAX_SCALERS], vscale_k[MAX_SCALERS];
> >> + int cdclk_adjustment;
> >> + int num_scaler_users;
> >> +
> >> + /*
> >> + * Assuming:
> >> + * Both scaler enabled.
> >> + * scaler 1 downscaling factor as 2 x 2 (Horiz x Vert)
> >> + * scaler 2 downscaling factor as 2 x 1 (Horiz x Vert)
> >> + * Cdclk Adjustment : 1
> >> + */
> >> + num_scaler_users = MAX_SCALERS;
> >> + hscale_k[0] = 2 * 1000;
> >> + vscale_k[0] = 2 * 1000;
> >> + hscale_k[1] = 2 * 1000;
> >> + vscale_k[1] = 1 * 1000;
> >> +
> >> + cdclk_adjustment = 1;
> >> +
> >> + return intel_vrr_guardband_dsc_latency(num_scaler_users, hscale_k, vscale_k,
> >> + chroma_downscaling_factor,
> >> + cdclk_adjustment,
> >> + linetime_us);
> >> +}
> >> +
> >> +static
> >> +int intel_vrr_compute_guardband(struct intel_crtc_state *crtc_state,
> >> + struct intel_connector *connector)
> >> +{
> >> + const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
> >> + struct intel_display *display = to_intel_display(crtc_state);
> >> + int dsc_prefill_time = 0;
> >> + int psr2_pr_latency = 0;
> >> + int scaler_prefill_time;
> >> + int wm0_prefill_time;
> >> + int pkgc_max_latency;
> >> + int sagv_latency;
> >> + int sdp_latency = 0;
> >> + int guardband_us;
> >> + int linetime_us;
> >> + int guardband;
> >> + int pm_delay;
> >> +
> >> + linetime_us = DIV_ROUND_UP(adjusted_mode->crtc_htotal * 1000,
> >> + adjusted_mode->crtc_clock);
> >> +
> >> + pkgc_max_latency = skl_watermark_max_latency(display, 1);
> >> + sagv_latency = display->sagv.block_time_us;
> >> +
> >> + /* Assuming max wm0 lines = 4 */
> >> + wm0_prefill_time = 4 * linetime_us + 20;
> >> +
> >> + scaler_prefill_time = scaler_prefill_latency(crtc_state, linetime_us);
> >> +
> >> + if (crtc_state->dsc.compression_enable)
> >> + dsc_prefill_time = dsc_prefill_latency(crtc_state, linetime_us);
> >> +
> >> + pm_delay = crtc_state->framestart_delay +
> >> + max(sagv_latency, pkgc_max_latency) +
> >> + wm0_prefill_time +
> >> + scaler_prefill_time +
> >> + dsc_prefill_time;
> >> +
> >> + switch (connector->base.connector_type) {
> >> + case DRM_MODE_CONNECTOR_eDP:
> >> + case DRM_MODE_CONNECTOR_DisplayPort:
> >> + psr2_pr_latency = intel_alpm_compute_max_link_wake_latency(crtc_state, true);
> >> + sdp_latency = intel_dp_compute_sdp_latency(crtc_state, true);
> >> + break;
> >> + default:
> >> + break;
> >> + }
> >> +
> >> + guardband_us = max(sdp_latency, psr2_pr_latency);
> >> + guardband_us = max(guardband_us, pm_delay);
> >> +
> >> + guardband = DIV_ROUND_UP(guardband_us, linetime_us);
> >> +
> >> + /* guardband cannot be more than the Vmax vblank */
> >> + guardband = min(guardband, crtc_state->vrr.vmax - adjusted_mode->crtc_vblank_start);
> >> +
> >> + return guardband;
> >> +}
> >> +
> >> +void intel_vrr_compute_config_late(struct intel_crtc_state *crtc_state,
> >> + struct drm_connector_state *conn_state)
> >> {
> >> struct intel_display *display = to_intel_display(crtc_state);
> >> const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
> >> + struct intel_connector *connector =
> >> + to_intel_connector(conn_state->connector);
> >>
> >> if (!intel_vrr_possible(crtc_state))
> >> return;
> >>
> >> - if (DISPLAY_VER(display) >= 13) {
> >> + if (intel_vrr_always_use_vrr_tg(display)) {
> >> + crtc_state->vrr.guardband = intel_vrr_compute_guardband(crtc_state, connector);
> > This all looks to be in the wrong place. It needs to be done as part of
> > intel_crtc_compute_vblank_delay() which then updates crtc_vblank_start
> > to reflect reality instead of leaving some incorrect junk in there.
> > Presumably that is why you also had to do that
> > intel_dsb_wait_for_delayed_vblank() hack.
>
> Hi Ville,
>
> Thanks for for the comments.
>
> Looks like intel_crtc_compute_vblank_delay() indeed is the place to add
> the vblank_delay and get the crtc_vblank_start point to the delayed vblank.
>
> But I think, there are couple of things need to take care with this change:
> 1) The SCL currently is derived from crtc_vblank_start - vdisplay, which
> is ~1 with guradband = vblank length.
> With modified crtc_vblank_start, this will become huge so perhaps we
> need to set it to 1?
When using the legacy timing generator SCL is what defines the
position of the delayed vblank. So it should be exactly what it is.
I don't quite recall how this stuff works with the VRR timing
generator. I think it might have been that guardband more or less
defines the location of the delayed vblank, and SCL then effectively
defines the start of the safe window. So I think it should stay
exactly the way it is. IIRC there is some way on new platforms to
even move the SCL position into the vertical active, but I haven't
though through what that would actually do for us.
>
> 2) The intel_dsb_wait_vblank_delay() (which inturn uses
> intel_vrr_real_vblank_delay() for vrr case) also computes a fixed delay
> based on (crtc_vblank_start - vdisplay) lines
> Instead of this should we not use intel_dsb_wait_scanline_in() to wait
> only till scanline reaches in range [flipline decision boundary, vmax
> decision boundary]?
You can't use absolute line numbers with VRR since you don't know
when the vblank will be terminated. It has to be all relative.
>
> Let me know what you think, I can send a follow-up patch with the
> suggested changes.
>
> Regards,
> Ankit
>
>
> >
> >> + } else if (DISPLAY_VER(display) >= 13) {
> >> crtc_state->vrr.guardband =
> >> crtc_state->vrr.vmin - adjusted_mode->crtc_vblank_start;
> >> } else {
> >> diff --git a/drivers/gpu/drm/i915/display/intel_vrr.h b/drivers/gpu/drm/i915/display/intel_vrr.h
> >> index 950041647e47..362638fd0d66 100644
> >> --- a/drivers/gpu/drm/i915/display/intel_vrr.h
> >> +++ b/drivers/gpu/drm/i915/display/intel_vrr.h
> >> @@ -21,7 +21,8 @@ bool intel_vrr_possible(const struct intel_crtc_state *crtc_state);
> >> void intel_vrr_check_modeset(struct intel_atomic_state *state);
> >> void intel_vrr_compute_config(struct intel_crtc_state *crtc_state,
> >> struct drm_connector_state *conn_state);
> >> -void intel_vrr_compute_config_late(struct intel_crtc_state *crtc_state);
> >> +void intel_vrr_compute_config_late(struct intel_crtc_state *crtc_state,
> >> + struct drm_connector_state *conn_state);
> >> void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state);
> >> void intel_vrr_enable(const struct intel_crtc_state *crtc_state);
> >> void intel_vrr_send_push(struct intel_dsb *dsb,
> >> --
> >> 2.45.2
--
Ville Syrjälä
Intel
next prev parent reply other threads:[~2025-09-10 12:30 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-07 7:32 [PATCH 00/14] Optimize vrr.guardband and fix LRR Ankit Nautiyal
2025-09-07 7:32 ` [PATCH 01/14] drm/i915/vrr: Use crtc_vsync_start/end for computing vrr.vsync_start/end Ankit Nautiyal
2025-09-08 8:08 ` Shankar, Uma
2025-09-07 7:32 ` [PATCH 02/14] drm/i915/skl_watermark: Fix the scaling factor for chroma subsampling Ankit Nautiyal
2025-09-07 7:32 ` [PATCH 03/14] drm/i915/skl_watermark: Pass linetime as argument to latency helpers Ankit Nautiyal
2025-09-07 7:32 ` [PATCH 04/14] drm/i915/skl_scaler: Introduce helper for chroma downscale factor Ankit Nautiyal
2025-09-07 7:32 ` [PATCH 05/14] drm/i915/display: Extract helpers to set dsc/scaler prefill latencies Ankit Nautiyal
2025-09-07 7:32 ` [PATCH 06/14] drm/i915/dp: Add SDP latency computation helper Ankit Nautiyal
2025-09-07 7:32 ` [PATCH 07/14] drm/i915/alpm: Add function to compute max link-wake latency Ankit Nautiyal
2025-09-07 7:32 ` [PATCH 08/14] drm/i915/vrr: Use vrr.sync_start for getting vtotal Ankit Nautiyal
2025-09-07 7:32 ` [PATCH 09/14] drm/i915/display: Add guardband check for feature latencies Ankit Nautiyal
2025-09-07 7:32 ` [PATCH 10/14] drm/i915/skl_watermark: Remove redundant latency checks from vblank validation Ankit Nautiyal
2025-09-07 7:32 ` [PATCH 11/14] drm/i915/dsb: Align flipdone with delayed vblank using guardband wait Ankit Nautiyal
2025-09-08 8:05 ` Shankar, Uma
2025-09-07 7:32 ` [PATCH 12/14] drm/i915/vrr: Use static guardband to support seamless LRR switching Ankit Nautiyal
2025-09-08 16:43 ` Ville Syrjälä
2025-09-09 15:02 ` Nautiyal, Ankit K
2025-09-10 12:30 ` Ville Syrjälä [this message]
2025-09-10 13:37 ` Nautiyal, Ankit K
2025-09-07 7:32 ` [PATCH 13/14] drm/i915/panel: Refactor helper to get highest fixed mode Ankit Nautiyal
2025-09-07 7:32 ` [PATCH 14/14] drm/i915/vrr: Fix seamless_mn drrs for PTL Ankit Nautiyal
2025-09-07 10:03 ` ✓ i915.CI.BAT: success for Optimize vrr.guardband and fix LRR (rev9) Patchwork
2025-09-08 20:33 ` ✓ CI.KUnit: " Patchwork
2025-09-09 0:38 ` ✗ Xe.CI.Full: failure " Patchwork
2025-09-09 1:09 ` ✓ CI.KUnit: success for Optimize vrr.guardband and fix LRR (rev10) Patchwork
2025-09-09 1:42 ` ✓ Xe.CI.BAT: " Patchwork
2025-09-09 8:02 ` ✗ Xe.CI.Full: failure " Patchwork
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aMFva97JaRWxZUKn@intel.com \
--to=ville.syrjala@linux.intel.com \
--cc=ankit.k.nautiyal@intel.com \
--cc=intel-gfx@lists.freedesktop.org \
--cc=intel-xe@lists.freedesktop.org \
--cc=mitulkumar.ajitkumar.golani@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.