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-11-01 11:58 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.