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 + LM_0); 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)) { 325 DPU_ERROR("failed to get pp on lm %d\n", lm_cfg->pingpong); 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_cfg->id, 331 lm_cfg->pingpong); 332 return false; 333 } 334 *pp_idx = idx; 335 336 if (!topology->num_dspp) 337 return true; 338 339 idx = lm_cfg->dspp - DSPP_0; 340 if (idx < 0 || idx >= ARRAY_SIZE(rm->dspp_blks)) { 341 DPU_ERROR("failed to get dspp on lm %d\n", lm_cfg->dspp); 342 return false; 343 } 344 345 if (reserved_by_other(global_state->dspp_to_crtc_id, idx, crtc_id)) { 346 DPU_DEBUG("lm %d dspp %d already reserved\n", lm_cfg->id, 347 lm_cfg->dspp); 348 return false; 349 } 350 *dspp_idx = idx; 351 352 return true; 353 } 354 355 static int _dpu_rm_reserve_lms(struct dpu_rm *rm, 356 struct dpu_global_state *global_state, 357 uint32_t crtc_id, 358 struct msm_display_topology *topology) 359 360 { 361 int lm_idx[MAX_BLOCKS]; 362 int pp_idx[MAX_BLOCKS]; 363 int dspp_idx[MAX_BLOCKS] = {0}; 364 int i, lm_count = 0; 365 366 if (!topology->num_lm) { 367 DPU_ERROR("invalid number of lm: %d\n", topology->num_lm); 368 return -EINVAL; 369 } 370 371 /* Find a primary mixer */ 372 for (i = 0; i < ARRAY_SIZE(rm->mixer_blks) && 373 lm_count < topology->num_lm; i++) { 374 if (!rm->mixer_blks[i]) 375 continue; 376 377 /* 378 * Reset lm_count to an even index. This will drop the previous 379 * primary mixer if failed to find its peer. 380 */ 381 lm_count &= ~1; 382 lm_idx[lm_count] = i; 383 384 if (!_dpu_rm_check_lm_and_get_connected_blks(rm, global_state, 385 crtc_id, i, &pp_idx[lm_count], 386 &dspp_idx[lm_count], topology)) { 387 continue; 388 } 389 390 ++lm_count; 391 392 /* Valid primary mixer found, find matching peers */ 393 if (lm_count < topology->num_lm) { 394 int j = _dpu_rm_get_lm_peer(rm, i); 395 396 /* ignore the peer if there is an error or if the peer was already processed */ 397 if (j < 0 || j < i) 398 continue; 399 400 if (!rm->mixer_blks[j]) 401 continue; 402 403 if (!_dpu_rm_check_lm_and_get_connected_blks(rm, 404 global_state, crtc_id, j, 405 &pp_idx[lm_count], &dspp_idx[lm_count], 406 topology)) { 407 continue; 408 } 409 410 lm_idx[lm_count] = j; 411 ++lm_count; 412 } 413 } 414 415 if (lm_count != topology->num_lm) { 416 DPU_DEBUG("unable to find appropriate mixers\n"); 417 return -ENAVAIL; 418 } 419 420 for (i = 0; i < lm_count; i++) { 421 global_state->mixer_to_crtc_id[lm_idx[i]] = crtc_id; 422 global_state->pingpong_to_crtc_id[pp_idx[i]] = crtc_id; 423 global_state->dspp_to_crtc_id[dspp_idx[i]] = 424 topology->num_dspp ? crtc_id : 0; 425 426 trace_dpu_rm_reserve_lms(lm_idx[i] + LM_0, crtc_id, 427 pp_idx[i] + PINGPONG_0); 428 } 429 430 return 0; 431 } 432 433 static int _dpu_rm_reserve_ctls( 434 struct dpu_rm *rm, 435 struct dpu_global_state *global_state, 436 uint32_t crtc_id, 437 const struct msm_display_topology *top) 438 { 439 int ctl_idx[MAX_BLOCKS]; 440 int i = 0, j, num_ctls; 441 bool needs_split_display; 442 443 if (rm->has_legacy_ctls) { 444 /* 445 * TODO: check if there is a need for special handling if 446 * DPU < 5.0 get CWB support. 447 */ 448 num_ctls = top->num_intf; 449 450 needs_split_display = _dpu_rm_needs_split_display(top); 451 } else { 452 /* use single CTL */ 453 num_ctls = 1; 454 needs_split_display = false; 455 } 456 457 for (j = 0; j < ARRAY_SIZE(rm->ctl_blks); j++) { 458 const struct dpu_hw_ctl *ctl; 459 unsigned long features; 460 bool has_split_display; 461 462 if (!rm->ctl_blks[j]) 463 continue; 464 if (reserved_by_other(global_state->ctl_to_crtc_id, j, crtc_id)) 465 continue; 466 467 ctl = to_dpu_hw_ctl(rm->ctl_blks[j]); 468 features = ctl->caps->features; 469 has_split_display = BIT(DPU_CTL_SPLIT_DISPLAY) & features; 470 471 DPU_DEBUG("ctl %d caps 0x%lX\n", j + CTL_0, features); 472 473 if (needs_split_display != has_split_display) 474 continue; 475 476 ctl_idx[i] = j; 477 DPU_DEBUG("ctl %d match\n", j + CTL_0); 478 479 if (++i == num_ctls) 480 break; 481 482 } 483 484 if (i != num_ctls) 485 return -ENAVAIL; 486 487 for (i = 0; i < ARRAY_SIZE(ctl_idx) && i < num_ctls; i++) { 488 global_state->ctl_to_crtc_id[ctl_idx[i]] = crtc_id; 489 trace_dpu_rm_reserve_ctls(i + CTL_0, crtc_id); 490 } 491 492 return 0; 493 } 494 495 static int _dpu_rm_pingpong_next_index(struct dpu_global_state *global_state, 496 int start, 497 uint32_t crtc_id) 498 { 499 int i; 500 501 for (i = start; i < (PINGPONG_MAX - PINGPONG_0); i++) { 502 if (global_state->pingpong_to_crtc_id[i] == crtc_id) 503 return i; 504 } 505 506 return -ENAVAIL; 507 } 508 509 static int _dpu_rm_pingpong_dsc_check(int dsc_idx, int pp_idx) 510 { 511 /* 512 * DSC with even index must be used with the PINGPONG with even index 513 * DSC with odd index must be used with the PINGPONG with odd index 514 */ 515 if ((dsc_idx & 0x01) != (pp_idx & 0x01)) 516 return -ENAVAIL; 517 518 return 0; 519 } 520 521 static int _dpu_rm_dsc_alloc(struct dpu_rm *rm, 522 struct dpu_global_state *global_state, 523 uint32_t crtc_id, 524 const struct msm_display_topology *top) 525 { 526 int num_dsc = 0; 527 int pp_idx = 0; 528 int dsc_idx; 529 int ret; 530 531 for (dsc_idx = 0; dsc_idx < ARRAY_SIZE(rm->dsc_blks) && 532 num_dsc < top->num_dsc; dsc_idx++) { 533 if (!rm->dsc_blks[dsc_idx]) 534 continue; 535 536 if (reserved_by_other(global_state->dsc_to_crtc_id, dsc_idx, crtc_id)) 537 continue; 538 539 pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx, crtc_id); 540 if (pp_idx < 0) 541 return -ENAVAIL; 542 543 ret = _dpu_rm_pingpong_dsc_check(dsc_idx, pp_idx); 544 if (ret) 545 return -ENAVAIL; 546 547 global_state->dsc_to_crtc_id[dsc_idx] = crtc_id; 548 num_dsc++; 549 pp_idx++; 550 } 551 552 if (num_dsc < top->num_dsc) { 553 DPU_ERROR("DSC allocation failed num_dsc=%d required=%d\n", 554 num_dsc, top->num_dsc); 555 return -ENAVAIL; 556 } 557 558 return 0; 559 } 560 561 static int _dpu_rm_dsc_alloc_pair(struct dpu_rm *rm, 562 struct dpu_global_state *global_state, 563 uint32_t crtc_id, 564 const struct msm_display_topology *top) 565 { 566 int num_dsc = 0; 567 int dsc_idx, pp_idx = 0; 568 int ret; 569 570 /* only start from even dsc index */ 571 for (dsc_idx = 0; dsc_idx < ARRAY_SIZE(rm->dsc_blks) && 572 num_dsc < top->num_dsc; dsc_idx += 2) { 573 if (!rm->dsc_blks[dsc_idx] || 574 !rm->dsc_blks[dsc_idx + 1]) 575 continue; 576 577 /* consective dsc index to be paired */ 578 if (reserved_by_other(global_state->dsc_to_crtc_id, dsc_idx, crtc_id) || 579 reserved_by_other(global_state->dsc_to_crtc_id, dsc_idx + 1, crtc_id)) 580 continue; 581 582 pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx, crtc_id); 583 if (pp_idx < 0) 584 return -ENAVAIL; 585 586 ret = _dpu_rm_pingpong_dsc_check(dsc_idx, pp_idx); 587 if (ret) { 588 pp_idx = 0; 589 continue; 590 } 591 592 pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx + 1, crtc_id); 593 if (pp_idx < 0) 594 return -ENAVAIL; 595 596 ret = _dpu_rm_pingpong_dsc_check(dsc_idx + 1, pp_idx); 597 if (ret) { 598 pp_idx = 0; 599 continue; 600 } 601 602 global_state->dsc_to_crtc_id[dsc_idx] = crtc_id; 603 global_state->dsc_to_crtc_id[dsc_idx + 1] = crtc_id; 604 num_dsc += 2; 605 pp_idx++; /* start for next pair */ 606 } 607 608 if (num_dsc < top->num_dsc) { 609 DPU_ERROR("DSC allocation failed num_dsc=%d required=%d\n", 610 num_dsc, top->num_dsc); 611 return -ENAVAIL; 612 } 613 614 return 0; 615 } 616 617 static int _dpu_rm_reserve_dsc(struct dpu_rm *rm, 618 struct dpu_global_state *global_state, 619 uint32_t crtc_id, 620 const struct msm_display_topology *top) 621 { 622 if (!top->num_dsc || !top->num_intf) 623 return 0; 624 625 /* 626 * Facts: 627 * 1) no pingpong split (two layer mixers shared one pingpong) 628 * 2) DSC pair starts from even index, such as index(0,1), (2,3), etc 629 * 3) even PINGPONG connects to even DSC 630 * 4) odd PINGPONG connects to odd DSC 631 * 5) pair: encoder +--> pp_idx_0 --> dsc_idx_0 632 * +--> pp_idx_1 --> dsc_idx_1 633 */ 634 635 /* num_dsc should be either 1, 2 or 4 */ 636 if (top->num_dsc > top->num_intf) /* merge mode */ 637 return _dpu_rm_dsc_alloc_pair(rm, global_state, crtc_id, top); 638 else 639 return _dpu_rm_dsc_alloc(rm, global_state, crtc_id, top); 640 641 return 0; 642 } 643 644 static int _dpu_rm_reserve_cdm(struct dpu_rm *rm, 645 struct dpu_global_state *global_state, 646 uint32_t crtc_id, 647 int num_cdm) 648 { 649 /* try allocating only one CDM block */ 650 if (!rm->cdm_blk) { 651 DPU_ERROR("CDM block does not exist\n"); 652 return -EIO; 653 } 654 655 if (num_cdm > 1) { 656 DPU_ERROR("More than 1 INTF requesting CDM\n"); 657 return -EINVAL; 658 } 659 660 if (global_state->cdm_to_crtc_id) { 661 DPU_ERROR("CDM_0 is already allocated\n"); 662 return -EIO; 663 } 664 665 global_state->cdm_to_crtc_id = crtc_id; 666 667 return 0; 668 } 669 670 static int _dpu_rm_make_reservation( 671 struct dpu_rm *rm, 672 struct dpu_global_state *global_state, 673 uint32_t crtc_id, 674 struct msm_display_topology *topology) 675 { 676 int ret; 677 678 ret = _dpu_rm_reserve_lms(rm, global_state, crtc_id, topology); 679 if (ret) { 680 DPU_ERROR("unable to find appropriate mixers\n"); 681 return ret; 682 } 683 684 if (topology->cwb_enabled) { 685 ret = _dpu_rm_reserve_cwb_mux_and_pingpongs(rm, global_state, 686 crtc_id, topology); 687 if (ret) 688 return ret; 689 } 690 691 ret = _dpu_rm_reserve_ctls(rm, global_state, crtc_id, 692 topology); 693 if (ret) { 694 DPU_ERROR("unable to find appropriate CTL\n"); 695 return ret; 696 } 697 698 ret = _dpu_rm_reserve_dsc(rm, global_state, crtc_id, topology); 699 if (ret) 700 return ret; 701 702 if (topology->num_cdm > 0) { 703 ret = _dpu_rm_reserve_cdm(rm, global_state, crtc_id, topology->num_cdm); 704 if (ret) { 705 DPU_ERROR("unable to find CDM blk\n"); 706 return ret; 707 } 708 } 709 710 return ret; 711 } 712 713 static void _dpu_rm_clear_mapping(uint32_t *res_mapping, int cnt, 714 uint32_t crtc_id) 715 { 716 int i; 717 718 for (i = 0; i < cnt; i++) { 719 if (res_mapping[i] == crtc_id) 720 res_mapping[i] = 0; 721 } 722 } 723 724 /** 725 * dpu_rm_release - Given the encoder for the display chain, release any 726 * HW blocks previously reserved for that use case. 727 * @global_state: resources shared across multiple kms objects 728 * @crtc: DRM CRTC handle 729 * @return: 0 on Success otherwise -ERROR 730 */ 731 void dpu_rm_release(struct dpu_global_state *global_state, 732 struct drm_crtc *crtc) 733 { 734 uint32_t crtc_id = crtc->base.id; 735 736 _dpu_rm_clear_mapping(global_state->pingpong_to_crtc_id, 737 ARRAY_SIZE(global_state->pingpong_to_crtc_id), crtc_id); 738 _dpu_rm_clear_mapping(global_state->mixer_to_crtc_id, 739 ARRAY_SIZE(global_state->mixer_to_crtc_id), crtc_id); 740 _dpu_rm_clear_mapping(global_state->ctl_to_crtc_id, 741 ARRAY_SIZE(global_state->ctl_to_crtc_id), crtc_id); 742 _dpu_rm_clear_mapping(global_state->dsc_to_crtc_id, 743 ARRAY_SIZE(global_state->dsc_to_crtc_id), crtc_id); 744 _dpu_rm_clear_mapping(global_state->dspp_to_crtc_id, 745 ARRAY_SIZE(global_state->dspp_to_crtc_id), crtc_id); 746 _dpu_rm_clear_mapping(&global_state->cdm_to_crtc_id, 1, crtc_id); 747 _dpu_rm_clear_mapping(global_state->cwb_to_crtc_id, 748 ARRAY_SIZE(global_state->cwb_to_crtc_id), crtc_id); 749 } 750 751 /** 752 * dpu_rm_reserve - Given a CRTC->Encoder->Connector display chain, analyze 753 * the use connections and user requirements, specified through related 754 * topology control properties, and reserve hardware blocks to that 755 * display chain. 756 * HW blocks can then be accessed through dpu_rm_get_* functions. 757 * HW Reservations should be released via dpu_rm_release_hw. 758 * @rm: DPU Resource Manager handle 759 * @global_state: resources shared across multiple kms objects 760 * @crtc: DRM CRTC handle 761 * @topology: Pointer to topology info for the display 762 * @return: 0 on Success otherwise -ERROR 763 */ 764 int dpu_rm_reserve( 765 struct dpu_rm *rm, 766 struct dpu_global_state *global_state, 767 struct drm_crtc *crtc, 768 struct msm_display_topology *topology) 769 { 770 int ret; 771 772 if (IS_ERR(global_state)) { 773 DPU_ERROR("failed to global state\n"); 774 return PTR_ERR(global_state); 775 } 776 777 DRM_DEBUG_KMS("reserving hw for crtc %d\n", crtc->base.id); 778 779 DRM_DEBUG_KMS("num_lm: %d num_dsc: %d num_intf: %d\n", 780 topology->num_lm, topology->num_dsc, 781 topology->num_intf); 782 783 ret = _dpu_rm_make_reservation(rm, global_state, crtc->base.id, topology); 784 if (ret) 785 DPU_ERROR("failed to reserve hw resources: %d\n", ret); 786 787 return ret; 788 } 789 790 static struct dpu_hw_sspp *dpu_rm_try_sspp(struct dpu_rm *rm, 791 struct dpu_global_state *global_state, 792 struct drm_crtc *crtc, 793 struct dpu_rm_sspp_requirements *reqs, 794 unsigned int type) 795 { 796 uint32_t crtc_id = crtc->base.id; 797 struct dpu_hw_sspp *hw_sspp; 798 int i; 799 800 for (i = 0; i < ARRAY_SIZE(rm->hw_sspp); i++) { 801 if (!rm->hw_sspp[i]) 802 continue; 803 804 if (global_state->sspp_to_crtc_id[i]) 805 continue; 806 807 hw_sspp = rm->hw_sspp[i]; 808 809 if (hw_sspp->cap->type != type) 810 continue; 811 812 if (reqs->scale && !hw_sspp->cap->sblk->scaler_blk.len) 813 continue; 814 815 // TODO: QSEED2 and RGB scalers are not yet supported 816 if (reqs->scale && !hw_sspp->ops.setup_scaler) 817 continue; 818 819 if (reqs->yuv && !hw_sspp->cap->sblk->csc_blk.len) 820 continue; 821 822 if (reqs->rot90 && !(hw_sspp->cap->features & DPU_SSPP_INLINE_ROTATION)) 823 continue; 824 825 global_state->sspp_to_crtc_id[i] = crtc_id; 826 827 return rm->hw_sspp[i]; 828 } 829 830 return NULL; 831 } 832 833 /** 834 * dpu_rm_reserve_sspp - Reserve the required SSPP for the provided CRTC 835 * @rm: DPU Resource Manager handle 836 * @global_state: private global state 837 * @crtc: DRM CRTC handle 838 * @reqs: SSPP required features 839 */ 840 struct dpu_hw_sspp *dpu_rm_reserve_sspp(struct dpu_rm *rm, 841 struct dpu_global_state *global_state, 842 struct drm_crtc *crtc, 843 struct dpu_rm_sspp_requirements *reqs) 844 { 845 struct dpu_hw_sspp *hw_sspp = NULL; 846 847 if (!reqs->scale && !reqs->yuv) 848 hw_sspp = dpu_rm_try_sspp(rm, global_state, crtc, reqs, SSPP_TYPE_DMA); 849 if (!hw_sspp && !reqs->yuv) 850 hw_sspp = dpu_rm_try_sspp(rm, global_state, crtc, reqs, SSPP_TYPE_RGB); 851 if (!hw_sspp) 852 hw_sspp = dpu_rm_try_sspp(rm, global_state, crtc, reqs, SSPP_TYPE_VIG); 853 854 return hw_sspp; 855 } 856 857 /** 858 * dpu_rm_release_all_sspp - Given the CRTC, release all SSPP 859 * blocks previously reserved for that use case. 860 * @global_state: resources shared across multiple kms objects 861 * @crtc: DRM CRTC handle 862 */ 863 void dpu_rm_release_all_sspp(struct dpu_global_state *global_state, 864 struct drm_crtc *crtc) 865 { 866 uint32_t crtc_id = crtc->base.id; 867 868 _dpu_rm_clear_mapping(global_state->sspp_to_crtc_id, 869 ARRAY_SIZE(global_state->sspp_to_crtc_id), crtc_id); 870 } 871 872 static char *dpu_hw_blk_type_name[] = { 873 [DPU_HW_BLK_TOP] = "TOP", 874 [DPU_HW_BLK_SSPP] = "SSPP", 875 [DPU_HW_BLK_LM] = "LM", 876 [DPU_HW_BLK_CTL] = "CTL", 877 [DPU_HW_BLK_PINGPONG] = "pingpong", 878 [DPU_HW_BLK_INTF] = "INTF", 879 [DPU_HW_BLK_WB] = "WB", 880 [DPU_HW_BLK_DSPP] = "DSPP", 881 [DPU_HW_BLK_MERGE_3D] = "merge_3d", 882 [DPU_HW_BLK_DSC] = "DSC", 883 [DPU_HW_BLK_CDM] = "CDM", 884 [DPU_HW_BLK_MAX] = "unknown", 885 }; 886 887 /** 888 * dpu_rm_get_assigned_resources - Get hw resources of the given type that are 889 * assigned to this encoder 890 * @rm: DPU Resource Manager handle 891 * @global_state: resources shared across multiple kms objects 892 * @crtc: DRM CRTC handle 893 * @type: resource type to return data for 894 * @blks: pointer to the array to be filled by HW resources 895 * @blks_size: size of the @blks array 896 */ 897 int dpu_rm_get_assigned_resources(struct dpu_rm *rm, 898 struct dpu_global_state *global_state, struct drm_crtc *crtc, 899 enum dpu_hw_blk_type type, struct dpu_hw_blk **blks, int blks_size) 900 { 901 uint32_t crtc_id = crtc->base.id; 902 struct dpu_hw_blk **hw_blks; 903 uint32_t *hw_to_crtc_id; 904 int i, num_blks, max_blks; 905 906 switch (type) { 907 case DPU_HW_BLK_PINGPONG: 908 case DPU_HW_BLK_DCWB_PINGPONG: 909 hw_blks = rm->pingpong_blks; 910 hw_to_crtc_id = global_state->pingpong_to_crtc_id; 911 max_blks = ARRAY_SIZE(rm->pingpong_blks); 912 break; 913 case DPU_HW_BLK_LM: 914 hw_blks = rm->mixer_blks; 915 hw_to_crtc_id = global_state->mixer_to_crtc_id; 916 max_blks = ARRAY_SIZE(rm->mixer_blks); 917 break; 918 case DPU_HW_BLK_CTL: 919 hw_blks = rm->ctl_blks; 920 hw_to_crtc_id = global_state->ctl_to_crtc_id; 921 max_blks = ARRAY_SIZE(rm->ctl_blks); 922 break; 923 case DPU_HW_BLK_DSPP: 924 hw_blks = rm->dspp_blks; 925 hw_to_crtc_id = global_state->dspp_to_crtc_id; 926 max_blks = ARRAY_SIZE(rm->dspp_blks); 927 break; 928 case DPU_HW_BLK_DSC: 929 hw_blks = rm->dsc_blks; 930 hw_to_crtc_id = global_state->dsc_to_crtc_id; 931 max_blks = ARRAY_SIZE(rm->dsc_blks); 932 break; 933 case DPU_HW_BLK_CDM: 934 hw_blks = &rm->cdm_blk; 935 hw_to_crtc_id = &global_state->cdm_to_crtc_id; 936 max_blks = 1; 937 break; 938 case DPU_HW_BLK_CWB: 939 hw_blks = rm->cwb_blks; 940 hw_to_crtc_id = global_state->cwb_to_crtc_id; 941 max_blks = ARRAY_SIZE(rm->cwb_blks); 942 break; 943 default: 944 DPU_ERROR("blk type %d not managed by rm\n", type); 945 return 0; 946 } 947 948 num_blks = 0; 949 for (i = 0; i < max_blks; i++) { 950 if (hw_to_crtc_id[i] != crtc_id) 951 continue; 952 953 if (type == DPU_HW_BLK_PINGPONG) { 954 struct dpu_hw_pingpong *pp = to_dpu_hw_pingpong(hw_blks[i]); 955 956 if (pp->idx >= PINGPONG_CWB_0) 957 continue; 958 } 959 960 if (type == DPU_HW_BLK_DCWB_PINGPONG) { 961 struct dpu_hw_pingpong *pp = to_dpu_hw_pingpong(hw_blks[i]); 962 963 if (pp->idx < PINGPONG_CWB_0) 964 continue; 965 } 966 967 if (num_blks == blks_size) { 968 DPU_ERROR("More than %d %s assigned to crtc %d\n", 969 blks_size, dpu_hw_blk_type_name[type], crtc_id); 970 break; 971 } 972 if (!hw_blks[i]) { 973 DPU_ERROR("%s unavailable to assign to crtc %d\n", 974 dpu_hw_blk_type_name[type], crtc_id); 975 break; 976 } 977 blks[num_blks++] = hw_blks[i]; 978 } 979 980 return num_blks; 981 } 982 983 static void dpu_rm_print_state_helper(struct drm_printer *p, 984 struct dpu_hw_blk *blk, 985 uint32_t mapping) 986 { 987 if (!blk) 988 drm_puts(p, "- "); 989 else if (!mapping) 990 drm_puts(p, "# "); 991 else 992 drm_printf(p, "%d ", mapping); 993 } 994 995 996 /** 997 * dpu_rm_print_state - output the RM private state 998 * @p: DRM printer 999 * @global_state: global state 1000 */ 1001 void dpu_rm_print_state(struct drm_printer *p, 1002 const struct dpu_global_state *global_state) 1003 { 1004 const struct dpu_rm *rm = global_state->rm; 1005 int i; 1006 1007 drm_puts(p, "resource mapping:\n"); 1008 drm_puts(p, "\tpingpong="); 1009 for (i = 0; i < ARRAY_SIZE(global_state->pingpong_to_crtc_id); i++) 1010 dpu_rm_print_state_helper(p, rm->pingpong_blks[i], 1011 global_state->pingpong_to_crtc_id[i]); 1012 drm_puts(p, "\n"); 1013 1014 drm_puts(p, "\tmixer="); 1015 for (i = 0; i < ARRAY_SIZE(global_state->mixer_to_crtc_id); i++) 1016 dpu_rm_print_state_helper(p, rm->mixer_blks[i], 1017 global_state->mixer_to_crtc_id[i]); 1018 drm_puts(p, "\n"); 1019 1020 drm_puts(p, "\tctl="); 1021 for (i = 0; i < ARRAY_SIZE(global_state->ctl_to_crtc_id); i++) 1022 dpu_rm_print_state_helper(p, rm->ctl_blks[i], 1023 global_state->ctl_to_crtc_id[i]); 1024 drm_puts(p, "\n"); 1025 1026 drm_puts(p, "\tdspp="); 1027 for (i = 0; i < ARRAY_SIZE(global_state->dspp_to_crtc_id); i++) 1028 dpu_rm_print_state_helper(p, rm->dspp_blks[i], 1029 global_state->dspp_to_crtc_id[i]); 1030 drm_puts(p, "\n"); 1031 1032 drm_puts(p, "\tdsc="); 1033 for (i = 0; i < ARRAY_SIZE(global_state->dsc_to_crtc_id); i++) 1034 dpu_rm_print_state_helper(p, rm->dsc_blks[i], 1035 global_state->dsc_to_crtc_id[i]); 1036 drm_puts(p, "\n"); 1037 1038 drm_puts(p, "\tcdm="); 1039 dpu_rm_print_state_helper(p, rm->cdm_blk, 1040 global_state->cdm_to_crtc_id); 1041 drm_puts(p, "\n"); 1042 1043 drm_puts(p, "\tsspp="); 1044 /* skip SSPP_NONE and start from the next index */ 1045 for (i = SSPP_NONE + 1; i < ARRAY_SIZE(global_state->sspp_to_crtc_id); i++) 1046 dpu_rm_print_state_helper(p, rm->hw_sspp[i] ? &rm->hw_sspp[i]->base : NULL, 1047 global_state->sspp_to_crtc_id[i]); 1048 drm_puts(p, "\n"); 1049 1050 drm_puts(p, "\tcwb="); 1051 for (i = 0; i < ARRAY_SIZE(global_state->cwb_to_crtc_id); i++) 1052 dpu_rm_print_state_helper(p, rm->cwb_blks[i], 1053 global_state->cwb_to_crtc_id[i]); 1054 drm_puts(p, "\n"); 1055 } 1056