diff options
Diffstat (limited to 'drivers/gpu/drm/drm_plane.c')
| -rw-r--r-- | drivers/gpu/drm/drm_plane.c | 899 |
1 files changed, 784 insertions, 115 deletions
diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index 5f650d8fc66b..ce76c55913f7 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -20,6 +20,7 @@ * OF THIS SOFTWARE. */ +#include <linux/export.h> #include <linux/slab.h> #include <linux/uaccess.h> @@ -30,6 +31,7 @@ #include <drm/drm_file.h> #include <drm/drm_crtc.h> #include <drm/drm_fourcc.h> +#include <drm/drm_managed.h> #include <drm/drm_vblank.h> #include "drm_crtc_internal.h" @@ -37,26 +39,135 @@ /** * DOC: overview * - * A plane represents an image source that can be blended with or overlayed on + * A plane represents an image source that can be blended with or overlaid on * top of a CRTC during the scanout process. Planes take their input data from a * &drm_framebuffer object. The plane itself specifies the cropping and scaling - * of that image, and where it is placed on the visible are of a display + * of that image, and where it is placed on the visible area of a display * pipeline, represented by &drm_crtc. A plane can also have additional * properties that specify how the pixels are positioned and blended, like * rotation or Z-position. All these properties are stored in &drm_plane_state. * + * Unless explicitly specified (via CRTC property or otherwise), the active area + * of a CRTC will be black by default. This means portions of the active area + * which are not covered by a plane will be black, and alpha blending of any + * planes with the CRTC background will blend with black at the lowest zpos. + * * To create a plane, a KMS drivers allocates and zeroes an instances of * &struct drm_plane (possibly as part of a larger structure) and registers it * with a call to drm_universal_plane_init(). * - * Cursor and overlay planes are optional. All drivers should provide one - * primary plane per CRTC to avoid surprising userspace too much. See enum - * drm_plane_type for a more in-depth discussion of these special uapi-relevant - * plane types. Special planes are associated with their CRTC by calling - * drm_crtc_init_with_planes(). + * Each plane has a type, see enum drm_plane_type. A plane can be compatible + * with multiple CRTCs, see &drm_plane.possible_crtcs. + * + * Each CRTC must have a unique primary plane userspace can attach to enable + * the CRTC. In other words, userspace must be able to attach a different + * primary plane to each CRTC at the same time. Primary planes can still be + * compatible with multiple CRTCs. There must be exactly as many primary planes + * as there are CRTCs. * - * The type of a plane is exposed in the immutable "type" enumeration property, - * which has one of the following values: "Overlay", "Primary", "Cursor". + * Legacy uAPI doesn't expose the primary and cursor planes directly. DRM core + * relies on the driver to set the primary and optionally the cursor plane used + * for legacy IOCTLs. This is done by calling drm_crtc_init_with_planes(). All + * drivers must provide one primary plane per CRTC to avoid surprising legacy + * userspace too much. + */ + +/** + * DOC: standard plane properties + * + * DRM planes have a few standardized properties: + * + * type: + * Immutable property describing the type of the plane. + * + * For user-space which has enabled the &DRM_CLIENT_CAP_ATOMIC capability, + * the plane type is just a hint and is mostly superseded by atomic + * test-only commits. The type hint can still be used to come up more + * easily with a plane configuration accepted by the driver. + * + * The value of this property can be one of the following: + * + * "Primary": + * To light up a CRTC, attaching a primary plane is the most likely to + * work if it covers the whole CRTC and doesn't have scaling or + * cropping set up. + * + * Drivers may support more features for the primary plane, user-space + * can find out with test-only atomic commits. + * + * Some primary planes are implicitly used by the kernel in the legacy + * IOCTLs &DRM_IOCTL_MODE_SETCRTC and &DRM_IOCTL_MODE_PAGE_FLIP. + * Therefore user-space must not mix explicit usage of any primary + * plane (e.g. through an atomic commit) with these legacy IOCTLs. + * + * "Cursor": + * To enable this plane, using a framebuffer configured without scaling + * or cropping and with the following properties is the most likely to + * work: + * + * - If the driver provides the capabilities &DRM_CAP_CURSOR_WIDTH and + * &DRM_CAP_CURSOR_HEIGHT, create the framebuffer with this size. + * Otherwise, create a framebuffer with the size 64x64. + * - If the driver doesn't support modifiers, create a framebuffer with + * a linear layout. Otherwise, use the IN_FORMATS plane property. + * + * Drivers may support more features for the cursor plane, user-space + * can find out with test-only atomic commits. + * + * Some cursor planes are implicitly used by the kernel in the legacy + * IOCTLs &DRM_IOCTL_MODE_CURSOR and &DRM_IOCTL_MODE_CURSOR2. + * Therefore user-space must not mix explicit usage of any cursor + * plane (e.g. through an atomic commit) with these legacy IOCTLs. + * + * Some drivers may support cursors even if no cursor plane is exposed. + * In this case, the legacy cursor IOCTLs can be used to configure the + * cursor. + * + * "Overlay": + * Neither primary nor cursor. + * + * Overlay planes are the only planes exposed when the + * &DRM_CLIENT_CAP_UNIVERSAL_PLANES capability is disabled. + * + * IN_FORMATS: + * Blob property which contains the set of buffer format and modifier + * pairs supported by this plane. The blob is a struct + * drm_format_modifier_blob. Without this property the plane doesn't + * support buffers with modifiers. Userspace cannot change this property. + * + * Note that userspace can check the &DRM_CAP_ADDFB2_MODIFIERS driver + * capability for general modifier support. If this flag is set then every + * plane will have the IN_FORMATS property, even when it only supports + * DRM_FORMAT_MOD_LINEAR. Before linux kernel release v5.1 there have been + * various bugs in this area with inconsistencies between the capability + * flag and per-plane properties. + * + * IN_FORMATS_ASYNC: + * Blob property which contains the set of buffer format and modifier + * pairs supported by this plane for asynchronous flips. The blob is a struct + * drm_format_modifier_blob. Userspace cannot change this property. This is an + * optional property and if not present then user should expect a failure in + * atomic ioctl when the modifier/format is not supported by that plane under + * asynchronous flip. + * + * SIZE_HINTS: + * Blob property which contains the set of recommended plane size + * which can used for simple "cursor like" use cases (eg. no scaling). + * Using these hints frees userspace from extensive probing of + * supported plane sizes through atomic/setcursor ioctls. + * + * The blob contains an array of struct drm_plane_size_hint, in + * order of preference. For optimal usage userspace should pick + * the first size that satisfies its own requirements. + * + * Drivers should only attach this property to planes that + * support a very limited set of sizes. + * + * Note that property value 0 (ie. no blob) is reserved for potential + * future use. Current userspace is expected to ignore the property + * if the value is 0, and fall back to some other means (eg. + * &DRM_CAP_CURSOR_WIDTH and &DRM_CAP_CURSOR_HEIGHT) to determine + * the appropriate plane size to use. */ static unsigned int drm_num_planes(struct drm_device *dev) @@ -83,9 +194,13 @@ modifiers_ptr(struct drm_format_modifier_blob *blob) return (struct drm_format_modifier *)(((char *)blob) + blob->modifiers_offset); } -static int create_in_format_blob(struct drm_device *dev, struct drm_plane *plane) +static struct drm_property_blob *create_in_format_blob(struct drm_device *dev, + struct drm_plane *plane, + bool (*format_mod_supported) + (struct drm_plane *plane, + u32 format, + u64 modifier)) { - const struct drm_mode_config *config = &dev->mode_config; struct drm_property_blob *blob; struct drm_format_modifier *mod; size_t blob_size, formats_size, modifiers_size; @@ -95,7 +210,7 @@ static int create_in_format_blob(struct drm_device *dev, struct drm_plane *plane formats_size = sizeof(__u32) * plane->format_count; if (WARN_ON(!formats_size)) { /* 0 formats are never expected */ - return 0; + return ERR_PTR(-EINVAL); } modifiers_size = @@ -111,7 +226,7 @@ static int create_in_format_blob(struct drm_device *dev, struct drm_plane *plane blob = drm_property_create_blob(dev, blob_size, NULL); if (IS_ERR(blob)) - return -1; + return blob; blob_data = blob->data; blob_data->version = FORMAT_BLOB_CURRENT; @@ -124,17 +239,13 @@ static int create_in_format_blob(struct drm_device *dev, struct drm_plane *plane memcpy(formats_ptr(blob_data), plane->format_types, formats_size); - /* If we can't determine support, just bail */ - if (!plane->funcs->format_mod_supported) - goto done; - mod = modifiers_ptr(blob_data); for (i = 0; i < plane->modifier_count; i++) { for (j = 0; j < plane->format_count; j++) { - if (plane->funcs->format_mod_supported(plane, - plane->format_types[j], - plane->modifiers[i])) { - + if (!format_mod_supported || + format_mod_supported(plane, + plane->format_types[j], + plane->modifiers[i])) { mod->formats |= 1ULL << j; } } @@ -145,40 +256,122 @@ static int create_in_format_blob(struct drm_device *dev, struct drm_plane *plane mod++; } -done: - drm_object_attach_property(&plane->base, config->modifiers_property, - blob->base.id); - - return 0; + return blob; } /** - * drm_universal_plane_init - Initialize a new universal plane object - * @dev: DRM device - * @plane: plane object to init - * @possible_crtcs: bitmask of possible CRTCs - * @funcs: callbacks for the new plane - * @formats: array of supported formats (DRM_FORMAT\_\*) - * @format_count: number of elements in @formats - * @format_modifiers: array of struct drm_format modifiers terminated by - * DRM_FORMAT_MOD_INVALID - * @type: type of plane (overlay, primary, cursor) - * @name: printf style format string for the plane name, or NULL for default name + * DOC: hotspot properties * - * Initializes a plane object of type @type. + * HOTSPOT_X: property to set mouse hotspot x offset. + * HOTSPOT_Y: property to set mouse hotspot y offset. * - * Returns: - * Zero on success, error code on failure. + * When the plane is being used as a cursor image to display a mouse pointer, + * the "hotspot" is the offset within the cursor image where mouse events + * are expected to go. + * + * Positive values move the hotspot from the top-left corner of the cursor + * plane towards the right and bottom. + * + * Most display drivers do not need this information because the + * hotspot is not actually connected to anything visible on screen. + * However, this is necessary for display drivers like the para-virtualized + * drivers (eg qxl, vbox, virtio, vmwgfx), that are attached to a user console + * with a mouse pointer. Since these consoles are often being remoted over a + * network, they would otherwise have to wait to display the pointer movement to + * the user until a full network round-trip has occurred. New mouse events have + * to be sent from the user's console, over the network to the virtual input + * devices, forwarded to the desktop for processing, and then the cursor plane's + * position can be updated and sent back to the user's console over the network. + * Instead, with the hotspot information, the console can anticipate the new + * location, and draw the mouse cursor there before the confirmation comes in. + * To do that correctly, the user's console must be able predict how the + * desktop will process mouse events, which normally requires the desktop's + * mouse topology information, ie where each CRTC sits in the mouse coordinate + * space. This is typically sent to the para-virtualized drivers using some + * driver-specific method, and the driver then forwards it to the console by + * way of the virtual display device or hypervisor. + * + * The assumption is generally made that there is only one cursor plane being + * used this way at a time, and that the desktop is feeding all mouse devices + * into the same global pointer. Para-virtualized drivers that require this + * should only be exposing a single cursor plane, or find some other way + * to coordinate with a userspace desktop that supports multiple pointers. + * If the hotspot properties are set, the cursor plane is therefore assumed to be + * used only for displaying a mouse cursor image, and the position of the combined + * cursor plane + offset can therefore be used for coordinating with input from a + * mouse device. + * + * The cursor will then be drawn either at the location of the plane in the CRTC + * console, or as a free-floating cursor plane on the user's console + * corresponding to their desktop mouse position. + * + * DRM clients which would like to work correctly on drivers which expose + * hotspot properties should advertise DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT. + * Setting this property on drivers which do not special case + * cursor planes will return EOPNOTSUPP, which can be used by userspace to + * gauge requirements of the hardware/drivers they're running on. Advertising + * DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT implies that the userspace client will be + * correctly setting the hotspot properties. */ -int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, - uint32_t possible_crtcs, - const struct drm_plane_funcs *funcs, - const uint32_t *formats, unsigned int format_count, - const uint64_t *format_modifiers, - enum drm_plane_type type, - const char *name, ...) + +/** + * drm_plane_create_hotspot_properties - creates the mouse hotspot + * properties and attaches them to the given cursor plane + * + * @plane: drm cursor plane + * + * This function enables the mouse hotspot property on a given + * cursor plane. Look at the documentation for hotspot properties + * to get a better understanding for what they're used for. + * + * RETURNS: + * Zero for success or -errno + */ +static int drm_plane_create_hotspot_properties(struct drm_plane *plane) +{ + struct drm_property *prop_x; + struct drm_property *prop_y; + + drm_WARN_ON(plane->dev, + !drm_core_check_feature(plane->dev, + DRIVER_CURSOR_HOTSPOT)); + + prop_x = drm_property_create_signed_range(plane->dev, 0, "HOTSPOT_X", + INT_MIN, INT_MAX); + if (IS_ERR(prop_x)) + return PTR_ERR(prop_x); + + prop_y = drm_property_create_signed_range(plane->dev, 0, "HOTSPOT_Y", + INT_MIN, INT_MAX); + if (IS_ERR(prop_y)) { + drm_property_destroy(plane->dev, prop_x); + return PTR_ERR(prop_y); + } + + drm_object_attach_property(&plane->base, prop_x, 0); + drm_object_attach_property(&plane->base, prop_y, 0); + plane->hotspot_x_property = prop_x; + plane->hotspot_y_property = prop_y; + + return 0; +} + +__printf(9, 0) +static int __drm_universal_plane_init(struct drm_device *dev, + struct drm_plane *plane, + uint32_t possible_crtcs, + const struct drm_plane_funcs *funcs, + const uint32_t *formats, + unsigned int format_count, + const uint64_t *format_modifiers, + enum drm_plane_type type, + const char *name, va_list ap) { struct drm_mode_config *config = &dev->mode_config; + struct drm_property_blob *blob; + static const uint64_t default_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + }; unsigned int format_modifier_count = 0; int ret; @@ -186,6 +379,13 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, if (WARN_ON(config->num_total_plane >= 32)) return -EINVAL; + /* + * First driver to need more than 64 formats needs to fix this. Each + * format is encoded as a bit and the current code only supports a u64. + */ + if (WARN_ON(format_count > 64)) + return -EINVAL; + WARN_ON(drm_drv_uses_atomic_modeset(dev) && (!funcs->atomic_destroy_state || !funcs->atomic_duplicate_state)); @@ -207,22 +407,25 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, return -ENOMEM; } - /* - * First driver to need more than 64 formats needs to fix this. Each - * format is encoded as a bit and the current code only supports a u64. - */ - if (WARN_ON(format_count > 64)) - return -EINVAL; - if (format_modifiers) { const uint64_t *temp_modifiers = format_modifiers; + while (*temp_modifiers++ != DRM_FORMAT_MOD_INVALID) format_modifier_count++; + } else { + if (!dev->mode_config.fb_modifiers_not_supported) { + format_modifiers = default_modifiers; + format_modifier_count = ARRAY_SIZE(default_modifiers); + } } + /* autoset the cap and check for consistency across all planes */ + drm_WARN_ON(dev, config->fb_modifiers_not_supported && + format_modifier_count); + plane->modifier_count = format_modifier_count; plane->modifiers = kmalloc_array(format_modifier_count, - sizeof(format_modifiers[0]), + sizeof(*plane->modifiers), GFP_KERNEL); if (format_modifier_count && !plane->modifiers) { @@ -233,11 +436,7 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, } if (name) { - va_list ap; - - va_start(ap, name); plane->name = kvasprintf(GFP_KERNEL, name, ap); - va_end(ap); } else { plane->name = kasprintf(GFP_KERNEL, "plane-%d", drm_num_planes(dev)); @@ -276,16 +475,173 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, drm_object_attach_property(&plane->base, config->prop_src_w, 0); drm_object_attach_property(&plane->base, config->prop_src_h, 0); } + if (drm_core_check_feature(dev, DRIVER_CURSOR_HOTSPOT) && + type == DRM_PLANE_TYPE_CURSOR) { + drm_plane_create_hotspot_properties(plane); + } + + if (format_modifier_count) { + blob = create_in_format_blob(dev, plane, + plane->funcs->format_mod_supported); + if (!IS_ERR(blob)) + drm_object_attach_property(&plane->base, + config->modifiers_property, + blob->base.id); + } + + if (plane->funcs->format_mod_supported_async) { + blob = create_in_format_blob(dev, plane, + plane->funcs->format_mod_supported_async); + if (!IS_ERR(blob)) + drm_object_attach_property(&plane->base, + config->async_modifiers_property, + blob->base.id); + } - if (config->allow_fb_modifiers) - create_in_format_blob(dev, plane); return 0; } + +/** + * drm_universal_plane_init - Initialize a new universal plane object + * @dev: DRM device + * @plane: plane object to init + * @possible_crtcs: bitmask of possible CRTCs + * @funcs: callbacks for the new plane + * @formats: array of supported formats (DRM_FORMAT\_\*) + * @format_count: number of elements in @formats + * @format_modifiers: array of struct drm_format modifiers terminated by + * DRM_FORMAT_MOD_INVALID + * @type: type of plane (overlay, primary, cursor) + * @name: printf style format string for the plane name, or NULL for default name + * + * Initializes a plane object of type @type. The &drm_plane_funcs.destroy hook + * should call drm_plane_cleanup() and kfree() the plane structure. The plane + * structure should not be allocated with devm_kzalloc(). + * + * Note: consider using drmm_universal_plane_alloc() instead of + * drm_universal_plane_init() to let the DRM managed resource infrastructure + * take care of cleanup and deallocation. + * + * Drivers that only support the DRM_FORMAT_MOD_LINEAR modifier support may set + * @format_modifiers to NULL. The plane will advertise the linear modifier. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, + uint32_t possible_crtcs, + const struct drm_plane_funcs *funcs, + const uint32_t *formats, unsigned int format_count, + const uint64_t *format_modifiers, + enum drm_plane_type type, + const char *name, ...) +{ + va_list ap; + int ret; + + WARN_ON(!funcs->destroy); + + va_start(ap, name); + ret = __drm_universal_plane_init(dev, plane, possible_crtcs, funcs, + formats, format_count, format_modifiers, + type, name, ap); + va_end(ap); + return ret; +} EXPORT_SYMBOL(drm_universal_plane_init); +static void drmm_universal_plane_alloc_release(struct drm_device *dev, void *ptr) +{ + struct drm_plane *plane = ptr; + + if (WARN_ON(!plane->dev)) + return; + + drm_plane_cleanup(plane); +} + +void *__drmm_universal_plane_alloc(struct drm_device *dev, size_t size, + size_t offset, uint32_t possible_crtcs, + const struct drm_plane_funcs *funcs, + const uint32_t *formats, unsigned int format_count, + const uint64_t *format_modifiers, + enum drm_plane_type type, + const char *name, ...) +{ + void *container; + struct drm_plane *plane; + va_list ap; + int ret; + + if (WARN_ON(!funcs || funcs->destroy)) + return ERR_PTR(-EINVAL); + + container = drmm_kzalloc(dev, size, GFP_KERNEL); + if (!container) + return ERR_PTR(-ENOMEM); + + plane = container + offset; + + va_start(ap, name); + ret = __drm_universal_plane_init(dev, plane, possible_crtcs, funcs, + formats, format_count, format_modifiers, + type, name, ap); + va_end(ap); + if (ret) + return ERR_PTR(ret); + + ret = drmm_add_action_or_reset(dev, drmm_universal_plane_alloc_release, + plane); + if (ret) + return ERR_PTR(ret); + + return container; +} +EXPORT_SYMBOL(__drmm_universal_plane_alloc); + +void *__drm_universal_plane_alloc(struct drm_device *dev, size_t size, + size_t offset, uint32_t possible_crtcs, + const struct drm_plane_funcs *funcs, + const uint32_t *formats, unsigned int format_count, + const uint64_t *format_modifiers, + enum drm_plane_type type, + const char *name, ...) +{ + void *container; + struct drm_plane *plane; + va_list ap; + int ret; + + if (drm_WARN_ON(dev, !funcs)) + return ERR_PTR(-EINVAL); + + container = kzalloc(size, GFP_KERNEL); + if (!container) + return ERR_PTR(-ENOMEM); + + plane = container + offset; + + va_start(ap, name); + ret = __drm_universal_plane_init(dev, plane, possible_crtcs, funcs, + formats, format_count, format_modifiers, + type, name, ap); + va_end(ap); + if (ret) + goto err_kfree; + + return container; + +err_kfree: + kfree(container); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(__drm_universal_plane_alloc); + int drm_plane_register_all(struct drm_device *dev) { + unsigned int num_planes = 0; + unsigned int num_zpos = 0; struct drm_plane *plane; int ret = 0; @@ -294,8 +650,15 @@ int drm_plane_register_all(struct drm_device *dev) ret = plane->funcs->late_register(plane); if (ret) return ret; + + if (plane->zpos_property) + num_zpos++; + num_planes++; } + drm_WARN(dev, num_zpos && num_planes != num_zpos, + "Mixing planes with and without zpos property is invalid\n"); + return 0; } @@ -310,38 +673,6 @@ void drm_plane_unregister_all(struct drm_device *dev) } /** - * drm_plane_init - Initialize a legacy plane - * @dev: DRM device - * @plane: plane object to init - * @possible_crtcs: bitmask of possible CRTCs - * @funcs: callbacks for the new plane - * @formats: array of supported formats (DRM_FORMAT\_\*) - * @format_count: number of elements in @formats - * @is_primary: plane type (primary vs overlay) - * - * Legacy API to initialize a DRM plane. - * - * New drivers should call drm_universal_plane_init() instead. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, - uint32_t possible_crtcs, - const struct drm_plane_funcs *funcs, - const uint32_t *formats, unsigned int format_count, - bool is_primary) -{ - enum drm_plane_type type; - - type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; - return drm_universal_plane_init(dev, plane, possible_crtcs, funcs, - formats, format_count, - NULL, type, NULL); -} -EXPORT_SYMBOL(drm_plane_init); - -/** * drm_plane_cleanup - Clean up the core plane usage * @plane: plane to cleanup * @@ -494,6 +825,19 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data, !file_priv->universal_planes) continue; + /* + * If we're running on a virtualized driver then, + * unless userspace advertizes support for the + * virtualized cursor plane, disable cursor planes + * because they'll be broken due to missing cursor + * hotspot info. + */ + if (plane->type == DRM_PLANE_TYPE_CURSOR && + drm_core_check_feature(dev, DRIVER_CURSOR_HOTSPOT) && + file_priv->atomic && + !file_priv->supports_virtualized_cursor_plane) + continue; + if (drm_lease_held(file_priv, plane->base.id)) { if (count < plane_resp->count_planes && put_user(plane->base.id, plane_ptr + count)) @@ -560,8 +904,17 @@ int drm_mode_getplane(struct drm_device *dev, void *data, return 0; } -int drm_plane_check_pixel_format(struct drm_plane *plane, - u32 format, u64 modifier) +/** + * drm_plane_has_format - Check whether the plane supports this format and modifier combination + * @plane: drm plane + * @format: pixel format (DRM_FORMAT_*) + * @modifier: data layout modifier + * + * Returns: + * Whether the plane supports the specified format and modifier combination. + */ +bool drm_plane_has_format(struct drm_plane *plane, + u32 format, u64 modifier) { unsigned int i; @@ -570,25 +923,26 @@ int drm_plane_check_pixel_format(struct drm_plane *plane, break; } if (i == plane->format_count) - return -EINVAL; + return false; if (plane->funcs->format_mod_supported) { if (!plane->funcs->format_mod_supported(plane, format, modifier)) - return -EINVAL; + return false; } else { if (!plane->modifier_count) - return 0; + return true; for (i = 0; i < plane->modifier_count; i++) { if (modifier == plane->modifiers[i]) break; } if (i == plane->modifier_count) - return -EINVAL; + return false; } - return 0; + return true; } +EXPORT_SYMBOL(drm_plane_has_format); static int __setplane_check(struct drm_plane *plane, struct drm_crtc *crtc, @@ -607,16 +961,10 @@ static int __setplane_check(struct drm_plane *plane, } /* Check whether this plane supports the fb pixel format. */ - ret = drm_plane_check_pixel_format(plane, fb->format->format, - fb->modifier); - if (ret) { - struct drm_format_name_buf format_name; - - DRM_DEBUG_KMS("Invalid pixel format %s, modifier 0x%llx\n", - drm_get_format_name(fb->format->format, - &format_name), - fb->modifier); - return ret; + if (!drm_plane_has_format(plane, fb->format->format, fb->modifier)) { + DRM_DEBUG_KMS("Invalid pixel format %p4cc, modifier 0x%llx\n", + &fb->format->format, fb->modifier); + return -EINVAL; } /* Give drivers some help against integer overflows */ @@ -651,7 +999,7 @@ bool drm_any_plane_has_format(struct drm_device *dev, struct drm_plane *plane; drm_for_each_plane(plane, dev) { - if (drm_plane_check_pixel_format(plane, format, modifier) == 0) + if (drm_plane_has_format(plane, format, modifier)) return true; } @@ -779,7 +1127,7 @@ static int setplane_internal(struct drm_plane *plane, crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h, &ctx); - DRM_MODESET_LOCK_ALL_END(ctx, ret); + DRM_MODESET_LOCK_ALL_END(plane->dev, ctx, ret); return ret; } @@ -872,8 +1220,10 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc, return PTR_ERR(fb); } - fb->hot_x = req->hot_x; - fb->hot_y = req->hot_y; + if (plane->hotspot_x_property && plane->state) + plane->state->hotspot_x = req->hot_x; + if (plane->hotspot_y_property && plane->state) + plane->state->hotspot_y = req->hot_y; } else { fb = NULL; } @@ -957,6 +1307,11 @@ retry: if (ret) goto out; + if (!drm_lease_held(file_priv, crtc->cursor->base.id)) { + ret = -EACCES; + goto out; + } + ret = drm_mode_cursor_universal(crtc, req, file_priv, &ctx); goto out; } @@ -1059,6 +1414,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, plane = crtc->primary; + if (!drm_lease_held(file_priv, plane->base.id)) + return -EACCES; + if (crtc->funcs->page_flip_target) { u32 current_vblank; int r; @@ -1142,7 +1500,14 @@ retry: if (ret) goto out; - if (old_fb->format != fb->format) { + /* + * Only check the FOURCC format code, excluding modifiers. This is + * enough for all legacy drivers. Atomic drivers have their own + * checks in their ->atomic_check implementation, which will + * return -EINVAL if any hw or driver constraint is violated due + * to modifier changes. + */ + if (old_fb->format->format != fb->format->format) { DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n"); ret = -EINVAL; goto out; @@ -1192,6 +1557,7 @@ retry: out: if (fb) drm_framebuffer_put(fb); + fb = NULL; if (plane->old_fb) drm_framebuffer_put(plane->old_fb); plane->old_fb = NULL; @@ -1210,3 +1576,306 @@ out: return ret; } + +/** + * DOC: damage tracking + * + * FB_DAMAGE_CLIPS is an optional plane property which provides a means to + * specify a list of damage rectangles on a plane in framebuffer coordinates of + * the framebuffer attached to the plane. In current context damage is the area + * of plane framebuffer that has changed since last plane update (also called + * page-flip), irrespective of whether currently attached framebuffer is same as + * framebuffer attached during last plane update or not. + * + * FB_DAMAGE_CLIPS is a hint to kernel which could be helpful for some drivers + * to optimize internally especially for virtual devices where each framebuffer + * change needs to be transmitted over network, usb, etc. + * + * Since FB_DAMAGE_CLIPS is a hint so it is an optional property. User-space can + * ignore damage clips property and in that case driver will do a full plane + * update. In case damage clips are provided then it is guaranteed that the area + * inside damage clips will be updated to plane. For efficiency driver can do + * full update or can update more than specified in damage clips. Since driver + * is free to read more, user-space must always render the entire visible + * framebuffer. Otherwise there can be corruptions. Also, if a user-space + * provides damage clips which doesn't encompass the actual damage to + * framebuffer (since last plane update) can result in incorrect rendering. + * + * FB_DAMAGE_CLIPS is a blob property with the layout of blob data is simply an + * array of &drm_mode_rect. Unlike plane &drm_plane_state.src coordinates, + * damage clips are not in 16.16 fixed point. Similar to plane src in + * framebuffer, damage clips cannot be negative. In damage clip, x1/y1 are + * inclusive and x2/y2 are exclusive. While kernel does not error for overlapped + * damage clips, it is strongly discouraged. + * + * Drivers that are interested in damage interface for plane should enable + * FB_DAMAGE_CLIPS property by calling drm_plane_enable_fb_damage_clips(). + * Drivers implementing damage can use drm_atomic_helper_damage_iter_init() and + * drm_atomic_helper_damage_iter_next() helper iterator function to get damage + * rectangles clipped to &drm_plane_state.src. + * + * Note that there are two types of damage handling: frame damage and buffer + * damage, the type of damage handling implemented depends on a driver's upload + * target. Drivers implementing a per-plane or per-CRTC upload target need to + * handle frame damage, while drivers implementing a per-buffer upload target + * need to handle buffer damage. + * + * The existing damage helpers only support the frame damage type, there is no + * buffer age support or similar damage accumulation algorithm implemented yet. + * + * Only drivers handling frame damage can use the mentioned damage helpers to + * iterate over the damaged regions. Drivers that handle buffer damage, must set + * &drm_plane_state.ignore_damage_clips for drm_atomic_helper_damage_iter_init() + * to know that damage clips should be ignored and return &drm_plane_state.src + * as the damage rectangle, to force a full plane update. + * + * Drivers with a per-buffer upload target could compare the &drm_plane_state.fb + * of the old and new plane states to determine if the framebuffer attached to a + * plane has changed or not since the last plane update. If &drm_plane_state.fb + * has changed, then &drm_plane_state.ignore_damage_clips must be set to true. + * + * That is because drivers with a per-plane upload target, expect the backing + * storage buffer to not change for a given plane. If the upload buffer changes + * between page flips, the new upload buffer has to be updated as a whole. This + * can be improved in the future if support for frame damage is added to the DRM + * damage helpers, similarly to how user-space already handle this case as it is + * explained in the following documents: + * + * https://registry.khronos.org/EGL/extensions/KHR/EGL_KHR_swap_buffers_with_damage.txt + * https://emersion.fr/blog/2019/intro-to-damage-tracking/ + */ + +/** + * drm_plane_enable_fb_damage_clips - Enables plane fb damage clips property. + * @plane: Plane on which to enable damage clips property. + * + * This function lets driver to enable the damage clips property on a plane. + */ +void drm_plane_enable_fb_damage_clips(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct drm_mode_config *config = &dev->mode_config; + + drm_object_attach_property(&plane->base, config->prop_fb_damage_clips, + 0); +} +EXPORT_SYMBOL(drm_plane_enable_fb_damage_clips); + +/** + * drm_plane_get_damage_clips_count - Returns damage clips count. + * @state: Plane state. + * + * Simple helper to get the number of &drm_mode_rect clips set by user-space + * during plane update. + * + * Return: Number of clips in plane fb_damage_clips blob property. + */ +unsigned int +drm_plane_get_damage_clips_count(const struct drm_plane_state *state) +{ + return (state && state->fb_damage_clips) ? + state->fb_damage_clips->length/sizeof(struct drm_mode_rect) : 0; +} +EXPORT_SYMBOL(drm_plane_get_damage_clips_count); + +struct drm_mode_rect * +__drm_plane_get_damage_clips(const struct drm_plane_state *state) +{ + return (struct drm_mode_rect *)((state && state->fb_damage_clips) ? + state->fb_damage_clips->data : NULL); +} + +/** + * drm_plane_get_damage_clips - Returns damage clips. + * @state: Plane state. + * + * Note that this function returns uapi type &drm_mode_rect. Drivers might want + * to use the helper functions drm_atomic_helper_damage_iter_init() and + * drm_atomic_helper_damage_iter_next() or drm_atomic_helper_damage_merged() if + * the driver can only handle a single damage region at most. + * + * Return: Damage clips in plane fb_damage_clips blob property. + */ +struct drm_mode_rect * +drm_plane_get_damage_clips(const struct drm_plane_state *state) +{ + struct drm_device *dev = state->plane->dev; + struct drm_mode_config *config = &dev->mode_config; + + /* check that drm_plane_enable_fb_damage_clips() was called */ + if (!drm_mode_obj_find_prop_id(&state->plane->base, + config->prop_fb_damage_clips->base.id)) + drm_warn_once(dev, "drm_plane_enable_fb_damage_clips() not called\n"); + + return __drm_plane_get_damage_clips(state); +} +EXPORT_SYMBOL(drm_plane_get_damage_clips); + +struct drm_property * +drm_create_scaling_filter_prop(struct drm_device *dev, + unsigned int supported_filters) +{ + struct drm_property *prop; + static const struct drm_prop_enum_list props[] = { + { DRM_SCALING_FILTER_DEFAULT, "Default" }, + { DRM_SCALING_FILTER_NEAREST_NEIGHBOR, "Nearest Neighbor" }, + }; + unsigned int valid_mode_mask = BIT(DRM_SCALING_FILTER_DEFAULT) | + BIT(DRM_SCALING_FILTER_NEAREST_NEIGHBOR); + int i; + + if (WARN_ON((supported_filters & ~valid_mode_mask) || + ((supported_filters & BIT(DRM_SCALING_FILTER_DEFAULT)) == 0))) + return ERR_PTR(-EINVAL); + + prop = drm_property_create(dev, DRM_MODE_PROP_ENUM, + "SCALING_FILTER", + hweight32(supported_filters)); + if (!prop) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < ARRAY_SIZE(props); i++) { + int ret; + + if (!(BIT(props[i].type) & supported_filters)) + continue; + + ret = drm_property_add_enum(prop, props[i].type, + props[i].name); + + if (ret) { + drm_property_destroy(dev, prop); + + return ERR_PTR(ret); + } + } + + return prop; +} + +/** + * drm_plane_create_scaling_filter_property - create a new scaling filter + * property + * + * @plane: drm plane + * @supported_filters: bitmask of supported scaling filters, must include + * BIT(DRM_SCALING_FILTER_DEFAULT). + * + * This function lets driver to enable the scaling filter property on a given + * plane. + * + * RETURNS: + * Zero for success or -errno + */ +int drm_plane_create_scaling_filter_property(struct drm_plane *plane, + unsigned int supported_filters) +{ + struct drm_property *prop = + drm_create_scaling_filter_prop(plane->dev, supported_filters); + + if (IS_ERR(prop)) + return PTR_ERR(prop); + + drm_object_attach_property(&plane->base, prop, + DRM_SCALING_FILTER_DEFAULT); + plane->scaling_filter_property = prop; + + return 0; +} +EXPORT_SYMBOL(drm_plane_create_scaling_filter_property); + +/** + * drm_plane_add_size_hints_property - create a size hints property + * + * @plane: drm plane + * @hints: size hints + * @num_hints: number of size hints + * + * Create a size hints property for the plane. + * + * RETURNS: + * Zero for success or -errno + */ +int drm_plane_add_size_hints_property(struct drm_plane *plane, + const struct drm_plane_size_hint *hints, + int num_hints) +{ + struct drm_device *dev = plane->dev; + struct drm_mode_config *config = &dev->mode_config; + struct drm_property_blob *blob; + + /* extending to other plane types needs actual thought */ + if (drm_WARN_ON(dev, plane->type != DRM_PLANE_TYPE_CURSOR)) + return -EINVAL; + + blob = drm_property_create_blob(dev, + array_size(sizeof(hints[0]), num_hints), + hints); + if (IS_ERR(blob)) + return PTR_ERR(blob); + + drm_object_attach_property(&plane->base, config->size_hints_property, + blob->base.id); + + return 0; +} +EXPORT_SYMBOL(drm_plane_add_size_hints_property); + +/** + * drm_plane_create_color_pipeline_property - create a new color pipeline + * property + * + * @plane: drm plane + * @pipelines: list of pipelines + * @num_pipelines: number of pipelines + * + * Create the COLOR_PIPELINE plane property to specific color pipelines on + * the plane. + * + * RETURNS: + * Zero for success or -errno + */ +int drm_plane_create_color_pipeline_property(struct drm_plane *plane, + const struct drm_prop_enum_list *pipelines, + int num_pipelines) +{ + struct drm_prop_enum_list *all_pipelines; + struct drm_property *prop; + int len = 0; + int i; + + all_pipelines = kcalloc(num_pipelines + 1, + sizeof(*all_pipelines), + GFP_KERNEL); + + if (!all_pipelines) { + drm_err(plane->dev, "failed to allocate color pipeline\n"); + return -ENOMEM; + } + + /* Create default Bypass color pipeline */ + all_pipelines[len].type = 0; + all_pipelines[len].name = "Bypass"; + len++; + + /* Add all other color pipelines */ + for (i = 0; i < num_pipelines; i++, len++) { + all_pipelines[len].type = pipelines[i].type; + all_pipelines[len].name = pipelines[i].name; + } + + prop = drm_property_create_enum(plane->dev, DRM_MODE_PROP_ATOMIC, + "COLOR_PIPELINE", + all_pipelines, len); + if (IS_ERR(prop)) { + kfree(all_pipelines); + return PTR_ERR(prop); + } + + drm_object_attach_property(&plane->base, prop, 0); + plane->color_pipeline_property = prop; + + kfree(all_pipelines); + return 0; +} +EXPORT_SYMBOL(drm_plane_create_color_pipeline_property); |
