Commit | Line | Data |
---|---|---|
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 | */ | |
23 | struct 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 | */ | |
36 | struct 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 |
43 | void 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 | ||
53 | static 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 | ||
86 | bool 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 | ||
97 | static 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 | ||
123 | int 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 | ||
142 | static 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 | ||
196 | int 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 | ||
279 | fail: | |
280 | dpu_rm_destroy(rm); | |
281 | ||
282 | return rc; | |
283 | } | |
284 | ||
ad92af7e JS |
285 | static 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 | */ | |
305 | static 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 |
361 | static 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 | ||
429 | static 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 |
480 | static 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 | ||
514 | static 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 | 535 | static 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 | ||
562 | static 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 | 580 | static 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 | ||
596 | void 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 |
605 | int 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 |
642 | end: |
643 | mutex_unlock(&rm->rm_lock); | |
644 | ||
645 | return ret; | |
646 | } |