162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2016 Intel Corporation 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission to use, copy, modify, distribute, and sell this software and its 562306a36Sopenharmony_ci * documentation for any purpose is hereby granted without fee, provided that 662306a36Sopenharmony_ci * the above copyright notice appear in all copies and that both that copyright 762306a36Sopenharmony_ci * notice and this permission notice appear in supporting documentation, and 862306a36Sopenharmony_ci * that the name of the copyright holders not be used in advertising or 962306a36Sopenharmony_ci * publicity pertaining to distribution of the software without specific, 1062306a36Sopenharmony_ci * written prior permission. The copyright holders make no representations 1162306a36Sopenharmony_ci * about the suitability of this software for any purpose. It is provided "as 1262306a36Sopenharmony_ci * is" without express or implied warranty. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 1562306a36Sopenharmony_ci * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 1662306a36Sopenharmony_ci * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 1762306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 1862306a36Sopenharmony_ci * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 1962306a36Sopenharmony_ci * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 2062306a36Sopenharmony_ci * OF THIS SOFTWARE. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/export.h> 2462306a36Sopenharmony_ci#include <linux/uaccess.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <drm/drm_atomic.h> 2762306a36Sopenharmony_ci#include <drm/drm_atomic_uapi.h> 2862306a36Sopenharmony_ci#include <drm/drm_auth.h> 2962306a36Sopenharmony_ci#include <drm/drm_debugfs.h> 3062306a36Sopenharmony_ci#include <drm/drm_drv.h> 3162306a36Sopenharmony_ci#include <drm/drm_file.h> 3262306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 3362306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 3462306a36Sopenharmony_ci#include <drm/drm_gem.h> 3562306a36Sopenharmony_ci#include <drm/drm_print.h> 3662306a36Sopenharmony_ci#include <drm/drm_util.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include "drm_crtc_internal.h" 3962306a36Sopenharmony_ci#include "drm_internal.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/** 4262306a36Sopenharmony_ci * DOC: overview 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * Frame buffers are abstract memory objects that provide a source of pixels to 4562306a36Sopenharmony_ci * scanout to a CRTC. Applications explicitly request the creation of frame 4662306a36Sopenharmony_ci * buffers through the DRM_IOCTL_MODE_ADDFB(2) ioctls and receive an opaque 4762306a36Sopenharmony_ci * handle that can be passed to the KMS CRTC control, plane configuration and 4862306a36Sopenharmony_ci * page flip functions. 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * Frame buffers rely on the underlying memory manager for allocating backing 5162306a36Sopenharmony_ci * storage. When creating a frame buffer applications pass a memory handle 5262306a36Sopenharmony_ci * (or a list of memory handles for multi-planar formats) through the 5362306a36Sopenharmony_ci * &struct drm_mode_fb_cmd2 argument. For drivers using GEM as their userspace 5462306a36Sopenharmony_ci * buffer management interface this would be a GEM handle. Drivers are however 5562306a36Sopenharmony_ci * free to use their own backing storage object handles, e.g. vmwgfx directly 5662306a36Sopenharmony_ci * exposes special TTM handles to userspace and so expects TTM handles in the 5762306a36Sopenharmony_ci * create ioctl and not GEM handles. 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * Framebuffers are tracked with &struct drm_framebuffer. They are published 6062306a36Sopenharmony_ci * using drm_framebuffer_init() - after calling that function userspace can use 6162306a36Sopenharmony_ci * and access the framebuffer object. The helper function 6262306a36Sopenharmony_ci * drm_helper_mode_fill_fb_struct() can be used to pre-fill the required 6362306a36Sopenharmony_ci * metadata fields. 6462306a36Sopenharmony_ci * 6562306a36Sopenharmony_ci * The lifetime of a drm framebuffer is controlled with a reference count, 6662306a36Sopenharmony_ci * drivers can grab additional references with drm_framebuffer_get() and drop 6762306a36Sopenharmony_ci * them again with drm_framebuffer_put(). For driver-private framebuffers for 6862306a36Sopenharmony_ci * which the last reference is never dropped (e.g. for the fbdev framebuffer 6962306a36Sopenharmony_ci * when the struct &struct drm_framebuffer is embedded into the fbdev helper 7062306a36Sopenharmony_ci * struct) drivers can manually clean up a framebuffer at module unload time 7162306a36Sopenharmony_ci * with drm_framebuffer_unregister_private(). But doing this is not 7262306a36Sopenharmony_ci * recommended, and it's better to have a normal free-standing &struct 7362306a36Sopenharmony_ci * drm_framebuffer. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ciint drm_framebuffer_check_src_coords(uint32_t src_x, uint32_t src_y, 7762306a36Sopenharmony_ci uint32_t src_w, uint32_t src_h, 7862306a36Sopenharmony_ci const struct drm_framebuffer *fb) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci unsigned int fb_width, fb_height; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci fb_width = fb->width << 16; 8362306a36Sopenharmony_ci fb_height = fb->height << 16; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* Make sure source coordinates are inside the fb. */ 8662306a36Sopenharmony_ci if (src_w > fb_width || 8762306a36Sopenharmony_ci src_x > fb_width - src_w || 8862306a36Sopenharmony_ci src_h > fb_height || 8962306a36Sopenharmony_ci src_y > fb_height - src_h) { 9062306a36Sopenharmony_ci drm_dbg_kms(fb->dev, "Invalid source coordinates " 9162306a36Sopenharmony_ci "%u.%06ux%u.%06u+%u.%06u+%u.%06u (fb %ux%u)\n", 9262306a36Sopenharmony_ci src_w >> 16, ((src_w & 0xffff) * 15625) >> 10, 9362306a36Sopenharmony_ci src_h >> 16, ((src_h & 0xffff) * 15625) >> 10, 9462306a36Sopenharmony_ci src_x >> 16, ((src_x & 0xffff) * 15625) >> 10, 9562306a36Sopenharmony_ci src_y >> 16, ((src_y & 0xffff) * 15625) >> 10, 9662306a36Sopenharmony_ci fb->width, fb->height); 9762306a36Sopenharmony_ci return -ENOSPC; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/** 10462306a36Sopenharmony_ci * drm_mode_addfb - add an FB to the graphics configuration 10562306a36Sopenharmony_ci * @dev: drm device for the ioctl 10662306a36Sopenharmony_ci * @or: pointer to request structure 10762306a36Sopenharmony_ci * @file_priv: drm file 10862306a36Sopenharmony_ci * 10962306a36Sopenharmony_ci * Add a new FB to the specified CRTC, given a user request. This is the 11062306a36Sopenharmony_ci * original addfb ioctl which only supported RGB formats. 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * Called by the user via ioctl, or by an in-kernel client. 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * Returns: 11562306a36Sopenharmony_ci * Zero on success, negative errno on failure. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ciint drm_mode_addfb(struct drm_device *dev, struct drm_mode_fb_cmd *or, 11862306a36Sopenharmony_ci struct drm_file *file_priv) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct drm_mode_fb_cmd2 r = {}; 12162306a36Sopenharmony_ci int ret; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_MODESET)) 12462306a36Sopenharmony_ci return -EOPNOTSUPP; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci r.pixel_format = drm_driver_legacy_fb_format(dev, or->bpp, or->depth); 12762306a36Sopenharmony_ci if (r.pixel_format == DRM_FORMAT_INVALID) { 12862306a36Sopenharmony_ci drm_dbg_kms(dev, "bad {bpp:%d, depth:%d}\n", or->bpp, or->depth); 12962306a36Sopenharmony_ci return -EINVAL; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* convert to new format and call new ioctl */ 13362306a36Sopenharmony_ci r.fb_id = or->fb_id; 13462306a36Sopenharmony_ci r.width = or->width; 13562306a36Sopenharmony_ci r.height = or->height; 13662306a36Sopenharmony_ci r.pitches[0] = or->pitch; 13762306a36Sopenharmony_ci r.handles[0] = or->handle; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci ret = drm_mode_addfb2(dev, &r, file_priv); 14062306a36Sopenharmony_ci if (ret) 14162306a36Sopenharmony_ci return ret; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci or->fb_id = r.fb_id; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return 0; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ciint drm_mode_addfb_ioctl(struct drm_device *dev, 14962306a36Sopenharmony_ci void *data, struct drm_file *file_priv) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci return drm_mode_addfb(dev, data, file_priv); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int fb_plane_width(int width, 15562306a36Sopenharmony_ci const struct drm_format_info *format, int plane) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci if (plane == 0) 15862306a36Sopenharmony_ci return width; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return DIV_ROUND_UP(width, format->hsub); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int fb_plane_height(int height, 16462306a36Sopenharmony_ci const struct drm_format_info *format, int plane) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci if (plane == 0) 16762306a36Sopenharmony_ci return height; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return DIV_ROUND_UP(height, format->vsub); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int framebuffer_check(struct drm_device *dev, 17362306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *r) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci const struct drm_format_info *info; 17662306a36Sopenharmony_ci int i; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* check if the format is supported at all */ 17962306a36Sopenharmony_ci if (!__drm_format_info(r->pixel_format)) { 18062306a36Sopenharmony_ci drm_dbg_kms(dev, "bad framebuffer format %p4cc\n", 18162306a36Sopenharmony_ci &r->pixel_format); 18262306a36Sopenharmony_ci return -EINVAL; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (r->width == 0) { 18662306a36Sopenharmony_ci drm_dbg_kms(dev, "bad framebuffer width %u\n", r->width); 18762306a36Sopenharmony_ci return -EINVAL; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (r->height == 0) { 19162306a36Sopenharmony_ci drm_dbg_kms(dev, "bad framebuffer height %u\n", r->height); 19262306a36Sopenharmony_ci return -EINVAL; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* now let the driver pick its own format info */ 19662306a36Sopenharmony_ci info = drm_get_format_info(dev, r); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci for (i = 0; i < info->num_planes; i++) { 19962306a36Sopenharmony_ci unsigned int width = fb_plane_width(r->width, info, i); 20062306a36Sopenharmony_ci unsigned int height = fb_plane_height(r->height, info, i); 20162306a36Sopenharmony_ci unsigned int block_size = info->char_per_block[i]; 20262306a36Sopenharmony_ci u64 min_pitch = drm_format_info_min_pitch(info, i, width); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (!block_size && (r->modifier[i] == DRM_FORMAT_MOD_LINEAR)) { 20562306a36Sopenharmony_ci drm_dbg_kms(dev, "Format requires non-linear modifier for plane %d\n", i); 20662306a36Sopenharmony_ci return -EINVAL; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (!r->handles[i]) { 21062306a36Sopenharmony_ci drm_dbg_kms(dev, "no buffer object handle for plane %d\n", i); 21162306a36Sopenharmony_ci return -EINVAL; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (min_pitch > UINT_MAX) 21562306a36Sopenharmony_ci return -ERANGE; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX) 21862306a36Sopenharmony_ci return -ERANGE; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (block_size && r->pitches[i] < min_pitch) { 22162306a36Sopenharmony_ci drm_dbg_kms(dev, "bad pitch %u for plane %d\n", r->pitches[i], i); 22262306a36Sopenharmony_ci return -EINVAL; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) { 22662306a36Sopenharmony_ci drm_dbg_kms(dev, "bad fb modifier %llu for plane %d\n", 22762306a36Sopenharmony_ci r->modifier[i], i); 22862306a36Sopenharmony_ci return -EINVAL; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (r->flags & DRM_MODE_FB_MODIFIERS && 23262306a36Sopenharmony_ci r->modifier[i] != r->modifier[0]) { 23362306a36Sopenharmony_ci drm_dbg_kms(dev, "bad fb modifier %llu for plane %d\n", 23462306a36Sopenharmony_ci r->modifier[i], i); 23562306a36Sopenharmony_ci return -EINVAL; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* modifier specific checks: */ 23962306a36Sopenharmony_ci switch (r->modifier[i]) { 24062306a36Sopenharmony_ci case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE: 24162306a36Sopenharmony_ci /* NOTE: the pitch restriction may be lifted later if it turns 24262306a36Sopenharmony_ci * out that no hw has this restriction: 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci if (r->pixel_format != DRM_FORMAT_NV12 || 24562306a36Sopenharmony_ci width % 128 || height % 32 || 24662306a36Sopenharmony_ci r->pitches[i] % 128) { 24762306a36Sopenharmony_ci drm_dbg_kms(dev, "bad modifier data for plane %d\n", i); 24862306a36Sopenharmony_ci return -EINVAL; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci default: 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci for (i = info->num_planes; i < 4; i++) { 25862306a36Sopenharmony_ci if (r->modifier[i]) { 25962306a36Sopenharmony_ci drm_dbg_kms(dev, "non-zero modifier for unused plane %d\n", i); 26062306a36Sopenharmony_ci return -EINVAL; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* Pre-FB_MODIFIERS userspace didn't clear the structs properly. */ 26462306a36Sopenharmony_ci if (!(r->flags & DRM_MODE_FB_MODIFIERS)) 26562306a36Sopenharmony_ci continue; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (r->handles[i]) { 26862306a36Sopenharmony_ci drm_dbg_kms(dev, "buffer object handle for unused plane %d\n", i); 26962306a36Sopenharmony_ci return -EINVAL; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (r->pitches[i]) { 27362306a36Sopenharmony_ci drm_dbg_kms(dev, "non-zero pitch for unused plane %d\n", i); 27462306a36Sopenharmony_ci return -EINVAL; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (r->offsets[i]) { 27862306a36Sopenharmony_ci drm_dbg_kms(dev, "non-zero offset for unused plane %d\n", i); 27962306a36Sopenharmony_ci return -EINVAL; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return 0; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistruct drm_framebuffer * 28762306a36Sopenharmony_cidrm_internal_framebuffer_create(struct drm_device *dev, 28862306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *r, 28962306a36Sopenharmony_ci struct drm_file *file_priv) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct drm_mode_config *config = &dev->mode_config; 29262306a36Sopenharmony_ci struct drm_framebuffer *fb; 29362306a36Sopenharmony_ci int ret; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) { 29662306a36Sopenharmony_ci drm_dbg_kms(dev, "bad framebuffer flags 0x%08x\n", r->flags); 29762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if ((config->min_width > r->width) || (r->width > config->max_width)) { 30162306a36Sopenharmony_ci drm_dbg_kms(dev, "bad framebuffer width %d, should be >= %d && <= %d\n", 30262306a36Sopenharmony_ci r->width, config->min_width, config->max_width); 30362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci if ((config->min_height > r->height) || (r->height > config->max_height)) { 30662306a36Sopenharmony_ci drm_dbg_kms(dev, "bad framebuffer height %d, should be >= %d && <= %d\n", 30762306a36Sopenharmony_ci r->height, config->min_height, config->max_height); 30862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (r->flags & DRM_MODE_FB_MODIFIERS && 31262306a36Sopenharmony_ci dev->mode_config.fb_modifiers_not_supported) { 31362306a36Sopenharmony_ci drm_dbg_kms(dev, "driver does not support fb modifiers\n"); 31462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci ret = framebuffer_check(dev, r); 31862306a36Sopenharmony_ci if (ret) 31962306a36Sopenharmony_ci return ERR_PTR(ret); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); 32262306a36Sopenharmony_ci if (IS_ERR(fb)) { 32362306a36Sopenharmony_ci drm_dbg_kms(dev, "could not create framebuffer\n"); 32462306a36Sopenharmony_ci return fb; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return fb; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ciEXPORT_SYMBOL_FOR_TESTS_ONLY(drm_internal_framebuffer_create); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci/** 33262306a36Sopenharmony_ci * drm_mode_addfb2 - add an FB to the graphics configuration 33362306a36Sopenharmony_ci * @dev: drm device for the ioctl 33462306a36Sopenharmony_ci * @data: data pointer for the ioctl 33562306a36Sopenharmony_ci * @file_priv: drm file for the ioctl call 33662306a36Sopenharmony_ci * 33762306a36Sopenharmony_ci * Add a new FB to the specified CRTC, given a user request with format. This is 33862306a36Sopenharmony_ci * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers 33962306a36Sopenharmony_ci * and uses fourcc codes as pixel format specifiers. 34062306a36Sopenharmony_ci * 34162306a36Sopenharmony_ci * Called by the user via ioctl. 34262306a36Sopenharmony_ci * 34362306a36Sopenharmony_ci * Returns: 34462306a36Sopenharmony_ci * Zero on success, negative errno on failure. 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ciint drm_mode_addfb2(struct drm_device *dev, 34762306a36Sopenharmony_ci void *data, struct drm_file *file_priv) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct drm_mode_fb_cmd2 *r = data; 35062306a36Sopenharmony_ci struct drm_framebuffer *fb; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_MODESET)) 35362306a36Sopenharmony_ci return -EOPNOTSUPP; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci fb = drm_internal_framebuffer_create(dev, r, file_priv); 35662306a36Sopenharmony_ci if (IS_ERR(fb)) 35762306a36Sopenharmony_ci return PTR_ERR(fb); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci drm_dbg_kms(dev, "[FB:%d]\n", fb->base.id); 36062306a36Sopenharmony_ci r->fb_id = fb->base.id; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Transfer ownership to the filp for reaping on close */ 36362306a36Sopenharmony_ci mutex_lock(&file_priv->fbs_lock); 36462306a36Sopenharmony_ci list_add(&fb->filp_head, &file_priv->fbs); 36562306a36Sopenharmony_ci mutex_unlock(&file_priv->fbs_lock); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ciint drm_mode_addfb2_ioctl(struct drm_device *dev, 37162306a36Sopenharmony_ci void *data, struct drm_file *file_priv) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 37462306a36Sopenharmony_ci if (!dev->mode_config.quirk_addfb_prefer_host_byte_order) { 37562306a36Sopenharmony_ci /* 37662306a36Sopenharmony_ci * Drivers must set the 37762306a36Sopenharmony_ci * quirk_addfb_prefer_host_byte_order quirk to make 37862306a36Sopenharmony_ci * the drm_mode_addfb() compat code work correctly on 37962306a36Sopenharmony_ci * bigendian machines. 38062306a36Sopenharmony_ci * 38162306a36Sopenharmony_ci * If they don't they interpret pixel_format values 38262306a36Sopenharmony_ci * incorrectly for bug compatibility, which in turn 38362306a36Sopenharmony_ci * implies the ADDFB2 ioctl does not work correctly 38462306a36Sopenharmony_ci * then. So block it to make userspace fallback to 38562306a36Sopenharmony_ci * ADDFB. 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_ci drm_dbg_kms(dev, "addfb2 broken on bigendian"); 38862306a36Sopenharmony_ci return -EOPNOTSUPP; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci#endif 39162306a36Sopenharmony_ci return drm_mode_addfb2(dev, data, file_priv); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistruct drm_mode_rmfb_work { 39562306a36Sopenharmony_ci struct work_struct work; 39662306a36Sopenharmony_ci struct list_head fbs; 39762306a36Sopenharmony_ci}; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic void drm_mode_rmfb_work_fn(struct work_struct *w) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci while (!list_empty(&arg->fbs)) { 40462306a36Sopenharmony_ci struct drm_framebuffer *fb = 40562306a36Sopenharmony_ci list_first_entry(&arg->fbs, typeof(*fb), filp_head); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci drm_dbg_kms(fb->dev, 40862306a36Sopenharmony_ci "Removing [FB:%d] from all active usage due to RMFB ioctl\n", 40962306a36Sopenharmony_ci fb->base.id); 41062306a36Sopenharmony_ci list_del_init(&fb->filp_head); 41162306a36Sopenharmony_ci drm_framebuffer_remove(fb); 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci/** 41662306a36Sopenharmony_ci * drm_mode_rmfb - remove an FB from the configuration 41762306a36Sopenharmony_ci * @dev: drm device 41862306a36Sopenharmony_ci * @fb_id: id of framebuffer to remove 41962306a36Sopenharmony_ci * @file_priv: drm file 42062306a36Sopenharmony_ci * 42162306a36Sopenharmony_ci * Remove the specified FB. 42262306a36Sopenharmony_ci * 42362306a36Sopenharmony_ci * Called by the user via ioctl, or by an in-kernel client. 42462306a36Sopenharmony_ci * 42562306a36Sopenharmony_ci * Returns: 42662306a36Sopenharmony_ci * Zero on success, negative errno on failure. 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_ciint drm_mode_rmfb(struct drm_device *dev, u32 fb_id, 42962306a36Sopenharmony_ci struct drm_file *file_priv) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct drm_framebuffer *fb = NULL; 43262306a36Sopenharmony_ci struct drm_framebuffer *fbl = NULL; 43362306a36Sopenharmony_ci int found = 0; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_MODESET)) 43662306a36Sopenharmony_ci return -EOPNOTSUPP; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci fb = drm_framebuffer_lookup(dev, file_priv, fb_id); 43962306a36Sopenharmony_ci if (!fb) 44062306a36Sopenharmony_ci return -ENOENT; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci mutex_lock(&file_priv->fbs_lock); 44362306a36Sopenharmony_ci list_for_each_entry(fbl, &file_priv->fbs, filp_head) 44462306a36Sopenharmony_ci if (fb == fbl) 44562306a36Sopenharmony_ci found = 1; 44662306a36Sopenharmony_ci if (!found) { 44762306a36Sopenharmony_ci mutex_unlock(&file_priv->fbs_lock); 44862306a36Sopenharmony_ci goto fail_unref; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci list_del_init(&fb->filp_head); 45262306a36Sopenharmony_ci mutex_unlock(&file_priv->fbs_lock); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* drop the reference we picked up in framebuffer lookup */ 45562306a36Sopenharmony_ci drm_framebuffer_put(fb); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* 45862306a36Sopenharmony_ci * we now own the reference that was stored in the fbs list 45962306a36Sopenharmony_ci * 46062306a36Sopenharmony_ci * drm_framebuffer_remove may fail with -EINTR on pending signals, 46162306a36Sopenharmony_ci * so run this in a separate stack as there's no way to correctly 46262306a36Sopenharmony_ci * handle this after the fb is already removed from the lookup table. 46362306a36Sopenharmony_ci */ 46462306a36Sopenharmony_ci if (drm_framebuffer_read_refcount(fb) > 1) { 46562306a36Sopenharmony_ci struct drm_mode_rmfb_work arg; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn); 46862306a36Sopenharmony_ci INIT_LIST_HEAD(&arg.fbs); 46962306a36Sopenharmony_ci list_add_tail(&fb->filp_head, &arg.fbs); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci schedule_work(&arg.work); 47262306a36Sopenharmony_ci flush_work(&arg.work); 47362306a36Sopenharmony_ci destroy_work_on_stack(&arg.work); 47462306a36Sopenharmony_ci } else 47562306a36Sopenharmony_ci drm_framebuffer_put(fb); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return 0; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cifail_unref: 48062306a36Sopenharmony_ci drm_framebuffer_put(fb); 48162306a36Sopenharmony_ci return -ENOENT; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ciint drm_mode_rmfb_ioctl(struct drm_device *dev, 48562306a36Sopenharmony_ci void *data, struct drm_file *file_priv) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci uint32_t *fb_id = data; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return drm_mode_rmfb(dev, *fb_id, file_priv); 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci/** 49362306a36Sopenharmony_ci * drm_mode_getfb - get FB info 49462306a36Sopenharmony_ci * @dev: drm device for the ioctl 49562306a36Sopenharmony_ci * @data: data pointer for the ioctl 49662306a36Sopenharmony_ci * @file_priv: drm file for the ioctl call 49762306a36Sopenharmony_ci * 49862306a36Sopenharmony_ci * Lookup the FB given its ID and return info about it. 49962306a36Sopenharmony_ci * 50062306a36Sopenharmony_ci * Called by the user via ioctl. 50162306a36Sopenharmony_ci * 50262306a36Sopenharmony_ci * Returns: 50362306a36Sopenharmony_ci * Zero on success, negative errno on failure. 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_ciint drm_mode_getfb(struct drm_device *dev, 50662306a36Sopenharmony_ci void *data, struct drm_file *file_priv) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct drm_mode_fb_cmd *r = data; 50962306a36Sopenharmony_ci struct drm_framebuffer *fb; 51062306a36Sopenharmony_ci int ret; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_MODESET)) 51362306a36Sopenharmony_ci return -EOPNOTSUPP; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id); 51662306a36Sopenharmony_ci if (!fb) 51762306a36Sopenharmony_ci return -ENOENT; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* Multi-planar framebuffers need getfb2. */ 52062306a36Sopenharmony_ci if (fb->format->num_planes > 1) { 52162306a36Sopenharmony_ci ret = -EINVAL; 52262306a36Sopenharmony_ci goto out; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (!fb->funcs->create_handle) { 52662306a36Sopenharmony_ci ret = -ENODEV; 52762306a36Sopenharmony_ci goto out; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci r->height = fb->height; 53162306a36Sopenharmony_ci r->width = fb->width; 53262306a36Sopenharmony_ci r->depth = fb->format->depth; 53362306a36Sopenharmony_ci r->bpp = drm_format_info_bpp(fb->format, 0); 53462306a36Sopenharmony_ci r->pitch = fb->pitches[0]; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* GET_FB() is an unprivileged ioctl so we must not return a 53762306a36Sopenharmony_ci * buffer-handle to non-master processes! For 53862306a36Sopenharmony_ci * backwards-compatibility reasons, we cannot make GET_FB() privileged, 53962306a36Sopenharmony_ci * so just return an invalid handle for non-masters. 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_ci if (!drm_is_current_master(file_priv) && !capable(CAP_SYS_ADMIN)) { 54262306a36Sopenharmony_ci r->handle = 0; 54362306a36Sopenharmony_ci ret = 0; 54462306a36Sopenharmony_ci goto out; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci ret = fb->funcs->create_handle(fb, file_priv, &r->handle); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ciout: 55062306a36Sopenharmony_ci drm_framebuffer_put(fb); 55162306a36Sopenharmony_ci return ret; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/** 55562306a36Sopenharmony_ci * drm_mode_getfb2_ioctl - get extended FB info 55662306a36Sopenharmony_ci * @dev: drm device for the ioctl 55762306a36Sopenharmony_ci * @data: data pointer for the ioctl 55862306a36Sopenharmony_ci * @file_priv: drm file for the ioctl call 55962306a36Sopenharmony_ci * 56062306a36Sopenharmony_ci * Lookup the FB given its ID and return info about it. 56162306a36Sopenharmony_ci * 56262306a36Sopenharmony_ci * Called by the user via ioctl. 56362306a36Sopenharmony_ci * 56462306a36Sopenharmony_ci * Returns: 56562306a36Sopenharmony_ci * Zero on success, negative errno on failure. 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ciint drm_mode_getfb2_ioctl(struct drm_device *dev, 56862306a36Sopenharmony_ci void *data, struct drm_file *file_priv) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct drm_mode_fb_cmd2 *r = data; 57162306a36Sopenharmony_ci struct drm_framebuffer *fb; 57262306a36Sopenharmony_ci unsigned int i; 57362306a36Sopenharmony_ci int ret = 0; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_MODESET)) 57662306a36Sopenharmony_ci return -EINVAL; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id); 57962306a36Sopenharmony_ci if (!fb) 58062306a36Sopenharmony_ci return -ENOENT; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* For multi-plane framebuffers, we require the driver to place the 58362306a36Sopenharmony_ci * GEM objects directly in the drm_framebuffer. For single-plane 58462306a36Sopenharmony_ci * framebuffers, we can fall back to create_handle. 58562306a36Sopenharmony_ci */ 58662306a36Sopenharmony_ci if (!fb->obj[0] && 58762306a36Sopenharmony_ci (fb->format->num_planes > 1 || !fb->funcs->create_handle)) { 58862306a36Sopenharmony_ci ret = -ENODEV; 58962306a36Sopenharmony_ci goto out; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci r->height = fb->height; 59362306a36Sopenharmony_ci r->width = fb->width; 59462306a36Sopenharmony_ci r->pixel_format = fb->format->format; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci r->flags = 0; 59762306a36Sopenharmony_ci if (!dev->mode_config.fb_modifiers_not_supported) 59862306a36Sopenharmony_ci r->flags |= DRM_MODE_FB_MODIFIERS; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(r->handles); i++) { 60162306a36Sopenharmony_ci r->handles[i] = 0; 60262306a36Sopenharmony_ci r->pitches[i] = 0; 60362306a36Sopenharmony_ci r->offsets[i] = 0; 60462306a36Sopenharmony_ci r->modifier[i] = 0; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci for (i = 0; i < fb->format->num_planes; i++) { 60862306a36Sopenharmony_ci r->pitches[i] = fb->pitches[i]; 60962306a36Sopenharmony_ci r->offsets[i] = fb->offsets[i]; 61062306a36Sopenharmony_ci if (!dev->mode_config.fb_modifiers_not_supported) 61162306a36Sopenharmony_ci r->modifier[i] = fb->modifier; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* GET_FB2() is an unprivileged ioctl so we must not return a 61562306a36Sopenharmony_ci * buffer-handle to non master/root processes! To match GET_FB() 61662306a36Sopenharmony_ci * just return invalid handles (0) for non masters/root 61762306a36Sopenharmony_ci * rather than making GET_FB2() privileged. 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_ci if (!drm_is_current_master(file_priv) && !capable(CAP_SYS_ADMIN)) { 62062306a36Sopenharmony_ci ret = 0; 62162306a36Sopenharmony_ci goto out; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci for (i = 0; i < fb->format->num_planes; i++) { 62562306a36Sopenharmony_ci int j; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* If we reuse the same object for multiple planes, also 62862306a36Sopenharmony_ci * return the same handle. 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_ci for (j = 0; j < i; j++) { 63162306a36Sopenharmony_ci if (fb->obj[i] == fb->obj[j]) { 63262306a36Sopenharmony_ci r->handles[i] = r->handles[j]; 63362306a36Sopenharmony_ci break; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (r->handles[i]) 63862306a36Sopenharmony_ci continue; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (fb->obj[i]) { 64162306a36Sopenharmony_ci ret = drm_gem_handle_create(file_priv, fb->obj[i], 64262306a36Sopenharmony_ci &r->handles[i]); 64362306a36Sopenharmony_ci } else { 64462306a36Sopenharmony_ci WARN_ON(i > 0); 64562306a36Sopenharmony_ci ret = fb->funcs->create_handle(fb, file_priv, 64662306a36Sopenharmony_ci &r->handles[i]); 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (ret != 0) 65062306a36Sopenharmony_ci goto out; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ciout: 65462306a36Sopenharmony_ci if (ret != 0) { 65562306a36Sopenharmony_ci /* Delete any previously-created handles on failure. */ 65662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(r->handles); i++) { 65762306a36Sopenharmony_ci int j; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (r->handles[i]) 66062306a36Sopenharmony_ci drm_gem_handle_delete(file_priv, r->handles[i]); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* Zero out any handles identical to the one we just 66362306a36Sopenharmony_ci * deleted. 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_ci for (j = i + 1; j < ARRAY_SIZE(r->handles); j++) { 66662306a36Sopenharmony_ci if (r->handles[j] == r->handles[i]) 66762306a36Sopenharmony_ci r->handles[j] = 0; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci drm_framebuffer_put(fb); 67362306a36Sopenharmony_ci return ret; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci/** 67762306a36Sopenharmony_ci * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB 67862306a36Sopenharmony_ci * @dev: drm device for the ioctl 67962306a36Sopenharmony_ci * @data: data pointer for the ioctl 68062306a36Sopenharmony_ci * @file_priv: drm file for the ioctl call 68162306a36Sopenharmony_ci * 68262306a36Sopenharmony_ci * Lookup the FB and flush out the damaged area supplied by userspace as a clip 68362306a36Sopenharmony_ci * rectangle list. Generic userspace which does frontbuffer rendering must call 68462306a36Sopenharmony_ci * this ioctl to flush out the changes on manual-update display outputs, e.g. 68562306a36Sopenharmony_ci * usb display-link, mipi manual update panels or edp panel self refresh modes. 68662306a36Sopenharmony_ci * 68762306a36Sopenharmony_ci * Modesetting drivers which always update the frontbuffer do not need to 68862306a36Sopenharmony_ci * implement the corresponding &drm_framebuffer_funcs.dirty callback. 68962306a36Sopenharmony_ci * 69062306a36Sopenharmony_ci * Called by the user via ioctl. 69162306a36Sopenharmony_ci * 69262306a36Sopenharmony_ci * Returns: 69362306a36Sopenharmony_ci * Zero on success, negative errno on failure. 69462306a36Sopenharmony_ci */ 69562306a36Sopenharmony_ciint drm_mode_dirtyfb_ioctl(struct drm_device *dev, 69662306a36Sopenharmony_ci void *data, struct drm_file *file_priv) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci struct drm_clip_rect __user *clips_ptr; 69962306a36Sopenharmony_ci struct drm_clip_rect *clips = NULL; 70062306a36Sopenharmony_ci struct drm_mode_fb_dirty_cmd *r = data; 70162306a36Sopenharmony_ci struct drm_framebuffer *fb; 70262306a36Sopenharmony_ci unsigned flags; 70362306a36Sopenharmony_ci int num_clips; 70462306a36Sopenharmony_ci int ret; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_MODESET)) 70762306a36Sopenharmony_ci return -EOPNOTSUPP; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id); 71062306a36Sopenharmony_ci if (!fb) 71162306a36Sopenharmony_ci return -ENOENT; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci num_clips = r->num_clips; 71462306a36Sopenharmony_ci clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci if (!num_clips != !clips_ptr) { 71762306a36Sopenharmony_ci ret = -EINVAL; 71862306a36Sopenharmony_ci goto out_err1; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* If userspace annotates copy, clips must come in pairs */ 72462306a36Sopenharmony_ci if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) { 72562306a36Sopenharmony_ci ret = -EINVAL; 72662306a36Sopenharmony_ci goto out_err1; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (num_clips && clips_ptr) { 73062306a36Sopenharmony_ci if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) { 73162306a36Sopenharmony_ci ret = -EINVAL; 73262306a36Sopenharmony_ci goto out_err1; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL); 73562306a36Sopenharmony_ci if (!clips) { 73662306a36Sopenharmony_ci ret = -ENOMEM; 73762306a36Sopenharmony_ci goto out_err1; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci ret = copy_from_user(clips, clips_ptr, 74162306a36Sopenharmony_ci num_clips * sizeof(*clips)); 74262306a36Sopenharmony_ci if (ret) { 74362306a36Sopenharmony_ci ret = -EFAULT; 74462306a36Sopenharmony_ci goto out_err2; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci if (fb->funcs->dirty) { 74962306a36Sopenharmony_ci ret = fb->funcs->dirty(fb, file_priv, flags, r->color, 75062306a36Sopenharmony_ci clips, num_clips); 75162306a36Sopenharmony_ci } else { 75262306a36Sopenharmony_ci ret = -ENOSYS; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ciout_err2: 75662306a36Sopenharmony_ci kfree(clips); 75762306a36Sopenharmony_ciout_err1: 75862306a36Sopenharmony_ci drm_framebuffer_put(fb); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci return ret; 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci/** 76462306a36Sopenharmony_ci * drm_fb_release - remove and free the FBs on this file 76562306a36Sopenharmony_ci * @priv: drm file for the ioctl 76662306a36Sopenharmony_ci * 76762306a36Sopenharmony_ci * Destroy all the FBs associated with @filp. 76862306a36Sopenharmony_ci * 76962306a36Sopenharmony_ci * Called by the user via ioctl. 77062306a36Sopenharmony_ci * 77162306a36Sopenharmony_ci * Returns: 77262306a36Sopenharmony_ci * Zero on success, negative errno on failure. 77362306a36Sopenharmony_ci */ 77462306a36Sopenharmony_civoid drm_fb_release(struct drm_file *priv) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci struct drm_framebuffer *fb, *tfb; 77762306a36Sopenharmony_ci struct drm_mode_rmfb_work arg; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci INIT_LIST_HEAD(&arg.fbs); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* 78262306a36Sopenharmony_ci * When the file gets released that means no one else can access the fb 78362306a36Sopenharmony_ci * list any more, so no need to grab fpriv->fbs_lock. And we need to 78462306a36Sopenharmony_ci * avoid upsetting lockdep since the universal cursor code adds a 78562306a36Sopenharmony_ci * framebuffer while holding mutex locks. 78662306a36Sopenharmony_ci * 78762306a36Sopenharmony_ci * Note that a real deadlock between fpriv->fbs_lock and the modeset 78862306a36Sopenharmony_ci * locks is impossible here since no one else but this function can get 78962306a36Sopenharmony_ci * at it any more. 79062306a36Sopenharmony_ci */ 79162306a36Sopenharmony_ci list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { 79262306a36Sopenharmony_ci if (drm_framebuffer_read_refcount(fb) > 1) { 79362306a36Sopenharmony_ci list_move_tail(&fb->filp_head, &arg.fbs); 79462306a36Sopenharmony_ci } else { 79562306a36Sopenharmony_ci list_del_init(&fb->filp_head); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* This drops the fpriv->fbs reference. */ 79862306a36Sopenharmony_ci drm_framebuffer_put(fb); 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (!list_empty(&arg.fbs)) { 80362306a36Sopenharmony_ci INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci schedule_work(&arg.work); 80662306a36Sopenharmony_ci flush_work(&arg.work); 80762306a36Sopenharmony_ci destroy_work_on_stack(&arg.work); 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_civoid drm_framebuffer_free(struct kref *kref) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci struct drm_framebuffer *fb = 81462306a36Sopenharmony_ci container_of(kref, struct drm_framebuffer, base.refcount); 81562306a36Sopenharmony_ci struct drm_device *dev = fb->dev; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci /* 81862306a36Sopenharmony_ci * The lookup idr holds a weak reference, which has not necessarily been 81962306a36Sopenharmony_ci * removed at this point. Check for that. 82062306a36Sopenharmony_ci */ 82162306a36Sopenharmony_ci drm_mode_object_unregister(dev, &fb->base); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci fb->funcs->destroy(fb); 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci/** 82762306a36Sopenharmony_ci * drm_framebuffer_init - initialize a framebuffer 82862306a36Sopenharmony_ci * @dev: DRM device 82962306a36Sopenharmony_ci * @fb: framebuffer to be initialized 83062306a36Sopenharmony_ci * @funcs: ... with these functions 83162306a36Sopenharmony_ci * 83262306a36Sopenharmony_ci * Allocates an ID for the framebuffer's parent mode object, sets its mode 83362306a36Sopenharmony_ci * functions & device file and adds it to the master fd list. 83462306a36Sopenharmony_ci * 83562306a36Sopenharmony_ci * IMPORTANT: 83662306a36Sopenharmony_ci * This functions publishes the fb and makes it available for concurrent access 83762306a36Sopenharmony_ci * by other users. Which means by this point the fb _must_ be fully set up - 83862306a36Sopenharmony_ci * since all the fb attributes are invariant over its lifetime, no further 83962306a36Sopenharmony_ci * locking but only correct reference counting is required. 84062306a36Sopenharmony_ci * 84162306a36Sopenharmony_ci * Returns: 84262306a36Sopenharmony_ci * Zero on success, error code on failure. 84362306a36Sopenharmony_ci */ 84462306a36Sopenharmony_ciint drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, 84562306a36Sopenharmony_ci const struct drm_framebuffer_funcs *funcs) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci int ret; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (WARN_ON_ONCE(fb->dev != dev || !fb->format)) 85062306a36Sopenharmony_ci return -EINVAL; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci INIT_LIST_HEAD(&fb->filp_head); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci fb->funcs = funcs; 85562306a36Sopenharmony_ci strcpy(fb->comm, current->comm); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci ret = __drm_mode_object_add(dev, &fb->base, DRM_MODE_OBJECT_FB, 85862306a36Sopenharmony_ci false, drm_framebuffer_free); 85962306a36Sopenharmony_ci if (ret) 86062306a36Sopenharmony_ci goto out; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci mutex_lock(&dev->mode_config.fb_lock); 86362306a36Sopenharmony_ci dev->mode_config.num_fb++; 86462306a36Sopenharmony_ci list_add(&fb->head, &dev->mode_config.fb_list); 86562306a36Sopenharmony_ci mutex_unlock(&dev->mode_config.fb_lock); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci drm_mode_object_register(dev, &fb->base); 86862306a36Sopenharmony_ciout: 86962306a36Sopenharmony_ci return ret; 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_framebuffer_init); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci/** 87462306a36Sopenharmony_ci * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference 87562306a36Sopenharmony_ci * @dev: drm device 87662306a36Sopenharmony_ci * @file_priv: drm file to check for lease against. 87762306a36Sopenharmony_ci * @id: id of the fb object 87862306a36Sopenharmony_ci * 87962306a36Sopenharmony_ci * If successful, this grabs an additional reference to the framebuffer - 88062306a36Sopenharmony_ci * callers need to make sure to eventually unreference the returned framebuffer 88162306a36Sopenharmony_ci * again, using drm_framebuffer_put(). 88262306a36Sopenharmony_ci */ 88362306a36Sopenharmony_cistruct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, 88462306a36Sopenharmony_ci struct drm_file *file_priv, 88562306a36Sopenharmony_ci uint32_t id) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci struct drm_mode_object *obj; 88862306a36Sopenharmony_ci struct drm_framebuffer *fb = NULL; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci obj = __drm_mode_object_find(dev, file_priv, id, DRM_MODE_OBJECT_FB); 89162306a36Sopenharmony_ci if (obj) 89262306a36Sopenharmony_ci fb = obj_to_fb(obj); 89362306a36Sopenharmony_ci return fb; 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_framebuffer_lookup); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci/** 89862306a36Sopenharmony_ci * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr 89962306a36Sopenharmony_ci * @fb: fb to unregister 90062306a36Sopenharmony_ci * 90162306a36Sopenharmony_ci * Drivers need to call this when cleaning up driver-private framebuffers, e.g. 90262306a36Sopenharmony_ci * those used for fbdev. Note that the caller must hold a reference of its own, 90362306a36Sopenharmony_ci * i.e. the object may not be destroyed through this call (since it'll lead to a 90462306a36Sopenharmony_ci * locking inversion). 90562306a36Sopenharmony_ci * 90662306a36Sopenharmony_ci * NOTE: This function is deprecated. For driver-private framebuffers it is not 90762306a36Sopenharmony_ci * recommended to embed a framebuffer struct info fbdev struct, instead, a 90862306a36Sopenharmony_ci * framebuffer pointer is preferred and drm_framebuffer_put() should be called 90962306a36Sopenharmony_ci * when the framebuffer is to be cleaned up. 91062306a36Sopenharmony_ci */ 91162306a36Sopenharmony_civoid drm_framebuffer_unregister_private(struct drm_framebuffer *fb) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci struct drm_device *dev; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (!fb) 91662306a36Sopenharmony_ci return; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci dev = fb->dev; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci /* Mark fb as reaped and drop idr ref. */ 92162306a36Sopenharmony_ci drm_mode_object_unregister(dev, &fb->base); 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_framebuffer_unregister_private); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci/** 92662306a36Sopenharmony_ci * drm_framebuffer_cleanup - remove a framebuffer object 92762306a36Sopenharmony_ci * @fb: framebuffer to remove 92862306a36Sopenharmony_ci * 92962306a36Sopenharmony_ci * Cleanup framebuffer. This function is intended to be used from the drivers 93062306a36Sopenharmony_ci * &drm_framebuffer_funcs.destroy callback. It can also be used to clean up 93162306a36Sopenharmony_ci * driver private framebuffers embedded into a larger structure. 93262306a36Sopenharmony_ci * 93362306a36Sopenharmony_ci * Note that this function does not remove the fb from active usage - if it is 93462306a36Sopenharmony_ci * still used anywhere, hilarity can ensue since userspace could call getfb on 93562306a36Sopenharmony_ci * the id and get back -EINVAL. Obviously no concern at driver unload time. 93662306a36Sopenharmony_ci * 93762306a36Sopenharmony_ci * Also, the framebuffer will not be removed from the lookup idr - for 93862306a36Sopenharmony_ci * user-created framebuffers this will happen in the rmfb ioctl. For 93962306a36Sopenharmony_ci * driver-private objects (e.g. for fbdev) drivers need to explicitly call 94062306a36Sopenharmony_ci * drm_framebuffer_unregister_private. 94162306a36Sopenharmony_ci */ 94262306a36Sopenharmony_civoid drm_framebuffer_cleanup(struct drm_framebuffer *fb) 94362306a36Sopenharmony_ci{ 94462306a36Sopenharmony_ci struct drm_device *dev = fb->dev; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci mutex_lock(&dev->mode_config.fb_lock); 94762306a36Sopenharmony_ci list_del(&fb->head); 94862306a36Sopenharmony_ci dev->mode_config.num_fb--; 94962306a36Sopenharmony_ci mutex_unlock(&dev->mode_config.fb_lock); 95062306a36Sopenharmony_ci} 95162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_framebuffer_cleanup); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic int atomic_remove_fb(struct drm_framebuffer *fb) 95462306a36Sopenharmony_ci{ 95562306a36Sopenharmony_ci struct drm_modeset_acquire_ctx ctx; 95662306a36Sopenharmony_ci struct drm_device *dev = fb->dev; 95762306a36Sopenharmony_ci struct drm_atomic_state *state; 95862306a36Sopenharmony_ci struct drm_plane *plane; 95962306a36Sopenharmony_ci struct drm_connector *conn __maybe_unused; 96062306a36Sopenharmony_ci struct drm_connector_state *conn_state; 96162306a36Sopenharmony_ci int i, ret; 96262306a36Sopenharmony_ci unsigned plane_mask; 96362306a36Sopenharmony_ci bool disable_crtcs = false; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ciretry_disable: 96662306a36Sopenharmony_ci drm_modeset_acquire_init(&ctx, 0); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci state = drm_atomic_state_alloc(dev); 96962306a36Sopenharmony_ci if (!state) { 97062306a36Sopenharmony_ci ret = -ENOMEM; 97162306a36Sopenharmony_ci goto out; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci state->acquire_ctx = &ctx; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ciretry: 97662306a36Sopenharmony_ci plane_mask = 0; 97762306a36Sopenharmony_ci ret = drm_modeset_lock_all_ctx(dev, &ctx); 97862306a36Sopenharmony_ci if (ret) 97962306a36Sopenharmony_ci goto unlock; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci drm_for_each_plane(plane, dev) { 98262306a36Sopenharmony_ci struct drm_plane_state *plane_state; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (plane->state->fb != fb) 98562306a36Sopenharmony_ci continue; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci drm_dbg_kms(dev, 98862306a36Sopenharmony_ci "Disabling [PLANE:%d:%s] because [FB:%d] is removed\n", 98962306a36Sopenharmony_ci plane->base.id, plane->name, fb->base.id); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci plane_state = drm_atomic_get_plane_state(state, plane); 99262306a36Sopenharmony_ci if (IS_ERR(plane_state)) { 99362306a36Sopenharmony_ci ret = PTR_ERR(plane_state); 99462306a36Sopenharmony_ci goto unlock; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci if (disable_crtcs && plane_state->crtc->primary == plane) { 99862306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci drm_dbg_kms(dev, 100162306a36Sopenharmony_ci "Disabling [CRTC:%d:%s] because [FB:%d] is removed\n", 100262306a36Sopenharmony_ci plane_state->crtc->base.id, 100362306a36Sopenharmony_ci plane_state->crtc->name, fb->base.id); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci crtc_state = drm_atomic_get_existing_crtc_state(state, plane_state->crtc); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci ret = drm_atomic_add_affected_connectors(state, plane_state->crtc); 100862306a36Sopenharmony_ci if (ret) 100962306a36Sopenharmony_ci goto unlock; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci crtc_state->active = false; 101262306a36Sopenharmony_ci ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL); 101362306a36Sopenharmony_ci if (ret) 101462306a36Sopenharmony_ci goto unlock; 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci drm_atomic_set_fb_for_plane(plane_state, NULL); 101862306a36Sopenharmony_ci ret = drm_atomic_set_crtc_for_plane(plane_state, NULL); 101962306a36Sopenharmony_ci if (ret) 102062306a36Sopenharmony_ci goto unlock; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci plane_mask |= drm_plane_mask(plane); 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci /* This list is only filled when disable_crtcs is set. */ 102662306a36Sopenharmony_ci for_each_new_connector_in_state(state, conn, conn_state, i) { 102762306a36Sopenharmony_ci ret = drm_atomic_set_crtc_for_connector(conn_state, NULL); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci if (ret) 103062306a36Sopenharmony_ci goto unlock; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci if (plane_mask) 103462306a36Sopenharmony_ci ret = drm_atomic_commit(state); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ciunlock: 103762306a36Sopenharmony_ci if (ret == -EDEADLK) { 103862306a36Sopenharmony_ci drm_atomic_state_clear(state); 103962306a36Sopenharmony_ci drm_modeset_backoff(&ctx); 104062306a36Sopenharmony_ci goto retry; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci drm_atomic_state_put(state); 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ciout: 104662306a36Sopenharmony_ci drm_modeset_drop_locks(&ctx); 104762306a36Sopenharmony_ci drm_modeset_acquire_fini(&ctx); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci if (ret == -EINVAL && !disable_crtcs) { 105062306a36Sopenharmony_ci disable_crtcs = true; 105162306a36Sopenharmony_ci goto retry_disable; 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci return ret; 105562306a36Sopenharmony_ci} 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_cistatic void legacy_remove_fb(struct drm_framebuffer *fb) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci struct drm_device *dev = fb->dev; 106062306a36Sopenharmony_ci struct drm_crtc *crtc; 106162306a36Sopenharmony_ci struct drm_plane *plane; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci drm_modeset_lock_all(dev); 106462306a36Sopenharmony_ci /* remove from any CRTC */ 106562306a36Sopenharmony_ci drm_for_each_crtc(crtc, dev) { 106662306a36Sopenharmony_ci if (crtc->primary->fb == fb) { 106762306a36Sopenharmony_ci drm_dbg_kms(dev, 106862306a36Sopenharmony_ci "Disabling [CRTC:%d:%s] because [FB:%d] is removed\n", 106962306a36Sopenharmony_ci crtc->base.id, crtc->name, fb->base.id); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci /* should turn off the crtc */ 107262306a36Sopenharmony_ci if (drm_crtc_force_disable(crtc)) 107362306a36Sopenharmony_ci DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci drm_for_each_plane(plane, dev) { 107862306a36Sopenharmony_ci if (plane->fb == fb) { 107962306a36Sopenharmony_ci drm_dbg_kms(dev, 108062306a36Sopenharmony_ci "Disabling [PLANE:%d:%s] because [FB:%d] is removed\n", 108162306a36Sopenharmony_ci plane->base.id, plane->name, fb->base.id); 108262306a36Sopenharmony_ci drm_plane_force_disable(plane); 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci drm_modeset_unlock_all(dev); 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci/** 108962306a36Sopenharmony_ci * drm_framebuffer_remove - remove and unreference a framebuffer object 109062306a36Sopenharmony_ci * @fb: framebuffer to remove 109162306a36Sopenharmony_ci * 109262306a36Sopenharmony_ci * Scans all the CRTCs and planes in @dev's mode_config. If they're 109362306a36Sopenharmony_ci * using @fb, removes it, setting it to NULL. Then drops the reference to the 109462306a36Sopenharmony_ci * passed-in framebuffer. Might take the modeset locks. 109562306a36Sopenharmony_ci * 109662306a36Sopenharmony_ci * Note that this function optimizes the cleanup away if the caller holds the 109762306a36Sopenharmony_ci * last reference to the framebuffer. It is also guaranteed to not take the 109862306a36Sopenharmony_ci * modeset locks in this case. 109962306a36Sopenharmony_ci */ 110062306a36Sopenharmony_civoid drm_framebuffer_remove(struct drm_framebuffer *fb) 110162306a36Sopenharmony_ci{ 110262306a36Sopenharmony_ci struct drm_device *dev; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci if (!fb) 110562306a36Sopenharmony_ci return; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci dev = fb->dev; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci WARN_ON(!list_empty(&fb->filp_head)); 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci /* 111262306a36Sopenharmony_ci * drm ABI mandates that we remove any deleted framebuffers from active 111362306a36Sopenharmony_ci * usage. But since most sane clients only remove framebuffers they no 111462306a36Sopenharmony_ci * longer need, try to optimize this away. 111562306a36Sopenharmony_ci * 111662306a36Sopenharmony_ci * Since we're holding a reference ourselves, observing a refcount of 1 111762306a36Sopenharmony_ci * means that we're the last holder and can skip it. Also, the refcount 111862306a36Sopenharmony_ci * can never increase from 1 again, so we don't need any barriers or 111962306a36Sopenharmony_ci * locks. 112062306a36Sopenharmony_ci * 112162306a36Sopenharmony_ci * Note that userspace could try to race with use and instate a new 112262306a36Sopenharmony_ci * usage _after_ we've cleared all current ones. End result will be an 112362306a36Sopenharmony_ci * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot 112462306a36Sopenharmony_ci * in this manner. 112562306a36Sopenharmony_ci */ 112662306a36Sopenharmony_ci if (drm_framebuffer_read_refcount(fb) > 1) { 112762306a36Sopenharmony_ci if (drm_drv_uses_atomic_modeset(dev)) { 112862306a36Sopenharmony_ci int ret = atomic_remove_fb(fb); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci WARN(ret, "atomic remove_fb failed with %i\n", ret); 113162306a36Sopenharmony_ci } else 113262306a36Sopenharmony_ci legacy_remove_fb(fb); 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci drm_framebuffer_put(fb); 113662306a36Sopenharmony_ci} 113762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_framebuffer_remove); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci/** 114062306a36Sopenharmony_ci * drm_framebuffer_plane_width - width of the plane given the first plane 114162306a36Sopenharmony_ci * @width: width of the first plane 114262306a36Sopenharmony_ci * @fb: the framebuffer 114362306a36Sopenharmony_ci * @plane: plane index 114462306a36Sopenharmony_ci * 114562306a36Sopenharmony_ci * Returns: 114662306a36Sopenharmony_ci * The width of @plane, given that the width of the first plane is @width. 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_ciint drm_framebuffer_plane_width(int width, 114962306a36Sopenharmony_ci const struct drm_framebuffer *fb, int plane) 115062306a36Sopenharmony_ci{ 115162306a36Sopenharmony_ci if (plane >= fb->format->num_planes) 115262306a36Sopenharmony_ci return 0; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci return fb_plane_width(width, fb->format, plane); 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_framebuffer_plane_width); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci/** 115962306a36Sopenharmony_ci * drm_framebuffer_plane_height - height of the plane given the first plane 116062306a36Sopenharmony_ci * @height: height of the first plane 116162306a36Sopenharmony_ci * @fb: the framebuffer 116262306a36Sopenharmony_ci * @plane: plane index 116362306a36Sopenharmony_ci * 116462306a36Sopenharmony_ci * Returns: 116562306a36Sopenharmony_ci * The height of @plane, given that the height of the first plane is @height. 116662306a36Sopenharmony_ci */ 116762306a36Sopenharmony_ciint drm_framebuffer_plane_height(int height, 116862306a36Sopenharmony_ci const struct drm_framebuffer *fb, int plane) 116962306a36Sopenharmony_ci{ 117062306a36Sopenharmony_ci if (plane >= fb->format->num_planes) 117162306a36Sopenharmony_ci return 0; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci return fb_plane_height(height, fb->format, plane); 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_framebuffer_plane_height); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_civoid drm_framebuffer_print_info(struct drm_printer *p, unsigned int indent, 117862306a36Sopenharmony_ci const struct drm_framebuffer *fb) 117962306a36Sopenharmony_ci{ 118062306a36Sopenharmony_ci unsigned int i; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci drm_printf_indent(p, indent, "allocated by = %s\n", fb->comm); 118362306a36Sopenharmony_ci drm_printf_indent(p, indent, "refcount=%u\n", 118462306a36Sopenharmony_ci drm_framebuffer_read_refcount(fb)); 118562306a36Sopenharmony_ci drm_printf_indent(p, indent, "format=%p4cc\n", &fb->format->format); 118662306a36Sopenharmony_ci drm_printf_indent(p, indent, "modifier=0x%llx\n", fb->modifier); 118762306a36Sopenharmony_ci drm_printf_indent(p, indent, "size=%ux%u\n", fb->width, fb->height); 118862306a36Sopenharmony_ci drm_printf_indent(p, indent, "layers:\n"); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci for (i = 0; i < fb->format->num_planes; i++) { 119162306a36Sopenharmony_ci drm_printf_indent(p, indent + 1, "size[%u]=%dx%d\n", i, 119262306a36Sopenharmony_ci drm_framebuffer_plane_width(fb->width, fb, i), 119362306a36Sopenharmony_ci drm_framebuffer_plane_height(fb->height, fb, i)); 119462306a36Sopenharmony_ci drm_printf_indent(p, indent + 1, "pitch[%u]=%u\n", i, fb->pitches[i]); 119562306a36Sopenharmony_ci drm_printf_indent(p, indent + 1, "offset[%u]=%u\n", i, fb->offsets[i]); 119662306a36Sopenharmony_ci drm_printf_indent(p, indent + 1, "obj[%u]:%s\n", i, 119762306a36Sopenharmony_ci fb->obj[i] ? "" : "(null)"); 119862306a36Sopenharmony_ci if (fb->obj[i]) 119962306a36Sopenharmony_ci drm_gem_print_info(p, indent + 2, fb->obj[i]); 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci} 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 120462306a36Sopenharmony_cistatic int drm_framebuffer_info(struct seq_file *m, void *data) 120562306a36Sopenharmony_ci{ 120662306a36Sopenharmony_ci struct drm_debugfs_entry *entry = m->private; 120762306a36Sopenharmony_ci struct drm_device *dev = entry->dev; 120862306a36Sopenharmony_ci struct drm_printer p = drm_seq_file_printer(m); 120962306a36Sopenharmony_ci struct drm_framebuffer *fb; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci mutex_lock(&dev->mode_config.fb_lock); 121262306a36Sopenharmony_ci drm_for_each_fb(fb, dev) { 121362306a36Sopenharmony_ci drm_printf(&p, "framebuffer[%u]:\n", fb->base.id); 121462306a36Sopenharmony_ci drm_framebuffer_print_info(&p, 1, fb); 121562306a36Sopenharmony_ci } 121662306a36Sopenharmony_ci mutex_unlock(&dev->mode_config.fb_lock); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci return 0; 121962306a36Sopenharmony_ci} 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_cistatic const struct drm_debugfs_info drm_framebuffer_debugfs_list[] = { 122262306a36Sopenharmony_ci { "framebuffer", drm_framebuffer_info, 0 }, 122362306a36Sopenharmony_ci}; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_civoid drm_framebuffer_debugfs_init(struct drm_minor *minor) 122662306a36Sopenharmony_ci{ 122762306a36Sopenharmony_ci drm_debugfs_add_files(minor->dev, drm_framebuffer_debugfs_list, 122862306a36Sopenharmony_ci ARRAY_SIZE(drm_framebuffer_debugfs_list)); 122962306a36Sopenharmony_ci} 123062306a36Sopenharmony_ci#endif 1231