1 /*
2  * Copyright 2016 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 #include "dm_services.h"
27 #include "basics/dc_common.h"
28 #include "core_types.h"
29 #include "resource.h"
30 #include "dcn201_hwseq.h"
31 #include "dcn201_optc.h"
32 #include "dce/dce_hwseq.h"
33 #include "hubp.h"
34 #include "dchubbub.h"
35 #include "timing_generator.h"
36 #include "opp.h"
37 #include "ipp.h"
38 #include "mpc.h"
39 #include "dccg.h"
40 #include "clk_mgr.h"
41 #include "reg_helper.h"
42 
43 #define CTX \
44 	hws->ctx
45 
46 #define REG(reg)\
47 	hws->regs->reg
48 
49 #define DC_LOGGER \
50 	dc->ctx->logger
51 
52 #undef FN
53 #define FN(reg_name, field_name) \
54 	hws->shifts->field_name, hws->masks->field_name
55 
56 static bool patch_address_for_sbs_tb_stereo(
57 		struct pipe_ctx *pipe_ctx, PHYSICAL_ADDRESS_LOC *addr)
58 {
59 	struct dc_plane_state *plane_state = pipe_ctx->plane_state;
60 	bool sec_split = pipe_ctx->top_pipe &&
61 		pipe_ctx->top_pipe->plane_state == pipe_ctx->plane_state;
62 
63 	if (sec_split && plane_state->address.type == PLN_ADDR_TYPE_GRPH_STEREO &&
64 		(pipe_ctx->stream->timing.timing_3d_format ==
65 			TIMING_3D_FORMAT_SIDE_BY_SIDE ||
66 		pipe_ctx->stream->timing.timing_3d_format ==
67 			TIMING_3D_FORMAT_TOP_AND_BOTTOM)) {
68 		*addr = plane_state->address.grph_stereo.left_addr;
69 		plane_state->address.grph_stereo.left_addr =
70 			plane_state->address.grph_stereo.right_addr;
71 		return true;
72 	} else {
73 		if (pipe_ctx->stream->view_format != VIEW_3D_FORMAT_NONE &&
74 			plane_state->address.type != PLN_ADDR_TYPE_GRPH_STEREO) {
75 			plane_state->address.type = PLN_ADDR_TYPE_GRPH_STEREO;
76 			plane_state->address.grph_stereo.right_addr =
77 			plane_state->address.grph_stereo.left_addr;
78 			plane_state->address.grph_stereo.right_meta_addr =
79 			plane_state->address.grph_stereo.left_meta_addr;
80 		}
81 	}
82 	return false;
83 }
84 
85 static void gpu_addr_to_uma(struct dce_hwseq *hwseq,
86 		PHYSICAL_ADDRESS_LOC *addr)
87 {
88 	bool is_in_uma;
89 
90 	if (hwseq->fb_base.quad_part <= addr->quad_part &&
91 			addr->quad_part < hwseq->fb_top.quad_part) {
92 		addr->quad_part -= hwseq->fb_base.quad_part;
93 		addr->quad_part += hwseq->fb_offset.quad_part;
94 		is_in_uma = true;
95 	} else if (hwseq->fb_offset.quad_part <= addr->quad_part &&
96 			addr->quad_part <= hwseq->uma_top.quad_part) {
97 		is_in_uma = true;
98 	} else if (addr->quad_part == 0) {
99 		is_in_uma = false;
100 	} else {
101 		is_in_uma = false;
102 	}
103 }
104 
105 static void plane_address_in_gpu_space_to_uma(struct dce_hwseq *hwseq,
106 		struct dc_plane_address *addr)
107 {
108 	switch (addr->type) {
109 	case PLN_ADDR_TYPE_GRAPHICS:
110 		gpu_addr_to_uma(hwseq, &addr->grph.addr);
111 		gpu_addr_to_uma(hwseq, &addr->grph.meta_addr);
112 		break;
113 	case PLN_ADDR_TYPE_GRPH_STEREO:
114 		gpu_addr_to_uma(hwseq, &addr->grph_stereo.left_addr);
115 		gpu_addr_to_uma(hwseq, &addr->grph_stereo.left_meta_addr);
116 		gpu_addr_to_uma(hwseq, &addr->grph_stereo.right_addr);
117 		gpu_addr_to_uma(hwseq, &addr->grph_stereo.right_meta_addr);
118 		break;
119 	case PLN_ADDR_TYPE_VIDEO_PROGRESSIVE:
120 		gpu_addr_to_uma(hwseq, &addr->video_progressive.luma_addr);
121 		gpu_addr_to_uma(hwseq, &addr->video_progressive.luma_meta_addr);
122 		gpu_addr_to_uma(hwseq, &addr->video_progressive.chroma_addr);
123 		gpu_addr_to_uma(hwseq, &addr->video_progressive.chroma_meta_addr);
124 		break;
125 	default:
126 		BREAK_TO_DEBUGGER();
127 		break;
128 	}
129 }
130 
131 void dcn201_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx)
132 {
133 	bool addr_patched = false;
134 	PHYSICAL_ADDRESS_LOC addr;
135 	struct dc_plane_state *plane_state = pipe_ctx->plane_state;
136 	struct dce_hwseq *hws = dc->hwseq;
137 	struct dc_plane_address uma;
138 
139 	if (plane_state == NULL)
140 		return;
141 
142 	uma = plane_state->address;
143 	addr_patched = patch_address_for_sbs_tb_stereo(pipe_ctx, &addr);
144 
145 	plane_address_in_gpu_space_to_uma(hws, &uma);
146 
147 	pipe_ctx->plane_res.hubp->funcs->hubp_program_surface_flip_and_addr(
148 			pipe_ctx->plane_res.hubp,
149 			&uma,
150 			plane_state->flip_immediate);
151 
152 	plane_state->status.requested_address = plane_state->address;
153 
154 	if (plane_state->flip_immediate)
155 		plane_state->status.current_address = plane_state->address;
156 
157 	if (addr_patched)
158 		pipe_ctx->plane_state->address.grph_stereo.left_addr = addr;
159 }
160 
161 /* Blank pixel data during initialization */
162 void dcn201_init_blank(
163 		struct dc *dc,
164 		struct timing_generator *tg)
165 {
166 	struct dce_hwseq *hws = dc->hwseq;
167 	enum dc_color_space color_space;
168 	struct tg_color black_color = {0};
169 	struct output_pixel_processor *opp = NULL;
170 	uint32_t num_opps, opp_id_src0, opp_id_src1;
171 	uint32_t otg_active_width, otg_active_height;
172 
173 	/* program opp dpg blank color */
174 	color_space = COLOR_SPACE_SRGB;
175 	color_space_to_black_color(dc, color_space, &black_color);
176 
177 	/* get the OTG active size */
178 	tg->funcs->get_otg_active_size(tg,
179 			&otg_active_width,
180 			&otg_active_height);
181 
182 	/* get the OPTC source */
183 	tg->funcs->get_optc_source(tg, &num_opps, &opp_id_src0, &opp_id_src1);
184 	ASSERT(opp_id_src0 < dc->res_pool->res_cap->num_opp);
185 	opp = dc->res_pool->opps[opp_id_src0];
186 
187 	opp->funcs->opp_set_disp_pattern_generator(
188 			opp,
189 			CONTROLLER_DP_TEST_PATTERN_SOLID_COLOR,
190 			CONTROLLER_DP_COLOR_SPACE_UDEFINED,
191 			COLOR_DEPTH_UNDEFINED,
192 			&black_color,
193 			otg_active_width,
194 			otg_active_height,
195 			0);
196 
197 	hws->funcs.wait_for_blank_complete(opp);
198 }
199 
200 static void read_mmhub_vm_setup(struct dce_hwseq *hws)
201 {
202 	uint32_t fb_base = REG_READ(MC_VM_FB_LOCATION_BASE);
203 	uint32_t fb_top = REG_READ(MC_VM_FB_LOCATION_TOP);
204 	uint32_t fb_offset = REG_READ(MC_VM_FB_OFFSET);
205 
206 	/* MC_VM_FB_LOCATION_TOP is in pages, actual top should add 1 */
207 	fb_top++;
208 
209 	/* bit 23:0 in register map to bit 47:24 in address */
210 	hws->fb_base.low_part = fb_base;
211 	hws->fb_base.quad_part <<= 24;
212 
213 	hws->fb_top.low_part  = fb_top;
214 	hws->fb_top.quad_part <<= 24;
215 	hws->fb_offset.low_part = fb_offset;
216 	hws->fb_offset.quad_part <<= 24;
217 
218 	hws->uma_top.quad_part = hws->fb_top.quad_part
219 			- hws->fb_base.quad_part + hws->fb_offset.quad_part;
220 }
221 
222 void dcn201_init_hw(struct dc *dc)
223 {
224 	int i, j;
225 	struct dce_hwseq *hws = dc->hwseq;
226 	struct resource_pool *res_pool = dc->res_pool;
227 	struct dc_state  *context = dc->current_state;
228 
229 	if (res_pool->dccg->funcs->dccg_init)
230 		res_pool->dccg->funcs->dccg_init(res_pool->dccg);
231 
232 	if (dc->clk_mgr && dc->clk_mgr->funcs->init_clocks)
233 		dc->clk_mgr->funcs->init_clocks(dc->clk_mgr);
234 
235 	if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) {
236 		REG_WRITE(RBBMIF_TIMEOUT_DIS, 0xFFFFFFFF);
237 		REG_WRITE(RBBMIF_TIMEOUT_DIS_2, 0xFFFFFFFF);
238 
239 		hws->funcs.dccg_init(hws);
240 
241 		REG_UPDATE(DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_REFDIV, 2);
242 		REG_UPDATE(DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_ENABLE, 1);
243 		REG_WRITE(REFCLK_CNTL, 0);
244 	} else {
245 		hws->funcs.bios_golden_init(dc);
246 
247 		if (dc->ctx->dc_bios->fw_info_valid) {
248 			res_pool->ref_clocks.xtalin_clock_inKhz =
249 				dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency;
250 
251 			if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) {
252 				if (res_pool->dccg && res_pool->hubbub) {
253 					(res_pool->dccg->funcs->get_dccg_ref_freq)(res_pool->dccg,
254 							dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency,
255 							&res_pool->ref_clocks.dccg_ref_clock_inKhz);
256 
257 					(res_pool->hubbub->funcs->get_dchub_ref_freq)(res_pool->hubbub,
258 							res_pool->ref_clocks.dccg_ref_clock_inKhz,
259 							&res_pool->ref_clocks.dchub_ref_clock_inKhz);
260 				} else {
261 					res_pool->ref_clocks.dccg_ref_clock_inKhz =
262 							res_pool->ref_clocks.xtalin_clock_inKhz;
263 					res_pool->ref_clocks.dchub_ref_clock_inKhz =
264 							res_pool->ref_clocks.xtalin_clock_inKhz;
265 				}
266 			}
267 		} else
268 			ASSERT_CRITICAL(false);
269 		for (i = 0; i < dc->link_count; i++) {
270 			/* Power up AND update implementation according to the
271 			 * required signal (which may be different from the
272 			 * default signal on connector).
273 			 */
274 			struct dc_link *link = dc->links[i];
275 
276 			link->link_enc->funcs->hw_init(link->link_enc);
277 		}
278 		if (hws->fb_offset.quad_part == 0)
279 			read_mmhub_vm_setup(hws);
280 	}
281 
282 	/* Blank pixel data with OPP DPG */
283 	for (i = 0; i < res_pool->timing_generator_count; i++) {
284 		struct timing_generator *tg = res_pool->timing_generators[i];
285 
286 		if (tg->funcs->is_tg_enabled(tg)) {
287 			dcn201_init_blank(dc, tg);
288 		}
289 	}
290 
291 	for (i = 0; i < res_pool->timing_generator_count; i++) {
292 		struct timing_generator *tg = res_pool->timing_generators[i];
293 
294 		if (tg->funcs->is_tg_enabled(tg))
295 			tg->funcs->lock(tg);
296 	}
297 
298 	for (i = 0; i < res_pool->pipe_count; i++) {
299 		struct dpp *dpp = res_pool->dpps[i];
300 
301 		dpp->funcs->dpp_reset(dpp);
302 	}
303 
304 	/* Reset all MPCC muxes */
305 	res_pool->mpc->funcs->mpc_init(res_pool->mpc);
306 
307 	/* initialize OPP mpc_tree parameter */
308 	for (i = 0; i < res_pool->res_cap->num_opp; i++) {
309 		res_pool->opps[i]->mpc_tree_params.opp_id = res_pool->opps[i]->inst;
310 		res_pool->opps[i]->mpc_tree_params.opp_list = NULL;
311 		for (j = 0; j < MAX_PIPES; j++)
312 			res_pool->opps[i]->mpcc_disconnect_pending[j] = false;
313 	}
314 
315 	for (i = 0; i < res_pool->timing_generator_count; i++) {
316 		struct timing_generator *tg = res_pool->timing_generators[i];
317 		struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
318 		struct hubp *hubp = res_pool->hubps[i];
319 		struct dpp *dpp = res_pool->dpps[i];
320 
321 		pipe_ctx->stream_res.tg = tg;
322 		pipe_ctx->pipe_idx = i;
323 
324 		pipe_ctx->plane_res.hubp = hubp;
325 		pipe_ctx->plane_res.dpp = dpp;
326 		pipe_ctx->plane_res.mpcc_inst = dpp->inst;
327 		hubp->mpcc_id = dpp->inst;
328 		hubp->opp_id = OPP_ID_INVALID;
329 		hubp->power_gated = false;
330 		pipe_ctx->stream_res.opp = NULL;
331 
332 		hubp->funcs->hubp_init(hubp);
333 
334 		res_pool->opps[i]->mpcc_disconnect_pending[pipe_ctx->plane_res.mpcc_inst] = true;
335 		pipe_ctx->stream_res.opp = res_pool->opps[i];
336 		/*To do: number of MPCC != number of opp*/
337 		hws->funcs.plane_atomic_disconnect(dc, pipe_ctx);
338 	}
339 
340 	/* initialize DWB pointer to MCIF_WB */
341 	for (i = 0; i < res_pool->res_cap->num_dwb; i++)
342 		res_pool->dwbc[i]->mcif = res_pool->mcif_wb[i];
343 
344 	for (i = 0; i < res_pool->timing_generator_count; i++) {
345 		struct timing_generator *tg = res_pool->timing_generators[i];
346 
347 		if (tg->funcs->is_tg_enabled(tg))
348 			tg->funcs->unlock(tg);
349 	}
350 
351 	for (i = 0; i < res_pool->pipe_count; i++) {
352 		struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
353 
354 		dc->hwss.disable_plane(dc, pipe_ctx);
355 
356 		pipe_ctx->stream_res.tg = NULL;
357 		pipe_ctx->plane_res.hubp = NULL;
358 	}
359 
360 	for (i = 0; i < res_pool->timing_generator_count; i++) {
361 		struct timing_generator *tg = res_pool->timing_generators[i];
362 
363 		tg->funcs->tg_init(tg);
364 	}
365 
366 	/* end of FPGA. Below if real ASIC */
367 	if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment))
368 		return;
369 
370 	for (i = 0; i < res_pool->audio_count; i++) {
371 		struct audio *audio = res_pool->audios[i];
372 
373 		audio->funcs->hw_init(audio);
374 	}
375 
376 	/* power AFMT HDMI memory TODO: may move to dis/en output save power*/
377 	REG_WRITE(DIO_MEM_PWR_CTRL, 0);
378 
379 	if (!dc->debug.disable_clock_gate) {
380 		/* enable all DCN clock gating */
381 		REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0);
382 
383 		REG_WRITE(DCCG_GATE_DISABLE_CNTL2, 0);
384 
385 		REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0);
386 	}
387 }
388 
389 /* trigger HW to start disconnect plane from stream on the next vsync */
390 void dcn201_plane_atomic_disconnect(struct dc *dc, struct pipe_ctx *pipe_ctx)
391 {
392 	struct dce_hwseq *hws = dc->hwseq;
393 	struct hubp *hubp = pipe_ctx->plane_res.hubp;
394 	int dpp_id = pipe_ctx->plane_res.dpp->inst;
395 	struct mpc *mpc = dc->res_pool->mpc;
396 	struct mpc_tree *mpc_tree_params;
397 	struct mpcc *mpcc_to_remove = NULL;
398 	struct output_pixel_processor *opp = pipe_ctx->stream_res.opp;
399 	bool mpcc_removed = false;
400 
401 	mpc_tree_params = &(opp->mpc_tree_params);
402 
403 	/* check if this plane is being used by an MPCC in the secondary blending chain */
404 	if (mpc->funcs->get_mpcc_for_dpp_from_secondary)
405 		mpcc_to_remove = mpc->funcs->get_mpcc_for_dpp_from_secondary(mpc_tree_params, dpp_id);
406 
407 	/* remove MPCC from secondary if being used */
408 	if (mpcc_to_remove != NULL && mpc->funcs->remove_mpcc_from_secondary) {
409 		mpc->funcs->remove_mpcc_from_secondary(mpc, mpc_tree_params, mpcc_to_remove);
410 		mpcc_removed = true;
411 	}
412 
413 	/* check if this MPCC is already being used for this plane (dpp) in the primary blending chain */
414 	mpcc_to_remove = mpc->funcs->get_mpcc_for_dpp(mpc_tree_params, dpp_id);
415 	if (mpcc_to_remove != NULL) {
416 		mpc->funcs->remove_mpcc(mpc, mpc_tree_params, mpcc_to_remove);
417 		mpcc_removed = true;
418 	}
419 
420 	/*Already reset*/
421 	if (mpcc_removed == false)
422 		return;
423 
424 	if (opp != NULL)
425 		opp->mpcc_disconnect_pending[pipe_ctx->plane_res.mpcc_inst] = true;
426 
427 	dc->optimized_required = true;
428 
429 	if (hubp->funcs->hubp_disconnect)
430 		hubp->funcs->hubp_disconnect(hubp);
431 
432 	if (dc->debug.sanity_checks)
433 		hws->funcs.verify_allow_pstate_change_high(dc);
434 }
435 
436 void dcn201_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx)
437 {
438 	struct hubp *hubp = pipe_ctx->plane_res.hubp;
439 	struct mpcc_blnd_cfg blnd_cfg;
440 	bool per_pixel_alpha = pipe_ctx->plane_state->per_pixel_alpha && pipe_ctx->bottom_pipe;
441 	int mpcc_id, dpp_id;
442 	struct mpcc *new_mpcc;
443 	struct mpcc *remove_mpcc = NULL;
444 	struct mpc *mpc = dc->res_pool->mpc;
445 	struct mpc_tree *mpc_tree_params = &(pipe_ctx->stream_res.opp->mpc_tree_params);
446 
447 	if (dc->debug.visual_confirm == VISUAL_CONFIRM_HDR) {
448 		get_hdr_visual_confirm_color(
449 				pipe_ctx, &blnd_cfg.black_color);
450 	} else if (dc->debug.visual_confirm == VISUAL_CONFIRM_SURFACE) {
451 		get_surface_visual_confirm_color(
452 				pipe_ctx, &blnd_cfg.black_color);
453 	} else {
454 		color_space_to_black_color(
455 				dc, pipe_ctx->stream->output_color_space,
456 				&blnd_cfg.black_color);
457 	}
458 
459 	if (per_pixel_alpha)
460 		blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA;
461 	else
462 		blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_GLOBAL_ALPHA;
463 
464 	blnd_cfg.overlap_only = false;
465 
466 	if (pipe_ctx->plane_state->global_alpha_value)
467 		blnd_cfg.global_alpha = pipe_ctx->plane_state->global_alpha_value;
468 	else
469 		blnd_cfg.global_alpha = 0xff;
470 
471 	blnd_cfg.global_gain = 0xff;
472 	blnd_cfg.background_color_bpc = 4;
473 	blnd_cfg.bottom_gain_mode = 0;
474 	blnd_cfg.top_gain = 0x1f000;
475 	blnd_cfg.bottom_inside_gain = 0x1f000;
476 	blnd_cfg.bottom_outside_gain = 0x1f000;
477 	/*the input to MPCC is RGB*/
478 	blnd_cfg.black_color.color_b_cb = 0;
479 	blnd_cfg.black_color.color_g_y = 0;
480 	blnd_cfg.black_color.color_r_cr = 0;
481 
482 	/* DCN1.0 has output CM before MPC which seems to screw with
483 	 * pre-multiplied alpha. This is a w/a hopefully unnecessary for DCN2.
484 	 */
485 	blnd_cfg.pre_multiplied_alpha = per_pixel_alpha;
486 
487 	/*
488 	 * TODO: remove hack
489 	 * Note: currently there is a bug in init_hw such that
490 	 * on resume from hibernate, BIOS sets up MPCC0, and
491 	 * we do mpcc_remove but the mpcc cannot go to idle
492 	 * after remove. This cause us to pick mpcc1 here,
493 	 * which causes a pstate hang for yet unknown reason.
494 	 */
495 	dpp_id = hubp->inst;
496 	mpcc_id = dpp_id;
497 
498 	/* If there is no full update, don't need to touch MPC tree*/
499 	if (!pipe_ctx->plane_state->update_flags.bits.full_update) {
500 		dc->hwss.update_visual_confirm_color(dc, pipe_ctx, &blnd_cfg.black_color, mpcc_id);
501 		mpc->funcs->update_blending(mpc, &blnd_cfg, mpcc_id);
502 		return;
503 	}
504 
505 	/* check if this plane is being used by an MPCC in the secondary blending chain */
506 	if (mpc->funcs->get_mpcc_for_dpp_from_secondary)
507 		remove_mpcc = mpc->funcs->get_mpcc_for_dpp_from_secondary(mpc_tree_params, dpp_id);
508 
509 	/* remove MPCC from secondary if being used */
510 	if (remove_mpcc != NULL && mpc->funcs->remove_mpcc_from_secondary)
511 		mpc->funcs->remove_mpcc_from_secondary(mpc, mpc_tree_params, remove_mpcc);
512 
513 	/* check if this MPCC is already being used for this plane (dpp) in the primary blending chain */
514 	remove_mpcc = mpc->funcs->get_mpcc_for_dpp(mpc_tree_params, dpp_id);
515 	/* remove MPCC if being used */
516 
517 	if (remove_mpcc != NULL)
518 		mpc->funcs->remove_mpcc(mpc, mpc_tree_params, remove_mpcc);
519 	else
520 		if (dc->debug.sanity_checks)
521 			mpc->funcs->assert_mpcc_idle_before_connect(
522 					dc->res_pool->mpc, mpcc_id);
523 
524 	/* Call MPC to insert new plane */
525 	dc->hwss.update_visual_confirm_color(dc, pipe_ctx, &blnd_cfg.black_color, mpcc_id);
526 	new_mpcc = mpc->funcs->insert_plane(dc->res_pool->mpc,
527 			mpc_tree_params,
528 			&blnd_cfg,
529 			NULL,
530 			NULL,
531 			dpp_id,
532 			mpcc_id);
533 
534 	ASSERT(new_mpcc != NULL);
535 	hubp->opp_id = pipe_ctx->stream_res.opp->inst;
536 	hubp->mpcc_id = mpcc_id;
537 }
538 
539 void dcn201_pipe_control_lock(
540 	struct dc *dc,
541 	struct pipe_ctx *pipe,
542 	bool lock)
543 {
544 	struct dce_hwseq *hws = dc->hwseq;
545 	struct hubp *hubp = NULL;
546 	hubp = dc->res_pool->hubps[pipe->pipe_idx];
547 	/* use TG master update lock to lock everything on the TG
548 	 * therefore only top pipe need to lock
549 	 */
550 	if (pipe->top_pipe)
551 		return;
552 
553 	if (dc->debug.sanity_checks)
554 		hws->funcs.verify_allow_pstate_change_high(dc);
555 
556 	if (pipe->plane_state != NULL && pipe->plane_state->triplebuffer_flips) {
557 		if (lock)
558 			pipe->stream_res.tg->funcs->triplebuffer_lock(pipe->stream_res.tg);
559 		else
560 			pipe->stream_res.tg->funcs->triplebuffer_unlock(pipe->stream_res.tg);
561 	} else {
562 		if (lock)
563 			pipe->stream_res.tg->funcs->lock(pipe->stream_res.tg);
564 		else
565 			pipe->stream_res.tg->funcs->unlock(pipe->stream_res.tg);
566 	}
567 
568 	if (dc->debug.sanity_checks)
569 		hws->funcs.verify_allow_pstate_change_high(dc);
570 }
571 
572 void dcn201_set_cursor_attribute(struct pipe_ctx *pipe_ctx)
573 {
574 	struct dc_cursor_attributes *attributes = &pipe_ctx->stream->cursor_attributes;
575 
576 	gpu_addr_to_uma(pipe_ctx->stream->ctx->dc->hwseq, &attributes->address);
577 
578 	pipe_ctx->plane_res.hubp->funcs->set_cursor_attributes(
579 			pipe_ctx->plane_res.hubp, attributes);
580 	pipe_ctx->plane_res.dpp->funcs->set_cursor_attributes(
581 		pipe_ctx->plane_res.dpp, attributes);
582 }
583 
584 void dcn201_set_dmdata_attributes(struct pipe_ctx *pipe_ctx)
585 {
586 	struct dc_dmdata_attributes attr = { 0 };
587 	struct hubp *hubp = pipe_ctx->plane_res.hubp;
588 
589 	gpu_addr_to_uma(pipe_ctx->stream->ctx->dc->hwseq,
590 			&pipe_ctx->stream->dmdata_address);
591 
592 	attr.dmdata_mode = DMDATA_HW_MODE;
593 	attr.dmdata_size =
594 		dc_is_hdmi_signal(pipe_ctx->stream->signal) ? 32 : 36;
595 	attr.address.quad_part =
596 			pipe_ctx->stream->dmdata_address.quad_part;
597 	attr.dmdata_dl_delta = 0;
598 	attr.dmdata_qos_mode = 0;
599 	attr.dmdata_qos_level = 0;
600 	attr.dmdata_repeat = 1; /* always repeat */
601 	attr.dmdata_updated = 1;
602 	attr.dmdata_sw_data = NULL;
603 
604 	hubp->funcs->dmdata_set_attributes(hubp, &attr);
605 }
606 
607 void dcn201_unblank_stream(struct pipe_ctx *pipe_ctx,
608 		struct dc_link_settings *link_settings)
609 {
610 	struct encoder_unblank_param params = { { 0 } };
611 	struct dc_stream_state *stream = pipe_ctx->stream;
612 	struct dc_link *link = stream->link;
613 	struct dce_hwseq *hws = link->dc->hwseq;
614 
615 	/* only 3 items below are used by unblank */
616 	params.timing = pipe_ctx->stream->timing;
617 
618 	params.link_settings.link_rate = link_settings->link_rate;
619 
620 	if (dc_is_dp_signal(pipe_ctx->stream->signal)) {
621 		/*check whether it is half the rate*/
622 		if (optc201_is_two_pixels_per_containter(&stream->timing))
623 			params.timing.pix_clk_100hz /= 2;
624 
625 		pipe_ctx->stream_res.stream_enc->funcs->dp_unblank(link, pipe_ctx->stream_res.stream_enc, &params);
626 	}
627 
628 	if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) {
629 		hws->funcs.edp_backlight_control(link, true);
630 	}
631 }
632