| 1 | |
| 2 | |
| 3 | * Copyright 2026 Advanced Micro Devices, Inc.
|
| 4 | *
|
| 5 | * Permission is hereby granted, free of charge, to any person obtaining a
|
| 6 | * copy of this software and associated documentation files (the "Software"),
|
| 7 | * to deal in the Software without restriction, including without limitation
|
| 8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
| 9 | * and/or sell copies of the Software, and to permit persons to whom the
|
| 10 | * Software is furnished to do so, subject to the following conditions:
|
| 11 | *
|
| 12 | * The above copyright notice and this permission notice shall be included in
|
| 13 | * all copies or substantial portions of the Software.
|
| 14 | *
|
| 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
| 18 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
| 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
| 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
| 21 | * OTHER DEALINGS IN THE SOFTWARE.
|
| 22 | *
|
| 23 | * Authors: AMD
|
| 24 | *
|
| 25 |
|
| 26 |
|
| 27 | #include <drm/drm_atomic.h>
|
| 28 | #include <drm/drm_modes.h>
|
| 29 | #include <drm/drm_vblank.h>
|
| 30 |
|
| 31 | #include "dc.h"
|
| 32 | #include "amdgpu.h"
|
| 33 | #include "amdgpu_dm.h"
|
| 34 | #include "amdgpu_dm_crtc.h"
|
| 35 | #include "amdgpu_dm_psr.h"
|
| 36 | #include "amdgpu_dm_replay.h"
|
| 37 | #include "amdgpu_dm_freesync.h"
|
| 38 | #include "dm_helpers.h"
|
| 39 | #include "modules/inc/mod_freesync.h"
|
| 40 |
|
| 41 | bool amdgpu_dm_is_dc_timing_adjust_needed(struct dm_crtc_state *old_state,
|
| 42 | struct dm_crtc_state *new_state)
|
| 43 | {
|
| 44 | if (new_state->stream->adjust.timing_adjust_pending)
|
| 45 | return true;
|
| 46 | if (new_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED)
|
| 47 | return true;
|
| 48 | else if (amdgpu_dm_crtc_vrr_active(old_state) != amdgpu_dm_crtc_vrr_active(new_state))
|
| 49 | return true;
|
| 50 | else
|
| 51 | return false;
|
| 52 | }
|
| 53 | EXPORT_IF_KUNIT(amdgpu_dm_is_dc_timing_adjust_needed);
|
| 54 |
|
| 55 | bool
|
| 56 | amdgpu_dm_is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
|
| 57 | struct drm_crtc_state *new_crtc_state)
|
| 58 | {
|
| 59 | const struct drm_display_mode *old_mode, *new_mode;
|
| 60 |
|
| 61 | if (!old_crtc_state || !new_crtc_state)
|
| 62 | return false;
|
| 63 |
|
| 64 | old_mode = &old_crtc_state->mode;
|
| 65 | new_mode = &new_crtc_state->mode;
|
| 66 |
|
| 67 | if (old_mode->clock == new_mode->clock &&
|
| 68 | old_mode->hdisplay == new_mode->hdisplay &&
|
| 69 | old_mode->vdisplay == new_mode->vdisplay &&
|
| 70 | old_mode->htotal == new_mode->htotal &&
|
| 71 | old_mode->vtotal != new_mode->vtotal &&
|
| 72 | old_mode->hsync_start == new_mode->hsync_start &&
|
| 73 | old_mode->vsync_start != new_mode->vsync_start &&
|
| 74 | old_mode->hsync_end == new_mode->hsync_end &&
|
| 75 | old_mode->vsync_end != new_mode->vsync_end &&
|
| 76 | old_mode->hskew == new_mode->hskew &&
|
| 77 | old_mode->vscan == new_mode->vscan &&
|
| 78 | (old_mode->vsync_end - old_mode->vsync_start) ==
|
| 79 | (new_mode->vsync_end - new_mode->vsync_start))
|
| 80 | return true;
|
| 81 |
|
| 82 | return false;
|
| 83 | }
|
| 84 | EXPORT_IF_KUNIT(amdgpu_dm_is_timing_unchanged_for_freesync);
|
| 85 |
|
| 86 | void amdgpu_dm_set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state)
|
| 87 | {
|
| 88 | u64 num, den, res;
|
| 89 | struct drm_crtc_state *new_crtc_state = &dm_new_crtc_state->base;
|
| 90 |
|
| 91 | dm_new_crtc_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED;
|
| 92 |
|
| 93 | num = (unsigned long long)new_crtc_state->mode.clock * 1000 * 1000000;
|
| 94 | den = (unsigned long long)new_crtc_state->mode.htotal *
|
| 95 | (unsigned long long)new_crtc_state->mode.vtotal;
|
| 96 |
|
| 97 | res = div_u64(num, den);
|
| 98 | dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = res;
|
| 99 | }
|
| 100 | EXPORT_IF_KUNIT(amdgpu_dm_set_freesync_fixed_config);
|
| 101 |
|
| 102 | void amdgpu_dm_reset_freesync_config_for_crtc(
|
| 103 | struct dm_crtc_state *new_crtc_state)
|
| 104 | {
|
| 105 | new_crtc_state->vrr_supported = false;
|
| 106 |
|
| 107 | memset(&new_crtc_state->vrr_infopacket, 0,
|
| 108 | sizeof(new_crtc_state->vrr_infopacket));
|
| 109 | }
|
| 110 | EXPORT_IF_KUNIT(amdgpu_dm_reset_freesync_config_for_crtc);
|
| 111 |
|
| 112 | void amdgpu_dm_get_freesync_config_for_crtc(
|
| 113 | struct dm_crtc_state *new_crtc_state,
|
| 114 | struct dm_connector_state *new_con_state)
|
| 115 | {
|
| 116 | struct mod_freesync_config config = {0};
|
| 117 | struct amdgpu_dm_connector *aconnector;
|
| 118 | struct drm_display_mode *mode = &new_crtc_state->base.mode;
|
| 119 | int vrefresh = drm_mode_vrefresh(mode);
|
| 120 | bool fs_vid_mode = false;
|
| 121 |
|
| 122 | if (new_con_state->base.connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
|
| 123 | return;
|
| 124 |
|
| 125 | aconnector = to_amdgpu_dm_connector(new_con_state->base.connector);
|
| 126 |
|
| 127 | new_crtc_state->vrr_supported = new_con_state->freesync_capable &&
|
| 128 | vrefresh >= aconnector->min_vfreq &&
|
| 129 | vrefresh <= aconnector->max_vfreq;
|
| 130 |
|
| 131 | if (new_crtc_state->vrr_supported) {
|
| 132 | new_crtc_state->stream->ignore_msa_timing_param = true;
|
| 133 | fs_vid_mode = new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED;
|
| 134 |
|
| 135 | config.min_refresh_in_uhz = aconnector->min_vfreq * 1000000;
|
| 136 | config.max_refresh_in_uhz = aconnector->max_vfreq * 1000000;
|
| 137 | config.vsif_supported = true;
|
| 138 | config.btr = true;
|
| 139 |
|
| 140 | if (fs_vid_mode) {
|
| 141 | config.state = VRR_STATE_ACTIVE_FIXED;
|
| 142 | config.fixed_refresh_in_uhz = new_crtc_state->freesync_config.fixed_refresh_in_uhz;
|
| 143 | goto out;
|
| 144 | } else if (new_crtc_state->base.vrr_enabled) {
|
| 145 | config.state = VRR_STATE_ACTIVE_VARIABLE;
|
| 146 | } else {
|
| 147 | config.state = VRR_STATE_INACTIVE;
|
| 148 | }
|
| 149 | } else {
|
| 150 | config.state = VRR_STATE_UNSUPPORTED;
|
| 151 | }
|
| 152 | out:
|
| 153 | new_crtc_state->freesync_config = config;
|
| 154 | }
|
| 155 | EXPORT_IF_KUNIT(amdgpu_dm_get_freesync_config_for_crtc);
|
| 156 |
|
| 157 | void amdgpu_dm_update_freesync_state_on_stream(
|
| 158 | struct amdgpu_display_manager *dm,
|
| 159 | struct dm_crtc_state *new_crtc_state,
|
| 160 | struct dc_stream_state *new_stream,
|
| 161 | struct dc_plane_state *surface,
|
| 162 | u32 flip_timestamp_in_us)
|
| 163 | {
|
| 164 | struct mod_vrr_params vrr_params;
|
| 165 | struct dc_info_packet vrr_infopacket = {0};
|
| 166 | struct amdgpu_device *adev = dm->adev;
|
| 167 | struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc);
|
| 168 | unsigned long flags;
|
| 169 | bool pack_sdp_v1_3 = false;
|
| 170 | struct amdgpu_dm_connector *aconn;
|
| 171 | enum vrr_packet_type packet_type = PACKET_TYPE_VRR;
|
| 172 |
|
| 173 | if (!new_stream)
|
| 174 | return;
|
| 175 |
|
| 176 | |
| 177 | * TODO: Determine why min/max totals and vrefresh can be 0 here.
|
| 178 | * For now it's sufficient to just guard against these conditions.
|
| 179 |
|
| 180 |
|
| 181 | if (!new_stream->timing.h_total || !new_stream->timing.v_total)
|
| 182 | return;
|
| 183 |
|
| 184 | spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
|
| 185 | vrr_params = acrtc->dm_irq_params.vrr_params;
|
| 186 |
|
| 187 | if (surface) {
|
| 188 | mod_freesync_handle_preflip(
|
| 189 | dm->freesync_module,
|
| 190 | surface,
|
| 191 | new_stream,
|
| 192 | flip_timestamp_in_us,
|
| 193 | &vrr_params);
|
| 194 |
|
| 195 | if (adev->family < AMDGPU_FAMILY_AI &&
|
| 196 | amdgpu_dm_crtc_vrr_active(new_crtc_state)) {
|
| 197 | mod_freesync_handle_v_update(dm->freesync_module,
|
| 198 | new_stream, &vrr_params);
|
| 199 |
|
| 200 |
|
| 201 | dc_stream_adjust_vmin_vmax(dm->dc,
|
| 202 | new_crtc_state->stream,
|
| 203 | &vrr_params.adjust);
|
| 204 | }
|
| 205 | }
|
| 206 |
|
| 207 | aconn = (struct amdgpu_dm_connector *)new_stream->dm_stream_context;
|
| 208 |
|
| 209 | if (aconn && (aconn->as_type == FREESYNC_TYPE_PCON_IN_WHITELIST || aconn->vsdb_info.replay_mode)) {
|
| 210 | pack_sdp_v1_3 = aconn->pack_sdp_v1_3;
|
| 211 |
|
| 212 | if (aconn->vsdb_info.amd_vsdb_version == 1)
|
| 213 | packet_type = PACKET_TYPE_FS_V1;
|
| 214 | else if (aconn->vsdb_info.amd_vsdb_version == 2)
|
| 215 | packet_type = PACKET_TYPE_FS_V2;
|
| 216 | else if (aconn->vsdb_info.amd_vsdb_version == 3)
|
| 217 | packet_type = PACKET_TYPE_FS_V3;
|
| 218 |
|
| 219 | mod_build_adaptive_sync_infopacket(new_stream, aconn->as_type, NULL,
|
| 220 | &new_stream->adaptive_sync_infopacket);
|
| 221 | }
|
| 222 |
|
| 223 | mod_freesync_build_vrr_infopacket(
|
| 224 | dm->freesync_module,
|
| 225 | new_stream,
|
| 226 | &vrr_params,
|
| 227 | packet_type,
|
| 228 | TRANSFER_FUNC_UNKNOWN,
|
| 229 | &vrr_infopacket,
|
| 230 | pack_sdp_v1_3);
|
| 231 |
|
| 232 | new_crtc_state->freesync_vrr_info_changed |=
|
| 233 | (memcmp(&new_crtc_state->vrr_infopacket,
|
| 234 | &vrr_infopacket,
|
| 235 | sizeof(vrr_infopacket)) != 0);
|
| 236 |
|
| 237 | acrtc->dm_irq_params.vrr_params = vrr_params;
|
| 238 | new_crtc_state->vrr_infopacket = vrr_infopacket;
|
| 239 |
|
| 240 | new_stream->vrr_infopacket = vrr_infopacket;
|
| 241 | new_stream->allow_freesync = mod_freesync_get_freesync_enabled(&vrr_params);
|
| 242 |
|
| 243 | if (new_crtc_state->freesync_vrr_info_changed)
|
| 244 | drm_dbg_kms(adev_to_drm(adev), "VRR packet update: crtc=%u enabled=%d state=%d",
|
| 245 | new_crtc_state->base.crtc->base.id,
|
| 246 | (int)new_crtc_state->base.vrr_enabled,
|
| 247 | (int)vrr_params.state);
|
| 248 |
|
| 249 | spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
|
| 250 | }
|
| 251 |
|
| 252 | void amdgpu_dm_update_stream_irq_parameters(
|
| 253 | struct amdgpu_display_manager *dm,
|
| 254 | struct dm_crtc_state *new_crtc_state)
|
| 255 | {
|
| 256 | struct dc_stream_state *new_stream = new_crtc_state->stream;
|
| 257 | struct mod_vrr_params vrr_params;
|
| 258 | struct mod_freesync_config config = new_crtc_state->freesync_config;
|
| 259 | struct amdgpu_device *adev = dm->adev;
|
| 260 | struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc);
|
| 261 | unsigned long flags;
|
| 262 |
|
| 263 | if (!new_stream)
|
| 264 | return;
|
| 265 |
|
| 266 | |
| 267 | * TODO: Determine why min/max totals and vrefresh can be 0 here.
|
| 268 | * For now it's sufficient to just guard against these conditions.
|
| 269 |
|
| 270 | if (!new_stream->timing.h_total || !new_stream->timing.v_total)
|
| 271 | return;
|
| 272 |
|
| 273 | spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
|
| 274 | vrr_params = acrtc->dm_irq_params.vrr_params;
|
| 275 |
|
| 276 | if (new_crtc_state->vrr_supported &&
|
| 277 | config.min_refresh_in_uhz &&
|
| 278 | config.max_refresh_in_uhz) {
|
| 279 | |
| 280 | * if freesync compatible mode was set, config.state will be set
|
| 281 | * in atomic check
|
| 282 |
|
| 283 | if (config.state == VRR_STATE_ACTIVE_FIXED && config.fixed_refresh_in_uhz &&
|
| 284 | (!drm_atomic_crtc_needs_modeset(&new_crtc_state->base) ||
|
| 285 | new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED)) {
|
| 286 | vrr_params.max_refresh_in_uhz = config.max_refresh_in_uhz;
|
| 287 | vrr_params.min_refresh_in_uhz = config.min_refresh_in_uhz;
|
| 288 | vrr_params.fixed_refresh_in_uhz = config.fixed_refresh_in_uhz;
|
| 289 | vrr_params.state = VRR_STATE_ACTIVE_FIXED;
|
| 290 | } else {
|
| 291 | config.state = new_crtc_state->base.vrr_enabled ?
|
| 292 | VRR_STATE_ACTIVE_VARIABLE :
|
| 293 | VRR_STATE_INACTIVE;
|
| 294 | }
|
| 295 | } else {
|
| 296 | config.state = VRR_STATE_UNSUPPORTED;
|
| 297 | }
|
| 298 |
|
| 299 | mod_freesync_build_vrr_params(dm->freesync_module,
|
| 300 | new_stream,
|
| 301 | &config, &vrr_params);
|
| 302 |
|
| 303 | new_crtc_state->freesync_config = config;
|
| 304 |
|
| 305 | acrtc->dm_irq_params.freesync_config = config;
|
| 306 | acrtc->dm_irq_params.active_planes = new_crtc_state->active_planes;
|
| 307 | acrtc->dm_irq_params.vrr_params = vrr_params;
|
| 308 | spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
|
| 309 | }
|
| 310 |
|
| 311 | void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm,
|
| 312 | struct dm_crtc_state *old_state,
|
| 313 | struct dm_crtc_state *new_state)
|
| 314 | {
|
| 315 | struct amdgpu_device *adev = dm->adev;
|
| 316 | bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state);
|
| 317 | bool new_vrr_active = amdgpu_dm_crtc_vrr_active(new_state);
|
| 318 |
|
| 319 |
|
| 320 | bool vrr_gates_vupdate = amdgpu_ip_version(adev, DCE_HWIP, 0) == 0;
|
| 321 |
|
| 322 | if (!old_vrr_active && new_vrr_active) {
|
| 323 | |
| 324 | * While VRR is active, we must not disable vblank irq, as a
|
| 325 | * reenable after disable would compute bogus vblank/pflip
|
| 326 | * timestamps if it likely happened inside display front-porch.
|
| 327 | *
|
| 328 | * We also need vupdate irq for the actual core vblank handling
|
| 329 | * at end of vblank.
|
| 330 |
|
| 331 | if (vrr_gates_vupdate)
|
| 332 | WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
|
| 333 | WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0);
|
| 334 | drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR off->on: Get vblank ref\n",
|
| 335 | __func__, new_state->base.crtc->base.id);
|
| 336 |
|
| 337 | scoped_guard(mutex, &dm->dc_lock) {
|
| 338 | dc_exit_ips_for_hw_access(dm->dc);
|
| 339 | amdgpu_dm_psr_set_event(dm, new_state->stream, true,
|
| 340 | psr_event_vrr_transition, true);
|
| 341 | amdgpu_dm_replay_set_event(dm, new_state->stream, true,
|
| 342 | replay_event_vrr, true);
|
| 343 | }
|
| 344 | } else if (old_vrr_active && !new_vrr_active) {
|
| 345 | |
| 346 | * Allow vblank irq disable again for fixed refresh rate.
|
| 347 |
|
| 348 | if (vrr_gates_vupdate)
|
| 349 | WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0);
|
| 350 | drm_crtc_vblank_put(new_state->base.crtc);
|
| 351 | drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR on->off: Drop vblank ref\n",
|
| 352 | __func__, new_state->base.crtc->base.id);
|
| 353 |
|
| 354 | scoped_guard(mutex, &dm->dc_lock) {
|
| 355 | dc_exit_ips_for_hw_access(dm->dc);
|
| 356 | amdgpu_dm_psr_set_event(dm, new_state->stream, false,
|
| 357 | psr_event_vrr_transition, false);
|
| 358 | amdgpu_dm_replay_set_event(dm, new_state->stream, false,
|
| 359 | replay_event_vrr, false);
|
| 360 | }
|
| 361 | }
|
| 362 | }
|