1796 lines · c
1/*2 * Copyright (c) 2016 Intel Corporation3 *4 * Permission to use, copy, modify, distribute, and sell this software and its5 * documentation for any purpose is hereby granted without fee, provided that6 * the above copyright notice appear in all copies and that both that copyright7 * notice and this permission notice appear in supporting documentation, and8 * that the name of the copyright holders not be used in advertising or9 * publicity pertaining to distribution of the software without specific,10 * written prior permission. The copyright holders make no representations11 * about the suitability of this software for any purpose. It is provided "as12 * is" without express or implied warranty.13 *14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE20 * OF THIS SOFTWARE.21 */22 23#include <linux/slab.h>24#include <linux/uaccess.h>25 26#include <drm/drm_plane.h>27#include <drm/drm_drv.h>28#include <drm/drm_print.h>29#include <drm/drm_framebuffer.h>30#include <drm/drm_file.h>31#include <drm/drm_crtc.h>32#include <drm/drm_fourcc.h>33#include <drm/drm_managed.h>34#include <drm/drm_vblank.h>35 36#include "drm_crtc_internal.h"37 38/**39 * DOC: overview40 *41 * A plane represents an image source that can be blended with or overlaid on42 * top of a CRTC during the scanout process. Planes take their input data from a43 * &drm_framebuffer object. The plane itself specifies the cropping and scaling44 * of that image, and where it is placed on the visible area of a display45 * pipeline, represented by &drm_crtc. A plane can also have additional46 * properties that specify how the pixels are positioned and blended, like47 * rotation or Z-position. All these properties are stored in &drm_plane_state.48 *49 * Unless explicitly specified (via CRTC property or otherwise), the active area50 * of a CRTC will be black by default. This means portions of the active area51 * which are not covered by a plane will be black, and alpha blending of any52 * planes with the CRTC background will blend with black at the lowest zpos.53 *54 * To create a plane, a KMS drivers allocates and zeroes an instances of55 * &struct drm_plane (possibly as part of a larger structure) and registers it56 * with a call to drm_universal_plane_init().57 *58 * Each plane has a type, see enum drm_plane_type. A plane can be compatible59 * with multiple CRTCs, see &drm_plane.possible_crtcs.60 *61 * Each CRTC must have a unique primary plane userspace can attach to enable62 * the CRTC. In other words, userspace must be able to attach a different63 * primary plane to each CRTC at the same time. Primary planes can still be64 * compatible with multiple CRTCs. There must be exactly as many primary planes65 * as there are CRTCs.66 *67 * Legacy uAPI doesn't expose the primary and cursor planes directly. DRM core68 * relies on the driver to set the primary and optionally the cursor plane used69 * for legacy IOCTLs. This is done by calling drm_crtc_init_with_planes(). All70 * drivers must provide one primary plane per CRTC to avoid surprising legacy71 * userspace too much.72 */73 74/**75 * DOC: standard plane properties76 *77 * DRM planes have a few standardized properties:78 *79 * type:80 * Immutable property describing the type of the plane.81 *82 * For user-space which has enabled the &DRM_CLIENT_CAP_ATOMIC capability,83 * the plane type is just a hint and is mostly superseded by atomic84 * test-only commits. The type hint can still be used to come up more85 * easily with a plane configuration accepted by the driver.86 *87 * The value of this property can be one of the following:88 *89 * "Primary":90 * To light up a CRTC, attaching a primary plane is the most likely to91 * work if it covers the whole CRTC and doesn't have scaling or92 * cropping set up.93 *94 * Drivers may support more features for the primary plane, user-space95 * can find out with test-only atomic commits.96 *97 * Some primary planes are implicitly used by the kernel in the legacy98 * IOCTLs &DRM_IOCTL_MODE_SETCRTC and &DRM_IOCTL_MODE_PAGE_FLIP.99 * Therefore user-space must not mix explicit usage of any primary100 * plane (e.g. through an atomic commit) with these legacy IOCTLs.101 *102 * "Cursor":103 * To enable this plane, using a framebuffer configured without scaling104 * or cropping and with the following properties is the most likely to105 * work:106 *107 * - If the driver provides the capabilities &DRM_CAP_CURSOR_WIDTH and108 * &DRM_CAP_CURSOR_HEIGHT, create the framebuffer with this size.109 * Otherwise, create a framebuffer with the size 64x64.110 * - If the driver doesn't support modifiers, create a framebuffer with111 * a linear layout. Otherwise, use the IN_FORMATS plane property.112 *113 * Drivers may support more features for the cursor plane, user-space114 * can find out with test-only atomic commits.115 *116 * Some cursor planes are implicitly used by the kernel in the legacy117 * IOCTLs &DRM_IOCTL_MODE_CURSOR and &DRM_IOCTL_MODE_CURSOR2.118 * Therefore user-space must not mix explicit usage of any cursor119 * plane (e.g. through an atomic commit) with these legacy IOCTLs.120 *121 * Some drivers may support cursors even if no cursor plane is exposed.122 * In this case, the legacy cursor IOCTLs can be used to configure the123 * cursor.124 *125 * "Overlay":126 * Neither primary nor cursor.127 *128 * Overlay planes are the only planes exposed when the129 * &DRM_CLIENT_CAP_UNIVERSAL_PLANES capability is disabled.130 *131 * IN_FORMATS:132 * Blob property which contains the set of buffer format and modifier133 * pairs supported by this plane. The blob is a struct134 * drm_format_modifier_blob. Without this property the plane doesn't135 * support buffers with modifiers. Userspace cannot change this property.136 *137 * Note that userspace can check the &DRM_CAP_ADDFB2_MODIFIERS driver138 * capability for general modifier support. If this flag is set then every139 * plane will have the IN_FORMATS property, even when it only supports140 * DRM_FORMAT_MOD_LINEAR. Before linux kernel release v5.1 there have been141 * various bugs in this area with inconsistencies between the capability142 * flag and per-plane properties.143 *144 * SIZE_HINTS:145 * Blob property which contains the set of recommended plane size146 * which can used for simple "cursor like" use cases (eg. no scaling).147 * Using these hints frees userspace from extensive probing of148 * supported plane sizes through atomic/setcursor ioctls.149 *150 * The blob contains an array of struct drm_plane_size_hint, in151 * order of preference. For optimal usage userspace should pick152 * the first size that satisfies its own requirements.153 *154 * Drivers should only attach this property to planes that155 * support a very limited set of sizes.156 *157 * Note that property value 0 (ie. no blob) is reserved for potential158 * future use. Current userspace is expected to ignore the property159 * if the value is 0, and fall back to some other means (eg.160 * &DRM_CAP_CURSOR_WIDTH and &DRM_CAP_CURSOR_HEIGHT) to determine161 * the appropriate plane size to use.162 */163 164static unsigned int drm_num_planes(struct drm_device *dev)165{166 unsigned int num = 0;167 struct drm_plane *tmp;168 169 drm_for_each_plane(tmp, dev) {170 num++;171 }172 173 return num;174}175 176static inline u32 *177formats_ptr(struct drm_format_modifier_blob *blob)178{179 return (u32 *)(((char *)blob) + blob->formats_offset);180}181 182static inline struct drm_format_modifier *183modifiers_ptr(struct drm_format_modifier_blob *blob)184{185 return (struct drm_format_modifier *)(((char *)blob) + blob->modifiers_offset);186}187 188static int create_in_format_blob(struct drm_device *dev, struct drm_plane *plane)189{190 const struct drm_mode_config *config = &dev->mode_config;191 struct drm_property_blob *blob;192 struct drm_format_modifier *mod;193 size_t blob_size, formats_size, modifiers_size;194 struct drm_format_modifier_blob *blob_data;195 unsigned int i, j;196 197 formats_size = sizeof(__u32) * plane->format_count;198 if (WARN_ON(!formats_size)) {199 /* 0 formats are never expected */200 return 0;201 }202 203 modifiers_size =204 sizeof(struct drm_format_modifier) * plane->modifier_count;205 206 blob_size = sizeof(struct drm_format_modifier_blob);207 /* Modifiers offset is a pointer to a struct with a 64 bit field so it208 * should be naturally aligned to 8B.209 */210 BUILD_BUG_ON(sizeof(struct drm_format_modifier_blob) % 8);211 blob_size += ALIGN(formats_size, 8);212 blob_size += modifiers_size;213 214 blob = drm_property_create_blob(dev, blob_size, NULL);215 if (IS_ERR(blob))216 return -1;217 218 blob_data = blob->data;219 blob_data->version = FORMAT_BLOB_CURRENT;220 blob_data->count_formats = plane->format_count;221 blob_data->formats_offset = sizeof(struct drm_format_modifier_blob);222 blob_data->count_modifiers = plane->modifier_count;223 224 blob_data->modifiers_offset =225 ALIGN(blob_data->formats_offset + formats_size, 8);226 227 memcpy(formats_ptr(blob_data), plane->format_types, formats_size);228 229 mod = modifiers_ptr(blob_data);230 for (i = 0; i < plane->modifier_count; i++) {231 for (j = 0; j < plane->format_count; j++) {232 if (!plane->funcs->format_mod_supported ||233 plane->funcs->format_mod_supported(plane,234 plane->format_types[j],235 plane->modifiers[i])) {236 mod->formats |= 1ULL << j;237 }238 }239 240 mod->modifier = plane->modifiers[i];241 mod->offset = 0;242 mod->pad = 0;243 mod++;244 }245 246 drm_object_attach_property(&plane->base, config->modifiers_property,247 blob->base.id);248 249 return 0;250}251 252/**253 * DOC: hotspot properties254 *255 * HOTSPOT_X: property to set mouse hotspot x offset.256 * HOTSPOT_Y: property to set mouse hotspot y offset.257 *258 * When the plane is being used as a cursor image to display a mouse pointer,259 * the "hotspot" is the offset within the cursor image where mouse events260 * are expected to go.261 *262 * Positive values move the hotspot from the top-left corner of the cursor263 * plane towards the right and bottom.264 *265 * Most display drivers do not need this information because the266 * hotspot is not actually connected to anything visible on screen.267 * However, this is necessary for display drivers like the para-virtualized268 * drivers (eg qxl, vbox, virtio, vmwgfx), that are attached to a user console269 * with a mouse pointer. Since these consoles are often being remoted over a270 * network, they would otherwise have to wait to display the pointer movement to271 * the user until a full network round-trip has occurred. New mouse events have272 * to be sent from the user's console, over the network to the virtual input273 * devices, forwarded to the desktop for processing, and then the cursor plane's274 * position can be updated and sent back to the user's console over the network.275 * Instead, with the hotspot information, the console can anticipate the new276 * location, and draw the mouse cursor there before the confirmation comes in.277 * To do that correctly, the user's console must be able predict how the278 * desktop will process mouse events, which normally requires the desktop's279 * mouse topology information, ie where each CRTC sits in the mouse coordinate280 * space. This is typically sent to the para-virtualized drivers using some281 * driver-specific method, and the driver then forwards it to the console by282 * way of the virtual display device or hypervisor.283 *284 * The assumption is generally made that there is only one cursor plane being285 * used this way at a time, and that the desktop is feeding all mouse devices286 * into the same global pointer. Para-virtualized drivers that require this287 * should only be exposing a single cursor plane, or find some other way288 * to coordinate with a userspace desktop that supports multiple pointers.289 * If the hotspot properties are set, the cursor plane is therefore assumed to be290 * used only for displaying a mouse cursor image, and the position of the combined291 * cursor plane + offset can therefore be used for coordinating with input from a292 * mouse device.293 *294 * The cursor will then be drawn either at the location of the plane in the CRTC295 * console, or as a free-floating cursor plane on the user's console296 * corresponding to their desktop mouse position.297 *298 * DRM clients which would like to work correctly on drivers which expose299 * hotspot properties should advertise DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT.300 * Setting this property on drivers which do not special case301 * cursor planes will return EOPNOTSUPP, which can be used by userspace to302 * gauge requirements of the hardware/drivers they're running on. Advertising303 * DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT implies that the userspace client will be304 * correctly setting the hotspot properties.305 */306 307/**308 * drm_plane_create_hotspot_properties - creates the mouse hotspot309 * properties and attaches them to the given cursor plane310 *311 * @plane: drm cursor plane312 *313 * This function enables the mouse hotspot property on a given314 * cursor plane. Look at the documentation for hotspot properties315 * to get a better understanding for what they're used for.316 *317 * RETURNS:318 * Zero for success or -errno319 */320static int drm_plane_create_hotspot_properties(struct drm_plane *plane)321{322 struct drm_property *prop_x;323 struct drm_property *prop_y;324 325 drm_WARN_ON(plane->dev,326 !drm_core_check_feature(plane->dev,327 DRIVER_CURSOR_HOTSPOT));328 329 prop_x = drm_property_create_signed_range(plane->dev, 0, "HOTSPOT_X",330 INT_MIN, INT_MAX);331 if (IS_ERR(prop_x))332 return PTR_ERR(prop_x);333 334 prop_y = drm_property_create_signed_range(plane->dev, 0, "HOTSPOT_Y",335 INT_MIN, INT_MAX);336 if (IS_ERR(prop_y)) {337 drm_property_destroy(plane->dev, prop_x);338 return PTR_ERR(prop_y);339 }340 341 drm_object_attach_property(&plane->base, prop_x, 0);342 drm_object_attach_property(&plane->base, prop_y, 0);343 plane->hotspot_x_property = prop_x;344 plane->hotspot_y_property = prop_y;345 346 return 0;347}348 349__printf(9, 0)350static int __drm_universal_plane_init(struct drm_device *dev,351 struct drm_plane *plane,352 uint32_t possible_crtcs,353 const struct drm_plane_funcs *funcs,354 const uint32_t *formats,355 unsigned int format_count,356 const uint64_t *format_modifiers,357 enum drm_plane_type type,358 const char *name, va_list ap)359{360 struct drm_mode_config *config = &dev->mode_config;361 static const uint64_t default_modifiers[] = {362 DRM_FORMAT_MOD_LINEAR,363 };364 unsigned int format_modifier_count = 0;365 int ret;366 367 /* plane index is used with 32bit bitmasks */368 if (WARN_ON(config->num_total_plane >= 32))369 return -EINVAL;370 371 /*372 * First driver to need more than 64 formats needs to fix this. Each373 * format is encoded as a bit and the current code only supports a u64.374 */375 if (WARN_ON(format_count > 64))376 return -EINVAL;377 378 WARN_ON(drm_drv_uses_atomic_modeset(dev) &&379 (!funcs->atomic_destroy_state ||380 !funcs->atomic_duplicate_state));381 382 ret = drm_mode_object_add(dev, &plane->base, DRM_MODE_OBJECT_PLANE);383 if (ret)384 return ret;385 386 drm_modeset_lock_init(&plane->mutex);387 388 plane->base.properties = &plane->properties;389 plane->dev = dev;390 plane->funcs = funcs;391 plane->format_types = kmalloc_array(format_count, sizeof(uint32_t),392 GFP_KERNEL);393 if (!plane->format_types) {394 DRM_DEBUG_KMS("out of memory when allocating plane\n");395 drm_mode_object_unregister(dev, &plane->base);396 return -ENOMEM;397 }398 399 if (format_modifiers) {400 const uint64_t *temp_modifiers = format_modifiers;401 402 while (*temp_modifiers++ != DRM_FORMAT_MOD_INVALID)403 format_modifier_count++;404 } else {405 if (!dev->mode_config.fb_modifiers_not_supported) {406 format_modifiers = default_modifiers;407 format_modifier_count = ARRAY_SIZE(default_modifiers);408 }409 }410 411 /* autoset the cap and check for consistency across all planes */412 drm_WARN_ON(dev, config->fb_modifiers_not_supported &&413 format_modifier_count);414 415 plane->modifier_count = format_modifier_count;416 plane->modifiers = kmalloc_array(format_modifier_count,417 sizeof(format_modifiers[0]),418 GFP_KERNEL);419 420 if (format_modifier_count && !plane->modifiers) {421 DRM_DEBUG_KMS("out of memory when allocating plane\n");422 kfree(plane->format_types);423 drm_mode_object_unregister(dev, &plane->base);424 return -ENOMEM;425 }426 427 if (name) {428 plane->name = kvasprintf(GFP_KERNEL, name, ap);429 } else {430 plane->name = kasprintf(GFP_KERNEL, "plane-%d",431 drm_num_planes(dev));432 }433 if (!plane->name) {434 kfree(plane->format_types);435 kfree(plane->modifiers);436 drm_mode_object_unregister(dev, &plane->base);437 return -ENOMEM;438 }439 440 memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));441 plane->format_count = format_count;442 memcpy(plane->modifiers, format_modifiers,443 format_modifier_count * sizeof(format_modifiers[0]));444 plane->possible_crtcs = possible_crtcs;445 plane->type = type;446 447 list_add_tail(&plane->head, &config->plane_list);448 plane->index = config->num_total_plane++;449 450 drm_object_attach_property(&plane->base,451 config->plane_type_property,452 plane->type);453 454 if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {455 drm_object_attach_property(&plane->base, config->prop_fb_id, 0);456 drm_object_attach_property(&plane->base, config->prop_in_fence_fd, -1);457 drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);458 drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);459 drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);460 drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);461 drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);462 drm_object_attach_property(&plane->base, config->prop_src_x, 0);463 drm_object_attach_property(&plane->base, config->prop_src_y, 0);464 drm_object_attach_property(&plane->base, config->prop_src_w, 0);465 drm_object_attach_property(&plane->base, config->prop_src_h, 0);466 }467 if (drm_core_check_feature(dev, DRIVER_CURSOR_HOTSPOT) &&468 type == DRM_PLANE_TYPE_CURSOR) {469 drm_plane_create_hotspot_properties(plane);470 }471 472 if (format_modifier_count)473 create_in_format_blob(dev, plane);474 475 return 0;476}477 478/**479 * drm_universal_plane_init - Initialize a new universal plane object480 * @dev: DRM device481 * @plane: plane object to init482 * @possible_crtcs: bitmask of possible CRTCs483 * @funcs: callbacks for the new plane484 * @formats: array of supported formats (DRM_FORMAT\_\*)485 * @format_count: number of elements in @formats486 * @format_modifiers: array of struct drm_format modifiers terminated by487 * DRM_FORMAT_MOD_INVALID488 * @type: type of plane (overlay, primary, cursor)489 * @name: printf style format string for the plane name, or NULL for default name490 *491 * Initializes a plane object of type @type. The &drm_plane_funcs.destroy hook492 * should call drm_plane_cleanup() and kfree() the plane structure. The plane493 * structure should not be allocated with devm_kzalloc().494 *495 * Note: consider using drmm_universal_plane_alloc() instead of496 * drm_universal_plane_init() to let the DRM managed resource infrastructure497 * take care of cleanup and deallocation.498 *499 * Drivers that only support the DRM_FORMAT_MOD_LINEAR modifier support may set500 * @format_modifiers to NULL. The plane will advertise the linear modifier.501 *502 * Returns:503 * Zero on success, error code on failure.504 */505int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,506 uint32_t possible_crtcs,507 const struct drm_plane_funcs *funcs,508 const uint32_t *formats, unsigned int format_count,509 const uint64_t *format_modifiers,510 enum drm_plane_type type,511 const char *name, ...)512{513 va_list ap;514 int ret;515 516 WARN_ON(!funcs->destroy);517 518 va_start(ap, name);519 ret = __drm_universal_plane_init(dev, plane, possible_crtcs, funcs,520 formats, format_count, format_modifiers,521 type, name, ap);522 va_end(ap);523 return ret;524}525EXPORT_SYMBOL(drm_universal_plane_init);526 527static void drmm_universal_plane_alloc_release(struct drm_device *dev, void *ptr)528{529 struct drm_plane *plane = ptr;530 531 if (WARN_ON(!plane->dev))532 return;533 534 drm_plane_cleanup(plane);535}536 537void *__drmm_universal_plane_alloc(struct drm_device *dev, size_t size,538 size_t offset, uint32_t possible_crtcs,539 const struct drm_plane_funcs *funcs,540 const uint32_t *formats, unsigned int format_count,541 const uint64_t *format_modifiers,542 enum drm_plane_type type,543 const char *name, ...)544{545 void *container;546 struct drm_plane *plane;547 va_list ap;548 int ret;549 550 if (WARN_ON(!funcs || funcs->destroy))551 return ERR_PTR(-EINVAL);552 553 container = drmm_kzalloc(dev, size, GFP_KERNEL);554 if (!container)555 return ERR_PTR(-ENOMEM);556 557 plane = container + offset;558 559 va_start(ap, name);560 ret = __drm_universal_plane_init(dev, plane, possible_crtcs, funcs,561 formats, format_count, format_modifiers,562 type, name, ap);563 va_end(ap);564 if (ret)565 return ERR_PTR(ret);566 567 ret = drmm_add_action_or_reset(dev, drmm_universal_plane_alloc_release,568 plane);569 if (ret)570 return ERR_PTR(ret);571 572 return container;573}574EXPORT_SYMBOL(__drmm_universal_plane_alloc);575 576void *__drm_universal_plane_alloc(struct drm_device *dev, size_t size,577 size_t offset, uint32_t possible_crtcs,578 const struct drm_plane_funcs *funcs,579 const uint32_t *formats, unsigned int format_count,580 const uint64_t *format_modifiers,581 enum drm_plane_type type,582 const char *name, ...)583{584 void *container;585 struct drm_plane *plane;586 va_list ap;587 int ret;588 589 if (drm_WARN_ON(dev, !funcs))590 return ERR_PTR(-EINVAL);591 592 container = kzalloc(size, GFP_KERNEL);593 if (!container)594 return ERR_PTR(-ENOMEM);595 596 plane = container + offset;597 598 va_start(ap, name);599 ret = __drm_universal_plane_init(dev, plane, possible_crtcs, funcs,600 formats, format_count, format_modifiers,601 type, name, ap);602 va_end(ap);603 if (ret)604 goto err_kfree;605 606 return container;607 608err_kfree:609 kfree(container);610 return ERR_PTR(ret);611}612EXPORT_SYMBOL(__drm_universal_plane_alloc);613 614int drm_plane_register_all(struct drm_device *dev)615{616 unsigned int num_planes = 0;617 unsigned int num_zpos = 0;618 struct drm_plane *plane;619 int ret = 0;620 621 drm_for_each_plane(plane, dev) {622 if (plane->funcs->late_register)623 ret = plane->funcs->late_register(plane);624 if (ret)625 return ret;626 627 if (plane->zpos_property)628 num_zpos++;629 num_planes++;630 }631 632 drm_WARN(dev, num_zpos && num_planes != num_zpos,633 "Mixing planes with and without zpos property is invalid\n");634 635 return 0;636}637 638void drm_plane_unregister_all(struct drm_device *dev)639{640 struct drm_plane *plane;641 642 drm_for_each_plane(plane, dev) {643 if (plane->funcs->early_unregister)644 plane->funcs->early_unregister(plane);645 }646}647 648/**649 * drm_plane_cleanup - Clean up the core plane usage650 * @plane: plane to cleanup651 *652 * This function cleans up @plane and removes it from the DRM mode setting653 * core. Note that the function does *not* free the plane structure itself,654 * this is the responsibility of the caller.655 */656void drm_plane_cleanup(struct drm_plane *plane)657{658 struct drm_device *dev = plane->dev;659 660 drm_modeset_lock_fini(&plane->mutex);661 662 kfree(plane->format_types);663 kfree(plane->modifiers);664 drm_mode_object_unregister(dev, &plane->base);665 666 BUG_ON(list_empty(&plane->head));667 668 /* Note that the plane_list is considered to be static; should we669 * remove the drm_plane at runtime we would have to decrement all670 * the indices on the drm_plane after us in the plane_list.671 */672 673 list_del(&plane->head);674 dev->mode_config.num_total_plane--;675 676 WARN_ON(plane->state && !plane->funcs->atomic_destroy_state);677 if (plane->state && plane->funcs->atomic_destroy_state)678 plane->funcs->atomic_destroy_state(plane, plane->state);679 680 kfree(plane->name);681 682 memset(plane, 0, sizeof(*plane));683}684EXPORT_SYMBOL(drm_plane_cleanup);685 686/**687 * drm_plane_from_index - find the registered plane at an index688 * @dev: DRM device689 * @idx: index of registered plane to find for690 *691 * Given a plane index, return the registered plane from DRM device's692 * list of planes with matching index. This is the inverse of drm_plane_index().693 */694struct drm_plane *695drm_plane_from_index(struct drm_device *dev, int idx)696{697 struct drm_plane *plane;698 699 drm_for_each_plane(plane, dev)700 if (idx == plane->index)701 return plane;702 703 return NULL;704}705EXPORT_SYMBOL(drm_plane_from_index);706 707/**708 * drm_plane_force_disable - Forcibly disable a plane709 * @plane: plane to disable710 *711 * Forces the plane to be disabled.712 *713 * Used when the plane's current framebuffer is destroyed,714 * and when restoring fbdev mode.715 *716 * Note that this function is not suitable for atomic drivers, since it doesn't717 * wire through the lock acquisition context properly and hence can't handle718 * retries or driver private locks. You probably want to use719 * drm_atomic_helper_disable_plane() or720 * drm_atomic_helper_disable_planes_on_crtc() instead.721 */722void drm_plane_force_disable(struct drm_plane *plane)723{724 int ret;725 726 if (!plane->fb)727 return;728 729 WARN_ON(drm_drv_uses_atomic_modeset(plane->dev));730 731 plane->old_fb = plane->fb;732 ret = plane->funcs->disable_plane(plane, NULL);733 if (ret) {734 DRM_ERROR("failed to disable plane with busy fb\n");735 plane->old_fb = NULL;736 return;737 }738 /* disconnect the plane from the fb and crtc: */739 drm_framebuffer_put(plane->old_fb);740 plane->old_fb = NULL;741 plane->fb = NULL;742 plane->crtc = NULL;743}744EXPORT_SYMBOL(drm_plane_force_disable);745 746/**747 * drm_mode_plane_set_obj_prop - set the value of a property748 * @plane: drm plane object to set property value for749 * @property: property to set750 * @value: value the property should be set to751 *752 * This functions sets a given property on a given plane object. This function753 * calls the driver's ->set_property callback and changes the software state of754 * the property if the callback succeeds.755 *756 * Returns:757 * Zero on success, error code on failure.758 */759int drm_mode_plane_set_obj_prop(struct drm_plane *plane,760 struct drm_property *property,761 uint64_t value)762{763 int ret = -EINVAL;764 struct drm_mode_object *obj = &plane->base;765 766 if (plane->funcs->set_property)767 ret = plane->funcs->set_property(plane, property, value);768 if (!ret)769 drm_object_property_set_value(obj, property, value);770 771 return ret;772}773EXPORT_SYMBOL(drm_mode_plane_set_obj_prop);774 775int drm_mode_getplane_res(struct drm_device *dev, void *data,776 struct drm_file *file_priv)777{778 struct drm_mode_get_plane_res *plane_resp = data;779 struct drm_plane *plane;780 uint32_t __user *plane_ptr;781 int count = 0;782 783 if (!drm_core_check_feature(dev, DRIVER_MODESET))784 return -EOPNOTSUPP;785 786 plane_ptr = u64_to_user_ptr(plane_resp->plane_id_ptr);787 788 /*789 * This ioctl is called twice, once to determine how much space is790 * needed, and the 2nd time to fill it.791 */792 drm_for_each_plane(plane, dev) {793 /*794 * Unless userspace set the 'universal planes'795 * capability bit, only advertise overlays.796 */797 if (plane->type != DRM_PLANE_TYPE_OVERLAY &&798 !file_priv->universal_planes)799 continue;800 801 /*802 * If we're running on a virtualized driver then,803 * unless userspace advertizes support for the804 * virtualized cursor plane, disable cursor planes805 * because they'll be broken due to missing cursor806 * hotspot info.807 */808 if (plane->type == DRM_PLANE_TYPE_CURSOR &&809 drm_core_check_feature(dev, DRIVER_CURSOR_HOTSPOT) &&810 file_priv->atomic &&811 !file_priv->supports_virtualized_cursor_plane)812 continue;813 814 if (drm_lease_held(file_priv, plane->base.id)) {815 if (count < plane_resp->count_planes &&816 put_user(plane->base.id, plane_ptr + count))817 return -EFAULT;818 count++;819 }820 }821 plane_resp->count_planes = count;822 823 return 0;824}825 826int drm_mode_getplane(struct drm_device *dev, void *data,827 struct drm_file *file_priv)828{829 struct drm_mode_get_plane *plane_resp = data;830 struct drm_plane *plane;831 uint32_t __user *format_ptr;832 833 if (!drm_core_check_feature(dev, DRIVER_MODESET))834 return -EOPNOTSUPP;835 836 plane = drm_plane_find(dev, file_priv, plane_resp->plane_id);837 if (!plane)838 return -ENOENT;839 840 drm_modeset_lock(&plane->mutex, NULL);841 if (plane->state && plane->state->crtc && drm_lease_held(file_priv, plane->state->crtc->base.id))842 plane_resp->crtc_id = plane->state->crtc->base.id;843 else if (!plane->state && plane->crtc && drm_lease_held(file_priv, plane->crtc->base.id))844 plane_resp->crtc_id = plane->crtc->base.id;845 else846 plane_resp->crtc_id = 0;847 848 if (plane->state && plane->state->fb)849 plane_resp->fb_id = plane->state->fb->base.id;850 else if (!plane->state && plane->fb)851 plane_resp->fb_id = plane->fb->base.id;852 else853 plane_resp->fb_id = 0;854 drm_modeset_unlock(&plane->mutex);855 856 plane_resp->plane_id = plane->base.id;857 plane_resp->possible_crtcs = drm_lease_filter_crtcs(file_priv,858 plane->possible_crtcs);859 860 plane_resp->gamma_size = 0;861 862 /*863 * This ioctl is called twice, once to determine how much space is864 * needed, and the 2nd time to fill it.865 */866 if (plane->format_count &&867 (plane_resp->count_format_types >= plane->format_count)) {868 format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr;869 if (copy_to_user(format_ptr,870 plane->format_types,871 sizeof(uint32_t) * plane->format_count)) {872 return -EFAULT;873 }874 }875 plane_resp->count_format_types = plane->format_count;876 877 return 0;878}879 880/**881 * drm_plane_has_format - Check whether the plane supports this format and modifier combination882 * @plane: drm plane883 * @format: pixel format (DRM_FORMAT_*)884 * @modifier: data layout modifier885 *886 * Returns:887 * Whether the plane supports the specified format and modifier combination.888 */889bool drm_plane_has_format(struct drm_plane *plane,890 u32 format, u64 modifier)891{892 unsigned int i;893 894 for (i = 0; i < plane->format_count; i++) {895 if (format == plane->format_types[i])896 break;897 }898 if (i == plane->format_count)899 return false;900 901 if (plane->funcs->format_mod_supported) {902 if (!plane->funcs->format_mod_supported(plane, format, modifier))903 return false;904 } else {905 if (!plane->modifier_count)906 return true;907 908 for (i = 0; i < plane->modifier_count; i++) {909 if (modifier == plane->modifiers[i])910 break;911 }912 if (i == plane->modifier_count)913 return false;914 }915 916 return true;917}918EXPORT_SYMBOL(drm_plane_has_format);919 920static int __setplane_check(struct drm_plane *plane,921 struct drm_crtc *crtc,922 struct drm_framebuffer *fb,923 int32_t crtc_x, int32_t crtc_y,924 uint32_t crtc_w, uint32_t crtc_h,925 uint32_t src_x, uint32_t src_y,926 uint32_t src_w, uint32_t src_h)927{928 int ret;929 930 /* Check whether this plane is usable on this CRTC */931 if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) {932 DRM_DEBUG_KMS("Invalid crtc for plane\n");933 return -EINVAL;934 }935 936 /* Check whether this plane supports the fb pixel format. */937 if (!drm_plane_has_format(plane, fb->format->format, fb->modifier)) {938 DRM_DEBUG_KMS("Invalid pixel format %p4cc, modifier 0x%llx\n",939 &fb->format->format, fb->modifier);940 return -EINVAL;941 }942 943 /* Give drivers some help against integer overflows */944 if (crtc_w > INT_MAX ||945 crtc_x > INT_MAX - (int32_t) crtc_w ||946 crtc_h > INT_MAX ||947 crtc_y > INT_MAX - (int32_t) crtc_h) {948 DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",949 crtc_w, crtc_h, crtc_x, crtc_y);950 return -ERANGE;951 }952 953 ret = drm_framebuffer_check_src_coords(src_x, src_y, src_w, src_h, fb);954 if (ret)955 return ret;956 957 return 0;958}959 960/**961 * drm_any_plane_has_format - Check whether any plane supports this format and modifier combination962 * @dev: DRM device963 * @format: pixel format (DRM_FORMAT_*)964 * @modifier: data layout modifier965 *966 * Returns:967 * Whether at least one plane supports the specified format and modifier combination.968 */969bool drm_any_plane_has_format(struct drm_device *dev,970 u32 format, u64 modifier)971{972 struct drm_plane *plane;973 974 drm_for_each_plane(plane, dev) {975 if (drm_plane_has_format(plane, format, modifier))976 return true;977 }978 979 return false;980}981EXPORT_SYMBOL(drm_any_plane_has_format);982 983/*984 * __setplane_internal - setplane handler for internal callers985 *986 * This function will take a reference on the new fb for the plane987 * on success.988 *989 * src_{x,y,w,h} are provided in 16.16 fixed point format990 */991static int __setplane_internal(struct drm_plane *plane,992 struct drm_crtc *crtc,993 struct drm_framebuffer *fb,994 int32_t crtc_x, int32_t crtc_y,995 uint32_t crtc_w, uint32_t crtc_h,996 /* src_{x,y,w,h} values are 16.16 fixed point */997 uint32_t src_x, uint32_t src_y,998 uint32_t src_w, uint32_t src_h,999 struct drm_modeset_acquire_ctx *ctx)1000{1001 int ret = 0;1002 1003 WARN_ON(drm_drv_uses_atomic_modeset(plane->dev));1004 1005 /* No fb means shut it down */1006 if (!fb) {1007 plane->old_fb = plane->fb;1008 ret = plane->funcs->disable_plane(plane, ctx);1009 if (!ret) {1010 plane->crtc = NULL;1011 plane->fb = NULL;1012 } else {1013 plane->old_fb = NULL;1014 }1015 goto out;1016 }1017 1018 ret = __setplane_check(plane, crtc, fb,1019 crtc_x, crtc_y, crtc_w, crtc_h,1020 src_x, src_y, src_w, src_h);1021 if (ret)1022 goto out;1023 1024 plane->old_fb = plane->fb;1025 ret = plane->funcs->update_plane(plane, crtc, fb,1026 crtc_x, crtc_y, crtc_w, crtc_h,1027 src_x, src_y, src_w, src_h, ctx);1028 if (!ret) {1029 plane->crtc = crtc;1030 plane->fb = fb;1031 drm_framebuffer_get(plane->fb);1032 } else {1033 plane->old_fb = NULL;1034 }1035 1036out:1037 if (plane->old_fb)1038 drm_framebuffer_put(plane->old_fb);1039 plane->old_fb = NULL;1040 1041 return ret;1042}1043 1044static int __setplane_atomic(struct drm_plane *plane,1045 struct drm_crtc *crtc,1046 struct drm_framebuffer *fb,1047 int32_t crtc_x, int32_t crtc_y,1048 uint32_t crtc_w, uint32_t crtc_h,1049 uint32_t src_x, uint32_t src_y,1050 uint32_t src_w, uint32_t src_h,1051 struct drm_modeset_acquire_ctx *ctx)1052{1053 int ret;1054 1055 WARN_ON(!drm_drv_uses_atomic_modeset(plane->dev));1056 1057 /* No fb means shut it down */1058 if (!fb)1059 return plane->funcs->disable_plane(plane, ctx);1060 1061 /*1062 * FIXME: This is redundant with drm_atomic_plane_check(),1063 * but the legacy cursor/"async" .update_plane() tricks1064 * don't call that so we still need this here. Should remove1065 * this when all .update_plane() implementations have been1066 * fixed to call drm_atomic_plane_check().1067 */1068 ret = __setplane_check(plane, crtc, fb,1069 crtc_x, crtc_y, crtc_w, crtc_h,1070 src_x, src_y, src_w, src_h);1071 if (ret)1072 return ret;1073 1074 return plane->funcs->update_plane(plane, crtc, fb,1075 crtc_x, crtc_y, crtc_w, crtc_h,1076 src_x, src_y, src_w, src_h, ctx);1077}1078 1079static int setplane_internal(struct drm_plane *plane,1080 struct drm_crtc *crtc,1081 struct drm_framebuffer *fb,1082 int32_t crtc_x, int32_t crtc_y,1083 uint32_t crtc_w, uint32_t crtc_h,1084 /* src_{x,y,w,h} values are 16.16 fixed point */1085 uint32_t src_x, uint32_t src_y,1086 uint32_t src_w, uint32_t src_h)1087{1088 struct drm_modeset_acquire_ctx ctx;1089 int ret;1090 1091 DRM_MODESET_LOCK_ALL_BEGIN(plane->dev, ctx,1092 DRM_MODESET_ACQUIRE_INTERRUPTIBLE, ret);1093 1094 if (drm_drv_uses_atomic_modeset(plane->dev))1095 ret = __setplane_atomic(plane, crtc, fb,1096 crtc_x, crtc_y, crtc_w, crtc_h,1097 src_x, src_y, src_w, src_h, &ctx);1098 else1099 ret = __setplane_internal(plane, crtc, fb,1100 crtc_x, crtc_y, crtc_w, crtc_h,1101 src_x, src_y, src_w, src_h, &ctx);1102 1103 DRM_MODESET_LOCK_ALL_END(plane->dev, ctx, ret);1104 1105 return ret;1106}1107 1108int drm_mode_setplane(struct drm_device *dev, void *data,1109 struct drm_file *file_priv)1110{1111 struct drm_mode_set_plane *plane_req = data;1112 struct drm_plane *plane;1113 struct drm_crtc *crtc = NULL;1114 struct drm_framebuffer *fb = NULL;1115 int ret;1116 1117 if (!drm_core_check_feature(dev, DRIVER_MODESET))1118 return -EOPNOTSUPP;1119 1120 /*1121 * First, find the plane, crtc, and fb objects. If not available,1122 * we don't bother to call the driver.1123 */1124 plane = drm_plane_find(dev, file_priv, plane_req->plane_id);1125 if (!plane) {1126 DRM_DEBUG_KMS("Unknown plane ID %d\n",1127 plane_req->plane_id);1128 return -ENOENT;1129 }1130 1131 if (plane_req->fb_id) {1132 fb = drm_framebuffer_lookup(dev, file_priv, plane_req->fb_id);1133 if (!fb) {1134 DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",1135 plane_req->fb_id);1136 return -ENOENT;1137 }1138 1139 crtc = drm_crtc_find(dev, file_priv, plane_req->crtc_id);1140 if (!crtc) {1141 drm_framebuffer_put(fb);1142 DRM_DEBUG_KMS("Unknown crtc ID %d\n",1143 plane_req->crtc_id);1144 return -ENOENT;1145 }1146 }1147 1148 ret = setplane_internal(plane, crtc, fb,1149 plane_req->crtc_x, plane_req->crtc_y,1150 plane_req->crtc_w, plane_req->crtc_h,1151 plane_req->src_x, plane_req->src_y,1152 plane_req->src_w, plane_req->src_h);1153 1154 if (fb)1155 drm_framebuffer_put(fb);1156 1157 return ret;1158}1159 1160static int drm_mode_cursor_universal(struct drm_crtc *crtc,1161 struct drm_mode_cursor2 *req,1162 struct drm_file *file_priv,1163 struct drm_modeset_acquire_ctx *ctx)1164{1165 struct drm_device *dev = crtc->dev;1166 struct drm_plane *plane = crtc->cursor;1167 struct drm_framebuffer *fb = NULL;1168 struct drm_mode_fb_cmd2 fbreq = {1169 .width = req->width,1170 .height = req->height,1171 .pixel_format = DRM_FORMAT_ARGB8888,1172 .pitches = { req->width * 4 },1173 .handles = { req->handle },1174 };1175 int32_t crtc_x, crtc_y;1176 uint32_t crtc_w = 0, crtc_h = 0;1177 uint32_t src_w = 0, src_h = 0;1178 int ret = 0;1179 1180 BUG_ON(!plane);1181 WARN_ON(plane->crtc != crtc && plane->crtc != NULL);1182 1183 /*1184 * Obtain fb we'll be using (either new or existing) and take an extra1185 * reference to it if fb != null. setplane will take care of dropping1186 * the reference if the plane update fails.1187 */1188 if (req->flags & DRM_MODE_CURSOR_BO) {1189 if (req->handle) {1190 fb = drm_internal_framebuffer_create(dev, &fbreq, file_priv);1191 if (IS_ERR(fb)) {1192 DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n");1193 return PTR_ERR(fb);1194 }1195 1196 if (plane->hotspot_x_property && plane->state)1197 plane->state->hotspot_x = req->hot_x;1198 if (plane->hotspot_y_property && plane->state)1199 plane->state->hotspot_y = req->hot_y;1200 } else {1201 fb = NULL;1202 }1203 } else {1204 if (plane->state)1205 fb = plane->state->fb;1206 else1207 fb = plane->fb;1208 1209 if (fb)1210 drm_framebuffer_get(fb);1211 }1212 1213 if (req->flags & DRM_MODE_CURSOR_MOVE) {1214 crtc_x = req->x;1215 crtc_y = req->y;1216 } else {1217 crtc_x = crtc->cursor_x;1218 crtc_y = crtc->cursor_y;1219 }1220 1221 if (fb) {1222 crtc_w = fb->width;1223 crtc_h = fb->height;1224 src_w = fb->width << 16;1225 src_h = fb->height << 16;1226 }1227 1228 if (drm_drv_uses_atomic_modeset(dev))1229 ret = __setplane_atomic(plane, crtc, fb,1230 crtc_x, crtc_y, crtc_w, crtc_h,1231 0, 0, src_w, src_h, ctx);1232 else1233 ret = __setplane_internal(plane, crtc, fb,1234 crtc_x, crtc_y, crtc_w, crtc_h,1235 0, 0, src_w, src_h, ctx);1236 1237 if (fb)1238 drm_framebuffer_put(fb);1239 1240 /* Update successful; save new cursor position, if necessary */1241 if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) {1242 crtc->cursor_x = req->x;1243 crtc->cursor_y = req->y;1244 }1245 1246 return ret;1247}1248 1249static int drm_mode_cursor_common(struct drm_device *dev,1250 struct drm_mode_cursor2 *req,1251 struct drm_file *file_priv)1252{1253 struct drm_crtc *crtc;1254 struct drm_modeset_acquire_ctx ctx;1255 int ret = 0;1256 1257 if (!drm_core_check_feature(dev, DRIVER_MODESET))1258 return -EOPNOTSUPP;1259 1260 if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))1261 return -EINVAL;1262 1263 crtc = drm_crtc_find(dev, file_priv, req->crtc_id);1264 if (!crtc) {1265 DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);1266 return -ENOENT;1267 }1268 1269 drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);1270retry:1271 ret = drm_modeset_lock(&crtc->mutex, &ctx);1272 if (ret)1273 goto out;1274 /*1275 * If this crtc has a universal cursor plane, call that plane's update1276 * handler rather than using legacy cursor handlers.1277 */1278 if (crtc->cursor) {1279 ret = drm_modeset_lock(&crtc->cursor->mutex, &ctx);1280 if (ret)1281 goto out;1282 1283 if (!drm_lease_held(file_priv, crtc->cursor->base.id)) {1284 ret = -EACCES;1285 goto out;1286 }1287 1288 ret = drm_mode_cursor_universal(crtc, req, file_priv, &ctx);1289 goto out;1290 }1291 1292 if (req->flags & DRM_MODE_CURSOR_BO) {1293 if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {1294 ret = -ENXIO;1295 goto out;1296 }1297 /* Turns off the cursor if handle is 0 */1298 if (crtc->funcs->cursor_set2)1299 ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle,1300 req->width, req->height, req->hot_x, req->hot_y);1301 else1302 ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle,1303 req->width, req->height);1304 }1305 1306 if (req->flags & DRM_MODE_CURSOR_MOVE) {1307 if (crtc->funcs->cursor_move) {1308 ret = crtc->funcs->cursor_move(crtc, req->x, req->y);1309 } else {1310 ret = -EFAULT;1311 goto out;1312 }1313 }1314out:1315 if (ret == -EDEADLK) {1316 ret = drm_modeset_backoff(&ctx);1317 if (!ret)1318 goto retry;1319 }1320 1321 drm_modeset_drop_locks(&ctx);1322 drm_modeset_acquire_fini(&ctx);1323 1324 return ret;1325 1326}1327 1328 1329int drm_mode_cursor_ioctl(struct drm_device *dev,1330 void *data, struct drm_file *file_priv)1331{1332 struct drm_mode_cursor *req = data;1333 struct drm_mode_cursor2 new_req;1334 1335 memcpy(&new_req, req, sizeof(struct drm_mode_cursor));1336 new_req.hot_x = new_req.hot_y = 0;1337 1338 return drm_mode_cursor_common(dev, &new_req, file_priv);1339}1340 1341/*1342 * Set the cursor configuration based on user request. This implements the 2nd1343 * version of the cursor ioctl, which allows userspace to additionally specify1344 * the hotspot of the pointer.1345 */1346int drm_mode_cursor2_ioctl(struct drm_device *dev,1347 void *data, struct drm_file *file_priv)1348{1349 struct drm_mode_cursor2 *req = data;1350 1351 return drm_mode_cursor_common(dev, req, file_priv);1352}1353 1354int drm_mode_page_flip_ioctl(struct drm_device *dev,1355 void *data, struct drm_file *file_priv)1356{1357 struct drm_mode_crtc_page_flip_target *page_flip = data;1358 struct drm_crtc *crtc;1359 struct drm_plane *plane;1360 struct drm_framebuffer *fb = NULL, *old_fb;1361 struct drm_pending_vblank_event *e = NULL;1362 u32 target_vblank = page_flip->sequence;1363 struct drm_modeset_acquire_ctx ctx;1364 int ret = -EINVAL;1365 1366 if (!drm_core_check_feature(dev, DRIVER_MODESET))1367 return -EOPNOTSUPP;1368 1369 if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS)1370 return -EINVAL;1371 1372 if (page_flip->sequence != 0 && !(page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET))1373 return -EINVAL;1374 1375 /* Only one of the DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags1376 * can be specified1377 */1378 if ((page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) == DRM_MODE_PAGE_FLIP_TARGET)1379 return -EINVAL;1380 1381 if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip)1382 return -EINVAL;1383 1384 crtc = drm_crtc_find(dev, file_priv, page_flip->crtc_id);1385 if (!crtc)1386 return -ENOENT;1387 1388 plane = crtc->primary;1389 1390 if (!drm_lease_held(file_priv, plane->base.id))1391 return -EACCES;1392 1393 if (crtc->funcs->page_flip_target) {1394 u32 current_vblank;1395 int r;1396 1397 r = drm_crtc_vblank_get(crtc);1398 if (r)1399 return r;1400 1401 current_vblank = (u32)drm_crtc_vblank_count(crtc);1402 1403 switch (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) {1404 case DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE:1405 if ((int)(target_vblank - current_vblank) > 1) {1406 DRM_DEBUG("Invalid absolute flip target %u, "1407 "must be <= %u\n", target_vblank,1408 current_vblank + 1);1409 drm_crtc_vblank_put(crtc);1410 return -EINVAL;1411 }1412 break;1413 case DRM_MODE_PAGE_FLIP_TARGET_RELATIVE:1414 if (target_vblank != 0 && target_vblank != 1) {1415 DRM_DEBUG("Invalid relative flip target %u, "1416 "must be 0 or 1\n", target_vblank);1417 drm_crtc_vblank_put(crtc);1418 return -EINVAL;1419 }1420 target_vblank += current_vblank;1421 break;1422 default:1423 target_vblank = current_vblank +1424 !(page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC);1425 break;1426 }1427 } else if (crtc->funcs->page_flip == NULL ||1428 (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET)) {1429 return -EINVAL;1430 }1431 1432 drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);1433retry:1434 ret = drm_modeset_lock(&crtc->mutex, &ctx);1435 if (ret)1436 goto out;1437 ret = drm_modeset_lock(&plane->mutex, &ctx);1438 if (ret)1439 goto out;1440 1441 if (plane->state)1442 old_fb = plane->state->fb;1443 else1444 old_fb = plane->fb;1445 1446 if (old_fb == NULL) {1447 /* The framebuffer is currently unbound, presumably1448 * due to a hotplug event, that userspace has not1449 * yet discovered.1450 */1451 ret = -EBUSY;1452 goto out;1453 }1454 1455 fb = drm_framebuffer_lookup(dev, file_priv, page_flip->fb_id);1456 if (!fb) {1457 ret = -ENOENT;1458 goto out;1459 }1460 1461 if (plane->state) {1462 const struct drm_plane_state *state = plane->state;1463 1464 ret = drm_framebuffer_check_src_coords(state->src_x,1465 state->src_y,1466 state->src_w,1467 state->src_h,1468 fb);1469 } else {1470 ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y,1471 &crtc->mode, fb);1472 }1473 if (ret)1474 goto out;1475 1476 /*1477 * Only check the FOURCC format code, excluding modifiers. This is1478 * enough for all legacy drivers. Atomic drivers have their own1479 * checks in their ->atomic_check implementation, which will1480 * return -EINVAL if any hw or driver constraint is violated due1481 * to modifier changes.1482 */1483 if (old_fb->format->format != fb->format->format) {1484 DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");1485 ret = -EINVAL;1486 goto out;1487 }1488 1489 if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {1490 e = kzalloc(sizeof *e, GFP_KERNEL);1491 if (!e) {1492 ret = -ENOMEM;1493 goto out;1494 }1495 1496 e->event.base.type = DRM_EVENT_FLIP_COMPLETE;1497 e->event.base.length = sizeof(e->event);1498 e->event.vbl.user_data = page_flip->user_data;1499 e->event.vbl.crtc_id = crtc->base.id;1500 1501 ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base);1502 if (ret) {1503 kfree(e);1504 e = NULL;1505 goto out;1506 }1507 }1508 1509 plane->old_fb = plane->fb;1510 if (crtc->funcs->page_flip_target)1511 ret = crtc->funcs->page_flip_target(crtc, fb, e,1512 page_flip->flags,1513 target_vblank,1514 &ctx);1515 else1516 ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags,1517 &ctx);1518 if (ret) {1519 if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT)1520 drm_event_cancel_free(dev, &e->base);1521 /* Keep the old fb, don't unref it. */1522 plane->old_fb = NULL;1523 } else {1524 if (!plane->state) {1525 plane->fb = fb;1526 drm_framebuffer_get(fb);1527 }1528 }1529 1530out:1531 if (fb)1532 drm_framebuffer_put(fb);1533 fb = NULL;1534 if (plane->old_fb)1535 drm_framebuffer_put(plane->old_fb);1536 plane->old_fb = NULL;1537 1538 if (ret == -EDEADLK) {1539 ret = drm_modeset_backoff(&ctx);1540 if (!ret)1541 goto retry;1542 }1543 1544 drm_modeset_drop_locks(&ctx);1545 drm_modeset_acquire_fini(&ctx);1546 1547 if (ret && crtc->funcs->page_flip_target)1548 drm_crtc_vblank_put(crtc);1549 1550 return ret;1551}1552 1553/**1554 * DOC: damage tracking1555 *1556 * FB_DAMAGE_CLIPS is an optional plane property which provides a means to1557 * specify a list of damage rectangles on a plane in framebuffer coordinates of1558 * the framebuffer attached to the plane. In current context damage is the area1559 * of plane framebuffer that has changed since last plane update (also called1560 * page-flip), irrespective of whether currently attached framebuffer is same as1561 * framebuffer attached during last plane update or not.1562 *1563 * FB_DAMAGE_CLIPS is a hint to kernel which could be helpful for some drivers1564 * to optimize internally especially for virtual devices where each framebuffer1565 * change needs to be transmitted over network, usb, etc.1566 *1567 * Since FB_DAMAGE_CLIPS is a hint so it is an optional property. User-space can1568 * ignore damage clips property and in that case driver will do a full plane1569 * update. In case damage clips are provided then it is guaranteed that the area1570 * inside damage clips will be updated to plane. For efficiency driver can do1571 * full update or can update more than specified in damage clips. Since driver1572 * is free to read more, user-space must always render the entire visible1573 * framebuffer. Otherwise there can be corruptions. Also, if a user-space1574 * provides damage clips which doesn't encompass the actual damage to1575 * framebuffer (since last plane update) can result in incorrect rendering.1576 *1577 * FB_DAMAGE_CLIPS is a blob property with the layout of blob data is simply an1578 * array of &drm_mode_rect. Unlike plane &drm_plane_state.src coordinates,1579 * damage clips are not in 16.16 fixed point. Similar to plane src in1580 * framebuffer, damage clips cannot be negative. In damage clip, x1/y1 are1581 * inclusive and x2/y2 are exclusive. While kernel does not error for overlapped1582 * damage clips, it is strongly discouraged.1583 *1584 * Drivers that are interested in damage interface for plane should enable1585 * FB_DAMAGE_CLIPS property by calling drm_plane_enable_fb_damage_clips().1586 * Drivers implementing damage can use drm_atomic_helper_damage_iter_init() and1587 * drm_atomic_helper_damage_iter_next() helper iterator function to get damage1588 * rectangles clipped to &drm_plane_state.src.1589 *1590 * Note that there are two types of damage handling: frame damage and buffer1591 * damage, the type of damage handling implemented depends on a driver's upload1592 * target. Drivers implementing a per-plane or per-CRTC upload target need to1593 * handle frame damage, while drivers implementing a per-buffer upload target1594 * need to handle buffer damage.1595 *1596 * The existing damage helpers only support the frame damage type, there is no1597 * buffer age support or similar damage accumulation algorithm implemented yet.1598 *1599 * Only drivers handling frame damage can use the mentioned damage helpers to1600 * iterate over the damaged regions. Drivers that handle buffer damage, must set1601 * &drm_plane_state.ignore_damage_clips for drm_atomic_helper_damage_iter_init()1602 * to know that damage clips should be ignored and return &drm_plane_state.src1603 * as the damage rectangle, to force a full plane update.1604 *1605 * Drivers with a per-buffer upload target could compare the &drm_plane_state.fb1606 * of the old and new plane states to determine if the framebuffer attached to a1607 * plane has changed or not since the last plane update. If &drm_plane_state.fb1608 * has changed, then &drm_plane_state.ignore_damage_clips must be set to true.1609 *1610 * That is because drivers with a per-plane upload target, expect the backing1611 * storage buffer to not change for a given plane. If the upload buffer changes1612 * between page flips, the new upload buffer has to be updated as a whole. This1613 * can be improved in the future if support for frame damage is added to the DRM1614 * damage helpers, similarly to how user-space already handle this case as it is1615 * explained in the following documents:1616 *1617 * https://registry.khronos.org/EGL/extensions/KHR/EGL_KHR_swap_buffers_with_damage.txt1618 * https://emersion.fr/blog/2019/intro-to-damage-tracking/1619 */1620 1621/**1622 * drm_plane_enable_fb_damage_clips - Enables plane fb damage clips property.1623 * @plane: Plane on which to enable damage clips property.1624 *1625 * This function lets driver to enable the damage clips property on a plane.1626 */1627void drm_plane_enable_fb_damage_clips(struct drm_plane *plane)1628{1629 struct drm_device *dev = plane->dev;1630 struct drm_mode_config *config = &dev->mode_config;1631 1632 drm_object_attach_property(&plane->base, config->prop_fb_damage_clips,1633 0);1634}1635EXPORT_SYMBOL(drm_plane_enable_fb_damage_clips);1636 1637/**1638 * drm_plane_get_damage_clips_count - Returns damage clips count.1639 * @state: Plane state.1640 *1641 * Simple helper to get the number of &drm_mode_rect clips set by user-space1642 * during plane update.1643 *1644 * Return: Number of clips in plane fb_damage_clips blob property.1645 */1646unsigned int1647drm_plane_get_damage_clips_count(const struct drm_plane_state *state)1648{1649 return (state && state->fb_damage_clips) ?1650 state->fb_damage_clips->length/sizeof(struct drm_mode_rect) : 0;1651}1652EXPORT_SYMBOL(drm_plane_get_damage_clips_count);1653 1654struct drm_mode_rect *1655__drm_plane_get_damage_clips(const struct drm_plane_state *state)1656{1657 return (struct drm_mode_rect *)((state && state->fb_damage_clips) ?1658 state->fb_damage_clips->data : NULL);1659}1660 1661/**1662 * drm_plane_get_damage_clips - Returns damage clips.1663 * @state: Plane state.1664 *1665 * Note that this function returns uapi type &drm_mode_rect. Drivers might want1666 * to use the helper functions drm_atomic_helper_damage_iter_init() and1667 * drm_atomic_helper_damage_iter_next() or drm_atomic_helper_damage_merged() if1668 * the driver can only handle a single damage region at most.1669 *1670 * Return: Damage clips in plane fb_damage_clips blob property.1671 */1672struct drm_mode_rect *1673drm_plane_get_damage_clips(const struct drm_plane_state *state)1674{1675 struct drm_device *dev = state->plane->dev;1676 struct drm_mode_config *config = &dev->mode_config;1677 1678 /* check that drm_plane_enable_fb_damage_clips() was called */1679 if (!drm_mode_obj_find_prop_id(&state->plane->base,1680 config->prop_fb_damage_clips->base.id))1681 drm_warn_once(dev, "drm_plane_enable_fb_damage_clips() not called\n");1682 1683 return __drm_plane_get_damage_clips(state);1684}1685EXPORT_SYMBOL(drm_plane_get_damage_clips);1686 1687struct drm_property *1688drm_create_scaling_filter_prop(struct drm_device *dev,1689 unsigned int supported_filters)1690{1691 struct drm_property *prop;1692 static const struct drm_prop_enum_list props[] = {1693 { DRM_SCALING_FILTER_DEFAULT, "Default" },1694 { DRM_SCALING_FILTER_NEAREST_NEIGHBOR, "Nearest Neighbor" },1695 };1696 unsigned int valid_mode_mask = BIT(DRM_SCALING_FILTER_DEFAULT) |1697 BIT(DRM_SCALING_FILTER_NEAREST_NEIGHBOR);1698 int i;1699 1700 if (WARN_ON((supported_filters & ~valid_mode_mask) ||1701 ((supported_filters & BIT(DRM_SCALING_FILTER_DEFAULT)) == 0)))1702 return ERR_PTR(-EINVAL);1703 1704 prop = drm_property_create(dev, DRM_MODE_PROP_ENUM,1705 "SCALING_FILTER",1706 hweight32(supported_filters));1707 if (!prop)1708 return ERR_PTR(-ENOMEM);1709 1710 for (i = 0; i < ARRAY_SIZE(props); i++) {1711 int ret;1712 1713 if (!(BIT(props[i].type) & supported_filters))1714 continue;1715 1716 ret = drm_property_add_enum(prop, props[i].type,1717 props[i].name);1718 1719 if (ret) {1720 drm_property_destroy(dev, prop);1721 1722 return ERR_PTR(ret);1723 }1724 }1725 1726 return prop;1727}1728 1729/**1730 * drm_plane_create_scaling_filter_property - create a new scaling filter1731 * property1732 *1733 * @plane: drm plane1734 * @supported_filters: bitmask of supported scaling filters, must include1735 * BIT(DRM_SCALING_FILTER_DEFAULT).1736 *1737 * This function lets driver to enable the scaling filter property on a given1738 * plane.1739 *1740 * RETURNS:1741 * Zero for success or -errno1742 */1743int drm_plane_create_scaling_filter_property(struct drm_plane *plane,1744 unsigned int supported_filters)1745{1746 struct drm_property *prop =1747 drm_create_scaling_filter_prop(plane->dev, supported_filters);1748 1749 if (IS_ERR(prop))1750 return PTR_ERR(prop);1751 1752 drm_object_attach_property(&plane->base, prop,1753 DRM_SCALING_FILTER_DEFAULT);1754 plane->scaling_filter_property = prop;1755 1756 return 0;1757}1758EXPORT_SYMBOL(drm_plane_create_scaling_filter_property);1759 1760/**1761 * drm_plane_add_size_hints_property - create a size hints property1762 *1763 * @plane: drm plane1764 * @hints: size hints1765 * @num_hints: number of size hints1766 *1767 * Create a size hints property for the plane.1768 *1769 * RETURNS:1770 * Zero for success or -errno1771 */1772int drm_plane_add_size_hints_property(struct drm_plane *plane,1773 const struct drm_plane_size_hint *hints,1774 int num_hints)1775{1776 struct drm_device *dev = plane->dev;1777 struct drm_mode_config *config = &dev->mode_config;1778 struct drm_property_blob *blob;1779 1780 /* extending to other plane types needs actual thought */1781 if (drm_WARN_ON(dev, plane->type != DRM_PLANE_TYPE_CURSOR))1782 return -EINVAL;1783 1784 blob = drm_property_create_blob(dev,1785 array_size(sizeof(hints[0]), num_hints),1786 hints);1787 if (IS_ERR(blob))1788 return PTR_ERR(blob);1789 1790 drm_object_attach_property(&plane->base, config->size_hints_property,1791 blob->base.id);1792 1793 return 0;1794}1795EXPORT_SYMBOL(drm_plane_add_size_hints_property);1796