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/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index bacd440..be4d422 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -103,6 +103,7 @@
 	PLANE_PROP_SATURATION_ADJUST,
 	PLANE_PROP_VALUE_ADJUST,
 	PLANE_PROP_CONTRAST_ADJUST,
+	PLANE_PROP_EXCL_RECT_V1,
 
 	/* enum/bitmask properties */
 	PLANE_PROP_ROTATION,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 6394f46..494ee35 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -121,6 +121,7 @@
 	SSPP_SCALE_SIZE,
 	SSPP_VIG_BLOCKS,
 	SSPP_RGB_BLOCKS,
+	SSPP_EXCL_RECT,
 	SSPP_PROP_MAX,
 };
 
@@ -284,6 +285,7 @@
 	{SSPP_SCALE_SIZE, "qcom,sde-sspp-scale-size", false, PROP_TYPE_U32},
 	{SSPP_VIG_BLOCKS, "qcom,sde-sspp-vig-blocks", false, PROP_TYPE_NODE},
 	{SSPP_RGB_BLOCKS, "qcom,sde-sspp-rgb-blocks", false, PROP_TYPE_NODE},
+	{SSPP_EXCL_RECT, "qcom,sde-sspp-excl-rect", false, PROP_TYPE_U32_ARRAY},
 };
 
 static struct sde_prop_type vig_prop[] = {
@@ -910,6 +912,9 @@
 		sblk->pixel_ram_size = DEFAULT_PIXEL_RAM_SIZE;
 		sblk->src_blk.len = PROP_VALUE_ACCESS(prop_value, SSPP_SIZE, 0);
 
+		if (PROP_VALUE_ACCESS(prop_value, SSPP_EXCL_RECT, i) == 1)
+			set_bit(SDE_SSPP_EXCL_RECT, &sspp->features);
+
 		for (j = 0; j < sde_cfg->mdp_count; j++) {
 			sde_cfg->mdp[j].clk_ctrls[sspp->clk_ctrl].reg_off =
 				PROP_BITVALUE_ACCESS(prop_value,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index 7282f75..b9587de 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -92,6 +92,7 @@
  * @SDE_SSPP_PCC,            Color correction support
  * @SDE_SSPP_CURSOR,         SSPP can be used as a cursor layer
  * @SDE_SSPP_QOS,            SSPP support QoS control, danger/safe/creq
+ * @SDE_SSPP_EXCL_RECT,      SSPP supports exclusion rect
  * @SDE_SSPP_MAX             maximum value
  */
 enum {
@@ -107,6 +108,7 @@
 	SDE_SSPP_PCC,
 	SDE_SSPP_CURSOR,
 	SDE_SSPP_QOS,
+	SDE_SSPP_EXCL_RECT,
 	SDE_SSPP_MAX
 };
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
index 929c59a..3ebe6cd 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
@@ -47,6 +47,7 @@
 #define MDSS_MDP_OP_BWC_Q_MED              (2 << 1)
 
 #define SSPP_SRC_CONSTANT_COLOR            0x3c
+#define SSPP_EXCL_REC_CTL                  0x40
 #define SSPP_FETCH_CONFIG                  0x048
 #define SSPP_DANGER_LUT                    0x60
 #define SSPP_SAFE_LUT                      0x64
@@ -64,6 +65,8 @@
 #define SSPP_SW_PIX_EXT_C3_TB              0x124
 #define SSPP_SW_PIX_EXT_C3_REQ_PIXELS      0x128
 #define SSPP_UBWC_ERROR_STATUS             0x138
+#define SSPP_EXCL_REC_SIZE                 0x1B4
+#define SSPP_EXCL_REC_XY                   0x1B8
 #define SSPP_VIG_OP_MODE                   0x0
 #define SSPP_VIG_CSC_10_OP_MODE            0x0
 
@@ -737,6 +740,35 @@
 	SDE_REG_WRITE(c, SSPP_DECIMATION_CONFIG + idx, decimation);
 }
 
+/**
+ * _sde_hw_sspp_setup_excl_rect() - set exclusion rect configs
+ * @ctx: Pointer to pipe context
+ * @excl_rect: Exclusion rect configs
+ */
+static void _sde_hw_sspp_setup_excl_rect(struct sde_hw_pipe *ctx,
+		struct sde_rect *excl_rect)
+{
+	struct sde_hw_blk_reg_map *c;
+	u32 size, xy;
+	u32 idx;
+
+	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx) || !excl_rect)
+		return;
+
+	c = &ctx->hw;
+
+	xy = (excl_rect->y << 16) | (excl_rect->x);
+	size = (excl_rect->h << 16) | (excl_rect->w);
+
+	if (!size) {
+		SDE_REG_WRITE(c, SSPP_EXCL_REC_CTL + idx, 0);
+	} else {
+		SDE_REG_WRITE(c, SSPP_EXCL_REC_CTL + idx, BIT(0));
+		SDE_REG_WRITE(c, SSPP_EXCL_REC_SIZE + idx, size);
+		SDE_REG_WRITE(c, SSPP_EXCL_REC_XY + idx, xy);
+	}
+}
+
 static void sde_hw_sspp_setup_sourceaddress(struct sde_hw_pipe *ctx,
 		struct sde_hw_pipe_cfg *cfg)
 {
@@ -850,6 +882,10 @@
 		c->ops.setup_sourceaddress = sde_hw_sspp_setup_sourceaddress;
 		c->ops.setup_solidfill = sde_hw_sspp_setup_solidfill;
 	}
+
+	if (test_bit(SDE_SSPP_EXCL_RECT, &features))
+		c->ops.setup_excl_rect = _sde_hw_sspp_setup_excl_rect;
+
 	if (test_bit(SDE_SSPP_QOS, &features)) {
 		c->ops.setup_danger_safe_lut =
 			sde_hw_sspp_setup_danger_safe_lut;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
index 743f5e7..08dc5d3 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -297,6 +297,14 @@
 			void *scale_cfg);
 
 	/**
+	 * setup_excl_rect - setup pipe exclusion rectangle
+	 * @ctx: Pointer to pipe context
+	 * @excl_rect: Pointer to exclclusion rect structure
+	 */
+	void (*setup_excl_rect)(struct sde_hw_pipe *ctx,
+			struct sde_rect *excl_rect);
+
+	/**
 	 * setup_sourceaddress - setup pipe source addresses
 	 * @ctx: Pointer to pipe context
 	 * @cfg: Pointer to pipe config structure
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index e2ed7d2..558c47a 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -360,6 +360,15 @@
 void sde_kms_info_stop(struct sde_kms_info *info);
 
 /**
+ * sde_kms_rect_intersect() - find the intersecting region between two rects
+ * @res: Intersecting region between the two rectangles
+ * @rect1: first rectangle coordinates
+ * @rect2: second rectangle coordinates
+ */
+void sde_kms_rect_intersect(struct sde_rect *res,
+		const struct sde_rect *rect1, const struct sde_rect *rect2);
+
+/**
  * Vblank enable/disable functions
  */
 int sde_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc);
diff --git a/drivers/gpu/drm/msm/sde/sde_kms_utils.c b/drivers/gpu/drm/msm/sde/sde_kms_utils.c
index 6e29c09..f95f5df 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms_utils.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms_utils.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -151,3 +151,19 @@
 			info->len = info->staged_len + len;
 	}
 }
+
+void sde_kms_rect_intersect(struct sde_rect *res,
+		const struct sde_rect *rect1, const struct sde_rect *rect2)
+{
+	int l, t, r, b;
+
+	l = max(rect1->x, rect2->x);
+	t = max(rect1->y, rect2->y);
+	r = min((rect1->x + rect1->w), (rect2->x + rect2->w));
+	b = min((rect1->y + rect1->h), (rect2->y + rect2->h));
+
+	if (r < l || b < t)
+		*res = (struct sde_rect) {0, 0, 0, 0};
+	else
+		*res = (struct sde_rect) {l, t, (r - l), (b - t)};
+}
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;
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.h b/drivers/gpu/drm/msm/sde/sde_plane.h
index 1514f63..bbf6af6 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.h
+++ b/drivers/gpu/drm/msm/sde/sde_plane.h
@@ -31,6 +31,7 @@
  * @property_blobs:	blob properties
  * @input_fence:	dereferenced input fence pointer
  * @stage:	assigned by crtc blender
+ * @excl_rect:	exclusion rect values
  * @dirty:	bitmask for which pipe h/w config functions need to be updated
  * @pending:	whether the current update is still pending
  */
@@ -40,6 +41,7 @@
 	struct drm_property_blob *property_blobs[PLANE_PROP_BLOBCOUNT];
 	void *input_fence;
 	enum sde_stage stage;
+	struct sde_rect excl_rect;
 	uint32_t dirty;
 	bool pending;
 };