xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/drm_framebuffer.c (revision 2514f435bdec593d794175f965f3a86b06a21af0)
1*2514f435Sriastradh /*	$NetBSD: drm_framebuffer.c,v 1.6 2021/12/19 09:47:44 riastradh Exp $	*/
24e390cabSriastradh 
34e390cabSriastradh /*
44e390cabSriastradh  * Copyright (c) 2016 Intel Corporation
54e390cabSriastradh  *
64e390cabSriastradh  * Permission to use, copy, modify, distribute, and sell this software and its
74e390cabSriastradh  * documentation for any purpose is hereby granted without fee, provided that
84e390cabSriastradh  * the above copyright notice appear in all copies and that both that copyright
94e390cabSriastradh  * notice and this permission notice appear in supporting documentation, and
104e390cabSriastradh  * that the name of the copyright holders not be used in advertising or
114e390cabSriastradh  * publicity pertaining to distribution of the software without specific,
124e390cabSriastradh  * written prior permission.  The copyright holders make no representations
134e390cabSriastradh  * about the suitability of this software for any purpose.  It is provided "as
144e390cabSriastradh  * is" without express or implied warranty.
154e390cabSriastradh  *
164e390cabSriastradh  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
174e390cabSriastradh  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
184e390cabSriastradh  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
194e390cabSriastradh  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
204e390cabSriastradh  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
214e390cabSriastradh  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
224e390cabSriastradh  * OF THIS SOFTWARE.
234e390cabSriastradh  */
244e390cabSriastradh 
254e390cabSriastradh #include <sys/cdefs.h>
26*2514f435Sriastradh __KERNEL_RCSID(0, "$NetBSD: drm_framebuffer.c,v 1.6 2021/12/19 09:47:44 riastradh Exp $");
274e390cabSriastradh 
284e390cabSriastradh #include <linux/export.h>
29a80c87d5Sriastradh #include <linux/capability.h>
304e390cabSriastradh #include <linux/uaccess.h>
314e390cabSriastradh 
324e390cabSriastradh #include <drm/drm_atomic.h>
334e390cabSriastradh #include <drm/drm_atomic_uapi.h>
344e390cabSriastradh #include <drm/drm_auth.h>
354e390cabSriastradh #include <drm/drm_debugfs.h>
364e390cabSriastradh #include <drm/drm_drv.h>
374e390cabSriastradh #include <drm/drm_file.h>
384e390cabSriastradh #include <drm/drm_fourcc.h>
394e390cabSriastradh #include <drm/drm_framebuffer.h>
404e390cabSriastradh #include <drm/drm_print.h>
414e390cabSriastradh #include <drm/drm_util.h>
424e390cabSriastradh 
434e390cabSriastradh #include "drm_crtc_internal.h"
444e390cabSriastradh #include "drm_internal.h"
454e390cabSriastradh 
464e390cabSriastradh /**
474e390cabSriastradh  * DOC: overview
484e390cabSriastradh  *
494e390cabSriastradh  * Frame buffers are abstract memory objects that provide a source of pixels to
504e390cabSriastradh  * scanout to a CRTC. Applications explicitly request the creation of frame
514e390cabSriastradh  * buffers through the DRM_IOCTL_MODE_ADDFB(2) ioctls and receive an opaque
524e390cabSriastradh  * handle that can be passed to the KMS CRTC control, plane configuration and
534e390cabSriastradh  * page flip functions.
544e390cabSriastradh  *
554e390cabSriastradh  * Frame buffers rely on the underlying memory manager for allocating backing
564e390cabSriastradh  * storage. When creating a frame buffer applications pass a memory handle
574e390cabSriastradh  * (or a list of memory handles for multi-planar formats) through the
584e390cabSriastradh  * &struct drm_mode_fb_cmd2 argument. For drivers using GEM as their userspace
594e390cabSriastradh  * buffer management interface this would be a GEM handle.  Drivers are however
604e390cabSriastradh  * free to use their own backing storage object handles, e.g. vmwgfx directly
614e390cabSriastradh  * exposes special TTM handles to userspace and so expects TTM handles in the
624e390cabSriastradh  * create ioctl and not GEM handles.
634e390cabSriastradh  *
644e390cabSriastradh  * Framebuffers are tracked with &struct drm_framebuffer. They are published
654e390cabSriastradh  * using drm_framebuffer_init() - after calling that function userspace can use
664e390cabSriastradh  * and access the framebuffer object. The helper function
674e390cabSriastradh  * drm_helper_mode_fill_fb_struct() can be used to pre-fill the required
684e390cabSriastradh  * metadata fields.
694e390cabSriastradh  *
704e390cabSriastradh  * The lifetime of a drm framebuffer is controlled with a reference count,
714e390cabSriastradh  * drivers can grab additional references with drm_framebuffer_get() and drop
724e390cabSriastradh  * them again with drm_framebuffer_put(). For driver-private framebuffers for
734e390cabSriastradh  * which the last reference is never dropped (e.g. for the fbdev framebuffer
744e390cabSriastradh  * when the struct &struct drm_framebuffer is embedded into the fbdev helper
754e390cabSriastradh  * struct) drivers can manually clean up a framebuffer at module unload time
764e390cabSriastradh  * with drm_framebuffer_unregister_private(). But doing this is not
774e390cabSriastradh  * recommended, and it's better to have a normal free-standing &struct
784e390cabSriastradh  * drm_framebuffer.
794e390cabSriastradh  */
804e390cabSriastradh 
drm_framebuffer_check_src_coords(uint32_t src_x,uint32_t src_y,uint32_t src_w,uint32_t src_h,const struct drm_framebuffer * fb)814e390cabSriastradh int drm_framebuffer_check_src_coords(uint32_t src_x, uint32_t src_y,
824e390cabSriastradh 				     uint32_t src_w, uint32_t src_h,
834e390cabSriastradh 				     const struct drm_framebuffer *fb)
844e390cabSriastradh {
854e390cabSriastradh 	unsigned int fb_width, fb_height;
864e390cabSriastradh 
874e390cabSriastradh 	fb_width = fb->width << 16;
884e390cabSriastradh 	fb_height = fb->height << 16;
894e390cabSriastradh 
904e390cabSriastradh 	/* Make sure source coordinates are inside the fb. */
914e390cabSriastradh 	if (src_w > fb_width ||
924e390cabSriastradh 	    src_x > fb_width - src_w ||
934e390cabSriastradh 	    src_h > fb_height ||
944e390cabSriastradh 	    src_y > fb_height - src_h) {
954e390cabSriastradh 		DRM_DEBUG_KMS("Invalid source coordinates "
964e390cabSriastradh 			      "%u.%06ux%u.%06u+%u.%06u+%u.%06u (fb %ux%u)\n",
974e390cabSriastradh 			      src_w >> 16, ((src_w & 0xffff) * 15625) >> 10,
984e390cabSriastradh 			      src_h >> 16, ((src_h & 0xffff) * 15625) >> 10,
994e390cabSriastradh 			      src_x >> 16, ((src_x & 0xffff) * 15625) >> 10,
1004e390cabSriastradh 			      src_y >> 16, ((src_y & 0xffff) * 15625) >> 10,
1014e390cabSriastradh 			      fb->width, fb->height);
1024e390cabSriastradh 		return -ENOSPC;
1034e390cabSriastradh 	}
1044e390cabSriastradh 
1054e390cabSriastradh 	return 0;
1064e390cabSriastradh }
1074e390cabSriastradh 
1084e390cabSriastradh /**
1094e390cabSriastradh  * drm_mode_addfb - add an FB to the graphics configuration
1104e390cabSriastradh  * @dev: drm device for the ioctl
1114e390cabSriastradh  * @or: pointer to request structure
1124e390cabSriastradh  * @file_priv: drm file
1134e390cabSriastradh  *
1144e390cabSriastradh  * Add a new FB to the specified CRTC, given a user request. This is the
1154e390cabSriastradh  * original addfb ioctl which only supported RGB formats.
1164e390cabSriastradh  *
1174e390cabSriastradh  * Called by the user via ioctl, or by an in-kernel client.
1184e390cabSriastradh  *
1194e390cabSriastradh  * Returns:
1204e390cabSriastradh  * Zero on success, negative errno on failure.
1214e390cabSriastradh  */
drm_mode_addfb(struct drm_device * dev,struct drm_mode_fb_cmd * or,struct drm_file * file_priv)1224e390cabSriastradh int drm_mode_addfb(struct drm_device *dev, struct drm_mode_fb_cmd *or,
1234e390cabSriastradh 		   struct drm_file *file_priv)
1244e390cabSriastradh {
1254e390cabSriastradh 	struct drm_mode_fb_cmd2 r = {};
1264e390cabSriastradh 	int ret;
1274e390cabSriastradh 
1284e390cabSriastradh 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
1294e390cabSriastradh 		return -EOPNOTSUPP;
1304e390cabSriastradh 
1314e390cabSriastradh 	r.pixel_format = drm_driver_legacy_fb_format(dev, or->bpp, or->depth);
1324e390cabSriastradh 	if (r.pixel_format == DRM_FORMAT_INVALID) {
1334e390cabSriastradh 		DRM_DEBUG("bad {bpp:%d, depth:%d}\n", or->bpp, or->depth);
1344e390cabSriastradh 		return -EINVAL;
1354e390cabSriastradh 	}
1364e390cabSriastradh 
1374e390cabSriastradh 	/* convert to new format and call new ioctl */
1384e390cabSriastradh 	r.fb_id = or->fb_id;
1394e390cabSriastradh 	r.width = or->width;
1404e390cabSriastradh 	r.height = or->height;
1414e390cabSriastradh 	r.pitches[0] = or->pitch;
1424e390cabSriastradh 	r.handles[0] = or->handle;
1434e390cabSriastradh 
1444e390cabSriastradh 	ret = drm_mode_addfb2(dev, &r, file_priv);
1454e390cabSriastradh 	if (ret)
1464e390cabSriastradh 		return ret;
1474e390cabSriastradh 
1484e390cabSriastradh 	or->fb_id = r.fb_id;
1494e390cabSriastradh 
1504e390cabSriastradh 	return 0;
1514e390cabSriastradh }
1524e390cabSriastradh 
drm_mode_addfb_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)1534e390cabSriastradh int drm_mode_addfb_ioctl(struct drm_device *dev,
1544e390cabSriastradh 			 void *data, struct drm_file *file_priv)
1554e390cabSriastradh {
1564e390cabSriastradh 	return drm_mode_addfb(dev, data, file_priv);
1574e390cabSriastradh }
1584e390cabSriastradh 
fb_plane_width(int width,const struct drm_format_info * format,int plane)1594e390cabSriastradh static int fb_plane_width(int width,
1604e390cabSriastradh 			  const struct drm_format_info *format, int plane)
1614e390cabSriastradh {
1624e390cabSriastradh 	if (plane == 0)
1634e390cabSriastradh 		return width;
1644e390cabSriastradh 
1654e390cabSriastradh 	return DIV_ROUND_UP(width, format->hsub);
1664e390cabSriastradh }
1674e390cabSriastradh 
fb_plane_height(int height,const struct drm_format_info * format,int plane)1684e390cabSriastradh static int fb_plane_height(int height,
1694e390cabSriastradh 			   const struct drm_format_info *format, int plane)
1704e390cabSriastradh {
1714e390cabSriastradh 	if (plane == 0)
1724e390cabSriastradh 		return height;
1734e390cabSriastradh 
1744e390cabSriastradh 	return DIV_ROUND_UP(height, format->vsub);
1754e390cabSriastradh }
1764e390cabSriastradh 
framebuffer_check(struct drm_device * dev,const struct drm_mode_fb_cmd2 * r)1774e390cabSriastradh static int framebuffer_check(struct drm_device *dev,
1784e390cabSriastradh 			     const struct drm_mode_fb_cmd2 *r)
1794e390cabSriastradh {
1804e390cabSriastradh 	const struct drm_format_info *info;
1814e390cabSriastradh 	int i;
1824e390cabSriastradh 
1834e390cabSriastradh 	/* check if the format is supported at all */
1844e390cabSriastradh 	info = __drm_format_info(r->pixel_format);
1854e390cabSriastradh 	if (!info) {
1864e390cabSriastradh 		struct drm_format_name_buf format_name;
1874e390cabSriastradh 
1884e390cabSriastradh 		DRM_DEBUG_KMS("bad framebuffer format %s\n",
1894e390cabSriastradh 			      drm_get_format_name(r->pixel_format,
1904e390cabSriastradh 						  &format_name));
1914e390cabSriastradh 		return -EINVAL;
1924e390cabSriastradh 	}
1934e390cabSriastradh 
1944e390cabSriastradh 	/* now let the driver pick its own format info */
1954e390cabSriastradh 	info = drm_get_format_info(dev, r);
1964e390cabSriastradh 
1974e390cabSriastradh 	if (r->width == 0) {
1984e390cabSriastradh 		DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width);
1994e390cabSriastradh 		return -EINVAL;
2004e390cabSriastradh 	}
2014e390cabSriastradh 
2024e390cabSriastradh 	if (r->height == 0) {
2034e390cabSriastradh 		DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height);
2044e390cabSriastradh 		return -EINVAL;
2054e390cabSriastradh 	}
2064e390cabSriastradh 
2074e390cabSriastradh 	for (i = 0; i < info->num_planes; i++) {
2084e390cabSriastradh 		unsigned int width = fb_plane_width(r->width, info, i);
2094e390cabSriastradh 		unsigned int height = fb_plane_height(r->height, info, i);
2104e390cabSriastradh 		unsigned int block_size = info->char_per_block[i];
2114e390cabSriastradh 		u64 min_pitch = drm_format_info_min_pitch(info, i, width);
2124e390cabSriastradh 
2134e390cabSriastradh 		if (!block_size && (r->modifier[i] == DRM_FORMAT_MOD_LINEAR)) {
2144e390cabSriastradh 			DRM_DEBUG_KMS("Format requires non-linear modifier for plane %d\n", i);
2154e390cabSriastradh 			return -EINVAL;
2164e390cabSriastradh 		}
2174e390cabSriastradh 
2184e390cabSriastradh 		if (!r->handles[i]) {
2194e390cabSriastradh 			DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i);
2204e390cabSriastradh 			return -EINVAL;
2214e390cabSriastradh 		}
2224e390cabSriastradh 
2234e390cabSriastradh 		if (min_pitch > UINT_MAX)
2244e390cabSriastradh 			return -ERANGE;
2254e390cabSriastradh 
2264e390cabSriastradh 		if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX)
2274e390cabSriastradh 			return -ERANGE;
2284e390cabSriastradh 
2294e390cabSriastradh 		if (block_size && r->pitches[i] < min_pitch) {
2304e390cabSriastradh 			DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i);
2314e390cabSriastradh 			return -EINVAL;
2324e390cabSriastradh 		}
2334e390cabSriastradh 
2344e390cabSriastradh 		if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) {
235*2514f435Sriastradh 			DRM_DEBUG_KMS("bad fb modifier %"PRIu64" for plane %d\n",
2364e390cabSriastradh 				      r->modifier[i], i);
2374e390cabSriastradh 			return -EINVAL;
2384e390cabSriastradh 		}
2394e390cabSriastradh 
2404e390cabSriastradh 		if (r->flags & DRM_MODE_FB_MODIFIERS &&
2414e390cabSriastradh 		    r->modifier[i] != r->modifier[0]) {
242*2514f435Sriastradh 			DRM_DEBUG_KMS("bad fb modifier %"PRIu64" for plane %d\n",
2434e390cabSriastradh 				      r->modifier[i], i);
2444e390cabSriastradh 			return -EINVAL;
2454e390cabSriastradh 		}
2464e390cabSriastradh 
2474e390cabSriastradh 		/* modifier specific checks: */
2484e390cabSriastradh 		switch (r->modifier[i]) {
2494e390cabSriastradh 		case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE:
2504e390cabSriastradh 			/* NOTE: the pitch restriction may be lifted later if it turns
2514e390cabSriastradh 			 * out that no hw has this restriction:
2524e390cabSriastradh 			 */
2534e390cabSriastradh 			if (r->pixel_format != DRM_FORMAT_NV12 ||
2544e390cabSriastradh 					width % 128 || height % 32 ||
2554e390cabSriastradh 					r->pitches[i] % 128) {
2564e390cabSriastradh 				DRM_DEBUG_KMS("bad modifier data for plane %d\n", i);
2574e390cabSriastradh 				return -EINVAL;
2584e390cabSriastradh 			}
2594e390cabSriastradh 			break;
2604e390cabSriastradh 
2614e390cabSriastradh 		default:
2624e390cabSriastradh 			break;
2634e390cabSriastradh 		}
2644e390cabSriastradh 	}
2654e390cabSriastradh 
2664e390cabSriastradh 	for (i = info->num_planes; i < 4; i++) {
2674e390cabSriastradh 		if (r->modifier[i]) {
2684e390cabSriastradh 			DRM_DEBUG_KMS("non-zero modifier for unused plane %d\n", i);
2694e390cabSriastradh 			return -EINVAL;
2704e390cabSriastradh 		}
2714e390cabSriastradh 
2724e390cabSriastradh 		/* Pre-FB_MODIFIERS userspace didn't clear the structs properly. */
2734e390cabSriastradh 		if (!(r->flags & DRM_MODE_FB_MODIFIERS))
2744e390cabSriastradh 			continue;
2754e390cabSriastradh 
2764e390cabSriastradh 		if (r->handles[i]) {
2774e390cabSriastradh 			DRM_DEBUG_KMS("buffer object handle for unused plane %d\n", i);
2784e390cabSriastradh 			return -EINVAL;
2794e390cabSriastradh 		}
2804e390cabSriastradh 
2814e390cabSriastradh 		if (r->pitches[i]) {
2824e390cabSriastradh 			DRM_DEBUG_KMS("non-zero pitch for unused plane %d\n", i);
2834e390cabSriastradh 			return -EINVAL;
2844e390cabSriastradh 		}
2854e390cabSriastradh 
2864e390cabSriastradh 		if (r->offsets[i]) {
2874e390cabSriastradh 			DRM_DEBUG_KMS("non-zero offset for unused plane %d\n", i);
2884e390cabSriastradh 			return -EINVAL;
2894e390cabSriastradh 		}
2904e390cabSriastradh 	}
2914e390cabSriastradh 
2924e390cabSriastradh 	return 0;
2934e390cabSriastradh }
2944e390cabSriastradh 
2954e390cabSriastradh struct drm_framebuffer *
drm_internal_framebuffer_create(struct drm_device * dev,const struct drm_mode_fb_cmd2 * r,struct drm_file * file_priv)2964e390cabSriastradh drm_internal_framebuffer_create(struct drm_device *dev,
2974e390cabSriastradh 				const struct drm_mode_fb_cmd2 *r,
2984e390cabSriastradh 				struct drm_file *file_priv)
2994e390cabSriastradh {
3004e390cabSriastradh 	struct drm_mode_config *config = &dev->mode_config;
3014e390cabSriastradh 	struct drm_framebuffer *fb;
3024e390cabSriastradh 	int ret;
3034e390cabSriastradh 
3044e390cabSriastradh 	if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) {
3054e390cabSriastradh 		DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags);
3064e390cabSriastradh 		return ERR_PTR(-EINVAL);
3074e390cabSriastradh 	}
3084e390cabSriastradh 
3094e390cabSriastradh 	if ((config->min_width > r->width) || (r->width > config->max_width)) {
3104e390cabSriastradh 		DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n",
3114e390cabSriastradh 			  r->width, config->min_width, config->max_width);
3124e390cabSriastradh 		return ERR_PTR(-EINVAL);
3134e390cabSriastradh 	}
3144e390cabSriastradh 	if ((config->min_height > r->height) || (r->height > config->max_height)) {
3154e390cabSriastradh 		DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n",
3164e390cabSriastradh 			  r->height, config->min_height, config->max_height);
3174e390cabSriastradh 		return ERR_PTR(-EINVAL);
3184e390cabSriastradh 	}
3194e390cabSriastradh 
3204e390cabSriastradh 	if (r->flags & DRM_MODE_FB_MODIFIERS &&
3214e390cabSriastradh 	    !dev->mode_config.allow_fb_modifiers) {
3224e390cabSriastradh 		DRM_DEBUG_KMS("driver does not support fb modifiers\n");
3234e390cabSriastradh 		return ERR_PTR(-EINVAL);
3244e390cabSriastradh 	}
3254e390cabSriastradh 
3264e390cabSriastradh 	ret = framebuffer_check(dev, r);
3274e390cabSriastradh 	if (ret)
3284e390cabSriastradh 		return ERR_PTR(ret);
3294e390cabSriastradh 
3304e390cabSriastradh 	fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
3314e390cabSriastradh 	if (IS_ERR(fb)) {
3324e390cabSriastradh 		DRM_DEBUG_KMS("could not create framebuffer\n");
3334e390cabSriastradh 		return fb;
3344e390cabSriastradh 	}
3354e390cabSriastradh 
3364e390cabSriastradh 	return fb;
3374e390cabSriastradh }
3384e390cabSriastradh EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_internal_framebuffer_create);
3394e390cabSriastradh 
3404e390cabSriastradh /**
3414e390cabSriastradh  * drm_mode_addfb2 - add an FB to the graphics configuration
3424e390cabSriastradh  * @dev: drm device for the ioctl
3434e390cabSriastradh  * @data: data pointer for the ioctl
3444e390cabSriastradh  * @file_priv: drm file for the ioctl call
3454e390cabSriastradh  *
3464e390cabSriastradh  * Add a new FB to the specified CRTC, given a user request with format. This is
3474e390cabSriastradh  * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers
3484e390cabSriastradh  * and uses fourcc codes as pixel format specifiers.
3494e390cabSriastradh  *
3504e390cabSriastradh  * Called by the user via ioctl.
3514e390cabSriastradh  *
3524e390cabSriastradh  * Returns:
3534e390cabSriastradh  * Zero on success, negative errno on failure.
3544e390cabSriastradh  */
drm_mode_addfb2(struct drm_device * dev,void * data,struct drm_file * file_priv)3554e390cabSriastradh int drm_mode_addfb2(struct drm_device *dev,
3564e390cabSriastradh 		    void *data, struct drm_file *file_priv)
3574e390cabSriastradh {
3584e390cabSriastradh 	struct drm_mode_fb_cmd2 *r = data;
3594e390cabSriastradh 	struct drm_framebuffer *fb;
3604e390cabSriastradh 
3614e390cabSriastradh 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
3624e390cabSriastradh 		return -EOPNOTSUPP;
3634e390cabSriastradh 
3644e390cabSriastradh 	fb = drm_internal_framebuffer_create(dev, r, file_priv);
3654e390cabSriastradh 	if (IS_ERR(fb))
3664e390cabSriastradh 		return PTR_ERR(fb);
3674e390cabSriastradh 
3684e390cabSriastradh 	DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
3694e390cabSriastradh 	r->fb_id = fb->base.id;
3704e390cabSriastradh 
3714e390cabSriastradh 	/* Transfer ownership to the filp for reaping on close */
3724e390cabSriastradh 	mutex_lock(&file_priv->fbs_lock);
3734e390cabSriastradh 	list_add(&fb->filp_head, &file_priv->fbs);
3744e390cabSriastradh 	mutex_unlock(&file_priv->fbs_lock);
3754e390cabSriastradh 
3764e390cabSriastradh 	return 0;
3774e390cabSriastradh }
3784e390cabSriastradh 
drm_mode_addfb2_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)3794e390cabSriastradh int drm_mode_addfb2_ioctl(struct drm_device *dev,
3804e390cabSriastradh 			  void *data, struct drm_file *file_priv)
3814e390cabSriastradh {
3824e390cabSriastradh #ifdef __BIG_ENDIAN
3834e390cabSriastradh 	if (!dev->mode_config.quirk_addfb_prefer_host_byte_order) {
3844e390cabSriastradh 		/*
3854e390cabSriastradh 		 * Drivers must set the
3864e390cabSriastradh 		 * quirk_addfb_prefer_host_byte_order quirk to make
3874e390cabSriastradh 		 * the drm_mode_addfb() compat code work correctly on
3884e390cabSriastradh 		 * bigendian machines.
3894e390cabSriastradh 		 *
3904e390cabSriastradh 		 * If they don't they interpret pixel_format values
3914e390cabSriastradh 		 * incorrectly for bug compatibility, which in turn
3924e390cabSriastradh 		 * implies the ADDFB2 ioctl does not work correctly
3934e390cabSriastradh 		 * then.  So block it to make userspace fallback to
3944e390cabSriastradh 		 * ADDFB.
3954e390cabSriastradh 		 */
3964e390cabSriastradh 		DRM_DEBUG_KMS("addfb2 broken on bigendian");
3974e390cabSriastradh 		return -EOPNOTSUPP;
3984e390cabSriastradh 	}
3994e390cabSriastradh #endif
4004e390cabSriastradh 	return drm_mode_addfb2(dev, data, file_priv);
4014e390cabSriastradh }
4024e390cabSriastradh 
4034e390cabSriastradh struct drm_mode_rmfb_work {
4044e390cabSriastradh 	struct work_struct work;
4054e390cabSriastradh 	struct list_head fbs;
4064e390cabSriastradh };
4074e390cabSriastradh 
drm_mode_rmfb_work_fn(struct work_struct * w)4084e390cabSriastradh static void drm_mode_rmfb_work_fn(struct work_struct *w)
4094e390cabSriastradh {
4104e390cabSriastradh 	struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work);
4114e390cabSriastradh 
4124e390cabSriastradh 	while (!list_empty(&arg->fbs)) {
4134e390cabSriastradh 		struct drm_framebuffer *fb =
4144e390cabSriastradh 			list_first_entry(&arg->fbs, typeof(*fb), filp_head);
4154e390cabSriastradh 
4164e390cabSriastradh 		list_del_init(&fb->filp_head);
4174e390cabSriastradh 		drm_framebuffer_remove(fb);
4184e390cabSriastradh 	}
4194e390cabSriastradh }
4204e390cabSriastradh 
4214e390cabSriastradh /**
4224e390cabSriastradh  * drm_mode_rmfb - remove an FB from the configuration
4234e390cabSriastradh  * @dev: drm device
4244e390cabSriastradh  * @fb_id: id of framebuffer to remove
4254e390cabSriastradh  * @file_priv: drm file
4264e390cabSriastradh  *
4274e390cabSriastradh  * Remove the specified FB.
4284e390cabSriastradh  *
4294e390cabSriastradh  * Called by the user via ioctl, or by an in-kernel client.
4304e390cabSriastradh  *
4314e390cabSriastradh  * Returns:
4324e390cabSriastradh  * Zero on success, negative errno on failure.
4334e390cabSriastradh  */
drm_mode_rmfb(struct drm_device * dev,u32 fb_id,struct drm_file * file_priv)4344e390cabSriastradh int drm_mode_rmfb(struct drm_device *dev, u32 fb_id,
4354e390cabSriastradh 		  struct drm_file *file_priv)
4364e390cabSriastradh {
4374e390cabSriastradh 	struct drm_framebuffer *fb = NULL;
4384e390cabSriastradh 	struct drm_framebuffer *fbl = NULL;
4394e390cabSriastradh 	int found = 0;
4404e390cabSriastradh 
4414e390cabSriastradh 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
4424e390cabSriastradh 		return -EOPNOTSUPP;
4434e390cabSriastradh 
4444e390cabSriastradh 	fb = drm_framebuffer_lookup(dev, file_priv, fb_id);
4454e390cabSriastradh 	if (!fb)
4464e390cabSriastradh 		return -ENOENT;
4474e390cabSriastradh 
4484e390cabSriastradh 	mutex_lock(&file_priv->fbs_lock);
4494e390cabSriastradh 	list_for_each_entry(fbl, &file_priv->fbs, filp_head)
4504e390cabSriastradh 		if (fb == fbl)
4514e390cabSriastradh 			found = 1;
4524e390cabSriastradh 	if (!found) {
4534e390cabSriastradh 		mutex_unlock(&file_priv->fbs_lock);
4544e390cabSriastradh 		goto fail_unref;
4554e390cabSriastradh 	}
4564e390cabSriastradh 
4574e390cabSriastradh 	list_del_init(&fb->filp_head);
4584e390cabSriastradh 	mutex_unlock(&file_priv->fbs_lock);
4594e390cabSriastradh 
4604e390cabSriastradh 	/* drop the reference we picked up in framebuffer lookup */
4614e390cabSriastradh 	drm_framebuffer_put(fb);
4624e390cabSriastradh 
4634e390cabSriastradh 	/*
4644e390cabSriastradh 	 * we now own the reference that was stored in the fbs list
4654e390cabSriastradh 	 *
4664e390cabSriastradh 	 * drm_framebuffer_remove may fail with -EINTR on pending signals,
4674e390cabSriastradh 	 * so run this in a separate stack as there's no way to correctly
4684e390cabSriastradh 	 * handle this after the fb is already removed from the lookup table.
4694e390cabSriastradh 	 */
4704e390cabSriastradh 	if (drm_framebuffer_read_refcount(fb) > 1) {
4714e390cabSriastradh 		struct drm_mode_rmfb_work arg;
4724e390cabSriastradh 
4734e390cabSriastradh 		INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
4744e390cabSriastradh 		INIT_LIST_HEAD(&arg.fbs);
4754e390cabSriastradh 		list_add_tail(&fb->filp_head, &arg.fbs);
4764e390cabSriastradh 
4774e390cabSriastradh 		schedule_work(&arg.work);
4784e390cabSriastradh 		flush_work(&arg.work);
4794e390cabSriastradh 		destroy_work_on_stack(&arg.work);
4804e390cabSriastradh 	} else
4814e390cabSriastradh 		drm_framebuffer_put(fb);
4824e390cabSriastradh 
4834e390cabSriastradh 	return 0;
4844e390cabSriastradh 
4854e390cabSriastradh fail_unref:
4864e390cabSriastradh 	drm_framebuffer_put(fb);
4874e390cabSriastradh 	return -ENOENT;
4884e390cabSriastradh }
4894e390cabSriastradh 
drm_mode_rmfb_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)4904e390cabSriastradh int drm_mode_rmfb_ioctl(struct drm_device *dev,
4914e390cabSriastradh 			void *data, struct drm_file *file_priv)
4924e390cabSriastradh {
4934e390cabSriastradh 	uint32_t *fb_id = data;
4944e390cabSriastradh 
4954e390cabSriastradh 	return drm_mode_rmfb(dev, *fb_id, file_priv);
4964e390cabSriastradh }
4974e390cabSriastradh 
4984e390cabSriastradh /**
4994e390cabSriastradh  * drm_mode_getfb - get FB info
5004e390cabSriastradh  * @dev: drm device for the ioctl
5014e390cabSriastradh  * @data: data pointer for the ioctl
5024e390cabSriastradh  * @file_priv: drm file for the ioctl call
5034e390cabSriastradh  *
5044e390cabSriastradh  * Lookup the FB given its ID and return info about it.
5054e390cabSriastradh  *
5064e390cabSriastradh  * Called by the user via ioctl.
5074e390cabSriastradh  *
5084e390cabSriastradh  * Returns:
5094e390cabSriastradh  * Zero on success, negative errno on failure.
5104e390cabSriastradh  */
drm_mode_getfb(struct drm_device * dev,void * data,struct drm_file * file_priv)5114e390cabSriastradh int drm_mode_getfb(struct drm_device *dev,
5124e390cabSriastradh 		   void *data, struct drm_file *file_priv)
5134e390cabSriastradh {
5144e390cabSriastradh 	struct drm_mode_fb_cmd *r = data;
5154e390cabSriastradh 	struct drm_framebuffer *fb;
5164e390cabSriastradh 	int ret;
5174e390cabSriastradh 
5184e390cabSriastradh 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
5194e390cabSriastradh 		return -EOPNOTSUPP;
5204e390cabSriastradh 
5214e390cabSriastradh 	fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id);
5224e390cabSriastradh 	if (!fb)
5234e390cabSriastradh 		return -ENOENT;
5244e390cabSriastradh 
5254e390cabSriastradh 	/* Multi-planar framebuffers need getfb2. */
5264e390cabSriastradh 	if (fb->format->num_planes > 1) {
5274e390cabSriastradh 		ret = -EINVAL;
5284e390cabSriastradh 		goto out;
5294e390cabSriastradh 	}
5304e390cabSriastradh 
5314e390cabSriastradh 	if (!fb->funcs->create_handle) {
5324e390cabSriastradh 		ret = -ENODEV;
5334e390cabSriastradh 		goto out;
5344e390cabSriastradh 	}
5354e390cabSriastradh 
5364e390cabSriastradh 	r->height = fb->height;
5374e390cabSriastradh 	r->width = fb->width;
5384e390cabSriastradh 	r->depth = fb->format->depth;
5394e390cabSriastradh 	r->bpp = fb->format->cpp[0] * 8;
5404e390cabSriastradh 	r->pitch = fb->pitches[0];
5414e390cabSriastradh 
5424e390cabSriastradh 	/* GET_FB() is an unprivileged ioctl so we must not return a
5434e390cabSriastradh 	 * buffer-handle to non-master processes! For
5444e390cabSriastradh 	 * backwards-compatibility reasons, we cannot make GET_FB() privileged,
5454e390cabSriastradh 	 * so just return an invalid handle for non-masters.
5464e390cabSriastradh 	 */
5474e390cabSriastradh 	if (!drm_is_current_master(file_priv) && !capable(CAP_SYS_ADMIN)) {
5484e390cabSriastradh 		r->handle = 0;
5494e390cabSriastradh 		ret = 0;
5504e390cabSriastradh 		goto out;
5514e390cabSriastradh 	}
5524e390cabSriastradh 
5534e390cabSriastradh 	ret = fb->funcs->create_handle(fb, file_priv, &r->handle);
5544e390cabSriastradh 
5554e390cabSriastradh out:
5564e390cabSriastradh 	drm_framebuffer_put(fb);
5574e390cabSriastradh 
5584e390cabSriastradh 	return ret;
5594e390cabSriastradh }
5604e390cabSriastradh 
5614e390cabSriastradh /**
5624e390cabSriastradh  * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB
5634e390cabSriastradh  * @dev: drm device for the ioctl
5644e390cabSriastradh  * @data: data pointer for the ioctl
5654e390cabSriastradh  * @file_priv: drm file for the ioctl call
5664e390cabSriastradh  *
5674e390cabSriastradh  * Lookup the FB and flush out the damaged area supplied by userspace as a clip
5684e390cabSriastradh  * rectangle list. Generic userspace which does frontbuffer rendering must call
5694e390cabSriastradh  * this ioctl to flush out the changes on manual-update display outputs, e.g.
5704e390cabSriastradh  * usb display-link, mipi manual update panels or edp panel self refresh modes.
5714e390cabSriastradh  *
5724e390cabSriastradh  * Modesetting drivers which always update the frontbuffer do not need to
5734e390cabSriastradh  * implement the corresponding &drm_framebuffer_funcs.dirty callback.
5744e390cabSriastradh  *
5754e390cabSriastradh  * Called by the user via ioctl.
5764e390cabSriastradh  *
5774e390cabSriastradh  * Returns:
5784e390cabSriastradh  * Zero on success, negative errno on failure.
5794e390cabSriastradh  */
drm_mode_dirtyfb_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)5804e390cabSriastradh int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
5814e390cabSriastradh 			   void *data, struct drm_file *file_priv)
5824e390cabSriastradh {
5834e390cabSriastradh 	struct drm_clip_rect __user *clips_ptr;
5844e390cabSriastradh 	struct drm_clip_rect *clips = NULL;
5854e390cabSriastradh 	struct drm_mode_fb_dirty_cmd *r = data;
5864e390cabSriastradh 	struct drm_framebuffer *fb;
5874e390cabSriastradh 	unsigned flags;
5884e390cabSriastradh 	int num_clips;
5894e390cabSriastradh 	int ret;
5904e390cabSriastradh 
5914e390cabSriastradh 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
5924e390cabSriastradh 		return -EOPNOTSUPP;
5934e390cabSriastradh 
5944e390cabSriastradh 	fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id);
5954e390cabSriastradh 	if (!fb)
5964e390cabSriastradh 		return -ENOENT;
5974e390cabSriastradh 
5984e390cabSriastradh 	num_clips = r->num_clips;
5994e390cabSriastradh 	clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr;
6004e390cabSriastradh 
6014e390cabSriastradh 	if (!num_clips != !clips_ptr) {
6024e390cabSriastradh 		ret = -EINVAL;
6034e390cabSriastradh 		goto out_err1;
6044e390cabSriastradh 	}
6054e390cabSriastradh 
6064e390cabSriastradh 	flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags;
6074e390cabSriastradh 
6084e390cabSriastradh 	/* If userspace annotates copy, clips must come in pairs */
6094e390cabSriastradh 	if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) {
6104e390cabSriastradh 		ret = -EINVAL;
6114e390cabSriastradh 		goto out_err1;
6124e390cabSriastradh 	}
6134e390cabSriastradh 
6144e390cabSriastradh 	if (num_clips && clips_ptr) {
6154e390cabSriastradh 		if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) {
6164e390cabSriastradh 			ret = -EINVAL;
6174e390cabSriastradh 			goto out_err1;
6184e390cabSriastradh 		}
6194e390cabSriastradh 		clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL);
6204e390cabSriastradh 		if (!clips) {
6214e390cabSriastradh 			ret = -ENOMEM;
6224e390cabSriastradh 			goto out_err1;
6234e390cabSriastradh 		}
6244e390cabSriastradh 
6254e390cabSriastradh 		ret = copy_from_user(clips, clips_ptr,
6264e390cabSriastradh 				     num_clips * sizeof(*clips));
6274e390cabSriastradh 		if (ret) {
6284e390cabSriastradh 			ret = -EFAULT;
6294e390cabSriastradh 			goto out_err2;
6304e390cabSriastradh 		}
6314e390cabSriastradh 	}
6324e390cabSriastradh 
6334e390cabSriastradh 	if (fb->funcs->dirty) {
6344e390cabSriastradh 		ret = fb->funcs->dirty(fb, file_priv, flags, r->color,
6354e390cabSriastradh 				       clips, num_clips);
6364e390cabSriastradh 	} else {
6374e390cabSriastradh 		ret = -ENOSYS;
6384e390cabSriastradh 	}
6394e390cabSriastradh 
6404e390cabSriastradh out_err2:
6414e390cabSriastradh 	kfree(clips);
6424e390cabSriastradh out_err1:
6434e390cabSriastradh 	drm_framebuffer_put(fb);
6444e390cabSriastradh 
6454e390cabSriastradh 	return ret;
6464e390cabSriastradh }
6474e390cabSriastradh 
6484e390cabSriastradh /**
6494e390cabSriastradh  * drm_fb_release - remove and free the FBs on this file
6504e390cabSriastradh  * @priv: drm file for the ioctl
6514e390cabSriastradh  *
6524e390cabSriastradh  * Destroy all the FBs associated with @filp.
6534e390cabSriastradh  *
6544e390cabSriastradh  * Called by the user via ioctl.
6554e390cabSriastradh  *
6564e390cabSriastradh  * Returns:
6574e390cabSriastradh  * Zero on success, negative errno on failure.
6584e390cabSriastradh  */
drm_fb_release(struct drm_file * priv)6594e390cabSriastradh void drm_fb_release(struct drm_file *priv)
6604e390cabSriastradh {
6614e390cabSriastradh 	struct drm_framebuffer *fb, *tfb;
6624e390cabSriastradh 	struct drm_mode_rmfb_work arg;
6634e390cabSriastradh 
6644e390cabSriastradh 	INIT_LIST_HEAD(&arg.fbs);
6654e390cabSriastradh 
6664e390cabSriastradh 	/*
6674e390cabSriastradh 	 * When the file gets released that means no one else can access the fb
6684e390cabSriastradh 	 * list any more, so no need to grab fpriv->fbs_lock. And we need to
6694e390cabSriastradh 	 * avoid upsetting lockdep since the universal cursor code adds a
6704e390cabSriastradh 	 * framebuffer while holding mutex locks.
6714e390cabSriastradh 	 *
6724e390cabSriastradh 	 * Note that a real deadlock between fpriv->fbs_lock and the modeset
6734e390cabSriastradh 	 * locks is impossible here since no one else but this function can get
6744e390cabSriastradh 	 * at it any more.
6754e390cabSriastradh 	 */
6764e390cabSriastradh 	list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
6774e390cabSriastradh 		if (drm_framebuffer_read_refcount(fb) > 1) {
6784e390cabSriastradh 			list_move_tail(&fb->filp_head, &arg.fbs);
6794e390cabSriastradh 		} else {
6804e390cabSriastradh 			list_del_init(&fb->filp_head);
6814e390cabSriastradh 
6824e390cabSriastradh 			/* This drops the fpriv->fbs reference. */
6834e390cabSriastradh 			drm_framebuffer_put(fb);
6844e390cabSriastradh 		}
6854e390cabSriastradh 	}
6864e390cabSriastradh 
6874e390cabSriastradh 	if (!list_empty(&arg.fbs)) {
6884e390cabSriastradh 		INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
6894e390cabSriastradh 
6904e390cabSriastradh 		schedule_work(&arg.work);
6914e390cabSriastradh 		flush_work(&arg.work);
6924e390cabSriastradh 		destroy_work_on_stack(&arg.work);
6934e390cabSriastradh 	}
6944e390cabSriastradh }
6954e390cabSriastradh 
drm_framebuffer_free(struct kref * kref)6964e390cabSriastradh void drm_framebuffer_free(struct kref *kref)
6974e390cabSriastradh {
6984e390cabSriastradh 	struct drm_framebuffer *fb =
6994e390cabSriastradh 			container_of(kref, struct drm_framebuffer, base.refcount);
7004e390cabSriastradh 	struct drm_device *dev = fb->dev;
7014e390cabSriastradh 
7024e390cabSriastradh 	/*
7034e390cabSriastradh 	 * The lookup idr holds a weak reference, which has not necessarily been
7044e390cabSriastradh 	 * removed at this point. Check for that.
7054e390cabSriastradh 	 */
7064e390cabSriastradh 	drm_mode_object_unregister(dev, &fb->base);
7074e390cabSriastradh 
7084e390cabSriastradh 	fb->funcs->destroy(fb);
7094e390cabSriastradh }
7104e390cabSriastradh 
7114e390cabSriastradh /**
7124e390cabSriastradh  * drm_framebuffer_init - initialize a framebuffer
7134e390cabSriastradh  * @dev: DRM device
7144e390cabSriastradh  * @fb: framebuffer to be initialized
7154e390cabSriastradh  * @funcs: ... with these functions
7164e390cabSriastradh  *
7174e390cabSriastradh  * Allocates an ID for the framebuffer's parent mode object, sets its mode
7184e390cabSriastradh  * functions & device file and adds it to the master fd list.
7194e390cabSriastradh  *
7204e390cabSriastradh  * IMPORTANT:
7214e390cabSriastradh  * This functions publishes the fb and makes it available for concurrent access
7224e390cabSriastradh  * by other users. Which means by this point the fb _must_ be fully set up -
7234e390cabSriastradh  * since all the fb attributes are invariant over its lifetime, no further
7244e390cabSriastradh  * locking but only correct reference counting is required.
7254e390cabSriastradh  *
7264e390cabSriastradh  * Returns:
7274e390cabSriastradh  * Zero on success, error code on failure.
7284e390cabSriastradh  */
drm_framebuffer_init(struct drm_device * dev,struct drm_framebuffer * fb,const struct drm_framebuffer_funcs * funcs)7294e390cabSriastradh int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
7304e390cabSriastradh 			 const struct drm_framebuffer_funcs *funcs)
7314e390cabSriastradh {
7324e390cabSriastradh 	int ret;
7334e390cabSriastradh 
7344e390cabSriastradh 	if (WARN_ON_ONCE(fb->dev != dev || !fb->format))
7354e390cabSriastradh 		return -EINVAL;
7364e390cabSriastradh 
7374e390cabSriastradh 	INIT_LIST_HEAD(&fb->filp_head);
7384e390cabSriastradh 
7394e390cabSriastradh 	fb->funcs = funcs;
740c889a9ebSriastradh #ifdef __NetBSD__
741c889a9ebSriastradh 	strlcpy(fb->comm, curproc->p_comm, sizeof fb->comm);
742c889a9ebSriastradh #else
7434e390cabSriastradh 	strcpy(fb->comm, current->comm);
744c889a9ebSriastradh #endif
7454e390cabSriastradh 
7464e390cabSriastradh 	ret = __drm_mode_object_add(dev, &fb->base, DRM_MODE_OBJECT_FB,
7474e390cabSriastradh 				    false, drm_framebuffer_free);
7484e390cabSriastradh 	if (ret)
7494e390cabSriastradh 		goto out;
7504e390cabSriastradh 
7514e390cabSriastradh 	mutex_lock(&dev->mode_config.fb_lock);
7524e390cabSriastradh 	dev->mode_config.num_fb++;
7534e390cabSriastradh 	list_add(&fb->head, &dev->mode_config.fb_list);
7544e390cabSriastradh 	mutex_unlock(&dev->mode_config.fb_lock);
7554e390cabSriastradh 
7564e390cabSriastradh 	drm_mode_object_register(dev, &fb->base);
7574e390cabSriastradh out:
7584e390cabSriastradh 	return ret;
7594e390cabSriastradh }
7604e390cabSriastradh EXPORT_SYMBOL(drm_framebuffer_init);
7614e390cabSriastradh 
7624e390cabSriastradh /**
7634e390cabSriastradh  * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference
7644e390cabSriastradh  * @dev: drm device
7654e390cabSriastradh  * @file_priv: drm file to check for lease against.
7664e390cabSriastradh  * @id: id of the fb object
7674e390cabSriastradh  *
7684e390cabSriastradh  * If successful, this grabs an additional reference to the framebuffer -
7694e390cabSriastradh  * callers need to make sure to eventually unreference the returned framebuffer
7704e390cabSriastradh  * again, using drm_framebuffer_put().
7714e390cabSriastradh  */
drm_framebuffer_lookup(struct drm_device * dev,struct drm_file * file_priv,uint32_t id)7724e390cabSriastradh struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
7734e390cabSriastradh 					       struct drm_file *file_priv,
7744e390cabSriastradh 					       uint32_t id)
7754e390cabSriastradh {
7764e390cabSriastradh 	struct drm_mode_object *obj;
7774e390cabSriastradh 	struct drm_framebuffer *fb = NULL;
7784e390cabSriastradh 
7794e390cabSriastradh 	obj = __drm_mode_object_find(dev, file_priv, id, DRM_MODE_OBJECT_FB);
7804e390cabSriastradh 	if (obj)
7814e390cabSriastradh 		fb = obj_to_fb(obj);
7824e390cabSriastradh 	return fb;
7834e390cabSriastradh }
7844e390cabSriastradh EXPORT_SYMBOL(drm_framebuffer_lookup);
7854e390cabSriastradh 
7864e390cabSriastradh /**
7874e390cabSriastradh  * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
7884e390cabSriastradh  * @fb: fb to unregister
7894e390cabSriastradh  *
7904e390cabSriastradh  * Drivers need to call this when cleaning up driver-private framebuffers, e.g.
7914e390cabSriastradh  * those used for fbdev. Note that the caller must hold a reference of its own,
7924e390cabSriastradh  * i.e. the object may not be destroyed through this call (since it'll lead to a
7934e390cabSriastradh  * locking inversion).
7944e390cabSriastradh  *
7954e390cabSriastradh  * NOTE: This function is deprecated. For driver-private framebuffers it is not
7964e390cabSriastradh  * recommended to embed a framebuffer struct info fbdev struct, instead, a
7974e390cabSriastradh  * framebuffer pointer is preferred and drm_framebuffer_put() should be called
7984e390cabSriastradh  * when the framebuffer is to be cleaned up.
7994e390cabSriastradh  */
drm_framebuffer_unregister_private(struct drm_framebuffer * fb)8004e390cabSriastradh void drm_framebuffer_unregister_private(struct drm_framebuffer *fb)
8014e390cabSriastradh {
8024e390cabSriastradh 	struct drm_device *dev;
8034e390cabSriastradh 
8044e390cabSriastradh 	if (!fb)
8054e390cabSriastradh 		return;
8064e390cabSriastradh 
8074e390cabSriastradh 	dev = fb->dev;
8084e390cabSriastradh 
8094e390cabSriastradh 	/* Mark fb as reaped and drop idr ref. */
8104e390cabSriastradh 	drm_mode_object_unregister(dev, &fb->base);
8114e390cabSriastradh }
8124e390cabSriastradh EXPORT_SYMBOL(drm_framebuffer_unregister_private);
8134e390cabSriastradh 
8144e390cabSriastradh /**
8154e390cabSriastradh  * drm_framebuffer_cleanup - remove a framebuffer object
8164e390cabSriastradh  * @fb: framebuffer to remove
8174e390cabSriastradh  *
8184e390cabSriastradh  * Cleanup framebuffer. This function is intended to be used from the drivers
8194e390cabSriastradh  * &drm_framebuffer_funcs.destroy callback. It can also be used to clean up
8204e390cabSriastradh  * driver private framebuffers embedded into a larger structure.
8214e390cabSriastradh  *
8224e390cabSriastradh  * Note that this function does not remove the fb from active usage - if it is
8234e390cabSriastradh  * still used anywhere, hilarity can ensue since userspace could call getfb on
8244e390cabSriastradh  * the id and get back -EINVAL. Obviously no concern at driver unload time.
8254e390cabSriastradh  *
8264e390cabSriastradh  * Also, the framebuffer will not be removed from the lookup idr - for
8274e390cabSriastradh  * user-created framebuffers this will happen in in the rmfb ioctl. For
8284e390cabSriastradh  * driver-private objects (e.g. for fbdev) drivers need to explicitly call
8294e390cabSriastradh  * drm_framebuffer_unregister_private.
8304e390cabSriastradh  */
drm_framebuffer_cleanup(struct drm_framebuffer * fb)8314e390cabSriastradh void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
8324e390cabSriastradh {
8334e390cabSriastradh 	struct drm_device *dev = fb->dev;
8344e390cabSriastradh 
8354e390cabSriastradh 	mutex_lock(&dev->mode_config.fb_lock);
8364e390cabSriastradh 	list_del(&fb->head);
8374e390cabSriastradh 	dev->mode_config.num_fb--;
8384e390cabSriastradh 	mutex_unlock(&dev->mode_config.fb_lock);
8394e390cabSriastradh }
8404e390cabSriastradh EXPORT_SYMBOL(drm_framebuffer_cleanup);
8414e390cabSriastradh 
atomic_remove_fb(struct drm_framebuffer * fb)8424e390cabSriastradh static int atomic_remove_fb(struct drm_framebuffer *fb)
8434e390cabSriastradh {
8444e390cabSriastradh 	struct drm_modeset_acquire_ctx ctx;
8454e390cabSriastradh 	struct drm_device *dev = fb->dev;
8464e390cabSriastradh 	struct drm_atomic_state *state;
8474e390cabSriastradh 	struct drm_plane *plane;
8484e390cabSriastradh 	struct drm_connector *conn __maybe_unused;
8494e390cabSriastradh 	struct drm_connector_state *conn_state;
8504e390cabSriastradh 	int i, ret;
8514e390cabSriastradh 	unsigned plane_mask;
8524e390cabSriastradh 	bool disable_crtcs = false;
8534e390cabSriastradh 
8544e390cabSriastradh retry_disable:
8554e390cabSriastradh 	drm_modeset_acquire_init(&ctx, 0);
8564e390cabSriastradh 
8574e390cabSriastradh 	state = drm_atomic_state_alloc(dev);
8584e390cabSriastradh 	if (!state) {
8594e390cabSriastradh 		ret = -ENOMEM;
8604e390cabSriastradh 		goto out;
8614e390cabSriastradh 	}
8624e390cabSriastradh 	state->acquire_ctx = &ctx;
8634e390cabSriastradh 
8644e390cabSriastradh retry:
8654e390cabSriastradh 	plane_mask = 0;
8664e390cabSriastradh 	ret = drm_modeset_lock_all_ctx(dev, &ctx);
8674e390cabSriastradh 	if (ret)
8684e390cabSriastradh 		goto unlock;
8694e390cabSriastradh 
8704e390cabSriastradh 	drm_for_each_plane(plane, dev) {
8714e390cabSriastradh 		struct drm_plane_state *plane_state;
8724e390cabSriastradh 
8734e390cabSriastradh 		if (plane->state->fb != fb)
8744e390cabSriastradh 			continue;
8754e390cabSriastradh 
8764e390cabSriastradh 		plane_state = drm_atomic_get_plane_state(state, plane);
8774e390cabSriastradh 		if (IS_ERR(plane_state)) {
8784e390cabSriastradh 			ret = PTR_ERR(plane_state);
8794e390cabSriastradh 			goto unlock;
8804e390cabSriastradh 		}
8814e390cabSriastradh 
8824e390cabSriastradh 		if (disable_crtcs && plane_state->crtc->primary == plane) {
8834e390cabSriastradh 			struct drm_crtc_state *crtc_state;
8844e390cabSriastradh 
8854e390cabSriastradh 			crtc_state = drm_atomic_get_existing_crtc_state(state, plane_state->crtc);
8864e390cabSriastradh 
8874e390cabSriastradh 			ret = drm_atomic_add_affected_connectors(state, plane_state->crtc);
8884e390cabSriastradh 			if (ret)
8894e390cabSriastradh 				goto unlock;
8904e390cabSriastradh 
8914e390cabSriastradh 			crtc_state->active = false;
8924e390cabSriastradh 			ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
8934e390cabSriastradh 			if (ret)
8944e390cabSriastradh 				goto unlock;
8954e390cabSriastradh 		}
8964e390cabSriastradh 
8974e390cabSriastradh 		drm_atomic_set_fb_for_plane(plane_state, NULL);
8984e390cabSriastradh 		ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
8994e390cabSriastradh 		if (ret)
9004e390cabSriastradh 			goto unlock;
9014e390cabSriastradh 
9024e390cabSriastradh 		plane_mask |= drm_plane_mask(plane);
9034e390cabSriastradh 	}
9044e390cabSriastradh 
9054e390cabSriastradh 	/* This list is only filled when disable_crtcs is set. */
9064e390cabSriastradh 	for_each_new_connector_in_state(state, conn, conn_state, i) {
9074e390cabSriastradh 		ret = drm_atomic_set_crtc_for_connector(conn_state, NULL);
9084e390cabSriastradh 
9094e390cabSriastradh 		if (ret)
9104e390cabSriastradh 			goto unlock;
9114e390cabSriastradh 	}
9124e390cabSriastradh 
9134e390cabSriastradh 	if (plane_mask)
9144e390cabSriastradh 		ret = drm_atomic_commit(state);
9154e390cabSriastradh 
9164e390cabSriastradh unlock:
9174e390cabSriastradh 	if (ret == -EDEADLK) {
9184e390cabSriastradh 		drm_atomic_state_clear(state);
9194e390cabSriastradh 		drm_modeset_backoff(&ctx);
9204e390cabSriastradh 		goto retry;
9214e390cabSriastradh 	}
9224e390cabSriastradh 
9234e390cabSriastradh 	drm_atomic_state_put(state);
9244e390cabSriastradh 
9254e390cabSriastradh out:
9264e390cabSriastradh 	drm_modeset_drop_locks(&ctx);
9274e390cabSriastradh 	drm_modeset_acquire_fini(&ctx);
9284e390cabSriastradh 
9294e390cabSriastradh 	if (ret == -EINVAL && !disable_crtcs) {
9304e390cabSriastradh 		disable_crtcs = true;
9314e390cabSriastradh 		goto retry_disable;
9324e390cabSriastradh 	}
9334e390cabSriastradh 
9344e390cabSriastradh 	return ret;
9354e390cabSriastradh }
9364e390cabSriastradh 
legacy_remove_fb(struct drm_framebuffer * fb)9374e390cabSriastradh static void legacy_remove_fb(struct drm_framebuffer *fb)
9384e390cabSriastradh {
9394e390cabSriastradh 	struct drm_device *dev = fb->dev;
9404e390cabSriastradh 	struct drm_crtc *crtc;
9414e390cabSriastradh 	struct drm_plane *plane;
9424e390cabSriastradh 
9434e390cabSriastradh 	drm_modeset_lock_all(dev);
9444e390cabSriastradh 	/* remove from any CRTC */
9454e390cabSriastradh 	drm_for_each_crtc(crtc, dev) {
9464e390cabSriastradh 		if (crtc->primary->fb == fb) {
9474e390cabSriastradh 			/* should turn off the crtc */
9484e390cabSriastradh 			if (drm_crtc_force_disable(crtc))
9494e390cabSriastradh 				DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
9504e390cabSriastradh 		}
9514e390cabSriastradh 	}
9524e390cabSriastradh 
9534e390cabSriastradh 	drm_for_each_plane(plane, dev) {
9544e390cabSriastradh 		if (plane->fb == fb)
9554e390cabSriastradh 			drm_plane_force_disable(plane);
9564e390cabSriastradh 	}
9574e390cabSriastradh 	drm_modeset_unlock_all(dev);
9584e390cabSriastradh }
9594e390cabSriastradh 
9604e390cabSriastradh /**
9614e390cabSriastradh  * drm_framebuffer_remove - remove and unreference a framebuffer object
9624e390cabSriastradh  * @fb: framebuffer to remove
9634e390cabSriastradh  *
9644e390cabSriastradh  * Scans all the CRTCs and planes in @dev's mode_config.  If they're
9654e390cabSriastradh  * using @fb, removes it, setting it to NULL. Then drops the reference to the
9664e390cabSriastradh  * passed-in framebuffer. Might take the modeset locks.
9674e390cabSriastradh  *
9684e390cabSriastradh  * Note that this function optimizes the cleanup away if the caller holds the
9694e390cabSriastradh  * last reference to the framebuffer. It is also guaranteed to not take the
9704e390cabSriastradh  * modeset locks in this case.
9714e390cabSriastradh  */
drm_framebuffer_remove(struct drm_framebuffer * fb)9724e390cabSriastradh void drm_framebuffer_remove(struct drm_framebuffer *fb)
9734e390cabSriastradh {
9744e390cabSriastradh 	struct drm_device *dev;
9754e390cabSriastradh 
9764e390cabSriastradh 	if (!fb)
9774e390cabSriastradh 		return;
9784e390cabSriastradh 
9794e390cabSriastradh 	dev = fb->dev;
9804e390cabSriastradh 
9814e390cabSriastradh 	WARN_ON(!list_empty(&fb->filp_head));
9824e390cabSriastradh 
9834e390cabSriastradh 	/*
9844e390cabSriastradh 	 * drm ABI mandates that we remove any deleted framebuffers from active
9854e390cabSriastradh 	 * useage. But since most sane clients only remove framebuffers they no
9864e390cabSriastradh 	 * longer need, try to optimize this away.
9874e390cabSriastradh 	 *
9884e390cabSriastradh 	 * Since we're holding a reference ourselves, observing a refcount of 1
9894e390cabSriastradh 	 * means that we're the last holder and can skip it. Also, the refcount
9904e390cabSriastradh 	 * can never increase from 1 again, so we don't need any barriers or
9914e390cabSriastradh 	 * locks.
9924e390cabSriastradh 	 *
9934e390cabSriastradh 	 * Note that userspace could try to race with use and instate a new
9944e390cabSriastradh 	 * usage _after_ we've cleared all current ones. End result will be an
9954e390cabSriastradh 	 * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot
9964e390cabSriastradh 	 * in this manner.
9974e390cabSriastradh 	 */
9984e390cabSriastradh 	if (drm_framebuffer_read_refcount(fb) > 1) {
9994e390cabSriastradh 		if (drm_drv_uses_atomic_modeset(dev)) {
10004e390cabSriastradh 			int ret = atomic_remove_fb(fb);
10014e390cabSriastradh 			WARN(ret, "atomic remove_fb failed with %i\n", ret);
10024e390cabSriastradh 		} else
10034e390cabSriastradh 			legacy_remove_fb(fb);
10044e390cabSriastradh 	}
10054e390cabSriastradh 
10064e390cabSriastradh 	drm_framebuffer_put(fb);
10074e390cabSriastradh }
10084e390cabSriastradh EXPORT_SYMBOL(drm_framebuffer_remove);
10094e390cabSriastradh 
10104e390cabSriastradh /**
10114e390cabSriastradh  * drm_framebuffer_plane_width - width of the plane given the first plane
10124e390cabSriastradh  * @width: width of the first plane
10134e390cabSriastradh  * @fb: the framebuffer
10144e390cabSriastradh  * @plane: plane index
10154e390cabSriastradh  *
10164e390cabSriastradh  * Returns:
10174e390cabSriastradh  * The width of @plane, given that the width of the first plane is @width.
10184e390cabSriastradh  */
drm_framebuffer_plane_width(int width,const struct drm_framebuffer * fb,int plane)10194e390cabSriastradh int drm_framebuffer_plane_width(int width,
10204e390cabSriastradh 				const struct drm_framebuffer *fb, int plane)
10214e390cabSriastradh {
10224e390cabSriastradh 	if (plane >= fb->format->num_planes)
10234e390cabSriastradh 		return 0;
10244e390cabSriastradh 
10254e390cabSriastradh 	return fb_plane_width(width, fb->format, plane);
10264e390cabSriastradh }
10274e390cabSriastradh EXPORT_SYMBOL(drm_framebuffer_plane_width);
10284e390cabSriastradh 
10294e390cabSriastradh /**
10304e390cabSriastradh  * drm_framebuffer_plane_height - height of the plane given the first plane
10314e390cabSriastradh  * @height: height of the first plane
10324e390cabSriastradh  * @fb: the framebuffer
10334e390cabSriastradh  * @plane: plane index
10344e390cabSriastradh  *
10354e390cabSriastradh  * Returns:
10364e390cabSriastradh  * The height of @plane, given that the height of the first plane is @height.
10374e390cabSriastradh  */
drm_framebuffer_plane_height(int height,const struct drm_framebuffer * fb,int plane)10384e390cabSriastradh int drm_framebuffer_plane_height(int height,
10394e390cabSriastradh 				 const struct drm_framebuffer *fb, int plane)
10404e390cabSriastradh {
10414e390cabSriastradh 	if (plane >= fb->format->num_planes)
10424e390cabSriastradh 		return 0;
10434e390cabSriastradh 
10444e390cabSriastradh 	return fb_plane_height(height, fb->format, plane);
10454e390cabSriastradh }
10464e390cabSriastradh EXPORT_SYMBOL(drm_framebuffer_plane_height);
10474e390cabSriastradh 
drm_framebuffer_print_info(struct drm_printer * p,unsigned int indent,const struct drm_framebuffer * fb)10484e390cabSriastradh void drm_framebuffer_print_info(struct drm_printer *p, unsigned int indent,
10494e390cabSriastradh 				const struct drm_framebuffer *fb)
10504e390cabSriastradh {
10514e390cabSriastradh 	struct drm_format_name_buf format_name;
10524e390cabSriastradh 	unsigned int i;
10534e390cabSriastradh 
10544e390cabSriastradh 	drm_printf_indent(p, indent, "allocated by = %s\n", fb->comm);
10554e390cabSriastradh 	drm_printf_indent(p, indent, "refcount=%u\n",
10564e390cabSriastradh 			  drm_framebuffer_read_refcount(fb));
10574e390cabSriastradh 	drm_printf_indent(p, indent, "format=%s\n",
10584e390cabSriastradh 			  drm_get_format_name(fb->format->format, &format_name));
10593af3cc51Sriastradh 	drm_printf_indent(p, indent, "modifier=0x%"PRIx64"\n", fb->modifier);
10604e390cabSriastradh 	drm_printf_indent(p, indent, "size=%ux%u\n", fb->width, fb->height);
10614e390cabSriastradh 	drm_printf_indent(p, indent, "layers:\n");
10624e390cabSriastradh 
10634e390cabSriastradh 	for (i = 0; i < fb->format->num_planes; i++) {
10644e390cabSriastradh 		drm_printf_indent(p, indent + 1, "size[%u]=%dx%d\n", i,
10654e390cabSriastradh 				  drm_framebuffer_plane_width(fb->width, fb, i),
10664e390cabSriastradh 				  drm_framebuffer_plane_height(fb->height, fb, i));
10674e390cabSriastradh 		drm_printf_indent(p, indent + 1, "pitch[%u]=%u\n", i, fb->pitches[i]);
10684e390cabSriastradh 		drm_printf_indent(p, indent + 1, "offset[%u]=%u\n", i, fb->offsets[i]);
10694e390cabSriastradh 		drm_printf_indent(p, indent + 1, "obj[%u]:%s\n", i,
10704e390cabSriastradh 				  fb->obj[i] ? "" : "(null)");
10714e390cabSriastradh 		if (fb->obj[i])
10724e390cabSriastradh 			drm_gem_print_info(p, indent + 2, fb->obj[i]);
10734e390cabSriastradh 	}
10744e390cabSriastradh }
10754e390cabSriastradh 
10764e390cabSriastradh #ifdef CONFIG_DEBUG_FS
drm_framebuffer_info(struct seq_file * m,void * data)10774e390cabSriastradh static int drm_framebuffer_info(struct seq_file *m, void *data)
10784e390cabSriastradh {
10794e390cabSriastradh 	struct drm_info_node *node = m->private;
10804e390cabSriastradh 	struct drm_device *dev = node->minor->dev;
10814e390cabSriastradh 	struct drm_printer p = drm_seq_file_printer(m);
10824e390cabSriastradh 	struct drm_framebuffer *fb;
10834e390cabSriastradh 
10844e390cabSriastradh 	mutex_lock(&dev->mode_config.fb_lock);
10854e390cabSriastradh 	drm_for_each_fb(fb, dev) {
10864e390cabSriastradh 		drm_printf(&p, "framebuffer[%u]:\n", fb->base.id);
10874e390cabSriastradh 		drm_framebuffer_print_info(&p, 1, fb);
10884e390cabSriastradh 	}
10894e390cabSriastradh 	mutex_unlock(&dev->mode_config.fb_lock);
10904e390cabSriastradh 
10914e390cabSriastradh 	return 0;
10924e390cabSriastradh }
10934e390cabSriastradh 
10944e390cabSriastradh static const struct drm_info_list drm_framebuffer_debugfs_list[] = {
10954e390cabSriastradh 	{ "framebuffer", drm_framebuffer_info, 0 },
10964e390cabSriastradh };
10974e390cabSriastradh 
drm_framebuffer_debugfs_init(struct drm_minor * minor)10984e390cabSriastradh int drm_framebuffer_debugfs_init(struct drm_minor *minor)
10994e390cabSriastradh {
11004e390cabSriastradh 	return drm_debugfs_create_files(drm_framebuffer_debugfs_list,
11014e390cabSriastradh 				ARRAY_SIZE(drm_framebuffer_debugfs_list),
11024e390cabSriastradh 				minor->debugfs_root, minor);
11034e390cabSriastradh }
11044e390cabSriastradh #endif
1105