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 <[email protected]>
---
 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

Reply via email to