2 * Copyright 2016 Advanced Micro Devices, Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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.
26 #include <linux/slab.h>
28 #include "dm_services.h"
30 #include "mod_freesync.h"
31 #include "core_types.h"
33 #define MOD_FREESYNC_MAX_CONCURRENT_STREAMS 32
35 #define MIN_REFRESH_RANGE_IN_US 10000000
36 /* Refresh rate ramp at a fixed rate of 65 Hz/second */
37 #define STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME ((1000 / 60) * 65)
38 /* Number of elements in the render times cache array */
39 #define RENDER_TIMES_MAX_COUNT 10
40 /* Threshold to exit/exit BTR (to avoid frequent enter-exits at the lower limit) */
41 #define BTR_MAX_MARGIN 2500
42 /* Threshold to change BTR multiplier (to avoid frequent changes) */
43 #define BTR_DRIFT_MARGIN 2000
44 /*Threshold to exit fixed refresh rate*/
45 #define FIXED_REFRESH_EXIT_MARGIN_IN_HZ 4
46 /* Number of consecutive frames to check before entering/exiting fixed refresh*/
47 #define FIXED_REFRESH_ENTER_FRAME_COUNT 5
48 #define FIXED_REFRESH_EXIT_FRAME_COUNT 5
50 struct core_freesync
{
51 struct mod_freesync
public;
55 #define MOD_FREESYNC_TO_CORE(mod_freesync)\
56 container_of(mod_freesync, struct core_freesync, public)
58 struct mod_freesync
*mod_freesync_create(struct dc
*dc
)
60 struct core_freesync
*core_freesync
=
61 kzalloc(sizeof(struct core_freesync
), GFP_KERNEL
);
63 if (core_freesync
== NULL
)
64 goto fail_alloc_context
;
69 core_freesync
->dc
= dc
;
70 return &core_freesync
->public;
79 void mod_freesync_destroy(struct mod_freesync
*mod_freesync
)
81 struct core_freesync
*core_freesync
= NULL
;
82 if (mod_freesync
== NULL
)
84 core_freesync
= MOD_FREESYNC_TO_CORE(mod_freesync
);
88 #if 0 /* unused currently */
89 static unsigned int calc_refresh_in_uhz_from_duration(
90 unsigned int duration_in_ns
)
92 unsigned int refresh_in_uhz
=
93 ((unsigned int)(div64_u64((1000000000ULL * 1000000),
95 return refresh_in_uhz
;
99 static unsigned int calc_duration_in_us_from_refresh_in_uhz(
100 unsigned int refresh_in_uhz
)
102 unsigned int duration_in_us
=
103 ((unsigned int)(div64_u64((1000000000ULL * 1000),
105 return duration_in_us
;
108 static unsigned int calc_duration_in_us_from_v_total(
109 const struct dc_stream_state
*stream
,
110 const struct mod_vrr_params
*in_vrr
,
111 unsigned int v_total
)
113 unsigned int duration_in_us
=
114 (unsigned int)(div64_u64(((unsigned long long)(v_total
)
115 * 10000) * stream
->timing
.h_total
,
116 stream
->timing
.pix_clk_100hz
));
118 return duration_in_us
;
121 static unsigned int calc_v_total_from_refresh(
122 const struct dc_stream_state
*stream
,
123 unsigned int refresh_in_uhz
)
125 unsigned int v_total
;
126 unsigned int frame_duration_in_ns
;
128 frame_duration_in_ns
=
129 ((unsigned int)(div64_u64((1000000000ULL * 1000000),
132 v_total
= div64_u64(div64_u64(((unsigned long long)(
133 frame_duration_in_ns
) * (stream
->timing
.pix_clk_100hz
/ 10)),
134 stream
->timing
.h_total
), 1000000);
136 /* v_total cannot be less than nominal */
137 if (v_total
< stream
->timing
.v_total
) {
138 ASSERT(v_total
< stream
->timing
.v_total
);
139 v_total
= stream
->timing
.v_total
;
145 static unsigned int calc_v_total_from_duration(
146 const struct dc_stream_state
*stream
,
147 const struct mod_vrr_params
*vrr
,
148 unsigned int duration_in_us
)
150 unsigned int v_total
= 0;
152 if (duration_in_us
< vrr
->min_duration_in_us
)
153 duration_in_us
= vrr
->min_duration_in_us
;
155 if (duration_in_us
> vrr
->max_duration_in_us
)
156 duration_in_us
= vrr
->max_duration_in_us
;
158 v_total
= div64_u64(div64_u64(((unsigned long long)(
159 duration_in_us
) * (stream
->timing
.pix_clk_100hz
/ 10)),
160 stream
->timing
.h_total
), 1000);
162 /* v_total cannot be less than nominal */
163 if (v_total
< stream
->timing
.v_total
) {
164 ASSERT(v_total
< stream
->timing
.v_total
);
165 v_total
= stream
->timing
.v_total
;
171 static void update_v_total_for_static_ramp(
172 struct core_freesync
*core_freesync
,
173 const struct dc_stream_state
*stream
,
174 struct mod_vrr_params
*in_out_vrr
)
176 unsigned int v_total
= 0;
177 unsigned int current_duration_in_us
=
178 calc_duration_in_us_from_v_total(
180 in_out_vrr
->adjust
.v_total_max
);
181 unsigned int target_duration_in_us
=
182 calc_duration_in_us_from_refresh_in_uhz(
183 in_out_vrr
->fixed
.target_refresh_in_uhz
);
184 bool ramp_direction_is_up
= (current_duration_in_us
>
185 target_duration_in_us
) ? true : false;
187 /* Calc ratio between new and current frame duration with 3 digit */
188 unsigned int frame_duration_ratio
= div64_u64(1000000,
189 (1000 + div64_u64(((unsigned long long)(
190 STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME
) *
191 current_duration_in_us
),
194 /* Calculate delta between new and current frame duration in us */
195 unsigned int frame_duration_delta
= div64_u64(((unsigned long long)(
196 current_duration_in_us
) *
197 (1000 - frame_duration_ratio
)), 1000);
199 /* Adjust frame duration delta based on ratio between current and
200 * standard frame duration (frame duration at 60 Hz refresh rate).
202 unsigned int ramp_rate_interpolated
= div64_u64(((unsigned long long)(
203 frame_duration_delta
) * current_duration_in_us
), 16666);
205 /* Going to a higher refresh rate (lower frame duration) */
206 if (ramp_direction_is_up
) {
207 /* reduce frame duration */
208 current_duration_in_us
-= ramp_rate_interpolated
;
210 /* adjust for frame duration below min */
211 if (current_duration_in_us
<= target_duration_in_us
) {
212 in_out_vrr
->fixed
.ramping_active
= false;
213 in_out_vrr
->fixed
.ramping_done
= true;
214 current_duration_in_us
=
215 calc_duration_in_us_from_refresh_in_uhz(
216 in_out_vrr
->fixed
.target_refresh_in_uhz
);
218 /* Going to a lower refresh rate (larger frame duration) */
220 /* increase frame duration */
221 current_duration_in_us
+= ramp_rate_interpolated
;
223 /* adjust for frame duration above max */
224 if (current_duration_in_us
>= target_duration_in_us
) {
225 in_out_vrr
->fixed
.ramping_active
= false;
226 in_out_vrr
->fixed
.ramping_done
= true;
227 current_duration_in_us
=
228 calc_duration_in_us_from_refresh_in_uhz(
229 in_out_vrr
->fixed
.target_refresh_in_uhz
);
233 v_total
= div64_u64(div64_u64(((unsigned long long)(
234 current_duration_in_us
) * (stream
->timing
.pix_clk_100hz
/ 10)),
235 stream
->timing
.h_total
), 1000);
237 /* v_total cannot be less than nominal */
238 if (v_total
< stream
->timing
.v_total
)
239 v_total
= stream
->timing
.v_total
;
241 in_out_vrr
->adjust
.v_total_min
= v_total
;
242 in_out_vrr
->adjust
.v_total_max
= v_total
;
245 static void apply_below_the_range(struct core_freesync
*core_freesync
,
246 const struct dc_stream_state
*stream
,
247 unsigned int last_render_time_in_us
,
248 struct mod_vrr_params
*in_out_vrr
)
250 unsigned int inserted_frame_duration_in_us
= 0;
251 unsigned int mid_point_frames_ceil
= 0;
252 unsigned int mid_point_frames_floor
= 0;
253 unsigned int frame_time_in_us
= 0;
254 unsigned int delta_from_mid_point_in_us_1
= 0xFFFFFFFF;
255 unsigned int delta_from_mid_point_in_us_2
= 0xFFFFFFFF;
256 unsigned int frames_to_insert
= 0;
257 unsigned int delta_from_mid_point_delta_in_us
;
258 unsigned int max_render_time_in_us
=
259 in_out_vrr
->max_duration_in_us
- in_out_vrr
->btr
.margin_in_us
;
262 if ((last_render_time_in_us
+ in_out_vrr
->btr
.margin_in_us
/ 2) < max_render_time_in_us
) {
263 /* Exit Below the Range */
264 if (in_out_vrr
->btr
.btr_active
) {
265 in_out_vrr
->btr
.frame_counter
= 0;
266 in_out_vrr
->btr
.btr_active
= false;
268 } else if (last_render_time_in_us
> (max_render_time_in_us
+ in_out_vrr
->btr
.margin_in_us
/ 2)) {
269 /* Enter Below the Range */
270 if (!in_out_vrr
->btr
.btr_active
) {
271 in_out_vrr
->btr
.btr_active
= true;
275 /* BTR set to "not active" so disengage */
276 if (!in_out_vrr
->btr
.btr_active
) {
277 in_out_vrr
->btr
.inserted_duration_in_us
= 0;
278 in_out_vrr
->btr
.frames_to_insert
= 0;
279 in_out_vrr
->btr
.frame_counter
= 0;
281 /* Restore FreeSync */
282 in_out_vrr
->adjust
.v_total_min
=
283 calc_v_total_from_refresh(stream
,
284 in_out_vrr
->max_refresh_in_uhz
);
285 in_out_vrr
->adjust
.v_total_max
=
286 calc_v_total_from_refresh(stream
,
287 in_out_vrr
->min_refresh_in_uhz
);
288 /* BTR set to "active" so engage */
291 /* Calculate number of midPoint frames that could fit within
292 * the render time interval- take ceil of this value
294 mid_point_frames_ceil
= (last_render_time_in_us
+
295 in_out_vrr
->btr
.mid_point_in_us
- 1) /
296 in_out_vrr
->btr
.mid_point_in_us
;
298 if (mid_point_frames_ceil
> 0) {
299 frame_time_in_us
= last_render_time_in_us
/
300 mid_point_frames_ceil
;
301 delta_from_mid_point_in_us_1
=
302 (in_out_vrr
->btr
.mid_point_in_us
>
304 (in_out_vrr
->btr
.mid_point_in_us
- frame_time_in_us
) :
305 (frame_time_in_us
- in_out_vrr
->btr
.mid_point_in_us
);
308 /* Calculate number of midPoint frames that could fit within
309 * the render time interval- take floor of this value
311 mid_point_frames_floor
= last_render_time_in_us
/
312 in_out_vrr
->btr
.mid_point_in_us
;
314 if (mid_point_frames_floor
> 0) {
316 frame_time_in_us
= last_render_time_in_us
/
317 mid_point_frames_floor
;
318 delta_from_mid_point_in_us_2
=
319 (in_out_vrr
->btr
.mid_point_in_us
>
321 (in_out_vrr
->btr
.mid_point_in_us
- frame_time_in_us
) :
322 (frame_time_in_us
- in_out_vrr
->btr
.mid_point_in_us
);
325 /* Choose number of frames to insert based on how close it
326 * can get to the mid point of the variable range.
328 if ((frame_time_in_us
/ mid_point_frames_ceil
) > in_out_vrr
->min_duration_in_us
&&
329 (delta_from_mid_point_in_us_1
< delta_from_mid_point_in_us_2
||
330 mid_point_frames_floor
< 2)) {
331 frames_to_insert
= mid_point_frames_ceil
;
332 delta_from_mid_point_delta_in_us
= delta_from_mid_point_in_us_2
-
333 delta_from_mid_point_in_us_1
;
335 frames_to_insert
= mid_point_frames_floor
;
336 delta_from_mid_point_delta_in_us
= delta_from_mid_point_in_us_1
-
337 delta_from_mid_point_in_us_2
;
340 /* Prefer current frame multiplier when BTR is enabled unless it drifts
341 * too far from the midpoint
343 if (in_out_vrr
->btr
.frames_to_insert
!= 0 &&
344 delta_from_mid_point_delta_in_us
< BTR_DRIFT_MARGIN
) {
345 if (((last_render_time_in_us
/ in_out_vrr
->btr
.frames_to_insert
) <
346 max_render_time_in_us
) &&
347 ((last_render_time_in_us
/ in_out_vrr
->btr
.frames_to_insert
) >
348 in_out_vrr
->min_duration_in_us
))
349 frames_to_insert
= in_out_vrr
->btr
.frames_to_insert
;
352 /* Either we've calculated the number of frames to insert,
353 * or we need to insert min duration frames
355 if (last_render_time_in_us
/ frames_to_insert
<
356 in_out_vrr
->min_duration_in_us
){
357 frames_to_insert
-= (frames_to_insert
> 1) ?
361 if (frames_to_insert
> 0)
362 inserted_frame_duration_in_us
= last_render_time_in_us
/
365 if (inserted_frame_duration_in_us
< in_out_vrr
->min_duration_in_us
)
366 inserted_frame_duration_in_us
= in_out_vrr
->min_duration_in_us
;
368 /* Cache the calculated variables */
369 in_out_vrr
->btr
.inserted_duration_in_us
=
370 inserted_frame_duration_in_us
;
371 in_out_vrr
->btr
.frames_to_insert
= frames_to_insert
;
372 in_out_vrr
->btr
.frame_counter
= frames_to_insert
;
376 static void apply_fixed_refresh(struct core_freesync
*core_freesync
,
377 const struct dc_stream_state
*stream
,
378 unsigned int last_render_time_in_us
,
379 struct mod_vrr_params
*in_out_vrr
)
382 unsigned int max_render_time_in_us
= in_out_vrr
->max_duration_in_us
;
384 //Compute the exit refresh rate and exit frame duration
385 unsigned int exit_refresh_rate_in_milli_hz
= ((1000000000/max_render_time_in_us
)
386 + (1000*FIXED_REFRESH_EXIT_MARGIN_IN_HZ
));
387 unsigned int exit_frame_duration_in_us
= 1000000000/exit_refresh_rate_in_milli_hz
;
389 if (last_render_time_in_us
< exit_frame_duration_in_us
) {
390 /* Exit Fixed Refresh mode */
391 if (in_out_vrr
->fixed
.fixed_active
) {
392 in_out_vrr
->fixed
.frame_counter
++;
394 if (in_out_vrr
->fixed
.frame_counter
>
395 FIXED_REFRESH_EXIT_FRAME_COUNT
) {
396 in_out_vrr
->fixed
.frame_counter
= 0;
397 in_out_vrr
->fixed
.fixed_active
= false;
398 in_out_vrr
->fixed
.target_refresh_in_uhz
= 0;
402 } else if (last_render_time_in_us
> max_render_time_in_us
) {
403 /* Enter Fixed Refresh mode */
404 if (!in_out_vrr
->fixed
.fixed_active
) {
405 in_out_vrr
->fixed
.frame_counter
++;
407 if (in_out_vrr
->fixed
.frame_counter
>
408 FIXED_REFRESH_ENTER_FRAME_COUNT
) {
409 in_out_vrr
->fixed
.frame_counter
= 0;
410 in_out_vrr
->fixed
.fixed_active
= true;
411 in_out_vrr
->fixed
.target_refresh_in_uhz
=
412 in_out_vrr
->max_refresh_in_uhz
;
419 if (in_out_vrr
->fixed
.fixed_active
) {
420 in_out_vrr
->adjust
.v_total_min
=
421 calc_v_total_from_refresh(
422 stream
, in_out_vrr
->max_refresh_in_uhz
);
423 in_out_vrr
->adjust
.v_total_max
=
424 in_out_vrr
->adjust
.v_total_min
;
426 in_out_vrr
->adjust
.v_total_min
=
427 calc_v_total_from_refresh(stream
,
428 in_out_vrr
->max_refresh_in_uhz
);
429 in_out_vrr
->adjust
.v_total_max
=
430 calc_v_total_from_refresh(stream
,
431 in_out_vrr
->min_refresh_in_uhz
);
436 static bool vrr_settings_require_update(struct core_freesync
*core_freesync
,
437 struct mod_freesync_config
*in_config
,
438 unsigned int min_refresh_in_uhz
,
439 unsigned int max_refresh_in_uhz
,
440 struct mod_vrr_params
*in_vrr
)
442 if (in_vrr
->state
!= in_config
->state
) {
444 } else if (in_vrr
->state
== VRR_STATE_ACTIVE_FIXED
&&
445 in_vrr
->fixed
.target_refresh_in_uhz
!=
446 in_config
->min_refresh_in_uhz
) {
448 } else if (in_vrr
->min_refresh_in_uhz
!= min_refresh_in_uhz
) {
450 } else if (in_vrr
->max_refresh_in_uhz
!= max_refresh_in_uhz
) {
457 bool mod_freesync_get_vmin_vmax(struct mod_freesync
*mod_freesync
,
458 const struct dc_stream_state
*stream
,
462 *vmin
= stream
->adjust
.v_total_min
;
463 *vmax
= stream
->adjust
.v_total_max
;
468 bool mod_freesync_get_v_position(struct mod_freesync
*mod_freesync
,
469 struct dc_stream_state
*stream
,
470 unsigned int *nom_v_pos
,
473 struct core_freesync
*core_freesync
= NULL
;
474 struct crtc_position position
;
476 if (mod_freesync
== NULL
)
479 core_freesync
= MOD_FREESYNC_TO_CORE(mod_freesync
);
481 if (dc_stream_get_crtc_position(core_freesync
->dc
, &stream
, 1,
482 &position
.vertical_count
,
483 &position
.nominal_vcount
)) {
485 *nom_v_pos
= position
.nominal_vcount
;
486 *v_pos
= position
.vertical_count
;
494 static void build_vrr_infopacket_data(const struct mod_vrr_params
*vrr
,
495 struct dc_info_packet
*infopacket
)
497 /* PB1 = 0x1A (24bit AMD IEEE OUI (0x00001A) - Byte 0) */
498 infopacket
->sb
[1] = 0x1A;
500 /* PB2 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 1) */
501 infopacket
->sb
[2] = 0x00;
503 /* PB3 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 2) */
504 infopacket
->sb
[3] = 0x00;
510 /* PB6 = [Bits 7:3 = Reserved] */
512 /* PB6 = [Bit 0 = FreeSync Supported] */
513 if (vrr
->state
!= VRR_STATE_UNSUPPORTED
)
514 infopacket
->sb
[6] |= 0x01;
516 /* PB6 = [Bit 1 = FreeSync Enabled] */
517 if (vrr
->state
!= VRR_STATE_DISABLED
&&
518 vrr
->state
!= VRR_STATE_UNSUPPORTED
)
519 infopacket
->sb
[6] |= 0x02;
521 /* PB6 = [Bit 2 = FreeSync Active] */
522 if (vrr
->state
== VRR_STATE_ACTIVE_VARIABLE
||
523 vrr
->state
== VRR_STATE_ACTIVE_FIXED
)
524 infopacket
->sb
[6] |= 0x04;
526 /* PB7 = FreeSync Minimum refresh rate (Hz) */
527 infopacket
->sb
[7] = (unsigned char)(vrr
->min_refresh_in_uhz
/ 1000000);
529 /* PB8 = FreeSync Maximum refresh rate (Hz)
530 * Note: We should never go above the field rate of the mode timing set.
532 infopacket
->sb
[8] = (unsigned char)(vrr
->max_refresh_in_uhz
/ 1000000);
536 infopacket
->sb
[9] = 0;
537 infopacket
->sb
[10] = 0;
540 static void build_vrr_infopacket_fs2_data(enum color_transfer_func app_tf
,
541 struct dc_info_packet
*infopacket
)
543 if (app_tf
!= TRANSFER_FUNC_UNKNOWN
) {
544 infopacket
->valid
= true;
546 infopacket
->sb
[6] |= 0x08; // PB6 = [Bit 3 = Native Color Active]
548 if (app_tf
== TRANSFER_FUNC_GAMMA_22
) {
549 infopacket
->sb
[9] |= 0x04; // PB6 = [Bit 2 = Gamma 2.2 EOTF Active]
554 static void build_vrr_infopacket_header_v1(enum signal_type signal
,
555 struct dc_info_packet
*infopacket
,
556 unsigned int *payload_size
)
558 if (dc_is_hdmi_signal(signal
)) {
562 /* HB0 = Packet Type = 0x83 (Source Product
563 * Descriptor InfoFrame)
565 infopacket
->hb0
= DC_HDMI_INFOFRAME_TYPE_SPD
;
567 /* HB1 = Version = 0x01 */
568 infopacket
->hb1
= 0x01;
570 /* HB2 = [Bits 7:5 = 0] [Bits 4:0 = Length = 0x08] */
571 infopacket
->hb2
= 0x08;
573 *payload_size
= 0x08;
575 } else if (dc_is_dp_signal(signal
)) {
579 /* HB0 = Secondary-data Packet ID = 0 - Only non-zero
580 * when used to associate audio related info packets
582 infopacket
->hb0
= 0x00;
584 /* HB1 = Packet Type = 0x83 (Source Product
585 * Descriptor InfoFrame)
587 infopacket
->hb1
= DC_HDMI_INFOFRAME_TYPE_SPD
;
589 /* HB2 = [Bits 7:0 = Least significant eight bits -
590 * For INFOFRAME, the value must be 1Bh]
592 infopacket
->hb2
= 0x1B;
594 /* HB3 = [Bits 7:2 = INFOFRAME SDP Version Number = 0x1]
595 * [Bits 1:0 = Most significant two bits = 0x00]
597 infopacket
->hb3
= 0x04;
599 *payload_size
= 0x1B;
603 static void build_vrr_infopacket_header_v2(enum signal_type signal
,
604 struct dc_info_packet
*infopacket
,
605 unsigned int *payload_size
)
607 if (dc_is_hdmi_signal(signal
)) {
611 /* HB0 = Packet Type = 0x83 (Source Product
612 * Descriptor InfoFrame)
614 infopacket
->hb0
= DC_HDMI_INFOFRAME_TYPE_SPD
;
616 /* HB1 = Version = 0x02 */
617 infopacket
->hb1
= 0x02;
619 /* HB2 = [Bits 7:5 = 0] [Bits 4:0 = Length = 0x09] */
620 infopacket
->hb2
= 0x09;
622 *payload_size
= 0x0A;
624 } else if (dc_is_dp_signal(signal
)) {
628 /* HB0 = Secondary-data Packet ID = 0 - Only non-zero
629 * when used to associate audio related info packets
631 infopacket
->hb0
= 0x00;
633 /* HB1 = Packet Type = 0x83 (Source Product
634 * Descriptor InfoFrame)
636 infopacket
->hb1
= DC_HDMI_INFOFRAME_TYPE_SPD
;
638 /* HB2 = [Bits 7:0 = Least significant eight bits -
639 * For INFOFRAME, the value must be 1Bh]
641 infopacket
->hb2
= 0x1B;
643 /* HB3 = [Bits 7:2 = INFOFRAME SDP Version Number = 0x2]
644 * [Bits 1:0 = Most significant two bits = 0x00]
646 infopacket
->hb3
= 0x08;
648 *payload_size
= 0x1B;
652 static void build_vrr_infopacket_checksum(unsigned int *payload_size
,
653 struct dc_info_packet
*infopacket
)
655 /* Calculate checksum */
656 unsigned int idx
= 0;
657 unsigned char checksum
= 0;
659 checksum
+= infopacket
->hb0
;
660 checksum
+= infopacket
->hb1
;
661 checksum
+= infopacket
->hb2
;
662 checksum
+= infopacket
->hb3
;
664 for (idx
= 1; idx
<= *payload_size
; idx
++)
665 checksum
+= infopacket
->sb
[idx
];
667 /* PB0 = Checksum (one byte complement) */
668 infopacket
->sb
[0] = (unsigned char)(0x100 - checksum
);
670 infopacket
->valid
= true;
673 static void build_vrr_infopacket_v1(enum signal_type signal
,
674 const struct mod_vrr_params
*vrr
,
675 struct dc_info_packet
*infopacket
)
677 /* SPD info packet for FreeSync */
678 unsigned int payload_size
= 0;
680 build_vrr_infopacket_header_v1(signal
, infopacket
, &payload_size
);
681 build_vrr_infopacket_data(vrr
, infopacket
);
682 build_vrr_infopacket_checksum(&payload_size
, infopacket
);
684 infopacket
->valid
= true;
687 static void build_vrr_infopacket_v2(enum signal_type signal
,
688 const struct mod_vrr_params
*vrr
,
689 enum color_transfer_func app_tf
,
690 struct dc_info_packet
*infopacket
)
692 unsigned int payload_size
= 0;
694 build_vrr_infopacket_header_v2(signal
, infopacket
, &payload_size
);
695 build_vrr_infopacket_data(vrr
, infopacket
);
697 build_vrr_infopacket_fs2_data(app_tf
, infopacket
);
699 build_vrr_infopacket_checksum(&payload_size
, infopacket
);
701 infopacket
->valid
= true;
704 void mod_freesync_build_vrr_infopacket(struct mod_freesync
*mod_freesync
,
705 const struct dc_stream_state
*stream
,
706 const struct mod_vrr_params
*vrr
,
707 enum vrr_packet_type packet_type
,
708 enum color_transfer_func app_tf
,
709 struct dc_info_packet
*infopacket
)
711 /* SPD info packet for FreeSync
712 * VTEM info packet for HdmiVRR
713 * Check if Freesync is supported. Return if false. If true,
714 * set the corresponding bit in the info packet
716 if (!vrr
->supported
|| (!vrr
->send_info_frame
))
719 switch (packet_type
) {
720 case PACKET_TYPE_FS2
:
721 build_vrr_infopacket_v2(stream
->signal
, vrr
, app_tf
, infopacket
);
723 case PACKET_TYPE_VRR
:
724 case PACKET_TYPE_FS1
:
726 build_vrr_infopacket_v1(stream
->signal
, vrr
, infopacket
);
730 void mod_freesync_build_vrr_params(struct mod_freesync
*mod_freesync
,
731 const struct dc_stream_state
*stream
,
732 struct mod_freesync_config
*in_config
,
733 struct mod_vrr_params
*in_out_vrr
)
735 struct core_freesync
*core_freesync
= NULL
;
736 unsigned long long nominal_field_rate_in_uhz
= 0;
737 unsigned int refresh_range
= 0;
738 unsigned long long min_refresh_in_uhz
= 0;
739 unsigned long long max_refresh_in_uhz
= 0;
741 if (mod_freesync
== NULL
)
744 core_freesync
= MOD_FREESYNC_TO_CORE(mod_freesync
);
746 /* Calculate nominal field rate for stream */
747 nominal_field_rate_in_uhz
=
748 mod_freesync_calc_nominal_field_rate(stream
);
750 /* Rounded to the nearest Hz */
751 nominal_field_rate_in_uhz
= 1000000ULL *
752 div_u64(nominal_field_rate_in_uhz
+ 500000, 1000000);
754 min_refresh_in_uhz
= in_config
->min_refresh_in_uhz
;
755 max_refresh_in_uhz
= in_config
->max_refresh_in_uhz
;
757 // Don't allow min > max
758 if (min_refresh_in_uhz
> max_refresh_in_uhz
)
759 min_refresh_in_uhz
= max_refresh_in_uhz
;
761 // Full range may be larger than current video timing, so cap at nominal
762 if (max_refresh_in_uhz
> nominal_field_rate_in_uhz
)
763 max_refresh_in_uhz
= nominal_field_rate_in_uhz
;
765 // Full range may be larger than current video timing, so cap at nominal
766 if (min_refresh_in_uhz
> nominal_field_rate_in_uhz
)
767 min_refresh_in_uhz
= nominal_field_rate_in_uhz
;
769 if (!vrr_settings_require_update(core_freesync
,
770 in_config
, (unsigned int)min_refresh_in_uhz
, (unsigned int)max_refresh_in_uhz
,
774 in_out_vrr
->state
= in_config
->state
;
775 in_out_vrr
->send_info_frame
= in_config
->vsif_supported
;
777 if (in_config
->state
== VRR_STATE_UNSUPPORTED
) {
778 in_out_vrr
->state
= VRR_STATE_UNSUPPORTED
;
779 in_out_vrr
->supported
= false;
780 in_out_vrr
->adjust
.v_total_min
= stream
->timing
.v_total
;
781 in_out_vrr
->adjust
.v_total_max
= stream
->timing
.v_total
;
786 in_out_vrr
->min_refresh_in_uhz
= (unsigned int)min_refresh_in_uhz
;
787 in_out_vrr
->max_duration_in_us
=
788 calc_duration_in_us_from_refresh_in_uhz(
789 (unsigned int)min_refresh_in_uhz
);
791 in_out_vrr
->max_refresh_in_uhz
= (unsigned int)max_refresh_in_uhz
;
792 in_out_vrr
->min_duration_in_us
=
793 calc_duration_in_us_from_refresh_in_uhz(
794 (unsigned int)max_refresh_in_uhz
);
796 refresh_range
= in_out_vrr
->max_refresh_in_uhz
-
797 in_out_vrr
->min_refresh_in_uhz
;
799 in_out_vrr
->btr
.margin_in_us
= in_out_vrr
->max_duration_in_us
-
800 2 * in_out_vrr
->min_duration_in_us
;
801 if (in_out_vrr
->btr
.margin_in_us
> BTR_MAX_MARGIN
)
802 in_out_vrr
->btr
.margin_in_us
= BTR_MAX_MARGIN
;
804 in_out_vrr
->supported
= true;
807 in_out_vrr
->fixed
.ramping_active
= in_config
->ramping
;
809 in_out_vrr
->btr
.btr_enabled
= in_config
->btr
;
811 if (in_out_vrr
->max_refresh_in_uhz
<
812 2 * in_out_vrr
->min_refresh_in_uhz
)
813 in_out_vrr
->btr
.btr_enabled
= false;
815 in_out_vrr
->btr
.btr_active
= false;
816 in_out_vrr
->btr
.inserted_duration_in_us
= 0;
817 in_out_vrr
->btr
.frames_to_insert
= 0;
818 in_out_vrr
->btr
.frame_counter
= 0;
819 in_out_vrr
->fixed
.fixed_active
= false;
820 in_out_vrr
->fixed
.target_refresh_in_uhz
= 0;
822 in_out_vrr
->btr
.mid_point_in_us
=
823 (in_out_vrr
->min_duration_in_us
+
824 in_out_vrr
->max_duration_in_us
) / 2;
826 if (in_out_vrr
->state
== VRR_STATE_UNSUPPORTED
) {
827 in_out_vrr
->adjust
.v_total_min
= stream
->timing
.v_total
;
828 in_out_vrr
->adjust
.v_total_max
= stream
->timing
.v_total
;
829 } else if (in_out_vrr
->state
== VRR_STATE_DISABLED
) {
830 in_out_vrr
->adjust
.v_total_min
= stream
->timing
.v_total
;
831 in_out_vrr
->adjust
.v_total_max
= stream
->timing
.v_total
;
832 } else if (in_out_vrr
->state
== VRR_STATE_INACTIVE
) {
833 in_out_vrr
->adjust
.v_total_min
= stream
->timing
.v_total
;
834 in_out_vrr
->adjust
.v_total_max
= stream
->timing
.v_total
;
835 } else if (in_out_vrr
->state
== VRR_STATE_ACTIVE_VARIABLE
&&
836 refresh_range
>= MIN_REFRESH_RANGE_IN_US
) {
838 in_out_vrr
->adjust
.v_total_min
=
839 calc_v_total_from_refresh(stream
,
840 in_out_vrr
->max_refresh_in_uhz
);
841 in_out_vrr
->adjust
.v_total_max
=
842 calc_v_total_from_refresh(stream
,
843 in_out_vrr
->min_refresh_in_uhz
);
844 } else if (in_out_vrr
->state
== VRR_STATE_ACTIVE_FIXED
) {
845 in_out_vrr
->fixed
.target_refresh_in_uhz
=
846 in_out_vrr
->min_refresh_in_uhz
;
847 if (in_out_vrr
->fixed
.ramping_active
&&
848 in_out_vrr
->fixed
.fixed_active
) {
849 /* Do not update vtotals if ramping is already active
850 * in order to continue ramp from current refresh.
852 in_out_vrr
->fixed
.fixed_active
= true;
854 in_out_vrr
->fixed
.fixed_active
= true;
855 in_out_vrr
->adjust
.v_total_min
=
856 calc_v_total_from_refresh(stream
,
857 in_out_vrr
->fixed
.target_refresh_in_uhz
);
858 in_out_vrr
->adjust
.v_total_max
=
859 in_out_vrr
->adjust
.v_total_min
;
862 in_out_vrr
->state
= VRR_STATE_INACTIVE
;
863 in_out_vrr
->adjust
.v_total_min
= stream
->timing
.v_total
;
864 in_out_vrr
->adjust
.v_total_max
= stream
->timing
.v_total
;
868 void mod_freesync_handle_preflip(struct mod_freesync
*mod_freesync
,
869 const struct dc_plane_state
*plane
,
870 const struct dc_stream_state
*stream
,
871 unsigned int curr_time_stamp_in_us
,
872 struct mod_vrr_params
*in_out_vrr
)
874 struct core_freesync
*core_freesync
= NULL
;
875 unsigned int last_render_time_in_us
= 0;
876 unsigned int average_render_time_in_us
= 0;
878 if (mod_freesync
== NULL
)
881 core_freesync
= MOD_FREESYNC_TO_CORE(mod_freesync
);
883 if (in_out_vrr
->supported
&&
884 in_out_vrr
->state
== VRR_STATE_ACTIVE_VARIABLE
) {
886 unsigned int oldest_index
= plane
->time
.index
+ 1;
888 if (oldest_index
>= DC_PLANE_UPDATE_TIMES_MAX
)
891 last_render_time_in_us
= curr_time_stamp_in_us
-
892 plane
->time
.prev_update_time_in_us
;
894 // Sum off all entries except oldest one
895 for (i
= 0; i
< DC_PLANE_UPDATE_TIMES_MAX
; i
++) {
896 average_render_time_in_us
+=
897 plane
->time
.time_elapsed_in_us
[i
];
899 average_render_time_in_us
-=
900 plane
->time
.time_elapsed_in_us
[oldest_index
];
902 // Add render time for current flip
903 average_render_time_in_us
+= last_render_time_in_us
;
904 average_render_time_in_us
/= DC_PLANE_UPDATE_TIMES_MAX
;
906 if (in_out_vrr
->btr
.btr_enabled
) {
907 apply_below_the_range(core_freesync
,
909 last_render_time_in_us
,
912 apply_fixed_refresh(core_freesync
,
914 last_render_time_in_us
,
921 void mod_freesync_handle_v_update(struct mod_freesync
*mod_freesync
,
922 const struct dc_stream_state
*stream
,
923 struct mod_vrr_params
*in_out_vrr
)
925 struct core_freesync
*core_freesync
= NULL
;
927 if ((mod_freesync
== NULL
) || (stream
== NULL
) || (in_out_vrr
== NULL
))
930 core_freesync
= MOD_FREESYNC_TO_CORE(mod_freesync
);
932 if (in_out_vrr
->supported
== false)
935 /* Below the Range Logic */
937 /* Only execute if in fullscreen mode */
938 if (in_out_vrr
->state
== VRR_STATE_ACTIVE_VARIABLE
&&
939 in_out_vrr
->btr
.btr_active
) {
940 /* TODO: pass in flag for Pre-DCE12 ASIC
941 * in order for frame variable duration to take affect,
942 * it needs to be done one VSYNC early, which is at
944 * For DCE12 and newer updates to V_TOTAL_MIN/MAX
945 * will take affect on current frame
947 if (in_out_vrr
->btr
.frames_to_insert
==
948 in_out_vrr
->btr
.frame_counter
) {
949 in_out_vrr
->adjust
.v_total_min
=
950 calc_v_total_from_duration(stream
,
952 in_out_vrr
->btr
.inserted_duration_in_us
);
953 in_out_vrr
->adjust
.v_total_max
=
954 in_out_vrr
->adjust
.v_total_min
;
957 if (in_out_vrr
->btr
.frame_counter
> 0)
958 in_out_vrr
->btr
.frame_counter
--;
960 /* Restore FreeSync */
961 if (in_out_vrr
->btr
.frame_counter
== 0) {
962 in_out_vrr
->adjust
.v_total_min
=
963 calc_v_total_from_refresh(stream
,
964 in_out_vrr
->max_refresh_in_uhz
);
965 in_out_vrr
->adjust
.v_total_max
=
966 calc_v_total_from_refresh(stream
,
967 in_out_vrr
->min_refresh_in_uhz
);
971 /* If in fullscreen freesync mode or in video, do not program
972 * static screen ramp values
974 if (in_out_vrr
->state
== VRR_STATE_ACTIVE_VARIABLE
)
975 in_out_vrr
->fixed
.ramping_active
= false;
977 /* Gradual Static Screen Ramping Logic */
978 /* Execute if ramp is active and user enabled freesync static screen*/
979 if (in_out_vrr
->state
== VRR_STATE_ACTIVE_FIXED
&&
980 in_out_vrr
->fixed
.ramping_active
) {
981 update_v_total_for_static_ramp(
982 core_freesync
, stream
, in_out_vrr
);
986 void mod_freesync_get_settings(struct mod_freesync
*mod_freesync
,
987 const struct mod_vrr_params
*vrr
,
988 unsigned int *v_total_min
, unsigned int *v_total_max
,
989 unsigned int *event_triggers
,
990 unsigned int *window_min
, unsigned int *window_max
,
991 unsigned int *lfc_mid_point_in_us
,
992 unsigned int *inserted_frames
,
993 unsigned int *inserted_duration_in_us
)
995 if (mod_freesync
== NULL
)
998 if (vrr
->supported
) {
999 *v_total_min
= vrr
->adjust
.v_total_min
;
1000 *v_total_max
= vrr
->adjust
.v_total_max
;
1001 *event_triggers
= 0;
1002 *lfc_mid_point_in_us
= vrr
->btr
.mid_point_in_us
;
1003 *inserted_frames
= vrr
->btr
.frames_to_insert
;
1004 *inserted_duration_in_us
= vrr
->btr
.inserted_duration_in_us
;
1008 unsigned long long mod_freesync_calc_nominal_field_rate(
1009 const struct dc_stream_state
*stream
)
1011 unsigned long long nominal_field_rate_in_uhz
= 0;
1012 unsigned int total
= stream
->timing
.h_total
* stream
->timing
.v_total
;
1014 /* Calculate nominal field rate for stream, rounded up to nearest integer */
1015 nominal_field_rate_in_uhz
= stream
->timing
.pix_clk_100hz
/ 10;
1016 nominal_field_rate_in_uhz
*= 1000ULL * 1000ULL * 1000ULL;
1018 nominal_field_rate_in_uhz
= div_u64(nominal_field_rate_in_uhz
, total
);
1020 return nominal_field_rate_in_uhz
;
1023 bool mod_freesync_is_valid_range(struct mod_freesync
*mod_freesync
,
1024 const struct dc_stream_state
*stream
,
1025 uint32_t min_refresh_cap_in_uhz
,
1026 uint32_t max_refresh_cap_in_uhz
,
1027 uint32_t min_refresh_request_in_uhz
,
1028 uint32_t max_refresh_request_in_uhz
)
1030 /* Calculate nominal field rate for stream */
1031 unsigned long long nominal_field_rate_in_uhz
=
1032 mod_freesync_calc_nominal_field_rate(stream
);
1034 /* Typically nominal refresh calculated can have some fractional part.
1035 * Allow for some rounding error of actual video timing by taking floor
1036 * of caps and request. Round the nominal refresh rate.
1038 * Dividing will convert everything to units in Hz although input
1039 * variable name is in uHz!
1041 * Also note, this takes care of rounding error on the nominal refresh
1042 * so by rounding error we only expect it to be off by a small amount,
1043 * such as < 0.1 Hz. i.e. 143.9xxx or 144.1xxx.
1045 * Example 1. Caps Min = 40 Hz, Max = 144 Hz
1046 * Request Min = 40 Hz, Max = 144 Hz
1047 * Nominal = 143.5x Hz rounded to 144 Hz
1048 * This function should allow this as valid request
1050 * Example 2. Caps Min = 40 Hz, Max = 144 Hz
1051 * Request Min = 40 Hz, Max = 144 Hz
1052 * Nominal = 144.4x Hz rounded to 144 Hz
1053 * This function should allow this as valid request
1055 * Example 3. Caps Min = 40 Hz, Max = 144 Hz
1056 * Request Min = 40 Hz, Max = 144 Hz
1057 * Nominal = 120.xx Hz rounded to 120 Hz
1058 * This function should return NOT valid since the requested
1059 * max is greater than current timing's nominal
1061 * Example 4. Caps Min = 40 Hz, Max = 120 Hz
1062 * Request Min = 40 Hz, Max = 120 Hz
1063 * Nominal = 144.xx Hz rounded to 144 Hz
1064 * This function should return NOT valid since the nominal
1065 * is greater than the capability's max refresh
1067 nominal_field_rate_in_uhz
=
1068 div_u64(nominal_field_rate_in_uhz
+ 500000, 1000000);
1069 min_refresh_cap_in_uhz
/= 1000000;
1070 max_refresh_cap_in_uhz
/= 1000000;
1071 min_refresh_request_in_uhz
/= 1000000;
1072 max_refresh_request_in_uhz
/= 1000000;
1074 // Check nominal is within range
1075 if (nominal_field_rate_in_uhz
> max_refresh_cap_in_uhz
||
1076 nominal_field_rate_in_uhz
< min_refresh_cap_in_uhz
)
1079 // If nominal is less than max, limit the max allowed refresh rate
1080 if (nominal_field_rate_in_uhz
< max_refresh_cap_in_uhz
)
1081 max_refresh_cap_in_uhz
= nominal_field_rate_in_uhz
;
1083 // Don't allow min > max
1084 if (min_refresh_request_in_uhz
> max_refresh_request_in_uhz
)
1087 // Check min is within range
1088 if (min_refresh_request_in_uhz
> max_refresh_cap_in_uhz
||
1089 min_refresh_request_in_uhz
< min_refresh_cap_in_uhz
)
1092 // Check max is within range
1093 if (max_refresh_request_in_uhz
> max_refresh_cap_in_uhz
||
1094 max_refresh_request_in_uhz
< min_refresh_cap_in_uhz
)
1097 // For variable range, check for at least 10 Hz range
1098 if ((max_refresh_request_in_uhz
!= min_refresh_request_in_uhz
) &&
1099 (max_refresh_request_in_uhz
- min_refresh_request_in_uhz
< 10))