]>
Commit | Line | Data |
---|---|---|
4562236b HW |
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" | |
4562236b HW |
30 | |
31 | #define MOD_FREESYNC_MAX_CONCURRENT_STREAMS 32 | |
32 | ||
33 | /* Refresh rate ramp at a fixed rate of 65 Hz/second */ | |
34 | #define STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME ((1000 / 60) * 65) | |
35 | /* Number of elements in the render times cache array */ | |
36 | #define RENDER_TIMES_MAX_COUNT 20 | |
37 | /* Threshold to exit BTR (to avoid frequent enter-exits at the lower limit) */ | |
38 | #define BTR_EXIT_MARGIN 2000 | |
d09fec0f AK |
39 | /* Number of consecutive frames to check before entering/exiting fixed refresh*/ |
40 | #define FIXED_REFRESH_ENTER_FRAME_COUNT 5 | |
41 | #define FIXED_REFRESH_EXIT_FRAME_COUNT 5 | |
4562236b HW |
42 | |
43 | #define FREESYNC_REGISTRY_NAME "freesync_v1" | |
44 | ||
2233ec72 AK |
45 | #define FREESYNC_NO_STATIC_FOR_EXTERNAL_DP_REGKEY "DalFreeSyncNoStaticForExternalDp" |
46 | ||
47 | #define FREESYNC_NO_STATIC_FOR_INTERNAL_REGKEY "DalFreeSyncNoStaticForInternal" | |
48 | ||
4562236b HW |
49 | struct gradual_static_ramp { |
50 | bool ramp_is_active; | |
51 | bool ramp_direction_is_up; | |
52 | unsigned int ramp_current_frame_duration_in_ns; | |
53 | }; | |
54 | ||
55 | struct time_cache { | |
56 | /* video (48Hz feature) related */ | |
57 | unsigned int update_duration_in_ns; | |
58 | ||
59 | /* BTR/fixed refresh related */ | |
60 | unsigned int prev_time_stamp_in_us; | |
61 | ||
62 | unsigned int min_render_time_in_us; | |
63 | unsigned int max_render_time_in_us; | |
64 | ||
65 | unsigned int render_times_index; | |
66 | unsigned int render_times[RENDER_TIMES_MAX_COUNT]; | |
67 | }; | |
68 | ||
69 | struct below_the_range { | |
70 | bool btr_active; | |
71 | bool program_btr; | |
72 | ||
73 | unsigned int mid_point_in_us; | |
74 | ||
75 | unsigned int inserted_frame_duration_in_us; | |
76 | unsigned int frames_to_insert; | |
77 | unsigned int frame_counter; | |
78 | }; | |
79 | ||
80 | struct fixed_refresh { | |
d09fec0f AK |
81 | bool fixed_active; |
82 | bool program_fixed; | |
83 | unsigned int frame_counter; | |
4562236b HW |
84 | }; |
85 | ||
72ada5f7 EC |
86 | struct freesync_range { |
87 | unsigned int min_refresh; | |
88 | unsigned int max_frame_duration; | |
89 | unsigned int vmax; | |
90 | ||
91 | unsigned int max_refresh; | |
92 | unsigned int min_frame_duration; | |
93 | unsigned int vmin; | |
94 | }; | |
95 | ||
4562236b HW |
96 | struct freesync_state { |
97 | bool fullscreen; | |
98 | bool static_screen; | |
99 | bool video; | |
100 | ||
101 | unsigned int nominal_refresh_rate_in_micro_hz; | |
102 | bool windowed_fullscreen; | |
103 | ||
104 | struct time_cache time; | |
105 | ||
106 | struct gradual_static_ramp static_ramp; | |
107 | struct below_the_range btr; | |
108 | struct fixed_refresh fixed_refresh; | |
72ada5f7 | 109 | struct freesync_range freesync_range; |
4562236b HW |
110 | }; |
111 | ||
112 | struct freesync_entity { | |
0971c40e | 113 | struct dc_stream_state *stream; |
4562236b HW |
114 | struct mod_freesync_caps *caps; |
115 | struct freesync_state state; | |
116 | struct mod_freesync_user_enable user_enable; | |
117 | }; | |
118 | ||
9e594f4c | 119 | struct freesync_registry_options { |
2233ec72 AK |
120 | bool drr_external_supported; |
121 | bool drr_internal_supported; | |
9e594f4c EC |
122 | }; |
123 | ||
4562236b HW |
124 | struct core_freesync { |
125 | struct mod_freesync public; | |
126 | struct dc *dc; | |
127 | struct freesync_entity *map; | |
128 | int num_entities; | |
9e594f4c | 129 | struct freesync_registry_options opts; |
4562236b HW |
130 | }; |
131 | ||
132 | #define MOD_FREESYNC_TO_CORE(mod_freesync)\ | |
133 | container_of(mod_freesync, struct core_freesync, public) | |
134 | ||
135 | static bool check_dc_support(const struct dc *dc) | |
136 | { | |
137 | if (dc->stream_funcs.adjust_vmin_vmax == NULL) | |
138 | return false; | |
139 | ||
140 | return true; | |
141 | } | |
142 | ||
143 | struct mod_freesync *mod_freesync_create(struct dc *dc) | |
144 | { | |
145 | struct core_freesync *core_freesync = | |
2004f45e | 146 | kzalloc(sizeof(struct core_freesync), GFP_KERNEL); |
4562236b | 147 | |
4562236b HW |
148 | |
149 | struct persistent_data_flag flag; | |
150 | ||
9e594f4c | 151 | int i, data = 0; |
4562236b HW |
152 | |
153 | if (core_freesync == NULL) | |
154 | goto fail_alloc_context; | |
155 | ||
2004f45e HW |
156 | core_freesync->map = kzalloc(sizeof(struct freesync_entity) * MOD_FREESYNC_MAX_CONCURRENT_STREAMS, |
157 | GFP_KERNEL); | |
4562236b HW |
158 | |
159 | if (core_freesync->map == NULL) | |
160 | goto fail_alloc_map; | |
161 | ||
162 | for (i = 0; i < MOD_FREESYNC_MAX_CONCURRENT_STREAMS; i++) | |
163 | core_freesync->map[i].stream = NULL; | |
164 | ||
165 | core_freesync->num_entities = 0; | |
166 | ||
167 | if (dc == NULL) | |
168 | goto fail_construct; | |
169 | ||
170 | core_freesync->dc = dc; | |
171 | ||
172 | if (!check_dc_support(dc)) | |
173 | goto fail_construct; | |
174 | ||
175 | /* Create initial module folder in registry for freesync enable data */ | |
176 | flag.save_per_edid = true; | |
177 | flag.save_per_link = false; | |
15659045 | 178 | dm_write_persistent_data(dc->ctx, NULL, FREESYNC_REGISTRY_NAME, |
d09fec0f | 179 | NULL, NULL, 0, &flag); |
9e594f4c EC |
180 | flag.save_per_edid = false; |
181 | flag.save_per_link = false; | |
2233ec72 | 182 | |
15659045 | 183 | if (dm_read_persistent_data(dc->ctx, NULL, NULL, |
2233ec72 AK |
184 | FREESYNC_NO_STATIC_FOR_INTERNAL_REGKEY, |
185 | &data, sizeof(data), &flag)) { | |
186 | core_freesync->opts.drr_internal_supported = | |
187 | (data & 1) ? false : true; | |
188 | } | |
189 | ||
15659045 | 190 | if (dm_read_persistent_data(dc->ctx, NULL, NULL, |
2233ec72 AK |
191 | FREESYNC_NO_STATIC_FOR_EXTERNAL_DP_REGKEY, |
192 | &data, sizeof(data), &flag)) { | |
193 | core_freesync->opts.drr_external_supported = | |
194 | (data & 1) ? false : true; | |
9e594f4c | 195 | } |
4562236b HW |
196 | |
197 | return &core_freesync->public; | |
198 | ||
199 | fail_construct: | |
2004f45e | 200 | kfree(core_freesync->map); |
4562236b HW |
201 | |
202 | fail_alloc_map: | |
2004f45e | 203 | kfree(core_freesync); |
4562236b HW |
204 | |
205 | fail_alloc_context: | |
206 | return NULL; | |
207 | } | |
208 | ||
209 | void mod_freesync_destroy(struct mod_freesync *mod_freesync) | |
210 | { | |
211 | if (mod_freesync != NULL) { | |
212 | int i; | |
213 | struct core_freesync *core_freesync = | |
214 | MOD_FREESYNC_TO_CORE(mod_freesync); | |
215 | ||
216 | for (i = 0; i < core_freesync->num_entities; i++) | |
217 | if (core_freesync->map[i].stream) | |
218 | dc_stream_release(core_freesync->map[i].stream); | |
219 | ||
2004f45e | 220 | kfree(core_freesync->map); |
4562236b | 221 | |
2004f45e | 222 | kfree(core_freesync); |
4562236b HW |
223 | } |
224 | } | |
225 | ||
226 | /* Given a specific dc_stream* this function finds its equivalent | |
227 | * on the core_freesync->map and returns the corresponding index | |
228 | */ | |
229 | static unsigned int map_index_from_stream(struct core_freesync *core_freesync, | |
0971c40e | 230 | struct dc_stream_state *stream) |
4562236b HW |
231 | { |
232 | unsigned int index = 0; | |
233 | ||
234 | for (index = 0; index < core_freesync->num_entities; index++) { | |
235 | if (core_freesync->map[index].stream == stream) { | |
236 | return index; | |
237 | } | |
238 | } | |
239 | /* Could not find stream requested */ | |
240 | ASSERT(false); | |
241 | return index; | |
242 | } | |
243 | ||
244 | bool mod_freesync_add_stream(struct mod_freesync *mod_freesync, | |
0971c40e | 245 | struct dc_stream_state *stream, struct mod_freesync_caps *caps) |
4562236b | 246 | { |
15659045 | 247 | struct dc *dc = NULL; |
7a1c37e0 | 248 | struct core_freesync *core_freesync = NULL; |
ba624cdd | 249 | int persistent_freesync_enable = 0; |
c7141c47 | 250 | struct persistent_data_flag flag; |
2233ec72 | 251 | unsigned int nom_refresh_rate_uhz; |
4a9054dd | 252 | unsigned long long temp; |
7a1c37e0 AK |
253 | |
254 | if (mod_freesync == NULL) | |
255 | return false; | |
256 | ||
257 | core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); | |
15659045 | 258 | dc = core_freesync->dc; |
4562236b | 259 | |
4562236b HW |
260 | flag.save_per_edid = true; |
261 | flag.save_per_link = false; | |
262 | ||
263 | if (core_freesync->num_entities < MOD_FREESYNC_MAX_CONCURRENT_STREAMS) { | |
264 | ||
265 | dc_stream_retain(stream); | |
266 | ||
9e594f4c EC |
267 | temp = stream->timing.pix_clk_khz; |
268 | temp *= 1000ULL * 1000ULL * 1000ULL; | |
269 | temp = div_u64(temp, stream->timing.h_total); | |
270 | temp = div_u64(temp, stream->timing.v_total); | |
271 | ||
2233ec72 | 272 | nom_refresh_rate_uhz = (unsigned int) temp; |
9e594f4c | 273 | |
4562236b HW |
274 | core_freesync->map[core_freesync->num_entities].stream = stream; |
275 | core_freesync->map[core_freesync->num_entities].caps = caps; | |
276 | ||
277 | core_freesync->map[core_freesync->num_entities].state. | |
278 | fullscreen = false; | |
279 | core_freesync->map[core_freesync->num_entities].state. | |
280 | static_screen = false; | |
281 | core_freesync->map[core_freesync->num_entities].state. | |
282 | video = false; | |
283 | core_freesync->map[core_freesync->num_entities].state.time. | |
284 | update_duration_in_ns = 0; | |
285 | core_freesync->map[core_freesync->num_entities].state. | |
286 | static_ramp.ramp_is_active = false; | |
287 | ||
288 | /* get persistent data from registry */ | |
15659045 | 289 | if (dm_read_persistent_data(dc->ctx, stream->sink, |
4562236b HW |
290 | FREESYNC_REGISTRY_NAME, |
291 | "userenable", &persistent_freesync_enable, | |
292 | sizeof(int), &flag)) { | |
293 | core_freesync->map[core_freesync->num_entities].user_enable. | |
294 | enable_for_gaming = | |
295 | (persistent_freesync_enable & 1) ? true : false; | |
296 | core_freesync->map[core_freesync->num_entities].user_enable. | |
297 | enable_for_static = | |
298 | (persistent_freesync_enable & 2) ? true : false; | |
299 | core_freesync->map[core_freesync->num_entities].user_enable. | |
300 | enable_for_video = | |
301 | (persistent_freesync_enable & 4) ? true : false; | |
302 | } else { | |
303 | core_freesync->map[core_freesync->num_entities].user_enable. | |
304 | enable_for_gaming = false; | |
305 | core_freesync->map[core_freesync->num_entities].user_enable. | |
306 | enable_for_static = false; | |
307 | core_freesync->map[core_freesync->num_entities].user_enable. | |
308 | enable_for_video = false; | |
309 | } | |
310 | ||
4a9054dd | 311 | if (caps->supported && |
2233ec72 AK |
312 | nom_refresh_rate_uhz >= caps->min_refresh_in_micro_hz && |
313 | nom_refresh_rate_uhz <= caps->max_refresh_in_micro_hz) | |
4fa086b9 | 314 | stream->ignore_msa_timing_param = 1; |
4562236b HW |
315 | |
316 | core_freesync->num_entities++; | |
317 | return true; | |
318 | } | |
319 | return false; | |
320 | } | |
321 | ||
322 | bool mod_freesync_remove_stream(struct mod_freesync *mod_freesync, | |
0971c40e | 323 | struct dc_stream_state *stream) |
4562236b | 324 | { |
4562236b | 325 | int i = 0; |
7a1c37e0 AK |
326 | struct core_freesync *core_freesync = NULL; |
327 | unsigned int index = 0; | |
328 | ||
329 | if (mod_freesync == NULL) | |
330 | return false; | |
331 | ||
332 | core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); | |
333 | index = map_index_from_stream(core_freesync, stream); | |
334 | ||
4562236b HW |
335 | dc_stream_release(core_freesync->map[index].stream); |
336 | core_freesync->map[index].stream = NULL; | |
337 | /* To remove this entity, shift everything after down */ | |
338 | for (i = index; i < core_freesync->num_entities - 1; i++) | |
339 | core_freesync->map[i] = core_freesync->map[i + 1]; | |
340 | core_freesync->num_entities--; | |
341 | return true; | |
342 | } | |
343 | ||
344 | static void update_stream_freesync_context(struct core_freesync *core_freesync, | |
0971c40e | 345 | struct dc_stream_state *stream) |
4562236b HW |
346 | { |
347 | unsigned int index; | |
348 | struct freesync_context *ctx; | |
4562236b | 349 | |
4fa086b9 | 350 | ctx = &stream->freesync_ctx; |
4562236b HW |
351 | |
352 | index = map_index_from_stream(core_freesync, stream); | |
353 | ||
354 | ctx->supported = core_freesync->map[index].caps->supported; | |
355 | ctx->enabled = (core_freesync->map[index].user_enable.enable_for_gaming || | |
356 | core_freesync->map[index].user_enable.enable_for_video || | |
357 | core_freesync->map[index].user_enable.enable_for_static); | |
358 | ctx->active = (core_freesync->map[index].state.fullscreen || | |
359 | core_freesync->map[index].state.video || | |
360 | core_freesync->map[index].state.static_ramp.ramp_is_active); | |
361 | ctx->min_refresh_in_micro_hz = | |
362 | core_freesync->map[index].caps->min_refresh_in_micro_hz; | |
363 | ctx->nominal_refresh_in_micro_hz = core_freesync-> | |
364 | map[index].state.nominal_refresh_rate_in_micro_hz; | |
365 | ||
366 | } | |
367 | ||
368 | static void update_stream(struct core_freesync *core_freesync, | |
0971c40e | 369 | struct dc_stream_state *stream) |
4562236b | 370 | { |
4562236b HW |
371 | unsigned int index = map_index_from_stream(core_freesync, stream); |
372 | if (core_freesync->map[index].caps->supported) { | |
4fa086b9 | 373 | stream->ignore_msa_timing_param = 1; |
4562236b HW |
374 | update_stream_freesync_context(core_freesync, stream); |
375 | } | |
376 | } | |
377 | ||
72ada5f7 | 378 | static void calc_freesync_range(struct core_freesync *core_freesync, |
0971c40e | 379 | struct dc_stream_state *stream, |
72ada5f7 EC |
380 | struct freesync_state *state, |
381 | unsigned int min_refresh_in_uhz, | |
382 | unsigned int max_refresh_in_uhz) | |
4562236b HW |
383 | { |
384 | unsigned int min_frame_duration_in_ns = 0, max_frame_duration_in_ns = 0; | |
385 | unsigned int index = map_index_from_stream(core_freesync, stream); | |
6c626ffb | 386 | uint32_t vtotal = stream->timing.v_total; |
4562236b | 387 | |
7cc9e7a6 EC |
388 | if ((min_refresh_in_uhz == 0) || (max_refresh_in_uhz == 0)) { |
389 | state->freesync_range.min_refresh = | |
390 | state->nominal_refresh_rate_in_micro_hz; | |
391 | state->freesync_range.max_refresh = | |
392 | state->nominal_refresh_rate_in_micro_hz; | |
393 | ||
394 | state->freesync_range.max_frame_duration = 0; | |
395 | state->freesync_range.min_frame_duration = 0; | |
396 | ||
397 | state->freesync_range.vmax = vtotal; | |
398 | state->freesync_range.vmin = vtotal; | |
399 | ||
400 | return; | |
401 | } | |
402 | ||
4562236b HW |
403 | min_frame_duration_in_ns = ((unsigned int) (div64_u64( |
404 | (1000000000ULL * 1000000), | |
72ada5f7 | 405 | max_refresh_in_uhz))); |
4562236b | 406 | max_frame_duration_in_ns = ((unsigned int) (div64_u64( |
72ada5f7 EC |
407 | (1000000000ULL * 1000000), |
408 | min_refresh_in_uhz))); | |
409 | ||
410 | state->freesync_range.min_refresh = min_refresh_in_uhz; | |
411 | state->freesync_range.max_refresh = max_refresh_in_uhz; | |
4562236b | 412 | |
72ada5f7 EC |
413 | state->freesync_range.max_frame_duration = max_frame_duration_in_ns; |
414 | state->freesync_range.min_frame_duration = min_frame_duration_in_ns; | |
415 | ||
416 | state->freesync_range.vmax = div64_u64(div64_u64(((unsigned long long)( | |
417 | max_frame_duration_in_ns) * stream->timing.pix_clk_khz), | |
418 | stream->timing.h_total), 1000000); | |
419 | state->freesync_range.vmin = div64_u64(div64_u64(((unsigned long long)( | |
420 | min_frame_duration_in_ns) * stream->timing.pix_clk_khz), | |
421 | stream->timing.h_total), 1000000); | |
6c626ffb | 422 | |
d09fec0f | 423 | /* vmin/vmax cannot be less than vtotal */ |
72ada5f7 | 424 | if (state->freesync_range.vmin < vtotal) { |
ac5c2947 DL |
425 | /* Error of 1 is permissible */ |
426 | ASSERT((state->freesync_range.vmin + 1) >= vtotal); | |
72ada5f7 | 427 | state->freesync_range.vmin = vtotal; |
6c626ffb YS |
428 | } |
429 | ||
72ada5f7 | 430 | if (state->freesync_range.vmax < vtotal) { |
ac5c2947 DL |
431 | /* Error of 1 is permissible */ |
432 | ASSERT((state->freesync_range.vmax + 1) >= vtotal); | |
72ada5f7 | 433 | state->freesync_range.vmax = vtotal; |
6c626ffb | 434 | } |
72ada5f7 EC |
435 | |
436 | /* Determine whether BTR can be supported */ | |
1a87fbfe AZ |
437 | if (max_frame_duration_in_ns >= |
438 | 2 * min_frame_duration_in_ns) | |
439 | core_freesync->map[index].caps->btr_supported = true; | |
440 | else | |
441 | core_freesync->map[index].caps->btr_supported = false; | |
72ada5f7 EC |
442 | |
443 | /* Cache the time variables */ | |
444 | state->time.max_render_time_in_us = | |
445 | max_frame_duration_in_ns / 1000; | |
446 | state->time.min_render_time_in_us = | |
447 | min_frame_duration_in_ns / 1000; | |
448 | state->btr.mid_point_in_us = | |
449 | (max_frame_duration_in_ns + | |
450 | min_frame_duration_in_ns) / 2000; | |
4562236b HW |
451 | } |
452 | ||
0971c40e | 453 | static void calc_v_total_from_duration(struct dc_stream_state *stream, |
4562236b HW |
454 | unsigned int duration_in_ns, int *v_total_nominal) |
455 | { | |
456 | *v_total_nominal = div64_u64(div64_u64(((unsigned long long)( | |
457 | duration_in_ns) * stream->timing.pix_clk_khz), | |
458 | stream->timing.h_total), 1000000); | |
459 | } | |
460 | ||
461 | static void calc_v_total_for_static_ramp(struct core_freesync *core_freesync, | |
0971c40e | 462 | struct dc_stream_state *stream, |
4562236b HW |
463 | unsigned int index, int *v_total) |
464 | { | |
465 | unsigned int frame_duration = 0; | |
466 | ||
467 | struct gradual_static_ramp *static_ramp_variables = | |
468 | &core_freesync->map[index].state.static_ramp; | |
469 | ||
470 | /* Calc ratio between new and current frame duration with 3 digit */ | |
471 | unsigned int frame_duration_ratio = div64_u64(1000000, | |
472 | (1000 + div64_u64(((unsigned long long)( | |
473 | STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME) * | |
474 | static_ramp_variables->ramp_current_frame_duration_in_ns), | |
475 | 1000000000))); | |
476 | ||
477 | /* Calculate delta between new and current frame duration in ns */ | |
478 | unsigned int frame_duration_delta = div64_u64(((unsigned long long)( | |
479 | static_ramp_variables->ramp_current_frame_duration_in_ns) * | |
480 | (1000 - frame_duration_ratio)), 1000); | |
481 | ||
482 | /* Adjust frame duration delta based on ratio between current and | |
483 | * standard frame duration (frame duration at 60 Hz refresh rate). | |
484 | */ | |
485 | unsigned int ramp_rate_interpolated = div64_u64(((unsigned long long)( | |
486 | frame_duration_delta) * static_ramp_variables-> | |
487 | ramp_current_frame_duration_in_ns), 16666666); | |
488 | ||
489 | /* Going to a higher refresh rate (lower frame duration) */ | |
490 | if (static_ramp_variables->ramp_direction_is_up) { | |
491 | /* reduce frame duration */ | |
492 | static_ramp_variables->ramp_current_frame_duration_in_ns -= | |
493 | ramp_rate_interpolated; | |
494 | ||
495 | /* min frame duration */ | |
496 | frame_duration = ((unsigned int) (div64_u64( | |
497 | (1000000000ULL * 1000000), | |
498 | core_freesync->map[index].state. | |
499 | nominal_refresh_rate_in_micro_hz))); | |
500 | ||
501 | /* adjust for frame duration below min */ | |
502 | if (static_ramp_variables->ramp_current_frame_duration_in_ns <= | |
503 | frame_duration) { | |
504 | ||
505 | static_ramp_variables->ramp_is_active = false; | |
506 | static_ramp_variables-> | |
507 | ramp_current_frame_duration_in_ns = | |
508 | frame_duration; | |
509 | } | |
510 | /* Going to a lower refresh rate (larger frame duration) */ | |
511 | } else { | |
512 | /* increase frame duration */ | |
513 | static_ramp_variables->ramp_current_frame_duration_in_ns += | |
514 | ramp_rate_interpolated; | |
515 | ||
516 | /* max frame duration */ | |
517 | frame_duration = ((unsigned int) (div64_u64( | |
518 | (1000000000ULL * 1000000), | |
519 | core_freesync->map[index].caps->min_refresh_in_micro_hz))); | |
520 | ||
521 | /* adjust for frame duration above max */ | |
522 | if (static_ramp_variables->ramp_current_frame_duration_in_ns >= | |
523 | frame_duration) { | |
524 | ||
525 | static_ramp_variables->ramp_is_active = false; | |
526 | static_ramp_variables-> | |
527 | ramp_current_frame_duration_in_ns = | |
528 | frame_duration; | |
529 | } | |
530 | } | |
531 | ||
532 | calc_v_total_from_duration(stream, static_ramp_variables-> | |
533 | ramp_current_frame_duration_in_ns, v_total); | |
534 | } | |
535 | ||
536 | static void reset_freesync_state_variables(struct freesync_state* state) | |
537 | { | |
538 | state->static_ramp.ramp_is_active = false; | |
539 | if (state->nominal_refresh_rate_in_micro_hz) | |
540 | state->static_ramp.ramp_current_frame_duration_in_ns = | |
541 | ((unsigned int) (div64_u64( | |
542 | (1000000000ULL * 1000000), | |
543 | state->nominal_refresh_rate_in_micro_hz))); | |
544 | ||
545 | state->btr.btr_active = false; | |
546 | state->btr.frame_counter = 0; | |
547 | state->btr.frames_to_insert = 0; | |
548 | state->btr.inserted_frame_duration_in_us = 0; | |
549 | state->btr.program_btr = false; | |
550 | ||
d09fec0f AK |
551 | state->fixed_refresh.fixed_active = false; |
552 | state->fixed_refresh.program_fixed = false; | |
4562236b HW |
553 | } |
554 | /* | |
555 | * Sets freesync mode on a stream depending on current freesync state. | |
556 | */ | |
557 | static bool set_freesync_on_streams(struct core_freesync *core_freesync, | |
0971c40e | 558 | struct dc_stream_state **streams, int num_streams) |
4562236b HW |
559 | { |
560 | int v_total_nominal = 0, v_total_min = 0, v_total_max = 0; | |
561 | unsigned int stream_idx, map_index = 0; | |
562 | struct freesync_state *state; | |
563 | ||
564 | if (num_streams == 0 || streams == NULL || num_streams > 1) | |
565 | return false; | |
566 | ||
567 | for (stream_idx = 0; stream_idx < num_streams; stream_idx++) { | |
568 | ||
569 | map_index = map_index_from_stream(core_freesync, | |
570 | streams[stream_idx]); | |
571 | ||
572 | state = &core_freesync->map[map_index].state; | |
573 | ||
574 | if (core_freesync->map[map_index].caps->supported) { | |
575 | ||
576 | /* Fullscreen has the topmost priority. If the | |
577 | * fullscreen bit is set, we are in a fullscreen | |
578 | * application where it should not matter if it is | |
579 | * static screen. We should not check the static_screen | |
580 | * or video bit. | |
581 | * | |
582 | * Special cases of fullscreen include btr and fixed | |
583 | * refresh. We program btr on every flip and involves | |
584 | * programming full range right before the last inserted frame. | |
585 | * However, we do not want to program the full freesync range | |
586 | * when fixed refresh is active, because we only program | |
587 | * that logic once and this will override it. | |
588 | */ | |
589 | if (core_freesync->map[map_index].user_enable. | |
590 | enable_for_gaming == true && | |
591 | state->fullscreen == true && | |
d09fec0f | 592 | state->fixed_refresh.fixed_active == false) { |
4562236b HW |
593 | /* Enable freesync */ |
594 | ||
72ada5f7 EC |
595 | v_total_min = state->freesync_range.vmin; |
596 | v_total_max = state->freesync_range.vmax; | |
4562236b HW |
597 | |
598 | /* Update the freesync context for the stream */ | |
599 | update_stream_freesync_context(core_freesync, | |
600 | streams[stream_idx]); | |
601 | ||
602 | core_freesync->dc->stream_funcs. | |
603 | adjust_vmin_vmax(core_freesync->dc, streams, | |
604 | num_streams, v_total_min, | |
605 | v_total_max); | |
606 | ||
607 | return true; | |
608 | ||
609 | } else if (core_freesync->map[map_index].user_enable. | |
610 | enable_for_video && state->video == true) { | |
611 | /* Enable 48Hz feature */ | |
612 | ||
613 | calc_v_total_from_duration(streams[stream_idx], | |
614 | state->time.update_duration_in_ns, | |
615 | &v_total_nominal); | |
616 | ||
617 | /* Program only if v_total_nominal is in range*/ | |
618 | if (v_total_nominal >= | |
619 | streams[stream_idx]->timing.v_total) { | |
620 | ||
621 | /* Update the freesync context for | |
622 | * the stream | |
623 | */ | |
624 | update_stream_freesync_context( | |
625 | core_freesync, | |
626 | streams[stream_idx]); | |
627 | ||
628 | core_freesync->dc->stream_funcs. | |
629 | adjust_vmin_vmax( | |
630 | core_freesync->dc, streams, | |
631 | num_streams, v_total_nominal, | |
632 | v_total_nominal); | |
633 | } | |
634 | return true; | |
635 | ||
636 | } else { | |
637 | /* Disable freesync */ | |
638 | v_total_nominal = streams[stream_idx]-> | |
639 | timing.v_total; | |
640 | ||
641 | /* Update the freesync context for | |
642 | * the stream | |
643 | */ | |
644 | update_stream_freesync_context( | |
645 | core_freesync, | |
646 | streams[stream_idx]); | |
647 | ||
648 | core_freesync->dc->stream_funcs. | |
649 | adjust_vmin_vmax( | |
650 | core_freesync->dc, streams, | |
651 | num_streams, v_total_nominal, | |
652 | v_total_nominal); | |
653 | ||
654 | /* Reset the cached variables */ | |
655 | reset_freesync_state_variables(state); | |
656 | ||
657 | return true; | |
658 | } | |
659 | } else { | |
660 | /* Disable freesync */ | |
661 | v_total_nominal = streams[stream_idx]-> | |
662 | timing.v_total; | |
663 | /* | |
664 | * we have to reset drr always even sink does | |
665 | * not support freesync because a former stream has | |
666 | * be programmed | |
667 | */ | |
668 | core_freesync->dc->stream_funcs. | |
669 | adjust_vmin_vmax( | |
670 | core_freesync->dc, streams, | |
671 | num_streams, v_total_nominal, | |
672 | v_total_nominal); | |
673 | /* Reset the cached variables */ | |
674 | reset_freesync_state_variables(state); | |
675 | } | |
676 | ||
677 | } | |
678 | ||
679 | return false; | |
680 | } | |
681 | ||
682 | static void set_static_ramp_variables(struct core_freesync *core_freesync, | |
683 | unsigned int index, bool enable_static_screen) | |
684 | { | |
685 | unsigned int frame_duration = 0; | |
fc82c5cb AZ |
686 | unsigned int nominal_refresh_rate = core_freesync->map[index].state. |
687 | nominal_refresh_rate_in_micro_hz; | |
688 | unsigned int min_refresh_rate= core_freesync->map[index].caps-> | |
689 | min_refresh_in_micro_hz; | |
4562236b HW |
690 | struct gradual_static_ramp *static_ramp_variables = |
691 | &core_freesync->map[index].state.static_ramp; | |
692 | ||
fc82c5cb AZ |
693 | /* If we are ENABLING static screen, refresh rate should go DOWN. |
694 | * If we are DISABLING static screen, refresh rate should go UP. | |
695 | */ | |
696 | if (enable_static_screen) | |
697 | static_ramp_variables->ramp_direction_is_up = false; | |
698 | else | |
699 | static_ramp_variables->ramp_direction_is_up = true; | |
700 | ||
4562236b HW |
701 | /* If ramp is not active, set initial frame duration depending on |
702 | * whether we are enabling/disabling static screen mode. If the ramp is | |
703 | * already active, ramp should continue in the opposite direction | |
704 | * starting with the current frame duration | |
705 | */ | |
706 | if (!static_ramp_variables->ramp_is_active) { | |
4562236b HW |
707 | if (enable_static_screen == true) { |
708 | /* Going to lower refresh rate, so start from max | |
709 | * refresh rate (min frame duration) | |
710 | */ | |
711 | frame_duration = ((unsigned int) (div64_u64( | |
712 | (1000000000ULL * 1000000), | |
fc82c5cb | 713 | nominal_refresh_rate))); |
4562236b HW |
714 | } else { |
715 | /* Going to higher refresh rate, so start from min | |
716 | * refresh rate (max frame duration) | |
717 | */ | |
718 | frame_duration = ((unsigned int) (div64_u64( | |
719 | (1000000000ULL * 1000000), | |
fc82c5cb | 720 | min_refresh_rate))); |
4562236b | 721 | } |
4562236b HW |
722 | static_ramp_variables-> |
723 | ramp_current_frame_duration_in_ns = frame_duration; | |
4562236b | 724 | |
fc82c5cb AZ |
725 | static_ramp_variables->ramp_is_active = true; |
726 | } | |
4562236b HW |
727 | } |
728 | ||
729 | void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, | |
0971c40e | 730 | struct dc_stream_state **streams, int num_streams) |
4562236b | 731 | { |
09e2d07f EC |
732 | unsigned int index, v_total, inserted_frame_v_total = 0; |
733 | unsigned int min_frame_duration_in_ns, vmax, vmin = 0; | |
4562236b | 734 | struct freesync_state *state; |
7a1c37e0 | 735 | struct core_freesync *core_freesync = NULL; |
94267b3d | 736 | struct dc_static_screen_events triggers = {0}; |
7a1c37e0 AK |
737 | |
738 | if (mod_freesync == NULL) | |
739 | return; | |
740 | ||
741 | core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); | |
4562236b HW |
742 | |
743 | if (core_freesync->num_entities == 0) | |
744 | return; | |
745 | ||
746 | index = map_index_from_stream(core_freesync, | |
747 | streams[0]); | |
748 | ||
749 | if (core_freesync->map[index].caps->supported == false) | |
750 | return; | |
751 | ||
752 | state = &core_freesync->map[index].state; | |
753 | ||
754 | /* Below the Range Logic */ | |
755 | ||
756 | /* Only execute if in fullscreen mode */ | |
757 | if (state->fullscreen == true && | |
09e2d07f EC |
758 | core_freesync->map[index].user_enable.enable_for_gaming && |
759 | core_freesync->map[index].caps->btr_supported && | |
760 | state->btr.btr_active) { | |
761 | ||
762 | /* TODO: pass in flag for Pre-DCE12 ASIC | |
763 | * in order for frame variable duration to take affect, | |
764 | * it needs to be done one VSYNC early, which is at | |
765 | * frameCounter == 1. | |
766 | * For DCE12 and newer updates to V_TOTAL_MIN/MAX | |
767 | * will take affect on current frame | |
768 | */ | |
769 | if (state->btr.frames_to_insert == state->btr.frame_counter) { | |
770 | ||
771 | min_frame_duration_in_ns = ((unsigned int) (div64_u64( | |
772 | (1000000000ULL * 1000000), | |
773 | state->nominal_refresh_rate_in_micro_hz))); | |
774 | ||
72ada5f7 | 775 | vmin = state->freesync_range.vmin; |
4562236b | 776 | |
09e2d07f | 777 | inserted_frame_v_total = vmin; |
4562236b | 778 | |
09e2d07f EC |
779 | if (min_frame_duration_in_ns / 1000) |
780 | inserted_frame_v_total = | |
781 | state->btr.inserted_frame_duration_in_us * | |
782 | vmin / (min_frame_duration_in_ns / 1000); | |
4562236b | 783 | |
09e2d07f EC |
784 | /* Set length of inserted frames as v_total_max*/ |
785 | vmax = inserted_frame_v_total; | |
786 | vmin = inserted_frame_v_total; | |
4562236b | 787 | |
09e2d07f EC |
788 | /* Program V_TOTAL */ |
789 | core_freesync->dc->stream_funcs.adjust_vmin_vmax( | |
790 | core_freesync->dc, streams, | |
791 | num_streams, vmin, vmax); | |
4562236b | 792 | } |
09e2d07f EC |
793 | |
794 | if (state->btr.frame_counter > 0) | |
795 | state->btr.frame_counter--; | |
796 | ||
797 | /* Restore FreeSync */ | |
798 | if (state->btr.frame_counter == 0) | |
799 | set_freesync_on_streams(core_freesync, streams, num_streams); | |
4562236b HW |
800 | } |
801 | ||
802 | /* If in fullscreen freesync mode or in video, do not program | |
803 | * static screen ramp values | |
804 | */ | |
805 | if (state->fullscreen == true || state->video == true) { | |
806 | ||
807 | state->static_ramp.ramp_is_active = false; | |
808 | ||
809 | return; | |
810 | } | |
811 | ||
812 | /* Gradual Static Screen Ramping Logic */ | |
813 | ||
814 | /* Execute if ramp is active and user enabled freesync static screen*/ | |
815 | if (state->static_ramp.ramp_is_active && | |
816 | core_freesync->map[index].user_enable.enable_for_static) { | |
817 | ||
818 | calc_v_total_for_static_ramp(core_freesync, streams[0], | |
819 | index, &v_total); | |
820 | ||
821 | /* Update the freesync context for the stream */ | |
822 | update_stream_freesync_context(core_freesync, streams[0]); | |
823 | ||
824 | /* Program static screen ramp values */ | |
825 | core_freesync->dc->stream_funcs.adjust_vmin_vmax( | |
826 | core_freesync->dc, streams, | |
827 | num_streams, v_total, | |
828 | v_total); | |
94267b3d ST |
829 | |
830 | triggers.overlay_update = true; | |
831 | triggers.surface_update = true; | |
832 | ||
833 | core_freesync->dc->stream_funcs.set_static_screen_events( | |
834 | core_freesync->dc, streams, num_streams, | |
835 | &triggers); | |
4562236b HW |
836 | } |
837 | } | |
838 | ||
839 | void mod_freesync_update_state(struct mod_freesync *mod_freesync, | |
0971c40e | 840 | struct dc_stream_state **streams, int num_streams, |
4562236b HW |
841 | struct mod_freesync_params *freesync_params) |
842 | { | |
4562236b HW |
843 | bool freesync_program_required = false; |
844 | unsigned int stream_index; | |
845 | struct freesync_state *state; | |
7a1c37e0 | 846 | struct core_freesync *core_freesync = NULL; |
fc82c5cb | 847 | struct dc_static_screen_events triggers = {0}; |
7a1c37e0 AK |
848 | |
849 | if (mod_freesync == NULL) | |
850 | return; | |
851 | ||
852 | core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); | |
4562236b HW |
853 | |
854 | if (core_freesync->num_entities == 0) | |
855 | return; | |
856 | ||
857 | for(stream_index = 0; stream_index < num_streams; stream_index++) { | |
858 | ||
859 | unsigned int map_index = map_index_from_stream(core_freesync, | |
860 | streams[stream_index]); | |
861 | ||
2233ec72 AK |
862 | bool is_embedded = dc_is_embedded_signal( |
863 | streams[stream_index]->sink->sink_signal); | |
864 | ||
865 | struct freesync_registry_options *opts = &core_freesync->opts; | |
866 | ||
4562236b HW |
867 | state = &core_freesync->map[map_index].state; |
868 | ||
869 | switch (freesync_params->state){ | |
870 | case FREESYNC_STATE_FULLSCREEN: | |
871 | state->fullscreen = freesync_params->enable; | |
872 | freesync_program_required = true; | |
873 | state->windowed_fullscreen = | |
874 | freesync_params->windowed_fullscreen; | |
875 | break; | |
876 | case FREESYNC_STATE_STATIC_SCREEN: | |
2233ec72 AK |
877 | /* Static screen ramp is disabled by default, but can |
878 | * be enabled through regkey. | |
4562236b | 879 | */ |
2233ec72 AK |
880 | if ((is_embedded && opts->drr_internal_supported) || |
881 | (!is_embedded && opts->drr_external_supported)) | |
882 | ||
883 | if (state->static_screen != | |
884 | freesync_params->enable) { | |
885 | ||
886 | /* Change the state flag */ | |
887 | state->static_screen = | |
888 | freesync_params->enable; | |
889 | ||
890 | /* Update static screen ramp */ | |
891 | set_static_ramp_variables(core_freesync, | |
4562236b HW |
892 | map_index, |
893 | freesync_params->enable); | |
2233ec72 | 894 | } |
4562236b HW |
895 | /* We program the ramp starting next VUpdate */ |
896 | break; | |
897 | case FREESYNC_STATE_VIDEO: | |
898 | /* Change core variables only if there is a change*/ | |
899 | if(freesync_params->update_duration_in_ns != | |
900 | state->time.update_duration_in_ns) { | |
901 | ||
902 | state->video = freesync_params->enable; | |
903 | state->time.update_duration_in_ns = | |
904 | freesync_params->update_duration_in_ns; | |
905 | ||
906 | freesync_program_required = true; | |
907 | } | |
908 | break; | |
eaf90944 HW |
909 | case FREESYNC_STATE_NONE: |
910 | /* handle here to avoid warning */ | |
911 | break; | |
4562236b HW |
912 | } |
913 | } | |
914 | ||
fc82c5cb AZ |
915 | /* Update mask */ |
916 | triggers.overlay_update = true; | |
917 | triggers.surface_update = true; | |
918 | ||
919 | core_freesync->dc->stream_funcs.set_static_screen_events( | |
920 | core_freesync->dc, streams, num_streams, | |
921 | &triggers); | |
922 | ||
4562236b HW |
923 | if (freesync_program_required) |
924 | /* Program freesync according to current state*/ | |
925 | set_freesync_on_streams(core_freesync, streams, num_streams); | |
926 | } | |
927 | ||
928 | ||
929 | bool mod_freesync_get_state(struct mod_freesync *mod_freesync, | |
0971c40e | 930 | struct dc_stream_state *stream, |
4562236b HW |
931 | struct mod_freesync_params *freesync_params) |
932 | { | |
c7141c47 | 933 | unsigned int index = 0; |
7a1c37e0 | 934 | struct core_freesync *core_freesync = NULL; |
4562236b | 935 | |
7a1c37e0 AK |
936 | if (mod_freesync == NULL) |
937 | return false; | |
938 | ||
939 | core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); | |
940 | index = map_index_from_stream(core_freesync, stream); | |
4562236b HW |
941 | |
942 | if (core_freesync->map[index].state.fullscreen) { | |
943 | freesync_params->state = FREESYNC_STATE_FULLSCREEN; | |
944 | freesync_params->enable = true; | |
945 | } else if (core_freesync->map[index].state.static_screen) { | |
946 | freesync_params->state = FREESYNC_STATE_STATIC_SCREEN; | |
947 | freesync_params->enable = true; | |
948 | } else if (core_freesync->map[index].state.video) { | |
949 | freesync_params->state = FREESYNC_STATE_VIDEO; | |
950 | freesync_params->enable = true; | |
951 | } else { | |
952 | freesync_params->state = FREESYNC_STATE_NONE; | |
953 | freesync_params->enable = false; | |
954 | } | |
955 | ||
956 | freesync_params->update_duration_in_ns = | |
957 | core_freesync->map[index].state.time.update_duration_in_ns; | |
958 | ||
4b5752c7 AW |
959 | freesync_params->windowed_fullscreen = |
960 | core_freesync->map[index].state.windowed_fullscreen; | |
961 | ||
4562236b HW |
962 | return true; |
963 | } | |
964 | ||
965 | bool mod_freesync_set_user_enable(struct mod_freesync *mod_freesync, | |
0971c40e | 966 | struct dc_stream_state **streams, int num_streams, |
4562236b HW |
967 | struct mod_freesync_user_enable *user_enable) |
968 | { | |
4562236b HW |
969 | unsigned int stream_index, map_index; |
970 | int persistent_data = 0; | |
971 | struct persistent_data_flag flag; | |
15659045 | 972 | struct dc *dc = NULL; |
7a1c37e0 AK |
973 | struct core_freesync *core_freesync = NULL; |
974 | ||
975 | if (mod_freesync == NULL) | |
976 | return false; | |
977 | ||
978 | core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); | |
15659045 | 979 | dc = core_freesync->dc; |
4562236b HW |
980 | |
981 | flag.save_per_edid = true; | |
982 | flag.save_per_link = false; | |
983 | ||
984 | for(stream_index = 0; stream_index < num_streams; | |
985 | stream_index++){ | |
986 | ||
987 | map_index = map_index_from_stream(core_freesync, | |
988 | streams[stream_index]); | |
989 | ||
990 | core_freesync->map[map_index].user_enable = *user_enable; | |
991 | ||
992 | /* Write persistent data in registry*/ | |
993 | if (core_freesync->map[map_index].user_enable. | |
994 | enable_for_gaming) | |
995 | persistent_data = persistent_data | 1; | |
996 | if (core_freesync->map[map_index].user_enable. | |
997 | enable_for_static) | |
998 | persistent_data = persistent_data | 2; | |
999 | if (core_freesync->map[map_index].user_enable. | |
1000 | enable_for_video) | |
1001 | persistent_data = persistent_data | 4; | |
1002 | ||
15659045 | 1003 | dm_write_persistent_data(dc->ctx, |
4562236b HW |
1004 | streams[stream_index]->sink, |
1005 | FREESYNC_REGISTRY_NAME, | |
1006 | "userenable", | |
1007 | &persistent_data, | |
1008 | sizeof(int), | |
1009 | &flag); | |
1010 | } | |
1011 | ||
1012 | set_freesync_on_streams(core_freesync, streams, num_streams); | |
1013 | ||
1014 | return true; | |
1015 | } | |
1016 | ||
1017 | bool mod_freesync_get_user_enable(struct mod_freesync *mod_freesync, | |
0971c40e | 1018 | struct dc_stream_state *stream, |
4562236b HW |
1019 | struct mod_freesync_user_enable *user_enable) |
1020 | { | |
7a1c37e0 AK |
1021 | unsigned int index = 0; |
1022 | struct core_freesync *core_freesync = NULL; | |
4562236b | 1023 | |
7a1c37e0 AK |
1024 | if (mod_freesync == NULL) |
1025 | return false; | |
1026 | ||
1027 | core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); | |
1028 | index = map_index_from_stream(core_freesync, stream); | |
4562236b HW |
1029 | |
1030 | *user_enable = core_freesync->map[index].user_enable; | |
1031 | ||
1032 | return true; | |
1033 | } | |
1034 | ||
1a87fbfe | 1035 | bool mod_freesync_get_static_ramp_active(struct mod_freesync *mod_freesync, |
0971c40e | 1036 | struct dc_stream_state *stream, |
1a87fbfe AZ |
1037 | bool *is_ramp_active) |
1038 | { | |
1039 | unsigned int index = 0; | |
1040 | struct core_freesync *core_freesync = NULL; | |
1041 | ||
1042 | if (mod_freesync == NULL) | |
1043 | return false; | |
1044 | ||
1045 | core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); | |
1046 | index = map_index_from_stream(core_freesync, stream); | |
1047 | ||
1048 | *is_ramp_active = | |
1049 | core_freesync->map[index].state.static_ramp.ramp_is_active; | |
1050 | ||
1051 | return true; | |
1052 | } | |
1053 | ||
72ada5f7 | 1054 | bool mod_freesync_override_min_max(struct mod_freesync *mod_freesync, |
0971c40e | 1055 | struct dc_stream_state *streams, |
72ada5f7 | 1056 | unsigned int min_refresh, |
fc82c5cb AZ |
1057 | unsigned int max_refresh, |
1058 | struct mod_freesync_caps *caps) | |
72ada5f7 EC |
1059 | { |
1060 | unsigned int index = 0; | |
1061 | struct core_freesync *core_freesync; | |
1062 | struct freesync_state *state; | |
1063 | ||
1064 | if (mod_freesync == NULL) | |
1065 | return false; | |
1066 | ||
1067 | core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); | |
1068 | index = map_index_from_stream(core_freesync, streams); | |
1069 | state = &core_freesync->map[index].state; | |
1070 | ||
fc82c5cb AZ |
1071 | if (max_refresh == 0) |
1072 | max_refresh = state->nominal_refresh_rate_in_micro_hz; | |
1073 | ||
1074 | if (min_refresh == 0) { | |
72ada5f7 EC |
1075 | /* Restore defaults */ |
1076 | calc_freesync_range(core_freesync, streams, state, | |
1077 | core_freesync->map[index].caps-> | |
1078 | min_refresh_in_micro_hz, | |
1079 | state->nominal_refresh_rate_in_micro_hz); | |
1080 | } else { | |
1081 | calc_freesync_range(core_freesync, streams, | |
1082 | state, | |
1083 | min_refresh, | |
1084 | max_refresh); | |
1085 | ||
1086 | /* Program vtotal min/max */ | |
1087 | core_freesync->dc->stream_funcs.adjust_vmin_vmax( | |
1088 | core_freesync->dc, &streams, 1, | |
1089 | state->freesync_range.vmin, | |
1090 | state->freesync_range.vmax); | |
1091 | } | |
1092 | ||
fc82c5cb AZ |
1093 | if (min_refresh != 0 && |
1094 | dc_is_embedded_signal(streams->sink->sink_signal) && | |
1095 | (max_refresh - min_refresh >= 10000000)) { | |
1096 | caps->supported = true; | |
1097 | caps->min_refresh_in_micro_hz = min_refresh; | |
1098 | caps->max_refresh_in_micro_hz = max_refresh; | |
1099 | } | |
1100 | ||
1101 | /* Update the stream */ | |
1102 | update_stream(core_freesync, streams); | |
1103 | ||
72ada5f7 EC |
1104 | return true; |
1105 | } | |
1106 | ||
1107 | bool mod_freesync_get_min_max(struct mod_freesync *mod_freesync, | |
0971c40e | 1108 | struct dc_stream_state *stream, |
72ada5f7 EC |
1109 | unsigned int *min_refresh, |
1110 | unsigned int *max_refresh) | |
1111 | { | |
1112 | unsigned int index = 0; | |
1113 | struct core_freesync *core_freesync = NULL; | |
1114 | ||
1115 | if (mod_freesync == NULL) | |
1116 | return false; | |
1117 | ||
1118 | core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); | |
1119 | index = map_index_from_stream(core_freesync, stream); | |
1120 | ||
1121 | *min_refresh = | |
1122 | core_freesync->map[index].state.freesync_range.min_refresh; | |
1123 | *max_refresh = | |
1124 | core_freesync->map[index].state.freesync_range.max_refresh; | |
1125 | ||
1126 | return true; | |
1127 | } | |
1128 | ||
1129 | bool mod_freesync_get_vmin_vmax(struct mod_freesync *mod_freesync, | |
0971c40e | 1130 | struct dc_stream_state *stream, |
72ada5f7 EC |
1131 | unsigned int *vmin, |
1132 | unsigned int *vmax) | |
1133 | { | |
1134 | unsigned int index = 0; | |
1135 | struct core_freesync *core_freesync = NULL; | |
1136 | ||
1137 | if (mod_freesync == NULL) | |
1138 | return false; | |
1139 | ||
1140 | core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); | |
1141 | index = map_index_from_stream(core_freesync, stream); | |
1142 | ||
1143 | *vmin = | |
1144 | core_freesync->map[index].state.freesync_range.vmin; | |
1145 | *vmax = | |
1146 | core_freesync->map[index].state.freesync_range.vmax; | |
1147 | ||
1148 | return true; | |
1149 | } | |
1150 | ||
1151 | bool mod_freesync_get_v_position(struct mod_freesync *mod_freesync, | |
0971c40e | 1152 | struct dc_stream_state *stream, |
72ada5f7 EC |
1153 | unsigned int *nom_v_pos, |
1154 | unsigned int *v_pos) | |
1155 | { | |
1156 | unsigned int index = 0; | |
1157 | struct core_freesync *core_freesync = NULL; | |
1158 | struct crtc_position position; | |
1159 | ||
1160 | if (mod_freesync == NULL) | |
1161 | return false; | |
1162 | ||
1163 | core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); | |
1164 | index = map_index_from_stream(core_freesync, stream); | |
1165 | ||
1166 | if (core_freesync->dc->stream_funcs.get_crtc_position( | |
1167 | core_freesync->dc, &stream, 1, | |
1168 | &position.vertical_count, &position.nominal_vcount)) { | |
1169 | ||
fc82c5cb AZ |
1170 | *nom_v_pos = position.nominal_vcount; |
1171 | *v_pos = position.vertical_count; | |
72ada5f7 EC |
1172 | |
1173 | return true; | |
1174 | } | |
1175 | ||
1176 | return false; | |
1177 | } | |
1178 | ||
4562236b | 1179 | void mod_freesync_notify_mode_change(struct mod_freesync *mod_freesync, |
0971c40e | 1180 | struct dc_stream_state **streams, int num_streams) |
4562236b | 1181 | { |
4562236b | 1182 | unsigned int stream_index, map_index; |
4562236b | 1183 | struct freesync_state *state; |
7a1c37e0 | 1184 | struct core_freesync *core_freesync = NULL; |
6838161c | 1185 | struct dc_static_screen_events triggers = {0}; |
fc82c5cb | 1186 | unsigned long long temp = 0; |
7a1c37e0 AK |
1187 | |
1188 | if (mod_freesync == NULL) | |
1189 | return; | |
1190 | ||
1191 | core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); | |
4562236b HW |
1192 | |
1193 | for (stream_index = 0; stream_index < num_streams; stream_index++) { | |
4562236b HW |
1194 | map_index = map_index_from_stream(core_freesync, |
1195 | streams[stream_index]); | |
1196 | ||
1197 | state = &core_freesync->map[map_index].state; | |
1198 | ||
fc82c5cb AZ |
1199 | /* Update the field rate for new timing */ |
1200 | temp = streams[stream_index]->timing.pix_clk_khz; | |
1201 | temp *= 1000ULL * 1000ULL * 1000ULL; | |
1202 | temp = div_u64(temp, | |
1203 | streams[stream_index]->timing.h_total); | |
1204 | temp = div_u64(temp, | |
1205 | streams[stream_index]->timing.v_total); | |
1206 | state->nominal_refresh_rate_in_micro_hz = | |
1207 | (unsigned int) temp; | |
1208 | ||
4562236b | 1209 | if (core_freesync->map[map_index].caps->supported) { |
4562236b HW |
1210 | |
1211 | /* Update the stream */ | |
1212 | update_stream(core_freesync, streams[stream_index]); | |
1213 | ||
72ada5f7 EC |
1214 | /* Calculate vmin/vmax and refresh rate for |
1215 | * current mode | |
1216 | */ | |
1217 | calc_freesync_range(core_freesync, *streams, state, | |
1c29313b | 1218 | core_freesync->map[map_index].caps-> |
72ada5f7 EC |
1219 | min_refresh_in_micro_hz, |
1220 | state->nominal_refresh_rate_in_micro_hz); | |
6838161c CM |
1221 | |
1222 | /* Update mask */ | |
1223 | triggers.overlay_update = true; | |
1224 | triggers.surface_update = true; | |
1225 | ||
1226 | core_freesync->dc->stream_funcs.set_static_screen_events( | |
1227 | core_freesync->dc, streams, num_streams, | |
1228 | &triggers); | |
4562236b HW |
1229 | } |
1230 | } | |
1231 | ||
1232 | /* Program freesync according to current state*/ | |
1233 | set_freesync_on_streams(core_freesync, streams, num_streams); | |
1234 | } | |
1235 | ||
1236 | /* Add the timestamps to the cache and determine whether BTR programming | |
1237 | * is required, depending on the times calculated | |
1238 | */ | |
1239 | static void update_timestamps(struct core_freesync *core_freesync, | |
0971c40e | 1240 | const struct dc_stream_state *stream, unsigned int map_index, |
4562236b HW |
1241 | unsigned int last_render_time_in_us) |
1242 | { | |
1243 | struct freesync_state *state = &core_freesync->map[map_index].state; | |
1244 | ||
1245 | state->time.render_times[state->time.render_times_index] = | |
1246 | last_render_time_in_us; | |
1247 | state->time.render_times_index++; | |
1248 | ||
1249 | if (state->time.render_times_index >= RENDER_TIMES_MAX_COUNT) | |
1250 | state->time.render_times_index = 0; | |
1251 | ||
1252 | if (last_render_time_in_us + BTR_EXIT_MARGIN < | |
1253 | state->time.max_render_time_in_us) { | |
1254 | ||
1255 | /* Exit Below the Range */ | |
1256 | if (state->btr.btr_active) { | |
1257 | ||
1258 | state->btr.program_btr = true; | |
1259 | state->btr.btr_active = false; | |
1260 | state->btr.frame_counter = 0; | |
1261 | ||
1262 | /* Exit Fixed Refresh mode */ | |
d09fec0f | 1263 | } else if (state->fixed_refresh.fixed_active) { |
4562236b | 1264 | |
d09fec0f | 1265 | state->fixed_refresh.frame_counter++; |
4562236b | 1266 | |
d09fec0f AK |
1267 | if (state->fixed_refresh.frame_counter > |
1268 | FIXED_REFRESH_EXIT_FRAME_COUNT) { | |
1269 | state->fixed_refresh.frame_counter = 0; | |
1270 | state->fixed_refresh.program_fixed = true; | |
1271 | state->fixed_refresh.fixed_active = false; | |
1272 | } | |
4562236b HW |
1273 | } |
1274 | ||
1275 | } else if (last_render_time_in_us > state->time.max_render_time_in_us) { | |
1276 | ||
1277 | /* Enter Below the Range */ | |
1278 | if (!state->btr.btr_active && | |
d09fec0f | 1279 | core_freesync->map[map_index].caps->btr_supported) { |
4562236b HW |
1280 | |
1281 | state->btr.program_btr = true; | |
1282 | state->btr.btr_active = true; | |
1283 | ||
1284 | /* Enter Fixed Refresh mode */ | |
d09fec0f | 1285 | } else if (!state->fixed_refresh.fixed_active && |
4562236b HW |
1286 | !core_freesync->map[map_index].caps->btr_supported) { |
1287 | ||
d09fec0f | 1288 | state->fixed_refresh.frame_counter++; |
4562236b | 1289 | |
d09fec0f AK |
1290 | if (state->fixed_refresh.frame_counter > |
1291 | FIXED_REFRESH_ENTER_FRAME_COUNT) { | |
1292 | state->fixed_refresh.frame_counter = 0; | |
1293 | state->fixed_refresh.program_fixed = true; | |
1294 | state->fixed_refresh.fixed_active = true; | |
1295 | } | |
4562236b HW |
1296 | } |
1297 | } | |
1298 | ||
1299 | /* When Below the Range is active, must react on every frame */ | |
1300 | if (state->btr.btr_active) | |
1301 | state->btr.program_btr = true; | |
1302 | } | |
1303 | ||
1304 | static void apply_below_the_range(struct core_freesync *core_freesync, | |
0971c40e | 1305 | struct dc_stream_state *stream, unsigned int map_index, |
4562236b HW |
1306 | unsigned int last_render_time_in_us) |
1307 | { | |
1308 | unsigned int inserted_frame_duration_in_us = 0; | |
1309 | unsigned int mid_point_frames_ceil = 0; | |
1310 | unsigned int mid_point_frames_floor = 0; | |
1311 | unsigned int frame_time_in_us = 0; | |
1312 | unsigned int delta_from_mid_point_in_us_1 = 0xFFFFFFFF; | |
1313 | unsigned int delta_from_mid_point_in_us_2 = 0xFFFFFFFF; | |
1314 | unsigned int frames_to_insert = 0; | |
4562236b HW |
1315 | unsigned int min_frame_duration_in_ns = 0; |
1316 | struct freesync_state *state = &core_freesync->map[map_index].state; | |
1317 | ||
1318 | if (!state->btr.program_btr) | |
1319 | return; | |
1320 | ||
1321 | state->btr.program_btr = false; | |
1322 | ||
1323 | min_frame_duration_in_ns = ((unsigned int) (div64_u64( | |
1324 | (1000000000ULL * 1000000), | |
1325 | state->nominal_refresh_rate_in_micro_hz))); | |
1326 | ||
1327 | /* Program BTR */ | |
1328 | ||
1329 | /* BTR set to "not active" so disengage */ | |
1330 | if (!state->btr.btr_active) | |
1331 | ||
1332 | /* Restore FreeSync */ | |
1333 | set_freesync_on_streams(core_freesync, &stream, 1); | |
1334 | ||
1335 | /* BTR set to "active" so engage */ | |
1336 | else { | |
1337 | ||
1338 | /* Calculate number of midPoint frames that could fit within | |
1339 | * the render time interval- take ceil of this value | |
1340 | */ | |
1341 | mid_point_frames_ceil = (last_render_time_in_us + | |
1342 | state->btr.mid_point_in_us- 1) / | |
1343 | state->btr.mid_point_in_us; | |
1344 | ||
1345 | if (mid_point_frames_ceil > 0) { | |
1346 | ||
1347 | frame_time_in_us = last_render_time_in_us / | |
1348 | mid_point_frames_ceil; | |
d09fec0f AK |
1349 | delta_from_mid_point_in_us_1 = |
1350 | (state->btr.mid_point_in_us > | |
4562236b HW |
1351 | frame_time_in_us) ? |
1352 | (state->btr.mid_point_in_us - frame_time_in_us): | |
1353 | (frame_time_in_us - state->btr.mid_point_in_us); | |
1354 | } | |
1355 | ||
1356 | /* Calculate number of midPoint frames that could fit within | |
1357 | * the render time interval- take floor of this value | |
1358 | */ | |
1359 | mid_point_frames_floor = last_render_time_in_us / | |
1360 | state->btr.mid_point_in_us; | |
1361 | ||
1362 | if (mid_point_frames_floor > 0) { | |
1363 | ||
1364 | frame_time_in_us = last_render_time_in_us / | |
1365 | mid_point_frames_floor; | |
d09fec0f AK |
1366 | delta_from_mid_point_in_us_2 = |
1367 | (state->btr.mid_point_in_us > | |
4562236b HW |
1368 | frame_time_in_us) ? |
1369 | (state->btr.mid_point_in_us - frame_time_in_us): | |
1370 | (frame_time_in_us - state->btr.mid_point_in_us); | |
1371 | } | |
1372 | ||
1373 | /* Choose number of frames to insert based on how close it | |
1374 | * can get to the mid point of the variable range. | |
1375 | */ | |
1376 | if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) | |
1377 | frames_to_insert = mid_point_frames_ceil; | |
1378 | else | |
1379 | frames_to_insert = mid_point_frames_floor; | |
1380 | ||
1381 | /* Either we've calculated the number of frames to insert, | |
1382 | * or we need to insert min duration frames | |
1383 | */ | |
1384 | if (frames_to_insert > 0) | |
1385 | inserted_frame_duration_in_us = last_render_time_in_us / | |
1386 | frames_to_insert; | |
1387 | ||
1388 | if (inserted_frame_duration_in_us < | |
1389 | state->time.min_render_time_in_us) | |
1390 | ||
1391 | inserted_frame_duration_in_us = | |
1392 | state->time.min_render_time_in_us; | |
1393 | ||
4562236b HW |
1394 | /* Cache the calculated variables */ |
1395 | state->btr.inserted_frame_duration_in_us = | |
1396 | inserted_frame_duration_in_us; | |
1397 | state->btr.frames_to_insert = frames_to_insert; | |
1398 | state->btr.frame_counter = frames_to_insert; | |
1399 | ||
1400 | } | |
1401 | } | |
1402 | ||
1403 | static void apply_fixed_refresh(struct core_freesync *core_freesync, | |
0971c40e | 1404 | struct dc_stream_state *stream, unsigned int map_index) |
4562236b HW |
1405 | { |
1406 | unsigned int vmin = 0, vmax = 0; | |
1407 | struct freesync_state *state = &core_freesync->map[map_index].state; | |
1408 | ||
d09fec0f | 1409 | if (!state->fixed_refresh.program_fixed) |
4562236b HW |
1410 | return; |
1411 | ||
d09fec0f | 1412 | state->fixed_refresh.program_fixed = false; |
4562236b HW |
1413 | |
1414 | /* Program Fixed Refresh */ | |
1415 | ||
1416 | /* Fixed Refresh set to "not active" so disengage */ | |
d09fec0f | 1417 | if (!state->fixed_refresh.fixed_active) { |
4562236b HW |
1418 | set_freesync_on_streams(core_freesync, &stream, 1); |
1419 | ||
1420 | /* Fixed Refresh set to "active" so engage (fix to max) */ | |
1421 | } else { | |
1422 | ||
72ada5f7 | 1423 | vmin = state->freesync_range.vmin; |
4562236b HW |
1424 | |
1425 | vmax = vmin; | |
1426 | ||
1427 | core_freesync->dc->stream_funcs.adjust_vmin_vmax( | |
1428 | core_freesync->dc, &stream, | |
1429 | 1, vmin, | |
1430 | vmax); | |
1431 | } | |
1432 | } | |
1433 | ||
1434 | void mod_freesync_pre_update_plane_addresses(struct mod_freesync *mod_freesync, | |
0971c40e | 1435 | struct dc_stream_state **streams, int num_streams, |
4562236b HW |
1436 | unsigned int curr_time_stamp_in_us) |
1437 | { | |
1438 | unsigned int stream_index, map_index, last_render_time_in_us = 0; | |
7a1c37e0 AK |
1439 | struct core_freesync *core_freesync = NULL; |
1440 | ||
1441 | if (mod_freesync == NULL) | |
1442 | return; | |
1443 | ||
1444 | core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); | |
4562236b HW |
1445 | |
1446 | for (stream_index = 0; stream_index < num_streams; stream_index++) { | |
1447 | ||
1448 | map_index = map_index_from_stream(core_freesync, | |
1449 | streams[stream_index]); | |
1450 | ||
1451 | if (core_freesync->map[map_index].caps->supported) { | |
1452 | ||
1453 | last_render_time_in_us = curr_time_stamp_in_us - | |
1454 | core_freesync->map[map_index].state.time. | |
1455 | prev_time_stamp_in_us; | |
1456 | ||
1457 | /* Add the timestamps to the cache and determine | |
1458 | * whether BTR program is required | |
1459 | */ | |
1460 | update_timestamps(core_freesync, streams[stream_index], | |
1461 | map_index, last_render_time_in_us); | |
1462 | ||
1463 | if (core_freesync->map[map_index].state.fullscreen && | |
1464 | core_freesync->map[map_index].user_enable. | |
1465 | enable_for_gaming) { | |
1466 | ||
1467 | if (core_freesync->map[map_index].caps->btr_supported) { | |
1468 | ||
1469 | apply_below_the_range(core_freesync, | |
1470 | streams[stream_index], map_index, | |
1471 | last_render_time_in_us); | |
1472 | } else { | |
1473 | apply_fixed_refresh(core_freesync, | |
1474 | streams[stream_index], map_index); | |
1475 | } | |
1476 | } | |
1477 | ||
1478 | core_freesync->map[map_index].state.time. | |
1479 | prev_time_stamp_in_us = curr_time_stamp_in_us; | |
1480 | } | |
1481 | ||
1482 | } | |
1483 | } |