diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c
index 3fcee8f3ae67..1ae8f499a5d4 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power.c
+++ b/drivers/gpu/drm/i915/display/intel_display_power.c
@@ -20,6 +20,7 @@
 #include "intel_sideband.h"
 #include "intel_tc.h"
 #include "intel_pm.h"
+#include "intel_psr.h"
 
 bool intel_display_power_well_is_enabled(struct drm_i915_private *dev_priv,
 					 enum i915_power_well_id power_well_id);
@@ -794,6 +795,9 @@ void tgl_clear_psr2_transcoder_exitline(const struct intel_crtc_state *cstate)
 	val = I915_READ(EXITLINE(cstate->cpu_transcoder));
 	val &= ~(EXITLINE_MASK | EXITLINE_ENABLE);
 	I915_WRITE(EXITLINE(cstate->cpu_transcoder), val);
+
+	/* As psr2 encoder has disabled, cancel the dc5 idle delayed work */
+	cancel_delayed_work_sync(&dev_priv->csr.idle_work);
 }
 
 void tgl_set_psr2_transcoder_exitline(const struct intel_crtc_state *cstate)
@@ -813,6 +817,27 @@ void tgl_set_psr2_transcoder_exitline(const struct intel_crtc_state *cstate)
 	I915_WRITE(EXITLINE(cstate->cpu_transcoder), val);
 }
 
+static u32 intel_get_frame_time_us(const struct intel_crtc_state *cstate)
+{
+	u32 pixel_rate, crtc_htotal, crtc_vtotal;
+	u32 frametime_us;
+
+	if (!cstate || !cstate->base.active)
+		return 0;
+
+	pixel_rate = cstate->pixel_rate;
+
+	if (WARN_ON(pixel_rate == 0))
+		return 0;
+
+	crtc_htotal = cstate->base.adjusted_mode.crtc_htotal;
+	crtc_vtotal = cstate->base.adjusted_mode.crtc_vtotal;
+	frametime_us = DIV_ROUND_UP(crtc_htotal * crtc_vtotal * 1000ULL,
+				    pixel_rate);
+
+	return frametime_us;
+}
+
 /*
  * DC3CO requires to enable exitline and program DC3CO requires
  * exit scanlines to TRANS_EXITLINE register, which should only
@@ -872,6 +897,77 @@ void tgl_dc3co_exitline_get_config(struct intel_crtc_state *crtc_state)
 		crtc_state->dc3co_exitline = val & EXITLINE_MASK;
 }
 
+/*
+ * When we will enable manual PSR2 S/W tracking in future
+ * we will implement this entire DC3CO flush logic in
+ * intel_psr_flush().
+ */
+void tgl_dc3co_flush(struct drm_i915_private *dev_priv,
+		     unsigned int frontbuffer_bits, enum fb_op_origin origin)
+{
+	struct intel_crtc_state *cstate;
+	struct intel_crtc *crtc;
+	u32 delay;
+	unsigned int busy_frontbuffer_bits = 0;
+
+	if (origin != ORIGIN_FLIP)
+		return;
+
+	mutex_lock(&dev_priv->psr.lock);
+
+	if (!dev_priv->psr.dc3co_exitline)
+		goto unlock;
+
+	crtc = intel_get_crtc_for_pipe(dev_priv, dev_priv->psr.pipe);
+	cstate = to_intel_crtc_state(crtc->base.state);
+
+	frontbuffer_bits &=
+		INTEL_FRONTBUFFER_ALL_MASK(dev_priv->psr.pipe);
+
+	busy_frontbuffer_bits &= ~frontbuffer_bits;
+
+	if (!dev_priv->psr.psr2_enabled || !dev_priv->psr.active)
+		goto unlock;
+
+	/*
+	 * At every flip frontbuffer flush modified delay of delayed work,
+	 * when delayed schedules that means display has been idle.
+	 */
+	if (!busy_frontbuffer_bits) {
+		tgl_psr2_deep_sleep_disable(dev_priv);
+		tgl_set_target_dc_state(dev_priv, DC_STATE_EN_DC3CO);
+		/* DC5/DC6 required idle frames = 6 */
+		delay = 6 * intel_get_frame_time_us(cstate);
+		mod_delayed_work(system_wq, &dev_priv->csr.idle_work,
+				 usecs_to_jiffies(delay));
+	}
+
+unlock:
+	mutex_unlock(&dev_priv->psr.lock);
+}
+
+void tgl_enable_psr2_deep_sleep_dc6(struct drm_i915_private *dev_priv)
+{
+	tgl_set_target_dc_state(dev_priv, DC_STATE_EN_UPTO_DC6);
+	tgl_psr2_deep_sleep_enable(dev_priv);
+}
+
+static void tgl_dc5_idle_thread(struct work_struct *work)
+{
+	struct drm_i915_private *dev_priv =
+		container_of(work, typeof(*dev_priv), csr.idle_work.work);
+
+	mutex_lock(&dev_priv->psr.lock);
+	/* If delayed work is pending, it is not idle */
+	if (delayed_work_pending(&dev_priv->csr.idle_work))
+		goto unlock;
+
+	DRM_DEBUG_KMS("DC5/6 idle thread\n");
+	tgl_enable_psr2_deep_sleep_dc6(dev_priv);
+unlock:
+	mutex_unlock(&dev_priv->psr.lock);
+}
+
 static void
 allowed_dc_mask_to_target_dc_state(struct drm_i915_private *dev_priv)
 {
@@ -4241,6 +4337,7 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv)
 
 	INIT_DELAYED_WORK(&power_domains->async_put_work,
 			  intel_display_power_put_async_work);
+	INIT_DELAYED_WORK(&dev_priv->csr.idle_work, tgl_dc5_idle_thread);
 
 	/*
 	 * The enabling order will be from lower to higher indexed wells,
diff --git a/drivers/gpu/drm/i915/display/intel_display_power.h b/drivers/gpu/drm/i915/display/intel_display_power.h
index 981b6a52d192..5f5caea857af 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power.h
+++ b/drivers/gpu/drm/i915/display/intel_display_power.h
@@ -9,6 +9,7 @@
 #include "intel_display.h"
 #include "intel_runtime_pm.h"
 #include "i915_reg.h"
+#include "intel_frontbuffer.h"
 
 struct drm_i915_private;
 struct intel_encoder;
@@ -265,6 +266,9 @@ void tgl_dc3co_exitline_compute_config(struct intel_encoder *encoder,
 void tgl_dc3co_exitline_get_config(struct intel_crtc_state *crtc_state);
 void tgl_clear_psr2_transcoder_exitline(const struct intel_crtc_state *state);
 void tgl_set_psr2_transcoder_exitline(const struct intel_crtc_state *state);
+void tgl_dc3co_flush(struct drm_i915_private *dev_priv,
+		     unsigned int frontbuffer_bits, enum fb_op_origin origin);
+void tgl_enable_psr2_deep_sleep_dc6(struct drm_i915_private *dev_priv);
 
 const char *
 intel_display_power_domain_str(enum intel_display_power_domain domain);
diff --git a/drivers/gpu/drm/i915/display/intel_frontbuffer.c b/drivers/gpu/drm/i915/display/intel_frontbuffer.c
index fc40dc1fdbcc..c3b10f6e4382 100644
--- a/drivers/gpu/drm/i915/display/intel_frontbuffer.c
+++ b/drivers/gpu/drm/i915/display/intel_frontbuffer.c
@@ -90,6 +90,7 @@ static void frontbuffer_flush(struct drm_i915_private *i915,
 	might_sleep();
 	intel_edp_drrs_flush(i915, frontbuffer_bits);
 	intel_psr_flush(i915, frontbuffer_bits, origin);
+	tgl_dc3co_flush(i915, frontbuffer_bits, origin);
 	intel_fbc_flush(i915, frontbuffer_bits, origin);
 }
 
diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
index bf0b741d3243..5faaf35ba4ff 100644
--- a/drivers/gpu/drm/i915/display/intel_psr.c
+++ b/drivers/gpu/drm/i915/display/intel_psr.c
@@ -534,10 +534,44 @@ transcoder_has_psr2(struct drm_i915_private *dev_priv, enum transcoder trans)
 		return trans == TRANSCODER_EDP;
 }
 
+static void psr2_program_idle_frames(struct drm_i915_private *dev_priv,
+				     u32 idle_frames)
+{
+	u32 val;
+
+	idle_frames <<=  EDP_PSR2_IDLE_FRAME_SHIFT;
+	val = I915_READ(EDP_PSR2_CTL(dev_priv->psr.transcoder));
+	val &= ~EDP_PSR2_IDLE_FRAME_MASK;
+	val |= idle_frames;
+	I915_WRITE(EDP_PSR2_CTL(dev_priv->psr.transcoder), val);
+}
+
+void tgl_psr2_deep_sleep_disable(struct drm_i915_private *dev_priv)
+{
+	psr2_program_idle_frames(dev_priv, 0);
+}
+
+void tgl_psr2_deep_sleep_enable(struct drm_i915_private *dev_priv)
+{
+	int idle_frames;
+
+	/*
+	 * Let's use 6 as the minimum to cover all known cases including the
+	 * off-by-one issue that HW has in some cases.
+	 */
+	idle_frames = max(6, dev_priv->vbt.psr.idle_frames);
+	idle_frames = max(idle_frames, dev_priv->psr.sink_sync_latency + 1);
+	psr2_program_idle_frames(dev_priv, idle_frames);
+}
+
 static void tgl_disallow_dc3co_on_psr2_exit(struct drm_i915_private *dev_priv)
 {
 	if (!dev_priv->psr.dc3co_exitline)
 		return;
+
+	cancel_delayed_work(&dev_priv->csr.idle_work);
+	/* Before PSR2 exit disallow dc3co*/
+	tgl_enable_psr2_deep_sleep_dc6(dev_priv);
 }
 
 static bool intel_psr2_config_valid(struct intel_dp *intel_dp,
diff --git a/drivers/gpu/drm/i915/display/intel_psr.h b/drivers/gpu/drm/i915/display/intel_psr.h
index 46e4de8b8cd5..75a9862f36fd 100644
--- a/drivers/gpu/drm/i915/display/intel_psr.h
+++ b/drivers/gpu/drm/i915/display/intel_psr.h
@@ -35,5 +35,7 @@ void intel_psr_short_pulse(struct intel_dp *intel_dp);
 int intel_psr_wait_for_idle(const struct intel_crtc_state *new_crtc_state,
 			    u32 *out_value);
 bool intel_psr_enabled(struct intel_dp *intel_dp);
+void tgl_psr2_deep_sleep_disable(struct drm_i915_private *dev_priv);
+void tgl_psr2_deep_sleep_enable(struct drm_i915_private *dev_priv);
 
 #endif /* __INTEL_PSR_H__ */
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index b86b5bd1b70a..087c13cb26b2 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -339,6 +339,7 @@ struct intel_csr {
 	u32 dc_state;
 	u32 target_dc_state;
 	u32 allowed_dc_mask;
+	struct delayed_work idle_work;
 	intel_wakeref_t wakeref;
 };
 
