.. | .. |
---|
21 | 21 | */ |
---|
22 | 22 | |
---|
23 | 23 | #include <linux/export.h> |
---|
24 | | -#include <drm/drmP.h> |
---|
25 | | -#include <drm/drm_auth.h> |
---|
26 | | -#include <drm/drm_framebuffer.h> |
---|
27 | | -#include <drm/drm_atomic.h> |
---|
28 | | -#include <drm/drm_print.h> |
---|
| 24 | +#include <linux/uaccess.h> |
---|
29 | 25 | |
---|
30 | | -#include "drm_internal.h" |
---|
| 26 | +#include <drm/drm_atomic.h> |
---|
| 27 | +#include <drm/drm_atomic_uapi.h> |
---|
| 28 | +#include <drm/drm_auth.h> |
---|
| 29 | +#include <drm/drm_debugfs.h> |
---|
| 30 | +#include <drm/drm_drv.h> |
---|
| 31 | +#include <drm/drm_file.h> |
---|
| 32 | +#include <drm/drm_fourcc.h> |
---|
| 33 | +#include <drm/drm_framebuffer.h> |
---|
| 34 | +#include <drm/drm_gem.h> |
---|
| 35 | +#include <drm/drm_print.h> |
---|
| 36 | +#include <drm/drm_util.h> |
---|
| 37 | + |
---|
31 | 38 | #include "drm_crtc_internal.h" |
---|
| 39 | +#include "drm_internal.h" |
---|
32 | 40 | |
---|
33 | 41 | /** |
---|
34 | 42 | * DOC: overview |
---|
.. | .. |
---|
112 | 120 | struct drm_mode_fb_cmd2 r = {}; |
---|
113 | 121 | int ret; |
---|
114 | 122 | |
---|
| 123 | + if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
---|
| 124 | + return -EOPNOTSUPP; |
---|
| 125 | + |
---|
| 126 | + r.pixel_format = drm_driver_legacy_fb_format(dev, or->bpp, or->depth); |
---|
| 127 | + if (r.pixel_format == DRM_FORMAT_INVALID) { |
---|
| 128 | + DRM_DEBUG("bad {bpp:%d, depth:%d}\n", or->bpp, or->depth); |
---|
| 129 | + return -EINVAL; |
---|
| 130 | + } |
---|
| 131 | + |
---|
115 | 132 | /* convert to new format and call new ioctl */ |
---|
116 | 133 | r.fb_id = or->fb_id; |
---|
117 | 134 | r.width = or->width; |
---|
118 | 135 | r.height = or->height; |
---|
119 | 136 | r.pitches[0] = or->pitch; |
---|
120 | | - r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); |
---|
121 | 137 | r.handles[0] = or->handle; |
---|
122 | | - |
---|
123 | | - if (r.pixel_format == DRM_FORMAT_XRGB2101010 && |
---|
124 | | - dev->driver->driver_features & DRIVER_PREFER_XBGR_30BPP) |
---|
125 | | - r.pixel_format = DRM_FORMAT_XBGR2101010; |
---|
126 | 138 | |
---|
127 | 139 | ret = drm_mode_addfb2(dev, &r, file_priv); |
---|
128 | 140 | if (ret) |
---|
.. | .. |
---|
164 | 176 | int i; |
---|
165 | 177 | |
---|
166 | 178 | /* check if the format is supported at all */ |
---|
167 | | - info = __drm_format_info(r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN); |
---|
168 | | - if (!info) { |
---|
| 179 | + if (!__drm_format_info(r->pixel_format)) { |
---|
169 | 180 | struct drm_format_name_buf format_name; |
---|
170 | 181 | |
---|
171 | 182 | DRM_DEBUG_KMS("bad framebuffer format %s\n", |
---|
.. | .. |
---|
173 | 184 | &format_name)); |
---|
174 | 185 | return -EINVAL; |
---|
175 | 186 | } |
---|
176 | | - |
---|
177 | | - /* now let the driver pick its own format info */ |
---|
178 | | - info = drm_get_format_info(dev, r); |
---|
179 | 187 | |
---|
180 | 188 | if (r->width == 0) { |
---|
181 | 189 | DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width); |
---|
.. | .. |
---|
187 | 195 | return -EINVAL; |
---|
188 | 196 | } |
---|
189 | 197 | |
---|
| 198 | + /* now let the driver pick its own format info */ |
---|
| 199 | + info = drm_get_format_info(dev, r); |
---|
| 200 | + |
---|
190 | 201 | for (i = 0; i < info->num_planes; i++) { |
---|
191 | 202 | unsigned int width = fb_plane_width(r->width, info, i); |
---|
192 | 203 | unsigned int height = fb_plane_height(r->height, info, i); |
---|
193 | | - unsigned int bpp = info->bpp[i]; |
---|
| 204 | + unsigned int block_size = info->char_per_block[i]; |
---|
| 205 | + u64 min_pitch = drm_format_info_min_pitch(info, i, width); |
---|
| 206 | + |
---|
| 207 | + if (!block_size && (r->modifier[i] == DRM_FORMAT_MOD_LINEAR)) { |
---|
| 208 | + DRM_DEBUG_KMS("Format requires non-linear modifier for plane %d\n", i); |
---|
| 209 | + return -EINVAL; |
---|
| 210 | + } |
---|
194 | 211 | |
---|
195 | 212 | if (!r->handles[i]) { |
---|
196 | 213 | DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i); |
---|
197 | 214 | return -EINVAL; |
---|
198 | 215 | } |
---|
199 | 216 | |
---|
200 | | - if ((uint64_t) width * bpp / 8 > UINT_MAX) |
---|
| 217 | + if (min_pitch > UINT_MAX) |
---|
201 | 218 | return -ERANGE; |
---|
202 | 219 | |
---|
203 | 220 | if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX) |
---|
204 | 221 | return -ERANGE; |
---|
205 | 222 | |
---|
206 | | - if (r->pitches[i] < roundup(width * bpp, 8) / 8) { |
---|
| 223 | + if (block_size && r->pitches[i] < min_pitch) { |
---|
207 | 224 | DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); |
---|
208 | 225 | return -EINVAL; |
---|
209 | 226 | } |
---|
.. | .. |
---|
278 | 295 | struct drm_framebuffer *fb; |
---|
279 | 296 | int ret; |
---|
280 | 297 | |
---|
281 | | - if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS | |
---|
282 | | - DRM_MODE_FB_SECURE)) { |
---|
| 298 | + if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) { |
---|
283 | 299 | DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags); |
---|
284 | 300 | return ERR_PTR(-EINVAL); |
---|
285 | 301 | } |
---|
.. | .. |
---|
313 | 329 | |
---|
314 | 330 | return fb; |
---|
315 | 331 | } |
---|
| 332 | +EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_internal_framebuffer_create); |
---|
316 | 333 | |
---|
317 | 334 | /** |
---|
318 | 335 | * drm_mode_addfb2 - add an FB to the graphics configuration |
---|
.. | .. |
---|
336 | 353 | struct drm_framebuffer *fb; |
---|
337 | 354 | |
---|
338 | 355 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
---|
339 | | - return -EINVAL; |
---|
| 356 | + return -EOPNOTSUPP; |
---|
340 | 357 | |
---|
341 | 358 | fb = drm_internal_framebuffer_create(dev, r, file_priv); |
---|
342 | 359 | if (IS_ERR(fb)) |
---|
.. | .. |
---|
351 | 368 | mutex_unlock(&file_priv->fbs_lock); |
---|
352 | 369 | |
---|
353 | 370 | return 0; |
---|
| 371 | +} |
---|
| 372 | + |
---|
| 373 | +int drm_mode_addfb2_ioctl(struct drm_device *dev, |
---|
| 374 | + void *data, struct drm_file *file_priv) |
---|
| 375 | +{ |
---|
| 376 | +#ifdef __BIG_ENDIAN |
---|
| 377 | + if (!dev->mode_config.quirk_addfb_prefer_host_byte_order) { |
---|
| 378 | + /* |
---|
| 379 | + * Drivers must set the |
---|
| 380 | + * quirk_addfb_prefer_host_byte_order quirk to make |
---|
| 381 | + * the drm_mode_addfb() compat code work correctly on |
---|
| 382 | + * bigendian machines. |
---|
| 383 | + * |
---|
| 384 | + * If they don't they interpret pixel_format values |
---|
| 385 | + * incorrectly for bug compatibility, which in turn |
---|
| 386 | + * implies the ADDFB2 ioctl does not work correctly |
---|
| 387 | + * then. So block it to make userspace fallback to |
---|
| 388 | + * ADDFB. |
---|
| 389 | + */ |
---|
| 390 | + DRM_DEBUG_KMS("addfb2 broken on bigendian"); |
---|
| 391 | + return -EOPNOTSUPP; |
---|
| 392 | + } |
---|
| 393 | +#endif |
---|
| 394 | + return drm_mode_addfb2(dev, data, file_priv); |
---|
354 | 395 | } |
---|
355 | 396 | |
---|
356 | 397 | struct drm_mode_rmfb_work { |
---|
.. | .. |
---|
392 | 433 | int found = 0; |
---|
393 | 434 | |
---|
394 | 435 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
---|
395 | | - return -EINVAL; |
---|
| 436 | + return -EOPNOTSUPP; |
---|
396 | 437 | |
---|
397 | 438 | fb = drm_framebuffer_lookup(dev, file_priv, fb_id); |
---|
398 | 439 | if (!fb) |
---|
.. | .. |
---|
469 | 510 | int ret; |
---|
470 | 511 | |
---|
471 | 512 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
---|
472 | | - return -EINVAL; |
---|
| 513 | + return -EOPNOTSUPP; |
---|
473 | 514 | |
---|
474 | 515 | fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id); |
---|
475 | 516 | if (!fb) |
---|
.. | .. |
---|
489 | 530 | r->height = fb->height; |
---|
490 | 531 | r->width = fb->width; |
---|
491 | 532 | r->depth = fb->format->depth; |
---|
492 | | - r->bpp = fb->format->bpp[0]; |
---|
| 533 | + r->bpp = fb->format->cpp[0] * 8; |
---|
493 | 534 | r->pitch = fb->pitches[0]; |
---|
494 | 535 | |
---|
495 | 536 | /* GET_FB() is an unprivileged ioctl so we must not return a |
---|
.. | .. |
---|
507 | 548 | |
---|
508 | 549 | out: |
---|
509 | 550 | drm_framebuffer_put(fb); |
---|
| 551 | + return ret; |
---|
| 552 | +} |
---|
510 | 553 | |
---|
| 554 | +/** |
---|
| 555 | + * drm_mode_getfb2 - get extended FB info |
---|
| 556 | + * @dev: drm device for the ioctl |
---|
| 557 | + * @data: data pointer for the ioctl |
---|
| 558 | + * @file_priv: drm file for the ioctl call |
---|
| 559 | + * |
---|
| 560 | + * Lookup the FB given its ID and return info about it. |
---|
| 561 | + * |
---|
| 562 | + * Called by the user via ioctl. |
---|
| 563 | + * |
---|
| 564 | + * Returns: |
---|
| 565 | + * Zero on success, negative errno on failure. |
---|
| 566 | + */ |
---|
| 567 | +int drm_mode_getfb2_ioctl(struct drm_device *dev, |
---|
| 568 | + void *data, struct drm_file *file_priv) |
---|
| 569 | +{ |
---|
| 570 | + struct drm_mode_fb_cmd2 *r = data; |
---|
| 571 | + struct drm_framebuffer *fb; |
---|
| 572 | + unsigned int i; |
---|
| 573 | + int ret; |
---|
| 574 | + |
---|
| 575 | + if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
---|
| 576 | + return -EINVAL; |
---|
| 577 | + |
---|
| 578 | + fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id); |
---|
| 579 | + if (!fb) |
---|
| 580 | + return -ENOENT; |
---|
| 581 | + |
---|
| 582 | + /* For multi-plane framebuffers, we require the driver to place the |
---|
| 583 | + * GEM objects directly in the drm_framebuffer. For single-plane |
---|
| 584 | + * framebuffers, we can fall back to create_handle. |
---|
| 585 | + */ |
---|
| 586 | + if (!fb->obj[0] && |
---|
| 587 | + (fb->format->num_planes > 1 || !fb->funcs->create_handle)) { |
---|
| 588 | + ret = -ENODEV; |
---|
| 589 | + goto out; |
---|
| 590 | + } |
---|
| 591 | + |
---|
| 592 | + r->height = fb->height; |
---|
| 593 | + r->width = fb->width; |
---|
| 594 | + r->pixel_format = fb->format->format; |
---|
| 595 | + |
---|
| 596 | + r->flags = 0; |
---|
| 597 | + if (dev->mode_config.allow_fb_modifiers) |
---|
| 598 | + r->flags |= DRM_MODE_FB_MODIFIERS; |
---|
| 599 | + |
---|
| 600 | + for (i = 0; i < ARRAY_SIZE(r->handles); i++) { |
---|
| 601 | + r->handles[i] = 0; |
---|
| 602 | + r->pitches[i] = 0; |
---|
| 603 | + r->offsets[i] = 0; |
---|
| 604 | + r->modifier[i] = 0; |
---|
| 605 | + } |
---|
| 606 | + |
---|
| 607 | + for (i = 0; i < fb->format->num_planes; i++) { |
---|
| 608 | + r->pitches[i] = fb->pitches[i]; |
---|
| 609 | + r->offsets[i] = fb->offsets[i]; |
---|
| 610 | + if (dev->mode_config.allow_fb_modifiers) |
---|
| 611 | + r->modifier[i] = fb->modifier; |
---|
| 612 | + } |
---|
| 613 | + |
---|
| 614 | + /* GET_FB2() is an unprivileged ioctl so we must not return a |
---|
| 615 | + * buffer-handle to non master/root processes! To match GET_FB() |
---|
| 616 | + * just return invalid handles (0) for non masters/root |
---|
| 617 | + * rather than making GET_FB2() privileged. |
---|
| 618 | + */ |
---|
| 619 | + if (!drm_is_current_master(file_priv) && !capable(CAP_SYS_ADMIN)) { |
---|
| 620 | + ret = 0; |
---|
| 621 | + goto out; |
---|
| 622 | + } |
---|
| 623 | + |
---|
| 624 | + for (i = 0; i < fb->format->num_planes; i++) { |
---|
| 625 | + int j; |
---|
| 626 | + |
---|
| 627 | + /* If we reuse the same object for multiple planes, also |
---|
| 628 | + * return the same handle. |
---|
| 629 | + */ |
---|
| 630 | + for (j = 0; j < i; j++) { |
---|
| 631 | + if (fb->obj[i] == fb->obj[j]) { |
---|
| 632 | + r->handles[i] = r->handles[j]; |
---|
| 633 | + break; |
---|
| 634 | + } |
---|
| 635 | + } |
---|
| 636 | + |
---|
| 637 | + if (r->handles[i]) |
---|
| 638 | + continue; |
---|
| 639 | + |
---|
| 640 | + if (fb->obj[i]) { |
---|
| 641 | + ret = drm_gem_handle_create(file_priv, fb->obj[i], |
---|
| 642 | + &r->handles[i]); |
---|
| 643 | + } else { |
---|
| 644 | + WARN_ON(i > 0); |
---|
| 645 | + ret = fb->funcs->create_handle(fb, file_priv, |
---|
| 646 | + &r->handles[i]); |
---|
| 647 | + } |
---|
| 648 | + |
---|
| 649 | + if (ret != 0) |
---|
| 650 | + goto out; |
---|
| 651 | + } |
---|
| 652 | + |
---|
| 653 | +out: |
---|
| 654 | + if (ret != 0) { |
---|
| 655 | + /* Delete any previously-created handles on failure. */ |
---|
| 656 | + for (i = 0; i < ARRAY_SIZE(r->handles); i++) { |
---|
| 657 | + int j; |
---|
| 658 | + |
---|
| 659 | + if (r->handles[i]) |
---|
| 660 | + drm_gem_handle_delete(file_priv, r->handles[i]); |
---|
| 661 | + |
---|
| 662 | + /* Zero out any handles identical to the one we just |
---|
| 663 | + * deleted. |
---|
| 664 | + */ |
---|
| 665 | + for (j = i + 1; j < ARRAY_SIZE(r->handles); j++) { |
---|
| 666 | + if (r->handles[j] == r->handles[i]) |
---|
| 667 | + r->handles[j] = 0; |
---|
| 668 | + } |
---|
| 669 | + } |
---|
| 670 | + } |
---|
| 671 | + |
---|
| 672 | + drm_framebuffer_put(fb); |
---|
511 | 673 | return ret; |
---|
512 | 674 | } |
---|
513 | 675 | |
---|
.. | .. |
---|
542 | 704 | int ret; |
---|
543 | 705 | |
---|
544 | 706 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
---|
545 | | - return -EINVAL; |
---|
| 707 | + return -EOPNOTSUPP; |
---|
546 | 708 | |
---|
547 | 709 | fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id); |
---|
548 | 710 | if (!fb) |
---|
.. | .. |
---|
737 | 899 | * @fb: fb to unregister |
---|
738 | 900 | * |
---|
739 | 901 | * Drivers need to call this when cleaning up driver-private framebuffers, e.g. |
---|
740 | | - * those used for fbdev. Note that the caller must hold a reference of it's own, |
---|
| 902 | + * those used for fbdev. Note that the caller must hold a reference of its own, |
---|
741 | 903 | * i.e. the object may not be destroyed through this call (since it'll lead to a |
---|
742 | 904 | * locking inversion). |
---|
743 | 905 | * |
---|
.. | .. |
---|
947 | 1109 | if (drm_framebuffer_read_refcount(fb) > 1) { |
---|
948 | 1110 | if (drm_drv_uses_atomic_modeset(dev)) { |
---|
949 | 1111 | int ret = atomic_remove_fb(fb); |
---|
| 1112 | + |
---|
950 | 1113 | WARN(ret, "atomic remove_fb failed with %i\n", ret); |
---|
951 | 1114 | } else |
---|
952 | 1115 | legacy_remove_fb(fb); |
---|
.. | .. |
---|
1044 | 1207 | { "framebuffer", drm_framebuffer_info, 0 }, |
---|
1045 | 1208 | }; |
---|
1046 | 1209 | |
---|
1047 | | -int drm_framebuffer_debugfs_init(struct drm_minor *minor) |
---|
| 1210 | +void drm_framebuffer_debugfs_init(struct drm_minor *minor) |
---|
1048 | 1211 | { |
---|
1049 | | - return drm_debugfs_create_files(drm_framebuffer_debugfs_list, |
---|
1050 | | - ARRAY_SIZE(drm_framebuffer_debugfs_list), |
---|
1051 | | - minor->debugfs_root, minor); |
---|
| 1212 | + drm_debugfs_create_files(drm_framebuffer_debugfs_list, |
---|
| 1213 | + ARRAY_SIZE(drm_framebuffer_debugfs_list), |
---|
| 1214 | + minor->debugfs_root, minor); |
---|
1052 | 1215 | } |
---|
1053 | 1216 | #endif |
---|