1fb4d8502Sjsg /* 2f005ef32Sjsg * Copyright 2016-2023 Advanced Micro Devices, Inc. 3fb4d8502Sjsg * 4fb4d8502Sjsg * Permission is hereby granted, free of charge, to any person obtaining a 5fb4d8502Sjsg * copy of this software and associated documentation files (the "Software"), 6fb4d8502Sjsg * to deal in the Software without restriction, including without limitation 7fb4d8502Sjsg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8fb4d8502Sjsg * and/or sell copies of the Software, and to permit persons to whom the 9fb4d8502Sjsg * Software is furnished to do so, subject to the following conditions: 10fb4d8502Sjsg * 11fb4d8502Sjsg * The above copyright notice and this permission notice shall be included in 12fb4d8502Sjsg * all copies or substantial portions of the Software. 13fb4d8502Sjsg * 14fb4d8502Sjsg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15fb4d8502Sjsg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16fb4d8502Sjsg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17fb4d8502Sjsg * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18fb4d8502Sjsg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19fb4d8502Sjsg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20fb4d8502Sjsg * OTHER DEALINGS IN THE SOFTWARE. 21fb4d8502Sjsg * 22fb4d8502Sjsg * Authors: AMD 23fb4d8502Sjsg * 24fb4d8502Sjsg */ 25fb4d8502Sjsg 26fb4d8502Sjsg #include "dm_services.h" 27fb4d8502Sjsg #include "dc.h" 28fb4d8502Sjsg #include "mod_freesync.h" 29fb4d8502Sjsg #include "core_types.h" 30fb4d8502Sjsg 31fb4d8502Sjsg #define MOD_FREESYNC_MAX_CONCURRENT_STREAMS 32 32fb4d8502Sjsg 33ad8b1aafSjsg #define MIN_REFRESH_RANGE 10 34fb4d8502Sjsg /* Refresh rate ramp at a fixed rate of 65 Hz/second */ 35fb4d8502Sjsg #define STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME ((1000 / 60) * 65) 36fb4d8502Sjsg /* Number of elements in the render times cache array */ 37fb4d8502Sjsg #define RENDER_TIMES_MAX_COUNT 10 38c349dbc7Sjsg /* Threshold to exit/exit BTR (to avoid frequent enter-exits at the lower limit) */ 39c349dbc7Sjsg #define BTR_MAX_MARGIN 2500 40c349dbc7Sjsg /* Threshold to change BTR multiplier (to avoid frequent changes) */ 41c349dbc7Sjsg #define BTR_DRIFT_MARGIN 2000 42c349dbc7Sjsg /* Threshold to exit fixed refresh rate */ 435ca02815Sjsg #define FIXED_REFRESH_EXIT_MARGIN_IN_HZ 1 44fb4d8502Sjsg /* Number of consecutive frames to check before entering/exiting fixed refresh */ 45fb4d8502Sjsg #define FIXED_REFRESH_ENTER_FRAME_COUNT 5 465ca02815Sjsg #define FIXED_REFRESH_EXIT_FRAME_COUNT 10 471bb76ff1Sjsg /* Flip interval workaround constants */ 481bb76ff1Sjsg #define VSYNCS_BETWEEN_FLIP_THRESHOLD 2 491bb76ff1Sjsg #define FREESYNC_CONSEC_FLIP_AFTER_VSYNC 5 501bb76ff1Sjsg #define FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US 500 51fb4d8502Sjsg 52fb4d8502Sjsg struct core_freesync { 53fb4d8502Sjsg struct mod_freesync public; 54fb4d8502Sjsg struct dc *dc; 55fb4d8502Sjsg }; 56fb4d8502Sjsg 57fb4d8502Sjsg #define MOD_FREESYNC_TO_CORE(mod_freesync)\ 58fb4d8502Sjsg container_of(mod_freesync, struct core_freesync, public) 59fb4d8502Sjsg 60fb4d8502Sjsg struct mod_freesync *mod_freesync_create(struct dc *dc) 61fb4d8502Sjsg { 62fb4d8502Sjsg struct core_freesync *core_freesync = 63fb4d8502Sjsg kzalloc(sizeof(struct core_freesync), GFP_KERNEL); 64fb4d8502Sjsg 65fb4d8502Sjsg if (core_freesync == NULL) 66fb4d8502Sjsg goto fail_alloc_context; 67fb4d8502Sjsg 68fb4d8502Sjsg if (dc == NULL) 69fb4d8502Sjsg goto fail_construct; 70fb4d8502Sjsg 71fb4d8502Sjsg core_freesync->dc = dc; 72fb4d8502Sjsg return &core_freesync->public; 73fb4d8502Sjsg 74fb4d8502Sjsg fail_construct: 75fb4d8502Sjsg kfree(core_freesync); 76fb4d8502Sjsg 77fb4d8502Sjsg fail_alloc_context: 78fb4d8502Sjsg return NULL; 79fb4d8502Sjsg } 80fb4d8502Sjsg 81fb4d8502Sjsg void mod_freesync_destroy(struct mod_freesync *mod_freesync) 82fb4d8502Sjsg { 83c349dbc7Sjsg struct core_freesync *core_freesync = NULL; 84c349dbc7Sjsg if (mod_freesync == NULL) 85c349dbc7Sjsg return; 86c349dbc7Sjsg core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); 87fb4d8502Sjsg kfree(core_freesync); 88fb4d8502Sjsg } 89fb4d8502Sjsg 905ca02815Sjsg #if 0 /* Unused currently */ 91c349dbc7Sjsg static unsigned int calc_refresh_in_uhz_from_duration( 92c349dbc7Sjsg unsigned int duration_in_ns) 93fb4d8502Sjsg { 94c349dbc7Sjsg unsigned int refresh_in_uhz = 95c349dbc7Sjsg ((unsigned int)(div64_u64((1000000000ULL * 1000000), 96c349dbc7Sjsg duration_in_ns))); 97c349dbc7Sjsg return refresh_in_uhz; 98c349dbc7Sjsg } 99c349dbc7Sjsg #endif 100fb4d8502Sjsg 101c349dbc7Sjsg static unsigned int calc_duration_in_us_from_refresh_in_uhz( 102c349dbc7Sjsg unsigned int refresh_in_uhz) 103fb4d8502Sjsg { 104c349dbc7Sjsg unsigned int duration_in_us = 105c349dbc7Sjsg ((unsigned int)(div64_u64((1000000000ULL * 1000), 106c349dbc7Sjsg refresh_in_uhz))); 107c349dbc7Sjsg return duration_in_us; 108fb4d8502Sjsg } 109fb4d8502Sjsg 110c349dbc7Sjsg static unsigned int calc_duration_in_us_from_v_total( 111c349dbc7Sjsg const struct dc_stream_state *stream, 112c349dbc7Sjsg const struct mod_vrr_params *in_vrr, 113c349dbc7Sjsg unsigned int v_total) 114fb4d8502Sjsg { 115c349dbc7Sjsg unsigned int duration_in_us = 116c349dbc7Sjsg (unsigned int)(div64_u64(((unsigned long long)(v_total) 117c349dbc7Sjsg * 10000) * stream->timing.h_total, 118c349dbc7Sjsg stream->timing.pix_clk_100hz)); 119fb4d8502Sjsg 120c349dbc7Sjsg return duration_in_us; 121fb4d8502Sjsg } 122fb4d8502Sjsg 1235ca02815Sjsg unsigned int mod_freesync_calc_v_total_from_refresh( 124c349dbc7Sjsg const struct dc_stream_state *stream, 125c349dbc7Sjsg unsigned int refresh_in_uhz) 126fb4d8502Sjsg { 127c349dbc7Sjsg unsigned int v_total; 128c349dbc7Sjsg unsigned int frame_duration_in_ns; 129fb4d8502Sjsg 130c349dbc7Sjsg frame_duration_in_ns = 131c349dbc7Sjsg ((unsigned int)(div64_u64((1000000000ULL * 1000000), 132c349dbc7Sjsg refresh_in_uhz))); 133fb4d8502Sjsg 134c349dbc7Sjsg v_total = div64_u64(div64_u64(((unsigned long long)( 135c349dbc7Sjsg frame_duration_in_ns) * (stream->timing.pix_clk_100hz / 10)), 136*cad63ac9Sjsg stream->timing.h_total) + 500000, 1000000); 137fb4d8502Sjsg 138c349dbc7Sjsg /* v_total cannot be less than nominal */ 139c349dbc7Sjsg if (v_total < stream->timing.v_total) { 140c349dbc7Sjsg ASSERT(v_total < stream->timing.v_total); 141c349dbc7Sjsg v_total = stream->timing.v_total; 142fb4d8502Sjsg } 143fb4d8502Sjsg 144c349dbc7Sjsg return v_total; 145fb4d8502Sjsg } 146fb4d8502Sjsg 147c349dbc7Sjsg static unsigned int calc_v_total_from_duration( 148c349dbc7Sjsg const struct dc_stream_state *stream, 149c349dbc7Sjsg const struct mod_vrr_params *vrr, 150c349dbc7Sjsg unsigned int duration_in_us) 151fb4d8502Sjsg { 152c349dbc7Sjsg unsigned int v_total = 0; 153c349dbc7Sjsg 154c349dbc7Sjsg if (duration_in_us < vrr->min_duration_in_us) 155c349dbc7Sjsg duration_in_us = vrr->min_duration_in_us; 156c349dbc7Sjsg 157c349dbc7Sjsg if (duration_in_us > vrr->max_duration_in_us) 158c349dbc7Sjsg duration_in_us = vrr->max_duration_in_us; 159c349dbc7Sjsg 1601bb76ff1Sjsg if (dc_is_hdmi_signal(stream->signal)) { 1611bb76ff1Sjsg uint32_t h_total_up_scaled; 1621bb76ff1Sjsg 1631bb76ff1Sjsg h_total_up_scaled = stream->timing.h_total * 10000; 1641bb76ff1Sjsg v_total = div_u64((unsigned long long)duration_in_us 1651bb76ff1Sjsg * stream->timing.pix_clk_100hz + (h_total_up_scaled - 1), 1661bb76ff1Sjsg h_total_up_scaled); 1671bb76ff1Sjsg } else { 168c349dbc7Sjsg v_total = div64_u64(div64_u64(((unsigned long long)( 169c349dbc7Sjsg duration_in_us) * (stream->timing.pix_clk_100hz / 10)), 170c349dbc7Sjsg stream->timing.h_total), 1000); 1711bb76ff1Sjsg } 172c349dbc7Sjsg 173c349dbc7Sjsg /* v_total cannot be less than nominal */ 174c349dbc7Sjsg if (v_total < stream->timing.v_total) { 175c349dbc7Sjsg ASSERT(v_total < stream->timing.v_total); 176c349dbc7Sjsg v_total = stream->timing.v_total; 177fb4d8502Sjsg } 178fb4d8502Sjsg 179c349dbc7Sjsg return v_total; 180c349dbc7Sjsg } 181fb4d8502Sjsg 182c349dbc7Sjsg static void update_v_total_for_static_ramp( 183c349dbc7Sjsg struct core_freesync *core_freesync, 184c349dbc7Sjsg const struct dc_stream_state *stream, 185c349dbc7Sjsg struct mod_vrr_params *in_out_vrr) 186c349dbc7Sjsg { 187c349dbc7Sjsg unsigned int v_total = 0; 188c349dbc7Sjsg unsigned int current_duration_in_us = 189c349dbc7Sjsg calc_duration_in_us_from_v_total( 190c349dbc7Sjsg stream, in_out_vrr, 191c349dbc7Sjsg in_out_vrr->adjust.v_total_max); 192c349dbc7Sjsg unsigned int target_duration_in_us = 193c349dbc7Sjsg calc_duration_in_us_from_refresh_in_uhz( 194c349dbc7Sjsg in_out_vrr->fixed.target_refresh_in_uhz); 195c349dbc7Sjsg bool ramp_direction_is_up = (current_duration_in_us > 196c349dbc7Sjsg target_duration_in_us) ? true : false; 197fb4d8502Sjsg 1985ca02815Sjsg /* Calculate ratio between new and current frame duration with 3 digit */ 199fb4d8502Sjsg unsigned int frame_duration_ratio = div64_u64(1000000, 200fb4d8502Sjsg (1000 + div64_u64(((unsigned long long)( 201fb4d8502Sjsg STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME) * 202c349dbc7Sjsg current_duration_in_us), 203c349dbc7Sjsg 1000000))); 204fb4d8502Sjsg 205c349dbc7Sjsg /* Calculate delta between new and current frame duration in us */ 206fb4d8502Sjsg unsigned int frame_duration_delta = div64_u64(((unsigned long long)( 207c349dbc7Sjsg current_duration_in_us) * 208fb4d8502Sjsg (1000 - frame_duration_ratio)), 1000); 209fb4d8502Sjsg 210fb4d8502Sjsg /* Adjust frame duration delta based on ratio between current and 211fb4d8502Sjsg * standard frame duration (frame duration at 60 Hz refresh rate). 212fb4d8502Sjsg */ 213fb4d8502Sjsg unsigned int ramp_rate_interpolated = div64_u64(((unsigned long long)( 214c349dbc7Sjsg frame_duration_delta) * current_duration_in_us), 16666); 215fb4d8502Sjsg 216fb4d8502Sjsg /* Going to a higher refresh rate (lower frame duration) */ 217c349dbc7Sjsg if (ramp_direction_is_up) { 2185ca02815Sjsg /* Reduce frame duration */ 219c349dbc7Sjsg current_duration_in_us -= ramp_rate_interpolated; 220fb4d8502Sjsg 2215ca02815Sjsg /* Adjust for frame duration below min */ 222c349dbc7Sjsg if (current_duration_in_us <= target_duration_in_us) { 223c349dbc7Sjsg in_out_vrr->fixed.ramping_active = false; 224c349dbc7Sjsg in_out_vrr->fixed.ramping_done = true; 225c349dbc7Sjsg current_duration_in_us = 226c349dbc7Sjsg calc_duration_in_us_from_refresh_in_uhz( 227c349dbc7Sjsg in_out_vrr->fixed.target_refresh_in_uhz); 228fb4d8502Sjsg } 229fb4d8502Sjsg /* Going to a lower refresh rate (larger frame duration) */ 230fb4d8502Sjsg } else { 2315ca02815Sjsg /* Increase frame duration */ 232c349dbc7Sjsg current_duration_in_us += ramp_rate_interpolated; 233fb4d8502Sjsg 2345ca02815Sjsg /* Adjust for frame duration above max */ 235c349dbc7Sjsg if (current_duration_in_us >= target_duration_in_us) { 236c349dbc7Sjsg in_out_vrr->fixed.ramping_active = false; 237c349dbc7Sjsg in_out_vrr->fixed.ramping_done = true; 238c349dbc7Sjsg current_duration_in_us = 239c349dbc7Sjsg calc_duration_in_us_from_refresh_in_uhz( 240c349dbc7Sjsg in_out_vrr->fixed.target_refresh_in_uhz); 241fb4d8502Sjsg } 242fb4d8502Sjsg } 243fb4d8502Sjsg 244c349dbc7Sjsg v_total = div64_u64(div64_u64(((unsigned long long)( 245c349dbc7Sjsg current_duration_in_us) * (stream->timing.pix_clk_100hz / 10)), 246c349dbc7Sjsg stream->timing.h_total), 1000); 247c349dbc7Sjsg 248c349dbc7Sjsg /* v_total cannot be less than nominal */ 249c349dbc7Sjsg if (v_total < stream->timing.v_total) 250c349dbc7Sjsg v_total = stream->timing.v_total; 251c349dbc7Sjsg 252c349dbc7Sjsg in_out_vrr->adjust.v_total_min = v_total; 253c349dbc7Sjsg in_out_vrr->adjust.v_total_max = v_total; 254fb4d8502Sjsg } 255fb4d8502Sjsg 256c349dbc7Sjsg static void apply_below_the_range(struct core_freesync *core_freesync, 257c349dbc7Sjsg const struct dc_stream_state *stream, 258c349dbc7Sjsg unsigned int last_render_time_in_us, 259c349dbc7Sjsg struct mod_vrr_params *in_out_vrr) 260fb4d8502Sjsg { 261c349dbc7Sjsg unsigned int inserted_frame_duration_in_us = 0; 262c349dbc7Sjsg unsigned int mid_point_frames_ceil = 0; 263c349dbc7Sjsg unsigned int mid_point_frames_floor = 0; 264c349dbc7Sjsg unsigned int frame_time_in_us = 0; 265c349dbc7Sjsg unsigned int delta_from_mid_point_in_us_1 = 0xFFFFFFFF; 266c349dbc7Sjsg unsigned int delta_from_mid_point_in_us_2 = 0xFFFFFFFF; 267c349dbc7Sjsg unsigned int frames_to_insert = 0; 268c349dbc7Sjsg unsigned int delta_from_mid_point_delta_in_us; 269c349dbc7Sjsg unsigned int max_render_time_in_us = 270c349dbc7Sjsg in_out_vrr->max_duration_in_us - in_out_vrr->btr.margin_in_us; 271fb4d8502Sjsg 272c349dbc7Sjsg /* Program BTR */ 273c349dbc7Sjsg if ((last_render_time_in_us + in_out_vrr->btr.margin_in_us / 2) < max_render_time_in_us) { 274c349dbc7Sjsg /* Exit Below the Range */ 275c349dbc7Sjsg if (in_out_vrr->btr.btr_active) { 276c349dbc7Sjsg in_out_vrr->btr.frame_counter = 0; 277c349dbc7Sjsg in_out_vrr->btr.btr_active = false; 278fb4d8502Sjsg } 279c349dbc7Sjsg } else if (last_render_time_in_us > (max_render_time_in_us + in_out_vrr->btr.margin_in_us / 2)) { 280c349dbc7Sjsg /* Enter Below the Range */ 281c349dbc7Sjsg if (!in_out_vrr->btr.btr_active) { 282c349dbc7Sjsg in_out_vrr->btr.btr_active = true; 283fb4d8502Sjsg } 284fb4d8502Sjsg } 285fb4d8502Sjsg 286c349dbc7Sjsg /* BTR set to "not active" so disengage */ 287c349dbc7Sjsg if (!in_out_vrr->btr.btr_active) { 288c349dbc7Sjsg in_out_vrr->btr.inserted_duration_in_us = 0; 289c349dbc7Sjsg in_out_vrr->btr.frames_to_insert = 0; 290c349dbc7Sjsg in_out_vrr->btr.frame_counter = 0; 291fb4d8502Sjsg 292fb4d8502Sjsg /* Restore FreeSync */ 293c349dbc7Sjsg in_out_vrr->adjust.v_total_min = 2945ca02815Sjsg mod_freesync_calc_v_total_from_refresh(stream, 295c349dbc7Sjsg in_out_vrr->max_refresh_in_uhz); 296c349dbc7Sjsg in_out_vrr->adjust.v_total_max = 2975ca02815Sjsg mod_freesync_calc_v_total_from_refresh(stream, 298c349dbc7Sjsg in_out_vrr->min_refresh_in_uhz); 299c349dbc7Sjsg /* BTR set to "active" so engage */ 300fb4d8502Sjsg } else { 301c349dbc7Sjsg 302c349dbc7Sjsg /* Calculate number of midPoint frames that could fit within 303c349dbc7Sjsg * the render time interval - take ceil of this value 304c349dbc7Sjsg */ 305c349dbc7Sjsg mid_point_frames_ceil = (last_render_time_in_us + 306c349dbc7Sjsg in_out_vrr->btr.mid_point_in_us - 1) / 307c349dbc7Sjsg in_out_vrr->btr.mid_point_in_us; 308c349dbc7Sjsg 309c349dbc7Sjsg if (mid_point_frames_ceil > 0) { 310c349dbc7Sjsg frame_time_in_us = last_render_time_in_us / 311c349dbc7Sjsg mid_point_frames_ceil; 312c349dbc7Sjsg delta_from_mid_point_in_us_1 = 313c349dbc7Sjsg (in_out_vrr->btr.mid_point_in_us > 314c349dbc7Sjsg frame_time_in_us) ? 315c349dbc7Sjsg (in_out_vrr->btr.mid_point_in_us - frame_time_in_us) : 316c349dbc7Sjsg (frame_time_in_us - in_out_vrr->btr.mid_point_in_us); 317fb4d8502Sjsg } 318fb4d8502Sjsg 319c349dbc7Sjsg /* Calculate number of midPoint frames that could fit within 320c349dbc7Sjsg * the render time interval - take floor of this value 321c349dbc7Sjsg */ 322c349dbc7Sjsg mid_point_frames_floor = last_render_time_in_us / 323c349dbc7Sjsg in_out_vrr->btr.mid_point_in_us; 324fb4d8502Sjsg 325c349dbc7Sjsg if (mid_point_frames_floor > 0) { 326fb4d8502Sjsg 327c349dbc7Sjsg frame_time_in_us = last_render_time_in_us / 328c349dbc7Sjsg mid_point_frames_floor; 329c349dbc7Sjsg delta_from_mid_point_in_us_2 = 330c349dbc7Sjsg (in_out_vrr->btr.mid_point_in_us > 331c349dbc7Sjsg frame_time_in_us) ? 332c349dbc7Sjsg (in_out_vrr->btr.mid_point_in_us - frame_time_in_us) : 333c349dbc7Sjsg (frame_time_in_us - in_out_vrr->btr.mid_point_in_us); 334fb4d8502Sjsg } 335fb4d8502Sjsg 336c349dbc7Sjsg /* Choose number of frames to insert based on how close it 337c349dbc7Sjsg * can get to the mid point of the variable range. 338ad8b1aafSjsg * - Delta for CEIL: delta_from_mid_point_in_us_1 339ad8b1aafSjsg * - Delta for FLOOR: delta_from_mid_point_in_us_2 340c349dbc7Sjsg */ 3414369b360Sjsg if (mid_point_frames_ceil && 3424369b360Sjsg (last_render_time_in_us / mid_point_frames_ceil) < 3434369b360Sjsg in_out_vrr->min_duration_in_us) { 344ad8b1aafSjsg /* Check for out of range. 345ad8b1aafSjsg * If using CEIL produces a value that is out of range, 346ad8b1aafSjsg * then we are forced to use FLOOR. 347ad8b1aafSjsg */ 348c349dbc7Sjsg frames_to_insert = mid_point_frames_floor; 349ad8b1aafSjsg } else if (mid_point_frames_floor < 2) { 350ad8b1aafSjsg /* Check if FLOOR would result in non-LFC. In this case 351ad8b1aafSjsg * choose to use CEIL 352ad8b1aafSjsg */ 353ad8b1aafSjsg frames_to_insert = mid_point_frames_ceil; 354ad8b1aafSjsg } else if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) { 355ad8b1aafSjsg /* If choosing CEIL results in a frame duration that is 356ad8b1aafSjsg * closer to the mid point of the range. 357ad8b1aafSjsg * Choose CEIL 358ad8b1aafSjsg */ 359ad8b1aafSjsg frames_to_insert = mid_point_frames_ceil; 360ad8b1aafSjsg } else { 361ad8b1aafSjsg /* If choosing FLOOR results in a frame duration that is 362ad8b1aafSjsg * closer to the mid point of the range. 363ad8b1aafSjsg * Choose FLOOR 364ad8b1aafSjsg */ 365ad8b1aafSjsg frames_to_insert = mid_point_frames_floor; 366fb4d8502Sjsg } 367fb4d8502Sjsg 368c349dbc7Sjsg /* Prefer current frame multiplier when BTR is enabled unless it drifts 369c349dbc7Sjsg * too far from the midpoint 370c349dbc7Sjsg */ 371ad8b1aafSjsg if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) { 372ad8b1aafSjsg delta_from_mid_point_delta_in_us = delta_from_mid_point_in_us_2 - 373ad8b1aafSjsg delta_from_mid_point_in_us_1; 374ad8b1aafSjsg } else { 375ad8b1aafSjsg delta_from_mid_point_delta_in_us = delta_from_mid_point_in_us_1 - 376ad8b1aafSjsg delta_from_mid_point_in_us_2; 377ad8b1aafSjsg } 378c349dbc7Sjsg if (in_out_vrr->btr.frames_to_insert != 0 && 379c349dbc7Sjsg delta_from_mid_point_delta_in_us < BTR_DRIFT_MARGIN) { 380c349dbc7Sjsg if (((last_render_time_in_us / in_out_vrr->btr.frames_to_insert) < 381c349dbc7Sjsg max_render_time_in_us) && 382c349dbc7Sjsg ((last_render_time_in_us / in_out_vrr->btr.frames_to_insert) > 383c349dbc7Sjsg in_out_vrr->min_duration_in_us)) 384c349dbc7Sjsg frames_to_insert = in_out_vrr->btr.frames_to_insert; 385fb4d8502Sjsg } 386fb4d8502Sjsg 387c349dbc7Sjsg /* Either we've calculated the number of frames to insert, 388c349dbc7Sjsg * or we need to insert min duration frames 389c349dbc7Sjsg */ 3904369b360Sjsg if (frames_to_insert && 3914369b360Sjsg (last_render_time_in_us / frames_to_insert) < 392c349dbc7Sjsg in_out_vrr->min_duration_in_us){ 393c349dbc7Sjsg frames_to_insert -= (frames_to_insert > 1) ? 394c349dbc7Sjsg 1 : 0; 395fb4d8502Sjsg } 396fb4d8502Sjsg 397c349dbc7Sjsg if (frames_to_insert > 0) 398c349dbc7Sjsg inserted_frame_duration_in_us = last_render_time_in_us / 399c349dbc7Sjsg frames_to_insert; 400c349dbc7Sjsg 401c349dbc7Sjsg if (inserted_frame_duration_in_us < in_out_vrr->min_duration_in_us) 402c349dbc7Sjsg inserted_frame_duration_in_us = in_out_vrr->min_duration_in_us; 403c349dbc7Sjsg 404c349dbc7Sjsg /* Cache the calculated variables */ 405c349dbc7Sjsg in_out_vrr->btr.inserted_duration_in_us = 406c349dbc7Sjsg inserted_frame_duration_in_us; 407c349dbc7Sjsg in_out_vrr->btr.frames_to_insert = frames_to_insert; 408c349dbc7Sjsg in_out_vrr->btr.frame_counter = frames_to_insert; 409c349dbc7Sjsg } 410c349dbc7Sjsg } 411c349dbc7Sjsg 412c349dbc7Sjsg static void apply_fixed_refresh(struct core_freesync *core_freesync, 413c349dbc7Sjsg const struct dc_stream_state *stream, 414c349dbc7Sjsg unsigned int last_render_time_in_us, 415c349dbc7Sjsg struct mod_vrr_params *in_out_vrr) 416fb4d8502Sjsg { 417c349dbc7Sjsg bool update = false; 418c349dbc7Sjsg unsigned int max_render_time_in_us = in_out_vrr->max_duration_in_us; 419fb4d8502Sjsg 420c349dbc7Sjsg /* Compute the exit refresh rate and exit frame duration */ 421c349dbc7Sjsg unsigned int exit_refresh_rate_in_milli_hz = ((1000000000/max_render_time_in_us) 422c349dbc7Sjsg + (1000*FIXED_REFRESH_EXIT_MARGIN_IN_HZ)); 423c349dbc7Sjsg unsigned int exit_frame_duration_in_us = 1000000000/exit_refresh_rate_in_milli_hz; 424fb4d8502Sjsg 425c349dbc7Sjsg if (last_render_time_in_us < exit_frame_duration_in_us) { 426c349dbc7Sjsg /* Exit Fixed Refresh mode */ 427c349dbc7Sjsg if (in_out_vrr->fixed.fixed_active) { 428c349dbc7Sjsg in_out_vrr->fixed.frame_counter++; 429fb4d8502Sjsg 430c349dbc7Sjsg if (in_out_vrr->fixed.frame_counter > 431c349dbc7Sjsg FIXED_REFRESH_EXIT_FRAME_COUNT) { 432c349dbc7Sjsg in_out_vrr->fixed.frame_counter = 0; 433c349dbc7Sjsg in_out_vrr->fixed.fixed_active = false; 434c349dbc7Sjsg in_out_vrr->fixed.target_refresh_in_uhz = 0; 435c349dbc7Sjsg update = true; 436c349dbc7Sjsg } 4375ca02815Sjsg } else 4385ca02815Sjsg in_out_vrr->fixed.frame_counter = 0; 439c349dbc7Sjsg } else if (last_render_time_in_us > max_render_time_in_us) { 440c349dbc7Sjsg /* Enter Fixed Refresh mode */ 441c349dbc7Sjsg if (!in_out_vrr->fixed.fixed_active) { 442c349dbc7Sjsg in_out_vrr->fixed.frame_counter++; 443fb4d8502Sjsg 444c349dbc7Sjsg if (in_out_vrr->fixed.frame_counter > 445c349dbc7Sjsg FIXED_REFRESH_ENTER_FRAME_COUNT) { 446c349dbc7Sjsg in_out_vrr->fixed.frame_counter = 0; 447c349dbc7Sjsg in_out_vrr->fixed.fixed_active = true; 448c349dbc7Sjsg in_out_vrr->fixed.target_refresh_in_uhz = 449c349dbc7Sjsg in_out_vrr->max_refresh_in_uhz; 450c349dbc7Sjsg update = true; 451c349dbc7Sjsg } 4525ca02815Sjsg } else 4535ca02815Sjsg in_out_vrr->fixed.frame_counter = 0; 454c349dbc7Sjsg } 455c349dbc7Sjsg 456c349dbc7Sjsg if (update) { 457c349dbc7Sjsg if (in_out_vrr->fixed.fixed_active) { 458c349dbc7Sjsg in_out_vrr->adjust.v_total_min = 4595ca02815Sjsg mod_freesync_calc_v_total_from_refresh( 460c349dbc7Sjsg stream, in_out_vrr->max_refresh_in_uhz); 461c349dbc7Sjsg in_out_vrr->adjust.v_total_max = 462c349dbc7Sjsg in_out_vrr->adjust.v_total_min; 463c349dbc7Sjsg } else { 464c349dbc7Sjsg in_out_vrr->adjust.v_total_min = 4655ca02815Sjsg mod_freesync_calc_v_total_from_refresh(stream, 466c349dbc7Sjsg in_out_vrr->max_refresh_in_uhz); 467c349dbc7Sjsg in_out_vrr->adjust.v_total_max = 4685ca02815Sjsg mod_freesync_calc_v_total_from_refresh(stream, 469c349dbc7Sjsg in_out_vrr->min_refresh_in_uhz); 470c349dbc7Sjsg } 471c349dbc7Sjsg } 472c349dbc7Sjsg } 473c349dbc7Sjsg 4741bb76ff1Sjsg static void determine_flip_interval_workaround_req(struct mod_vrr_params *in_vrr, 4751bb76ff1Sjsg unsigned int curr_time_stamp_in_us) 4761bb76ff1Sjsg { 4771bb76ff1Sjsg in_vrr->flip_interval.vsync_to_flip_in_us = curr_time_stamp_in_us - 4781bb76ff1Sjsg in_vrr->flip_interval.v_update_timestamp_in_us; 4791bb76ff1Sjsg 4801bb76ff1Sjsg /* Determine conditions for stopping workaround */ 4811bb76ff1Sjsg if (in_vrr->flip_interval.flip_interval_workaround_active && 4821bb76ff1Sjsg in_vrr->flip_interval.vsyncs_between_flip < VSYNCS_BETWEEN_FLIP_THRESHOLD && 4831bb76ff1Sjsg in_vrr->flip_interval.vsync_to_flip_in_us > FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US) { 4841bb76ff1Sjsg in_vrr->flip_interval.flip_interval_detect_counter = 0; 4851bb76ff1Sjsg in_vrr->flip_interval.program_flip_interval_workaround = true; 4861bb76ff1Sjsg in_vrr->flip_interval.flip_interval_workaround_active = false; 4871bb76ff1Sjsg } else { 4881bb76ff1Sjsg /* Determine conditions for starting workaround */ 4891bb76ff1Sjsg if (in_vrr->flip_interval.vsyncs_between_flip >= VSYNCS_BETWEEN_FLIP_THRESHOLD && 4901bb76ff1Sjsg in_vrr->flip_interval.vsync_to_flip_in_us < FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US) { 4911bb76ff1Sjsg /* Increase flip interval counter we have 2 vsyncs between flips and 4921bb76ff1Sjsg * vsync to flip interval is less than 500us 4931bb76ff1Sjsg */ 4941bb76ff1Sjsg in_vrr->flip_interval.flip_interval_detect_counter++; 4951bb76ff1Sjsg if (in_vrr->flip_interval.flip_interval_detect_counter > FREESYNC_CONSEC_FLIP_AFTER_VSYNC) { 4961bb76ff1Sjsg /* Start workaround if we detect 5 consecutive instances of the above case */ 4971bb76ff1Sjsg in_vrr->flip_interval.program_flip_interval_workaround = true; 4981bb76ff1Sjsg in_vrr->flip_interval.flip_interval_workaround_active = true; 4991bb76ff1Sjsg } 5001bb76ff1Sjsg } else { 5011bb76ff1Sjsg /* Reset the flip interval counter if we condition is no longer met */ 5021bb76ff1Sjsg in_vrr->flip_interval.flip_interval_detect_counter = 0; 5031bb76ff1Sjsg } 5041bb76ff1Sjsg } 5051bb76ff1Sjsg 5061bb76ff1Sjsg in_vrr->flip_interval.vsyncs_between_flip = 0; 5071bb76ff1Sjsg } 5081bb76ff1Sjsg 509c349dbc7Sjsg static bool vrr_settings_require_update(struct core_freesync *core_freesync, 510c349dbc7Sjsg struct mod_freesync_config *in_config, 511c349dbc7Sjsg unsigned int min_refresh_in_uhz, 512c349dbc7Sjsg unsigned int max_refresh_in_uhz, 513c349dbc7Sjsg struct mod_vrr_params *in_vrr) 514c349dbc7Sjsg { 515c349dbc7Sjsg if (in_vrr->state != in_config->state) { 516fb4d8502Sjsg return true; 517c349dbc7Sjsg } else if (in_vrr->state == VRR_STATE_ACTIVE_FIXED && 518c349dbc7Sjsg in_vrr->fixed.target_refresh_in_uhz != 519ad8b1aafSjsg in_config->fixed_refresh_in_uhz) { 520c349dbc7Sjsg return true; 521c349dbc7Sjsg } else if (in_vrr->min_refresh_in_uhz != min_refresh_in_uhz) { 522c349dbc7Sjsg return true; 523c349dbc7Sjsg } else if (in_vrr->max_refresh_in_uhz != max_refresh_in_uhz) { 524c349dbc7Sjsg return true; 525c349dbc7Sjsg } 526c349dbc7Sjsg 527c349dbc7Sjsg return false; 528fb4d8502Sjsg } 529fb4d8502Sjsg 530fb4d8502Sjsg bool mod_freesync_get_vmin_vmax(struct mod_freesync *mod_freesync, 531c349dbc7Sjsg const struct dc_stream_state *stream, 532fb4d8502Sjsg unsigned int *vmin, 533fb4d8502Sjsg unsigned int *vmax) 534fb4d8502Sjsg { 535c349dbc7Sjsg *vmin = stream->adjust.v_total_min; 536c349dbc7Sjsg *vmax = stream->adjust.v_total_max; 537fb4d8502Sjsg 538fb4d8502Sjsg return true; 539fb4d8502Sjsg } 540fb4d8502Sjsg 541fb4d8502Sjsg bool mod_freesync_get_v_position(struct mod_freesync *mod_freesync, 542fb4d8502Sjsg struct dc_stream_state *stream, 543fb4d8502Sjsg unsigned int *nom_v_pos, 544fb4d8502Sjsg unsigned int *v_pos) 545fb4d8502Sjsg { 546fb4d8502Sjsg struct core_freesync *core_freesync = NULL; 547fb4d8502Sjsg struct crtc_position position; 548fb4d8502Sjsg 549fb4d8502Sjsg if (mod_freesync == NULL) 550fb4d8502Sjsg return false; 551fb4d8502Sjsg 552fb4d8502Sjsg core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); 553fb4d8502Sjsg 554fb4d8502Sjsg if (dc_stream_get_crtc_position(core_freesync->dc, &stream, 1, 555fb4d8502Sjsg &position.vertical_count, 556fb4d8502Sjsg &position.nominal_vcount)) { 557fb4d8502Sjsg 558fb4d8502Sjsg *nom_v_pos = position.nominal_vcount; 559fb4d8502Sjsg *v_pos = position.vertical_count; 560fb4d8502Sjsg 561fb4d8502Sjsg return true; 562fb4d8502Sjsg } 563fb4d8502Sjsg 564fb4d8502Sjsg return false; 565fb4d8502Sjsg } 566fb4d8502Sjsg 567ad8b1aafSjsg static void build_vrr_infopacket_data_v1(const struct mod_vrr_params *vrr, 5685ca02815Sjsg struct dc_info_packet *infopacket, 5695ca02815Sjsg bool freesync_on_desktop) 570fb4d8502Sjsg { 571c349dbc7Sjsg /* PB1 = 0x1A (24bit AMD IEEE OUI (0x00001A) - Byte 0) */ 572c349dbc7Sjsg infopacket->sb[1] = 0x1A; 573c349dbc7Sjsg 574c349dbc7Sjsg /* PB2 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 1) */ 575c349dbc7Sjsg infopacket->sb[2] = 0x00; 576c349dbc7Sjsg 577c349dbc7Sjsg /* PB3 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 2) */ 578c349dbc7Sjsg infopacket->sb[3] = 0x00; 579c349dbc7Sjsg 580c349dbc7Sjsg /* PB4 = Reserved */ 581c349dbc7Sjsg 582c349dbc7Sjsg /* PB5 = Reserved */ 583c349dbc7Sjsg 584c349dbc7Sjsg /* PB6 = [Bits 7:3 = Reserved] */ 585c349dbc7Sjsg 586c349dbc7Sjsg /* PB6 = [Bit 0 = FreeSync Supported] */ 587c349dbc7Sjsg if (vrr->state != VRR_STATE_UNSUPPORTED) 588c349dbc7Sjsg infopacket->sb[6] |= 0x01; 589c349dbc7Sjsg 590c349dbc7Sjsg /* PB6 = [Bit 1 = FreeSync Enabled] */ 591c349dbc7Sjsg if (vrr->state != VRR_STATE_DISABLED && 592c349dbc7Sjsg vrr->state != VRR_STATE_UNSUPPORTED) 593c349dbc7Sjsg infopacket->sb[6] |= 0x02; 594c349dbc7Sjsg 5955ca02815Sjsg if (freesync_on_desktop) { 596c349dbc7Sjsg /* PB6 = [Bit 2 = FreeSync Active] */ 5975ca02815Sjsg if (vrr->state != VRR_STATE_DISABLED && 5985ca02815Sjsg vrr->state != VRR_STATE_UNSUPPORTED) 5995ca02815Sjsg infopacket->sb[6] |= 0x04; 6005ca02815Sjsg } else { 601c349dbc7Sjsg if (vrr->state == VRR_STATE_ACTIVE_VARIABLE || 602c349dbc7Sjsg vrr->state == VRR_STATE_ACTIVE_FIXED) 603c349dbc7Sjsg infopacket->sb[6] |= 0x04; 6045ca02815Sjsg } 605c349dbc7Sjsg 606ad8b1aafSjsg // For v1 & 2 infoframes program nominal if non-fs mode, otherwise full range 607c349dbc7Sjsg /* PB7 = FreeSync Minimum refresh rate (Hz) */ 608ad8b1aafSjsg if (vrr->state == VRR_STATE_ACTIVE_VARIABLE || 609ad8b1aafSjsg vrr->state == VRR_STATE_ACTIVE_FIXED) { 610c349dbc7Sjsg infopacket->sb[7] = (unsigned char)((vrr->min_refresh_in_uhz + 500000) / 1000000); 611ad8b1aafSjsg } else { 612ad8b1aafSjsg infopacket->sb[7] = (unsigned char)((vrr->max_refresh_in_uhz + 500000) / 1000000); 613ad8b1aafSjsg } 614c349dbc7Sjsg 615c349dbc7Sjsg /* PB8 = FreeSync Maximum refresh rate (Hz) 616c349dbc7Sjsg * Note: We should never go above the field rate of the mode timing set. 617c349dbc7Sjsg */ 618c349dbc7Sjsg infopacket->sb[8] = (unsigned char)((vrr->max_refresh_in_uhz + 500000) / 1000000); 619ad8b1aafSjsg } 620ad8b1aafSjsg 621ad8b1aafSjsg static void build_vrr_infopacket_data_v3(const struct mod_vrr_params *vrr, 622f005ef32Sjsg struct dc_info_packet *infopacket, 623f005ef32Sjsg bool freesync_on_desktop) 624ad8b1aafSjsg { 6255ca02815Sjsg unsigned int min_refresh; 6265ca02815Sjsg unsigned int max_refresh; 6275ca02815Sjsg unsigned int fixed_refresh; 6285ca02815Sjsg unsigned int min_programmed; 6295ca02815Sjsg unsigned int max_programmed; 6305ca02815Sjsg 631ad8b1aafSjsg /* PB1 = 0x1A (24bit AMD IEEE OUI (0x00001A) - Byte 0) */ 632ad8b1aafSjsg infopacket->sb[1] = 0x1A; 633ad8b1aafSjsg 634ad8b1aafSjsg /* PB2 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 1) */ 635ad8b1aafSjsg infopacket->sb[2] = 0x00; 636ad8b1aafSjsg 637ad8b1aafSjsg /* PB3 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 2) */ 638ad8b1aafSjsg infopacket->sb[3] = 0x00; 639ad8b1aafSjsg 640ad8b1aafSjsg /* PB4 = Reserved */ 641ad8b1aafSjsg 642ad8b1aafSjsg /* PB5 = Reserved */ 643ad8b1aafSjsg 644ad8b1aafSjsg /* PB6 = [Bits 7:3 = Reserved] */ 645ad8b1aafSjsg 646ad8b1aafSjsg /* PB6 = [Bit 0 = FreeSync Supported] */ 647ad8b1aafSjsg if (vrr->state != VRR_STATE_UNSUPPORTED) 648ad8b1aafSjsg infopacket->sb[6] |= 0x01; 649ad8b1aafSjsg 650ad8b1aafSjsg /* PB6 = [Bit 1 = FreeSync Enabled] */ 651ad8b1aafSjsg if (vrr->state != VRR_STATE_DISABLED && 652ad8b1aafSjsg vrr->state != VRR_STATE_UNSUPPORTED) 653ad8b1aafSjsg infopacket->sb[6] |= 0x02; 654ad8b1aafSjsg 655ad8b1aafSjsg /* PB6 = [Bit 2 = FreeSync Active] */ 656f005ef32Sjsg if (freesync_on_desktop) { 657f005ef32Sjsg if (vrr->state != VRR_STATE_DISABLED && 658f005ef32Sjsg vrr->state != VRR_STATE_UNSUPPORTED) 659f005ef32Sjsg infopacket->sb[6] |= 0x04; 660f005ef32Sjsg } else { 661ad8b1aafSjsg if (vrr->state == VRR_STATE_ACTIVE_VARIABLE || 662ad8b1aafSjsg vrr->state == VRR_STATE_ACTIVE_FIXED) 663ad8b1aafSjsg infopacket->sb[6] |= 0x04; 664f005ef32Sjsg } 665ad8b1aafSjsg 6665ca02815Sjsg min_refresh = (vrr->min_refresh_in_uhz + 500000) / 1000000; 6675ca02815Sjsg max_refresh = (vrr->max_refresh_in_uhz + 500000) / 1000000; 6685ca02815Sjsg fixed_refresh = (vrr->fixed_refresh_in_uhz + 500000) / 1000000; 6695ca02815Sjsg 6705ca02815Sjsg min_programmed = (vrr->state == VRR_STATE_ACTIVE_FIXED) ? fixed_refresh : 6715ca02815Sjsg (vrr->state == VRR_STATE_ACTIVE_VARIABLE) ? min_refresh : 6725ca02815Sjsg (vrr->state == VRR_STATE_INACTIVE) ? min_refresh : 6735ca02815Sjsg max_refresh; // Non-fs case, program nominal range 6745ca02815Sjsg 6755ca02815Sjsg max_programmed = (vrr->state == VRR_STATE_ACTIVE_FIXED) ? fixed_refresh : 6765ca02815Sjsg (vrr->state == VRR_STATE_ACTIVE_VARIABLE) ? max_refresh : 6775ca02815Sjsg max_refresh;// Non-fs case, program nominal range 6785ca02815Sjsg 679ad8b1aafSjsg /* PB7 = FreeSync Minimum refresh rate (Hz) */ 6805ca02815Sjsg infopacket->sb[7] = min_programmed & 0xFF; 6815ca02815Sjsg 682ad8b1aafSjsg /* PB8 = FreeSync Maximum refresh rate (Hz) */ 6835ca02815Sjsg infopacket->sb[8] = max_programmed & 0xFF; 6845ca02815Sjsg 6855ca02815Sjsg /* PB11 : MSB FreeSync Minimum refresh rate [Hz] - bits 9:8 */ 6865ca02815Sjsg infopacket->sb[11] = (min_programmed >> 8) & 0x03; 6875ca02815Sjsg 6885ca02815Sjsg /* PB12 : MSB FreeSync Maximum refresh rate [Hz] - bits 9:8 */ 6895ca02815Sjsg infopacket->sb[12] = (max_programmed >> 8) & 0x03; 6905ca02815Sjsg 6915ca02815Sjsg /* PB16 : Reserved bits 7:1, FixedRate bit 0 */ 6925ca02815Sjsg infopacket->sb[16] = (vrr->state == VRR_STATE_ACTIVE_FIXED) ? 1 : 0; 693c349dbc7Sjsg } 694c349dbc7Sjsg 695c349dbc7Sjsg static void build_vrr_infopacket_fs2_data(enum color_transfer_func app_tf, 696c349dbc7Sjsg struct dc_info_packet *infopacket) 697c349dbc7Sjsg { 698c349dbc7Sjsg if (app_tf != TRANSFER_FUNC_UNKNOWN) { 699c349dbc7Sjsg infopacket->valid = true; 700c349dbc7Sjsg 701f005ef32Sjsg if (app_tf != TRANSFER_FUNC_PQ2084) { 702c349dbc7Sjsg infopacket->sb[6] |= 0x08; // PB6 = [Bit 3 = Native Color Active] 703f005ef32Sjsg if (app_tf == TRANSFER_FUNC_GAMMA_22) 704c349dbc7Sjsg infopacket->sb[9] |= 0x04; // PB6 = [Bit 2 = Gamma 2.2 EOTF Active] 705c349dbc7Sjsg } 706c349dbc7Sjsg } 707c349dbc7Sjsg } 708c349dbc7Sjsg 709c349dbc7Sjsg static void build_vrr_infopacket_header_v1(enum amd_signal_type signal, 710c349dbc7Sjsg struct dc_info_packet *infopacket, 711c349dbc7Sjsg unsigned int *payload_size) 712c349dbc7Sjsg { 713c349dbc7Sjsg if (dc_is_hdmi_signal(signal)) { 714c349dbc7Sjsg 715c349dbc7Sjsg /* HEADER */ 716c349dbc7Sjsg 717c349dbc7Sjsg /* HB0 = Packet Type = 0x83 (Source Product 718c349dbc7Sjsg * Descriptor InfoFrame) 719c349dbc7Sjsg */ 720c349dbc7Sjsg infopacket->hb0 = DC_HDMI_INFOFRAME_TYPE_SPD; 721c349dbc7Sjsg 722c349dbc7Sjsg /* HB1 = Version = 0x01 */ 723c349dbc7Sjsg infopacket->hb1 = 0x01; 724c349dbc7Sjsg 725c349dbc7Sjsg /* HB2 = [Bits 7:5 = 0] [Bits 4:0 = Length = 0x08] */ 726c349dbc7Sjsg infopacket->hb2 = 0x08; 727c349dbc7Sjsg 728c349dbc7Sjsg *payload_size = 0x08; 729c349dbc7Sjsg 730c349dbc7Sjsg } else if (dc_is_dp_signal(signal)) { 731c349dbc7Sjsg 732c349dbc7Sjsg /* HEADER */ 733c349dbc7Sjsg 734c349dbc7Sjsg /* HB0 = Secondary-data Packet ID = 0 - Only non-zero 735c349dbc7Sjsg * when used to associate audio related info packets 736c349dbc7Sjsg */ 737c349dbc7Sjsg infopacket->hb0 = 0x00; 738c349dbc7Sjsg 739c349dbc7Sjsg /* HB1 = Packet Type = 0x83 (Source Product 740c349dbc7Sjsg * Descriptor InfoFrame) 741c349dbc7Sjsg */ 742c349dbc7Sjsg infopacket->hb1 = DC_HDMI_INFOFRAME_TYPE_SPD; 743c349dbc7Sjsg 744c349dbc7Sjsg /* HB2 = [Bits 7:0 = Least significant eight bits - 745c349dbc7Sjsg * For INFOFRAME, the value must be 1Bh] 746c349dbc7Sjsg */ 747c349dbc7Sjsg infopacket->hb2 = 0x1B; 748c349dbc7Sjsg 749c349dbc7Sjsg /* HB3 = [Bits 7:2 = INFOFRAME SDP Version Number = 0x1] 750c349dbc7Sjsg * [Bits 1:0 = Most significant two bits = 0x00] 751c349dbc7Sjsg */ 752c349dbc7Sjsg infopacket->hb3 = 0x04; 753c349dbc7Sjsg 754c349dbc7Sjsg *payload_size = 0x1B; 755c349dbc7Sjsg } 756c349dbc7Sjsg } 757c349dbc7Sjsg 758c349dbc7Sjsg static void build_vrr_infopacket_header_v2(enum amd_signal_type signal, 759c349dbc7Sjsg struct dc_info_packet *infopacket, 760c349dbc7Sjsg unsigned int *payload_size) 761c349dbc7Sjsg { 762c349dbc7Sjsg if (dc_is_hdmi_signal(signal)) { 763c349dbc7Sjsg 764c349dbc7Sjsg /* HEADER */ 765c349dbc7Sjsg 766c349dbc7Sjsg /* HB0 = Packet Type = 0x83 (Source Product 767c349dbc7Sjsg * Descriptor InfoFrame) 768c349dbc7Sjsg */ 769c349dbc7Sjsg infopacket->hb0 = DC_HDMI_INFOFRAME_TYPE_SPD; 770c349dbc7Sjsg 771c349dbc7Sjsg /* HB1 = Version = 0x02 */ 772c349dbc7Sjsg infopacket->hb1 = 0x02; 773c349dbc7Sjsg 774c349dbc7Sjsg /* HB2 = [Bits 7:5 = 0] [Bits 4:0 = Length = 0x09] */ 775c349dbc7Sjsg infopacket->hb2 = 0x09; 776c349dbc7Sjsg 77760287b98Sjsg *payload_size = 0x09; 778c349dbc7Sjsg } else if (dc_is_dp_signal(signal)) { 779c349dbc7Sjsg 780c349dbc7Sjsg /* HEADER */ 781c349dbc7Sjsg 782c349dbc7Sjsg /* HB0 = Secondary-data Packet ID = 0 - Only non-zero 783c349dbc7Sjsg * when used to associate audio related info packets 784c349dbc7Sjsg */ 785c349dbc7Sjsg infopacket->hb0 = 0x00; 786c349dbc7Sjsg 787c349dbc7Sjsg /* HB1 = Packet Type = 0x83 (Source Product 788c349dbc7Sjsg * Descriptor InfoFrame) 789c349dbc7Sjsg */ 790c349dbc7Sjsg infopacket->hb1 = DC_HDMI_INFOFRAME_TYPE_SPD; 791c349dbc7Sjsg 792c349dbc7Sjsg /* HB2 = [Bits 7:0 = Least significant eight bits - 793c349dbc7Sjsg * For INFOFRAME, the value must be 1Bh] 794c349dbc7Sjsg */ 795c349dbc7Sjsg infopacket->hb2 = 0x1B; 796c349dbc7Sjsg 797c349dbc7Sjsg /* HB3 = [Bits 7:2 = INFOFRAME SDP Version Number = 0x2] 798c349dbc7Sjsg * [Bits 1:0 = Most significant two bits = 0x00] 799c349dbc7Sjsg */ 800c349dbc7Sjsg infopacket->hb3 = 0x08; 801c349dbc7Sjsg 802c349dbc7Sjsg *payload_size = 0x1B; 803c349dbc7Sjsg } 804c349dbc7Sjsg } 805c349dbc7Sjsg 8065ca02815Sjsg static void build_vrr_infopacket_header_v3(enum amd_signal_type signal, 8075ca02815Sjsg struct dc_info_packet *infopacket, 8085ca02815Sjsg unsigned int *payload_size) 8095ca02815Sjsg { 8105ca02815Sjsg unsigned char version; 8115ca02815Sjsg 8125ca02815Sjsg version = 3; 8135ca02815Sjsg if (dc_is_hdmi_signal(signal)) { 8145ca02815Sjsg 8155ca02815Sjsg /* HEADER */ 8165ca02815Sjsg 8175ca02815Sjsg /* HB0 = Packet Type = 0x83 (Source Product 8185ca02815Sjsg * Descriptor InfoFrame) 8195ca02815Sjsg */ 8205ca02815Sjsg infopacket->hb0 = DC_HDMI_INFOFRAME_TYPE_SPD; 8215ca02815Sjsg 8225ca02815Sjsg /* HB1 = Version = 0x03 */ 8235ca02815Sjsg infopacket->hb1 = version; 8245ca02815Sjsg 8255ca02815Sjsg /* HB2 = [Bits 7:5 = 0] [Bits 4:0 = Length] */ 82660287b98Sjsg infopacket->hb2 = 0x10; 8275ca02815Sjsg 82860287b98Sjsg *payload_size = 0x10; 8295ca02815Sjsg } else if (dc_is_dp_signal(signal)) { 8305ca02815Sjsg 8315ca02815Sjsg /* HEADER */ 8325ca02815Sjsg 8335ca02815Sjsg /* HB0 = Secondary-data Packet ID = 0 - Only non-zero 8345ca02815Sjsg * when used to associate audio related info packets 8355ca02815Sjsg */ 8365ca02815Sjsg infopacket->hb0 = 0x00; 8375ca02815Sjsg 8385ca02815Sjsg /* HB1 = Packet Type = 0x83 (Source Product 8395ca02815Sjsg * Descriptor InfoFrame) 8405ca02815Sjsg */ 8415ca02815Sjsg infopacket->hb1 = DC_HDMI_INFOFRAME_TYPE_SPD; 8425ca02815Sjsg 8435ca02815Sjsg /* HB2 = [Bits 7:0 = Least significant eight bits - 8445ca02815Sjsg * For INFOFRAME, the value must be 1Bh] 8455ca02815Sjsg */ 8465ca02815Sjsg infopacket->hb2 = 0x1B; 8475ca02815Sjsg 8485ca02815Sjsg /* HB3 = [Bits 7:2 = INFOFRAME SDP Version Number = 0x2] 8495ca02815Sjsg * [Bits 1:0 = Most significant two bits = 0x00] 8505ca02815Sjsg */ 8515ca02815Sjsg 8525ca02815Sjsg infopacket->hb3 = (version & 0x3F) << 2; 8535ca02815Sjsg 8545ca02815Sjsg *payload_size = 0x1B; 8555ca02815Sjsg } 8565ca02815Sjsg } 8575ca02815Sjsg 858c349dbc7Sjsg static void build_vrr_infopacket_checksum(unsigned int *payload_size, 859c349dbc7Sjsg struct dc_info_packet *infopacket) 860c349dbc7Sjsg { 861c349dbc7Sjsg /* Calculate checksum */ 862c349dbc7Sjsg unsigned int idx = 0; 863c349dbc7Sjsg unsigned char checksum = 0; 864c349dbc7Sjsg 865c349dbc7Sjsg checksum += infopacket->hb0; 866c349dbc7Sjsg checksum += infopacket->hb1; 867c349dbc7Sjsg checksum += infopacket->hb2; 868c349dbc7Sjsg checksum += infopacket->hb3; 869c349dbc7Sjsg 870c349dbc7Sjsg for (idx = 1; idx <= *payload_size; idx++) 871c349dbc7Sjsg checksum += infopacket->sb[idx]; 872c349dbc7Sjsg 873c349dbc7Sjsg /* PB0 = Checksum (one byte complement) */ 874c349dbc7Sjsg infopacket->sb[0] = (unsigned char)(0x100 - checksum); 875c349dbc7Sjsg 876c349dbc7Sjsg infopacket->valid = true; 877c349dbc7Sjsg } 878c349dbc7Sjsg 879c349dbc7Sjsg static void build_vrr_infopacket_v1(enum amd_signal_type signal, 880c349dbc7Sjsg const struct mod_vrr_params *vrr, 8815ca02815Sjsg struct dc_info_packet *infopacket, 8825ca02815Sjsg bool freesync_on_desktop) 883c349dbc7Sjsg { 884c349dbc7Sjsg /* SPD info packet for FreeSync */ 885c349dbc7Sjsg unsigned int payload_size = 0; 886c349dbc7Sjsg 887c349dbc7Sjsg build_vrr_infopacket_header_v1(signal, infopacket, &payload_size); 8885ca02815Sjsg build_vrr_infopacket_data_v1(vrr, infopacket, freesync_on_desktop); 889c349dbc7Sjsg build_vrr_infopacket_checksum(&payload_size, infopacket); 890c349dbc7Sjsg 891c349dbc7Sjsg infopacket->valid = true; 892c349dbc7Sjsg } 893c349dbc7Sjsg 894c349dbc7Sjsg static void build_vrr_infopacket_v2(enum amd_signal_type signal, 895c349dbc7Sjsg const struct mod_vrr_params *vrr, 896c349dbc7Sjsg enum color_transfer_func app_tf, 8975ca02815Sjsg struct dc_info_packet *infopacket, 8985ca02815Sjsg bool freesync_on_desktop) 899c349dbc7Sjsg { 900c349dbc7Sjsg unsigned int payload_size = 0; 901c349dbc7Sjsg 902c349dbc7Sjsg build_vrr_infopacket_header_v2(signal, infopacket, &payload_size); 9035ca02815Sjsg build_vrr_infopacket_data_v1(vrr, infopacket, freesync_on_desktop); 904c349dbc7Sjsg 905c349dbc7Sjsg build_vrr_infopacket_fs2_data(app_tf, infopacket); 906c349dbc7Sjsg 907c349dbc7Sjsg build_vrr_infopacket_checksum(&payload_size, infopacket); 908c349dbc7Sjsg 909c349dbc7Sjsg infopacket->valid = true; 910c349dbc7Sjsg } 911ad8b1aafSjsg 912ad8b1aafSjsg static void build_vrr_infopacket_v3(enum amd_signal_type signal, 913ad8b1aafSjsg const struct mod_vrr_params *vrr, 914ad8b1aafSjsg enum color_transfer_func app_tf, 915f005ef32Sjsg struct dc_info_packet *infopacket, 916f005ef32Sjsg bool freesync_on_desktop) 917ad8b1aafSjsg { 918ad8b1aafSjsg unsigned int payload_size = 0; 919ad8b1aafSjsg 9205ca02815Sjsg build_vrr_infopacket_header_v3(signal, infopacket, &payload_size); 921f005ef32Sjsg build_vrr_infopacket_data_v3(vrr, infopacket, freesync_on_desktop); 922ad8b1aafSjsg 923ad8b1aafSjsg build_vrr_infopacket_fs2_data(app_tf, infopacket); 924ad8b1aafSjsg 925ad8b1aafSjsg build_vrr_infopacket_checksum(&payload_size, infopacket); 926ad8b1aafSjsg 927ad8b1aafSjsg infopacket->valid = true; 928ad8b1aafSjsg } 929c349dbc7Sjsg 9305ca02815Sjsg static void build_vrr_infopacket_sdp_v1_3(enum vrr_packet_type packet_type, 9315ca02815Sjsg struct dc_info_packet *infopacket) 9325ca02815Sjsg { 9335ca02815Sjsg uint8_t idx = 0, size = 0; 9345ca02815Sjsg 9355ca02815Sjsg size = ((packet_type == PACKET_TYPE_FS_V1) ? 0x08 : 9365ca02815Sjsg (packet_type == PACKET_TYPE_FS_V3) ? 0x10 : 9375ca02815Sjsg 0x09); 9385ca02815Sjsg 9395ca02815Sjsg for (idx = infopacket->hb2; idx > 1; idx--) // Data Byte Count: 0x1B 9405ca02815Sjsg infopacket->sb[idx] = infopacket->sb[idx-1]; 9415ca02815Sjsg 9425ca02815Sjsg infopacket->sb[1] = size; // Length 9435ca02815Sjsg infopacket->sb[0] = (infopacket->hb3 >> 2) & 0x3F;//Version 9445ca02815Sjsg infopacket->hb3 = (0x13 << 2); // Header,SDP 1.3 9455ca02815Sjsg infopacket->hb2 = 0x1D; 9465ca02815Sjsg } 9475ca02815Sjsg 948c349dbc7Sjsg void mod_freesync_build_vrr_infopacket(struct mod_freesync *mod_freesync, 949c349dbc7Sjsg const struct dc_stream_state *stream, 950c349dbc7Sjsg const struct mod_vrr_params *vrr, 951c349dbc7Sjsg enum vrr_packet_type packet_type, 952c349dbc7Sjsg enum color_transfer_func app_tf, 9535ca02815Sjsg struct dc_info_packet *infopacket, 9545ca02815Sjsg bool pack_sdp_v1_3) 955c349dbc7Sjsg { 956c349dbc7Sjsg /* SPD info packet for FreeSync 957c349dbc7Sjsg * VTEM info packet for HdmiVRR 958c349dbc7Sjsg * Check if Freesync is supported. Return if false. If true, 959c349dbc7Sjsg * set the corresponding bit in the info packet 960c349dbc7Sjsg */ 961ad8b1aafSjsg if (!vrr->send_info_frame) 962c349dbc7Sjsg return; 963c349dbc7Sjsg 964c349dbc7Sjsg switch (packet_type) { 965ad8b1aafSjsg case PACKET_TYPE_FS_V3: 966f005ef32Sjsg build_vrr_infopacket_v3(stream->signal, vrr, app_tf, infopacket, stream->freesync_on_desktop); 967ad8b1aafSjsg break; 968ad8b1aafSjsg case PACKET_TYPE_FS_V2: 9695ca02815Sjsg build_vrr_infopacket_v2(stream->signal, vrr, app_tf, infopacket, stream->freesync_on_desktop); 970c349dbc7Sjsg break; 971c349dbc7Sjsg case PACKET_TYPE_VRR: 972ad8b1aafSjsg case PACKET_TYPE_FS_V1: 973c349dbc7Sjsg default: 9745ca02815Sjsg build_vrr_infopacket_v1(stream->signal, vrr, infopacket, stream->freesync_on_desktop); 975c349dbc7Sjsg } 9765ca02815Sjsg 9775ca02815Sjsg if (true == pack_sdp_v1_3 && 9785ca02815Sjsg true == dc_is_dp_signal(stream->signal) && 9795ca02815Sjsg packet_type != PACKET_TYPE_VRR && 9805ca02815Sjsg packet_type != PACKET_TYPE_VTEM) 9815ca02815Sjsg build_vrr_infopacket_sdp_v1_3(packet_type, infopacket); 982c349dbc7Sjsg } 983c349dbc7Sjsg 984c349dbc7Sjsg void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync, 985c349dbc7Sjsg const struct dc_stream_state *stream, 986c349dbc7Sjsg struct mod_freesync_config *in_config, 987c349dbc7Sjsg struct mod_vrr_params *in_out_vrr) 988c349dbc7Sjsg { 989fb4d8502Sjsg struct core_freesync *core_freesync = NULL; 990c349dbc7Sjsg unsigned long long nominal_field_rate_in_uhz = 0; 991c349dbc7Sjsg unsigned long long rounded_nominal_in_uhz = 0; 992c349dbc7Sjsg unsigned int refresh_range = 0; 993c349dbc7Sjsg unsigned long long min_refresh_in_uhz = 0; 994c349dbc7Sjsg unsigned long long max_refresh_in_uhz = 0; 995f005ef32Sjsg unsigned long long min_hardware_refresh_in_uhz = 0; 996fb4d8502Sjsg 997fb4d8502Sjsg if (mod_freesync == NULL) 998fb4d8502Sjsg return; 999fb4d8502Sjsg 1000fb4d8502Sjsg core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); 1001fb4d8502Sjsg 1002c349dbc7Sjsg /* Calculate nominal field rate for stream */ 1003c349dbc7Sjsg nominal_field_rate_in_uhz = 1004c349dbc7Sjsg mod_freesync_calc_nominal_field_rate(stream); 1005fb4d8502Sjsg 1006f005ef32Sjsg if (stream->ctx->dc->caps.max_v_total != 0 && stream->timing.h_total != 0) { 1007f005ef32Sjsg min_hardware_refresh_in_uhz = div64_u64((stream->timing.pix_clk_100hz * 100000000ULL), 1008f005ef32Sjsg (stream->timing.h_total * stream->ctx->dc->caps.max_v_total)); 1009f005ef32Sjsg } 1010f005ef32Sjsg /* Limit minimum refresh rate to what can be supported by hardware */ 1011f005ef32Sjsg min_refresh_in_uhz = min_hardware_refresh_in_uhz > in_config->min_refresh_in_uhz ? 1012f005ef32Sjsg min_hardware_refresh_in_uhz : in_config->min_refresh_in_uhz; 1013c349dbc7Sjsg max_refresh_in_uhz = in_config->max_refresh_in_uhz; 1014fb4d8502Sjsg 10155ca02815Sjsg /* Full range may be larger than current video timing, so cap at nominal */ 1016c349dbc7Sjsg if (max_refresh_in_uhz > nominal_field_rate_in_uhz) 1017c349dbc7Sjsg max_refresh_in_uhz = nominal_field_rate_in_uhz; 1018fb4d8502Sjsg 10195ca02815Sjsg /* Full range may be larger than current video timing, so cap at nominal */ 1020c349dbc7Sjsg if (min_refresh_in_uhz > max_refresh_in_uhz) 1021c349dbc7Sjsg min_refresh_in_uhz = max_refresh_in_uhz; 1022fb4d8502Sjsg 10235ca02815Sjsg /* If a monitor reports exactly max refresh of 2x of min, enforce it on nominal */ 1024c349dbc7Sjsg rounded_nominal_in_uhz = 1025c349dbc7Sjsg div_u64(nominal_field_rate_in_uhz + 50000, 100000) * 100000; 1026c349dbc7Sjsg if (in_config->max_refresh_in_uhz == (2 * in_config->min_refresh_in_uhz) && 1027c349dbc7Sjsg in_config->max_refresh_in_uhz == rounded_nominal_in_uhz) 1028c349dbc7Sjsg min_refresh_in_uhz = div_u64(nominal_field_rate_in_uhz, 2); 1029fb4d8502Sjsg 1030c349dbc7Sjsg if (!vrr_settings_require_update(core_freesync, 1031c349dbc7Sjsg in_config, (unsigned int)min_refresh_in_uhz, (unsigned int)max_refresh_in_uhz, 1032c349dbc7Sjsg in_out_vrr)) 1033fb4d8502Sjsg return; 1034fb4d8502Sjsg 1035c349dbc7Sjsg in_out_vrr->state = in_config->state; 1036c349dbc7Sjsg in_out_vrr->send_info_frame = in_config->vsif_supported; 1037fb4d8502Sjsg 1038c349dbc7Sjsg if (in_config->state == VRR_STATE_UNSUPPORTED) { 1039c349dbc7Sjsg in_out_vrr->state = VRR_STATE_UNSUPPORTED; 1040c349dbc7Sjsg in_out_vrr->supported = false; 1041c349dbc7Sjsg in_out_vrr->adjust.v_total_min = stream->timing.v_total; 1042c349dbc7Sjsg in_out_vrr->adjust.v_total_max = stream->timing.v_total; 1043fb4d8502Sjsg 1044fb4d8502Sjsg return; 1045fb4d8502Sjsg 1046fb4d8502Sjsg } else { 1047c349dbc7Sjsg in_out_vrr->min_refresh_in_uhz = (unsigned int)min_refresh_in_uhz; 1048c349dbc7Sjsg in_out_vrr->max_duration_in_us = 1049c349dbc7Sjsg calc_duration_in_us_from_refresh_in_uhz( 1050c349dbc7Sjsg (unsigned int)min_refresh_in_uhz); 1051fb4d8502Sjsg 1052c349dbc7Sjsg in_out_vrr->max_refresh_in_uhz = (unsigned int)max_refresh_in_uhz; 1053c349dbc7Sjsg in_out_vrr->min_duration_in_us = 1054c349dbc7Sjsg calc_duration_in_us_from_refresh_in_uhz( 1055c349dbc7Sjsg (unsigned int)max_refresh_in_uhz); 1056c349dbc7Sjsg 1057ad8b1aafSjsg if (in_config->state == VRR_STATE_ACTIVE_FIXED) 1058ad8b1aafSjsg in_out_vrr->fixed_refresh_in_uhz = in_config->fixed_refresh_in_uhz; 1059ad8b1aafSjsg else 1060ad8b1aafSjsg in_out_vrr->fixed_refresh_in_uhz = 0; 1061ad8b1aafSjsg 1062ad8b1aafSjsg refresh_range = div_u64(in_out_vrr->max_refresh_in_uhz + 500000, 1000000) - 1063ad8b1aafSjsg + div_u64(in_out_vrr->min_refresh_in_uhz + 500000, 1000000); 1064c349dbc7Sjsg 1065c349dbc7Sjsg in_out_vrr->supported = true; 1066c349dbc7Sjsg } 1067c349dbc7Sjsg 1068c349dbc7Sjsg in_out_vrr->fixed.ramping_active = in_config->ramping; 1069c349dbc7Sjsg 1070c349dbc7Sjsg in_out_vrr->btr.btr_enabled = in_config->btr; 1071c349dbc7Sjsg 1072c349dbc7Sjsg if (in_out_vrr->max_refresh_in_uhz < (2 * in_out_vrr->min_refresh_in_uhz)) 1073c349dbc7Sjsg in_out_vrr->btr.btr_enabled = false; 1074c349dbc7Sjsg else { 1075c349dbc7Sjsg in_out_vrr->btr.margin_in_us = in_out_vrr->max_duration_in_us - 1076c349dbc7Sjsg 2 * in_out_vrr->min_duration_in_us; 1077c349dbc7Sjsg if (in_out_vrr->btr.margin_in_us > BTR_MAX_MARGIN) 1078c349dbc7Sjsg in_out_vrr->btr.margin_in_us = BTR_MAX_MARGIN; 1079c349dbc7Sjsg } 1080c349dbc7Sjsg 1081c349dbc7Sjsg in_out_vrr->btr.btr_active = false; 1082c349dbc7Sjsg in_out_vrr->btr.inserted_duration_in_us = 0; 1083c349dbc7Sjsg in_out_vrr->btr.frames_to_insert = 0; 1084c349dbc7Sjsg in_out_vrr->btr.frame_counter = 0; 1085c349dbc7Sjsg in_out_vrr->fixed.fixed_active = false; 1086c349dbc7Sjsg in_out_vrr->fixed.target_refresh_in_uhz = 0; 1087c349dbc7Sjsg 1088c349dbc7Sjsg in_out_vrr->btr.mid_point_in_us = 1089c349dbc7Sjsg (in_out_vrr->min_duration_in_us + 1090c349dbc7Sjsg in_out_vrr->max_duration_in_us) / 2; 1091c349dbc7Sjsg 1092c349dbc7Sjsg if (in_out_vrr->state == VRR_STATE_UNSUPPORTED) { 1093c349dbc7Sjsg in_out_vrr->adjust.v_total_min = stream->timing.v_total; 1094c349dbc7Sjsg in_out_vrr->adjust.v_total_max = stream->timing.v_total; 1095c349dbc7Sjsg } else if (in_out_vrr->state == VRR_STATE_DISABLED) { 1096c349dbc7Sjsg in_out_vrr->adjust.v_total_min = stream->timing.v_total; 1097c349dbc7Sjsg in_out_vrr->adjust.v_total_max = stream->timing.v_total; 1098c349dbc7Sjsg } else if (in_out_vrr->state == VRR_STATE_INACTIVE) { 1099c349dbc7Sjsg in_out_vrr->adjust.v_total_min = stream->timing.v_total; 1100c349dbc7Sjsg in_out_vrr->adjust.v_total_max = stream->timing.v_total; 1101c349dbc7Sjsg } else if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE && 1102ad8b1aafSjsg refresh_range >= MIN_REFRESH_RANGE) { 1103c349dbc7Sjsg 1104c349dbc7Sjsg in_out_vrr->adjust.v_total_min = 11055ca02815Sjsg mod_freesync_calc_v_total_from_refresh(stream, 1106c349dbc7Sjsg in_out_vrr->max_refresh_in_uhz); 1107c349dbc7Sjsg in_out_vrr->adjust.v_total_max = 11085ca02815Sjsg mod_freesync_calc_v_total_from_refresh(stream, 1109c349dbc7Sjsg in_out_vrr->min_refresh_in_uhz); 1110c349dbc7Sjsg } else if (in_out_vrr->state == VRR_STATE_ACTIVE_FIXED) { 1111c349dbc7Sjsg in_out_vrr->fixed.target_refresh_in_uhz = 1112ad8b1aafSjsg in_out_vrr->fixed_refresh_in_uhz; 1113c349dbc7Sjsg if (in_out_vrr->fixed.ramping_active && 1114c349dbc7Sjsg in_out_vrr->fixed.fixed_active) { 1115c349dbc7Sjsg /* Do not update vtotals if ramping is already active 1116c349dbc7Sjsg * in order to continue ramp from current refresh. 1117c349dbc7Sjsg */ 1118c349dbc7Sjsg in_out_vrr->fixed.fixed_active = true; 1119c349dbc7Sjsg } else { 1120c349dbc7Sjsg in_out_vrr->fixed.fixed_active = true; 1121c349dbc7Sjsg in_out_vrr->adjust.v_total_min = 11225ca02815Sjsg mod_freesync_calc_v_total_from_refresh(stream, 1123c349dbc7Sjsg in_out_vrr->fixed.target_refresh_in_uhz); 1124c349dbc7Sjsg in_out_vrr->adjust.v_total_max = 1125c349dbc7Sjsg in_out_vrr->adjust.v_total_min; 1126c349dbc7Sjsg } 1127c349dbc7Sjsg } else { 1128c349dbc7Sjsg in_out_vrr->state = VRR_STATE_INACTIVE; 1129c349dbc7Sjsg in_out_vrr->adjust.v_total_min = stream->timing.v_total; 1130c349dbc7Sjsg in_out_vrr->adjust.v_total_max = stream->timing.v_total; 1131fb4d8502Sjsg } 1132fb4d8502Sjsg } 1133fb4d8502Sjsg 1134c349dbc7Sjsg void mod_freesync_handle_preflip(struct mod_freesync *mod_freesync, 1135c349dbc7Sjsg const struct dc_plane_state *plane, 1136c349dbc7Sjsg const struct dc_stream_state *stream, 1137c349dbc7Sjsg unsigned int curr_time_stamp_in_us, 1138c349dbc7Sjsg struct mod_vrr_params *in_out_vrr) 1139fb4d8502Sjsg { 1140fb4d8502Sjsg struct core_freesync *core_freesync = NULL; 1141c349dbc7Sjsg unsigned int last_render_time_in_us = 0; 1142fb4d8502Sjsg 1143fb4d8502Sjsg if (mod_freesync == NULL) 1144fb4d8502Sjsg return; 1145fb4d8502Sjsg 1146fb4d8502Sjsg core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); 1147fb4d8502Sjsg 1148c349dbc7Sjsg if (in_out_vrr->supported && 1149c349dbc7Sjsg in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE) { 1150fb4d8502Sjsg 1151fb4d8502Sjsg last_render_time_in_us = curr_time_stamp_in_us - 1152c349dbc7Sjsg plane->time.prev_update_time_in_us; 1153fb4d8502Sjsg 1154c349dbc7Sjsg if (in_out_vrr->btr.btr_enabled) { 1155fb4d8502Sjsg apply_below_the_range(core_freesync, 1156c349dbc7Sjsg stream, 1157c349dbc7Sjsg last_render_time_in_us, 1158c349dbc7Sjsg in_out_vrr); 1159fb4d8502Sjsg } else { 1160fb4d8502Sjsg apply_fixed_refresh(core_freesync, 1161c349dbc7Sjsg stream, 1162c349dbc7Sjsg last_render_time_in_us, 1163c349dbc7Sjsg in_out_vrr); 1164c349dbc7Sjsg } 1165c349dbc7Sjsg 11661bb76ff1Sjsg determine_flip_interval_workaround_req(in_out_vrr, 11671bb76ff1Sjsg curr_time_stamp_in_us); 11681bb76ff1Sjsg 1169fb4d8502Sjsg } 1170fb4d8502Sjsg } 1171fb4d8502Sjsg 1172c349dbc7Sjsg void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, 1173c349dbc7Sjsg const struct dc_stream_state *stream, 1174c349dbc7Sjsg struct mod_vrr_params *in_out_vrr) 1175c349dbc7Sjsg { 1176c349dbc7Sjsg struct core_freesync *core_freesync = NULL; 11771bb76ff1Sjsg unsigned int cur_timestamp_in_us; 11781bb76ff1Sjsg unsigned long long cur_tick; 1179c349dbc7Sjsg 1180c349dbc7Sjsg if ((mod_freesync == NULL) || (stream == NULL) || (in_out_vrr == NULL)) 1181c349dbc7Sjsg return; 1182c349dbc7Sjsg 1183c349dbc7Sjsg core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); 1184c349dbc7Sjsg 1185c349dbc7Sjsg if (in_out_vrr->supported == false) 1186c349dbc7Sjsg return; 1187c349dbc7Sjsg 11881bb76ff1Sjsg cur_tick = dm_get_timestamp(core_freesync->dc->ctx); 11891bb76ff1Sjsg cur_timestamp_in_us = (unsigned int) 11901bb76ff1Sjsg div_u64(dm_get_elapse_time_in_ns(core_freesync->dc->ctx, cur_tick, 0), 1000); 11911bb76ff1Sjsg 11921bb76ff1Sjsg in_out_vrr->flip_interval.vsyncs_between_flip++; 11931bb76ff1Sjsg in_out_vrr->flip_interval.v_update_timestamp_in_us = cur_timestamp_in_us; 11941bb76ff1Sjsg 11951bb76ff1Sjsg if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE && 11961bb76ff1Sjsg (in_out_vrr->flip_interval.flip_interval_workaround_active || 11971bb76ff1Sjsg (!in_out_vrr->flip_interval.flip_interval_workaround_active && 11981bb76ff1Sjsg in_out_vrr->flip_interval.program_flip_interval_workaround))) { 11991bb76ff1Sjsg // set freesync vmin vmax to nominal for workaround 12001bb76ff1Sjsg in_out_vrr->adjust.v_total_min = 12011bb76ff1Sjsg mod_freesync_calc_v_total_from_refresh( 12021bb76ff1Sjsg stream, in_out_vrr->max_refresh_in_uhz); 12031bb76ff1Sjsg in_out_vrr->adjust.v_total_max = 12041bb76ff1Sjsg in_out_vrr->adjust.v_total_min; 12051bb76ff1Sjsg in_out_vrr->flip_interval.program_flip_interval_workaround = false; 12061bb76ff1Sjsg in_out_vrr->flip_interval.do_flip_interval_workaround_cleanup = true; 12071bb76ff1Sjsg return; 12081bb76ff1Sjsg } 12091bb76ff1Sjsg 12101bb76ff1Sjsg if (in_out_vrr->state != VRR_STATE_ACTIVE_VARIABLE && 12111bb76ff1Sjsg in_out_vrr->flip_interval.do_flip_interval_workaround_cleanup) { 12121bb76ff1Sjsg in_out_vrr->flip_interval.do_flip_interval_workaround_cleanup = false; 12131bb76ff1Sjsg in_out_vrr->flip_interval.flip_interval_detect_counter = 0; 12141bb76ff1Sjsg in_out_vrr->flip_interval.vsyncs_between_flip = 0; 12151bb76ff1Sjsg in_out_vrr->flip_interval.vsync_to_flip_in_us = 0; 12161bb76ff1Sjsg } 12171bb76ff1Sjsg 1218c349dbc7Sjsg /* Below the Range Logic */ 1219c349dbc7Sjsg 1220c349dbc7Sjsg /* Only execute if in fullscreen mode */ 1221c349dbc7Sjsg if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE && 1222c349dbc7Sjsg in_out_vrr->btr.btr_active) { 1223c349dbc7Sjsg /* TODO: pass in flag for Pre-DCE12 ASIC 1224c349dbc7Sjsg * in order for frame variable duration to take affect, 1225c349dbc7Sjsg * it needs to be done one VSYNC early, which is at 1226c349dbc7Sjsg * frameCounter == 1. 1227c349dbc7Sjsg * For DCE12 and newer updates to V_TOTAL_MIN/MAX 1228c349dbc7Sjsg * will take affect on current frame 1229c349dbc7Sjsg */ 1230c349dbc7Sjsg if (in_out_vrr->btr.frames_to_insert == 1231c349dbc7Sjsg in_out_vrr->btr.frame_counter) { 1232c349dbc7Sjsg in_out_vrr->adjust.v_total_min = 1233c349dbc7Sjsg calc_v_total_from_duration(stream, 1234c349dbc7Sjsg in_out_vrr, 1235c349dbc7Sjsg in_out_vrr->btr.inserted_duration_in_us); 1236c349dbc7Sjsg in_out_vrr->adjust.v_total_max = 1237c349dbc7Sjsg in_out_vrr->adjust.v_total_min; 1238fb4d8502Sjsg } 1239fb4d8502Sjsg 1240c349dbc7Sjsg if (in_out_vrr->btr.frame_counter > 0) 1241c349dbc7Sjsg in_out_vrr->btr.frame_counter--; 1242c349dbc7Sjsg 1243c349dbc7Sjsg /* Restore FreeSync */ 1244c349dbc7Sjsg if (in_out_vrr->btr.frame_counter == 0) { 1245c349dbc7Sjsg in_out_vrr->adjust.v_total_min = 12465ca02815Sjsg mod_freesync_calc_v_total_from_refresh(stream, 1247c349dbc7Sjsg in_out_vrr->max_refresh_in_uhz); 1248c349dbc7Sjsg in_out_vrr->adjust.v_total_max = 12495ca02815Sjsg mod_freesync_calc_v_total_from_refresh(stream, 1250c349dbc7Sjsg in_out_vrr->min_refresh_in_uhz); 1251c349dbc7Sjsg } 1252c349dbc7Sjsg } 1253c349dbc7Sjsg 1254c349dbc7Sjsg /* If in fullscreen freesync mode or in video, do not program 1255c349dbc7Sjsg * static screen ramp values 1256c349dbc7Sjsg */ 1257c349dbc7Sjsg if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE) 1258c349dbc7Sjsg in_out_vrr->fixed.ramping_active = false; 1259c349dbc7Sjsg 12605ca02815Sjsg /* Gradual Static Screen Ramping Logic 12615ca02815Sjsg * Execute if ramp is active and user enabled freesync static screen 12625ca02815Sjsg */ 1263c349dbc7Sjsg if (in_out_vrr->state == VRR_STATE_ACTIVE_FIXED && 1264c349dbc7Sjsg in_out_vrr->fixed.ramping_active) { 1265c349dbc7Sjsg update_v_total_for_static_ramp( 1266c349dbc7Sjsg core_freesync, stream, in_out_vrr); 1267fb4d8502Sjsg } 1268fb4d8502Sjsg } 1269fb4d8502Sjsg 1270fb4d8502Sjsg void mod_freesync_get_settings(struct mod_freesync *mod_freesync, 1271c349dbc7Sjsg const struct mod_vrr_params *vrr, 1272fb4d8502Sjsg unsigned int *v_total_min, unsigned int *v_total_max, 1273fb4d8502Sjsg unsigned int *event_triggers, 1274fb4d8502Sjsg unsigned int *window_min, unsigned int *window_max, 1275fb4d8502Sjsg unsigned int *lfc_mid_point_in_us, 1276fb4d8502Sjsg unsigned int *inserted_frames, 1277fb4d8502Sjsg unsigned int *inserted_duration_in_us) 1278fb4d8502Sjsg { 1279fb4d8502Sjsg if (mod_freesync == NULL) 1280fb4d8502Sjsg return; 1281fb4d8502Sjsg 1282c349dbc7Sjsg if (vrr->supported) { 1283c349dbc7Sjsg *v_total_min = vrr->adjust.v_total_min; 1284c349dbc7Sjsg *v_total_max = vrr->adjust.v_total_max; 1285fb4d8502Sjsg *event_triggers = 0; 1286c349dbc7Sjsg *lfc_mid_point_in_us = vrr->btr.mid_point_in_us; 1287c349dbc7Sjsg *inserted_frames = vrr->btr.frames_to_insert; 1288c349dbc7Sjsg *inserted_duration_in_us = vrr->btr.inserted_duration_in_us; 1289c349dbc7Sjsg } 1290fb4d8502Sjsg } 1291fb4d8502Sjsg 1292c349dbc7Sjsg unsigned long long mod_freesync_calc_nominal_field_rate( 1293c349dbc7Sjsg const struct dc_stream_state *stream) 1294c349dbc7Sjsg { 1295c349dbc7Sjsg unsigned long long nominal_field_rate_in_uhz = 0; 1296c349dbc7Sjsg unsigned int total = stream->timing.h_total * stream->timing.v_total; 1297c349dbc7Sjsg 1298c349dbc7Sjsg /* Calculate nominal field rate for stream, rounded up to nearest integer */ 1299c349dbc7Sjsg nominal_field_rate_in_uhz = stream->timing.pix_clk_100hz; 1300c349dbc7Sjsg nominal_field_rate_in_uhz *= 100000000ULL; 1301c349dbc7Sjsg 1302c349dbc7Sjsg nominal_field_rate_in_uhz = div_u64(nominal_field_rate_in_uhz, total); 1303c349dbc7Sjsg 1304c349dbc7Sjsg return nominal_field_rate_in_uhz; 1305fb4d8502Sjsg } 1306c349dbc7Sjsg 13075ca02815Sjsg unsigned long long mod_freesync_calc_field_rate_from_timing( 13085ca02815Sjsg unsigned int vtotal, unsigned int htotal, unsigned int pix_clk) 13095ca02815Sjsg { 13105ca02815Sjsg unsigned long long field_rate_in_uhz = 0; 13115ca02815Sjsg unsigned int total = htotal * vtotal; 13125ca02815Sjsg 13135ca02815Sjsg /* Calculate nominal field rate for stream, rounded up to nearest integer */ 13145ca02815Sjsg field_rate_in_uhz = pix_clk; 13155ca02815Sjsg field_rate_in_uhz *= 1000000ULL; 13165ca02815Sjsg 13175ca02815Sjsg field_rate_in_uhz = div_u64(field_rate_in_uhz, total); 13185ca02815Sjsg 13195ca02815Sjsg return field_rate_in_uhz; 13205ca02815Sjsg } 13215ca02815Sjsg 13221bb76ff1Sjsg bool mod_freesync_get_freesync_enabled(struct mod_vrr_params *pVrr) 13231bb76ff1Sjsg { 13241bb76ff1Sjsg return (pVrr->state != VRR_STATE_UNSUPPORTED) && (pVrr->state != VRR_STATE_DISABLED); 13251bb76ff1Sjsg } 13261bb76ff1Sjsg 1327ad8b1aafSjsg bool mod_freesync_is_valid_range(uint32_t min_refresh_cap_in_uhz, 1328c349dbc7Sjsg uint32_t max_refresh_cap_in_uhz, 1329ad8b1aafSjsg uint32_t nominal_field_rate_in_uhz) 1330c349dbc7Sjsg { 1331c349dbc7Sjsg 1332c349dbc7Sjsg /* Typically nominal refresh calculated can have some fractional part. 1333c349dbc7Sjsg * Allow for some rounding error of actual video timing by taking floor 1334c349dbc7Sjsg * of caps and request. Round the nominal refresh rate. 1335c349dbc7Sjsg * 1336c349dbc7Sjsg * Dividing will convert everything to units in Hz although input 1337c349dbc7Sjsg * variable name is in uHz! 1338c349dbc7Sjsg * 1339c349dbc7Sjsg * Also note, this takes care of rounding error on the nominal refresh 1340c349dbc7Sjsg * so by rounding error we only expect it to be off by a small amount, 1341c349dbc7Sjsg * such as < 0.1 Hz. i.e. 143.9xxx or 144.1xxx. 1342c349dbc7Sjsg * 1343c349dbc7Sjsg * Example 1. Caps Min = 40 Hz, Max = 144 Hz 1344c349dbc7Sjsg * Request Min = 40 Hz, Max = 144 Hz 1345c349dbc7Sjsg * Nominal = 143.5x Hz rounded to 144 Hz 1346c349dbc7Sjsg * This function should allow this as valid request 1347c349dbc7Sjsg * 1348c349dbc7Sjsg * Example 2. Caps Min = 40 Hz, Max = 144 Hz 1349c349dbc7Sjsg * Request Min = 40 Hz, Max = 144 Hz 1350c349dbc7Sjsg * Nominal = 144.4x Hz rounded to 144 Hz 1351c349dbc7Sjsg * This function should allow this as valid request 1352c349dbc7Sjsg * 1353c349dbc7Sjsg * Example 3. Caps Min = 40 Hz, Max = 144 Hz 1354c349dbc7Sjsg * Request Min = 40 Hz, Max = 144 Hz 1355c349dbc7Sjsg * Nominal = 120.xx Hz rounded to 120 Hz 1356c349dbc7Sjsg * This function should return NOT valid since the requested 1357c349dbc7Sjsg * max is greater than current timing's nominal 1358c349dbc7Sjsg * 1359c349dbc7Sjsg * Example 4. Caps Min = 40 Hz, Max = 120 Hz 1360c349dbc7Sjsg * Request Min = 40 Hz, Max = 120 Hz 1361c349dbc7Sjsg * Nominal = 144.xx Hz rounded to 144 Hz 1362c349dbc7Sjsg * This function should return NOT valid since the nominal 1363c349dbc7Sjsg * is greater than the capability's max refresh 1364c349dbc7Sjsg */ 1365c349dbc7Sjsg nominal_field_rate_in_uhz = 1366c349dbc7Sjsg div_u64(nominal_field_rate_in_uhz + 500000, 1000000); 1367c349dbc7Sjsg min_refresh_cap_in_uhz /= 1000000; 1368c349dbc7Sjsg max_refresh_cap_in_uhz /= 1000000; 1369c349dbc7Sjsg 13705ca02815Sjsg /* Check nominal is within range */ 1371c349dbc7Sjsg if (nominal_field_rate_in_uhz > max_refresh_cap_in_uhz || 1372c349dbc7Sjsg nominal_field_rate_in_uhz < min_refresh_cap_in_uhz) 1373c349dbc7Sjsg return false; 1374c349dbc7Sjsg 13755ca02815Sjsg /* If nominal is less than max, limit the max allowed refresh rate */ 1376c349dbc7Sjsg if (nominal_field_rate_in_uhz < max_refresh_cap_in_uhz) 1377c349dbc7Sjsg max_refresh_cap_in_uhz = nominal_field_rate_in_uhz; 1378c349dbc7Sjsg 13795ca02815Sjsg /* Check min is within range */ 1380ad8b1aafSjsg if (min_refresh_cap_in_uhz > max_refresh_cap_in_uhz) 1381c349dbc7Sjsg return false; 1382c349dbc7Sjsg 13835ca02815Sjsg /* For variable range, check for at least 10 Hz range */ 1384ad8b1aafSjsg if (nominal_field_rate_in_uhz - min_refresh_cap_in_uhz < 10) 1385c349dbc7Sjsg return false; 1386c349dbc7Sjsg 1387c349dbc7Sjsg return true; 1388fb4d8502Sjsg } 1389