1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. 4 */ 5 6 #define pr_fmt(fmt) "[drm:%s] " fmt, __func__ 7 #include "dpu_kms.h" 8 #include "dpu_hw_lm.h" 9 #include "dpu_hw_ctl.h" 10 #include "dpu_hw_pingpong.h" 11 #include "dpu_hw_intf.h" 12 #include "dpu_hw_dspp.h" 13 #include "dpu_hw_merge3d.h" 14 #include "dpu_encoder.h" 15 #include "dpu_trace.h" 16 17 18 static inline bool reserved_by_other(uint32_t *res_map, int idx, 19 uint32_t enc_id) 20 { 21 return res_map[idx] && res_map[idx] != enc_id; 22 } 23 24 /** 25 * struct dpu_rm_requirements - Reservation requirements parameter bundle 26 * @topology: selected topology for the display 27 * @hw_res: Hardware resources required as reported by the encoders 28 */ 29 struct dpu_rm_requirements { 30 struct msm_display_topology topology; 31 }; 32 33 int dpu_rm_destroy(struct dpu_rm *rm) 34 { 35 int i; 36 37 for (i = 0; i < ARRAY_SIZE(rm->dspp_blks); i++) { 38 struct dpu_hw_dspp *hw; 39 40 if (rm->dspp_blks[i]) { 41 hw = to_dpu_hw_dspp(rm->dspp_blks[i]); 42 dpu_hw_dspp_destroy(hw); 43 } 44 } 45 for (i = 0; i < ARRAY_SIZE(rm->pingpong_blks); i++) { 46 struct dpu_hw_pingpong *hw; 47 48 if (rm->pingpong_blks[i]) { 49 hw = to_dpu_hw_pingpong(rm->pingpong_blks[i]); 50 dpu_hw_pingpong_destroy(hw); 51 } 52 } 53 for (i = 0; i < ARRAY_SIZE(rm->merge_3d_blks); i++) { 54 struct dpu_hw_merge_3d *hw; 55 56 if (rm->merge_3d_blks[i]) { 57 hw = to_dpu_hw_merge_3d(rm->merge_3d_blks[i]); 58 dpu_hw_merge_3d_destroy(hw); 59 } 60 } 61 for (i = 0; i < ARRAY_SIZE(rm->mixer_blks); i++) { 62 struct dpu_hw_mixer *hw; 63 64 if (rm->mixer_blks[i]) { 65 hw = to_dpu_hw_mixer(rm->mixer_blks[i]); 66 dpu_hw_lm_destroy(hw); 67 } 68 } 69 for (i = 0; i < ARRAY_SIZE(rm->ctl_blks); i++) { 70 struct dpu_hw_ctl *hw; 71 72 if (rm->ctl_blks[i]) { 73 hw = to_dpu_hw_ctl(rm->ctl_blks[i]); 74 dpu_hw_ctl_destroy(hw); 75 } 76 } 77 for (i = 0; i < ARRAY_SIZE(rm->hw_intf); i++) 78 dpu_hw_intf_destroy(rm->hw_intf[i]); 79 80 return 0; 81 } 82 83 int dpu_rm_init(struct dpu_rm *rm, 84 struct dpu_mdss_cfg *cat, 85 void __iomem *mmio) 86 { 87 int rc, i; 88 89 if (!rm || !cat || !mmio) { 90 DPU_ERROR("invalid kms\n"); 91 return -EINVAL; 92 } 93 94 /* Clear, setup lists */ 95 memset(rm, 0, sizeof(*rm)); 96 97 /* Interrogate HW catalog and create tracking items for hw blocks */ 98 for (i = 0; i < cat->mixer_count; i++) { 99 struct dpu_hw_mixer *hw; 100 const struct dpu_lm_cfg *lm = &cat->mixer[i]; 101 102 if (lm->pingpong == PINGPONG_MAX) { 103 DPU_DEBUG("skip mixer %d without pingpong\n", lm->id); 104 continue; 105 } 106 107 if (lm->id < LM_0 || lm->id >= LM_MAX) { 108 DPU_ERROR("skip mixer %d with invalid id\n", lm->id); 109 continue; 110 } 111 hw = dpu_hw_lm_init(lm->id, mmio, cat); 112 if (IS_ERR(hw)) { 113 rc = PTR_ERR(hw); 114 DPU_ERROR("failed lm object creation: err %d\n", rc); 115 goto fail; 116 } 117 rm->mixer_blks[lm->id - LM_0] = &hw->base; 118 } 119 120 for (i = 0; i < cat->merge_3d_count; i++) { 121 struct dpu_hw_merge_3d *hw; 122 const struct dpu_merge_3d_cfg *merge_3d = &cat->merge_3d[i]; 123 124 if (merge_3d->id < MERGE_3D_0 || merge_3d->id >= MERGE_3D_MAX) { 125 DPU_ERROR("skip merge_3d %d with invalid id\n", merge_3d->id); 126 continue; 127 } 128 hw = dpu_hw_merge_3d_init(merge_3d->id, mmio, cat); 129 if (IS_ERR(hw)) { 130 rc = PTR_ERR(hw); 131 DPU_ERROR("failed merge_3d object creation: err %d\n", 132 rc); 133 goto fail; 134 } 135 rm->merge_3d_blks[merge_3d->id - MERGE_3D_0] = &hw->base; 136 } 137 138 for (i = 0; i < cat->pingpong_count; i++) { 139 struct dpu_hw_pingpong *hw; 140 const struct dpu_pingpong_cfg *pp = &cat->pingpong[i]; 141 142 if (pp->id < PINGPONG_0 || pp->id >= PINGPONG_MAX) { 143 DPU_ERROR("skip pingpong %d with invalid id\n", pp->id); 144 continue; 145 } 146 hw = dpu_hw_pingpong_init(pp->id, mmio, cat); 147 if (IS_ERR(hw)) { 148 rc = PTR_ERR(hw); 149 DPU_ERROR("failed pingpong object creation: err %d\n", 150 rc); 151 goto fail; 152 } 153 if (pp->merge_3d && pp->merge_3d < MERGE_3D_MAX) 154 hw->merge_3d = to_dpu_hw_merge_3d(rm->merge_3d_blks[pp->merge_3d - MERGE_3D_0]); 155 rm->pingpong_blks[pp->id - PINGPONG_0] = &hw->base; 156 } 157 158 for (i = 0; i < cat->intf_count; i++) { 159 struct dpu_hw_intf *hw; 160 const struct dpu_intf_cfg *intf = &cat->intf[i]; 161 162 if (intf->type == INTF_NONE) { 163 DPU_DEBUG("skip intf %d with type none\n", i); 164 continue; 165 } 166 if (intf->id < INTF_0 || intf->id >= INTF_MAX) { 167 DPU_ERROR("skip intf %d with invalid id\n", intf->id); 168 continue; 169 } 170 hw = dpu_hw_intf_init(intf->id, mmio, cat); 171 if (IS_ERR(hw)) { 172 rc = PTR_ERR(hw); 173 DPU_ERROR("failed intf object creation: err %d\n", rc); 174 goto fail; 175 } 176 rm->hw_intf[intf->id - INTF_0] = hw; 177 } 178 179 for (i = 0; i < cat->ctl_count; i++) { 180 struct dpu_hw_ctl *hw; 181 const struct dpu_ctl_cfg *ctl = &cat->ctl[i]; 182 183 if (ctl->id < CTL_0 || ctl->id >= CTL_MAX) { 184 DPU_ERROR("skip ctl %d with invalid id\n", ctl->id); 185 continue; 186 } 187 hw = dpu_hw_ctl_init(ctl->id, mmio, cat); 188 if (IS_ERR(hw)) { 189 rc = PTR_ERR(hw); 190 DPU_ERROR("failed ctl object creation: err %d\n", rc); 191 goto fail; 192 } 193 rm->ctl_blks[ctl->id - CTL_0] = &hw->base; 194 } 195 196 for (i = 0; i < cat->dspp_count; i++) { 197 struct dpu_hw_dspp *hw; 198 const struct dpu_dspp_cfg *dspp = &cat->dspp[i]; 199 200 if (dspp->id < DSPP_0 || dspp->id >= DSPP_MAX) { 201 DPU_ERROR("skip dspp %d with invalid id\n", dspp->id); 202 continue; 203 } 204 hw = dpu_hw_dspp_init(dspp->id, mmio, cat); 205 if (IS_ERR(hw)) { 206 rc = PTR_ERR(hw); 207 DPU_ERROR("failed dspp object creation: err %d\n", rc); 208 goto fail; 209 } 210 rm->dspp_blks[dspp->id - DSPP_0] = &hw->base; 211 } 212 213 return 0; 214 215 fail: 216 dpu_rm_destroy(rm); 217 218 return rc ? rc : -EFAULT; 219 } 220 221 static bool _dpu_rm_needs_split_display(const struct msm_display_topology *top) 222 { 223 return top->num_intf > 1; 224 } 225 226 /** 227 * _dpu_rm_check_lm_peer - check if a mixer is a peer of the primary 228 * @rm: dpu resource manager handle 229 * @primary_idx: index of primary mixer in rm->mixer_blks[] 230 * @peer_idx: index of other mixer in rm->mixer_blks[] 231 * Return: true if rm->mixer_blks[peer_idx] is a peer of 232 * rm->mixer_blks[primary_idx] 233 */ 234 static bool _dpu_rm_check_lm_peer(struct dpu_rm *rm, int primary_idx, 235 int peer_idx) 236 { 237 const struct dpu_lm_cfg *prim_lm_cfg; 238 const struct dpu_lm_cfg *peer_cfg; 239 240 prim_lm_cfg = to_dpu_hw_mixer(rm->mixer_blks[primary_idx])->cap; 241 peer_cfg = to_dpu_hw_mixer(rm->mixer_blks[peer_idx])->cap; 242 243 if (!test_bit(peer_cfg->id, &prim_lm_cfg->lm_pair_mask)) { 244 DPU_DEBUG("lm %d not peer of lm %d\n", peer_cfg->id, 245 peer_cfg->id); 246 return false; 247 } 248 return true; 249 } 250 251 /** 252 * _dpu_rm_check_lm_and_get_connected_blks - check if proposed layer mixer meets 253 * proposed use case requirements, incl. hardwired dependent blocks like 254 * pingpong 255 * @rm: dpu resource manager handle 256 * @global_state: resources shared across multiple kms objects 257 * @enc_id: encoder id requesting for allocation 258 * @lm_idx: index of proposed layer mixer in rm->mixer_blks[], function checks 259 * if lm, and all other hardwired blocks connected to the lm (pp) is 260 * available and appropriate 261 * @pp_idx: output parameter, index of pingpong block attached to the layer 262 * mixer in rm->pingpong_blks[]. 263 * @dspp_idx: output parameter, index of dspp block attached to the layer 264 * mixer in rm->dspp_blks[]. 265 * @reqs: input parameter, rm requirements for HW blocks needed in the 266 * datapath. 267 * Return: true if lm matches all requirements, false otherwise 268 */ 269 static bool _dpu_rm_check_lm_and_get_connected_blks(struct dpu_rm *rm, 270 struct dpu_global_state *global_state, 271 uint32_t enc_id, int lm_idx, int *pp_idx, int *dspp_idx, 272 struct dpu_rm_requirements *reqs) 273 { 274 const struct dpu_lm_cfg *lm_cfg; 275 int idx; 276 277 /* Already reserved? */ 278 if (reserved_by_other(global_state->mixer_to_enc_id, lm_idx, enc_id)) { 279 DPU_DEBUG("lm %d already reserved\n", lm_idx + LM_0); 280 return false; 281 } 282 283 lm_cfg = to_dpu_hw_mixer(rm->mixer_blks[lm_idx])->cap; 284 idx = lm_cfg->pingpong - PINGPONG_0; 285 if (idx < 0 || idx >= ARRAY_SIZE(rm->pingpong_blks)) { 286 DPU_ERROR("failed to get pp on lm %d\n", lm_cfg->pingpong); 287 return false; 288 } 289 290 if (reserved_by_other(global_state->pingpong_to_enc_id, idx, enc_id)) { 291 DPU_DEBUG("lm %d pp %d already reserved\n", lm_cfg->id, 292 lm_cfg->pingpong); 293 return false; 294 } 295 *pp_idx = idx; 296 297 if (!reqs->topology.num_dspp) 298 return true; 299 300 idx = lm_cfg->dspp - DSPP_0; 301 if (idx < 0 || idx >= ARRAY_SIZE(rm->dspp_blks)) { 302 DPU_ERROR("failed to get dspp on lm %d\n", lm_cfg->dspp); 303 return false; 304 } 305 306 if (reserved_by_other(global_state->dspp_to_enc_id, idx, enc_id)) { 307 DPU_DEBUG("lm %d dspp %d already reserved\n", lm_cfg->id, 308 lm_cfg->dspp); 309 return false; 310 } 311 *dspp_idx = idx; 312 313 return true; 314 } 315 316 static int _dpu_rm_reserve_lms(struct dpu_rm *rm, 317 struct dpu_global_state *global_state, 318 uint32_t enc_id, 319 struct dpu_rm_requirements *reqs) 320 321 { 322 int lm_idx[MAX_BLOCKS]; 323 int pp_idx[MAX_BLOCKS]; 324 int dspp_idx[MAX_BLOCKS] = {0}; 325 int i, j, lm_count = 0; 326 327 if (!reqs->topology.num_lm) { 328 DPU_ERROR("invalid number of lm: %d\n", reqs->topology.num_lm); 329 return -EINVAL; 330 } 331 332 /* Find a primary mixer */ 333 for (i = 0; i < ARRAY_SIZE(rm->mixer_blks) && 334 lm_count < reqs->topology.num_lm; i++) { 335 if (!rm->mixer_blks[i]) 336 continue; 337 338 lm_count = 0; 339 lm_idx[lm_count] = i; 340 341 if (!_dpu_rm_check_lm_and_get_connected_blks(rm, global_state, 342 enc_id, i, &pp_idx[lm_count], 343 &dspp_idx[lm_count], reqs)) { 344 continue; 345 } 346 347 ++lm_count; 348 349 /* Valid primary mixer found, find matching peers */ 350 for (j = i + 1; j < ARRAY_SIZE(rm->mixer_blks) && 351 lm_count < reqs->topology.num_lm; j++) { 352 if (!rm->mixer_blks[j]) 353 continue; 354 355 if (!_dpu_rm_check_lm_peer(rm, i, j)) { 356 DPU_DEBUG("lm %d not peer of lm %d\n", LM_0 + j, 357 LM_0 + i); 358 continue; 359 } 360 361 if (!_dpu_rm_check_lm_and_get_connected_blks(rm, 362 global_state, enc_id, j, 363 &pp_idx[lm_count], &dspp_idx[lm_count], 364 reqs)) { 365 continue; 366 } 367 368 lm_idx[lm_count] = j; 369 ++lm_count; 370 } 371 } 372 373 if (lm_count != reqs->topology.num_lm) { 374 DPU_DEBUG("unable to find appropriate mixers\n"); 375 return -ENAVAIL; 376 } 377 378 for (i = 0; i < lm_count; i++) { 379 global_state->mixer_to_enc_id[lm_idx[i]] = enc_id; 380 global_state->pingpong_to_enc_id[pp_idx[i]] = enc_id; 381 global_state->dspp_to_enc_id[dspp_idx[i]] = 382 reqs->topology.num_dspp ? enc_id : 0; 383 384 trace_dpu_rm_reserve_lms(lm_idx[i] + LM_0, enc_id, 385 pp_idx[i] + PINGPONG_0); 386 } 387 388 return 0; 389 } 390 391 static int _dpu_rm_reserve_ctls( 392 struct dpu_rm *rm, 393 struct dpu_global_state *global_state, 394 uint32_t enc_id, 395 const struct msm_display_topology *top) 396 { 397 int ctl_idx[MAX_BLOCKS]; 398 int i = 0, j, num_ctls; 399 bool needs_split_display; 400 401 /* each hw_intf needs its own hw_ctrl to program its control path */ 402 num_ctls = top->num_intf; 403 404 needs_split_display = _dpu_rm_needs_split_display(top); 405 406 for (j = 0; j < ARRAY_SIZE(rm->ctl_blks); j++) { 407 const struct dpu_hw_ctl *ctl; 408 unsigned long features; 409 bool has_split_display; 410 411 if (!rm->ctl_blks[j]) 412 continue; 413 if (reserved_by_other(global_state->ctl_to_enc_id, j, enc_id)) 414 continue; 415 416 ctl = to_dpu_hw_ctl(rm->ctl_blks[j]); 417 features = ctl->caps->features; 418 has_split_display = BIT(DPU_CTL_SPLIT_DISPLAY) & features; 419 420 DPU_DEBUG("ctl %d caps 0x%lX\n", j + CTL_0, features); 421 422 if (needs_split_display != has_split_display) 423 continue; 424 425 ctl_idx[i] = j; 426 DPU_DEBUG("ctl %d match\n", j + CTL_0); 427 428 if (++i == num_ctls) 429 break; 430 431 } 432 433 if (i != num_ctls) 434 return -ENAVAIL; 435 436 for (i = 0; i < ARRAY_SIZE(ctl_idx) && i < num_ctls; i++) { 437 global_state->ctl_to_enc_id[ctl_idx[i]] = enc_id; 438 trace_dpu_rm_reserve_ctls(i + CTL_0, enc_id); 439 } 440 441 return 0; 442 } 443 444 static int _dpu_rm_make_reservation( 445 struct dpu_rm *rm, 446 struct dpu_global_state *global_state, 447 struct drm_encoder *enc, 448 struct dpu_rm_requirements *reqs) 449 { 450 int ret; 451 452 ret = _dpu_rm_reserve_lms(rm, global_state, enc->base.id, reqs); 453 if (ret) { 454 DPU_ERROR("unable to find appropriate mixers\n"); 455 return ret; 456 } 457 458 ret = _dpu_rm_reserve_ctls(rm, global_state, enc->base.id, 459 &reqs->topology); 460 if (ret) { 461 DPU_ERROR("unable to find appropriate CTL\n"); 462 return ret; 463 } 464 465 return ret; 466 } 467 468 static int _dpu_rm_populate_requirements( 469 struct drm_encoder *enc, 470 struct dpu_rm_requirements *reqs, 471 struct msm_display_topology req_topology) 472 { 473 reqs->topology = req_topology; 474 475 DRM_DEBUG_KMS("num_lm: %d num_enc: %d num_intf: %d\n", 476 reqs->topology.num_lm, reqs->topology.num_enc, 477 reqs->topology.num_intf); 478 479 return 0; 480 } 481 482 static void _dpu_rm_clear_mapping(uint32_t *res_mapping, int cnt, 483 uint32_t enc_id) 484 { 485 int i; 486 487 for (i = 0; i < cnt; i++) { 488 if (res_mapping[i] == enc_id) 489 res_mapping[i] = 0; 490 } 491 } 492 493 void dpu_rm_release(struct dpu_global_state *global_state, 494 struct drm_encoder *enc) 495 { 496 _dpu_rm_clear_mapping(global_state->pingpong_to_enc_id, 497 ARRAY_SIZE(global_state->pingpong_to_enc_id), enc->base.id); 498 _dpu_rm_clear_mapping(global_state->mixer_to_enc_id, 499 ARRAY_SIZE(global_state->mixer_to_enc_id), enc->base.id); 500 _dpu_rm_clear_mapping(global_state->ctl_to_enc_id, 501 ARRAY_SIZE(global_state->ctl_to_enc_id), enc->base.id); 502 } 503 504 int dpu_rm_reserve( 505 struct dpu_rm *rm, 506 struct dpu_global_state *global_state, 507 struct drm_encoder *enc, 508 struct drm_crtc_state *crtc_state, 509 struct msm_display_topology topology) 510 { 511 struct dpu_rm_requirements reqs; 512 int ret; 513 514 /* Check if this is just a page-flip */ 515 if (!drm_atomic_crtc_needs_modeset(crtc_state)) 516 return 0; 517 518 if (IS_ERR(global_state)) { 519 DPU_ERROR("failed to global state\n"); 520 return PTR_ERR(global_state); 521 } 522 523 DRM_DEBUG_KMS("reserving hw for enc %d crtc %d\n", 524 enc->base.id, crtc_state->crtc->base.id); 525 526 ret = _dpu_rm_populate_requirements(enc, &reqs, topology); 527 if (ret) { 528 DPU_ERROR("failed to populate hw requirements\n"); 529 return ret; 530 } 531 532 ret = _dpu_rm_make_reservation(rm, global_state, enc, &reqs); 533 if (ret) 534 DPU_ERROR("failed to reserve hw resources: %d\n", ret); 535 536 537 538 return ret; 539 } 540 541 int dpu_rm_get_assigned_resources(struct dpu_rm *rm, 542 struct dpu_global_state *global_state, uint32_t enc_id, 543 enum dpu_hw_blk_type type, struct dpu_hw_blk **blks, int blks_size) 544 { 545 struct dpu_hw_blk **hw_blks; 546 uint32_t *hw_to_enc_id; 547 int i, num_blks, max_blks; 548 549 switch (type) { 550 case DPU_HW_BLK_PINGPONG: 551 hw_blks = rm->pingpong_blks; 552 hw_to_enc_id = global_state->pingpong_to_enc_id; 553 max_blks = ARRAY_SIZE(rm->pingpong_blks); 554 break; 555 case DPU_HW_BLK_LM: 556 hw_blks = rm->mixer_blks; 557 hw_to_enc_id = global_state->mixer_to_enc_id; 558 max_blks = ARRAY_SIZE(rm->mixer_blks); 559 break; 560 case DPU_HW_BLK_CTL: 561 hw_blks = rm->ctl_blks; 562 hw_to_enc_id = global_state->ctl_to_enc_id; 563 max_blks = ARRAY_SIZE(rm->ctl_blks); 564 break; 565 case DPU_HW_BLK_DSPP: 566 hw_blks = rm->dspp_blks; 567 hw_to_enc_id = global_state->dspp_to_enc_id; 568 max_blks = ARRAY_SIZE(rm->dspp_blks); 569 break; 570 default: 571 DPU_ERROR("blk type %d not managed by rm\n", type); 572 return 0; 573 } 574 575 num_blks = 0; 576 for (i = 0; i < max_blks; i++) { 577 if (hw_to_enc_id[i] != enc_id) 578 continue; 579 580 if (num_blks == blks_size) { 581 DPU_ERROR("More than %d resources assigned to enc %d\n", 582 blks_size, enc_id); 583 break; 584 } 585 blks[num_blks++] = hw_blks[i]; 586 } 587 588 return num_blks; 589 } 590