162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2016 Intel Corporation
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Permission to use, copy, modify, distribute, and sell this software and its
562306a36Sopenharmony_ci * documentation for any purpose is hereby granted without fee, provided that
662306a36Sopenharmony_ci * the above copyright notice appear in all copies and that both that copyright
762306a36Sopenharmony_ci * notice and this permission notice appear in supporting documentation, and
862306a36Sopenharmony_ci * that the name of the copyright holders not be used in advertising or
962306a36Sopenharmony_ci * publicity pertaining to distribution of the software without specific,
1062306a36Sopenharmony_ci * written prior permission.  The copyright holders make no representations
1162306a36Sopenharmony_ci * about the suitability of this software for any purpose.  It is provided "as
1262306a36Sopenharmony_ci * is" without express or implied warranty.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1562306a36Sopenharmony_ci * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
1662306a36Sopenharmony_ci * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
1762306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
1862306a36Sopenharmony_ci * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
1962306a36Sopenharmony_ci * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
2062306a36Sopenharmony_ci * OF THIS SOFTWARE.
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <linux/slab.h>
2462306a36Sopenharmony_ci#include <linux/uaccess.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <drm/drm_plane.h>
2762306a36Sopenharmony_ci#include <drm/drm_drv.h>
2862306a36Sopenharmony_ci#include <drm/drm_print.h>
2962306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
3062306a36Sopenharmony_ci#include <drm/drm_file.h>
3162306a36Sopenharmony_ci#include <drm/drm_crtc.h>
3262306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
3362306a36Sopenharmony_ci#include <drm/drm_managed.h>
3462306a36Sopenharmony_ci#include <drm/drm_vblank.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#include "drm_crtc_internal.h"
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/**
3962306a36Sopenharmony_ci * DOC: overview
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci * A plane represents an image source that can be blended with or overlaid on
4262306a36Sopenharmony_ci * top of a CRTC during the scanout process. Planes take their input data from a
4362306a36Sopenharmony_ci * &drm_framebuffer object. The plane itself specifies the cropping and scaling
4462306a36Sopenharmony_ci * of that image, and where it is placed on the visible area of a display
4562306a36Sopenharmony_ci * pipeline, represented by &drm_crtc. A plane can also have additional
4662306a36Sopenharmony_ci * properties that specify how the pixels are positioned and blended, like
4762306a36Sopenharmony_ci * rotation or Z-position. All these properties are stored in &drm_plane_state.
4862306a36Sopenharmony_ci *
4962306a36Sopenharmony_ci * Unless explicitly specified (via CRTC property or otherwise), the active area
5062306a36Sopenharmony_ci * of a CRTC will be black by default. This means portions of the active area
5162306a36Sopenharmony_ci * which are not covered by a plane will be black, and alpha blending of any
5262306a36Sopenharmony_ci * planes with the CRTC background will blend with black at the lowest zpos.
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * To create a plane, a KMS drivers allocates and zeroes an instances of
5562306a36Sopenharmony_ci * &struct drm_plane (possibly as part of a larger structure) and registers it
5662306a36Sopenharmony_ci * with a call to drm_universal_plane_init().
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci * Each plane has a type, see enum drm_plane_type. A plane can be compatible
5962306a36Sopenharmony_ci * with multiple CRTCs, see &drm_plane.possible_crtcs.
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci * Each CRTC must have a unique primary plane userspace can attach to enable
6262306a36Sopenharmony_ci * the CRTC. In other words, userspace must be able to attach a different
6362306a36Sopenharmony_ci * primary plane to each CRTC at the same time. Primary planes can still be
6462306a36Sopenharmony_ci * compatible with multiple CRTCs. There must be exactly as many primary planes
6562306a36Sopenharmony_ci * as there are CRTCs.
6662306a36Sopenharmony_ci *
6762306a36Sopenharmony_ci * Legacy uAPI doesn't expose the primary and cursor planes directly. DRM core
6862306a36Sopenharmony_ci * relies on the driver to set the primary and optionally the cursor plane used
6962306a36Sopenharmony_ci * for legacy IOCTLs. This is done by calling drm_crtc_init_with_planes(). All
7062306a36Sopenharmony_ci * drivers must provide one primary plane per CRTC to avoid surprising legacy
7162306a36Sopenharmony_ci * userspace too much.
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/**
7562306a36Sopenharmony_ci * DOC: standard plane properties
7662306a36Sopenharmony_ci *
7762306a36Sopenharmony_ci * DRM planes have a few standardized properties:
7862306a36Sopenharmony_ci *
7962306a36Sopenharmony_ci * type:
8062306a36Sopenharmony_ci *     Immutable property describing the type of the plane.
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci *     For user-space which has enabled the &DRM_CLIENT_CAP_ATOMIC capability,
8362306a36Sopenharmony_ci *     the plane type is just a hint and is mostly superseded by atomic
8462306a36Sopenharmony_ci *     test-only commits. The type hint can still be used to come up more
8562306a36Sopenharmony_ci *     easily with a plane configuration accepted by the driver.
8662306a36Sopenharmony_ci *
8762306a36Sopenharmony_ci *     The value of this property can be one of the following:
8862306a36Sopenharmony_ci *
8962306a36Sopenharmony_ci *     "Primary":
9062306a36Sopenharmony_ci *         To light up a CRTC, attaching a primary plane is the most likely to
9162306a36Sopenharmony_ci *         work if it covers the whole CRTC and doesn't have scaling or
9262306a36Sopenharmony_ci *         cropping set up.
9362306a36Sopenharmony_ci *
9462306a36Sopenharmony_ci *         Drivers may support more features for the primary plane, user-space
9562306a36Sopenharmony_ci *         can find out with test-only atomic commits.
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci *         Some primary planes are implicitly used by the kernel in the legacy
9862306a36Sopenharmony_ci *         IOCTLs &DRM_IOCTL_MODE_SETCRTC and &DRM_IOCTL_MODE_PAGE_FLIP.
9962306a36Sopenharmony_ci *         Therefore user-space must not mix explicit usage of any primary
10062306a36Sopenharmony_ci *         plane (e.g. through an atomic commit) with these legacy IOCTLs.
10162306a36Sopenharmony_ci *
10262306a36Sopenharmony_ci *     "Cursor":
10362306a36Sopenharmony_ci *         To enable this plane, using a framebuffer configured without scaling
10462306a36Sopenharmony_ci *         or cropping and with the following properties is the most likely to
10562306a36Sopenharmony_ci *         work:
10662306a36Sopenharmony_ci *
10762306a36Sopenharmony_ci *         - If the driver provides the capabilities &DRM_CAP_CURSOR_WIDTH and
10862306a36Sopenharmony_ci *           &DRM_CAP_CURSOR_HEIGHT, create the framebuffer with this size.
10962306a36Sopenharmony_ci *           Otherwise, create a framebuffer with the size 64x64.
11062306a36Sopenharmony_ci *         - If the driver doesn't support modifiers, create a framebuffer with
11162306a36Sopenharmony_ci *           a linear layout. Otherwise, use the IN_FORMATS plane property.
11262306a36Sopenharmony_ci *
11362306a36Sopenharmony_ci *         Drivers may support more features for the cursor plane, user-space
11462306a36Sopenharmony_ci *         can find out with test-only atomic commits.
11562306a36Sopenharmony_ci *
11662306a36Sopenharmony_ci *         Some cursor planes are implicitly used by the kernel in the legacy
11762306a36Sopenharmony_ci *         IOCTLs &DRM_IOCTL_MODE_CURSOR and &DRM_IOCTL_MODE_CURSOR2.
11862306a36Sopenharmony_ci *         Therefore user-space must not mix explicit usage of any cursor
11962306a36Sopenharmony_ci *         plane (e.g. through an atomic commit) with these legacy IOCTLs.
12062306a36Sopenharmony_ci *
12162306a36Sopenharmony_ci *         Some drivers may support cursors even if no cursor plane is exposed.
12262306a36Sopenharmony_ci *         In this case, the legacy cursor IOCTLs can be used to configure the
12362306a36Sopenharmony_ci *         cursor.
12462306a36Sopenharmony_ci *
12562306a36Sopenharmony_ci *     "Overlay":
12662306a36Sopenharmony_ci *         Neither primary nor cursor.
12762306a36Sopenharmony_ci *
12862306a36Sopenharmony_ci *         Overlay planes are the only planes exposed when the
12962306a36Sopenharmony_ci *         &DRM_CLIENT_CAP_UNIVERSAL_PLANES capability is disabled.
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci * IN_FORMATS:
13262306a36Sopenharmony_ci *     Blob property which contains the set of buffer format and modifier
13362306a36Sopenharmony_ci *     pairs supported by this plane. The blob is a struct
13462306a36Sopenharmony_ci *     drm_format_modifier_blob. Without this property the plane doesn't
13562306a36Sopenharmony_ci *     support buffers with modifiers. Userspace cannot change this property.
13662306a36Sopenharmony_ci *
13762306a36Sopenharmony_ci *     Note that userspace can check the &DRM_CAP_ADDFB2_MODIFIERS driver
13862306a36Sopenharmony_ci *     capability for general modifier support. If this flag is set then every
13962306a36Sopenharmony_ci *     plane will have the IN_FORMATS property, even when it only supports
14062306a36Sopenharmony_ci *     DRM_FORMAT_MOD_LINEAR. Before linux kernel release v5.1 there have been
14162306a36Sopenharmony_ci *     various bugs in this area with inconsistencies between the capability
14262306a36Sopenharmony_ci *     flag and per-plane properties.
14362306a36Sopenharmony_ci */
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic unsigned int drm_num_planes(struct drm_device *dev)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	unsigned int num = 0;
14862306a36Sopenharmony_ci	struct drm_plane *tmp;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	drm_for_each_plane(tmp, dev) {
15162306a36Sopenharmony_ci		num++;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return num;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic inline u32 *
15862306a36Sopenharmony_ciformats_ptr(struct drm_format_modifier_blob *blob)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	return (u32 *)(((char *)blob) + blob->formats_offset);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic inline struct drm_format_modifier *
16462306a36Sopenharmony_cimodifiers_ptr(struct drm_format_modifier_blob *blob)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	return (struct drm_format_modifier *)(((char *)blob) + blob->modifiers_offset);
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int create_in_format_blob(struct drm_device *dev, struct drm_plane *plane)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	const struct drm_mode_config *config = &dev->mode_config;
17262306a36Sopenharmony_ci	struct drm_property_blob *blob;
17362306a36Sopenharmony_ci	struct drm_format_modifier *mod;
17462306a36Sopenharmony_ci	size_t blob_size, formats_size, modifiers_size;
17562306a36Sopenharmony_ci	struct drm_format_modifier_blob *blob_data;
17662306a36Sopenharmony_ci	unsigned int i, j;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	formats_size = sizeof(__u32) * plane->format_count;
17962306a36Sopenharmony_ci	if (WARN_ON(!formats_size)) {
18062306a36Sopenharmony_ci		/* 0 formats are never expected */
18162306a36Sopenharmony_ci		return 0;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	modifiers_size =
18562306a36Sopenharmony_ci		sizeof(struct drm_format_modifier) * plane->modifier_count;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	blob_size = sizeof(struct drm_format_modifier_blob);
18862306a36Sopenharmony_ci	/* Modifiers offset is a pointer to a struct with a 64 bit field so it
18962306a36Sopenharmony_ci	 * should be naturally aligned to 8B.
19062306a36Sopenharmony_ci	 */
19162306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct drm_format_modifier_blob) % 8);
19262306a36Sopenharmony_ci	blob_size += ALIGN(formats_size, 8);
19362306a36Sopenharmony_ci	blob_size += modifiers_size;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	blob = drm_property_create_blob(dev, blob_size, NULL);
19662306a36Sopenharmony_ci	if (IS_ERR(blob))
19762306a36Sopenharmony_ci		return -1;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	blob_data = blob->data;
20062306a36Sopenharmony_ci	blob_data->version = FORMAT_BLOB_CURRENT;
20162306a36Sopenharmony_ci	blob_data->count_formats = plane->format_count;
20262306a36Sopenharmony_ci	blob_data->formats_offset = sizeof(struct drm_format_modifier_blob);
20362306a36Sopenharmony_ci	blob_data->count_modifiers = plane->modifier_count;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	blob_data->modifiers_offset =
20662306a36Sopenharmony_ci		ALIGN(blob_data->formats_offset + formats_size, 8);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	memcpy(formats_ptr(blob_data), plane->format_types, formats_size);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	mod = modifiers_ptr(blob_data);
21162306a36Sopenharmony_ci	for (i = 0; i < plane->modifier_count; i++) {
21262306a36Sopenharmony_ci		for (j = 0; j < plane->format_count; j++) {
21362306a36Sopenharmony_ci			if (!plane->funcs->format_mod_supported ||
21462306a36Sopenharmony_ci			    plane->funcs->format_mod_supported(plane,
21562306a36Sopenharmony_ci							       plane->format_types[j],
21662306a36Sopenharmony_ci							       plane->modifiers[i])) {
21762306a36Sopenharmony_ci				mod->formats |= 1ULL << j;
21862306a36Sopenharmony_ci			}
21962306a36Sopenharmony_ci		}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		mod->modifier = plane->modifiers[i];
22262306a36Sopenharmony_ci		mod->offset = 0;
22362306a36Sopenharmony_ci		mod->pad = 0;
22462306a36Sopenharmony_ci		mod++;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	drm_object_attach_property(&plane->base, config->modifiers_property,
22862306a36Sopenharmony_ci				   blob->base.id);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	return 0;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci__printf(9, 0)
23462306a36Sopenharmony_cistatic int __drm_universal_plane_init(struct drm_device *dev,
23562306a36Sopenharmony_ci				      struct drm_plane *plane,
23662306a36Sopenharmony_ci				      uint32_t possible_crtcs,
23762306a36Sopenharmony_ci				      const struct drm_plane_funcs *funcs,
23862306a36Sopenharmony_ci				      const uint32_t *formats,
23962306a36Sopenharmony_ci				      unsigned int format_count,
24062306a36Sopenharmony_ci				      const uint64_t *format_modifiers,
24162306a36Sopenharmony_ci				      enum drm_plane_type type,
24262306a36Sopenharmony_ci				      const char *name, va_list ap)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct drm_mode_config *config = &dev->mode_config;
24562306a36Sopenharmony_ci	static const uint64_t default_modifiers[] = {
24662306a36Sopenharmony_ci		DRM_FORMAT_MOD_LINEAR,
24762306a36Sopenharmony_ci	};
24862306a36Sopenharmony_ci	unsigned int format_modifier_count = 0;
24962306a36Sopenharmony_ci	int ret;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	/* plane index is used with 32bit bitmasks */
25262306a36Sopenharmony_ci	if (WARN_ON(config->num_total_plane >= 32))
25362306a36Sopenharmony_ci		return -EINVAL;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	/*
25662306a36Sopenharmony_ci	 * First driver to need more than 64 formats needs to fix this. Each
25762306a36Sopenharmony_ci	 * format is encoded as a bit and the current code only supports a u64.
25862306a36Sopenharmony_ci	 */
25962306a36Sopenharmony_ci	if (WARN_ON(format_count > 64))
26062306a36Sopenharmony_ci		return -EINVAL;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	WARN_ON(drm_drv_uses_atomic_modeset(dev) &&
26362306a36Sopenharmony_ci		(!funcs->atomic_destroy_state ||
26462306a36Sopenharmony_ci		 !funcs->atomic_duplicate_state));
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	ret = drm_mode_object_add(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
26762306a36Sopenharmony_ci	if (ret)
26862306a36Sopenharmony_ci		return ret;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	drm_modeset_lock_init(&plane->mutex);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	plane->base.properties = &plane->properties;
27362306a36Sopenharmony_ci	plane->dev = dev;
27462306a36Sopenharmony_ci	plane->funcs = funcs;
27562306a36Sopenharmony_ci	plane->format_types = kmalloc_array(format_count, sizeof(uint32_t),
27662306a36Sopenharmony_ci					    GFP_KERNEL);
27762306a36Sopenharmony_ci	if (!plane->format_types) {
27862306a36Sopenharmony_ci		DRM_DEBUG_KMS("out of memory when allocating plane\n");
27962306a36Sopenharmony_ci		drm_mode_object_unregister(dev, &plane->base);
28062306a36Sopenharmony_ci		return -ENOMEM;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (format_modifiers) {
28462306a36Sopenharmony_ci		const uint64_t *temp_modifiers = format_modifiers;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci		while (*temp_modifiers++ != DRM_FORMAT_MOD_INVALID)
28762306a36Sopenharmony_ci			format_modifier_count++;
28862306a36Sopenharmony_ci	} else {
28962306a36Sopenharmony_ci		if (!dev->mode_config.fb_modifiers_not_supported) {
29062306a36Sopenharmony_ci			format_modifiers = default_modifiers;
29162306a36Sopenharmony_ci			format_modifier_count = ARRAY_SIZE(default_modifiers);
29262306a36Sopenharmony_ci		}
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* autoset the cap and check for consistency across all planes */
29662306a36Sopenharmony_ci	drm_WARN_ON(dev, config->fb_modifiers_not_supported &&
29762306a36Sopenharmony_ci				format_modifier_count);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	plane->modifier_count = format_modifier_count;
30062306a36Sopenharmony_ci	plane->modifiers = kmalloc_array(format_modifier_count,
30162306a36Sopenharmony_ci					 sizeof(format_modifiers[0]),
30262306a36Sopenharmony_ci					 GFP_KERNEL);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (format_modifier_count && !plane->modifiers) {
30562306a36Sopenharmony_ci		DRM_DEBUG_KMS("out of memory when allocating plane\n");
30662306a36Sopenharmony_ci		kfree(plane->format_types);
30762306a36Sopenharmony_ci		drm_mode_object_unregister(dev, &plane->base);
30862306a36Sopenharmony_ci		return -ENOMEM;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (name) {
31262306a36Sopenharmony_ci		plane->name = kvasprintf(GFP_KERNEL, name, ap);
31362306a36Sopenharmony_ci	} else {
31462306a36Sopenharmony_ci		plane->name = kasprintf(GFP_KERNEL, "plane-%d",
31562306a36Sopenharmony_ci					drm_num_planes(dev));
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci	if (!plane->name) {
31862306a36Sopenharmony_ci		kfree(plane->format_types);
31962306a36Sopenharmony_ci		kfree(plane->modifiers);
32062306a36Sopenharmony_ci		drm_mode_object_unregister(dev, &plane->base);
32162306a36Sopenharmony_ci		return -ENOMEM;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
32562306a36Sopenharmony_ci	plane->format_count = format_count;
32662306a36Sopenharmony_ci	memcpy(plane->modifiers, format_modifiers,
32762306a36Sopenharmony_ci	       format_modifier_count * sizeof(format_modifiers[0]));
32862306a36Sopenharmony_ci	plane->possible_crtcs = possible_crtcs;
32962306a36Sopenharmony_ci	plane->type = type;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	list_add_tail(&plane->head, &config->plane_list);
33262306a36Sopenharmony_ci	plane->index = config->num_total_plane++;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	drm_object_attach_property(&plane->base,
33562306a36Sopenharmony_ci				   config->plane_type_property,
33662306a36Sopenharmony_ci				   plane->type);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
33962306a36Sopenharmony_ci		drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
34062306a36Sopenharmony_ci		drm_object_attach_property(&plane->base, config->prop_in_fence_fd, -1);
34162306a36Sopenharmony_ci		drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
34262306a36Sopenharmony_ci		drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
34362306a36Sopenharmony_ci		drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
34462306a36Sopenharmony_ci		drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
34562306a36Sopenharmony_ci		drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
34662306a36Sopenharmony_ci		drm_object_attach_property(&plane->base, config->prop_src_x, 0);
34762306a36Sopenharmony_ci		drm_object_attach_property(&plane->base, config->prop_src_y, 0);
34862306a36Sopenharmony_ci		drm_object_attach_property(&plane->base, config->prop_src_w, 0);
34962306a36Sopenharmony_ci		drm_object_attach_property(&plane->base, config->prop_src_h, 0);
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	if (format_modifier_count)
35362306a36Sopenharmony_ci		create_in_format_blob(dev, plane);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return 0;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci/**
35962306a36Sopenharmony_ci * drm_universal_plane_init - Initialize a new universal plane object
36062306a36Sopenharmony_ci * @dev: DRM device
36162306a36Sopenharmony_ci * @plane: plane object to init
36262306a36Sopenharmony_ci * @possible_crtcs: bitmask of possible CRTCs
36362306a36Sopenharmony_ci * @funcs: callbacks for the new plane
36462306a36Sopenharmony_ci * @formats: array of supported formats (DRM_FORMAT\_\*)
36562306a36Sopenharmony_ci * @format_count: number of elements in @formats
36662306a36Sopenharmony_ci * @format_modifiers: array of struct drm_format modifiers terminated by
36762306a36Sopenharmony_ci *                    DRM_FORMAT_MOD_INVALID
36862306a36Sopenharmony_ci * @type: type of plane (overlay, primary, cursor)
36962306a36Sopenharmony_ci * @name: printf style format string for the plane name, or NULL for default name
37062306a36Sopenharmony_ci *
37162306a36Sopenharmony_ci * Initializes a plane object of type @type. The &drm_plane_funcs.destroy hook
37262306a36Sopenharmony_ci * should call drm_plane_cleanup() and kfree() the plane structure. The plane
37362306a36Sopenharmony_ci * structure should not be allocated with devm_kzalloc().
37462306a36Sopenharmony_ci *
37562306a36Sopenharmony_ci * Note: consider using drmm_universal_plane_alloc() instead of
37662306a36Sopenharmony_ci * drm_universal_plane_init() to let the DRM managed resource infrastructure
37762306a36Sopenharmony_ci * take care of cleanup and deallocation.
37862306a36Sopenharmony_ci *
37962306a36Sopenharmony_ci * Drivers that only support the DRM_FORMAT_MOD_LINEAR modifier support may set
38062306a36Sopenharmony_ci * @format_modifiers to NULL. The plane will advertise the linear modifier.
38162306a36Sopenharmony_ci *
38262306a36Sopenharmony_ci * Returns:
38362306a36Sopenharmony_ci * Zero on success, error code on failure.
38462306a36Sopenharmony_ci */
38562306a36Sopenharmony_ciint drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
38662306a36Sopenharmony_ci			     uint32_t possible_crtcs,
38762306a36Sopenharmony_ci			     const struct drm_plane_funcs *funcs,
38862306a36Sopenharmony_ci			     const uint32_t *formats, unsigned int format_count,
38962306a36Sopenharmony_ci			     const uint64_t *format_modifiers,
39062306a36Sopenharmony_ci			     enum drm_plane_type type,
39162306a36Sopenharmony_ci			     const char *name, ...)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	va_list ap;
39462306a36Sopenharmony_ci	int ret;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	WARN_ON(!funcs->destroy);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	va_start(ap, name);
39962306a36Sopenharmony_ci	ret = __drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
40062306a36Sopenharmony_ci					 formats, format_count, format_modifiers,
40162306a36Sopenharmony_ci					 type, name, ap);
40262306a36Sopenharmony_ci	va_end(ap);
40362306a36Sopenharmony_ci	return ret;
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_universal_plane_init);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic void drmm_universal_plane_alloc_release(struct drm_device *dev, void *ptr)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct drm_plane *plane = ptr;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (WARN_ON(!plane->dev))
41262306a36Sopenharmony_ci		return;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	drm_plane_cleanup(plane);
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_civoid *__drmm_universal_plane_alloc(struct drm_device *dev, size_t size,
41862306a36Sopenharmony_ci				   size_t offset, uint32_t possible_crtcs,
41962306a36Sopenharmony_ci				   const struct drm_plane_funcs *funcs,
42062306a36Sopenharmony_ci				   const uint32_t *formats, unsigned int format_count,
42162306a36Sopenharmony_ci				   const uint64_t *format_modifiers,
42262306a36Sopenharmony_ci				   enum drm_plane_type type,
42362306a36Sopenharmony_ci				   const char *name, ...)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	void *container;
42662306a36Sopenharmony_ci	struct drm_plane *plane;
42762306a36Sopenharmony_ci	va_list ap;
42862306a36Sopenharmony_ci	int ret;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (WARN_ON(!funcs || funcs->destroy))
43162306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	container = drmm_kzalloc(dev, size, GFP_KERNEL);
43462306a36Sopenharmony_ci	if (!container)
43562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	plane = container + offset;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	va_start(ap, name);
44062306a36Sopenharmony_ci	ret = __drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
44162306a36Sopenharmony_ci					 formats, format_count, format_modifiers,
44262306a36Sopenharmony_ci					 type, name, ap);
44362306a36Sopenharmony_ci	va_end(ap);
44462306a36Sopenharmony_ci	if (ret)
44562306a36Sopenharmony_ci		return ERR_PTR(ret);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	ret = drmm_add_action_or_reset(dev, drmm_universal_plane_alloc_release,
44862306a36Sopenharmony_ci				       plane);
44962306a36Sopenharmony_ci	if (ret)
45062306a36Sopenharmony_ci		return ERR_PTR(ret);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	return container;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ciEXPORT_SYMBOL(__drmm_universal_plane_alloc);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_civoid *__drm_universal_plane_alloc(struct drm_device *dev, size_t size,
45762306a36Sopenharmony_ci				  size_t offset, uint32_t possible_crtcs,
45862306a36Sopenharmony_ci				  const struct drm_plane_funcs *funcs,
45962306a36Sopenharmony_ci				  const uint32_t *formats, unsigned int format_count,
46062306a36Sopenharmony_ci				  const uint64_t *format_modifiers,
46162306a36Sopenharmony_ci				  enum drm_plane_type type,
46262306a36Sopenharmony_ci				  const char *name, ...)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	void *container;
46562306a36Sopenharmony_ci	struct drm_plane *plane;
46662306a36Sopenharmony_ci	va_list ap;
46762306a36Sopenharmony_ci	int ret;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	if (drm_WARN_ON(dev, !funcs))
47062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	container = kzalloc(size, GFP_KERNEL);
47362306a36Sopenharmony_ci	if (!container)
47462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	plane = container + offset;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	va_start(ap, name);
47962306a36Sopenharmony_ci	ret = __drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
48062306a36Sopenharmony_ci					 formats, format_count, format_modifiers,
48162306a36Sopenharmony_ci					 type, name, ap);
48262306a36Sopenharmony_ci	va_end(ap);
48362306a36Sopenharmony_ci	if (ret)
48462306a36Sopenharmony_ci		goto err_kfree;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	return container;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cierr_kfree:
48962306a36Sopenharmony_ci	kfree(container);
49062306a36Sopenharmony_ci	return ERR_PTR(ret);
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ciEXPORT_SYMBOL(__drm_universal_plane_alloc);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ciint drm_plane_register_all(struct drm_device *dev)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	unsigned int num_planes = 0;
49762306a36Sopenharmony_ci	unsigned int num_zpos = 0;
49862306a36Sopenharmony_ci	struct drm_plane *plane;
49962306a36Sopenharmony_ci	int ret = 0;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	drm_for_each_plane(plane, dev) {
50262306a36Sopenharmony_ci		if (plane->funcs->late_register)
50362306a36Sopenharmony_ci			ret = plane->funcs->late_register(plane);
50462306a36Sopenharmony_ci		if (ret)
50562306a36Sopenharmony_ci			return ret;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci		if (plane->zpos_property)
50862306a36Sopenharmony_ci			num_zpos++;
50962306a36Sopenharmony_ci		num_planes++;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	drm_WARN(dev, num_zpos && num_planes != num_zpos,
51362306a36Sopenharmony_ci		 "Mixing planes with and without zpos property is invalid\n");
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	return 0;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_civoid drm_plane_unregister_all(struct drm_device *dev)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	struct drm_plane *plane;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	drm_for_each_plane(plane, dev) {
52362306a36Sopenharmony_ci		if (plane->funcs->early_unregister)
52462306a36Sopenharmony_ci			plane->funcs->early_unregister(plane);
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci/**
52962306a36Sopenharmony_ci * drm_plane_cleanup - Clean up the core plane usage
53062306a36Sopenharmony_ci * @plane: plane to cleanup
53162306a36Sopenharmony_ci *
53262306a36Sopenharmony_ci * This function cleans up @plane and removes it from the DRM mode setting
53362306a36Sopenharmony_ci * core. Note that the function does *not* free the plane structure itself,
53462306a36Sopenharmony_ci * this is the responsibility of the caller.
53562306a36Sopenharmony_ci */
53662306a36Sopenharmony_civoid drm_plane_cleanup(struct drm_plane *plane)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct drm_device *dev = plane->dev;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	drm_modeset_lock_fini(&plane->mutex);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	kfree(plane->format_types);
54362306a36Sopenharmony_ci	kfree(plane->modifiers);
54462306a36Sopenharmony_ci	drm_mode_object_unregister(dev, &plane->base);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	BUG_ON(list_empty(&plane->head));
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/* Note that the plane_list is considered to be static; should we
54962306a36Sopenharmony_ci	 * remove the drm_plane at runtime we would have to decrement all
55062306a36Sopenharmony_ci	 * the indices on the drm_plane after us in the plane_list.
55162306a36Sopenharmony_ci	 */
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	list_del(&plane->head);
55462306a36Sopenharmony_ci	dev->mode_config.num_total_plane--;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	WARN_ON(plane->state && !plane->funcs->atomic_destroy_state);
55762306a36Sopenharmony_ci	if (plane->state && plane->funcs->atomic_destroy_state)
55862306a36Sopenharmony_ci		plane->funcs->atomic_destroy_state(plane, plane->state);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	kfree(plane->name);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	memset(plane, 0, sizeof(*plane));
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_plane_cleanup);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci/**
56762306a36Sopenharmony_ci * drm_plane_from_index - find the registered plane at an index
56862306a36Sopenharmony_ci * @dev: DRM device
56962306a36Sopenharmony_ci * @idx: index of registered plane to find for
57062306a36Sopenharmony_ci *
57162306a36Sopenharmony_ci * Given a plane index, return the registered plane from DRM device's
57262306a36Sopenharmony_ci * list of planes with matching index. This is the inverse of drm_plane_index().
57362306a36Sopenharmony_ci */
57462306a36Sopenharmony_cistruct drm_plane *
57562306a36Sopenharmony_cidrm_plane_from_index(struct drm_device *dev, int idx)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	struct drm_plane *plane;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	drm_for_each_plane(plane, dev)
58062306a36Sopenharmony_ci		if (idx == plane->index)
58162306a36Sopenharmony_ci			return plane;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	return NULL;
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_plane_from_index);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci/**
58862306a36Sopenharmony_ci * drm_plane_force_disable - Forcibly disable a plane
58962306a36Sopenharmony_ci * @plane: plane to disable
59062306a36Sopenharmony_ci *
59162306a36Sopenharmony_ci * Forces the plane to be disabled.
59262306a36Sopenharmony_ci *
59362306a36Sopenharmony_ci * Used when the plane's current framebuffer is destroyed,
59462306a36Sopenharmony_ci * and when restoring fbdev mode.
59562306a36Sopenharmony_ci *
59662306a36Sopenharmony_ci * Note that this function is not suitable for atomic drivers, since it doesn't
59762306a36Sopenharmony_ci * wire through the lock acquisition context properly and hence can't handle
59862306a36Sopenharmony_ci * retries or driver private locks. You probably want to use
59962306a36Sopenharmony_ci * drm_atomic_helper_disable_plane() or
60062306a36Sopenharmony_ci * drm_atomic_helper_disable_planes_on_crtc() instead.
60162306a36Sopenharmony_ci */
60262306a36Sopenharmony_civoid drm_plane_force_disable(struct drm_plane *plane)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	int ret;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	if (!plane->fb)
60762306a36Sopenharmony_ci		return;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	WARN_ON(drm_drv_uses_atomic_modeset(plane->dev));
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	plane->old_fb = plane->fb;
61262306a36Sopenharmony_ci	ret = plane->funcs->disable_plane(plane, NULL);
61362306a36Sopenharmony_ci	if (ret) {
61462306a36Sopenharmony_ci		DRM_ERROR("failed to disable plane with busy fb\n");
61562306a36Sopenharmony_ci		plane->old_fb = NULL;
61662306a36Sopenharmony_ci		return;
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci	/* disconnect the plane from the fb and crtc: */
61962306a36Sopenharmony_ci	drm_framebuffer_put(plane->old_fb);
62062306a36Sopenharmony_ci	plane->old_fb = NULL;
62162306a36Sopenharmony_ci	plane->fb = NULL;
62262306a36Sopenharmony_ci	plane->crtc = NULL;
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_plane_force_disable);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci/**
62762306a36Sopenharmony_ci * drm_mode_plane_set_obj_prop - set the value of a property
62862306a36Sopenharmony_ci * @plane: drm plane object to set property value for
62962306a36Sopenharmony_ci * @property: property to set
63062306a36Sopenharmony_ci * @value: value the property should be set to
63162306a36Sopenharmony_ci *
63262306a36Sopenharmony_ci * This functions sets a given property on a given plane object. This function
63362306a36Sopenharmony_ci * calls the driver's ->set_property callback and changes the software state of
63462306a36Sopenharmony_ci * the property if the callback succeeds.
63562306a36Sopenharmony_ci *
63662306a36Sopenharmony_ci * Returns:
63762306a36Sopenharmony_ci * Zero on success, error code on failure.
63862306a36Sopenharmony_ci */
63962306a36Sopenharmony_ciint drm_mode_plane_set_obj_prop(struct drm_plane *plane,
64062306a36Sopenharmony_ci				struct drm_property *property,
64162306a36Sopenharmony_ci				uint64_t value)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	int ret = -EINVAL;
64462306a36Sopenharmony_ci	struct drm_mode_object *obj = &plane->base;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	if (plane->funcs->set_property)
64762306a36Sopenharmony_ci		ret = plane->funcs->set_property(plane, property, value);
64862306a36Sopenharmony_ci	if (!ret)
64962306a36Sopenharmony_ci		drm_object_property_set_value(obj, property, value);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	return ret;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_mode_plane_set_obj_prop);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ciint drm_mode_getplane_res(struct drm_device *dev, void *data,
65662306a36Sopenharmony_ci			  struct drm_file *file_priv)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	struct drm_mode_get_plane_res *plane_resp = data;
65962306a36Sopenharmony_ci	struct drm_plane *plane;
66062306a36Sopenharmony_ci	uint32_t __user *plane_ptr;
66162306a36Sopenharmony_ci	int count = 0;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_MODESET))
66462306a36Sopenharmony_ci		return -EOPNOTSUPP;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	plane_ptr = u64_to_user_ptr(plane_resp->plane_id_ptr);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/*
66962306a36Sopenharmony_ci	 * This ioctl is called twice, once to determine how much space is
67062306a36Sopenharmony_ci	 * needed, and the 2nd time to fill it.
67162306a36Sopenharmony_ci	 */
67262306a36Sopenharmony_ci	drm_for_each_plane(plane, dev) {
67362306a36Sopenharmony_ci		/*
67462306a36Sopenharmony_ci		 * Unless userspace set the 'universal planes'
67562306a36Sopenharmony_ci		 * capability bit, only advertise overlays.
67662306a36Sopenharmony_ci		 */
67762306a36Sopenharmony_ci		if (plane->type != DRM_PLANE_TYPE_OVERLAY &&
67862306a36Sopenharmony_ci		    !file_priv->universal_planes)
67962306a36Sopenharmony_ci			continue;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci		/*
68262306a36Sopenharmony_ci		 * If we're running on a virtualized driver then,
68362306a36Sopenharmony_ci		 * unless userspace advertizes support for the
68462306a36Sopenharmony_ci		 * virtualized cursor plane, disable cursor planes
68562306a36Sopenharmony_ci		 * because they'll be broken due to missing cursor
68662306a36Sopenharmony_ci		 * hotspot info.
68762306a36Sopenharmony_ci		 */
68862306a36Sopenharmony_ci		if (plane->type == DRM_PLANE_TYPE_CURSOR &&
68962306a36Sopenharmony_ci		    drm_core_check_feature(dev, DRIVER_CURSOR_HOTSPOT) &&
69062306a36Sopenharmony_ci		    file_priv->atomic &&
69162306a36Sopenharmony_ci		    !file_priv->supports_virtualized_cursor_plane)
69262306a36Sopenharmony_ci			continue;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci		if (drm_lease_held(file_priv, plane->base.id)) {
69562306a36Sopenharmony_ci			if (count < plane_resp->count_planes &&
69662306a36Sopenharmony_ci			    put_user(plane->base.id, plane_ptr + count))
69762306a36Sopenharmony_ci				return -EFAULT;
69862306a36Sopenharmony_ci			count++;
69962306a36Sopenharmony_ci		}
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci	plane_resp->count_planes = count;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	return 0;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ciint drm_mode_getplane(struct drm_device *dev, void *data,
70762306a36Sopenharmony_ci		      struct drm_file *file_priv)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct drm_mode_get_plane *plane_resp = data;
71062306a36Sopenharmony_ci	struct drm_plane *plane;
71162306a36Sopenharmony_ci	uint32_t __user *format_ptr;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_MODESET))
71462306a36Sopenharmony_ci		return -EOPNOTSUPP;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	plane = drm_plane_find(dev, file_priv, plane_resp->plane_id);
71762306a36Sopenharmony_ci	if (!plane)
71862306a36Sopenharmony_ci		return -ENOENT;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	drm_modeset_lock(&plane->mutex, NULL);
72162306a36Sopenharmony_ci	if (plane->state && plane->state->crtc && drm_lease_held(file_priv, plane->state->crtc->base.id))
72262306a36Sopenharmony_ci		plane_resp->crtc_id = plane->state->crtc->base.id;
72362306a36Sopenharmony_ci	else if (!plane->state && plane->crtc && drm_lease_held(file_priv, plane->crtc->base.id))
72462306a36Sopenharmony_ci		plane_resp->crtc_id = plane->crtc->base.id;
72562306a36Sopenharmony_ci	else
72662306a36Sopenharmony_ci		plane_resp->crtc_id = 0;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	if (plane->state && plane->state->fb)
72962306a36Sopenharmony_ci		plane_resp->fb_id = plane->state->fb->base.id;
73062306a36Sopenharmony_ci	else if (!plane->state && plane->fb)
73162306a36Sopenharmony_ci		plane_resp->fb_id = plane->fb->base.id;
73262306a36Sopenharmony_ci	else
73362306a36Sopenharmony_ci		plane_resp->fb_id = 0;
73462306a36Sopenharmony_ci	drm_modeset_unlock(&plane->mutex);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	plane_resp->plane_id = plane->base.id;
73762306a36Sopenharmony_ci	plane_resp->possible_crtcs = drm_lease_filter_crtcs(file_priv,
73862306a36Sopenharmony_ci							    plane->possible_crtcs);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	plane_resp->gamma_size = 0;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	/*
74362306a36Sopenharmony_ci	 * This ioctl is called twice, once to determine how much space is
74462306a36Sopenharmony_ci	 * needed, and the 2nd time to fill it.
74562306a36Sopenharmony_ci	 */
74662306a36Sopenharmony_ci	if (plane->format_count &&
74762306a36Sopenharmony_ci	    (plane_resp->count_format_types >= plane->format_count)) {
74862306a36Sopenharmony_ci		format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr;
74962306a36Sopenharmony_ci		if (copy_to_user(format_ptr,
75062306a36Sopenharmony_ci				 plane->format_types,
75162306a36Sopenharmony_ci				 sizeof(uint32_t) * plane->format_count)) {
75262306a36Sopenharmony_ci			return -EFAULT;
75362306a36Sopenharmony_ci		}
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ci	plane_resp->count_format_types = plane->format_count;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	return 0;
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ciint drm_plane_check_pixel_format(struct drm_plane *plane,
76162306a36Sopenharmony_ci				 u32 format, u64 modifier)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	unsigned int i;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	for (i = 0; i < plane->format_count; i++) {
76662306a36Sopenharmony_ci		if (format == plane->format_types[i])
76762306a36Sopenharmony_ci			break;
76862306a36Sopenharmony_ci	}
76962306a36Sopenharmony_ci	if (i == plane->format_count)
77062306a36Sopenharmony_ci		return -EINVAL;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	if (plane->funcs->format_mod_supported) {
77362306a36Sopenharmony_ci		if (!plane->funcs->format_mod_supported(plane, format, modifier))
77462306a36Sopenharmony_ci			return -EINVAL;
77562306a36Sopenharmony_ci	} else {
77662306a36Sopenharmony_ci		if (!plane->modifier_count)
77762306a36Sopenharmony_ci			return 0;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci		for (i = 0; i < plane->modifier_count; i++) {
78062306a36Sopenharmony_ci			if (modifier == plane->modifiers[i])
78162306a36Sopenharmony_ci				break;
78262306a36Sopenharmony_ci		}
78362306a36Sopenharmony_ci		if (i == plane->modifier_count)
78462306a36Sopenharmony_ci			return -EINVAL;
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	return 0;
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic int __setplane_check(struct drm_plane *plane,
79162306a36Sopenharmony_ci			    struct drm_crtc *crtc,
79262306a36Sopenharmony_ci			    struct drm_framebuffer *fb,
79362306a36Sopenharmony_ci			    int32_t crtc_x, int32_t crtc_y,
79462306a36Sopenharmony_ci			    uint32_t crtc_w, uint32_t crtc_h,
79562306a36Sopenharmony_ci			    uint32_t src_x, uint32_t src_y,
79662306a36Sopenharmony_ci			    uint32_t src_w, uint32_t src_h)
79762306a36Sopenharmony_ci{
79862306a36Sopenharmony_ci	int ret;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	/* Check whether this plane is usable on this CRTC */
80162306a36Sopenharmony_ci	if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) {
80262306a36Sopenharmony_ci		DRM_DEBUG_KMS("Invalid crtc for plane\n");
80362306a36Sopenharmony_ci		return -EINVAL;
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	/* Check whether this plane supports the fb pixel format. */
80762306a36Sopenharmony_ci	ret = drm_plane_check_pixel_format(plane, fb->format->format,
80862306a36Sopenharmony_ci					   fb->modifier);
80962306a36Sopenharmony_ci	if (ret) {
81062306a36Sopenharmony_ci		DRM_DEBUG_KMS("Invalid pixel format %p4cc, modifier 0x%llx\n",
81162306a36Sopenharmony_ci			      &fb->format->format, fb->modifier);
81262306a36Sopenharmony_ci		return ret;
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	/* Give drivers some help against integer overflows */
81662306a36Sopenharmony_ci	if (crtc_w > INT_MAX ||
81762306a36Sopenharmony_ci	    crtc_x > INT_MAX - (int32_t) crtc_w ||
81862306a36Sopenharmony_ci	    crtc_h > INT_MAX ||
81962306a36Sopenharmony_ci	    crtc_y > INT_MAX - (int32_t) crtc_h) {
82062306a36Sopenharmony_ci		DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
82162306a36Sopenharmony_ci			      crtc_w, crtc_h, crtc_x, crtc_y);
82262306a36Sopenharmony_ci		return -ERANGE;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	ret = drm_framebuffer_check_src_coords(src_x, src_y, src_w, src_h, fb);
82662306a36Sopenharmony_ci	if (ret)
82762306a36Sopenharmony_ci		return ret;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	return 0;
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci/**
83362306a36Sopenharmony_ci * drm_any_plane_has_format - Check whether any plane supports this format and modifier combination
83462306a36Sopenharmony_ci * @dev: DRM device
83562306a36Sopenharmony_ci * @format: pixel format (DRM_FORMAT_*)
83662306a36Sopenharmony_ci * @modifier: data layout modifier
83762306a36Sopenharmony_ci *
83862306a36Sopenharmony_ci * Returns:
83962306a36Sopenharmony_ci * Whether at least one plane supports the specified format and modifier combination.
84062306a36Sopenharmony_ci */
84162306a36Sopenharmony_cibool drm_any_plane_has_format(struct drm_device *dev,
84262306a36Sopenharmony_ci			      u32 format, u64 modifier)
84362306a36Sopenharmony_ci{
84462306a36Sopenharmony_ci	struct drm_plane *plane;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	drm_for_each_plane(plane, dev) {
84762306a36Sopenharmony_ci		if (drm_plane_check_pixel_format(plane, format, modifier) == 0)
84862306a36Sopenharmony_ci			return true;
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	return false;
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_any_plane_has_format);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci/*
85662306a36Sopenharmony_ci * __setplane_internal - setplane handler for internal callers
85762306a36Sopenharmony_ci *
85862306a36Sopenharmony_ci * This function will take a reference on the new fb for the plane
85962306a36Sopenharmony_ci * on success.
86062306a36Sopenharmony_ci *
86162306a36Sopenharmony_ci * src_{x,y,w,h} are provided in 16.16 fixed point format
86262306a36Sopenharmony_ci */
86362306a36Sopenharmony_cistatic int __setplane_internal(struct drm_plane *plane,
86462306a36Sopenharmony_ci			       struct drm_crtc *crtc,
86562306a36Sopenharmony_ci			       struct drm_framebuffer *fb,
86662306a36Sopenharmony_ci			       int32_t crtc_x, int32_t crtc_y,
86762306a36Sopenharmony_ci			       uint32_t crtc_w, uint32_t crtc_h,
86862306a36Sopenharmony_ci			       /* src_{x,y,w,h} values are 16.16 fixed point */
86962306a36Sopenharmony_ci			       uint32_t src_x, uint32_t src_y,
87062306a36Sopenharmony_ci			       uint32_t src_w, uint32_t src_h,
87162306a36Sopenharmony_ci			       struct drm_modeset_acquire_ctx *ctx)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	int ret = 0;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	WARN_ON(drm_drv_uses_atomic_modeset(plane->dev));
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	/* No fb means shut it down */
87862306a36Sopenharmony_ci	if (!fb) {
87962306a36Sopenharmony_ci		plane->old_fb = plane->fb;
88062306a36Sopenharmony_ci		ret = plane->funcs->disable_plane(plane, ctx);
88162306a36Sopenharmony_ci		if (!ret) {
88262306a36Sopenharmony_ci			plane->crtc = NULL;
88362306a36Sopenharmony_ci			plane->fb = NULL;
88462306a36Sopenharmony_ci		} else {
88562306a36Sopenharmony_ci			plane->old_fb = NULL;
88662306a36Sopenharmony_ci		}
88762306a36Sopenharmony_ci		goto out;
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	ret = __setplane_check(plane, crtc, fb,
89162306a36Sopenharmony_ci			       crtc_x, crtc_y, crtc_w, crtc_h,
89262306a36Sopenharmony_ci			       src_x, src_y, src_w, src_h);
89362306a36Sopenharmony_ci	if (ret)
89462306a36Sopenharmony_ci		goto out;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	plane->old_fb = plane->fb;
89762306a36Sopenharmony_ci	ret = plane->funcs->update_plane(plane, crtc, fb,
89862306a36Sopenharmony_ci					 crtc_x, crtc_y, crtc_w, crtc_h,
89962306a36Sopenharmony_ci					 src_x, src_y, src_w, src_h, ctx);
90062306a36Sopenharmony_ci	if (!ret) {
90162306a36Sopenharmony_ci		plane->crtc = crtc;
90262306a36Sopenharmony_ci		plane->fb = fb;
90362306a36Sopenharmony_ci		drm_framebuffer_get(plane->fb);
90462306a36Sopenharmony_ci	} else {
90562306a36Sopenharmony_ci		plane->old_fb = NULL;
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ciout:
90962306a36Sopenharmony_ci	if (plane->old_fb)
91062306a36Sopenharmony_ci		drm_framebuffer_put(plane->old_fb);
91162306a36Sopenharmony_ci	plane->old_fb = NULL;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	return ret;
91462306a36Sopenharmony_ci}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_cistatic int __setplane_atomic(struct drm_plane *plane,
91762306a36Sopenharmony_ci			     struct drm_crtc *crtc,
91862306a36Sopenharmony_ci			     struct drm_framebuffer *fb,
91962306a36Sopenharmony_ci			     int32_t crtc_x, int32_t crtc_y,
92062306a36Sopenharmony_ci			     uint32_t crtc_w, uint32_t crtc_h,
92162306a36Sopenharmony_ci			     uint32_t src_x, uint32_t src_y,
92262306a36Sopenharmony_ci			     uint32_t src_w, uint32_t src_h,
92362306a36Sopenharmony_ci			     struct drm_modeset_acquire_ctx *ctx)
92462306a36Sopenharmony_ci{
92562306a36Sopenharmony_ci	int ret;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	WARN_ON(!drm_drv_uses_atomic_modeset(plane->dev));
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	/* No fb means shut it down */
93062306a36Sopenharmony_ci	if (!fb)
93162306a36Sopenharmony_ci		return plane->funcs->disable_plane(plane, ctx);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	/*
93462306a36Sopenharmony_ci	 * FIXME: This is redundant with drm_atomic_plane_check(),
93562306a36Sopenharmony_ci	 * but the legacy cursor/"async" .update_plane() tricks
93662306a36Sopenharmony_ci	 * don't call that so we still need this here. Should remove
93762306a36Sopenharmony_ci	 * this when all .update_plane() implementations have been
93862306a36Sopenharmony_ci	 * fixed to call drm_atomic_plane_check().
93962306a36Sopenharmony_ci	 */
94062306a36Sopenharmony_ci	ret = __setplane_check(plane, crtc, fb,
94162306a36Sopenharmony_ci			       crtc_x, crtc_y, crtc_w, crtc_h,
94262306a36Sopenharmony_ci			       src_x, src_y, src_w, src_h);
94362306a36Sopenharmony_ci	if (ret)
94462306a36Sopenharmony_ci		return ret;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	return plane->funcs->update_plane(plane, crtc, fb,
94762306a36Sopenharmony_ci					  crtc_x, crtc_y, crtc_w, crtc_h,
94862306a36Sopenharmony_ci					  src_x, src_y, src_w, src_h, ctx);
94962306a36Sopenharmony_ci}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_cistatic int setplane_internal(struct drm_plane *plane,
95262306a36Sopenharmony_ci			     struct drm_crtc *crtc,
95362306a36Sopenharmony_ci			     struct drm_framebuffer *fb,
95462306a36Sopenharmony_ci			     int32_t crtc_x, int32_t crtc_y,
95562306a36Sopenharmony_ci			     uint32_t crtc_w, uint32_t crtc_h,
95662306a36Sopenharmony_ci			     /* src_{x,y,w,h} values are 16.16 fixed point */
95762306a36Sopenharmony_ci			     uint32_t src_x, uint32_t src_y,
95862306a36Sopenharmony_ci			     uint32_t src_w, uint32_t src_h)
95962306a36Sopenharmony_ci{
96062306a36Sopenharmony_ci	struct drm_modeset_acquire_ctx ctx;
96162306a36Sopenharmony_ci	int ret;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	DRM_MODESET_LOCK_ALL_BEGIN(plane->dev, ctx,
96462306a36Sopenharmony_ci				   DRM_MODESET_ACQUIRE_INTERRUPTIBLE, ret);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	if (drm_drv_uses_atomic_modeset(plane->dev))
96762306a36Sopenharmony_ci		ret = __setplane_atomic(plane, crtc, fb,
96862306a36Sopenharmony_ci					crtc_x, crtc_y, crtc_w, crtc_h,
96962306a36Sopenharmony_ci					src_x, src_y, src_w, src_h, &ctx);
97062306a36Sopenharmony_ci	else
97162306a36Sopenharmony_ci		ret = __setplane_internal(plane, crtc, fb,
97262306a36Sopenharmony_ci					  crtc_x, crtc_y, crtc_w, crtc_h,
97362306a36Sopenharmony_ci					  src_x, src_y, src_w, src_h, &ctx);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	DRM_MODESET_LOCK_ALL_END(plane->dev, ctx, ret);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	return ret;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ciint drm_mode_setplane(struct drm_device *dev, void *data,
98162306a36Sopenharmony_ci		      struct drm_file *file_priv)
98262306a36Sopenharmony_ci{
98362306a36Sopenharmony_ci	struct drm_mode_set_plane *plane_req = data;
98462306a36Sopenharmony_ci	struct drm_plane *plane;
98562306a36Sopenharmony_ci	struct drm_crtc *crtc = NULL;
98662306a36Sopenharmony_ci	struct drm_framebuffer *fb = NULL;
98762306a36Sopenharmony_ci	int ret;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_MODESET))
99062306a36Sopenharmony_ci		return -EOPNOTSUPP;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	/*
99362306a36Sopenharmony_ci	 * First, find the plane, crtc, and fb objects.  If not available,
99462306a36Sopenharmony_ci	 * we don't bother to call the driver.
99562306a36Sopenharmony_ci	 */
99662306a36Sopenharmony_ci	plane = drm_plane_find(dev, file_priv, plane_req->plane_id);
99762306a36Sopenharmony_ci	if (!plane) {
99862306a36Sopenharmony_ci		DRM_DEBUG_KMS("Unknown plane ID %d\n",
99962306a36Sopenharmony_ci			      plane_req->plane_id);
100062306a36Sopenharmony_ci		return -ENOENT;
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	if (plane_req->fb_id) {
100462306a36Sopenharmony_ci		fb = drm_framebuffer_lookup(dev, file_priv, plane_req->fb_id);
100562306a36Sopenharmony_ci		if (!fb) {
100662306a36Sopenharmony_ci			DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
100762306a36Sopenharmony_ci				      plane_req->fb_id);
100862306a36Sopenharmony_ci			return -ENOENT;
100962306a36Sopenharmony_ci		}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci		crtc = drm_crtc_find(dev, file_priv, plane_req->crtc_id);
101262306a36Sopenharmony_ci		if (!crtc) {
101362306a36Sopenharmony_ci			drm_framebuffer_put(fb);
101462306a36Sopenharmony_ci			DRM_DEBUG_KMS("Unknown crtc ID %d\n",
101562306a36Sopenharmony_ci				      plane_req->crtc_id);
101662306a36Sopenharmony_ci			return -ENOENT;
101762306a36Sopenharmony_ci		}
101862306a36Sopenharmony_ci	}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	ret = setplane_internal(plane, crtc, fb,
102162306a36Sopenharmony_ci				plane_req->crtc_x, plane_req->crtc_y,
102262306a36Sopenharmony_ci				plane_req->crtc_w, plane_req->crtc_h,
102362306a36Sopenharmony_ci				plane_req->src_x, plane_req->src_y,
102462306a36Sopenharmony_ci				plane_req->src_w, plane_req->src_h);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	if (fb)
102762306a36Sopenharmony_ci		drm_framebuffer_put(fb);
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	return ret;
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_cistatic int drm_mode_cursor_universal(struct drm_crtc *crtc,
103362306a36Sopenharmony_ci				     struct drm_mode_cursor2 *req,
103462306a36Sopenharmony_ci				     struct drm_file *file_priv,
103562306a36Sopenharmony_ci				     struct drm_modeset_acquire_ctx *ctx)
103662306a36Sopenharmony_ci{
103762306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
103862306a36Sopenharmony_ci	struct drm_plane *plane = crtc->cursor;
103962306a36Sopenharmony_ci	struct drm_framebuffer *fb = NULL;
104062306a36Sopenharmony_ci	struct drm_mode_fb_cmd2 fbreq = {
104162306a36Sopenharmony_ci		.width = req->width,
104262306a36Sopenharmony_ci		.height = req->height,
104362306a36Sopenharmony_ci		.pixel_format = DRM_FORMAT_ARGB8888,
104462306a36Sopenharmony_ci		.pitches = { req->width * 4 },
104562306a36Sopenharmony_ci		.handles = { req->handle },
104662306a36Sopenharmony_ci	};
104762306a36Sopenharmony_ci	int32_t crtc_x, crtc_y;
104862306a36Sopenharmony_ci	uint32_t crtc_w = 0, crtc_h = 0;
104962306a36Sopenharmony_ci	uint32_t src_w = 0, src_h = 0;
105062306a36Sopenharmony_ci	int ret = 0;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	BUG_ON(!plane);
105362306a36Sopenharmony_ci	WARN_ON(plane->crtc != crtc && plane->crtc != NULL);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	/*
105662306a36Sopenharmony_ci	 * Obtain fb we'll be using (either new or existing) and take an extra
105762306a36Sopenharmony_ci	 * reference to it if fb != null.  setplane will take care of dropping
105862306a36Sopenharmony_ci	 * the reference if the plane update fails.
105962306a36Sopenharmony_ci	 */
106062306a36Sopenharmony_ci	if (req->flags & DRM_MODE_CURSOR_BO) {
106162306a36Sopenharmony_ci		if (req->handle) {
106262306a36Sopenharmony_ci			fb = drm_internal_framebuffer_create(dev, &fbreq, file_priv);
106362306a36Sopenharmony_ci			if (IS_ERR(fb)) {
106462306a36Sopenharmony_ci				DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n");
106562306a36Sopenharmony_ci				return PTR_ERR(fb);
106662306a36Sopenharmony_ci			}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci			fb->hot_x = req->hot_x;
106962306a36Sopenharmony_ci			fb->hot_y = req->hot_y;
107062306a36Sopenharmony_ci		} else {
107162306a36Sopenharmony_ci			fb = NULL;
107262306a36Sopenharmony_ci		}
107362306a36Sopenharmony_ci	} else {
107462306a36Sopenharmony_ci		if (plane->state)
107562306a36Sopenharmony_ci			fb = plane->state->fb;
107662306a36Sopenharmony_ci		else
107762306a36Sopenharmony_ci			fb = plane->fb;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci		if (fb)
108062306a36Sopenharmony_ci			drm_framebuffer_get(fb);
108162306a36Sopenharmony_ci	}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	if (req->flags & DRM_MODE_CURSOR_MOVE) {
108462306a36Sopenharmony_ci		crtc_x = req->x;
108562306a36Sopenharmony_ci		crtc_y = req->y;
108662306a36Sopenharmony_ci	} else {
108762306a36Sopenharmony_ci		crtc_x = crtc->cursor_x;
108862306a36Sopenharmony_ci		crtc_y = crtc->cursor_y;
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	if (fb) {
109262306a36Sopenharmony_ci		crtc_w = fb->width;
109362306a36Sopenharmony_ci		crtc_h = fb->height;
109462306a36Sopenharmony_ci		src_w = fb->width << 16;
109562306a36Sopenharmony_ci		src_h = fb->height << 16;
109662306a36Sopenharmony_ci	}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (drm_drv_uses_atomic_modeset(dev))
109962306a36Sopenharmony_ci		ret = __setplane_atomic(plane, crtc, fb,
110062306a36Sopenharmony_ci					crtc_x, crtc_y, crtc_w, crtc_h,
110162306a36Sopenharmony_ci					0, 0, src_w, src_h, ctx);
110262306a36Sopenharmony_ci	else
110362306a36Sopenharmony_ci		ret = __setplane_internal(plane, crtc, fb,
110462306a36Sopenharmony_ci					  crtc_x, crtc_y, crtc_w, crtc_h,
110562306a36Sopenharmony_ci					  0, 0, src_w, src_h, ctx);
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	if (fb)
110862306a36Sopenharmony_ci		drm_framebuffer_put(fb);
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	/* Update successful; save new cursor position, if necessary */
111162306a36Sopenharmony_ci	if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) {
111262306a36Sopenharmony_ci		crtc->cursor_x = req->x;
111362306a36Sopenharmony_ci		crtc->cursor_y = req->y;
111462306a36Sopenharmony_ci	}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	return ret;
111762306a36Sopenharmony_ci}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_cistatic int drm_mode_cursor_common(struct drm_device *dev,
112062306a36Sopenharmony_ci				  struct drm_mode_cursor2 *req,
112162306a36Sopenharmony_ci				  struct drm_file *file_priv)
112262306a36Sopenharmony_ci{
112362306a36Sopenharmony_ci	struct drm_crtc *crtc;
112462306a36Sopenharmony_ci	struct drm_modeset_acquire_ctx ctx;
112562306a36Sopenharmony_ci	int ret = 0;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_MODESET))
112862306a36Sopenharmony_ci		return -EOPNOTSUPP;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
113162306a36Sopenharmony_ci		return -EINVAL;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	crtc = drm_crtc_find(dev, file_priv, req->crtc_id);
113462306a36Sopenharmony_ci	if (!crtc) {
113562306a36Sopenharmony_ci		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
113662306a36Sopenharmony_ci		return -ENOENT;
113762306a36Sopenharmony_ci	}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
114062306a36Sopenharmony_ciretry:
114162306a36Sopenharmony_ci	ret = drm_modeset_lock(&crtc->mutex, &ctx);
114262306a36Sopenharmony_ci	if (ret)
114362306a36Sopenharmony_ci		goto out;
114462306a36Sopenharmony_ci	/*
114562306a36Sopenharmony_ci	 * If this crtc has a universal cursor plane, call that plane's update
114662306a36Sopenharmony_ci	 * handler rather than using legacy cursor handlers.
114762306a36Sopenharmony_ci	 */
114862306a36Sopenharmony_ci	if (crtc->cursor) {
114962306a36Sopenharmony_ci		ret = drm_modeset_lock(&crtc->cursor->mutex, &ctx);
115062306a36Sopenharmony_ci		if (ret)
115162306a36Sopenharmony_ci			goto out;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci		if (!drm_lease_held(file_priv, crtc->cursor->base.id)) {
115462306a36Sopenharmony_ci			ret = -EACCES;
115562306a36Sopenharmony_ci			goto out;
115662306a36Sopenharmony_ci		}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci		ret = drm_mode_cursor_universal(crtc, req, file_priv, &ctx);
115962306a36Sopenharmony_ci		goto out;
116062306a36Sopenharmony_ci	}
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	if (req->flags & DRM_MODE_CURSOR_BO) {
116362306a36Sopenharmony_ci		if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
116462306a36Sopenharmony_ci			ret = -ENXIO;
116562306a36Sopenharmony_ci			goto out;
116662306a36Sopenharmony_ci		}
116762306a36Sopenharmony_ci		/* Turns off the cursor if handle is 0 */
116862306a36Sopenharmony_ci		if (crtc->funcs->cursor_set2)
116962306a36Sopenharmony_ci			ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle,
117062306a36Sopenharmony_ci						      req->width, req->height, req->hot_x, req->hot_y);
117162306a36Sopenharmony_ci		else
117262306a36Sopenharmony_ci			ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle,
117362306a36Sopenharmony_ci						      req->width, req->height);
117462306a36Sopenharmony_ci	}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	if (req->flags & DRM_MODE_CURSOR_MOVE) {
117762306a36Sopenharmony_ci		if (crtc->funcs->cursor_move) {
117862306a36Sopenharmony_ci			ret = crtc->funcs->cursor_move(crtc, req->x, req->y);
117962306a36Sopenharmony_ci		} else {
118062306a36Sopenharmony_ci			ret = -EFAULT;
118162306a36Sopenharmony_ci			goto out;
118262306a36Sopenharmony_ci		}
118362306a36Sopenharmony_ci	}
118462306a36Sopenharmony_ciout:
118562306a36Sopenharmony_ci	if (ret == -EDEADLK) {
118662306a36Sopenharmony_ci		ret = drm_modeset_backoff(&ctx);
118762306a36Sopenharmony_ci		if (!ret)
118862306a36Sopenharmony_ci			goto retry;
118962306a36Sopenharmony_ci	}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	drm_modeset_drop_locks(&ctx);
119262306a36Sopenharmony_ci	drm_modeset_acquire_fini(&ctx);
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	return ret;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ciint drm_mode_cursor_ioctl(struct drm_device *dev,
120062306a36Sopenharmony_ci			  void *data, struct drm_file *file_priv)
120162306a36Sopenharmony_ci{
120262306a36Sopenharmony_ci	struct drm_mode_cursor *req = data;
120362306a36Sopenharmony_ci	struct drm_mode_cursor2 new_req;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	memcpy(&new_req, req, sizeof(struct drm_mode_cursor));
120662306a36Sopenharmony_ci	new_req.hot_x = new_req.hot_y = 0;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	return drm_mode_cursor_common(dev, &new_req, file_priv);
120962306a36Sopenharmony_ci}
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci/*
121262306a36Sopenharmony_ci * Set the cursor configuration based on user request. This implements the 2nd
121362306a36Sopenharmony_ci * version of the cursor ioctl, which allows userspace to additionally specify
121462306a36Sopenharmony_ci * the hotspot of the pointer.
121562306a36Sopenharmony_ci */
121662306a36Sopenharmony_ciint drm_mode_cursor2_ioctl(struct drm_device *dev,
121762306a36Sopenharmony_ci			   void *data, struct drm_file *file_priv)
121862306a36Sopenharmony_ci{
121962306a36Sopenharmony_ci	struct drm_mode_cursor2 *req = data;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	return drm_mode_cursor_common(dev, req, file_priv);
122262306a36Sopenharmony_ci}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ciint drm_mode_page_flip_ioctl(struct drm_device *dev,
122562306a36Sopenharmony_ci			     void *data, struct drm_file *file_priv)
122662306a36Sopenharmony_ci{
122762306a36Sopenharmony_ci	struct drm_mode_crtc_page_flip_target *page_flip = data;
122862306a36Sopenharmony_ci	struct drm_crtc *crtc;
122962306a36Sopenharmony_ci	struct drm_plane *plane;
123062306a36Sopenharmony_ci	struct drm_framebuffer *fb = NULL, *old_fb;
123162306a36Sopenharmony_ci	struct drm_pending_vblank_event *e = NULL;
123262306a36Sopenharmony_ci	u32 target_vblank = page_flip->sequence;
123362306a36Sopenharmony_ci	struct drm_modeset_acquire_ctx ctx;
123462306a36Sopenharmony_ci	int ret = -EINVAL;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_MODESET))
123762306a36Sopenharmony_ci		return -EOPNOTSUPP;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS)
124062306a36Sopenharmony_ci		return -EINVAL;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	if (page_flip->sequence != 0 && !(page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET))
124362306a36Sopenharmony_ci		return -EINVAL;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	/* Only one of the DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags
124662306a36Sopenharmony_ci	 * can be specified
124762306a36Sopenharmony_ci	 */
124862306a36Sopenharmony_ci	if ((page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) == DRM_MODE_PAGE_FLIP_TARGET)
124962306a36Sopenharmony_ci		return -EINVAL;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip)
125262306a36Sopenharmony_ci		return -EINVAL;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	crtc = drm_crtc_find(dev, file_priv, page_flip->crtc_id);
125562306a36Sopenharmony_ci	if (!crtc)
125662306a36Sopenharmony_ci		return -ENOENT;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	plane = crtc->primary;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	if (!drm_lease_held(file_priv, plane->base.id))
126162306a36Sopenharmony_ci		return -EACCES;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	if (crtc->funcs->page_flip_target) {
126462306a36Sopenharmony_ci		u32 current_vblank;
126562306a36Sopenharmony_ci		int r;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci		r = drm_crtc_vblank_get(crtc);
126862306a36Sopenharmony_ci		if (r)
126962306a36Sopenharmony_ci			return r;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci		current_vblank = (u32)drm_crtc_vblank_count(crtc);
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci		switch (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) {
127462306a36Sopenharmony_ci		case DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE:
127562306a36Sopenharmony_ci			if ((int)(target_vblank - current_vblank) > 1) {
127662306a36Sopenharmony_ci				DRM_DEBUG("Invalid absolute flip target %u, "
127762306a36Sopenharmony_ci					  "must be <= %u\n", target_vblank,
127862306a36Sopenharmony_ci					  current_vblank + 1);
127962306a36Sopenharmony_ci				drm_crtc_vblank_put(crtc);
128062306a36Sopenharmony_ci				return -EINVAL;
128162306a36Sopenharmony_ci			}
128262306a36Sopenharmony_ci			break;
128362306a36Sopenharmony_ci		case DRM_MODE_PAGE_FLIP_TARGET_RELATIVE:
128462306a36Sopenharmony_ci			if (target_vblank != 0 && target_vblank != 1) {
128562306a36Sopenharmony_ci				DRM_DEBUG("Invalid relative flip target %u, "
128662306a36Sopenharmony_ci					  "must be 0 or 1\n", target_vblank);
128762306a36Sopenharmony_ci				drm_crtc_vblank_put(crtc);
128862306a36Sopenharmony_ci				return -EINVAL;
128962306a36Sopenharmony_ci			}
129062306a36Sopenharmony_ci			target_vblank += current_vblank;
129162306a36Sopenharmony_ci			break;
129262306a36Sopenharmony_ci		default:
129362306a36Sopenharmony_ci			target_vblank = current_vblank +
129462306a36Sopenharmony_ci				!(page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC);
129562306a36Sopenharmony_ci			break;
129662306a36Sopenharmony_ci		}
129762306a36Sopenharmony_ci	} else if (crtc->funcs->page_flip == NULL ||
129862306a36Sopenharmony_ci		   (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET)) {
129962306a36Sopenharmony_ci		return -EINVAL;
130062306a36Sopenharmony_ci	}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
130362306a36Sopenharmony_ciretry:
130462306a36Sopenharmony_ci	ret = drm_modeset_lock(&crtc->mutex, &ctx);
130562306a36Sopenharmony_ci	if (ret)
130662306a36Sopenharmony_ci		goto out;
130762306a36Sopenharmony_ci	ret = drm_modeset_lock(&plane->mutex, &ctx);
130862306a36Sopenharmony_ci	if (ret)
130962306a36Sopenharmony_ci		goto out;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	if (plane->state)
131262306a36Sopenharmony_ci		old_fb = plane->state->fb;
131362306a36Sopenharmony_ci	else
131462306a36Sopenharmony_ci		old_fb = plane->fb;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	if (old_fb == NULL) {
131762306a36Sopenharmony_ci		/* The framebuffer is currently unbound, presumably
131862306a36Sopenharmony_ci		 * due to a hotplug event, that userspace has not
131962306a36Sopenharmony_ci		 * yet discovered.
132062306a36Sopenharmony_ci		 */
132162306a36Sopenharmony_ci		ret = -EBUSY;
132262306a36Sopenharmony_ci		goto out;
132362306a36Sopenharmony_ci	}
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	fb = drm_framebuffer_lookup(dev, file_priv, page_flip->fb_id);
132662306a36Sopenharmony_ci	if (!fb) {
132762306a36Sopenharmony_ci		ret = -ENOENT;
132862306a36Sopenharmony_ci		goto out;
132962306a36Sopenharmony_ci	}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	if (plane->state) {
133262306a36Sopenharmony_ci		const struct drm_plane_state *state = plane->state;
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci		ret = drm_framebuffer_check_src_coords(state->src_x,
133562306a36Sopenharmony_ci						       state->src_y,
133662306a36Sopenharmony_ci						       state->src_w,
133762306a36Sopenharmony_ci						       state->src_h,
133862306a36Sopenharmony_ci						       fb);
133962306a36Sopenharmony_ci	} else {
134062306a36Sopenharmony_ci		ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y,
134162306a36Sopenharmony_ci					      &crtc->mode, fb);
134262306a36Sopenharmony_ci	}
134362306a36Sopenharmony_ci	if (ret)
134462306a36Sopenharmony_ci		goto out;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	/*
134762306a36Sopenharmony_ci	 * Only check the FOURCC format code, excluding modifiers. This is
134862306a36Sopenharmony_ci	 * enough for all legacy drivers. Atomic drivers have their own
134962306a36Sopenharmony_ci	 * checks in their ->atomic_check implementation, which will
135062306a36Sopenharmony_ci	 * return -EINVAL if any hw or driver constraint is violated due
135162306a36Sopenharmony_ci	 * to modifier changes.
135262306a36Sopenharmony_ci	 */
135362306a36Sopenharmony_ci	if (old_fb->format->format != fb->format->format) {
135462306a36Sopenharmony_ci		DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
135562306a36Sopenharmony_ci		ret = -EINVAL;
135662306a36Sopenharmony_ci		goto out;
135762306a36Sopenharmony_ci	}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
136062306a36Sopenharmony_ci		e = kzalloc(sizeof *e, GFP_KERNEL);
136162306a36Sopenharmony_ci		if (!e) {
136262306a36Sopenharmony_ci			ret = -ENOMEM;
136362306a36Sopenharmony_ci			goto out;
136462306a36Sopenharmony_ci		}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci		e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
136762306a36Sopenharmony_ci		e->event.base.length = sizeof(e->event);
136862306a36Sopenharmony_ci		e->event.vbl.user_data = page_flip->user_data;
136962306a36Sopenharmony_ci		e->event.vbl.crtc_id = crtc->base.id;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci		ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base);
137262306a36Sopenharmony_ci		if (ret) {
137362306a36Sopenharmony_ci			kfree(e);
137462306a36Sopenharmony_ci			e = NULL;
137562306a36Sopenharmony_ci			goto out;
137662306a36Sopenharmony_ci		}
137762306a36Sopenharmony_ci	}
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	plane->old_fb = plane->fb;
138062306a36Sopenharmony_ci	if (crtc->funcs->page_flip_target)
138162306a36Sopenharmony_ci		ret = crtc->funcs->page_flip_target(crtc, fb, e,
138262306a36Sopenharmony_ci						    page_flip->flags,
138362306a36Sopenharmony_ci						    target_vblank,
138462306a36Sopenharmony_ci						    &ctx);
138562306a36Sopenharmony_ci	else
138662306a36Sopenharmony_ci		ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags,
138762306a36Sopenharmony_ci					     &ctx);
138862306a36Sopenharmony_ci	if (ret) {
138962306a36Sopenharmony_ci		if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT)
139062306a36Sopenharmony_ci			drm_event_cancel_free(dev, &e->base);
139162306a36Sopenharmony_ci		/* Keep the old fb, don't unref it. */
139262306a36Sopenharmony_ci		plane->old_fb = NULL;
139362306a36Sopenharmony_ci	} else {
139462306a36Sopenharmony_ci		if (!plane->state) {
139562306a36Sopenharmony_ci			plane->fb = fb;
139662306a36Sopenharmony_ci			drm_framebuffer_get(fb);
139762306a36Sopenharmony_ci		}
139862306a36Sopenharmony_ci	}
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ciout:
140162306a36Sopenharmony_ci	if (fb)
140262306a36Sopenharmony_ci		drm_framebuffer_put(fb);
140362306a36Sopenharmony_ci	fb = NULL;
140462306a36Sopenharmony_ci	if (plane->old_fb)
140562306a36Sopenharmony_ci		drm_framebuffer_put(plane->old_fb);
140662306a36Sopenharmony_ci	plane->old_fb = NULL;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	if (ret == -EDEADLK) {
140962306a36Sopenharmony_ci		ret = drm_modeset_backoff(&ctx);
141062306a36Sopenharmony_ci		if (!ret)
141162306a36Sopenharmony_ci			goto retry;
141262306a36Sopenharmony_ci	}
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	drm_modeset_drop_locks(&ctx);
141562306a36Sopenharmony_ci	drm_modeset_acquire_fini(&ctx);
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	if (ret && crtc->funcs->page_flip_target)
141862306a36Sopenharmony_ci		drm_crtc_vblank_put(crtc);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	return ret;
142162306a36Sopenharmony_ci}
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci/**
142462306a36Sopenharmony_ci * DOC: damage tracking
142562306a36Sopenharmony_ci *
142662306a36Sopenharmony_ci * FB_DAMAGE_CLIPS is an optional plane property which provides a means to
142762306a36Sopenharmony_ci * specify a list of damage rectangles on a plane in framebuffer coordinates of
142862306a36Sopenharmony_ci * the framebuffer attached to the plane. In current context damage is the area
142962306a36Sopenharmony_ci * of plane framebuffer that has changed since last plane update (also called
143062306a36Sopenharmony_ci * page-flip), irrespective of whether currently attached framebuffer is same as
143162306a36Sopenharmony_ci * framebuffer attached during last plane update or not.
143262306a36Sopenharmony_ci *
143362306a36Sopenharmony_ci * FB_DAMAGE_CLIPS is a hint to kernel which could be helpful for some drivers
143462306a36Sopenharmony_ci * to optimize internally especially for virtual devices where each framebuffer
143562306a36Sopenharmony_ci * change needs to be transmitted over network, usb, etc.
143662306a36Sopenharmony_ci *
143762306a36Sopenharmony_ci * Since FB_DAMAGE_CLIPS is a hint so it is an optional property. User-space can
143862306a36Sopenharmony_ci * ignore damage clips property and in that case driver will do a full plane
143962306a36Sopenharmony_ci * update. In case damage clips are provided then it is guaranteed that the area
144062306a36Sopenharmony_ci * inside damage clips will be updated to plane. For efficiency driver can do
144162306a36Sopenharmony_ci * full update or can update more than specified in damage clips. Since driver
144262306a36Sopenharmony_ci * is free to read more, user-space must always render the entire visible
144362306a36Sopenharmony_ci * framebuffer. Otherwise there can be corruptions. Also, if a user-space
144462306a36Sopenharmony_ci * provides damage clips which doesn't encompass the actual damage to
144562306a36Sopenharmony_ci * framebuffer (since last plane update) can result in incorrect rendering.
144662306a36Sopenharmony_ci *
144762306a36Sopenharmony_ci * FB_DAMAGE_CLIPS is a blob property with the layout of blob data is simply an
144862306a36Sopenharmony_ci * array of &drm_mode_rect. Unlike plane &drm_plane_state.src coordinates,
144962306a36Sopenharmony_ci * damage clips are not in 16.16 fixed point. Similar to plane src in
145062306a36Sopenharmony_ci * framebuffer, damage clips cannot be negative. In damage clip, x1/y1 are
145162306a36Sopenharmony_ci * inclusive and x2/y2 are exclusive. While kernel does not error for overlapped
145262306a36Sopenharmony_ci * damage clips, it is strongly discouraged.
145362306a36Sopenharmony_ci *
145462306a36Sopenharmony_ci * Drivers that are interested in damage interface for plane should enable
145562306a36Sopenharmony_ci * FB_DAMAGE_CLIPS property by calling drm_plane_enable_fb_damage_clips().
145662306a36Sopenharmony_ci * Drivers implementing damage can use drm_atomic_helper_damage_iter_init() and
145762306a36Sopenharmony_ci * drm_atomic_helper_damage_iter_next() helper iterator function to get damage
145862306a36Sopenharmony_ci * rectangles clipped to &drm_plane_state.src.
145962306a36Sopenharmony_ci */
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci/**
146262306a36Sopenharmony_ci * drm_plane_enable_fb_damage_clips - Enables plane fb damage clips property.
146362306a36Sopenharmony_ci * @plane: Plane on which to enable damage clips property.
146462306a36Sopenharmony_ci *
146562306a36Sopenharmony_ci * This function lets driver to enable the damage clips property on a plane.
146662306a36Sopenharmony_ci */
146762306a36Sopenharmony_civoid drm_plane_enable_fb_damage_clips(struct drm_plane *plane)
146862306a36Sopenharmony_ci{
146962306a36Sopenharmony_ci	struct drm_device *dev = plane->dev;
147062306a36Sopenharmony_ci	struct drm_mode_config *config = &dev->mode_config;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	drm_object_attach_property(&plane->base, config->prop_fb_damage_clips,
147362306a36Sopenharmony_ci				   0);
147462306a36Sopenharmony_ci}
147562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_plane_enable_fb_damage_clips);
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci/**
147862306a36Sopenharmony_ci * drm_plane_get_damage_clips_count - Returns damage clips count.
147962306a36Sopenharmony_ci * @state: Plane state.
148062306a36Sopenharmony_ci *
148162306a36Sopenharmony_ci * Simple helper to get the number of &drm_mode_rect clips set by user-space
148262306a36Sopenharmony_ci * during plane update.
148362306a36Sopenharmony_ci *
148462306a36Sopenharmony_ci * Return: Number of clips in plane fb_damage_clips blob property.
148562306a36Sopenharmony_ci */
148662306a36Sopenharmony_ciunsigned int
148762306a36Sopenharmony_cidrm_plane_get_damage_clips_count(const struct drm_plane_state *state)
148862306a36Sopenharmony_ci{
148962306a36Sopenharmony_ci	return (state && state->fb_damage_clips) ?
149062306a36Sopenharmony_ci		state->fb_damage_clips->length/sizeof(struct drm_mode_rect) : 0;
149162306a36Sopenharmony_ci}
149262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_plane_get_damage_clips_count);
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_cistruct drm_mode_rect *
149562306a36Sopenharmony_ci__drm_plane_get_damage_clips(const struct drm_plane_state *state)
149662306a36Sopenharmony_ci{
149762306a36Sopenharmony_ci	return (struct drm_mode_rect *)((state && state->fb_damage_clips) ?
149862306a36Sopenharmony_ci					state->fb_damage_clips->data : NULL);
149962306a36Sopenharmony_ci}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci/**
150262306a36Sopenharmony_ci * drm_plane_get_damage_clips - Returns damage clips.
150362306a36Sopenharmony_ci * @state: Plane state.
150462306a36Sopenharmony_ci *
150562306a36Sopenharmony_ci * Note that this function returns uapi type &drm_mode_rect. Drivers might want
150662306a36Sopenharmony_ci * to use the helper functions drm_atomic_helper_damage_iter_init() and
150762306a36Sopenharmony_ci * drm_atomic_helper_damage_iter_next() or drm_atomic_helper_damage_merged() if
150862306a36Sopenharmony_ci * the driver can only handle a single damage region at most.
150962306a36Sopenharmony_ci *
151062306a36Sopenharmony_ci * Return: Damage clips in plane fb_damage_clips blob property.
151162306a36Sopenharmony_ci */
151262306a36Sopenharmony_cistruct drm_mode_rect *
151362306a36Sopenharmony_cidrm_plane_get_damage_clips(const struct drm_plane_state *state)
151462306a36Sopenharmony_ci{
151562306a36Sopenharmony_ci	struct drm_device *dev = state->plane->dev;
151662306a36Sopenharmony_ci	struct drm_mode_config *config = &dev->mode_config;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	/* check that drm_plane_enable_fb_damage_clips() was called */
151962306a36Sopenharmony_ci	if (!drm_mode_obj_find_prop_id(&state->plane->base,
152062306a36Sopenharmony_ci				       config->prop_fb_damage_clips->base.id))
152162306a36Sopenharmony_ci		drm_warn_once(dev, "drm_plane_enable_fb_damage_clips() not called\n");
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	return __drm_plane_get_damage_clips(state);
152462306a36Sopenharmony_ci}
152562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_plane_get_damage_clips);
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_cistruct drm_property *
152862306a36Sopenharmony_cidrm_create_scaling_filter_prop(struct drm_device *dev,
152962306a36Sopenharmony_ci			       unsigned int supported_filters)
153062306a36Sopenharmony_ci{
153162306a36Sopenharmony_ci	struct drm_property *prop;
153262306a36Sopenharmony_ci	static const struct drm_prop_enum_list props[] = {
153362306a36Sopenharmony_ci		{ DRM_SCALING_FILTER_DEFAULT, "Default" },
153462306a36Sopenharmony_ci		{ DRM_SCALING_FILTER_NEAREST_NEIGHBOR, "Nearest Neighbor" },
153562306a36Sopenharmony_ci	};
153662306a36Sopenharmony_ci	unsigned int valid_mode_mask = BIT(DRM_SCALING_FILTER_DEFAULT) |
153762306a36Sopenharmony_ci				       BIT(DRM_SCALING_FILTER_NEAREST_NEIGHBOR);
153862306a36Sopenharmony_ci	int i;
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	if (WARN_ON((supported_filters & ~valid_mode_mask) ||
154162306a36Sopenharmony_ci		    ((supported_filters & BIT(DRM_SCALING_FILTER_DEFAULT)) == 0)))
154262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	prop = drm_property_create(dev, DRM_MODE_PROP_ENUM,
154562306a36Sopenharmony_ci				   "SCALING_FILTER",
154662306a36Sopenharmony_ci				   hweight32(supported_filters));
154762306a36Sopenharmony_ci	if (!prop)
154862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(props); i++) {
155162306a36Sopenharmony_ci		int ret;
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci		if (!(BIT(props[i].type) & supported_filters))
155462306a36Sopenharmony_ci			continue;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci		ret = drm_property_add_enum(prop, props[i].type,
155762306a36Sopenharmony_ci					    props[i].name);
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci		if (ret) {
156062306a36Sopenharmony_ci			drm_property_destroy(dev, prop);
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci			return ERR_PTR(ret);
156362306a36Sopenharmony_ci		}
156462306a36Sopenharmony_ci	}
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	return prop;
156762306a36Sopenharmony_ci}
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci/**
157062306a36Sopenharmony_ci * drm_plane_create_scaling_filter_property - create a new scaling filter
157162306a36Sopenharmony_ci * property
157262306a36Sopenharmony_ci *
157362306a36Sopenharmony_ci * @plane: drm plane
157462306a36Sopenharmony_ci * @supported_filters: bitmask of supported scaling filters, must include
157562306a36Sopenharmony_ci *		       BIT(DRM_SCALING_FILTER_DEFAULT).
157662306a36Sopenharmony_ci *
157762306a36Sopenharmony_ci * This function lets driver to enable the scaling filter property on a given
157862306a36Sopenharmony_ci * plane.
157962306a36Sopenharmony_ci *
158062306a36Sopenharmony_ci * RETURNS:
158162306a36Sopenharmony_ci * Zero for success or -errno
158262306a36Sopenharmony_ci */
158362306a36Sopenharmony_ciint drm_plane_create_scaling_filter_property(struct drm_plane *plane,
158462306a36Sopenharmony_ci					     unsigned int supported_filters)
158562306a36Sopenharmony_ci{
158662306a36Sopenharmony_ci	struct drm_property *prop =
158762306a36Sopenharmony_ci		drm_create_scaling_filter_prop(plane->dev, supported_filters);
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	if (IS_ERR(prop))
159062306a36Sopenharmony_ci		return PTR_ERR(prop);
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	drm_object_attach_property(&plane->base, prop,
159362306a36Sopenharmony_ci				   DRM_SCALING_FILTER_DEFAULT);
159462306a36Sopenharmony_ci	plane->scaling_filter_property = prop;
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	return 0;
159762306a36Sopenharmony_ci}
159862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_plane_create_scaling_filter_property);
1599