diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h
index 5e0d42d82c11..b2969d8ff625 100644
--- a/drivers/gpu/drm/i915/display/intel_display.h
+++ b/drivers/gpu/drm/i915/display/intel_display.h
@@ -465,6 +465,15 @@ enum phy_fia {
 	     (__i)++) \
 		for_each_if(plane)
 
+#define for_each_oldnew_intel_plane_in_state_reverse(__state, plane, old_plane_state, new_plane_state, __i) \
+	for ((__i) = (__state)->base.dev->mode_config.num_total_plane - 1; \
+	     (__i) >= 0 && \
+		     ((plane) = to_intel_plane((__state)->base.planes[__i].ptr), \
+		      (old_plane_state) = to_intel_plane_state((__state)->base.planes[__i].old_state), \
+		      (new_plane_state) = to_intel_plane_state((__state)->base.planes[__i].new_state), 1); \
+	     (__i)--) \
+		for_each_if(plane)
+
 #define for_each_oldnew_intel_crtc_in_state(__state, crtc, old_crtc_state, new_crtc_state, __i) \
 	for ((__i) = 0; \
 	     (__i) < (__state)->base.dev->mode_config.num_crtc && \
diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
index d9a395c486d3..f314f550b809 100644
--- a/drivers/gpu/drm/i915/display/intel_psr.c
+++ b/drivers/gpu/drm/i915/display/intel_psr.c
@@ -1249,75 +1249,231 @@ static void psr2_man_trk_ctl_calc(struct intel_crtc_state *crtc_state,
 	crtc_state->psr2_man_track_ctl = val;
 }
 
-static void clip_area_update(struct drm_rect *overlap_damage_area,
-			     struct drm_rect *damage_area)
+/* calculate and update selective update rect with update rect */
+static void
+su_rect_update(struct drm_rect *su_rect, struct drm_rect *update_rect)
 {
-	if (overlap_damage_area->y1 == -1) {
-		overlap_damage_area->y1 = damage_area->y1;
-		overlap_damage_area->y2 = damage_area->y2;
+	if (drm_rect_height(update_rect) <= 0)
 		return;
+
+	if (!drm_rect_height(su_rect)) {
+		/* when select update rect is empty */
+		su_rect->y1 = update_rect->y1;
+		su_rect->y2 = update_rect->y2;
+	} else {
+		su_rect->y1 = min(su_rect->y1, update_rect->y1);
+		su_rect->y2 = max(su_rect->y2, update_rect->y2);
 	}
+}
+
+static void
+plane_get_damage_rect(const struct drm_plane_state *state,
+		      struct drm_rect *damage_rect)
+{
+	struct drm_mode_rect *damage_clips;
+	int i;
+	u32 num_clips = drm_plane_get_damage_clips_count(state);
+
+
+	if (!num_clips)
+		return;
 
-	if (damage_area->y1 < overlap_damage_area->y1)
-		overlap_damage_area->y1 = damage_area->y1;
+	damage_clips = drm_plane_get_damage_clips(state);
 
-	if (damage_area->y2 > overlap_damage_area->y2)
-		overlap_damage_area->y2 = damage_area->y2;
+	/* initialize with first damage_clip */
+	damage_rect->x1 = damage_clips[0].x1;
+	damage_rect->y1 = damage_clips[0].y1;
+	damage_rect->x2 = damage_clips[0].x2;
+	damage_rect->y2 = damage_clips[0].y2;
+
+	for (i = 1; i < num_clips; i++) {
+		/* Selective Fetch has limitattion which only can have one rect */
+		damage_rect->x1 = min(damage_rect->x1, damage_clips[i].x1);
+		damage_rect->x2 = max(damage_rect->x2, damage_clips[i].x2);
+		damage_rect->y1 = min(damage_rect->y1, damage_clips[i].y1);
+		damage_rect->y2 = max(damage_rect->y2, damage_clips[i].y2);
+	}
 }
 
-int intel_psr2_sel_fetch_update(struct intel_atomic_state *state,
-				struct intel_crtc *crtc)
+/*
+ * Generate Selective Update area rect
+ * Todo: In order to optimize calculating of SU area, it should subtract
+ * accumulated SU area from the visible area.
+ */
+static void
+psr_generate_su_rect(struct intel_atomic_state *state,
+		     struct intel_crtc_state *crtc_state,
+		     struct drm_rect *su_rect)
 {
-	struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
 	struct intel_plane_state *new_plane_state, *old_plane_state;
-	struct drm_rect pipe_clip = { .y1 = -1 };
 	struct intel_plane *plane;
-	bool full_update = false;
-	int i, ret;
+	int i;
 
-	if (!crtc_state->enable_psr2_sel_fetch)
-		return 0;
+	for_each_oldnew_intel_plane_in_state_reverse(state, plane, old_plane_state,
+						     new_plane_state, i) {
+		bool old_alpha, new_alpha, alpha_change;
+		bool visible, visibility_change;
+		bool flip, move;
+		u32 num_clips;
 
-	ret = drm_atomic_add_affected_planes(&state->base, &crtc->base);
-	if (ret)
-		return ret;
+		if (new_plane_state->uapi.crtc != crtc_state->uapi.crtc)
+			continue;
 
-	for_each_oldnew_intel_plane_in_state(state, plane, old_plane_state,
-					     new_plane_state, i) {
-		struct drm_rect *sel_fetch_area, temp;
+		if (plane->id == PLANE_CURSOR)
+			continue;
 
-		if (new_plane_state->uapi.crtc != crtc_state->uapi.crtc)
+		new_alpha = new_plane_state->uapi.alpha != DRM_BLEND_ALPHA_OPAQUE;
+		old_alpha = old_plane_state->uapi.alpha != DRM_BLEND_ALPHA_OPAQUE;
+		alpha_change = new_alpha != old_alpha;
+		visible = new_plane_state->uapi.visible;
+		visibility_change =
+			old_plane_state->uapi.visible != new_plane_state->uapi.visible;
+		flip = new_plane_state->uapi.fb != old_plane_state->uapi.fb;
+		move = !drm_rect_equals(&new_plane_state->uapi.dst,
+					&old_plane_state->uapi.dst);
+
+		/* 1. plane has moved */
+		if (move && !visibility_change && visible) {
+			su_rect_update(su_rect, &old_plane_state->uapi.dst);
+			su_rect_update(su_rect, &new_plane_state->uapi.dst);
 			continue;
+		}
 
-		/*
-		 * TODO: Not clear how to handle planes with negative position,
-		 * also planes are not updated if they have a negative X
-		 * position so for now doing a full update in this cases
-		 */
-		if (new_plane_state->uapi.dst.y1 < 0 ||
-		    new_plane_state->uapi.dst.x1 < 0) {
-			full_update = true;
-			break;
+		/* 2. plane's alpha value has changed */
+		if (alpha_change && !visibility_change && visible) {
+			su_rect_update(su_rect, &new_plane_state->uapi.dst);
+			continue;
+		}
+
+		/* 3. plane's visibility has changed */
+		if (visibility_change) {
+			if (visible)
+				su_rect_update(su_rect, &new_plane_state->uapi.dst);
+			else
+				su_rect_update(su_rect, &old_plane_state->uapi.dst);
+			continue;
+		}
+
+		num_clips = drm_plane_get_damage_clips_count(&new_plane_state->uapi);
+		/* 4. plane's damage clips exist */
+		if (num_clips && visible) {
+			struct drm_rect damage_rect, src_rect;
+
+			drm_rect_init(&damage_rect, 0, 0, 0, 0);
+			plane_get_damage_rect(&new_plane_state->uapi, &damage_rect);
+			/* convert fixed point float to int */
+			src_rect.x1 = new_plane_state->uapi.src.x1 >> 16;
+			src_rect.x2 = new_plane_state->uapi.src.x2 >> 16;
+			src_rect.y1 = new_plane_state->uapi.src.y1 >> 16;
+			src_rect.y2 = new_plane_state->uapi.src.y2 >> 16;
+
+			/* damage rect is based on src geometry */
+			if (drm_rect_intersect(&damage_rect, &src_rect)) {
+				/*
+				 * su rect is based on dst geometry
+				 * convert damage_rect's src geometry to dst geometry
+				 */
+				damage_rect.x1 =
+					damage_rect.x1 - src_rect.x1 +
+					new_plane_state->uapi.dst.x1;
+				damage_rect.x2 =
+					damage_rect.x2 - src_rect.x1 +
+					new_plane_state->uapi.dst.x1;
+				damage_rect.y1 =
+					damage_rect.y1 - src_rect.y1 +
+					new_plane_state->uapi.dst.y1;
+				damage_rect.y2 =
+					damage_rect.y2 - src_rect.y1 +
+					new_plane_state->uapi.dst.y1;
+
+				su_rect_update(su_rect, &damage_rect);
+			}
+			continue;
+		}
+
+		/* 5. plane's fb has flipped, but there were no damaged clips */
+		if (flip && visible) {
+			su_rect_update(su_rect, &new_plane_state->uapi.dst);
+			continue;
 		}
+	}
+}
+
+/*
+ * Generate Plane's Selective Fetch rect from intersected area between
+ * Selective Update area rect and plane's dst rect.
+ * Todo: Consider to handle a fully obscured plane.
+ */
+static void
+psr_generate_plane_sf_rect(struct intel_atomic_state *state,
+			   struct intel_crtc_state *crtc_state,
+			   const struct drm_rect *su_rect)
+{
+	struct intel_plane_state *new_plane_state, *old_plane_state;
+	struct intel_plane *plane;
+	int i;
+
+	for_each_oldnew_intel_plane_in_state_reverse(state, plane, old_plane_state,
+						     new_plane_state, i) {
+
+		struct drm_rect sf_rect = new_plane_state->uapi.dst;
+
+		if (new_plane_state->uapi.crtc != crtc_state->uapi.crtc)
+			continue;
+
+		if (plane->id == PLANE_CURSOR)
+			continue;
 
 		if (!new_plane_state->uapi.visible)
 			continue;
 
-		/*
-		 * For now doing a selective fetch in the whole plane area,
-		 * optimizations will come in the future.
-		 */
-		sel_fetch_area = &new_plane_state->psr2_sel_fetch_area;
-		sel_fetch_area->y1 = new_plane_state->uapi.src.y1 >> 16;
-		sel_fetch_area->y2 = new_plane_state->uapi.src.y2 >> 16;
-
-		temp = *sel_fetch_area;
-		temp.y1 += new_plane_state->uapi.dst.y1;
-		temp.y2 += new_plane_state->uapi.dst.y2;
-		clip_area_update(&pipe_clip, &temp);
+		if (drm_rect_intersect(&sf_rect, su_rect)) {
+			/*
+			 * su rect is based on dst geometry and sf rect should
+			 * follow src geometry. convert sf rect's dst geometry
+			 * to dst geometry
+			 */
+			sf_rect.x1 = sf_rect.x1 - new_plane_state->uapi.dst.x1 +
+				     (new_plane_state->uapi.src.x1 >> 16);
+			sf_rect.x2 = sf_rect.x2 - new_plane_state->uapi.dst.x1 +
+				     (new_plane_state->uapi.src.x1 >> 16);
+			sf_rect.y1 = sf_rect.y1 - new_plane_state->uapi.dst.y1 +
+				     (new_plane_state->uapi.src.y1 >> 16);
+			sf_rect.y2 = sf_rect.y2 - new_plane_state->uapi.dst.y1 +
+				     (new_plane_state->uapi.src.y1 >> 16);
+
+			new_plane_state->psr2_sel_fetch_area = sf_rect;
+		}
 	}
+}
+
+int intel_psr2_sel_fetch_update(struct intel_atomic_state *state,
+				struct intel_crtc *crtc)
+{
+	struct intel_crtc_state *crtc_state =
+		intel_atomic_get_new_crtc_state(state, crtc);
+	struct drm_rect su_rect;
+	bool full_update = false;
+	int ret;
+
+	if (!crtc_state->enable_psr2_sel_fetch)
+		return 0;
+
+	ret = drm_atomic_add_affected_planes(&state->base, &crtc->base);
+	if (ret)
+		return ret;
+
+	/*
+	 * Generate Selective Update area rect
+	 * Todo: In order to optimize calculating of SU area, it should subtract
+	 * accumulated SU area from the visible area.
+	 */
+	drm_rect_init(&su_rect, 0, 0,
+		      crtc_state->uapi.adjusted_mode.crtc_hdisplay, 0);
+	psr_generate_su_rect(state, crtc_state, &su_rect);
+	psr_generate_plane_sf_rect(state, crtc_state, &su_rect);
+	psr2_man_trk_ctl_calc(crtc_state, &su_rect, full_update);
 
-	psr2_man_trk_ctl_calc(crtc_state, &pipe_clip, full_update);
 	return 0;
 }
 
