1*1dedbd3bSFrançois Tigeot /* 2*1dedbd3bSFrançois Tigeot * Copyright (c) 2016 Intel Corporation 3*1dedbd3bSFrançois Tigeot * 4*1dedbd3bSFrançois Tigeot * Permission to use, copy, modify, distribute, and sell this software and its 5*1dedbd3bSFrançois Tigeot * documentation for any purpose is hereby granted without fee, provided that 6*1dedbd3bSFrançois Tigeot * the above copyright notice appear in all copies and that both that copyright 7*1dedbd3bSFrançois Tigeot * notice and this permission notice appear in supporting documentation, and 8*1dedbd3bSFrançois Tigeot * that the name of the copyright holders not be used in advertising or 9*1dedbd3bSFrançois Tigeot * publicity pertaining to distribution of the software without specific, 10*1dedbd3bSFrançois Tigeot * written prior permission. The copyright holders make no representations 11*1dedbd3bSFrançois Tigeot * about the suitability of this software for any purpose. It is provided "as 12*1dedbd3bSFrançois Tigeot * is" without express or implied warranty. 13*1dedbd3bSFrançois Tigeot * 14*1dedbd3bSFrançois Tigeot * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15*1dedbd3bSFrançois Tigeot * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 16*1dedbd3bSFrançois Tigeot * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 17*1dedbd3bSFrançois Tigeot * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 18*1dedbd3bSFrançois Tigeot * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19*1dedbd3bSFrançois Tigeot * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 20*1dedbd3bSFrançois Tigeot * OF THIS SOFTWARE. 21*1dedbd3bSFrançois Tigeot */ 22*1dedbd3bSFrançois Tigeot 23*1dedbd3bSFrançois Tigeot #include <linux/export.h> 24*1dedbd3bSFrançois Tigeot #include <drm/drmP.h> 25*1dedbd3bSFrançois Tigeot #include <drm/drm_auth.h> 26*1dedbd3bSFrançois Tigeot #include <drm/drm_framebuffer.h> 27*1dedbd3bSFrançois Tigeot 28*1dedbd3bSFrançois Tigeot #include "drm_crtc_internal.h" 29*1dedbd3bSFrançois Tigeot 30*1dedbd3bSFrançois Tigeot /** 31*1dedbd3bSFrançois Tigeot * DOC: overview 32*1dedbd3bSFrançois Tigeot * 33*1dedbd3bSFrançois Tigeot * Frame buffers are abstract memory objects that provide a source of pixels to 34*1dedbd3bSFrançois Tigeot * scanout to a CRTC. Applications explicitly request the creation of frame 35*1dedbd3bSFrançois Tigeot * buffers through the DRM_IOCTL_MODE_ADDFB(2) ioctls and receive an opaque 36*1dedbd3bSFrançois Tigeot * handle that can be passed to the KMS CRTC control, plane configuration and 37*1dedbd3bSFrançois Tigeot * page flip functions. 38*1dedbd3bSFrançois Tigeot * 39*1dedbd3bSFrançois Tigeot * Frame buffers rely on the underlying memory manager for allocating backing 40*1dedbd3bSFrançois Tigeot * storage. When creating a frame buffer applications pass a memory handle 41*1dedbd3bSFrançois Tigeot * (or a list of memory handles for multi-planar formats) through the 42*1dedbd3bSFrançois Tigeot * struct &drm_mode_fb_cmd2 argument. For drivers using GEM as their userspace 43*1dedbd3bSFrançois Tigeot * buffer management interface this would be a GEM handle. Drivers are however 44*1dedbd3bSFrançois Tigeot * free to use their own backing storage object handles, e.g. vmwgfx directly 45*1dedbd3bSFrançois Tigeot * exposes special TTM handles to userspace and so expects TTM handles in the 46*1dedbd3bSFrançois Tigeot * create ioctl and not GEM handles. 47*1dedbd3bSFrançois Tigeot * 48*1dedbd3bSFrançois Tigeot * Framebuffers are tracked with struct &drm_framebuffer. They are published 49*1dedbd3bSFrançois Tigeot * using drm_framebuffer_init() - after calling that function userspace can use 50*1dedbd3bSFrançois Tigeot * and access the framebuffer object. The helper function 51*1dedbd3bSFrançois Tigeot * drm_helper_mode_fill_fb_struct() can be used to pre-fill the required 52*1dedbd3bSFrançois Tigeot * metadata fields. 53*1dedbd3bSFrançois Tigeot * 54*1dedbd3bSFrançois Tigeot * The lifetime of a drm framebuffer is controlled with a reference count, 55*1dedbd3bSFrançois Tigeot * drivers can grab additional references with drm_framebuffer_reference() and 56*1dedbd3bSFrançois Tigeot * drop them again with drm_framebuffer_unreference(). For driver-private 57*1dedbd3bSFrançois Tigeot * framebuffers for which the last reference is never dropped (e.g. for the 58*1dedbd3bSFrançois Tigeot * fbdev framebuffer when the struct struct &drm_framebuffer is embedded into 59*1dedbd3bSFrançois Tigeot * the fbdev helper struct) drivers can manually clean up a framebuffer at 60*1dedbd3bSFrançois Tigeot * module unload time with drm_framebuffer_unregister_private(). But doing this 61*1dedbd3bSFrançois Tigeot * is not recommended, and it's better to have a normal free-standing struct 62*1dedbd3bSFrançois Tigeot * &drm_framebuffer. 63*1dedbd3bSFrançois Tigeot */ 64*1dedbd3bSFrançois Tigeot 65*1dedbd3bSFrançois Tigeot int drm_framebuffer_check_src_coords(uint32_t src_x, uint32_t src_y, 66*1dedbd3bSFrançois Tigeot uint32_t src_w, uint32_t src_h, 67*1dedbd3bSFrançois Tigeot const struct drm_framebuffer *fb) 68*1dedbd3bSFrançois Tigeot { 69*1dedbd3bSFrançois Tigeot unsigned int fb_width, fb_height; 70*1dedbd3bSFrançois Tigeot 71*1dedbd3bSFrançois Tigeot fb_width = fb->width << 16; 72*1dedbd3bSFrançois Tigeot fb_height = fb->height << 16; 73*1dedbd3bSFrançois Tigeot 74*1dedbd3bSFrançois Tigeot /* Make sure source coordinates are inside the fb. */ 75*1dedbd3bSFrançois Tigeot if (src_w > fb_width || 76*1dedbd3bSFrançois Tigeot src_x > fb_width - src_w || 77*1dedbd3bSFrançois Tigeot src_h > fb_height || 78*1dedbd3bSFrançois Tigeot src_y > fb_height - src_h) { 79*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("Invalid source coordinates " 80*1dedbd3bSFrançois Tigeot "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", 81*1dedbd3bSFrançois Tigeot src_w >> 16, ((src_w & 0xffff) * 15625) >> 10, 82*1dedbd3bSFrançois Tigeot src_h >> 16, ((src_h & 0xffff) * 15625) >> 10, 83*1dedbd3bSFrançois Tigeot src_x >> 16, ((src_x & 0xffff) * 15625) >> 10, 84*1dedbd3bSFrançois Tigeot src_y >> 16, ((src_y & 0xffff) * 15625) >> 10); 85*1dedbd3bSFrançois Tigeot return -ENOSPC; 86*1dedbd3bSFrançois Tigeot } 87*1dedbd3bSFrançois Tigeot 88*1dedbd3bSFrançois Tigeot return 0; 89*1dedbd3bSFrançois Tigeot } 90*1dedbd3bSFrançois Tigeot 91*1dedbd3bSFrançois Tigeot /** 92*1dedbd3bSFrançois Tigeot * drm_mode_addfb - add an FB to the graphics configuration 93*1dedbd3bSFrançois Tigeot * @dev: drm device for the ioctl 94*1dedbd3bSFrançois Tigeot * @data: data pointer for the ioctl 95*1dedbd3bSFrançois Tigeot * @file_priv: drm file for the ioctl call 96*1dedbd3bSFrançois Tigeot * 97*1dedbd3bSFrançois Tigeot * Add a new FB to the specified CRTC, given a user request. This is the 98*1dedbd3bSFrançois Tigeot * original addfb ioctl which only supported RGB formats. 99*1dedbd3bSFrançois Tigeot * 100*1dedbd3bSFrançois Tigeot * Called by the user via ioctl. 101*1dedbd3bSFrançois Tigeot * 102*1dedbd3bSFrançois Tigeot * Returns: 103*1dedbd3bSFrançois Tigeot * Zero on success, negative errno on failure. 104*1dedbd3bSFrançois Tigeot */ 105*1dedbd3bSFrançois Tigeot int drm_mode_addfb(struct drm_device *dev, 106*1dedbd3bSFrançois Tigeot void *data, struct drm_file *file_priv) 107*1dedbd3bSFrançois Tigeot { 108*1dedbd3bSFrançois Tigeot struct drm_mode_fb_cmd *or = data; 109*1dedbd3bSFrançois Tigeot struct drm_mode_fb_cmd2 r = {}; 110*1dedbd3bSFrançois Tigeot int ret; 111*1dedbd3bSFrançois Tigeot 112*1dedbd3bSFrançois Tigeot /* convert to new format and call new ioctl */ 113*1dedbd3bSFrançois Tigeot r.fb_id = or->fb_id; 114*1dedbd3bSFrançois Tigeot r.width = or->width; 115*1dedbd3bSFrançois Tigeot r.height = or->height; 116*1dedbd3bSFrançois Tigeot r.pitches[0] = or->pitch; 117*1dedbd3bSFrançois Tigeot r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); 118*1dedbd3bSFrançois Tigeot r.handles[0] = or->handle; 119*1dedbd3bSFrançois Tigeot 120*1dedbd3bSFrançois Tigeot ret = drm_mode_addfb2(dev, &r, file_priv); 121*1dedbd3bSFrançois Tigeot if (ret) 122*1dedbd3bSFrançois Tigeot return ret; 123*1dedbd3bSFrançois Tigeot 124*1dedbd3bSFrançois Tigeot or->fb_id = r.fb_id; 125*1dedbd3bSFrançois Tigeot 126*1dedbd3bSFrançois Tigeot return 0; 127*1dedbd3bSFrançois Tigeot } 128*1dedbd3bSFrançois Tigeot 129*1dedbd3bSFrançois Tigeot static int format_check(const struct drm_mode_fb_cmd2 *r) 130*1dedbd3bSFrançois Tigeot { 131*1dedbd3bSFrançois Tigeot uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN; 132*1dedbd3bSFrançois Tigeot char *format_name; 133*1dedbd3bSFrançois Tigeot 134*1dedbd3bSFrançois Tigeot switch (format) { 135*1dedbd3bSFrançois Tigeot case DRM_FORMAT_C8: 136*1dedbd3bSFrançois Tigeot case DRM_FORMAT_RGB332: 137*1dedbd3bSFrançois Tigeot case DRM_FORMAT_BGR233: 138*1dedbd3bSFrançois Tigeot case DRM_FORMAT_XRGB4444: 139*1dedbd3bSFrançois Tigeot case DRM_FORMAT_XBGR4444: 140*1dedbd3bSFrançois Tigeot case DRM_FORMAT_RGBX4444: 141*1dedbd3bSFrançois Tigeot case DRM_FORMAT_BGRX4444: 142*1dedbd3bSFrançois Tigeot case DRM_FORMAT_ARGB4444: 143*1dedbd3bSFrançois Tigeot case DRM_FORMAT_ABGR4444: 144*1dedbd3bSFrançois Tigeot case DRM_FORMAT_RGBA4444: 145*1dedbd3bSFrançois Tigeot case DRM_FORMAT_BGRA4444: 146*1dedbd3bSFrançois Tigeot case DRM_FORMAT_XRGB1555: 147*1dedbd3bSFrançois Tigeot case DRM_FORMAT_XBGR1555: 148*1dedbd3bSFrançois Tigeot case DRM_FORMAT_RGBX5551: 149*1dedbd3bSFrançois Tigeot case DRM_FORMAT_BGRX5551: 150*1dedbd3bSFrançois Tigeot case DRM_FORMAT_ARGB1555: 151*1dedbd3bSFrançois Tigeot case DRM_FORMAT_ABGR1555: 152*1dedbd3bSFrançois Tigeot case DRM_FORMAT_RGBA5551: 153*1dedbd3bSFrançois Tigeot case DRM_FORMAT_BGRA5551: 154*1dedbd3bSFrançois Tigeot case DRM_FORMAT_RGB565: 155*1dedbd3bSFrançois Tigeot case DRM_FORMAT_BGR565: 156*1dedbd3bSFrançois Tigeot case DRM_FORMAT_RGB888: 157*1dedbd3bSFrançois Tigeot case DRM_FORMAT_BGR888: 158*1dedbd3bSFrançois Tigeot case DRM_FORMAT_XRGB8888: 159*1dedbd3bSFrançois Tigeot case DRM_FORMAT_XBGR8888: 160*1dedbd3bSFrançois Tigeot case DRM_FORMAT_RGBX8888: 161*1dedbd3bSFrançois Tigeot case DRM_FORMAT_BGRX8888: 162*1dedbd3bSFrançois Tigeot case DRM_FORMAT_ARGB8888: 163*1dedbd3bSFrançois Tigeot case DRM_FORMAT_ABGR8888: 164*1dedbd3bSFrançois Tigeot case DRM_FORMAT_RGBA8888: 165*1dedbd3bSFrançois Tigeot case DRM_FORMAT_BGRA8888: 166*1dedbd3bSFrançois Tigeot case DRM_FORMAT_XRGB2101010: 167*1dedbd3bSFrançois Tigeot case DRM_FORMAT_XBGR2101010: 168*1dedbd3bSFrançois Tigeot case DRM_FORMAT_RGBX1010102: 169*1dedbd3bSFrançois Tigeot case DRM_FORMAT_BGRX1010102: 170*1dedbd3bSFrançois Tigeot case DRM_FORMAT_ARGB2101010: 171*1dedbd3bSFrançois Tigeot case DRM_FORMAT_ABGR2101010: 172*1dedbd3bSFrançois Tigeot case DRM_FORMAT_RGBA1010102: 173*1dedbd3bSFrançois Tigeot case DRM_FORMAT_BGRA1010102: 174*1dedbd3bSFrançois Tigeot case DRM_FORMAT_YUYV: 175*1dedbd3bSFrançois Tigeot case DRM_FORMAT_YVYU: 176*1dedbd3bSFrançois Tigeot case DRM_FORMAT_UYVY: 177*1dedbd3bSFrançois Tigeot case DRM_FORMAT_VYUY: 178*1dedbd3bSFrançois Tigeot case DRM_FORMAT_AYUV: 179*1dedbd3bSFrançois Tigeot case DRM_FORMAT_NV12: 180*1dedbd3bSFrançois Tigeot case DRM_FORMAT_NV21: 181*1dedbd3bSFrançois Tigeot case DRM_FORMAT_NV16: 182*1dedbd3bSFrançois Tigeot case DRM_FORMAT_NV61: 183*1dedbd3bSFrançois Tigeot case DRM_FORMAT_NV24: 184*1dedbd3bSFrançois Tigeot case DRM_FORMAT_NV42: 185*1dedbd3bSFrançois Tigeot case DRM_FORMAT_YUV410: 186*1dedbd3bSFrançois Tigeot case DRM_FORMAT_YVU410: 187*1dedbd3bSFrançois Tigeot case DRM_FORMAT_YUV411: 188*1dedbd3bSFrançois Tigeot case DRM_FORMAT_YVU411: 189*1dedbd3bSFrançois Tigeot case DRM_FORMAT_YUV420: 190*1dedbd3bSFrançois Tigeot case DRM_FORMAT_YVU420: 191*1dedbd3bSFrançois Tigeot case DRM_FORMAT_YUV422: 192*1dedbd3bSFrançois Tigeot case DRM_FORMAT_YVU422: 193*1dedbd3bSFrançois Tigeot case DRM_FORMAT_YUV444: 194*1dedbd3bSFrançois Tigeot case DRM_FORMAT_YVU444: 195*1dedbd3bSFrançois Tigeot return 0; 196*1dedbd3bSFrançois Tigeot default: 197*1dedbd3bSFrançois Tigeot format_name = drm_get_format_name(r->pixel_format); 198*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("invalid pixel format %s\n", format_name); 199*1dedbd3bSFrançois Tigeot kfree(format_name); 200*1dedbd3bSFrançois Tigeot return -EINVAL; 201*1dedbd3bSFrançois Tigeot } 202*1dedbd3bSFrançois Tigeot } 203*1dedbd3bSFrançois Tigeot 204*1dedbd3bSFrançois Tigeot static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) 205*1dedbd3bSFrançois Tigeot { 206*1dedbd3bSFrançois Tigeot int ret, hsub, vsub, num_planes, i; 207*1dedbd3bSFrançois Tigeot 208*1dedbd3bSFrançois Tigeot ret = format_check(r); 209*1dedbd3bSFrançois Tigeot if (ret) { 210*1dedbd3bSFrançois Tigeot char *format_name = drm_get_format_name(r->pixel_format); 211*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer format %s\n", format_name); 212*1dedbd3bSFrançois Tigeot kfree(format_name); 213*1dedbd3bSFrançois Tigeot return ret; 214*1dedbd3bSFrançois Tigeot } 215*1dedbd3bSFrançois Tigeot 216*1dedbd3bSFrançois Tigeot hsub = drm_format_horz_chroma_subsampling(r->pixel_format); 217*1dedbd3bSFrançois Tigeot vsub = drm_format_vert_chroma_subsampling(r->pixel_format); 218*1dedbd3bSFrançois Tigeot num_planes = drm_format_num_planes(r->pixel_format); 219*1dedbd3bSFrançois Tigeot 220*1dedbd3bSFrançois Tigeot if (r->width == 0 || r->width % hsub) { 221*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width); 222*1dedbd3bSFrançois Tigeot return -EINVAL; 223*1dedbd3bSFrançois Tigeot } 224*1dedbd3bSFrançois Tigeot 225*1dedbd3bSFrançois Tigeot if (r->height == 0 || r->height % vsub) { 226*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height); 227*1dedbd3bSFrançois Tigeot return -EINVAL; 228*1dedbd3bSFrançois Tigeot } 229*1dedbd3bSFrançois Tigeot 230*1dedbd3bSFrançois Tigeot for (i = 0; i < num_planes; i++) { 231*1dedbd3bSFrançois Tigeot unsigned int width = r->width / (i != 0 ? hsub : 1); 232*1dedbd3bSFrançois Tigeot unsigned int height = r->height / (i != 0 ? vsub : 1); 233*1dedbd3bSFrançois Tigeot unsigned int cpp = drm_format_plane_cpp(r->pixel_format, i); 234*1dedbd3bSFrançois Tigeot 235*1dedbd3bSFrançois Tigeot if (!r->handles[i]) { 236*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i); 237*1dedbd3bSFrançois Tigeot return -EINVAL; 238*1dedbd3bSFrançois Tigeot } 239*1dedbd3bSFrançois Tigeot 240*1dedbd3bSFrançois Tigeot if ((uint64_t) width * cpp > UINT_MAX) 241*1dedbd3bSFrançois Tigeot return -ERANGE; 242*1dedbd3bSFrançois Tigeot 243*1dedbd3bSFrançois Tigeot if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX) 244*1dedbd3bSFrançois Tigeot return -ERANGE; 245*1dedbd3bSFrançois Tigeot 246*1dedbd3bSFrançois Tigeot if (r->pitches[i] < width * cpp) { 247*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); 248*1dedbd3bSFrançois Tigeot return -EINVAL; 249*1dedbd3bSFrançois Tigeot } 250*1dedbd3bSFrançois Tigeot 251*1dedbd3bSFrançois Tigeot if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) { 252*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n", 253*1dedbd3bSFrançois Tigeot r->modifier[i], i); 254*1dedbd3bSFrançois Tigeot return -EINVAL; 255*1dedbd3bSFrançois Tigeot } 256*1dedbd3bSFrançois Tigeot 257*1dedbd3bSFrançois Tigeot /* modifier specific checks: */ 258*1dedbd3bSFrançois Tigeot switch (r->modifier[i]) { 259*1dedbd3bSFrançois Tigeot case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE: 260*1dedbd3bSFrançois Tigeot /* NOTE: the pitch restriction may be lifted later if it turns 261*1dedbd3bSFrançois Tigeot * out that no hw has this restriction: 262*1dedbd3bSFrançois Tigeot */ 263*1dedbd3bSFrançois Tigeot if (r->pixel_format != DRM_FORMAT_NV12 || 264*1dedbd3bSFrançois Tigeot width % 128 || height % 32 || 265*1dedbd3bSFrançois Tigeot r->pitches[i] % 128) { 266*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad modifier data for plane %d\n", i); 267*1dedbd3bSFrançois Tigeot return -EINVAL; 268*1dedbd3bSFrançois Tigeot } 269*1dedbd3bSFrançois Tigeot break; 270*1dedbd3bSFrançois Tigeot 271*1dedbd3bSFrançois Tigeot default: 272*1dedbd3bSFrançois Tigeot break; 273*1dedbd3bSFrançois Tigeot } 274*1dedbd3bSFrançois Tigeot } 275*1dedbd3bSFrançois Tigeot 276*1dedbd3bSFrançois Tigeot for (i = num_planes; i < 4; i++) { 277*1dedbd3bSFrançois Tigeot if (r->modifier[i]) { 278*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("non-zero modifier for unused plane %d\n", i); 279*1dedbd3bSFrançois Tigeot return -EINVAL; 280*1dedbd3bSFrançois Tigeot } 281*1dedbd3bSFrançois Tigeot 282*1dedbd3bSFrançois Tigeot /* Pre-FB_MODIFIERS userspace didn't clear the structs properly. */ 283*1dedbd3bSFrançois Tigeot if (!(r->flags & DRM_MODE_FB_MODIFIERS)) 284*1dedbd3bSFrançois Tigeot continue; 285*1dedbd3bSFrançois Tigeot 286*1dedbd3bSFrançois Tigeot if (r->handles[i]) { 287*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("buffer object handle for unused plane %d\n", i); 288*1dedbd3bSFrançois Tigeot return -EINVAL; 289*1dedbd3bSFrançois Tigeot } 290*1dedbd3bSFrançois Tigeot 291*1dedbd3bSFrançois Tigeot if (r->pitches[i]) { 292*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("non-zero pitch for unused plane %d\n", i); 293*1dedbd3bSFrançois Tigeot return -EINVAL; 294*1dedbd3bSFrançois Tigeot } 295*1dedbd3bSFrançois Tigeot 296*1dedbd3bSFrançois Tigeot if (r->offsets[i]) { 297*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("non-zero offset for unused plane %d\n", i); 298*1dedbd3bSFrançois Tigeot return -EINVAL; 299*1dedbd3bSFrançois Tigeot } 300*1dedbd3bSFrançois Tigeot } 301*1dedbd3bSFrançois Tigeot 302*1dedbd3bSFrançois Tigeot return 0; 303*1dedbd3bSFrançois Tigeot } 304*1dedbd3bSFrançois Tigeot 305*1dedbd3bSFrançois Tigeot struct drm_framebuffer * 306*1dedbd3bSFrançois Tigeot drm_internal_framebuffer_create(struct drm_device *dev, 307*1dedbd3bSFrançois Tigeot const struct drm_mode_fb_cmd2 *r, 308*1dedbd3bSFrançois Tigeot struct drm_file *file_priv) 309*1dedbd3bSFrançois Tigeot { 310*1dedbd3bSFrançois Tigeot struct drm_mode_config *config = &dev->mode_config; 311*1dedbd3bSFrançois Tigeot struct drm_framebuffer *fb; 312*1dedbd3bSFrançois Tigeot int ret; 313*1dedbd3bSFrançois Tigeot 314*1dedbd3bSFrançois Tigeot if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) { 315*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags); 316*1dedbd3bSFrançois Tigeot return ERR_PTR(-EINVAL); 317*1dedbd3bSFrançois Tigeot } 318*1dedbd3bSFrançois Tigeot 319*1dedbd3bSFrançois Tigeot if ((config->min_width > r->width) || (r->width > config->max_width)) { 320*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n", 321*1dedbd3bSFrançois Tigeot r->width, config->min_width, config->max_width); 322*1dedbd3bSFrançois Tigeot return ERR_PTR(-EINVAL); 323*1dedbd3bSFrançois Tigeot } 324*1dedbd3bSFrançois Tigeot if ((config->min_height > r->height) || (r->height > config->max_height)) { 325*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n", 326*1dedbd3bSFrançois Tigeot r->height, config->min_height, config->max_height); 327*1dedbd3bSFrançois Tigeot return ERR_PTR(-EINVAL); 328*1dedbd3bSFrançois Tigeot } 329*1dedbd3bSFrançois Tigeot 330*1dedbd3bSFrançois Tigeot if (r->flags & DRM_MODE_FB_MODIFIERS && 331*1dedbd3bSFrançois Tigeot !dev->mode_config.allow_fb_modifiers) { 332*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("driver does not support fb modifiers\n"); 333*1dedbd3bSFrançois Tigeot return ERR_PTR(-EINVAL); 334*1dedbd3bSFrançois Tigeot } 335*1dedbd3bSFrançois Tigeot 336*1dedbd3bSFrançois Tigeot ret = framebuffer_check(r); 337*1dedbd3bSFrançois Tigeot if (ret) 338*1dedbd3bSFrançois Tigeot return ERR_PTR(ret); 339*1dedbd3bSFrançois Tigeot 340*1dedbd3bSFrançois Tigeot fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); 341*1dedbd3bSFrançois Tigeot if (IS_ERR(fb)) { 342*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("could not create framebuffer\n"); 343*1dedbd3bSFrançois Tigeot return fb; 344*1dedbd3bSFrançois Tigeot } 345*1dedbd3bSFrançois Tigeot 346*1dedbd3bSFrançois Tigeot return fb; 347*1dedbd3bSFrançois Tigeot } 348*1dedbd3bSFrançois Tigeot 349*1dedbd3bSFrançois Tigeot /** 350*1dedbd3bSFrançois Tigeot * drm_mode_addfb2 - add an FB to the graphics configuration 351*1dedbd3bSFrançois Tigeot * @dev: drm device for the ioctl 352*1dedbd3bSFrançois Tigeot * @data: data pointer for the ioctl 353*1dedbd3bSFrançois Tigeot * @file_priv: drm file for the ioctl call 354*1dedbd3bSFrançois Tigeot * 355*1dedbd3bSFrançois Tigeot * Add a new FB to the specified CRTC, given a user request with format. This is 356*1dedbd3bSFrançois Tigeot * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers 357*1dedbd3bSFrançois Tigeot * and uses fourcc codes as pixel format specifiers. 358*1dedbd3bSFrançois Tigeot * 359*1dedbd3bSFrançois Tigeot * Called by the user via ioctl. 360*1dedbd3bSFrançois Tigeot * 361*1dedbd3bSFrançois Tigeot * Returns: 362*1dedbd3bSFrançois Tigeot * Zero on success, negative errno on failure. 363*1dedbd3bSFrançois Tigeot */ 364*1dedbd3bSFrançois Tigeot int drm_mode_addfb2(struct drm_device *dev, 365*1dedbd3bSFrançois Tigeot void *data, struct drm_file *file_priv) 366*1dedbd3bSFrançois Tigeot { 367*1dedbd3bSFrançois Tigeot struct drm_mode_fb_cmd2 *r = data; 368*1dedbd3bSFrançois Tigeot struct drm_framebuffer *fb; 369*1dedbd3bSFrançois Tigeot 370*1dedbd3bSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 371*1dedbd3bSFrançois Tigeot return -EINVAL; 372*1dedbd3bSFrançois Tigeot 373*1dedbd3bSFrançois Tigeot fb = drm_internal_framebuffer_create(dev, r, file_priv); 374*1dedbd3bSFrançois Tigeot if (IS_ERR(fb)) 375*1dedbd3bSFrançois Tigeot return PTR_ERR(fb); 376*1dedbd3bSFrançois Tigeot 377*1dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); 378*1dedbd3bSFrançois Tigeot r->fb_id = fb->base.id; 379*1dedbd3bSFrançois Tigeot 380*1dedbd3bSFrançois Tigeot /* Transfer ownership to the filp for reaping on close */ 381*1dedbd3bSFrançois Tigeot mutex_lock(&file_priv->fbs_lock); 382*1dedbd3bSFrançois Tigeot list_add(&fb->filp_head, &file_priv->fbs); 383*1dedbd3bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 384*1dedbd3bSFrançois Tigeot 385*1dedbd3bSFrançois Tigeot return 0; 386*1dedbd3bSFrançois Tigeot } 387*1dedbd3bSFrançois Tigeot 388*1dedbd3bSFrançois Tigeot struct drm_mode_rmfb_work { 389*1dedbd3bSFrançois Tigeot struct work_struct work; 390*1dedbd3bSFrançois Tigeot struct list_head fbs; 391*1dedbd3bSFrançois Tigeot }; 392*1dedbd3bSFrançois Tigeot 393*1dedbd3bSFrançois Tigeot static void drm_mode_rmfb_work_fn(struct work_struct *w) 394*1dedbd3bSFrançois Tigeot { 395*1dedbd3bSFrançois Tigeot struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work); 396*1dedbd3bSFrançois Tigeot 397*1dedbd3bSFrançois Tigeot while (!list_empty(&arg->fbs)) { 398*1dedbd3bSFrançois Tigeot struct drm_framebuffer *fb = 399*1dedbd3bSFrançois Tigeot list_first_entry(&arg->fbs, typeof(*fb), filp_head); 400*1dedbd3bSFrançois Tigeot 401*1dedbd3bSFrançois Tigeot list_del_init(&fb->filp_head); 402*1dedbd3bSFrançois Tigeot drm_framebuffer_remove(fb); 403*1dedbd3bSFrançois Tigeot } 404*1dedbd3bSFrançois Tigeot } 405*1dedbd3bSFrançois Tigeot 406*1dedbd3bSFrançois Tigeot /** 407*1dedbd3bSFrançois Tigeot * drm_mode_rmfb - remove an FB from the configuration 408*1dedbd3bSFrançois Tigeot * @dev: drm device for the ioctl 409*1dedbd3bSFrançois Tigeot * @data: data pointer for the ioctl 410*1dedbd3bSFrançois Tigeot * @file_priv: drm file for the ioctl call 411*1dedbd3bSFrançois Tigeot * 412*1dedbd3bSFrançois Tigeot * Remove the FB specified by the user. 413*1dedbd3bSFrançois Tigeot * 414*1dedbd3bSFrançois Tigeot * Called by the user via ioctl. 415*1dedbd3bSFrançois Tigeot * 416*1dedbd3bSFrançois Tigeot * Returns: 417*1dedbd3bSFrançois Tigeot * Zero on success, negative errno on failure. 418*1dedbd3bSFrançois Tigeot */ 419*1dedbd3bSFrançois Tigeot int drm_mode_rmfb(struct drm_device *dev, 420*1dedbd3bSFrançois Tigeot void *data, struct drm_file *file_priv) 421*1dedbd3bSFrançois Tigeot { 422*1dedbd3bSFrançois Tigeot struct drm_framebuffer *fb = NULL; 423*1dedbd3bSFrançois Tigeot struct drm_framebuffer *fbl = NULL; 424*1dedbd3bSFrançois Tigeot uint32_t *id = data; 425*1dedbd3bSFrançois Tigeot int found = 0; 426*1dedbd3bSFrançois Tigeot 427*1dedbd3bSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 428*1dedbd3bSFrançois Tigeot return -EINVAL; 429*1dedbd3bSFrançois Tigeot 430*1dedbd3bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, *id); 431*1dedbd3bSFrançois Tigeot if (!fb) 432*1dedbd3bSFrançois Tigeot return -ENOENT; 433*1dedbd3bSFrançois Tigeot 434*1dedbd3bSFrançois Tigeot mutex_lock(&file_priv->fbs_lock); 435*1dedbd3bSFrançois Tigeot list_for_each_entry(fbl, &file_priv->fbs, filp_head) 436*1dedbd3bSFrançois Tigeot if (fb == fbl) 437*1dedbd3bSFrançois Tigeot found = 1; 438*1dedbd3bSFrançois Tigeot if (!found) { 439*1dedbd3bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 440*1dedbd3bSFrançois Tigeot goto fail_unref; 441*1dedbd3bSFrançois Tigeot } 442*1dedbd3bSFrançois Tigeot 443*1dedbd3bSFrançois Tigeot list_del_init(&fb->filp_head); 444*1dedbd3bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 445*1dedbd3bSFrançois Tigeot 446*1dedbd3bSFrançois Tigeot /* drop the reference we picked up in framebuffer lookup */ 447*1dedbd3bSFrançois Tigeot drm_framebuffer_unreference(fb); 448*1dedbd3bSFrançois Tigeot 449*1dedbd3bSFrançois Tigeot /* 450*1dedbd3bSFrançois Tigeot * we now own the reference that was stored in the fbs list 451*1dedbd3bSFrançois Tigeot * 452*1dedbd3bSFrançois Tigeot * drm_framebuffer_remove may fail with -EINTR on pending signals, 453*1dedbd3bSFrançois Tigeot * so run this in a separate stack as there's no way to correctly 454*1dedbd3bSFrançois Tigeot * handle this after the fb is already removed from the lookup table. 455*1dedbd3bSFrançois Tigeot */ 456*1dedbd3bSFrançois Tigeot if (drm_framebuffer_read_refcount(fb) > 1) { 457*1dedbd3bSFrançois Tigeot struct drm_mode_rmfb_work arg; 458*1dedbd3bSFrançois Tigeot 459*1dedbd3bSFrançois Tigeot INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn); 460*1dedbd3bSFrançois Tigeot INIT_LIST_HEAD(&arg.fbs); 461*1dedbd3bSFrançois Tigeot list_add_tail(&fb->filp_head, &arg.fbs); 462*1dedbd3bSFrançois Tigeot 463*1dedbd3bSFrançois Tigeot schedule_work(&arg.work); 464*1dedbd3bSFrançois Tigeot flush_work(&arg.work); 465*1dedbd3bSFrançois Tigeot destroy_work_on_stack(&arg.work); 466*1dedbd3bSFrançois Tigeot } else 467*1dedbd3bSFrançois Tigeot drm_framebuffer_unreference(fb); 468*1dedbd3bSFrançois Tigeot 469*1dedbd3bSFrançois Tigeot return 0; 470*1dedbd3bSFrançois Tigeot 471*1dedbd3bSFrançois Tigeot fail_unref: 472*1dedbd3bSFrançois Tigeot drm_framebuffer_unreference(fb); 473*1dedbd3bSFrançois Tigeot return -ENOENT; 474*1dedbd3bSFrançois Tigeot } 475*1dedbd3bSFrançois Tigeot 476*1dedbd3bSFrançois Tigeot /** 477*1dedbd3bSFrançois Tigeot * drm_mode_getfb - get FB info 478*1dedbd3bSFrançois Tigeot * @dev: drm device for the ioctl 479*1dedbd3bSFrançois Tigeot * @data: data pointer for the ioctl 480*1dedbd3bSFrançois Tigeot * @file_priv: drm file for the ioctl call 481*1dedbd3bSFrançois Tigeot * 482*1dedbd3bSFrançois Tigeot * Lookup the FB given its ID and return info about it. 483*1dedbd3bSFrançois Tigeot * 484*1dedbd3bSFrançois Tigeot * Called by the user via ioctl. 485*1dedbd3bSFrançois Tigeot * 486*1dedbd3bSFrançois Tigeot * Returns: 487*1dedbd3bSFrançois Tigeot * Zero on success, negative errno on failure. 488*1dedbd3bSFrançois Tigeot */ 489*1dedbd3bSFrançois Tigeot int drm_mode_getfb(struct drm_device *dev, 490*1dedbd3bSFrançois Tigeot void *data, struct drm_file *file_priv) 491*1dedbd3bSFrançois Tigeot { 492*1dedbd3bSFrançois Tigeot struct drm_mode_fb_cmd *r = data; 493*1dedbd3bSFrançois Tigeot struct drm_framebuffer *fb; 494*1dedbd3bSFrançois Tigeot int ret; 495*1dedbd3bSFrançois Tigeot 496*1dedbd3bSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 497*1dedbd3bSFrançois Tigeot return -EINVAL; 498*1dedbd3bSFrançois Tigeot 499*1dedbd3bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, r->fb_id); 500*1dedbd3bSFrançois Tigeot if (!fb) 501*1dedbd3bSFrançois Tigeot return -ENOENT; 502*1dedbd3bSFrançois Tigeot 503*1dedbd3bSFrançois Tigeot r->height = fb->height; 504*1dedbd3bSFrançois Tigeot r->width = fb->width; 505*1dedbd3bSFrançois Tigeot r->depth = fb->depth; 506*1dedbd3bSFrançois Tigeot r->bpp = fb->bits_per_pixel; 507*1dedbd3bSFrançois Tigeot r->pitch = fb->pitches[0]; 508*1dedbd3bSFrançois Tigeot if (fb->funcs->create_handle) { 509*1dedbd3bSFrançois Tigeot if (drm_is_current_master(file_priv) || capable(CAP_SYS_ADMIN) || 510*1dedbd3bSFrançois Tigeot drm_is_control_client(file_priv)) { 511*1dedbd3bSFrançois Tigeot ret = fb->funcs->create_handle(fb, file_priv, 512*1dedbd3bSFrançois Tigeot &r->handle); 513*1dedbd3bSFrançois Tigeot } else { 514*1dedbd3bSFrançois Tigeot /* GET_FB() is an unprivileged ioctl so we must not 515*1dedbd3bSFrançois Tigeot * return a buffer-handle to non-master processes! For 516*1dedbd3bSFrançois Tigeot * backwards-compatibility reasons, we cannot make 517*1dedbd3bSFrançois Tigeot * GET_FB() privileged, so just return an invalid handle 518*1dedbd3bSFrançois Tigeot * for non-masters. */ 519*1dedbd3bSFrançois Tigeot r->handle = 0; 520*1dedbd3bSFrançois Tigeot ret = 0; 521*1dedbd3bSFrançois Tigeot } 522*1dedbd3bSFrançois Tigeot } else { 523*1dedbd3bSFrançois Tigeot ret = -ENODEV; 524*1dedbd3bSFrançois Tigeot } 525*1dedbd3bSFrançois Tigeot 526*1dedbd3bSFrançois Tigeot drm_framebuffer_unreference(fb); 527*1dedbd3bSFrançois Tigeot 528*1dedbd3bSFrançois Tigeot return ret; 529*1dedbd3bSFrançois Tigeot } 530*1dedbd3bSFrançois Tigeot 531*1dedbd3bSFrançois Tigeot /** 532*1dedbd3bSFrançois Tigeot * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB 533*1dedbd3bSFrançois Tigeot * @dev: drm device for the ioctl 534*1dedbd3bSFrançois Tigeot * @data: data pointer for the ioctl 535*1dedbd3bSFrançois Tigeot * @file_priv: drm file for the ioctl call 536*1dedbd3bSFrançois Tigeot * 537*1dedbd3bSFrançois Tigeot * Lookup the FB and flush out the damaged area supplied by userspace as a clip 538*1dedbd3bSFrançois Tigeot * rectangle list. Generic userspace which does frontbuffer rendering must call 539*1dedbd3bSFrançois Tigeot * this ioctl to flush out the changes on manual-update display outputs, e.g. 540*1dedbd3bSFrançois Tigeot * usb display-link, mipi manual update panels or edp panel self refresh modes. 541*1dedbd3bSFrançois Tigeot * 542*1dedbd3bSFrançois Tigeot * Modesetting drivers which always update the frontbuffer do not need to 543*1dedbd3bSFrançois Tigeot * implement the corresponding ->dirty framebuffer callback. 544*1dedbd3bSFrançois Tigeot * 545*1dedbd3bSFrançois Tigeot * Called by the user via ioctl. 546*1dedbd3bSFrançois Tigeot * 547*1dedbd3bSFrançois Tigeot * Returns: 548*1dedbd3bSFrançois Tigeot * Zero on success, negative errno on failure. 549*1dedbd3bSFrançois Tigeot */ 550*1dedbd3bSFrançois Tigeot int drm_mode_dirtyfb_ioctl(struct drm_device *dev, 551*1dedbd3bSFrançois Tigeot void *data, struct drm_file *file_priv) 552*1dedbd3bSFrançois Tigeot { 553*1dedbd3bSFrançois Tigeot struct drm_clip_rect __user *clips_ptr; 554*1dedbd3bSFrançois Tigeot struct drm_clip_rect *clips = NULL; 555*1dedbd3bSFrançois Tigeot struct drm_mode_fb_dirty_cmd *r = data; 556*1dedbd3bSFrançois Tigeot struct drm_framebuffer *fb; 557*1dedbd3bSFrançois Tigeot unsigned flags; 558*1dedbd3bSFrançois Tigeot int num_clips; 559*1dedbd3bSFrançois Tigeot int ret; 560*1dedbd3bSFrançois Tigeot 561*1dedbd3bSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 562*1dedbd3bSFrançois Tigeot return -EINVAL; 563*1dedbd3bSFrançois Tigeot 564*1dedbd3bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, r->fb_id); 565*1dedbd3bSFrançois Tigeot if (!fb) 566*1dedbd3bSFrançois Tigeot return -ENOENT; 567*1dedbd3bSFrançois Tigeot 568*1dedbd3bSFrançois Tigeot num_clips = r->num_clips; 569*1dedbd3bSFrançois Tigeot clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; 570*1dedbd3bSFrançois Tigeot 571*1dedbd3bSFrançois Tigeot if (!num_clips != !clips_ptr) { 572*1dedbd3bSFrançois Tigeot ret = -EINVAL; 573*1dedbd3bSFrançois Tigeot goto out_err1; 574*1dedbd3bSFrançois Tigeot } 575*1dedbd3bSFrançois Tigeot 576*1dedbd3bSFrançois Tigeot flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags; 577*1dedbd3bSFrançois Tigeot 578*1dedbd3bSFrançois Tigeot /* If userspace annotates copy, clips must come in pairs */ 579*1dedbd3bSFrançois Tigeot if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) { 580*1dedbd3bSFrançois Tigeot ret = -EINVAL; 581*1dedbd3bSFrançois Tigeot goto out_err1; 582*1dedbd3bSFrançois Tigeot } 583*1dedbd3bSFrançois Tigeot 584*1dedbd3bSFrançois Tigeot if (num_clips && clips_ptr) { 585*1dedbd3bSFrançois Tigeot if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) { 586*1dedbd3bSFrançois Tigeot ret = -EINVAL; 587*1dedbd3bSFrançois Tigeot goto out_err1; 588*1dedbd3bSFrançois Tigeot } 589*1dedbd3bSFrançois Tigeot clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL); 590*1dedbd3bSFrançois Tigeot if (!clips) { 591*1dedbd3bSFrançois Tigeot ret = -ENOMEM; 592*1dedbd3bSFrançois Tigeot goto out_err1; 593*1dedbd3bSFrançois Tigeot } 594*1dedbd3bSFrançois Tigeot 595*1dedbd3bSFrançois Tigeot ret = copy_from_user(clips, clips_ptr, 596*1dedbd3bSFrançois Tigeot num_clips * sizeof(*clips)); 597*1dedbd3bSFrançois Tigeot if (ret) { 598*1dedbd3bSFrançois Tigeot ret = -EFAULT; 599*1dedbd3bSFrançois Tigeot goto out_err2; 600*1dedbd3bSFrançois Tigeot } 601*1dedbd3bSFrançois Tigeot } 602*1dedbd3bSFrançois Tigeot 603*1dedbd3bSFrançois Tigeot if (fb->funcs->dirty) { 604*1dedbd3bSFrançois Tigeot ret = fb->funcs->dirty(fb, file_priv, flags, r->color, 605*1dedbd3bSFrançois Tigeot clips, num_clips); 606*1dedbd3bSFrançois Tigeot } else { 607*1dedbd3bSFrançois Tigeot ret = -ENOSYS; 608*1dedbd3bSFrançois Tigeot } 609*1dedbd3bSFrançois Tigeot 610*1dedbd3bSFrançois Tigeot out_err2: 611*1dedbd3bSFrançois Tigeot kfree(clips); 612*1dedbd3bSFrançois Tigeot out_err1: 613*1dedbd3bSFrançois Tigeot drm_framebuffer_unreference(fb); 614*1dedbd3bSFrançois Tigeot 615*1dedbd3bSFrançois Tigeot return ret; 616*1dedbd3bSFrançois Tigeot } 617*1dedbd3bSFrançois Tigeot 618*1dedbd3bSFrançois Tigeot /** 619*1dedbd3bSFrançois Tigeot * drm_fb_release - remove and free the FBs on this file 620*1dedbd3bSFrançois Tigeot * @priv: drm file for the ioctl 621*1dedbd3bSFrançois Tigeot * 622*1dedbd3bSFrançois Tigeot * Destroy all the FBs associated with @filp. 623*1dedbd3bSFrançois Tigeot * 624*1dedbd3bSFrançois Tigeot * Called by the user via ioctl. 625*1dedbd3bSFrançois Tigeot * 626*1dedbd3bSFrançois Tigeot * Returns: 627*1dedbd3bSFrançois Tigeot * Zero on success, negative errno on failure. 628*1dedbd3bSFrançois Tigeot */ 629*1dedbd3bSFrançois Tigeot void drm_fb_release(struct drm_file *priv) 630*1dedbd3bSFrançois Tigeot { 631*1dedbd3bSFrançois Tigeot struct drm_framebuffer *fb, *tfb; 632*1dedbd3bSFrançois Tigeot struct drm_mode_rmfb_work arg; 633*1dedbd3bSFrançois Tigeot 634*1dedbd3bSFrançois Tigeot INIT_LIST_HEAD(&arg.fbs); 635*1dedbd3bSFrançois Tigeot 636*1dedbd3bSFrançois Tigeot /* 637*1dedbd3bSFrançois Tigeot * When the file gets released that means no one else can access the fb 638*1dedbd3bSFrançois Tigeot * list any more, so no need to grab fpriv->fbs_lock. And we need to 639*1dedbd3bSFrançois Tigeot * avoid upsetting lockdep since the universal cursor code adds a 640*1dedbd3bSFrançois Tigeot * framebuffer while holding mutex locks. 641*1dedbd3bSFrançois Tigeot * 642*1dedbd3bSFrançois Tigeot * Note that a real deadlock between fpriv->fbs_lock and the modeset 643*1dedbd3bSFrançois Tigeot * locks is impossible here since no one else but this function can get 644*1dedbd3bSFrançois Tigeot * at it any more. 645*1dedbd3bSFrançois Tigeot */ 646*1dedbd3bSFrançois Tigeot list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { 647*1dedbd3bSFrançois Tigeot if (drm_framebuffer_read_refcount(fb) > 1) { 648*1dedbd3bSFrançois Tigeot list_move_tail(&fb->filp_head, &arg.fbs); 649*1dedbd3bSFrançois Tigeot } else { 650*1dedbd3bSFrançois Tigeot list_del_init(&fb->filp_head); 651*1dedbd3bSFrançois Tigeot 652*1dedbd3bSFrançois Tigeot /* This drops the fpriv->fbs reference. */ 653*1dedbd3bSFrançois Tigeot drm_framebuffer_unreference(fb); 654*1dedbd3bSFrançois Tigeot } 655*1dedbd3bSFrançois Tigeot } 656*1dedbd3bSFrançois Tigeot 657*1dedbd3bSFrançois Tigeot if (!list_empty(&arg.fbs)) { 658*1dedbd3bSFrançois Tigeot INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn); 659*1dedbd3bSFrançois Tigeot 660*1dedbd3bSFrançois Tigeot schedule_work(&arg.work); 661*1dedbd3bSFrançois Tigeot flush_work(&arg.work); 662*1dedbd3bSFrançois Tigeot destroy_work_on_stack(&arg.work); 663*1dedbd3bSFrançois Tigeot } 664*1dedbd3bSFrançois Tigeot } 665*1dedbd3bSFrançois Tigeot 666*1dedbd3bSFrançois Tigeot void drm_framebuffer_free(struct kref *kref) 667*1dedbd3bSFrançois Tigeot { 668*1dedbd3bSFrançois Tigeot struct drm_framebuffer *fb = 669*1dedbd3bSFrançois Tigeot container_of(kref, struct drm_framebuffer, base.refcount); 670*1dedbd3bSFrançois Tigeot struct drm_device *dev = fb->dev; 671*1dedbd3bSFrançois Tigeot 672*1dedbd3bSFrançois Tigeot /* 673*1dedbd3bSFrançois Tigeot * The lookup idr holds a weak reference, which has not necessarily been 674*1dedbd3bSFrançois Tigeot * removed at this point. Check for that. 675*1dedbd3bSFrançois Tigeot */ 676*1dedbd3bSFrançois Tigeot drm_mode_object_unregister(dev, &fb->base); 677*1dedbd3bSFrançois Tigeot 678*1dedbd3bSFrançois Tigeot fb->funcs->destroy(fb); 679*1dedbd3bSFrançois Tigeot } 680*1dedbd3bSFrançois Tigeot 681*1dedbd3bSFrançois Tigeot /** 682*1dedbd3bSFrançois Tigeot * drm_framebuffer_init - initialize a framebuffer 683*1dedbd3bSFrançois Tigeot * @dev: DRM device 684*1dedbd3bSFrançois Tigeot * @fb: framebuffer to be initialized 685*1dedbd3bSFrançois Tigeot * @funcs: ... with these functions 686*1dedbd3bSFrançois Tigeot * 687*1dedbd3bSFrançois Tigeot * Allocates an ID for the framebuffer's parent mode object, sets its mode 688*1dedbd3bSFrançois Tigeot * functions & device file and adds it to the master fd list. 689*1dedbd3bSFrançois Tigeot * 690*1dedbd3bSFrançois Tigeot * IMPORTANT: 691*1dedbd3bSFrançois Tigeot * This functions publishes the fb and makes it available for concurrent access 692*1dedbd3bSFrançois Tigeot * by other users. Which means by this point the fb _must_ be fully set up - 693*1dedbd3bSFrançois Tigeot * since all the fb attributes are invariant over its lifetime, no further 694*1dedbd3bSFrançois Tigeot * locking but only correct reference counting is required. 695*1dedbd3bSFrançois Tigeot * 696*1dedbd3bSFrançois Tigeot * Returns: 697*1dedbd3bSFrançois Tigeot * Zero on success, error code on failure. 698*1dedbd3bSFrançois Tigeot */ 699*1dedbd3bSFrançois Tigeot int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, 700*1dedbd3bSFrançois Tigeot const struct drm_framebuffer_funcs *funcs) 701*1dedbd3bSFrançois Tigeot { 702*1dedbd3bSFrançois Tigeot int ret; 703*1dedbd3bSFrançois Tigeot 704*1dedbd3bSFrançois Tigeot INIT_LIST_HEAD(&fb->filp_head); 705*1dedbd3bSFrançois Tigeot fb->dev = dev; 706*1dedbd3bSFrançois Tigeot fb->funcs = funcs; 707*1dedbd3bSFrançois Tigeot 708*1dedbd3bSFrançois Tigeot ret = drm_mode_object_get_reg(dev, &fb->base, DRM_MODE_OBJECT_FB, 709*1dedbd3bSFrançois Tigeot false, drm_framebuffer_free); 710*1dedbd3bSFrançois Tigeot if (ret) 711*1dedbd3bSFrançois Tigeot goto out; 712*1dedbd3bSFrançois Tigeot 713*1dedbd3bSFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock); 714*1dedbd3bSFrançois Tigeot dev->mode_config.num_fb++; 715*1dedbd3bSFrançois Tigeot list_add(&fb->head, &dev->mode_config.fb_list); 716*1dedbd3bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 717*1dedbd3bSFrançois Tigeot 718*1dedbd3bSFrançois Tigeot drm_mode_object_register(dev, &fb->base); 719*1dedbd3bSFrançois Tigeot out: 720*1dedbd3bSFrançois Tigeot return ret; 721*1dedbd3bSFrançois Tigeot } 722*1dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_init); 723*1dedbd3bSFrançois Tigeot 724*1dedbd3bSFrançois Tigeot /** 725*1dedbd3bSFrançois Tigeot * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference 726*1dedbd3bSFrançois Tigeot * @dev: drm device 727*1dedbd3bSFrançois Tigeot * @id: id of the fb object 728*1dedbd3bSFrançois Tigeot * 729*1dedbd3bSFrançois Tigeot * If successful, this grabs an additional reference to the framebuffer - 730*1dedbd3bSFrançois Tigeot * callers need to make sure to eventually unreference the returned framebuffer 731*1dedbd3bSFrançois Tigeot * again, using @drm_framebuffer_unreference. 732*1dedbd3bSFrançois Tigeot */ 733*1dedbd3bSFrançois Tigeot struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, 734*1dedbd3bSFrançois Tigeot uint32_t id) 735*1dedbd3bSFrançois Tigeot { 736*1dedbd3bSFrançois Tigeot struct drm_mode_object *obj; 737*1dedbd3bSFrançois Tigeot struct drm_framebuffer *fb = NULL; 738*1dedbd3bSFrançois Tigeot 739*1dedbd3bSFrançois Tigeot obj = __drm_mode_object_find(dev, id, DRM_MODE_OBJECT_FB); 740*1dedbd3bSFrançois Tigeot if (obj) 741*1dedbd3bSFrançois Tigeot fb = obj_to_fb(obj); 742*1dedbd3bSFrançois Tigeot return fb; 743*1dedbd3bSFrançois Tigeot } 744*1dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_lookup); 745*1dedbd3bSFrançois Tigeot 746*1dedbd3bSFrançois Tigeot /** 747*1dedbd3bSFrançois Tigeot * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr 748*1dedbd3bSFrançois Tigeot * @fb: fb to unregister 749*1dedbd3bSFrançois Tigeot * 750*1dedbd3bSFrançois Tigeot * Drivers need to call this when cleaning up driver-private framebuffers, e.g. 751*1dedbd3bSFrançois Tigeot * those used for fbdev. Note that the caller must hold a reference of it's own, 752*1dedbd3bSFrançois Tigeot * i.e. the object may not be destroyed through this call (since it'll lead to a 753*1dedbd3bSFrançois Tigeot * locking inversion). 754*1dedbd3bSFrançois Tigeot */ 755*1dedbd3bSFrançois Tigeot void drm_framebuffer_unregister_private(struct drm_framebuffer *fb) 756*1dedbd3bSFrançois Tigeot { 757*1dedbd3bSFrançois Tigeot struct drm_device *dev; 758*1dedbd3bSFrançois Tigeot 759*1dedbd3bSFrançois Tigeot if (!fb) 760*1dedbd3bSFrançois Tigeot return; 761*1dedbd3bSFrançois Tigeot 762*1dedbd3bSFrançois Tigeot dev = fb->dev; 763*1dedbd3bSFrançois Tigeot 764*1dedbd3bSFrançois Tigeot /* Mark fb as reaped and drop idr ref. */ 765*1dedbd3bSFrançois Tigeot drm_mode_object_unregister(dev, &fb->base); 766*1dedbd3bSFrançois Tigeot } 767*1dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_unregister_private); 768*1dedbd3bSFrançois Tigeot 769*1dedbd3bSFrançois Tigeot /** 770*1dedbd3bSFrançois Tigeot * drm_framebuffer_cleanup - remove a framebuffer object 771*1dedbd3bSFrançois Tigeot * @fb: framebuffer to remove 772*1dedbd3bSFrançois Tigeot * 773*1dedbd3bSFrançois Tigeot * Cleanup framebuffer. This function is intended to be used from the drivers 774*1dedbd3bSFrançois Tigeot * ->destroy callback. It can also be used to clean up driver private 775*1dedbd3bSFrançois Tigeot * framebuffers embedded into a larger structure. 776*1dedbd3bSFrançois Tigeot * 777*1dedbd3bSFrançois Tigeot * Note that this function does not remove the fb from active usuage - if it is 778*1dedbd3bSFrançois Tigeot * still used anywhere, hilarity can ensue since userspace could call getfb on 779*1dedbd3bSFrançois Tigeot * the id and get back -EINVAL. Obviously no concern at driver unload time. 780*1dedbd3bSFrançois Tigeot * 781*1dedbd3bSFrançois Tigeot * Also, the framebuffer will not be removed from the lookup idr - for 782*1dedbd3bSFrançois Tigeot * user-created framebuffers this will happen in in the rmfb ioctl. For 783*1dedbd3bSFrançois Tigeot * driver-private objects (e.g. for fbdev) drivers need to explicitly call 784*1dedbd3bSFrançois Tigeot * drm_framebuffer_unregister_private. 785*1dedbd3bSFrançois Tigeot */ 786*1dedbd3bSFrançois Tigeot void drm_framebuffer_cleanup(struct drm_framebuffer *fb) 787*1dedbd3bSFrançois Tigeot { 788*1dedbd3bSFrançois Tigeot struct drm_device *dev = fb->dev; 789*1dedbd3bSFrançois Tigeot 790*1dedbd3bSFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock); 791*1dedbd3bSFrançois Tigeot list_del(&fb->head); 792*1dedbd3bSFrançois Tigeot dev->mode_config.num_fb--; 793*1dedbd3bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 794*1dedbd3bSFrançois Tigeot } 795*1dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_cleanup); 796*1dedbd3bSFrançois Tigeot 797*1dedbd3bSFrançois Tigeot /** 798*1dedbd3bSFrançois Tigeot * drm_framebuffer_remove - remove and unreference a framebuffer object 799*1dedbd3bSFrançois Tigeot * @fb: framebuffer to remove 800*1dedbd3bSFrançois Tigeot * 801*1dedbd3bSFrançois Tigeot * Scans all the CRTCs and planes in @dev's mode_config. If they're 802*1dedbd3bSFrançois Tigeot * using @fb, removes it, setting it to NULL. Then drops the reference to the 803*1dedbd3bSFrançois Tigeot * passed-in framebuffer. Might take the modeset locks. 804*1dedbd3bSFrançois Tigeot * 805*1dedbd3bSFrançois Tigeot * Note that this function optimizes the cleanup away if the caller holds the 806*1dedbd3bSFrançois Tigeot * last reference to the framebuffer. It is also guaranteed to not take the 807*1dedbd3bSFrançois Tigeot * modeset locks in this case. 808*1dedbd3bSFrançois Tigeot */ 809*1dedbd3bSFrançois Tigeot void drm_framebuffer_remove(struct drm_framebuffer *fb) 810*1dedbd3bSFrançois Tigeot { 811*1dedbd3bSFrançois Tigeot struct drm_device *dev; 812*1dedbd3bSFrançois Tigeot struct drm_crtc *crtc; 813*1dedbd3bSFrançois Tigeot struct drm_plane *plane; 814*1dedbd3bSFrançois Tigeot 815*1dedbd3bSFrançois Tigeot if (!fb) 816*1dedbd3bSFrançois Tigeot return; 817*1dedbd3bSFrançois Tigeot 818*1dedbd3bSFrançois Tigeot dev = fb->dev; 819*1dedbd3bSFrançois Tigeot 820*1dedbd3bSFrançois Tigeot WARN_ON(!list_empty(&fb->filp_head)); 821*1dedbd3bSFrançois Tigeot 822*1dedbd3bSFrançois Tigeot /* 823*1dedbd3bSFrançois Tigeot * drm ABI mandates that we remove any deleted framebuffers from active 824*1dedbd3bSFrançois Tigeot * useage. But since most sane clients only remove framebuffers they no 825*1dedbd3bSFrançois Tigeot * longer need, try to optimize this away. 826*1dedbd3bSFrançois Tigeot * 827*1dedbd3bSFrançois Tigeot * Since we're holding a reference ourselves, observing a refcount of 1 828*1dedbd3bSFrançois Tigeot * means that we're the last holder and can skip it. Also, the refcount 829*1dedbd3bSFrançois Tigeot * can never increase from 1 again, so we don't need any barriers or 830*1dedbd3bSFrançois Tigeot * locks. 831*1dedbd3bSFrançois Tigeot * 832*1dedbd3bSFrançois Tigeot * Note that userspace could try to race with use and instate a new 833*1dedbd3bSFrançois Tigeot * usage _after_ we've cleared all current ones. End result will be an 834*1dedbd3bSFrançois Tigeot * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot 835*1dedbd3bSFrançois Tigeot * in this manner. 836*1dedbd3bSFrançois Tigeot */ 837*1dedbd3bSFrançois Tigeot if (drm_framebuffer_read_refcount(fb) > 1) { 838*1dedbd3bSFrançois Tigeot drm_modeset_lock_all(dev); 839*1dedbd3bSFrançois Tigeot /* remove from any CRTC */ 840*1dedbd3bSFrançois Tigeot drm_for_each_crtc(crtc, dev) { 841*1dedbd3bSFrançois Tigeot if (crtc->primary->fb == fb) { 842*1dedbd3bSFrançois Tigeot /* should turn off the crtc */ 843*1dedbd3bSFrançois Tigeot if (drm_crtc_force_disable(crtc)) 844*1dedbd3bSFrançois Tigeot DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); 845*1dedbd3bSFrançois Tigeot } 846*1dedbd3bSFrançois Tigeot } 847*1dedbd3bSFrançois Tigeot 848*1dedbd3bSFrançois Tigeot drm_for_each_plane(plane, dev) { 849*1dedbd3bSFrançois Tigeot if (plane->fb == fb) 850*1dedbd3bSFrançois Tigeot drm_plane_force_disable(plane); 851*1dedbd3bSFrançois Tigeot } 852*1dedbd3bSFrançois Tigeot drm_modeset_unlock_all(dev); 853*1dedbd3bSFrançois Tigeot } 854*1dedbd3bSFrançois Tigeot 855*1dedbd3bSFrançois Tigeot drm_framebuffer_unreference(fb); 856*1dedbd3bSFrançois Tigeot } 857*1dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_remove); 858