As described in previous commit "drm: Add infrastructure to allow
seamless mode switches" here doing the i915 implementation.

The main steps are:
- drm_atomic_helper_check_modeset() will call
atomic_seamless_mode_switch_check()/intel_drrs_seamless_mode_switch_check()
if conditions match
- intel_drrs_seamless_mode_switch_check() will check if seamless DRRS
is enabled and if the requested mode matches with fixed or downclock
mode
- now at the atomic commit phase during the execution of
intel_pre_plane_update(), intel_drrs_deactivate() will
return without change the DRRS state if seamless_mode_switch_enabled
or seamless_mode_changed is set and there is no modeset happening in
this pipe(something after drm_atomic_helper_check_modeset() could
still require a modeset and set mode_changed)
- then in intel_post_plane_update(), intel_drrs_activate() is called
and DRRS mode is switched if seamless_mode_changed is set or the
function is skipped if seamless_mode_switch_enabled is set
and pipe don't need a modeset

After a modeset, seamless_mode_switch_enabled is set to 0 and
DRRS is back to automatic mode until the next commit
that intel_drrs_seamless_mode_switch_check() is executed and
it supports the mode.

Cc: Vidya Srinivas <vidya.srini...@intel.com>
Cc: Sean Paul <seanp...@chromium.org>
Cc: Ville Syrjälä <ville.syrj...@linux.intel.com>
Signed-off-by: José Roberto de Souza <jose.so...@intel.com>
---
 drivers/gpu/drm/i915/display/intel_crtc.c     | 10 +++
 drivers/gpu/drm/i915/display/intel_display.c  |  5 +-
 .../drm/i915/display/intel_display_debugfs.c  |  5 +-
 .../drm/i915/display/intel_display_types.h    |  3 +-
 drivers/gpu/drm/i915/display/intel_dp.c       |  2 +
 drivers/gpu/drm/i915/display/intel_drrs.c     | 73 ++++++++++++++++++-
 drivers/gpu/drm/i915/display/intel_drrs.h     |  5 +-
 7 files changed, 96 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c 
b/drivers/gpu/drm/i915/display/intel_crtc.c
index 4442aa355f868..d411daa0b2bab 100644
--- a/drivers/gpu/drm/i915/display/intel_crtc.c
+++ b/drivers/gpu/drm/i915/display/intel_crtc.c
@@ -215,6 +215,15 @@ static int intel_crtc_late_register(struct drm_crtc *crtc)
        return 0;
 }
 
+static int intel_crtc_seamless_mode_switch_check(struct drm_atomic_state 
*_state,
+                                                struct drm_crtc *_crtc)
+{
+       struct intel_atomic_state *state = to_intel_atomic_state(_state);
+       struct intel_crtc *crtc = to_intel_crtc(_crtc);
+
+       return intel_drrs_seamless_mode_switch_check(state, crtc);
+}
+
 #define INTEL_CRTC_FUNCS \
        .set_config = drm_atomic_helper_set_config, \
        .destroy = intel_crtc_destroy, \
@@ -233,6 +242,7 @@ static const struct drm_crtc_funcs bdw_crtc_funcs = {
        .enable_vblank = bdw_enable_vblank,
        .disable_vblank = bdw_disable_vblank,
        .get_vblank_timestamp = intel_crtc_get_vblank_timestamp,
+       .atomic_seamless_mode_switch_check = 
intel_crtc_seamless_mode_switch_check,
 };
 
 static const struct drm_crtc_funcs ilk_crtc_funcs = {
diff --git a/drivers/gpu/drm/i915/display/intel_display.c 
b/drivers/gpu/drm/i915/display/intel_display.c
index a5f5caeced9a0..ebfa7d68e35fe 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -1329,7 +1329,7 @@ static void intel_pre_plane_update(struct 
intel_atomic_state *state,
                intel_atomic_get_new_crtc_state(state, crtc);
        enum pipe pipe = crtc->pipe;
 
-       intel_drrs_deactivate(old_crtc_state);
+       intel_drrs_deactivate(old_crtc_state, new_crtc_state);
 
        intel_psr_pre_plane_update(state, crtc);
 
@@ -7089,6 +7089,7 @@ static void intel_crtc_copy_fastset(const struct 
intel_crtc_state *old_crtc_stat
        new_crtc_state->dp_m_n = old_crtc_state->dp_m_n;
        new_crtc_state->dp_m2_n2 = old_crtc_state->dp_m2_n2;
        new_crtc_state->drrs_downclock_mode = 
old_crtc_state->drrs_downclock_mode;
+       new_crtc_state->drrs_fixed_mode = old_crtc_state->drrs_fixed_mode;
 }
 
 static int intel_crtc_add_planes_to_state(struct intel_atomic_state *state,
@@ -7716,6 +7717,8 @@ static int intel_atomic_check(struct drm_device *dev,
                if (!intel_crtc_needs_modeset(new_crtc_state)) {
                        if (intel_crtc_is_bigjoiner_slave(new_crtc_state))
                                copy_bigjoiner_crtc_state_nomodeset(state, 
crtc);
+                       else if (new_crtc_state->uapi.seamless_mode_changed)
+                               intel_crtc_copy_uapi_to_hw_state_modeset(state, 
crtc);
                        else
                                
intel_crtc_copy_uapi_to_hw_state_nomodeset(state, crtc);
                        continue;
diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c 
b/drivers/gpu/drm/i915/display/intel_display_debugfs.c
index f9720562336da..1d27ed2b68210 100644
--- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c
+++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c
@@ -1108,6 +1108,9 @@ static int i915_drrs_status(struct seq_file *m, void 
*unused)
                           crtc->drrs.refresh_rate == DRRS_REFRESH_RATE_LOW ?
                           "low" : "high");
 
+               seq_printf(m, "Seamless mode switch enabled: %s\n",
+                          str_yes_no(crtc->drrs.seamless_mode_switch_enabled));
+
                mutex_unlock(&crtc->drrs.mutex);
        }
 
@@ -1802,7 +1805,7 @@ static int i915_drrs_ctl_set(void *data, u64 val)
                if (val)
                        intel_drrs_activate(crtc_state);
                else
-                       intel_drrs_deactivate(crtc_state);
+                       intel_drrs_deactivate(crtc_state, crtc_state);
 
 out:
                drm_modeset_unlock(&crtc->base.mutex);
diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h 
b/drivers/gpu/drm/i915/display/intel_display_types.h
index f0b3cfd3138ce..b04922fd45e45 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -1056,7 +1056,7 @@ struct intel_crtc_state {
 
        /* m2_n2 for eDP downclock */
        struct intel_link_m_n dp_m2_n2;
-       const struct drm_display_mode *drrs_downclock_mode;
+       const struct drm_display_mode *drrs_fixed_mode, *drrs_downclock_mode;
 
        /* PSR is supported but might not be enabled due the lack of enabled 
planes */
        bool has_psr;
@@ -1316,6 +1316,7 @@ struct intel_crtc {
                unsigned int busy_frontbuffer_bits;
                enum transcoder cpu_transcoder;
                struct intel_link_m_n m_n, m2_n2;
+               bool seamless_mode_switch_enabled;
        } drrs;
 
        int scanline_offset;
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c 
b/drivers/gpu/drm/i915/display/intel_dp.c
index feea172dd2753..82f13c3b0f2b3 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -1881,6 +1881,8 @@ intel_dp_drrs_compute_config(struct intel_connector 
*connector,
        if (IS_IRONLAKE(i915) || IS_SANDYBRIDGE(i915) || IS_IVYBRIDGE(i915))
                pipe_config->msa_timing_delay = 
i915->vbt.edp.drrs_msa_timing_delay;
 
+       pipe_config->drrs_fixed_mode = intel_panel_fixed_mode(connector,
+                                                             
&pipe_config->hw.adjusted_mode);
        pipe_config->drrs_downclock_mode = downclock_mode;
 
        pixel_clock = downclock_mode->clock;
diff --git a/drivers/gpu/drm/i915/display/intel_drrs.c 
b/drivers/gpu/drm/i915/display/intel_drrs.c
index dd527dfc2d1d5..3a801f7e79187 100644
--- a/drivers/gpu/drm/i915/display/intel_drrs.c
+++ b/drivers/gpu/drm/i915/display/intel_drrs.c
@@ -155,6 +155,23 @@ void intel_drrs_activate(const struct intel_crtc_state 
*crtc_state)
 
        mutex_lock(&crtc->drrs.mutex);
 
+       /* Until the next modeset user-space will control the refresh rate */
+       if (crtc_state->uapi.seamless_mode_changed &&
+           !intel_crtc_needs_modeset(crtc_state)) {
+               enum drrs_refresh_rate rate = DRRS_REFRESH_RATE_HIGH;
+
+               if (drm_mode_match(&crtc_state->hw.adjusted_mode,
+                                  crtc_state->drrs_downclock_mode,
+                                  DRM_MODE_MATCH_CLOCK))
+                       rate = DRRS_REFRESH_RATE_LOW;
+
+               crtc->drrs.seamless_mode_switch_enabled = true;
+               intel_drrs_set_state(crtc, rate);
+       }
+
+       if (crtc->drrs.seamless_mode_switch_enabled)
+               goto unlock;
+
        crtc->drrs.cpu_transcoder = crtc_state->cpu_transcoder;
        crtc->drrs.m_n = crtc_state->dp_m_n;
        crtc->drrs.m2_n2 = crtc_state->dp_m2_n2;
@@ -162,7 +179,7 @@ void intel_drrs_activate(const struct intel_crtc_state 
*crtc_state)
        crtc->drrs.busy_frontbuffer_bits = 0;
 
        intel_drrs_schedule_work(crtc);
-
+unlock:
        mutex_unlock(&crtc->drrs.mutex);
 }
 
@@ -172,7 +189,8 @@ void intel_drrs_activate(const struct intel_crtc_state 
*crtc_state)
  *
  * Deactivates DRRS on the crtc.
  */
-void intel_drrs_deactivate(const struct intel_crtc_state *old_crtc_state)
+void intel_drrs_deactivate(const struct intel_crtc_state *old_crtc_state,
+                          const struct intel_crtc_state *new_crtc_state)
 {
        struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->uapi.crtc);
 
@@ -187,13 +205,25 @@ void intel_drrs_deactivate(const struct intel_crtc_state 
*old_crtc_state)
 
        mutex_lock(&crtc->drrs.mutex);
 
+       /*
+        * When seamless_mode_switch_enabled is enabled, user-space is in
+        * control of DRRS and it will only go to the automatic mode on the
+        * next modeset
+        */
+       if ((crtc->drrs.seamless_mode_switch_enabled ||
+            new_crtc_state->uapi.seamless_mode_changed) &&
+           !intel_crtc_needs_modeset(new_crtc_state))
+               goto unlock;
+
        if (intel_drrs_is_active(crtc))
                intel_drrs_set_state(crtc, DRRS_REFRESH_RATE_HIGH);
 
        crtc->drrs.cpu_transcoder = INVALID_TRANSCODER;
        crtc->drrs.frontbuffer_bits = 0;
        crtc->drrs.busy_frontbuffer_bits = 0;
+       crtc->drrs.seamless_mode_switch_enabled = false;
 
+unlock:
        mutex_unlock(&crtc->drrs.mutex);
 
        cancel_delayed_work_sync(&crtc->drrs.work);
@@ -205,7 +235,8 @@ static void intel_drrs_downclock_work(struct work_struct 
*work)
 
        mutex_lock(&crtc->drrs.mutex);
 
-       if (intel_drrs_is_active(crtc) && !crtc->drrs.busy_frontbuffer_bits)
+       if (intel_drrs_is_active(crtc) && !crtc->drrs.busy_frontbuffer_bits &&
+           !crtc->drrs.seamless_mode_switch_enabled)
                intel_drrs_set_state(crtc, DRRS_REFRESH_RATE_LOW);
 
        mutex_unlock(&crtc->drrs.mutex);
@@ -236,6 +267,11 @@ static void intel_drrs_frontbuffer_update(struct 
drm_i915_private *dev_priv,
                else
                        crtc->drrs.busy_frontbuffer_bits &= ~frontbuffer_bits;
 
+               if (crtc->drrs.seamless_mode_switch_enabled) {
+                       mutex_unlock(&crtc->drrs.mutex);
+                       continue;
+               }
+
                /* flush/invalidate means busy screen hence upclock */
                intel_drrs_set_state(crtc, DRRS_REFRESH_RATE_HIGH);
 
@@ -300,3 +336,34 @@ void intel_crtc_drrs_init(struct intel_crtc *crtc)
        mutex_init(&crtc->drrs.mutex);
        crtc->drrs.cpu_transcoder = INVALID_TRANSCODER;
 }
+
+int intel_drrs_seamless_mode_switch_check(struct intel_atomic_state *state,
+                                         struct intel_crtc *crtc)
+{
+       const unsigned int match_mode_flags = DRM_MODE_MATCH_TIMINGS |
+                                             DRM_MODE_MATCH_FLAGS |
+                                             DRM_MODE_MATCH_3D_FLAGS |
+                                             DRM_MODE_MATCH_ASPECT_RATIO;
+       struct intel_crtc_state *new_crtc_state;
+
+       new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
+       if (IS_ERR(new_crtc_state))
+               return PTR_ERR(new_crtc_state);
+
+       if (!CRTC_STATE_HAS_DRRS(new_crtc_state))
+               return -EOPNOTSUPP;
+
+       /* Requested mode matches with fixed or downclock mode? */
+       if (!drm_mode_match(&new_crtc_state->uapi.mode,
+                           new_crtc_state->drrs_fixed_mode,
+                           match_mode_flags) &&
+           !drm_mode_match(&new_crtc_state->uapi.mode,
+                           new_crtc_state->drrs_downclock_mode,
+                           match_mode_flags))
+               return -EOPNOTSUPP;
+
+       drm_mode_copy(&new_crtc_state->uapi.adjusted_mode,
+                     &new_crtc_state->uapi.mode);
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/i915/display/intel_drrs.h 
b/drivers/gpu/drm/i915/display/intel_drrs.h
index 3ad1be1ad9c13..85ad987c45945 100644
--- a/drivers/gpu/drm/i915/display/intel_drrs.h
+++ b/drivers/gpu/drm/i915/display/intel_drrs.h
@@ -18,11 +18,14 @@ struct intel_connector;
 const char *intel_drrs_type_str(enum drrs_type drrs_type);
 bool intel_drrs_is_active(struct intel_crtc *crtc);
 void intel_drrs_activate(const struct intel_crtc_state *crtc_state);
-void intel_drrs_deactivate(const struct intel_crtc_state *crtc_state);
+void intel_drrs_deactivate(const struct intel_crtc_state *old_crtc_state,
+                          const struct intel_crtc_state *new_crtc_state);
 void intel_drrs_invalidate(struct drm_i915_private *dev_priv,
                           unsigned int frontbuffer_bits);
 void intel_drrs_flush(struct drm_i915_private *dev_priv,
                      unsigned int frontbuffer_bits);
 void intel_crtc_drrs_init(struct intel_crtc *crtc);
+int intel_drrs_seamless_mode_switch_check(struct intel_atomic_state *state,
+                                         struct intel_crtc *crtc);
 
 #endif /* __INTEL_DRRS_H__ */
-- 
2.36.0

Reply via email to