Blob Blame History Raw
From: Eric Yang <Eric.Yang2@amd.com>
Date: Wed, 18 Oct 2017 20:22:40 -0400
Subject: drm/amd/display: Add timing validation against dongle cap
Git-commit: 6bffebc90c23e2341a1f8371e7b496ec94136e47
Patch-mainline: v4.15-rc2
References: FATE#326289 FATE#326079 FATE#326049 FATE#322398 FATE#326166

For DP active dongles, the dpcd dongle caps are read but not
used to validate mode timing. This addresses this.

In particular, this change fixes light up on the HDMI 4k TV
connected through DP active dongle. Since the 4k TV defaults
to YCbCr420, which the dongle don't support.

This change does not address MST cases, a more generalized
approach must be taken for that.

Signed-off-by: Eric Yang <Eric.Yang2@amd.com>
Reviewed-by: Tony Cheng <Tony.Cheng@amd.com>
Acked-by: Harry Wentland <harry.wentland@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Acked-by: Petr Tesarik <ptesarik@suse.com>
---
 drivers/gpu/drm/amd/display/dc/core/dc.c         |    1 
 drivers/gpu/drm/amd/display/dc/core/dc_link.c    |   70 ++++++++++++++++++++++-
 drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c |   21 ++++++
 drivers/gpu/drm/amd/display/dc/inc/core_status.h |    2 
 4 files changed, 91 insertions(+), 3 deletions(-)

--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -128,6 +128,7 @@ static bool create_links(
 		link->link_id.id = CONNECTOR_ID_VIRTUAL;
 		link->link_id.enum_id = ENUM_ID_1;
 		link->link_enc = kzalloc(sizeof(*link->link_enc), GFP_KERNEL);
+		link->link_status.dpcd_caps = &link->dpcd_caps;
 
 		enc_init.ctx = dc->ctx;
 		enc_init.channel = CHANNEL_ID_UNKNOWN;
--- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c
@@ -1801,12 +1801,75 @@ static void disable_link(struct dc_link
 		link->link_enc->funcs->disable_output(link->link_enc, signal, link);
 }
 
+bool dp_active_dongle_validate_timing(
+		const struct dc_crtc_timing *timing,
+		const struct dc_dongle_caps *dongle_caps)
+{
+	unsigned int required_pix_clk = timing->pix_clk_khz;
+
+	if (dongle_caps->dongle_type != DISPLAY_DONGLE_DP_HDMI_CONVERTER ||
+		dongle_caps->extendedCapValid == false)
+		return true;
+
+	/* Check Pixel Encoding */
+	switch (timing->pixel_encoding) {
+	case PIXEL_ENCODING_RGB:
+	case PIXEL_ENCODING_YCBCR444:
+		break;
+	case PIXEL_ENCODING_YCBCR422:
+		if (!dongle_caps->is_dp_hdmi_ycbcr422_pass_through)
+			return false;
+		break;
+	case PIXEL_ENCODING_YCBCR420:
+		if (!dongle_caps->is_dp_hdmi_ycbcr420_pass_through)
+			return false;
+		break;
+	default:
+		/* Invalid Pixel Encoding*/
+		return false;
+	}
+
+
+	/* Check Color Depth and Pixel Clock */
+	if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420)
+		required_pix_clk /= 2;
+
+	switch (timing->display_color_depth) {
+	case COLOR_DEPTH_666:
+	case COLOR_DEPTH_888:
+		/*888 and 666 should always be supported*/
+		break;
+	case COLOR_DEPTH_101010:
+		if (dongle_caps->dp_hdmi_max_bpc < 10)
+			return false;
+		required_pix_clk = required_pix_clk * 10 / 8;
+		break;
+	case COLOR_DEPTH_121212:
+		if (dongle_caps->dp_hdmi_max_bpc < 12)
+			return false;
+		required_pix_clk = required_pix_clk * 12 / 8;
+		break;
+
+	case COLOR_DEPTH_141414:
+	case COLOR_DEPTH_161616:
+	default:
+		/* These color depths are currently not supported */
+		return false;
+	}
+
+	if (required_pix_clk > dongle_caps->dp_hdmi_max_pixel_clk)
+		return false;
+
+	return true;
+}
+
 enum dc_status dc_link_validate_mode_timing(
 		const struct dc_stream_state *stream,
 		struct dc_link *link,
 		const struct dc_crtc_timing *timing)
 {
 	uint32_t max_pix_clk = stream->sink->dongle_max_pix_clk;
+	struct dc_dongle_caps *dongle_caps = &link->link_status.dpcd_caps->dongle_caps;
 
 	/* A hack to avoid failing any modes for EDID override feature on
 	 * topology change such as lower quality cable for DP or different dongle
@@ -1814,8 +1877,13 @@ enum dc_status dc_link_validate_mode_tim
 	if (link->remote_sinks[0])
 		return DC_OK;
 
+	/* Passive Dongle */
 	if (0 != max_pix_clk && timing->pix_clk_khz > max_pix_clk)
-		return DC_EXCEED_DONGLE_MAX_CLK;
+		return DC_EXCEED_DONGLE_CAP;
+
+	/* Active Dongle*/
+	if (!dp_active_dongle_validate_timing(timing, dongle_caps))
+		return DC_EXCEED_DONGLE_CAP;
 
 	switch (stream->signal) {
 	case SIGNAL_TYPE_EDP:
--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c
@@ -2062,6 +2062,24 @@ bool is_dp_active_dongle(const struct dc
 			(dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER);
 }
 
+static int translate_dpcd_max_bpc(enum dpcd_downstream_port_max_bpc bpc)
+{
+	switch (bpc) {
+	case DOWN_STREAM_MAX_8BPC:
+		return 8;
+	case DOWN_STREAM_MAX_10BPC:
+		return 10;
+	case DOWN_STREAM_MAX_12BPC:
+		return 12;
+	case DOWN_STREAM_MAX_16BPC:
+		return 16;
+	default:
+		break;
+	}
+
+	return -1;
+}
+
 static void get_active_converter_info(
 	uint8_t data, struct dc_link *link)
 {
@@ -2131,7 +2149,8 @@ static void get_active_converter_info(
 					hdmi_caps.bits.YCrCr420_CONVERSION;
 
 				link->dpcd_caps.dongle_caps.dp_hdmi_max_bpc =
-					hdmi_color_caps.bits.MAX_BITS_PER_COLOR_COMPONENT;
+					translate_dpcd_max_bpc(
+						hdmi_color_caps.bits.MAX_BITS_PER_COLOR_COMPONENT);
 
 				link->dpcd_caps.dongle_caps.extendedCapValid = true;
 			}
--- a/drivers/gpu/drm/amd/display/dc/inc/core_status.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/core_status.h
@@ -38,7 +38,7 @@ enum dc_status {
 	DC_FAIL_DETACH_SURFACES = 8,
 	DC_FAIL_SURFACE_VALIDATE = 9,
 	DC_NO_DP_LINK_BANDWIDTH = 10,
-	DC_EXCEED_DONGLE_MAX_CLK = 11,
+	DC_EXCEED_DONGLE_CAP = 11,
 	DC_SURFACE_PIXEL_FORMAT_UNSUPPORTED = 12,
 	DC_FAIL_BANDWIDTH_VALIDATE = 13, /* BW and Watermark validation */
 	DC_FAIL_SCALING = 14,