drm/msm/sde: add support for smart DMA 2.0

SmartDMA allows fetching multiple surfaces in a single source pipe.
SmartDMA version 2.0 can fetch up to 2 surfaces in a single pipe for
2 destination rectangles. This change adds support for this feature
in SDE DRM. Each destination rectangle path of a smartDMA pipe is
enumerated as a separate DRM plane with necessary property to
indicate the pair. Clients need to identify the pair and program the
source surfaces as per the HW limitations of the version supported.
SmartDMA will be enabled only on requesting SDE customization by
kernel-clients.

Change-Id: Ic0a0496cb785d6212e375cc7e0fc55407f7d8a0b
Signed-off-by: Jeykumar Sankaran <jsanka@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 06e9b53..499dfcc 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -64,7 +64,7 @@
 
 #define NUM_DOMAINS    4    /* one for KMS, then one per gpu core (?) */
 #define MAX_CRTCS      8
-#define MAX_PLANES     12
+#define MAX_PLANES     20
 #define MAX_ENCODERS   8
 #define MAX_BRIDGES    8
 #define MAX_CONNECTORS 8
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index b0bde45..9832ca5 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -270,6 +270,9 @@
 
 		stage_cfg->stage[lm_idx][pstate->stage][idx] =
 							sde_plane_pipe(plane);
+		stage_cfg->multirect_index
+				[lm_idx][pstate->stage][idx] =
+				pstate->multirect_index;
 		mixer[lm_idx].flush_mask |= flush_mask;
 
 		SDE_DEBUG("crtc %d stage:%d - plane %d sspp %d fb %d\n",
@@ -300,6 +303,9 @@
 			idx = right_crtc_zpos_cnt[pstate->stage]++;
 			stage_cfg->stage[RIGHT_MIXER][pstate->stage][idx] =
 							sde_plane_pipe(plane);
+			stage_cfg->multirect_index
+				[RIGHT_MIXER][pstate->stage][idx] =
+				pstate->multirect_index;
 			mixer[RIGHT_MIXER].flush_mask |= flush_mask;
 
 			/* blend config update */
@@ -1156,8 +1162,8 @@
 struct plane_state {
 	struct sde_plane_state *sde_pstate;
 	const struct drm_plane_state *drm_pstate;
-
 	int stage;
+	u32 pipe_id;
 };
 
 static int pstate_cmp(const void *a, const void *b)
@@ -1182,7 +1188,7 @@
 		struct drm_crtc_state *state)
 {
 	struct sde_crtc *sde_crtc;
-	struct plane_state pstates[SDE_STAGE_MAX * 2];
+	struct plane_state pstates[SDE_STAGE_MAX * 4];
 	struct sde_crtc_state *cstate;
 
 	const struct drm_plane_state *pstate;
@@ -1190,8 +1196,12 @@
 	struct drm_display_mode *mode;
 
 	int cnt = 0, rc = 0, mixer_width, i, z_pos;
+
 	int left_crtc_zpos_cnt[SDE_STAGE_MAX] = {0};
 	int right_crtc_zpos_cnt[SDE_STAGE_MAX] = {0};
+	struct sde_multirect_plane_states multirect_plane[SDE_STAGE_MAX * 2];
+	int multirect_count = 0;
+	const struct drm_plane_state *pipe_staged[SSPP_MAX];
 
 	if (!crtc) {
 		SDE_ERROR("invalid crtc\n");
@@ -1209,6 +1219,8 @@
 	mode = &state->adjusted_mode;
 	SDE_DEBUG("%s: check", sde_crtc->name);
 
+	memset(pipe_staged, 0, sizeof(pipe_staged));
+
 	mixer_width = sde_crtc_mixer_width(sde_crtc, mode);
 
 	 /* get plane state for all drm planes associated with crtc state */
@@ -1226,6 +1238,7 @@
 		pstates[cnt].drm_pstate = pstate;
 		pstates[cnt].stage = sde_plane_get_property(
 				pstates[cnt].sde_pstate, PLANE_PROP_ZPOS);
+		pstates[cnt].pipe_id = sde_plane_pipe(plane);
 
 		/* check dim layer stage with every plane */
 		for (i = 0; i < cstate->num_dim_layers; i++) {
@@ -1238,6 +1251,17 @@
 			}
 		}
 
+		if (pipe_staged[pstates[cnt].pipe_id]) {
+			multirect_plane[multirect_count].r0 =
+				pipe_staged[pstates[cnt].pipe_id];
+			multirect_plane[multirect_count].r1 = pstate;
+			multirect_count++;
+
+			pipe_staged[pstates[cnt].pipe_id] = NULL;
+		} else {
+			pipe_staged[pstates[cnt].pipe_id] = pstate;
+		}
+
 		cnt++;
 
 		if (CHECK_LAYER_BOUNDS(pstate->crtc_y, pstate->crtc_h,
@@ -1253,6 +1277,15 @@
 		}
 	}
 
+	for (i = 1; i < SSPP_MAX; i++) {
+		if (pipe_staged[i] &&
+			is_sde_plane_virtual(pipe_staged[i]->plane)) {
+			SDE_ERROR("invalid use of virtual plane: %d\n",
+					pipe_staged[i]->plane->base.id);
+			goto end;
+		}
+	}
+
 	/* Check dim layer rect bounds and stage */
 	for (i = 0; i < cstate->num_dim_layers; i++) {
 		if ((CHECK_LAYER_BOUNDS(cstate->dim_layer[i].rect.y,
@@ -1300,7 +1333,7 @@
 			goto end;
 		} else if (pstates[i].drm_pstate->crtc_x < mixer_width) {
 			if (left_crtc_zpos_cnt[z_pos] == 2) {
-				SDE_ERROR("> 2 plane @ stage%d on left\n",
+				SDE_ERROR("> 2 planes @ stage %d on left\n",
 					z_pos);
 				rc = -EINVAL;
 				goto end;
@@ -1308,7 +1341,7 @@
 			left_crtc_zpos_cnt[z_pos]++;
 		} else {
 			if (right_crtc_zpos_cnt[z_pos] == 2) {
-				SDE_ERROR("> 2 plane @ stage%d on right\n",
+				SDE_ERROR("> 2 planes @ stage %d on right\n",
 					z_pos);
 				rc = -EINVAL;
 				goto end;
@@ -1319,6 +1352,17 @@
 		SDE_DEBUG("%s: zpos %d", sde_crtc->name, z_pos);
 	}
 
+	for (i = 0; i < multirect_count; i++) {
+		if (sde_plane_validate_multirect_v2(&multirect_plane[i])) {
+			SDE_ERROR(
+			"multirect validation failed for planes (%d - %d)\n",
+					multirect_plane[i].r0->plane->base.id,
+					multirect_plane[i].r1->plane->base.id);
+			rc = -EINVAL;
+			break;
+		}
+	}
+
 end:
 	return rc;
 }
@@ -1427,6 +1471,16 @@
 		sde_kms_info_add_keystr(info, "qseed_type", "qseed2");
 	if (catalog->qseed_type == SDE_SSPP_SCALER_QSEED3)
 		sde_kms_info_add_keystr(info, "qseed_type", "qseed3");
+
+	if (sde_is_custom_client()) {
+		if (catalog->smart_dma_rev == SDE_SSPP_SMART_DMA_V1)
+			sde_kms_info_add_keystr(info,
+					"smart_dma_rev", "smart_dma_v1");
+		if (catalog->smart_dma_rev == SDE_SSPP_SMART_DMA_V2)
+			sde_kms_info_add_keystr(info,
+					"smart_dma_rev", "smart_dma_v2");
+	}
+
 	sde_kms_info_add_keyint(info, "has_src_split", catalog->has_src_split);
 	msm_property_set_blob(&sde_crtc->property_info, &sde_crtc->blob_info,
 			info->data, info->len, CRTC_PROP_INFO);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 533147c..dd39cc7 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -46,9 +46,6 @@
 /* default hardware block size if dtsi entry is not present */
 #define DEFAULT_SDE_HW_BLOCK_LEN 0x100
 
-/* default rects for multi rect case */
-#define DEFAULT_SDE_SSPP_MAX_RECTS 1
-
 /* total number of intf - dp, dsi, hdmi */
 #define INTF_COUNT			3
 
@@ -106,6 +103,7 @@
 	CDP,
 	SRC_SPLIT,
 	DIM_LAYER,
+	SMART_DMA_REV,
 	SDE_PROP_MAX,
 };
 
@@ -118,11 +116,11 @@
 	SSPP_CLK_STATUS,
 	SSPP_DANGER,
 	SSPP_SAFE,
-	SSPP_MAX_RECTS,
 	SSPP_SCALE_SIZE,
 	SSPP_VIG_BLOCKS,
 	SSPP_RGB_BLOCKS,
 	SSPP_EXCL_RECT,
+	SSPP_SMART_DMA,
 	SSPP_PROP_MAX,
 };
 
@@ -283,6 +281,7 @@
 	{CDP, "qcom,sde-has-cdp", false, PROP_TYPE_BOOL},
 	{SRC_SPLIT, "qcom,sde-has-src-split", false, PROP_TYPE_BOOL},
 	{DIM_LAYER, "qcom,sde-has-dim-layer", false, PROP_TYPE_BOOL},
+	{SMART_DMA_REV, "qcom,sde-smart-dma-rev", false, PROP_TYPE_STRING},
 };
 
 static struct sde_prop_type sspp_prop[] = {
@@ -296,11 +295,12 @@
 		PROP_TYPE_BIT_OFFSET_ARRAY},
 	{SSPP_DANGER, "qcom,sde-sspp-danger-lut", false, PROP_TYPE_U32_ARRAY},
 	{SSPP_SAFE, "qcom,sde-sspp-safe-lut", false, PROP_TYPE_U32_ARRAY},
-	{SSPP_MAX_RECTS, "qcom,sde-sspp-max-rects", false, PROP_TYPE_U32_ARRAY},
 	{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},
+	{SSPP_SMART_DMA, "qcom,sde-sspp-smart-dma-priority", false,
+		PROP_TYPE_U32_ARRAY},
 };
 
 static struct sde_prop_type vig_prop[] = {
@@ -900,6 +900,13 @@
 		sblk->maxlinewidth = sde_cfg->max_sspp_linewidth;
 
 		set_bit(SDE_SSPP_SRC, &sspp->features);
+
+		sblk->smart_dma_priority =
+			PROP_VALUE_ACCESS(prop_value, SSPP_SMART_DMA, i);
+
+		if (sblk->smart_dma_priority && sde_cfg->smart_dma_rev)
+			set_bit(sde_cfg->smart_dma_rev, &sspp->features);
+
 		sblk->src_blk.id = SDE_SSPP_SRC;
 
 		of_property_read_string_index(np,
@@ -1815,7 +1822,7 @@
 
 static int sde_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg)
 {
-	int rc, len, prop_count[SDE_PROP_MAX];
+	int rc, dma_rc, len, prop_count[SDE_PROP_MAX];
 	struct sde_prop_value *prop_value = NULL;
 	bool prop_exists[SDE_PROP_MAX];
 	const char *type;
@@ -1890,6 +1897,20 @@
 	else if (!rc && !strcmp(type, "csc-10bit"))
 		cfg->csc_type = SDE_SSPP_CSC_10BIT;
 
+	/*
+	 * Current SDE support only Smart DMA 2.0.
+	 * No support for Smart DMA 1.0 yet.
+	 */
+	cfg->smart_dma_rev = 0;
+	dma_rc = of_property_read_string(np, sde_prop[SMART_DMA_REV].prop_name,
+			&type);
+	if (!dma_rc && !strcmp(type, "smart_dma_v2")) {
+		cfg->smart_dma_rev = SDE_SSPP_SMART_DMA_V2;
+	} else if (!dma_rc && !strcmp(type, "smart_dma_v1")) {
+		SDE_ERROR("smart dma 1.0 is not supported in SDE\n");
+		cfg->smart_dma_rev = 0;
+	}
+
 	cfg->has_src_split = PROP_VALUE_ACCESS(prop_value, SRC_SPLIT, 0);
 	cfg->has_dim_layer = PROP_VALUE_ACCESS(prop_value, DIM_LAYER, 0);
 end:
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index ef69ae0..5e35e4e 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -93,6 +93,8 @@
  * @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_SMART_DMA_V1,   SmartDMA 1.0 support
+ * @SDE_SSPP_SMART_DMA_V2,   SmartDMA 2.0 support
  * @SDE_SSPP_MAX             maximum value
  */
 enum {
@@ -109,6 +111,8 @@
 	SDE_SSPP_CURSOR,
 	SDE_SSPP_QOS,
 	SDE_SSPP_EXCL_RECT,
+	SDE_SSPP_SMART_DMA_V1,
+	SDE_SSPP_SMART_DMA_V2,
 	SDE_SSPP_MAX
 };
 
@@ -317,6 +321,7 @@
  * @creq_vblank: creq priority during vertical blanking
  * @danger_vblank: danger priority during vertical blanking
  * @pixel_ram_size: size of latency hiding and de-tiling buffer in bytes
+ * @smart_dma_priority: hw priority of rect1 of multirect pipe
  * @src_blk:
  * @scaler_blk:
  * @csc_blk:
@@ -342,6 +347,7 @@
 	u32 maxupscale;
 	u32 maxhdeciexp; /* max decimation is 2^value */
 	u32 maxvdeciexp; /* max decimation is 2^value */
+	u32 smart_dma_priority;
 	struct sde_src_blk src_blk;
 	struct sde_scaler_blk scaler_blk;
 	struct sde_pp_blk csc_blk;
@@ -627,6 +633,7 @@
  * @highest_bank_bit   highest memory bit setting for tile buffers.
  * @qseed_type         qseed2 or qseed3 support.
  * @csc_type           csc or csc_10bit support.
+ * @smart_dma_rev      Supported version of SmartDMA feature.
  * @has_src_split      source split feature status
  * @has_cdp            Client driver prefetch feature status
  */
@@ -640,6 +647,7 @@
 	u32 highest_bank_bit;
 	u32 qseed_type;
 	u32 csc_type;
+	u32 smart_dma_rev;
 	bool has_src_split;
 	bool has_cdp;
 	bool has_dim_layer;
@@ -721,4 +729,13 @@
  */
 void sde_hw_catalog_deinit(struct sde_mdss_cfg *sde_cfg);
 
+/**
+ * sde_hw_sspp_multirect_enabled - check multirect enabled for the sspp
+ * @cfg:          pointer to sspp cfg
+ */
+static inline bool sde_hw_sspp_multirect_enabled(const struct sde_sspp_cfg *cfg)
+{
+	return test_bit(SDE_SSPP_SMART_DMA_V1, &cfg->features) ||
+			 test_bit(SDE_SSPP_SMART_DMA_V2, &cfg->features);
+}
 #endif /* _SDE_HW_CATALOG_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
index 56d9f2a..19e3a7a 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
@@ -20,6 +20,8 @@
 	(0x40 + (((lm) - LM_0) * 0x004))
 #define   CTL_LAYER_EXT2(lm)             \
 	(0x70 + (((lm) - LM_0) * 0x004))
+#define   CTL_LAYER_EXT3(lm)             \
+	(0xA0 + (((lm) - LM_0) * 0x004))
 #define   CTL_TOP                       0x014
 #define   CTL_FLUSH                     0x018
 #define   CTL_START                     0x01C
@@ -281,7 +283,8 @@
 	enum sde_lm lm, struct sde_hw_stage_cfg *stage_cfg, u32 index)
 {
 	struct sde_hw_blk_reg_map *c = &ctx->hw;
-	u32 mixercfg, mixercfg_ext, mix, ext, mixercfg_ext2;
+	u32 mixercfg = 0, mixercfg_ext = 0, mix, ext;
+	u32 mixercfg_ext2 = 0, mixercfg_ext3 = 0;
 	int i, j;
 	u8 stages;
 	int pipes_per_stage;
@@ -300,8 +303,6 @@
 		pipes_per_stage = 1;
 
 	mixercfg = BIT(24); /* always set BORDER_OUT */
-	mixercfg_ext = 0;
-	mixercfg_ext2 = 0;
 
 	for (i = 0; i <= stages; i++) {
 		/* overflow to ext register if 'i + 1 > 7' */
@@ -309,22 +310,41 @@
 		ext = i >= 7;
 
 		for (j = 0 ; j < pipes_per_stage; j++) {
+			enum sde_sspp_multirect_index rect_index =
+				stage_cfg->multirect_index[index][i][j];
+
 			switch (stage_cfg->stage[index][i][j]) {
 			case SSPP_VIG0:
-				mixercfg |= mix << 0;
-				mixercfg_ext |= ext << 0;
+				if (rect_index == SDE_SSPP_RECT_1) {
+					mixercfg_ext3 |= ((i + 1) & 0xF) << 0;
+				} else {
+					mixercfg |= mix << 0;
+					mixercfg_ext |= ext << 0;
+				}
 				break;
 			case SSPP_VIG1:
-				mixercfg |= mix << 3;
-				mixercfg_ext |= ext << 2;
+				if (rect_index == SDE_SSPP_RECT_1) {
+					mixercfg_ext3 |= ((i + 1) & 0xF) << 4;
+				} else {
+					mixercfg |= mix << 3;
+					mixercfg_ext |= ext << 2;
+				}
 				break;
 			case SSPP_VIG2:
-				mixercfg |= mix << 6;
-				mixercfg_ext |= ext << 4;
+				if (rect_index == SDE_SSPP_RECT_1) {
+					mixercfg_ext3 |= ((i + 1) & 0xF) << 8;
+				} else {
+					mixercfg |= mix << 6;
+					mixercfg_ext |= ext << 4;
+				}
 				break;
 			case SSPP_VIG3:
-				mixercfg |= mix << 26;
-				mixercfg_ext |= ext << 6;
+				if (rect_index == SDE_SSPP_RECT_1) {
+					mixercfg_ext3 |= ((i + 1) & 0xF) << 12;
+				} else {
+					mixercfg |= mix << 26;
+					mixercfg_ext |= ext << 6;
+				}
 				break;
 			case SSPP_RGB0:
 				mixercfg |= mix << 9;
@@ -343,20 +363,36 @@
 				mixercfg_ext |= ext << 14;
 				break;
 			case SSPP_DMA0:
-				mixercfg |= mix << 18;
-				mixercfg_ext |= ext << 16;
+				if (rect_index == SDE_SSPP_RECT_1) {
+					mixercfg_ext2 |= ((i + 1) & 0xF) << 8;
+				} else {
+					mixercfg |= mix << 18;
+					mixercfg_ext |= ext << 16;
+				}
 				break;
 			case SSPP_DMA1:
-				mixercfg |= mix << 21;
-				mixercfg_ext |= ext << 18;
+				if (rect_index == SDE_SSPP_RECT_1) {
+					mixercfg_ext2 |= ((i + 1) & 0xF) << 12;
+				} else {
+					mixercfg |= mix << 21;
+					mixercfg_ext |= ext << 18;
+				}
 				break;
 			case SSPP_DMA2:
-				mix = (i + 1) & 0xf;
-				mixercfg_ext2 |= mix << 0;
+				if (rect_index == SDE_SSPP_RECT_1) {
+					mixercfg_ext2 |= ((i + 1) & 0xF) << 16;
+				} else {
+					mix |= (i + 1) & 0xF;
+					mixercfg_ext2 |= mix << 0;
+				}
 				break;
 			case SSPP_DMA3:
-				mix = (i + 1) & 0xf;
-				mixercfg_ext2 |= mix << 4;
+				if (rect_index == SDE_SSPP_RECT_1) {
+					mixercfg_ext2 |= ((i + 1) & 0xF) << 20;
+				} else {
+					mix |= (i + 1) & 0xF;
+					mixercfg_ext2 |= mix << 4;
+				}
 				break;
 			case SSPP_CURSOR0:
 				mixercfg_ext |= ((i + 1) & 0xF) << 20;
@@ -373,6 +409,7 @@
 	SDE_REG_WRITE(c, CTL_LAYER(lm), mixercfg);
 	SDE_REG_WRITE(c, CTL_LAYER_EXT(lm), mixercfg_ext);
 	SDE_REG_WRITE(c, CTL_LAYER_EXT2(lm), mixercfg_ext2);
+	SDE_REG_WRITE(c, CTL_LAYER_EXT3(lm), mixercfg_ext3);
 }
 
 static void sde_hw_ctl_intf_cfg(struct sde_hw_ctl *ctx,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
index 2fb7b37..670a03d 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.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
@@ -16,6 +16,7 @@
 #include "sde_hw_mdss.h"
 #include "sde_hw_util.h"
 #include "sde_hw_catalog.h"
+#include "sde_hw_sspp.h"
 
 /**
  * sde_ctl_mode_sel: Interface mode selection
@@ -30,10 +31,13 @@
 struct sde_hw_ctl;
 /**
  * struct sde_hw_stage_cfg - blending stage cfg
- * @stage
+ * @stage : SSPP_ID at each stage
+ * @multirect_index: index of the rectangle of SSPP.
  */
 struct sde_hw_stage_cfg {
 	enum sde_sspp stage[CRTC_DUAL_MIXERS][SDE_STAGE_MAX][PIPES_PER_STAGE];
+	enum sde_sspp_multirect_index multirect_index[CRTC_DUAL_MIXERS]
+					[SDE_STAGE_MAX][PIPES_PER_STAGE];
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
index 88dc98f..1b98683 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
@@ -32,8 +32,21 @@
 #define SSPP_SRC_FORMAT                    0x30
 #define SSPP_SRC_UNPACK_PATTERN            0x34
 #define SSPP_SRC_OP_MODE                   0x38
-#define MDSS_MDP_OP_DEINTERLACE            BIT(22)
 
+/* SSPP_MULTIRECT*/
+#define SSPP_SRC_SIZE_REC1                 0x16C
+#define SSPP_SRC_XY_REC1                   0x168
+#define SSPP_OUT_SIZE_REC1                 0x160
+#define SSPP_OUT_XY_REC1                   0x164
+#define SSPP_SRC_FORMAT_REC1               0x174
+#define SSPP_SRC_UNPACK_PATTERN_REC1       0x178
+#define SSPP_SRC_OP_MODE_REC1              0x17C
+#define SSPP_MULTIRECT_OPMODE              0x170
+#define SSPP_SRC_CONSTANT_COLOR_REC1       0x180
+#define SSPP_EXCL_REC_SIZE_REC1            0x184
+#define SSPP_EXCL_REC_XY_REC1              0x188
+
+#define MDSS_MDP_OP_DEINTERLACE            BIT(22)
 #define MDSS_MDP_OP_DEINTERLACE_ODD        BIT(23)
 #define MDSS_MDP_OP_IGC_ROM_1              BIT(18)
 #define MDSS_MDP_OP_IGC_ROM_0              BIT(17)
@@ -205,6 +218,32 @@
 	return rc;
 }
 
+static void sde_hw_sspp_setup_multirect(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index,
+		enum sde_sspp_multirect_mode mode)
+{
+	u32 mode_mask;
+	u32 idx;
+
+	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx))
+		return;
+
+	if (index == SDE_SSPP_RECT_SOLO) {
+		/**
+		 * if rect index is RECT_SOLO, we cannot expect a
+		 * virtual plane sharing the same SSPP id. So we go
+		 * and disable multirect
+		 */
+		mode_mask = 0;
+	} else {
+		mode_mask = SDE_REG_READ(&ctx->hw, SSPP_MULTIRECT_OPMODE + idx);
+		mode_mask |= index;
+		mode_mask |= (mode == SDE_SSPP_MULTIRECT_TIME_MX) ? 0x4 : 0x0;
+	}
+
+	SDE_REG_WRITE(&ctx->hw, SSPP_MULTIRECT_OPMODE + idx, mode_mask);
+}
+
 static void _sspp_setup_opmode(struct sde_hw_pipe *ctx,
 		u32 mask, u8 en)
 {
@@ -248,24 +287,44 @@
  * Setup source pixel format, flip,
  */
 static void sde_hw_sspp_setup_format(struct sde_hw_pipe *ctx,
-		const struct sde_format *fmt, u32 flags)
+		const struct sde_format *fmt, u32 flags,
+		enum sde_sspp_multirect_index rect_mode)
 {
 	struct sde_hw_blk_reg_map *c;
 	u32 chroma_samp, unpack, src_format;
 	u32 secure = 0;
 	u32 opmode = 0;
+	u32 op_mode_off, unpack_pat_off, format_off;
 	u32 idx;
 
 	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx) || !fmt)
 		return;
 
+	if (rect_mode == SDE_SSPP_RECT_SOLO || rect_mode == SDE_SSPP_RECT_0) {
+		op_mode_off = SSPP_SRC_OP_MODE;
+		unpack_pat_off = SSPP_SRC_UNPACK_PATTERN;
+		format_off = SSPP_SRC_FORMAT;
+	} else {
+		op_mode_off = SSPP_SRC_OP_MODE_REC1;
+		unpack_pat_off = SSPP_SRC_UNPACK_PATTERN_REC1;
+		format_off = SSPP_SRC_FORMAT_REC1;
+	}
+
 	c = &ctx->hw;
-	opmode = SDE_REG_READ(c, SSPP_SRC_OP_MODE + idx);
+	opmode = SDE_REG_READ(c, op_mode_off + idx);
 	opmode &= ~(MDSS_MDP_OP_FLIP_LR | MDSS_MDP_OP_FLIP_UD |
 			MDSS_MDP_OP_BWC_EN | MDSS_MDP_OP_PE_OVERRIDE);
 
-	if (flags & SDE_SSPP_SECURE_OVERLAY_SESSION)
-		secure = 0xF;
+	if (flags & SDE_SSPP_SECURE_OVERLAY_SESSION) {
+		secure = SDE_REG_READ(c, SSPP_SRC_ADDR_SW_STATUS + idx);
+
+		if (rect_mode == SDE_SSPP_RECT_SOLO)
+			secure |= 0xF;
+		else if (rect_mode == SDE_SSPP_RECT_0)
+			secure |= 0x5;
+		else if (rect_mode == SDE_SSPP_RECT_1)
+			secure |= 0xA;
+	}
 
 	if (flags & SDE_SSPP_FLIP_LR)
 		opmode |= MDSS_MDP_OP_FLIP_LR;
@@ -327,9 +386,9 @@
 			VIG_CSC_10_EN | VIG_CSC_10_SRC_DATAFMT,
 			SDE_FORMAT_IS_YUV(fmt));
 
-	SDE_REG_WRITE(c, SSPP_SRC_FORMAT + idx, src_format);
-	SDE_REG_WRITE(c, SSPP_SRC_UNPACK_PATTERN + idx, unpack);
-	SDE_REG_WRITE(c, SSPP_SRC_OP_MODE + idx, opmode);
+	SDE_REG_WRITE(c, format_off + idx, src_format);
+	SDE_REG_WRITE(c, unpack_pat_off + idx, unpack);
+	SDE_REG_WRITE(c, op_mode_off + idx, opmode);
 	SDE_REG_WRITE(c, SSPP_SRC_ADDR_SW_STATUS + idx, secure);
 
 	/* clear previous UBWC error */
@@ -692,10 +751,12 @@
 static void sde_hw_sspp_setup_rects(struct sde_hw_pipe *ctx,
 		struct sde_hw_pipe_cfg *cfg,
 		struct sde_hw_pixel_ext *pe_ext,
+		enum sde_sspp_multirect_index rect_index,
 		void *scale_cfg)
 {
 	struct sde_hw_blk_reg_map *c;
 	u32 src_size, src_xy, dst_size, dst_xy, ystride0, ystride1;
+	u32 src_size_off, src_xy_off, out_size_off, out_xy_off;
 	u32 decimation = 0;
 	u32 idx;
 
@@ -704,6 +765,18 @@
 
 	c = &ctx->hw;
 
+	if (rect_index == SDE_SSPP_RECT_SOLO || rect_index == SDE_SSPP_RECT_0) {
+		src_size_off = SSPP_SRC_SIZE;
+		src_xy_off = SSPP_SRC_XY;
+		out_size_off = SSPP_OUT_SIZE;
+		out_xy_off = SSPP_OUT_XY;
+	} else {
+		src_size_off = SSPP_SRC_SIZE_REC1;
+		src_xy_off = SSPP_SRC_XY_REC1;
+		out_size_off = SSPP_OUT_SIZE_REC1;
+		out_xy_off = SSPP_OUT_XY_REC1;
+	}
+
 	/* program pixel extension override */
 	if (pe_ext)
 		sde_hw_sspp_setup_pe_config(ctx, pe_ext);
@@ -714,10 +787,23 @@
 	dst_xy = (cfg->dst_rect.y << 16) | (cfg->dst_rect.x);
 	dst_size = (cfg->dst_rect.h << 16) | (cfg->dst_rect.w);
 
-	ystride0 = (cfg->layout.plane_pitch[0]) |
+	if (rect_index == SDE_SSPP_RECT_SOLO) {
+		ystride0 = (cfg->layout.plane_pitch[0]) |
 			(cfg->layout.plane_pitch[1] << 16);
-	ystride1 = (cfg->layout.plane_pitch[2]) |
+		ystride1 = (cfg->layout.plane_pitch[2]) |
 			(cfg->layout.plane_pitch[3] << 16);
+	} else {
+		ystride0 = SDE_REG_READ(c, SSPP_SRC_YSTRIDE0 + idx);
+		ystride1 = SDE_REG_READ(c, SSPP_SRC_YSTRIDE1 + idx);
+
+		if (rect_index == SDE_SSPP_RECT_0) {
+			ystride0 |= cfg->layout.plane_pitch[0];
+			ystride1 |= cfg->layout.plane_pitch[2];
+		}  else {
+			ystride0 |= cfg->layout.plane_pitch[0] << 16;
+			ystride1 |= cfg->layout.plane_pitch[2] << 16;
+		}
+	}
 
 	/* program scaler, phase registers, if pipes supporting scaling */
 	if (ctx->cap->features & SDE_SSPP_SCALER) {
@@ -728,10 +814,10 @@
 	}
 
 	/* rectangle register programming */
-	SDE_REG_WRITE(c, SSPP_SRC_SIZE + idx, src_size);
-	SDE_REG_WRITE(c, SSPP_SRC_XY + idx, src_xy);
-	SDE_REG_WRITE(c, SSPP_OUT_SIZE + idx, dst_size);
-	SDE_REG_WRITE(c, SSPP_OUT_XY + idx, dst_xy);
+	SDE_REG_WRITE(c, src_size_off + idx, src_size);
+	SDE_REG_WRITE(c, src_xy_off + idx, src_xy);
+	SDE_REG_WRITE(c, out_size_off + idx, dst_size);
+	SDE_REG_WRITE(c, out_xy_off + idx, dst_xy);
 
 	SDE_REG_WRITE(c, SSPP_SRC_YSTRIDE0 + idx, ystride0);
 	SDE_REG_WRITE(c, SSPP_SRC_YSTRIDE1 + idx, ystride1);
@@ -744,31 +830,48 @@
  * @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_rect *excl_rect,
+		enum sde_sspp_multirect_index rect_index)
 {
 	struct sde_hw_blk_reg_map *c;
 	u32 size, xy;
 	u32 idx;
+	u32 reg_xy, reg_size;
+	u32 excl_ctrl, enable_bit;
 
 	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx) || !excl_rect)
 		return;
 
+	if (rect_index == SDE_SSPP_RECT_0 || rect_index == SDE_SSPP_RECT_SOLO) {
+		reg_xy = SSPP_EXCL_REC_XY;
+		reg_size = SSPP_EXCL_REC_SIZE;
+		enable_bit = BIT(0);
+	} else {
+		reg_xy = SSPP_EXCL_REC_XY_REC1;
+		reg_size = SSPP_EXCL_REC_SIZE_REC1;
+		enable_bit = BIT(1);
+	}
+
 	c = &ctx->hw;
 
 	xy = (excl_rect->y << 16) | (excl_rect->x);
 	size = (excl_rect->h << 16) | (excl_rect->w);
 
+	excl_ctrl = SDE_REG_READ(c, SSPP_EXCL_REC_CTL + idx);
 	if (!size) {
-		SDE_REG_WRITE(c, SSPP_EXCL_REC_CTL + idx, 0);
+		SDE_REG_WRITE(c, SSPP_EXCL_REC_CTL + idx,
+				excl_ctrl & ~enable_bit);
 	} 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);
+		SDE_REG_WRITE(c, SSPP_EXCL_REC_CTL + idx,
+				excl_ctrl | enable_bit);
+		SDE_REG_WRITE(c, reg_size + idx, size);
+		SDE_REG_WRITE(c, reg_xy + idx, xy);
 	}
 }
 
 static void sde_hw_sspp_setup_sourceaddress(struct sde_hw_pipe *ctx,
-		struct sde_hw_pipe_cfg *cfg)
+		struct sde_hw_pipe_cfg *cfg,
+		enum sde_sspp_multirect_index rect_mode)
 {
 	int i;
 	u32 idx;
@@ -776,9 +879,21 @@
 	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx))
 		return;
 
-	for (i = 0; i < ARRAY_SIZE(cfg->layout.plane_addr); i++)
-		SDE_REG_WRITE(&ctx->hw, SSPP_SRC0_ADDR + idx + i * 0x4,
-			cfg->layout.plane_addr[i]);
+	if (rect_mode == SDE_SSPP_RECT_SOLO) {
+		for (i = 0; i < ARRAY_SIZE(cfg->layout.plane_addr); i++)
+			SDE_REG_WRITE(&ctx->hw, SSPP_SRC0_ADDR + idx + i * 0x4,
+					cfg->layout.plane_addr[i]);
+	} else if (rect_mode == SDE_SSPP_RECT_0) {
+		SDE_REG_WRITE(&ctx->hw, SSPP_SRC0_ADDR + idx,
+				cfg->layout.plane_addr[0]);
+		SDE_REG_WRITE(&ctx->hw, SSPP_SRC2_ADDR + idx,
+				cfg->layout.plane_addr[2]);
+	} else {
+		SDE_REG_WRITE(&ctx->hw, SSPP_SRC1_ADDR + idx,
+				cfg->layout.plane_addr[0]);
+		SDE_REG_WRITE(&ctx->hw, SSPP_SRC3_ADDR + idx,
+				cfg->layout.plane_addr[2]);
+	}
 }
 
 static void sde_hw_sspp_setup_csc(struct sde_hw_pipe *ctx,
@@ -813,14 +928,19 @@
 	SDE_REG_WRITE(c, VIG_0_QSEED2_SHARP + idx + 0xC, cfg->noise_thr);
 }
 
-static void sde_hw_sspp_setup_solidfill(struct sde_hw_pipe *ctx, u32 color)
+static void sde_hw_sspp_setup_solidfill(struct sde_hw_pipe *ctx, u32 color, enum
+		sde_sspp_multirect_index rect_index)
 {
 	u32 idx;
 
 	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx))
 		return;
 
-	SDE_REG_WRITE(&ctx->hw, SSPP_SRC_CONSTANT_COLOR + idx, color);
+	if (rect_index == SDE_SSPP_RECT_SOLO || rect_index == SDE_SSPP_RECT_0)
+		SDE_REG_WRITE(&ctx->hw, SSPP_SRC_CONSTANT_COLOR + idx, color);
+	else
+		SDE_REG_WRITE(&ctx->hw, SSPP_SRC_CONSTANT_COLOR_REC1 + idx,
+				color);
 }
 
 static void sde_hw_sspp_setup_danger_safe_lut(struct sde_hw_pipe *ctx,
@@ -898,6 +1018,9 @@
 	if (test_bit(SDE_SSPP_SCALER_QSEED2, &features))
 		c->ops.setup_sharpening = sde_hw_sspp_setup_sharpening;
 
+	if (sde_hw_sspp_multirect_enabled(c->cap))
+		c->ops.setup_multirect = sde_hw_sspp_setup_multirect;
+
 	if (test_bit(SDE_SSPP_SCALER_QSEED3, &features))
 		c->ops.setup_scaler = _sde_hw_sspp_setup_scaler3;
 	else
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
index d7101fd..d81f673 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
@@ -50,6 +50,28 @@
 	SDE_SSPP_COMP_MAX
 };
 
+/**
+ * SDE_SSPP_RECT_SOLO - multirect disabled
+ * SDE_SSPP_RECT_0 - rect0 of a multirect pipe
+ * SDE_SSPP_RECT_1 - rect1 of a multirect pipe
+ *
+ * Note: HW supports multirect with either RECT0 or
+ * RECT1. Considering no benefit of such configs over
+ * SOLO mode and to keep the plane management simple,
+ * we dont support single rect multirect configs.
+ */
+enum sde_sspp_multirect_index {
+	SDE_SSPP_RECT_SOLO = 0,
+	SDE_SSPP_RECT_0,
+	SDE_SSPP_RECT_1,
+};
+
+enum sde_sspp_multirect_mode {
+	SDE_SSPP_MULTIRECT_NONE = 0,
+	SDE_SSPP_MULTIRECT_PARALLEL,
+	SDE_SSPP_MULTIRECT_TIME_MX,
+};
+
 enum {
 	SDE_FRAME_LINEAR,
 	SDE_FRAME_TILE_A4X,
@@ -252,6 +274,8 @@
  *              4: Read 1 line/pixel drop 3  lines/pixels
  *              8: Read 1 line/pixel drop 7 lines/pixels
  *              16: Read 1 line/pixel drop 15 line/pixels
+ * @index:     index of the rectangle of SSPP
+ * @mode:      parallel or time multiplex multirect mode
  */
 struct sde_hw_pipe_cfg {
 	struct sde_hw_fmt_layout layout;
@@ -259,6 +283,8 @@
 	struct sde_rect dst_rect;
 	u8 horz_decimation;
 	u8 vert_decimation;
+	enum sde_sspp_multirect_index index;
+	enum sde_sspp_multirect_mode mode;
 };
 
 /**
@@ -292,37 +318,45 @@
 	 * @ctx: Pointer to pipe context
 	 * @cfg: Pointer to pipe config structure
 	 * @flags: Extra flags for format config
+	 * @index: rectangle index in multirect
 	 */
 	void (*setup_format)(struct sde_hw_pipe *ctx,
-			const struct sde_format *fmt, u32 flags);
+			const struct sde_format *fmt, u32 flags,
+			enum sde_sspp_multirect_index index);
 
 	/**
 	 * setup_rects - setup pipe ROI rectangles
 	 * @ctx: Pointer to pipe context
 	 * @cfg: Pointer to pipe config structure
 	 * @pe_ext: Pointer to pixel ext settings
+	 * @index: rectangle index in multirect
 	 * @scale_cfg: Pointer to scaler settings
 	 */
 	void (*setup_rects)(struct sde_hw_pipe *ctx,
 			struct sde_hw_pipe_cfg *cfg,
 			struct sde_hw_pixel_ext *pe_ext,
+			enum sde_sspp_multirect_index index,
 			void *scale_cfg);
 
 	/**
 	 * setup_excl_rect - setup pipe exclusion rectangle
 	 * @ctx: Pointer to pipe context
 	 * @excl_rect: Pointer to exclclusion rect structure
+	 * @index: rectangle index in multirect
 	 */
 	void (*setup_excl_rect)(struct sde_hw_pipe *ctx,
-			struct sde_rect *excl_rect);
+			struct sde_rect *excl_rect,
+			enum sde_sspp_multirect_index index);
 
 	/**
 	 * setup_sourceaddress - setup pipe source addresses
 	 * @ctx: Pointer to pipe context
 	 * @cfg: Pointer to pipe config structure
+	 * @index: rectangle index in multirect
 	 */
 	void (*setup_sourceaddress)(struct sde_hw_pipe *ctx,
-			struct sde_hw_pipe_cfg *cfg);
+			struct sde_hw_pipe_cfg *cfg,
+			enum sde_sspp_multirect_index index);
 
 	/**
 	 * setup_csc - setup color space coversion
@@ -336,8 +370,21 @@
 	 * @ctx: Pointer to pipe context
 	 * @const_color: Fill color value
 	 * @flags: Pipe flags
+	 * @index: rectangle index in multirect
 	 */
-	void (*setup_solidfill)(struct sde_hw_pipe *ctx, u32 color);
+	void (*setup_solidfill)(struct sde_hw_pipe *ctx, u32 color,
+			enum sde_sspp_multirect_index index);
+
+	/**
+	 * setup_multirect - setup multirect configuration
+	 * @ctx: Pointer to pipe context
+	 * @index: rectangle index in multirect
+	 * @mode: parallel fetch / time multiplex multirect mode
+	 */
+
+	void (*setup_multirect)(struct sde_hw_pipe *ctx,
+			enum sde_sspp_multirect_index index,
+			enum sde_sspp_multirect_mode mode);
 
 	/**
 	 * setup_sharpening - setup sharpening
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index cb3daba..2770160 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -716,8 +716,12 @@
 	struct msm_drm_private *priv;
 	struct sde_mdss_cfg *catalog;
 
-	int primary_planes_idx, i, ret;
-	int max_crtc_count, max_plane_count;
+	int primary_planes_idx = 0, i, ret;
+	int max_crtc_count;
+
+	u32 sspp_id[MAX_PLANES];
+	u32 master_plane_id[MAX_PLANES];
+	u32 num_virt_planes = 0;
 
 	if (!sde_kms || !sde_kms->dev || !sde_kms->dev->dev) {
 		SDE_ERROR("invalid sde_kms\n");
@@ -736,11 +740,9 @@
 		(void)_sde_kms_setup_displays(dev, priv, sde_kms);
 
 	max_crtc_count = min(catalog->mixer_count, priv->num_encoders);
-	max_plane_count = min_t(u32, catalog->sspp_count, MAX_PLANES);
 
 	/* Create the planes */
-	primary_planes_idx = 0;
-	for (i = 0; i < max_plane_count; i++) {
+	for (i = 0; i < catalog->sspp_count; i++) {
 		bool primary = true;
 
 		if (catalog->sspp[i].features & BIT(SDE_SSPP_CURSOR)
@@ -748,7 +750,7 @@
 			primary = false;
 
 		plane = sde_plane_init(dev, catalog->sspp[i].id, primary,
-				(1UL << max_crtc_count) - 1);
+				(1UL << max_crtc_count) - 1, 0);
 		if (IS_ERR(plane)) {
 			SDE_ERROR("sde_plane_init failed\n");
 			ret = PTR_ERR(plane);
@@ -758,6 +760,27 @@
 
 		if (primary)
 			primary_planes[primary_planes_idx++] = plane;
+
+		if (sde_hw_sspp_multirect_enabled(&catalog->sspp[i]) &&
+			sde_is_custom_client()) {
+			int priority =
+				catalog->sspp[i].sblk->smart_dma_priority;
+			sspp_id[priority - 1] = catalog->sspp[i].id;
+			master_plane_id[priority - 1] = plane->base.id;
+			num_virt_planes++;
+		}
+	}
+
+	/* Initialize smart DMA virtual planes */
+	for (i = 0; i < num_virt_planes; i++) {
+		plane = sde_plane_init(dev, sspp_id[i], false,
+			(1UL << max_crtc_count) - 1, master_plane_id[i]);
+		if (IS_ERR(plane)) {
+			SDE_ERROR("sde_plane for virtual SSPP init failed\n");
+			ret = PTR_ERR(plane);
+			goto fail;
+		}
+		priv->planes[priv->num_planes++] = plane;
 	}
 
 	max_crtc_count = min(max_crtc_count, primary_planes_idx);
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 746c19d..585d93e2 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -23,11 +23,13 @@
 #include <uapi/drm/msm_drm_pp.h>
 
 #include "msm_prop.h"
+#include "msm_drv.h"
 
 #include "sde_kms.h"
 #include "sde_fence.h"
 #include "sde_formats.h"
 #include "sde_hw_sspp.h"
+#include "sde_hw_catalog_format.h"
 #include "sde_trace.h"
 #include "sde_crtc.h"
 #include "sde_vbif.h"
@@ -54,6 +56,15 @@
 
 #define SDE_PLANE_COLOR_FILL_FLAG	BIT(31)
 
+/* multirect rect index */
+enum {
+	R0,
+	R1,
+	R_MAX
+};
+
+#define TX_MODE_BUFFER_LINE_THRES 2
+
 /* dirty bits for update function */
 #define SDE_PLANE_DIRTY_RECTS	0x1
 #define SDE_PLANE_DIRTY_FORMAT	0x2
@@ -103,6 +114,7 @@
 	uint32_t color_fill;
 	bool is_error;
 	bool is_rt_pipe;
+	bool is_virtual;
 
 	struct sde_hw_pixel_ext pixel_ext;
 	bool pixel_ext_usr;
@@ -586,7 +598,8 @@
 	else if (ret)
 		SDE_ERROR_PLANE(psde, "failed to get format layout, %d\n", ret);
 	else if (psde->pipe_hw->ops.setup_sourceaddress)
-		psde->pipe_hw->ops.setup_sourceaddress(psde->pipe_hw, pipe_cfg);
+		psde->pipe_hw->ops.setup_sourceaddress(psde->pipe_hw, pipe_cfg,
+						pstate->multirect_index);
 }
 
 static int _sde_plane_setup_scaler3_lut(struct sde_plane *psde,
@@ -1055,6 +1068,8 @@
 		uint32_t color, uint32_t alpha)
 {
 	const struct sde_format *fmt;
+	const struct drm_plane *plane;
+	const struct sde_plane_state *pstate;
 
 	if (!psde) {
 		SDE_ERROR("invalid plane\n");
@@ -1066,6 +1081,9 @@
 		return -EINVAL;
 	}
 
+	plane = &psde->base;
+	pstate = to_sde_plane_state(plane->state);
+
 	SDE_DEBUG_PLANE(psde, "\n");
 
 	/*
@@ -1077,7 +1095,8 @@
 	/* update sspp */
 	if (fmt && psde->pipe_hw->ops.setup_solidfill) {
 		psde->pipe_hw->ops.setup_solidfill(psde->pipe_hw,
-				(color & 0xFFFFFF) | ((alpha & 0xFF) << 24));
+				(color & 0xFFFFFF) | ((alpha & 0xFF) << 24),
+				pstate->multirect_index);
 
 		/* override scaler/decimation if solid fill */
 		psde->pipe_cfg.src_rect.x = 0;
@@ -1089,11 +1108,13 @@
 
 		if (psde->pipe_hw->ops.setup_format)
 			psde->pipe_hw->ops.setup_format(psde->pipe_hw,
-					fmt, SDE_SSPP_SOLID_FILL);
+					fmt, SDE_SSPP_SOLID_FILL,
+					pstate->multirect_index);
 
 		if (psde->pipe_hw->ops.setup_rects)
 			psde->pipe_hw->ops.setup_rects(psde->pipe_hw,
 					&psde->pipe_cfg, &psde->pixel_ext,
+					pstate->multirect_index,
 					psde->scaler3_cfg);
 	}
 
@@ -1221,13 +1242,21 @@
 
 			psde->pipe_hw->ops.setup_rects(psde->pipe_hw,
 					&psde->pipe_cfg, &psde->pixel_ext,
+					pstate->multirect_index,
 					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);
+					&pstate->excl_rect,
+					pstate->multirect_index);
+
+		if (psde->pipe_hw->ops.setup_multirect)
+			psde->pipe_hw->ops.setup_multirect(
+					psde->pipe_hw,
+					pstate->multirect_index,
+					pstate->multirect_mode);
 	}
 
 	if ((pstate->dirty & SDE_PLANE_DIRTY_FORMAT) &&
@@ -1243,7 +1272,8 @@
 			src_flags |= SDE_SSPP_FLIP_UD;
 
 		/* update format */
-		psde->pipe_hw->ops.setup_format(psde->pipe_hw, fmt, src_flags);
+		psde->pipe_hw->ops.setup_format(psde->pipe_hw, fmt, src_flags,
+				pstate->multirect_index);
 
 		/* update csc */
 		if (SDE_FORMAT_IS_YUV(fmt))
@@ -1277,6 +1307,98 @@
 	/* clear dirty */
 	pstate->dirty = 0x0;
 
+	/* clear multirect mode*/
+	pstate->multirect_index = SDE_SSPP_RECT_SOLO;
+	pstate->multirect_mode = SDE_SSPP_MULTIRECT_NONE;
+	return 0;
+}
+
+int sde_plane_validate_multirect_v2(struct sde_multirect_plane_states *plane)
+{
+	struct sde_plane_state *pstate[R_MAX];
+	const struct drm_plane_state *drm_state[R_MAX];
+	struct sde_rect src[R_MAX], dst[R_MAX];
+	struct sde_plane *sde_plane[R_MAX];
+	const struct sde_format *fmt[R_MAX];
+	bool q16_data = true;
+	int i, max_sspp_linewidth;
+	int buffer_lines = TX_MODE_BUFFER_LINE_THRES;
+
+	for (i = 0; i < R_MAX; i++) {
+		const struct msm_format *msm_fmt;
+
+		drm_state[i] = i ? plane->r1 : plane->r0;
+		pstate[i] = to_sde_plane_state(drm_state[i]);
+		sde_plane[i] = to_sde_plane(drm_state[i]->plane);
+
+		if (pstate[i] == NULL) {
+			SDE_ERROR("SDE plane state of plane id %d is NULL\n",
+				drm_state[i]->plane->base.id);
+			return -EINVAL;
+		}
+
+		POPULATE_RECT(&src[i], drm_state[i]->src_x, drm_state[i]->src_y,
+			drm_state[i]->src_w, drm_state[i]->src_h, q16_data);
+		POPULATE_RECT(&dst[i], drm_state[i]->crtc_x,
+				drm_state[i]->crtc_y, drm_state[i]->crtc_w,
+				drm_state[i]->crtc_h, !q16_data);
+
+		if (src[i].w != dst[i].w || src[i].h != dst[i].h) {
+			SDE_ERROR_PLANE(sde_plane[i],
+				"scaling is not supported in multirect mode\n");
+			return -EINVAL;
+		}
+
+		msm_fmt = msm_framebuffer_format(drm_state[i]->fb);
+		fmt[i] = to_sde_format(msm_fmt);
+		if (SDE_FORMAT_IS_YUV(fmt[i])) {
+			SDE_ERROR_PLANE(sde_plane[i],
+				"Unsupported format for multirect mode\n");
+			return -EINVAL;
+		}
+	}
+
+	max_sspp_linewidth = sde_plane[R0]->pipe_sblk->maxlinewidth;
+
+	/* Validate RECT's and set the mode */
+
+	/* Prefer PARALLEL FETCH Mode over TIME_MX Mode */
+	if (src[R0].w <= max_sspp_linewidth/2 &&
+			src[R1].w <= max_sspp_linewidth/2) {
+		if (dst[R0].x <= dst[R1].x) {
+			pstate[R0]->multirect_index = SDE_SSPP_RECT_0;
+			pstate[R1]->multirect_index = SDE_SSPP_RECT_1;
+		} else {
+			pstate[R0]->multirect_index = SDE_SSPP_RECT_1;
+			pstate[R1]->multirect_index = SDE_SSPP_RECT_0;
+		}
+
+		pstate[R0]->multirect_mode = SDE_SSPP_MULTIRECT_PARALLEL;
+		pstate[R1]->multirect_mode = SDE_SSPP_MULTIRECT_PARALLEL;
+		goto done;
+	}
+
+	/* TIME_MX Mode */
+	if (SDE_FORMAT_IS_UBWC(fmt[R0]))
+		buffer_lines = 2 * fmt[R0]->tile_height;
+
+	if (dst[R1].y >= dst[R0].y + dst[R0].h + buffer_lines) {
+		pstate[R0]->multirect_index = SDE_SSPP_RECT_0;
+		pstate[R1]->multirect_index = SDE_SSPP_RECT_1;
+	} else if (dst[R0].y >= dst[R1].y + dst[R1].h + buffer_lines) {
+		pstate[R0]->multirect_index = SDE_SSPP_RECT_1;
+		pstate[R1]->multirect_index = SDE_SSPP_RECT_0;
+	} else {
+		SDE_ERROR(
+			"No multirect mode possible for the planes (%d - %d)\n",
+			drm_state[R0]->plane->base.id,
+			drm_state[R1]->plane->base.id);
+		return -EINVAL;
+	}
+
+	pstate[R0]->multirect_mode = SDE_SSPP_MULTIRECT_TIME_MX;
+	pstate[R1]->multirect_mode = SDE_SSPP_MULTIRECT_TIME_MX;
+done:
 	return 0;
 }
 
@@ -1343,6 +1465,10 @@
 		   pstate->excl_rect.y != old_pstate->excl_rect.y) {
 		SDE_DEBUG_PLANE(psde, "excl rect updated\n");
 		pstate->dirty |= SDE_PLANE_DIRTY_RECTS;
+	} else if (pstate->multirect_index != old_pstate->multirect_index ||
+			pstate->multirect_mode != old_pstate->multirect_mode) {
+		SDE_DEBUG_PLANE(psde, "multirect config updated\n");
+		pstate->dirty |= SDE_PLANE_DIRTY_RECTS;
 	}
 
 	if (!state->fb || !old_state->fb) {
@@ -1614,7 +1740,7 @@
 
 /* helper to install properties which are common to planes and crtcs */
 static void _sde_plane_install_properties(struct drm_plane *plane,
-	struct sde_mdss_cfg *catalog)
+	struct sde_mdss_cfg *catalog, u32 master_plane_id)
 {
 	static const struct drm_prop_enum_list e_blend_op[] = {
 		{SDE_DRM_BLEND_OP_NOT_DEFINED,    "not_defined"},
@@ -1666,62 +1792,71 @@
 	msm_property_install_range(&psde->property_info, "input_fence",
 		0x0, 0, INR_OPEN_MAX, 0, PLANE_PROP_INPUT_FENCE);
 
-	if (psde->pipe_sblk->maxhdeciexp) {
-		msm_property_install_range(&psde->property_info, "h_decimate",
-			0x0, 0, psde->pipe_sblk->maxhdeciexp, 0,
-			PLANE_PROP_H_DECIMATE);
-	}
+	if (!master_plane_id) {
+		if (psde->pipe_sblk->maxhdeciexp) {
+			msm_property_install_range(&psde->property_info,
+					"h_decimate", 0x0, 0,
+					psde->pipe_sblk->maxhdeciexp, 0,
+					PLANE_PROP_H_DECIMATE);
+		}
 
-	if (psde->pipe_sblk->maxvdeciexp) {
-		msm_property_install_range(&psde->property_info, "v_decimate",
-				0x0, 0, psde->pipe_sblk->maxvdeciexp, 0,
-				PLANE_PROP_V_DECIMATE);
-	}
+		if (psde->pipe_sblk->maxvdeciexp) {
+			msm_property_install_range(&psde->property_info,
+					"v_decimate", 0x0, 0,
+					psde->pipe_sblk->maxvdeciexp, 0,
+					PLANE_PROP_V_DECIMATE);
+		}
 
-	if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) {
-		msm_property_install_volatile_range(&psde->property_info,
-			"scaler_v2", 0x0, 0, ~0, 0, PLANE_PROP_SCALER_V2);
-		msm_property_install_blob(&psde->property_info, "lut_ed", 0,
-			PLANE_PROP_SCALER_LUT_ED);
-		msm_property_install_blob(&psde->property_info, "lut_cir", 0,
-			PLANE_PROP_SCALER_LUT_CIR);
-		msm_property_install_blob(&psde->property_info, "lut_sep", 0,
-			PLANE_PROP_SCALER_LUT_SEP);
-	} else if (psde->features & SDE_SSPP_SCALER) {
-		msm_property_install_volatile_range(&psde->property_info,
-			"scaler_v1", 0x0, 0, ~0, 0, PLANE_PROP_SCALER_V1);
-	}
+		if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) {
+			msm_property_install_volatile_range(
+					&psde->property_info, "scaler_v2",
+					0x0, 0, ~0, 0, PLANE_PROP_SCALER_V2);
+			msm_property_install_blob(&psde->property_info,
+					"lut_ed", 0, PLANE_PROP_SCALER_LUT_ED);
+			msm_property_install_blob(&psde->property_info,
+					"lut_cir", 0,
+					PLANE_PROP_SCALER_LUT_CIR);
+			msm_property_install_blob(&psde->property_info,
+					"lut_sep", 0,
+					PLANE_PROP_SCALER_LUT_SEP);
+		} else if (psde->features & SDE_SSPP_SCALER) {
+			msm_property_install_volatile_range(
+					&psde->property_info, "scaler_v1", 0x0,
+					0, ~0, 0, PLANE_PROP_SCALER_V1);
+		}
 
-	if (psde->features & BIT(SDE_SSPP_CSC)) {
-		msm_property_install_volatile_range(&psde->property_info,
-			"csc_v1", 0x0, 0, ~0, 0, PLANE_PROP_CSC_V1);
-	}
+		if (psde->features & BIT(SDE_SSPP_CSC)) {
+			msm_property_install_volatile_range(
+					&psde->property_info, "csc_v1", 0x0,
+					0, ~0, 0, PLANE_PROP_CSC_V1);
+		}
 
-	if (psde->features & BIT(SDE_SSPP_HSIC)) {
-		snprintf(feature_name, sizeof(feature_name), "%s%d",
-			"SDE_SSPP_HUE_V",
-			psde->pipe_sblk->hsic_blk.version >> 16);
-		msm_property_install_range(&psde->property_info,
-			feature_name, 0, 0, 0xFFFFFFFF, 0,
-			PLANE_PROP_HUE_ADJUST);
-		snprintf(feature_name, sizeof(feature_name), "%s%d",
-			"SDE_SSPP_SATURATION_V",
-			psde->pipe_sblk->hsic_blk.version >> 16);
-		msm_property_install_range(&psde->property_info,
-			feature_name, 0, 0, 0xFFFFFFFF, 0,
-			PLANE_PROP_SATURATION_ADJUST);
-		snprintf(feature_name, sizeof(feature_name), "%s%d",
-			"SDE_SSPP_VALUE_V",
-			psde->pipe_sblk->hsic_blk.version >> 16);
-		msm_property_install_range(&psde->property_info,
-			feature_name, 0, 0, 0xFFFFFFFF, 0,
-			PLANE_PROP_VALUE_ADJUST);
-		snprintf(feature_name, sizeof(feature_name), "%s%d",
-			"SDE_SSPP_CONTRAST_V",
-			psde->pipe_sblk->hsic_blk.version >> 16);
-		msm_property_install_range(&psde->property_info,
-			feature_name, 0, 0, 0xFFFFFFFF, 0,
-			PLANE_PROP_CONTRAST_ADJUST);
+		if (psde->features & BIT(SDE_SSPP_HSIC)) {
+			snprintf(feature_name, sizeof(feature_name), "%s%d",
+				"SDE_SSPP_HUE_V",
+				psde->pipe_sblk->hsic_blk.version >> 16);
+			msm_property_install_range(&psde->property_info,
+				feature_name, 0, 0, 0xFFFFFFFF, 0,
+				PLANE_PROP_HUE_ADJUST);
+			snprintf(feature_name, sizeof(feature_name), "%s%d",
+				"SDE_SSPP_SATURATION_V",
+				psde->pipe_sblk->hsic_blk.version >> 16);
+			msm_property_install_range(&psde->property_info,
+				feature_name, 0, 0, 0xFFFFFFFF, 0,
+				PLANE_PROP_SATURATION_ADJUST);
+			snprintf(feature_name, sizeof(feature_name), "%s%d",
+				"SDE_SSPP_VALUE_V",
+				psde->pipe_sblk->hsic_blk.version >> 16);
+			msm_property_install_range(&psde->property_info,
+				feature_name, 0, 0, 0xFFFFFFFF, 0,
+				PLANE_PROP_VALUE_ADJUST);
+			snprintf(feature_name, sizeof(feature_name), "%s%d",
+				"SDE_SSPP_CONTRAST_V",
+				psde->pipe_sblk->hsic_blk.version >> 16);
+			msm_property_install_range(&psde->property_info,
+				feature_name, 0, 0, 0xFFFFFFFF, 0,
+				PLANE_PROP_CONTRAST_ADJUST);
+		}
 	}
 
 	if (psde->features & BIT(SDE_SSPP_EXCL_RECT))
@@ -1754,6 +1889,13 @@
 	sde_kms_info_reset(info);
 
 	format_list = psde->pipe_sblk->format_list;
+
+	if (master_plane_id) {
+		sde_kms_info_add_keyint(info, "primary_smart_plane_id",
+				master_plane_id);
+		format_list = plane_formats;
+	}
+
 	if (format_list) {
 		sde_kms_info_start(info, "pixel_formats");
 		while (format_list->fourcc_format) {
@@ -2248,6 +2390,11 @@
 	return plane ? to_sde_plane(plane)->pipe : SSPP_NONE;
 }
 
+bool is_sde_plane_virtual(struct drm_plane *plane)
+{
+	return plane ? to_sde_plane(plane)->is_virtual : false;
+}
+
 static ssize_t _sde_plane_danger_read(struct file *file,
 			char __user *buff, size_t count, loff_t *ppos)
 {
@@ -2411,9 +2558,10 @@
 /* initialize plane */
 struct drm_plane *sde_plane_init(struct drm_device *dev,
 		uint32_t pipe, bool primary_plane,
-		unsigned long possible_crtcs)
+		unsigned long possible_crtcs, u32 master_plane_id)
 {
 	struct drm_plane *plane = NULL;
+	const struct sde_format_extended *format_list;
 	struct sde_plane *psde;
 	struct msm_drm_private *priv;
 	struct sde_kms *kms;
@@ -2454,6 +2602,7 @@
 	plane = &psde->base;
 	psde->pipe = pipe;
 	psde->mmu_id = kms->mmu_id[MSM_SMMU_DOMAIN_UNSECURE];
+	psde->is_virtual = (master_plane_id != 0);
 
 	/* initialize underlying h/w driver */
 	psde->pipe_hw = sde_hw_sspp_init(pipe, kms->mmio, kms->catalog);
@@ -2485,11 +2634,15 @@
 		}
 	}
 
-	/* add plane to DRM framework */
-	psde->nformats = sde_populate_formats(psde->pipe_sblk->format_list,
-			psde->formats,
-			0,
-			ARRAY_SIZE(psde->formats));
+	format_list = psde->pipe_sblk->format_list;
+
+	if (master_plane_id)
+		format_list = plane_formats;
+
+	psde->nformats = sde_populate_formats(plane_formats,
+				psde->formats,
+				0,
+				ARRAY_SIZE(psde->formats));
 
 	if (!psde->nformats) {
 		SDE_ERROR("[%u]no valid formats for plane\n", pipe);
@@ -2516,7 +2669,7 @@
 			PLANE_PROP_COUNT, PLANE_PROP_BLOBCOUNT,
 			sizeof(struct sde_plane_state));
 
-	_sde_plane_install_properties(plane, kms->catalog);
+	_sde_plane_install_properties(plane, kms->catalog, master_plane_id);
 
 	/* save user friendly pipe name for later */
 	snprintf(psde->pipe_name, SDE_NAME_SIZE, "plane%u", plane->base.id);
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.h b/drivers/gpu/drm/msm/sde/sde_plane.h
index bbf6af6..9d7056e 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.h
+++ b/drivers/gpu/drm/msm/sde/sde_plane.h
@@ -33,6 +33,8 @@
  * @stage:	assigned by crtc blender
  * @excl_rect:	exclusion rect values
  * @dirty:	bitmask for which pipe h/w config functions need to be updated
+ * @multirect_index: index of the rectangle of SSPP
+ * @multirect_mode: parallel or time multiplex multirect mode
  * @pending:	whether the current update is still pending
  */
 struct sde_plane_state {
@@ -43,9 +45,21 @@
 	enum sde_stage stage;
 	struct sde_rect excl_rect;
 	uint32_t dirty;
+	uint32_t multirect_index;
+	uint32_t multirect_mode;
 	bool pending;
 };
 
+/**
+ * struct sde_multirect_plane_states: Defines multirect pair of drm plane states
+ * @r0: drm plane configured on rect 0
+ * @r1: drm plane configured on rect 1
+ */
+struct sde_multirect_plane_states {
+	const struct drm_plane_state *r0;
+	const struct drm_plane_state *r1;
+};
+
 #define to_sde_plane_state(x) \
 	container_of(x, struct sde_plane_state, base)
 
@@ -66,6 +80,14 @@
 enum sde_sspp sde_plane_pipe(struct drm_plane *plane);
 
 /**
+ * is_sde_plane_virtual - check for virtual plane
+ * @plane: Pointer to DRM plane object
+ * returns: true - if the plane is virtual
+ *          false - if the plane is primary
+ */
+bool is_sde_plane_virtual(struct drm_plane *plane);
+
+/**
  * sde_plane_flush - final plane operations before commit flush
  * @plane: Pointer to drm plane structure
  */
@@ -77,10 +99,22 @@
  * @pipe:  sde hardware pipe identifier
  * @primary_plane: true if this pipe is primary plane for crtc
  * @possible_crtcs: bitmask of crtc that can be attached to the given pipe
+ * @master_plane_id: primary plane id of a multirect pipe. 0 value passed for
+ *                   a regular plane initialization. A non-zero primary plane
+ *                   id will be passed for a virtual pipe initialization.
+ *
  */
 struct drm_plane *sde_plane_init(struct drm_device *dev,
 		uint32_t pipe, bool primary_plane,
-		unsigned long possible_crtcs);
+		unsigned long possible_crtcs, u32 master_plane_id);
+
+/**
+ * sde_plane_validate_multirecti_v2 - validate the multirect planes
+ *				      against hw limitations
+ * @plane: drm plate states of the multirect pair
+ */
+
+int sde_plane_validate_multirect_v2(struct sde_multirect_plane_states *plane);
 
 /**
  * sde_plane_wait_input_fence - wait for input fence object