1554 lines · 44.6 KB · C
RawDownload
/* * 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 = 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;	} 	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);} 

Lines 1–500 of 1554.