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