[PATCH 3/5] drm/dp: Add helper to find bpc of a connected DP sink.

Mario Kleiner mario.kleiner.de at gmail.com
Wed Jul 6 10:05:46 UTC 2016


drm_dp_sink_bpc() is meant as a fallback to be used by
kms drivers if a DP sink doesn't provide supported color
depth bpc via EDID.

It parses dpcd registers and optionally downstream port
registers read by the driver from DP AUX and passed to
the function to find out what the bpc of the connected
sink likely is.

For a DP sink without downstream ports it assumes it is a
native DP display sink and applies the 6 bpc / 18 bpp
fallback as required by the DP spec.

For a DP sink with downstream ports, e.g., a DP -> legacy
converter it makes the following assignment:

If the converter is DP->DVI or DP->HDMI, it assumes 8 bpc
depth. If the converter is DP->VGA it assumes at least
8 bpc, but tries to get a more accurate value (8, 10, 12
or 16 bpc) if the converter exposes this info.

Tested with MiniDP->DP adapter, MiniDP->HDMI adapter,
MiniDP->single-link DVI adapter, MiniDP->dual-link DVI
active adapter, and a Apple MiniDP->VGA active adapter.

Signed-off-by: Mario Kleiner <mario.kleiner.de at gmail.com>
Cc: Ville Syrjälä <ville.syrjala at linux.intel.com>
Cc: Daniel Vetter <daniel.vetter at ffwll.ch>
Cc: Jani Nikula <jani.nikula at intel.com>
---
 drivers/gpu/drm/drm_dp_helper.c | 90 +++++++++++++++++++++++++++++++++++++++++
 include/drm/drm_dp_helper.h     |  6 +++
 2 files changed, 96 insertions(+)

diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index 091053e..2cf0289 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -160,6 +160,96 @@ int drm_dp_bw_code_to_link_rate(u8 link_bw)
 }
 EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate);
 
+/**
+ * drm_dp_sink_bpc - Parse dpcd to derive bpc of a DP sink.
+ *
+ * @dpcd: dpcd registers, starting at offset 0
+ * @downstream_ports: downstream port info registers, starting at
+ * offset DP_DOWNSTREAM_PORT_0
+ * @index: Index of the downstream port to query
+ *
+ * Derive color depth bpc of the connected DP sink. This is meant as
+ * a fallback for getting the sink bpc if the DP sink doesn't provide
+ * a useable bpc via EDID. It returns 6 bpc for a native DP sink
+ * without any downstream ports, as the spec mandates to assume 6 bpc
+ * if the true bpc of a native DP sink can't be found out from EDID.
+ *
+ * If the sink has multiple downstream ports then @index selects the
+ * downstream port to query, otherwise the first and only one is
+ * queried.
+ *
+ * The function can return 0 to signal that it can't derive bpc from the
+ * given information.
+ */
+int drm_dp_sink_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+		    const u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS],
+		    unsigned int index)
+{
+	uint8_t type;
+	bool detail = false;
+	int bpc = 0;
+
+	/*
+	 * If there isn't any downstream port then this is a native DP sink.
+	 * The standard requires to fall back to 6 bpc / 18 bpp for native DP
+	 * sinks which don't provide bit depth via EDID.
+	 */
+	if (!(dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT))
+		return 6;
+
+	/* Basic type of downstream ports? */
+	type = dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK;
+
+	/*
+	 * Lacking other info, 8 bpc is a reasonable start for analog out.
+	 * E.g., Apple MiniDP->VGA adaptors don't provide more info than
+	 * that. Despite having DP_DPCD_REV == 0x11, their downstream_ports
+	 * descriptor is empty - all zeros. DVI and HDMI also support at least
+	 * 8 bpc, so a TMDS port type also implies 8 bpc.
+	 */
+	if (type == DP_DWN_STRM_PORT_TYPE_ANALOG ||
+	    type == DP_DWN_STRM_PORT_TYPE_TMDS)
+		bpc = 8;
+
+	if (dpcd[DP_DPCD_REV] < 0x11)
+		return bpc;
+
+	/* Rev 1.1+. More specific info available per downstream port. */
+
+	/* Detailed info of 4 Bytes per downstream port available? */
+	if (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) {
+		index *= 4;
+		detail = true;
+	}
+
+	type = downstream_ports[index] & DP_DS_PORT_TYPE_MASK;
+
+	/* VGA, DVI and HDMI support at least 8 bpc */
+	if (type == DP_DS_PORT_TYPE_VGA || type == DP_DS_PORT_TYPE_DVI ||
+	    type == DP_DS_PORT_TYPE_HDMI)
+		bpc = 8;
+
+	/* As of DP interop v1.1a only VGA defines additional detail */
+	if (detail && (type == DP_DS_PORT_TYPE_VGA)) {
+		/* VGA with detail provides bpc info */
+		switch (downstream_ports[index + 2] &
+			DP_DS_VGA_MAX_BPC_MASK) {
+		    default:
+		    case DP_DS_VGA_8BPC:
+			return 8;
+		    case DP_DS_VGA_10BPC:
+			return 10;
+		    case DP_DS_VGA_12BPC:
+			return 12;
+		    case DP_DS_VGA_16BPC:
+			return 16;
+		}
+	}
+
+	return bpc;
+}
+EXPORT_SYMBOL(drm_dp_sink_bpc);
+
 #define AUX_RETRY_INTERVAL 500 /* us */
 
 /**
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index 4d85cf2..0737cd0 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -203,6 +203,8 @@
  * DP interop v1.1a only VGA defines additional detail.
  */
 
+#define DP_MAX_DOWNSTREAM_PORTS		    0x10
+
 /* offset 0 */
 #define DP_DOWNSTREAM_PORT_0		    0x80
 # define DP_DS_PORT_TYPE_MASK		    (7 << 0)
@@ -630,6 +632,10 @@ void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
 u8 drm_dp_link_rate_to_bw_code(int link_rate);
 int drm_dp_bw_code_to_link_rate(u8 link_bw);
 
+int drm_dp_sink_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+		    const u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS],
+		    unsigned int index);
+
 struct edp_sdp_header {
 	u8 HB0; /* Secondary Data Packet ID */
 	u8 HB1; /* Secondary Data Packet Type */
-- 
2.7.0



More information about the dri-devel mailing list