diff options
author | Dave Airlie <airlied@redhat.com> | 2018-09-13 19:43:06 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2018-09-13 19:43:16 -0400 |
commit | 2dc7bad71cd310dc94d1c9907909324dd2b0618f (patch) | |
tree | a087555dd4b1588eeac02c1af5d8dde7b5bb15fa /drivers/gpu/drm/drm_atomic_uapi.c | |
parent | b1c1566822ab489a945dfdafee651aa29de160c7 (diff) | |
parent | 169cc4c7a14e988985c8833ddec2f3e897de2c28 (diff) |
Merge tag 'drm-misc-next-2018-09-13' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for 4.20:
UAPI Changes:
- Add host endian variants for the most common formats (Gerd)
- Fail ADDFB2 for big-endian drivers that don't advertise BE quirk (Gerd)
- clear smem_start in fbdev for drm drivers to avoid leaking fb addr (Daniel)
Cross-subsystem Changes:
Core Changes:
- fix drm_mode_addfb() on big endian machines (Gerd)
- add timeline point to syncobj find+replace (Chunming)
- more drmP.h removal effort (Daniel)
- split uapi portions of drm_atomic.c into drm_atomic_uapi.c (Daniel)
Driver Changes:
- bochs: Convert open-coded portions to use helpers (Peter)
- vkms: Add cursor support (Haneen)
- udmabuf: Lots of fixups (mostly cosmetic afaict) (Gerd)
- qxl: Convert to use fbdev helper (Peter)
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Chunming Zhou <david1.zhou@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Peter Wu <peter@lekensteyn.nl>
Cc: Haneen Mohammed <hamohammed.sa@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Sean Paul <sean@poorly.run>
Link: https://patchwork.freedesktop.org/patch/msgid/20180913130254.GA156437@art_vandelay
Diffstat (limited to 'drivers/gpu/drm/drm_atomic_uapi.c')
-rw-r--r-- | drivers/gpu/drm/drm_atomic_uapi.c | 1393 |
1 files changed, 1393 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c new file mode 100644 index 000000000000..26690a664ec6 --- /dev/null +++ b/drivers/gpu/drm/drm_atomic_uapi.c | |||
@@ -0,0 +1,1393 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014 Red Hat | ||
3 | * Copyright (C) 2014 Intel Corp. | ||
4 | * Copyright (C) 2018 Intel Corp. | ||
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 shall be included in | ||
14 | * all copies or substantial portions of the Software. | ||
15 | * | ||
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
19 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
20 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
22 | * OTHER DEALINGS IN THE SOFTWARE. | ||
23 | * | ||
24 | * Authors: | ||
25 | * Rob Clark <robdclark@gmail.com> | ||
26 | * Daniel Vetter <daniel.vetter@ffwll.ch> | ||
27 | */ | ||
28 | |||
29 | #include <drm/drm_atomic_uapi.h> | ||
30 | #include <drm/drm_atomic.h> | ||
31 | #include <drm/drm_print.h> | ||
32 | #include <drm/drm_drv.h> | ||
33 | #include <drm/drm_writeback.h> | ||
34 | #include <drm/drm_vblank.h> | ||
35 | |||
36 | #include <linux/dma-fence.h> | ||
37 | #include <linux/uaccess.h> | ||
38 | #include <linux/sync_file.h> | ||
39 | #include <linux/file.h> | ||
40 | |||
41 | #include "drm_crtc_internal.h" | ||
42 | |||
43 | /** | ||
44 | * DOC: overview | ||
45 | * | ||
46 | * This file contains the marshalling and demarshalling glue for the atomic UAPI | ||
47 | * in all it's form: The monster ATOMIC IOCTL itself, code for GET_PROPERTY and | ||
48 | * SET_PROPERTY IOCTls. Plus interface functions for compatibility helpers and | ||
49 | * drivers which have special needs to construct their own atomic updates, e.g. | ||
50 | * for load detect or similiar. | ||
51 | */ | ||
52 | |||
53 | /** | ||
54 | * drm_atomic_set_mode_for_crtc - set mode for CRTC | ||
55 | * @state: the CRTC whose incoming state to update | ||
56 | * @mode: kernel-internal mode to use for the CRTC, or NULL to disable | ||
57 | * | ||
58 | * Set a mode (originating from the kernel) on the desired CRTC state and update | ||
59 | * the enable property. | ||
60 | * | ||
61 | * RETURNS: | ||
62 | * Zero on success, error code on failure. Cannot return -EDEADLK. | ||
63 | */ | ||
64 | int drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state, | ||
65 | const struct drm_display_mode *mode) | ||
66 | { | ||
67 | struct drm_crtc *crtc = state->crtc; | ||
68 | struct drm_mode_modeinfo umode; | ||
69 | |||
70 | /* Early return for no change. */ | ||
71 | if (mode && memcmp(&state->mode, mode, sizeof(*mode)) == 0) | ||
72 | return 0; | ||
73 | |||
74 | drm_property_blob_put(state->mode_blob); | ||
75 | state->mode_blob = NULL; | ||
76 | |||
77 | if (mode) { | ||
78 | drm_mode_convert_to_umode(&umode, mode); | ||
79 | state->mode_blob = | ||
80 | drm_property_create_blob(state->crtc->dev, | ||
81 | sizeof(umode), | ||
82 | &umode); | ||
83 | if (IS_ERR(state->mode_blob)) | ||
84 | return PTR_ERR(state->mode_blob); | ||
85 | |||
86 | drm_mode_copy(&state->mode, mode); | ||
87 | state->enable = true; | ||
88 | DRM_DEBUG_ATOMIC("Set [MODE:%s] for [CRTC:%d:%s] state %p\n", | ||
89 | mode->name, crtc->base.id, crtc->name, state); | ||
90 | } else { | ||
91 | memset(&state->mode, 0, sizeof(state->mode)); | ||
92 | state->enable = false; | ||
93 | DRM_DEBUG_ATOMIC("Set [NOMODE] for [CRTC:%d:%s] state %p\n", | ||
94 | crtc->base.id, crtc->name, state); | ||
95 | } | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | EXPORT_SYMBOL(drm_atomic_set_mode_for_crtc); | ||
100 | |||
101 | /** | ||
102 | * drm_atomic_set_mode_prop_for_crtc - set mode for CRTC | ||
103 | * @state: the CRTC whose incoming state to update | ||
104 | * @blob: pointer to blob property to use for mode | ||
105 | * | ||
106 | * Set a mode (originating from a blob property) on the desired CRTC state. | ||
107 | * This function will take a reference on the blob property for the CRTC state, | ||
108 | * and release the reference held on the state's existing mode property, if any | ||
109 | * was set. | ||
110 | * | ||
111 | * RETURNS: | ||
112 | * Zero on success, error code on failure. Cannot return -EDEADLK. | ||
113 | */ | ||
114 | int drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state, | ||
115 | struct drm_property_blob *blob) | ||
116 | { | ||
117 | struct drm_crtc *crtc = state->crtc; | ||
118 | |||
119 | if (blob == state->mode_blob) | ||
120 | return 0; | ||
121 | |||
122 | drm_property_blob_put(state->mode_blob); | ||
123 | state->mode_blob = NULL; | ||
124 | |||
125 | memset(&state->mode, 0, sizeof(state->mode)); | ||
126 | |||
127 | if (blob) { | ||
128 | int ret; | ||
129 | |||
130 | if (blob->length != sizeof(struct drm_mode_modeinfo)) { | ||
131 | DRM_DEBUG_ATOMIC("[CRTC:%d:%s] bad mode blob length: %zu\n", | ||
132 | crtc->base.id, crtc->name, | ||
133 | blob->length); | ||
134 | return -EINVAL; | ||
135 | } | ||
136 | |||
137 | ret = drm_mode_convert_umode(crtc->dev, | ||
138 | &state->mode, blob->data); | ||
139 | if (ret) { | ||
140 | DRM_DEBUG_ATOMIC("[CRTC:%d:%s] invalid mode (ret=%d, status=%s):\n", | ||
141 | crtc->base.id, crtc->name, | ||
142 | ret, drm_get_mode_status_name(state->mode.status)); | ||
143 | drm_mode_debug_printmodeline(&state->mode); | ||
144 | return -EINVAL; | ||
145 | } | ||
146 | |||
147 | state->mode_blob = drm_property_blob_get(blob); | ||
148 | state->enable = true; | ||
149 | DRM_DEBUG_ATOMIC("Set [MODE:%s] for [CRTC:%d:%s] state %p\n", | ||
150 | state->mode.name, crtc->base.id, crtc->name, | ||
151 | state); | ||
152 | } else { | ||
153 | state->enable = false; | ||
154 | DRM_DEBUG_ATOMIC("Set [NOMODE] for [CRTC:%d:%s] state %p\n", | ||
155 | crtc->base.id, crtc->name, state); | ||
156 | } | ||
157 | |||
158 | return 0; | ||
159 | } | ||
160 | EXPORT_SYMBOL(drm_atomic_set_mode_prop_for_crtc); | ||
161 | |||
162 | /** | ||
163 | * drm_atomic_set_crtc_for_plane - set crtc for plane | ||
164 | * @plane_state: the plane whose incoming state to update | ||
165 | * @crtc: crtc to use for the plane | ||
166 | * | ||
167 | * Changing the assigned crtc for a plane requires us to grab the lock and state | ||
168 | * for the new crtc, as needed. This function takes care of all these details | ||
169 | * besides updating the pointer in the state object itself. | ||
170 | * | ||
171 | * Returns: | ||
172 | * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK | ||
173 | * then the w/w mutex code has detected a deadlock and the entire atomic | ||
174 | * sequence must be restarted. All other errors are fatal. | ||
175 | */ | ||
176 | int | ||
177 | drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, | ||
178 | struct drm_crtc *crtc) | ||
179 | { | ||
180 | struct drm_plane *plane = plane_state->plane; | ||
181 | struct drm_crtc_state *crtc_state; | ||
182 | /* Nothing to do for same crtc*/ | ||
183 | if (plane_state->crtc == crtc) | ||
184 | return 0; | ||
185 | if (plane_state->crtc) { | ||
186 | crtc_state = drm_atomic_get_crtc_state(plane_state->state, | ||
187 | plane_state->crtc); | ||
188 | if (WARN_ON(IS_ERR(crtc_state))) | ||
189 | return PTR_ERR(crtc_state); | ||
190 | |||
191 | crtc_state->plane_mask &= ~drm_plane_mask(plane); | ||
192 | } | ||
193 | |||
194 | plane_state->crtc = crtc; | ||
195 | |||
196 | if (crtc) { | ||
197 | crtc_state = drm_atomic_get_crtc_state(plane_state->state, | ||
198 | crtc); | ||
199 | if (IS_ERR(crtc_state)) | ||
200 | return PTR_ERR(crtc_state); | ||
201 | crtc_state->plane_mask |= drm_plane_mask(plane); | ||
202 | } | ||
203 | |||
204 | if (crtc) | ||
205 | DRM_DEBUG_ATOMIC("Link [PLANE:%d:%s] state %p to [CRTC:%d:%s]\n", | ||
206 | plane->base.id, plane->name, plane_state, | ||
207 | crtc->base.id, crtc->name); | ||
208 | else | ||
209 | DRM_DEBUG_ATOMIC("Link [PLANE:%d:%s] state %p to [NOCRTC]\n", | ||
210 | plane->base.id, plane->name, plane_state); | ||
211 | |||
212 | return 0; | ||
213 | } | ||
214 | EXPORT_SYMBOL(drm_atomic_set_crtc_for_plane); | ||
215 | |||
216 | /** | ||
217 | * drm_atomic_set_fb_for_plane - set framebuffer for plane | ||
218 | * @plane_state: atomic state object for the plane | ||
219 | * @fb: fb to use for the plane | ||
220 | * | ||
221 | * Changing the assigned framebuffer for a plane requires us to grab a reference | ||
222 | * to the new fb and drop the reference to the old fb, if there is one. This | ||
223 | * function takes care of all these details besides updating the pointer in the | ||
224 | * state object itself. | ||
225 | */ | ||
226 | void | ||
227 | drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state, | ||
228 | struct drm_framebuffer *fb) | ||
229 | { | ||
230 | struct drm_plane *plane = plane_state->plane; | ||
231 | |||
232 | if (fb) | ||
233 | DRM_DEBUG_ATOMIC("Set [FB:%d] for [PLANE:%d:%s] state %p\n", | ||
234 | fb->base.id, plane->base.id, plane->name, | ||
235 | plane_state); | ||
236 | else | ||
237 | DRM_DEBUG_ATOMIC("Set [NOFB] for [PLANE:%d:%s] state %p\n", | ||
238 | plane->base.id, plane->name, plane_state); | ||
239 | |||
240 | drm_framebuffer_assign(&plane_state->fb, fb); | ||
241 | } | ||
242 | EXPORT_SYMBOL(drm_atomic_set_fb_for_plane); | ||
243 | |||
244 | /** | ||
245 | * drm_atomic_set_fence_for_plane - set fence for plane | ||
246 | * @plane_state: atomic state object for the plane | ||
247 | * @fence: dma_fence to use for the plane | ||
248 | * | ||
249 | * Helper to setup the plane_state fence in case it is not set yet. | ||
250 | * By using this drivers doesn't need to worry if the user choose | ||
251 | * implicit or explicit fencing. | ||
252 | * | ||
253 | * This function will not set the fence to the state if it was set | ||
254 | * via explicit fencing interfaces on the atomic ioctl. In that case it will | ||
255 | * drop the reference to the fence as we are not storing it anywhere. | ||
256 | * Otherwise, if &drm_plane_state.fence is not set this function we just set it | ||
257 | * with the received implicit fence. In both cases this function consumes a | ||
258 | * reference for @fence. | ||
259 | * | ||
260 | * This way explicit fencing can be used to overrule implicit fencing, which is | ||
261 | * important to make explicit fencing use-cases work: One example is using one | ||
262 | * buffer for 2 screens with different refresh rates. Implicit fencing will | ||
263 | * clamp rendering to the refresh rate of the slower screen, whereas explicit | ||
264 | * fence allows 2 independent render and display loops on a single buffer. If a | ||
265 | * driver allows obeys both implicit and explicit fences for plane updates, then | ||
266 | * it will break all the benefits of explicit fencing. | ||
267 | */ | ||
268 | void | ||
269 | drm_atomic_set_fence_for_plane(struct drm_plane_state *plane_state, | ||
270 | struct dma_fence *fence) | ||
271 | { | ||
272 | if (plane_state->fence) { | ||
273 | dma_fence_put(fence); | ||
274 | return; | ||
275 | } | ||
276 | |||
277 | plane_state->fence = fence; | ||
278 | } | ||
279 | EXPORT_SYMBOL(drm_atomic_set_fence_for_plane); | ||
280 | |||
281 | /** | ||
282 | * drm_atomic_set_crtc_for_connector - set crtc for connector | ||
283 | * @conn_state: atomic state object for the connector | ||
284 | * @crtc: crtc to use for the connector | ||
285 | * | ||
286 | * Changing the assigned crtc for a connector requires us to grab the lock and | ||
287 | * state for the new crtc, as needed. This function takes care of all these | ||
288 | * details besides updating the pointer in the state object itself. | ||
289 | * | ||
290 | * Returns: | ||
291 | * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK | ||
292 | * then the w/w mutex code has detected a deadlock and the entire atomic | ||
293 | * sequence must be restarted. All other errors are fatal. | ||
294 | */ | ||
295 | int | ||
296 | drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state, | ||
297 | struct drm_crtc *crtc) | ||
298 | { | ||
299 | struct drm_connector *connector = conn_state->connector; | ||
300 | struct drm_crtc_state *crtc_state; | ||
301 | |||
302 | if (conn_state->crtc == crtc) | ||
303 | return 0; | ||
304 | |||
305 | if (conn_state->crtc) { | ||
306 | crtc_state = drm_atomic_get_new_crtc_state(conn_state->state, | ||
307 | conn_state->crtc); | ||
308 | |||
309 | crtc_state->connector_mask &= | ||
310 | ~drm_connector_mask(conn_state->connector); | ||
311 | |||
312 | drm_connector_put(conn_state->connector); | ||
313 | conn_state->crtc = NULL; | ||
314 | } | ||
315 | |||
316 | if (crtc) { | ||
317 | crtc_state = drm_atomic_get_crtc_state(conn_state->state, crtc); | ||
318 | if (IS_ERR(crtc_state)) | ||
319 | return PTR_ERR(crtc_state); | ||
320 | |||
321 | crtc_state->connector_mask |= | ||
322 | drm_connector_mask(conn_state->connector); | ||
323 | |||
324 | drm_connector_get(conn_state->connector); | ||
325 | conn_state->crtc = crtc; | ||
326 | |||
327 | DRM_DEBUG_ATOMIC("Link [CONNECTOR:%d:%s] state %p to [CRTC:%d:%s]\n", | ||
328 | connector->base.id, connector->name, | ||
329 | conn_state, crtc->base.id, crtc->name); | ||
330 | } else { | ||
331 | DRM_DEBUG_ATOMIC("Link [CONNECTOR:%d:%s] state %p to [NOCRTC]\n", | ||
332 | connector->base.id, connector->name, | ||
333 | conn_state); | ||
334 | } | ||
335 | |||
336 | return 0; | ||
337 | } | ||
338 | EXPORT_SYMBOL(drm_atomic_set_crtc_for_connector); | ||
339 | |||
340 | static void set_out_fence_for_crtc(struct drm_atomic_state *state, | ||
341 | struct drm_crtc *crtc, s32 __user *fence_ptr) | ||
342 | { | ||
343 | state->crtcs[drm_crtc_index(crtc)].out_fence_ptr = fence_ptr; | ||
344 | } | ||
345 | |||
346 | static s32 __user *get_out_fence_for_crtc(struct drm_atomic_state *state, | ||
347 | struct drm_crtc *crtc) | ||
348 | { | ||
349 | s32 __user *fence_ptr; | ||
350 | |||
351 | fence_ptr = state->crtcs[drm_crtc_index(crtc)].out_fence_ptr; | ||
352 | state->crtcs[drm_crtc_index(crtc)].out_fence_ptr = NULL; | ||
353 | |||
354 | return fence_ptr; | ||
355 | } | ||
356 | |||
357 | static int set_out_fence_for_connector(struct drm_atomic_state *state, | ||
358 | struct drm_connector *connector, | ||
359 | s32 __user *fence_ptr) | ||
360 | { | ||
361 | unsigned int index = drm_connector_index(connector); | ||
362 | |||
363 | if (!fence_ptr) | ||
364 | return 0; | ||
365 | |||
366 | if (put_user(-1, fence_ptr)) | ||
367 | return -EFAULT; | ||
368 | |||
369 | state->connectors[index].out_fence_ptr = fence_ptr; | ||
370 | |||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | static s32 __user *get_out_fence_for_connector(struct drm_atomic_state *state, | ||
375 | struct drm_connector *connector) | ||
376 | { | ||
377 | unsigned int index = drm_connector_index(connector); | ||
378 | s32 __user *fence_ptr; | ||
379 | |||
380 | fence_ptr = state->connectors[index].out_fence_ptr; | ||
381 | state->connectors[index].out_fence_ptr = NULL; | ||
382 | |||
383 | return fence_ptr; | ||
384 | } | ||
385 | |||
386 | static int | ||
387 | drm_atomic_replace_property_blob_from_id(struct drm_device *dev, | ||
388 | struct drm_property_blob **blob, | ||
389 | uint64_t blob_id, | ||
390 | ssize_t expected_size, | ||
391 | ssize_t expected_elem_size, | ||
392 | bool *replaced) | ||
393 | { | ||
394 | struct drm_property_blob *new_blob = NULL; | ||
395 | |||
396 | if (blob_id != 0) { | ||
397 | new_blob = drm_property_lookup_blob(dev, blob_id); | ||
398 | if (new_blob == NULL) | ||
399 | return -EINVAL; | ||
400 | |||
401 | if (expected_size > 0 && | ||
402 | new_blob->length != expected_size) { | ||
403 | drm_property_blob_put(new_blob); | ||
404 | return -EINVAL; | ||
405 | } | ||
406 | if (expected_elem_size > 0 && | ||
407 | new_blob->length % expected_elem_size != 0) { | ||
408 | drm_property_blob_put(new_blob); | ||
409 | return -EINVAL; | ||
410 | } | ||
411 | } | ||
412 | |||
413 | *replaced |= drm_property_replace_blob(blob, new_blob); | ||
414 | drm_property_blob_put(new_blob); | ||
415 | |||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | static int drm_atomic_crtc_set_property(struct drm_crtc *crtc, | ||
420 | struct drm_crtc_state *state, struct drm_property *property, | ||
421 | uint64_t val) | ||
422 | { | ||
423 | struct drm_device *dev = crtc->dev; | ||
424 | struct drm_mode_config *config = &dev->mode_config; | ||
425 | bool replaced = false; | ||
426 | int ret; | ||
427 | |||
428 | if (property == config->prop_active) | ||
429 | state->active = val; | ||
430 | else if (property == config->prop_mode_id) { | ||
431 | struct drm_property_blob *mode = | ||
432 | drm_property_lookup_blob(dev, val); | ||
433 | ret = drm_atomic_set_mode_prop_for_crtc(state, mode); | ||
434 | drm_property_blob_put(mode); | ||
435 | return ret; | ||
436 | } else if (property == config->degamma_lut_property) { | ||
437 | ret = drm_atomic_replace_property_blob_from_id(dev, | ||
438 | &state->degamma_lut, | ||
439 | val, | ||
440 | -1, sizeof(struct drm_color_lut), | ||
441 | &replaced); | ||
442 | state->color_mgmt_changed |= replaced; | ||
443 | return ret; | ||
444 | } else if (property == config->ctm_property) { | ||
445 | ret = drm_atomic_replace_property_blob_from_id(dev, | ||
446 | &state->ctm, | ||
447 | val, | ||
448 | sizeof(struct drm_color_ctm), -1, | ||
449 | &replaced); | ||
450 | state->color_mgmt_changed |= replaced; | ||
451 | return ret; | ||
452 | } else if (property == config->gamma_lut_property) { | ||
453 | ret = drm_atomic_replace_property_blob_from_id(dev, | ||
454 | &state->gamma_lut, | ||
455 | val, | ||
456 | -1, sizeof(struct drm_color_lut), | ||
457 | &replaced); | ||
458 | state->color_mgmt_changed |= replaced; | ||
459 | return ret; | ||
460 | } else if (property == config->prop_out_fence_ptr) { | ||
461 | s32 __user *fence_ptr = u64_to_user_ptr(val); | ||
462 | |||
463 | if (!fence_ptr) | ||
464 | return 0; | ||
465 | |||
466 | if (put_user(-1, fence_ptr)) | ||
467 | return -EFAULT; | ||
468 | |||
469 | set_out_fence_for_crtc(state->state, crtc, fence_ptr); | ||
470 | } else if (crtc->funcs->atomic_set_property) { | ||
471 | return crtc->funcs->atomic_set_property(crtc, state, property, val); | ||
472 | } else { | ||
473 | DRM_DEBUG_ATOMIC("[CRTC:%d:%s] unknown property [PROP:%d:%s]]\n", | ||
474 | crtc->base.id, crtc->name, | ||
475 | property->base.id, property->name); | ||
476 | return -EINVAL; | ||
477 | } | ||
478 | |||
479 | return 0; | ||
480 | } | ||
481 | |||
482 | static int | ||
483 | drm_atomic_crtc_get_property(struct drm_crtc *crtc, | ||
484 | const struct drm_crtc_state *state, | ||
485 | struct drm_property *property, uint64_t *val) | ||
486 | { | ||
487 | struct drm_device *dev = crtc->dev; | ||
488 | struct drm_mode_config *config = &dev->mode_config; | ||
489 | |||
490 | if (property == config->prop_active) | ||
491 | *val = state->active; | ||
492 | else if (property == config->prop_mode_id) | ||
493 | *val = (state->mode_blob) ? state->mode_blob->base.id : 0; | ||
494 | else if (property == config->degamma_lut_property) | ||
495 | *val = (state->degamma_lut) ? state->degamma_lut->base.id : 0; | ||
496 | else if (property == config->ctm_property) | ||
497 | *val = (state->ctm) ? state->ctm->base.id : 0; | ||
498 | else if (property == config->gamma_lut_property) | ||
499 | *val = (state->gamma_lut) ? state->gamma_lut->base.id : 0; | ||
500 | else if (property == config->prop_out_fence_ptr) | ||
501 | *val = 0; | ||
502 | else if (crtc->funcs->atomic_get_property) | ||
503 | return crtc->funcs->atomic_get_property(crtc, state, property, val); | ||
504 | else | ||
505 | return -EINVAL; | ||
506 | |||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | static int drm_atomic_plane_set_property(struct drm_plane *plane, | ||
511 | struct drm_plane_state *state, struct drm_property *property, | ||
512 | uint64_t val) | ||
513 | { | ||
514 | struct drm_device *dev = plane->dev; | ||
515 | struct drm_mode_config *config = &dev->mode_config; | ||
516 | |||
517 | if (property == config->prop_fb_id) { | ||
518 | struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, NULL, val); | ||
519 | drm_atomic_set_fb_for_plane(state, fb); | ||
520 | if (fb) | ||
521 | drm_framebuffer_put(fb); | ||
522 | } else if (property == config->prop_in_fence_fd) { | ||
523 | if (state->fence) | ||
524 | return -EINVAL; | ||
525 | |||
526 | if (U642I64(val) == -1) | ||
527 | return 0; | ||
528 | |||
529 | state->fence = sync_file_get_fence(val); | ||
530 | if (!state->fence) | ||
531 | return -EINVAL; | ||
532 | |||
533 | } else if (property == config->prop_crtc_id) { | ||
534 | struct drm_crtc *crtc = drm_crtc_find(dev, NULL, val); | ||
535 | return drm_atomic_set_crtc_for_plane(state, crtc); | ||
536 | } else if (property == config->prop_crtc_x) { | ||
537 | state->crtc_x = U642I64(val); | ||
538 | } else if (property == config->prop_crtc_y) { | ||
539 | state->crtc_y = U642I64(val); | ||
540 | } else if (property == config->prop_crtc_w) { | ||
541 | state->crtc_w = val; | ||
542 | } else if (property == config->prop_crtc_h) { | ||
543 | state->crtc_h = val; | ||
544 | } else if (property == config->prop_src_x) { | ||
545 | state->src_x = val; | ||
546 | } else if (property == config->prop_src_y) { | ||
547 | state->src_y = val; | ||
548 | } else if (property == config->prop_src_w) { | ||
549 | state->src_w = val; | ||
550 | } else if (property == config->prop_src_h) { | ||
551 | state->src_h = val; | ||
552 | } else if (property == plane->alpha_property) { | ||
553 | state->alpha = val; | ||
554 | } else if (property == plane->blend_mode_property) { | ||
555 | state->pixel_blend_mode = val; | ||
556 | } else if (property == plane->rotation_property) { | ||
557 | if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK)) { | ||
558 | DRM_DEBUG_ATOMIC("[PLANE:%d:%s] bad rotation bitmask: 0x%llx\n", | ||
559 | plane->base.id, plane->name, val); | ||
560 | return -EINVAL; | ||
561 | } | ||
562 | state->rotation = val; | ||
563 | } else if (property == plane->zpos_property) { | ||
564 | state->zpos = val; | ||
565 | } else if (property == plane->color_encoding_property) { | ||
566 | state->color_encoding = val; | ||
567 | } else if (property == plane->color_range_property) { | ||
568 | state->color_range = val; | ||
569 | } else if (plane->funcs->atomic_set_property) { | ||
570 | return plane->funcs->atomic_set_property(plane, state, | ||
571 | property, val); | ||
572 | } else { | ||
573 | DRM_DEBUG_ATOMIC("[PLANE:%d:%s] unknown property [PROP:%d:%s]]\n", | ||
574 | plane->base.id, plane->name, | ||
575 | property->base.id, property->name); | ||
576 | return -EINVAL; | ||
577 | } | ||
578 | |||
579 | return 0; | ||
580 | } | ||
581 | |||
582 | static int | ||
583 | drm_atomic_plane_get_property(struct drm_plane *plane, | ||
584 | const struct drm_plane_state *state, | ||
585 | struct drm_property *property, uint64_t *val) | ||
586 | { | ||
587 | struct drm_device *dev = plane->dev; | ||
588 | struct drm_mode_config *config = &dev->mode_config; | ||
589 | |||
590 | if (property == config->prop_fb_id) { | ||
591 | *val = (state->fb) ? state->fb->base.id : 0; | ||
592 | } else if (property == config->prop_in_fence_fd) { | ||
593 | *val = -1; | ||
594 | } else if (property == config->prop_crtc_id) { | ||
595 | *val = (state->crtc) ? state->crtc->base.id : 0; | ||
596 | } else if (property == config->prop_crtc_x) { | ||
597 | *val = I642U64(state->crtc_x); | ||
598 | } else if (property == config->prop_crtc_y) { | ||
599 | *val = I642U64(state->crtc_y); | ||
600 | } else if (property == config->prop_crtc_w) { | ||
601 | *val = state->crtc_w; | ||
602 | } else if (property == config->prop_crtc_h) { | ||
603 | *val = state->crtc_h; | ||
604 | } else if (property == config->prop_src_x) { | ||
605 | *val = state->src_x; | ||
606 | } else if (property == config->prop_src_y) { | ||
607 | *val = state->src_y; | ||
608 | } else if (property == config->prop_src_w) { | ||
609 | *val = state->src_w; | ||
610 | } else if (property == config->prop_src_h) { | ||
611 | *val = state->src_h; | ||
612 | } else if (property == plane->alpha_property) { | ||
613 | *val = state->alpha; | ||
614 | } else if (property == plane->blend_mode_property) { | ||
615 | *val = state->pixel_blend_mode; | ||
616 | } else if (property == plane->rotation_property) { | ||
617 | *val = state->rotation; | ||
618 | } else if (property == plane->zpos_property) { | ||
619 | *val = state->zpos; | ||
620 | } else if (property == plane->color_encoding_property) { | ||
621 | *val = state->color_encoding; | ||
622 | } else if (property == plane->color_range_property) { | ||
623 | *val = state->color_range; | ||
624 | } else if (plane->funcs->atomic_get_property) { | ||
625 | return plane->funcs->atomic_get_property(plane, state, property, val); | ||
626 | } else { | ||
627 | return -EINVAL; | ||
628 | } | ||
629 | |||
630 | return 0; | ||
631 | } | ||
632 | |||
633 | static struct drm_writeback_job * | ||
634 | drm_atomic_get_writeback_job(struct drm_connector_state *conn_state) | ||
635 | { | ||
636 | WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK); | ||
637 | |||
638 | if (!conn_state->writeback_job) | ||
639 | conn_state->writeback_job = | ||
640 | kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL); | ||
641 | |||
642 | return conn_state->writeback_job; | ||
643 | } | ||
644 | |||
645 | static int drm_atomic_set_writeback_fb_for_connector( | ||
646 | struct drm_connector_state *conn_state, | ||
647 | struct drm_framebuffer *fb) | ||
648 | { | ||
649 | struct drm_writeback_job *job = | ||
650 | drm_atomic_get_writeback_job(conn_state); | ||
651 | if (!job) | ||
652 | return -ENOMEM; | ||
653 | |||
654 | drm_framebuffer_assign(&job->fb, fb); | ||
655 | |||
656 | if (fb) | ||
657 | DRM_DEBUG_ATOMIC("Set [FB:%d] for connector state %p\n", | ||
658 | fb->base.id, conn_state); | ||
659 | else | ||
660 | DRM_DEBUG_ATOMIC("Set [NOFB] for connector state %p\n", | ||
661 | conn_state); | ||
662 | |||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | static int drm_atomic_connector_set_property(struct drm_connector *connector, | ||
667 | struct drm_connector_state *state, struct drm_property *property, | ||
668 | uint64_t val) | ||
669 | { | ||
670 | struct drm_device *dev = connector->dev; | ||
671 | struct drm_mode_config *config = &dev->mode_config; | ||
672 | |||
673 | if (property == config->prop_crtc_id) { | ||
674 | struct drm_crtc *crtc = drm_crtc_find(dev, NULL, val); | ||
675 | return drm_atomic_set_crtc_for_connector(state, crtc); | ||
676 | } else if (property == config->dpms_property) { | ||
677 | /* setting DPMS property requires special handling, which | ||
678 | * is done in legacy setprop path for us. Disallow (for | ||
679 | * now?) atomic writes to DPMS property: | ||
680 | */ | ||
681 | return -EINVAL; | ||
682 | } else if (property == config->tv_select_subconnector_property) { | ||
683 | state->tv.subconnector = val; | ||
684 | } else if (property == config->tv_left_margin_property) { | ||
685 | state->tv.margins.left = val; | ||
686 | } else if (property == config->tv_right_margin_property) { | ||
687 | state->tv.margins.right = val; | ||
688 | } else if (property == config->tv_top_margin_property) { | ||
689 | state->tv.margins.top = val; | ||
690 | } else if (property == config->tv_bottom_margin_property) { | ||
691 | state->tv.margins.bottom = val; | ||
692 | } else if (property == config->tv_mode_property) { | ||
693 | state->tv.mode = val; | ||
694 | } else if (property == config->tv_brightness_property) { | ||
695 | state->tv.brightness = val; | ||
696 | } else if (property == config->tv_contrast_property) { | ||
697 | state->tv.contrast = val; | ||
698 | } else if (property == config->tv_flicker_reduction_property) { | ||
699 | state->tv.flicker_reduction = val; | ||
700 | } else if (property == config->tv_overscan_property) { | ||
701 | state->tv.overscan = val; | ||
702 | } else if (property == config->tv_saturation_property) { | ||
703 | state->tv.saturation = val; | ||
704 | } else if (property == config->tv_hue_property) { | ||
705 | state->tv.hue = val; | ||
706 | } else if (property == config->link_status_property) { | ||
707 | /* Never downgrade from GOOD to BAD on userspace's request here, | ||
708 | * only hw issues can do that. | ||
709 | * | ||
710 | * For an atomic property the userspace doesn't need to be able | ||
711 | * to understand all the properties, but needs to be able to | ||
712 | * restore the state it wants on VT switch. So if the userspace | ||
713 | * tries to change the link_status from GOOD to BAD, driver | ||
714 | * silently rejects it and returns a 0. This prevents userspace | ||
715 | * from accidently breaking the display when it restores the | ||
716 | * state. | ||
717 | */ | ||
718 | if (state->link_status != DRM_LINK_STATUS_GOOD) | ||
719 | state->link_status = val; | ||
720 | } else if (property == config->aspect_ratio_property) { | ||
721 | state->picture_aspect_ratio = val; | ||
722 | } else if (property == config->content_type_property) { | ||
723 | state->content_type = val; | ||
724 | } else if (property == connector->scaling_mode_property) { | ||
725 | state->scaling_mode = val; | ||
726 | } else if (property == connector->content_protection_property) { | ||
727 | if (val == DRM_MODE_CONTENT_PROTECTION_ENABLED) { | ||
728 | DRM_DEBUG_KMS("only drivers can set CP Enabled\n"); | ||
729 | return -EINVAL; | ||
730 | } | ||
731 | state->content_protection = val; | ||
732 | } else if (property == config->writeback_fb_id_property) { | ||
733 | struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, NULL, val); | ||
734 | int ret = drm_atomic_set_writeback_fb_for_connector(state, fb); | ||
735 | if (fb) | ||
736 | drm_framebuffer_put(fb); | ||
737 | return ret; | ||
738 | } else if (property == config->writeback_out_fence_ptr_property) { | ||
739 | s32 __user *fence_ptr = u64_to_user_ptr(val); | ||
740 | |||
741 | return set_out_fence_for_connector(state->state, connector, | ||
742 | fence_ptr); | ||
743 | } else if (connector->funcs->atomic_set_property) { | ||
744 | return connector->funcs->atomic_set_property(connector, | ||
745 | state, property, val); | ||
746 | } else { | ||
747 | DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] unknown property [PROP:%d:%s]]\n", | ||
748 | connector->base.id, connector->name, | ||
749 | property->base.id, property->name); | ||
750 | return -EINVAL; | ||
751 | } | ||
752 | |||
753 | return 0; | ||
754 | } | ||
755 | |||
756 | static int | ||
757 | drm_atomic_connector_get_property(struct drm_connector *connector, | ||
758 | const struct drm_connector_state *state, | ||
759 | struct drm_property *property, uint64_t *val) | ||
760 | { | ||
761 | struct drm_device *dev = connector->dev; | ||
762 | struct drm_mode_config *config = &dev->mode_config; | ||
763 | |||
764 | if (property == config->prop_crtc_id) { | ||
765 | *val = (state->crtc) ? state->crtc->base.id : 0; | ||
766 | } else if (property == config->dpms_property) { | ||
767 | *val = connector->dpms; | ||
768 | } else if (property == config->tv_select_subconnector_property) { | ||
769 | *val = state->tv.subconnector; | ||
770 | } else if (property == config->tv_left_margin_property) { | ||
771 | *val = state->tv.margins.left; | ||
772 | } else if (property == config->tv_right_margin_property) { | ||
773 | *val = state->tv.margins.right; | ||
774 | } else if (property == config->tv_top_margin_property) { | ||
775 | *val = state->tv.margins.top; | ||
776 | } else if (property == config->tv_bottom_margin_property) { | ||
777 | *val = state->tv.margins.bottom; | ||
778 | } else if (property == config->tv_mode_property) { | ||
779 | *val = state->tv.mode; | ||
780 | } else if (property == config->tv_brightness_property) { | ||
781 | *val = state->tv.brightness; | ||
782 | } else if (property == config->tv_contrast_property) { | ||
783 | *val = state->tv.contrast; | ||
784 | } else if (property == config->tv_flicker_reduction_property) { | ||
785 | *val = state->tv.flicker_reduction; | ||
786 | } else if (property == config->tv_overscan_property) { | ||
787 | *val = state->tv.overscan; | ||
788 | } else if (property == config->tv_saturation_property) { | ||
789 | *val = state->tv.saturation; | ||
790 | } else if (property == config->tv_hue_property) { | ||
791 | *val = state->tv.hue; | ||
792 | } else if (property == config->link_status_property) { | ||
793 | *val = state->link_status; | ||
794 | } else if (property == config->aspect_ratio_property) { | ||
795 | *val = state->picture_aspect_ratio; | ||
796 | } else if (property == config->content_type_property) { | ||
797 | *val = state->content_type; | ||
798 | } else if (property == connector->scaling_mode_property) { | ||
799 | *val = state->scaling_mode; | ||
800 | } else if (property == connector->content_protection_property) { | ||
801 | *val = state->content_protection; | ||
802 | } else if (property == config->writeback_fb_id_property) { | ||
803 | /* Writeback framebuffer is one-shot, write and forget */ | ||
804 | *val = 0; | ||
805 | } else if (property == config->writeback_out_fence_ptr_property) { | ||
806 | *val = 0; | ||
807 | } else if (connector->funcs->atomic_get_property) { | ||
808 | return connector->funcs->atomic_get_property(connector, | ||
809 | state, property, val); | ||
810 | } else { | ||
811 | return -EINVAL; | ||
812 | } | ||
813 | |||
814 | return 0; | ||
815 | } | ||
816 | |||
817 | int drm_atomic_get_property(struct drm_mode_object *obj, | ||
818 | struct drm_property *property, uint64_t *val) | ||
819 | { | ||
820 | struct drm_device *dev = property->dev; | ||
821 | int ret; | ||
822 | |||
823 | switch (obj->type) { | ||
824 | case DRM_MODE_OBJECT_CONNECTOR: { | ||
825 | struct drm_connector *connector = obj_to_connector(obj); | ||
826 | WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); | ||
827 | ret = drm_atomic_connector_get_property(connector, | ||
828 | connector->state, property, val); | ||
829 | break; | ||
830 | } | ||
831 | case DRM_MODE_OBJECT_CRTC: { | ||
832 | struct drm_crtc *crtc = obj_to_crtc(obj); | ||
833 | WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); | ||
834 | ret = drm_atomic_crtc_get_property(crtc, | ||
835 | crtc->state, property, val); | ||
836 | break; | ||
837 | } | ||
838 | case DRM_MODE_OBJECT_PLANE: { | ||
839 | struct drm_plane *plane = obj_to_plane(obj); | ||
840 | WARN_ON(!drm_modeset_is_locked(&plane->mutex)); | ||
841 | ret = drm_atomic_plane_get_property(plane, | ||
842 | plane->state, property, val); | ||
843 | break; | ||
844 | } | ||
845 | default: | ||
846 | ret = -EINVAL; | ||
847 | break; | ||
848 | } | ||
849 | |||
850 | return ret; | ||
851 | } | ||
852 | |||
853 | /* | ||
854 | * The big monster ioctl | ||
855 | */ | ||
856 | |||
857 | static struct drm_pending_vblank_event *create_vblank_event( | ||
858 | struct drm_crtc *crtc, uint64_t user_data) | ||
859 | { | ||
860 | struct drm_pending_vblank_event *e = NULL; | ||
861 | |||
862 | e = kzalloc(sizeof *e, GFP_KERNEL); | ||
863 | if (!e) | ||
864 | return NULL; | ||
865 | |||
866 | e->event.base.type = DRM_EVENT_FLIP_COMPLETE; | ||
867 | e->event.base.length = sizeof(e->event); | ||
868 | e->event.vbl.crtc_id = crtc->base.id; | ||
869 | e->event.vbl.user_data = user_data; | ||
870 | |||
871 | return e; | ||
872 | } | ||
873 | |||
874 | int drm_atomic_connector_commit_dpms(struct drm_atomic_state *state, | ||
875 | struct drm_connector *connector, | ||
876 | int mode) | ||
877 | { | ||
878 | struct drm_connector *tmp_connector; | ||
879 | struct drm_connector_state *new_conn_state; | ||
880 | struct drm_crtc *crtc; | ||
881 | struct drm_crtc_state *crtc_state; | ||
882 | int i, ret, old_mode = connector->dpms; | ||
883 | bool active = false; | ||
884 | |||
885 | ret = drm_modeset_lock(&state->dev->mode_config.connection_mutex, | ||
886 | state->acquire_ctx); | ||
887 | if (ret) | ||
888 | return ret; | ||
889 | |||
890 | if (mode != DRM_MODE_DPMS_ON) | ||
891 | mode = DRM_MODE_DPMS_OFF; | ||
892 | connector->dpms = mode; | ||
893 | |||
894 | crtc = connector->state->crtc; | ||
895 | if (!crtc) | ||
896 | goto out; | ||
897 | ret = drm_atomic_add_affected_connectors(state, crtc); | ||
898 | if (ret) | ||
899 | goto out; | ||
900 | |||
901 | crtc_state = drm_atomic_get_crtc_state(state, crtc); | ||
902 | if (IS_ERR(crtc_state)) { | ||
903 | ret = PTR_ERR(crtc_state); | ||
904 | goto out; | ||
905 | } | ||
906 | |||
907 | for_each_new_connector_in_state(state, tmp_connector, new_conn_state, i) { | ||
908 | if (new_conn_state->crtc != crtc) | ||
909 | continue; | ||
910 | if (tmp_connector->dpms == DRM_MODE_DPMS_ON) { | ||
911 | active = true; | ||
912 | break; | ||
913 | } | ||
914 | } | ||
915 | |||
916 | crtc_state->active = active; | ||
917 | ret = drm_atomic_commit(state); | ||
918 | out: | ||
919 | if (ret != 0) | ||
920 | connector->dpms = old_mode; | ||
921 | return ret; | ||
922 | } | ||
923 | |||
924 | int drm_atomic_set_property(struct drm_atomic_state *state, | ||
925 | struct drm_mode_object *obj, | ||
926 | struct drm_property *prop, | ||
927 | uint64_t prop_value) | ||
928 | { | ||
929 | struct drm_mode_object *ref; | ||
930 | int ret; | ||
931 | |||
932 | if (!drm_property_change_valid_get(prop, prop_value, &ref)) | ||
933 | return -EINVAL; | ||
934 | |||
935 | switch (obj->type) { | ||
936 | case DRM_MODE_OBJECT_CONNECTOR: { | ||
937 | struct drm_connector *connector = obj_to_connector(obj); | ||
938 | struct drm_connector_state *connector_state; | ||
939 | |||
940 | connector_state = drm_atomic_get_connector_state(state, connector); | ||
941 | if (IS_ERR(connector_state)) { | ||
942 | ret = PTR_ERR(connector_state); | ||
943 | break; | ||
944 | } | ||
945 | |||
946 | ret = drm_atomic_connector_set_property(connector, | ||
947 | connector_state, prop, prop_value); | ||
948 | break; | ||
949 | } | ||
950 | case DRM_MODE_OBJECT_CRTC: { | ||
951 | struct drm_crtc *crtc = obj_to_crtc(obj); | ||
952 | struct drm_crtc_state *crtc_state; | ||
953 | |||
954 | crtc_state = drm_atomic_get_crtc_state(state, crtc); | ||
955 | if (IS_ERR(crtc_state)) { | ||
956 | ret = PTR_ERR(crtc_state); | ||
957 | break; | ||
958 | } | ||
959 | |||
960 | ret = drm_atomic_crtc_set_property(crtc, | ||
961 | crtc_state, prop, prop_value); | ||
962 | break; | ||
963 | } | ||
964 | case DRM_MODE_OBJECT_PLANE: { | ||
965 | struct drm_plane *plane = obj_to_plane(obj); | ||
966 | struct drm_plane_state *plane_state; | ||
967 | |||
968 | plane_state = drm_atomic_get_plane_state(state, plane); | ||
969 | if (IS_ERR(plane_state)) { | ||
970 | ret = PTR_ERR(plane_state); | ||
971 | break; | ||
972 | } | ||
973 | |||
974 | ret = drm_atomic_plane_set_property(plane, | ||
975 | plane_state, prop, prop_value); | ||
976 | break; | ||
977 | } | ||
978 | default: | ||
979 | ret = -EINVAL; | ||
980 | break; | ||
981 | } | ||
982 | |||
983 | drm_property_change_valid_put(prop, ref); | ||
984 | return ret; | ||
985 | } | ||
986 | |||
987 | /** | ||
988 | * DOC: explicit fencing properties | ||
989 | * | ||
990 | * Explicit fencing allows userspace to control the buffer synchronization | ||
991 | * between devices. A Fence or a group of fences are transfered to/from | ||
992 | * userspace using Sync File fds and there are two DRM properties for that. | ||
993 | * IN_FENCE_FD on each DRM Plane to send fences to the kernel and | ||
994 | * OUT_FENCE_PTR on each DRM CRTC to receive fences from the kernel. | ||
995 | * | ||
996 | * As a contrast, with implicit fencing the kernel keeps track of any | ||
997 | * ongoing rendering, and automatically ensures that the atomic update waits | ||
998 | * for any pending rendering to complete. For shared buffers represented with | ||
999 | * a &struct dma_buf this is tracked in &struct reservation_object. | ||
1000 | * Implicit syncing is how Linux traditionally worked (e.g. DRI2/3 on X.org), | ||
1001 | * whereas explicit fencing is what Android wants. | ||
1002 | * | ||
1003 | * "IN_FENCE_FD”: | ||
1004 | * Use this property to pass a fence that DRM should wait on before | ||
1005 | * proceeding with the Atomic Commit request and show the framebuffer for | ||
1006 | * the plane on the screen. The fence can be either a normal fence or a | ||
1007 | * merged one, the sync_file framework will handle both cases and use a | ||
1008 | * fence_array if a merged fence is received. Passing -1 here means no | ||
1009 | * fences to wait on. | ||
1010 | * | ||
1011 | * If the Atomic Commit request has the DRM_MODE_ATOMIC_TEST_ONLY flag | ||
1012 | * it will only check if the Sync File is a valid one. | ||
1013 | * | ||
1014 | * On the driver side the fence is stored on the @fence parameter of | ||
1015 | * &struct drm_plane_state. Drivers which also support implicit fencing | ||
1016 | * should set the implicit fence using drm_atomic_set_fence_for_plane(), | ||
1017 | * to make sure there's consistent behaviour between drivers in precedence | ||
1018 | * of implicit vs. explicit fencing. | ||
1019 | * | ||
1020 | * "OUT_FENCE_PTR”: | ||
1021 | * Use this property to pass a file descriptor pointer to DRM. Once the | ||
1022 | * Atomic Commit request call returns OUT_FENCE_PTR will be filled with | ||
1023 | * the file descriptor number of a Sync File. This Sync File contains the | ||
1024 | * CRTC fence that will be signaled when all framebuffers present on the | ||
1025 | * Atomic Commit * request for that given CRTC are scanned out on the | ||
1026 | * screen. | ||
1027 | * | ||
1028 | * The Atomic Commit request fails if a invalid pointer is passed. If the | ||
1029 | * Atomic Commit request fails for any other reason the out fence fd | ||
1030 | * returned will be -1. On a Atomic Commit with the | ||
1031 | * DRM_MODE_ATOMIC_TEST_ONLY flag the out fence will also be set to -1. | ||
1032 | * | ||
1033 | * Note that out-fences don't have a special interface to drivers and are | ||
1034 | * internally represented by a &struct drm_pending_vblank_event in struct | ||
1035 | * &drm_crtc_state, which is also used by the nonblocking atomic commit | ||
1036 | * helpers and for the DRM event handling for existing userspace. | ||
1037 | */ | ||
1038 | |||
1039 | struct drm_out_fence_state { | ||
1040 | s32 __user *out_fence_ptr; | ||
1041 | struct sync_file *sync_file; | ||
1042 | int fd; | ||
1043 | }; | ||
1044 | |||
1045 | static int setup_out_fence(struct drm_out_fence_state *fence_state, | ||
1046 | struct dma_fence *fence) | ||
1047 | { | ||
1048 | fence_state->fd = get_unused_fd_flags(O_CLOEXEC); | ||
1049 | if (fence_state->fd < 0) | ||
1050 | return fence_state->fd; | ||
1051 | |||
1052 | if (put_user(fence_state->fd, fence_state->out_fence_ptr)) | ||
1053 | return -EFAULT; | ||
1054 | |||
1055 | fence_state->sync_file = sync_file_create(fence); | ||
1056 | if (!fence_state->sync_file) | ||
1057 | return -ENOMEM; | ||
1058 | |||
1059 | return 0; | ||
1060 | } | ||
1061 | |||
1062 | static int prepare_signaling(struct drm_device *dev, | ||
1063 | struct drm_atomic_state *state, | ||
1064 | struct drm_mode_atomic *arg, | ||
1065 | struct drm_file *file_priv, | ||
1066 | struct drm_out_fence_state **fence_state, | ||
1067 | unsigned int *num_fences) | ||
1068 | { | ||
1069 | struct drm_crtc *crtc; | ||
1070 | struct drm_crtc_state *crtc_state; | ||
1071 | struct drm_connector *conn; | ||
1072 | struct drm_connector_state *conn_state; | ||
1073 | int i, c = 0, ret; | ||
1074 | |||
1075 | if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) | ||
1076 | return 0; | ||
1077 | |||
1078 | for_each_new_crtc_in_state(state, crtc, crtc_state, i) { | ||
1079 | s32 __user *fence_ptr; | ||
1080 | |||
1081 | fence_ptr = get_out_fence_for_crtc(crtc_state->state, crtc); | ||
1082 | |||
1083 | if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT || fence_ptr) { | ||
1084 | struct drm_pending_vblank_event *e; | ||
1085 | |||
1086 | e = create_vblank_event(crtc, arg->user_data); | ||
1087 | if (!e) | ||
1088 | return -ENOMEM; | ||
1089 | |||
1090 | crtc_state->event = e; | ||
1091 | } | ||
1092 | |||
1093 | if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) { | ||
1094 | struct drm_pending_vblank_event *e = crtc_state->event; | ||
1095 | |||
1096 | if (!file_priv) | ||
1097 | continue; | ||
1098 | |||
1099 | ret = drm_event_reserve_init(dev, file_priv, &e->base, | ||
1100 | &e->event.base); | ||
1101 | if (ret) { | ||
1102 | kfree(e); | ||
1103 | crtc_state->event = NULL; | ||
1104 | return ret; | ||
1105 | } | ||
1106 | } | ||
1107 | |||
1108 | if (fence_ptr) { | ||
1109 | struct dma_fence *fence; | ||
1110 | struct drm_out_fence_state *f; | ||
1111 | |||
1112 | f = krealloc(*fence_state, sizeof(**fence_state) * | ||
1113 | (*num_fences + 1), GFP_KERNEL); | ||
1114 | if (!f) | ||
1115 | return -ENOMEM; | ||
1116 | |||
1117 | memset(&f[*num_fences], 0, sizeof(*f)); | ||
1118 | |||
1119 | f[*num_fences].out_fence_ptr = fence_ptr; | ||
1120 | *fence_state = f; | ||
1121 | |||
1122 | fence = drm_crtc_create_fence(crtc); | ||
1123 | if (!fence) | ||
1124 | return -ENOMEM; | ||
1125 | |||
1126 | ret = setup_out_fence(&f[(*num_fences)++], fence); | ||
1127 | if (ret) { | ||
1128 | dma_fence_put(fence); | ||
1129 | return ret; | ||
1130 | } | ||
1131 | |||
1132 | crtc_state->event->base.fence = fence; | ||
1133 | } | ||
1134 | |||
1135 | c++; | ||
1136 | } | ||
1137 | |||
1138 | for_each_new_connector_in_state(state, conn, conn_state, i) { | ||
1139 | struct drm_writeback_connector *wb_conn; | ||
1140 | struct drm_writeback_job *job; | ||
1141 | struct drm_out_fence_state *f; | ||
1142 | struct dma_fence *fence; | ||
1143 | s32 __user *fence_ptr; | ||
1144 | |||
1145 | fence_ptr = get_out_fence_for_connector(state, conn); | ||
1146 | if (!fence_ptr) | ||
1147 | continue; | ||
1148 | |||
1149 | job = drm_atomic_get_writeback_job(conn_state); | ||
1150 | if (!job) | ||
1151 | return -ENOMEM; | ||
1152 | |||
1153 | f = krealloc(*fence_state, sizeof(**fence_state) * | ||
1154 | (*num_fences + 1), GFP_KERNEL); | ||
1155 | if (!f) | ||
1156 | return -ENOMEM; | ||
1157 | |||
1158 | memset(&f[*num_fences], 0, sizeof(*f)); | ||
1159 | |||
1160 | f[*num_fences].out_fence_ptr = fence_ptr; | ||
1161 | *fence_state = f; | ||
1162 | |||
1163 | wb_conn = drm_connector_to_writeback(conn); | ||
1164 | fence = drm_writeback_get_out_fence(wb_conn); | ||
1165 | if (!fence) | ||
1166 | return -ENOMEM; | ||
1167 | |||
1168 | ret = setup_out_fence(&f[(*num_fences)++], fence); | ||
1169 | if (ret) { | ||
1170 | dma_fence_put(fence); | ||
1171 | return ret; | ||
1172 | } | ||
1173 | |||
1174 | job->out_fence = fence; | ||
1175 | } | ||
1176 | |||
1177 | /* | ||
1178 | * Having this flag means user mode pends on event which will never | ||
1179 | * reach due to lack of at least one CRTC for signaling | ||
1180 | */ | ||
1181 | if (c == 0 && (arg->flags & DRM_MODE_PAGE_FLIP_EVENT)) | ||
1182 | return -EINVAL; | ||
1183 | |||
1184 | return 0; | ||
1185 | } | ||
1186 | |||
1187 | static void complete_signaling(struct drm_device *dev, | ||
1188 | struct drm_atomic_state *state, | ||
1189 | struct drm_out_fence_state *fence_state, | ||
1190 | unsigned int num_fences, | ||
1191 | bool install_fds) | ||
1192 | { | ||
1193 | struct drm_crtc *crtc; | ||
1194 | struct drm_crtc_state *crtc_state; | ||
1195 | int i; | ||
1196 | |||
1197 | if (install_fds) { | ||
1198 | for (i = 0; i < num_fences; i++) | ||
1199 | fd_install(fence_state[i].fd, | ||
1200 | fence_state[i].sync_file->file); | ||
1201 | |||
1202 | kfree(fence_state); | ||
1203 | return; | ||
1204 | } | ||
1205 | |||
1206 | for_each_new_crtc_in_state(state, crtc, crtc_state, i) { | ||
1207 | struct drm_pending_vblank_event *event = crtc_state->event; | ||
1208 | /* | ||
1209 | * Free the allocated event. drm_atomic_helper_setup_commit | ||
1210 | * can allocate an event too, so only free it if it's ours | ||
1211 | * to prevent a double free in drm_atomic_state_clear. | ||
1212 | */ | ||
1213 | if (event && (event->base.fence || event->base.file_priv)) { | ||
1214 | drm_event_cancel_free(dev, &event->base); | ||
1215 | crtc_state->event = NULL; | ||
1216 | } | ||
1217 | } | ||
1218 | |||
1219 | if (!fence_state) | ||
1220 | return; | ||
1221 | |||
1222 | for (i = 0; i < num_fences; i++) { | ||
1223 | if (fence_state[i].sync_file) | ||
1224 | fput(fence_state[i].sync_file->file); | ||
1225 | if (fence_state[i].fd >= 0) | ||
1226 | put_unused_fd(fence_state[i].fd); | ||
1227 | |||
1228 | /* If this fails log error to the user */ | ||
1229 | if (fence_state[i].out_fence_ptr && | ||
1230 | put_user(-1, fence_state[i].out_fence_ptr)) | ||
1231 | DRM_DEBUG_ATOMIC("Couldn't clear out_fence_ptr\n"); | ||
1232 | } | ||
1233 | |||
1234 | kfree(fence_state); | ||
1235 | } | ||
1236 | |||
1237 | int drm_mode_atomic_ioctl(struct drm_device *dev, | ||
1238 | void *data, struct drm_file *file_priv) | ||
1239 | { | ||
1240 | struct drm_mode_atomic *arg = data; | ||
1241 | uint32_t __user *objs_ptr = (uint32_t __user *)(unsigned long)(arg->objs_ptr); | ||
1242 | uint32_t __user *count_props_ptr = (uint32_t __user *)(unsigned long)(arg->count_props_ptr); | ||
1243 | uint32_t __user *props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr); | ||
1244 | uint64_t __user *prop_values_ptr = (uint64_t __user *)(unsigned long)(arg->prop_values_ptr); | ||
1245 | unsigned int copied_objs, copied_props; | ||
1246 | struct drm_atomic_state *state; | ||
1247 | struct drm_modeset_acquire_ctx ctx; | ||
1248 | struct drm_out_fence_state *fence_state; | ||
1249 | int ret = 0; | ||
1250 | unsigned int i, j, num_fences; | ||
1251 | |||
1252 | /* disallow for drivers not supporting atomic: */ | ||
1253 | if (!drm_core_check_feature(dev, DRIVER_ATOMIC)) | ||
1254 | return -EINVAL; | ||
1255 | |||
1256 | /* disallow for userspace that has not enabled atomic cap (even | ||
1257 | * though this may be a bit overkill, since legacy userspace | ||
1258 | * wouldn't know how to call this ioctl) | ||
1259 | */ | ||
1260 | if (!file_priv->atomic) | ||
1261 | return -EINVAL; | ||
1262 | |||
1263 | if (arg->flags & ~DRM_MODE_ATOMIC_FLAGS) | ||
1264 | return -EINVAL; | ||
1265 | |||
1266 | if (arg->reserved) | ||
1267 | return -EINVAL; | ||
1268 | |||
1269 | if ((arg->flags & DRM_MODE_PAGE_FLIP_ASYNC) && | ||
1270 | !dev->mode_config.async_page_flip) | ||
1271 | return -EINVAL; | ||
1272 | |||
1273 | /* can't test and expect an event at the same time. */ | ||
1274 | if ((arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) && | ||
1275 | (arg->flags & DRM_MODE_PAGE_FLIP_EVENT)) | ||
1276 | return -EINVAL; | ||
1277 | |||
1278 | drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE); | ||
1279 | |||
1280 | state = drm_atomic_state_alloc(dev); | ||
1281 | if (!state) | ||
1282 | return -ENOMEM; | ||
1283 | |||
1284 | state->acquire_ctx = &ctx; | ||
1285 | state->allow_modeset = !!(arg->flags & DRM_MODE_ATOMIC_ALLOW_MODESET); | ||
1286 | |||
1287 | retry: | ||
1288 | copied_objs = 0; | ||
1289 | copied_props = 0; | ||
1290 | fence_state = NULL; | ||
1291 | num_fences = 0; | ||
1292 | |||
1293 | for (i = 0; i < arg->count_objs; i++) { | ||
1294 | uint32_t obj_id, count_props; | ||
1295 | struct drm_mode_object *obj; | ||
1296 | |||
1297 | if (get_user(obj_id, objs_ptr + copied_objs)) { | ||
1298 | ret = -EFAULT; | ||
1299 | goto out; | ||
1300 | } | ||
1301 | |||
1302 | obj = drm_mode_object_find(dev, file_priv, obj_id, DRM_MODE_OBJECT_ANY); | ||
1303 | if (!obj) { | ||
1304 | ret = -ENOENT; | ||
1305 | goto out; | ||
1306 | } | ||
1307 | |||
1308 | if (!obj->properties) { | ||
1309 | drm_mode_object_put(obj); | ||
1310 | ret = -ENOENT; | ||
1311 | goto out; | ||
1312 | } | ||
1313 | |||
1314 | if (get_user(count_props, count_props_ptr + copied_objs)) { | ||
1315 | drm_mode_object_put(obj); | ||
1316 | ret = -EFAULT; | ||
1317 | goto out; | ||
1318 | } | ||
1319 | |||
1320 | copied_objs++; | ||
1321 | |||
1322 | for (j = 0; j < count_props; j++) { | ||
1323 | uint32_t prop_id; | ||
1324 | uint64_t prop_value; | ||
1325 | struct drm_property *prop; | ||
1326 | |||
1327 | if (get_user(prop_id, props_ptr + copied_props)) { | ||
1328 | drm_mode_object_put(obj); | ||
1329 | ret = -EFAULT; | ||
1330 | goto out; | ||
1331 | } | ||
1332 | |||
1333 | prop = drm_mode_obj_find_prop_id(obj, prop_id); | ||
1334 | if (!prop) { | ||
1335 | drm_mode_object_put(obj); | ||
1336 | ret = -ENOENT; | ||
1337 | goto out; | ||
1338 | } | ||
1339 | |||
1340 | if (copy_from_user(&prop_value, | ||
1341 | prop_values_ptr + copied_props, | ||
1342 | sizeof(prop_value))) { | ||
1343 | drm_mode_object_put(obj); | ||
1344 | ret = -EFAULT; | ||
1345 | goto out; | ||
1346 | } | ||
1347 | |||
1348 | ret = drm_atomic_set_property(state, obj, prop, | ||
1349 | prop_value); | ||
1350 | if (ret) { | ||
1351 | drm_mode_object_put(obj); | ||
1352 | goto out; | ||
1353 | } | ||
1354 | |||
1355 | copied_props++; | ||
1356 | } | ||
1357 | |||
1358 | drm_mode_object_put(obj); | ||
1359 | } | ||
1360 | |||
1361 | ret = prepare_signaling(dev, state, arg, file_priv, &fence_state, | ||
1362 | &num_fences); | ||
1363 | if (ret) | ||
1364 | goto out; | ||
1365 | |||
1366 | if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) { | ||
1367 | ret = drm_atomic_check_only(state); | ||
1368 | } else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) { | ||
1369 | ret = drm_atomic_nonblocking_commit(state); | ||
1370 | } else { | ||
1371 | if (unlikely(drm_debug & DRM_UT_STATE)) | ||
1372 | drm_atomic_print_state(state); | ||
1373 | |||
1374 | ret = drm_atomic_commit(state); | ||
1375 | } | ||
1376 | |||
1377 | out: | ||
1378 | complete_signaling(dev, state, fence_state, num_fences, !ret); | ||
1379 | |||
1380 | if (ret == -EDEADLK) { | ||
1381 | drm_atomic_state_clear(state); | ||
1382 | ret = drm_modeset_backoff(&ctx); | ||
1383 | if (!ret) | ||
1384 | goto retry; | ||
1385 | } | ||
1386 | |||
1387 | drm_atomic_state_put(state); | ||
1388 | |||
1389 | drm_modeset_drop_locks(&ctx); | ||
1390 | drm_modeset_acquire_fini(&ctx); | ||
1391 | |||
1392 | return ret; | ||
1393 | } | ||