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 enc_id) 26 { 27 return res_map[idx] && res_map[idx] != enc_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 msm_mdss_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 /* Interrogate HW catalog and create tracking items for hw blocks */ 57 for (i = 0; i < cat->mixer_count; i++) { 58 struct dpu_hw_mixer *hw; 59 const struct dpu_lm_cfg *lm = &cat->mixer[i]; 60 61 hw = dpu_hw_lm_init(dev, lm, mmio); 62 if (IS_ERR(hw)) { 63 rc = PTR_ERR(hw); 64 DPU_ERROR("failed lm object creation: err %d\n", rc); 65 goto fail; 66 } 67 rm->mixer_blks[lm->id - LM_0] = &hw->base; 68 } 69 70 for (i = 0; i < cat->merge_3d_count; i++) { 71 struct dpu_hw_merge_3d *hw; 72 const struct dpu_merge_3d_cfg *merge_3d = &cat->merge_3d[i]; 73 74 hw = dpu_hw_merge_3d_init(dev, merge_3d, mmio); 75 if (IS_ERR(hw)) { 76 rc = PTR_ERR(hw); 77 DPU_ERROR("failed merge_3d object creation: err %d\n", 78 rc); 79 goto fail; 80 } 81 rm->merge_3d_blks[merge_3d->id - MERGE_3D_0] = &hw->base; 82 } 83 84 for (i = 0; i < cat->pingpong_count; i++) { 85 struct dpu_hw_pingpong *hw; 86 const struct dpu_pingpong_cfg *pp = &cat->pingpong[i]; 87 88 hw = dpu_hw_pingpong_init(dev, pp, mmio, cat->mdss_ver); 89 if (IS_ERR(hw)) { 90 rc = PTR_ERR(hw); 91 DPU_ERROR("failed pingpong object creation: err %d\n", 92 rc); 93 goto fail; 94 } 95 if (pp->merge_3d && pp->merge_3d < MERGE_3D_MAX) 96 hw->merge_3d = to_dpu_hw_merge_3d(rm->merge_3d_blks[pp->merge_3d - MERGE_3D_0]); 97 rm->pingpong_blks[pp->id - PINGPONG_0] = &hw->base; 98 } 99 100 for (i = 0; i < cat->intf_count; i++) { 101 struct dpu_hw_intf *hw; 102 const struct dpu_intf_cfg *intf = &cat->intf[i]; 103 104 hw = dpu_hw_intf_init(dev, intf, mmio, cat->mdss_ver); 105 if (IS_ERR(hw)) { 106 rc = PTR_ERR(hw); 107 DPU_ERROR("failed intf object creation: err %d\n", rc); 108 goto fail; 109 } 110 rm->hw_intf[intf->id - INTF_0] = hw; 111 } 112 113 for (i = 0; i < cat->wb_count; i++) { 114 struct dpu_hw_wb *hw; 115 const struct dpu_wb_cfg *wb = &cat->wb[i]; 116 117 hw = dpu_hw_wb_init(dev, wb, mmio, cat->mdss_ver); 118 if (IS_ERR(hw)) { 119 rc = PTR_ERR(hw); 120 DPU_ERROR("failed wb object creation: err %d\n", rc); 121 goto fail; 122 } 123 rm->hw_wb[wb->id - WB_0] = hw; 124 } 125 126 for (i = 0; i < cat->cwb_count; i++) { 127 struct dpu_hw_cwb *hw; 128 const struct dpu_cwb_cfg *cwb = &cat->cwb[i]; 129 130 hw = dpu_hw_cwb_init(dev, cwb, mmio); 131 if (IS_ERR(hw)) { 132 rc = PTR_ERR(hw); 133 DPU_ERROR("failed cwb object creation: err %d\n", rc); 134 goto fail; 135 } 136 rm->cwb_blks[cwb->id - CWB_0] = &hw->base; 137 } 138 139 for (i = 0; i < cat->ctl_count; i++) { 140 struct dpu_hw_ctl *hw; 141 const struct dpu_ctl_cfg *ctl = &cat->ctl[i]; 142 143 hw = dpu_hw_ctl_init(dev, ctl, mmio, cat->mixer_count, cat->mixer); 144 if (IS_ERR(hw)) { 145 rc = PTR_ERR(hw); 146 DPU_ERROR("failed ctl object creation: err %d\n", rc); 147 goto fail; 148 } 149 rm->ctl_blks[ctl->id - CTL_0] = &hw->base; 150 } 151 152 for (i = 0; i < cat->dspp_count; i++) { 153 struct dpu_hw_dspp *hw; 154 const struct dpu_dspp_cfg *dspp = &cat->dspp[i]; 155 156 hw = dpu_hw_dspp_init(dev, dspp, mmio); 157 if (IS_ERR(hw)) { 158 rc = PTR_ERR(hw); 159 DPU_ERROR("failed dspp object creation: err %d\n", rc); 160 goto fail; 161 } 162 rm->dspp_blks[dspp->id - DSPP_0] = &hw->base; 163 } 164 165 for (i = 0; i < cat->dsc_count; i++) { 166 struct dpu_hw_dsc *hw; 167 const struct dpu_dsc_cfg *dsc = &cat->dsc[i]; 168 169 if (test_bit(DPU_DSC_HW_REV_1_2, &dsc->features)) 170 hw = dpu_hw_dsc_init_1_2(dev, dsc, mmio); 171 else 172 hw = dpu_hw_dsc_init(dev, dsc, mmio); 173 174 if (IS_ERR(hw)) { 175 rc = PTR_ERR(hw); 176 DPU_ERROR("failed dsc object creation: err %d\n", rc); 177 goto fail; 178 } 179 rm->dsc_blks[dsc->id - DSC_0] = &hw->base; 180 } 181 182 for (i = 0; i < cat->sspp_count; i++) { 183 struct dpu_hw_sspp *hw; 184 const struct dpu_sspp_cfg *sspp = &cat->sspp[i]; 185 186 hw = dpu_hw_sspp_init(dev, sspp, mmio, mdss_data, cat->mdss_ver); 187 if (IS_ERR(hw)) { 188 rc = PTR_ERR(hw); 189 DPU_ERROR("failed sspp object creation: err %d\n", rc); 190 goto fail; 191 } 192 rm->hw_sspp[sspp->id - SSPP_NONE] = hw; 193 } 194 195 if (cat->cdm) { 196 struct dpu_hw_cdm *hw; 197 198 hw = dpu_hw_cdm_init(dev, cat->cdm, mmio, cat->mdss_ver); 199 if (IS_ERR(hw)) { 200 rc = PTR_ERR(hw); 201 DPU_ERROR("failed cdm object creation: err %d\n", rc); 202 goto fail; 203 } 204 rm->cdm_blk = &hw->base; 205 } 206 207 return 0; 208 209 fail: 210 return rc ? rc : -EFAULT; 211 } 212 213 static bool _dpu_rm_needs_split_display(const struct msm_display_topology *top) 214 { 215 return top->num_intf > 1; 216 } 217 218 /** 219 * _dpu_rm_get_lm_peer - get the id of a mixer which is a peer of the primary 220 * @rm: dpu resource manager handle 221 * @primary_idx: index of primary mixer in rm->mixer_blks[] 222 * 223 * Returns: lm peer mixed id on success or %-EINVAL on error 224 */ 225 static int _dpu_rm_get_lm_peer(struct dpu_rm *rm, int primary_idx) 226 { 227 const struct dpu_lm_cfg *prim_lm_cfg; 228 229 prim_lm_cfg = to_dpu_hw_mixer(rm->mixer_blks[primary_idx])->cap; 230 231 if (prim_lm_cfg->lm_pair >= LM_0 && prim_lm_cfg->lm_pair < LM_MAX) 232 return prim_lm_cfg->lm_pair - LM_0; 233 return -EINVAL; 234 } 235 236 /** 237 * _dpu_rm_check_lm_and_get_connected_blks - check if proposed layer mixer meets 238 * proposed use case requirements, incl. hardwired dependent blocks like 239 * pingpong 240 * @rm: dpu resource manager handle 241 * @global_state: resources shared across multiple kms objects 242 * @enc_id: encoder id requesting for allocation 243 * @lm_idx: index of proposed layer mixer in rm->mixer_blks[], function checks 244 * if lm, and all other hardwired blocks connected to the lm (pp) is 245 * available and appropriate 246 * @pp_idx: output parameter, index of pingpong block attached to the layer 247 * mixer in rm->pingpong_blks[]. 248 * @dspp_idx: output parameter, index of dspp block attached to the layer 249 * mixer in rm->dspp_blks[]. 250 * @topology: selected topology for the display 251 * Return: true if lm matches all requirements, false otherwise 252 */ 253 static bool _dpu_rm_check_lm_and_get_connected_blks(struct dpu_rm *rm, 254 struct dpu_global_state *global_state, 255 uint32_t enc_id, int lm_idx, int *pp_idx, int *dspp_idx, 256 struct msm_display_topology *topology) 257 { 258 const struct dpu_lm_cfg *lm_cfg; 259 int idx; 260 261 /* Already reserved? */ 262 if (reserved_by_other(global_state->mixer_to_enc_id, lm_idx, enc_id)) { 263 DPU_DEBUG("lm %d already reserved\n", lm_idx + LM_0); 264 return false; 265 } 266 267 lm_cfg = to_dpu_hw_mixer(rm->mixer_blks[lm_idx])->cap; 268 idx = lm_cfg->pingpong - PINGPONG_0; 269 if (idx < 0 || idx >= ARRAY_SIZE(rm->pingpong_blks)) { 270 DPU_ERROR("failed to get pp on lm %d\n", lm_cfg->pingpong); 271 return false; 272 } 273 274 if (reserved_by_other(global_state->pingpong_to_enc_id, idx, enc_id)) { 275 DPU_DEBUG("lm %d pp %d already reserved\n", lm_cfg->id, 276 lm_cfg->pingpong); 277 return false; 278 } 279 *pp_idx = idx; 280 281 if (!topology->num_dspp) 282 return true; 283 284 idx = lm_cfg->dspp - DSPP_0; 285 if (idx < 0 || idx >= ARRAY_SIZE(rm->dspp_blks)) { 286 DPU_ERROR("failed to get dspp on lm %d\n", lm_cfg->dspp); 287 return false; 288 } 289 290 if (reserved_by_other(global_state->dspp_to_enc_id, idx, enc_id)) { 291 DPU_DEBUG("lm %d dspp %d already reserved\n", lm_cfg->id, 292 lm_cfg->dspp); 293 return false; 294 } 295 *dspp_idx = idx; 296 297 return true; 298 } 299 300 static int _dpu_rm_reserve_lms(struct dpu_rm *rm, 301 struct dpu_global_state *global_state, 302 uint32_t enc_id, 303 struct msm_display_topology *topology) 304 305 { 306 int lm_idx[MAX_BLOCKS]; 307 int pp_idx[MAX_BLOCKS]; 308 int dspp_idx[MAX_BLOCKS] = {0}; 309 int i, lm_count = 0; 310 311 if (!topology->num_lm) { 312 DPU_ERROR("invalid number of lm: %d\n", topology->num_lm); 313 return -EINVAL; 314 } 315 316 /* Find a primary mixer */ 317 for (i = 0; i < ARRAY_SIZE(rm->mixer_blks) && 318 lm_count < topology->num_lm; i++) { 319 if (!rm->mixer_blks[i]) 320 continue; 321 322 lm_count = 0; 323 lm_idx[lm_count] = i; 324 325 if (!_dpu_rm_check_lm_and_get_connected_blks(rm, global_state, 326 enc_id, i, &pp_idx[lm_count], 327 &dspp_idx[lm_count], topology)) { 328 continue; 329 } 330 331 ++lm_count; 332 333 /* Valid primary mixer found, find matching peers */ 334 if (lm_count < topology->num_lm) { 335 int j = _dpu_rm_get_lm_peer(rm, i); 336 337 /* ignore the peer if there is an error or if the peer was already processed */ 338 if (j < 0 || j < i) 339 continue; 340 341 if (!rm->mixer_blks[j]) 342 continue; 343 344 if (!_dpu_rm_check_lm_and_get_connected_blks(rm, 345 global_state, enc_id, j, 346 &pp_idx[lm_count], &dspp_idx[lm_count], 347 topology)) { 348 continue; 349 } 350 351 lm_idx[lm_count] = j; 352 ++lm_count; 353 } 354 } 355 356 if (lm_count != topology->num_lm) { 357 DPU_DEBUG("unable to find appropriate mixers\n"); 358 return -ENAVAIL; 359 } 360 361 for (i = 0; i < lm_count; i++) { 362 global_state->mixer_to_enc_id[lm_idx[i]] = enc_id; 363 global_state->pingpong_to_enc_id[pp_idx[i]] = enc_id; 364 global_state->dspp_to_enc_id[dspp_idx[i]] = 365 topology->num_dspp ? enc_id : 0; 366 367 trace_dpu_rm_reserve_lms(lm_idx[i] + LM_0, enc_id, 368 pp_idx[i] + PINGPONG_0); 369 } 370 371 return 0; 372 } 373 374 static int _dpu_rm_reserve_ctls( 375 struct dpu_rm *rm, 376 struct dpu_global_state *global_state, 377 uint32_t enc_id, 378 const struct msm_display_topology *top) 379 { 380 int ctl_idx[MAX_BLOCKS]; 381 int i = 0, j, num_ctls; 382 bool needs_split_display; 383 384 /* each hw_intf needs its own hw_ctrl to program its control path */ 385 num_ctls = top->num_intf; 386 387 needs_split_display = _dpu_rm_needs_split_display(top); 388 389 for (j = 0; j < ARRAY_SIZE(rm->ctl_blks); j++) { 390 const struct dpu_hw_ctl *ctl; 391 unsigned long features; 392 bool has_split_display; 393 394 if (!rm->ctl_blks[j]) 395 continue; 396 if (reserved_by_other(global_state->ctl_to_enc_id, j, enc_id)) 397 continue; 398 399 ctl = to_dpu_hw_ctl(rm->ctl_blks[j]); 400 features = ctl->caps->features; 401 has_split_display = BIT(DPU_CTL_SPLIT_DISPLAY) & features; 402 403 DPU_DEBUG("ctl %d caps 0x%lX\n", j + CTL_0, features); 404 405 if (needs_split_display != has_split_display) 406 continue; 407 408 ctl_idx[i] = j; 409 DPU_DEBUG("ctl %d match\n", j + CTL_0); 410 411 if (++i == num_ctls) 412 break; 413 414 } 415 416 if (i != num_ctls) 417 return -ENAVAIL; 418 419 for (i = 0; i < ARRAY_SIZE(ctl_idx) && i < num_ctls; i++) { 420 global_state->ctl_to_enc_id[ctl_idx[i]] = enc_id; 421 trace_dpu_rm_reserve_ctls(i + CTL_0, enc_id); 422 } 423 424 return 0; 425 } 426 427 static int _dpu_rm_pingpong_next_index(struct dpu_global_state *global_state, 428 int start, 429 uint32_t enc_id) 430 { 431 int i; 432 433 for (i = start; i < (PINGPONG_MAX - PINGPONG_0); i++) { 434 if (global_state->pingpong_to_enc_id[i] == enc_id) 435 return i; 436 } 437 438 return -ENAVAIL; 439 } 440 441 static int _dpu_rm_pingpong_dsc_check(int dsc_idx, int pp_idx) 442 { 443 /* 444 * DSC with even index must be used with the PINGPONG with even index 445 * DSC with odd index must be used with the PINGPONG with odd index 446 */ 447 if ((dsc_idx & 0x01) != (pp_idx & 0x01)) 448 return -ENAVAIL; 449 450 return 0; 451 } 452 453 static int _dpu_rm_dsc_alloc(struct dpu_rm *rm, 454 struct dpu_global_state *global_state, 455 uint32_t enc_id, 456 const struct msm_display_topology *top) 457 { 458 int num_dsc = 0; 459 int pp_idx = 0; 460 int dsc_idx; 461 int ret; 462 463 for (dsc_idx = 0; dsc_idx < ARRAY_SIZE(rm->dsc_blks) && 464 num_dsc < top->num_dsc; dsc_idx++) { 465 if (!rm->dsc_blks[dsc_idx]) 466 continue; 467 468 if (reserved_by_other(global_state->dsc_to_enc_id, dsc_idx, enc_id)) 469 continue; 470 471 pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx, enc_id); 472 if (pp_idx < 0) 473 return -ENAVAIL; 474 475 ret = _dpu_rm_pingpong_dsc_check(dsc_idx, pp_idx); 476 if (ret) 477 return -ENAVAIL; 478 479 global_state->dsc_to_enc_id[dsc_idx] = enc_id; 480 num_dsc++; 481 pp_idx++; 482 } 483 484 if (num_dsc < top->num_dsc) { 485 DPU_ERROR("DSC allocation failed num_dsc=%d required=%d\n", 486 num_dsc, top->num_dsc); 487 return -ENAVAIL; 488 } 489 490 return 0; 491 } 492 493 static int _dpu_rm_dsc_alloc_pair(struct dpu_rm *rm, 494 struct dpu_global_state *global_state, 495 uint32_t enc_id, 496 const struct msm_display_topology *top) 497 { 498 int num_dsc = 0; 499 int dsc_idx, pp_idx = 0; 500 int ret; 501 502 /* only start from even dsc index */ 503 for (dsc_idx = 0; dsc_idx < ARRAY_SIZE(rm->dsc_blks) && 504 num_dsc < top->num_dsc; dsc_idx += 2) { 505 if (!rm->dsc_blks[dsc_idx] || 506 !rm->dsc_blks[dsc_idx + 1]) 507 continue; 508 509 /* consective dsc index to be paired */ 510 if (reserved_by_other(global_state->dsc_to_enc_id, dsc_idx, enc_id) || 511 reserved_by_other(global_state->dsc_to_enc_id, dsc_idx + 1, enc_id)) 512 continue; 513 514 pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx, enc_id); 515 if (pp_idx < 0) 516 return -ENAVAIL; 517 518 ret = _dpu_rm_pingpong_dsc_check(dsc_idx, pp_idx); 519 if (ret) { 520 pp_idx = 0; 521 continue; 522 } 523 524 pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx + 1, enc_id); 525 if (pp_idx < 0) 526 return -ENAVAIL; 527 528 ret = _dpu_rm_pingpong_dsc_check(dsc_idx + 1, pp_idx); 529 if (ret) { 530 pp_idx = 0; 531 continue; 532 } 533 534 global_state->dsc_to_enc_id[dsc_idx] = enc_id; 535 global_state->dsc_to_enc_id[dsc_idx + 1] = enc_id; 536 num_dsc += 2; 537 pp_idx++; /* start for next pair */ 538 } 539 540 if (num_dsc < top->num_dsc) { 541 DPU_ERROR("DSC allocation failed num_dsc=%d required=%d\n", 542 num_dsc, top->num_dsc); 543 return -ENAVAIL; 544 } 545 546 return 0; 547 } 548 549 static int _dpu_rm_reserve_dsc(struct dpu_rm *rm, 550 struct dpu_global_state *global_state, 551 struct drm_encoder *enc, 552 const struct msm_display_topology *top) 553 { 554 uint32_t enc_id = enc->base.id; 555 556 if (!top->num_dsc || !top->num_intf) 557 return 0; 558 559 /* 560 * Facts: 561 * 1) no pingpong split (two layer mixers shared one pingpong) 562 * 2) DSC pair starts from even index, such as index(0,1), (2,3), etc 563 * 3) even PINGPONG connects to even DSC 564 * 4) odd PINGPONG connects to odd DSC 565 * 5) pair: encoder +--> pp_idx_0 --> dsc_idx_0 566 * +--> pp_idx_1 --> dsc_idx_1 567 */ 568 569 /* num_dsc should be either 1, 2 or 4 */ 570 if (top->num_dsc > top->num_intf) /* merge mode */ 571 return _dpu_rm_dsc_alloc_pair(rm, global_state, enc_id, top); 572 else 573 return _dpu_rm_dsc_alloc(rm, global_state, enc_id, top); 574 575 return 0; 576 } 577 578 static int _dpu_rm_reserve_cdm(struct dpu_rm *rm, 579 struct dpu_global_state *global_state, 580 struct drm_encoder *enc) 581 { 582 /* try allocating only one CDM block */ 583 if (!rm->cdm_blk) { 584 DPU_ERROR("CDM block does not exist\n"); 585 return -EIO; 586 } 587 588 if (global_state->cdm_to_enc_id) { 589 DPU_ERROR("CDM_0 is already allocated\n"); 590 return -EIO; 591 } 592 593 global_state->cdm_to_enc_id = enc->base.id; 594 595 return 0; 596 } 597 598 static int _dpu_rm_make_reservation( 599 struct dpu_rm *rm, 600 struct dpu_global_state *global_state, 601 struct drm_encoder *enc, 602 struct msm_display_topology *topology) 603 { 604 int ret; 605 606 ret = _dpu_rm_reserve_lms(rm, global_state, enc->base.id, topology); 607 if (ret) { 608 DPU_ERROR("unable to find appropriate mixers\n"); 609 return ret; 610 } 611 612 ret = _dpu_rm_reserve_ctls(rm, global_state, enc->base.id, 613 topology); 614 if (ret) { 615 DPU_ERROR("unable to find appropriate CTL\n"); 616 return ret; 617 } 618 619 ret = _dpu_rm_reserve_dsc(rm, global_state, enc, topology); 620 if (ret) 621 return ret; 622 623 if (topology->needs_cdm) { 624 ret = _dpu_rm_reserve_cdm(rm, global_state, enc); 625 if (ret) { 626 DPU_ERROR("unable to find CDM blk\n"); 627 return ret; 628 } 629 } 630 631 return ret; 632 } 633 634 static void _dpu_rm_clear_mapping(uint32_t *res_mapping, int cnt, 635 uint32_t enc_id) 636 { 637 int i; 638 639 for (i = 0; i < cnt; i++) { 640 if (res_mapping[i] == enc_id) 641 res_mapping[i] = 0; 642 } 643 } 644 645 /** 646 * dpu_rm_release - Given the encoder for the display chain, release any 647 * HW blocks previously reserved for that use case. 648 * @global_state: resources shared across multiple kms objects 649 * @enc: DRM Encoder handle 650 * @return: 0 on Success otherwise -ERROR 651 */ 652 void dpu_rm_release(struct dpu_global_state *global_state, 653 struct drm_encoder *enc) 654 { 655 _dpu_rm_clear_mapping(global_state->pingpong_to_enc_id, 656 ARRAY_SIZE(global_state->pingpong_to_enc_id), enc->base.id); 657 _dpu_rm_clear_mapping(global_state->mixer_to_enc_id, 658 ARRAY_SIZE(global_state->mixer_to_enc_id), enc->base.id); 659 _dpu_rm_clear_mapping(global_state->ctl_to_enc_id, 660 ARRAY_SIZE(global_state->ctl_to_enc_id), enc->base.id); 661 _dpu_rm_clear_mapping(global_state->dsc_to_enc_id, 662 ARRAY_SIZE(global_state->dsc_to_enc_id), enc->base.id); 663 _dpu_rm_clear_mapping(global_state->dspp_to_enc_id, 664 ARRAY_SIZE(global_state->dspp_to_enc_id), enc->base.id); 665 _dpu_rm_clear_mapping(&global_state->cdm_to_enc_id, 1, enc->base.id); 666 } 667 668 /** 669 * dpu_rm_reserve - Given a CRTC->Encoder->Connector display chain, analyze 670 * the use connections and user requirements, specified through related 671 * topology control properties, and reserve hardware blocks to that 672 * display chain. 673 * HW blocks can then be accessed through dpu_rm_get_* functions. 674 * HW Reservations should be released via dpu_rm_release_hw. 675 * @rm: DPU Resource Manager handle 676 * @global_state: resources shared across multiple kms objects 677 * @enc: DRM Encoder handle 678 * @crtc_state: Proposed Atomic DRM CRTC State handle 679 * @topology: Pointer to topology info for the display 680 * @return: 0 on Success otherwise -ERROR 681 */ 682 int dpu_rm_reserve( 683 struct dpu_rm *rm, 684 struct dpu_global_state *global_state, 685 struct drm_encoder *enc, 686 struct drm_crtc_state *crtc_state, 687 struct msm_display_topology *topology) 688 { 689 int ret; 690 691 /* Check if this is just a page-flip */ 692 if (!drm_atomic_crtc_needs_modeset(crtc_state)) 693 return 0; 694 695 if (IS_ERR(global_state)) { 696 DPU_ERROR("failed to global state\n"); 697 return PTR_ERR(global_state); 698 } 699 700 DRM_DEBUG_KMS("reserving hw for enc %d crtc %d\n", 701 enc->base.id, crtc_state->crtc->base.id); 702 703 DRM_DEBUG_KMS("num_lm: %d num_dsc: %d num_intf: %d\n", 704 topology->num_lm, topology->num_dsc, 705 topology->num_intf); 706 707 ret = _dpu_rm_make_reservation(rm, global_state, enc, topology); 708 if (ret) 709 DPU_ERROR("failed to reserve hw resources: %d\n", ret); 710 711 712 713 return ret; 714 } 715 716 static struct dpu_hw_sspp *dpu_rm_try_sspp(struct dpu_rm *rm, 717 struct dpu_global_state *global_state, 718 struct drm_crtc *crtc, 719 struct dpu_rm_sspp_requirements *reqs, 720 unsigned int type) 721 { 722 uint32_t crtc_id = crtc->base.id; 723 struct dpu_hw_sspp *hw_sspp; 724 int i; 725 726 for (i = 0; i < ARRAY_SIZE(rm->hw_sspp); i++) { 727 if (!rm->hw_sspp[i]) 728 continue; 729 730 if (global_state->sspp_to_crtc_id[i]) 731 continue; 732 733 hw_sspp = rm->hw_sspp[i]; 734 735 if (hw_sspp->cap->type != type) 736 continue; 737 738 if (reqs->scale && !hw_sspp->cap->sblk->scaler_blk.len) 739 continue; 740 741 // TODO: QSEED2 and RGB scalers are not yet supported 742 if (reqs->scale && !hw_sspp->ops.setup_scaler) 743 continue; 744 745 if (reqs->yuv && !hw_sspp->cap->sblk->csc_blk.len) 746 continue; 747 748 if (reqs->rot90 && !(hw_sspp->cap->features & DPU_SSPP_INLINE_ROTATION)) 749 continue; 750 751 global_state->sspp_to_crtc_id[i] = crtc_id; 752 753 return rm->hw_sspp[i]; 754 } 755 756 return NULL; 757 } 758 759 /** 760 * dpu_rm_reserve_sspp - Reserve the required SSPP for the provided CRTC 761 * @rm: DPU Resource Manager handle 762 * @global_state: private global state 763 * @crtc: DRM CRTC handle 764 * @reqs: SSPP required features 765 */ 766 struct dpu_hw_sspp *dpu_rm_reserve_sspp(struct dpu_rm *rm, 767 struct dpu_global_state *global_state, 768 struct drm_crtc *crtc, 769 struct dpu_rm_sspp_requirements *reqs) 770 { 771 struct dpu_hw_sspp *hw_sspp = NULL; 772 773 if (!reqs->scale && !reqs->yuv) 774 hw_sspp = dpu_rm_try_sspp(rm, global_state, crtc, reqs, SSPP_TYPE_DMA); 775 if (!hw_sspp && reqs->scale) 776 hw_sspp = dpu_rm_try_sspp(rm, global_state, crtc, reqs, SSPP_TYPE_RGB); 777 if (!hw_sspp) 778 hw_sspp = dpu_rm_try_sspp(rm, global_state, crtc, reqs, SSPP_TYPE_VIG); 779 780 return hw_sspp; 781 } 782 783 /** 784 * dpu_rm_release_all_sspp - Given the CRTC, release all SSPP 785 * blocks previously reserved for that use case. 786 * @global_state: resources shared across multiple kms objects 787 * @crtc: DRM CRTC handle 788 */ 789 void dpu_rm_release_all_sspp(struct dpu_global_state *global_state, 790 struct drm_crtc *crtc) 791 { 792 uint32_t crtc_id = crtc->base.id; 793 794 _dpu_rm_clear_mapping(global_state->sspp_to_crtc_id, 795 ARRAY_SIZE(global_state->sspp_to_crtc_id), crtc_id); 796 } 797 798 /** 799 * dpu_rm_get_assigned_resources - Get hw resources of the given type that are 800 * assigned to this encoder 801 * @rm: DPU Resource Manager handle 802 * @global_state: resources shared across multiple kms objects 803 * @enc_id: encoder id requesting for allocation 804 * @type: resource type to return data for 805 * @blks: pointer to the array to be filled by HW resources 806 * @blks_size: size of the @blks array 807 */ 808 int dpu_rm_get_assigned_resources(struct dpu_rm *rm, 809 struct dpu_global_state *global_state, uint32_t enc_id, 810 enum dpu_hw_blk_type type, struct dpu_hw_blk **blks, int blks_size) 811 { 812 struct dpu_hw_blk **hw_blks; 813 uint32_t *hw_to_enc_id; 814 int i, num_blks, max_blks; 815 816 switch (type) { 817 case DPU_HW_BLK_PINGPONG: 818 hw_blks = rm->pingpong_blks; 819 hw_to_enc_id = global_state->pingpong_to_enc_id; 820 max_blks = ARRAY_SIZE(rm->pingpong_blks); 821 break; 822 case DPU_HW_BLK_LM: 823 hw_blks = rm->mixer_blks; 824 hw_to_enc_id = global_state->mixer_to_enc_id; 825 max_blks = ARRAY_SIZE(rm->mixer_blks); 826 break; 827 case DPU_HW_BLK_CTL: 828 hw_blks = rm->ctl_blks; 829 hw_to_enc_id = global_state->ctl_to_enc_id; 830 max_blks = ARRAY_SIZE(rm->ctl_blks); 831 break; 832 case DPU_HW_BLK_DSPP: 833 hw_blks = rm->dspp_blks; 834 hw_to_enc_id = global_state->dspp_to_enc_id; 835 max_blks = ARRAY_SIZE(rm->dspp_blks); 836 break; 837 case DPU_HW_BLK_DSC: 838 hw_blks = rm->dsc_blks; 839 hw_to_enc_id = global_state->dsc_to_enc_id; 840 max_blks = ARRAY_SIZE(rm->dsc_blks); 841 break; 842 case DPU_HW_BLK_CDM: 843 hw_blks = &rm->cdm_blk; 844 hw_to_enc_id = &global_state->cdm_to_enc_id; 845 max_blks = 1; 846 break; 847 default: 848 DPU_ERROR("blk type %d not managed by rm\n", type); 849 return 0; 850 } 851 852 num_blks = 0; 853 for (i = 0; i < max_blks; i++) { 854 if (hw_to_enc_id[i] != enc_id) 855 continue; 856 857 if (num_blks == blks_size) { 858 DPU_ERROR("More than %d resources assigned to enc %d\n", 859 blks_size, enc_id); 860 break; 861 } 862 if (!hw_blks[i]) { 863 DPU_ERROR("Allocated resource %d unavailable to assign to enc %d\n", 864 type, enc_id); 865 break; 866 } 867 blks[num_blks++] = hw_blks[i]; 868 } 869 870 return num_blks; 871 } 872 873 static void dpu_rm_print_state_helper(struct drm_printer *p, 874 struct dpu_hw_blk *blk, 875 uint32_t mapping) 876 { 877 if (!blk) 878 drm_puts(p, "- "); 879 else if (!mapping) 880 drm_puts(p, "# "); 881 else 882 drm_printf(p, "%d ", mapping); 883 } 884 885 886 /** 887 * dpu_rm_print_state - output the RM private state 888 * @p: DRM printer 889 * @global_state: global state 890 */ 891 void dpu_rm_print_state(struct drm_printer *p, 892 const struct dpu_global_state *global_state) 893 { 894 const struct dpu_rm *rm = global_state->rm; 895 int i; 896 897 drm_puts(p, "resource mapping:\n"); 898 drm_puts(p, "\tpingpong="); 899 for (i = 0; i < ARRAY_SIZE(global_state->pingpong_to_enc_id); i++) 900 dpu_rm_print_state_helper(p, rm->pingpong_blks[i], 901 global_state->pingpong_to_enc_id[i]); 902 drm_puts(p, "\n"); 903 904 drm_puts(p, "\tmixer="); 905 for (i = 0; i < ARRAY_SIZE(global_state->mixer_to_enc_id); i++) 906 dpu_rm_print_state_helper(p, rm->mixer_blks[i], 907 global_state->mixer_to_enc_id[i]); 908 drm_puts(p, "\n"); 909 910 drm_puts(p, "\tctl="); 911 for (i = 0; i < ARRAY_SIZE(global_state->ctl_to_enc_id); i++) 912 dpu_rm_print_state_helper(p, rm->ctl_blks[i], 913 global_state->ctl_to_enc_id[i]); 914 drm_puts(p, "\n"); 915 916 drm_puts(p, "\tdspp="); 917 for (i = 0; i < ARRAY_SIZE(global_state->dspp_to_enc_id); i++) 918 dpu_rm_print_state_helper(p, rm->dspp_blks[i], 919 global_state->dspp_to_enc_id[i]); 920 drm_puts(p, "\n"); 921 922 drm_puts(p, "\tdsc="); 923 for (i = 0; i < ARRAY_SIZE(global_state->dsc_to_enc_id); i++) 924 dpu_rm_print_state_helper(p, rm->dsc_blks[i], 925 global_state->dsc_to_enc_id[i]); 926 drm_puts(p, "\n"); 927 928 drm_puts(p, "\tcdm="); 929 dpu_rm_print_state_helper(p, rm->cdm_blk, 930 global_state->cdm_to_enc_id); 931 drm_puts(p, "\n"); 932 933 drm_puts(p, "\tsspp="); 934 /* skip SSPP_NONE and start from the next index */ 935 for (i = SSPP_NONE + 1; i < ARRAY_SIZE(global_state->sspp_to_crtc_id); i++) 936 dpu_rm_print_state_helper(p, rm->hw_sspp[i] ? &rm->hw_sspp[i]->base : NULL, 937 global_state->sspp_to_crtc_id[i]); 938 drm_puts(p, "\n"); 939 } 940