Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.9.4.
   1/*
   2 * Copyright 2020 Advanced Micro Devices, Inc.
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice shall be included in
  12 * all copies or substantial portions of the Software.
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20 * OTHER DEALINGS IN THE SOFTWARE.
  21 *
  22 * Authors: AMD
  23 *
  24 */
  25
  26
  27#include "dm_services.h"
  28#include "dm_helpers.h"
  29#include "core_types.h"
  30#include "resource.h"
  31#include "dcn30_hwseq.h"
  32#include "dccg.h"
  33#include "dce/dce_hwseq.h"
  34#include "dcn30_mpc.h"
  35#include "dcn30_dpp.h"
  36#include "dcn10/dcn10_cm_common.h"
  37#include "dcn30_cm_common.h"
  38#include "reg_helper.h"
  39#include "abm.h"
  40#include "clk_mgr.h"
  41#include "hubp.h"
  42#include "dchubbub.h"
  43#include "timing_generator.h"
  44#include "opp.h"
  45#include "ipp.h"
  46#include "mpc.h"
  47#include "mcif_wb.h"
  48#include "dc_dmub_srv.h"
  49#include "link_hwss.h"
  50#include "dpcd_defs.h"
  51#include "inc/dc_link_dp.h"
  52#include "inc/link_dpcd.h"
  53
  54
  55
  56
  57#define DC_LOGGER_INIT(logger)
  58
  59#define CTX \
  60	hws->ctx
  61#define REG(reg)\
  62	hws->regs->reg
  63#define DC_LOGGER \
  64		dc->ctx->logger
  65
  66
  67#undef FN
  68#define FN(reg_name, field_name) \
  69	hws->shifts->field_name, hws->masks->field_name
  70
  71bool dcn30_set_blend_lut(
  72	struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state)
  73{
  74	struct dpp *dpp_base = pipe_ctx->plane_res.dpp;
  75	bool result = true;
  76	struct pwl_params *blend_lut = NULL;
  77
  78	if (plane_state->blend_tf) {
  79		if (plane_state->blend_tf->type == TF_TYPE_HWPWL)
  80			blend_lut = &plane_state->blend_tf->pwl;
  81		else if (plane_state->blend_tf->type == TF_TYPE_DISTRIBUTED_POINTS) {
  82			cm3_helper_translate_curve_to_hw_format(
  83					plane_state->blend_tf, &dpp_base->regamma_params, false);
  84			blend_lut = &dpp_base->regamma_params;
  85		}
  86	}
  87	result = dpp_base->funcs->dpp_program_blnd_lut(dpp_base, blend_lut);
  88
  89	return result;
  90}
  91
  92static bool dcn30_set_mpc_shaper_3dlut(
  93	struct pipe_ctx *pipe_ctx, const struct dc_stream_state *stream)
  94{
  95	struct dpp *dpp_base = pipe_ctx->plane_res.dpp;
  96	int mpcc_id = pipe_ctx->plane_res.hubp->inst;
  97	struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc;
  98	bool result = false;
  99	int acquired_rmu = 0;
 100	int mpcc_id_projected = 0;
 101
 102	const struct pwl_params *shaper_lut = NULL;
 103	//get the shaper lut params
 104	if (stream->func_shaper) {
 105		if (stream->func_shaper->type == TF_TYPE_HWPWL)
 106			shaper_lut = &stream->func_shaper->pwl;
 107		else if (stream->func_shaper->type == TF_TYPE_DISTRIBUTED_POINTS) {
 108			cm_helper_translate_curve_to_hw_format(
 109					stream->func_shaper,
 110					&dpp_base->shaper_params, true);
 111			shaper_lut = &dpp_base->shaper_params;
 112		}
 113	}
 114
 115	if (stream->lut3d_func &&
 116		stream->lut3d_func->state.bits.initialized == 1 &&
 117		stream->lut3d_func->state.bits.rmu_idx_valid == 1) {
 118		if (stream->lut3d_func->state.bits.rmu_mux_num == 0)
 119			mpcc_id_projected = stream->lut3d_func->state.bits.mpc_rmu0_mux;
 120		else if (stream->lut3d_func->state.bits.rmu_mux_num == 1)
 121			mpcc_id_projected = stream->lut3d_func->state.bits.mpc_rmu1_mux;
 122		else if (stream->lut3d_func->state.bits.rmu_mux_num == 2)
 123			mpcc_id_projected = stream->lut3d_func->state.bits.mpc_rmu2_mux;
 124		if (mpcc_id_projected != mpcc_id)
 125			BREAK_TO_DEBUGGER();
 126		/*find the reason why logical layer assigned a differant mpcc_id into acquire_post_bldn_3dlut*/
 127		acquired_rmu = mpc->funcs->acquire_rmu(mpc, mpcc_id,
 128				stream->lut3d_func->state.bits.rmu_mux_num);
 129		if (acquired_rmu != stream->lut3d_func->state.bits.rmu_mux_num)
 130			BREAK_TO_DEBUGGER();
 131		result = mpc->funcs->program_3dlut(mpc,
 132								&stream->lut3d_func->lut_3d,
 133								stream->lut3d_func->state.bits.rmu_mux_num);
 134		result = mpc->funcs->program_shaper(mpc, shaper_lut,
 135				stream->lut3d_func->state.bits.rmu_mux_num);
 136	} else
 137		/*loop through the available mux and release the requested mpcc_id*/
 138		mpc->funcs->release_rmu(mpc, mpcc_id);
 139
 140
 141	return result;
 142}
 143
 144bool dcn30_set_input_transfer_func(struct dc *dc,
 145				struct pipe_ctx *pipe_ctx,
 146				const struct dc_plane_state *plane_state)
 147{
 148	struct dce_hwseq *hws = dc->hwseq;
 149	struct dpp *dpp_base = pipe_ctx->plane_res.dpp;
 150	enum dc_transfer_func_predefined tf;
 151	bool result = true;
 152	struct pwl_params *params = NULL;
 153
 154	if (dpp_base == NULL || plane_state == NULL)
 155		return false;
 156
 157	tf = TRANSFER_FUNCTION_UNITY;
 158
 159	if (plane_state->in_transfer_func &&
 160		plane_state->in_transfer_func->type == TF_TYPE_PREDEFINED)
 161		tf = plane_state->in_transfer_func->tf;
 162
 163	dpp_base->funcs->dpp_set_pre_degam(dpp_base, tf);
 164
 165	if (plane_state->in_transfer_func) {
 166		if (plane_state->in_transfer_func->type == TF_TYPE_HWPWL)
 167			params = &plane_state->in_transfer_func->pwl;
 168		else if (plane_state->in_transfer_func->type == TF_TYPE_DISTRIBUTED_POINTS &&
 169			cm3_helper_translate_curve_to_hw_format(plane_state->in_transfer_func,
 170					&dpp_base->degamma_params, false))
 171			params = &dpp_base->degamma_params;
 172	}
 173
 174	result = dpp_base->funcs->dpp_program_gamcor_lut(dpp_base, params);
 175
 176	if (pipe_ctx->stream_res.opp && pipe_ctx->stream_res.opp->ctx) {
 177		if (dpp_base->funcs->dpp_program_blnd_lut)
 178			hws->funcs.set_blend_lut(pipe_ctx, plane_state);
 179		if (dpp_base->funcs->dpp_program_shaper_lut &&
 180				dpp_base->funcs->dpp_program_3dlut)
 181			hws->funcs.set_shaper_3dlut(pipe_ctx, plane_state);
 182	}
 183
 184	return result;
 185}
 186
 187bool dcn30_set_output_transfer_func(struct dc *dc,
 188				struct pipe_ctx *pipe_ctx,
 189				const struct dc_stream_state *stream)
 190{
 191	int mpcc_id = pipe_ctx->plane_res.hubp->inst;
 192	struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc;
 193	struct pwl_params *params = NULL;
 194	bool ret = false;
 195
 196	/* program OGAM or 3DLUT only for the top pipe*/
 197	if (pipe_ctx->top_pipe == NULL) {
 198		/*program rmu shaper and 3dlut in MPC*/
 199		ret = dcn30_set_mpc_shaper_3dlut(pipe_ctx, stream);
 200		if (ret == false && mpc->funcs->set_output_gamma && stream->out_transfer_func) {
 201			if (stream->out_transfer_func->type == TF_TYPE_HWPWL)
 202				params = &stream->out_transfer_func->pwl;
 203			else if (pipe_ctx->stream->out_transfer_func->type ==
 204					TF_TYPE_DISTRIBUTED_POINTS &&
 205					cm3_helper_translate_curve_to_hw_format(
 206					stream->out_transfer_func,
 207					&mpc->blender_params, false))
 208				params = &mpc->blender_params;
 209			 /* there are no ROM LUTs in OUTGAM */
 210			if (stream->out_transfer_func->type == TF_TYPE_PREDEFINED)
 211				BREAK_TO_DEBUGGER();
 212		}
 213	}
 214
 215	mpc->funcs->set_output_gamma(mpc, mpcc_id, params);
 216	return ret;
 217}
 218
 219static void dcn30_set_writeback(
 220		struct dc *dc,
 221		struct dc_writeback_info *wb_info,
 222		struct dc_state *context)
 223{
 224	struct mcif_wb *mcif_wb;
 225	struct mcif_buf_params *mcif_buf_params;
 226
 227	ASSERT(wb_info->dwb_pipe_inst < MAX_DWB_PIPES);
 228	ASSERT(wb_info->wb_enabled);
 229	ASSERT(wb_info->mpcc_inst >= 0);
 230	ASSERT(wb_info->mpcc_inst < dc->res_pool->mpcc_count);
 231	mcif_wb = dc->res_pool->mcif_wb[wb_info->dwb_pipe_inst];
 232	mcif_buf_params = &wb_info->mcif_buf_params;
 233
 234	/* set DWB MPC mux */
 235	dc->res_pool->mpc->funcs->set_dwb_mux(dc->res_pool->mpc,
 236			wb_info->dwb_pipe_inst, wb_info->mpcc_inst);
 237	/* set MCIF_WB buffer and arbitration configuration */
 238	mcif_wb->funcs->config_mcif_buf(mcif_wb, mcif_buf_params, wb_info->dwb_params.dest_height);
 239	mcif_wb->funcs->config_mcif_arb(mcif_wb, &context->bw_ctx.bw.dcn.bw_writeback.mcif_wb_arb[wb_info->dwb_pipe_inst]);
 240}
 241
 242void dcn30_update_writeback(
 243		struct dc *dc,
 244		struct dc_writeback_info *wb_info,
 245		struct dc_state *context)
 246{
 247	struct dwbc *dwb;
 248	dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst];
 249	DC_LOG_DWB("%s dwb_pipe_inst = %d, mpcc_inst = %d",\
 250		__func__, wb_info->dwb_pipe_inst,\
 251		wb_info->mpcc_inst);
 252
 253	dcn30_set_writeback(dc, wb_info, context);
 254
 255	/* update DWB */
 256	dwb->funcs->update(dwb, &wb_info->dwb_params);
 257}
 258
 259bool dcn30_mmhubbub_warmup(
 260	struct dc *dc,
 261	unsigned int num_dwb,
 262	struct dc_writeback_info *wb_info)
 263{
 264	struct dwbc *dwb;
 265	struct mcif_wb *mcif_wb;
 266	struct mcif_warmup_params warmup_params = {0};
 267	unsigned int  i, i_buf;
 268	/*make sure there is no active DWB eanbled */
 269	for (i = 0; i < num_dwb; i++) {
 270		dwb = dc->res_pool->dwbc[wb_info[i].dwb_pipe_inst];
 271		if (dwb->dwb_is_efc_transition || dwb->dwb_is_drc) {
 272			/*can not do warmup while any dwb enabled*/
 273			return false;
 274		}
 275	}
 276
 277	if (wb_info->mcif_warmup_params.p_vmid == 0)
 278		return false;
 279
 280	/*check whether this is new interface: warmup big buffer once*/
 281	if (wb_info->mcif_warmup_params.start_address.quad_part != 0 &&
 282		wb_info->mcif_warmup_params.region_size != 0) {
 283		/*mmhubbub is shared, so it does not matter which MCIF*/
 284		mcif_wb = dc->res_pool->mcif_wb[0];
 285		/*warmup a big chunk of VM buffer at once*/
 286		warmup_params.start_address.quad_part = wb_info->mcif_warmup_params.start_address.quad_part;
 287		warmup_params.address_increment =  wb_info->mcif_warmup_params.region_size;
 288		warmup_params.region_size = wb_info->mcif_warmup_params.region_size;
 289		warmup_params.p_vmid = wb_info->mcif_warmup_params.p_vmid;
 290
 291		if (warmup_params.address_increment == 0)
 292			warmup_params.address_increment = dc->dml.soc.vmm_page_size_bytes;
 293
 294		mcif_wb->funcs->warmup_mcif(mcif_wb, &warmup_params);
 295		return true;
 296	}
 297	/*following is the original: warmup each DWB's mcif buffer*/
 298	for (i = 0; i < num_dwb; i++) {
 299		dwb = dc->res_pool->dwbc[wb_info[i].dwb_pipe_inst];
 300		mcif_wb = dc->res_pool->mcif_wb[wb_info[i].dwb_pipe_inst];
 301		/*warmup is for VM mode only*/
 302		if (wb_info[i].mcif_buf_params.p_vmid == 0)
 303			return false;
 304
 305		/* Warmup MCIF_WB */
 306		for (i_buf = 0; i_buf < MCIF_BUF_COUNT; i_buf++) {
 307			warmup_params.start_address.quad_part = wb_info[i].mcif_buf_params.luma_address[i_buf];
 308			warmup_params.address_increment = dc->dml.soc.vmm_page_size_bytes;
 309			warmup_params.region_size = wb_info[i].mcif_buf_params.luma_pitch * wb_info[i].dwb_params.dest_height;
 310			warmup_params.p_vmid = wb_info[i].mcif_buf_params.p_vmid;
 311			mcif_wb->funcs->warmup_mcif(mcif_wb, &warmup_params);
 312		}
 313	}
 314	return true;
 315}
 316
 317void dcn30_enable_writeback(
 318		struct dc *dc,
 319		struct dc_writeback_info *wb_info,
 320		struct dc_state *context)
 321{
 322	struct dwbc *dwb;
 323	struct mcif_wb *mcif_wb;
 324	struct timing_generator *optc;
 325
 326	dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst];
 327	mcif_wb = dc->res_pool->mcif_wb[wb_info->dwb_pipe_inst];
 328
 329	/* set the OPTC source mux */
 330	optc = dc->res_pool->timing_generators[dwb->otg_inst];
 331	DC_LOG_DWB("%s dwb_pipe_inst = %d, mpcc_inst = %d",\
 332		__func__, wb_info->dwb_pipe_inst,\
 333		wb_info->mpcc_inst);
 334	if (IS_DIAG_DC(dc->ctx->dce_environment)) {
 335		/*till diags switch to warmup interface*/
 336		dcn30_mmhubbub_warmup(dc, 1, wb_info);
 337	}
 338	/* Update writeback pipe */
 339	dcn30_set_writeback(dc, wb_info, context);
 340
 341	/* Enable MCIF_WB */
 342	mcif_wb->funcs->enable_mcif(mcif_wb);
 343	/* Enable DWB */
 344	dwb->funcs->enable(dwb, &wb_info->dwb_params);
 345}
 346
 347void dcn30_disable_writeback(
 348		struct dc *dc,
 349		unsigned int dwb_pipe_inst)
 350{
 351	struct dwbc *dwb;
 352	struct mcif_wb *mcif_wb;
 353
 354	ASSERT(dwb_pipe_inst < MAX_DWB_PIPES);
 355	dwb = dc->res_pool->dwbc[dwb_pipe_inst];
 356	mcif_wb = dc->res_pool->mcif_wb[dwb_pipe_inst];
 357	DC_LOG_DWB("%s dwb_pipe_inst = %d",\
 358		__func__, dwb_pipe_inst);
 359
 360	/* disable DWB */
 361	dwb->funcs->disable(dwb);
 362	/* disable MCIF */
 363	mcif_wb->funcs->disable_mcif(mcif_wb);
 364	/* disable MPC DWB mux */
 365	dc->res_pool->mpc->funcs->disable_dwb_mux(dc->res_pool->mpc, dwb_pipe_inst);
 366}
 367
 368void dcn30_program_all_writeback_pipes_in_tree(
 369		struct dc *dc,
 370		const struct dc_stream_state *stream,
 371		struct dc_state *context)
 372{
 373	struct dc_writeback_info wb_info;
 374	struct dwbc *dwb;
 375	struct dc_stream_status *stream_status = NULL;
 376	int i_wb, i_pipe, i_stream;
 377	DC_LOG_DWB("%s", __func__);
 378
 379	ASSERT(stream);
 380	for (i_stream = 0; i_stream < context->stream_count; i_stream++) {
 381		if (context->streams[i_stream] == stream) {
 382			stream_status = &context->stream_status[i_stream];
 383			break;
 384		}
 385	}
 386	ASSERT(stream_status);
 387
 388	ASSERT(stream->num_wb_info <= dc->res_pool->res_cap->num_dwb);
 389	/* For each writeback pipe */
 390	for (i_wb = 0; i_wb < stream->num_wb_info; i_wb++) {
 391
 392		/* copy writeback info to local non-const so mpcc_inst can be set */
 393		wb_info = stream->writeback_info[i_wb];
 394		if (wb_info.wb_enabled) {
 395
 396			/* get the MPCC instance for writeback_source_plane */
 397			wb_info.mpcc_inst = -1;
 398			for (i_pipe = 0; i_pipe < dc->res_pool->pipe_count; i_pipe++) {
 399				struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i_pipe];
 400
 401				if (!pipe_ctx->plane_state)
 402					continue;
 403
 404				if (pipe_ctx->plane_state == wb_info.writeback_source_plane) {
 405					wb_info.mpcc_inst = pipe_ctx->plane_res.mpcc_inst;
 406					break;
 407				}
 408			}
 409
 410			if (wb_info.mpcc_inst == -1) {
 411				/* Disable writeback pipe and disconnect from MPCC
 412				 * if source plane has been removed
 413				 */
 414				dc->hwss.disable_writeback(dc, wb_info.dwb_pipe_inst);
 415				continue;
 416			}
 417
 418			ASSERT(wb_info.dwb_pipe_inst < dc->res_pool->res_cap->num_dwb);
 419			dwb = dc->res_pool->dwbc[wb_info.dwb_pipe_inst];
 420			if (dwb->funcs->is_enabled(dwb)) {
 421				/* writeback pipe already enabled, only need to update */
 422				dc->hwss.update_writeback(dc, &wb_info, context);
 423			} else {
 424				/* Enable writeback pipe and connect to MPCC */
 425				dc->hwss.enable_writeback(dc, &wb_info, context);
 426			}
 427		} else {
 428			/* Disable writeback pipe and disconnect from MPCC */
 429			dc->hwss.disable_writeback(dc, wb_info.dwb_pipe_inst);
 430		}
 431	}
 432}
 433
 434void dcn30_init_hw(struct dc *dc)
 435{
 436	struct abm **abms = dc->res_pool->multiple_abms;
 437	struct dce_hwseq *hws = dc->hwseq;
 438	struct dc_bios *dcb = dc->ctx->dc_bios;
 439	struct resource_pool *res_pool = dc->res_pool;
 440	int i, j;
 441	int edp_num;
 442	uint32_t backlight = MAX_BACKLIGHT_LEVEL;
 443
 444	if (dc->clk_mgr && dc->clk_mgr->funcs->init_clocks)
 445		dc->clk_mgr->funcs->init_clocks(dc->clk_mgr);
 446
 447	// Initialize the dccg
 448	if (res_pool->dccg->funcs->dccg_init)
 449		res_pool->dccg->funcs->dccg_init(res_pool->dccg);
 450
 451	if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) {
 452
 453		REG_WRITE(REFCLK_CNTL, 0);
 454		REG_UPDATE(DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_ENABLE, 1);
 455		REG_WRITE(DIO_MEM_PWR_CTRL, 0);
 456
 457		if (!dc->debug.disable_clock_gate) {
 458			/* enable all DCN clock gating */
 459			REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0);
 460
 461			REG_WRITE(DCCG_GATE_DISABLE_CNTL2, 0);
 462
 463			REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0);
 464		}
 465
 466		//Enable ability to power gate / don't force power on permanently
 467		if (hws->funcs.enable_power_gating_plane)
 468			hws->funcs.enable_power_gating_plane(hws, true);
 469
 470		return;
 471	}
 472
 473	if (!dcb->funcs->is_accelerated_mode(dcb)) {
 474		hws->funcs.bios_golden_init(dc);
 475		hws->funcs.disable_vga(dc->hwseq);
 476	}
 477
 478	if (dc->debug.enable_mem_low_power.bits.dmcu) {
 479		// Force ERAM to shutdown if DMCU is not enabled
 480		if (dc->debug.disable_dmcu || dc->config.disable_dmcu) {
 481			REG_UPDATE(DMU_MEM_PWR_CNTL, DMCU_ERAM_MEM_PWR_FORCE, 3);
 482		}
 483	}
 484
 485	// Set default OPTC memory power states
 486	if (dc->debug.enable_mem_low_power.bits.optc) {
 487		// Shutdown when unassigned and light sleep in VBLANK
 488		REG_SET_2(ODM_MEM_PWR_CTRL3, 0, ODM_MEM_UNASSIGNED_PWR_MODE, 3, ODM_MEM_VBLANK_PWR_MODE, 1);
 489	}
 490
 491	if (dc->ctx->dc_bios->fw_info_valid) {
 492		res_pool->ref_clocks.xtalin_clock_inKhz =
 493				dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency;
 494
 495		if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) {
 496			if (res_pool->dccg && res_pool->hubbub) {
 497
 498				(res_pool->dccg->funcs->get_dccg_ref_freq)(res_pool->dccg,
 499						dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency,
 500						&res_pool->ref_clocks.dccg_ref_clock_inKhz);
 501
 502				(res_pool->hubbub->funcs->get_dchub_ref_freq)(res_pool->hubbub,
 503						res_pool->ref_clocks.dccg_ref_clock_inKhz,
 504						&res_pool->ref_clocks.dchub_ref_clock_inKhz);
 505			} else {
 506				// Not all ASICs have DCCG sw component
 507				res_pool->ref_clocks.dccg_ref_clock_inKhz =
 508						res_pool->ref_clocks.xtalin_clock_inKhz;
 509				res_pool->ref_clocks.dchub_ref_clock_inKhz =
 510						res_pool->ref_clocks.xtalin_clock_inKhz;
 511			}
 512		}
 513	} else
 514		ASSERT_CRITICAL(false);
 515
 516	for (i = 0; i < dc->link_count; i++) {
 517		/* Power up AND update implementation according to the
 518		 * required signal (which may be different from the
 519		 * default signal on connector).
 520		 */
 521		struct dc_link *link = dc->links[i];
 522
 523		link->link_enc->funcs->hw_init(link->link_enc);
 524
 525		/* Check for enabled DIG to identify enabled display */
 526		if (link->link_enc->funcs->is_dig_enabled &&
 527			link->link_enc->funcs->is_dig_enabled(link->link_enc))
 528			link->link_status.link_active = true;
 529	}
 530
 531	/* Power gate DSCs */
 532	for (i = 0; i < res_pool->res_cap->num_dsc; i++)
 533		if (hws->funcs.dsc_pg_control != NULL)
 534			hws->funcs.dsc_pg_control(hws, res_pool->dscs[i]->inst, false);
 535
 536	/* we want to turn off all dp displays before doing detection */
 537	if (dc->config.power_down_display_on_boot) {
 538		uint8_t dpcd_power_state = '\0';
 539		enum dc_status status = DC_ERROR_UNEXPECTED;
 540
 541		for (i = 0; i < dc->link_count; i++) {
 542			if (dc->links[i]->connector_signal != SIGNAL_TYPE_DISPLAY_PORT)
 543				continue;
 544			/* DP 2.0 states that LTTPR regs must be read first */
 545			dp_retrieve_lttpr_cap(dc->links[i]);
 546
 547			/* if any of the displays are lit up turn them off */
 548			status = core_link_read_dpcd(dc->links[i], DP_SET_POWER,
 549						     &dpcd_power_state, sizeof(dpcd_power_state));
 550			if (status == DC_OK && dpcd_power_state == DP_POWER_STATE_D0) {
 551				/* blank dp stream before power off receiver*/
 552				if (dc->links[i]->link_enc->funcs->get_dig_frontend) {
 553					unsigned int fe;
 554
 555					fe = dc->links[i]->link_enc->funcs->get_dig_frontend(
 556										dc->links[i]->link_enc);
 557					if (fe == ENGINE_ID_UNKNOWN)
 558						continue;
 559
 560					for (j = 0; j < dc->res_pool->stream_enc_count; j++) {
 561						if (fe == dc->res_pool->stream_enc[j]->id) {
 562							dc->res_pool->stream_enc[j]->funcs->dp_blank(
 563										dc->res_pool->stream_enc[j]);
 564							break;
 565						}
 566					}
 567				}
 568				dp_receiver_power_ctrl(dc->links[i], false);
 569			}
 570		}
 571	}
 572
 573	/* If taking control over from VBIOS, we may want to optimize our first
 574	 * mode set, so we need to skip powering down pipes until we know which
 575	 * pipes we want to use.
 576	 * Otherwise, if taking control is not possible, we need to power
 577	 * everything down.
 578	 */
 579	if (dcb->funcs->is_accelerated_mode(dcb) || dc->config.power_down_display_on_boot) {
 580		hws->funcs.init_pipes(dc, dc->current_state);
 581		if (dc->res_pool->hubbub->funcs->allow_self_refresh_control)
 582			dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub,
 583					!dc->res_pool->hubbub->ctx->dc->debug.disable_stutter);
 584	}
 585
 586	/* In headless boot cases, DIG may be turned
 587	 * on which causes HW/SW discrepancies.
 588	 * To avoid this, power down hardware on boot
 589	 * if DIG is turned on and seamless boot not enabled
 590	 */
 591	if (dc->config.power_down_display_on_boot) {
 592		struct dc_link *edp_links[MAX_NUM_EDP];
 593		struct dc_link *edp_link = NULL;
 594
 595		get_edp_links(dc, edp_links, &edp_num);
 596		if (edp_num)
 597			edp_link = edp_links[0];
 598		if (edp_link && edp_link->link_enc->funcs->is_dig_enabled &&
 599				edp_link->link_enc->funcs->is_dig_enabled(edp_link->link_enc) &&
 600				dc->hwss.edp_backlight_control &&
 601				dc->hwss.power_down &&
 602				dc->hwss.edp_power_control) {
 603			dc->hwss.edp_backlight_control(edp_link, false);
 604			dc->hwss.power_down(dc);
 605			dc->hwss.edp_power_control(edp_link, false);
 606		} else {
 607			for (i = 0; i < dc->link_count; i++) {
 608				struct dc_link *link = dc->links[i];
 609
 610				if (link->link_enc->funcs->is_dig_enabled &&
 611						link->link_enc->funcs->is_dig_enabled(link->link_enc) &&
 612						dc->hwss.power_down) {
 613					dc->hwss.power_down(dc);
 614					break;
 615				}
 616
 617			}
 618		}
 619	}
 620
 621	for (i = 0; i < res_pool->audio_count; i++) {
 622		struct audio *audio = res_pool->audios[i];
 623
 624		audio->funcs->hw_init(audio);
 625	}
 626
 627	for (i = 0; i < dc->link_count; i++) {
 628		struct dc_link *link = dc->links[i];
 629
 630		if (link->panel_cntl)
 631			backlight = link->panel_cntl->funcs->hw_init(link->panel_cntl);
 632	}
 633
 634	for (i = 0; i < dc->res_pool->pipe_count; i++) {
 635		if (abms[i] != NULL)
 636			abms[i]->funcs->abm_init(abms[i], backlight);
 637	}
 638
 639	/* power AFMT HDMI memory TODO: may move to dis/en output save power*/
 640	REG_WRITE(DIO_MEM_PWR_CTRL, 0);
 641
 642	if (!dc->debug.disable_clock_gate) {
 643		/* enable all DCN clock gating */
 644		REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0);
 645
 646		REG_WRITE(DCCG_GATE_DISABLE_CNTL2, 0);
 647
 648		REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0);
 649	}
 650	if (hws->funcs.enable_power_gating_plane)
 651		hws->funcs.enable_power_gating_plane(dc->hwseq, true);
 652
 653	if (!dcb->funcs->is_accelerated_mode(dcb) && dc->res_pool->hubbub->funcs->init_watermarks)
 654		dc->res_pool->hubbub->funcs->init_watermarks(dc->res_pool->hubbub);
 655
 656	if (dc->clk_mgr->funcs->notify_wm_ranges)
 657		dc->clk_mgr->funcs->notify_wm_ranges(dc->clk_mgr);
 658
 659	if (dc->clk_mgr->funcs->set_hard_max_memclk)
 660		dc->clk_mgr->funcs->set_hard_max_memclk(dc->clk_mgr);
 661
 662	if (dc->res_pool->hubbub->funcs->force_pstate_change_control)
 663		dc->res_pool->hubbub->funcs->force_pstate_change_control(
 664				dc->res_pool->hubbub, false, false);
 665	if (dc->res_pool->hubbub->funcs->init_crb)
 666		dc->res_pool->hubbub->funcs->init_crb(dc->res_pool->hubbub);
 667
 668}
 669
 670void dcn30_set_avmute(struct pipe_ctx *pipe_ctx, bool enable)
 671{
 672	if (pipe_ctx == NULL)
 673		return;
 674
 675	if (dc_is_hdmi_signal(pipe_ctx->stream->signal) && pipe_ctx->stream_res.stream_enc != NULL)
 676		pipe_ctx->stream_res.stream_enc->funcs->set_avmute(
 677				pipe_ctx->stream_res.stream_enc,
 678				enable);
 679}
 680
 681void dcn30_update_info_frame(struct pipe_ctx *pipe_ctx)
 682{
 683	bool is_hdmi_tmds;
 684	bool is_dp;
 685
 686	ASSERT(pipe_ctx->stream);
 687
 688	if (pipe_ctx->stream_res.stream_enc == NULL)
 689		return;  /* this is not root pipe */
 690
 691	is_hdmi_tmds = dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal);
 692	is_dp = dc_is_dp_signal(pipe_ctx->stream->signal);
 693
 694	if (!is_hdmi_tmds && !is_dp)
 695		return;
 696
 697	if (is_hdmi_tmds)
 698		pipe_ctx->stream_res.stream_enc->funcs->update_hdmi_info_packets(
 699			pipe_ctx->stream_res.stream_enc,
 700			&pipe_ctx->stream_res.encoder_info_frame);
 701	else
 702		pipe_ctx->stream_res.stream_enc->funcs->update_dp_info_packets(
 703			pipe_ctx->stream_res.stream_enc,
 704			&pipe_ctx->stream_res.encoder_info_frame);
 705}
 706
 707void dcn30_program_dmdata_engine(struct pipe_ctx *pipe_ctx)
 708{
 709	struct dc_stream_state    *stream     = pipe_ctx->stream;
 710	struct hubp               *hubp       = pipe_ctx->plane_res.hubp;
 711	bool                       enable     = false;
 712	struct stream_encoder     *stream_enc = pipe_ctx->stream_res.stream_enc;
 713	enum dynamic_metadata_mode mode       = dc_is_dp_signal(stream->signal)
 714							? dmdata_dp
 715							: dmdata_hdmi;
 716
 717	/* if using dynamic meta, don't set up generic infopackets */
 718	if (pipe_ctx->stream->dmdata_address.quad_part != 0) {
 719		pipe_ctx->stream_res.encoder_info_frame.hdrsmd.valid = false;
 720		enable = true;
 721	}
 722
 723	if (!hubp)
 724		return;
 725
 726	if (!stream_enc || !stream_enc->funcs->set_dynamic_metadata)
 727		return;
 728
 729	stream_enc->funcs->set_dynamic_metadata(stream_enc, enable,
 730							hubp->inst, mode);
 731}
 732
 733bool dcn30_apply_idle_power_optimizations(struct dc *dc, bool enable)
 734{
 735	union dmub_rb_cmd cmd;
 736	uint32_t tmr_delay = 0, tmr_scale = 0;
 737	struct dc_cursor_attributes cursor_attr;
 738	bool cursor_cache_enable = false;
 739	struct dc_stream_state *stream = NULL;
 740	struct dc_plane_state *plane = NULL;
 741
 742	if (!dc->ctx->dmub_srv)
 743		return false;
 744
 745	if (enable) {
 746		if (dc->current_state) {
 747			int i;
 748
 749			/* First, check no-memory-requests case */
 750			for (i = 0; i < dc->current_state->stream_count; i++) {
 751				if (dc->current_state->stream_status[i].plane_count)
 752					/* Fail eligibility on a visible stream */
 753					break;
 754			}
 755
 756			if (i == dc->current_state->stream_count) {
 757				/* Enable no-memory-requests case */
 758				memset(&cmd, 0, sizeof(cmd));
 759				cmd.mall.header.type = DMUB_CMD__MALL;
 760				cmd.mall.header.sub_type = DMUB_CMD__MALL_ACTION_NO_DF_REQ;
 761				cmd.mall.header.payload_bytes = sizeof(cmd.mall) - sizeof(cmd.mall.header);
 762
 763				dc_dmub_srv_cmd_queue(dc->ctx->dmub_srv, &cmd);
 764				dc_dmub_srv_cmd_execute(dc->ctx->dmub_srv);
 765
 766				return true;
 767			}
 768
 769			stream = dc->current_state->streams[0];
 770			plane = (stream ? dc->current_state->stream_status[0].plane_states[0] : NULL);
 771
 772			if (stream && plane) {
 773				cursor_cache_enable = stream->cursor_position.enable &&
 774						plane->address.grph.cursor_cache_addr.quad_part;
 775				cursor_attr = stream->cursor_attributes;
 776			}
 777
 778			/*
 779			 * Second, check MALL eligibility
 780			 *
 781			 * single display only, single surface only, 8 and 16 bit formats only, no VM,
 782			 * do not use MALL for displays that support PSR as they use D0i3.2 in DMCUB FW
 783			 *
 784			 * TODO: When we implement multi-display, PSR displays will be allowed if there is
 785			 * a non-PSR display present, since in that case we can't do D0i3.2
 786			 */
 787			if (dc->current_state->stream_count == 1 &&
 788					stream->link->psr_settings.psr_version == DC_PSR_VERSION_UNSUPPORTED &&
 789					dc->current_state->stream_status[0].plane_count == 1 &&
 790					plane->format <= SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F &&
 791					plane->format >= SURFACE_PIXEL_FORMAT_GRPH_ARGB8888 &&
 792					plane->address.page_table_base.quad_part == 0 &&
 793					dc->hwss.does_plane_fit_in_mall &&
 794					dc->hwss.does_plane_fit_in_mall(dc, plane,
 795							cursor_cache_enable ? &cursor_attr : NULL)) {
 796				unsigned int v_total = stream->adjust.v_total_max ?
 797						stream->adjust.v_total_max : stream->timing.v_total;
 798				unsigned int refresh_hz = div_u64((unsigned long long) stream->timing.pix_clk_100hz *
 799						100LL, (v_total * stream->timing.h_total));
 800
 801				/*
 802				 * one frame time in microsec:
 803				 * Delay_Us = 1000000 / refresh
 804				 * dynamic_delay_us = 1000000 / refresh + 2 * stutter_period
 805				 *
 806				 * one frame time modified by 'additional timer percent' (p):
 807				 * Delay_Us_modified = dynamic_delay_us + dynamic_delay_us * p / 100
 808				 *                   = dynamic_delay_us * (1 + p / 100)
 809				 *                   = (1000000 / refresh + 2 * stutter_period) * (100 + p) / 100
 810				 *                   = (1000000 + 2 * stutter_period * refresh) * (100 + p) / (100 * refresh)
 811				 *
 812				 * formula for timer duration based on parameters, from regspec:
 813				 * dynamic_delay_us = 65.28 * (64 + MallFrameCacheTmrDly) * 2^MallFrameCacheTmrScale
 814				 *
 815				 * dynamic_delay_us / 65.28 = (64 + MallFrameCacheTmrDly) * 2^MallFrameCacheTmrScale
 816				 * (dynamic_delay_us / 65.28) / 2^MallFrameCacheTmrScale = 64 + MallFrameCacheTmrDly
 817				 * MallFrameCacheTmrDly = ((dynamic_delay_us / 65.28) / 2^MallFrameCacheTmrScale) - 64
 818				 *                      = (1000000 + 2 * stutter_period * refresh) * (100 + p) / (100 * refresh) / 65.28 / 2^MallFrameCacheTmrScale - 64
 819				 *                      = (1000000 + 2 * stutter_period * refresh) * (100 + p) / (refresh * 6528 * 2^MallFrameCacheTmrScale) - 64
 820				 *
 821				 * need to round up the result of the division before the subtraction
 822				 */
 823				unsigned int denom = refresh_hz * 6528;
 824				unsigned int stutter_period = dc->current_state->perf_params.stutter_period_us;
 825
 826				tmr_delay = div_u64(((1000000LL + 2 * stutter_period * refresh_hz) *
 827						(100LL + dc->debug.mall_additional_timer_percent) + denom - 1),
 828						denom) - 64LL;
 829
 830				/* In some cases the stutter period is really big (tiny modes) in these
 831				 * cases MALL cant be enabled, So skip these cases to avoid a ASSERT()
 832				 *
 833				 * We can check if stutter_period is more than 1/10th the frame time to
 834				 * consider if we can actually meet the range of hysteresis timer
 835				 */
 836				if (stutter_period > 100000/refresh_hz)
 837					return false;
 838
 839				/* scale should be increased until it fits into 6 bits */
 840				while (tmr_delay & ~0x3F) {
 841					tmr_scale++;
 842
 843					if (tmr_scale > 3) {
 844						/* Delay exceeds range of hysteresis timer */
 845						ASSERT(false);
 846						return false;
 847					}
 848
 849					denom *= 2;
 850					tmr_delay = div_u64(((1000000LL + 2 * stutter_period * refresh_hz) *
 851							(100LL + dc->debug.mall_additional_timer_percent) + denom - 1),
 852							denom) - 64LL;
 853				}
 854
 855				/* Copy HW cursor */
 856				if (cursor_cache_enable) {
 857					memset(&cmd, 0, sizeof(cmd));
 858					cmd.mall.header.type = DMUB_CMD__MALL;
 859					cmd.mall.header.sub_type = DMUB_CMD__MALL_ACTION_COPY_CURSOR;
 860					cmd.mall.header.payload_bytes =
 861							sizeof(cmd.mall) - sizeof(cmd.mall.header);
 862
 863					switch (cursor_attr.color_format) {
 864					case CURSOR_MODE_MONO:
 865						cmd.mall.cursor_bpp = 2;
 866						break;
 867					case CURSOR_MODE_COLOR_1BIT_AND:
 868					case CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA:
 869					case CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA:
 870						cmd.mall.cursor_bpp = 32;
 871						break;
 872
 873					case CURSOR_MODE_COLOR_64BIT_FP_PRE_MULTIPLIED:
 874					case CURSOR_MODE_COLOR_64BIT_FP_UN_PRE_MULTIPLIED:
 875						cmd.mall.cursor_bpp = 64;
 876						break;
 877					}
 878
 879					cmd.mall.cursor_copy_src.quad_part = cursor_attr.address.quad_part;
 880					cmd.mall.cursor_copy_dst.quad_part =
 881							(plane->address.grph.cursor_cache_addr.quad_part + 2047) & ~2047;
 882					cmd.mall.cursor_width = cursor_attr.width;
 883					cmd.mall.cursor_height = cursor_attr.height;
 884					cmd.mall.cursor_pitch = cursor_attr.pitch;
 885
 886					dc_dmub_srv_cmd_queue(dc->ctx->dmub_srv, &cmd);
 887					dc_dmub_srv_cmd_execute(dc->ctx->dmub_srv);
 888					dc_dmub_srv_wait_idle(dc->ctx->dmub_srv);
 889
 890					/* Use copied cursor, and it's okay to not switch back */
 891					cursor_attr.address.quad_part = cmd.mall.cursor_copy_dst.quad_part;
 892					dc_stream_set_cursor_attributes(stream, &cursor_attr);
 893				}
 894
 895				/* Enable MALL */
 896				memset(&cmd, 0, sizeof(cmd));
 897				cmd.mall.header.type = DMUB_CMD__MALL;
 898				cmd.mall.header.sub_type = DMUB_CMD__MALL_ACTION_ALLOW;
 899				cmd.mall.header.payload_bytes = sizeof(cmd.mall) - sizeof(cmd.mall.header);
 900				cmd.mall.tmr_delay = tmr_delay;
 901				cmd.mall.tmr_scale = tmr_scale;
 902				cmd.mall.debug_bits = dc->debug.mall_error_as_fatal;
 903
 904				dc_dmub_srv_cmd_queue(dc->ctx->dmub_srv, &cmd);
 905				dc_dmub_srv_cmd_execute(dc->ctx->dmub_srv);
 906
 907				return true;
 908			}
 909		}
 910
 911		/* No applicable optimizations */
 912		return false;
 913	}
 914
 915	/* Disable MALL */
 916	memset(&cmd, 0, sizeof(cmd));
 917	cmd.mall.header.type = DMUB_CMD__MALL;
 918	cmd.mall.header.sub_type = DMUB_CMD__MALL_ACTION_DISALLOW;
 919	cmd.mall.header.payload_bytes =
 920		sizeof(cmd.mall) - sizeof(cmd.mall.header);
 921
 922	dc_dmub_srv_cmd_queue(dc->ctx->dmub_srv, &cmd);
 923	dc_dmub_srv_cmd_execute(dc->ctx->dmub_srv);
 924	dc_dmub_srv_wait_idle(dc->ctx->dmub_srv);
 925
 926	return true;
 927}
 928
 929bool dcn30_does_plane_fit_in_mall(struct dc *dc, struct dc_plane_state *plane, struct dc_cursor_attributes *cursor_attr)
 930{
 931	// add meta size?
 932	unsigned int surface_size = plane->plane_size.surface_pitch * plane->plane_size.surface_size.height *
 933			(plane->format >= SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616 ? 8 : 4);
 934	unsigned int mall_size = dc->caps.mall_size_total;
 935	unsigned int cursor_size = 0;
 936
 937	if (dc->debug.mall_size_override)
 938		mall_size = 1024 * 1024 * dc->debug.mall_size_override;
 939
 940	if (cursor_attr) {
 941		cursor_size = dc->caps.max_cursor_size * dc->caps.max_cursor_size;
 942
 943		switch (cursor_attr->color_format) {
 944		case CURSOR_MODE_MONO:
 945			cursor_size /= 2;
 946			break;
 947		case CURSOR_MODE_COLOR_1BIT_AND:
 948		case CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA:
 949		case CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA:
 950			cursor_size *= 4;
 951			break;
 952
 953		case CURSOR_MODE_COLOR_64BIT_FP_PRE_MULTIPLIED:
 954		case CURSOR_MODE_COLOR_64BIT_FP_UN_PRE_MULTIPLIED:
 955			cursor_size *= 8;
 956			break;
 957		}
 958	}
 959
 960	return (surface_size + cursor_size) < mall_size;
 961}
 962
 963void dcn30_hardware_release(struct dc *dc)
 964{
 965	/* if pstate unsupported, force it supported */
 966	if (!dc->clk_mgr->clks.p_state_change_support &&
 967			dc->res_pool->hubbub->funcs->force_pstate_change_control)
 968		dc->res_pool->hubbub->funcs->force_pstate_change_control(
 969				dc->res_pool->hubbub, true, true);
 970}
 971
 972void dcn30_set_disp_pattern_generator(const struct dc *dc,
 973		struct pipe_ctx *pipe_ctx,
 974		enum controller_dp_test_pattern test_pattern,
 975		enum controller_dp_color_space color_space,
 976		enum dc_color_depth color_depth,
 977		const struct tg_color *solid_color,
 978		int width, int height, int offset)
 979{
 980	struct stream_resource *stream_res = &pipe_ctx->stream_res;
 981	struct pipe_ctx *mpcc_pipe;
 982
 983	if (test_pattern != CONTROLLER_DP_TEST_PATTERN_VIDEOMODE) {
 984		pipe_ctx->vtp_locked = false;
 985		/* turning on DPG */
 986		stream_res->opp->funcs->opp_set_disp_pattern_generator(stream_res->opp, test_pattern, color_space,
 987				color_depth, solid_color, width, height, offset);
 988
 989		/* Defer hubp blank if tg is locked */
 990		if (stream_res->tg->funcs->is_tg_enabled(stream_res->tg)) {
 991			if (stream_res->tg->funcs->is_locked(stream_res->tg))
 992				pipe_ctx->vtp_locked = true;
 993			else {
 994				/* Blank HUBP to allow p-state during blank on all timings */
 995				pipe_ctx->plane_res.hubp->funcs->set_blank(pipe_ctx->plane_res.hubp, true);
 996
 997				for (mpcc_pipe = pipe_ctx->bottom_pipe; mpcc_pipe; mpcc_pipe = mpcc_pipe->bottom_pipe)
 998					mpcc_pipe->plane_res.hubp->funcs->set_blank(mpcc_pipe->plane_res.hubp, true);
 999			}
1000		}
1001	} else {
1002		/* turning off DPG */
1003		pipe_ctx->plane_res.hubp->funcs->set_blank(pipe_ctx->plane_res.hubp, false);
1004		for (mpcc_pipe = pipe_ctx->bottom_pipe; mpcc_pipe; mpcc_pipe = mpcc_pipe->bottom_pipe)
1005			mpcc_pipe->plane_res.hubp->funcs->set_blank(mpcc_pipe->plane_res.hubp, false);
1006
1007		stream_res->opp->funcs->opp_set_disp_pattern_generator(stream_res->opp, test_pattern, color_space,
1008				color_depth, solid_color, width, height, offset);
1009	}
1010}