LCOV - code coverage report
Current view: top level - drivers/gpu/drm/amd/display/modules/freesync - freesync.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 517 0.0 %
Date: 2022-12-09 01:23:36 Functions: 0 23 0.0 %

          Line data    Source code
       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             : 
      31             : #define MOD_FREESYNC_MAX_CONCURRENT_STREAMS  32
      32             : 
      33             : #define MIN_REFRESH_RANGE 10
      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 10
      38             : /* Threshold to exit/exit BTR (to avoid frequent enter-exits at the lower limit) */
      39             : #define BTR_MAX_MARGIN 2500
      40             : /* Threshold to change BTR multiplier (to avoid frequent changes) */
      41             : #define BTR_DRIFT_MARGIN 2000
      42             : /* Threshold to exit fixed refresh rate */
      43             : #define FIXED_REFRESH_EXIT_MARGIN_IN_HZ 1
      44             : /* Number of consecutive frames to check before entering/exiting fixed refresh */
      45             : #define FIXED_REFRESH_ENTER_FRAME_COUNT 5
      46             : #define FIXED_REFRESH_EXIT_FRAME_COUNT 10
      47             : /* Flip interval workaround constants */
      48             : #define VSYNCS_BETWEEN_FLIP_THRESHOLD 2
      49             : #define FREESYNC_CONSEC_FLIP_AFTER_VSYNC 5
      50             : #define FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US 500
      51             : 
      52             : struct core_freesync {
      53             :         struct mod_freesync public;
      54             :         struct dc *dc;
      55             : };
      56             : 
      57             : #define MOD_FREESYNC_TO_CORE(mod_freesync)\
      58             :                 container_of(mod_freesync, struct core_freesync, public)
      59             : 
      60           0 : struct mod_freesync *mod_freesync_create(struct dc *dc)
      61             : {
      62           0 :         struct core_freesync *core_freesync =
      63             :                         kzalloc(sizeof(struct core_freesync), GFP_KERNEL);
      64             : 
      65           0 :         if (core_freesync == NULL)
      66             :                 goto fail_alloc_context;
      67             : 
      68           0 :         if (dc == NULL)
      69             :                 goto fail_construct;
      70             : 
      71           0 :         core_freesync->dc = dc;
      72           0 :         return &core_freesync->public;
      73             : 
      74             : fail_construct:
      75           0 :         kfree(core_freesync);
      76             : 
      77             : fail_alloc_context:
      78             :         return NULL;
      79             : }
      80             : 
      81           0 : void mod_freesync_destroy(struct mod_freesync *mod_freesync)
      82             : {
      83           0 :         struct core_freesync *core_freesync = NULL;
      84           0 :         if (mod_freesync == NULL)
      85             :                 return;
      86           0 :         core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
      87           0 :         kfree(core_freesync);
      88             : }
      89             : 
      90             : #if 0 /* Unused currently */
      91             : static unsigned int calc_refresh_in_uhz_from_duration(
      92             :                 unsigned int duration_in_ns)
      93             : {
      94             :         unsigned int refresh_in_uhz =
      95             :                         ((unsigned int)(div64_u64((1000000000ULL * 1000000),
      96             :                                         duration_in_ns)));
      97             :         return refresh_in_uhz;
      98             : }
      99             : #endif
     100             : 
     101             : static unsigned int calc_duration_in_us_from_refresh_in_uhz(
     102             :                 unsigned int refresh_in_uhz)
     103             : {
     104           0 :         unsigned int duration_in_us =
     105           0 :                         ((unsigned int)(div64_u64((1000000000ULL * 1000),
     106             :                                         refresh_in_uhz)));
     107             :         return duration_in_us;
     108             : }
     109             : 
     110             : static unsigned int calc_duration_in_us_from_v_total(
     111             :                 const struct dc_stream_state *stream,
     112             :                 const struct mod_vrr_params *in_vrr,
     113             :                 unsigned int v_total)
     114             : {
     115           0 :         unsigned int duration_in_us =
     116           0 :                         (unsigned int)(div64_u64(((unsigned long long)(v_total)
     117           0 :                                 * 10000) * stream->timing.h_total,
     118             :                                         stream->timing.pix_clk_100hz));
     119             : 
     120             :         return duration_in_us;
     121             : }
     122             : 
     123           0 : unsigned int mod_freesync_calc_v_total_from_refresh(
     124             :                 const struct dc_stream_state *stream,
     125             :                 unsigned int refresh_in_uhz)
     126             : {
     127             :         unsigned int v_total;
     128             :         unsigned int frame_duration_in_ns;
     129             : 
     130           0 :         frame_duration_in_ns =
     131           0 :                         ((unsigned int)(div64_u64((1000000000ULL * 1000000),
     132             :                                         refresh_in_uhz)));
     133             : 
     134           0 :         v_total = div64_u64(div64_u64(((unsigned long long)(
     135           0 :                         frame_duration_in_ns) * (stream->timing.pix_clk_100hz / 10)),
     136           0 :                         stream->timing.h_total), 1000000);
     137             : 
     138             :         /* v_total cannot be less than nominal */
     139           0 :         if (v_total < stream->timing.v_total) {
     140             :                 ASSERT(v_total < stream->timing.v_total);
     141             :                 v_total = stream->timing.v_total;
     142             :         }
     143             : 
     144           0 :         return v_total;
     145             : }
     146             : 
     147             : static unsigned int calc_v_total_from_duration(
     148             :                 const struct dc_stream_state *stream,
     149             :                 const struct mod_vrr_params *vrr,
     150             :                 unsigned int duration_in_us)
     151             : {
     152           0 :         unsigned int v_total = 0;
     153             : 
     154           0 :         if (duration_in_us < vrr->min_duration_in_us)
     155           0 :                 duration_in_us = vrr->min_duration_in_us;
     156             : 
     157           0 :         if (duration_in_us > vrr->max_duration_in_us)
     158           0 :                 duration_in_us = vrr->max_duration_in_us;
     159             : 
     160           0 :         if (dc_is_hdmi_signal(stream->signal)) {
     161             :                 uint32_t h_total_up_scaled;
     162             : 
     163           0 :                 h_total_up_scaled = stream->timing.h_total * 10000;
     164           0 :                 v_total = div_u64((unsigned long long)duration_in_us
     165           0 :                                         * stream->timing.pix_clk_100hz + (h_total_up_scaled - 1),
     166             :                                         h_total_up_scaled);
     167             :         } else {
     168           0 :                 v_total = div64_u64(div64_u64(((unsigned long long)(
     169           0 :                                         duration_in_us) * (stream->timing.pix_clk_100hz / 10)),
     170             :                                         stream->timing.h_total), 1000);
     171             :         }
     172             : 
     173             :         /* v_total cannot be less than nominal */
     174           0 :         if (v_total < stream->timing.v_total) {
     175             :                 ASSERT(v_total < stream->timing.v_total);
     176             :                 v_total = stream->timing.v_total;
     177             :         }
     178             : 
     179             :         return v_total;
     180             : }
     181             : 
     182           0 : static void update_v_total_for_static_ramp(
     183             :                 struct core_freesync *core_freesync,
     184             :                 const struct dc_stream_state *stream,
     185             :                 struct mod_vrr_params *in_out_vrr)
     186             : {
     187           0 :         unsigned int v_total = 0;
     188           0 :         unsigned int current_duration_in_us =
     189           0 :                         calc_duration_in_us_from_v_total(
     190             :                                 stream, in_out_vrr,
     191             :                                 in_out_vrr->adjust.v_total_max);
     192           0 :         unsigned int target_duration_in_us =
     193           0 :                         calc_duration_in_us_from_refresh_in_uhz(
     194             :                                 in_out_vrr->fixed.target_refresh_in_uhz);
     195           0 :         bool ramp_direction_is_up = (current_duration_in_us >
     196             :                                 target_duration_in_us) ? true : false;
     197             : 
     198             :         /* Calculate ratio between new and current frame duration with 3 digit */
     199           0 :         unsigned int frame_duration_ratio = div64_u64(1000000,
     200           0 :                 (1000 +  div64_u64(((unsigned long long)(
     201             :                 STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME) *
     202             :                 current_duration_in_us),
     203             :                 1000000)));
     204             : 
     205             :         /* Calculate delta between new and current frame duration in us */
     206           0 :         unsigned int frame_duration_delta = div64_u64(((unsigned long long)(
     207             :                 current_duration_in_us) *
     208           0 :                 (1000 - frame_duration_ratio)), 1000);
     209             : 
     210             :         /* Adjust frame duration delta based on ratio between current and
     211             :          * standard frame duration (frame duration at 60 Hz refresh rate).
     212             :          */
     213           0 :         unsigned int ramp_rate_interpolated = div64_u64(((unsigned long long)(
     214             :                 frame_duration_delta) * current_duration_in_us), 16666);
     215             : 
     216             :         /* Going to a higher refresh rate (lower frame duration) */
     217           0 :         if (ramp_direction_is_up) {
     218             :                 /* Reduce frame duration */
     219           0 :                 current_duration_in_us -= ramp_rate_interpolated;
     220             : 
     221             :                 /* Adjust for frame duration below min */
     222           0 :                 if (current_duration_in_us <= target_duration_in_us) {
     223           0 :                         in_out_vrr->fixed.ramping_active = false;
     224           0 :                         in_out_vrr->fixed.ramping_done = true;
     225           0 :                         current_duration_in_us =
     226           0 :                                 calc_duration_in_us_from_refresh_in_uhz(
     227             :                                 in_out_vrr->fixed.target_refresh_in_uhz);
     228             :                 }
     229             :         /* Going to a lower refresh rate (larger frame duration) */
     230             :         } else {
     231             :                 /* Increase frame duration */
     232           0 :                 current_duration_in_us += ramp_rate_interpolated;
     233             : 
     234             :                 /* Adjust for frame duration above max */
     235           0 :                 if (current_duration_in_us >= target_duration_in_us) {
     236           0 :                         in_out_vrr->fixed.ramping_active = false;
     237           0 :                         in_out_vrr->fixed.ramping_done = true;
     238           0 :                         current_duration_in_us =
     239           0 :                                 calc_duration_in_us_from_refresh_in_uhz(
     240             :                                 in_out_vrr->fixed.target_refresh_in_uhz);
     241             :                 }
     242             :         }
     243             : 
     244           0 :         v_total = div64_u64(div64_u64(((unsigned long long)(
     245           0 :                         current_duration_in_us) * (stream->timing.pix_clk_100hz / 10)),
     246           0 :                                 stream->timing.h_total), 1000);
     247             : 
     248             :         /* v_total cannot be less than nominal */
     249           0 :         if (v_total < stream->timing.v_total)
     250           0 :                 v_total = stream->timing.v_total;
     251             : 
     252           0 :         in_out_vrr->adjust.v_total_min = v_total;
     253           0 :         in_out_vrr->adjust.v_total_max = v_total;
     254           0 : }
     255             : 
     256           0 : static void apply_below_the_range(struct core_freesync *core_freesync,
     257             :                 const struct dc_stream_state *stream,
     258             :                 unsigned int last_render_time_in_us,
     259             :                 struct mod_vrr_params *in_out_vrr)
     260             : {
     261           0 :         unsigned int inserted_frame_duration_in_us = 0;
     262           0 :         unsigned int mid_point_frames_ceil = 0;
     263           0 :         unsigned int mid_point_frames_floor = 0;
     264           0 :         unsigned int frame_time_in_us = 0;
     265           0 :         unsigned int delta_from_mid_point_in_us_1 = 0xFFFFFFFF;
     266           0 :         unsigned int delta_from_mid_point_in_us_2 = 0xFFFFFFFF;
     267           0 :         unsigned int frames_to_insert = 0;
     268             :         unsigned int delta_from_mid_point_delta_in_us;
     269           0 :         unsigned int max_render_time_in_us =
     270           0 :                         in_out_vrr->max_duration_in_us - in_out_vrr->btr.margin_in_us;
     271             : 
     272             :         /* Program BTR */
     273           0 :         if ((last_render_time_in_us + in_out_vrr->btr.margin_in_us / 2) < max_render_time_in_us) {
     274             :                 /* Exit Below the Range */
     275           0 :                 if (in_out_vrr->btr.btr_active) {
     276           0 :                         in_out_vrr->btr.frame_counter = 0;
     277           0 :                         in_out_vrr->btr.btr_active = false;
     278             :                 }
     279           0 :         } else if (last_render_time_in_us > (max_render_time_in_us + in_out_vrr->btr.margin_in_us / 2)) {
     280             :                 /* Enter Below the Range */
     281           0 :                 if (!in_out_vrr->btr.btr_active) {
     282           0 :                         in_out_vrr->btr.btr_active = true;
     283             :                 }
     284             :         }
     285             : 
     286             :         /* BTR set to "not active" so disengage */
     287           0 :         if (!in_out_vrr->btr.btr_active) {
     288           0 :                 in_out_vrr->btr.inserted_duration_in_us = 0;
     289           0 :                 in_out_vrr->btr.frames_to_insert = 0;
     290           0 :                 in_out_vrr->btr.frame_counter = 0;
     291             : 
     292             :                 /* Restore FreeSync */
     293           0 :                 in_out_vrr->adjust.v_total_min =
     294           0 :                         mod_freesync_calc_v_total_from_refresh(stream,
     295             :                                 in_out_vrr->max_refresh_in_uhz);
     296           0 :                 in_out_vrr->adjust.v_total_max =
     297           0 :                         mod_freesync_calc_v_total_from_refresh(stream,
     298             :                                 in_out_vrr->min_refresh_in_uhz);
     299             :         /* BTR set to "active" so engage */
     300             :         } else {
     301             : 
     302             :                 /* Calculate number of midPoint frames that could fit within
     303             :                  * the render time interval - take ceil of this value
     304             :                  */
     305           0 :                 mid_point_frames_ceil = (last_render_time_in_us +
     306           0 :                                 in_out_vrr->btr.mid_point_in_us - 1) /
     307             :                                         in_out_vrr->btr.mid_point_in_us;
     308             : 
     309           0 :                 if (mid_point_frames_ceil > 0) {
     310           0 :                         frame_time_in_us = last_render_time_in_us /
     311             :                                 mid_point_frames_ceil;
     312             :                         delta_from_mid_point_in_us_1 =
     313             :                                 (in_out_vrr->btr.mid_point_in_us >
     314             :                                 frame_time_in_us) ?
     315           0 :                                 (in_out_vrr->btr.mid_point_in_us - frame_time_in_us) :
     316             :                                 (frame_time_in_us - in_out_vrr->btr.mid_point_in_us);
     317             :                 }
     318             : 
     319             :                 /* Calculate number of midPoint frames that could fit within
     320             :                  * the render time interval - take floor of this value
     321             :                  */
     322           0 :                 mid_point_frames_floor = last_render_time_in_us /
     323             :                                 in_out_vrr->btr.mid_point_in_us;
     324             : 
     325           0 :                 if (mid_point_frames_floor > 0) {
     326             : 
     327           0 :                         frame_time_in_us = last_render_time_in_us /
     328             :                                 mid_point_frames_floor;
     329             :                         delta_from_mid_point_in_us_2 =
     330             :                                 (in_out_vrr->btr.mid_point_in_us >
     331             :                                 frame_time_in_us) ?
     332           0 :                                 (in_out_vrr->btr.mid_point_in_us - frame_time_in_us) :
     333             :                                 (frame_time_in_us - in_out_vrr->btr.mid_point_in_us);
     334             :                 }
     335             : 
     336             :                 /* Choose number of frames to insert based on how close it
     337             :                  * can get to the mid point of the variable range.
     338             :                  *  - Delta for CEIL: delta_from_mid_point_in_us_1
     339             :                  *  - Delta for FLOOR: delta_from_mid_point_in_us_2
     340             :                  */
     341           0 :                 if ((last_render_time_in_us / mid_point_frames_ceil) < in_out_vrr->min_duration_in_us) {
     342             :                         /* Check for out of range.
     343             :                          * If using CEIL produces a value that is out of range,
     344             :                          * then we are forced to use FLOOR.
     345             :                          */
     346             :                         frames_to_insert = mid_point_frames_floor;
     347           0 :                 } else if (mid_point_frames_floor < 2) {
     348             :                         /* Check if FLOOR would result in non-LFC. In this case
     349             :                          * choose to use CEIL
     350             :                          */
     351             :                         frames_to_insert = mid_point_frames_ceil;
     352           0 :                 } else if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) {
     353             :                         /* If choosing CEIL results in a frame duration that is
     354             :                          * closer to the mid point of the range.
     355             :                          * Choose CEIL
     356             :                          */
     357             :                         frames_to_insert = mid_point_frames_ceil;
     358             :                 } else {
     359             :                         /* If choosing FLOOR results in a frame duration that is
     360             :                          * closer to the mid point of the range.
     361             :                          * Choose FLOOR
     362             :                          */
     363           0 :                         frames_to_insert = mid_point_frames_floor;
     364             :                 }
     365             : 
     366             :                 /* Prefer current frame multiplier when BTR is enabled unless it drifts
     367             :                  * too far from the midpoint
     368             :                  */
     369           0 :                 if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) {
     370           0 :                         delta_from_mid_point_delta_in_us = delta_from_mid_point_in_us_2 -
     371             :                                         delta_from_mid_point_in_us_1;
     372             :                 } else {
     373           0 :                         delta_from_mid_point_delta_in_us = delta_from_mid_point_in_us_1 -
     374             :                                         delta_from_mid_point_in_us_2;
     375             :                 }
     376           0 :                 if (in_out_vrr->btr.frames_to_insert != 0 &&
     377             :                                 delta_from_mid_point_delta_in_us < BTR_DRIFT_MARGIN) {
     378           0 :                         if (((last_render_time_in_us / in_out_vrr->btr.frames_to_insert) <
     379           0 :                                         max_render_time_in_us) &&
     380             :                                 ((last_render_time_in_us / in_out_vrr->btr.frames_to_insert) >
     381             :                                         in_out_vrr->min_duration_in_us))
     382           0 :                                 frames_to_insert = in_out_vrr->btr.frames_to_insert;
     383             :                 }
     384             : 
     385             :                 /* Either we've calculated the number of frames to insert,
     386             :                  * or we need to insert min duration frames
     387             :                  */
     388           0 :                 if (last_render_time_in_us / frames_to_insert <
     389             :                                 in_out_vrr->min_duration_in_us){
     390           0 :                         frames_to_insert -= (frames_to_insert > 1) ?
     391           0 :                                         1 : 0;
     392             :                 }
     393             : 
     394           0 :                 if (frames_to_insert > 0)
     395           0 :                         inserted_frame_duration_in_us = last_render_time_in_us /
     396             :                                                         frames_to_insert;
     397             : 
     398           0 :                 if (inserted_frame_duration_in_us < in_out_vrr->min_duration_in_us)
     399           0 :                         inserted_frame_duration_in_us = in_out_vrr->min_duration_in_us;
     400             : 
     401             :                 /* Cache the calculated variables */
     402           0 :                 in_out_vrr->btr.inserted_duration_in_us =
     403             :                         inserted_frame_duration_in_us;
     404           0 :                 in_out_vrr->btr.frames_to_insert = frames_to_insert;
     405           0 :                 in_out_vrr->btr.frame_counter = frames_to_insert;
     406             :         }
     407           0 : }
     408             : 
     409           0 : static void apply_fixed_refresh(struct core_freesync *core_freesync,
     410             :                 const struct dc_stream_state *stream,
     411             :                 unsigned int last_render_time_in_us,
     412             :                 struct mod_vrr_params *in_out_vrr)
     413             : {
     414           0 :         bool update = false;
     415           0 :         unsigned int max_render_time_in_us = in_out_vrr->max_duration_in_us;
     416             : 
     417             :         /* Compute the exit refresh rate and exit frame duration */
     418           0 :         unsigned int exit_refresh_rate_in_milli_hz = ((1000000000/max_render_time_in_us)
     419             :                         + (1000*FIXED_REFRESH_EXIT_MARGIN_IN_HZ));
     420           0 :         unsigned int exit_frame_duration_in_us = 1000000000/exit_refresh_rate_in_milli_hz;
     421             : 
     422           0 :         if (last_render_time_in_us < exit_frame_duration_in_us) {
     423             :                 /* Exit Fixed Refresh mode */
     424           0 :                 if (in_out_vrr->fixed.fixed_active) {
     425           0 :                         in_out_vrr->fixed.frame_counter++;
     426             : 
     427           0 :                         if (in_out_vrr->fixed.frame_counter >
     428             :                                         FIXED_REFRESH_EXIT_FRAME_COUNT) {
     429           0 :                                 in_out_vrr->fixed.frame_counter = 0;
     430           0 :                                 in_out_vrr->fixed.fixed_active = false;
     431           0 :                                 in_out_vrr->fixed.target_refresh_in_uhz = 0;
     432           0 :                                 update = true;
     433             :                         }
     434             :                 } else
     435           0 :                         in_out_vrr->fixed.frame_counter = 0;
     436           0 :         } else if (last_render_time_in_us > max_render_time_in_us) {
     437             :                 /* Enter Fixed Refresh mode */
     438           0 :                 if (!in_out_vrr->fixed.fixed_active) {
     439           0 :                         in_out_vrr->fixed.frame_counter++;
     440             : 
     441           0 :                         if (in_out_vrr->fixed.frame_counter >
     442             :                                         FIXED_REFRESH_ENTER_FRAME_COUNT) {
     443           0 :                                 in_out_vrr->fixed.frame_counter = 0;
     444           0 :                                 in_out_vrr->fixed.fixed_active = true;
     445           0 :                                 in_out_vrr->fixed.target_refresh_in_uhz =
     446           0 :                                                 in_out_vrr->max_refresh_in_uhz;
     447           0 :                                 update = true;
     448             :                         }
     449             :                 } else
     450           0 :                         in_out_vrr->fixed.frame_counter = 0;
     451             :         }
     452             : 
     453           0 :         if (update) {
     454           0 :                 if (in_out_vrr->fixed.fixed_active) {
     455           0 :                         in_out_vrr->adjust.v_total_min =
     456           0 :                                 mod_freesync_calc_v_total_from_refresh(
     457             :                                 stream, in_out_vrr->max_refresh_in_uhz);
     458           0 :                         in_out_vrr->adjust.v_total_max =
     459             :                                         in_out_vrr->adjust.v_total_min;
     460             :                 } else {
     461           0 :                         in_out_vrr->adjust.v_total_min =
     462           0 :                                 mod_freesync_calc_v_total_from_refresh(stream,
     463             :                                         in_out_vrr->max_refresh_in_uhz);
     464           0 :                         in_out_vrr->adjust.v_total_max =
     465           0 :                                 mod_freesync_calc_v_total_from_refresh(stream,
     466             :                                         in_out_vrr->min_refresh_in_uhz);
     467             :                 }
     468             :         }
     469           0 : }
     470             : 
     471           0 : static void determine_flip_interval_workaround_req(struct mod_vrr_params *in_vrr,
     472             :                 unsigned int curr_time_stamp_in_us)
     473             : {
     474           0 :         in_vrr->flip_interval.vsync_to_flip_in_us = curr_time_stamp_in_us -
     475           0 :                         in_vrr->flip_interval.v_update_timestamp_in_us;
     476             : 
     477             :         /* Determine conditions for stopping workaround */
     478           0 :         if (in_vrr->flip_interval.flip_interval_workaround_active &&
     479           0 :                         in_vrr->flip_interval.vsyncs_between_flip < VSYNCS_BETWEEN_FLIP_THRESHOLD &&
     480             :                         in_vrr->flip_interval.vsync_to_flip_in_us > FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US) {
     481           0 :                 in_vrr->flip_interval.flip_interval_detect_counter = 0;
     482           0 :                 in_vrr->flip_interval.program_flip_interval_workaround = true;
     483           0 :                 in_vrr->flip_interval.flip_interval_workaround_active = false;
     484             :         } else {
     485             :                 /* Determine conditions for starting workaround */
     486           0 :                 if (in_vrr->flip_interval.vsyncs_between_flip >= VSYNCS_BETWEEN_FLIP_THRESHOLD &&
     487             :                                 in_vrr->flip_interval.vsync_to_flip_in_us < FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US) {
     488             :                         /* Increase flip interval counter we have 2 vsyncs between flips and
     489             :                          * vsync to flip interval is less than 500us
     490             :                          */
     491           0 :                         in_vrr->flip_interval.flip_interval_detect_counter++;
     492           0 :                         if (in_vrr->flip_interval.flip_interval_detect_counter > FREESYNC_CONSEC_FLIP_AFTER_VSYNC) {
     493             :                                 /* Start workaround if we detect 5 consecutive instances of the above case */
     494           0 :                                 in_vrr->flip_interval.program_flip_interval_workaround = true;
     495           0 :                                 in_vrr->flip_interval.flip_interval_workaround_active = true;
     496             :                         }
     497             :                 } else {
     498             :                         /* Reset the flip interval counter if we condition is no longer met */
     499           0 :                         in_vrr->flip_interval.flip_interval_detect_counter = 0;
     500             :                 }
     501             :         }
     502             : 
     503           0 :         in_vrr->flip_interval.vsyncs_between_flip = 0;
     504           0 : }
     505             : 
     506             : static bool vrr_settings_require_update(struct core_freesync *core_freesync,
     507             :                 struct mod_freesync_config *in_config,
     508             :                 unsigned int min_refresh_in_uhz,
     509             :                 unsigned int max_refresh_in_uhz,
     510             :                 struct mod_vrr_params *in_vrr)
     511             : {
     512           0 :         if (in_vrr->state != in_config->state) {
     513             :                 return true;
     514           0 :         } else if (in_vrr->state == VRR_STATE_ACTIVE_FIXED &&
     515           0 :                         in_vrr->fixed.target_refresh_in_uhz !=
     516           0 :                                         in_config->fixed_refresh_in_uhz) {
     517             :                 return true;
     518           0 :         } else if (in_vrr->min_refresh_in_uhz != min_refresh_in_uhz) {
     519             :                 return true;
     520           0 :         } else if (in_vrr->max_refresh_in_uhz != max_refresh_in_uhz) {
     521             :                 return true;
     522             :         }
     523             : 
     524             :         return false;
     525             : }
     526             : 
     527           0 : bool mod_freesync_get_vmin_vmax(struct mod_freesync *mod_freesync,
     528             :                 const struct dc_stream_state *stream,
     529             :                 unsigned int *vmin,
     530             :                 unsigned int *vmax)
     531             : {
     532           0 :         *vmin = stream->adjust.v_total_min;
     533           0 :         *vmax = stream->adjust.v_total_max;
     534             : 
     535           0 :         return true;
     536             : }
     537             : 
     538           0 : bool mod_freesync_get_v_position(struct mod_freesync *mod_freesync,
     539             :                 struct dc_stream_state *stream,
     540             :                 unsigned int *nom_v_pos,
     541             :                 unsigned int *v_pos)
     542             : {
     543           0 :         struct core_freesync *core_freesync = NULL;
     544             :         struct crtc_position position;
     545             : 
     546           0 :         if (mod_freesync == NULL)
     547             :                 return false;
     548             : 
     549           0 :         core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
     550             : 
     551           0 :         if (dc_stream_get_crtc_position(core_freesync->dc, &stream, 1,
     552             :                                         &position.vertical_count,
     553             :                                         &position.nominal_vcount)) {
     554             : 
     555           0 :                 *nom_v_pos = position.nominal_vcount;
     556           0 :                 *v_pos = position.vertical_count;
     557             : 
     558           0 :                 return true;
     559             :         }
     560             : 
     561             :         return false;
     562             : }
     563             : 
     564           0 : static void build_vrr_infopacket_data_v1(const struct mod_vrr_params *vrr,
     565             :                 struct dc_info_packet *infopacket,
     566             :                 bool freesync_on_desktop)
     567             : {
     568             :         /* PB1 = 0x1A (24bit AMD IEEE OUI (0x00001A) - Byte 0) */
     569           0 :         infopacket->sb[1] = 0x1A;
     570             : 
     571             :         /* PB2 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 1) */
     572           0 :         infopacket->sb[2] = 0x00;
     573             : 
     574             :         /* PB3 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 2) */
     575           0 :         infopacket->sb[3] = 0x00;
     576             : 
     577             :         /* PB4 = Reserved */
     578             : 
     579             :         /* PB5 = Reserved */
     580             : 
     581             :         /* PB6 = [Bits 7:3 = Reserved] */
     582             : 
     583             :         /* PB6 = [Bit 0 = FreeSync Supported] */
     584           0 :         if (vrr->state != VRR_STATE_UNSUPPORTED)
     585           0 :                 infopacket->sb[6] |= 0x01;
     586             : 
     587             :         /* PB6 = [Bit 1 = FreeSync Enabled] */
     588           0 :         if (vrr->state != VRR_STATE_DISABLED &&
     589             :                         vrr->state != VRR_STATE_UNSUPPORTED)
     590           0 :                 infopacket->sb[6] |= 0x02;
     591             : 
     592           0 :         if (freesync_on_desktop) {
     593             :                 /* PB6 = [Bit 2 = FreeSync Active] */
     594           0 :                 if (vrr->state != VRR_STATE_DISABLED &&
     595             :                         vrr->state != VRR_STATE_UNSUPPORTED)
     596           0 :                         infopacket->sb[6] |= 0x04;
     597             :         } else {
     598           0 :                 if (vrr->state == VRR_STATE_ACTIVE_VARIABLE ||
     599             :                         vrr->state == VRR_STATE_ACTIVE_FIXED)
     600           0 :                         infopacket->sb[6] |= 0x04;
     601             :         }
     602             : 
     603             :         // For v1 & 2 infoframes program nominal if non-fs mode, otherwise full range
     604             :         /* PB7 = FreeSync Minimum refresh rate (Hz) */
     605           0 :         if (vrr->state == VRR_STATE_ACTIVE_VARIABLE ||
     606             :                         vrr->state == VRR_STATE_ACTIVE_FIXED) {
     607           0 :                 infopacket->sb[7] = (unsigned char)((vrr->min_refresh_in_uhz + 500000) / 1000000);
     608             :         } else {
     609           0 :                 infopacket->sb[7] = (unsigned char)((vrr->max_refresh_in_uhz + 500000) / 1000000);
     610             :         }
     611             : 
     612             :         /* PB8 = FreeSync Maximum refresh rate (Hz)
     613             :          * Note: We should never go above the field rate of the mode timing set.
     614             :          */
     615           0 :         infopacket->sb[8] = (unsigned char)((vrr->max_refresh_in_uhz + 500000) / 1000000);
     616           0 : }
     617             : 
     618           0 : static void build_vrr_infopacket_data_v3(const struct mod_vrr_params *vrr,
     619             :                 struct dc_info_packet *infopacket)
     620             : {
     621             :         unsigned int min_refresh;
     622             :         unsigned int max_refresh;
     623             :         unsigned int fixed_refresh;
     624             :         unsigned int min_programmed;
     625             :         unsigned int max_programmed;
     626             : 
     627             :         /* PB1 = 0x1A (24bit AMD IEEE OUI (0x00001A) - Byte 0) */
     628           0 :         infopacket->sb[1] = 0x1A;
     629             : 
     630             :         /* PB2 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 1) */
     631           0 :         infopacket->sb[2] = 0x00;
     632             : 
     633             :         /* PB3 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 2) */
     634           0 :         infopacket->sb[3] = 0x00;
     635             : 
     636             :         /* PB4 = Reserved */
     637             : 
     638             :         /* PB5 = Reserved */
     639             : 
     640             :         /* PB6 = [Bits 7:3 = Reserved] */
     641             : 
     642             :         /* PB6 = [Bit 0 = FreeSync Supported] */
     643           0 :         if (vrr->state != VRR_STATE_UNSUPPORTED)
     644           0 :                 infopacket->sb[6] |= 0x01;
     645             : 
     646             :         /* PB6 = [Bit 1 = FreeSync Enabled] */
     647           0 :         if (vrr->state != VRR_STATE_DISABLED &&
     648             :                         vrr->state != VRR_STATE_UNSUPPORTED)
     649           0 :                 infopacket->sb[6] |= 0x02;
     650             : 
     651             :         /* PB6 = [Bit 2 = FreeSync Active] */
     652           0 :         if (vrr->state == VRR_STATE_ACTIVE_VARIABLE ||
     653             :                         vrr->state == VRR_STATE_ACTIVE_FIXED)
     654           0 :                 infopacket->sb[6] |= 0x04;
     655             : 
     656           0 :         min_refresh = (vrr->min_refresh_in_uhz + 500000) / 1000000;
     657           0 :         max_refresh = (vrr->max_refresh_in_uhz + 500000) / 1000000;
     658           0 :         fixed_refresh = (vrr->fixed_refresh_in_uhz + 500000) / 1000000;
     659             : 
     660           0 :         min_programmed = (vrr->state == VRR_STATE_ACTIVE_FIXED) ? fixed_refresh :
     661           0 :                         (vrr->state == VRR_STATE_ACTIVE_VARIABLE) ? min_refresh :
     662           0 :                         (vrr->state == VRR_STATE_INACTIVE) ? min_refresh :
     663             :                         max_refresh; // Non-fs case, program nominal range
     664             : 
     665           0 :         max_programmed = (vrr->state == VRR_STATE_ACTIVE_FIXED) ? fixed_refresh :
     666             :                         (vrr->state == VRR_STATE_ACTIVE_VARIABLE) ? max_refresh :
     667             :                         max_refresh;// Non-fs case, program nominal range
     668             : 
     669             :         /* PB7 = FreeSync Minimum refresh rate (Hz) */
     670           0 :         infopacket->sb[7] = min_programmed & 0xFF;
     671             : 
     672             :         /* PB8 = FreeSync Maximum refresh rate (Hz) */
     673           0 :         infopacket->sb[8] = max_programmed & 0xFF;
     674             : 
     675             :         /* PB11 : MSB FreeSync Minimum refresh rate [Hz] - bits 9:8 */
     676           0 :         infopacket->sb[11] = (min_programmed >> 8) & 0x03;
     677             : 
     678             :         /* PB12 : MSB FreeSync Maximum refresh rate [Hz] - bits 9:8 */
     679           0 :         infopacket->sb[12] = (max_programmed >> 8) & 0x03;
     680             : 
     681             :         /* PB16 : Reserved bits 7:1, FixedRate bit 0 */
     682           0 :         infopacket->sb[16] = (vrr->state == VRR_STATE_ACTIVE_FIXED) ? 1 : 0;
     683           0 : }
     684             : 
     685             : static void build_vrr_infopacket_fs2_data(enum color_transfer_func app_tf,
     686             :                 struct dc_info_packet *infopacket)
     687             : {
     688           0 :         if (app_tf != TRANSFER_FUNC_UNKNOWN) {
     689           0 :                 infopacket->valid = true;
     690             : 
     691           0 :                 infopacket->sb[6] |= 0x08;  // PB6 = [Bit 3 = Native Color Active]
     692             : 
     693           0 :                 if (app_tf == TRANSFER_FUNC_GAMMA_22) {
     694           0 :                         infopacket->sb[9] |= 0x04;  // PB6 = [Bit 2 = Gamma 2.2 EOTF Active]
     695             :                 }
     696             :         }
     697             : }
     698             : 
     699             : static void build_vrr_infopacket_header_v1(enum signal_type signal,
     700             :                 struct dc_info_packet *infopacket,
     701             :                 unsigned int *payload_size)
     702             : {
     703           0 :         if (dc_is_hdmi_signal(signal)) {
     704             : 
     705             :                 /* HEADER */
     706             : 
     707             :                 /* HB0  = Packet Type = 0x83 (Source Product
     708             :                  *        Descriptor InfoFrame)
     709             :                  */
     710           0 :                 infopacket->hb0 = DC_HDMI_INFOFRAME_TYPE_SPD;
     711             : 
     712             :                 /* HB1  = Version = 0x01 */
     713           0 :                 infopacket->hb1 = 0x01;
     714             : 
     715             :                 /* HB2  = [Bits 7:5 = 0] [Bits 4:0 = Length = 0x08] */
     716           0 :                 infopacket->hb2 = 0x08;
     717             : 
     718           0 :                 *payload_size = 0x08;
     719             : 
     720           0 :         } else if (dc_is_dp_signal(signal)) {
     721             : 
     722             :                 /* HEADER */
     723             : 
     724             :                 /* HB0  = Secondary-data Packet ID = 0 - Only non-zero
     725             :                  *        when used to associate audio related info packets
     726             :                  */
     727           0 :                 infopacket->hb0 = 0x00;
     728             : 
     729             :                 /* HB1  = Packet Type = 0x83 (Source Product
     730             :                  *        Descriptor InfoFrame)
     731             :                  */
     732           0 :                 infopacket->hb1 = DC_HDMI_INFOFRAME_TYPE_SPD;
     733             : 
     734             :                 /* HB2  = [Bits 7:0 = Least significant eight bits -
     735             :                  *        For INFOFRAME, the value must be 1Bh]
     736             :                  */
     737           0 :                 infopacket->hb2 = 0x1B;
     738             : 
     739             :                 /* HB3  = [Bits 7:2 = INFOFRAME SDP Version Number = 0x1]
     740             :                  *        [Bits 1:0 = Most significant two bits = 0x00]
     741             :                  */
     742           0 :                 infopacket->hb3 = 0x04;
     743             : 
     744           0 :                 *payload_size = 0x1B;
     745             :         }
     746             : }
     747             : 
     748             : static void build_vrr_infopacket_header_v2(enum signal_type signal,
     749             :                 struct dc_info_packet *infopacket,
     750             :                 unsigned int *payload_size)
     751             : {
     752           0 :         if (dc_is_hdmi_signal(signal)) {
     753             : 
     754             :                 /* HEADER */
     755             : 
     756             :                 /* HB0  = Packet Type = 0x83 (Source Product
     757             :                  *        Descriptor InfoFrame)
     758             :                  */
     759           0 :                 infopacket->hb0 = DC_HDMI_INFOFRAME_TYPE_SPD;
     760             : 
     761             :                 /* HB1  = Version = 0x02 */
     762           0 :                 infopacket->hb1 = 0x02;
     763             : 
     764             :                 /* HB2  = [Bits 7:5 = 0] [Bits 4:0 = Length = 0x09] */
     765           0 :                 infopacket->hb2 = 0x09;
     766             : 
     767           0 :                 *payload_size = 0x09;
     768           0 :         } else if (dc_is_dp_signal(signal)) {
     769             : 
     770             :                 /* HEADER */
     771             : 
     772             :                 /* HB0  = Secondary-data Packet ID = 0 - Only non-zero
     773             :                  *        when used to associate audio related info packets
     774             :                  */
     775           0 :                 infopacket->hb0 = 0x00;
     776             : 
     777             :                 /* HB1  = Packet Type = 0x83 (Source Product
     778             :                  *        Descriptor InfoFrame)
     779             :                  */
     780           0 :                 infopacket->hb1 = DC_HDMI_INFOFRAME_TYPE_SPD;
     781             : 
     782             :                 /* HB2  = [Bits 7:0 = Least significant eight bits -
     783             :                  *        For INFOFRAME, the value must be 1Bh]
     784             :                  */
     785           0 :                 infopacket->hb2 = 0x1B;
     786             : 
     787             :                 /* HB3  = [Bits 7:2 = INFOFRAME SDP Version Number = 0x2]
     788             :                  *        [Bits 1:0 = Most significant two bits = 0x00]
     789             :                  */
     790           0 :                 infopacket->hb3 = 0x08;
     791             : 
     792           0 :                 *payload_size = 0x1B;
     793             :         }
     794             : }
     795             : 
     796             : static void build_vrr_infopacket_header_v3(enum signal_type signal,
     797             :                 struct dc_info_packet *infopacket,
     798             :                 unsigned int *payload_size)
     799             : {
     800             :         unsigned char version;
     801             : 
     802           0 :         version = 3;
     803           0 :         if (dc_is_hdmi_signal(signal)) {
     804             : 
     805             :                 /* HEADER */
     806             : 
     807             :                 /* HB0  = Packet Type = 0x83 (Source Product
     808             :                  *        Descriptor InfoFrame)
     809             :                  */
     810           0 :                 infopacket->hb0 = DC_HDMI_INFOFRAME_TYPE_SPD;
     811             : 
     812             :                 /* HB1  = Version = 0x03 */
     813           0 :                 infopacket->hb1 = version;
     814             : 
     815             :                 /* HB2  = [Bits 7:5 = 0] [Bits 4:0 = Length] */
     816           0 :                 infopacket->hb2 = 0x10;
     817             : 
     818           0 :                 *payload_size = 0x10;
     819           0 :         } else if (dc_is_dp_signal(signal)) {
     820             : 
     821             :                 /* HEADER */
     822             : 
     823             :                 /* HB0  = Secondary-data Packet ID = 0 - Only non-zero
     824             :                  *        when used to associate audio related info packets
     825             :                  */
     826           0 :                 infopacket->hb0 = 0x00;
     827             : 
     828             :                 /* HB1  = Packet Type = 0x83 (Source Product
     829             :                  *        Descriptor InfoFrame)
     830             :                  */
     831           0 :                 infopacket->hb1 = DC_HDMI_INFOFRAME_TYPE_SPD;
     832             : 
     833             :                 /* HB2  = [Bits 7:0 = Least significant eight bits -
     834             :                  *        For INFOFRAME, the value must be 1Bh]
     835             :                  */
     836           0 :                 infopacket->hb2 = 0x1B;
     837             : 
     838             :                 /* HB3  = [Bits 7:2 = INFOFRAME SDP Version Number = 0x2]
     839             :                  *        [Bits 1:0 = Most significant two bits = 0x00]
     840             :                  */
     841             : 
     842           0 :                 infopacket->hb3 = (version & 0x3F) << 2;
     843             : 
     844           0 :                 *payload_size = 0x1B;
     845             :         }
     846             : }
     847             : 
     848             : static void build_vrr_infopacket_checksum(unsigned int *payload_size,
     849             :                 struct dc_info_packet *infopacket)
     850             : {
     851             :         /* Calculate checksum */
     852           0 :         unsigned int idx = 0;
     853           0 :         unsigned char checksum = 0;
     854             : 
     855           0 :         checksum += infopacket->hb0;
     856           0 :         checksum += infopacket->hb1;
     857           0 :         checksum += infopacket->hb2;
     858           0 :         checksum += infopacket->hb3;
     859             : 
     860           0 :         for (idx = 1; idx <= *payload_size; idx++)
     861           0 :                 checksum += infopacket->sb[idx];
     862             : 
     863             :         /* PB0 = Checksum (one byte complement) */
     864           0 :         infopacket->sb[0] = (unsigned char)(0x100 - checksum);
     865             : 
     866           0 :         infopacket->valid = true;
     867             : }
     868             : 
     869           0 : static void build_vrr_infopacket_v1(enum signal_type signal,
     870             :                 const struct mod_vrr_params *vrr,
     871             :                 struct dc_info_packet *infopacket,
     872             :                 bool freesync_on_desktop)
     873             : {
     874             :         /* SPD info packet for FreeSync */
     875           0 :         unsigned int payload_size = 0;
     876             : 
     877           0 :         build_vrr_infopacket_header_v1(signal, infopacket, &payload_size);
     878           0 :         build_vrr_infopacket_data_v1(vrr, infopacket, freesync_on_desktop);
     879           0 :         build_vrr_infopacket_checksum(&payload_size, infopacket);
     880             : 
     881             :         infopacket->valid = true;
     882           0 : }
     883             : 
     884           0 : static void build_vrr_infopacket_v2(enum signal_type signal,
     885             :                 const struct mod_vrr_params *vrr,
     886             :                 enum color_transfer_func app_tf,
     887             :                 struct dc_info_packet *infopacket,
     888             :                 bool freesync_on_desktop)
     889             : {
     890           0 :         unsigned int payload_size = 0;
     891             : 
     892           0 :         build_vrr_infopacket_header_v2(signal, infopacket, &payload_size);
     893           0 :         build_vrr_infopacket_data_v1(vrr, infopacket, freesync_on_desktop);
     894             : 
     895           0 :         build_vrr_infopacket_fs2_data(app_tf, infopacket);
     896             : 
     897           0 :         build_vrr_infopacket_checksum(&payload_size, infopacket);
     898             : 
     899             :         infopacket->valid = true;
     900           0 : }
     901             : #ifndef TRIM_FSFT
     902             : static void build_vrr_infopacket_fast_transport_data(
     903             :         bool ftActive,
     904             :         unsigned int ftOutputRate,
     905             :         struct dc_info_packet *infopacket)
     906             : {
     907             :         /* PB9 : bit7 - fast transport Active*/
     908           0 :         unsigned char activeBit = (ftActive) ? 1 << 7 : 0;
     909             : 
     910           0 :         infopacket->sb[1] &= ~activeBit;  //clear bit
     911           0 :         infopacket->sb[1] |=  activeBit;  //set bit
     912             : 
     913             :         /* PB13 : Target Output Pixel Rate [kHz] - bits 7:0  */
     914           0 :         infopacket->sb[13] = ftOutputRate & 0xFF;
     915             : 
     916             :         /* PB14 : Target Output Pixel Rate [kHz] - bits 15:8  */
     917           0 :         infopacket->sb[14] = (ftOutputRate >> 8) & 0xFF;
     918             : 
     919             :         /* PB15 : Target Output Pixel Rate [kHz] - bits 23:16  */
     920           0 :         infopacket->sb[15] = (ftOutputRate >> 16) & 0xFF;
     921             : 
     922             : }
     923             : #endif
     924             : 
     925           0 : static void build_vrr_infopacket_v3(enum signal_type signal,
     926             :                 const struct mod_vrr_params *vrr,
     927             : #ifndef TRIM_FSFT
     928             :                 bool ftActive, unsigned int ftOutputRate,
     929             : #endif
     930             :                 enum color_transfer_func app_tf,
     931             :                 struct dc_info_packet *infopacket)
     932             : {
     933           0 :         unsigned int payload_size = 0;
     934             : 
     935           0 :         build_vrr_infopacket_header_v3(signal, infopacket, &payload_size);
     936           0 :         build_vrr_infopacket_data_v3(vrr, infopacket);
     937             : 
     938           0 :         build_vrr_infopacket_fs2_data(app_tf, infopacket);
     939             : 
     940             : #ifndef TRIM_FSFT
     941           0 :         build_vrr_infopacket_fast_transport_data(
     942             :                         ftActive,
     943             :                         ftOutputRate,
     944             :                         infopacket);
     945             : #endif
     946             : 
     947           0 :         build_vrr_infopacket_checksum(&payload_size, infopacket);
     948             : 
     949             :         infopacket->valid = true;
     950           0 : }
     951             : 
     952             : static void build_vrr_infopacket_sdp_v1_3(enum vrr_packet_type packet_type,
     953             :                                                                                 struct dc_info_packet *infopacket)
     954             : {
     955           0 :         uint8_t idx = 0, size = 0;
     956             : 
     957           0 :         size = ((packet_type == PACKET_TYPE_FS_V1) ? 0x08 :
     958             :                         (packet_type == PACKET_TYPE_FS_V3) ? 0x10 :
     959             :                                                                                                 0x09);
     960             : 
     961           0 :         for (idx = infopacket->hb2; idx > 1; idx--) // Data Byte Count: 0x1B
     962           0 :                 infopacket->sb[idx] = infopacket->sb[idx-1];
     963             : 
     964           0 :         infopacket->sb[1] = size;                         // Length
     965           0 :         infopacket->sb[0] = (infopacket->hb3 >> 2) & 0x3F;//Version
     966           0 :         infopacket->hb3   = (0x13 << 2);                  // Header,SDP 1.3
     967           0 :         infopacket->hb2   = 0x1D;
     968             : }
     969             : 
     970           0 : void mod_freesync_build_vrr_infopacket(struct mod_freesync *mod_freesync,
     971             :                 const struct dc_stream_state *stream,
     972             :                 const struct mod_vrr_params *vrr,
     973             :                 enum vrr_packet_type packet_type,
     974             :                 enum color_transfer_func app_tf,
     975             :                 struct dc_info_packet *infopacket,
     976             :                 bool pack_sdp_v1_3)
     977             : {
     978             :         /* SPD info packet for FreeSync
     979             :          * VTEM info packet for HdmiVRR
     980             :          * Check if Freesync is supported. Return if false. If true,
     981             :          * set the corresponding bit in the info packet
     982             :          */
     983           0 :         if (!vrr->send_info_frame)
     984             :                 return;
     985             : 
     986           0 :         switch (packet_type) {
     987             :         case PACKET_TYPE_FS_V3:
     988             : #ifndef TRIM_FSFT
     989             :                 // always populate with pixel rate.
     990           0 :                 build_vrr_infopacket_v3(
     991             :                                 stream->signal, vrr,
     992             :                                 stream->timing.flags.FAST_TRANSPORT,
     993           0 :                                 (stream->timing.flags.FAST_TRANSPORT) ?
     994             :                                                 stream->timing.fast_transport_output_rate_100hz :
     995             :                                                 stream->timing.pix_clk_100hz,
     996             :                                 app_tf, infopacket);
     997             : #else
     998             :                 build_vrr_infopacket_v3(stream->signal, vrr, app_tf, infopacket);
     999             : #endif
    1000           0 :                 break;
    1001             :         case PACKET_TYPE_FS_V2:
    1002           0 :                 build_vrr_infopacket_v2(stream->signal, vrr, app_tf, infopacket, stream->freesync_on_desktop);
    1003           0 :                 break;
    1004             :         case PACKET_TYPE_VRR:
    1005             :         case PACKET_TYPE_FS_V1:
    1006             :         default:
    1007           0 :                 build_vrr_infopacket_v1(stream->signal, vrr, infopacket, stream->freesync_on_desktop);
    1008             :         }
    1009             : 
    1010           0 :         if (true == pack_sdp_v1_3 &&
    1011           0 :                 true == dc_is_dp_signal(stream->signal) &&
    1012           0 :                 packet_type != PACKET_TYPE_VRR &&
    1013           0 :                 packet_type != PACKET_TYPE_VTEM)
    1014             :                 build_vrr_infopacket_sdp_v1_3(packet_type, infopacket);
    1015             : }
    1016             : 
    1017           0 : void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync,
    1018             :                 const struct dc_stream_state *stream,
    1019             :                 struct mod_freesync_config *in_config,
    1020             :                 struct mod_vrr_params *in_out_vrr)
    1021             : {
    1022           0 :         struct core_freesync *core_freesync = NULL;
    1023           0 :         unsigned long long nominal_field_rate_in_uhz = 0;
    1024           0 :         unsigned long long rounded_nominal_in_uhz = 0;
    1025           0 :         unsigned int refresh_range = 0;
    1026           0 :         unsigned long long min_refresh_in_uhz = 0;
    1027           0 :         unsigned long long max_refresh_in_uhz = 0;
    1028             : 
    1029           0 :         if (mod_freesync == NULL)
    1030             :                 return;
    1031             : 
    1032           0 :         core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
    1033             : 
    1034             :         /* Calculate nominal field rate for stream */
    1035           0 :         nominal_field_rate_in_uhz =
    1036             :                         mod_freesync_calc_nominal_field_rate(stream);
    1037             : 
    1038           0 :         min_refresh_in_uhz = in_config->min_refresh_in_uhz;
    1039           0 :         max_refresh_in_uhz = in_config->max_refresh_in_uhz;
    1040             : 
    1041             :         /* Full range may be larger than current video timing, so cap at nominal */
    1042           0 :         if (max_refresh_in_uhz > nominal_field_rate_in_uhz)
    1043           0 :                 max_refresh_in_uhz = nominal_field_rate_in_uhz;
    1044             : 
    1045             :         /* Full range may be larger than current video timing, so cap at nominal */
    1046           0 :         if (min_refresh_in_uhz > max_refresh_in_uhz)
    1047           0 :                 min_refresh_in_uhz = max_refresh_in_uhz;
    1048             : 
    1049             :         /* If a monitor reports exactly max refresh of 2x of min, enforce it on nominal */
    1050           0 :         rounded_nominal_in_uhz =
    1051           0 :                         div_u64(nominal_field_rate_in_uhz + 50000, 100000) * 100000;
    1052           0 :         if (in_config->max_refresh_in_uhz == (2 * in_config->min_refresh_in_uhz) &&
    1053             :                 in_config->max_refresh_in_uhz == rounded_nominal_in_uhz)
    1054           0 :                 min_refresh_in_uhz = div_u64(nominal_field_rate_in_uhz, 2);
    1055             : 
    1056           0 :         if (!vrr_settings_require_update(core_freesync,
    1057             :                         in_config, (unsigned int)min_refresh_in_uhz, (unsigned int)max_refresh_in_uhz,
    1058             :                         in_out_vrr))
    1059             :                 return;
    1060             : 
    1061           0 :         in_out_vrr->state = in_config->state;
    1062           0 :         in_out_vrr->send_info_frame = in_config->vsif_supported;
    1063             : 
    1064           0 :         if (in_config->state == VRR_STATE_UNSUPPORTED) {
    1065           0 :                 in_out_vrr->state = VRR_STATE_UNSUPPORTED;
    1066           0 :                 in_out_vrr->supported = false;
    1067           0 :                 in_out_vrr->adjust.v_total_min = stream->timing.v_total;
    1068           0 :                 in_out_vrr->adjust.v_total_max = stream->timing.v_total;
    1069             : 
    1070           0 :                 return;
    1071             : 
    1072             :         } else {
    1073           0 :                 in_out_vrr->min_refresh_in_uhz = (unsigned int)min_refresh_in_uhz;
    1074           0 :                 in_out_vrr->max_duration_in_us =
    1075           0 :                                 calc_duration_in_us_from_refresh_in_uhz(
    1076             :                                                 (unsigned int)min_refresh_in_uhz);
    1077             : 
    1078           0 :                 in_out_vrr->max_refresh_in_uhz = (unsigned int)max_refresh_in_uhz;
    1079           0 :                 in_out_vrr->min_duration_in_us =
    1080           0 :                                 calc_duration_in_us_from_refresh_in_uhz(
    1081             :                                                 (unsigned int)max_refresh_in_uhz);
    1082             : 
    1083           0 :                 if (in_config->state == VRR_STATE_ACTIVE_FIXED)
    1084           0 :                         in_out_vrr->fixed_refresh_in_uhz = in_config->fixed_refresh_in_uhz;
    1085             :                 else
    1086           0 :                         in_out_vrr->fixed_refresh_in_uhz = 0;
    1087             : 
    1088           0 :                 refresh_range = div_u64(in_out_vrr->max_refresh_in_uhz + 500000, 1000000) -
    1089           0 : +                               div_u64(in_out_vrr->min_refresh_in_uhz + 500000, 1000000);
    1090             : 
    1091           0 :                 in_out_vrr->supported = true;
    1092             :         }
    1093             : 
    1094           0 :         in_out_vrr->fixed.ramping_active = in_config->ramping;
    1095             : 
    1096           0 :         in_out_vrr->btr.btr_enabled = in_config->btr;
    1097             : 
    1098           0 :         if (in_out_vrr->max_refresh_in_uhz < (2 * in_out_vrr->min_refresh_in_uhz))
    1099           0 :                 in_out_vrr->btr.btr_enabled = false;
    1100             :         else {
    1101           0 :                 in_out_vrr->btr.margin_in_us = in_out_vrr->max_duration_in_us -
    1102           0 :                                 2 * in_out_vrr->min_duration_in_us;
    1103           0 :                 if (in_out_vrr->btr.margin_in_us > BTR_MAX_MARGIN)
    1104           0 :                         in_out_vrr->btr.margin_in_us = BTR_MAX_MARGIN;
    1105             :         }
    1106             : 
    1107           0 :         in_out_vrr->btr.btr_active = false;
    1108           0 :         in_out_vrr->btr.inserted_duration_in_us = 0;
    1109           0 :         in_out_vrr->btr.frames_to_insert = 0;
    1110           0 :         in_out_vrr->btr.frame_counter = 0;
    1111           0 :         in_out_vrr->fixed.fixed_active = false;
    1112           0 :         in_out_vrr->fixed.target_refresh_in_uhz = 0;
    1113             : 
    1114           0 :         in_out_vrr->btr.mid_point_in_us =
    1115           0 :                                 (in_out_vrr->min_duration_in_us +
    1116           0 :                                  in_out_vrr->max_duration_in_us) / 2;
    1117             : 
    1118           0 :         if (in_out_vrr->state == VRR_STATE_UNSUPPORTED) {
    1119           0 :                 in_out_vrr->adjust.v_total_min = stream->timing.v_total;
    1120           0 :                 in_out_vrr->adjust.v_total_max = stream->timing.v_total;
    1121           0 :         } else if (in_out_vrr->state == VRR_STATE_DISABLED) {
    1122           0 :                 in_out_vrr->adjust.v_total_min = stream->timing.v_total;
    1123           0 :                 in_out_vrr->adjust.v_total_max = stream->timing.v_total;
    1124           0 :         } else if (in_out_vrr->state == VRR_STATE_INACTIVE) {
    1125           0 :                 in_out_vrr->adjust.v_total_min = stream->timing.v_total;
    1126           0 :                 in_out_vrr->adjust.v_total_max = stream->timing.v_total;
    1127           0 :         } else if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE &&
    1128             :                         refresh_range >= MIN_REFRESH_RANGE) {
    1129             : 
    1130           0 :                 in_out_vrr->adjust.v_total_min =
    1131           0 :                         mod_freesync_calc_v_total_from_refresh(stream,
    1132             :                                 in_out_vrr->max_refresh_in_uhz);
    1133           0 :                 in_out_vrr->adjust.v_total_max =
    1134           0 :                         mod_freesync_calc_v_total_from_refresh(stream,
    1135             :                                 in_out_vrr->min_refresh_in_uhz);
    1136           0 :         } else if (in_out_vrr->state == VRR_STATE_ACTIVE_FIXED) {
    1137           0 :                 in_out_vrr->fixed.target_refresh_in_uhz =
    1138           0 :                                 in_out_vrr->fixed_refresh_in_uhz;
    1139             :                 if (in_out_vrr->fixed.ramping_active &&
    1140             :                                 in_out_vrr->fixed.fixed_active) {
    1141             :                         /* Do not update vtotals if ramping is already active
    1142             :                          * in order to continue ramp from current refresh.
    1143             :                          */
    1144             :                         in_out_vrr->fixed.fixed_active = true;
    1145             :                 } else {
    1146           0 :                         in_out_vrr->fixed.fixed_active = true;
    1147           0 :                         in_out_vrr->adjust.v_total_min =
    1148           0 :                                 mod_freesync_calc_v_total_from_refresh(stream,
    1149             :                                         in_out_vrr->fixed.target_refresh_in_uhz);
    1150           0 :                         in_out_vrr->adjust.v_total_max =
    1151             :                                 in_out_vrr->adjust.v_total_min;
    1152             :                 }
    1153             :         } else {
    1154           0 :                 in_out_vrr->state = VRR_STATE_INACTIVE;
    1155           0 :                 in_out_vrr->adjust.v_total_min = stream->timing.v_total;
    1156           0 :                 in_out_vrr->adjust.v_total_max = stream->timing.v_total;
    1157             :         }
    1158             : }
    1159             : 
    1160           0 : void mod_freesync_handle_preflip(struct mod_freesync *mod_freesync,
    1161             :                 const struct dc_plane_state *plane,
    1162             :                 const struct dc_stream_state *stream,
    1163             :                 unsigned int curr_time_stamp_in_us,
    1164             :                 struct mod_vrr_params *in_out_vrr)
    1165             : {
    1166           0 :         struct core_freesync *core_freesync = NULL;
    1167           0 :         unsigned int last_render_time_in_us = 0;
    1168           0 :         unsigned int average_render_time_in_us = 0;
    1169             : 
    1170           0 :         if (mod_freesync == NULL)
    1171             :                 return;
    1172             : 
    1173           0 :         core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
    1174             : 
    1175           0 :         if (in_out_vrr->supported &&
    1176           0 :                         in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE) {
    1177           0 :                 unsigned int i = 0;
    1178           0 :                 unsigned int oldest_index = plane->time.index + 1;
    1179             : 
    1180             :                 if (oldest_index >= DC_PLANE_UPDATE_TIMES_MAX)
    1181             :                         oldest_index = 0;
    1182             : 
    1183           0 :                 last_render_time_in_us = curr_time_stamp_in_us -
    1184           0 :                                 plane->time.prev_update_time_in_us;
    1185             : 
    1186             :                 /* Sum off all entries except oldest one */
    1187           0 :                 for (i = 0; i < DC_PLANE_UPDATE_TIMES_MAX; i++) {
    1188             :                         average_render_time_in_us +=
    1189             :                                         plane->time.time_elapsed_in_us[i];
    1190             :                 }
    1191           0 :                 average_render_time_in_us -=
    1192           0 :                                 plane->time.time_elapsed_in_us[oldest_index];
    1193             : 
    1194             :                 /* Add render time for current flip */
    1195           0 :                 average_render_time_in_us += last_render_time_in_us;
    1196           0 :                 average_render_time_in_us /= DC_PLANE_UPDATE_TIMES_MAX;
    1197             : 
    1198           0 :                 if (in_out_vrr->btr.btr_enabled) {
    1199           0 :                         apply_below_the_range(core_freesync,
    1200             :                                         stream,
    1201             :                                         last_render_time_in_us,
    1202             :                                         in_out_vrr);
    1203             :                 } else {
    1204           0 :                         apply_fixed_refresh(core_freesync,
    1205             :                                 stream,
    1206             :                                 last_render_time_in_us,
    1207             :                                 in_out_vrr);
    1208             :                 }
    1209             : 
    1210           0 :                 determine_flip_interval_workaround_req(in_out_vrr,
    1211             :                                 curr_time_stamp_in_us);
    1212             : 
    1213             :         }
    1214             : }
    1215             : 
    1216           0 : void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync,
    1217             :                 const struct dc_stream_state *stream,
    1218             :                 struct mod_vrr_params *in_out_vrr)
    1219             : {
    1220           0 :         struct core_freesync *core_freesync = NULL;
    1221             :         unsigned int cur_timestamp_in_us;
    1222             :         unsigned long long cur_tick;
    1223             : 
    1224           0 :         if ((mod_freesync == NULL) || (stream == NULL) || (in_out_vrr == NULL))
    1225             :                 return;
    1226             : 
    1227           0 :         core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
    1228             : 
    1229           0 :         if (in_out_vrr->supported == false)
    1230             :                 return;
    1231             : 
    1232           0 :         cur_tick = dm_get_timestamp(core_freesync->dc->ctx);
    1233           0 :         cur_timestamp_in_us = (unsigned int)
    1234           0 :                         div_u64(dm_get_elapse_time_in_ns(core_freesync->dc->ctx, cur_tick, 0), 1000);
    1235             : 
    1236           0 :         in_out_vrr->flip_interval.vsyncs_between_flip++;
    1237           0 :         in_out_vrr->flip_interval.v_update_timestamp_in_us = cur_timestamp_in_us;
    1238             : 
    1239           0 :         if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE &&
    1240           0 :                         (in_out_vrr->flip_interval.flip_interval_workaround_active ||
    1241           0 :                         (!in_out_vrr->flip_interval.flip_interval_workaround_active &&
    1242           0 :                         in_out_vrr->flip_interval.program_flip_interval_workaround))) {
    1243             :                 // set freesync vmin vmax to nominal for workaround
    1244           0 :                 in_out_vrr->adjust.v_total_min =
    1245           0 :                         mod_freesync_calc_v_total_from_refresh(
    1246             :                         stream, in_out_vrr->max_refresh_in_uhz);
    1247           0 :                 in_out_vrr->adjust.v_total_max =
    1248             :                                 in_out_vrr->adjust.v_total_min;
    1249           0 :                 in_out_vrr->flip_interval.program_flip_interval_workaround = false;
    1250           0 :                 in_out_vrr->flip_interval.do_flip_interval_workaround_cleanup = true;
    1251           0 :                 return;
    1252             :         }
    1253             : 
    1254           0 :         if (in_out_vrr->state != VRR_STATE_ACTIVE_VARIABLE &&
    1255           0 :                         in_out_vrr->flip_interval.do_flip_interval_workaround_cleanup) {
    1256           0 :                 in_out_vrr->flip_interval.do_flip_interval_workaround_cleanup = false;
    1257           0 :                 in_out_vrr->flip_interval.flip_interval_detect_counter = 0;
    1258           0 :                 in_out_vrr->flip_interval.vsyncs_between_flip = 0;
    1259           0 :                 in_out_vrr->flip_interval.vsync_to_flip_in_us = 0;
    1260             :         }
    1261             : 
    1262             :         /* Below the Range Logic */
    1263             : 
    1264             :         /* Only execute if in fullscreen mode */
    1265           0 :         if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE &&
    1266           0 :                                         in_out_vrr->btr.btr_active) {
    1267             :                 /* TODO: pass in flag for Pre-DCE12 ASIC
    1268             :                  * in order for frame variable duration to take affect,
    1269             :                  * it needs to be done one VSYNC early, which is at
    1270             :                  * frameCounter == 1.
    1271             :                  * For DCE12 and newer updates to V_TOTAL_MIN/MAX
    1272             :                  * will take affect on current frame
    1273             :                  */
    1274           0 :                 if (in_out_vrr->btr.frames_to_insert ==
    1275           0 :                                 in_out_vrr->btr.frame_counter) {
    1276           0 :                         in_out_vrr->adjust.v_total_min =
    1277           0 :                                 calc_v_total_from_duration(stream,
    1278             :                                 in_out_vrr,
    1279             :                                 in_out_vrr->btr.inserted_duration_in_us);
    1280           0 :                         in_out_vrr->adjust.v_total_max =
    1281             :                                 in_out_vrr->adjust.v_total_min;
    1282             :                 }
    1283             : 
    1284           0 :                 if (in_out_vrr->btr.frame_counter > 0)
    1285           0 :                         in_out_vrr->btr.frame_counter--;
    1286             : 
    1287             :                 /* Restore FreeSync */
    1288           0 :                 if (in_out_vrr->btr.frame_counter == 0) {
    1289           0 :                         in_out_vrr->adjust.v_total_min =
    1290           0 :                                 mod_freesync_calc_v_total_from_refresh(stream,
    1291             :                                 in_out_vrr->max_refresh_in_uhz);
    1292           0 :                         in_out_vrr->adjust.v_total_max =
    1293           0 :                                 mod_freesync_calc_v_total_from_refresh(stream,
    1294             :                                 in_out_vrr->min_refresh_in_uhz);
    1295             :                 }
    1296             :         }
    1297             : 
    1298             :         /* If in fullscreen freesync mode or in video, do not program
    1299             :          * static screen ramp values
    1300             :          */
    1301           0 :         if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE)
    1302           0 :                 in_out_vrr->fixed.ramping_active = false;
    1303             : 
    1304             :         /* Gradual Static Screen Ramping Logic
    1305             :          * Execute if ramp is active and user enabled freesync static screen
    1306             :          */
    1307           0 :         if (in_out_vrr->state == VRR_STATE_ACTIVE_FIXED &&
    1308           0 :                                 in_out_vrr->fixed.ramping_active) {
    1309           0 :                 update_v_total_for_static_ramp(
    1310             :                                 core_freesync, stream, in_out_vrr);
    1311             :         }
    1312             : }
    1313             : 
    1314           0 : void mod_freesync_get_settings(struct mod_freesync *mod_freesync,
    1315             :                 const struct mod_vrr_params *vrr,
    1316             :                 unsigned int *v_total_min, unsigned int *v_total_max,
    1317             :                 unsigned int *event_triggers,
    1318             :                 unsigned int *window_min, unsigned int *window_max,
    1319             :                 unsigned int *lfc_mid_point_in_us,
    1320             :                 unsigned int *inserted_frames,
    1321             :                 unsigned int *inserted_duration_in_us)
    1322             : {
    1323           0 :         if (mod_freesync == NULL)
    1324             :                 return;
    1325             : 
    1326           0 :         if (vrr->supported) {
    1327           0 :                 *v_total_min = vrr->adjust.v_total_min;
    1328           0 :                 *v_total_max = vrr->adjust.v_total_max;
    1329           0 :                 *event_triggers = 0;
    1330           0 :                 *lfc_mid_point_in_us = vrr->btr.mid_point_in_us;
    1331           0 :                 *inserted_frames = vrr->btr.frames_to_insert;
    1332           0 :                 *inserted_duration_in_us = vrr->btr.inserted_duration_in_us;
    1333             :         }
    1334             : }
    1335             : 
    1336           0 : unsigned long long mod_freesync_calc_nominal_field_rate(
    1337             :                         const struct dc_stream_state *stream)
    1338             : {
    1339           0 :         unsigned long long nominal_field_rate_in_uhz = 0;
    1340           0 :         unsigned int total = stream->timing.h_total * stream->timing.v_total;
    1341             : 
    1342             :         /* Calculate nominal field rate for stream, rounded up to nearest integer */
    1343           0 :         nominal_field_rate_in_uhz = stream->timing.pix_clk_100hz;
    1344           0 :         nominal_field_rate_in_uhz *= 100000000ULL;
    1345             : 
    1346           0 :         nominal_field_rate_in_uhz =     div_u64(nominal_field_rate_in_uhz, total);
    1347             : 
    1348           0 :         return nominal_field_rate_in_uhz;
    1349             : }
    1350             : 
    1351           0 : unsigned long long mod_freesync_calc_field_rate_from_timing(
    1352             :                 unsigned int vtotal, unsigned int htotal, unsigned int pix_clk)
    1353             : {
    1354           0 :         unsigned long long field_rate_in_uhz = 0;
    1355           0 :         unsigned int total = htotal * vtotal;
    1356             : 
    1357             :         /* Calculate nominal field rate for stream, rounded up to nearest integer */
    1358           0 :         field_rate_in_uhz = pix_clk;
    1359           0 :         field_rate_in_uhz *= 1000000ULL;
    1360             : 
    1361           0 :         field_rate_in_uhz =     div_u64(field_rate_in_uhz, total);
    1362             : 
    1363           0 :         return field_rate_in_uhz;
    1364             : }
    1365             : 
    1366           0 : bool mod_freesync_get_freesync_enabled(struct mod_vrr_params *pVrr)
    1367             : {
    1368           0 :         return (pVrr->state != VRR_STATE_UNSUPPORTED) && (pVrr->state != VRR_STATE_DISABLED);
    1369             : }
    1370             : 
    1371           0 : bool mod_freesync_is_valid_range(uint32_t min_refresh_cap_in_uhz,
    1372             :                 uint32_t max_refresh_cap_in_uhz,
    1373             :                 uint32_t nominal_field_rate_in_uhz)
    1374             : {
    1375             : 
    1376             :         /* Typically nominal refresh calculated can have some fractional part.
    1377             :          * Allow for some rounding error of actual video timing by taking floor
    1378             :          * of caps and request. Round the nominal refresh rate.
    1379             :          *
    1380             :          * Dividing will convert everything to units in Hz although input
    1381             :          * variable name is in uHz!
    1382             :          *
    1383             :          * Also note, this takes care of rounding error on the nominal refresh
    1384             :          * so by rounding error we only expect it to be off by a small amount,
    1385             :          * such as < 0.1 Hz. i.e. 143.9xxx or 144.1xxx.
    1386             :          *
    1387             :          * Example 1. Caps    Min = 40 Hz, Max = 144 Hz
    1388             :          *            Request Min = 40 Hz, Max = 144 Hz
    1389             :          *                    Nominal = 143.5x Hz rounded to 144 Hz
    1390             :          *            This function should allow this as valid request
    1391             :          *
    1392             :          * Example 2. Caps    Min = 40 Hz, Max = 144 Hz
    1393             :          *            Request Min = 40 Hz, Max = 144 Hz
    1394             :          *                    Nominal = 144.4x Hz rounded to 144 Hz
    1395             :          *            This function should allow this as valid request
    1396             :          *
    1397             :          * Example 3. Caps    Min = 40 Hz, Max = 144 Hz
    1398             :          *            Request Min = 40 Hz, Max = 144 Hz
    1399             :          *                    Nominal = 120.xx Hz rounded to 120 Hz
    1400             :          *            This function should return NOT valid since the requested
    1401             :          *            max is greater than current timing's nominal
    1402             :          *
    1403             :          * Example 4. Caps    Min = 40 Hz, Max = 120 Hz
    1404             :          *            Request Min = 40 Hz, Max = 120 Hz
    1405             :          *                    Nominal = 144.xx Hz rounded to 144 Hz
    1406             :          *            This function should return NOT valid since the nominal
    1407             :          *            is greater than the capability's max refresh
    1408             :          */
    1409           0 :         nominal_field_rate_in_uhz =
    1410           0 :                         div_u64(nominal_field_rate_in_uhz + 500000, 1000000);
    1411           0 :         min_refresh_cap_in_uhz /= 1000000;
    1412           0 :         max_refresh_cap_in_uhz /= 1000000;
    1413             : 
    1414             :         /* Check nominal is within range */
    1415           0 :         if (nominal_field_rate_in_uhz > max_refresh_cap_in_uhz ||
    1416           0 :                 nominal_field_rate_in_uhz < min_refresh_cap_in_uhz)
    1417             :                 return false;
    1418             : 
    1419             :         /* If nominal is less than max, limit the max allowed refresh rate */
    1420           0 :         if (nominal_field_rate_in_uhz < max_refresh_cap_in_uhz)
    1421           0 :                 max_refresh_cap_in_uhz = nominal_field_rate_in_uhz;
    1422             : 
    1423             :         /* Check min is within range */
    1424           0 :         if (min_refresh_cap_in_uhz > max_refresh_cap_in_uhz)
    1425             :                 return false;
    1426             : 
    1427             :         /* For variable range, check for at least 10 Hz range */
    1428           0 :         if (nominal_field_rate_in_uhz - min_refresh_cap_in_uhz < 10)
    1429             :                 return false;
    1430             : 
    1431           0 :         return true;
    1432             : }

Generated by: LCOV version 1.14