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

Reply via email to