xref: /dflybsd-src/sys/dev/drm/drm_framebuffer.c (revision 4be47400997875399098ad904fb4ca65b3244cca)
11dedbd3bSFrançois Tigeot /*
21dedbd3bSFrançois Tigeot  * Copyright (c) 2016 Intel Corporation
31dedbd3bSFrançois Tigeot  *
41dedbd3bSFrançois Tigeot  * Permission to use, copy, modify, distribute, and sell this software and its
51dedbd3bSFrançois Tigeot  * documentation for any purpose is hereby granted without fee, provided that
61dedbd3bSFrançois Tigeot  * the above copyright notice appear in all copies and that both that copyright
71dedbd3bSFrançois Tigeot  * notice and this permission notice appear in supporting documentation, and
81dedbd3bSFrançois Tigeot  * that the name of the copyright holders not be used in advertising or
91dedbd3bSFrançois Tigeot  * publicity pertaining to distribution of the software without specific,
101dedbd3bSFrançois Tigeot  * written prior permission.  The copyright holders make no representations
111dedbd3bSFrançois Tigeot  * about the suitability of this software for any purpose.  It is provided "as
121dedbd3bSFrançois Tigeot  * is" without express or implied warranty.
131dedbd3bSFrançois Tigeot  *
141dedbd3bSFrançois Tigeot  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
151dedbd3bSFrançois Tigeot  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
161dedbd3bSFrançois Tigeot  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
171dedbd3bSFrançois Tigeot  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
181dedbd3bSFrançois Tigeot  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
191dedbd3bSFrançois Tigeot  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
201dedbd3bSFrançois Tigeot  * OF THIS SOFTWARE.
211dedbd3bSFrançois Tigeot  */
221dedbd3bSFrançois Tigeot 
231dedbd3bSFrançois Tigeot #include <linux/export.h>
241dedbd3bSFrançois Tigeot #include <drm/drmP.h>
251dedbd3bSFrançois Tigeot #include <drm/drm_auth.h>
261dedbd3bSFrançois Tigeot #include <drm/drm_framebuffer.h>
271dedbd3bSFrançois Tigeot 
281dedbd3bSFrançois Tigeot #include "drm_crtc_internal.h"
291dedbd3bSFrançois Tigeot 
301dedbd3bSFrançois Tigeot /**
311dedbd3bSFrançois Tigeot  * DOC: overview
321dedbd3bSFrançois Tigeot  *
331dedbd3bSFrançois Tigeot  * Frame buffers are abstract memory objects that provide a source of pixels to
341dedbd3bSFrançois Tigeot  * scanout to a CRTC. Applications explicitly request the creation of frame
351dedbd3bSFrançois Tigeot  * buffers through the DRM_IOCTL_MODE_ADDFB(2) ioctls and receive an opaque
361dedbd3bSFrançois Tigeot  * handle that can be passed to the KMS CRTC control, plane configuration and
371dedbd3bSFrançois Tigeot  * page flip functions.
381dedbd3bSFrançois Tigeot  *
391dedbd3bSFrançois Tigeot  * Frame buffers rely on the underlying memory manager for allocating backing
401dedbd3bSFrançois Tigeot  * storage. When creating a frame buffer applications pass a memory handle
411dedbd3bSFrançois Tigeot  * (or a list of memory handles for multi-planar formats) through the
421dedbd3bSFrançois Tigeot  * struct &drm_mode_fb_cmd2 argument. For drivers using GEM as their userspace
431dedbd3bSFrançois Tigeot  * buffer management interface this would be a GEM handle.  Drivers are however
441dedbd3bSFrançois Tigeot  * free to use their own backing storage object handles, e.g. vmwgfx directly
451dedbd3bSFrançois Tigeot  * exposes special TTM handles to userspace and so expects TTM handles in the
461dedbd3bSFrançois Tigeot  * create ioctl and not GEM handles.
471dedbd3bSFrançois Tigeot  *
481dedbd3bSFrançois Tigeot  * Framebuffers are tracked with struct &drm_framebuffer. They are published
491dedbd3bSFrançois Tigeot  * using drm_framebuffer_init() - after calling that function userspace can use
501dedbd3bSFrançois Tigeot  * and access the framebuffer object. The helper function
511dedbd3bSFrançois Tigeot  * drm_helper_mode_fill_fb_struct() can be used to pre-fill the required
521dedbd3bSFrançois Tigeot  * metadata fields.
531dedbd3bSFrançois Tigeot  *
541dedbd3bSFrançois Tigeot  * The lifetime of a drm framebuffer is controlled with a reference count,
551dedbd3bSFrançois Tigeot  * drivers can grab additional references with drm_framebuffer_reference() and
561dedbd3bSFrançois Tigeot  * drop them again with drm_framebuffer_unreference(). For driver-private
571dedbd3bSFrançois Tigeot  * framebuffers for which the last reference is never dropped (e.g. for the
581dedbd3bSFrançois Tigeot  * fbdev framebuffer when the struct struct &drm_framebuffer is embedded into
591dedbd3bSFrançois Tigeot  * the fbdev helper struct) drivers can manually clean up a framebuffer at
601dedbd3bSFrançois Tigeot  * module unload time with drm_framebuffer_unregister_private(). But doing this
611dedbd3bSFrançois Tigeot  * is not recommended, and it's better to have a normal free-standing struct
621dedbd3bSFrançois Tigeot  * &drm_framebuffer.
631dedbd3bSFrançois Tigeot  */
641dedbd3bSFrançois Tigeot 
651dedbd3bSFrançois Tigeot int drm_framebuffer_check_src_coords(uint32_t src_x, uint32_t src_y,
661dedbd3bSFrançois Tigeot 				     uint32_t src_w, uint32_t src_h,
671dedbd3bSFrançois Tigeot 				     const struct drm_framebuffer *fb)
681dedbd3bSFrançois Tigeot {
691dedbd3bSFrançois Tigeot 	unsigned int fb_width, fb_height;
701dedbd3bSFrançois Tigeot 
711dedbd3bSFrançois Tigeot 	fb_width = fb->width << 16;
721dedbd3bSFrançois Tigeot 	fb_height = fb->height << 16;
731dedbd3bSFrançois Tigeot 
741dedbd3bSFrançois Tigeot 	/* Make sure source coordinates are inside the fb. */
751dedbd3bSFrançois Tigeot 	if (src_w > fb_width ||
761dedbd3bSFrançois Tigeot 	    src_x > fb_width - src_w ||
771dedbd3bSFrançois Tigeot 	    src_h > fb_height ||
781dedbd3bSFrançois Tigeot 	    src_y > fb_height - src_h) {
791dedbd3bSFrançois Tigeot 		DRM_DEBUG_KMS("Invalid source coordinates "
801dedbd3bSFrançois Tigeot 			      "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
811dedbd3bSFrançois Tigeot 			      src_w >> 16, ((src_w & 0xffff) * 15625) >> 10,
821dedbd3bSFrançois Tigeot 			      src_h >> 16, ((src_h & 0xffff) * 15625) >> 10,
831dedbd3bSFrançois Tigeot 			      src_x >> 16, ((src_x & 0xffff) * 15625) >> 10,
841dedbd3bSFrançois Tigeot 			      src_y >> 16, ((src_y & 0xffff) * 15625) >> 10);
851dedbd3bSFrançois Tigeot 		return -ENOSPC;
861dedbd3bSFrançois Tigeot 	}
871dedbd3bSFrançois Tigeot 
881dedbd3bSFrançois Tigeot 	return 0;
891dedbd3bSFrançois Tigeot }
901dedbd3bSFrançois Tigeot 
911dedbd3bSFrançois Tigeot /**
921dedbd3bSFrançois Tigeot  * drm_mode_addfb - add an FB to the graphics configuration
931dedbd3bSFrançois Tigeot  * @dev: drm device for the ioctl
941dedbd3bSFrançois Tigeot  * @data: data pointer for the ioctl
951dedbd3bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
961dedbd3bSFrançois Tigeot  *
971dedbd3bSFrançois Tigeot  * Add a new FB to the specified CRTC, given a user request. This is the
981dedbd3bSFrançois Tigeot  * original addfb ioctl which only supported RGB formats.
991dedbd3bSFrançois Tigeot  *
1001dedbd3bSFrançois Tigeot  * Called by the user via ioctl.
1011dedbd3bSFrançois Tigeot  *
1021dedbd3bSFrançois Tigeot  * Returns:
1031dedbd3bSFrançois Tigeot  * Zero on success, negative errno on failure.
1041dedbd3bSFrançois Tigeot  */
1051dedbd3bSFrançois Tigeot int drm_mode_addfb(struct drm_device *dev,
1061dedbd3bSFrançois Tigeot 		   void *data, struct drm_file *file_priv)
1071dedbd3bSFrançois Tigeot {
1081dedbd3bSFrançois Tigeot 	struct drm_mode_fb_cmd *or = data;
1091dedbd3bSFrançois Tigeot 	struct drm_mode_fb_cmd2 r = {};
1101dedbd3bSFrançois Tigeot 	int ret;
1111dedbd3bSFrançois Tigeot 
1121dedbd3bSFrançois Tigeot 	/* convert to new format and call new ioctl */
1131dedbd3bSFrançois Tigeot 	r.fb_id = or->fb_id;
1141dedbd3bSFrançois Tigeot 	r.width = or->width;
1151dedbd3bSFrançois Tigeot 	r.height = or->height;
1161dedbd3bSFrançois Tigeot 	r.pitches[0] = or->pitch;
1171dedbd3bSFrançois Tigeot 	r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth);
1181dedbd3bSFrançois Tigeot 	r.handles[0] = or->handle;
1191dedbd3bSFrançois Tigeot 
1201dedbd3bSFrançois Tigeot 	ret = drm_mode_addfb2(dev, &r, file_priv);
1211dedbd3bSFrançois Tigeot 	if (ret)
1221dedbd3bSFrançois Tigeot 		return ret;
1231dedbd3bSFrançois Tigeot 
1241dedbd3bSFrançois Tigeot 	or->fb_id = r.fb_id;
1251dedbd3bSFrançois Tigeot 
1261dedbd3bSFrançois Tigeot 	return 0;
1271dedbd3bSFrançois Tigeot }
1281dedbd3bSFrançois Tigeot 
1291dedbd3bSFrançois Tigeot static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)
1301dedbd3bSFrançois Tigeot {
131*4be47400SFrançois Tigeot 	const struct drm_format_info *info;
132*4be47400SFrançois Tigeot 	int i;
1331dedbd3bSFrançois Tigeot 
134*4be47400SFrançois Tigeot 	info = __drm_format_info(r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN);
135*4be47400SFrançois Tigeot 	if (!info) {
136*4be47400SFrançois Tigeot 		struct drm_format_name_buf format_name;
137*4be47400SFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer format %s\n",
138*4be47400SFrançois Tigeot 		              drm_get_format_name(r->pixel_format,
139*4be47400SFrançois Tigeot 		                                  &format_name));
140*4be47400SFrançois Tigeot 		return -EINVAL;
1411dedbd3bSFrançois Tigeot 	}
1421dedbd3bSFrançois Tigeot 
143*4be47400SFrançois Tigeot 	if (r->width == 0 || r->width % info->hsub) {
1441dedbd3bSFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width);
1451dedbd3bSFrançois Tigeot 		return -EINVAL;
1461dedbd3bSFrançois Tigeot 	}
1471dedbd3bSFrançois Tigeot 
148*4be47400SFrançois Tigeot 	if (r->height == 0 || r->height % info->vsub) {
1491dedbd3bSFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height);
1501dedbd3bSFrançois Tigeot 		return -EINVAL;
1511dedbd3bSFrançois Tigeot 	}
1521dedbd3bSFrançois Tigeot 
153*4be47400SFrançois Tigeot 	for (i = 0; i < info->num_planes; i++) {
154*4be47400SFrançois Tigeot 		unsigned int width = r->width / (i != 0 ? info->hsub : 1);
155*4be47400SFrançois Tigeot 		unsigned int height = r->height / (i != 0 ? info->vsub : 1);
156*4be47400SFrançois Tigeot 		unsigned int cpp = info->cpp[i];
1571dedbd3bSFrançois Tigeot 
1581dedbd3bSFrançois Tigeot 		if (!r->handles[i]) {
1591dedbd3bSFrançois Tigeot 			DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i);
1601dedbd3bSFrançois Tigeot 			return -EINVAL;
1611dedbd3bSFrançois Tigeot 		}
1621dedbd3bSFrançois Tigeot 
1631dedbd3bSFrançois Tigeot 		if ((uint64_t) width * cpp > UINT_MAX)
1641dedbd3bSFrançois Tigeot 			return -ERANGE;
1651dedbd3bSFrançois Tigeot 
1661dedbd3bSFrançois Tigeot 		if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX)
1671dedbd3bSFrançois Tigeot 			return -ERANGE;
1681dedbd3bSFrançois Tigeot 
1691dedbd3bSFrançois Tigeot 		if (r->pitches[i] < width * cpp) {
1701dedbd3bSFrançois Tigeot 			DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i);
1711dedbd3bSFrançois Tigeot 			return -EINVAL;
1721dedbd3bSFrançois Tigeot 		}
1731dedbd3bSFrançois Tigeot 
1741dedbd3bSFrançois Tigeot 		if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) {
1751dedbd3bSFrançois Tigeot 			DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n",
1761dedbd3bSFrançois Tigeot 				      r->modifier[i], i);
1771dedbd3bSFrançois Tigeot 			return -EINVAL;
1781dedbd3bSFrançois Tigeot 		}
1791dedbd3bSFrançois Tigeot 
180*4be47400SFrançois Tigeot 		if (r->flags & DRM_MODE_FB_MODIFIERS &&
181*4be47400SFrançois Tigeot 		    r->modifier[i] != r->modifier[0]) {
182*4be47400SFrançois Tigeot 			DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n",
183*4be47400SFrançois Tigeot 				      r->modifier[i], i);
184*4be47400SFrançois Tigeot 			return -EINVAL;
185*4be47400SFrançois Tigeot 		}
186*4be47400SFrançois Tigeot 
1871dedbd3bSFrançois Tigeot 		/* modifier specific checks: */
1881dedbd3bSFrançois Tigeot 		switch (r->modifier[i]) {
1891dedbd3bSFrançois Tigeot 		case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE:
1901dedbd3bSFrançois Tigeot 			/* NOTE: the pitch restriction may be lifted later if it turns
1911dedbd3bSFrançois Tigeot 			 * out that no hw has this restriction:
1921dedbd3bSFrançois Tigeot 			 */
1931dedbd3bSFrançois Tigeot 			if (r->pixel_format != DRM_FORMAT_NV12 ||
1941dedbd3bSFrançois Tigeot 					width % 128 || height % 32 ||
1951dedbd3bSFrançois Tigeot 					r->pitches[i] % 128) {
1961dedbd3bSFrançois Tigeot 				DRM_DEBUG_KMS("bad modifier data for plane %d\n", i);
1971dedbd3bSFrançois Tigeot 				return -EINVAL;
1981dedbd3bSFrançois Tigeot 			}
1991dedbd3bSFrançois Tigeot 			break;
2001dedbd3bSFrançois Tigeot 
2011dedbd3bSFrançois Tigeot 		default:
2021dedbd3bSFrançois Tigeot 			break;
2031dedbd3bSFrançois Tigeot 		}
2041dedbd3bSFrançois Tigeot 	}
2051dedbd3bSFrançois Tigeot 
206*4be47400SFrançois Tigeot 	for (i = info->num_planes; i < 4; i++) {
2071dedbd3bSFrançois Tigeot 		if (r->modifier[i]) {
2081dedbd3bSFrançois Tigeot 			DRM_DEBUG_KMS("non-zero modifier for unused plane %d\n", i);
2091dedbd3bSFrançois Tigeot 			return -EINVAL;
2101dedbd3bSFrançois Tigeot 		}
2111dedbd3bSFrançois Tigeot 
2121dedbd3bSFrançois Tigeot 		/* Pre-FB_MODIFIERS userspace didn't clear the structs properly. */
2131dedbd3bSFrançois Tigeot 		if (!(r->flags & DRM_MODE_FB_MODIFIERS))
2141dedbd3bSFrançois Tigeot 			continue;
2151dedbd3bSFrançois Tigeot 
2161dedbd3bSFrançois Tigeot 		if (r->handles[i]) {
2171dedbd3bSFrançois Tigeot 			DRM_DEBUG_KMS("buffer object handle for unused plane %d\n", i);
2181dedbd3bSFrançois Tigeot 			return -EINVAL;
2191dedbd3bSFrançois Tigeot 		}
2201dedbd3bSFrançois Tigeot 
2211dedbd3bSFrançois Tigeot 		if (r->pitches[i]) {
2221dedbd3bSFrançois Tigeot 			DRM_DEBUG_KMS("non-zero pitch for unused plane %d\n", i);
2231dedbd3bSFrançois Tigeot 			return -EINVAL;
2241dedbd3bSFrançois Tigeot 		}
2251dedbd3bSFrançois Tigeot 
2261dedbd3bSFrançois Tigeot 		if (r->offsets[i]) {
2271dedbd3bSFrançois Tigeot 			DRM_DEBUG_KMS("non-zero offset for unused plane %d\n", i);
2281dedbd3bSFrançois Tigeot 			return -EINVAL;
2291dedbd3bSFrançois Tigeot 		}
2301dedbd3bSFrançois Tigeot 	}
2311dedbd3bSFrançois Tigeot 
2321dedbd3bSFrançois Tigeot 	return 0;
2331dedbd3bSFrançois Tigeot }
2341dedbd3bSFrançois Tigeot 
2351dedbd3bSFrançois Tigeot struct drm_framebuffer *
2361dedbd3bSFrançois Tigeot drm_internal_framebuffer_create(struct drm_device *dev,
2371dedbd3bSFrançois Tigeot 				const struct drm_mode_fb_cmd2 *r,
2381dedbd3bSFrançois Tigeot 				struct drm_file *file_priv)
2391dedbd3bSFrançois Tigeot {
2401dedbd3bSFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
2411dedbd3bSFrançois Tigeot 	struct drm_framebuffer *fb;
2421dedbd3bSFrançois Tigeot 	int ret;
2431dedbd3bSFrançois Tigeot 
2441dedbd3bSFrançois Tigeot 	if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) {
2451dedbd3bSFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags);
2461dedbd3bSFrançois Tigeot 		return ERR_PTR(-EINVAL);
2471dedbd3bSFrançois Tigeot 	}
2481dedbd3bSFrançois Tigeot 
2491dedbd3bSFrançois Tigeot 	if ((config->min_width > r->width) || (r->width > config->max_width)) {
2501dedbd3bSFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n",
2511dedbd3bSFrançois Tigeot 			  r->width, config->min_width, config->max_width);
2521dedbd3bSFrançois Tigeot 		return ERR_PTR(-EINVAL);
2531dedbd3bSFrançois Tigeot 	}
2541dedbd3bSFrançois Tigeot 	if ((config->min_height > r->height) || (r->height > config->max_height)) {
2551dedbd3bSFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n",
2561dedbd3bSFrançois Tigeot 			  r->height, config->min_height, config->max_height);
2571dedbd3bSFrançois Tigeot 		return ERR_PTR(-EINVAL);
2581dedbd3bSFrançois Tigeot 	}
2591dedbd3bSFrançois Tigeot 
2601dedbd3bSFrançois Tigeot 	if (r->flags & DRM_MODE_FB_MODIFIERS &&
2611dedbd3bSFrançois Tigeot 	    !dev->mode_config.allow_fb_modifiers) {
2621dedbd3bSFrançois Tigeot 		DRM_DEBUG_KMS("driver does not support fb modifiers\n");
2631dedbd3bSFrançois Tigeot 		return ERR_PTR(-EINVAL);
2641dedbd3bSFrançois Tigeot 	}
2651dedbd3bSFrançois Tigeot 
2661dedbd3bSFrançois Tigeot 	ret = framebuffer_check(r);
2671dedbd3bSFrançois Tigeot 	if (ret)
2681dedbd3bSFrançois Tigeot 		return ERR_PTR(ret);
2691dedbd3bSFrançois Tigeot 
2701dedbd3bSFrançois Tigeot 	fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
2711dedbd3bSFrançois Tigeot 	if (IS_ERR(fb)) {
2721dedbd3bSFrançois Tigeot 		DRM_DEBUG_KMS("could not create framebuffer\n");
2731dedbd3bSFrançois Tigeot 		return fb;
2741dedbd3bSFrançois Tigeot 	}
2751dedbd3bSFrançois Tigeot 
2761dedbd3bSFrançois Tigeot 	return fb;
2771dedbd3bSFrançois Tigeot }
2781dedbd3bSFrançois Tigeot 
2791dedbd3bSFrançois Tigeot /**
2801dedbd3bSFrançois Tigeot  * drm_mode_addfb2 - add an FB to the graphics configuration
2811dedbd3bSFrançois Tigeot  * @dev: drm device for the ioctl
2821dedbd3bSFrançois Tigeot  * @data: data pointer for the ioctl
2831dedbd3bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
2841dedbd3bSFrançois Tigeot  *
2851dedbd3bSFrançois Tigeot  * Add a new FB to the specified CRTC, given a user request with format. This is
2861dedbd3bSFrançois Tigeot  * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers
2871dedbd3bSFrançois Tigeot  * and uses fourcc codes as pixel format specifiers.
2881dedbd3bSFrançois Tigeot  *
2891dedbd3bSFrançois Tigeot  * Called by the user via ioctl.
2901dedbd3bSFrançois Tigeot  *
2911dedbd3bSFrançois Tigeot  * Returns:
2921dedbd3bSFrançois Tigeot  * Zero on success, negative errno on failure.
2931dedbd3bSFrançois Tigeot  */
2941dedbd3bSFrançois Tigeot int drm_mode_addfb2(struct drm_device *dev,
2951dedbd3bSFrançois Tigeot 		    void *data, struct drm_file *file_priv)
2961dedbd3bSFrançois Tigeot {
2971dedbd3bSFrançois Tigeot 	struct drm_mode_fb_cmd2 *r = data;
2981dedbd3bSFrançois Tigeot 	struct drm_framebuffer *fb;
2991dedbd3bSFrançois Tigeot 
3001dedbd3bSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
3011dedbd3bSFrançois Tigeot 		return -EINVAL;
3021dedbd3bSFrançois Tigeot 
3031dedbd3bSFrançois Tigeot 	fb = drm_internal_framebuffer_create(dev, r, file_priv);
3041dedbd3bSFrançois Tigeot 	if (IS_ERR(fb))
3051dedbd3bSFrançois Tigeot 		return PTR_ERR(fb);
3061dedbd3bSFrançois Tigeot 
3071dedbd3bSFrançois Tigeot 	DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
3081dedbd3bSFrançois Tigeot 	r->fb_id = fb->base.id;
3091dedbd3bSFrançois Tigeot 
3101dedbd3bSFrançois Tigeot 	/* Transfer ownership to the filp for reaping on close */
3111dedbd3bSFrançois Tigeot 	mutex_lock(&file_priv->fbs_lock);
3121dedbd3bSFrançois Tigeot 	list_add(&fb->filp_head, &file_priv->fbs);
3131dedbd3bSFrançois Tigeot 	mutex_unlock(&file_priv->fbs_lock);
3141dedbd3bSFrançois Tigeot 
3151dedbd3bSFrançois Tigeot 	return 0;
3161dedbd3bSFrançois Tigeot }
3171dedbd3bSFrançois Tigeot 
3181dedbd3bSFrançois Tigeot struct drm_mode_rmfb_work {
3191dedbd3bSFrançois Tigeot 	struct work_struct work;
3201dedbd3bSFrançois Tigeot 	struct list_head fbs;
3211dedbd3bSFrançois Tigeot };
3221dedbd3bSFrançois Tigeot 
3231dedbd3bSFrançois Tigeot static void drm_mode_rmfb_work_fn(struct work_struct *w)
3241dedbd3bSFrançois Tigeot {
3251dedbd3bSFrançois Tigeot 	struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work);
3261dedbd3bSFrançois Tigeot 
3271dedbd3bSFrançois Tigeot 	while (!list_empty(&arg->fbs)) {
3281dedbd3bSFrançois Tigeot 		struct drm_framebuffer *fb =
3291dedbd3bSFrançois Tigeot 			list_first_entry(&arg->fbs, typeof(*fb), filp_head);
3301dedbd3bSFrançois Tigeot 
3311dedbd3bSFrançois Tigeot 		list_del_init(&fb->filp_head);
3321dedbd3bSFrançois Tigeot 		drm_framebuffer_remove(fb);
3331dedbd3bSFrançois Tigeot 	}
3341dedbd3bSFrançois Tigeot }
3351dedbd3bSFrançois Tigeot 
3361dedbd3bSFrançois Tigeot /**
3371dedbd3bSFrançois Tigeot  * drm_mode_rmfb - remove an FB from the configuration
3381dedbd3bSFrançois Tigeot  * @dev: drm device for the ioctl
3391dedbd3bSFrançois Tigeot  * @data: data pointer for the ioctl
3401dedbd3bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
3411dedbd3bSFrançois Tigeot  *
3421dedbd3bSFrançois Tigeot  * Remove the FB specified by the user.
3431dedbd3bSFrançois Tigeot  *
3441dedbd3bSFrançois Tigeot  * Called by the user via ioctl.
3451dedbd3bSFrançois Tigeot  *
3461dedbd3bSFrançois Tigeot  * Returns:
3471dedbd3bSFrançois Tigeot  * Zero on success, negative errno on failure.
3481dedbd3bSFrançois Tigeot  */
3491dedbd3bSFrançois Tigeot int drm_mode_rmfb(struct drm_device *dev,
3501dedbd3bSFrançois Tigeot 		   void *data, struct drm_file *file_priv)
3511dedbd3bSFrançois Tigeot {
3521dedbd3bSFrançois Tigeot 	struct drm_framebuffer *fb = NULL;
3531dedbd3bSFrançois Tigeot 	struct drm_framebuffer *fbl = NULL;
3541dedbd3bSFrançois Tigeot 	uint32_t *id = data;
3551dedbd3bSFrançois Tigeot 	int found = 0;
3561dedbd3bSFrançois Tigeot 
3571dedbd3bSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
3581dedbd3bSFrançois Tigeot 		return -EINVAL;
3591dedbd3bSFrançois Tigeot 
3601dedbd3bSFrançois Tigeot 	fb = drm_framebuffer_lookup(dev, *id);
3611dedbd3bSFrançois Tigeot 	if (!fb)
3621dedbd3bSFrançois Tigeot 		return -ENOENT;
3631dedbd3bSFrançois Tigeot 
3641dedbd3bSFrançois Tigeot 	mutex_lock(&file_priv->fbs_lock);
3651dedbd3bSFrançois Tigeot 	list_for_each_entry(fbl, &file_priv->fbs, filp_head)
3661dedbd3bSFrançois Tigeot 		if (fb == fbl)
3671dedbd3bSFrançois Tigeot 			found = 1;
3681dedbd3bSFrançois Tigeot 	if (!found) {
3691dedbd3bSFrançois Tigeot 		mutex_unlock(&file_priv->fbs_lock);
3701dedbd3bSFrançois Tigeot 		goto fail_unref;
3711dedbd3bSFrançois Tigeot 	}
3721dedbd3bSFrançois Tigeot 
3731dedbd3bSFrançois Tigeot 	list_del_init(&fb->filp_head);
3741dedbd3bSFrançois Tigeot 	mutex_unlock(&file_priv->fbs_lock);
3751dedbd3bSFrançois Tigeot 
3761dedbd3bSFrançois Tigeot 	/* drop the reference we picked up in framebuffer lookup */
3771dedbd3bSFrançois Tigeot 	drm_framebuffer_unreference(fb);
3781dedbd3bSFrançois Tigeot 
3791dedbd3bSFrançois Tigeot 	/*
3801dedbd3bSFrançois Tigeot 	 * we now own the reference that was stored in the fbs list
3811dedbd3bSFrançois Tigeot 	 *
3821dedbd3bSFrançois Tigeot 	 * drm_framebuffer_remove may fail with -EINTR on pending signals,
3831dedbd3bSFrançois Tigeot 	 * so run this in a separate stack as there's no way to correctly
3841dedbd3bSFrançois Tigeot 	 * handle this after the fb is already removed from the lookup table.
3851dedbd3bSFrançois Tigeot 	 */
3861dedbd3bSFrançois Tigeot 	if (drm_framebuffer_read_refcount(fb) > 1) {
3871dedbd3bSFrançois Tigeot 		struct drm_mode_rmfb_work arg;
3881dedbd3bSFrançois Tigeot 
3891dedbd3bSFrançois Tigeot 		INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
3901dedbd3bSFrançois Tigeot 		INIT_LIST_HEAD(&arg.fbs);
3911dedbd3bSFrançois Tigeot 		list_add_tail(&fb->filp_head, &arg.fbs);
3921dedbd3bSFrançois Tigeot 
3931dedbd3bSFrançois Tigeot 		schedule_work(&arg.work);
3941dedbd3bSFrançois Tigeot 		flush_work(&arg.work);
3951dedbd3bSFrançois Tigeot 		destroy_work_on_stack(&arg.work);
3961dedbd3bSFrançois Tigeot 	} else
3971dedbd3bSFrançois Tigeot 		drm_framebuffer_unreference(fb);
3981dedbd3bSFrançois Tigeot 
3991dedbd3bSFrançois Tigeot 	return 0;
4001dedbd3bSFrançois Tigeot 
4011dedbd3bSFrançois Tigeot fail_unref:
4021dedbd3bSFrançois Tigeot 	drm_framebuffer_unreference(fb);
4031dedbd3bSFrançois Tigeot 	return -ENOENT;
4041dedbd3bSFrançois Tigeot }
4051dedbd3bSFrançois Tigeot 
4061dedbd3bSFrançois Tigeot /**
4071dedbd3bSFrançois Tigeot  * drm_mode_getfb - get FB info
4081dedbd3bSFrançois Tigeot  * @dev: drm device for the ioctl
4091dedbd3bSFrançois Tigeot  * @data: data pointer for the ioctl
4101dedbd3bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
4111dedbd3bSFrançois Tigeot  *
4121dedbd3bSFrançois Tigeot  * Lookup the FB given its ID and return info about it.
4131dedbd3bSFrançois Tigeot  *
4141dedbd3bSFrançois Tigeot  * Called by the user via ioctl.
4151dedbd3bSFrançois Tigeot  *
4161dedbd3bSFrançois Tigeot  * Returns:
4171dedbd3bSFrançois Tigeot  * Zero on success, negative errno on failure.
4181dedbd3bSFrançois Tigeot  */
4191dedbd3bSFrançois Tigeot int drm_mode_getfb(struct drm_device *dev,
4201dedbd3bSFrançois Tigeot 		   void *data, struct drm_file *file_priv)
4211dedbd3bSFrançois Tigeot {
4221dedbd3bSFrançois Tigeot 	struct drm_mode_fb_cmd *r = data;
4231dedbd3bSFrançois Tigeot 	struct drm_framebuffer *fb;
4241dedbd3bSFrançois Tigeot 	int ret;
4251dedbd3bSFrançois Tigeot 
4261dedbd3bSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
4271dedbd3bSFrançois Tigeot 		return -EINVAL;
4281dedbd3bSFrançois Tigeot 
4291dedbd3bSFrançois Tigeot 	fb = drm_framebuffer_lookup(dev, r->fb_id);
4301dedbd3bSFrançois Tigeot 	if (!fb)
4311dedbd3bSFrançois Tigeot 		return -ENOENT;
4321dedbd3bSFrançois Tigeot 
4331dedbd3bSFrançois Tigeot 	r->height = fb->height;
4341dedbd3bSFrançois Tigeot 	r->width = fb->width;
4351dedbd3bSFrançois Tigeot 	r->depth = fb->depth;
4361dedbd3bSFrançois Tigeot 	r->bpp = fb->bits_per_pixel;
4371dedbd3bSFrançois Tigeot 	r->pitch = fb->pitches[0];
4381dedbd3bSFrançois Tigeot 	if (fb->funcs->create_handle) {
4391dedbd3bSFrançois Tigeot 		if (drm_is_current_master(file_priv) || capable(CAP_SYS_ADMIN) ||
4401dedbd3bSFrançois Tigeot 		    drm_is_control_client(file_priv)) {
4411dedbd3bSFrançois Tigeot 			ret = fb->funcs->create_handle(fb, file_priv,
4421dedbd3bSFrançois Tigeot 						       &r->handle);
4431dedbd3bSFrançois Tigeot 		} else {
4441dedbd3bSFrançois Tigeot 			/* GET_FB() is an unprivileged ioctl so we must not
4451dedbd3bSFrançois Tigeot 			 * return a buffer-handle to non-master processes! For
4461dedbd3bSFrançois Tigeot 			 * backwards-compatibility reasons, we cannot make
4471dedbd3bSFrançois Tigeot 			 * GET_FB() privileged, so just return an invalid handle
4481dedbd3bSFrançois Tigeot 			 * for non-masters. */
4491dedbd3bSFrançois Tigeot 			r->handle = 0;
4501dedbd3bSFrançois Tigeot 			ret = 0;
4511dedbd3bSFrançois Tigeot 		}
4521dedbd3bSFrançois Tigeot 	} else {
4531dedbd3bSFrançois Tigeot 		ret = -ENODEV;
4541dedbd3bSFrançois Tigeot 	}
4551dedbd3bSFrançois Tigeot 
4561dedbd3bSFrançois Tigeot 	drm_framebuffer_unreference(fb);
4571dedbd3bSFrançois Tigeot 
4581dedbd3bSFrançois Tigeot 	return ret;
4591dedbd3bSFrançois Tigeot }
4601dedbd3bSFrançois Tigeot 
4611dedbd3bSFrançois Tigeot /**
4621dedbd3bSFrançois Tigeot  * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB
4631dedbd3bSFrançois Tigeot  * @dev: drm device for the ioctl
4641dedbd3bSFrançois Tigeot  * @data: data pointer for the ioctl
4651dedbd3bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
4661dedbd3bSFrançois Tigeot  *
4671dedbd3bSFrançois Tigeot  * Lookup the FB and flush out the damaged area supplied by userspace as a clip
4681dedbd3bSFrançois Tigeot  * rectangle list. Generic userspace which does frontbuffer rendering must call
4691dedbd3bSFrançois Tigeot  * this ioctl to flush out the changes on manual-update display outputs, e.g.
4701dedbd3bSFrançois Tigeot  * usb display-link, mipi manual update panels or edp panel self refresh modes.
4711dedbd3bSFrançois Tigeot  *
4721dedbd3bSFrançois Tigeot  * Modesetting drivers which always update the frontbuffer do not need to
4731dedbd3bSFrançois Tigeot  * implement the corresponding ->dirty framebuffer callback.
4741dedbd3bSFrançois Tigeot  *
4751dedbd3bSFrançois Tigeot  * Called by the user via ioctl.
4761dedbd3bSFrançois Tigeot  *
4771dedbd3bSFrançois Tigeot  * Returns:
4781dedbd3bSFrançois Tigeot  * Zero on success, negative errno on failure.
4791dedbd3bSFrançois Tigeot  */
4801dedbd3bSFrançois Tigeot int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
4811dedbd3bSFrançois Tigeot 			   void *data, struct drm_file *file_priv)
4821dedbd3bSFrançois Tigeot {
4831dedbd3bSFrançois Tigeot 	struct drm_clip_rect __user *clips_ptr;
4841dedbd3bSFrançois Tigeot 	struct drm_clip_rect *clips = NULL;
4851dedbd3bSFrançois Tigeot 	struct drm_mode_fb_dirty_cmd *r = data;
4861dedbd3bSFrançois Tigeot 	struct drm_framebuffer *fb;
4871dedbd3bSFrançois Tigeot 	unsigned flags;
4881dedbd3bSFrançois Tigeot 	int num_clips;
4891dedbd3bSFrançois Tigeot 	int ret;
4901dedbd3bSFrançois Tigeot 
4911dedbd3bSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
4921dedbd3bSFrançois Tigeot 		return -EINVAL;
4931dedbd3bSFrançois Tigeot 
4941dedbd3bSFrançois Tigeot 	fb = drm_framebuffer_lookup(dev, r->fb_id);
4951dedbd3bSFrançois Tigeot 	if (!fb)
4961dedbd3bSFrançois Tigeot 		return -ENOENT;
4971dedbd3bSFrançois Tigeot 
4981dedbd3bSFrançois Tigeot 	num_clips = r->num_clips;
4991dedbd3bSFrançois Tigeot 	clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr;
5001dedbd3bSFrançois Tigeot 
5011dedbd3bSFrançois Tigeot 	if (!num_clips != !clips_ptr) {
5021dedbd3bSFrançois Tigeot 		ret = -EINVAL;
5031dedbd3bSFrançois Tigeot 		goto out_err1;
5041dedbd3bSFrançois Tigeot 	}
5051dedbd3bSFrançois Tigeot 
5061dedbd3bSFrançois Tigeot 	flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags;
5071dedbd3bSFrançois Tigeot 
5081dedbd3bSFrançois Tigeot 	/* If userspace annotates copy, clips must come in pairs */
5091dedbd3bSFrançois Tigeot 	if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) {
5101dedbd3bSFrançois Tigeot 		ret = -EINVAL;
5111dedbd3bSFrançois Tigeot 		goto out_err1;
5121dedbd3bSFrançois Tigeot 	}
5131dedbd3bSFrançois Tigeot 
5141dedbd3bSFrançois Tigeot 	if (num_clips && clips_ptr) {
5151dedbd3bSFrançois Tigeot 		if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) {
5161dedbd3bSFrançois Tigeot 			ret = -EINVAL;
5171dedbd3bSFrançois Tigeot 			goto out_err1;
5181dedbd3bSFrançois Tigeot 		}
5191dedbd3bSFrançois Tigeot 		clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL);
5201dedbd3bSFrançois Tigeot 		if (!clips) {
5211dedbd3bSFrançois Tigeot 			ret = -ENOMEM;
5221dedbd3bSFrançois Tigeot 			goto out_err1;
5231dedbd3bSFrançois Tigeot 		}
5241dedbd3bSFrançois Tigeot 
5251dedbd3bSFrançois Tigeot 		ret = copy_from_user(clips, clips_ptr,
5261dedbd3bSFrançois Tigeot 				     num_clips * sizeof(*clips));
5271dedbd3bSFrançois Tigeot 		if (ret) {
5281dedbd3bSFrançois Tigeot 			ret = -EFAULT;
5291dedbd3bSFrançois Tigeot 			goto out_err2;
5301dedbd3bSFrançois Tigeot 		}
5311dedbd3bSFrançois Tigeot 	}
5321dedbd3bSFrançois Tigeot 
5331dedbd3bSFrançois Tigeot 	if (fb->funcs->dirty) {
5341dedbd3bSFrançois Tigeot 		ret = fb->funcs->dirty(fb, file_priv, flags, r->color,
5351dedbd3bSFrançois Tigeot 				       clips, num_clips);
5361dedbd3bSFrançois Tigeot 	} else {
5371dedbd3bSFrançois Tigeot 		ret = -ENOSYS;
5381dedbd3bSFrançois Tigeot 	}
5391dedbd3bSFrançois Tigeot 
5401dedbd3bSFrançois Tigeot out_err2:
5411dedbd3bSFrançois Tigeot 	kfree(clips);
5421dedbd3bSFrançois Tigeot out_err1:
5431dedbd3bSFrançois Tigeot 	drm_framebuffer_unreference(fb);
5441dedbd3bSFrançois Tigeot 
5451dedbd3bSFrançois Tigeot 	return ret;
5461dedbd3bSFrançois Tigeot }
5471dedbd3bSFrançois Tigeot 
5481dedbd3bSFrançois Tigeot /**
5491dedbd3bSFrançois Tigeot  * drm_fb_release - remove and free the FBs on this file
5501dedbd3bSFrançois Tigeot  * @priv: drm file for the ioctl
5511dedbd3bSFrançois Tigeot  *
5521dedbd3bSFrançois Tigeot  * Destroy all the FBs associated with @filp.
5531dedbd3bSFrançois Tigeot  *
5541dedbd3bSFrançois Tigeot  * Called by the user via ioctl.
5551dedbd3bSFrançois Tigeot  *
5561dedbd3bSFrançois Tigeot  * Returns:
5571dedbd3bSFrançois Tigeot  * Zero on success, negative errno on failure.
5581dedbd3bSFrançois Tigeot  */
5591dedbd3bSFrançois Tigeot void drm_fb_release(struct drm_file *priv)
5601dedbd3bSFrançois Tigeot {
5611dedbd3bSFrançois Tigeot 	struct drm_framebuffer *fb, *tfb;
5621dedbd3bSFrançois Tigeot 	struct drm_mode_rmfb_work arg;
5631dedbd3bSFrançois Tigeot 
5641dedbd3bSFrançois Tigeot 	INIT_LIST_HEAD(&arg.fbs);
5651dedbd3bSFrançois Tigeot 
5661dedbd3bSFrançois Tigeot 	/*
5671dedbd3bSFrançois Tigeot 	 * When the file gets released that means no one else can access the fb
5681dedbd3bSFrançois Tigeot 	 * list any more, so no need to grab fpriv->fbs_lock. And we need to
5691dedbd3bSFrançois Tigeot 	 * avoid upsetting lockdep since the universal cursor code adds a
5701dedbd3bSFrançois Tigeot 	 * framebuffer while holding mutex locks.
5711dedbd3bSFrançois Tigeot 	 *
5721dedbd3bSFrançois Tigeot 	 * Note that a real deadlock between fpriv->fbs_lock and the modeset
5731dedbd3bSFrançois Tigeot 	 * locks is impossible here since no one else but this function can get
5741dedbd3bSFrançois Tigeot 	 * at it any more.
5751dedbd3bSFrançois Tigeot 	 */
5761dedbd3bSFrançois Tigeot 	list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
5771dedbd3bSFrançois Tigeot 		if (drm_framebuffer_read_refcount(fb) > 1) {
5781dedbd3bSFrançois Tigeot 			list_move_tail(&fb->filp_head, &arg.fbs);
5791dedbd3bSFrançois Tigeot 		} else {
5801dedbd3bSFrançois Tigeot 			list_del_init(&fb->filp_head);
5811dedbd3bSFrançois Tigeot 
5821dedbd3bSFrançois Tigeot 			/* This drops the fpriv->fbs reference. */
5831dedbd3bSFrançois Tigeot 			drm_framebuffer_unreference(fb);
5841dedbd3bSFrançois Tigeot 		}
5851dedbd3bSFrançois Tigeot 	}
5861dedbd3bSFrançois Tigeot 
5871dedbd3bSFrançois Tigeot 	if (!list_empty(&arg.fbs)) {
5881dedbd3bSFrançois Tigeot 		INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
5891dedbd3bSFrançois Tigeot 
5901dedbd3bSFrançois Tigeot 		schedule_work(&arg.work);
5911dedbd3bSFrançois Tigeot 		flush_work(&arg.work);
5921dedbd3bSFrançois Tigeot 		destroy_work_on_stack(&arg.work);
5931dedbd3bSFrançois Tigeot 	}
5941dedbd3bSFrançois Tigeot }
5951dedbd3bSFrançois Tigeot 
5961dedbd3bSFrançois Tigeot void drm_framebuffer_free(struct kref *kref)
5971dedbd3bSFrançois Tigeot {
5981dedbd3bSFrançois Tigeot 	struct drm_framebuffer *fb =
5991dedbd3bSFrançois Tigeot 			container_of(kref, struct drm_framebuffer, base.refcount);
6001dedbd3bSFrançois Tigeot 	struct drm_device *dev = fb->dev;
6011dedbd3bSFrançois Tigeot 
6021dedbd3bSFrançois Tigeot 	/*
6031dedbd3bSFrançois Tigeot 	 * The lookup idr holds a weak reference, which has not necessarily been
6041dedbd3bSFrançois Tigeot 	 * removed at this point. Check for that.
6051dedbd3bSFrançois Tigeot 	 */
6061dedbd3bSFrançois Tigeot 	drm_mode_object_unregister(dev, &fb->base);
6071dedbd3bSFrançois Tigeot 
6081dedbd3bSFrançois Tigeot 	fb->funcs->destroy(fb);
6091dedbd3bSFrançois Tigeot }
6101dedbd3bSFrançois Tigeot 
6111dedbd3bSFrançois Tigeot /**
6121dedbd3bSFrançois Tigeot  * drm_framebuffer_init - initialize a framebuffer
6131dedbd3bSFrançois Tigeot  * @dev: DRM device
6141dedbd3bSFrançois Tigeot  * @fb: framebuffer to be initialized
6151dedbd3bSFrançois Tigeot  * @funcs: ... with these functions
6161dedbd3bSFrançois Tigeot  *
6171dedbd3bSFrançois Tigeot  * Allocates an ID for the framebuffer's parent mode object, sets its mode
6181dedbd3bSFrançois Tigeot  * functions & device file and adds it to the master fd list.
6191dedbd3bSFrançois Tigeot  *
6201dedbd3bSFrançois Tigeot  * IMPORTANT:
6211dedbd3bSFrançois Tigeot  * This functions publishes the fb and makes it available for concurrent access
6221dedbd3bSFrançois Tigeot  * by other users. Which means by this point the fb _must_ be fully set up -
6231dedbd3bSFrançois Tigeot  * since all the fb attributes are invariant over its lifetime, no further
6241dedbd3bSFrançois Tigeot  * locking but only correct reference counting is required.
6251dedbd3bSFrançois Tigeot  *
6261dedbd3bSFrançois Tigeot  * Returns:
6271dedbd3bSFrançois Tigeot  * Zero on success, error code on failure.
6281dedbd3bSFrançois Tigeot  */
6291dedbd3bSFrançois Tigeot int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
6301dedbd3bSFrançois Tigeot 			 const struct drm_framebuffer_funcs *funcs)
6311dedbd3bSFrançois Tigeot {
6321dedbd3bSFrançois Tigeot 	int ret;
6331dedbd3bSFrançois Tigeot 
6341dedbd3bSFrançois Tigeot 	INIT_LIST_HEAD(&fb->filp_head);
6351dedbd3bSFrançois Tigeot 	fb->dev = dev;
6361dedbd3bSFrançois Tigeot 	fb->funcs = funcs;
6371dedbd3bSFrançois Tigeot 
6381dedbd3bSFrançois Tigeot 	ret = drm_mode_object_get_reg(dev, &fb->base, DRM_MODE_OBJECT_FB,
6391dedbd3bSFrançois Tigeot 				      false, drm_framebuffer_free);
6401dedbd3bSFrançois Tigeot 	if (ret)
6411dedbd3bSFrançois Tigeot 		goto out;
6421dedbd3bSFrançois Tigeot 
6431dedbd3bSFrançois Tigeot 	mutex_lock(&dev->mode_config.fb_lock);
6441dedbd3bSFrançois Tigeot 	dev->mode_config.num_fb++;
6451dedbd3bSFrançois Tigeot 	list_add(&fb->head, &dev->mode_config.fb_list);
6461dedbd3bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.fb_lock);
6471dedbd3bSFrançois Tigeot 
6481dedbd3bSFrançois Tigeot 	drm_mode_object_register(dev, &fb->base);
6491dedbd3bSFrançois Tigeot out:
6501dedbd3bSFrançois Tigeot 	return ret;
6511dedbd3bSFrançois Tigeot }
6521dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_init);
6531dedbd3bSFrançois Tigeot 
6541dedbd3bSFrançois Tigeot /**
6551dedbd3bSFrançois Tigeot  * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference
6561dedbd3bSFrançois Tigeot  * @dev: drm device
6571dedbd3bSFrançois Tigeot  * @id: id of the fb object
6581dedbd3bSFrançois Tigeot  *
6591dedbd3bSFrançois Tigeot  * If successful, this grabs an additional reference to the framebuffer -
6601dedbd3bSFrançois Tigeot  * callers need to make sure to eventually unreference the returned framebuffer
6611dedbd3bSFrançois Tigeot  * again, using @drm_framebuffer_unreference.
6621dedbd3bSFrançois Tigeot  */
6631dedbd3bSFrançois Tigeot struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
6641dedbd3bSFrançois Tigeot 					       uint32_t id)
6651dedbd3bSFrançois Tigeot {
6661dedbd3bSFrançois Tigeot 	struct drm_mode_object *obj;
6671dedbd3bSFrançois Tigeot 	struct drm_framebuffer *fb = NULL;
6681dedbd3bSFrançois Tigeot 
6691dedbd3bSFrançois Tigeot 	obj = __drm_mode_object_find(dev, id, DRM_MODE_OBJECT_FB);
6701dedbd3bSFrançois Tigeot 	if (obj)
6711dedbd3bSFrançois Tigeot 		fb = obj_to_fb(obj);
6721dedbd3bSFrançois Tigeot 	return fb;
6731dedbd3bSFrançois Tigeot }
6741dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_lookup);
6751dedbd3bSFrançois Tigeot 
6761dedbd3bSFrançois Tigeot /**
6771dedbd3bSFrançois Tigeot  * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
6781dedbd3bSFrançois Tigeot  * @fb: fb to unregister
6791dedbd3bSFrançois Tigeot  *
6801dedbd3bSFrançois Tigeot  * Drivers need to call this when cleaning up driver-private framebuffers, e.g.
6811dedbd3bSFrançois Tigeot  * those used for fbdev. Note that the caller must hold a reference of it's own,
6821dedbd3bSFrançois Tigeot  * i.e. the object may not be destroyed through this call (since it'll lead to a
6831dedbd3bSFrançois Tigeot  * locking inversion).
684*4be47400SFrançois Tigeot  *
685*4be47400SFrançois Tigeot  * NOTE: This function is deprecated. For driver-private framebuffers it is not
686*4be47400SFrançois Tigeot  * recommended to embed a framebuffer struct info fbdev struct, instead, a
687*4be47400SFrançois Tigeot  * framebuffer pointer is preferred and drm_framebuffer_unreference() should be
688*4be47400SFrançois Tigeot  * called when the framebuffer is to be cleaned up.
6891dedbd3bSFrançois Tigeot  */
6901dedbd3bSFrançois Tigeot void drm_framebuffer_unregister_private(struct drm_framebuffer *fb)
6911dedbd3bSFrançois Tigeot {
6921dedbd3bSFrançois Tigeot 	struct drm_device *dev;
6931dedbd3bSFrançois Tigeot 
6941dedbd3bSFrançois Tigeot 	if (!fb)
6951dedbd3bSFrançois Tigeot 		return;
6961dedbd3bSFrançois Tigeot 
6971dedbd3bSFrançois Tigeot 	dev = fb->dev;
6981dedbd3bSFrançois Tigeot 
6991dedbd3bSFrançois Tigeot 	/* Mark fb as reaped and drop idr ref. */
7001dedbd3bSFrançois Tigeot 	drm_mode_object_unregister(dev, &fb->base);
7011dedbd3bSFrançois Tigeot }
7021dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_unregister_private);
7031dedbd3bSFrançois Tigeot 
7041dedbd3bSFrançois Tigeot /**
7051dedbd3bSFrançois Tigeot  * drm_framebuffer_cleanup - remove a framebuffer object
7061dedbd3bSFrançois Tigeot  * @fb: framebuffer to remove
7071dedbd3bSFrançois Tigeot  *
7081dedbd3bSFrançois Tigeot  * Cleanup framebuffer. This function is intended to be used from the drivers
7091dedbd3bSFrançois Tigeot  * ->destroy callback. It can also be used to clean up driver private
7101dedbd3bSFrançois Tigeot  * framebuffers embedded into a larger structure.
7111dedbd3bSFrançois Tigeot  *
7121dedbd3bSFrançois Tigeot  * Note that this function does not remove the fb from active usuage - if it is
7131dedbd3bSFrançois Tigeot  * still used anywhere, hilarity can ensue since userspace could call getfb on
7141dedbd3bSFrançois Tigeot  * the id and get back -EINVAL. Obviously no concern at driver unload time.
7151dedbd3bSFrançois Tigeot  *
7161dedbd3bSFrançois Tigeot  * Also, the framebuffer will not be removed from the lookup idr - for
7171dedbd3bSFrançois Tigeot  * user-created framebuffers this will happen in in the rmfb ioctl. For
7181dedbd3bSFrançois Tigeot  * driver-private objects (e.g. for fbdev) drivers need to explicitly call
7191dedbd3bSFrançois Tigeot  * drm_framebuffer_unregister_private.
7201dedbd3bSFrançois Tigeot  */
7211dedbd3bSFrançois Tigeot void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
7221dedbd3bSFrançois Tigeot {
7231dedbd3bSFrançois Tigeot 	struct drm_device *dev = fb->dev;
7241dedbd3bSFrançois Tigeot 
7251dedbd3bSFrançois Tigeot 	mutex_lock(&dev->mode_config.fb_lock);
7261dedbd3bSFrançois Tigeot 	list_del(&fb->head);
7271dedbd3bSFrançois Tigeot 	dev->mode_config.num_fb--;
7281dedbd3bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.fb_lock);
7291dedbd3bSFrançois Tigeot }
7301dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_cleanup);
7311dedbd3bSFrançois Tigeot 
7321dedbd3bSFrançois Tigeot /**
7331dedbd3bSFrançois Tigeot  * drm_framebuffer_remove - remove and unreference a framebuffer object
7341dedbd3bSFrançois Tigeot  * @fb: framebuffer to remove
7351dedbd3bSFrançois Tigeot  *
7361dedbd3bSFrançois Tigeot  * Scans all the CRTCs and planes in @dev's mode_config.  If they're
7371dedbd3bSFrançois Tigeot  * using @fb, removes it, setting it to NULL. Then drops the reference to the
7381dedbd3bSFrançois Tigeot  * passed-in framebuffer. Might take the modeset locks.
7391dedbd3bSFrançois Tigeot  *
7401dedbd3bSFrançois Tigeot  * Note that this function optimizes the cleanup away if the caller holds the
7411dedbd3bSFrançois Tigeot  * last reference to the framebuffer. It is also guaranteed to not take the
7421dedbd3bSFrançois Tigeot  * modeset locks in this case.
7431dedbd3bSFrançois Tigeot  */
7441dedbd3bSFrançois Tigeot void drm_framebuffer_remove(struct drm_framebuffer *fb)
7451dedbd3bSFrançois Tigeot {
7461dedbd3bSFrançois Tigeot 	struct drm_device *dev;
7471dedbd3bSFrançois Tigeot 	struct drm_crtc *crtc;
7481dedbd3bSFrançois Tigeot 	struct drm_plane *plane;
7491dedbd3bSFrançois Tigeot 
7501dedbd3bSFrançois Tigeot 	if (!fb)
7511dedbd3bSFrançois Tigeot 		return;
7521dedbd3bSFrançois Tigeot 
7531dedbd3bSFrançois Tigeot 	dev = fb->dev;
7541dedbd3bSFrançois Tigeot 
7551dedbd3bSFrançois Tigeot 	WARN_ON(!list_empty(&fb->filp_head));
7561dedbd3bSFrançois Tigeot 
7571dedbd3bSFrançois Tigeot 	/*
7581dedbd3bSFrançois Tigeot 	 * drm ABI mandates that we remove any deleted framebuffers from active
7591dedbd3bSFrançois Tigeot 	 * useage. But since most sane clients only remove framebuffers they no
7601dedbd3bSFrançois Tigeot 	 * longer need, try to optimize this away.
7611dedbd3bSFrançois Tigeot 	 *
7621dedbd3bSFrançois Tigeot 	 * Since we're holding a reference ourselves, observing a refcount of 1
7631dedbd3bSFrançois Tigeot 	 * means that we're the last holder and can skip it. Also, the refcount
7641dedbd3bSFrançois Tigeot 	 * can never increase from 1 again, so we don't need any barriers or
7651dedbd3bSFrançois Tigeot 	 * locks.
7661dedbd3bSFrançois Tigeot 	 *
7671dedbd3bSFrançois Tigeot 	 * Note that userspace could try to race with use and instate a new
7681dedbd3bSFrançois Tigeot 	 * usage _after_ we've cleared all current ones. End result will be an
7691dedbd3bSFrançois Tigeot 	 * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot
7701dedbd3bSFrançois Tigeot 	 * in this manner.
7711dedbd3bSFrançois Tigeot 	 */
7721dedbd3bSFrançois Tigeot 	if (drm_framebuffer_read_refcount(fb) > 1) {
7731dedbd3bSFrançois Tigeot 		drm_modeset_lock_all(dev);
7741dedbd3bSFrançois Tigeot 		/* remove from any CRTC */
7751dedbd3bSFrançois Tigeot 		drm_for_each_crtc(crtc, dev) {
7761dedbd3bSFrançois Tigeot 			if (crtc->primary->fb == fb) {
7771dedbd3bSFrançois Tigeot 				/* should turn off the crtc */
7781dedbd3bSFrançois Tigeot 				if (drm_crtc_force_disable(crtc))
7791dedbd3bSFrançois Tigeot 					DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
7801dedbd3bSFrançois Tigeot 			}
7811dedbd3bSFrançois Tigeot 		}
7821dedbd3bSFrançois Tigeot 
7831dedbd3bSFrançois Tigeot 		drm_for_each_plane(plane, dev) {
7841dedbd3bSFrançois Tigeot 			if (plane->fb == fb)
7851dedbd3bSFrançois Tigeot 				drm_plane_force_disable(plane);
7861dedbd3bSFrançois Tigeot 		}
7871dedbd3bSFrançois Tigeot 		drm_modeset_unlock_all(dev);
7881dedbd3bSFrançois Tigeot 	}
7891dedbd3bSFrançois Tigeot 
7901dedbd3bSFrançois Tigeot 	drm_framebuffer_unreference(fb);
7911dedbd3bSFrançois Tigeot }
7921dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_remove);
793