From f1d34bfd70b1b4543a139ea28bad4c001c5f413d Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 19 Jun 2018 15:02:16 +0200 Subject: drm/vmwgfx: Replace vmw_dma_buffer with vmw_buffer_object Initially vmware buffer objects were only used as DMA buffers, so the name DMA buffer was a natural one. However, currently they are used also as dumb buffers and MOBs backing guest backed objects so renaming them to buffer objects is logical. Particularly since there is a dmabuf subsystem in the kernel where a dma buffer means something completely different. This also renames user-space api structures and IOCTL names correspondingly, but the old names remain defined for now and the ABI hasn't changed. There are a couple of minor style changes to make checkpatch happy. Signed-off-by: Thomas Hellstrom Reviewed-by: Sinclair Yeh Reviewed-by: Deepak Rawat --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 203 ++++++++++++++++++------------------ 1 file changed, 101 insertions(+), 102 deletions(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_kms.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index ef96ba7432ad..7a32be0cef14 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -85,10 +85,10 @@ static int vmw_cursor_update_image(struct vmw_private *dev_priv, return 0; } -static int vmw_cursor_update_dmabuf(struct vmw_private *dev_priv, - struct vmw_dma_buffer *dmabuf, - u32 width, u32 height, - u32 hotspotX, u32 hotspotY) +static int vmw_cursor_update_bo(struct vmw_private *dev_priv, + struct vmw_buffer_object *bo, + u32 width, u32 height, + u32 hotspotX, u32 hotspotY) { struct ttm_bo_kmap_obj map; unsigned long kmap_offset; @@ -100,13 +100,13 @@ static int vmw_cursor_update_dmabuf(struct vmw_private *dev_priv, kmap_offset = 0; kmap_num = (width*height*4 + PAGE_SIZE - 1) >> PAGE_SHIFT; - ret = ttm_bo_reserve(&dmabuf->base, true, false, NULL); + ret = ttm_bo_reserve(&bo->base, true, false, NULL); if (unlikely(ret != 0)) { DRM_ERROR("reserve failed\n"); return -EINVAL; } - ret = ttm_bo_kmap(&dmabuf->base, kmap_offset, kmap_num, &map); + ret = ttm_bo_kmap(&bo->base, kmap_offset, kmap_num, &map); if (unlikely(ret != 0)) goto err_unreserve; @@ -116,7 +116,7 @@ static int vmw_cursor_update_dmabuf(struct vmw_private *dev_priv, ttm_bo_kunmap(&map); err_unreserve: - ttm_bo_unreserve(&dmabuf->base); + ttm_bo_unreserve(&bo->base); return ret; } @@ -352,13 +352,13 @@ vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane, if (vps->surf) vmw_surface_unreference(&vps->surf); - if (vps->dmabuf) - vmw_dmabuf_unreference(&vps->dmabuf); + if (vps->bo) + vmw_bo_unreference(&vps->bo); if (fb) { - if (vmw_framebuffer_to_vfb(fb)->dmabuf) { - vps->dmabuf = vmw_framebuffer_to_vfbd(fb)->buffer; - vmw_dmabuf_reference(vps->dmabuf); + if (vmw_framebuffer_to_vfb(fb)->bo) { + vps->bo = vmw_framebuffer_to_vfbd(fb)->buffer; + vmw_bo_reference(vps->bo); } else { vps->surf = vmw_framebuffer_to_vfbs(fb)->surface; vmw_surface_reference(vps->surf); @@ -390,7 +390,7 @@ vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, } du->cursor_surface = vps->surf; - du->cursor_dmabuf = vps->dmabuf; + du->cursor_bo = vps->bo; if (vps->surf) { du->cursor_age = du->cursor_surface->snooper.age; @@ -399,11 +399,11 @@ vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, vps->surf->snooper.image, 64, 64, hotspot_x, hotspot_y); - } else if (vps->dmabuf) { - ret = vmw_cursor_update_dmabuf(dev_priv, vps->dmabuf, - plane->state->crtc_w, - plane->state->crtc_h, - hotspot_x, hotspot_y); + } else if (vps->bo) { + ret = vmw_cursor_update_bo(dev_priv, vps->bo, + plane->state->crtc_w, + plane->state->crtc_h, + hotspot_x, hotspot_y); } else { vmw_cursor_update_position(dev_priv, false, 0, 0); return; @@ -519,7 +519,7 @@ int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, ret = -EINVAL; } - if (!vmw_framebuffer_to_vfb(fb)->dmabuf) + if (!vmw_framebuffer_to_vfb(fb)->bo) surface = vmw_framebuffer_to_vfbs(fb)->surface; if (surface && !surface->snooper.image) { @@ -687,8 +687,8 @@ vmw_du_plane_duplicate_state(struct drm_plane *plane) if (vps->surf) (void) vmw_surface_reference(vps->surf); - if (vps->dmabuf) - (void) vmw_dmabuf_reference(vps->dmabuf); + if (vps->bo) + (void) vmw_bo_reference(vps->bo); state = &vps->base; @@ -745,8 +745,8 @@ vmw_du_plane_destroy_state(struct drm_plane *plane, if (vps->surf) vmw_surface_unreference(&vps->surf); - if (vps->dmabuf) - vmw_dmabuf_unreference(&vps->dmabuf); + if (vps->bo) + vmw_bo_unreference(&vps->bo); drm_atomic_helper_plane_destroy_state(plane, state); } @@ -902,12 +902,12 @@ static int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, /** * vmw_kms_readback - Perform a readback from the screen system to - * a dma-buffer backed framebuffer. + * a buffer-object backed framebuffer. * * @dev_priv: Pointer to the device private structure. * @file_priv: Pointer to a struct drm_file identifying the caller. * Must be set to NULL if @user_fence_rep is NULL. - * @vfb: Pointer to the dma-buffer backed framebuffer. + * @vfb: Pointer to the buffer-object backed framebuffer. * @user_fence_rep: User-space provided structure for fence information. * Must be set to non-NULL if @file_priv is non-NULL. * @vclips: Array of clip rects. @@ -951,7 +951,7 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, struct vmw_framebuffer **out, const struct drm_mode_fb_cmd2 *mode_cmd, - bool is_dmabuf_proxy) + bool is_bo_proxy) { struct drm_device *dev = dev_priv->dev; @@ -1019,7 +1019,7 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, drm_helper_mode_fill_fb_struct(dev, &vfbs->base.base, mode_cmd); vfbs->surface = vmw_surface_reference(surface); vfbs->base.user_handle = mode_cmd->handles[0]; - vfbs->is_dmabuf_proxy = is_dmabuf_proxy; + vfbs->is_bo_proxy = is_bo_proxy; *out = &vfbs->base; @@ -1038,30 +1038,30 @@ out_err1: } /* - * Dmabuf framebuffer code + * Buffer-object framebuffer code */ -static void vmw_framebuffer_dmabuf_destroy(struct drm_framebuffer *framebuffer) +static void vmw_framebuffer_bo_destroy(struct drm_framebuffer *framebuffer) { - struct vmw_framebuffer_dmabuf *vfbd = + struct vmw_framebuffer_bo *vfbd = vmw_framebuffer_to_vfbd(framebuffer); drm_framebuffer_cleanup(framebuffer); - vmw_dmabuf_unreference(&vfbd->buffer); + vmw_bo_unreference(&vfbd->buffer); if (vfbd->base.user_obj) ttm_base_object_unref(&vfbd->base.user_obj); kfree(vfbd); } -static int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, - struct drm_file *file_priv, - unsigned flags, unsigned color, - struct drm_clip_rect *clips, - unsigned num_clips) +static int vmw_framebuffer_bo_dirty(struct drm_framebuffer *framebuffer, + struct drm_file *file_priv, + unsigned int flags, unsigned int color, + struct drm_clip_rect *clips, + unsigned int num_clips) { struct vmw_private *dev_priv = vmw_priv(framebuffer->dev); - struct vmw_framebuffer_dmabuf *vfbd = + struct vmw_framebuffer_bo *vfbd = vmw_framebuffer_to_vfbd(framebuffer); struct drm_clip_rect norect; int ret, increment = 1; @@ -1092,13 +1092,13 @@ static int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, true, true, NULL); break; case vmw_du_screen_object: - ret = vmw_kms_sou_do_dmabuf_dirty(dev_priv, &vfbd->base, - clips, NULL, num_clips, - increment, true, NULL, NULL); + ret = vmw_kms_sou_do_bo_dirty(dev_priv, &vfbd->base, + clips, NULL, num_clips, + increment, true, NULL, NULL); break; case vmw_du_legacy: - ret = vmw_kms_ldu_do_dmabuf_dirty(dev_priv, &vfbd->base, 0, 0, - clips, num_clips, increment); + ret = vmw_kms_ldu_do_bo_dirty(dev_priv, &vfbd->base, 0, 0, + clips, num_clips, increment); break; default: ret = -EINVAL; @@ -1114,23 +1114,23 @@ static int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, return ret; } -static const struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = { - .destroy = vmw_framebuffer_dmabuf_destroy, - .dirty = vmw_framebuffer_dmabuf_dirty, +static const struct drm_framebuffer_funcs vmw_framebuffer_bo_funcs = { + .destroy = vmw_framebuffer_bo_destroy, + .dirty = vmw_framebuffer_bo_dirty, }; /** - * Pin the dmabuffer in a location suitable for access by the + * Pin the bofer in a location suitable for access by the * display system. */ static int vmw_framebuffer_pin(struct vmw_framebuffer *vfb) { struct vmw_private *dev_priv = vmw_priv(vfb->base.dev); - struct vmw_dma_buffer *buf; + struct vmw_buffer_object *buf; struct ttm_placement *placement; int ret; - buf = vfb->dmabuf ? vmw_framebuffer_to_vfbd(&vfb->base)->buffer : + buf = vfb->bo ? vmw_framebuffer_to_vfbd(&vfb->base)->buffer : vmw_framebuffer_to_vfbs(&vfb->base)->surface->res.backup; if (!buf) @@ -1139,12 +1139,12 @@ static int vmw_framebuffer_pin(struct vmw_framebuffer *vfb) switch (dev_priv->active_display_unit) { case vmw_du_legacy: vmw_overlay_pause_all(dev_priv); - ret = vmw_dmabuf_pin_in_start_of_vram(dev_priv, buf, false); + ret = vmw_bo_pin_in_start_of_vram(dev_priv, buf, false); vmw_overlay_resume_all(dev_priv); break; case vmw_du_screen_object: case vmw_du_screen_target: - if (vfb->dmabuf) { + if (vfb->bo) { if (dev_priv->capabilities & SVGA_CAP_3D) { /* * Use surface DMA to get content to @@ -1160,8 +1160,7 @@ static int vmw_framebuffer_pin(struct vmw_framebuffer *vfb) placement = &vmw_mob_placement; } - return vmw_dmabuf_pin_in_placement(dev_priv, buf, placement, - false); + return vmw_bo_pin_in_placement(dev_priv, buf, placement, false); default: return -EINVAL; } @@ -1172,36 +1171,36 @@ static int vmw_framebuffer_pin(struct vmw_framebuffer *vfb) static int vmw_framebuffer_unpin(struct vmw_framebuffer *vfb) { struct vmw_private *dev_priv = vmw_priv(vfb->base.dev); - struct vmw_dma_buffer *buf; + struct vmw_buffer_object *buf; - buf = vfb->dmabuf ? vmw_framebuffer_to_vfbd(&vfb->base)->buffer : + buf = vfb->bo ? vmw_framebuffer_to_vfbd(&vfb->base)->buffer : vmw_framebuffer_to_vfbs(&vfb->base)->surface->res.backup; if (WARN_ON(!buf)) return 0; - return vmw_dmabuf_unpin(dev_priv, buf, false); + return vmw_bo_unpin(dev_priv, buf, false); } /** - * vmw_create_dmabuf_proxy - create a proxy surface for the DMA buf + * vmw_create_bo_proxy - create a proxy surface for the buffer object * * @dev: DRM device * @mode_cmd: parameters for the new surface - * @dmabuf_mob: MOB backing the DMA buf + * @bo_mob: MOB backing the buffer object * @srf_out: newly created surface * - * When the content FB is a DMA buf, we create a surface as a proxy to the + * When the content FB is a buffer object, we create a surface as a proxy to the * same buffer. This way we can do a surface copy rather than a surface DMA. * This is a more efficient approach * * RETURNS: * 0 on success, error code otherwise */ -static int vmw_create_dmabuf_proxy(struct drm_device *dev, - const struct drm_mode_fb_cmd2 *mode_cmd, - struct vmw_dma_buffer *dmabuf_mob, - struct vmw_surface **srf_out) +static int vmw_create_bo_proxy(struct drm_device *dev, + const struct drm_mode_fb_cmd2 *mode_cmd, + struct vmw_buffer_object *bo_mob, + struct vmw_surface **srf_out) { uint32_t format; struct drm_vmw_size content_base_size = {0}; @@ -1258,8 +1257,8 @@ static int vmw_create_dmabuf_proxy(struct drm_device *dev, /* Reserve and switch the backing mob. */ mutex_lock(&res->dev_priv->cmdbuf_mutex); (void) vmw_resource_reserve(res, false, true); - vmw_dmabuf_unreference(&res->backup); - res->backup = vmw_dmabuf_reference(dmabuf_mob); + vmw_bo_unreference(&res->backup); + res->backup = vmw_bo_reference(bo_mob); res->backup_offset = 0; vmw_resource_unreserve(res, false, NULL, 0); mutex_unlock(&res->dev_priv->cmdbuf_mutex); @@ -1269,21 +1268,21 @@ static int vmw_create_dmabuf_proxy(struct drm_device *dev, -static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, - struct vmw_dma_buffer *dmabuf, - struct vmw_framebuffer **out, - const struct drm_mode_fb_cmd2 - *mode_cmd) +static int vmw_kms_new_framebuffer_bo(struct vmw_private *dev_priv, + struct vmw_buffer_object *bo, + struct vmw_framebuffer **out, + const struct drm_mode_fb_cmd2 + *mode_cmd) { struct drm_device *dev = dev_priv->dev; - struct vmw_framebuffer_dmabuf *vfbd; + struct vmw_framebuffer_bo *vfbd; unsigned int requested_size; struct drm_format_name_buf format_name; int ret; requested_size = mode_cmd->height * mode_cmd->pitches[0]; - if (unlikely(requested_size > dmabuf->base.num_pages * PAGE_SIZE)) { + if (unlikely(requested_size > bo->base.num_pages * PAGE_SIZE)) { DRM_ERROR("Screen buffer object size is too small " "for requested mode.\n"); return -EINVAL; @@ -1312,20 +1311,20 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, } drm_helper_mode_fill_fb_struct(dev, &vfbd->base.base, mode_cmd); - vfbd->base.dmabuf = true; - vfbd->buffer = vmw_dmabuf_reference(dmabuf); + vfbd->base.bo = true; + vfbd->buffer = vmw_bo_reference(bo); vfbd->base.user_handle = mode_cmd->handles[0]; *out = &vfbd->base; ret = drm_framebuffer_init(dev, &vfbd->base.base, - &vmw_framebuffer_dmabuf_funcs); + &vmw_framebuffer_bo_funcs); if (ret) goto out_err2; return 0; out_err2: - vmw_dmabuf_unreference(&dmabuf); + vmw_bo_unreference(&bo); kfree(vfbd); out_err1: return ret; @@ -1354,57 +1353,57 @@ vmw_kms_srf_ok(struct vmw_private *dev_priv, uint32_t width, uint32_t height) * vmw_kms_new_framebuffer - Create a new framebuffer. * * @dev_priv: Pointer to device private struct. - * @dmabuf: Pointer to dma buffer to wrap the kms framebuffer around. - * Either @dmabuf or @surface must be NULL. + * @bo: Pointer to buffer object to wrap the kms framebuffer around. + * Either @bo or @surface must be NULL. * @surface: Pointer to a surface to wrap the kms framebuffer around. - * Either @dmabuf or @surface must be NULL. - * @only_2d: No presents will occur to this dma buffer based framebuffer. This - * Helps the code to do some important optimizations. + * Either @bo or @surface must be NULL. + * @only_2d: No presents will occur to this buffer object based framebuffer. + * This helps the code to do some important optimizations. * @mode_cmd: Frame-buffer metadata. */ struct vmw_framebuffer * vmw_kms_new_framebuffer(struct vmw_private *dev_priv, - struct vmw_dma_buffer *dmabuf, + struct vmw_buffer_object *bo, struct vmw_surface *surface, bool only_2d, const struct drm_mode_fb_cmd2 *mode_cmd) { struct vmw_framebuffer *vfb = NULL; - bool is_dmabuf_proxy = false; + bool is_bo_proxy = false; int ret; /* * We cannot use the SurfaceDMA command in an non-accelerated VM, - * therefore, wrap the DMA buf in a surface so we can use the + * therefore, wrap the buffer object in a surface so we can use the * SurfaceCopy command. */ if (vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height) && - dmabuf && only_2d && + bo && only_2d && mode_cmd->width > 64 && /* Don't create a proxy for cursor */ dev_priv->active_display_unit == vmw_du_screen_target) { - ret = vmw_create_dmabuf_proxy(dev_priv->dev, mode_cmd, - dmabuf, &surface); + ret = vmw_create_bo_proxy(dev_priv->dev, mode_cmd, + bo, &surface); if (ret) return ERR_PTR(ret); - is_dmabuf_proxy = true; + is_bo_proxy = true; } /* Create the new framebuffer depending one what we have */ if (surface) { ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb, mode_cmd, - is_dmabuf_proxy); + is_bo_proxy); /* - * vmw_create_dmabuf_proxy() adds a reference that is no longer + * vmw_create_bo_proxy() adds a reference that is no longer * needed */ - if (is_dmabuf_proxy) + if (is_bo_proxy) vmw_surface_unreference(&surface); - } else if (dmabuf) { - ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, dmabuf, &vfb, - mode_cmd); + } else if (bo) { + ret = vmw_kms_new_framebuffer_bo(dev_priv, bo, &vfb, + mode_cmd); } else { BUG(); } @@ -1430,7 +1429,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; struct vmw_framebuffer *vfb = NULL; struct vmw_surface *surface = NULL; - struct vmw_dma_buffer *bo = NULL; + struct vmw_buffer_object *bo = NULL; struct ttm_base_object *user_obj; int ret; @@ -1466,7 +1465,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, * End conditioned code. */ - /* returns either a dmabuf or surface */ + /* returns either a bo or surface */ ret = vmw_user_lookup_handle(dev_priv, tfile, mode_cmd->handles[0], &surface, &bo); @@ -1494,7 +1493,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, err_out: /* vmw_user_lookup_handle takes one ref so does new_fb */ if (bo) - vmw_dmabuf_unreference(&bo); + vmw_bo_unreference(&bo); if (surface) vmw_surface_unreference(&surface); @@ -2427,7 +2426,7 @@ int vmw_kms_helper_dirty(struct vmw_private *dev_priv, * interrupted by a signal. */ int vmw_kms_helper_buffer_prepare(struct vmw_private *dev_priv, - struct vmw_dma_buffer *buf, + struct vmw_buffer_object *buf, bool interruptible, bool validate_as_mob, bool for_cpu_blit) @@ -2459,7 +2458,7 @@ int vmw_kms_helper_buffer_prepare(struct vmw_private *dev_priv, * Helper to be used if an error forces the caller to undo the actions of * vmw_kms_helper_buffer_prepare. */ -void vmw_kms_helper_buffer_revert(struct vmw_dma_buffer *buf) +void vmw_kms_helper_buffer_revert(struct vmw_buffer_object *buf) { if (buf) ttm_bo_unreserve(&buf->base); @@ -2482,7 +2481,7 @@ void vmw_kms_helper_buffer_revert(struct vmw_dma_buffer *buf) */ void vmw_kms_helper_buffer_finish(struct vmw_private *dev_priv, struct drm_file *file_priv, - struct vmw_dma_buffer *buf, + struct vmw_buffer_object *buf, struct vmw_fence_obj **out_fence, struct drm_vmw_fence_rep __user * user_fence_rep) @@ -2522,7 +2521,7 @@ void vmw_kms_helper_resource_revert(struct vmw_validation_ctx *ctx) struct vmw_resource *res = ctx->res; vmw_kms_helper_buffer_revert(ctx->buf); - vmw_dmabuf_unreference(&ctx->buf); + vmw_bo_unreference(&ctx->buf); vmw_resource_unreserve(res, false, NULL, 0); mutex_unlock(&res->dev_priv->cmdbuf_mutex); } @@ -2567,7 +2566,7 @@ int vmw_kms_helper_resource_prepare(struct vmw_resource *res, if (ret) goto out_unreserve; - ctx->buf = vmw_dmabuf_reference(res->backup); + ctx->buf = vmw_bo_reference(res->backup); } ret = vmw_resource_validate(res); if (ret) @@ -2600,7 +2599,7 @@ void vmw_kms_helper_resource_finish(struct vmw_validation_ctx *ctx, vmw_kms_helper_buffer_finish(res->dev_priv, NULL, ctx->buf, out_fence, NULL); - vmw_dmabuf_unreference(&ctx->buf); + vmw_bo_unreference(&ctx->buf); vmw_resource_unreserve(res, false, NULL, 0); mutex_unlock(&res->dev_priv->cmdbuf_mutex); } -- cgit v1.2.3 From e9431ea5076a913a3b350cf5f89eacf9375126b1 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 19 Jun 2018 15:33:53 +0200 Subject: drm/vmwgfx: Move buffer object related code to vmwgfx_bo.c It makes more sense to have all the buffer object related code in a single file rather than splitting it up between the resource code and buffer object pinning utilities. Place all buffer object related code in vmwgfx_bo.c. Fix up headers and export resource functionality when needed in the buffer object code. Signed-off-by: Thomas Hellstrom Reviewed-by: Brian Paul Reviewed-by: Sinclair Yeh Reviewed-by: Deepak Rawat --- drivers/gpu/drm/vmwgfx/vmwgfx_bo.c | 816 +++++++++++++++++++++++++++-- drivers/gpu/drm/vmwgfx/vmwgfx_context.c | 4 +- drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c | 4 +- drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 73 ++- drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_mob.c | 6 +- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 640 +--------------------- drivers/gpu/drm/vmwgfx/vmwgfx_shader.c | 4 +- drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 4 +- drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c | 4 +- 11 files changed, 851 insertions(+), 708 deletions(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_kms.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c index f26f658cccdb..d950244798fe 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c @@ -1,6 +1,6 @@ /************************************************************************** * - * Copyright © 2011-2015 VMware, Inc., Palo Alto, CA., USA + * Copyright © 2011-2018 VMware, Inc., Palo Alto, CA., USA * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -29,6 +29,51 @@ #include #include "vmwgfx_drv.h" +#include "drm/ttm/ttm_object.h" + + +/** + * struct vmw_user_buffer_object - User-space-visible buffer object + * + * @prime: The prime object providing user visibility. + * @vbo: The struct vmw_buffer_object + */ +struct vmw_user_buffer_object { + struct ttm_prime_object prime; + struct vmw_buffer_object vbo; +}; + + +/** + * vmw_buffer_object - Convert a struct ttm_buffer_object to a struct + * vmw_buffer_object. + * + * @bo: Pointer to the TTM buffer object. + * Return: Pointer to the struct vmw_buffer_object embedding the + * TTM buffer object. + */ +static struct vmw_buffer_object * +vmw_buffer_object(struct ttm_buffer_object *bo) +{ + return container_of(bo, struct vmw_buffer_object, base); +} + + +/** + * vmw_user_buffer_object - Convert a struct ttm_buffer_object to a struct + * vmw_user_buffer_object. + * + * @bo: Pointer to the TTM buffer object. + * Return: Pointer to the struct vmw_buffer_object embedding the TTM buffer + * object. + */ +static struct vmw_user_buffer_object * +vmw_user_buffer_object(struct ttm_buffer_object *bo) +{ + struct vmw_buffer_object *vmw_bo = vmw_buffer_object(bo); + + return container_of(vmw_bo, struct vmw_user_buffer_object, vbo); +} /** @@ -38,9 +83,8 @@ * @buf: DMA buffer to move. * @placement: The placement to pin it. * @interruptible: Use interruptible wait. - * - * Returns - * -ERESTARTSYS if interrupted by a signal. + * Return: Zero on success, Negative error code on failure. In particular + * -ERESTARTSYS if interrupted by a signal */ int vmw_bo_pin_in_placement(struct vmw_private *dev_priv, struct vmw_buffer_object *buf, @@ -78,6 +122,7 @@ err: return ret; } + /** * vmw_bo_pin_in_vram_or_gmr - Move a buffer to vram or gmr. * @@ -88,9 +133,8 @@ err: * @buf: DMA buffer to move. * @pin: Pin buffer if true. * @interruptible: Use interruptible wait. - * - * Returns - * -ERESTARTSYS if interrupted by a signal. + * Return: Zero on success, Negative error code on failure. In particular + * -ERESTARTSYS if interrupted by a signal */ int vmw_bo_pin_in_vram_or_gmr(struct vmw_private *dev_priv, struct vmw_buffer_object *buf, @@ -133,6 +177,7 @@ err: return ret; } + /** * vmw_bo_pin_in_vram - Move a buffer to vram. * @@ -142,9 +187,8 @@ err: * @dev_priv: Driver private. * @buf: DMA buffer to move. * @interruptible: Use interruptible wait. - * - * Returns - * -ERESTARTSYS if interrupted by a signal. + * Return: Zero on success, Negative error code on failure. In particular + * -ERESTARTSYS if interrupted by a signal */ int vmw_bo_pin_in_vram(struct vmw_private *dev_priv, struct vmw_buffer_object *buf, @@ -154,6 +198,7 @@ int vmw_bo_pin_in_vram(struct vmw_private *dev_priv, interruptible); } + /** * vmw_bo_pin_in_start_of_vram - Move a buffer to start of vram. * @@ -163,9 +208,8 @@ int vmw_bo_pin_in_vram(struct vmw_private *dev_priv, * @dev_priv: Driver private. * @buf: DMA buffer to pin. * @interruptible: Use interruptible wait. - * - * Returns - * -ERESTARTSYS if interrupted by a signal. + * Return: Zero on success, Negative error code on failure. In particular + * -ERESTARTSYS if interrupted by a signal */ int vmw_bo_pin_in_start_of_vram(struct vmw_private *dev_priv, struct vmw_buffer_object *buf, @@ -225,6 +269,7 @@ err_unlock: return ret; } + /** * vmw_bo_unpin - Unpin the buffer given buffer, does not move the buffer. * @@ -233,9 +278,8 @@ err_unlock: * @dev_priv: Driver private. * @buf: DMA buffer to unpin. * @interruptible: Use interruptible wait. - * - * Returns - * -ERESTARTSYS if interrupted by a signal. + * Return: Zero on success, Negative error code on failure. In particular + * -ERESTARTSYS if interrupted by a signal */ int vmw_bo_unpin(struct vmw_private *dev_priv, struct vmw_buffer_object *buf, @@ -325,25 +369,8 @@ void vmw_bo_pin_reserved(struct vmw_buffer_object *vbo, bool pin) } -/* - * vmw_buffer_object_unmap - Tear down a cached buffer object map. - * - * @vbo: The buffer object whose map we are tearing down. - * - * This function tears down a cached map set up using - * vmw_buffer_object_map_and_cache(). - */ -void vmw_buffer_object_unmap(struct vmw_buffer_object *vbo) -{ - if (vbo->map.bo == NULL) - return; - - ttm_bo_kunmap(&vbo->map); -} - - -/* - * vmw_buffer_object_map_and_cache - Map a buffer object and cache the map +/** + * vmw_bo_map_and_cache - Map a buffer object and cache the map * * @vbo: The buffer object to map * Return: A kernel virtual address or NULL if mapping failed. @@ -357,7 +384,7 @@ void vmw_buffer_object_unmap(struct vmw_buffer_object *vbo) * 3) Buffer object destruction * */ -void *vmw_buffer_object_map_and_cache(struct vmw_buffer_object *vbo) +void *vmw_bo_map_and_cache(struct vmw_buffer_object *vbo) { struct ttm_buffer_object *bo = &vbo->base; bool not_used; @@ -374,3 +401,720 @@ void *vmw_buffer_object_map_and_cache(struct vmw_buffer_object *vbo) return ttm_kmap_obj_virtual(&vbo->map, ¬_used); } + + +/** + * vmw_bo_unmap - Tear down a cached buffer object map. + * + * @vbo: The buffer object whose map we are tearing down. + * + * This function tears down a cached map set up using + * vmw_buffer_object_map_and_cache(). + */ +void vmw_bo_unmap(struct vmw_buffer_object *vbo) +{ + if (vbo->map.bo == NULL) + return; + + ttm_bo_kunmap(&vbo->map); +} + + +/** + * vmw_bo_acc_size - Calculate the pinned memory usage of buffers + * + * @dev_priv: Pointer to a struct vmw_private identifying the device. + * @size: The requested buffer size. + * @user: Whether this is an ordinary dma buffer or a user dma buffer. + */ +static size_t vmw_bo_acc_size(struct vmw_private *dev_priv, size_t size, + bool user) +{ + static size_t struct_size, user_struct_size; + size_t num_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; + size_t page_array_size = ttm_round_pot(num_pages * sizeof(void *)); + + if (unlikely(struct_size == 0)) { + size_t backend_size = ttm_round_pot(vmw_tt_size); + + struct_size = backend_size + + ttm_round_pot(sizeof(struct vmw_buffer_object)); + user_struct_size = backend_size + + ttm_round_pot(sizeof(struct vmw_user_buffer_object)); + } + + if (dev_priv->map_mode == vmw_dma_alloc_coherent) + page_array_size += + ttm_round_pot(num_pages * sizeof(dma_addr_t)); + + return ((user) ? user_struct_size : struct_size) + + page_array_size; +} + + +/** + * vmw_bo_bo_free - vmw buffer object destructor + * + * @bo: Pointer to the embedded struct ttm_buffer_object + */ +void vmw_bo_bo_free(struct ttm_buffer_object *bo) +{ + struct vmw_buffer_object *vmw_bo = vmw_buffer_object(bo); + + vmw_bo_unmap(vmw_bo); + kfree(vmw_bo); +} + + +/** + * vmw_user_bo_destroy - vmw buffer object destructor + * + * @bo: Pointer to the embedded struct ttm_buffer_object + */ +static void vmw_user_bo_destroy(struct ttm_buffer_object *bo) +{ + struct vmw_user_buffer_object *vmw_user_bo = vmw_user_buffer_object(bo); + + vmw_bo_unmap(&vmw_user_bo->vbo); + ttm_prime_object_kfree(vmw_user_bo, prime); +} + + +/** + * vmw_bo_init - Initialize a vmw buffer object + * + * @dev_priv: Pointer to the device private struct + * @vmw_bo: Pointer to the struct vmw_buffer_object to initialize. + * @size: Buffer object size in bytes. + * @placement: Initial placement. + * @interruptible: Whether waits should be performed interruptible. + * @bo_free: The buffer object destructor. + * Returns: Zero on success, negative error code on error. + * + * Note that on error, the code will free the buffer object. + */ +int vmw_bo_init(struct vmw_private *dev_priv, + struct vmw_buffer_object *vmw_bo, + size_t size, struct ttm_placement *placement, + bool interruptible, + void (*bo_free)(struct ttm_buffer_object *bo)) +{ + struct ttm_bo_device *bdev = &dev_priv->bdev; + size_t acc_size; + int ret; + bool user = (bo_free == &vmw_user_bo_destroy); + + WARN_ON_ONCE(!bo_free && (!user && (bo_free != vmw_bo_bo_free))); + + acc_size = vmw_bo_acc_size(dev_priv, size, user); + memset(vmw_bo, 0, sizeof(*vmw_bo)); + + INIT_LIST_HEAD(&vmw_bo->res_list); + + ret = ttm_bo_init(bdev, &vmw_bo->base, size, + ttm_bo_type_device, placement, + 0, interruptible, acc_size, + NULL, NULL, bo_free); + return ret; +} + + +/** + * vmw_user_bo_release - TTM reference base object release callback for + * vmw user buffer objects + * + * @p_base: The TTM base object pointer about to be unreferenced. + * + * Clears the TTM base object pointer and drops the reference the + * base object has on the underlying struct vmw_buffer_object. + */ +static void vmw_user_bo_release(struct ttm_base_object **p_base) +{ + struct vmw_user_buffer_object *vmw_user_bo; + struct ttm_base_object *base = *p_base; + struct ttm_buffer_object *bo; + + *p_base = NULL; + + if (unlikely(base == NULL)) + return; + + vmw_user_bo = container_of(base, struct vmw_user_buffer_object, + prime.base); + bo = &vmw_user_bo->vbo.base; + ttm_bo_unref(&bo); +} + + +/** + * vmw_user_bo_ref_obj-release - TTM synccpu reference object release callback + * for vmw user buffer objects + * + * @base: Pointer to the TTM base object + * @ref_type: Reference type of the reference reaching zero. + * + * Called when user-space drops its last synccpu reference on the buffer + * object, Either explicitly or as part of a cleanup file close. + */ +static void vmw_user_bo_ref_obj_release(struct ttm_base_object *base, + enum ttm_ref_type ref_type) +{ + struct vmw_user_buffer_object *user_bo; + + user_bo = container_of(base, struct vmw_user_buffer_object, prime.base); + + switch (ref_type) { + case TTM_REF_SYNCCPU_WRITE: + ttm_bo_synccpu_write_release(&user_bo->vbo.base); + break; + default: + WARN_ONCE(true, "Undefined buffer object reference release.\n"); + } +} + + +/** + * vmw_user_bo_alloc - Allocate a user buffer object + * + * @dev_priv: Pointer to a struct device private. + * @tfile: Pointer to a struct ttm_object_file on which to register the user + * object. + * @size: Size of the buffer object. + * @shareable: Boolean whether the buffer is shareable with other open files. + * @handle: Pointer to where the handle value should be assigned. + * @p_vbo: Pointer to where the refcounted struct vmw_buffer_object pointer + * should be assigned. + * Return: Zero on success, negative error code on error. + */ +int vmw_user_bo_alloc(struct vmw_private *dev_priv, + struct ttm_object_file *tfile, + uint32_t size, + bool shareable, + uint32_t *handle, + struct vmw_buffer_object **p_vbo, + struct ttm_base_object **p_base) +{ + struct vmw_user_buffer_object *user_bo; + struct ttm_buffer_object *tmp; + int ret; + + user_bo = kzalloc(sizeof(*user_bo), GFP_KERNEL); + if (unlikely(!user_bo)) { + DRM_ERROR("Failed to allocate a buffer.\n"); + return -ENOMEM; + } + + ret = vmw_bo_init(dev_priv, &user_bo->vbo, size, + (dev_priv->has_mob) ? + &vmw_sys_placement : + &vmw_vram_sys_placement, true, + &vmw_user_bo_destroy); + if (unlikely(ret != 0)) + return ret; + + tmp = ttm_bo_reference(&user_bo->vbo.base); + ret = ttm_prime_object_init(tfile, + size, + &user_bo->prime, + shareable, + ttm_buffer_type, + &vmw_user_bo_release, + &vmw_user_bo_ref_obj_release); + if (unlikely(ret != 0)) { + ttm_bo_unref(&tmp); + goto out_no_base_object; + } + + *p_vbo = &user_bo->vbo; + if (p_base) { + *p_base = &user_bo->prime.base; + kref_get(&(*p_base)->refcount); + } + *handle = user_bo->prime.base.hash.key; + +out_no_base_object: + return ret; +} + + +/** + * vmw_user_bo_verify_access - verify access permissions on this + * buffer object. + * + * @bo: Pointer to the buffer object being accessed + * @tfile: Identifying the caller. + */ +int vmw_user_bo_verify_access(struct ttm_buffer_object *bo, + struct ttm_object_file *tfile) +{ + struct vmw_user_buffer_object *vmw_user_bo; + + if (unlikely(bo->destroy != vmw_user_bo_destroy)) + return -EPERM; + + vmw_user_bo = vmw_user_buffer_object(bo); + + /* Check that the caller has opened the object. */ + if (likely(ttm_ref_object_exists(tfile, &vmw_user_bo->prime.base))) + return 0; + + DRM_ERROR("Could not grant buffer access.\n"); + return -EPERM; +} + + +/** + * vmw_user_bo_synccpu_grab - Grab a struct vmw_user_buffer_object for cpu + * access, idling previous GPU operations on the buffer and optionally + * blocking it for further command submissions. + * + * @user_bo: Pointer to the buffer object being grabbed for CPU access + * @tfile: Identifying the caller. + * @flags: Flags indicating how the grab should be performed. + * Return: Zero on success, Negative error code on error. In particular, + * -EBUSY will be returned if a dontblock operation is requested and the + * buffer object is busy, and -ERESTARTSYS will be returned if a wait is + * interrupted by a signal. + * + * A blocking grab will be automatically released when @tfile is closed. + */ +static int vmw_user_bo_synccpu_grab(struct vmw_user_buffer_object *user_bo, + struct ttm_object_file *tfile, + uint32_t flags) +{ + struct ttm_buffer_object *bo = &user_bo->vbo.base; + bool existed; + int ret; + + if (flags & drm_vmw_synccpu_allow_cs) { + bool nonblock = !!(flags & drm_vmw_synccpu_dontblock); + long lret; + + lret = reservation_object_wait_timeout_rcu + (bo->resv, true, true, + nonblock ? 0 : MAX_SCHEDULE_TIMEOUT); + if (!lret) + return -EBUSY; + else if (lret < 0) + return lret; + return 0; + } + + ret = ttm_bo_synccpu_write_grab + (bo, !!(flags & drm_vmw_synccpu_dontblock)); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_ref_object_add(tfile, &user_bo->prime.base, + TTM_REF_SYNCCPU_WRITE, &existed, false); + if (ret != 0 || existed) + ttm_bo_synccpu_write_release(&user_bo->vbo.base); + + return ret; +} + +/** + * vmw_user_bo_synccpu_release - Release a previous grab for CPU access, + * and unblock command submission on the buffer if blocked. + * + * @handle: Handle identifying the buffer object. + * @tfile: Identifying the caller. + * @flags: Flags indicating the type of release. + */ +static int vmw_user_bo_synccpu_release(uint32_t handle, + struct ttm_object_file *tfile, + uint32_t flags) +{ + if (!(flags & drm_vmw_synccpu_allow_cs)) + return ttm_ref_object_base_unref(tfile, handle, + TTM_REF_SYNCCPU_WRITE); + + return 0; +} + + +/** + * vmw_user_bo_synccpu_ioctl - ioctl function implementing the synccpu + * functionality. + * + * @dev: Identifies the drm device. + * @data: Pointer to the ioctl argument. + * @file_priv: Identifies the caller. + * Return: Zero on success, negative error code on error. + * + * This function checks the ioctl arguments for validity and calls the + * relevant synccpu functions. + */ +int vmw_user_bo_synccpu_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_synccpu_arg *arg = + (struct drm_vmw_synccpu_arg *) data; + struct vmw_buffer_object *vbo; + struct vmw_user_buffer_object *user_bo; + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct ttm_base_object *buffer_base; + int ret; + + if ((arg->flags & (drm_vmw_synccpu_read | drm_vmw_synccpu_write)) == 0 + || (arg->flags & ~(drm_vmw_synccpu_read | drm_vmw_synccpu_write | + drm_vmw_synccpu_dontblock | + drm_vmw_synccpu_allow_cs)) != 0) { + DRM_ERROR("Illegal synccpu flags.\n"); + return -EINVAL; + } + + switch (arg->op) { + case drm_vmw_synccpu_grab: + ret = vmw_user_bo_lookup(tfile, arg->handle, &vbo, + &buffer_base); + if (unlikely(ret != 0)) + return ret; + + user_bo = container_of(vbo, struct vmw_user_buffer_object, + vbo); + ret = vmw_user_bo_synccpu_grab(user_bo, tfile, arg->flags); + vmw_bo_unreference(&vbo); + ttm_base_object_unref(&buffer_base); + if (unlikely(ret != 0 && ret != -ERESTARTSYS && + ret != -EBUSY)) { + DRM_ERROR("Failed synccpu grab on handle 0x%08x.\n", + (unsigned int) arg->handle); + return ret; + } + break; + case drm_vmw_synccpu_release: + ret = vmw_user_bo_synccpu_release(arg->handle, tfile, + arg->flags); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed synccpu release on handle 0x%08x.\n", + (unsigned int) arg->handle); + return ret; + } + break; + default: + DRM_ERROR("Invalid synccpu operation.\n"); + return -EINVAL; + } + + return 0; +} + + +/** + * vmw_bo_alloc_ioctl - ioctl function implementing the buffer object + * allocation functionality. + * + * @dev: Identifies the drm device. + * @data: Pointer to the ioctl argument. + * @file_priv: Identifies the caller. + * Return: Zero on success, negative error code on error. + * + * This function checks the ioctl arguments for validity and allocates a + * struct vmw_user_buffer_object bo. + */ +int vmw_bo_alloc_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + union drm_vmw_alloc_dmabuf_arg *arg = + (union drm_vmw_alloc_dmabuf_arg *)data; + struct drm_vmw_alloc_dmabuf_req *req = &arg->req; + struct drm_vmw_dmabuf_rep *rep = &arg->rep; + struct vmw_buffer_object *vbo; + uint32_t handle; + int ret; + + ret = ttm_read_lock(&dev_priv->reservation_sem, true); + if (unlikely(ret != 0)) + return ret; + + ret = vmw_user_bo_alloc(dev_priv, vmw_fpriv(file_priv)->tfile, + req->size, false, &handle, &vbo, + NULL); + if (unlikely(ret != 0)) + goto out_no_bo; + + rep->handle = handle; + rep->map_handle = drm_vma_node_offset_addr(&vbo->base.vma_node); + rep->cur_gmr_id = handle; + rep->cur_gmr_offset = 0; + + vmw_bo_unreference(&vbo); + +out_no_bo: + ttm_read_unlock(&dev_priv->reservation_sem); + + return ret; +} + + +/** + * vmw_bo_unref_ioctl - Generic handle close ioctl. + * + * @dev: Identifies the drm device. + * @data: Pointer to the ioctl argument. + * @file_priv: Identifies the caller. + * Return: Zero on success, negative error code on error. + * + * This function checks the ioctl arguments for validity and closes a + * handle to a TTM base object, optionally freeing the object. + */ +int vmw_bo_unref_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_unref_dmabuf_arg *arg = + (struct drm_vmw_unref_dmabuf_arg *)data; + + return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, + arg->handle, + TTM_REF_USAGE); +} + + +/** + * vmw_user_bo_lookup - Look up a vmw user buffer object from a handle. + * + * @tfile: The TTM object file the handle is registered with. + * @handle: The user buffer object handle + * @out: Pointer to a where a pointer to the embedded + * struct vmw_buffer_object should be placed. + * @p_base: Pointer to where a pointer to the TTM base object should be + * placed, or NULL if no such pointer is required. + * Return: Zero on success, Negative error code on error. + * + * Both the output base object pointer and the vmw buffer object pointer + * will be refcounted. + */ +int vmw_user_bo_lookup(struct ttm_object_file *tfile, + uint32_t handle, struct vmw_buffer_object **out, + struct ttm_base_object **p_base) +{ + struct vmw_user_buffer_object *vmw_user_bo; + struct ttm_base_object *base; + + base = ttm_base_object_lookup(tfile, handle); + if (unlikely(base == NULL)) { + DRM_ERROR("Invalid buffer object handle 0x%08lx.\n", + (unsigned long)handle); + return -ESRCH; + } + + if (unlikely(ttm_base_object_type(base) != ttm_buffer_type)) { + ttm_base_object_unref(&base); + DRM_ERROR("Invalid buffer object handle 0x%08lx.\n", + (unsigned long)handle); + return -EINVAL; + } + + vmw_user_bo = container_of(base, struct vmw_user_buffer_object, + prime.base); + (void)ttm_bo_reference(&vmw_user_bo->vbo.base); + if (p_base) + *p_base = base; + else + ttm_base_object_unref(&base); + *out = &vmw_user_bo->vbo; + + return 0; +} + + +/** + * vmw_user_bo_reference - Open a handle to a vmw user buffer object. + * + * @tfile: The TTM object file to register the handle with. + * @vbo: The embedded vmw buffer object. + * @handle: Pointer to where the new handle should be placed. + * Return: Zero on success, Negative error code on error. + */ +int vmw_user_bo_reference(struct ttm_object_file *tfile, + struct vmw_buffer_object *vbo, + uint32_t *handle) +{ + struct vmw_user_buffer_object *user_bo; + + if (vbo->base.destroy != vmw_user_bo_destroy) + return -EINVAL; + + user_bo = container_of(vbo, struct vmw_user_buffer_object, vbo); + + *handle = user_bo->prime.base.hash.key; + return ttm_ref_object_add(tfile, &user_bo->prime.base, + TTM_REF_USAGE, NULL, false); +} + + +/** + * vmw_bo_fence_single - Utility function to fence a single TTM buffer + * object without unreserving it. + * + * @bo: Pointer to the struct ttm_buffer_object to fence. + * @fence: Pointer to the fence. If NULL, this function will + * insert a fence into the command stream.. + * + * Contrary to the ttm_eu version of this function, it takes only + * a single buffer object instead of a list, and it also doesn't + * unreserve the buffer object, which needs to be done separately. + */ +void vmw_bo_fence_single(struct ttm_buffer_object *bo, + struct vmw_fence_obj *fence) +{ + struct ttm_bo_device *bdev = bo->bdev; + + struct vmw_private *dev_priv = + container_of(bdev, struct vmw_private, bdev); + + if (fence == NULL) { + vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); + reservation_object_add_excl_fence(bo->resv, &fence->base); + dma_fence_put(&fence->base); + } else + reservation_object_add_excl_fence(bo->resv, &fence->base); +} + + +/** + * vmw_dumb_create - Create a dumb kms buffer + * + * @file_priv: Pointer to a struct drm_file identifying the caller. + * @dev: Pointer to the drm device. + * @args: Pointer to a struct drm_mode_create_dumb structure + * Return: Zero on success, negative error code on failure. + * + * This is a driver callback for the core drm create_dumb functionality. + * Note that this is very similar to the vmw_bo_alloc ioctl, except + * that the arguments have a different format. + */ +int vmw_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_buffer_object *vbo; + int ret; + + args->pitch = args->width * ((args->bpp + 7) / 8); + args->size = args->pitch * args->height; + + ret = ttm_read_lock(&dev_priv->reservation_sem, true); + if (unlikely(ret != 0)) + return ret; + + ret = vmw_user_bo_alloc(dev_priv, vmw_fpriv(file_priv)->tfile, + args->size, false, &args->handle, + &vbo, NULL); + if (unlikely(ret != 0)) + goto out_no_bo; + + vmw_bo_unreference(&vbo); +out_no_bo: + ttm_read_unlock(&dev_priv->reservation_sem); + return ret; +} + + +/** + * vmw_dumb_map_offset - Return the address space offset of a dumb buffer + * + * @file_priv: Pointer to a struct drm_file identifying the caller. + * @dev: Pointer to the drm device. + * @handle: Handle identifying the dumb buffer. + * @offset: The address space offset returned. + * Return: Zero on success, negative error code on failure. + * + * This is a driver callback for the core drm dumb_map_offset functionality. + */ +int vmw_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *dev, uint32_t handle, + uint64_t *offset) +{ + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_buffer_object *out_buf; + int ret; + + ret = vmw_user_bo_lookup(tfile, handle, &out_buf, NULL); + if (ret != 0) + return -EINVAL; + + *offset = drm_vma_node_offset_addr(&out_buf->base.vma_node); + vmw_bo_unreference(&out_buf); + return 0; +} + + +/** + * vmw_dumb_destroy - Destroy a dumb boffer + * + * @file_priv: Pointer to a struct drm_file identifying the caller. + * @dev: Pointer to the drm device. + * @handle: Handle identifying the dumb buffer. + * Return: Zero on success, negative error code on failure. + * + * This is a driver callback for the core drm dumb_destroy functionality. + */ +int vmw_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle) +{ + return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, + handle, TTM_REF_USAGE); +} + + +/** + * vmw_bo_swap_notify - swapout notify callback. + * + * @bo: The buffer object to be swapped out. + */ +void vmw_bo_swap_notify(struct ttm_buffer_object *bo) +{ + /* Is @bo embedded in a struct vmw_buffer_object? */ + if (bo->destroy != vmw_bo_bo_free && + bo->destroy != vmw_user_bo_destroy) + return; + + /* Kill any cached kernel maps before swapout */ + vmw_bo_unmap(vmw_buffer_object(bo)); +} + + +/** + * vmw_bo_move_notify - TTM move_notify_callback + * + * @bo: The TTM buffer object about to move. + * @mem: The struct ttm_mem_reg indicating to what memory + * region the move is taking place. + * + * Detaches cached maps and device bindings that require that the + * buffer doesn't move. + */ +void vmw_bo_move_notify(struct ttm_buffer_object *bo, + struct ttm_mem_reg *mem) +{ + struct vmw_buffer_object *vbo; + + if (mem == NULL) + return; + + /* Make sure @bo is embedded in a struct vmw_buffer_object? */ + if (bo->destroy != vmw_bo_bo_free && + bo->destroy != vmw_user_bo_destroy) + return; + + vbo = container_of(bo, struct vmw_buffer_object, base); + + /* + * Kill any cached kernel maps before move. An optimization could + * be to do this iff source or destination memory type is in VRAM. + */ + vmw_bo_unmap(vbo); + + /* + * If we're moving a backup MOB out of MOB placement, then make sure we + * read back all resource content first, and unbind the MOB from + * the resource. + */ + if (mem->mem_type != VMW_PL_MOB) + vmw_resource_unbind_list(vbo); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c index ff8acc74786c..d460a721eaee 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c @@ -424,7 +424,7 @@ static int vmw_gb_context_unbind(struct vmw_resource *res, (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); - vmw_fence_single_bo(bo, fence); + vmw_bo_fence_single(bo, fence); if (likely(fence != NULL)) vmw_fence_obj_unreference(&fence); @@ -648,7 +648,7 @@ static int vmw_dx_context_unbind(struct vmw_resource *res, (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); - vmw_fence_single_bo(bo, fence); + vmw_bo_fence_single(bo, fence); if (likely(fence != NULL)) vmw_fence_obj_unreference(&fence); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c index 1052cd3cb700..980e6e7cd592 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c @@ -324,7 +324,7 @@ static int vmw_cotable_unbind(struct vmw_resource *res, vmw_dx_context_scrub_cotables(vcotbl->ctx, readback); mutex_unlock(&dev_priv->binding_mutex); (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); - vmw_fence_single_bo(bo, fence); + vmw_bo_fence_single(bo, fence); if (likely(fence != NULL)) vmw_fence_obj_unreference(&fence); @@ -367,7 +367,7 @@ static int vmw_cotable_readback(struct vmw_resource *res) } (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); - vmw_fence_single_bo(&res->backup->base, fence); + vmw_bo_fence_single(&res->backup->base, fence); vmw_fence_obj_unreference(&fence); return 0; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 25c2f668ad6c..769d72fabb56 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -630,36 +630,6 @@ extern int vmw_user_resource_lookup_handle( uint32_t handle, const struct vmw_user_resource_conv *converter, struct vmw_resource **p_res); -extern void vmw_bo_bo_free(struct ttm_buffer_object *bo); -extern int vmw_bo_init(struct vmw_private *dev_priv, - struct vmw_buffer_object *vmw_bo, - size_t size, struct ttm_placement *placement, - bool interuptable, - void (*bo_free)(struct ttm_buffer_object *bo)); -extern int vmw_user_bo_verify_access(struct ttm_buffer_object *bo, - struct ttm_object_file *tfile); -extern int vmw_user_bo_alloc(struct vmw_private *dev_priv, - struct ttm_object_file *tfile, - uint32_t size, - bool shareable, - uint32_t *handle, - struct vmw_buffer_object **p_dma_buf, - struct ttm_base_object **p_base); -extern int vmw_user_bo_reference(struct ttm_object_file *tfile, - struct vmw_buffer_object *dma_buf, - uint32_t *handle); -extern int vmw_bo_alloc_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int vmw_bo_unref_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int vmw_user_bo_synccpu_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern uint32_t vmw_bo_validate_node(struct ttm_buffer_object *bo, - uint32_t cur_validate_node); -extern void vmw_bo_validate_clear(struct ttm_buffer_object *bo); -extern int vmw_user_bo_lookup(struct ttm_object_file *tfile, - uint32_t id, struct vmw_buffer_object **out, - struct ttm_base_object **base); extern int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int vmw_stream_unref_ioctl(struct drm_device *dev, void *data, @@ -672,16 +642,11 @@ extern void vmw_resource_unreserve(struct vmw_resource *res, bool switch_backup, struct vmw_buffer_object *new_backup, unsigned long new_backup_offset); -extern void vmw_resource_move_notify(struct ttm_buffer_object *bo, - struct ttm_mem_reg *mem); extern void vmw_query_move_notify(struct ttm_buffer_object *bo, struct ttm_mem_reg *mem); -extern void vmw_resource_swap_notify(struct ttm_buffer_object *bo); extern int vmw_query_readback_all(struct vmw_buffer_object *dx_query_mob); -extern void vmw_fence_single_bo(struct ttm_buffer_object *bo, - struct vmw_fence_obj *fence); extern void vmw_resource_evict_all(struct vmw_private *dev_priv); - +extern void vmw_resource_unbind_list(struct vmw_buffer_object *vbo); /** * Buffer object helper functions - vmwgfx_bo.c @@ -705,8 +670,40 @@ extern int vmw_bo_unpin(struct vmw_private *vmw_priv, extern void vmw_bo_get_guest_ptr(const struct ttm_buffer_object *buf, SVGAGuestPtr *ptr); extern void vmw_bo_pin_reserved(struct vmw_buffer_object *bo, bool pin); -extern void *vmw_buffer_object_map_and_cache(struct vmw_buffer_object *vbo); -extern void vmw_buffer_object_unmap(struct vmw_buffer_object *vbo); +extern void vmw_bo_bo_free(struct ttm_buffer_object *bo); +extern int vmw_bo_init(struct vmw_private *dev_priv, + struct vmw_buffer_object *vmw_bo, + size_t size, struct ttm_placement *placement, + bool interuptable, + void (*bo_free)(struct ttm_buffer_object *bo)); +extern int vmw_user_bo_verify_access(struct ttm_buffer_object *bo, + struct ttm_object_file *tfile); +extern int vmw_user_bo_alloc(struct vmw_private *dev_priv, + struct ttm_object_file *tfile, + uint32_t size, + bool shareable, + uint32_t *handle, + struct vmw_buffer_object **p_dma_buf, + struct ttm_base_object **p_base); +extern int vmw_user_bo_reference(struct ttm_object_file *tfile, + struct vmw_buffer_object *dma_buf, + uint32_t *handle); +extern int vmw_bo_alloc_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int vmw_bo_unref_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int vmw_user_bo_synccpu_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int vmw_user_bo_lookup(struct ttm_object_file *tfile, + uint32_t id, struct vmw_buffer_object **out, + struct ttm_base_object **base); +extern void vmw_bo_fence_single(struct ttm_buffer_object *bo, + struct vmw_fence_obj *fence); +extern void *vmw_bo_map_and_cache(struct vmw_buffer_object *vbo); +extern void vmw_bo_unmap(struct vmw_buffer_object *vbo); +extern void vmw_bo_move_notify(struct ttm_buffer_object *bo, + struct ttm_mem_reg *mem); +extern void vmw_bo_swap_notify(struct ttm_buffer_object *bo); /** * Misc Ioctl functionality - vmwgfx_ioctl.c diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index dcde4985c574..b913a56f3426 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -197,7 +197,7 @@ static void vmw_fb_dirty_flush(struct work_struct *work) (void) ttm_read_lock(&vmw_priv->reservation_sem, false); (void) ttm_bo_reserve(&vbo->base, false, false, NULL); - virtual = vmw_buffer_object_map_and_cache(vbo); + virtual = vmw_bo_map_and_cache(vbo); if (!virtual) goto out_unreserve; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 7a32be0cef14..e7a7a2e73bbf 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -2493,7 +2493,7 @@ void vmw_kms_helper_buffer_finish(struct vmw_private *dev_priv, ret = vmw_execbuf_fence_commands(file_priv, dev_priv, &fence, file_priv ? &handle : NULL); if (buf) - vmw_fence_single_bo(&buf->base, fence); + vmw_bo_fence_single(&buf->base, fence); if (file_priv) vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), ret, user_fence_rep, fence, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c index d07c585e3c1d..e44da7bb4861 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c @@ -225,7 +225,7 @@ static void vmw_takedown_otable_base(struct vmw_private *dev_priv, ret = ttm_bo_reserve(bo, false, true, NULL); BUG_ON(ret != 0); - vmw_fence_single_bo(bo, NULL); + vmw_bo_fence_single(bo, NULL); ttm_bo_unreserve(bo); } @@ -362,7 +362,7 @@ static void vmw_otable_batch_takedown(struct vmw_private *dev_priv, ret = ttm_bo_reserve(bo, false, true, NULL); BUG_ON(ret != 0); - vmw_fence_single_bo(bo, NULL); + vmw_bo_fence_single(bo, NULL); ttm_bo_unreserve(bo); ttm_bo_unref(&batch->otable_bo); @@ -620,7 +620,7 @@ void vmw_mob_unbind(struct vmw_private *dev_priv, vmw_fifo_commit(dev_priv, sizeof(*cmd)); } if (bo) { - vmw_fence_single_bo(bo, NULL); + vmw_bo_fence_single(bo, NULL); ttm_bo_unreserve(bo); } vmw_fifo_resource_dec(dev_priv); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 5aaf9ac65cba..6d2cad744f5d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -27,7 +27,6 @@ #include "vmwgfx_drv.h" #include -#include #include #include #include "vmwgfx_resource_priv.h" @@ -35,30 +34,6 @@ #define VMW_RES_EVICT_ERR_COUNT 10 -struct vmw_user_buffer_object { - struct ttm_prime_object prime; - struct vmw_buffer_object vbo; -}; - -struct vmw_bo_user_rep { - uint32_t handle; - uint64_t map_handle; -}; - -static inline struct vmw_buffer_object * -vmw_buffer_object(struct ttm_buffer_object *bo) -{ - return container_of(bo, struct vmw_buffer_object, base); -} - -static inline struct vmw_user_buffer_object * -vmw_user_buffer_object(struct ttm_buffer_object *bo) -{ - struct vmw_buffer_object *vmw_bo = vmw_buffer_object(bo); - - return container_of(vmw_bo, struct vmw_user_buffer_object, vbo); -} - struct vmw_resource *vmw_resource_reference(struct vmw_resource *res) { kref_get(&res->kref); @@ -316,509 +291,6 @@ int vmw_user_lookup_handle(struct vmw_private *dev_priv, return ret; } -/** - * Buffer management. - */ - -/** - * vmw_bo_acc_size - Calculate the pinned memory usage of buffers - * - * @dev_priv: Pointer to a struct vmw_private identifying the device. - * @size: The requested buffer size. - * @user: Whether this is an ordinary dma buffer or a user dma buffer. - */ -static size_t vmw_bo_acc_size(struct vmw_private *dev_priv, size_t size, - bool user) -{ - static size_t struct_size, user_struct_size; - size_t num_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; - size_t page_array_size = ttm_round_pot(num_pages * sizeof(void *)); - - if (unlikely(struct_size == 0)) { - size_t backend_size = ttm_round_pot(vmw_tt_size); - - struct_size = backend_size + - ttm_round_pot(sizeof(struct vmw_buffer_object)); - user_struct_size = backend_size + - ttm_round_pot(sizeof(struct vmw_user_buffer_object)); - } - - if (dev_priv->map_mode == vmw_dma_alloc_coherent) - page_array_size += - ttm_round_pot(num_pages * sizeof(dma_addr_t)); - - return ((user) ? user_struct_size : struct_size) + - page_array_size; -} - -void vmw_bo_bo_free(struct ttm_buffer_object *bo) -{ - struct vmw_buffer_object *vmw_bo = vmw_buffer_object(bo); - - vmw_buffer_object_unmap(vmw_bo); - kfree(vmw_bo); -} - -static void vmw_user_bo_destroy(struct ttm_buffer_object *bo) -{ - struct vmw_user_buffer_object *vmw_user_bo = vmw_user_buffer_object(bo); - - vmw_buffer_object_unmap(&vmw_user_bo->vbo); - ttm_prime_object_kfree(vmw_user_bo, prime); -} - -int vmw_bo_init(struct vmw_private *dev_priv, - struct vmw_buffer_object *vmw_bo, - size_t size, struct ttm_placement *placement, - bool interruptible, - void (*bo_free)(struct ttm_buffer_object *bo)) -{ - struct ttm_bo_device *bdev = &dev_priv->bdev; - size_t acc_size; - int ret; - bool user = (bo_free == &vmw_user_bo_destroy); - - WARN_ON_ONCE(!bo_free && (!user && (bo_free != vmw_bo_bo_free))); - - acc_size = vmw_bo_acc_size(dev_priv, size, user); - memset(vmw_bo, 0, sizeof(*vmw_bo)); - - INIT_LIST_HEAD(&vmw_bo->res_list); - - ret = ttm_bo_init(bdev, &vmw_bo->base, size, - ttm_bo_type_device, placement, - 0, interruptible, acc_size, - NULL, NULL, bo_free); - return ret; -} - -static void vmw_user_bo_release(struct ttm_base_object **p_base) -{ - struct vmw_user_buffer_object *vmw_user_bo; - struct ttm_base_object *base = *p_base; - struct ttm_buffer_object *bo; - - *p_base = NULL; - - if (unlikely(base == NULL)) - return; - - vmw_user_bo = container_of(base, struct vmw_user_buffer_object, - prime.base); - bo = &vmw_user_bo->vbo.base; - ttm_bo_unref(&bo); -} - -static void vmw_user_bo_ref_obj_release(struct ttm_base_object *base, - enum ttm_ref_type ref_type) -{ - struct vmw_user_buffer_object *user_bo; - - user_bo = container_of(base, struct vmw_user_buffer_object, prime.base); - - switch (ref_type) { - case TTM_REF_SYNCCPU_WRITE: - ttm_bo_synccpu_write_release(&user_bo->vbo.base); - break; - default: - BUG(); - } -} - -/** - * vmw_user_bo_alloc - Allocate a user dma buffer - * - * @dev_priv: Pointer to a struct device private. - * @tfile: Pointer to a struct ttm_object_file on which to register the user - * object. - * @size: Size of the dma buffer. - * @shareable: Boolean whether the buffer is shareable with other open files. - * @handle: Pointer to where the handle value should be assigned. - * @p_vbo: Pointer to where the refcounted struct vmw_buffer_object pointer - * should be assigned. - */ -int vmw_user_bo_alloc(struct vmw_private *dev_priv, - struct ttm_object_file *tfile, - uint32_t size, - bool shareable, - uint32_t *handle, - struct vmw_buffer_object **p_vbo, - struct ttm_base_object **p_base) -{ - struct vmw_user_buffer_object *user_bo; - struct ttm_buffer_object *tmp; - int ret; - - user_bo = kzalloc(sizeof(*user_bo), GFP_KERNEL); - if (unlikely(!user_bo)) { - DRM_ERROR("Failed to allocate a buffer.\n"); - return -ENOMEM; - } - - ret = vmw_bo_init(dev_priv, &user_bo->vbo, size, - (dev_priv->has_mob) ? - &vmw_sys_placement : - &vmw_vram_sys_placement, true, - &vmw_user_bo_destroy); - if (unlikely(ret != 0)) - return ret; - - tmp = ttm_bo_reference(&user_bo->vbo.base); - ret = ttm_prime_object_init(tfile, - size, - &user_bo->prime, - shareable, - ttm_buffer_type, - &vmw_user_bo_release, - &vmw_user_bo_ref_obj_release); - if (unlikely(ret != 0)) { - ttm_bo_unref(&tmp); - goto out_no_base_object; - } - - *p_vbo = &user_bo->vbo; - if (p_base) { - *p_base = &user_bo->prime.base; - kref_get(&(*p_base)->refcount); - } - *handle = user_bo->prime.base.hash.key; - -out_no_base_object: - return ret; -} - -/** - * vmw_user_bo_verify_access - verify access permissions on this - * buffer object. - * - * @bo: Pointer to the buffer object being accessed - * @tfile: Identifying the caller. - */ -int vmw_user_bo_verify_access(struct ttm_buffer_object *bo, - struct ttm_object_file *tfile) -{ - struct vmw_user_buffer_object *vmw_user_bo; - - if (unlikely(bo->destroy != vmw_user_bo_destroy)) - return -EPERM; - - vmw_user_bo = vmw_user_buffer_object(bo); - - /* Check that the caller has opened the object. */ - if (likely(ttm_ref_object_exists(tfile, &vmw_user_bo->prime.base))) - return 0; - - DRM_ERROR("Could not grant buffer access.\n"); - return -EPERM; -} - -/** - * vmw_user_bo_synccpu_grab - Grab a struct vmw_user_buffer_object for cpu - * access, idling previous GPU operations on the buffer and optionally - * blocking it for further command submissions. - * - * @user_bo: Pointer to the buffer object being grabbed for CPU access - * @tfile: Identifying the caller. - * @flags: Flags indicating how the grab should be performed. - * - * A blocking grab will be automatically released when @tfile is closed. - */ -static int vmw_user_bo_synccpu_grab(struct vmw_user_buffer_object *user_bo, - struct ttm_object_file *tfile, - uint32_t flags) -{ - struct ttm_buffer_object *bo = &user_bo->vbo.base; - bool existed; - int ret; - - if (flags & drm_vmw_synccpu_allow_cs) { - bool nonblock = !!(flags & drm_vmw_synccpu_dontblock); - long lret; - - lret = reservation_object_wait_timeout_rcu(bo->resv, true, true, - nonblock ? 0 : MAX_SCHEDULE_TIMEOUT); - if (!lret) - return -EBUSY; - else if (lret < 0) - return lret; - return 0; - } - - ret = ttm_bo_synccpu_write_grab - (bo, !!(flags & drm_vmw_synccpu_dontblock)); - if (unlikely(ret != 0)) - return ret; - - ret = ttm_ref_object_add(tfile, &user_bo->prime.base, - TTM_REF_SYNCCPU_WRITE, &existed, false); - if (ret != 0 || existed) - ttm_bo_synccpu_write_release(&user_bo->vbo.base); - - return ret; -} - -/** - * vmw_user_bo_synccpu_release - Release a previous grab for CPU access, - * and unblock command submission on the buffer if blocked. - * - * @handle: Handle identifying the buffer object. - * @tfile: Identifying the caller. - * @flags: Flags indicating the type of release. - */ -static int vmw_user_bo_synccpu_release(uint32_t handle, - struct ttm_object_file *tfile, - uint32_t flags) -{ - if (!(flags & drm_vmw_synccpu_allow_cs)) - return ttm_ref_object_base_unref(tfile, handle, - TTM_REF_SYNCCPU_WRITE); - - return 0; -} - -/** - * vmw_user_bo_synccpu_release - ioctl function implementing the synccpu - * functionality. - * - * @dev: Identifies the drm device. - * @data: Pointer to the ioctl argument. - * @file_priv: Identifies the caller. - * - * This function checks the ioctl arguments for validity and calls the - * relevant synccpu functions. - */ -int vmw_user_bo_synccpu_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_vmw_synccpu_arg *arg = - (struct drm_vmw_synccpu_arg *) data; - struct vmw_buffer_object *vbo; - struct vmw_user_buffer_object *user_bo; - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - struct ttm_base_object *buffer_base; - int ret; - - if ((arg->flags & (drm_vmw_synccpu_read | drm_vmw_synccpu_write)) == 0 - || (arg->flags & ~(drm_vmw_synccpu_read | drm_vmw_synccpu_write | - drm_vmw_synccpu_dontblock | - drm_vmw_synccpu_allow_cs)) != 0) { - DRM_ERROR("Illegal synccpu flags.\n"); - return -EINVAL; - } - - switch (arg->op) { - case drm_vmw_synccpu_grab: - ret = vmw_user_bo_lookup(tfile, arg->handle, &vbo, - &buffer_base); - if (unlikely(ret != 0)) - return ret; - - user_bo = container_of(vbo, struct vmw_user_buffer_object, - vbo); - ret = vmw_user_bo_synccpu_grab(user_bo, tfile, arg->flags); - vmw_bo_unreference(&vbo); - ttm_base_object_unref(&buffer_base); - if (unlikely(ret != 0 && ret != -ERESTARTSYS && - ret != -EBUSY)) { - DRM_ERROR("Failed synccpu grab on handle 0x%08x.\n", - (unsigned int) arg->handle); - return ret; - } - break; - case drm_vmw_synccpu_release: - ret = vmw_user_bo_synccpu_release(arg->handle, tfile, - arg->flags); - if (unlikely(ret != 0)) { - DRM_ERROR("Failed synccpu release on handle 0x%08x.\n", - (unsigned int) arg->handle); - return ret; - } - break; - default: - DRM_ERROR("Invalid synccpu operation.\n"); - return -EINVAL; - } - - return 0; -} - -int vmw_bo_alloc_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct vmw_private *dev_priv = vmw_priv(dev); - union drm_vmw_alloc_dmabuf_arg *arg = - (union drm_vmw_alloc_dmabuf_arg *)data; - struct drm_vmw_alloc_dmabuf_req *req = &arg->req; - struct drm_vmw_dmabuf_rep *rep = &arg->rep; - struct vmw_buffer_object *vbo; - uint32_t handle; - int ret; - - ret = ttm_read_lock(&dev_priv->reservation_sem, true); - if (unlikely(ret != 0)) - return ret; - - ret = vmw_user_bo_alloc(dev_priv, vmw_fpriv(file_priv)->tfile, - req->size, false, &handle, &vbo, - NULL); - if (unlikely(ret != 0)) - goto out_no_bo; - - rep->handle = handle; - rep->map_handle = drm_vma_node_offset_addr(&vbo->base.vma_node); - rep->cur_gmr_id = handle; - rep->cur_gmr_offset = 0; - - vmw_bo_unreference(&vbo); - -out_no_bo: - ttm_read_unlock(&dev_priv->reservation_sem); - - return ret; -} - -int vmw_bo_unref_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_vmw_unref_dmabuf_arg *arg = - (struct drm_vmw_unref_dmabuf_arg *)data; - - return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, - arg->handle, - TTM_REF_USAGE); -} - -int vmw_user_bo_lookup(struct ttm_object_file *tfile, - uint32_t handle, struct vmw_buffer_object **out, - struct ttm_base_object **p_base) -{ - struct vmw_user_buffer_object *vmw_user_bo; - struct ttm_base_object *base; - - base = ttm_base_object_lookup(tfile, handle); - if (unlikely(base == NULL)) { - pr_err("Invalid buffer object handle 0x%08lx\n", - (unsigned long)handle); - return -ESRCH; - } - - if (unlikely(ttm_base_object_type(base) != ttm_buffer_type)) { - ttm_base_object_unref(&base); - pr_err("Invalid buffer object handle 0x%08lx\n", - (unsigned long)handle); - return -EINVAL; - } - - vmw_user_bo = container_of(base, struct vmw_user_buffer_object, - prime.base); - (void)ttm_bo_reference(&vmw_user_bo->vbo.base); - if (p_base) - *p_base = base; - else - ttm_base_object_unref(&base); - *out = &vmw_user_bo->vbo; - - return 0; -} - -int vmw_user_bo_reference(struct ttm_object_file *tfile, - struct vmw_buffer_object *vbo, - uint32_t *handle) -{ - struct vmw_user_buffer_object *user_bo; - - if (vbo->base.destroy != vmw_user_bo_destroy) - return -EINVAL; - - user_bo = container_of(vbo, struct vmw_user_buffer_object, vbo); - - *handle = user_bo->prime.base.hash.key; - return ttm_ref_object_add(tfile, &user_bo->prime.base, - TTM_REF_USAGE, NULL, false); -} - -/** - * vmw_dumb_create - Create a dumb kms buffer - * - * @file_priv: Pointer to a struct drm_file identifying the caller. - * @dev: Pointer to the drm device. - * @args: Pointer to a struct drm_mode_create_dumb structure - * - * This is a driver callback for the core drm create_dumb functionality. - * Note that this is very similar to the vmw_bo_alloc ioctl, except - * that the arguments have a different format. - */ -int vmw_dumb_create(struct drm_file *file_priv, - struct drm_device *dev, - struct drm_mode_create_dumb *args) -{ - struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_buffer_object *vbo; - int ret; - - args->pitch = args->width * ((args->bpp + 7) / 8); - args->size = args->pitch * args->height; - - ret = ttm_read_lock(&dev_priv->reservation_sem, true); - if (unlikely(ret != 0)) - return ret; - - ret = vmw_user_bo_alloc(dev_priv, vmw_fpriv(file_priv)->tfile, - args->size, false, &args->handle, - &vbo, NULL); - if (unlikely(ret != 0)) - goto out_no_bo; - - vmw_bo_unreference(&vbo); -out_no_bo: - ttm_read_unlock(&dev_priv->reservation_sem); - return ret; -} - -/** - * vmw_dumb_map_offset - Return the address space offset of a dumb buffer - * - * @file_priv: Pointer to a struct drm_file identifying the caller. - * @dev: Pointer to the drm device. - * @handle: Handle identifying the dumb buffer. - * @offset: The address space offset returned. - * - * This is a driver callback for the core drm dumb_map_offset functionality. - */ -int vmw_dumb_map_offset(struct drm_file *file_priv, - struct drm_device *dev, uint32_t handle, - uint64_t *offset) -{ - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - struct vmw_buffer_object *out_buf; - int ret; - - ret = vmw_user_bo_lookup(tfile, handle, &out_buf, NULL); - if (ret != 0) - return -EINVAL; - - *offset = drm_vma_node_offset_addr(&out_buf->base.vma_node); - vmw_bo_unreference(&out_buf); - return 0; -} - -/** - * vmw_dumb_destroy - Destroy a dumb boffer - * - * @file_priv: Pointer to a struct drm_file identifying the caller. - * @dev: Pointer to the drm device. - * @handle: Handle identifying the dumb buffer. - * - * This is a driver callback for the core drm dumb_destroy functionality. - */ -int vmw_dumb_destroy(struct drm_file *file_priv, - struct drm_device *dev, - uint32_t handle) -{ - return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, - handle, TTM_REF_USAGE); -} - /** * vmw_resource_buf_alloc - Allocate a backup buffer for a resource. * @@ -1182,109 +654,39 @@ out_no_validate: return ret; } -/** - * vmw_fence_single_bo - Utility function to fence a single TTM buffer - * object without unreserving it. - * - * @bo: Pointer to the struct ttm_buffer_object to fence. - * @fence: Pointer to the fence. If NULL, this function will - * insert a fence into the command stream.. - * - * Contrary to the ttm_eu version of this function, it takes only - * a single buffer object instead of a list, and it also doesn't - * unreserve the buffer object, which needs to be done separately. - */ -void vmw_fence_single_bo(struct ttm_buffer_object *bo, - struct vmw_fence_obj *fence) -{ - struct ttm_bo_device *bdev = bo->bdev; - - struct vmw_private *dev_priv = - container_of(bdev, struct vmw_private, bdev); - - if (fence == NULL) { - vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); - reservation_object_add_excl_fence(bo->resv, &fence->base); - dma_fence_put(&fence->base); - } else - reservation_object_add_excl_fence(bo->resv, &fence->base); -} /** - * vmw_resource_move_notify - TTM move_notify_callback + * vmw_resource_unbind_list * - * @bo: The TTM buffer object about to move. - * @mem: The struct ttm_mem_reg indicating to what memory - * region the move is taking place. + * @vbo: Pointer to the current backing MOB. * * Evicts the Guest Backed hardware resource if the backup * buffer is being moved out of MOB memory. - * Note that this function should not race with the resource - * validation code as long as it accesses only members of struct - * resource that remain static while bo::res is !NULL and - * while we have @bo reserved. struct resource::backup is *not* a - * static member. The resource validation code will take care - * to set @bo::res to NULL, while having @bo reserved when the - * buffer is no longer bound to the resource, so @bo:res can be - * used to determine whether there is a need to unbind and whether - * it is safe to unbind. + * Note that this function will not race with the resource + * validation code, since resource validation and eviction + * both require the backup buffer to be reserved. */ -void vmw_resource_move_notify(struct ttm_buffer_object *bo, - struct ttm_mem_reg *mem) +void vmw_resource_unbind_list(struct vmw_buffer_object *vbo) { - struct vmw_buffer_object *vbo; - - if (mem == NULL) - return; - - if (bo->destroy != vmw_bo_bo_free && - bo->destroy != vmw_user_bo_destroy) - return; - - vbo = container_of(bo, struct vmw_buffer_object, base); - - /* - * Kill any cached kernel maps before move. An optimization could - * be to do this iff source or destination memory type is VRAM. - */ - vmw_buffer_object_unmap(vbo); - if (mem->mem_type != VMW_PL_MOB) { - struct vmw_resource *res, *n; - struct ttm_validate_buffer val_buf; + struct vmw_resource *res, *next; + struct ttm_validate_buffer val_buf = { + .bo = &vbo->base, + .shared = false + }; - val_buf.bo = bo; - val_buf.shared = false; + lockdep_assert_held(&vbo->base.resv->lock.base); + list_for_each_entry_safe(res, next, &vbo->res_list, mob_head) { + if (!res->func->unbind) + continue; - list_for_each_entry_safe(res, n, &vbo->res_list, mob_head) { - - if (unlikely(res->func->unbind == NULL)) - continue; - - (void) res->func->unbind(res, true, &val_buf); - res->backup_dirty = true; - res->res_dirty = false; - list_del_init(&res->mob_head); - } - - (void) ttm_bo_wait(bo, false, false); + (void) res->func->unbind(res, true, &val_buf); + res->backup_dirty = true; + res->res_dirty = false; + list_del_init(&res->mob_head); } -} - - -/** - * vmw_resource_swap_notify - swapout notify callback. - * - * @bo: The buffer object to be swapped out. - */ -void vmw_resource_swap_notify(struct ttm_buffer_object *bo) -{ - if (bo->destroy != vmw_bo_bo_free && - bo->destroy != vmw_user_bo_destroy) - return; - /* Kill any cached kernel maps before swapout */ - vmw_buffer_object_unmap(vmw_buffer_object(bo)); + (void) ttm_bo_wait(&vbo->base, false, false); } @@ -1370,7 +772,7 @@ void vmw_query_move_notify(struct ttm_buffer_object *bo, /* Create a fence and attach the BO to it */ (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); - vmw_fence_single_bo(bo, fence); + vmw_bo_fence_single(bo, fence); if (fence != NULL) vmw_fence_obj_unreference(&fence); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c index f6c939f3ff5e..4ec1a91990d9 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c @@ -306,7 +306,7 @@ static int vmw_gb_shader_unbind(struct vmw_resource *res, (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); - vmw_fence_single_bo(val_buf->bo, fence); + vmw_bo_fence_single(val_buf->bo, fence); if (likely(fence != NULL)) vmw_fence_obj_unreference(&fence); @@ -537,7 +537,7 @@ static int vmw_dx_shader_unbind(struct vmw_resource *res, (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); - vmw_fence_single_bo(val_buf->bo, fence); + vmw_bo_fence_single(val_buf->bo, fence); if (likely(fence != NULL)) vmw_fence_obj_unreference(&fence); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index 2b2e8aa7114a..f2af53613803 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -468,7 +468,7 @@ static int vmw_legacy_srf_dma(struct vmw_resource *res, (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); - vmw_fence_single_bo(val_buf->bo, fence); + vmw_bo_fence_single(val_buf->bo, fence); if (likely(fence != NULL)) vmw_fence_obj_unreference(&fence); @@ -1210,7 +1210,7 @@ static int vmw_gb_surface_unbind(struct vmw_resource *res, (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); - vmw_fence_single_bo(val_buf->bo, fence); + vmw_bo_fence_single(val_buf->bo, fence); if (likely(fence != NULL)) vmw_fence_obj_unreference(&fence); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c index 0931f43913b1..239e1edf0919 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c @@ -852,7 +852,7 @@ static void vmw_move_notify(struct ttm_buffer_object *bo, bool evict, struct ttm_mem_reg *mem) { - vmw_resource_move_notify(bo, mem); + vmw_bo_move_notify(bo, mem); vmw_query_move_notify(bo, mem); } @@ -864,7 +864,7 @@ static void vmw_move_notify(struct ttm_buffer_object *bo, */ static void vmw_swap_notify(struct ttm_buffer_object *bo) { - vmw_resource_swap_notify(bo); + vmw_bo_swap_notify(bo); (void) ttm_bo_wait(bo, false, false); } -- cgit v1.2.3 From b249cb4f6bc45485f631803b9762e443108a4b00 Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Tue, 19 Jun 2018 19:46:58 +0200 Subject: drm/vmwgfx: Fix atomic mode set check vmw_kms_atomic_check_modeset() is currently checking config using the legacy state, which is updated after a commit has happened. This means vmw_kms_atomic_check_modeset() will reject an invalid config on the next update rather than the current one. Fix this by using the new states for config checking Signed-off-by: Sinclair Yeh Reviewed-by: Deepak Rawat Signed-off-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 40 ++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 14 deletions(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_kms.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index e7a7a2e73bbf..6b8541f9215d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1526,33 +1526,45 @@ static int vmw_kms_atomic_check_modeset(struct drm_device *dev, struct drm_atomic_state *state) { - struct drm_crtc_state *crtc_state; + struct drm_crtc_state *new_crtc_state; + struct drm_plane_state *new_plane_state; + struct drm_plane *plane; struct drm_crtc *crtc; struct vmw_private *dev_priv = vmw_priv(dev); - int i; + int i, ret, cpp = 0; - for_each_new_crtc_in_state(state, crtc, crtc_state, i) { - unsigned long requested_bb_mem = 0; + ret = drm_atomic_helper_check(dev, state); - if (dev_priv->active_display_unit == vmw_du_screen_target) { - struct drm_plane *plane = crtc->primary; - struct drm_plane_state *plane_state; + /* If this is not a STDU, then no more checking is necessary */ + if (ret || dev_priv->active_display_unit != vmw_du_screen_target) + return ret; - plane_state = drm_atomic_get_new_plane_state(state, plane); + for_each_new_plane_in_state(state, plane, new_plane_state, i) { + if (new_plane_state->fb) { + int current_cpp = new_plane_state->fb->pitches[0] / + new_plane_state->fb->width; - if (plane_state && plane_state->fb) { - int cpp = plane_state->fb->format->cpp[0]; + if (cpp == 0) + cpp = current_cpp; + else if (current_cpp != cpp) + return -EINVAL; + } + } - requested_bb_mem += crtc->mode.hdisplay * cpp * - crtc->mode.vdisplay; - } + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { + unsigned long requested_bb_mem = 0; + + if (drm_atomic_crtc_needs_modeset(new_crtc_state)) { + requested_bb_mem += new_crtc_state->mode.hdisplay * + new_crtc_state->mode.vdisplay * + cpp; if (requested_bb_mem > dev_priv->prim_bb_mem) return -EINVAL; } } - return drm_atomic_helper_check(dev, state); + return ret; } static const struct drm_mode_config_funcs vmw_kms_funcs = { -- cgit v1.2.3 From 0a80eb4c122fd6119f3a15910d7bb9e6e33c3661 Mon Sep 17 00:00:00 2001 From: Deepak Rawat Date: Tue, 19 Jun 2018 19:51:15 +0200 Subject: drm/vmwgfx: Perform topology validation during atomic modeset. This patch adds display (primary) memory validation during modeset check. Display memory validation are applicable to both SOU and STDU, so allow both display unit to undergo this check. Also added check for SVGA_CAP_NO_BB_RESTRICTION capability which lifts bounding box restriction for STDU. Signed-off-by: Deepak Rawat Reviewed-by: Thomas Hellstrom Signed-off-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/device_include/svga_reg.h | 31 +++- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 184 ++++++++++++++++++----- 2 files changed, 177 insertions(+), 38 deletions(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_kms.c') diff --git a/drivers/gpu/drm/vmwgfx/device_include/svga_reg.h b/drivers/gpu/drm/vmwgfx/device_include/svga_reg.h index 88e72bf9a534..cdd48a3763db 100644 --- a/drivers/gpu/drm/vmwgfx/device_include/svga_reg.h +++ b/drivers/gpu/drm/vmwgfx/device_include/svga_reg.h @@ -672,8 +672,34 @@ SVGASignedPoint; * SVGA_CAP_GBOBJECTS -- * Enable guest-backed objects and surfaces. * - * SVGA_CAP_CMD_BUFFERS_3 -- - * Enable support for command buffers in a mob. + * SVGA_CAP_DX -- + * Enable support for DX commands, and command buffers in a mob. + * + * SVGA_CAP_HP_CMD_QUEUE -- + * Enable support for the high priority command queue, and the + * ScreenCopy command. + * + * SVGA_CAP_NO_BB_RESTRICTION -- + * Allow ScreenTargets to be defined without regard to the 32-bpp + * bounding-box memory restrictions. ie: + * + * The summed memory usage of all screens (assuming they were defined as + * 32-bpp) must always be less than the value of the + * SVGA_REG_MAX_PRIMARY_MEM register. + * + * If this cap is not present, the 32-bpp bounding box around all screens + * must additionally be under the value of the SVGA_REG_MAX_PRIMARY_MEM + * register. + * + * If the cap is present, the bounding box restriction is lifted (and only + * the screen-sum limit applies). + * + * (Note that this is a slight lie... there is still a sanity limit on any + * dimension of the topology to be less than SVGA_SCREEN_ROOT_LIMIT, even + * when SVGA_CAP_NO_BB_RESTRICTION is present, but that should be + * large enough to express any possible topology without holes between + * monitors.) + * */ #define SVGA_CAP_NONE 0x00000000 @@ -699,6 +725,7 @@ SVGASignedPoint; #define SVGA_CAP_GBOBJECTS 0x08000000 #define SVGA_CAP_DX 0x10000000 #define SVGA_CAP_HP_CMD_QUEUE 0x20000000 +#define SVGA_CAP_NO_BB_RESTRICTION 0x40000000 #define SVGA_CAP_CMD_RESERVED 0x80000000 diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 6b8541f9215d..a7c9a017fc3c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1507,66 +1507,178 @@ err_out: return &vfb->base; } +/** + * vmw_kms_check_display_memory - Validates display memory required for a + * topology + * @dev: DRM device + * @num_rects: number of drm_rect in rects + * @rects: array of drm_rect representing the topology to validate indexed by + * crtc index. + * + * Returns: + * 0 on success otherwise negative error code + */ +static int vmw_kms_check_display_memory(struct drm_device *dev, + uint32_t num_rects, + struct drm_rect *rects) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_rect bounding_box = {0}; + u64 total_pixels = 0, pixel_mem, bb_mem; + int i; + + for (i = 0; i < num_rects; i++) { + /* + * Currently this check is limiting the topology within max + * texture/screentarget size. This should change in future when + * user-space support multiple fb with topology. + */ + if (rects[i].x1 < 0 || rects[i].y1 < 0 || + rects[i].x2 > mode_config->max_width || + rects[i].y2 > mode_config->max_height) { + DRM_ERROR("Invalid GUI layout.\n"); + return -EINVAL; + } + + /* Bounding box upper left is at (0,0). */ + if (rects[i].x2 > bounding_box.x2) + bounding_box.x2 = rects[i].x2; + + if (rects[i].y2 > bounding_box.y2) + bounding_box.y2 = rects[i].y2; + + total_pixels += (u64) drm_rect_width(&rects[i]) * + (u64) drm_rect_height(&rects[i]); + } + + /* Virtual svga device primary limits are always in 32-bpp. */ + pixel_mem = total_pixels * 4; + + /* + * For HV10 and below prim_bb_mem is vram size. When + * SVGA_REG_MAX_PRIMARY_BOUNDING_BOX_MEM is not present vram size is + * limit on primary bounding box + */ + if (pixel_mem > dev_priv->prim_bb_mem) { + DRM_ERROR("Combined output size too large.\n"); + return -EINVAL; + } + /* SVGA_CAP_NO_BB_RESTRICTION is available for STDU only. */ + if (dev_priv->active_display_unit != vmw_du_screen_target || + !(dev_priv->capabilities & SVGA_CAP_NO_BB_RESTRICTION)) { + bb_mem = (u64) bounding_box.x2 * bounding_box.y2 * 4; + + if (bb_mem > dev_priv->prim_bb_mem) { + DRM_ERROR("Topology is beyond supported limits.\n"); + return -EINVAL; + } + } + + return 0; +} /** - * vmw_kms_atomic_check_modeset- validate state object for modeset changes - * + * vmw_kms_check_topology - Validates topology in drm_atomic_state * @dev: DRM device * @state: the driver state object * - * This is a simple wrapper around drm_atomic_helper_check_modeset() for - * us to assign a value to mode->crtc_clock so that - * drm_calc_timestamping_constants() won't throw an error message - * - * RETURNS - * Zero for success or -errno + * Returns: + * 0 on success otherwise negative error code */ -static int -vmw_kms_atomic_check_modeset(struct drm_device *dev, - struct drm_atomic_state *state) +static int vmw_kms_check_topology(struct drm_device *dev, + struct drm_atomic_state *state) { - struct drm_crtc_state *new_crtc_state; - struct drm_plane_state *new_plane_state; - struct drm_plane *plane; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + struct drm_rect *rects; struct drm_crtc *crtc; - struct vmw_private *dev_priv = vmw_priv(dev); - int i, ret, cpp = 0; + uint32_t i; + int ret = 0; - ret = drm_atomic_helper_check(dev, state); + rects = kcalloc(dev->mode_config.num_crtc, sizeof(struct drm_rect), + GFP_KERNEL); + if (!rects) + return -ENOMEM; - /* If this is not a STDU, then no more checking is necessary */ - if (ret || dev_priv->active_display_unit != vmw_du_screen_target) - return ret; + drm_for_each_crtc(crtc, dev) { + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + struct drm_crtc_state *crtc_state = crtc->state; - for_each_new_plane_in_state(state, plane, new_plane_state, i) { - if (new_plane_state->fb) { - int current_cpp = new_plane_state->fb->pitches[0] / - new_plane_state->fb->width; + i = drm_crtc_index(crtc); - if (cpp == 0) - cpp = current_cpp; - else if (current_cpp != cpp) - return -EINVAL; + if (crtc_state && crtc_state->enable) { + /* + * There could be a race condition with update of gui_x/ + * gui_y. Those are protected by dev->mode_config.mutex. + */ + rects[i].x1 = du->gui_x; + rects[i].y1 = du->gui_y; + rects[i].x2 = du->gui_x + crtc_state->mode.hdisplay; + rects[i].y2 = du->gui_y + crtc_state->mode.vdisplay; } } - for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { - unsigned long requested_bb_mem = 0; + /* Determine change to topology due to new atomic state */ + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, + new_crtc_state, i) { + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + + if (!new_crtc_state->enable && old_crtc_state->enable) { + rects[i].x1 = 0; + rects[i].y1 = 0; + rects[i].x2 = 0; + rects[i].y2 = 0; + } - if (drm_atomic_crtc_needs_modeset(new_crtc_state)) { - requested_bb_mem += new_crtc_state->mode.hdisplay * - new_crtc_state->mode.vdisplay * - cpp; + if (new_crtc_state->enable) { + /* If display unit is not active cannot enable CRTC */ + if (!du->pref_active) { + ret = -EINVAL; + goto clean; + } - if (requested_bb_mem > dev_priv->prim_bb_mem) - return -EINVAL; + rects[i].x1 = du->gui_x; + rects[i].y1 = du->gui_y; + rects[i].x2 = du->gui_x + new_crtc_state->mode.hdisplay; + rects[i].y2 = du->gui_y + new_crtc_state->mode.vdisplay; } } + ret = vmw_kms_check_display_memory(dev, dev->mode_config.num_crtc, + rects); + +clean: + kfree(rects); return ret; } +/** + * vmw_kms_atomic_check_modeset- validate state object for modeset changes + * + * @dev: DRM device + * @state: the driver state object + * + * This is a simple wrapper around drm_atomic_helper_check_modeset() for + * us to assign a value to mode->crtc_clock so that + * drm_calc_timestamping_constants() won't throw an error message + * + * Returns: + * Zero for success or -errno + */ +static int +vmw_kms_atomic_check_modeset(struct drm_device *dev, + struct drm_atomic_state *state) +{ + int ret; + + ret = drm_atomic_helper_check(dev, state); + if (ret) + return ret; + + return vmw_kms_check_topology(dev, state); +} + static const struct drm_mode_config_funcs vmw_kms_funcs = { .fb_create = vmw_kms_fb_create, .atomic_check = vmw_kms_atomic_check_modeset, -- cgit v1.2.3 From 5e24133ea3dfb89fd0cf28cd7a845b66fa680b69 Mon Sep 17 00:00:00 2001 From: Deepak Rawat Date: Wed, 20 Jun 2018 11:17:16 +0200 Subject: drm/vmwgfx: Use modeset display memory validation for layout ioctl Call the same display memory validation function which is used by modeset_check. This ensure consistency that kernel change preferred mode/topology only if supported. Also change the internal function to use drm_rect instead of drm_vmw_rect. Signed-off-by: Deepak Rawat Reviewed-by: Thomas Hellstrom Signed-off-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 120 +++++++++++++++--------------------- 1 file changed, 51 insertions(+), 69 deletions(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_kms.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index a7c9a017fc3c..387bb39de839 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1968,13 +1968,15 @@ void vmw_disable_vblank(struct drm_device *dev, unsigned int pipe) { } - -/* - * Small shared kms functions. +/** + * vmw_du_update_layout - Update the display unit with topology from resolution + * plugin and generate DRM uevent + * @dev_priv: device private + * @num_rects: number of drm_rect in rects + * @rects: toplogy to update */ - -static int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num, - struct drm_vmw_rect *rects) +static int vmw_du_update_layout(struct vmw_private *dev_priv, + unsigned int num_rects, struct drm_rect *rects) { struct drm_device *dev = dev_priv->dev; struct vmw_display_unit *du; @@ -1982,26 +1984,14 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num, mutex_lock(&dev->mode_config.mutex); -#if 0 - { - unsigned int i; - - DRM_INFO("%s: new layout ", __func__); - for (i = 0; i < num; i++) - DRM_INFO("(%i, %i %ux%u) ", rects[i].x, rects[i].y, - rects[i].w, rects[i].h); - DRM_INFO("\n"); - } -#endif - list_for_each_entry(con, &dev->mode_config.connector_list, head) { du = vmw_connector_to_du(con); - if (num > du->unit) { - du->pref_width = rects[du->unit].w; - du->pref_height = rects[du->unit].h; + if (num_rects > du->unit) { + du->pref_width = drm_rect_width(&rects[du->unit]); + du->pref_height = drm_rect_height(&rects[du->unit]); du->pref_active = true; - du->gui_x = rects[du->unit].x; - du->gui_y = rects[du->unit].y; + du->gui_x = rects[du->unit].x1; + du->gui_y = rects[du->unit].y1; drm_object_property_set_value (&con->base, dev->mode_config.suggested_x_property, du->gui_x); @@ -2322,7 +2312,25 @@ vmw_du_connector_atomic_get_property(struct drm_connector *connector, return 0; } - +/** + * vmw_kms_update_layout_ioctl - Handler for DRM_VMW_UPDATE_LAYOUT ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Update preferred topology of display unit as per ioctl request. The topology + * is expressed as array of drm_vmw_rect. + * e.g. + * [0 0 640 480] [640 0 800 600] [0 480 640 480] + * + * NOTE: + * The x and y offset (upper left) in drm_vmw_rect cannot be less than 0. Beside + * device limit on topology, x + w and y + h (lower right) cannot be greater + * than INT_MAX. So topology beyond these limits will return with error. + * + * Returns: + * Zero on success, negative errno on failure. + */ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -2331,15 +2339,12 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, (struct drm_vmw_update_layout_arg *)data; void __user *user_rects; struct drm_vmw_rect *rects; + struct drm_rect *drm_rects; unsigned rects_size; - int ret; - int i; - u64 total_pixels = 0; - struct drm_mode_config *mode_config = &dev->mode_config; - struct drm_vmw_rect bounding_box = {0}; + int ret, i; if (!arg->num_outputs) { - struct drm_vmw_rect def_rect = {0, 0, 800, 600}; + struct drm_rect def_rect = {0, 0, 800, 600}; vmw_du_update_layout(dev_priv, 1, &def_rect); return 0; } @@ -2358,52 +2363,29 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, goto out_free; } - for (i = 0; i < arg->num_outputs; ++i) { - if (rects[i].x < 0 || - rects[i].y < 0 || - rects[i].x + rects[i].w > mode_config->max_width || - rects[i].y + rects[i].h > mode_config->max_height) { - DRM_ERROR("Invalid GUI layout.\n"); - ret = -EINVAL; - goto out_free; - } - - /* - * bounding_box.w and bunding_box.h are used as - * lower-right coordinates - */ - if (rects[i].x + rects[i].w > bounding_box.w) - bounding_box.w = rects[i].x + rects[i].w; - - if (rects[i].y + rects[i].h > bounding_box.h) - bounding_box.h = rects[i].y + rects[i].h; + drm_rects = (struct drm_rect *)rects; - total_pixels += (u64) rects[i].w * (u64) rects[i].h; - } + for (i = 0; i < arg->num_outputs; i++) { + struct drm_vmw_rect curr_rect; - if (dev_priv->active_display_unit == vmw_du_screen_target) { - /* - * For Screen Targets, the limits for a toplogy are: - * 1. Bounding box (assuming 32bpp) must be < prim_bb_mem - * 2. Total pixels (assuming 32bpp) must be < prim_bb_mem - */ - u64 bb_mem = (u64) bounding_box.w * bounding_box.h * 4; - u64 pixel_mem = total_pixels * 4; - - if (bb_mem > dev_priv->prim_bb_mem) { - DRM_ERROR("Topology is beyond supported limits.\n"); - ret = -EINVAL; + /* Verify user-space for overflow as kernel use drm_rect */ + if ((rects[i].x + rects[i].w > INT_MAX) || + (rects[i].y + rects[i].h > INT_MAX)) { + ret = -ERANGE; goto out_free; } - if (pixel_mem > dev_priv->prim_bb_mem) { - DRM_ERROR("Combined output size too large\n"); - ret = -EINVAL; - goto out_free; - } + curr_rect = rects[i]; + drm_rects[i].x1 = curr_rect.x; + drm_rects[i].y1 = curr_rect.y; + drm_rects[i].x2 = curr_rect.x + curr_rect.w; + drm_rects[i].y2 = curr_rect.y + curr_rect.h; } - vmw_du_update_layout(dev_priv, arg->num_outputs, rects); + ret = vmw_kms_check_display_memory(dev, arg->num_outputs, drm_rects); + + if (ret == 0) + vmw_du_update_layout(dev_priv, arg->num_outputs, drm_rects); out_free: kfree(rects); -- cgit v1.2.3 From 7e14eabca82ec75da5200769989d396da46033ee Mon Sep 17 00:00:00 2001 From: Deepak Rawat Date: Wed, 20 Jun 2018 11:23:55 +0200 Subject: drm/vmwgfx: Perform memory validations only when need full modeset. For cases when full modeset is not requested like page-flip, skip memory validation as the topology is not changed. Signed-off-by: Deepak Rawat Reviewed-by: Thomas Hellstrom Signed-off-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_kms.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 387bb39de839..3605ac1702c2 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1670,13 +1670,32 @@ static int vmw_kms_atomic_check_modeset(struct drm_device *dev, struct drm_atomic_state *state) { - int ret; + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + bool need_modeset = false; + int i, ret; ret = drm_atomic_helper_check(dev, state); if (ret) return ret; - return vmw_kms_check_topology(dev, state); + if (!state->allow_modeset) + return ret; + + /* + * Legacy path do not set allow_modeset properly like + * @drm_atomic_helper_update_plane, This will result in unnecessary call + * to vmw_kms_check_topology. So extra set of check. + */ + for_each_new_crtc_in_state(state, crtc, crtc_state, i) { + if (drm_atomic_crtc_needs_modeset(crtc_state)) + need_modeset = true; + } + + if (need_modeset) + return vmw_kms_check_topology(dev, state); + + return ret; } static const struct drm_mode_config_funcs vmw_kms_funcs = { -- cgit v1.2.3 From 018f60b26602bb41b33139da3408224e36dfe02d Mon Sep 17 00:00:00 2001 From: Deepak Rawat Date: Wed, 20 Jun 2018 11:26:59 +0200 Subject: drm/vmwgfx: Remove primary memory validation against mode while creating fb This validation is not required because user-space will send create_fb request once the memory is allocated. This check should be performed during mode-setting. Signed-off-by: Deepak Rawat Reviewed-by: Sinclair Yeh Reviewed-by: Thomas Hellstrom Signed-off-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_kms.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 3605ac1702c2..f0ae0b2ee2e6 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1433,19 +1433,6 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, struct ttm_base_object *user_obj; int ret; - /** - * This code should be conditioned on Screen Objects not being used. - * If screen objects are used, we can allocate a GMR to hold the - * requested framebuffer. - */ - - if (!vmw_kms_validate_mode_vram(dev_priv, - mode_cmd->pitches[0], - mode_cmd->height)) { - DRM_ERROR("Requested mode exceed bounding box limit.\n"); - return ERR_PTR(-ENOMEM); - } - /* * Take a reference on the user object of the resource * backing the kms fb. This ensures that user-space handle -- cgit v1.2.3 From b89e5ff9eeeb8b1fe3d2d7477fb9e1c1aed5dd5b Mon Sep 17 00:00:00 2001 From: Deepak Rawat Date: Wed, 20 Jun 2018 11:32:29 +0200 Subject: drm/vmwgfx: Use a mutex to protect gui positioning in vmw_display_unit To avoid race condition between update_layout ioctl and modeset ioctl for access to gui_x/y positioning added a new mutex requested_layout_mutex. Also used drm_for_each_connector_iter to iterate over connector list. Signed-off-by: Deepak Rawat Reviewed-by: Thomas Hellstrom Signed-off-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 1 + drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 9 +++++++++ drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 38 ++++++++++++++++++++++++++----------- 3 files changed, 37 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_kms.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 4f18304226bc..45dfff7733d6 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -644,6 +644,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) mutex_init(&dev_priv->cmdbuf_mutex); mutex_init(&dev_priv->release_mutex); mutex_init(&dev_priv->binding_mutex); + mutex_init(&dev_priv->requested_layout_mutex); mutex_init(&dev_priv->global_kms_state_mutex); rwlock_init(&dev_priv->resource_lock); ttm_lock_init(&dev_priv->reservation_sem); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 769d72fabb56..a38318c3efe4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -411,6 +411,15 @@ struct vmw_private { uint32_t num_displays; + /* + * Currently requested_layout_mutex is used to protect the gui + * positionig state in display unit. With that use case currently this + * mutex is only taken during layout ioctl and atomic check_modeset. + * Other display unit state can be protected with this mutex but that + * needs careful consideration. + */ + struct mutex requested_layout_mutex; + /* * Framebuffer info. */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index f0ae0b2ee2e6..a592d10e5c76 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1577,6 +1577,7 @@ static int vmw_kms_check_display_memory(struct drm_device *dev, static int vmw_kms_check_topology(struct drm_device *dev, struct drm_atomic_state *state) { + struct vmw_private *dev_priv = vmw_priv(dev); struct drm_crtc_state *old_crtc_state, *new_crtc_state; struct drm_rect *rects; struct drm_crtc *crtc; @@ -1588,6 +1589,8 @@ static int vmw_kms_check_topology(struct drm_device *dev, if (!rects) return -ENOMEM; + mutex_lock(&dev_priv->requested_layout_mutex); + drm_for_each_crtc(crtc, dev) { struct vmw_display_unit *du = vmw_crtc_to_du(crtc); struct drm_crtc_state *crtc_state = crtc->state; @@ -1595,10 +1598,6 @@ static int vmw_kms_check_topology(struct drm_device *dev, i = drm_crtc_index(crtc); if (crtc_state && crtc_state->enable) { - /* - * There could be a race condition with update of gui_x/ - * gui_y. Those are protected by dev->mode_config.mutex. - */ rects[i].x1 = du->gui_x; rects[i].y1 = du->gui_y; rects[i].x2 = du->gui_x + crtc_state->mode.hdisplay; @@ -1636,6 +1635,7 @@ static int vmw_kms_check_topology(struct drm_device *dev, rects); clean: + mutex_unlock(&dev_priv->requested_layout_mutex); kfree(rects); return ret; } @@ -1987,10 +1987,14 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, struct drm_device *dev = dev_priv->dev; struct vmw_display_unit *du; struct drm_connector *con; + struct drm_connector_list_iter conn_iter; - mutex_lock(&dev->mode_config.mutex); - - list_for_each_entry(con, &dev->mode_config.connector_list, head) { + /* + * Currently only gui_x/y is protected with requested_layout_mutex. + */ + mutex_lock(&dev_priv->requested_layout_mutex); + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(con, &conn_iter) { du = vmw_connector_to_du(con); if (num_rects > du->unit) { du->pref_width = drm_rect_width(&rects[du->unit]); @@ -1998,6 +2002,21 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, du->pref_active = true; du->gui_x = rects[du->unit].x1; du->gui_y = rects[du->unit].y1; + } else { + du->pref_width = 800; + du->pref_height = 600; + du->pref_active = false; + du->gui_x = 0; + du->gui_y = 0; + } + } + drm_connector_list_iter_end(&conn_iter); + mutex_unlock(&dev_priv->requested_layout_mutex); + + mutex_lock(&dev->mode_config.mutex); + list_for_each_entry(con, &dev->mode_config.connector_list, head) { + du = vmw_connector_to_du(con); + if (num_rects > du->unit) { drm_object_property_set_value (&con->base, dev->mode_config.suggested_x_property, du->gui_x); @@ -2005,9 +2024,6 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, (&con->base, dev->mode_config.suggested_y_property, du->gui_y); } else { - du->pref_width = 800; - du->pref_height = 600; - du->pref_active = false; drm_object_property_set_value (&con->base, dev->mode_config.suggested_x_property, 0); @@ -2017,8 +2033,8 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, } con->status = vmw_du_connector_detect(con, true); } - mutex_unlock(&dev->mode_config.mutex); + drm_sysfs_hotplug_event(dev); return 0; -- cgit v1.2.3 From 3e79ecdad8897dad4d311576989b6856dbcb958e Mon Sep 17 00:00:00 2001 From: Deepak Rawat Date: Wed, 20 Jun 2018 11:34:26 +0200 Subject: drm/vmwgfx: Add gui_x/y to vmw_connector_state As gui_x/y positioning is display unit is protected by requested_layout_mutex adding vmw_connector_state copy of the same and modeset commit will refer the state copy to sync with modeset_check state. v2: Tested with CONFIG_PROVE_LOCKING enabled. Signed-off-by: Deepak Rawat Reviewed-by: Thomas Hellstrom Signed-off-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 37 +++++++++++++++++++++++++---------- drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 18 +++++++++++++++++ drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 38 ++++++++++++++++++++++-------------- drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 29 ++++++++++++++++----------- 4 files changed, 86 insertions(+), 36 deletions(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_kms.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index a592d10e5c76..0fb363458ab5 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1609,26 +1609,43 @@ static int vmw_kms_check_topology(struct drm_device *dev, for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + struct drm_connector *connector; + struct drm_connector_state *conn_state; + struct vmw_connector_state *vmw_conn_state; if (!new_crtc_state->enable && old_crtc_state->enable) { rects[i].x1 = 0; rects[i].y1 = 0; rects[i].x2 = 0; rects[i].y2 = 0; + continue; } - if (new_crtc_state->enable) { - /* If display unit is not active cannot enable CRTC */ - if (!du->pref_active) { - ret = -EINVAL; - goto clean; - } + if (!du->pref_active) { + ret = -EINVAL; + goto clean; + } - rects[i].x1 = du->gui_x; - rects[i].y1 = du->gui_y; - rects[i].x2 = du->gui_x + new_crtc_state->mode.hdisplay; - rects[i].y2 = du->gui_y + new_crtc_state->mode.vdisplay; + /* + * For vmwgfx each crtc has only one connector attached and it + * is not changed so don't really need to check the + * crtc->connector_mask and iterate over it. + */ + connector = &du->connector; + conn_state = drm_atomic_get_connector_state(state, connector); + if (IS_ERR(conn_state)) { + ret = PTR_ERR(conn_state); + goto clean; } + + vmw_conn_state = vmw_connector_state_to_vcs(conn_state); + vmw_conn_state->gui_x = du->gui_x; + vmw_conn_state->gui_y = du->gui_y; + + rects[i].x1 = du->gui_x; + rects[i].y1 = du->gui_y; + rects[i].x2 = du->gui_x + new_crtc_state->mode.hdisplay; + rects[i].y2 = du->gui_y + new_crtc_state->mode.vdisplay; } ret = vmw_kms_check_display_memory(dev, dev->mode_config.num_crtc, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index ff1caed38f94..1f2b01862652 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -192,6 +192,24 @@ struct vmw_connector_state { struct drm_connector_state base; bool is_implicit; + + /** + * @gui_x: + * + * vmwgfx connector property representing the x position of this display + * unit (connector is synonymous to display unit) in overall topology. + * This is what the device expect as xRoot while creating screen. + */ + int gui_x; + + /** + * @gui_y: + * + * vmwgfx connector property representing the y position of this display + * unit (connector is synonymous to display unit) in overall topology. + * This is what the device expect as yRoot while creating screen. + */ + int gui_y; }; /** diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index 74dfd4621b7e..df21d5a6f84a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -109,7 +109,7 @@ static void vmw_sou_crtc_destroy(struct drm_crtc *crtc) */ static int vmw_sou_fifo_create(struct vmw_private *dev_priv, struct vmw_screen_object_unit *sou, - uint32_t x, uint32_t y, + int x, int y, struct drm_display_mode *mode) { size_t fifo_size; @@ -139,13 +139,8 @@ static int vmw_sou_fifo_create(struct vmw_private *dev_priv, (sou->base.unit == 0 ? SVGA_SCREEN_IS_PRIMARY : 0); cmd->obj.size.width = mode->hdisplay; cmd->obj.size.height = mode->vdisplay; - if (sou->base.is_implicit) { - cmd->obj.root.x = x; - cmd->obj.root.y = y; - } else { - cmd->obj.root.x = sou->base.gui_x; - cmd->obj.root.y = sou->base.gui_y; - } + cmd->obj.root.x = x; + cmd->obj.root.y = y; sou->base.set_gui_x = cmd->obj.root.x; sou->base.set_gui_y = cmd->obj.root.y; @@ -222,12 +217,11 @@ static void vmw_sou_crtc_mode_set_nofb(struct drm_crtc *crtc) struct vmw_plane_state *vps; int ret; - - sou = vmw_crtc_to_sou(crtc); + sou = vmw_crtc_to_sou(crtc); dev_priv = vmw_priv(crtc->dev); - ps = crtc->primary->state; - fb = ps->fb; - vps = vmw_plane_state_to_vps(ps); + ps = crtc->primary->state; + fb = ps->fb; + vps = vmw_plane_state_to_vps(ps); vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; @@ -240,11 +234,25 @@ static void vmw_sou_crtc_mode_set_nofb(struct drm_crtc *crtc) } if (vfb) { + struct drm_connector_state *conn_state; + struct vmw_connector_state *vmw_conn_state; + int x, y; + sou->buffer = vps->bo; sou->buffer_size = vps->bo_size; - ret = vmw_sou_fifo_create(dev_priv, sou, crtc->x, crtc->y, - &crtc->mode); + if (sou->base.is_implicit) { + x = crtc->x; + y = crtc->y; + } else { + conn_state = sou->base.connector.state; + vmw_conn_state = vmw_connector_state_to_vcs(conn_state); + + x = vmw_conn_state->gui_x; + y = vmw_conn_state->gui_y; + } + + ret = vmw_sou_fifo_create(dev_priv, sou, x, y, &crtc->mode); if (ret) DRM_ERROR("Failed to define Screen Object %dx%d\n", crtc->x, crtc->y); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 537df9034008..15f2cb2a151b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -178,13 +178,9 @@ static int vmw_stdu_define_st(struct vmw_private *dev_priv, cmd->body.height = mode->vdisplay; cmd->body.flags = (0 == cmd->body.stid) ? SVGA_STFLAG_PRIMARY : 0; cmd->body.dpi = 0; - if (stdu->base.is_implicit) { - cmd->body.xRoot = crtc_x; - cmd->body.yRoot = crtc_y; - } else { - cmd->body.xRoot = stdu->base.gui_x; - cmd->body.yRoot = stdu->base.gui_y; - } + cmd->body.xRoot = crtc_x; + cmd->body.yRoot = crtc_y; + stdu->base.set_gui_x = cmd->body.xRoot; stdu->base.set_gui_y = cmd->body.yRoot; @@ -374,11 +370,14 @@ static void vmw_stdu_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct vmw_private *dev_priv; struct vmw_screen_target_display_unit *stdu; - int ret; - + struct drm_connector_state *conn_state; + struct vmw_connector_state *vmw_conn_state; + int x, y, ret; - stdu = vmw_crtc_to_stdu(crtc); + stdu = vmw_crtc_to_stdu(crtc); dev_priv = vmw_priv(crtc->dev); + conn_state = stdu->base.connector.state; + vmw_conn_state = vmw_connector_state_to_vcs(conn_state); if (stdu->defined) { ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); @@ -397,8 +396,16 @@ static void vmw_stdu_crtc_mode_set_nofb(struct drm_crtc *crtc) if (!crtc->state->enable) return; + if (stdu->base.is_implicit) { + x = crtc->x; + y = crtc->y; + } else { + x = vmw_conn_state->gui_x; + y = vmw_conn_state->gui_y; + } + vmw_svga_enable(dev_priv); - ret = vmw_stdu_define_st(dev_priv, stdu, &crtc->mode, crtc->x, crtc->y); + ret = vmw_stdu_define_st(dev_priv, stdu, &crtc->mode, x, y); if (ret) DRM_ERROR("Failed to define Screen Target of size %dx%d\n", -- cgit v1.2.3 From 14b1c33e84295693c3b1a1d7c6ac82b3f384cd17 Mon Sep 17 00:00:00 2001 From: Deepak Rawat Date: Wed, 20 Jun 2018 14:48:35 -0700 Subject: drm/vmwgfx: Add new ioctl for GB surface create and reference New ioctls DRM_VMW_GB_SURFACE_CREATE_EXT and DRM_VMW_GB_SURFACE_REF_EXT are added which support 64-bit wide svga device surface flags, quality level and multisample pattern. Signed-off-by: Deepak Rawat Reviewed-by: Sinclair Yeh Reviewed-by: Brian Paul Reviewed-by: Thomas Hellstrom Reviewed-by: Charmaine Lee Signed-off-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 12 + drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 8 + drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 20 +- drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 2 + drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 468 +++++++++++++++++++++----------- include/uapi/drm/vmwgfx_drm.h | 102 +++++++ 6 files changed, 437 insertions(+), 175 deletions(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_kms.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 6cf81e19182f..59229111f303 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -137,6 +137,12 @@ #define DRM_IOCTL_VMW_CREATE_EXTENDED_CONTEXT \ DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_CREATE_EXTENDED_CONTEXT, \ struct drm_vmw_context_arg) +#define DRM_IOCTL_VMW_GB_SURFACE_CREATE_EXT \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_GB_SURFACE_CREATE_EXT, \ + union drm_vmw_gb_surface_create_ext_arg) +#define DRM_IOCTL_VMW_GB_SURFACE_REF_EXT \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_GB_SURFACE_REF_EXT, \ + union drm_vmw_gb_surface_reference_ext_arg) /** * The core DRM version of this macro doesn't account for @@ -224,6 +230,12 @@ static const struct drm_ioctl_desc vmw_ioctls[] = { VMW_IOCTL_DEF(VMW_CREATE_EXTENDED_CONTEXT, vmw_extended_context_define_ioctl, DRM_AUTH | DRM_RENDER_ALLOW), + VMW_IOCTL_DEF(VMW_GB_SURFACE_CREATE_EXT, + vmw_gb_surface_define_ext_ioctl, + DRM_AUTH | DRM_RENDER_ALLOW), + VMW_IOCTL_DEF(VMW_GB_SURFACE_REF_EXT, + vmw_gb_surface_reference_ext_ioctl, + DRM_AUTH | DRM_RENDER_ALLOW), }; static const struct pci_device_id vmw_pci_id_list[] = { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 7e5c93083036..59af14714797 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -1087,7 +1087,15 @@ int vmw_surface_gb_priv_define(struct drm_device *dev, uint32_t multisample_count, uint32_t array_size, struct drm_vmw_size size, + SVGA3dMSPattern multisample_pattern, + SVGA3dMSQualityLevel quality_level, struct vmw_surface **srf_out); +extern int vmw_gb_surface_define_ext_ioctl(struct drm_device *dev, + void *data, + struct drm_file *file_priv); +extern int vmw_gb_surface_reference_ext_ioctl(struct drm_device *dev, + void *data, + struct drm_file *file_priv); /* * Shader management - vmwgfx_shader.c diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 0fb363458ab5..3201b0a51d10 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1238,15 +1238,17 @@ static int vmw_create_bo_proxy(struct drm_device *dev, content_base_size.depth = 1; ret = vmw_surface_gb_priv_define(dev, - 0, /* kernel visible only */ - 0, /* flags */ - format, - true, /* can be a scanout buffer */ - 1, /* num of mip levels */ - 0, - 0, - content_base_size, - srf_out); + 0, /* kernel visible only */ + 0, /* flags */ + format, + true, /* can be a scanout buffer */ + 1, /* num of mip levels */ + 0, + 0, + content_base_size, + SVGA3D_MS_PATTERN_NONE, + SVGA3D_MS_QUALITY_NONE, + srf_out); if (ret) { DRM_ERROR("Failed to allocate proxy content buffer\n"); return ret; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 6630abf3a95c..f9872c9e60c4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -1188,6 +1188,8 @@ vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane, content_srf.multisample_count, 0, display_base_size, + content_srf.multisample_pattern, + content_srf.quality_level, &vps->surf); if (ret != 0) { DRM_ERROR("Couldn't allocate STDU surface.\n"); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index a5f93f62c7fa..1d4c010a0e48 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -33,6 +33,10 @@ #include "vmwgfx_binding.h" #include "device_include/svga3d_surfacedefs.h" +#define SVGA3D_FLAGS_64(upper32, lower32) (((uint64_t)upper32 << 32) | lower32) +#define SVGA3D_FLAGS_UPPER_32(svga3d_flags) (svga3d_flags >> 32) +#define SVGA3D_FLAGS_LOWER_32(svga3d_flags) \ + (svga3d_flags & ((uint64_t)U32_MAX)) /** * struct vmw_user_surface - User-space visible surface resource @@ -81,7 +85,16 @@ static int vmw_gb_surface_unbind(struct vmw_resource *res, bool readback, struct ttm_validate_buffer *val_buf); static int vmw_gb_surface_destroy(struct vmw_resource *res); - +static int +vmw_gb_surface_define_internal(struct drm_device *dev, + struct drm_vmw_gb_surface_create_ext_req *req, + struct drm_vmw_gb_surface_create_rep *rep, + struct drm_file *file_priv); +static int +vmw_gb_surface_reference_internal(struct drm_device *dev, + struct drm_vmw_surface_arg *req, + struct drm_vmw_gb_surface_ref_ext_rep *rep, + struct drm_file *file_priv); static const struct vmw_user_resource_conv user_surface_conv = { .object_type = VMW_RES_SURFACE, @@ -1289,193 +1302,55 @@ static int vmw_gb_surface_destroy(struct vmw_resource *res) /** * vmw_gb_surface_define_ioctl - Ioctl function implementing - * the user surface define functionality. + * the user surface define functionality. * - * @dev: Pointer to a struct drm_device. - * @data: Pointer to data copied from / to user-space. - * @file_priv: Pointer to a drm file private structure. + * @dev: Pointer to a struct drm_device. + * @data: Pointer to data copied from / to user-space. + * @file_priv: Pointer to a drm file private structure. */ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_user_surface *user_srf; - struct vmw_surface *srf; - struct vmw_resource *res; - struct vmw_resource *tmp; union drm_vmw_gb_surface_create_arg *arg = (union drm_vmw_gb_surface_create_arg *)data; - struct drm_vmw_gb_surface_create_req *req = &arg->req; struct drm_vmw_gb_surface_create_rep *rep = &arg->rep; - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - int ret; - uint32_t size; - uint32_t backup_handle = 0; - - if (req->multisample_count != 0) - return -EINVAL; - - if (req->mip_levels > DRM_VMW_MAX_MIP_LEVELS) - return -EINVAL; - - if (unlikely(vmw_user_surface_size == 0)) - vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) + - 128; - - size = vmw_user_surface_size + 128; - - /* Define a surface based on the parameters. */ - ret = vmw_surface_gb_priv_define(dev, - size, - (SVGA3dSurfaceAllFlags)req->svga3d_flags, - req->format, - req->drm_surface_flags & drm_vmw_surface_flag_scanout, - req->mip_levels, - req->multisample_count, - req->array_size, - req->base_size, - &srf); - if (unlikely(ret != 0)) - return ret; - - user_srf = container_of(srf, struct vmw_user_surface, srf); - if (drm_is_primary_client(file_priv)) - user_srf->master = drm_master_get(file_priv->master); - - ret = ttm_read_lock(&dev_priv->reservation_sem, true); - if (unlikely(ret != 0)) - return ret; - - res = &user_srf->srf.res; - - - if (req->buffer_handle != SVGA3D_INVALID_ID) { - ret = vmw_user_bo_lookup(tfile, req->buffer_handle, - &res->backup, - &user_srf->backup_base); - if (ret == 0) { - if (res->backup->base.num_pages * PAGE_SIZE < - res->backup_size) { - DRM_ERROR("Surface backup buffer is too small.\n"); - vmw_bo_unreference(&res->backup); - ret = -EINVAL; - goto out_unlock; - } else { - backup_handle = req->buffer_handle; - } - } - } else if (req->drm_surface_flags & drm_vmw_surface_flag_create_buffer) - ret = vmw_user_bo_alloc(dev_priv, tfile, - res->backup_size, - req->drm_surface_flags & - drm_vmw_surface_flag_shareable, - &backup_handle, - &res->backup, - &user_srf->backup_base); - - if (unlikely(ret != 0)) { - vmw_resource_unreference(&res); - goto out_unlock; - } - - tmp = vmw_resource_reference(res); - ret = ttm_prime_object_init(tfile, res->backup_size, &user_srf->prime, - req->drm_surface_flags & - drm_vmw_surface_flag_shareable, - VMW_RES_SURFACE, - &vmw_user_surface_base_release, NULL); - - if (unlikely(ret != 0)) { - vmw_resource_unreference(&tmp); - vmw_resource_unreference(&res); - goto out_unlock; - } - - rep->handle = user_srf->prime.base.hash.key; - rep->backup_size = res->backup_size; - if (res->backup) { - rep->buffer_map_handle = - drm_vma_node_offset_addr(&res->backup->base.vma_node); - rep->buffer_size = res->backup->base.num_pages * PAGE_SIZE; - rep->buffer_handle = backup_handle; - } else { - rep->buffer_map_handle = 0; - rep->buffer_size = 0; - rep->buffer_handle = SVGA3D_INVALID_ID; - } + struct drm_vmw_gb_surface_create_ext_req req_ext; - vmw_resource_unreference(&res); + req_ext.base = arg->req; + req_ext.version = drm_vmw_gb_surface_v1; + req_ext.svga3d_flags_upper_32_bits = 0; + req_ext.multisample_pattern = SVGA3D_MS_PATTERN_NONE; + req_ext.quality_level = SVGA3D_MS_QUALITY_NONE; + req_ext.must_be_zero = 0; -out_unlock: - ttm_read_unlock(&dev_priv->reservation_sem); - return ret; + return vmw_gb_surface_define_internal(dev, &req_ext, rep, file_priv); } /** * vmw_gb_surface_reference_ioctl - Ioctl function implementing - * the user surface reference functionality. + * the user surface reference functionality. * - * @dev: Pointer to a struct drm_device. - * @data: Pointer to data copied from / to user-space. - * @file_priv: Pointer to a drm file private structure. + * @dev: Pointer to a struct drm_device. + * @data: Pointer to data copied from / to user-space. + * @file_priv: Pointer to a drm file private structure. */ int vmw_gb_surface_reference_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct vmw_private *dev_priv = vmw_priv(dev); union drm_vmw_gb_surface_reference_arg *arg = (union drm_vmw_gb_surface_reference_arg *)data; struct drm_vmw_surface_arg *req = &arg->req; struct drm_vmw_gb_surface_ref_rep *rep = &arg->rep; - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - struct vmw_surface *srf; - struct vmw_user_surface *user_srf; - struct ttm_base_object *base; - uint32_t backup_handle; - int ret = -EINVAL; + struct drm_vmw_gb_surface_ref_ext_rep rep_ext; + int ret; + + ret = vmw_gb_surface_reference_internal(dev, req, &rep_ext, file_priv); - ret = vmw_surface_handle_reference(dev_priv, file_priv, req->sid, - req->handle_type, &base); if (unlikely(ret != 0)) return ret; - user_srf = container_of(base, struct vmw_user_surface, prime.base); - srf = &user_srf->srf; - if (!srf->res.backup) { - DRM_ERROR("Shared GB surface is missing a backup buffer.\n"); - goto out_bad_resource; - } - - mutex_lock(&dev_priv->cmdbuf_mutex); /* Protect res->backup */ - ret = vmw_user_bo_reference(tfile, srf->res.backup, &backup_handle); - mutex_unlock(&dev_priv->cmdbuf_mutex); - - if (unlikely(ret != 0)) { - DRM_ERROR("Could not add a reference to a GB surface " - "backup buffer.\n"); - (void) ttm_ref_object_base_unref(tfile, base->hash.key, - TTM_REF_USAGE); - goto out_bad_resource; - } - - rep->creq.svga3d_flags = (uint32_t)srf->flags; - rep->creq.format = srf->format; - rep->creq.mip_levels = srf->mip_levels[0]; - rep->creq.drm_surface_flags = 0; - rep->creq.multisample_count = srf->multisample_count; - rep->creq.autogen_filter = srf->autogen_filter; - rep->creq.array_size = srf->array_size; - rep->creq.buffer_handle = backup_handle; - rep->creq.base_size = srf->base_size; - rep->crep.handle = user_srf->prime.base.hash.key; - rep->crep.backup_size = srf->res.backup_size; - rep->crep.buffer_handle = backup_handle; - rep->crep.buffer_map_handle = - drm_vma_node_offset_addr(&srf->res.backup->base.vma_node); - rep->crep.buffer_size = srf->res.backup->base.num_pages * PAGE_SIZE; - -out_bad_resource: - ttm_base_object_unref(&base); + rep->creq = rep_ext.creq.base; + rep->crep = rep_ext.crep; return ret; } @@ -1493,6 +1368,8 @@ out_bad_resource: * @multisample_count: * @array_size: Surface array size. * @size: width, heigh, depth of the surface requested + * @multisample_pattern: Multisampling pattern when msaa is supported + * @quality_level: Precision settings * @user_srf_out: allocated user_srf. Set to NULL on failure. * * GB surfaces allocated by this function will not have a user mode handle, and @@ -1509,6 +1386,8 @@ int vmw_surface_gb_priv_define(struct drm_device *dev, uint32_t multisample_count, uint32_t array_size, struct drm_vmw_size size, + SVGA3dMSPattern multisample_pattern, + SVGA3dMSQualityLevel quality_level, struct vmw_surface **srf_out) { struct vmw_private *dev_priv = vmw_priv(dev); @@ -1519,7 +1398,7 @@ int vmw_surface_gb_priv_define(struct drm_device *dev, }; struct vmw_surface *srf; int ret; - u32 num_layers; + u32 num_layers = 1; *srf_out = NULL; @@ -1594,15 +1473,13 @@ int vmw_surface_gb_priv_define(struct drm_device *dev, srf->autogen_filter = SVGA3D_TEX_FILTER_NONE; srf->array_size = array_size; srf->multisample_count = multisample_count; - srf->multisample_pattern = SVGA3D_MS_PATTERN_NONE; - srf->quality_level = SVGA3D_MS_QUALITY_NONE; + srf->multisample_pattern = multisample_pattern; + srf->quality_level = quality_level; if (array_size) num_layers = array_size; else if (svga3d_flags & SVGA3D_SURFACE_CUBEMAP) num_layers = SVGA3D_MAX_SURFACE_FACES; - else - num_layers = 1; srf->res.backup_size = svga3dsurface_get_serialized_size(srf->format, @@ -1633,3 +1510,262 @@ out_unlock: ttm_read_unlock(&dev_priv->reservation_sem); return ret; } + +/** + * vmw_gb_surface_define_ext_ioctl - Ioctl function implementing + * the user surface define functionality. + * + * @dev: Pointer to a struct drm_device. + * @data: Pointer to data copied from / to user-space. + * @file_priv: Pointer to a drm file private structure. + */ +int vmw_gb_surface_define_ext_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + union drm_vmw_gb_surface_create_ext_arg *arg = + (union drm_vmw_gb_surface_create_ext_arg *)data; + struct drm_vmw_gb_surface_create_ext_req *req = &arg->req; + struct drm_vmw_gb_surface_create_rep *rep = &arg->rep; + + return vmw_gb_surface_define_internal(dev, req, rep, file_priv); +} + +/** + * vmw_gb_surface_reference_ext_ioctl - Ioctl function implementing + * the user surface reference functionality. + * + * @dev: Pointer to a struct drm_device. + * @data: Pointer to data copied from / to user-space. + * @file_priv: Pointer to a drm file private structure. + */ +int vmw_gb_surface_reference_ext_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + union drm_vmw_gb_surface_reference_ext_arg *arg = + (union drm_vmw_gb_surface_reference_ext_arg *)data; + struct drm_vmw_surface_arg *req = &arg->req; + struct drm_vmw_gb_surface_ref_ext_rep *rep = &arg->rep; + + return vmw_gb_surface_reference_internal(dev, req, rep, file_priv); +} + +/** + * vmw_gb_surface_define_internal - Ioctl function implementing + * the user surface define functionality. + * + * @dev: Pointer to a struct drm_device. + * @req: Request argument from user-space. + * @rep: Response argument to user-space. + * @file_priv: Pointer to a drm file private structure. + */ +static int +vmw_gb_surface_define_internal(struct drm_device *dev, + struct drm_vmw_gb_surface_create_ext_req *req, + struct drm_vmw_gb_surface_create_rep *rep, + struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_user_surface *user_srf; + struct vmw_surface *srf; + struct vmw_resource *res; + struct vmw_resource *tmp; + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + int ret; + uint32_t size; + uint32_t backup_handle = 0; + SVGA3dSurfaceAllFlags svga3d_flags_64 = + SVGA3D_FLAGS_64(req->svga3d_flags_upper_32_bits, + req->base.svga3d_flags); + + if (!dev_priv->has_sm4_1) { + /* + * If SM4_1 is not support then cannot send 64-bit flag to + * device. + */ + if (req->svga3d_flags_upper_32_bits != 0) + return -EINVAL; + + if (req->base.multisample_count != 0) + return -EINVAL; + + if (req->multisample_pattern != SVGA3D_MS_PATTERN_NONE) + return -EINVAL; + + if (req->quality_level != SVGA3D_MS_QUALITY_NONE) + return -EINVAL; + } + + if (req->base.mip_levels > DRM_VMW_MAX_MIP_LEVELS) + return -EINVAL; + + if (unlikely(vmw_user_surface_size == 0)) + vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) + + 128; + + size = vmw_user_surface_size + 128; + + /* Define a surface based on the parameters. */ + ret = vmw_surface_gb_priv_define(dev, + size, + svga3d_flags_64, + req->base.format, + req->base.drm_surface_flags & + drm_vmw_surface_flag_scanout, + req->base.mip_levels, + req->base.multisample_count, + req->base.array_size, + req->base.base_size, + req->multisample_pattern, + req->quality_level, + &srf); + if (unlikely(ret != 0)) + return ret; + + user_srf = container_of(srf, struct vmw_user_surface, srf); + if (drm_is_primary_client(file_priv)) + user_srf->master = drm_master_get(file_priv->master); + + ret = ttm_read_lock(&dev_priv->reservation_sem, true); + if (unlikely(ret != 0)) + return ret; + + res = &user_srf->srf.res; + + if (req->base.buffer_handle != SVGA3D_INVALID_ID) { + ret = vmw_user_bo_lookup(tfile, req->base.buffer_handle, + &res->backup, + &user_srf->backup_base); + if (ret == 0) { + if (res->backup->base.num_pages * PAGE_SIZE < + res->backup_size) { + DRM_ERROR("Surface backup buffer too small.\n"); + vmw_bo_unreference(&res->backup); + ret = -EINVAL; + goto out_unlock; + } else { + backup_handle = req->base.buffer_handle; + } + } + } else if (req->base.drm_surface_flags & + drm_vmw_surface_flag_create_buffer) + ret = vmw_user_bo_alloc(dev_priv, tfile, + res->backup_size, + req->base.drm_surface_flags & + drm_vmw_surface_flag_shareable, + &backup_handle, + &res->backup, + &user_srf->backup_base); + + if (unlikely(ret != 0)) { + vmw_resource_unreference(&res); + goto out_unlock; + } + + tmp = vmw_resource_reference(res); + ret = ttm_prime_object_init(tfile, res->backup_size, &user_srf->prime, + req->base.drm_surface_flags & + drm_vmw_surface_flag_shareable, + VMW_RES_SURFACE, + &vmw_user_surface_base_release, NULL); + + if (unlikely(ret != 0)) { + vmw_resource_unreference(&tmp); + vmw_resource_unreference(&res); + goto out_unlock; + } + + rep->handle = user_srf->prime.base.hash.key; + rep->backup_size = res->backup_size; + if (res->backup) { + rep->buffer_map_handle = + drm_vma_node_offset_addr(&res->backup->base.vma_node); + rep->buffer_size = res->backup->base.num_pages * PAGE_SIZE; + rep->buffer_handle = backup_handle; + } else { + rep->buffer_map_handle = 0; + rep->buffer_size = 0; + rep->buffer_handle = SVGA3D_INVALID_ID; + } + + vmw_resource_unreference(&res); + +out_unlock: + ttm_read_unlock(&dev_priv->reservation_sem); + return ret; +} + +/** + * vmw_gb_surface_reference_internal - Ioctl function implementing + * the user surface reference functionality. + * + * @dev: Pointer to a struct drm_device. + * @req: Pointer to user-space request surface arg. + * @rep: Pointer to response to user-space. + * @file_priv: Pointer to a drm file private structure. + */ +static int +vmw_gb_surface_reference_internal(struct drm_device *dev, + struct drm_vmw_surface_arg *req, + struct drm_vmw_gb_surface_ref_ext_rep *rep, + struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_surface *srf; + struct vmw_user_surface *user_srf; + struct ttm_base_object *base; + uint32_t backup_handle; + int ret = -EINVAL; + + ret = vmw_surface_handle_reference(dev_priv, file_priv, req->sid, + req->handle_type, &base); + if (unlikely(ret != 0)) + return ret; + + user_srf = container_of(base, struct vmw_user_surface, prime.base); + srf = &user_srf->srf; + if (!srf->res.backup) { + DRM_ERROR("Shared GB surface is missing a backup buffer.\n"); + goto out_bad_resource; + } + + mutex_lock(&dev_priv->cmdbuf_mutex); /* Protect res->backup */ + ret = vmw_user_bo_reference(tfile, srf->res.backup, &backup_handle); + mutex_unlock(&dev_priv->cmdbuf_mutex); + + if (unlikely(ret != 0)) { + DRM_ERROR("Could not add a reference to a GB surface " + "backup buffer.\n"); + (void) ttm_ref_object_base_unref(tfile, base->hash.key, + TTM_REF_USAGE); + goto out_bad_resource; + } + + rep->creq.base.svga3d_flags = SVGA3D_FLAGS_LOWER_32(srf->flags); + rep->creq.base.format = srf->format; + rep->creq.base.mip_levels = srf->mip_levels[0]; + rep->creq.base.drm_surface_flags = 0; + rep->creq.base.multisample_count = srf->multisample_count; + rep->creq.base.autogen_filter = srf->autogen_filter; + rep->creq.base.array_size = srf->array_size; + rep->creq.base.buffer_handle = backup_handle; + rep->creq.base.base_size = srf->base_size; + rep->crep.handle = user_srf->prime.base.hash.key; + rep->crep.backup_size = srf->res.backup_size; + rep->crep.buffer_handle = backup_handle; + rep->crep.buffer_map_handle = + drm_vma_node_offset_addr(&srf->res.backup->base.vma_node); + rep->crep.buffer_size = srf->res.backup->base.num_pages * PAGE_SIZE; + + rep->creq.version = drm_vmw_gb_surface_v1; + rep->creq.svga3d_flags_upper_32_bits = + SVGA3D_FLAGS_UPPER_32(srf->flags); + rep->creq.multisample_pattern = srf->multisample_pattern; + rep->creq.quality_level = srf->quality_level; + rep->creq.must_be_zero = 0; + +out_bad_resource: + ttm_base_object_unref(&base); + + return ret; +} diff --git a/include/uapi/drm/vmwgfx_drm.h b/include/uapi/drm/vmwgfx_drm.h index 84e81b38ca18..68ff37d4c035 100644 --- a/include/uapi/drm/vmwgfx_drm.h +++ b/include/uapi/drm/vmwgfx_drm.h @@ -69,6 +69,8 @@ extern "C" { #define DRM_VMW_GB_SURFACE_REF 24 #define DRM_VMW_SYNCCPU 25 #define DRM_VMW_CREATE_EXTENDED_CONTEXT 26 +#define DRM_VMW_GB_SURFACE_CREATE_EXT 27 +#define DRM_VMW_GB_SURFACE_REF_EXT 28 /*************************************************************************/ /** @@ -1105,6 +1107,106 @@ struct drm_vmw_handle_close_arg { }; #define drm_vmw_unref_dmabuf_arg drm_vmw_handle_close_arg +/*************************************************************************/ +/** + * DRM_VMW_GB_SURFACE_CREATE_EXT - Create a host guest-backed surface. + * + * Allocates a surface handle and queues a create surface command + * for the host on the first use of the surface. The surface ID can + * be used as the surface ID in commands referencing the surface. + * + * This new command extends DRM_VMW_GB_SURFACE_CREATE by adding version + * parameter and 64 bit svga flag. + */ + +/** + * enum drm_vmw_surface_version + * + * @drm_vmw_surface_gb_v1: Corresponds to current gb surface format with + * svga3d surface flags split into 2, upper half and lower half. + */ +enum drm_vmw_surface_version { + drm_vmw_gb_surface_v1 +}; + +/** + * struct drm_vmw_gb_surface_create_ext_req + * + * @base: Surface create parameters. + * @version: Version of surface create ioctl. + * @svga3d_flags_upper_32_bits: Upper 32 bits of svga3d flags. + * @multisample_pattern: Multisampling pattern when msaa is supported. + * @quality_level: Precision settings for each sample. + * @must_be_zero: Reserved for future usage. + * + * Input argument to the DRM_VMW_GB_SURFACE_CREATE_EXT Ioctl. + * Part of output argument for the DRM_VMW_GB_SURFACE_REF_EXT Ioctl. + */ +struct drm_vmw_gb_surface_create_ext_req { + struct drm_vmw_gb_surface_create_req base; + enum drm_vmw_surface_version version; + uint32_t svga3d_flags_upper_32_bits; + SVGA3dMSPattern multisample_pattern; + SVGA3dMSQualityLevel quality_level; + uint64_t must_be_zero; +}; + +/** + * union drm_vmw_gb_surface_create_ext_arg + * + * @req: Input argument as described above. + * @rep: Output argument as described above. + * + * Argument to the DRM_VMW_GB_SURFACE_CREATE_EXT ioctl. + */ +union drm_vmw_gb_surface_create_ext_arg { + struct drm_vmw_gb_surface_create_rep rep; + struct drm_vmw_gb_surface_create_ext_req req; +}; + +/*************************************************************************/ +/** + * DRM_VMW_GB_SURFACE_REF_EXT - Reference a host surface. + * + * Puts a reference on a host surface with a given handle, as previously + * returned by the DRM_VMW_GB_SURFACE_CREATE_EXT ioctl. + * A reference will make sure the surface isn't destroyed while we hold + * it and will allow the calling client to use the surface handle in + * the command stream. + * + * On successful return, the Ioctl returns the surface information given + * to and returned from the DRM_VMW_GB_SURFACE_CREATE_EXT ioctl. + */ + +/** + * struct drm_vmw_gb_surface_ref_ext_rep + * + * @creq: The data used as input when the surface was created, as described + * above at "struct drm_vmw_gb_surface_create_ext_req" + * @crep: Additional data output when the surface was created, as described + * above at "struct drm_vmw_gb_surface_create_rep" + * + * Output Argument to the DRM_VMW_GB_SURFACE_REF_EXT ioctl. + */ +struct drm_vmw_gb_surface_ref_ext_rep { + struct drm_vmw_gb_surface_create_ext_req creq; + struct drm_vmw_gb_surface_create_rep crep; +}; + +/** + * union drm_vmw_gb_surface_reference_ext_arg + * + * @req: Input data as described above at "struct drm_vmw_surface_arg" + * @rep: Output data as described above at + * "struct drm_vmw_gb_surface_ref_ext_rep" + * + * Argument to the DRM_VMW_GB_SURFACE_REF Ioctl. + */ +union drm_vmw_gb_surface_reference_ext_arg { + struct drm_vmw_gb_surface_ref_ext_rep rep; + struct drm_vmw_surface_arg req; +}; + #if defined(__cplusplus) } #endif -- cgit v1.2.3