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