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
