Browse Source

disp: msm: sde: add support for hardware based rounded corner

Add support for hardware based rounded corner part of
color processing framework.

Change-Id: I3e5f4dac6ffc759bb940215b7621ac716f255169
Signed-off-by: Amine Najahi <[email protected]>
Signed-off-by: Steve Cohen <[email protected]>
Amine Najahi 5 years ago
parent
commit
af07b8a5d4

+ 1 - 0
msm/Makefile

@@ -76,6 +76,7 @@ msm_drm-$(CONFIG_DRM_MSM_SDE) += sde/sde_crtc.o \
 	sde/sde_hw_qdss.o \
 	sde/sde_hw_qdss.o \
 	sde_dsc_helper.o \
 	sde_dsc_helper.o \
 	sde_vdc_helper.o \
 	sde_vdc_helper.o \
+	sde/sde_hw_rc.o \
 
 
 msm_drm-$(CONFIG_DRM_SDE_WB) += sde/sde_wb.o \
 msm_drm-$(CONFIG_DRM_SDE_WB) += sde/sde_wb.o \
 	sde/sde_encoder_phys_wb.o \
 	sde/sde_encoder_phys_wb.o \

+ 162 - 4
msm/sde/sde_color_processing.c

@@ -56,6 +56,8 @@ static void dspp_ad_install_property(struct drm_crtc *crtc);
 
 
 static void dspp_ltm_install_property(struct drm_crtc *crtc);
 static void dspp_ltm_install_property(struct drm_crtc *crtc);
 
 
+static void dspp_rc_install_property(struct drm_crtc *crtc);
+
 static void dspp_vlut_install_property(struct drm_crtc *crtc);
 static void dspp_vlut_install_property(struct drm_crtc *crtc);
 
 
 static void dspp_gamut_install_property(struct drm_crtc *crtc);
 static void dspp_gamut_install_property(struct drm_crtc *crtc);
@@ -112,6 +114,7 @@ do { \
 	func[SDE_DSPP_IGC] = dspp_igc_install_property; \
 	func[SDE_DSPP_IGC] = dspp_igc_install_property; \
 	func[SDE_DSPP_HIST] = dspp_hist_install_property; \
 	func[SDE_DSPP_HIST] = dspp_hist_install_property; \
 	func[SDE_DSPP_DITHER] = dspp_dither_install_property; \
 	func[SDE_DSPP_DITHER] = dspp_dither_install_property; \
+	func[SDE_DSPP_RC] = dspp_rc_install_property; \
 } while (0)
 } while (0)
 
 
 typedef void (*lm_prop_install_func_t)(struct drm_crtc *crtc);
 typedef void (*lm_prop_install_func_t)(struct drm_crtc *crtc);
@@ -160,6 +163,7 @@ enum sde_cp_crtc_features {
 	SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF3,
 	SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF3,
 	SDE_CP_CRTC_DSPP_LTM_VLUT,
 	SDE_CP_CRTC_DSPP_LTM_VLUT,
 	SDE_CP_CRTC_DSPP_SB,
 	SDE_CP_CRTC_DSPP_SB,
+	SDE_CP_CRTC_DSPP_RC_MASK,
 	SDE_CP_CRTC_DSPP_MAX,
 	SDE_CP_CRTC_DSPP_MAX,
 	/* DSPP features end */
 	/* DSPP features end */
 
 
@@ -172,11 +176,15 @@ enum sde_cp_crtc_features {
 };
 };
 
 
 enum sde_cp_crtc_pu_features {
 enum sde_cp_crtc_pu_features {
+	SDE_CP_CRTC_DSPP_RC_PU,
 	SDE_CP_CRTC_MAX_PU_FEATURES,
 	SDE_CP_CRTC_MAX_PU_FEATURES,
 };
 };
 
 
 static enum sde_cp_crtc_pu_features
 static enum sde_cp_crtc_pu_features
-		sde_cp_crtc_pu_to_feature[SDE_CP_CRTC_MAX_PU_FEATURES];
+		sde_cp_crtc_pu_to_feature[SDE_CP_CRTC_MAX_PU_FEATURES] = {
+	[SDE_CP_CRTC_DSPP_RC_PU] =
+		(enum sde_cp_crtc_pu_features) SDE_CP_CRTC_DSPP_RC_MASK,
+};
 
 
 static void _sde_cp_crtc_enable_hist_irq(struct sde_crtc *sde_crtc);
 static void _sde_cp_crtc_enable_hist_irq(struct sde_crtc *sde_crtc);
 
 
@@ -672,10 +680,122 @@ static int set_ltm_hist_crtl_feature(struct sde_hw_dspp *hw_dspp,
 	return ret;
 	return ret;
 }
 }
 
 
+static int check_rc_mask_feature(struct sde_hw_dspp *hw_dspp,
+				 struct sde_hw_cp_cfg *hw_cfg,
+				 struct sde_crtc *sde_crtc)
+{
+	int ret = 0;
+
+	if (!hw_dspp || !hw_cfg || !sde_crtc) {
+		DRM_ERROR("invalid arguments");
+		return -EINVAL;
+	}
+
+	if (!hw_dspp->ops.validate_rc_mask) {
+		DRM_ERROR("invalid rc ops");
+		return -EINVAL;
+	}
+
+	ret = hw_dspp->ops.validate_rc_mask(hw_dspp, hw_cfg);
+	if (ret)
+		DRM_ERROR("failed to validate rc mask %d", ret);
+
+	return ret;
+}
+
+static int set_rc_mask_feature(struct sde_hw_dspp *hw_dspp,
+			       struct sde_hw_cp_cfg *hw_cfg,
+			       struct sde_crtc *sde_crtc)
+{
+	int ret = 0;
+
+	if (!hw_dspp || !hw_cfg || !sde_crtc) {
+		DRM_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	if (!hw_dspp->ops.setup_rc_mask || !hw_dspp->ops.setup_rc_data) {
+		DRM_ERROR("invalid rc ops\n");
+		return -EINVAL;
+	}
+
+	DRM_DEBUG_DRIVER("dspp %d setup mask for rc instance %u\n",
+			hw_dspp->idx, hw_dspp->cap->sblk->rc.idx);
+
+	ret = hw_dspp->ops.setup_rc_mask(hw_dspp, hw_cfg);
+	if (ret) {
+		DRM_ERROR("failed to setup rc mask, ret %d\n", ret);
+		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;
+		}
+	}
+
+exit:
+	return ret;
+}
+
+static int set_rc_pu_feature(struct sde_hw_dspp *hw_dspp,
+			     struct sde_hw_cp_cfg *hw_cfg,
+			     struct sde_crtc *sde_crtc)
+{
+	int ret = 0;
+
+	if (!hw_dspp || !hw_cfg || !sde_crtc) {
+		DRM_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	if (!hw_dspp->ops.setup_rc_pu_roi) {
+		DRM_ERROR("invalid rc ops\n");
+		return -EINVAL;
+	}
+
+	DRM_DEBUG_DRIVER("dspp %d setup pu roi for rc instance %u\n",
+			hw_dspp->idx, hw_dspp->cap->sblk->rc.idx);
+
+	ret = hw_dspp->ops.setup_rc_pu_roi(hw_dspp, hw_cfg);
+	if (ret < 0)
+		DRM_ERROR("failed to setup rc pu roi, ret %d\n", ret);
+
+	return ret;
+}
+
+static int check_rc_pu_feature(struct sde_hw_dspp *hw_dspp,
+			       struct sde_hw_cp_cfg *hw_cfg,
+			       struct sde_crtc *sde_crtc)
+{
+	int ret = 0;
+
+	if (!hw_dspp || !hw_cfg || !sde_crtc) {
+		DRM_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	if (!hw_dspp->ops.validate_rc_pu_roi) {
+		SDE_ERROR("invalid rc ops");
+		return -EINVAL;
+	}
+
+	ret = hw_dspp->ops.validate_rc_pu_roi(hw_dspp, hw_cfg);
+	if (ret)
+		SDE_ERROR("failed to validate rc pu roi, ret %d", ret);
+
+	return ret;
+}
 
 
 feature_wrapper check_crtc_feature_wrappers[SDE_CP_CRTC_MAX_FEATURES];
 feature_wrapper check_crtc_feature_wrappers[SDE_CP_CRTC_MAX_FEATURES];
 #define setup_check_crtc_feature_wrappers(wrappers) \
 #define setup_check_crtc_feature_wrappers(wrappers) \
-memset(wrappers, 0, sizeof(wrappers))
+do { \
+	memset(wrappers, 0, sizeof(wrappers)); \
+	wrappers[SDE_CP_CRTC_DSPP_RC_MASK] = check_rc_mask_feature; \
+} while (0)
 
 
 feature_wrapper set_crtc_feature_wrappers[SDE_CP_CRTC_MAX_FEATURES];
 feature_wrapper set_crtc_feature_wrappers[SDE_CP_CRTC_MAX_FEATURES];
 #define setup_set_crtc_feature_wrappers(wrappers) \
 #define setup_set_crtc_feature_wrappers(wrappers) \
@@ -718,15 +838,22 @@ do { \
 	wrappers[SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF2] = set_ltm_queue_buf_feature; \
 	wrappers[SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF2] = set_ltm_queue_buf_feature; \
 	wrappers[SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF3] = set_ltm_queue_buf_feature; \
 	wrappers[SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF3] = set_ltm_queue_buf_feature; \
 	wrappers[SDE_CP_CRTC_DSPP_LTM_HIST_CTL] = set_ltm_hist_crtl_feature; \
 	wrappers[SDE_CP_CRTC_DSPP_LTM_HIST_CTL] = set_ltm_hist_crtl_feature; \
+	wrappers[SDE_CP_CRTC_DSPP_RC_MASK] = set_rc_mask_feature; \
 } while (0)
 } while (0)
 
 
 feature_wrapper set_crtc_pu_feature_wrappers[SDE_CP_CRTC_MAX_PU_FEATURES];
 feature_wrapper set_crtc_pu_feature_wrappers[SDE_CP_CRTC_MAX_PU_FEATURES];
 #define setup_set_crtc_pu_feature_wrappers(wrappers) \
 #define setup_set_crtc_pu_feature_wrappers(wrappers) \
-memset(wrappers, 0, sizeof(wrappers))
+do { \
+	memset(wrappers, 0, sizeof(wrappers)); \
+	wrappers[SDE_CP_CRTC_DSPP_RC_PU] = set_rc_pu_feature; \
+} while (0)
 
 
 feature_wrapper check_crtc_pu_feature_wrappers[SDE_CP_CRTC_MAX_PU_FEATURES];
 feature_wrapper check_crtc_pu_feature_wrappers[SDE_CP_CRTC_MAX_PU_FEATURES];
 #define setup_check_crtc_pu_feature_wrappers(wrappers) \
 #define setup_check_crtc_pu_feature_wrappers(wrappers) \
-memset(wrappers, 0, sizeof(wrappers))
+do { \
+	memset(wrappers, 0, sizeof(wrappers)); \
+	wrappers[SDE_CP_CRTC_DSPP_RC_PU] = check_rc_pu_feature; \
+} while (0)
 
 
 #define INIT_PROP_ATTACH(p, crtc, prop, node, feature, val) \
 #define INIT_PROP_ATTACH(p, crtc, prop, node, feature, val) \
 	do { \
 	do { \
@@ -1372,6 +1499,7 @@ static const int dspp_feature_to_sub_blk_tbl[SDE_CP_CRTC_MAX_FEATURES] = {
 	[SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF3] = SDE_DSPP_LTM,
 	[SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF3] = SDE_DSPP_LTM,
 	[SDE_CP_CRTC_DSPP_LTM_VLUT] = SDE_DSPP_LTM,
 	[SDE_CP_CRTC_DSPP_LTM_VLUT] = SDE_DSPP_LTM,
 	[SDE_CP_CRTC_DSPP_SB] = SDE_DSPP_SB,
 	[SDE_CP_CRTC_DSPP_SB] = SDE_DSPP_SB,
+	[SDE_CP_CRTC_DSPP_RC_MASK] = SDE_DSPP_RC,
 	[SDE_CP_CRTC_DSPP_MAX] = SDE_DSPP_MAX,
 	[SDE_CP_CRTC_DSPP_MAX] = SDE_DSPP_MAX,
 	[SDE_CP_CRTC_LM_GC] = SDE_DSPP_MAX,
 	[SDE_CP_CRTC_LM_GC] = SDE_DSPP_MAX,
 };
 };
@@ -2391,6 +2519,36 @@ static void dspp_ltm_install_property(struct drm_crtc *crtc)
 	}
 	}
 }
 }
 
 
+static void dspp_rc_install_property(struct drm_crtc *crtc)
+{
+	char feature_name[256];
+	struct sde_kms *kms = NULL;
+	struct sde_mdss_cfg *catalog = NULL;
+	u32 version;
+
+	if (!crtc) {
+		DRM_ERROR("invalid arguments");
+		return;
+	}
+
+	kms = get_kms(crtc);
+	catalog = kms->catalog;
+	version = catalog->dspp[0].sblk->rc.version >> 16;
+	switch (version) {
+	case 1:
+		snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+				"SDE_DSPP_RC_MASK_V", version);
+		sde_cp_crtc_install_blob_property(crtc, feature_name,
+				SDE_CP_CRTC_DSPP_RC_MASK,
+				sizeof(struct drm_msm_rc_mask_cfg));
+
+		break;
+	default:
+		DRM_ERROR("version %d not supported\n", version);
+		break;
+	}
+}
+
 static void lm_gc_install_property(struct drm_crtc *crtc)
 static void lm_gc_install_property(struct drm_crtc *crtc)
 {
 {
 	char feature_name[256];
 	char feature_name[256];

+ 8 - 0
msm/sde/sde_crtc.c

@@ -755,6 +755,9 @@ static int _sde_crtc_set_crtc_roi(struct drm_crtc *crtc,
 		}
 		}
 
 
 		sde_kms_rect_merge_rectangles(&sde_conn_state->rois, &conn_roi);
 		sde_kms_rect_merge_rectangles(&sde_conn_state->rois, &conn_roi);
+		SDE_DEBUG("conn_roi x:%u, y:%u, w:%u, h:%u\n",
+				conn_roi.x, conn_roi.y,
+				conn_roi.w, conn_roi.h);
 		SDE_EVT32_VERBOSE(DRMID(crtc), DRMID(conn),
 		SDE_EVT32_VERBOSE(DRMID(crtc), DRMID(conn),
 				conn_roi.x, conn_roi.y,
 				conn_roi.x, conn_roi.y,
 				conn_roi.w, conn_roi.h);
 				conn_roi.w, conn_roi.h);
@@ -5003,8 +5006,13 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,
 		sde_crtc_install_dest_scale_properties(sde_crtc, catalog,
 		sde_crtc_install_dest_scale_properties(sde_crtc, catalog,
 				info);
 				info);
 
 
+	if (catalog->dspp_count && catalog->rc_count)
+		sde_kms_info_add_keyint(info, "rc_mem_size",
+				catalog->dspp[0].sblk->rc.mem_total_size);
+
 	msm_property_install_blob(&sde_crtc->property_info, "capabilities",
 	msm_property_install_blob(&sde_crtc->property_info, "capabilities",
 		DRM_MODE_PROP_IMMUTABLE, CRTC_PROP_INFO);
 		DRM_MODE_PROP_IMMUTABLE, CRTC_PROP_INFO);
+
 	msm_property_set_blob(&sde_crtc->property_info, &sde_crtc->blob_info,
 	msm_property_set_blob(&sde_crtc->property_info, &sde_crtc->blob_info,
 			info->data, SDE_KMS_INFO_DATALEN(info),
 			info->data, SDE_KMS_INFO_DATALEN(info),
 			CRTC_PROP_INFO);
 			CRTC_PROP_INFO);

+ 44 - 3
msm/sde/sde_hw_catalog.c

@@ -383,6 +383,14 @@ enum {
 	LTM_PROP_MAX,
 	LTM_PROP_MAX,
 };
 };
 
 
+enum {
+	RC_OFF,
+	RC_LEN,
+	RC_VERSION,
+	RC_MEM_TOTAL_SIZE,
+	RC_PROP_MAX,
+};
+
 enum {
 enum {
 	MIXER_OFF,
 	MIXER_OFF,
 	MIXER_LEN,
 	MIXER_LEN,
@@ -699,6 +707,13 @@ static struct sde_prop_type ltm_prop[] = {
 	{LTM_VERSION, "qcom,sde-dspp-ltm-version", false, PROP_TYPE_U32},
 	{LTM_VERSION, "qcom,sde-dspp-ltm-version", false, PROP_TYPE_U32},
 };
 };
 
 
+static struct sde_prop_type rc_prop[] = {
+	{RC_OFF, "qcom,sde-dspp-rc-off", false, PROP_TYPE_U32_ARRAY},
+	{RC_LEN, "qcom,sde-dspp-rc-size", false, PROP_TYPE_U32},
+	{RC_VERSION, "qcom,sde-dspp-rc-version", false, PROP_TYPE_U32},
+	{RC_MEM_TOTAL_SIZE, "qcom,sde-dspp-rc-mem-size", false, PROP_TYPE_U32},
+};
+
 static struct sde_prop_type ds_top_prop[] = {
 static struct sde_prop_type ds_top_prop[] = {
 	{DS_TOP_OFF, "qcom,sde-dest-scaler-top-off", false, PROP_TYPE_U32},
 	{DS_TOP_OFF, "qcom,sde-dest-scaler-top-off", false, PROP_TYPE_U32},
 	{DS_TOP_LEN, "qcom,sde-dest-scaler-top-size", false, PROP_TYPE_U32},
 	{DS_TOP_LEN, "qcom,sde-dest-scaler-top-size", false, PROP_TYPE_U32},
@@ -2471,8 +2486,8 @@ static int sde_dspp_parse_dt(struct device_node *np,
 						struct sde_mdss_cfg *sde_cfg)
 						struct sde_mdss_cfg *sde_cfg)
 {
 {
 	int rc = 0, i;
 	int rc = 0, i;
-	u32 off_count, ad_off_count, ltm_off_count;
-	struct sde_dt_props *props, *ad_props, *ltm_props;
+	u32 off_count, ad_off_count, ltm_off_count, rc_off_count;
+	struct sde_dt_props *props, *ad_props, *ltm_props, *rc_props;
 	struct sde_dt_props *blocks_props = NULL;
 	struct sde_dt_props *blocks_props = NULL;
 	struct sde_dspp_cfg *dspp;
 	struct sde_dspp_cfg *dspp;
 	struct sde_dspp_sub_blks *sblk;
 	struct sde_dspp_sub_blks *sblk;
@@ -2506,6 +2521,14 @@ static int sde_dspp_parse_dt(struct device_node *np,
 		goto put_ad_props;
 		goto put_ad_props;
 	}
 	}
 
 
+	/* Parse RC dtsi entries */
+	rc_props = sde_get_dt_props(np, RC_PROP_MAX, rc_prop,
+			ARRAY_SIZE(rc_prop), &rc_off_count);
+	if (IS_ERR_OR_NULL(rc_props)) {
+		rc = PTR_ERR(rc_props);
+		goto put_ltm_props;
+	}
+
 	/* get DSPP feature dt properties if they exist */
 	/* get DSPP feature dt properties if they exist */
 	snp = of_get_child_by_name(np, dspp_prop[DSPP_BLOCKS].prop_name);
 	snp = of_get_child_by_name(np, dspp_prop[DSPP_BLOCKS].prop_name);
 	if (snp) {
 	if (snp) {
@@ -2514,7 +2537,7 @@ static int sde_dspp_parse_dt(struct device_node *np,
 				NULL);
 				NULL);
 		if (IS_ERR_OR_NULL(blocks_props)) {
 		if (IS_ERR_OR_NULL(blocks_props)) {
 			rc = PTR_ERR(blocks_props);
 			rc = PTR_ERR(blocks_props);
-			goto put_ltm_props;
+			goto put_rc_props;
 		}
 		}
 	}
 	}
 
 
@@ -2571,10 +2594,28 @@ static int sde_dspp_parse_dt(struct device_node *np,
 				goto end;
 				goto end;
 		}
 		}
 
 
+		sblk->rc.id = SDE_DSPP_RC;
+		sde_cfg->rc_count = rc_off_count;
+		if (rc_props && (i < rc_off_count) &&
+		    rc_props->exists[RC_OFF]) {
+			sblk->rc.base = PROP_VALUE_ACCESS(rc_props->values,
+					RC_OFF, i);
+			sblk->rc.len = PROP_VALUE_ACCESS(rc_props->values,
+					RC_LEN, 0);
+			sblk->rc.version = PROP_VALUE_ACCESS(rc_props->values,
+					RC_VERSION, 0);
+			sblk->rc.mem_total_size = PROP_VALUE_ACCESS(
+					rc_props->values, RC_MEM_TOTAL_SIZE,
+					0);
+			sblk->rc.idx = i;
+			set_bit(SDE_DSPP_RC, &dspp->features);
+		}
 	}
 	}
 
 
 end:
 end:
 	sde_put_dt_props(blocks_props);
 	sde_put_dt_props(blocks_props);
+put_rc_props:
+	sde_put_dt_props(rc_props);
 put_ltm_props:
 put_ltm_props:
 	sde_put_dt_props(ltm_props);
 	sde_put_dt_props(ltm_props);
 put_ad_props:
 put_ad_props:

+ 18 - 0
msm/sde/sde_hw_catalog.h

@@ -755,6 +755,20 @@ struct sde_lm_sub_blks {
 	struct sde_pp_blk gc;
 	struct sde_pp_blk gc;
 };
 };
 
 
+/**
+ * struct sde_dspp_rc: Pixel processing rounded corner sub-blk information
+ * @info: HW register and features supported by this sub-blk.
+ * @version: HW Algorithm version.
+ * @idx: HW block instance id.
+ * @mem_total_size: data memory size.
+ */
+struct sde_dspp_rc {
+	SDE_HW_SUBBLK_INFO;
+	u32 version;
+	u32 idx;
+	u32 mem_total_size;
+};
+
 struct sde_dspp_sub_blks {
 struct sde_dspp_sub_blks {
 	struct sde_pp_blk igc;
 	struct sde_pp_blk igc;
 	struct sde_pp_blk pcc;
 	struct sde_pp_blk pcc;
@@ -768,6 +782,7 @@ struct sde_dspp_sub_blks {
 	struct sde_pp_blk ad;
 	struct sde_pp_blk ad;
 	struct sde_pp_blk ltm;
 	struct sde_pp_blk ltm;
 	struct sde_pp_blk vlut;
 	struct sde_pp_blk vlut;
+	struct sde_dspp_rc rc;
 };
 };
 
 
 struct sde_pingpong_sub_blks {
 struct sde_pingpong_sub_blks {
@@ -1426,6 +1441,7 @@ struct sde_limit_cfg {
  * @has_vig_p010  indicates if vig pipe supports p010 format
  * @has_vig_p010  indicates if vig pipe supports p010 format
  * @inline_rot_formats	formats supported by the inline rotator feature
  * @inline_rot_formats	formats supported by the inline rotator feature
  * @irq_offset_list     list of sde_intr_irq_offsets to initialize irq table
  * @irq_offset_list     list of sde_intr_irq_offsets to initialize irq table
+ * @rc_count	number of rounded corner hardware instances
  */
  */
 struct sde_mdss_cfg {
 struct sde_mdss_cfg {
 	u32 hwversion;
 	u32 hwversion;
@@ -1536,6 +1552,7 @@ struct sde_mdss_cfg {
 
 
 	u32 ad_count;
 	u32 ad_count;
 	u32 ltm_count;
 	u32 ltm_count;
+	u32 rc_count;
 
 
 	u32 merge_3d_count;
 	u32 merge_3d_count;
 	struct sde_merge_3d_cfg merge_3d[MAX_BLOCKS];
 	struct sde_merge_3d_cfg merge_3d[MAX_BLOCKS];
@@ -1583,6 +1600,7 @@ struct sde_mdss_hw_cfg_handler {
 #define BLK_WB(s) ((s)->wb)
 #define BLK_WB(s) ((s)->wb)
 #define BLK_AD(s) ((s)->ad)
 #define BLK_AD(s) ((s)->ad)
 #define BLK_LTM(s) ((s)->ltm)
 #define BLK_LTM(s) ((s)->ltm)
+#define BLK_RC(s) ((s)->rc)
 
 
 /**
 /**
  * sde_hw_set_preference: populate the individual hw lm preferences,
  * sde_hw_set_preference: populate the individual hw lm preferences,

+ 42 - 0
msm/sde/sde_hw_dspp.c

@@ -2,6 +2,7 @@
 /*
 /*
  * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
  * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
  */
  */
+
 #include <drm/msm_drm_pp.h>
 #include <drm/msm_drm_pp.h>
 #include "sde_hw_mdss.h"
 #include "sde_hw_mdss.h"
 #include "sde_hwio.h"
 #include "sde_hwio.h"
@@ -10,6 +11,7 @@
 #include "sde_hw_color_processing.h"
 #include "sde_hw_color_processing.h"
 #include "sde_dbg.h"
 #include "sde_dbg.h"
 #include "sde_ad4.h"
 #include "sde_ad4.h"
+#include "sde_hw_rc.h"
 #include "sde_kms.h"
 #include "sde_kms.h"
 
 
 static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp,
 static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp,
@@ -241,6 +243,37 @@ static void dspp_ltm(struct sde_hw_dspp *c)
 	}
 	}
 }
 }
 
 
+static void dspp_rc(struct sde_hw_dspp *c)
+{
+	int ret = 0;
+
+	if (!c) {
+		SDE_ERROR("invalid arguments\n");
+		return;
+	}
+
+	if (c->cap->sblk->rc.version == SDE_COLOR_PROCESS_VER(0x1, 0x0)) {
+		ret = sde_hw_rc_init(c);
+		if (ret) {
+			SDE_ERROR("rc init failed, ret %d\n", ret);
+			return;
+		}
+
+		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;
+
+		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;
+	}
+}
+
 static void (*dspp_blocks[SDE_DSPP_MAX])(struct sde_hw_dspp *c);
 static void (*dspp_blocks[SDE_DSPP_MAX])(struct sde_hw_dspp *c);
 
 
 static void _init_dspp_ops(void)
 static void _init_dspp_ops(void)
@@ -257,6 +290,7 @@ static void _init_dspp_ops(void)
 	dspp_blocks[SDE_DSPP_VLUT] = dspp_vlut;
 	dspp_blocks[SDE_DSPP_VLUT] = dspp_vlut;
 	dspp_blocks[SDE_DSPP_AD] = dspp_ad;
 	dspp_blocks[SDE_DSPP_AD] = dspp_ad;
 	dspp_blocks[SDE_DSPP_LTM] = dspp_ltm;
 	dspp_blocks[SDE_DSPP_LTM] = dspp_ltm;
+	dspp_blocks[SDE_DSPP_RC] = dspp_rc;
 }
 }
 
 
 static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features)
 static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features)
@@ -286,6 +320,7 @@ struct sde_hw_dspp *sde_hw_dspp_init(enum sde_dspp idx,
 	struct sde_hw_dspp *c;
 	struct sde_hw_dspp *c;
 	struct sde_dspp_cfg *cfg;
 	struct sde_dspp_cfg *cfg;
 	int rc;
 	int rc;
+	char buf[256];
 
 
 	if (!addr || !m)
 	if (!addr || !m)
 		return ERR_PTR(-EINVAL);
 		return ERR_PTR(-EINVAL);
@@ -329,6 +364,13 @@ struct sde_hw_dspp *sde_hw_dspp_init(enum sde_dspp idx,
 				c->hw.xin_id);
 				c->hw.xin_id);
 	}
 	}
 
 
+	if ((cfg->sblk->rc.id == SDE_DSPP_RC) && cfg->sblk->rc.base) {
+		snprintf(buf, ARRAY_SIZE(buf), "%s_%d", "rc", c->idx - DSPP_0);
+		sde_dbg_reg_register_dump_range(SDE_DBG_NAME, buf,
+				c->hw.blk_off + cfg->sblk->rc.base,
+				c->hw.blk_off + cfg->sblk->rc.base +
+				cfg->sblk->rc.len, c->hw.xin_id);
+	}
 	return c;
 	return c;
 
 
 blk_init_error:
 blk_init_error:

+ 40 - 0
msm/sde/sde_hw_dspp.h

@@ -209,6 +209,46 @@ struct sde_hw_dspp_ops {
 	 * @status: Pointer to u32 where ltm status value is dumped.
 	 * @status: Pointer to u32 where ltm status value is dumped.
 	 */
 	 */
 	void (*ltm_read_intr_status)(struct sde_hw_dspp *ctx, u32 *status);
 	void (*ltm_read_intr_status)(struct sde_hw_dspp *ctx, u32 *status);
+
+	/**
+	 * validate_rc_mask -  Validate RC mask configuration
+	 * @ctx: Pointer to dspp context.
+	 * @cfg: Pointer to configuration.
+	 * Return: 0 on success, non-zero otherwise.
+	 */
+	int (*validate_rc_mask)(struct sde_hw_dspp *ctx, void *cfg);
+
+	/**
+	 * setup_rc_mask -  Setup RC mask configuration
+	 * @ctx: Pointer to dspp context.
+	 * @cfg: Pointer to configuration.
+	 * Return: 0 on success, non-zero otherwise.
+	 */
+	int (*setup_rc_mask)(struct sde_hw_dspp *ctx, void *cfg);
+
+	/**
+	 * validate_rc_pu_roi -  Validate RC regions in during partial update.
+	 * @ctx: Pointer to dspp context.
+	 * @cfg: Pointer to configuration.
+	 * Return: 0 on success, non-zero otherwise.
+	 */
+	int (*validate_rc_pu_roi)(struct sde_hw_dspp *ctx, void *cfg);
+
+	/**
+	 * setup_rc_pu_roi -  Setup RC regions in during partial update.
+	 * @ctx: Pointer to dspp context.
+	 * @cfg: Pointer to configuration.
+	 * Return: 0 on success, non-zero otherwise.
+	 */
+	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);
 };
 };
 
 
 /**
 /**

+ 7 - 0
msm/sde/sde_hw_mdss.h

@@ -172,6 +172,7 @@ enum sde_stage {
 	SDE_STAGE_10,
 	SDE_STAGE_10,
 	SDE_STAGE_MAX
 	SDE_STAGE_MAX
 };
 };
+
 enum sde_dspp {
 enum sde_dspp {
 	DSPP_0 = 1,
 	DSPP_0 = 1,
 	DSPP_1,
 	DSPP_1,
@@ -186,6 +187,12 @@ enum sde_ltm {
 	LTM_MAX
 	LTM_MAX
 };
 };
 
 
+enum sde_rc {
+	RC_0 = DSPP_0,
+	RC_1,
+	RC_MAX
+};
+
 enum sde_ds {
 enum sde_ds {
 	DS_TOP,
 	DS_TOP,
 	DS_0,
 	DS_0,

+ 974 - 0
msm/sde/sde_hw_rc.c

@@ -0,0 +1,974 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#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_STATE(hw_dspp) rc_state[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;
+};
+
+static struct rc_config_table config_table[] =  {
+	/* RC_PARAM_A0 configurations */
+	{
+		.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_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_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_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 void _sde_hw_rc_reg_write(
+		struct sde_hw_dspp *hw_dspp,
+		int offset,
+		u32 value)
+{
+	u32 address = hw_dspp->cap->sblk->rc.base + offset;
+
+	SDE_DEBUG("rc:%u, address:0x%08X, value:0x%08X\n",
+			hw_dspp->cap->sblk->rc.idx,
+			hw_dspp->hw.blk_off + address, value);
+	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->displayh;
+		rc_roi->h = hw_cfg->displayv;
+	} else {
+		memcpy(rc_roi, pu_roi, sizeof(struct sde_rect));
+	}
+
+	SDE_DEBUG("displayh:%u, displayv:%u\n", hw_cfg->displayh,
+			hw_cfg->displayv);
+	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);
+	SDE_DEBUG("param_r:0x%08X param_b:0x%08X\n",
+			*param_r, *param_b);
+
+	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;
+	}
+
+	return rc;
+}
+
+static int _sde_hw_rc_program_enable_bits(
+		struct sde_hw_dspp *hw_dspp,
+		struct drm_msm_rc_mask_cfg *rc_mask_cfg,
+		enum rc_param_r param_r,
+		enum rc_param_a param_a,
+		enum rc_param_b param_b,
+		int merge_mode,
+		struct sde_rect *rc_roi)
+{
+	int rc = 0;
+	u32 val = 0, param_c = 0, rc_merge_mode = 0, ystart = 0;
+
+	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;
+	}
+
+	if (param_r & RC_PARAM_R1) {
+		val |= BIT(0);
+		SDE_DEBUG("enable R1\n");
+	}
+
+	if (param_r & RC_PARAM_R2) {
+		val |= BIT(4);
+		SDE_DEBUG("enable R2\n");
+	}
+
+	/*corner case for partial update*/
+	if (param_r == RC_PARAM_R0) {
+		ystart = rc_roi->y;
+		SDE_DEBUG("set partial update ystart:%u\n", ystart);
+	}
+
+	if ((rc_mask_cfg->flags & SDE_HW_RC_DISABLE_R1)
+			== SDE_HW_RC_DISABLE_R1) {
+		val &= ~BIT(0);
+		SDE_DEBUG("override disable R1\n");
+	}
+
+	if ((rc_mask_cfg->flags & SDE_HW_RC_DISABLE_R2)
+			== SDE_HW_RC_DISABLE_R2) {
+		val &= ~BIT(4);
+		SDE_DEBUG("override disable R2\n");
+	}
+
+	val |= param_c;
+	_sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG1, val);
+	_sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG13, ystart);
+	_sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG9, rc_merge_mode);
+
+	return rc;
+}
+
+static int _sde_hw_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)
+{
+	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 = _sde_hw_rc_program_enable_bits(hw_dspp, rc_mask_cfg,
+			param_r, param_a, param_b, merge_mode, rc_roi);
+	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]);
+	}
+
+	_sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG2, val2);
+	_sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG3, val3);
+	_sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG4, val4);
+
+	return 0;
+}
+
+static int _sde_hw_rc_program_data_offset(
+		struct sde_hw_dspp *hw_dspp,
+		struct drm_msm_rc_mask_cfg *rc_mask_cfg)
+{
+	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);
+	}
+
+	_sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG5, val5);
+	_sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG6, val6);
+	_sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG7, val7);
+	_sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG8, val8);
+
+	return rc;
+}
+
+static int sde_hw_rc_check_mask_cfg(
+		struct sde_hw_dspp *hw_dspp,
+		struct sde_hw_cp_cfg *hw_cfg,
+		struct drm_msm_rc_mask_cfg *rc_mask_cfg)
+{
+	int rc = 0;
+	u32 i = 0;
+	u32 half_panel_width;
+
+	if (!hw_dspp || !hw_cfg || !rc_mask_cfg) {
+		SDE_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	if (!rc_mask_cfg->cfg_param_08 ||
+			rc_mask_cfg->cfg_param_08 > RC_DATA_SIZE_MAX) {
+		SDE_ERROR("invalid cfg_param_08:%d\n",
+				rc_mask_cfg->cfg_param_08);
+		return -EINVAL;
+	}
+
+	if (rc_mask_cfg->cfg_param_07 + rc_mask_cfg->cfg_param_08 >
+			hw_dspp->cap->sblk->rc.mem_total_size) {
+		SDE_ERROR("invalid cfg_param_08:%d, cfg_param_07:%d, max:%u\n",
+				rc_mask_cfg->cfg_param_08,
+				rc_mask_cfg->cfg_param_07,
+				hw_dspp->cap->sblk->rc.mem_total_size);
+		return -EINVAL;
+	}
+
+	if (!rc_mask_cfg->cfg_param_03 ||
+			(rc_mask_cfg->cfg_param_03 != RC_PARAM_A1 &&
+			rc_mask_cfg->cfg_param_03 != RC_PARAM_A0)) {
+		SDE_ERROR("invalid cfg_param_03:%d\n",
+				rc_mask_cfg->cfg_param_03);
+		return -EINVAL;
+	}
+
+	if ((rc_mask_cfg->cfg_param_01 < 1) ||
+			((hw_cfg->displayv - rc_mask_cfg->cfg_param_02) < 1)) {
+		SDE_ERROR("invalid min cfg_param_01:%d or cfg_param_02:%d\n",
+				rc_mask_cfg->cfg_param_01,
+				rc_mask_cfg->cfg_param_02);
+		return -EINVAL;
+	}
+
+	if (rc_mask_cfg->cfg_param_01 > rc_mask_cfg->cfg_param_02) {
+		SDE_ERROR("invalid cfg_param_01:%d or cfg_param_02:%d\n",
+				rc_mask_cfg->cfg_param_01,
+				rc_mask_cfg->cfg_param_02);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < rc_mask_cfg->cfg_param_03; i++) {
+		if (rc_mask_cfg->cfg_param_04[i] < 4) {
+			SDE_ERROR("invalid cfg_param_04[%d]:%d\n", i,
+					rc_mask_cfg->cfg_param_04[i]);
+			return -EINVAL;
+		}
+	}
+
+	half_panel_width = hw_cfg->displayh / rc_mask_cfg->cfg_param_03 * 2;
+	for (i = 0; i < rc_mask_cfg->cfg_param_03; i += 2) {
+		if (rc_mask_cfg->cfg_param_04[i] +
+				rc_mask_cfg->cfg_param_04[i+1] !=
+				half_panel_width) {
+			SDE_ERROR("invalid ratio [%d]:%d, [%d]:%d, %d\n",
+					i,
+					rc_mask_cfg->cfg_param_04[i],
+					i+1,
+					rc_mask_cfg->cfg_param_04[i+1],
+					half_panel_width);
+			return -EINVAL;
+		}
+	}
+
+	for (i = 0; i < rc_mask_cfg->cfg_param_03 - 1; i++) {
+		if (rc_mask_cfg->cfg_param_05[i] >=
+				rc_mask_cfg->cfg_param_05[i+1]) {
+			SDE_ERROR("invalid cfg_param_05 overlap %d, %d\n",
+					rc_mask_cfg->cfg_param_05[i],
+					rc_mask_cfg->cfg_param_05[i+1]);
+			return -EINVAL;
+		}
+	}
+
+	for (i = 0; i < rc_mask_cfg->cfg_param_03; i++) {
+		if (rc_mask_cfg->cfg_param_05[i] >
+				RC_DATA_SIZE_MAX) {
+			SDE_ERROR("invalid cfg_param_05[%d]:%d\n", i,
+					rc_mask_cfg->cfg_param_05[i]);
+			return -EINVAL;
+		}
+
+	}
+
+	return rc;
+}
+
+int sde_hw_rc_check_mask(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 mask checks\n");
+		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 (hw_cfg->num_of_mixers != 1 && hw_cfg->num_of_mixers != 2) {
+		SDE_ERROR("invalid number of mixers:%d\n",
+				hw_cfg->num_of_mixers);
+		return -EINVAL;
+	}
+
+	rc = sde_hw_rc_check_mask_cfg(hw_dspp, hw_cfg, rc_mask_cfg);
+	if (rc) {
+		SDE_ERROR("invalid rc mask configuration, rc:%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+int sde_hw_rc_check_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg)
+{
+	int rc = 0;
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	struct msm_roi_list *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;
+
+	if (!hw_dspp || !hw_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_mask_cfg = RC_STATE(hw_dspp).last_rc_mask_cfg;
+	mask_programmed = RC_STATE(hw_dspp).mask_programmed;
+
+	/* early return when there is no mask in memory */
+	if (!mask_programmed || !rc_mask_cfg) {
+		SDE_DEBUG("no previous rc mask programmed\n");
+		return SDE_HW_RC_PU_SKIP_OP;
+	}
+
+	rc = sde_hw_rc_check_mask_cfg(hw_dspp, hw_cfg, rc_mask_cfg);
+	if (rc) {
+		SDE_ERROR("invalid rc mask configuration, rc:%d\n", rc);
+		return rc;
+	}
+
+	roi_list = hw_cfg->payload;
+	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_param_rb(rc_mask_cfg, &rc_roi,
+			&param_r, &param_b);
+	if (rc) {
+		SDE_ERROR("invalid rc roi, rc:%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+int sde_hw_rc_setup_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg)
+{
+	int rc = 0;
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	struct msm_roi_list *roi_list;
+	struct sde_rect rc_roi, merged_roi;
+	struct drm_msm_rc_mask_cfg *rc_mask_cfg;
+	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;
+	bool mask_programmed = false;
+
+	if (!hw_dspp || !hw_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_mask_cfg = RC_STATE(hw_dspp).last_rc_mask_cfg;
+	mask_programmed = RC_STATE(hw_dspp).mask_programmed;
+
+	/* early return when there is no mask in memory */
+	if (!mask_programmed || !rc_mask_cfg) {
+		SDE_DEBUG("no previous rc mask programmed\n");
+		return SDE_HW_RC_PU_SKIP_OP;
+	}
+
+	roi_list = hw_cfg->payload;
+	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");
+		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 = _sde_hw_rc_program_enable_bits(hw_dspp, rc_mask_cfg,
+			param_r, param_a, param_b, merge_mode, &rc_roi);
+	if (rc) {
+		SDE_ERROR("failed to program enable bits, rc:%d\n", rc);
+		return rc;
+	}
+
+	memcpy(RC_STATE(hw_dspp).last_roi_list,
+			roi_list, sizeof(struct msm_roi_list));
+	RC_STATE(hw_dspp).roi_programmed = true;
+
+	return 0;
+}
+
+int sde_hw_rc_setup_mask(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;
+	struct sde_rect rc_roi, merged_roi;
+	struct msm_roi_list *last_roi_list;
+	u32 merge_mode = 0;
+	bool roi_programmed = false;
+
+	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\n");
+
+		_sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG1, 0);
+
+		memset(RC_STATE(hw_dspp).last_rc_mask_cfg, 0,
+				sizeof(struct drm_msm_rc_mask_cfg));
+		RC_STATE(hw_dspp).mask_programmed = false;
+
+		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;
+	last_roi_list = RC_STATE(hw_dspp).last_roi_list;
+	roi_programmed = RC_STATE(hw_dspp).roi_programmed;
+
+	if (!roi_programmed) {
+		SDE_DEBUG("no previously programmed partial update rois\n");
+		memset(&merged_roi, 0, sizeof(struct sde_rect));
+	} else {
+		sde_kms_rect_merge_rectangles(last_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");
+		return rc;
+	}
+
+	rc = _sde_hw_rc_program_roi(hw_dspp, rc_mask_cfg,
+			merge_mode, &rc_roi);
+	if (rc) {
+		SDE_ERROR("unable to program rc roi, rc:%d\n", rc);
+		return rc;
+	}
+
+	rc = _sde_hw_rc_program_data_offset(hw_dspp, rc_mask_cfg);
+	if (rc) {
+		SDE_ERROR("unable to program data offsets, rc:%d\n", rc);
+		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");
+		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");
+		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;
+}
+
+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;
+
+	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");
+		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");
+		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%016lX 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);
+	}
+
+	return rc;
+}
+
+int sde_hw_rc_init(struct sde_hw_dspp *hw_dspp)
+{
+	int rc = 0;
+
+	RC_STATE(hw_dspp).last_roi_list = kzalloc(
+			sizeof(struct msm_roi_list), GFP_KERNEL);
+	if (!RC_STATE(hw_dspp).last_roi_list)
+		return -ENOMEM;
+
+	RC_STATE(hw_dspp).last_rc_mask_cfg = kzalloc(
+			sizeof(struct drm_msm_rc_mask_cfg), GFP_KERNEL);
+	if (!RC_STATE(hw_dspp).last_rc_mask_cfg)
+		return -ENOMEM;
+
+	return rc;
+}

+ 71 - 0
msm/sde/sde_hw_rc.h

@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _SDE_HW_RC_H
+#define _SDE_HW_RC_H
+
+#include "sde_hw_mdss.h"
+
+
+/**
+ * sde_hw_rc_init -  Initialize RC internal state object and ops
+ * @hw_dspp: DSPP instance.
+ * Return: 0 on success, non-zero otherwise.
+ */
+int sde_hw_rc_init(struct sde_hw_dspp *hw_dspp);
+
+/**
+ * sde_hw_rc_check_mask -  Validate RC mask configuration
+ * @hw_dspp: DSPP instance.
+ * @cfg: Pointer to configuration blob.
+ * Return: 0 on success, non-zero otherwise.
+ */
+int sde_hw_rc_check_mask(struct sde_hw_dspp *hw_dspp, void *cfg);
+
+/**
+ * sde_hw_rc_setup_mask -  Setup RC mask configuration
+ * @hw_dspp: DSPP instance.
+ * @cfg: Pointer to configuration blob.
+ * Return: 0 on success, non-zero otherwise.
+ */
+int sde_hw_rc_setup_mask(struct sde_hw_dspp *hw_dspp, void *cfg);
+
+/**
+ * sde_hw_rc_check_pu_roi -  Validate RC partial update region of interest
+ * @hw_dspp: DSPP instance.
+ * @cfg: Pointer to configuration blob.
+ * Return: 0 on success.
+ *         > 0 on early return.
+ *         < 0 on error.
+ */
+int sde_hw_rc_check_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg);
+
+/**
+ * sde_hw_rc_setup_pu_roi -  Setup RC partial update region of interest
+ * @hw_dspp: DSPP instance.
+ * @cfg: Pointer to configuration blob.
+ * Return: 0 on success.
+ *         > 0 on early return.
+ *         < 0 on error.
+ */
+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

+ 2 - 0
msm/sde/sde_hw_reg_dma_v1.c

@@ -806,6 +806,8 @@ int init_v12(struct sde_hw_reg_dma *cfg)
 	v1_supported[LTM_INIT] = GRP_LTM_HW_BLK_SELECT;
 	v1_supported[LTM_INIT] = GRP_LTM_HW_BLK_SELECT;
 	v1_supported[LTM_ROI] = 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[LTM_VLUT] = GRP_LTM_HW_BLK_SELECT;
+	v1_supported[RC_DATA] = (GRP_DSPP_HW_BLK_SELECT |
+			GRP_MDSS_HW_BLK_SELECT);
 
 
 	return 0;
 	return 0;
 }
 }

+ 89 - 0
msm/sde/sde_hw_reg_dma_v1_color_proc.c

@@ -2,6 +2,7 @@
 /*
 /*
  * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  */
  */
+
 #include <drm/msm_drm_pp.h>
 #include <drm/msm_drm_pp.h>
 #include "sde_reg_dma.h"
 #include "sde_reg_dma.h"
 #include "sde_hw_reg_dma_v1_color_proc.h"
 #include "sde_hw_reg_dma_v1_color_proc.h"
@@ -65,6 +66,9 @@
 #define MEMCOLOR_MEM_SIZE ((sizeof(struct drm_msm_memcol)) + \
 #define MEMCOLOR_MEM_SIZE ((sizeof(struct drm_msm_memcol)) + \
 		REG_DMA_HEADERS_BUFFER_SZ)
 		REG_DMA_HEADERS_BUFFER_SZ)
 
 
+#define RC_MEM_SIZE ((RC_DATA_SIZE_MAX * 2 * sizeof(u32)) + \
+		REG_DMA_HEADERS_BUFFER_SZ)
+
 #define QSEED3_MEM_SIZE (sizeof(struct sde_hw_scaler3_cfg) + \
 #define QSEED3_MEM_SIZE (sizeof(struct sde_hw_scaler3_cfg) + \
 			(450 * sizeof(u32)) + \
 			(450 * sizeof(u32)) + \
 			REG_DMA_HEADERS_BUFFER_SZ)
 			REG_DMA_HEADERS_BUFFER_SZ)
@@ -133,6 +137,7 @@ static u32 feature_map[SDE_DSPP_MAX] = {
 	[SDE_DSPP_DITHER] = REG_DMA_FEATURES_MAX,
 	[SDE_DSPP_DITHER] = REG_DMA_FEATURES_MAX,
 	[SDE_DSPP_HIST] = REG_DMA_FEATURES_MAX,
 	[SDE_DSPP_HIST] = REG_DMA_FEATURES_MAX,
 	[SDE_DSPP_AD] = REG_DMA_FEATURES_MAX,
 	[SDE_DSPP_AD] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_RC] = RC_DATA,
 };
 };
 
 
 static u32 sspp_feature_map[SDE_SSPP_MAX] = {
 static u32 sspp_feature_map[SDE_SSPP_MAX] = {
@@ -159,6 +164,7 @@ static u32 feature_reg_dma_sz[SDE_DSPP_MAX] = {
 	[SDE_DSPP_HSIC] = HSIC_MEM_SIZE,
 	[SDE_DSPP_HSIC] = HSIC_MEM_SIZE,
 	[SDE_DSPP_SIXZONE] = SIXZONE_MEM_SIZE,
 	[SDE_DSPP_SIXZONE] = SIXZONE_MEM_SIZE,
 	[SDE_DSPP_MEMCOLOR] = MEMCOLOR_MEM_SIZE,
 	[SDE_DSPP_MEMCOLOR] = MEMCOLOR_MEM_SIZE,
+	[SDE_DSPP_RC] = RC_MEM_SIZE,
 };
 };
 
 
 static u32 sspp_feature_reg_dma_sz[SDE_SSPP_MAX] = {
 static u32 sspp_feature_reg_dma_sz[SDE_SSPP_MAX] = {
@@ -1117,6 +1123,89 @@ void reg_dmav1_setup_dspp_igcv31(struct sde_hw_dspp *ctx, void *cfg)
 		DRM_ERROR("failed to kick off ret %d\n", rc);
 		DRM_ERROR("failed to kick off ret %d\n", rc);
 }
 }
 
 
+int reg_dmav1_setup_rc_datav1(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct drm_msm_rc_mask_cfg *rc_mask_cfg;
+	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;
+	int rc = 0;
+	u32 i = 0;
+	u32 *data = NULL;
+	u32 buf_sz = 0, abs_offset = 0;
+	u32 cfg_param_07;
+	u64 cfg_param_09;
+
+	rc = reg_dma_dspp_check(ctx, cfg, RC_DATA);
+	if (rc) {
+		DRM_ERROR("invalid dma dspp check rc = %d\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));
+		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]);
+
+	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);
+		return -ENOMEM;
+	}
+
+	DRM_DEBUG_DRIVER("allocating %u bytes of memory for dma\n", buf_sz);
+	data = kzalloc(buf_sz, GFP_KERNEL);
+	if (!data) {
+		DRM_ERROR("memory allocation failed ret %d\n", rc);
+		return -ENOMEM;
+	}
+
+	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%016lX 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) {
+		DRM_ERROR("rc dma write failed ret %d\n", rc);
+		goto exit;
+	}
+
+	/* 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 = dma_ops->kick_off(&kick_off);
+	if (rc) {
+		DRM_ERROR("failed to kick off ret %d\n", rc);
+		goto exit;
+	}
+
+exit:
+	kfree(data);
+	return rc;
+}
+
 static void _dspp_pccv4_off(struct sde_hw_dspp *ctx, void *cfg)
 static void _dspp_pccv4_off(struct sde_hw_dspp *ctx, void *cfg)
 {
 {
 	struct sde_reg_dma_kickoff_cfg kick_off;
 	struct sde_reg_dma_kickoff_cfg kick_off;

+ 9 - 0
msm/sde/sde_hw_reg_dma_v1_color_proc.h

@@ -2,6 +2,7 @@
 /*
 /*
  * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  */
  */
+
 #ifndef _SDE_HW_REG_DMA_V1_COLOR_PROC_H
 #ifndef _SDE_HW_REG_DMA_V1_COLOR_PROC_H
 #define _SDE_HW_REG_DMA_V1_COLOR_PROC_H
 #define _SDE_HW_REG_DMA_V1_COLOR_PROC_H
 
 
@@ -242,6 +243,14 @@ void reg_dmav1_setup_ltm_roiv1(struct sde_hw_dspp *ctx, void *cfg);
  */
  */
 void reg_dmav1_setup_ltm_vlutv1(struct sde_hw_dspp *ctx, void *cfg);
 void reg_dmav1_setup_ltm_vlutv1(struct sde_hw_dspp *ctx, void *cfg);
 
 
+/**
+ * reg_dmav1_setup_rc_datav1() - RC DATA 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);
+
 /**
 /**
  * reg_dmav1_deinit_ltm_ops() - deinitialize the ltm feature op for sde v4
  * reg_dmav1_deinit_ltm_ops() - deinitialize the ltm feature op for sde v4
  *                               which were initialized.
  *                               which were initialized.

+ 3 - 0
msm/sde/sde_reg_dma.h

@@ -57,6 +57,7 @@ enum sde_reg_dma_read_sel {
  * @LTM_INIT: LTM INIT
  * @LTM_INIT: LTM INIT
  * @LTM_ROI: LTM ROI
  * @LTM_ROI: LTM ROI
  * @LTM_VLUT: LTM VLUT
  * @LTM_VLUT: LTM VLUT
+ * @RC_DATA: Rounded corner data
  * @REG_DMA_FEATURES_MAX: invalid selection
  * @REG_DMA_FEATURES_MAX: invalid selection
  */
  */
 enum sde_reg_dma_features {
 enum sde_reg_dma_features {
@@ -75,6 +76,7 @@ enum sde_reg_dma_features {
 	LTM_INIT,
 	LTM_INIT,
 	LTM_ROI,
 	LTM_ROI,
 	LTM_VLUT,
 	LTM_VLUT,
+	RC_DATA,
 	REG_DMA_FEATURES_MAX,
 	REG_DMA_FEATURES_MAX,
 };
 };
 
 
@@ -152,6 +154,7 @@ enum sde_reg_dma_setup_ops {
 };
 };
 
 
 #define REG_DMA_BLK_MAX 32
 #define REG_DMA_BLK_MAX 32
+
 /**
 /**
  * enum sde_reg_dma_blk - defines blocks for which reg dma op should be
  * enum sde_reg_dma_blk - defines blocks for which reg dma op should be
  *                        performed
  *                        performed