Some eDP panels report their VRR limits only via a DisplayID v1r2 Dynamic Video Timing Range Limits block (tag 0x25), without setting DRM_EDID_FEATURE_CONTINUOUS_FREQ. drm_get_monitor_range() returns early for such panels, leaving monitor_range zeroed.
Add drm_get_monitor_range_displayid() and call it from update_display_info() immediately after drm_get_monitor_range(). It uses displayid_iter_edid_begin() to locate Dynamic Video Timing blocks and extracts min/max refresh rates, including the 10-bit max_vfreq encoding signalled by block->rev bits [2:0]. It is a no-op when monitor_range is already populated by the classic EDID path. All drivers reading display_info.monitor_range now receive correct VRR limits from DisplayID-only panels without any raw EDID access. Byte offsets verified against parse_edid_displayid_vrr() in amdgpu_dm.c. Cc: Jani Nikula <[email protected]> Cc: [email protected] Signed-off-by: Adriano Vero <[email protected]> --- drivers/gpu/drm/drm_edid.c | 68 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index ff432ac6b..d2c360178 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -6504,6 +6504,71 @@ void get_monitor_range(const struct detailed_timing *timing, void *c) } } +/** + * drm_get_monitor_range_displayid - populate monitor_range from a + * DisplayID v1r2 Dynamic Video Timing Range Limits block (tag 0x25). + * + * Some eDP panels report their VRR limits only in a DisplayID block, + * without setting DRM_EDID_FEATURE_CONTINUOUS_FREQ. drm_get_monitor_range() + * returns early for such panels, leaving monitor_range zeroed. This + * function is called separately from update_display_info() as a fallback. + * + * Block payload layout (block->num_bytes == 9): + * data[6] min_vfreq in Hz + * data[7] max_vfreq low 8 bits + * data[8][1:0] max_vfreq high bits (when block->rev & 7 is nonzero) + * + * Byte offsets verified against parse_edid_displayid_vrr() in amdgpu_dm.c. + */ +static void +drm_get_monitor_range_displayid(struct drm_connector *connector, + const struct drm_edid *drm_edid) +{ + struct drm_display_info *info = &connector->display_info; + const struct displayid_block *block; + struct displayid_iter iter; + + /* Only run when the classic EDID path left these zeroed */ + if (info->monitor_range.min_vfreq && info->monitor_range.max_vfreq) + return; + + displayid_iter_edid_begin(drm_edid, &iter); + displayid_iter_for_each(block, &iter) { + const u8 *data; + u16 min_vfreq, max_vfreq; + + if (block->tag != DATA_BLOCK_2_DYNAMIC_VIDEO_TIMING) + continue; + + /* rev bits [7:1] must be zero; payload must be exactly 9 bytes */ + if ((block->rev & 0xFE) != 0 || block->num_bytes != 9) + continue; + + data = (const u8 *)(block + 1); + + min_vfreq = data[6]; + + /* rev bits [2:0] nonzero: max_vfreq is 10-bit */ + if (block->rev & 7) + max_vfreq = data[7] | ((u16)(data[8] & 3) << 8); + else + max_vfreq = data[7]; + + if (!min_vfreq || !max_vfreq) + continue; + + 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); + break; + } + displayid_iter_end(&iter); +} + static void drm_get_monitor_range(struct drm_connector *connector, const struct drm_edid *drm_edid) { @@ -6691,10 +6756,9 @@ static void update_display_info(struct drm_connector *connector, info->height_mm = edid->height_cm * 10; drm_get_monitor_range(connector, drm_edid); - + drm_get_monitor_range_displayid(connector, drm_edid); if (edid->revision < 3) goto out; - if (!drm_edid_is_digital(drm_edid)) goto out; -- 2.47.3
