Some eDP panels report their VRR limits only via a DisplayID v2r0
Dynamic Video Timing Range Limits block (tag 0x25), without setting
DRM_EDID_FEATURE_CONTINUOUS_FREQ, causing monitor_range to remain
zeroed after drm_get_monitor_range().

Add displayid_is_dynamic_video_timing_block() and
displayid_parse_dynamic_video_timing(), following the pattern of
displayid_is_tiled_block(), to separate block identification from
parsing. Add a __packed struct for the block payload. Call
drm_get_monitor_range_displayid() from update_display_info() near
drm_update_mso().

Reported-by: kernel test robot <[email protected]>
Closes: 
https://lore.kernel.org/oe-kbuild-all/[email protected]/
Cc: Jani Nikula <[email protected]>
Cc: [email protected]
Signed-off-by: Adriano Vero <[email protected]>
---
 drivers/gpu/drm/drm_displayid_internal.h | 14 +++++
 drivers/gpu/drm/drm_edid.c               | 67 +++++++++++++++++++++++-
 2 files changed, 79 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_displayid_internal.h 
b/drivers/gpu/drm/drm_displayid_internal.h
index 5b1b32f73..5988e9b89 100644
--- a/drivers/gpu/drm/drm_displayid_internal.h
+++ b/drivers/gpu/drm/drm_displayid_internal.h
@@ -145,6 +145,20 @@ struct displayid_formula_timing_block {
 #define DISPLAYID_VESA_MSO_OVERLAP     GENMASK(3, 0)
 #define DISPLAYID_VESA_MSO_MODE                GENMASK(6, 5)
 
+/*
+ * DisplayID v2r0 ยง4.9 - Dynamic Video Timing Range Limits Block
+ * num_bytes must be 9, rev bits [7:1] must be zero.
+ * When rev bits [2:0] are nonzero, max_vfreq is a 10-bit value
+ * formed by max_vfreq_lo and bits [1:0] of max_vfreq_hi_flags.
+ */
+struct displayid_dynamic_video_timing_block {
+       struct displayid_block base;
+       u8 reserved[6];
+       u8 min_vfreq;
+       u8 max_vfreq_lo;
+       u8 max_vfreq_hi_flags;
+} __packed;
+
 struct displayid_vesa_vendor_specific_block {
        struct displayid_block base;
        u8 oui[3];
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index ff432ac6b..24aa37bea 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -6504,6 +6504,70 @@ void get_monitor_range(const struct detailed_timing 
*timing, void *c)
        }
 }
 
+static bool
+displayid_is_dynamic_video_timing_block(const struct displayid_iter *iter,
+                                       const struct displayid_block *block)
+{
+       return displayid_version(iter) == DISPLAY_ID_STRUCTURE_VER_20 &&
+               block->tag == DATA_BLOCK_2_DYNAMIC_VIDEO_TIMING;
+}
+
+static void
+displayid_parse_dynamic_video_timing(struct drm_connector *connector,
+                                    const struct displayid_block *block)
+{
+       const struct displayid_dynamic_video_timing_block *dvt =
+               (const struct displayid_dynamic_video_timing_block *)block;
+       struct drm_display_info *info = &connector->display_info;
+       u16 min_vfreq, max_vfreq;
+
+       /* Already populated from classic EDID descriptor */
+       if (info->monitor_range.min_vfreq && info->monitor_range.max_vfreq)
+               return;
+
+       /* Specification requires exactly 9 payload bytes */
+       if (block->num_bytes != 9)
+               return;
+
+       /* Specification requires rev bits [7:1] to be zero */
+       if (block->rev & 0xFE)
+               return;
+
+       min_vfreq = dvt->min_vfreq;
+
+       /* rev bits [2:0] nonzero: max_vfreq is a 10-bit value */
+       if (block->rev & 0x07)
+               max_vfreq = dvt->max_vfreq_lo | ((u16)(dvt->max_vfreq_hi_flags 
& 0x3) << 8);
+       else
+               max_vfreq = dvt->max_vfreq_lo;
+
+       if (!min_vfreq || !max_vfreq)
+               return;
+
+       info->monitor_range.min_vfreq = min_vfreq;
+       info->monitor_range.max_vfreq = max_vfreq;
+
+       drm_dbg_kms(connector->dev,
+                   "[CONNECTOR:%d:%s] DisplayID dynamic video timing range: 
%u-%u Hz\n",
+                   connector->base.id, connector->name,
+                   min_vfreq, max_vfreq);
+}
+
+static void
+drm_get_monitor_range_displayid(struct drm_connector *connector,
+                               const struct drm_edid *drm_edid)
+{
+       const struct displayid_block *block;
+       struct displayid_iter iter;
+
+       displayid_iter_edid_begin(drm_edid, &iter);
+       displayid_iter_for_each(block, &iter) {
+               if (displayid_is_dynamic_video_timing_block(&iter, block))
+                       displayid_parse_dynamic_video_timing(connector, block);
+       }
+       displayid_iter_end(&iter);
+}
+
 static void drm_get_monitor_range(struct drm_connector *connector,
                                  const struct drm_edid *drm_edid)
 {
@@ -6691,10 +6755,8 @@ static void update_display_info(struct drm_connector 
*connector,
        info->height_mm = edid->height_cm * 10;
 
        drm_get_monitor_range(connector, drm_edid);
-
        if (edid->revision < 3)
                goto out;
-
        if (!drm_edid_is_digital(drm_edid))
                goto out;
 
@@ -6757,6 +6819,7 @@ static void update_display_info(struct drm_connector 
*connector,
                info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
 
        drm_update_mso(connector, drm_edid);
+       drm_get_monitor_range_displayid(connector, drm_edid);
 
 out:
        if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_NON_DESKTOP)) {
-- 
2.47.3

Reply via email to