linux/drivers/gpu/drm/amd/display/modules/freesync/freesync.c
<<
>>
Prefs
   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 <linux/slab.h>
  27
  28#include "dm_services.h"
  29#include "dc.h"
  30#include "mod_freesync.h"
  31#include "core_types.h"
  32
  33#define MOD_FREESYNC_MAX_CONCURRENT_STREAMS  32
  34
  35#define MIN_REFRESH_RANGE_IN_US 10000000
  36/* Refresh rate ramp at a fixed rate of 65 Hz/second */
  37#define STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME ((1000 / 60) * 65)
  38/* Number of elements in the render times cache array */
  39#define RENDER_TIMES_MAX_COUNT 10
  40/* Threshold to exit BTR (to avoid frequent enter-exits at the lower limit) */
  41#define BTR_EXIT_MARGIN 2000
  42/* Threshold to change BTR multiplier (to avoid frequent changes) */
  43#define BTR_DRIFT_MARGIN 2000
  44/*Threshold to exit fixed refresh rate*/
  45#define FIXED_REFRESH_EXIT_MARGIN_IN_HZ 4
  46/* Number of consecutive frames to check before entering/exiting fixed refresh*/
  47#define FIXED_REFRESH_ENTER_FRAME_COUNT 5
  48#define FIXED_REFRESH_EXIT_FRAME_COUNT 5
  49
  50struct core_freesync {
  51        struct mod_freesync public;
  52        struct dc *dc;
  53};
  54
  55void setFieldWithMask(unsigned char *dest, unsigned int mask, unsigned int value)
  56{
  57        unsigned int shift = 0;
  58
  59        if (!mask || !dest)
  60                return;
  61
  62        while (!((mask >> shift) & 1))
  63                shift++;
  64
  65        //reset
  66        *dest = *dest & ~mask;
  67        //set
  68        //dont let value span past mask
  69        value = value & (mask >> shift);
  70        //insert value
  71        *dest = *dest | (value << shift);
  72}
  73
  74// VTEM Byte Offset
  75#define VRR_VTEM_PB0            0
  76#define VRR_VTEM_PB1            1
  77#define VRR_VTEM_PB2            2
  78#define VRR_VTEM_PB3            3
  79#define VRR_VTEM_PB4            4
  80#define VRR_VTEM_PB5            5
  81#define VRR_VTEM_PB6            6
  82
  83#define VRR_VTEM_MD0            7
  84#define VRR_VTEM_MD1            8
  85#define VRR_VTEM_MD2            9
  86#define VRR_VTEM_MD3            10
  87
  88
  89// VTEM Byte Masks
  90//PB0
  91#define MASK__VRR_VTEM_PB0__RESERVED0  0x01
  92#define MASK__VRR_VTEM_PB0__SYNC       0x02
  93#define MASK__VRR_VTEM_PB0__VFR        0x04
  94#define MASK__VRR_VTEM_PB0__AFR        0x08
  95#define MASK__VRR_VTEM_PB0__DS_TYPE    0x30
  96        //0: Periodic pseudo-static EM Data Set
  97        //1: Periodic dynamic EM Data Set
  98        //2: Unique EM Data Set
  99        //3: Reserved
 100#define MASK__VRR_VTEM_PB0__END        0x40
 101#define MASK__VRR_VTEM_PB0__NEW        0x80
 102
 103//PB1
 104#define MASK__VRR_VTEM_PB1__RESERVED1 0xFF
 105
 106//PB2
 107#define MASK__VRR_VTEM_PB2__ORGANIZATION_ID 0xFF
 108        //0: This is a Vendor Specific EM Data Set
 109        //1: This EM Data Set is defined by This Specification (HDMI 2.1 r102.clean)
 110        //2: This EM Data Set is defined by CTA-861-G
 111        //3: This EM Data Set is defined by VESA
 112//PB3
 113#define MASK__VRR_VTEM_PB3__DATA_SET_TAG_MSB    0xFF
 114//PB4
 115#define MASK__VRR_VTEM_PB4__DATA_SET_TAG_LSB    0xFF
 116//PB5
 117#define MASK__VRR_VTEM_PB5__DATA_SET_LENGTH_MSB 0xFF
 118//PB6
 119#define MASK__VRR_VTEM_PB6__DATA_SET_LENGTH_LSB 0xFF
 120
 121
 122
 123//PB7-27 (20 bytes):
 124//PB7 = MD0
 125#define MASK__VRR_VTEM_MD0__VRR_EN         0x01
 126#define MASK__VRR_VTEM_MD0__M_CONST        0x02
 127#define MASK__VRR_VTEM_MD0__RESERVED2      0x0C
 128#define MASK__VRR_VTEM_MD0__FVA_FACTOR_M1  0xF0
 129
 130//MD1
 131#define MASK__VRR_VTEM_MD1__BASE_VFRONT    0xFF
 132
 133//MD2
 134#define MASK__VRR_VTEM_MD2__BASE_REFRESH_RATE_98  0x03
 135#define MASK__VRR_VTEM_MD2__RB                    0x04
 136#define MASK__VRR_VTEM_MD2__RESERVED3             0xF8
 137
 138//MD3
 139#define MASK__VRR_VTEM_MD3__BASE_REFRESH_RATE_07  0xFF
 140
 141
 142#define MOD_FREESYNC_TO_CORE(mod_freesync)\
 143                container_of(mod_freesync, struct core_freesync, public)
 144
 145struct mod_freesync *mod_freesync_create(struct dc *dc)
 146{
 147        struct core_freesync *core_freesync =
 148                        kzalloc(sizeof(struct core_freesync), GFP_KERNEL);
 149
 150        if (core_freesync == NULL)
 151                goto fail_alloc_context;
 152
 153        if (dc == NULL)
 154                goto fail_construct;
 155
 156        core_freesync->dc = dc;
 157        return &core_freesync->public;
 158
 159fail_construct:
 160        kfree(core_freesync);
 161
 162fail_alloc_context:
 163        return NULL;
 164}
 165
 166void mod_freesync_destroy(struct mod_freesync *mod_freesync)
 167{
 168        struct core_freesync *core_freesync = NULL;
 169        if (mod_freesync == NULL)
 170                return;
 171        core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
 172        kfree(core_freesync);
 173}
 174
 175#if 0 /* unused currently */
 176static unsigned int calc_refresh_in_uhz_from_duration(
 177                unsigned int duration_in_ns)
 178{
 179        unsigned int refresh_in_uhz =
 180                        ((unsigned int)(div64_u64((1000000000ULL * 1000000),
 181                                        duration_in_ns)));
 182        return refresh_in_uhz;
 183}
 184#endif
 185
 186static unsigned int calc_duration_in_us_from_refresh_in_uhz(
 187                unsigned int refresh_in_uhz)
 188{
 189        unsigned int duration_in_us =
 190                        ((unsigned int)(div64_u64((1000000000ULL * 1000),
 191                                        refresh_in_uhz)));
 192        return duration_in_us;
 193}
 194
 195static unsigned int calc_duration_in_us_from_v_total(
 196                const struct dc_stream_state *stream,
 197                const struct mod_vrr_params *in_vrr,
 198                unsigned int v_total)
 199{
 200        unsigned int duration_in_us =
 201                        (unsigned int)(div64_u64(((unsigned long long)(v_total)
 202                                * 10000) * stream->timing.h_total,
 203                                        stream->timing.pix_clk_100hz));
 204
 205        return duration_in_us;
 206}
 207
 208static unsigned int calc_v_total_from_refresh(
 209                const struct dc_stream_state *stream,
 210                unsigned int refresh_in_uhz)
 211{
 212        unsigned int v_total = stream->timing.v_total;
 213        unsigned int frame_duration_in_ns;
 214
 215        frame_duration_in_ns =
 216                        ((unsigned int)(div64_u64((1000000000ULL * 1000000),
 217                                        refresh_in_uhz)));
 218
 219        v_total = div64_u64(div64_u64(((unsigned long long)(
 220                        frame_duration_in_ns) * (stream->timing.pix_clk_100hz / 10)),
 221                        stream->timing.h_total), 1000000);
 222
 223        /* v_total cannot be less than nominal */
 224        if (v_total < stream->timing.v_total) {
 225                ASSERT(v_total < stream->timing.v_total);
 226                v_total = stream->timing.v_total;
 227        }
 228
 229        return v_total;
 230}
 231
 232static unsigned int calc_v_total_from_duration(
 233                const struct dc_stream_state *stream,
 234                const struct mod_vrr_params *vrr,
 235                unsigned int duration_in_us)
 236{
 237        unsigned int v_total = 0;
 238
 239        if (duration_in_us < vrr->min_duration_in_us)
 240                duration_in_us = vrr->min_duration_in_us;
 241
 242        if (duration_in_us > vrr->max_duration_in_us)
 243                duration_in_us = vrr->max_duration_in_us;
 244
 245        v_total = div64_u64(div64_u64(((unsigned long long)(
 246                                duration_in_us) * (stream->timing.pix_clk_100hz / 10)),
 247                                stream->timing.h_total), 1000);
 248
 249        /* v_total cannot be less than nominal */
 250        if (v_total < stream->timing.v_total) {
 251                ASSERT(v_total < stream->timing.v_total);
 252                v_total = stream->timing.v_total;
 253        }
 254
 255        return v_total;
 256}
 257
 258static void update_v_total_for_static_ramp(
 259                struct core_freesync *core_freesync,
 260                const struct dc_stream_state *stream,
 261                struct mod_vrr_params *in_out_vrr)
 262{
 263        unsigned int v_total = 0;
 264        unsigned int current_duration_in_us =
 265                        calc_duration_in_us_from_v_total(
 266                                stream, in_out_vrr,
 267                                in_out_vrr->adjust.v_total_max);
 268        unsigned int target_duration_in_us =
 269                        calc_duration_in_us_from_refresh_in_uhz(
 270                                in_out_vrr->fixed.target_refresh_in_uhz);
 271        bool ramp_direction_is_up = (current_duration_in_us >
 272                                target_duration_in_us) ? true : false;
 273
 274        /* Calc ratio between new and current frame duration with 3 digit */
 275        unsigned int frame_duration_ratio = div64_u64(1000000,
 276                (1000 +  div64_u64(((unsigned long long)(
 277                STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME) *
 278                current_duration_in_us),
 279                1000000)));
 280
 281        /* Calculate delta between new and current frame duration in us */
 282        unsigned int frame_duration_delta = div64_u64(((unsigned long long)(
 283                current_duration_in_us) *
 284                (1000 - frame_duration_ratio)), 1000);
 285
 286        /* Adjust frame duration delta based on ratio between current and
 287         * standard frame duration (frame duration at 60 Hz refresh rate).
 288         */
 289        unsigned int ramp_rate_interpolated = div64_u64(((unsigned long long)(
 290                frame_duration_delta) * current_duration_in_us), 16666);
 291
 292        /* Going to a higher refresh rate (lower frame duration) */
 293        if (ramp_direction_is_up) {
 294                /* reduce frame duration */
 295                current_duration_in_us -= ramp_rate_interpolated;
 296
 297                /* adjust for frame duration below min */
 298                if (current_duration_in_us <= target_duration_in_us) {
 299                        in_out_vrr->fixed.ramping_active = false;
 300                        in_out_vrr->fixed.ramping_done = true;
 301                        current_duration_in_us =
 302                                calc_duration_in_us_from_refresh_in_uhz(
 303                                in_out_vrr->fixed.target_refresh_in_uhz);
 304                }
 305        /* Going to a lower refresh rate (larger frame duration) */
 306        } else {
 307                /* increase frame duration */
 308                current_duration_in_us += ramp_rate_interpolated;
 309
 310                /* adjust for frame duration above max */
 311                if (current_duration_in_us >= target_duration_in_us) {
 312                        in_out_vrr->fixed.ramping_active = false;
 313                        in_out_vrr->fixed.ramping_done = true;
 314                        current_duration_in_us =
 315                                calc_duration_in_us_from_refresh_in_uhz(
 316                                in_out_vrr->fixed.target_refresh_in_uhz);
 317                }
 318        }
 319
 320        v_total = div64_u64(div64_u64(((unsigned long long)(
 321                        current_duration_in_us) * (stream->timing.pix_clk_100hz / 10)),
 322                                stream->timing.h_total), 1000);
 323
 324        in_out_vrr->adjust.v_total_min = v_total;
 325        in_out_vrr->adjust.v_total_max = v_total;
 326}
 327
 328static void apply_below_the_range(struct core_freesync *core_freesync,
 329                const struct dc_stream_state *stream,
 330                unsigned int last_render_time_in_us,
 331                struct mod_vrr_params *in_out_vrr)
 332{
 333        unsigned int inserted_frame_duration_in_us = 0;
 334        unsigned int mid_point_frames_ceil = 0;
 335        unsigned int mid_point_frames_floor = 0;
 336        unsigned int frame_time_in_us = 0;
 337        unsigned int delta_from_mid_point_in_us_1 = 0xFFFFFFFF;
 338        unsigned int delta_from_mid_point_in_us_2 = 0xFFFFFFFF;
 339        unsigned int frames_to_insert = 0;
 340        unsigned int min_frame_duration_in_ns = 0;
 341        unsigned int max_render_time_in_us = in_out_vrr->max_duration_in_us;
 342        unsigned int delta_from_mid_point_delta_in_us;
 343
 344        min_frame_duration_in_ns = ((unsigned int) (div64_u64(
 345                (1000000000ULL * 1000000),
 346                in_out_vrr->max_refresh_in_uhz)));
 347
 348        /* Program BTR */
 349        if (last_render_time_in_us + BTR_EXIT_MARGIN < max_render_time_in_us) {
 350                /* Exit Below the Range */
 351                if (in_out_vrr->btr.btr_active) {
 352                        in_out_vrr->btr.frame_counter = 0;
 353                        in_out_vrr->btr.btr_active = false;
 354                }
 355        } else if (last_render_time_in_us > max_render_time_in_us) {
 356                /* Enter Below the Range */
 357                in_out_vrr->btr.btr_active = true;
 358        }
 359
 360        /* BTR set to "not active" so disengage */
 361        if (!in_out_vrr->btr.btr_active) {
 362                in_out_vrr->btr.inserted_duration_in_us = 0;
 363                in_out_vrr->btr.frames_to_insert = 0;
 364                in_out_vrr->btr.frame_counter = 0;
 365
 366                /* Restore FreeSync */
 367                in_out_vrr->adjust.v_total_min =
 368                        calc_v_total_from_refresh(stream,
 369                                in_out_vrr->max_refresh_in_uhz);
 370                in_out_vrr->adjust.v_total_max =
 371                        calc_v_total_from_refresh(stream,
 372                                in_out_vrr->min_refresh_in_uhz);
 373        /* BTR set to "active" so engage */
 374        } else {
 375
 376                /* Calculate number of midPoint frames that could fit within
 377                 * the render time interval- take ceil of this value
 378                 */
 379                mid_point_frames_ceil = (last_render_time_in_us +
 380                                in_out_vrr->btr.mid_point_in_us - 1) /
 381                                        in_out_vrr->btr.mid_point_in_us;
 382
 383                if (mid_point_frames_ceil > 0) {
 384                        frame_time_in_us = last_render_time_in_us /
 385                                mid_point_frames_ceil;
 386                        delta_from_mid_point_in_us_1 =
 387                                (in_out_vrr->btr.mid_point_in_us >
 388                                frame_time_in_us) ?
 389                                (in_out_vrr->btr.mid_point_in_us - frame_time_in_us) :
 390                                (frame_time_in_us - in_out_vrr->btr.mid_point_in_us);
 391                }
 392
 393                /* Calculate number of midPoint frames that could fit within
 394                 * the render time interval- take floor of this value
 395                 */
 396                mid_point_frames_floor = last_render_time_in_us /
 397                                in_out_vrr->btr.mid_point_in_us;
 398
 399                if (mid_point_frames_floor > 0) {
 400
 401                        frame_time_in_us = last_render_time_in_us /
 402                                mid_point_frames_floor;
 403                        delta_from_mid_point_in_us_2 =
 404                                (in_out_vrr->btr.mid_point_in_us >
 405                                frame_time_in_us) ?
 406                                (in_out_vrr->btr.mid_point_in_us - frame_time_in_us) :
 407                                (frame_time_in_us - in_out_vrr->btr.mid_point_in_us);
 408                }
 409
 410                /* Choose number of frames to insert based on how close it
 411                 * can get to the mid point of the variable range.
 412                 */
 413                if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) {
 414                        frames_to_insert = mid_point_frames_ceil;
 415                        delta_from_mid_point_delta_in_us = delta_from_mid_point_in_us_2 -
 416                                        delta_from_mid_point_in_us_1;
 417                } else {
 418                        frames_to_insert = mid_point_frames_floor;
 419                        delta_from_mid_point_delta_in_us = delta_from_mid_point_in_us_1 -
 420                                        delta_from_mid_point_in_us_2;
 421                }
 422
 423                /* Prefer current frame multiplier when BTR is enabled unless it drifts
 424                 * too far from the midpoint
 425                 */
 426                if (in_out_vrr->btr.frames_to_insert != 0 &&
 427                                delta_from_mid_point_delta_in_us < BTR_DRIFT_MARGIN) {
 428                        if (((last_render_time_in_us / in_out_vrr->btr.frames_to_insert) <
 429                                        in_out_vrr->max_duration_in_us) &&
 430                                ((last_render_time_in_us / in_out_vrr->btr.frames_to_insert) >
 431                                        in_out_vrr->min_duration_in_us))
 432                                frames_to_insert = in_out_vrr->btr.frames_to_insert;
 433                }
 434
 435                /* Either we've calculated the number of frames to insert,
 436                 * or we need to insert min duration frames
 437                 */
 438                if (frames_to_insert > 0)
 439                        inserted_frame_duration_in_us = last_render_time_in_us /
 440                                                        frames_to_insert;
 441
 442                if (inserted_frame_duration_in_us < in_out_vrr->min_duration_in_us)
 443                        inserted_frame_duration_in_us = in_out_vrr->min_duration_in_us;
 444
 445                /* Cache the calculated variables */
 446                in_out_vrr->btr.inserted_duration_in_us =
 447                        inserted_frame_duration_in_us;
 448                in_out_vrr->btr.frames_to_insert = frames_to_insert;
 449                in_out_vrr->btr.frame_counter = frames_to_insert;
 450        }
 451}
 452
 453static void apply_fixed_refresh(struct core_freesync *core_freesync,
 454                const struct dc_stream_state *stream,
 455                unsigned int last_render_time_in_us,
 456                struct mod_vrr_params *in_out_vrr)
 457{
 458        bool update = false;
 459        unsigned int max_render_time_in_us = in_out_vrr->max_duration_in_us;
 460
 461        //Compute the exit refresh rate and exit frame duration
 462        unsigned int exit_refresh_rate_in_milli_hz = ((1000000000/max_render_time_in_us)
 463                        + (1000*FIXED_REFRESH_EXIT_MARGIN_IN_HZ));
 464        unsigned int exit_frame_duration_in_us = 1000000000/exit_refresh_rate_in_milli_hz;
 465
 466        if (last_render_time_in_us < exit_frame_duration_in_us) {
 467                /* Exit Fixed Refresh mode */
 468                if (in_out_vrr->fixed.fixed_active) {
 469                        in_out_vrr->fixed.frame_counter++;
 470
 471                        if (in_out_vrr->fixed.frame_counter >
 472                                        FIXED_REFRESH_EXIT_FRAME_COUNT) {
 473                                in_out_vrr->fixed.frame_counter = 0;
 474                                in_out_vrr->fixed.fixed_active = false;
 475                                in_out_vrr->fixed.target_refresh_in_uhz = 0;
 476                                update = true;
 477                        }
 478                }
 479        } else if (last_render_time_in_us > max_render_time_in_us) {
 480                /* Enter Fixed Refresh mode */
 481                if (!in_out_vrr->fixed.fixed_active) {
 482                        in_out_vrr->fixed.frame_counter++;
 483
 484                        if (in_out_vrr->fixed.frame_counter >
 485                                        FIXED_REFRESH_ENTER_FRAME_COUNT) {
 486                                in_out_vrr->fixed.frame_counter = 0;
 487                                in_out_vrr->fixed.fixed_active = true;
 488                                in_out_vrr->fixed.target_refresh_in_uhz =
 489                                                in_out_vrr->max_refresh_in_uhz;
 490                                update = true;
 491                        }
 492                }
 493        }
 494
 495        if (update) {
 496                if (in_out_vrr->fixed.fixed_active) {
 497                        in_out_vrr->adjust.v_total_min =
 498                                calc_v_total_from_refresh(
 499                                stream, in_out_vrr->max_refresh_in_uhz);
 500                        in_out_vrr->adjust.v_total_max =
 501                                        in_out_vrr->adjust.v_total_min;
 502                } else {
 503                        in_out_vrr->adjust.v_total_min =
 504                                calc_v_total_from_refresh(stream,
 505                                        in_out_vrr->max_refresh_in_uhz);
 506                        in_out_vrr->adjust.v_total_max =
 507                                calc_v_total_from_refresh(stream,
 508                                        in_out_vrr->min_refresh_in_uhz);
 509                }
 510        }
 511}
 512
 513static bool vrr_settings_require_update(struct core_freesync *core_freesync,
 514                struct mod_freesync_config *in_config,
 515                unsigned int min_refresh_in_uhz,
 516                unsigned int max_refresh_in_uhz,
 517                struct mod_vrr_params *in_vrr)
 518{
 519        if (in_vrr->state != in_config->state) {
 520                return true;
 521        } else if (in_vrr->state == VRR_STATE_ACTIVE_FIXED &&
 522                        in_vrr->fixed.target_refresh_in_uhz !=
 523                                        in_config->min_refresh_in_uhz) {
 524                return true;
 525        } else if (in_vrr->min_refresh_in_uhz != min_refresh_in_uhz) {
 526                return true;
 527        } else if (in_vrr->max_refresh_in_uhz != max_refresh_in_uhz) {
 528                return true;
 529        }
 530
 531        return false;
 532}
 533
 534bool mod_freesync_get_vmin_vmax(struct mod_freesync *mod_freesync,
 535                const struct dc_stream_state *stream,
 536                unsigned int *vmin,
 537                unsigned int *vmax)
 538{
 539        *vmin = stream->adjust.v_total_min;
 540        *vmax = stream->adjust.v_total_max;
 541
 542        return true;
 543}
 544
 545bool mod_freesync_get_v_position(struct mod_freesync *mod_freesync,
 546                struct dc_stream_state *stream,
 547                unsigned int *nom_v_pos,
 548                unsigned int *v_pos)
 549{
 550        struct core_freesync *core_freesync = NULL;
 551        struct crtc_position position;
 552
 553        if (mod_freesync == NULL)
 554                return false;
 555
 556        core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
 557
 558        if (dc_stream_get_crtc_position(core_freesync->dc, &stream, 1,
 559                                        &position.vertical_count,
 560                                        &position.nominal_vcount)) {
 561
 562                *nom_v_pos = position.nominal_vcount;
 563                *v_pos = position.vertical_count;
 564
 565                return true;
 566        }
 567
 568        return false;
 569}
 570
 571static void build_vrr_infopacket_header_vtem(enum signal_type signal,
 572                struct dc_info_packet *infopacket)
 573{
 574        // HEADER
 575
 576        // HB0, HB1, HB2 indicates PacketType VTEMPacket
 577        infopacket->hb0 = 0x7F;
 578        infopacket->hb1 = 0xC0;
 579        infopacket->hb2 = 0x00; //sequence_index
 580
 581        setFieldWithMask(&infopacket->sb[VRR_VTEM_PB0], MASK__VRR_VTEM_PB0__VFR, 1);
 582        setFieldWithMask(&infopacket->sb[VRR_VTEM_PB2], MASK__VRR_VTEM_PB2__ORGANIZATION_ID, 1);
 583        setFieldWithMask(&infopacket->sb[VRR_VTEM_PB3], MASK__VRR_VTEM_PB3__DATA_SET_TAG_MSB, 0);
 584        setFieldWithMask(&infopacket->sb[VRR_VTEM_PB4], MASK__VRR_VTEM_PB4__DATA_SET_TAG_LSB, 1);
 585        setFieldWithMask(&infopacket->sb[VRR_VTEM_PB5], MASK__VRR_VTEM_PB5__DATA_SET_LENGTH_MSB, 0);
 586        setFieldWithMask(&infopacket->sb[VRR_VTEM_PB6], MASK__VRR_VTEM_PB6__DATA_SET_LENGTH_LSB, 4);
 587}
 588
 589static void build_vrr_infopacket_header_v1(enum signal_type signal,
 590                struct dc_info_packet *infopacket,
 591                unsigned int *payload_size)
 592{
 593        if (dc_is_hdmi_signal(signal)) {
 594
 595                /* HEADER */
 596
 597                /* HB0  = Packet Type = 0x83 (Source Product
 598                 *        Descriptor InfoFrame)
 599                 */
 600                infopacket->hb0 = DC_HDMI_INFOFRAME_TYPE_SPD;
 601
 602                /* HB1  = Version = 0x01 */
 603                infopacket->hb1 = 0x01;
 604
 605                /* HB2  = [Bits 7:5 = 0] [Bits 4:0 = Length = 0x08] */
 606                infopacket->hb2 = 0x08;
 607
 608                *payload_size = 0x08;
 609
 610        } else if (dc_is_dp_signal(signal)) {
 611
 612                /* HEADER */
 613
 614                /* HB0  = Secondary-data Packet ID = 0 - Only non-zero
 615                 *        when used to associate audio related info packets
 616                 */
 617                infopacket->hb0 = 0x00;
 618
 619                /* HB1  = Packet Type = 0x83 (Source Product
 620                 *        Descriptor InfoFrame)
 621                 */
 622                infopacket->hb1 = DC_HDMI_INFOFRAME_TYPE_SPD;
 623
 624                /* HB2  = [Bits 7:0 = Least significant eight bits -
 625                 *        For INFOFRAME, the value must be 1Bh]
 626                 */
 627                infopacket->hb2 = 0x1B;
 628
 629                /* HB3  = [Bits 7:2 = INFOFRAME SDP Version Number = 0x1]
 630                 *        [Bits 1:0 = Most significant two bits = 0x00]
 631                 */
 632                infopacket->hb3 = 0x04;
 633
 634                *payload_size = 0x1B;
 635        }
 636}
 637
 638static void build_vrr_infopacket_header_v2(enum signal_type signal,
 639                struct dc_info_packet *infopacket,
 640                unsigned int *payload_size)
 641{
 642        if (dc_is_hdmi_signal(signal)) {
 643
 644                /* HEADER */
 645
 646                /* HB0  = Packet Type = 0x83 (Source Product
 647                 *        Descriptor InfoFrame)
 648                 */
 649                infopacket->hb0 = DC_HDMI_INFOFRAME_TYPE_SPD;
 650
 651                /* HB1  = Version = 0x02 */
 652                infopacket->hb1 = 0x02;
 653
 654                /* HB2  = [Bits 7:5 = 0] [Bits 4:0 = Length = 0x09] */
 655                infopacket->hb2 = 0x09;
 656
 657                *payload_size = 0x0A;
 658
 659        } else if (dc_is_dp_signal(signal)) {
 660
 661                /* HEADER */
 662
 663                /* HB0  = Secondary-data Packet ID = 0 - Only non-zero
 664                 *        when used to associate audio related info packets
 665                 */
 666                infopacket->hb0 = 0x00;
 667
 668                /* HB1  = Packet Type = 0x83 (Source Product
 669                 *        Descriptor InfoFrame)
 670                 */
 671                infopacket->hb1 = DC_HDMI_INFOFRAME_TYPE_SPD;
 672
 673                /* HB2  = [Bits 7:0 = Least significant eight bits -
 674                 *        For INFOFRAME, the value must be 1Bh]
 675                 */
 676                infopacket->hb2 = 0x1B;
 677
 678                /* HB3  = [Bits 7:2 = INFOFRAME SDP Version Number = 0x2]
 679                 *        [Bits 1:0 = Most significant two bits = 0x00]
 680                 */
 681                infopacket->hb3 = 0x08;
 682
 683                *payload_size = 0x1B;
 684        }
 685}
 686
 687static void build_vrr_vtem_infopacket_data(const struct dc_stream_state *stream,
 688                const struct mod_vrr_params *vrr,
 689                struct dc_info_packet *infopacket)
 690{
 691        unsigned int fieldRateInHz;
 692
 693        if (vrr->state == VRR_STATE_ACTIVE_VARIABLE ||
 694                                vrr->state == VRR_STATE_ACTIVE_FIXED) {
 695                setFieldWithMask(&infopacket->sb[VRR_VTEM_MD0], MASK__VRR_VTEM_MD0__VRR_EN, 1);
 696        } else {
 697                setFieldWithMask(&infopacket->sb[VRR_VTEM_MD0], MASK__VRR_VTEM_MD0__VRR_EN, 0);
 698        }
 699
 700        if (!stream->timing.vic) {
 701                setFieldWithMask(&infopacket->sb[VRR_VTEM_MD1], MASK__VRR_VTEM_MD1__BASE_VFRONT,
 702                                stream->timing.v_front_porch);
 703
 704
 705                /* TODO: In dal2, we check mode flags for a reduced blanking timing.
 706                 * Need a way to relay that information to this function.
 707                 * if("ReducedBlanking")
 708                 * {
 709                 *   setFieldWithMask(&infopacket->sb[VRR_VTEM_MD2], MASK__VRR_VTEM_MD2__RB, 1;
 710                 * }
 711                 */
 712
 713                //TODO: DAL2 does FixPoint and rounding. Here we might need to account for that
 714                fieldRateInHz = (stream->timing.pix_clk_100hz * 100)/
 715                        (stream->timing.h_total * stream->timing.v_total);
 716
 717                setFieldWithMask(&infopacket->sb[VRR_VTEM_MD2],  MASK__VRR_VTEM_MD2__BASE_REFRESH_RATE_98,
 718                                fieldRateInHz >> 8);
 719                setFieldWithMask(&infopacket->sb[VRR_VTEM_MD3], MASK__VRR_VTEM_MD3__BASE_REFRESH_RATE_07,
 720                                fieldRateInHz);
 721
 722        }
 723        infopacket->valid = true;
 724}
 725
 726static void build_vrr_infopacket_data(const struct mod_vrr_params *vrr,
 727                struct dc_info_packet *infopacket)
 728{
 729        /* PB1 = 0x1A (24bit AMD IEEE OUI (0x00001A) - Byte 0) */
 730        infopacket->sb[1] = 0x1A;
 731
 732        /* PB2 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 1) */
 733        infopacket->sb[2] = 0x00;
 734
 735        /* PB3 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 2) */
 736        infopacket->sb[3] = 0x00;
 737
 738        /* PB4 = Reserved */
 739
 740        /* PB5 = Reserved */
 741
 742        /* PB6 = [Bits 7:3 = Reserved] */
 743
 744        /* PB6 = [Bit 0 = FreeSync Supported] */
 745        if (vrr->state != VRR_STATE_UNSUPPORTED)
 746                infopacket->sb[6] |= 0x01;
 747
 748        /* PB6 = [Bit 1 = FreeSync Enabled] */
 749        if (vrr->state != VRR_STATE_DISABLED &&
 750                        vrr->state != VRR_STATE_UNSUPPORTED)
 751                infopacket->sb[6] |= 0x02;
 752
 753        /* PB6 = [Bit 2 = FreeSync Active] */
 754        if (vrr->state == VRR_STATE_ACTIVE_VARIABLE ||
 755                        vrr->state == VRR_STATE_ACTIVE_FIXED)
 756                infopacket->sb[6] |= 0x04;
 757
 758        /* PB7 = FreeSync Minimum refresh rate (Hz) */
 759        infopacket->sb[7] = (unsigned char)(vrr->min_refresh_in_uhz / 1000000);
 760
 761        /* PB8 = FreeSync Maximum refresh rate (Hz)
 762         * Note: We should never go above the field rate of the mode timing set.
 763         */
 764        infopacket->sb[8] = (unsigned char)(vrr->max_refresh_in_uhz / 1000000);
 765
 766
 767        //FreeSync HDR
 768        infopacket->sb[9] = 0;
 769        infopacket->sb[10] = 0;
 770}
 771
 772static void build_vrr_infopacket_fs2_data(enum color_transfer_func app_tf,
 773                struct dc_info_packet *infopacket)
 774{
 775        if (app_tf != TRANSFER_FUNC_UNKNOWN) {
 776                infopacket->valid = true;
 777
 778                infopacket->sb[6] |= 0x08;  // PB6 = [Bit 3 = Native Color Active]
 779
 780                if (app_tf == TRANSFER_FUNC_GAMMA_22) {
 781                        infopacket->sb[9] |= 0x04;  // PB6 = [Bit 2 = Gamma 2.2 EOTF Active]
 782                }
 783        }
 784}
 785
 786static void build_vrr_infopacket_checksum(unsigned int *payload_size,
 787                struct dc_info_packet *infopacket)
 788{
 789        /* Calculate checksum */
 790        unsigned int idx = 0;
 791        unsigned char checksum = 0;
 792
 793        checksum += infopacket->hb0;
 794        checksum += infopacket->hb1;
 795        checksum += infopacket->hb2;
 796        checksum += infopacket->hb3;
 797
 798        for (idx = 1; idx <= *payload_size; idx++)
 799                checksum += infopacket->sb[idx];
 800
 801        /* PB0 = Checksum (one byte complement) */
 802        infopacket->sb[0] = (unsigned char)(0x100 - checksum);
 803
 804        infopacket->valid = true;
 805}
 806
 807static void build_vrr_infopacket_v1(enum signal_type signal,
 808                const struct mod_vrr_params *vrr,
 809                struct dc_info_packet *infopacket)
 810{
 811        /* SPD info packet for FreeSync */
 812        unsigned int payload_size = 0;
 813
 814        build_vrr_infopacket_header_v1(signal, infopacket, &payload_size);
 815        build_vrr_infopacket_data(vrr, infopacket);
 816        build_vrr_infopacket_checksum(&payload_size, infopacket);
 817
 818        infopacket->valid = true;
 819}
 820
 821static void build_vrr_infopacket_v2(enum signal_type signal,
 822                const struct mod_vrr_params *vrr,
 823                enum color_transfer_func app_tf,
 824                struct dc_info_packet *infopacket)
 825{
 826        unsigned int payload_size = 0;
 827
 828        build_vrr_infopacket_header_v2(signal, infopacket, &payload_size);
 829        build_vrr_infopacket_data(vrr, infopacket);
 830
 831        build_vrr_infopacket_fs2_data(app_tf, infopacket);
 832
 833        build_vrr_infopacket_checksum(&payload_size, infopacket);
 834
 835        infopacket->valid = true;
 836}
 837
 838static void build_vrr_infopacket_vtem(const struct dc_stream_state *stream,
 839                const struct mod_vrr_params *vrr,
 840                struct dc_info_packet *infopacket)
 841{
 842        //VTEM info packet for HdmiVrr
 843
 844        memset(infopacket, 0, sizeof(struct dc_info_packet));
 845
 846        //VTEM Packet is structured differently
 847        build_vrr_infopacket_header_vtem(stream->signal, infopacket);
 848        build_vrr_vtem_infopacket_data(stream, vrr, infopacket);
 849
 850        infopacket->valid = true;
 851}
 852
 853void mod_freesync_build_vrr_infopacket(struct mod_freesync *mod_freesync,
 854                const struct dc_stream_state *stream,
 855                const struct mod_vrr_params *vrr,
 856                enum vrr_packet_type packet_type,
 857                enum color_transfer_func app_tf,
 858                struct dc_info_packet *infopacket)
 859{
 860        /* SPD info packet for FreeSync
 861         * VTEM info packet for HdmiVRR
 862         * Check if Freesync is supported. Return if false. If true,
 863         * set the corresponding bit in the info packet
 864         */
 865        if (!vrr->supported || (!vrr->send_info_frame && packet_type != PACKET_TYPE_VTEM))
 866                return;
 867
 868        switch (packet_type) {
 869        case PACKET_TYPE_FS2:
 870                build_vrr_infopacket_v2(stream->signal, vrr, app_tf, infopacket);
 871                break;
 872        case PACKET_TYPE_VTEM:
 873                build_vrr_infopacket_vtem(stream, vrr, infopacket);
 874                break;
 875        case PACKET_TYPE_VRR:
 876        case PACKET_TYPE_FS1:
 877        default:
 878                build_vrr_infopacket_v1(stream->signal, vrr, infopacket);
 879        }
 880}
 881
 882void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync,
 883                const struct dc_stream_state *stream,
 884                struct mod_freesync_config *in_config,
 885                struct mod_vrr_params *in_out_vrr)
 886{
 887        struct core_freesync *core_freesync = NULL;
 888        unsigned long long nominal_field_rate_in_uhz = 0;
 889        unsigned int refresh_range = 0;
 890        unsigned int min_refresh_in_uhz = 0;
 891        unsigned int max_refresh_in_uhz = 0;
 892
 893        if (mod_freesync == NULL)
 894                return;
 895
 896        core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
 897
 898        /* Calculate nominal field rate for stream */
 899        nominal_field_rate_in_uhz =
 900                        mod_freesync_calc_nominal_field_rate(stream);
 901
 902        min_refresh_in_uhz = in_config->min_refresh_in_uhz;
 903        max_refresh_in_uhz = in_config->max_refresh_in_uhz;
 904
 905        // Don't allow min > max
 906        if (min_refresh_in_uhz > max_refresh_in_uhz)
 907                min_refresh_in_uhz = max_refresh_in_uhz;
 908
 909        // Full range may be larger than current video timing, so cap at nominal
 910        if (max_refresh_in_uhz > nominal_field_rate_in_uhz)
 911                max_refresh_in_uhz = nominal_field_rate_in_uhz;
 912
 913        // Full range may be larger than current video timing, so cap at nominal
 914        if (min_refresh_in_uhz > nominal_field_rate_in_uhz)
 915                min_refresh_in_uhz = nominal_field_rate_in_uhz;
 916
 917        if (!vrr_settings_require_update(core_freesync,
 918                        in_config, min_refresh_in_uhz, max_refresh_in_uhz,
 919                        in_out_vrr))
 920                return;
 921
 922        in_out_vrr->state = in_config->state;
 923        in_out_vrr->send_info_frame = in_config->vsif_supported;
 924
 925        if (in_config->state == VRR_STATE_UNSUPPORTED) {
 926                in_out_vrr->state = VRR_STATE_UNSUPPORTED;
 927                in_out_vrr->supported = false;
 928                in_out_vrr->adjust.v_total_min = stream->timing.v_total;
 929                in_out_vrr->adjust.v_total_max = stream->timing.v_total;
 930
 931                return;
 932
 933        } else {
 934                in_out_vrr->min_refresh_in_uhz = min_refresh_in_uhz;
 935                in_out_vrr->max_duration_in_us =
 936                                calc_duration_in_us_from_refresh_in_uhz(
 937                                                min_refresh_in_uhz);
 938
 939                in_out_vrr->max_refresh_in_uhz = max_refresh_in_uhz;
 940                in_out_vrr->min_duration_in_us =
 941                                calc_duration_in_us_from_refresh_in_uhz(
 942                                                max_refresh_in_uhz);
 943
 944                refresh_range = in_out_vrr->max_refresh_in_uhz -
 945                                in_out_vrr->min_refresh_in_uhz;
 946
 947                in_out_vrr->supported = true;
 948        }
 949
 950        in_out_vrr->fixed.ramping_active = in_config->ramping;
 951
 952        in_out_vrr->btr.btr_enabled = in_config->btr;
 953        if (in_out_vrr->max_refresh_in_uhz <
 954                        2 * in_out_vrr->min_refresh_in_uhz)
 955                in_out_vrr->btr.btr_enabled = false;
 956        in_out_vrr->btr.btr_active = false;
 957        in_out_vrr->btr.inserted_duration_in_us = 0;
 958        in_out_vrr->btr.frames_to_insert = 0;
 959        in_out_vrr->btr.frame_counter = 0;
 960        in_out_vrr->btr.mid_point_in_us =
 961                        in_out_vrr->min_duration_in_us +
 962                                (in_out_vrr->max_duration_in_us -
 963                                in_out_vrr->min_duration_in_us) / 2;
 964
 965        if (in_out_vrr->state == VRR_STATE_UNSUPPORTED) {
 966                in_out_vrr->adjust.v_total_min = stream->timing.v_total;
 967                in_out_vrr->adjust.v_total_max = stream->timing.v_total;
 968        } else if (in_out_vrr->state == VRR_STATE_DISABLED) {
 969                in_out_vrr->adjust.v_total_min = stream->timing.v_total;
 970                in_out_vrr->adjust.v_total_max = stream->timing.v_total;
 971        } else if (in_out_vrr->state == VRR_STATE_INACTIVE) {
 972                in_out_vrr->adjust.v_total_min = stream->timing.v_total;
 973                in_out_vrr->adjust.v_total_max = stream->timing.v_total;
 974        } else if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE &&
 975                        refresh_range >= MIN_REFRESH_RANGE_IN_US) {
 976                in_out_vrr->adjust.v_total_min =
 977                        calc_v_total_from_refresh(stream,
 978                                in_out_vrr->max_refresh_in_uhz);
 979                in_out_vrr->adjust.v_total_max =
 980                        calc_v_total_from_refresh(stream,
 981                                in_out_vrr->min_refresh_in_uhz);
 982        } else if (in_out_vrr->state == VRR_STATE_ACTIVE_FIXED) {
 983                in_out_vrr->fixed.target_refresh_in_uhz =
 984                                in_out_vrr->min_refresh_in_uhz;
 985                if (in_out_vrr->fixed.ramping_active &&
 986                                in_out_vrr->fixed.fixed_active) {
 987                        /* Do not update vtotals if ramping is already active
 988                         * in order to continue ramp from current refresh.
 989                         */
 990                        in_out_vrr->fixed.fixed_active = true;
 991                } else {
 992                        in_out_vrr->fixed.fixed_active = true;
 993                        in_out_vrr->adjust.v_total_min =
 994                                calc_v_total_from_refresh(stream,
 995                                        in_out_vrr->fixed.target_refresh_in_uhz);
 996                        in_out_vrr->adjust.v_total_max =
 997                                in_out_vrr->adjust.v_total_min;
 998                }
 999        } else {
1000                in_out_vrr->state = VRR_STATE_INACTIVE;
1001                in_out_vrr->adjust.v_total_min = stream->timing.v_total;
1002                in_out_vrr->adjust.v_total_max = stream->timing.v_total;
1003        }
1004}
1005
1006void mod_freesync_handle_preflip(struct mod_freesync *mod_freesync,
1007                const struct dc_plane_state *plane,
1008                const struct dc_stream_state *stream,
1009                unsigned int curr_time_stamp_in_us,
1010                struct mod_vrr_params *in_out_vrr)
1011{
1012        struct core_freesync *core_freesync = NULL;
1013        unsigned int last_render_time_in_us = 0;
1014        unsigned int average_render_time_in_us = 0;
1015
1016        if (mod_freesync == NULL)
1017                return;
1018
1019        core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
1020
1021        if (in_out_vrr->supported &&
1022                        in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE) {
1023                unsigned int i = 0;
1024                unsigned int oldest_index = plane->time.index + 1;
1025
1026                if (oldest_index >= DC_PLANE_UPDATE_TIMES_MAX)
1027                        oldest_index = 0;
1028
1029                last_render_time_in_us = curr_time_stamp_in_us -
1030                                plane->time.prev_update_time_in_us;
1031
1032                // Sum off all entries except oldest one
1033                for (i = 0; i < DC_PLANE_UPDATE_TIMES_MAX; i++) {
1034                        average_render_time_in_us +=
1035                                        plane->time.time_elapsed_in_us[i];
1036                }
1037                average_render_time_in_us -=
1038                                plane->time.time_elapsed_in_us[oldest_index];
1039
1040                // Add render time for current flip
1041                average_render_time_in_us += last_render_time_in_us;
1042                average_render_time_in_us /= DC_PLANE_UPDATE_TIMES_MAX;
1043
1044                if (in_out_vrr->btr.btr_enabled) {
1045                        apply_below_the_range(core_freesync,
1046                                        stream,
1047                                        last_render_time_in_us,
1048                                        in_out_vrr);
1049                } else {
1050                        apply_fixed_refresh(core_freesync,
1051                                stream,
1052                                last_render_time_in_us,
1053                                in_out_vrr);
1054                }
1055
1056        }
1057}
1058
1059void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync,
1060                const struct dc_stream_state *stream,
1061                struct mod_vrr_params *in_out_vrr)
1062{
1063        struct core_freesync *core_freesync = NULL;
1064
1065        if ((mod_freesync == NULL) || (stream == NULL) || (in_out_vrr == NULL))
1066                return;
1067
1068        core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
1069
1070        if (in_out_vrr->supported == false)
1071                return;
1072
1073        /* Below the Range Logic */
1074
1075        /* Only execute if in fullscreen mode */
1076        if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE &&
1077                                        in_out_vrr->btr.btr_active) {
1078                /* TODO: pass in flag for Pre-DCE12 ASIC
1079                 * in order for frame variable duration to take affect,
1080                 * it needs to be done one VSYNC early, which is at
1081                 * frameCounter == 1.
1082                 * For DCE12 and newer updates to V_TOTAL_MIN/MAX
1083                 * will take affect on current frame
1084                 */
1085                if (in_out_vrr->btr.frames_to_insert ==
1086                                in_out_vrr->btr.frame_counter) {
1087                        in_out_vrr->adjust.v_total_min =
1088                                calc_v_total_from_duration(stream,
1089                                in_out_vrr,
1090                                in_out_vrr->btr.inserted_duration_in_us);
1091                        in_out_vrr->adjust.v_total_max =
1092                                in_out_vrr->adjust.v_total_min;
1093                }
1094
1095                if (in_out_vrr->btr.frame_counter > 0)
1096                        in_out_vrr->btr.frame_counter--;
1097
1098                /* Restore FreeSync */
1099                if (in_out_vrr->btr.frame_counter == 0) {
1100                        in_out_vrr->adjust.v_total_min =
1101                                calc_v_total_from_refresh(stream,
1102                                in_out_vrr->max_refresh_in_uhz);
1103                        in_out_vrr->adjust.v_total_max =
1104                                calc_v_total_from_refresh(stream,
1105                                in_out_vrr->min_refresh_in_uhz);
1106                }
1107        }
1108
1109        /* If in fullscreen freesync mode or in video, do not program
1110         * static screen ramp values
1111         */
1112        if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE)
1113                in_out_vrr->fixed.ramping_active = false;
1114
1115        /* Gradual Static Screen Ramping Logic */
1116        /* Execute if ramp is active and user enabled freesync static screen*/
1117        if (in_out_vrr->state == VRR_STATE_ACTIVE_FIXED &&
1118                                in_out_vrr->fixed.ramping_active) {
1119                update_v_total_for_static_ramp(
1120                                core_freesync, stream, in_out_vrr);
1121        }
1122}
1123
1124void mod_freesync_get_settings(struct mod_freesync *mod_freesync,
1125                const struct mod_vrr_params *vrr,
1126                unsigned int *v_total_min, unsigned int *v_total_max,
1127                unsigned int *event_triggers,
1128                unsigned int *window_min, unsigned int *window_max,
1129                unsigned int *lfc_mid_point_in_us,
1130                unsigned int *inserted_frames,
1131                unsigned int *inserted_duration_in_us)
1132{
1133        struct core_freesync *core_freesync = NULL;
1134
1135        if (mod_freesync == NULL)
1136                return;
1137
1138        core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
1139
1140        if (vrr->supported) {
1141                *v_total_min = vrr->adjust.v_total_min;
1142                *v_total_max = vrr->adjust.v_total_max;
1143                *event_triggers = 0;
1144                *lfc_mid_point_in_us = vrr->btr.mid_point_in_us;
1145                *inserted_frames = vrr->btr.frames_to_insert;
1146                *inserted_duration_in_us = vrr->btr.inserted_duration_in_us;
1147        }
1148}
1149
1150unsigned long long mod_freesync_calc_nominal_field_rate(
1151                        const struct dc_stream_state *stream)
1152{
1153        unsigned long long nominal_field_rate_in_uhz = 0;
1154
1155        /* Calculate nominal field rate for stream */
1156        nominal_field_rate_in_uhz = stream->timing.pix_clk_100hz / 10;
1157        nominal_field_rate_in_uhz *= 1000ULL * 1000ULL * 1000ULL;
1158        nominal_field_rate_in_uhz = div_u64(nominal_field_rate_in_uhz,
1159                                                stream->timing.h_total);
1160        nominal_field_rate_in_uhz = div_u64(nominal_field_rate_in_uhz,
1161                                                stream->timing.v_total);
1162
1163        return nominal_field_rate_in_uhz;
1164}
1165
1166bool mod_freesync_is_valid_range(struct mod_freesync *mod_freesync,
1167                const struct dc_stream_state *stream,
1168                uint32_t min_refresh_cap_in_uhz,
1169                uint32_t max_refresh_cap_in_uhz,
1170                uint32_t min_refresh_request_in_uhz,
1171                uint32_t max_refresh_request_in_uhz)
1172{
1173        /* Calculate nominal field rate for stream */
1174        unsigned long long nominal_field_rate_in_uhz =
1175                        mod_freesync_calc_nominal_field_rate(stream);
1176
1177        /* Typically nominal refresh calculated can have some fractional part.
1178         * Allow for some rounding error of actual video timing by taking floor
1179         * of caps and request. Round the nominal refresh rate.
1180         *
1181         * Dividing will convert everything to units in Hz although input
1182         * variable name is in uHz!
1183         *
1184         * Also note, this takes care of rounding error on the nominal refresh
1185         * so by rounding error we only expect it to be off by a small amount,
1186         * such as < 0.1 Hz. i.e. 143.9xxx or 144.1xxx.
1187         *
1188         * Example 1. Caps    Min = 40 Hz, Max = 144 Hz
1189         *            Request Min = 40 Hz, Max = 144 Hz
1190         *                    Nominal = 143.5x Hz rounded to 144 Hz
1191         *            This function should allow this as valid request
1192         *
1193         * Example 2. Caps    Min = 40 Hz, Max = 144 Hz
1194         *            Request Min = 40 Hz, Max = 144 Hz
1195         *                    Nominal = 144.4x Hz rounded to 144 Hz
1196         *            This function should allow this as valid request
1197         *
1198         * Example 3. Caps    Min = 40 Hz, Max = 144 Hz
1199         *            Request Min = 40 Hz, Max = 144 Hz
1200         *                    Nominal = 120.xx Hz rounded to 120 Hz
1201         *            This function should return NOT valid since the requested
1202         *            max is greater than current timing's nominal
1203         *
1204         * Example 4. Caps    Min = 40 Hz, Max = 120 Hz
1205         *            Request Min = 40 Hz, Max = 120 Hz
1206         *                    Nominal = 144.xx Hz rounded to 144 Hz
1207         *            This function should return NOT valid since the nominal
1208         *            is greater than the capability's max refresh
1209         */
1210        nominal_field_rate_in_uhz =
1211                        div_u64(nominal_field_rate_in_uhz + 500000, 1000000);
1212        min_refresh_cap_in_uhz /= 1000000;
1213        max_refresh_cap_in_uhz /= 1000000;
1214        min_refresh_request_in_uhz /= 1000000;
1215        max_refresh_request_in_uhz /= 1000000;
1216
1217        // Check nominal is within range
1218        if (nominal_field_rate_in_uhz > max_refresh_cap_in_uhz ||
1219                nominal_field_rate_in_uhz < min_refresh_cap_in_uhz)
1220                return false;
1221
1222        // If nominal is less than max, limit the max allowed refresh rate
1223        if (nominal_field_rate_in_uhz < max_refresh_cap_in_uhz)
1224                max_refresh_cap_in_uhz = nominal_field_rate_in_uhz;
1225
1226        // Don't allow min > max
1227        if (min_refresh_request_in_uhz > max_refresh_request_in_uhz)
1228                return false;
1229
1230        // Check min is within range
1231        if (min_refresh_request_in_uhz > max_refresh_cap_in_uhz ||
1232                min_refresh_request_in_uhz < min_refresh_cap_in_uhz)
1233                return false;
1234
1235        // Check max is within range
1236        if (max_refresh_request_in_uhz > max_refresh_cap_in_uhz ||
1237                max_refresh_request_in_uhz < min_refresh_cap_in_uhz)
1238                return false;
1239
1240        // For variable range, check for at least 10 Hz range
1241        if ((max_refresh_request_in_uhz != min_refresh_request_in_uhz) &&
1242                (max_refresh_request_in_uhz - min_refresh_request_in_uhz < 10))
1243                return false;
1244
1245        return true;
1246}
1247
1248