1 | /* |
2 | * Copyright (C) 2014 Intel Corporation |
3 | * |
4 | * DRM universal plane helper functions |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a |
7 | * copy of this software and associated documentation files (the "Software"), |
8 | * to deal in the Software without restriction, including without limitation |
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
10 | * and/or sell copies of the Software, and to permit persons to whom the |
11 | * Software is furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice (including the next |
14 | * paragraph) shall be included in all copies or substantial portions of the |
15 | * Software. |
16 | * |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
23 | * SOFTWARE. |
24 | */ |
25 | |
26 | #include <linux/list.h> |
27 | |
28 | #include <drm/drm_atomic.h> |
29 | #include <drm/drm_atomic_helper.h> |
30 | #include <drm/drm_atomic_uapi.h> |
31 | #include <drm/drm_device.h> |
32 | #include <drm/drm_drv.h> |
33 | #include <drm/drm_encoder.h> |
34 | #include <drm/drm_plane_helper.h> |
35 | #include <drm/drm_print.h> |
36 | #include <drm/drm_rect.h> |
37 | |
38 | #define SUBPIXEL_MASK 0xffff |
39 | |
40 | /** |
41 | * DOC: overview |
42 | * |
43 | * This helper library contains helpers to implement primary plane support on |
44 | * top of the normal CRTC configuration interface. |
45 | * Since the legacy &drm_mode_config_funcs.set_config interface ties the primary |
46 | * plane together with the CRTC state this does not allow userspace to disable |
47 | * the primary plane itself. The default primary plane only expose XRBG8888 and |
48 | * ARGB8888 as valid pixel formats for the attached framebuffer. |
49 | * |
50 | * Drivers are highly recommended to implement proper support for primary |
51 | * planes, and newly merged drivers must not rely upon these transitional |
52 | * helpers. |
53 | * |
54 | * The plane helpers share the function table structures with other helpers, |
55 | * specifically also the atomic helpers. See &struct drm_plane_helper_funcs for |
56 | * the details. |
57 | */ |
58 | |
59 | /* |
60 | * Returns the connectors currently associated with a CRTC. This function |
61 | * should be called twice: once with a NULL connector list to retrieve |
62 | * the list size, and once with the properly allocated list to be filled in. |
63 | */ |
64 | static int get_connectors_for_crtc(struct drm_crtc *crtc, |
65 | struct drm_connector **connector_list, |
66 | int num_connectors) |
67 | { |
68 | struct drm_device *dev = crtc->dev; |
69 | struct drm_connector *connector; |
70 | struct drm_connector_list_iter conn_iter; |
71 | int count = 0; |
72 | |
73 | /* |
74 | * Note: Once we change the plane hooks to more fine-grained locking we |
75 | * need to grab the connection_mutex here to be able to make these |
76 | * checks. |
77 | */ |
78 | WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); |
79 | |
80 | drm_connector_list_iter_begin(dev, iter: &conn_iter); |
81 | drm_for_each_connector_iter(connector, &conn_iter) { |
82 | if (connector->encoder && connector->encoder->crtc == crtc) { |
83 | if (connector_list != NULL && count < num_connectors) |
84 | *(connector_list++) = connector; |
85 | |
86 | count++; |
87 | } |
88 | } |
89 | drm_connector_list_iter_end(iter: &conn_iter); |
90 | |
91 | return count; |
92 | } |
93 | |
94 | static int drm_plane_helper_check_update(struct drm_plane *plane, |
95 | struct drm_crtc *crtc, |
96 | struct drm_framebuffer *fb, |
97 | struct drm_rect *src, |
98 | struct drm_rect *dst, |
99 | unsigned int rotation, |
100 | int min_scale, |
101 | int max_scale, |
102 | bool can_position, |
103 | bool can_update_disabled, |
104 | bool *visible) |
105 | { |
106 | struct drm_plane_state plane_state = { |
107 | .plane = plane, |
108 | .crtc = crtc, |
109 | .fb = fb, |
110 | .src_x = src->x1, |
111 | .src_y = src->y1, |
112 | .src_w = drm_rect_width(r: src), |
113 | .src_h = drm_rect_height(r: src), |
114 | .crtc_x = dst->x1, |
115 | .crtc_y = dst->y1, |
116 | .crtc_w = drm_rect_width(r: dst), |
117 | .crtc_h = drm_rect_height(r: dst), |
118 | .rotation = rotation, |
119 | }; |
120 | struct drm_crtc_state crtc_state = { |
121 | .crtc = crtc, |
122 | .enable = crtc->enabled, |
123 | .mode = crtc->mode, |
124 | }; |
125 | int ret; |
126 | |
127 | ret = drm_atomic_helper_check_plane_state(plane_state: &plane_state, crtc_state: &crtc_state, |
128 | min_scale, max_scale, |
129 | can_position, |
130 | can_update_disabled); |
131 | if (ret) |
132 | return ret; |
133 | |
134 | *src = plane_state.src; |
135 | *dst = plane_state.dst; |
136 | *visible = plane_state.visible; |
137 | |
138 | return 0; |
139 | } |
140 | |
141 | /** |
142 | * drm_plane_helper_update_primary - Helper for updating primary planes |
143 | * @plane: plane to update |
144 | * @crtc: the plane's new CRTC |
145 | * @fb: the plane's new framebuffer |
146 | * @crtc_x: x coordinate within CRTC |
147 | * @crtc_y: y coordinate within CRTC |
148 | * @crtc_w: width coordinate within CRTC |
149 | * @crtc_h: height coordinate within CRTC |
150 | * @src_x: x coordinate within source |
151 | * @src_y: y coordinate within source |
152 | * @src_w: width coordinate within source |
153 | * @src_h: height coordinate within source |
154 | * @ctx: modeset locking context |
155 | * |
156 | * This helper validates the given parameters and updates the primary plane. |
157 | * |
158 | * This function is only useful for non-atomic modesetting. Don't use |
159 | * it in new drivers. |
160 | * |
161 | * Returns: |
162 | * Zero on success, or an errno code otherwise. |
163 | */ |
164 | int drm_plane_helper_update_primary(struct drm_plane *plane, struct drm_crtc *crtc, |
165 | struct drm_framebuffer *fb, |
166 | int crtc_x, int crtc_y, |
167 | unsigned int crtc_w, unsigned int crtc_h, |
168 | uint32_t src_x, uint32_t src_y, |
169 | uint32_t src_w, uint32_t src_h, |
170 | struct drm_modeset_acquire_ctx *ctx) |
171 | { |
172 | struct drm_mode_set set = { |
173 | .crtc = crtc, |
174 | .fb = fb, |
175 | .mode = &crtc->mode, |
176 | .x = src_x >> 16, |
177 | .y = src_y >> 16, |
178 | }; |
179 | struct drm_rect src = { |
180 | .x1 = src_x, |
181 | .y1 = src_y, |
182 | .x2 = src_x + src_w, |
183 | .y2 = src_y + src_h, |
184 | }; |
185 | struct drm_rect dest = { |
186 | .x1 = crtc_x, |
187 | .y1 = crtc_y, |
188 | .x2 = crtc_x + crtc_w, |
189 | .y2 = crtc_y + crtc_h, |
190 | }; |
191 | struct drm_device *dev = plane->dev; |
192 | struct drm_connector **connector_list; |
193 | int num_connectors, ret; |
194 | bool visible; |
195 | |
196 | if (drm_WARN_ON_ONCE(dev, drm_drv_uses_atomic_modeset(dev))) |
197 | return -EINVAL; |
198 | |
199 | ret = drm_plane_helper_check_update(plane, crtc, fb, |
200 | src: &src, dst: &dest, |
201 | DRM_MODE_ROTATE_0, |
202 | DRM_PLANE_NO_SCALING, |
203 | DRM_PLANE_NO_SCALING, |
204 | can_position: false, can_update_disabled: false, visible: &visible); |
205 | if (ret) |
206 | return ret; |
207 | |
208 | if (!visible) |
209 | /* |
210 | * Primary plane isn't visible. Note that unless a driver |
211 | * provides their own disable function, this will just |
212 | * wind up returning -EINVAL to userspace. |
213 | */ |
214 | return plane->funcs->disable_plane(plane, ctx); |
215 | |
216 | /* Find current connectors for CRTC */ |
217 | num_connectors = get_connectors_for_crtc(crtc, NULL, num_connectors: 0); |
218 | BUG_ON(num_connectors == 0); |
219 | connector_list = kcalloc(n: num_connectors, size: sizeof(*connector_list), |
220 | GFP_KERNEL); |
221 | if (!connector_list) |
222 | return -ENOMEM; |
223 | get_connectors_for_crtc(crtc, connector_list, num_connectors); |
224 | |
225 | set.connectors = connector_list; |
226 | set.num_connectors = num_connectors; |
227 | |
228 | /* |
229 | * We call set_config() directly here rather than using |
230 | * drm_mode_set_config_internal. We're reprogramming the same |
231 | * connectors that were already in use, so we shouldn't need the extra |
232 | * cross-CRTC fb refcounting to accommodate stealing connectors. |
233 | * drm_mode_setplane() already handles the basic refcounting for the |
234 | * framebuffers involved in this operation. |
235 | */ |
236 | ret = crtc->funcs->set_config(&set, ctx); |
237 | |
238 | kfree(objp: connector_list); |
239 | return ret; |
240 | } |
241 | EXPORT_SYMBOL(drm_plane_helper_update_primary); |
242 | |
243 | /** |
244 | * drm_plane_helper_disable_primary - Helper for disabling primary planes |
245 | * @plane: plane to disable |
246 | * @ctx: modeset locking context |
247 | * |
248 | * This helper returns an error when trying to disable the primary |
249 | * plane. |
250 | * |
251 | * This function is only useful for non-atomic modesetting. Don't use |
252 | * it in new drivers. |
253 | * |
254 | * Returns: |
255 | * An errno code. |
256 | */ |
257 | int drm_plane_helper_disable_primary(struct drm_plane *plane, |
258 | struct drm_modeset_acquire_ctx *ctx) |
259 | { |
260 | struct drm_device *dev = plane->dev; |
261 | |
262 | drm_WARN_ON_ONCE(dev, drm_drv_uses_atomic_modeset(dev)); |
263 | |
264 | return -EINVAL; |
265 | } |
266 | EXPORT_SYMBOL(drm_plane_helper_disable_primary); |
267 | |
268 | /** |
269 | * drm_plane_helper_destroy() - Helper for primary plane destruction |
270 | * @plane: plane to destroy |
271 | * |
272 | * Provides a default plane destroy handler for primary planes. This handler |
273 | * is called during CRTC destruction. We disable the primary plane, remove |
274 | * it from the DRM plane list, and deallocate the plane structure. |
275 | */ |
276 | void drm_plane_helper_destroy(struct drm_plane *plane) |
277 | { |
278 | drm_plane_cleanup(plane); |
279 | kfree(objp: plane); |
280 | } |
281 | EXPORT_SYMBOL(drm_plane_helper_destroy); |
282 | |
283 | /** |
284 | * drm_plane_helper_atomic_check() - Helper to check plane atomic-state |
285 | * @plane: plane to check |
286 | * @state: atomic state object |
287 | * |
288 | * Provides a default plane-state check handler for planes whose atomic-state |
289 | * scale and positioning are not expected to change since the plane is always |
290 | * a fullscreen scanout buffer. |
291 | * |
292 | * This is often the case for the primary plane of simple framebuffers. See |
293 | * also drm_crtc_helper_atomic_check() for the respective CRTC-state check |
294 | * helper function. |
295 | * |
296 | * RETURNS: |
297 | * Zero on success, or an errno code otherwise. |
298 | */ |
299 | int drm_plane_helper_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state) |
300 | { |
301 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); |
302 | struct drm_crtc *new_crtc = new_plane_state->crtc; |
303 | struct drm_crtc_state *new_crtc_state = NULL; |
304 | |
305 | if (new_crtc) |
306 | new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc: new_crtc); |
307 | |
308 | return drm_atomic_helper_check_plane_state(plane_state: new_plane_state, crtc_state: new_crtc_state, |
309 | DRM_PLANE_NO_SCALING, |
310 | DRM_PLANE_NO_SCALING, |
311 | can_position: false, can_update_disabled: false); |
312 | } |
313 | EXPORT_SYMBOL(drm_plane_helper_atomic_check); |
314 | |