drm/amd/display: add FreeSync/VRR module

[WHAT]
Group the FreeSync/VRR helpers from amdgpu_dm.c into a dedicated
amdgpu_dm_freesync.c and amdgpu_dm_freesync.h. The moved functions are:

  is_dc_timing_adjust_needed, is_timing_unchanged_for_freesync,
  set_freesync_fixed_config, reset_freesync_config_for_crtc,
  get_freesync_config_for_crtc, update_freesync_state_on_stream,
  update_stream_irq_parameters, amdgpu_dm_handle_vrr_transition.

The FreeSync/VRR helpers exposed by amdgpu_dm_freesync.h are called
from amdgpu_dm.c, so give the seven that lacked a namespace prefix the
standard amdgpu_dm_ prefix:

Relocate the corresponding KUnit tests into amdgpu_dm_freesync_test.c.

No functional change.

Reviewed-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com>
Signed-off-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: Fangzhi Zuo <jerry.zuo@amd.com>
Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Alex Hung 2026-07-09 19:56:03 -06:00 committed by Alex Deucher
parent 66517ca0cd
commit 09dd9fbc0b
8 changed files with 894 additions and 764 deletions

View File

@ -45,7 +45,8 @@ AMDGPUDM = \
amdgpu_dm_backlight.o \
amdgpu_dm_audio.o \
amdgpu_dm_dmub.o \
amdgpu_dm_connector.o
amdgpu_dm_connector.o \
amdgpu_dm_freesync.o
ifdef CONFIG_DRM_AMD_DC_FP
AMDGPUDM += dc_fpu.o

View File

@ -71,6 +71,7 @@
#include "amdgpu_dm_dmub.h"
#include "amdgpu_dm_connector.h"
#include "amdgpu_dm_pp_smu.h"
#include "amdgpu_dm_freesync.h"
#include "ivsrcid/ivsrcid_vislands30.h"
@ -127,7 +128,6 @@ MODULE_FIRMWARE(FIRMWARE_NAVI12_DMCU);
/* basic init/fini API */
static int amdgpu_dm_init(struct amdgpu_device *adev);
static void amdgpu_dm_fini(struct amdgpu_device *adev);
STATIC_IFN_KUNIT void reset_freesync_config_for_crtc(struct dm_crtc_state *new_crtc_state);
/*
* initializes drm_device display related structures, based on the information
@ -147,10 +147,6 @@ STATIC_IFN_KUNIT void dm_enable_per_frame_crtc_master_sync(struct dc_state *cont
static int amdgpu_dm_atomic_check(struct drm_device *dev,
struct drm_atomic_commit *state);
STATIC_IFN_KUNIT bool
is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
struct drm_crtc_state *new_crtc_state);
static inline void amdgpu_dm_exit_ips_for_hw_access(struct dc *dc)
{
if (dc->ctx->dmub_srv && !dc->ctx->dmub_srv->idle_exit_counter)
@ -248,20 +244,6 @@ STATIC_IFN_KUNIT int dm_soft_reset(struct amdgpu_ip_block *ip_block)
}
EXPORT_IF_KUNIT(dm_soft_reset);
STATIC_IFN_KUNIT bool 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(is_dc_timing_adjust_needed);
/*
* DC will program planes with their z-order determined by their ordering
* in the dc_surface_updates array. This comparator is used to sort them
@ -1542,7 +1524,7 @@ static void dm_destroy_cached_state(struct amdgpu_device *adev)
for_each_new_crtc_in_state(dm->cached_state, crtc, new_crtc_state, i) {
new_crtc_state->active_changed = true;
dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
reset_freesync_config_for_crtc(dm_new_crtc_state);
amdgpu_dm_reset_freesync_config_for_crtc(dm_new_crtc_state);
}
/*
@ -3614,213 +3596,6 @@ static void prepare_flip_isr(struct amdgpu_crtc *acrtc)
acrtc->crtc_id);
}
static void 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);
}
static void 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);
}
static 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);
}
}
}
static void amdgpu_dm_commit_cursors(struct drm_atomic_commit *state)
{
struct drm_plane *plane;
@ -4177,7 +3952,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
}
if (plane == pcrtc->primary)
update_freesync_state_on_stream(
amdgpu_dm_update_freesync_state_on_stream(
dm,
acrtc_state,
acrtc_state->stream,
@ -4295,7 +4070,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
* re-adjust the min/max bounds now that DC doesn't handle this
* as part of commit.
*/
if (is_dc_timing_adjust_needed(dm_old_crtc_state, acrtc_state)) {
if (amdgpu_dm_is_dc_timing_adjust_needed(dm_old_crtc_state, acrtc_state)) {
spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
dc_stream_adjust_vmin_vmax(
dm->dc, acrtc_state->stream,
@ -5346,7 +5121,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state)
dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
/* For freesync config update on crtc state and params for irq */
update_stream_irq_parameters(dm, dm_new_crtc_state);
amdgpu_dm_update_stream_irq_parameters(dm, dm_new_crtc_state);
#ifdef CONFIG_DEBUG_FS
spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
@ -5546,108 +5321,6 @@ static int do_aquire_global_lock(struct drm_device *dev,
return ret < 0 ? ret : 0;
}
STATIC_IFN_KUNIT void 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(get_freesync_config_for_crtc);
STATIC_IFN_KUNIT void 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(reset_freesync_config_for_crtc);
STATIC_IFN_KUNIT bool
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(is_timing_unchanged_for_freesync);
STATIC_IFN_KUNIT void 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(set_freesync_fixed_config);
static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
struct drm_atomic_commit *state,
struct drm_crtc *crtc,
@ -5744,7 +5417,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
*/
if (amdgpu_freesync_vid_mode &&
dm_new_crtc_state->stream &&
is_timing_unchanged_for_freesync(new_crtc_state, old_crtc_state))
amdgpu_dm_is_timing_unchanged_for_freesync(new_crtc_state, old_crtc_state))
goto skip_modeset;
if (dm_new_crtc_state->stream &&
@ -5786,14 +5459,14 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
if (amdgpu_freesync_vid_mode && dm_new_crtc_state->stream &&
dc_is_stream_unchanged(new_stream, dm_old_crtc_state->stream) &&
dc_is_stream_scaling_unchanged(new_stream, dm_old_crtc_state->stream) &&
is_timing_unchanged_for_freesync(new_crtc_state,
amdgpu_dm_is_timing_unchanged_for_freesync(new_crtc_state,
old_crtc_state)) {
new_crtc_state->mode_changed = false;
drm_dbg_driver(adev_to_drm(adev),
"Mode change not required for front porch change, setting mode_changed to %d",
new_crtc_state->mode_changed);
set_freesync_fixed_config(dm_new_crtc_state);
amdgpu_dm_set_freesync_fixed_config(dm_new_crtc_state);
goto skip_modeset;
} else if (amdgpu_freesync_vid_mode && aconnector &&
@ -5803,7 +5476,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
high_mode = amdgpu_dm_get_highest_refresh_rate_mode(aconnector, false);
if (!drm_mode_equal(&new_crtc_state->mode, high_mode))
set_freesync_fixed_config(dm_new_crtc_state);
amdgpu_dm_set_freesync_fixed_config(dm_new_crtc_state);
}
ret = dm_atomic_get_state(state, &dm_state);
@ -5825,7 +5498,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
dc_stream_release(dm_old_crtc_state->stream);
dm_new_crtc_state->stream = NULL;
reset_freesync_config_for_crtc(dm_new_crtc_state);
amdgpu_dm_reset_freesync_config_for_crtc(dm_new_crtc_state);
*lock_and_validation_needed = true;
@ -5913,7 +5586,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
}
/* Update Freesync settings. */
get_freesync_config_for_crtc(dm_new_crtc_state,
amdgpu_dm_get_freesync_config_for_crtc(dm_new_crtc_state,
dm_new_conn_state);
return ret;

View File

@ -1172,16 +1172,8 @@ void dm_get_plane_scale(struct drm_plane_state *plane_state,
int *out_plane_scale_w, int *out_plane_scale_h);
bool is_scaling_state_different(const struct dm_connector_state *dm_state,
const struct dm_connector_state *old_dm_state);
bool is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
struct drm_crtc_state *new_crtc_state);
void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state);
bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state,
struct dm_crtc_state *new_state);
void set_multisync_trigger_params(struct dc_stream_state *stream);
void set_master_stream(struct dc_stream_state *stream_set[], int stream_count);
void reset_freesync_config_for_crtc(struct dm_crtc_state *new_crtc_state);
void get_freesync_config_for_crtc(struct dm_crtc_state *new_crtc_state,
struct dm_connector_state *new_con_state);
void dm_enable_per_frame_crtc_master_sync(struct dc_state *context);
struct hdcp_workqueue;
bool is_content_protection_different(struct drm_crtc_state *new_crtc_state,

View File

@ -0,0 +1,362 @@
// 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);
}
}
}

View File

@ -0,0 +1,65 @@
/* 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
*
*/
#ifndef __AMDGPU_DM_FREESYNC_H__
#define __AMDGPU_DM_FREESYNC_H__
#include <linux/types.h>
struct amdgpu_display_manager;
struct dm_crtc_state;
struct dm_connector_state;
struct dc_stream_state;
struct dc_plane_state;
struct drm_crtc_state;
bool amdgpu_dm_is_dc_timing_adjust_needed(struct dm_crtc_state *old_state,
struct dm_crtc_state *new_state);
bool amdgpu_dm_is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
struct drm_crtc_state *new_crtc_state);
void amdgpu_dm_set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state);
void amdgpu_dm_reset_freesync_config_for_crtc(struct dm_crtc_state *new_crtc_state);
void amdgpu_dm_get_freesync_config_for_crtc(struct dm_crtc_state *new_crtc_state,
struct dm_connector_state *new_con_state);
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);
void amdgpu_dm_update_stream_irq_parameters(struct amdgpu_display_manager *dm,
struct dm_crtc_state *new_crtc_state);
void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm,
struct dm_crtc_state *old_state,
struct dm_crtc_state *new_state);
#endif /* __AMDGPU_DM_FREESYNC_H__ */

View File

@ -29,6 +29,7 @@ obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_plane_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_mst_types_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_pp_smu_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_freesync_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_crtc_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_services_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_helpers_test.o

View File

@ -0,0 +1,453 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* KUnit tests for amdgpu_dm_freesync.c
*
* Copyright 2026 Advanced Micro Devices, Inc.
*/
#include <kunit/test.h>
#include <drm/drm_atomic.h>
#include <drm/drm_connector.h>
#include <drm/drm_crtc.h>
#include <drm/drm_modes.h>
#include "dc.h"
#include "inc/core_types.h"
#include "amd_shared.h"
#include "amdgpu.h"
#include "amdgpu_mode.h"
#include "amdgpu_dm.h"
#include "amdgpu_dm_freesync.h"
#include "amdgpu_dm_kunit_test_helpers.h"
/* Tests for amdgpu_dm_is_timing_unchanged_for_freesync() */
/**
* dm_test_timing_unchanged_null_args - Test NULL crtc states return false
* @test: The KUnit test context
*/
static void dm_test_timing_unchanged_null_args(struct kunit *test)
{
struct drm_crtc_state crtc_state = { 0 };
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_is_timing_unchanged_for_freesync(NULL, &crtc_state));
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_is_timing_unchanged_for_freesync(&crtc_state, NULL));
}
/**
* dm_test_timing_unchanged_identical_modes - Test identical modes are not "unchanged"
* @test: The KUnit test context
*
* The helper only returns true when vtotal/vsync shift (vrr) while the rest
* of the timing stays fixed, so identical modes must return false.
*/
static void dm_test_timing_unchanged_identical_modes(struct kunit *test)
{
struct drm_crtc_state old_state = { 0 };
struct drm_crtc_state new_state = { 0 };
old_state.mode.clock = 148500;
old_state.mode.hdisplay = 1920;
old_state.mode.vdisplay = 1080;
old_state.mode.htotal = 2200;
old_state.mode.vtotal = 1125;
new_state.mode = old_state.mode;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_is_timing_unchanged_for_freesync(&old_state, &new_state));
}
/**
* dm_test_timing_unchanged_vrr_shift - Test vrr-style vtotal/vsync shift is detected
* @test: The KUnit test context
*/
static void dm_test_timing_unchanged_vrr_shift(struct kunit *test)
{
struct drm_crtc_state old_state = { 0 };
struct drm_crtc_state new_state = { 0 };
old_state.mode.clock = 148500;
old_state.mode.hdisplay = 1920;
old_state.mode.vdisplay = 1080;
old_state.mode.htotal = 2200;
old_state.mode.vtotal = 1125;
old_state.mode.hsync_start = 2008;
old_state.mode.vsync_start = 1084;
old_state.mode.hsync_end = 2052;
old_state.mode.vsync_end = 1089;
/* Same horizontal timing, vertical totals/sync shifted by 125 lines */
new_state.mode = old_state.mode;
new_state.mode.vtotal = 1250;
new_state.mode.vsync_start = 1209;
new_state.mode.vsync_end = 1214;
KUNIT_EXPECT_TRUE(test,
amdgpu_dm_is_timing_unchanged_for_freesync(&old_state, &new_state));
}
/**
* dm_test_timing_unchanged_clock_changed - Test pixel clock change returns false
* @test: The KUnit test context
*/
static void dm_test_timing_unchanged_clock_changed(struct kunit *test)
{
struct drm_crtc_state old_state = { 0 };
struct drm_crtc_state new_state = { 0 };
old_state.mode.clock = 148500;
old_state.mode.htotal = 2200;
old_state.mode.vtotal = 1125;
old_state.mode.vsync_start = 1084;
old_state.mode.vsync_end = 1089;
new_state.mode = old_state.mode;
new_state.mode.clock = 297000;
new_state.mode.vtotal = 1250;
new_state.mode.vsync_start = 1209;
new_state.mode.vsync_end = 1214;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_is_timing_unchanged_for_freesync(&old_state, &new_state));
}
/* Tests for amdgpu_dm_set_freesync_fixed_config() */
/**
* dm_test_set_freesync_fixed_config_60hz - Test fixed refresh computed for 1080p60
* @test: The KUnit test context
*/
static void dm_test_set_freesync_fixed_config_60hz(struct kunit *test)
{
struct dm_crtc_state dm_crtc_state = { 0 };
dm_crtc_state.base.mode.clock = 148500;
dm_crtc_state.base.mode.htotal = 2200;
dm_crtc_state.base.mode.vtotal = 1125;
amdgpu_dm_set_freesync_fixed_config(&dm_crtc_state);
KUNIT_EXPECT_EQ(test, (int)dm_crtc_state.freesync_config.state,
(int)VRR_STATE_ACTIVE_FIXED);
/* 148500 kHz / (2200 * 1125) = 60 Hz = 60000000 uHz */
KUNIT_EXPECT_EQ(test, dm_crtc_state.freesync_config.fixed_refresh_in_uhz,
60000000U);
}
/* Tests for amdgpu_dm_is_dc_timing_adjust_needed() */
/**
* dm_test_dc_timing_adjust_pending - Test a pending hw timing adjust forces true
* @test: The KUnit test context
*/
static void dm_test_dc_timing_adjust_pending(struct kunit *test)
{
struct dm_crtc_state *old_state, *new_state;
struct dc_stream_state *stream;
old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state);
new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state);
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
new_state->stream = stream;
stream->adjust.timing_adjust_pending = 1;
KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_dc_timing_adjust_needed(old_state, new_state));
}
/**
* dm_test_dc_timing_adjust_active_fixed - Test VRR active-fixed forces true
* @test: The KUnit test context
*/
static void dm_test_dc_timing_adjust_active_fixed(struct kunit *test)
{
struct dm_crtc_state *old_state, *new_state;
struct dc_stream_state *stream;
old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state);
new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state);
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
new_state->stream = stream;
new_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED;
KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_dc_timing_adjust_needed(old_state, new_state));
}
/**
* dm_test_dc_timing_adjust_vrr_toggle - Test a change in vrr active state forces true
* @test: The KUnit test context
*/
static void dm_test_dc_timing_adjust_vrr_toggle(struct kunit *test)
{
struct dm_crtc_state *old_state, *new_state;
struct dc_stream_state *stream;
old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state);
new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state);
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
new_state->stream = stream;
old_state->freesync_config.state = VRR_STATE_ACTIVE_VARIABLE;
new_state->freesync_config.state = VRR_STATE_INACTIVE;
KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_dc_timing_adjust_needed(old_state, new_state));
}
/**
* dm_test_dc_timing_adjust_not_needed - Test steady-state timing needs no adjust
* @test: The KUnit test context
*/
static void dm_test_dc_timing_adjust_not_needed(struct kunit *test)
{
struct dm_crtc_state *old_state, *new_state;
struct dc_stream_state *stream;
old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state);
new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state);
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
new_state->stream = stream;
old_state->freesync_config.state = VRR_STATE_INACTIVE;
new_state->freesync_config.state = VRR_STATE_INACTIVE;
KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_dc_timing_adjust_needed(old_state, new_state));
}
/* Tests for amdgpu_dm_get_freesync_config_for_crtc() */
struct dm_test_freesync_ctx {
struct amdgpu_dm_connector *aconnector;
struct dm_crtc_state *crtc_state;
struct dm_connector_state *conn_state;
struct dc_stream_state *stream;
};
static struct dm_test_freesync_ctx *dm_test_freesync_ctx_alloc(struct kunit *test)
{
struct dm_test_freesync_ctx *ctx;
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx);
ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector);
ctx->crtc_state = kunit_kzalloc(test, sizeof(*ctx->crtc_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->crtc_state);
ctx->conn_state = kunit_kzalloc(test, sizeof(*ctx->conn_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->conn_state);
ctx->stream = dm_kunit_alloc_stream(test, NULL);
ctx->conn_state->base.connector = &ctx->aconnector->base;
ctx->aconnector->base.connector_type = DRM_MODE_CONNECTOR_DisplayPort;
ctx->crtc_state->stream = ctx->stream;
/* 1080p60 timing so drm_mode_vrefresh() == 60 */
ctx->crtc_state->base.mode.clock = 148500;
ctx->crtc_state->base.mode.htotal = 2200;
ctx->crtc_state->base.mode.vtotal = 1125;
return ctx;
}
/**
* dm_test_freesync_config_writeback - Test writeback connector is left untouched
* @test: The KUnit test context
*/
static void dm_test_freesync_config_writeback(struct kunit *test)
{
struct dm_test_freesync_ctx *ctx = dm_test_freesync_ctx_alloc(test);
ctx->aconnector->base.connector_type = DRM_MODE_CONNECTOR_WRITEBACK;
ctx->conn_state->freesync_capable = true;
ctx->aconnector->min_vfreq = 48;
ctx->aconnector->max_vfreq = 120;
ctx->crtc_state->vrr_supported = true; /* sentinel: must stay set */
amdgpu_dm_get_freesync_config_for_crtc(ctx->crtc_state, ctx->conn_state);
/* Writeback: early return leaves vrr_supported sentinel untouched */
KUNIT_EXPECT_TRUE(test, ctx->crtc_state->vrr_supported);
}
/**
* dm_test_freesync_config_not_capable - Test a non-freesync sink reports UNSUPPORTED
* @test: The KUnit test context
*/
static void dm_test_freesync_config_not_capable(struct kunit *test)
{
struct dm_test_freesync_ctx *ctx = dm_test_freesync_ctx_alloc(test);
ctx->conn_state->freesync_capable = false;
ctx->aconnector->min_vfreq = 48;
ctx->aconnector->max_vfreq = 120;
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);
}
/**
* dm_test_freesync_config_out_of_range - Test a refresh outside the range is UNSUPPORTED
* @test: The KUnit test context
*/
static void dm_test_freesync_config_out_of_range(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 = 90; /* 60 < 90 -> out of range */
ctx->aconnector->max_vfreq = 120;
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);
}
/**
* dm_test_freesync_config_active_variable - Test vrr_enabled yields ACTIVE_VARIABLE
* @test: The KUnit test context
*/
static void dm_test_freesync_config_active_variable(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 = 48;
ctx->aconnector->max_vfreq = 120;
ctx->crtc_state->base.vrr_enabled = true;
amdgpu_dm_get_freesync_config_for_crtc(ctx->crtc_state, ctx->conn_state);
KUNIT_EXPECT_TRUE(test, ctx->crtc_state->vrr_supported);
KUNIT_EXPECT_TRUE(test, ctx->stream->ignore_msa_timing_param);
KUNIT_EXPECT_EQ(test, (int)ctx->crtc_state->freesync_config.state,
(int)VRR_STATE_ACTIVE_VARIABLE);
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);
KUNIT_EXPECT_TRUE(test, ctx->crtc_state->freesync_config.vsif_supported);
KUNIT_EXPECT_TRUE(test, ctx->crtc_state->freesync_config.btr);
}
/**
* dm_test_freesync_config_inactive - Test supported-but-off yields INACTIVE
* @test: The KUnit test context
*/
static void dm_test_freesync_config_inactive(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 = 48;
ctx->aconnector->max_vfreq = 120;
ctx->crtc_state->base.vrr_enabled = false;
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_fixed - Test freesync-video mode yields ACTIVE_FIXED
* @test: The KUnit test context
*/
static void dm_test_freesync_config_active_fixed(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 = 48;
ctx->aconnector->max_vfreq = 120;
/* 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;
ctx->crtc_state->base.vrr_enabled = true; /* ignored on the fixed path */
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_ACTIVE_FIXED);
KUNIT_EXPECT_EQ(test, ctx->crtc_state->freesync_config.fixed_refresh_in_uhz,
60000000U);
}
/* Tests for amdgpu_dm_reset_freesync_config_for_crtc() */
/**
* dm_test_reset_freesync_config - Test reset clears vrr support and info packet
* @test: The KUnit test context
*/
static void dm_test_reset_freesync_config(struct kunit *test)
{
struct dm_crtc_state *crtc_state;
crtc_state = kunit_kzalloc(test, sizeof(*crtc_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, crtc_state);
crtc_state->vrr_supported = true;
crtc_state->vrr_infopacket.valid = true;
amdgpu_dm_reset_freesync_config_for_crtc(crtc_state);
KUNIT_EXPECT_FALSE(test, crtc_state->vrr_supported);
KUNIT_EXPECT_FALSE(test, crtc_state->vrr_infopacket.valid);
}
static struct kunit_case amdgpu_dm_freesync_tests[] = {
/* amdgpu_dm_is_timing_unchanged_for_freesync */
KUNIT_CASE(dm_test_timing_unchanged_null_args),
KUNIT_CASE(dm_test_timing_unchanged_identical_modes),
KUNIT_CASE(dm_test_timing_unchanged_vrr_shift),
KUNIT_CASE(dm_test_timing_unchanged_clock_changed),
/* amdgpu_dm_set_freesync_fixed_config */
KUNIT_CASE(dm_test_set_freesync_fixed_config_60hz),
/* amdgpu_dm_is_dc_timing_adjust_needed */
KUNIT_CASE(dm_test_dc_timing_adjust_pending),
KUNIT_CASE(dm_test_dc_timing_adjust_active_fixed),
KUNIT_CASE(dm_test_dc_timing_adjust_vrr_toggle),
KUNIT_CASE(dm_test_dc_timing_adjust_not_needed),
/* amdgpu_dm_get_freesync_config_for_crtc */
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_active_variable),
KUNIT_CASE(dm_test_freesync_config_inactive),
KUNIT_CASE(dm_test_freesync_config_active_fixed),
/* amdgpu_dm_reset_freesync_config_for_crtc */
KUNIT_CASE(dm_test_reset_freesync_config),
{}
};
static struct kunit_suite amdgpu_dm_freesync_test_suite = {
.name = "amdgpu_dm_freesync",
.test_cases = amdgpu_dm_freesync_tests,
};
kunit_test_suite(amdgpu_dm_freesync_test_suite);
MODULE_AUTHOR("AMD");
MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_freesync");
MODULE_LICENSE("Dual MIT/GPL");

View File

@ -860,214 +860,6 @@ static void dm_test_scaling_state_underscan_border_changed(struct kunit *test)
KUNIT_EXPECT_TRUE(test, is_scaling_state_different(a, b));
}
/* Tests for is_timing_unchanged_for_freesync() */
/**
* dm_test_timing_unchanged_null_args - Test NULL crtc states return false
* @test: The KUnit test context
*/
static void dm_test_timing_unchanged_null_args(struct kunit *test)
{
struct drm_crtc_state crtc_state = { 0 };
KUNIT_EXPECT_FALSE(test,
is_timing_unchanged_for_freesync(NULL, &crtc_state));
KUNIT_EXPECT_FALSE(test,
is_timing_unchanged_for_freesync(&crtc_state, NULL));
}
/**
* dm_test_timing_unchanged_identical_modes - Test identical modes are not "unchanged"
* @test: The KUnit test context
*
* The helper only returns true when vtotal/vsync shift (vrr) while the rest
* of the timing stays fixed, so identical modes must return false.
*/
static void dm_test_timing_unchanged_identical_modes(struct kunit *test)
{
struct drm_crtc_state old_state = { 0 };
struct drm_crtc_state new_state = { 0 };
old_state.mode.clock = 148500;
old_state.mode.hdisplay = 1920;
old_state.mode.vdisplay = 1080;
old_state.mode.htotal = 2200;
old_state.mode.vtotal = 1125;
new_state.mode = old_state.mode;
KUNIT_EXPECT_FALSE(test,
is_timing_unchanged_for_freesync(&old_state, &new_state));
}
/**
* dm_test_timing_unchanged_vrr_shift - Test vrr-style vtotal/vsync shift is detected
* @test: The KUnit test context
*/
static void dm_test_timing_unchanged_vrr_shift(struct kunit *test)
{
struct drm_crtc_state old_state = { 0 };
struct drm_crtc_state new_state = { 0 };
old_state.mode.clock = 148500;
old_state.mode.hdisplay = 1920;
old_state.mode.vdisplay = 1080;
old_state.mode.htotal = 2200;
old_state.mode.vtotal = 1125;
old_state.mode.hsync_start = 2008;
old_state.mode.vsync_start = 1084;
old_state.mode.hsync_end = 2052;
old_state.mode.vsync_end = 1089;
/* Same horizontal timing, vertical totals/sync shifted by 125 lines */
new_state.mode = old_state.mode;
new_state.mode.vtotal = 1250;
new_state.mode.vsync_start = 1209;
new_state.mode.vsync_end = 1214;
KUNIT_EXPECT_TRUE(test,
is_timing_unchanged_for_freesync(&old_state, &new_state));
}
/**
* dm_test_timing_unchanged_clock_changed - Test pixel clock change returns false
* @test: The KUnit test context
*/
static void dm_test_timing_unchanged_clock_changed(struct kunit *test)
{
struct drm_crtc_state old_state = { 0 };
struct drm_crtc_state new_state = { 0 };
old_state.mode.clock = 148500;
old_state.mode.htotal = 2200;
old_state.mode.vtotal = 1125;
old_state.mode.vsync_start = 1084;
old_state.mode.vsync_end = 1089;
new_state.mode = old_state.mode;
new_state.mode.clock = 297000;
new_state.mode.vtotal = 1250;
new_state.mode.vsync_start = 1209;
new_state.mode.vsync_end = 1214;
KUNIT_EXPECT_FALSE(test,
is_timing_unchanged_for_freesync(&old_state, &new_state));
}
/* Tests for set_freesync_fixed_config() */
/**
* dm_test_set_freesync_fixed_config_60hz - Test fixed refresh computed for 1080p60
* @test: The KUnit test context
*/
static void dm_test_set_freesync_fixed_config_60hz(struct kunit *test)
{
struct dm_crtc_state dm_crtc_state = { 0 };
dm_crtc_state.base.mode.clock = 148500;
dm_crtc_state.base.mode.htotal = 2200;
dm_crtc_state.base.mode.vtotal = 1125;
set_freesync_fixed_config(&dm_crtc_state);
KUNIT_EXPECT_EQ(test, (int)dm_crtc_state.freesync_config.state,
(int)VRR_STATE_ACTIVE_FIXED);
/* 148500 kHz / (2200 * 1125) = 60 Hz = 60000000 uHz */
KUNIT_EXPECT_EQ(test, dm_crtc_state.freesync_config.fixed_refresh_in_uhz,
60000000U);
}
/* Tests for is_dc_timing_adjust_needed() */
/**
* dm_test_dc_timing_adjust_pending - Test a pending hw timing adjust forces true
* @test: The KUnit test context
*/
static void dm_test_dc_timing_adjust_pending(struct kunit *test)
{
struct dm_crtc_state *old_state, *new_state;
struct dc_stream_state *stream;
old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state);
new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state);
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
new_state->stream = stream;
stream->adjust.timing_adjust_pending = 1;
KUNIT_EXPECT_TRUE(test, is_dc_timing_adjust_needed(old_state, new_state));
}
/**
* dm_test_dc_timing_adjust_active_fixed - Test VRR active-fixed forces true
* @test: The KUnit test context
*/
static void dm_test_dc_timing_adjust_active_fixed(struct kunit *test)
{
struct dm_crtc_state *old_state, *new_state;
struct dc_stream_state *stream;
old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state);
new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state);
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
new_state->stream = stream;
new_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED;
KUNIT_EXPECT_TRUE(test, is_dc_timing_adjust_needed(old_state, new_state));
}
/**
* dm_test_dc_timing_adjust_vrr_toggle - Test a change in vrr active state forces true
* @test: The KUnit test context
*/
static void dm_test_dc_timing_adjust_vrr_toggle(struct kunit *test)
{
struct dm_crtc_state *old_state, *new_state;
struct dc_stream_state *stream;
old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state);
new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state);
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
new_state->stream = stream;
old_state->freesync_config.state = VRR_STATE_ACTIVE_VARIABLE;
new_state->freesync_config.state = VRR_STATE_INACTIVE;
KUNIT_EXPECT_TRUE(test, is_dc_timing_adjust_needed(old_state, new_state));
}
/**
* dm_test_dc_timing_adjust_not_needed - Test steady-state timing needs no adjust
* @test: The KUnit test context
*/
static void dm_test_dc_timing_adjust_not_needed(struct kunit *test)
{
struct dm_crtc_state *old_state, *new_state;
struct dc_stream_state *stream;
old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state);
new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state);
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
new_state->stream = stream;
old_state->freesync_config.state = VRR_STATE_INACTIVE;
new_state->freesync_config.state = VRR_STATE_INACTIVE;
KUNIT_EXPECT_FALSE(test, is_dc_timing_adjust_needed(old_state, new_state));
}
/* Tests for set_multisync_trigger_params() */
/**
@ -1435,194 +1227,6 @@ static void dm_test_cp_diff_desired_to_undesired(struct kunit *test)
KUNIT_EXPECT_TRUE(test, dm_test_cp_diff(ctx));
}
/* Tests for get_freesync_config_for_crtc() */
struct dm_test_freesync_ctx {
struct amdgpu_dm_connector *aconnector;
struct dm_crtc_state *crtc_state;
struct dm_connector_state *conn_state;
struct dc_stream_state *stream;
};
static struct dm_test_freesync_ctx *dm_test_freesync_ctx_alloc(struct kunit *test)
{
struct dm_test_freesync_ctx *ctx;
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx);
ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector);
ctx->crtc_state = kunit_kzalloc(test, sizeof(*ctx->crtc_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->crtc_state);
ctx->conn_state = kunit_kzalloc(test, sizeof(*ctx->conn_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->conn_state);
ctx->stream = dm_kunit_alloc_stream(test, NULL);
ctx->conn_state->base.connector = &ctx->aconnector->base;
ctx->aconnector->base.connector_type = DRM_MODE_CONNECTOR_DisplayPort;
ctx->crtc_state->stream = ctx->stream;
/* 1080p60 timing so drm_mode_vrefresh() == 60 */
ctx->crtc_state->base.mode.clock = 148500;
ctx->crtc_state->base.mode.htotal = 2200;
ctx->crtc_state->base.mode.vtotal = 1125;
return ctx;
}
/**
* dm_test_freesync_config_writeback - Test writeback connector is left untouched
* @test: The KUnit test context
*/
static void dm_test_freesync_config_writeback(struct kunit *test)
{
struct dm_test_freesync_ctx *ctx = dm_test_freesync_ctx_alloc(test);
ctx->aconnector->base.connector_type = DRM_MODE_CONNECTOR_WRITEBACK;
ctx->conn_state->freesync_capable = true;
ctx->aconnector->min_vfreq = 48;
ctx->aconnector->max_vfreq = 120;
ctx->crtc_state->vrr_supported = true; /* sentinel: must stay set */
get_freesync_config_for_crtc(ctx->crtc_state, ctx->conn_state);
/* Writeback: early return leaves vrr_supported sentinel untouched */
KUNIT_EXPECT_TRUE(test, ctx->crtc_state->vrr_supported);
}
/**
* dm_test_freesync_config_not_capable - Test a non-freesync sink reports UNSUPPORTED
* @test: The KUnit test context
*/
static void dm_test_freesync_config_not_capable(struct kunit *test)
{
struct dm_test_freesync_ctx *ctx = dm_test_freesync_ctx_alloc(test);
ctx->conn_state->freesync_capable = false;
ctx->aconnector->min_vfreq = 48;
ctx->aconnector->max_vfreq = 120;
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);
}
/**
* dm_test_freesync_config_out_of_range - Test a refresh outside the range is UNSUPPORTED
* @test: The KUnit test context
*/
static void dm_test_freesync_config_out_of_range(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 = 90; /* 60 < 90 -> out of range */
ctx->aconnector->max_vfreq = 120;
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);
}
/**
* dm_test_freesync_config_active_variable - Test vrr_enabled yields ACTIVE_VARIABLE
* @test: The KUnit test context
*/
static void dm_test_freesync_config_active_variable(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 = 48;
ctx->aconnector->max_vfreq = 120;
ctx->crtc_state->base.vrr_enabled = true;
get_freesync_config_for_crtc(ctx->crtc_state, ctx->conn_state);
KUNIT_EXPECT_TRUE(test, ctx->crtc_state->vrr_supported);
KUNIT_EXPECT_TRUE(test, ctx->stream->ignore_msa_timing_param);
KUNIT_EXPECT_EQ(test, (int)ctx->crtc_state->freesync_config.state,
(int)VRR_STATE_ACTIVE_VARIABLE);
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);
KUNIT_EXPECT_TRUE(test, ctx->crtc_state->freesync_config.vsif_supported);
KUNIT_EXPECT_TRUE(test, ctx->crtc_state->freesync_config.btr);
}
/**
* dm_test_freesync_config_inactive - Test supported-but-off yields INACTIVE
* @test: The KUnit test context
*/
static void dm_test_freesync_config_inactive(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 = 48;
ctx->aconnector->max_vfreq = 120;
ctx->crtc_state->base.vrr_enabled = false;
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_fixed - Test freesync-video mode yields ACTIVE_FIXED
* @test: The KUnit test context
*/
static void dm_test_freesync_config_active_fixed(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 = 48;
ctx->aconnector->max_vfreq = 120;
/* 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;
ctx->crtc_state->base.vrr_enabled = true; /* ignored on the fixed path */
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_ACTIVE_FIXED);
KUNIT_EXPECT_EQ(test, ctx->crtc_state->freesync_config.fixed_refresh_in_uhz,
60000000U);
}
/* Tests for reset_freesync_config_for_crtc() */
/**
* dm_test_reset_freesync_config - Test reset clears vrr support and info packet
* @test: The KUnit test context
*/
static void dm_test_reset_freesync_config(struct kunit *test)
{
struct dm_crtc_state *crtc_state;
crtc_state = kunit_kzalloc(test, sizeof(*crtc_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, crtc_state);
crtc_state->vrr_supported = true;
crtc_state->vrr_infopacket.valid = true;
reset_freesync_config_for_crtc(crtc_state);
KUNIT_EXPECT_FALSE(test, crtc_state->vrr_supported);
KUNIT_EXPECT_FALSE(test, crtc_state->vrr_infopacket.valid);
}
/* Tests for dm_enable_per_frame_crtc_master_sync() */
/**
@ -1818,18 +1422,6 @@ static struct kunit_case amdgpu_dm_tests[] = {
KUNIT_CASE(dm_test_scaling_state_underscan_enabled),
KUNIT_CASE(dm_test_scaling_state_underscan_disabled),
KUNIT_CASE(dm_test_scaling_state_underscan_border_changed),
/* is_timing_unchanged_for_freesync */
KUNIT_CASE(dm_test_timing_unchanged_null_args),
KUNIT_CASE(dm_test_timing_unchanged_identical_modes),
KUNIT_CASE(dm_test_timing_unchanged_vrr_shift),
KUNIT_CASE(dm_test_timing_unchanged_clock_changed),
/* set_freesync_fixed_config */
KUNIT_CASE(dm_test_set_freesync_fixed_config_60hz),
/* is_dc_timing_adjust_needed */
KUNIT_CASE(dm_test_dc_timing_adjust_pending),
KUNIT_CASE(dm_test_dc_timing_adjust_active_fixed),
KUNIT_CASE(dm_test_dc_timing_adjust_vrr_toggle),
KUNIT_CASE(dm_test_dc_timing_adjust_not_needed),
/* set_multisync_trigger_params */
KUNIT_CASE(dm_test_multisync_trigger_disabled),
KUNIT_CASE(dm_test_multisync_trigger_rising),
@ -1849,15 +1441,6 @@ static struct kunit_case amdgpu_dm_tests[] = {
KUNIT_CASE(dm_test_cp_diff_s3_undesired_to_enabled),
KUNIT_CASE(dm_test_cp_diff_desired_to_enabled),
KUNIT_CASE(dm_test_cp_diff_desired_to_undesired),
/* get_freesync_config_for_crtc */
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_active_variable),
KUNIT_CASE(dm_test_freesync_config_inactive),
KUNIT_CASE(dm_test_freesync_config_active_fixed),
/* reset_freesync_config_for_crtc */
KUNIT_CASE(dm_test_reset_freesync_config),
/* dm_enable_per_frame_crtc_master_sync */
KUNIT_CASE(dm_test_per_frame_master_sync_single_stream),
KUNIT_CASE(dm_test_per_frame_master_sync_two_streams),