1879 lines · 56.6 KB · C
RawDownload
/* * Copyright (c) 2016 Intel Corporation * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission.  The copyright holders make no representations * about the suitability of this software for any purpose.  It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #include <linux/export.h>#include <linux/slab.h>#include <linux/uaccess.h> #include <drm/drm_plane.h>#include <drm/drm_drv.h>#include <drm/drm_print.h>#include <drm/drm_framebuffer.h>#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" /** * DOC: overview * * 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 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(). * * 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. * * 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){	unsigned int num = 0;	struct drm_plane *tmp; 	drm_for_each_plane(tmp, dev) {		num++;	} 	return num;} static inline u32 *formats_ptr(struct drm_format_modifier_blob *blob){	return (u32 *)(((char *)blob) + blob->formats_offset);} static inline struct drm_format_modifier *modifiers_ptr(struct drm_format_modifier_blob *blob){	return (struct drm_format_modifier *)(((char *)blob) + blob->modifiers_offset);} 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)){	struct drm_property_blob *blob;	struct drm_format_modifier *mod;	size_t blob_size, formats_size, modifiers_size;	struct drm_format_modifier_blob *blob_data;	unsigned int i, j; 	formats_size = sizeof(__u32) * plane->format_count;	if (WARN_ON(!formats_size)) {		/* 0 formats are never expected */		return ERR_PTR(-EINVAL);	} 	modifiers_size =		sizeof(struct drm_format_modifier) * plane->modifier_count; 	blob_size = sizeof(struct drm_format_modifier_blob);	/* Modifiers offset is a pointer to a struct with a 64 bit field so it	 * should be naturally aligned to 8B.	 */	BUILD_BUG_ON(sizeof(struct drm_format_modifier_blob) % 8);	blob_size += ALIGN(formats_size, 8);	blob_size += modifiers_size; 	blob = drm_property_create_blob(dev, blob_size, NULL);	if (IS_ERR(blob))		return blob; 	blob_data = blob->data;	blob_data->version = FORMAT_BLOB_CURRENT;	blob_data->count_formats = plane->format_count;	blob_data->formats_offset = sizeof(struct drm_format_modifier_blob);	blob_data->count_modifiers = plane->modifier_count; 	blob_data->modifiers_offset =		ALIGN(blob_data->formats_offset + formats_size, 8); 	memcpy(formats_ptr(blob_data), plane->format_types, formats_size); 	mod = modifiers_ptr(blob_data);	for (i = 0; i < plane->modifier_count; i++) {		for (j = 0; j < plane->format_count; j++) {			if (!format_mod_supported ||			    format_mod_supported(plane,						 plane->format_types[j],						 plane->modifiers[i])) {				mod->formats |= 1ULL << j;			}		} 		mod->modifier = plane->modifiers[i];		mod->offset = 0;		mod->pad = 0;		mod++;	} 	return blob;} /** * DOC: hotspot properties * * HOTSPOT_X: property to set mouse hotspot x offset. * HOTSPOT_Y: property to set mouse hotspot y offset. * * 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. */ /** * 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 (!prop_x)		return -ENOMEM; 	prop_y = drm_property_create_signed_range(plane->dev, 0, "HOTSPOT_Y",						  INT_MIN, INT_MAX);	if (!prop_y) {		drm_property_destroy(plane->dev, prop_x);		return -ENOMEM;	} 	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; 	/* plane index is used with 32bit bitmasks */	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)); 	ret = drm_mode_object_add(dev, &plane->base, DRM_MODE_OBJECT_PLANE);	if (ret)		return ret; 	drm_modeset_lock_init(&plane->mutex); 	plane->base.properties = &plane->properties;	plane->dev = dev;	plane->funcs = funcs;	plane->format_types = kmalloc_array(format_count, sizeof(uint32_t),					    GFP_KERNEL);	if (!plane->format_types) {		DRM_DEBUG_KMS("out of memory when allocating plane\n");		drm_mode_object_unregister(dev, &plane->base);		return -ENOMEM;	} 	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(*plane->modifiers),					 GFP_KERNEL); 	if (format_modifier_count && !plane->modifiers) {		DRM_DEBUG_KMS("out of memory when allocating plane\n");		kfree(plane->format_types);		drm_mode_object_unregister(dev, &plane->base);		return -ENOMEM;	} 	if (name) {		plane->name = kvasprintf(GFP_KERNEL, name, ap);	} else {		plane->name = kasprintf(GFP_KERNEL, "plane-%d",					drm_num_planes(dev));	}	if (!plane->name) {		kfree(plane->format_types);		kfree(plane->modifiers);		drm_mode_object_unregister(dev, &plane->base);		return -ENOMEM;	} 	memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));	plane->format_count = format_count;	memcpy(plane->modifiers, format_modifiers,	       format_modifier_count * sizeof(format_modifiers[0]));	plane->possible_crtcs = possible_crtcs;	plane->type = type; 	list_add_tail(&plane->head, &config->plane_list);	plane->index = config->num_total_plane++; 	drm_object_attach_property(&plane->base,				   config->plane_type_property,				   plane->type); 	if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {		drm_object_attach_property(&plane->base, config->prop_fb_id, 0);		drm_object_attach_property(&plane->base, config->prop_in_fence_fd, -1);		drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);		drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);		drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);		drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);		drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);		drm_object_attach_property(&plane->base, config->prop_src_x, 0);		drm_object_attach_property(&plane->base, config->prop_src_y, 0);		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);	} 

Lines 1–500 of 1879.