1 /* 2 * Copyright 2016 Advanced Micro Devices, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: AMD 23 * 24 */ 25 26 #include "dm_services.h" 27 #include "dc.h" 28 #include "mod_freesync.h" 29 #include "core_types.h" 30 #include "core_dc.h" 31 32 #define MOD_FREESYNC_MAX_CONCURRENT_STREAMS 32 33 34 /* Refresh rate ramp at a fixed rate of 65 Hz/second */ 35 #define STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME ((1000 / 60) * 65) 36 /* Number of elements in the render times cache array */ 37 #define RENDER_TIMES_MAX_COUNT 20 38 /* Threshold to exit BTR (to avoid frequent enter-exits at the lower limit) */ 39 #define BTR_EXIT_MARGIN 2000 40 41 #define FREESYNC_REGISTRY_NAME "freesync_v1" 42 43 struct gradual_static_ramp { 44 bool ramp_is_active; 45 bool ramp_direction_is_up; 46 unsigned int ramp_current_frame_duration_in_ns; 47 }; 48 49 struct time_cache { 50 /* video (48Hz feature) related */ 51 unsigned int update_duration_in_ns; 52 53 /* BTR/fixed refresh related */ 54 unsigned int prev_time_stamp_in_us; 55 56 unsigned int min_render_time_in_us; 57 unsigned int max_render_time_in_us; 58 59 unsigned int render_times_index; 60 unsigned int render_times[RENDER_TIMES_MAX_COUNT]; 61 }; 62 63 struct below_the_range { 64 bool btr_active; 65 bool program_btr; 66 67 unsigned int mid_point_in_us; 68 69 unsigned int inserted_frame_duration_in_us; 70 unsigned int frames_to_insert; 71 unsigned int frame_counter; 72 }; 73 74 struct fixed_refresh { 75 bool fixed_refresh_active; 76 bool program_fixed_refresh; 77 }; 78 79 struct freesync_state { 80 bool fullscreen; 81 bool static_screen; 82 bool video; 83 84 unsigned int nominal_refresh_rate_in_micro_hz; 85 bool windowed_fullscreen; 86 87 struct time_cache time; 88 89 struct gradual_static_ramp static_ramp; 90 struct below_the_range btr; 91 struct fixed_refresh fixed_refresh; 92 }; 93 94 struct freesync_entity { 95 const struct dc_stream *stream; 96 struct mod_freesync_caps *caps; 97 struct freesync_state state; 98 struct mod_freesync_user_enable user_enable; 99 }; 100 101 struct core_freesync { 102 struct mod_freesync public; 103 struct dc *dc; 104 struct freesync_entity *map; 105 int num_entities; 106 }; 107 108 #define MOD_FREESYNC_TO_CORE(mod_freesync)\ 109 container_of(mod_freesync, struct core_freesync, public) 110 111 static bool check_dc_support(const struct dc *dc) 112 { 113 if (dc->stream_funcs.adjust_vmin_vmax == NULL) 114 return false; 115 116 return true; 117 } 118 119 struct mod_freesync *mod_freesync_create(struct dc *dc) 120 { 121 struct core_freesync *core_freesync = 122 dm_alloc(sizeof(struct core_freesync)); 123 124 struct core_dc *core_dc = DC_TO_CORE(dc); 125 126 struct persistent_data_flag flag; 127 128 int i = 0; 129 130 if (core_freesync == NULL) 131 goto fail_alloc_context; 132 133 core_freesync->map = dm_alloc(sizeof(struct freesync_entity) * 134 MOD_FREESYNC_MAX_CONCURRENT_STREAMS); 135 136 if (core_freesync->map == NULL) 137 goto fail_alloc_map; 138 139 for (i = 0; i < MOD_FREESYNC_MAX_CONCURRENT_STREAMS; i++) 140 core_freesync->map[i].stream = NULL; 141 142 core_freesync->num_entities = 0; 143 144 if (dc == NULL) 145 goto fail_construct; 146 147 core_freesync->dc = dc; 148 149 if (!check_dc_support(dc)) 150 goto fail_construct; 151 152 /* Create initial module folder in registry for freesync enable data */ 153 flag.save_per_edid = true; 154 flag.save_per_link = false; 155 dm_write_persistent_data(core_dc->ctx, NULL, FREESYNC_REGISTRY_NAME, NULL, NULL, 156 0, &flag); 157 158 return &core_freesync->public; 159 160 fail_construct: 161 dm_free(core_freesync->map); 162 163 fail_alloc_map: 164 dm_free(core_freesync); 165 166 fail_alloc_context: 167 return NULL; 168 } 169 170 void mod_freesync_destroy(struct mod_freesync *mod_freesync) 171 { 172 if (mod_freesync != NULL) { 173 int i; 174 struct core_freesync *core_freesync = 175 MOD_FREESYNC_TO_CORE(mod_freesync); 176 177 for (i = 0; i < core_freesync->num_entities; i++) 178 if (core_freesync->map[i].stream) 179 dc_stream_release(core_freesync->map[i].stream); 180 181 dm_free(core_freesync->map); 182 183 dm_free(core_freesync); 184 } 185 } 186 187 /* Given a specific dc_stream* this function finds its equivalent 188 * on the core_freesync->map and returns the corresponding index 189 */ 190 static unsigned int map_index_from_stream(struct core_freesync *core_freesync, 191 const struct dc_stream *stream) 192 { 193 unsigned int index = 0; 194 195 for (index = 0; index < core_freesync->num_entities; index++) { 196 if (core_freesync->map[index].stream == stream) { 197 return index; 198 } 199 } 200 /* Could not find stream requested */ 201 ASSERT(false); 202 return index; 203 } 204 205 bool mod_freesync_add_stream(struct mod_freesync *mod_freesync, 206 const struct dc_stream *stream, struct mod_freesync_caps *caps) 207 { 208 struct core_stream *core_stream = NULL; 209 struct core_dc *core_dc = NULL; 210 struct core_freesync *core_freesync = NULL; 211 212 if (mod_freesync == NULL) 213 return false; 214 215 core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); 216 core_stream = DC_STREAM_TO_CORE(stream); 217 core_dc = DC_TO_CORE(core_freesync->dc); 218 219 int persistent_freesync_enable = 0; 220 struct persistent_data_flag flag; 221 222 flag.save_per_edid = true; 223 flag.save_per_link = false; 224 225 if (core_freesync->num_entities < MOD_FREESYNC_MAX_CONCURRENT_STREAMS) { 226 227 dc_stream_retain(stream); 228 229 core_freesync->map[core_freesync->num_entities].stream = stream; 230 core_freesync->map[core_freesync->num_entities].caps = caps; 231 232 core_freesync->map[core_freesync->num_entities].state. 233 fullscreen = false; 234 core_freesync->map[core_freesync->num_entities].state. 235 static_screen = false; 236 core_freesync->map[core_freesync->num_entities].state. 237 video = false; 238 core_freesync->map[core_freesync->num_entities].state.time. 239 update_duration_in_ns = 0; 240 core_freesync->map[core_freesync->num_entities].state. 241 static_ramp.ramp_is_active = false; 242 243 /* get persistent data from registry */ 244 if (dm_read_persistent_data(core_dc->ctx, stream->sink, 245 FREESYNC_REGISTRY_NAME, 246 "userenable", &persistent_freesync_enable, 247 sizeof(int), &flag)) { 248 core_freesync->map[core_freesync->num_entities].user_enable. 249 enable_for_gaming = 250 (persistent_freesync_enable & 1) ? true : false; 251 core_freesync->map[core_freesync->num_entities].user_enable. 252 enable_for_static = 253 (persistent_freesync_enable & 2) ? true : false; 254 core_freesync->map[core_freesync->num_entities].user_enable. 255 enable_for_video = 256 (persistent_freesync_enable & 4) ? true : false; 257 } else { 258 core_freesync->map[core_freesync->num_entities].user_enable. 259 enable_for_gaming = false; 260 core_freesync->map[core_freesync->num_entities].user_enable. 261 enable_for_static = false; 262 core_freesync->map[core_freesync->num_entities].user_enable. 263 enable_for_video = false; 264 } 265 266 if (caps->supported) 267 core_stream->public.ignore_msa_timing_param = 1; 268 269 core_freesync->num_entities++; 270 return true; 271 } 272 return false; 273 } 274 275 bool mod_freesync_remove_stream(struct mod_freesync *mod_freesync, 276 const struct dc_stream *stream) 277 { 278 int i = 0; 279 struct core_freesync *core_freesync = NULL; 280 unsigned int index = 0; 281 282 if (mod_freesync == NULL) 283 return false; 284 285 core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); 286 index = map_index_from_stream(core_freesync, stream); 287 288 dc_stream_release(core_freesync->map[index].stream); 289 core_freesync->map[index].stream = NULL; 290 /* To remove this entity, shift everything after down */ 291 for (i = index; i < core_freesync->num_entities - 1; i++) 292 core_freesync->map[i] = core_freesync->map[i + 1]; 293 core_freesync->num_entities--; 294 return true; 295 } 296 297 static void update_stream_freesync_context(struct core_freesync *core_freesync, 298 const struct dc_stream *stream) 299 { 300 unsigned int index; 301 struct freesync_context *ctx; 302 struct core_stream *core_stream; 303 304 core_stream = DC_STREAM_TO_CORE(stream); 305 ctx = &core_stream->public.freesync_ctx; 306 307 index = map_index_from_stream(core_freesync, stream); 308 309 ctx->supported = core_freesync->map[index].caps->supported; 310 ctx->enabled = (core_freesync->map[index].user_enable.enable_for_gaming || 311 core_freesync->map[index].user_enable.enable_for_video || 312 core_freesync->map[index].user_enable.enable_for_static); 313 ctx->active = (core_freesync->map[index].state.fullscreen || 314 core_freesync->map[index].state.video || 315 core_freesync->map[index].state.static_ramp.ramp_is_active); 316 ctx->min_refresh_in_micro_hz = 317 core_freesync->map[index].caps->min_refresh_in_micro_hz; 318 ctx->nominal_refresh_in_micro_hz = core_freesync-> 319 map[index].state.nominal_refresh_rate_in_micro_hz; 320 321 } 322 323 static void update_stream(struct core_freesync *core_freesync, 324 const struct dc_stream *stream) 325 { 326 struct core_stream *core_stream = DC_STREAM_TO_CORE(stream); 327 328 unsigned int index = map_index_from_stream(core_freesync, stream); 329 if (core_freesync->map[index].caps->supported) { 330 core_stream->public.ignore_msa_timing_param = 1; 331 update_stream_freesync_context(core_freesync, stream); 332 } 333 } 334 335 static void calc_vmin_vmax(struct core_freesync *core_freesync, 336 const struct dc_stream *stream, int *vmin, int *vmax) 337 { 338 unsigned int min_frame_duration_in_ns = 0, max_frame_duration_in_ns = 0; 339 unsigned int index = map_index_from_stream(core_freesync, stream); 340 341 min_frame_duration_in_ns = ((unsigned int) (div64_u64( 342 (1000000000ULL * 1000000), 343 core_freesync->map[index].state. 344 nominal_refresh_rate_in_micro_hz))); 345 max_frame_duration_in_ns = ((unsigned int) (div64_u64( 346 (1000000000ULL * 1000000), 347 core_freesync->map[index].caps->min_refresh_in_micro_hz))); 348 349 *vmax = div64_u64(div64_u64(((unsigned long long)( 350 max_frame_duration_in_ns) * stream->timing.pix_clk_khz), 351 stream->timing.h_total), 1000000); 352 *vmin = div64_u64(div64_u64(((unsigned long long)( 353 min_frame_duration_in_ns) * stream->timing.pix_clk_khz), 354 stream->timing.h_total), 1000000); 355 } 356 357 static void calc_v_total_from_duration(const struct dc_stream *stream, 358 unsigned int duration_in_ns, int *v_total_nominal) 359 { 360 *v_total_nominal = div64_u64(div64_u64(((unsigned long long)( 361 duration_in_ns) * stream->timing.pix_clk_khz), 362 stream->timing.h_total), 1000000); 363 } 364 365 static void calc_v_total_for_static_ramp(struct core_freesync *core_freesync, 366 const struct dc_stream *stream, 367 unsigned int index, int *v_total) 368 { 369 unsigned int frame_duration = 0; 370 371 struct gradual_static_ramp *static_ramp_variables = 372 &core_freesync->map[index].state.static_ramp; 373 374 /* Calc ratio between new and current frame duration with 3 digit */ 375 unsigned int frame_duration_ratio = div64_u64(1000000, 376 (1000 + div64_u64(((unsigned long long)( 377 STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME) * 378 static_ramp_variables->ramp_current_frame_duration_in_ns), 379 1000000000))); 380 381 /* Calculate delta between new and current frame duration in ns */ 382 unsigned int frame_duration_delta = div64_u64(((unsigned long long)( 383 static_ramp_variables->ramp_current_frame_duration_in_ns) * 384 (1000 - frame_duration_ratio)), 1000); 385 386 /* Adjust frame duration delta based on ratio between current and 387 * standard frame duration (frame duration at 60 Hz refresh rate). 388 */ 389 unsigned int ramp_rate_interpolated = div64_u64(((unsigned long long)( 390 frame_duration_delta) * static_ramp_variables-> 391 ramp_current_frame_duration_in_ns), 16666666); 392 393 /* Going to a higher refresh rate (lower frame duration) */ 394 if (static_ramp_variables->ramp_direction_is_up) { 395 /* reduce frame duration */ 396 static_ramp_variables->ramp_current_frame_duration_in_ns -= 397 ramp_rate_interpolated; 398 399 /* min frame duration */ 400 frame_duration = ((unsigned int) (div64_u64( 401 (1000000000ULL * 1000000), 402 core_freesync->map[index].state. 403 nominal_refresh_rate_in_micro_hz))); 404 405 /* adjust for frame duration below min */ 406 if (static_ramp_variables->ramp_current_frame_duration_in_ns <= 407 frame_duration) { 408 409 static_ramp_variables->ramp_is_active = false; 410 static_ramp_variables-> 411 ramp_current_frame_duration_in_ns = 412 frame_duration; 413 } 414 /* Going to a lower refresh rate (larger frame duration) */ 415 } else { 416 /* increase frame duration */ 417 static_ramp_variables->ramp_current_frame_duration_in_ns += 418 ramp_rate_interpolated; 419 420 /* max frame duration */ 421 frame_duration = ((unsigned int) (div64_u64( 422 (1000000000ULL * 1000000), 423 core_freesync->map[index].caps->min_refresh_in_micro_hz))); 424 425 /* adjust for frame duration above max */ 426 if (static_ramp_variables->ramp_current_frame_duration_in_ns >= 427 frame_duration) { 428 429 static_ramp_variables->ramp_is_active = false; 430 static_ramp_variables-> 431 ramp_current_frame_duration_in_ns = 432 frame_duration; 433 } 434 } 435 436 calc_v_total_from_duration(stream, static_ramp_variables-> 437 ramp_current_frame_duration_in_ns, v_total); 438 } 439 440 static void reset_freesync_state_variables(struct freesync_state* state) 441 { 442 state->static_ramp.ramp_is_active = false; 443 if (state->nominal_refresh_rate_in_micro_hz) 444 state->static_ramp.ramp_current_frame_duration_in_ns = 445 ((unsigned int) (div64_u64( 446 (1000000000ULL * 1000000), 447 state->nominal_refresh_rate_in_micro_hz))); 448 449 state->btr.btr_active = false; 450 state->btr.frame_counter = 0; 451 state->btr.frames_to_insert = 0; 452 state->btr.inserted_frame_duration_in_us = 0; 453 state->btr.program_btr = false; 454 455 state->fixed_refresh.fixed_refresh_active = false; 456 state->fixed_refresh.program_fixed_refresh = false; 457 } 458 /* 459 * Sets freesync mode on a stream depending on current freesync state. 460 */ 461 static bool set_freesync_on_streams(struct core_freesync *core_freesync, 462 const struct dc_stream **streams, int num_streams) 463 { 464 int v_total_nominal = 0, v_total_min = 0, v_total_max = 0; 465 unsigned int stream_idx, map_index = 0; 466 struct freesync_state *state; 467 468 if (num_streams == 0 || streams == NULL || num_streams > 1) 469 return false; 470 471 for (stream_idx = 0; stream_idx < num_streams; stream_idx++) { 472 473 map_index = map_index_from_stream(core_freesync, 474 streams[stream_idx]); 475 476 state = &core_freesync->map[map_index].state; 477 478 if (core_freesync->map[map_index].caps->supported) { 479 480 /* Fullscreen has the topmost priority. If the 481 * fullscreen bit is set, we are in a fullscreen 482 * application where it should not matter if it is 483 * static screen. We should not check the static_screen 484 * or video bit. 485 * 486 * Special cases of fullscreen include btr and fixed 487 * refresh. We program btr on every flip and involves 488 * programming full range right before the last inserted frame. 489 * However, we do not want to program the full freesync range 490 * when fixed refresh is active, because we only program 491 * that logic once and this will override it. 492 */ 493 if (core_freesync->map[map_index].user_enable. 494 enable_for_gaming == true && 495 state->fullscreen == true && 496 state->fixed_refresh.fixed_refresh_active == false) { 497 /* Enable freesync */ 498 499 calc_vmin_vmax(core_freesync, 500 streams[stream_idx], 501 &v_total_min, &v_total_max); 502 503 /* Update the freesync context for the stream */ 504 update_stream_freesync_context(core_freesync, 505 streams[stream_idx]); 506 507 core_freesync->dc->stream_funcs. 508 adjust_vmin_vmax(core_freesync->dc, streams, 509 num_streams, v_total_min, 510 v_total_max); 511 512 return true; 513 514 } else if (core_freesync->map[map_index].user_enable. 515 enable_for_video && state->video == true) { 516 /* Enable 48Hz feature */ 517 518 calc_v_total_from_duration(streams[stream_idx], 519 state->time.update_duration_in_ns, 520 &v_total_nominal); 521 522 /* Program only if v_total_nominal is in range*/ 523 if (v_total_nominal >= 524 streams[stream_idx]->timing.v_total) { 525 526 /* Update the freesync context for 527 * the stream 528 */ 529 update_stream_freesync_context( 530 core_freesync, 531 streams[stream_idx]); 532 533 core_freesync->dc->stream_funcs. 534 adjust_vmin_vmax( 535 core_freesync->dc, streams, 536 num_streams, v_total_nominal, 537 v_total_nominal); 538 } 539 return true; 540 541 } else { 542 /* Disable freesync */ 543 v_total_nominal = streams[stream_idx]-> 544 timing.v_total; 545 546 /* Update the freesync context for 547 * the stream 548 */ 549 update_stream_freesync_context( 550 core_freesync, 551 streams[stream_idx]); 552 553 core_freesync->dc->stream_funcs. 554 adjust_vmin_vmax( 555 core_freesync->dc, streams, 556 num_streams, v_total_nominal, 557 v_total_nominal); 558 559 /* Reset the cached variables */ 560 reset_freesync_state_variables(state); 561 562 return true; 563 } 564 } else { 565 /* Disable freesync */ 566 v_total_nominal = streams[stream_idx]-> 567 timing.v_total; 568 /* 569 * we have to reset drr always even sink does 570 * not support freesync because a former stream has 571 * be programmed 572 */ 573 core_freesync->dc->stream_funcs. 574 adjust_vmin_vmax( 575 core_freesync->dc, streams, 576 num_streams, v_total_nominal, 577 v_total_nominal); 578 /* Reset the cached variables */ 579 reset_freesync_state_variables(state); 580 } 581 582 } 583 584 return false; 585 } 586 587 static void set_static_ramp_variables(struct core_freesync *core_freesync, 588 unsigned int index, bool enable_static_screen) 589 { 590 unsigned int frame_duration = 0; 591 592 struct gradual_static_ramp *static_ramp_variables = 593 &core_freesync->map[index].state.static_ramp; 594 595 /* If ramp is not active, set initial frame duration depending on 596 * whether we are enabling/disabling static screen mode. If the ramp is 597 * already active, ramp should continue in the opposite direction 598 * starting with the current frame duration 599 */ 600 if (!static_ramp_variables->ramp_is_active) { 601 602 static_ramp_variables->ramp_is_active = true; 603 604 if (enable_static_screen == true) { 605 /* Going to lower refresh rate, so start from max 606 * refresh rate (min frame duration) 607 */ 608 frame_duration = ((unsigned int) (div64_u64( 609 (1000000000ULL * 1000000), 610 core_freesync->map[index].state. 611 nominal_refresh_rate_in_micro_hz))); 612 } else { 613 /* Going to higher refresh rate, so start from min 614 * refresh rate (max frame duration) 615 */ 616 frame_duration = ((unsigned int) (div64_u64( 617 (1000000000ULL * 1000000), 618 core_freesync->map[index].caps->min_refresh_in_micro_hz))); 619 } 620 621 static_ramp_variables-> 622 ramp_current_frame_duration_in_ns = frame_duration; 623 } 624 625 /* If we are ENABLING static screen, refresh rate should go DOWN. 626 * If we are DISABLING static screen, refresh rate should go UP. 627 */ 628 static_ramp_variables->ramp_direction_is_up = !enable_static_screen; 629 } 630 631 void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, 632 const struct dc_stream **streams, int num_streams) 633 { 634 unsigned int index, v_total = 0; 635 struct freesync_state *state; 636 struct core_freesync *core_freesync = NULL; 637 638 if (mod_freesync == NULL) 639 return; 640 641 core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); 642 643 if (core_freesync->num_entities == 0) 644 return; 645 646 index = map_index_from_stream(core_freesync, 647 streams[0]); 648 649 if (core_freesync->map[index].caps->supported == false) 650 return; 651 652 state = &core_freesync->map[index].state; 653 654 /* Below the Range Logic */ 655 656 /* Only execute if in fullscreen mode */ 657 if (state->fullscreen == true && 658 core_freesync->map[index].user_enable.enable_for_gaming) { 659 660 if (state->btr.btr_active) 661 if (state->btr.frame_counter > 0) 662 663 state->btr.frame_counter--; 664 665 if (state->btr.frame_counter == 1) { 666 667 /* Restore FreeSync */ 668 set_freesync_on_streams(core_freesync, streams, 669 num_streams); 670 } 671 } 672 673 /* If in fullscreen freesync mode or in video, do not program 674 * static screen ramp values 675 */ 676 if (state->fullscreen == true || state->video == true) { 677 678 state->static_ramp.ramp_is_active = false; 679 680 return; 681 } 682 683 /* Gradual Static Screen Ramping Logic */ 684 685 /* Execute if ramp is active and user enabled freesync static screen*/ 686 if (state->static_ramp.ramp_is_active && 687 core_freesync->map[index].user_enable.enable_for_static) { 688 689 calc_v_total_for_static_ramp(core_freesync, streams[0], 690 index, &v_total); 691 692 /* Update the freesync context for the stream */ 693 update_stream_freesync_context(core_freesync, streams[0]); 694 695 /* Program static screen ramp values */ 696 core_freesync->dc->stream_funcs.adjust_vmin_vmax( 697 core_freesync->dc, streams, 698 num_streams, v_total, 699 v_total); 700 } 701 } 702 703 void mod_freesync_update_state(struct mod_freesync *mod_freesync, 704 const struct dc_stream **streams, int num_streams, 705 struct mod_freesync_params *freesync_params) 706 { 707 bool freesync_program_required = false; 708 unsigned int stream_index; 709 struct freesync_state *state; 710 struct core_freesync *core_freesync = NULL; 711 712 if (mod_freesync == NULL) 713 return; 714 715 core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); 716 717 if (core_freesync->num_entities == 0) 718 return; 719 720 for(stream_index = 0; stream_index < num_streams; stream_index++) { 721 722 unsigned int map_index = map_index_from_stream(core_freesync, 723 streams[stream_index]); 724 725 state = &core_freesync->map[map_index].state; 726 727 switch (freesync_params->state){ 728 case FREESYNC_STATE_FULLSCREEN: 729 state->fullscreen = freesync_params->enable; 730 freesync_program_required = true; 731 state->windowed_fullscreen = 732 freesync_params->windowed_fullscreen; 733 break; 734 case FREESYNC_STATE_STATIC_SCREEN: 735 /* Static screen ramp is only enabled for embedded 736 * panels. Also change core variables only if there 737 * is a change. 738 */ 739 if (dc_is_embedded_signal( 740 streams[stream_index]->sink->sink_signal) && 741 state->static_screen != 742 freesync_params->enable) { 743 744 /* Change the state flag */ 745 state->static_screen = freesync_params->enable; 746 747 /* Change static screen ramp variables */ 748 set_static_ramp_variables(core_freesync, 749 map_index, 750 freesync_params->enable); 751 } 752 /* We program the ramp starting next VUpdate */ 753 break; 754 case FREESYNC_STATE_VIDEO: 755 /* Change core variables only if there is a change*/ 756 if(freesync_params->update_duration_in_ns != 757 state->time.update_duration_in_ns) { 758 759 state->video = freesync_params->enable; 760 state->time.update_duration_in_ns = 761 freesync_params->update_duration_in_ns; 762 763 freesync_program_required = true; 764 } 765 break; 766 case FREESYNC_STATE_NONE: 767 /* handle here to avoid warning */ 768 break; 769 } 770 } 771 772 if (freesync_program_required) 773 /* Program freesync according to current state*/ 774 set_freesync_on_streams(core_freesync, streams, num_streams); 775 } 776 777 778 bool mod_freesync_get_state(struct mod_freesync *mod_freesync, 779 const struct dc_stream *stream, 780 struct mod_freesync_params *freesync_params) 781 { 782 unsigned int index = NULL; 783 struct core_freesync *core_freesync = NULL; 784 785 if (mod_freesync == NULL) 786 return false; 787 788 core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); 789 index = map_index_from_stream(core_freesync, stream); 790 791 if (core_freesync->map[index].state.fullscreen) { 792 freesync_params->state = FREESYNC_STATE_FULLSCREEN; 793 freesync_params->enable = true; 794 } else if (core_freesync->map[index].state.static_screen) { 795 freesync_params->state = FREESYNC_STATE_STATIC_SCREEN; 796 freesync_params->enable = true; 797 } else if (core_freesync->map[index].state.video) { 798 freesync_params->state = FREESYNC_STATE_VIDEO; 799 freesync_params->enable = true; 800 } else { 801 freesync_params->state = FREESYNC_STATE_NONE; 802 freesync_params->enable = false; 803 } 804 805 freesync_params->update_duration_in_ns = 806 core_freesync->map[index].state.time.update_duration_in_ns; 807 808 freesync_params->windowed_fullscreen = 809 core_freesync->map[index].state.windowed_fullscreen; 810 811 return true; 812 } 813 814 bool mod_freesync_set_user_enable(struct mod_freesync *mod_freesync, 815 const struct dc_stream **streams, int num_streams, 816 struct mod_freesync_user_enable *user_enable) 817 { 818 unsigned int stream_index, map_index; 819 int persistent_data = 0; 820 struct persistent_data_flag flag; 821 struct core_dc *core_dc = NULL; 822 struct core_freesync *core_freesync = NULL; 823 824 if (mod_freesync == NULL) 825 return false; 826 827 core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); 828 core_dc = DC_TO_CORE(core_freesync->dc); 829 830 flag.save_per_edid = true; 831 flag.save_per_link = false; 832 833 for(stream_index = 0; stream_index < num_streams; 834 stream_index++){ 835 836 map_index = map_index_from_stream(core_freesync, 837 streams[stream_index]); 838 839 core_freesync->map[map_index].user_enable = *user_enable; 840 841 /* Write persistent data in registry*/ 842 if (core_freesync->map[map_index].user_enable. 843 enable_for_gaming) 844 persistent_data = persistent_data | 1; 845 if (core_freesync->map[map_index].user_enable. 846 enable_for_static) 847 persistent_data = persistent_data | 2; 848 if (core_freesync->map[map_index].user_enable. 849 enable_for_video) 850 persistent_data = persistent_data | 4; 851 852 dm_write_persistent_data(core_dc->ctx, 853 streams[stream_index]->sink, 854 FREESYNC_REGISTRY_NAME, 855 "userenable", 856 &persistent_data, 857 sizeof(int), 858 &flag); 859 } 860 861 set_freesync_on_streams(core_freesync, streams, num_streams); 862 863 return true; 864 } 865 866 bool mod_freesync_get_user_enable(struct mod_freesync *mod_freesync, 867 const struct dc_stream *stream, 868 struct mod_freesync_user_enable *user_enable) 869 { 870 unsigned int index = 0; 871 struct core_freesync *core_freesync = NULL; 872 873 if (mod_freesync == NULL) 874 return false; 875 876 core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); 877 index = map_index_from_stream(core_freesync, stream); 878 879 *user_enable = core_freesync->map[index].user_enable; 880 881 return true; 882 } 883 884 void mod_freesync_notify_mode_change(struct mod_freesync *mod_freesync, 885 const struct dc_stream **streams, int num_streams) 886 { 887 unsigned int stream_index, map_index; 888 unsigned min_frame_duration_in_ns, max_frame_duration_in_ns; 889 struct freesync_state *state; 890 struct core_freesync *core_freesync = NULL; 891 892 if (mod_freesync == NULL) 893 return; 894 895 core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); 896 897 for (stream_index = 0; stream_index < num_streams; stream_index++) { 898 899 map_index = map_index_from_stream(core_freesync, 900 streams[stream_index]); 901 902 state = &core_freesync->map[map_index].state; 903 904 if (core_freesync->map[map_index].caps->supported) { 905 /* Update the field rate for new timing */ 906 state->nominal_refresh_rate_in_micro_hz = 1000000 * 907 div64_u64(div64_u64((streams[stream_index]-> 908 timing.pix_clk_khz * 1000), 909 streams[stream_index]->timing.v_total), 910 streams[stream_index]->timing.h_total); 911 912 /* Update the stream */ 913 update_stream(core_freesync, streams[stream_index]); 914 915 /* Determine whether BTR can be supported */ 916 min_frame_duration_in_ns = ((unsigned int) (div64_u64( 917 (1000000000ULL * 1000000), 918 state->nominal_refresh_rate_in_micro_hz))); 919 920 max_frame_duration_in_ns = ((unsigned int) (div64_u64( 921 (1000000000ULL * 1000000), 922 core_freesync->map[map_index].caps->min_refresh_in_micro_hz))); 923 924 if (max_frame_duration_in_ns >= 925 2 * min_frame_duration_in_ns) 926 core_freesync->map[map_index].caps->btr_supported = true; 927 else 928 core_freesync->map[map_index].caps->btr_supported = false; 929 930 /* Cache the time variables */ 931 state->time.max_render_time_in_us = 932 max_frame_duration_in_ns / 1000; 933 state->time.min_render_time_in_us = 934 min_frame_duration_in_ns / 1000; 935 state->btr.mid_point_in_us = 936 (max_frame_duration_in_ns + 937 min_frame_duration_in_ns) / 2000; 938 939 } 940 } 941 942 /* Program freesync according to current state*/ 943 set_freesync_on_streams(core_freesync, streams, num_streams); 944 } 945 946 /* Add the timestamps to the cache and determine whether BTR programming 947 * is required, depending on the times calculated 948 */ 949 static void update_timestamps(struct core_freesync *core_freesync, 950 const struct dc_stream *stream, unsigned int map_index, 951 unsigned int last_render_time_in_us) 952 { 953 struct freesync_state *state = &core_freesync->map[map_index].state; 954 955 state->time.render_times[state->time.render_times_index] = 956 last_render_time_in_us; 957 state->time.render_times_index++; 958 959 if (state->time.render_times_index >= RENDER_TIMES_MAX_COUNT) 960 state->time.render_times_index = 0; 961 962 if (last_render_time_in_us + BTR_EXIT_MARGIN < 963 state->time.max_render_time_in_us) { 964 965 /* Exit Below the Range */ 966 if (state->btr.btr_active) { 967 968 state->btr.program_btr = true; 969 state->btr.btr_active = false; 970 state->btr.frame_counter = 0; 971 972 /* Exit Fixed Refresh mode */ 973 } else if (state->fixed_refresh.fixed_refresh_active) { 974 975 state->fixed_refresh.program_fixed_refresh = true; 976 state->fixed_refresh.fixed_refresh_active = false; 977 978 } 979 980 } else if (last_render_time_in_us > state->time.max_render_time_in_us) { 981 982 /* Enter Below the Range */ 983 if (!state->btr.btr_active && 984 core_freesync->map[map_index].caps->btr_supported) { 985 986 state->btr.program_btr = true; 987 state->btr.btr_active = true; 988 989 /* Enter Fixed Refresh mode */ 990 } else if (!state->fixed_refresh.fixed_refresh_active && 991 !core_freesync->map[map_index].caps->btr_supported) { 992 993 state->fixed_refresh.program_fixed_refresh = true; 994 state->fixed_refresh.fixed_refresh_active = true; 995 996 } 997 } 998 999 /* When Below the Range is active, must react on every frame */ 1000 if (state->btr.btr_active) 1001 state->btr.program_btr = true; 1002 } 1003 1004 static void apply_below_the_range(struct core_freesync *core_freesync, 1005 const struct dc_stream *stream, unsigned int map_index, 1006 unsigned int last_render_time_in_us) 1007 { 1008 unsigned int inserted_frame_duration_in_us = 0; 1009 unsigned int mid_point_frames_ceil = 0; 1010 unsigned int mid_point_frames_floor = 0; 1011 unsigned int frame_time_in_us = 0; 1012 unsigned int delta_from_mid_point_in_us_1 = 0xFFFFFFFF; 1013 unsigned int delta_from_mid_point_in_us_2 = 0xFFFFFFFF; 1014 unsigned int frames_to_insert = 0; 1015 unsigned int inserted_frame_v_total = 0; 1016 unsigned int vmin = 0, vmax = 0; 1017 unsigned int min_frame_duration_in_ns = 0; 1018 struct freesync_state *state = &core_freesync->map[map_index].state; 1019 1020 if (!state->btr.program_btr) 1021 return; 1022 1023 state->btr.program_btr = false; 1024 1025 min_frame_duration_in_ns = ((unsigned int) (div64_u64( 1026 (1000000000ULL * 1000000), 1027 state->nominal_refresh_rate_in_micro_hz))); 1028 1029 /* Program BTR */ 1030 1031 /* BTR set to "not active" so disengage */ 1032 if (!state->btr.btr_active) 1033 1034 /* Restore FreeSync */ 1035 set_freesync_on_streams(core_freesync, &stream, 1); 1036 1037 /* BTR set to "active" so engage */ 1038 else { 1039 1040 /* Calculate number of midPoint frames that could fit within 1041 * the render time interval- take ceil of this value 1042 */ 1043 mid_point_frames_ceil = (last_render_time_in_us + 1044 state->btr.mid_point_in_us- 1) / 1045 state->btr.mid_point_in_us; 1046 1047 if (mid_point_frames_ceil > 0) { 1048 1049 frame_time_in_us = last_render_time_in_us / 1050 mid_point_frames_ceil; 1051 delta_from_mid_point_in_us_1 = (state->btr.mid_point_in_us > 1052 frame_time_in_us) ? 1053 (state->btr.mid_point_in_us - frame_time_in_us): 1054 (frame_time_in_us - state->btr.mid_point_in_us); 1055 } 1056 1057 /* Calculate number of midPoint frames that could fit within 1058 * the render time interval- take floor of this value 1059 */ 1060 mid_point_frames_floor = last_render_time_in_us / 1061 state->btr.mid_point_in_us; 1062 1063 if (mid_point_frames_floor > 0) { 1064 1065 frame_time_in_us = last_render_time_in_us / 1066 mid_point_frames_floor; 1067 delta_from_mid_point_in_us_2 = (state->btr.mid_point_in_us > 1068 frame_time_in_us) ? 1069 (state->btr.mid_point_in_us - frame_time_in_us): 1070 (frame_time_in_us - state->btr.mid_point_in_us); 1071 } 1072 1073 /* Choose number of frames to insert based on how close it 1074 * can get to the mid point of the variable range. 1075 */ 1076 if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) 1077 frames_to_insert = mid_point_frames_ceil; 1078 else 1079 frames_to_insert = mid_point_frames_floor; 1080 1081 /* Either we've calculated the number of frames to insert, 1082 * or we need to insert min duration frames 1083 */ 1084 if (frames_to_insert > 0) 1085 inserted_frame_duration_in_us = last_render_time_in_us / 1086 frames_to_insert; 1087 1088 if (inserted_frame_duration_in_us < 1089 state->time.min_render_time_in_us) 1090 1091 inserted_frame_duration_in_us = 1092 state->time.min_render_time_in_us; 1093 1094 /* We need the v_total_min from capability */ 1095 calc_vmin_vmax(core_freesync, stream, &vmin, &vmax); 1096 1097 inserted_frame_v_total = vmin; 1098 if (min_frame_duration_in_ns / 1000) 1099 inserted_frame_v_total = inserted_frame_duration_in_us * 1100 vmin / (min_frame_duration_in_ns / 1000); 1101 1102 /* Set length of inserted frames as v_total_max*/ 1103 vmax = inserted_frame_v_total; 1104 1105 /* Program V_TOTAL */ 1106 core_freesync->dc->stream_funcs.adjust_vmin_vmax( 1107 core_freesync->dc, &stream, 1108 1, vmin, 1109 vmax); 1110 1111 /* Cache the calculated variables */ 1112 state->btr.inserted_frame_duration_in_us = 1113 inserted_frame_duration_in_us; 1114 state->btr.frames_to_insert = frames_to_insert; 1115 state->btr.frame_counter = frames_to_insert; 1116 1117 } 1118 } 1119 1120 static void apply_fixed_refresh(struct core_freesync *core_freesync, 1121 const struct dc_stream *stream, unsigned int map_index) 1122 { 1123 unsigned int vmin = 0, vmax = 0; 1124 struct freesync_state *state = &core_freesync->map[map_index].state; 1125 1126 if (!state->fixed_refresh.program_fixed_refresh) 1127 return; 1128 1129 state->fixed_refresh.program_fixed_refresh = false; 1130 1131 /* Program Fixed Refresh */ 1132 1133 /* Fixed Refresh set to "not active" so disengage */ 1134 if (!state->fixed_refresh.fixed_refresh_active) { 1135 set_freesync_on_streams(core_freesync, &stream, 1); 1136 1137 /* Fixed Refresh set to "active" so engage (fix to max) */ 1138 } else { 1139 1140 calc_vmin_vmax(core_freesync, stream, &vmin, &vmax); 1141 1142 vmax = vmin; 1143 1144 core_freesync->dc->stream_funcs.adjust_vmin_vmax( 1145 core_freesync->dc, &stream, 1146 1, vmin, 1147 vmax); 1148 } 1149 } 1150 1151 void mod_freesync_pre_update_plane_addresses(struct mod_freesync *mod_freesync, 1152 const struct dc_stream **streams, int num_streams, 1153 unsigned int curr_time_stamp_in_us) 1154 { 1155 unsigned int stream_index, map_index, last_render_time_in_us = 0; 1156 struct core_freesync *core_freesync = NULL; 1157 1158 if (mod_freesync == NULL) 1159 return; 1160 1161 core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); 1162 1163 for (stream_index = 0; stream_index < num_streams; stream_index++) { 1164 1165 map_index = map_index_from_stream(core_freesync, 1166 streams[stream_index]); 1167 1168 if (core_freesync->map[map_index].caps->supported) { 1169 1170 last_render_time_in_us = curr_time_stamp_in_us - 1171 core_freesync->map[map_index].state.time. 1172 prev_time_stamp_in_us; 1173 1174 /* Add the timestamps to the cache and determine 1175 * whether BTR program is required 1176 */ 1177 update_timestamps(core_freesync, streams[stream_index], 1178 map_index, last_render_time_in_us); 1179 1180 if (core_freesync->map[map_index].state.fullscreen && 1181 core_freesync->map[map_index].user_enable. 1182 enable_for_gaming) { 1183 1184 if (core_freesync->map[map_index].caps->btr_supported) { 1185 1186 apply_below_the_range(core_freesync, 1187 streams[stream_index], map_index, 1188 last_render_time_in_us); 1189 } else { 1190 apply_fixed_refresh(core_freesync, 1191 streams[stream_index], map_index); 1192 } 1193 } 1194 1195 core_freesync->map[map_index].state.time. 1196 prev_time_stamp_in_us = curr_time_stamp_in_us; 1197 } 1198 1199 } 1200 } 1201