drm/msm/sde: add exclusion rect support

Add exclusion rect support for DRM planes. The hardware avoids fetching
data for the excluded region for pipe. This feature is supported from
SDE 4.0 for both linear and UBWC RGB formats. The programming changes
for exclusion rect related to multirect will be done as part of
multirect changes.

Change-Id: Id07ca491d6913fc04589eca47a1019d0b124adbe
Signed-off-by: Veera Sundaram Sankaran <veeras@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 143bbe9..cf24fcb 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -1073,6 +1073,7 @@
 		case PLANE_PROP_V_DECIMATE:
 		case PLANE_PROP_SRC_CONFIG:
 		case PLANE_PROP_ZPOS:
+		case PLANE_PROP_EXCL_RECT_V1:
 			pstate->dirty |= SDE_PLANE_DIRTY_RECTS;
 			break;
 		case PLANE_PROP_CSC_V1:
@@ -1152,6 +1153,11 @@
 					&psde->pipe_cfg, &psde->pixel_ext,
 					psde->scaler3_cfg);
 		}
+
+		/* update excl rect */
+		if (psde->pipe_hw->ops.setup_excl_rect)
+			psde->pipe_hw->ops.setup_excl_rect(psde->pipe_hw,
+					&pstate->excl_rect);
 	}
 
 	if ((pstate->dirty & SDE_PLANE_DIRTY_FORMAT) &&
@@ -1235,6 +1241,7 @@
 		struct drm_plane_state *old_state)
 {
 	struct sde_plane_state *pstate = to_sde_plane_state(state);
+	struct sde_plane_state *old_pstate = to_sde_plane_state(old_state);
 
 	/* no need to check it again */
 	if (pstate->dirty == SDE_PLANE_DIRTY_ALL)
@@ -1260,6 +1267,12 @@
 		   state->crtc_y != old_state->crtc_y) {
 		SDE_DEBUG_PLANE(psde, "crtc rect updated\n");
 		pstate->dirty |= SDE_PLANE_DIRTY_RECTS;
+	} else if (pstate->excl_rect.w != old_pstate->excl_rect.w ||
+		   pstate->excl_rect.h != old_pstate->excl_rect.h ||
+		   pstate->excl_rect.x != old_pstate->excl_rect.x ||
+		   pstate->excl_rect.y != old_pstate->excl_rect.y) {
+		SDE_DEBUG_PLANE(psde, "excl rect updated\n");
+		pstate->dirty |= SDE_PLANE_DIRTY_RECTS;
 	}
 
 	if (!state->fb || !old_state->fb) {
@@ -1434,6 +1447,27 @@
 		ret = -E2BIG;
 	}
 
+	/* check excl rect configs */
+	if (pstate->excl_rect.w && pstate->excl_rect.h) {
+		struct sde_rect intersect;
+
+		/*
+		 * Check exclusion rect against src rect.
+		 * Cropping is not required as hardware will consider only the
+		 * intersecting region with the src rect.
+		 */
+		sde_kms_rect_intersect(&intersect, &src, &pstate->excl_rect);
+		if (!intersect.w || !intersect.h || SDE_FORMAT_IS_YUV(fmt)) {
+			SDE_ERROR_PLANE(psde,
+				"invalid excl_rect:{%d,%d,%d,%d} src:{%d,%d,%d,%d}, fmt:%s\n",
+				pstate->excl_rect.x, pstate->excl_rect.y,
+				pstate->excl_rect.w, pstate->excl_rect.h,
+				src.x, src.y, src.w, src.h,
+				drm_get_format_name(fmt->base.pixel_format));
+			ret = -EINVAL;
+		}
+	}
+
 modeset_update:
 	if (!ret)
 		_sde_plane_atomic_check_mode_changed(psde, state, plane->state);
@@ -1620,6 +1654,10 @@
 			PLANE_PROP_CONTRAST_ADJUST);
 	}
 
+	if (psde->features & BIT(SDE_SSPP_EXCL_RECT))
+		msm_property_install_volatile_range(&psde->property_info,
+			"excl_rect_v1", 0x0, 0, ~0, 0, PLANE_PROP_EXCL_RECT_V1);
+
 	/* standard properties */
 	msm_property_install_rotation(&psde->property_info,
 		(unsigned int) (BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y)),
@@ -1868,6 +1906,33 @@
 	SDE_DEBUG_PLANE(psde, "user property data copied\n");
 }
 
+static void _sde_plane_set_excl_rect_v1(struct sde_plane *psde,
+		struct sde_plane_state *pstate, void *usr_ptr)
+{
+	struct drm_clip_rect excl_rect_v1;
+
+	if (!psde) {
+		SDE_ERROR("invalid plane\n");
+		return;
+	}
+
+	if (!usr_ptr) {
+		SDE_DEBUG_PLANE(psde, "excl rect data removed\n");
+		return;
+	}
+
+	if (copy_from_user(&excl_rect_v1, usr_ptr, sizeof(excl_rect_v1))) {
+		SDE_ERROR_PLANE(psde, "failed to copy excl rect data\n");
+		return;
+	}
+
+	/* populate from user space */
+	pstate->excl_rect.x = excl_rect_v1.x1;
+	pstate->excl_rect.y = excl_rect_v1.y1;
+	pstate->excl_rect.w = excl_rect_v1.x2 - excl_rect_v1.x1 + 1;
+	pstate->excl_rect.h = excl_rect_v1.y2 - excl_rect_v1.y1 + 1;
+}
+
 static int sde_plane_atomic_set_property(struct drm_plane *plane,
 		struct drm_plane_state *state, struct drm_property *property,
 		uint64_t val)
@@ -1904,6 +1969,10 @@
 				_sde_plane_set_scaler_v2(psde, pstate,
 					(void *)val);
 				break;
+			case PLANE_PROP_EXCL_RECT_V1:
+				_sde_plane_set_excl_rect_v1(psde, pstate,
+						(void *)val);
+				break;
 			default:
 				/* nothing to do */
 				break;