Home | History | Annotate | Line # | Download | only in core
      1 /*	$NetBSD: amdgpu_dc_stream.c,v 1.3 2023/08/15 05:01:57 mrg Exp $	*/
      2 
      3 /*
      4  * Copyright 2012-15 Advanced Micro Devices, Inc.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     22  * OTHER DEALINGS IN THE SOFTWARE.
     23  *
     24  * Authors: AMD
     25  *
     26  */
     27 
     28 #include <sys/cdefs.h>
     29 __KERNEL_RCSID(0, "$NetBSD: amdgpu_dc_stream.c,v 1.3 2023/08/15 05:01:57 mrg Exp $");
     30 
     31 #include <linux/delay.h>
     32 #include <linux/slab.h>
     33 
     34 #include "dm_services.h"
     35 #include "basics/dc_common.h"
     36 #include "dc.h"
     37 #include "core_types.h"
     38 #include "resource.h"
     39 #include "ipp.h"
     40 #include "timing_generator.h"
     41 
     42 #define DC_LOGGER dc->ctx->logger
     43 
     44 /*******************************************************************************
     45  * Private functions
     46  ******************************************************************************/
     47 void update_stream_signal(struct dc_stream_state *stream, struct dc_sink *sink)
     48 {
     49 	if (sink->sink_signal == SIGNAL_TYPE_NONE)
     50 		stream->signal = stream->link->connector_signal;
     51 	else
     52 		stream->signal = sink->sink_signal;
     53 
     54 	if (dc_is_dvi_signal(stream->signal)) {
     55 		if (stream->ctx->dc->caps.dual_link_dvi &&
     56 			(stream->timing.pix_clk_100hz / 10) > TMDS_MAX_PIXEL_CLOCK &&
     57 			sink->sink_signal != SIGNAL_TYPE_DVI_SINGLE_LINK)
     58 			stream->signal = SIGNAL_TYPE_DVI_DUAL_LINK;
     59 		else
     60 			stream->signal = SIGNAL_TYPE_DVI_SINGLE_LINK;
     61 	}
     62 }
     63 
     64 static void dc_stream_construct(struct dc_stream_state *stream,
     65 	struct dc_sink *dc_sink_data)
     66 {
     67 	uint32_t i = 0;
     68 
     69 	stream->sink = dc_sink_data;
     70 	dc_sink_retain(dc_sink_data);
     71 
     72 	stream->ctx = dc_sink_data->ctx;
     73 	stream->link = dc_sink_data->link;
     74 	stream->sink_patches = dc_sink_data->edid_caps.panel_patch;
     75 	stream->converter_disable_audio = dc_sink_data->converter_disable_audio;
     76 	stream->qs_bit = dc_sink_data->edid_caps.qs_bit;
     77 	stream->qy_bit = dc_sink_data->edid_caps.qy_bit;
     78 
     79 	/* Copy audio modes */
     80 	/* TODO - Remove this translation */
     81 	for (i = 0; i < (dc_sink_data->edid_caps.audio_mode_count); i++)
     82 	{
     83 		stream->audio_info.modes[i].channel_count = dc_sink_data->edid_caps.audio_modes[i].channel_count;
     84 		stream->audio_info.modes[i].format_code = dc_sink_data->edid_caps.audio_modes[i].format_code;
     85 		stream->audio_info.modes[i].sample_rates.all = dc_sink_data->edid_caps.audio_modes[i].sample_rate;
     86 		stream->audio_info.modes[i].sample_size = dc_sink_data->edid_caps.audio_modes[i].sample_size;
     87 	}
     88 	stream->audio_info.mode_count = dc_sink_data->edid_caps.audio_mode_count;
     89 	stream->audio_info.audio_latency = dc_sink_data->edid_caps.audio_latency;
     90 	stream->audio_info.video_latency = dc_sink_data->edid_caps.video_latency;
     91 	memmove(
     92 		stream->audio_info.display_name,
     93 		dc_sink_data->edid_caps.display_name,
     94 		AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS);
     95 	stream->audio_info.manufacture_id = dc_sink_data->edid_caps.manufacturer_id;
     96 	stream->audio_info.product_id = dc_sink_data->edid_caps.product_id;
     97 	stream->audio_info.flags.all = dc_sink_data->edid_caps.speaker_flags;
     98 
     99 	if (dc_sink_data->dc_container_id != NULL) {
    100 		struct dc_container_id *dc_container_id = dc_sink_data->dc_container_id;
    101 
    102 		stream->audio_info.port_id[0] = dc_container_id->portId[0];
    103 		stream->audio_info.port_id[1] = dc_container_id->portId[1];
    104 	} else {
    105 		/* TODO - WindowDM has implemented,
    106 		other DMs need Unhardcode port_id */
    107 		stream->audio_info.port_id[0] = 0x5558859e;
    108 		stream->audio_info.port_id[1] = 0xd989449;
    109 	}
    110 
    111 	/* EDID CAP translation for HDMI 2.0 */
    112 	stream->timing.flags.LTE_340MCSC_SCRAMBLE = dc_sink_data->edid_caps.lte_340mcsc_scramble;
    113 
    114 	memset(&stream->timing.dsc_cfg, 0, sizeof(stream->timing.dsc_cfg));
    115 	stream->timing.dsc_cfg.num_slices_h = 0;
    116 	stream->timing.dsc_cfg.num_slices_v = 0;
    117 	stream->timing.dsc_cfg.bits_per_pixel = 128;
    118 	stream->timing.dsc_cfg.block_pred_enable = 1;
    119 	stream->timing.dsc_cfg.linebuf_depth = 9;
    120 	stream->timing.dsc_cfg.version_minor = 2;
    121 	stream->timing.dsc_cfg.ycbcr422_simple = 0;
    122 
    123 	update_stream_signal(stream, dc_sink_data);
    124 
    125 	stream->out_transfer_func = dc_create_transfer_func();
    126 	stream->out_transfer_func->type = TF_TYPE_BYPASS;
    127 	stream->out_transfer_func->ctx = stream->ctx;
    128 
    129 	stream->stream_id = stream->ctx->dc_stream_id_count;
    130 	stream->ctx->dc_stream_id_count++;
    131 }
    132 
    133 static void dc_stream_destruct(struct dc_stream_state *stream)
    134 {
    135 	dc_sink_release(stream->sink);
    136 	if (stream->out_transfer_func != NULL) {
    137 		dc_transfer_func_release(stream->out_transfer_func);
    138 		stream->out_transfer_func = NULL;
    139 	}
    140 }
    141 
    142 void dc_stream_retain(struct dc_stream_state *stream)
    143 {
    144 	kref_get(&stream->refcount);
    145 }
    146 
    147 static void dc_stream_free(struct kref *kref)
    148 {
    149 	struct dc_stream_state *stream = container_of(kref, struct dc_stream_state, refcount);
    150 
    151 	dc_stream_destruct(stream);
    152 	kfree(stream);
    153 }
    154 
    155 void dc_stream_release(struct dc_stream_state *stream)
    156 {
    157 	if (stream != NULL) {
    158 		kref_put(&stream->refcount, dc_stream_free);
    159 	}
    160 }
    161 
    162 struct dc_stream_state *dc_create_stream_for_sink(
    163 		struct dc_sink *sink)
    164 {
    165 	struct dc_stream_state *stream;
    166 
    167 	if (sink == NULL)
    168 		return NULL;
    169 
    170 	stream = kzalloc(sizeof(struct dc_stream_state), GFP_KERNEL);
    171 	if (stream == NULL)
    172 		return NULL;
    173 
    174 	dc_stream_construct(stream, sink);
    175 
    176 	kref_init(&stream->refcount);
    177 
    178 	return stream;
    179 }
    180 
    181 struct dc_stream_state *dc_copy_stream(const struct dc_stream_state *stream)
    182 {
    183 	struct dc_stream_state *new_stream;
    184 
    185 	new_stream = kmemdup(stream, sizeof(struct dc_stream_state), GFP_KERNEL);
    186 	if (!new_stream)
    187 		return NULL;
    188 
    189 	if (new_stream->sink)
    190 		dc_sink_retain(new_stream->sink);
    191 
    192 	if (new_stream->out_transfer_func)
    193 		dc_transfer_func_retain(new_stream->out_transfer_func);
    194 
    195 	new_stream->stream_id = new_stream->ctx->dc_stream_id_count;
    196 	new_stream->ctx->dc_stream_id_count++;
    197 
    198 	kref_init(&new_stream->refcount);
    199 
    200 	return new_stream;
    201 }
    202 
    203 /**
    204  * dc_stream_get_status_from_state - Get stream status from given dc state
    205  * @state: DC state to find the stream status in
    206  * @stream: The stream to get the stream status for
    207  *
    208  * The given stream is expected to exist in the given dc state. Otherwise, NULL
    209  * will be returned.
    210  */
    211 struct dc_stream_status *dc_stream_get_status_from_state(
    212 	struct dc_state *state,
    213 	struct dc_stream_state *stream)
    214 {
    215 	uint8_t i;
    216 
    217 	for (i = 0; i < state->stream_count; i++) {
    218 		if (stream == state->streams[i])
    219 			return &state->stream_status[i];
    220 	}
    221 
    222 	return NULL;
    223 }
    224 
    225 /**
    226  * dc_stream_get_status() - Get current stream status of the given stream state
    227  * @stream: The stream to get the stream status for.
    228  *
    229  * The given stream is expected to exist in dc->current_state. Otherwise, NULL
    230  * will be returned.
    231  */
    232 struct dc_stream_status *dc_stream_get_status(
    233 	struct dc_stream_state *stream)
    234 {
    235 	struct dc *dc = stream->ctx->dc;
    236 	return dc_stream_get_status_from_state(dc->current_state, stream);
    237 }
    238 
    239 static void delay_cursor_until_vupdate(struct pipe_ctx *pipe_ctx, struct dc *dc)
    240 {
    241 #if defined(CONFIG_DRM_AMD_DC_DCN)
    242 	unsigned int vupdate_line;
    243 	unsigned int lines_to_vupdate, us_to_vupdate, vpos, nvpos;
    244 	struct dc_stream_state *stream = pipe_ctx->stream;
    245 	unsigned int us_per_line;
    246 
    247 	if (stream->ctx->asic_id.chip_family == FAMILY_RV &&
    248 			ASICREV_IS_RAVEN(stream->ctx->asic_id.hw_internal_rev)) {
    249 
    250 		vupdate_line = dc->hwss.get_vupdate_offset_from_vsync(pipe_ctx);
    251 		if (!dc_stream_get_crtc_position(dc, &stream, 1, &vpos, &nvpos))
    252 			return;
    253 
    254 		if (vpos >= vupdate_line)
    255 			return;
    256 
    257 		us_per_line = stream->timing.h_total * 10000 / stream->timing.pix_clk_100hz;
    258 		lines_to_vupdate = vupdate_line - vpos;
    259 		us_to_vupdate = lines_to_vupdate * us_per_line;
    260 
    261 		/* 70 us is a conservative estimate of cursor update time*/
    262 		if (us_to_vupdate < 70)
    263 			udelay(us_to_vupdate);
    264 	}
    265 #endif
    266 }
    267 
    268 /**
    269  * dc_stream_set_cursor_attributes() - Update cursor attributes and set cursor surface address
    270  */
    271 bool dc_stream_set_cursor_attributes(
    272 	struct dc_stream_state *stream,
    273 	const struct dc_cursor_attributes *attributes)
    274 {
    275 	int i;
    276 	struct dc  *dc;
    277 	struct resource_context *res_ctx;
    278 	struct pipe_ctx *pipe_to_program = NULL;
    279 
    280 	if (NULL == stream) {
    281 		dm_error("DC: dc_stream is NULL!\n");
    282 		return false;
    283 	}
    284 	if (NULL == attributes) {
    285 		dm_error("DC: attributes is NULL!\n");
    286 		return false;
    287 	}
    288 
    289 	if (attributes->address.quad_part == 0) {
    290 		dm_output_to_console("DC: Cursor address is 0!\n");
    291 		return false;
    292 	}
    293 
    294 	dc = stream->ctx->dc;
    295 	res_ctx = &dc->current_state->res_ctx;
    296 	stream->cursor_attributes = *attributes;
    297 
    298 	for (i = 0; i < MAX_PIPES; i++) {
    299 		struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i];
    300 
    301 		if (pipe_ctx->stream != stream)
    302 			continue;
    303 
    304 		if (!pipe_to_program) {
    305 			pipe_to_program = pipe_ctx;
    306 
    307 			delay_cursor_until_vupdate(pipe_ctx, dc);
    308 			dc->hwss.pipe_control_lock(dc, pipe_to_program, true);
    309 		}
    310 
    311 		dc->hwss.set_cursor_attribute(pipe_ctx);
    312 		if (dc->hwss.set_cursor_sdr_white_level)
    313 			dc->hwss.set_cursor_sdr_white_level(pipe_ctx);
    314 	}
    315 
    316 	if (pipe_to_program)
    317 		dc->hwss.pipe_control_lock(dc, pipe_to_program, false);
    318 
    319 	return true;
    320 }
    321 
    322 bool dc_stream_set_cursor_position(
    323 	struct dc_stream_state *stream,
    324 	const struct dc_cursor_position *position)
    325 {
    326 	int i;
    327 	struct dc  *dc;
    328 	struct resource_context *res_ctx;
    329 	struct pipe_ctx *pipe_to_program = NULL;
    330 
    331 	if (NULL == stream) {
    332 		dm_error("DC: dc_stream is NULL!\n");
    333 		return false;
    334 	}
    335 
    336 	if (NULL == position) {
    337 		dm_error("DC: cursor position is NULL!\n");
    338 		return false;
    339 	}
    340 
    341 	dc = stream->ctx->dc;
    342 	res_ctx = &dc->current_state->res_ctx;
    343 	stream->cursor_position = *position;
    344 
    345 	for (i = 0; i < MAX_PIPES; i++) {
    346 		struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i];
    347 
    348 		if (pipe_ctx->stream != stream ||
    349 				(!pipe_ctx->plane_res.mi  && !pipe_ctx->plane_res.hubp) ||
    350 				!pipe_ctx->plane_state ||
    351 				(!pipe_ctx->plane_res.xfm && !pipe_ctx->plane_res.dpp) ||
    352 				(!pipe_ctx->plane_res.ipp && !pipe_ctx->plane_res.dpp))
    353 			continue;
    354 
    355 		if (!pipe_to_program) {
    356 			pipe_to_program = pipe_ctx;
    357 
    358 			delay_cursor_until_vupdate(pipe_ctx, dc);
    359 			dc->hwss.pipe_control_lock(dc, pipe_to_program, true);
    360 		}
    361 
    362 		dc->hwss.set_cursor_position(pipe_ctx);
    363 	}
    364 
    365 	if (pipe_to_program)
    366 		dc->hwss.pipe_control_lock(dc, pipe_to_program, false);
    367 
    368 	return true;
    369 }
    370 
    371 bool dc_stream_add_writeback(struct dc *dc,
    372 		struct dc_stream_state *stream,
    373 		struct dc_writeback_info *wb_info)
    374 {
    375 	bool isDrc = false;
    376 	int i = 0;
    377 	struct dwbc *dwb;
    378 
    379 	if (stream == NULL) {
    380 		dm_error("DC: dc_stream is NULL!\n");
    381 		return false;
    382 	}
    383 
    384 	if (wb_info == NULL) {
    385 		dm_error("DC: dc_writeback_info is NULL!\n");
    386 		return false;
    387 	}
    388 
    389 	if (wb_info->dwb_pipe_inst >= MAX_DWB_PIPES) {
    390 		dm_error("DC: writeback pipe is invalid!\n");
    391 		return false;
    392 	}
    393 
    394 	wb_info->dwb_params.out_transfer_func = stream->out_transfer_func;
    395 
    396 	dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst];
    397 	dwb->dwb_is_drc = false;
    398 
    399 	/* recalculate and apply DML parameters */
    400 
    401 	for (i = 0; i < stream->num_wb_info; i++) {
    402 		/*dynamic update*/
    403 		if (stream->writeback_info[i].wb_enabled &&
    404 			stream->writeback_info[i].dwb_pipe_inst == wb_info->dwb_pipe_inst) {
    405 			stream->writeback_info[i] = *wb_info;
    406 			isDrc = true;
    407 		}
    408 	}
    409 
    410 	if (!isDrc) {
    411 		stream->writeback_info[stream->num_wb_info++] = *wb_info;
    412 	}
    413 
    414 	if (dc->hwss.enable_writeback) {
    415 		struct dc_stream_status *stream_status = dc_stream_get_status(stream);
    416 		struct dwbc *dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst];
    417 		dwb->otg_inst = stream_status->primary_otg_inst;
    418 	}
    419 	if (IS_DIAG_DC(dc->ctx->dce_environment)) {
    420 		if (!dc->hwss.update_bandwidth(dc, dc->current_state)) {
    421 			dm_error("DC: update_bandwidth failed!\n");
    422 			return false;
    423 		}
    424 
    425 		/* enable writeback */
    426 		if (dc->hwss.enable_writeback) {
    427 			struct dwbc *dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst];
    428 
    429 			if (dwb->funcs->is_enabled(dwb)) {
    430 				/* writeback pipe already enabled, only need to update */
    431 				dc->hwss.update_writeback(dc, wb_info, dc->current_state);
    432 			} else {
    433 				/* Enable writeback pipe from scratch*/
    434 				dc->hwss.enable_writeback(dc, wb_info, dc->current_state);
    435 			}
    436 		}
    437 	}
    438 	return true;
    439 }
    440 
    441 bool dc_stream_remove_writeback(struct dc *dc,
    442 		struct dc_stream_state *stream,
    443 		uint32_t dwb_pipe_inst)
    444 {
    445 	int i = 0, j = 0;
    446 	if (stream == NULL) {
    447 		dm_error("DC: dc_stream is NULL!\n");
    448 		return false;
    449 	}
    450 
    451 	if (dwb_pipe_inst >= MAX_DWB_PIPES) {
    452 		dm_error("DC: writeback pipe is invalid!\n");
    453 		return false;
    454 	}
    455 
    456 //	stream->writeback_info[dwb_pipe_inst].wb_enabled = false;
    457 	for (i = 0; i < stream->num_wb_info; i++) {
    458 		/*dynamic update*/
    459 		if (stream->writeback_info[i].wb_enabled &&
    460 			stream->writeback_info[i].dwb_pipe_inst == dwb_pipe_inst) {
    461 			stream->writeback_info[i].wb_enabled = false;
    462 		}
    463 	}
    464 
    465 	/* remove writeback info for disabled writeback pipes from stream */
    466 	for (i = 0, j = 0; i < stream->num_wb_info; i++) {
    467 		if (stream->writeback_info[i].wb_enabled) {
    468 #ifdef __NetBSD__
    469 			/*
    470 			 * XXXGCC12
    471 			 * The array is only 1 entry long, so i and j must
    472 			 * always be 0 here, so the below test fails.
    473 			 */
    474 			CTASSERT(ARRAY_SIZE(stream->writeback_info) == 1);
    475 #else
    476 			if (i != j)
    477 				/* trim the array */
    478 				stream->writeback_info[j] = stream->writeback_info[i];
    479 #endif
    480 			j++;
    481 		}
    482 	}
    483 	stream->num_wb_info = j;
    484 
    485 	if (IS_DIAG_DC(dc->ctx->dce_environment)) {
    486 		/* recalculate and apply DML parameters */
    487 		if (!dc->hwss.update_bandwidth(dc, dc->current_state)) {
    488 			dm_error("DC: update_bandwidth failed!\n");
    489 			return false;
    490 		}
    491 
    492 		/* disable writeback */
    493 		if (dc->hwss.disable_writeback)
    494 			dc->hwss.disable_writeback(dc, dwb_pipe_inst);
    495 	}
    496 	return true;
    497 }
    498 
    499 bool dc_stream_warmup_writeback(struct dc *dc,
    500 		int num_dwb,
    501 		struct dc_writeback_info *wb_info)
    502 {
    503 	if (dc->hwss.mmhubbub_warmup)
    504 		return dc->hwss.mmhubbub_warmup(dc, num_dwb, wb_info);
    505 	else
    506 		return false;
    507 }
    508 uint32_t dc_stream_get_vblank_counter(const struct dc_stream_state *stream)
    509 {
    510 	uint8_t i;
    511 	struct dc  *dc = stream->ctx->dc;
    512 	struct resource_context *res_ctx =
    513 		&dc->current_state->res_ctx;
    514 
    515 	for (i = 0; i < MAX_PIPES; i++) {
    516 		struct timing_generator *tg = res_ctx->pipe_ctx[i].stream_res.tg;
    517 
    518 		if (res_ctx->pipe_ctx[i].stream != stream)
    519 			continue;
    520 
    521 		return tg->funcs->get_frame_count(tg);
    522 	}
    523 
    524 	return 0;
    525 }
    526 
    527 bool dc_stream_send_dp_sdp(const struct dc_stream_state *stream,
    528 		const uint8_t *custom_sdp_message,
    529 		unsigned int sdp_message_size)
    530 {
    531 	int i;
    532 	struct dc  *dc;
    533 	struct resource_context *res_ctx;
    534 
    535 	if (stream == NULL) {
    536 		dm_error("DC: dc_stream is NULL!\n");
    537 		return false;
    538 	}
    539 
    540 	dc = stream->ctx->dc;
    541 	res_ctx = &dc->current_state->res_ctx;
    542 
    543 	for (i = 0; i < MAX_PIPES; i++) {
    544 		struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i];
    545 
    546 		if (pipe_ctx->stream != stream)
    547 			continue;
    548 
    549 		if (dc->hwss.send_immediate_sdp_message != NULL)
    550 			dc->hwss.send_immediate_sdp_message(pipe_ctx,
    551 								custom_sdp_message,
    552 								sdp_message_size);
    553 		else
    554 			DC_LOG_WARNING("%s:send_immediate_sdp_message not implemented on this ASIC\n",
    555 			__func__);
    556 
    557 	}
    558 
    559 	return true;
    560 }
    561 
    562 bool dc_stream_get_scanoutpos(const struct dc_stream_state *stream,
    563 				  uint32_t *v_blank_start,
    564 				  uint32_t *v_blank_end,
    565 				  uint32_t *h_position,
    566 				  uint32_t *v_position)
    567 {
    568 	uint8_t i;
    569 	bool ret = false;
    570 	struct dc  *dc = stream->ctx->dc;
    571 	struct resource_context *res_ctx =
    572 		&dc->current_state->res_ctx;
    573 
    574 	for (i = 0; i < MAX_PIPES; i++) {
    575 		struct timing_generator *tg = res_ctx->pipe_ctx[i].stream_res.tg;
    576 
    577 		if (res_ctx->pipe_ctx[i].stream != stream)
    578 			continue;
    579 
    580 		tg->funcs->get_scanoutpos(tg,
    581 					  v_blank_start,
    582 					  v_blank_end,
    583 					  h_position,
    584 					  v_position);
    585 
    586 		ret = true;
    587 		break;
    588 	}
    589 
    590 	return ret;
    591 }
    592 
    593 bool dc_stream_dmdata_status_done(struct dc *dc, struct dc_stream_state *stream)
    594 {
    595 	struct pipe_ctx *pipe = NULL;
    596 	int i;
    597 
    598 	if (!dc->hwss.dmdata_status_done)
    599 		return false;
    600 
    601 	for (i = 0; i < MAX_PIPES; i++) {
    602 		pipe = &dc->current_state->res_ctx.pipe_ctx[i];
    603 		if (pipe->stream == stream)
    604 			break;
    605 	}
    606 	/* Stream not found, by default we'll assume HUBP fetched dm data */
    607 	if (i == MAX_PIPES)
    608 		return true;
    609 
    610 	return dc->hwss.dmdata_status_done(pipe);
    611 }
    612 
    613 bool dc_stream_set_dynamic_metadata(struct dc *dc,
    614 		struct dc_stream_state *stream,
    615 		struct dc_dmdata_attributes *attr)
    616 {
    617 	struct pipe_ctx *pipe_ctx = NULL;
    618 	struct hubp *hubp;
    619 	int i;
    620 
    621 	/* Dynamic metadata is only supported on HDMI or DP */
    622 	if (!dc_is_hdmi_signal(stream->signal) && !dc_is_dp_signal(stream->signal))
    623 		return false;
    624 
    625 	/* Check hardware support */
    626 	if (!dc->hwss.program_dmdata_engine)
    627 		return false;
    628 
    629 	for (i = 0; i < MAX_PIPES; i++) {
    630 		pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
    631 		if (pipe_ctx->stream == stream)
    632 			break;
    633 	}
    634 
    635 	if (i == MAX_PIPES)
    636 		return false;
    637 
    638 	hubp = pipe_ctx->plane_res.hubp;
    639 	if (hubp == NULL)
    640 		return false;
    641 
    642 	pipe_ctx->stream->dmdata_address = attr->address;
    643 
    644 	dc->hwss.program_dmdata_engine(pipe_ctx);
    645 
    646 	if (hubp->funcs->dmdata_set_attributes != NULL &&
    647 			pipe_ctx->stream->dmdata_address.quad_part != 0) {
    648 		hubp->funcs->dmdata_set_attributes(hubp, attr);
    649 	}
    650 
    651 	return true;
    652 }
    653 
    654 void dc_stream_log(const struct dc *dc, const struct dc_stream_state *stream)
    655 {
    656 	DC_LOG_DC(
    657 			"core_stream 0x%p: src: %d, %d, %d, %d; dst: %d, %d, %d, %d, colorSpace:%d\n",
    658 			stream,
    659 			stream->src.x,
    660 			stream->src.y,
    661 			stream->src.width,
    662 			stream->src.height,
    663 			stream->dst.x,
    664 			stream->dst.y,
    665 			stream->dst.width,
    666 			stream->dst.height,
    667 			stream->output_color_space);
    668 	DC_LOG_DC(
    669 			"\tpix_clk_khz: %d, h_total: %d, v_total: %d, pixelencoder:%d, displaycolorDepth:%d\n",
    670 			stream->timing.pix_clk_100hz / 10,
    671 			stream->timing.h_total,
    672 			stream->timing.v_total,
    673 			stream->timing.pixel_encoding,
    674 			stream->timing.display_color_depth);
    675 	DC_LOG_DC(
    676 			"\tlink: %d\n",
    677 			stream->link->link_index);
    678 }
    679