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,
oystersuki <oss@oysterdaisuki.com>
Subject: [PATCH v2 1/2] drm/amd/display: Saturate refresh rates in v1/v2 FreeSync infopackets
Date: Thu, 12 Nov 2026 17:05:27 -0800 [thread overview]
Message-ID: <20261113010529.11514-2-ext-github@cwooper.dev> (raw)
In-Reply-To: <20261113010529.11514-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. 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.
Saturate the fields at 255 Hz instead of wrapping. With this patch
alone, the MSI shows a correct picture at 320 Hz and an LG 27GX700A-B
shows every frame at 280 Hz.
Fixes: 98e6436d3af5 ("drm/amd/display: Refactor FreeSync module")
Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/5988
Cc: stable@vger.kernel.org
Suggested-by: oystersuki <oss@oysterdaisuki.com>
Signed-off-by: Cooper Morgan <ext-github@cwooper.dev>
Tested-by: oystersuki <oss@oysterdaisuki.com>
Reviewed-by: Harry Wentland <harry.wentland@amd.com>
---
.../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 710cdc7f40bf..b603516e2244 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 f154a619b533..21165855cb92 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-13 1:06 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 1:05 [PATCH v2 0/2] drm/amd/display: Fix FreeSync infopacket refresh rates above 255 Hz Cooper Morgan
2026-11-13 1:05 ` Cooper Morgan [this message]
2026-11-13 1:05 ` [PATCH v2 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=20261113010529.11514-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=oss@oysterdaisuki.com \
--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.