diff options
Diffstat (limited to 'drivers/gpu/drm/drm_framebuffer.c')
-rw-r--r-- | drivers/gpu/drm/drm_framebuffer.c | 144 |
1 files changed, 94 insertions, 50 deletions
diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index ad67203de715..fcaea8f50513 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -25,6 +25,7 @@ #include <drm/drm_auth.h> #include <drm/drm_framebuffer.h> #include <drm/drm_atomic.h> +#include <drm/drm_atomic_uapi.h> #include <drm/drm_print.h> #include "drm_internal.h" @@ -95,36 +96,39 @@ int drm_framebuffer_check_src_coords(uint32_t src_x, uint32_t src_y, /** * drm_mode_addfb - add an FB to the graphics configuration * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call + * @or: pointer to request structure + * @file_priv: drm file * * Add a new FB to the specified CRTC, given a user request. This is the * original addfb ioctl which only supported RGB formats. * - * Called by the user via ioctl. + * Called by the user via ioctl, or by an in-kernel client. * * Returns: * Zero on success, negative errno on failure. */ -int drm_mode_addfb(struct drm_device *dev, - void *data, struct drm_file *file_priv) +int drm_mode_addfb(struct drm_device *dev, struct drm_mode_fb_cmd *or, + struct drm_file *file_priv) { - struct drm_mode_fb_cmd *or = data; struct drm_mode_fb_cmd2 r = {}; int ret; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EOPNOTSUPP; + + r.pixel_format = drm_driver_legacy_fb_format(dev, or->bpp, or->depth); + if (r.pixel_format == DRM_FORMAT_INVALID) { + DRM_DEBUG("bad {bpp:%d, depth:%d}\n", or->bpp, or->depth); + return -EINVAL; + } + /* convert to new format and call new ioctl */ r.fb_id = or->fb_id; r.width = or->width; r.height = or->height; r.pitches[0] = or->pitch; - r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); r.handles[0] = or->handle; - if (r.pixel_format == DRM_FORMAT_XRGB2101010 && - dev->driver->driver_features & DRIVER_PREFER_XBGR_30BPP) - r.pixel_format = DRM_FORMAT_XBGR2101010; - ret = drm_mode_addfb2(dev, &r, file_priv); if (ret) return ret; @@ -134,6 +138,12 @@ int drm_mode_addfb(struct drm_device *dev, return 0; } +int drm_mode_addfb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + return drm_mode_addfb(dev, data, file_priv); +} + static int fb_plane_width(int width, const struct drm_format_info *format, int plane) { @@ -159,7 +169,7 @@ static int framebuffer_check(struct drm_device *dev, int i; /* check if the format is supported at all */ - info = __drm_format_info(r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN); + info = __drm_format_info(r->pixel_format); if (!info) { struct drm_format_name_buf format_name; @@ -185,20 +195,26 @@ static int framebuffer_check(struct drm_device *dev, for (i = 0; i < info->num_planes; i++) { unsigned int width = fb_plane_width(r->width, info, i); unsigned int height = fb_plane_height(r->height, info, i); - unsigned int cpp = info->cpp[i]; + unsigned int block_size = info->char_per_block[i]; + u64 min_pitch = drm_format_info_min_pitch(info, i, width); + + if (!block_size && (r->modifier[i] == DRM_FORMAT_MOD_LINEAR)) { + DRM_DEBUG_KMS("Format requires non-linear modifier for plane %d\n", i); + return -EINVAL; + } if (!r->handles[i]) { DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i); return -EINVAL; } - if ((uint64_t) width * cpp > UINT_MAX) + if (min_pitch > UINT_MAX) return -ERANGE; if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX) return -ERANGE; - if (r->pitches[i] < width * cpp) { + if (block_size && r->pitches[i] < min_pitch) { DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); return -EINVAL; } @@ -307,6 +323,7 @@ drm_internal_framebuffer_create(struct drm_device *dev, return fb; } +EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_internal_framebuffer_create); /** * drm_mode_addfb2 - add an FB to the graphics configuration @@ -330,7 +347,7 @@ int drm_mode_addfb2(struct drm_device *dev, struct drm_framebuffer *fb; if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; + return -EOPNOTSUPP; fb = drm_internal_framebuffer_create(dev, r, file_priv); if (IS_ERR(fb)) @@ -347,6 +364,30 @@ int drm_mode_addfb2(struct drm_device *dev, return 0; } +int drm_mode_addfb2_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ +#ifdef __BIG_ENDIAN + if (!dev->mode_config.quirk_addfb_prefer_host_byte_order) { + /* + * Drivers must set the + * quirk_addfb_prefer_host_byte_order quirk to make + * the drm_mode_addfb() compat code work correctly on + * bigendian machines. + * + * If they don't they interpret pixel_format values + * incorrectly for bug compatibility, which in turn + * implies the ADDFB2 ioctl does not work correctly + * then. So block it to make userspace fallback to + * ADDFB. + */ + DRM_DEBUG_KMS("addfb2 broken on bigendian"); + return -EOPNOTSUPP; + } +#endif + return drm_mode_addfb2(dev, data, file_priv); +} + struct drm_mode_rmfb_work { struct work_struct work; struct list_head fbs; @@ -367,29 +408,28 @@ static void drm_mode_rmfb_work_fn(struct work_struct *w) /** * drm_mode_rmfb - remove an FB from the configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call + * @dev: drm device + * @fb_id: id of framebuffer to remove + * @file_priv: drm file * - * Remove the FB specified by the user. + * Remove the specified FB. * - * Called by the user via ioctl. + * Called by the user via ioctl, or by an in-kernel client. * * Returns: * Zero on success, negative errno on failure. */ -int drm_mode_rmfb(struct drm_device *dev, - void *data, struct drm_file *file_priv) +int drm_mode_rmfb(struct drm_device *dev, u32 fb_id, + struct drm_file *file_priv) { struct drm_framebuffer *fb = NULL; struct drm_framebuffer *fbl = NULL; - uint32_t *id = data; int found = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; + return -EOPNOTSUPP; - fb = drm_framebuffer_lookup(dev, file_priv, *id); + fb = drm_framebuffer_lookup(dev, file_priv, fb_id); if (!fb) return -ENOENT; @@ -435,6 +475,14 @@ fail_unref: return -ENOENT; } +int drm_mode_rmfb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + uint32_t *fb_id = data; + + return drm_mode_rmfb(dev, *fb_id, file_priv); +} + /** * drm_mode_getfb - get FB info * @dev: drm device for the ioctl @@ -456,7 +504,7 @@ int drm_mode_getfb(struct drm_device *dev, int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; + return -EOPNOTSUPP; fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id); if (!fb) @@ -468,29 +516,30 @@ int drm_mode_getfb(struct drm_device *dev, goto out; } + if (!fb->funcs->create_handle) { + ret = -ENODEV; + goto out; + } + r->height = fb->height; r->width = fb->width; r->depth = fb->format->depth; r->bpp = fb->format->cpp[0] * 8; r->pitch = fb->pitches[0]; - if (fb->funcs->create_handle) { - if (drm_is_current_master(file_priv) || capable(CAP_SYS_ADMIN) || - drm_is_control_client(file_priv)) { - ret = fb->funcs->create_handle(fb, file_priv, - &r->handle); - } else { - /* GET_FB() is an unprivileged ioctl so we must not - * return a buffer-handle to non-master processes! For - * backwards-compatibility reasons, we cannot make - * GET_FB() privileged, so just return an invalid handle - * for non-masters. */ - r->handle = 0; - ret = 0; - } - } else { - ret = -ENODEV; + + /* GET_FB() is an unprivileged ioctl so we must not return a + * buffer-handle to non-master processes! For + * backwards-compatibility reasons, we cannot make GET_FB() privileged, + * so just return an invalid handle for non-masters. + */ + if (!drm_is_current_master(file_priv) && !capable(CAP_SYS_ADMIN)) { + r->handle = 0; + ret = 0; + goto out; } + ret = fb->funcs->create_handle(fb, file_priv, &r->handle); + out: drm_framebuffer_put(fb); @@ -528,7 +577,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; + return -EOPNOTSUPP; fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id); if (!fb) @@ -834,9 +883,7 @@ retry: if (ret) goto unlock; - plane_mask |= BIT(drm_plane_index(plane)); - - plane->old_fb = plane->fb; + plane_mask |= drm_plane_mask(plane); } /* This list is only filled when disable_crtcs is set. */ @@ -851,9 +898,6 @@ retry: ret = drm_atomic_commit(state); unlock: - if (plane_mask) - drm_atomic_clean_old_fb(dev, plane_mask, ret); - if (ret == -EDEADLK) { drm_atomic_state_clear(state); drm_modeset_backoff(&ctx); |