xref: /openbmc/linux/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c (revision 740828c7)
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