1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2016 Red Hat
4  * Author: Rob Clark <robdclark@gmail.com>
5  */
6 
7 #include "mdp5_kms.h"
8 
9 int mdp5_pipe_assign(struct drm_atomic_state *s, struct drm_plane *plane,
10 		     uint32_t caps, uint32_t blkcfg,
11 		     struct mdp5_hw_pipe **hwpipe,
12 		     struct mdp5_hw_pipe **r_hwpipe)
13 {
14 	struct msm_drm_private *priv = s->dev->dev_private;
15 	struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms));
16 	struct mdp5_global_state *new_global_state, *old_global_state;
17 	struct mdp5_hw_pipe_state *old_state, *new_state;
18 	int i, j;
19 
20 	new_global_state = mdp5_get_global_state(s);
21 	if (IS_ERR(new_global_state))
22 		return PTR_ERR(new_global_state);
23 
24 	/* grab old_state after mdp5_get_global_state(), since now we hold lock: */
25 	old_global_state = mdp5_get_existing_global_state(mdp5_kms);
26 
27 	old_state = &old_global_state->hwpipe;
28 	new_state = &new_global_state->hwpipe;
29 
30 	for (i = 0; i < mdp5_kms->num_hwpipes; i++) {
31 		struct mdp5_hw_pipe *cur = mdp5_kms->hwpipes[i];
32 
33 		/* skip if already in-use.. check both new and old state,
34 		 * since we cannot immediately re-use a pipe that is
35 		 * released in the current update in some cases:
36 		 *  (1) mdp5 can have SMP (non-double-buffered)
37 		 *  (2) hw pipe previously assigned to different CRTC
38 		 *      (vblanks might not be aligned)
39 		 */
40 		if (new_state->hwpipe_to_plane[cur->idx] ||
41 				old_state->hwpipe_to_plane[cur->idx])
42 			continue;
43 
44 		/* skip if doesn't support some required caps: */
45 		if (caps & ~cur->caps)
46 			continue;
47 
48 		/*
49 		 * don't assign a cursor pipe to a plane that isn't going to
50 		 * be used as a cursor
51 		 */
52 		if (cur->caps & MDP_PIPE_CAP_CURSOR &&
53 				plane->type != DRM_PLANE_TYPE_CURSOR)
54 			continue;
55 
56 		/* possible candidate, take the one with the
57 		 * fewest unneeded caps bits set:
58 		 */
59 		if (!(*hwpipe) || (hweight_long(cur->caps & ~caps) <
60 				   hweight_long((*hwpipe)->caps & ~caps))) {
61 			bool r_found = false;
62 
63 			if (r_hwpipe) {
64 				for (j = i + 1; j < mdp5_kms->num_hwpipes;
65 				     j++) {
66 					struct mdp5_hw_pipe *r_cur =
67 							mdp5_kms->hwpipes[j];
68 
69 					/* reject different types of hwpipes */
70 					if (r_cur->caps != cur->caps)
71 						continue;
72 
73 					/* respect priority, eg. VIG0 > VIG1 */
74 					if (cur->pipe > r_cur->pipe)
75 						continue;
76 
77 					*r_hwpipe = r_cur;
78 					r_found = true;
79 					break;
80 				}
81 			}
82 
83 			if (!r_hwpipe || r_found)
84 				*hwpipe = cur;
85 		}
86 	}
87 
88 	if (!(*hwpipe))
89 		return -ENOMEM;
90 
91 	if (r_hwpipe && !(*r_hwpipe))
92 		return -ENOMEM;
93 
94 	if (mdp5_kms->smp) {
95 		int ret;
96 
97 		/* We don't support SMP and 2 hwpipes/plane together */
98 		WARN_ON(r_hwpipe);
99 
100 		DBG("%s: alloc SMP blocks", (*hwpipe)->name);
101 		ret = mdp5_smp_assign(mdp5_kms->smp, &new_global_state->smp,
102 				(*hwpipe)->pipe, blkcfg);
103 		if (ret)
104 			return -ENOMEM;
105 
106 		(*hwpipe)->blkcfg = blkcfg;
107 	}
108 
109 	DBG("%s: assign to plane %s for caps %x",
110 			(*hwpipe)->name, plane->name, caps);
111 	new_state->hwpipe_to_plane[(*hwpipe)->idx] = plane;
112 
113 	if (r_hwpipe) {
114 		DBG("%s: assign to right of plane %s for caps %x",
115 		    (*r_hwpipe)->name, plane->name, caps);
116 		new_state->hwpipe_to_plane[(*r_hwpipe)->idx] = plane;
117 	}
118 
119 	return 0;
120 }
121 
122 void mdp5_pipe_release(struct drm_atomic_state *s, struct mdp5_hw_pipe *hwpipe)
123 {
124 	struct msm_drm_private *priv = s->dev->dev_private;
125 	struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms));
126 	struct mdp5_global_state *state = mdp5_get_global_state(s);
127 	struct mdp5_hw_pipe_state *new_state = &state->hwpipe;
128 
129 	if (!hwpipe)
130 		return;
131 
132 	if (WARN_ON(!new_state->hwpipe_to_plane[hwpipe->idx]))
133 		return;
134 
135 	DBG("%s: release from plane %s", hwpipe->name,
136 		new_state->hwpipe_to_plane[hwpipe->idx]->name);
137 
138 	if (mdp5_kms->smp) {
139 		DBG("%s: free SMP blocks", hwpipe->name);
140 		mdp5_smp_release(mdp5_kms->smp, &state->smp, hwpipe->pipe);
141 	}
142 
143 	new_state->hwpipe_to_plane[hwpipe->idx] = NULL;
144 }
145 
146 void mdp5_pipe_destroy(struct mdp5_hw_pipe *hwpipe)
147 {
148 	kfree(hwpipe);
149 }
150 
151 struct mdp5_hw_pipe *mdp5_pipe_init(enum mdp5_pipe pipe,
152 		uint32_t reg_offset, uint32_t caps)
153 {
154 	struct mdp5_hw_pipe *hwpipe;
155 
156 	hwpipe = kzalloc(sizeof(*hwpipe), GFP_KERNEL);
157 	if (!hwpipe)
158 		return ERR_PTR(-ENOMEM);
159 
160 	hwpipe->name = pipe2name(pipe);
161 	hwpipe->pipe = pipe;
162 	hwpipe->reg_offset = reg_offset;
163 	hwpipe->caps = caps;
164 	hwpipe->flush_mask = mdp_ctl_flush_mask_pipe(pipe);
165 
166 	return hwpipe;
167 }
168