xref: /linux/drivers/gpu/drm/amd/display/dc/core/dc_stream.c (revision 32e940f2bd3b16551f23ea44be47f6f5d1746d64)
1 /*
2  * Copyright 2012-15 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 "dc.h"
29 #include "core_types.h"
30 #include "resource.h"
31 #include "ipp.h"
32 #include "timing_generator.h"
33 #include "dc_dmub_srv.h"
34 #include "dc_state_priv.h"
35 #include "dc_stream_priv.h"
36 
37 #define DC_LOGGER dc->ctx->logger
38 #ifndef MIN
39 #define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
40 #endif
41 #ifndef MAX
42 #define MAX(x, y) ((x > y) ? x : y)
43 #endif
44 
45 #include "dc_fpu.h"
46 
47 #if !defined(DC_RUN_WITH_PREEMPTION_ENABLED)
48 #define DC_RUN_WITH_PREEMPTION_ENABLED(code) code
49 #endif // !DC_RUN_WITH_PREEMPTION_ENABLED
50 
51 
52 /*******************************************************************************
53  * Private functions
54  ******************************************************************************/
update_stream_signal(struct dc_stream_state * stream,struct dc_sink * sink)55 void update_stream_signal(struct dc_stream_state *stream, struct dc_sink *sink)
56 {
57 	if (sink->sink_signal == SIGNAL_TYPE_NONE)
58 		stream->signal = stream->link->connector_signal;
59 	else
60 		stream->signal = sink->sink_signal;
61 
62 	if (dc_is_dvi_signal(stream->signal)) {
63 		if (stream->ctx->dc->caps.dual_link_dvi &&
64 			(stream->timing.pix_clk_100hz / 10) > TMDS_MAX_PIXEL_CLOCK &&
65 			sink->sink_signal != SIGNAL_TYPE_DVI_SINGLE_LINK)
66 			stream->signal = SIGNAL_TYPE_DVI_DUAL_LINK;
67 		else
68 			stream->signal = SIGNAL_TYPE_DVI_SINGLE_LINK;
69 	}
70 }
71 
dc_stream_construct(struct dc_stream_state * stream,struct dc_sink * dc_sink_data)72 bool dc_stream_construct(struct dc_stream_state *stream,
73 	struct dc_sink *dc_sink_data)
74 {
75 	uint32_t i = 0;
76 
77 	stream->sink = dc_sink_data;
78 	dc_sink_retain(dc_sink_data);
79 
80 	stream->ctx = dc_sink_data->ctx;
81 	stream->link = dc_sink_data->link;
82 	stream->sink_patches = dc_sink_data->edid_caps.panel_patch;
83 	stream->converter_disable_audio = dc_sink_data->converter_disable_audio;
84 	stream->qs_bit = dc_sink_data->edid_caps.qs_bit;
85 	stream->qy_bit = dc_sink_data->edid_caps.qy_bit;
86 
87 	/* Copy audio modes */
88 	/* TODO - Remove this translation */
89 	for (i = 0; i < (dc_sink_data->edid_caps.audio_mode_count); i++) {
90 		stream->audio_info.modes[i].channel_count = dc_sink_data->edid_caps.audio_modes[i].channel_count;
91 		stream->audio_info.modes[i].format_code = dc_sink_data->edid_caps.audio_modes[i].format_code;
92 		stream->audio_info.modes[i].sample_rates.all = dc_sink_data->edid_caps.audio_modes[i].sample_rate;
93 		stream->audio_info.modes[i].sample_size = dc_sink_data->edid_caps.audio_modes[i].sample_size;
94 	}
95 	stream->audio_info.mode_count = dc_sink_data->edid_caps.audio_mode_count;
96 	stream->audio_info.audio_latency = dc_sink_data->edid_caps.audio_latency;
97 	stream->audio_info.video_latency = dc_sink_data->edid_caps.video_latency;
98 	memmove(
99 		stream->audio_info.display_name,
100 		dc_sink_data->edid_caps.display_name,
101 		AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS);
102 	stream->audio_info.manufacture_id = dc_sink_data->edid_caps.manufacturer_id;
103 	stream->audio_info.product_id = dc_sink_data->edid_caps.product_id;
104 	stream->audio_info.flags.all = dc_sink_data->edid_caps.speaker_flags;
105 
106 	if (dc_sink_data->dc_container_id != NULL) {
107 		struct dc_container_id *dc_container_id = dc_sink_data->dc_container_id;
108 
109 		stream->audio_info.port_id[0] = dc_container_id->portId[0];
110 		stream->audio_info.port_id[1] = dc_container_id->portId[1];
111 	} else {
112 		/* TODO - WindowDM has implemented,
113 		other DMs need Unhardcode port_id */
114 		stream->audio_info.port_id[0] = 0x5558859e;
115 		stream->audio_info.port_id[1] = 0xd989449;
116 	}
117 
118 	/* EDID CAP translation for HDMI 2.0 */
119 	stream->timing.flags.LTE_340MCSC_SCRAMBLE = dc_sink_data->edid_caps.lte_340mcsc_scramble;
120 
121 	memset(&stream->timing.dsc_cfg, 0, sizeof(stream->timing.dsc_cfg));
122 	stream->timing.dsc_cfg.num_slices_h = 0;
123 	stream->timing.dsc_cfg.num_slices_v = 0;
124 	stream->timing.dsc_cfg.bits_per_pixel = 128;
125 	stream->timing.dsc_cfg.block_pred_enable = 1;
126 	stream->timing.dsc_cfg.linebuf_depth = 9;
127 	stream->timing.dsc_cfg.version_minor = 2;
128 	stream->timing.dsc_cfg.ycbcr422_simple = 0;
129 
130 	update_stream_signal(stream, dc_sink_data);
131 
132 	stream->out_transfer_func.type = TF_TYPE_BYPASS;
133 
134 	dc_stream_assign_stream_id(stream);
135 
136 	return true;
137 }
138 
dc_stream_destruct(struct dc_stream_state * stream)139 void dc_stream_destruct(struct dc_stream_state *stream)
140 {
141 	dc_sink_release(stream->sink);
142 }
143 
dc_stream_assign_stream_id(struct dc_stream_state * stream)144 void dc_stream_assign_stream_id(struct dc_stream_state *stream)
145 {
146 	/* MSB is reserved to indicate phantoms */
147 	stream->stream_id = stream->ctx->dc_stream_id_count;
148 	stream->ctx->dc_stream_id_count++;
149 }
150 
dc_stream_retain(struct dc_stream_state * stream)151 void dc_stream_retain(struct dc_stream_state *stream)
152 {
153 	kref_get(&stream->refcount);
154 }
155 
dc_stream_free(struct kref * kref)156 static void dc_stream_free(struct kref *kref)
157 {
158 	struct dc_stream_state *stream = container_of(kref, struct dc_stream_state, refcount);
159 
160 	dc_stream_destruct(stream);
161 	kfree(stream->update_scratch);
162 	kfree(stream);
163 }
164 
dc_stream_release(struct dc_stream_state * stream)165 void dc_stream_release(struct dc_stream_state *stream)
166 {
167 	if (stream != NULL) {
168 		kref_put(&stream->refcount, dc_stream_free);
169 	}
170 }
171 
dc_create_stream_for_sink(struct dc_sink * sink)172 struct dc_stream_state *dc_create_stream_for_sink(
173 		struct dc_sink *sink)
174 {
175 	struct dc_stream_state *stream = NULL;
176 
177 	if (sink == NULL)
178 		goto fail;
179 
180 	DC_RUN_WITH_PREEMPTION_ENABLED(stream = kzalloc_obj(struct dc_stream_state, GFP_ATOMIC));
181 
182 	if (stream == NULL)
183 		goto fail;
184 
185 	DC_RUN_WITH_PREEMPTION_ENABLED(stream->update_scratch =
186 					kzalloc((int32_t) dc_update_scratch_space_size(),
187 						GFP_ATOMIC));
188 
189 	if (stream->update_scratch == NULL)
190 		goto fail;
191 
192 	if (dc_stream_construct(stream, sink) == false)
193 		goto fail;
194 
195 	kref_init(&stream->refcount);
196 
197 	return stream;
198 
199 fail:
200 	if (stream) {
201 		kfree(stream->update_scratch);
202 		kfree(stream);
203 	}
204 
205 	return NULL;
206 }
207 
dc_copy_stream(const struct dc_stream_state * stream)208 struct dc_stream_state *dc_copy_stream(const struct dc_stream_state *stream)
209 {
210 	struct dc_stream_state *new_stream;
211 
212 	new_stream = kmemdup(stream, sizeof(struct dc_stream_state), GFP_KERNEL);
213 	if (!new_stream)
214 		return NULL;
215 
216 	// Scratch is not meant to be reused across copies, as might have self-referential pointers
217 	new_stream->update_scratch = kzalloc(
218 			(int32_t) dc_update_scratch_space_size(),
219 			GFP_KERNEL
220 	);
221 	if (!new_stream->update_scratch) {
222 		kfree(new_stream);
223 		return NULL;
224 	}
225 
226 	if (new_stream->sink)
227 		dc_sink_retain(new_stream->sink);
228 
229 	dc_stream_assign_stream_id(new_stream);
230 
231 	/* If using dynamic encoder assignment, wait till stream committed to assign encoder. */
232 	if (new_stream->ctx->dc->res_pool->funcs->link_encs_assign &&
233 			!new_stream->ctx->dc->config.unify_link_enc_assignment)
234 		new_stream->link_enc = NULL;
235 
236 	kref_init(&new_stream->refcount);
237 
238 	return new_stream;
239 }
240 
241 /**
242  * dc_stream_get_status() - Get current stream status of the given stream state
243  * @stream: The stream to get the stream status for.
244  *
245  * The given stream is expected to exist in dc->current_state. Otherwise, NULL
246  * will be returned.
247  */
dc_stream_get_status(struct dc_stream_state * stream)248 struct dc_stream_status *dc_stream_get_status(
249 	struct dc_stream_state *stream)
250 {
251 	struct dc *dc = stream->ctx->dc;
252 	return dc_state_get_stream_status(dc->current_state, stream);
253 }
254 
dc_stream_get_status_const(const struct dc_stream_state * stream)255 const struct dc_stream_status *dc_stream_get_status_const(
256 	const struct dc_stream_state *stream)
257 {
258 	struct dc *dc = stream->ctx->dc;
259 	return dc_state_get_stream_status(dc->current_state, stream);
260 }
261 
program_cursor_attributes(struct dc * dc,struct dc_stream_state * stream)262 void program_cursor_attributes(
263 	struct dc *dc,
264 	struct dc_stream_state *stream)
265 {
266 	int i;
267 	struct resource_context *res_ctx;
268 	struct pipe_ctx *pipe_to_program = NULL;
269 	bool enable_cursor_offload = dc_dmub_srv_is_cursor_offload_enabled(dc);
270 
271 	if (!stream)
272 		return;
273 
274 	res_ctx = &dc->current_state->res_ctx;
275 
276 	for (i = 0; i < MAX_PIPES; i++) {
277 		struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i];
278 
279 		if (pipe_ctx->stream != stream)
280 			continue;
281 
282 		if (!pipe_to_program) {
283 			pipe_to_program = pipe_ctx;
284 
285 			if (enable_cursor_offload && dc->hwss.begin_cursor_offload_update) {
286 				dc->hwss.begin_cursor_offload_update(dc, pipe_ctx);
287 			} else {
288 				dc->hwss.cursor_lock(dc, pipe_to_program, true);
289 				if (pipe_to_program->next_odm_pipe)
290 					dc->hwss.cursor_lock(dc, pipe_to_program->next_odm_pipe, true);
291 			}
292 		}
293 
294 		dc->hwss.set_cursor_attribute(pipe_ctx);
295 		if (dc->ctx->dmub_srv)
296 			dc_send_update_cursor_info_to_dmu(pipe_ctx, i);
297 		if (dc->hwss.set_cursor_sdr_white_level)
298 			dc->hwss.set_cursor_sdr_white_level(pipe_ctx);
299 		if (enable_cursor_offload && dc->hwss.update_cursor_offload_pipe)
300 			dc->hwss.update_cursor_offload_pipe(dc, pipe_ctx);
301 	}
302 
303 	if (pipe_to_program) {
304 		if (enable_cursor_offload && dc->hwss.commit_cursor_offload_update) {
305 			dc->hwss.commit_cursor_offload_update(dc, pipe_to_program);
306 		} else {
307 			dc->hwss.cursor_lock(dc, pipe_to_program, false);
308 			if (pipe_to_program->next_odm_pipe)
309 				dc->hwss.cursor_lock(dc, pipe_to_program->next_odm_pipe, false);
310 		}
311 	}
312 }
313 
314 /*
315  * dc_stream_check_cursor_attributes() - Check validitity of cursor attributes and surface address
316  */
dc_stream_check_cursor_attributes(const struct dc_stream_state * stream,struct dc_state * state,const struct dc_cursor_attributes * attributes)317 bool dc_stream_check_cursor_attributes(
318 	const struct dc_stream_state *stream,
319 	struct dc_state *state,
320 	const struct dc_cursor_attributes *attributes)
321 {
322 	const struct dc *dc;
323 
324 	unsigned int max_cursor_size;
325 
326 	if (NULL == stream) {
327 		dm_error("DC: dc_stream is NULL!\n");
328 		return false;
329 	}
330 	if (NULL == attributes) {
331 		dm_error("DC: attributes is NULL!\n");
332 		return false;
333 	}
334 
335 	if (attributes->address.quad_part == 0) {
336 		dm_output_to_console("DC: Cursor address is 0!\n");
337 		return false;
338 	}
339 
340 	dc = stream->ctx->dc;
341 
342 	/* SubVP is not compatible with HW cursor larger than what can fit in cursor SRAM.
343 	 * Therefore, if cursor is greater than this, fallback to SW cursor.
344 	 */
345 	if (dc->debug.allow_sw_cursor_fallback && dc->res_pool->funcs->get_max_hw_cursor_size) {
346 		max_cursor_size = dc->res_pool->funcs->get_max_hw_cursor_size(dc, state, stream);
347 		max_cursor_size = max_cursor_size * max_cursor_size * 4;
348 
349 		if (attributes->height * attributes->width * 4 > max_cursor_size) {
350 			return false;
351 		}
352 	}
353 
354 	return true;
355 }
356 
357 /*
358  * dc_stream_set_cursor_attributes() - Update cursor attributes and set cursor surface address
359  */
dc_stream_set_cursor_attributes(struct dc_stream_state * stream,const struct dc_cursor_attributes * attributes)360 bool dc_stream_set_cursor_attributes(
361 	struct dc_stream_state *stream,
362 	const struct dc_cursor_attributes *attributes)
363 {
364 	bool result = false;
365 
366 	if (!stream)
367 		return false;
368 
369 	if (dc_stream_check_cursor_attributes(stream, stream->ctx->dc->current_state, attributes)) {
370 		stream->cursor_attributes = *attributes;
371 		result = true;
372 	}
373 
374 	return result;
375 }
376 
dc_stream_program_cursor_attributes(struct dc_stream_state * stream,const struct dc_cursor_attributes * attributes)377 bool dc_stream_program_cursor_attributes(
378 	struct dc_stream_state *stream,
379 	const struct dc_cursor_attributes *attributes)
380 {
381 	struct dc  *dc;
382 	bool reset_idle_optimizations = false;
383 
384 	if (!stream)
385 		return false;
386 
387 	dc = stream->ctx->dc;
388 
389 	if (dc_stream_set_cursor_attributes(stream, attributes)) {
390 		dc_z10_restore(dc);
391 		/* disable idle optimizations while updating cursor */
392 		if (dc->idle_optimizations_allowed) {
393 			dc_allow_idle_optimizations(dc, false);
394 			reset_idle_optimizations = true;
395 		}
396 
397 		program_cursor_attributes(dc, stream);
398 
399 		/* re-enable idle optimizations if necessary */
400 		if (reset_idle_optimizations && !dc->debug.disable_dmub_reallow_idle)
401 			dc_allow_idle_optimizations(dc, true);
402 
403 		return true;
404 	}
405 
406 	return false;
407 }
408 
program_cursor_position(struct dc * dc,struct dc_stream_state * stream)409 void program_cursor_position(
410 	struct dc *dc,
411 	struct dc_stream_state *stream)
412 {
413 	int i;
414 	struct resource_context *res_ctx;
415 	struct pipe_ctx *pipe_to_program = NULL;
416 	bool enable_cursor_offload = dc_dmub_srv_is_cursor_offload_enabled(dc);
417 
418 	if (!stream)
419 		return;
420 
421 	res_ctx = &dc->current_state->res_ctx;
422 
423 	for (i = 0; i < MAX_PIPES; i++) {
424 		struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i];
425 
426 		if (pipe_ctx->stream != stream ||
427 				(!pipe_ctx->plane_res.mi  && !pipe_ctx->plane_res.hubp) ||
428 				!pipe_ctx->plane_state ||
429 				(!pipe_ctx->plane_res.xfm && !pipe_ctx->plane_res.dpp) ||
430 				(!pipe_ctx->plane_res.ipp && !pipe_ctx->plane_res.dpp))
431 			continue;
432 
433 		if (!pipe_to_program) {
434 			pipe_to_program = pipe_ctx;
435 
436 			if (enable_cursor_offload && dc->hwss.begin_cursor_offload_update)
437 				dc->hwss.begin_cursor_offload_update(dc, pipe_ctx);
438 			else
439 				dc->hwss.cursor_lock(dc, pipe_to_program, true);
440 		}
441 
442 		dc->hwss.set_cursor_position(pipe_ctx);
443 		if (enable_cursor_offload && dc->hwss.update_cursor_offload_pipe)
444 			dc->hwss.update_cursor_offload_pipe(dc, pipe_ctx);
445 
446 		if (dc->ctx->dmub_srv)
447 			dc_send_update_cursor_info_to_dmu(pipe_ctx, i);
448 	}
449 
450 	if (pipe_to_program) {
451 		if (enable_cursor_offload && dc->hwss.commit_cursor_offload_update)
452 			dc->hwss.commit_cursor_offload_update(dc, pipe_to_program);
453 		else
454 			dc->hwss.cursor_lock(dc, pipe_to_program, false);
455 	}
456 }
457 
dc_stream_set_cursor_position(struct dc_stream_state * stream,const struct dc_cursor_position * position)458 bool dc_stream_set_cursor_position(
459 	struct dc_stream_state *stream,
460 	const struct dc_cursor_position *position)
461 {
462 	if (NULL == stream) {
463 		dm_error("DC: dc_stream is NULL!\n");
464 		return false;
465 	}
466 
467 	if (NULL == position) {
468 		dm_error("DC: cursor position is NULL!\n");
469 		return false;
470 	}
471 
472 	stream->cursor_position = *position;
473 
474 
475 	return true;
476 }
477 
dc_stream_program_cursor_position(struct dc_stream_state * stream,const struct dc_cursor_position * position)478 bool dc_stream_program_cursor_position(
479 	struct dc_stream_state *stream,
480 	const struct dc_cursor_position *position)
481 {
482 	struct dc *dc;
483 	bool reset_idle_optimizations = false;
484 	const struct dc_cursor_position *old_position;
485 
486 	if (!stream)
487 		return false;
488 
489 	old_position = &stream->cursor_position;
490 	dc = stream->ctx->dc;
491 
492 	if (dc_stream_set_cursor_position(stream, position)) {
493 		dc_z10_restore(dc);
494 
495 		/* disable idle optimizations if enabling cursor */
496 		if (dc->idle_optimizations_allowed &&
497 		    (!old_position->enable || dc->debug.exit_idle_opt_for_cursor_updates) &&
498 		    position->enable) {
499 			dc_allow_idle_optimizations(dc, false);
500 			reset_idle_optimizations = true;
501 		}
502 
503 		program_cursor_position(dc, stream);
504 		/* re-enable idle optimizations if necessary */
505 		if (reset_idle_optimizations && !dc->debug.disable_dmub_reallow_idle)
506 			dc_allow_idle_optimizations(dc, true);
507 
508 		/* apply/update visual confirm */
509 		if (dc->debug.visual_confirm == VISUAL_CONFIRM_HW_CURSOR) {
510 			/* update software state */
511 			int i;
512 
513 			for (i = 0; i < dc->res_pool->pipe_count; i++) {
514 				struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
515 
516 				/* adjust visual confirm color for all pipes with current stream */
517 				if (stream == pipe_ctx->stream) {
518 					get_cursor_visual_confirm_color(pipe_ctx, &(pipe_ctx->visual_confirm_color));
519 
520 					/* programming hardware */
521 					if (pipe_ctx->plane_state)
522 						dc->hwss.update_visual_confirm_color(dc, pipe_ctx,
523 								pipe_ctx->plane_res.hubp->mpcc_id);
524 				}
525 			}
526 		}
527 
528 		if (stream->drr_trigger_mode == DRR_TRIGGER_ON_FLIP_AND_CURSOR) {
529 			/* apply manual trigger */
530 			int i;
531 
532 			for (i = 0; i < dc->res_pool->pipe_count; i++) {
533 				struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
534 
535 				/* trigger event on first pipe with current stream */
536 				if (stream == pipe_ctx->stream &&
537 				pipe_ctx->stream_res.tg->funcs->program_manual_trigger) {
538 					pipe_ctx->stream_res.tg->funcs->program_manual_trigger(
539 					pipe_ctx->stream_res.tg);
540 					break;
541 				}
542 			}
543 		}
544 
545 		return true;
546 	}
547 
548 	return false;
549 }
550 
dc_stream_add_writeback(struct dc * dc,struct dc_stream_state * stream,struct dc_writeback_info * wb_info)551 bool dc_stream_add_writeback(struct dc *dc,
552 		struct dc_stream_state *stream,
553 		struct dc_writeback_info *wb_info)
554 {
555 	bool isDrc = false;
556 	int i = 0;
557 	struct dwbc *dwb;
558 
559 	if (stream == NULL) {
560 		dm_error("DC: dc_stream is NULL!\n");
561 		return false;
562 	}
563 
564 	if (wb_info == NULL) {
565 		dm_error("DC: dc_writeback_info is NULL!\n");
566 		return false;
567 	}
568 
569 	if (wb_info->dwb_pipe_inst >= MAX_DWB_PIPES) {
570 		dm_error("DC: writeback pipe is invalid!\n");
571 		return false;
572 	}
573 
574 	dc_exit_ips_for_hw_access(dc);
575 
576 	wb_info->dwb_params.out_transfer_func = &stream->out_transfer_func;
577 
578 	dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst];
579 	dwb->dwb_is_drc = false;
580 
581 	/* recalculate and apply DML parameters */
582 
583 	for (i = 0; i < stream->num_wb_info; i++) {
584 		/*dynamic update*/
585 		if (stream->writeback_info[i].wb_enabled &&
586 			stream->writeback_info[i].dwb_pipe_inst == wb_info->dwb_pipe_inst) {
587 			stream->writeback_info[i] = *wb_info;
588 			isDrc = true;
589 		}
590 	}
591 
592 	if (!isDrc) {
593 		ASSERT(stream->num_wb_info + 1 <= MAX_DWB_PIPES);
594 		stream->writeback_info[stream->num_wb_info++] = *wb_info;
595 	}
596 
597 	if (dc->hwss.enable_writeback) {
598 		struct dc_stream_status *stream_status = dc_stream_get_status(stream);
599 		struct dwbc *dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst];
600 		if (stream_status)
601 			dwb->otg_inst = stream_status->primary_otg_inst;
602 	}
603 
604 	if (!dc->hwss.update_bandwidth(dc, dc->current_state)) {
605 		dm_error("DC: update_bandwidth failed!\n");
606 		return false;
607 	}
608 
609 	/* enable writeback */
610 	if (dc->hwss.enable_writeback) {
611 		struct dwbc *dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst];
612 
613 		if (dwb->funcs->is_enabled(dwb)) {
614 			/* writeback pipe already enabled, only need to update */
615 			dc->hwss.update_writeback(dc, wb_info, dc->current_state);
616 		} else {
617 			/* Enable writeback pipe from scratch*/
618 			dc->hwss.enable_writeback(dc, wb_info, dc->current_state);
619 		}
620 	}
621 
622 	return true;
623 }
624 
dc_stream_fc_disable_writeback(struct dc * dc,struct dc_stream_state * stream,uint32_t dwb_pipe_inst)625 bool dc_stream_fc_disable_writeback(struct dc *dc,
626 		struct dc_stream_state *stream,
627 		uint32_t dwb_pipe_inst)
628 {
629 	struct dwbc *dwb = dc->res_pool->dwbc[dwb_pipe_inst];
630 
631 	if (stream == NULL) {
632 		dm_error("DC: dc_stream is NULL!\n");
633 		return false;
634 	}
635 
636 	if (dwb_pipe_inst >= MAX_DWB_PIPES) {
637 		dm_error("DC: writeback pipe is invalid!\n");
638 		return false;
639 	}
640 
641 	if (stream->num_wb_info > MAX_DWB_PIPES) {
642 		dm_error("DC: num_wb_info is invalid!\n");
643 		return false;
644 	}
645 
646 	dc_exit_ips_for_hw_access(dc);
647 
648 	if (dwb->funcs->set_fc_enable)
649 		dwb->funcs->set_fc_enable(dwb, DWB_FRAME_CAPTURE_DISABLE);
650 
651 	return true;
652 }
653 
654 /**
655  * dc_stream_remove_writeback() - Disables writeback and removes writeback info.
656  * @dc: Display core control structure.
657  * @stream: Display core stream state.
658  * @dwb_pipe_inst: Display writeback pipe.
659  *
660  * Return: returns true on success, false otherwise.
661  */
dc_stream_remove_writeback(struct dc * dc,struct dc_stream_state * stream,uint32_t dwb_pipe_inst)662 bool dc_stream_remove_writeback(struct dc *dc,
663 		struct dc_stream_state *stream,
664 		uint32_t dwb_pipe_inst)
665 {
666 	unsigned int i, j;
667 	if (stream == NULL) {
668 		dm_error("DC: dc_stream is NULL!\n");
669 		return false;
670 	}
671 
672 	if (dwb_pipe_inst >= MAX_DWB_PIPES) {
673 		dm_error("DC: writeback pipe is invalid!\n");
674 		return false;
675 	}
676 
677 	if (stream->num_wb_info > MAX_DWB_PIPES) {
678 		dm_error("DC: num_wb_info is invalid!\n");
679 		return false;
680 	}
681 
682 	/* remove writeback info for disabled writeback pipes from stream */
683 	for (i = 0, j = 0; i < stream->num_wb_info; i++) {
684 		if (stream->writeback_info[i].wb_enabled) {
685 
686 			if (stream->writeback_info[i].dwb_pipe_inst == dwb_pipe_inst)
687 				stream->writeback_info[i].wb_enabled = false;
688 
689 			/* trim the array */
690 			if (j < i) {
691 				memcpy(&stream->writeback_info[j], &stream->writeback_info[i],
692 						sizeof(struct dc_writeback_info));
693 				j++;
694 			}
695 		}
696 	}
697 	stream->num_wb_info = j;
698 
699 	/* recalculate and apply DML parameters */
700 	if (!dc->hwss.update_bandwidth(dc, dc->current_state)) {
701 		dm_error("DC: update_bandwidth failed!\n");
702 		return false;
703 	}
704 
705 	dc_exit_ips_for_hw_access(dc);
706 
707 	/* disable writeback */
708 	if (dc->hwss.disable_writeback) {
709 		struct dwbc *dwb = dc->res_pool->dwbc[dwb_pipe_inst];
710 
711 		if (dwb->funcs->is_enabled(dwb))
712 			dc->hwss.disable_writeback(dc, dwb_pipe_inst);
713 	}
714 
715 	return true;
716 }
717 
dc_stream_get_vblank_counter(const struct dc_stream_state * stream)718 uint32_t dc_stream_get_vblank_counter(const struct dc_stream_state *stream)
719 {
720 	uint8_t i;
721 	struct dc  *dc = stream->ctx->dc;
722 	struct resource_context *res_ctx =
723 		&dc->current_state->res_ctx;
724 
725 	dc_exit_ips_for_hw_access(dc);
726 
727 	for (i = 0; i < MAX_PIPES; i++) {
728 		struct timing_generator *tg = res_ctx->pipe_ctx[i].stream_res.tg;
729 
730 		if (res_ctx->pipe_ctx[i].stream != stream || !tg)
731 			continue;
732 
733 		return tg->funcs->get_frame_count(tg);
734 	}
735 
736 	return 0;
737 }
738 
dc_stream_send_dp_sdp(const struct dc_stream_state * stream,const uint8_t * custom_sdp_message,unsigned int sdp_message_size)739 bool dc_stream_send_dp_sdp(const struct dc_stream_state *stream,
740 		const uint8_t *custom_sdp_message,
741 		unsigned int sdp_message_size)
742 {
743 	int i;
744 	struct dc  *dc;
745 	struct resource_context *res_ctx;
746 
747 	if (stream == NULL) {
748 		dm_error("DC: dc_stream is NULL!\n");
749 		return false;
750 	}
751 
752 	dc = stream->ctx->dc;
753 	res_ctx = &dc->current_state->res_ctx;
754 
755 	dc_exit_ips_for_hw_access(dc);
756 
757 	for (i = 0; i < MAX_PIPES; i++) {
758 		struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i];
759 
760 		if (pipe_ctx->stream != stream)
761 			continue;
762 
763 		if (dc->hwss.send_immediate_sdp_message != NULL)
764 			dc->hwss.send_immediate_sdp_message(pipe_ctx,
765 								custom_sdp_message,
766 								sdp_message_size);
767 		else
768 			DC_LOG_WARNING("%s:send_immediate_sdp_message not implemented on this ASIC\n",
769 			__func__);
770 
771 	}
772 
773 	return true;
774 }
775 
dc_stream_get_scanoutpos(const struct dc_stream_state * stream,uint32_t * v_blank_start,uint32_t * v_blank_end,uint32_t * h_position,uint32_t * v_position)776 bool dc_stream_get_scanoutpos(const struct dc_stream_state *stream,
777 				  uint32_t *v_blank_start,
778 				  uint32_t *v_blank_end,
779 				  uint32_t *h_position,
780 				  uint32_t *v_position)
781 {
782 	uint8_t i;
783 	bool ret = false;
784 	struct dc  *dc;
785 	struct resource_context *res_ctx;
786 
787 	if (!stream->ctx)
788 		return false;
789 
790 	dc = stream->ctx->dc;
791 	res_ctx = &dc->current_state->res_ctx;
792 
793 	dc_exit_ips_for_hw_access(dc);
794 
795 	for (i = 0; i < MAX_PIPES; i++) {
796 		struct timing_generator *tg = res_ctx->pipe_ctx[i].stream_res.tg;
797 
798 		if (res_ctx->pipe_ctx[i].stream != stream || !tg)
799 			continue;
800 
801 		tg->funcs->get_scanoutpos(tg,
802 					  v_blank_start,
803 					  v_blank_end,
804 					  h_position,
805 					  v_position);
806 
807 		ret = true;
808 		break;
809 	}
810 
811 	return ret;
812 }
813 
dc_stream_dmdata_status_done(struct dc * dc,struct dc_stream_state * stream)814 bool dc_stream_dmdata_status_done(struct dc *dc, struct dc_stream_state *stream)
815 {
816 	struct pipe_ctx *pipe = NULL;
817 	int i;
818 
819 	if (!dc->hwss.dmdata_status_done)
820 		return false;
821 
822 	for (i = 0; i < MAX_PIPES; i++) {
823 		pipe = &dc->current_state->res_ctx.pipe_ctx[i];
824 		if (pipe->stream == stream)
825 			break;
826 	}
827 	/* Stream not found, by default we'll assume HUBP fetched dm data */
828 	if (i == MAX_PIPES)
829 		return true;
830 
831 	dc_exit_ips_for_hw_access(dc);
832 
833 	return dc->hwss.dmdata_status_done(pipe);
834 }
835 
dc_stream_set_dynamic_metadata(struct dc * dc,struct dc_stream_state * stream,struct dc_dmdata_attributes * attr)836 bool dc_stream_set_dynamic_metadata(struct dc *dc,
837 		struct dc_stream_state *stream,
838 		struct dc_dmdata_attributes *attr)
839 {
840 	struct pipe_ctx *pipe_ctx = NULL;
841 	struct hubp *hubp;
842 	int i;
843 
844 	/* Dynamic metadata is only supported on HDMI or DP */
845 	if (!dc_is_hdmi_signal(stream->signal) && !dc_is_dp_signal(stream->signal))
846 		return false;
847 
848 	/* Check hardware support */
849 	if (!dc->hwss.program_dmdata_engine)
850 		return false;
851 
852 	for (i = 0; i < MAX_PIPES; i++) {
853 		pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
854 		if (pipe_ctx->stream == stream)
855 			break;
856 	}
857 
858 	if (i == MAX_PIPES)
859 		return false;
860 
861 	hubp = pipe_ctx->plane_res.hubp;
862 	if (hubp == NULL)
863 		return false;
864 
865 	pipe_ctx->stream->dmdata_address = attr->address;
866 
867 	dc_exit_ips_for_hw_access(dc);
868 
869 	dc->hwss.program_dmdata_engine(pipe_ctx);
870 
871 	if (hubp->funcs->dmdata_set_attributes != NULL &&
872 			pipe_ctx->stream->dmdata_address.quad_part != 0) {
873 		hubp->funcs->dmdata_set_attributes(hubp, attr);
874 	}
875 
876 	return true;
877 }
878 
dc_stream_add_dsc_to_resource(struct dc * dc,struct dc_state * state,struct dc_stream_state * stream)879 enum dc_status dc_stream_add_dsc_to_resource(struct dc *dc,
880 		struct dc_state *state,
881 		struct dc_stream_state *stream)
882 {
883 	if (dc->res_pool->funcs->add_dsc_to_stream_resource) {
884 		return dc->res_pool->funcs->add_dsc_to_stream_resource(dc, state, stream);
885 	} else {
886 		return DC_NO_DSC_RESOURCE;
887 	}
888 }
889 
dc_stream_get_pipe_ctx(struct dc_stream_state * stream)890 struct pipe_ctx *dc_stream_get_pipe_ctx(struct dc_stream_state *stream)
891 {
892 	int i = 0;
893 
894 	for (i = 0; i < MAX_PIPES; i++) {
895 		struct pipe_ctx *pipe = &stream->ctx->dc->current_state->res_ctx.pipe_ctx[i];
896 
897 		if (pipe->stream == stream)
898 			return pipe;
899 	}
900 
901 	return NULL;
902 }
903 
dc_stream_log(const struct dc * dc,const struct dc_stream_state * stream)904 void dc_stream_log(const struct dc *dc, const struct dc_stream_state *stream)
905 {
906 	DC_LOG_DC(
907 			"core_stream 0x%p: src: %d, %d, %d, %d; dst: %d, %d, %d, %d, colorSpace:%d\n",
908 			stream,
909 			stream->src.x,
910 			stream->src.y,
911 			stream->src.width,
912 			stream->src.height,
913 			stream->dst.x,
914 			stream->dst.y,
915 			stream->dst.width,
916 			stream->dst.height,
917 			stream->output_color_space);
918 	DC_LOG_DC(
919 			"\tpix_clk_khz: %d, h_total: %d, v_total: %d, pixel_encoding:%s, color_depth:%s\n",
920 			stream->timing.pix_clk_100hz / 10,
921 			stream->timing.h_total,
922 			stream->timing.v_total,
923 			dc_pixel_encoding_to_str(stream->timing.pixel_encoding),
924 			dc_color_depth_to_str(stream->timing.display_color_depth));
925 	DC_LOG_DC(
926 			"\tlink: %d\n",
927 			stream->link->link_index);
928 
929 	DC_LOG_DC(
930 			"\tdsc: %d, mst_pbn: %d\n",
931 			stream->timing.flags.DSC,
932 			stream->timing.dsc_cfg.mst_pbn);
933 
934 	if (stream->sink) {
935 		if (stream->sink->sink_signal != SIGNAL_TYPE_VIRTUAL &&
936 			stream->sink->sink_signal != SIGNAL_TYPE_NONE) {
937 
938 			DC_LOG_DC(
939 					"\tsignal: %x dispname: %s manufacturer_id: 0x%x product_id: 0x%x\n",
940 					stream->signal,
941 					stream->sink->edid_caps.display_name,
942 					stream->sink->edid_caps.manufacturer_id,
943 					stream->sink->edid_caps.product_id);
944 		}
945 	}
946 }
947 
948 /*
949 *	dc_stream_get_3dlut()
950 *	Requirements:
951 *	1. Is stream already owns an RMCM instance, return it.
952 *	2. If it doesn't and we don't need to allocate, return NULL.
953 *	3. If there's a free RMCM instance, assign to stream and return it.
954 *	4. If no free RMCM instances, return NULL.
955 */
956 
dc_stream_get_3dlut_for_stream(const struct dc * dc,const struct dc_stream_state * stream,bool allocate_one)957 struct dc_rmcm_3dlut *dc_stream_get_3dlut_for_stream(
958 	const struct dc *dc,
959 	const struct dc_stream_state *stream,
960 	bool allocate_one)
961 {
962 	unsigned int num_rmcm = dc->caps.color.mpc.num_rmcm_3dluts;
963 
964 	// see if one is allocated for this stream
965 	for (int i = 0; i < num_rmcm; i++) {
966 		if (dc->res_pool->rmcm_3dlut[i].isInUse &&
967 			dc->res_pool->rmcm_3dlut[i].stream == stream)
968 			return &dc->res_pool->rmcm_3dlut[i];
969 	}
970 
971 	//case: not found one, and dont need to allocate
972 	if (!allocate_one)
973 		return NULL;
974 
975 	//see if there is an unused 3dlut, allocate
976 	for (int i = 0; i < num_rmcm; i++) {
977 		if (!dc->res_pool->rmcm_3dlut[i].isInUse) {
978 			dc->res_pool->rmcm_3dlut[i].isInUse = true;
979 			dc->res_pool->rmcm_3dlut[i].stream = stream;
980 			return &dc->res_pool->rmcm_3dlut[i];
981 		}
982 	}
983 
984 	//dont have a 3dlut
985 	return NULL;
986 }
987 
988 
dc_stream_release_3dlut_for_stream(const struct dc * dc,const struct dc_stream_state * stream)989 void dc_stream_release_3dlut_for_stream(
990 	const struct dc *dc,
991 	const struct dc_stream_state *stream)
992 {
993 	struct dc_rmcm_3dlut *rmcm_3dlut =
994 		dc_stream_get_3dlut_for_stream(dc, stream, false);
995 
996 	if (rmcm_3dlut) {
997 		rmcm_3dlut->isInUse = false;
998 		rmcm_3dlut->stream  = NULL;
999 	}
1000 }
1001 
1002 
dc_stream_init_rmcm_3dlut(struct dc * dc)1003 void dc_stream_init_rmcm_3dlut(struct dc *dc)
1004 {
1005 	unsigned int num_rmcm = dc->caps.color.mpc.num_rmcm_3dluts;
1006 
1007 	for (int i = 0; i < num_rmcm; i++) {
1008 		dc->res_pool->rmcm_3dlut[i].isInUse = false;
1009 		dc->res_pool->rmcm_3dlut[i].stream = NULL;
1010 	}
1011 }
1012 
1013 /*
1014  * Finds the greatest index in refresh_rate_hz that contains a value <= refresh
1015  */
dc_stream_get_nearest_smallest_index(struct dc_stream_state * stream,int refresh)1016 static int dc_stream_get_nearest_smallest_index(struct dc_stream_state *stream, int refresh)
1017 {
1018 	for (int i = 0; i < (LUMINANCE_DATA_TABLE_SIZE - 1); ++i) {
1019 		if ((stream->lumin_data.refresh_rate_hz[i] <= refresh) && (refresh < stream->lumin_data.refresh_rate_hz[i + 1])) {
1020 			return i;
1021 		}
1022 	}
1023 	return 9;
1024 }
1025 
1026 /*
1027  * Finds a corresponding brightness for a given refresh rate between 2 given indices, where index1 < index2
1028  */
dc_stream_get_brightness_millinits_linear_interpolation(struct dc_stream_state * stream,int index1,int index2,int refresh_hz)1029 static int dc_stream_get_brightness_millinits_linear_interpolation (struct dc_stream_state *stream,
1030 								     int index1,
1031 								     int index2,
1032 								     int refresh_hz)
1033 {
1034 	long long slope = 0;
1035 	if (stream->lumin_data.refresh_rate_hz[index2] != stream->lumin_data.refresh_rate_hz[index1]) {
1036 		slope = (stream->lumin_data.luminance_millinits[index2] - stream->lumin_data.luminance_millinits[index1]) /
1037 			    (stream->lumin_data.refresh_rate_hz[index2] - stream->lumin_data.refresh_rate_hz[index1]);
1038 	}
1039 
1040 	int y_intercept = stream->lumin_data.luminance_millinits[index2] - slope * stream->lumin_data.refresh_rate_hz[index2];
1041 
1042 	return (y_intercept + refresh_hz * slope);
1043 }
1044 
1045 /*
1046  * Finds a corresponding refresh rate for a given brightness between 2 given indices, where index1 < index2
1047  */
dc_stream_get_refresh_hz_linear_interpolation(struct dc_stream_state * stream,int index1,int index2,int brightness_millinits)1048 static int dc_stream_get_refresh_hz_linear_interpolation (struct dc_stream_state *stream,
1049 							   int index1,
1050 							   int index2,
1051 							   int brightness_millinits)
1052 {
1053 	long long slope = 1;
1054 	if (stream->lumin_data.refresh_rate_hz[index2] != stream->lumin_data.refresh_rate_hz[index1]) {
1055 		slope = (stream->lumin_data.luminance_millinits[index2] - stream->lumin_data.luminance_millinits[index1]) /
1056 				(stream->lumin_data.refresh_rate_hz[index2] - stream->lumin_data.refresh_rate_hz[index1]);
1057 	}
1058 
1059 	int y_intercept = stream->lumin_data.luminance_millinits[index2] - slope * stream->lumin_data.refresh_rate_hz[index2];
1060 
1061 	return ((int)div64_s64((brightness_millinits - y_intercept), slope));
1062 }
1063 
1064 /*
1065  * Finds the current brightness in millinits given a refresh rate
1066  */
dc_stream_get_brightness_millinits_from_refresh(struct dc_stream_state * stream,int refresh_hz)1067 static int dc_stream_get_brightness_millinits_from_refresh (struct dc_stream_state *stream, int refresh_hz)
1068 {
1069 	int nearest_smallest_index = dc_stream_get_nearest_smallest_index(stream, refresh_hz);
1070 	int nearest_smallest_value = stream->lumin_data.refresh_rate_hz[nearest_smallest_index];
1071 
1072 	if (nearest_smallest_value == refresh_hz)
1073 		return stream->lumin_data.luminance_millinits[nearest_smallest_index];
1074 
1075 	if (nearest_smallest_index >= 9)
1076 		return dc_stream_get_brightness_millinits_linear_interpolation(stream, nearest_smallest_index - 1, nearest_smallest_index, refresh_hz);
1077 
1078 	if (nearest_smallest_value == stream->lumin_data.refresh_rate_hz[nearest_smallest_index + 1])
1079 		return stream->lumin_data.luminance_millinits[nearest_smallest_index];
1080 
1081 	return dc_stream_get_brightness_millinits_linear_interpolation(stream, nearest_smallest_index, nearest_smallest_index + 1, refresh_hz);
1082 }
1083 
1084 /*
1085  * Finds the lowest/highest refresh rate (depending on search_for_max_increase)
1086  * that can be achieved from starting_refresh_hz while staying
1087  * within flicker criteria
1088  */
dc_stream_calculate_flickerless_refresh_rate(struct dc_stream_state * stream,int current_brightness,int starting_refresh_hz,bool is_gaming,bool search_for_max_increase)1089 static int dc_stream_calculate_flickerless_refresh_rate(struct dc_stream_state *stream,
1090 							 int current_brightness,
1091 							 int starting_refresh_hz,
1092 							 bool is_gaming,
1093 							 bool search_for_max_increase)
1094 {
1095 	int nearest_smallest_index = dc_stream_get_nearest_smallest_index(stream, starting_refresh_hz);
1096 
1097 	int flicker_criteria_millinits = is_gaming ?
1098 					 stream->lumin_data.flicker_criteria_milli_nits_GAMING :
1099 					 stream->lumin_data.flicker_criteria_milli_nits_STATIC;
1100 
1101 	int safe_upper_bound = current_brightness + flicker_criteria_millinits;
1102 	int safe_lower_bound = current_brightness - flicker_criteria_millinits;
1103 	int lumin_millinits_temp = 0;
1104 
1105 	int offset = -1;
1106 	if (search_for_max_increase) {
1107 		offset = 1;
1108 	}
1109 
1110 	/*
1111 	 * Increments up or down by 1 depending on search_for_max_increase
1112 	 */
1113 	for (int i = nearest_smallest_index; (i > 0 && !search_for_max_increase) || (i < (LUMINANCE_DATA_TABLE_SIZE - 1) && search_for_max_increase); i += offset) {
1114 
1115 		lumin_millinits_temp = stream->lumin_data.luminance_millinits[i + offset];
1116 
1117 		if ((lumin_millinits_temp >= safe_upper_bound) || (lumin_millinits_temp <= safe_lower_bound)) {
1118 
1119 			if (stream->lumin_data.refresh_rate_hz[i + offset] == stream->lumin_data.refresh_rate_hz[i])
1120 				return stream->lumin_data.refresh_rate_hz[i];
1121 
1122 			int target_brightness = (stream->lumin_data.luminance_millinits[i + offset] >= (current_brightness + flicker_criteria_millinits)) ?
1123 											current_brightness + flicker_criteria_millinits :
1124 											current_brightness - flicker_criteria_millinits;
1125 
1126 			int refresh = 0;
1127 
1128 			/*
1129 			 * Need the second input to be < third input for dc_stream_get_refresh_hz_linear_interpolation
1130 			 */
1131 			if (search_for_max_increase)
1132 				refresh = dc_stream_get_refresh_hz_linear_interpolation(stream, i, i + offset, target_brightness);
1133 			else
1134 				refresh = dc_stream_get_refresh_hz_linear_interpolation(stream, i + offset, i, target_brightness);
1135 
1136 			if (refresh == stream->lumin_data.refresh_rate_hz[i + offset])
1137 				return stream->lumin_data.refresh_rate_hz[i + offset];
1138 
1139 			return refresh;
1140 		}
1141 	}
1142 
1143 	if (search_for_max_increase)
1144 		return (int)div64_s64((long long)stream->timing.pix_clk_100hz*100, stream->timing.v_total*(long long)stream->timing.h_total);
1145 	else
1146 		return stream->lumin_data.refresh_rate_hz[0];
1147 }
1148 
1149 /*
1150  * Gets the max delta luminance within a specified refresh range
1151  */
dc_stream_get_max_delta_lumin_millinits(struct dc_stream_state * stream,int hz1,int hz2,bool isGaming)1152 static int dc_stream_get_max_delta_lumin_millinits(struct dc_stream_state *stream, int hz1, int hz2, bool isGaming)
1153 {
1154 	int lower_refresh_brightness = dc_stream_get_brightness_millinits_from_refresh (stream, hz1);
1155 	int higher_refresh_brightness = dc_stream_get_brightness_millinits_from_refresh (stream, hz2);
1156 
1157 	int min = lower_refresh_brightness;
1158 	int max = higher_refresh_brightness;
1159 
1160 	/*
1161 	 * Static screen, therefore no need to scan through array
1162 	 */
1163 	if (!isGaming) {
1164 		if (lower_refresh_brightness >= higher_refresh_brightness) {
1165 			return lower_refresh_brightness - higher_refresh_brightness;
1166 		}
1167 		return higher_refresh_brightness - lower_refresh_brightness;
1168 	}
1169 
1170 	min = MIN(lower_refresh_brightness, higher_refresh_brightness);
1171 	max = MAX(lower_refresh_brightness, higher_refresh_brightness);
1172 
1173 	int nearest_smallest_index = dc_stream_get_nearest_smallest_index(stream, hz1);
1174 
1175 	for (; nearest_smallest_index < (LUMINANCE_DATA_TABLE_SIZE - 1) &&
1176 			stream->lumin_data.refresh_rate_hz[nearest_smallest_index + 1] <= hz2 ; nearest_smallest_index++) {
1177 		min = MIN(min, stream->lumin_data.luminance_millinits[nearest_smallest_index + 1]);
1178 		max = MAX(max, stream->lumin_data.luminance_millinits[nearest_smallest_index + 1]);
1179 	}
1180 
1181 	return (max - min);
1182 }
1183 
1184 /*
1185  * Determines the max flickerless instant vtotal delta for a stream.
1186  * Determines vtotal increase/decrease based on the bool "increase"
1187  */
dc_stream_get_max_flickerless_instant_vtotal_delta(struct dc_stream_state * stream,bool is_gaming,bool increase)1188 static unsigned int dc_stream_get_max_flickerless_instant_vtotal_delta(struct dc_stream_state *stream, bool is_gaming, bool increase)
1189 {
1190 	if (stream->timing.v_total * stream->timing.h_total == 0)
1191 		return 0;
1192 
1193 	int current_refresh_hz = (int)div64_s64((long long)stream->timing.pix_clk_100hz*100, stream->timing.v_total*(long long)stream->timing.h_total);
1194 
1195 	int safe_refresh_hz = dc_stream_calculate_flickerless_refresh_rate(stream,
1196 							 dc_stream_get_brightness_millinits_from_refresh(stream, current_refresh_hz),
1197 							 current_refresh_hz,
1198 							 is_gaming,
1199 							 increase);
1200 
1201 	int safe_refresh_v_total = (int)div64_s64((long long)stream->timing.pix_clk_100hz*100, safe_refresh_hz*(long long)stream->timing.h_total);
1202 
1203 	if (increase)
1204 		return (((int) stream->timing.v_total - safe_refresh_v_total) >= 0) ? (stream->timing.v_total - safe_refresh_v_total) : 0;
1205 
1206 	return ((safe_refresh_v_total - (int) stream->timing.v_total) >= 0) ? (safe_refresh_v_total - stream->timing.v_total) : 0;
1207 }
1208 
1209 /*
1210  * Finds the highest refresh rate that can be achieved
1211  * from starting_refresh_hz while staying within flicker criteria
1212  */
dc_stream_calculate_max_flickerless_refresh_rate(struct dc_stream_state * stream,int starting_refresh_hz,bool is_gaming)1213 int dc_stream_calculate_max_flickerless_refresh_rate(struct dc_stream_state *stream, int starting_refresh_hz, bool is_gaming)
1214 {
1215 	if (!stream->lumin_data.is_valid)
1216 		return 0;
1217 
1218 	int current_brightness = dc_stream_get_brightness_millinits_from_refresh(stream, starting_refresh_hz);
1219 
1220 	return dc_stream_calculate_flickerless_refresh_rate(stream,
1221 							    current_brightness,
1222 							    starting_refresh_hz,
1223 							    is_gaming,
1224 							    true);
1225 }
1226 
1227 /*
1228  * Finds the lowest refresh rate that can be achieved
1229  * from starting_refresh_hz while staying within flicker criteria
1230  */
dc_stream_calculate_min_flickerless_refresh_rate(struct dc_stream_state * stream,int starting_refresh_hz,bool is_gaming)1231 int dc_stream_calculate_min_flickerless_refresh_rate(struct dc_stream_state *stream, int starting_refresh_hz, bool is_gaming)
1232 {
1233 	if (!stream->lumin_data.is_valid)
1234 			return 0;
1235 
1236 	int current_brightness = dc_stream_get_brightness_millinits_from_refresh(stream, starting_refresh_hz);
1237 
1238 	return dc_stream_calculate_flickerless_refresh_rate(stream,
1239 							    current_brightness,
1240 							    starting_refresh_hz,
1241 							    is_gaming,
1242 							    false);
1243 }
1244 
1245 /*
1246  * Determines if there will be a flicker when moving between 2 refresh rates
1247  */
dc_stream_is_refresh_rate_range_flickerless(struct dc_stream_state * stream,int hz1,int hz2,bool is_gaming)1248 bool dc_stream_is_refresh_rate_range_flickerless(struct dc_stream_state *stream, int hz1, int hz2, bool is_gaming)
1249 {
1250 
1251 	/*
1252 	 * Assume that we wont flicker if there is invalid data
1253 	 */
1254 	if (!stream->lumin_data.is_valid)
1255 		return false;
1256 
1257 	int dl = dc_stream_get_max_delta_lumin_millinits(stream, hz1, hz2, is_gaming);
1258 
1259 	int flicker_criteria_millinits = (is_gaming) ?
1260 					  stream->lumin_data.flicker_criteria_milli_nits_GAMING :
1261 					  stream->lumin_data.flicker_criteria_milli_nits_STATIC;
1262 
1263 	return (dl <= flicker_criteria_millinits);
1264 }
1265 
1266 /*
1267  * Determines the max instant vtotal delta increase that can be applied without
1268  * flickering for a given stream
1269  */
dc_stream_get_max_flickerless_instant_vtotal_decrease(struct dc_stream_state * stream,bool is_gaming)1270 unsigned int dc_stream_get_max_flickerless_instant_vtotal_decrease(struct dc_stream_state *stream,
1271 									  bool is_gaming)
1272 {
1273 	if (!stream->lumin_data.is_valid)
1274 		return 0;
1275 
1276 	return dc_stream_get_max_flickerless_instant_vtotal_delta(stream, is_gaming, true);
1277 }
1278 
1279 /*
1280  * Determines the max instant vtotal delta decrease that can be applied without
1281  * flickering for a given stream
1282  */
dc_stream_get_max_flickerless_instant_vtotal_increase(struct dc_stream_state * stream,bool is_gaming)1283 unsigned int dc_stream_get_max_flickerless_instant_vtotal_increase(struct dc_stream_state *stream,
1284 									  bool is_gaming)
1285 {
1286 	if (!stream->lumin_data.is_valid)
1287 		return 0;
1288 
1289 	return dc_stream_get_max_flickerless_instant_vtotal_delta(stream, is_gaming, false);
1290 }
1291 
dc_stream_is_cursor_limit_pending(struct dc * dc,struct dc_stream_state * stream)1292 bool dc_stream_is_cursor_limit_pending(struct dc *dc, struct dc_stream_state *stream)
1293 {
1294 	bool is_limit_pending = false;
1295 
1296 	if (dc->current_state)
1297 		is_limit_pending = dc_state_get_stream_cursor_subvp_limit(stream, dc->current_state);
1298 
1299 	return is_limit_pending;
1300 }
1301 
dc_stream_can_clear_cursor_limit(struct dc * dc,struct dc_stream_state * stream)1302 bool dc_stream_can_clear_cursor_limit(struct dc *dc, struct dc_stream_state *stream)
1303 {
1304 	bool can_clear_limit = false;
1305 
1306 	if (dc->current_state)
1307 		can_clear_limit = dc_state_get_stream_cursor_subvp_limit(stream, dc->current_state) &&
1308 				(stream->hw_cursor_req ||
1309 				!stream->cursor_position.enable ||
1310 				dc_stream_check_cursor_attributes(stream, dc->current_state, &stream->cursor_attributes));
1311 
1312 	return can_clear_limit;
1313 }
1314