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

disp: msm: dp: add colorspace property for MSM DP

Add the colorspace property for DP controller for MSM. Also, change
the default method to send the colorimetry information to the sink
from MISC bits of MSA to VSC SDP packets if the sink supports it. This
helps to avoid dynamic switches between the packet types for sending
the colorimetry information during BT2020 and DCI-P3 use-cases.

Change-Id: I7ddf879a187b023fcf7404d64028e4d19b031119
Signed-off-by: Abhinav Kumar <[email protected]>
Abhinav Kumar 6 жил өмнө
parent
commit
14e02e4b02

+ 149 - 57
msm/dp/dp_catalog.c

@@ -477,6 +477,29 @@ static u32 dp_catalog_ctrl_read_hdcp_status(struct dp_catalog_ctrl *ctrl)
 	return dp_read(catalog->exe_mode, io_data, DP_HDCP_STATUS);
 }
 
+static void dp_catalog_panel_sdp_update(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 sdp_cfg3_off = 0;
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		pr_err("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	if (panel->stream_id == DP_STREAM_1)
+		sdp_cfg3_off = MMSS_DP1_SDP_CFG3 - MMSS_DP_SDP_CFG3;
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	dp_write(catalog->exe_mode, io_data, MMSS_DP_SDP_CFG3 + sdp_cfg3_off,
+			 0x01);
+	dp_write(catalog->exe_mode, io_data, MMSS_DP_SDP_CFG3 + sdp_cfg3_off,
+			 0x00);
+}
+
 static void dp_catalog_panel_setup_vsif_infoframe_sdp(
 		struct dp_catalog_panel *panel)
 {
@@ -761,15 +784,125 @@ static void dp_catalog_panel_setup_vsc_sdp(struct dp_catalog_panel *panel)
 			DUMP_PREFIX_NONE, 16, 4, buf, off, false);
 }
 
+static void dp_catalog_panel_config_sdp(struct dp_catalog_panel *panel,
+	bool en)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 cfg, cfg2;
+	u32 sdp_cfg_off = 0;
+	u32 sdp_cfg2_off = 0;
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	if (panel->stream_id == DP_STREAM_1) {
+		sdp_cfg_off = MMSS_DP1_SDP_CFG - MMSS_DP_SDP_CFG;
+		sdp_cfg2_off = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
+	}
+
+	cfg = dp_read(catalog->exe_mode, io_data,
+				MMSS_DP_SDP_CFG + sdp_cfg_off);
+	cfg2 = dp_read(catalog->exe_mode, io_data,
+				MMSS_DP_SDP_CFG2 + sdp_cfg2_off);
+
+	if (en) {
+		/* GEN0_SDP_EN */
+		cfg |= BIT(17);
+		dp_write(catalog->exe_mode, io_data,
+				 MMSS_DP_SDP_CFG + sdp_cfg_off, cfg);
+
+		/* GENERIC0_SDPSIZE */
+		cfg2 |= BIT(16);
+		dp_write(catalog->exe_mode, io_data,
+				 MMSS_DP_SDP_CFG2 + sdp_cfg2_off, cfg2);
+
+		/* setup the GENERIC0 in case of en = true */
+		dp_catalog_panel_setup_vsc_sdp(panel);
+
+	} else {
+		/* GEN0_SDP_EN */
+		cfg &= ~BIT(17);
+		dp_write(catalog->exe_mode, io_data,
+				 MMSS_DP_SDP_CFG + sdp_cfg_off, cfg);
+
+		/* GENERIC0_SDPSIZE */
+		cfg2 &= ~BIT(16);
+		dp_write(catalog->exe_mode, io_data,
+				 MMSS_DP_SDP_CFG2 + sdp_cfg2_off, cfg2);
+	}
+
+	dp_catalog_panel_sdp_update(panel);
+}
+
+static void dp_catalog_panel_config_misc(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 reg_offset = 0;
+
+	if (!panel) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		pr_err("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	if (panel->stream_id == DP_STREAM_1)
+		reg_offset = DP1_MISC1_MISC0 - DP_MISC1_MISC0;
+
+	DP_DEBUG("misc settings = 0x%x\n", panel->misc_val);
+	dp_write(catalog->exe_mode, io_data, DP_MISC1_MISC0 + reg_offset,
+			panel->misc_val);
+}
+
+static int dp_catalog_panel_set_colorspace(struct dp_catalog_panel *panel,
+bool vsc_supported)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!panel) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		pr_err("invalid stream_id:%d\n", panel->stream_id);
+		return -EINVAL;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	if (vsc_supported) {
+		dp_catalog_panel_setup_vsc_sdp(panel);
+		dp_catalog_panel_sdp_update(panel);
+	} else
+		dp_catalog_panel_config_misc(panel);
+
+	return 0;
+}
+
 static void dp_catalog_panel_config_hdr(struct dp_catalog_panel *panel, bool en,
-		u32 dhdr_max_pkts)
+	u32 dhdr_max_pkts, bool flush)
 {
 	struct dp_catalog_private *catalog;
 	struct dp_io_data *io_data;
 	u32 cfg, cfg2, cfg4, misc;
 	u32 sdp_cfg_off = 0;
 	u32 sdp_cfg2_off = 0;
-	u32 sdp_cfg3_off = 0;
 	u32 sdp_cfg4_off = 0;
 	u32 misc1_misc0_off = 0;
 
@@ -789,7 +922,6 @@ static void dp_catalog_panel_config_hdr(struct dp_catalog_panel *panel, bool en,
 	if (panel->stream_id == DP_STREAM_1) {
 		sdp_cfg_off = MMSS_DP1_SDP_CFG - MMSS_DP_SDP_CFG;
 		sdp_cfg2_off = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
-		sdp_cfg3_off = MMSS_DP1_SDP_CFG3 - MMSS_DP_SDP_CFG3;
 		sdp_cfg4_off = MMSS_DP1_SDP_CFG4 - MMSS_DP_SDP_CFG4;
 		misc1_misc0_off = DP1_MISC1_MISC0 - DP_MISC1_MISC0;
 	}
@@ -812,34 +944,30 @@ static void dp_catalog_panel_config_hdr(struct dp_catalog_panel *panel, bool en,
 			dp_catalog_panel_setup_vsif_infoframe_sdp(panel);
 		}
 
-		/* GEN0_SDP_EN, GEN2_SDP_EN */
-		cfg |= BIT(17) | BIT(19);
+		/* GEN2_SDP_EN */
+		cfg |= BIT(19);
 		dp_write(catalog->exe_mode, io_data,
 				MMSS_DP_SDP_CFG + sdp_cfg_off, cfg);
 
-		/* GENERIC0_SDPSIZE GENERIC2_SDPSIZE */
-		cfg2 |= BIT(16) | BIT(20);
+		/* GENERIC2_SDPSIZE */
+		cfg2 |= BIT(20);
 		dp_write(catalog->exe_mode, io_data,
 				MMSS_DP_SDP_CFG2 + sdp_cfg2_off, cfg2);
 
-		dp_catalog_panel_setup_vsc_sdp(panel);
 		dp_catalog_panel_setup_hdr_infoframe_sdp(panel);
 
-		/* indicates presence of VSC (BIT(6) of MISC1) */
-		misc |= BIT(14);
-
 		if (panel->hdr_meta.eotf)
 			DP_DEBUG("Enabled\n");
 		else
 			DP_DEBUG("Reset\n");
 	} else {
-		/* VSCEXT_SDP_EN, GEN0_SDP_EN */
-		cfg &= ~BIT(16) & ~BIT(17) & ~BIT(19);
+		/* VSCEXT_SDP_ENG */
+		cfg &= ~BIT(16) & ~BIT(19);
 		dp_write(catalog->exe_mode, io_data,
 				MMSS_DP_SDP_CFG + sdp_cfg_off, cfg);
 
 		/* GENERIC0_SDPSIZE GENERIC2_SDPSIZE */
-		cfg2 &= ~BIT(16) & ~BIT(20);
+		cfg2 &= ~BIT(20);
 		dp_write(catalog->exe_mode, io_data,
 				MMSS_DP_SDP_CFG2 + sdp_cfg2_off, cfg2);
 
@@ -848,19 +976,13 @@ static void dp_catalog_panel_config_hdr(struct dp_catalog_panel *panel, bool en,
 		dp_write(catalog->exe_mode, io_data, MMSS_DP_SDP_CFG4
 				+ sdp_cfg4_off, cfg4);
 
-		/* switch back to MSA */
-		misc &= ~BIT(14);
-
 		DP_DEBUG("Disabled\n");
 	}
 
-	dp_write(catalog->exe_mode, io_data, DP_MISC1_MISC0 + misc1_misc0_off,
-			misc);
-
-	dp_write(catalog->exe_mode, io_data, MMSS_DP_SDP_CFG3 + sdp_cfg3_off,
-			0x01);
-	dp_write(catalog->exe_mode, io_data, MMSS_DP_SDP_CFG3 + sdp_cfg3_off,
-			0x00);
+	if (flush) {
+		DP_DEBUG("flushing HDR metadata\n");
+		dp_catalog_panel_sdp_update(panel);
+	}
 }
 
 static void dp_catalog_panel_update_transfer_unit(
@@ -1095,33 +1217,6 @@ static void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog_ctrl *ctrl,
 	}
 }
 
-static void dp_catalog_panel_config_misc(struct dp_catalog_panel *panel)
-{
-	struct dp_catalog_private *catalog;
-	struct dp_io_data *io_data;
-	u32 reg_offset = 0;
-
-	if (!panel) {
-		DP_ERR("invalid input\n");
-		return;
-	}
-
-	if (panel->stream_id >= DP_STREAM_MAX) {
-		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
-		return;
-	}
-
-	catalog = dp_catalog_get_priv(panel);
-	io_data = catalog->io.dp_link;
-
-	if (panel->stream_id == DP_STREAM_1)
-		reg_offset = DP1_MISC1_MISC0 - DP_MISC1_MISC0;
-
-	DP_DEBUG("misc settings = 0x%x\n", panel->misc_val);
-	dp_write(catalog->exe_mode, io_data, DP_MISC1_MISC0 + reg_offset,
-			panel->misc_val);
-}
-
 static void dp_catalog_panel_config_msa(struct dp_catalog_panel *panel,
 					u32 rate, u32 stream_rate_khz)
 {
@@ -2468,7 +2563,6 @@ static void dp_catalog_panel_config_spd(struct dp_catalog_panel *panel)
 	u32 offset = 0;
 	u32 sdp_cfg_off = 0;
 	u32 sdp_cfg2_off = 0;
-	u32 sdp_cfg3_off = 0;
 
 	/*
 	 * Source Device Information
@@ -2541,7 +2635,6 @@ static void dp_catalog_panel_config_spd(struct dp_catalog_panel *panel)
 	if (panel->stream_id == DP_STREAM_1) {
 		sdp_cfg_off = MMSS_DP1_SDP_CFG - MMSS_DP_SDP_CFG;
 		sdp_cfg2_off = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
-		sdp_cfg3_off = MMSS_DP1_SDP_CFG3 - MMSS_DP_SDP_CFG3;
 	}
 
 	spd_cfg = dp_read(catalog->exe_mode, io_data,
@@ -2558,10 +2651,7 @@ static void dp_catalog_panel_config_spd(struct dp_catalog_panel *panel)
 	dp_write(catalog->exe_mode, io_data, MMSS_DP_SDP_CFG2 + sdp_cfg2_off,
 			spd_cfg2);
 
-	dp_write(catalog->exe_mode, io_data, MMSS_DP_SDP_CFG3 + sdp_cfg3_off,
-				0x1);
-	dp_write(catalog->exe_mode, io_data, MMSS_DP_SDP_CFG3 + sdp_cfg3_off,
-				0x0);
+	dp_catalog_panel_sdp_update(panel);
 }
 
 static void dp_catalog_get_io_buf(struct dp_catalog_private *catalog)
@@ -2719,9 +2809,11 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser)
 	struct dp_catalog_panel panel = {
 		.timing_cfg = dp_catalog_panel_timing_cfg,
 		.config_hdr = dp_catalog_panel_config_hdr,
+		.config_sdp = dp_catalog_panel_config_sdp,
 		.tpg_config = dp_catalog_panel_tpg_cfg,
 		.config_spd = dp_catalog_panel_config_spd,
 		.config_misc = dp_catalog_panel_config_misc,
+		.set_colorspace = dp_catalog_panel_set_colorspace,
 		.config_msa = dp_catalog_panel_config_msa,
 		.update_transfer_unit = dp_catalog_panel_update_transfer_unit,
 		.config_ctrl = dp_catalog_panel_config_ctrl,

+ 4 - 1
msm/dp/dp_catalog.h

@@ -203,7 +203,10 @@ struct dp_catalog_panel {
 
 	int (*timing_cfg)(struct dp_catalog_panel *panel);
 	void (*config_hdr)(struct dp_catalog_panel *panel, bool en,
-			u32 dhdr_max_pkts);
+		u32 dhdr_max_pkts, bool flush);
+	void (*config_sdp)(struct dp_catalog_panel *panel, bool en);
+	int (*set_colorspace)(struct dp_catalog_panel *panel,
+		 bool vsc_supported);
 	void (*tpg_config)(struct dp_catalog_panel *panel, bool enable);
 	void (*config_spd)(struct dp_catalog_panel *panel);
 	void (*config_misc)(struct dp_catalog_panel *panel);

+ 35 - 2
msm/dp/dp_display.c

@@ -1627,7 +1627,7 @@ static void dp_display_stream_post_enable(struct dp_display_private *dp,
 			struct dp_panel *dp_panel)
 {
 	dp_panel->spd_config(dp_panel);
-	dp_panel->setup_hdr(dp_panel, NULL, false, 0);
+	dp_panel->setup_hdr(dp_panel, NULL, false, 0, true);
 }
 
 static int dp_display_post_enable(struct dp_display *dp_display, void *panel)
@@ -2092,8 +2092,10 @@ static int dp_display_config_hdr(struct dp_display *dp_display, void *panel,
 			struct drm_msm_ext_hdr_metadata *hdr, bool dhdr_update)
 {
 	struct dp_panel *dp_panel;
+	struct sde_connector *sde_conn;
 	struct dp_display_private *dp;
 	u64 core_clk_rate;
+	bool flush_hdr;
 
 	if (!dp_display || !panel) {
 		DP_ERR("invalid input\n");
@@ -2102,6 +2104,7 @@ static int dp_display_config_hdr(struct dp_display *dp_display, void *panel,
 
 	dp_panel = panel;
 	dp = container_of(dp_display, struct dp_display_private, dp_display);
+	sde_conn =  to_sde_connector(dp_panel->connector);
 
 	core_clk_rate = dp->power->clk_get_rate(dp->power, "core_clk");
 	if (!core_clk_rate) {
@@ -2109,7 +2112,36 @@ static int dp_display_config_hdr(struct dp_display *dp_display, void *panel,
 		return -EINVAL;
 	}
 
-	return dp_panel->setup_hdr(dp_panel, hdr, dhdr_update, core_clk_rate);
+	/*
+	 * In rare cases where HDR metadata is updated independently
+	 * flush the HDR metadata immediately instead of relying on
+	 * the colorspace
+	 */
+	flush_hdr = !sde_conn->colorspace_updated;
+
+	if (flush_hdr)
+		DP_DEBUG("flushing the HDR metadata\n");
+	else
+		DP_DEBUG("piggy-backing with colorspace\n");
+
+	return dp_panel->setup_hdr(dp_panel, hdr, dhdr_update,
+		core_clk_rate, flush_hdr);
+}
+
+static int dp_display_setup_colospace(struct dp_display *dp_display,
+		void *panel,
+		u32 colorspace)
+{
+	struct dp_panel *dp_panel;
+
+	if (!dp_display || !panel) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp_panel = panel;
+
+	return dp_panel->set_colorspace(dp_panel, colorspace);
 }
 
 static int dp_display_create_workqueue(struct dp_display_private *dp)
@@ -2633,6 +2665,7 @@ static int dp_display_probe(struct platform_device *pdev)
 					dp_display_mst_get_fixed_topology_port;
 	g_dp_display->wakeup_phy_layer =
 					dp_display_wakeup_phy_layer;
+	g_dp_display->set_colorspace = dp_display_setup_colospace;
 
 	rc = component_add(&pdev->dev, &dp_display_comp_ops);
 	if (rc) {

+ 2 - 0
msm/dp/dp_display.h

@@ -93,6 +93,8 @@ struct dp_display {
 	int (*config_hdr)(struct dp_display *dp_display, void *panel,
 				struct drm_msm_ext_hdr_metadata *hdr_meta,
 				bool dhdr_update);
+	int (*set_colorspace)(struct dp_display *dp_display, void *panel,
+				u32 colorspace);
 	int (*post_init)(struct dp_display *dp_display);
 	int (*mst_install)(struct dp_display *dp_display,
 			struct dp_mst_drm_install_info *mst_install_info);

+ 45 - 0
msm/dp/dp_drm.c

@@ -326,6 +326,25 @@ int dp_connector_config_hdr(struct drm_connector *connector, void *display,
 			c_state->dyn_hdr_meta.dynamic_hdr_update);
 }
 
+int dp_connector_set_colorspace(struct drm_connector *connector,
+	void *display)
+{
+	struct dp_display *dp_display = display;
+	struct sde_connector *sde_conn;
+
+	if (!dp_display || !connector)
+		return -EINVAL;
+
+	sde_conn = to_sde_connector(connector);
+	if (!sde_conn->drv_panel) {
+		pr_err("invalid dp panel\n");
+		return -EINVAL;
+	}
+
+	return dp_display->set_colorspace(dp_display,
+		sde_conn->drv_panel, connector->state->colorspace);
+}
+
 int dp_connector_post_init(struct drm_connector *connector, void *display)
 {
 	int rc;
@@ -469,6 +488,32 @@ void dp_connector_post_open(struct drm_connector *connector, void *display)
 		dp->post_open(dp);
 }
 
+int dp_connector_atomic_check(struct drm_connector *connector,
+	void *display,
+	struct drm_connector_state *c_state)
+{
+	struct sde_connector *sde_conn;
+	struct drm_connector_state *old_state =
+		drm_atomic_get_old_connector_state(c_state->state, connector);
+
+	if (!connector || !display)
+		return -EINVAL;
+
+	sde_conn = to_sde_connector(connector);
+
+	/*
+	 * Marking the colorspace has been changed
+	 * the flag shall be checked in the pre_kickoff
+	 * to configure the new colorspace in HW
+	 */
+	if (c_state->colorspace != old_state->colorspace) {
+		DP_DEBUG("colorspace has been updated\n");
+		sde_conn->colorspace_updated = true;
+	}
+
+	return 0;
+}
+
 int dp_connector_get_modes(struct drm_connector *connector,
 		void *display, const struct msm_resource_caps_info *avail_res)
 {

+ 21 - 0
msm/dp/dp_drm.h

@@ -37,6 +37,27 @@ int dp_connector_config_hdr(struct drm_connector *connector,
 		void *display,
 		struct sde_connector_state *c_state);
 
+/**
+ * dp_connector_atomic_check - callback to perform atomic
+ * check for DP
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display handle
+ * @c_state: connect state data
+ * Returns: Zero on success
+ */
+int dp_connector_atomic_check(struct drm_connector *connector,
+	void *display,
+	struct drm_connector_state *c_state);
+
+/**
+ * dp_connector_set_colorspace - callback to set new colorspace
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display handle
+ * Returns: Zero on success
+ */
+int dp_connector_set_colorspace(struct drm_connector *connector,
+	void *display);
+
 /**
  * dp_connector_post_init - callback to perform additional initialization steps
  * @connector: Pointer to drm connector structure

+ 1 - 1
msm/dp/dp_link.c

@@ -1352,7 +1352,7 @@ static int dp_link_get_colorimetry_config(struct dp_link *dp_link)
 	/* Only RGB_VESA nd RGB_CEA supported for now */
 	switch (dr) {
 	case DP_DYNAMIC_RANGE_RGB_CEA:
-		cc = BIT(3);
+		cc = BIT(2);
 		break;
 	case DP_DYNAMIC_RANGE_RGB_VESA:
 	default:

+ 191 - 9
msm/dp/dp_panel.c

@@ -189,6 +189,98 @@ struct tu_algo_data {
 	s64 ratio;
 };
 
+/**
+ * Mapper function which outputs colorimetry and dynamic range
+ * to be used for a given colorspace value when the vsc sdp
+ * packets are used to change the colorimetry.
+ */
+static void get_sdp_colorimetry_range(struct dp_panel_private *panel,
+	u32 colorspace, u32 *colorimetry, u32 *dynamic_range)
+{
+
+	u32 cc;
+
+	/*
+	 * Some rules being used for assignment of dynamic
+	 * range for colorimetry using SDP:
+	 *
+	 * 1) If compliance test is ongoing return sRGB with
+	 *    CEA primaries
+	 * 2) For BT2020 cases, dynamic range shall be CEA
+	 * 3) For DCI-P3 cases, as per HW team dynamic range
+	 *    shall be VESA for RGB and CEA for YUV content
+	 *    Hence defaulting to RGB and picking VESA
+	 * 4) Default shall be sRGB with VESA
+	 */
+
+	cc = panel->link->get_colorimetry_config(panel->link);
+
+	if (cc) {
+		*colorimetry = sRGB;
+		*dynamic_range = CEA;
+		return;
+	}
+
+	switch (colorspace) {
+	case DRM_MODE_COLORIMETRY_BT2020_RGB:
+		*colorimetry = ITU_R_BT_2020_RGB;
+		*dynamic_range = CEA;
+		break;
+	case DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65:
+	case DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER:
+		*colorimetry = DCI_P3;
+		*dynamic_range = VESA;
+		break;
+	default:
+		*colorimetry = sRGB;
+		*dynamic_range = VESA;
+	}
+}
+
+/**
+ * Mapper function which outputs colorimetry to be used for a
+ * given colorspace value when misc field of MSA is used to
+ * change the colorimetry. Currently only RGB formats have been
+ * added. This API will be extended to YUV once its supported on DP.
+ */
+static u8 get_misc_colorimetry_val(struct dp_panel_private *panel,
+	u32 colorspace)
+{
+	u8 colorimetry;
+	u32 cc;
+
+	cc = panel->link->get_colorimetry_config(panel->link);
+	/*
+	 * If there is a non-zero value then compliance test-case
+	 * is going on, otherwise we can honor the colorspace setting
+	 */
+	if (cc)
+		return cc;
+
+	switch (colorspace) {
+	case DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65:
+	case DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER:
+		colorimetry = 0x7;
+		break;
+	case DRM_MODE_DP_COLORIMETRY_SRGB:
+		colorimetry = 0x4;
+		break;
+	case DRM_MODE_DP_COLORIMETRY_RGB_WIDE_GAMUT:
+		colorimetry = 0x3;
+		break;
+	case DRM_MODE_DP_COLORIMETRY_SCRGB:
+		colorimetry = 0xb;
+		break;
+	case DRM_MODE_COLORIMETRY_OPRGB:
+		colorimetry = 0xc;
+		break;
+	default:
+		colorimetry = 0;
+	}
+
+	return colorimetry;
+}
+
 static int _tu_param_compare(s64 a, s64 b)
 {
 	u32 a_int, a_frac, a_sign;
@@ -2482,6 +2574,7 @@ static int dp_panel_deinit_panel_info(struct dp_panel *dp_panel, u32 flags)
 	struct dp_panel_private *panel;
 	struct drm_msm_ext_hdr_metadata *hdr_meta;
 	struct dp_sdp_header *dhdr_vsif_sdp;
+	struct sde_connector *sde_conn;
 	struct dp_sdp_header *shdr_if_sdp;
 	struct dp_catalog_vsc_sdp_colorimetry *vsc_colorimetry;
 	struct drm_connector *connector;
@@ -2517,6 +2610,7 @@ static int dp_panel_deinit_panel_info(struct dp_panel *dp_panel, u32 flags)
 	panel->panel_on = false;
 
 	connector = dp_panel->connector;
+	sde_conn = to_sde_connector(connector);
 	c_state = to_sde_connector_state(connector->state);
 
 	connector->hdr_eotf = 0;
@@ -2527,6 +2621,8 @@ static int dp_panel_deinit_panel_info(struct dp_panel *dp_panel, u32 flags)
 	connector->hdr_supported = false;
 	connector->hdr_plus_app_ver = 0;
 
+	sde_conn->colorspace_updated = false;
+
 	memset(&c_state->hdr_meta, 0, sizeof(c_state->hdr_meta));
 	memset(&c_state->dyn_hdr_meta, 0, sizeof(c_state->dyn_hdr_meta));
 
@@ -2657,11 +2753,14 @@ static u32 dp_panel_calc_dhdr_pkt_limit(struct dp_panel *dp_panel,
 	return calc_pkt_limit;
 }
 
-static void dp_panel_setup_colorimetry_sdp(struct dp_panel *dp_panel)
+static void dp_panel_setup_colorimetry_sdp(struct dp_panel *dp_panel,
+	u32 cspace)
 {
 	struct dp_panel_private *panel;
 	struct dp_catalog_vsc_sdp_colorimetry *hdr_colorimetry;
 	u8 bpc;
+	u32 colorimetry = 0;
+	u32 dynamic_range = 0;
 
 	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
 	hdr_colorimetry = &panel->catalog->vsc_colorimetry;
@@ -2671,11 +2770,14 @@ static void dp_panel_setup_colorimetry_sdp(struct dp_panel *dp_panel)
 	hdr_colorimetry->header.HB2 = 0x05;
 	hdr_colorimetry->header.HB3 = 0x13;
 
+	get_sdp_colorimetry_range(panel, cspace, &colorimetry,
+		&dynamic_range);
+
 	/* VSC SDP Payload for DB16 */
-	hdr_colorimetry->data[16] = (RGB << 4) | ITU_R_BT_2020_RGB;
+	hdr_colorimetry->data[16] = (RGB << 4) | colorimetry;
 
 	/* VSC SDP Payload for DB17 */
-	hdr_colorimetry->data[17] = (CEA << 7);
+	hdr_colorimetry->data[17] = (dynamic_range << 7);
 	bpc = (dp_panel->pinfo.bpp / 3);
 
 	switch (bpc) {
@@ -2719,9 +2821,60 @@ static void dp_panel_setup_dhdr_vsif(struct dp_panel_private *panel)
 	dhdr_vsif->HB3 = 0x13 << 2;
 }
 
+static void dp_panel_setup_misc_colorimetry(struct dp_panel *dp_panel,
+	u32 colorspace)
+{
+	struct dp_panel_private *panel;
+	struct dp_catalog_panel *catalog;
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+	catalog = panel->catalog;
+
+	catalog->misc_val &= ~0x1e;
+
+	catalog->misc_val |= (get_misc_colorimetry_val(panel,
+		colorspace) << 1);
+}
+
+static int dp_panel_set_colorspace(struct dp_panel *dp_panel,
+	u32 colorspace)
+{
+	int rc = 0;
+	struct dp_panel_private *panel;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+	if (panel->vsc_supported)
+		dp_panel_setup_colorimetry_sdp(dp_panel,
+			colorspace);
+	else
+		dp_panel_setup_misc_colorimetry(dp_panel,
+			colorspace);
+
+	/*
+	 * During the first frame update panel_on will be false and
+	 * the colorspace will be cached in the connector's state which
+	 * shall be used in the dp_panel_hw_cfg
+	 */
+	if (panel->panel_on) {
+		DP_DEBUG("panel is ON programming colorspace\n");
+		rc =  panel->catalog->set_colorspace(panel->catalog,
+			  panel->vsc_supported);
+	}
+
+end:
+	return rc;
+}
+
 static int dp_panel_setup_hdr(struct dp_panel *dp_panel,
 		struct drm_msm_ext_hdr_metadata *hdr_meta,
-		bool dhdr_update, u64 core_clk_rate)
+		bool dhdr_update, u64 core_clk_rate, bool flush)
 {
 	int rc = 0, max_pkts = 0;
 	struct dp_panel_private *panel;
@@ -2748,8 +2901,6 @@ static int dp_panel_setup_hdr(struct dp_panel *dp_panel,
 
 	panel->hdr_state = hdr_meta->hdr_state;
 
-	dp_panel_setup_colorimetry_sdp(dp_panel);
-
 	dp_panel_setup_hdr_if(panel);
 
 	if (panel->hdr_state) {
@@ -2777,7 +2928,7 @@ cached:
 	if (panel->panel_on) {
 		panel->catalog->stream_id = dp_panel->stream_id;
 		panel->catalog->config_hdr(panel->catalog, panel->hdr_state,
-				max_pkts);
+			max_pkts, flush);
 		if (dhdr_update)
 			panel->catalog->dhdr_flush(panel->catalog);
 	}
@@ -2856,19 +3007,29 @@ static void dp_panel_config_misc(struct dp_panel *dp_panel)
 {
 	struct dp_panel_private *panel;
 	struct dp_catalog_panel *catalog;
+	struct drm_connector *connector;
 	u32 misc_val;
-	u32 tb, cc;
+	u32 tb, cc, colorspace;
 
 	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
 	catalog = panel->catalog;
+	connector = dp_panel->connector;
+	cc = 0;
 
 	tb = panel->link->get_test_bits_depth(panel->link, dp_panel->pinfo.bpp);
-	cc = panel->link->get_colorimetry_config(panel->link);
+	colorspace = connector->state->colorspace;
+
+
+	cc = (get_misc_colorimetry_val(panel, colorspace) << 1);
 
 	misc_val = cc;
 	misc_val |= (tb << 5);
 	misc_val |= BIT(0); /* Configure clock to synchronous mode */
 
+	/* if VSC is supported then set bit 6 of MISC1 */
+	if (panel->vsc_supported)
+		misc_val |= BIT(14);
+
 	catalog->misc_val = misc_val;
 	catalog->config_misc(catalog);
 }
@@ -2910,9 +3071,21 @@ static void dp_panel_resolution_info(struct dp_panel_private *panel)
 		panel->link->link_params.lane_count);
 }
 
+static void dp_panel_config_sdp(struct dp_panel *dp_panel,
+	bool en)
+{
+	struct dp_panel_private *panel;
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+	panel->catalog->stream_id = dp_panel->stream_id;
+
+	panel->catalog->config_sdp(panel->catalog, en);
+}
+
 static int dp_panel_hw_cfg(struct dp_panel *dp_panel, bool enable)
 {
 	struct dp_panel_private *panel;
+	struct drm_connector *connector;
 
 	if (!dp_panel) {
 		DP_ERR("invalid input\n");
@@ -2926,15 +3099,23 @@ static int dp_panel_hw_cfg(struct dp_panel *dp_panel, bool enable)
 
 	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
 	panel->catalog->stream_id = dp_panel->stream_id;
+	connector = dp_panel->connector;
 
 	if (enable) {
 		dp_panel_config_ctrl(dp_panel);
 		dp_panel_config_misc(dp_panel);
 		dp_panel_config_msa(dp_panel);
+		if (panel->vsc_supported) {
+			dp_panel_setup_colorimetry_sdp(dp_panel,
+				connector->state->colorspace);
+			dp_panel_config_sdp(dp_panel, true);
+		}
 		dp_panel_config_dsc(dp_panel, enable);
 		dp_panel_config_tr_unit(dp_panel);
 		dp_panel_config_timing(dp_panel);
 		dp_panel_resolution_info(panel);
+	} else {
+		dp_panel_config_sdp(dp_panel, false);
 	}
 
 	panel->catalog->config_dto(panel->catalog, !enable);
@@ -3148,6 +3329,7 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in)
 	dp_panel->tpg_config = dp_panel_tpg_config;
 	dp_panel->spd_config = dp_panel_spd_config;
 	dp_panel->setup_hdr = dp_panel_setup_hdr;
+	dp_panel->set_colorspace = dp_panel_set_colorspace;
 	dp_panel->hdr_supported = dp_panel_hdr_supported;
 	dp_panel->set_stream_info = dp_panel_set_stream_info;
 	dp_panel->read_sink_status = dp_panel_read_sink_sts;

+ 3 - 1
msm/dp/dp_panel.h

@@ -148,7 +148,9 @@ struct dp_panel {
 	int (*set_dpcd)(struct dp_panel *dp_panel, u8 *dpcd);
 	int (*setup_hdr)(struct dp_panel *dp_panel,
 		struct drm_msm_ext_hdr_metadata *hdr_meta,
-		bool dhdr_update, u64 core_clk_rate);
+			bool dhdr_update, u64 core_clk_rate, bool flush);
+	int (*set_colorspace)(struct dp_panel *dp_panel,
+		u32 colorspace);
 	void (*tpg_config)(struct dp_panel *dp_panel, bool enable);
 	int (*spd_config)(struct dp_panel *dp_panel);
 	bool (*hdr_supported)(struct dp_panel *dp_panel);

+ 28 - 0
msm/sde/sde_connector.c

@@ -606,6 +606,19 @@ static int _sde_connector_update_bl_scale(struct sde_connector *c_conn)
 	return rc;
 }
 
+void sde_connector_set_colorspace(struct sde_connector *c_conn)
+{
+	int rc = 0;
+
+	if (c_conn->ops.set_colorspace)
+		rc = c_conn->ops.set_colorspace(&c_conn->base,
+			c_conn->display);
+
+	if (rc)
+		SDE_ERROR_CONN(c_conn, "cannot apply new colorspace %d\n", rc);
+
+}
+
 void sde_connector_set_qsync_params(struct drm_connector *connector)
 {
 	struct sde_connector *c_conn = to_sde_connector(connector);
@@ -680,6 +693,12 @@ static int _sde_connector_update_dirty_properties(
 		}
 	}
 
+	/* if colorspace needs to be updated do it first */
+	if (c_conn->colorspace_updated) {
+		c_conn->colorspace_updated = false;
+		sde_connector_set_colorspace(c_conn);
+	}
+
 	/*
 	 * Special handling for postproc properties and
 	 * for updating backlight if any unset backlight level is present
@@ -2134,6 +2153,7 @@ static const struct drm_connector_helper_funcs sde_connector_helper_ops = {
 	.get_modes =    sde_connector_get_modes,
 	.mode_valid =   sde_connector_mode_valid,
 	.best_encoder = sde_connector_best_encoder,
+	.atomic_check = sde_connector_atomic_check,
 };
 
 static const struct drm_connector_helper_funcs sde_connector_helper_ops_v2 = {
@@ -2311,6 +2331,7 @@ static int _sde_connector_install_properties(struct drm_device *dev,
 {
 	struct dsi_display *dsi_display;
 	int rc;
+	struct drm_connector *connector;
 
 	msm_property_install_blob(&c_conn->property_info, "capabilities",
 			DRM_MODE_PROP_IMMUTABLE, CONNECTOR_PROP_SDE_INFO);
@@ -2323,6 +2344,8 @@ static int _sde_connector_install_properties(struct drm_device *dev,
 		return rc;
 	}
 
+	connector = &c_conn->base;
+
 	msm_property_install_blob(&c_conn->property_info, "mode_properties",
 			DRM_MODE_PROP_IMMUTABLE, CONNECTOR_PROP_MODE_INFO);
 
@@ -2366,6 +2389,11 @@ static int _sde_connector_install_properties(struct drm_device *dev,
 			      &hdr,
 			      sizeof(hdr),
 			      CONNECTOR_PROP_EXT_HDR_INFO);
+
+		/* create and attach colorspace property for DP */
+		if (!drm_mode_create_colorspace_property(connector))
+			drm_object_attach_property(&connector->base,
+				connector->colorspace_property, 0);
 	}
 
 	msm_property_install_volatile_range(&c_conn->property_info,

+ 11 - 0
msm/sde/sde_connector.h

@@ -168,6 +168,14 @@ struct sde_connector_ops {
 	int (*set_backlight)(struct drm_connector *connector,
 			void *display, u32 bl_lvl);
 
+	/**
+	 * set_colorspace - set colorspace for connector
+	 * @connector: Pointer to drm connector structure
+	 * @display: Pointer to private display structure
+	 */
+	int (*set_colorspace)(struct drm_connector *connector,
+			void *display);
+
 	/**
 	 * soft_reset - perform a soft reset on the connector
 	 * @display: Pointer to private display structure
@@ -400,6 +408,7 @@ struct sde_connector_dyn_hdr_metadata {
  * @allow_bl_update: Flag to indicate if BL update is allowed currently or not
  * @qsync_mode: Cached Qsync mode, 0=disabled, 1=continuous mode
  * @qsync_updated: Qsync settings were updated
+ * @colorspace_updated: Colorspace property was updated
  * last_cmd_tx_sts: status of the last command transfer
  * @hdr_capable: external hdr support present
  * @core_clk_rate: MDP core clk rate used for dynamic HDR packet calculation
@@ -452,6 +461,8 @@ struct sde_connector {
 	u32 qsync_mode;
 	bool qsync_updated;
 
+	bool colorspace_updated;
+
 	bool last_cmd_tx_sts;
 	bool hdr_capable;
 };

+ 2 - 0
msm/sde/sde_kms.c

@@ -1222,11 +1222,13 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
 		.post_init  = dp_connector_post_init,
 		.detect     = dp_connector_detect,
 		.get_modes  = dp_connector_get_modes,
+		.atomic_check = dp_connector_atomic_check,
 		.mode_valid = dp_connector_mode_valid,
 		.get_info   = dp_connector_get_info,
 		.get_mode_info  = dp_connector_get_mode_info,
 		.post_open  = dp_connector_post_open,
 		.check_status = NULL,
+		.set_colorspace = dp_connector_set_colorspace,
 		.config_hdr = dp_connector_config_hdr,
 		.cmd_transfer = NULL,
 		.cont_splash_config = NULL,