drm/msm/dp: configure quantization ranges as per video format

As per CEA-861-F, use Limited Range levels if a CE video format
pxl data is encoded using RGB color space. Use Full Range levels
for a IT video format.

Change-Id: I254a0e4b5c72d9dd783a539c10c339d50a409ab0
Signed-off-by: Narender Ankam <nankam@codeaurora.org>
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index a3985c6..0a8fc78 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -2960,7 +2960,7 @@
  *
  * Returns the HDMI Video ID (VIC) of the mode or 0 if it isn't one.
  */
-static u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match)
+u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match)
 {
 	u8 vic;
 
@@ -2982,6 +2982,7 @@
 	}
 	return 0;
 }
+EXPORT_SYMBOL(drm_match_hdmi_mode);
 
 static bool drm_valid_hdmi_vic(u8 vic)
 {
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 922df7e..3229987 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -59,6 +59,7 @@
 int dp_display_get_num_of_displays(void);
 int dp_display_get_displays(void **displays, int count);
 bool dp_connector_mode_needs_full_range(void *display);
+bool dp_connector_mode_is_cea_mode(void *display);
 enum sde_csc_type dp_connector_get_csc_type(struct drm_connector *conn,
 	void *data);
 #endif /* _DP_DISPLAY_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 1ffb0e2..442179f 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -134,6 +134,8 @@
 		!!(drm_mode->flags & DRM_MODE_FLAG_NHSYNC);
 
 	dp_mode->flags = drm_mode->flags;
+
+	dp_mode->timing.par = drm_mode->picture_aspect_ratio;
 }
 
 static void convert_to_drm_mode(const struct dp_display_mode *dp_mode,
@@ -174,6 +176,8 @@
 	drm_mode->flags = flags;
 	drm_mode->flags |= (dp_mode->flags & SDE_DRM_MODE_FLAG_FMT_MASK);
 
+	drm_mode->picture_aspect_ratio = dp_mode->timing.par;
+
 	drm_mode->type = 0x48;
 	drm_mode_set_name(drm_mode);
 }
@@ -474,6 +478,45 @@
 	return false;
 }
 
+bool dp_connector_mode_is_cea_mode(void *data)
+{
+	struct dp_display *display = data;
+	struct dp_bridge *bridge;
+	struct dp_display_mode *mode;
+	struct drm_display_mode drm_mode;
+	struct dp_panel_info *timing;
+	bool is_ce_mode = false;
+
+	if (!display) {
+		pr_err("invalid input\n");
+		return false;
+	}
+
+	bridge = display->bridge;
+	if (!bridge) {
+		pr_err("invalid bridge data\n");
+		return false;
+	}
+
+	mode = &bridge->dp_mode;
+	timing = &mode->timing;
+
+	if (timing->h_active == 640 &&
+	    timing->v_active == 480)
+		is_ce_mode = false;
+
+	convert_to_drm_mode(mode, &drm_mode);
+	drm_mode.flags &= ~SDE_DRM_MODE_FLAG_FMT_MASK;
+
+	if (drm_match_cea_mode(&drm_mode) || drm_match_hdmi_mode(&drm_mode))
+		is_ce_mode = true;
+
+	pr_debug("%s: %s : %s video format\n", __func__,
+			drm_mode.name, is_ce_mode ? "CE" : "IT");
+
+	return is_ce_mode;
+}
+
 enum sde_csc_type dp_connector_get_csc_type(struct drm_connector *conn,
 	void *data)
 {
@@ -500,10 +543,10 @@
 		return SDE_CSC_RGB2YUV_2020L;
 	else if (dp_connector_mode_needs_full_range(data)
 		|| conn->yuv_qs)
-		return SDE_CSC_RGB2YUV_601FR;
+		return SDE_CSC_RGB2YUV_709FR;
 
 error:
-	return SDE_CSC_RGB2YUV_601L;
+	return SDE_CSC_RGB2YUV_709L;
 }
 
 enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
index 57f63a8..1a87a96 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -47,6 +47,7 @@
 	u32 pixel_clk_khz;
 	u32 bpp;
 	u32 out_format;
+	enum hdmi_picture_aspect par;
 };
 
 struct dp_display_mode {
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index 32478a7..9ef04a8 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -768,6 +768,28 @@
 	return c_conn->ops.mode_needs_full_range(c_conn->display);
 }
 
+bool sde_connector_mode_is_cea_mode(struct drm_connector *connector)
+{
+	struct sde_connector *c_conn;
+
+	if (!connector) {
+		SDE_ERROR("invalid argument\n");
+		return false;
+	}
+
+	c_conn = to_sde_connector(connector);
+
+	if (!c_conn->display) {
+		SDE_ERROR("invalid argument\n");
+		return false;
+	}
+
+	if (!c_conn->ops.mode_is_cea_mode)
+		return false;
+
+	return c_conn->ops.mode_is_cea_mode(c_conn->display);
+}
+
 static void sde_connector_destroy(struct drm_connector *connector)
 {
 	struct sde_connector *c_conn;
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index 2543822..3fb4531 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -192,6 +192,14 @@
 	bool (*mode_needs_full_range)(void *display);
 
 	/**
+	 * mode_is_cea_mode - is this mode a CE video format
+	 * or IT video format.
+	 * @display: Pointer to private display structure
+	 * Returns: true or false based on CE or IT video format
+	 */
+	bool (*mode_is_cea_mode)(void *display);
+
+	/**
 	 * clk_ctrl - perform clk enable/disable on the connector
 	 * @handle: Pointer to clk handle
 	 * @type: Type of clks
@@ -728,6 +736,14 @@
 bool sde_connector_mode_needs_full_range(struct drm_connector *connector);
 
 /**
+ * sde_connector_mode_is_cea_mode - query if this mode is
+ * CE or IT video format
+ * @connector: Pointer to drm connector object
+ * Returns: true OR false based on CE or IT video format mode
+ */
+bool sde_connector_mode_is_cea_mode(struct drm_connector *connector);
+
+/**
  * sde_connector_get_csc_type - query csc type
  * to be used for the connector
  * @connector: Pointer to drm connector object
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 0956c01..3e625fe 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -234,6 +234,30 @@
 		{ 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
 		{ 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
 	},
+
+	[SDE_CSC_RGB2RGB_L] = {
+		{
+			TO_S15D16(0x01b7), TO_S15D16(0x0000), TO_S15D16(0x0000),
+			TO_S15D16(0x0000), TO_S15D16(0x01b7), TO_S15D16(0x0000),
+			TO_S15D16(0x0000), TO_S15D16(0x0000), TO_S15D16(0x01b7),
+		},
+		{ 0x0, 0x0, 0x0,},
+		{ 0x0040, 0x0040, 0x0040,},
+		{ 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
+		{ 0x40, 0x3ac, 0x40, 0x3ac, 0x40, 0x3ac,},
+	},
+
+	[SDE_CSC_RGB2RGB_FR] = {
+		{
+			TO_S15D16(0x0200), TO_S15D16(0x0000), TO_S15D16(0x0000),
+			TO_S15D16(0x0000), TO_S15D16(0x0200), TO_S15D16(0x0000),
+			TO_S15D16(0x0000), TO_S15D16(0x0000), TO_S15D16(0x0200),
+		},
+		{ 0x0, 0x0, 0x0,},
+		{ 0x0, 0x0, 0x0,},
+		{ 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
+		{ 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
+	}
 };
 
 /**
@@ -4198,8 +4222,10 @@
 			phys = sde_enc->phys_encs[i];
 			if (phys) {
 				mode = &phys->cached_mode;
-				mode_is_yuv = (mode->private_flags &
-					MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420);
+				mode_is_yuv = ((mode->private_flags &
+					MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420) ||
+					(mode->private_flags &
+					 MSM_MODE_FLAG_COLOR_FORMAT_YCBCR422));
 			}
 			/**
 			 * Check the CSC matrix type to which the
@@ -5269,7 +5295,8 @@
 	}
 	sde_enc = to_sde_encoder_virt(encoder);
 
-	if (!SDE_FORMAT_IS_YUV(format)) {
+	if ((output_type == CDM_CDWN_OUTPUT_WB) &&
+			!SDE_FORMAT_IS_YUV(format)) {
 		SDE_DEBUG_ENC(sde_enc, "[cdm_disable fmt:%x]\n",
 				format->base.pixel_format);
 
@@ -5329,13 +5356,21 @@
 	 */
 
 	if (output_type == CDM_CDWN_OUTPUT_HDMI) {
-		if (connector && connector->yuv_qs)
-			csc_type = SDE_CSC_RGB2YUV_709FR;
-		else if (connector &&
-			sde_connector_mode_needs_full_range(connector))
-			csc_type = SDE_CSC_RGB2YUV_709FR;
-		else
-			csc_type = SDE_CSC_RGB2YUV_709L;
+		if (SDE_FORMAT_IS_YUV(format)) {
+			if (connector && connector->yuv_qs)
+				csc_type = SDE_CSC_RGB2YUV_709FR;
+			else if (connector &&
+				sde_connector_mode_needs_full_range(connector))
+				csc_type = SDE_CSC_RGB2YUV_709FR;
+			else
+				csc_type = SDE_CSC_RGB2YUV_709L;
+		} else if (connector &&
+			sde_connector_mode_is_cea_mode(connector)) {
+			csc_type = SDE_CSC_RGB2RGB_L;
+		} else {
+			csc_type = SDE_CSC_RGB2RGB_FR;
+		}
+
 	} else if (output_type == CDM_CDWN_OUTPUT_WB) {
 		csc_type = SDE_CSC_RGB2YUV_601L;
 	}
@@ -5368,3 +5403,21 @@
 		}
 	}
 }
+
+void sde_encoder_phys_destroy_cdm(struct sde_encoder_phys *phys_enc)
+{
+	struct drm_encoder *encoder = phys_enc->parent;
+	struct sde_encoder_virt *sde_enc = NULL;
+	struct sde_hw_cdm *hw_cdm = phys_enc->hw_cdm;
+
+	if (!encoder) {
+		SDE_ERROR("invalid encoder\n");
+		return;
+	}
+	sde_enc = to_sde_encoder_virt(encoder);
+
+	SDE_DEBUG_ENC(sde_enc, "[cdm_disable]\n");
+
+	if (hw_cdm && hw_cdm->ops.disable)
+		hw_cdm->ops.disable(hw_cdm);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index b1547ab..d5e9f4a 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -498,6 +498,8 @@
 		const struct sde_format *format, u32 output_type,
 		struct sde_rect *roi);
 
+void sde_encoder_phys_destroy_cdm(struct sde_encoder_phys *phys_enc);
+
 /**
  * sde_encoder_helper_trigger_flush - control flush helper function
  *	This helper function may be optionally specified by physical
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index a1c3bfa..bd0868b 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -794,6 +794,8 @@
 		fmt = sde_get_sde_format(DRM_FORMAT_YUV420);
 	else if (mode.private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR422)
 		fmt = sde_get_sde_format(DRM_FORMAT_NV61);
+	else if (mode.private_flags & MSM_MODE_FLAG_COLOR_FORMAT_RGB444)
+		fmt = sde_get_sde_format(DRM_FORMAT_RGB888);
 
 	if (fmt) {
 		struct sde_rect hdmi_roi;
@@ -830,6 +832,7 @@
 	}
 
 	vid_enc = to_sde_encoder_phys_vid(phys_enc);
+	sde_encoder_phys_destroy_cdm(phys_enc);
 	SDE_DEBUG_VIDENC(vid_enc, "\n");
 	kfree(vid_enc);
 }
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
index a223e77..86661983 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -226,21 +226,29 @@
 	u32 opmode = 0;
 	u32 csc = 0;
 
-	if (!SDE_FORMAT_IS_YUV(fmt))
+	if ((cdm->output_type == CDM_CDWN_OUTPUT_WB) &&
+			!SDE_FORMAT_IS_YUV(fmt))
 		return -EINVAL;
 
 	if (cdm->output_type == CDM_CDWN_OUTPUT_HDMI) {
 		if (fmt->chroma_sample == SDE_CHROMA_H1V2)
 			return -EINVAL; /*unsupported format */
-		opmode = BIT(0);
-		opmode |= (fmt->chroma_sample << 1);
+		if (fmt->chroma_sample == SDE_CHROMA_RGB) {
+			opmode = 0;
+		} else {
+			opmode = BIT(0);
+			opmode |= (fmt->chroma_sample << 1);
+		}
 		cdm_cfg.intf_en = true;
 	} else {
 		opmode = 0;
 		cdm_cfg.wb_en = true;
 	}
 
-	csc |= BIT(2);
+	if (fmt->chroma_sample == SDE_CHROMA_RGB)
+		csc &= ~BIT(2);
+	else
+		csc |= BIT(2);
 	csc &= ~BIT(1);
 	csc |= BIT(0);
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index 322613d..db84ba4 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
@@ -400,6 +400,8 @@
 	SDE_CSC_RGB2YUV_709FR,
 	SDE_CSC_RGB2YUV_2020L,
 	SDE_CSC_RGB2YUV_2020FR,
+	SDE_CSC_RGB2RGB_L,
+	SDE_CSC_RGB2RGB_FR,
 	SDE_MAX_CSC
 };
 
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 977e3a5..df4eaec 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -1320,6 +1320,7 @@
 		.cont_splash_config = NULL,
 		.get_panel_vfp = NULL,
 		.mode_needs_full_range = dp_connector_mode_needs_full_range,
+		.mode_is_cea_mode = dp_connector_mode_is_cea_mode,
 		.get_csc_type = dp_connector_get_csc_type,
 	};
 	static const struct sde_connector_ops ext_bridge_ops = {
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index b5c134a..c363e3b 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -458,6 +458,7 @@
 int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);
 
 u8 drm_match_cea_mode(const struct drm_display_mode *to_match);
+u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match);
 enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code);
 bool drm_detect_hdmi_monitor(struct edid *edid);
 bool drm_detect_monitor_audio(struct edid *edid);