brintos

brintos / linux-shallow public Read only

0
0
Text · 35.3 KiB · 3d29169 Raw
913 lines · c
1/* SPDX-License-Identifier: MIT */2/*3 * Copyright 2023 Advanced Micro Devices, Inc.4 *5 * Permission is hereby granted, free of charge, to any person obtaining a6 * copy of this software and associated documentation files (the "Software"),7 * to deal in the Software without restriction, including without limitation8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,9 * and/or sell copies of the Software, and to permit persons to whom the10 * Software is furnished to do so, subject to the following conditions:11 *12 * The above copyright notice and this permission notice shall be included in13 * all copies or substantial portions of the Software.14 *15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL18 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR21 * OTHER DEALINGS IN THE SOFTWARE.22 *23 * Authors: AMD24 *25 */26 27#include "dml2_dc_types.h"28#include "dml2_internal_types.h"29#include "dml2_utils.h"30#include "dml2_mall_phantom.h"31 32unsigned int dml2_helper_calculate_num_ways_for_subvp(struct dml2_context *ctx, struct dc_state *context)33{34	uint32_t num_ways = 0;35	uint32_t bytes_per_pixel = 0;36	uint32_t cache_lines_used = 0;37	uint32_t lines_per_way = 0;38	uint32_t total_cache_lines = 0;39	uint32_t bytes_in_mall = 0;40	uint32_t num_mblks = 0;41	uint32_t cache_lines_per_plane = 0;42	uint32_t i = 0;43	uint32_t mblk_width = 0;44	uint32_t mblk_height = 0;45	uint32_t full_vp_width_blk_aligned = 0;46	uint32_t mall_alloc_width_blk_aligned = 0;47	uint32_t mall_alloc_height_blk_aligned = 0;48 49	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {50		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];51 52		// Find the phantom pipes53		if (pipe->stream && pipe->plane_state && !pipe->top_pipe && !pipe->prev_odm_pipe &&54				ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM) {55			bytes_per_pixel = pipe->plane_state->format >= SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616 ? 8 : 4;56			mblk_width = ctx->config.mall_cfg.mblk_width_pixels;57			mblk_height = bytes_per_pixel == 4 ? mblk_width = ctx->config.mall_cfg.mblk_height_4bpe_pixels : ctx->config.mall_cfg.mblk_height_8bpe_pixels;58 59			/* full_vp_width_blk_aligned = FLOOR(vp_x_start + full_vp_width + blk_width - 1, blk_width) -60			 * FLOOR(vp_x_start, blk_width)61			 */62			full_vp_width_blk_aligned = ((pipe->plane_res.scl_data.viewport.x +63					pipe->plane_res.scl_data.viewport.width + mblk_width - 1) / mblk_width * mblk_width) +64					(pipe->plane_res.scl_data.viewport.x / mblk_width * mblk_width);65 66			/* mall_alloc_width_blk_aligned_l/c = full_vp_width_blk_aligned_l/c */67			mall_alloc_width_blk_aligned = full_vp_width_blk_aligned;68 69			/* mall_alloc_height_blk_aligned_l/c = CEILING(sub_vp_height_l/c - 1, blk_height_l/c) + blk_height_l/c */70			mall_alloc_height_blk_aligned = (pipe->stream->timing.v_addressable - 1 + mblk_height - 1) /71					mblk_height * mblk_height + mblk_height;72 73			/* full_mblk_width_ub_l/c = malldml2_mall_phantom.c_alloc_width_blk_aligned_l/c;74			 * full_mblk_height_ub_l/c = mall_alloc_height_blk_aligned_l/c;75			 * num_mblk_l/c = (full_mblk_width_ub_l/c / mblk_width_l/c) * (full_mblk_height_ub_l/c / mblk_height_l/c);76			 * (Should be divisible, but round up if not)77			 */78			num_mblks = ((mall_alloc_width_blk_aligned + mblk_width - 1) / mblk_width) *79					((mall_alloc_height_blk_aligned + mblk_height - 1) / mblk_height);80			bytes_in_mall = num_mblks * ctx->config.mall_cfg.mblk_size_bytes;81			// cache lines used is total bytes / cache_line size. Add +2 for worst case alignment82			// (MALL is 64-byte aligned)83			cache_lines_per_plane = bytes_in_mall / ctx->config.mall_cfg.cache_line_size_bytes + 2;84 85			// For DCC we must cache the meat surface, so double cache lines required86			if (pipe->plane_state->dcc.enable)87				cache_lines_per_plane *= 2;88			cache_lines_used += cache_lines_per_plane;89		}90	}91 92	total_cache_lines = ctx->config.mall_cfg.max_cab_allocation_bytes / ctx->config.mall_cfg.cache_line_size_bytes;93	lines_per_way = total_cache_lines / ctx->config.mall_cfg.cache_num_ways;94	num_ways = cache_lines_used / lines_per_way;95	if (cache_lines_used % lines_per_way > 0)96		num_ways++;97 98	return num_ways;99}100 101static void merge_pipes_for_subvp(struct dml2_context *ctx, struct dc_state *context)102{103	int i;104 105	/* merge pipes if necessary */106	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {107		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];108 109		// For now merge all pipes for SubVP since pipe split case isn't supported yet110 111		/* if ODM merge we ignore mpc tree, mpo pipes will have their own flags */112		if (pipe->prev_odm_pipe) {113			/*split off odm pipe*/114			pipe->prev_odm_pipe->next_odm_pipe = pipe->next_odm_pipe;115			if (pipe->next_odm_pipe)116				pipe->next_odm_pipe->prev_odm_pipe = pipe->prev_odm_pipe;117 118			pipe->bottom_pipe = NULL;119			pipe->next_odm_pipe = NULL;120			pipe->plane_state = NULL;121			pipe->stream = NULL;122			pipe->top_pipe = NULL;123			pipe->prev_odm_pipe = NULL;124			if (pipe->stream_res.dsc)125				ctx->config.svp_pstate.callbacks.release_dsc(&context->res_ctx, ctx->config.svp_pstate.callbacks.dc->res_pool, &pipe->stream_res.dsc);126			memset(&pipe->plane_res, 0, sizeof(pipe->plane_res));127			memset(&pipe->stream_res, 0, sizeof(pipe->stream_res));128		} else if (pipe->top_pipe && pipe->top_pipe->plane_state == pipe->plane_state) {129			struct pipe_ctx *top_pipe = pipe->top_pipe;130			struct pipe_ctx *bottom_pipe = pipe->bottom_pipe;131 132			top_pipe->bottom_pipe = bottom_pipe;133			if (bottom_pipe)134				bottom_pipe->top_pipe = top_pipe;135 136			pipe->top_pipe = NULL;137			pipe->bottom_pipe = NULL;138			pipe->plane_state = NULL;139			pipe->stream = NULL;140			memset(&pipe->plane_res, 0, sizeof(pipe->plane_res));141			memset(&pipe->stream_res, 0, sizeof(pipe->stream_res));142		}143	}144}145 146static bool all_pipes_have_stream_and_plane(struct dml2_context *ctx, const struct dc_state *context)147{148	int i;149 150	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {151		const struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];152 153		if (!pipe->stream)154			continue;155 156		if (!pipe->plane_state)157			return false;158	}159	return true;160}161 162static bool mpo_in_use(const struct dc_state *context)163{164	int i;165 166	for (i = 0; i < context->stream_count; i++) {167		if (context->stream_status[i].plane_count > 1)168			return true;169	}170	return false;171}172 173/*174 * dcn32_get_num_free_pipes: Calculate number of free pipes175 *176 * This function assumes that a "used" pipe is a pipe that has177 * both a stream and a plane assigned to it.178 *179 * @dc: current dc state180 * @context: new dc state181 *182 * Return:183 * Number of free pipes available in the context184 */185static unsigned int get_num_free_pipes(struct dml2_context *ctx, struct dc_state *state)186{187	unsigned int i;188	unsigned int free_pipes = 0;189	unsigned int num_pipes = 0;190 191	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {192		struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];193 194		if (pipe->stream && !pipe->top_pipe) {195			while (pipe) {196				num_pipes++;197				pipe = pipe->bottom_pipe;198			}199		}200	}201 202	free_pipes = ctx->config.dcn_pipe_count - num_pipes;203	return free_pipes;204}205 206/*207 * dcn32_assign_subvp_pipe: Function to decide which pipe will use Sub-VP.208 *209 * We enter this function if we are Sub-VP capable (i.e. enough pipes available)210 * and regular P-State switching (i.e. VACTIVE/VBLANK) is not supported, or if211 * we are forcing SubVP P-State switching on the current config.212 *213 * The number of pipes used for the chosen surface must be less than or equal to the214 * number of free pipes available.215 *216 * In general we choose surfaces with the longest frame time first (better for SubVP + VBLANK).217 * For multi-display cases the ActiveDRAMClockChangeMargin doesn't provide enough info on its own218 * for determining which should be the SubVP pipe (need a way to determine if a pipe / plane doesn't219 * support MCLK switching naturally [i.e. ACTIVE or VBLANK]).220 *221 * @param dc: current dc state222 * @param context: new dc state223 * @param index: [out] dc pipe index for the pipe chosen to have phantom pipes assigned224 *225 * Return:226 * True if a valid pipe assignment was found for Sub-VP. Otherwise false.227 */228static bool assign_subvp_pipe(struct dml2_context *ctx, struct dc_state *context, unsigned int *index)229{230	unsigned int i, pipe_idx;231	unsigned int max_frame_time = 0;232	bool valid_assignment_found = false;233	unsigned int free_pipes = 2; //dcn32_get_num_free_pipes(dc, context);234	bool current_assignment_freesync = false;235	struct vba_vars_st *vba = &context->bw_ctx.dml.vba;236 237	for (i = 0, pipe_idx = 0; i < ctx->config.dcn_pipe_count; i++) {238		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];239		unsigned int num_pipes = 0;240		unsigned int refresh_rate = 0;241 242		if (!pipe->stream)243			continue;244 245		// Round up246		refresh_rate = (pipe->stream->timing.pix_clk_100hz * 100 +247				pipe->stream->timing.v_total * pipe->stream->timing.h_total - 1)248				/ (double)(pipe->stream->timing.v_total * pipe->stream->timing.h_total);249		/* SubVP pipe candidate requirements:250		 * - Refresh rate < 120hz251		 * - Not able to switch in vactive naturally (switching in active means the252		 *   DET provides enough buffer to hide the P-State switch latency -- trying253		 *   to combine this with SubVP can cause issues with the scheduling).254		 */255		if (pipe->plane_state && !pipe->top_pipe &&256				ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe) == SUBVP_NONE && refresh_rate < 120 &&257				vba->ActiveDRAMClockChangeLatencyMarginPerState[vba->VoltageLevel][vba->maxMpcComb][vba->pipe_plane[pipe_idx]] <= 0) {258			while (pipe) {259				num_pipes++;260				pipe = pipe->bottom_pipe;261			}262 263			pipe = &context->res_ctx.pipe_ctx[i];264			if (num_pipes <= free_pipes) {265				struct dc_stream_state *stream = pipe->stream;266				unsigned int frame_us = (stream->timing.v_total * stream->timing.h_total /267						(double)(stream->timing.pix_clk_100hz * 100)) * 1000000;268				if (frame_us > max_frame_time && !stream->ignore_msa_timing_param) {269					*index = i;270					max_frame_time = frame_us;271					valid_assignment_found = true;272					current_assignment_freesync = false;273				/* For the 2-Freesync display case, still choose the one with the274			     * longest frame time275			     */276				} else if (stream->ignore_msa_timing_param && (!valid_assignment_found ||277						(current_assignment_freesync && frame_us > max_frame_time))) {278					*index = i;279					valid_assignment_found = true;280					current_assignment_freesync = true;281				}282			}283		}284		pipe_idx++;285	}286	return valid_assignment_found;287}288 289/*290 * enough_pipes_for_subvp: Function to check if there are "enough" pipes for SubVP.291 *292 * This function returns true if there are enough free pipes293 * to create the required phantom pipes for any given stream294 * (that does not already have phantom pipe assigned).295 *296 * e.g. For a 2 stream config where the first stream uses one297 * pipe and the second stream uses 2 pipes (i.e. pipe split),298 * this function will return true because there is 1 remaining299 * pipe which can be used as the phantom pipe for the non pipe300 * split pipe.301 *302 * @dc: current dc state303 * @context: new dc state304 *305 * Return:306 * True if there are enough free pipes to assign phantom pipes to at least one307 * stream that does not already have phantom pipes assigned. Otherwise false.308 */309static bool enough_pipes_for_subvp(struct dml2_context *ctx, struct dc_state *state)310{311	unsigned int i, split_cnt, free_pipes;312	unsigned int min_pipe_split = ctx->config.dcn_pipe_count + 1; // init as max number of pipes + 1313	bool subvp_possible = false;314 315	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {316		struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];317 318		// Find the minimum pipe split count for non SubVP pipes319		if (pipe->stream && !pipe->top_pipe &&320				ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(state, pipe) == SUBVP_NONE) {321			split_cnt = 0;322			while (pipe) {323				split_cnt++;324				pipe = pipe->bottom_pipe;325			}326 327			if (split_cnt < min_pipe_split)328				min_pipe_split = split_cnt;329		}330	}331 332	free_pipes = get_num_free_pipes(ctx, state);333 334	// SubVP only possible if at least one pipe is being used (i.e. free_pipes335	// should not equal to the pipe_count)336	if (free_pipes >= min_pipe_split && free_pipes < ctx->config.dcn_pipe_count)337		subvp_possible = true;338 339	return subvp_possible;340}341 342/*343 * subvp_subvp_schedulable: Determine if SubVP + SubVP config is schedulable344 *345 * High level algorithm:346 * 1. Find longest microschedule length (in us) between the two SubVP pipes347 * 2. Check if the worst case overlap (VBLANK in middle of ACTIVE) for both348 * pipes still allows for the maximum microschedule to fit in the active349 * region for both pipes.350 *351 * @dc: current dc state352 * @context: new dc state353 *354 * Return:355 * bool - True if the SubVP + SubVP config is schedulable, false otherwise356 */357static bool subvp_subvp_schedulable(struct dml2_context *ctx, struct dc_state *context)358{359	struct pipe_ctx *subvp_pipes[2];360	struct dc_stream_state *phantom = NULL;361	uint32_t microschedule_lines = 0;362	uint32_t index = 0;363	uint32_t i;364	uint32_t max_microschedule_us = 0;365	int32_t vactive1_us, vactive2_us, vblank1_us, vblank2_us;366 367	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {368		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];369		uint32_t time_us = 0;370 371		/* Loop to calculate the maximum microschedule time between the two SubVP pipes,372		 * and also to store the two main SubVP pipe pointers in subvp_pipes[2].373		 */374		if (pipe->stream && pipe->plane_state && !pipe->top_pipe &&375				ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe) == SUBVP_MAIN) {376			phantom = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(context, pipe->stream);377			microschedule_lines = (phantom->timing.v_total - phantom->timing.v_front_porch) +378					phantom->timing.v_addressable;379 380			// Round up when calculating microschedule time (+ 1 at the end)381			time_us = (microschedule_lines * phantom->timing.h_total) /382					(double)(phantom->timing.pix_clk_100hz * 100) * 1000000 +383					ctx->config.svp_pstate.subvp_prefetch_end_to_mall_start_us +384					ctx->config.svp_pstate.subvp_fw_processing_delay_us + 1;385			if (time_us > max_microschedule_us)386				max_microschedule_us = time_us;387 388			subvp_pipes[index] = pipe;389			index++;390 391			// Maximum 2 SubVP pipes392			if (index == 2)393				break;394		}395	}396	vactive1_us = ((subvp_pipes[0]->stream->timing.v_addressable * subvp_pipes[0]->stream->timing.h_total) /397			(double)(subvp_pipes[0]->stream->timing.pix_clk_100hz * 100)) * 1000000;398	vactive2_us = ((subvp_pipes[1]->stream->timing.v_addressable * subvp_pipes[1]->stream->timing.h_total) /399				(double)(subvp_pipes[1]->stream->timing.pix_clk_100hz * 100)) * 1000000;400	vblank1_us = (((subvp_pipes[0]->stream->timing.v_total - subvp_pipes[0]->stream->timing.v_addressable) *401			subvp_pipes[0]->stream->timing.h_total) /402			(double)(subvp_pipes[0]->stream->timing.pix_clk_100hz * 100)) * 1000000;403	vblank2_us = (((subvp_pipes[1]->stream->timing.v_total - subvp_pipes[1]->stream->timing.v_addressable) *404			subvp_pipes[1]->stream->timing.h_total) /405			(double)(subvp_pipes[1]->stream->timing.pix_clk_100hz * 100)) * 1000000;406 407	if ((vactive1_us - vblank2_us) / 2 > max_microschedule_us &&408	    (vactive2_us - vblank1_us) / 2 > max_microschedule_us)409		return true;410 411	return false;412}413 414/*415 * dml2_svp_drr_schedulable: Determine if SubVP + DRR config is schedulable416 *417 * High level algorithm:418 * 1. Get timing for SubVP pipe, phantom pipe, and DRR pipe419 * 2. Determine the frame time for the DRR display when adding required margin for MCLK switching420 * (the margin is equal to the MALL region + DRR margin (500us))421 * 3.If (SubVP Active - Prefetch > Stretched DRR frame + max(MALL region, Stretched DRR frame))422 * then report the configuration as supported423 *424 * @dc: current dc state425 * @context: new dc state426 * @drr_pipe: DRR pipe_ctx for the SubVP + DRR config427 *428 * Return:429 * bool - True if the SubVP + DRR config is schedulable, false otherwise430 */431bool dml2_svp_drr_schedulable(struct dml2_context *ctx, struct dc_state *context, struct dc_crtc_timing *drr_timing)432{433	bool schedulable = false;434	uint32_t i;435	struct pipe_ctx *pipe = NULL;436	struct dc_crtc_timing *main_timing = NULL;437	struct dc_crtc_timing *phantom_timing = NULL;438	struct dc_stream_state *phantom_stream;439	int16_t prefetch_us = 0;440	int16_t mall_region_us = 0;441	int16_t drr_frame_us = 0;	// nominal frame time442	int16_t subvp_active_us = 0;443	int16_t stretched_drr_us = 0;444	int16_t drr_stretched_vblank_us = 0;445	int16_t max_vblank_mallregion = 0;446 447	// Find SubVP pipe448	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {449		pipe = &context->res_ctx.pipe_ctx[i];450 451		// We check for master pipe, but it shouldn't matter since we only need452		// the pipe for timing info (stream should be same for any pipe splits)453		if (!pipe->stream || !pipe->plane_state || pipe->top_pipe || pipe->prev_odm_pipe)454			continue;455 456		// Find the SubVP pipe457		if (ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe) == SUBVP_MAIN)458			break;459	}460 461	phantom_stream = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(context, pipe->stream);462	main_timing = &pipe->stream->timing;463	phantom_timing = &phantom_stream->timing;464	prefetch_us = (phantom_timing->v_total - phantom_timing->v_front_porch) * phantom_timing->h_total /465			(double)(phantom_timing->pix_clk_100hz * 100) * 1000000 +466			ctx->config.svp_pstate.subvp_prefetch_end_to_mall_start_us;467	subvp_active_us = main_timing->v_addressable * main_timing->h_total /468			(double)(main_timing->pix_clk_100hz * 100) * 1000000;469	drr_frame_us = drr_timing->v_total * drr_timing->h_total /470			(double)(drr_timing->pix_clk_100hz * 100) * 1000000;471	// P-State allow width and FW delays already included phantom_timing->v_addressable472	mall_region_us = phantom_timing->v_addressable * phantom_timing->h_total /473			(double)(phantom_timing->pix_clk_100hz * 100) * 1000000;474	stretched_drr_us = drr_frame_us + mall_region_us + SUBVP_DRR_MARGIN_US;475	drr_stretched_vblank_us = (drr_timing->v_total - drr_timing->v_addressable) * drr_timing->h_total /476			(double)(drr_timing->pix_clk_100hz * 100) * 1000000 + (stretched_drr_us - drr_frame_us);477	max_vblank_mallregion = drr_stretched_vblank_us > mall_region_us ? drr_stretched_vblank_us : mall_region_us;478 479	/* We consider SubVP + DRR schedulable if the stretched frame duration of the DRR display (i.e. the480	 * highest refresh rate + margin that can support UCLK P-State switch) passes the static analysis481	 * for VBLANK: (VACTIVE region of the SubVP pipe can fit the MALL prefetch, VBLANK frame time,482	 * and the max of (VBLANK blanking time, MALL region)).483	 */484	if (stretched_drr_us < (1 / (double)drr_timing->min_refresh_in_uhz) * 1000000 * 1000000 &&485			subvp_active_us - prefetch_us - stretched_drr_us - max_vblank_mallregion > 0)486		schedulable = true;487 488	return schedulable;489}490 491 492/*493 * subvp_vblank_schedulable: Determine if SubVP + VBLANK config is schedulable494 *495 * High level algorithm:496 * 1. Get timing for SubVP pipe, phantom pipe, and VBLANK pipe497 * 2. If (SubVP Active - Prefetch > Vblank Frame Time + max(MALL region, Vblank blanking time))498 * then report the configuration as supported499 * 3. If the VBLANK display is DRR, then take the DRR static schedulability path500 *501 * @dc: current dc state502 * @context: new dc state503 *504 * Return:505 * bool - True if the SubVP + VBLANK/DRR config is schedulable, false otherwise506 */507static bool subvp_vblank_schedulable(struct dml2_context *ctx, struct dc_state *context)508{509	struct pipe_ctx *pipe = NULL;510	struct pipe_ctx *subvp_pipe = NULL;511	bool found = false;512	bool schedulable = false;513	uint32_t i = 0;514	uint8_t vblank_index = 0;515	uint16_t prefetch_us = 0;516	uint16_t mall_region_us = 0;517	uint16_t vblank_frame_us = 0;518	uint16_t subvp_active_us = 0;519	uint16_t vblank_blank_us = 0;520	uint16_t max_vblank_mallregion = 0;521	struct dc_crtc_timing *main_timing = NULL;522	struct dc_crtc_timing *phantom_timing = NULL;523	struct dc_crtc_timing *vblank_timing = NULL;524	struct dc_stream_state *phantom_stream;525	enum mall_stream_type pipe_mall_type;526 527	/* For SubVP + VBLANK/DRR cases, we assume there can only be528	 * a single VBLANK/DRR display. If DML outputs SubVP + VBLANK529	 * is supported, it is either a single VBLANK case or two VBLANK530	 * displays which are synchronized (in which case they have identical531	 * timings).532	 */533	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {534		pipe = &context->res_ctx.pipe_ctx[i];535		pipe_mall_type = ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe);536 537		// We check for master pipe, but it shouldn't matter since we only need538		// the pipe for timing info (stream should be same for any pipe splits)539		if (!pipe->stream || !pipe->plane_state || pipe->top_pipe || pipe->prev_odm_pipe)540			continue;541 542		if (!found && pipe_mall_type == SUBVP_NONE) {543			// Found pipe which is not SubVP or Phantom (i.e. the VBLANK pipe).544			vblank_index = i;545			found = true;546		}547 548		if (!subvp_pipe && pipe_mall_type == SUBVP_MAIN)549			subvp_pipe = pipe;550	}551	// Use ignore_msa_timing_param flag to identify as DRR552	if (found && context->res_ctx.pipe_ctx[vblank_index].stream->ignore_msa_timing_param) {553		// SUBVP + DRR case554		schedulable = dml2_svp_drr_schedulable(ctx, context, &context->res_ctx.pipe_ctx[vblank_index].stream->timing);555	} else if (found) {556		phantom_stream = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(context, subvp_pipe->stream);557		main_timing = &subvp_pipe->stream->timing;558		phantom_timing = &phantom_stream->timing;559		vblank_timing = &context->res_ctx.pipe_ctx[vblank_index].stream->timing;560		// Prefetch time is equal to VACTIVE + BP + VSYNC of the phantom pipe561		// Also include the prefetch end to mallstart delay time562		prefetch_us = (phantom_timing->v_total - phantom_timing->v_front_porch) * phantom_timing->h_total /563				(double)(phantom_timing->pix_clk_100hz * 100) * 1000000 +564				ctx->config.svp_pstate.subvp_prefetch_end_to_mall_start_us;565		// P-State allow width and FW delays already included phantom_timing->v_addressable566		mall_region_us = phantom_timing->v_addressable * phantom_timing->h_total /567				(double)(phantom_timing->pix_clk_100hz * 100) * 1000000;568		vblank_frame_us = vblank_timing->v_total * vblank_timing->h_total /569				(double)(vblank_timing->pix_clk_100hz * 100) * 1000000;570		vblank_blank_us =  (vblank_timing->v_total - vblank_timing->v_addressable) * vblank_timing->h_total /571				(double)(vblank_timing->pix_clk_100hz * 100) * 1000000;572		subvp_active_us = main_timing->v_addressable * main_timing->h_total /573				(double)(main_timing->pix_clk_100hz * 100) * 1000000;574		max_vblank_mallregion = vblank_blank_us > mall_region_us ? vblank_blank_us : mall_region_us;575 576		// Schedulable if VACTIVE region of the SubVP pipe can fit the MALL prefetch, VBLANK frame time,577		// and the max of (VBLANK blanking time, MALL region)578		// TODO: Possibly add some margin (i.e. the below conditions should be [...] > X instead of [...] > 0)579		if (subvp_active_us - prefetch_us - vblank_frame_us - max_vblank_mallregion > 0)580			schedulable = true;581	}582	return schedulable;583}584 585/*586 * subvp_validate_static_schedulability: Check which SubVP case is calculated and handle587 * static analysis based on the case.588 *589 * Three cases:590 * 1. SubVP + SubVP591 * 2. SubVP + VBLANK (DRR checked internally)592 * 3. SubVP + VACTIVE (currently unsupported)593 *594 * @dc: current dc state595 * @context: new dc state596 * @vlevel: Voltage level calculated by DML597 *598 * Return:599 * bool - True if statically schedulable, false otherwise600 */601bool dml2_svp_validate_static_schedulability(struct dml2_context *ctx, struct dc_state *context, enum dml_dram_clock_change_support pstate_change_type)602{603	bool schedulable = true;	// true by default for single display case604	struct vba_vars_st *vba = &context->bw_ctx.dml.vba;605	uint32_t i, pipe_idx;606	uint8_t subvp_count = 0;607	uint8_t vactive_count = 0;608 609	for (i = 0, pipe_idx = 0; i < ctx->config.dcn_pipe_count; i++) {610		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];611		enum mall_stream_type pipe_mall_type = ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe);612 613		if (!pipe->stream)614			continue;615 616		if (pipe->plane_state && !pipe->top_pipe &&617				pipe_mall_type == SUBVP_MAIN)618			subvp_count++;619 620		// Count how many planes that aren't SubVP/phantom are capable of VACTIVE621		// switching (SubVP + VACTIVE unsupported). In situations where we force622		// SubVP for a VACTIVE plane, we don't want to increment the vactive_count.623		if (vba->ActiveDRAMClockChangeLatencyMargin[vba->pipe_plane[pipe_idx]] > 0 &&624		    pipe_mall_type == SUBVP_NONE) {625			vactive_count++;626		}627		pipe_idx++;628	}629 630	if (subvp_count == 2) {631		// Static schedulability check for SubVP + SubVP case632		schedulable = subvp_subvp_schedulable(ctx, context);633	} else if (pstate_change_type == dml_dram_clock_change_vblank_w_mall_sub_vp) {634		// Static schedulability check for SubVP + VBLANK case. Also handle the case where635		// DML outputs SubVP + VBLANK + VACTIVE (DML will report as SubVP + VBLANK)636		if (vactive_count > 0)637			schedulable = false;638		else639			schedulable = subvp_vblank_schedulable(ctx, context);640	} else if (pstate_change_type == dml_dram_clock_change_vactive_w_mall_sub_vp &&641			vactive_count > 0) {642		// For single display SubVP cases, DML will output dm_dram_clock_change_vactive_w_mall_sub_vp by default.643		// We tell the difference between SubVP vs. SubVP + VACTIVE by checking the vactive_count.644		// SubVP + VACTIVE currently unsupported645		schedulable = false;646	}647	return schedulable;648}649 650static void set_phantom_stream_timing(struct dml2_context *ctx, struct dc_state *state,651				     struct pipe_ctx *ref_pipe,652				     struct dc_stream_state *phantom_stream,653				     unsigned int dc_pipe_idx,654				     unsigned int svp_height,655				     unsigned int svp_vstartup)656{657	unsigned int i, pipe_idx;658	double line_time, fp_and_sync_width_time;659	struct pipe_ctx *pipe;660	uint32_t phantom_vactive, phantom_bp, pstate_width_fw_delay_lines;661	static const double cvt_rb_vblank_max = ((double) 460 / (1000 * 1000));662 663	// Find DML pipe index (pipe_idx) using dc_pipe_idx664	for (i = 0, pipe_idx = 0; i < ctx->config.dcn_pipe_count; i++) {665		pipe = &state->res_ctx.pipe_ctx[i];666 667		if (!pipe->stream)668			continue;669 670		if (i == dc_pipe_idx)671			break;672 673		pipe_idx++;674	}675 676	// Calculate lines required for pstate allow width and FW processing delays677	pstate_width_fw_delay_lines = ((double)(ctx->config.svp_pstate.subvp_fw_processing_delay_us +678			ctx->config.svp_pstate.subvp_pstate_allow_width_us) / 1000000) *679			(ref_pipe->stream->timing.pix_clk_100hz * 100) /680			(double)ref_pipe->stream->timing.h_total;681 682	// DML calculation for MALL region doesn't take into account FW delay683	// and required pstate allow width for multi-display cases684	/* Add 16 lines margin to the MALL REGION because SUB_VP_START_LINE must be aligned685	 * to 2 swaths (i.e. 16 lines)686	 */687	phantom_vactive = svp_height + pstate_width_fw_delay_lines + ctx->config.svp_pstate.subvp_swath_height_margin_lines;688 689	phantom_stream->timing.v_front_porch = 1;690 691	line_time = phantom_stream->timing.h_total / ((double)phantom_stream->timing.pix_clk_100hz * 100);692	fp_and_sync_width_time = (phantom_stream->timing.v_front_porch + phantom_stream->timing.v_sync_width) * line_time;693 694	if ((svp_vstartup * line_time) + fp_and_sync_width_time > cvt_rb_vblank_max) {695		svp_vstartup = (cvt_rb_vblank_max - fp_and_sync_width_time) / line_time;696	}697 698	// For backporch of phantom pipe, use vstartup of the main pipe699	phantom_bp = svp_vstartup;700 701	phantom_stream->dst.y = 0;702	phantom_stream->dst.height = phantom_vactive;703	phantom_stream->src.y = 0;704	phantom_stream->src.height = phantom_vactive;705 706	phantom_stream->timing.v_addressable = phantom_vactive;707 708	phantom_stream->timing.v_total = phantom_stream->timing.v_addressable +709						phantom_stream->timing.v_front_porch +710						phantom_stream->timing.v_sync_width +711						phantom_bp;712	phantom_stream->timing.flags.DSC = 0; // Don't need DSC for phantom timing713}714 715static struct dc_stream_state *enable_phantom_stream(struct dml2_context *ctx, struct dc_state *state, unsigned int dc_pipe_idx, unsigned int svp_height, unsigned int vstartup)716{717	struct pipe_ctx *ref_pipe = &state->res_ctx.pipe_ctx[dc_pipe_idx];718	struct dc_stream_state *phantom_stream = ctx->config.svp_pstate.callbacks.create_phantom_stream(719			ctx->config.svp_pstate.callbacks.dc,720			state,721			ref_pipe->stream);722 723	/* stream has limited viewport and small timing */724	memcpy(&phantom_stream->timing, &ref_pipe->stream->timing, sizeof(phantom_stream->timing));725	memcpy(&phantom_stream->src, &ref_pipe->stream->src, sizeof(phantom_stream->src));726	memcpy(&phantom_stream->dst, &ref_pipe->stream->dst, sizeof(phantom_stream->dst));727	set_phantom_stream_timing(ctx, state, ref_pipe, phantom_stream, dc_pipe_idx, svp_height, vstartup);728 729	ctx->config.svp_pstate.callbacks.add_phantom_stream(ctx->config.svp_pstate.callbacks.dc,730			state,731			phantom_stream,732			ref_pipe->stream);733	return phantom_stream;734}735 736static void enable_phantom_plane(struct dml2_context *ctx,737		struct dc_state *state,738		struct dc_stream_state *phantom_stream,739		unsigned int dc_pipe_idx)740{741	struct dc_plane_state *phantom_plane = NULL;742	struct dc_plane_state *prev_phantom_plane = NULL;743	struct pipe_ctx *curr_pipe = &state->res_ctx.pipe_ctx[dc_pipe_idx];744 745	while (curr_pipe) {746		if (curr_pipe->top_pipe && curr_pipe->top_pipe->plane_state == curr_pipe->plane_state) {747			phantom_plane = prev_phantom_plane;748		} else {749			phantom_plane = ctx->config.svp_pstate.callbacks.create_phantom_plane(750					ctx->config.svp_pstate.callbacks.dc,751					state,752					curr_pipe->plane_state);753			if (!phantom_plane)754				return;755		}756 757		memcpy(&phantom_plane->address, &curr_pipe->plane_state->address, sizeof(phantom_plane->address));758		memcpy(&phantom_plane->scaling_quality, &curr_pipe->plane_state->scaling_quality,759				sizeof(phantom_plane->scaling_quality));760		memcpy(&phantom_plane->src_rect, &curr_pipe->plane_state->src_rect, sizeof(phantom_plane->src_rect));761		memcpy(&phantom_plane->dst_rect, &curr_pipe->plane_state->dst_rect, sizeof(phantom_plane->dst_rect));762		memcpy(&phantom_plane->clip_rect, &curr_pipe->plane_state->clip_rect, sizeof(phantom_plane->clip_rect));763		memcpy(&phantom_plane->plane_size, &curr_pipe->plane_state->plane_size,764				sizeof(phantom_plane->plane_size));765		memcpy(&phantom_plane->tiling_info, &curr_pipe->plane_state->tiling_info,766				sizeof(phantom_plane->tiling_info));767		memcpy(&phantom_plane->dcc, &curr_pipe->plane_state->dcc, sizeof(phantom_plane->dcc));768		//phantom_plane->tiling_info.gfx10compatible.compat_level = curr_pipe->plane_state->tiling_info.gfx10compatible.compat_level;769		phantom_plane->format = curr_pipe->plane_state->format;770		phantom_plane->rotation = curr_pipe->plane_state->rotation;771		phantom_plane->visible = curr_pipe->plane_state->visible;772 773		/* Shadow pipe has small viewport. */774		phantom_plane->clip_rect.y = 0;775		phantom_plane->clip_rect.height = phantom_stream->timing.v_addressable;776 777		ctx->config.svp_pstate.callbacks.add_phantom_plane(ctx->config.svp_pstate.callbacks.dc, phantom_stream, phantom_plane, state);778 779		curr_pipe = curr_pipe->bottom_pipe;780		prev_phantom_plane = phantom_plane;781	}782}783 784static void add_phantom_pipes_for_main_pipe(struct dml2_context *ctx, struct dc_state *state, unsigned int main_pipe_idx, unsigned int svp_height, unsigned int vstartup)785{786	struct dc_stream_state *phantom_stream = NULL;787	unsigned int i;788 789	// The index of the DC pipe passed into this function is guarenteed to790	// be a valid candidate for SubVP (i.e. has a plane, stream, doesn't791	// already have phantom pipe assigned, etc.) by previous checks.792	phantom_stream = enable_phantom_stream(ctx, state, main_pipe_idx, svp_height, vstartup);793	enable_phantom_plane(ctx, state, phantom_stream, main_pipe_idx);794 795	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {796		struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];797 798		// Build scaling params for phantom pipes which were newly added.799		// We determine which phantom pipes were added by comparing with800		// the phantom stream.801		if (pipe->plane_state && pipe->stream && pipe->stream == phantom_stream &&802				ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(state, pipe) == SUBVP_PHANTOM) {803			pipe->stream->use_dynamic_meta = false;804			pipe->plane_state->flip_immediate = false;805			if (!ctx->config.svp_pstate.callbacks.build_scaling_params(pipe)) {806				// Log / remove phantom pipes since failed to build scaling params807			}808		}809	}810}811 812static bool remove_all_phantom_planes_for_stream(struct dml2_context *ctx, struct dc_stream_state *stream, struct dc_state *context)813{814	int i, old_plane_count;815	struct dc_stream_status *stream_status = NULL;816	struct dc_plane_state *del_planes[MAX_SURFACE_NUM] = { 0 };817 818	for (i = 0; i < context->stream_count; i++)819			if (context->streams[i] == stream) {820				stream_status = &context->stream_status[i];821				break;822			}823 824	if (stream_status == NULL) {825		return false;826	}827 828	old_plane_count = stream_status->plane_count;829 830	for (i = 0; i < old_plane_count; i++)831		del_planes[i] = stream_status->plane_states[i];832 833	for (i = 0; i < old_plane_count; i++) {834		if (!ctx->config.svp_pstate.callbacks.remove_phantom_plane(ctx->config.svp_pstate.callbacks.dc, stream, del_planes[i], context))835			return false;836		ctx->config.svp_pstate.callbacks.release_phantom_plane(ctx->config.svp_pstate.callbacks.dc, context, del_planes[i]);837	}838 839	return true;840}841 842bool dml2_svp_remove_all_phantom_pipes(struct dml2_context *ctx, struct dc_state *state)843{844	int i;845	bool removed_pipe = false;846	struct dc_stream_state *phantom_stream = NULL;847 848	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {849		struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];850		// build scaling params for phantom pipes851		if (pipe->plane_state && pipe->stream && ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(state, pipe) == SUBVP_PHANTOM) {852			phantom_stream = pipe->stream;853 854			remove_all_phantom_planes_for_stream(ctx, phantom_stream, state);855			ctx->config.svp_pstate.callbacks.remove_phantom_stream(ctx->config.svp_pstate.callbacks.dc, state, phantom_stream);856			ctx->config.svp_pstate.callbacks.release_phantom_stream(ctx->config.svp_pstate.callbacks.dc, state, phantom_stream);857 858			removed_pipe = true;859		}860 861		if (pipe->plane_state) {862			pipe->plane_state->is_phantom = false;863		}864	}865	return removed_pipe;866}867 868 869/* Conditions for setting up phantom pipes for SubVP:870 * 1. Not force disable SubVP871 * 2. Full update (i.e. !fast_validate)872 * 3. Enough pipes are available to support SubVP (TODO: Which pipes will use VACTIVE / VBLANK / SUBVP?)873 * 4. Display configuration passes validation874 * 5. (Config doesn't support MCLK in VACTIVE/VBLANK || dc->debug.force_subvp_mclk_switch)875 */876bool dml2_svp_add_phantom_pipe_to_dc_state(struct dml2_context *ctx, struct dc_state *state, struct dml_mode_support_info_st *mode_support_info)877{878	unsigned int dc_pipe_idx, dml_pipe_idx;879	unsigned int svp_height, vstartup;880 881	if (ctx->config.svp_pstate.force_disable_subvp)882		return false;883 884	if (!all_pipes_have_stream_and_plane(ctx, state))885		return false;886 887	if (mpo_in_use(state))888		return false;889 890	merge_pipes_for_subvp(ctx, state);891	// to re-initialize viewport after the pipe merge892	for (int i = 0; i < ctx->config.dcn_pipe_count; i++) {893		struct pipe_ctx *pipe_ctx = &state->res_ctx.pipe_ctx[i];894 895		if (!pipe_ctx->plane_state || !pipe_ctx->stream)896			continue;897 898		ctx->config.svp_pstate.callbacks.build_scaling_params(pipe_ctx);899	}900 901	if (enough_pipes_for_subvp(ctx, state) && assign_subvp_pipe(ctx, state, &dc_pipe_idx)) {902		dml_pipe_idx = dml2_helper_find_dml_pipe_idx_by_stream_id(ctx, state->res_ctx.pipe_ctx[dc_pipe_idx].stream->stream_id);903		svp_height = mode_support_info->SubViewportLinesNeededInMALL[dml_pipe_idx];904		vstartup = dml_get_vstartup_calculated(&ctx->v20.dml_core_ctx, dml_pipe_idx);905 906		add_phantom_pipes_for_main_pipe(ctx, state, dc_pipe_idx, svp_height, vstartup);907 908		return true;909	}910 911	return false;912}913