treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 284
[linux-block.git] / drivers / gpu / drm / msm / disp / dpu1 / dpu_rm.c
CommitLineData
97fb5e8d 1// SPDX-License-Identifier: GPL-2.0-only
25fdd593
JS
2/*
3 * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
25fdd593
JS
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"
25fdd593
JS
10#include "dpu_hw_pingpong.h"
11#include "dpu_hw_intf.h"
12#include "dpu_encoder.h"
13#include "dpu_trace.h"
14
8998010c
JS
15#define RESERVED_BY_OTHER(h, r) \
16 ((h)->enc_id && (h)->enc_id != r)
25fdd593 17
25fdd593
JS
18/**
19 * struct dpu_rm_requirements - Reservation requirements parameter bundle
ad92af7e 20 * @topology: selected topology for the display
25fdd593
JS
21 * @hw_res: Hardware resources required as reported by the encoders
22 */
23struct dpu_rm_requirements {
ad92af7e 24 struct msm_display_topology topology;
25fdd593
JS
25 struct dpu_encoder_hw_resources hw_res;
26};
27
25fdd593
JS
28
29/**
30 * struct dpu_rm_hw_blk - hardware block tracking list member
31 * @list: List head for list of all hardware blocks tracking items
25fdd593 32 * @id: Hardware ID number, within it's own space, ie. LM_X
8998010c 33 * @enc_id: Encoder id to which this blk is binded
25fdd593
JS
34 * @hw: Pointer to the hardware register access object for this block
35 */
36struct dpu_rm_hw_blk {
37 struct list_head list;
25fdd593 38 uint32_t id;
8998010c 39 uint32_t enc_id;
25fdd593
JS
40 struct dpu_hw_blk *hw;
41};
42
25fdd593
JS
43void dpu_rm_init_hw_iter(
44 struct dpu_rm_hw_iter *iter,
45 uint32_t enc_id,
46 enum dpu_hw_blk_type type)
47{
48 memset(iter, 0, sizeof(*iter));
49 iter->enc_id = enc_id;
50 iter->type = type;
51}
52
53static bool _dpu_rm_get_hw_locked(struct dpu_rm *rm, struct dpu_rm_hw_iter *i)
54{
55 struct list_head *blk_list;
56
57 if (!rm || !i || i->type >= DPU_HW_BLK_MAX) {
58 DPU_ERROR("invalid rm\n");
59 return false;
60 }
61
62 i->hw = NULL;
63 blk_list = &rm->hw_blks[i->type];
64
65 if (i->blk && (&i->blk->list == blk_list)) {
66 DPU_DEBUG("attempt resume iteration past last\n");
67 return false;
68 }
69
70 i->blk = list_prepare_entry(i->blk, blk_list, list);
71
72 list_for_each_entry_continue(i->blk, blk_list, list) {
8998010c 73 if (i->enc_id == i->blk->enc_id) {
25fdd593
JS
74 i->hw = i->blk->hw;
75 DPU_DEBUG("found type %d id %d for enc %d\n",
76 i->type, i->blk->id, i->enc_id);
77 return true;
78 }
79 }
80
81 DPU_DEBUG("no match, type %d for enc %d\n", i->type, i->enc_id);
82
83 return false;
84}
85
86bool dpu_rm_get_hw(struct dpu_rm *rm, struct dpu_rm_hw_iter *i)
87{
88 bool ret;
89
90 mutex_lock(&rm->rm_lock);
91 ret = _dpu_rm_get_hw_locked(rm, i);
92 mutex_unlock(&rm->rm_lock);
93
94 return ret;
95}
96
97static void _dpu_rm_hw_destroy(enum dpu_hw_blk_type type, void *hw)
98{
99 switch (type) {
100 case DPU_HW_BLK_LM:
101 dpu_hw_lm_destroy(hw);
102 break;
103 case DPU_HW_BLK_CTL:
104 dpu_hw_ctl_destroy(hw);
105 break;
25fdd593
JS
106 case DPU_HW_BLK_PINGPONG:
107 dpu_hw_pingpong_destroy(hw);
108 break;
109 case DPU_HW_BLK_INTF:
110 dpu_hw_intf_destroy(hw);
111 break;
112 case DPU_HW_BLK_SSPP:
113 /* SSPPs are not managed by the resource manager */
114 case DPU_HW_BLK_TOP:
115 /* Top is a singleton, not managed in hw_blks list */
116 case DPU_HW_BLK_MAX:
117 default:
118 DPU_ERROR("unsupported block type %d\n", type);
119 break;
120 }
121}
122
123int dpu_rm_destroy(struct dpu_rm *rm)
124{
25fdd593
JS
125 struct dpu_rm_hw_blk *hw_cur, *hw_nxt;
126 enum dpu_hw_blk_type type;
127
25fdd593
JS
128 for (type = 0; type < DPU_HW_BLK_MAX; type++) {
129 list_for_each_entry_safe(hw_cur, hw_nxt, &rm->hw_blks[type],
130 list) {
131 list_del(&hw_cur->list);
1a5e1778 132 _dpu_rm_hw_destroy(type, hw_cur->hw);
25fdd593
JS
133 kfree(hw_cur);
134 }
135 }
136
25fdd593
JS
137 mutex_destroy(&rm->rm_lock);
138
139 return 0;
140}
141
142static int _dpu_rm_hw_blk_create(
143 struct dpu_rm *rm,
144 struct dpu_mdss_cfg *cat,
145 void __iomem *mmio,
146 enum dpu_hw_blk_type type,
147 uint32_t id,
148 void *hw_catalog_info)
149{
150 struct dpu_rm_hw_blk *blk;
25fdd593
JS
151 void *hw;
152
25fdd593
JS
153 switch (type) {
154 case DPU_HW_BLK_LM:
155 hw = dpu_hw_lm_init(id, mmio, cat);
156 break;
157 case DPU_HW_BLK_CTL:
158 hw = dpu_hw_ctl_init(id, mmio, cat);
159 break;
25fdd593
JS
160 case DPU_HW_BLK_PINGPONG:
161 hw = dpu_hw_pingpong_init(id, mmio, cat);
162 break;
163 case DPU_HW_BLK_INTF:
164 hw = dpu_hw_intf_init(id, mmio, cat);
165 break;
166 case DPU_HW_BLK_SSPP:
167 /* SSPPs are not managed by the resource manager */
168 case DPU_HW_BLK_TOP:
169 /* Top is a singleton, not managed in hw_blks list */
170 case DPU_HW_BLK_MAX:
171 default:
172 DPU_ERROR("unsupported block type %d\n", type);
173 return -EINVAL;
174 }
175
176 if (IS_ERR_OR_NULL(hw)) {
177 DPU_ERROR("failed hw object creation: type %d, err %ld\n",
178 type, PTR_ERR(hw));
179 return -EFAULT;
180 }
181
182 blk = kzalloc(sizeof(*blk), GFP_KERNEL);
183 if (!blk) {
184 _dpu_rm_hw_destroy(type, hw);
185 return -ENOMEM;
186 }
187
25fdd593
JS
188 blk->id = id;
189 blk->hw = hw;
8998010c 190 blk->enc_id = 0;
25fdd593
JS
191 list_add_tail(&blk->list, &rm->hw_blks[type]);
192
193 return 0;
194}
195
196int dpu_rm_init(struct dpu_rm *rm,
197 struct dpu_mdss_cfg *cat,
3763f1a5 198 void __iomem *mmio)
25fdd593
JS
199{
200 int rc, i;
201 enum dpu_hw_blk_type type;
202
3763f1a5 203 if (!rm || !cat || !mmio) {
25fdd593
JS
204 DPU_ERROR("invalid kms\n");
205 return -EINVAL;
206 }
207
208 /* Clear, setup lists */
209 memset(rm, 0, sizeof(*rm));
210
211 mutex_init(&rm->rm_lock);
212
25fdd593
JS
213 for (type = 0; type < DPU_HW_BLK_MAX; type++)
214 INIT_LIST_HEAD(&rm->hw_blks[type]);
215
25fdd593
JS
216 /* Interrogate HW catalog and create tracking items for hw blocks */
217 for (i = 0; i < cat->mixer_count; i++) {
218 struct dpu_lm_cfg *lm = &cat->mixer[i];
219
220 if (lm->pingpong == PINGPONG_MAX) {
221 DPU_DEBUG("skip mixer %d without pingpong\n", lm->id);
222 continue;
223 }
224
225 rc = _dpu_rm_hw_blk_create(rm, cat, mmio, DPU_HW_BLK_LM,
226 cat->mixer[i].id, &cat->mixer[i]);
227 if (rc) {
228 DPU_ERROR("failed: lm hw not available\n");
229 goto fail;
230 }
231
232 if (!rm->lm_max_width) {
233 rm->lm_max_width = lm->sblk->maxwidth;
234 } else if (rm->lm_max_width != lm->sblk->maxwidth) {
235 /*
236 * Don't expect to have hw where lm max widths differ.
237 * If found, take the min.
238 */
239 DPU_ERROR("unsupported: lm maxwidth differs\n");
240 if (rm->lm_max_width > lm->sblk->maxwidth)
241 rm->lm_max_width = lm->sblk->maxwidth;
242 }
243 }
244
245 for (i = 0; i < cat->pingpong_count; i++) {
246 rc = _dpu_rm_hw_blk_create(rm, cat, mmio, DPU_HW_BLK_PINGPONG,
247 cat->pingpong[i].id, &cat->pingpong[i]);
248 if (rc) {
249 DPU_ERROR("failed: pp hw not available\n");
250 goto fail;
251 }
252 }
253
254 for (i = 0; i < cat->intf_count; i++) {
255 if (cat->intf[i].type == INTF_NONE) {
256 DPU_DEBUG("skip intf %d with type none\n", i);
257 continue;
258 }
259
260 rc = _dpu_rm_hw_blk_create(rm, cat, mmio, DPU_HW_BLK_INTF,
261 cat->intf[i].id, &cat->intf[i]);
262 if (rc) {
263 DPU_ERROR("failed: intf hw not available\n");
264 goto fail;
265 }
266 }
267
268 for (i = 0; i < cat->ctl_count; i++) {
269 rc = _dpu_rm_hw_blk_create(rm, cat, mmio, DPU_HW_BLK_CTL,
270 cat->ctl[i].id, &cat->ctl[i]);
271 if (rc) {
272 DPU_ERROR("failed: ctl hw not available\n");
273 goto fail;
274 }
275 }
276
25fdd593
JS
277 return 0;
278
279fail:
280 dpu_rm_destroy(rm);
281
282 return rc;
283}
284
ad92af7e
JS
285static bool _dpu_rm_needs_split_display(const struct msm_display_topology *top)
286{
287 return top->num_intf > 1;
288}
289
25fdd593
JS
290/**
291 * _dpu_rm_check_lm_and_get_connected_blks - check if proposed layer mixer meets
292 * proposed use case requirements, incl. hardwired dependent blocks like
293 * pingpong
294 * @rm: dpu resource manager handle
8998010c 295 * @enc_id: encoder id requesting for allocation
25fdd593
JS
296 * @reqs: proposed use case requirements
297 * @lm: proposed layer mixer, function checks if lm, and all other hardwired
298 * blocks connected to the lm (pp) is available and appropriate
299 * @pp: output parameter, pingpong block attached to the layer mixer.
300 * NULL if pp was not available, or not matching requirements.
301 * @primary_lm: if non-null, this function check if lm is compatible primary_lm
302 * as well as satisfying all other requirements
303 * @Return: true if lm matches all requirements, false otherwise
304 */
305static bool _dpu_rm_check_lm_and_get_connected_blks(
306 struct dpu_rm *rm,
8998010c 307 uint32_t enc_id,
25fdd593
JS
308 struct dpu_rm_requirements *reqs,
309 struct dpu_rm_hw_blk *lm,
310 struct dpu_rm_hw_blk **pp,
311 struct dpu_rm_hw_blk *primary_lm)
312{
313 const struct dpu_lm_cfg *lm_cfg = to_dpu_hw_mixer(lm->hw)->cap;
314 struct dpu_rm_hw_iter iter;
315
316 *pp = NULL;
317
318 DPU_DEBUG("check lm %d pp %d\n",
319 lm_cfg->id, lm_cfg->pingpong);
320
321 /* Check if this layer mixer is a peer of the proposed primary LM */
322 if (primary_lm) {
323 const struct dpu_lm_cfg *prim_lm_cfg =
324 to_dpu_hw_mixer(primary_lm->hw)->cap;
325
326 if (!test_bit(lm_cfg->id, &prim_lm_cfg->lm_pair_mask)) {
327 DPU_DEBUG("lm %d not peer of lm %d\n", lm_cfg->id,
328 prim_lm_cfg->id);
329 return false;
330 }
331 }
332
333 /* Already reserved? */
8998010c 334 if (RESERVED_BY_OTHER(lm, enc_id)) {
25fdd593
JS
335 DPU_DEBUG("lm %d already reserved\n", lm_cfg->id);
336 return false;
337 }
338
339 dpu_rm_init_hw_iter(&iter, 0, DPU_HW_BLK_PINGPONG);
340 while (_dpu_rm_get_hw_locked(rm, &iter)) {
341 if (iter.blk->id == lm_cfg->pingpong) {
342 *pp = iter.blk;
343 break;
344 }
345 }
346
347 if (!*pp) {
348 DPU_ERROR("failed to get pp on lm %d\n", lm_cfg->pingpong);
349 return false;
350 }
351
8998010c 352 if (RESERVED_BY_OTHER(*pp, enc_id)) {
25fdd593
JS
353 DPU_DEBUG("lm %d pp %d already reserved\n", lm->id,
354 (*pp)->id);
355 return false;
356 }
357
358 return true;
359}
360
8998010c
JS
361static int _dpu_rm_reserve_lms(struct dpu_rm *rm, uint32_t enc_id,
362 struct dpu_rm_requirements *reqs)
25fdd593
JS
363
364{
365 struct dpu_rm_hw_blk *lm[MAX_BLOCKS];
366 struct dpu_rm_hw_blk *pp[MAX_BLOCKS];
367 struct dpu_rm_hw_iter iter_i, iter_j;
368 int lm_count = 0;
369 int i, rc = 0;
370
ad92af7e
JS
371 if (!reqs->topology.num_lm) {
372 DPU_ERROR("invalid number of lm: %d\n", reqs->topology.num_lm);
25fdd593
JS
373 return -EINVAL;
374 }
375
376 /* Find a primary mixer */
377 dpu_rm_init_hw_iter(&iter_i, 0, DPU_HW_BLK_LM);
ad92af7e 378 while (lm_count != reqs->topology.num_lm &&
25fdd593
JS
379 _dpu_rm_get_hw_locked(rm, &iter_i)) {
380 memset(&lm, 0, sizeof(lm));
381 memset(&pp, 0, sizeof(pp));
382
383 lm_count = 0;
384 lm[lm_count] = iter_i.blk;
385
386 if (!_dpu_rm_check_lm_and_get_connected_blks(
8998010c 387 rm, enc_id, reqs, lm[lm_count],
25fdd593
JS
388 &pp[lm_count], NULL))
389 continue;
390
391 ++lm_count;
392
393 /* Valid primary mixer found, find matching peers */
394 dpu_rm_init_hw_iter(&iter_j, 0, DPU_HW_BLK_LM);
395
ad92af7e 396 while (lm_count != reqs->topology.num_lm &&
25fdd593
JS
397 _dpu_rm_get_hw_locked(rm, &iter_j)) {
398 if (iter_i.blk == iter_j.blk)
399 continue;
400
401 if (!_dpu_rm_check_lm_and_get_connected_blks(
8998010c 402 rm, enc_id, reqs, iter_j.blk,
25fdd593
JS
403 &pp[lm_count], iter_i.blk))
404 continue;
405
406 lm[lm_count] = iter_j.blk;
407 ++lm_count;
408 }
409 }
410
ad92af7e 411 if (lm_count != reqs->topology.num_lm) {
25fdd593
JS
412 DPU_DEBUG("unable to find appropriate mixers\n");
413 return -ENAVAIL;
414 }
415
416 for (i = 0; i < ARRAY_SIZE(lm); i++) {
417 if (!lm[i])
418 break;
419
8998010c
JS
420 lm[i]->enc_id = enc_id;
421 pp[i]->enc_id = enc_id;
25fdd593 422
1a5e1778 423 trace_dpu_rm_reserve_lms(lm[i]->id, enc_id, pp[i]->id);
25fdd593
JS
424 }
425
426 return rc;
427}
428
429static int _dpu_rm_reserve_ctls(
430 struct dpu_rm *rm,
8998010c 431 uint32_t enc_id,
ad92af7e 432 const struct msm_display_topology *top)
25fdd593
JS
433{
434 struct dpu_rm_hw_blk *ctls[MAX_BLOCKS];
435 struct dpu_rm_hw_iter iter;
ad92af7e
JS
436 int i = 0, num_ctls = 0;
437 bool needs_split_display = false;
25fdd593
JS
438
439 memset(&ctls, 0, sizeof(ctls));
440
ad92af7e
JS
441 /* each hw_intf needs its own hw_ctrl to program its control path */
442 num_ctls = top->num_intf;
443
444 needs_split_display = _dpu_rm_needs_split_display(top);
445
25fdd593
JS
446 dpu_rm_init_hw_iter(&iter, 0, DPU_HW_BLK_CTL);
447 while (_dpu_rm_get_hw_locked(rm, &iter)) {
448 const struct dpu_hw_ctl *ctl = to_dpu_hw_ctl(iter.blk->hw);
449 unsigned long features = ctl->caps->features;
450 bool has_split_display;
451
8998010c 452 if (RESERVED_BY_OTHER(iter.blk, enc_id))
25fdd593
JS
453 continue;
454
455 has_split_display = BIT(DPU_CTL_SPLIT_DISPLAY) & features;
456
457 DPU_DEBUG("ctl %d caps 0x%lX\n", iter.blk->id, features);
458
ad92af7e 459 if (needs_split_display != has_split_display)
25fdd593
JS
460 continue;
461
462 ctls[i] = iter.blk;
463 DPU_DEBUG("ctl %d match\n", iter.blk->id);
464
ad92af7e 465 if (++i == num_ctls)
25fdd593
JS
466 break;
467 }
468
ad92af7e 469 if (i != num_ctls)
25fdd593
JS
470 return -ENAVAIL;
471
ad92af7e 472 for (i = 0; i < ARRAY_SIZE(ctls) && i < num_ctls; i++) {
8998010c 473 ctls[i]->enc_id = enc_id;
1a5e1778 474 trace_dpu_rm_reserve_ctls(ctls[i]->id, enc_id);
25fdd593
JS
475 }
476
477 return 0;
478}
479
25fdd593
JS
480static int _dpu_rm_reserve_intf(
481 struct dpu_rm *rm,
8998010c 482 uint32_t enc_id,
25fdd593 483 uint32_t id,
9816b226 484 enum dpu_hw_blk_type type)
25fdd593
JS
485{
486 struct dpu_rm_hw_iter iter;
487 int ret = 0;
488
489 /* Find the block entry in the rm, and note the reservation */
490 dpu_rm_init_hw_iter(&iter, 0, type);
491 while (_dpu_rm_get_hw_locked(rm, &iter)) {
492 if (iter.blk->id != id)
493 continue;
494
8998010c 495 if (RESERVED_BY_OTHER(iter.blk, enc_id)) {
25fdd593
JS
496 DPU_ERROR("type %d id %d already reserved\n", type, id);
497 return -ENAVAIL;
498 }
499
8998010c 500 iter.blk->enc_id = enc_id;
1a5e1778 501 trace_dpu_rm_reserve_intf(iter.blk->id, enc_id);
25fdd593
JS
502 break;
503 }
504
505 /* Shouldn't happen since intfs are fixed at probe */
506 if (!iter.hw) {
507 DPU_ERROR("couldn't find type %d id %d\n", type, id);
508 return -EINVAL;
509 }
510
25fdd593
JS
511 return ret;
512}
513
514static int _dpu_rm_reserve_intf_related_hw(
515 struct dpu_rm *rm,
8998010c 516 uint32_t enc_id,
25fdd593
JS
517 struct dpu_encoder_hw_resources *hw_res)
518{
519 int i, ret = 0;
520 u32 id;
521
522 for (i = 0; i < ARRAY_SIZE(hw_res->intfs); i++) {
523 if (hw_res->intfs[i] == INTF_MODE_NONE)
524 continue;
525 id = i + INTF_0;
8998010c 526 ret = _dpu_rm_reserve_intf(rm, enc_id, id,
9816b226 527 DPU_HW_BLK_INTF);
25fdd593
JS
528 if (ret)
529 return ret;
530 }
531
532 return ret;
533}
534
8998010c 535static int _dpu_rm_make_reservation(
25fdd593
JS
536 struct dpu_rm *rm,
537 struct drm_encoder *enc,
538 struct drm_crtc_state *crtc_state,
25fdd593
JS
539 struct dpu_rm_requirements *reqs)
540{
541 int ret;
25fdd593 542
8998010c 543 ret = _dpu_rm_reserve_lms(rm, enc->base.id, reqs);
25fdd593
JS
544 if (ret) {
545 DPU_ERROR("unable to find appropriate mixers\n");
546 return ret;
547 }
548
8998010c 549 ret = _dpu_rm_reserve_ctls(rm, enc->base.id, &reqs->topology);
25fdd593
JS
550 if (ret) {
551 DPU_ERROR("unable to find appropriate CTL\n");
552 return ret;
553 }
554
8998010c 555 ret = _dpu_rm_reserve_intf_related_hw(rm, enc->base.id, &reqs->hw_res);
25fdd593
JS
556 if (ret)
557 return ret;
558
559 return ret;
560}
561
562static int _dpu_rm_populate_requirements(
563 struct dpu_rm *rm,
564 struct drm_encoder *enc,
565 struct drm_crtc_state *crtc_state,
25fdd593
JS
566 struct dpu_rm_requirements *reqs,
567 struct msm_display_topology req_topology)
568{
32ecf92a 569 dpu_encoder_get_hw_resources(enc, &reqs->hw_res);
25fdd593 570
ad92af7e 571 reqs->topology = req_topology;
25fdd593 572
ad92af7e
JS
573 DRM_DEBUG_KMS("num_lm: %d num_enc: %d num_intf: %d\n",
574 reqs->topology.num_lm, reqs->topology.num_enc,
575 reqs->topology.num_intf);
25fdd593
JS
576
577 return 0;
578}
579
8998010c 580static void _dpu_rm_release_reservation(struct dpu_rm *rm, uint32_t enc_id)
25fdd593 581{
25fdd593
JS
582 struct dpu_rm_hw_blk *blk;
583 enum dpu_hw_blk_type type;
584
25fdd593
JS
585 for (type = 0; type < DPU_HW_BLK_MAX; type++) {
586 list_for_each_entry(blk, &rm->hw_blks[type], list) {
8998010c
JS
587 if (blk->enc_id == enc_id) {
588 blk->enc_id = 0;
589 DPU_DEBUG("rel enc %d %d %d\n", enc_id,
1a5e1778 590 type, blk->id);
25fdd593
JS
591 }
592 }
593 }
25fdd593
JS
594}
595
596void dpu_rm_release(struct dpu_rm *rm, struct drm_encoder *enc)
597{
25fdd593
JS
598 mutex_lock(&rm->rm_lock);
599
8998010c 600 _dpu_rm_release_reservation(rm, enc->base.id);
25fdd593 601
25fdd593
JS
602 mutex_unlock(&rm->rm_lock);
603}
604
25fdd593
JS
605int dpu_rm_reserve(
606 struct dpu_rm *rm,
607 struct drm_encoder *enc,
608 struct drm_crtc_state *crtc_state,
25fdd593
JS
609 struct msm_display_topology topology,
610 bool test_only)
611{
25fdd593
JS
612 struct dpu_rm_requirements reqs;
613 int ret;
614
25fdd593
JS
615 /* Check if this is just a page-flip */
616 if (!drm_atomic_crtc_needs_modeset(crtc_state))
617 return 0;
618
32ecf92a
JS
619 DRM_DEBUG_KMS("reserving hw for enc %d crtc %d test_only %d\n",
620 enc->base.id, crtc_state->crtc->base.id, test_only);
25fdd593
JS
621
622 mutex_lock(&rm->rm_lock);
623
32ecf92a
JS
624 ret = _dpu_rm_populate_requirements(rm, enc, crtc_state, &reqs,
625 topology);
25fdd593
JS
626 if (ret) {
627 DPU_ERROR("failed to populate hw requirements\n");
628 goto end;
629 }
630
8998010c 631 ret = _dpu_rm_make_reservation(rm, enc, crtc_state, &reqs);
25fdd593
JS
632 if (ret) {
633 DPU_ERROR("failed to reserve hw resources: %d\n", ret);
8998010c 634 _dpu_rm_release_reservation(rm, enc->base.id);
4a0dc640 635 } else if (test_only) {
8998010c
JS
636 /* test_only: test the reservation and then undo */
637 DPU_DEBUG("test_only: discard test [enc: %d]\n",
638 enc->base.id);
639 _dpu_rm_release_reservation(rm, enc->base.id);
25fdd593
JS
640 }
641
25fdd593
JS
642end:
643 mutex_unlock(&rm->rm_lock);
644
645 return ret;
646}