[PATCH 26/34] drm/amd/display: Force FreeSync range minimum on quirky panels

Tom Chung <[email protected]> Wed, 5 Aug 2026 14:36:38 +0800
Newsgroups org.freedesktop.lists.amd-gfx
Message-ID <[email protected]>
From: Fangzhi Zuo <[email protected]>

[why]
Some panels advertise a FreeSync range minimum that does not operate
reliably at the low end:
- X34GS (EDID panel id ACR 0x08AF) reports a minimum that
  can flicker or drop out when the refresh rate approaches the reported
  minimum.
- 34w-30 (EDID panel id LEN 0x66F1) reports a 48 Hz minimum over
  DisplayPort, but the panel fails to light up at that minimum, resulting
  in a black screen. Forcing the minimum to 60 Hz avoids the black screen
  and lets the display light up normally.

[how]
- Add a force_freesync_min_hz field to struct dc_panel_patch.
- Add apply_edid_quirks() cases keyed on the affected panel ids that set
  force_freesync_min_hz (55 Hz for the X34GS, 60 Hz for the G34w-30).
- In amdgpu_dm_update_freesync_caps(), when the quirk is set and the sink
  is otherwise FreeSync capable, clamp the reported VRR range minimum
  (min_vfreq and monitor_range.min_vfreq) to the quirked value.
- Add a KUnit test covering the new quirk.

Reviewed-by: George Zhang <[email protected]>
Signed-off-by: Fangzhi Zuo <[email protected]>
Signed-off-by: Tom Chung <[email protected]>
---
 .../display/amdgpu_dm/amdgpu_dm_connector.c    | 12 ++++++++++++
 .../amd/display/amdgpu_dm/amdgpu_dm_helpers.c  | 18 ++++++++++++++++++
 .../amdgpu_dm/tests/amdgpu_dm_helpers_test.c   | 17 +++++++++++++++++
 drivers/gpu/drm/amd/display/dc/dc_types.h      |  1 +
 4 files changed, 48 insertions(+)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
index 4304520d2484..2cc3734c992b 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
@@ -3880,6 +3880,18 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
 		}
 	}
 
+	/*
+	 * Apply per-monitor FreeSync range quirks. Some panels report a
+	 * VRR minimum that does not operate reliably; force it when the
+	 * monitor is quirked (see apply_edid_quirks()).
+	 */
+	if (sink->edid_caps.panel_patch.force_freesync_min_hz && freesync_capable) {
+		amdgpu_dm_connector->min_vfreq =
+			sink->edid_caps.panel_patch.force_freesync_min_hz;
+		connector->display_info.monitor_range.min_vfreq =
+			amdgpu_dm_connector->min_vfreq;
+	}
+
 	/* Handle MCCS */
 	if (do_mccs)
 		dm_helpers_read_mccs_caps(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink);
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 d451082552e8..b05455498cbf 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
@@ -147,6 +147,24 @@ STATIC_IFN_KUNIT void apply_edid_quirks(struct dc_link *link, struct edid *edid,
 		drm_dbg_driver(dev, "Hiding secondary tile on panel id %X\n", panel_id);
 		edid_caps->panel_patch.disable_second_tile = true;
 		break;
+	/*
+	 * Workaround for the Acer Predator X34GS which reports a FreeSync
+	 * minimum refresh rate that does not operate reliably. Force the
+	 * FreeSync range minimum to 55 Hz.
+	 */
+	case drm_edid_encode_panel_id('A', 'C', 'R', 0x08AF):
+		drm_dbg_driver(dev, "Force FreeSync min to 55 Hz on panel id %X\n", panel_id);
+		edid_caps->panel_patch.force_freesync_min_hz = 55;
+		break;
+	/*
+	 * Workaround for the Lenovo G34w-30 which reports a FreeSync
+	 * minimum refresh rate (48 Hz) that fails to light up over DP.
+	 * Force the FreeSync range minimum to 60 Hz.
+	 */
+	case drm_edid_encode_panel_id('L', 'E', 'N', 0x66F1):
+		drm_dbg_driver(dev, "Force FreeSync min to 60 Hz on panel id %X\n", panel_id);
+		edid_caps->panel_patch.force_freesync_min_hz = 60;
+		break;
 	default:
 		return;
 	}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c
index 058e1ad15dfe..5de697ca0fcc 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c
@@ -186,6 +186,22 @@ static void dm_test_apply_edid_quirks_skip_phy_ssc(struct kunit *test)
 	KUNIT_EXPECT_TRUE(test, link->wa_flags.skip_phy_ssc_reduction);
 }
 
+/**
+ * dm_test_apply_edid_quirks_force_freesync_min - Test ACR 0x08AF FreeSync min quirk
+ * @test: The KUnit test context
+ */
+static void dm_test_apply_edid_quirks_force_freesync_min(struct kunit *test)
+{
+	struct dc_edid_caps edid_caps = {0};
+	struct dc_link *link = dm_test_quirk_link(test);
+	struct edid *edid = dm_test_edid_with_panel_id(test,
+			drm_edid_encode_panel_id('A', 'C', 'R', 0x08AF));
+
+	apply_edid_quirks(link, edid, &edid_caps);
+
+	KUNIT_EXPECT_EQ(test, edid_caps.panel_patch.force_freesync_min_hz, 55U);
+}
+
 /**
  * dm_test_apply_edid_quirks_unknown_noop - Test unknown panel id is a no-op
  * @test: The KUnit test context
@@ -3724,6 +3740,7 @@ static struct kunit_case amdgpu_dm_helpers_test_cases[] = {
 	KUNIT_CASE(dm_test_apply_edid_quirks_remove_sink_ext_caps),
 	KUNIT_CASE(dm_test_apply_edid_quirks_disable_colorimetry),
 	KUNIT_CASE(dm_test_apply_edid_quirks_skip_phy_ssc),
+	KUNIT_CASE(dm_test_apply_edid_quirks_force_freesync_min),
 	KUNIT_CASE(dm_test_apply_edid_quirks_unknown_noop),
 	/* dm_helpers_parse_edid_caps */
 	KUNIT_CASE(dm_test_parse_edid_caps_null_edid),
diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h
index 7da673111ed9..64b4adee36c4 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_types.h
@@ -194,6 +194,7 @@ struct dc_panel_patch {
 	bool oled_optimize_display_on;
 	unsigned int force_mst_blocked_discovery;
 	unsigned int wait_after_dpcd_poweroff_ms;
+	unsigned int force_freesync_min_hz;
 };
 
 /**
-- 
2.43.0