From f8b7d3bad1225150c8909df309f8d10c365fdf3b Mon Sep 17 00:00:00 2001
From: Yaroslav Bolyukin <iam@lach.pw>
Date: Sun, 30 Oct 2022 18:59:15 +0100
Subject: [PATCH 1/2] drm/edid: parse DRM VESA dsc bpp target

As per DisplayID v2.0 Errata E9 spec "DSC pass-through timing support"
VESA vendor-specific data block may contain target DSC bits per pixel
fields

Signed-off-by: Yaroslav Bolyukin <iam@lach.pw>
Signed-off-by: Lach <iam@lach.pw>
---
 drivers/gpu/drm/drm_displayid_internal.h |  8 ++++
 drivers/gpu/drm/drm_edid.c               | 61 ++++++++++++++++--------
 include/drm/drm_connector.h              |  6 +++
 include/drm/drm_modes.h                  | 10 ++++
 4 files changed, 64 insertions(+), 21 deletions(-)

diff --git a/drivers/gpu/drm/drm_displayid_internal.h b/drivers/gpu/drm/drm_displayid_internal.h
index 957dd0619f5c..d008a98994bb 100644
--- a/drivers/gpu/drm/drm_displayid_internal.h
+++ b/drivers/gpu/drm/drm_displayid_internal.h
@@ -97,6 +97,10 @@ struct displayid_header {
 	u8 ext_count;
 } __packed;
 
+#define DISPLAYID_BLOCK_REV				GENMASK(2, 0)
+#define DISPLAYID_BLOCK_PASSTHROUGH_TIMINGS_SUPPORT	BIT(3)
+#define DISPLAYID_BLOCK_DESCRIPTOR_PAYLOAD_BYTES	GENMASK(6, 4)
+
 struct displayid_block {
 	u8 tag;
 	u8 rev;
@@ -144,12 +148,16 @@ struct displayid_formula_timing_block {
 
 #define DISPLAYID_VESA_MSO_OVERLAP	GENMASK(3, 0)
 #define DISPLAYID_VESA_MSO_MODE		GENMASK(6, 5)
+#define DISPLAYID_VESA_DSC_BPP_INT	GENMASK(5, 0)
+#define DISPLAYID_VESA_DSC_BPP_FRACT	GENMASK(3, 0)
 
 struct displayid_vesa_vendor_specific_block {
 	struct displayid_block base;
 	u8 oui[3];
 	u8 data_structure_type;
 	u8 mso;
+	u8 dsc_bpp_int;
+	u8 dsc_bpp_fract;
 } __packed;
 
 /*
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index e2e85345aa9a..6e42e55b41f9 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -6524,8 +6524,8 @@ static void drm_get_monitor_range(struct drm_connector *connector,
 		   info->monitor_range.min_vfreq, info->monitor_range.max_vfreq);
 }
 
-static void drm_parse_vesa_mso_data(struct drm_connector *connector,
-				   const struct displayid_block *block)
+static void drm_parse_vesa_specific_block(struct drm_connector *connector,
+					 const struct displayid_block *block)
 {
 	struct displayid_vesa_vendor_specific_block *vesa =
 		(struct displayid_vesa_vendor_specific_block *)block;
@@ -6541,7 +6541,7 @@ static void drm_parse_vesa_mso_data(struct drm_connector *connector,
 	if (oui(vesa->oui[0], vesa->oui[1], vesa->oui[2]) != VESA_IEEE_OUI)
 		return;
 
-	if (sizeof(*vesa) != sizeof(*block) + block->num_bytes) {
+	if (block->num_bytes < 5) {
 		drm_dbg_kms(connector->dev,
 			   "[CONNECTOR:%d:%s] Unexpected VESA vendor block size\n",
 			   connector->base.id, connector->name);
@@ -6564,28 +6564,40 @@ static void drm_parse_vesa_mso_data(struct drm_connector *connector,
 		break;
 	}
 
-	if (!info->mso_stream_count) {
-		info->mso_pixel_overlap = 0;
-		return;
-	}
+	info->mso_pixel_overlap = 0;
 
-	info->mso_pixel_overlap = FIELD_GET(DISPLAYID_VESA_MSO_OVERLAP, vesa->mso);
-	if (info->mso_pixel_overlap > 8) {
-		drm_dbg_kms(connector->dev,
-			   "[CONNECTOR:%d:%s] Reserved MSO pixel overlap value %u\n",
-			   connector->base.id, connector->name,
-			   info->mso_pixel_overlap);
-		info->mso_pixel_overlap = 8;
+	if (info->mso_stream_count) {
+		info->mso_pixel_overlap = FIELD_GET(DISPLAYID_VESA_MSO_OVERLAP, vesa->mso);
+		if (info->mso_pixel_overlap > 8) {
+			drm_dbg_kms(connector->dev,
+				   "[CONNECTOR:%d:%s] Reserved MSO pixel overlap value %u\n",
+				   connector->base.id, connector->name,
+				   info->mso_pixel_overlap);
+			info->mso_pixel_overlap = 8;
+		}
 	}
 
 	drm_dbg_kms(connector->dev,
 		   "[CONNECTOR:%d:%s] MSO stream count %u, pixel overlap %u\n",
 		   connector->base.id, connector->name,
 		   info->mso_stream_count, info->mso_pixel_overlap);
+
+	if (block->num_bytes < 7) {
+		/* DSC bpp is optional */
+		return;
+	}
+
+	info->dp_dsc_bpp = FIELD_GET(DISPLAYID_VESA_DSC_BPP_INT, vesa->dsc_bpp_int) << 4 |
+			  FIELD_GET(DISPLAYID_VESA_DSC_BPP_FRACT, vesa->dsc_bpp_fract);
+
+	drm_dbg_kms(connector->dev,
+		   "[CONNECTOR:%d:%s] DSC bits per pixel %u\n",
+		   connector->base.id, connector->name,
+		   info->dp_dsc_bpp);
 }
 
-static void drm_update_mso(struct drm_connector *connector,
-			  const struct drm_edid *drm_edid)
+static void drm_update_vesa_specific_block(struct drm_connector *connector,
+					  const struct drm_edid *drm_edid)
 {
 	const struct displayid_block *block;
 	struct displayid_iter iter;
@@ -6593,7 +6605,7 @@ static void drm_update_mso(struct drm_connector *connector,
 	displayid_iter_edid_begin(drm_edid, &iter);
 	displayid_iter_for_each(block, &iter) {
 		if (block->tag == DATA_BLOCK_2_VENDOR_SPECIFIC)
-			drm_parse_vesa_mso_data(connector, block);
+			drm_parse_vesa_specific_block(connector, block);
 	}
 	displayid_iter_end(&iter);
 }
@@ -6630,6 +6642,7 @@ static void drm_reset_display_info(struct drm_connector *connector)
 	info->mso_stream_count = 0;
 	info->mso_pixel_overlap = 0;
 	info->max_dsc_bpp = 0;
+	info->dp_dsc_bpp = 0;
 
 	kfree(info->vics);
 	info->vics = NULL;
@@ -6753,7 +6766,7 @@ static void update_display_info(struct drm_connector *connector,
 	if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422)
 		info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
 
-	drm_update_mso(connector, drm_edid);
+	drm_update_vesa_specific_block(connector, drm_edid);
 
 out:
 	if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_NON_DESKTOP)) {
@@ -6784,7 +6797,8 @@ static void update_display_info(struct drm_connector *connector,
 
 static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev,
 							   const struct displayid_detailed_timings_1 *timings,
-							   bool type_7)
+							   bool type_7,
+							   int rev)
 {
 	struct drm_display_mode *mode;
 	unsigned int pixel_clock = (timings->pixel_clock[0] |
@@ -6805,6 +6819,10 @@ static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *d
 	if (!mode)
 		return NULL;
 
+	if (type_7 && FIELD_GET(DISPLAYID_BLOCK_REV, rev) >= 1)
+		mode->dsc_passthrough_timings_support =
+			!!(rev & DISPLAYID_BLOCK_PASSTHROUGH_TIMINGS_SUPPORT);
+
 	/* resolution is kHz for type VII, and 10 kHz for type I */
 	mode->clock = type_7 ? pixel_clock : pixel_clock * 10;
 	mode->hdisplay = hactive;
@@ -6846,7 +6864,7 @@ static int add_displayid_detailed_1_modes(struct drm_connector *connector,
 	for (i = 0; i < num_timings; i++) {
 		struct displayid_detailed_timings_1 *timings = &det->timings[i];
 
-		newmode = drm_mode_displayid_detailed(connector->dev, timings, type_7);
+		newmode = drm_mode_displayid_detailed(connector->dev, timings, type_7, block->rev);
 		if (!newmode)
 			continue;
 
@@ -6893,7 +6911,8 @@ static int add_displayid_formula_modes(struct drm_connector *connector,
 	struct drm_display_mode *newmode;
 	int num_modes = 0;
 	bool type_10 = block->tag == DATA_BLOCK_2_TYPE_10_FORMULA_TIMING;
-	int timing_size = 6 + ((formula_block->base.rev & 0x70) >> 4);
+	int timing_size = 6 +
+		FIELD_GET(DISPLAYID_BLOCK_DESCRIPTOR_PAYLOAD_BYTES, formula_block->base.rev);
 
 	/* extended blocks are not supported yet */
 	if (timing_size != 6)
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 8f34f4b8183d..01640fcf7464 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -837,6 +837,12 @@ struct drm_display_info {
 	*/
 	u32 max_dsc_bpp;
 
+	/**
+	* @dp_dsc_bpp: DP Display-Stream-Compression (DSC) timing's target
+	* DSC bits per pixel in 6.4 fixed point format. 0 means undefined.
+	*/
+	u16 dp_dsc_bpp;
+
 	/**
 	* @vics: Array of vics_len VICs. Internal to EDID parsing.
 	*/
diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h
index b9bb92e4b029..312e5c03af9a 100644
--- a/include/drm/drm_modes.h
+++ b/include/drm/drm_modes.h
@@ -417,6 +417,16 @@ struct drm_display_mode {
 	*/
 	enum hdmi_picture_aspect picture_aspect_ratio;
 
+	/**
+	* @dsc_passthrough_timing_support:
+	*
+	* Indicates whether this mode timing descriptor is supported
+	* with specific target DSC bits per pixel only.
+	*
+	* VESA vendor-specific data block shall exist with the relevant
+	* DSC bits per pixel declaration when this flag is set to true.
+	*/
+	bool dsc_passthrough_timings_support;
 };
 
 /**
-- 
2.51.0
