There are two kinds of VRR panels with fixed modes to consider: Type 1: Modes with different clocks (e.g. 60Hz @ 347MHz, 120Hz @ 695MHz) For such panels, it is not possible to seamlessly switch from a lower RR mode to a higher RR mode, since at lower clock we cannot increase the clock without a full modeset. But seamless switch from 120Hz to 60Hz can be achieved by running at the same (higher) clock and just extending the vtotal.
Type 2: Modes with same clock but different vtotal Here the clock is the same, so we can go from higher RR to lower RR or vice versa just by changing the vtotal. Seamless switching is possible in both directions. The previous change makes intel_panel_fixed_mode() always return the highest refresh rate mode for all VRR panels. This works well for Type 2 panels since there is no clock advantage from picking a lower mode. However for Type 1 (seamless DRRS) panels, if the user sets the allow_modeset flag they really want a lower RR mode with a lower clock to save power. So avoid selecting the highest RR mode when allow_modeset is set for such panels. Also, for seamless DRRS panels on platforms with double-buffered M/N support, the clock can be changed on the fly, so we don't need the highest RR + vtotal adjustment approach. To understand the user requirement for full modeset/seamless switch, add a nullable struct drm_atomic_commit state parameter to intel_panel_fixed_mode() to check the allow_modeset flag. Note: The mode_valid callers pass NULL since they have no atomic state. In that case use the existing approach to select the closest-match to avoid pruning valid modes. Signed-off-by: Ankit Nautiyal <[email protected]> --- drivers/gpu/drm/i915/display/intel_panel.c | 35 ++++++++++++++++++---- drivers/gpu/drm/i915/display/intel_panel.h | 3 +- drivers/gpu/drm/i915/display/intel_sdvo.c | 5 ++-- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 1c2a8cd454be..cc228ff81510 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -72,8 +72,10 @@ static bool is_best_fixed_mode(int vrefresh, int fixed_mode_vrefresh, } static bool need_higher_rr_mode(struct intel_connector *connector, - const struct drm_display_mode *mode) + const struct drm_display_mode *mode, + const struct drm_atomic_commit *state) { + struct intel_display *display = to_intel_display(connector); int vrefresh = drm_mode_vrefresh(mode); if (!intel_vrr_is_capable(connector)) @@ -82,12 +84,33 @@ static bool need_higher_rr_mode(struct intel_connector *connector, if (!intel_vrr_is_in_range(connector, vrefresh)) return false; - return true; + if (!state) + return false; + + /* + * If Seamless switch requested, use highest RR mode + vtotal + * adjustment, unless DRRS with double-buffered M/N which + * can change the clock on the fly. + */ + if (!state->allow_modeset) { + if (intel_panel_drrs_type(connector) == DRRS_TYPE_SEAMLESS && + HAS_DOUBLE_BUFFERED_M_N(display)) + return false; + return true; + } + + /* + * If full modeset is allowed, then for DRRS panels, use nearest mode + * (lower clock saves power). For non-DRRS VRR panels, use highest RR + * mode (no clock advantage from picking a lower mode). + */ + return intel_panel_drrs_type(connector) != DRRS_TYPE_SEAMLESS; } const struct drm_display_mode * intel_panel_fixed_mode(struct intel_connector *connector, - const struct drm_display_mode *mode) + const struct drm_display_mode *mode, + const struct drm_atomic_commit *state) { const struct drm_display_mode *fixed_mode, *best_mode = NULL; int vrefresh = drm_mode_vrefresh(mode); @@ -97,7 +120,7 @@ intel_panel_fixed_mode(struct intel_connector *connector, * which we can then reduce to match the requested * vrefresh by extending the vblank length. */ - if (need_higher_rr_mode(connector, mode)) + if (need_higher_rr_mode(connector, mode, state)) return intel_panel_highest_vrefresh_mode(connector); list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) { @@ -229,7 +252,7 @@ int intel_panel_compute_config(struct intel_connector *connector, { struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; const struct drm_display_mode *fixed_mode = - intel_panel_fixed_mode(connector, adjusted_mode); + intel_panel_fixed_mode(connector, adjusted_mode, state); int vrefresh, fixed_mode_vrefresh; bool is_vrr; @@ -435,7 +458,7 @@ intel_panel_mode_valid(struct intel_connector *connector, int *target_clock) { const struct drm_display_mode *fixed_mode = - intel_panel_fixed_mode(connector, mode); + intel_panel_fixed_mode(connector, mode, NULL); if (target_clock) *target_clock = mode->clock; diff --git a/drivers/gpu/drm/i915/display/intel_panel.h b/drivers/gpu/drm/i915/display/intel_panel.h index 3c4ff6735c21..c44323918768 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.h +++ b/drivers/gpu/drm/i915/display/intel_panel.h @@ -33,7 +33,8 @@ const struct drm_display_mode * intel_panel_preferred_fixed_mode(struct intel_connector *connector); const struct drm_display_mode * intel_panel_fixed_mode(struct intel_connector *connector, - const struct drm_display_mode *mode); + const struct drm_display_mode *mode, + const struct drm_atomic_commit *state); const struct drm_display_mode * intel_panel_downclock_mode(struct intel_connector *connector, const struct drm_display_mode *adjusted_mode); diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c index e11c1dfc602a..0574a027526a 100644 --- a/drivers/gpu/drm/i915/display/intel_sdvo.c +++ b/drivers/gpu/drm/i915/display/intel_sdvo.c @@ -797,7 +797,7 @@ intel_sdvo_create_preferred_input_timing(struct intel_sdvo *intel_sdvo, if (IS_LVDS(intel_sdvo_connector)) { const struct drm_display_mode *fixed_mode = -intel_panel_fixed_mode(&intel_sdvo_connector->base, mode); + intel_panel_fixed_mode(&intel_sdvo_connector->base, mode, NULL); if (fixed_mode->hdisplay != args.width || fixed_mode->vdisplay != args.height) @@ -1564,7 +1564,8 @@ static void intel_sdvo_pre_enable(struct intel_atomic_state *state, /* lvds has a special fixed output timing. */ if (IS_LVDS(intel_sdvo_connector)) { const struct drm_display_mode *fixed_mode = - intel_panel_fixed_mode(&intel_sdvo_connector->base, mode); + intel_panel_fixed_mode(&intel_sdvo_connector->base, + mode, conn_state->state); intel_sdvo_get_dtd_from_mode(&output_dtd, fixed_mode); } else { -- 2.45.2
