drm/msm: update CSC matrix during HDR playback

Update the CSC matrix used in the CDM block
to BT2020 format from the default value
during HDR video playback.

Add support in the SDE driver to enable switching
CSC matrix of CDM block to BT2020 during start of
HDR playback and restore it at the stop of the
playback to the default CSC.

Change-Id: Ic589380188ddef8ada2c8bbc0ca945bb1f319c85
Signed-off-by: Abhinav Kumar <abhinavk@codeaurora.org>
Signed-off-by: Ajay Singh Parmar <aparmar@codeaurora.org>
Signed-off-by: Chirag Khurana <ckhurana@codeaurora.org>
Signed-off-by: Narender Ankam <nankam@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 4da21d9..6b83368 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-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
@@ -51,4 +51,6 @@
 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);
+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 5b78218..fdd40a7 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-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
@@ -26,6 +26,11 @@
 
 #define to_dp_bridge(x)     container_of((x), struct dp_bridge, base)
 
+enum dp_connector_hdr_state {
+	HDR_DISABLE,
+	HDR_ENABLE
+};
+
 static void convert_to_dp_mode(const struct drm_display_mode *drm_mode,
 			struct dp_display_mode *dp_mode, struct dp_display *dp)
 {
@@ -384,6 +389,38 @@
 	return false;
 }
 
+enum sde_csc_type dp_connector_get_csc_type(struct drm_connector *conn,
+	void *data)
+{
+	struct dp_display *display = data;
+	struct sde_connector_state *c_state;
+	struct drm_msm_ext_hdr_metadata *hdr_meta;
+
+	if (!display || !conn) {
+		pr_err("invalid input\n");
+		goto error;
+	}
+
+	c_state = to_sde_connector_state(conn->state);
+
+	if (!c_state) {
+		pr_err("invalid input\n");
+		goto error;
+	}
+
+	hdr_meta = &c_state->hdr_meta;
+
+	if ((hdr_meta->hdr_state == HDR_ENABLE)
+		&& (hdr_meta->eotf != 0))
+		return SDE_CSC_RGB2YUV_2020L;
+	else if (dp_connector_mode_needs_full_range(data)
+		|| conn->yuv_qs)
+		return SDE_CSC_RGB2YUV_601FR;
+
+error:
+	return SDE_CSC_RGB2YUV_601L;
+}
+
 enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
 		bool force,
 		void *display)
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index 9fba20f..2d2f352 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
@@ -724,6 +724,28 @@
 	return rc;
 }
 
+enum sde_csc_type sde_connector_get_csc_type(struct drm_connector *conn)
+{
+	struct sde_connector *c_conn;
+
+	if (!conn) {
+		SDE_ERROR("invalid argument\n");
+		return -EINVAL;
+	}
+
+	c_conn = to_sde_connector(conn);
+
+	if (!c_conn->display) {
+		SDE_ERROR("invalid argument\n");
+		return -EINVAL;
+	}
+
+	if (!c_conn->ops.get_csc_type)
+		return SDE_CSC_RGB2YUV_601L;
+
+	return c_conn->ops.get_csc_type(conn, c_conn->display);
+}
+
 bool sde_connector_mode_needs_full_range(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 87c2ee2..2543822 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
@@ -198,6 +198,15 @@
 	 * @enable: State of clks
 	 */
 	int (*clk_ctrl)(void *handle, u32 type, u32 state);
+	/**
+	 * get_csc_type - returns the CSC type to be used
+	 * by the CDM block based on HDR state
+	 * @connector: Pointer to drm connector structure
+	 * @display: Pointer to private display structure
+	 * Returns: type of CSC matrix to be used
+	 */
+	enum sde_csc_type (*get_csc_type)(struct drm_connector *connector,
+		void *display);
 
 	/**
 	 * set_power - update dpms setting
@@ -719,6 +728,14 @@
 bool sde_connector_mode_needs_full_range(struct drm_connector *connector);
 
 /**
+ * sde_connector_get_csc_type - query csc type
+ * to be used for the connector
+ * @connector: Pointer to drm connector object
+ * Returns: csc type based on connector HDR state
+ */
+enum sde_csc_type sde_connector_get_csc_type(struct drm_connector *conn);
+
+/**
  * sde_connector_get_dither_cfg - get dither property data
  * @conn: Pointer to drm_connector struct
  * @state: Pointer to drm_connector_state struct
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 16a9791..66016fc 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -4094,10 +4094,15 @@
 	struct sde_encoder_phys *phys;
 	struct sde_kms *sde_kms = NULL;
 	struct msm_drm_private *priv = NULL;
+	struct drm_connector *conn_mas = NULL;
+	struct drm_display_mode *mode;
+	struct sde_hw_cdm *hw_cdm;
+	enum sde_csc_type conn_csc;
 	bool needs_hw_reset = false;
 	uint32_t ln_cnt1, ln_cnt2;
 	unsigned int i;
 	int rc, ret = 0;
+	int mode_is_yuv = 0;
 
 	if (!drm_enc || !params || !drm_enc->dev ||
 		!drm_enc->dev->dev_private) {
@@ -4167,13 +4172,48 @@
 	_sde_encoder_update_roi(drm_enc);
 
 	if (sde_enc->cur_master && sde_enc->cur_master->connector) {
-		rc = sde_connector_pre_kickoff(sde_enc->cur_master->connector);
+		conn_mas = sde_enc->cur_master->connector;
+		rc = sde_connector_pre_kickoff(conn_mas);
 		if (rc) {
-			SDE_ERROR_ENC(sde_enc, "kickoff conn%d failed rc %d\n",
-					sde_enc->cur_master->connector->base.id,
-					rc);
+			SDE_ERROR_ENC(sde_enc,
+				"kickoff conn%d failed rc %d\n",
+				conn_mas->base.id,
+				rc);
 			ret = rc;
 		}
+
+		for (i = 0; i < sde_enc->num_phys_encs; i++) {
+			phys = sde_enc->phys_encs[i];
+			if (phys) {
+				mode = &phys->cached_mode;
+				mode_is_yuv = (mode->private_flags &
+					MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420);
+			}
+			/**
+			 * Check the CSC matrix type to which the
+			 * CDM CSC matrix should be updated to based
+			 * on the connector HDR state
+			 */
+			conn_csc = sde_connector_get_csc_type(conn_mas);
+			if (phys && mode_is_yuv) {
+				if (phys->enc_cdm_csc != conn_csc) {
+					hw_cdm = phys->hw_cdm;
+					rc = hw_cdm->ops.setup_csc_data(hw_cdm,
+					&sde_csc_10bit_convert[conn_csc]);
+
+					if (rc)
+						SDE_ERROR_ENC(sde_enc,
+							"CSC setup failed rc %d\n",
+							rc);
+					SDE_DEBUG_ENC(sde_enc,
+						"updating CSC %d to %d\n",
+						phys->enc_cdm_csc,
+						conn_csc);
+					phys->enc_cdm_csc = conn_csc;
+
+				}
+			}
+		}
 	}
 
 	if (_sde_encoder_is_dsc_enabled(drm_enc) &&
@@ -5297,6 +5337,9 @@
 		}
 	}
 
+	/* Cache the CSC default matrix type */
+	phys_enc->enc_cdm_csc = csc_type;
+
 	if (hw_cdm && hw_cdm->ops.setup_cdwn) {
 		ret = hw_cdm->ops.setup_cdwn(hw_cdm, cdm_cfg);
 		if (ret < 0) {
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index 685d5b2..b1547ab 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -252,6 +252,7 @@
  * @intf_mode:		Interface mode
  * @intf_idx:		Interface index on sde hardware
  * @comp_type:      Type of compression supported
+ * @enc_cdm_csc:	Cached CSC type of CDM block
  * @enc_spinlock:	Virtual-Encoder-Wide Spin Lock for IRQ purposes
  * @enable_state:	Enable state tracking
  * @vblank_refcount:	Reference count of vblank request
@@ -291,6 +292,7 @@
 	enum sde_intf_mode intf_mode;
 	enum sde_intf intf_idx;
 	enum msm_display_compression_type comp_type;
+	enum sde_csc_type enc_cdm_csc;
 	spinlock_t *enc_spinlock;
 	enum sde_enc_enable_state enable_state;
 	struct mutex *vblank_ctl_lock;
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 68c3ca0..1b59ac7 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved.
  * Copyright (C) 2013 Red Hat
  * Author: Rob Clark <robdclark@gmail.com>
  *
@@ -1319,6 +1319,7 @@
 		.cont_splash_config = NULL,
 		.get_panel_vfp = NULL,
 		.mode_needs_full_range = dp_connector_mode_needs_full_range,
+		.get_csc_type = dp_connector_get_csc_type,
 	};
 	static const struct sde_connector_ops ext_bridge_ops = {
 		.set_info_blob = dsi_conn_set_info_blob,