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

Reply via email to