// SPDX-License-Identifier: MIT/* * Copyright 2026 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: AMD * */ #include <drm/drm_atomic.h>#include <drm/drm_modes.h>#include <drm/drm_vblank.h> #include "dc.h"#include "amdgpu.h"#include "amdgpu_dm.h"#include "amdgpu_dm_crtc.h"#include "amdgpu_dm_psr.h"#include "amdgpu_dm_replay.h"#include "amdgpu_dm_freesync.h"#include "dm_helpers.h"#include "modules/inc/mod_freesync.h" bool amdgpu_dm_is_dc_timing_adjust_needed(struct dm_crtc_state *old_state, struct dm_crtc_state *new_state){ if (new_state->stream->adjust.timing_adjust_pending) return true; if (new_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED) return true; else if (amdgpu_dm_crtc_vrr_active(old_state) != amdgpu_dm_crtc_vrr_active(new_state)) return true; else return false;}EXPORT_IF_KUNIT(amdgpu_dm_is_dc_timing_adjust_needed); boolamdgpu_dm_is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state, struct drm_crtc_state *new_crtc_state){ const struct drm_display_mode *old_mode, *new_mode; if (!old_crtc_state || !new_crtc_state) return false; old_mode = &old_crtc_state->mode; new_mode = &new_crtc_state->mode; if (old_mode->clock == new_mode->clock && old_mode->hdisplay == new_mode->hdisplay && old_mode->vdisplay == new_mode->vdisplay && old_mode->htotal == new_mode->htotal && old_mode->vtotal != new_mode->vtotal && old_mode->hsync_start == new_mode->hsync_start && old_mode->vsync_start != new_mode->vsync_start && old_mode->hsync_end == new_mode->hsync_end && old_mode->vsync_end != new_mode->vsync_end && old_mode->hskew == new_mode->hskew && old_mode->vscan == new_mode->vscan && (old_mode->vsync_end - old_mode->vsync_start) == (new_mode->vsync_end - new_mode->vsync_start)) return true; return false;}EXPORT_IF_KUNIT(amdgpu_dm_is_timing_unchanged_for_freesync); void amdgpu_dm_set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state){ u64 num, den, res; struct drm_crtc_state *new_crtc_state = &dm_new_crtc_state->base; dm_new_crtc_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED; num = (unsigned long long)new_crtc_state->mode.clock * 1000 * 1000000; den = (unsigned long long)new_crtc_state->mode.htotal * (unsigned long long)new_crtc_state->mode.vtotal; res = div_u64(num, den); dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = res;}EXPORT_IF_KUNIT(amdgpu_dm_set_freesync_fixed_config); void amdgpu_dm_reset_freesync_config_for_crtc( struct dm_crtc_state *new_crtc_state){ new_crtc_state->vrr_supported = false; memset(&new_crtc_state->vrr_infopacket, 0, sizeof(new_crtc_state->vrr_infopacket));}EXPORT_IF_KUNIT(amdgpu_dm_reset_freesync_config_for_crtc); void amdgpu_dm_get_freesync_config_for_crtc( struct dm_crtc_state *new_crtc_state, struct dm_connector_state *new_con_state){ struct mod_freesync_config config = {0}; struct amdgpu_dm_connector *aconnector; struct drm_display_mode *mode = &new_crtc_state->base.mode; int vrefresh = drm_mode_vrefresh(mode); bool fs_vid_mode = false; if (new_con_state->base.connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) return; aconnector = to_amdgpu_dm_connector(new_con_state->base.connector); new_crtc_state->vrr_supported = new_con_state->freesync_capable && vrefresh >= aconnector->min_vfreq && vrefresh <= aconnector->max_vfreq; if (new_crtc_state->vrr_supported) { new_crtc_state->stream->ignore_msa_timing_param = true; fs_vid_mode = new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED; config.min_refresh_in_uhz = aconnector->min_vfreq * 1000000; config.max_refresh_in_uhz = aconnector->max_vfreq * 1000000; config.vsif_supported = true; config.btr = true; if (fs_vid_mode) { config.state = VRR_STATE_ACTIVE_FIXED; config.fixed_refresh_in_uhz = new_crtc_state->freesync_config.fixed_refresh_in_uhz; goto out; } else if (new_crtc_state->base.vrr_enabled) { config.state = VRR_STATE_ACTIVE_VARIABLE; } else { config.state = VRR_STATE_INACTIVE; } } else { config.state = VRR_STATE_UNSUPPORTED; }out: new_crtc_state->freesync_config = config;}EXPORT_IF_KUNIT(amdgpu_dm_get_freesync_config_for_crtc); void amdgpu_dm_update_freesync_state_on_stream( struct amdgpu_display_manager *dm, struct dm_crtc_state *new_crtc_state, struct dc_stream_state *new_stream, struct dc_plane_state *surface, u32 flip_timestamp_in_us){ struct mod_vrr_params vrr_params; struct dc_info_packet vrr_infopacket = {0}; struct amdgpu_device *adev = dm->adev; struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc); unsigned long flags; bool pack_sdp_v1_3 = false; struct amdgpu_dm_connector *aconn; enum vrr_packet_type packet_type = PACKET_TYPE_VRR; if (!new_stream) return; /* * TODO: Determine why min/max totals and vrefresh can be 0 here. * For now it's sufficient to just guard against these conditions. */ if (!new_stream->timing.h_total || !new_stream->timing.v_total) return; spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); vrr_params = acrtc->dm_irq_params.vrr_params; if (surface) { mod_freesync_handle_preflip( dm->freesync_module, surface, new_stream, flip_timestamp_in_us, &vrr_params); if (adev->family < AMDGPU_FAMILY_AI && amdgpu_dm_crtc_vrr_active(new_crtc_state)) { mod_freesync_handle_v_update(dm->freesync_module, new_stream, &vrr_params); /* Need to call this before the frame ends. */ dc_stream_adjust_vmin_vmax(dm->dc, new_crtc_state->stream, &vrr_params.adjust); } } aconn = (struct amdgpu_dm_connector *)new_stream->dm_stream_context; if (aconn && (aconn->as_type == FREESYNC_TYPE_PCON_IN_WHITELIST || aconn->vsdb_info.replay_mode)) { pack_sdp_v1_3 = aconn->pack_sdp_v1_3; if (aconn->vsdb_info.amd_vsdb_version == 1) packet_type = PACKET_TYPE_FS_V1; else if (aconn->vsdb_info.amd_vsdb_version == 2) packet_type = PACKET_TYPE_FS_V2; else if (aconn->vsdb_info.amd_vsdb_version == 3) packet_type = PACKET_TYPE_FS_V3; mod_build_adaptive_sync_infopacket(new_stream, aconn->as_type, NULL, &new_stream->adaptive_sync_infopacket); } mod_freesync_build_vrr_infopacket( dm->freesync_module, new_stream, &vrr_params, packet_type, TRANSFER_FUNC_UNKNOWN, &vrr_infopacket, pack_sdp_v1_3); new_crtc_state->freesync_vrr_info_changed |= (memcmp(&new_crtc_state->vrr_infopacket, &vrr_infopacket, sizeof(vrr_infopacket)) != 0); acrtc->dm_irq_params.vrr_params = vrr_params; new_crtc_state->vrr_infopacket = vrr_infopacket; new_stream->vrr_infopacket = vrr_infopacket; new_stream->allow_freesync = mod_freesync_get_freesync_enabled(&vrr_params); if (new_crtc_state->freesync_vrr_info_changed) drm_dbg_kms(adev_to_drm(adev), "VRR packet update: crtc=%u enabled=%d state=%d", new_crtc_state->base.crtc->base.id, (int)new_crtc_state->base.vrr_enabled, (int)vrr_params.state); spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);} void amdgpu_dm_update_stream_irq_parameters( struct amdgpu_display_manager *dm, struct dm_crtc_state *new_crtc_state){ struct dc_stream_state *new_stream = new_crtc_state->stream; struct mod_vrr_params vrr_params; struct mod_freesync_config config = new_crtc_state->freesync_config; struct amdgpu_device *adev = dm->adev; struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc); unsigned long flags; if (!new_stream) return; /* * TODO: Determine why min/max totals and vrefresh can be 0 here. * For now it's sufficient to just guard against these conditions. */ if (!new_stream->timing.h_total || !new_stream->timing.v_total) return; spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); vrr_params = acrtc->dm_irq_params.vrr_params; if (new_crtc_state->vrr_supported && config.min_refresh_in_uhz && config.max_refresh_in_uhz) { /* * if freesync compatible mode was set, config.state will be set * in atomic check */ if (config.state == VRR_STATE_ACTIVE_FIXED && config.fixed_refresh_in_uhz && (!drm_atomic_crtc_needs_modeset(&new_crtc_state->base) || new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED)) { vrr_params.max_refresh_in_uhz = config.max_refresh_in_uhz; vrr_params.min_refresh_in_uhz = config.min_refresh_in_uhz; vrr_params.fixed_refresh_in_uhz = config.fixed_refresh_in_uhz; vrr_params.state = VRR_STATE_ACTIVE_FIXED; } else { config.state = new_crtc_state->base.vrr_enabled ? VRR_STATE_ACTIVE_VARIABLE : VRR_STATE_INACTIVE; } } else { config.state = VRR_STATE_UNSUPPORTED; } mod_freesync_build_vrr_params(dm->freesync_module, new_stream, &config, &vrr_params); new_crtc_state->freesync_config = config; /* Copy state for access from DM IRQ handler */ acrtc->dm_irq_params.freesync_config = config; acrtc->dm_irq_params.active_planes = new_crtc_state->active_planes; acrtc->dm_irq_params.vrr_params = vrr_params; spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);} void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm, struct dm_crtc_state *old_state, struct dm_crtc_state *new_state){ struct amdgpu_device *adev = dm->adev; bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state); bool new_vrr_active = amdgpu_dm_crtc_vrr_active(new_state); /* Only DCE gates vupdate on VRR, keep it enabled for DCN */ bool vrr_gates_vupdate = amdgpu_ip_version(adev, DCE_HWIP, 0) == 0; if (!old_vrr_active && new_vrr_active) { /* Transition VRR inactive -> active: * While VRR is active, we must not disable vblank irq, as a * reenable after disable would compute bogus vblank/pflip * timestamps if it likely happened inside display front-porch. * * We also need vupdate irq for the actual core vblank handling * at end of vblank. */ if (vrr_gates_vupdate) WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0); WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0); drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR off->on: Get vblank ref\n", __func__, new_state->base.crtc->base.id); scoped_guard(mutex, &dm->dc_lock) { dc_exit_ips_for_hw_access(dm->dc); amdgpu_dm_psr_set_event(dm, new_state->stream, true, psr_event_vrr_transition, true); amdgpu_dm_replay_set_event(dm, new_state->stream, true, replay_event_vrr, true); } } else if (old_vrr_active && !new_vrr_active) { /* Transition VRR active -> inactive: * Allow vblank irq disable again for fixed refresh rate. */ if (vrr_gates_vupdate) WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0); drm_crtc_vblank_put(new_state->base.crtc); drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR on->off: Drop vblank ref\n", __func__, new_state->base.crtc->base.id); scoped_guard(mutex, &dm->dc_lock) { dc_exit_ips_for_hw_access(dm->dc); amdgpu_dm_psr_set_event(dm, new_state->stream, false, psr_event_vrr_transition, false); amdgpu_dm_replay_set_event(dm, new_state->stream, false, replay_event_vrr, false); } }}
// SPDX-License-Identifier: MIT
/*
* Copyright 2026 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#include <drm/drm_atomic.h>
#include <drm/drm_modes.h>
#include <drm/drm_vblank.h>
#include "dc.h"
#include "amdgpu.h"
#include "amdgpu_dm.h"
#include "amdgpu_dm_crtc.h"
#include "amdgpu_dm_psr.h"
#include "amdgpu_dm_replay.h"
#include "amdgpu_dm_freesync.h"
#include "dm_helpers.h"
#include "modules/inc/mod_freesync.h"
bool amdgpu_dm_is_dc_timing_adjust_needed(struct dm_crtc_state *old_state,
struct dm_crtc_state *new_state)
{
if (new_state->stream->adjust.timing_adjust_pending)
return true;
if (new_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED)
return true;
else if (amdgpu_dm_crtc_vrr_active(old_state) != amdgpu_dm_crtc_vrr_active(new_state))
return true;
else
return false;
}
EXPORT_IF_KUNIT(amdgpu_dm_is_dc_timing_adjust_needed);
bool
amdgpu_dm_is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
struct drm_crtc_state *new_crtc_state)
{
const struct drm_display_mode *old_mode, *new_mode;
if (!old_crtc_state || !new_crtc_state)
return false;
old_mode = &old_crtc_state->mode;
new_mode = &new_crtc_state->mode;
if (old_mode->clock == new_mode->clock &&
old_mode->hdisplay == new_mode->hdisplay &&
old_mode->vdisplay == new_mode->vdisplay &&
old_mode->htotal == new_mode->htotal &&
old_mode->vtotal != new_mode->vtotal &&
old_mode->hsync_start == new_mode->hsync_start &&
old_mode->vsync_start != new_mode->vsync_start &&
old_mode->hsync_end == new_mode->hsync_end &&
old_mode->vsync_end != new_mode->vsync_end &&
old_mode->hskew == new_mode->hskew &&
old_mode->vscan == new_mode->vscan &&
(old_mode->vsync_end - old_mode->vsync_start) ==
(new_mode->vsync_end - new_mode->vsync_start))
return true;
return false;
}
EXPORT_IF_KUNIT(amdgpu_dm_is_timing_unchanged_for_freesync);
void amdgpu_dm_set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state)
{
u64 num, den, res;
struct drm_crtc_state *new_crtc_state = &dm_new_crtc_state->base;
dm_new_crtc_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED;
num = (unsigned long long)new_crtc_state->mode.clock * 1000 * 1000000;
den = (unsigned long long)new_crtc_state->mode.htotal *
(unsigned long long)new_crtc_state->mode.vtotal;
res = div_u64(num, den);
dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = res;
}
EXPORT_IF_KUNIT(amdgpu_dm_set_freesync_fixed_config);
void amdgpu_dm_reset_freesync_config_for_crtc(
struct dm_crtc_state *new_crtc_state)
{
new_crtc_state->vrr_supported = false;
memset(&new_crtc_state->vrr_infopacket, 0,
sizeof(new_crtc_state->vrr_infopacket));
}
EXPORT_IF_KUNIT(amdgpu_dm_reset_freesync_config_for_crtc);
void amdgpu_dm_get_freesync_config_for_crtc(
struct dm_crtc_state *new_crtc_state,
struct dm_connector_state *new_con_state)
{
struct mod_freesync_config config = {0};
struct amdgpu_dm_connector *aconnector;
struct drm_display_mode *mode = &new_crtc_state->base.mode;
int vrefresh = drm_mode_vrefresh(mode);
bool fs_vid_mode = false;
if (new_con_state->base.connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
return;
aconnector = to_amdgpu_dm_connector(new_con_state->base.connector);
new_crtc_state->vrr_supported = new_con_state->freesync_capable &&
vrefresh >= aconnector->min_vfreq &&
vrefresh <= aconnector->max_vfreq;
if (new_crtc_state->vrr_supported) {
new_crtc_state->stream->ignore_msa_timing_param = true;
fs_vid_mode = new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED;
config.min_refresh_in_uhz = aconnector->min_vfreq * 1000000;
config.max_refresh_in_uhz = aconnector->max_vfreq * 1000000;
config.vsif_supported = true;
config.btr = true;
if (fs_vid_mode) {
config.state = VRR_STATE_ACTIVE_FIXED;
config.fixed_refresh_in_uhz = new_crtc_state->freesync_config.fixed_refresh_in_uhz;
goto out;
} else if (new_crtc_state->base.vrr_enabled) {
config.state = VRR_STATE_ACTIVE_VARIABLE;
} else {
config.state = VRR_STATE_INACTIVE;
}
} else {
config.state = VRR_STATE_UNSUPPORTED;
}
out:
new_crtc_state->freesync_config = config;
}
EXPORT_IF_KUNIT(amdgpu_dm_get_freesync_config_for_crtc);
void amdgpu_dm_update_freesync_state_on_stream(
struct amdgpu_display_manager *dm,
struct dm_crtc_state *new_crtc_state,
struct dc_stream_state *new_stream,
struct dc_plane_state *surface,
u32 flip_timestamp_in_us)
{
struct mod_vrr_params vrr_params;
struct dc_info_packet vrr_infopacket = {0};
struct amdgpu_device *adev = dm->adev;
struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc);
unsigned long flags;
bool pack_sdp_v1_3 = false;
struct amdgpu_dm_connector *aconn;
enum vrr_packet_type packet_type = PACKET_TYPE_VRR;
if (!new_stream)
return;
/*
* TODO: Determine why min/max totals and vrefresh can be 0 here.
* For now it's sufficient to just guard against these conditions.
*/
if (!new_stream->timing.h_total || !new_stream->timing.v_total)
return;
spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
vrr_params = acrtc->dm_irq_params.vrr_params;
if (surface) {
mod_freesync_handle_preflip(
dm->freesync_module,
surface,
new_stream,
flip_timestamp_in_us,
&vrr_params);
if (adev->family < AMDGPU_FAMILY_AI &&
amdgpu_dm_crtc_vrr_active(new_crtc_state)) {
mod_freesync_handle_v_update(dm->freesync_module,
new_stream, &vrr_params);
/* Need to call this before the frame ends. */
dc_stream_adjust_vmin_vmax(dm->dc,
new_crtc_state->stream,
&vrr_params.adjust);
}
}
aconn = (struct amdgpu_dm_connector *)new_stream->dm_stream_context;
if (aconn && (aconn->as_type == FREESYNC_TYPE_PCON_IN_WHITELIST || aconn->vsdb_info.replay_mode)) {
pack_sdp_v1_3 = aconn->pack_sdp_v1_3;
if (aconn->vsdb_info.amd_vsdb_version == 1)
packet_type = PACKET_TYPE_FS_V1;
else if (aconn->vsdb_info.amd_vsdb_version == 2)
packet_type = PACKET_TYPE_FS_V2;
else if (aconn->vsdb_info.amd_vsdb_version == 3)
packet_type = PACKET_TYPE_FS_V3;
mod_build_adaptive_sync_infopacket(new_stream, aconn->as_type, NULL,
&new_stream->adaptive_sync_infopacket);
}
mod_freesync_build_vrr_infopacket(
dm->freesync_module,
new_stream,
&vrr_params,
packet_type,
TRANSFER_FUNC_UNKNOWN,
&vrr_infopacket,
pack_sdp_v1_3);
new_crtc_state->freesync_vrr_info_changed |=
(memcmp(&new_crtc_state->vrr_infopacket,
&vrr_infopacket,
sizeof(vrr_infopacket)) != 0);
acrtc->dm_irq_params.vrr_params = vrr_params;
new_crtc_state->vrr_infopacket = vrr_infopacket;
new_stream->vrr_infopacket = vrr_infopacket;
new_stream->allow_freesync = mod_freesync_get_freesync_enabled(&vrr_params);
if (new_crtc_state->freesync_vrr_info_changed)
drm_dbg_kms(adev_to_drm(adev), "VRR packet update: crtc=%u enabled=%d state=%d",
new_crtc_state->base.crtc->base.id,
(int)new_crtc_state->base.vrr_enabled,
(int)vrr_params.state);
spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
}
void amdgpu_dm_update_stream_irq_parameters(
struct amdgpu_display_manager *dm,
struct dm_crtc_state *new_crtc_state)
{
struct dc_stream_state *new_stream = new_crtc_state->stream;
struct mod_vrr_params vrr_params;
struct mod_freesync_config config = new_crtc_state->freesync_config;
struct amdgpu_device *adev = dm->adev;
struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc);
unsigned long flags;
if (!new_stream)
return;
/*
* TODO: Determine why min/max totals and vrefresh can be 0 here.
* For now it's sufficient to just guard against these conditions.
*/
if (!new_stream->timing.h_total || !new_stream->timing.v_total)
return;
spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
vrr_params = acrtc->dm_irq_params.vrr_params;
if (new_crtc_state->vrr_supported &&
config.min_refresh_in_uhz &&
config.max_refresh_in_uhz) {
/*
* if freesync compatible mode was set, config.state will be set
* in atomic check
*/
if (config.state == VRR_STATE_ACTIVE_FIXED && config.fixed_refresh_in_uhz &&
(!drm_atomic_crtc_needs_modeset(&new_crtc_state->base) ||
new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED)) {
vrr_params.max_refresh_in_uhz = config.max_refresh_in_uhz;
vrr_params.min_refresh_in_uhz = config.min_refresh_in_uhz;
vrr_params.fixed_refresh_in_uhz = config.fixed_refresh_in_uhz;
vrr_params.state = VRR_STATE_ACTIVE_FIXED;
} else {
config.state = new_crtc_state->base.vrr_enabled ?
VRR_STATE_ACTIVE_VARIABLE :
VRR_STATE_INACTIVE;
}
} else {
config.state = VRR_STATE_UNSUPPORTED;
}
mod_freesync_build_vrr_params(dm->freesync_module,
new_stream,
&config, &vrr_params);
new_crtc_state->freesync_config = config;
/* Copy state for access from DM IRQ handler */
acrtc->dm_irq_params.freesync_config = config;
acrtc->dm_irq_params.active_planes = new_crtc_state->active_planes;
acrtc->dm_irq_params.vrr_params = vrr_params;
spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
}
void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm,
struct dm_crtc_state *old_state,
struct dm_crtc_state *new_state)
{
struct amdgpu_device *adev = dm->adev;
bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state);
bool new_vrr_active = amdgpu_dm_crtc_vrr_active(new_state);
/* Only DCE gates vupdate on VRR, keep it enabled for DCN */
bool vrr_gates_vupdate = amdgpu_ip_version(adev, DCE_HWIP, 0) == 0;
if (!old_vrr_active && new_vrr_active) {
/* Transition VRR inactive -> active:
* While VRR is active, we must not disable vblank irq, as a
* reenable after disable would compute bogus vblank/pflip
* timestamps if it likely happened inside display front-porch.
*
* We also need vupdate irq for the actual core vblank handling
* at end of vblank.
*/
if (vrr_gates_vupdate)
WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0);
drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR off->on: Get vblank ref\n",
__func__, new_state->base.crtc->base.id);
scoped_guard(mutex, &dm->dc_lock) {
dc_exit_ips_for_hw_access(dm->dc);
amdgpu_dm_psr_set_event(dm, new_state->stream, true,
psr_event_vrr_transition, true);
amdgpu_dm_replay_set_event(dm, new_state->stream, true,
replay_event_vrr, true);
}
} else if (old_vrr_active && !new_vrr_active) {
/* Transition VRR active -> inactive:
* Allow vblank irq disable again for fixed refresh rate.
*/
if (vrr_gates_vupdate)
WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0);
drm_crtc_vblank_put(new_state->base.crtc);
drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR on->off: Drop vblank ref\n",
__func__, new_state->base.crtc->base.id);
scoped_guard(mutex, &dm->dc_lock) {
dc_exit_ips_for_hw_access(dm->dc);
amdgpu_dm_psr_set_event(dm, new_state->stream, false,
psr_event_vrr_transition, false);
amdgpu_dm_replay_set_event(dm, new_state->stream, false,
replay_event_vrr, false);
}
}
}