Эх сурвалжийг харах

disp: msm: sde: program both rc config and ram through lut dma

Currently, only rc ram is programmed through lut dma and
rc config is programmed through ahb.
This change programs both rc config and rc ram through lut dma.

Change-Id: I50a6e87dfbadb9c4a93cebdaa1e813f5be9ba5f5
Signed-off-by: Saurabh Yadav <[email protected]>
Saurabh Yadav 2 жил өмнө
parent
commit
c27d7c56ce

+ 1 - 10
msm/sde/sde_color_processing.c

@@ -825,7 +825,7 @@ static int _set_rc_mask_feature(struct sde_hw_dspp *hw_dspp,
 		return -EINVAL;
 	}
 
-	if (!hw_dspp->ops.setup_rc_mask || !hw_dspp->ops.setup_rc_data) {
+	if (!hw_dspp->ops.setup_rc_mask) {
 		DRM_ERROR("invalid rc ops\n");
 		return -EINVAL;
 	}
@@ -839,15 +839,6 @@ static int _set_rc_mask_feature(struct sde_hw_dspp *hw_dspp,
 		goto exit;
 	}
 
-	/* rc data should be programmed once if dspp are in multi-pipe mode */
-	if (hw_dspp->cap->sblk->rc.idx % hw_cfg->num_of_mixers == 0) {
-		ret = hw_dspp->ops.setup_rc_data(hw_dspp, hw_cfg);
-		if (ret) {
-			DRM_ERROR("failed to setup rc data, ret %d\n", ret);
-			goto exit;
-		}
-	}
-
 	_update_pu_feature_enable(sde_crtc, SDE_CP_CRTC_DSPP_RC_PU,
 			hw_cfg->payload != NULL);
 exit:

+ 3 - 1
msm/sde/sde_hw_catalog.h

@@ -455,7 +455,8 @@ enum {
  * @SDE_DSPP_LTM             LTM block
  * @SDE_DSPP_SPR             SPR block
  * @SDE_DSPP_DEMURA          Demura block
- * @SDE_DSPP_RC              RC block
+ * @SDE_DSPP_RC              RC block (mask)
+ * @SDE_DSPP_RC_PU           RC block (pu)
  * @SDE_DSPP_SB              SB LUT DMA
  * @SDE_DSPP_DEMURA_CFG0_PARAM2 Demura block
  * @SDE_DSPP_MAX             maximum value
@@ -476,6 +477,7 @@ enum {
 	SDE_DSPP_SPR,
 	SDE_DSPP_DEMURA,
 	SDE_DSPP_RC,
+	SDE_DSPP_RC_PU,
 	SDE_DSPP_SB,
 	SDE_DSPP_DEMURA_CFG0_PARAM2,
 	SDE_DSPP_MAX

+ 366 - 1
msm/sde/sde_hw_color_proc_common_v4.h

@@ -1,12 +1,13 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) 2017-2019, 2021 The Linux Foundation. All rights reserved.
  */
 #ifndef _SDE_HW_COLOR_PROC_COMMON_V4_H_
 #define _SDE_HW_COLOR_PROC_COMMON_V4_H_
 
 #include "sde_hw_mdss.h"
+#include "sde_dbg.h"
 
 /*
  * DEMURA fetch planes
@@ -157,6 +158,75 @@ enum {
 
 #define LTM_CONFIG_MERGE_MODE_ONLY (BIT(16) | BIT(17))
 
+/**
+ * Hardware register set
+ */
+#define SDE_HW_RC_REG0 0x00
+#define SDE_HW_RC_REG1 0x04
+#define SDE_HW_RC_REG2 0x08
+#define SDE_HW_RC_REG3 0x0C
+#define SDE_HW_RC_REG4 0x10
+#define SDE_HW_RC_REG5 0x14
+#define SDE_HW_RC_REG6 0x18
+#define SDE_HW_RC_REG7 0x1C
+#define SDE_HW_RC_REG8 0x20
+#define SDE_HW_RC_REG9 0x24
+#define SDE_HW_RC_REG10 0x28
+#define SDE_HW_RC_REG11 0x2C
+#define SDE_HW_RC_REG12 0x30
+#define SDE_HW_RC_REG13 0x34
+
+#define SDE_HW_RC_DATA_REG_SIZE  18
+#define SDE_HW_RC_SKIP_DATA_PROG 0x1
+
+#define SDE_HW_RC_DISABLE_R1 0x01E
+#define SDE_HW_RC_DISABLE_R2 0x1E0
+
+#define SDE_HW_RC_PU_SKIP_OP 0x1
+
+#define RC_IDX(hw_dspp) hw_dspp->cap->sblk->rc.idx
+
+enum rc_param_r {
+	RC_PARAM_R0     = 0x0,
+	RC_PARAM_R1     = 0x1,
+	RC_PARAM_R2     = 0x2,
+	RC_PARAM_R1R2   = (RC_PARAM_R1 | RC_PARAM_R2),
+};
+
+enum rc_param_a {
+	RC_PARAM_A0     = 0x2,
+	RC_PARAM_A1     = 0x4,
+};
+
+enum rc_param_b {
+	RC_PARAM_B0     = 0x0,
+	RC_PARAM_B1     = 0x1,
+	RC_PARAM_B2     = 0x2,
+	RC_PARAM_B1B2   = (RC_PARAM_B1 | RC_PARAM_B2),
+};
+
+enum rc_param_c {
+	RC_PARAM_C0     = (BIT(8)),
+	RC_PARAM_C1     = (BIT(10)),
+	RC_PARAM_C2     = (BIT(10) | BIT(11)),
+	RC_PARAM_C3     = (BIT(8) | BIT(10)),
+	RC_PARAM_C4     = (BIT(8) | BIT(9)),
+	RC_PARAM_C5     = (BIT(8) | BIT(9) | BIT(10) | BIT(11)),
+};
+
+enum rc_merge_mode {
+	RC_MERGE_SINGLE_PIPE = 0x0,
+	RC_MERGE_DUAL_PIPE   = 0x1
+};
+
+struct rc_config_table {
+	enum rc_param_a param_a;
+	enum rc_param_b param_b;
+	enum rc_param_c param_c;
+	enum rc_merge_mode merge_mode;
+	enum rc_merge_mode merge_mode_en;
+};
+
 struct sde_ltm_phase_info {
 	u32 init_h[LTM_MAX];
 	u32 init_v;
@@ -202,4 +272,299 @@ static inline void sde_ltm_get_phase_info(struct sde_hw_cp_cfg *hw_cfg,
 	}
 }
 
+static struct rc_config_table config_table[] =  {
+	/* RC_PARAM_A0 configurations */
+	{
+		.param_a = RC_PARAM_A0,
+		.param_b = RC_PARAM_B0,
+		.param_c = RC_PARAM_C5,
+		.merge_mode = RC_MERGE_SINGLE_PIPE,
+		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
+
+	},
+	{
+		.param_a = RC_PARAM_A0,
+		.param_b = RC_PARAM_B1B2,
+		.param_c = RC_PARAM_C3,
+		.merge_mode = RC_MERGE_SINGLE_PIPE,
+		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
+
+	},
+	{
+		.param_a = RC_PARAM_A0,
+		.param_b = RC_PARAM_B1,
+		.param_c = RC_PARAM_C0,
+		.merge_mode = RC_MERGE_SINGLE_PIPE,
+		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
+
+	},
+	{
+		.param_a = RC_PARAM_A0,
+		.param_b = RC_PARAM_B2,
+		.param_c = RC_PARAM_C1,
+		.merge_mode = RC_MERGE_SINGLE_PIPE,
+		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
+
+	},
+	{
+		.param_a = RC_PARAM_A0,
+		.param_b = RC_PARAM_B0,
+		.param_c = RC_PARAM_C5,
+		.merge_mode = RC_MERGE_DUAL_PIPE,
+		.merge_mode_en = RC_MERGE_DUAL_PIPE,
+
+	},
+	{
+		.param_a = RC_PARAM_A0,
+		.param_b = RC_PARAM_B1B2,
+		.param_c = RC_PARAM_C3,
+		.merge_mode = RC_MERGE_DUAL_PIPE,
+		.merge_mode_en = RC_MERGE_DUAL_PIPE,
+
+	},
+	{
+		.param_a = RC_PARAM_A0,
+		.param_b = RC_PARAM_B1,
+		.param_c = RC_PARAM_C0,
+		.merge_mode = RC_MERGE_DUAL_PIPE,
+		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
+
+	},
+	{
+		.param_a = RC_PARAM_A0,
+		.param_b = RC_PARAM_B2,
+		.param_c = RC_PARAM_C1,
+		.merge_mode = RC_MERGE_DUAL_PIPE,
+		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
+	},
+
+	/* RC_PARAM_A1 configurations */
+	{
+		.param_a = RC_PARAM_A1,
+		.param_b = RC_PARAM_B0,
+		.param_c = RC_PARAM_C5,
+		.merge_mode = RC_MERGE_SINGLE_PIPE,
+		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
+
+	},
+	{
+		.param_a = RC_PARAM_A1,
+		.param_b = RC_PARAM_B1B2,
+		.param_c = RC_PARAM_C5,
+		.merge_mode = RC_MERGE_SINGLE_PIPE,
+		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
+
+	},
+	{
+		.param_a = RC_PARAM_A1,
+		.param_b = RC_PARAM_B1,
+		.param_c = RC_PARAM_C4,
+		.merge_mode = RC_MERGE_SINGLE_PIPE,
+		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
+
+	},
+	{
+		.param_a = RC_PARAM_A1,
+		.param_b = RC_PARAM_B2,
+		.param_c = RC_PARAM_C2,
+		.merge_mode = RC_MERGE_SINGLE_PIPE,
+		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
+
+	},
+	{
+		.param_a = RC_PARAM_A1,
+		.param_b = RC_PARAM_B0,
+		.param_c = RC_PARAM_C5,
+		.merge_mode = RC_MERGE_DUAL_PIPE,
+		.merge_mode_en = RC_MERGE_DUAL_PIPE,
+
+	},
+	{
+		.param_a = RC_PARAM_A1,
+		.param_b = RC_PARAM_B1B2,
+		.param_c = RC_PARAM_C5,
+		.merge_mode = RC_MERGE_DUAL_PIPE,
+		.merge_mode_en = RC_MERGE_DUAL_PIPE,
+
+	},
+	{
+		.param_a = RC_PARAM_A1,
+		.param_b = RC_PARAM_B1,
+		.param_c = RC_PARAM_C4,
+		.merge_mode = RC_MERGE_DUAL_PIPE,
+		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
+
+	},
+	{
+		.param_a = RC_PARAM_A1,
+		.param_b = RC_PARAM_B2,
+		.param_c = RC_PARAM_C2,
+		.merge_mode = RC_MERGE_DUAL_PIPE,
+		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
+
+	},
+};
+
+static inline int _sde_hw_rc_get_enable_bits(
+		enum rc_param_a param_a,
+		enum rc_param_b param_b,
+		enum rc_param_c *param_c,
+		u32 merge_mode,
+		u32 *merge_mode_en)
+{
+	int i = 0;
+
+	if (!param_c || !merge_mode_en) {
+		DRM_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(config_table); i++) {
+		if (merge_mode == config_table[i].merge_mode &&
+				param_a == config_table[i].param_a &&
+				param_b == config_table[i].param_b) {
+			*param_c = config_table[i].param_c;
+			*merge_mode_en = config_table[i].merge_mode_en;
+			DRM_DEBUG("found param_c:0x%08X, merge_mode_en:%d\n",
+					*param_c, *merge_mode_en);
+			return 0;
+		}
+	}
+	DRM_ERROR("configuration not supported");
+
+	return -EINVAL;
+}
+
+static inline int _sde_hw_rc_get_merge_mode(
+		const struct sde_hw_cp_cfg *hw_cfg,
+		u32 *merge_mode)
+{
+	int rc = 0;
+
+	if (!hw_cfg || !merge_mode) {
+		DRM_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	if (hw_cfg->num_of_mixers == 1)
+		*merge_mode = RC_MERGE_SINGLE_PIPE;
+	else if (hw_cfg->num_of_mixers == 2)
+		*merge_mode = RC_MERGE_DUAL_PIPE;
+	else {
+		DRM_ERROR("invalid number of mixers:%d\n",
+				hw_cfg->num_of_mixers);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("number mixers:%u, merge mode:%u\n",
+			hw_cfg->num_of_mixers, *merge_mode);
+
+	return rc;
+}
+
+static inline int _sde_hw_rc_get_ajusted_roi(
+		const struct sde_hw_cp_cfg *hw_cfg,
+		const struct sde_rect *pu_roi,
+		struct sde_rect *rc_roi)
+{
+	int rc = 0;
+
+	if (!hw_cfg || !pu_roi || !rc_roi) {
+		DRM_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	/*when partial update is disabled, use full screen ROI*/
+	if (pu_roi->w == 0 && pu_roi->h == 0) {
+		rc_roi->x = pu_roi->x;
+		rc_roi->y = pu_roi->y;
+		rc_roi->w = hw_cfg->panel_width;
+		rc_roi->h = hw_cfg->panel_height;
+	} else {
+		memcpy(rc_roi, pu_roi, sizeof(struct sde_rect));
+	}
+
+	SDE_EVT32(hw_cfg->displayh, hw_cfg->displayv, hw_cfg->panel_width, hw_cfg->panel_height);
+	DRM_DEBUG("displayh:%u, displayv:%u, panel_w:%u, panel_h:%u\n", hw_cfg->displayh,
+			hw_cfg->displayv, hw_cfg->panel_width, hw_cfg->panel_height);
+	DRM_DEBUG("pu_roi x:%u, y:%u, w:%u, h:%u\n", pu_roi->x, pu_roi->y,
+			pu_roi->w, pu_roi->h);
+	DRM_DEBUG("rc_roi x:%u, y:%u, w:%u, h:%u\n", rc_roi->x, rc_roi->y,
+			rc_roi->w, rc_roi->h);
+
+	return rc;
+}
+
+static inline int _sde_hw_rc_get_param_rb(
+		const struct drm_msm_rc_mask_cfg *rc_mask_cfg,
+		const struct sde_rect *rc_roi,
+		enum rc_param_r *param_r,
+		enum rc_param_b *param_b)
+{
+	int rc = 0;
+	int half_panel_x = 0, half_panel_w = 0;
+	int cfg_param_01 = 0, cfg_param_02 = 0;
+	int x1 = 0, x2 = 0, y1 = 0, y2 = 0;
+
+	if (!rc_mask_cfg || !rc_roi || !param_r || !param_b) {
+		DRM_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	if (rc_mask_cfg->cfg_param_03 == RC_PARAM_A1)
+		half_panel_w = rc_mask_cfg->cfg_param_04[0] +
+				rc_mask_cfg->cfg_param_04[1];
+	else if (rc_mask_cfg->cfg_param_03 == RC_PARAM_A0)
+		half_panel_w = rc_mask_cfg->cfg_param_04[0];
+	else {
+		DRM_ERROR("invalid cfg_param_03:%u\n",
+				rc_mask_cfg->cfg_param_03);
+		return -EINVAL;
+	}
+
+	cfg_param_01 = rc_mask_cfg->cfg_param_01;
+	cfg_param_02 = rc_mask_cfg->cfg_param_02;
+	x1 = rc_roi->x;
+	x2 = rc_roi->x + rc_roi->w - 1;
+	y1 = rc_roi->y;
+	y2 = rc_roi->y + rc_roi->h - 1;
+	half_panel_x = half_panel_w - 1;
+
+	DRM_DEBUG("x1:%u y1:%u x2:%u y2:%u\n", x1, y1, x2, y2);
+	DRM_DEBUG("cfg_param_01:%u cfg_param_02:%u half_panel_x:%u",
+			cfg_param_01, cfg_param_02, half_panel_x);
+
+	if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0 || half_panel_x < 0 ||
+			x1 >= x2 || y1 >= y2) {
+		DRM_ERROR("invalid coordinates\n");
+		return -EINVAL;
+	}
+
+	if (y1 <= cfg_param_01) {
+		*param_r |= RC_PARAM_R1;
+		if (x1 <= half_panel_x && x2 <= half_panel_x)
+			*param_b |= RC_PARAM_B1;
+		else if (x1 > half_panel_x && x2 > half_panel_x)
+			*param_b |= RC_PARAM_B2;
+		else
+			*param_b |= RC_PARAM_B1B2;
+	}
+
+	if (y2 >= cfg_param_02) {
+		*param_r |= RC_PARAM_R2;
+		if (x1 <= half_panel_x && x2 <= half_panel_x)
+			*param_b |= RC_PARAM_B1;
+		else if (x1 > half_panel_x && x2 > half_panel_x)
+			*param_b |= RC_PARAM_B2;
+		else
+			*param_b |= RC_PARAM_B1B2;
+	}
+
+	DRM_DEBUG("param_r:0x%08X param_b:0x%08X\n", *param_r, *param_b);
+	SDE_EVT32(rc_roi->x, rc_roi->y, rc_roi->w, rc_roi->h);
+	SDE_EVT32(x1, y1, x2, y2, cfg_param_01, cfg_param_02, half_panel_x);
+
+	return rc;
+}
+
 #endif /* _SDE_HW_COLOR_PROC_COMMON_V4_H_ */

+ 8 - 8
msm/sde/sde_hw_dspp.c

@@ -308,17 +308,17 @@ static void dspp_rc(struct sde_hw_dspp *c)
 		}
 
 		ret = reg_dmav1_init_dspp_op_v4(SDE_DSPP_RC, c->idx);
-		if (!ret)
-			c->ops.setup_rc_data =
-					sde_hw_rc_setup_data_dma;
-		else
-			c->ops.setup_rc_data =
-					sde_hw_rc_setup_data_ahb;
+		if (!ret) {
+			c->ops.setup_rc_mask = reg_dmav1_setup_rc_mask_configv1;
+			c->ops.setup_rc_pu_roi = reg_dmav1_setup_rc_pu_configv1;
+
+		} else {
+			c->ops.setup_rc_mask = sde_hw_rc_setup_mask;
+			c->ops.setup_rc_pu_roi = sde_hw_rc_setup_pu_roi;
+		}
 
 		c->ops.validate_rc_mask = sde_hw_rc_check_mask;
-		c->ops.setup_rc_mask = sde_hw_rc_setup_mask;
 		c->ops.validate_rc_pu_roi = sde_hw_rc_check_pu_roi;
-		c->ops.setup_rc_pu_roi = sde_hw_rc_setup_pu_roi;
 	}
 }
 

+ 16 - 8
msm/sde/sde_hw_dspp.h

@@ -7,6 +7,8 @@
 #ifndef _SDE_HW_DSPP_H
 #define _SDE_HW_DSPP_H
 
+#include <drm/msm_drm_pp.h>
+#include "msm_drv.h"
 
 struct sde_hw_dspp;
 
@@ -248,14 +250,6 @@ struct sde_hw_dspp_ops {
 	 */
 	int (*setup_rc_pu_roi)(struct sde_hw_dspp *ctx, void *cfg);
 
-	/**
-	 * setup_rc_data -  Program RC mask data
-	 * @ctx: Pointer to dspp context.
-	 * @cfg: Pointer to configuration.
-	 * Return: 0 on success, non-zero otherwise.
-	 */
-	int (*setup_rc_data)(struct sde_hw_dspp *ctx, void *cfg);
-
 	/**
 	 * validate_spr_init_config -  Validate SPR configuration
 	 * @ctx: Pointer to dspp context.
@@ -334,6 +328,16 @@ struct sde_hw_dspp_ops {
 	void (*setup_demura_cfg0_param2)(struct sde_hw_dspp *ctx, void *cfg);
 };
 
+/**
+ * struct sde_hw_rc_state - rounded corner cached state per RC instance
+ * @last_rc_mask_cfg: cached value of most recent programmed mask.
+ * @last_roi_list: cached value of most recent processed list of ROIs.
+ */
+struct sde_hw_rc_state {
+	struct drm_msm_rc_mask_cfg *last_rc_mask_cfg;
+	struct msm_roi_list *last_roi_list;
+};
+
 /**
  * struct sde_hw_dspp - dspp description
  * @base: Hardware block base structure
@@ -345,6 +349,7 @@ struct sde_hw_dspp_ops {
  * @ops: Pointer to operations possible for this DSPP
  * @ltm_checksum_support: flag to check if checksum present
  * @spr_cfg_18_default: Default SPR cfg 18 HW details. Needed for PU handling
+ * @rc_state: Structure for RC state
  */
 struct sde_hw_dspp {
 	struct sde_hw_blk_reg_map hw;
@@ -361,6 +366,9 @@ struct sde_hw_dspp {
 
 	/* Ops */
 	struct sde_hw_dspp_ops ops;
+
+	/* rc state */
+	struct sde_hw_rc_state rc_state;
 };
 
 /**

+ 41 - 511
msm/sde/sde_hw_rc.c

@@ -1,251 +1,17 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
  */
 
 #define pr_fmt(fmt)	"[drm:%s:%d] " fmt, __func__, __LINE__
 #include <drm/msm_drm_pp.h>
 #include "sde_kms.h"
-#include "sde_reg_dma.h"
 #include "sde_hw_rc.h"
 #include "sde_hw_catalog.h"
 #include "sde_hw_util.h"
 #include "sde_hw_dspp.h"
-#include "sde_hw_reg_dma_v1_color_proc.h"
-
-/**
- * Hardware register set
- */
-#define SDE_HW_RC_REG0 0x00
-#define SDE_HW_RC_REG1 0x04
-#define SDE_HW_RC_REG2 0x08
-#define SDE_HW_RC_REG3 0x0C
-#define SDE_HW_RC_REG4 0x10
-#define SDE_HW_RC_REG5 0x14
-#define SDE_HW_RC_REG6 0x18
-#define SDE_HW_RC_REG7 0x1C
-#define SDE_HW_RC_REG8 0x20
-#define SDE_HW_RC_REG9 0x24
-#define SDE_HW_RC_REG10 0x28
-#define SDE_HW_RC_REG11 0x2C
-#define SDE_HW_RC_REG12 0x30
-#define SDE_HW_RC_REG13 0x34
-
-#define SDE_HW_RC_DATA_REG_SIZE  18
-#define SDE_HW_RC_SKIP_DATA_PROG 0x1
-
-#define SDE_HW_RC_DISABLE_R1 0x01E
-#define SDE_HW_RC_DISABLE_R2 0x1E0
-
-#define SDE_HW_RC_PU_SKIP_OP 0x1
-
-/**
- * struct sde_hw_rc_state - rounded corner cached state per RC instance
- *
- * @last_rc_mask_cfg: cached value of most recent programmed mask.
- * @mask_programmed: true if mask was programmed at least once to RC hardware.
- * @last_roi_list: cached value of most recent processed list of ROIs.
- * @roi_programmed: true if list of ROIs were processed at least once.
- */
-struct sde_hw_rc_state {
-	struct drm_msm_rc_mask_cfg *last_rc_mask_cfg;
-	bool mask_programmed;
-	struct msm_roi_list *last_roi_list;
-	bool roi_programmed;
-};
-
-static struct sde_hw_rc_state rc_state[RC_MAX - RC_0] = {
-	{
-		.last_rc_mask_cfg = NULL,
-		.last_roi_list = NULL,
-		.mask_programmed = false,
-		.roi_programmed = false,
-	},
-	{
-		.last_rc_mask_cfg = NULL,
-		.last_roi_list = NULL,
-		.mask_programmed = false,
-		.roi_programmed = false,
-	},
-};
-
-#define RC_IDX(hw_dspp) hw_dspp->cap->sblk->rc.idx
-#define RC_STATE(hw_dspp) rc_state[RC_IDX(hw_dspp)]
-
-enum rc_param_r {
-	RC_PARAM_R0     = 0x0,
-	RC_PARAM_R1     = 0x1,
-	RC_PARAM_R2     = 0x2,
-	RC_PARAM_R1R2   = (RC_PARAM_R1 | RC_PARAM_R2),
-};
-
-enum rc_param_a {
-	RC_PARAM_A0     = 0x2,
-	RC_PARAM_A1     = 0x4,
-};
-
-enum rc_param_b {
-	RC_PARAM_B0     = 0x0,
-	RC_PARAM_B1     = 0x1,
-	RC_PARAM_B2     = 0x2,
-	RC_PARAM_B1B2   = (RC_PARAM_B1 | RC_PARAM_B2),
-};
-
-enum rc_param_c {
-	RC_PARAM_C0     = (BIT(8)),
-	RC_PARAM_C1     = (BIT(10)),
-	RC_PARAM_C2     = (BIT(10) | BIT(11)),
-	RC_PARAM_C3     = (BIT(8) | BIT(10)),
-	RC_PARAM_C4     = (BIT(8) | BIT(9)),
-	RC_PARAM_C5     = (BIT(8) | BIT(9) | BIT(10) | BIT(11)),
-};
-
-enum rc_merge_mode {
-	RC_MERGE_SINGLE_PIPE = 0x0,
-	RC_MERGE_DUAL_PIPE   = 0x1
-};
-
-struct rc_config_table {
-	enum rc_param_a param_a;
-	enum rc_param_b param_b;
-	enum rc_param_c param_c;
-	enum rc_merge_mode merge_mode;
-	enum rc_merge_mode merge_mode_en;
-};
-
-static struct rc_config_table config_table[] =  {
-	/* RC_PARAM_A0 configurations */
-	{
-		.param_a = RC_PARAM_A0,
-		.param_b = RC_PARAM_B0,
-		.param_c = RC_PARAM_C5,
-		.merge_mode = RC_MERGE_SINGLE_PIPE,
-		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
-
-	},
-	{
-		.param_a = RC_PARAM_A0,
-		.param_b = RC_PARAM_B1B2,
-		.param_c = RC_PARAM_C3,
-		.merge_mode = RC_MERGE_SINGLE_PIPE,
-		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
-
-	},
-	{
-		.param_a = RC_PARAM_A0,
-		.param_b = RC_PARAM_B1,
-		.param_c = RC_PARAM_C0,
-		.merge_mode = RC_MERGE_SINGLE_PIPE,
-		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
-
-	},
-	{
-		.param_a = RC_PARAM_A0,
-		.param_b = RC_PARAM_B2,
-		.param_c = RC_PARAM_C1,
-		.merge_mode = RC_MERGE_SINGLE_PIPE,
-		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
-
-	},
-	{
-		.param_a = RC_PARAM_A0,
-		.param_b = RC_PARAM_B0,
-		.param_c = RC_PARAM_C5,
-		.merge_mode = RC_MERGE_DUAL_PIPE,
-		.merge_mode_en = RC_MERGE_DUAL_PIPE,
-
-	},
-	{
-		.param_a = RC_PARAM_A0,
-		.param_b = RC_PARAM_B1B2,
-		.param_c = RC_PARAM_C3,
-		.merge_mode = RC_MERGE_DUAL_PIPE,
-		.merge_mode_en = RC_MERGE_DUAL_PIPE,
-
-	},
-	{
-		.param_a = RC_PARAM_A0,
-		.param_b = RC_PARAM_B1,
-		.param_c = RC_PARAM_C0,
-		.merge_mode = RC_MERGE_DUAL_PIPE,
-		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
-
-	},
-	{
-		.param_a = RC_PARAM_A0,
-		.param_b = RC_PARAM_B2,
-		.param_c = RC_PARAM_C1,
-		.merge_mode = RC_MERGE_DUAL_PIPE,
-		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
-	},
-
-	/* RC_PARAM_A1 configurations */
-	{
-		.param_a = RC_PARAM_A1,
-		.param_b = RC_PARAM_B0,
-		.param_c = RC_PARAM_C5,
-		.merge_mode = RC_MERGE_SINGLE_PIPE,
-		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
-
-	},
-	{
-		.param_a = RC_PARAM_A1,
-		.param_b = RC_PARAM_B1B2,
-		.param_c = RC_PARAM_C5,
-		.merge_mode = RC_MERGE_SINGLE_PIPE,
-		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
-
-	},
-	{
-		.param_a = RC_PARAM_A1,
-		.param_b = RC_PARAM_B1,
-		.param_c = RC_PARAM_C4,
-		.merge_mode = RC_MERGE_SINGLE_PIPE,
-		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
-
-	},
-	{
-		.param_a = RC_PARAM_A1,
-		.param_b = RC_PARAM_B2,
-		.param_c = RC_PARAM_C2,
-		.merge_mode = RC_MERGE_SINGLE_PIPE,
-		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
-
-	},
-	{
-		.param_a = RC_PARAM_A1,
-		.param_b = RC_PARAM_B0,
-		.param_c = RC_PARAM_C5,
-		.merge_mode = RC_MERGE_DUAL_PIPE,
-		.merge_mode_en = RC_MERGE_DUAL_PIPE,
-
-	},
-	{
-		.param_a = RC_PARAM_A1,
-		.param_b = RC_PARAM_B1B2,
-		.param_c = RC_PARAM_C5,
-		.merge_mode = RC_MERGE_DUAL_PIPE,
-		.merge_mode_en = RC_MERGE_DUAL_PIPE,
-
-	},
-	{
-		.param_a = RC_PARAM_A1,
-		.param_b = RC_PARAM_B1,
-		.param_c = RC_PARAM_C4,
-		.merge_mode = RC_MERGE_DUAL_PIPE,
-		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
-
-	},
-	{
-		.param_a = RC_PARAM_A1,
-		.param_b = RC_PARAM_B2,
-		.param_c = RC_PARAM_C2,
-		.merge_mode = RC_MERGE_DUAL_PIPE,
-		.merge_mode_en = RC_MERGE_SINGLE_PIPE,
-
-	},
-};
+#include "sde_hw_color_proc_common_v4.h"
 
 static inline void _sde_hw_rc_reg_write(
 		struct sde_hw_dspp *hw_dspp,
@@ -260,167 +26,6 @@ static inline void _sde_hw_rc_reg_write(
 	SDE_REG_WRITE(&hw_dspp->hw, address, value);
 }
 
-static int _sde_hw_rc_get_enable_bits(
-		enum rc_param_a param_a,
-		enum rc_param_b param_b,
-		enum rc_param_c *param_c,
-		u32 merge_mode,
-		u32 *merge_mode_en)
-{
-	int i = 0;
-
-	if (!param_c || !merge_mode_en) {
-		SDE_ERROR("invalid arguments\n");
-		return -EINVAL;
-	}
-
-	for (i = 0; i < ARRAY_SIZE(config_table); i++) {
-		if (merge_mode == config_table[i].merge_mode &&
-				param_a == config_table[i].param_a &&
-				param_b == config_table[i].param_b) {
-			*param_c = config_table[i].param_c;
-			*merge_mode_en = config_table[i].merge_mode_en;
-			SDE_DEBUG("found param_c:0x%08X, merge_mode_en:%d\n",
-					*param_c, *merge_mode_en);
-			return 0;
-		}
-	}
-	SDE_ERROR("configuration not supported");
-
-	return -EINVAL;
-}
-
-static int _sde_hw_rc_get_merge_mode(
-		const struct sde_hw_cp_cfg *hw_cfg,
-		u32 *merge_mode)
-{
-	int rc = 0;
-
-	if (!hw_cfg || !merge_mode) {
-		SDE_ERROR("invalid arguments\n");
-		return -EINVAL;
-	}
-
-	if (hw_cfg->num_of_mixers == 1)
-		*merge_mode = RC_MERGE_SINGLE_PIPE;
-	else if (hw_cfg->num_of_mixers == 2)
-		*merge_mode = RC_MERGE_DUAL_PIPE;
-	else {
-		SDE_ERROR("invalid number of mixers:%d\n",
-				hw_cfg->num_of_mixers);
-		return -EINVAL;
-	}
-
-	SDE_DEBUG("number mixers:%u, merge mode:%u\n",
-			hw_cfg->num_of_mixers, *merge_mode);
-
-	return rc;
-}
-
-static int _sde_hw_rc_get_ajusted_roi(
-		const struct sde_hw_cp_cfg *hw_cfg,
-		const struct sde_rect *pu_roi,
-		struct sde_rect *rc_roi)
-{
-	int rc = 0;
-
-	if (!hw_cfg || !pu_roi || !rc_roi) {
-		SDE_ERROR("invalid arguments\n");
-		return -EINVAL;
-	}
-
-	/*when partial update is disabled, use full screen ROI*/
-	if (pu_roi->w == 0 && pu_roi->h == 0) {
-		rc_roi->x = pu_roi->x;
-		rc_roi->y = pu_roi->y;
-		rc_roi->w = hw_cfg->panel_width;
-		rc_roi->h = hw_cfg->panel_height;
-	} else {
-		memcpy(rc_roi, pu_roi, sizeof(struct sde_rect));
-	}
-
-	SDE_EVT32(hw_cfg->displayh, hw_cfg->displayv, hw_cfg->panel_width, hw_cfg->panel_height);
-	SDE_DEBUG("displayh:%u, displayv:%u, panel_w:%u, panel_h:%u\n", hw_cfg->displayh,
-			hw_cfg->displayv, hw_cfg->panel_width, hw_cfg->panel_height);
-	SDE_DEBUG("pu_roi x:%u, y:%u, w:%u, h:%u\n", pu_roi->x, pu_roi->y,
-			pu_roi->w, pu_roi->h);
-	SDE_DEBUG("rc_roi x:%u, y:%u, w:%u, h:%u\n", rc_roi->x, rc_roi->y,
-			rc_roi->w, rc_roi->h);
-
-	return rc;
-}
-
-static int _sde_hw_rc_get_param_rb(
-		const struct drm_msm_rc_mask_cfg *rc_mask_cfg,
-		const struct sde_rect *rc_roi,
-		enum rc_param_r *param_r,
-		enum rc_param_b *param_b)
-{
-	int rc = 0;
-	int half_panel_x = 0, half_panel_w = 0;
-	int cfg_param_01 = 0, cfg_param_02 = 0;
-	int x1 = 0, x2 = 0, y1 = 0, y2 = 0;
-
-	if (!rc_mask_cfg || !rc_roi || !param_r || !param_b) {
-		SDE_ERROR("invalid arguments\n");
-		return -EINVAL;
-	}
-
-	if (rc_mask_cfg->cfg_param_03 == RC_PARAM_A1)
-		half_panel_w = rc_mask_cfg->cfg_param_04[0] +
-				rc_mask_cfg->cfg_param_04[1];
-	else if (rc_mask_cfg->cfg_param_03 == RC_PARAM_A0)
-		half_panel_w = rc_mask_cfg->cfg_param_04[0];
-	else {
-		SDE_ERROR("invalid cfg_param_03:%u\n",
-				rc_mask_cfg->cfg_param_03);
-		return -EINVAL;
-	}
-
-	cfg_param_01 = rc_mask_cfg->cfg_param_01;
-	cfg_param_02 = rc_mask_cfg->cfg_param_02;
-	x1 = rc_roi->x;
-	x2 = rc_roi->x + rc_roi->w - 1;
-	y1 = rc_roi->y;
-	y2 = rc_roi->y + rc_roi->h - 1;
-	half_panel_x = half_panel_w - 1;
-
-	SDE_DEBUG("x1:%u y1:%u x2:%u y2:%u\n", x1, y1, x2, y2);
-	SDE_DEBUG("cfg_param_01:%u cfg_param_02:%u half_panel_x:%u",
-			cfg_param_01, cfg_param_02, half_panel_x);
-
-	if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0 || half_panel_x < 0 ||
-			x1 >= x2 || y1 >= y2) {
-		SDE_ERROR("invalid coordinates\n");
-		return -EINVAL;
-	}
-
-	if (y1 <= cfg_param_01) {
-		*param_r |= RC_PARAM_R1;
-		if (x1 <= half_panel_x && x2 <= half_panel_x)
-			*param_b |= RC_PARAM_B1;
-		else if (x1 > half_panel_x && x2 > half_panel_x)
-			*param_b |= RC_PARAM_B2;
-		else
-			*param_b |= RC_PARAM_B1B2;
-	}
-
-	if (y2 >= cfg_param_02) {
-		*param_r |= RC_PARAM_R2;
-		if (x1 <= half_panel_x && x2 <= half_panel_x)
-			*param_b |= RC_PARAM_B1;
-		else if (x1 > half_panel_x && x2 > half_panel_x)
-			*param_b |= RC_PARAM_B2;
-		else
-			*param_b |= RC_PARAM_B1B2;
-	}
-
-	SDE_DEBUG("param_r:0x%08X param_b:0x%08X\n", *param_r, *param_b);
-	SDE_EVT32(rc_roi->x, rc_roi->y, rc_roi->w, rc_roi->h);
-	SDE_EVT32(x1, y1, x2, y2, cfg_param_01, cfg_param_02, half_panel_x);
-
-	return rc;
-}
 
 static int _sde_hw_rc_program_enable_bits(
 		struct sde_hw_dspp *hw_dspp,
@@ -785,7 +390,6 @@ int sde_hw_rc_check_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg)
 	struct msm_roi_list empty_roi_list;
 	struct sde_rect rc_roi, merged_roi;
 	struct drm_msm_rc_mask_cfg *rc_mask_cfg;
-	bool mask_programmed = false;
 	enum rc_param_r param_r = RC_PARAM_R0;
 	enum rc_param_b param_b = RC_PARAM_B0;
 
@@ -807,11 +411,10 @@ int sde_hw_rc_check_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg)
 		SDE_EVT32(RC_IDX(hw_dspp));
 	}
 
-	rc_mask_cfg = RC_STATE(hw_dspp).last_rc_mask_cfg;
-	mask_programmed = RC_STATE(hw_dspp).mask_programmed;
+	rc_mask_cfg = hw_dspp->rc_state.last_rc_mask_cfg;
 
 	/* early return when there is no mask in memory */
-	if (!mask_programmed || !rc_mask_cfg) {
+	if (!rc_mask_cfg || !rc_mask_cfg->cfg_param_03) {
 		SDE_DEBUG("no previous rc mask programmed\n");
 		SDE_EVT32(RC_IDX(hw_dspp));
 		return SDE_HW_RC_PU_SKIP_OP;
@@ -852,7 +455,6 @@ int sde_hw_rc_setup_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg)
 	enum rc_param_a param_a = RC_PARAM_A0;
 	enum rc_param_b param_b = RC_PARAM_B0;
 	u32 merge_mode = 0;
-	bool mask_programmed = false;
 
 	if (!hw_dspp || !hw_cfg) {
 		SDE_ERROR("invalid arguments\n");
@@ -871,12 +473,11 @@ int sde_hw_rc_setup_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg)
 		roi_list = &empty_roi_list;
 	}
 
-	rc_mask_cfg = RC_STATE(hw_dspp).last_rc_mask_cfg;
-	mask_programmed = RC_STATE(hw_dspp).mask_programmed;
-	SDE_EVT32(RC_IDX(hw_dspp), roi_list, rc_mask_cfg, mask_programmed);
+	rc_mask_cfg = hw_dspp->rc_state.last_rc_mask_cfg;
+	SDE_EVT32(RC_IDX(hw_dspp), roi_list, rc_mask_cfg, rc_mask_cfg->cfg_param_03);
 
 	/* early return when there is no mask in memory */
-	if (!mask_programmed || !rc_mask_cfg) {
+	if (!rc_mask_cfg || !rc_mask_cfg->cfg_param_03) {
 		SDE_DEBUG("no previous rc mask programmed\n");
 		SDE_EVT32(RC_IDX(hw_dspp));
 		return SDE_HW_RC_PU_SKIP_OP;
@@ -910,9 +511,8 @@ int sde_hw_rc_setup_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg)
 		return rc;
 	}
 
-	memcpy(RC_STATE(hw_dspp).last_roi_list,
+	memcpy(hw_dspp->rc_state.last_roi_list,
 			roi_list, sizeof(struct msm_roi_list));
-	RC_STATE(hw_dspp).roi_programmed = true;
 
 	return 0;
 }
@@ -925,8 +525,9 @@ int sde_hw_rc_setup_mask(struct sde_hw_dspp *hw_dspp, void *cfg)
 	struct sde_rect rc_roi, merged_roi;
 	struct msm_roi_list *last_roi_list;
 	u32 merge_mode = 0;
-	bool roi_programmed = false;
 	u64 mask_w = 0, mask_h = 0, panel_w = 0, panel_h = 0;
+	u32 data = 0, cfg_param_07 = 0;
+	int i = 0;
 
 	if (!hw_dspp || !hw_cfg) {
 		SDE_ERROR("invalid arguments\n");
@@ -937,15 +538,13 @@ int sde_hw_rc_setup_mask(struct sde_hw_dspp *hw_dspp, void *cfg)
 		SDE_DEBUG("RC feature disabled\n");
 		_sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG1, 0);
 
-		memset(RC_STATE(hw_dspp).last_rc_mask_cfg, 0,
+		memset(hw_dspp->rc_state.last_rc_mask_cfg, 0,
 				sizeof(struct drm_msm_rc_mask_cfg));
-		RC_STATE(hw_dspp).mask_programmed = false;
-		memset(RC_STATE(hw_dspp).last_roi_list, 0,
+		memset(hw_dspp->rc_state.last_roi_list, 0,
 				sizeof(struct msm_roi_list));
-		RC_STATE(hw_dspp).roi_programmed = false;
-		SDE_EVT32(RC_IDX(hw_dspp), RC_STATE(hw_dspp).last_rc_mask_cfg,
-				RC_STATE(hw_dspp).mask_programmed,
-				RC_STATE(hw_dspp).roi_programmed);
+		SDE_EVT32(RC_IDX(hw_dspp), hw_dspp->rc_state.last_rc_mask_cfg,
+				hw_dspp->rc_state.last_rc_mask_cfg->cfg_param_03,
+				hw_dspp->rc_state.last_roi_list->num_rects);
 		return 0;
 	}
 
@@ -956,8 +555,7 @@ int sde_hw_rc_setup_mask(struct sde_hw_dspp *hw_dspp, void *cfg)
 	}
 
 	rc_mask_cfg = hw_cfg->payload;
-	last_roi_list = RC_STATE(hw_dspp).last_roi_list;
-	roi_programmed = RC_STATE(hw_dspp).roi_programmed;
+	last_roi_list = hw_dspp->rc_state.last_roi_list;
 
 	mask_w = rc_mask_cfg->width;
 	mask_h = rc_mask_cfg->height;
@@ -972,14 +570,14 @@ int sde_hw_rc_setup_mask(struct sde_hw_dspp *hw_dspp, void *cfg)
 		return -EINVAL;
 	}
 
-	if (!roi_programmed) {
+	if (!last_roi_list || !last_roi_list->num_rects) {
 		SDE_DEBUG("full frame update\n");
 		memset(&merged_roi, 0, sizeof(struct sde_rect));
 	} else {
 		SDE_DEBUG("partial frame update\n");
 		sde_kms_rect_merge_rectangles(last_roi_list, &merged_roi);
 	}
-	SDE_EVT32(RC_IDX(hw_dspp), roi_programmed);
+	SDE_EVT32(RC_IDX(hw_dspp), last_roi_list->num_rects);
 
 	rc = _sde_hw_rc_get_ajusted_roi(hw_cfg, &merged_roi, &rc_roi);
 	if (rc) {
@@ -1006,115 +604,47 @@ int sde_hw_rc_setup_mask(struct sde_hw_dspp *hw_dspp, void *cfg)
 		return rc;
 	}
 
-	memcpy(RC_STATE(hw_dspp).last_rc_mask_cfg, rc_mask_cfg,
-			sizeof(struct drm_msm_rc_mask_cfg));
-	RC_STATE(hw_dspp).mask_programmed = true;
-
-	return 0;
-}
-
-int sde_hw_rc_setup_data_dma(struct sde_hw_dspp *hw_dspp, void *cfg)
-{
-	int rc = 0;
-	struct sde_hw_cp_cfg *hw_cfg = cfg;
-	struct drm_msm_rc_mask_cfg *rc_mask_cfg;
-
-	if (!hw_dspp || !hw_cfg) {
-		SDE_ERROR("invalid arguments\n");
-		return -EINVAL;
-	}
-
-	if ((hw_cfg->len == 0 && hw_cfg->payload == NULL)) {
-		SDE_DEBUG("RC feature disabled, skip data programming\n");
-		SDE_EVT32(RC_IDX(hw_dspp));
-		return 0;
-	}
-
-	if (hw_cfg->len != sizeof(struct drm_msm_rc_mask_cfg) ||
-			!hw_cfg->payload) {
-		SDE_ERROR("invalid payload\n");
-		return -EINVAL;
-	}
-
-	rc_mask_cfg = hw_cfg->payload;
-
-	if (rc_mask_cfg->flags & SDE_HW_RC_SKIP_DATA_PROG) {
-		SDE_DEBUG("skip data programming\n");
-		SDE_EVT32(RC_IDX(hw_dspp));
-		return 0;
-	}
-
-	rc = reg_dmav1_setup_rc_datav1(hw_dspp, cfg);
-	if (rc) {
-		SDE_ERROR("unable to setup rc with dma, rc:%d\n", rc);
-		return rc;
-	}
-
-	return rc;
-}
+	/* rc data should be programmed once if dspp are in multi-pipe mode */
+	if (!(rc_mask_cfg->flags & SDE_HW_RC_SKIP_DATA_PROG) &&
+		(hw_dspp->cap->sblk->rc.idx % hw_cfg->num_of_mixers == 0)) {
 
-int sde_hw_rc_setup_data_ahb(struct sde_hw_dspp *hw_dspp, void *cfg)
-{
-	int rc = 0, i = 0;
-	u32 data = 0, cfg_param_07 = 0;
-	struct sde_hw_cp_cfg *hw_cfg = cfg;
-	struct drm_msm_rc_mask_cfg *rc_mask_cfg;
+		cfg_param_07 = rc_mask_cfg->cfg_param_07;
+		SDE_DEBUG("cfg_param_07:%u\n", cfg_param_07);
 
-	if (!hw_dspp || !hw_cfg) {
-		SDE_ERROR("invalid arguments\n");
-		return -EINVAL;
-	}
+		for (i = 0; i < rc_mask_cfg->cfg_param_08; i++) {
+			SDE_DEBUG("cfg_param_09[%d] = 0x%016llX at %u\n", i,
+					rc_mask_cfg->cfg_param_09[i], i + cfg_param_07);
 
-	if ((hw_cfg->len == 0 && hw_cfg->payload == NULL)) {
-		SDE_DEBUG("rc feature disabled, skip data programming\n");
-		SDE_EVT32(RC_IDX(hw_dspp));
-		return 0;
-	}
-
-	if (hw_cfg->len != sizeof(struct drm_msm_rc_mask_cfg) ||
-			!hw_cfg->payload) {
-		SDE_ERROR("invalid payload\n");
-		return -EINVAL;
-	}
-
-	rc_mask_cfg = hw_cfg->payload;
-
-	if (rc_mask_cfg->flags & SDE_HW_RC_SKIP_DATA_PROG) {
+			data = (i == 0) ? (BIT(30) | (cfg_param_07 << 18)) : 0;
+			data |= (rc_mask_cfg->cfg_param_09[i] & 0x3FFFF);
+			_sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG10, data);
+			data = ((rc_mask_cfg->cfg_param_09[i] >>
+					SDE_HW_RC_DATA_REG_SIZE) & 0x3FFFF);
+			_sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG10, data);
+		}
+	} else {
 		SDE_DEBUG("skip data programming\n");
 		SDE_EVT32(RC_IDX(hw_dspp));
-		return 0;
 	}
 
-	cfg_param_07 = rc_mask_cfg->cfg_param_07;
-	SDE_DEBUG("cfg_param_07:%u\n", cfg_param_07);
-
-	for (i = 0; i < rc_mask_cfg->cfg_param_08; i++) {
-		SDE_DEBUG("cfg_param_09[%d] = 0x%016llX at %u\n", i,
-				rc_mask_cfg->cfg_param_09[i], i + cfg_param_07);
-
-		data = (i == 0) ? (BIT(30) | (cfg_param_07 << 18)) : 0;
-		data |= (rc_mask_cfg->cfg_param_09[i] & 0x3FFFF);
-		_sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG10, data);
-		data = ((rc_mask_cfg->cfg_param_09[i] >>
-				SDE_HW_RC_DATA_REG_SIZE) & 0x3FFFF);
-		_sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG10, data);
-	}
+	memcpy(hw_dspp->rc_state.last_rc_mask_cfg, rc_mask_cfg,
+			sizeof(struct drm_msm_rc_mask_cfg));
 
-	return rc;
+	return 0;
 }
 
 int sde_hw_rc_init(struct sde_hw_dspp *hw_dspp)
 {
 	int rc = 0;
 
-	RC_STATE(hw_dspp).last_roi_list = kzalloc(
+	hw_dspp->rc_state.last_roi_list = kzalloc(
 			sizeof(struct msm_roi_list), GFP_KERNEL);
-	if (!RC_STATE(hw_dspp).last_roi_list)
+	if (!hw_dspp->rc_state.last_roi_list)
 		return -ENOMEM;
 
-	RC_STATE(hw_dspp).last_rc_mask_cfg = kzalloc(
+	hw_dspp->rc_state.last_rc_mask_cfg = kzalloc(
 			sizeof(struct drm_msm_rc_mask_cfg), GFP_KERNEL);
-	if (!RC_STATE(hw_dspp).last_rc_mask_cfg)
+	if (!hw_dspp->rc_state.last_rc_mask_cfg)
 		return -ENOMEM;
 
 	return rc;

+ 1 - 16
msm/sde/sde_hw_rc.h

@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #ifndef _SDE_HW_RC_H
@@ -52,20 +53,4 @@ int sde_hw_rc_check_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg);
  */
 int sde_hw_rc_setup_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg);
 
-/**
- * sde_hw_rc_setup_data_ahb - Program mask data with AHB
- * @hw_dspp: DSPP instance.
- * @cfg: Pointer to configuration blob.
- * Return: 0 on success, non-zero otherwise.
- */
-int sde_hw_rc_setup_data_ahb(struct sde_hw_dspp *hw_dspp, void *cfg);
-
-/**
- * sde_hw_rc_setup_data_dma - Program mask data with DMA
- * @hw_dspp: DSPP instance.
- * @cfg: Pointer to configuration blob.
- * Return: 0 on success, non-zero otherwise.
- */
-int sde_hw_rc_setup_data_dma(struct sde_hw_dspp *hw_dspp, void *cfg);
-
 #endif

+ 3 - 1
msm/sde/sde_hw_reg_dma_v1.c

@@ -928,7 +928,9 @@ int init_v12(struct sde_hw_reg_dma *cfg)
 	v1_supported[LTM_INIT] = GRP_LTM_HW_BLK_SELECT;
 	v1_supported[LTM_ROI] = GRP_LTM_HW_BLK_SELECT;
 	v1_supported[LTM_VLUT] = GRP_LTM_HW_BLK_SELECT;
-	v1_supported[RC_DATA] = (GRP_DSPP_HW_BLK_SELECT |
+	v1_supported[RC_MASK_CFG] = (GRP_DSPP_HW_BLK_SELECT |
+			GRP_MDSS_HW_BLK_SELECT);
+	v1_supported[RC_PU_CFG] = (GRP_DSPP_HW_BLK_SELECT |
 			GRP_MDSS_HW_BLK_SELECT);
 	v1_supported[SPR_INIT] = (GRP_DSPP_HW_BLK_SELECT |
 			GRP_MDSS_HW_BLK_SELECT);

+ 454 - 47
msm/sde/sde_hw_reg_dma_v1_color_proc.c

@@ -14,6 +14,7 @@
 #include "sde_hw_lm.h"
 #include "sde_dbg.h"
 #include "sde_hw_util.h"
+#include "sde_kms.h"
 
 /* Reserve space of 128 words for LUT dma payload set-up */
 #define REG_DMA_HEADERS_BUFFER_SZ (sizeof(u32) * 128)
@@ -69,7 +70,10 @@
 #define MEMCOLOR_MEM_SIZE ((sizeof(struct drm_msm_memcol)) + \
 		REG_DMA_HEADERS_BUFFER_SZ)
 
-#define RC_MEM_SIZE ((RC_DATA_SIZE_MAX * 2 * sizeof(u32)) + \
+#define RC_MASK_CFG_SIZE (sizeof(struct drm_msm_rc_mask_cfg) + \
+		REG_DMA_HEADERS_BUFFER_SZ)
+//TBD: exact size calculations
+#define RC_PU_CFG_SIZE ((64 * sizeof(u32)) + \
 		REG_DMA_HEADERS_BUFFER_SZ)
 
 #define QSEED3_MEM_SIZE (sizeof(struct sde_hw_scaler3_cfg) + \
@@ -158,7 +162,8 @@ static u32 feature_map[SDE_DSPP_MAX] = {
 	[SDE_DSPP_DITHER] = REG_DMA_FEATURES_MAX,
 	[SDE_DSPP_HIST] = REG_DMA_FEATURES_MAX,
 	[SDE_DSPP_AD] = REG_DMA_FEATURES_MAX,
-	[SDE_DSPP_RC] = RC_DATA,
+	/* RC can be mapped to RC_MASK_CFG & RC_PU_CFG */
+	[SDE_DSPP_RC] = RC_MASK_CFG,
 	[SDE_DSPP_DEMURA] = DEMURA_CFG,
 	[SDE_DSPP_DEMURA_CFG0_PARAM2] = DEMURA_CFG0_PARAM2,
 };
@@ -187,7 +192,8 @@ static u32 feature_reg_dma_sz[SDE_DSPP_MAX] = {
 	[SDE_DSPP_HSIC] = HSIC_MEM_SIZE,
 	[SDE_DSPP_SIXZONE] = SIXZONE_MEM_SIZE,
 	[SDE_DSPP_MEMCOLOR] = MEMCOLOR_MEM_SIZE,
-	[SDE_DSPP_RC] = RC_MEM_SIZE,
+	[SDE_DSPP_RC] = RC_MASK_CFG_SIZE,
+	[SDE_DSPP_RC_PU] = RC_PU_CFG_SIZE,
 	[SDE_DSPP_SPR] = SPR_INIT_MEM_SIZE,
 	[SDE_DSPP_DEMURA] = DEMURA_MEM_SIZE,
 	[SDE_DSPP_DEMURA_CFG0_PARAM2] = DEMURA_CFG0_PARAM2_MEM_SIZE,
@@ -281,6 +287,187 @@ static void _perform_sbdma_kickoff(struct sde_hw_dspp *ctx,
 		struct sde_hw_reg_dma_ops *dma_ops,
 		u32 blk, enum sde_reg_dma_features feature);
 
+static inline int _reg_dmav1_rc_write(struct sde_hw_dspp *ctx,
+		u32 reg_offset, u32 val,
+		struct sde_hw_reg_dma_ops *dma_ops,
+		enum sde_reg_dma_features feature)
+{
+	int rc = 0;
+	struct sde_reg_dma_setup_ops_cfg dma_write_cfg;
+	uint32_t abs_offset;
+
+	abs_offset = ctx->hw.blk_off + ctx->cap->sblk->rc.base + reg_offset;
+	REG_DMA_INIT_OPS(dma_write_cfg, MDSS, feature,
+		dspp_buf[feature][ctx->idx]);
+	REG_DMA_SETUP_OPS(dma_write_cfg, abs_offset, &val,
+		sizeof(__u32), REG_SINGLE_WRITE, 0, 0, 0);
+	rc = dma_ops->setup_payload(&dma_write_cfg);
+	if (rc)
+		SDE_ERROR("rc dma write failed ret %d\n", rc);
+	return rc;
+}
+
+static int _reg_dmav1_rc_program_enable_bits(
+		struct sde_hw_dspp *hw_dspp,
+		struct drm_msm_rc_mask_cfg *rc_mask_cfg,
+		enum rc_param_a param_a,
+		enum rc_param_b param_b,
+		enum rc_param_r param_r,
+		int merge_mode,
+		struct sde_rect *rc_roi,
+		struct sde_hw_reg_dma_ops *dma_ops,
+		enum sde_reg_dma_features feature)
+{
+	int rc = 0;
+	u32 val = 0, param_c = 0, rc_merge_mode = 0, ystart = 0;
+	u64 flags = 0, mask_w = 0, mask_h = 0;
+	bool r1_valid = false, r2_valid = false;
+	bool pu_in_r1 = false, pu_in_r2 = false;
+	bool r1_enable = false, r2_enable = false;
+
+	if (!hw_dspp || !rc_mask_cfg || !rc_roi) {
+		SDE_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	rc = _sde_hw_rc_get_enable_bits(param_a, param_b, &param_c,
+			merge_mode, &rc_merge_mode);
+	if (rc) {
+		SDE_ERROR("invalid enable bits, rc:%d\n", rc);
+		return rc;
+	}
+
+	flags = rc_mask_cfg->flags;
+	mask_w = rc_mask_cfg->width;
+	mask_h = rc_mask_cfg->height;
+	r1_valid = ((flags & SDE_HW_RC_DISABLE_R1) != SDE_HW_RC_DISABLE_R1);
+	r2_valid = ((flags & SDE_HW_RC_DISABLE_R2) != SDE_HW_RC_DISABLE_R2);
+	pu_in_r1 = (param_r == RC_PARAM_R1 || param_r == RC_PARAM_R1R2);
+	pu_in_r2 = (param_r == RC_PARAM_R2 || param_r == RC_PARAM_R1R2);
+	r1_enable = (r1_valid && pu_in_r1);
+	r2_enable = (r2_valid && pu_in_r2);
+
+	if (r1_enable)
+		val |= BIT(0);
+
+	if (r2_enable)
+		val |= BIT(4);
+
+	/*corner case for partial update in R2 region*/
+	if (!r1_enable && r2_enable)
+		ystart = rc_roi->y;
+
+	SDE_DEBUG("idx:%d w:%d h:%d flags:%x, R1:%d, R2:%d, PU R1:%d, PU R2:%d, Y_START:%d\n",
+		RC_IDX(hw_dspp), mask_w, mask_h, flags, r1_valid, r2_valid, pu_in_r1,
+		pu_in_r2, ystart);
+	SDE_EVT32(RC_IDX(hw_dspp), mask_w, mask_h, flags, r1_valid, r2_valid, pu_in_r1, pu_in_r2,
+		ystart);
+
+	val |= param_c;
+	rc = _reg_dmav1_rc_write(hw_dspp, SDE_HW_RC_REG1, val, dma_ops, feature);
+	rc = _reg_dmav1_rc_write(hw_dspp, SDE_HW_RC_REG13, ystart, dma_ops, feature);
+	rc = _reg_dmav1_rc_write(hw_dspp, SDE_HW_RC_REG9, rc_merge_mode, dma_ops, feature);
+
+	return rc;
+}
+
+static int _reg_dmav1_rc_program_roi(struct sde_hw_dspp *hw_dspp,
+		struct drm_msm_rc_mask_cfg *rc_mask_cfg,
+		int merge_mode,
+		struct sde_rect *rc_roi,
+		struct sde_hw_reg_dma_ops *dma_ops,
+		enum sde_reg_dma_features feature)
+{
+	int rc = 0;
+	u32 val2 = 0, val3 = 0, val4 = 0;
+	enum rc_param_r param_r = RC_PARAM_R0;
+	enum rc_param_a param_a = RC_PARAM_A0;
+	enum rc_param_b param_b = RC_PARAM_B0;
+
+	if (!hw_dspp || !rc_mask_cfg || !rc_roi) {
+		SDE_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	rc = _sde_hw_rc_get_param_rb(rc_mask_cfg, rc_roi, &param_r,
+			&param_b);
+	if (rc) {
+		SDE_ERROR("invalid rc roi, rc:%d\n", rc);
+		return rc;
+	}
+
+	param_a = rc_mask_cfg->cfg_param_03;
+	rc = _reg_dmav1_rc_program_enable_bits(hw_dspp, rc_mask_cfg,
+		param_a, param_b, param_r, merge_mode, rc_roi,
+		dma_ops, feature);
+	if (rc) {
+		SDE_ERROR("failed to program enable bits, rc:%d\n", rc);
+		return rc;
+	}
+
+	val2 = ((rc_mask_cfg->cfg_param_01 & 0x0000FFFF) |
+			((rc_mask_cfg->cfg_param_02 << 16) & 0xFFFF0000));
+	if (param_a == RC_PARAM_A1) {
+		val3 = (rc_mask_cfg->cfg_param_04[0] |
+				(rc_mask_cfg->cfg_param_04[1] << 16));
+		val4 = (rc_mask_cfg->cfg_param_04[2] |
+				(rc_mask_cfg->cfg_param_04[3] << 16));
+	} else if (param_a == RC_PARAM_A0) {
+		val3 = (rc_mask_cfg->cfg_param_04[0]);
+		val4 = (rc_mask_cfg->cfg_param_04[1]);
+	}
+
+	rc = _reg_dmav1_rc_write(hw_dspp, SDE_HW_RC_REG2, val2, dma_ops, feature);
+	rc = _reg_dmav1_rc_write(hw_dspp, SDE_HW_RC_REG3, val3, dma_ops, feature);
+	rc = _reg_dmav1_rc_write(hw_dspp, SDE_HW_RC_REG4, val4, dma_ops, feature);
+
+	return rc;
+}
+
+static int _reg_dmav1_rc_program_data_offset(
+		struct sde_hw_dspp *hw_dspp,
+		struct drm_msm_rc_mask_cfg *rc_mask_cfg,
+		struct sde_hw_reg_dma_ops *dma_ops,
+		enum sde_reg_dma_features feature)
+{
+	int rc = 0;
+	u32 val5 = 0, val6 = 0, val7 = 0, val8 = 0;
+	u32 cfg_param_07;
+
+	if (!hw_dspp || !rc_mask_cfg) {
+		SDE_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	cfg_param_07 = rc_mask_cfg->cfg_param_07;
+	if (rc_mask_cfg->cfg_param_03 == RC_PARAM_A1) {
+		val5 = ((rc_mask_cfg->cfg_param_05[0] + cfg_param_07) |
+				((rc_mask_cfg->cfg_param_05[1] + cfg_param_07)
+				<< 16));
+		val6 = ((rc_mask_cfg->cfg_param_05[2] + cfg_param_07)|
+				((rc_mask_cfg->cfg_param_05[3] + cfg_param_07)
+				<< 16));
+		val7 = ((rc_mask_cfg->cfg_param_06[0] + cfg_param_07) |
+				((rc_mask_cfg->cfg_param_06[1] + cfg_param_07)
+				<< 16));
+		val8 = ((rc_mask_cfg->cfg_param_06[2] + cfg_param_07) |
+				((rc_mask_cfg->cfg_param_06[3] + cfg_param_07)
+				<< 16));
+	} else if (rc_mask_cfg->cfg_param_03 == RC_PARAM_A0) {
+		val5 = (rc_mask_cfg->cfg_param_05[0] + cfg_param_07);
+		val6 = (rc_mask_cfg->cfg_param_05[1] + cfg_param_07);
+		val7 = (rc_mask_cfg->cfg_param_06[0] + cfg_param_07);
+		val8 = (rc_mask_cfg->cfg_param_06[1] + cfg_param_07);
+	}
+
+	rc = _reg_dmav1_rc_write(hw_dspp, SDE_HW_RC_REG5, val5, dma_ops, feature);
+	rc = _reg_dmav1_rc_write(hw_dspp, SDE_HW_RC_REG6, val6, dma_ops, feature);
+	rc = _reg_dmav1_rc_write(hw_dspp, SDE_HW_RC_REG7, val7, dma_ops, feature);
+	rc = _reg_dmav1_rc_write(hw_dspp, SDE_HW_RC_REG8, val8, dma_ops, feature);
+
+	return rc;
+}
+
 static int reg_dma_buf_init(struct sde_reg_dma_buffer **buf, u32 size)
 {
 	struct sde_hw_reg_dma_ops *dma_ops;
@@ -361,6 +548,16 @@ static int _reg_dma_init_dspp_feature_buf(int feature, enum sde_dspp idx)
 		rc = reg_dma_buf_init(
 			&dspp_buf[MEMC_PROT][idx],
 			feature_reg_dma_sz[feature]);
+	} else if (feature == SDE_DSPP_RC) {
+		rc = reg_dma_buf_init(
+			&dspp_buf[RC_MASK_CFG][idx],
+			feature_reg_dma_sz[feature]);
+		if (rc)
+			return rc;
+
+		rc = reg_dma_buf_init(
+			&dspp_buf[RC_PU_CFG][idx],
+			feature_reg_dma_sz[SDE_DSPP_RC_PU]);
 	} else {
 		rc = reg_dma_buf_init(
 			&dspp_buf[feature_map[feature]][idx],
@@ -1161,87 +1358,297 @@ void reg_dmav1_setup_dspp_igcv31(struct sde_hw_dspp *ctx, void *cfg)
 		DRM_ERROR("failed to kick off ret %d\n", rc);
 }
 
-int reg_dmav1_setup_rc_datav1(struct sde_hw_dspp *ctx, void *cfg)
+int reg_dmav1_setup_rc_pu_configv1(struct sde_hw_dspp *ctx, void *cfg)
 {
-	struct drm_msm_rc_mask_cfg *rc_mask_cfg;
+	int rc = 0;
 	struct sde_hw_reg_dma_ops *dma_ops;
 	struct sde_reg_dma_kickoff_cfg kick_off;
-	struct sde_hw_cp_cfg *hw_cfg = cfg;
 	struct sde_reg_dma_setup_ops_cfg dma_write_cfg;
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	struct drm_msm_rc_mask_cfg *rc_mask_cfg;
+	struct msm_roi_list *roi_list;
+	struct msm_roi_list empty_roi_list;
+	struct sde_rect rc_roi, merged_roi;
+	enum rc_param_r param_r = RC_PARAM_R0;
+	enum rc_param_a param_a = RC_PARAM_A0;
+	enum rc_param_b param_b = RC_PARAM_B0;
+	u32 merge_mode = 0;
+
+	if (!ctx || !cfg) {
+		SDE_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	if (hw_cfg->len != sizeof(struct sde_drm_roi_v1)) {
+		SDE_ERROR("invalid payload size\n");
+		return -EINVAL;
+	}
+
+	rc = reg_dma_dspp_check(ctx, cfg, RC_PU_CFG);
+	if (rc) {
+		SDE_ERROR("invalid dma dspp check rc = %d\n", rc);
+		return -EINVAL;
+	}
+
+	dma_ops = sde_reg_dma_get_ops();
+	dma_ops->reset_reg_dma_buf(dspp_buf[RC_PU_CFG][ctx->idx]);
+	REG_DMA_INIT_OPS(dma_write_cfg, MDSS, RC_PU_CFG,
+		dspp_buf[RC_PU_CFG][ctx->idx]);
+
+	REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0, 0);
+	rc = dma_ops->setup_payload(&dma_write_cfg);
+	if (rc) {
+		SDE_ERROR("write decode select failed ret %d\n", rc);
+		return -ENOMEM;
+	}
+
+	roi_list = hw_cfg->payload;
+	if (!roi_list) {
+		SDE_DEBUG("full frame update\n");
+		memset(&empty_roi_list, 0, sizeof(struct msm_roi_list));
+		roi_list = &empty_roi_list;
+	}
+
+	rc_mask_cfg = ctx->rc_state.last_rc_mask_cfg;
+	SDE_EVT32(RC_IDX(ctx), roi_list, rc_mask_cfg, rc_mask_cfg->cfg_param_03);
+
+	/* early return when there is no mask in memory */
+	if (!rc_mask_cfg || !rc_mask_cfg->cfg_param_03) {
+		SDE_DEBUG("no previous rc mask programmed\n");
+		SDE_EVT32(RC_IDX(ctx));
+		return SDE_HW_RC_PU_SKIP_OP;
+	}
+
+	sde_kms_rect_merge_rectangles(roi_list, &merged_roi);
+	rc = _sde_hw_rc_get_ajusted_roi(hw_cfg, &merged_roi, &rc_roi);
+	if (rc) {
+		SDE_ERROR("failed to get adjusted roi, rc:%d\n", rc);
+		return rc;
+	}
+
+	rc = _sde_hw_rc_get_merge_mode(hw_cfg, &merge_mode);
+	if (rc) {
+		SDE_ERROR("invalid merge_mode, rc:%d\n", rc);
+		return rc;
+	}
+
+	rc = _sde_hw_rc_get_param_rb(rc_mask_cfg, &rc_roi, &param_r,
+			&param_b);
+	if (rc) {
+		SDE_ERROR("invalid roi, rc:%d\n", rc);
+		return rc;
+	}
+
+	param_a = rc_mask_cfg->cfg_param_03;
+	rc = _reg_dmav1_rc_program_enable_bits(ctx, rc_mask_cfg,
+		param_a, param_b, param_r, merge_mode, &rc_roi,
+		dma_ops, RC_PU_CFG);
+	if (rc) {
+		SDE_ERROR("failed to program enable bits, rc:%d\n", rc);
+		return rc;
+	}
+
+	/* defer trigger to kickoff phase */
+	REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl,
+		dspp_buf[RC_PU_CFG][ctx->idx], REG_DMA_WRITE,
+		DMA_CTL_QUEUE0, WRITE_TRIGGER, RC_PU_CFG);
+	rc = dma_ops->kick_off(&kick_off);
+	if (rc) {
+		SDE_ERROR("failed to kick off ret %d\n", rc);
+		return rc;
+	}
+	LOG_FEATURE_ON;
+
+	memcpy(ctx->rc_state.last_roi_list,
+			roi_list, sizeof(struct msm_roi_list));
+
+	return 0;
+}
+
+int reg_dmav1_setup_rc_mask_configv1(struct sde_hw_dspp *ctx, void *cfg)
+{
 	int rc = 0;
-	u32 i = 0;
+	struct sde_hw_reg_dma_ops *dma_ops;
+	struct sde_reg_dma_kickoff_cfg kick_off;
+	struct sde_reg_dma_setup_ops_cfg dma_write_cfg;
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	struct drm_msm_rc_mask_cfg *rc_mask_cfg;
+	struct sde_rect rc_roi, merged_roi;
+	struct msm_roi_list *last_roi_list;
+	u32 merge_mode = 0;
+	u64 mask_w = 0, mask_h = 0, panel_w = 0, panel_h = 0;
 	u32 *data = NULL;
+	u32 cfg_param_07; u64 cfg_param_09;
 	u32 buf_sz = 0, abs_offset = 0;
-	u32 cfg_param_07;
-	u64 cfg_param_09;
+	int i = 0;
 
-	rc = reg_dma_dspp_check(ctx, cfg, RC_DATA);
-	if (rc) {
-		DRM_ERROR("invalid dma dspp check rc = %d\n", rc);
+	if (!ctx || !cfg) {
+		SDE_ERROR("invalid arguments\n");
 		return -EINVAL;
 	}
 
-	if (hw_cfg->len != sizeof(struct drm_msm_rc_mask_cfg)) {
-		DRM_ERROR("invalid size of payload len %d exp %zd\n",
-			  hw_cfg->len, sizeof(struct drm_msm_rc_mask_cfg));
+	rc = reg_dma_dspp_check(ctx, cfg, RC_MASK_CFG);
+	if (rc) {
+		SDE_ERROR("invalid dma dspp check rc = %d\n", rc);
 		return -EINVAL;
 	}
 
-	rc_mask_cfg = hw_cfg->payload;
-	buf_sz = rc_mask_cfg->cfg_param_08 * 2 * sizeof(u32);
-	abs_offset = ctx->hw.blk_off + ctx->cap->sblk->rc.base + 0x28;
-
 	dma_ops = sde_reg_dma_get_ops();
-	dma_ops->reset_reg_dma_buf(dspp_buf[RC_DATA][ctx->idx]);
-	REG_DMA_INIT_OPS(dma_write_cfg, MDSS, RC_DATA,
-		dspp_buf[RC_DATA][ctx->idx]);
+	dma_ops->reset_reg_dma_buf(dspp_buf[RC_MASK_CFG][ctx->idx]);
+	REG_DMA_INIT_OPS(dma_write_cfg, MDSS, RC_MASK_CFG,
+		dspp_buf[RC_MASK_CFG][ctx->idx]);
 
 	REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0, 0);
 	rc = dma_ops->setup_payload(&dma_write_cfg);
 	if (rc) {
-		DRM_ERROR("write decode select failed ret %d\n", rc);
+		SDE_ERROR("write decode select failed ret %d\n", rc);
 		return -ENOMEM;
 	}
 
-	DRM_DEBUG_DRIVER("allocating %u bytes of memory for dma\n", buf_sz);
-	data = kvzalloc(buf_sz, GFP_KERNEL);
-	if (!data) {
-		DRM_ERROR("memory allocation failed ret %d\n", rc);
-		return -ENOMEM;
+	if ((hw_cfg->len == 0 && hw_cfg->payload == NULL)) {
+		SDE_DEBUG("RC feature disabled\n");
+		rc = _reg_dmav1_rc_write(ctx, SDE_HW_RC_REG1, 0, dma_ops, RC_MASK_CFG);
+		memset(ctx->rc_state.last_rc_mask_cfg, 0,
+				sizeof(struct drm_msm_rc_mask_cfg));
+		memset(ctx->rc_state.last_roi_list, 0,
+				sizeof(struct msm_roi_list));
+		SDE_EVT32(RC_IDX(ctx), ctx->rc_state.last_rc_mask_cfg,
+				ctx->rc_state.last_rc_mask_cfg->cfg_param_03,
+				ctx->rc_state.last_roi_list->num_rects);
+
+		REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl,
+			dspp_buf[RC_MASK_CFG][ctx->idx], REG_DMA_WRITE,
+			DMA_CTL_QUEUE0, WRITE_TRIGGER, RC_MASK_CFG);
+		rc = dma_ops->kick_off(&kick_off);
+		if (rc) {
+			SDE_ERROR("failed to kick off ret %d\n", rc);
+			return rc;
+		}
+		LOG_FEATURE_OFF;
+		return 0;
 	}
 
-	cfg_param_07 = rc_mask_cfg->cfg_param_07;
-	for (i = 0; i < rc_mask_cfg->cfg_param_08; i++) {
-		cfg_param_09 =  rc_mask_cfg->cfg_param_09[i];
-		DRM_DEBUG_DRIVER("cfg_param_09[%d] = 0x%016llX at %u\n", i,
-				 cfg_param_09,
-				 i + cfg_param_07);
-		data[i * 2] = (i == 0) ? (BIT(30) | (cfg_param_07 << 18)) : 0;
-		data[i * 2] |= (cfg_param_09 & 0x3FFFF);
-		data[i * 2 + 1] = ((cfg_param_09 >> 18) & 0x3FFFF);
+	if (hw_cfg->len != sizeof(struct drm_msm_rc_mask_cfg) ||
+			!hw_cfg->payload) {
+		SDE_ERROR("invalid payload\n");
+		return -EINVAL;
 	}
 
-	REG_DMA_SETUP_OPS(dma_write_cfg, abs_offset, data, buf_sz,
-			REG_BLK_WRITE_INC, 0, 0, 0);
-	rc = dma_ops->setup_payload(&dma_write_cfg);
+	rc_mask_cfg = hw_cfg->payload;
+	last_roi_list = ctx->rc_state.last_roi_list;
+
+	mask_w = rc_mask_cfg->width;
+	mask_h = rc_mask_cfg->height;
+	panel_w =  hw_cfg->panel_width;
+	panel_h = hw_cfg->panel_height;
+
+	if ((panel_w != mask_w || panel_h != mask_h)) {
+		SDE_ERROR("RC-%d mask: w %d h %d panel: w %d h %d mismatch\n",
+				RC_IDX(ctx), mask_w, mask_h, panel_w, panel_h);
+		SDE_EVT32(1);
+		rc = _reg_dmav1_rc_write(ctx, SDE_HW_RC_REG1, 0, dma_ops, RC_MASK_CFG);
+
+		REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl,
+			dspp_buf[RC_MASK_CFG][ctx->idx], REG_DMA_WRITE,
+			DMA_CTL_QUEUE0, WRITE_TRIGGER, RC_MASK_CFG);
+		rc = dma_ops->kick_off(&kick_off);
+		if (rc) {
+			SDE_ERROR("failed to kick off ret %d\n", rc);
+			return -EINVAL;
+		}
+		LOG_FEATURE_OFF;
+		return -EINVAL;
+	}
+
+	if (!last_roi_list || !last_roi_list->num_rects) {
+		SDE_DEBUG("full frame update\n");
+		memset(&merged_roi, 0, sizeof(struct sde_rect));
+	} else {
+		SDE_DEBUG("partial frame update\n");
+		sde_kms_rect_merge_rectangles(last_roi_list, &merged_roi);
+	}
+	SDE_EVT32(RC_IDX(ctx), last_roi_list->num_rects);
+
+	rc = _sde_hw_rc_get_ajusted_roi(hw_cfg, &merged_roi, &rc_roi);
 	if (rc) {
-		DRM_ERROR("rc dma write failed ret %d\n", rc);
-		goto exit;
+		SDE_ERROR("failed to get adjusted roi, rc:%d\n", rc);
+		return rc;
+	}
+
+	rc = _sde_hw_rc_get_merge_mode(hw_cfg, &merge_mode);
+	if (rc) {
+		SDE_ERROR("invalid merge_mode, rc:%d\n", rc);
+		return rc;
+	}
+
+	rc = _reg_dmav1_rc_program_roi(ctx, rc_mask_cfg,
+		merge_mode, &rc_roi, dma_ops, RC_MASK_CFG);
+	if (rc) {
+		SDE_ERROR("unable to program rc roi, rc:%d\n", rc);
+		return rc;
+	}
+
+	rc = _reg_dmav1_rc_program_data_offset(ctx, rc_mask_cfg, dma_ops, RC_MASK_CFG);
+	if (rc) {
+		SDE_ERROR("unable to program data offsets, rc:%d\n", rc);
+		return rc;
+	}
+
+	/* rc data should be programmed once if dspp are in multi-pipe mode */
+	if (!(rc_mask_cfg->flags & SDE_HW_RC_SKIP_DATA_PROG) &&
+		(ctx->cap->sblk->rc.idx % hw_cfg->num_of_mixers == 0)) {
+		buf_sz = rc_mask_cfg->cfg_param_08 * 2 * sizeof(u32);
+		abs_offset = ctx->hw.blk_off + ctx->cap->sblk->rc.base + 0x28;
+
+		SDE_DEBUG("allocating %u bytes of memory for dma\n", buf_sz);
+		data = kvzalloc(buf_sz, GFP_KERNEL);
+		if (!data) {
+			SDE_ERROR("memory allocation failed ret %d\n", rc);
+			return -ENOMEM;
+		}
+
+		cfg_param_07 = rc_mask_cfg->cfg_param_07;
+		SDE_DEBUG("cfg_param_07:%u\n", cfg_param_07);
+
+		for (i = 0; i < rc_mask_cfg->cfg_param_08; i++) {
+			cfg_param_09 =  rc_mask_cfg->cfg_param_09[i];
+			SDE_DEBUG("cfg_param_09[%d] = 0x%016llX at %u\n", i,
+					cfg_param_09,
+					i + cfg_param_07);
+			data[i * 2] = (i == 0) ? (BIT(30) | (cfg_param_07 << 18)) : 0;
+			data[i * 2] |= (cfg_param_09 & 0x3FFFF);
+			data[i * 2 + 1] = ((cfg_param_09 >> 18) & 0x3FFFF);
+		}
+
+		REG_DMA_SETUP_OPS(dma_write_cfg, abs_offset, data, buf_sz,
+				REG_BLK_WRITE_INC, 0, 0, 0);
+		rc = dma_ops->setup_payload(&dma_write_cfg);
+		if (rc) {
+			SDE_ERROR("rc dma write failed ret %d\n", rc);
+			goto exit;
+		}
+	} else {
+		SDE_DEBUG("skip data programming\n");
+		SDE_EVT32(RC_IDX(ctx));
 	}
 
 	/* defer trigger to kickoff phase */
 	REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl,
-		dspp_buf[RC_DATA][ctx->idx], REG_DMA_WRITE,
-		DMA_CTL_QUEUE0, WRITE_TRIGGER, RC_DATA);
-	LOG_FEATURE_ON;
+		dspp_buf[RC_MASK_CFG][ctx->idx], REG_DMA_WRITE,
+		DMA_CTL_QUEUE0, WRITE_TRIGGER, RC_MASK_CFG);
 	rc = dma_ops->kick_off(&kick_off);
 	if (rc) {
-		DRM_ERROR("failed to kick off ret %d\n", rc);
+		SDE_ERROR("failed to kick off ret %d\n", rc);
 		goto exit;
 	}
 
+	LOG_FEATURE_ON;
+	memcpy(ctx->rc_state.last_rc_mask_cfg, rc_mask_cfg,
+			sizeof(struct drm_msm_rc_mask_cfg));
+
 exit:
-	kvfree(data);
+	if (data != NULL)
+		kvfree(data);
 	return rc;
 }
 

+ 10 - 2
msm/sde/sde_hw_reg_dma_v1_color_proc.h

@@ -267,12 +267,20 @@ void reg_dmav1_setup_ltm_vlutv1(struct sde_hw_dspp *ctx, void *cfg);
 void reg_dmav1_setup_ltm_vlutv1_2(struct sde_hw_dspp *ctx, void *cfg);
 
 /**
- * reg_dmav1_setup_rc_datav1() - RC DATA v1 implementation using reg dma v1.
+ * reg_dmav1_setup_rc_pu_configv1() - RC PU CFG v1 implementation using reg dma v1.
  * @ctx: dspp ctx info
  * @cfg: pointer to struct sde_hw_cp_cfg
  * @returns: 0 if success, non-zero otherwise
  */
-int reg_dmav1_setup_rc_datav1(struct sde_hw_dspp *ctx, void *cfg);
+int reg_dmav1_setup_rc_pu_configv1(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * reg_dmav1_setup_rc_mask_configv1() - RC Mask CFG v1 implementation using reg dma v1.
+ * @ctx: dspp ctx info
+ * @cfg: pointer to struct sde_hw_cp_cfg
+ * @returns: 0 if success, non-zero otherwise
+ */
+int reg_dmav1_setup_rc_mask_configv1(struct sde_hw_dspp *ctx, void *cfg);
 
 /**
  * reg_dmav1_deinit_ltm_ops() - deinitialize the ltm feature op for sde v4

+ 4 - 2
msm/sde/sde_reg_dma.h

@@ -60,7 +60,8 @@ enum sde_reg_dma_read_sel {
  * @LTM_INIT: LTM INIT
  * @LTM_ROI: LTM ROI
  * @LTM_VLUT: LTM VLUT
- * @RC_DATA: Rounded corner data
+ * @RC_MASK_CFG: Rounded corner config and mask
+ * @RC_PU_CFG: Rounded corner partial update
  * @DEMURA_CFG: Demura feature
  * @REG_DMA_FEATURES_MAX: invalid selection
  */
@@ -83,7 +84,8 @@ enum sde_reg_dma_features {
 	LTM_INIT,
 	LTM_ROI,
 	LTM_VLUT,
-	RC_DATA,
+	RC_MASK_CFG,
+	RC_PU_CFG,
 	DEMURA_CFG,
 	DEMURA_CFG0_PARAM2,
 	REG_DMA_FEATURES_MAX,