From: Cooper Morgan <ext-github@cwooper.dev>
To: amd-gfx@lists.freedesktop.org
Cc: "Harry Wentland" <harry.wentland@amd.com>,
"Leo Li" <sunpeng.li@amd.com>,
"Rodrigo Siqueira" <siqueira@igalia.com>,
"Alex Deucher" <alexander.deucher@amd.com>,
"Christian König" <christian.koenig@amd.com>,
"David Airlie" <airlied@gmail.com>,
"Simona Vetter" <simona@ffwll.ch>,
dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org
Subject: [PATCH 1/2] drm/amd/display: Saturate refresh rates in v1/v2 FreeSync infopackets
Date: Fri, 30 Oct 2026 21:18:25 -0700 [thread overview]
Message-ID: <20261031041827.162730-2-ext-github@cwooper.dev> (raw)
In-Reply-To: <20261031041827.162730-1-ext-github@cwooper.dev>
The v1 and v2 FreeSync infopackets carry the minimum and maximum
refresh rate in one byte each, and build_vrr_infopacket_data_v1() casts
the rate in Hz to unsigned char. On a sink whose range goes above
255 Hz the value wraps: a 320 Hz mode is advertised as a 64 Hz maximum.
Sinks that treat the field as authoritative misbehave once the wrapped
value lands inside their range. An MSI MAG 271QPX32 (48-320 Hz) over
native DP looks like a ~720p image upscaled to the full screen at 320 Hz
and runs its self-test pattern at 305-315 Hz, while 300 Hz (wrapping to
44 Hz, below its minimum) is fine. Capping the EDID range at 255 Hz, so
the packet says 255, gives a correct picture at 320 Hz.
Saturate the fields at 255 Hz instead of wrapping.
Capping v1 at 255 as a fallback was suggested by oystersuki in the linked
issue.
Fixes: 98e6436d3af5 ("drm/amd/display: Refactor FreeSync module")
Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/5988
Cc: stable@vger.kernel.org
Signed-off-by: Cooper Morgan <ext-github@cwooper.dev>
---
.../amdgpu_dm/tests/amdgpu_dm_freesync_test.c | 57 +++++++++++++++++++
.../amd/display/modules/freesync/freesync.c | 12 +++-
2 files changed, 66 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_freesync_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_freesync_test.c
index 710cdc7f4..b603516e2 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_freesync_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_freesync_test.c
@@ -664,6 +664,62 @@ static void dm_test_update_freesync_state_pcon_default(struct kunit *test)
dm_test_update_freesync_state_pcon_version(test, 0);
}
+/*
+ * Build the VRR infopacket for a non-PCON, non-Replay sink with VRR inactive
+ * over 48Hz to @mode_hz, the max that mod_freesync_build_vrr_params() clamps
+ * to the mode's rate.
+ */
+static struct dc_info_packet
+dm_test_freesync_packet(struct kunit *test, enum signal_type signal,
+ u8 vsdb_version, int sink_max_hz, int mode_hz)
+{
+ struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
+ struct dc_stream_state *stream = dm_kunit_alloc_stream(test, link);
+ struct amdgpu_dm_connector *aconnector;
+ struct mod_vrr_params *vrr;
+ struct amdgpu_crtc *acrtc;
+ struct dm_crtc_state crtc_state = { 0 };
+
+ acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, acrtc);
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+ crtc_state.base.crtc = &acrtc->base;
+ stream->timing.h_total = 2200;
+ stream->timing.v_total = 1125;
+ stream->signal = signal;
+ stream->dm_stream_context = aconnector;
+ aconnector->vsdb_info.amd_vsdb_version = vsdb_version;
+ aconnector->max_vfreq = sink_max_hz;
+ vrr = &acrtc->dm_irq_params.vrr_params;
+ vrr->send_info_frame = true;
+ vrr->state = VRR_STATE_INACTIVE;
+ vrr->min_refresh_in_uhz = 48000000;
+ vrr->max_refresh_in_uhz = mode_hz * 1000000;
+ adev->dm.adev = adev;
+
+ amdgpu_dm_update_freesync_state_on_stream(&adev->dm, &crtc_state, stream, NULL, 0);
+
+ return crtc_state.vrr_infopacket;
+}
+
+/**
+ * dm_test_update_freesync_state_dp_v2_saturates - Test v1 saturates above 255Hz
+ * @test: The KUnit test context
+ *
+ * A VSDB v2 sink keeps the v1 packet, which cannot carry 320Hz.
+ */
+static void dm_test_update_freesync_state_dp_v2_saturates(struct kunit *test)
+{
+ struct dc_info_packet p =
+ dm_test_freesync_packet(test, SIGNAL_TYPE_DISPLAY_PORT, 2, 320, 320);
+
+ KUNIT_EXPECT_EQ(test, p.hb3, 0x04);
+ KUNIT_EXPECT_EQ(test, p.sb[7], 255);
+ KUNIT_EXPECT_EQ(test, p.sb[8], 255);
+}
+
/* Tests for amdgpu_dm_update_stream_irq_parameters() */
/**
@@ -900,6 +956,7 @@ static struct kunit_case amdgpu_dm_freesync_tests[] = {
KUNIT_CASE(dm_test_update_freesync_state_pcon_v2),
KUNIT_CASE(dm_test_update_freesync_state_pcon_v3),
KUNIT_CASE(dm_test_update_freesync_state_pcon_default),
+ KUNIT_CASE(dm_test_update_freesync_state_dp_v2_saturates),
/* amdgpu_dm_update_stream_irq_parameters */
KUNIT_CASE(dm_test_update_stream_irq_parameters_no_stream),
KUNIT_CASE(dm_test_update_stream_irq_parameters_invalid_timing),
diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c
index 4bd060e30..af7054668 100644
--- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c
+++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c
@@ -571,6 +571,12 @@ static bool vrr_settings_require_update(struct core_freesync *core_freesync,
return false;
}
+/* v1/v2 packets hold each rate in one byte; saturate instead of wrapping. */
+static unsigned char vrr_refresh_hz_u8(unsigned int refresh_in_uhz)
+{
+ return min((refresh_in_uhz + 500000) / 1000000, 255U);
+}
+
static void build_vrr_infopacket_data_v1(const struct mod_vrr_params *vrr,
struct dc_info_packet *infopacket,
bool freesync_on_desktop)
@@ -614,15 +620,15 @@ static void build_vrr_infopacket_data_v1(const struct mod_vrr_params *vrr,
/* PB7 = FreeSync Minimum refresh rate (Hz) */
if (vrr->state == VRR_STATE_ACTIVE_VARIABLE ||
vrr->state == VRR_STATE_ACTIVE_FIXED) {
- infopacket->sb[7] = (unsigned char)((vrr->min_refresh_in_uhz + 500000) / 1000000);
+ infopacket->sb[7] = vrr_refresh_hz_u8(vrr->min_refresh_in_uhz);
} else {
- infopacket->sb[7] = (unsigned char)((vrr->max_refresh_in_uhz + 500000) / 1000000);
+ infopacket->sb[7] = vrr_refresh_hz_u8(vrr->max_refresh_in_uhz);
}
/* PB8 = FreeSync Maximum refresh rate (Hz)
* Note: We should never go above the field rate of the mode timing set.
*/
- infopacket->sb[8] = (unsigned char)((vrr->max_refresh_in_uhz + 500000) / 1000000);
+ infopacket->sb[8] = vrr_refresh_hz_u8(vrr->max_refresh_in_uhz);
}
static void build_vrr_infopacket_data_v3(const struct mod_vrr_params *vrr,
--
2.56.0
next prev parent reply other threads:[~2026-10-31 4:19 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-31 4:18 [PATCH 0/2] drm/amd/display: Fix FreeSync infopacket refresh rates above 255 Hz Cooper Morgan
2026-10-31 4:18 ` Cooper Morgan [this message]
2026-10-31 4:18 ` [PATCH 2/2] drm/amd/display: Use FreeSync v3 infopacket for sinks " Cooper Morgan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261031041827.162730-2-ext-github@cwooper.dev \
--to=ext-github@cwooper.dev \
--cc=airlied@gmail.com \
--cc=alexander.deucher@amd.com \
--cc=amd-gfx@lists.freedesktop.org \
--cc=christian.koenig@amd.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=harry.wentland@amd.com \
--cc=linux-kernel@vger.kernel.org \
--cc=simona@ffwll.ch \
--cc=siqueira@igalia.com \
--cc=sunpeng.li@amd.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®