From df474214a557ae937042b42fc23c0889ba0f7ecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:08 +0000 Subject: [PATCH 01/19] [FROM-ML] drm/amd/display: Return if DisplayID not found in parse_amd_vsdb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] The function would continue to try to parse EDID even if DisplayID extension block wasn't found. Sometimes it got lucky and found AMD vsdb in CEA extension block which made debugging harder. [How] Add a return if DisplayID extension block wasn't found Signed-off-by: Tomasz Pakuła --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index a8a59126b2d2b..59e1a25fdd27a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -13126,6 +13126,9 @@ static int parse_amd_vsdb(struct amdgpu_dm_connector *aconnector, break; } + if (i == edid->extensions) + return false; + while (j < EDID_LENGTH - sizeof(struct amd_vsdb_block)) { struct amd_vsdb_block *amd_vsdb = (struct amd_vsdb_block *)&edid_ext[j]; unsigned int ieeeId = (amd_vsdb->ieee_id[2] << 16) | (amd_vsdb->ieee_id[1] << 8) | (amd_vsdb->ieee_id[0]); From f755cd0a63f8ae1372736310157c3d7a03470b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:09 +0000 Subject: [PATCH 02/19] [FROM-ML] drm/amd/display: Refactor amdgpu_dm_update_freesync_caps() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] This function started to get very messy and hard to follow. [How] Eject some functionality to separate functions and simplify greatly. Changes in v3: - Less struct traversal in helper functions Signed-off-by: Tomasz Pakuła --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 123 +++++++++++------- 1 file changed, 73 insertions(+), 50 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 59e1a25fdd27a..997da59b74825 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -13109,8 +13109,8 @@ static void parse_edid_displayid_vrr(struct drm_connector *connector, } } -static int parse_amd_vsdb(struct amdgpu_dm_connector *aconnector, - const struct edid *edid, struct amdgpu_hdmi_vsdb_info *vsdb_info) +static int parse_amd_vsdb_did(struct amdgpu_dm_connector *aconnector, + const struct edid *edid, struct amdgpu_hdmi_vsdb_info *vsdb_info) { u8 *edid_ext = NULL; int i; @@ -13147,13 +13147,13 @@ static int parse_amd_vsdb(struct amdgpu_dm_connector *aconnector, return false; } -static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector, - const struct edid *edid, - struct amdgpu_hdmi_vsdb_info *vsdb_info) +static int parse_amd_vsdb_cea(struct amdgpu_dm_connector *aconnector, + const struct edid *edid, + struct amdgpu_hdmi_vsdb_info *vsdb_info) { + struct amdgpu_hdmi_vsdb_info vsdb_local = {0}; u8 *edid_ext = NULL; int i; - bool valid_vsdb_found = false; /*----- drm_find_cea_extension() -----*/ /* No EDID or EDID extensions */ @@ -13174,9 +13174,47 @@ static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector, if (edid_ext[0] != CEA_EXT) return -ENODEV; - valid_vsdb_found = parse_edid_cea(aconnector, edid_ext, EDID_LENGTH, vsdb_info); + if (!parse_edid_cea(aconnector, edid_ext, EDID_LENGTH, &vsdb_local)) + return -ENODEV; + + *vsdb_info = vsdb_local; + return i; +} + +static bool is_monitor_range_invalid(const struct drm_connector *conn) +{ + return conn->display_info.monitor_range.min_vfreq == 0 || + conn->display_info.monitor_range.max_vfreq == 0; +} + +/* + * Returns true if (max_vfreq - min_vfreq) > 10 + */ +static bool is_freesync_capable(const struct drm_monitor_range_info *range) +{ + return (range->max_vfreq - range->min_vfreq) > 10; +} + +static void monitor_range_from_vsdb(struct drm_display_info *display, + const struct amdgpu_hdmi_vsdb_info *vsdb) +{ + display->monitor_range.min_vfreq = vsdb->min_refresh_rate_hz; + display->monitor_range.max_vfreq = vsdb->max_refresh_rate_hz; +} + +/* + * Returns true if connector is capable of freesync + * Optionally, can fetch the range from AMD vsdb + */ +static bool copy_range_to_amdgpu_connector(struct drm_connector *conn) +{ + struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(conn); + struct drm_monitor_range_info *range = &conn->display_info.monitor_range; - return valid_vsdb_found ? i : -ENODEV; + aconn->min_vfreq = range->min_vfreq; + aconn->max_vfreq = range->max_vfreq; + + return is_freesync_capable(range); } /** @@ -13193,13 +13231,14 @@ static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector, void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, const struct drm_edid *drm_edid) { - int i = 0; struct amdgpu_dm_connector *amdgpu_dm_connector = to_amdgpu_dm_connector(connector); struct dm_connector_state *dm_con_state = NULL; struct dc_sink *sink; struct amdgpu_device *adev = drm_to_adev(connector->dev); struct amdgpu_hdmi_vsdb_info vsdb_info = {0}; + struct amdgpu_hdmi_vsdb_info vsdb_did = {0}; + struct dpcd_caps dpcd_caps = {0}; const struct edid *edid; bool freesync_capable = false; enum adaptive_sync_type as_type = ADAPTIVE_SYNC_TYPE_NONE; @@ -13231,62 +13270,46 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, goto update; edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw() + parse_amd_vsdb_cea(amdgpu_dm_connector, edid, &vsdb_info); + + if (amdgpu_dm_connector->dc_link) + dpcd_caps = amdgpu_dm_connector->dc_link->dpcd_caps; /* Some eDP panels only have the refresh rate range info in DisplayID */ - if ((connector->display_info.monitor_range.min_vfreq == 0 || - connector->display_info.monitor_range.max_vfreq == 0)) + if (is_monitor_range_invalid(connector)) parse_edid_displayid_vrr(connector, edid); - if (edid && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT || - sink->sink_signal == SIGNAL_TYPE_EDP)) { - if (amdgpu_dm_connector->dc_link && - amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param) { - amdgpu_dm_connector->min_vfreq = connector->display_info.monitor_range.min_vfreq; - amdgpu_dm_connector->max_vfreq = connector->display_info.monitor_range.max_vfreq; - if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) - freesync_capable = true; - } + if (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT || + sink->sink_signal == SIGNAL_TYPE_EDP) { + + if (dpcd_caps.allow_invalid_MSA_timing_param) + freesync_capable = copy_range_to_amdgpu_connector(connector); - parse_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info); + /* eDP */ + if (edid) + parse_amd_vsdb_did(amdgpu_dm_connector, edid, &vsdb_did); - if (vsdb_info.replay_mode) { - amdgpu_dm_connector->vsdb_info.replay_mode = vsdb_info.replay_mode; - amdgpu_dm_connector->vsdb_info.amd_vsdb_version = vsdb_info.amd_vsdb_version; + if (vsdb_did.replay_mode) { + amdgpu_dm_connector->vsdb_info.replay_mode = vsdb_did.replay_mode; + amdgpu_dm_connector->vsdb_info.amd_vsdb_version = vsdb_did.amd_vsdb_version; amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_EDP; } - } else if (drm_edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) { - i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info); - if (i >= 0 && vsdb_info.freesync_supported) { - amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz; - amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz; - if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) - freesync_capable = true; - - connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz; - connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz; - } + } else if (sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A && vsdb_info.freesync_supported) { + monitor_range_from_vsdb(&connector->display_info, &vsdb_info); + freesync_capable = copy_range_to_amdgpu_connector(connector); } if (amdgpu_dm_connector->dc_link) as_type = dm_get_adaptive_sync_support_type(amdgpu_dm_connector->dc_link); - if (as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) { - i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info); - if (i >= 0 && vsdb_info.freesync_supported && vsdb_info.amd_vsdb_version > 0) { + if (as_type == FREESYNC_TYPE_PCON_IN_WHITELIST && vsdb_info.freesync_supported) { + amdgpu_dm_connector->pack_sdp_v1_3 = true; + amdgpu_dm_connector->as_type = as_type; + amdgpu_dm_connector->vsdb_info = vsdb_info; - amdgpu_dm_connector->pack_sdp_v1_3 = true; - amdgpu_dm_connector->as_type = as_type; - amdgpu_dm_connector->vsdb_info = vsdb_info; - - amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz; - amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz; - if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) - freesync_capable = true; - - connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz; - connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz; - } + monitor_range_from_vsdb(&connector->display_info, &vsdb_info); + freesync_capable = copy_range_to_amdgpu_connector(connector); } update: From b33f6405eac2bab4e984df44a2a27a08730d0426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:10 +0000 Subject: [PATCH 03/19] [FROM-ML] drm/amd/display: Check for VRR range in CEA AMD vsdb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] Some monitors only expose GTF ranges (or others, without Range Limits Only flag). This breaks VRR even though they have explicit FreeSync support. Currently, if monitor ranges were missing, amdgpu only searched for AMD vsdb in DisplayID but many monitors have it in CEA, just like HDMI. [How] For DP and eDP connections, check for VRR ranges provided in AMD vendor- specific data block if VRR range wasn't detected. Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/3894 Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/4457 Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/4747 Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/4856 Signed-off-by: Tomasz Pakuła --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 997da59b74825..81d78f77bc015 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -13281,6 +13281,12 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, if (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT || sink->sink_signal == SIGNAL_TYPE_EDP) { + /* + * Many monitors expose AMD vsdb in CAE even for DP and their + * monitor ranges do not contain Range Limits Only flag + */ + if (is_monitor_range_invalid(connector)) + monitor_range_from_vsdb(&connector->display_info, &vsdb_info); if (dpcd_caps.allow_invalid_MSA_timing_param) freesync_capable = copy_range_to_amdgpu_connector(connector); From f7e80a497ce9505b5ec77a42bc4461ba1160e204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:11 +0000 Subject: [PATCH 04/19] [FROM-ML] drm/amd/display: Use bigger VRR range if found in AMD vsdb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] Some monitors only expose their full VRR range in AMD vsdb for some reason. [How] Compare exposed ranges and use the bigger one. This check could be merged with the previous one but it's better to keep them separate to easily convey their meaning. Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/4177 Signed-off-by: Tomasz Pakuła --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 81d78f77bc015..259b622719218 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -13217,6 +13217,18 @@ static bool copy_range_to_amdgpu_connector(struct drm_connector *conn) return is_freesync_capable(range); } +/* + * Returns true if range from AMD vsdb is bigger + */ +static bool compare_ranges(struct drm_connector *conn, + struct amdgpu_hdmi_vsdb_info *vsdb) +{ + struct drm_monitor_range_info *range = &conn->display_info.monitor_range; + + return (vsdb->max_refresh_rate_hz - vsdb->min_refresh_rate_hz) > + (range->max_vfreq - range->min_vfreq); +} + /** * amdgpu_dm_update_freesync_caps - Update Freesync capabilities * @@ -13288,6 +13300,10 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, if (is_monitor_range_invalid(connector)) monitor_range_from_vsdb(&connector->display_info, &vsdb_info); + /* Use bigger range if found in AMD vsdb */ + if (compare_ranges(connector, &vsdb_info)) + monitor_range_from_vsdb(&connector->display_info, &vsdb_info); + if (dpcd_caps.allow_invalid_MSA_timing_param) freesync_capable = copy_range_to_amdgpu_connector(connector); From fd221899c1dac3492c39f18e84450ff9dea17b0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:12 +0000 Subject: [PATCH 05/19] [FROM-ML] drm/amd/display: Refactor PCON VRR compatibility check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] DP->HDMI PCONs prevously entered the DP path [How] Restructure amdgpu_dm_update_freesync_caps() and move dm_get_adaptive_sync_support_type() to dm_helpers_is_vrr_pcon_allowed() to better reflect what this function does. It never actually gave us any other info. Signed-off-by: Tomasz Pakuła --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 24 +++++++----- .../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 39 ++++++------------- drivers/gpu/drm/amd/display/dc/dm_helpers.h | 2 +- 3 files changed, 27 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 259b622719218..7b5d63cc0db95 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -13253,7 +13253,8 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, struct dpcd_caps dpcd_caps = {0}; const struct edid *edid; bool freesync_capable = false; - enum adaptive_sync_type as_type = ADAPTIVE_SYNC_TYPE_NONE; + bool pcon_allowed = false; + bool is_pcon = false; if (!connector->state) { drm_err(adev_to_drm(adev), "%s - Connector has no state", __func__); @@ -13281,18 +13282,23 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, if (!adev->dm.freesync_module || !dc_supports_vrr(sink->ctx->dce_version)) goto update; + /* Gather all data */ edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw() parse_amd_vsdb_cea(amdgpu_dm_connector, edid, &vsdb_info); - if (amdgpu_dm_connector->dc_link) + if (amdgpu_dm_connector->dc_link) { dpcd_caps = amdgpu_dm_connector->dc_link->dpcd_caps; + is_pcon = dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER; + pcon_allowed = dm_helpers_is_vrr_pcon_allowed(amdgpu_dm_connector->dc_link); + } /* Some eDP panels only have the refresh rate range info in DisplayID */ if (is_monitor_range_invalid(connector)) parse_edid_displayid_vrr(connector, edid); - if (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT || - sink->sink_signal == SIGNAL_TYPE_EDP) { + /* DP & eDP excluding PCONs */ + if ((sink->sink_signal == SIGNAL_TYPE_EDP || + sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT) && !is_pcon) { /* * Many monitors expose AMD vsdb in CAE even for DP and their * monitor ranges do not contain Range Limits Only flag @@ -13317,17 +13323,15 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_EDP; } + /* HDMI */ } else if (sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A && vsdb_info.freesync_supported) { monitor_range_from_vsdb(&connector->display_info, &vsdb_info); freesync_capable = copy_range_to_amdgpu_connector(connector); - } - - if (amdgpu_dm_connector->dc_link) - as_type = dm_get_adaptive_sync_support_type(amdgpu_dm_connector->dc_link); - if (as_type == FREESYNC_TYPE_PCON_IN_WHITELIST && vsdb_info.freesync_supported) { + /* DP -> HDMI PCON */ + } else if (pcon_allowed && vsdb_info.freesync_supported) { + amdgpu_dm_connector->as_type = FREESYNC_TYPE_PCON_IN_WHITELIST; amdgpu_dm_connector->pack_sdp_v1_3 = true; - amdgpu_dm_connector->as_type = as_type; amdgpu_dm_connector->vsdb_info = vsdb_info; monitor_range_from_vsdb(&connector->display_info, &vsdb_info); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index e5e993d3ef74a..76a10fe8d545c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -1375,40 +1375,25 @@ void dm_helpers_dp_mst_update_branch_bandwidth( // TODO } -static bool dm_is_freesync_pcon_whitelist(const uint32_t branch_dev_id) +bool dm_helpers_is_vrr_pcon_allowed(const struct dc_link *link) { - bool ret_val = false; + if (link->dpcd_caps.dongle_type != DISPLAY_DONGLE_DP_HDMI_CONVERTER) + return false; + + if (!link->dpcd_caps.allow_invalid_MSA_timing_param) + return false; + + if (!link->dpcd_caps.adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT) + return false; - switch (branch_dev_id) { + switch (link->dpcd_caps.branch_dev_id) { case DP_BRANCH_DEVICE_ID_0060AD: case DP_BRANCH_DEVICE_ID_00E04C: case DP_BRANCH_DEVICE_ID_90CC24: - ret_val = true; - break; - default: - break; - } - - return ret_val; -} - -enum adaptive_sync_type dm_get_adaptive_sync_support_type(struct dc_link *link) -{ - struct dpcd_caps *dpcd_caps = &link->dpcd_caps; - enum adaptive_sync_type as_type = ADAPTIVE_SYNC_TYPE_NONE; - - switch (dpcd_caps->dongle_type) { - case DISPLAY_DONGLE_DP_HDMI_CONVERTER: - if (dpcd_caps->adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT == true && - dpcd_caps->allow_invalid_MSA_timing_param == true && - dm_is_freesync_pcon_whitelist(dpcd_caps->branch_dev_id)) - as_type = FREESYNC_TYPE_PCON_IN_WHITELIST; - break; - default: - break; + return true; } - return as_type; + return false; } bool dm_helpers_is_fullscreen(struct dc_context *ctx, struct dc_stream_state *stream) diff --git a/drivers/gpu/drm/amd/display/dc/dm_helpers.h b/drivers/gpu/drm/amd/display/dc/dm_helpers.h index 9d160b39e8c5a..f8b45a09d6806 100644 --- a/drivers/gpu/drm/amd/display/dc/dm_helpers.h +++ b/drivers/gpu/drm/amd/display/dc/dm_helpers.h @@ -219,10 +219,10 @@ int dm_helpers_dmub_set_config_sync(struct dc_context *ctx, const struct dc_link *link, struct set_config_cmd_payload *payload, enum set_config_status *operation_result); -enum adaptive_sync_type dm_get_adaptive_sync_support_type(struct dc_link *link); enum dc_edid_status dm_helpers_get_sbios_edid(struct dc_link *link, struct dc_edid *edid); +bool dm_helpers_is_vrr_pcon_allowed(const struct dc_link *link); bool dm_helpers_is_fullscreen(struct dc_context *ctx, struct dc_stream_state *stream); bool dm_helpers_is_hdr_on(struct dc_context *ctx, struct dc_stream_state *stream); From 6089fe9efcafd5eb1453e48c1449532f9baacace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:13 +0000 Subject: [PATCH 06/19] [FROM-ML] drm/amd/display: Add PCON VRR ID check override MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] It's currently very hard to test if a random PCON supports VRR and report it's ID. [How] Adds override as part of dc debug mask. Allows faster testing and reporting of VRR-compatible DP->HDMI adapters. Signed-off-by: Tomasz Pakuła --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 6 +++++- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 8 +++++++- drivers/gpu/drm/amd/display/dc/dc.h | 1 + drivers/gpu/drm/amd/display/dc/dm_helpers.h | 2 +- drivers/gpu/drm/amd/include/amd_shared.h | 6 ++++++ 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 7b5d63cc0db95..6d4dbabb4ef87 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -2069,6 +2069,9 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) if (amdgpu_dc_debug_mask & DC_SKIP_DETECTION_LT) adev->dm.dc->debug.skip_detection_link_training = true; + if (amdgpu_dc_debug_mask & DC_OVERRIDE_PCON_VRR_ID_CHECK) + adev->dm.dc->debug.override_pcon_vrr_id_check = true; + adev->dm.dc->debug.visual_confirm = amdgpu_dc_visual_confirm; /* TODO: Remove after DP2 receiver gets proper support of Cable ID feature */ @@ -13289,7 +13292,8 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, if (amdgpu_dm_connector->dc_link) { dpcd_caps = amdgpu_dm_connector->dc_link->dpcd_caps; is_pcon = dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER; - pcon_allowed = dm_helpers_is_vrr_pcon_allowed(amdgpu_dm_connector->dc_link); + pcon_allowed = dm_helpers_is_vrr_pcon_allowed( + amdgpu_dm_connector->dc_link, connector->dev); } /* Some eDP panels only have the refresh rate range info in DisplayID */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index 76a10fe8d545c..2ef515a4e1c45 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -1375,7 +1375,7 @@ void dm_helpers_dp_mst_update_branch_bandwidth( // TODO } -bool dm_helpers_is_vrr_pcon_allowed(const struct dc_link *link) +bool dm_helpers_is_vrr_pcon_allowed(const struct dc_link *link, const struct drm_device *dev) { if (link->dpcd_caps.dongle_type != DISPLAY_DONGLE_DP_HDMI_CONVERTER) return false; @@ -1393,6 +1393,12 @@ bool dm_helpers_is_vrr_pcon_allowed(const struct dc_link *link) return true; } + if (link->dc->debug.override_pcon_vrr_id_check) { + drm_info(dev, "Overriding VRR PCON check for ID: 0x%06x\n", + link->dpcd_caps.branch_dev_id); + return true; + } + return false; } diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 29edfa51ea2cc..f24ece2eafc51 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -1039,6 +1039,7 @@ struct dc_debug_options { bool scl_reset_length10; bool hdmi20_disable; bool skip_detection_link_training; + bool override_pcon_vrr_id_check; uint32_t edid_read_retry_times; unsigned int force_odm_combine; //bit vector based on otg inst unsigned int seamless_boot_odm_combine; diff --git a/drivers/gpu/drm/amd/display/dc/dm_helpers.h b/drivers/gpu/drm/amd/display/dc/dm_helpers.h index f8b45a09d6806..ea94c52d2b871 100644 --- a/drivers/gpu/drm/amd/display/dc/dm_helpers.h +++ b/drivers/gpu/drm/amd/display/dc/dm_helpers.h @@ -222,7 +222,7 @@ int dm_helpers_dmub_set_config_sync(struct dc_context *ctx, enum dc_edid_status dm_helpers_get_sbios_edid(struct dc_link *link, struct dc_edid *edid); -bool dm_helpers_is_vrr_pcon_allowed(const struct dc_link *link); +bool dm_helpers_is_vrr_pcon_allowed(const struct dc_link *link, const struct drm_device *dev); bool dm_helpers_is_fullscreen(struct dc_context *ctx, struct dc_stream_state *stream); bool dm_helpers_is_hdr_on(struct dc_context *ctx, struct dc_stream_state *stream); diff --git a/drivers/gpu/drm/amd/include/amd_shared.h b/drivers/gpu/drm/amd/include/amd_shared.h index 17945094a1383..839cfe2eeabe6 100644 --- a/drivers/gpu/drm/amd/include/amd_shared.h +++ b/drivers/gpu/drm/amd/include/amd_shared.h @@ -411,6 +411,12 @@ enum DC_DEBUG_MASK { * @DC_SKIP_DETECTION_LT: (0x200000) If set, skip detection link training */ DC_SKIP_DETECTION_LT = 0x200000, + + /** + * @DC_OVERRIDE_PCON_VRR_ID_CHECK: (0x400000) If set, always return true if checking for + * PCON VRR compatibility and print it's ID in kernel log. + */ + DC_OVERRIDE_PCON_VRR_ID_CHECK = 0x400000, }; enum amd_dpm_forced_level; From 5e007f7fa03ddcdc9d17505c9f4d391a4304649f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:14 +0000 Subject: [PATCH 07/19] [FROM-ML] drm/amd/display: Add CH7218 PCON ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] Chrontel CH7218 found in Ugreen DP -> HDMI 2.1 adapter (model 85564) works perfectly with VRR after testing. VRR and FreeSync compatibility is explicitly advertised as a feature so it's addition is a formality. Support FreeSync info packet passthrough and "generic" HDMI VRR. [How] Add CH7218's ID to dm_helpers_is_vrr_pcon_allowed() Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/4773 Signed-off-by: Tomasz Pakuła --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 1 + drivers/gpu/drm/amd/display/include/ddc_service_types.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index 2ef515a4e1c45..37747f87b55a7 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -1390,6 +1390,7 @@ bool dm_helpers_is_vrr_pcon_allowed(const struct dc_link *link, const struct drm case DP_BRANCH_DEVICE_ID_0060AD: case DP_BRANCH_DEVICE_ID_00E04C: case DP_BRANCH_DEVICE_ID_90CC24: + case DP_BRANCH_DEVICE_ID_2B02F0: return true; } diff --git a/drivers/gpu/drm/amd/display/include/ddc_service_types.h b/drivers/gpu/drm/amd/display/include/ddc_service_types.h index 1c603b12957f6..e838f7c1269c4 100644 --- a/drivers/gpu/drm/amd/display/include/ddc_service_types.h +++ b/drivers/gpu/drm/amd/display/include/ddc_service_types.h @@ -36,6 +36,7 @@ #define DP_BRANCH_DEVICE_ID_006037 0x006037 #define DP_BRANCH_DEVICE_ID_001CF8 0x001CF8 #define DP_BRANCH_DEVICE_ID_0060AD 0x0060AD +#define DP_BRANCH_DEVICE_ID_2B02F0 0x2B02F0 /* Chrontel CH7218 */ #define DP_BRANCH_HW_REV_10 0x10 #define DP_BRANCH_HW_REV_20 0x20 From 72a5beb0d65c13152f29720094e6c158cc83262e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:15 +0000 Subject: [PATCH 08/19] [FROM-ML] drm/edid: Parse more info from HDMI Forum vsdb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] Drivers may need info about gaming features exposed by HDMI sinks. Add a central way of storing this information. [How] Adds flags and a struct to hold HDMI VRR information. `supported` here is an additional property which allows easier parsing in consumers and adds a bit of logic used to detect malformed VRRmin/VRRmax values. Signed-off-by: Tomasz Pakuła Tested-by: Bernhard Berger --- drivers/gpu/drm/drm_edid.c | 41 +++++++++++++++++++++++++++++++- include/drm/drm_connector.h | 47 +++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 26bb7710a4620..056eff8cbd1a8 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -6152,6 +6152,33 @@ static void drm_parse_ycbcr420_deep_color_info(struct drm_connector *connector, hdmi->y420_dc_modes = dc_mask; } +static void drm_parse_hdmi_gaming_info(struct drm_hdmi_info *hdmi, const u8 *db) +{ + struct drm_hdmi_vrr_cap *vrr = &hdmi->vrr_cap; + + if (cea_db_payload_len(db) < 8) + return; + + hdmi->fapa_start_location = db[8] & DRM_EDID_FAPA_START_LOCATION; + hdmi->allm = db[8] & DRM_EDID_ALLM; + vrr->fva = db[8] & DRM_EDID_FVA; + vrr->cnmvrr = db[8] & DRM_EDID_CNMVRR; + vrr->cinema_vrr = db[8] & DRM_EDID_CINEMA_VRR; + vrr->mdelta = db[8] & DRM_EDID_MDELTA; + + if (cea_db_payload_len(db) < 9) + return; + + vrr->vrr_min = db[9] & DRM_EDID_VRR_MIN_MASK; + vrr->supported = (vrr->vrr_min > 0 && vrr->vrr_min <= 48); + + if (cea_db_payload_len(db) < 10) + return; + + vrr->vrr_max = (db[9] & DRM_EDID_VRR_MAX_UPPER_MASK) << 2 | db[10]; + vrr->supported &= (vrr->vrr_max == 0 || vrr->vrr_max >= 100); +} + static void drm_parse_dsc_info(struct drm_hdmi_dsc_cap *hdmi_dsc, const u8 *hf_scds) { @@ -6277,7 +6304,7 @@ static void drm_parse_hdmi_forum_scds(struct drm_connector *connector, } drm_parse_ycbcr420_deep_color_info(connector, hf_scds); - + drm_parse_hdmi_gaming_info(&connector->display_info.hdmi, hf_scds); if (cea_db_payload_len(hf_scds) >= 11 && hf_scds[11]) { drm_parse_dsc_info(hdmi_dsc, hf_scds); dsc_support = true; @@ -6287,6 +6314,18 @@ static void drm_parse_hdmi_forum_scds(struct drm_connector *connector, "[CONNECTOR:%d:%s] HF-VSDB: max TMDS clock: %d KHz, HDMI 2.1 support: %s, DSC 1.2 support: %s\n", connector->base.id, connector->name, max_tmds_clock, str_yes_no(max_frl_rate), str_yes_no(dsc_support)); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] FAPA in blanking: %s, ALLM support: %s, Fast Vactive support: %s\n", + connector->base.id, connector->name, str_yes_no(hdmi->fapa_start_location), + str_yes_no(hdmi->allm), str_yes_no(hdmi->vrr_cap.fva)); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] Negative M VRR support: %s, CinemaVRR support: %s, Mdelta: %d\n", + connector->base.id, connector->name, str_yes_no(hdmi->vrr_cap.cnmvrr), + str_yes_no(hdmi->vrr_cap.cinema_vrr), hdmi->vrr_cap.mdelta); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] VRRmin: %u, VRRmax: %u, VRR supported: %s\n", + connector->base.id, connector->name, hdmi->vrr_cap.vrr_min, + hdmi->vrr_cap.vrr_max, str_yes_no(hdmi->vrr_cap.supported)); } static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector, diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 8f34f4b8183d8..dab9d5521f41b 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -254,6 +254,44 @@ struct drm_scdc { struct drm_scrambling scrambling; }; +/** + * struct drm_hdmi_vrr_cap - Information about VRR capabilities of a HDMI sink + * + * Describes the VRR support provided by HDMI 2.1 sink. The information is + * fetched fom additional HFVSDB blocks defined for HDMI 2.1. + */ +struct drm_hdmi_vrr_cap { + /** @fva: flag for Fast VActive (Quick Frame Transport) support */ + bool fva; + + /** @mcnmvrr: flag for Negative M VRR support */ + bool cnmvrr; + + /** @mcinema_vrr: flag for Cinema VRR support */ + bool cinema_vrr; + + /** @mdelta: flag for limited frame-to-frame compensation support */ + bool mdelta; + + /** + * @vrr_min : minimum supported variable refresh rate in Hz. + * Valid values only inide 1 - 48 range + */ + u16 vrr_min; + + /** + * @vrr_max : maximum supported variable refresh rate in Hz (optional). + * Valid values are either 0 (max based on video mode) or >= 100 + */ + u16 vrr_max; + + /** + * @supported: flag for vrr support based on checking for VRRmin and + * VRRmax values having correct values. + */ + bool supported; +}; + /** * struct drm_hdmi_dsc_cap - DSC capabilities of HDMI sink * @@ -330,6 +368,15 @@ struct drm_hdmi_info { /** @max_lanes: supported by sink */ u8 max_lanes; + /** @fapa_start_location: flag for the FAPA in blanking support */ + bool fapa_start_location; + + /** @allm: flag for Auto Low Latency Mode support by sink */ + bool allm; + + /** @vrr_cap: VRR capabilities of the sink */ + struct drm_hdmi_vrr_cap vrr_cap; + /** @dsc_cap: DSC capabilities of the sink */ struct drm_hdmi_dsc_cap dsc_cap; }; From 6531fec604e92ea199fb5dc52bf853993e4a66a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:16 +0000 Subject: [PATCH 09/19] [FROM-ML] drm/amd/display: Rename PCON adaptive sync types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] PCONs support sending out HDMI VRR infopackets on their own and this makes this types not specific to FreeSync [How] Make the name more generic for the upcoming HDMI VRR over PCON implementation Signed-off-by: Tomasz Pakuła Tested-by: Bernhard Berger --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 5 +++-- .../gpu/drm/amd/display/modules/inc/mod_info_packet.h | 10 +++++----- .../drm/amd/display/modules/info_packet/info_packet.c | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 6d4dbabb4ef87..3167c84b6394d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -9610,7 +9610,8 @@ static void update_freesync_state_on_stream( aconn = (struct amdgpu_dm_connector *)new_stream->dm_stream_context; - if (aconn && (aconn->as_type == FREESYNC_TYPE_PCON_IN_WHITELIST || aconn->vsdb_info.replay_mode)) { + if (aconn && (aconn->as_type == ADAPTIVE_SYNC_TYPE_PCON_ALLOWED || + aconn->vsdb_info.replay_mode)) { pack_sdp_v1_3 = aconn->pack_sdp_v1_3; if (aconn->vsdb_info.amd_vsdb_version == 1) @@ -13334,7 +13335,7 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, /* DP -> HDMI PCON */ } else if (pcon_allowed && vsdb_info.freesync_supported) { - amdgpu_dm_connector->as_type = FREESYNC_TYPE_PCON_IN_WHITELIST; + amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_PCON_ALLOWED; amdgpu_dm_connector->pack_sdp_v1_3 = true; amdgpu_dm_connector->vsdb_info = vsdb_info; diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h index 66dc9a19aebe5..040aa14cb1ce3 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h @@ -42,11 +42,11 @@ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, struct dc_info_packet *info_packet); enum adaptive_sync_type { - ADAPTIVE_SYNC_TYPE_NONE = 0, - ADAPTIVE_SYNC_TYPE_DP = 1, - FREESYNC_TYPE_PCON_IN_WHITELIST = 2, - FREESYNC_TYPE_PCON_NOT_IN_WHITELIST = 3, - ADAPTIVE_SYNC_TYPE_EDP = 4, + ADAPTIVE_SYNC_TYPE_NONE = 0, + ADAPTIVE_SYNC_TYPE_DP = 1, + ADAPTIVE_SYNC_TYPE_PCON_ALLOWED = 2, + ADAPTIVE_SYNC_TYPE_PCON_NOT_ALLOWED = 3, + ADAPTIVE_SYNC_TYPE_EDP = 4, }; enum adaptive_sync_sdp_version { diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c index b3d55cac35694..b50fb75236e93 100644 --- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c +++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c @@ -535,12 +535,12 @@ void mod_build_adaptive_sync_infopacket(const struct dc_stream_state *stream, if (stream != NULL) mod_build_adaptive_sync_infopacket_v2(stream, param, info_packet); break; - case FREESYNC_TYPE_PCON_IN_WHITELIST: + case ADAPTIVE_SYNC_TYPE_PCON_ALLOWED: case ADAPTIVE_SYNC_TYPE_EDP: mod_build_adaptive_sync_infopacket_v1(info_packet); break; case ADAPTIVE_SYNC_TYPE_NONE: - case FREESYNC_TYPE_PCON_NOT_IN_WHITELIST: + case ADAPTIVE_SYNC_TYPE_PCON_NOT_ALLOWED: default: break; } From 75e4c1631db8f4dc26ba9fbff43e0e7e0da96707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:17 +0000 Subject: [PATCH 10/19] [FROM-ML] drm/amd/display: Enable HDMI VRR over PCON MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] Not all TVs support FreeSync and many TVs suffer from VRR flickering while Freesync is activated. [How] This works the same as FreeSync over PCON just without sending FreeSync info packets (we're sending standard DisplayPort info packets) + reading the VRR range from the HDMI Forum vendor specific data block. PCONs take over HDMI VRR triggering. Prefer HDMI VRR over FreeSync to reduce VRR flickering on many TVs. FreeSync over HDMI seems to be a fallback solution and not a first-class citizen. This especially helps VMM7100. Tested with VMM7100 and CH7218 based adapters on multiple HDMI 2.1 and HDMI 2.0 devices. (Samsung S95B, LG C4, Sony Bravia 8, Dell AW3423DWF) Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/4805 Signed-off-by: Tomasz Pakuła Tested-by: Bernhard Berger --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 3167c84b6394d..2af7a9b72c5c6 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -13206,6 +13206,17 @@ static void monitor_range_from_vsdb(struct drm_display_info *display, display->monitor_range.max_vfreq = vsdb->max_refresh_rate_hz; } +/** + * Get VRR range from HDMI VRR info in EDID. + * + * @conn: drm_connector with HDMI VRR info + */ +static void monitor_range_from_hdmi(struct drm_display_info *display) +{ + display->monitor_range.min_vfreq = display->hdmi.vrr_cap.vrr_min; + display->monitor_range.max_vfreq = display->hdmi.vrr_cap.vrr_max; +} + /* * Returns true if connector is capable of freesync * Optionally, can fetch the range from AMD vsdb @@ -13254,6 +13265,7 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, struct amdgpu_device *adev = drm_to_adev(connector->dev); struct amdgpu_hdmi_vsdb_info vsdb_info = {0}; struct amdgpu_hdmi_vsdb_info vsdb_did = {0}; + struct drm_hdmi_vrr_cap hdmi_vrr = {0}; struct dpcd_caps dpcd_caps = {0}; const struct edid *edid; bool freesync_capable = false; @@ -13289,6 +13301,7 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, /* Gather all data */ edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw() parse_amd_vsdb_cea(amdgpu_dm_connector, edid, &vsdb_info); + hdmi_vrr = connector->display_info.hdmi.vrr_cap; if (amdgpu_dm_connector->dc_link) { dpcd_caps = amdgpu_dm_connector->dc_link->dpcd_caps; @@ -13334,12 +13347,17 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, freesync_capable = copy_range_to_amdgpu_connector(connector); /* DP -> HDMI PCON */ - } else if (pcon_allowed && vsdb_info.freesync_supported) { - amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_PCON_ALLOWED; - amdgpu_dm_connector->pack_sdp_v1_3 = true; - amdgpu_dm_connector->vsdb_info = vsdb_info; + } else if (pcon_allowed) { + /* Prefer HDMI VRR */ + if (hdmi_vrr.supported && hdmi_vrr.vrr_max > 0) + monitor_range_from_hdmi(&connector->display_info); + else if (vsdb_info.freesync_supported) { + amdgpu_dm_connector->vsdb_info = vsdb_info; + monitor_range_from_vsdb(&connector->display_info, &vsdb_info); + } - monitor_range_from_vsdb(&connector->display_info, &vsdb_info); + amdgpu_dm_connector->pack_sdp_v1_3 = true; + amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_PCON_ALLOWED; freesync_capable = copy_range_to_amdgpu_connector(connector); } From d90fba5eebb1087e6cc0ffddce94f1796da8b20e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:18 +0000 Subject: [PATCH 11/19] [FROM-ML] drm/amd/display: Support HDMI VRRmax=0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] VRRmax=0 is a valid value and means that the upper bound is guared by the selected video mode. [How] In this context, saved vrr max is the max possible refresh rate ever. Try getting upper VRR bound from AMD vsdbif it exists or rely on the limitations of BRR in VTEM info frames. I found through testing, that TVs seem to reject VTEM when BRR is set to over 1023 Hz. Use this as the last resort VRRmax. 1023 is the max value for a 10-bit field as well. v2: - Update max BRR value to 1023 Signed-off-by: Tomasz Pakuła --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 23 +++++++++++++++---- .../amd/display/modules/inc/mod_info_packet.h | 2 ++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 2af7a9b72c5c6..567f0e25cd262 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -13207,14 +13207,27 @@ static void monitor_range_from_vsdb(struct drm_display_info *display, } /** - * Get VRR range from HDMI VRR info in EDID. + * Get VRR range from HDMI VRR info in EDID. If VRRmax == 0, + * try getting upper bound from AMD vsdb. * * @conn: drm_connector with HDMI VRR info + * @vsdb: AMD vsdb from CAE */ -static void monitor_range_from_hdmi(struct drm_display_info *display) +static void monitor_range_from_hdmi(struct drm_display_info *display, + const struct amdgpu_hdmi_vsdb_info *vsdb) { + u16 vrr_max = display->hdmi.vrr_cap.vrr_max; + + /* Try getting upper vrr bound from AMD vsdb */ + if (vrr_max == 0) + vrr_max = vsdb->max_refresh_rate_hz; + + /* Use max possible BRR value as a last resort */ + if (vrr_max == 0) + vrr_max = VTEM_BRR_MAX; + display->monitor_range.min_vfreq = display->hdmi.vrr_cap.vrr_min; - display->monitor_range.max_vfreq = display->hdmi.vrr_cap.vrr_max; + display->monitor_range.max_vfreq = vrr_max; } /* @@ -13349,8 +13362,8 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, /* DP -> HDMI PCON */ } else if (pcon_allowed) { /* Prefer HDMI VRR */ - if (hdmi_vrr.supported && hdmi_vrr.vrr_max > 0) - monitor_range_from_hdmi(&connector->display_info); + if (hdmi_vrr.supported) + monitor_range_from_hdmi(&connector->display_info, &vsdb_info); else if (vsdb_info.freesync_supported) { amdgpu_dm_connector->vsdb_info = vsdb_info; monitor_range_from_vsdb(&connector->display_info, &vsdb_info); diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h index 040aa14cb1ce3..c8f7d1b2951c4 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h @@ -33,6 +33,8 @@ struct dc_stream_state; struct dc_info_packet; struct mod_vrr_params; +#define VTEM_BRR_MAX 1023 + void mod_build_vsc_infopacket(const struct dc_stream_state *stream, struct dc_info_packet *info_packet, enum dc_color_space cs, From 4a8f620fda4561c134448848f9e21ddc11a0239a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:19 +0000 Subject: [PATCH 12/19] [FROM-ML] drm/amd/display: Build HDMI vsif in correct slot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] HDMI vsif was assigned to vsp_infopacket (FreeSync) field [How] Build HDMI vsif in the correct hfvsif_infopacket field Signed-off-by: Tomasz Pakuła --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 567f0e25cd262..94ec178531e89 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -7373,7 +7373,7 @@ create_stream_for_sink(struct drm_connector *connector, update_stream_signal(stream, sink); if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) - mod_build_hf_vsif_infopacket(stream, &stream->vsp_infopacket); + mod_build_hf_vsif_infopacket(stream, &stream->hfvsif_infopacket); if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT || stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST || From bd9c923c15c79a3453f29e1f593ac22c9fa707db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:20 +0000 Subject: [PATCH 13/19] [FROM-ML] drm/amd/display: Save HDMI gaming info to edid caps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] We need info about these features in parts of the driver where fishing for drm_connector struct is infeasible. [How] Add three new fields to dc_edid_caps and fill them if connected device is HDMI based on it's EDID Signed-off-by: Tomasz Pakuła --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 7 ++++++- drivers/gpu/drm/amd/display/dc/dc_types.h | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index 37747f87b55a7..6413f2a587d5f 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -137,7 +137,12 @@ enum dc_edid_status dm_helpers_parse_edid_caps( edid_caps->display_name, AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS); - edid_caps->edid_hdmi = connector->display_info.is_hdmi; + if (connector->display_info.is_hdmi) { + edid_caps->edid_hdmi = true; + edid_caps->allm = connector->display_info.hdmi.allm; + edid_caps->fva = connector->display_info.hdmi.vrr_cap.fva; + edid_caps->hdmi_vrr = connector->display_info.hdmi.vrr_cap.supported; + } if (edid_caps->edid_hdmi) populate_hdmi_info_from_connector(&connector->display_info.hdmi, edid_caps); diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h index f46039f642034..e9281e022a7c7 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_types.h @@ -210,9 +210,14 @@ struct dc_edid_caps { uint32_t max_tmds_clk_mhz; - /*HDMI 2.0 caps*/ + /* HDMI 2.0 caps */ bool lte_340mcsc_scramble; + /* HDMI 2.1 caps */ + bool allm; + bool fva; + bool hdmi_vrr; + bool edid_hdmi; bool hdr_supported; bool rr_capable; From 1269ebcd5910a5d2301f0faf8a9a3ee1b0fe7119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:21 +0000 Subject: [PATCH 14/19] [FROM-ML] drm/amd/display: Restore ALLM support in HDMI vsif MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] Support for triggering ALLM in modern TVs is missing. When HDMI vsif was added in 2019: commit 3c2381b92cba ("drm/amd/display: add support for VSIP info packet") it was improperly handeled as HDMI actually has two separate vsifs. The implementation was based on H14b-vsif and ALLM bit was messing it up because H14b-vsif doesn't support ALLM. It was later removed in: commit 75f77aafe281 ("drm/amd/display: Send H14b-VSIF specified in HDMI") ALLM is supported by hf-vsif (HDMI Forum) instead. [How] Add proper logic to construct either h14b-vsif or hf-vsif based on required capabilities. Currently, only ALLM from hf-vsif is supported. Turns out, hf-vsif is almost identical to h14b-vsif, BUT has additional two bytes of data after OUI. First byte is static and seems like a version supported by leftover define. Second byte consists of 3D and ALLM bits. Implement logic to offset 3D data if building hf-vsif. Signed-off-by: Tomasz Pakuła --- .../display/modules/info_packet/info_packet.c | 107 ++++++++++++------ 1 file changed, 71 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c index b50fb75236e93..9db483a21b7ff 100644 --- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c +++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c @@ -45,7 +45,10 @@ enum vsc_packet_revision { }; #define HDMI_INFOFRAME_TYPE_VENDOR 0x81 -#define HF_VSIF_VERSION 1 +#define HDMI_INFOFRAME_LENGTH_MASK 0x1F +#define HF_VSIF_VERSION 1 +#define HF_VSIF_3D_BIT 0 +#define HF_VSIF_ALLM_BIT 1 // VTEM Byte Offset #define VTEM_PB0 0 @@ -441,9 +444,29 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream, } } +static bool is_hdmi_vic_mode(const struct dc_stream_state *stream) +{ + if (stream->timing.hdmi_vic == 0) + return false; + + if (stream->timing.h_total < 3840 || + stream->timing.v_total < 2160) + return false; + + /* 3D/ALLM forces HDMI VIC -> CTA VIC translation */ + if (stream->view_format != VIEW_3D_FORMAT_NONE) + return false; + + if (stream->link->local_sink->edid_caps.allm) + return false; + + return true; +} + /** * mod_build_hf_vsif_infopacket - Prepare HDMI Vendor Specific info frame. * Follows HDMI Spec to build up Vendor Specific info frame + * Conforms to h14b-vsif or hf-vsif based on the capabilities * * @stream: contains data we may need to construct VSIF (i.e. timing_3d_format, etc.) * @info_packet: output structure where to store VSIF @@ -451,63 +474,75 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream, void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, struct dc_info_packet *info_packet) { - unsigned int length = 5; bool hdmi_vic_mode = false; + bool allm = false; + bool stereo = false; uint8_t checksum = 0; - uint32_t i = 0; + uint8_t offset = 0; + uint8_t i = 0; + uint8_t length = 5; + uint32_t oui = HDMI_IEEE_OUI; enum dc_timing_3d_format format; info_packet->valid = false; + format = stream->timing.timing_3d_format; if (stream->view_format == VIEW_3D_FORMAT_NONE) format = TIMING_3D_FORMAT_NONE; + stereo = format != TIMING_3D_FORMAT_NONE; + hdmi_vic_mode = is_hdmi_vic_mode(stream); - if (stream->timing.hdmi_vic != 0 - && stream->timing.h_total >= 3840 - && stream->timing.v_total >= 2160 - && format == TIMING_3D_FORMAT_NONE) - hdmi_vic_mode = true; - - if ((format == TIMING_3D_FORMAT_NONE) && !hdmi_vic_mode) + if (!stereo && !hdmi_vic_mode && !allm) return; - info_packet->sb[1] = 0x03; - info_packet->sb[2] = 0x0C; - info_packet->sb[3] = 0x00; + if (allm) + oui = HDMI_FORUM_IEEE_OUI; - if (format != TIMING_3D_FORMAT_NONE) - info_packet->sb[4] = (2 << 5); + info_packet->sb[1] = oui & 0xFF; + info_packet->sb[2] = (oui >> 8) & 0xFF; + info_packet->sb[3] = (oui >> 16) & 0xFF; - else if (hdmi_vic_mode) - info_packet->sb[4] = (1 << 5); + if (oui == HDMI_FORUM_IEEE_OUI) { + offset = 2; + length += 2; + info_packet->sb[4] = HF_VSIF_VERSION; + info_packet->sb[5] = stereo << HF_VSIF_3D_BIT; + info_packet->sb[5] |= allm << HF_VSIF_ALLM_BIT; + } - switch (format) { - case TIMING_3D_FORMAT_HW_FRAME_PACKING: - case TIMING_3D_FORMAT_SW_FRAME_PACKING: - info_packet->sb[5] = (0x0 << 4); - break; + if (stereo) { + info_packet->sb[4 + offset] = (2 << 5); - case TIMING_3D_FORMAT_SIDE_BY_SIDE: - case TIMING_3D_FORMAT_SBS_SW_PACKED: - info_packet->sb[5] = (0x8 << 4); - length = 6; - break; + switch (format) { + case TIMING_3D_FORMAT_HW_FRAME_PACKING: + case TIMING_3D_FORMAT_SW_FRAME_PACKING: + info_packet->sb[5 + offset] = (0x0 << 4); + break; - case TIMING_3D_FORMAT_TOP_AND_BOTTOM: - case TIMING_3D_FORMAT_TB_SW_PACKED: - info_packet->sb[5] = (0x6 << 4); - break; + case TIMING_3D_FORMAT_SIDE_BY_SIDE: + case TIMING_3D_FORMAT_SBS_SW_PACKED: + info_packet->sb[5 + offset] = (0x8 << 4); + ++length; + break; - default: - break; - } + case TIMING_3D_FORMAT_TOP_AND_BOTTOM: + case TIMING_3D_FORMAT_TB_SW_PACKED: + info_packet->sb[5 + offset] = (0x6 << 4); + break; - if (hdmi_vic_mode) + default: + break; + } + + /* Doesn't need the offset as it can't be used with hf-vsif */ + } else if (hdmi_vic_mode) { + info_packet->sb[4] = (1 << 5); info_packet->sb[5] = stream->timing.hdmi_vic; + } info_packet->hb0 = HDMI_INFOFRAME_TYPE_VENDOR; info_packet->hb1 = 0x01; - info_packet->hb2 = (uint8_t) (length); + info_packet->hb2 = length & HDMI_INFOFRAME_LENGTH_MASK; checksum += info_packet->hb0; checksum += info_packet->hb1; From 47b0301cfa1ae1c8f201b438cbf12e6a51bba545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:22 +0000 Subject: [PATCH 15/19] [FROM-ML] drm/amd/display: Trigger ALLM if it's available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] ALLM automatically puts TVs into low latency modes (gaming modes) which we basically always want for PC use, be it gaming, or using precise inputs like mice and keyboards. [How] Read the ALLM info from HDMI caps and use it to determine if ALLM should be indicated in HDMI Forum vsif. Additionally, make sure VIC modes are translated in case of ALLM active as VIC cannot be used in conjunction with hf-vsif. I learned this the hard way... Signed-off-by: Tomasz Pakuła --- drivers/gpu/drm/amd/display/dc/core/dc_resource.c | 4 +++- .../gpu/drm/amd/display/modules/info_packet/info_packet.c | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index 848c267ef11e7..4a7c9f810e35f 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -4504,6 +4504,7 @@ static void set_avi_info_frame( unsigned int rid = pipe_ctx->stream->timing.rid; unsigned int fr_ind = pipe_ctx->stream->timing.fr_index; enum dc_timing_3d_format format; + bool allm; if (stream->avi_infopacket.valid) { *info_packet = stream->avi_infopacket; @@ -4658,8 +4659,9 @@ static void set_avi_info_frame( if (pipe_ctx->stream->timing.hdmi_vic != 0) vic = 0; format = stream->timing.timing_3d_format; + allm = stream->link->local_sink->edid_caps.allm; /*todo, add 3DStereo support*/ - if (format != TIMING_3D_FORMAT_NONE) { + if ((format != TIMING_3D_FORMAT_NONE) || allm) { // Based on HDMI specs hdmi vic needs to be converted to cea vic when 3D is enabled switch (pipe_ctx->stream->timing.hdmi_vic) { case 1: diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c index 9db483a21b7ff..e362276bc3244 100644 --- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c +++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c @@ -486,9 +486,10 @@ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, info_packet->valid = false; - format = stream->timing.timing_3d_format; - if (stream->view_format == VIEW_3D_FORMAT_NONE) - format = TIMING_3D_FORMAT_NONE; + allm = stream->link->local_sink->edid_caps.allm; + format = stream->view_format == VIEW_3D_FORMAT_NONE ? + TIMING_3D_FORMAT_NONE : + stream->timing.timing_3d_format; stereo = format != TIMING_3D_FORMAT_NONE; hdmi_vic_mode = is_hdmi_vic_mode(stream); From fae154410e221c2c90c5fbd08a8fd0851ba760ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:23 +0000 Subject: [PATCH 16/19] [FROM-ML] drm/amd/display: Add parameter to control ALLM behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] Some users prefer to always manually control ALLM/Gaming mode while others might want it permanently forced on. [How] Since there isn't yet an API to control this, expose module parameter Changes in v3: - Include a fix for possible NULL pointer dereference by Peter Closes: https://github.com/CachyOS/linux-cachyos/issues/680 Co-developed-by: Peter Jung Signed-off-by: Peter Jung Signed-off-by: Tomasz Pakuła --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 14 +++++- .../gpu/drm/amd/display/dc/core/dc_resource.c | 7 +-- .../amd/display/modules/inc/mod_info_packet.h | 1 + .../display/modules/info_packet/info_packet.c | 44 ++++++++++++++++--- 5 files changed, 55 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index b20a06abb65dd..e14eb9b4eee13 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -273,6 +273,7 @@ extern int amdgpu_rebar; extern int amdgpu_wbrf; extern int amdgpu_user_queue; +extern uint amdgpu_allm_mode; extern uint amdgpu_hdmi_hpd_debounce_delay_ms; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 39387da8586b4..4b72f7f474dc5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -248,6 +248,7 @@ int amdgpu_umsch_mm_fwlog; int amdgpu_rebar = -1; /* auto */ int amdgpu_user_queue = -1; uint amdgpu_hdmi_hpd_debounce_delay_ms; +uint amdgpu_allm_mode = 1; DECLARE_DYNDBG_CLASSMAP(drm_debug_classes, DD_CLASS_TYPE_DISJOINT_BITS, 0, "DRM_UT_CORE", @@ -1124,7 +1125,7 @@ module_param_named(rebar, amdgpu_rebar, int, 0444); MODULE_PARM_DESC(user_queue, "Enable user queues (-1 = auto (default), 0 = disable, 1 = enable, 2 = enable UQs and disable KQs)"); module_param_named(user_queue, amdgpu_user_queue, int, 0444); -/* +/** * DOC: hdmi_hpd_debounce_delay_ms (uint) * HDMI HPD disconnect debounce delay in milliseconds. * @@ -1134,6 +1135,17 @@ module_param_named(user_queue, amdgpu_user_queue, int, 0444); MODULE_PARM_DESC(hdmi_hpd_debounce_delay_ms, "HDMI HPD disconnect debounce delay in milliseconds (0 to disable (by default), 1500 is common)"); module_param_named(hdmi_hpd_debounce_delay_ms, amdgpu_hdmi_hpd_debounce_delay_ms, uint, 0644); +/** + * DOC: allm_mode (int) + * Changes ALLM triggering mode (if sink supports ALLM). Possible values: + * + * - 0 = ALLM disabled + * - 1 = ALLM dynamically triggered based on VRR state / Game Content Type Hint + * - 2 = ALLM forced always on + */ +MODULE_PARM_DESC(allm_mode, "Changes ALLM trigger mode (0 = disable, 1 = enable (default), 2 = force enable)"); +module_param_named(allm_mode, amdgpu_allm_mode, uint, 0644); + /* These devices are not supported by amdgpu. * They are supported by the mach64, r128, radeon drivers */ diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index 4a7c9f810e35f..b779aac28dfa6 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -44,6 +44,7 @@ #include "clk_mgr.h" #include "dc_state_priv.h" #include "dc_stream_priv.h" +#include "modules/inc/mod_info_packet.h" #include "virtual/virtual_link_hwss.h" #include "link/hwss/link_hwss_dio.h" @@ -4503,8 +4504,6 @@ static void set_avi_info_frame( unsigned int vic = pipe_ctx->stream->timing.vic; unsigned int rid = pipe_ctx->stream->timing.rid; unsigned int fr_ind = pipe_ctx->stream->timing.fr_index; - enum dc_timing_3d_format format; - bool allm; if (stream->avi_infopacket.valid) { *info_packet = stream->avi_infopacket; @@ -4658,10 +4657,8 @@ static void set_avi_info_frame( ///VIC if (pipe_ctx->stream->timing.hdmi_vic != 0) vic = 0; - format = stream->timing.timing_3d_format; - allm = stream->link->local_sink->edid_caps.allm; /*todo, add 3DStereo support*/ - if ((format != TIMING_3D_FORMAT_NONE) || allm) { + if (!is_hdmi_vic_mode(pipe_ctx->stream)) { // Based on HDMI specs hdmi vic needs to be converted to cea vic when 3D is enabled switch (pipe_ctx->stream->timing.hdmi_vic) { case 1: diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h index c8f7d1b2951c4..871bd5422566b 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h @@ -35,6 +35,7 @@ struct mod_vrr_params; #define VTEM_BRR_MAX 1023 +bool is_hdmi_vic_mode(const struct dc_stream_state *stream); void mod_build_vsc_infopacket(const struct dc_stream_state *stream, struct dc_info_packet *info_packet, enum dc_color_space cs, diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c index e362276bc3244..c6e590726b7ca 100644 --- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c +++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c @@ -23,12 +23,13 @@ * */ -#include "mod_info_packet.h" +#include "amdgpu.h" #include "core_types.h" +#include "dc.h" #include "dc_types.h" -#include "mod_shared.h" #include "mod_freesync.h" -#include "dc.h" +#include "mod_info_packet.h" +#include "mod_shared.h" enum vsc_packet_revision { vsc_packet_undefined = 0, @@ -50,6 +51,12 @@ enum vsc_packet_revision { #define HF_VSIF_3D_BIT 0 #define HF_VSIF_ALLM_BIT 1 +enum allm_trigger_mode { + ALLM_MODE_DISABLED = 0, + ALLM_MODE_ENABLED_DYNAMIC = 1, + ALLM_MODE_ENABLED_FORCED = 2, +}; + // VTEM Byte Offset #define VTEM_PB0 0 #define VTEM_PB1 1 @@ -444,7 +451,32 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream, } } -static bool is_hdmi_vic_mode(const struct dc_stream_state *stream) +static bool is_hdmi_allm_mode(const struct dc_stream_state *stream) +{ + /* No local sink */ + if (!stream->link->local_sink) + return false; + + /* Sink doesn't expose ALLM support in edid */ + if (!stream->link->local_sink->edid_caps.allm) + return false; + + switch (amdgpu_allm_mode) { + case ALLM_MODE_DISABLED: + return false; + + case ALLM_MODE_ENABLED_DYNAMIC: + break; + + case ALLM_MODE_ENABLED_FORCED: + return true; + } + + return stream->content_type == DISPLAY_CONTENT_TYPE_GAME || + stream->vrr_active_variable; +} + +bool is_hdmi_vic_mode(const struct dc_stream_state *stream) { if (stream->timing.hdmi_vic == 0) return false; @@ -457,7 +489,7 @@ static bool is_hdmi_vic_mode(const struct dc_stream_state *stream) if (stream->view_format != VIEW_3D_FORMAT_NONE) return false; - if (stream->link->local_sink->edid_caps.allm) + if (is_hdmi_allm_mode(stream)) return false; return true; @@ -486,7 +518,7 @@ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, info_packet->valid = false; - allm = stream->link->local_sink->edid_caps.allm; + allm = is_hdmi_allm_mode(stream); format = stream->view_format == VIEW_3D_FORMAT_NONE ? TIMING_3D_FORMAT_NONE : stream->timing.timing_3d_format; From 9a24bba55ccabb3da829de4a16bd9c11ede1e441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:24 +0000 Subject: [PATCH 17/19] [FROM-ML] drm/amd/display: Reintroduce VTEM info frame MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] VTEM info fram building was removed back in: commit a9f54ce3c603 ("drm/amd/display: Refactoring VTEM"), but it's needed to support HDMI VRR signalling. [How] Build completely new and more robust functions to build out the VTEM infopacket. Many values are defined but could have added logic in the future, that's shy they are not static values but already value + bit position in it's byte. Reduced blanking detection was previously missing. Use possible hblank periods defined for RB1 (from CVT 1.2), RB2 and RB3 (from CVT 2.1). Changes in v3: - Use div_u64() instead of division operator Signed-off-by: Tomasz Pakuła --- .../amd/display/modules/inc/mod_info_packet.h | 4 + .../display/modules/info_packet/info_packet.c | 189 +++++++++++------- 2 files changed, 126 insertions(+), 67 deletions(-) diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h index 871bd5422566b..d88ad1d2b215a 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h @@ -44,6 +44,10 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream, void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, struct dc_info_packet *info_packet); +void mod_build_vtem_infopacket(const struct dc_stream_state *stream, + const struct mod_vrr_params *vrr, + struct dc_info_packet *infopacket); + enum adaptive_sync_type { ADAPTIVE_SYNC_TYPE_NONE = 0, ADAPTIVE_SYNC_TYPE_DP = 1, diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c index c6e590726b7ca..0945bc394924b 100644 --- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c +++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c @@ -45,6 +45,7 @@ enum vsc_packet_revision { vsc_packet_rev5 = 5, }; +#define HDMI_INFOFRAME_TYPE_EMP 0x7F #define HDMI_INFOFRAME_TYPE_VENDOR 0x81 #define HDMI_INFOFRAME_LENGTH_MASK 0x1F #define HF_VSIF_VERSION 1 @@ -57,73 +58,52 @@ enum allm_trigger_mode { ALLM_MODE_ENABLED_FORCED = 2, }; -// VTEM Byte Offset -#define VTEM_PB0 0 -#define VTEM_PB1 1 -#define VTEM_PB2 2 -#define VTEM_PB3 3 -#define VTEM_PB4 4 -#define VTEM_PB5 5 -#define VTEM_PB6 6 - -#define VTEM_MD0 7 -#define VTEM_MD1 8 -#define VTEM_MD2 9 -#define VTEM_MD3 10 - - -// VTEM Byte Masks -//PB0 -#define MASK_VTEM_PB0__RESERVED0 0x01 -#define MASK_VTEM_PB0__SYNC 0x02 -#define MASK_VTEM_PB0__VFR 0x04 -#define MASK_VTEM_PB0__AFR 0x08 -#define MASK_VTEM_PB0__DS_TYPE 0x30 - //0: Periodic pseudo-static EM Data Set - //1: Periodic dynamic EM Data Set - //2: Unique EM Data Set - //3: Reserved -#define MASK_VTEM_PB0__END 0x40 -#define MASK_VTEM_PB0__NEW 0x80 - -//PB1 -#define MASK_VTEM_PB1__RESERVED1 0xFF - -//PB2 -#define MASK_VTEM_PB2__ORGANIZATION_ID 0xFF - //0: This is a Vendor Specific EM Data Set - //1: This EM Data Set is defined by This Specification (HDMI 2.1 r102.clean) - //2: This EM Data Set is defined by CTA-861-G - //3: This EM Data Set is defined by VESA -//PB3 -#define MASK_VTEM_PB3__DATA_SET_TAG_MSB 0xFF -//PB4 -#define MASK_VTEM_PB4__DATA_SET_TAG_LSB 0xFF -//PB5 -#define MASK_VTEM_PB5__DATA_SET_LENGTH_MSB 0xFF -//PB6 -#define MASK_VTEM_PB6__DATA_SET_LENGTH_LSB 0xFF - - - -//PB7-27 (20 bytes): -//PB7 = MD0 -#define MASK_VTEM_MD0__VRR_EN 0x01 -#define MASK_VTEM_MD0__M_CONST 0x02 -#define MASK_VTEM_MD0__QMS_EN 0x04 -#define MASK_VTEM_MD0__RESERVED2 0x08 -#define MASK_VTEM_MD0__FVA_FACTOR_M1 0xF0 - -//MD1 -#define MASK_VTEM_MD1__BASE_VFRONT 0xFF - -//MD2 -#define MASK_VTEM_MD2__BASE_REFRESH_RATE_98 0x03 -#define MASK_VTEM_MD2__RB 0x04 -#define MASK_VTEM_MD2__NEXT_TFR 0xF8 - -//MD3 -#define MASK_VTEM_MD3__BASE_REFRESH_RATE_07 0xFF +#define VTEM_ORG_ID 1 +#define VTEM_DATA_SET_TAG 1 +#define VTEM_DATA_SET_LENGTH 4 + +#define VTEM_M_CONST 0 +#define VTEM_FVA_FACTOR 0 + +#define VTEM_BRR_MASK_UPPER 0x03 +#define VTEM_BRR_MASK_LOWER 0xFF + + +/* VTEM Byte Offset */ +#define VTEM_PB0 0 +#define VTEM_PB1 1 +#define VTEM_PB2 2 +#define VTEM_PB3 3 +#define VTEM_PB4 4 +#define VTEM_PB5 5 +#define VTEM_PB6 6 + +#define VTEM_MD0 7 +#define VTEM_MD1 8 +#define VTEM_MD2 9 +#define VTEM_MD3 10 + +/* Extended Metadata Packet */ +/* Header */ +#define EMP_LAST_BIT 6 +#define EMP_FIRST_BIT 7 +/* PB0 */ +#define EMP_SNC_BIT 1 +#define EMP_VFR_BIT 2 +#define EMP_AFR_BIT 3 +#define EMP_DST_BIT 4 +#define EMP_END_BIT 6 +#define EMP_NEW_BIT 7 +/* PB7 = MD0 */ +#define VTEM_VRR_BIT 0 +#define VTEM_M_CONST_BIT 1 +#define VTEM_FVA_BIT 4 +/* MD1 Base_Vfront */ +/* MD2 */ +#define VTEM_BRR_UPPER_BIT 0 +#define VTEM_RB_BIT 2 +/* MD3 BRR Lower */ + enum ColorimetryRGBDP { ColorimetryRGB_DP_sRGB = 0, @@ -589,6 +569,81 @@ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, info_packet->valid = true; } +static void build_vtem_infopacket_header(struct dc_info_packet *infopacket) +{ + uint8_t pb0 = 0; + + /* might need logic in the future */ + pb0 |= 0 << EMP_SNC_BIT; + pb0 |= 1 << EMP_VFR_BIT; + pb0 |= 0 << EMP_AFR_BIT; + pb0 |= 0 << EMP_DST_BIT; + pb0 |= 0 << EMP_END_BIT; + pb0 |= 1 << EMP_NEW_BIT; + + infopacket->hb0 = HDMI_INFOFRAME_TYPE_EMP; + infopacket->hb1 = (1 << EMP_FIRST_BIT) | (1 << EMP_LAST_BIT); + infopacket->hb2 = 0; // sequence + + infopacket->sb[VTEM_PB0] = pb0; + infopacket->sb[VTEM_PB2] = VTEM_ORG_ID; + infopacket->sb[VTEM_PB4] = VTEM_DATA_SET_TAG; + infopacket->sb[VTEM_PB6] = VTEM_DATA_SET_LENGTH; +} + +static void build_vtem_infopacket_data(const struct dc_stream_state *stream, + const struct mod_vrr_params *vrr, + struct dc_info_packet *infopacket) +{ + unsigned int hblank = 0; + unsigned int brr = 0; + bool vrr_active = false; + bool rb = false; + + vrr_active = vrr->state == VRR_STATE_ACTIVE_VARIABLE || + vrr->state == VRR_STATE_ACTIVE_FIXED; + + infopacket->sb[VTEM_MD0] = VTEM_M_CONST << VTEM_M_CONST_BIT; + infopacket->sb[VTEM_MD0] |= VTEM_FVA_FACTOR << VTEM_FVA_BIT; + infopacket->sb[VTEM_MD0] |= vrr_active << VTEM_VRR_BIT; + + infopacket->sb[VTEM_MD1] = 0; + infopacket->sb[VTEM_MD2] = 0; + infopacket->sb[VTEM_MD3] = 0; + + if (!vrr_active || is_hdmi_vic_mode(stream)) + return; + /* + * In accordance with CVT 1.2 and CVT 2.1: + * Reduced Blanking standard defines a fixed value of + * 160 for hblank, further reduced to 80 in RB2. RB3 uses + * fixed hblank of 80 pixels + up to 120 additional pixels + * in 8-pixel steps. + */ + hblank = stream->timing.h_total - stream->timing.h_addressable; + rb = (hblank >= 80 && hblank <= 200 && hblank % 8 == 0); + brr = div_u64(mod_freesync_calc_nominal_field_rate(stream), 1000000); + + if (brr > VTEM_BRR_MAX) { + infopacket->valid = false; + return; + } + + infopacket->sb[VTEM_MD1] = (uint8_t) stream->timing.v_front_porch; + infopacket->sb[VTEM_MD2] = rb << VTEM_RB_BIT; + infopacket->sb[VTEM_MD2] |= (brr >> 8) & VTEM_BRR_MASK_UPPER; + infopacket->sb[VTEM_MD3] = brr & VTEM_BRR_MASK_LOWER; +} + +void mod_build_vtem_infopacket(const struct dc_stream_state *stream, + const struct mod_vrr_params *vrr, + struct dc_info_packet *infopacket) +{ + infopacket->valid = true; + build_vtem_infopacket_header(infopacket); + build_vtem_infopacket_data(stream, vrr, infopacket); +} + void mod_build_adaptive_sync_infopacket(const struct dc_stream_state *stream, enum adaptive_sync_type asType, const struct AS_Df_params *param, From fd09ba376c8c9cd405782461ca34fffddcb55d2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:25 +0000 Subject: [PATCH 18/19] [FROM-ML] drm/amd/display: Enable HDMI VRR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] We'd like to expose VRR functionality to end user if HDMI sink is advertising it's support. [How] VTEM info frame is used to signal HDMI sink that VRR is active. Use VTEM info packet as vrr_infopacket Signed-off-by: Tomasz Pakuła --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 15 ++++++++++++--- .../drm/amd/display/modules/freesync/freesync.c | 4 ++++ .../drm/amd/display/modules/inc/mod_info_packet.h | 1 + .../amd/display/modules/info_packet/info_packet.c | 1 + 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 94ec178531e89..59e88bb9a1415 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -9610,7 +9610,10 @@ static void update_freesync_state_on_stream( aconn = (struct amdgpu_dm_connector *)new_stream->dm_stream_context; - if (aconn && (aconn->as_type == ADAPTIVE_SYNC_TYPE_PCON_ALLOWED || + if (aconn && aconn->as_type == ADAPTIVE_SYNC_TYPE_HDMI) + packet_type = PACKET_TYPE_VTEM; + + else if (aconn && (aconn->as_type == ADAPTIVE_SYNC_TYPE_PCON_ALLOWED || aconn->vsdb_info.replay_mode)) { pack_sdp_v1_3 = aconn->pack_sdp_v1_3; @@ -13355,8 +13358,14 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, } /* HDMI */ - } else if (sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A && vsdb_info.freesync_supported) { - monitor_range_from_vsdb(&connector->display_info, &vsdb_info); + } else if (sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) { + /* Prefer HDMI VRR */ + if (hdmi_vrr.supported) { + amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_HDMI; + monitor_range_from_hdmi(&connector->display_info, &vsdb_info); + } else if (vsdb_info.freesync_supported) + monitor_range_from_vsdb(&connector->display_info, &vsdb_info); + freesync_capable = copy_range_to_amdgpu_connector(connector); /* DP -> HDMI PCON */ diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c index 1aae46d703ba0..db197cf048e1b 100644 --- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c @@ -27,6 +27,7 @@ #include "dc.h" #include "mod_freesync.h" #include "core_types.h" +#include "mod_info_packet.h" #define MOD_FREESYNC_MAX_CONCURRENT_STREAMS 32 @@ -955,6 +956,9 @@ void mod_freesync_build_vrr_infopacket(struct mod_freesync *mod_freesync, return; switch (packet_type) { + case PACKET_TYPE_VTEM: + mod_build_vtem_infopacket(stream, vrr, infopacket); + break; case PACKET_TYPE_FS_V3: build_vrr_infopacket_v3(stream->signal, vrr, app_tf, infopacket, stream->freesync_on_desktop); break; diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h index d88ad1d2b215a..6383966d01c5a 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h @@ -54,6 +54,7 @@ enum adaptive_sync_type { ADAPTIVE_SYNC_TYPE_PCON_ALLOWED = 2, ADAPTIVE_SYNC_TYPE_PCON_NOT_ALLOWED = 3, ADAPTIVE_SYNC_TYPE_EDP = 4, + ADAPTIVE_SYNC_TYPE_HDMI = 5, }; enum adaptive_sync_sdp_version { diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c index 0945bc394924b..5c81c5aa4a065 100644 --- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c +++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c @@ -664,6 +664,7 @@ void mod_build_adaptive_sync_infopacket(const struct dc_stream_state *stream, break; case ADAPTIVE_SYNC_TYPE_NONE: case ADAPTIVE_SYNC_TYPE_PCON_NOT_ALLOWED: + case ADAPTIVE_SYNC_TYPE_HDMI: default: break; } From b252e033d2dbba26bc93b883b3ddca791555067b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 3 Feb 2026 18:56:26 +0000 Subject: [PATCH 19/19] [FROM-ML] drm/amd/display: Add HDMI VRR desktop mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] Many TVs and other HDMI sinks suffer from blanking and possibly other glitches when VRR is toggled. With FreeSync present on such sinks, they behave like the signal is always variable, even in fixed refresh rate situations. [How] Keep HDMI VRR toggled if it's supported and not explicitly disabled. Additionnally, add module parameter which allows users to configure HDMI VRR triggering to only happen when the signal is truly asking for variable state. This is useful if end user has a TV that automatically toggles ALLM/Game mode when VRR is active and such user doesn't want gaming mode in normal desktop usage. Signed-off-by: Tomasz Pakuła --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 13 +++++++++++++ .../amd/display/modules/info_packet/info_packet.c | 9 +++++++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index e14eb9b4eee13..8eb4cf4df0836 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -274,6 +274,7 @@ extern int amdgpu_rebar; extern int amdgpu_wbrf; extern int amdgpu_user_queue; extern uint amdgpu_allm_mode; +extern bool amdgpu_hdmi_vrr_desktop_mode; extern uint amdgpu_hdmi_hpd_debounce_delay_ms; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 4b72f7f474dc5..ac993e64acaad 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -249,6 +249,7 @@ int amdgpu_rebar = -1; /* auto */ int amdgpu_user_queue = -1; uint amdgpu_hdmi_hpd_debounce_delay_ms; uint amdgpu_allm_mode = 1; +bool amdgpu_hdmi_vrr_desktop_mode = true; DECLARE_DYNDBG_CLASSMAP(drm_debug_classes, DD_CLASS_TYPE_DISJOINT_BITS, 0, "DRM_UT_CORE", @@ -1146,6 +1147,18 @@ module_param_named(hdmi_hpd_debounce_delay_ms, amdgpu_hdmi_hpd_debounce_delay_ms MODULE_PARM_DESC(allm_mode, "Changes ALLM trigger mode (0 = disable, 1 = enable (default), 2 = force enable)"); module_param_named(allm_mode, amdgpu_allm_mode, uint, 0644); +/** + * DOC: hdmi_vrr_on_dekstop (bool) + * Enables FreeSync behavior mimicking by keeping HDMI VRR signalling active in + * fixed refresh rate conditions like normal desktop work/web browsing. + * Possible values: + * + * - false = HDMI VRR is only enabled if refresh rate is truly variable + * - true = Mimics FreeSync behavior and keeps HDMI VRR always active + */ +MODULE_PARM_DESC(hdmi_vrr_desktop_mode, "Changes HDMI VRR desktop mode (false = disable, true = enable (default))"); +module_param_named(hdmi_vrr_desktop_mode, amdgpu_hdmi_vrr_desktop_mode, bool, 0644); + /* These devices are not supported by amdgpu. * They are supported by the mach64, r128, radeon drivers */ diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c index 5c81c5aa4a065..ffa629f0c81d4 100644 --- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c +++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c @@ -600,8 +600,13 @@ static void build_vtem_infopacket_data(const struct dc_stream_state *stream, bool vrr_active = false; bool rb = false; - vrr_active = vrr->state == VRR_STATE_ACTIVE_VARIABLE || - vrr->state == VRR_STATE_ACTIVE_FIXED; + if (amdgpu_hdmi_vrr_desktop_mode) { + vrr_active = vrr->state != VRR_STATE_UNSUPPORTED && + vrr->state != VRR_STATE_DISABLED; + } else { + vrr_active = vrr->state == VRR_STATE_ACTIVE_VARIABLE || + vrr->state == VRR_STATE_ACTIVE_FIXED; + } infopacket->sb[VTEM_MD0] = VTEM_M_CONST << VTEM_M_CONST_BIT; infopacket->sb[VTEM_MD0] |= VTEM_FVA_FACTOR << VTEM_FVA_BIT;