@@ -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 */
/**
@@ -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 */
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@gmail.com> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: Jani Nikula <jani.nikula@intel.com> --- drivers/gpu/drm/drm_dp_helper.c | 90 +++++++++++++++++++++++++++++++++++++++++ include/drm/drm_dp_helper.h | 6 +++ 2 files changed, 96 insertions(+)