Browse Source

msm: drm: sde: Add support for UCSC via AHB programming

Add support for UCSC block parameters that includes unmult,
IGC/GC modes, CSC coefficients, and clamps.

Change-Id: I3ef4b729e9c973a98d53dc583233bf5e004035fa
Signed-off-by: Alisha Thapaliya <[email protected]>
Alisha Thapaliya 3 years ago
parent
commit
c2364bdd17

+ 7 - 0
include/uapi/display/drm/msm_drm_pp.h

@@ -775,4 +775,11 @@ struct drm_msm_misr_sign {
 	__u64 misr_sign_value[MAX_DSI_DISPLAY];
 };
 
+#define UCSC_SUPPORTED
+
+#define UCSC_CSC_CFG0_PARAM_LEN FP16_CSC_CFG0_PARAM_LEN
+#define UCSC_CSC_CFG1_PARAM_LEN FP16_CSC_CFG1_PARAM_LEN
+
+typedef struct drm_msm_fp16_csc drm_msm_ucsc_csc;
+
 #endif /* _MSM_DRM_PP_H_ */

+ 6 - 0
msm/msm_drv.h

@@ -124,6 +124,7 @@ enum msm_mdp_plane_property {
 	PLANE_PROP_FP16_GC,
 	PLANE_PROP_FP16_CSC,
 	PLANE_PROP_UBWC_STATS_ROI,
+	PLANE_PROP_UCSC_CSC,
 
 	/* # of blob properties */
 	PLANE_PROP_BLOBCOUNT,
@@ -147,12 +148,17 @@ enum msm_mdp_plane_property {
 	PLANE_PROP_INVERSE_PMA,
 	PLANE_PROP_FP16_IGC,
 	PLANE_PROP_FP16_UNMULT,
+	PLANE_PROP_UCSC_UNMULT,
+	PLANE_PROP_UCSC_ALPHA_DITHER,
 
 	/* enum/bitmask properties */
 	PLANE_PROP_BLEND_OP,
 	PLANE_PROP_SRC_CONFIG,
 	PLANE_PROP_FB_TRANSLATION_MODE,
 	PLANE_PROP_MULTIRECT_MODE,
+	PLANE_PROP_UCSC_IGC,
+	PLANE_PROP_UCSC_GC,
+
 
 	/* total # of properties */
 	PLANE_PROP_COUNT

+ 106 - 0
msm/sde/sde_hw_catalog.c

@@ -298,6 +298,11 @@ enum {
 	VIG_FP16_GC_PROP,
 	VIG_FP16_CSC_PROP,
 	VIG_FP16_UNMULT_PROP,
+	VIG_UCSC_IGC_PROP,
+	VIG_UCSC_GC_PROP,
+	VIG_UCSC_CSC_PROP,
+	VIG_UCSC_UNMULT_PROP,
+	VIG_UCSC_ALPHA_DITHER_PROP,
 	VIG_PROP_MAX,
 };
 
@@ -312,6 +317,11 @@ enum {
 	DMA_FP16_GC_PROP,
 	DMA_FP16_CSC_PROP,
 	DMA_FP16_UNMULT_PROP,
+	DMA_UCSC_IGC_PROP,
+	DMA_UCSC_GC_PROP,
+	DMA_UCSC_CSC_PROP,
+	DMA_UCSC_UNMULT_PROP,
+	DMA_UCSC_ALPHA_DITHER_PROP,
 	DMA_PROP_MAX,
 };
 
@@ -731,6 +741,16 @@ static struct sde_prop_type vig_prop[] = {
 			PROP_TYPE_U32_ARRAY},
 	[VIG_FP16_UNMULT_PROP] = {VIG_FP16_UNMULT_PROP, "qcom,sde-fp16-unmult",
 			false, PROP_TYPE_U32_ARRAY},
+	[VIG_UCSC_IGC_PROP] = {VIG_UCSC_IGC_PROP, "qcom,sde-ucsc-igc", false,
+			PROP_TYPE_U32_ARRAY},
+	[VIG_UCSC_GC_PROP] = {VIG_UCSC_GC_PROP, "qcom,sde-ucsc-gc", false,
+			PROP_TYPE_U32_ARRAY},
+	[VIG_UCSC_CSC_PROP] = {VIG_UCSC_CSC_PROP, "qcom,sde-ucsc-csc", false,
+			PROP_TYPE_U32_ARRAY},
+	[VIG_UCSC_UNMULT_PROP] = {VIG_UCSC_UNMULT_PROP, "qcom,sde-ucsc-unmult",
+			false, PROP_TYPE_U32_ARRAY},
+	[VIG_UCSC_ALPHA_DITHER_PROP] = {VIG_UCSC_ALPHA_DITHER_PROP, "qcom,sde-ucsc-alpha-dither",
+			false, PROP_TYPE_U32_ARRAY},
 };
 
 static struct sde_prop_type dma_prop[] = {
@@ -754,6 +774,16 @@ static struct sde_prop_type dma_prop[] = {
 			PROP_TYPE_U32_ARRAY},
 	[DMA_FP16_UNMULT_PROP] = {DMA_FP16_UNMULT_PROP, "qcom,sde-fp16-unmult",
 			false, PROP_TYPE_U32_ARRAY},
+	[DMA_UCSC_IGC_PROP] = {DMA_UCSC_IGC_PROP, "qcom,sde-ucsc-igc", false,
+			PROP_TYPE_U32_ARRAY},
+	[DMA_UCSC_GC_PROP] = {DMA_UCSC_GC_PROP, "qcom,sde-ucsc-gc", false,
+			PROP_TYPE_U32_ARRAY},
+	[DMA_UCSC_CSC_PROP] = {DMA_UCSC_CSC_PROP, "qcom,sde-ucsc-csc", false,
+			PROP_TYPE_U32_ARRAY},
+	[DMA_UCSC_UNMULT_PROP] = {DMA_UCSC_UNMULT_PROP, "qcom,sde-ucsc-unmult",
+			false, PROP_TYPE_U32_ARRAY},
+	[DMA_UCSC_ALPHA_DITHER_PROP] = {DMA_UCSC_ALPHA_DITHER_PROP, "qcom,sde-ucsc-alpha-dither",
+			false, PROP_TYPE_U32_ARRAY},
 };
 
 static struct sde_prop_type ctl_prop[] = {
@@ -1558,6 +1588,12 @@ static int _sde_sspp_setup_vigs(struct device_node *np,
 		sblk->num_fp16_csc_blk = 0;
 		sblk->num_fp16_unmult_blk = 0;
 
+		sblk->num_ucsc_igc_blk = 0;
+		sblk->num_ucsc_gc_blk = 0;
+		sblk->num_ucsc_csc_blk = 0;
+		sblk->num_ucsc_unmult_blk = 0;
+		sblk->num_ucsc_alpha_dither_blk = 0;
+
 		for (j = 0; j < SSPP_SUBBLK_COUNT_MAX; j++) {
 			if (!props[j])
 				continue;
@@ -1589,6 +1625,41 @@ static int _sde_sspp_setup_vigs(struct device_node *np,
 					SDE_SSPP_FP16_UNMULT,
 					VIG_FP16_UNMULT_PROP, true))
 				sblk->num_fp16_unmult_blk += 1;
+
+			if (_sde_sspp_setup_vcm(sspp, props[j],
+					"sspp_vig_ucsc_igc",
+					&sblk->ucsc_igc_blk[j],
+					SDE_SSPP_UCSC_IGC, VIG_UCSC_IGC_PROP,
+					true))
+				sblk->num_ucsc_igc_blk += 1;
+
+			if (_sde_sspp_setup_vcm(sspp, props[j],
+					"sspp_vig_ucsc_gc",
+					&sblk->ucsc_gc_blk[j],
+					SDE_SSPP_UCSC_GC, VIG_UCSC_GC_PROP,
+					true))
+				sblk->num_ucsc_gc_blk += 1;
+
+			if (_sde_sspp_setup_vcm(sspp, props[j],
+					"sspp_vig_ucsc_csc",
+					&sblk->ucsc_csc_blk[j],
+					SDE_SSPP_UCSC_CSC, VIG_UCSC_CSC_PROP,
+					true))
+				sblk->num_ucsc_csc_blk += 1;
+
+			if (_sde_sspp_setup_vcm(sspp, props[j],
+					"sspp_vig_ucsc_unmult",
+					&sblk->ucsc_unmult_blk[j],
+					SDE_SSPP_UCSC_UNMULT,
+					VIG_UCSC_UNMULT_PROP, true))
+				sblk->num_ucsc_unmult_blk += 1;
+
+			if (_sde_sspp_setup_vcm(sspp, props[j],
+					"sspp_vig_ucsc_alpha_dither",
+					&sblk->ucsc_alpha_dither_blk[j],
+					SDE_SSPP_UCSC_ALPHA_DITHER,
+					VIG_UCSC_ALPHA_DITHER_PROP, true))
+				sblk->num_ucsc_alpha_dither_blk += 1;
 		}
 
 		/* PP + scaling only supported on VIG rect 0 */
@@ -1804,6 +1875,41 @@ static int _sde_sspp_setup_dmas(struct device_node *np,
 						&sblk->fp16_unmult_blk[j],
 						SDE_SSPP_FP16_UNMULT,
 						DMA_FP16_UNMULT_PROP, true);
+
+			if (props[j]->exists[DMA_UCSC_IGC_PROP])
+				_sde_sspp_setup_dgm(sspp, props[j],
+						"sspp_dma_ucsc_igc",
+						&sblk->ucsc_igc_blk[j],
+						SDE_SSPP_UCSC_IGC,
+						DMA_UCSC_IGC_PROP, true);
+
+			if (props[j]->exists[DMA_UCSC_GC_PROP])
+				_sde_sspp_setup_dgm(sspp, props[j],
+						"sspp_dma_ucsc_gc",
+						&sblk->ucsc_gc_blk[j],
+						SDE_SSPP_UCSC_GC,
+						DMA_UCSC_GC_PROP, true);
+
+			if (props[j]->exists[DMA_UCSC_CSC_PROP])
+				_sde_sspp_setup_dgm(sspp, props[j],
+						"sspp_dma_ucsc_csc",
+						&sblk->ucsc_csc_blk[j],
+						SDE_SSPP_UCSC_CSC,
+						DMA_UCSC_CSC_PROP, true);
+
+			if (props[j]->exists[DMA_UCSC_UNMULT_PROP])
+				_sde_sspp_setup_dgm(sspp, props[j],
+						"sspp_dma_ucsc_unmult",
+						&sblk->ucsc_unmult_blk[j],
+						SDE_SSPP_UCSC_UNMULT,
+						DMA_UCSC_UNMULT_PROP, true);
+
+			if (props[j]->exists[DMA_UCSC_ALPHA_DITHER_PROP])
+				_sde_sspp_setup_dgm(sspp, props[j],
+						"sspp_dma_ucsc_alpha_dither",
+						&sblk->ucsc_alpha_dither_blk[j],
+						SDE_SSPP_UCSC_ALPHA_DITHER,
+						DMA_UCSC_ALPHA_DITHER_PROP, true);
 		}
 	}
 

+ 32 - 0
msm/sde/sde_hw_catalog.h

@@ -317,6 +317,11 @@ enum {
  * @SDE_SSPP_UBWC_STATS:     Support for ubwc stats
  * @SDE_SSPP_SCALER_DE_LPF_BLEND:     Support for detail enhancer
  * @SDE_SSPP_LINE_INSERTION  Line insertion support
+ * @SDE_SSPP_UCSC_IGC        UCSC IGC color processing block support
+ * @SDE_SSPP_UCSC_GC         UCSC GC color processing block support
+ * @SDE_SSPP_UCSC_CSC        UCSC CSC color processing block support
+ * @SDE_SSPP_UCSC_UNMULT     UCSC alpha unmult color processing block support
+ * @SDE_SSPP_UCSC_ALPHA_DITHER UCSC alpha dither color processing block support
  * @SDE_SSPP_MAX             maximum value
  */
 enum {
@@ -354,6 +359,11 @@ enum {
 	SDE_SSPP_UBWC_STATS,
 	SDE_SSPP_SCALER_DE_LPF_BLEND,
 	SDE_SSPP_LINE_INSERTION,
+	SDE_SSPP_UCSC_IGC,
+	SDE_SSPP_UCSC_GC,
+	SDE_SSPP_UCSC_CSC,
+	SDE_SSPP_UCSC_UNMULT,
+	SDE_SSPP_UCSC_ALPHA_DITHER,
 	SDE_SSPP_MAX
 };
 
@@ -742,6 +752,7 @@ enum {
  * @SDE_FEATURE_SYS_CACHE_NSE  Support for no-self-evict feature
  * @SDE_FEATURE_HW_FENCE_IPCC  HW fence supports ipcc signaling in dpu
  * @SDE_FEATURE_EMULATED_ENV   Emulated environment supported
+ * @SDE_FEATURE_UCSC_SUPPORTED  UCSC pipe format supported
  * @SDE_FEATURE_MAX:             MAX features value
  */
 enum sde_mdss_features {
@@ -786,6 +797,7 @@ enum sde_mdss_features {
 	SDE_FEATURE_SYS_CACHE_NSE,
 	SDE_FEATURE_HW_FENCE_IPCC,
 	SDE_FEATURE_EMULATED_ENV,
+	SDE_FEATURE_UCSC_SUPPORTED,
 	SDE_FEATURE_MAX
 };
 
@@ -969,6 +981,16 @@ enum sde_danger_safe_lut_types {
  * @fp16_csc_blk: FP16 CSC block array
  * @num_fp16_unmult_blk: number of FP16 UNMULT blocks
  * @fp16_unmult_blk: FP16 UNMULT block array
+ * @num_ucsc_igc_blk: number of UCSC IGC blocks
+ * @ucsc_igc_blk: UCSC IGC block array
+ * @num_ucsc_gc_blk: number of UCSC GC blocks
+ * @ucsc_gc_blk: UCSC GC block array
+ * @num_ucsc_csc_blk: number of UCSC CSC blocks
+ * @ucsc_csc_blk: UCSC CSC block array
+ * @num_ucsc_unmult_blk: number of ucsc UNMULT blocks
+ * @ucsc_unmult_blk: UCSC UNMULT block array
+ * @num_ucsc_alpha_dither_blk: number of ucsc ALPHA DITHER blocks
+ * @ucsc_alpha_dither_blk: UCSC ALPHA DITHER block array
  * @unmult_offset: Unmult register offset
  * @format_list: Pointer to list of supported formats
  * @virt_format_list: Pointer to list of supported formats for virtual planes
@@ -1021,6 +1043,16 @@ struct sde_sspp_sub_blks {
 	struct sde_pp_blk fp16_csc_blk[SSPP_SUBBLK_COUNT_MAX];
 	u32 num_fp16_unmult_blk;
 	struct sde_pp_blk fp16_unmult_blk[SSPP_SUBBLK_COUNT_MAX];
+	u32 num_ucsc_igc_blk;
+	struct sde_pp_blk ucsc_igc_blk[SSPP_SUBBLK_COUNT_MAX];
+	u32 num_ucsc_gc_blk;
+	struct sde_pp_blk ucsc_gc_blk[SSPP_SUBBLK_COUNT_MAX];
+	u32 num_ucsc_csc_blk;
+	struct sde_pp_blk ucsc_csc_blk[SSPP_SUBBLK_COUNT_MAX];
+	u32 num_ucsc_unmult_blk;
+	struct sde_pp_blk ucsc_unmult_blk[SSPP_SUBBLK_COUNT_MAX];
+	u32 num_ucsc_alpha_dither_blk;
+	struct sde_pp_blk ucsc_alpha_dither_blk[SSPP_SUBBLK_COUNT_MAX];
 	u32 unmult_offset[SSPP_SUBBLK_COUNT_MAX];
 
 	const struct sde_format_extended *format_list;

+ 283 - 0
msm/sde/sde_hw_color_proc_v4.c

@@ -759,3 +759,286 @@ int sde_spr_read_opr_value(struct sde_hw_dspp *ctx, uint32_t *opr_value)
 
 	return 0;
 }
+
+void sde_setup_ucsc_cscv1(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index, void *data)
+{
+	struct sde_hw_cp_cfg *hw_cfg = data;
+	drm_msm_ucsc_csc *ucsc_csc;
+	u32 csc_base, csc, i, offset = 0;
+
+	if (!ctx || !data || index == SDE_SSPP_RECT_MAX) {
+		DRM_ERROR("invalid parameter\tctx: %pK\tdata: %pK\tindex: %d\n",
+				ctx, data, index);
+		return;
+	}
+
+	if (index == SDE_SSPP_RECT_SOLO || index == SDE_SSPP_RECT_0)
+		csc_base = ctx->cap->sblk->ucsc_csc_blk[0].base;
+	else
+		csc_base = ctx->cap->sblk->ucsc_csc_blk[1].base;
+
+	if (!csc_base) {
+		DRM_ERROR("invalid offset for UCSC CSC CP block\tpipe: %d\tindex: %d\n",
+				ctx->idx, index);
+		return;
+	}
+
+	ucsc_csc = (drm_msm_ucsc_csc *)(hw_cfg->payload);
+	if (!ucsc_csc)
+		goto write_base;
+
+	if (hw_cfg->len != sizeof(drm_msm_ucsc_csc) ||
+			!hw_cfg->payload) {
+		DRM_ERROR("invalid hw_cfg payload\tpipe: %d\tindex: %d\tlen: %d\tpayload: %pK\n",
+				ctx->idx, index, hw_cfg->len, hw_cfg->payload);
+		return;
+	}
+
+	if (ucsc_csc->cfg_param_0_len != UCSC_CSC_CFG0_PARAM_LEN) {
+		DRM_ERROR("invalid param 0 length! Got: %d\tExpected: %d\tpipe: %d\tindex: %d\n",
+				ucsc_csc->cfg_param_0_len, UCSC_CSC_CFG0_PARAM_LEN,
+				ctx->idx, index);
+		return;
+	} else if (ucsc_csc->cfg_param_1_len !=  UCSC_CSC_CFG1_PARAM_LEN) {
+		DRM_ERROR("invalid param 1 length! Got: %d\tExpected: %d\tpipe: %d\tindex: %d\n",
+				ucsc_csc->cfg_param_1_len, UCSC_CSC_CFG1_PARAM_LEN,
+				ctx->idx, index);
+		return;
+	}
+
+	for (i = 0; i < (ucsc_csc->cfg_param_0_len / 2); i++) {
+		offset += 0x4;
+		csc = ucsc_csc->cfg_param_0[2 * i] & 0xFFFF;
+		csc |= (ucsc_csc->cfg_param_0[2 * i + 1] & 0xFFFF) << 16;
+		SDE_REG_WRITE(&ctx->hw, csc_base + offset, csc);
+	}
+	for (i = 0; i < (ucsc_csc->cfg_param_1_len / 2); i++) {
+		offset += 0x4;
+		csc = ucsc_csc->cfg_param_1[2 * i] & 0xFFFF;
+		csc |= (ucsc_csc->cfg_param_1[2 * i + 1] & 0xFFFF) << 16;
+		SDE_REG_WRITE(&ctx->hw, csc_base + offset, csc);
+	}
+
+write_base:
+	csc = SDE_REG_READ(&ctx->hw, csc_base);
+	if (ucsc_csc)
+		csc |= BIT(2);
+	else
+		csc &= ~BIT(2);
+
+	SDE_REG_WRITE(&ctx->hw, csc_base, csc);
+}
+
+void sde_setup_ucsc_gcv1(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index, void *data)
+{
+	struct sde_hw_cp_cfg *hw_cfg = data;
+	int *ucsc_gc;
+	u32 gc_base, gc;
+
+	if (!ctx || !data || index == SDE_SSPP_RECT_MAX) {
+		DRM_ERROR("invalid parameter\tctx: %pK\tdata: %pK\tindex: %d\n",
+				ctx, data, index);
+		return;
+	}
+
+	if (index == SDE_SSPP_RECT_SOLO || index == SDE_SSPP_RECT_0)
+		gc_base = ctx->cap->sblk->ucsc_gc_blk[0].base;
+	else
+		gc_base = ctx->cap->sblk->ucsc_gc_blk[1].base;
+
+	if (!gc_base) {
+		DRM_ERROR("invalid offset for UCSC GC CP block\tpipe: %d\tindex: %d\n",
+				ctx->idx, index);
+		return;
+	}
+
+	ucsc_gc = (int *)(hw_cfg->payload);
+
+	if (!ucsc_gc || (hw_cfg->len != sizeof(int))) {
+		DRM_ERROR("invalid hw_cfg payload\tpipe: %d\tindex: %d\tlen: %d\tpayload: %pK\n",
+				ctx->idx, index, hw_cfg->len, ucsc_gc);
+		return;
+	}
+
+	gc = SDE_REG_READ(&ctx->hw, gc_base);
+	gc &= 0x60707;
+
+	if (*ucsc_gc == UCSC_GC_MODE_DISABLE) {
+		DRM_INFO("UCSC GC is not enabled!\n");
+		goto reset_gc;
+	}
+
+	if (ucsc_gc && *ucsc_gc) {
+		gc |= BIT(4);
+
+		switch (*ucsc_gc)
+		{
+		case UCSC_GC_MODE_SRGB:
+			break;
+		case UCSC_GC_MODE_PQ:
+			gc |= BIT(5);
+			break;
+		case UCSC_GC_MODE_GAMMA2_2:
+			gc |= BIT(6);
+			break;
+		case UCSC_GC_MODE_HLG:
+			gc |= BIT(5)|BIT(6);
+			break;
+		default:
+			DRM_ERROR("Invalid UCSC GC mode \tmode: %d\n", *ucsc_gc);
+			return;
+		}
+	}
+
+reset_gc:
+	SDE_REG_WRITE(&ctx->hw, gc_base, gc);
+}
+
+void sde_setup_ucsc_igcv1(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index, void *data)
+{
+	struct sde_hw_cp_cfg *hw_cfg = data;
+	int *ucsc_igc;
+	u32 igc_base, igc;
+
+	if (!ctx || !data || index == SDE_SSPP_RECT_MAX) {
+		DRM_ERROR("invalid parameter\tctx: %pK\tdata: %pK\tindex: %d\n",
+				ctx, data, index);
+		return;
+	}
+
+	if (index == SDE_SSPP_RECT_SOLO || index == SDE_SSPP_RECT_0)
+		igc_base = ctx->cap->sblk->ucsc_igc_blk[0].base;
+	else
+		igc_base = ctx->cap->sblk->ucsc_igc_blk[1].base;
+
+	if (!igc_base) {
+		DRM_ERROR("invalid offset for UCSC GC CP block\tpipe: %d\tindex: %d\n",
+				ctx->idx, index);
+		return;
+	}
+
+	ucsc_igc = (int *)(hw_cfg->payload);
+
+	if (!ucsc_igc || (hw_cfg->len != sizeof(int))) {
+		DRM_ERROR("invalid hw_cfg payload\tpipe: %d\tindex: %d\tlen: %d\tpayload: %pK\n",
+				ctx->idx, index, hw_cfg->len, ucsc_igc);
+		return;
+	}
+
+	igc = SDE_REG_READ(&ctx->hw, igc_base);
+	igc &= 0x600FD;
+
+	if (*ucsc_igc == UCSC_IGC_MODE_DISABLE) {
+		DRM_INFO("UCSC IGC is not enabled!\n");
+		goto reset_igc;
+	}
+
+	if (ucsc_igc && *ucsc_igc) {
+		igc |= BIT(1);
+
+		switch (*ucsc_igc)
+		{
+		case UCSC_IGC_MODE_SRGB:
+			break;
+		case UCSC_IGC_MODE_REC709:
+			igc |= BIT(8);
+			break;
+		case UCSC_IGC_MODE_GAMMA2_2:
+			igc |= BIT(9);
+			break;
+		case UCSC_IGC_MODE_HLG:
+			igc |= BIT(8)|BIT(9);
+			break;
+		case UCSC_IGC_MODE_PQ:
+			igc |= BIT(10);
+			break;
+		default:
+		    DRM_ERROR("Invalid UCSC IGC mode \tmode: %d\n", *ucsc_igc);
+			return;
+		}
+	}
+
+reset_igc:
+	SDE_REG_WRITE(&ctx->hw, igc_base, igc);
+}
+
+void sde_setup_ucsc_unmultv1(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index, void *data)
+{
+	struct sde_hw_cp_cfg *hw_cfg = data;
+	bool *ucsc_unmult;
+	u32 unmult_base, unmult;
+
+	if (!ctx || !data || index == SDE_SSPP_RECT_MAX) {
+		DRM_ERROR("invalid parameter\tctx: %pK\tdata: %pK\tindex: %d\n",
+				ctx, data, index);
+		return;
+	} else if (hw_cfg->len != sizeof(bool) || !hw_cfg->payload) {
+		DRM_ERROR("invalid hw_cfg payload\tpipe: %d\tindex: %d\tlen: %d\tpayload: %pK\n",
+				  ctx->idx, index, hw_cfg->len, hw_cfg->payload);
+		return;
+	}
+
+	if (index == SDE_SSPP_RECT_SOLO || index == SDE_SSPP_RECT_0)
+		unmult_base = ctx->cap->sblk->ucsc_unmult_blk[0].base;
+	else
+		unmult_base = ctx->cap->sblk->ucsc_unmult_blk[1].base;
+
+	if (!unmult_base) {
+		DRM_ERROR("invalid offset for UCSC UNMULT CP block\tpipe: %d\tindex: %d\n",
+				ctx->idx, index);
+		return;
+	}
+
+	unmult = SDE_REG_READ(&ctx->hw, unmult_base);
+	ucsc_unmult = (bool *)(hw_cfg->payload);
+
+	if (ucsc_unmult && *ucsc_unmult)
+		unmult |= BIT(0)|BIT(18);
+	else
+		unmult &= ~(BIT(0)|BIT(18));
+
+	SDE_REG_WRITE(&ctx->hw, unmult_base, unmult);
+}
+
+void sde_setup_ucsc_alpha_ditherv1(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index, void *data)
+{
+	struct sde_hw_cp_cfg *hw_cfg = data;
+	bool *ucsc_alpha_dither;
+	u32 alpha_dither_base, alpha_dither;
+
+	if (!ctx || index == SDE_SSPP_RECT_MAX) {
+		DRM_ERROR("invalid parameter\tctx: %pK\tindex: %d\n",
+				ctx, index);
+		return;
+	} else if (hw_cfg->len != sizeof(bool) || !hw_cfg->payload) {
+		DRM_ERROR("invalid hw_cfg payload\tpipe: %d\tindex: %d\tlen: %d\tpayload: %pK\n",
+				  ctx->idx, index, hw_cfg->len, hw_cfg->payload);
+		return;
+	}
+
+	if (index == SDE_SSPP_RECT_SOLO || index == SDE_SSPP_RECT_0)
+		alpha_dither_base = ctx->cap->sblk->ucsc_alpha_dither_blk[0].base;
+	else
+		alpha_dither_base = ctx->cap->sblk->ucsc_alpha_dither_blk[1].base;
+
+	if (!alpha_dither_base) {
+		DRM_ERROR("invalid offset for UCSC ALPHA DITHER CP block\tpipe: %d\tindex: %d\n",
+				ctx->idx, index);
+		return;
+	}
+
+	alpha_dither = SDE_REG_READ(&ctx->hw, alpha_dither_base);
+	ucsc_alpha_dither = (bool *)(hw_cfg->payload);
+
+	if (ucsc_alpha_dither && *ucsc_alpha_dither)
+		alpha_dither |= BIT(17);
+	else
+		alpha_dither &= ~BIT(17);
+
+	SDE_REG_WRITE(&ctx->hw, alpha_dither_base, alpha_dither);
+}

+ 45 - 0
msm/sde/sde_hw_color_proc_v4.h

@@ -161,4 +161,49 @@ void sde_demura_pu_cfg(struct sde_hw_dspp *ctx, void *cfg);
  */
 int sde_spr_read_opr_value(struct sde_hw_dspp *ctx, uint32_t *opr_value);
 
+/**
+ * sde_setup_ucsc_cscv1 - api to set UCSC CSC cp block
+ * @ctx: pointer to pipe object
+ * @index: pipe rectangle to operate on
+ * @data: pointer to sde_hw_cp_cfg object containing drm_msm_ucsc_csc data
+ */
+void sde_setup_ucsc_cscv1(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index, void *data);
+
+/**
+ * sde_setup_ucsc_gcv1 - api to set UCSC GC cp block
+ * @ctx: pointer to pipe object
+ * @index: pipe rectangle to operate on
+ * @data: pointer to sde_hw_cp_cfg object containing gc mode data
+ */
+void sde_setup_ucsc_gcv1(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index, void *data);
+
+/**
+ * sde_setup_ucsc_igcv1 - api to set UCSC IGC cp block
+ * @ctx: pointer to pipe object
+ * @index: pipe rectangle to operate on
+ * @data: pointer to sde_hw_cp_cfg object containing igc mode data
+ */
+void sde_setup_ucsc_igcv1(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index, void *data);
+
+/**
+ * sde_setup_ucsc_unmultv1 - api to set UCSC UNMULT cp block
+ * @ctx: pointer to pipe object
+ * @index: pipe rectangle to operate on
+ * @data: pointer to sde_hw_cp_cfg object containing bool data
+ */
+void sde_setup_ucsc_unmultv1(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index, void *data);
+
+/**
+ * sde_setup_ucsc_alpha_ditherv1 - api to set UCSC ALPHA DITHER cp block
+ * @ctx: pointer to pipe object
+ * @index: pipe rectangle to operate on
+ * @data: pointer to sde_hw_cp_cfg object containing bool data
+ */
+void sde_setup_ucsc_alpha_ditherv1(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index, void *data);
+
 #endif /* _SDE_HW_COLOR_PROC_V4_H_ */

+ 32 - 0
msm/sde/sde_hw_sspp.c

@@ -59,6 +59,7 @@
 #define SSPP_DGM_CSC_1                     0x1800
 #define SSPP_DGM_CSC_SIZE                  0xFC
 #define VIG_GAMUT_SIZE                     0x1CC
+#define SSPP_UCSC_SIZE                     0x80
 
 #define MDSS_MDP_OP_DEINTERLACE            BIT(22)
 #define MDSS_MDP_OP_DEINTERLACE_ODD        BIT(23)
@@ -1340,6 +1341,26 @@ static void _setup_layer_ops_colorproc(struct sde_hw_pipe *c,
 	if (test_bit(SDE_SSPP_FP16_UNMULT, &features) &&
 			IS_SDE_CP_VER_1_0(c->cap->sblk->fp16_unmult_blk[0].version))
 		c->ops.setup_fp16_unmult = sde_setup_fp16_unmultv1;
+
+	if (test_bit(SDE_SSPP_UCSC_IGC, &features) &&
+			IS_SDE_CP_VER_1_0(c->cap->sblk->ucsc_igc_blk[0].version))
+		c->ops.setup_ucsc_igc = sde_setup_ucsc_igcv1;
+
+	if (test_bit(SDE_SSPP_UCSC_GC, &features) &&
+			IS_SDE_CP_VER_1_0(c->cap->sblk->ucsc_gc_blk[0].version))
+		c->ops.setup_ucsc_gc = sde_setup_ucsc_gcv1;
+
+	if (test_bit(SDE_SSPP_UCSC_CSC, &features) &&
+			IS_SDE_CP_VER_1_0(c->cap->sblk->ucsc_csc_blk[0].version))
+		c->ops.setup_ucsc_csc = sde_setup_ucsc_cscv1;
+
+	if (test_bit(SDE_SSPP_UCSC_UNMULT, &features) &&
+			IS_SDE_CP_VER_1_0(c->cap->sblk->ucsc_unmult_blk[0].version))
+		c->ops.setup_ucsc_unmult = sde_setup_ucsc_unmultv1;
+
+	if (test_bit(SDE_SSPP_UCSC_ALPHA_DITHER, &features) &&
+			IS_SDE_CP_VER_1_0(c->cap->sblk->ucsc_alpha_dither_blk[0].version))
+		c->ops.setup_ucsc_alpha_dither = sde_setup_ucsc_alpha_ditherv1;
 }
 
 static void sde_hw_sspp_setup_inverse_pma(struct sde_hw_pipe *ctx,
@@ -1672,6 +1693,17 @@ struct sde_hw_pipe *sde_hw_sspp_init(enum sde_sspp idx,
 				hw_pipe->hw.blk_off + cfg->sblk->gamut_blk.base + VIG_GAMUT_SIZE,
 				hw_pipe->hw.xin_id);
 		}
+
+		if (test_bit(SDE_SSPP_UCSC_CSC, &hw_pipe->cap->features)) {
+			sde_dbg_reg_register_dump_range(SDE_DBG_NAME, "UCSC_0",
+				hw_pipe->hw.blk_off + cfg->sblk->ucsc_csc_blk[0].base,
+				hw_pipe->hw.blk_off + cfg->sblk->ucsc_csc_blk[0].base +\
+				SSPP_UCSC_SIZE, hw_pipe->hw.xin_id);
+			sde_dbg_reg_register_dump_range(SDE_DBG_NAME, "UCSC_0",
+				hw_pipe->hw.blk_off + cfg->sblk->ucsc_csc_blk[1].base,
+				hw_pipe->hw.blk_off + cfg->sblk->ucsc_csc_blk[1].base +\
+				SSPP_UCSC_SIZE, hw_pipe->hw.xin_id);
+		}
 	}
 
 	if (cfg->sblk->scaler_blk.len && !is_virtual_pipe)

+ 62 - 0
msm/sde/sde_hw_sspp.h

@@ -97,6 +97,23 @@ enum sde_hw_filter_yuv {
 	SDE_SCALE_BIL
 };
 
+enum sde_sspp_ucsc_gc {
+	UCSC_GC_MODE_DISABLE = 0,
+	UCSC_GC_MODE_SRGB,
+	UCSC_GC_MODE_PQ,
+	UCSC_GC_MODE_GAMMA2_2,
+	UCSC_GC_MODE_HLG,
+};
+
+enum sde_sspp_ucsc_igc {
+	UCSC_IGC_MODE_DISABLE = 0,
+	UCSC_IGC_MODE_SRGB,
+	UCSC_IGC_MODE_REC709,
+	UCSC_IGC_MODE_GAMMA2_2,
+	UCSC_IGC_MODE_HLG,
+	UCSC_IGC_MODE_PQ,
+};
+
 struct sde_hw_sharp_cfg {
 	u32 strength;
 	u32 edge_thr;
@@ -713,6 +730,51 @@ struct sde_hw_sspp_ops {
 	void (*setup_line_insertion)(struct sde_hw_pipe *ctx,
 				     enum sde_sspp_multirect_index index,
 				     struct sde_hw_pipe_line_insertion_cfg *cfg);
+
+	/**
+	 * setup_ucsc_csc - set UCSC CSC cp block
+	 * @ctx: Pointer to pipe object
+	 * @index: Pipe rectangle to operate on
+	 * @data: Pointer to sde_hw_cp_cfg object holding drm_msm_ucsc_csc data
+	 */
+	void (*setup_ucsc_csc)(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index, void *data);
+
+	/**
+	 * sde_setup_ucsc_gcv1 - set UCSC GC cp block
+	 * @ctx: Pointer to pipe object
+	 * @index: Pipe rectangle to operate on
+	 * @mode: Pointer to sde_hw_cp_cfg object holding GC mode data
+	 */
+	void (*setup_ucsc_gc)(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index, void *data);
+
+	/**
+	 * sde_setup_ucsc_igcv1 - set UCSC IGC cp block
+	 * @ctx: Pointer to pipe object
+	 * @index: Pipe rectangle to operate on
+	 * @mode: Pointer to sde_hw_cp_cfg object containing IGC mode data
+	 */
+	void (*setup_ucsc_igc)(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index, void *data);
+
+	/**
+	 * sde_setup_ucsc_unmultv1 - set UCSC UNMULT cp block
+	 * @ctx: Pointer to pipe object
+	 * @index: Pipe rectangle to operate on
+	 * @data: Pointer to sde_hw_cp_cfg object containing bool data
+	 */
+	void (*setup_ucsc_unmult)(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index, void *data);
+
+	/**
+	 * sde_setup_ucsc_alpha_ditherv1 - set UCSC ALPHA DITHER cp block
+	 * @ctx: Pointer to pipe object
+	 * @index: Pipe rectangle to operate on
+	 * @data: Pointer to sde_hw_cp_cfg object containing bool data
+	 */
+	void (*setup_ucsc_alpha_dither)(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index, void *data);
 };
 
 /**

+ 122 - 1
msm/sde/sde_plane.c

@@ -1163,9 +1163,11 @@ static void sde_color_process_plane_setup(struct drm_plane *plane)
 	size_t memcol_sz = 0, size = 0;
 	struct sde_hw_cp_cfg hw_cfg = {};
 	struct sde_hw_ctl *ctl = _sde_plane_get_hw_ctl(plane);
-	bool fp16_igc, fp16_unmult;
+	bool fp16_igc, fp16_unmult, ucsc_unmult, ucsc_alpha_dither;
+	int ucsc_gc, ucsc_igc;
 	struct drm_msm_fp16_gc *fp16_gc = NULL;
 	struct drm_msm_fp16_csc *fp16_csc = NULL;
+	struct drm_msm_ucsc_csc *ucsc_csc = NULL;
 
 	psde = to_sde_plane(plane);
 	pstate = to_sde_plane_state(plane->state);
@@ -1317,6 +1319,68 @@ static void sde_color_process_plane_setup(struct drm_plane *plane)
 		psde->pipe_hw->ops.setup_fp16_unmult(psde->pipe_hw,
 				pstate->multirect_index, &hw_cfg);
 	}
+
+	if (pstate->dirty & SDE_PLANE_DIRTY_UCSC_IGC &&
+			psde->pipe_hw->ops.setup_ucsc_igc) {
+		ucsc_igc = sde_plane_get_property(pstate,
+				PLANE_PROP_UCSC_IGC);
+		hw_cfg.last_feature = 0;
+		hw_cfg.ctl = ctl;
+		hw_cfg.len = sizeof(int);
+		hw_cfg.payload = &ucsc_igc;
+		psde->pipe_hw->ops.setup_ucsc_igc(psde->pipe_hw,
+				pstate->multirect_index, &hw_cfg);
+	}
+
+	if (pstate->dirty & SDE_PLANE_DIRTY_UCSC_GC &&
+			psde->pipe_hw->ops.setup_ucsc_gc) {
+		ucsc_gc = sde_plane_get_property(pstate,
+				PLANE_PROP_UCSC_GC);
+		hw_cfg.last_feature = 0;
+		hw_cfg.ctl = ctl;
+		hw_cfg.len = sizeof(int);
+		hw_cfg.payload = &ucsc_gc;
+		psde->pipe_hw->ops.setup_ucsc_gc(psde->pipe_hw,
+				pstate->multirect_index, &hw_cfg);
+	}
+
+	if (pstate->dirty & SDE_PLANE_DIRTY_UCSC_CSC &&
+			psde->pipe_hw->ops.setup_ucsc_csc) {
+		ucsc_csc = msm_property_get_blob(&psde->property_info,
+				&pstate->property_state,
+				&size,
+				PLANE_PROP_UCSC_CSC);
+		hw_cfg.last_feature = 0;
+		hw_cfg.ctl = ctl;
+		hw_cfg.len = size;
+		hw_cfg.payload = ucsc_csc;
+		psde->pipe_hw->ops.setup_ucsc_csc(psde->pipe_hw,
+				pstate->multirect_index, &hw_cfg);
+	}
+
+	if (pstate->dirty & SDE_PLANE_DIRTY_UCSC_UNMULT &&
+			psde->pipe_hw->ops.setup_ucsc_unmult) {
+		ucsc_unmult = !!sde_plane_get_property(pstate,
+				PLANE_PROP_UCSC_UNMULT);
+		hw_cfg.last_feature = 0;
+		hw_cfg.ctl = ctl;
+		hw_cfg.len = sizeof(bool);
+		hw_cfg.payload = &ucsc_unmult;
+		psde->pipe_hw->ops.setup_ucsc_unmult(psde->pipe_hw,
+				pstate->multirect_index, &hw_cfg);
+	}
+
+	if (pstate->dirty & SDE_PLANE_DIRTY_UCSC_ALPHA_DITHER &&
+			psde->pipe_hw->ops.setup_ucsc_alpha_dither) {
+		ucsc_alpha_dither = !!sde_plane_get_property(pstate,
+				PLANE_PROP_UCSC_ALPHA_DITHER);
+		hw_cfg.last_feature = 0;
+		hw_cfg.ctl = ctl;
+		hw_cfg.len = sizeof(bool);
+		hw_cfg.payload = &ucsc_alpha_dither;
+		psde->pipe_hw->ops.setup_ucsc_alpha_dither(psde->pipe_hw,
+				pstate->multirect_index, &hw_cfg);
+	}
 }
 
 static void _sde_plane_setup_scaler(struct sde_plane *psde,
@@ -3669,6 +3733,23 @@ static void _sde_plane_install_colorproc_properties(struct sde_plane *psde,
 	char feature_name[256];
 	bool is_master = !psde->is_virtual;
 
+	static const struct drm_prop_enum_list ucsc_gc[] = {
+		{UCSC_GC_MODE_DISABLE, "disable"},
+		{UCSC_GC_MODE_SRGB, "srgb"},
+		{UCSC_GC_MODE_PQ, "pq"},
+		{UCSC_GC_MODE_GAMMA2_2, "gamma2_2"},
+		{UCSC_GC_MODE_HLG, "hlg"},
+	};
+
+	static const struct drm_prop_enum_list ucsc_igc[] = {
+		{UCSC_IGC_MODE_DISABLE, "disable"},
+		{UCSC_IGC_MODE_SRGB, "srgb"},
+		{UCSC_IGC_MODE_REC709, "rec709"},
+		{UCSC_IGC_MODE_GAMMA2_2, "gamma2_2"},
+		{UCSC_IGC_MODE_HLG, "hlg"},
+		{UCSC_IGC_MODE_PQ, "pq"},
+	};
+
 	if ((is_master &&
 		(psde->features & BIT(SDE_SSPP_INVERSE_PMA))) ||
 		(psde->features & BIT(SDE_SSPP_DGM_INVERSE_PMA))) {
@@ -3765,6 +3846,46 @@ static void _sde_plane_install_colorproc_properties(struct sde_plane *psde,
 		msm_property_install_range(&psde->property_info, feature_name,
 			0x0, 0, 1, 0, PLANE_PROP_FP16_UNMULT);
 	}
+
+	if (psde->features & BIT(SDE_SSPP_UCSC_IGC)) {
+		snprintf(feature_name, sizeof(feature_name), "%s%d",
+			"SDE_SSPP_UCSC_IGC_V",
+			psde->pipe_sblk->ucsc_igc_blk[0].version >> 16);
+		msm_property_install_enum(&psde->property_info, feature_name,
+				0x0, 0, ucsc_igc, ARRAY_SIZE(ucsc_igc), 0, PLANE_PROP_UCSC_IGC);
+	}
+
+	if (psde->features & BIT(SDE_SSPP_UCSC_GC)) {
+		snprintf(feature_name, sizeof(feature_name), "%s%d",
+			"SDE_SSPP_UCSC_GC_V",
+			psde->pipe_sblk->ucsc_gc_blk[0].version >> 16);
+		msm_property_install_enum(&psde->property_info, feature_name,
+		0x0, 0, ucsc_gc, ARRAY_SIZE(ucsc_gc), 0, PLANE_PROP_UCSC_GC);
+	}
+
+	if (psde->features & BIT(SDE_SSPP_UCSC_CSC)) {
+		snprintf(feature_name, sizeof(feature_name), "%s%d",
+			"SDE_SSPP_UCSC_CSC_V",
+			psde->pipe_sblk->ucsc_csc_blk[0].version >> 16);
+		msm_property_install_blob(&psde->property_info, feature_name, 0,
+			PLANE_PROP_UCSC_CSC);
+	}
+
+	if (psde->features & BIT(SDE_SSPP_UCSC_UNMULT)) {
+		snprintf(feature_name, sizeof(feature_name), "%s%d",
+			"SDE_SSPP_UCSC_UNMULT_V",
+			psde->pipe_sblk->ucsc_unmult_blk[0].version >> 16);
+		msm_property_install_range(&psde->property_info, feature_name,
+			0x0, 0, 1, 0, PLANE_PROP_UCSC_UNMULT);
+	}
+
+	if (psde->features & BIT(SDE_SSPP_UCSC_ALPHA_DITHER)) {
+		snprintf(feature_name, sizeof(feature_name), "%s%d",
+			"SDE_SSPP_UCSC_ALPHA_DITHER_V",
+			psde->pipe_sblk->ucsc_alpha_dither_blk[0].version >> 16);
+		msm_property_install_range(&psde->property_info, feature_name,
+			0x0, 0, 1, 0, PLANE_PROP_UCSC_ALPHA_DITHER);
+	}
 }
 
 static void _sde_plane_setup_capabilities_blob(struct sde_plane *psde,

+ 8 - 1
msm/sde/sde_plane.h

@@ -43,11 +43,18 @@
 #define SDE_PLANE_DIRTY_FP16_GC 0x800
 #define SDE_PLANE_DIRTY_FP16_CSC 0x1000
 #define SDE_PLANE_DIRTY_FP16_UNMULT 0x2000
+#define SDE_PLANE_DIRTY_UCSC_IGC 0x4000
+#define SDE_PLANE_DIRTY_UCSC_GC 0x8000
+#define SDE_PLANE_DIRTY_UCSC_CSC 0x10000
+#define SDE_PLANE_DIRTY_UCSC_UNMULT 0x20000
+#define SDE_PLANE_DIRTY_UCSC_ALPHA_DITHER 0x40000
 #define SDE_PLANE_DIRTY_CP (SDE_PLANE_DIRTY_VIG_GAMUT |\
 		SDE_PLANE_DIRTY_VIG_IGC | SDE_PLANE_DIRTY_DMA_IGC |\
 		SDE_PLANE_DIRTY_DMA_GC | SDE_PLANE_DIRTY_FP16_IGC |\
 		SDE_PLANE_DIRTY_FP16_GC | SDE_PLANE_DIRTY_FP16_CSC |\
-		SDE_PLANE_DIRTY_FP16_UNMULT)
+		SDE_PLANE_DIRTY_FP16_UNMULT | SDE_PLANE_DIRTY_UCSC_IGC |\
+		SDE_PLANE_DIRTY_UCSC_GC | SDE_PLANE_DIRTY_UCSC_CSC |\
+		SDE_PLANE_DIRTY_UCSC_UNMULT | SDE_PLANE_DIRTY_UCSC_ALPHA_DITHER)
 #define SDE_PLANE_DIRTY_ALL	(0xFFFFFFFF & ~(SDE_PLANE_DIRTY_CP))
 
 struct sde_plane {