Commit | Line | Data |
---|---|---|
c103d1cf MR |
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> | |
0500c04e | 27 | |
321ebf04 | 28 | #include <drm/drm_atomic.h> |
0500c04e | 29 | #include <drm/drm_atomic_helper.h> |
72fdb40c | 30 | #include <drm/drm_atomic_uapi.h> |
acf24a39 | 31 | #include <drm/drm_crtc_helper.h> |
0500c04e | 32 | #include <drm/drm_device.h> |
fa0706e9 | 33 | #include <drm/drm_drv.h> |
9338203c | 34 | #include <drm/drm_encoder.h> |
0500c04e | 35 | #include <drm/drm_plane_helper.h> |
fa0706e9 | 36 | #include <drm/drm_print.h> |
0500c04e | 37 | #include <drm/drm_rect.h> |
c103d1cf MR |
38 | |
39 | #define SUBPIXEL_MASK 0xffff | |
40 | ||
3150c7d0 DV |
41 | /** |
42 | * DOC: overview | |
43 | * | |
44 | * This helper library has two parts. The first part has support to implement | |
45 | * primary plane support on top of the normal CRTC configuration interface. | |
6806cdf9 DV |
46 | * Since the legacy &drm_mode_config_funcs.set_config interface ties the primary |
47 | * plane together with the CRTC state this does not allow userspace to disable | |
6b6fce62 DV |
48 | * the primary plane itself. The default primary plane only expose XRBG8888 and |
49 | * ARGB8888 as valid pixel formats for the attached framebuffer. | |
3150c7d0 DV |
50 | * |
51 | * Drivers are highly recommended to implement proper support for primary | |
52 | * planes, and newly merged drivers must not rely upon these transitional | |
53 | * helpers. | |
54 | * | |
55 | * The second part also implements transitional helpers which allow drivers to | |
56 | * gradually switch to the atomic helper infrastructure for plane updates. Once | |
57 | * that switch is complete drivers shouldn't use these any longer, instead using | |
58 | * the proper legacy implementations for update and disable plane hooks provided | |
59 | * by the atomic helpers. | |
60 | * | |
61 | * Again drivers are strongly urged to switch to the new interfaces. | |
092d01da DV |
62 | * |
63 | * The plane helpers share the function table structures with other helpers, | |
ea0dd85a | 64 | * specifically also the atomic helpers. See &struct drm_plane_helper_funcs for |
092d01da | 65 | * the details. |
3150c7d0 DV |
66 | */ |
67 | ||
c103d1cf MR |
68 | /* |
69 | * Returns the connectors currently associated with a CRTC. This function | |
70 | * should be called twice: once with a NULL connector list to retrieve | |
71 | * the list size, and once with the properly allocated list to be filled in. | |
72 | */ | |
73 | static int get_connectors_for_crtc(struct drm_crtc *crtc, | |
74 | struct drm_connector **connector_list, | |
75 | int num_connectors) | |
76 | { | |
77 | struct drm_device *dev = crtc->dev; | |
78 | struct drm_connector *connector; | |
c36a3254 | 79 | struct drm_connector_list_iter conn_iter; |
c103d1cf MR |
80 | int count = 0; |
81 | ||
6e9f798d DV |
82 | /* |
83 | * Note: Once we change the plane hooks to more fine-grained locking we | |
84 | * need to grab the connection_mutex here to be able to make these | |
85 | * checks. | |
86 | */ | |
51fd371b | 87 | WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); |
6e9f798d | 88 | |
b982dab1 | 89 | drm_connector_list_iter_begin(dev, &conn_iter); |
c36a3254 | 90 | drm_for_each_connector_iter(connector, &conn_iter) { |
c103d1cf MR |
91 | if (connector->encoder && connector->encoder->crtc == crtc) { |
92 | if (connector_list != NULL && count < num_connectors) | |
93 | *(connector_list++) = connector; | |
94 | ||
95 | count++; | |
96 | } | |
9a9f5ce8 | 97 | } |
b982dab1 | 98 | drm_connector_list_iter_end(&conn_iter); |
c103d1cf MR |
99 | |
100 | return count; | |
101 | } | |
102 | ||
84c08517 DV |
103 | static int drm_plane_helper_check_update(struct drm_plane *plane, |
104 | struct drm_crtc *crtc, | |
105 | struct drm_framebuffer *fb, | |
106 | struct drm_rect *src, | |
107 | struct drm_rect *dst, | |
108 | unsigned int rotation, | |
109 | int min_scale, | |
110 | int max_scale, | |
111 | bool can_position, | |
112 | bool can_update_disabled, | |
113 | bool *visible) | |
df86af91 | 114 | { |
10b47ee0 | 115 | struct drm_plane_state plane_state = { |
df86af91 VS |
116 | .plane = plane, |
117 | .crtc = crtc, | |
118 | .fb = fb, | |
119 | .src_x = src->x1, | |
120 | .src_y = src->y1, | |
121 | .src_w = drm_rect_width(src), | |
122 | .src_h = drm_rect_height(src), | |
123 | .crtc_x = dst->x1, | |
124 | .crtc_y = dst->y1, | |
125 | .crtc_w = drm_rect_width(dst), | |
126 | .crtc_h = drm_rect_height(dst), | |
127 | .rotation = rotation, | |
df86af91 | 128 | }; |
10b47ee0 VS |
129 | struct drm_crtc_state crtc_state = { |
130 | .crtc = crtc, | |
131 | .enable = crtc->enabled, | |
81af63a4 | 132 | .mode = crtc->mode, |
10b47ee0 | 133 | }; |
df86af91 VS |
134 | int ret; |
135 | ||
a01cb8ba | 136 | ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, |
81af63a4 | 137 | min_scale, max_scale, |
a01cb8ba VS |
138 | can_position, |
139 | can_update_disabled); | |
df86af91 VS |
140 | if (ret) |
141 | return ret; | |
142 | ||
10b47ee0 VS |
143 | *src = plane_state.src; |
144 | *dst = plane_state.dst; | |
145 | *visible = plane_state.visible; | |
df86af91 VS |
146 | |
147 | return 0; | |
148 | } | |
7daf8d54 | 149 | |
30c63715 TZ |
150 | /** |
151 | * drm_plane_helper_update_primary - Helper for updating primary planes | |
152 | * @plane: plane to update | |
153 | * @crtc: the plane's new CRTC | |
154 | * @fb: the plane's new framebuffer | |
155 | * @crtc_x: x coordinate within CRTC | |
156 | * @crtc_y: y coordinate within CRTC | |
157 | * @crtc_w: width coordinate within CRTC | |
158 | * @crtc_h: height coordinate within CRTC | |
159 | * @src_x: x coordinate within source | |
160 | * @src_y: y coordinate within source | |
161 | * @src_w: width coordinate within source | |
162 | * @src_h: height coordinate within source | |
163 | * @ctx: modeset locking context | |
164 | * | |
165 | * This helper validates the given parameters and updates the primary plane. | |
166 | * | |
167 | * This function is only useful for non-atomic modesetting. Don't use | |
168 | * it in new drivers. | |
169 | * | |
170 | * Returns: | |
171 | * Zero on success, or an errno code otherwise. | |
172 | */ | |
173 | int drm_plane_helper_update_primary(struct drm_plane *plane, struct drm_crtc *crtc, | |
174 | struct drm_framebuffer *fb, | |
175 | int crtc_x, int crtc_y, | |
176 | unsigned int crtc_w, unsigned int crtc_h, | |
177 | uint32_t src_x, uint32_t src_y, | |
178 | uint32_t src_w, uint32_t src_h, | |
179 | struct drm_modeset_acquire_ctx *ctx) | |
c103d1cf MR |
180 | { |
181 | struct drm_mode_set set = { | |
182 | .crtc = crtc, | |
183 | .fb = fb, | |
184 | .mode = &crtc->mode, | |
185 | .x = src_x >> 16, | |
186 | .y = src_y >> 16, | |
187 | }; | |
7daf8d54 MR |
188 | struct drm_rect src = { |
189 | .x1 = src_x, | |
190 | .y1 = src_y, | |
191 | .x2 = src_x + src_w, | |
192 | .y2 = src_y + src_h, | |
193 | }; | |
c103d1cf MR |
194 | struct drm_rect dest = { |
195 | .x1 = crtc_x, | |
196 | .y1 = crtc_y, | |
197 | .x2 = crtc_x + crtc_w, | |
198 | .y2 = crtc_y + crtc_h, | |
199 | }; | |
fa0706e9 | 200 | struct drm_device *dev = plane->dev; |
c103d1cf | 201 | struct drm_connector **connector_list; |
c103d1cf | 202 | int num_connectors, ret; |
7daf8d54 | 203 | bool visible; |
c103d1cf | 204 | |
fa0706e9 TZ |
205 | if (drm_WARN_ON_ONCE(dev, drm_drv_uses_atomic_modeset(dev))) |
206 | return -EINVAL; | |
207 | ||
7daf8d54 | 208 | ret = drm_plane_helper_check_update(plane, crtc, fb, |
81af63a4 | 209 | &src, &dest, |
c2c446ad | 210 | DRM_MODE_ROTATE_0, |
cce32e4e TZ |
211 | DRM_PLANE_NO_SCALING, |
212 | DRM_PLANE_NO_SCALING, | |
7daf8d54 | 213 | false, false, &visible); |
c103d1cf MR |
214 | if (ret) |
215 | return ret; | |
216 | ||
7daf8d54 MR |
217 | if (!visible) |
218 | /* | |
219 | * Primary plane isn't visible. Note that unless a driver | |
220 | * provides their own disable function, this will just | |
221 | * wind up returning -EINVAL to userspace. | |
222 | */ | |
19315294 | 223 | return plane->funcs->disable_plane(plane, ctx); |
7daf8d54 | 224 | |
c103d1cf MR |
225 | /* Find current connectors for CRTC */ |
226 | num_connectors = get_connectors_for_crtc(crtc, NULL, 0); | |
227 | BUG_ON(num_connectors == 0); | |
4b947b1c | 228 | connector_list = kcalloc(num_connectors, sizeof(*connector_list), |
c103d1cf MR |
229 | GFP_KERNEL); |
230 | if (!connector_list) | |
231 | return -ENOMEM; | |
232 | get_connectors_for_crtc(crtc, connector_list, num_connectors); | |
233 | ||
234 | set.connectors = connector_list; | |
235 | set.num_connectors = num_connectors; | |
236 | ||
237 | /* | |
0fe27f06 | 238 | * We call set_config() directly here rather than using |
c103d1cf MR |
239 | * drm_mode_set_config_internal. We're reprogramming the same |
240 | * connectors that were already in use, so we shouldn't need the extra | |
0ae865ef | 241 | * cross-CRTC fb refcounting to accommodate stealing connectors. |
c103d1cf MR |
242 | * drm_mode_setplane() already handles the basic refcounting for the |
243 | * framebuffers involved in this operation. | |
244 | */ | |
a4eff9aa | 245 | ret = crtc->funcs->set_config(&set, ctx); |
c103d1cf MR |
246 | |
247 | kfree(connector_list); | |
248 | return ret; | |
249 | } | |
30c63715 | 250 | EXPORT_SYMBOL(drm_plane_helper_update_primary); |
c103d1cf | 251 | |
30c63715 TZ |
252 | /** |
253 | * drm_plane_helper_disable_primary - Helper for disabling primary planes | |
254 | * @plane: plane to disable | |
255 | * @ctx: modeset locking context | |
256 | * | |
257 | * This helper returns an error when trying to disable the primary | |
258 | * plane. | |
259 | * | |
260 | * This function is only useful for non-atomic modesetting. Don't use | |
261 | * it in new drivers. | |
262 | * | |
263 | * Returns: | |
264 | * An errno code. | |
265 | */ | |
266 | int drm_plane_helper_disable_primary(struct drm_plane *plane, | |
267 | struct drm_modeset_acquire_ctx *ctx) | |
c103d1cf | 268 | { |
fa0706e9 TZ |
269 | struct drm_device *dev = plane->dev; |
270 | ||
271 | drm_WARN_ON_ONCE(dev, drm_drv_uses_atomic_modeset(dev)); | |
272 | ||
b6ccd7b9 | 273 | return -EINVAL; |
c103d1cf | 274 | } |
30c63715 | 275 | EXPORT_SYMBOL(drm_plane_helper_disable_primary); |
c103d1cf MR |
276 | |
277 | /** | |
30c63715 | 278 | * drm_plane_helper_destroy() - Helper for primary plane destruction |
c103d1cf MR |
279 | * @plane: plane to destroy |
280 | * | |
281 | * Provides a default plane destroy handler for primary planes. This handler | |
282 | * is called during CRTC destruction. We disable the primary plane, remove | |
283 | * it from the DRM plane list, and deallocate the plane structure. | |
284 | */ | |
30c63715 | 285 | void drm_plane_helper_destroy(struct drm_plane *plane) |
c103d1cf | 286 | { |
c103d1cf MR |
287 | drm_plane_cleanup(plane); |
288 | kfree(plane); | |
289 | } | |
30c63715 | 290 | EXPORT_SYMBOL(drm_plane_helper_destroy); |
8401bd36 JMC |
291 | |
292 | /** | |
293 | * drm_plane_helper_atomic_check() - Helper to check plane atomic-state | |
294 | * @plane: plane to check | |
295 | * @state: atomic state object | |
296 | * | |
297 | * Provides a default plane-state check handler for planes whose atomic-state | |
298 | * scale and positioning are not expected to change since the plane is always | |
299 | * a fullscreen scanout buffer. | |
300 | * | |
7fed7fa3 JMC |
301 | * This is often the case for the primary plane of simple framebuffers. See |
302 | * also drm_crtc_helper_atomic_check() for the respective CRTC-state check | |
303 | * helper function. | |
8401bd36 JMC |
304 | * |
305 | * RETURNS: | |
306 | * Zero on success, or an errno code otherwise. | |
307 | */ | |
308 | int drm_plane_helper_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state) | |
309 | { | |
310 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); | |
311 | struct drm_crtc *new_crtc = new_plane_state->crtc; | |
312 | struct drm_crtc_state *new_crtc_state = NULL; | |
313 | ||
314 | if (new_crtc) | |
315 | new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); | |
316 | ||
317 | return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, | |
318 | DRM_PLANE_NO_SCALING, | |
319 | DRM_PLANE_NO_SCALING, | |
320 | false, false); | |
321 | } | |
322 | EXPORT_SYMBOL(drm_plane_helper_atomic_check); |