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