brintos

brintos / linux-shallow public Read only

0
0
Text · 60.2 KiB · 0b74366 Raw
1773 lines · c
1// SPDX-License-Identifier: MIT2//3// Copyright 2024 Advanced Micro Devices, Inc.4 5#include "dm_services.h"6#include "dm_helpers.h"7#include "core_types.h"8#include "resource.h"9#include "dccg.h"10#include "dce/dce_hwseq.h"11#include "reg_helper.h"12#include "abm.h"13#include "hubp.h"14#include "dchubbub.h"15#include "timing_generator.h"16#include "opp.h"17#include "ipp.h"18#include "mpc.h"19#include "mcif_wb.h"20#include "dc_dmub_srv.h"21#include "link_hwss.h"22#include "dpcd_defs.h"23#include "clk_mgr.h"24#include "dsc.h"25#include "link.h"26 27#include "dce/dmub_hw_lock_mgr.h"28#include "dcn10/dcn10_cm_common.h"29#include "dcn20/dcn20_optc.h"30#include "dcn30/dcn30_cm_common.h"31#include "dcn32/dcn32_hwseq.h"32#include "dcn401_hwseq.h"33#include "dcn401/dcn401_resource.h"34#include "dc_state_priv.h"35#include "link_enc_cfg.h"36 37#define DC_LOGGER_INIT(logger)38 39#define CTX \40	hws->ctx41#define REG(reg)\42	hws->regs->reg43#define DC_LOGGER \44	dc->ctx->logger45 46 47#undef FN48#define FN(reg_name, field_name) \49	hws->shifts->field_name, hws->masks->field_name50 51static void dcn401_initialize_min_clocks(struct dc *dc)52{53	struct dc_clocks *clocks = &dc->current_state->bw_ctx.bw.dcn.clk;54 55	clocks->dcfclk_deep_sleep_khz = DCN3_2_DCFCLK_DS_INIT_KHZ;56	clocks->dcfclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].dcfclk_mhz * 1000;57	clocks->socclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].socclk_mhz * 1000;58	clocks->dramclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].memclk_mhz * 1000;59	clocks->dppclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].dppclk_mhz * 1000;60	if (dc->debug.disable_boot_optimizations) {61		clocks->dispclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].dispclk_mhz * 1000;62	} else {63		/* Even though DPG_EN = 1 for the connected display, it still requires the64		 * correct timing so we cannot set DISPCLK to min freq or it could cause65		 * audio corruption. Read current DISPCLK from DENTIST and request the same66		 * freq to ensure that the timing is valid and unchanged.67		 */68		clocks->dispclk_khz = dc->clk_mgr->funcs->get_dispclk_from_dentist(dc->clk_mgr);69	}70	clocks->ref_dtbclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].dtbclk_mhz * 1000;71	clocks->fclk_p_state_change_support = true;72	clocks->p_state_change_support = true;73 74	dc->clk_mgr->funcs->update_clocks(75			dc->clk_mgr,76			dc->current_state,77			true);78}79 80void dcn401_program_gamut_remap(struct pipe_ctx *pipe_ctx)81{82	unsigned int i = 0;83	struct mpc_grph_gamut_adjustment mpc_adjust;84	unsigned int mpcc_id = pipe_ctx->plane_res.mpcc_inst;85	struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc;86 87	//For now assert if location is not pre-blend88	if (pipe_ctx->plane_state)89		ASSERT(pipe_ctx->plane_state->mcm_location == MPCC_MOVABLE_CM_LOCATION_BEFORE);90 91	// program MPCC_MCM_FIRST_GAMUT_REMAP92	memset(&mpc_adjust, 0, sizeof(mpc_adjust));93	mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS;94	mpc_adjust.mpcc_gamut_remap_block_id = MPCC_MCM_FIRST_GAMUT_REMAP;95 96	if (pipe_ctx->plane_state &&97		pipe_ctx->plane_state->gamut_remap_matrix.enable_remap == true) {98		mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW;99		for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++)100			mpc_adjust.temperature_matrix[i] =101			pipe_ctx->plane_state->gamut_remap_matrix.matrix[i];102	}103 104	mpc->funcs->set_gamut_remap(mpc, mpcc_id, &mpc_adjust);105 106	// program MPCC_MCM_SECOND_GAMUT_REMAP for Bypass / Disable for now107	mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS;108	mpc_adjust.mpcc_gamut_remap_block_id = MPCC_MCM_SECOND_GAMUT_REMAP;109 110	mpc->funcs->set_gamut_remap(mpc, mpcc_id, &mpc_adjust);111 112	// program MPCC_OGAM_GAMUT_REMAP same as is currently used on DCN3x113	memset(&mpc_adjust, 0, sizeof(mpc_adjust));114	mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS;115	mpc_adjust.mpcc_gamut_remap_block_id = MPCC_OGAM_GAMUT_REMAP;116 117	if (pipe_ctx->top_pipe == NULL) {118		if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) {119			mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW;120			for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++)121				mpc_adjust.temperature_matrix[i] =122				pipe_ctx->stream->gamut_remap_matrix.matrix[i];123		}124	}125 126	mpc->funcs->set_gamut_remap(mpc, mpcc_id, &mpc_adjust);127}128 129struct ips_ono_region_state dcn401_read_ono_state(struct dc *dc, uint8_t region)130{131	struct dce_hwseq *hws = dc->hwseq;132	struct ips_ono_region_state state = {0, 0};133 134	switch (region) {135	case 0:136		/* dccg, dio, dcio */137		REG_GET_2(DOMAIN22_PG_STATUS,138			DOMAIN_DESIRED_PWR_STATE, &state.desire_pwr_state,139			DOMAIN_PGFSM_PWR_STATUS, &state.current_pwr_state);140		break;141	case 1:142		/* dchubbub, dchvm, dchubbubmem */143		REG_GET_2(DOMAIN23_PG_STATUS,144			DOMAIN_DESIRED_PWR_STATE, &state.desire_pwr_state,145			DOMAIN_PGFSM_PWR_STATUS, &state.current_pwr_state);146		break;147	case 2:148		/* mpc, opp, optc, dwb */149		REG_GET_2(DOMAIN24_PG_STATUS,150			DOMAIN_DESIRED_PWR_STATE, &state.desire_pwr_state,151			DOMAIN_PGFSM_PWR_STATUS, &state.current_pwr_state);152		break;153	case 3:154		/* hpo */155		REG_GET_2(DOMAIN25_PG_STATUS,156			DOMAIN_DESIRED_PWR_STATE, &state.desire_pwr_state,157			DOMAIN_PGFSM_PWR_STATUS, &state.current_pwr_state);158		break;159	case 4:160		/* dchubp0, dpp0 */161		REG_GET_2(DOMAIN0_PG_STATUS,162			DOMAIN_DESIRED_PWR_STATE, &state.desire_pwr_state,163			DOMAIN_PGFSM_PWR_STATUS, &state.current_pwr_state);164		break;165	case 5:166		/* dsc0 */167		REG_GET_2(DOMAIN16_PG_STATUS,168			DOMAIN_DESIRED_PWR_STATE, &state.desire_pwr_state,169			DOMAIN_PGFSM_PWR_STATUS, &state.current_pwr_state);170		break;171	case 6:172		/* dchubp1, dpp1 */173		REG_GET_2(DOMAIN1_PG_STATUS,174			DOMAIN_DESIRED_PWR_STATE, &state.desire_pwr_state,175			DOMAIN_PGFSM_PWR_STATUS, &state.current_pwr_state);176		break;177	case 7:178		/* dsc1 */179		REG_GET_2(DOMAIN17_PG_STATUS,180			DOMAIN_DESIRED_PWR_STATE, &state.desire_pwr_state,181			DOMAIN_PGFSM_PWR_STATUS, &state.current_pwr_state);182		break;183	case 8:184		/* dchubp2, dpp2 */185		REG_GET_2(DOMAIN2_PG_STATUS,186			DOMAIN_DESIRED_PWR_STATE, &state.desire_pwr_state,187			DOMAIN_PGFSM_PWR_STATUS, &state.current_pwr_state);188		break;189	case 9:190		/* dsc2 */191		REG_GET_2(DOMAIN18_PG_STATUS,192			DOMAIN_DESIRED_PWR_STATE, &state.desire_pwr_state,193			DOMAIN_PGFSM_PWR_STATUS, &state.current_pwr_state);194		break;195	case 10:196		/* dchubp3, dpp3 */197		REG_GET_2(DOMAIN3_PG_STATUS,198			DOMAIN_DESIRED_PWR_STATE, &state.desire_pwr_state,199			DOMAIN_PGFSM_PWR_STATUS, &state.current_pwr_state);200		break;201	case 11:202		/* dsc3 */203		REG_GET_2(DOMAIN19_PG_STATUS,204			DOMAIN_DESIRED_PWR_STATE, &state.desire_pwr_state,205			DOMAIN_PGFSM_PWR_STATUS, &state.current_pwr_state);206		break;207	default:208		break;209	}210 211	return state;212}213 214void dcn401_init_hw(struct dc *dc)215{216	struct abm **abms = dc->res_pool->multiple_abms;217	struct dce_hwseq *hws = dc->hwseq;218	struct dc_bios *dcb = dc->ctx->dc_bios;219	struct resource_pool *res_pool = dc->res_pool;220	int i;221	int edp_num;222	uint32_t backlight = MAX_BACKLIGHT_LEVEL;223	uint32_t user_level = MAX_BACKLIGHT_LEVEL;224	int current_dchub_ref_freq = 0;225 226	if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->init_clocks) {227		dc->clk_mgr->funcs->init_clocks(dc->clk_mgr);228 229		// mark dcmode limits present if any clock has distinct AC and DC values from SMU230		dc->caps.dcmode_power_limits_present =231				(dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dcfclk_levels && dc->clk_mgr->bw_params->dc_mode_limit.dcfclk_mhz) ||232				(dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dispclk_levels && dc->clk_mgr->bw_params->dc_mode_limit.dispclk_mhz) ||233				(dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dtbclk_levels && dc->clk_mgr->bw_params->dc_mode_limit.dtbclk_mhz) ||234				(dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_fclk_levels && dc->clk_mgr->bw_params->dc_mode_limit.fclk_mhz) ||235				(dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_memclk_levels && dc->clk_mgr->bw_params->dc_mode_limit.memclk_mhz) ||236				(dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_socclk_levels && dc->clk_mgr->bw_params->dc_mode_limit.socclk_mhz);237	}238 239	// Initialize the dccg240	if (res_pool->dccg->funcs->dccg_init)241		res_pool->dccg->funcs->dccg_init(res_pool->dccg);242 243	// Disable DMUB Initialization until IPS state programming is finalized244	//if (!dcb->funcs->is_accelerated_mode(dcb)) {245	//	hws->funcs.bios_golden_init(dc);246	//}247 248	// Set default OPTC memory power states249	if (dc->debug.enable_mem_low_power.bits.optc) {250		// Shutdown when unassigned and light sleep in VBLANK251		REG_SET_2(ODM_MEM_PWR_CTRL3, 0, ODM_MEM_UNASSIGNED_PWR_MODE, 3, ODM_MEM_VBLANK_PWR_MODE, 1);252	}253 254	if (dc->debug.enable_mem_low_power.bits.vga) {255		// Power down VGA memory256		REG_UPDATE(MMHUBBUB_MEM_PWR_CNTL, VGA_MEM_PWR_FORCE, 1);257	}258 259	if (dc->ctx->dc_bios->fw_info_valid) {260		res_pool->ref_clocks.xtalin_clock_inKhz =261				dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency;262 263		if (res_pool->hubbub) {264			(res_pool->dccg->funcs->get_dccg_ref_freq)(res_pool->dccg,265					dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency,266					&res_pool->ref_clocks.dccg_ref_clock_inKhz);267 268			current_dchub_ref_freq = res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000;269 270			(res_pool->hubbub->funcs->get_dchub_ref_freq)(res_pool->hubbub,271					res_pool->ref_clocks.dccg_ref_clock_inKhz,272					&res_pool->ref_clocks.dchub_ref_clock_inKhz);273		} else {274			// Not all ASICs have DCCG sw component275			res_pool->ref_clocks.dccg_ref_clock_inKhz =276					res_pool->ref_clocks.xtalin_clock_inKhz;277			res_pool->ref_clocks.dchub_ref_clock_inKhz =278					res_pool->ref_clocks.xtalin_clock_inKhz;279		}280	} else281		ASSERT_CRITICAL(false);282 283	for (i = 0; i < dc->link_count; i++) {284		/* Power up AND update implementation according to the285		 * required signal (which may be different from the286		 * default signal on connector).287		 */288		struct dc_link *link = dc->links[i];289 290		link->link_enc->funcs->hw_init(link->link_enc);291 292		/* Check for enabled DIG to identify enabled display */293		if (link->link_enc->funcs->is_dig_enabled &&294			link->link_enc->funcs->is_dig_enabled(link->link_enc)) {295			link->link_status.link_active = true;296			link->phy_state.symclk_state = SYMCLK_ON_TX_ON;297			if (link->link_enc->funcs->fec_is_active &&298					link->link_enc->funcs->fec_is_active(link->link_enc))299				link->fec_state = dc_link_fec_enabled;300		}301	}302 303	/* enable_power_gating_plane before dsc_pg_control because304	 * FORCEON = 1 with hw default value on bootup, resume from s3305	 */306	if (hws->funcs.enable_power_gating_plane)307		hws->funcs.enable_power_gating_plane(dc->hwseq, true);308 309	/* we want to turn off all dp displays before doing detection */310	dc->link_srv->blank_all_dp_displays(dc);311 312	/* If taking control over from VBIOS, we may want to optimize our first313	 * mode set, so we need to skip powering down pipes until we know which314	 * pipes we want to use.315	 * Otherwise, if taking control is not possible, we need to power316	 * everything down.317	 */318	if (dcb->funcs->is_accelerated_mode(dcb) || !dc->config.seamless_boot_edp_requested) {319		/* Disable boot optimizations means power down everything including PHY, DIG,320		 * and OTG (i.e. the boot is not optimized because we do a full power down).321		 */322		if (dc->hwss.enable_accelerated_mode && dc->debug.disable_boot_optimizations)323			dc->hwss.enable_accelerated_mode(dc, dc->current_state);324		else325			hws->funcs.init_pipes(dc, dc->current_state);326 327		if (dc->res_pool->hubbub->funcs->allow_self_refresh_control)328			dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub,329					!dc->res_pool->hubbub->ctx->dc->debug.disable_stutter);330 331		dcn401_initialize_min_clocks(dc);332 333		/* On HW init, allow idle optimizations after pipes have been turned off.334		 *335		 * In certain D3 cases (i.e. BOCO / BOMACO) it's possible that hardware state336		 * is reset (i.e. not in idle at the time hw init is called), but software state337		 * still has idle_optimizations = true, so we must disable idle optimizations first338		 * (i.e. set false), then re-enable (set true).339		 */340		dc_allow_idle_optimizations(dc, false);341		dc_allow_idle_optimizations(dc, true);342	}343 344	/* In headless boot cases, DIG may be turned345	 * on which causes HW/SW discrepancies.346	 * To avoid this, power down hardware on boot347	 * if DIG is turned on and seamless boot not enabled348	 */349	if (!dc->config.seamless_boot_edp_requested) {350		struct dc_link *edp_links[MAX_NUM_EDP];351		struct dc_link *edp_link;352 353		dc_get_edp_links(dc, edp_links, &edp_num);354		if (edp_num) {355			for (i = 0; i < edp_num; i++) {356				edp_link = edp_links[i];357				if (edp_link->link_enc->funcs->is_dig_enabled &&358						edp_link->link_enc->funcs->is_dig_enabled(edp_link->link_enc) &&359						dc->hwss.edp_backlight_control &&360						hws->funcs.power_down &&361						dc->hwss.edp_power_control) {362					dc->hwss.edp_backlight_control(edp_link, false);363					hws->funcs.power_down(dc);364					dc->hwss.edp_power_control(edp_link, false);365				}366			}367		} else {368			for (i = 0; i < dc->link_count; i++) {369				struct dc_link *link = dc->links[i];370 371				if (link->link_enc->funcs->is_dig_enabled &&372						link->link_enc->funcs->is_dig_enabled(link->link_enc) &&373						hws->funcs.power_down) {374					hws->funcs.power_down(dc);375					break;376				}377 378			}379		}380	}381 382	for (i = 0; i < res_pool->audio_count; i++) {383		struct audio *audio = res_pool->audios[i];384 385		audio->funcs->hw_init(audio);386	}387 388	for (i = 0; i < dc->link_count; i++) {389		struct dc_link *link = dc->links[i];390 391		if (link->panel_cntl) {392			backlight = link->panel_cntl->funcs->hw_init(link->panel_cntl);393			user_level = link->panel_cntl->stored_backlight_registers.USER_LEVEL;394		}395	}396 397	for (i = 0; i < dc->res_pool->pipe_count; i++) {398		if (abms[i] != NULL && abms[i]->funcs != NULL)399			abms[i]->funcs->abm_init(abms[i], backlight, user_level);400	}401 402	/* power AFMT HDMI memory TODO: may move to dis/en output save power*/403	REG_WRITE(DIO_MEM_PWR_CTRL, 0);404 405	if (!dc->debug.disable_clock_gate) {406		/* enable all DCN clock gating */407		REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0);408 409		REG_WRITE(DCCG_GATE_DISABLE_CNTL2, 0);410 411		REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0);412	}413 414	dcn401_setup_hpo_hw_control(hws, true);415 416	if (!dcb->funcs->is_accelerated_mode(dcb) && dc->res_pool->hubbub->funcs->init_watermarks)417		dc->res_pool->hubbub->funcs->init_watermarks(dc->res_pool->hubbub);418 419	if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->notify_wm_ranges)420		dc->clk_mgr->funcs->notify_wm_ranges(dc->clk_mgr);421 422	if (dc->res_pool->hubbub->funcs->force_pstate_change_control)423		dc->res_pool->hubbub->funcs->force_pstate_change_control(424				dc->res_pool->hubbub, false, false);425 426	if (dc->res_pool->hubbub->funcs->init_crb)427		dc->res_pool->hubbub->funcs->init_crb(dc->res_pool->hubbub);428 429	if (dc->res_pool->hubbub->funcs->set_request_limit && dc->config.sdpif_request_limit_words_per_umc > 0)430		dc->res_pool->hubbub->funcs->set_request_limit(dc->res_pool->hubbub, dc->ctx->dc_bios->vram_info.num_chans, dc->config.sdpif_request_limit_words_per_umc);431 432	// Get DMCUB capabilities433	if (dc->ctx->dmub_srv) {434		dc_dmub_srv_query_caps_cmd(dc->ctx->dmub_srv);435		dc->caps.dmub_caps.psr = dc->ctx->dmub_srv->dmub->feature_caps.psr;436		dc->caps.dmub_caps.mclk_sw = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver > 0;437		dc->caps.dmub_caps.fams_ver = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver;438		dc->debug.fams2_config.bits.enable &= dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver == 2;439		if ((!dc->debug.fams2_config.bits.enable && dc->res_pool->funcs->update_bw_bounding_box)440			|| res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000 != current_dchub_ref_freq) {441			/* update bounding box if FAMS2 disabled, or if dchub clk has changed */442			if (dc->clk_mgr)443				dc->res_pool->funcs->update_bw_bounding_box(dc,444									    dc->clk_mgr->bw_params);445		}446	}447}448 449static void dcn401_get_mcm_lut_xable_from_pipe_ctx(struct dc *dc, struct pipe_ctx *pipe_ctx,450		enum MCM_LUT_XABLE *shaper_xable,451		enum MCM_LUT_XABLE *lut3d_xable,452		enum MCM_LUT_XABLE *lut1d_xable)453{454	enum dc_cm2_shaper_3dlut_setting shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL;455	bool lut1d_enable = false;456	struct mpc *mpc = dc->res_pool->mpc;457	int mpcc_id = pipe_ctx->plane_res.hubp->inst;458 459	if (!pipe_ctx->plane_state)460		return;461	shaper_3dlut_setting = pipe_ctx->plane_state->mcm_shaper_3dlut_setting;462	lut1d_enable = pipe_ctx->plane_state->mcm_lut1d_enable;463	mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id);464	pipe_ctx->plane_state->mcm_location = MPCC_MOVABLE_CM_LOCATION_BEFORE;465 466	*lut1d_xable = lut1d_enable ? MCM_LUT_ENABLE : MCM_LUT_DISABLE;467 468	switch (shaper_3dlut_setting) {469	case DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL:470		*lut3d_xable = *shaper_xable = MCM_LUT_DISABLE;471		break;472	case DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER:473		*lut3d_xable = MCM_LUT_DISABLE;474		*shaper_xable = MCM_LUT_ENABLE;475		break;476	case DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT:477		*lut3d_xable = *shaper_xable = MCM_LUT_ENABLE;478		break;479	}480}481 482void dcn401_populate_mcm_luts(struct dc *dc,483		struct pipe_ctx *pipe_ctx,484		struct dc_cm2_func_luts mcm_luts,485		bool lut_bank_a)486{487	struct dpp *dpp_base = pipe_ctx->plane_res.dpp;488	struct hubp *hubp = pipe_ctx->plane_res.hubp;489	int mpcc_id = hubp->inst;490	struct mpc *mpc = dc->res_pool->mpc;491	union mcm_lut_params m_lut_params;492	enum dc_cm2_transfer_func_source lut3d_src = mcm_luts.lut3d_data.lut3d_src;493	enum hubp_3dlut_fl_format format;494	enum hubp_3dlut_fl_mode mode;495	enum hubp_3dlut_fl_width width;496	enum hubp_3dlut_fl_addressing_mode addr_mode;497	enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g;498	enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b;499	enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cr_r;500	enum MCM_LUT_XABLE shaper_xable = MCM_LUT_DISABLE;501	enum MCM_LUT_XABLE lut3d_xable = MCM_LUT_DISABLE;502	enum MCM_LUT_XABLE lut1d_xable = MCM_LUT_DISABLE;503	bool is_17x17x17 = true;504	bool rval;505 506	dcn401_get_mcm_lut_xable_from_pipe_ctx(dc, pipe_ctx, &shaper_xable, &lut3d_xable, &lut1d_xable);507 508	/* 1D LUT */509	if (mcm_luts.lut1d_func && lut3d_xable != MCM_LUT_DISABLE) {510		memset(&m_lut_params, 0, sizeof(m_lut_params));511		if (mcm_luts.lut1d_func->type == TF_TYPE_HWPWL)512			m_lut_params.pwl = &mcm_luts.lut1d_func->pwl;513		else if (mcm_luts.lut1d_func->type == TF_TYPE_DISTRIBUTED_POINTS) {514			rval = cm3_helper_translate_curve_to_hw_format(515					mcm_luts.lut1d_func,516					&dpp_base->regamma_params, false);517			m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL;518		}519		if (m_lut_params.pwl) {520			if (mpc->funcs->populate_lut)521				mpc->funcs->populate_lut(mpc, MCM_LUT_1DLUT, m_lut_params, lut_bank_a, mpcc_id);522		}523		if (mpc->funcs->program_lut_mode)524			mpc->funcs->program_lut_mode(mpc, MCM_LUT_1DLUT, lut1d_xable, lut_bank_a, mpcc_id);525	}526 527	/* Shaper */528	if (mcm_luts.shaper) {529		memset(&m_lut_params, 0, sizeof(m_lut_params));530		if (mcm_luts.shaper->type == TF_TYPE_HWPWL)531			m_lut_params.pwl = &mcm_luts.shaper->pwl;532		else if (mcm_luts.shaper->type == TF_TYPE_DISTRIBUTED_POINTS) {533			ASSERT(false);534			rval = cm3_helper_translate_curve_to_hw_format(535					mcm_luts.shaper,536					&dpp_base->regamma_params, true);537			m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL;538		}539		if (m_lut_params.pwl) {540			if (mpc->funcs->populate_lut)541				mpc->funcs->populate_lut(mpc, MCM_LUT_SHAPER, m_lut_params, lut_bank_a, mpcc_id);542		}543		if (mpc->funcs->program_lut_mode)544			mpc->funcs->program_lut_mode(mpc, MCM_LUT_SHAPER, shaper_xable, lut_bank_a, mpcc_id);545	}546 547	/* 3DLUT */548	switch (lut3d_src) {549	case DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM:550		memset(&m_lut_params, 0, sizeof(m_lut_params));551		if (hubp->funcs->hubp_enable_3dlut_fl)552			hubp->funcs->hubp_enable_3dlut_fl(hubp, false);553		if (mcm_luts.lut3d_data.lut3d_func && mcm_luts.lut3d_data.lut3d_func->state.bits.initialized) {554			m_lut_params.lut3d = &mcm_luts.lut3d_data.lut3d_func->lut_3d;555			if (mpc->funcs->populate_lut)556				mpc->funcs->populate_lut(mpc, MCM_LUT_3DLUT, m_lut_params, lut_bank_a, mpcc_id);557			if (mpc->funcs->program_lut_mode)558				mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut3d_xable, lut_bank_a,559						mpcc_id);560		}561		break;562	case DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM:563 564		if (mpc->funcs->program_lut_read_write_control)565			mpc->funcs->program_lut_read_write_control(mpc, MCM_LUT_3DLUT, lut_bank_a, mpcc_id);566		if (mpc->funcs->program_lut_mode)567			mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut3d_xable, lut_bank_a, mpcc_id);568		if (mpc->funcs->program_3dlut_size)569			mpc->funcs->program_3dlut_size(mpc, is_17x17x17, mpcc_id);570		if (hubp->funcs->hubp_program_3dlut_fl_addr)571			hubp->funcs->hubp_program_3dlut_fl_addr(hubp, mcm_luts.lut3d_data.gpu_mem_params.addr);572		switch (mcm_luts.lut3d_data.gpu_mem_params.layout) {573		case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_RGB:574			mode = hubp_3dlut_fl_mode_native_1;575			addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear;576			break;577		case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_BGR:578			mode = hubp_3dlut_fl_mode_native_2;579			addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear;580			break;581		case DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR:582			mode = hubp_3dlut_fl_mode_transform;583			addr_mode = hubp_3dlut_fl_addressing_mode_simple_linear;584			break;585		default:586			mode = hubp_3dlut_fl_mode_disable;587			addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear;588			break;589		}590		if (hubp->funcs->hubp_program_3dlut_fl_mode)591			hubp->funcs->hubp_program_3dlut_fl_mode(hubp, mode);592 593		if (hubp->funcs->hubp_program_3dlut_fl_addressing_mode)594			hubp->funcs->hubp_program_3dlut_fl_addressing_mode(hubp, addr_mode);595 596		switch (mcm_luts.lut3d_data.gpu_mem_params.format_params.format) {597		case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12MSB:598		default:599			format = hubp_3dlut_fl_format_unorm_12msb_bitslice;600			break;601		case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12LSB:602			format = hubp_3dlut_fl_format_unorm_12lsb_bitslice;603			break;604		case DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10:605			format = hubp_3dlut_fl_format_float_fp1_5_10;606			break;607		}608		if (hubp->funcs->hubp_program_3dlut_fl_format)609			hubp->funcs->hubp_program_3dlut_fl_format(hubp, format);610		if (hubp->funcs->hubp_update_3dlut_fl_bias_scale)611			hubp->funcs->hubp_update_3dlut_fl_bias_scale(hubp,612					mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias,613					mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale);614 615		switch (mcm_luts.lut3d_data.gpu_mem_params.component_order) {616		case DC_CM2_GPU_MEM_PIXEL_COMPONENT_ORDER_RGBA:617		default:618			crossbar_bit_slice_cr_r = hubp_3dlut_fl_crossbar_bit_slice_0_15;619			crossbar_bit_slice_y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31;620			crossbar_bit_slice_cb_b = hubp_3dlut_fl_crossbar_bit_slice_32_47;621			break;622		}623 624		if (hubp->funcs->hubp_program_3dlut_fl_crossbar)625			hubp->funcs->hubp_program_3dlut_fl_crossbar(hubp,626					crossbar_bit_slice_y_g,627					crossbar_bit_slice_cb_b,628					crossbar_bit_slice_cr_r);629 630		switch (mcm_luts.lut3d_data.gpu_mem_params.size) {631		case DC_CM2_GPU_MEM_SIZE_171717:632		default:633			width = hubp_3dlut_fl_width_17;634			break;635		case DC_CM2_GPU_MEM_SIZE_TRANSFORMED:636			width = hubp_3dlut_fl_width_transformed;637			break;638		}639		if (hubp->funcs->hubp_program_3dlut_fl_width)640			hubp->funcs->hubp_program_3dlut_fl_width(hubp, width);641		if (mpc->funcs->update_3dlut_fast_load_select)642			mpc->funcs->update_3dlut_fast_load_select(mpc, mpcc_id, hubp->inst);643 644		if (hubp->funcs->hubp_enable_3dlut_fl)645			hubp->funcs->hubp_enable_3dlut_fl(hubp, true);646		else {647			if (mpc->funcs->program_lut_mode) {648				mpc->funcs->program_lut_mode(mpc, MCM_LUT_SHAPER, MCM_LUT_DISABLE, lut_bank_a, mpcc_id);649				mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, MCM_LUT_DISABLE, lut_bank_a, mpcc_id);650				mpc->funcs->program_lut_mode(mpc, MCM_LUT_1DLUT, MCM_LUT_DISABLE, lut_bank_a, mpcc_id);651			}652		}653		break;654 655	}656}657 658void dcn401_trigger_3dlut_dma_load(struct dc *dc, struct pipe_ctx *pipe_ctx)659{660	struct hubp *hubp = pipe_ctx->plane_res.hubp;661 662	if (hubp->funcs->hubp_enable_3dlut_fl) {663		hubp->funcs->hubp_enable_3dlut_fl(hubp, true);664	}665}666 667bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx,668				const struct dc_plane_state *plane_state)669{670	struct dpp *dpp_base = pipe_ctx->plane_res.dpp;671	int mpcc_id = pipe_ctx->plane_res.hubp->inst;672	struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc;673	bool result;674	const struct pwl_params *lut_params = NULL;675	bool rval;676 677	mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id);678	pipe_ctx->plane_state->mcm_location = MPCC_MOVABLE_CM_LOCATION_BEFORE;679	// 1D LUT680	if (plane_state->blend_tf.type == TF_TYPE_HWPWL)681		lut_params = &plane_state->blend_tf.pwl;682	else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) {683		rval = cm3_helper_translate_curve_to_hw_format(&plane_state->blend_tf,684				&dpp_base->regamma_params, false);685		lut_params = rval ? &dpp_base->regamma_params : NULL;686	}687	result = mpc->funcs->program_1dlut(mpc, lut_params, mpcc_id);688	lut_params = NULL;689 690	// Shaper691	if (plane_state->in_shaper_func.type == TF_TYPE_HWPWL)692		lut_params = &plane_state->in_shaper_func.pwl;693	else if (plane_state->in_shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) {694		// TODO: dpp_base replace695		rval = cm3_helper_translate_curve_to_hw_format(&plane_state->in_shaper_func,696				&dpp_base->shaper_params, true);697		lut_params = rval ? &dpp_base->shaper_params : NULL;698	}699	result &= mpc->funcs->program_shaper(mpc, lut_params, mpcc_id);700 701	// 3D702	if (mpc->funcs->program_3dlut) {703		if (plane_state->lut3d_func.state.bits.initialized == 1)704			result &= mpc->funcs->program_3dlut(mpc, &plane_state->lut3d_func.lut_3d, mpcc_id);705		else706			result &= mpc->funcs->program_3dlut(mpc, NULL, mpcc_id);707	}708 709	return result;710}711 712bool dcn401_set_output_transfer_func(struct dc *dc,713				struct pipe_ctx *pipe_ctx,714				const struct dc_stream_state *stream)715{716	int mpcc_id = pipe_ctx->plane_res.hubp->inst;717	struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc;718	const struct pwl_params *params = NULL;719	bool ret = false;720 721	/* program OGAM or 3DLUT only for the top pipe*/722	if (resource_is_pipe_type(pipe_ctx, OPP_HEAD)) {723		/*program shaper and 3dlut in MPC*/724		ret = dcn32_set_mpc_shaper_3dlut(pipe_ctx, stream);725		if (ret == false && mpc->funcs->set_output_gamma) {726			if (stream->out_transfer_func.type == TF_TYPE_HWPWL)727				params = &stream->out_transfer_func.pwl;728			else if (pipe_ctx->stream->out_transfer_func.type ==729					TF_TYPE_DISTRIBUTED_POINTS &&730					cm3_helper_translate_curve_to_hw_format(731					&stream->out_transfer_func,732					&mpc->blender_params, false))733				params = &mpc->blender_params;734			/* there are no ROM LUTs in OUTGAM */735			if (stream->out_transfer_func.type == TF_TYPE_PREDEFINED)736				BREAK_TO_DEBUGGER();737		}738	}739 740	if (mpc->funcs->set_output_gamma)741		mpc->funcs->set_output_gamma(mpc, mpcc_id, params);742 743	return ret;744}745 746void dcn401_calculate_dccg_tmds_div_value(struct pipe_ctx *pipe_ctx,747				unsigned int *tmds_div)748{749	struct dc_stream_state *stream = pipe_ctx->stream;750 751	if (dc_is_tmds_signal(stream->signal) || dc_is_virtual_signal(stream->signal)) {752		if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420)753			*tmds_div = PIXEL_RATE_DIV_BY_2;754		else755			*tmds_div = PIXEL_RATE_DIV_BY_4;756	} else {757		*tmds_div = PIXEL_RATE_DIV_BY_1;758	}759 760	if (*tmds_div == PIXEL_RATE_DIV_NA)761		ASSERT(false);762 763}764 765static void enable_stream_timing_calc(766		struct pipe_ctx *pipe_ctx,767		struct dc_state *context,768		struct dc *dc,769		unsigned int *tmds_div,770		int *opp_inst,771		int *opp_cnt,772		struct pipe_ctx *opp_heads[MAX_PIPES],773		bool *manual_mode,774		struct drr_params *params,775		unsigned int *event_triggers)776{777	struct dc_stream_state *stream = pipe_ctx->stream;778	int i;779 780	if (dc_is_tmds_signal(stream->signal) || dc_is_virtual_signal(stream->signal))781		dcn401_calculate_dccg_tmds_div_value(pipe_ctx, tmds_div);782 783	*opp_cnt = resource_get_opp_heads_for_otg_master(pipe_ctx, &context->res_ctx, opp_heads);784	for (i = 0; i < *opp_cnt; i++)785		opp_inst[i] = opp_heads[i]->stream_res.opp->inst;786 787	if (dc_is_tmds_signal(stream->signal)) {788		stream->link->phy_state.symclk_ref_cnts.otg = 1;789		if (stream->link->phy_state.symclk_state == SYMCLK_OFF_TX_OFF)790			stream->link->phy_state.symclk_state = SYMCLK_ON_TX_OFF;791		else792			stream->link->phy_state.symclk_state = SYMCLK_ON_TX_ON;793	}794 795	params->vertical_total_min = stream->adjust.v_total_min;796	params->vertical_total_max = stream->adjust.v_total_max;797	params->vertical_total_mid = stream->adjust.v_total_mid;798	params->vertical_total_mid_frame_num = stream->adjust.v_total_mid_frame_num;799 800	// DRR should set trigger event to monitor surface update event801	if (stream->adjust.v_total_min != 0 && stream->adjust.v_total_max != 0)802		*event_triggers = 0x80;803}804 805enum dc_status dcn401_enable_stream_timing(806		struct pipe_ctx *pipe_ctx,807		struct dc_state *context,808		struct dc *dc)809{810	struct dce_hwseq *hws = dc->hwseq;811	struct dc_stream_state *stream = pipe_ctx->stream;812	struct drr_params params = {0};813	unsigned int event_triggers = 0;814	int opp_cnt = 1;815	int opp_inst[MAX_PIPES] = {0};816	struct pipe_ctx *opp_heads[MAX_PIPES] = {0};817	bool manual_mode;818	unsigned int tmds_div = PIXEL_RATE_DIV_NA;819	unsigned int unused_div = PIXEL_RATE_DIV_NA;820	int odm_slice_width;821	int last_odm_slice_width;822	int i;823 824	if (!resource_is_pipe_type(pipe_ctx, OTG_MASTER))825		return DC_OK;826 827	enable_stream_timing_calc(pipe_ctx, context, dc, &tmds_div, opp_inst,828			&opp_cnt, opp_heads, &manual_mode, &params, &event_triggers);829 830	if (dc->res_pool->dccg->funcs->set_pixel_rate_div) {831		dc->res_pool->dccg->funcs->set_pixel_rate_div(832			dc->res_pool->dccg, pipe_ctx->stream_res.tg->inst,833			tmds_div, unused_div);834	}835 836	/* TODO check if timing_changed, disable stream if timing changed */837 838	if (opp_cnt > 1) {839		odm_slice_width = resource_get_odm_slice_dst_width(pipe_ctx, false);840		last_odm_slice_width = resource_get_odm_slice_dst_width(pipe_ctx, true);841		pipe_ctx->stream_res.tg->funcs->set_odm_combine(842				pipe_ctx->stream_res.tg,843				opp_inst, opp_cnt,844				odm_slice_width, last_odm_slice_width);845	}846 847	/* HW program guide assume display already disable848	 * by unplug sequence. OTG assume stop.849	 */850	pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, true);851 852	if (false == pipe_ctx->clock_source->funcs->program_pix_clk(853			pipe_ctx->clock_source,854			&pipe_ctx->stream_res.pix_clk_params,855			dc->link_srv->dp_get_encoding_format(&pipe_ctx->link_config.dp_link_settings),856			&pipe_ctx->pll_settings)) {857		BREAK_TO_DEBUGGER();858		return DC_ERROR_UNEXPECTED;859	}860 861	if (dc->hwseq->funcs.PLAT_58856_wa && (!dc_is_dp_signal(stream->signal)))862		dc->hwseq->funcs.PLAT_58856_wa(context, pipe_ctx);863 864	pipe_ctx->stream_res.tg->funcs->program_timing(865			pipe_ctx->stream_res.tg,866			&stream->timing,867			pipe_ctx->pipe_dlg_param.vready_offset,868			pipe_ctx->pipe_dlg_param.vstartup_start,869			pipe_ctx->pipe_dlg_param.vupdate_offset,870			pipe_ctx->pipe_dlg_param.vupdate_width,871			pipe_ctx->pipe_dlg_param.pstate_keepout,872			pipe_ctx->stream->signal,873			true);874 875	for (i = 0; i < opp_cnt; i++) {876		opp_heads[i]->stream_res.opp->funcs->opp_pipe_clock_control(877				opp_heads[i]->stream_res.opp,878				true);879		opp_heads[i]->stream_res.opp->funcs->opp_program_left_edge_extra_pixel(880				opp_heads[i]->stream_res.opp,881				stream->timing.pixel_encoding,882				resource_is_pipe_type(opp_heads[i], OTG_MASTER));883	}884 885	pipe_ctx->stream_res.opp->funcs->opp_pipe_clock_control(886			pipe_ctx->stream_res.opp,887			true);888 889	hws->funcs.blank_pixel_data(dc, pipe_ctx, true);890 891	/* VTG is  within DCHUB command block. DCFCLK is always on */892	if (false == pipe_ctx->stream_res.tg->funcs->enable_crtc(pipe_ctx->stream_res.tg)) {893		BREAK_TO_DEBUGGER();894		return DC_ERROR_UNEXPECTED;895	}896 897	hws->funcs.wait_for_blank_complete(pipe_ctx->stream_res.opp);898 899	if (pipe_ctx->stream_res.tg->funcs->set_drr)900		pipe_ctx->stream_res.tg->funcs->set_drr(901			pipe_ctx->stream_res.tg, &params);902 903	/* Event triggers and num frames initialized for DRR, but can be904	 * later updated for PSR use. Note DRR trigger events are generated905	 * regardless of whether num frames met.906	 */907	if (pipe_ctx->stream_res.tg->funcs->set_static_screen_control)908		pipe_ctx->stream_res.tg->funcs->set_static_screen_control(909				pipe_ctx->stream_res.tg, event_triggers, 2);910 911	/* TODO program crtc source select for non-virtual signal*/912	/* TODO program FMT */913	/* TODO setup link_enc */914	/* TODO set stream attributes */915	/* TODO program audio */916	/* TODO enable stream if timing changed */917	/* TODO unblank stream if DP */918 919	if (dc_state_get_pipe_subvp_type(context, pipe_ctx) == SUBVP_PHANTOM) {920		if (pipe_ctx->stream_res.tg->funcs->phantom_crtc_post_enable)921			pipe_ctx->stream_res.tg->funcs->phantom_crtc_post_enable(pipe_ctx->stream_res.tg);922	}923 924	return DC_OK;925}926 927static enum phyd32clk_clock_source get_phyd32clk_src(struct dc_link *link)928{929	switch (link->link_enc->transmitter) {930	case TRANSMITTER_UNIPHY_A:931		return PHYD32CLKA;932	case TRANSMITTER_UNIPHY_B:933		return PHYD32CLKB;934	case TRANSMITTER_UNIPHY_C:935		return PHYD32CLKC;936	case TRANSMITTER_UNIPHY_D:937		return PHYD32CLKD;938	case TRANSMITTER_UNIPHY_E:939		return PHYD32CLKE;940	default:941		return PHYD32CLKA;942	}943}944 945static void dcn401_enable_stream_calc(946		struct pipe_ctx *pipe_ctx,947		int *dp_hpo_inst,948		enum phyd32clk_clock_source *phyd32clk,949		unsigned int *tmds_div,950		uint32_t *early_control)951{952 953	struct dc *dc = pipe_ctx->stream->ctx->dc;954	struct dc_crtc_timing *timing = &pipe_ctx->stream->timing;955	enum dc_lane_count lane_count =956			pipe_ctx->stream->link->cur_link_settings.lane_count;957	uint32_t active_total_with_borders;958 959	if (dc->link_srv->dp_is_128b_132b_signal(pipe_ctx))960		*dp_hpo_inst = pipe_ctx->stream_res.hpo_dp_stream_enc->inst;961 962	*phyd32clk = get_phyd32clk_src(pipe_ctx->stream->link);963 964	if (dc_is_tmds_signal(pipe_ctx->stream->signal))965		dcn401_calculate_dccg_tmds_div_value(pipe_ctx, tmds_div);966	else967		*tmds_div = PIXEL_RATE_DIV_BY_1;968 969	/* enable early control to avoid corruption on DP monitor*/970	active_total_with_borders =971			timing->h_addressable972				+ timing->h_border_left973				+ timing->h_border_right;974 975	if (lane_count != 0)976		*early_control = active_total_with_borders % lane_count;977 978	if (*early_control == 0)979		*early_control = lane_count;980 981}982 983void dcn401_enable_stream(struct pipe_ctx *pipe_ctx)984{985	uint32_t early_control = 0;986	struct timing_generator *tg = pipe_ctx->stream_res.tg;987	struct dc_link *link = pipe_ctx->stream->link;988	const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res);989	struct dc *dc = pipe_ctx->stream->ctx->dc;990	struct dccg *dccg = dc->res_pool->dccg;991	enum phyd32clk_clock_source phyd32clk;992	int dp_hpo_inst = 0;993	unsigned int tmds_div = PIXEL_RATE_DIV_NA;994	unsigned int unused_div = PIXEL_RATE_DIV_NA;995	struct link_encoder *link_enc = link_enc_cfg_get_link_enc(pipe_ctx->stream->link);996	struct stream_encoder *stream_enc = pipe_ctx->stream_res.stream_enc;997 998	dcn401_enable_stream_calc(pipe_ctx, &dp_hpo_inst, &phyd32clk,999				&tmds_div, &early_control);1000 1001	if (dc_is_dp_signal(pipe_ctx->stream->signal) || dc_is_virtual_signal(pipe_ctx->stream->signal)) {1002		if (dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) {1003			dccg->funcs->set_dpstreamclk(dccg, DPREFCLK, tg->inst, dp_hpo_inst);1004 1005			dccg->funcs->enable_symclk32_se(dccg, dp_hpo_inst, phyd32clk);1006		} else {1007			/* need to set DTBCLK_P source to DPREFCLK for DP8B10B */1008			dccg->funcs->set_dtbclk_p_src(dccg, DPREFCLK, tg->inst);1009			dccg->funcs->enable_symclk_se(dccg, stream_enc->stream_enc_inst,1010					link_enc->transmitter - TRANSMITTER_UNIPHY_A);1011		}1012	}1013 1014	if (dc->res_pool->dccg->funcs->set_pixel_rate_div) {1015		dc->res_pool->dccg->funcs->set_pixel_rate_div(1016			dc->res_pool->dccg,1017			pipe_ctx->stream_res.tg->inst,1018			tmds_div,1019			unused_div);1020	}1021 1022	link_hwss->setup_stream_encoder(pipe_ctx);1023 1024	if (pipe_ctx->plane_state && pipe_ctx->plane_state->flip_immediate != 1) {1025		if (dc->hwss.program_dmdata_engine)1026			dc->hwss.program_dmdata_engine(pipe_ctx);1027	}1028 1029	dc->hwss.update_info_frame(pipe_ctx);1030 1031	if (dc_is_dp_signal(pipe_ctx->stream->signal))1032		dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_UPDATE_INFO_FRAME);1033 1034	tg->funcs->set_early_control(tg, early_control);1035}1036 1037void dcn401_setup_hpo_hw_control(const struct dce_hwseq *hws, bool enable)1038{1039	REG_UPDATE(HPO_TOP_HW_CONTROL, HPO_IO_EN, enable);1040}1041 1042static bool dcn401_can_pipe_disable_cursor(struct pipe_ctx *pipe_ctx)1043{1044	struct pipe_ctx *test_pipe, *split_pipe;1045	const struct scaler_data *scl_data = &pipe_ctx->plane_res.scl_data;1046	struct rect r1 = scl_data->recout, r2, r2_half;1047	int r1_r = r1.x + r1.width, r1_b = r1.y + r1.height, r2_r, r2_b;1048	int cur_layer = pipe_ctx->plane_state->layer_index;1049 1050	/**1051	 * Disable the cursor if there's another pipe above this with a1052	 * plane that contains this pipe's viewport to prevent double cursor1053	 * and incorrect scaling artifacts.1054	 */1055	for (test_pipe = pipe_ctx->top_pipe; test_pipe;1056		test_pipe = test_pipe->top_pipe) {1057		// Skip invisible layer and pipe-split plane on same layer1058		if (!test_pipe->plane_state ||1059			!test_pipe->plane_state->visible ||1060			test_pipe->plane_state->layer_index == cur_layer)1061			continue;1062 1063		r2 = test_pipe->plane_res.scl_data.recout;1064		r2_r = r2.x + r2.width;1065		r2_b = r2.y + r2.height;1066		split_pipe = test_pipe;1067 1068		/**1069		 * There is another half plane on same layer because of1070		 * pipe-split, merge together per same height.1071		 */1072		for (split_pipe = pipe_ctx->top_pipe; split_pipe;1073			split_pipe = split_pipe->top_pipe)1074			if (split_pipe->plane_state->layer_index == test_pipe->plane_state->layer_index) {1075				r2_half = split_pipe->plane_res.scl_data.recout;1076				r2.x = (r2_half.x < r2.x) ? r2_half.x : r2.x;1077				r2.width = r2.width + r2_half.width;1078				r2_r = r2.x + r2.width;1079				break;1080			}1081 1082		if (r1.x >= r2.x && r1.y >= r2.y && r1_r <= r2_r && r1_b <= r2_b)1083			return true;1084	}1085 1086	return false;1087}1088 1089void adjust_hotspot_between_slices_for_2x_magnify(uint32_t cursor_width, struct dc_cursor_position *pos_cpy)1090{1091	if (cursor_width <= 128) {1092		pos_cpy->x_hotspot /= 2;1093		pos_cpy->x_hotspot += 1;1094	} else {1095		pos_cpy->x_hotspot /= 2;1096		pos_cpy->x_hotspot += 2;1097	}1098}1099 1100void dcn401_set_cursor_position(struct pipe_ctx *pipe_ctx)1101{1102	struct dc_cursor_position pos_cpy = pipe_ctx->stream->cursor_position;1103	struct hubp *hubp = pipe_ctx->plane_res.hubp;1104	struct dpp *dpp = pipe_ctx->plane_res.dpp;1105	struct dc_cursor_mi_param param = {1106		.pixel_clk_khz = pipe_ctx->stream->timing.pix_clk_100hz / 10,1107		.ref_clk_khz = pipe_ctx->stream->ctx->dc->res_pool->ref_clocks.dchub_ref_clock_inKhz,1108		.viewport = pipe_ctx->plane_res.scl_data.viewport,1109		.recout = pipe_ctx->plane_res.scl_data.recout,1110		.h_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.horz,1111		.v_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.vert,1112		.rotation = pipe_ctx->plane_state->rotation,1113		.mirror = pipe_ctx->plane_state->horizontal_mirror,1114		.stream = pipe_ctx->stream1115	};1116	struct rect odm_slice_src = { 0 };1117	bool odm_combine_on = (pipe_ctx->next_odm_pipe != NULL) ||1118		(pipe_ctx->prev_odm_pipe != NULL);1119	int prev_odm_width = 0;1120	struct pipe_ctx *prev_odm_pipe = NULL;1121	bool mpc_combine_on = false;1122	int  bottom_pipe_x_pos = 0;1123 1124	int x_pos = pos_cpy.x;1125	int y_pos = pos_cpy.y;1126	int recout_x_pos = 0;1127	int recout_y_pos = 0;1128 1129	if ((pipe_ctx->top_pipe != NULL) || (pipe_ctx->bottom_pipe != NULL)) {1130		if ((pipe_ctx->plane_state->src_rect.width != pipe_ctx->plane_res.scl_data.viewport.width) ||1131			(pipe_ctx->plane_state->src_rect.height != pipe_ctx->plane_res.scl_data.viewport.height)) {1132			mpc_combine_on = true;1133		}1134	}1135 1136	/* DCN4 moved cursor composition after Scaler, so in HW it is in1137	 * recout space and for HW Cursor position programming need to1138	 * translate to recout space.1139	 *1140	 * Cursor X and Y position programmed into HW can't be negative,1141	 * in fact it is X, Y coordinate shifted for the HW Cursor Hot spot1142	 * position that goes into HW X and Y coordinates while HW Hot spot1143	 * X and Y coordinates are length relative to the cursor top left1144	 * corner, hotspot must be smaller than the cursor size.1145	 *1146	 * DMs/DC interface for Cursor position is in stream->src space, and1147	 * DMs supposed to transform Cursor coordinates to stream->src space,1148	 * then here we need to translate Cursor coordinates to stream->dst1149	 * space, as now in HW, Cursor coordinates are in per pipe recout1150	 * space, and for the given pipe valid coordinates are only in range1151	 * from 0,0 - recout width, recout height space.1152	 * If certain pipe combining is in place, need to further adjust per1153	 * pipe to make sure each pipe enabling cursor on its part of the1154	 * screen.1155	 */1156	x_pos = pipe_ctx->stream->dst.x + x_pos * pipe_ctx->stream->dst.width /1157		pipe_ctx->stream->src.width;1158	y_pos = pipe_ctx->stream->dst.y + y_pos * pipe_ctx->stream->dst.height /1159		pipe_ctx->stream->src.height;1160 1161	/* If the cursor's source viewport is clipped then we need to1162	 * translate the cursor to appear in the correct position on1163	 * the screen.1164	 *1165	 * This translation isn't affected by scaling so it needs to be1166	 * done *after* we adjust the position for the scale factor.1167	 *1168	 * This is only done by opt-in for now since there are still1169	 * some usecases like tiled display that might enable the1170	 * cursor on both streams while expecting dc to clip it.1171	 */1172	if (pos_cpy.translate_by_source) {1173		x_pos += pipe_ctx->plane_state->src_rect.x;1174		y_pos += pipe_ctx->plane_state->src_rect.y;1175	}1176 1177	/* Adjust for ODM Combine1178	 * next/prev_odm_offset is to account for scaled modes that have underscan1179	 */1180	if (odm_combine_on) {1181		prev_odm_pipe = pipe_ctx->prev_odm_pipe;1182 1183		while (prev_odm_pipe != NULL) {1184			odm_slice_src = resource_get_odm_slice_src_rect(prev_odm_pipe);1185			prev_odm_width += odm_slice_src.width;1186			prev_odm_pipe = prev_odm_pipe->prev_odm_pipe;1187		}1188 1189		x_pos -= (prev_odm_width);1190	}1191 1192	/* If the position is negative then we need to add to the hotspot1193	 * to fix cursor size between ODM slices1194	 */1195 1196	if (x_pos < 0) {1197		pos_cpy.x_hotspot -= x_pos;1198		if (hubp->curs_attr.attribute_flags.bits.ENABLE_MAGNIFICATION)1199			adjust_hotspot_between_slices_for_2x_magnify(hubp->curs_attr.width, &pos_cpy);1200		x_pos = 0;1201	}1202 1203	if (y_pos < 0) {1204		pos_cpy.y_hotspot -= y_pos;1205		y_pos = 0;1206	}1207 1208	/* If the position on bottom MPC pipe is negative then we need to add to the hotspot and1209	 * adjust x_pos on bottom pipe to make cursor visible when crossing between MPC slices.1210	 */1211	if (mpc_combine_on &&1212		pipe_ctx->top_pipe &&1213		(pipe_ctx == pipe_ctx->top_pipe->bottom_pipe)) {1214 1215		bottom_pipe_x_pos = x_pos - pipe_ctx->plane_res.scl_data.recout.x;1216		if (bottom_pipe_x_pos < 0) {1217			x_pos = pipe_ctx->plane_res.scl_data.recout.x;1218			pos_cpy.x_hotspot -= bottom_pipe_x_pos;1219			if (hubp->curs_attr.attribute_flags.bits.ENABLE_MAGNIFICATION)1220				adjust_hotspot_between_slices_for_2x_magnify(hubp->curs_attr.width, &pos_cpy);1221		}1222	}1223 1224	pos_cpy.x = (uint32_t)x_pos;1225	pos_cpy.y = (uint32_t)y_pos;1226 1227	if (pos_cpy.enable && dcn401_can_pipe_disable_cursor(pipe_ctx))1228		pos_cpy.enable = false;1229 1230	x_pos = pos_cpy.x - param.recout.x;1231	y_pos = pos_cpy.y - param.recout.y;1232 1233	recout_x_pos = x_pos - pos_cpy.x_hotspot;1234	recout_y_pos = y_pos - pos_cpy.y_hotspot;1235 1236	if (recout_x_pos >= (int)param.recout.width)1237		pos_cpy.enable = false;  /* not visible beyond right edge*/1238 1239	if (recout_y_pos >= (int)param.recout.height)1240		pos_cpy.enable = false;  /* not visible beyond bottom edge*/1241 1242	if (recout_x_pos + (int)hubp->curs_attr.width <= 0)1243		pos_cpy.enable = false;  /* not visible beyond left edge*/1244 1245	if (recout_y_pos + (int)hubp->curs_attr.height <= 0)1246		pos_cpy.enable = false;  /* not visible beyond top edge*/1247 1248	hubp->funcs->set_cursor_position(hubp, &pos_cpy, &param);1249	dpp->funcs->set_cursor_position(dpp, &pos_cpy, &param, hubp->curs_attr.width, hubp->curs_attr.height);1250}1251 1252static bool dcn401_check_no_memory_request_for_cab(struct dc *dc)1253{1254	int i;1255 1256	/* First, check no-memory-request case */1257	for (i = 0; i < dc->current_state->stream_count; i++) {1258		if ((dc->current_state->stream_status[i].plane_count) &&1259			(dc->current_state->streams[i]->link->psr_settings.psr_version == DC_PSR_VERSION_UNSUPPORTED))1260			/* Fail eligibility on a visible stream */1261			return false;1262	}1263 1264	return true;1265}1266 1267static uint32_t dcn401_calculate_cab_allocation(struct dc *dc, struct dc_state *ctx)1268{1269	int i;1270	uint8_t num_ways = 0;1271	uint32_t mall_ss_size_bytes = 0;1272 1273	mall_ss_size_bytes = ctx->bw_ctx.bw.dcn.mall_ss_size_bytes;1274	// TODO add additional logic for PSR active stream exclusion optimization1275	// mall_ss_psr_active_size_bytes = ctx->bw_ctx.bw.dcn.mall_ss_psr_active_size_bytes;1276 1277	// Include cursor size for CAB allocation1278	for (i = 0; i < dc->res_pool->pipe_count; i++) {1279		struct pipe_ctx *pipe = &ctx->res_ctx.pipe_ctx[i];1280 1281		if (!pipe->stream || !pipe->plane_state)1282			continue;1283 1284		mall_ss_size_bytes += dcn32_helper_calculate_mall_bytes_for_cursor(dc, pipe, false);1285	}1286 1287	// Convert number of cache lines required to number of ways1288	if (dc->debug.force_mall_ss_num_ways > 0)1289		num_ways = dc->debug.force_mall_ss_num_ways;1290	else if (dc->res_pool->funcs->calculate_mall_ways_from_bytes)1291		num_ways = dc->res_pool->funcs->calculate_mall_ways_from_bytes(dc, mall_ss_size_bytes);1292	else1293		num_ways = 0;1294 1295	return num_ways;1296}1297 1298bool dcn401_apply_idle_power_optimizations(struct dc *dc, bool enable)1299{1300	union dmub_rb_cmd cmd;1301	uint8_t ways, i;1302	int j;1303	bool mall_ss_unsupported = false;1304	struct dc_plane_state *plane = NULL;1305 1306	if (!dc->ctx->dmub_srv || !dc->current_state)1307		return false;1308 1309	for (i = 0; i < dc->current_state->stream_count; i++) {1310		/* MALL SS messaging is not supported with PSR at this time */1311		if (dc->current_state->streams[i] != NULL &&1312				dc->current_state->streams[i]->link->psr_settings.psr_version != DC_PSR_VERSION_UNSUPPORTED) {1313			DC_LOG_MALL("MALL SS not supported with PSR at this time\n");1314			return false;1315		}1316	}1317 1318	memset(&cmd, 0, sizeof(cmd));1319	cmd.cab.header.type = DMUB_CMD__CAB_FOR_SS;1320	cmd.cab.header.payload_bytes = sizeof(cmd.cab) - sizeof(cmd.cab.header);1321 1322	if (enable) {1323		if (dcn401_check_no_memory_request_for_cab(dc)) {1324			/* 1. Check no memory request case for CAB.1325			 * If no memory request case, send CAB_ACTION NO_DCN_REQ DMUB message1326			 */1327			DC_LOG_MALL("sending CAB action NO_DCN_REQ\n");1328			cmd.cab.header.sub_type = DMUB_CMD__CAB_NO_DCN_REQ;1329		} else {1330			/* 2. Check if all surfaces can fit in CAB.1331			 * If surfaces can fit into CAB, send CAB_ACTION_ALLOW DMUB message1332			 * and configure HUBP's to fetch from MALL1333			 */1334			ways = dcn401_calculate_cab_allocation(dc, dc->current_state);1335 1336			/* MALL not supported with Stereo3D or TMZ surface. If any plane is using stereo,1337			 * or TMZ surface, don't try to enter MALL.1338			 */1339			for (i = 0; i < dc->current_state->stream_count; i++) {1340				for (j = 0; j < dc->current_state->stream_status[i].plane_count; j++) {1341					plane = dc->current_state->stream_status[i].plane_states[j];1342 1343					if (plane->address.type == PLN_ADDR_TYPE_GRPH_STEREO ||1344							plane->address.tmz_surface) {1345						mall_ss_unsupported = true;1346						break;1347					}1348				}1349				if (mall_ss_unsupported)1350					break;1351			}1352			if (ways <= dc->caps.cache_num_ways && !mall_ss_unsupported) {1353				cmd.cab.header.sub_type = DMUB_CMD__CAB_DCN_SS_FIT_IN_CAB;1354				cmd.cab.cab_alloc_ways = ways;1355				DC_LOG_MALL("cab allocation: %d ways. CAB action: DCN_SS_FIT_IN_CAB\n", ways);1356			} else {1357				cmd.cab.header.sub_type = DMUB_CMD__CAB_DCN_SS_NOT_FIT_IN_CAB;1358				DC_LOG_MALL("frame does not fit in CAB: %d ways required. CAB action: DCN_SS_NOT_FIT_IN_CAB\n", ways);1359			}1360		}1361	} else {1362		/* Disable CAB */1363		cmd.cab.header.sub_type = DMUB_CMD__CAB_NO_IDLE_OPTIMIZATION;1364		DC_LOG_MALL("idle optimization disabled\n");1365	}1366 1367	dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);1368 1369	return true;1370}1371 1372void dcn401_wait_for_dcc_meta_propagation(const struct dc *dc,1373		const struct pipe_ctx *top_pipe)1374{1375	bool is_wait_needed = false;1376	const struct pipe_ctx *pipe_ctx = top_pipe;1377 1378	/* check if any surfaces are updating address while using flip immediate and dcc */1379	while (pipe_ctx != NULL) {1380		if (pipe_ctx->plane_state &&1381				pipe_ctx->plane_state->dcc.enable &&1382				pipe_ctx->plane_state->flip_immediate &&1383				pipe_ctx->plane_state->update_flags.bits.addr_update) {1384			is_wait_needed = true;1385			break;1386		}1387 1388		/* check next pipe */1389		pipe_ctx = pipe_ctx->bottom_pipe;1390	}1391 1392	if (is_wait_needed && dc->debug.dcc_meta_propagation_delay_us > 0) {1393		udelay(dc->debug.dcc_meta_propagation_delay_us);1394	}1395}1396 1397void dcn401_prepare_bandwidth(struct dc *dc,1398	struct dc_state *context)1399{1400	struct hubbub *hubbub = dc->res_pool->hubbub;1401	bool p_state_change_support = context->bw_ctx.bw.dcn.clk.p_state_change_support;1402	unsigned int compbuf_size = 0;1403 1404	/* Any transition into P-State support should disable MCLK switching first to avoid hangs */1405	if (p_state_change_support) {1406		dc->optimized_required = true;1407		context->bw_ctx.bw.dcn.clk.p_state_change_support = false;1408	}1409 1410	if (dc->clk_mgr->dc_mode_softmax_enabled)1411		if (dc->clk_mgr->clks.dramclk_khz <= dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000 &&1412				context->bw_ctx.bw.dcn.clk.dramclk_khz > dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000)1413			dc->clk_mgr->funcs->set_max_memclk(dc->clk_mgr, dc->clk_mgr->bw_params->clk_table.entries[dc->clk_mgr->bw_params->clk_table.num_entries - 1].memclk_mhz);1414 1415	/* Increase clocks */1416	dc->clk_mgr->funcs->update_clocks(1417			dc->clk_mgr,1418			context,1419			false);1420 1421	/* program dchubbub watermarks:1422	 * For assigning wm_optimized_required, use |= operator since we don't want1423	 * to clear the value if the optimize has not happened yet1424	 */1425	dc->wm_optimized_required |= hubbub->funcs->program_watermarks(hubbub,1426					&context->bw_ctx.bw.dcn.watermarks,1427					dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000,1428					false);1429 1430	/* decrease compbuf size */1431	if (hubbub->funcs->program_compbuf_segments) {1432		compbuf_size = context->bw_ctx.bw.dcn.arb_regs.compbuf_size;1433		dc->wm_optimized_required |= (compbuf_size != dc->current_state->bw_ctx.bw.dcn.arb_regs.compbuf_size);1434 1435		hubbub->funcs->program_compbuf_segments(hubbub, compbuf_size, false);1436	}1437 1438	if (dc->debug.fams2_config.bits.enable) {1439		dcn401_fams2_global_control_lock(dc, context, true);1440		dcn401_fams2_update_config(dc, context, false);1441		dcn401_fams2_global_control_lock(dc, context, false);1442	}1443 1444	if (p_state_change_support != context->bw_ctx.bw.dcn.clk.p_state_change_support) {1445		/* After disabling P-State, restore the original value to ensure we get the correct P-State1446		 * on the next optimize. */1447		context->bw_ctx.bw.dcn.clk.p_state_change_support = p_state_change_support;1448	}1449}1450 1451void dcn401_optimize_bandwidth(1452		struct dc *dc,1453		struct dc_state *context)1454{1455	int i;1456	struct hubbub *hubbub = dc->res_pool->hubbub;1457 1458	/* enable fams2 if needed */1459	if (dc->debug.fams2_config.bits.enable) {1460		dcn401_fams2_global_control_lock(dc, context, true);1461		dcn401_fams2_update_config(dc, context, true);1462		dcn401_fams2_global_control_lock(dc, context, false);1463	}1464 1465	/* program dchubbub watermarks */1466	hubbub->funcs->program_watermarks(hubbub,1467					&context->bw_ctx.bw.dcn.watermarks,1468					dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000,1469					true);1470 1471	if (dc->clk_mgr->dc_mode_softmax_enabled)1472		if (dc->clk_mgr->clks.dramclk_khz > dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000 &&1473				context->bw_ctx.bw.dcn.clk.dramclk_khz <= dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000)1474			dc->clk_mgr->funcs->set_max_memclk(dc->clk_mgr, dc->clk_mgr->bw_params->dc_mode_softmax_memclk);1475 1476	/* increase compbuf size */1477	if (hubbub->funcs->program_compbuf_segments)1478		hubbub->funcs->program_compbuf_segments(hubbub, context->bw_ctx.bw.dcn.arb_regs.compbuf_size, true);1479 1480	dc->clk_mgr->funcs->update_clocks(1481			dc->clk_mgr,1482			context,1483			true);1484	if (context->bw_ctx.bw.dcn.clk.zstate_support == DCN_ZSTATE_SUPPORT_ALLOW) {1485		for (i = 0; i < dc->res_pool->pipe_count; ++i) {1486			struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];1487 1488			if (pipe_ctx->stream && pipe_ctx->plane_res.hubp->funcs->program_extended_blank1489				&& pipe_ctx->stream->adjust.v_total_min == pipe_ctx->stream->adjust.v_total_max1490				&& pipe_ctx->stream->adjust.v_total_max > pipe_ctx->stream->timing.v_total)1491					pipe_ctx->plane_res.hubp->funcs->program_extended_blank(pipe_ctx->plane_res.hubp,1492						pipe_ctx->dlg_regs.min_dst_y_next_start);1493		}1494	}1495}1496 1497void dcn401_fams2_global_control_lock(struct dc *dc,1498		struct dc_state *context,1499		bool lock)1500{1501	/* use always for now */1502	union dmub_inbox0_cmd_lock_hw hw_lock_cmd = { 0 };1503 1504	if (!dc->ctx || !dc->ctx->dmub_srv || !dc->debug.fams2_config.bits.enable)1505		return;1506 1507	hw_lock_cmd.bits.command_code = DMUB_INBOX0_CMD__HW_LOCK;1508	hw_lock_cmd.bits.hw_lock_client = HW_LOCK_CLIENT_DRIVER;1509	hw_lock_cmd.bits.lock = lock;1510	hw_lock_cmd.bits.should_release = !lock;1511	dmub_hw_lock_mgr_inbox0_cmd(dc->ctx->dmub_srv, hw_lock_cmd);1512}1513 1514void dcn401_fams2_global_control_lock_fast(union block_sequence_params *params)1515{1516	struct dc *dc = params->fams2_global_control_lock_fast_params.dc;1517	bool lock = params->fams2_global_control_lock_fast_params.lock;1518 1519	if (params->fams2_global_control_lock_fast_params.is_required) {1520		union dmub_inbox0_cmd_lock_hw hw_lock_cmd = { 0 };1521 1522		hw_lock_cmd.bits.command_code = DMUB_INBOX0_CMD__HW_LOCK;1523		hw_lock_cmd.bits.hw_lock_client = HW_LOCK_CLIENT_DRIVER;1524		hw_lock_cmd.bits.lock = lock;1525		hw_lock_cmd.bits.should_release = !lock;1526		dmub_hw_lock_mgr_inbox0_cmd(dc->ctx->dmub_srv, hw_lock_cmd);1527	}1528}1529 1530void dcn401_fams2_update_config(struct dc *dc, struct dc_state *context, bool enable)1531{1532	bool fams2_required;1533 1534	if (!dc->ctx || !dc->ctx->dmub_srv || !dc->debug.fams2_config.bits.enable)1535		return;1536 1537	fams2_required = context->bw_ctx.bw.dcn.fams2_global_config.features.bits.enable;1538 1539	dc_dmub_srv_fams2_update_config(dc, context, enable && fams2_required);1540}1541 1542static void update_dsc_for_odm_change(struct dc *dc, struct dc_state *context,1543		struct pipe_ctx *otg_master)1544{1545	int i;1546	struct pipe_ctx *old_pipe;1547	struct pipe_ctx *new_pipe;1548	struct pipe_ctx *old_opp_heads[MAX_PIPES];1549	struct pipe_ctx *old_otg_master;1550	int old_opp_head_count = 0;1551 1552	old_otg_master = &dc->current_state->res_ctx.pipe_ctx[otg_master->pipe_idx];1553 1554	if (resource_is_pipe_type(old_otg_master, OTG_MASTER)) {1555		old_opp_head_count = resource_get_opp_heads_for_otg_master(old_otg_master,1556									   &dc->current_state->res_ctx,1557									   old_opp_heads);1558	} else {1559		// DC cannot assume that the current state and the new state1560		// share the same OTG pipe since this is not true when called1561		// in the context of a commit stream not checked. Hence, set1562		// old_otg_master to NULL to skip the DSC configuration.1563		old_otg_master = NULL;1564	}1565 1566 1567	if (otg_master->stream_res.dsc)1568		dcn32_update_dsc_on_stream(otg_master,1569				otg_master->stream->timing.flags.DSC);1570	if (old_otg_master && old_otg_master->stream_res.dsc) {1571		for (i = 0; i < old_opp_head_count; i++) {1572			old_pipe = old_opp_heads[i];1573			new_pipe = &context->res_ctx.pipe_ctx[old_pipe->pipe_idx];1574			if (old_pipe->stream_res.dsc && !new_pipe->stream_res.dsc)1575				old_pipe->stream_res.dsc->funcs->dsc_disconnect(1576						old_pipe->stream_res.dsc);1577		}1578	}1579}1580 1581void dcn401_update_odm(struct dc *dc, struct dc_state *context,1582		struct pipe_ctx *otg_master)1583{1584	struct pipe_ctx *opp_heads[MAX_PIPES];1585	int opp_inst[MAX_PIPES] = {0};1586	int opp_head_count;1587	int odm_slice_width = resource_get_odm_slice_dst_width(otg_master, false);1588	int last_odm_slice_width = resource_get_odm_slice_dst_width(otg_master, true);1589	int i;1590 1591	opp_head_count = resource_get_opp_heads_for_otg_master(1592			otg_master, &context->res_ctx, opp_heads);1593 1594	for (i = 0; i < opp_head_count; i++)1595		opp_inst[i] = opp_heads[i]->stream_res.opp->inst;1596	if (opp_head_count > 1)1597		otg_master->stream_res.tg->funcs->set_odm_combine(1598				otg_master->stream_res.tg,1599				opp_inst, opp_head_count,1600				odm_slice_width, last_odm_slice_width);1601	else1602		otg_master->stream_res.tg->funcs->set_odm_bypass(1603				otg_master->stream_res.tg,1604				&otg_master->stream->timing);1605 1606	for (i = 0; i < opp_head_count; i++) {1607		opp_heads[i]->stream_res.opp->funcs->opp_pipe_clock_control(1608				opp_heads[i]->stream_res.opp,1609				true);1610		opp_heads[i]->stream_res.opp->funcs->opp_program_left_edge_extra_pixel(1611				opp_heads[i]->stream_res.opp,1612				opp_heads[i]->stream->timing.pixel_encoding,1613				resource_is_pipe_type(opp_heads[i], OTG_MASTER));1614	}1615 1616	update_dsc_for_odm_change(dc, context, otg_master);1617 1618	if (!resource_is_pipe_type(otg_master, DPP_PIPE))1619		/*1620		 * blank pattern is generated by OPP, reprogram blank pattern1621		 * due to OPP count change1622		 */1623		dc->hwseq->funcs.blank_pixel_data(dc, otg_master, true);1624}1625 1626void dcn401_unblank_stream(struct pipe_ctx *pipe_ctx,1627		struct dc_link_settings *link_settings)1628{1629	struct encoder_unblank_param params = {0};1630	struct dc_stream_state *stream = pipe_ctx->stream;1631	struct dc_link *link = stream->link;1632	struct dce_hwseq *hws = link->dc->hwseq;1633 1634	/* calculate parameters for unblank */1635	params.opp_cnt = resource_get_odm_slice_count(pipe_ctx);1636 1637	params.timing = pipe_ctx->stream->timing;1638	params.link_settings.link_rate = link_settings->link_rate;1639	params.pix_per_cycle = pipe_ctx->stream_res.pix_clk_params.dio_se_pix_per_cycle;1640 1641	if (link->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) {1642		pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->dp_unblank(1643				pipe_ctx->stream_res.hpo_dp_stream_enc,1644				pipe_ctx->stream_res.tg->inst);1645	} else if (dc_is_dp_signal(pipe_ctx->stream->signal)) {1646		pipe_ctx->stream_res.stream_enc->funcs->dp_unblank(link, pipe_ctx->stream_res.stream_enc, &params);1647	}1648 1649	if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP)1650		hws->funcs.edp_backlight_control(link, true);1651}1652 1653void dcn401_hardware_release(struct dc *dc)1654{1655	dc_dmub_srv_fams2_update_config(dc, dc->current_state, false);1656 1657	/* If pstate unsupported, or still supported1658	 * by firmware, force it supported by dcn1659	 */1660	if (dc->current_state) {1661		if ((!dc->clk_mgr->clks.p_state_change_support ||1662				dc->current_state->bw_ctx.bw.dcn.fams2_global_config.features.bits.enable) &&1663				dc->res_pool->hubbub->funcs->force_pstate_change_control)1664			dc->res_pool->hubbub->funcs->force_pstate_change_control(1665					dc->res_pool->hubbub, true, true);1666 1667		dc->current_state->bw_ctx.bw.dcn.clk.p_state_change_support = true;1668		dc->clk_mgr->funcs->update_clocks(dc->clk_mgr, dc->current_state, true);1669	}1670}1671 1672void dcn401_wait_for_det_buffer_update(struct dc *dc, struct dc_state *context, struct pipe_ctx *otg_master)1673{1674	struct pipe_ctx *opp_heads[MAX_PIPES];1675	struct pipe_ctx *dpp_pipes[MAX_PIPES];1676	struct hubbub *hubbub = dc->res_pool->hubbub;1677	int dpp_count = 0;1678 1679	if (!otg_master->stream)1680		return;1681 1682	int slice_count = resource_get_opp_heads_for_otg_master(otg_master,1683			&context->res_ctx, opp_heads);1684 1685	for (int slice_idx = 0; slice_idx < slice_count; slice_idx++) {1686		if (opp_heads[slice_idx]->plane_state) {1687			dpp_count = resource_get_dpp_pipes_for_opp_head(1688					opp_heads[slice_idx],1689					&context->res_ctx,1690					dpp_pipes);1691			for (int dpp_idx = 0; dpp_idx < dpp_count; dpp_idx++) {1692				struct pipe_ctx *dpp_pipe = dpp_pipes[dpp_idx];1693					if (dpp_pipe && hubbub &&1694						dpp_pipe->plane_res.hubp &&1695						hubbub->funcs->wait_for_det_update)1696						hubbub->funcs->wait_for_det_update(hubbub, dpp_pipe->plane_res.hubp->inst);1697			}1698		}1699	}1700}1701 1702void dcn401_interdependent_update_lock(struct dc *dc,1703		struct dc_state *context, bool lock)1704{1705	unsigned int i = 0;1706	struct pipe_ctx *pipe = NULL;1707	struct timing_generator *tg = NULL;1708	bool pipe_unlocked[MAX_PIPES] = {0};1709 1710	if (lock) {1711		for (i = 0; i < dc->res_pool->pipe_count; i++) {1712			pipe = &context->res_ctx.pipe_ctx[i];1713			tg = pipe->stream_res.tg;1714 1715			if (!resource_is_pipe_type(pipe, OTG_MASTER) ||1716					!tg->funcs->is_tg_enabled(tg) ||1717					dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM)1718				continue;1719			dc->hwss.pipe_control_lock(dc, pipe, true);1720		}1721	} else {1722		/* Unlock pipes based on the change in DET allocation instead of pipe index1723		 * Prevents over allocation of DET during unlock process1724		 * e.g. 2 pipe config with different streams with a max of 20 DET segments1725		 *	Before:								After:1726		 *		- Pipe0: 10 DET segments			- Pipe0: 12 DET segments1727		 *		- Pipe1: 10 DET segments			- Pipe1: 8 DET segments1728		 * If Pipe0 gets updated first, 22 DET segments will be allocated1729		 */1730		for (i = 0; i < dc->res_pool->pipe_count; i++) {1731			pipe = &context->res_ctx.pipe_ctx[i];1732			tg = pipe->stream_res.tg;1733			int current_pipe_idx = i;1734 1735			if (!resource_is_pipe_type(pipe, OTG_MASTER) ||1736					!tg->funcs->is_tg_enabled(tg) ||1737					dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM) {1738				pipe_unlocked[i] = true;1739				continue;1740			}1741 1742			// If the same stream exists in old context, ensure the OTG_MASTER pipes for the same stream get compared1743			struct pipe_ctx *old_otg_master = resource_get_otg_master_for_stream(&dc->current_state->res_ctx, pipe->stream);1744 1745			if (old_otg_master)1746				current_pipe_idx = old_otg_master->pipe_idx;1747			if (resource_calculate_det_for_stream(context, pipe) <1748					resource_calculate_det_for_stream(dc->current_state, &dc->current_state->res_ctx.pipe_ctx[current_pipe_idx])) {1749				dc->hwss.pipe_control_lock(dc, pipe, false);1750				pipe_unlocked[i] = true;1751				dcn401_wait_for_det_buffer_update(dc, context, pipe);1752			}1753		}1754 1755		for (i = 0; i < dc->res_pool->pipe_count; i++) {1756			if (pipe_unlocked[i])1757				continue;1758			pipe = &context->res_ctx.pipe_ctx[i];1759			dc->hwss.pipe_control_lock(dc, pipe, false);1760		}1761	}1762}1763 1764void dcn401_program_outstanding_updates(struct dc *dc,1765		struct dc_state *context)1766{1767	struct hubbub *hubbub = dc->res_pool->hubbub;1768 1769	/* update compbuf if required */1770	if (hubbub->funcs->program_compbuf_segments)1771		hubbub->funcs->program_compbuf_segments(hubbub, context->bw_ctx.bw.dcn.arb_regs.compbuf_size, true);1772}1773