* [PATCH] drm/amd/display: only allow freesync on a VRR_ENABLED crtc
@ 2026-09-09 19:59 Matthew Schwartz
0 siblings, 0 replies; only message in thread
From: Matthew Schwartz @ 2026-09-09 19:59 UTC (permalink / raw)
To: amd-gfx
Cc: Matthew Schwartz, Harry Wentland, Leo Li, Rodrigo Siqueira,
Alex Deucher, Christian König, David Airlie, Simona Vetter,
Austin Zheng, Jun Lei, Alan Liu, Aurabindo Pillai, Alvin Lee,
dri-devel, linux-kernel
VRR-capable sinks set allow_freesync even with VRR_ENABLED=0, letting
FPO stretch frames during UCLK switches. On a Navi 33 driving 4K120
through a DP-HDMI PCON, a 40fps compositor limiter then runs at 37fps.
This surfaced after commit d49086491bcb ("drm/amd/display: Adjust
freesync pcon whitelist").
Allow frame stretching only for VRR_STATE_ACTIVE_VARIABLE. Store the
proposed permission in the private DC state so TEST_ONLY and failed
checks cannot change the shared stream. Apply changed permissions at
commit and force full DC validation, including native DP and eDP.
Fixes: 7eaef1163b82 ("drm/amd/display: set allow_freesync parameter in DM")
Signed-off-by: Matthew Schwartz <matthew.schwartz@linux.dev>
---
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 26 ++++++++++++++++
.../display/amdgpu_dm/amdgpu_dm_freesync.c | 1 -
drivers/gpu/drm/amd/display/dc/core/dc.c | 4 +++
.../gpu/drm/amd/display/dc/core/dc_state.c | 31 +++++++++++++++++++
drivers/gpu/drm/amd/display/dc/dc_state.h | 6 ++++
drivers/gpu/drm/amd/display/dc/dc_stream.h | 3 ++
.../drm/amd/display/dc/dml/dcn32/dcn32_fpu.c | 9 ++++--
.../dml21_wrapper/dml21_translation_helper.c | 9 ++++--
.../dc/resource/dcn30/dcn30_resource.c | 2 +-
.../resource/dcn32/dcn32_resource_helpers.c | 10 ++++--
10 files changed, 92 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index f49cc198fb0d..09c088b878bb 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -3835,6 +3835,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
unsigned long flags;
u32 target_vblank, last_flip_vblank;
bool vrr_active = amdgpu_dm_crtc_vrr_active(acrtc_state);
+ bool allow_freesync = acrtc_state->freesync_config.state == VRR_STATE_ACTIVE_VARIABLE;
bool cursor_update = false;
bool pflip_present = false;
bool immediate_flip = false;
@@ -4146,6 +4147,9 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
}
mutex_lock(&dm->dc_lock);
+ if (acrtc_state->update_type == UPDATE_TYPE_FULL &&
+ allow_freesync != acrtc_state->stream->allow_freesync)
+ bundle->stream_update.allow_freesync = &allow_freesync;
update_planes_and_stream_adapter(dm->dc,
acrtc_state->update_type,
planes_count,
@@ -5413,6 +5417,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
struct dc_stream_state *new_stream;
struct amdgpu_device *adev = dm->adev;
int ret = 0;
+ bool allow_freesync, old_allow_freesync;
/*
* TODO Move this code into dm_crtc_atomic_check once we get rid of dc_validation_set
@@ -5668,6 +5673,27 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
amdgpu_dm_get_freesync_config_for_crtc(dm_new_crtc_state,
dm_new_conn_state);
+ allow_freesync = dm_new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_VARIABLE;
+ old_allow_freesync = dm_old_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_VARIABLE;
+
+ /* Keep the proposed FreeSync permission in the private DC state. */
+ if (dm_new_crtc_state->stream != dm_old_crtc_state->stream ||
+ old_allow_freesync != allow_freesync) {
+ ret = dm_atomic_get_state(state, &dm_state);
+ if (ret)
+ goto fail;
+
+ if (!dc_state_set_stream_allow_freesync(dm_state->context,
+ dm_new_crtc_state->stream,
+ allow_freesync)) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* DML must re-evaluate FPO when the permission changes. */
+ *lock_and_validation_needed = true;
+ }
+
return ret;
fail:
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 281c6cec9f4b..9f40064d13c5 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
@@ -253,7 +253,6 @@ void amdgpu_dm_update_freesync_state_on_stream(
new_crtc_state->vrr_infopacket = vrr_infopacket;
new_stream->vrr_infopacket = vrr_infopacket;
- new_stream->allow_freesync = mod_freesync_get_freesync_enabled(&vrr_params);
/*
* HDMI ALLM: when Gaming-VRR is active (VRR_EN=1) and the sink
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index ebbd81995c38..6064cc992d91 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -3175,6 +3175,10 @@ static struct dc_update_descriptor check_update_surfaces_for_stream(
if (stream_update->mst_bw_update)
su_flags->bits.mst_bw = 1;
+ if (stream_update->allow_freesync &&
+ *stream_update->allow_freesync != stream_update->stream->allow_freesync)
+ su_flags->bits.fams_changed = 1;
+
if (stream_update->stream->freesync_on_desktop &&
(stream_update->vrr_infopacket || stream_update->allow_freesync ||
stream_update->vrr_active_variable || stream_update->vrr_active_fixed))
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_state.c b/drivers/gpu/drm/amd/display/dc/core/dc_state.c
index 1e4fe2f941ca..382858b6c1da 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_state.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_state.c
@@ -425,6 +425,7 @@ enum dc_status dc_state_add_stream(
}
state->streams[state->stream_count] = stream;
+ state->stream_status[state->stream_count].allow_freesync_valid = false;
dc_stream_retain(stream);
state->stream_count++;
@@ -746,6 +747,36 @@ struct dc_stream_status *dc_state_get_stream_status(
return status.stream_count > 0 ? status.stream_status[0] : NULL;
}
+bool dc_state_get_stream_allow_freesync(const struct dc_state *state,
+ const struct dc_stream_state *stream)
+{
+ unsigned int i;
+
+ for (i = 0; i < state->stream_count; i++) {
+ if (state->streams[i] == stream &&
+ state->stream_status[i].allow_freesync_valid)
+ return state->stream_status[i].allow_freesync;
+ }
+
+ return stream->allow_freesync;
+}
+
+bool dc_state_set_stream_allow_freesync(struct dc_state *state,
+ const struct dc_stream_state *stream, bool allow_freesync)
+{
+ unsigned int i;
+
+ for (i = 0; i < state->stream_count; i++) {
+ if (state->streams[i] == stream) {
+ state->stream_status[i].allow_freesync = allow_freesync;
+ state->stream_status[i].allow_freesync_valid = true;
+ return true;
+ }
+ }
+
+ return false;
+}
+
enum mall_stream_type dc_state_get_pipe_subvp_type(const struct dc_state *state,
const struct pipe_ctx *pipe_ctx)
{
diff --git a/drivers/gpu/drm/amd/display/dc/dc_state.h b/drivers/gpu/drm/amd/display/dc/dc_state.h
index acf461225e9d..40ad063cec09 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_state.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_state.h
@@ -81,4 +81,10 @@ struct dc_get_status_options;
enum dc_status dc_state_get_status(struct dc_state_status *status,
const struct dc_get_status_options *options);
+bool dc_state_get_stream_allow_freesync(const struct dc_state *state,
+ const struct dc_stream_state *stream);
+
+bool dc_state_set_stream_allow_freesync(struct dc_state *state,
+ const struct dc_stream_state *stream, bool allow_freesync);
+
#endif /* _DC_STATE_H_ */
diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h
index 934ae381e730..14d8da9f15e5 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_stream.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h
@@ -62,6 +62,9 @@ struct dc_stream_status {
bool is_abm_supported;
struct mall_stream_config mall_stream_config;
bool fpo_in_use;
+ /* Proposed permission for validation, without modifying the stream. */
+ bool allow_freesync;
+ bool allow_freesync_valid;
};
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c
index b5bd1358fdfd..a816e47153ad 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c
@@ -844,7 +844,9 @@ static bool subvp_drr_schedulable(struct dc *dc, struct dc_state *context)
continue;
if (dc_state_get_pipe_subvp_type(context, drr_pipe) == SUBVP_NONE && drr_pipe->stream->ignore_msa_timing_param &&
- (drr_pipe->stream->allow_freesync || drr_pipe->stream->vrr_active_variable || drr_pipe->stream->vrr_active_fixed)) {
+ (dc_state_get_stream_allow_freesync(context, drr_pipe->stream) ||
+ drr_pipe->stream->vrr_active_variable ||
+ drr_pipe->stream->vrr_active_fixed)) {
drr_found = true;
break;
}
@@ -3444,7 +3446,10 @@ bool dcn32_allow_subvp_high_refresh_rate(struct dc *dc, struct dc_state *context
uint32_t height = subvp_high_refresh_list.res[i].height;
if (dcn32_check_native_scaling_for_res(pipe, width, height)) {
- if ((context->stream_count == 1 && !pipe->stream->allow_freesync) || context->stream_count > 1) {
+ if ((context->stream_count == 1 &&
+ !dc_state_get_stream_allow_freesync(context,
+ pipe->stream)) ||
+ context->stream_count > 1) {
allow = true;
break;
}
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c
index 3c25de54d55b..031f90955b95 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c
@@ -125,6 +125,7 @@ static unsigned int calc_vblank_nom_lines(const struct dc_stream_state *stream,
}
static void populate_dml21_timing_config_from_stream_state(struct dml2_timing_cfg *timing,
+ const struct dc_state *context,
struct dc_stream_state *stream,
struct pipe_ctx *otg_master_pipe,
struct dml2_context *dml_ctx)
@@ -166,7 +167,7 @@ static void populate_dml21_timing_config_from_stream_state(struct dml2_timing_cf
timing->drr_config.enabled = stream->ignore_msa_timing_param;
timing->drr_config.drr_active_variable = stream->vrr_active_variable;
timing->drr_config.drr_active_fixed = stream->vrr_active_fixed;
- timing->drr_config.disallowed = !stream->allow_freesync;
+ timing->drr_config.disallowed = !dc_state_get_stream_allow_freesync(context, stream);
/* limit min refresh rate to DC cap */
min_hardware_refresh_in_uhz = stream->timing.min_refresh_in_uhz;
@@ -923,6 +924,7 @@ bool dml21_map_dc_state_into_dml_display_cfg(const struct dc *in_dc, struct dc_s
struct dml2_display_cfg *dml_dispcfg = &dml_ctx->v21.display_config;
unsigned int plane_count = 0;
struct pipe_ctx *otg_master_pipe;
+ struct dml2_timing_cfg *timing;
memset(&dml_ctx->v21.dml_to_dc_pipe_mapping, 0, sizeof(struct dml2_dml_to_dc_pipe_mapping));
@@ -952,7 +954,10 @@ bool dml21_map_dc_state_into_dml_display_cfg(const struct dc *in_dc, struct dc_s
ASSERT(disp_cfg_stream_location >= 0 && disp_cfg_stream_location < __DML2_WRAPPER_MAX_STREAMS_PLANES__);
- populate_dml21_timing_config_from_stream_state(&dml_dispcfg->stream_descriptors[disp_cfg_stream_location].timing, context->streams[stream_index], otg_master_pipe, dml_ctx);
+ timing = &dml_dispcfg->stream_descriptors[disp_cfg_stream_location].timing;
+ populate_dml21_timing_config_from_stream_state(timing, context,
+ context->streams[stream_index],
+ otg_master_pipe, dml_ctx);
populate_dml21_output_config_from_stream_state(&dml_dispcfg->stream_descriptors[disp_cfg_stream_location].output, context->streams[stream_index], otg_master_pipe);
populate_dml21_writeback_config_from_stream_state(&dml_dispcfg->stream_descriptors[disp_cfg_stream_location].writeback, context->streams[stream_index]);
populate_dml21_stream_overrides_from_stream_state(&dml_dispcfg->stream_descriptors[disp_cfg_stream_location], context->streams[stream_index], &context->stream_status[stream_index]);
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c
index dbb8bb7fc20d..5333c4cc9eba 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c
@@ -2152,7 +2152,7 @@ bool dcn30_can_support_mclk_switch_using_fw_based_vblank_stretch(struct dc *dc,
if (!is_refresh_rate_support_mclk_switch_using_fw_based_vblank_stretch(context))
return false;
- if (!context->streams[0]->allow_freesync)
+ if (!dc_state_get_stream_allow_freesync(context, context->streams[0]))
return false;
if (context->streams[0]->vrr_active_variable && (dc->debug.disable_fams_gaming == INGAME_FAMS_DISABLE))
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource_helpers.c b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource_helpers.c
index 602a0e4e5dc0..a86d3111703f 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource_helpers.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource_helpers.c
@@ -579,7 +579,7 @@ struct dc_stream_state *dcn32_can_support_mclk_switch_using_fw_based_vblank_stre
if (!is_refresh_rate_support_mclk_switch_using_fw_based_vblank_stretch(fpo_candidate_stream, fpo_vactive_margin_us, refresh_rate))
return NULL;
- if (!fpo_candidate_stream->allow_freesync)
+ if (!dc_state_get_stream_allow_freesync(context, fpo_candidate_stream))
return NULL;
if (fpo_candidate_stream->vrr_active_variable &&
@@ -673,7 +673,9 @@ bool dcn32_subvp_drr_admissable(struct dc *dc, struct dc_state *context)
non_subvp_pipes++;
drr_psr_capable = (drr_psr_capable || dcn32_is_psr_capable(pipe));
if (pipe->stream->ignore_msa_timing_param &&
- (pipe->stream->allow_freesync || pipe->stream->vrr_active_variable || pipe->stream->vrr_active_fixed)) {
+ (dc_state_get_stream_allow_freesync(context, pipe->stream) ||
+ pipe->stream->vrr_active_variable ||
+ pipe->stream->vrr_active_fixed)) {
drr_pipe_found = true;
}
}
@@ -734,7 +736,9 @@ bool dcn32_subvp_vblank_admissable(struct dc *dc, struct dc_state *context, int
non_subvp_pipes++;
vblank_psr_capable = (vblank_psr_capable || dcn32_is_psr_capable(pipe));
if (pipe->stream->ignore_msa_timing_param &&
- (pipe->stream->allow_freesync || pipe->stream->vrr_active_variable || pipe->stream->vrr_active_fixed)) {
+ (dc_state_get_stream_allow_freesync(context, pipe->stream) ||
+ pipe->stream->vrr_active_variable ||
+ pipe->stream->vrr_active_fixed)) {
drr_pipe_found = true;
}
}
--
2.55.0
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-09-09 19:59 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-09 19:59 [PATCH] drm/amd/display: only allow freesync on a VRR_ENABLED crtc Matthew Schwartz
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®