11dedbd3bSFrançois Tigeot /*
21dedbd3bSFrançois Tigeot * Copyright (c) 2016 Intel Corporation
31dedbd3bSFrançois Tigeot *
41dedbd3bSFrançois Tigeot * Permission to use, copy, modify, distribute, and sell this software and its
51dedbd3bSFrançois Tigeot * documentation for any purpose is hereby granted without fee, provided that
61dedbd3bSFrançois Tigeot * the above copyright notice appear in all copies and that both that copyright
71dedbd3bSFrançois Tigeot * notice and this permission notice appear in supporting documentation, and
81dedbd3bSFrançois Tigeot * that the name of the copyright holders not be used in advertising or
91dedbd3bSFrançois Tigeot * publicity pertaining to distribution of the software without specific,
101dedbd3bSFrançois Tigeot * written prior permission. The copyright holders make no representations
111dedbd3bSFrançois Tigeot * about the suitability of this software for any purpose. It is provided "as
121dedbd3bSFrançois Tigeot * is" without express or implied warranty.
131dedbd3bSFrançois Tigeot *
141dedbd3bSFrançois Tigeot * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
151dedbd3bSFrançois Tigeot * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
161dedbd3bSFrançois Tigeot * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
171dedbd3bSFrançois Tigeot * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
181dedbd3bSFrançois Tigeot * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
191dedbd3bSFrançois Tigeot * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
201dedbd3bSFrançois Tigeot * OF THIS SOFTWARE.
211dedbd3bSFrançois Tigeot */
221dedbd3bSFrançois Tigeot
231dedbd3bSFrançois Tigeot #include <linux/export.h>
241dedbd3bSFrançois Tigeot #include <drm/drmP.h>
251dedbd3bSFrançois Tigeot #include <drm/drm_auth.h>
261dedbd3bSFrançois Tigeot #include <drm/drm_framebuffer.h>
27a85cb24fSFrançois Tigeot #include <drm/drm_atomic.h>
281dedbd3bSFrançois Tigeot
291dedbd3bSFrançois Tigeot #include "drm_crtc_internal.h"
301dedbd3bSFrançois Tigeot
311dedbd3bSFrançois Tigeot /**
321dedbd3bSFrançois Tigeot * DOC: overview
331dedbd3bSFrançois Tigeot *
341dedbd3bSFrançois Tigeot * Frame buffers are abstract memory objects that provide a source of pixels to
351dedbd3bSFrançois Tigeot * scanout to a CRTC. Applications explicitly request the creation of frame
361dedbd3bSFrançois Tigeot * buffers through the DRM_IOCTL_MODE_ADDFB(2) ioctls and receive an opaque
371dedbd3bSFrançois Tigeot * handle that can be passed to the KMS CRTC control, plane configuration and
381dedbd3bSFrançois Tigeot * page flip functions.
391dedbd3bSFrançois Tigeot *
401dedbd3bSFrançois Tigeot * Frame buffers rely on the underlying memory manager for allocating backing
411dedbd3bSFrançois Tigeot * storage. When creating a frame buffer applications pass a memory handle
421dedbd3bSFrançois Tigeot * (or a list of memory handles for multi-planar formats) through the
43*3f2dd94aSFrançois Tigeot * &struct drm_mode_fb_cmd2 argument. For drivers using GEM as their userspace
441dedbd3bSFrançois Tigeot * buffer management interface this would be a GEM handle. Drivers are however
451dedbd3bSFrançois Tigeot * free to use their own backing storage object handles, e.g. vmwgfx directly
461dedbd3bSFrançois Tigeot * exposes special TTM handles to userspace and so expects TTM handles in the
471dedbd3bSFrançois Tigeot * create ioctl and not GEM handles.
481dedbd3bSFrançois Tigeot *
49*3f2dd94aSFrançois Tigeot * Framebuffers are tracked with &struct drm_framebuffer. They are published
501dedbd3bSFrançois Tigeot * using drm_framebuffer_init() - after calling that function userspace can use
511dedbd3bSFrançois Tigeot * and access the framebuffer object. The helper function
521dedbd3bSFrançois Tigeot * drm_helper_mode_fill_fb_struct() can be used to pre-fill the required
531dedbd3bSFrançois Tigeot * metadata fields.
541dedbd3bSFrançois Tigeot *
551dedbd3bSFrançois Tigeot * The lifetime of a drm framebuffer is controlled with a reference count,
56*3f2dd94aSFrançois Tigeot * drivers can grab additional references with drm_framebuffer_get() and drop
57*3f2dd94aSFrançois Tigeot * them again with drm_framebuffer_put(). For driver-private framebuffers for
58*3f2dd94aSFrançois Tigeot * which the last reference is never dropped (e.g. for the fbdev framebuffer
59*3f2dd94aSFrançois Tigeot * when the struct &struct drm_framebuffer is embedded into the fbdev helper
60*3f2dd94aSFrançois Tigeot * struct) drivers can manually clean up a framebuffer at module unload time
61*3f2dd94aSFrançois Tigeot * with drm_framebuffer_unregister_private(). But doing this is not
62*3f2dd94aSFrançois Tigeot * recommended, and it's better to have a normal free-standing &struct
63a85cb24fSFrançois Tigeot * drm_framebuffer.
641dedbd3bSFrançois Tigeot */
651dedbd3bSFrançois Tigeot
drm_framebuffer_check_src_coords(uint32_t src_x,uint32_t src_y,uint32_t src_w,uint32_t src_h,const struct drm_framebuffer * fb)661dedbd3bSFrançois Tigeot int drm_framebuffer_check_src_coords(uint32_t src_x, uint32_t src_y,
671dedbd3bSFrançois Tigeot uint32_t src_w, uint32_t src_h,
681dedbd3bSFrançois Tigeot const struct drm_framebuffer *fb)
691dedbd3bSFrançois Tigeot {
701dedbd3bSFrançois Tigeot unsigned int fb_width, fb_height;
711dedbd3bSFrançois Tigeot
721dedbd3bSFrançois Tigeot fb_width = fb->width << 16;
731dedbd3bSFrançois Tigeot fb_height = fb->height << 16;
741dedbd3bSFrançois Tigeot
751dedbd3bSFrançois Tigeot /* Make sure source coordinates are inside the fb. */
761dedbd3bSFrançois Tigeot if (src_w > fb_width ||
771dedbd3bSFrançois Tigeot src_x > fb_width - src_w ||
781dedbd3bSFrançois Tigeot src_h > fb_height ||
791dedbd3bSFrançois Tigeot src_y > fb_height - src_h) {
801dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("Invalid source coordinates "
811dedbd3bSFrançois Tigeot "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
821dedbd3bSFrançois Tigeot src_w >> 16, ((src_w & 0xffff) * 15625) >> 10,
831dedbd3bSFrançois Tigeot src_h >> 16, ((src_h & 0xffff) * 15625) >> 10,
841dedbd3bSFrançois Tigeot src_x >> 16, ((src_x & 0xffff) * 15625) >> 10,
851dedbd3bSFrançois Tigeot src_y >> 16, ((src_y & 0xffff) * 15625) >> 10);
861dedbd3bSFrançois Tigeot return -ENOSPC;
871dedbd3bSFrançois Tigeot }
881dedbd3bSFrançois Tigeot
891dedbd3bSFrançois Tigeot return 0;
901dedbd3bSFrançois Tigeot }
911dedbd3bSFrançois Tigeot
921dedbd3bSFrançois Tigeot /**
931dedbd3bSFrançois Tigeot * drm_mode_addfb - add an FB to the graphics configuration
941dedbd3bSFrançois Tigeot * @dev: drm device for the ioctl
951dedbd3bSFrançois Tigeot * @data: data pointer for the ioctl
961dedbd3bSFrançois Tigeot * @file_priv: drm file for the ioctl call
971dedbd3bSFrançois Tigeot *
981dedbd3bSFrançois Tigeot * Add a new FB to the specified CRTC, given a user request. This is the
991dedbd3bSFrançois Tigeot * original addfb ioctl which only supported RGB formats.
1001dedbd3bSFrançois Tigeot *
1011dedbd3bSFrançois Tigeot * Called by the user via ioctl.
1021dedbd3bSFrançois Tigeot *
1031dedbd3bSFrançois Tigeot * Returns:
1041dedbd3bSFrançois Tigeot * Zero on success, negative errno on failure.
1051dedbd3bSFrançois Tigeot */
drm_mode_addfb(struct drm_device * dev,void * data,struct drm_file * file_priv)1061dedbd3bSFrançois Tigeot int drm_mode_addfb(struct drm_device *dev,
1071dedbd3bSFrançois Tigeot void *data, struct drm_file *file_priv)
1081dedbd3bSFrançois Tigeot {
1091dedbd3bSFrançois Tigeot struct drm_mode_fb_cmd *or = data;
1101dedbd3bSFrançois Tigeot struct drm_mode_fb_cmd2 r = {};
1111dedbd3bSFrançois Tigeot int ret;
1121dedbd3bSFrançois Tigeot
1131dedbd3bSFrançois Tigeot /* convert to new format and call new ioctl */
1141dedbd3bSFrançois Tigeot r.fb_id = or->fb_id;
1151dedbd3bSFrançois Tigeot r.width = or->width;
1161dedbd3bSFrançois Tigeot r.height = or->height;
1171dedbd3bSFrançois Tigeot r.pitches[0] = or->pitch;
1181dedbd3bSFrançois Tigeot r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth);
1191dedbd3bSFrançois Tigeot r.handles[0] = or->handle;
1201dedbd3bSFrançois Tigeot
121*3f2dd94aSFrançois Tigeot if (r.pixel_format == DRM_FORMAT_XRGB2101010 &&
122*3f2dd94aSFrançois Tigeot dev->driver->driver_features & DRIVER_PREFER_XBGR_30BPP)
123*3f2dd94aSFrançois Tigeot r.pixel_format = DRM_FORMAT_XBGR2101010;
124*3f2dd94aSFrançois Tigeot
1251dedbd3bSFrançois Tigeot ret = drm_mode_addfb2(dev, &r, file_priv);
1261dedbd3bSFrançois Tigeot if (ret)
1271dedbd3bSFrançois Tigeot return ret;
1281dedbd3bSFrançois Tigeot
1291dedbd3bSFrançois Tigeot or->fb_id = r.fb_id;
1301dedbd3bSFrançois Tigeot
1311dedbd3bSFrançois Tigeot return 0;
1321dedbd3bSFrançois Tigeot }
1331dedbd3bSFrançois Tigeot
fb_plane_width(int width,const struct drm_format_info * format,int plane)134a85cb24fSFrançois Tigeot static int fb_plane_width(int width,
135a85cb24fSFrançois Tigeot const struct drm_format_info *format, int plane)
136a85cb24fSFrançois Tigeot {
137a85cb24fSFrançois Tigeot if (plane == 0)
138a85cb24fSFrançois Tigeot return width;
139a85cb24fSFrançois Tigeot
140a85cb24fSFrançois Tigeot return DIV_ROUND_UP(width, format->hsub);
141a85cb24fSFrançois Tigeot }
142a85cb24fSFrançois Tigeot
fb_plane_height(int height,const struct drm_format_info * format,int plane)143a85cb24fSFrançois Tigeot static int fb_plane_height(int height,
144a85cb24fSFrançois Tigeot const struct drm_format_info *format, int plane)
145a85cb24fSFrançois Tigeot {
146a85cb24fSFrançois Tigeot if (plane == 0)
147a85cb24fSFrançois Tigeot return height;
148a85cb24fSFrançois Tigeot
149a85cb24fSFrançois Tigeot return DIV_ROUND_UP(height, format->vsub);
150a85cb24fSFrançois Tigeot }
151a85cb24fSFrançois Tigeot
framebuffer_check(struct drm_device * dev,const struct drm_mode_fb_cmd2 * r)152a85cb24fSFrançois Tigeot static int framebuffer_check(struct drm_device *dev,
153a85cb24fSFrançois Tigeot const struct drm_mode_fb_cmd2 *r)
1541dedbd3bSFrançois Tigeot {
1554be47400SFrançois Tigeot const struct drm_format_info *info;
1564be47400SFrançois Tigeot int i;
1571dedbd3bSFrançois Tigeot
158a85cb24fSFrançois Tigeot /* check if the format is supported at all */
1594be47400SFrançois Tigeot info = __drm_format_info(r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN);
1604be47400SFrançois Tigeot if (!info) {
1614be47400SFrançois Tigeot struct drm_format_name_buf format_name;
1624be47400SFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer format %s\n",
1634be47400SFrançois Tigeot drm_get_format_name(r->pixel_format,
1644be47400SFrançois Tigeot &format_name));
1654be47400SFrançois Tigeot return -EINVAL;
1661dedbd3bSFrançois Tigeot }
1671dedbd3bSFrançois Tigeot
168a85cb24fSFrançois Tigeot /* now let the driver pick its own format info */
169a85cb24fSFrançois Tigeot info = drm_get_format_info(dev, r);
170a85cb24fSFrançois Tigeot
171a85cb24fSFrançois Tigeot if (r->width == 0) {
1721dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width);
1731dedbd3bSFrançois Tigeot return -EINVAL;
1741dedbd3bSFrançois Tigeot }
1751dedbd3bSFrançois Tigeot
176a85cb24fSFrançois Tigeot if (r->height == 0) {
1771dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height);
1781dedbd3bSFrançois Tigeot return -EINVAL;
1791dedbd3bSFrançois Tigeot }
1801dedbd3bSFrançois Tigeot
1814be47400SFrançois Tigeot for (i = 0; i < info->num_planes; i++) {
182a85cb24fSFrançois Tigeot unsigned int width = fb_plane_width(r->width, info, i);
183a85cb24fSFrançois Tigeot unsigned int height = fb_plane_height(r->height, info, i);
1844be47400SFrançois Tigeot unsigned int cpp = info->cpp[i];
1851dedbd3bSFrançois Tigeot
1861dedbd3bSFrançois Tigeot if (!r->handles[i]) {
1871dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i);
1881dedbd3bSFrançois Tigeot return -EINVAL;
1891dedbd3bSFrançois Tigeot }
1901dedbd3bSFrançois Tigeot
1911dedbd3bSFrançois Tigeot if ((uint64_t) width * cpp > UINT_MAX)
1921dedbd3bSFrançois Tigeot return -ERANGE;
1931dedbd3bSFrançois Tigeot
1941dedbd3bSFrançois Tigeot if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX)
1951dedbd3bSFrançois Tigeot return -ERANGE;
1961dedbd3bSFrançois Tigeot
1971dedbd3bSFrançois Tigeot if (r->pitches[i] < width * cpp) {
1981dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i);
1991dedbd3bSFrançois Tigeot return -EINVAL;
2001dedbd3bSFrançois Tigeot }
2011dedbd3bSFrançois Tigeot
2021dedbd3bSFrançois Tigeot if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) {
2031dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n",
2041dedbd3bSFrançois Tigeot r->modifier[i], i);
2051dedbd3bSFrançois Tigeot return -EINVAL;
2061dedbd3bSFrançois Tigeot }
2071dedbd3bSFrançois Tigeot
2084be47400SFrançois Tigeot if (r->flags & DRM_MODE_FB_MODIFIERS &&
2094be47400SFrançois Tigeot r->modifier[i] != r->modifier[0]) {
2104be47400SFrançois Tigeot DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n",
2114be47400SFrançois Tigeot r->modifier[i], i);
2124be47400SFrançois Tigeot return -EINVAL;
2134be47400SFrançois Tigeot }
2144be47400SFrançois Tigeot
2151dedbd3bSFrançois Tigeot /* modifier specific checks: */
2161dedbd3bSFrançois Tigeot switch (r->modifier[i]) {
2171dedbd3bSFrançois Tigeot case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE:
2181dedbd3bSFrançois Tigeot /* NOTE: the pitch restriction may be lifted later if it turns
2191dedbd3bSFrançois Tigeot * out that no hw has this restriction:
2201dedbd3bSFrançois Tigeot */
2211dedbd3bSFrançois Tigeot if (r->pixel_format != DRM_FORMAT_NV12 ||
2221dedbd3bSFrançois Tigeot width % 128 || height % 32 ||
2231dedbd3bSFrançois Tigeot r->pitches[i] % 128) {
2241dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad modifier data for plane %d\n", i);
2251dedbd3bSFrançois Tigeot return -EINVAL;
2261dedbd3bSFrançois Tigeot }
2271dedbd3bSFrançois Tigeot break;
2281dedbd3bSFrançois Tigeot
2291dedbd3bSFrançois Tigeot default:
2301dedbd3bSFrançois Tigeot break;
2311dedbd3bSFrançois Tigeot }
2321dedbd3bSFrançois Tigeot }
2331dedbd3bSFrançois Tigeot
2344be47400SFrançois Tigeot for (i = info->num_planes; i < 4; i++) {
2351dedbd3bSFrançois Tigeot if (r->modifier[i]) {
2361dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("non-zero modifier for unused plane %d\n", i);
2371dedbd3bSFrançois Tigeot return -EINVAL;
2381dedbd3bSFrançois Tigeot }
2391dedbd3bSFrançois Tigeot
2401dedbd3bSFrançois Tigeot /* Pre-FB_MODIFIERS userspace didn't clear the structs properly. */
2411dedbd3bSFrançois Tigeot if (!(r->flags & DRM_MODE_FB_MODIFIERS))
2421dedbd3bSFrançois Tigeot continue;
2431dedbd3bSFrançois Tigeot
2441dedbd3bSFrançois Tigeot if (r->handles[i]) {
2451dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("buffer object handle for unused plane %d\n", i);
2461dedbd3bSFrançois Tigeot return -EINVAL;
2471dedbd3bSFrançois Tigeot }
2481dedbd3bSFrançois Tigeot
2491dedbd3bSFrançois Tigeot if (r->pitches[i]) {
2501dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("non-zero pitch for unused plane %d\n", i);
2511dedbd3bSFrançois Tigeot return -EINVAL;
2521dedbd3bSFrançois Tigeot }
2531dedbd3bSFrançois Tigeot
2541dedbd3bSFrançois Tigeot if (r->offsets[i]) {
2551dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("non-zero offset for unused plane %d\n", i);
2561dedbd3bSFrançois Tigeot return -EINVAL;
2571dedbd3bSFrançois Tigeot }
2581dedbd3bSFrançois Tigeot }
2591dedbd3bSFrançois Tigeot
2601dedbd3bSFrançois Tigeot return 0;
2611dedbd3bSFrançois Tigeot }
2621dedbd3bSFrançois Tigeot
2631dedbd3bSFrançois Tigeot struct drm_framebuffer *
drm_internal_framebuffer_create(struct drm_device * dev,const struct drm_mode_fb_cmd2 * r,struct drm_file * file_priv)2641dedbd3bSFrançois Tigeot drm_internal_framebuffer_create(struct drm_device *dev,
2651dedbd3bSFrançois Tigeot const struct drm_mode_fb_cmd2 *r,
2661dedbd3bSFrançois Tigeot struct drm_file *file_priv)
2671dedbd3bSFrançois Tigeot {
2681dedbd3bSFrançois Tigeot struct drm_mode_config *config = &dev->mode_config;
2691dedbd3bSFrançois Tigeot struct drm_framebuffer *fb;
2701dedbd3bSFrançois Tigeot int ret;
2711dedbd3bSFrançois Tigeot
2721dedbd3bSFrançois Tigeot if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) {
2731dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags);
2741dedbd3bSFrançois Tigeot return ERR_PTR(-EINVAL);
2751dedbd3bSFrançois Tigeot }
2761dedbd3bSFrançois Tigeot
2771dedbd3bSFrançois Tigeot if ((config->min_width > r->width) || (r->width > config->max_width)) {
2781dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n",
2791dedbd3bSFrançois Tigeot r->width, config->min_width, config->max_width);
2801dedbd3bSFrançois Tigeot return ERR_PTR(-EINVAL);
2811dedbd3bSFrançois Tigeot }
2821dedbd3bSFrançois Tigeot if ((config->min_height > r->height) || (r->height > config->max_height)) {
2831dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n",
2841dedbd3bSFrançois Tigeot r->height, config->min_height, config->max_height);
2851dedbd3bSFrançois Tigeot return ERR_PTR(-EINVAL);
2861dedbd3bSFrançois Tigeot }
2871dedbd3bSFrançois Tigeot
2881dedbd3bSFrançois Tigeot if (r->flags & DRM_MODE_FB_MODIFIERS &&
2891dedbd3bSFrançois Tigeot !dev->mode_config.allow_fb_modifiers) {
2901dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("driver does not support fb modifiers\n");
2911dedbd3bSFrançois Tigeot return ERR_PTR(-EINVAL);
2921dedbd3bSFrançois Tigeot }
2931dedbd3bSFrançois Tigeot
294a85cb24fSFrançois Tigeot ret = framebuffer_check(dev, r);
2951dedbd3bSFrançois Tigeot if (ret)
2961dedbd3bSFrançois Tigeot return ERR_PTR(ret);
2971dedbd3bSFrançois Tigeot
2981dedbd3bSFrançois Tigeot fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
2991dedbd3bSFrançois Tigeot if (IS_ERR(fb)) {
3001dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("could not create framebuffer\n");
3011dedbd3bSFrançois Tigeot return fb;
3021dedbd3bSFrançois Tigeot }
3031dedbd3bSFrançois Tigeot
3041dedbd3bSFrançois Tigeot return fb;
3051dedbd3bSFrançois Tigeot }
3061dedbd3bSFrançois Tigeot
3071dedbd3bSFrançois Tigeot /**
3081dedbd3bSFrançois Tigeot * drm_mode_addfb2 - add an FB to the graphics configuration
3091dedbd3bSFrançois Tigeot * @dev: drm device for the ioctl
3101dedbd3bSFrançois Tigeot * @data: data pointer for the ioctl
3111dedbd3bSFrançois Tigeot * @file_priv: drm file for the ioctl call
3121dedbd3bSFrançois Tigeot *
3131dedbd3bSFrançois Tigeot * Add a new FB to the specified CRTC, given a user request with format. This is
3141dedbd3bSFrançois Tigeot * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers
3151dedbd3bSFrançois Tigeot * and uses fourcc codes as pixel format specifiers.
3161dedbd3bSFrançois Tigeot *
3171dedbd3bSFrançois Tigeot * Called by the user via ioctl.
3181dedbd3bSFrançois Tigeot *
3191dedbd3bSFrançois Tigeot * Returns:
3201dedbd3bSFrançois Tigeot * Zero on success, negative errno on failure.
3211dedbd3bSFrançois Tigeot */
drm_mode_addfb2(struct drm_device * dev,void * data,struct drm_file * file_priv)3221dedbd3bSFrançois Tigeot int drm_mode_addfb2(struct drm_device *dev,
3231dedbd3bSFrançois Tigeot void *data, struct drm_file *file_priv)
3241dedbd3bSFrançois Tigeot {
3251dedbd3bSFrançois Tigeot struct drm_mode_fb_cmd2 *r = data;
3261dedbd3bSFrançois Tigeot struct drm_framebuffer *fb;
3271dedbd3bSFrançois Tigeot
3281dedbd3bSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET))
3291dedbd3bSFrançois Tigeot return -EINVAL;
3301dedbd3bSFrançois Tigeot
3311dedbd3bSFrançois Tigeot fb = drm_internal_framebuffer_create(dev, r, file_priv);
3321dedbd3bSFrançois Tigeot if (IS_ERR(fb))
3331dedbd3bSFrançois Tigeot return PTR_ERR(fb);
3341dedbd3bSFrançois Tigeot
3351dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
3361dedbd3bSFrançois Tigeot r->fb_id = fb->base.id;
3371dedbd3bSFrançois Tigeot
3381dedbd3bSFrançois Tigeot /* Transfer ownership to the filp for reaping on close */
3391dedbd3bSFrançois Tigeot mutex_lock(&file_priv->fbs_lock);
3401dedbd3bSFrançois Tigeot list_add(&fb->filp_head, &file_priv->fbs);
3411dedbd3bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock);
3421dedbd3bSFrançois Tigeot
3431dedbd3bSFrançois Tigeot return 0;
3441dedbd3bSFrançois Tigeot }
3451dedbd3bSFrançois Tigeot
3461dedbd3bSFrançois Tigeot struct drm_mode_rmfb_work {
3471dedbd3bSFrançois Tigeot struct work_struct work;
3481dedbd3bSFrançois Tigeot struct list_head fbs;
3491dedbd3bSFrançois Tigeot };
3501dedbd3bSFrançois Tigeot
drm_mode_rmfb_work_fn(struct work_struct * w)3511dedbd3bSFrançois Tigeot static void drm_mode_rmfb_work_fn(struct work_struct *w)
3521dedbd3bSFrançois Tigeot {
3531dedbd3bSFrançois Tigeot struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work);
3541dedbd3bSFrançois Tigeot
3551dedbd3bSFrançois Tigeot while (!list_empty(&arg->fbs)) {
3561dedbd3bSFrançois Tigeot struct drm_framebuffer *fb =
3571dedbd3bSFrançois Tigeot list_first_entry(&arg->fbs, typeof(*fb), filp_head);
3581dedbd3bSFrançois Tigeot
3591dedbd3bSFrançois Tigeot list_del_init(&fb->filp_head);
3601dedbd3bSFrançois Tigeot drm_framebuffer_remove(fb);
3611dedbd3bSFrançois Tigeot }
3621dedbd3bSFrançois Tigeot }
3631dedbd3bSFrançois Tigeot
3641dedbd3bSFrançois Tigeot /**
3651dedbd3bSFrançois Tigeot * drm_mode_rmfb - remove an FB from the configuration
3661dedbd3bSFrançois Tigeot * @dev: drm device for the ioctl
3671dedbd3bSFrançois Tigeot * @data: data pointer for the ioctl
3681dedbd3bSFrançois Tigeot * @file_priv: drm file for the ioctl call
3691dedbd3bSFrançois Tigeot *
3701dedbd3bSFrançois Tigeot * Remove the FB specified by the user.
3711dedbd3bSFrançois Tigeot *
3721dedbd3bSFrançois Tigeot * Called by the user via ioctl.
3731dedbd3bSFrançois Tigeot *
3741dedbd3bSFrançois Tigeot * Returns:
3751dedbd3bSFrançois Tigeot * Zero on success, negative errno on failure.
3761dedbd3bSFrançois Tigeot */
drm_mode_rmfb(struct drm_device * dev,void * data,struct drm_file * file_priv)3771dedbd3bSFrançois Tigeot int drm_mode_rmfb(struct drm_device *dev,
3781dedbd3bSFrançois Tigeot void *data, struct drm_file *file_priv)
3791dedbd3bSFrançois Tigeot {
3801dedbd3bSFrançois Tigeot struct drm_framebuffer *fb = NULL;
3811dedbd3bSFrançois Tigeot struct drm_framebuffer *fbl = NULL;
3821dedbd3bSFrançois Tigeot uint32_t *id = data;
3831dedbd3bSFrançois Tigeot int found = 0;
3841dedbd3bSFrançois Tigeot
3851dedbd3bSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET))
3861dedbd3bSFrançois Tigeot return -EINVAL;
3871dedbd3bSFrançois Tigeot
388*3f2dd94aSFrançois Tigeot fb = drm_framebuffer_lookup(dev, file_priv, *id);
3891dedbd3bSFrançois Tigeot if (!fb)
3901dedbd3bSFrançois Tigeot return -ENOENT;
3911dedbd3bSFrançois Tigeot
3921dedbd3bSFrançois Tigeot mutex_lock(&file_priv->fbs_lock);
3931dedbd3bSFrançois Tigeot list_for_each_entry(fbl, &file_priv->fbs, filp_head)
3941dedbd3bSFrançois Tigeot if (fb == fbl)
3951dedbd3bSFrançois Tigeot found = 1;
3961dedbd3bSFrançois Tigeot if (!found) {
3971dedbd3bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock);
3981dedbd3bSFrançois Tigeot goto fail_unref;
3991dedbd3bSFrançois Tigeot }
4001dedbd3bSFrançois Tigeot
4011dedbd3bSFrançois Tigeot list_del_init(&fb->filp_head);
4021dedbd3bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock);
4031dedbd3bSFrançois Tigeot
4041dedbd3bSFrançois Tigeot /* drop the reference we picked up in framebuffer lookup */
405a85cb24fSFrançois Tigeot drm_framebuffer_put(fb);
4061dedbd3bSFrançois Tigeot
4071dedbd3bSFrançois Tigeot /*
4081dedbd3bSFrançois Tigeot * we now own the reference that was stored in the fbs list
4091dedbd3bSFrançois Tigeot *
4101dedbd3bSFrançois Tigeot * drm_framebuffer_remove may fail with -EINTR on pending signals,
4111dedbd3bSFrançois Tigeot * so run this in a separate stack as there's no way to correctly
4121dedbd3bSFrançois Tigeot * handle this after the fb is already removed from the lookup table.
4131dedbd3bSFrançois Tigeot */
4141dedbd3bSFrançois Tigeot if (drm_framebuffer_read_refcount(fb) > 1) {
4151dedbd3bSFrançois Tigeot struct drm_mode_rmfb_work arg;
4161dedbd3bSFrançois Tigeot
4171dedbd3bSFrançois Tigeot INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
4181dedbd3bSFrançois Tigeot INIT_LIST_HEAD(&arg.fbs);
4191dedbd3bSFrançois Tigeot list_add_tail(&fb->filp_head, &arg.fbs);
4201dedbd3bSFrançois Tigeot
4211dedbd3bSFrançois Tigeot schedule_work(&arg.work);
4221dedbd3bSFrançois Tigeot flush_work(&arg.work);
4231dedbd3bSFrançois Tigeot destroy_work_on_stack(&arg.work);
4241dedbd3bSFrançois Tigeot } else
425a85cb24fSFrançois Tigeot drm_framebuffer_put(fb);
4261dedbd3bSFrançois Tigeot
4271dedbd3bSFrançois Tigeot return 0;
4281dedbd3bSFrançois Tigeot
4291dedbd3bSFrançois Tigeot fail_unref:
430a85cb24fSFrançois Tigeot drm_framebuffer_put(fb);
4311dedbd3bSFrançois Tigeot return -ENOENT;
4321dedbd3bSFrançois Tigeot }
4331dedbd3bSFrançois Tigeot
4341dedbd3bSFrançois Tigeot /**
4351dedbd3bSFrançois Tigeot * drm_mode_getfb - get FB info
4361dedbd3bSFrançois Tigeot * @dev: drm device for the ioctl
4371dedbd3bSFrançois Tigeot * @data: data pointer for the ioctl
4381dedbd3bSFrançois Tigeot * @file_priv: drm file for the ioctl call
4391dedbd3bSFrançois Tigeot *
4401dedbd3bSFrançois Tigeot * Lookup the FB given its ID and return info about it.
4411dedbd3bSFrançois Tigeot *
4421dedbd3bSFrançois Tigeot * Called by the user via ioctl.
4431dedbd3bSFrançois Tigeot *
4441dedbd3bSFrançois Tigeot * Returns:
4451dedbd3bSFrançois Tigeot * Zero on success, negative errno on failure.
4461dedbd3bSFrançois Tigeot */
drm_mode_getfb(struct drm_device * dev,void * data,struct drm_file * file_priv)4471dedbd3bSFrançois Tigeot int drm_mode_getfb(struct drm_device *dev,
4481dedbd3bSFrançois Tigeot void *data, struct drm_file *file_priv)
4491dedbd3bSFrançois Tigeot {
4501dedbd3bSFrançois Tigeot struct drm_mode_fb_cmd *r = data;
4511dedbd3bSFrançois Tigeot struct drm_framebuffer *fb;
4521dedbd3bSFrançois Tigeot int ret;
4531dedbd3bSFrançois Tigeot
4541dedbd3bSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET))
4551dedbd3bSFrançois Tigeot return -EINVAL;
4561dedbd3bSFrançois Tigeot
457*3f2dd94aSFrançois Tigeot fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id);
4581dedbd3bSFrançois Tigeot if (!fb)
4591dedbd3bSFrançois Tigeot return -ENOENT;
4601dedbd3bSFrançois Tigeot
461*3f2dd94aSFrançois Tigeot /* Multi-planar framebuffers need getfb2. */
462*3f2dd94aSFrançois Tigeot if (fb->format->num_planes > 1) {
463*3f2dd94aSFrançois Tigeot ret = -EINVAL;
464*3f2dd94aSFrançois Tigeot goto out;
465*3f2dd94aSFrançois Tigeot }
466*3f2dd94aSFrançois Tigeot
4671dedbd3bSFrançois Tigeot r->height = fb->height;
4681dedbd3bSFrançois Tigeot r->width = fb->width;
469a85cb24fSFrançois Tigeot r->depth = fb->format->depth;
470a85cb24fSFrançois Tigeot r->bpp = fb->format->cpp[0] * 8;
4711dedbd3bSFrançois Tigeot r->pitch = fb->pitches[0];
4721dedbd3bSFrançois Tigeot if (fb->funcs->create_handle) {
4731dedbd3bSFrançois Tigeot if (drm_is_current_master(file_priv) || capable(CAP_SYS_ADMIN) ||
4741dedbd3bSFrançois Tigeot drm_is_control_client(file_priv)) {
4751dedbd3bSFrançois Tigeot ret = fb->funcs->create_handle(fb, file_priv,
4761dedbd3bSFrançois Tigeot &r->handle);
4771dedbd3bSFrançois Tigeot } else {
4781dedbd3bSFrançois Tigeot /* GET_FB() is an unprivileged ioctl so we must not
4791dedbd3bSFrançois Tigeot * return a buffer-handle to non-master processes! For
4801dedbd3bSFrançois Tigeot * backwards-compatibility reasons, we cannot make
4811dedbd3bSFrançois Tigeot * GET_FB() privileged, so just return an invalid handle
4821dedbd3bSFrançois Tigeot * for non-masters. */
4831dedbd3bSFrançois Tigeot r->handle = 0;
4841dedbd3bSFrançois Tigeot ret = 0;
4851dedbd3bSFrançois Tigeot }
4861dedbd3bSFrançois Tigeot } else {
4871dedbd3bSFrançois Tigeot ret = -ENODEV;
4881dedbd3bSFrançois Tigeot }
4891dedbd3bSFrançois Tigeot
490*3f2dd94aSFrançois Tigeot out:
491a85cb24fSFrançois Tigeot drm_framebuffer_put(fb);
4921dedbd3bSFrançois Tigeot
4931dedbd3bSFrançois Tigeot return ret;
4941dedbd3bSFrançois Tigeot }
4951dedbd3bSFrançois Tigeot
4961dedbd3bSFrançois Tigeot /**
4971dedbd3bSFrançois Tigeot * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB
4981dedbd3bSFrançois Tigeot * @dev: drm device for the ioctl
4991dedbd3bSFrançois Tigeot * @data: data pointer for the ioctl
5001dedbd3bSFrançois Tigeot * @file_priv: drm file for the ioctl call
5011dedbd3bSFrançois Tigeot *
5021dedbd3bSFrançois Tigeot * Lookup the FB and flush out the damaged area supplied by userspace as a clip
5031dedbd3bSFrançois Tigeot * rectangle list. Generic userspace which does frontbuffer rendering must call
5041dedbd3bSFrançois Tigeot * this ioctl to flush out the changes on manual-update display outputs, e.g.
5051dedbd3bSFrançois Tigeot * usb display-link, mipi manual update panels or edp panel self refresh modes.
5061dedbd3bSFrançois Tigeot *
5071dedbd3bSFrançois Tigeot * Modesetting drivers which always update the frontbuffer do not need to
508a85cb24fSFrançois Tigeot * implement the corresponding &drm_framebuffer_funcs.dirty callback.
5091dedbd3bSFrançois Tigeot *
5101dedbd3bSFrançois Tigeot * Called by the user via ioctl.
5111dedbd3bSFrançois Tigeot *
5121dedbd3bSFrançois Tigeot * Returns:
5131dedbd3bSFrançois Tigeot * Zero on success, negative errno on failure.
5141dedbd3bSFrançois Tigeot */
drm_mode_dirtyfb_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)5151dedbd3bSFrançois Tigeot int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
5161dedbd3bSFrançois Tigeot void *data, struct drm_file *file_priv)
5171dedbd3bSFrançois Tigeot {
5181dedbd3bSFrançois Tigeot struct drm_clip_rect __user *clips_ptr;
5191dedbd3bSFrançois Tigeot struct drm_clip_rect *clips = NULL;
5201dedbd3bSFrançois Tigeot struct drm_mode_fb_dirty_cmd *r = data;
5211dedbd3bSFrançois Tigeot struct drm_framebuffer *fb;
5221dedbd3bSFrançois Tigeot unsigned flags;
5231dedbd3bSFrançois Tigeot int num_clips;
5241dedbd3bSFrançois Tigeot int ret;
5251dedbd3bSFrançois Tigeot
5261dedbd3bSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET))
5271dedbd3bSFrançois Tigeot return -EINVAL;
5281dedbd3bSFrançois Tigeot
529*3f2dd94aSFrançois Tigeot fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id);
5301dedbd3bSFrançois Tigeot if (!fb)
5311dedbd3bSFrançois Tigeot return -ENOENT;
5321dedbd3bSFrançois Tigeot
5331dedbd3bSFrançois Tigeot num_clips = r->num_clips;
5341dedbd3bSFrançois Tigeot clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr;
5351dedbd3bSFrançois Tigeot
5361dedbd3bSFrançois Tigeot if (!num_clips != !clips_ptr) {
5371dedbd3bSFrançois Tigeot ret = -EINVAL;
5381dedbd3bSFrançois Tigeot goto out_err1;
5391dedbd3bSFrançois Tigeot }
5401dedbd3bSFrançois Tigeot
5411dedbd3bSFrançois Tigeot flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags;
5421dedbd3bSFrançois Tigeot
5431dedbd3bSFrançois Tigeot /* If userspace annotates copy, clips must come in pairs */
5441dedbd3bSFrançois Tigeot if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) {
5451dedbd3bSFrançois Tigeot ret = -EINVAL;
5461dedbd3bSFrançois Tigeot goto out_err1;
5471dedbd3bSFrançois Tigeot }
5481dedbd3bSFrançois Tigeot
5491dedbd3bSFrançois Tigeot if (num_clips && clips_ptr) {
5501dedbd3bSFrançois Tigeot if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) {
5511dedbd3bSFrançois Tigeot ret = -EINVAL;
5521dedbd3bSFrançois Tigeot goto out_err1;
5531dedbd3bSFrançois Tigeot }
5541dedbd3bSFrançois Tigeot clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL);
5551dedbd3bSFrançois Tigeot if (!clips) {
5561dedbd3bSFrançois Tigeot ret = -ENOMEM;
5571dedbd3bSFrançois Tigeot goto out_err1;
5581dedbd3bSFrançois Tigeot }
5591dedbd3bSFrançois Tigeot
5601dedbd3bSFrançois Tigeot ret = copy_from_user(clips, clips_ptr,
5611dedbd3bSFrançois Tigeot num_clips * sizeof(*clips));
5621dedbd3bSFrançois Tigeot if (ret) {
5631dedbd3bSFrançois Tigeot ret = -EFAULT;
5641dedbd3bSFrançois Tigeot goto out_err2;
5651dedbd3bSFrançois Tigeot }
5661dedbd3bSFrançois Tigeot }
5671dedbd3bSFrançois Tigeot
5681dedbd3bSFrançois Tigeot if (fb->funcs->dirty) {
5691dedbd3bSFrançois Tigeot ret = fb->funcs->dirty(fb, file_priv, flags, r->color,
5701dedbd3bSFrançois Tigeot clips, num_clips);
5711dedbd3bSFrançois Tigeot } else {
5721dedbd3bSFrançois Tigeot ret = -ENOSYS;
5731dedbd3bSFrançois Tigeot }
5741dedbd3bSFrançois Tigeot
5751dedbd3bSFrançois Tigeot out_err2:
5761dedbd3bSFrançois Tigeot kfree(clips);
5771dedbd3bSFrançois Tigeot out_err1:
578a85cb24fSFrançois Tigeot drm_framebuffer_put(fb);
5791dedbd3bSFrançois Tigeot
5801dedbd3bSFrançois Tigeot return ret;
5811dedbd3bSFrançois Tigeot }
5821dedbd3bSFrançois Tigeot
5831dedbd3bSFrançois Tigeot /**
5841dedbd3bSFrançois Tigeot * drm_fb_release - remove and free the FBs on this file
5851dedbd3bSFrançois Tigeot * @priv: drm file for the ioctl
5861dedbd3bSFrançois Tigeot *
5871dedbd3bSFrançois Tigeot * Destroy all the FBs associated with @filp.
5881dedbd3bSFrançois Tigeot *
5891dedbd3bSFrançois Tigeot * Called by the user via ioctl.
5901dedbd3bSFrançois Tigeot *
5911dedbd3bSFrançois Tigeot * Returns:
5921dedbd3bSFrançois Tigeot * Zero on success, negative errno on failure.
5931dedbd3bSFrançois Tigeot */
drm_fb_release(struct drm_file * priv)5941dedbd3bSFrançois Tigeot void drm_fb_release(struct drm_file *priv)
5951dedbd3bSFrançois Tigeot {
5961dedbd3bSFrançois Tigeot struct drm_framebuffer *fb, *tfb;
5971dedbd3bSFrançois Tigeot struct drm_mode_rmfb_work arg;
5981dedbd3bSFrançois Tigeot
5991dedbd3bSFrançois Tigeot INIT_LIST_HEAD(&arg.fbs);
6001dedbd3bSFrançois Tigeot
6011dedbd3bSFrançois Tigeot /*
6021dedbd3bSFrançois Tigeot * When the file gets released that means no one else can access the fb
6031dedbd3bSFrançois Tigeot * list any more, so no need to grab fpriv->fbs_lock. And we need to
6041dedbd3bSFrançois Tigeot * avoid upsetting lockdep since the universal cursor code adds a
6051dedbd3bSFrançois Tigeot * framebuffer while holding mutex locks.
6061dedbd3bSFrançois Tigeot *
6071dedbd3bSFrançois Tigeot * Note that a real deadlock between fpriv->fbs_lock and the modeset
6081dedbd3bSFrançois Tigeot * locks is impossible here since no one else but this function can get
6091dedbd3bSFrançois Tigeot * at it any more.
6101dedbd3bSFrançois Tigeot */
6111dedbd3bSFrançois Tigeot list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
6121dedbd3bSFrançois Tigeot if (drm_framebuffer_read_refcount(fb) > 1) {
6131dedbd3bSFrançois Tigeot list_move_tail(&fb->filp_head, &arg.fbs);
6141dedbd3bSFrançois Tigeot } else {
6151dedbd3bSFrançois Tigeot list_del_init(&fb->filp_head);
6161dedbd3bSFrançois Tigeot
6171dedbd3bSFrançois Tigeot /* This drops the fpriv->fbs reference. */
618a85cb24fSFrançois Tigeot drm_framebuffer_put(fb);
6191dedbd3bSFrançois Tigeot }
6201dedbd3bSFrançois Tigeot }
6211dedbd3bSFrançois Tigeot
6221dedbd3bSFrançois Tigeot if (!list_empty(&arg.fbs)) {
6231dedbd3bSFrançois Tigeot INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
6241dedbd3bSFrançois Tigeot
6251dedbd3bSFrançois Tigeot schedule_work(&arg.work);
6261dedbd3bSFrançois Tigeot flush_work(&arg.work);
6271dedbd3bSFrançois Tigeot destroy_work_on_stack(&arg.work);
6281dedbd3bSFrançois Tigeot }
6291dedbd3bSFrançois Tigeot }
6301dedbd3bSFrançois Tigeot
drm_framebuffer_free(struct kref * kref)6311dedbd3bSFrançois Tigeot void drm_framebuffer_free(struct kref *kref)
6321dedbd3bSFrançois Tigeot {
6331dedbd3bSFrançois Tigeot struct drm_framebuffer *fb =
6341dedbd3bSFrançois Tigeot container_of(kref, struct drm_framebuffer, base.refcount);
6351dedbd3bSFrançois Tigeot struct drm_device *dev = fb->dev;
6361dedbd3bSFrançois Tigeot
6371dedbd3bSFrançois Tigeot /*
6381dedbd3bSFrançois Tigeot * The lookup idr holds a weak reference, which has not necessarily been
6391dedbd3bSFrançois Tigeot * removed at this point. Check for that.
6401dedbd3bSFrançois Tigeot */
6411dedbd3bSFrançois Tigeot drm_mode_object_unregister(dev, &fb->base);
6421dedbd3bSFrançois Tigeot
6431dedbd3bSFrançois Tigeot fb->funcs->destroy(fb);
6441dedbd3bSFrançois Tigeot }
6451dedbd3bSFrançois Tigeot
6461dedbd3bSFrançois Tigeot /**
6471dedbd3bSFrançois Tigeot * drm_framebuffer_init - initialize a framebuffer
6481dedbd3bSFrançois Tigeot * @dev: DRM device
6491dedbd3bSFrançois Tigeot * @fb: framebuffer to be initialized
6501dedbd3bSFrançois Tigeot * @funcs: ... with these functions
6511dedbd3bSFrançois Tigeot *
6521dedbd3bSFrançois Tigeot * Allocates an ID for the framebuffer's parent mode object, sets its mode
6531dedbd3bSFrançois Tigeot * functions & device file and adds it to the master fd list.
6541dedbd3bSFrançois Tigeot *
6551dedbd3bSFrançois Tigeot * IMPORTANT:
6561dedbd3bSFrançois Tigeot * This functions publishes the fb and makes it available for concurrent access
6571dedbd3bSFrançois Tigeot * by other users. Which means by this point the fb _must_ be fully set up -
6581dedbd3bSFrançois Tigeot * since all the fb attributes are invariant over its lifetime, no further
6591dedbd3bSFrançois Tigeot * locking but only correct reference counting is required.
6601dedbd3bSFrançois Tigeot *
6611dedbd3bSFrançois Tigeot * Returns:
6621dedbd3bSFrançois Tigeot * Zero on success, error code on failure.
6631dedbd3bSFrançois Tigeot */
drm_framebuffer_init(struct drm_device * dev,struct drm_framebuffer * fb,const struct drm_framebuffer_funcs * funcs)6641dedbd3bSFrançois Tigeot int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
6651dedbd3bSFrançois Tigeot const struct drm_framebuffer_funcs *funcs)
6661dedbd3bSFrançois Tigeot {
6671dedbd3bSFrançois Tigeot int ret;
6681dedbd3bSFrançois Tigeot
669a85cb24fSFrançois Tigeot if (WARN_ON_ONCE(fb->dev != dev || !fb->format))
670a85cb24fSFrançois Tigeot return -EINVAL;
671a85cb24fSFrançois Tigeot
6721dedbd3bSFrançois Tigeot INIT_LIST_HEAD(&fb->filp_head);
673a85cb24fSFrançois Tigeot
6741dedbd3bSFrançois Tigeot fb->funcs = funcs;
6751dedbd3bSFrançois Tigeot
676a85cb24fSFrançois Tigeot ret = __drm_mode_object_add(dev, &fb->base, DRM_MODE_OBJECT_FB,
6771dedbd3bSFrançois Tigeot false, drm_framebuffer_free);
6781dedbd3bSFrançois Tigeot if (ret)
6791dedbd3bSFrançois Tigeot goto out;
6801dedbd3bSFrançois Tigeot
6811dedbd3bSFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock);
6821dedbd3bSFrançois Tigeot dev->mode_config.num_fb++;
6831dedbd3bSFrançois Tigeot list_add(&fb->head, &dev->mode_config.fb_list);
6841dedbd3bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock);
6851dedbd3bSFrançois Tigeot
6861dedbd3bSFrançois Tigeot drm_mode_object_register(dev, &fb->base);
6871dedbd3bSFrançois Tigeot out:
6881dedbd3bSFrançois Tigeot return ret;
6891dedbd3bSFrançois Tigeot }
6901dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_init);
6911dedbd3bSFrançois Tigeot
6921dedbd3bSFrançois Tigeot /**
6931dedbd3bSFrançois Tigeot * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference
6941dedbd3bSFrançois Tigeot * @dev: drm device
695*3f2dd94aSFrançois Tigeot * @file_priv: drm file to check for lease against.
6961dedbd3bSFrançois Tigeot * @id: id of the fb object
6971dedbd3bSFrançois Tigeot *
6981dedbd3bSFrançois Tigeot * If successful, this grabs an additional reference to the framebuffer -
6991dedbd3bSFrançois Tigeot * callers need to make sure to eventually unreference the returned framebuffer
700a85cb24fSFrançois Tigeot * again, using drm_framebuffer_put().
7011dedbd3bSFrançois Tigeot */
drm_framebuffer_lookup(struct drm_device * dev,struct drm_file * file_priv,uint32_t id)7021dedbd3bSFrançois Tigeot struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
703*3f2dd94aSFrançois Tigeot struct drm_file *file_priv,
7041dedbd3bSFrançois Tigeot uint32_t id)
7051dedbd3bSFrançois Tigeot {
7061dedbd3bSFrançois Tigeot struct drm_mode_object *obj;
7071dedbd3bSFrançois Tigeot struct drm_framebuffer *fb = NULL;
7081dedbd3bSFrançois Tigeot
709*3f2dd94aSFrançois Tigeot obj = __drm_mode_object_find(dev, file_priv, id, DRM_MODE_OBJECT_FB);
7101dedbd3bSFrançois Tigeot if (obj)
7111dedbd3bSFrançois Tigeot fb = obj_to_fb(obj);
7121dedbd3bSFrançois Tigeot return fb;
7131dedbd3bSFrançois Tigeot }
7141dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_lookup);
7151dedbd3bSFrançois Tigeot
7161dedbd3bSFrançois Tigeot /**
7171dedbd3bSFrançois Tigeot * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
7181dedbd3bSFrançois Tigeot * @fb: fb to unregister
7191dedbd3bSFrançois Tigeot *
7201dedbd3bSFrançois Tigeot * Drivers need to call this when cleaning up driver-private framebuffers, e.g.
7211dedbd3bSFrançois Tigeot * those used for fbdev. Note that the caller must hold a reference of it's own,
7221dedbd3bSFrançois Tigeot * i.e. the object may not be destroyed through this call (since it'll lead to a
7231dedbd3bSFrançois Tigeot * locking inversion).
7244be47400SFrançois Tigeot *
7254be47400SFrançois Tigeot * NOTE: This function is deprecated. For driver-private framebuffers it is not
7264be47400SFrançois Tigeot * recommended to embed a framebuffer struct info fbdev struct, instead, a
727a85cb24fSFrançois Tigeot * framebuffer pointer is preferred and drm_framebuffer_put() should be called
728a85cb24fSFrançois Tigeot * when the framebuffer is to be cleaned up.
7291dedbd3bSFrançois Tigeot */
drm_framebuffer_unregister_private(struct drm_framebuffer * fb)7301dedbd3bSFrançois Tigeot void drm_framebuffer_unregister_private(struct drm_framebuffer *fb)
7311dedbd3bSFrançois Tigeot {
7321dedbd3bSFrançois Tigeot struct drm_device *dev;
7331dedbd3bSFrançois Tigeot
7341dedbd3bSFrançois Tigeot if (!fb)
7351dedbd3bSFrançois Tigeot return;
7361dedbd3bSFrançois Tigeot
7371dedbd3bSFrançois Tigeot dev = fb->dev;
7381dedbd3bSFrançois Tigeot
7391dedbd3bSFrançois Tigeot /* Mark fb as reaped and drop idr ref. */
7401dedbd3bSFrançois Tigeot drm_mode_object_unregister(dev, &fb->base);
7411dedbd3bSFrançois Tigeot }
7421dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_unregister_private);
7431dedbd3bSFrançois Tigeot
7441dedbd3bSFrançois Tigeot /**
7451dedbd3bSFrançois Tigeot * drm_framebuffer_cleanup - remove a framebuffer object
7461dedbd3bSFrançois Tigeot * @fb: framebuffer to remove
7471dedbd3bSFrançois Tigeot *
7481dedbd3bSFrançois Tigeot * Cleanup framebuffer. This function is intended to be used from the drivers
749a85cb24fSFrançois Tigeot * &drm_framebuffer_funcs.destroy callback. It can also be used to clean up
750a85cb24fSFrançois Tigeot * driver private framebuffers embedded into a larger structure.
7511dedbd3bSFrançois Tigeot *
752a85cb24fSFrançois Tigeot * Note that this function does not remove the fb from active usage - if it is
7531dedbd3bSFrançois Tigeot * still used anywhere, hilarity can ensue since userspace could call getfb on
7541dedbd3bSFrançois Tigeot * the id and get back -EINVAL. Obviously no concern at driver unload time.
7551dedbd3bSFrançois Tigeot *
7561dedbd3bSFrançois Tigeot * Also, the framebuffer will not be removed from the lookup idr - for
7571dedbd3bSFrançois Tigeot * user-created framebuffers this will happen in in the rmfb ioctl. For
7581dedbd3bSFrançois Tigeot * driver-private objects (e.g. for fbdev) drivers need to explicitly call
7591dedbd3bSFrançois Tigeot * drm_framebuffer_unregister_private.
7601dedbd3bSFrançois Tigeot */
drm_framebuffer_cleanup(struct drm_framebuffer * fb)7611dedbd3bSFrançois Tigeot void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
7621dedbd3bSFrançois Tigeot {
7631dedbd3bSFrançois Tigeot struct drm_device *dev = fb->dev;
7641dedbd3bSFrançois Tigeot
7651dedbd3bSFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock);
7661dedbd3bSFrançois Tigeot list_del(&fb->head);
7671dedbd3bSFrançois Tigeot dev->mode_config.num_fb--;
7681dedbd3bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock);
7691dedbd3bSFrançois Tigeot }
7701dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_cleanup);
7711dedbd3bSFrançois Tigeot
atomic_remove_fb(struct drm_framebuffer * fb)772a85cb24fSFrançois Tigeot static int atomic_remove_fb(struct drm_framebuffer *fb)
773a85cb24fSFrançois Tigeot {
774a85cb24fSFrançois Tigeot struct drm_modeset_acquire_ctx ctx;
775a85cb24fSFrançois Tigeot struct drm_device *dev = fb->dev;
776a85cb24fSFrançois Tigeot struct drm_atomic_state *state;
777a85cb24fSFrançois Tigeot struct drm_plane *plane;
778a85cb24fSFrançois Tigeot struct drm_connector *conn;
779a85cb24fSFrançois Tigeot struct drm_connector_state *conn_state;
780a85cb24fSFrançois Tigeot int i, ret = 0;
781a85cb24fSFrançois Tigeot unsigned plane_mask;
782a85cb24fSFrançois Tigeot
783a85cb24fSFrançois Tigeot state = drm_atomic_state_alloc(dev);
784a85cb24fSFrançois Tigeot if (!state)
785a85cb24fSFrançois Tigeot return -ENOMEM;
786a85cb24fSFrançois Tigeot
787a85cb24fSFrançois Tigeot drm_modeset_acquire_init(&ctx, 0);
788a85cb24fSFrançois Tigeot state->acquire_ctx = &ctx;
789a85cb24fSFrançois Tigeot
790a85cb24fSFrançois Tigeot retry:
791a85cb24fSFrançois Tigeot plane_mask = 0;
792a85cb24fSFrançois Tigeot ret = drm_modeset_lock_all_ctx(dev, &ctx);
793a85cb24fSFrançois Tigeot if (ret)
794a85cb24fSFrançois Tigeot goto unlock;
795a85cb24fSFrançois Tigeot
796a85cb24fSFrançois Tigeot drm_for_each_plane(plane, dev) {
797a85cb24fSFrançois Tigeot struct drm_plane_state *plane_state;
798a85cb24fSFrançois Tigeot
799a85cb24fSFrançois Tigeot if (plane->state->fb != fb)
800a85cb24fSFrançois Tigeot continue;
801a85cb24fSFrançois Tigeot
802a85cb24fSFrançois Tigeot plane_state = drm_atomic_get_plane_state(state, plane);
803a85cb24fSFrançois Tigeot if (IS_ERR(plane_state)) {
804a85cb24fSFrançois Tigeot ret = PTR_ERR(plane_state);
805a85cb24fSFrançois Tigeot goto unlock;
806a85cb24fSFrançois Tigeot }
807a85cb24fSFrançois Tigeot
808a85cb24fSFrançois Tigeot if (plane_state->crtc->primary == plane) {
809a85cb24fSFrançois Tigeot struct drm_crtc_state *crtc_state;
810a85cb24fSFrançois Tigeot
811a85cb24fSFrançois Tigeot crtc_state = drm_atomic_get_existing_crtc_state(state, plane_state->crtc);
812a85cb24fSFrançois Tigeot
813a85cb24fSFrançois Tigeot ret = drm_atomic_add_affected_connectors(state, plane_state->crtc);
814a85cb24fSFrançois Tigeot if (ret)
815a85cb24fSFrançois Tigeot goto unlock;
816a85cb24fSFrançois Tigeot
817a85cb24fSFrançois Tigeot crtc_state->active = false;
818a85cb24fSFrançois Tigeot ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
819a85cb24fSFrançois Tigeot if (ret)
820a85cb24fSFrançois Tigeot goto unlock;
821a85cb24fSFrançois Tigeot }
822a85cb24fSFrançois Tigeot
823a85cb24fSFrançois Tigeot drm_atomic_set_fb_for_plane(plane_state, NULL);
824a85cb24fSFrançois Tigeot ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
825a85cb24fSFrançois Tigeot if (ret)
826a85cb24fSFrançois Tigeot goto unlock;
827a85cb24fSFrançois Tigeot
828a85cb24fSFrançois Tigeot plane_mask |= BIT(drm_plane_index(plane));
829a85cb24fSFrançois Tigeot
830a85cb24fSFrançois Tigeot plane->old_fb = plane->fb;
831a85cb24fSFrançois Tigeot }
832a85cb24fSFrançois Tigeot
833*3f2dd94aSFrançois Tigeot for_each_new_connector_in_state(state, conn, conn_state, i) {
834a85cb24fSFrançois Tigeot ret = drm_atomic_set_crtc_for_connector(conn_state, NULL);
835a85cb24fSFrançois Tigeot
836a85cb24fSFrançois Tigeot if (ret)
837a85cb24fSFrançois Tigeot goto unlock;
838a85cb24fSFrançois Tigeot }
839a85cb24fSFrançois Tigeot
840a85cb24fSFrançois Tigeot if (plane_mask)
841a85cb24fSFrançois Tigeot ret = drm_atomic_commit(state);
842a85cb24fSFrançois Tigeot
843a85cb24fSFrançois Tigeot unlock:
844a85cb24fSFrançois Tigeot if (plane_mask)
845a85cb24fSFrançois Tigeot drm_atomic_clean_old_fb(dev, plane_mask, ret);
846a85cb24fSFrançois Tigeot
847a85cb24fSFrançois Tigeot if (ret == -EDEADLK) {
848a85cb24fSFrançois Tigeot drm_atomic_state_clear(state);
849a85cb24fSFrançois Tigeot drm_modeset_backoff(&ctx);
850a85cb24fSFrançois Tigeot goto retry;
851a85cb24fSFrançois Tigeot }
852a85cb24fSFrançois Tigeot
853a85cb24fSFrançois Tigeot drm_atomic_state_put(state);
854a85cb24fSFrançois Tigeot
855a85cb24fSFrançois Tigeot drm_modeset_drop_locks(&ctx);
856a85cb24fSFrançois Tigeot drm_modeset_acquire_fini(&ctx);
857a85cb24fSFrançois Tigeot
858a85cb24fSFrançois Tigeot return ret;
859a85cb24fSFrançois Tigeot }
860a85cb24fSFrançois Tigeot
legacy_remove_fb(struct drm_framebuffer * fb)861a85cb24fSFrançois Tigeot static void legacy_remove_fb(struct drm_framebuffer *fb)
862a85cb24fSFrançois Tigeot {
863a85cb24fSFrançois Tigeot struct drm_device *dev = fb->dev;
864a85cb24fSFrançois Tigeot struct drm_crtc *crtc;
865a85cb24fSFrançois Tigeot struct drm_plane *plane;
866a85cb24fSFrançois Tigeot
867a85cb24fSFrançois Tigeot drm_modeset_lock_all(dev);
868a85cb24fSFrançois Tigeot /* remove from any CRTC */
869a85cb24fSFrançois Tigeot drm_for_each_crtc(crtc, dev) {
870a85cb24fSFrançois Tigeot if (crtc->primary->fb == fb) {
871a85cb24fSFrançois Tigeot /* should turn off the crtc */
872a85cb24fSFrançois Tigeot if (drm_crtc_force_disable(crtc))
873a85cb24fSFrançois Tigeot DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
874a85cb24fSFrançois Tigeot }
875a85cb24fSFrançois Tigeot }
876a85cb24fSFrançois Tigeot
877a85cb24fSFrançois Tigeot drm_for_each_plane(plane, dev) {
878a85cb24fSFrançois Tigeot if (plane->fb == fb)
879a85cb24fSFrançois Tigeot drm_plane_force_disable(plane);
880a85cb24fSFrançois Tigeot }
881a85cb24fSFrançois Tigeot drm_modeset_unlock_all(dev);
882a85cb24fSFrançois Tigeot }
883a85cb24fSFrançois Tigeot
8841dedbd3bSFrançois Tigeot /**
8851dedbd3bSFrançois Tigeot * drm_framebuffer_remove - remove and unreference a framebuffer object
8861dedbd3bSFrançois Tigeot * @fb: framebuffer to remove
8871dedbd3bSFrançois Tigeot *
8881dedbd3bSFrançois Tigeot * Scans all the CRTCs and planes in @dev's mode_config. If they're
8891dedbd3bSFrançois Tigeot * using @fb, removes it, setting it to NULL. Then drops the reference to the
8901dedbd3bSFrançois Tigeot * passed-in framebuffer. Might take the modeset locks.
8911dedbd3bSFrançois Tigeot *
8921dedbd3bSFrançois Tigeot * Note that this function optimizes the cleanup away if the caller holds the
8931dedbd3bSFrançois Tigeot * last reference to the framebuffer. It is also guaranteed to not take the
8941dedbd3bSFrançois Tigeot * modeset locks in this case.
8951dedbd3bSFrançois Tigeot */
drm_framebuffer_remove(struct drm_framebuffer * fb)8961dedbd3bSFrançois Tigeot void drm_framebuffer_remove(struct drm_framebuffer *fb)
8971dedbd3bSFrançois Tigeot {
8981dedbd3bSFrançois Tigeot struct drm_device *dev;
8991dedbd3bSFrançois Tigeot
9001dedbd3bSFrançois Tigeot if (!fb)
9011dedbd3bSFrançois Tigeot return;
9021dedbd3bSFrançois Tigeot
9031dedbd3bSFrançois Tigeot dev = fb->dev;
9041dedbd3bSFrançois Tigeot
9051dedbd3bSFrançois Tigeot WARN_ON(!list_empty(&fb->filp_head));
9061dedbd3bSFrançois Tigeot
9071dedbd3bSFrançois Tigeot /*
9081dedbd3bSFrançois Tigeot * drm ABI mandates that we remove any deleted framebuffers from active
9091dedbd3bSFrançois Tigeot * useage. But since most sane clients only remove framebuffers they no
9101dedbd3bSFrançois Tigeot * longer need, try to optimize this away.
9111dedbd3bSFrançois Tigeot *
9121dedbd3bSFrançois Tigeot * Since we're holding a reference ourselves, observing a refcount of 1
9131dedbd3bSFrançois Tigeot * means that we're the last holder and can skip it. Also, the refcount
9141dedbd3bSFrançois Tigeot * can never increase from 1 again, so we don't need any barriers or
9151dedbd3bSFrançois Tigeot * locks.
9161dedbd3bSFrançois Tigeot *
9171dedbd3bSFrançois Tigeot * Note that userspace could try to race with use and instate a new
9181dedbd3bSFrançois Tigeot * usage _after_ we've cleared all current ones. End result will be an
9191dedbd3bSFrançois Tigeot * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot
9201dedbd3bSFrançois Tigeot * in this manner.
9211dedbd3bSFrançois Tigeot */
9221dedbd3bSFrançois Tigeot if (drm_framebuffer_read_refcount(fb) > 1) {
923a85cb24fSFrançois Tigeot if (drm_drv_uses_atomic_modeset(dev)) {
924a85cb24fSFrançois Tigeot int ret = atomic_remove_fb(fb);
925a85cb24fSFrançois Tigeot WARN(ret, "atomic remove_fb failed with %i\n", ret);
926a85cb24fSFrançois Tigeot } else
927a85cb24fSFrançois Tigeot legacy_remove_fb(fb);
9281dedbd3bSFrançois Tigeot }
9291dedbd3bSFrançois Tigeot
930a85cb24fSFrançois Tigeot drm_framebuffer_put(fb);
9311dedbd3bSFrançois Tigeot }
9321dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_remove);
933a85cb24fSFrançois Tigeot
934a85cb24fSFrançois Tigeot /**
935a85cb24fSFrançois Tigeot * drm_framebuffer_plane_width - width of the plane given the first plane
936a85cb24fSFrançois Tigeot * @width: width of the first plane
937a85cb24fSFrançois Tigeot * @fb: the framebuffer
938a85cb24fSFrançois Tigeot * @plane: plane index
939a85cb24fSFrançois Tigeot *
940a85cb24fSFrançois Tigeot * Returns:
941a85cb24fSFrançois Tigeot * The width of @plane, given that the width of the first plane is @width.
942a85cb24fSFrançois Tigeot */
drm_framebuffer_plane_width(int width,const struct drm_framebuffer * fb,int plane)943a85cb24fSFrançois Tigeot int drm_framebuffer_plane_width(int width,
944a85cb24fSFrançois Tigeot const struct drm_framebuffer *fb, int plane)
945a85cb24fSFrançois Tigeot {
946a85cb24fSFrançois Tigeot if (plane >= fb->format->num_planes)
947a85cb24fSFrançois Tigeot return 0;
948a85cb24fSFrançois Tigeot
949a85cb24fSFrançois Tigeot return fb_plane_width(width, fb->format, plane);
950a85cb24fSFrançois Tigeot }
951a85cb24fSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_plane_width);
952a85cb24fSFrançois Tigeot
953a85cb24fSFrançois Tigeot /**
954a85cb24fSFrançois Tigeot * drm_framebuffer_plane_height - height of the plane given the first plane
955a85cb24fSFrançois Tigeot * @height: height of the first plane
956a85cb24fSFrançois Tigeot * @fb: the framebuffer
957a85cb24fSFrançois Tigeot * @plane: plane index
958a85cb24fSFrançois Tigeot *
959a85cb24fSFrançois Tigeot * Returns:
960a85cb24fSFrançois Tigeot * The height of @plane, given that the height of the first plane is @height.
961a85cb24fSFrançois Tigeot */
drm_framebuffer_plane_height(int height,const struct drm_framebuffer * fb,int plane)962a85cb24fSFrançois Tigeot int drm_framebuffer_plane_height(int height,
963a85cb24fSFrançois Tigeot const struct drm_framebuffer *fb, int plane)
964a85cb24fSFrançois Tigeot {
965a85cb24fSFrançois Tigeot if (plane >= fb->format->num_planes)
966a85cb24fSFrançois Tigeot return 0;
967a85cb24fSFrançois Tigeot
968a85cb24fSFrançois Tigeot return fb_plane_height(height, fb->format, plane);
969a85cb24fSFrançois Tigeot }
970a85cb24fSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_plane_height);
971