| /* |
| * Copyright 2016 Advanced Micro Devices, Inc. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
| * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
| * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| * OTHER DEALINGS IN THE SOFTWARE. |
| * |
| * Authors: AMD |
| * |
| */ |
| |
| #include "dm_services.h" |
| #include "dc.h" |
| #include "mod_freesync.h" |
| #include "core_types.h" |
| |
| #define MOD_FREESYNC_MAX_CONCURRENT_STREAMS 32 |
| |
| /* Refresh rate ramp at a fixed rate of 65 Hz/second */ |
| #define STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME ((1000 / 60) * 65) |
| /* Number of elements in the render times cache array */ |
| #define RENDER_TIMES_MAX_COUNT 10 |
| /* Threshold to exit BTR (to avoid frequent enter-exits at the lower limit) */ |
| #define BTR_EXIT_MARGIN 2000 |
| /* Number of consecutive frames to check before entering/exiting fixed refresh*/ |
| #define FIXED_REFRESH_ENTER_FRAME_COUNT 5 |
| #define FIXED_REFRESH_EXIT_FRAME_COUNT 5 |
| |
| #define FREESYNC_REGISTRY_NAME "freesync_v1" |
| |
| #define FREESYNC_NO_STATIC_FOR_EXTERNAL_DP_REGKEY "DalFreeSyncNoStaticForExternalDp" |
| |
| #define FREESYNC_NO_STATIC_FOR_INTERNAL_REGKEY "DalFreeSyncNoStaticForInternal" |
| |
| #define FREESYNC_DEFAULT_REGKEY "LCDFreeSyncDefault" |
| |
| struct gradual_static_ramp { |
| bool ramp_is_active; |
| bool ramp_direction_is_up; |
| unsigned int ramp_current_frame_duration_in_ns; |
| }; |
| |
| struct freesync_time { |
| /* video (48Hz feature) related */ |
| unsigned int update_duration_in_ns; |
| |
| /* BTR/fixed refresh related */ |
| unsigned int prev_time_stamp_in_us; |
| |
| unsigned int min_render_time_in_us; |
| unsigned int max_render_time_in_us; |
| |
| unsigned int render_times_index; |
| unsigned int render_times[RENDER_TIMES_MAX_COUNT]; |
| |
| unsigned int min_window; |
| unsigned int max_window; |
| }; |
| |
| struct below_the_range { |
| bool btr_active; |
| bool program_btr; |
| |
| unsigned int mid_point_in_us; |
| |
| unsigned int inserted_frame_duration_in_us; |
| unsigned int frames_to_insert; |
| unsigned int frame_counter; |
| }; |
| |
| struct fixed_refresh { |
| bool fixed_active; |
| bool program_fixed; |
| unsigned int frame_counter; |
| }; |
| |
| struct freesync_range { |
| unsigned int min_refresh; |
| unsigned int max_frame_duration; |
| unsigned int vmax; |
| |
| unsigned int max_refresh; |
| unsigned int min_frame_duration; |
| unsigned int vmin; |
| }; |
| |
| struct freesync_state { |
| bool fullscreen; |
| bool static_screen; |
| bool video; |
| |
| unsigned int vmin; |
| unsigned int vmax; |
| |
| struct freesync_time time; |
| |
| unsigned int nominal_refresh_rate_in_micro_hz; |
| bool windowed_fullscreen; |
| |
| struct gradual_static_ramp static_ramp; |
| struct below_the_range btr; |
| struct fixed_refresh fixed_refresh; |
| struct freesync_range freesync_range; |
| }; |
| |
| struct freesync_entity { |
| struct dc_stream_state *stream; |
| struct mod_freesync_caps *caps; |
| struct freesync_state state; |
| struct mod_freesync_user_enable user_enable; |
| }; |
| |
| struct freesync_registry_options { |
| bool drr_external_supported; |
| bool drr_internal_supported; |
| bool lcd_freesync_default_set; |
| int lcd_freesync_default_value; |
| }; |
| |
| struct core_freesync { |
| struct mod_freesync public; |
| struct dc *dc; |
| struct freesync_registry_options opts; |
| struct freesync_entity *map; |
| int num_entities; |
| }; |
| |
| #define MOD_FREESYNC_TO_CORE(mod_freesync)\ |
| container_of(mod_freesync, struct core_freesync, public) |
| |
| struct mod_freesync *mod_freesync_create(struct dc *dc) |
| { |
| struct core_freesync *core_freesync = |
| kzalloc(sizeof(struct core_freesync), GFP_KERNEL); |
| |
| |
| struct persistent_data_flag flag; |
| |
| int i, data = 0; |
| |
| if (core_freesync == NULL) |
| goto fail_alloc_context; |
| |
| core_freesync->map = kcalloc(MOD_FREESYNC_MAX_CONCURRENT_STREAMS, |
| sizeof(struct freesync_entity), |
| GFP_KERNEL); |
| |
| if (core_freesync->map == NULL) |
| goto fail_alloc_map; |
| |
| for (i = 0; i < MOD_FREESYNC_MAX_CONCURRENT_STREAMS; i++) |
| core_freesync->map[i].stream = NULL; |
| |
| core_freesync->num_entities = 0; |
| |
| if (dc == NULL) |
| goto fail_construct; |
| |
| core_freesync->dc = dc; |
| |
| /* Create initial module folder in registry for freesync enable data */ |
| flag.save_per_edid = true; |
| flag.save_per_link = false; |
| dm_write_persistent_data(dc->ctx, NULL, FREESYNC_REGISTRY_NAME, |
| NULL, NULL, 0, &flag); |
| flag.save_per_edid = false; |
| flag.save_per_link = false; |
| |
| if (dm_read_persistent_data(dc->ctx, NULL, NULL, |
| FREESYNC_NO_STATIC_FOR_INTERNAL_REGKEY, |
| &data, sizeof(data), &flag)) { |
| core_freesync->opts.drr_internal_supported = |
| (data & 1) ? false : true; |
| } |
| |
| if (dm_read_persistent_data(dc->ctx, NULL, NULL, |
| FREESYNC_NO_STATIC_FOR_EXTERNAL_DP_REGKEY, |
| &data, sizeof(data), &flag)) { |
| core_freesync->opts.drr_external_supported = |
| (data & 1) ? false : true; |
| } |
| |
| if (dm_read_persistent_data(dc->ctx, NULL, NULL, |
| FREESYNC_DEFAULT_REGKEY, |
| &data, sizeof(data), &flag)) { |
| core_freesync->opts.lcd_freesync_default_set = true; |
| core_freesync->opts.lcd_freesync_default_value = data; |
| } else { |
| core_freesync->opts.lcd_freesync_default_set = false; |
| core_freesync->opts.lcd_freesync_default_value = 0; |
| } |
| |
| return &core_freesync->public; |
| |
| fail_construct: |
| kfree(core_freesync->map); |
| |
| fail_alloc_map: |
| kfree(core_freesync); |
| |
| fail_alloc_context: |
| return NULL; |
| } |
| |
| void mod_freesync_destroy(struct mod_freesync *mod_freesync) |
| { |
| if (mod_freesync != NULL) { |
| int i; |
| struct core_freesync *core_freesync = |
| MOD_FREESYNC_TO_CORE(mod_freesync); |
| |
| for (i = 0; i < core_freesync->num_entities; i++) |
| if (core_freesync->map[i].stream) |
| dc_stream_release(core_freesync->map[i].stream); |
| |
| kfree(core_freesync->map); |
| |
| kfree(core_freesync); |
| } |
| } |
| |
| /* Given a specific dc_stream* this function finds its equivalent |
| * on the core_freesync->map and returns the corresponding index |
| */ |
| static unsigned int map_index_from_stream(struct core_freesync *core_freesync, |
| struct dc_stream_state *stream) |
| { |
| unsigned int index = 0; |
| |
| for (index = 0; index < core_freesync->num_entities; index++) { |
| if (core_freesync->map[index].stream == stream) { |
| return index; |
| } |
| } |
| /* Could not find stream requested */ |
| ASSERT(false); |
| return index; |
| } |
| |
| bool mod_freesync_add_stream(struct mod_freesync *mod_freesync, |
| struct dc_stream_state *stream, struct mod_freesync_caps *caps) |
| { |
| struct dc *dc = NULL; |
| struct core_freesync *core_freesync = NULL; |
| int persistent_freesync_enable = 0; |
| struct persistent_data_flag flag; |
| unsigned int nom_refresh_rate_uhz; |
| unsigned long long temp; |
| |
| if (mod_freesync == NULL) |
| return false; |
| |
| core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); |
| dc = core_freesync->dc; |
| |
| flag.save_per_edid = true; |
| flag.save_per_link = false; |
| |
| if (core_freesync->num_entities < MOD_FREESYNC_MAX_CONCURRENT_STREAMS) { |
| |
| dc_stream_retain(stream); |
| |
| temp = stream->timing.pix_clk_khz; |
| temp *= 1000ULL * 1000ULL * 1000ULL; |
| temp = div_u64(temp, stream->timing.h_total); |
| temp = div_u64(temp, stream->timing.v_total); |
| |
| nom_refresh_rate_uhz = (unsigned int) temp; |
| |
| core_freesync->map[core_freesync->num_entities].stream = stream; |
| core_freesync->map[core_freesync->num_entities].caps = caps; |
| |
| core_freesync->map[core_freesync->num_entities].state. |
| fullscreen = false; |
| core_freesync->map[core_freesync->num_entities].state. |
| static_screen = false; |
| core_freesync->map[core_freesync->num_entities].state. |
| video = false; |
| core_freesync->map[core_freesync->num_entities].state.time. |
| update_duration_in_ns = 0; |
| core_freesync->map[core_freesync->num_entities].state. |
| static_ramp.ramp_is_active = false; |
| |
| /* get persistent data from registry */ |
| if (dm_read_persistent_data(dc->ctx, stream->sink, |
| FREESYNC_REGISTRY_NAME, |
| "userenable", &persistent_freesync_enable, |
| sizeof(int), &flag)) { |
| core_freesync->map[core_freesync->num_entities].user_enable. |
| enable_for_gaming = |
| (persistent_freesync_enable & 1) ? true : false; |
| core_freesync->map[core_freesync->num_entities].user_enable. |
| enable_for_static = |
| (persistent_freesync_enable & 2) ? true : false; |
| core_freesync->map[core_freesync->num_entities].user_enable. |
| enable_for_video = |
| (persistent_freesync_enable & 4) ? true : false; |
| /* If FreeSync display and LCDFreeSyncDefault is set, use as default values write back to userenable */ |
| } else if (caps->supported && (core_freesync->opts.lcd_freesync_default_set)) { |
| core_freesync->map[core_freesync->num_entities].user_enable.enable_for_gaming = |
| (core_freesync->opts.lcd_freesync_default_value & 1) ? true : false; |
| core_freesync->map[core_freesync->num_entities].user_enable.enable_for_static = |
| (core_freesync->opts.lcd_freesync_default_value & 2) ? true : false; |
| core_freesync->map[core_freesync->num_entities].user_enable.enable_for_video = |
| (core_freesync->opts.lcd_freesync_default_value & 4) ? true : false; |
| dm_write_persistent_data(dc->ctx, stream->sink, |
| FREESYNC_REGISTRY_NAME, |
| "userenable", &core_freesync->opts.lcd_freesync_default_value, |
| sizeof(int), &flag); |
| } else { |
| core_freesync->map[core_freesync->num_entities].user_enable. |
| enable_for_gaming = false; |
| core_freesync->map[core_freesync->num_entities].user_enable. |
| enable_for_static = false; |
| core_freesync->map[core_freesync->num_entities].user_enable. |
| enable_for_video = false; |
| } |
| |
| if (caps->supported && |
| nom_refresh_rate_uhz >= caps->min_refresh_in_micro_hz && |
| nom_refresh_rate_uhz <= caps->max_refresh_in_micro_hz) |
| stream->ignore_msa_timing_param = 1; |
| |
| core_freesync->num_entities++; |
| return true; |
| } |
| return false; |
| } |
| |
| bool mod_freesync_remove_stream(struct mod_freesync *mod_freesync, |
| struct dc_stream_state *stream) |
| { |
| int i = 0; |
| struct core_freesync *core_freesync = NULL; |
| unsigned int index = 0; |
| |
| if (mod_freesync == NULL) |
| return false; |
| |
| core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); |
| index = map_index_from_stream(core_freesync, stream); |
| |
| dc_stream_release(core_freesync->map[index].stream); |
| core_freesync->map[index].stream = NULL; |
| /* To remove this entity, shift everything after down */ |
| for (i = index; i < core_freesync->num_entities - 1; i++) |
| core_freesync->map[i] = core_freesync->map[i + 1]; |
| core_freesync->num_entities--; |
| return true; |
| } |
| |
| static void adjust_vmin_vmax(struct core_freesync *core_freesync, |
| struct dc_stream_state **streams, |
| int num_streams, |
| int map_index, |
| unsigned int v_total_min, |
| unsigned int v_total_max) |
| { |
| if (num_streams == 0 || streams == NULL || num_streams > 1) |
| return; |
| |
| core_freesync->map[map_index].state.vmin = v_total_min; |
| core_freesync->map[map_index].state.vmax = v_total_max; |
| |
| dc_stream_adjust_vmin_vmax(core_freesync->dc, streams, |
| num_streams, v_total_min, |
| v_total_max); |
| } |
| |
| |
| static void update_stream_freesync_context(struct core_freesync *core_freesync, |
| struct dc_stream_state *stream) |
| { |
| unsigned int index; |
| struct freesync_context *ctx; |
| |
| ctx = &stream->freesync_ctx; |
| |
| index = map_index_from_stream(core_freesync, stream); |
| |
| ctx->supported = core_freesync->map[index].caps->supported; |
| ctx->enabled = (core_freesync->map[index].user_enable.enable_for_gaming || |
| core_freesync->map[index].user_enable.enable_for_video || |
| core_freesync->map[index].user_enable.enable_for_static); |
| ctx->active = (core_freesync->map[index].state.fullscreen || |
| core_freesync->map[index].state.video || |
| core_freesync->map[index].state.static_ramp.ramp_is_active); |
| ctx->min_refresh_in_micro_hz = |
| core_freesync->map[index].caps->min_refresh_in_micro_hz; |
| ctx->nominal_refresh_in_micro_hz = core_freesync-> |
| map[index].state.nominal_refresh_rate_in_micro_hz; |
| |
| } |
| |
| static void update_stream(struct core_freesync *core_freesync, |
| struct dc_stream_state *stream) |
| { |
| unsigned int index = map_index_from_stream(core_freesync, stream); |
| if (core_freesync->map[index].caps->supported) { |
| stream->ignore_msa_timing_param = 1; |
| update_stream_freesync_context(core_freesync, stream); |
| } |
| } |
| |
| static void calc_freesync_range(struct core_freesync *core_freesync, |
| struct dc_stream_state *stream, |
| struct freesync_state *state, |
| unsigned int min_refresh_in_uhz, |
| unsigned int max_refresh_in_uhz) |
| { |
| unsigned int min_frame_duration_in_ns = 0, max_frame_duration_in_ns = 0; |
| unsigned int index = map_index_from_stream(core_freesync, stream); |
| uint32_t vtotal = stream->timing.v_total; |
| |
| if ((min_refresh_in_uhz == 0) || (max_refresh_in_uhz == 0)) { |
| state->freesync_range.min_refresh = |
| state->nominal_refresh_rate_in_micro_hz; |
| state->freesync_range.max_refresh = |
| state->nominal_refresh_rate_in_micro_hz; |
| |
| state->freesync_range.max_frame_duration = 0; |
| state->freesync_range.min_frame_duration = 0; |
| |
| state->freesync_range.vmax = vtotal; |
| state->freesync_range.vmin = vtotal; |
| |
| return; |
| } |
| |
| min_frame_duration_in_ns = ((unsigned int) (div64_u64( |
| (1000000000ULL * 1000000), |
| max_refresh_in_uhz))); |
| max_frame_duration_in_ns = ((unsigned int) (div64_u64( |
| (1000000000ULL * 1000000), |
| min_refresh_in_uhz))); |
| |
| state->freesync_range.min_refresh = min_refresh_in_uhz; |
| state->freesync_range.max_refresh = max_refresh_in_uhz; |
| |
| state->freesync_range.max_frame_duration = max_frame_duration_in_ns; |
| state->freesync_range.min_frame_duration = min_frame_duration_in_ns; |
| |
| state->freesync_range.vmax = div64_u64(div64_u64(((unsigned long long)( |
| max_frame_duration_in_ns) * stream->timing.pix_clk_khz), |
| stream->timing.h_total), 1000000); |
| state->freesync_range.vmin = div64_u64(div64_u64(((unsigned long long)( |
| min_frame_duration_in_ns) * stream->timing.pix_clk_khz), |
| stream->timing.h_total), 1000000); |
| |
| /* vmin/vmax cannot be less than vtotal */ |
| if (state->freesync_range.vmin < vtotal) { |
| /* Error of 1 is permissible */ |
| ASSERT((state->freesync_range.vmin + 1) >= vtotal); |
| state->freesync_range.vmin = vtotal; |
| } |
| |
| if (state->freesync_range.vmax < vtotal) { |
| /* Error of 1 is permissible */ |
| ASSERT((state->freesync_range.vmax + 1) >= vtotal); |
| state->freesync_range.vmax = vtotal; |
| } |
| |
| /* Determine whether BTR can be supported */ |
| if (max_frame_duration_in_ns >= |
| 2 * min_frame_duration_in_ns) |
| core_freesync->map[index].caps->btr_supported = true; |
| else |
| core_freesync->map[index].caps->btr_supported = false; |
| |
| /* Cache the time variables */ |
| state->time.max_render_time_in_us = |
| max_frame_duration_in_ns / 1000; |
| state->time.min_render_time_in_us = |
| min_frame_duration_in_ns / 1000; |
| state->btr.mid_point_in_us = |
| (max_frame_duration_in_ns + |
| min_frame_duration_in_ns) / 2000; |
| } |
| |
| static void calc_v_total_from_duration(struct dc_stream_state *stream, |
| unsigned int duration_in_ns, int *v_total_nominal) |
| { |
| *v_total_nominal = div64_u64(div64_u64(((unsigned long long)( |
| duration_in_ns) * stream->timing.pix_clk_khz), |
| stream->timing.h_total), 1000000); |
| } |
| |
| static void calc_v_total_for_static_ramp(struct core_freesync *core_freesync, |
| struct dc_stream_state *stream, |
| unsigned int index, int *v_total) |
| { |
| unsigned int frame_duration = 0; |
| |
| struct gradual_static_ramp *static_ramp_variables = |
| &core_freesync->map[index].state.static_ramp; |
| |
| /* Calc ratio between new and current frame duration with 3 digit */ |
| unsigned int frame_duration_ratio = div64_u64(1000000, |
| (1000 + div64_u64(((unsigned long long)( |
| STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME) * |
| static_ramp_variables->ramp_current_frame_duration_in_ns), |
| 1000000000))); |
| |
| /* Calculate delta between new and current frame duration in ns */ |
| unsigned int frame_duration_delta = div64_u64(((unsigned long long)( |
| static_ramp_variables->ramp_current_frame_duration_in_ns) * |
| (1000 - frame_duration_ratio)), 1000); |
| |
| /* Adjust frame duration delta based on ratio between current and |
| * standard frame duration (frame duration at 60 Hz refresh rate). |
| */ |
| unsigned int ramp_rate_interpolated = div64_u64(((unsigned long long)( |
| frame_duration_delta) * static_ramp_variables-> |
| ramp_current_frame_duration_in_ns), 16666666); |
| |
| /* Going to a higher refresh rate (lower frame duration) */ |
| if (static_ramp_variables->ramp_direction_is_up) { |
| /* reduce frame duration */ |
| static_ramp_variables->ramp_current_frame_duration_in_ns -= |
| ramp_rate_interpolated; |
| |
| /* min frame duration */ |
| frame_duration = ((unsigned int) (div64_u64( |
| (1000000000ULL * 1000000), |
| core_freesync->map[index].state. |
| nominal_refresh_rate_in_micro_hz))); |
| |
| /* adjust for frame duration below min */ |
| if (static_ramp_variables->ramp_current_frame_duration_in_ns <= |
| frame_duration) { |
| |
| static_ramp_variables->ramp_is_active = false; |
| static_ramp_variables-> |
| ramp_current_frame_duration_in_ns = |
| frame_duration; |
| } |
| /* Going to a lower refresh rate (larger frame duration) */ |
| } else { |
| /* increase frame duration */ |
| static_ramp_variables->ramp_current_frame_duration_in_ns += |
| ramp_rate_interpolated; |
| |
| /* max frame duration */ |
| frame_duration = ((unsigned int) (div64_u64( |
| (1000000000ULL * 1000000), |
| core_freesync->map[index].caps->min_refresh_in_micro_hz))); |
| |
| /* adjust for frame duration above max */ |
| if (static_ramp_variables->ramp_current_frame_duration_in_ns >= |
| frame_duration) { |
| |
| static_ramp_variables->ramp_is_active = false; |
| static_ramp_variables-> |
| ramp_current_frame_duration_in_ns = |
| frame_duration; |
| } |
| } |
| |
| calc_v_total_from_duration(stream, static_ramp_variables-> |
| ramp_current_frame_duration_in_ns, v_total); |
| } |
| |
| static void reset_freesync_state_variables(struct freesync_state* state) |
| { |
| state->static_ramp.ramp_is_active = false; |
| if (state->nominal_refresh_rate_in_micro_hz) |
| state->static_ramp.ramp_current_frame_duration_in_ns = |
| ((unsigned int) (div64_u64( |
| (1000000000ULL * 1000000), |
| state->nominal_refresh_rate_in_micro_hz))); |
| |
| state->btr.btr_active = false; |
| state->btr.frame_counter = 0; |
| state->btr.frames_to_insert = 0; |
| state->btr.inserted_frame_duration_in_us = 0; |
| state->btr.program_btr = false; |
| |
| state->fixed_refresh.fixed_active = false; |
| state->fixed_refresh.program_fixed = false; |
| } |
| /* |
| * Sets freesync mode on a stream depending on current freesync state. |
| */ |
| static bool set_freesync_on_streams(struct core_freesync *core_freesync, |
| struct dc_stream_state **streams, int num_streams) |
| { |
| int v_total_nominal = 0, v_total_min = 0, v_total_max = 0; |
| unsigned int stream_idx, map_index = 0; |
| struct freesync_state *state; |
| |
| if (num_streams == 0 || streams == NULL || num_streams > 1) |
| return false; |
| |
| for (stream_idx = 0; stream_idx < num_streams; stream_idx++) { |
| |
| map_index = map_index_from_stream(core_freesync, |
| streams[stream_idx]); |
| |
| state = &core_freesync->map[map_index].state; |
| |
| if (core_freesync->map[map_index].caps->supported) { |
| |
| /* Fullscreen has the topmost priority. If the |
| * fullscreen bit is set, we are in a fullscreen |
| * application where it should not matter if it is |
| * static screen. We should not check the static_screen |
| * or video bit. |
| * |
| * Special cases of fullscreen include btr and fixed |
| * refresh. We program btr on every flip and involves |
| * programming full range right before the last inserted frame. |
| * However, we do not want to program the full freesync range |
| * when fixed refresh is active, because we only program |
| * that logic once and this will override it. |
| */ |
| if (core_freesync->map[map_index].user_enable. |
| enable_for_gaming == true && |
| state->fullscreen == true && |
| state->fixed_refresh.fixed_active == false) { |
| /* Enable freesync */ |
| |
| v_total_min = state->freesync_range.vmin; |
| v_total_max = state->freesync_range.vmax; |
| |
| /* Update the freesync context for the stream */ |
| update_stream_freesync_context(core_freesync, |
| streams[stream_idx]); |
| |
| adjust_vmin_vmax(core_freesync, streams, |
| num_streams, map_index, |
| v_total_min, |
| v_total_max); |
| |
| return true; |
| |
| } else if (core_freesync->map[map_index].user_enable. |
| enable_for_video && state->video == true) { |
| /* Enable 48Hz feature */ |
| |
| calc_v_total_from_duration(streams[stream_idx], |
| state->time.update_duration_in_ns, |
| &v_total_nominal); |
| |
| /* Program only if v_total_nominal is in range*/ |
| if (v_total_nominal >= |
| streams[stream_idx]->timing.v_total) { |
| |
| /* Update the freesync context for |
| * the stream |
| */ |
| update_stream_freesync_context( |
| core_freesync, |
| streams[stream_idx]); |
| |
| adjust_vmin_vmax( |
| core_freesync, streams, |
| num_streams, map_index, |
| v_total_nominal, |
| v_total_nominal); |
| } |
| return true; |
| |
| } else { |
| /* Disable freesync */ |
| v_total_nominal = streams[stream_idx]-> |
| timing.v_total; |
| |
| /* Update the freesync context for |
| * the stream |
| */ |
| update_stream_freesync_context( |
| core_freesync, |
| streams[stream_idx]); |
| |
| adjust_vmin_vmax(core_freesync, streams, |
| num_streams, map_index, |
| v_total_nominal, |
| v_total_nominal); |
| |
| /* Reset the cached variables */ |
| reset_freesync_state_variables(state); |
| |
| return true; |
| } |
| } else { |
| /* Disable freesync */ |
| v_total_nominal = streams[stream_idx]-> |
| timing.v_total; |
| /* |
| * we have to reset drr always even sink does |
| * not support freesync because a former stream has |
| * be programmed |
| */ |
| adjust_vmin_vmax(core_freesync, streams, |
| num_streams, map_index, |
| v_total_nominal, |
| v_total_nominal); |
| /* Reset the cached variables */ |
| reset_freesync_state_variables(state); |
| } |
| |
| } |
| |
| return false; |
| } |
| |
| static void set_static_ramp_variables(struct core_freesync *core_freesync, |
| unsigned int index, bool enable_static_screen) |
| { |
| unsigned int frame_duration = 0; |
| unsigned int nominal_refresh_rate = core_freesync->map[index].state. |
| nominal_refresh_rate_in_micro_hz; |
| unsigned int min_refresh_rate= core_freesync->map[index].caps-> |
| min_refresh_in_micro_hz; |
| struct gradual_static_ramp *static_ramp_variables = |
| &core_freesync->map[index].state.static_ramp; |
| |
| /* If we are ENABLING static screen, refresh rate should go DOWN. |
| * If we are DISABLING static screen, refresh rate should go UP. |
| */ |
| if (enable_static_screen) |
| static_ramp_variables->ramp_direction_is_up = false; |
| else |
| static_ramp_variables->ramp_direction_is_up = true; |
| |
| /* If ramp is not active, set initial frame duration depending on |
| * whether we are enabling/disabling static screen mode. If the ramp is |
| * already active, ramp should continue in the opposite direction |
| * starting with the current frame duration |
| */ |
| if (!static_ramp_variables->ramp_is_active) { |
| if (enable_static_screen == true) { |
| /* Going to lower refresh rate, so start from max |
| * refresh rate (min frame duration) |
| */ |
| frame_duration = ((unsigned int) (div64_u64( |
| (1000000000ULL * 1000000), |
| nominal_refresh_rate))); |
| } else { |
| /* Going to higher refresh rate, so start from min |
| * refresh rate (max frame duration) |
| */ |
| frame_duration = ((unsigned int) (div64_u64( |
| (1000000000ULL * 1000000), |
| min_refresh_rate))); |
| } |
| static_ramp_variables-> |
| ramp_current_frame_duration_in_ns = frame_duration; |
| |
| static_ramp_variables->ramp_is_active = true; |
| } |
| } |
| |
| void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, |
| struct dc_stream_state **streams, int num_streams) |
| { |
| unsigned int index, v_total, inserted_frame_v_total = 0; |
| unsigned int min_frame_duration_in_ns, vmax, vmin = 0; |
| struct freesync_state *state; |
| struct core_freesync *core_freesync = NULL; |
| struct dc_static_screen_events triggers = {0}; |
| |
| if (mod_freesync == NULL) |
| return; |
| |
| core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); |
| |
| if (core_freesync->num_entities == 0) |
| return; |
| |
| index = map_index_from_stream(core_freesync, |
| streams[0]); |
| |
| if (core_freesync->map[index].caps->supported == false) |
| return; |
| |
| state = &core_freesync->map[index].state; |
| |
| /* Below the Range Logic */ |
| |
| /* Only execute if in fullscreen mode */ |
| if (state->fullscreen == true && |
| core_freesync->map[index].user_enable.enable_for_gaming && |
| core_freesync->map[index].caps->btr_supported && |
| state->btr.btr_active) { |
| |
| /* TODO: pass in flag for Pre-DCE12 ASIC |
| * in order for frame variable duration to take affect, |
| * it needs to be done one VSYNC early, which is at |
| * frameCounter == 1. |
| * For DCE12 and newer updates to V_TOTAL_MIN/MAX |
| * will take affect on current frame |
| */ |
| if (state->btr.frames_to_insert == state->btr.frame_counter) { |
| |
| min_frame_duration_in_ns = ((unsigned int) (div64_u64( |
| (1000000000ULL * 1000000), |
| state->nominal_refresh_rate_in_micro_hz))); |
| |
| vmin = state->freesync_range.vmin; |
| |
| inserted_frame_v_total = vmin; |
| |
| if (min_frame_duration_in_ns / 1000) |
| inserted_frame_v_total = |
| state->btr.inserted_frame_duration_in_us * |
| vmin / (min_frame_duration_in_ns / 1000); |
| |
| /* Set length of inserted frames as v_total_max*/ |
| vmax = inserted_frame_v_total; |
| vmin = inserted_frame_v_total; |
| |
| /* Program V_TOTAL */ |
| adjust_vmin_vmax(core_freesync, streams, |
| num_streams, index, |
| vmin, vmax); |
| } |
| |
| if (state->btr.frame_counter > 0) |
| state->btr.frame_counter--; |
| |
| /* Restore FreeSync */ |
| if (state->btr.frame_counter == 0) |
| set_freesync_on_streams(core_freesync, streams, num_streams); |
| } |
| |
| /* If in fullscreen freesync mode or in video, do not program |
| * static screen ramp values |
| */ |
| if (state->fullscreen == true || state->video == true) { |
| |
| state->static_ramp.ramp_is_active = false; |
| |
| return; |
| } |
| |
| /* Gradual Static Screen Ramping Logic */ |
| |
| /* Execute if ramp is active and user enabled freesync static screen*/ |
| if (state->static_ramp.ramp_is_active && |
| core_freesync->map[index].user_enable.enable_for_static) { |
| |
| calc_v_total_for_static_ramp(core_freesync, streams[0], |
| index, &v_total); |
| |
| /* Update the freesync context for the stream */ |
| update_stream_freesync_context(core_freesync, streams[0]); |
| |
| /* Program static screen ramp values */ |
| adjust_vmin_vmax(core_freesync, streams, |
| num_streams, index, |
| v_total, |
| v_total); |
| |
| triggers.overlay_update = true; |
| triggers.surface_update = true; |
| |
| dc_stream_set_static_screen_events(core_freesync->dc, streams, |
| num_streams, &triggers); |
| } |
| } |
| |
| void mod_freesync_update_state(struct mod_freesync *mod_freesync, |
| struct dc_stream_state **streams, int num_streams, |
| struct mod_freesync_params *freesync_params) |
| { |
| bool freesync_program_required = false; |
| unsigned int stream_index; |
| struct freesync_state *state; |
| struct core_freesync *core_freesync = NULL; |
| struct dc_static_screen_events triggers = {0}; |
| |
| if (mod_freesync == NULL) |
| return; |
| |
| core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); |
| |
| if (core_freesync->num_entities == 0) |
| return; |
| |
| for(stream_index = 0; stream_index < num_streams; stream_index++) { |
| |
| unsigned int map_index = map_index_from_stream(core_freesync, |
| streams[stream_index]); |
| |
| bool is_embedded = dc_is_embedded_signal( |
| streams[stream_index]->sink->sink_signal); |
| |
| struct freesync_registry_options *opts = &core_freesync->opts; |
| |
| state = &core_freesync->map[map_index].state; |
| |
| switch (freesync_params->state){ |
| case FREESYNC_STATE_FULLSCREEN: |
| state->fullscreen = freesync_params->enable; |
| freesync_program_required = true; |
| state->windowed_fullscreen = |
| freesync_params->windowed_fullscreen; |
| break; |
| case FREESYNC_STATE_STATIC_SCREEN: |
| /* Static screen ramp is disabled by default, but can |
| * be enabled through regkey. |
| */ |
| if ((is_embedded && opts->drr_internal_supported) || |
| (!is_embedded && opts->drr_external_supported)) |
| |
| if (state->static_screen != |
| freesync_params->enable) { |
| |
| /* Change the state flag */ |
| state->static_screen = |
| freesync_params->enable; |
| |
| /* Update static screen ramp */ |
| set_static_ramp_variables(core_freesync, |
| map_index, |
| freesync_params->enable); |
| } |
| /* We program the ramp starting next VUpdate */ |
| break; |
| case FREESYNC_STATE_VIDEO: |
| /* Change core variables only if there is a change*/ |
| if(freesync_params->update_duration_in_ns != |
| state->time.update_duration_in_ns) { |
| |
| state->video = freesync_params->enable; |
| state->time.update_duration_in_ns = |
| freesync_params->update_duration_in_ns; |
| |
| freesync_program_required = true; |
| } |
| break; |
| case FREESYNC_STATE_NONE: |
| /* handle here to avoid warning */ |
| break; |
| } |
| } |
| |
| /* Update mask */ |
| triggers.overlay_update = true; |
| triggers.surface_update = true; |
| |
| dc_stream_set_static_screen_events(core_freesync->dc, streams, |
| num_streams, &triggers); |
| |
| if (freesync_program_required) |
| /* Program freesync according to current state*/ |
| set_freesync_on_streams(core_freesync, streams, num_streams); |
| } |
| |
| |
| bool mod_freesync_get_state(struct mod_freesync *mod_freesync, |
| struct dc_stream_state *stream, |
| struct mod_freesync_params *freesync_params) |
| { |
| unsigned int index = 0; |
| struct core_freesync *core_freesync = NULL; |
| |
| if (mod_freesync == NULL) |
| return false; |
| |
| core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); |
| index = map_index_from_stream(core_freesync, stream); |
| |
| if (core_freesync->map[index].state.fullscreen) { |
| freesync_params->state = FREESYNC_STATE_FULLSCREEN; |
| freesync_params->enable = true; |
| } else if (core_freesync->map[index].state.static_screen) { |
| freesync_params->state = FREESYNC_STATE_STATIC_SCREEN; |
| freesync_params->enable = true; |
| } else if (core_freesync->map[index].state.video) { |
| freesync_params->state = FREESYNC_STATE_VIDEO; |
| freesync_params->enable = true; |
| } else { |
| freesync_params->state = FREESYNC_STATE_NONE; |
| freesync_params->enable = false; |
| } |
| |
| freesync_params->update_duration_in_ns = |
| core_freesync->map[index].state.time.update_duration_in_ns; |
| |
| freesync_params->windowed_fullscreen = |
| core_freesync->map[index].state.windowed_fullscreen; |
| |
| return true; |
| } |
| |
| bool mod_freesync_set_user_enable(struct mod_freesync *mod_freesync, |
| struct dc_stream_state **streams, int num_streams, |
| struct mod_freesync_user_enable *user_enable) |
| { |
| unsigned int stream_index, map_index; |
| int persistent_data = 0; |
| struct persistent_data_flag flag; |
| struct dc *dc = NULL; |
| struct core_freesync *core_freesync = NULL; |
| |
| if (mod_freesync == NULL) |
| return false; |
| |
| core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); |
| dc = core_freesync->dc; |
| |
| flag.save_per_edid = true; |
| flag.save_per_link = false; |
| |
| for(stream_index = 0; stream_index < num_streams; |
| stream_index++){ |
| |
| map_index = map_index_from_stream(core_freesync, |
| streams[stream_index]); |
| |
| core_freesync->map[map_index].user_enable = *user_enable; |
| |
| /* Write persistent data in registry*/ |
| if (core_freesync->map[map_index].user_enable. |
| enable_for_gaming) |
| persistent_data = persistent_data | 1; |
| if (core_freesync->map[map_index].user_enable. |
| enable_for_static) |
| persistent_data = persistent_data | 2; |
| if (core_freesync->map[map_index].user_enable. |
| enable_for_video) |
| persistent_data = persistent_data | 4; |
| |
| dm_write_persistent_data(dc->ctx, |
| streams[stream_index]->sink, |
| FREESYNC_REGISTRY_NAME, |
| "userenable", |
| &persistent_data, |
| sizeof(int), |
| &flag); |
| } |
| |
| set_freesync_on_streams(core_freesync, streams, num_streams); |
| |
| return true; |
| } |
| |
| bool mod_freesync_get_user_enable(struct mod_freesync *mod_freesync, |
| struct dc_stream_state *stream, |
| struct mod_freesync_user_enable *user_enable) |
| { |
| unsigned int index = 0; |
| struct core_freesync *core_freesync = NULL; |
| |
| if (mod_freesync == NULL) |
| return false; |
| |
| core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); |
| index = map_index_from_stream(core_freesync, stream); |
| |
| *user_enable = core_freesync->map[index].user_enable; |
| |
| return true; |
| } |
| |
| bool mod_freesync_get_static_ramp_active(struct mod_freesync *mod_freesync, |
| struct dc_stream_state *stream, |
| bool *is_ramp_active) |
| { |
| unsigned int index = 0; |
| struct core_freesync *core_freesync = NULL; |
| |
| if (mod_freesync == NULL) |
| return false; |
| |
| core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); |
| index = map_index_from_stream(core_freesync, stream); |
| |
| *is_ramp_active = |
| core_freesync->map[index].state.static_ramp.ramp_is_active; |
| |
| return true; |
| } |
| |
| bool mod_freesync_override_min_max(struct mod_freesync *mod_freesync, |
| struct dc_stream_state *streams, |
| unsigned int min_refresh, |
| unsigned int max_refresh, |
| struct mod_freesync_caps *caps) |
| { |
| unsigned int index = 0; |
| struct core_freesync *core_freesync; |
| struct freesync_state *state; |
| |
| if (mod_freesync == NULL) |
| return false; |
| |
| core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); |
| index = map_index_from_stream(core_freesync, streams); |
| state = &core_freesync->map[index].state; |
| |
| if (max_refresh == 0) |
| max_refresh = state->nominal_refresh_rate_in_micro_hz; |
| |
| if (min_refresh == 0) { |
| /* Restore defaults */ |
| calc_freesync_range(core_freesync, streams, state, |
| core_freesync->map[index].caps-> |
| min_refresh_in_micro_hz, |
| state->nominal_refresh_rate_in_micro_hz); |
| } else { |
| calc_freesync_range(core_freesync, streams, |
| state, |
| min_refresh, |
| max_refresh); |
| |
| /* Program vtotal min/max */ |
| adjust_vmin_vmax(core_freesync, &streams, 1, index, |
| state->freesync_range.vmin, |
| state->freesync_range.vmax); |
| } |
| |
| if (min_refresh != 0 && |
| dc_is_embedded_signal(streams->sink->sink_signal) && |
| (max_refresh - min_refresh >= 10000000)) { |
| caps->supported = true; |
| caps->min_refresh_in_micro_hz = min_refresh; |
| caps->max_refresh_in_micro_hz = max_refresh; |
| } |
| |
| /* Update the stream */ |
| update_stream(core_freesync, streams); |
| |
| return true; |
| } |
| |
| bool mod_freesync_get_min_max(struct mod_freesync *mod_freesync, |
| struct dc_stream_state *stream, |
| unsigned int *min_refresh, |
| unsigned int *max_refresh) |
| { |
| unsigned int index = 0; |
| struct core_freesync *core_freesync = NULL; |
| |
| if (mod_freesync == NULL) |
| return false; |
| |
| core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); |
| index = map_index_from_stream(core_freesync, stream); |
| |
| *min_refresh = |
| core_freesync->map[index].state.freesync_range.min_refresh; |
| *max_refresh = |
| core_freesync->map[index].state.freesync_range.max_refresh; |
| |
| return true; |
| } |
| |
| bool mod_freesync_get_vmin_vmax(struct mod_freesync *mod_freesync, |
| struct dc_stream_state *stream, |
| unsigned int *vmin, |
| unsigned int *vmax) |
| { |
| unsigned int index = 0; |
| struct core_freesync *core_freesync = NULL; |
| |
| if (mod_freesync == NULL) |
| return false; |
| |
| core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); |
| index = map_index_from_stream(core_freesync, stream); |
| |
| *vmin = |
| core_freesync->map[index].state.freesync_range.vmin; |
| *vmax = |
| core_freesync->map[index].state.freesync_range.vmax; |
| |
| return true; |
| } |
| |
| bool mod_freesync_get_v_position(struct mod_freesync *mod_freesync, |
| struct dc_stream_state *stream, |
| unsigned int *nom_v_pos, |
| unsigned int *v_pos) |
| { |
| unsigned int index = 0; |
| struct core_freesync *core_freesync = NULL; |
| struct crtc_position position; |
| |
| if (mod_freesync == NULL) |
| return false; |
| |
| core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); |
| index = map_index_from_stream(core_freesync, stream); |
| |
| if (dc_stream_get_crtc_position(core_freesync->dc, &stream, 1, |
| &position.vertical_count, |
| &position.nominal_vcount)) { |
| |
| *nom_v_pos = position.nominal_vcount; |
| *v_pos = position.vertical_count; |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void mod_freesync_notify_mode_change(struct mod_freesync *mod_freesync, |
| struct dc_stream_state **streams, int num_streams) |
| { |
| unsigned int stream_index, map_index; |
| struct freesync_state *state; |
| struct core_freesync *core_freesync = NULL; |
| struct dc_static_screen_events triggers = {0}; |
| unsigned long long temp = 0; |
| |
| if (mod_freesync == NULL) |
| return; |
| |
| core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); |
| |
| for (stream_index = 0; stream_index < num_streams; stream_index++) { |
| map_index = map_index_from_stream(core_freesync, |
| streams[stream_index]); |
| |
| state = &core_freesync->map[map_index].state; |
| |
| /* Update the field rate for new timing */ |
| temp = streams[stream_index]->timing.pix_clk_khz; |
| temp *= 1000ULL * 1000ULL * 1000ULL; |
| temp = div_u64(temp, |
| streams[stream_index]->timing.h_total); |
| temp = div_u64(temp, |
| streams[stream_index]->timing.v_total); |
| state->nominal_refresh_rate_in_micro_hz = |
| (unsigned int) temp; |
| |
| if (core_freesync->map[map_index].caps->supported) { |
| |
| /* Update the stream */ |
| update_stream(core_freesync, streams[stream_index]); |
| |
| /* Calculate vmin/vmax and refresh rate for |
| * current mode |
| */ |
| calc_freesync_range(core_freesync, *streams, state, |
| core_freesync->map[map_index].caps-> |
| min_refresh_in_micro_hz, |
| state->nominal_refresh_rate_in_micro_hz); |
| |
| /* Update mask */ |
| triggers.overlay_update = true; |
| triggers.surface_update = true; |
| |
| dc_stream_set_static_screen_events(core_freesync->dc, |
| streams, num_streams, |
| &triggers); |
| } |
| } |
| |
| /* Program freesync according to current state*/ |
| set_freesync_on_streams(core_freesync, streams, num_streams); |
| } |
| |
| /* Add the timestamps to the cache and determine whether BTR programming |
| * is required, depending on the times calculated |
| */ |
| static void update_timestamps(struct core_freesync *core_freesync, |
| const struct dc_stream_state *stream, unsigned int map_index, |
| unsigned int last_render_time_in_us) |
| { |
| struct freesync_state *state = &core_freesync->map[map_index].state; |
| |
| state->time.render_times[state->time.render_times_index] = |
| last_render_time_in_us; |
| state->time.render_times_index++; |
| |
| if (state->time.render_times_index >= RENDER_TIMES_MAX_COUNT) |
| state->time.render_times_index = 0; |
| |
| if (last_render_time_in_us + BTR_EXIT_MARGIN < |
| state->time.max_render_time_in_us) { |
| |
| /* Exit Below the Range */ |
| if (state->btr.btr_active) { |
| |
| state->btr.program_btr = true; |
| state->btr.btr_active = false; |
| state->btr.frame_counter = 0; |
| |
| /* Exit Fixed Refresh mode */ |
| } else if (state->fixed_refresh.fixed_active) { |
| |
| state->fixed_refresh.frame_counter++; |
| |
| if (state->fixed_refresh.frame_counter > |
| FIXED_REFRESH_EXIT_FRAME_COUNT) { |
| state->fixed_refresh.frame_counter = 0; |
| state->fixed_refresh.program_fixed = true; |
| state->fixed_refresh.fixed_active = false; |
| } |
| } |
| |
| } else if (last_render_time_in_us > state->time.max_render_time_in_us) { |
| |
| /* Enter Below the Range */ |
| if (!state->btr.btr_active && |
| core_freesync->map[map_index].caps->btr_supported) { |
| |
| state->btr.program_btr = true; |
| state->btr.btr_active = true; |
| |
| /* Enter Fixed Refresh mode */ |
| } else if (!state->fixed_refresh.fixed_active && |
| !core_freesync->map[map_index].caps->btr_supported) { |
| |
| state->fixed_refresh.frame_counter++; |
| |
| if (state->fixed_refresh.frame_counter > |
| FIXED_REFRESH_ENTER_FRAME_COUNT) { |
| state->fixed_refresh.frame_counter = 0; |
| state->fixed_refresh.program_fixed = true; |
| state->fixed_refresh.fixed_active = true; |
| } |
| } |
| } |
| |
| /* When Below the Range is active, must react on every frame */ |
| if (state->btr.btr_active) |
| state->btr.program_btr = true; |
| } |
| |
| static void apply_below_the_range(struct core_freesync *core_freesync, |
| struct dc_stream_state *stream, unsigned int map_index, |
| unsigned int last_render_time_in_us) |
| { |
| unsigned int inserted_frame_duration_in_us = 0; |
| unsigned int mid_point_frames_ceil = 0; |
| unsigned int mid_point_frames_floor = 0; |
| unsigned int frame_time_in_us = 0; |
| unsigned int delta_from_mid_point_in_us_1 = 0xFFFFFFFF; |
| unsigned int delta_from_mid_point_in_us_2 = 0xFFFFFFFF; |
| unsigned int frames_to_insert = 0; |
| unsigned int min_frame_duration_in_ns = 0; |
| struct freesync_state *state = &core_freesync->map[map_index].state; |
| |
| if (!state->btr.program_btr) |
| return; |
| |
| state->btr.program_btr = false; |
| |
| min_frame_duration_in_ns = ((unsigned int) (div64_u64( |
| (1000000000ULL * 1000000), |
| state->nominal_refresh_rate_in_micro_hz))); |
| |
| /* Program BTR */ |
| |
| /* BTR set to "not active" so disengage */ |
| if (!state->btr.btr_active) |
| |
| /* Restore FreeSync */ |
| set_freesync_on_streams(core_freesync, &stream, 1); |
| |
| /* BTR set to "active" so engage */ |
| else { |
| |
| /* Calculate number of midPoint frames that could fit within |
| * the render time interval- take ceil of this value |
| */ |
| mid_point_frames_ceil = (last_render_time_in_us + |
| state->btr.mid_point_in_us- 1) / |
| state->btr.mid_point_in_us; |
| |
| if (mid_point_frames_ceil > 0) { |
| |
| frame_time_in_us = last_render_time_in_us / |
| mid_point_frames_ceil; |
| delta_from_mid_point_in_us_1 = |
| (state->btr.mid_point_in_us > |
| frame_time_in_us) ? |
| (state->btr.mid_point_in_us - frame_time_in_us): |
| (frame_time_in_us - state->btr.mid_point_in_us); |
| } |
| |
| /* Calculate number of midPoint frames that could fit within |
| * the render time interval- take floor of this value |
| */ |
| mid_point_frames_floor = last_render_time_in_us / |
| state->btr.mid_point_in_us; |
| |
| if (mid_point_frames_floor > 0) { |
| |
| frame_time_in_us = last_render_time_in_us / |
| mid_point_frames_floor; |
| delta_from_mid_point_in_us_2 = |
| (state->btr.mid_point_in_us > |
| frame_time_in_us) ? |
| (state->btr.mid_point_in_us - frame_time_in_us): |
| (frame_time_in_us - state->btr.mid_point_in_us); |
| } |
| |
| /* Choose number of frames to insert based on how close it |
| * can get to the mid point of the variable range. |
| */ |
| if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) |
| frames_to_insert = mid_point_frames_ceil; |
| else |
| frames_to_insert = mid_point_frames_floor; |
| |
| /* Either we've calculated the number of frames to insert, |
| * or we need to insert min duration frames |
| */ |
| if (frames_to_insert > 0) |
| inserted_frame_duration_in_us = last_render_time_in_us / |
| frames_to_insert; |
| |
| if (inserted_frame_duration_in_us < |
| state->time.min_render_time_in_us) |
| |
| inserted_frame_duration_in_us = |
| state->time.min_render_time_in_us; |
| |
| /* Cache the calculated variables */ |
| state->btr.inserted_frame_duration_in_us = |
| inserted_frame_duration_in_us; |
| state->btr.frames_to_insert = frames_to_insert; |
| state->btr.frame_counter = frames_to_insert; |
| |
| } |
| } |
| |
| static void apply_fixed_refresh(struct core_freesync *core_freesync, |
| struct dc_stream_state *stream, unsigned int map_index) |
| { |
| unsigned int vmin = 0, vmax = 0; |
| struct freesync_state *state = &core_freesync->map[map_index].state; |
| |
| if (!state->fixed_refresh.program_fixed) |
| return; |
| |
| state->fixed_refresh.program_fixed = false; |
| |
| /* Program Fixed Refresh */ |
| |
| /* Fixed Refresh set to "not active" so disengage */ |
| if (!state->fixed_refresh.fixed_active) { |
| set_freesync_on_streams(core_freesync, &stream, 1); |
| |
| /* Fixed Refresh set to "active" so engage (fix to max) */ |
| } else { |
| |
| vmin = state->freesync_range.vmin; |
| vmax = vmin; |
| adjust_vmin_vmax(core_freesync, &stream, map_index, |
| 1, vmin, vmax); |
| } |
| } |
| |
| void mod_freesync_pre_update_plane_addresses(struct mod_freesync *mod_freesync, |
| struct dc_stream_state **streams, int num_streams, |
| unsigned int curr_time_stamp_in_us) |
| { |
| unsigned int stream_index, map_index, last_render_time_in_us = 0; |
| struct core_freesync *core_freesync = NULL; |
| |
| if (mod_freesync == NULL) |
| return; |
| |
| core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); |
| |
| for (stream_index = 0; stream_index < num_streams; stream_index++) { |
| |
| map_index = map_index_from_stream(core_freesync, |
| streams[stream_index]); |
| |
| if (core_freesync->map[map_index].caps->supported) { |
| |
| last_render_time_in_us = curr_time_stamp_in_us - |
| core_freesync->map[map_index].state.time. |
| prev_time_stamp_in_us; |
| |
| /* Add the timestamps to the cache and determine |
| * whether BTR program is required |
| */ |
| update_timestamps(core_freesync, streams[stream_index], |
| map_index, last_render_time_in_us); |
| |
| if (core_freesync->map[map_index].state.fullscreen && |
| core_freesync->map[map_index].user_enable. |
| enable_for_gaming) { |
| |
| if (core_freesync->map[map_index].caps->btr_supported) { |
| |
| apply_below_the_range(core_freesync, |
| streams[stream_index], map_index, |
| last_render_time_in_us); |
| } else { |
| apply_fixed_refresh(core_freesync, |
| streams[stream_index], map_index); |
| } |
| } |
| |
| core_freesync->map[map_index].state.time. |
| prev_time_stamp_in_us = curr_time_stamp_in_us; |
| } |
| |
| } |
| } |
| |
| void mod_freesync_get_settings(struct mod_freesync *mod_freesync, |
| struct dc_stream_state **streams, int num_streams, |
| unsigned int *v_total_min, unsigned int *v_total_max, |
| unsigned int *event_triggers, |
| unsigned int *window_min, unsigned int *window_max, |
| unsigned int *lfc_mid_point_in_us, |
| unsigned int *inserted_frames, |
| unsigned int *inserted_duration_in_us) |
| { |
| unsigned int stream_index, map_index; |
| struct core_freesync *core_freesync = NULL; |
| |
| if (mod_freesync == NULL) |
| return; |
| |
| core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); |
| |
| for (stream_index = 0; stream_index < num_streams; stream_index++) { |
| |
| map_index = map_index_from_stream(core_freesync, |
| streams[stream_index]); |
| |
| if (core_freesync->map[map_index].caps->supported) { |
| struct freesync_state state = |
| core_freesync->map[map_index].state; |
| *v_total_min = state.vmin; |
| *v_total_max = state.vmax; |
| *event_triggers = 0; |
| *window_min = state.time.min_window; |
| *window_max = state.time.max_window; |
| *lfc_mid_point_in_us = state.btr.mid_point_in_us; |
| *inserted_frames = state.btr.frames_to_insert; |
| *inserted_duration_in_us = |
| state.btr.inserted_frame_duration_in_us; |
| } |
| |
| } |
| } |
| |