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