From: Fangzhi Zuo <[email protected]>
[Why & How]
A zero EDID FreeSync VCP code means that no dynamic FreeSync on/off
control is available from the monitor OSD; it does not mean that the
sink lacks FreeSync capability. Do not clear freesync_capable solely
because freesync_vcp_code is zero; keep capability based on the parsed
FreeSync range while validating VRR timing per CRTC.
Some display modes cannot represent the sink's advertised maximum
FreeSync refresh without reducing the mode's nominal vertical total.
The FreeSync module clamps such a calculated vtotal back to nominal
while the stream can remain FreeSync-capable, leaving no valid timing
window for the requested maximum refresh. This affects low-clock
timings such as 720x576p50.
Compute the vtotal required at max_vfreq from each mode's pixel clock
and htotal, using ceiling division. Mark VRR unsupported when this
value is below the mode's nominal vtotal. If timing or range inputs
are incomplete, preserve the existing capability decision.
Add KUnit coverage for the invalid 720x576 timing and a valid 4K60
timing.
Fixes: 6f742bc837f1 ("drm/amd/display: Restore FreeSync VCP code check for
HDMI/PCON sinks")
Reviewed-by: Roman Li <[email protected]>
Signed-off-by: Fangzhi Zuo <[email protected]>
Signed-off-by: Ray Wu <[email protected]>
---
.../display/amdgpu_dm/amdgpu_dm_connector.c | 6 ---
.../display/amdgpu_dm/amdgpu_dm_freesync.c | 26 ++++++++++-
.../tests/amdgpu_dm_connector_test.c | 8 ++--
.../amdgpu_dm/tests/amdgpu_dm_freesync_test.c | 45 +++++++++++++++++--
4 files changed, 70 insertions(+), 15 deletions(-)
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 428a5f9377c6..224e8c6121c4 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
@@ -4018,12 +4018,6 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector
*connector,
amdgpu_dm_connector->min_vfreq;
}
- /* HDMI/PCON without an EDID FreeSync VCP code is not FreeSync-capable.
*/
- if ((sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A ||
- as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) &&
- !sink->edid_caps.freesync_vcp_code)
- freesync_capable = false;
-
/* 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_freesync.c
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_freesync.c
index ab9cfbce8d73..b65af2cc1fb7 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_freesync.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_freesync.c
@@ -112,6 +112,26 @@ void amdgpu_dm_reset_freesync_config_for_crtc(
}
EXPORT_IF_KUNIT(amdgpu_dm_reset_freesync_config_for_crtc);
+static bool amdgpu_dm_is_vrr_timing_valid(const struct drm_display_mode *mode,
+ int max_vfreq)
+{
+ u64 numerator, denominator, vtotal_at_max_refresh;
+
+ /*
+ * Leave the existing decision unchanged without complete timing data;
+ * otherwise, the computed vtotal must not be below the nominal vtotal.
+ */
+ if (!mode->clock || !mode->htotal || !mode->vtotal || max_vfreq <= 0)
+ return true;
+
+ numerator = (u64)mode->clock * 1000;
+ denominator = (u64)mode->htotal * max_vfreq;
+ vtotal_at_max_refresh =
+ div64_u64(numerator + denominator - 1, denominator);
+
+ return vtotal_at_max_refresh >= mode->vtotal;
+}
+
void amdgpu_dm_get_freesync_config_for_crtc(
struct dm_crtc_state *new_crtc_state,
struct dm_connector_state *new_con_state)
@@ -121,15 +141,19 @@ void amdgpu_dm_get_freesync_config_for_crtc(
struct drm_display_mode *mode = &new_crtc_state->base.mode;
int vrefresh = drm_mode_vrefresh(mode);
bool fs_vid_mode = false;
+ bool vrr_timing_valid;
if (new_con_state->base.connector->connector_type ==
DRM_MODE_CONNECTOR_WRITEBACK)
return;
aconnector = to_amdgpu_dm_connector(new_con_state->base.connector);
+ vrr_timing_valid = amdgpu_dm_is_vrr_timing_valid(mode,
aconnector->max_vfreq);
+
new_crtc_state->vrr_supported = new_con_state->freesync_capable &&
vrefresh >= aconnector->min_vfreq &&
- vrefresh <= aconnector->max_vfreq;
+ vrefresh <= aconnector->max_vfreq &&
+ vrr_timing_valid;
if (new_crtc_state->vrr_supported) {
new_crtc_state->stream->ignore_msa_timing_param = true;
diff --git
a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
index de3a0b9c961a..d291ae40ca1d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
@@ -9572,7 +9572,7 @@ static void dm_test_fs_caps_hdmi_vsdb(struct kunit *test)
KUNIT_EXPECT_EQ(test, ctx->aconnector->max_vfreq, 60);
KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.min_vfreq,
24);
KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.max_vfreq,
60);
- KUNIT_EXPECT_FALSE(test, dm_test_fs_caps_capable(ctx));
+ KUNIT_EXPECT_TRUE(test, dm_test_fs_caps_capable(ctx));
}
/**
@@ -9599,8 +9599,8 @@ static void dm_test_fs_caps_hdmi_no_vsdb(struct kunit
*test)
* @test: The KUnit test context
*
* A whitelisted protocol converter re-parses the AMD VSDB, enables SDP v1.3
- * packing and adopts the converter adaptive sync type. Without an EDID
- * FreeSync VCP code, the connector is not marked FreeSync-capable.
+ * packing and adopts the converter adaptive sync type. The connector remains
+ * FreeSync-capable when the EDID has no FreeSync VCP code.
*/
static void dm_test_fs_caps_pcon_whitelist(struct kunit *test)
{
@@ -9622,7 +9622,7 @@ static void dm_test_fs_caps_pcon_whitelist(struct kunit
*test)
(int)FREESYNC_TYPE_PCON_IN_WHITELIST);
KUNIT_EXPECT_EQ(test, ctx->aconnector->min_vfreq, 24);
KUNIT_EXPECT_EQ(test, ctx->aconnector->max_vfreq, 60);
- KUNIT_EXPECT_FALSE(test, dm_test_fs_caps_capable(ctx));
+ KUNIT_EXPECT_TRUE(test, dm_test_fs_caps_capable(ctx));
}
/**
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 5ad50bc31613..547a543b9f5d 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
@@ -340,6 +340,42 @@ static void dm_test_freesync_config_out_of_range(struct
kunit *test)
(int)VRR_STATE_UNSUPPORTED);
}
+/**
+ * dm_test_freesync_config_invalid_timing - Reject an invalid VRR timing
+ * @test: The KUnit test context
+ */
+static void dm_test_freesync_config_invalid_timing(struct kunit *test)
+{
+ struct dm_test_freesync_ctx *ctx = dm_test_freesync_ctx_alloc(test);
+
+ ctx->conn_state->freesync_capable = true;
+ ctx->aconnector->min_vfreq = 40;
+ ctx->aconnector->max_vfreq = 60;
+ ctx->crtc_state->base.mode.clock = 27000;
+ ctx->crtc_state->base.mode.hdisplay = 720;
+ ctx->crtc_state->base.mode.vdisplay = 576;
+ ctx->crtc_state->base.mode.htotal = 864;
+ ctx->crtc_state->base.mode.vtotal = 625;
+
+ amdgpu_dm_get_freesync_config_for_crtc(ctx->crtc_state,
ctx->conn_state);
+
+ KUNIT_EXPECT_FALSE(test, ctx->crtc_state->vrr_supported);
+ KUNIT_EXPECT_EQ(test, (int)ctx->crtc_state->freesync_config.state,
+ (int)VRR_STATE_UNSUPPORTED);
+
+ ctx->crtc_state->base.mode.clock = 594000;
+ ctx->crtc_state->base.mode.hdisplay = 3840;
+ ctx->crtc_state->base.mode.vdisplay = 2160;
+ ctx->crtc_state->base.mode.htotal = 4400;
+ ctx->crtc_state->base.mode.vtotal = 2250;
+
+ amdgpu_dm_get_freesync_config_for_crtc(ctx->crtc_state,
ctx->conn_state);
+
+ KUNIT_EXPECT_TRUE(test, ctx->crtc_state->vrr_supported);
+ KUNIT_EXPECT_EQ(test, (int)ctx->crtc_state->freesync_config.state,
+ (int)VRR_STATE_INACTIVE);
+}
+
/**
* dm_test_freesync_config_active_variable - Test vrr_enabled yields
ACTIVE_VARIABLE
* @test: The KUnit test context
@@ -350,7 +386,7 @@ static void dm_test_freesync_config_active_variable(struct
kunit *test)
ctx->conn_state->freesync_capable = true;
ctx->aconnector->min_vfreq = 48;
- ctx->aconnector->max_vfreq = 120;
+ ctx->aconnector->max_vfreq = 60;
ctx->crtc_state->base.vrr_enabled = true;
amdgpu_dm_get_freesync_config_for_crtc(ctx->crtc_state,
ctx->conn_state);
@@ -362,7 +398,7 @@ static void dm_test_freesync_config_active_variable(struct
kunit *test)
KUNIT_EXPECT_EQ(test,
ctx->crtc_state->freesync_config.min_refresh_in_uhz,
48000000U);
KUNIT_EXPECT_EQ(test,
ctx->crtc_state->freesync_config.max_refresh_in_uhz,
- 120000000U);
+ 60000000U);
KUNIT_EXPECT_TRUE(test,
ctx->crtc_state->freesync_config.vsif_supported);
KUNIT_EXPECT_TRUE(test, ctx->crtc_state->freesync_config.btr);
}
@@ -377,7 +413,7 @@ static void dm_test_freesync_config_inactive(struct kunit
*test)
ctx->conn_state->freesync_capable = true;
ctx->aconnector->min_vfreq = 48;
- ctx->aconnector->max_vfreq = 120;
+ ctx->aconnector->max_vfreq = 60;
ctx->crtc_state->base.vrr_enabled = false;
amdgpu_dm_get_freesync_config_for_crtc(ctx->crtc_state,
ctx->conn_state);
@@ -397,7 +433,7 @@ static void dm_test_freesync_config_active_fixed(struct
kunit *test)
ctx->conn_state->freesync_capable = true;
ctx->aconnector->min_vfreq = 48;
- ctx->aconnector->max_vfreq = 120;
+ ctx->aconnector->max_vfreq = 60;
/* Pre-set fixed state selects the freesync-video (fixed) path */
ctx->crtc_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED;
ctx->crtc_state->freesync_config.fixed_refresh_in_uhz = 60000000;
@@ -842,6 +878,7 @@ static struct kunit_case amdgpu_dm_freesync_tests[] = {
KUNIT_CASE(dm_test_freesync_config_writeback),
KUNIT_CASE(dm_test_freesync_config_not_capable),
KUNIT_CASE(dm_test_freesync_config_out_of_range),
+ KUNIT_CASE(dm_test_freesync_config_invalid_timing),
KUNIT_CASE(dm_test_freesync_config_active_variable),
KUNIT_CASE(dm_test_freesync_config_inactive),
KUNIT_CASE(dm_test_freesync_config_active_fixed),
--
2.43.0