File Index
Files and directories
Sign in
FILE

dcn401_hwseq.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c C
   1// SPDX-License-Identifier: MIT
   2//
   3// Copyright 2024 Advanced Micro Devices, Inc.
   4
   5
   6#include "os_types.h"
   7#include "dm_services.h"
   8#include "basics/dc_common.h"
   9#include "dm_helpers.h"
  10#include "core_types.h"
  11#include "resource.h"
  12#include "dccg.h"
  13#include "dce/dce_hwseq.h"
  14#include "reg_helper.h"
  15#include "abm.h"
  16#include "hubp.h"
  17#include "dchubbub.h"
  18#include "timing_generator.h"
  19#include "opp.h"
  20#include "ipp.h"
  21#include "mpc.h"
  22#include "mcif_wb.h"
  23#include "dc_dmub_srv.h"
  24#include "link_hwss.h"
  25#include "dpcd_defs.h"
  26#include "clk_mgr.h"
  27#include "dsc.h"
  28#include "link_service.h"
  29#include "custom_float.h"
  30
  31#include "dce/dmub_hw_lock_mgr.h"
  32#include "dcn10/dcn10_cm_common.h"
  33#include "dcn10/dcn10_hubbub.h"
  34#include "dcn20/dcn20_optc.h"
  35#include "dcn30/dcn30_cm_common.h"
  36#include "dcn32/dcn32_hwseq.h"
  37#include "dcn401_hwseq.h"
  38#include "dcn401/dcn401_resource.h"
  39#include "dc_state_priv.h"
  40#include "link_enc_cfg.h"
  41#include "../hw_sequencer.h"
  42
  43#define DC_LOGGER_INIT(logger)
  44
  45#define CTX \
  46	hws->ctx
  47#define REG(reg)\
  48	hws->regs->reg
  49#define DC_LOGGER \
  50	dc->ctx->logger
  51
  52
  53#undef FN
  54#define FN(reg_name, field_name) \
  55	hws->shifts->field_name, hws->masks->field_name
  56
  57void dcn401_initialize_min_clocks(struct dc *dc)
  58{
  59	struct dc_clocks *clocks = &dc->current_state->bw_ctx.bw.dcn.clk;
  60
  61	clocks->dcfclk_deep_sleep_khz = DCN3_2_DCFCLK_DS_INIT_KHZ;
  62	clocks->dcfclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].dcfclk_mhz * 1000;
  63	clocks->socclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].socclk_mhz * 1000;
  64	clocks->dramclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].memclk_mhz * 1000;
  65	clocks->dppclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].dppclk_mhz * 1000;
  66	if (dc->debug.disable_boot_optimizations) {
  67		clocks->dispclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].dispclk_mhz * 1000;
  68	} else {
  69		/* Even though DPG_EN = 1 for the connected display, it still requires the
  70		 * correct timing so we cannot set DISPCLK to min freq or it could cause
  71		 * audio corruption. Read current DISPCLK from DENTIST and request the same
  72		 * freq to ensure that the timing is valid and unchanged.
  73		 */
  74		if (dc->clk_mgr->funcs->get_dispclk_from_dentist) {
  75			clocks->dispclk_khz = dc->clk_mgr->funcs->get_dispclk_from_dentist(dc->clk_mgr);
  76		} else {
  77			clocks->dispclk_khz = dc->clk_mgr->boot_snapshot.dispclk * 1000;
  78		}
  79	}
  80	clocks->ref_dtbclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].dtbclk_mhz * 1000;
  81	clocks->fclk_p_state_change_support = true;
  82	clocks->p_state_change_support = true;
  83
  84	dc->clk_mgr->funcs->update_clocks(
  85			dc->clk_mgr,
  86			dc->current_state,
  87			true);
  88}
  89
  90void dcn401_program_gamut_remap(struct pipe_ctx *pipe_ctx)
  91{
  92	unsigned int i = 0;
  93	struct mpc_grph_gamut_adjustment mpc_adjust;
  94	unsigned int mpcc_id = pipe_ctx->plane_res.mpcc_inst;
  95	struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc;
  96
  97	//For now assert if location is not pre-blend
  98	if (pipe_ctx->plane_state)
  99		ASSERT(pipe_ctx->plane_state->mcm_location == MPCC_MOVABLE_CM_LOCATION_BEFORE);
 100
 101	// program MPCC_MCM_FIRST_GAMUT_REMAP
 102	memset(&mpc_adjust, 0, sizeof(mpc_adjust));
 103	mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS;
 104	mpc_adjust.mpcc_gamut_remap_block_id = MPCC_MCM_FIRST_GAMUT_REMAP;
 105
 106	if (pipe_ctx->plane_state &&
 107		pipe_ctx->plane_state->gamut_remap_matrix.enable_remap == true) {
 108		mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW;
 109		for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++)
 110			mpc_adjust.temperature_matrix[i] =
 111			pipe_ctx->plane_state->gamut_remap_matrix.matrix[i];
 112	}
 113
 114	mpc->funcs->set_gamut_remap(mpc, mpcc_id, &mpc_adjust);
 115
 116	// program MPCC_MCM_SECOND_GAMUT_REMAP for Bypass / Disable for now
 117	mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS;
 118	mpc_adjust.mpcc_gamut_remap_block_id = MPCC_MCM_SECOND_GAMUT_REMAP;
 119
 120	mpc->funcs->set_gamut_remap(mpc, mpcc_id, &mpc_adjust);
 121
 122	// program MPCC_OGAM_GAMUT_REMAP same as is currently used on DCN3x
 123	memset(&mpc_adjust, 0, sizeof(mpc_adjust));
 124	mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS;
 125	mpc_adjust.mpcc_gamut_remap_block_id = MPCC_OGAM_GAMUT_REMAP;
 126
 127	if (pipe_ctx->top_pipe == NULL) {
 128		if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) {
 129			mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW;
 130			for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++)
 131				mpc_adjust.temperature_matrix[i] =
 132				pipe_ctx->stream->gamut_remap_matrix.matrix[i];
 133		}
 134	}
 135
 136	mpc->funcs->set_gamut_remap(mpc, mpcc_id, &mpc_adjust);
 137}
 138
 139void dcn401_init_hw(struct dc *dc)
 140{
 141	struct abm **abms = dc->res_pool->multiple_abms;
 142	struct dce_hwseq *hws = dc->hwseq;
 143	struct dc_bios *dcb = dc->ctx->dc_bios;
 144	struct resource_pool *res_pool = dc->res_pool;
 145	int i;
 146	int edp_num;
 147	uint32_t backlight = MAX_BACKLIGHT_LEVEL;
 148	uint32_t user_level = MAX_BACKLIGHT_LEVEL;
 149	bool dchub_ref_freq_changed;
 150	int current_dchub_ref_freq = 0;
 151
 152	if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->init_clocks) {
 153		dc->clk_mgr->funcs->init_clocks(dc->clk_mgr);
 154
 155		// mark dcmode limits present if any clock has distinct AC and DC values from SMU
 156		dc->caps.dcmode_power_limits_present = dc->clk_mgr->funcs->is_dc_mode_present &&
 157				dc->clk_mgr->funcs->is_dc_mode_present(dc->clk_mgr);
 158	}
 159
 160	// Initialize the dccg
 161	if (res_pool->dccg->funcs->dccg_init)
 162		res_pool->dccg->funcs->dccg_init(res_pool->dccg);
 163
 164	// Disable DMUB Initialization until IPS state programming is finalized
 165	//if (!dcb->funcs->is_accelerated_mode(dcb)) {
 166	//	hws->funcs.bios_golden_init(dc);
 167	//}
 168
 169	// Set default OPTC memory power states
 170	if (dc->debug.enable_mem_low_power.bits.optc) {
 171		// Shutdown when unassigned and light sleep in VBLANK
 172		REG_SET_2(ODM_MEM_PWR_CTRL3, 0, ODM_MEM_UNASSIGNED_PWR_MODE, 3, ODM_MEM_VBLANK_PWR_MODE, 1);
 173	}
 174
 175	if (dc->debug.enable_mem_low_power.bits.vga) {
 176		// Power down VGA memory
 177		REG_UPDATE(MMHUBBUB_MEM_PWR_CNTL, VGA_MEM_PWR_FORCE, 1);
 178	}
 179
 180	if (dc->ctx->dc_bios->fw_info_valid) {
 181		res_pool->ref_clocks.xtalin_clock_inKhz =
 182				dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency;
 183
 184		if (res_pool->hubbub) {
 185			(res_pool->dccg->funcs->get_dccg_ref_freq)(res_pool->dccg,
 186					dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency,
 187					&res_pool->ref_clocks.dccg_ref_clock_inKhz);
 188
 189			current_dchub_ref_freq = res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000;
 190
 191			(res_pool->hubbub->funcs->get_dchub_ref_freq)(res_pool->hubbub,
 192					res_pool->ref_clocks.dccg_ref_clock_inKhz,
 193					&res_pool->ref_clocks.dchub_ref_clock_inKhz);
 194		} else {
 195			// Not all ASICs have DCCG sw component
 196			res_pool->ref_clocks.dccg_ref_clock_inKhz =
 197					res_pool->ref_clocks.xtalin_clock_inKhz;
 198			res_pool->ref_clocks.dchub_ref_clock_inKhz =
 199					res_pool->ref_clocks.xtalin_clock_inKhz;
 200		}
 201	} else
 202		ASSERT_CRITICAL(false);
 203
 204	for (i = 0; i < dc->link_count; i++) {
 205		/* Power up AND update implementation according to the
 206		 * required signal (which may be different from the
 207		 * default signal on connector).
 208		 */
 209		struct dc_link *link = dc->links[i];
 210
 211		if (link->ep_type != DISPLAY_ENDPOINT_PHY)
 212			continue;
 213
 214		link->link_enc->funcs->hw_init(link->link_enc);
 215
 216		/* Check for enabled DIG to identify enabled display */
 217		if (link->link_enc->funcs->is_dig_enabled &&
 218			link->link_enc->funcs->is_dig_enabled(link->link_enc)) {
 219			link->link_status.link_active = true;
 220			link->phy_state.symclk_state = SYMCLK_ON_TX_ON;
 221			if (link->link_enc->funcs->fec_is_active &&
 222					link->link_enc->funcs->fec_is_active(link->link_enc))
 223				link->fec_state = dc_link_fec_enabled;
 224		}
 225	}
 226
 227	/* enable_power_gating_plane before dsc_pg_control because
 228	 * FORCEON = 1 with hw default value on bootup, resume from s3
 229	 */
 230	if (hws->funcs.enable_power_gating_plane)
 231		hws->funcs.enable_power_gating_plane(dc->hwseq, true);
 232
 233	/* we want to turn off all dp displays before doing detection */
 234	dc->link_srv->blank_all_dp_displays(dc);
 235
 236	/* If taking control over from VBIOS, we may want to optimize our first
 237	 * mode set, so we need to skip powering down pipes until we know which
 238	 * pipes we want to use.
 239	 * Otherwise, if taking control is not possible, we need to power
 240	 * everything down.
 241	 */
 242	if (dcb->funcs->is_accelerated_mode(dcb) || !dc->config.seamless_boot_edp_requested) {
 243		/* Disable boot optimizations means power down everything including PHY, DIG,
 244		 * and OTG (i.e. the boot is not optimized because we do a full power down).
 245		 */
 246		if (dc->hwss.enable_accelerated_mode && dc->debug.disable_boot_optimizations)
 247			dc->hwss.enable_accelerated_mode(dc, dc->current_state);
 248		else
 249			hws->funcs.init_pipes(dc, dc->current_state);
 250
 251		if (dc->res_pool->hubbub->funcs->allow_self_refresh_control)
 252			dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub,
 253					!dc->res_pool->hubbub->ctx->dc->debug.disable_stutter);
 254
 255		dcn401_initialize_min_clocks(dc);
 256
 257		/* On HW init, allow idle optimizations after pipes have been turned off.
 258		 *
 259		 * In certain D3 cases (i.e. BOCO / BOMACO) it's possible that hardware state
 260		 * is reset (i.e. not in idle at the time hw init is called), but software state
 261		 * still has idle_optimizations = true, so we must disable idle optimizations first
 262		 * (i.e. set false), then re-enable (set true).
 263		 */
 264		dc_allow_idle_optimizations(dc, false);
 265		dc_allow_idle_optimizations(dc, true);
 266	}
 267
 268	/* In headless boot cases, DIG may be turned
 269	 * on which causes HW/SW discrepancies.
 270	 * To avoid this, power down hardware on boot
 271	 * if DIG is turned on and seamless boot not enabled
 272	 */
 273	if (!dc->config.seamless_boot_edp_requested) {
 274		struct dc_link *edp_links[MAX_NUM_EDP];
 275		struct dc_link *edp_link;
 276
 277		dc_get_edp_links(dc, edp_links, &edp_num);
 278		if (edp_num) {
 279			for (i = 0; i < edp_num; i++) {
 280				edp_link = edp_links[i];
 281				if (edp_link->link_enc->funcs->is_dig_enabled &&
 282						edp_link->link_enc->funcs->is_dig_enabled(edp_link->link_enc) &&
 283						dc->hwss.edp_backlight_control &&
 284						hws->funcs.power_down &&
 285						dc->hwss.edp_power_control) {
 286					dc->hwss.edp_backlight_control(edp_link, false);
 287					hws->funcs.power_down(dc);
 288					dc->hwss.edp_power_control(edp_link, false);
 289				}
 290			}
 291		} else {
 292			for (i = 0; i < dc->link_count; i++) {
 293				struct dc_link *link = dc->links[i];
 294
 295				if (link->ep_type != DISPLAY_ENDPOINT_PHY)
 296					continue;
 297				if (link->link_enc->funcs->is_dig_enabled &&
 298						link->link_enc->funcs->is_dig_enabled(link->link_enc) &&
 299						hws->funcs.power_down) {
 300					hws->funcs.power_down(dc);
 301					break;
 302				}
 303
 304			}
 305		}
 306	}
 307	for (i = 0; i < res_pool->audio_count; i++) {
 308		struct audio *audio = res_pool->audios[i];
 309
 310		audio->funcs->hw_init(audio);
 311	}
 312
 313	for (i = 0; i < dc->link_count; i++) {
 314		struct dc_link *link = dc->links[i];
 315
 316		if (link->panel_cntl) {
 317			backlight = link->panel_cntl->funcs->hw_init(link->panel_cntl);
 318			user_level = link->panel_cntl->stored_backlight_registers.USER_LEVEL;
 319		}
 320	}
 321
 322	for (i = 0; i < dc->res_pool->pipe_count; i++) {
 323		if (abms[i] != NULL && abms[i]->funcs != NULL)
 324			abms[i]->funcs->abm_init(abms[i], backlight, user_level);
 325	}
 326
 327	/* power AFMT HDMI memory TODO: may move to dis/en output save power*/
 328	REG_WRITE(DIO_MEM_PWR_CTRL, 0);
 329
 330	if (!dc->debug.disable_clock_gate) {
 331		/* enable all DCN clock gating */
 332		REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0);
 333
 334		REG_WRITE(DCCG_GATE_DISABLE_CNTL2, 0);
 335
 336		REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0);
 337	}
 338
 339	dcn401_setup_hpo_hw_control(hws, true);
 340
 341	if (!dcb->funcs->is_accelerated_mode(dcb) && dc->res_pool->hubbub->funcs->init_watermarks)
 342		dc->res_pool->hubbub->funcs->init_watermarks(dc->res_pool->hubbub);
 343
 344	if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->notify_wm_ranges)
 345		dc->clk_mgr->funcs->notify_wm_ranges(dc->clk_mgr);
 346
 347	if (dc->res_pool->hubbub->funcs->force_pstate_change_control)
 348		dc->res_pool->hubbub->funcs->force_pstate_change_control(
 349				dc->res_pool->hubbub, false, false);
 350
 351	if (dc->res_pool->hubbub->funcs->init_crb)
 352		dc->res_pool->hubbub->funcs->init_crb(dc->res_pool->hubbub);
 353
 354	if (dc->res_pool->hubbub->funcs->set_request_limit && dc->config.sdpif_request_limit_words_per_umc > 0)
 355		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);
 356
 357	// Get DMCUB capabilities
 358	if (dc->ctx->dmub_srv) {
 359		dc_dmub_srv_query_caps_cmd(dc->ctx->dmub_srv);
 360		dc->caps.dmub_caps.psr = dc->ctx->dmub_srv->dmub->feature_caps.psr;
 361		dc->caps.dmub_caps.mclk_sw = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver > 0;
 362		dc->caps.dmub_caps.fams_ver = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver;
 363
 364		/* sw and fw FAMS versions must match for support */
 365		dc->debug.fams2_config.bits.enable &=
 366			dc->caps.dmub_caps.fams_ver == dc->debug.fams_version.ver;
 367		dchub_ref_freq_changed =
 368			res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000 != current_dchub_ref_freq;
 369		if ((!dc->debug.fams2_config.bits.enable || dchub_ref_freq_changed) &&
 370		    dc->res_pool->funcs->update_bw_bounding_box &&
 371		    dc->clk_mgr && dc->clk_mgr->bw_params) {
 372			/* update bounding box if FAMS2 disabled, or if dchub clk has changed */
 373			dc->res_pool->funcs->update_bw_bounding_box(dc,
 374								    dc->clk_mgr->bw_params);
 375		}
 376	}
 377}
 378
 379static void dcn401_get_mcm_lut_xable_from_pipe_ctx(struct dc *dc, struct pipe_ctx *pipe_ctx,
 380		enum MCM_LUT_XABLE *shaper_xable,
 381		enum MCM_LUT_XABLE *lut3d_xable,
 382		enum MCM_LUT_XABLE *lut1d_xable)
 383{
 384	enum dc_cm2_shaper_3dlut_setting shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL;
 385	bool lut1d_enable = false;
 386	struct mpc *mpc = dc->res_pool->mpc;
 387	int mpcc_id = pipe_ctx->plane_res.hubp->inst;
 388
 389	if (!pipe_ctx->plane_state)
 390		return;
 391	shaper_3dlut_setting = pipe_ctx->plane_state->mcm_shaper_3dlut_setting;
 392	lut1d_enable = pipe_ctx->plane_state->mcm_lut1d_enable;
 393	mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id);
 394	pipe_ctx->plane_state->mcm_location = MPCC_MOVABLE_CM_LOCATION_BEFORE;
 395
 396	*lut1d_xable = lut1d_enable ? MCM_LUT_ENABLE : MCM_LUT_DISABLE;
 397
 398	switch (shaper_3dlut_setting) {
 399	case DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL:
 400		*lut3d_xable = *shaper_xable = MCM_LUT_DISABLE;
 401		break;
 402	case DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER:
 403		*lut3d_xable = MCM_LUT_DISABLE;
 404		*shaper_xable = MCM_LUT_ENABLE;
 405		break;
 406	case DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT:
 407		*lut3d_xable = *shaper_xable = MCM_LUT_ENABLE;
 408		break;
 409	}
 410}
 411
 412void dcn401_populate_mcm_luts(struct dc *dc,
 413		struct pipe_ctx *pipe_ctx,
 414		struct dc_cm2_func_luts mcm_luts,
 415		bool lut_bank_a)
 416{
 417	struct dpp *dpp_base = pipe_ctx->plane_res.dpp;
 418	struct hubp *hubp = pipe_ctx->plane_res.hubp;
 419	int mpcc_id = hubp->inst;
 420	struct mpc *mpc = dc->res_pool->mpc;
 421	union mcm_lut_params m_lut_params;
 422	enum dc_cm2_transfer_func_source lut3d_src = mcm_luts.lut3d_data.lut3d_src;
 423	enum hubp_3dlut_fl_format format = 0;
 424	enum hubp_3dlut_fl_mode mode;
 425	enum hubp_3dlut_fl_width width = 0;
 426	enum hubp_3dlut_fl_addressing_mode addr_mode;
 427	enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g = 0;
 428	enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b = 0;
 429	enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cr_r = 0;
 430	enum MCM_LUT_XABLE shaper_xable = MCM_LUT_DISABLE;
 431	enum MCM_LUT_XABLE lut3d_xable = MCM_LUT_DISABLE;
 432	enum MCM_LUT_XABLE lut1d_xable = MCM_LUT_DISABLE;
 433	bool rval;
 434
 435	dcn401_get_mcm_lut_xable_from_pipe_ctx(dc, pipe_ctx, &shaper_xable, &lut3d_xable, &lut1d_xable);
 436
 437	/* 1D LUT */
 438	if (mcm_luts.lut1d_func) {
 439		memset(&m_lut_params, 0, sizeof(m_lut_params));
 440		if (mcm_luts.lut1d_func->type == TF_TYPE_HWPWL)
 441			m_lut_params.pwl = &mcm_luts.lut1d_func->pwl;
 442		else if (mcm_luts.lut1d_func->type == TF_TYPE_DISTRIBUTED_POINTS) {
 443			rval = cm3_helper_translate_curve_to_hw_format(mpc->ctx,
 444					mcm_luts.lut1d_func,
 445					&dpp_base->regamma_params, false);
 446			m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL;
 447		}
 448		if (m_lut_params.pwl) {
 449			if (mpc->funcs->populate_lut)
 450				mpc->funcs->populate_lut(mpc, MCM_LUT_1DLUT, m_lut_params, lut_bank_a, mpcc_id);
 451		}
 452		if (mpc->funcs->program_lut_mode)
 453			mpc->funcs->program_lut_mode(mpc, MCM_LUT_1DLUT, lut1d_xable && m_lut_params.pwl, lut_bank_a, mpcc_id);
 454	}
 455
 456	/* Shaper */
 457	if (mcm_luts.shaper && mcm_luts.lut3d_data.mpc_3dlut_enable) {
 458		memset(&m_lut_params, 0, sizeof(m_lut_params));
 459		if (mcm_luts.shaper->type == TF_TYPE_HWPWL)
 460			m_lut_params.pwl = &mcm_luts.shaper->pwl;
 461		else if (mcm_luts.shaper->type == TF_TYPE_DISTRIBUTED_POINTS) {
 462			ASSERT(false);
 463			rval = cm3_helper_translate_curve_to_hw_format(mpc->ctx,
 464					mcm_luts.shaper,
 465					&dpp_base->regamma_params, true);
 466			m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL;
 467		}
 468		if (m_lut_params.pwl) {
 469			if (mpc->funcs->mcm.populate_lut)
 470				mpc->funcs->mcm.populate_lut(mpc, m_lut_params, lut_bank_a, mpcc_id);
 471			if (mpc->funcs->program_lut_mode)
 472				mpc->funcs->program_lut_mode(mpc, MCM_LUT_SHAPER, MCM_LUT_ENABLE, lut_bank_a, mpcc_id);
 473		}
 474	}
 475
 476	/* 3DLUT */
 477	switch (lut3d_src) {
 478	case DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM:
 479		memset(&m_lut_params, 0, sizeof(m_lut_params));
 480		if (hubp->funcs->hubp_enable_3dlut_fl)
 481			hubp->funcs->hubp_enable_3dlut_fl(hubp, false);
 482
 483		if (mcm_luts.lut3d_data.lut3d_func && mcm_luts.lut3d_data.lut3d_func->state.bits.initialized) {
 484			m_lut_params.lut3d = &mcm_luts.lut3d_data.lut3d_func->lut_3d;
 485			if (mpc->funcs->populate_lut)
 486				mpc->funcs->populate_lut(mpc, MCM_LUT_3DLUT, m_lut_params, lut_bank_a, mpcc_id);
 487			if (mpc->funcs->program_lut_mode)
 488				mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut3d_xable, lut_bank_a,
 489						mpcc_id);
 490		}
 491		break;
 492		case DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM:
 493		switch (mcm_luts.lut3d_data.gpu_mem_params.size) {
 494		case DC_CM2_GPU_MEM_SIZE_171717:
 495			width = hubp_3dlut_fl_width_17;
 496			break;
 497		case DC_CM2_GPU_MEM_SIZE_TRANSFORMED:
 498			width = hubp_3dlut_fl_width_transformed;
 499			break;
 500		default:
 501			//TODO: handle default case
 502			break;
 503		}
 504
 505		//check for support
 506		if (mpc->funcs->mcm.is_config_supported &&
 507			!mpc->funcs->mcm.is_config_supported(width))
 508			break;
 509
 510		if (mpc->funcs->program_lut_read_write_control)
 511			mpc->funcs->program_lut_read_write_control(mpc, MCM_LUT_3DLUT, lut_bank_a, mpcc_id);
 512		if (mpc->funcs->program_lut_mode)
 513			mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut3d_xable, lut_bank_a, mpcc_id);
 514
 515		if (hubp->funcs->hubp_program_3dlut_fl_addr)
 516			hubp->funcs->hubp_program_3dlut_fl_addr(hubp, mcm_luts.lut3d_data.gpu_mem_params.addr);
 517
 518		if (mpc->funcs->mcm.program_bit_depth)
 519			mpc->funcs->mcm.program_bit_depth(mpc, mcm_luts.lut3d_data.gpu_mem_params.bit_depth, mpcc_id);
 520
 521		switch (mcm_luts.lut3d_data.gpu_mem_params.layout) {
 522		case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_RGB:
 523			mode = hubp_3dlut_fl_mode_native_1;
 524			addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear;
 525			break;
 526		case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_BGR:
 527			mode = hubp_3dlut_fl_mode_native_2;
 528			addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear;
 529			break;
 530		case DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR:
 531			mode = hubp_3dlut_fl_mode_transform;
 532			addr_mode = hubp_3dlut_fl_addressing_mode_simple_linear;
 533			break;
 534		default:
 535			mode = hubp_3dlut_fl_mode_disable;
 536			addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear;
 537			break;
 538		}
 539		if (hubp->funcs->hubp_program_3dlut_fl_mode)
 540			hubp->funcs->hubp_program_3dlut_fl_mode(hubp, mode);
 541
 542		if (hubp->funcs->hubp_program_3dlut_fl_addressing_mode)
 543			hubp->funcs->hubp_program_3dlut_fl_addressing_mode(hubp, addr_mode);
 544
 545		switch (mcm_luts.lut3d_data.gpu_mem_params.format_params.format) {
 546		case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12MSB:
 547			format = hubp_3dlut_fl_format_unorm_12msb_bitslice;
 548			break;
 549		case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12LSB:
 550			format = hubp_3dlut_fl_format_unorm_12lsb_bitslice;
 551			break;
 552		case DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10:
 553			format = hubp_3dlut_fl_format_float_fp1_5_10;
 554			break;
 555		}
 556		if (hubp->funcs->hubp_program_3dlut_fl_format)
 557			hubp->funcs->hubp_program_3dlut_fl_format(hubp, format);
 558		if (hubp->funcs->hubp_update_3dlut_fl_bias_scale &&
 559				mpc->funcs->mcm.program_bias_scale) {
 560			mpc->funcs->mcm.program_bias_scale(mpc,
 561				mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias,
 562				mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale,
 563				mpcc_id);
 564			hubp->funcs->hubp_update_3dlut_fl_bias_scale(hubp,
 565						mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias,
 566						mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale);
 567		}
 568
 569		//navi 4x has a bug and r and blue are swapped and need to be worked around here in
 570		//TODO: need to make a method for get_xbar per asic OR do the workaround in program_crossbar for 4x
 571		switch (mcm_luts.lut3d_data.gpu_mem_params.component_order) {
 572		case DC_CM2_GPU_MEM_PIXEL_COMPONENT_ORDER_RGBA:
 573		default:
 574			crossbar_bit_slice_cr_r = hubp_3dlut_fl_crossbar_bit_slice_0_15;
 575			crossbar_bit_slice_y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31;
 576			crossbar_bit_slice_cb_b = hubp_3dlut_fl_crossbar_bit_slice_32_47;
 577			break;
 578		}
 579
 580		if (hubp->funcs->hubp_program_3dlut_fl_crossbar)
 581			hubp->funcs->hubp_program_3dlut_fl_crossbar(hubp,
 582					crossbar_bit_slice_cr_r,
 583					crossbar_bit_slice_y_g,
 584					crossbar_bit_slice_cb_b);
 585
 586		if (mpc->funcs->mcm.program_lut_read_write_control)
 587			mpc->funcs->mcm.program_lut_read_write_control(mpc, MCM_LUT_3DLUT, lut_bank_a, true, mpcc_id);
 588
 589		if (mpc->funcs->mcm.program_3dlut_size)
 590			mpc->funcs->mcm.program_3dlut_size(mpc, width, mpcc_id);
 591
 592		if (mpc->funcs->update_3dlut_fast_load_select)
 593			mpc->funcs->update_3dlut_fast_load_select(mpc, mpcc_id, hubp->inst);
 594
 595		if (hubp->funcs->hubp_enable_3dlut_fl)
 596			hubp->funcs->hubp_enable_3dlut_fl(hubp, true);
 597		else {
 598			if (mpc->funcs->program_lut_mode) {
 599				mpc->funcs->program_lut_mode(mpc, MCM_LUT_SHAPER, MCM_LUT_DISABLE, lut_bank_a, mpcc_id);
 600				mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, MCM_LUT_DISABLE, lut_bank_a, mpcc_id);
 601				mpc->funcs->program_lut_mode(mpc, MCM_LUT_1DLUT, MCM_LUT_DISABLE, lut_bank_a, mpcc_id);
 602			}
 603		}
 604		break;
 605
 606	}
 607}
 608
 609void dcn401_trigger_3dlut_dma_load(struct dc *dc, struct pipe_ctx *pipe_ctx)
 610{
 611	struct hubp *hubp = pipe_ctx->plane_res.hubp;
 612
 613	if (hubp->funcs->hubp_enable_3dlut_fl) {
 614		hubp->funcs->hubp_enable_3dlut_fl(hubp, true);
 615	}
 616}
 617
 618bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx,
 619				const struct dc_plane_state *plane_state)
 620{
 621	struct dpp *dpp_base = pipe_ctx->plane_res.dpp;
 622	int mpcc_id = pipe_ctx->plane_res.hubp->inst;
 623	struct dc *dc = pipe_ctx->stream_res.opp->ctx->dc;
 624	struct mpc *mpc = dc->res_pool->mpc;
 625	bool result;
 626	const struct pwl_params *lut_params = NULL;
 627	bool rval;
 628
 629	if (plane_state->mcm_luts.lut3d_data.lut3d_src == DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM) {
 630		dcn401_populate_mcm_luts(dc, pipe_ctx, plane_state->mcm_luts, plane_state->lut_bank_a);
 631		return true;
 632	}
 633
 634	mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id);
 635	pipe_ctx->plane_state->mcm_location = MPCC_MOVABLE_CM_LOCATION_BEFORE;
 636	// 1D LUT
 637	if (plane_state->blend_tf.type == TF_TYPE_HWPWL)
 638		lut_params = &plane_state->blend_tf.pwl;
 639	else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) {
 640		rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx,
 641							       &plane_state->blend_tf,
 642							       &dpp_base->regamma_params, false);
 643		lut_params = rval ? &dpp_base->regamma_params : NULL;
 644	}
 645	result = mpc->funcs->program_1dlut(mpc, lut_params, mpcc_id);
 646	lut_params = NULL;
 647
 648	// Shaper
 649	if (plane_state->in_shaper_func.type == TF_TYPE_HWPWL)
 650		lut_params = &plane_state->in_shaper_func.pwl;
 651	else if (plane_state->in_shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
 652		// TODO: dpp_base replace
 653		rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx,
 654							       &plane_state->in_shaper_func,
 655							       &dpp_base->shaper_params, true);
 656		lut_params = rval ? &dpp_base->shaper_params : NULL;
 657	}
 658	result &= mpc->funcs->program_shaper(mpc, lut_params, mpcc_id);
 659
 660	// 3D
 661	if (mpc->funcs->program_3dlut) {
 662		if (plane_state->lut3d_func.state.bits.initialized == 1)
 663			result &= mpc->funcs->program_3dlut(mpc, &plane_state->lut3d_func.lut_3d, mpcc_id);
 664		else
 665			result &= mpc->funcs->program_3dlut(mpc, NULL, mpcc_id);
 666	}
 667
 668	return result;
 669}
 670
 671bool dcn401_set_output_transfer_func(struct dc *dc,
 672				struct pipe_ctx *pipe_ctx,
 673				const struct dc_stream_state *stream)
 674{
 675	int mpcc_id = pipe_ctx->plane_res.hubp->inst;
 676	struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc;
 677	const struct pwl_params *params = NULL;
 678	bool ret = false;
 679
 680	/* program OGAM or 3DLUT only for the top pipe*/
 681	if (resource_is_pipe_type(pipe_ctx, OPP_HEAD)) {
 682		/*program shaper and 3dlut in MPC*/
 683		ret = dcn32_set_mpc_shaper_3dlut(pipe_ctx, stream);
 684		if (ret == false && mpc->funcs->set_output_gamma) {
 685			if (stream->out_transfer_func.type == TF_TYPE_HWPWL)
 686				params = &stream->out_transfer_func.pwl;
 687			else if (pipe_ctx->stream->out_transfer_func.type ==
 688					TF_TYPE_DISTRIBUTED_POINTS &&
 689					cm3_helper_translate_curve_to_hw_format(stream->ctx,
 690					&stream->out_transfer_func,
 691					&mpc->blender_params, false))
 692				params = &mpc->blender_params;
 693			/* there are no ROM LUTs in OUTGAM */
 694			if (stream->out_transfer_func.type == TF_TYPE_PREDEFINED)
 695				BREAK_TO_DEBUGGER();
 696		}
 697	}
 698
 699	if (mpc->funcs->set_output_gamma)
 700		mpc->funcs->set_output_gamma(mpc, mpcc_id, params);
 701
 702	return ret;
 703}
 704
 705void dcn401_calculate_dccg_tmds_div_value(struct pipe_ctx *pipe_ctx,
 706				unsigned int *tmds_div)
 707{
 708	struct dc_stream_state *stream = pipe_ctx->stream;
 709
 710	if (dc_is_tmds_signal(stream->signal) || dc_is_virtual_signal(stream->signal)) {
 711		if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420)
 712			*tmds_div = PIXEL_RATE_DIV_BY_2;
 713		else
 714			*tmds_div = PIXEL_RATE_DIV_BY_4;
 715	} else {
 716		*tmds_div = PIXEL_RATE_DIV_BY_1;
 717	}
 718
 719	if (*tmds_div == PIXEL_RATE_DIV_NA)
 720		ASSERT(false);
 721
 722}
 723
 724static void enable_stream_timing_calc(
 725		struct pipe_ctx *pipe_ctx,
 726		struct dc_state *context,
 727		struct dc *dc,
 728		unsigned int *tmds_div,
 729		int *opp_inst,
 730		int *opp_cnt,
 731		struct pipe_ctx *opp_heads[MAX_PIPES],
 732		bool *manual_mode,
 733		struct drr_params *params,
 734		unsigned int *event_triggers)
 735{
 736	struct dc_stream_state *stream = pipe_ctx->stream;
 737	int i;
 738
 739	if (dc_is_tmds_signal(stream->signal) || dc_is_virtual_signal(stream->signal))
 740		dcn401_calculate_dccg_tmds_div_value(pipe_ctx, tmds_div);
 741
 742	*opp_cnt = resource_get_opp_heads_for_otg_master(pipe_ctx, &context->res_ctx, opp_heads);
 743	for (i = 0; i < *opp_cnt; i++)
 744		opp_inst[i] = opp_heads[i]->stream_res.opp->inst;
 745
 746	if (dc_is_tmds_signal(stream->signal)) {
 747		stream->link->phy_state.symclk_ref_cnts.otg = 1;
 748		if (stream->link->phy_state.symclk_state == SYMCLK_OFF_TX_OFF)
 749			stream->link->phy_state.symclk_state = SYMCLK_ON_TX_OFF;
 750		else
 751			stream->link->phy_state.symclk_state = SYMCLK_ON_TX_ON;
 752	}
 753
 754	params->vertical_total_min = stream->adjust.v_total_min;
 755	params->vertical_total_max = stream->adjust.v_total_max;
 756	params->vertical_total_mid = stream->adjust.v_total_mid;
 757	params->vertical_total_mid_frame_num = stream->adjust.v_total_mid_frame_num;
 758
 759	// DRR should set trigger event to monitor surface update event
 760	if (stream->adjust.v_total_min != 0 && stream->adjust.v_total_max != 0)
 761		*event_triggers = 0x80;
 762}
 763
 764enum dc_status dcn401_enable_stream_timing(
 765		struct pipe_ctx *pipe_ctx,
 766		struct dc_state *context,
 767		struct dc *dc)
 768{
 769	struct dce_hwseq *hws = dc->hwseq;
 770	struct dc_stream_state *stream = pipe_ctx->stream;
 771	struct drr_params params = {0};
 772	unsigned int event_triggers = 0;
 773	int opp_cnt = 1;
 774	int opp_inst[MAX_PIPES] = {0};
 775	struct pipe_ctx *opp_heads[MAX_PIPES] = {0};
 776	struct dc_crtc_timing patched_crtc_timing = stream->timing;
 777	bool manual_mode = false;
 778	unsigned int tmds_div = PIXEL_RATE_DIV_NA;
 779	unsigned int unused_div = PIXEL_RATE_DIV_NA;
 780	int odm_slice_width;
 781	int last_odm_slice_width;
 782	int i;
 783
 784	if (!resource_is_pipe_type(pipe_ctx, OTG_MASTER))
 785		return DC_OK;
 786
 787	enable_stream_timing_calc(pipe_ctx, context, dc, &tmds_div, opp_inst,
 788			&opp_cnt, opp_heads, &manual_mode, &params, &event_triggers);
 789
 790	if (dc->res_pool->dccg->funcs->set_pixel_rate_div) {
 791		dc->res_pool->dccg->funcs->set_pixel_rate_div(
 792			dc->res_pool->dccg, pipe_ctx->stream_res.tg->inst,
 793			tmds_div, unused_div);
 794	}
 795
 796	/* TODO check if timing_changed, disable stream if timing changed */
 797
 798	if (opp_cnt > 1) {
 799		odm_slice_width = resource_get_odm_slice_dst_width(pipe_ctx, false);
 800		last_odm_slice_width = resource_get_odm_slice_dst_width(pipe_ctx, true);
 801		pipe_ctx->stream_res.tg->funcs->set_odm_combine(
 802				pipe_ctx->stream_res.tg,
 803				opp_inst, opp_cnt,
 804				odm_slice_width, last_odm_slice_width);
 805	}
 806
 807	/* set DTBCLK_P */
 808	if (dc->res_pool->dccg->funcs->set_dtbclk_p_src) {
 809		if (dc_is_dp_signal(stream->signal) || dc_is_virtual_signal(stream->signal)) {
 810			dc->res_pool->dccg->funcs->set_dtbclk_p_src(dc->res_pool->dccg, DPREFCLK, pipe_ctx->stream_res.tg->inst);
 811		}
 812	}
 813
 814	/* HW program guide assume display already disable
 815	 * by unplug sequence. OTG assume stop.
 816	 */
 817	pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, true);
 818
 819	if (false == pipe_ctx->clock_source->funcs->program_pix_clk(
 820			pipe_ctx->clock_source,
 821			&pipe_ctx->stream_res.pix_clk_params,
 822			dc->link_srv->dp_get_encoding_format(&pipe_ctx->link_config.dp_link_settings),
 823			&pipe_ctx->pll_settings)) {
 824		BREAK_TO_DEBUGGER();
 825		return DC_ERROR_UNEXPECTED;
 826	}
 827
 828	if (dc->hwseq->funcs.PLAT_58856_wa && (!dc_is_dp_signal(stream->signal)))
 829		dc->hwseq->funcs.PLAT_58856_wa(context, pipe_ctx);
 830
 831	/* if we are padding, h_addressable needs to be adjusted */
 832	if (dc->debug.enable_hblank_borrow) {
 833		patched_crtc_timing.h_addressable = patched_crtc_timing.h_addressable + pipe_ctx->dsc_padding_params.dsc_hactive_padding;
 834		patched_crtc_timing.h_total = patched_crtc_timing.h_total + pipe_ctx->dsc_padding_params.dsc_htotal_padding;
 835		patched_crtc_timing.pix_clk_100hz = pipe_ctx->dsc_padding_params.dsc_pix_clk_100hz;
 836	}
 837
 838	pipe_ctx->stream_res.tg->funcs->program_timing(
 839		pipe_ctx->stream_res.tg,
 840		&patched_crtc_timing,
 841		(unsigned int)pipe_ctx->global_sync.dcn4x.vready_offset_pixels,
 842		(unsigned int)pipe_ctx->global_sync.dcn4x.vstartup_lines,
 843		(unsigned int)pipe_ctx->global_sync.dcn4x.vupdate_offset_pixels,
 844		(unsigned int)pipe_ctx->global_sync.dcn4x.vupdate_vupdate_width_pixels,
 845		(unsigned int)pipe_ctx->global_sync.dcn4x.pstate_keepout_start_lines,
 846		pipe_ctx->stream->signal,
 847		true);
 848
 849	for (i = 0; i < opp_cnt; i++) {
 850		opp_heads[i]->stream_res.opp->funcs->opp_pipe_clock_control(
 851				opp_heads[i]->stream_res.opp,
 852				true);
 853		opp_heads[i]->stream_res.opp->funcs->opp_program_left_edge_extra_pixel(
 854				opp_heads[i]->stream_res.opp,
 855				stream->timing.pixel_encoding,
 856				resource_is_pipe_type(opp_heads[i], OTG_MASTER));
 857	}
 858
 859	pipe_ctx->stream_res.opp->funcs->opp_pipe_clock_control(
 860			pipe_ctx->stream_res.opp,
 861			true);
 862
 863	hws->funcs.blank_pixel_data(dc, pipe_ctx, true);
 864
 865	/* VTG is  within DCHUB command block. DCFCLK is always on */
 866	if (false == pipe_ctx->stream_res.tg->funcs->enable_crtc(pipe_ctx->stream_res.tg)) {
 867		BREAK_TO_DEBUGGER();
 868		return DC_ERROR_UNEXPECTED;
 869	}
 870
 871	hws->funcs.wait_for_blank_complete(pipe_ctx->stream_res.opp);
 872	set_drr_and_clear_adjust_pending(pipe_ctx, stream, &params);
 873
 874	/* Event triggers and num frames initialized for DRR, but can be
 875	 * later updated for PSR use. Note DRR trigger events are generated
 876	 * regardless of whether num frames met.
 877	 */
 878	if (pipe_ctx->stream_res.tg->funcs->set_static_screen_control)
 879		pipe_ctx->stream_res.tg->funcs->set_static_screen_control(
 880				pipe_ctx->stream_res.tg, event_triggers, 2);
 881
 882	/* TODO program crtc source select for non-virtual signal*/
 883	/* TODO program FMT */
 884	/* TODO setup link_enc */
 885	/* TODO set stream attributes */
 886	/* TODO program audio */
 887	/* TODO enable stream if timing changed */
 888	/* TODO unblank stream if DP */
 889
 890	if (dc_state_get_pipe_subvp_type(context, pipe_ctx) == SUBVP_PHANTOM) {
 891		if (pipe_ctx->stream_res.tg->funcs->phantom_crtc_post_enable)
 892			pipe_ctx->stream_res.tg->funcs->phantom_crtc_post_enable(pipe_ctx->stream_res.tg);
 893	}
 894
 895	return DC_OK;
 896}
 897
 898static enum phyd32clk_clock_source get_phyd32clk_src(struct dc_link *link)
 899{
 900	switch (link->link_enc->transmitter) {
 901	case TRANSMITTER_UNIPHY_A:
 902		return PHYD32CLKA;
 903	case TRANSMITTER_UNIPHY_B:
 904		return PHYD32CLKB;
 905	case TRANSMITTER_UNIPHY_C:
 906		return PHYD32CLKC;
 907	case TRANSMITTER_UNIPHY_D:
 908		return PHYD32CLKD;
 909	case TRANSMITTER_UNIPHY_E:
 910		return PHYD32CLKE;
 911	default:
 912		return PHYD32CLKA;
 913	}
 914}
 915
 916static void dcn401_enable_stream_calc(
 917		struct pipe_ctx *pipe_ctx,
 918		int *dp_hpo_inst,
 919		enum phyd32clk_clock_source *phyd32clk,
 920		unsigned int *tmds_div,
 921		uint32_t *early_control)
 922{
 923
 924	struct dc *dc = pipe_ctx->stream->ctx->dc;
 925	struct dc_crtc_timing *timing = &pipe_ctx->stream->timing;
 926	enum dc_lane_count lane_count =
 927			pipe_ctx->stream->link->cur_link_settings.lane_count;
 928	uint32_t active_total_with_borders;
 929
 930	if (dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) {
 931		*dp_hpo_inst = pipe_ctx->stream_res.hpo_dp_stream_enc->inst;
 932		*phyd32clk = get_phyd32clk_src(pipe_ctx->stream->link);
 933	}
 934
 935	if (dc_is_tmds_signal(pipe_ctx->stream->signal))
 936		dcn401_calculate_dccg_tmds_div_value(pipe_ctx, tmds_div);
 937	else
 938		*tmds_div = PIXEL_RATE_DIV_BY_1;
 939
 940	/* enable early control to avoid corruption on DP monitor*/
 941	active_total_with_borders =
 942			timing->h_addressable
 943				+ timing->h_border_left
 944				+ timing->h_border_right;
 945
 946	if (lane_count != 0)
 947		*early_control = active_total_with_borders % lane_count;
 948
 949	if (*early_control == 0)
 950		*early_control = lane_count;
 951
 952}
 953
 954void dcn401_enable_stream(struct pipe_ctx *pipe_ctx)
 955{
 956	uint32_t early_control = 0;
 957	struct timing_generator *tg = pipe_ctx->stream_res.tg;
 958	struct dc_link *link = pipe_ctx->stream->link;
 959	const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res);
 960	struct dc *dc = pipe_ctx->stream->ctx->dc;
 961	struct dccg *dccg = dc->res_pool->dccg;
 962	enum phyd32clk_clock_source phyd32clk;
 963	int dp_hpo_inst = 0;
 964	unsigned int tmds_div = PIXEL_RATE_DIV_NA;
 965	unsigned int unused_div = PIXEL_RATE_DIV_NA;
 966	struct link_encoder *link_enc = pipe_ctx->link_res.dio_link_enc;
 967	struct stream_encoder *stream_enc = pipe_ctx->stream_res.stream_enc;
 968
 969	if (!dc->config.unify_link_enc_assignment)
 970		link_enc = link_enc_cfg_get_link_enc(link);
 971
 972	dcn401_enable_stream_calc(pipe_ctx, &dp_hpo_inst, &phyd32clk,
 973				&tmds_div, &early_control);
 974
 975	if (dc_is_dp_signal(pipe_ctx->stream->signal) || dc_is_virtual_signal(pipe_ctx->stream->signal)) {
 976		if (dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) {
 977			dccg->funcs->set_dpstreamclk(dccg, DPREFCLK, tg->inst, dp_hpo_inst);
 978			if (link->cur_link_settings.link_rate == LINK_RATE_UNKNOWN) {
 979				dccg->funcs->disable_symclk32_se(dccg, dp_hpo_inst);
 980			} else {
 981				dccg->funcs->enable_symclk32_se(dccg, dp_hpo_inst, phyd32clk);
 982			}
 983		} else {
 984			dccg->funcs->enable_symclk_se(dccg, stream_enc->stream_enc_inst,
 985					link_enc->transmitter - TRANSMITTER_UNIPHY_A);
 986		}
 987	}
 988
 989	if (dc->res_pool->dccg->funcs->set_pixel_rate_div) {
 990		dc->res_pool->dccg->funcs->set_pixel_rate_div(
 991			dc->res_pool->dccg,
 992			pipe_ctx->stream_res.tg->inst,
 993			tmds_div,
 994			unused_div);
 995	}
 996
 997	link_hwss->setup_stream_encoder(pipe_ctx);
 998
 999	if (pipe_ctx->plane_state && pipe_ctx->plane_state->flip_immediate != 1) {
1000		if (dc->hwss.program_dmdata_engine)
1001			dc->hwss.program_dmdata_engine(pipe_ctx);
1002	}
1003
1004	dc->hwss.update_info_frame(pipe_ctx);
1005
1006	if (dc_is_dp_signal(pipe_ctx->stream->signal))
1007		dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_UPDATE_INFO_FRAME);
1008
1009	tg->funcs->set_early_control(tg, early_control);
1010}
1011
1012void dcn401_setup_hpo_hw_control(const struct dce_hwseq *hws, bool enable)
1013{
1014	REG_UPDATE(HPO_TOP_HW_CONTROL, HPO_IO_EN, enable);
1015}
1016
1017void adjust_hotspot_between_slices_for_2x_magnify(uint32_t cursor_width, struct dc_cursor_position *pos_cpy)
1018{
1019	if (cursor_width <= 128) {
1020		pos_cpy->x_hotspot /= 2;
1021		pos_cpy->x_hotspot += 1;
1022	} else {
1023		pos_cpy->x_hotspot /= 2;
1024		pos_cpy->x_hotspot += 2;
1025	}
1026}
1027
1028static void disable_link_output_symclk_on_tx_off(struct dc_link *link, enum dp_link_encoding link_encoding)
1029{
1030	struct dc *dc = link->ctx->dc;
1031	struct pipe_ctx *pipe_ctx = NULL;
1032	uint8_t i;
1033
1034	for (i = 0; i < MAX_PIPES; i++) {
1035		pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
1036		if (pipe_ctx->stream && pipe_ctx->stream->link == link && pipe_ctx->top_pipe == NULL) {
1037			pipe_ctx->clock_source->funcs->program_pix_clk(
1038					pipe_ctx->clock_source,
1039					&pipe_ctx->stream_res.pix_clk_params,
1040					link_encoding,
1041					&pipe_ctx->pll_settings);
1042			break;
1043		}
1044	}
1045}
1046
1047void dcn401_disable_link_output(struct dc_link *link,
1048		const struct link_resource *link_res,
1049		enum signal_type signal)
1050{
1051	struct dc *dc = link->ctx->dc;
1052	const struct link_hwss *link_hwss = get_link_hwss(link, link_res);
1053	struct dmcu *dmcu = dc->res_pool->dmcu;
1054
1055	if (signal == SIGNAL_TYPE_EDP &&
1056			link->dc->hwss.edp_backlight_control &&
1057			!link->skip_implict_edp_power_control)
1058		link->dc->hwss.edp_backlight_control(link, false);
1059	else if (dmcu != NULL && dmcu->funcs->lock_phy)
1060		dmcu->funcs->lock_phy(dmcu);
1061
1062	if (dc_is_tmds_signal(signal) && link->phy_state.symclk_ref_cnts.otg > 0) {
1063		disable_link_output_symclk_on_tx_off(link, DP_UNKNOWN_ENCODING);
1064		link->phy_state.symclk_state = SYMCLK_ON_TX_OFF;
1065	} else {
1066		link_hwss->disable_link_output(link, link_res, signal);
1067		link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF;
1068	}
1069
1070	if (signal == SIGNAL_TYPE_EDP &&
1071			link->dc->hwss.edp_backlight_control &&
1072			!link->skip_implict_edp_power_control)
1073		link->dc->hwss.edp_power_control(link, false);
1074	else if (dmcu != NULL && dmcu->funcs->lock_phy)
1075		dmcu->funcs->unlock_phy(dmcu);
1076
1077	dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_DISABLE_LINK_PHY);
1078}
1079
1080void dcn401_set_cursor_position(struct pipe_ctx *pipe_ctx)
1081{
1082	struct dc_cursor_position pos_cpy = pipe_ctx->stream->cursor_position;
1083	struct hubp *hubp = pipe_ctx->plane_res.hubp;
1084	struct dpp *dpp = pipe_ctx->plane_res.dpp;
1085	struct dc_cursor_mi_param param = {
1086		.pixel_clk_khz = pipe_ctx->stream->timing.pix_clk_100hz / 10,
1087		.ref_clk_khz = pipe_ctx->stream->ctx->dc->res_pool->ref_clocks.dchub_ref_clock_inKhz,
1088		.viewport = pipe_ctx->plane_res.scl_data.viewport,
1089		.recout = pipe_ctx->plane_res.scl_data.recout,
1090		.h_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.horz,
1091		.v_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.vert,
1092		.rotation = pipe_ctx->plane_state->rotation,
1093		.mirror = pipe_ctx->plane_state->horizontal_mirror,
1094		.stream = pipe_ctx->stream
1095	};
1096	struct rect odm_slice_src = { 0 };
1097	bool odm_combine_on = (pipe_ctx->next_odm_pipe != NULL) ||
1098		(pipe_ctx->prev_odm_pipe != NULL);
1099	int prev_odm_width = 0;
1100	struct pipe_ctx *prev_odm_pipe = NULL;
1101	bool mpc_combine_on = false;
1102	int  bottom_pipe_x_pos = 0;
1103
1104	int x_pos = pos_cpy.x;
1105	int y_pos = pos_cpy.y;
1106	int recout_x_pos = 0;
1107	int recout_y_pos = 0;
1108
1109	if ((pipe_ctx->top_pipe != NULL) || (pipe_ctx->bottom_pipe != NULL)) {
1110		if ((pipe_ctx->plane_state->src_rect.width != pipe_ctx->plane_res.scl_data.viewport.width) ||
1111			(pipe_ctx->plane_state->src_rect.height != pipe_ctx->plane_res.scl_data.viewport.height)) {
1112			mpc_combine_on = true;
1113		}
1114	}
1115
1116	/* DCN4 moved cursor composition after Scaler, so in HW it is in
1117	 * recout space and for HW Cursor position programming need to
1118	 * translate to recout space.
1119	 *
1120	 * Cursor X and Y position programmed into HW can't be negative,
1121	 * in fact it is X, Y coordinate shifted for the HW Cursor Hot spot
1122	 * position that goes into HW X and Y coordinates while HW Hot spot
1123	 * X and Y coordinates are length relative to the cursor top left
1124	 * corner, hotspot must be smaller than the cursor size.
1125	 *
1126	 * DMs/DC interface for Cursor position is in stream->src space, and
1127	 * DMs supposed to transform Cursor coordinates to stream->src space,
1128	 * then here we need to translate Cursor coordinates to stream->dst
1129	 * space, as now in HW, Cursor coordinates are in per pipe recout
1130	 * space, and for the given pipe valid coordinates are only in range
1131	 * from 0,0 - recout width, recout height space.
1132	 * If certain pipe combining is in place, need to further adjust per
1133	 * pipe to make sure each pipe enabling cursor on its part of the
1134	 * screen.
1135	 */
1136	x_pos = pipe_ctx->stream->dst.x + x_pos * pipe_ctx->stream->dst.width /
1137		pipe_ctx->stream->src.width;
1138	y_pos = pipe_ctx->stream->dst.y + y_pos * pipe_ctx->stream->dst.height /
1139		pipe_ctx->stream->src.height;
1140
1141	/* If the cursor's source viewport is clipped then we need to
1142	 * translate the cursor to appear in the correct position on
1143	 * the screen.
1144	 *
1145	 * This translation isn't affected by scaling so it needs to be
1146	 * done *after* we adjust the position for the scale factor.
1147	 *
1148	 * This is only done by opt-in for now since there are still
1149	 * some usecases like tiled display that might enable the
1150	 * cursor on both streams while expecting dc to clip it.
1151	 */
1152	if (pos_cpy.translate_by_source) {
1153		x_pos += pipe_ctx->plane_state->src_rect.x;
1154		y_pos += pipe_ctx->plane_state->src_rect.y;
1155	}
1156
1157	/* Adjust for ODM Combine
1158	 * next/prev_odm_offset is to account for scaled modes that have underscan
1159	 */
1160	if (odm_combine_on) {
1161		prev_odm_pipe = pipe_ctx->prev_odm_pipe;
1162
1163		while (prev_odm_pipe != NULL) {
1164			odm_slice_src = resource_get_odm_slice_src_rect(prev_odm_pipe);
1165			prev_odm_width += odm_slice_src.width;
1166			prev_odm_pipe = prev_odm_pipe->prev_odm_pipe;
1167		}
1168
1169		x_pos -= (prev_odm_width);
1170	}
1171
1172	/* If the position is negative then we need to add to the hotspot
1173	 * to fix cursor size between ODM slices
1174	 */
1175
1176	if (x_pos < 0) {
1177		pos_cpy.x_hotspot -= x_pos;
1178		if (hubp->curs_attr.attribute_flags.bits.ENABLE_MAGNIFICATION)
1179			adjust_hotspot_between_slices_for_2x_magnify(hubp->curs_attr.width, &pos_cpy);
1180		x_pos = 0;
1181	}
1182
1183	if (y_pos < 0) {
1184		pos_cpy.y_hotspot -= y_pos;
1185		y_pos = 0;
1186	}
1187
1188	/* If the position on bottom MPC pipe is negative then we need to add to the hotspot and
1189	 * adjust x_pos on bottom pipe to make cursor visible when crossing between MPC slices.
1190	 */
1191	if (mpc_combine_on &&
1192		pipe_ctx->top_pipe &&
1193		(pipe_ctx == pipe_ctx->top_pipe->bottom_pipe)) {
1194
1195		bottom_pipe_x_pos = x_pos - pipe_ctx->plane_res.scl_data.recout.x;
1196		if (bottom_pipe_x_pos < 0) {
1197			x_pos = pipe_ctx->plane_res.scl_data.recout.x;
1198			pos_cpy.x_hotspot -= bottom_pipe_x_pos;
1199			if (hubp->curs_attr.attribute_flags.bits.ENABLE_MAGNIFICATION)
1200				adjust_hotspot_between_slices_for_2x_magnify(hubp->curs_attr.width, &pos_cpy);
1201		}
1202	}
1203
1204	pos_cpy.x = (uint32_t)x_pos;
1205	pos_cpy.y = (uint32_t)y_pos;
1206
1207	if (pos_cpy.enable && resource_can_pipe_disable_cursor(pipe_ctx))
1208		pos_cpy.enable = false;
1209
1210	x_pos = pos_cpy.x - param.recout.x;
1211	y_pos = pos_cpy.y - param.recout.y;
1212
1213	recout_x_pos = x_pos - pos_cpy.x_hotspot;
1214	recout_y_pos = y_pos - pos_cpy.y_hotspot;
1215
1216	if (recout_x_pos >= (int)param.recout.width)
1217		pos_cpy.enable = false;  /* not visible beyond right edge*/
1218
1219	if (recout_y_pos >= (int)param.recout.height)
1220		pos_cpy.enable = false;  /* not visible beyond bottom edge*/
1221
1222	if (recout_x_pos + (int)hubp->curs_attr.width <= 0)
1223		pos_cpy.enable = false;  /* not visible beyond left edge*/
1224
1225	if (recout_y_pos + (int)hubp->curs_attr.height <= 0)
1226		pos_cpy.enable = false;  /* not visible beyond top edge*/
1227
1228	pos_cpy.x = x_pos;
1229	pos_cpy.y = y_pos;
1230
1231	hubp->funcs->set_cursor_position(hubp, &pos_cpy, &param);
1232	dpp->funcs->set_cursor_position(dpp, &pos_cpy, &param, hubp->curs_attr.width, hubp->curs_attr.height);
1233}
1234
1235static bool dcn401_check_no_memory_request_for_cab(struct dc *dc)
1236{
1237	int i;
1238
1239	/* First, check no-memory-request case */
1240	for (i = 0; i < dc->current_state->stream_count; i++) {
1241		if ((dc->current_state->stream_status[i].plane_count) &&
1242			(dc->current_state->streams[i]->link->psr_settings.psr_version == DC_PSR_VERSION_UNSUPPORTED))
1243			/* Fail eligibility on a visible stream */
1244			return false;
1245	}
1246
1247	return true;
1248}
1249
1250static uint32_t dcn401_calculate_cab_allocation(struct dc *dc, struct dc_state *ctx)
1251{
1252	int i;
1253	uint8_t num_ways = 0;
1254	uint32_t mall_ss_size_bytes = 0;
1255
1256	mall_ss_size_bytes = ctx->bw_ctx.bw.dcn.mall_ss_size_bytes;
1257	// TODO add additional logic for PSR active stream exclusion optimization
1258	// mall_ss_psr_active_size_bytes = ctx->bw_ctx.bw.dcn.mall_ss_psr_active_size_bytes;
1259
1260	// Include cursor size for CAB allocation
1261	for (i = 0; i < dc->res_pool->pipe_count; i++) {
1262		struct pipe_ctx *pipe = &ctx->res_ctx.pipe_ctx[i];
1263
1264		if (!pipe->stream || !pipe->plane_state)
1265			continue;
1266
1267		mall_ss_size_bytes += dcn32_helper_calculate_mall_bytes_for_cursor(dc, pipe, false);
1268	}
1269
1270	// Convert number of cache lines required to number of ways
1271	if (dc->debug.force_mall_ss_num_ways > 0)
1272		num_ways = dc->debug.force_mall_ss_num_ways;
1273	else if (dc->res_pool->funcs->calculate_mall_ways_from_bytes)
1274		num_ways = dc->res_pool->funcs->calculate_mall_ways_from_bytes(dc, mall_ss_size_bytes);
1275	else
1276		num_ways = 0;
1277
1278	return num_ways;
1279}
1280
1281bool dcn401_apply_idle_power_optimizations(struct dc *dc, bool enable)
1282{
1283	union dmub_rb_cmd cmd;
1284	uint8_t ways, i;
1285	int j;
1286	bool mall_ss_unsupported = false;
1287	struct dc_plane_state *plane = NULL;
1288
1289	if (!dc->ctx->dmub_srv || !dc->current_state)
1290		return false;
1291
1292	for (i = 0; i < dc->current_state->stream_count; i++) {
1293		/* MALL SS messaging is not supported with PSR at this time */
1294		if (dc->current_state->streams[i] != NULL &&
1295				dc->current_state->streams[i]->link->psr_settings.psr_version != DC_PSR_VERSION_UNSUPPORTED) {
1296			DC_LOG_MALL("MALL SS not supported with PSR at this time\n");
1297			return false;
1298		}
1299	}
1300
1301	memset(&cmd, 0, sizeof(cmd));
1302	cmd.cab.header.type = DMUB_CMD__CAB_FOR_SS;
1303	cmd.cab.header.payload_bytes = sizeof(cmd.cab) - sizeof(cmd.cab.header);
1304
1305	if (enable) {
1306		if (dcn401_check_no_memory_request_for_cab(dc)) {
1307			/* 1. Check no memory request case for CAB.
1308			 * If no memory request case, send CAB_ACTION NO_DCN_REQ DMUB message
1309			 */
1310			DC_LOG_MALL("sending CAB action NO_DCN_REQ\n");
1311			cmd.cab.header.sub_type = DMUB_CMD__CAB_NO_DCN_REQ;
1312		} else {
1313			/* 2. Check if all surfaces can fit in CAB.
1314			 * If surfaces can fit into CAB, send CAB_ACTION_ALLOW DMUB message
1315			 * and configure HUBP's to fetch from MALL
1316			 */
1317			ways = dcn401_calculate_cab_allocation(dc, dc->current_state);
1318
1319			/* MALL not supported with Stereo3D or TMZ surface. If any plane is using stereo,
1320			 * or TMZ surface, don't try to enter MALL.
1321			 */
1322			for (i = 0; i < dc->current_state->stream_count; i++) {
1323				for (j = 0; j < dc->current_state->stream_status[i].plane_count; j++) {
1324					plane = dc->current_state->stream_status[i].plane_states[j];
1325
1326					if (plane->address.type == PLN_ADDR_TYPE_GRPH_STEREO ||
1327							plane->address.tmz_surface) {
1328						mall_ss_unsupported = true;
1329						break;
1330					}
1331				}
1332				if (mall_ss_unsupported)
1333					break;
1334			}
1335			if (ways <= dc->caps.cache_num_ways && !mall_ss_unsupported) {
1336				cmd.cab.header.sub_type = DMUB_CMD__CAB_DCN_SS_FIT_IN_CAB;
1337				cmd.cab.cab_alloc_ways = ways;
1338				DC_LOG_MALL("cab allocation: %d ways. CAB action: DCN_SS_FIT_IN_CAB\n", ways);
1339			} else {
1340				cmd.cab.header.sub_type = DMUB_CMD__CAB_DCN_SS_NOT_FIT_IN_CAB;
1341				DC_LOG_MALL("frame does not fit in CAB: %d ways required. CAB action: DCN_SS_NOT_FIT_IN_CAB\n", ways);
1342			}
1343		}
1344	} else {
1345		/* Disable CAB */
1346		cmd.cab.header.sub_type = DMUB_CMD__CAB_NO_IDLE_OPTIMIZATION;
1347		DC_LOG_MALL("idle optimization disabled\n");
1348	}
1349
1350	dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
1351
1352	return true;
1353}
1354
1355void dcn401_wait_for_dcc_meta_propagation(const struct dc *dc,
1356		const struct pipe_ctx *top_pipe)
1357{
1358	bool is_wait_needed = false;
1359	const struct pipe_ctx *pipe_ctx = top_pipe;
1360
1361	/* check if any surfaces are updating address while using flip immediate and dcc */
1362	while (pipe_ctx != NULL) {
1363		if (pipe_ctx->plane_state &&
1364				pipe_ctx->plane_state->dcc.enable &&
1365				pipe_ctx->plane_state->flip_immediate &&
1366				pipe_ctx->plane_state->update_flags.bits.addr_update) {
1367			is_wait_needed = true;
1368			break;
1369		}
1370
1371		/* check next pipe */
1372		pipe_ctx = pipe_ctx->bottom_pipe;
1373	}
1374
1375	if (is_wait_needed && dc->debug.dcc_meta_propagation_delay_us > 0) {
1376		udelay(dc->debug.dcc_meta_propagation_delay_us);
1377	}
1378}
1379
1380void dcn401_prepare_bandwidth(struct dc *dc,
1381	struct dc_state *context)
1382{
1383	struct hubbub *hubbub = dc->res_pool->hubbub;
1384	bool p_state_change_support = context->bw_ctx.bw.dcn.clk.p_state_change_support;
1385	unsigned int compbuf_size = 0;
1386
1387	/* Any transition into P-State support should disable MCLK switching first to avoid hangs */
1388	if (p_state_change_support) {
1389		dc->optimized_required = true;
1390		context->bw_ctx.bw.dcn.clk.p_state_change_support = false;
1391	}
1392
1393	if (dc->clk_mgr->dc_mode_softmax_enabled)
1394		if (dc->clk_mgr->clks.dramclk_khz <= dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000 &&
1395				context->bw_ctx.bw.dcn.clk.dramclk_khz > dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000)
1396			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);
1397
1398	/* Increase clocks */
1399	dc->clk_mgr->funcs->update_clocks(
1400			dc->clk_mgr,
1401			context,
1402			false);
1403
1404	/* program dchubbub watermarks:
1405	 * For assigning optimized_required, use |= operator since we don't want
1406	 * to clear the value if the optimize has not happened yet
1407	 */
1408	dc->optimized_required |= hubbub->funcs->program_watermarks(hubbub,
1409					&context->bw_ctx.bw.dcn.watermarks,
1410					dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000,
1411					false);
1412	/* update timeout thresholds */
1413	if (hubbub->funcs->program_arbiter) {
1414		dc->optimized_required |= hubbub->funcs->program_arbiter(hubbub, &context->bw_ctx.bw.dcn.arb_regs, false);
1415	}
1416
1417	/* decrease compbuf size */
1418	if (hubbub->funcs->program_compbuf_segments) {
1419		compbuf_size = context->bw_ctx.bw.dcn.arb_regs.compbuf_size;
1420		dc->optimized_required |= (compbuf_size != dc->current_state->bw_ctx.bw.dcn.arb_regs.compbuf_size);
1421
1422		hubbub->funcs->program_compbuf_segments(hubbub, compbuf_size, false);
1423	}
1424
1425	if (dc->debug.fams2_config.bits.enable) {
1426		dcn401_dmub_hw_control_lock(dc, context, true);
1427		dcn401_fams2_update_config(dc, context, false);
1428		dcn401_dmub_hw_control_lock(dc, context, false);
1429	}
1430
1431	if (p_state_change_support != context->bw_ctx.bw.dcn.clk.p_state_change_support) {
1432		/* After disabling P-State, restore the original value to ensure we get the correct P-State
1433		 * on the next optimize. */
1434		context->bw_ctx.bw.dcn.clk.p_state_change_support = p_state_change_support;
1435	}
1436}
1437
1438void dcn401_optimize_bandwidth(
1439		struct dc *dc,
1440		struct dc_state *context)
1441{
1442	int i;
1443	struct hubbub *hubbub = dc->res_pool->hubbub;
1444
1445	/* enable fams2 if needed */
1446	if (dc->debug.fams2_config.bits.enable) {
1447		dcn401_dmub_hw_control_lock(dc, context, true);
1448		dcn401_fams2_update_config(dc, context, true);
1449		dcn401_dmub_hw_control_lock(dc, context, false);
1450	}
1451
1452	/* program dchubbub watermarks */
1453	hubbub->funcs->program_watermarks(hubbub,
1454					&context->bw_ctx.bw.dcn.watermarks,
1455					dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000,
1456					true);
1457	/* update timeout thresholds */
1458	if (hubbub->funcs->program_arbiter) {
1459		hubbub->funcs->program_arbiter(hubbub, &context->bw_ctx.bw.dcn.arb_regs, true);
1460	}
1461
1462	if (dc->clk_mgr->dc_mode_softmax_enabled)
1463		if (dc->clk_mgr->clks.dramclk_khz > dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000 &&
1464				context->bw_ctx.bw.dcn.clk.dramclk_khz <= dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000)
1465			dc->clk_mgr->funcs->set_max_memclk(dc->clk_mgr, dc->clk_mgr->bw_params->dc_mode_softmax_memclk);
1466
1467	/* increase compbuf size */
1468	if (hubbub->funcs->program_compbuf_segments)
1469		hubbub->funcs->program_compbuf_segments(hubbub, context->bw_ctx.bw.dcn.arb_regs.compbuf_size, true);
1470
1471	dc->clk_mgr->funcs->update_clocks(
1472			dc->clk_mgr,
1473			context,
1474			true);
1475	if (context->bw_ctx.bw.dcn.clk.zstate_support == DCN_ZSTATE_SUPPORT_ALLOW) {
1476		for (i = 0; i < dc->res_pool->pipe_count; ++i) {
1477			struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
1478
1479			if (pipe_ctx->stream && pipe_ctx->plane_res.hubp->funcs->program_extended_blank
1480				&& pipe_ctx->stream->adjust.v_total_min == pipe_ctx->stream->adjust.v_total_max
1481				&& pipe_ctx->stream->adjust.v_total_max > pipe_ctx->stream->timing.v_total)
1482					pipe_ctx->plane_res.hubp->funcs->program_extended_blank(pipe_ctx->plane_res.hubp,
1483						pipe_ctx->dlg_regs.min_dst_y_next_start);
1484		}
1485	}
1486}
1487
1488void dcn401_dmub_hw_control_lock(struct dc *dc,
1489		struct dc_state *context,
1490		bool lock)
1491{
1492	/* use always for now */
1493	union dmub_inbox0_cmd_lock_hw hw_lock_cmd = { 0 };
1494
1495	if (!dc->ctx || !dc->ctx->dmub_srv)
1496		return;
1497
1498	if (!dc->debug.fams2_config.bits.enable && !dc_dmub_srv_is_cursor_offload_enabled(dc))
1499		return;
1500
1501	hw_lock_cmd.bits.command_code = DMUB_INBOX0_CMD__HW_LOCK;
1502	hw_lock_cmd.bits.hw_lock_client = HW_LOCK_CLIENT_DRIVER;
1503	hw_lock_cmd.bits.lock = lock;
1504	hw_lock_cmd.bits.should_release = !lock;
1505	dmub_hw_lock_mgr_inbox0_cmd(dc->ctx->dmub_srv, hw_lock_cmd);
1506}
1507
1508void dcn401_dmub_hw_control_lock_fast(union block_sequence_params *params)
1509{
1510	struct dc *dc = params->dmub_hw_control_lock_fast_params.dc;
1511	bool lock = params->dmub_hw_control_lock_fast_params.lock;
1512
1513	if (params->dmub_hw_control_lock_fast_params.is_required) {
1514		union dmub_inbox0_cmd_lock_hw hw_lock_cmd = { 0 };
1515
1516		hw_lock_cmd.bits.command_code = DMUB_INBOX0_CMD__HW_LOCK;
1517		hw_lock_cmd.bits.hw_lock_client = HW_LOCK_CLIENT_DRIVER;
1518		hw_lock_cmd.bits.lock = lock;
1519		hw_lock_cmd.bits.should_release = !lock;
1520		dmub_hw_lock_mgr_inbox0_cmd(dc->ctx->dmub_srv, hw_lock_cmd);
1521	}
1522}
1523
1524void dcn401_fams2_update_config(struct dc *dc, struct dc_state *context, bool enable)
1525{
1526	bool fams2_required;
1527
1528	if (!dc->ctx || !dc->ctx->dmub_srv || !dc->debug.fams2_config.bits.enable)
1529		return;
1530
1531	fams2_required = context->bw_ctx.bw.dcn.fams2_global_config.features.bits.enable;
1532
1533	dc_dmub_srv_fams2_update_config(dc, context, enable && fams2_required);
1534}
1535
1536static void update_dsc_for_odm_change(struct dc *dc, struct dc_state *context,
1537		struct pipe_ctx *otg_master)
1538{
1539	int i;
1540	struct pipe_ctx *old_pipe;
1541	struct pipe_ctx *new_pipe;
1542	struct pipe_ctx *old_opp_heads[MAX_PIPES];
1543	struct pipe_ctx *old_otg_master;
1544	int old_opp_head_count = 0;
1545
1546	old_otg_master = &dc->current_state->res_ctx.pipe_ctx[otg_master->pipe_idx];
1547
1548	if (resource_is_pipe_type(old_otg_master, OTG_MASTER)) {
1549		old_opp_head_count = resource_get_opp_heads_for_otg_master(old_otg_master,
1550									   &dc->current_state->res_ctx,
1551									   old_opp_heads);
1552	} else {
1553		// DC cannot assume that the current state and the new state
1554		// share the same OTG pipe since this is not true when called
1555		// in the context of a commit stream not checked. Hence, set
1556		// old_otg_master to NULL to skip the DSC configuration.
1557		old_otg_master = NULL;
1558	}
1559
1560
1561	if (otg_master->stream_res.dsc)
1562		dcn32_update_dsc_on_stream(otg_master,
1563				otg_master->stream->timing.flags.DSC);
1564	if (old_otg_master && old_otg_master->stream_res.dsc) {
1565		for (i = 0; i < old_opp_head_count; i++) {
1566			old_pipe = old_opp_heads[i];
1567			new_pipe = &context->res_ctx.pipe_ctx[old_pipe->pipe_idx];
1568			if (old_pipe->stream_res.dsc && !new_pipe->stream_res.dsc)
1569				old_pipe->stream_res.dsc->funcs->dsc_disconnect(
1570						old_pipe->stream_res.dsc);
1571		}
1572	}
1573}
1574
1575void dcn401_update_odm(struct dc *dc, struct dc_state *context,
1576		struct pipe_ctx *otg_master)
1577{
1578	struct pipe_ctx *opp_heads[MAX_PIPES];
1579	int opp_inst[MAX_PIPES] = {0};
1580	int opp_head_count;
1581	int odm_slice_width = resource_get_odm_slice_dst_width(otg_master, false);
1582	int last_odm_slice_width = resource_get_odm_slice_dst_width(otg_master, true);
1583	int i;
1584
1585	opp_head_count = resource_get_opp_heads_for_otg_master(
1586			otg_master, &context->res_ctx, opp_heads);
1587
1588	for (i = 0; i < opp_head_count; i++)
1589		opp_inst[i] = opp_heads[i]->stream_res.opp->inst;
1590	if (opp_head_count > 1)
1591		otg_master->stream_res.tg->funcs->set_odm_combine(
1592				otg_master->stream_res.tg,
1593				opp_inst, opp_head_count,
1594				odm_slice_width, last_odm_slice_width);
1595	else
1596		otg_master->stream_res.tg->funcs->set_odm_bypass(
1597				otg_master->stream_res.tg,
1598				&otg_master->stream->timing);
1599
1600	for (i = 0; i < opp_head_count; i++) {
1601		opp_heads[i]->stream_res.opp->funcs->opp_pipe_clock_control(
1602				opp_heads[i]->stream_res.opp,
1603				true);
1604		opp_heads[i]->stream_res.opp->funcs->opp_program_left_edge_extra_pixel(
1605				opp_heads[i]->stream_res.opp,
1606				opp_heads[i]->stream->timing.pixel_encoding,
1607				resource_is_pipe_type(opp_heads[i], OTG_MASTER));
1608	}
1609
1610	update_dsc_for_odm_change(dc, context, otg_master);
1611
1612	if (!resource_is_pipe_type(otg_master, DPP_PIPE))
1613		/*
1614		 * blank pattern is generated by OPP, reprogram blank pattern
1615		 * due to OPP count change
1616		 */
1617		dc->hwseq->funcs.blank_pixel_data(dc, otg_master, true);
1618}
1619
1620static void dcn401_add_dsc_sequence_for_odm_change(struct dc *dc, struct dc_state *context,
1621		struct pipe_ctx *otg_master, struct block_sequence_state *seq_state)
1622{
1623	struct pipe_ctx *old_pipe;
1624	struct pipe_ctx *new_pipe;
1625	struct pipe_ctx *old_opp_heads[MAX_PIPES];
1626	struct pipe_ctx *old_otg_master;
1627	int old_opp_head_count = 0;
1628	int i;
1629
1630	old_otg_master = &dc->current_state->res_ctx.pipe_ctx[otg_master->pipe_idx];
1631
1632	if (resource_is_pipe_type(old_otg_master, OTG_MASTER)) {
1633		old_opp_head_count = resource_get_opp_heads_for_otg_master(old_otg_master,
1634			&dc->current_state->res_ctx,
1635			old_opp_heads);
1636	} else {
1637		old_otg_master = NULL;
1638	}
1639
1640	/* Process new DSC configuration if DSC is enabled */
1641	if (otg_master->stream_res.dsc && otg_master->stream->timing.flags.DSC) {
1642		struct dc_stream_state *stream = otg_master->stream;
1643		struct pipe_ctx *odm_pipe;
1644		int opp_cnt = 1;
1645		int last_dsc_calc = 0;
1646		bool should_use_dto_dscclk = (dc->res_pool->dccg->funcs->set_dto_dscclk != NULL) &&
1647				stream->timing.pix_clk_100hz > 480000;
1648
1649		/* Count ODM pipes */
1650		for (odm_pipe = otg_master->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe)
1651			opp_cnt++;
1652
1653		int num_slices_h = stream->timing.dsc_cfg.num_slices_h / opp_cnt;
1654
1655		/* Step 1: Set DTO DSCCLK for main DSC if needed */
1656		if (should_use_dto_dscclk) {
1657			hwss_add_dccg_set_dto_dscclk(seq_state, dc->res_pool->dccg,
1658					otg_master->stream_res.dsc->inst, num_slices_h);
1659		}
1660
1661		/* Step 2: Calculate and set DSC config for main DSC */
1662		last_dsc_calc = *seq_state->num_steps;
1663		hwss_add_dsc_calculate_and_set_config(seq_state, otg_master, true, opp_cnt);
1664
1665		/* Step 3: Enable main DSC block */
1666		hwss_add_dsc_enable_with_opp(seq_state, otg_master);
1667
1668		/* Step 4: Configure and enable ODM DSC blocks */
1669		for (odm_pipe = otg_master->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {
1670			if (!odm_pipe->stream_res.dsc)
1671				continue;
1672
1673			/* Set DTO DSCCLK for ODM DSC if needed */
1674			if (should_use_dto_dscclk) {
1675				hwss_add_dccg_set_dto_dscclk(seq_state, dc->res_pool->dccg,
1676						odm_pipe->stream_res.dsc->inst, num_slices_h);
1677			}
1678
1679			/* Calculate and set DSC config for ODM DSC */
1680			last_dsc_calc = *seq_state->num_steps;
1681			hwss_add_dsc_calculate_and_set_config(seq_state, odm_pipe, true, opp_cnt);
1682
1683			/* Enable ODM DSC block */
1684			hwss_add_dsc_enable_with_opp(seq_state, odm_pipe);
1685		}
1686
1687		/* Step 5: Configure DSC in timing generator */
1688		hwss_add_tg_set_dsc_config(seq_state, otg_master->stream_res.tg,
1689			&seq_state->steps[last_dsc_calc].params.dsc_calculate_and_set_config_params.dsc_optc_cfg, true);
1690	} else if (otg_master->stream_res.dsc && !otg_master->stream->timing.flags.DSC) {
1691		/* Disable DSC in OPTC */
1692		hwss_add_tg_set_dsc_config(seq_state, otg_master->stream_res.tg, NULL, false);
1693
1694		hwss_add_dsc_disconnect(seq_state, otg_master->stream_res.dsc);
1695	}
1696
1697	/* Disable DSC for old pipes that no longer need it */
1698	if (old_otg_master && old_otg_master->stream_res.dsc) {
1699		for (i = 0; i < old_opp_head_count; i++) {
1700			old_pipe = old_opp_heads[i];
1701			new_pipe = &context->res_ctx.pipe_ctx[old_pipe->pipe_idx];
1702
1703			/* If old pipe had DSC but new pipe doesn't, disable the old DSC */
1704			if (old_pipe->stream_res.dsc && !new_pipe->stream_res.dsc) {
1705				/* Then disconnect DSC block */
1706				hwss_add_dsc_disconnect(seq_state, old_pipe->stream_res.dsc);
1707			}
1708		}
1709	}
1710}
1711
1712void dcn401_update_odm_sequence(struct dc *dc, struct dc_state *context,
1713		struct pipe_ctx *otg_master, struct block_sequence_state *seq_state)
1714{
1715	struct pipe_ctx *opp_heads[MAX_PIPES];
1716	int opp_inst[MAX_PIPES] = {0};
1717	int opp_head_count;
1718	int odm_slice_width = resource_get_odm_slice_dst_width(otg_master, false);
1719	int last_odm_slice_width = resource_get_odm_slice_dst_width(otg_master, true);
1720	int i;
1721
1722	opp_head_count = resource_get_opp_heads_for_otg_master(
1723			otg_master, &context->res_ctx, opp_heads);
1724
1725	for (i = 0; i < opp_head_count; i++)
1726		opp_inst[i] = opp_heads[i]->stream_res.opp->inst;
1727
1728	/* Add ODM combine/bypass operation to sequence */
1729	if (opp_head_count > 1) {
1730		hwss_add_optc_set_odm_combine(seq_state, otg_master->stream_res.tg, opp_inst,
1731			opp_head_count, odm_slice_width, last_odm_slice_width);
1732	} else {
1733		hwss_add_optc_set_odm_bypass(seq_state, otg_master->stream_res.tg, &otg_master->stream->timing);
1734	}
1735
1736	/* Add OPP operations to sequence */
1737	for (i = 0; i < opp_head_count; i++) {
1738		/* Add OPP pipe clock control operation */
1739		hwss_add_opp_pipe_clock_control(seq_state, opp_heads[i]->stream_res.opp, true);
1740
1741		/* Add OPP program left edge extra pixel operation */
1742		hwss_add_opp_program_left_edge_extra_pixel(seq_state, opp_heads[i]->stream_res.opp,
1743			opp_heads[i]->stream->timing.pixel_encoding, resource_is_pipe_type(opp_heads[i], OTG_MASTER));
1744	}
1745
1746	/* Add DSC update operations to sequence */
1747	dcn401_add_dsc_sequence_for_odm_change(dc, context, otg_master, seq_state);
1748
1749	/* Add blank pixel data operation if needed */
1750	if (!resource_is_pipe_type(otg_master, DPP_PIPE)) {
1751		if (dc->hwseq->funcs.blank_pixel_data_sequence)
1752			dc->hwseq->funcs.blank_pixel_data_sequence(
1753				dc, otg_master, true, seq_state);
1754	}
1755}
1756
1757void dcn401_unblank_stream(struct pipe_ctx *pipe_ctx,
1758		struct dc_link_settings *link_settings)
1759{
1760	struct encoder_unblank_param params = {0};
1761	struct dc_stream_state *stream = pipe_ctx->stream;
1762	struct dc_link *link = stream->link;
1763	struct dce_hwseq *hws = link->dc->hwseq;
1764
1765	/* calculate parameters for unblank */
1766	params.opp_cnt = resource_get_odm_slice_count(pipe_ctx);
1767
1768	params.timing = pipe_ctx->stream->timing;
1769	params.link_settings.link_rate = link_settings->link_rate;
1770	params.pix_per_cycle = pipe_ctx->stream_res.pix_clk_params.dio_se_pix_per_cycle;
1771
1772	if (link->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) {
1773		pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->dp_unblank(
1774				pipe_ctx->stream_res.hpo_dp_stream_enc,
1775				pipe_ctx->stream_res.tg->inst);
1776	} else if (dc_is_dp_signal(pipe_ctx->stream->signal)) {
1777		pipe_ctx->stream_res.stream_enc->funcs->dp_unblank(link, pipe_ctx->stream_res.stream_enc, &params);
1778	}
1779
1780	if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP)
1781		hws->funcs.edp_backlight_control(link, true);
1782}
1783
1784void dcn401_hardware_release(struct dc *dc)
1785{
1786	if (!dc->debug.disable_force_pstate_allow_on_hw_release) {
1787		if (dc->ctx->dmub_srv && dc->debug.fams2_config.bits.enable)
1788			dc_dmub_srv_fams2_update_config(dc, dc->current_state, false);
1789
1790		/* If pstate unsupported, or still supported
1791		* by firmware, force it supported by dcn
1792		*/
1793		if (dc->current_state) {
1794			if ((!dc->clk_mgr->clks.p_state_change_support ||
1795					dc->current_state->bw_ctx.bw.dcn.fams2_global_config.features.bits.enable) &&
1796					dc->res_pool->hubbub->funcs->force_pstate_change_control)
1797				dc->res_pool->hubbub->funcs->force_pstate_change_control(
1798						dc->res_pool->hubbub, true, true);
1799
1800			dc->current_state->bw_ctx.bw.dcn.clk.p_state_change_support = true;
1801			dc->clk_mgr->funcs->update_clocks(dc->clk_mgr, dc->current_state, true);
1802		}
1803	} else {
1804		if (dc->current_state) {
1805			dc->clk_mgr->clks.p_state_change_support = false;
1806			dc->clk_mgr->funcs->update_clocks(dc->clk_mgr, dc->current_state, true);
1807		}
1808
1809		if (dc->ctx->dmub_srv && dc->debug.fams2_config.bits.enable)
1810			dc_dmub_srv_fams2_update_config(dc, dc->current_state, false);
1811	}
1812}
1813
1814void dcn401_wait_for_det_buffer_update_under_otg_master(struct dc *dc, struct dc_state *context, struct pipe_ctx *otg_master)
1815{
1816	struct pipe_ctx *opp_heads[MAX_PIPES];
1817	struct pipe_ctx *dpp_pipes[MAX_PIPES];
1818	struct hubbub *hubbub = dc->res_pool->hubbub;
1819	int dpp_count = 0;
1820
1821	if (!otg_master->stream)
1822		return;
1823
1824	int slice_count = resource_get_opp_heads_for_otg_master(otg_master,
1825			&context->res_ctx, opp_heads);
1826
1827	for (int slice_idx = 0; slice_idx < slice_count; slice_idx++) {
1828		if (opp_heads[slice_idx]->plane_state) {
1829			dpp_count = resource_get_dpp_pipes_for_opp_head(
1830					opp_heads[slice_idx],
1831					&context->res_ctx,
1832					dpp_pipes);
1833			for (int dpp_idx = 0; dpp_idx < dpp_count; dpp_idx++) {
1834				struct pipe_ctx *dpp_pipe = dpp_pipes[dpp_idx];
1835					if (dpp_pipe && hubbub &&
1836						dpp_pipe->plane_res.hubp &&
1837						hubbub->funcs->wait_for_det_update)
1838						hubbub->funcs->wait_for_det_update(hubbub, dpp_pipe->plane_res.hubp->inst);
1839			}
1840		} else {
1841			if (hubbub && opp_heads[slice_idx]->plane_res.hubp && hubbub->funcs->wait_for_det_update)
1842				hubbub->funcs->wait_for_det_update(hubbub, opp_heads[slice_idx]->plane_res.hubp->inst);
1843		}
1844	}
1845}
1846
1847void dcn401_interdependent_update_lock(struct dc *dc,
1848		struct dc_state *context, bool lock)
1849{
1850	unsigned int i = 0;
1851	struct pipe_ctx *pipe = NULL;
1852	struct timing_generator *tg = NULL;
1853
1854	if (lock) {
1855		for (i = 0; i < dc->res_pool->pipe_count; i++) {
1856			pipe = &context->res_ctx.pipe_ctx[i];
1857			tg = pipe->stream_res.tg;
1858
1859			if (!resource_is_pipe_type(pipe, OTG_MASTER) ||
1860					!tg->funcs->is_tg_enabled(tg) ||
1861					dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM)
1862				continue;
1863			dc->hwss.pipe_control_lock(dc, pipe, true);
1864		}
1865	} else {
1866		/* Need to free DET being used first and have pipe update, then unlock the remaining pipes*/
1867		for (i = 0; i < dc->res_pool->pipe_count; i++) {
1868			pipe = &context->res_ctx.pipe_ctx[i];
1869			tg = pipe->stream_res.tg;
1870
1871			if (!resource_is_pipe_type(pipe, OTG_MASTER) ||
1872					!tg->funcs->is_tg_enabled(tg) ||
1873					dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM) {
1874				continue;
1875			}
1876
1877			if (dc->scratch.pipes_to_unlock_first[i]) {
1878				struct pipe_ctx *old_pipe = &dc->current_state->res_ctx.pipe_ctx[i];
1879				dc->hwss.pipe_control_lock(dc, pipe, false);
1880				/* Assumes pipe of the same index in current_state is also an OTG_MASTER pipe*/
1881				dcn401_wait_for_det_buffer_update_under_otg_master(dc, dc->current_state, old_pipe);
1882			}
1883		}
1884
1885		/* Unlocking the rest of the pipes */
1886		for (i = 0; i < dc->res_pool->pipe_count; i++) {
1887			if (dc->scratch.pipes_to_unlock_first[i])
1888				continue;
1889
1890			pipe = &context->res_ctx.pipe_ctx[i];
1891			tg = pipe->stream_res.tg;
1892			if (!resource_is_pipe_type(pipe, OTG_MASTER) ||
1893					!tg->funcs->is_tg_enabled(tg) ||
1894					dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM) {
1895				continue;
1896			}
1897
1898			dc->hwss.pipe_control_lock(dc, pipe, false);
1899		}
1900	}
1901}
1902
1903void dcn401_perform_3dlut_wa_unlock(struct pipe_ctx *pipe_ctx)
1904{
1905	/* If 3DLUT FL is enabled and 3DLUT is in use, follow the workaround sequence for pipe unlock to make sure that
1906	 * HUBP will properly fetch 3DLUT contents after unlock.
1907	 *
1908	 * This is meant to work around a known HW issue where VREADY will cancel the pending 3DLUT_ENABLE signal regardless
1909	 * of whether OTG lock is currently being held or not.
1910	 */
1911	struct pipe_ctx *wa_pipes[MAX_PIPES] = { NULL };
1912	struct pipe_ctx *odm_pipe, *mpc_pipe;
1913	int i, wa_pipe_ct = 0;
1914
1915	for (odm_pipe = pipe_ctx; odm_pipe != NULL; odm_pipe = odm_pipe->next_odm_pipe) {
1916		for (mpc_pipe = odm_pipe; mpc_pipe != NULL; mpc_pipe = mpc_pipe->bottom_pipe) {
1917			if (mpc_pipe->plane_state && mpc_pipe->plane_state->mcm_luts.lut3d_data.lut3d_src
1918						== DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM
1919					&& mpc_pipe->plane_state->mcm_shaper_3dlut_setting
1920						== DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT) {
1921				wa_pipes[wa_pipe_ct++] = mpc_pipe;
1922			}
1923		}
1924	}
1925
1926	if (wa_pipe_ct > 0) {
1927		if (pipe_ctx->stream_res.tg->funcs->set_vupdate_keepout)
1928			pipe_ctx->stream_res.tg->funcs->set_vupdate_keepout(pipe_ctx->stream_res.tg, true);
1929
1930		for (i = 0; i < wa_pipe_ct; ++i) {
1931			if (wa_pipes[i]->plane_res.hubp->funcs->hubp_enable_3dlut_fl)
1932				wa_pipes[i]->plane_res.hubp->funcs->hubp_enable_3dlut_fl(wa_pipes[i]->plane_res.hubp, true);
1933		}
1934
1935		pipe_ctx->stream_res.tg->funcs->unlock(pipe_ctx->stream_res.tg);
1936		if (pipe_ctx->stream_res.tg->funcs->wait_update_lock_status)
1937			pipe_ctx->stream_res.tg->funcs->wait_update_lock_status(pipe_ctx->stream_res.tg, false);
1938
1939		for (i = 0; i < wa_pipe_ct; ++i) {
1940			if (wa_pipes[i]->plane_res.hubp->funcs->hubp_enable_3dlut_fl)
1941				wa_pipes[i]->plane_res.hubp->funcs->hubp_enable_3dlut_fl(wa_pipes[i]->plane_res.hubp, true);
1942		}
1943
1944		if (pipe_ctx->stream_res.tg->funcs->set_vupdate_keepout)
1945			pipe_ctx->stream_res.tg->funcs->set_vupdate_keepout(pipe_ctx->stream_res.tg, false);
1946	} else {
1947		pipe_ctx->stream_res.tg->funcs->unlock(pipe_ctx->stream_res.tg);
1948	}
1949}
1950
1951void dcn401_program_outstanding_updates(struct dc *dc,
1952		struct dc_state *context)
1953{
1954	struct hubbub *hubbub = dc->res_pool->hubbub;
1955
1956	/* update compbuf if required */
1957	if (hubbub->funcs->program_compbuf_segments)
1958		hubbub->funcs->program_compbuf_segments(hubbub, context->bw_ctx.bw.dcn.arb_regs.compbuf_size, true);
1959}
1960
1961void dcn401_reset_back_end_for_pipe(
1962		struct dc *dc,
1963		struct pipe_ctx *pipe_ctx,
1964		struct dc_state *context)
1965{
1966	struct dc_link *link = pipe_ctx->stream->link;
1967	const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res);
1968
1969	DC_LOGGER_INIT(dc->ctx->logger);
1970	if (pipe_ctx->stream_res.stream_enc == NULL) {
1971		pipe_ctx->stream = NULL;
1972		return;
1973	}
1974
1975	/* DPMS may already disable or */
1976	/* dpms_off status is incorrect due to fastboot
1977	 * feature. When system resume from S4 with second
1978	 * screen only, the dpms_off would be true but
1979	 * VBIOS lit up eDP, so check link status too.
1980	 */
1981	if (!pipe_ctx->stream->dpms_off || link->link_status.link_active)
1982		dc->link_srv->set_dpms_off(pipe_ctx);
1983	else if (pipe_ctx->stream_res.audio)
1984		dc->hwss.disable_audio_stream(pipe_ctx);
1985
1986	/* free acquired resources */
1987	if (pipe_ctx->stream_res.audio) {
1988		/*disable az_endpoint*/
1989		pipe_ctx->stream_res.audio->funcs->az_disable(pipe_ctx->stream_res.audio);
1990
1991		/*free audio*/
1992		if (dc->caps.dynamic_audio == true) {
1993			/*we have to dynamic arbitrate the audio endpoints*/
1994			/*we free the resource, need reset is_audio_acquired*/
1995			update_audio_usage(&dc->current_state->res_ctx, dc->res_pool,
1996					pipe_ctx->stream_res.audio, false);
1997			pipe_ctx->stream_res.audio = NULL;
1998		}
1999	}
2000
2001	/* by upper caller loop, parent pipe: pipe0, will be reset last.
2002	 * back end share by all pipes and will be disable only when disable
2003	 * parent pipe.
2004	 */
2005	if (pipe_ctx->top_pipe == NULL) {
2006
2007		dc->hwss.set_abm_immediate_disable(pipe_ctx);
2008
2009		pipe_ctx->stream_res.tg->funcs->disable_crtc(pipe_ctx->stream_res.tg);
2010
2011		pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false);
2012		if (pipe_ctx->stream_res.tg->funcs->set_odm_bypass)
2013			pipe_ctx->stream_res.tg->funcs->set_odm_bypass(
2014					pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing);
2015
2016		set_drr_and_clear_adjust_pending(pipe_ctx, pipe_ctx->stream, NULL);
2017
2018		/* TODO - convert symclk_ref_cnts for otg to a bit map to solve
2019		 * the case where the same symclk is shared across multiple otg
2020		 * instances
2021		 */
2022		if (dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal))
2023			link->phy_state.symclk_ref_cnts.otg = 0;
2024		if (link->phy_state.symclk_state == SYMCLK_ON_TX_OFF) {
2025			link_hwss->disable_link_output(link,
2026					&pipe_ctx->link_res, pipe_ctx->stream->signal);
2027			link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF;
2028		}
2029
2030		/* reset DTBCLK_P */
2031		if (dc->res_pool->dccg->funcs->set_dtbclk_p_src)
2032			dc->res_pool->dccg->funcs->set_dtbclk_p_src(dc->res_pool->dccg, REFCLK, pipe_ctx->stream_res.tg->inst);
2033	}
2034
2035/*
2036 * In case of a dangling plane, setting this to NULL unconditionally
2037 * causes failures during reset hw ctx where, if stream is NULL,
2038 * it is expected that the pipe_ctx pointers to pipes and plane are NULL.
2039 */
2040	pipe_ctx->stream = NULL;
2041	pipe_ctx->top_pipe = NULL;
2042	pipe_ctx->bottom_pipe = NULL;
2043	pipe_ctx->next_odm_pipe = NULL;
2044	pipe_ctx->prev_odm_pipe = NULL;
2045	DC_LOG_DEBUG("Reset back end for pipe %d, tg:%d\n",
2046					pipe_ctx->pipe_idx, pipe_ctx->stream_res.tg->inst);
2047}
2048
2049void dcn401_reset_hw_ctx_wrap(
2050		struct dc *dc,
2051		struct dc_state *context)
2052{
2053	int i;
2054	struct dce_hwseq *hws = dc->hwseq;
2055
2056	/* Reset Back End*/
2057	for (i = dc->res_pool->pipe_count - 1; i >= 0 ; i--) {
2058		struct pipe_ctx *pipe_ctx_old =
2059			&dc->current_state->res_ctx.pipe_ctx[i];
2060		struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
2061
2062		if (!pipe_ctx_old->stream)
2063			continue;
2064
2065		if (pipe_ctx_old->top_pipe || pipe_ctx_old->prev_odm_pipe)
2066			continue;
2067
2068		if (!pipe_ctx->stream ||
2069				pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) {
2070			struct clock_source *old_clk = pipe_ctx_old->clock_source;
2071
2072			if (hws->funcs.reset_back_end_for_pipe)
2073				hws->funcs.reset_back_end_for_pipe(dc, pipe_ctx_old, dc->current_state);
2074			if (hws->funcs.enable_stream_gating)
2075				hws->funcs.enable_stream_gating(dc, pipe_ctx_old);
2076			if (old_clk)
2077				old_clk->funcs->cs_power_down(old_clk);
2078		}
2079	}
2080}
2081
2082static unsigned int dcn401_calculate_vready_offset_for_group(struct pipe_ctx *pipe)
2083{
2084	struct pipe_ctx *other_pipe;
2085	unsigned int vready_offset = pipe->global_sync.dcn4x.vready_offset_pixels;
2086
2087	/* Always use the largest vready_offset of all connected pipes */
2088	for (other_pipe = pipe->bottom_pipe; other_pipe != NULL; other_pipe = other_pipe->bottom_pipe) {
2089		if (other_pipe->global_sync.dcn4x.vready_offset_pixels > vready_offset)
2090			vready_offset = other_pipe->global_sync.dcn4x.vready_offset_pixels;
2091	}
2092	for (other_pipe = pipe->top_pipe; other_pipe != NULL; other_pipe = other_pipe->top_pipe) {
2093		if (other_pipe->global_sync.dcn4x.vready_offset_pixels > vready_offset)
2094			vready_offset = other_pipe->global_sync.dcn4x.vready_offset_pixels;
2095	}
2096	for (other_pipe = pipe->next_odm_pipe; other_pipe != NULL; other_pipe = other_pipe->next_odm_pipe) {
2097		if (other_pipe->global_sync.dcn4x.vready_offset_pixels > vready_offset)
2098			vready_offset = other_pipe->global_sync.dcn4x.vready_offset_pixels;
2099	}
2100	for (other_pipe = pipe->prev_odm_pipe; other_pipe != NULL; other_pipe = other_pipe->prev_odm_pipe) {
2101		if (other_pipe->global_sync.dcn4x.vready_offset_pixels > vready_offset)
2102			vready_offset = other_pipe->global_sync.dcn4x.vready_offset_pixels;
2103	}
2104
2105	return vready_offset;
2106}
2107
2108static void dcn401_program_tg(
2109	struct dc *dc,
2110	struct pipe_ctx *pipe_ctx,
2111	struct dc_state *context,
2112	struct dce_hwseq *hws)
2113{
2114	pipe_ctx->stream_res.tg->funcs->program_global_sync(
2115		pipe_ctx->stream_res.tg,
2116		dcn401_calculate_vready_offset_for_group(pipe_ctx),
2117		(unsigned int)pipe_ctx->global_sync.dcn4x.vstartup_lines,
2118		(unsigned int)pipe_ctx->global_sync.dcn4x.vupdate_offset_pixels,
2119		(unsigned int)pipe_ctx->global_sync.dcn4x.vupdate_vupdate_width_pixels,
2120		(unsigned int)pipe_ctx->global_sync.dcn4x.pstate_keepout_start_lines);
2121
2122	if (dc_state_get_pipe_subvp_type(context, pipe_ctx) != SUBVP_PHANTOM)
2123		pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
2124
2125	pipe_ctx->stream_res.tg->funcs->set_vtg_params(
2126		pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing, true);
2127
2128	if (hws->funcs.setup_vupdate_interrupt)
2129		hws->funcs.setup_vupdate_interrupt(dc, pipe_ctx);
2130}
2131
2132void dcn401_program_pipe(
2133	struct dc *dc,
2134	struct pipe_ctx *pipe_ctx,
2135	struct dc_state *context)
2136{
2137	struct dce_hwseq *hws = dc->hwseq;
2138
2139	/* Only need to unblank on top pipe */
2140	if (resource_is_pipe_type(pipe_ctx, OTG_MASTER)) {
2141		if (pipe_ctx->update_flags.bits.enable ||
2142			pipe_ctx->update_flags.bits.odm ||
2143			pipe_ctx->stream->update_flags.bits.abm_level)
2144			hws->funcs.blank_pixel_data(dc, pipe_ctx,
2145				!pipe_ctx->plane_state ||
2146				!pipe_ctx->plane_state->visible);
2147	}
2148
2149	/* Only update TG on top pipe */
2150	if (pipe_ctx->update_flags.bits.global_sync && !pipe_ctx->top_pipe
2151		&& !pipe_ctx->prev_odm_pipe)
2152		dcn401_program_tg(dc, pipe_ctx, context, hws);
2153
2154	if (pipe_ctx->update_flags.bits.odm)
2155		hws->funcs.update_odm(dc, context, pipe_ctx);
2156
2157	if (pipe_ctx->update_flags.bits.enable) {
2158		if (hws->funcs.enable_plane)
2159			hws->funcs.enable_plane(dc, pipe_ctx, context);
2160		else
2161			dc->hwss.enable_plane(dc, pipe_ctx, context);
2162
2163		if (dc->res_pool->hubbub->funcs->force_wm_propagate_to_pipes)
2164			dc->res_pool->hubbub->funcs->force_wm_propagate_to_pipes(dc->res_pool->hubbub);
2165	}
2166
2167	if (pipe_ctx->update_flags.bits.det_size) {
2168		if (dc->res_pool->hubbub->funcs->program_det_size)
2169			dc->res_pool->hubbub->funcs->program_det_size(
2170				dc->res_pool->hubbub, pipe_ctx->plane_res.hubp->inst, pipe_ctx->det_buffer_size_kb);
2171		if (dc->res_pool->hubbub->funcs->program_det_segments)
2172			dc->res_pool->hubbub->funcs->program_det_segments(
2173				dc->res_pool->hubbub, pipe_ctx->plane_res.hubp->inst, pipe_ctx->hubp_regs.det_size);
2174	}
2175
2176	if (pipe_ctx->plane_state && (pipe_ctx->update_flags.raw ||
2177	    pipe_ctx->plane_state->update_flags.raw ||
2178	    pipe_ctx->stream->update_flags.raw))
2179		dc->hwss.update_dchubp_dpp(dc, pipe_ctx, context);
2180
2181	if (pipe_ctx->plane_state && (pipe_ctx->update_flags.bits.enable ||
2182		pipe_ctx->plane_state->update_flags.bits.hdr_mult))
2183		hws->funcs.set_hdr_multiplier(pipe_ctx);
2184
2185	if (pipe_ctx->plane_state &&
2186		(pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change ||
2187			pipe_ctx->plane_state->update_flags.bits.gamma_change ||
2188			pipe_ctx->plane_state->update_flags.bits.lut_3d ||
2189			pipe_ctx->update_flags.bits.enable))
2190		hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state);
2191
2192	/* dcn10_translate_regamma_to_hw_format takes 750us to finish
2193	 * only do gamma programming for powering on, internal memcmp to avoid
2194	 * updating on slave planes
2195	 */
2196	if (pipe_ctx->update_flags.bits.enable ||
2197	    pipe_ctx->update_flags.bits.plane_changed ||
2198	    pipe_ctx->stream->update_flags.bits.out_tf)
2199		hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream);
2200
2201	/* If the pipe has been enabled or has a different opp, we
2202	 * should reprogram the fmt. This deals with cases where
2203	 * interation between mpc and odm combine on different streams
2204	 * causes a different pipe to be chosen to odm combine with.
2205	 */
2206	if (pipe_ctx->update_flags.bits.enable
2207		|| pipe_ctx->update_flags.bits.opp_changed) {
2208
2209		pipe_ctx->stream_res.opp->funcs->opp_set_dyn_expansion(
2210			pipe_ctx->stream_res.opp,
2211			COLOR_SPACE_YCBCR601,
2212			pipe_ctx->stream->timing.display_color_depth,
2213			pipe_ctx->stream->signal);
2214
2215		pipe_ctx->stream_res.opp->funcs->opp_program_fmt(
2216			pipe_ctx->stream_res.opp,
2217			&pipe_ctx->stream->bit_depth_params,
2218			&pipe_ctx->stream->clamping);
2219	}
2220
2221	/* Set ABM pipe after other pipe configurations done */
2222	if ((pipe_ctx->plane_state && pipe_ctx->plane_state->visible)) {
2223		if (pipe_ctx->stream_res.abm) {
2224			dc->hwss.set_pipe(pipe_ctx);
2225			pipe_ctx->stream_res.abm->funcs->set_abm_level(pipe_ctx->stream_res.abm,
2226				pipe_ctx->stream->abm_level);
2227		}
2228	}
2229
2230	if (pipe_ctx->update_flags.bits.test_pattern_changed) {
2231		struct output_pixel_processor *odm_opp = pipe_ctx->stream_res.opp;
2232		struct bit_depth_reduction_params params;
2233
2234		memset(&params, 0, sizeof(params));
2235		odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, &params);
2236		dc->hwss.set_disp_pattern_generator(dc,
2237			pipe_ctx,
2238			pipe_ctx->stream_res.test_pattern_params.test_pattern,
2239			pipe_ctx->stream_res.test_pattern_params.color_space,
2240			pipe_ctx->stream_res.test_pattern_params.color_depth,
2241			NULL,
2242			pipe_ctx->stream_res.test_pattern_params.width,
2243			pipe_ctx->stream_res.test_pattern_params.height,
2244			pipe_ctx->stream_res.test_pattern_params.offset);
2245	}
2246}
2247
2248/*
2249 * dcn401_program_pipe_sequence - Sequence-based version of dcn401_program_pipe
2250 *
2251 * This function creates a sequence-based version of the original dcn401_program_pipe
2252 * function. Instead of directly calling hardware programming functions, it appends
2253 * sequence steps to the provided block_sequence array that can later be executed
2254 * as part of hwss_execute_sequence.
2255 *
2256 */
2257void dcn401_program_pipe_sequence(
2258	struct dc *dc,
2259	struct pipe_ctx *pipe_ctx,
2260	struct dc_state *context,
2261	struct block_sequence_state *seq_state)
2262{
2263	struct dce_hwseq *hws = dc->hwseq;
2264
2265	/* Only need to unblank on top pipe */
2266	if (resource_is_pipe_type(pipe_ctx, OTG_MASTER)) {
2267		if (pipe_ctx->update_flags.bits.enable ||
2268				pipe_ctx->update_flags.bits.odm ||
2269				pipe_ctx->stream->update_flags.bits.abm_level) {
2270			if (dc->hwseq->funcs.blank_pixel_data_sequence)
2271				dc->hwseq->funcs.blank_pixel_data_sequence(dc, pipe_ctx,
2272					 !pipe_ctx->plane_state || !pipe_ctx->plane_state->visible,
2273					 seq_state);
2274		}
2275	}
2276
2277	/* Only update TG on top pipe */
2278	if (pipe_ctx->update_flags.bits.global_sync && !pipe_ctx->top_pipe
2279		&& !pipe_ctx->prev_odm_pipe) {
2280
2281		/* Step 1: Program global sync */
2282		hwss_add_tg_program_global_sync(seq_state, pipe_ctx->stream_res.tg,
2283			dcn401_calculate_vready_offset_for_group(pipe_ctx),
2284			(unsigned int)pipe_ctx->global_sync.dcn4x.vstartup_lines,
2285			(unsigned int)pipe_ctx->global_sync.dcn4x.vupdate_offset_pixels,
2286			(unsigned int)pipe_ctx->global_sync.dcn4x.vupdate_vupdate_width_pixels,
2287			(unsigned int)pipe_ctx->global_sync.dcn4x.pstate_keepout_start_lines);
2288
2289		/* Step 2: Wait for VACTIVE state (if not phantom pipe) */
2290		if (dc_state_get_pipe_subvp_type(context, pipe_ctx) != SUBVP_PHANTOM)
2291			hwss_add_tg_wait_for_state(seq_state, pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
2292
2293		/* Step 3: Set VTG params */
2294		hwss_add_tg_set_vtg_params(seq_state, pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing, true);
2295
2296		/* Step 4: Setup vupdate interrupt (if available) */
2297		if (hws->funcs.setup_vupdate_interrupt)
2298			dcn401_setup_vupdate_interrupt_sequence(dc, pipe_ctx, seq_state);
2299	}
2300
2301	if (pipe_ctx->update_flags.bits.odm) {
2302		if (hws->funcs.update_odm_sequence)
2303			hws->funcs.update_odm_sequence(dc, context, pipe_ctx, seq_state);
2304	}
2305
2306	if (pipe_ctx->update_flags.bits.enable) {
2307		if (dc->hwss.enable_plane_sequence)
2308			dc->hwss.enable_plane_sequence(dc, pipe_ctx, context, seq_state);
2309	}
2310
2311	if (pipe_ctx->update_flags.bits.det_size) {
2312		if (dc->res_pool->hubbub->funcs->program_det_size) {
2313			hwss_add_hubp_program_det_size(seq_state, dc->res_pool->hubbub,
2314				pipe_ctx->plane_res.hubp->inst, pipe_ctx->det_buffer_size_kb);
2315		}
2316
2317		if (dc->res_pool->hubbub->funcs->program_det_segments) {
2318			hwss_add_hubp_program_det_segments(seq_state, dc->res_pool->hubbub,
2319				pipe_ctx->plane_res.hubp->inst, pipe_ctx->hubp_regs.det_size);
2320		}
2321	}
2322
2323	if (pipe_ctx->plane_state && (pipe_ctx->update_flags.raw ||
2324	    pipe_ctx->plane_state->update_flags.raw ||
2325	    pipe_ctx->stream->update_flags.raw)) {
2326
2327		if (dc->hwss.update_dchubp_dpp_sequence)
2328			dc->hwss.update_dchubp_dpp_sequence(dc, pipe_ctx, context, seq_state);
2329	}
2330
2331	if (pipe_ctx->plane_state && (pipe_ctx->update_flags.bits.enable ||
2332		pipe_ctx->plane_state->update_flags.bits.hdr_mult)) {
2333
2334		hws->funcs.set_hdr_multiplier_sequence(pipe_ctx, seq_state);
2335	}
2336
2337	if (pipe_ctx->plane_state &&
2338		(pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change ||
2339			pipe_ctx->plane_state->update_flags.bits.gamma_change ||
2340			pipe_ctx->plane_state->update_flags.bits.lut_3d ||
2341			pipe_ctx->update_flags.bits.enable)) {
2342
2343		hwss_add_dpp_set_input_transfer_func(seq_state, dc, pipe_ctx, pipe_ctx->plane_state);
2344	}
2345
2346	/* dcn10_translate_regamma_to_hw_format takes 750us to finish
2347	 * only do gamma programming for powering on, internal memcmp to avoid
2348	 * updating on slave planes
2349	 */
2350	if (pipe_ctx->update_flags.bits.enable ||
2351			pipe_ctx->update_flags.bits.plane_changed ||
2352			pipe_ctx->stream->update_flags.bits.out_tf) {
2353		hwss_add_dpp_set_output_transfer_func(seq_state, dc, pipe_ctx, pipe_ctx->stream);
2354	}
2355
2356	/* If the pipe has been enabled or has a different opp, we
2357	 * should reprogram the fmt. This deals with cases where
2358	 * interation between mpc and odm combine on different streams
2359	 * causes a different pipe to be chosen to odm combine with.
2360	 */
2361	if (pipe_ctx->update_flags.bits.enable
2362		|| pipe_ctx->update_flags.bits.opp_changed) {
2363
2364		hwss_add_opp_set_dyn_expansion(seq_state, pipe_ctx->stream_res.opp, COLOR_SPACE_YCBCR601,
2365			pipe_ctx->stream->timing.display_color_depth, pipe_ctx->stream->signal);
2366
2367		hwss_add_opp_program_fmt(seq_state, pipe_ctx->stream_res.opp,
2368			&pipe_ctx->stream->bit_depth_params, &pipe_ctx->stream->clamping);
2369	}
2370
2371	/* Set ABM pipe after other pipe configurations done */
2372	if ((pipe_ctx->plane_state && pipe_ctx->plane_state->visible)) {
2373		if (pipe_ctx->stream_res.abm) {
2374			hwss_add_abm_set_pipe(seq_state, dc, pipe_ctx);
2375
2376			hwss_add_abm_set_level(seq_state, pipe_ctx->stream_res.abm, pipe_ctx->stream->abm_level);
2377		}
2378	}
2379
2380	if (pipe_ctx->update_flags.bits.test_pattern_changed) {
2381		struct output_pixel_processor *odm_opp = pipe_ctx->stream_res.opp;
2382
2383		hwss_add_opp_program_bit_depth_reduction(seq_state, odm_opp, true, pipe_ctx);
2384
2385		hwss_add_opp_set_disp_pattern_generator(seq_state,
2386			odm_opp,
2387			pipe_ctx->stream_res.test_pattern_params.test_pattern,
2388			pipe_ctx->stream_res.test_pattern_params.color_space,
2389			pipe_ctx->stream_res.test_pattern_params.color_depth,
2390			(struct tg_color){0},
2391			false,
2392			pipe_ctx->stream_res.test_pattern_params.width,
2393			pipe_ctx->stream_res.test_pattern_params.height,
2394			pipe_ctx->stream_res.test_pattern_params.offset);
2395	}
2396
2397}
2398
2399void dcn401_program_front_end_for_ctx(
2400	struct dc *dc,
2401	struct dc_state *context)
2402{
2403	int i;
2404	unsigned int prev_hubp_count = 0;
2405	unsigned int hubp_count = 0;
2406	struct dce_hwseq *hws = dc->hwseq;
2407	struct pipe_ctx *pipe = NULL;
2408
2409	DC_LOGGER_INIT(dc->ctx->logger);
2410
2411	if (resource_is_pipe_topology_changed(dc->current_state, context))
2412		resource_log_pipe_topology_update(dc, context);
2413
2414	if (dc->hwss.program_triplebuffer != NULL && dc->debug.enable_tri_buf) {
2415		for (i = 0; i < dc->res_pool->pipe_count; i++) {
2416			pipe = &context->res_ctx.pipe_ctx[i];
2417
2418			if (pipe->plane_state) {
2419				if (pipe->plane_state->triplebuffer_flips)
2420					BREAK_TO_DEBUGGER();
2421
2422				/*turn off triple buffer for full update*/
2423				dc->hwss.program_triplebuffer(
2424					dc, pipe, pipe->plane_state->triplebuffer_flips);
2425			}
2426		}
2427	}
2428
2429	for (i = 0; i < dc->res_pool->pipe_count; i++) {
2430		if (dc->current_state->res_ctx.pipe_ctx[i].plane_state)
2431			prev_hubp_count++;
2432		if (context->res_ctx.pipe_ctx[i].plane_state)
2433			hubp_count++;
2434	}
2435
2436	if (prev_hubp_count == 0 && hubp_count > 0) {
2437		if (dc->res_pool->hubbub->funcs->force_pstate_change_control)
2438			dc->res_pool->hubbub->funcs->force_pstate_change_control(
2439				dc->res_pool->hubbub, true, false);
2440		udelay(500);
2441	}
2442
2443	/* Set pipe update flags and lock pipes */
2444	for (i = 0; i < dc->res_pool->pipe_count; i++)
2445		dc->hwss.detect_pipe_changes(dc->current_state, context, &dc->current_state->res_ctx.pipe_ctx[i],
2446			&context->res_ctx.pipe_ctx[i]);
2447
2448	/* When disabling phantom pipes, turn on phantom OTG first (so we can get double
2449	 * buffer updates properly)
2450	 */
2451	for (i = 0; i < dc->res_pool->pipe_count; i++) {
2452		struct dc_stream_state *stream = dc->current_state->res_ctx.pipe_ctx[i].stream;
2453
2454		pipe = &dc->current_state->res_ctx.pipe_ctx[i];
2455
2456		if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable && stream &&
2457			dc_state_get_pipe_subvp_type(dc->current_state, pipe) == SUBVP_PHANTOM) {
2458			struct timing_generator *tg = dc->current_state->res_ctx.pipe_ctx[i].stream_res.tg;
2459
2460			if (tg->funcs->enable_crtc) {
2461				if (dc->hwseq->funcs.blank_pixel_data)
2462					dc->hwseq->funcs.blank_pixel_data(dc, pipe, true);
2463
2464				tg->funcs->enable_crtc(tg);
2465			}
2466		}
2467	}
2468	/* OTG blank before disabling all front ends */
2469	for (i = 0; i < dc->res_pool->pipe_count; i++)
2470		if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable
2471			&& !context->res_ctx.pipe_ctx[i].top_pipe
2472			&& !context->res_ctx.pipe_ctx[i].prev_odm_pipe
2473			&& context->res_ctx.pipe_ctx[i].stream)
2474			hws->funcs.blank_pixel_data(dc, &context->res_ctx.pipe_ctx[i], true);
2475
2476	/* Disconnect mpcc */
2477	for (i = 0; i < dc->res_pool->pipe_count; i++)
2478		if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable
2479			|| context->res_ctx.pipe_ctx[i].update_flags.bits.opp_changed) {
2480			struct hubbub *hubbub = dc->res_pool->hubbub;
2481
2482			/* Phantom pipe DET should be 0, but if a pipe in use is being transitioned to phantom
2483			 * then we want to do the programming here (effectively it's being disabled). If we do
2484			 * the programming later the DET won't be updated until the OTG for the phantom pipe is
2485			 * turned on (i.e. in an MCLK switch) which can come in too late and cause issues with
2486			 * DET allocation.
2487			 */
2488			if ((context->res_ctx.pipe_ctx[i].update_flags.bits.disable ||
2489				(context->res_ctx.pipe_ctx[i].plane_state &&
2490				dc_state_get_pipe_subvp_type(context, &context->res_ctx.pipe_ctx[i]) ==
2491				SUBVP_PHANTOM))) {
2492				if (hubbub->funcs->program_det_size)
2493					hubbub->funcs->program_det_size(hubbub,
2494						dc->current_state->res_ctx.pipe_ctx[i].plane_res.hubp->inst, 0);
2495				if (dc->res_pool->hubbub->funcs->program_det_segments)
2496					dc->res_pool->hubbub->funcs->program_det_segments(
2497						hubbub,	dc->current_state->res_ctx.pipe_ctx[i].plane_res.hubp->inst, 0);
2498			}
2499			hws->funcs.plane_atomic_disconnect(dc, dc->current_state,
2500				&dc->current_state->res_ctx.pipe_ctx[i]);
2501			DC_LOG_DC("Reset mpcc for pipe %d\n", dc->current_state->res_ctx.pipe_ctx[i].pipe_idx);
2502		}
2503
2504	/* update ODM for blanked OTG master pipes */
2505	for (i = 0; i < dc->res_pool->pipe_count; i++) {
2506		pipe = &context->res_ctx.pipe_ctx[i];
2507		if (resource_is_pipe_type(pipe, OTG_MASTER) &&
2508			!resource_is_pipe_type(pipe, DPP_PIPE) &&
2509			pipe->update_flags.bits.odm &&
2510			hws->funcs.update_odm)
2511			hws->funcs.update_odm(dc, context, pipe);
2512	}
2513
2514	/*
2515	 * Program all updated pipes, order matters for mpcc setup. Start with
2516	 * top pipe and program all pipes that follow in order
2517	 */
2518	for (i = 0; i < dc->res_pool->pipe_count; i++) {
2519		pipe = &context->res_ctx.pipe_ctx[i];
2520
2521		if (pipe->plane_state && !pipe->top_pipe) {
2522			while (pipe) {
2523				if (hws->funcs.program_pipe)
2524					hws->funcs.program_pipe(dc, pipe, context);
2525				else {
2526					/* Don't program phantom pipes in the regular front end programming sequence.
2527					 * There is an MPO transition case where a pipe being used by a video plane is
2528					 * transitioned directly to be a phantom pipe when closing the MPO video.
2529					 * However the phantom pipe will program a new HUBP_VTG_SEL (update takes place
2530					 * right away) but the MPO still exists until the double buffered update of the
2531					 * main pipe so we will get a frame of underflow if the phantom pipe is
2532					 * programmed here.
2533					 */
2534					if (pipe->stream &&
2535						dc_state_get_pipe_subvp_type(context, pipe) != SUBVP_PHANTOM)
2536						dcn401_program_pipe(dc, pipe, context);
2537				}
2538
2539				pipe = pipe->bottom_pipe;
2540			}
2541		}
2542
2543		/* Program secondary blending tree and writeback pipes */
2544		pipe = &context->res_ctx.pipe_ctx[i];
2545		if (!pipe->top_pipe && !pipe->prev_odm_pipe
2546			&& pipe->stream && pipe->stream->num_wb_info > 0
2547			&& (pipe->update_flags.raw || (pipe->plane_state && pipe->plane_state->update_flags.raw)
2548				|| pipe->stream->update_flags.raw)
2549			&& hws->funcs.program_all_writeback_pipes_in_tree)
2550			hws->funcs.program_all_writeback_pipes_in_tree(dc, pipe->stream, context);
2551
2552		/* Avoid underflow by check of pipe line read when adding 2nd plane. */
2553		if (hws->wa.wait_hubpret_read_start_during_mpo_transition &&
2554				!pipe->top_pipe &&
2555				pipe->stream &&
2556				pipe->plane_res.hubp->funcs->hubp_wait_pipe_read_start &&
2557				dc->current_state->stream_status[0].plane_count == 1 &&
2558				context->stream_status[0].plane_count > 1) {
2559			pipe->plane_res.hubp->funcs->hubp_wait_pipe_read_start(pipe->plane_res.hubp);
2560		}
2561	}
2562}
2563
2564void dcn401_post_unlock_program_front_end(
2565	struct dc *dc,
2566	struct dc_state *context)
2567{
2568	// Timeout for pipe enable
2569	unsigned int timeout_us = 100000;
2570	unsigned int polling_interval_us = 1;
2571	struct dce_hwseq *hwseq = dc->hwseq;
2572	int i;
2573
2574	DC_LOGGER_INIT(dc->ctx->logger);
2575
2576	for (i = 0; i < dc->res_pool->pipe_count; i++)
2577		if (resource_is_pipe_type(&dc->current_state->res_ctx.pipe_ctx[i], OPP_HEAD) &&
2578			!resource_is_pipe_type(&context->res_ctx.pipe_ctx[i], OPP_HEAD))
2579			dc->hwss.post_unlock_reset_opp(dc,
2580				&dc->current_state->res_ctx.pipe_ctx[i]);
2581
2582	for (i = 0; i < dc->res_pool->pipe_count; i++)
2583		if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable)
2584			dc->hwss.disable_plane(dc, dc->current_state, &dc->current_state->res_ctx.pipe_ctx[i]);
2585
2586	/*
2587	 * If we are enabling a pipe, we need to wait for pending clear as this is a critical
2588	 * part of the enable operation otherwise, DM may request an immediate flip which
2589	 * will cause HW to perform an "immediate enable" (as opposed to "vsync enable") which
2590	 * is unsupported on DCN.
2591	 */
2592	for (i = 0; i < dc->res_pool->pipe_count; i++) {
2593		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
2594		// Don't check flip pending on phantom pipes
2595		if (pipe->plane_state && !pipe->top_pipe && pipe->update_flags.bits.enable &&
2596			dc_state_get_pipe_subvp_type(context, pipe) != SUBVP_PHANTOM) {
2597			struct hubp *hubp = pipe->plane_res.hubp;
2598			int j = 0;
2599
2600			for (j = 0; j < timeout_us / polling_interval_us
2601				&& hubp->funcs->hubp_is_flip_pending(hubp); j++)
2602				udelay(polling_interval_us);
2603		}
2604	}
2605
2606	for (i = 0; i < dc->res_pool->pipe_count; i++) {
2607		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
2608		struct pipe_ctx *old_pipe = &dc->current_state->res_ctx.pipe_ctx[i];
2609
2610		/* When going from a smaller ODM slice count to larger, we must ensure double
2611		 * buffer update completes before we return to ensure we don't reduce DISPCLK
2612		 * before we've transitioned to 2:1 or 4:1
2613		 */
2614		if (resource_is_pipe_type(old_pipe, OTG_MASTER) && resource_is_pipe_type(pipe, OTG_MASTER) &&
2615			resource_get_odm_slice_count(old_pipe) < resource_get_odm_slice_count(pipe) &&
2616			dc_state_get_pipe_subvp_type(context, pipe) != SUBVP_PHANTOM) {
2617			int j = 0;
2618			struct timing_generator *tg = pipe->stream_res.tg;
2619
2620			if (tg->funcs->get_optc_double_buffer_pending) {
2621				for (j = 0; j < timeout_us / polling_interval_us
2622					&& tg->funcs->get_optc_double_buffer_pending(tg); j++)
2623					udelay(polling_interval_us);
2624			}
2625		}
2626	}
2627
2628	if (dc->res_pool->hubbub->funcs->force_pstate_change_control)
2629		dc->res_pool->hubbub->funcs->force_pstate_change_control(
2630			dc->res_pool->hubbub, false, false);
2631
2632
2633	for (i = 0; i < dc->res_pool->pipe_count; i++) {
2634		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
2635
2636		if (pipe->plane_state && !pipe->top_pipe) {
2637			/* Program phantom pipe here to prevent a frame of underflow in the MPO transition
2638			 * case (if a pipe being used for a video plane transitions to a phantom pipe, it
2639			 * can underflow due to HUBP_VTG_SEL programming if done in the regular front end
2640			 * programming sequence).
2641			 */
2642			while (pipe) {
2643				if (pipe->stream && dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM) {
2644					/* When turning on the phantom pipe we want to run through the
2645					 * entire enable sequence, so apply all the "enable" flags.
2646					 */
2647					if (dc->hwss.apply_update_flags_for_phantom)
2648						dc->hwss.apply_update_flags_for_phantom(pipe);
2649					if (dc->hwss.update_phantom_vp_position)
2650						dc->hwss.update_phantom_vp_position(dc, context, pipe);
2651					dcn401_program_pipe(dc, pipe, context);
2652				}
2653				pipe = pipe->bottom_pipe;
2654			}
2655		}
2656	}
2657
2658	if (!hwseq)
2659		return;
2660
2661	/* P-State support transitions:
2662	 * Natural -> FPO:      P-State disabled in prepare, force disallow anytime is safe
2663	 * FPO -> Natural:      Unforce anytime after FW disable is safe (P-State will assert naturally)
2664	 * Unsupported -> FPO:  P-State enabled in optimize, force disallow anytime is safe
2665	 * FPO -> Unsupported:  P-State disabled in prepare, unforce disallow anytime is safe
2666	 * FPO <-> SubVP:       Force disallow is maintained on the FPO / SubVP pipes
2667	 */
2668	if (hwseq->funcs.update_force_pstate)
2669		dc->hwseq->funcs.update_force_pstate(dc, context);
2670	/* Only program the MALL registers after all the main and phantom pipes
2671	 * are done programming.
2672	 */
2673	if (hwseq->funcs.program_mall_pipe_config)
2674		hwseq->funcs.program_mall_pipe_config(dc, context);
2675
2676	/* WA to apply WM setting*/
2677	if (hwseq->wa.DEGVIDCN21)
2678		dc->res_pool->hubbub->funcs->apply_DEDCN21_147_wa(dc->res_pool->hubbub);
2679
2680
2681	/* WA for stutter underflow during MPO transitions when adding 2nd plane */
2682	if (hwseq->wa.disallow_self_refresh_during_multi_plane_transition) {
2683
2684		if (dc->current_state->stream_status[0].plane_count == 1 &&
2685			context->stream_status[0].plane_count > 1) {
2686
2687			struct timing_generator *tg = dc->res_pool->timing_generators[0];
2688
2689			dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub, false);
2690
2691			hwseq->wa_state.disallow_self_refresh_during_multi_plane_transition_applied = true;
2692			hwseq->wa_state.disallow_self_refresh_during_multi_plane_transition_applied_on_frame =
2693				tg->funcs->get_frame_count(tg);
2694		}
2695	}
2696}
2697
2698bool dcn401_update_bandwidth(
2699	struct dc *dc,
2700	struct dc_state *context)
2701{
2702	int i;
2703	struct dce_hwseq *hws = dc->hwseq;
2704
2705	/* recalculate DML parameters */
2706	if (dc->res_pool->funcs->validate_bandwidth(dc, context, DC_VALIDATE_MODE_AND_PROGRAMMING) != DC_OK)
2707		return false;
2708
2709	/* apply updated bandwidth parameters */
2710	dc->hwss.prepare_bandwidth(dc, context);
2711
2712	/* update hubp configs for all pipes */
2713	for (i = 0; i < dc->res_pool->pipe_count; i++) {
2714		struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
2715
2716		if (pipe_ctx->plane_state == NULL)
2717			continue;
2718
2719		if (pipe_ctx->top_pipe == NULL) {
2720			bool blank = !is_pipe_tree_visible(pipe_ctx);
2721
2722			pipe_ctx->stream_res.tg->funcs->program_global_sync(
2723				pipe_ctx->stream_res.tg,
2724				dcn401_calculate_vready_offset_for_group(pipe_ctx),
2725				(unsigned int)pipe_ctx->global_sync.dcn4x.vstartup_lines,
2726				(unsigned int)pipe_ctx->global_sync.dcn4x.vupdate_offset_pixels,
2727				(unsigned int)pipe_ctx->global_sync.dcn4x.vupdate_vupdate_width_pixels,
2728				(unsigned int)pipe_ctx->global_sync.dcn4x.pstate_keepout_start_lines);
2729
2730			pipe_ctx->stream_res.tg->funcs->set_vtg_params(
2731				pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing, false);
2732
2733			if (pipe_ctx->prev_odm_pipe == NULL)
2734				hws->funcs.blank_pixel_data(dc, pipe_ctx, blank);
2735
2736			if (hws->funcs.setup_vupdate_interrupt)
2737				hws->funcs.setup_vupdate_interrupt(dc, pipe_ctx);
2738		}
2739
2740		if (pipe_ctx->plane_res.hubp->funcs->hubp_setup2)
2741			pipe_ctx->plane_res.hubp->funcs->hubp_setup2(
2742				pipe_ctx->plane_res.hubp,
2743				&pipe_ctx->hubp_regs,
2744				&pipe_ctx->global_sync,
2745				&pipe_ctx->stream->timing);
2746	}
2747
2748	return true;
2749}
2750
2751void dcn401_detect_pipe_changes(struct dc_state *old_state,
2752	struct dc_state *new_state,
2753	struct pipe_ctx *old_pipe,
2754	struct pipe_ctx *new_pipe)
2755{
2756	bool old_is_phantom = dc_state_get_pipe_subvp_type(old_state, old_pipe) == SUBVP_PHANTOM;
2757	bool new_is_phantom = dc_state_get_pipe_subvp_type(new_state, new_pipe) == SUBVP_PHANTOM;
2758
2759	unsigned int old_pipe_vready_offset_pixels = old_pipe->global_sync.dcn4x.vready_offset_pixels;
2760	unsigned int new_pipe_vready_offset_pixels = new_pipe->global_sync.dcn4x.vready_offset_pixels;
2761	unsigned int old_pipe_vstartup_lines = old_pipe->global_sync.dcn4x.vstartup_lines;
2762	unsigned int new_pipe_vstartup_lines = new_pipe->global_sync.dcn4x.vstartup_lines;
2763	unsigned int old_pipe_vupdate_offset_pixels = old_pipe->global_sync.dcn4x.vupdate_offset_pixels;
2764	unsigned int new_pipe_vupdate_offset_pixels = new_pipe->global_sync.dcn4x.vupdate_offset_pixels;
2765	unsigned int old_pipe_vupdate_width_pixels = old_pipe->global_sync.dcn4x.vupdate_vupdate_width_pixels;
2766	unsigned int new_pipe_vupdate_width_pixels = new_pipe->global_sync.dcn4x.vupdate_vupdate_width_pixels;
2767
2768	new_pipe->update_flags.raw = 0;
2769
2770	/* If non-phantom pipe is being transitioned to a phantom pipe,
2771	 * set disable and return immediately. This is because the pipe
2772	 * that was previously in use must be fully disabled before we
2773	 * can "enable" it as a phantom pipe (since the OTG will certainly
2774	 * be different). The post_unlock sequence will set the correct
2775	 * update flags to enable the phantom pipe.
2776	 */
2777	if (old_pipe->plane_state && !old_is_phantom &&
2778		new_pipe->plane_state && new_is_phantom) {
2779		new_pipe->update_flags.bits.disable = 1;
2780		return;
2781	}
2782
2783	if (resource_is_pipe_type(new_pipe, OTG_MASTER) &&
2784		resource_is_odm_topology_changed(new_pipe, old_pipe))
2785		/* Detect odm changes */
2786		new_pipe->update_flags.bits.odm = 1;
2787
2788	/* Exit on unchanged, unused pipe */
2789	if (!old_pipe->plane_state && !new_pipe->plane_state)
2790		return;
2791	/* Detect pipe enable/disable */
2792	if (!old_pipe->plane_state && new_pipe->plane_state) {
2793		new_pipe->update_flags.bits.enable = 1;
2794		new_pipe->update_flags.bits.mpcc = 1;
2795		new_pipe->update_flags.bits.dppclk = 1;
2796		new_pipe->update_flags.bits.hubp_interdependent = 1;
2797		new_pipe->update_flags.bits.hubp_rq_dlg_ttu = 1;
2798		new_pipe->update_flags.bits.unbounded_req = 1;
2799		new_pipe->update_flags.bits.gamut_remap = 1;
2800		new_pipe->update_flags.bits.scaler = 1;
2801		new_pipe->update_flags.bits.viewport = 1;
2802		new_pipe->update_flags.bits.det_size = 1;
2803		if (new_pipe->stream->test_pattern.type != DP_TEST_PATTERN_VIDEO_MODE &&
2804			new_pipe->stream_res.test_pattern_params.width != 0 &&
2805			new_pipe->stream_res.test_pattern_params.height != 0)
2806			new_pipe->update_flags.bits.test_pattern_changed = 1;
2807		if (!new_pipe->top_pipe && !new_pipe->prev_odm_pipe) {
2808			new_pipe->update_flags.bits.odm = 1;
2809			new_pipe->update_flags.bits.global_sync = 1;
2810		}
2811		return;
2812	}
2813
2814	/* For SubVP we need to unconditionally enable because any phantom pipes are
2815	 * always removed then newly added for every full updates whenever SubVP is in use.
2816	 * The remove-add sequence of the phantom pipe always results in the pipe
2817	 * being blanked in enable_stream_timing (DPG).
2818	 */
2819	if (new_pipe->stream && dc_state_get_pipe_subvp_type(new_state, new_pipe) == SUBVP_PHANTOM)
2820		new_pipe->update_flags.bits.enable = 1;
2821
2822	/* Phantom pipes are effectively disabled, if the pipe was previously phantom
2823	 * we have to enable
2824	 */
2825	if (old_pipe->plane_state && old_is_phantom &&
2826		new_pipe->plane_state && !new_is_phantom)
2827		new_pipe->update_flags.bits.enable = 1;
2828
2829	if (old_pipe->plane_state && !new_pipe->plane_state) {
2830		new_pipe->update_flags.bits.disable = 1;
2831		return;
2832	}
2833
2834	/* Detect plane change */
2835	if (old_pipe->plane_state != new_pipe->plane_state)
2836		new_pipe->update_flags.bits.plane_changed = true;
2837
2838	/* Detect top pipe only changes */
2839	if (resource_is_pipe_type(new_pipe, OTG_MASTER)) {
2840		/* Detect global sync changes */
2841		if ((old_pipe_vready_offset_pixels != new_pipe_vready_offset_pixels)
2842			|| (old_pipe_vstartup_lines != new_pipe_vstartup_lines)
2843			|| (old_pipe_vupdate_offset_pixels != new_pipe_vupdate_offset_pixels)
2844			|| (old_pipe_vupdate_width_pixels != new_pipe_vupdate_width_pixels))
2845			new_pipe->update_flags.bits.global_sync = 1;
2846	}
2847
2848	if (old_pipe->det_buffer_size_kb != new_pipe->det_buffer_size_kb)
2849		new_pipe->update_flags.bits.det_size = 1;
2850
2851	/*
2852	 * Detect opp / tg change, only set on change, not on enable
2853	 * Assume mpcc inst = pipe index, if not this code needs to be updated
2854	 * since mpcc is what is affected by these. In fact all of our sequence
2855	 * makes this assumption at the moment with how hubp reset is matched to
2856	 * same index mpcc reset.
2857	 */
2858	if (old_pipe->stream_res.opp != new_pipe->stream_res.opp)
2859		new_pipe->update_flags.bits.opp_changed = 1;
2860	if (old_pipe->stream_res.tg != new_pipe->stream_res.tg)
2861		new_pipe->update_flags.bits.tg_changed = 1;
2862
2863	/*
2864	 * Detect mpcc blending changes, only dpp inst and opp matter here,
2865	 * mpccs getting removed/inserted update connected ones during their own
2866	 * programming
2867	 */
2868	if (old_pipe->plane_res.dpp != new_pipe->plane_res.dpp
2869		|| old_pipe->stream_res.opp != new_pipe->stream_res.opp)
2870		new_pipe->update_flags.bits.mpcc = 1;
2871
2872	/* Detect dppclk change */
2873	if (old_pipe->plane_res.bw.dppclk_khz != new_pipe->plane_res.bw.dppclk_khz)
2874		new_pipe->update_flags.bits.dppclk = 1;
2875
2876	/* Check for scl update */
2877	if (memcmp(&old_pipe->plane_res.scl_data, &new_pipe->plane_res.scl_data, sizeof(struct scaler_data)))
2878		new_pipe->update_flags.bits.scaler = 1;
2879	/* Check for vp update */
2880	if (memcmp(&old_pipe->plane_res.scl_data.viewport, &new_pipe->plane_res.scl_data.viewport, sizeof(struct rect))
2881		|| memcmp(&old_pipe->plane_res.scl_data.viewport_c,
2882			&new_pipe->plane_res.scl_data.viewport_c, sizeof(struct rect)))
2883		new_pipe->update_flags.bits.viewport = 1;
2884
2885	/* Detect dlg/ttu/rq updates */
2886	{
2887		struct dml2_display_dlg_regs old_dlg_regs = old_pipe->hubp_regs.dlg_regs;
2888		struct dml2_display_ttu_regs old_ttu_regs = old_pipe->hubp_regs.ttu_regs;
2889		struct dml2_display_rq_regs	 old_rq_regs = old_pipe->hubp_regs.rq_regs;
2890		struct dml2_display_dlg_regs *new_dlg_regs = &new_pipe->hubp_regs.dlg_regs;
2891		struct dml2_display_ttu_regs *new_ttu_regs = &new_pipe->hubp_regs.ttu_regs;
2892		struct dml2_display_rq_regs	 *new_rq_regs = &new_pipe->hubp_regs.rq_regs;
2893
2894		/* Detect pipe interdependent updates */
2895		if ((old_dlg_regs.dst_y_prefetch != new_dlg_regs->dst_y_prefetch)
2896			|| (old_dlg_regs.vratio_prefetch != new_dlg_regs->vratio_prefetch)
2897			|| (old_dlg_regs.vratio_prefetch_c != new_dlg_regs->vratio_prefetch_c)
2898			|| (old_dlg_regs.dst_y_per_vm_vblank != new_dlg_regs->dst_y_per_vm_vblank)
2899			|| (old_dlg_regs.dst_y_per_row_vblank != new_dlg_regs->dst_y_per_row_vblank)
2900			|| (old_dlg_regs.dst_y_per_vm_flip != new_dlg_regs->dst_y_per_vm_flip)
2901			|| (old_dlg_regs.dst_y_per_row_flip != new_dlg_regs->dst_y_per_row_flip)
2902			|| (old_dlg_regs.refcyc_per_meta_chunk_vblank_l != new_dlg_regs->refcyc_per_meta_chunk_vblank_l)
2903			|| (old_dlg_regs.refcyc_per_meta_chunk_vblank_c != new_dlg_regs->refcyc_per_meta_chunk_vblank_c)
2904			|| (old_dlg_regs.refcyc_per_meta_chunk_flip_l != new_dlg_regs->refcyc_per_meta_chunk_flip_l)
2905			|| (old_dlg_regs.refcyc_per_line_delivery_pre_l != new_dlg_regs->refcyc_per_line_delivery_pre_l)
2906			|| (old_dlg_regs.refcyc_per_line_delivery_pre_c != new_dlg_regs->refcyc_per_line_delivery_pre_c)
2907			|| (old_ttu_regs.refcyc_per_req_delivery_pre_l != new_ttu_regs->refcyc_per_req_delivery_pre_l)
2908			|| (old_ttu_regs.refcyc_per_req_delivery_pre_c != new_ttu_regs->refcyc_per_req_delivery_pre_c)
2909			|| (old_ttu_regs.refcyc_per_req_delivery_pre_cur0 !=
2910				new_ttu_regs->refcyc_per_req_delivery_pre_cur0)
2911			|| (old_ttu_regs.min_ttu_vblank != new_ttu_regs->min_ttu_vblank)
2912			|| (old_ttu_regs.qos_level_flip != new_ttu_regs->qos_level_flip)) {
2913			old_dlg_regs.dst_y_prefetch = new_dlg_regs->dst_y_prefetch;
2914			old_dlg_regs.vratio_prefetch = new_dlg_regs->vratio_prefetch;
2915			old_dlg_regs.vratio_prefetch_c = new_dlg_regs->vratio_prefetch_c;
2916			old_dlg_regs.dst_y_per_vm_vblank = new_dlg_regs->dst_y_per_vm_vblank;
2917			old_dlg_regs.dst_y_per_row_vblank = new_dlg_regs->dst_y_per_row_vblank;
2918			old_dlg_regs.dst_y_per_vm_flip = new_dlg_regs->dst_y_per_vm_flip;
2919			old_dlg_regs.dst_y_per_row_flip = new_dlg_regs->dst_y_per_row_flip;
2920			old_dlg_regs.refcyc_per_meta_chunk_vblank_l = new_dlg_regs->refcyc_per_meta_chunk_vblank_l;
2921			old_dlg_regs.refcyc_per_meta_chunk_vblank_c = new_dlg_regs->refcyc_per_meta_chunk_vblank_c;
2922			old_dlg_regs.refcyc_per_meta_chunk_flip_l = new_dlg_regs->refcyc_per_meta_chunk_flip_l;
2923			old_dlg_regs.refcyc_per_line_delivery_pre_l = new_dlg_regs->refcyc_per_line_delivery_pre_l;
2924			old_dlg_regs.refcyc_per_line_delivery_pre_c = new_dlg_regs->refcyc_per_line_delivery_pre_c;
2925			old_ttu_regs.refcyc_per_req_delivery_pre_l = new_ttu_regs->refcyc_per_req_delivery_pre_l;
2926			old_ttu_regs.refcyc_per_req_delivery_pre_c = new_ttu_regs->refcyc_per_req_delivery_pre_c;
2927			old_ttu_regs.refcyc_per_req_delivery_pre_cur0 = new_ttu_regs->refcyc_per_req_delivery_pre_cur0;
2928			old_ttu_regs.min_ttu_vblank = new_ttu_regs->min_ttu_vblank;
2929			old_ttu_regs.qos_level_flip = new_ttu_regs->qos_level_flip;
2930			new_pipe->update_flags.bits.hubp_interdependent = 1;
2931		}
2932		/* Detect any other updates to ttu/rq/dlg */
2933		if (memcmp(&old_dlg_regs, new_dlg_regs, sizeof(old_dlg_regs)) ||
2934			memcmp(&old_ttu_regs, new_ttu_regs, sizeof(old_ttu_regs)) ||
2935			memcmp(&old_rq_regs, new_rq_regs, sizeof(old_rq_regs)))
2936			new_pipe->update_flags.bits.hubp_rq_dlg_ttu = 1;
2937	}
2938
2939	if (old_pipe->unbounded_req != new_pipe->unbounded_req)
2940		new_pipe->update_flags.bits.unbounded_req = 1;
2941
2942	if (memcmp(&old_pipe->stream_res.test_pattern_params,
2943		&new_pipe->stream_res.test_pattern_params, sizeof(struct test_pattern_params))) {
2944		new_pipe->update_flags.bits.test_pattern_changed = 1;
2945	}
2946}
2947
2948void dcn401_plane_atomic_power_down(struct dc *dc,
2949		struct dpp *dpp,
2950		struct hubp *hubp)
2951{
2952	struct dce_hwseq *hws = dc->hwseq;
2953	uint32_t org_ip_request_cntl = 0;
2954
2955	DC_LOGGER_INIT(dc->ctx->logger);
2956
2957	if (REG(DC_IP_REQUEST_CNTL)) {
2958		REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
2959		if (org_ip_request_cntl == 0)
2960			REG_SET(DC_IP_REQUEST_CNTL, 0,
2961				IP_REQUEST_EN, 1);
2962	}
2963
2964	if (hws->funcs.dpp_pg_control)
2965		hws->funcs.dpp_pg_control(hws, dpp->inst, false);
2966
2967	if (hws->funcs.hubp_pg_control)
2968		hws->funcs.hubp_pg_control(hws, hubp->inst, false);
2969
2970	hubp->funcs->hubp_reset(hubp);
2971	dpp->funcs->dpp_reset(dpp);
2972
2973	if (org_ip_request_cntl == 0 && REG(DC_IP_REQUEST_CNTL))
2974		REG_SET(DC_IP_REQUEST_CNTL, 0,
2975			IP_REQUEST_EN, 0);
2976
2977	DC_LOG_DEBUG(
2978			"Power gated front end %d\n", hubp->inst);
2979
2980	if (hws->funcs.dpp_root_clock_control)
2981		hws->funcs.dpp_root_clock_control(hws, dpp->inst, false);
2982}
2983
2984void dcn401_update_cursor_offload_pipe(struct dc *dc, const struct pipe_ctx *pipe)
2985{
2986	volatile struct dmub_cursor_offload_v1 *cs = dc->ctx->dmub_srv->dmub->cursor_offload_v1;
2987	const struct pipe_ctx *top_pipe = resource_get_otg_master(pipe);
2988	const struct hubp *hubp = pipe->plane_res.hubp;
2989	const struct dpp *dpp = pipe->plane_res.dpp;
2990	volatile struct dmub_cursor_offload_pipe_data_dcn401_v1 *p;
2991	uint32_t stream_idx, write_idx, payload_idx;
2992
2993	if (!top_pipe || !hubp || !dpp)
2994		return;
2995
2996	stream_idx = top_pipe->pipe_idx;
2997	write_idx = cs->offload_streams[stream_idx].write_idx + 1; /*  new payload (+1) */
2998	payload_idx = write_idx % ARRAY_SIZE(cs->offload_streams[stream_idx].payloads);
2999
3000	p = &cs->offload_streams[stream_idx].payloads[payload_idx].pipe_data[pipe->pipe_idx].dcn401;
3001
3002	p->CURSOR0_0_CURSOR_SURFACE_ADDRESS = hubp->att.SURFACE_ADDR;
3003	p->CURSOR0_0_CURSOR_SURFACE_ADDRESS_HIGH = hubp->att.SURFACE_ADDR_HIGH;
3004	p->CURSOR0_0_CURSOR_SIZE__CURSOR_WIDTH = hubp->att.size.bits.width;
3005	p->CURSOR0_0_CURSOR_SIZE__CURSOR_HEIGHT = hubp->att.size.bits.height;
3006	p->CURSOR0_0_CURSOR_POSITION__CURSOR_X_POSITION = hubp->pos.position.bits.x_pos;
3007	p->CURSOR0_0_CURSOR_POSITION__CURSOR_Y_POSITION = hubp->pos.position.bits.y_pos;
3008	p->CURSOR0_0_CURSOR_HOT_SPOT__CURSOR_HOT_SPOT_X = hubp->pos.hot_spot.bits.x_hot;
3009	p->CURSOR0_0_CURSOR_HOT_SPOT__CURSOR_HOT_SPOT_Y = hubp->pos.hot_spot.bits.y_hot;
3010	p->CURSOR0_0_CURSOR_DST_OFFSET__CURSOR_DST_X_OFFSET = hubp->pos.dst_offset.bits.dst_x_offset;
3011	p->CURSOR0_0_CURSOR_CONTROL__CURSOR_ENABLE = hubp->pos.cur_ctl.bits.cur_enable;
3012	p->CURSOR0_0_CURSOR_CONTROL__CURSOR_MODE = hubp->att.cur_ctl.bits.mode;
3013	p->CURSOR0_0_CURSOR_CONTROL__CURSOR_2X_MAGNIFY = hubp->pos.cur_ctl.bits.cur_2x_magnify;
3014	p->CURSOR0_0_CURSOR_CONTROL__CURSOR_PITCH = hubp->att.cur_ctl.bits.pitch;
3015	p->CURSOR0_0_CURSOR_CONTROL__CURSOR_LINES_PER_CHUNK = hubp->att.cur_ctl.bits.line_per_chunk;
3016
3017	p->CM_CUR0_CURSOR0_CONTROL__CUR0_ENABLE = dpp->att.cur0_ctl.bits.cur0_enable;
3018	p->CM_CUR0_CURSOR0_CONTROL__CUR0_MODE = dpp->att.cur0_ctl.bits.mode;
3019	p->CM_CUR0_CURSOR0_CONTROL__CUR0_EXPANSION_MODE = dpp->att.cur0_ctl.bits.expansion_mode;
3020	p->CM_CUR0_CURSOR0_CONTROL__CUR0_ROM_EN = dpp->att.cur0_ctl.bits.cur0_rom_en;
3021	p->CM_CUR0_CURSOR0_COLOR0__CUR0_COLOR0 = 0x000000;
3022	p->CM_CUR0_CURSOR0_COLOR1__CUR0_COLOR1 = 0xFFFFFF;
3023
3024	p->CM_CUR0_CURSOR0_FP_SCALE_BIAS_G_Y__CUR0_FP_BIAS_G_Y =
3025		dpp->att.fp_scale_bias_g_y.bits.fp_bias_g_y;
3026	p->CM_CUR0_CURSOR0_FP_SCALE_BIAS_G_Y__CUR0_FP_SCALE_G_Y =
3027		dpp->att.fp_scale_bias_g_y.bits.fp_scale_g_y;
3028	p->CM_CUR0_CURSOR0_FP_SCALE_BIAS_RB_CRCB__CUR0_FP_BIAS_RB_CRCB =
3029		dpp->att.fp_scale_bias_rb_crcb.bits.fp_bias_rb_crcb;
3030	p->CM_CUR0_CURSOR0_FP_SCALE_BIAS_RB_CRCB__CUR0_FP_SCALE_RB_CRCB =
3031		dpp->att.fp_scale_bias_rb_crcb.bits.fp_scale_rb_crcb;
3032
3033	p->HUBPREQ0_CURSOR_SETTINGS__CURSOR0_DST_Y_OFFSET = hubp->att.settings.bits.dst_y_offset;
3034	p->HUBPREQ0_CURSOR_SETTINGS__CURSOR0_CHUNK_HDL_ADJUST = hubp->att.settings.bits.chunk_hdl_adjust;
3035	p->HUBP0_DCHUBP_MALL_CONFIG__USE_MALL_FOR_CURSOR = hubp->use_mall_for_cursor;
3036
3037	cs->offload_streams[stream_idx].payloads[payload_idx].pipe_mask |= (1u << pipe->pipe_idx);
3038}
3039
3040void dcn401_plane_atomic_power_down_sequence(struct dc *dc,
3041		struct dpp *dpp,
3042		struct hubp *hubp,
3043		struct block_sequence_state *seq_state)
3044{
3045	struct dce_hwseq *hws = dc->hwseq;
3046	uint32_t org_ip_request_cntl = 0;
3047
3048	DC_LOGGER_INIT(dc->ctx->logger);
3049
3050	/* Check and set DC_IP_REQUEST_CNTL if needed */
3051	if (REG(DC_IP_REQUEST_CNTL)) {
3052		REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
3053		if (org_ip_request_cntl == 0)
3054			hwss_add_dc_ip_request_cntl(seq_state, dc, true);
3055	}
3056
3057	/* DPP power gating control */
3058	hwss_add_dpp_pg_control(seq_state, hws, dpp->inst, false);
3059
3060	/* HUBP power gating control */
3061	hwss_add_hubp_pg_control(seq_state, hws, hubp->inst, false);
3062
3063	/* HUBP reset */
3064	hwss_add_hubp_reset(seq_state, hubp);
3065
3066	/* DPP reset */
3067	hwss_add_dpp_reset(seq_state, dpp);
3068
3069	/* Restore DC_IP_REQUEST_CNTL if it was originally 0 */
3070	if (org_ip_request_cntl == 0 && REG(DC_IP_REQUEST_CNTL))
3071		hwss_add_dc_ip_request_cntl(seq_state, dc, false);
3072
3073	DC_LOG_DEBUG("Power gated front end %d\n", hubp->inst);
3074
3075	/* DPP root clock control */
3076	hwss_add_dpp_root_clock_control(seq_state, hws, dpp->inst, false);
3077}
3078
3079/* trigger HW to start disconnect plane from stream on the next vsync using block sequence */
3080void dcn401_plane_atomic_disconnect_sequence(struct dc *dc,
3081		struct dc_state *state,
3082		struct pipe_ctx *pipe_ctx,
3083		struct block_sequence_state *seq_state)
3084{
3085	struct hubp *hubp = pipe_ctx->plane_res.hubp;
3086	int dpp_id = pipe_ctx->plane_res.dpp->inst;
3087	struct mpc *mpc = dc->res_pool->mpc;
3088	struct mpc_tree *mpc_tree_params;
3089	struct mpcc *mpcc_to_remove = NULL;
3090	struct output_pixel_processor *opp = pipe_ctx->stream_res.opp;
3091
3092	mpc_tree_params = &(opp->mpc_tree_params);
3093	mpcc_to_remove = mpc->funcs->get_mpcc_for_dpp(mpc_tree_params, dpp_id);
3094
3095	/*Already reset*/
3096	if (mpcc_to_remove == NULL)
3097		return;
3098
3099	/* Step 1: Remove MPCC from MPC tree */
3100	hwss_add_mpc_remove_mpcc(seq_state, mpc, mpc_tree_params, mpcc_to_remove);
3101
3102	// Phantom pipes have OTG disabled by default, so MPCC_STATUS will never assert idle,
3103	// so don't wait for MPCC_IDLE in the programming sequence
3104	if (dc_state_get_pipe_subvp_type(state, pipe_ctx) != SUBVP_PHANTOM) {
3105		/* Step 2: Set MPCC disconnect pending flag */
3106		hwss_add_opp_set_mpcc_disconnect_pending(seq_state, opp, pipe_ctx->plane_res.mpcc_inst, true);
3107	}
3108
3109	/* Step 3: Set optimized required flag */
3110	hwss_add_dc_set_optimized_required(seq_state, dc, true);
3111
3112	/* Step 4: Disconnect HUBP if function exists */
3113	if (hubp->funcs->hubp_disconnect)
3114		hwss_add_hubp_disconnect(seq_state, hubp);
3115
3116	/* Step 5: Verify pstate change high if debug sanity checks are enabled */
3117	if (dc->debug.sanity_checks)
3118		dc->hwseq->funcs.verify_allow_pstate_change_high_sequence(dc, seq_state);
3119}
3120
3121void dcn401_blank_pixel_data_sequence(
3122	struct dc *dc,
3123	struct pipe_ctx *pipe_ctx,
3124	bool blank,
3125	struct block_sequence_state *seq_state)
3126{
3127	struct tg_color black_color = {0};
3128	struct stream_resource *stream_res = &pipe_ctx->stream_res;
3129	struct dc_stream_state *stream = pipe_ctx->stream;
3130	enum dc_color_space color_space = stream->output_color_space;
3131	enum controller_dp_test_pattern test_pattern = CONTROLLER_DP_TEST_PATTERN_SOLID_COLOR;
3132	enum controller_dp_color_space test_pattern_color_space = CONTROLLER_DP_COLOR_SPACE_UDEFINED;
3133	struct pipe_ctx *odm_pipe;
3134	struct rect odm_slice_src;
3135
3136	if (stream->link->test_pattern_enabled)
3137		return;
3138
3139	/* get opp dpg blank color */
3140	color_space_to_black_color(dc, color_space, &black_color);
3141
3142	if (blank) {
3143		/* Set ABM immediate disable */
3144		hwss_add_abm_set_immediate_disable(seq_state, dc, pipe_ctx);
3145
3146		if (dc->debug.visual_confirm != VISUAL_CONFIRM_DISABLE) {
3147			test_pattern = CONTROLLER_DP_TEST_PATTERN_COLORSQUARES;
3148			test_pattern_color_space = CONTROLLER_DP_COLOR_SPACE_RGB;
3149		}
3150	} else {
3151		test_pattern = CONTROLLER_DP_TEST_PATTERN_VIDEOMODE;
3152	}
3153
3154	odm_pipe = pipe_ctx;
3155
3156	/* Set display pattern generator for all ODM pipes */
3157	while (odm_pipe->next_odm_pipe) {
3158		odm_slice_src = resource_get_odm_slice_src_rect(odm_pipe);
3159
3160		hwss_add_opp_set_disp_pattern_generator(seq_state,
3161			odm_pipe->stream_res.opp,
3162			test_pattern,
3163			test_pattern_color_space,
3164			stream->timing.display_color_depth,
3165			black_color,
3166			true,
3167			odm_slice_src.width,
3168			odm_slice_src.height,
3169			odm_slice_src.x);
3170
3171		odm_pipe = odm_pipe->next_odm_pipe;
3172	}
3173
3174	/* Set display pattern generator for final ODM pipe */
3175	odm_slice_src = resource_get_odm_slice_src_rect(odm_pipe);
3176
3177	hwss_add_opp_set_disp_pattern_generator(seq_state,
3178		odm_pipe->stream_res.opp,
3179		test_pattern,
3180		test_pattern_color_space,
3181		stream->timing.display_color_depth,
3182		black_color,
3183		true,
3184		odm_slice_src.width,
3185		odm_slice_src.height,
3186		odm_slice_src.x);
3187
3188	/* Handle ABM level setting when not blanking */
3189	if (!blank) {
3190		if (stream_res->abm) {
3191			/* Set pipe for ABM */
3192			hwss_add_abm_set_pipe(seq_state, dc, pipe_ctx);
3193
3194			/* Set ABM level */
3195			hwss_add_abm_set_level(seq_state, stream_res->abm, stream->abm_level);
3196		}
3197	}
3198}
3199
3200void dcn401_program_all_writeback_pipes_in_tree_sequence(
3201		struct dc *dc,
3202		const struct dc_stream_state *stream,
3203		struct dc_state *context,
3204		struct block_sequence_state *seq_state)
3205{
3206	struct dwbc *dwb;
3207	int i_wb, i_pipe;
3208
3209	if (!stream || stream->num_wb_info > dc->res_pool->res_cap->num_dwb)
3210		return;
3211
3212	/* For each writeback pipe */
3213	for (i_wb = 0; i_wb < stream->num_wb_info; i_wb++) {
3214		/* Get direct pointer to writeback info */
3215		struct dc_writeback_info *wb_info = (struct dc_writeback_info *)&stream->writeback_info[i_wb];
3216		int mpcc_inst = -1;
3217
3218		if (wb_info->wb_enabled) {
3219			/* Get the MPCC instance for writeback_source_plane */
3220			for (i_pipe = 0; i_pipe < dc->res_pool->pipe_count; i_pipe++) {
3221				struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i_pipe];
3222
3223				if (!pipe_ctx->plane_state)
3224					continue;
3225
3226				if (pipe_ctx->plane_state == wb_info->writeback_source_plane) {
3227					mpcc_inst = pipe_ctx->plane_res.mpcc_inst;
3228					break;
3229				}
3230			}
3231
3232			if (mpcc_inst == -1) {
3233				/* Disable writeback pipe and disconnect from MPCC
3234				 * if source plane has been removed
3235				 */
3236				dcn401_disable_writeback_sequence(dc, wb_info, seq_state);
3237				continue;
3238			}
3239
3240			ASSERT(wb_info->dwb_pipe_inst < dc->res_pool->res_cap->num_dwb);
3241			dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst];
3242
3243			if (dwb->funcs->is_enabled(dwb)) {
3244				/* Writeback pipe already enabled, only need to update */
3245				dcn401_update_writeback_sequence(dc, wb_info, context, seq_state);
3246			} else {
3247				/* Enable writeback pipe and connect to MPCC */
3248				dcn401_enable_writeback_sequence(dc, wb_info, context, mpcc_inst, seq_state);
3249			}
3250		} else {
3251			/* Disable writeback pipe and disconnect from MPCC */
3252			dcn401_disable_writeback_sequence(dc, wb_info, seq_state);
3253		}
3254	}
3255}
3256
3257void dcn401_enable_writeback_sequence(
3258		struct dc *dc,
3259		struct dc_writeback_info *wb_info,
3260		struct dc_state *context,
3261		int mpcc_inst,
3262		struct block_sequence_state *seq_state)
3263{
3264	struct dwbc *dwb;
3265	struct mcif_wb *mcif_wb;
3266
3267	if (!wb_info->wb_enabled || wb_info->dwb_pipe_inst >= dc->res_pool->res_cap->num_dwb)
3268		return;
3269
3270	dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst];
3271	mcif_wb = dc->res_pool->mcif_wb[wb_info->dwb_pipe_inst];
3272
3273	/* Update DWBC with new parameters */
3274	hwss_add_dwbc_update(seq_state, dwb, &wb_info->dwb_params);
3275
3276	/* Configure MCIF_WB buffer settings */
3277	hwss_add_mcif_wb_config_buf(seq_state, mcif_wb, &wb_info->mcif_buf_params, wb_info->dwb_params.dest_height);
3278
3279	/* Configure MCIF_WB arbitration */
3280	hwss_add_mcif_wb_config_arb(seq_state, mcif_wb, &context->bw_ctx.bw.dcn.bw_writeback.mcif_wb_arb[wb_info->dwb_pipe_inst]);
3281
3282	/* Enable MCIF_WB */
3283	hwss_add_mcif_wb_enable(seq_state, mcif_wb);
3284
3285	/* Set DWB MUX to connect writeback to MPCC */
3286	hwss_add_mpc_set_dwb_mux(seq_state, dc->res_pool->mpc, wb_info->dwb_pipe_inst, mpcc_inst);
3287
3288	/* Enable DWBC */
3289	hwss_add_dwbc_enable(seq_state, dwb, &wb_info->dwb_params);
3290}
3291
3292void dcn401_disable_writeback_sequence(
3293		struct dc *dc,
3294		struct dc_writeback_info *wb_info,
3295		struct block_sequence_state *seq_state)
3296{
3297	struct dwbc *dwb;
3298	struct mcif_wb *mcif_wb;
3299
3300	if (wb_info->dwb_pipe_inst >= dc->res_pool->res_cap->num_dwb)
3301		return;
3302
3303	dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst];
3304	mcif_wb = dc->res_pool->mcif_wb[wb_info->dwb_pipe_inst];
3305
3306	/* Disable DWBC */
3307	hwss_add_dwbc_disable(seq_state, dwb);
3308
3309	/* Disable DWB MUX */
3310	hwss_add_mpc_disable_dwb_mux(seq_state, dc->res_pool->mpc, wb_info->dwb_pipe_inst);
3311
3312	/* Disable MCIF_WB */
3313	hwss_add_mcif_wb_disable(seq_state, mcif_wb);
3314}
3315
3316void dcn401_update_writeback_sequence(
3317		struct dc *dc,
3318		struct dc_writeback_info *wb_info,
3319		struct dc_state *context,
3320		struct block_sequence_state *seq_state)
3321{
3322	struct dwbc *dwb;
3323	struct mcif_wb *mcif_wb;
3324
3325	if (!wb_info->wb_enabled || wb_info->dwb_pipe_inst >= dc->res_pool->res_cap->num_dwb)
3326		return;
3327
3328	dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst];
3329	mcif_wb = dc->res_pool->mcif_wb[wb_info->dwb_pipe_inst];
3330
3331	/* Update writeback pipe */
3332	hwss_add_dwbc_update(seq_state, dwb, &wb_info->dwb_params);
3333
3334	/* Update MCIF_WB buffer settings if needed */
3335	hwss_add_mcif_wb_config_buf(seq_state, mcif_wb, &wb_info->mcif_buf_params, wb_info->dwb_params.dest_height);
3336}
3337
3338static int find_free_gsl_group(const struct dc *dc)
3339{
3340	if (dc->res_pool->gsl_groups.gsl_0 == 0)
3341		return 1;
3342	if (dc->res_pool->gsl_groups.gsl_1 == 0)
3343		return 2;
3344	if (dc->res_pool->gsl_groups.gsl_2 == 0)
3345		return 3;
3346
3347	return 0;
3348}
3349
3350void dcn401_setup_gsl_group_as_lock_sequence(
3351		const struct dc *dc,
3352		struct pipe_ctx *pipe_ctx,
3353		bool enable,
3354		struct block_sequence_state *seq_state)
3355{
3356	struct gsl_params gsl;
3357	int group_idx;
3358
3359	memset(&gsl, 0, sizeof(struct gsl_params));
3360
3361	if (enable) {
3362		/* return if group already assigned since GSL was set up
3363		 * for vsync flip, we would unassign so it can't be "left over"
3364		 */
3365		if (pipe_ctx->stream_res.gsl_group > 0)
3366			return;
3367
3368		group_idx = find_free_gsl_group(dc);
3369		ASSERT(group_idx != 0);
3370		pipe_ctx->stream_res.gsl_group = group_idx;
3371
3372		/* set gsl group reg field and mark resource used */
3373		switch (group_idx) {
3374		case 1:
3375			gsl.gsl0_en = 1;
3376			dc->res_pool->gsl_groups.gsl_0 = 1;
3377			break;
3378		case 2:
3379			gsl.gsl1_en = 1;
3380			dc->res_pool->gsl_groups.gsl_1 = 1;
3381			break;
3382		case 3:
3383			gsl.gsl2_en = 1;
3384			dc->res_pool->gsl_groups.gsl_2 = 1;
3385			break;
3386		default:
3387			BREAK_TO_DEBUGGER();
3388			return; // invalid case
3389		}
3390		gsl.gsl_master_en = 1;
3391	} else {
3392		group_idx = pipe_ctx->stream_res.gsl_group;
3393		if (group_idx == 0)
3394			return; // if not in use, just return
3395
3396		pipe_ctx->stream_res.gsl_group = 0;
3397
3398		/* unset gsl group reg field and mark resource free */
3399		switch (group_idx) {
3400		case 1:
3401			gsl.gsl0_en = 0;
3402			dc->res_pool->gsl_groups.gsl_0 = 0;
3403			break;
3404		case 2:
3405			gsl.gsl1_en = 0;
3406			dc->res_pool->gsl_groups.gsl_1 = 0;
3407			break;
3408		case 3:
3409			gsl.gsl2_en = 0;
3410			dc->res_pool->gsl_groups.gsl_2 = 0;
3411			break;
3412		default:
3413			BREAK_TO_DEBUGGER();
3414			return;
3415		}
3416		gsl.gsl_master_en = 0;
3417	}
3418
3419	hwss_add_tg_set_gsl(seq_state, pipe_ctx->stream_res.tg, gsl);
3420	hwss_add_tg_set_gsl_source_select(seq_state, pipe_ctx->stream_res.tg, group_idx, enable ? 4 : 0);
3421}
3422
3423void dcn401_disable_plane_sequence(
3424		struct dc *dc,
3425		struct dc_state *state,
3426		struct pipe_ctx *pipe_ctx,
3427		struct block_sequence_state *seq_state)
3428{
3429	bool is_phantom = dc_state_get_pipe_subvp_type(state, pipe_ctx) == SUBVP_PHANTOM;
3430	struct timing_generator *tg = is_phantom ? pipe_ctx->stream_res.tg : NULL;
3431
3432	if (!pipe_ctx->plane_res.hubp || pipe_ctx->plane_res.hubp->power_gated)
3433		return;
3434
3435	/* Wait for MPCC disconnect */
3436	if (dc->hwss.wait_for_mpcc_disconnect_sequence)
3437		dc->hwss.wait_for_mpcc_disconnect_sequence(dc, dc->res_pool, pipe_ctx, seq_state);
3438
3439	/* In flip immediate with pipe splitting case GSL is used for synchronization
3440	 * so we must disable it when the plane is disabled.
3441	 */
3442	if (pipe_ctx->stream_res.gsl_group != 0)
3443		dcn401_setup_gsl_group_as_lock_sequence(dc, pipe_ctx, false, seq_state);
3444
3445	/* Update HUBP mall sel */
3446	if (pipe_ctx->plane_res.hubp && pipe_ctx->plane_res.hubp->funcs->hubp_update_mall_sel)
3447		hwss_add_hubp_update_mall_sel(seq_state, pipe_ctx->plane_res.hubp, 0, false);
3448
3449	/* Set flip control GSL */
3450	hwss_add_hubp_set_flip_control_gsl(seq_state, pipe_ctx->plane_res.hubp, false);
3451
3452	/* HUBP clock control */
3453	hwss_add_hubp_clk_cntl(seq_state, pipe_ctx->plane_res.hubp, false);
3454
3455	/* DPP clock control */
3456	hwss_add_dpp_dppclk_control(seq_state, pipe_ctx->plane_res.dpp, false, false);
3457
3458	/* Plane atomic power down */
3459	if (dc->hwseq->funcs.plane_atomic_power_down_sequence)
3460		dc->hwseq->funcs.plane_atomic_power_down_sequence(dc, pipe_ctx->plane_res.dpp,
3461			pipe_ctx->plane_res.hubp, seq_state);
3462
3463	pipe_ctx->stream = NULL;
3464	memset(&pipe_ctx->stream_res, 0, sizeof(pipe_ctx->stream_res));
3465	memset(&pipe_ctx->plane_res, 0, sizeof(pipe_ctx->plane_res));
3466	pipe_ctx->top_pipe = NULL;
3467	pipe_ctx->bottom_pipe = NULL;
3468	pipe_ctx->prev_odm_pipe = NULL;
3469	pipe_ctx->next_odm_pipe = NULL;
3470	pipe_ctx->plane_state = NULL;
3471
3472	/* Turn back off the phantom OTG after the phantom plane is fully disabled */
3473	if (is_phantom && tg && tg->funcs->disable_phantom_crtc)
3474		hwss_add_disable_phantom_crtc(seq_state, tg);
3475}
3476
3477void dcn401_post_unlock_reset_opp_sequence(
3478		struct dc *dc,
3479		struct pipe_ctx *opp_head,
3480		struct block_sequence_state *seq_state)
3481{
3482	struct display_stream_compressor *dsc = opp_head->stream_res.dsc;
3483	struct dccg *dccg = dc->res_pool->dccg;
3484
3485	/* Wait for all DPP pipes in current mpc blending tree completes double
3486	 * buffered disconnection before resetting OPP
3487	 */
3488	if (dc->hwss.wait_for_mpcc_disconnect_sequence)
3489		dc->hwss.wait_for_mpcc_disconnect_sequence(dc, dc->res_pool, opp_head, seq_state);
3490
3491	if (dsc) {
3492		bool *is_ungated = NULL;
3493		/* Check DSC power gate status */
3494		if (dc->hwseq && dc->hwseq->funcs.dsc_pg_status)
3495			hwss_add_dsc_pg_status(seq_state, dc->hwseq, dsc->inst, false);
3496
3497		/* Seamless update specific where we will postpone non
3498		 * double buffered DSCCLK disable logic in post unlock
3499		 * sequence after DSC is disconnected from OPP but not
3500		 * yet power gated.
3501		 */
3502
3503		/* DSC wait disconnect pending clear */
3504		hwss_add_dsc_wait_disconnect_pending_clear(seq_state, dsc, is_ungated);
3505
3506		/* DSC disable */
3507		hwss_add_dsc_disable(seq_state, dsc, is_ungated);
3508
3509		/* Set reference DSCCLK */
3510		if (dccg && dccg->funcs->set_ref_dscclk)
3511			hwss_add_dccg_set_ref_dscclk(seq_state, dccg, dsc->inst, 0);
3512	}
3513}
3514
3515void dcn401_dc_ip_request_cntl(struct dc *dc, bool enable)
3516{
3517	struct dce_hwseq *hws = dc->hwseq;
3518
3519	if (REG(DC_IP_REQUEST_CNTL))
3520		REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, enable ? 1 : 0);
3521}
3522
3523void dcn401_enable_plane_sequence(struct dc *dc, struct pipe_ctx *pipe_ctx,
3524				 struct dc_state *context,
3525				 struct block_sequence_state *seq_state)
3526{
3527	struct dce_hwseq *hws = dc->hwseq;
3528	uint32_t org_ip_request_cntl = 0;
3529
3530	if (!pipe_ctx->plane_res.dpp || !pipe_ctx->plane_res.hubp || !pipe_ctx->stream_res.opp)
3531		return;
3532
3533	if (REG(DC_IP_REQUEST_CNTL))
3534		REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
3535
3536	/* Step 1: DPP root clock control - enable clock */
3537	if (hws->funcs.dpp_root_clock_control)
3538		hwss_add_dpp_root_clock_control(seq_state, hws, pipe_ctx->plane_res.dpp->inst, true);
3539
3540	/* Step 2: Enable DC IP request (if needed) */
3541	if (hws->funcs.dc_ip_request_cntl)
3542		hwss_add_dc_ip_request_cntl(seq_state, dc, true);
3543
3544	/* Step 3: DPP power gating control - power on */
3545	if (REG(DC_IP_REQUEST_CNTL) && hws->funcs.dpp_pg_control)
3546		hwss_add_dpp_pg_control(seq_state, hws, pipe_ctx->plane_res.dpp->inst, true);
3547
3548	/* Step 4: HUBP power gating control - power on */
3549	if (REG(DC_IP_REQUEST_CNTL) && hws->funcs.hubp_pg_control)
3550		hwss_add_hubp_pg_control(seq_state, hws, pipe_ctx->plane_res.hubp->inst, true);
3551
3552	/* Step 5: Disable DC IP request (restore state) */
3553	if (org_ip_request_cntl == 0 && hws->funcs.dc_ip_request_cntl)
3554		hwss_add_dc_ip_request_cntl(seq_state, dc, false);
3555
3556	/* Step 6: HUBP clock control - enable DCFCLK */
3557	if (pipe_ctx->plane_res.hubp->funcs->hubp_clk_cntl)
3558		hwss_add_hubp_clk_cntl(seq_state, pipe_ctx->plane_res.hubp, true);
3559
3560	/* Step 7: HUBP initialization */
3561	if (pipe_ctx->plane_res.hubp->funcs->hubp_init)
3562		hwss_add_hubp_init(seq_state, pipe_ctx->plane_res.hubp);
3563
3564	/* Step 8: OPP pipe clock control - enable */
3565	if (pipe_ctx->stream_res.opp->funcs->opp_pipe_clock_control)
3566		hwss_add_opp_pipe_clock_control(seq_state, pipe_ctx->stream_res.opp, true);
3567
3568	/* Step 9: VM system aperture settings */
3569	if (dc->vm_pa_config.valid && pipe_ctx->plane_res.hubp->funcs->hubp_set_vm_system_aperture_settings) {
3570		hwss_add_hubp_set_vm_system_aperture_settings(seq_state, pipe_ctx->plane_res.hubp, 0,
3571			dc->vm_pa_config.system_aperture.start_addr, dc->vm_pa_config.system_aperture.end_addr);
3572	}
3573
3574	/* Step 10: Flip interrupt setup */
3575	if (!pipe_ctx->top_pipe
3576			&& pipe_ctx->plane_state
3577			&& pipe_ctx->plane_state->flip_int_enabled
3578			&& pipe_ctx->plane_res.hubp->funcs->hubp_set_flip_int) {
3579		hwss_add_hubp_set_flip_int(seq_state, pipe_ctx->plane_res.hubp);
3580	}
3581}
3582
3583void dcn401_update_dchubp_dpp_sequence(struct dc *dc,
3584				       struct pipe_ctx *pipe_ctx,
3585				       struct dc_state *context,
3586				       struct block_sequence_state *seq_state)
3587{
3588	struct dce_hwseq *hws = dc->hwseq;
3589	struct hubp *hubp = pipe_ctx->plane_res.hubp;
3590	struct dpp *dpp = pipe_ctx->plane_res.dpp;
3591	struct dc_plane_state *plane_state = pipe_ctx->plane_state;
3592	struct dccg *dccg = dc->res_pool->dccg;
3593	bool viewport_changed = false;
3594	enum mall_stream_type pipe_mall_type = dc_state_get_pipe_subvp_type(context, pipe_ctx);
3595
3596	if (!hubp || !dpp || !plane_state)
3597		return;
3598
3599	/* Step 1: DPP DPPCLK control */
3600	if (pipe_ctx->update_flags.bits.dppclk)
3601		hwss_add_dpp_dppclk_control(seq_state, dpp, false, true);
3602
3603	/* Step 2: DCCG update DPP DTO */
3604	if (pipe_ctx->update_flags.bits.enable)
3605		hwss_add_dccg_update_dpp_dto(seq_state, dccg, dpp->inst, pipe_ctx->plane_res.bw.dppclk_khz);
3606
3607	/* Step 3: HUBP VTG selection */
3608	if (pipe_ctx->update_flags.bits.hubp_rq_dlg_ttu) {
3609		hwss_add_hubp_vtg_sel(seq_state, hubp, pipe_ctx->stream_res.tg->inst);
3610
3611		/* Step 4: HUBP setup (choose setup2 or setup) */
3612		if (hubp->funcs->hubp_setup2) {
3613			hwss_add_hubp_setup2(seq_state, hubp, &pipe_ctx->hubp_regs,
3614				&pipe_ctx->global_sync, &pipe_ctx->stream->timing);
3615		} else if (hubp->funcs->hubp_setup) {
3616			hwss_add_hubp_setup(seq_state, hubp, &pipe_ctx->dlg_regs,
3617				&pipe_ctx->ttu_regs, &pipe_ctx->rq_regs, &pipe_ctx->pipe_dlg_param);
3618		}
3619	}
3620
3621	/* Step 5: Set unbounded requesting */
3622	if (pipe_ctx->update_flags.bits.unbounded_req && hubp->funcs->set_unbounded_requesting)
3623		hwss_add_hubp_set_unbounded_requesting(seq_state, hubp, pipe_ctx->unbounded_req);
3624
3625	/* Step 6: HUBP interdependent setup */
3626	if (pipe_ctx->update_flags.bits.hubp_interdependent) {
3627		if (hubp->funcs->hubp_setup_interdependent2)
3628			hwss_add_hubp_setup_interdependent2(seq_state, hubp, &pipe_ctx->hubp_regs);
3629		else if (hubp->funcs->hubp_setup_interdependent)
3630			hwss_add_hubp_setup_interdependent(seq_state, hubp, &pipe_ctx->dlg_regs, &pipe_ctx->ttu_regs);
3631	}
3632
3633	/* Step 7: DPP setup - input CSC and format setup */
3634	if (pipe_ctx->update_flags.bits.enable ||
3635			pipe_ctx->update_flags.bits.plane_changed ||
3636			plane_state->update_flags.bits.bpp_change ||
3637			plane_state->update_flags.bits.input_csc_change ||
3638			plane_state->update_flags.bits.color_space_change ||
3639			plane_state->update_flags.bits.coeff_reduction_change) {
3640		hwss_add_dpp_setup_dpp(seq_state, pipe_ctx);
3641
3642		/* Step 8: DPP cursor matrix setup */
3643		if (dpp->funcs->set_cursor_matrix) {
3644			hwss_add_dpp_set_cursor_matrix(seq_state, dpp, plane_state->color_space,
3645				&plane_state->cursor_csc_color_matrix);
3646		}
3647
3648		/* Step 9: DPP program bias and scale */
3649		if (dpp->funcs->dpp_program_bias_and_scale)
3650			hwss_add_dpp_program_bias_and_scale(seq_state, pipe_ctx);
3651	}
3652
3653	/* Step 10: MPCC updates */
3654	if (pipe_ctx->update_flags.bits.mpcc ||
3655	     pipe_ctx->update_flags.bits.plane_changed ||
3656	     plane_state->update_flags.bits.global_alpha_change ||
3657	     plane_state->update_flags.bits.per_pixel_alpha_change) {
3658
3659		/* Check if update_mpcc_sequence is implemented and prefer it over single MPC_UPDATE_MPCC step */
3660		if (hws->funcs.update_mpcc_sequence)
3661			hws->funcs.update_mpcc_sequence(dc, pipe_ctx, seq_state);
3662	}
3663
3664	/* Step 11: DPP scaler setup */
3665	if (pipe_ctx->update_flags.bits.scaler ||
3666			plane_state->update_flags.bits.scaling_change ||
3667			plane_state->update_flags.bits.position_change ||
3668			plane_state->update_flags.bits.per_pixel_alpha_change ||
3669			pipe_ctx->stream->update_flags.bits.scaling) {
3670		pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->plane_state->per_pixel_alpha;
3671		ASSERT(pipe_ctx->plane_res.scl_data.lb_params.depth == LB_PIXEL_DEPTH_36BPP);
3672		hwss_add_dpp_set_scaler(seq_state, pipe_ctx->plane_res.dpp, &pipe_ctx->plane_res.scl_data);
3673	}
3674
3675	/* Step 12: HUBP viewport programming */
3676	if (pipe_ctx->update_flags.bits.viewport ||
3677	     (context == dc->current_state && plane_state->update_flags.bits.position_change) ||
3678	     (context == dc->current_state && plane_state->update_flags.bits.scaling_change) ||
3679	     (context == dc->current_state && pipe_ctx->stream->update_flags.bits.scaling)) {
3680		hwss_add_hubp_mem_program_viewport(seq_state, hubp,
3681			&pipe_ctx->plane_res.scl_data.viewport, &pipe_ctx->plane_res.scl_data.viewport_c);
3682		viewport_changed = true;
3683	}
3684
3685	/* Step 13: HUBP program mcache if available */
3686	if (hubp->funcs->hubp_program_mcache_id_and_split_coordinate)
3687		hwss_add_hubp_program_mcache_id(seq_state, hubp, &pipe_ctx->mcache_regs);
3688
3689	/* Step 14: Cursor attribute setup */
3690	if ((pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed ||
3691	     pipe_ctx->update_flags.bits.scaler || viewport_changed == true) &&
3692	    pipe_ctx->stream->cursor_attributes.address.quad_part != 0) {
3693
3694		hwss_add_abort_cursor_offload_update(seq_state, dc, pipe_ctx);
3695
3696		hwss_add_set_cursor_attribute(seq_state, dc, pipe_ctx);
3697
3698		/* Step 15: Cursor position setup */
3699		hwss_add_set_cursor_position(seq_state, dc, pipe_ctx);
3700
3701		/* Step 16: Cursor SDR white level */
3702		if (dc->hwss.set_cursor_sdr_white_level)
3703			hwss_add_set_cursor_sdr_white_level(seq_state, dc, pipe_ctx);
3704	}
3705
3706	/* Step 17: Gamut remap and output CSC */
3707	if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed ||
3708			pipe_ctx->update_flags.bits.plane_changed ||
3709			pipe_ctx->stream->update_flags.bits.gamut_remap ||
3710			plane_state->update_flags.bits.gamut_remap_change ||
3711			pipe_ctx->stream->update_flags.bits.out_csc) {
3712
3713		/* Gamut remap */
3714		hwss_add_dpp_program_gamut_remap(seq_state, pipe_ctx);
3715
3716		/* Output CSC */
3717		hwss_add_program_output_csc(seq_state, dc, pipe_ctx, pipe_ctx->stream->output_color_space,
3718			pipe_ctx->stream->csc_color_matrix.matrix, hubp->opp_id);
3719	}
3720
3721	/* Step 18: HUBP surface configuration */
3722	if (pipe_ctx->update_flags.bits.enable ||
3723			pipe_ctx->update_flags.bits.plane_changed ||
3724			pipe_ctx->update_flags.bits.opp_changed ||
3725			plane_state->update_flags.bits.pixel_format_change ||
3726			plane_state->update_flags.bits.horizontal_mirror_change ||
3727			plane_state->update_flags.bits.rotation_change ||
3728			plane_state->update_flags.bits.swizzle_change ||
3729			plane_state->update_flags.bits.dcc_change ||
3730			plane_state->update_flags.bits.bpp_change ||
3731			plane_state->update_flags.bits.scaling_change ||
3732			plane_state->update_flags.bits.plane_size_change) {
3733		struct plane_size size = plane_state->plane_size;
3734
3735		size.surface_size = pipe_ctx->plane_res.scl_data.viewport;
3736		hwss_add_hubp_program_surface_config(seq_state, hubp,
3737				plane_state->format, &plane_state->tiling_info, size,
3738				plane_state->rotation, &plane_state->dcc,
3739				plane_state->horizontal_mirror, 0);
3740		hubp->power_gated = false;
3741	}
3742
3743	/* Step 19: Update plane address (with SubVP support) */
3744	if (pipe_ctx->update_flags.bits.enable ||
3745	     pipe_ctx->update_flags.bits.plane_changed ||
3746	     plane_state->update_flags.bits.addr_update) {
3747
3748		/* SubVP save surface address if needed */
3749		if (resource_is_pipe_type(pipe_ctx, OTG_MASTER) && pipe_mall_type == SUBVP_MAIN) {
3750			hwss_add_dmub_subvp_save_surf_addr(seq_state, dc->ctx->dmub_srv,
3751				&pipe_ctx->plane_state->address, pipe_ctx->subvp_index);
3752		}
3753
3754		/* Update plane address */
3755		hwss_add_hubp_update_plane_addr(seq_state, dc, pipe_ctx);
3756	}
3757
3758	/* Step 20: HUBP set blank - enable plane */
3759	if (pipe_ctx->update_flags.bits.enable)
3760		hwss_add_hubp_set_blank(seq_state, hubp, false);
3761
3762	/* Step 21: Phantom HUBP post enable */
3763	if (pipe_mall_type == SUBVP_PHANTOM && hubp->funcs->phantom_hubp_post_enable)
3764		hwss_add_phantom_hubp_post_enable(seq_state, hubp);
3765}
3766
3767void dcn401_update_mpcc_sequence(struct dc *dc,
3768				struct pipe_ctx *pipe_ctx,
3769				struct block_sequence_state *seq_state)
3770{
3771	struct hubp *hubp = pipe_ctx->plane_res.hubp;
3772	struct mpcc_blnd_cfg blnd_cfg = {0};
3773	bool per_pixel_alpha;
3774	int mpcc_id;
3775	struct mpcc *new_mpcc;
3776	struct mpc *mpc = dc->res_pool->mpc;
3777	struct mpc_tree *mpc_tree_params = &(pipe_ctx->stream_res.opp->mpc_tree_params);
3778
3779	if (!hubp || !pipe_ctx->plane_state)
3780		return;
3781
3782	per_pixel_alpha = pipe_ctx->plane_state->per_pixel_alpha;
3783
3784	/* Initialize blend configuration */
3785	blnd_cfg.overlap_only = false;
3786	blnd_cfg.global_gain = 0xff;
3787
3788	if (per_pixel_alpha) {
3789		blnd_cfg.pre_multiplied_alpha = pipe_ctx->plane_state->pre_multiplied_alpha;
3790		if (pipe_ctx->plane_state->global_alpha) {
3791			blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA_COMBINED_GLOBAL_GAIN;
3792			blnd_cfg.global_gain = pipe_ctx->plane_state->global_alpha_value;
3793		} else {
3794			blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA;
3795		}
3796	} else {
3797		blnd_cfg.pre_multiplied_alpha = false;
3798		blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_GLOBAL_ALPHA;
3799	}
3800
3801	if (pipe_ctx->plane_state->global_alpha)
3802		blnd_cfg.global_alpha = pipe_ctx->plane_state->global_alpha_value;
3803	else
3804		blnd_cfg.global_alpha = 0xff;
3805
3806	blnd_cfg.background_color_bpc = 4;
3807	blnd_cfg.bottom_gain_mode = 0;
3808	blnd_cfg.top_gain = 0x1f000;
3809	blnd_cfg.bottom_inside_gain = 0x1f000;
3810	blnd_cfg.bottom_outside_gain = 0x1f000;
3811
3812	if (pipe_ctx->plane_state->format == SURFACE_PIXEL_FORMAT_GRPH_RGBE_ALPHA)
3813		blnd_cfg.pre_multiplied_alpha = false;
3814
3815	/* MPCC instance is equal to HUBP instance */
3816	mpcc_id = hubp->inst;
3817
3818	/* Step 1: Update blending if no full update needed */
3819	if (!pipe_ctx->plane_state->update_flags.bits.full_update &&
3820	    !pipe_ctx->update_flags.bits.mpcc) {
3821
3822		/* Update blending configuration */
3823		hwss_add_mpc_update_blending(seq_state, mpc, blnd_cfg, mpcc_id);
3824
3825		/* Update visual confirm color */
3826		hwss_add_mpc_update_visual_confirm(seq_state, dc, pipe_ctx, mpcc_id);
3827		return;
3828	}
3829
3830	/* Step 2: Get existing MPCC for DPP */
3831	new_mpcc = mpc->funcs->get_mpcc_for_dpp(mpc_tree_params, mpcc_id);
3832
3833	/* Step 3: Remove MPCC if being used */
3834	if (new_mpcc != NULL) {
3835		hwss_add_mpc_remove_mpcc(seq_state, mpc, mpc_tree_params, new_mpcc);
3836	} else {
3837		/* Step 4: Assert MPCC idle (debug only) */
3838		if (dc->debug.sanity_checks)
3839			hwss_add_mpc_assert_idle_mpcc(seq_state, mpc, mpcc_id);
3840	}
3841
3842	/* Step 5: Insert new plane into MPC tree */
3843	hwss_add_mpc_insert_plane(seq_state, mpc, mpc_tree_params, blnd_cfg, NULL, NULL, hubp->inst, mpcc_id);
3844
3845	/* Step 6: Update visual confirm color */
3846	hwss_add_mpc_update_visual_confirm(seq_state, dc, pipe_ctx, mpcc_id);
3847
3848	/* Step 7: Set HUBP OPP and MPCC IDs */
3849	hubp->opp_id = pipe_ctx->stream_res.opp->inst;
3850	hubp->mpcc_id = mpcc_id;
3851}
3852
3853static struct hubp *get_hubp_by_inst(struct resource_pool *res_pool, int mpcc_inst)
3854{
3855	int i;
3856
3857	for (i = 0; i < res_pool->pipe_count; i++) {
3858		if (res_pool->hubps[i]->inst == mpcc_inst)
3859			return res_pool->hubps[i];
3860	}
3861	ASSERT(false);
3862	return NULL;
3863}
3864
3865void dcn401_wait_for_mpcc_disconnect_sequence(
3866		struct dc *dc,
3867		struct resource_pool *res_pool,
3868		struct pipe_ctx *pipe_ctx,
3869		struct block_sequence_state *seq_state)
3870{
3871	int mpcc_inst;
3872
3873	if (dc->debug.sanity_checks)
3874		dc->hwseq->funcs.verify_allow_pstate_change_high_sequence(dc, seq_state);
3875
3876	if (!pipe_ctx->stream_res.opp)
3877		return;
3878
3879	for (mpcc_inst = 0; mpcc_inst < MAX_PIPES; mpcc_inst++) {
3880		if (pipe_ctx->stream_res.opp->mpcc_disconnect_pending[mpcc_inst]) {
3881			struct hubp *hubp = get_hubp_by_inst(res_pool, mpcc_inst);
3882
3883			if (pipe_ctx->stream_res.tg &&
3884				pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) {
3885				hwss_add_mpc_assert_idle_mpcc(seq_state, res_pool->mpc, mpcc_inst);
3886			}
3887			pipe_ctx->stream_res.opp->mpcc_disconnect_pending[mpcc_inst] = false;
3888			if (hubp)
3889				hwss_add_hubp_set_blank(seq_state, hubp, true);
3890		}
3891	}
3892
3893	if (dc->debug.sanity_checks)
3894		dc->hwseq->funcs.verify_allow_pstate_change_high_sequence(dc, seq_state);
3895}
3896
3897void dcn401_setup_vupdate_interrupt_sequence(struct dc *dc, struct pipe_ctx *pipe_ctx,
3898		struct block_sequence_state *seq_state)
3899{
3900	struct timing_generator *tg = pipe_ctx->stream_res.tg;
3901	int start_line = dc->hwss.get_vupdate_offset_from_vsync(pipe_ctx);
3902
3903	if (start_line < 0)
3904		start_line = 0;
3905
3906	if (tg->funcs->setup_vertical_interrupt2)
3907		hwss_add_tg_setup_vertical_interrupt2(seq_state, tg, start_line);
3908}
3909
3910void dcn401_set_hdr_multiplier_sequence(struct pipe_ctx *pipe_ctx,
3911		struct block_sequence_state *seq_state)
3912{
3913	struct fixed31_32 multiplier = pipe_ctx->plane_state->hdr_mult;
3914	uint32_t hw_mult = 0x1f000; // 1.0 default multiplier
3915	struct custom_float_format fmt;
3916
3917	fmt.exponenta_bits = 6;
3918	fmt.mantissa_bits = 12;
3919	fmt.sign = true;
3920
3921	if (!dc_fixpt_eq(multiplier, dc_fixpt_from_int(0))) // check != 0
3922		convert_to_custom_float_format(multiplier, &fmt, &hw_mult);
3923
3924	hwss_add_dpp_set_hdr_multiplier(seq_state, pipe_ctx->plane_res.dpp, hw_mult);
3925}
3926
3927void dcn401_program_mall_pipe_config_sequence(struct dc *dc, struct dc_state *context,
3928		struct block_sequence_state *seq_state)
3929{
3930	int i;
3931	unsigned int num_ways = dcn401_calculate_cab_allocation(dc, context);
3932	bool cache_cursor = false;
3933
3934	// Don't force p-state disallow -- can't block dummy p-state
3935
3936	// Update MALL_SEL register for each pipe (break down update_mall_sel call)
3937	for (i = 0; i < dc->res_pool->pipe_count; i++) {
3938		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
3939		struct hubp *hubp = pipe->plane_res.hubp;
3940
3941		if (pipe->stream && pipe->plane_state && hubp && hubp->funcs->hubp_update_mall_sel) {
3942			int cursor_size = hubp->curs_attr.pitch * hubp->curs_attr.height;
3943
3944			switch (hubp->curs_attr.color_format) {
3945			case CURSOR_MODE_MONO:
3946				cursor_size /= 2;
3947				break;
3948			case CURSOR_MODE_COLOR_1BIT_AND:
3949			case CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA:
3950			case CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA:
3951				cursor_size *= 4;
3952				break;
3953
3954			case CURSOR_MODE_COLOR_64BIT_FP_PRE_MULTIPLIED:
3955			case CURSOR_MODE_COLOR_64BIT_FP_UN_PRE_MULTIPLIED:
3956			default:
3957				cursor_size *= 8;
3958				break;
3959			}
3960
3961			if (cursor_size > 16384)
3962				cache_cursor = true;
3963
3964			if (dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM) {
3965				hwss_add_hubp_update_mall_sel(seq_state, hubp, 1, false);
3966			} else {
3967				// MALL not supported with Stereo3D
3968				uint32_t mall_sel = (num_ways <= dc->caps.cache_num_ways &&
3969					pipe->stream->link->psr_settings.psr_version == DC_PSR_VERSION_UNSUPPORTED &&
3970					pipe->plane_state->address.type != PLN_ADDR_TYPE_GRPH_STEREO &&
3971					!pipe->plane_state->address.tmz_surface) ? 2 : 0;
3972				hwss_add_hubp_update_mall_sel(seq_state, hubp, mall_sel, cache_cursor);
3973			}
3974		}
3975	}
3976
3977	// Program FORCE_ONE_ROW_FOR_FRAME and CURSOR_REQ_MODE for main subvp pipes
3978	for (i = 0; i < dc->res_pool->pipe_count; i++) {
3979		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
3980		struct hubp *hubp = pipe->plane_res.hubp;
3981
3982		if (pipe->stream && hubp && hubp->funcs->hubp_prepare_subvp_buffering) {
3983			if (dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_MAIN)
3984				hwss_add_hubp_prepare_subvp_buffering(seq_state, hubp, true);
3985		}
3986	}
3987}
3988
3989void dcn401_verify_allow_pstate_change_high_sequence(struct dc *dc,
3990		struct block_sequence_state *seq_state)
3991{
3992	struct hubbub *hubbub = dc->res_pool->hubbub;
3993
3994	if (!hubbub->funcs->verify_allow_pstate_change_high)
3995		return;
3996
3997	if (!hubbub->funcs->verify_allow_pstate_change_high(hubbub)) {
3998		/* Attempt hardware workaround force recovery */
3999		dcn401_hw_wa_force_recovery_sequence(dc, seq_state);
4000	}
4001}
4002
4003bool dcn401_hw_wa_force_recovery_sequence(struct dc *dc,
4004		struct block_sequence_state *seq_state)
4005{
4006	struct hubp *hubp;
4007	unsigned int i;
4008
4009	if (!dc->debug.recovery_enabled)
4010		return false;
4011
4012	/* Step 1: Set HUBP_BLANK_EN=1 for all active pipes */
4013	for (i = 0; i < dc->res_pool->pipe_count; i++) {
4014		struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
4015
4016		if (pipe_ctx != NULL) {
4017			hubp = pipe_ctx->plane_res.hubp;
4018			if (hubp != NULL && hubp->funcs->set_hubp_blank_en)
4019				hwss_add_hubp_set_blank_en(seq_state, hubp, true);
4020		}
4021	}
4022
4023	/* Step 2: DCHUBBUB_GLOBAL_SOFT_RESET=1 */
4024	hwss_add_hubbub_soft_reset(seq_state, dc->res_pool->hubbub, hubbub1_soft_reset, true);
4025
4026	/* Step 3: Set HUBP_DISABLE=1 for all active pipes */
4027	for (i = 0; i < dc->res_pool->pipe_count; i++) {
4028		struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
4029
4030		if (pipe_ctx != NULL) {
4031			hubp = pipe_ctx->plane_res.hubp;
4032			if (hubp != NULL && hubp->funcs->hubp_disable_control)
4033				hwss_add_hubp_disable_control(seq_state, hubp, true);
4034		}
4035	}
4036
4037	/* Step 4: Set HUBP_DISABLE=0 for all active pipes */
4038	for (i = 0; i < dc->res_pool->pipe_count; i++) {
4039		struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
4040
4041		if (pipe_ctx != NULL) {
4042			hubp = pipe_ctx->plane_res.hubp;
4043			if (hubp != NULL && hubp->funcs->hubp_disable_control)
4044				hwss_add_hubp_disable_control(seq_state, hubp, false);
4045		}
4046	}
4047
4048	/* Step 5: DCHUBBUB_GLOBAL_SOFT_RESET=0 */
4049	hwss_add_hubbub_soft_reset(seq_state, dc->res_pool->hubbub, hubbub1_soft_reset, false);
4050
4051	/* Step 6: Set HUBP_BLANK_EN=0 for all active pipes */
4052	for (i = 0; i < dc->res_pool->pipe_count; i++) {
4053		struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
4054
4055		if (pipe_ctx != NULL) {
4056			hubp = pipe_ctx->plane_res.hubp;
4057			if (hubp != NULL && hubp->funcs->set_hubp_blank_en)
4058				hwss_add_hubp_set_blank_en(seq_state, hubp, false);
4059		}
4060	}
4061
4062	return true;
4063}
~/ Read-only file preview.