1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26#include <linux/slab.h>
27
28#include "dm_services.h"
29#include "dc.h"
30#include "mod_freesync.h"
31#include "core_types.h"
32
33#define MOD_FREESYNC_MAX_CONCURRENT_STREAMS 32
34
35#define MIN_REFRESH_RANGE 10
36
37#define STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME ((1000 / 60) * 65)
38
39#define RENDER_TIMES_MAX_COUNT 10
40
41#define BTR_MAX_MARGIN 2500
42
43#define BTR_DRIFT_MARGIN 2000
44
45#define FIXED_REFRESH_EXIT_MARGIN_IN_HZ 1
46
47#define FIXED_REFRESH_ENTER_FRAME_COUNT 5
48#define FIXED_REFRESH_EXIT_FRAME_COUNT 10
49
50#define VSYNCS_BETWEEN_FLIP_THRESHOLD 2
51#define FREESYNC_CONSEC_FLIP_AFTER_VSYNC 5
52#define FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US 500
53
54struct core_freesync {
55 struct mod_freesync public;
56 struct dc *dc;
57};
58
59#define MOD_FREESYNC_TO_CORE(mod_freesync)\
60 container_of(mod_freesync, struct core_freesync, public)
61
62struct mod_freesync *mod_freesync_create(struct dc *dc)
63{
64 struct core_freesync *core_freesync =
65 kzalloc(sizeof(struct core_freesync), GFP_KERNEL);
66
67 if (core_freesync == NULL)
68 goto fail_alloc_context;
69
70 if (dc == NULL)
71 goto fail_construct;
72
73 core_freesync->dc = dc;
74 return &core_freesync->public;
75
76fail_construct:
77 kfree(core_freesync);
78
79fail_alloc_context:
80 return NULL;
81}
82
83void mod_freesync_destroy(struct mod_freesync *mod_freesync)
84{
85 struct core_freesync *core_freesync = NULL;
86 if (mod_freesync == NULL)
87 return;
88 core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
89 kfree(core_freesync);
90}
91
92#if 0
93static unsigned int calc_refresh_in_uhz_from_duration(
94 unsigned int duration_in_ns)
95{
96 unsigned int refresh_in_uhz =
97 ((unsigned int)(div64_u64((1000000000ULL * 1000000),
98 duration_in_ns)));
99 return refresh_in_uhz;
100}
101#endif
102
103static unsigned int calc_duration_in_us_from_refresh_in_uhz(
104 unsigned int refresh_in_uhz)
105{
106 unsigned int duration_in_us =
107 ((unsigned int)(div64_u64((1000000000ULL * 1000),
108 refresh_in_uhz)));
109 return duration_in_us;
110}
111
112static unsigned int calc_duration_in_us_from_v_total(
113 const struct dc_stream_state *stream,
114 const struct mod_vrr_params *in_vrr,
115 unsigned int v_total)
116{
117 unsigned int duration_in_us =
118 (unsigned int)(div64_u64(((unsigned long long)(v_total)
119 * 10000) * stream->timing.h_total,
120 stream->timing.pix_clk_100hz));
121
122 return duration_in_us;
123}
124
125unsigned int mod_freesync_calc_v_total_from_refresh(
126 const struct dc_stream_state *stream,
127 unsigned int refresh_in_uhz)
128{
129 unsigned int v_total;
130 unsigned int frame_duration_in_ns;
131
132 frame_duration_in_ns =
133 ((unsigned int)(div64_u64((1000000000ULL * 1000000),
134 refresh_in_uhz)));
135
136 v_total = div64_u64(div64_u64(((unsigned long long)(
137 frame_duration_in_ns) * (stream->timing.pix_clk_100hz / 10)),
138 stream->timing.h_total), 1000000);
139
140
141 if (v_total < stream->timing.v_total) {
142 ASSERT(v_total < stream->timing.v_total);
143 v_total = stream->timing.v_total;
144 }
145
146 return v_total;
147}
148
149static unsigned int calc_v_total_from_duration(
150 const struct dc_stream_state *stream,
151 const struct mod_vrr_params *vrr,
152 unsigned int duration_in_us)
153{
154 unsigned int v_total = 0;
155
156 if (duration_in_us < vrr->min_duration_in_us)
157 duration_in_us = vrr->min_duration_in_us;
158
159 if (duration_in_us > vrr->max_duration_in_us)
160 duration_in_us = vrr->max_duration_in_us;
161
162 if (dc_is_hdmi_signal(stream->signal)) {
163 uint32_t h_total_up_scaled;
164
165 h_total_up_scaled = stream->timing.h_total * 10000;
166 v_total = div_u64((unsigned long long)duration_in_us
167 * stream->timing.pix_clk_100hz + (h_total_up_scaled - 1),
168 h_total_up_scaled);
169 } else {
170 v_total = div64_u64(div64_u64(((unsigned long long)(
171 duration_in_us) * (stream->timing.pix_clk_100hz / 10)),
172 stream->timing.h_total), 1000);
173 }
174
175
176 if (v_total < stream->timing.v_total) {
177 ASSERT(v_total < stream->timing.v_total);
178 v_total = stream->timing.v_total;
179 }
180
181 return v_total;
182}
183
184static void update_v_total_for_static_ramp(
185 struct core_freesync *core_freesync,
186 const struct dc_stream_state *stream,
187 struct mod_vrr_params *in_out_vrr)
188{
189 unsigned int v_total = 0;
190 unsigned int current_duration_in_us =
191 calc_duration_in_us_from_v_total(
192 stream, in_out_vrr,
193 in_out_vrr->adjust.v_total_max);
194 unsigned int target_duration_in_us =
195 calc_duration_in_us_from_refresh_in_uhz(
196 in_out_vrr->fixed.target_refresh_in_uhz);
197 bool ramp_direction_is_up = (current_duration_in_us >
198 target_duration_in_us) ? true : false;
199
200
201 unsigned int frame_duration_ratio = div64_u64(1000000,
202 (1000 + div64_u64(((unsigned long long)(
203 STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME) *
204 current_duration_in_us),
205 1000000)));
206
207
208 unsigned int frame_duration_delta = div64_u64(((unsigned long long)(
209 current_duration_in_us) *
210 (1000 - frame_duration_ratio)), 1000);
211
212
213
214
215 unsigned int ramp_rate_interpolated = div64_u64(((unsigned long long)(
216 frame_duration_delta) * current_duration_in_us), 16666);
217
218
219 if (ramp_direction_is_up) {
220
221 current_duration_in_us -= ramp_rate_interpolated;
222
223
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);
230 }
231
232 } else {
233
234 current_duration_in_us += ramp_rate_interpolated;
235
236
237 if (current_duration_in_us >= target_duration_in_us) {
238 in_out_vrr->fixed.ramping_active = false;
239 in_out_vrr->fixed.ramping_done = true;
240 current_duration_in_us =
241 calc_duration_in_us_from_refresh_in_uhz(
242 in_out_vrr->fixed.target_refresh_in_uhz);
243 }
244 }
245
246 v_total = div64_u64(div64_u64(((unsigned long long)(
247 current_duration_in_us) * (stream->timing.pix_clk_100hz / 10)),
248 stream->timing.h_total), 1000);
249
250
251 if (v_total < stream->timing.v_total)
252 v_total = stream->timing.v_total;
253
254 in_out_vrr->adjust.v_total_min = v_total;
255 in_out_vrr->adjust.v_total_max = v_total;
256}
257
258static void apply_below_the_range(struct core_freesync *core_freesync,
259 const struct dc_stream_state *stream,
260 unsigned int last_render_time_in_us,
261 struct mod_vrr_params *in_out_vrr)
262{
263 unsigned int inserted_frame_duration_in_us = 0;
264 unsigned int mid_point_frames_ceil = 0;
265 unsigned int mid_point_frames_floor = 0;
266 unsigned int frame_time_in_us = 0;
267 unsigned int delta_from_mid_point_in_us_1 = 0xFFFFFFFF;
268 unsigned int delta_from_mid_point_in_us_2 = 0xFFFFFFFF;
269 unsigned int frames_to_insert = 0;
270 unsigned int delta_from_mid_point_delta_in_us;
271 unsigned int max_render_time_in_us =
272 in_out_vrr->max_duration_in_us - in_out_vrr->btr.margin_in_us;
273
274
275 if ((last_render_time_in_us + in_out_vrr->btr.margin_in_us / 2) < max_render_time_in_us) {
276
277 if (in_out_vrr->btr.btr_active) {
278 in_out_vrr->btr.frame_counter = 0;
279 in_out_vrr->btr.btr_active = false;
280 }
281 } else if (last_render_time_in_us > (max_render_time_in_us + in_out_vrr->btr.margin_in_us / 2)) {
282
283 if (!in_out_vrr->btr.btr_active) {
284 in_out_vrr->btr.btr_active = true;
285 }
286 }
287
288
289 if (!in_out_vrr->btr.btr_active) {
290 in_out_vrr->btr.inserted_duration_in_us = 0;
291 in_out_vrr->btr.frames_to_insert = 0;
292 in_out_vrr->btr.frame_counter = 0;
293
294
295 in_out_vrr->adjust.v_total_min =
296 mod_freesync_calc_v_total_from_refresh(stream,
297 in_out_vrr->max_refresh_in_uhz);
298 in_out_vrr->adjust.v_total_max =
299 mod_freesync_calc_v_total_from_refresh(stream,
300 in_out_vrr->min_refresh_in_uhz);
301
302 } else {
303
304
305
306
307 mid_point_frames_ceil = (last_render_time_in_us +
308 in_out_vrr->btr.mid_point_in_us - 1) /
309 in_out_vrr->btr.mid_point_in_us;
310
311 if (mid_point_frames_ceil > 0) {
312 frame_time_in_us = last_render_time_in_us /
313 mid_point_frames_ceil;
314 delta_from_mid_point_in_us_1 =
315 (in_out_vrr->btr.mid_point_in_us >
316 frame_time_in_us) ?
317 (in_out_vrr->btr.mid_point_in_us - frame_time_in_us) :
318 (frame_time_in_us - in_out_vrr->btr.mid_point_in_us);
319 }
320
321
322
323
324 mid_point_frames_floor = last_render_time_in_us /
325 in_out_vrr->btr.mid_point_in_us;
326
327 if (mid_point_frames_floor > 0) {
328
329 frame_time_in_us = last_render_time_in_us /
330 mid_point_frames_floor;
331 delta_from_mid_point_in_us_2 =
332 (in_out_vrr->btr.mid_point_in_us >
333 frame_time_in_us) ?
334 (in_out_vrr->btr.mid_point_in_us - frame_time_in_us) :
335 (frame_time_in_us - in_out_vrr->btr.mid_point_in_us);
336 }
337
338
339
340
341
342
343 if ((last_render_time_in_us / mid_point_frames_ceil) < in_out_vrr->min_duration_in_us) {
344
345
346
347
348 frames_to_insert = mid_point_frames_floor;
349 } else if (mid_point_frames_floor < 2) {
350
351
352
353 frames_to_insert = mid_point_frames_ceil;
354 } else if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) {
355
356
357
358
359 frames_to_insert = mid_point_frames_ceil;
360 } else {
361
362
363
364
365 frames_to_insert = mid_point_frames_floor;
366 }
367
368
369
370
371 if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) {
372 delta_from_mid_point_delta_in_us = delta_from_mid_point_in_us_2 -
373 delta_from_mid_point_in_us_1;
374 } else {
375 delta_from_mid_point_delta_in_us = delta_from_mid_point_in_us_1 -
376 delta_from_mid_point_in_us_2;
377 }
378 if (in_out_vrr->btr.frames_to_insert != 0 &&
379 delta_from_mid_point_delta_in_us < BTR_DRIFT_MARGIN) {
380 if (((last_render_time_in_us / in_out_vrr->btr.frames_to_insert) <
381 max_render_time_in_us) &&
382 ((last_render_time_in_us / in_out_vrr->btr.frames_to_insert) >
383 in_out_vrr->min_duration_in_us))
384 frames_to_insert = in_out_vrr->btr.frames_to_insert;
385 }
386
387
388
389
390 if (last_render_time_in_us / frames_to_insert <
391 in_out_vrr->min_duration_in_us){
392 frames_to_insert -= (frames_to_insert > 1) ?
393 1 : 0;
394 }
395
396 if (frames_to_insert > 0)
397 inserted_frame_duration_in_us = last_render_time_in_us /
398 frames_to_insert;
399
400 if (inserted_frame_duration_in_us < in_out_vrr->min_duration_in_us)
401 inserted_frame_duration_in_us = in_out_vrr->min_duration_in_us;
402
403
404 in_out_vrr->btr.inserted_duration_in_us =
405 inserted_frame_duration_in_us;
406 in_out_vrr->btr.frames_to_insert = frames_to_insert;
407 in_out_vrr->btr.frame_counter = frames_to_insert;
408 }
409}
410
411static void apply_fixed_refresh(struct core_freesync *core_freesync,
412 const struct dc_stream_state *stream,
413 unsigned int last_render_time_in_us,
414 struct mod_vrr_params *in_out_vrr)
415{
416 bool update = false;
417 unsigned int max_render_time_in_us = in_out_vrr->max_duration_in_us;
418
419
420 unsigned int exit_refresh_rate_in_milli_hz = ((1000000000/max_render_time_in_us)
421 + (1000*FIXED_REFRESH_EXIT_MARGIN_IN_HZ));
422 unsigned int exit_frame_duration_in_us = 1000000000/exit_refresh_rate_in_milli_hz;
423
424 if (last_render_time_in_us < exit_frame_duration_in_us) {
425
426 if (in_out_vrr->fixed.fixed_active) {
427 in_out_vrr->fixed.frame_counter++;
428
429 if (in_out_vrr->fixed.frame_counter >
430 FIXED_REFRESH_EXIT_FRAME_COUNT) {
431 in_out_vrr->fixed.frame_counter = 0;
432 in_out_vrr->fixed.fixed_active = false;
433 in_out_vrr->fixed.target_refresh_in_uhz = 0;
434 update = true;
435 }
436 } else
437 in_out_vrr->fixed.frame_counter = 0;
438 } else if (last_render_time_in_us > max_render_time_in_us) {
439
440 if (!in_out_vrr->fixed.fixed_active) {
441 in_out_vrr->fixed.frame_counter++;
442
443 if (in_out_vrr->fixed.frame_counter >
444 FIXED_REFRESH_ENTER_FRAME_COUNT) {
445 in_out_vrr->fixed.frame_counter = 0;
446 in_out_vrr->fixed.fixed_active = true;
447 in_out_vrr->fixed.target_refresh_in_uhz =
448 in_out_vrr->max_refresh_in_uhz;
449 update = true;
450 }
451 } else
452 in_out_vrr->fixed.frame_counter = 0;
453 }
454
455 if (update) {
456 if (in_out_vrr->fixed.fixed_active) {
457 in_out_vrr->adjust.v_total_min =
458 mod_freesync_calc_v_total_from_refresh(
459 stream, in_out_vrr->max_refresh_in_uhz);
460 in_out_vrr->adjust.v_total_max =
461 in_out_vrr->adjust.v_total_min;
462 } else {
463 in_out_vrr->adjust.v_total_min =
464 mod_freesync_calc_v_total_from_refresh(stream,
465 in_out_vrr->max_refresh_in_uhz);
466 in_out_vrr->adjust.v_total_max =
467 mod_freesync_calc_v_total_from_refresh(stream,
468 in_out_vrr->min_refresh_in_uhz);
469 }
470 }
471}
472
473static void determine_flip_interval_workaround_req(struct mod_vrr_params *in_vrr,
474 unsigned int curr_time_stamp_in_us)
475{
476 in_vrr->flip_interval.vsync_to_flip_in_us = curr_time_stamp_in_us -
477 in_vrr->flip_interval.v_update_timestamp_in_us;
478
479
480 if (in_vrr->flip_interval.flip_interval_workaround_active &&
481 in_vrr->flip_interval.vsyncs_between_flip < VSYNCS_BETWEEN_FLIP_THRESHOLD &&
482 in_vrr->flip_interval.vsync_to_flip_in_us > FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US) {
483 in_vrr->flip_interval.flip_interval_detect_counter = 0;
484 in_vrr->flip_interval.program_flip_interval_workaround = true;
485 in_vrr->flip_interval.flip_interval_workaround_active = false;
486 } else {
487
488 if (in_vrr->flip_interval.vsyncs_between_flip >= VSYNCS_BETWEEN_FLIP_THRESHOLD &&
489 in_vrr->flip_interval.vsync_to_flip_in_us < FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US) {
490
491
492
493 in_vrr->flip_interval.flip_interval_detect_counter++;
494 if (in_vrr->flip_interval.flip_interval_detect_counter > FREESYNC_CONSEC_FLIP_AFTER_VSYNC) {
495
496 in_vrr->flip_interval.program_flip_interval_workaround = true;
497 in_vrr->flip_interval.flip_interval_workaround_active = true;
498 }
499 } else {
500
501 in_vrr->flip_interval.flip_interval_detect_counter = 0;
502 }
503 }
504
505 in_vrr->flip_interval.vsyncs_between_flip = 0;
506}
507
508static bool vrr_settings_require_update(struct core_freesync *core_freesync,
509 struct mod_freesync_config *in_config,
510 unsigned int min_refresh_in_uhz,
511 unsigned int max_refresh_in_uhz,
512 struct mod_vrr_params *in_vrr)
513{
514 if (in_vrr->state != in_config->state) {
515 return true;
516 } else if (in_vrr->state == VRR_STATE_ACTIVE_FIXED &&
517 in_vrr->fixed.target_refresh_in_uhz !=
518 in_config->fixed_refresh_in_uhz) {
519 return true;
520 } else if (in_vrr->min_refresh_in_uhz != min_refresh_in_uhz) {
521 return true;
522 } else if (in_vrr->max_refresh_in_uhz != max_refresh_in_uhz) {
523 return true;
524 }
525
526 return false;
527}
528
529bool mod_freesync_get_vmin_vmax(struct mod_freesync *mod_freesync,
530 const struct dc_stream_state *stream,
531 unsigned int *vmin,
532 unsigned int *vmax)
533{
534 *vmin = stream->adjust.v_total_min;
535 *vmax = stream->adjust.v_total_max;
536
537 return true;
538}
539
540bool mod_freesync_get_v_position(struct mod_freesync *mod_freesync,
541 struct dc_stream_state *stream,
542 unsigned int *nom_v_pos,
543 unsigned int *v_pos)
544{
545 struct core_freesync *core_freesync = NULL;
546 struct crtc_position position;
547
548 if (mod_freesync == NULL)
549 return false;
550
551 core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
552
553 if (dc_stream_get_crtc_position(core_freesync->dc, &stream, 1,
554 &position.vertical_count,
555 &position.nominal_vcount)) {
556
557 *nom_v_pos = position.nominal_vcount;
558 *v_pos = position.vertical_count;
559
560 return true;
561 }
562
563 return false;
564}
565
566static void build_vrr_infopacket_data_v1(const struct mod_vrr_params *vrr,
567 struct dc_info_packet *infopacket,
568 bool freesync_on_desktop)
569{
570
571 infopacket->sb[1] = 0x1A;
572
573
574 infopacket->sb[2] = 0x00;
575
576
577 infopacket->sb[3] = 0x00;
578
579
580
581
582
583
584
585
586 if (vrr->state != VRR_STATE_UNSUPPORTED)
587 infopacket->sb[6] |= 0x01;
588
589
590 if (vrr->state != VRR_STATE_DISABLED &&
591 vrr->state != VRR_STATE_UNSUPPORTED)
592 infopacket->sb[6] |= 0x02;
593
594 if (freesync_on_desktop) {
595
596 if (vrr->state != VRR_STATE_DISABLED &&
597 vrr->state != VRR_STATE_UNSUPPORTED)
598 infopacket->sb[6] |= 0x04;
599 } else {
600 if (vrr->state == VRR_STATE_ACTIVE_VARIABLE ||
601 vrr->state == VRR_STATE_ACTIVE_FIXED)
602 infopacket->sb[6] |= 0x04;
603 }
604
605
606
607 if (vrr->state == VRR_STATE_ACTIVE_VARIABLE ||
608 vrr->state == VRR_STATE_ACTIVE_FIXED) {
609 infopacket->sb[7] = (unsigned char)((vrr->min_refresh_in_uhz + 500000) / 1000000);
610 } else {
611 infopacket->sb[7] = (unsigned char)((vrr->max_refresh_in_uhz + 500000) / 1000000);
612 }
613
614
615
616
617 infopacket->sb[8] = (unsigned char)((vrr->max_refresh_in_uhz + 500000) / 1000000);
618
619
620 infopacket->sb[9] = 0;
621 infopacket->sb[10] = 0;
622}
623
624static void build_vrr_infopacket_data_v3(const struct mod_vrr_params *vrr,
625 struct dc_info_packet *infopacket)
626{
627 unsigned int min_refresh;
628 unsigned int max_refresh;
629 unsigned int fixed_refresh;
630 unsigned int min_programmed;
631 unsigned int max_programmed;
632
633
634 infopacket->sb[1] = 0x1A;
635
636
637 infopacket->sb[2] = 0x00;
638
639
640 infopacket->sb[3] = 0x00;
641
642
643
644
645
646
647
648
649 if (vrr->state != VRR_STATE_UNSUPPORTED)
650 infopacket->sb[6] |= 0x01;
651
652
653 if (vrr->state != VRR_STATE_DISABLED &&
654 vrr->state != VRR_STATE_UNSUPPORTED)
655 infopacket->sb[6] |= 0x02;
656
657
658 if (vrr->state == VRR_STATE_ACTIVE_VARIABLE ||
659 vrr->state == VRR_STATE_ACTIVE_FIXED)
660 infopacket->sb[6] |= 0x04;
661
662 min_refresh = (vrr->min_refresh_in_uhz + 500000) / 1000000;
663 max_refresh = (vrr->max_refresh_in_uhz + 500000) / 1000000;
664 fixed_refresh = (vrr->fixed_refresh_in_uhz + 500000) / 1000000;
665
666 min_programmed = (vrr->state == VRR_STATE_ACTIVE_FIXED) ? fixed_refresh :
667 (vrr->state == VRR_STATE_ACTIVE_VARIABLE) ? min_refresh :
668 (vrr->state == VRR_STATE_INACTIVE) ? min_refresh :
669 max_refresh;
670
671 max_programmed = (vrr->state == VRR_STATE_ACTIVE_FIXED) ? fixed_refresh :
672 (vrr->state == VRR_STATE_ACTIVE_VARIABLE) ? max_refresh :
673 max_refresh;
674
675
676 infopacket->sb[7] = min_programmed & 0xFF;
677
678
679 infopacket->sb[8] = max_programmed & 0xFF;
680
681
682 infopacket->sb[11] = (min_programmed >> 8) & 0x03;
683
684
685 infopacket->sb[12] = (max_programmed >> 8) & 0x03;
686
687
688 infopacket->sb[16] = (vrr->state == VRR_STATE_ACTIVE_FIXED) ? 1 : 0;
689
690
691 infopacket->sb[9] = 0;
692 infopacket->sb[10] = 0;
693}
694
695static void build_vrr_infopacket_fs2_data(enum color_transfer_func app_tf,
696 struct dc_info_packet *infopacket)
697{
698 if (app_tf != TRANSFER_FUNC_UNKNOWN) {
699 infopacket->valid = true;
700
701 infopacket->sb[6] |= 0x08;
702
703 if (app_tf == TRANSFER_FUNC_GAMMA_22) {
704 infopacket->sb[9] |= 0x04;
705 }
706 }
707}
708
709static void build_vrr_infopacket_header_v1(enum signal_type signal,
710 struct dc_info_packet *infopacket,
711 unsigned int *payload_size)
712{
713 if (dc_is_hdmi_signal(signal)) {
714
715
716
717
718
719
720 infopacket->hb0 = DC_HDMI_INFOFRAME_TYPE_SPD;
721
722
723 infopacket->hb1 = 0x01;
724
725
726 infopacket->hb2 = 0x08;
727
728 *payload_size = 0x08;
729
730 } else if (dc_is_dp_signal(signal)) {
731
732
733
734
735
736
737 infopacket->hb0 = 0x00;
738
739
740
741
742 infopacket->hb1 = DC_HDMI_INFOFRAME_TYPE_SPD;
743
744
745
746
747 infopacket->hb2 = 0x1B;
748
749
750
751
752 infopacket->hb3 = 0x04;
753
754 *payload_size = 0x1B;
755 }
756}
757
758static void build_vrr_infopacket_header_v2(enum signal_type signal,
759 struct dc_info_packet *infopacket,
760 unsigned int *payload_size)
761{
762 if (dc_is_hdmi_signal(signal)) {
763
764
765
766
767
768
769 infopacket->hb0 = DC_HDMI_INFOFRAME_TYPE_SPD;
770
771
772 infopacket->hb1 = 0x02;
773
774
775 infopacket->hb2 = 0x09;
776
777 *payload_size = 0x0A;
778
779 } else if (dc_is_dp_signal(signal)) {
780
781
782
783
784
785
786 infopacket->hb0 = 0x00;
787
788
789
790
791 infopacket->hb1 = DC_HDMI_INFOFRAME_TYPE_SPD;
792
793
794
795
796 infopacket->hb2 = 0x1B;
797
798
799
800
801 infopacket->hb3 = 0x08;
802
803 *payload_size = 0x1B;
804 }
805}
806
807static void build_vrr_infopacket_header_v3(enum signal_type signal,
808 struct dc_info_packet *infopacket,
809 unsigned int *payload_size)
810{
811 unsigned char version;
812
813 version = 3;
814 if (dc_is_hdmi_signal(signal)) {
815
816
817
818
819
820
821 infopacket->hb0 = DC_HDMI_INFOFRAME_TYPE_SPD;
822
823
824 infopacket->hb1 = version;
825
826
827 *payload_size = 0x10;
828 infopacket->hb2 = *payload_size - 1;
829
830 } else if (dc_is_dp_signal(signal)) {
831
832
833
834
835
836
837 infopacket->hb0 = 0x00;
838
839
840
841
842 infopacket->hb1 = DC_HDMI_INFOFRAME_TYPE_SPD;
843
844
845
846
847 infopacket->hb2 = 0x1B;
848
849
850
851
852
853 infopacket->hb3 = (version & 0x3F) << 2;
854
855 *payload_size = 0x1B;
856 }
857}
858
859static void build_vrr_infopacket_checksum(unsigned int *payload_size,
860 struct dc_info_packet *infopacket)
861{
862
863 unsigned int idx = 0;
864 unsigned char checksum = 0;
865
866 checksum += infopacket->hb0;
867 checksum += infopacket->hb1;
868 checksum += infopacket->hb2;
869 checksum += infopacket->hb3;
870
871 for (idx = 1; idx <= *payload_size; idx++)
872 checksum += infopacket->sb[idx];
873
874
875 infopacket->sb[0] = (unsigned char)(0x100 - checksum);
876
877 infopacket->valid = true;
878}
879
880static void build_vrr_infopacket_v1(enum signal_type signal,
881 const struct mod_vrr_params *vrr,
882 struct dc_info_packet *infopacket,
883 bool freesync_on_desktop)
884{
885
886 unsigned int payload_size = 0;
887
888 build_vrr_infopacket_header_v1(signal, infopacket, &payload_size);
889 build_vrr_infopacket_data_v1(vrr, infopacket, freesync_on_desktop);
890 build_vrr_infopacket_checksum(&payload_size, infopacket);
891
892 infopacket->valid = true;
893}
894
895static void build_vrr_infopacket_v2(enum signal_type signal,
896 const struct mod_vrr_params *vrr,
897 enum color_transfer_func app_tf,
898 struct dc_info_packet *infopacket,
899 bool freesync_on_desktop)
900{
901 unsigned int payload_size = 0;
902
903 build_vrr_infopacket_header_v2(signal, infopacket, &payload_size);
904 build_vrr_infopacket_data_v1(vrr, infopacket, freesync_on_desktop);
905
906 build_vrr_infopacket_fs2_data(app_tf, infopacket);
907
908 build_vrr_infopacket_checksum(&payload_size, infopacket);
909
910 infopacket->valid = true;
911}
912#ifndef TRIM_FSFT
913static void build_vrr_infopacket_fast_transport_data(
914 bool ftActive,
915 unsigned int ftOutputRate,
916 struct dc_info_packet *infopacket)
917{
918
919 unsigned char activeBit = (ftActive) ? 1 << 7 : 0;
920
921 infopacket->sb[1] &= ~activeBit;
922 infopacket->sb[1] |= activeBit;
923
924
925 infopacket->sb[13] = ftOutputRate & 0xFF;
926
927
928 infopacket->sb[14] = (ftOutputRate >> 8) & 0xFF;
929
930
931 infopacket->sb[15] = (ftOutputRate >> 16) & 0xFF;
932
933}
934#endif
935
936static void build_vrr_infopacket_v3(enum signal_type signal,
937 const struct mod_vrr_params *vrr,
938#ifndef TRIM_FSFT
939 bool ftActive, unsigned int ftOutputRate,
940#endif
941 enum color_transfer_func app_tf,
942 struct dc_info_packet *infopacket)
943{
944 unsigned int payload_size = 0;
945
946 build_vrr_infopacket_header_v3(signal, infopacket, &payload_size);
947 build_vrr_infopacket_data_v3(vrr, infopacket);
948
949 build_vrr_infopacket_fs2_data(app_tf, infopacket);
950
951#ifndef TRIM_FSFT
952 build_vrr_infopacket_fast_transport_data(
953 ftActive,
954 ftOutputRate,
955 infopacket);
956#endif
957
958 build_vrr_infopacket_checksum(&payload_size, infopacket);
959
960 infopacket->valid = true;
961}
962
963static void build_vrr_infopacket_sdp_v1_3(enum vrr_packet_type packet_type,
964 struct dc_info_packet *infopacket)
965{
966 uint8_t idx = 0, size = 0;
967
968 size = ((packet_type == PACKET_TYPE_FS_V1) ? 0x08 :
969 (packet_type == PACKET_TYPE_FS_V3) ? 0x10 :
970 0x09);
971
972 for (idx = infopacket->hb2; idx > 1; idx--)
973 infopacket->sb[idx] = infopacket->sb[idx-1];
974
975 infopacket->sb[1] = size;
976 infopacket->sb[0] = (infopacket->hb3 >> 2) & 0x3F;
977 infopacket->hb3 = (0x13 << 2);
978 infopacket->hb2 = 0x1D;
979}
980
981void mod_freesync_build_vrr_infopacket(struct mod_freesync *mod_freesync,
982 const struct dc_stream_state *stream,
983 const struct mod_vrr_params *vrr,
984 enum vrr_packet_type packet_type,
985 enum color_transfer_func app_tf,
986 struct dc_info_packet *infopacket,
987 bool pack_sdp_v1_3)
988{
989
990
991
992
993
994 if (!vrr->send_info_frame)
995 return;
996
997 switch (packet_type) {
998 case PACKET_TYPE_FS_V3:
999#ifndef TRIM_FSFT
1000
1001 build_vrr_infopacket_v3(
1002 stream->signal, vrr,
1003 stream->timing.flags.FAST_TRANSPORT,
1004 (stream->timing.flags.FAST_TRANSPORT) ?
1005 stream->timing.fast_transport_output_rate_100hz :
1006 stream->timing.pix_clk_100hz,
1007 app_tf, infopacket);
1008#else
1009 build_vrr_infopacket_v3(stream->signal, vrr, app_tf, infopacket);
1010#endif
1011 break;
1012 case PACKET_TYPE_FS_V2:
1013 build_vrr_infopacket_v2(stream->signal, vrr, app_tf, infopacket, stream->freesync_on_desktop);
1014 break;
1015 case PACKET_TYPE_VRR:
1016 case PACKET_TYPE_FS_V1:
1017 default:
1018 build_vrr_infopacket_v1(stream->signal, vrr, infopacket, stream->freesync_on_desktop);
1019 }
1020
1021 if (true == pack_sdp_v1_3 &&
1022 true == dc_is_dp_signal(stream->signal) &&
1023 packet_type != PACKET_TYPE_VRR &&
1024 packet_type != PACKET_TYPE_VTEM)
1025 build_vrr_infopacket_sdp_v1_3(packet_type, infopacket);
1026}
1027
1028void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync,
1029 const struct dc_stream_state *stream,
1030 struct mod_freesync_config *in_config,
1031 struct mod_vrr_params *in_out_vrr)
1032{
1033 struct core_freesync *core_freesync = NULL;
1034 unsigned long long nominal_field_rate_in_uhz = 0;
1035 unsigned long long rounded_nominal_in_uhz = 0;
1036 unsigned int refresh_range = 0;
1037 unsigned long long min_refresh_in_uhz = 0;
1038 unsigned long long max_refresh_in_uhz = 0;
1039
1040 if (mod_freesync == NULL)
1041 return;
1042
1043 core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
1044
1045
1046 nominal_field_rate_in_uhz =
1047 mod_freesync_calc_nominal_field_rate(stream);
1048
1049 min_refresh_in_uhz = in_config->min_refresh_in_uhz;
1050 max_refresh_in_uhz = in_config->max_refresh_in_uhz;
1051
1052
1053 if (max_refresh_in_uhz > nominal_field_rate_in_uhz)
1054 max_refresh_in_uhz = nominal_field_rate_in_uhz;
1055
1056
1057 if (min_refresh_in_uhz > max_refresh_in_uhz)
1058 min_refresh_in_uhz = max_refresh_in_uhz;
1059
1060
1061 rounded_nominal_in_uhz =
1062 div_u64(nominal_field_rate_in_uhz + 50000, 100000) * 100000;
1063 if (in_config->max_refresh_in_uhz == (2 * in_config->min_refresh_in_uhz) &&
1064 in_config->max_refresh_in_uhz == rounded_nominal_in_uhz)
1065 min_refresh_in_uhz = div_u64(nominal_field_rate_in_uhz, 2);
1066
1067 if (!vrr_settings_require_update(core_freesync,
1068 in_config, (unsigned int)min_refresh_in_uhz, (unsigned int)max_refresh_in_uhz,
1069 in_out_vrr))
1070 return;
1071
1072 in_out_vrr->state = in_config->state;
1073 in_out_vrr->send_info_frame = in_config->vsif_supported;
1074
1075 if (in_config->state == VRR_STATE_UNSUPPORTED) {
1076 in_out_vrr->state = VRR_STATE_UNSUPPORTED;
1077 in_out_vrr->supported = false;
1078 in_out_vrr->adjust.v_total_min = stream->timing.v_total;
1079 in_out_vrr->adjust.v_total_max = stream->timing.v_total;
1080
1081 return;
1082
1083 } else {
1084 in_out_vrr->min_refresh_in_uhz = (unsigned int)min_refresh_in_uhz;
1085 in_out_vrr->max_duration_in_us =
1086 calc_duration_in_us_from_refresh_in_uhz(
1087 (unsigned int)min_refresh_in_uhz);
1088
1089 in_out_vrr->max_refresh_in_uhz = (unsigned int)max_refresh_in_uhz;
1090 in_out_vrr->min_duration_in_us =
1091 calc_duration_in_us_from_refresh_in_uhz(
1092 (unsigned int)max_refresh_in_uhz);
1093
1094 if (in_config->state == VRR_STATE_ACTIVE_FIXED)
1095 in_out_vrr->fixed_refresh_in_uhz = in_config->fixed_refresh_in_uhz;
1096 else
1097 in_out_vrr->fixed_refresh_in_uhz = 0;
1098
1099 refresh_range = div_u64(in_out_vrr->max_refresh_in_uhz + 500000, 1000000) -
1100+ div_u64(in_out_vrr->min_refresh_in_uhz + 500000, 1000000);
1101
1102 in_out_vrr->supported = true;
1103 }
1104
1105 in_out_vrr->fixed.ramping_active = in_config->ramping;
1106
1107 in_out_vrr->btr.btr_enabled = in_config->btr;
1108
1109 if (in_out_vrr->max_refresh_in_uhz < (2 * in_out_vrr->min_refresh_in_uhz))
1110 in_out_vrr->btr.btr_enabled = false;
1111 else {
1112 in_out_vrr->btr.margin_in_us = in_out_vrr->max_duration_in_us -
1113 2 * in_out_vrr->min_duration_in_us;
1114 if (in_out_vrr->btr.margin_in_us > BTR_MAX_MARGIN)
1115 in_out_vrr->btr.margin_in_us = BTR_MAX_MARGIN;
1116 }
1117
1118 in_out_vrr->btr.btr_active = false;
1119 in_out_vrr->btr.inserted_duration_in_us = 0;
1120 in_out_vrr->btr.frames_to_insert = 0;
1121 in_out_vrr->btr.frame_counter = 0;
1122 in_out_vrr->fixed.fixed_active = false;
1123 in_out_vrr->fixed.target_refresh_in_uhz = 0;
1124
1125 in_out_vrr->btr.mid_point_in_us =
1126 (in_out_vrr->min_duration_in_us +
1127 in_out_vrr->max_duration_in_us) / 2;
1128
1129 if (in_out_vrr->state == VRR_STATE_UNSUPPORTED) {
1130 in_out_vrr->adjust.v_total_min = stream->timing.v_total;
1131 in_out_vrr->adjust.v_total_max = stream->timing.v_total;
1132 } else if (in_out_vrr->state == VRR_STATE_DISABLED) {
1133 in_out_vrr->adjust.v_total_min = stream->timing.v_total;
1134 in_out_vrr->adjust.v_total_max = stream->timing.v_total;
1135 } else if (in_out_vrr->state == VRR_STATE_INACTIVE) {
1136 in_out_vrr->adjust.v_total_min = stream->timing.v_total;
1137 in_out_vrr->adjust.v_total_max = stream->timing.v_total;
1138 } else if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE &&
1139 refresh_range >= MIN_REFRESH_RANGE) {
1140
1141 in_out_vrr->adjust.v_total_min =
1142 mod_freesync_calc_v_total_from_refresh(stream,
1143 in_out_vrr->max_refresh_in_uhz);
1144 in_out_vrr->adjust.v_total_max =
1145 mod_freesync_calc_v_total_from_refresh(stream,
1146 in_out_vrr->min_refresh_in_uhz);
1147 } else if (in_out_vrr->state == VRR_STATE_ACTIVE_FIXED) {
1148 in_out_vrr->fixed.target_refresh_in_uhz =
1149 in_out_vrr->fixed_refresh_in_uhz;
1150 if (in_out_vrr->fixed.ramping_active &&
1151 in_out_vrr->fixed.fixed_active) {
1152
1153
1154
1155 in_out_vrr->fixed.fixed_active = true;
1156 } else {
1157 in_out_vrr->fixed.fixed_active = true;
1158 in_out_vrr->adjust.v_total_min =
1159 mod_freesync_calc_v_total_from_refresh(stream,
1160 in_out_vrr->fixed.target_refresh_in_uhz);
1161 in_out_vrr->adjust.v_total_max =
1162 in_out_vrr->adjust.v_total_min;
1163 }
1164 } else {
1165 in_out_vrr->state = VRR_STATE_INACTIVE;
1166 in_out_vrr->adjust.v_total_min = stream->timing.v_total;
1167 in_out_vrr->adjust.v_total_max = stream->timing.v_total;
1168 }
1169}
1170
1171void mod_freesync_handle_preflip(struct mod_freesync *mod_freesync,
1172 const struct dc_plane_state *plane,
1173 const struct dc_stream_state *stream,
1174 unsigned int curr_time_stamp_in_us,
1175 struct mod_vrr_params *in_out_vrr)
1176{
1177 struct core_freesync *core_freesync = NULL;
1178 unsigned int last_render_time_in_us = 0;
1179 unsigned int average_render_time_in_us = 0;
1180
1181 if (mod_freesync == NULL)
1182 return;
1183
1184 core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
1185
1186 if (in_out_vrr->supported &&
1187 in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE) {
1188 unsigned int i = 0;
1189 unsigned int oldest_index = plane->time.index + 1;
1190
1191 if (oldest_index >= DC_PLANE_UPDATE_TIMES_MAX)
1192 oldest_index = 0;
1193
1194 last_render_time_in_us = curr_time_stamp_in_us -
1195 plane->time.prev_update_time_in_us;
1196
1197
1198 for (i = 0; i < DC_PLANE_UPDATE_TIMES_MAX; i++) {
1199 average_render_time_in_us +=
1200 plane->time.time_elapsed_in_us[i];
1201 }
1202 average_render_time_in_us -=
1203 plane->time.time_elapsed_in_us[oldest_index];
1204
1205
1206 average_render_time_in_us += last_render_time_in_us;
1207 average_render_time_in_us /= DC_PLANE_UPDATE_TIMES_MAX;
1208
1209 if (in_out_vrr->btr.btr_enabled) {
1210 apply_below_the_range(core_freesync,
1211 stream,
1212 last_render_time_in_us,
1213 in_out_vrr);
1214 } else {
1215 apply_fixed_refresh(core_freesync,
1216 stream,
1217 last_render_time_in_us,
1218 in_out_vrr);
1219 }
1220
1221 determine_flip_interval_workaround_req(in_out_vrr,
1222 curr_time_stamp_in_us);
1223
1224 }
1225}
1226
1227void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync,
1228 const struct dc_stream_state *stream,
1229 struct mod_vrr_params *in_out_vrr)
1230{
1231 struct core_freesync *core_freesync = NULL;
1232 unsigned int cur_timestamp_in_us;
1233 unsigned long long cur_tick;
1234
1235 if ((mod_freesync == NULL) || (stream == NULL) || (in_out_vrr == NULL))
1236 return;
1237
1238 core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
1239
1240 if (in_out_vrr->supported == false)
1241 return;
1242
1243 cur_tick = dm_get_timestamp(core_freesync->dc->ctx);
1244 cur_timestamp_in_us = (unsigned int)
1245 div_u64(dm_get_elapse_time_in_ns(core_freesync->dc->ctx, cur_tick, 0), 1000);
1246
1247 in_out_vrr->flip_interval.vsyncs_between_flip++;
1248 in_out_vrr->flip_interval.v_update_timestamp_in_us = cur_timestamp_in_us;
1249
1250 if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE &&
1251 (in_out_vrr->flip_interval.flip_interval_workaround_active ||
1252 (!in_out_vrr->flip_interval.flip_interval_workaround_active &&
1253 in_out_vrr->flip_interval.program_flip_interval_workaround))) {
1254
1255 in_out_vrr->adjust.v_total_min =
1256 mod_freesync_calc_v_total_from_refresh(
1257 stream, in_out_vrr->max_refresh_in_uhz);
1258 in_out_vrr->adjust.v_total_max =
1259 in_out_vrr->adjust.v_total_min;
1260 in_out_vrr->flip_interval.program_flip_interval_workaround = false;
1261 in_out_vrr->flip_interval.do_flip_interval_workaround_cleanup = true;
1262 return;
1263 }
1264
1265 if (in_out_vrr->state != VRR_STATE_ACTIVE_VARIABLE &&
1266 in_out_vrr->flip_interval.do_flip_interval_workaround_cleanup) {
1267 in_out_vrr->flip_interval.do_flip_interval_workaround_cleanup = false;
1268 in_out_vrr->flip_interval.flip_interval_detect_counter = 0;
1269 in_out_vrr->flip_interval.vsyncs_between_flip = 0;
1270 in_out_vrr->flip_interval.vsync_to_flip_in_us = 0;
1271 }
1272
1273
1274
1275
1276 if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE &&
1277 in_out_vrr->btr.btr_active) {
1278
1279
1280
1281
1282
1283
1284
1285 if (in_out_vrr->btr.frames_to_insert ==
1286 in_out_vrr->btr.frame_counter) {
1287 in_out_vrr->adjust.v_total_min =
1288 calc_v_total_from_duration(stream,
1289 in_out_vrr,
1290 in_out_vrr->btr.inserted_duration_in_us);
1291 in_out_vrr->adjust.v_total_max =
1292 in_out_vrr->adjust.v_total_min;
1293 }
1294
1295 if (in_out_vrr->btr.frame_counter > 0)
1296 in_out_vrr->btr.frame_counter--;
1297
1298
1299 if (in_out_vrr->btr.frame_counter == 0) {
1300 in_out_vrr->adjust.v_total_min =
1301 mod_freesync_calc_v_total_from_refresh(stream,
1302 in_out_vrr->max_refresh_in_uhz);
1303 in_out_vrr->adjust.v_total_max =
1304 mod_freesync_calc_v_total_from_refresh(stream,
1305 in_out_vrr->min_refresh_in_uhz);
1306 }
1307 }
1308
1309
1310
1311
1312 if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE)
1313 in_out_vrr->fixed.ramping_active = false;
1314
1315
1316
1317
1318 if (in_out_vrr->state == VRR_STATE_ACTIVE_FIXED &&
1319 in_out_vrr->fixed.ramping_active) {
1320 update_v_total_for_static_ramp(
1321 core_freesync, stream, in_out_vrr);
1322 }
1323}
1324
1325void mod_freesync_get_settings(struct mod_freesync *mod_freesync,
1326 const struct mod_vrr_params *vrr,
1327 unsigned int *v_total_min, unsigned int *v_total_max,
1328 unsigned int *event_triggers,
1329 unsigned int *window_min, unsigned int *window_max,
1330 unsigned int *lfc_mid_point_in_us,
1331 unsigned int *inserted_frames,
1332 unsigned int *inserted_duration_in_us)
1333{
1334 if (mod_freesync == NULL)
1335 return;
1336
1337 if (vrr->supported) {
1338 *v_total_min = vrr->adjust.v_total_min;
1339 *v_total_max = vrr->adjust.v_total_max;
1340 *event_triggers = 0;
1341 *lfc_mid_point_in_us = vrr->btr.mid_point_in_us;
1342 *inserted_frames = vrr->btr.frames_to_insert;
1343 *inserted_duration_in_us = vrr->btr.inserted_duration_in_us;
1344 }
1345}
1346
1347unsigned long long mod_freesync_calc_nominal_field_rate(
1348 const struct dc_stream_state *stream)
1349{
1350 unsigned long long nominal_field_rate_in_uhz = 0;
1351 unsigned int total = stream->timing.h_total * stream->timing.v_total;
1352
1353
1354 nominal_field_rate_in_uhz = stream->timing.pix_clk_100hz;
1355 nominal_field_rate_in_uhz *= 100000000ULL;
1356
1357 nominal_field_rate_in_uhz = div_u64(nominal_field_rate_in_uhz, total);
1358
1359 return nominal_field_rate_in_uhz;
1360}
1361
1362unsigned long long mod_freesync_calc_field_rate_from_timing(
1363 unsigned int vtotal, unsigned int htotal, unsigned int pix_clk)
1364{
1365 unsigned long long field_rate_in_uhz = 0;
1366 unsigned int total = htotal * vtotal;
1367
1368
1369 field_rate_in_uhz = pix_clk;
1370 field_rate_in_uhz *= 1000000ULL;
1371
1372 field_rate_in_uhz = div_u64(field_rate_in_uhz, total);
1373
1374 return field_rate_in_uhz;
1375}
1376
1377bool mod_freesync_is_valid_range(uint32_t min_refresh_cap_in_uhz,
1378 uint32_t max_refresh_cap_in_uhz,
1379 uint32_t nominal_field_rate_in_uhz)
1380{
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415 nominal_field_rate_in_uhz =
1416 div_u64(nominal_field_rate_in_uhz + 500000, 1000000);
1417 min_refresh_cap_in_uhz /= 1000000;
1418 max_refresh_cap_in_uhz /= 1000000;
1419
1420
1421 if (nominal_field_rate_in_uhz > max_refresh_cap_in_uhz ||
1422 nominal_field_rate_in_uhz < min_refresh_cap_in_uhz)
1423 return false;
1424
1425
1426 if (nominal_field_rate_in_uhz < max_refresh_cap_in_uhz)
1427 max_refresh_cap_in_uhz = nominal_field_rate_in_uhz;
1428
1429
1430 if (min_refresh_cap_in_uhz > max_refresh_cap_in_uhz)
1431 return false;
1432
1433
1434 if (nominal_field_rate_in_uhz - min_refresh_cap_in_uhz < 10)
1435 return false;
1436
1437 return true;
1438}
1439