1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
6 #define pr_fmt(fmt) "[drm:%s] " fmt, __func__
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"
18 static inline bool reserved_by_other(uint32_t *res_map, int idx,
21 return res_map[idx] && res_map[idx] != enc_id;
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
29 struct dpu_rm_requirements {
30 struct msm_display_topology topology;
31 struct dpu_encoder_hw_resources hw_res;
34 int dpu_rm_destroy(struct dpu_rm *rm)
38 for (i = 0; i < ARRAY_SIZE(rm->pingpong_blks); i++) {
39 struct dpu_hw_pingpong *hw;
41 if (rm->pingpong_blks[i]) {
42 hw = to_dpu_hw_pingpong(rm->pingpong_blks[i]);
43 dpu_hw_pingpong_destroy(hw);
46 for (i = 0; i < ARRAY_SIZE(rm->merge_3d_blks); i++) {
47 struct dpu_hw_merge_3d *hw;
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);
54 for (i = 0; i < ARRAY_SIZE(rm->mixer_blks); i++) {
55 struct dpu_hw_mixer *hw;
57 if (rm->mixer_blks[i]) {
58 hw = to_dpu_hw_mixer(rm->mixer_blks[i]);
59 dpu_hw_lm_destroy(hw);
62 for (i = 0; i < ARRAY_SIZE(rm->ctl_blks); i++) {
63 struct dpu_hw_ctl *hw;
65 if (rm->ctl_blks[i]) {
66 hw = to_dpu_hw_ctl(rm->ctl_blks[i]);
67 dpu_hw_ctl_destroy(hw);
70 for (i = 0; i < ARRAY_SIZE(rm->intf_blks); i++) {
71 struct dpu_hw_intf *hw;
73 if (rm->intf_blks[i]) {
74 hw = to_dpu_hw_intf(rm->intf_blks[i]);
75 dpu_hw_intf_destroy(hw);
82 int dpu_rm_init(struct dpu_rm *rm,
83 struct dpu_mdss_cfg *cat,
88 if (!rm || !cat || !mmio) {
89 DPU_ERROR("invalid kms\n");
93 /* Clear, setup lists */
94 memset(rm, 0, sizeof(*rm));
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];
101 if (lm->pingpong == PINGPONG_MAX) {
102 DPU_DEBUG("skip mixer %d without pingpong\n", lm->id);
106 if (lm->id < LM_0 || lm->id >= LM_MAX) {
107 DPU_ERROR("skip mixer %d with invalid id\n", lm->id);
110 hw = dpu_hw_lm_init(lm->id, mmio, cat);
111 if (IS_ERR_OR_NULL(hw)) {
113 DPU_ERROR("failed lm object creation: err %d\n", rc);
116 rm->mixer_blks[lm->id - LM_0] = &hw->base;
119 for (i = 0; i < cat->merge_3d_count; i++) {
120 struct dpu_hw_merge_3d *hw;
121 const struct dpu_merge_3d_cfg *merge_3d = &cat->merge_3d[i];
123 if (merge_3d->id < MERGE_3D_0 || merge_3d->id >= MERGE_3D_MAX) {
124 DPU_ERROR("skip merge_3d %d with invalid id\n", merge_3d->id);
127 hw = dpu_hw_merge_3d_init(merge_3d->id, mmio, cat);
128 if (IS_ERR_OR_NULL(hw)) {
130 DPU_ERROR("failed merge_3d object creation: err %d\n",
134 rm->merge_3d_blks[merge_3d->id - MERGE_3D_0] = &hw->base;
137 for (i = 0; i < cat->pingpong_count; i++) {
138 struct dpu_hw_pingpong *hw;
139 const struct dpu_pingpong_cfg *pp = &cat->pingpong[i];
141 if (pp->id < PINGPONG_0 || pp->id >= PINGPONG_MAX) {
142 DPU_ERROR("skip pingpong %d with invalid id\n", pp->id);
145 hw = dpu_hw_pingpong_init(pp->id, mmio, cat);
146 if (IS_ERR_OR_NULL(hw)) {
148 DPU_ERROR("failed pingpong object creation: err %d\n",
152 if (pp->merge_3d && pp->merge_3d < MERGE_3D_MAX)
153 hw->merge_3d = to_dpu_hw_merge_3d(rm->merge_3d_blks[pp->merge_3d - MERGE_3D_0]);
154 rm->pingpong_blks[pp->id - PINGPONG_0] = &hw->base;
157 for (i = 0; i < cat->intf_count; i++) {
158 struct dpu_hw_intf *hw;
159 const struct dpu_intf_cfg *intf = &cat->intf[i];
161 if (intf->type == INTF_NONE) {
162 DPU_DEBUG("skip intf %d with type none\n", i);
165 if (intf->id < INTF_0 || intf->id >= INTF_MAX) {
166 DPU_ERROR("skip intf %d with invalid id\n", intf->id);
169 hw = dpu_hw_intf_init(intf->id, mmio, cat);
170 if (IS_ERR_OR_NULL(hw)) {
172 DPU_ERROR("failed intf object creation: err %d\n", rc);
175 rm->intf_blks[intf->id - INTF_0] = &hw->base;
178 for (i = 0; i < cat->ctl_count; i++) {
179 struct dpu_hw_ctl *hw;
180 const struct dpu_ctl_cfg *ctl = &cat->ctl[i];
182 if (ctl->id < CTL_0 || ctl->id >= CTL_MAX) {
183 DPU_ERROR("skip ctl %d with invalid id\n", ctl->id);
186 hw = dpu_hw_ctl_init(ctl->id, mmio, cat);
187 if (IS_ERR_OR_NULL(hw)) {
189 DPU_ERROR("failed ctl object creation: err %d\n", rc);
192 rm->ctl_blks[ctl->id - CTL_0] = &hw->base;
195 for (i = 0; i < cat->dspp_count; i++) {
196 struct dpu_hw_dspp *hw;
197 const struct dpu_dspp_cfg *dspp = &cat->dspp[i];
199 if (dspp->id < DSPP_0 || dspp->id >= DSPP_MAX) {
200 DPU_ERROR("skip dspp %d with invalid id\n", dspp->id);
203 hw = dpu_hw_dspp_init(dspp->id, mmio, cat);
204 if (IS_ERR_OR_NULL(hw)) {
206 DPU_ERROR("failed dspp object creation: err %d\n", rc);
209 rm->dspp_blks[dspp->id - DSPP_0] = &hw->base;
217 return rc ? rc : -EFAULT;
220 static bool _dpu_rm_needs_split_display(const struct msm_display_topology *top)
222 return top->num_intf > 1;
226 * _dpu_rm_check_lm_peer - check if a mixer is a peer of the primary
227 * @rm: dpu resource manager handle
228 * @primary_idx: index of primary mixer in rm->mixer_blks[]
229 * @peer_idx: index of other mixer in rm->mixer_blks[]
230 * Return: true if rm->mixer_blks[peer_idx] is a peer of
231 * rm->mixer_blks[primary_idx]
233 static bool _dpu_rm_check_lm_peer(struct dpu_rm *rm, int primary_idx,
236 const struct dpu_lm_cfg *prim_lm_cfg;
237 const struct dpu_lm_cfg *peer_cfg;
239 prim_lm_cfg = to_dpu_hw_mixer(rm->mixer_blks[primary_idx])->cap;
240 peer_cfg = to_dpu_hw_mixer(rm->mixer_blks[peer_idx])->cap;
242 if (!test_bit(peer_cfg->id, &prim_lm_cfg->lm_pair_mask)) {
243 DPU_DEBUG("lm %d not peer of lm %d\n", peer_cfg->id,
251 * _dpu_rm_check_lm_and_get_connected_blks - check if proposed layer mixer meets
252 * proposed use case requirements, incl. hardwired dependent blocks like
254 * @rm: dpu resource manager handle
255 * @global_state: resources shared across multiple kms objects
256 * @enc_id: encoder id requesting for allocation
257 * @lm_idx: index of proposed layer mixer in rm->mixer_blks[], function checks
258 * if lm, and all other hardwired blocks connected to the lm (pp) is
259 * available and appropriate
260 * @pp_idx: output parameter, index of pingpong block attached to the layer
261 * mixer in rm->pingpong_blks[].
262 * @dspp_idx: output parameter, index of dspp block attached to the layer
263 * mixer in rm->dspp_blks[].
264 * @reqs: input parameter, rm requirements for HW blocks needed in the
266 * Return: true if lm matches all requirements, false otherwise
268 static bool _dpu_rm_check_lm_and_get_connected_blks(struct dpu_rm *rm,
269 struct dpu_global_state *global_state,
270 uint32_t enc_id, int lm_idx, int *pp_idx, int *dspp_idx,
271 struct dpu_rm_requirements *reqs)
273 const struct dpu_lm_cfg *lm_cfg;
276 /* Already reserved? */
277 if (reserved_by_other(global_state->mixer_to_enc_id, lm_idx, enc_id)) {
278 DPU_DEBUG("lm %d already reserved\n", lm_idx + LM_0);
282 lm_cfg = to_dpu_hw_mixer(rm->mixer_blks[lm_idx])->cap;
283 idx = lm_cfg->pingpong - PINGPONG_0;
284 if (idx < 0 || idx >= ARRAY_SIZE(rm->pingpong_blks)) {
285 DPU_ERROR("failed to get pp on lm %d\n", lm_cfg->pingpong);
289 if (reserved_by_other(global_state->pingpong_to_enc_id, idx, enc_id)) {
290 DPU_DEBUG("lm %d pp %d already reserved\n", lm_cfg->id,
296 if (!reqs->topology.num_dspp)
299 idx = lm_cfg->dspp - DSPP_0;
300 if (idx < 0 || idx >= ARRAY_SIZE(rm->dspp_blks)) {
301 DPU_ERROR("failed to get dspp on lm %d\n", lm_cfg->dspp);
305 if (reserved_by_other(global_state->dspp_to_enc_id, idx, enc_id)) {
306 DPU_DEBUG("lm %d dspp %d already reserved\n", lm_cfg->id,
315 static int _dpu_rm_reserve_lms(struct dpu_rm *rm,
316 struct dpu_global_state *global_state,
318 struct dpu_rm_requirements *reqs)
321 int lm_idx[MAX_BLOCKS];
322 int pp_idx[MAX_BLOCKS];
323 int dspp_idx[MAX_BLOCKS] = {0};
324 int i, j, lm_count = 0;
326 if (!reqs->topology.num_lm) {
327 DPU_ERROR("invalid number of lm: %d\n", reqs->topology.num_lm);
331 /* Find a primary mixer */
332 for (i = 0; i < ARRAY_SIZE(rm->mixer_blks) &&
333 lm_count < reqs->topology.num_lm; i++) {
334 if (!rm->mixer_blks[i])
338 lm_idx[lm_count] = i;
340 if (!_dpu_rm_check_lm_and_get_connected_blks(rm, global_state,
341 enc_id, i, &pp_idx[lm_count],
342 &dspp_idx[lm_count], reqs)) {
348 /* Valid primary mixer found, find matching peers */
349 for (j = i + 1; j < ARRAY_SIZE(rm->mixer_blks) &&
350 lm_count < reqs->topology.num_lm; j++) {
351 if (!rm->mixer_blks[j])
354 if (!_dpu_rm_check_lm_peer(rm, i, j)) {
355 DPU_DEBUG("lm %d not peer of lm %d\n", LM_0 + j,
360 if (!_dpu_rm_check_lm_and_get_connected_blks(rm,
361 global_state, enc_id, j,
362 &pp_idx[lm_count], &dspp_idx[lm_count],
367 lm_idx[lm_count] = j;
372 if (lm_count != reqs->topology.num_lm) {
373 DPU_DEBUG("unable to find appropriate mixers\n");
377 for (i = 0; i < lm_count; i++) {
378 global_state->mixer_to_enc_id[lm_idx[i]] = enc_id;
379 global_state->pingpong_to_enc_id[pp_idx[i]] = enc_id;
380 global_state->dspp_to_enc_id[dspp_idx[i]] =
381 reqs->topology.num_dspp ? enc_id : 0;
383 trace_dpu_rm_reserve_lms(lm_idx[i] + LM_0, enc_id,
384 pp_idx[i] + PINGPONG_0);
390 static int _dpu_rm_reserve_ctls(
392 struct dpu_global_state *global_state,
394 const struct msm_display_topology *top)
396 int ctl_idx[MAX_BLOCKS];
397 int i = 0, j, num_ctls;
398 bool needs_split_display;
400 /* each hw_intf needs its own hw_ctrl to program its control path */
401 num_ctls = top->num_intf;
403 needs_split_display = _dpu_rm_needs_split_display(top);
405 for (j = 0; j < ARRAY_SIZE(rm->ctl_blks); j++) {
406 const struct dpu_hw_ctl *ctl;
407 unsigned long features;
408 bool has_split_display;
410 if (!rm->ctl_blks[j])
412 if (reserved_by_other(global_state->ctl_to_enc_id, j, enc_id))
415 ctl = to_dpu_hw_ctl(rm->ctl_blks[j]);
416 features = ctl->caps->features;
417 has_split_display = BIT(DPU_CTL_SPLIT_DISPLAY) & features;
419 DPU_DEBUG("ctl %d caps 0x%lX\n", j + CTL_0, features);
421 if (needs_split_display != has_split_display)
425 DPU_DEBUG("ctl %d match\n", j + CTL_0);
435 for (i = 0; i < ARRAY_SIZE(ctl_idx) && i < num_ctls; i++) {
436 global_state->ctl_to_enc_id[ctl_idx[i]] = enc_id;
437 trace_dpu_rm_reserve_ctls(i + CTL_0, enc_id);
443 static int _dpu_rm_reserve_intf(
445 struct dpu_global_state *global_state,
449 int idx = id - INTF_0;
451 if (idx < 0 || idx >= ARRAY_SIZE(rm->intf_blks)) {
452 DPU_ERROR("invalid intf id: %d", id);
456 if (!rm->intf_blks[idx]) {
457 DPU_ERROR("couldn't find intf id %d\n", id);
461 if (reserved_by_other(global_state->intf_to_enc_id, idx, enc_id)) {
462 DPU_ERROR("intf id %d already reserved\n", id);
466 global_state->intf_to_enc_id[idx] = enc_id;
470 static int _dpu_rm_reserve_intf_related_hw(
472 struct dpu_global_state *global_state,
474 struct dpu_encoder_hw_resources *hw_res)
479 for (i = 0; i < ARRAY_SIZE(hw_res->intfs); i++) {
480 if (hw_res->intfs[i] == INTF_MODE_NONE)
483 ret = _dpu_rm_reserve_intf(rm, global_state, enc_id, id);
491 static int _dpu_rm_make_reservation(
493 struct dpu_global_state *global_state,
494 struct drm_encoder *enc,
495 struct dpu_rm_requirements *reqs)
499 ret = _dpu_rm_reserve_lms(rm, global_state, enc->base.id, reqs);
501 DPU_ERROR("unable to find appropriate mixers\n");
505 ret = _dpu_rm_reserve_ctls(rm, global_state, enc->base.id,
508 DPU_ERROR("unable to find appropriate CTL\n");
512 ret = _dpu_rm_reserve_intf_related_hw(rm, global_state, enc->base.id,
520 static int _dpu_rm_populate_requirements(
521 struct drm_encoder *enc,
522 struct dpu_rm_requirements *reqs,
523 struct msm_display_topology req_topology)
525 dpu_encoder_get_hw_resources(enc, &reqs->hw_res);
527 reqs->topology = req_topology;
529 DRM_DEBUG_KMS("num_lm: %d num_enc: %d num_intf: %d\n",
530 reqs->topology.num_lm, reqs->topology.num_enc,
531 reqs->topology.num_intf);
536 static void _dpu_rm_clear_mapping(uint32_t *res_mapping, int cnt,
541 for (i = 0; i < cnt; i++) {
542 if (res_mapping[i] == enc_id)
547 void dpu_rm_release(struct dpu_global_state *global_state,
548 struct drm_encoder *enc)
550 _dpu_rm_clear_mapping(global_state->pingpong_to_enc_id,
551 ARRAY_SIZE(global_state->pingpong_to_enc_id), enc->base.id);
552 _dpu_rm_clear_mapping(global_state->mixer_to_enc_id,
553 ARRAY_SIZE(global_state->mixer_to_enc_id), enc->base.id);
554 _dpu_rm_clear_mapping(global_state->ctl_to_enc_id,
555 ARRAY_SIZE(global_state->ctl_to_enc_id), enc->base.id);
556 _dpu_rm_clear_mapping(global_state->intf_to_enc_id,
557 ARRAY_SIZE(global_state->intf_to_enc_id), enc->base.id);
562 struct dpu_global_state *global_state,
563 struct drm_encoder *enc,
564 struct drm_crtc_state *crtc_state,
565 struct msm_display_topology topology)
567 struct dpu_rm_requirements reqs;
570 /* Check if this is just a page-flip */
571 if (!drm_atomic_crtc_needs_modeset(crtc_state))
574 if (IS_ERR(global_state)) {
575 DPU_ERROR("failed to global state\n");
576 return PTR_ERR(global_state);
579 DRM_DEBUG_KMS("reserving hw for enc %d crtc %d\n",
580 enc->base.id, crtc_state->crtc->base.id);
582 ret = _dpu_rm_populate_requirements(enc, &reqs, topology);
584 DPU_ERROR("failed to populate hw requirements\n");
588 ret = _dpu_rm_make_reservation(rm, global_state, enc, &reqs);
590 DPU_ERROR("failed to reserve hw resources: %d\n", ret);
597 int dpu_rm_get_assigned_resources(struct dpu_rm *rm,
598 struct dpu_global_state *global_state, uint32_t enc_id,
599 enum dpu_hw_blk_type type, struct dpu_hw_blk **blks, int blks_size)
601 struct dpu_hw_blk **hw_blks;
602 uint32_t *hw_to_enc_id;
603 int i, num_blks, max_blks;
606 case DPU_HW_BLK_PINGPONG:
607 hw_blks = rm->pingpong_blks;
608 hw_to_enc_id = global_state->pingpong_to_enc_id;
609 max_blks = ARRAY_SIZE(rm->pingpong_blks);
612 hw_blks = rm->mixer_blks;
613 hw_to_enc_id = global_state->mixer_to_enc_id;
614 max_blks = ARRAY_SIZE(rm->mixer_blks);
617 hw_blks = rm->ctl_blks;
618 hw_to_enc_id = global_state->ctl_to_enc_id;
619 max_blks = ARRAY_SIZE(rm->ctl_blks);
621 case DPU_HW_BLK_INTF:
622 hw_blks = rm->intf_blks;
623 hw_to_enc_id = global_state->intf_to_enc_id;
624 max_blks = ARRAY_SIZE(rm->intf_blks);
626 case DPU_HW_BLK_DSPP:
627 hw_blks = rm->dspp_blks;
628 hw_to_enc_id = global_state->dspp_to_enc_id;
629 max_blks = ARRAY_SIZE(rm->dspp_blks);
632 DPU_ERROR("blk type %d not managed by rm\n", type);
637 for (i = 0; i < max_blks; i++) {
638 if (hw_to_enc_id[i] != enc_id)
641 if (num_blks == blks_size) {
642 DPU_ERROR("More than %d resources assigned to enc %d\n",
646 blks[num_blks++] = hw_blks[i];