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
28#include "resource.h"
29#include "include/irq_service_interface.h"
30#include "link_encoder.h"
31#include "stream_encoder.h"
32#include "opp.h"
33#include "timing_generator.h"
34#include "transform.h"
35#include "dccg.h"
36#include "dchubbub.h"
37#include "dpp.h"
38#include "core_types.h"
39#include "set_mode_types.h"
40#include "virtual/virtual_stream_encoder.h"
41#include "dpcd_defs.h"
42#include "link_enc_cfg.h"
43#include "link.h"
44#include "clk_mgr.h"
45#include "dc_state_priv.h"
46#include "virtual/virtual_link_hwss.h"
47#include "link/hwss/link_hwss_dio.h"
48#include "link/hwss/link_hwss_dpia.h"
49#include "link/hwss/link_hwss_hpo_dp.h"
50#include "link/hwss/link_hwss_dio_fixed_vs_pe_retimer.h"
51#include "link/hwss/link_hwss_hpo_fixed_vs_pe_retimer_dp.h"
52
53#if defined(CONFIG_DRM_AMD_DC_SI)
54#include "dce60/dce60_resource.h"
55#endif
56#include "dce80/dce80_resource.h"
57#include "dce100/dce100_resource.h"
58#include "dce110/dce110_resource.h"
59#include "dce112/dce112_resource.h"
60#include "dce120/dce120_resource.h"
61#include "dcn10/dcn10_resource.h"
62#include "dcn20/dcn20_resource.h"
63#include "dcn21/dcn21_resource.h"
64#include "dcn201/dcn201_resource.h"
65#include "dcn30/dcn30_resource.h"
66#include "dcn301/dcn301_resource.h"
67#include "dcn302/dcn302_resource.h"
68#include "dcn303/dcn303_resource.h"
69#include "dcn31/dcn31_resource.h"
70#include "dcn314/dcn314_resource.h"
71#include "dcn315/dcn315_resource.h"
72#include "dcn316/dcn316_resource.h"
73#include "dcn32/dcn32_resource.h"
74#include "dcn321/dcn321_resource.h"
75#include "dcn35/dcn35_resource.h"
76#include "dcn351/dcn351_resource.h"
77
78#define VISUAL_CONFIRM_BASE_DEFAULT 3
79#define VISUAL_CONFIRM_BASE_MIN 1
80#define VISUAL_CONFIRM_BASE_MAX 10
81/* we choose 240 because it is a common denominator of common v addressable
82 * such as 2160, 1440, 1200, 960. So we take 1/240 portion of v addressable as
83 * the visual confirm dpp offset height. So visual confirm height can stay
84 * relatively the same independent from timing used.
85 */
86#define VISUAL_CONFIRM_DPP_OFFSET_DENO 240
87
88#define DC_LOGGER \
89 dc->ctx->logger
90#define DC_LOGGER_INIT(logger)
91
92#include "dml2/dml2_wrapper.h"
93
94#define UNABLE_TO_SPLIT -1
95
96enum dce_version resource_parse_asic_id(struct hw_asic_id asic_id)
97{
98 enum dce_version dc_version = DCE_VERSION_UNKNOWN;
99
100 switch (asic_id.chip_family) {
101
102#if defined(CONFIG_DRM_AMD_DC_SI)
103 case FAMILY_SI:
104 if (ASIC_REV_IS_TAHITI_P(asic_id.hw_internal_rev) ||
105 ASIC_REV_IS_PITCAIRN_PM(asic_id.hw_internal_rev) ||
106 ASIC_REV_IS_CAPEVERDE_M(asic_id.hw_internal_rev))
107 dc_version = DCE_VERSION_6_0;
108 else if (ASIC_REV_IS_OLAND_M(asic_id.hw_internal_rev))
109 dc_version = DCE_VERSION_6_4;
110 else
111 dc_version = DCE_VERSION_6_1;
112 break;
113#endif
114 case FAMILY_CI:
115 dc_version = DCE_VERSION_8_0;
116 break;
117 case FAMILY_KV:
118 if (ASIC_REV_IS_KALINDI(asic_id.hw_internal_rev) ||
119 ASIC_REV_IS_BHAVANI(asic_id.hw_internal_rev) ||
120 ASIC_REV_IS_GODAVARI(asic_id.hw_internal_rev))
121 dc_version = DCE_VERSION_8_3;
122 else
123 dc_version = DCE_VERSION_8_1;
124 break;
125 case FAMILY_CZ:
126 dc_version = DCE_VERSION_11_0;
127 break;
128
129 case FAMILY_VI:
130 if (ASIC_REV_IS_TONGA_P(asic_id.hw_internal_rev) ||
131 ASIC_REV_IS_FIJI_P(asic_id.hw_internal_rev)) {
132 dc_version = DCE_VERSION_10_0;
133 break;
134 }
135 if (ASIC_REV_IS_POLARIS10_P(asic_id.hw_internal_rev) ||
136 ASIC_REV_IS_POLARIS11_M(asic_id.hw_internal_rev) ||
137 ASIC_REV_IS_POLARIS12_V(asic_id.hw_internal_rev)) {
138 dc_version = DCE_VERSION_11_2;
139 }
140 if (ASIC_REV_IS_VEGAM(asic_id.hw_internal_rev))
141 dc_version = DCE_VERSION_11_22;
142 break;
143 case FAMILY_AI:
144 if (ASICREV_IS_VEGA20_P(asic_id.hw_internal_rev))
145 dc_version = DCE_VERSION_12_1;
146 else
147 dc_version = DCE_VERSION_12_0;
148 break;
149 case FAMILY_RV:
150 dc_version = DCN_VERSION_1_0;
151 if (ASICREV_IS_RAVEN2(asic_id.hw_internal_rev))
152 dc_version = DCN_VERSION_1_01;
153 if (ASICREV_IS_RENOIR(asic_id.hw_internal_rev))
154 dc_version = DCN_VERSION_2_1;
155 if (ASICREV_IS_GREEN_SARDINE(asic_id.hw_internal_rev))
156 dc_version = DCN_VERSION_2_1;
157 break;
158
159 case FAMILY_NV:
160 dc_version = DCN_VERSION_2_0;
161 if (asic_id.chip_id == DEVICE_ID_NV_13FE || asic_id.chip_id == DEVICE_ID_NV_143F) {
162 dc_version = DCN_VERSION_2_01;
163 break;
164 }
165 if (ASICREV_IS_SIENNA_CICHLID_P(asic_id.hw_internal_rev))
166 dc_version = DCN_VERSION_3_0;
167 if (ASICREV_IS_DIMGREY_CAVEFISH_P(asic_id.hw_internal_rev))
168 dc_version = DCN_VERSION_3_02;
169 if (ASICREV_IS_BEIGE_GOBY_P(asic_id.hw_internal_rev))
170 dc_version = DCN_VERSION_3_03;
171 break;
172
173 case FAMILY_VGH:
174 dc_version = DCN_VERSION_3_01;
175 break;
176
177 case FAMILY_YELLOW_CARP:
178 if (ASICREV_IS_YELLOW_CARP(asic_id.hw_internal_rev))
179 dc_version = DCN_VERSION_3_1;
180 break;
181 case AMDGPU_FAMILY_GC_10_3_6:
182 if (ASICREV_IS_GC_10_3_6(asic_id.hw_internal_rev))
183 dc_version = DCN_VERSION_3_15;
184 break;
185 case AMDGPU_FAMILY_GC_10_3_7:
186 if (ASICREV_IS_GC_10_3_7(asic_id.hw_internal_rev))
187 dc_version = DCN_VERSION_3_16;
188 break;
189 case AMDGPU_FAMILY_GC_11_0_0:
190 dc_version = DCN_VERSION_3_2;
191 if (ASICREV_IS_GC_11_0_2(asic_id.hw_internal_rev))
192 dc_version = DCN_VERSION_3_21;
193 break;
194 case AMDGPU_FAMILY_GC_11_0_1:
195 dc_version = DCN_VERSION_3_14;
196 break;
197 case AMDGPU_FAMILY_GC_11_5_0:
198 dc_version = DCN_VERSION_3_5;
199 if (ASICREV_IS_GC_11_0_4(asic_id.hw_internal_rev))
200 dc_version = DCN_VERSION_3_51;
201 break;
202 default:
203 dc_version = DCE_VERSION_UNKNOWN;
204 break;
205 }
206 return dc_version;
207}
208
209struct resource_pool *dc_create_resource_pool(struct dc *dc,
210 const struct dc_init_data *init_data,
211 enum dce_version dc_version)
212{
213 struct resource_pool *res_pool = NULL;
214
215 switch (dc_version) {
216#if defined(CONFIG_DRM_AMD_DC_SI)
217 case DCE_VERSION_6_0:
218 res_pool = dce60_create_resource_pool(
219 num_virtual_links: init_data->num_virtual_links, dc);
220 break;
221 case DCE_VERSION_6_1:
222 res_pool = dce61_create_resource_pool(
223 num_virtual_links: init_data->num_virtual_links, dc);
224 break;
225 case DCE_VERSION_6_4:
226 res_pool = dce64_create_resource_pool(
227 num_virtual_links: init_data->num_virtual_links, dc);
228 break;
229#endif
230 case DCE_VERSION_8_0:
231 res_pool = dce80_create_resource_pool(
232 num_virtual_links: init_data->num_virtual_links, dc);
233 break;
234 case DCE_VERSION_8_1:
235 res_pool = dce81_create_resource_pool(
236 num_virtual_links: init_data->num_virtual_links, dc);
237 break;
238 case DCE_VERSION_8_3:
239 res_pool = dce83_create_resource_pool(
240 num_virtual_links: init_data->num_virtual_links, dc);
241 break;
242 case DCE_VERSION_10_0:
243 res_pool = dce100_create_resource_pool(
244 num_virtual_links: init_data->num_virtual_links, dc);
245 break;
246 case DCE_VERSION_11_0:
247 res_pool = dce110_create_resource_pool(
248 num_virtual_links: init_data->num_virtual_links, dc,
249 asic_id: init_data->asic_id);
250 break;
251 case DCE_VERSION_11_2:
252 case DCE_VERSION_11_22:
253 res_pool = dce112_create_resource_pool(
254 num_virtual_links: init_data->num_virtual_links, dc);
255 break;
256 case DCE_VERSION_12_0:
257 case DCE_VERSION_12_1:
258 res_pool = dce120_create_resource_pool(
259 num_virtual_links: init_data->num_virtual_links, dc);
260 break;
261
262#if defined(CONFIG_DRM_AMD_DC_FP)
263 case DCN_VERSION_1_0:
264 case DCN_VERSION_1_01:
265 res_pool = dcn10_create_resource_pool(init_data, dc);
266 break;
267 case DCN_VERSION_2_0:
268 res_pool = dcn20_create_resource_pool(init_data, dc);
269 break;
270 case DCN_VERSION_2_1:
271 res_pool = dcn21_create_resource_pool(init_data, dc);
272 break;
273 case DCN_VERSION_2_01:
274 res_pool = dcn201_create_resource_pool(init_data, dc);
275 break;
276 case DCN_VERSION_3_0:
277 res_pool = dcn30_create_resource_pool(init_data, dc);
278 break;
279 case DCN_VERSION_3_01:
280 res_pool = dcn301_create_resource_pool(init_data, dc);
281 break;
282 case DCN_VERSION_3_02:
283 res_pool = dcn302_create_resource_pool(init_data, dc);
284 break;
285 case DCN_VERSION_3_03:
286 res_pool = dcn303_create_resource_pool(init_data, dc);
287 break;
288 case DCN_VERSION_3_1:
289 res_pool = dcn31_create_resource_pool(init_data, dc);
290 break;
291 case DCN_VERSION_3_14:
292 res_pool = dcn314_create_resource_pool(init_data, dc);
293 break;
294 case DCN_VERSION_3_15:
295 res_pool = dcn315_create_resource_pool(init_data, dc);
296 break;
297 case DCN_VERSION_3_16:
298 res_pool = dcn316_create_resource_pool(init_data, dc);
299 break;
300 case DCN_VERSION_3_2:
301 res_pool = dcn32_create_resource_pool(init_data, dc);
302 break;
303 case DCN_VERSION_3_21:
304 res_pool = dcn321_create_resource_pool(init_data, dc);
305 break;
306 case DCN_VERSION_3_5:
307 res_pool = dcn35_create_resource_pool(init_data, dc);
308 break;
309 case DCN_VERSION_3_51:
310 res_pool = dcn351_create_resource_pool(init_data, dc);
311 break;
312#endif /* CONFIG_DRM_AMD_DC_FP */
313 default:
314 break;
315 }
316
317 if (res_pool != NULL) {
318 if (dc->ctx->dc_bios->fw_info_valid) {
319 res_pool->ref_clocks.xtalin_clock_inKhz =
320 dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency;
321 /* initialize with firmware data first, no all
322 * ASIC have DCCG SW component. FPGA or
323 * simulation need initialization of
324 * dccg_ref_clock_inKhz, dchub_ref_clock_inKhz
325 * with xtalin_clock_inKhz
326 */
327 res_pool->ref_clocks.dccg_ref_clock_inKhz =
328 res_pool->ref_clocks.xtalin_clock_inKhz;
329 res_pool->ref_clocks.dchub_ref_clock_inKhz =
330 res_pool->ref_clocks.xtalin_clock_inKhz;
331 if (dc->debug.using_dml2)
332 if (res_pool->hubbub && res_pool->hubbub->funcs->get_dchub_ref_freq)
333 res_pool->hubbub->funcs->get_dchub_ref_freq(res_pool->hubbub,
334 res_pool->ref_clocks.dccg_ref_clock_inKhz,
335 &res_pool->ref_clocks.dchub_ref_clock_inKhz);
336 } else
337 ASSERT_CRITICAL(false);
338 }
339
340 return res_pool;
341}
342
343void dc_destroy_resource_pool(struct dc *dc)
344{
345 if (dc) {
346 if (dc->res_pool)
347 dc->res_pool->funcs->destroy(&dc->res_pool);
348
349 kfree(objp: dc->hwseq);
350 }
351}
352
353static void update_num_audio(
354 const struct resource_straps *straps,
355 unsigned int *num_audio,
356 struct audio_support *aud_support)
357{
358 aud_support->dp_audio = true;
359 aud_support->hdmi_audio_native = false;
360 aud_support->hdmi_audio_on_dongle = false;
361
362 if (straps->hdmi_disable == 0) {
363 if (straps->dc_pinstraps_audio & 0x2) {
364 aud_support->hdmi_audio_on_dongle = true;
365 aud_support->hdmi_audio_native = true;
366 }
367 }
368
369 switch (straps->audio_stream_number) {
370 case 0: /* multi streams supported */
371 break;
372 case 1: /* multi streams not supported */
373 *num_audio = 1;
374 break;
375 default:
376 DC_ERR("DC: unexpected audio fuse!\n");
377 }
378}
379
380bool resource_construct(
381 unsigned int num_virtual_links,
382 struct dc *dc,
383 struct resource_pool *pool,
384 const struct resource_create_funcs *create_funcs)
385{
386 struct dc_context *ctx = dc->ctx;
387 const struct resource_caps *caps = pool->res_cap;
388 int i;
389 unsigned int num_audio = caps->num_audio;
390 struct resource_straps straps = {0};
391
392 if (create_funcs->read_dce_straps)
393 create_funcs->read_dce_straps(dc->ctx, &straps);
394
395 pool->audio_count = 0;
396 if (create_funcs->create_audio) {
397 /* find the total number of streams available via the
398 * AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT
399 * registers (one for each pin) starting from pin 1
400 * up to the max number of audio pins.
401 * We stop on the first pin where
402 * PORT_CONNECTIVITY == 1 (as instructed by HW team).
403 */
404 update_num_audio(straps: &straps, num_audio: &num_audio, aud_support: &pool->audio_support);
405 for (i = 0; i < caps->num_audio; i++) {
406 struct audio *aud = create_funcs->create_audio(ctx, i);
407
408 if (aud == NULL) {
409 DC_ERR("DC: failed to create audio!\n");
410 return false;
411 }
412 if (!aud->funcs->endpoint_valid(aud)) {
413 aud->funcs->destroy(&aud);
414 break;
415 }
416 pool->audios[i] = aud;
417 pool->audio_count++;
418 }
419 }
420
421 pool->stream_enc_count = 0;
422 if (create_funcs->create_stream_encoder) {
423 for (i = 0; i < caps->num_stream_encoder; i++) {
424 pool->stream_enc[i] = create_funcs->create_stream_encoder(i, ctx);
425 if (pool->stream_enc[i] == NULL)
426 DC_ERR("DC: failed to create stream_encoder!\n");
427 pool->stream_enc_count++;
428 }
429 }
430
431 pool->hpo_dp_stream_enc_count = 0;
432 if (create_funcs->create_hpo_dp_stream_encoder) {
433 for (i = 0; i < caps->num_hpo_dp_stream_encoder; i++) {
434 pool->hpo_dp_stream_enc[i] = create_funcs->create_hpo_dp_stream_encoder(i+ENGINE_ID_HPO_DP_0, ctx);
435 if (pool->hpo_dp_stream_enc[i] == NULL)
436 DC_ERR("DC: failed to create HPO DP stream encoder!\n");
437 pool->hpo_dp_stream_enc_count++;
438
439 }
440 }
441
442 pool->hpo_dp_link_enc_count = 0;
443 if (create_funcs->create_hpo_dp_link_encoder) {
444 for (i = 0; i < caps->num_hpo_dp_link_encoder; i++) {
445 pool->hpo_dp_link_enc[i] = create_funcs->create_hpo_dp_link_encoder(i, ctx);
446 if (pool->hpo_dp_link_enc[i] == NULL)
447 DC_ERR("DC: failed to create HPO DP link encoder!\n");
448 pool->hpo_dp_link_enc_count++;
449 }
450 }
451
452 for (i = 0; i < caps->num_mpc_3dlut; i++) {
453 pool->mpc_lut[i] = dc_create_3dlut_func();
454 if (pool->mpc_lut[i] == NULL)
455 DC_ERR("DC: failed to create MPC 3dlut!\n");
456 pool->mpc_shaper[i] = dc_create_transfer_func();
457 if (pool->mpc_shaper[i] == NULL)
458 DC_ERR("DC: failed to create MPC shaper!\n");
459 }
460
461 dc->caps.dynamic_audio = false;
462 if (pool->audio_count < pool->stream_enc_count) {
463 dc->caps.dynamic_audio = true;
464 }
465 for (i = 0; i < num_virtual_links; i++) {
466 pool->stream_enc[pool->stream_enc_count] =
467 virtual_stream_encoder_create(
468 ctx, bp: ctx->dc_bios);
469 if (pool->stream_enc[pool->stream_enc_count] == NULL) {
470 DC_ERR("DC: failed to create stream_encoder!\n");
471 return false;
472 }
473 pool->stream_enc_count++;
474 }
475
476 dc->hwseq = create_funcs->create_hwseq(ctx);
477
478 return true;
479}
480static int find_matching_clock_source(
481 const struct resource_pool *pool,
482 struct clock_source *clock_source)
483{
484
485 int i;
486
487 for (i = 0; i < pool->clk_src_count; i++) {
488 if (pool->clock_sources[i] == clock_source)
489 return i;
490 }
491 return -1;
492}
493
494void resource_unreference_clock_source(
495 struct resource_context *res_ctx,
496 const struct resource_pool *pool,
497 struct clock_source *clock_source)
498{
499 int i = find_matching_clock_source(pool, clock_source);
500
501 if (i > -1)
502 res_ctx->clock_source_ref_count[i]--;
503
504 if (pool->dp_clock_source == clock_source)
505 res_ctx->dp_clock_source_ref_count--;
506}
507
508void resource_reference_clock_source(
509 struct resource_context *res_ctx,
510 const struct resource_pool *pool,
511 struct clock_source *clock_source)
512{
513 int i = find_matching_clock_source(pool, clock_source);
514
515 if (i > -1)
516 res_ctx->clock_source_ref_count[i]++;
517
518 if (pool->dp_clock_source == clock_source)
519 res_ctx->dp_clock_source_ref_count++;
520}
521
522int resource_get_clock_source_reference(
523 struct resource_context *res_ctx,
524 const struct resource_pool *pool,
525 struct clock_source *clock_source)
526{
527 int i = find_matching_clock_source(pool, clock_source);
528
529 if (i > -1)
530 return res_ctx->clock_source_ref_count[i];
531
532 if (pool->dp_clock_source == clock_source)
533 return res_ctx->dp_clock_source_ref_count;
534
535 return -1;
536}
537
538bool resource_are_vblanks_synchronizable(
539 struct dc_stream_state *stream1,
540 struct dc_stream_state *stream2)
541{
542 uint32_t base60_refresh_rates[] = {10, 20, 5};
543 uint8_t i;
544 uint8_t rr_count = ARRAY_SIZE(base60_refresh_rates);
545 uint64_t frame_time_diff;
546
547 if (stream1->ctx->dc->config.vblank_alignment_dto_params &&
548 stream1->ctx->dc->config.vblank_alignment_max_frame_time_diff > 0 &&
549 dc_is_dp_signal(signal: stream1->signal) &&
550 dc_is_dp_signal(signal: stream2->signal) &&
551 false == stream1->has_non_synchronizable_pclk &&
552 false == stream2->has_non_synchronizable_pclk &&
553 stream1->timing.flags.VBLANK_SYNCHRONIZABLE &&
554 stream2->timing.flags.VBLANK_SYNCHRONIZABLE) {
555 /* disable refresh rates higher than 60Hz for now */
556 if (stream1->timing.pix_clk_100hz*100/stream1->timing.h_total/
557 stream1->timing.v_total > 60)
558 return false;
559 if (stream2->timing.pix_clk_100hz*100/stream2->timing.h_total/
560 stream2->timing.v_total > 60)
561 return false;
562 frame_time_diff = (uint64_t)10000 *
563 stream1->timing.h_total *
564 stream1->timing.v_total *
565 stream2->timing.pix_clk_100hz;
566 frame_time_diff = div_u64(dividend: frame_time_diff, divisor: stream1->timing.pix_clk_100hz);
567 frame_time_diff = div_u64(dividend: frame_time_diff, divisor: stream2->timing.h_total);
568 frame_time_diff = div_u64(dividend: frame_time_diff, divisor: stream2->timing.v_total);
569 for (i = 0; i < rr_count; i++) {
570 int64_t diff = (int64_t)div_u64(dividend: frame_time_diff * base60_refresh_rates[i], divisor: 10) - 10000;
571
572 if (diff < 0)
573 diff = -diff;
574 if (diff < stream1->ctx->dc->config.vblank_alignment_max_frame_time_diff)
575 return true;
576 }
577 }
578 return false;
579}
580
581bool resource_are_streams_timing_synchronizable(
582 struct dc_stream_state *stream1,
583 struct dc_stream_state *stream2)
584{
585 if (stream1->timing.h_total != stream2->timing.h_total)
586 return false;
587
588 if (stream1->timing.v_total != stream2->timing.v_total)
589 return false;
590
591 if (stream1->timing.h_addressable
592 != stream2->timing.h_addressable)
593 return false;
594
595 if (stream1->timing.v_addressable
596 != stream2->timing.v_addressable)
597 return false;
598
599 if (stream1->timing.v_front_porch
600 != stream2->timing.v_front_porch)
601 return false;
602
603 if (stream1->timing.pix_clk_100hz
604 != stream2->timing.pix_clk_100hz)
605 return false;
606
607 if (stream1->clamping.c_depth != stream2->clamping.c_depth)
608 return false;
609
610 if (stream1->phy_pix_clk != stream2->phy_pix_clk
611 && (!dc_is_dp_signal(signal: stream1->signal)
612 || !dc_is_dp_signal(signal: stream2->signal)))
613 return false;
614
615 if (stream1->view_format != stream2->view_format)
616 return false;
617
618 if (stream1->ignore_msa_timing_param || stream2->ignore_msa_timing_param)
619 return false;
620
621 return true;
622}
623static bool is_dp_and_hdmi_sharable(
624 struct dc_stream_state *stream1,
625 struct dc_stream_state *stream2)
626{
627 if (stream1->ctx->dc->caps.disable_dp_clk_share)
628 return false;
629
630 if (stream1->clamping.c_depth != COLOR_DEPTH_888 ||
631 stream2->clamping.c_depth != COLOR_DEPTH_888)
632 return false;
633
634 return true;
635
636}
637
638static bool is_sharable_clk_src(
639 const struct pipe_ctx *pipe_with_clk_src,
640 const struct pipe_ctx *pipe)
641{
642 if (pipe_with_clk_src->clock_source == NULL)
643 return false;
644
645 if (pipe_with_clk_src->stream->signal == SIGNAL_TYPE_VIRTUAL)
646 return false;
647
648 if (dc_is_dp_signal(signal: pipe_with_clk_src->stream->signal) ||
649 (dc_is_dp_signal(signal: pipe->stream->signal) &&
650 !is_dp_and_hdmi_sharable(stream1: pipe_with_clk_src->stream,
651 stream2: pipe->stream)))
652 return false;
653
654 if (dc_is_hdmi_signal(signal: pipe_with_clk_src->stream->signal)
655 && dc_is_dual_link_signal(signal: pipe->stream->signal))
656 return false;
657
658 if (dc_is_hdmi_signal(signal: pipe->stream->signal)
659 && dc_is_dual_link_signal(signal: pipe_with_clk_src->stream->signal))
660 return false;
661
662 if (!resource_are_streams_timing_synchronizable(
663 stream1: pipe_with_clk_src->stream, stream2: pipe->stream))
664 return false;
665
666 return true;
667}
668
669struct clock_source *resource_find_used_clk_src_for_sharing(
670 struct resource_context *res_ctx,
671 struct pipe_ctx *pipe_ctx)
672{
673 int i;
674
675 for (i = 0; i < MAX_PIPES; i++) {
676 if (is_sharable_clk_src(pipe_with_clk_src: &res_ctx->pipe_ctx[i], pipe: pipe_ctx))
677 return res_ctx->pipe_ctx[i].clock_source;
678 }
679
680 return NULL;
681}
682
683static enum pixel_format convert_pixel_format_to_dalsurface(
684 enum surface_pixel_format surface_pixel_format)
685{
686 enum pixel_format dal_pixel_format = PIXEL_FORMAT_UNKNOWN;
687
688 switch (surface_pixel_format) {
689 case SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS:
690 dal_pixel_format = PIXEL_FORMAT_INDEX8;
691 break;
692 case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555:
693 dal_pixel_format = PIXEL_FORMAT_RGB565;
694 break;
695 case SURFACE_PIXEL_FORMAT_GRPH_RGB565:
696 dal_pixel_format = PIXEL_FORMAT_RGB565;
697 break;
698 case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888:
699 dal_pixel_format = PIXEL_FORMAT_ARGB8888;
700 break;
701 case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888:
702 dal_pixel_format = PIXEL_FORMAT_ARGB8888;
703 break;
704 case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010:
705 dal_pixel_format = PIXEL_FORMAT_ARGB2101010;
706 break;
707 case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010:
708 dal_pixel_format = PIXEL_FORMAT_ARGB2101010;
709 break;
710 case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS:
711 dal_pixel_format = PIXEL_FORMAT_ARGB2101010_XRBIAS;
712 break;
713 case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F:
714 case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F:
715 dal_pixel_format = PIXEL_FORMAT_FP16;
716 break;
717 case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr:
718 case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb:
719 dal_pixel_format = PIXEL_FORMAT_420BPP8;
720 break;
721 case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr:
722 case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb:
723 dal_pixel_format = PIXEL_FORMAT_420BPP10;
724 break;
725 case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616:
726 case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616:
727 default:
728 dal_pixel_format = PIXEL_FORMAT_UNKNOWN;
729 break;
730 }
731 return dal_pixel_format;
732}
733
734static inline void get_vp_scan_direction(
735 enum dc_rotation_angle rotation,
736 bool horizontal_mirror,
737 bool *orthogonal_rotation,
738 bool *flip_vert_scan_dir,
739 bool *flip_horz_scan_dir)
740{
741 *orthogonal_rotation = false;
742 *flip_vert_scan_dir = false;
743 *flip_horz_scan_dir = false;
744 if (rotation == ROTATION_ANGLE_180) {
745 *flip_vert_scan_dir = true;
746 *flip_horz_scan_dir = true;
747 } else if (rotation == ROTATION_ANGLE_90) {
748 *orthogonal_rotation = true;
749 *flip_horz_scan_dir = true;
750 } else if (rotation == ROTATION_ANGLE_270) {
751 *orthogonal_rotation = true;
752 *flip_vert_scan_dir = true;
753 }
754
755 if (horizontal_mirror)
756 *flip_horz_scan_dir = !*flip_horz_scan_dir;
757}
758
759/*
760 * This is a preliminary vp size calculation to allow us to check taps support.
761 * The result is completely overridden afterwards.
762 */
763static void calculate_viewport_size(struct pipe_ctx *pipe_ctx)
764{
765 struct scaler_data *data = &pipe_ctx->plane_res.scl_data;
766
767 data->viewport.width = dc_fixpt_ceil(arg: dc_fixpt_mul_int(arg1: data->ratios.horz, arg2: data->recout.width));
768 data->viewport.height = dc_fixpt_ceil(arg: dc_fixpt_mul_int(arg1: data->ratios.vert, arg2: data->recout.height));
769 data->viewport_c.width = dc_fixpt_ceil(arg: dc_fixpt_mul_int(arg1: data->ratios.horz_c, arg2: data->recout.width));
770 data->viewport_c.height = dc_fixpt_ceil(arg: dc_fixpt_mul_int(arg1: data->ratios.vert_c, arg2: data->recout.height));
771 if (pipe_ctx->plane_state->rotation == ROTATION_ANGLE_90 ||
772 pipe_ctx->plane_state->rotation == ROTATION_ANGLE_270) {
773 swap(data->viewport.width, data->viewport.height);
774 swap(data->viewport_c.width, data->viewport_c.height);
775 }
776}
777
778static struct rect intersect_rec(const struct rect *r0, const struct rect *r1)
779{
780 struct rect rec;
781 int r0_x_end = r0->x + r0->width;
782 int r1_x_end = r1->x + r1->width;
783 int r0_y_end = r0->y + r0->height;
784 int r1_y_end = r1->y + r1->height;
785
786 rec.x = r0->x > r1->x ? r0->x : r1->x;
787 rec.width = r0_x_end > r1_x_end ? r1_x_end - rec.x : r0_x_end - rec.x;
788 rec.y = r0->y > r1->y ? r0->y : r1->y;
789 rec.height = r0_y_end > r1_y_end ? r1_y_end - rec.y : r0_y_end - rec.y;
790
791 /* in case that there is no intersection */
792 if (rec.width < 0 || rec.height < 0)
793 memset(&rec, 0, sizeof(rec));
794
795 return rec;
796}
797
798static struct rect shift_rec(const struct rect *rec_in, int x, int y)
799{
800 struct rect rec_out = *rec_in;
801
802 rec_out.x += x;
803 rec_out.y += y;
804
805 return rec_out;
806}
807
808static struct rect calculate_odm_slice_in_timing_active(struct pipe_ctx *pipe_ctx)
809{
810 const struct dc_stream_state *stream = pipe_ctx->stream;
811 int odm_slice_count = resource_get_odm_slice_count(pipe: pipe_ctx);
812 int odm_slice_idx = resource_get_odm_slice_index(opp_head: pipe_ctx);
813 bool is_last_odm_slice = (odm_slice_idx + 1) == odm_slice_count;
814 int h_active = stream->timing.h_addressable +
815 stream->timing.h_border_left +
816 stream->timing.h_border_right;
817 int odm_slice_width = h_active / odm_slice_count;
818 struct rect odm_rec;
819
820 odm_rec.x = odm_slice_width * odm_slice_idx;
821 odm_rec.width = is_last_odm_slice ?
822 /* last slice width is the reminder of h_active */
823 h_active - odm_slice_width * (odm_slice_count - 1) :
824 /* odm slice width is the floor of h_active / count */
825 odm_slice_width;
826 odm_rec.y = 0;
827 odm_rec.height = stream->timing.v_addressable +
828 stream->timing.v_border_bottom +
829 stream->timing.v_border_top;
830
831 return odm_rec;
832}
833
834static struct rect calculate_plane_rec_in_timing_active(
835 struct pipe_ctx *pipe_ctx,
836 const struct rect *rec_in)
837{
838 /*
839 * The following diagram shows an example where we map a 1920x1200
840 * desktop to a 2560x1440 timing with a plane rect in the middle
841 * of the screen. To map a plane rect from Stream Source to Timing
842 * Active space, we first multiply stream scaling ratios (i.e 2304/1920
843 * horizontal and 1440/1200 vertical) to the plane's x and y, then
844 * we add stream destination offsets (i.e 128 horizontal, 0 vertical).
845 * This will give us a plane rect's position in Timing Active. However
846 * we have to remove the fractional. The rule is that we find left/right
847 * and top/bottom positions and round the value to the adjacent integer.
848 *
849 * Stream Source Space
850 * ------------
851 * __________________________________________________
852 * |Stream Source (1920 x 1200) ^ |
853 * | y |
854 * | <------- w --------|> |
855 * | __________________V |
856 * |<-- x -->|Plane//////////////| ^ |
857 * | |(pre scale)////////| | |
858 * | |///////////////////| | |
859 * | |///////////////////| h |
860 * | |///////////////////| | |
861 * | |///////////////////| | |
862 * | |///////////////////| V |
863 * | |
864 * | |
865 * |__________________________________________________|
866 *
867 *
868 * Timing Active Space
869 * ---------------------------------
870 *
871 * Timing Active (2560 x 1440)
872 * __________________________________________________
873 * |*****| Stteam Destination (2304 x 1440) |*****|
874 * |*****| |*****|
875 * |<128>| |*****|
876 * |*****| __________________ |*****|
877 * |*****| |Plane/////////////| |*****|
878 * |*****| |(post scale)//////| |*****|
879 * |*****| |//////////////////| |*****|
880 * |*****| |//////////////////| |*****|
881 * |*****| |//////////////////| |*****|
882 * |*****| |//////////////////| |*****|
883 * |*****| |*****|
884 * |*****| |*****|
885 * |*****| |*****|
886 * |*****|______________________________________|*****|
887 *
888 * So the resulting formulas are shown below:
889 *
890 * recout_x = 128 + round(plane_x * 2304 / 1920)
891 * recout_w = 128 + round((plane_x + plane_w) * 2304 / 1920) - recout_x
892 * recout_y = 0 + round(plane_y * 1440 / 1280)
893 * recout_h = 0 + round((plane_y + plane_h) * 1440 / 1200) - recout_y
894 *
895 * NOTE: fixed point division is not error free. To reduce errors
896 * introduced by fixed point division, we divide only after
897 * multiplication is complete.
898 */
899 const struct dc_stream_state *stream = pipe_ctx->stream;
900 struct rect rec_out = {0};
901 struct fixed31_32 temp;
902
903 temp = dc_fixpt_from_fraction(numerator: rec_in->x * stream->dst.width,
904 denominator: stream->src.width);
905 rec_out.x = stream->dst.x + dc_fixpt_round(arg: temp);
906
907 temp = dc_fixpt_from_fraction(
908 numerator: (rec_in->x + rec_in->width) * stream->dst.width,
909 denominator: stream->src.width);
910 rec_out.width = stream->dst.x + dc_fixpt_round(arg: temp) - rec_out.x;
911
912 temp = dc_fixpt_from_fraction(numerator: rec_in->y * stream->dst.height,
913 denominator: stream->src.height);
914 rec_out.y = stream->dst.y + dc_fixpt_round(arg: temp);
915
916 temp = dc_fixpt_from_fraction(
917 numerator: (rec_in->y + rec_in->height) * stream->dst.height,
918 denominator: stream->src.height);
919 rec_out.height = stream->dst.y + dc_fixpt_round(arg: temp) - rec_out.y;
920
921 return rec_out;
922}
923
924static struct rect calculate_mpc_slice_in_timing_active(
925 struct pipe_ctx *pipe_ctx,
926 struct rect *plane_clip_rec)
927{
928 const struct dc_stream_state *stream = pipe_ctx->stream;
929 int mpc_slice_count = resource_get_mpc_slice_count(pipe: pipe_ctx);
930 int mpc_slice_idx = resource_get_mpc_slice_index(dpp_pipe: pipe_ctx);
931 int epimo = mpc_slice_count - plane_clip_rec->width % mpc_slice_count - 1;
932 struct rect mpc_rec;
933
934 mpc_rec.width = plane_clip_rec->width / mpc_slice_count;
935 mpc_rec.x = plane_clip_rec->x + mpc_rec.width * mpc_slice_idx;
936 mpc_rec.height = plane_clip_rec->height;
937 mpc_rec.y = plane_clip_rec->y;
938 ASSERT(mpc_slice_count == 1 ||
939 stream->view_format != VIEW_3D_FORMAT_SIDE_BY_SIDE ||
940 mpc_rec.width % 2 == 0);
941
942 /* extra pixels in the division remainder need to go to pipes after
943 * the extra pixel index minus one(epimo) defined here as:
944 */
945 if (mpc_slice_idx > epimo) {
946 mpc_rec.x += mpc_slice_idx - epimo - 1;
947 mpc_rec.width += 1;
948 }
949
950 if (stream->view_format == VIEW_3D_FORMAT_TOP_AND_BOTTOM) {
951 ASSERT(mpc_rec.height % 2 == 0);
952 mpc_rec.height /= 2;
953 }
954 return mpc_rec;
955}
956
957static void adjust_recout_for_visual_confirm(struct rect *recout,
958 struct pipe_ctx *pipe_ctx)
959{
960 struct dc *dc = pipe_ctx->stream->ctx->dc;
961 int dpp_offset, base_offset;
962
963 if (dc->debug.visual_confirm == VISUAL_CONFIRM_DISABLE || !pipe_ctx->plane_res.dpp)
964 return;
965
966 dpp_offset = pipe_ctx->stream->timing.v_addressable / VISUAL_CONFIRM_DPP_OFFSET_DENO;
967 dpp_offset *= pipe_ctx->plane_res.dpp->inst;
968
969 if ((dc->debug.visual_confirm_rect_height >= VISUAL_CONFIRM_BASE_MIN) &&
970 dc->debug.visual_confirm_rect_height <= VISUAL_CONFIRM_BASE_MAX)
971 base_offset = dc->debug.visual_confirm_rect_height;
972 else
973 base_offset = VISUAL_CONFIRM_BASE_DEFAULT;
974
975 recout->height -= base_offset;
976 recout->height -= dpp_offset;
977}
978
979/*
980 * The function maps a plane clip from Stream Source Space to ODM Slice Space
981 * and calculates the rec of the overlapping area of MPC slice of the plane
982 * clip, ODM slice associated with the pipe context and stream destination rec.
983 */
984static void calculate_recout(struct pipe_ctx *pipe_ctx)
985{
986 /*
987 * A plane clip represents the desired plane size and position in Stream
988 * Source Space. Stream Source is the destination where all planes are
989 * blended (i.e. positioned, scaled and overlaid). It is a canvas where
990 * all planes associated with the current stream are drawn together.
991 * After Stream Source is completed, we will further scale and
992 * reposition the entire canvas of the stream source to Stream
993 * Destination in Timing Active Space. This could be due to display
994 * overscan adjustment where we will need to rescale and reposition all
995 * the planes so they can fit into a TV with overscan or downscale
996 * upscale features such as GPU scaling or VSR.
997 *
998 * This two step blending is a virtual procedure in software. In
999 * hardware there is no such thing as Stream Source. all planes are
1000 * blended once in Timing Active Space. Software virtualizes a Stream
1001 * Source space to decouple the math complicity so scaling param
1002 * calculation focuses on one step at a time.
1003 *
1004 * In the following two diagrams, user applied 10% overscan adjustment
1005 * so the Stream Source needs to be scaled down a little before mapping
1006 * to Timing Active Space. As a result the Plane Clip is also scaled
1007 * down by the same ratio, Plane Clip position (i.e. x and y) with
1008 * respect to Stream Source is also scaled down. To map it in Timing
1009 * Active Space additional x and y offsets from Stream Destination are
1010 * added to Plane Clip as well.
1011 *
1012 * Stream Source Space
1013 * ------------
1014 * __________________________________________________
1015 * |Stream Source (3840 x 2160) ^ |
1016 * | y |
1017 * | | |
1018 * | __________________V |
1019 * |<-- x -->|Plane Clip/////////| |
1020 * | |(pre scale)////////| |
1021 * | |///////////////////| |
1022 * | |///////////////////| |
1023 * | |///////////////////| |
1024 * | |///////////////////| |
1025 * | |///////////////////| |
1026 * | |
1027 * | |
1028 * |__________________________________________________|
1029 *
1030 *
1031 * Timing Active Space (3840 x 2160)
1032 * ---------------------------------
1033 *
1034 * Timing Active
1035 * __________________________________________________
1036 * | y_____________________________________________ |
1037 * |x |Stream Destination (3456 x 1944) | |
1038 * | | | |
1039 * | | __________________ | |
1040 * | | |Plane Clip////////| | |
1041 * | | |(post scale)//////| | |
1042 * | | |//////////////////| | |
1043 * | | |//////////////////| | |
1044 * | | |//////////////////| | |
1045 * | | |//////////////////| | |
1046 * | | | |
1047 * | | | |
1048 * | |____________________________________________| |
1049 * |__________________________________________________|
1050 *
1051 *
1052 * In Timing Active Space a plane clip could be further sliced into
1053 * pieces called MPC slices. Each Pipe Context is responsible for
1054 * processing only one MPC slice so the plane processing workload can be
1055 * distributed to multiple DPP Pipes. MPC slices could be blended
1056 * together to a single ODM slice. Each ODM slice is responsible for
1057 * processing a portion of Timing Active divided horizontally so the
1058 * output pixel processing workload can be distributed to multiple OPP
1059 * pipes. All ODM slices are mapped together in ODM block so all MPC
1060 * slices belong to different ODM slices could be pieced together to
1061 * form a single image in Timing Active. MPC slices must belong to
1062 * single ODM slice. If an MPC slice goes across ODM slice boundary, it
1063 * needs to be divided into two MPC slices one for each ODM slice.
1064 *
1065 * In the following diagram the output pixel processing workload is
1066 * divided horizontally into two ODM slices one for each OPP blend tree.
1067 * OPP0 blend tree is responsible for processing left half of Timing
1068 * Active, while OPP2 blend tree is responsible for processing right
1069 * half.
1070 *
1071 * The plane has two MPC slices. However since the right MPC slice goes
1072 * across ODM boundary, two DPP pipes are needed one for each OPP blend
1073 * tree. (i.e. DPP1 for OPP0 blend tree and DPP2 for OPP2 blend tree).
1074 *
1075 * Assuming that we have a Pipe Context associated with OPP0 and DPP1
1076 * working on processing the plane in the diagram. We want to know the
1077 * width and height of the shaded rectangle and its relative position
1078 * with respect to the ODM slice0. This is called the recout of the pipe
1079 * context.
1080 *
1081 * Planes can be at arbitrary size and position and there could be an
1082 * arbitrary number of MPC and ODM slices. The algorithm needs to take
1083 * all scenarios into account.
1084 *
1085 * Timing Active Space (3840 x 2160)
1086 * ---------------------------------
1087 *
1088 * Timing Active
1089 * __________________________________________________
1090 * |OPP0(ODM slice0)^ |OPP2(ODM slice1) |
1091 * | y | |
1092 * | | <- w -> |
1093 * | _____V________|____ |
1094 * | |DPP0 ^ |DPP1 |DPP2| |
1095 * |<------ x |-----|->|/////| | |
1096 * | | | |/////| | |
1097 * | | h |/////| | |
1098 * | | | |/////| | |
1099 * | |_____V__|/////|____| |
1100 * | | |
1101 * | | |
1102 * | | |
1103 * |_________________________|________________________|
1104 *
1105 *
1106 */
1107 struct rect plane_clip;
1108 struct rect mpc_slice_of_plane_clip;
1109 struct rect odm_slice;
1110 struct rect overlapping_area;
1111
1112 plane_clip = calculate_plane_rec_in_timing_active(pipe_ctx,
1113 rec_in: &pipe_ctx->plane_state->clip_rect);
1114 /* guard plane clip from drawing beyond stream dst here */
1115 plane_clip = intersect_rec(r0: &plane_clip,
1116 r1: &pipe_ctx->stream->dst);
1117 mpc_slice_of_plane_clip = calculate_mpc_slice_in_timing_active(
1118 pipe_ctx, plane_clip_rec: &plane_clip);
1119 odm_slice = calculate_odm_slice_in_timing_active(pipe_ctx);
1120 overlapping_area = intersect_rec(r0: &mpc_slice_of_plane_clip, r1: &odm_slice);
1121 if (overlapping_area.height > 0 &&
1122 overlapping_area.width > 0) {
1123 /* shift the overlapping area so it is with respect to current
1124 * ODM slice's position
1125 */
1126 pipe_ctx->plane_res.scl_data.recout = shift_rec(
1127 rec_in: &overlapping_area,
1128 x: -odm_slice.x, y: -odm_slice.y);
1129 adjust_recout_for_visual_confirm(
1130 recout: &pipe_ctx->plane_res.scl_data.recout,
1131 pipe_ctx);
1132 } else {
1133 /* if there is no overlap, zero recout */
1134 memset(&pipe_ctx->plane_res.scl_data.recout, 0,
1135 sizeof(struct rect));
1136 }
1137
1138}
1139
1140static void calculate_scaling_ratios(struct pipe_ctx *pipe_ctx)
1141{
1142 const struct dc_plane_state *plane_state = pipe_ctx->plane_state;
1143 const struct dc_stream_state *stream = pipe_ctx->stream;
1144 struct rect surf_src = plane_state->src_rect;
1145 const int in_w = stream->src.width;
1146 const int in_h = stream->src.height;
1147 const int out_w = stream->dst.width;
1148 const int out_h = stream->dst.height;
1149
1150 /*Swap surf_src height and width since scaling ratios are in recout rotation*/
1151 if (pipe_ctx->plane_state->rotation == ROTATION_ANGLE_90 ||
1152 pipe_ctx->plane_state->rotation == ROTATION_ANGLE_270)
1153 swap(surf_src.height, surf_src.width);
1154
1155 pipe_ctx->plane_res.scl_data.ratios.horz = dc_fixpt_from_fraction(
1156 numerator: surf_src.width,
1157 denominator: plane_state->dst_rect.width);
1158 pipe_ctx->plane_res.scl_data.ratios.vert = dc_fixpt_from_fraction(
1159 numerator: surf_src.height,
1160 denominator: plane_state->dst_rect.height);
1161
1162 if (stream->view_format == VIEW_3D_FORMAT_SIDE_BY_SIDE)
1163 pipe_ctx->plane_res.scl_data.ratios.horz.value *= 2;
1164 else if (stream->view_format == VIEW_3D_FORMAT_TOP_AND_BOTTOM)
1165 pipe_ctx->plane_res.scl_data.ratios.vert.value *= 2;
1166
1167 pipe_ctx->plane_res.scl_data.ratios.vert.value = div64_s64(
1168 dividend: pipe_ctx->plane_res.scl_data.ratios.vert.value * in_h, divisor: out_h);
1169 pipe_ctx->plane_res.scl_data.ratios.horz.value = div64_s64(
1170 dividend: pipe_ctx->plane_res.scl_data.ratios.horz.value * in_w, divisor: out_w);
1171
1172 pipe_ctx->plane_res.scl_data.ratios.horz_c = pipe_ctx->plane_res.scl_data.ratios.horz;
1173 pipe_ctx->plane_res.scl_data.ratios.vert_c = pipe_ctx->plane_res.scl_data.ratios.vert;
1174
1175 if (pipe_ctx->plane_res.scl_data.format == PIXEL_FORMAT_420BPP8
1176 || pipe_ctx->plane_res.scl_data.format == PIXEL_FORMAT_420BPP10) {
1177 pipe_ctx->plane_res.scl_data.ratios.horz_c.value /= 2;
1178 pipe_ctx->plane_res.scl_data.ratios.vert_c.value /= 2;
1179 }
1180 pipe_ctx->plane_res.scl_data.ratios.horz = dc_fixpt_truncate(
1181 arg: pipe_ctx->plane_res.scl_data.ratios.horz, frac_bits: 19);
1182 pipe_ctx->plane_res.scl_data.ratios.vert = dc_fixpt_truncate(
1183 arg: pipe_ctx->plane_res.scl_data.ratios.vert, frac_bits: 19);
1184 pipe_ctx->plane_res.scl_data.ratios.horz_c = dc_fixpt_truncate(
1185 arg: pipe_ctx->plane_res.scl_data.ratios.horz_c, frac_bits: 19);
1186 pipe_ctx->plane_res.scl_data.ratios.vert_c = dc_fixpt_truncate(
1187 arg: pipe_ctx->plane_res.scl_data.ratios.vert_c, frac_bits: 19);
1188}
1189
1190
1191/*
1192 * We completely calculate vp offset, size and inits here based entirely on scaling
1193 * ratios and recout for pixel perfect pipe combine.
1194 */
1195static void calculate_init_and_vp(
1196 bool flip_scan_dir,
1197 int recout_offset_within_recout_full,
1198 int recout_size,
1199 int src_size,
1200 int taps,
1201 struct fixed31_32 ratio,
1202 struct fixed31_32 *init,
1203 int *vp_offset,
1204 int *vp_size)
1205{
1206 struct fixed31_32 temp;
1207 int int_part;
1208
1209 /*
1210 * First of the taps starts sampling pixel number <init_int_part> corresponding to recout
1211 * pixel 1. Next recout pixel samples int part of <init + scaling ratio> and so on.
1212 * All following calculations are based on this logic.
1213 *
1214 * Init calculated according to formula:
1215 * init = (scaling_ratio + number_of_taps + 1) / 2
1216 * init_bot = init + scaling_ratio
1217 * to get pixel perfect combine add the fraction from calculating vp offset
1218 */
1219 temp = dc_fixpt_mul_int(arg1: ratio, arg2: recout_offset_within_recout_full);
1220 *vp_offset = dc_fixpt_floor(arg: temp);
1221 temp.value &= 0xffffffff;
1222 *init = dc_fixpt_truncate(arg: dc_fixpt_add(arg1: dc_fixpt_div_int(
1223 arg1: dc_fixpt_add_int(arg1: ratio, arg2: taps + 1), arg2: 2), arg2: temp), frac_bits: 19);
1224 /*
1225 * If viewport has non 0 offset and there are more taps than covered by init then
1226 * we should decrease the offset and increase init so we are never sampling
1227 * outside of viewport.
1228 */
1229 int_part = dc_fixpt_floor(arg: *init);
1230 if (int_part < taps) {
1231 int_part = taps - int_part;
1232 if (int_part > *vp_offset)
1233 int_part = *vp_offset;
1234 *vp_offset -= int_part;
1235 *init = dc_fixpt_add_int(arg1: *init, arg2: int_part);
1236 }
1237 /*
1238 * If taps are sampling outside of viewport at end of recout and there are more pixels
1239 * available in the surface we should increase the viewport size, regardless set vp to
1240 * only what is used.
1241 */
1242 temp = dc_fixpt_add(arg1: *init, arg2: dc_fixpt_mul_int(arg1: ratio, arg2: recout_size - 1));
1243 *vp_size = dc_fixpt_floor(arg: temp);
1244 if (*vp_size + *vp_offset > src_size)
1245 *vp_size = src_size - *vp_offset;
1246
1247 /* We did all the math assuming we are scanning same direction as display does,
1248 * however mirror/rotation changes how vp scans vs how it is offset. If scan direction
1249 * is flipped we simply need to calculate offset from the other side of plane.
1250 * Note that outside of viewport all scaling hardware works in recout space.
1251 */
1252 if (flip_scan_dir)
1253 *vp_offset = src_size - *vp_offset - *vp_size;
1254}
1255
1256static void calculate_inits_and_viewports(struct pipe_ctx *pipe_ctx)
1257{
1258 const struct dc_plane_state *plane_state = pipe_ctx->plane_state;
1259 struct scaler_data *data = &pipe_ctx->plane_res.scl_data;
1260 struct rect src = plane_state->src_rect;
1261 struct rect recout_dst_in_active_timing;
1262 struct rect recout_clip_in_active_timing;
1263 struct rect recout_clip_in_recout_dst;
1264 struct rect overlap_in_active_timing;
1265 struct rect odm_slice = calculate_odm_slice_in_timing_active(pipe_ctx);
1266 int vpc_div = (data->format == PIXEL_FORMAT_420BPP8
1267 || data->format == PIXEL_FORMAT_420BPP10) ? 2 : 1;
1268 bool orthogonal_rotation, flip_vert_scan_dir, flip_horz_scan_dir;
1269
1270 recout_clip_in_active_timing = shift_rec(
1271 rec_in: &data->recout, x: odm_slice.x, y: odm_slice.y);
1272 recout_dst_in_active_timing = calculate_plane_rec_in_timing_active(
1273 pipe_ctx, rec_in: &plane_state->dst_rect);
1274 overlap_in_active_timing = intersect_rec(r0: &recout_clip_in_active_timing,
1275 r1: &recout_dst_in_active_timing);
1276 if (overlap_in_active_timing.width > 0 &&
1277 overlap_in_active_timing.height > 0)
1278 recout_clip_in_recout_dst = shift_rec(rec_in: &overlap_in_active_timing,
1279 x: -recout_dst_in_active_timing.x,
1280 y: -recout_dst_in_active_timing.y);
1281 else
1282 memset(&recout_clip_in_recout_dst, 0, sizeof(struct rect));
1283
1284 /*
1285 * Work in recout rotation since that requires less transformations
1286 */
1287 get_vp_scan_direction(
1288 rotation: plane_state->rotation,
1289 horizontal_mirror: plane_state->horizontal_mirror,
1290 orthogonal_rotation: &orthogonal_rotation,
1291 flip_vert_scan_dir: &flip_vert_scan_dir,
1292 flip_horz_scan_dir: &flip_horz_scan_dir);
1293
1294 if (orthogonal_rotation) {
1295 swap(src.width, src.height);
1296 swap(flip_vert_scan_dir, flip_horz_scan_dir);
1297 }
1298
1299 calculate_init_and_vp(
1300 flip_scan_dir: flip_horz_scan_dir,
1301 recout_offset_within_recout_full: recout_clip_in_recout_dst.x,
1302 recout_size: data->recout.width,
1303 src_size: src.width,
1304 taps: data->taps.h_taps,
1305 ratio: data->ratios.horz,
1306 init: &data->inits.h,
1307 vp_offset: &data->viewport.x,
1308 vp_size: &data->viewport.width);
1309 calculate_init_and_vp(
1310 flip_scan_dir: flip_horz_scan_dir,
1311 recout_offset_within_recout_full: recout_clip_in_recout_dst.x,
1312 recout_size: data->recout.width,
1313 src_size: src.width / vpc_div,
1314 taps: data->taps.h_taps_c,
1315 ratio: data->ratios.horz_c,
1316 init: &data->inits.h_c,
1317 vp_offset: &data->viewport_c.x,
1318 vp_size: &data->viewport_c.width);
1319 calculate_init_and_vp(
1320 flip_scan_dir: flip_vert_scan_dir,
1321 recout_offset_within_recout_full: recout_clip_in_recout_dst.y,
1322 recout_size: data->recout.height,
1323 src_size: src.height,
1324 taps: data->taps.v_taps,
1325 ratio: data->ratios.vert,
1326 init: &data->inits.v,
1327 vp_offset: &data->viewport.y,
1328 vp_size: &data->viewport.height);
1329 calculate_init_and_vp(
1330 flip_scan_dir: flip_vert_scan_dir,
1331 recout_offset_within_recout_full: recout_clip_in_recout_dst.y,
1332 recout_size: data->recout.height,
1333 src_size: src.height / vpc_div,
1334 taps: data->taps.v_taps_c,
1335 ratio: data->ratios.vert_c,
1336 init: &data->inits.v_c,
1337 vp_offset: &data->viewport_c.y,
1338 vp_size: &data->viewport_c.height);
1339 if (orthogonal_rotation) {
1340 swap(data->viewport.x, data->viewport.y);
1341 swap(data->viewport.width, data->viewport.height);
1342 swap(data->viewport_c.x, data->viewport_c.y);
1343 swap(data->viewport_c.width, data->viewport_c.height);
1344 }
1345 data->viewport.x += src.x;
1346 data->viewport.y += src.y;
1347 ASSERT(src.x % vpc_div == 0 && src.y % vpc_div == 0);
1348 data->viewport_c.x += src.x / vpc_div;
1349 data->viewport_c.y += src.y / vpc_div;
1350}
1351
1352static bool is_subvp_high_refresh_candidate(struct dc_stream_state *stream)
1353{
1354 uint32_t refresh_rate;
1355 struct dc *dc = stream->ctx->dc;
1356
1357 refresh_rate = (stream->timing.pix_clk_100hz * (uint64_t)100 +
1358 stream->timing.v_total * stream->timing.h_total - (uint64_t)1);
1359 refresh_rate = div_u64(dividend: refresh_rate, divisor: stream->timing.v_total);
1360 refresh_rate = div_u64(dividend: refresh_rate, divisor: stream->timing.h_total);
1361
1362 /* If there's any stream that fits the SubVP high refresh criteria,
1363 * we must return true. This is because cursor updates are asynchronous
1364 * with full updates, so we could transition into a SubVP config and
1365 * remain in HW cursor mode if there's no cursor update which will
1366 * then cause corruption.
1367 */
1368 if ((refresh_rate >= 120 && refresh_rate <= 175 &&
1369 stream->timing.v_addressable >= 1080 &&
1370 stream->timing.v_addressable <= 2160) &&
1371 (dc->current_state->stream_count > 1 ||
1372 (dc->current_state->stream_count == 1 && !stream->allow_freesync)))
1373 return true;
1374
1375 return false;
1376}
1377
1378static enum controller_dp_test_pattern convert_dp_to_controller_test_pattern(
1379 enum dp_test_pattern test_pattern)
1380{
1381 enum controller_dp_test_pattern controller_test_pattern;
1382
1383 switch (test_pattern) {
1384 case DP_TEST_PATTERN_COLOR_SQUARES:
1385 controller_test_pattern =
1386 CONTROLLER_DP_TEST_PATTERN_COLORSQUARES;
1387 break;
1388 case DP_TEST_PATTERN_COLOR_SQUARES_CEA:
1389 controller_test_pattern =
1390 CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA;
1391 break;
1392 case DP_TEST_PATTERN_VERTICAL_BARS:
1393 controller_test_pattern =
1394 CONTROLLER_DP_TEST_PATTERN_VERTICALBARS;
1395 break;
1396 case DP_TEST_PATTERN_HORIZONTAL_BARS:
1397 controller_test_pattern =
1398 CONTROLLER_DP_TEST_PATTERN_HORIZONTALBARS;
1399 break;
1400 case DP_TEST_PATTERN_COLOR_RAMP:
1401 controller_test_pattern =
1402 CONTROLLER_DP_TEST_PATTERN_COLORRAMP;
1403 break;
1404 default:
1405 controller_test_pattern =
1406 CONTROLLER_DP_TEST_PATTERN_VIDEOMODE;
1407 break;
1408 }
1409
1410 return controller_test_pattern;
1411}
1412
1413static enum controller_dp_color_space convert_dp_to_controller_color_space(
1414 enum dp_test_pattern_color_space color_space)
1415{
1416 enum controller_dp_color_space controller_color_space;
1417
1418 switch (color_space) {
1419 case DP_TEST_PATTERN_COLOR_SPACE_RGB:
1420 controller_color_space = CONTROLLER_DP_COLOR_SPACE_RGB;
1421 break;
1422 case DP_TEST_PATTERN_COLOR_SPACE_YCBCR601:
1423 controller_color_space = CONTROLLER_DP_COLOR_SPACE_YCBCR601;
1424 break;
1425 case DP_TEST_PATTERN_COLOR_SPACE_YCBCR709:
1426 controller_color_space = CONTROLLER_DP_COLOR_SPACE_YCBCR709;
1427 break;
1428 case DP_TEST_PATTERN_COLOR_SPACE_UNDEFINED:
1429 default:
1430 controller_color_space = CONTROLLER_DP_COLOR_SPACE_UDEFINED;
1431 break;
1432 }
1433
1434 return controller_color_space;
1435}
1436
1437void resource_build_test_pattern_params(struct resource_context *res_ctx,
1438 struct pipe_ctx *otg_master)
1439{
1440 int odm_slice_width, last_odm_slice_width, offset = 0;
1441 struct pipe_ctx *opp_heads[MAX_PIPES];
1442 struct test_pattern_params *params;
1443 int odm_cnt = 1;
1444 enum controller_dp_test_pattern controller_test_pattern;
1445 enum controller_dp_color_space controller_color_space;
1446 enum dc_color_depth color_depth = otg_master->stream->timing.display_color_depth;
1447 int h_active = otg_master->stream->timing.h_addressable +
1448 otg_master->stream->timing.h_border_left +
1449 otg_master->stream->timing.h_border_right;
1450 int v_active = otg_master->stream->timing.v_addressable +
1451 otg_master->stream->timing.v_border_bottom +
1452 otg_master->stream->timing.v_border_top;
1453 int i;
1454
1455 controller_test_pattern = convert_dp_to_controller_test_pattern(
1456 test_pattern: otg_master->stream->test_pattern.type);
1457 controller_color_space = convert_dp_to_controller_color_space(
1458 color_space: otg_master->stream->test_pattern.color_space);
1459
1460 odm_cnt = resource_get_opp_heads_for_otg_master(otg_master, res_ctx, opp_heads);
1461
1462 odm_slice_width = h_active / odm_cnt;
1463 last_odm_slice_width = h_active - odm_slice_width * (odm_cnt - 1);
1464
1465 for (i = 0; i < odm_cnt; i++) {
1466 params = &opp_heads[i]->stream_res.test_pattern_params;
1467 params->test_pattern = controller_test_pattern;
1468 params->color_space = controller_color_space;
1469 params->color_depth = color_depth;
1470 params->height = v_active;
1471 params->offset = offset;
1472
1473 if (i < odm_cnt - 1)
1474 params->width = odm_slice_width;
1475 else
1476 params->width = last_odm_slice_width;
1477
1478 offset += odm_slice_width;
1479 }
1480}
1481
1482bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx)
1483{
1484 const struct dc_plane_state *plane_state = pipe_ctx->plane_state;
1485 struct dc_crtc_timing *timing = &pipe_ctx->stream->timing;
1486 const struct rect odm_slice_rec = calculate_odm_slice_in_timing_active(pipe_ctx);
1487 bool res = false;
1488 DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger);
1489
1490 /* Invalid input */
1491 if (!plane_state->dst_rect.width ||
1492 !plane_state->dst_rect.height ||
1493 !plane_state->src_rect.width ||
1494 !plane_state->src_rect.height) {
1495 ASSERT(0);
1496 return false;
1497 }
1498
1499 pipe_ctx->plane_res.scl_data.format = convert_pixel_format_to_dalsurface(
1500 surface_pixel_format: pipe_ctx->plane_state->format);
1501
1502 /* Timing borders are part of vactive that we are also supposed to skip in addition
1503 * to any stream dst offset. Since dm logic assumes dst is in addressable
1504 * space we need to add the left and top borders to dst offsets temporarily.
1505 * TODO: fix in DM, stream dst is supposed to be in vactive
1506 */
1507 pipe_ctx->stream->dst.x += timing->h_border_left;
1508 pipe_ctx->stream->dst.y += timing->v_border_top;
1509
1510 /* Calculate H and V active size */
1511 pipe_ctx->plane_res.scl_data.h_active = odm_slice_rec.width;
1512 pipe_ctx->plane_res.scl_data.v_active = odm_slice_rec.height;
1513
1514 /* depends on h_active */
1515 calculate_recout(pipe_ctx);
1516 /* depends on pixel format */
1517 calculate_scaling_ratios(pipe_ctx);
1518 /* depends on scaling ratios and recout, does not calculate offset yet */
1519 calculate_viewport_size(pipe_ctx);
1520
1521 /*
1522 * LB calculations depend on vp size, h/v_active and scaling ratios
1523 * Setting line buffer pixel depth to 24bpp yields banding
1524 * on certain displays, such as the Sharp 4k. 36bpp is needed
1525 * to support SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616 and
1526 * SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616 with actual > 10 bpc
1527 * precision on DCN display engines, but apparently not for DCE, as
1528 * far as testing on DCE-11.2 and DCE-8 showed. Various DCE parts have
1529 * problems: Carrizo with DCE_VERSION_11_0 does not like 36 bpp lb depth,
1530 * neither do DCE-8 at 4k resolution, or DCE-11.2 (broken identify pixel
1531 * passthrough). Therefore only use 36 bpp on DCN where it is actually needed.
1532 */
1533 if (plane_state->ctx->dce_version > DCE_VERSION_MAX)
1534 pipe_ctx->plane_res.scl_data.lb_params.depth = LB_PIXEL_DEPTH_36BPP;
1535 else
1536 pipe_ctx->plane_res.scl_data.lb_params.depth = LB_PIXEL_DEPTH_30BPP;
1537
1538 pipe_ctx->plane_res.scl_data.lb_params.alpha_en = plane_state->per_pixel_alpha;
1539
1540 if (pipe_ctx->plane_res.xfm != NULL)
1541 res = pipe_ctx->plane_res.xfm->funcs->transform_get_optimal_number_of_taps(
1542 pipe_ctx->plane_res.xfm, &pipe_ctx->plane_res.scl_data, &plane_state->scaling_quality);
1543
1544 if (pipe_ctx->plane_res.dpp != NULL)
1545 res = pipe_ctx->plane_res.dpp->funcs->dpp_get_optimal_number_of_taps(
1546 pipe_ctx->plane_res.dpp, &pipe_ctx->plane_res.scl_data, &plane_state->scaling_quality);
1547
1548
1549 if (!res) {
1550 /* Try 24 bpp linebuffer */
1551 pipe_ctx->plane_res.scl_data.lb_params.depth = LB_PIXEL_DEPTH_24BPP;
1552
1553 if (pipe_ctx->plane_res.xfm != NULL)
1554 res = pipe_ctx->plane_res.xfm->funcs->transform_get_optimal_number_of_taps(
1555 pipe_ctx->plane_res.xfm,
1556 &pipe_ctx->plane_res.scl_data,
1557 &plane_state->scaling_quality);
1558
1559 if (pipe_ctx->plane_res.dpp != NULL)
1560 res = pipe_ctx->plane_res.dpp->funcs->dpp_get_optimal_number_of_taps(
1561 pipe_ctx->plane_res.dpp,
1562 &pipe_ctx->plane_res.scl_data,
1563 &plane_state->scaling_quality);
1564 }
1565
1566 /*
1567 * Depends on recout, scaling ratios, h_active and taps
1568 * May need to re-check lb size after this in some obscure scenario
1569 */
1570 if (res)
1571 calculate_inits_and_viewports(pipe_ctx);
1572
1573 /*
1574 * Handle side by side and top bottom 3d recout offsets after vp calculation
1575 * since 3d is special and needs to calculate vp as if there is no recout offset
1576 * This may break with rotation, good thing we aren't mixing hw rotation and 3d
1577 */
1578 if (pipe_ctx->top_pipe && pipe_ctx->top_pipe->plane_state == plane_state) {
1579 ASSERT(plane_state->rotation == ROTATION_ANGLE_0 ||
1580 (pipe_ctx->stream->view_format != VIEW_3D_FORMAT_TOP_AND_BOTTOM &&
1581 pipe_ctx->stream->view_format != VIEW_3D_FORMAT_SIDE_BY_SIDE));
1582 if (pipe_ctx->stream->view_format == VIEW_3D_FORMAT_TOP_AND_BOTTOM)
1583 pipe_ctx->plane_res.scl_data.recout.y += pipe_ctx->plane_res.scl_data.recout.height;
1584 else if (pipe_ctx->stream->view_format == VIEW_3D_FORMAT_SIDE_BY_SIDE)
1585 pipe_ctx->plane_res.scl_data.recout.x += pipe_ctx->plane_res.scl_data.recout.width;
1586 }
1587
1588 /* Clamp minimum viewport size */
1589 if (pipe_ctx->plane_res.scl_data.viewport.height < MIN_VIEWPORT_SIZE)
1590 pipe_ctx->plane_res.scl_data.viewport.height = MIN_VIEWPORT_SIZE;
1591 if (pipe_ctx->plane_res.scl_data.viewport.width < MIN_VIEWPORT_SIZE)
1592 pipe_ctx->plane_res.scl_data.viewport.width = MIN_VIEWPORT_SIZE;
1593
1594
1595 DC_LOG_SCALER("%s pipe %d:\nViewport: height:%d width:%d x:%d y:%d Recout: height:%d width:%d x:%d y:%d HACTIVE:%d VACTIVE:%d\n"
1596 "src_rect: height:%d width:%d x:%d y:%d dst_rect: height:%d width:%d x:%d y:%d clip_rect: height:%d width:%d x:%d y:%d\n",
1597 __func__,
1598 pipe_ctx->pipe_idx,
1599 pipe_ctx->plane_res.scl_data.viewport.height,
1600 pipe_ctx->plane_res.scl_data.viewport.width,
1601 pipe_ctx->plane_res.scl_data.viewport.x,
1602 pipe_ctx->plane_res.scl_data.viewport.y,
1603 pipe_ctx->plane_res.scl_data.recout.height,
1604 pipe_ctx->plane_res.scl_data.recout.width,
1605 pipe_ctx->plane_res.scl_data.recout.x,
1606 pipe_ctx->plane_res.scl_data.recout.y,
1607 pipe_ctx->plane_res.scl_data.h_active,
1608 pipe_ctx->plane_res.scl_data.v_active,
1609 plane_state->src_rect.height,
1610 plane_state->src_rect.width,
1611 plane_state->src_rect.x,
1612 plane_state->src_rect.y,
1613 plane_state->dst_rect.height,
1614 plane_state->dst_rect.width,
1615 plane_state->dst_rect.x,
1616 plane_state->dst_rect.y,
1617 plane_state->clip_rect.height,
1618 plane_state->clip_rect.width,
1619 plane_state->clip_rect.x,
1620 plane_state->clip_rect.y);
1621
1622 pipe_ctx->stream->dst.x -= timing->h_border_left;
1623 pipe_ctx->stream->dst.y -= timing->v_border_top;
1624
1625 return res;
1626}
1627
1628
1629enum dc_status resource_build_scaling_params_for_context(
1630 const struct dc *dc,
1631 struct dc_state *context)
1632{
1633 int i;
1634
1635 for (i = 0; i < MAX_PIPES; i++) {
1636 if (context->res_ctx.pipe_ctx[i].plane_state != NULL &&
1637 context->res_ctx.pipe_ctx[i].stream != NULL)
1638 if (!resource_build_scaling_params(pipe_ctx: &context->res_ctx.pipe_ctx[i]))
1639 return DC_FAIL_SCALING;
1640 }
1641
1642 return DC_OK;
1643}
1644
1645struct pipe_ctx *resource_find_free_secondary_pipe_legacy(
1646 struct resource_context *res_ctx,
1647 const struct resource_pool *pool,
1648 const struct pipe_ctx *primary_pipe)
1649{
1650 int i;
1651 struct pipe_ctx *secondary_pipe = NULL;
1652
1653 /*
1654 * We add a preferred pipe mapping to avoid the chance that
1655 * MPCCs already in use will need to be reassigned to other trees.
1656 * For example, if we went with the strict, assign backwards logic:
1657 *
1658 * (State 1)
1659 * Display A on, no surface, top pipe = 0
1660 * Display B on, no surface, top pipe = 1
1661 *
1662 * (State 2)
1663 * Display A on, no surface, top pipe = 0
1664 * Display B on, surface enable, top pipe = 1, bottom pipe = 5
1665 *
1666 * (State 3)
1667 * Display A on, surface enable, top pipe = 0, bottom pipe = 5
1668 * Display B on, surface enable, top pipe = 1, bottom pipe = 4
1669 *
1670 * The state 2->3 transition requires remapping MPCC 5 from display B
1671 * to display A.
1672 *
1673 * However, with the preferred pipe logic, state 2 would look like:
1674 *
1675 * (State 2)
1676 * Display A on, no surface, top pipe = 0
1677 * Display B on, surface enable, top pipe = 1, bottom pipe = 4
1678 *
1679 * This would then cause 2->3 to not require remapping any MPCCs.
1680 */
1681 if (primary_pipe) {
1682 int preferred_pipe_idx = (pool->pipe_count - 1) - primary_pipe->pipe_idx;
1683 if (res_ctx->pipe_ctx[preferred_pipe_idx].stream == NULL) {
1684 secondary_pipe = &res_ctx->pipe_ctx[preferred_pipe_idx];
1685 secondary_pipe->pipe_idx = preferred_pipe_idx;
1686 }
1687 }
1688
1689 /*
1690 * search backwards for the second pipe to keep pipe
1691 * assignment more consistent
1692 */
1693 if (!secondary_pipe)
1694 for (i = pool->pipe_count - 1; i >= 0; i--) {
1695 if (res_ctx->pipe_ctx[i].stream == NULL) {
1696 secondary_pipe = &res_ctx->pipe_ctx[i];
1697 secondary_pipe->pipe_idx = i;
1698 break;
1699 }
1700 }
1701
1702 return secondary_pipe;
1703}
1704
1705int resource_find_free_pipe_used_as_sec_opp_head_by_cur_otg_master(
1706 const struct resource_context *cur_res_ctx,
1707 struct resource_context *new_res_ctx,
1708 const struct pipe_ctx *cur_otg_master)
1709{
1710 const struct pipe_ctx *cur_sec_opp_head = cur_otg_master->next_odm_pipe;
1711 struct pipe_ctx *new_pipe;
1712 int free_pipe_idx = FREE_PIPE_INDEX_NOT_FOUND;
1713
1714 while (cur_sec_opp_head) {
1715 new_pipe = &new_res_ctx->pipe_ctx[cur_sec_opp_head->pipe_idx];
1716 if (resource_is_pipe_type(pipe_ctx: new_pipe, type: FREE_PIPE)) {
1717 free_pipe_idx = cur_sec_opp_head->pipe_idx;
1718 break;
1719 }
1720 cur_sec_opp_head = cur_sec_opp_head->next_odm_pipe;
1721 }
1722
1723 return free_pipe_idx;
1724}
1725
1726int resource_find_free_pipe_used_in_cur_mpc_blending_tree(
1727 const struct resource_context *cur_res_ctx,
1728 struct resource_context *new_res_ctx,
1729 const struct pipe_ctx *cur_opp_head)
1730{
1731 const struct pipe_ctx *cur_sec_dpp = cur_opp_head->bottom_pipe;
1732 struct pipe_ctx *new_pipe;
1733 int free_pipe_idx = FREE_PIPE_INDEX_NOT_FOUND;
1734
1735 while (cur_sec_dpp) {
1736 /* find a free pipe used in current opp blend tree,
1737 * this is to avoid MPO pipe switching to different opp blending
1738 * tree
1739 */
1740 new_pipe = &new_res_ctx->pipe_ctx[cur_sec_dpp->pipe_idx];
1741 if (resource_is_pipe_type(pipe_ctx: new_pipe, type: FREE_PIPE)) {
1742 free_pipe_idx = cur_sec_dpp->pipe_idx;
1743 break;
1744 }
1745 cur_sec_dpp = cur_sec_dpp->bottom_pipe;
1746 }
1747
1748 return free_pipe_idx;
1749}
1750
1751int recource_find_free_pipe_not_used_in_cur_res_ctx(
1752 const struct resource_context *cur_res_ctx,
1753 struct resource_context *new_res_ctx,
1754 const struct resource_pool *pool)
1755{
1756 int free_pipe_idx = FREE_PIPE_INDEX_NOT_FOUND;
1757 const struct pipe_ctx *new_pipe, *cur_pipe;
1758 int i;
1759
1760 for (i = 0; i < pool->pipe_count; i++) {
1761 cur_pipe = &cur_res_ctx->pipe_ctx[i];
1762 new_pipe = &new_res_ctx->pipe_ctx[i];
1763
1764 if (resource_is_pipe_type(pipe_ctx: cur_pipe, type: FREE_PIPE) &&
1765 resource_is_pipe_type(pipe_ctx: new_pipe, type: FREE_PIPE)) {
1766 free_pipe_idx = i;
1767 break;
1768 }
1769 }
1770
1771 return free_pipe_idx;
1772}
1773
1774int recource_find_free_pipe_used_as_otg_master_in_cur_res_ctx(
1775 const struct resource_context *cur_res_ctx,
1776 struct resource_context *new_res_ctx,
1777 const struct resource_pool *pool)
1778{
1779 int free_pipe_idx = FREE_PIPE_INDEX_NOT_FOUND;
1780 const struct pipe_ctx *new_pipe, *cur_pipe;
1781 int i;
1782
1783 for (i = 0; i < pool->pipe_count; i++) {
1784 cur_pipe = &cur_res_ctx->pipe_ctx[i];
1785 new_pipe = &new_res_ctx->pipe_ctx[i];
1786
1787 if (resource_is_pipe_type(pipe_ctx: cur_pipe, type: OTG_MASTER) &&
1788 resource_is_pipe_type(pipe_ctx: new_pipe, type: FREE_PIPE)) {
1789 free_pipe_idx = i;
1790 break;
1791 }
1792 }
1793
1794 return free_pipe_idx;
1795}
1796
1797int resource_find_free_pipe_used_as_cur_sec_dpp_in_mpcc_combine(
1798 const struct resource_context *cur_res_ctx,
1799 struct resource_context *new_res_ctx,
1800 const struct resource_pool *pool)
1801{
1802 int free_pipe_idx = FREE_PIPE_INDEX_NOT_FOUND;
1803 const struct pipe_ctx *new_pipe, *cur_pipe;
1804 int i;
1805
1806 for (i = 0; i < pool->pipe_count; i++) {
1807 cur_pipe = &cur_res_ctx->pipe_ctx[i];
1808 new_pipe = &new_res_ctx->pipe_ctx[i];
1809
1810 if (resource_is_pipe_type(pipe_ctx: cur_pipe, type: DPP_PIPE) &&
1811 !resource_is_pipe_type(pipe_ctx: cur_pipe, type: OPP_HEAD) &&
1812 resource_get_mpc_slice_index(dpp_pipe: cur_pipe) > 0 &&
1813 resource_is_pipe_type(pipe_ctx: new_pipe, type: FREE_PIPE)) {
1814 free_pipe_idx = i;
1815 break;
1816 }
1817 }
1818
1819 return free_pipe_idx;
1820}
1821
1822int resource_find_any_free_pipe(struct resource_context *new_res_ctx,
1823 const struct resource_pool *pool)
1824{
1825 int free_pipe_idx = FREE_PIPE_INDEX_NOT_FOUND;
1826 const struct pipe_ctx *new_pipe;
1827 int i;
1828
1829 for (i = 0; i < pool->pipe_count; i++) {
1830 new_pipe = &new_res_ctx->pipe_ctx[i];
1831
1832 if (resource_is_pipe_type(pipe_ctx: new_pipe, type: FREE_PIPE)) {
1833 free_pipe_idx = i;
1834 break;
1835 }
1836 }
1837
1838 return free_pipe_idx;
1839}
1840
1841bool resource_is_pipe_type(const struct pipe_ctx *pipe_ctx, enum pipe_type type)
1842{
1843 switch (type) {
1844 case OTG_MASTER:
1845 return !pipe_ctx->prev_odm_pipe &&
1846 !pipe_ctx->top_pipe &&
1847 pipe_ctx->stream;
1848 case OPP_HEAD:
1849 return !pipe_ctx->top_pipe && pipe_ctx->stream;
1850 case DPP_PIPE:
1851 return pipe_ctx->plane_state && pipe_ctx->stream;
1852 case FREE_PIPE:
1853 return !pipe_ctx->plane_state && !pipe_ctx->stream;
1854 default:
1855 return false;
1856 }
1857}
1858
1859struct pipe_ctx *resource_get_otg_master_for_stream(
1860 struct resource_context *res_ctx,
1861 const struct dc_stream_state *stream)
1862{
1863 int i;
1864
1865 for (i = 0; i < MAX_PIPES; i++) {
1866 if (res_ctx->pipe_ctx[i].stream == stream &&
1867 resource_is_pipe_type(pipe_ctx: &res_ctx->pipe_ctx[i], type: OTG_MASTER))
1868 return &res_ctx->pipe_ctx[i];
1869 }
1870 return NULL;
1871}
1872
1873int resource_get_opp_heads_for_otg_master(const struct pipe_ctx *otg_master,
1874 struct resource_context *res_ctx,
1875 struct pipe_ctx *opp_heads[MAX_PIPES])
1876{
1877 struct pipe_ctx *opp_head = &res_ctx->pipe_ctx[otg_master->pipe_idx];
1878 int i = 0;
1879
1880 if (!resource_is_pipe_type(pipe_ctx: otg_master, type: OTG_MASTER)) {
1881 ASSERT(0);
1882 return 0;
1883 }
1884 while (opp_head) {
1885 ASSERT(i < MAX_PIPES);
1886 opp_heads[i++] = opp_head;
1887 opp_head = opp_head->next_odm_pipe;
1888 }
1889 return i;
1890}
1891
1892int resource_get_dpp_pipes_for_opp_head(const struct pipe_ctx *opp_head,
1893 struct resource_context *res_ctx,
1894 struct pipe_ctx *dpp_pipes[MAX_PIPES])
1895{
1896 struct pipe_ctx *pipe = &res_ctx->pipe_ctx[opp_head->pipe_idx];
1897 int i = 0;
1898
1899 if (!resource_is_pipe_type(pipe_ctx: opp_head, type: OPP_HEAD)) {
1900 ASSERT(0);
1901 return 0;
1902 }
1903 while (pipe && resource_is_pipe_type(pipe_ctx: pipe, type: DPP_PIPE)) {
1904 ASSERT(i < MAX_PIPES);
1905 dpp_pipes[i++] = pipe;
1906 pipe = pipe->bottom_pipe;
1907 }
1908 return i;
1909}
1910
1911int resource_get_dpp_pipes_for_plane(const struct dc_plane_state *plane,
1912 struct resource_context *res_ctx,
1913 struct pipe_ctx *dpp_pipes[MAX_PIPES])
1914{
1915 int i = 0, j;
1916 struct pipe_ctx *pipe;
1917
1918 for (j = 0; j < MAX_PIPES; j++) {
1919 pipe = &res_ctx->pipe_ctx[j];
1920 if (pipe->plane_state == plane && pipe->prev_odm_pipe == NULL) {
1921 if (resource_is_pipe_type(pipe_ctx: pipe, type: OPP_HEAD) ||
1922 pipe->top_pipe->plane_state != plane)
1923 break;
1924 }
1925 }
1926
1927 if (j < MAX_PIPES) {
1928 if (pipe->next_odm_pipe)
1929 while (pipe) {
1930 dpp_pipes[i++] = pipe;
1931 pipe = pipe->next_odm_pipe;
1932 }
1933 else
1934 while (pipe && pipe->plane_state == plane) {
1935 dpp_pipes[i++] = pipe;
1936 pipe = pipe->bottom_pipe;
1937 }
1938 }
1939 return i;
1940}
1941
1942struct pipe_ctx *resource_get_otg_master(const struct pipe_ctx *pipe_ctx)
1943{
1944 struct pipe_ctx *otg_master = resource_get_opp_head(pipe_ctx);
1945
1946 while (otg_master->prev_odm_pipe)
1947 otg_master = otg_master->prev_odm_pipe;
1948 return otg_master;
1949}
1950
1951struct pipe_ctx *resource_get_opp_head(const struct pipe_ctx *pipe_ctx)
1952{
1953 struct pipe_ctx *opp_head = (struct pipe_ctx *) pipe_ctx;
1954
1955 ASSERT(!resource_is_pipe_type(opp_head, FREE_PIPE));
1956 while (opp_head->top_pipe)
1957 opp_head = opp_head->top_pipe;
1958 return opp_head;
1959}
1960
1961struct pipe_ctx *resource_get_primary_dpp_pipe(const struct pipe_ctx *dpp_pipe)
1962{
1963 struct pipe_ctx *pri_dpp_pipe = (struct pipe_ctx *) dpp_pipe;
1964
1965 ASSERT(resource_is_pipe_type(dpp_pipe, DPP_PIPE));
1966 while (pri_dpp_pipe->prev_odm_pipe)
1967 pri_dpp_pipe = pri_dpp_pipe->prev_odm_pipe;
1968 while (pri_dpp_pipe->top_pipe &&
1969 pri_dpp_pipe->top_pipe->plane_state == pri_dpp_pipe->plane_state)
1970 pri_dpp_pipe = pri_dpp_pipe->top_pipe;
1971 return pri_dpp_pipe;
1972}
1973
1974
1975int resource_get_mpc_slice_index(const struct pipe_ctx *pipe_ctx)
1976{
1977 struct pipe_ctx *split_pipe = pipe_ctx->top_pipe;
1978 int index = 0;
1979
1980 while (split_pipe && split_pipe->plane_state == pipe_ctx->plane_state) {
1981 index++;
1982 split_pipe = split_pipe->top_pipe;
1983 }
1984
1985 return index;
1986}
1987
1988int resource_get_mpc_slice_count(const struct pipe_ctx *pipe)
1989{
1990 int mpc_split_count = 1;
1991 const struct pipe_ctx *other_pipe = pipe->bottom_pipe;
1992
1993 while (other_pipe && other_pipe->plane_state == pipe->plane_state) {
1994 mpc_split_count++;
1995 other_pipe = other_pipe->bottom_pipe;
1996 }
1997 other_pipe = pipe->top_pipe;
1998 while (other_pipe && other_pipe->plane_state == pipe->plane_state) {
1999 mpc_split_count++;
2000 other_pipe = other_pipe->top_pipe;
2001 }
2002
2003 return mpc_split_count;
2004}
2005
2006int resource_get_odm_slice_count(const struct pipe_ctx *pipe)
2007{
2008 int odm_split_count = 1;
2009
2010 pipe = resource_get_otg_master(pipe_ctx: pipe);
2011
2012 while (pipe->next_odm_pipe) {
2013 odm_split_count++;
2014 pipe = pipe->next_odm_pipe;
2015 }
2016 return odm_split_count;
2017}
2018
2019int resource_get_odm_slice_index(const struct pipe_ctx *pipe_ctx)
2020{
2021 int index = 0;
2022
2023 pipe_ctx = resource_get_opp_head(pipe_ctx);
2024 if (!pipe_ctx)
2025 return 0;
2026
2027 while (pipe_ctx->prev_odm_pipe) {
2028 index++;
2029 pipe_ctx = pipe_ctx->prev_odm_pipe;
2030 }
2031
2032 return index;
2033}
2034
2035bool resource_is_pipe_topology_changed(const struct dc_state *state_a,
2036 const struct dc_state *state_b)
2037{
2038 int i;
2039 const struct pipe_ctx *pipe_a, *pipe_b;
2040
2041 if (state_a->stream_count != state_b->stream_count)
2042 return true;
2043
2044 for (i = 0; i < MAX_PIPES; i++) {
2045 pipe_a = &state_a->res_ctx.pipe_ctx[i];
2046 pipe_b = &state_b->res_ctx.pipe_ctx[i];
2047
2048 if (pipe_a->stream && !pipe_b->stream)
2049 return true;
2050 else if (!pipe_a->stream && pipe_b->stream)
2051 return true;
2052
2053 if (pipe_a->plane_state && !pipe_b->plane_state)
2054 return true;
2055 else if (!pipe_a->plane_state && pipe_b->plane_state)
2056 return true;
2057
2058 if (pipe_a->bottom_pipe && pipe_b->bottom_pipe) {
2059 if (pipe_a->bottom_pipe->pipe_idx != pipe_b->bottom_pipe->pipe_idx)
2060 return true;
2061 if ((pipe_a->bottom_pipe->plane_state == pipe_a->plane_state) &&
2062 (pipe_b->bottom_pipe->plane_state != pipe_b->plane_state))
2063 return true;
2064 else if ((pipe_a->bottom_pipe->plane_state != pipe_a->plane_state) &&
2065 (pipe_b->bottom_pipe->plane_state == pipe_b->plane_state))
2066 return true;
2067 } else if (pipe_a->bottom_pipe || pipe_b->bottom_pipe) {
2068 return true;
2069 }
2070
2071 if (pipe_a->next_odm_pipe && pipe_b->next_odm_pipe) {
2072 if (pipe_a->next_odm_pipe->pipe_idx != pipe_b->next_odm_pipe->pipe_idx)
2073 return true;
2074 } else if (pipe_a->next_odm_pipe || pipe_b->next_odm_pipe) {
2075 return true;
2076 }
2077 }
2078 return false;
2079}
2080
2081bool resource_is_odm_topology_changed(const struct pipe_ctx *otg_master_a,
2082 const struct pipe_ctx *otg_master_b)
2083{
2084 const struct pipe_ctx *opp_head_a = otg_master_a;
2085 const struct pipe_ctx *opp_head_b = otg_master_b;
2086
2087 if (!resource_is_pipe_type(pipe_ctx: otg_master_a, type: OTG_MASTER) ||
2088 !resource_is_pipe_type(pipe_ctx: otg_master_b, type: OTG_MASTER))
2089 return true;
2090
2091 while (opp_head_a && opp_head_b) {
2092 if (opp_head_a->stream_res.opp != opp_head_b->stream_res.opp)
2093 return true;
2094 if ((opp_head_a->next_odm_pipe && !opp_head_b->next_odm_pipe) ||
2095 (!opp_head_a->next_odm_pipe && opp_head_b->next_odm_pipe))
2096 return true;
2097 opp_head_a = opp_head_a->next_odm_pipe;
2098 opp_head_b = opp_head_b->next_odm_pipe;
2099 }
2100
2101 return false;
2102}
2103
2104/*
2105 * Sample log:
2106 * pipe topology update
2107 * ________________________
2108 * | plane0 slice0 stream0|
2109 * |DPP0----OPP0----OTG0----| <--- case 0 (OTG master pipe with plane)
2110 * | plane1 | | |
2111 * |DPP1----| | | <--- case 5 (DPP pipe not in last slice)
2112 * | plane0 slice1 | |
2113 * |DPP2----OPP2----| | <--- case 2 (OPP head pipe with plane)
2114 * | plane1 | |
2115 * |DPP3----| | <--- case 4 (DPP pipe in last slice)
2116 * | slice0 stream1|
2117 * |DPG4----OPP4----OTG4----| <--- case 1 (OTG master pipe without plane)
2118 * | slice1 | |
2119 * |DPG5----OPP5----| | <--- case 3 (OPP head pipe without plane)
2120 * |________________________|
2121 */
2122
2123static void resource_log_pipe(struct dc *dc, struct pipe_ctx *pipe,
2124 int stream_idx, int slice_idx, int plane_idx, int slice_count,
2125 bool is_primary)
2126{
2127 DC_LOGGER_INIT(dc->ctx->logger);
2128
2129 if (slice_idx == 0 && plane_idx == 0 && is_primary) {
2130 /* case 0 (OTG master pipe with plane) */
2131 DC_LOG_DC(" | plane%d slice%d stream%d|",
2132 plane_idx, slice_idx, stream_idx);
2133 DC_LOG_DC(" |DPP%d----OPP%d----OTG%d----|",
2134 pipe->plane_res.dpp->inst,
2135 pipe->stream_res.opp->inst,
2136 pipe->stream_res.tg->inst);
2137 } else if (slice_idx == 0 && plane_idx == -1) {
2138 /* case 1 (OTG master pipe without plane) */
2139 DC_LOG_DC(" | slice%d stream%d|",
2140 slice_idx, stream_idx);
2141 DC_LOG_DC(" |DPG%d----OPP%d----OTG%d----|",
2142 pipe->stream_res.opp->inst,
2143 pipe->stream_res.opp->inst,
2144 pipe->stream_res.tg->inst);
2145 } else if (slice_idx != 0 && plane_idx == 0 && is_primary) {
2146 /* case 2 (OPP head pipe with plane) */
2147 DC_LOG_DC(" | plane%d slice%d | |",
2148 plane_idx, slice_idx);
2149 DC_LOG_DC(" |DPP%d----OPP%d----| |",
2150 pipe->plane_res.dpp->inst,
2151 pipe->stream_res.opp->inst);
2152 } else if (slice_idx != 0 && plane_idx == -1) {
2153 /* case 3 (OPP head pipe without plane) */
2154 DC_LOG_DC(" | slice%d | |", slice_idx);
2155 DC_LOG_DC(" |DPG%d----OPP%d----| |",
2156 pipe->plane_res.dpp->inst,
2157 pipe->stream_res.opp->inst);
2158 } else if (slice_idx == slice_count - 1) {
2159 /* case 4 (DPP pipe in last slice) */
2160 DC_LOG_DC(" | plane%d | |", plane_idx);
2161 DC_LOG_DC(" |DPP%d----| |",
2162 pipe->plane_res.dpp->inst);
2163 } else {
2164 /* case 5 (DPP pipe not in last slice) */
2165 DC_LOG_DC(" | plane%d | | |", plane_idx);
2166 DC_LOG_DC(" |DPP%d----| | |",
2167 pipe->plane_res.dpp->inst);
2168 }
2169}
2170
2171void resource_log_pipe_topology_update(struct dc *dc, struct dc_state *state)
2172{
2173 struct pipe_ctx *otg_master;
2174 struct pipe_ctx *opp_heads[MAX_PIPES];
2175 struct pipe_ctx *dpp_pipes[MAX_PIPES];
2176
2177 int stream_idx, slice_idx, dpp_idx, plane_idx, slice_count, dpp_count;
2178 bool is_primary;
2179 DC_LOGGER_INIT(dc->ctx->logger);
2180
2181 DC_LOG_DC(" pipe topology update");
2182 DC_LOG_DC(" ________________________");
2183 for (stream_idx = 0; stream_idx < state->stream_count; stream_idx++) {
2184 otg_master = resource_get_otg_master_for_stream(
2185 res_ctx: &state->res_ctx, stream: state->streams[stream_idx]);
2186 if (!otg_master || otg_master->stream_res.tg == NULL) {
2187 DC_LOG_DC("topology update: otg_master NULL stream_idx %d!\n", stream_idx);
2188 return;
2189 }
2190 slice_count = resource_get_opp_heads_for_otg_master(otg_master,
2191 res_ctx: &state->res_ctx, opp_heads);
2192 for (slice_idx = 0; slice_idx < slice_count; slice_idx++) {
2193 plane_idx = -1;
2194 if (opp_heads[slice_idx]->plane_state) {
2195 dpp_count = resource_get_dpp_pipes_for_opp_head(
2196 opp_head: opp_heads[slice_idx],
2197 res_ctx: &state->res_ctx,
2198 dpp_pipes);
2199 for (dpp_idx = 0; dpp_idx < dpp_count; dpp_idx++) {
2200 is_primary = !dpp_pipes[dpp_idx]->top_pipe ||
2201 dpp_pipes[dpp_idx]->top_pipe->plane_state != dpp_pipes[dpp_idx]->plane_state;
2202 if (is_primary)
2203 plane_idx++;
2204 resource_log_pipe(dc, pipe: dpp_pipes[dpp_idx],
2205 stream_idx, slice_idx,
2206 plane_idx, slice_count,
2207 is_primary);
2208 }
2209 } else {
2210 resource_log_pipe(dc, pipe: opp_heads[slice_idx],
2211 stream_idx, slice_idx, plane_idx,
2212 slice_count, is_primary: true);
2213 }
2214
2215 }
2216 }
2217 DC_LOG_DC(" |________________________|\n");
2218}
2219
2220static struct pipe_ctx *get_tail_pipe(
2221 struct pipe_ctx *head_pipe)
2222{
2223 struct pipe_ctx *tail_pipe = head_pipe->bottom_pipe;
2224
2225 while (tail_pipe) {
2226 head_pipe = tail_pipe;
2227 tail_pipe = tail_pipe->bottom_pipe;
2228 }
2229
2230 return head_pipe;
2231}
2232
2233static struct pipe_ctx *get_last_opp_head(
2234 struct pipe_ctx *opp_head)
2235{
2236 ASSERT(resource_is_pipe_type(opp_head, OPP_HEAD));
2237 while (opp_head->next_odm_pipe)
2238 opp_head = opp_head->next_odm_pipe;
2239 return opp_head;
2240}
2241
2242static struct pipe_ctx *get_last_dpp_pipe_in_mpcc_combine(
2243 struct pipe_ctx *dpp_pipe)
2244{
2245 ASSERT(resource_is_pipe_type(dpp_pipe, DPP_PIPE));
2246 while (dpp_pipe->bottom_pipe &&
2247 dpp_pipe->plane_state == dpp_pipe->bottom_pipe->plane_state)
2248 dpp_pipe = dpp_pipe->bottom_pipe;
2249 return dpp_pipe;
2250}
2251
2252static bool update_pipe_params_after_odm_slice_count_change(
2253 struct pipe_ctx *otg_master,
2254 struct dc_state *context,
2255 const struct resource_pool *pool)
2256{
2257 int i;
2258 struct pipe_ctx *pipe;
2259 bool result = true;
2260
2261 for (i = 0; i < pool->pipe_count && result; i++) {
2262 pipe = &context->res_ctx.pipe_ctx[i];
2263 if (pipe->stream == otg_master->stream && pipe->plane_state)
2264 result = resource_build_scaling_params(pipe_ctx: pipe);
2265 }
2266
2267 if (pool->funcs->build_pipe_pix_clk_params)
2268 pool->funcs->build_pipe_pix_clk_params(otg_master);
2269 return result;
2270}
2271
2272static bool update_pipe_params_after_mpc_slice_count_change(
2273 const struct dc_plane_state *plane,
2274 struct dc_state *context,
2275 const struct resource_pool *pool)
2276{
2277 int i;
2278 struct pipe_ctx *pipe;
2279 bool result = true;
2280
2281 for (i = 0; i < pool->pipe_count && result; i++) {
2282 pipe = &context->res_ctx.pipe_ctx[i];
2283 if (pipe->plane_state == plane)
2284 result = resource_build_scaling_params(pipe_ctx: pipe);
2285 }
2286 return result;
2287}
2288
2289static int acquire_first_split_pipe(
2290 struct resource_context *res_ctx,
2291 const struct resource_pool *pool,
2292 struct dc_stream_state *stream)
2293{
2294 int i;
2295
2296 for (i = 0; i < pool->pipe_count; i++) {
2297 struct pipe_ctx *split_pipe = &res_ctx->pipe_ctx[i];
2298
2299 if (split_pipe->top_pipe &&
2300 split_pipe->top_pipe->plane_state == split_pipe->plane_state) {
2301 split_pipe->top_pipe->bottom_pipe = split_pipe->bottom_pipe;
2302 if (split_pipe->bottom_pipe)
2303 split_pipe->bottom_pipe->top_pipe = split_pipe->top_pipe;
2304
2305 if (split_pipe->top_pipe->plane_state)
2306 resource_build_scaling_params(pipe_ctx: split_pipe->top_pipe);
2307
2308 memset(split_pipe, 0, sizeof(*split_pipe));
2309 split_pipe->stream_res.tg = pool->timing_generators[i];
2310 split_pipe->plane_res.hubp = pool->hubps[i];
2311 split_pipe->plane_res.ipp = pool->ipps[i];
2312 split_pipe->plane_res.dpp = pool->dpps[i];
2313 split_pipe->stream_res.opp = pool->opps[i];
2314 split_pipe->plane_res.mpcc_inst = pool->dpps[i]->inst;
2315 split_pipe->pipe_idx = i;
2316
2317 split_pipe->stream = stream;
2318 return i;
2319 }
2320 }
2321 return FREE_PIPE_INDEX_NOT_FOUND;
2322}
2323
2324static void update_stream_engine_usage(
2325 struct resource_context *res_ctx,
2326 const struct resource_pool *pool,
2327 struct stream_encoder *stream_enc,
2328 bool acquired)
2329{
2330 int i;
2331
2332 for (i = 0; i < pool->stream_enc_count; i++) {
2333 if (pool->stream_enc[i] == stream_enc)
2334 res_ctx->is_stream_enc_acquired[i] = acquired;
2335 }
2336}
2337
2338static void update_hpo_dp_stream_engine_usage(
2339 struct resource_context *res_ctx,
2340 const struct resource_pool *pool,
2341 struct hpo_dp_stream_encoder *hpo_dp_stream_enc,
2342 bool acquired)
2343{
2344 int i;
2345
2346 for (i = 0; i < pool->hpo_dp_stream_enc_count; i++) {
2347 if (pool->hpo_dp_stream_enc[i] == hpo_dp_stream_enc)
2348 res_ctx->is_hpo_dp_stream_enc_acquired[i] = acquired;
2349 }
2350}
2351
2352static inline int find_acquired_hpo_dp_link_enc_for_link(
2353 const struct resource_context *res_ctx,
2354 const struct dc_link *link)
2355{
2356 int i;
2357
2358 for (i = 0; i < ARRAY_SIZE(res_ctx->hpo_dp_link_enc_to_link_idx); i++)
2359 if (res_ctx->hpo_dp_link_enc_ref_cnts[i] > 0 &&
2360 res_ctx->hpo_dp_link_enc_to_link_idx[i] == link->link_index)
2361 return i;
2362
2363 return -1;
2364}
2365
2366static inline int find_free_hpo_dp_link_enc(const struct resource_context *res_ctx,
2367 const struct resource_pool *pool)
2368{
2369 int i;
2370
2371 for (i = 0; i < ARRAY_SIZE(res_ctx->hpo_dp_link_enc_ref_cnts); i++)
2372 if (res_ctx->hpo_dp_link_enc_ref_cnts[i] == 0)
2373 break;
2374
2375 return (i < ARRAY_SIZE(res_ctx->hpo_dp_link_enc_ref_cnts) &&
2376 i < pool->hpo_dp_link_enc_count) ? i : -1;
2377}
2378
2379static inline void acquire_hpo_dp_link_enc(
2380 struct resource_context *res_ctx,
2381 unsigned int link_index,
2382 int enc_index)
2383{
2384 res_ctx->hpo_dp_link_enc_to_link_idx[enc_index] = link_index;
2385 res_ctx->hpo_dp_link_enc_ref_cnts[enc_index] = 1;
2386}
2387
2388static inline void retain_hpo_dp_link_enc(
2389 struct resource_context *res_ctx,
2390 int enc_index)
2391{
2392 res_ctx->hpo_dp_link_enc_ref_cnts[enc_index]++;
2393}
2394
2395static inline void release_hpo_dp_link_enc(
2396 struct resource_context *res_ctx,
2397 int enc_index)
2398{
2399 ASSERT(res_ctx->hpo_dp_link_enc_ref_cnts[enc_index] > 0);
2400 res_ctx->hpo_dp_link_enc_ref_cnts[enc_index]--;
2401}
2402
2403static bool add_hpo_dp_link_enc_to_ctx(struct resource_context *res_ctx,
2404 const struct resource_pool *pool,
2405 struct pipe_ctx *pipe_ctx,
2406 struct dc_stream_state *stream)
2407{
2408 int enc_index;
2409
2410 enc_index = find_acquired_hpo_dp_link_enc_for_link(res_ctx, link: stream->link);
2411
2412 if (enc_index >= 0) {
2413 retain_hpo_dp_link_enc(res_ctx, enc_index);
2414 } else {
2415 enc_index = find_free_hpo_dp_link_enc(res_ctx, pool);
2416 if (enc_index >= 0)
2417 acquire_hpo_dp_link_enc(res_ctx, link_index: stream->link->link_index, enc_index);
2418 }
2419
2420 if (enc_index >= 0)
2421 pipe_ctx->link_res.hpo_dp_link_enc = pool->hpo_dp_link_enc[enc_index];
2422
2423 return pipe_ctx->link_res.hpo_dp_link_enc != NULL;
2424}
2425
2426static void remove_hpo_dp_link_enc_from_ctx(struct resource_context *res_ctx,
2427 struct pipe_ctx *pipe_ctx,
2428 struct dc_stream_state *stream)
2429{
2430 int enc_index;
2431
2432 enc_index = find_acquired_hpo_dp_link_enc_for_link(res_ctx, link: stream->link);
2433
2434 if (enc_index >= 0) {
2435 release_hpo_dp_link_enc(res_ctx, enc_index);
2436 pipe_ctx->link_res.hpo_dp_link_enc = NULL;
2437 }
2438}
2439
2440enum dc_status resource_add_otg_master_for_stream_output(struct dc_state *new_ctx,
2441 const struct resource_pool *pool,
2442 struct dc_stream_state *stream)
2443{
2444 struct dc *dc = stream->ctx->dc;
2445
2446 return dc->res_pool->funcs->add_stream_to_ctx(dc, new_ctx, stream);
2447}
2448
2449void resource_remove_otg_master_for_stream_output(struct dc_state *context,
2450 const struct resource_pool *pool,
2451 struct dc_stream_state *stream)
2452{
2453 struct pipe_ctx *otg_master = resource_get_otg_master_for_stream(
2454 res_ctx: &context->res_ctx, stream);
2455
2456 if (!otg_master)
2457 return;
2458
2459 ASSERT(resource_get_odm_slice_count(otg_master) == 1);
2460 ASSERT(otg_master->plane_state == NULL);
2461 ASSERT(otg_master->stream_res.stream_enc);
2462 update_stream_engine_usage(
2463 res_ctx: &context->res_ctx,
2464 pool,
2465 stream_enc: otg_master->stream_res.stream_enc,
2466 acquired: false);
2467
2468 if (stream->ctx->dc->link_srv->dp_is_128b_132b_signal(otg_master)) {
2469 update_hpo_dp_stream_engine_usage(
2470 res_ctx: &context->res_ctx, pool,
2471 hpo_dp_stream_enc: otg_master->stream_res.hpo_dp_stream_enc,
2472 acquired: false);
2473 remove_hpo_dp_link_enc_from_ctx(
2474 res_ctx: &context->res_ctx, pipe_ctx: otg_master, stream);
2475 }
2476 if (otg_master->stream_res.audio)
2477 update_audio_usage(
2478 res_ctx: &context->res_ctx,
2479 pool,
2480 audio: otg_master->stream_res.audio,
2481 acquired: false);
2482
2483 resource_unreference_clock_source(res_ctx: &context->res_ctx,
2484 pool,
2485 clock_source: otg_master->clock_source);
2486
2487 if (pool->funcs->remove_stream_from_ctx)
2488 pool->funcs->remove_stream_from_ctx(
2489 stream->ctx->dc, context, stream);
2490 memset(otg_master, 0, sizeof(*otg_master));
2491}
2492
2493/* For each OPP head of an OTG master, add top plane at plane index 0.
2494 *
2495 * In the following example, the stream has 2 ODM slices without a top plane.
2496 * By adding a plane 0 to OPP heads, we are configuring our hardware to render
2497 * plane 0 by using each OPP head's DPP.
2498 *
2499 * Inter-pipe Relation (Before Adding Plane)
2500 * __________________________________________________
2501 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER |
2502 * | | | slice 0 | |
2503 * | 0 | |blank ----ODM----------- |
2504 * | | | slice 1 | | |
2505 * | 1 | |blank ---- | |
2506 * |________|_______________|___________|_____________|
2507 *
2508 * Inter-pipe Relation (After Adding Plane)
2509 * __________________________________________________
2510 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER |
2511 * | | plane 0 | slice 0 | |
2512 * | 0 | -------------------------ODM----------- |
2513 * | | plane 0 | slice 1 | | |
2514 * | 1 | ------------------------- | |
2515 * |________|_______________|___________|_____________|
2516 */
2517static bool add_plane_to_opp_head_pipes(struct pipe_ctx *otg_master_pipe,
2518 struct dc_plane_state *plane_state,
2519 struct dc_state *context)
2520{
2521 struct pipe_ctx *opp_head_pipe = otg_master_pipe;
2522
2523 while (opp_head_pipe) {
2524 if (opp_head_pipe->plane_state) {
2525 ASSERT(0);
2526 return false;
2527 }
2528 opp_head_pipe->plane_state = plane_state;
2529 opp_head_pipe = opp_head_pipe->next_odm_pipe;
2530 }
2531
2532 return true;
2533}
2534
2535/* For each OPP head of an OTG master, acquire a secondary DPP pipe and add
2536 * the plane. So the plane is added to all ODM slices associated with the OTG
2537 * master pipe in the bottom layer.
2538 *
2539 * In the following example, the stream has 2 ODM slices and a top plane 0.
2540 * By acquiring secondary DPP pipes and adding a plane 1, we are configuring our
2541 * hardware to render the plane 1 by acquiring a new pipe for each ODM slice and
2542 * render plane 1 using new pipes' DPP in the Z axis below plane 0.
2543 *
2544 * Inter-pipe Relation (Before Adding Plane)
2545 * __________________________________________________
2546 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER |
2547 * | | plane 0 | slice 0 | |
2548 * | 0 | -------------------------ODM----------- |
2549 * | | plane 0 | slice 1 | | |
2550 * | 1 | ------------------------- | |
2551 * |________|_______________|___________|_____________|
2552 *
2553 * Inter-pipe Relation (After Acquiring and Adding Plane)
2554 * __________________________________________________
2555 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER |
2556 * | | plane 0 | slice 0 | |
2557 * | 0 | -------------MPC---------ODM----------- |
2558 * | | plane 1 | | | | |
2559 * | 2 | ------------- | | | |
2560 * | | plane 0 | slice 1 | | |
2561 * | 1 | -------------MPC--------- | |
2562 * | | plane 1 | | | |
2563 * | 3 | ------------- | | |
2564 * |________|_______________|___________|_____________|
2565 */
2566static bool acquire_secondary_dpp_pipes_and_add_plane(
2567 struct pipe_ctx *otg_master_pipe,
2568 struct dc_plane_state *plane_state,
2569 struct dc_state *new_ctx,
2570 struct dc_state *cur_ctx,
2571 struct resource_pool *pool)
2572{
2573 struct pipe_ctx *opp_head_pipe, *sec_pipe, *tail_pipe;
2574
2575 if (!pool->funcs->acquire_free_pipe_as_secondary_dpp_pipe) {
2576 ASSERT(0);
2577 return false;
2578 }
2579
2580 opp_head_pipe = otg_master_pipe;
2581 while (opp_head_pipe) {
2582 sec_pipe = pool->funcs->acquire_free_pipe_as_secondary_dpp_pipe(
2583 cur_ctx,
2584 new_ctx,
2585 pool,
2586 opp_head_pipe);
2587 if (!sec_pipe) {
2588 /* try tearing down MPCC combine */
2589 int pipe_idx = acquire_first_split_pipe(
2590 res_ctx: &new_ctx->res_ctx, pool,
2591 stream: otg_master_pipe->stream);
2592
2593 if (pipe_idx >= 0)
2594 sec_pipe = &new_ctx->res_ctx.pipe_ctx[pipe_idx];
2595 }
2596
2597 if (!sec_pipe)
2598 return false;
2599
2600 sec_pipe->plane_state = plane_state;
2601
2602 /* establish pipe relationship */
2603 tail_pipe = get_tail_pipe(head_pipe: opp_head_pipe);
2604 tail_pipe->bottom_pipe = sec_pipe;
2605 sec_pipe->top_pipe = tail_pipe;
2606 sec_pipe->bottom_pipe = NULL;
2607 if (tail_pipe->prev_odm_pipe) {
2608 ASSERT(tail_pipe->prev_odm_pipe->bottom_pipe);
2609 sec_pipe->prev_odm_pipe = tail_pipe->prev_odm_pipe->bottom_pipe;
2610 tail_pipe->prev_odm_pipe->bottom_pipe->next_odm_pipe = sec_pipe;
2611 } else {
2612 sec_pipe->prev_odm_pipe = NULL;
2613 }
2614
2615 opp_head_pipe = opp_head_pipe->next_odm_pipe;
2616 }
2617 return true;
2618}
2619
2620bool resource_append_dpp_pipes_for_plane_composition(
2621 struct dc_state *new_ctx,
2622 struct dc_state *cur_ctx,
2623 struct resource_pool *pool,
2624 struct pipe_ctx *otg_master_pipe,
2625 struct dc_plane_state *plane_state)
2626{
2627 if (otg_master_pipe->plane_state == NULL)
2628 return add_plane_to_opp_head_pipes(otg_master_pipe,
2629 plane_state, context: new_ctx);
2630 else
2631 return acquire_secondary_dpp_pipes_and_add_plane(
2632 otg_master_pipe, plane_state, new_ctx,
2633 cur_ctx, pool);
2634}
2635
2636void resource_remove_dpp_pipes_for_plane_composition(
2637 struct dc_state *context,
2638 const struct resource_pool *pool,
2639 const struct dc_plane_state *plane_state)
2640{
2641 int i;
2642 for (i = pool->pipe_count - 1; i >= 0; i--) {
2643 struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
2644
2645 if (pipe_ctx->plane_state == plane_state) {
2646 if (pipe_ctx->top_pipe)
2647 pipe_ctx->top_pipe->bottom_pipe = pipe_ctx->bottom_pipe;
2648
2649 /* Second condition is to avoid setting NULL to top pipe
2650 * of tail pipe making it look like head pipe in subsequent
2651 * deletes
2652 */
2653 if (pipe_ctx->bottom_pipe && pipe_ctx->top_pipe)
2654 pipe_ctx->bottom_pipe->top_pipe = pipe_ctx->top_pipe;
2655
2656 /*
2657 * For head pipe detach surfaces from pipe for tail
2658 * pipe just zero it out
2659 */
2660 if (!pipe_ctx->top_pipe)
2661 pipe_ctx->plane_state = NULL;
2662 else
2663 memset(pipe_ctx, 0, sizeof(*pipe_ctx));
2664 }
2665 }
2666}
2667
2668/*
2669 * Increase ODM slice count by 1 by acquiring pipes and adding a new ODM slice
2670 * at the last index.
2671 * return - true if a new ODM slice is added and required pipes are acquired.
2672 * false if new_ctx is no longer a valid state after new ODM slice is added.
2673 *
2674 * This is achieved by duplicating MPC blending tree from previous ODM slice.
2675 * In the following example, we have a single MPC tree and 1 ODM slice 0. We
2676 * want to add a new odm slice by duplicating the MPC blending tree and add
2677 * ODM slice 1.
2678 *
2679 * Inter-pipe Relation (Before Acquiring and Adding ODM Slice)
2680 * __________________________________________________
2681 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER |
2682 * | | plane 0 | slice 0 | |
2683 * | 0 | -------------MPC---------ODM----------- |
2684 * | | plane 1 | | | |
2685 * | 1 | ------------- | | |
2686 * |________|_______________|___________|_____________|
2687 *
2688 * Inter-pipe Relation (After Acquiring and Adding ODM Slice)
2689 * __________________________________________________
2690 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER |
2691 * | | plane 0 | slice 0 | |
2692 * | 0 | -------------MPC---------ODM----------- |
2693 * | | plane 1 | | | | |
2694 * | 1 | ------------- | | | |
2695 * | | plane 0 | slice 1 | | |
2696 * | 2 | -------------MPC--------- | |
2697 * | | plane 1 | | | |
2698 * | 3 | ------------- | | |
2699 * |________|_______________|___________|_____________|
2700 */
2701static bool acquire_pipes_and_add_odm_slice(
2702 struct pipe_ctx *otg_master_pipe,
2703 struct dc_state *new_ctx,
2704 const struct dc_state *cur_ctx,
2705 const struct resource_pool *pool)
2706{
2707 struct pipe_ctx *last_opp_head = get_last_opp_head(opp_head: otg_master_pipe);
2708 struct pipe_ctx *new_opp_head;
2709 struct pipe_ctx *last_top_dpp_pipe, *last_bottom_dpp_pipe,
2710 *new_top_dpp_pipe, *new_bottom_dpp_pipe;
2711
2712 if (!pool->funcs->acquire_free_pipe_as_secondary_opp_head) {
2713 ASSERT(0);
2714 return false;
2715 }
2716 new_opp_head = pool->funcs->acquire_free_pipe_as_secondary_opp_head(
2717 cur_ctx, new_ctx, pool,
2718 otg_master_pipe);
2719 if (!new_opp_head)
2720 return false;
2721
2722 last_opp_head->next_odm_pipe = new_opp_head;
2723 new_opp_head->prev_odm_pipe = last_opp_head;
2724 new_opp_head->next_odm_pipe = NULL;
2725 new_opp_head->plane_state = last_opp_head->plane_state;
2726 last_top_dpp_pipe = last_opp_head;
2727 new_top_dpp_pipe = new_opp_head;
2728
2729 while (last_top_dpp_pipe->bottom_pipe) {
2730 last_bottom_dpp_pipe = last_top_dpp_pipe->bottom_pipe;
2731 new_bottom_dpp_pipe = pool->funcs->acquire_free_pipe_as_secondary_dpp_pipe(
2732 cur_ctx, new_ctx, pool,
2733 new_opp_head);
2734 if (!new_bottom_dpp_pipe)
2735 return false;
2736
2737 new_bottom_dpp_pipe->plane_state = last_bottom_dpp_pipe->plane_state;
2738 new_top_dpp_pipe->bottom_pipe = new_bottom_dpp_pipe;
2739 new_bottom_dpp_pipe->top_pipe = new_top_dpp_pipe;
2740 last_bottom_dpp_pipe->next_odm_pipe = new_bottom_dpp_pipe;
2741 new_bottom_dpp_pipe->prev_odm_pipe = last_bottom_dpp_pipe;
2742 new_bottom_dpp_pipe->next_odm_pipe = NULL;
2743 last_top_dpp_pipe = last_bottom_dpp_pipe;
2744 }
2745
2746 return true;
2747}
2748
2749/*
2750 * Decrease ODM slice count by 1 by releasing pipes and removing the ODM slice
2751 * at the last index.
2752 * return - true if the last ODM slice is removed and related pipes are
2753 * released. false if there is no removable ODM slice.
2754 *
2755 * In the following example, we have 2 MPC trees and ODM slice 0 and slice 1.
2756 * We want to remove the last ODM i.e slice 1. We are releasing secondary DPP
2757 * pipe 3 and OPP head pipe 2.
2758 *
2759 * Inter-pipe Relation (Before Releasing and Removing ODM Slice)
2760 * __________________________________________________
2761 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER |
2762 * | | plane 0 | slice 0 | |
2763 * | 0 | -------------MPC---------ODM----------- |
2764 * | | plane 1 | | | | |
2765 * | 1 | ------------- | | | |
2766 * | | plane 0 | slice 1 | | |
2767 * | 2 | -------------MPC--------- | |
2768 * | | plane 1 | | | |
2769 * | 3 | ------------- | | |
2770 * |________|_______________|___________|_____________|
2771 *
2772 * Inter-pipe Relation (After Releasing and Removing ODM Slice)
2773 * __________________________________________________
2774 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER |
2775 * | | plane 0 | slice 0 | |
2776 * | 0 | -------------MPC---------ODM----------- |
2777 * | | plane 1 | | | |
2778 * | 1 | ------------- | | |
2779 * |________|_______________|___________|_____________|
2780 */
2781static bool release_pipes_and_remove_odm_slice(
2782 struct pipe_ctx *otg_master_pipe,
2783 struct dc_state *context,
2784 const struct resource_pool *pool)
2785{
2786 struct pipe_ctx *last_opp_head = get_last_opp_head(opp_head: otg_master_pipe);
2787 struct pipe_ctx *tail_pipe = get_tail_pipe(head_pipe: last_opp_head);
2788
2789 if (!pool->funcs->release_pipe) {
2790 ASSERT(0);
2791 return false;
2792 }
2793
2794 if (resource_is_pipe_type(pipe_ctx: last_opp_head, type: OTG_MASTER))
2795 return false;
2796
2797 while (tail_pipe->top_pipe) {
2798 tail_pipe->prev_odm_pipe->next_odm_pipe = NULL;
2799 tail_pipe = tail_pipe->top_pipe;
2800 pool->funcs->release_pipe(context, tail_pipe->bottom_pipe, pool);
2801 tail_pipe->bottom_pipe = NULL;
2802 }
2803 last_opp_head->prev_odm_pipe->next_odm_pipe = NULL;
2804 pool->funcs->release_pipe(context, last_opp_head, pool);
2805
2806 return true;
2807}
2808
2809/*
2810 * Increase MPC slice count by 1 by acquiring a new DPP pipe and add it as the
2811 * last MPC slice of the plane associated with dpp_pipe.
2812 *
2813 * return - true if a new MPC slice is added and required pipes are acquired.
2814 * false if new_ctx is no longer a valid state after new MPC slice is added.
2815 *
2816 * In the following example, we add a new MPC slice for plane 0 into the
2817 * new_ctx. To do so we pass pipe 0 as dpp_pipe. The function acquires a new DPP
2818 * pipe 2 for plane 0 as the bottom most pipe for plane 0.
2819 *
2820 * Inter-pipe Relation (Before Acquiring and Adding MPC Slice)
2821 * __________________________________________________
2822 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER |
2823 * | | plane 0 | | |
2824 * | 0 | -------------MPC----------------------- |
2825 * | | plane 1 | | | |
2826 * | 1 | ------------- | | |
2827 * |________|_______________|___________|_____________|
2828 *
2829 * Inter-pipe Relation (After Acquiring and Adding MPC Slice)
2830 * __________________________________________________
2831 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER |
2832 * | | plane 0 | | |
2833 * | 0 | -------------MPC----------------------- |
2834 * | | plane 0 | | | |
2835 * | 2 | ------------- | | |
2836 * | | plane 1 | | | |
2837 * | 1 | ------------- | | |
2838 * |________|_______________|___________|_____________|
2839 */
2840static bool acquire_dpp_pipe_and_add_mpc_slice(
2841 struct pipe_ctx *dpp_pipe,
2842 struct dc_state *new_ctx,
2843 const struct dc_state *cur_ctx,
2844 const struct resource_pool *pool)
2845{
2846 struct pipe_ctx *last_dpp_pipe =
2847 get_last_dpp_pipe_in_mpcc_combine(dpp_pipe);
2848 struct pipe_ctx *opp_head = resource_get_opp_head(pipe_ctx: dpp_pipe);
2849 struct pipe_ctx *new_dpp_pipe;
2850
2851 if (!pool->funcs->acquire_free_pipe_as_secondary_dpp_pipe) {
2852 ASSERT(0);
2853 return false;
2854 }
2855 new_dpp_pipe = pool->funcs->acquire_free_pipe_as_secondary_dpp_pipe(
2856 cur_ctx, new_ctx, pool, opp_head);
2857 if (!new_dpp_pipe || resource_get_odm_slice_count(pipe: dpp_pipe) > 1)
2858 return false;
2859
2860 new_dpp_pipe->bottom_pipe = last_dpp_pipe->bottom_pipe;
2861 if (new_dpp_pipe->bottom_pipe)
2862 new_dpp_pipe->bottom_pipe->top_pipe = new_dpp_pipe;
2863 new_dpp_pipe->top_pipe = last_dpp_pipe;
2864 last_dpp_pipe->bottom_pipe = new_dpp_pipe;
2865 new_dpp_pipe->plane_state = last_dpp_pipe->plane_state;
2866
2867 return true;
2868}
2869
2870/*
2871 * Reduce MPC slice count by 1 by releasing the bottom DPP pipe in MPCC combine
2872 * with dpp_pipe and removing last MPC slice of the plane associated with
2873 * dpp_pipe.
2874 *
2875 * return - true if the last MPC slice of the plane associated with dpp_pipe is
2876 * removed and last DPP pipe in MPCC combine with dpp_pipe is released.
2877 * false if there is no removable MPC slice.
2878 *
2879 * In the following example, we remove an MPC slice for plane 0 from the
2880 * context. To do so we pass pipe 0 as dpp_pipe. The function releases pipe 1 as
2881 * it is the last pipe for plane 0.
2882 *
2883 * Inter-pipe Relation (Before Releasing and Removing MPC Slice)
2884 * __________________________________________________
2885 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER |
2886 * | | plane 0 | | |
2887 * | 0 | -------------MPC----------------------- |
2888 * | | plane 0 | | | |
2889 * | 1 | ------------- | | |
2890 * | | plane 1 | | | |
2891 * | 2 | ------------- | | |
2892 * |________|_______________|___________|_____________|
2893 *
2894 * Inter-pipe Relation (After Releasing and Removing MPC Slice)
2895 * __________________________________________________
2896 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER |
2897 * | | plane 0 | | |
2898 * | 0 | -------------MPC----------------------- |
2899 * | | plane 1 | | | |
2900 * | 2 | ------------- | | |
2901 * |________|_______________|___________|_____________|
2902 */
2903static bool release_dpp_pipe_and_remove_mpc_slice(
2904 struct pipe_ctx *dpp_pipe,
2905 struct dc_state *context,
2906 const struct resource_pool *pool)
2907{
2908 struct pipe_ctx *last_dpp_pipe =
2909 get_last_dpp_pipe_in_mpcc_combine(dpp_pipe);
2910
2911 if (!pool->funcs->release_pipe) {
2912 ASSERT(0);
2913 return false;
2914 }
2915
2916 if (resource_is_pipe_type(pipe_ctx: last_dpp_pipe, type: OPP_HEAD) ||
2917 resource_get_odm_slice_count(pipe: dpp_pipe) > 1)
2918 return false;
2919
2920 last_dpp_pipe->top_pipe->bottom_pipe = last_dpp_pipe->bottom_pipe;
2921 if (last_dpp_pipe->bottom_pipe)
2922 last_dpp_pipe->bottom_pipe->top_pipe = last_dpp_pipe->top_pipe;
2923 pool->funcs->release_pipe(context, last_dpp_pipe, pool);
2924
2925 return true;
2926}
2927
2928bool resource_update_pipes_for_stream_with_slice_count(
2929 struct dc_state *new_ctx,
2930 const struct dc_state *cur_ctx,
2931 const struct resource_pool *pool,
2932 const struct dc_stream_state *stream,
2933 int new_slice_count)
2934{
2935 int i;
2936 struct pipe_ctx *otg_master = resource_get_otg_master_for_stream(
2937 res_ctx: &new_ctx->res_ctx, stream);
2938 int cur_slice_count = resource_get_odm_slice_count(pipe: otg_master);
2939 bool result = true;
2940
2941 if (new_slice_count == cur_slice_count)
2942 return result;
2943
2944 if (new_slice_count > cur_slice_count)
2945 for (i = 0; i < new_slice_count - cur_slice_count && result; i++)
2946 result = acquire_pipes_and_add_odm_slice(
2947 otg_master_pipe: otg_master, new_ctx, cur_ctx, pool);
2948 else
2949 for (i = 0; i < cur_slice_count - new_slice_count && result; i++)
2950 result = release_pipes_and_remove_odm_slice(
2951 otg_master_pipe: otg_master, context: new_ctx, pool);
2952 if (result)
2953 result = update_pipe_params_after_odm_slice_count_change(
2954 otg_master, context: new_ctx, pool);
2955 return result;
2956}
2957
2958bool resource_update_pipes_for_plane_with_slice_count(
2959 struct dc_state *new_ctx,
2960 const struct dc_state *cur_ctx,
2961 const struct resource_pool *pool,
2962 const struct dc_plane_state *plane,
2963 int new_slice_count)
2964{
2965 int i;
2966 int dpp_pipe_count;
2967 int cur_slice_count;
2968 struct pipe_ctx *dpp_pipes[MAX_PIPES];
2969 bool result = true;
2970
2971 dpp_pipe_count = resource_get_dpp_pipes_for_plane(plane,
2972 res_ctx: &new_ctx->res_ctx, dpp_pipes);
2973 ASSERT(dpp_pipe_count > 0);
2974 cur_slice_count = resource_get_mpc_slice_count(pipe: dpp_pipes[0]);
2975
2976 if (new_slice_count == cur_slice_count)
2977 return result;
2978
2979 if (new_slice_count > cur_slice_count)
2980 for (i = 0; i < new_slice_count - cur_slice_count && result; i++)
2981 result = acquire_dpp_pipe_and_add_mpc_slice(
2982 dpp_pipe: dpp_pipes[0], new_ctx, cur_ctx, pool);
2983 else
2984 for (i = 0; i < cur_slice_count - new_slice_count && result; i++)
2985 result = release_dpp_pipe_and_remove_mpc_slice(
2986 dpp_pipe: dpp_pipes[0], context: new_ctx, pool);
2987 if (result)
2988 result = update_pipe_params_after_mpc_slice_count_change(
2989 plane: dpp_pipes[0]->plane_state, context: new_ctx, pool);
2990 return result;
2991}
2992
2993bool dc_is_timing_changed(struct dc_stream_state *cur_stream,
2994 struct dc_stream_state *new_stream)
2995{
2996 if (cur_stream == NULL)
2997 return true;
2998
2999 /* If output color space is changed, need to reprogram info frames */
3000 if (cur_stream->output_color_space != new_stream->output_color_space)
3001 return true;
3002
3003 return memcmp(
3004 p: &cur_stream->timing,
3005 q: &new_stream->timing,
3006 size: sizeof(struct dc_crtc_timing)) != 0;
3007}
3008
3009static bool are_stream_backends_same(
3010 struct dc_stream_state *stream_a, struct dc_stream_state *stream_b)
3011{
3012 if (stream_a == stream_b)
3013 return true;
3014
3015 if (stream_a == NULL || stream_b == NULL)
3016 return false;
3017
3018 if (dc_is_timing_changed(cur_stream: stream_a, new_stream: stream_b))
3019 return false;
3020
3021 if (stream_a->signal != stream_b->signal)
3022 return false;
3023
3024 if (stream_a->dpms_off != stream_b->dpms_off)
3025 return false;
3026
3027 return true;
3028}
3029
3030/*
3031 * dc_is_stream_unchanged() - Compare two stream states for equivalence.
3032 *
3033 * Checks if there a difference between the two states
3034 * that would require a mode change.
3035 *
3036 * Does not compare cursor position or attributes.
3037 */
3038bool dc_is_stream_unchanged(
3039 struct dc_stream_state *old_stream, struct dc_stream_state *stream)
3040{
3041
3042 if (!are_stream_backends_same(stream_a: old_stream, stream_b: stream))
3043 return false;
3044
3045 if (old_stream->ignore_msa_timing_param != stream->ignore_msa_timing_param)
3046 return false;
3047
3048 /*compare audio info*/
3049 if (memcmp(p: &old_stream->audio_info, q: &stream->audio_info, size: sizeof(stream->audio_info)) != 0)
3050 return false;
3051
3052 return true;
3053}
3054
3055/*
3056 * dc_is_stream_scaling_unchanged() - Compare scaling rectangles of two streams.
3057 */
3058bool dc_is_stream_scaling_unchanged(struct dc_stream_state *old_stream,
3059 struct dc_stream_state *stream)
3060{
3061 if (old_stream == stream)
3062 return true;
3063
3064 if (old_stream == NULL || stream == NULL)
3065 return false;
3066
3067 if (memcmp(p: &old_stream->src,
3068 q: &stream->src,
3069 size: sizeof(struct rect)) != 0)
3070 return false;
3071
3072 if (memcmp(p: &old_stream->dst,
3073 q: &stream->dst,
3074 size: sizeof(struct rect)) != 0)
3075 return false;
3076
3077 return true;
3078}
3079
3080/* TODO: release audio object */
3081void update_audio_usage(
3082 struct resource_context *res_ctx,
3083 const struct resource_pool *pool,
3084 struct audio *audio,
3085 bool acquired)
3086{
3087 int i;
3088 for (i = 0; i < pool->audio_count; i++) {
3089 if (pool->audios[i] == audio)
3090 res_ctx->is_audio_acquired[i] = acquired;
3091 }
3092}
3093
3094static struct hpo_dp_stream_encoder *find_first_free_match_hpo_dp_stream_enc_for_link(
3095 struct resource_context *res_ctx,
3096 const struct resource_pool *pool,
3097 struct dc_stream_state *stream)
3098{
3099 int i;
3100
3101 for (i = 0; i < pool->hpo_dp_stream_enc_count; i++) {
3102 if (!res_ctx->is_hpo_dp_stream_enc_acquired[i] &&
3103 pool->hpo_dp_stream_enc[i]) {
3104
3105 return pool->hpo_dp_stream_enc[i];
3106 }
3107 }
3108
3109 return NULL;
3110}
3111
3112static struct audio *find_first_free_audio(
3113 struct resource_context *res_ctx,
3114 const struct resource_pool *pool,
3115 enum engine_id id,
3116 enum dce_version dc_version)
3117{
3118 int i, available_audio_count;
3119
3120 available_audio_count = pool->audio_count;
3121
3122 for (i = 0; i < available_audio_count; i++) {
3123 if ((res_ctx->is_audio_acquired[i] == false) && (res_ctx->is_stream_enc_acquired[i] == true)) {
3124 /*we have enough audio endpoint, find the matching inst*/
3125 if (id != i)
3126 continue;
3127 return pool->audios[i];
3128 }
3129 }
3130
3131 /* use engine id to find free audio */
3132 if ((id < available_audio_count) && (res_ctx->is_audio_acquired[id] == false)) {
3133 return pool->audios[id];
3134 }
3135 /*not found the matching one, first come first serve*/
3136 for (i = 0; i < available_audio_count; i++) {
3137 if (res_ctx->is_audio_acquired[i] == false) {
3138 return pool->audios[i];
3139 }
3140 }
3141 return NULL;
3142}
3143
3144static struct dc_stream_state *find_pll_sharable_stream(
3145 struct dc_stream_state *stream_needs_pll,
3146 struct dc_state *context)
3147{
3148 int i;
3149
3150 for (i = 0; i < context->stream_count; i++) {
3151 struct dc_stream_state *stream_has_pll = context->streams[i];
3152
3153 /* We are looking for non dp, non virtual stream */
3154 if (resource_are_streams_timing_synchronizable(
3155 stream1: stream_needs_pll, stream2: stream_has_pll)
3156 && !dc_is_dp_signal(signal: stream_has_pll->signal)
3157 && stream_has_pll->link->connector_signal
3158 != SIGNAL_TYPE_VIRTUAL)
3159 return stream_has_pll;
3160
3161 }
3162
3163 return NULL;
3164}
3165
3166static int get_norm_pix_clk(const struct dc_crtc_timing *timing)
3167{
3168 uint32_t pix_clk = timing->pix_clk_100hz;
3169 uint32_t normalized_pix_clk = pix_clk;
3170
3171 if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420)
3172 pix_clk /= 2;
3173 if (timing->pixel_encoding != PIXEL_ENCODING_YCBCR422) {
3174 switch (timing->display_color_depth) {
3175 case COLOR_DEPTH_666:
3176 case COLOR_DEPTH_888:
3177 normalized_pix_clk = pix_clk;
3178 break;
3179 case COLOR_DEPTH_101010:
3180 normalized_pix_clk = (pix_clk * 30) / 24;
3181 break;
3182 case COLOR_DEPTH_121212:
3183 normalized_pix_clk = (pix_clk * 36) / 24;
3184 break;
3185 case COLOR_DEPTH_161616:
3186 normalized_pix_clk = (pix_clk * 48) / 24;
3187 break;
3188 default:
3189 ASSERT(0);
3190 break;
3191 }
3192 }
3193 return normalized_pix_clk;
3194}
3195
3196static void calculate_phy_pix_clks(struct dc_stream_state *stream)
3197{
3198 /* update actual pixel clock on all streams */
3199 if (dc_is_hdmi_signal(signal: stream->signal))
3200 stream->phy_pix_clk = get_norm_pix_clk(
3201 timing: &stream->timing) / 10;
3202 else
3203 stream->phy_pix_clk =
3204 stream->timing.pix_clk_100hz / 10;
3205
3206 if (stream->timing.timing_3d_format == TIMING_3D_FORMAT_HW_FRAME_PACKING)
3207 stream->phy_pix_clk *= 2;
3208}
3209
3210static int acquire_resource_from_hw_enabled_state(
3211 struct resource_context *res_ctx,
3212 const struct resource_pool *pool,
3213 struct dc_stream_state *stream)
3214{
3215 struct dc_link *link = stream->link;
3216 unsigned int i, inst, tg_inst = 0;
3217 uint32_t numPipes = 1;
3218 uint32_t id_src[4] = {0};
3219
3220 /* Check for enabled DIG to identify enabled display */
3221 if (!link->link_enc->funcs->is_dig_enabled(link->link_enc))
3222 return -1;
3223
3224 inst = link->link_enc->funcs->get_dig_frontend(link->link_enc);
3225
3226 if (inst == ENGINE_ID_UNKNOWN)
3227 return -1;
3228
3229 for (i = 0; i < pool->stream_enc_count; i++) {
3230 if (pool->stream_enc[i]->id == inst) {
3231 tg_inst = pool->stream_enc[i]->funcs->dig_source_otg(
3232 pool->stream_enc[i]);
3233 break;
3234 }
3235 }
3236
3237 // tg_inst not found
3238 if (i == pool->stream_enc_count)
3239 return -1;
3240
3241 if (tg_inst >= pool->timing_generator_count)
3242 return -1;
3243
3244 if (!res_ctx->pipe_ctx[tg_inst].stream) {
3245 struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[tg_inst];
3246
3247 pipe_ctx->stream_res.tg = pool->timing_generators[tg_inst];
3248 id_src[0] = tg_inst;
3249
3250 if (pipe_ctx->stream_res.tg->funcs->get_optc_source)
3251 pipe_ctx->stream_res.tg->funcs->get_optc_source(pipe_ctx->stream_res.tg,
3252 &numPipes, &id_src[0], &id_src[1]);
3253
3254 if (id_src[0] == 0xf && id_src[1] == 0xf) {
3255 id_src[0] = tg_inst;
3256 numPipes = 1;
3257 }
3258
3259 for (i = 0; i < numPipes; i++) {
3260 //Check if src id invalid
3261 if (id_src[i] == 0xf)
3262 return -1;
3263
3264 pipe_ctx = &res_ctx->pipe_ctx[id_src[i]];
3265
3266 pipe_ctx->stream_res.tg = pool->timing_generators[tg_inst];
3267 pipe_ctx->plane_res.mi = pool->mis[id_src[i]];
3268 pipe_ctx->plane_res.hubp = pool->hubps[id_src[i]];
3269 pipe_ctx->plane_res.ipp = pool->ipps[id_src[i]];
3270 pipe_ctx->plane_res.xfm = pool->transforms[id_src[i]];
3271 pipe_ctx->plane_res.dpp = pool->dpps[id_src[i]];
3272 pipe_ctx->stream_res.opp = pool->opps[id_src[i]];
3273
3274 if (pool->dpps[id_src[i]]) {
3275 pipe_ctx->plane_res.mpcc_inst = pool->dpps[id_src[i]]->inst;
3276
3277 if (pool->mpc->funcs->read_mpcc_state) {
3278 struct mpcc_state s = {0};
3279
3280 pool->mpc->funcs->read_mpcc_state(pool->mpc, pipe_ctx->plane_res.mpcc_inst, &s);
3281
3282 if (s.dpp_id < MAX_MPCC)
3283 pool->mpc->mpcc_array[pipe_ctx->plane_res.mpcc_inst].dpp_id =
3284 s.dpp_id;
3285
3286 if (s.bot_mpcc_id < MAX_MPCC)
3287 pool->mpc->mpcc_array[pipe_ctx->plane_res.mpcc_inst].mpcc_bot =
3288 &pool->mpc->mpcc_array[s.bot_mpcc_id];
3289
3290 if (s.opp_id < MAX_OPP)
3291 pipe_ctx->stream_res.opp->mpc_tree_params.opp_id = s.opp_id;
3292 }
3293 }
3294 pipe_ctx->pipe_idx = id_src[i];
3295
3296 if (id_src[i] >= pool->timing_generator_count) {
3297 id_src[i] = pool->timing_generator_count - 1;
3298
3299 pipe_ctx->stream_res.tg = pool->timing_generators[id_src[i]];
3300 pipe_ctx->stream_res.opp = pool->opps[id_src[i]];
3301 }
3302
3303 pipe_ctx->stream = stream;
3304 }
3305
3306 if (numPipes == 2) {
3307 stream->apply_boot_odm_mode = dm_odm_combine_policy_2to1;
3308 res_ctx->pipe_ctx[id_src[0]].next_odm_pipe = &res_ctx->pipe_ctx[id_src[1]];
3309 res_ctx->pipe_ctx[id_src[0]].prev_odm_pipe = NULL;
3310 res_ctx->pipe_ctx[id_src[1]].next_odm_pipe = NULL;
3311 res_ctx->pipe_ctx[id_src[1]].prev_odm_pipe = &res_ctx->pipe_ctx[id_src[0]];
3312 } else
3313 stream->apply_boot_odm_mode = dm_odm_combine_mode_disabled;
3314
3315 return id_src[0];
3316 }
3317
3318 return -1;
3319}
3320
3321static void mark_seamless_boot_stream(
3322 const struct dc *dc,
3323 struct dc_stream_state *stream)
3324{
3325 struct dc_bios *dcb = dc->ctx->dc_bios;
3326
3327 if (dc->config.allow_seamless_boot_optimization &&
3328 !dcb->funcs->is_accelerated_mode(dcb)) {
3329 if (dc_validate_boot_timing(dc, sink: stream->sink, crtc_timing: &stream->timing))
3330 stream->apply_seamless_boot_optimization = true;
3331 }
3332}
3333
3334/*
3335 * Acquire a pipe as OTG master and assign to the stream in new dc context.
3336 * return - true if OTG master pipe is acquired and new dc context is updated.
3337 * false if it fails to acquire an OTG master pipe for this stream.
3338 *
3339 * In the example below, we acquired pipe 0 as OTG master pipe for the stream.
3340 * After the function its Inter-pipe Relation is represented by the diagram
3341 * below.
3342 *
3343 * Inter-pipe Relation
3344 * __________________________________________________
3345 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER |
3346 * | | | | |
3347 * | 0 | |blank ------------------ |
3348 * |________|_______________|___________|_____________|
3349 */
3350static bool acquire_otg_master_pipe_for_stream(
3351 const struct dc_state *cur_ctx,
3352 struct dc_state *new_ctx,
3353 const struct resource_pool *pool,
3354 struct dc_stream_state *stream)
3355{
3356 /* TODO: Move this function to DCN specific resource file and acquire
3357 * DSC resource here. The reason is that the function should have the
3358 * same level of responsibility as when we acquire secondary OPP head.
3359 * We acquire DSC when we acquire secondary OPP head, so we should
3360 * acquire DSC when we acquire OTG master.
3361 */
3362 int pipe_idx;
3363 struct pipe_ctx *pipe_ctx = NULL;
3364
3365 /*
3366 * Upper level code is responsible to optimize unnecessary addition and
3367 * removal for unchanged streams. So unchanged stream will keep the same
3368 * OTG master instance allocated. When current stream is removed and a
3369 * new stream is added, we want to reuse the OTG instance made available
3370 * by the removed stream first. If not found, we try to avoid of using
3371 * any free pipes already used in current context as this could tear
3372 * down exiting ODM/MPC/MPO configuration unnecessarily.
3373 */
3374 pipe_idx = recource_find_free_pipe_used_as_otg_master_in_cur_res_ctx(
3375 cur_res_ctx: &cur_ctx->res_ctx, new_res_ctx: &new_ctx->res_ctx, pool);
3376 if (pipe_idx == FREE_PIPE_INDEX_NOT_FOUND)
3377 pipe_idx = recource_find_free_pipe_not_used_in_cur_res_ctx(
3378 cur_res_ctx: &cur_ctx->res_ctx, new_res_ctx: &new_ctx->res_ctx, pool);
3379 if (pipe_idx == FREE_PIPE_INDEX_NOT_FOUND)
3380 pipe_idx = resource_find_any_free_pipe(new_res_ctx: &new_ctx->res_ctx, pool);
3381 if (pipe_idx != FREE_PIPE_INDEX_NOT_FOUND) {
3382 pipe_ctx = &new_ctx->res_ctx.pipe_ctx[pipe_idx];
3383 memset(pipe_ctx, 0, sizeof(*pipe_ctx));
3384 pipe_ctx->pipe_idx = pipe_idx;
3385 pipe_ctx->stream_res.tg = pool->timing_generators[pipe_idx];
3386 pipe_ctx->plane_res.mi = pool->mis[pipe_idx];
3387 pipe_ctx->plane_res.hubp = pool->hubps[pipe_idx];
3388 pipe_ctx->plane_res.ipp = pool->ipps[pipe_idx];
3389 pipe_ctx->plane_res.xfm = pool->transforms[pipe_idx];
3390 pipe_ctx->plane_res.dpp = pool->dpps[pipe_idx];
3391 pipe_ctx->stream_res.opp = pool->opps[pipe_idx];
3392 if (pool->dpps[pipe_idx])
3393 pipe_ctx->plane_res.mpcc_inst = pool->dpps[pipe_idx]->inst;
3394
3395 if (pipe_idx >= pool->timing_generator_count) {
3396 int tg_inst = pool->timing_generator_count - 1;
3397
3398 pipe_ctx->stream_res.tg = pool->timing_generators[tg_inst];
3399 pipe_ctx->stream_res.opp = pool->opps[tg_inst];
3400 }
3401
3402 pipe_ctx->stream = stream;
3403 } else {
3404 pipe_idx = acquire_first_split_pipe(res_ctx: &new_ctx->res_ctx, pool, stream);
3405 }
3406
3407 return pipe_idx != FREE_PIPE_INDEX_NOT_FOUND;
3408}
3409
3410enum dc_status resource_map_pool_resources(
3411 const struct dc *dc,
3412 struct dc_state *context,
3413 struct dc_stream_state *stream)
3414{
3415 const struct resource_pool *pool = dc->res_pool;
3416 int i;
3417 struct dc_context *dc_ctx = dc->ctx;
3418 struct pipe_ctx *pipe_ctx = NULL;
3419 int pipe_idx = -1;
3420 bool acquired = false;
3421
3422 calculate_phy_pix_clks(stream);
3423
3424 mark_seamless_boot_stream(dc, stream);
3425
3426 if (stream->apply_seamless_boot_optimization) {
3427 pipe_idx = acquire_resource_from_hw_enabled_state(
3428 res_ctx: &context->res_ctx,
3429 pool,
3430 stream);
3431 if (pipe_idx < 0)
3432 /* hw resource was assigned to other stream */
3433 stream->apply_seamless_boot_optimization = false;
3434 else
3435 acquired = true;
3436 }
3437
3438 if (!acquired)
3439 /* acquire new resources */
3440 acquired = acquire_otg_master_pipe_for_stream(cur_ctx: dc->current_state,
3441 new_ctx: context, pool, stream);
3442
3443 pipe_ctx = resource_get_otg_master_for_stream(res_ctx: &context->res_ctx, stream);
3444
3445 if (!pipe_ctx || pipe_ctx->stream_res.tg == NULL)
3446 return DC_NO_CONTROLLER_RESOURCE;
3447
3448 pipe_ctx->stream_res.stream_enc =
3449 dc->res_pool->funcs->find_first_free_match_stream_enc_for_link(
3450 &context->res_ctx, pool, stream);
3451
3452 if (!pipe_ctx->stream_res.stream_enc)
3453 return DC_NO_STREAM_ENC_RESOURCE;
3454
3455 update_stream_engine_usage(
3456 res_ctx: &context->res_ctx, pool,
3457 stream_enc: pipe_ctx->stream_res.stream_enc,
3458 acquired: true);
3459
3460 /* Allocate DP HPO Stream Encoder based on signal, hw capabilities
3461 * and link settings
3462 */
3463 if (dc_is_dp_signal(signal: stream->signal)) {
3464 if (!dc->link_srv->dp_decide_link_settings(stream, &pipe_ctx->link_config.dp_link_settings))
3465 return DC_FAIL_DP_LINK_BANDWIDTH;
3466 if (dc->link_srv->dp_get_encoding_format(
3467 &pipe_ctx->link_config.dp_link_settings) == DP_128b_132b_ENCODING) {
3468 pipe_ctx->stream_res.hpo_dp_stream_enc =
3469 find_first_free_match_hpo_dp_stream_enc_for_link(
3470 res_ctx: &context->res_ctx, pool, stream);
3471
3472 if (!pipe_ctx->stream_res.hpo_dp_stream_enc)
3473 return DC_NO_STREAM_ENC_RESOURCE;
3474
3475 update_hpo_dp_stream_engine_usage(
3476 res_ctx: &context->res_ctx, pool,
3477 hpo_dp_stream_enc: pipe_ctx->stream_res.hpo_dp_stream_enc,
3478 acquired: true);
3479 if (!add_hpo_dp_link_enc_to_ctx(res_ctx: &context->res_ctx, pool, pipe_ctx, stream))
3480 return DC_NO_LINK_ENC_RESOURCE;
3481 }
3482 }
3483
3484 /* TODO: Add check if ASIC support and EDID audio */
3485 if (!stream->converter_disable_audio &&
3486 dc_is_audio_capable_signal(signal: pipe_ctx->stream->signal) &&
3487 stream->audio_info.mode_count && stream->audio_info.flags.all) {
3488 pipe_ctx->stream_res.audio = find_first_free_audio(
3489 res_ctx: &context->res_ctx, pool, id: pipe_ctx->stream_res.stream_enc->id, dc_version: dc_ctx->dce_version);
3490
3491 /*
3492 * Audio assigned in order first come first get.
3493 * There are asics which has number of audio
3494 * resources less then number of pipes
3495 */
3496 if (pipe_ctx->stream_res.audio)
3497 update_audio_usage(res_ctx: &context->res_ctx, pool,
3498 audio: pipe_ctx->stream_res.audio, acquired: true);
3499 }
3500
3501 /* Add ABM to the resource if on EDP */
3502 if (pipe_ctx->stream && dc_is_embedded_signal(signal: pipe_ctx->stream->signal)) {
3503 if (pool->abm)
3504 pipe_ctx->stream_res.abm = pool->abm;
3505 else
3506 pipe_ctx->stream_res.abm = pool->multiple_abms[pipe_ctx->stream_res.tg->inst];
3507 }
3508
3509 for (i = 0; i < context->stream_count; i++)
3510 if (context->streams[i] == stream) {
3511 context->stream_status[i].primary_otg_inst = pipe_ctx->stream_res.tg->inst;
3512 context->stream_status[i].stream_enc_inst = pipe_ctx->stream_res.stream_enc->stream_enc_inst;
3513 context->stream_status[i].audio_inst =
3514 pipe_ctx->stream_res.audio ? pipe_ctx->stream_res.audio->inst : -1;
3515
3516 return DC_OK;
3517 }
3518
3519 DC_ERROR("Stream %p not found in new ctx!\n", stream);
3520 return DC_ERROR_UNEXPECTED;
3521}
3522
3523bool dc_resource_is_dsc_encoding_supported(const struct dc *dc)
3524{
3525 if (dc->res_pool == NULL)
3526 return false;
3527
3528 return dc->res_pool->res_cap->num_dsc > 0;
3529}
3530
3531static bool planes_changed_for_existing_stream(struct dc_state *context,
3532 struct dc_stream_state *stream,
3533 const struct dc_validation_set set[],
3534 int set_count)
3535{
3536 int i, j;
3537 struct dc_stream_status *stream_status = NULL;
3538
3539 for (i = 0; i < context->stream_count; i++) {
3540 if (context->streams[i] == stream) {
3541 stream_status = &context->stream_status[i];
3542 break;
3543 }
3544 }
3545
3546 if (!stream_status)
3547 ASSERT(0);
3548
3549 for (i = 0; i < set_count; i++)
3550 if (set[i].stream == stream)
3551 break;
3552
3553 if (i == set_count)
3554 ASSERT(0);
3555
3556 if (set[i].plane_count != stream_status->plane_count)
3557 return true;
3558
3559 for (j = 0; j < set[i].plane_count; j++)
3560 if (set[i].plane_states[j] != stream_status->plane_states[j])
3561 return true;
3562
3563 return false;
3564}
3565
3566static bool add_all_planes_for_stream(
3567 const struct dc *dc,
3568 struct dc_stream_state *stream,
3569 const struct dc_validation_set set[],
3570 int set_count,
3571 struct dc_state *state)
3572{
3573 int i, j;
3574
3575 for (i = 0; i < set_count; i++)
3576 if (set[i].stream == stream)
3577 break;
3578
3579 if (i == set_count) {
3580 dm_error("Stream %p not found in set!\n", stream);
3581 return false;
3582 }
3583
3584 for (j = 0; j < set[i].plane_count; j++)
3585 if (!dc_state_add_plane(dc, stream, plane_state: set[i].plane_states[j], state))
3586 return false;
3587
3588 return true;
3589}
3590
3591/**
3592 * dc_validate_with_context - Validate and update the potential new stream in the context object
3593 *
3594 * @dc: Used to get the current state status
3595 * @set: An array of dc_validation_set with all the current streams reference
3596 * @set_count: Total of streams
3597 * @context: New context
3598 * @fast_validate: Enable or disable fast validation
3599 *
3600 * This function updates the potential new stream in the context object. It
3601 * creates multiple lists for the add, remove, and unchanged streams. In
3602 * particular, if the unchanged streams have a plane that changed, it is
3603 * necessary to remove all planes from the unchanged streams. In summary, this
3604 * function is responsible for validating the new context.
3605 *
3606 * Return:
3607 * In case of success, return DC_OK (1), otherwise, return a DC error.
3608 */
3609enum dc_status dc_validate_with_context(struct dc *dc,
3610 const struct dc_validation_set set[],
3611 int set_count,
3612 struct dc_state *context,
3613 bool fast_validate)
3614{
3615 struct dc_stream_state *unchanged_streams[MAX_PIPES] = { 0 };
3616 struct dc_stream_state *del_streams[MAX_PIPES] = { 0 };
3617 struct dc_stream_state *add_streams[MAX_PIPES] = { 0 };
3618 int old_stream_count = context->stream_count;
3619 enum dc_status res = DC_ERROR_UNEXPECTED;
3620 int unchanged_streams_count = 0;
3621 int del_streams_count = 0;
3622 int add_streams_count = 0;
3623 bool found = false;
3624 int i, j, k;
3625
3626 DC_LOGGER_INIT(dc->ctx->logger);
3627
3628 /* First build a list of streams to be remove from current context */
3629 for (i = 0; i < old_stream_count; i++) {
3630 struct dc_stream_state *stream = context->streams[i];
3631
3632 for (j = 0; j < set_count; j++) {
3633 if (stream == set[j].stream) {
3634 found = true;
3635 break;
3636 }
3637 }
3638
3639 if (!found)
3640 del_streams[del_streams_count++] = stream;
3641
3642 found = false;
3643 }
3644
3645 /* Second, build a list of new streams */
3646 for (i = 0; i < set_count; i++) {
3647 struct dc_stream_state *stream = set[i].stream;
3648
3649 for (j = 0; j < old_stream_count; j++) {
3650 if (stream == context->streams[j]) {
3651 found = true;
3652 break;
3653 }
3654 }
3655
3656 if (!found)
3657 add_streams[add_streams_count++] = stream;
3658
3659 found = false;
3660 }
3661
3662 /* Build a list of unchanged streams which is necessary for handling
3663 * planes change such as added, removed, and updated.
3664 */
3665 for (i = 0; i < set_count; i++) {
3666 /* Check if stream is part of the delete list */
3667 for (j = 0; j < del_streams_count; j++) {
3668 if (set[i].stream == del_streams[j]) {
3669 found = true;
3670 break;
3671 }
3672 }
3673
3674 if (!found) {
3675 /* Check if stream is part of the add list */
3676 for (j = 0; j < add_streams_count; j++) {
3677 if (set[i].stream == add_streams[j]) {
3678 found = true;
3679 break;
3680 }
3681 }
3682 }
3683
3684 if (!found)
3685 unchanged_streams[unchanged_streams_count++] = set[i].stream;
3686
3687 found = false;
3688 }
3689
3690 /* Remove all planes for unchanged streams if planes changed */
3691 for (i = 0; i < unchanged_streams_count; i++) {
3692 if (planes_changed_for_existing_stream(context,
3693 stream: unchanged_streams[i],
3694 set,
3695 set_count)) {
3696
3697 if (!dc_state_rem_all_planes_for_stream(dc,
3698 stream: unchanged_streams[i],
3699 state: context)) {
3700 res = DC_FAIL_DETACH_SURFACES;
3701 goto fail;
3702 }
3703 }
3704 }
3705
3706 /* Remove all planes for removed streams and then remove the streams */
3707 for (i = 0; i < del_streams_count; i++) {
3708 /* Need to cpy the dwb data from the old stream in order to efc to work */
3709 if (del_streams[i]->num_wb_info > 0) {
3710 for (j = 0; j < add_streams_count; j++) {
3711 if (del_streams[i]->sink == add_streams[j]->sink) {
3712 add_streams[j]->num_wb_info = del_streams[i]->num_wb_info;
3713 for (k = 0; k < del_streams[i]->num_wb_info; k++)
3714 add_streams[j]->writeback_info[k] = del_streams[i]->writeback_info[k];
3715 }
3716 }
3717 }
3718
3719 if (dc_state_get_stream_subvp_type(state: context, stream: del_streams[i]) == SUBVP_PHANTOM) {
3720 /* remove phantoms specifically */
3721 if (!dc_state_rem_all_phantom_planes_for_stream(dc, phantom_stream: del_streams[i], state: context, should_release_planes: true)) {
3722 res = DC_FAIL_DETACH_SURFACES;
3723 goto fail;
3724 }
3725
3726 res = dc_state_remove_phantom_stream(dc, state: context, phantom_stream: del_streams[i]);
3727 dc_state_release_phantom_stream(dc, state: context, phantom_stream: del_streams[i]);
3728 } else {
3729 if (!dc_state_rem_all_planes_for_stream(dc, stream: del_streams[i], state: context)) {
3730 res = DC_FAIL_DETACH_SURFACES;
3731 goto fail;
3732 }
3733
3734 res = dc_state_remove_stream(dc, state: context, stream: del_streams[i]);
3735 }
3736
3737 if (res != DC_OK)
3738 goto fail;
3739 }
3740
3741 /* Swap seamless boot stream to pipe 0 (if needed) to ensure pipe_ctx
3742 * matches. This may change in the future if seamless_boot_stream can be
3743 * multiple.
3744 */
3745 for (i = 0; i < add_streams_count; i++) {
3746 mark_seamless_boot_stream(dc, stream: add_streams[i]);
3747 if (add_streams[i]->apply_seamless_boot_optimization && i != 0) {
3748 struct dc_stream_state *temp = add_streams[0];
3749
3750 add_streams[0] = add_streams[i];
3751 add_streams[i] = temp;
3752 break;
3753 }
3754 }
3755
3756 /* Add new streams and then add all planes for the new stream */
3757 for (i = 0; i < add_streams_count; i++) {
3758 calculate_phy_pix_clks(stream: add_streams[i]);
3759 res = dc_state_add_stream(dc, state: context, stream: add_streams[i]);
3760 if (res != DC_OK)
3761 goto fail;
3762
3763 if (!add_all_planes_for_stream(dc, stream: add_streams[i], set, set_count, state: context)) {
3764 res = DC_FAIL_ATTACH_SURFACES;
3765 goto fail;
3766 }
3767 }
3768
3769 /* Add all planes for unchanged streams if planes changed */
3770 for (i = 0; i < unchanged_streams_count; i++) {
3771 if (planes_changed_for_existing_stream(context,
3772 stream: unchanged_streams[i],
3773 set,
3774 set_count)) {
3775 if (!add_all_planes_for_stream(dc, stream: unchanged_streams[i], set, set_count, state: context)) {
3776 res = DC_FAIL_ATTACH_SURFACES;
3777 goto fail;
3778 }
3779 }
3780 }
3781
3782 res = dc_validate_global_state(dc, new_ctx: context, fast_validate);
3783
3784fail:
3785 if (res != DC_OK)
3786 DC_LOG_WARNING("%s:resource validation failed, dc_status:%d\n",
3787 __func__,
3788 res);
3789
3790 return res;
3791}
3792
3793/**
3794 * dc_validate_global_state() - Determine if hardware can support a given state
3795 *
3796 * @dc: dc struct for this driver
3797 * @new_ctx: state to be validated
3798 * @fast_validate: set to true if only yes/no to support matters
3799 *
3800 * Checks hardware resource availability and bandwidth requirement.
3801 *
3802 * Return:
3803 * DC_OK if the result can be programmed. Otherwise, an error code.
3804 */
3805enum dc_status dc_validate_global_state(
3806 struct dc *dc,
3807 struct dc_state *new_ctx,
3808 bool fast_validate)
3809{
3810 enum dc_status result = DC_ERROR_UNEXPECTED;
3811 int i, j;
3812
3813 if (!new_ctx)
3814 return DC_ERROR_UNEXPECTED;
3815
3816 if (dc->res_pool->funcs->validate_global) {
3817 result = dc->res_pool->funcs->validate_global(dc, new_ctx);
3818 if (result != DC_OK)
3819 return result;
3820 }
3821
3822 for (i = 0; i < new_ctx->stream_count; i++) {
3823 struct dc_stream_state *stream = new_ctx->streams[i];
3824
3825 for (j = 0; j < dc->res_pool->pipe_count; j++) {
3826 struct pipe_ctx *pipe_ctx = &new_ctx->res_ctx.pipe_ctx[j];
3827
3828 if (pipe_ctx->stream != stream)
3829 continue;
3830
3831 if (dc->res_pool->funcs->patch_unknown_plane_state &&
3832 pipe_ctx->plane_state &&
3833 pipe_ctx->plane_state->tiling_info.gfx9.swizzle == DC_SW_UNKNOWN) {
3834 result = dc->res_pool->funcs->patch_unknown_plane_state(pipe_ctx->plane_state);
3835 if (result != DC_OK)
3836 return result;
3837 }
3838
3839 /* Switch to dp clock source only if there is
3840 * no non dp stream that shares the same timing
3841 * with the dp stream.
3842 */
3843 if (dc_is_dp_signal(signal: pipe_ctx->stream->signal) &&
3844 !find_pll_sharable_stream(stream_needs_pll: stream, context: new_ctx)) {
3845
3846 resource_unreference_clock_source(
3847 res_ctx: &new_ctx->res_ctx,
3848 pool: dc->res_pool,
3849 clock_source: pipe_ctx->clock_source);
3850
3851 pipe_ctx->clock_source = dc->res_pool->dp_clock_source;
3852 resource_reference_clock_source(
3853 res_ctx: &new_ctx->res_ctx,
3854 pool: dc->res_pool,
3855 clock_source: pipe_ctx->clock_source);
3856 }
3857 }
3858 }
3859
3860 result = resource_build_scaling_params_for_context(dc, context: new_ctx);
3861
3862 if (result == DC_OK)
3863 if (!dc->res_pool->funcs->validate_bandwidth(dc, new_ctx, fast_validate))
3864 result = DC_FAIL_BANDWIDTH_VALIDATE;
3865
3866 /*
3867 * Only update link encoder to stream assignment after bandwidth validation passed.
3868 * TODO: Split out assignment and validation.
3869 */
3870 if (result == DC_OK && dc->res_pool->funcs->link_encs_assign && fast_validate == false)
3871 dc->res_pool->funcs->link_encs_assign(
3872 dc, new_ctx, new_ctx->streams, new_ctx->stream_count);
3873
3874 return result;
3875}
3876
3877static void patch_gamut_packet_checksum(
3878 struct dc_info_packet *gamut_packet)
3879{
3880 /* For gamut we recalc checksum */
3881 if (gamut_packet->valid) {
3882 uint8_t chk_sum = 0;
3883 uint8_t *ptr;
3884 uint8_t i;
3885
3886 /*start of the Gamut data. */
3887 ptr = &gamut_packet->sb[3];
3888
3889 for (i = 0; i <= gamut_packet->sb[1]; i++)
3890 chk_sum += ptr[i];
3891
3892 gamut_packet->sb[2] = (uint8_t) (0x100 - chk_sum);
3893 }
3894}
3895
3896static void set_avi_info_frame(
3897 struct dc_info_packet *info_packet,
3898 struct pipe_ctx *pipe_ctx)
3899{
3900 struct dc_stream_state *stream = pipe_ctx->stream;
3901 enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
3902 uint32_t pixel_encoding = 0;
3903 enum scanning_type scan_type = SCANNING_TYPE_NODATA;
3904 enum dc_aspect_ratio aspect = ASPECT_RATIO_NO_DATA;
3905 uint8_t *check_sum = NULL;
3906 uint8_t byte_index = 0;
3907 union hdmi_info_packet hdmi_info;
3908 unsigned int vic = pipe_ctx->stream->timing.vic;
3909 unsigned int rid = pipe_ctx->stream->timing.rid;
3910 unsigned int fr_ind = pipe_ctx->stream->timing.fr_index;
3911 enum dc_timing_3d_format format;
3912
3913 memset(&hdmi_info, 0, sizeof(union hdmi_info_packet));
3914
3915 color_space = pipe_ctx->stream->output_color_space;
3916 if (color_space == COLOR_SPACE_UNKNOWN)
3917 color_space = (stream->timing.pixel_encoding == PIXEL_ENCODING_RGB) ?
3918 COLOR_SPACE_SRGB:COLOR_SPACE_YCBCR709;
3919
3920 /* Initialize header */
3921 hdmi_info.bits.header.info_frame_type = HDMI_INFOFRAME_TYPE_AVI;
3922 /* InfoFrameVersion_3 is defined by CEA861F (Section 6.4), but shall
3923 * not be used in HDMI 2.0 (Section 10.1) */
3924 hdmi_info.bits.header.version = 2;
3925 hdmi_info.bits.header.length = HDMI_AVI_INFOFRAME_SIZE;
3926
3927 /*
3928 * IDO-defined (Y2,Y1,Y0 = 1,1,1) shall not be used by devices built
3929 * according to HDMI 2.0 spec (Section 10.1)
3930 */
3931
3932 switch (stream->timing.pixel_encoding) {
3933 case PIXEL_ENCODING_YCBCR422:
3934 pixel_encoding = 1;
3935 break;
3936
3937 case PIXEL_ENCODING_YCBCR444:
3938 pixel_encoding = 2;
3939 break;
3940 case PIXEL_ENCODING_YCBCR420:
3941 pixel_encoding = 3;
3942 break;
3943
3944 case PIXEL_ENCODING_RGB:
3945 default:
3946 pixel_encoding = 0;
3947 }
3948
3949 /* Y0_Y1_Y2 : The pixel encoding */
3950 /* H14b AVI InfoFrame has extension on Y-field from 2 bits to 3 bits */
3951 hdmi_info.bits.Y0_Y1_Y2 = pixel_encoding;
3952
3953 /* A0 = 1 Active Format Information valid */
3954 hdmi_info.bits.A0 = ACTIVE_FORMAT_VALID;
3955
3956 /* B0, B1 = 3; Bar info data is valid */
3957 hdmi_info.bits.B0_B1 = BAR_INFO_BOTH_VALID;
3958
3959 hdmi_info.bits.SC0_SC1 = PICTURE_SCALING_UNIFORM;
3960
3961 /* S0, S1 : Underscan / Overscan */
3962 /* TODO: un-hardcode scan type */
3963 scan_type = SCANNING_TYPE_UNDERSCAN;
3964 hdmi_info.bits.S0_S1 = scan_type;
3965
3966 /* C0, C1 : Colorimetry */
3967 switch (color_space) {
3968 case COLOR_SPACE_YCBCR709:
3969 case COLOR_SPACE_YCBCR709_LIMITED:
3970 hdmi_info.bits.C0_C1 = COLORIMETRY_ITU709;
3971 break;
3972 case COLOR_SPACE_YCBCR601:
3973 case COLOR_SPACE_YCBCR601_LIMITED:
3974 hdmi_info.bits.C0_C1 = COLORIMETRY_ITU601;
3975 break;
3976 case COLOR_SPACE_2020_RGB_FULLRANGE:
3977 case COLOR_SPACE_2020_RGB_LIMITEDRANGE:
3978 case COLOR_SPACE_2020_YCBCR:
3979 hdmi_info.bits.EC0_EC2 = COLORIMETRYEX_BT2020RGBYCBCR;
3980 hdmi_info.bits.C0_C1 = COLORIMETRY_EXTENDED;
3981 break;
3982 case COLOR_SPACE_ADOBERGB:
3983 hdmi_info.bits.EC0_EC2 = COLORIMETRYEX_ADOBERGB;
3984 hdmi_info.bits.C0_C1 = COLORIMETRY_EXTENDED;
3985 break;
3986 case COLOR_SPACE_SRGB:
3987 default:
3988 hdmi_info.bits.C0_C1 = COLORIMETRY_NO_DATA;
3989 break;
3990 }
3991
3992 if (pixel_encoding && color_space == COLOR_SPACE_2020_YCBCR &&
3993 stream->out_transfer_func->tf == TRANSFER_FUNCTION_GAMMA22) {
3994 hdmi_info.bits.EC0_EC2 = 0;
3995 hdmi_info.bits.C0_C1 = COLORIMETRY_ITU709;
3996 }
3997
3998 /* TODO: un-hardcode aspect ratio */
3999 aspect = stream->timing.aspect_ratio;
4000
4001 switch (aspect) {
4002 case ASPECT_RATIO_4_3:
4003 case ASPECT_RATIO_16_9:
4004 hdmi_info.bits.M0_M1 = aspect;
4005 break;
4006
4007 case ASPECT_RATIO_NO_DATA:
4008 case ASPECT_RATIO_64_27:
4009 case ASPECT_RATIO_256_135:
4010 default:
4011 hdmi_info.bits.M0_M1 = 0;
4012 }
4013
4014 /* Active Format Aspect ratio - same as Picture Aspect Ratio. */
4015 hdmi_info.bits.R0_R3 = ACTIVE_FORMAT_ASPECT_RATIO_SAME_AS_PICTURE;
4016
4017 switch (stream->content_type) {
4018 case DISPLAY_CONTENT_TYPE_NO_DATA:
4019 hdmi_info.bits.CN0_CN1 = 0;
4020 hdmi_info.bits.ITC = 1;
4021 break;
4022 case DISPLAY_CONTENT_TYPE_GRAPHICS:
4023 hdmi_info.bits.CN0_CN1 = 0;
4024 hdmi_info.bits.ITC = 1;
4025 break;
4026 case DISPLAY_CONTENT_TYPE_PHOTO:
4027 hdmi_info.bits.CN0_CN1 = 1;
4028 hdmi_info.bits.ITC = 1;
4029 break;
4030 case DISPLAY_CONTENT_TYPE_CINEMA:
4031 hdmi_info.bits.CN0_CN1 = 2;
4032 hdmi_info.bits.ITC = 1;
4033 break;
4034 case DISPLAY_CONTENT_TYPE_GAME:
4035 hdmi_info.bits.CN0_CN1 = 3;
4036 hdmi_info.bits.ITC = 1;
4037 break;
4038 }
4039
4040 if (stream->qs_bit == 1) {
4041 if (color_space == COLOR_SPACE_SRGB ||
4042 color_space == COLOR_SPACE_2020_RGB_FULLRANGE)
4043 hdmi_info.bits.Q0_Q1 = RGB_QUANTIZATION_FULL_RANGE;
4044 else if (color_space == COLOR_SPACE_SRGB_LIMITED ||
4045 color_space == COLOR_SPACE_2020_RGB_LIMITEDRANGE)
4046 hdmi_info.bits.Q0_Q1 = RGB_QUANTIZATION_LIMITED_RANGE;
4047 else
4048 hdmi_info.bits.Q0_Q1 = RGB_QUANTIZATION_DEFAULT_RANGE;
4049 } else
4050 hdmi_info.bits.Q0_Q1 = RGB_QUANTIZATION_DEFAULT_RANGE;
4051
4052 /* TODO : We should handle YCC quantization */
4053 /* but we do not have matrix calculation */
4054 hdmi_info.bits.YQ0_YQ1 = YYC_QUANTIZATION_LIMITED_RANGE;
4055
4056 ///VIC
4057 if (pipe_ctx->stream->timing.hdmi_vic != 0)
4058 vic = 0;
4059 format = stream->timing.timing_3d_format;
4060 /*todo, add 3DStereo support*/
4061 if (format != TIMING_3D_FORMAT_NONE) {
4062 // Based on HDMI specs hdmi vic needs to be converted to cea vic when 3D is enabled
4063 switch (pipe_ctx->stream->timing.hdmi_vic) {
4064 case 1:
4065 vic = 95;
4066 break;
4067 case 2:
4068 vic = 94;
4069 break;
4070 case 3:
4071 vic = 93;
4072 break;
4073 case 4:
4074 vic = 98;
4075 break;
4076 default:
4077 break;
4078 }
4079 }
4080 /* If VIC >= 128, the Source shall use AVI InfoFrame Version 3*/
4081 hdmi_info.bits.VIC0_VIC7 = vic;
4082 if (vic >= 128)
4083 hdmi_info.bits.header.version = 3;
4084 /* If (C1, C0)=(1, 1) and (EC2, EC1, EC0)=(1, 1, 1),
4085 * the Source shall use 20 AVI InfoFrame Version 4
4086 */
4087 if (hdmi_info.bits.C0_C1 == COLORIMETRY_EXTENDED &&
4088 hdmi_info.bits.EC0_EC2 == COLORIMETRYEX_RESERVED) {
4089 hdmi_info.bits.header.version = 4;
4090 hdmi_info.bits.header.length = 14;
4091 }
4092
4093 if (rid != 0 && fr_ind != 0) {
4094 hdmi_info.bits.header.version = 5;
4095 hdmi_info.bits.header.length = 15;
4096
4097 hdmi_info.bits.FR0_FR3 = fr_ind & 0xF;
4098 hdmi_info.bits.FR4 = (fr_ind >> 4) & 0x1;
4099 hdmi_info.bits.RID0_RID5 = rid;
4100 }
4101
4102 /* pixel repetition
4103 * PR0 - PR3 start from 0 whereas pHwPathMode->mode.timing.flags.pixel
4104 * repetition start from 1 */
4105 hdmi_info.bits.PR0_PR3 = 0;
4106
4107 /* Bar Info
4108 * barTop: Line Number of End of Top Bar.
4109 * barBottom: Line Number of Start of Bottom Bar.
4110 * barLeft: Pixel Number of End of Left Bar.
4111 * barRight: Pixel Number of Start of Right Bar. */
4112 hdmi_info.bits.bar_top = stream->timing.v_border_top;
4113 hdmi_info.bits.bar_bottom = (stream->timing.v_total
4114 - stream->timing.v_border_bottom + 1);
4115 hdmi_info.bits.bar_left = stream->timing.h_border_left;
4116 hdmi_info.bits.bar_right = (stream->timing.h_total
4117 - stream->timing.h_border_right + 1);
4118
4119 /* Additional Colorimetry Extension
4120 * Used in conduction with C0-C1 and EC0-EC2
4121 * 0 = DCI-P3 RGB (D65)
4122 * 1 = DCI-P3 RGB (theater)
4123 */
4124 hdmi_info.bits.ACE0_ACE3 = 0;
4125
4126 /* check_sum - Calculate AFMT_AVI_INFO0 ~ AFMT_AVI_INFO3 */
4127 check_sum = &hdmi_info.packet_raw_data.sb[0];
4128
4129 *check_sum = HDMI_INFOFRAME_TYPE_AVI + hdmi_info.bits.header.length + hdmi_info.bits.header.version;
4130
4131 for (byte_index = 1; byte_index <= hdmi_info.bits.header.length; byte_index++)
4132 *check_sum += hdmi_info.packet_raw_data.sb[byte_index];
4133
4134 /* one byte complement */
4135 *check_sum = (uint8_t) (0x100 - *check_sum);
4136
4137 /* Store in hw_path_mode */
4138 info_packet->hb0 = hdmi_info.packet_raw_data.hb0;
4139 info_packet->hb1 = hdmi_info.packet_raw_data.hb1;
4140 info_packet->hb2 = hdmi_info.packet_raw_data.hb2;
4141
4142 for (byte_index = 0; byte_index < sizeof(hdmi_info.packet_raw_data.sb); byte_index++)
4143 info_packet->sb[byte_index] = hdmi_info.packet_raw_data.sb[byte_index];
4144
4145 info_packet->valid = true;
4146}
4147
4148static void set_vendor_info_packet(
4149 struct dc_info_packet *info_packet,
4150 struct dc_stream_state *stream)
4151{
4152 /* SPD info packet for FreeSync */
4153
4154 /* Check if Freesync is supported. Return if false. If true,
4155 * set the corresponding bit in the info packet
4156 */
4157 if (!stream->vsp_infopacket.valid)
4158 return;
4159
4160 *info_packet = stream->vsp_infopacket;
4161}
4162
4163static void set_spd_info_packet(
4164 struct dc_info_packet *info_packet,
4165 struct dc_stream_state *stream)
4166{
4167 /* SPD info packet for FreeSync */
4168
4169 /* Check if Freesync is supported. Return if false. If true,
4170 * set the corresponding bit in the info packet
4171 */
4172 if (!stream->vrr_infopacket.valid)
4173 return;
4174
4175 *info_packet = stream->vrr_infopacket;
4176}
4177
4178static void set_hdr_static_info_packet(
4179 struct dc_info_packet *info_packet,
4180 struct dc_stream_state *stream)
4181{
4182 /* HDR Static Metadata info packet for HDR10 */
4183
4184 if (!stream->hdr_static_metadata.valid ||
4185 stream->use_dynamic_meta)
4186 return;
4187
4188 *info_packet = stream->hdr_static_metadata;
4189}
4190
4191static void set_vsc_info_packet(
4192 struct dc_info_packet *info_packet,
4193 struct dc_stream_state *stream)
4194{
4195 if (!stream->vsc_infopacket.valid)
4196 return;
4197
4198 *info_packet = stream->vsc_infopacket;
4199}
4200static void set_hfvs_info_packet(
4201 struct dc_info_packet *info_packet,
4202 struct dc_stream_state *stream)
4203{
4204 if (!stream->hfvsif_infopacket.valid)
4205 return;
4206
4207 *info_packet = stream->hfvsif_infopacket;
4208}
4209
4210static void adaptive_sync_override_dp_info_packets_sdp_line_num(
4211 const struct dc_crtc_timing *timing,
4212 struct enc_sdp_line_num *sdp_line_num,
4213 struct _vcs_dpi_display_pipe_dest_params_st *pipe_dlg_param)
4214{
4215 uint32_t asic_blank_start = 0;
4216 uint32_t asic_blank_end = 0;
4217 uint32_t v_update = 0;
4218
4219 const struct dc_crtc_timing *tg = timing;
4220
4221 /* blank_start = frame end - front porch */
4222 asic_blank_start = tg->v_total - tg->v_front_porch;
4223
4224 /* blank_end = blank_start - active */
4225 asic_blank_end = (asic_blank_start - tg->v_border_bottom -
4226 tg->v_addressable - tg->v_border_top);
4227
4228 if (pipe_dlg_param->vstartup_start > asic_blank_end) {
4229 v_update = (tg->v_total - (pipe_dlg_param->vstartup_start - asic_blank_end));
4230 sdp_line_num->adaptive_sync_line_num_valid = true;
4231 sdp_line_num->adaptive_sync_line_num = (tg->v_total - v_update - 1);
4232 } else {
4233 sdp_line_num->adaptive_sync_line_num_valid = false;
4234 sdp_line_num->adaptive_sync_line_num = 0;
4235 }
4236}
4237
4238static void set_adaptive_sync_info_packet(
4239 struct dc_info_packet *info_packet,
4240 const struct dc_stream_state *stream,
4241 struct encoder_info_frame *info_frame,
4242 struct _vcs_dpi_display_pipe_dest_params_st *pipe_dlg_param)
4243{
4244 if (!stream->adaptive_sync_infopacket.valid)
4245 return;
4246
4247 adaptive_sync_override_dp_info_packets_sdp_line_num(
4248 timing: &stream->timing,
4249 sdp_line_num: &info_frame->sdp_line_num,
4250 pipe_dlg_param);
4251
4252 *info_packet = stream->adaptive_sync_infopacket;
4253}
4254
4255static void set_vtem_info_packet(
4256 struct dc_info_packet *info_packet,
4257 struct dc_stream_state *stream)
4258{
4259 if (!stream->vtem_infopacket.valid)
4260 return;
4261
4262 *info_packet = stream->vtem_infopacket;
4263}
4264
4265struct clock_source *dc_resource_find_first_free_pll(
4266 struct resource_context *res_ctx,
4267 const struct resource_pool *pool)
4268{
4269 int i;
4270
4271 for (i = 0; i < pool->clk_src_count; ++i) {
4272 if (res_ctx->clock_source_ref_count[i] == 0)
4273 return pool->clock_sources[i];
4274 }
4275
4276 return NULL;
4277}
4278
4279void resource_build_info_frame(struct pipe_ctx *pipe_ctx)
4280{
4281 enum signal_type signal = SIGNAL_TYPE_NONE;
4282 struct encoder_info_frame *info = &pipe_ctx->stream_res.encoder_info_frame;
4283
4284 /* default all packets to invalid */
4285 info->avi.valid = false;
4286 info->gamut.valid = false;
4287 info->vendor.valid = false;
4288 info->spd.valid = false;
4289 info->hdrsmd.valid = false;
4290 info->vsc.valid = false;
4291 info->hfvsif.valid = false;
4292 info->vtem.valid = false;
4293 info->adaptive_sync.valid = false;
4294 signal = pipe_ctx->stream->signal;
4295
4296 /* HDMi and DP have different info packets*/
4297 if (dc_is_hdmi_signal(signal)) {
4298 set_avi_info_frame(info_packet: &info->avi, pipe_ctx);
4299
4300 set_vendor_info_packet(info_packet: &info->vendor, stream: pipe_ctx->stream);
4301 set_hfvs_info_packet(info_packet: &info->hfvsif, stream: pipe_ctx->stream);
4302 set_vtem_info_packet(info_packet: &info->vtem, stream: pipe_ctx->stream);
4303
4304 set_spd_info_packet(info_packet: &info->spd, stream: pipe_ctx->stream);
4305
4306 set_hdr_static_info_packet(info_packet: &info->hdrsmd, stream: pipe_ctx->stream);
4307
4308 } else if (dc_is_dp_signal(signal)) {
4309 set_vsc_info_packet(info_packet: &info->vsc, stream: pipe_ctx->stream);
4310
4311 set_spd_info_packet(info_packet: &info->spd, stream: pipe_ctx->stream);
4312
4313 set_hdr_static_info_packet(info_packet: &info->hdrsmd, stream: pipe_ctx->stream);
4314 set_adaptive_sync_info_packet(info_packet: &info->adaptive_sync,
4315 stream: pipe_ctx->stream,
4316 info_frame: info,
4317 pipe_dlg_param: &pipe_ctx->pipe_dlg_param);
4318 }
4319
4320 patch_gamut_packet_checksum(gamut_packet: &info->gamut);
4321}
4322
4323enum dc_status resource_map_clock_resources(
4324 const struct dc *dc,
4325 struct dc_state *context,
4326 struct dc_stream_state *stream)
4327{
4328 /* acquire new resources */
4329 const struct resource_pool *pool = dc->res_pool;
4330 struct pipe_ctx *pipe_ctx = resource_get_otg_master_for_stream(
4331 res_ctx: &context->res_ctx, stream);
4332
4333 if (!pipe_ctx)
4334 return DC_ERROR_UNEXPECTED;
4335
4336 if (dc_is_dp_signal(signal: pipe_ctx->stream->signal)
4337 || pipe_ctx->stream->signal == SIGNAL_TYPE_VIRTUAL)
4338 pipe_ctx->clock_source = pool->dp_clock_source;
4339 else {
4340 pipe_ctx->clock_source = NULL;
4341
4342 if (!dc->config.disable_disp_pll_sharing)
4343 pipe_ctx->clock_source = resource_find_used_clk_src_for_sharing(
4344 res_ctx: &context->res_ctx,
4345 pipe_ctx);
4346
4347 if (pipe_ctx->clock_source == NULL)
4348 pipe_ctx->clock_source =
4349 dc_resource_find_first_free_pll(
4350 res_ctx: &context->res_ctx,
4351 pool);
4352 }
4353
4354 if (pipe_ctx->clock_source == NULL)
4355 return DC_NO_CLOCK_SOURCE_RESOURCE;
4356
4357 resource_reference_clock_source(
4358 res_ctx: &context->res_ctx, pool,
4359 clock_source: pipe_ctx->clock_source);
4360
4361 return DC_OK;
4362}
4363
4364/*
4365 * Note: We need to disable output if clock sources change,
4366 * since bios does optimization and doesn't apply if changing
4367 * PHY when not already disabled.
4368 */
4369bool pipe_need_reprogram(
4370 struct pipe_ctx *pipe_ctx_old,
4371 struct pipe_ctx *pipe_ctx)
4372{
4373 if (!pipe_ctx_old->stream)
4374 return false;
4375
4376 if (pipe_ctx_old->stream->sink != pipe_ctx->stream->sink)
4377 return true;
4378
4379 if (pipe_ctx_old->stream->signal != pipe_ctx->stream->signal)
4380 return true;
4381
4382 if (pipe_ctx_old->stream_res.audio != pipe_ctx->stream_res.audio)
4383 return true;
4384
4385 if (pipe_ctx_old->clock_source != pipe_ctx->clock_source
4386 && pipe_ctx_old->stream != pipe_ctx->stream)
4387 return true;
4388
4389 if (pipe_ctx_old->stream_res.stream_enc != pipe_ctx->stream_res.stream_enc)
4390 return true;
4391
4392 if (dc_is_timing_changed(cur_stream: pipe_ctx_old->stream, new_stream: pipe_ctx->stream))
4393 return true;
4394
4395 if (pipe_ctx_old->stream->dpms_off != pipe_ctx->stream->dpms_off)
4396 return true;
4397
4398 if (false == pipe_ctx_old->stream->link->link_state_valid &&
4399 false == pipe_ctx_old->stream->dpms_off)
4400 return true;
4401
4402 if (pipe_ctx_old->stream_res.dsc != pipe_ctx->stream_res.dsc)
4403 return true;
4404
4405 if (pipe_ctx_old->stream_res.hpo_dp_stream_enc != pipe_ctx->stream_res.hpo_dp_stream_enc)
4406 return true;
4407 if (pipe_ctx_old->link_res.hpo_dp_link_enc != pipe_ctx->link_res.hpo_dp_link_enc)
4408 return true;
4409
4410 /* DIG link encoder resource assignment for stream changed. */
4411 if (pipe_ctx_old->stream->ctx->dc->res_pool->funcs->link_encs_assign) {
4412 bool need_reprogram = false;
4413 struct dc *dc = pipe_ctx_old->stream->ctx->dc;
4414 struct link_encoder *link_enc_prev =
4415 link_enc_cfg_get_link_enc_used_by_stream_current(dc, stream: pipe_ctx_old->stream);
4416
4417 if (link_enc_prev != pipe_ctx->stream->link_enc)
4418 need_reprogram = true;
4419
4420 return need_reprogram;
4421 }
4422
4423 return false;
4424}
4425
4426void resource_build_bit_depth_reduction_params(struct dc_stream_state *stream,
4427 struct bit_depth_reduction_params *fmt_bit_depth)
4428{
4429 enum dc_dither_option option = stream->dither_option;
4430 enum dc_pixel_encoding pixel_encoding =
4431 stream->timing.pixel_encoding;
4432
4433 memset(fmt_bit_depth, 0, sizeof(*fmt_bit_depth));
4434
4435 if (option == DITHER_OPTION_DEFAULT) {
4436 switch (stream->timing.display_color_depth) {
4437 case COLOR_DEPTH_666:
4438 option = DITHER_OPTION_SPATIAL6;
4439 break;
4440 case COLOR_DEPTH_888:
4441 option = DITHER_OPTION_SPATIAL8;
4442 break;
4443 case COLOR_DEPTH_101010:
4444 option = DITHER_OPTION_TRUN10;
4445 break;
4446 default:
4447 option = DITHER_OPTION_DISABLE;
4448 }
4449 }
4450
4451 if (option == DITHER_OPTION_DISABLE)
4452 return;
4453
4454 if (option == DITHER_OPTION_TRUN6) {
4455 fmt_bit_depth->flags.TRUNCATE_ENABLED = 1;
4456 fmt_bit_depth->flags.TRUNCATE_DEPTH = 0;
4457 } else if (option == DITHER_OPTION_TRUN8 ||
4458 option == DITHER_OPTION_TRUN8_SPATIAL6 ||
4459 option == DITHER_OPTION_TRUN8_FM6) {
4460 fmt_bit_depth->flags.TRUNCATE_ENABLED = 1;
4461 fmt_bit_depth->flags.TRUNCATE_DEPTH = 1;
4462 } else if (option == DITHER_OPTION_TRUN10 ||
4463 option == DITHER_OPTION_TRUN10_SPATIAL6 ||
4464 option == DITHER_OPTION_TRUN10_SPATIAL8 ||
4465 option == DITHER_OPTION_TRUN10_FM8 ||
4466 option == DITHER_OPTION_TRUN10_FM6 ||
4467 option == DITHER_OPTION_TRUN10_SPATIAL8_FM6) {
4468 fmt_bit_depth->flags.TRUNCATE_ENABLED = 1;
4469 fmt_bit_depth->flags.TRUNCATE_DEPTH = 2;
4470 if (option == DITHER_OPTION_TRUN10)
4471 fmt_bit_depth->flags.TRUNCATE_MODE = 1;
4472 }
4473
4474 /* special case - Formatter can only reduce by 4 bits at most.
4475 * When reducing from 12 to 6 bits,
4476 * HW recommends we use trunc with round mode
4477 * (if we did nothing, trunc to 10 bits would be used)
4478 * note that any 12->10 bit reduction is ignored prior to DCE8,
4479 * as the input was 10 bits.
4480 */
4481 if (option == DITHER_OPTION_SPATIAL6_FRAME_RANDOM ||
4482 option == DITHER_OPTION_SPATIAL6 ||
4483 option == DITHER_OPTION_FM6) {
4484 fmt_bit_depth->flags.TRUNCATE_ENABLED = 1;
4485 fmt_bit_depth->flags.TRUNCATE_DEPTH = 2;
4486 fmt_bit_depth->flags.TRUNCATE_MODE = 1;
4487 }
4488
4489 /* spatial dither
4490 * note that spatial modes 1-3 are never used
4491 */
4492 if (option == DITHER_OPTION_SPATIAL6_FRAME_RANDOM ||
4493 option == DITHER_OPTION_SPATIAL6 ||
4494 option == DITHER_OPTION_TRUN10_SPATIAL6 ||
4495 option == DITHER_OPTION_TRUN8_SPATIAL6) {
4496 fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 1;
4497 fmt_bit_depth->flags.SPATIAL_DITHER_DEPTH = 0;
4498 fmt_bit_depth->flags.HIGHPASS_RANDOM = 1;
4499 fmt_bit_depth->flags.RGB_RANDOM =
4500 (pixel_encoding == PIXEL_ENCODING_RGB) ? 1 : 0;
4501 } else if (option == DITHER_OPTION_SPATIAL8_FRAME_RANDOM ||
4502 option == DITHER_OPTION_SPATIAL8 ||
4503 option == DITHER_OPTION_SPATIAL8_FM6 ||
4504 option == DITHER_OPTION_TRUN10_SPATIAL8 ||
4505 option == DITHER_OPTION_TRUN10_SPATIAL8_FM6) {
4506 fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 1;
4507 fmt_bit_depth->flags.SPATIAL_DITHER_DEPTH = 1;
4508 fmt_bit_depth->flags.HIGHPASS_RANDOM = 1;
4509 fmt_bit_depth->flags.RGB_RANDOM =
4510 (pixel_encoding == PIXEL_ENCODING_RGB) ? 1 : 0;
4511 } else if (option == DITHER_OPTION_SPATIAL10_FRAME_RANDOM ||
4512 option == DITHER_OPTION_SPATIAL10 ||
4513 option == DITHER_OPTION_SPATIAL10_FM8 ||
4514 option == DITHER_OPTION_SPATIAL10_FM6) {
4515 fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 1;
4516 fmt_bit_depth->flags.SPATIAL_DITHER_DEPTH = 2;
4517 fmt_bit_depth->flags.HIGHPASS_RANDOM = 1;
4518 fmt_bit_depth->flags.RGB_RANDOM =
4519 (pixel_encoding == PIXEL_ENCODING_RGB) ? 1 : 0;
4520 }
4521
4522 if (option == DITHER_OPTION_SPATIAL6 ||
4523 option == DITHER_OPTION_SPATIAL8 ||
4524 option == DITHER_OPTION_SPATIAL10) {
4525 fmt_bit_depth->flags.FRAME_RANDOM = 0;
4526 } else {
4527 fmt_bit_depth->flags.FRAME_RANDOM = 1;
4528 }
4529
4530 //////////////////////
4531 //// temporal dither
4532 //////////////////////
4533 if (option == DITHER_OPTION_FM6 ||
4534 option == DITHER_OPTION_SPATIAL8_FM6 ||
4535 option == DITHER_OPTION_SPATIAL10_FM6 ||
4536 option == DITHER_OPTION_TRUN10_FM6 ||
4537 option == DITHER_OPTION_TRUN8_FM6 ||
4538 option == DITHER_OPTION_TRUN10_SPATIAL8_FM6) {
4539 fmt_bit_depth->flags.FRAME_MODULATION_ENABLED = 1;
4540 fmt_bit_depth->flags.FRAME_MODULATION_DEPTH = 0;
4541 } else if (option == DITHER_OPTION_FM8 ||
4542 option == DITHER_OPTION_SPATIAL10_FM8 ||
4543 option == DITHER_OPTION_TRUN10_FM8) {
4544 fmt_bit_depth->flags.FRAME_MODULATION_ENABLED = 1;
4545 fmt_bit_depth->flags.FRAME_MODULATION_DEPTH = 1;
4546 } else if (option == DITHER_OPTION_FM10) {
4547 fmt_bit_depth->flags.FRAME_MODULATION_ENABLED = 1;
4548 fmt_bit_depth->flags.FRAME_MODULATION_DEPTH = 2;
4549 }
4550
4551 fmt_bit_depth->pixel_encoding = pixel_encoding;
4552}
4553
4554enum dc_status dc_validate_stream(struct dc *dc, struct dc_stream_state *stream)
4555{
4556 struct dc_link *link = stream->link;
4557 struct timing_generator *tg = dc->res_pool->timing_generators[0];
4558 enum dc_status res = DC_OK;
4559
4560 calculate_phy_pix_clks(stream);
4561
4562 if (!tg->funcs->validate_timing(tg, &stream->timing))
4563 res = DC_FAIL_CONTROLLER_VALIDATE;
4564
4565 if (res == DC_OK) {
4566 if (link->ep_type == DISPLAY_ENDPOINT_PHY &&
4567 !link->link_enc->funcs->validate_output_with_stream(
4568 link->link_enc, stream))
4569 res = DC_FAIL_ENC_VALIDATE;
4570 }
4571
4572 /* TODO: validate audio ASIC caps, encoder */
4573
4574 if (res == DC_OK)
4575 res = dc->link_srv->validate_mode_timing(stream,
4576 link,
4577 &stream->timing);
4578
4579 return res;
4580}
4581
4582enum dc_status dc_validate_plane(struct dc *dc, const struct dc_plane_state *plane_state)
4583{
4584 enum dc_status res = DC_OK;
4585
4586 /* check if surface has invalid dimensions */
4587 if (plane_state->src_rect.width == 0 || plane_state->src_rect.height == 0 ||
4588 plane_state->dst_rect.width == 0 || plane_state->dst_rect.height == 0)
4589 return DC_FAIL_SURFACE_VALIDATE;
4590
4591 /* TODO For now validates pixel format only */
4592 if (dc->res_pool->funcs->validate_plane)
4593 return dc->res_pool->funcs->validate_plane(plane_state, &dc->caps);
4594
4595 return res;
4596}
4597
4598unsigned int resource_pixel_format_to_bpp(enum surface_pixel_format format)
4599{
4600 switch (format) {
4601 case SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS:
4602 return 8;
4603 case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr:
4604 case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb:
4605 return 12;
4606 case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555:
4607 case SURFACE_PIXEL_FORMAT_GRPH_RGB565:
4608 case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr:
4609 case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb:
4610 return 16;
4611 case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888:
4612 case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888:
4613 case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010:
4614 case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010:
4615 case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS:
4616 case SURFACE_PIXEL_FORMAT_GRPH_RGBE:
4617 case SURFACE_PIXEL_FORMAT_GRPH_RGBE_ALPHA:
4618 return 32;
4619 case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616:
4620 case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616:
4621 case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F:
4622 case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F:
4623 return 64;
4624 default:
4625 ASSERT_CRITICAL(false);
4626 return -1;
4627 }
4628}
4629static unsigned int get_max_audio_sample_rate(struct audio_mode *modes)
4630{
4631 if (modes) {
4632 if (modes->sample_rates.rate.RATE_192)
4633 return 192000;
4634 if (modes->sample_rates.rate.RATE_176_4)
4635 return 176400;
4636 if (modes->sample_rates.rate.RATE_96)
4637 return 96000;
4638 if (modes->sample_rates.rate.RATE_88_2)
4639 return 88200;
4640 if (modes->sample_rates.rate.RATE_48)
4641 return 48000;
4642 if (modes->sample_rates.rate.RATE_44_1)
4643 return 44100;
4644 if (modes->sample_rates.rate.RATE_32)
4645 return 32000;
4646 }
4647 /*original logic when no audio info*/
4648 return 441000;
4649}
4650
4651void get_audio_check(struct audio_info *aud_modes,
4652 struct audio_check *audio_chk)
4653{
4654 unsigned int i;
4655 unsigned int max_sample_rate = 0;
4656
4657 if (aud_modes) {
4658 audio_chk->audio_packet_type = 0x2;/*audio sample packet AP = .25 for layout0, 1 for layout1*/
4659
4660 audio_chk->max_audiosample_rate = 0;
4661 for (i = 0; i < aud_modes->mode_count; i++) {
4662 max_sample_rate = get_max_audio_sample_rate(modes: &aud_modes->modes[i]);
4663 if (audio_chk->max_audiosample_rate < max_sample_rate)
4664 audio_chk->max_audiosample_rate = max_sample_rate;
4665 /*dts takes the same as type 2: AP = 0.25*/
4666 }
4667 /*check which one take more bandwidth*/
4668 if (audio_chk->max_audiosample_rate > 192000)
4669 audio_chk->audio_packet_type = 0x9;/*AP =1*/
4670 audio_chk->acat = 0;/*not support*/
4671 }
4672}
4673
4674static struct hpo_dp_link_encoder *get_temp_hpo_dp_link_enc(
4675 const struct resource_context *res_ctx,
4676 const struct resource_pool *const pool,
4677 const struct dc_link *link)
4678{
4679 struct hpo_dp_link_encoder *hpo_dp_link_enc = NULL;
4680 int enc_index;
4681
4682 enc_index = find_acquired_hpo_dp_link_enc_for_link(res_ctx, link);
4683
4684 if (enc_index < 0)
4685 enc_index = find_free_hpo_dp_link_enc(res_ctx, pool);
4686
4687 if (enc_index >= 0)
4688 hpo_dp_link_enc = pool->hpo_dp_link_enc[enc_index];
4689
4690 return hpo_dp_link_enc;
4691}
4692
4693bool get_temp_dp_link_res(struct dc_link *link,
4694 struct link_resource *link_res,
4695 struct dc_link_settings *link_settings)
4696{
4697 const struct dc *dc = link->dc;
4698 const struct resource_context *res_ctx = &dc->current_state->res_ctx;
4699
4700 memset(link_res, 0, sizeof(*link_res));
4701
4702 if (dc->link_srv->dp_get_encoding_format(link_settings) == DP_128b_132b_ENCODING) {
4703 link_res->hpo_dp_link_enc = get_temp_hpo_dp_link_enc(res_ctx,
4704 pool: dc->res_pool, link);
4705 if (!link_res->hpo_dp_link_enc)
4706 return false;
4707 }
4708 return true;
4709}
4710
4711void reset_syncd_pipes_from_disabled_pipes(struct dc *dc,
4712 struct dc_state *context)
4713{
4714 int i, j;
4715 struct pipe_ctx *pipe_ctx_old, *pipe_ctx, *pipe_ctx_syncd;
4716
4717 /* If pipe backend is reset, need to reset pipe syncd status */
4718 for (i = 0; i < dc->res_pool->pipe_count; i++) {
4719 pipe_ctx_old = &dc->current_state->res_ctx.pipe_ctx[i];
4720 pipe_ctx = &context->res_ctx.pipe_ctx[i];
4721
4722 if (!resource_is_pipe_type(pipe_ctx: pipe_ctx_old, type: OTG_MASTER))
4723 continue;
4724
4725 if (!pipe_ctx->stream ||
4726 pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) {
4727
4728 /* Reset all the syncd pipes from the disabled pipe */
4729 for (j = 0; j < dc->res_pool->pipe_count; j++) {
4730 pipe_ctx_syncd = &context->res_ctx.pipe_ctx[j];
4731 if ((GET_PIPE_SYNCD_FROM_PIPE(pipe_ctx_syncd) == pipe_ctx_old->pipe_idx) ||
4732 !IS_PIPE_SYNCD_VALID(pipe_ctx_syncd))
4733 SET_PIPE_SYNCD_TO_PIPE(pipe_ctx_syncd, j);
4734 }
4735 }
4736 }
4737}
4738
4739void check_syncd_pipes_for_disabled_master_pipe(struct dc *dc,
4740 struct dc_state *context,
4741 uint8_t disabled_master_pipe_idx)
4742{
4743 int i;
4744 struct pipe_ctx *pipe_ctx, *pipe_ctx_check;
4745
4746 pipe_ctx = &context->res_ctx.pipe_ctx[disabled_master_pipe_idx];
4747 if ((GET_PIPE_SYNCD_FROM_PIPE(pipe_ctx) != disabled_master_pipe_idx) ||
4748 !IS_PIPE_SYNCD_VALID(pipe_ctx))
4749 SET_PIPE_SYNCD_TO_PIPE(pipe_ctx, disabled_master_pipe_idx);
4750
4751 /* for the pipe disabled, check if any slave pipe exists and assert */
4752 for (i = 0; i < dc->res_pool->pipe_count; i++) {
4753 pipe_ctx_check = &context->res_ctx.pipe_ctx[i];
4754
4755 if ((GET_PIPE_SYNCD_FROM_PIPE(pipe_ctx_check) == disabled_master_pipe_idx) &&
4756 IS_PIPE_SYNCD_VALID(pipe_ctx_check) && (i != disabled_master_pipe_idx)) {
4757 struct pipe_ctx *first_pipe = pipe_ctx_check;
4758
4759 while (first_pipe->prev_odm_pipe)
4760 first_pipe = first_pipe->prev_odm_pipe;
4761 /* When ODM combine is enabled, this case is expected. If the disabled pipe
4762 * is part of the ODM tree, then we should not print an error.
4763 * */
4764 if (first_pipe->pipe_idx == disabled_master_pipe_idx)
4765 continue;
4766
4767 DC_ERR("DC: Failure: pipe_idx[%d] syncd with disabled master pipe_idx[%d]\n",
4768 i, disabled_master_pipe_idx);
4769 }
4770 }
4771}
4772
4773void reset_sync_context_for_pipe(const struct dc *dc,
4774 struct dc_state *context,
4775 uint8_t pipe_idx)
4776{
4777 int i;
4778 struct pipe_ctx *pipe_ctx_reset;
4779
4780 /* reset the otg sync context for the pipe and its slave pipes if any */
4781 for (i = 0; i < dc->res_pool->pipe_count; i++) {
4782 pipe_ctx_reset = &context->res_ctx.pipe_ctx[i];
4783
4784 if (((GET_PIPE_SYNCD_FROM_PIPE(pipe_ctx_reset) == pipe_idx) &&
4785 IS_PIPE_SYNCD_VALID(pipe_ctx_reset)) || (i == pipe_idx))
4786 SET_PIPE_SYNCD_TO_PIPE(pipe_ctx_reset, i);
4787 }
4788}
4789
4790uint8_t resource_transmitter_to_phy_idx(const struct dc *dc, enum transmitter transmitter)
4791{
4792 /* TODO - get transmitter to phy idx mapping from DMUB */
4793 uint8_t phy_idx = transmitter - TRANSMITTER_UNIPHY_A;
4794
4795 if (dc->ctx->dce_version == DCN_VERSION_3_1 &&
4796 dc->ctx->asic_id.hw_internal_rev == YELLOW_CARP_B0) {
4797 switch (transmitter) {
4798 case TRANSMITTER_UNIPHY_A:
4799 phy_idx = 0;
4800 break;
4801 case TRANSMITTER_UNIPHY_B:
4802 phy_idx = 1;
4803 break;
4804 case TRANSMITTER_UNIPHY_C:
4805 phy_idx = 5;
4806 break;
4807 case TRANSMITTER_UNIPHY_D:
4808 phy_idx = 6;
4809 break;
4810 case TRANSMITTER_UNIPHY_E:
4811 phy_idx = 4;
4812 break;
4813 default:
4814 phy_idx = 0;
4815 break;
4816 }
4817 }
4818
4819 return phy_idx;
4820}
4821
4822const struct link_hwss *get_link_hwss(const struct dc_link *link,
4823 const struct link_resource *link_res)
4824{
4825 /* Link_hwss is only accessible by getter function instead of accessing
4826 * by pointers in dc with the intent to protect against breaking polymorphism.
4827 */
4828 if (can_use_hpo_dp_link_hwss(link, link_res))
4829 /* TODO: some assumes that if decided link settings is 128b/132b
4830 * channel coding format hpo_dp_link_enc should be used.
4831 * Others believe that if hpo_dp_link_enc is available in link
4832 * resource then hpo_dp_link_enc must be used. This bound between
4833 * hpo_dp_link_enc != NULL and decided link settings is loosely coupled
4834 * with a premise that both hpo_dp_link_enc pointer and decided link
4835 * settings are determined based on single policy function like
4836 * "decide_link_settings" from upper layer. This "convention"
4837 * cannot be maintained and enforced at current level.
4838 * Therefore a refactor is due so we can enforce a strong bound
4839 * between those two parameters at this level.
4840 *
4841 * To put it simple, we want to make enforcement at low level so that
4842 * we will not return link hwss if caller plans to do 8b/10b
4843 * with an hpo encoder. Or we can return a very dummy one that doesn't
4844 * do work for all functions
4845 */
4846 return (requires_fixed_vs_pe_retimer_hpo_link_hwss(link) ?
4847 get_hpo_fixed_vs_pe_retimer_dp_link_hwss() : get_hpo_dp_link_hwss());
4848 else if (can_use_dpia_link_hwss(link, link_res))
4849 return get_dpia_link_hwss();
4850 else if (can_use_dio_link_hwss(link, link_res))
4851 return (requires_fixed_vs_pe_retimer_dio_link_hwss(link)) ?
4852 get_dio_fixed_vs_pe_retimer_link_hwss() : get_dio_link_hwss();
4853 else
4854 return get_virtual_link_hwss();
4855}
4856
4857bool is_h_timing_divisible_by_2(struct dc_stream_state *stream)
4858{
4859 bool divisible = false;
4860 uint16_t h_blank_start = 0;
4861 uint16_t h_blank_end = 0;
4862
4863 if (stream) {
4864 h_blank_start = stream->timing.h_total - stream->timing.h_front_porch;
4865 h_blank_end = h_blank_start - stream->timing.h_addressable;
4866
4867 /* HTOTAL, Hblank start/end, and Hsync start/end all must be
4868 * divisible by 2 in order for the horizontal timing params
4869 * to be considered divisible by 2. Hsync start is always 0.
4870 */
4871 divisible = (stream->timing.h_total % 2 == 0) &&
4872 (h_blank_start % 2 == 0) &&
4873 (h_blank_end % 2 == 0) &&
4874 (stream->timing.h_sync_width % 2 == 0);
4875 }
4876 return divisible;
4877}
4878
4879/* This interface is deprecated for new DCNs. It is replaced by the following
4880 * new interfaces. These two interfaces encapsulate pipe selection priority
4881 * with DCN specific minimum hardware transition optimization algorithm. With
4882 * the new interfaces caller no longer needs to know the implementation detail
4883 * of a pipe topology.
4884 *
4885 * resource_update_pipes_with_odm_slice_count
4886 * resource_update_pipes_with_mpc_slice_count
4887 *
4888 */
4889bool dc_resource_acquire_secondary_pipe_for_mpc_odm_legacy(
4890 const struct dc *dc,
4891 struct dc_state *state,
4892 struct pipe_ctx *pri_pipe,
4893 struct pipe_ctx *sec_pipe,
4894 bool odm)
4895{
4896 int pipe_idx = sec_pipe->pipe_idx;
4897 struct pipe_ctx *sec_top, *sec_bottom, *sec_next, *sec_prev;
4898 const struct resource_pool *pool = dc->res_pool;
4899
4900 sec_top = sec_pipe->top_pipe;
4901 sec_bottom = sec_pipe->bottom_pipe;
4902 sec_next = sec_pipe->next_odm_pipe;
4903 sec_prev = sec_pipe->prev_odm_pipe;
4904
4905 if (pri_pipe == NULL)
4906 return false;
4907
4908 *sec_pipe = *pri_pipe;
4909
4910 sec_pipe->top_pipe = sec_top;
4911 sec_pipe->bottom_pipe = sec_bottom;
4912 sec_pipe->next_odm_pipe = sec_next;
4913 sec_pipe->prev_odm_pipe = sec_prev;
4914
4915 sec_pipe->pipe_idx = pipe_idx;
4916 sec_pipe->plane_res.mi = pool->mis[pipe_idx];
4917 sec_pipe->plane_res.hubp = pool->hubps[pipe_idx];
4918 sec_pipe->plane_res.ipp = pool->ipps[pipe_idx];
4919 sec_pipe->plane_res.xfm = pool->transforms[pipe_idx];
4920 sec_pipe->plane_res.dpp = pool->dpps[pipe_idx];
4921 sec_pipe->plane_res.mpcc_inst = pool->dpps[pipe_idx]->inst;
4922 sec_pipe->stream_res.dsc = NULL;
4923 if (odm) {
4924 if (!sec_pipe->top_pipe)
4925 sec_pipe->stream_res.opp = pool->opps[pipe_idx];
4926 else
4927 sec_pipe->stream_res.opp = sec_pipe->top_pipe->stream_res.opp;
4928 if (sec_pipe->stream->timing.flags.DSC == 1) {
4929#if defined(CONFIG_DRM_AMD_DC_FP)
4930 dcn20_acquire_dsc(dc, res_ctx: &state->res_ctx, dsc: &sec_pipe->stream_res.dsc, pipe_idx);
4931#endif
4932 ASSERT(sec_pipe->stream_res.dsc);
4933 if (sec_pipe->stream_res.dsc == NULL)
4934 return false;
4935 }
4936#if defined(CONFIG_DRM_AMD_DC_FP)
4937 dcn20_build_mapped_resource(dc, context: state, stream: sec_pipe->stream);
4938#endif
4939 }
4940
4941 return true;
4942}
4943
4944enum dc_status update_dp_encoder_resources_for_test_harness(const struct dc *dc,
4945 struct dc_state *context,
4946 struct pipe_ctx *pipe_ctx)
4947{
4948 if (dc->link_srv->dp_get_encoding_format(&pipe_ctx->link_config.dp_link_settings) == DP_128b_132b_ENCODING) {
4949 if (pipe_ctx->stream_res.hpo_dp_stream_enc == NULL) {
4950 pipe_ctx->stream_res.hpo_dp_stream_enc =
4951 find_first_free_match_hpo_dp_stream_enc_for_link(
4952 res_ctx: &context->res_ctx, pool: dc->res_pool, stream: pipe_ctx->stream);
4953
4954 if (!pipe_ctx->stream_res.hpo_dp_stream_enc)
4955 return DC_NO_STREAM_ENC_RESOURCE;
4956
4957 update_hpo_dp_stream_engine_usage(
4958 res_ctx: &context->res_ctx, pool: dc->res_pool,
4959 hpo_dp_stream_enc: pipe_ctx->stream_res.hpo_dp_stream_enc,
4960 acquired: true);
4961 }
4962
4963 if (pipe_ctx->link_res.hpo_dp_link_enc == NULL) {
4964 if (!add_hpo_dp_link_enc_to_ctx(res_ctx: &context->res_ctx, pool: dc->res_pool, pipe_ctx, stream: pipe_ctx->stream))
4965 return DC_NO_LINK_ENC_RESOURCE;
4966 }
4967 } else {
4968 if (pipe_ctx->stream_res.hpo_dp_stream_enc) {
4969 update_hpo_dp_stream_engine_usage(
4970 res_ctx: &context->res_ctx, pool: dc->res_pool,
4971 hpo_dp_stream_enc: pipe_ctx->stream_res.hpo_dp_stream_enc,
4972 acquired: false);
4973 pipe_ctx->stream_res.hpo_dp_stream_enc = NULL;
4974 }
4975 if (pipe_ctx->link_res.hpo_dp_link_enc)
4976 remove_hpo_dp_link_enc_from_ctx(res_ctx: &context->res_ctx, pipe_ctx, stream: pipe_ctx->stream);
4977 }
4978
4979 return DC_OK;
4980}
4981
4982bool check_subvp_sw_cursor_fallback_req(const struct dc *dc, struct dc_stream_state *stream)
4983{
4984 if (!dc->debug.disable_subvp_high_refresh && is_subvp_high_refresh_candidate(stream))
4985 return true;
4986 if (dc->current_state->stream_count == 1 && stream->timing.v_addressable >= 2880 &&
4987 ((stream->timing.pix_clk_100hz * 100) / stream->timing.v_total / stream->timing.h_total) < 120)
4988 return true;
4989 else if (dc->current_state->stream_count > 1 && stream->timing.v_addressable >= 1080 &&
4990 ((stream->timing.pix_clk_100hz * 100) / stream->timing.v_total / stream->timing.h_total) < 120)
4991 return true;
4992
4993 return false;
4994}
4995

source code of linux/drivers/gpu/drm/amd/display/dc/core/dc_resource.c