diff options
Diffstat (limited to 'drivers/gpu/drm/drm_atomic.c')
-rw-r--r-- | drivers/gpu/drm/drm_atomic.c | 730 |
1 files changed, 717 insertions, 13 deletions
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index ff5f034cc405..1e38dfc8e462 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c | |||
@@ -56,6 +56,11 @@ drm_atomic_state_alloc(struct drm_device *dev) | |||
56 | if (!state) | 56 | if (!state) |
57 | return NULL; | 57 | return NULL; |
58 | 58 | ||
59 | /* TODO legacy paths should maybe do a better job about | ||
60 | * setting this appropriately? | ||
61 | */ | ||
62 | state->allow_modeset = true; | ||
63 | |||
59 | state->num_connector = ACCESS_ONCE(dev->mode_config.num_connector); | 64 | state->num_connector = ACCESS_ONCE(dev->mode_config.num_connector); |
60 | 65 | ||
61 | state->crtcs = kcalloc(dev->mode_config.num_crtc, | 66 | state->crtcs = kcalloc(dev->mode_config.num_crtc, |
@@ -217,6 +222,70 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state, | |||
217 | EXPORT_SYMBOL(drm_atomic_get_crtc_state); | 222 | EXPORT_SYMBOL(drm_atomic_get_crtc_state); |
218 | 223 | ||
219 | /** | 224 | /** |
225 | * drm_atomic_crtc_set_property - set property on CRTC | ||
226 | * @crtc: the drm CRTC to set a property on | ||
227 | * @state: the state object to update with the new property value | ||
228 | * @property: the property to set | ||
229 | * @val: the new property value | ||
230 | * | ||
231 | * Use this instead of calling crtc->atomic_set_property directly. | ||
232 | * This function handles generic/core properties and calls out to | ||
233 | * driver's ->atomic_set_property() for driver properties. To ensure | ||
234 | * consistent behavior you must call this function rather than the | ||
235 | * driver hook directly. | ||
236 | * | ||
237 | * RETURNS: | ||
238 | * Zero on success, error code on failure | ||
239 | */ | ||
240 | int drm_atomic_crtc_set_property(struct drm_crtc *crtc, | ||
241 | struct drm_crtc_state *state, struct drm_property *property, | ||
242 | uint64_t val) | ||
243 | { | ||
244 | if (crtc->funcs->atomic_set_property) | ||
245 | return crtc->funcs->atomic_set_property(crtc, state, property, val); | ||
246 | return -EINVAL; | ||
247 | } | ||
248 | EXPORT_SYMBOL(drm_atomic_crtc_set_property); | ||
249 | |||
250 | /* | ||
251 | * This function handles generic/core properties and calls out to | ||
252 | * driver's ->atomic_get_property() for driver properties. To ensure | ||
253 | * consistent behavior you must call this function rather than the | ||
254 | * driver hook directly. | ||
255 | */ | ||
256 | int drm_atomic_crtc_get_property(struct drm_crtc *crtc, | ||
257 | const struct drm_crtc_state *state, | ||
258 | struct drm_property *property, uint64_t *val) | ||
259 | { | ||
260 | if (crtc->funcs->atomic_get_property) | ||
261 | return crtc->funcs->atomic_get_property(crtc, state, property, val); | ||
262 | return -EINVAL; | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * drm_atomic_crtc_check - check crtc state | ||
267 | * @crtc: crtc to check | ||
268 | * @state: crtc state to check | ||
269 | * | ||
270 | * Provides core sanity checks for crtc state. | ||
271 | * | ||
272 | * RETURNS: | ||
273 | * Zero on success, error code on failure | ||
274 | */ | ||
275 | static int drm_atomic_crtc_check(struct drm_crtc *crtc, | ||
276 | struct drm_crtc_state *state) | ||
277 | { | ||
278 | /* NOTE: we explicitly don't enforce constraints such as primary | ||
279 | * layer covering entire screen, since that is something we want | ||
280 | * to allow (on hw that supports it). For hw that does not, it | ||
281 | * should be checked in driver's crtc->atomic_check() vfunc. | ||
282 | * | ||
283 | * TODO: Add generic modeset state checks once we support those. | ||
284 | */ | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | /** | ||
220 | * drm_atomic_get_plane_state - get plane state | 289 | * drm_atomic_get_plane_state - get plane state |
221 | * @state: global atomic state object | 290 | * @state: global atomic state object |
222 | * @plane: plane to get state object for | 291 | * @plane: plane to get state object for |
@@ -272,6 +341,183 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state, | |||
272 | EXPORT_SYMBOL(drm_atomic_get_plane_state); | 341 | EXPORT_SYMBOL(drm_atomic_get_plane_state); |
273 | 342 | ||
274 | /** | 343 | /** |
344 | * drm_atomic_plane_set_property - set property on plane | ||
345 | * @plane: the drm plane to set a property on | ||
346 | * @state: the state object to update with the new property value | ||
347 | * @property: the property to set | ||
348 | * @val: the new property value | ||
349 | * | ||
350 | * Use this instead of calling plane->atomic_set_property directly. | ||
351 | * This function handles generic/core properties and calls out to | ||
352 | * driver's ->atomic_set_property() for driver properties. To ensure | ||
353 | * consistent behavior you must call this function rather than the | ||
354 | * driver hook directly. | ||
355 | * | ||
356 | * RETURNS: | ||
357 | * Zero on success, error code on failure | ||
358 | */ | ||
359 | int drm_atomic_plane_set_property(struct drm_plane *plane, | ||
360 | struct drm_plane_state *state, struct drm_property *property, | ||
361 | uint64_t val) | ||
362 | { | ||
363 | struct drm_device *dev = plane->dev; | ||
364 | struct drm_mode_config *config = &dev->mode_config; | ||
365 | |||
366 | if (property == config->prop_fb_id) { | ||
367 | struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, val); | ||
368 | drm_atomic_set_fb_for_plane(state, fb); | ||
369 | if (fb) | ||
370 | drm_framebuffer_unreference(fb); | ||
371 | } else if (property == config->prop_crtc_id) { | ||
372 | struct drm_crtc *crtc = drm_crtc_find(dev, val); | ||
373 | return drm_atomic_set_crtc_for_plane(state, crtc); | ||
374 | } else if (property == config->prop_crtc_x) { | ||
375 | state->crtc_x = U642I64(val); | ||
376 | } else if (property == config->prop_crtc_y) { | ||
377 | state->crtc_y = U642I64(val); | ||
378 | } else if (property == config->prop_crtc_w) { | ||
379 | state->crtc_w = val; | ||
380 | } else if (property == config->prop_crtc_h) { | ||
381 | state->crtc_h = val; | ||
382 | } else if (property == config->prop_src_x) { | ||
383 | state->src_x = val; | ||
384 | } else if (property == config->prop_src_y) { | ||
385 | state->src_y = val; | ||
386 | } else if (property == config->prop_src_w) { | ||
387 | state->src_w = val; | ||
388 | } else if (property == config->prop_src_h) { | ||
389 | state->src_h = val; | ||
390 | } else if (plane->funcs->atomic_set_property) { | ||
391 | return plane->funcs->atomic_set_property(plane, state, | ||
392 | property, val); | ||
393 | } else { | ||
394 | return -EINVAL; | ||
395 | } | ||
396 | |||
397 | return 0; | ||
398 | } | ||
399 | EXPORT_SYMBOL(drm_atomic_plane_set_property); | ||
400 | |||
401 | /* | ||
402 | * This function handles generic/core properties and calls out to | ||
403 | * driver's ->atomic_get_property() for driver properties. To ensure | ||
404 | * consistent behavior you must call this function rather than the | ||
405 | * driver hook directly. | ||
406 | */ | ||
407 | static int | ||
408 | drm_atomic_plane_get_property(struct drm_plane *plane, | ||
409 | const struct drm_plane_state *state, | ||
410 | struct drm_property *property, uint64_t *val) | ||
411 | { | ||
412 | struct drm_device *dev = plane->dev; | ||
413 | struct drm_mode_config *config = &dev->mode_config; | ||
414 | |||
415 | if (property == config->prop_fb_id) { | ||
416 | *val = (state->fb) ? state->fb->base.id : 0; | ||
417 | } else if (property == config->prop_crtc_id) { | ||
418 | *val = (state->crtc) ? state->crtc->base.id : 0; | ||
419 | } else if (property == config->prop_crtc_x) { | ||
420 | *val = I642U64(state->crtc_x); | ||
421 | } else if (property == config->prop_crtc_y) { | ||
422 | *val = I642U64(state->crtc_y); | ||
423 | } else if (property == config->prop_crtc_w) { | ||
424 | *val = state->crtc_w; | ||
425 | } else if (property == config->prop_crtc_h) { | ||
426 | *val = state->crtc_h; | ||
427 | } else if (property == config->prop_src_x) { | ||
428 | *val = state->src_x; | ||
429 | } else if (property == config->prop_src_y) { | ||
430 | *val = state->src_y; | ||
431 | } else if (property == config->prop_src_w) { | ||
432 | *val = state->src_w; | ||
433 | } else if (property == config->prop_src_h) { | ||
434 | *val = state->src_h; | ||
435 | } else if (plane->funcs->atomic_get_property) { | ||
436 | return plane->funcs->atomic_get_property(plane, state, property, val); | ||
437 | } else { | ||
438 | return -EINVAL; | ||
439 | } | ||
440 | |||
441 | return 0; | ||
442 | } | ||
443 | |||
444 | /** | ||
445 | * drm_atomic_plane_check - check plane state | ||
446 | * @plane: plane to check | ||
447 | * @state: plane state to check | ||
448 | * | ||
449 | * Provides core sanity checks for plane state. | ||
450 | * | ||
451 | * RETURNS: | ||
452 | * Zero on success, error code on failure | ||
453 | */ | ||
454 | static int drm_atomic_plane_check(struct drm_plane *plane, | ||
455 | struct drm_plane_state *state) | ||
456 | { | ||
457 | unsigned int fb_width, fb_height; | ||
458 | unsigned int i; | ||
459 | |||
460 | /* either *both* CRTC and FB must be set, or neither */ | ||
461 | if (WARN_ON(state->crtc && !state->fb)) { | ||
462 | DRM_DEBUG_KMS("CRTC set but no FB\n"); | ||
463 | return -EINVAL; | ||
464 | } else if (WARN_ON(state->fb && !state->crtc)) { | ||
465 | DRM_DEBUG_KMS("FB set but no CRTC\n"); | ||
466 | return -EINVAL; | ||
467 | } | ||
468 | |||
469 | /* if disabled, we don't care about the rest of the state: */ | ||
470 | if (!state->crtc) | ||
471 | return 0; | ||
472 | |||
473 | /* Check whether this plane is usable on this CRTC */ | ||
474 | if (!(plane->possible_crtcs & drm_crtc_mask(state->crtc))) { | ||
475 | DRM_DEBUG_KMS("Invalid crtc for plane\n"); | ||
476 | return -EINVAL; | ||
477 | } | ||
478 | |||
479 | /* Check whether this plane supports the fb pixel format. */ | ||
480 | for (i = 0; i < plane->format_count; i++) | ||
481 | if (state->fb->pixel_format == plane->format_types[i]) | ||
482 | break; | ||
483 | if (i == plane->format_count) { | ||
484 | DRM_DEBUG_KMS("Invalid pixel format %s\n", | ||
485 | drm_get_format_name(state->fb->pixel_format)); | ||
486 | return -EINVAL; | ||
487 | } | ||
488 | |||
489 | /* Give drivers some help against integer overflows */ | ||
490 | if (state->crtc_w > INT_MAX || | ||
491 | state->crtc_x > INT_MAX - (int32_t) state->crtc_w || | ||
492 | state->crtc_h > INT_MAX || | ||
493 | state->crtc_y > INT_MAX - (int32_t) state->crtc_h) { | ||
494 | DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", | ||
495 | state->crtc_w, state->crtc_h, | ||
496 | state->crtc_x, state->crtc_y); | ||
497 | return -ERANGE; | ||
498 | } | ||
499 | |||
500 | fb_width = state->fb->width << 16; | ||
501 | fb_height = state->fb->height << 16; | ||
502 | |||
503 | /* Make sure source coordinates are inside the fb. */ | ||
504 | if (state->src_w > fb_width || | ||
505 | state->src_x > fb_width - state->src_w || | ||
506 | state->src_h > fb_height || | ||
507 | state->src_y > fb_height - state->src_h) { | ||
508 | DRM_DEBUG_KMS("Invalid source coordinates " | ||
509 | "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", | ||
510 | state->src_w >> 16, ((state->src_w & 0xffff) * 15625) >> 10, | ||
511 | state->src_h >> 16, ((state->src_h & 0xffff) * 15625) >> 10, | ||
512 | state->src_x >> 16, ((state->src_x & 0xffff) * 15625) >> 10, | ||
513 | state->src_y >> 16, ((state->src_y & 0xffff) * 15625) >> 10); | ||
514 | return -ENOSPC; | ||
515 | } | ||
516 | |||
517 | return 0; | ||
518 | } | ||
519 | |||
520 | /** | ||
275 | * drm_atomic_get_connector_state - get connector state | 521 | * drm_atomic_get_connector_state - get connector state |
276 | * @state: global atomic state object | 522 | * @state: global atomic state object |
277 | * @connector: connector to get state object for | 523 | * @connector: connector to get state object for |
@@ -343,9 +589,113 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, | |||
343 | EXPORT_SYMBOL(drm_atomic_get_connector_state); | 589 | EXPORT_SYMBOL(drm_atomic_get_connector_state); |
344 | 590 | ||
345 | /** | 591 | /** |
592 | * drm_atomic_connector_set_property - set property on connector. | ||
593 | * @connector: the drm connector to set a property on | ||
594 | * @state: the state object to update with the new property value | ||
595 | * @property: the property to set | ||
596 | * @val: the new property value | ||
597 | * | ||
598 | * Use this instead of calling connector->atomic_set_property directly. | ||
599 | * This function handles generic/core properties and calls out to | ||
600 | * driver's ->atomic_set_property() for driver properties. To ensure | ||
601 | * consistent behavior you must call this function rather than the | ||
602 | * driver hook directly. | ||
603 | * | ||
604 | * RETURNS: | ||
605 | * Zero on success, error code on failure | ||
606 | */ | ||
607 | int drm_atomic_connector_set_property(struct drm_connector *connector, | ||
608 | struct drm_connector_state *state, struct drm_property *property, | ||
609 | uint64_t val) | ||
610 | { | ||
611 | struct drm_device *dev = connector->dev; | ||
612 | struct drm_mode_config *config = &dev->mode_config; | ||
613 | |||
614 | if (property == config->prop_crtc_id) { | ||
615 | struct drm_crtc *crtc = drm_crtc_find(dev, val); | ||
616 | return drm_atomic_set_crtc_for_connector(state, crtc); | ||
617 | } else if (property == config->dpms_property) { | ||
618 | /* setting DPMS property requires special handling, which | ||
619 | * is done in legacy setprop path for us. Disallow (for | ||
620 | * now?) atomic writes to DPMS property: | ||
621 | */ | ||
622 | return -EINVAL; | ||
623 | } else if (connector->funcs->atomic_set_property) { | ||
624 | return connector->funcs->atomic_set_property(connector, | ||
625 | state, property, val); | ||
626 | } else { | ||
627 | return -EINVAL; | ||
628 | } | ||
629 | } | ||
630 | EXPORT_SYMBOL(drm_atomic_connector_set_property); | ||
631 | |||
632 | /* | ||
633 | * This function handles generic/core properties and calls out to | ||
634 | * driver's ->atomic_get_property() for driver properties. To ensure | ||
635 | * consistent behavior you must call this function rather than the | ||
636 | * driver hook directly. | ||
637 | */ | ||
638 | static int | ||
639 | drm_atomic_connector_get_property(struct drm_connector *connector, | ||
640 | const struct drm_connector_state *state, | ||
641 | struct drm_property *property, uint64_t *val) | ||
642 | { | ||
643 | struct drm_device *dev = connector->dev; | ||
644 | struct drm_mode_config *config = &dev->mode_config; | ||
645 | |||
646 | if (property == config->prop_crtc_id) { | ||
647 | *val = (state->crtc) ? state->crtc->base.id : 0; | ||
648 | } else if (property == config->dpms_property) { | ||
649 | *val = connector->dpms; | ||
650 | } else if (connector->funcs->atomic_get_property) { | ||
651 | return connector->funcs->atomic_get_property(connector, | ||
652 | state, property, val); | ||
653 | } else { | ||
654 | return -EINVAL; | ||
655 | } | ||
656 | |||
657 | return 0; | ||
658 | } | ||
659 | |||
660 | int drm_atomic_get_property(struct drm_mode_object *obj, | ||
661 | struct drm_property *property, uint64_t *val) | ||
662 | { | ||
663 | struct drm_device *dev = property->dev; | ||
664 | int ret; | ||
665 | |||
666 | switch (obj->type) { | ||
667 | case DRM_MODE_OBJECT_CONNECTOR: { | ||
668 | struct drm_connector *connector = obj_to_connector(obj); | ||
669 | WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); | ||
670 | ret = drm_atomic_connector_get_property(connector, | ||
671 | connector->state, property, val); | ||
672 | break; | ||
673 | } | ||
674 | case DRM_MODE_OBJECT_CRTC: { | ||
675 | struct drm_crtc *crtc = obj_to_crtc(obj); | ||
676 | WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); | ||
677 | ret = drm_atomic_crtc_get_property(crtc, | ||
678 | crtc->state, property, val); | ||
679 | break; | ||
680 | } | ||
681 | case DRM_MODE_OBJECT_PLANE: { | ||
682 | struct drm_plane *plane = obj_to_plane(obj); | ||
683 | WARN_ON(!drm_modeset_is_locked(&plane->mutex)); | ||
684 | ret = drm_atomic_plane_get_property(plane, | ||
685 | plane->state, property, val); | ||
686 | break; | ||
687 | } | ||
688 | default: | ||
689 | ret = -EINVAL; | ||
690 | break; | ||
691 | } | ||
692 | |||
693 | return ret; | ||
694 | } | ||
695 | |||
696 | /** | ||
346 | * drm_atomic_set_crtc_for_plane - set crtc for plane | 697 | * drm_atomic_set_crtc_for_plane - set crtc for plane |
347 | * @state: the incoming atomic state | 698 | * @plane_state: the plane whose incoming state to update |
348 | * @plane: the plane whose incoming state to update | ||
349 | * @crtc: crtc to use for the plane | 699 | * @crtc: crtc to use for the plane |
350 | * | 700 | * |
351 | * Changing the assigned crtc for a plane requires us to grab the lock and state | 701 | * Changing the assigned crtc for a plane requires us to grab the lock and state |
@@ -358,16 +708,12 @@ EXPORT_SYMBOL(drm_atomic_get_connector_state); | |||
358 | * sequence must be restarted. All other errors are fatal. | 708 | * sequence must be restarted. All other errors are fatal. |
359 | */ | 709 | */ |
360 | int | 710 | int |
361 | drm_atomic_set_crtc_for_plane(struct drm_atomic_state *state, | 711 | drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, |
362 | struct drm_plane *plane, struct drm_crtc *crtc) | 712 | struct drm_crtc *crtc) |
363 | { | 713 | { |
364 | struct drm_plane_state *plane_state = | 714 | struct drm_plane *plane = plane_state->plane; |
365 | drm_atomic_get_plane_state(state, plane); | ||
366 | struct drm_crtc_state *crtc_state; | 715 | struct drm_crtc_state *crtc_state; |
367 | 716 | ||
368 | if (WARN_ON(IS_ERR(plane_state))) | ||
369 | return PTR_ERR(plane_state); | ||
370 | |||
371 | if (plane_state->crtc) { | 717 | if (plane_state->crtc) { |
372 | crtc_state = drm_atomic_get_crtc_state(plane_state->state, | 718 | crtc_state = drm_atomic_get_crtc_state(plane_state->state, |
373 | plane_state->crtc); | 719 | plane_state->crtc); |
@@ -583,14 +929,62 @@ EXPORT_SYMBOL(drm_atomic_legacy_backoff); | |||
583 | */ | 929 | */ |
584 | int drm_atomic_check_only(struct drm_atomic_state *state) | 930 | int drm_atomic_check_only(struct drm_atomic_state *state) |
585 | { | 931 | { |
586 | struct drm_mode_config *config = &state->dev->mode_config; | 932 | struct drm_device *dev = state->dev; |
933 | struct drm_mode_config *config = &dev->mode_config; | ||
934 | int nplanes = config->num_total_plane; | ||
935 | int ncrtcs = config->num_crtc; | ||
936 | int i, ret = 0; | ||
587 | 937 | ||
588 | DRM_DEBUG_KMS("checking %p\n", state); | 938 | DRM_DEBUG_KMS("checking %p\n", state); |
589 | 939 | ||
940 | for (i = 0; i < nplanes; i++) { | ||
941 | struct drm_plane *plane = state->planes[i]; | ||
942 | |||
943 | if (!plane) | ||
944 | continue; | ||
945 | |||
946 | ret = drm_atomic_plane_check(plane, state->plane_states[i]); | ||
947 | if (ret) { | ||
948 | DRM_DEBUG_KMS("[PLANE:%d] atomic core check failed\n", | ||
949 | plane->base.id); | ||
950 | return ret; | ||
951 | } | ||
952 | } | ||
953 | |||
954 | for (i = 0; i < ncrtcs; i++) { | ||
955 | struct drm_crtc *crtc = state->crtcs[i]; | ||
956 | |||
957 | if (!crtc) | ||
958 | continue; | ||
959 | |||
960 | ret = drm_atomic_crtc_check(crtc, state->crtc_states[i]); | ||
961 | if (ret) { | ||
962 | DRM_DEBUG_KMS("[CRTC:%d] atomic core check failed\n", | ||
963 | crtc->base.id); | ||
964 | return ret; | ||
965 | } | ||
966 | } | ||
967 | |||
590 | if (config->funcs->atomic_check) | 968 | if (config->funcs->atomic_check) |
591 | return config->funcs->atomic_check(state->dev, state); | 969 | ret = config->funcs->atomic_check(state->dev, state); |
592 | else | 970 | |
593 | return 0; | 971 | if (!state->allow_modeset) { |
972 | for (i = 0; i < ncrtcs; i++) { | ||
973 | struct drm_crtc *crtc = state->crtcs[i]; | ||
974 | struct drm_crtc_state *crtc_state = state->crtc_states[i]; | ||
975 | |||
976 | if (!crtc) | ||
977 | continue; | ||
978 | |||
979 | if (crtc_state->mode_changed) { | ||
980 | DRM_DEBUG_KMS("[CRTC:%d] requires full modeset\n", | ||
981 | crtc->base.id); | ||
982 | return -EINVAL; | ||
983 | } | ||
984 | } | ||
985 | } | ||
986 | |||
987 | return ret; | ||
594 | } | 988 | } |
595 | EXPORT_SYMBOL(drm_atomic_check_only); | 989 | EXPORT_SYMBOL(drm_atomic_check_only); |
596 | 990 | ||
@@ -655,3 +1049,313 @@ int drm_atomic_async_commit(struct drm_atomic_state *state) | |||
655 | return config->funcs->atomic_commit(state->dev, state, true); | 1049 | return config->funcs->atomic_commit(state->dev, state, true); |
656 | } | 1050 | } |
657 | EXPORT_SYMBOL(drm_atomic_async_commit); | 1051 | EXPORT_SYMBOL(drm_atomic_async_commit); |
1052 | |||
1053 | /* | ||
1054 | * The big monstor ioctl | ||
1055 | */ | ||
1056 | |||
1057 | static struct drm_pending_vblank_event *create_vblank_event( | ||
1058 | struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data) | ||
1059 | { | ||
1060 | struct drm_pending_vblank_event *e = NULL; | ||
1061 | unsigned long flags; | ||
1062 | |||
1063 | spin_lock_irqsave(&dev->event_lock, flags); | ||
1064 | if (file_priv->event_space < sizeof e->event) { | ||
1065 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
1066 | goto out; | ||
1067 | } | ||
1068 | file_priv->event_space -= sizeof e->event; | ||
1069 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
1070 | |||
1071 | e = kzalloc(sizeof *e, GFP_KERNEL); | ||
1072 | if (e == NULL) { | ||
1073 | spin_lock_irqsave(&dev->event_lock, flags); | ||
1074 | file_priv->event_space += sizeof e->event; | ||
1075 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
1076 | goto out; | ||
1077 | } | ||
1078 | |||
1079 | e->event.base.type = DRM_EVENT_FLIP_COMPLETE; | ||
1080 | e->event.base.length = sizeof e->event; | ||
1081 | e->event.user_data = user_data; | ||
1082 | e->base.event = &e->event.base; | ||
1083 | e->base.file_priv = file_priv; | ||
1084 | e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; | ||
1085 | |||
1086 | out: | ||
1087 | return e; | ||
1088 | } | ||
1089 | |||
1090 | static void destroy_vblank_event(struct drm_device *dev, | ||
1091 | struct drm_file *file_priv, struct drm_pending_vblank_event *e) | ||
1092 | { | ||
1093 | unsigned long flags; | ||
1094 | |||
1095 | spin_lock_irqsave(&dev->event_lock, flags); | ||
1096 | file_priv->event_space += sizeof e->event; | ||
1097 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
1098 | kfree(e); | ||
1099 | } | ||
1100 | |||
1101 | static int atomic_set_prop(struct drm_atomic_state *state, | ||
1102 | struct drm_mode_object *obj, struct drm_property *prop, | ||
1103 | uint64_t prop_value) | ||
1104 | { | ||
1105 | struct drm_mode_object *ref; | ||
1106 | int ret; | ||
1107 | |||
1108 | if (!drm_property_change_valid_get(prop, prop_value, &ref)) | ||
1109 | return -EINVAL; | ||
1110 | |||
1111 | switch (obj->type) { | ||
1112 | case DRM_MODE_OBJECT_CONNECTOR: { | ||
1113 | struct drm_connector *connector = obj_to_connector(obj); | ||
1114 | struct drm_connector_state *connector_state; | ||
1115 | |||
1116 | connector_state = drm_atomic_get_connector_state(state, connector); | ||
1117 | if (IS_ERR(connector_state)) { | ||
1118 | ret = PTR_ERR(connector_state); | ||
1119 | break; | ||
1120 | } | ||
1121 | |||
1122 | ret = drm_atomic_connector_set_property(connector, | ||
1123 | connector_state, prop, prop_value); | ||
1124 | break; | ||
1125 | } | ||
1126 | case DRM_MODE_OBJECT_CRTC: { | ||
1127 | struct drm_crtc *crtc = obj_to_crtc(obj); | ||
1128 | struct drm_crtc_state *crtc_state; | ||
1129 | |||
1130 | crtc_state = drm_atomic_get_crtc_state(state, crtc); | ||
1131 | if (IS_ERR(crtc_state)) { | ||
1132 | ret = PTR_ERR(crtc_state); | ||
1133 | break; | ||
1134 | } | ||
1135 | |||
1136 | ret = drm_atomic_crtc_set_property(crtc, | ||
1137 | crtc_state, prop, prop_value); | ||
1138 | break; | ||
1139 | } | ||
1140 | case DRM_MODE_OBJECT_PLANE: { | ||
1141 | struct drm_plane *plane = obj_to_plane(obj); | ||
1142 | struct drm_plane_state *plane_state; | ||
1143 | |||
1144 | plane_state = drm_atomic_get_plane_state(state, plane); | ||
1145 | if (IS_ERR(plane_state)) { | ||
1146 | ret = PTR_ERR(plane_state); | ||
1147 | break; | ||
1148 | } | ||
1149 | |||
1150 | ret = drm_atomic_plane_set_property(plane, | ||
1151 | plane_state, prop, prop_value); | ||
1152 | break; | ||
1153 | } | ||
1154 | default: | ||
1155 | ret = -EINVAL; | ||
1156 | break; | ||
1157 | } | ||
1158 | |||
1159 | drm_property_change_valid_put(prop, ref); | ||
1160 | return ret; | ||
1161 | } | ||
1162 | |||
1163 | int drm_mode_atomic_ioctl(struct drm_device *dev, | ||
1164 | void *data, struct drm_file *file_priv) | ||
1165 | { | ||
1166 | struct drm_mode_atomic *arg = data; | ||
1167 | uint32_t __user *objs_ptr = (uint32_t __user *)(unsigned long)(arg->objs_ptr); | ||
1168 | uint32_t __user *count_props_ptr = (uint32_t __user *)(unsigned long)(arg->count_props_ptr); | ||
1169 | uint32_t __user *props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr); | ||
1170 | uint64_t __user *prop_values_ptr = (uint64_t __user *)(unsigned long)(arg->prop_values_ptr); | ||
1171 | unsigned int copied_objs, copied_props; | ||
1172 | struct drm_atomic_state *state; | ||
1173 | struct drm_modeset_acquire_ctx ctx; | ||
1174 | struct drm_plane *plane; | ||
1175 | unsigned plane_mask = 0; | ||
1176 | int ret = 0; | ||
1177 | unsigned int i, j; | ||
1178 | |||
1179 | /* disallow for drivers not supporting atomic: */ | ||
1180 | if (!drm_core_check_feature(dev, DRIVER_ATOMIC)) | ||
1181 | return -EINVAL; | ||
1182 | |||
1183 | /* disallow for userspace that has not enabled atomic cap (even | ||
1184 | * though this may be a bit overkill, since legacy userspace | ||
1185 | * wouldn't know how to call this ioctl) | ||
1186 | */ | ||
1187 | if (!file_priv->atomic) | ||
1188 | return -EINVAL; | ||
1189 | |||
1190 | if (arg->flags & ~DRM_MODE_ATOMIC_FLAGS) | ||
1191 | return -EINVAL; | ||
1192 | |||
1193 | if (arg->reserved) | ||
1194 | return -EINVAL; | ||
1195 | |||
1196 | if ((arg->flags & DRM_MODE_PAGE_FLIP_ASYNC) && | ||
1197 | !dev->mode_config.async_page_flip) | ||
1198 | return -EINVAL; | ||
1199 | |||
1200 | /* can't test and expect an event at the same time. */ | ||
1201 | if ((arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) && | ||
1202 | (arg->flags & DRM_MODE_PAGE_FLIP_EVENT)) | ||
1203 | return -EINVAL; | ||
1204 | |||
1205 | drm_modeset_acquire_init(&ctx, 0); | ||
1206 | |||
1207 | state = drm_atomic_state_alloc(dev); | ||
1208 | if (!state) | ||
1209 | return -ENOMEM; | ||
1210 | |||
1211 | state->acquire_ctx = &ctx; | ||
1212 | state->allow_modeset = !!(arg->flags & DRM_MODE_ATOMIC_ALLOW_MODESET); | ||
1213 | |||
1214 | retry: | ||
1215 | copied_objs = 0; | ||
1216 | copied_props = 0; | ||
1217 | |||
1218 | for (i = 0; i < arg->count_objs; i++) { | ||
1219 | uint32_t obj_id, count_props; | ||
1220 | struct drm_mode_object *obj; | ||
1221 | |||
1222 | if (get_user(obj_id, objs_ptr + copied_objs)) { | ||
1223 | ret = -EFAULT; | ||
1224 | goto fail; | ||
1225 | } | ||
1226 | |||
1227 | obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY); | ||
1228 | if (!obj || !obj->properties) { | ||
1229 | ret = -ENOENT; | ||
1230 | goto fail; | ||
1231 | } | ||
1232 | |||
1233 | if (obj->type == DRM_MODE_OBJECT_PLANE) { | ||
1234 | plane = obj_to_plane(obj); | ||
1235 | plane_mask |= (1 << drm_plane_index(plane)); | ||
1236 | plane->old_fb = plane->fb; | ||
1237 | } | ||
1238 | |||
1239 | if (get_user(count_props, count_props_ptr + copied_objs)) { | ||
1240 | ret = -EFAULT; | ||
1241 | goto fail; | ||
1242 | } | ||
1243 | |||
1244 | copied_objs++; | ||
1245 | |||
1246 | for (j = 0; j < count_props; j++) { | ||
1247 | uint32_t prop_id; | ||
1248 | uint64_t prop_value; | ||
1249 | struct drm_property *prop; | ||
1250 | |||
1251 | if (get_user(prop_id, props_ptr + copied_props)) { | ||
1252 | ret = -EFAULT; | ||
1253 | goto fail; | ||
1254 | } | ||
1255 | |||
1256 | prop = drm_property_find(dev, prop_id); | ||
1257 | if (!prop) { | ||
1258 | ret = -ENOENT; | ||
1259 | goto fail; | ||
1260 | } | ||
1261 | |||
1262 | if (get_user(prop_value, prop_values_ptr + copied_props)) { | ||
1263 | ret = -EFAULT; | ||
1264 | goto fail; | ||
1265 | } | ||
1266 | |||
1267 | ret = atomic_set_prop(state, obj, prop, prop_value); | ||
1268 | if (ret) | ||
1269 | goto fail; | ||
1270 | |||
1271 | copied_props++; | ||
1272 | } | ||
1273 | } | ||
1274 | |||
1275 | if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) { | ||
1276 | int ncrtcs = dev->mode_config.num_crtc; | ||
1277 | |||
1278 | for (i = 0; i < ncrtcs; i++) { | ||
1279 | struct drm_crtc_state *crtc_state = state->crtc_states[i]; | ||
1280 | struct drm_pending_vblank_event *e; | ||
1281 | |||
1282 | if (!crtc_state) | ||
1283 | continue; | ||
1284 | |||
1285 | e = create_vblank_event(dev, file_priv, arg->user_data); | ||
1286 | if (!e) { | ||
1287 | ret = -ENOMEM; | ||
1288 | goto fail; | ||
1289 | } | ||
1290 | |||
1291 | crtc_state->event = e; | ||
1292 | } | ||
1293 | } | ||
1294 | |||
1295 | if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) { | ||
1296 | ret = drm_atomic_check_only(state); | ||
1297 | /* _check_only() does not free state, unlike _commit() */ | ||
1298 | drm_atomic_state_free(state); | ||
1299 | } else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) { | ||
1300 | ret = drm_atomic_async_commit(state); | ||
1301 | } else { | ||
1302 | ret = drm_atomic_commit(state); | ||
1303 | } | ||
1304 | |||
1305 | /* if succeeded, fixup legacy plane crtc/fb ptrs before dropping | ||
1306 | * locks (ie. while it is still safe to deref plane->state). We | ||
1307 | * need to do this here because the driver entry points cannot | ||
1308 | * distinguish between legacy and atomic ioctls. | ||
1309 | */ | ||
1310 | drm_for_each_plane_mask(plane, dev, plane_mask) { | ||
1311 | if (ret == 0) { | ||
1312 | struct drm_framebuffer *new_fb = plane->state->fb; | ||
1313 | if (new_fb) | ||
1314 | drm_framebuffer_reference(new_fb); | ||
1315 | plane->fb = new_fb; | ||
1316 | plane->crtc = plane->state->crtc; | ||
1317 | } else { | ||
1318 | plane->old_fb = NULL; | ||
1319 | } | ||
1320 | if (plane->old_fb) { | ||
1321 | drm_framebuffer_unreference(plane->old_fb); | ||
1322 | plane->old_fb = NULL; | ||
1323 | } | ||
1324 | } | ||
1325 | |||
1326 | drm_modeset_drop_locks(&ctx); | ||
1327 | drm_modeset_acquire_fini(&ctx); | ||
1328 | |||
1329 | return ret; | ||
1330 | |||
1331 | fail: | ||
1332 | if (ret == -EDEADLK) | ||
1333 | goto backoff; | ||
1334 | |||
1335 | if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) { | ||
1336 | int ncrtcs = dev->mode_config.num_crtc; | ||
1337 | |||
1338 | for (i = 0; i < ncrtcs; i++) { | ||
1339 | struct drm_crtc_state *crtc_state = state->crtc_states[i]; | ||
1340 | |||
1341 | if (!crtc_state) | ||
1342 | continue; | ||
1343 | |||
1344 | destroy_vblank_event(dev, file_priv, crtc_state->event); | ||
1345 | crtc_state->event = NULL; | ||
1346 | } | ||
1347 | } | ||
1348 | |||
1349 | drm_atomic_state_free(state); | ||
1350 | |||
1351 | drm_modeset_drop_locks(&ctx); | ||
1352 | drm_modeset_acquire_fini(&ctx); | ||
1353 | |||
1354 | return ret; | ||
1355 | |||
1356 | backoff: | ||
1357 | drm_atomic_state_clear(state); | ||
1358 | drm_modeset_backoff(&ctx); | ||
1359 | |||
1360 | goto retry; | ||
1361 | } | ||