/* * 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 20/* 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" struct gradual_static_ramp { bool ramp_is_active; bool ramp_direction_is_up; unsigned int ramp_current_frame_duration_in_ns;}; struct time_cache { /* 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];}; 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 nominal_refresh_rate_in_micro_hz; bool windowed_fullscreen; struct time_cache time; 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;}; struct core_freesync { struct mod_freesync public; struct dc *dc; struct freesync_entity *map; int num_entities; struct freesync_registry_options opts;}; #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 = kzalloc(sizeof(struct freesync_entity) * MOD_FREESYNC_MAX_CONCURRENT_STREAMS, 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; } 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; } 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 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 {Lines 1–500 of 1459. Load more
/* * 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 20 /* 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" struct gradual_static_ramp { bool ramp_is_active; bool ramp_direction_is_up; unsigned int ramp_current_frame_duration_in_ns; }; struct time_cache { /* 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]; }; 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 nominal_refresh_rate_in_micro_hz; bool windowed_fullscreen; struct time_cache time; 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; }; struct core_freesync { struct mod_freesync public; struct dc *dc; struct freesync_entity *map; int num_entities; struct freesync_registry_options opts; }; #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 = kzalloc(sizeof(struct freesync_entity) * MOD_FREESYNC_MAX_CONCURRENT_STREAMS, 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; } 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; } 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 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 {