1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. 4 * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. 5 */ 6 7 #define pr_fmt(fmt) "[drm:%s] " fmt, __func__ 8 #include "dpu_kms.h" 9 #include "dpu_hw_lm.h" 10 #include "dpu_hw_ctl.h" 11 #include "dpu_hw_cdm.h" 12 #include "dpu_hw_cwb.h" 13 #include "dpu_hw_pingpong.h" 14 #include "dpu_hw_sspp.h" 15 #include "dpu_hw_intf.h" 16 #include "dpu_hw_wb.h" 17 #include "dpu_hw_dspp.h" 18 #include "dpu_hw_merge3d.h" 19 #include "dpu_hw_dsc.h" 20 #include "dpu_encoder.h" 21 #include "dpu_trace.h" 22 23 24 static inline bool reserved_by_other(uint32_t *res_map, int idx, 25 uint32_t crtc_id) 26 { 27 return res_map[idx] && res_map[idx] != crtc_id; 28 } 29 30 /** 31 * dpu_rm_init - Read hardware catalog and create reservation tracking objects 32 * for all HW blocks. 33 * @dev: Corresponding device for devres management 34 * @rm: DPU Resource Manager handle 35 * @cat: Pointer to hardware catalog 36 * @mdss_data: Pointer to MDSS / UBWC configuration 37 * @mmio: mapped register io address of MDP 38 * @return: 0 on Success otherwise -ERROR 39 */ 40 int dpu_rm_init(struct drm_device *dev, 41 struct dpu_rm *rm, 42 const struct dpu_mdss_cfg *cat, 43 const struct qcom_ubwc_cfg_data *mdss_data, 44 void __iomem *mmio) 45 { 46 int rc, i; 47 48 if (!rm || !cat || !mmio) { 49 DPU_ERROR("invalid kms\n"); 50 return -EINVAL; 51 } 52 53 /* Clear, setup lists */ 54 memset(rm, 0, sizeof(*rm)); 55 56 rm->has_legacy_ctls = (cat->mdss_ver->core_major_ver < 5); 57 58 /* Interrogate HW catalog and create tracking items for hw blocks */ 59 for (i = 0; i < cat->mixer_count; i++) { 60 struct dpu_hw_mixer *hw; 61 const struct dpu_lm_cfg *lm = &cat->mixer[i]; 62 63 hw = dpu_hw_lm_init(dev, lm, mmio, cat->mdss_ver); 64 if (IS_ERR(hw)) { 65 rc = PTR_ERR(hw); 66 DPU_ERROR("failed lm object creation: err %d\n", rc); 67 goto fail; 68 } 69 rm->mixer_blks[lm->id - LM_0] = &hw->base; 70 } 71 72 for (i = 0; i < cat->merge_3d_count; i++) { 73 struct dpu_hw_merge_3d *hw; 74 const struct dpu_merge_3d_cfg *merge_3d = &cat->merge_3d[i]; 75 76 hw = dpu_hw_merge_3d_init(dev, merge_3d, mmio); 77 if (IS_ERR(hw)) { 78 rc = PTR_ERR(hw); 79 DPU_ERROR("failed merge_3d object creation: err %d\n", 80 rc); 81 goto fail; 82 } 83 rm->merge_3d_blks[merge_3d->id - MERGE_3D_0] = &hw->base; 84 } 85 86 for (i = 0; i < cat->pingpong_count; i++) { 87 struct dpu_hw_pingpong *hw; 88 const struct dpu_pingpong_cfg *pp = &cat->pingpong[i]; 89 90 hw = dpu_hw_pingpong_init(dev, pp, mmio, cat->mdss_ver); 91 if (IS_ERR(hw)) { 92 rc = PTR_ERR(hw); 93 DPU_ERROR("failed pingpong object creation: err %d\n", 94 rc); 95 goto fail; 96 } 97 if (pp->merge_3d && pp->merge_3d < MERGE_3D_MAX) 98 hw->merge_3d = to_dpu_hw_merge_3d(rm->merge_3d_blks[pp->merge_3d - MERGE_3D_0]); 99 rm->pingpong_blks[pp->id - PINGPONG_0] = &hw->base; 100 } 101 102 for (i = 0; i < cat->intf_count; i++) { 103 struct dpu_hw_intf *hw; 104 const struct dpu_intf_cfg *intf = &cat->intf[i]; 105 106 hw = dpu_hw_intf_init(dev, intf, mmio, cat->mdss_ver); 107 if (IS_ERR(hw)) { 108 rc = PTR_ERR(hw); 109 DPU_ERROR("failed intf object creation: err %d\n", rc); 110 goto fail; 111 } 112 rm->hw_intf[intf->id - INTF_0] = hw; 113 } 114 115 for (i = 0; i < cat->wb_count; i++) { 116 struct dpu_hw_wb *hw; 117 const struct dpu_wb_cfg *wb = &cat->wb[i]; 118 119 hw = dpu_hw_wb_init(dev, wb, mmio, cat->mdss_ver); 120 if (IS_ERR(hw)) { 121 rc = PTR_ERR(hw); 122 DPU_ERROR("failed wb object creation: err %d\n", rc); 123 goto fail; 124 } 125 rm->hw_wb[wb->id - WB_0] = hw; 126 } 127 128 for (i = 0; i < cat->cwb_count; i++) { 129 struct dpu_hw_cwb *hw; 130 const struct dpu_cwb_cfg *cwb = &cat->cwb[i]; 131 132 hw = dpu_hw_cwb_init(dev, cwb, mmio); 133 if (IS_ERR(hw)) { 134 rc = PTR_ERR(hw); 135 DPU_ERROR("failed cwb object creation: err %d\n", rc); 136 goto fail; 137 } 138 rm->cwb_blks[cwb->id - CWB_0] = &hw->base; 139 } 140 141 for (i = 0; i < cat->ctl_count; i++) { 142 struct dpu_hw_ctl *hw; 143 const struct dpu_ctl_cfg *ctl = &cat->ctl[i]; 144 145 hw = dpu_hw_ctl_init(dev, ctl, mmio, cat->mdss_ver, cat->mixer_count, cat->mixer); 146 if (IS_ERR(hw)) { 147 rc = PTR_ERR(hw); 148 DPU_ERROR("failed ctl object creation: err %d\n", rc); 149 goto fail; 150 } 151 rm->ctl_blks[ctl->id - CTL_0] = &hw->base; 152 } 153 154 for (i = 0; i < cat->dspp_count; i++) { 155 struct dpu_hw_dspp *hw; 156 const struct dpu_dspp_cfg *dspp = &cat->dspp[i]; 157 158 hw = dpu_hw_dspp_init(dev, dspp, mmio); 159 if (IS_ERR(hw)) { 160 rc = PTR_ERR(hw); 161 DPU_ERROR("failed dspp object creation: err %d\n", rc); 162 goto fail; 163 } 164 rm->dspp_blks[dspp->id - DSPP_0] = &hw->base; 165 } 166 167 for (i = 0; i < cat->dsc_count; i++) { 168 struct dpu_hw_dsc *hw; 169 const struct dpu_dsc_cfg *dsc = &cat->dsc[i]; 170 171 if (cat->mdss_ver->core_major_ver >= 7) 172 hw = dpu_hw_dsc_init_1_2(dev, dsc, mmio); 173 else 174 hw = dpu_hw_dsc_init(dev, dsc, mmio, cat->mdss_ver); 175 176 if (IS_ERR(hw)) { 177 rc = PTR_ERR(hw); 178 DPU_ERROR("failed dsc object creation: err %d\n", rc); 179 goto fail; 180 } 181 rm->dsc_blks[dsc->id - DSC_0] = &hw->base; 182 } 183 184 for (i = 0; i < cat->sspp_count; i++) { 185 struct dpu_hw_sspp *hw; 186 const struct dpu_sspp_cfg *sspp = &cat->sspp[i]; 187 188 hw = dpu_hw_sspp_init(dev, sspp, mmio, mdss_data, cat->mdss_ver); 189 if (IS_ERR(hw)) { 190 rc = PTR_ERR(hw); 191 DPU_ERROR("failed sspp object creation: err %d\n", rc); 192 goto fail; 193 } 194 rm->hw_sspp[sspp->id - SSPP_NONE] = hw; 195 } 196 197 if (cat->cdm) { 198 struct dpu_hw_cdm *hw; 199 200 hw = dpu_hw_cdm_init(dev, cat->cdm, mmio, cat->mdss_ver); 201 if (IS_ERR(hw)) { 202 rc = PTR_ERR(hw); 203 DPU_ERROR("failed cdm object creation: err %d\n", rc); 204 goto fail; 205 } 206 rm->cdm_blk = &hw->base; 207 } 208 209 return 0; 210 211 fail: 212 return rc ? rc : -EFAULT; 213 } 214 215 static bool _dpu_rm_needs_split_display(const struct msm_display_topology *top) 216 { 217 return top->num_intf > 1; 218 } 219 220 /** 221 * _dpu_rm_get_lm_peer - get the id of a mixer which is a peer of the primary 222 * @rm: dpu resource manager handle 223 * @primary_idx: index of primary mixer in rm->mixer_blks[] 224 * 225 * Returns: lm peer mixed id on success or %-EINVAL on error 226 */ 227 static int _dpu_rm_get_lm_peer(struct dpu_rm *rm, int primary_idx) 228 { 229 const struct dpu_lm_cfg *prim_lm_cfg; 230 231 prim_lm_cfg = to_dpu_hw_mixer(rm->mixer_blks[primary_idx])->cap; 232 233 if (prim_lm_cfg->lm_pair >= LM_0 && prim_lm_cfg->lm_pair < LM_MAX) 234 return prim_lm_cfg->lm_pair - LM_0; 235 return -EINVAL; 236 } 237 238 static int _dpu_rm_reserve_cwb_mux_and_pingpongs(struct dpu_rm *rm, 239 struct dpu_global_state *global_state, 240 uint32_t crtc_id, 241 struct msm_display_topology *topology) 242 { 243 int num_cwb_mux = topology->num_lm, cwb_mux_count = 0; 244 int cwb_pp_start_idx = PINGPONG_CWB_0 - PINGPONG_0; 245 int cwb_pp_idx[MAX_BLOCKS]; 246 int cwb_mux_idx[MAX_BLOCKS]; 247 248 /* 249 * Reserve additional dedicated CWB PINGPONG blocks and muxes for each 250 * mixer 251 * 252 * TODO: add support reserving resources for platforms with no 253 * PINGPONG_CWB 254 */ 255 for (int i = 0; i < ARRAY_SIZE(rm->mixer_blks) && 256 cwb_mux_count < num_cwb_mux; i++) { 257 for (int j = 0; j < ARRAY_SIZE(rm->cwb_blks); j++) { 258 /* 259 * Odd LMs must be assigned to odd CWB muxes and even 260 * LMs with even CWB muxes. 261 * 262 * Since the RM HW block array index is based on the HW 263 * block ids, we can also use the array index to enforce 264 * the odd/even rule. See dpu_rm_init() for more 265 * information 266 */ 267 if (reserved_by_other(global_state->cwb_to_crtc_id, j, crtc_id) || 268 i % 2 != j % 2) 269 continue; 270 271 cwb_mux_idx[cwb_mux_count] = j; 272 cwb_pp_idx[cwb_mux_count] = j + cwb_pp_start_idx; 273 cwb_mux_count++; 274 break; 275 } 276 } 277 278 if (cwb_mux_count != num_cwb_mux) { 279 DPU_ERROR("Unable to reserve all CWB PINGPONGs\n"); 280 return -ENAVAIL; 281 } 282 283 for (int i = 0; i < cwb_mux_count; i++) { 284 global_state->pingpong_to_crtc_id[cwb_pp_idx[i]] = crtc_id; 285 global_state->cwb_to_crtc_id[cwb_mux_idx[i]] = crtc_id; 286 } 287 288 return 0; 289 } 290 291 /** 292 * _dpu_rm_check_lm_and_get_connected_blks - check if proposed layer mixer meets 293 * proposed use case requirements, incl. hardwired dependent blocks like 294 * pingpong 295 * @rm: dpu resource manager handle 296 * @global_state: resources shared across multiple kms objects 297 * @crtc_id: crtc id requesting for allocation 298 * @lm_idx: index of proposed layer mixer in rm->mixer_blks[], function checks 299 * if lm, and all other hardwired blocks connected to the lm (pp) is 300 * available and appropriate 301 * @pp_idx: output parameter, index of pingpong block attached to the layer 302 * mixer in rm->pingpong_blks[]. 303 * @dspp_idx: output parameter, index of dspp block attached to the layer 304 * mixer in rm->dspp_blks[]. 305 * @topology: selected topology for the display 306 * Return: true if lm matches all requirements, false otherwise 307 */ 308 static bool _dpu_rm_check_lm_and_get_connected_blks(struct dpu_rm *rm, 309 struct dpu_global_state *global_state, 310 uint32_t crtc_id, int lm_idx, int *pp_idx, int *dspp_idx, 311 struct msm_display_topology *topology) 312 { 313 const struct dpu_lm_cfg *lm_cfg; 314 int idx; 315 316 /* Already reserved? */ 317 if (reserved_by_other(global_state->mixer_to_crtc_id, lm_idx, crtc_id)) { 318 DPU_DEBUG("LM_%d already reserved\n", lm_idx); 319 return false; 320 } 321 322 lm_cfg = to_dpu_hw_mixer(rm->mixer_blks[lm_idx])->cap; 323 idx = lm_cfg->pingpong - PINGPONG_0; 324 if (idx < 0 || idx >= ARRAY_SIZE(rm->pingpong_blks) || !rm->pingpong_blks[idx]) { 325 DPU_ERROR("LM_%d, invalid PP_%d\n", lm_idx, idx); 326 return false; 327 } 328 329 if (reserved_by_other(global_state->pingpong_to_crtc_id, idx, crtc_id)) { 330 DPU_DEBUG("LM_%d PP_%d already reserved\n", lm_idx, idx); 331 return false; 332 } 333 *pp_idx = idx; 334 335 if (!topology->num_dspp) 336 return true; 337 338 idx = lm_cfg->dspp - DSPP_0; 339 if (idx < 0 || idx >= ARRAY_SIZE(rm->dspp_blks) || !rm->dspp_blks[idx]) { 340 DPU_ERROR("LM_%d, invalid DSPP_%d\n", lm_idx, idx); 341 return false; 342 } 343 344 if (reserved_by_other(global_state->dspp_to_crtc_id, idx, crtc_id)) { 345 DPU_DEBUG("LM_%d DSPP_%d already reserved\n", lm_idx, idx); 346 return false; 347 } 348 *dspp_idx = idx; 349 350 return true; 351 } 352 353 static bool dpu_rm_find_lms(struct dpu_rm *rm, 354 struct dpu_global_state *global_state, 355 uint32_t crtc_id, bool skip_dspp, 356 struct msm_display_topology *topology, 357 int *lm_idx, int *pp_idx, int *dspp_idx) 358 359 { 360 int i, lm_count = 0; 361 362 /* Find a primary mixer */ 363 for (i = 0; i < ARRAY_SIZE(rm->mixer_blks) && 364 lm_count < topology->num_lm; i++) { 365 if (!rm->mixer_blks[i]) 366 continue; 367 368 if (skip_dspp && to_dpu_hw_mixer(rm->mixer_blks[i])->cap->dspp) { 369 DPU_DEBUG("Skipping LM_%d, skipping LMs with DSPPs\n", i); 370 continue; 371 } 372 373 /* 374 * Reset lm_count to an even index. This will drop the previous 375 * primary mixer if failed to find its peer. 376 */ 377 lm_count &= ~1; 378 lm_idx[lm_count] = i; 379 380 if (!_dpu_rm_check_lm_and_get_connected_blks(rm, global_state, 381 crtc_id, i, &pp_idx[lm_count], 382 &dspp_idx[lm_count], topology)) { 383 continue; 384 } 385 386 ++lm_count; 387 388 /* Valid primary mixer found, find matching peers */ 389 if (lm_count < topology->num_lm) { 390 int j = _dpu_rm_get_lm_peer(rm, i); 391 392 /* ignore the peer if there is an error or if the peer was already processed */ 393 if (j < 0 || j < i) 394 continue; 395 396 if (!rm->mixer_blks[j]) 397 continue; 398 399 if (!_dpu_rm_check_lm_and_get_connected_blks(rm, 400 global_state, crtc_id, j, 401 &pp_idx[lm_count], &dspp_idx[lm_count], 402 topology)) { 403 continue; 404 } 405 406 lm_idx[lm_count] = j; 407 ++lm_count; 408 } 409 } 410 411 return lm_count == topology->num_lm; 412 } 413 414 static int _dpu_rm_reserve_lms(struct dpu_rm *rm, 415 struct dpu_global_state *global_state, 416 uint32_t crtc_id, 417 struct msm_display_topology *topology) 418 419 { 420 int lm_idx[MAX_BLOCKS]; 421 int pp_idx[MAX_BLOCKS]; 422 int dspp_idx[MAX_BLOCKS] = {0}; 423 int i; 424 bool found; 425 426 if (!topology->num_lm) { 427 DPU_ERROR("zero LMs in topology\n"); 428 return -EINVAL; 429 } 430 431 /* Try using non-DSPP LM blocks first */ 432 found = dpu_rm_find_lms(rm, global_state, crtc_id, !topology->num_dspp, 433 topology, lm_idx, pp_idx, dspp_idx); 434 if (!found && !topology->num_dspp) 435 found = dpu_rm_find_lms(rm, global_state, crtc_id, false, 436 topology, lm_idx, pp_idx, dspp_idx); 437 if (!found) { 438 DPU_DEBUG("unable to find appropriate mixers\n"); 439 return -ENAVAIL; 440 } 441 442 for (i = 0; i < topology->num_lm; i++) { 443 global_state->mixer_to_crtc_id[lm_idx[i]] = crtc_id; 444 global_state->pingpong_to_crtc_id[pp_idx[i]] = crtc_id; 445 global_state->dspp_to_crtc_id[dspp_idx[i]] = 446 topology->num_dspp ? crtc_id : 0; 447 448 trace_dpu_rm_reserve_lms(lm_idx[i] + LM_0, crtc_id, 449 pp_idx[i] + PINGPONG_0); 450 } 451 452 return 0; 453 } 454 455 static int _dpu_rm_reserve_ctls( 456 struct dpu_rm *rm, 457 struct dpu_global_state *global_state, 458 uint32_t crtc_id, 459 const struct msm_display_topology *top) 460 { 461 int ctl_idx[MAX_BLOCKS]; 462 int i = 0, j, num_ctls; 463 bool needs_split_display; 464 465 if (rm->has_legacy_ctls) { 466 /* 467 * TODO: check if there is a need for special handling if 468 * DPU < 5.0 get CWB support. 469 */ 470 num_ctls = top->num_intf; 471 472 needs_split_display = _dpu_rm_needs_split_display(top); 473 } else { 474 /* use single CTL */ 475 num_ctls = 1; 476 needs_split_display = false; 477 } 478 479 for (j = 0; j < ARRAY_SIZE(rm->ctl_blks); j++) { 480 const struct dpu_hw_ctl *ctl; 481 unsigned long features; 482 bool has_split_display; 483 484 if (!rm->ctl_blks[j]) 485 continue; 486 if (reserved_by_other(global_state->ctl_to_crtc_id, j, crtc_id)) 487 continue; 488 489 ctl = to_dpu_hw_ctl(rm->ctl_blks[j]); 490 features = ctl->caps->features; 491 has_split_display = BIT(DPU_CTL_SPLIT_DISPLAY) & features; 492 493 DPU_DEBUG("CTL_%d caps 0x%lX\n", j, features); 494 495 if (needs_split_display != has_split_display) 496 continue; 497 498 ctl_idx[i] = j; 499 DPU_DEBUG("CTL_%d match\n", j); 500 501 if (++i == num_ctls) 502 break; 503 504 } 505 506 if (i != num_ctls) 507 return -ENAVAIL; 508 509 for (i = 0; i < ARRAY_SIZE(ctl_idx) && i < num_ctls; i++) { 510 global_state->ctl_to_crtc_id[ctl_idx[i]] = crtc_id; 511 trace_dpu_rm_reserve_ctls(i + CTL_0, crtc_id); 512 } 513 514 return 0; 515 } 516 517 static int _dpu_rm_pingpong_next_index(struct dpu_global_state *global_state, 518 int start, 519 uint32_t crtc_id) 520 { 521 int i; 522 523 for (i = start; i < (PINGPONG_MAX - PINGPONG_0); i++) { 524 if (global_state->pingpong_to_crtc_id[i] == crtc_id) 525 return i; 526 } 527 528 return -ENAVAIL; 529 } 530 531 static int _dpu_rm_pingpong_dsc_check(int dsc_idx, int pp_idx) 532 { 533 /* 534 * DSC with even index must be used with the PINGPONG with even index 535 * DSC with odd index must be used with the PINGPONG with odd index 536 */ 537 if ((dsc_idx & 0x01) != (pp_idx & 0x01)) 538 return -ENAVAIL; 539 540 return 0; 541 } 542 543 static int _dpu_rm_dsc_alloc(struct dpu_rm *rm, 544 struct dpu_global_state *global_state, 545 uint32_t crtc_id, 546 const struct msm_display_topology *top) 547 { 548 int num_dsc = 0; 549 int pp_idx = 0; 550 int dsc_idx; 551 int ret; 552 553 for (dsc_idx = 0; dsc_idx < ARRAY_SIZE(rm->dsc_blks) && 554 num_dsc < top->num_dsc; dsc_idx++) { 555 if (!rm->dsc_blks[dsc_idx]) 556 continue; 557 558 if (reserved_by_other(global_state->dsc_to_crtc_id, dsc_idx, crtc_id)) 559 continue; 560 561 pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx, crtc_id); 562 if (pp_idx < 0) 563 return -ENAVAIL; 564 565 ret = _dpu_rm_pingpong_dsc_check(dsc_idx, pp_idx); 566 if (ret) 567 return -ENAVAIL; 568 569 global_state->dsc_to_crtc_id[dsc_idx] = crtc_id; 570 num_dsc++; 571 pp_idx++; 572 } 573 574 if (num_dsc < top->num_dsc) { 575 DPU_ERROR("DSC allocation failed num_dsc=%d required=%d\n", 576 num_dsc, top->num_dsc); 577 return -ENAVAIL; 578 } 579 580 return 0; 581 } 582 583 static int _dpu_rm_dsc_alloc_pair(struct dpu_rm *rm, 584 struct dpu_global_state *global_state, 585 uint32_t crtc_id, 586 const struct msm_display_topology *top) 587 { 588 int num_dsc = 0; 589 int dsc_idx, pp_idx = 0; 590 int ret; 591 592 /* only start from even dsc index */ 593 for (dsc_idx = 0; dsc_idx < ARRAY_SIZE(rm->dsc_blks) && 594 num_dsc < top->num_dsc; dsc_idx += 2) { 595 if (!rm->dsc_blks[dsc_idx] || 596 !rm->dsc_blks[dsc_idx + 1]) 597 continue; 598 599 /* consective dsc index to be paired */ 600 if (reserved_by_other(global_state->dsc_to_crtc_id, dsc_idx, crtc_id) || 601 reserved_by_other(global_state->dsc_to_crtc_id, dsc_idx + 1, crtc_id)) 602 continue; 603 604 pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx, crtc_id); 605 if (pp_idx < 0) 606 return -ENAVAIL; 607 608 ret = _dpu_rm_pingpong_dsc_check(dsc_idx, pp_idx); 609 if (ret) { 610 pp_idx = 0; 611 continue; 612 } 613 614 pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx + 1, crtc_id); 615 if (pp_idx < 0) 616 return -ENAVAIL; 617 618 ret = _dpu_rm_pingpong_dsc_check(dsc_idx + 1, pp_idx); 619 if (ret) { 620 pp_idx = 0; 621 continue; 622 } 623 624 global_state->dsc_to_crtc_id[dsc_idx] = crtc_id; 625 global_state->dsc_to_crtc_id[dsc_idx + 1] = crtc_id; 626 num_dsc += 2; 627 pp_idx++; /* start for next pair */ 628 } 629 630 if (num_dsc < top->num_dsc) { 631 DPU_ERROR("DSC allocation failed num_dsc=%d required=%d\n", 632 num_dsc, top->num_dsc); 633 return -ENAVAIL; 634 } 635 636 return 0; 637 } 638 639 static int _dpu_rm_reserve_dsc(struct dpu_rm *rm, 640 struct dpu_global_state *global_state, 641 uint32_t crtc_id, 642 const struct msm_display_topology *top) 643 { 644 if (!top->num_dsc || !top->num_intf) 645 return 0; 646 647 /* 648 * Facts: 649 * 1) no pingpong split (two layer mixers shared one pingpong) 650 * 2) DSC pair starts from even index, such as index(0,1), (2,3), etc 651 * 3) even PINGPONG connects to even DSC 652 * 4) odd PINGPONG connects to odd DSC 653 * 5) pair: encoder +--> pp_idx_0 --> dsc_idx_0 654 * +--> pp_idx_1 --> dsc_idx_1 655 */ 656 657 /* num_dsc should be either 1, 2 or 4 */ 658 if (top->num_dsc > top->num_intf) /* merge mode */ 659 return _dpu_rm_dsc_alloc_pair(rm, global_state, crtc_id, top); 660 else 661 return _dpu_rm_dsc_alloc(rm, global_state, crtc_id, top); 662 663 return 0; 664 } 665 666 static int _dpu_rm_reserve_cdm(struct dpu_rm *rm, 667 struct dpu_global_state *global_state, 668 uint32_t crtc_id, 669 int num_cdm) 670 { 671 /* try allocating only one CDM block */ 672 if (!rm->cdm_blk) { 673 DPU_ERROR("CDM block does not exist\n"); 674 return -EIO; 675 } 676 677 if (num_cdm > 1) { 678 DPU_ERROR("More than 1 INTF requesting CDM\n"); 679 return -EINVAL; 680 } 681 682 if (global_state->cdm_to_crtc_id) { 683 DPU_ERROR("CDM_0 is already allocated\n"); 684 return -EIO; 685 } 686 687 global_state->cdm_to_crtc_id = crtc_id; 688 689 return 0; 690 } 691 692 static int _dpu_rm_make_reservation( 693 struct dpu_rm *rm, 694 struct dpu_global_state *global_state, 695 uint32_t crtc_id, 696 struct msm_display_topology *topology) 697 { 698 int ret; 699 700 ret = _dpu_rm_reserve_lms(rm, global_state, crtc_id, topology); 701 if (ret) { 702 DPU_ERROR("unable to find appropriate mixers\n"); 703 return ret; 704 } 705 706 if (topology->cwb_enabled) { 707 ret = _dpu_rm_reserve_cwb_mux_and_pingpongs(rm, global_state, 708 crtc_id, topology); 709 if (ret) 710 return ret; 711 } 712 713 ret = _dpu_rm_reserve_ctls(rm, global_state, crtc_id, 714 topology); 715 if (ret) { 716 DPU_ERROR("unable to find appropriate CTL\n"); 717 return ret; 718 } 719 720 ret = _dpu_rm_reserve_dsc(rm, global_state, crtc_id, topology); 721 if (ret) 722 return ret; 723 724 if (topology->num_cdm > 0) { 725 ret = _dpu_rm_reserve_cdm(rm, global_state, crtc_id, topology->num_cdm); 726 if (ret) { 727 DPU_ERROR("unable to find CDM blk\n"); 728 return ret; 729 } 730 } 731 732 return ret; 733 } 734 735 static void _dpu_rm_clear_mapping(uint32_t *res_mapping, int cnt, 736 uint32_t crtc_id) 737 { 738 int i; 739 740 for (i = 0; i < cnt; i++) { 741 if (res_mapping[i] == crtc_id) 742 res_mapping[i] = 0; 743 } 744 } 745 746 /** 747 * dpu_rm_release - Given the encoder for the display chain, release any 748 * HW blocks previously reserved for that use case. 749 * @global_state: resources shared across multiple kms objects 750 * @crtc: DRM CRTC handle 751 * @return: 0 on Success otherwise -ERROR 752 */ 753 void dpu_rm_release(struct dpu_global_state *global_state, 754 struct drm_crtc *crtc) 755 { 756 uint32_t crtc_id = crtc->base.id; 757 758 _dpu_rm_clear_mapping(global_state->pingpong_to_crtc_id, 759 ARRAY_SIZE(global_state->pingpong_to_crtc_id), crtc_id); 760 _dpu_rm_clear_mapping(global_state->mixer_to_crtc_id, 761 ARRAY_SIZE(global_state->mixer_to_crtc_id), crtc_id); 762 _dpu_rm_clear_mapping(global_state->ctl_to_crtc_id, 763 ARRAY_SIZE(global_state->ctl_to_crtc_id), crtc_id); 764 _dpu_rm_clear_mapping(global_state->dsc_to_crtc_id, 765 ARRAY_SIZE(global_state->dsc_to_crtc_id), crtc_id); 766 _dpu_rm_clear_mapping(global_state->dspp_to_crtc_id, 767 ARRAY_SIZE(global_state->dspp_to_crtc_id), crtc_id); 768 _dpu_rm_clear_mapping(&global_state->cdm_to_crtc_id, 1, crtc_id); 769 _dpu_rm_clear_mapping(global_state->cwb_to_crtc_id, 770 ARRAY_SIZE(global_state->cwb_to_crtc_id), crtc_id); 771 } 772 773 /** 774 * dpu_rm_reserve - Given a CRTC->Encoder->Connector display chain, analyze 775 * the use connections and user requirements, specified through related 776 * topology control properties, and reserve hardware blocks to that 777 * display chain. 778 * HW blocks can then be accessed through dpu_rm_get_* functions. 779 * HW Reservations should be released via dpu_rm_release_hw. 780 * @rm: DPU Resource Manager handle 781 * @global_state: resources shared across multiple kms objects 782 * @crtc: DRM CRTC handle 783 * @topology: Pointer to topology info for the display 784 * @return: 0 on Success otherwise -ERROR 785 */ 786 int dpu_rm_reserve( 787 struct dpu_rm *rm, 788 struct dpu_global_state *global_state, 789 struct drm_crtc *crtc, 790 struct msm_display_topology *topology) 791 { 792 int ret; 793 794 if (IS_ERR(global_state)) { 795 DPU_ERROR("failed to global state\n"); 796 return PTR_ERR(global_state); 797 } 798 799 DRM_DEBUG_KMS("reserving hw for crtc %d\n", crtc->base.id); 800 801 DRM_DEBUG_KMS("num_lm: %d num_dsc: %d num_intf: %d\n", 802 topology->num_lm, topology->num_dsc, 803 topology->num_intf); 804 805 ret = _dpu_rm_make_reservation(rm, global_state, crtc->base.id, topology); 806 if (ret) 807 DPU_ERROR("failed to reserve hw resources: %d\n", ret); 808 809 return ret; 810 } 811 812 static struct dpu_hw_sspp *dpu_rm_try_sspp(struct dpu_rm *rm, 813 struct dpu_global_state *global_state, 814 struct drm_crtc *crtc, 815 struct dpu_rm_sspp_requirements *reqs, 816 unsigned int type) 817 { 818 uint32_t crtc_id = crtc->base.id; 819 struct dpu_hw_sspp *hw_sspp; 820 int i; 821 822 for (i = 0; i < ARRAY_SIZE(rm->hw_sspp); i++) { 823 if (!rm->hw_sspp[i]) 824 continue; 825 826 if (global_state->sspp_to_crtc_id[i]) 827 continue; 828 829 hw_sspp = rm->hw_sspp[i]; 830 831 if (hw_sspp->cap->type != type) 832 continue; 833 834 if (reqs->scale && !hw_sspp->cap->sblk->scaler_blk.len) 835 continue; 836 837 // TODO: QSEED2 and RGB scalers are not yet supported 838 if (reqs->scale && !hw_sspp->ops.setup_scaler) 839 continue; 840 841 if (reqs->yuv && !hw_sspp->cap->sblk->csc_blk.len) 842 continue; 843 844 if (reqs->rot90 && !(hw_sspp->cap->features & DPU_SSPP_INLINE_ROTATION)) 845 continue; 846 847 global_state->sspp_to_crtc_id[i] = crtc_id; 848 849 return rm->hw_sspp[i]; 850 } 851 852 return NULL; 853 } 854 855 /** 856 * dpu_rm_reserve_sspp - Reserve the required SSPP for the provided CRTC 857 * @rm: DPU Resource Manager handle 858 * @global_state: private global state 859 * @crtc: DRM CRTC handle 860 * @reqs: SSPP required features 861 */ 862 struct dpu_hw_sspp *dpu_rm_reserve_sspp(struct dpu_rm *rm, 863 struct dpu_global_state *global_state, 864 struct drm_crtc *crtc, 865 struct dpu_rm_sspp_requirements *reqs) 866 { 867 struct dpu_hw_sspp *hw_sspp = NULL; 868 869 if (!reqs->scale && !reqs->yuv) 870 hw_sspp = dpu_rm_try_sspp(rm, global_state, crtc, reqs, SSPP_TYPE_DMA); 871 if (!hw_sspp && !reqs->yuv) 872 hw_sspp = dpu_rm_try_sspp(rm, global_state, crtc, reqs, SSPP_TYPE_RGB); 873 if (!hw_sspp) 874 hw_sspp = dpu_rm_try_sspp(rm, global_state, crtc, reqs, SSPP_TYPE_VIG); 875 876 return hw_sspp; 877 } 878 879 /** 880 * dpu_rm_release_all_sspp - Given the CRTC, release all SSPP 881 * blocks previously reserved for that use case. 882 * @global_state: resources shared across multiple kms objects 883 * @crtc: DRM CRTC handle 884 */ 885 void dpu_rm_release_all_sspp(struct dpu_global_state *global_state, 886 struct drm_crtc *crtc) 887 { 888 uint32_t crtc_id = crtc->base.id; 889 890 _dpu_rm_clear_mapping(global_state->sspp_to_crtc_id, 891 ARRAY_SIZE(global_state->sspp_to_crtc_id), crtc_id); 892 } 893 894 static char *dpu_hw_blk_type_name[] = { 895 [DPU_HW_BLK_TOP] = "TOP", 896 [DPU_HW_BLK_SSPP] = "SSPP", 897 [DPU_HW_BLK_LM] = "LM", 898 [DPU_HW_BLK_CTL] = "CTL", 899 [DPU_HW_BLK_PINGPONG] = "pingpong", 900 [DPU_HW_BLK_INTF] = "INTF", 901 [DPU_HW_BLK_WB] = "WB", 902 [DPU_HW_BLK_DSPP] = "DSPP", 903 [DPU_HW_BLK_MERGE_3D] = "merge_3d", 904 [DPU_HW_BLK_DSC] = "DSC", 905 [DPU_HW_BLK_CDM] = "CDM", 906 [DPU_HW_BLK_MAX] = "unknown", 907 }; 908 909 /** 910 * dpu_rm_get_assigned_resources - Get hw resources of the given type that are 911 * assigned to this encoder 912 * @rm: DPU Resource Manager handle 913 * @global_state: resources shared across multiple kms objects 914 * @crtc: DRM CRTC handle 915 * @type: resource type to return data for 916 * @blks: pointer to the array to be filled by HW resources 917 * @blks_size: size of the @blks array 918 */ 919 int dpu_rm_get_assigned_resources(struct dpu_rm *rm, 920 struct dpu_global_state *global_state, struct drm_crtc *crtc, 921 enum dpu_hw_blk_type type, struct dpu_hw_blk **blks, int blks_size) 922 { 923 uint32_t crtc_id = crtc->base.id; 924 struct dpu_hw_blk **hw_blks; 925 uint32_t *hw_to_crtc_id; 926 int i, num_blks, max_blks; 927 928 switch (type) { 929 case DPU_HW_BLK_PINGPONG: 930 case DPU_HW_BLK_DCWB_PINGPONG: 931 hw_blks = rm->pingpong_blks; 932 hw_to_crtc_id = global_state->pingpong_to_crtc_id; 933 max_blks = ARRAY_SIZE(rm->pingpong_blks); 934 break; 935 case DPU_HW_BLK_LM: 936 hw_blks = rm->mixer_blks; 937 hw_to_crtc_id = global_state->mixer_to_crtc_id; 938 max_blks = ARRAY_SIZE(rm->mixer_blks); 939 break; 940 case DPU_HW_BLK_CTL: 941 hw_blks = rm->ctl_blks; 942 hw_to_crtc_id = global_state->ctl_to_crtc_id; 943 max_blks = ARRAY_SIZE(rm->ctl_blks); 944 break; 945 case DPU_HW_BLK_DSPP: 946 hw_blks = rm->dspp_blks; 947 hw_to_crtc_id = global_state->dspp_to_crtc_id; 948 max_blks = ARRAY_SIZE(rm->dspp_blks); 949 break; 950 case DPU_HW_BLK_DSC: 951 hw_blks = rm->dsc_blks; 952 hw_to_crtc_id = global_state->dsc_to_crtc_id; 953 max_blks = ARRAY_SIZE(rm->dsc_blks); 954 break; 955 case DPU_HW_BLK_CDM: 956 hw_blks = &rm->cdm_blk; 957 hw_to_crtc_id = &global_state->cdm_to_crtc_id; 958 max_blks = 1; 959 break; 960 case DPU_HW_BLK_CWB: 961 hw_blks = rm->cwb_blks; 962 hw_to_crtc_id = global_state->cwb_to_crtc_id; 963 max_blks = ARRAY_SIZE(rm->cwb_blks); 964 break; 965 default: 966 DPU_ERROR("blk type %d not managed by rm\n", type); 967 return 0; 968 } 969 970 num_blks = 0; 971 for (i = 0; i < max_blks; i++) { 972 if (hw_to_crtc_id[i] != crtc_id) 973 continue; 974 975 if (type == DPU_HW_BLK_PINGPONG) { 976 struct dpu_hw_pingpong *pp = to_dpu_hw_pingpong(hw_blks[i]); 977 978 if (pp->idx >= PINGPONG_CWB_0) 979 continue; 980 } 981 982 if (type == DPU_HW_BLK_DCWB_PINGPONG) { 983 struct dpu_hw_pingpong *pp = to_dpu_hw_pingpong(hw_blks[i]); 984 985 if (pp->idx < PINGPONG_CWB_0) 986 continue; 987 } 988 989 if (num_blks == blks_size) { 990 DPU_ERROR("More than %d %s assigned to crtc %d\n", 991 blks_size, dpu_hw_blk_type_name[type], crtc_id); 992 break; 993 } 994 if (!hw_blks[i]) { 995 DPU_ERROR("%s unavailable to assign to crtc %d\n", 996 dpu_hw_blk_type_name[type], crtc_id); 997 break; 998 } 999 blks[num_blks++] = hw_blks[i]; 1000 } 1001 1002 return num_blks; 1003 } 1004 1005 static void dpu_rm_print_state_helper(struct drm_printer *p, 1006 struct dpu_hw_blk *blk, 1007 uint32_t mapping) 1008 { 1009 if (!blk) 1010 drm_puts(p, "- "); 1011 else if (!mapping) 1012 drm_puts(p, "# "); 1013 else 1014 drm_printf(p, "%d ", mapping); 1015 } 1016 1017 1018 /** 1019 * dpu_rm_print_state - output the RM private state 1020 * @p: DRM printer 1021 * @global_state: global state 1022 */ 1023 void dpu_rm_print_state(struct drm_printer *p, 1024 const struct dpu_global_state *global_state) 1025 { 1026 const struct dpu_rm *rm = global_state->rm; 1027 int i; 1028 1029 drm_puts(p, "resource mapping:\n"); 1030 drm_puts(p, "\tpingpong="); 1031 for (i = 0; i < ARRAY_SIZE(global_state->pingpong_to_crtc_id); i++) 1032 dpu_rm_print_state_helper(p, rm->pingpong_blks[i], 1033 global_state->pingpong_to_crtc_id[i]); 1034 drm_puts(p, "\n"); 1035 1036 drm_puts(p, "\tmixer="); 1037 for (i = 0; i < ARRAY_SIZE(global_state->mixer_to_crtc_id); i++) 1038 dpu_rm_print_state_helper(p, rm->mixer_blks[i], 1039 global_state->mixer_to_crtc_id[i]); 1040 drm_puts(p, "\n"); 1041 1042 drm_puts(p, "\tctl="); 1043 for (i = 0; i < ARRAY_SIZE(global_state->ctl_to_crtc_id); i++) 1044 dpu_rm_print_state_helper(p, rm->ctl_blks[i], 1045 global_state->ctl_to_crtc_id[i]); 1046 drm_puts(p, "\n"); 1047 1048 drm_puts(p, "\tdspp="); 1049 for (i = 0; i < ARRAY_SIZE(global_state->dspp_to_crtc_id); i++) 1050 dpu_rm_print_state_helper(p, rm->dspp_blks[i], 1051 global_state->dspp_to_crtc_id[i]); 1052 drm_puts(p, "\n"); 1053 1054 drm_puts(p, "\tdsc="); 1055 for (i = 0; i < ARRAY_SIZE(global_state->dsc_to_crtc_id); i++) 1056 dpu_rm_print_state_helper(p, rm->dsc_blks[i], 1057 global_state->dsc_to_crtc_id[i]); 1058 drm_puts(p, "\n"); 1059 1060 drm_puts(p, "\tcdm="); 1061 dpu_rm_print_state_helper(p, rm->cdm_blk, 1062 global_state->cdm_to_crtc_id); 1063 drm_puts(p, "\n"); 1064 1065 drm_puts(p, "\tsspp="); 1066 /* skip SSPP_NONE and start from the next index */ 1067 for (i = SSPP_NONE + 1; i < ARRAY_SIZE(global_state->sspp_to_crtc_id); i++) 1068 dpu_rm_print_state_helper(p, rm->hw_sspp[i] ? &rm->hw_sspp[i]->base : NULL, 1069 global_state->sspp_to_crtc_id[i]); 1070 drm_puts(p, "\n"); 1071 1072 drm_puts(p, "\tcwb="); 1073 for (i = 0; i < ARRAY_SIZE(global_state->cwb_to_crtc_id); i++) 1074 dpu_rm_print_state_helper(p, rm->cwb_blks[i], 1075 global_state->cwb_to_crtc_id[i]); 1076 drm_puts(p, "\n"); 1077 } 1078