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__ */
