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