From 1a8c7e6db6898ea62820bdd4fc9ef70b04ea528a Mon Sep 17 00:00:00 2001
From: Laurentiu Palcu <laurentiu.palcu@nxp.com>
Date: Thu, 7 Nov 2019 15:23:41 +0200
Subject: [PATCH] drm/imx/hdp: handle the various deep-color settings

iMX8MQ has the ability to handle color depths up to 12bpc. This patch adds
support for higher color depths for various modes.

Signed-off-by: Laurentiu Palcu <laurentiu.palcu@nxp.com>
---
 drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c | 132 ++++++++++++++----------
 1 file changed, 75 insertions(+), 57 deletions(-)

--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c
+++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c
@@ -67,58 +67,20 @@ static void hdmi_lanes_config(struct cdn
 	cdns_mhdp_reg_write(mhdp, LANES_CONFIG, 0x00400000 | mhdp->lane_mapping);
 }
 
-#define RGB_ALLOWED_COLORIMETRY (BIT(HDMI_EXTENDED_COLORIMETRY_BT2020) |\
-				 BIT(HDMI_EXTENDED_COLORIMETRY_OPRGB))
-#define YCC_ALLOWED_COLORIMETRY (BIT(HDMI_EXTENDED_COLORIMETRY_BT2020) |\
-				 BIT(HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM) |\
-				 BIT(HDMI_EXTENDED_COLORIMETRY_OPYCC_601) |\
-				 BIT(HDMI_EXTENDED_COLORIMETRY_S_YCC_601) |\
-				 BIT(HDMI_EXTENDED_COLORIMETRY_XV_YCC_709) |\
-				 BIT(HDMI_EXTENDED_COLORIMETRY_XV_YCC_601))
 static int hdmi_avi_info_set(struct cdns_mhdp_device *mhdp,
-				struct drm_display_mode *mode)
+			     struct drm_display_mode *mode)
 {
 	struct hdmi_avi_infoframe frame;
-#if 0
-	struct drm_display_info *di = &mhdp->connector.base.display_info;
-	enum hdmi_extended_colorimetry ext_col;
-	u32 sink_col, allowed_col;
-#endif
 	int format = mhdp->video_info.color_fmt;
+	struct drm_connector_state *conn_state = mhdp->connector.base.state;
+	struct drm_display_mode *adj_mode;
+	enum hdmi_quantization_range qr;
 	u8 buf[32];
 	int ret;
 
 	/* Initialise info frame from DRM mode */
-	drm_hdmi_avi_infoframe_from_display_mode(&frame, &mhdp->connector.base, mode);
-
-#if 0 //TODO to DCSS
-	/* Set up colorimetry */
-	allowed_col = format == PXL_RGB ? RGB_ALLOWED_COLORIMETRY :
-						  YCC_ALLOWED_COLORIMETRY;
-
-	sink_col = di->hdmi.colorimetry & allowed_col;
-
-	if (sink_col & BIT(HDMI_EXTENDED_COLORIMETRY_BT2020))
-		ext_col = HDMI_EXTENDED_COLORIMETRY_BT2020;
-	else if (sink_col & BIT(HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM))
-		ext_col = HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM;
-	else if (sink_col & BIT(HDMI_EXTENDED_COLORIMETRY_OPRGB))
-		ext_col = HDMI_EXTENDED_COLORIMETRY_OPRGB;
-	else if (sink_col & BIT(HDMI_EXTENDED_COLORIMETRY_XV_YCC_709))
-		ext_col = HDMI_EXTENDED_COLORIMETRY_XV_YCC_709;
-	else if (sink_col & BIT(HDMI_EXTENDED_COLORIMETRY_OPYCC_601))
-		ext_col = HDMI_EXTENDED_COLORIMETRY_OPYCC_601;
-	else if (sink_col & BIT(HDMI_EXTENDED_COLORIMETRY_S_YCC_601))
-		ext_col = HDMI_EXTENDED_COLORIMETRY_S_YCC_601;
-	else if (sink_col & BIT(HDMI_EXTENDED_COLORIMETRY_XV_YCC_601))
-		ext_col = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
-	else
-		ext_col = 0;
-
-	frame.colorimetry = sink_col ? HDMI_COLORIMETRY_EXTENDED :
-					  HDMI_COLORIMETRY_NONE;
-	frame.extended_colorimetry = ext_col;
-#endif
+	drm_hdmi_avi_infoframe_from_display_mode(&frame, &mhdp->connector.base,
+						 mode);
 
 	switch (format) {
 	case YCBCR_4_4_4:
@@ -135,6 +97,19 @@ static int hdmi_avi_info_set(struct cdns
 		break;
 	}
 
+	drm_hdmi_avi_infoframe_colorspace(&frame, conn_state);
+
+	adj_mode = &mhdp->bridge.base.encoder->crtc->state->adjusted_mode;
+
+	qr = drm_default_rgb_quant_range(adj_mode);
+
+	drm_hdmi_avi_infoframe_quant_range(&frame, &mhdp->connector.base,
+					   adj_mode, qr);
+
+	ret = hdmi_avi_infoframe_check(&frame);
+	if (WARN_ON(ret))
+		return false;
+
 	ret = hdmi_avi_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1);
 	if (ret < 0) {
 		DRM_ERROR("failed to pack AVI infoframe: %d\n", ret);
@@ -404,19 +379,6 @@ static void cdns_hdmi_bridge_mode_set(st
 	struct drm_display_info *display_info = &mhdp->connector.base.display_info;
 	struct video_info *video = &mhdp->video_info;
 
-	switch (display_info->bpc) {
-	case 10:
-		video->color_depth = 10;
-		break;
-	case 6:
-		video->color_depth = 6;
-		break;
-	default:
-		video->color_depth = 8;
-		break;
-	}
-
-	video->color_fmt = PXL_RGB;
 	video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
 	video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
 
@@ -428,10 +390,66 @@ static void cdns_hdmi_bridge_mode_set(st
 	mutex_unlock(&mhdp->lock);
 }
 
+bool cdns_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
+				 const struct drm_display_mode *mode,
+				 struct drm_display_mode *adjusted_mode)
+{
+	struct cdns_mhdp_device *mhdp = bridge->driver_private;
+	struct drm_display_info *di = &mhdp->connector.base.display_info;
+	struct video_info *video = &mhdp->video_info;
+	int vic = drm_match_cea_mode(mode);
+
+	video->color_depth = 8;
+	video->color_fmt = PXL_RGB;
+
+	/* for all other platforms, other than imx8mq */
+	if (strncmp("imx8mq-hdmi", mhdp->plat_data->plat_name, 11)) {
+		if (di->bpc == 10 || di->bpc == 6)
+			video->color_depth = di->bpc;
+
+		return true;
+	}
+
+	/* imx8mq */
+	if (vic == 97 || vic == 96) {
+		if (di->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36)
+			video->color_depth = 12;
+		else if (di->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
+			video->color_depth = 10;
+
+		if (drm_mode_is_420_only(di, mode) ||
+		    (drm_mode_is_420_also(di, mode) &&
+		     video->color_depth > 8)) {
+			video->color_fmt = YCBCR_4_2_0;
+
+			adjusted_mode->private_flags = 1;
+			return true;
+		}
+
+		video->color_depth = 8;
+		return true;
+	}
+
+	/* Any defined maximum tmds clock limit we must not exceed*/
+	if ((di->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_36) &&
+	    (mode->clock * 3 / 2 <= di->max_tmds_clock))
+		video->color_depth = 12;
+	else if ((di->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30) &&
+		 (mode->clock * 5 / 4 <= di->max_tmds_clock))
+		video->color_depth = 10;
+
+	/* 10-bit color depth for the following modes is not supported */
+	if ((vic == 95 || vic == 94 || vic == 93) && video->color_depth == 10)
+		video->color_depth = 8;
+
+	return true;
+}
+
 static const struct drm_bridge_funcs cdns_hdmi_bridge_funcs = {
 	.attach = cdns_hdmi_bridge_attach,
 	.mode_set = cdns_hdmi_bridge_mode_set,
 	.mode_valid = cdns_hdmi_bridge_mode_valid,
+	.mode_fixup = cdns_hdmi_bridge_mode_fixup,
 };
 
 static void hotplug_work_func(struct work_struct *work)
