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