diff options
author | Harry Wentland <harry.wentland@amd.com> | 2017-09-12 15:58:20 -0400 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2017-09-26 17:01:32 -0400 |
commit | 4562236b3bc0a28aeb6ee93b2d8a849a4c4e1c7c (patch) | |
tree | 84301c04dcaaa05c3318a8fe62cf62ab52ecc162 /drivers/gpu/drm/amd/display/modules/freesync/freesync.c | |
parent | 9c5b2b0d409304c2e3c1f4d1c9bb4958e1d46f8f (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.c | 1158 |
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 | |||
43 | struct 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 | |||
49 | struct 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 | |||
63 | struct 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 | |||
74 | struct fixed_refresh { | ||
75 | bool fixed_refresh_active; | ||
76 | bool program_fixed_refresh; | ||
77 | }; | ||
78 | |||
79 | struct 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 | |||
94 | struct 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 | |||
101 | struct 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 | |||
111 | static 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 | |||
119 | struct 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 | |||
160 | fail_construct: | ||
161 | dm_free(core_freesync->map); | ||
162 | |||
163 | fail_alloc_map: | ||
164 | dm_free(core_freesync); | ||
165 | |||
166 | fail_alloc_context: | ||
167 | return NULL; | ||
168 | } | ||
169 | |||
170 | void 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 | */ | ||
190 | static 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 | |||
205 | bool 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 | |||
270 | bool 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 | |||
287 | static 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 | |||
313 | static 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 | |||
325 | static 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 | |||
347 | static 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 | |||
355 | static 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 | |||
430 | static 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 | */ | ||
451 | static 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 | |||
577 | static 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 | |||
621 | void 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 | |||
690 | void 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 | |||
758 | bool 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 | |||
787 | bool 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 | |||
835 | bool 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 | |||
849 | void 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 | */ | ||
911 | static 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 | |||
966 | static 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 | |||
1082 | static 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 | |||
1113 | void 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 | } | ||