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 | 41 +++++++++++++++++++--- drivers/gpu/drm/i915/display/intel_panel.h | 3 +- drivers/gpu/drm/i915/display/intel_sdvo.c | 5 +-- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 1c2a8cd454be..7f67b6ae45a9 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,41 @@ static bool need_higher_rr_mode(struct intel_connector *connector, if (!intel_vrr_is_in_range(connector, vrefresh)) return false; + /* For mode valid path do not use High RR mode */ + if (!state) + return false; + + /* For seamless switch use High RR mode */ + if (!state->allow_modeset) + return true; + + /* + * For DRRS panels with platforms that do not support + * Double buffered MN, if allow modeset is set then + * user might really need a lower clock mode, so do not + * pick Higher RR mode in such a case. + * + * Platforms with Double buffered MN do not need this + * since the lower clock can be achieved by seamless MN + * switch by DRRS. + */ + if (intel_panel_drrs_type(connector) == DRRS_TYPE_SEAMLESS && + !HAS_DOUBLE_BUFFERED_M_N(display)) { + const struct drm_display_mode *downclock_mode = + intel_panel_downclock_mode(connector, mode); + + if (downclock_mode && + drm_mode_vrefresh(downclock_mode) == drm_mode_vrefresh(mode)) + return false; + } + return true; } 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 +128,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 +260,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 +466,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 51746e10359f..9182a7f6b776 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
