diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile index 473d00451b0f..aac17a640cce 100644 --- a/drivers/gpu/drm/vmwgfx/Makefile +++ b/drivers/gpu/drm/vmwgfx/Makefile @@ -8,6 +8,7 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \ vmwgfx_fence.o vmwgfx_dmabuf.o vmwgfx_scrn.o vmwgfx_context.o \ vmwgfx_surface.o vmwgfx_prime.o vmwgfx_mob.o vmwgfx_shader.o \ vmwgfx_cmdbuf_res.o vmwgfx_cmdbuf.o vmwgfx_stdu.o \ - vmwgfx_cotable.o vmwgfx_so.o vmwgfx_binding.o vmwgfx_msg.o + vmwgfx_cotable.o vmwgfx_so.o vmwgfx_binding.o vmwgfx_msg.o \ + vmwgfx_simple_resource.o vmwgfx_va.o obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index d08f26973d0b..f6e936d90517 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -246,13 +246,13 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, void *ptr); MODULE_PARM_DESC(enable_fbdev, "Enable vmwgfx fbdev"); -module_param_named(enable_fbdev, enable_fbdev, int, S_IRUSR | S_IWUSR); +module_param_named(enable_fbdev, enable_fbdev, int, 0600); MODULE_PARM_DESC(force_dma_api, "Force using the DMA API for TTM pages"); -module_param_named(force_dma_api, vmw_force_iommu, int, S_IRUSR | S_IWUSR); +module_param_named(force_dma_api, vmw_force_iommu, int, 0600); MODULE_PARM_DESC(restrict_iommu, "Try to limit IOMMU usage for TTM pages"); -module_param_named(restrict_iommu, vmw_restrict_iommu, int, S_IRUSR | S_IWUSR); +module_param_named(restrict_iommu, vmw_restrict_iommu, int, 0600); MODULE_PARM_DESC(force_coherent, "Force coherent TTM pages"); -module_param_named(force_coherent, vmw_force_coherent, int, S_IRUSR | S_IWUSR); +module_param_named(force_coherent, vmw_force_coherent, int, 0600); MODULE_PARM_DESC(restrict_dma_mask, "Restrict DMA mask to 44 bits with IOMMU"); module_param_named(restrict_dma_mask, vmw_restrict_dma_mask, int, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(assume_16bpp, "Assume 16-bpp when filtering modes"); @@ -650,6 +650,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) spin_lock_init(&dev_priv->waiter_lock); spin_lock_init(&dev_priv->cap_lock); spin_lock_init(&dev_priv->svga_lock); + spin_lock_init(&dev_priv->cursor_lock); for (i = vmw_res_context; i < vmw_res_max; ++i) { idr_init(&dev_priv->res_idr[i]); @@ -897,6 +898,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) goto out_no_fifo; DRM_INFO("DX: %s\n", dev_priv->has_dx ? "yes." : "no."); + DRM_INFO("Atomic: %s\n", + (dev->driver->driver_features & DRIVER_ATOMIC) ? "yes" : "no"); snprintf(host_log, sizeof(host_log), "vmwgfx: %s-%s", VMWGFX_REPO, VMWGFX_GIT_VERSION); @@ -1509,7 +1512,7 @@ static const struct file_operations vmwgfx_driver_fops = { static struct drm_driver driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | - DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER, + DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER | DRIVER_ATOMIC, .load = vmw_driver_load, .unload = vmw_driver_unload, .lastclose = vmw_lastclose, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 59ff4197173a..130d51c5ec6a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -153,7 +153,6 @@ enum vmw_cmdbuf_res_type { struct vmw_cmdbuf_res_manager; struct vmw_cursor_snooper { - struct drm_crtc *crtc; size_t age; uint32_t *image; }; @@ -415,6 +414,7 @@ struct vmw_private { unsigned num_implicit; struct vmw_framebuffer *implicit_fb; struct mutex global_kms_state_mutex; + spinlock_t cursor_lock; /* * Context and surface management. diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index e9005b9a5e8c..09e120d50e65 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -434,7 +434,7 @@ static int vmw_fb_kms_detach(struct vmw_fb_par *par, set.y = 0; set.mode = NULL; set.fb = NULL; - set.num_connectors = 1; + set.num_connectors = 0; set.connectors = &par->con; ret = drm_mode_set_config_internal(&set); if (ret) { @@ -451,13 +451,15 @@ static int vmw_fb_kms_detach(struct vmw_fb_par *par, } if (par->vmw_bo && detach_bo) { + struct vmw_private *vmw_priv = par->vmw_priv; + if (par->bo_ptr) { ttm_bo_kunmap(&par->map); par->bo_ptr = NULL; } if (unref_bo) vmw_dmabuf_unreference(&par->vmw_bo); - else + else if (vmw_priv->active_display_unit != vmw_du_legacy) vmw_dmabuf_unpin(par->vmw_priv, par->vmw_bo, false); } @@ -585,18 +587,25 @@ static int vmw_fb_set_par(struct fb_info *info) /* * Pin before mapping. Since we don't know in what placement - * to pin, call into KMS to do it for us. + * to pin, call into KMS to do it for us. LDU doesn't require + * additional pinning because set_config() would've pinned + * it already */ - ret = vfb->pin(vfb); - if (ret) { - DRM_ERROR("Could not pin the fbdev framebuffer.\n"); - goto out_unlock; + if (vmw_priv->active_display_unit != vmw_du_legacy) { + ret = vfb->pin(vfb); + if (ret) { + DRM_ERROR("Could not pin the fbdev " + "framebuffer.\n"); + goto out_unlock; + } } ret = ttm_bo_kmap(&par->vmw_bo->base, 0, par->vmw_bo->base.num_pages, &par->map); if (ret) { - vfb->unpin(vfb); + if (vmw_priv->active_display_unit != vmw_du_legacy) + vfb->unpin(vfb); + DRM_ERROR("Could not map the fbdev framebuffer.\n"); goto out_unlock; } @@ -822,7 +831,9 @@ int vmw_fb_off(struct vmw_private *vmw_priv) flush_delayed_work(&par->local_work); mutex_lock(&par->bo_mutex); + drm_modeset_lock_all(vmw_priv->dev); (void) vmw_fb_kms_detach(par, true, false); + drm_modeset_unlock_all(vmw_priv->dev); mutex_unlock(&par->bo_mutex); return 0; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index d492d57d5309..c18c81f63ac3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -26,6 +26,10 @@ **************************************************************************/ #include "vmwgfx_kms.h" +#include +#include +#include +#include /* Might need a hrtimer here? */ @@ -33,10 +37,9 @@ void vmw_du_cleanup(struct vmw_display_unit *du) { - if (du->cursor_surface) - vmw_surface_unreference(&du->cursor_surface); - if (du->cursor_dmabuf) - vmw_dmabuf_unreference(&du->cursor_dmabuf); + drm_plane_cleanup(&du->primary); + drm_plane_cleanup(&du->cursor); + drm_connector_unregister(&du->connector); drm_crtc_cleanup(&du->crtc); drm_encoder_cleanup(&du->encoder); @@ -47,9 +50,9 @@ void vmw_du_cleanup(struct vmw_display_unit *du) * Display Unit Cursor functions */ -int vmw_cursor_update_image(struct vmw_private *dev_priv, - u32 *image, u32 width, u32 height, - u32 hotspotX, u32 hotspotY) +static int vmw_cursor_update_image(struct vmw_private *dev_priv, + u32 *image, u32 width, u32 height, + u32 hotspotX, u32 hotspotY) { struct { u32 cmd; @@ -83,10 +86,10 @@ int vmw_cursor_update_image(struct vmw_private *dev_priv, return 0; } -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_dmabuf(struct vmw_private *dev_priv, + struct vmw_dma_buffer *dmabuf, + u32 width, u32 height, + u32 hotspotX, u32 hotspotY) { struct ttm_bo_kmap_obj map; unsigned long kmap_offset; @@ -120,147 +123,22 @@ int vmw_cursor_update_dmabuf(struct vmw_private *dev_priv, } -void vmw_cursor_update_position(struct vmw_private *dev_priv, - bool show, int x, int y) +static void vmw_cursor_update_position(struct vmw_private *dev_priv, + bool show, int x, int y) { u32 *fifo_mem = dev_priv->mmio_virt; uint32_t count; + spin_lock(&dev_priv->cursor_lock); vmw_mmio_write(show ? 1 : 0, fifo_mem + SVGA_FIFO_CURSOR_ON); vmw_mmio_write(x, fifo_mem + SVGA_FIFO_CURSOR_X); vmw_mmio_write(y, fifo_mem + SVGA_FIFO_CURSOR_Y); count = vmw_mmio_read(fifo_mem + SVGA_FIFO_CURSOR_COUNT); vmw_mmio_write(++count, fifo_mem + SVGA_FIFO_CURSOR_COUNT); + spin_unlock(&dev_priv->cursor_lock); } -/* - * vmw_du_crtc_cursor_set2 - Driver cursor_set2 callback. - */ -int vmw_du_crtc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv, - uint32_t handle, uint32_t width, uint32_t height, - int32_t hot_x, int32_t hot_y) -{ - struct vmw_private *dev_priv = vmw_priv(crtc->dev); - struct vmw_display_unit *du = vmw_crtc_to_du(crtc); - struct vmw_surface *surface = NULL; - struct vmw_dma_buffer *dmabuf = NULL; - s32 hotspot_x, hotspot_y; - int ret; - - /* - * FIXME: Unclear whether there's any global state touched by the - * cursor_set function, especially vmw_cursor_update_position looks - * suspicious. For now take the easy route and reacquire all locks. We - * can do this since the caller in the drm core doesn't check anything - * which is protected by any looks. - */ - drm_modeset_unlock_crtc(crtc); - drm_modeset_lock_all(dev_priv->dev); - hotspot_x = hot_x + du->hotspot_x; - hotspot_y = hot_y + du->hotspot_y; - - /* A lot of the code assumes this */ - if (handle && (width != 64 || height != 64)) { - ret = -EINVAL; - goto out; - } - - if (handle) { - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - - ret = vmw_user_lookup_handle(dev_priv, tfile, - handle, &surface, &dmabuf); - if (ret) { - DRM_ERROR("failed to find surface or dmabuf: %i\n", ret); - ret = -EINVAL; - goto out; - } - } - - /* need to do this before taking down old image */ - if (surface && !surface->snooper.image) { - DRM_ERROR("surface not suitable for cursor\n"); - vmw_surface_unreference(&surface); - ret = -EINVAL; - goto out; - } - - /* takedown old cursor */ - if (du->cursor_surface) { - du->cursor_surface->snooper.crtc = NULL; - vmw_surface_unreference(&du->cursor_surface); - } - if (du->cursor_dmabuf) - vmw_dmabuf_unreference(&du->cursor_dmabuf); - - /* setup new image */ - ret = 0; - if (surface) { - /* vmw_user_surface_lookup takes one reference */ - du->cursor_surface = surface; - - du->cursor_surface->snooper.crtc = crtc; - du->cursor_age = du->cursor_surface->snooper.age; - ret = vmw_cursor_update_image(dev_priv, surface->snooper.image, - 64, 64, hotspot_x, hotspot_y); - } else if (dmabuf) { - /* vmw_user_surface_lookup takes one reference */ - du->cursor_dmabuf = dmabuf; - - ret = vmw_cursor_update_dmabuf(dev_priv, dmabuf, width, height, - hotspot_x, hotspot_y); - } else { - vmw_cursor_update_position(dev_priv, false, 0, 0); - goto out; - } - - if (!ret) { - vmw_cursor_update_position(dev_priv, true, - du->cursor_x + hotspot_x, - du->cursor_y + hotspot_y); - du->core_hotspot_x = hot_x; - du->core_hotspot_y = hot_y; - } - -out: - drm_modeset_unlock_all(dev_priv->dev); - drm_modeset_lock_crtc(crtc, crtc->cursor); - - return ret; -} - -int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) -{ - struct vmw_private *dev_priv = vmw_priv(crtc->dev); - struct vmw_display_unit *du = vmw_crtc_to_du(crtc); - bool shown = du->cursor_surface || du->cursor_dmabuf ? true : false; - - du->cursor_x = x + du->set_gui_x; - du->cursor_y = y + du->set_gui_y; - - /* - * FIXME: Unclear whether there's any global state touched by the - * cursor_set function, especially vmw_cursor_update_position looks - * suspicious. For now take the easy route and reacquire all locks. We - * can do this since the caller in the drm core doesn't check anything - * which is protected by any looks. - */ - drm_modeset_unlock_crtc(crtc); - drm_modeset_lock_all(dev_priv->dev); - - vmw_cursor_update_position(dev_priv, shown, - du->cursor_x + du->hotspot_x + - du->core_hotspot_x, - du->cursor_y + du->hotspot_y + - du->core_hotspot_y); - - drm_modeset_unlock_all(dev_priv->dev); - drm_modeset_lock_crtc(crtc, crtc->cursor); - - return 0; -} - void vmw_kms_cursor_snoop(struct vmw_surface *srf, struct ttm_object_file *tfile, struct ttm_buffer_object *bo, @@ -395,6 +273,693 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv) mutex_unlock(&dev->mode_config.mutex); } + + +/** + * vmw_du_cursor_plane_update() - Update cursor image and location + * + * @plane: plane object to update + * @crtc: owning CRTC of @plane + * @fb: framebuffer to flip onto plane + * @crtc_x: x offset of plane on crtc + * @crtc_y: y offset of plane on crtc + * @crtc_w: width of plane rectangle on crtc + * @crtc_h: height of plane rectangle on crtc + * @src_x: Not used + * @src_y: Not used + * @src_w: Not used + * @src_h: Not used + * + * + * RETURNS: + * Zero on success, error code on failure + */ +int vmw_du_cursor_plane_update(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, + unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct vmw_private *dev_priv = vmw_priv(crtc->dev); + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + struct vmw_surface *surface = NULL; + struct vmw_dma_buffer *dmabuf = NULL; + s32 hotspot_x, hotspot_y; + int ret; + + hotspot_x = du->hotspot_x + fb->hot_x; + hotspot_y = du->hotspot_y + fb->hot_y; + + /* A lot of the code assumes this */ + if (crtc_w != 64 || crtc_h != 64) { + ret = -EINVAL; + goto out; + } + + if (vmw_framebuffer_to_vfb(fb)->dmabuf) + dmabuf = vmw_framebuffer_to_vfbd(fb)->buffer; + else + surface = vmw_framebuffer_to_vfbs(fb)->surface; + + if (surface && !surface->snooper.image) { + DRM_ERROR("surface not suitable for cursor\n"); + ret = -EINVAL; + goto out; + } + + /* setup new image */ + ret = 0; + if (surface) { + /* vmw_user_surface_lookup takes one reference */ + du->cursor_surface = surface; + + du->cursor_age = du->cursor_surface->snooper.age; + + ret = vmw_cursor_update_image(dev_priv, surface->snooper.image, + 64, 64, hotspot_x, hotspot_y); + } else if (dmabuf) { + /* vmw_user_surface_lookup takes one reference */ + du->cursor_dmabuf = dmabuf; + + ret = vmw_cursor_update_dmabuf(dev_priv, dmabuf, crtc_w, crtc_h, + hotspot_x, hotspot_y); + } else { + vmw_cursor_update_position(dev_priv, false, 0, 0); + goto out; + } + + if (!ret) { + du->cursor_x = crtc_x + du->set_gui_x; + du->cursor_y = crtc_y + du->set_gui_y; + + vmw_cursor_update_position(dev_priv, true, + du->cursor_x + hotspot_x, + du->cursor_y + hotspot_y); + } + +out: + return ret; +} + + +int vmw_du_cursor_plane_disable(struct drm_plane *plane) +{ + if (plane->fb) { + drm_framebuffer_unreference(plane->fb); + plane->fb = NULL; + } + + return -EINVAL; +} + + +void vmw_du_cursor_plane_destroy(struct drm_plane *plane) +{ + vmw_cursor_update_position(plane->dev->dev_private, false, 0, 0); + + drm_plane_cleanup(plane); +} + + +void vmw_du_primary_plane_destroy(struct drm_plane *plane) +{ + drm_plane_cleanup(plane); + + /* Planes are static in our case so we don't free it */ +} + + +/** + * vmw_du_vps_unpin_surf - unpins resource associated with a framebuffer surface + * + * @vps: plane state associated with the display surface + * @unreference: true if we also want to unreference the display. + */ +void vmw_du_plane_unpin_surf(struct vmw_plane_state *vps, + bool unreference) +{ + if (vps->surf) { + if (vps->pinned) { + vmw_resource_unpin(&vps->surf->res); + vps->pinned--; + } + + if (unreference) { + if (vps->pinned) + DRM_ERROR("Surface still pinned\n"); + vmw_surface_unreference(&vps->surf); + } + } +} + + +/** + * vmw_du_plane_cleanup_fb - Unpins the cursor + * + * @plane: display plane + * @old_state: Contains the FB to clean up + * + * Unpins the framebuffer surface + * + * Returns 0 on success + */ +void +vmw_du_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); + + vmw_du_plane_unpin_surf(vps, false); +} + + +/** + * vmw_du_cursor_plane_prepare_fb - Readies the cursor by referencing it + * + * @plane: display plane + * @new_state: info on the new plane state, including the FB + * + * Returns 0 on success + */ +int +vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct drm_framebuffer *fb = new_state->fb; + struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); + + + if (vps->surf) + vmw_surface_unreference(&vps->surf); + + if (vps->dmabuf) + vmw_dmabuf_unreference(&vps->dmabuf); + + if (fb) { + if (vmw_framebuffer_to_vfb(fb)->dmabuf) { + vps->dmabuf = vmw_framebuffer_to_vfbd(fb)->buffer; + vmw_dmabuf_reference(vps->dmabuf); + } else { + vps->surf = vmw_framebuffer_to_vfbs(fb)->surface; + vmw_surface_reference(vps->surf); + } + } + + return 0; +} + + +void +vmw_du_cursor_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc; + struct vmw_private *dev_priv = vmw_priv(crtc->dev); + + drm_atomic_set_fb_for_plane(plane->state, NULL); + vmw_cursor_update_position(dev_priv, false, 0, 0); +} + + +void +vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc; + struct vmw_private *dev_priv = vmw_priv(crtc->dev); + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + struct vmw_plane_state *vps = vmw_plane_state_to_vps(plane->state); + s32 hotspot_x, hotspot_y; + int ret = 0; + + + hotspot_x = du->hotspot_x; + hotspot_y = du->hotspot_y; + du->cursor_surface = vps->surf; + du->cursor_dmabuf = vps->dmabuf; + + /* setup new image */ + if (vps->surf) { + du->cursor_age = du->cursor_surface->snooper.age; + + ret = vmw_cursor_update_image(dev_priv, + 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 { + vmw_cursor_update_position(dev_priv, false, 0, 0); + return; + } + + if (!ret) { + du->cursor_x = plane->state->crtc_x + du->set_gui_x; + du->cursor_y = plane->state->crtc_y + du->set_gui_y; + + vmw_cursor_update_position(dev_priv, true, + du->cursor_x + hotspot_x, + du->cursor_y + hotspot_y); + } else { + DRM_ERROR("Failed to update cursor image\n"); + } +} + + +/** + * vmw_du_primary_plane_atomic_check - check if the new state is okay + * + * @plane: display plane + * @state: info on the new plane state, including the FB + * + * Check if the new state is settable given the current state. Other + * than what the atomic helper checks, we care about crtc fitting + * the FB and maintaining one active framebuffer. + * + * Returns 0 on success + */ +int vmw_du_primary_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_framebuffer *new_fb = state->fb; + bool visible; + + struct drm_rect src = { + .x1 = state->src_x, + .y1 = state->src_y, + .x2 = state->src_x + state->src_w, + .y2 = state->src_y + state->src_h, + }; + struct drm_rect dest = { + .x1 = state->crtc_x, + .y1 = state->crtc_y, + .x2 = state->crtc_x + state->crtc_w, + .y2 = state->crtc_y + state->crtc_h, + }; + struct drm_rect clip = dest; + int ret; + + ret = drm_plane_helper_check_update(plane, state->crtc, new_fb, + &src, &dest, &clip, + DRM_ROTATE_0, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true, &visible); + + + if (!ret && new_fb) { + struct drm_crtc *crtc = state->crtc; + struct vmw_connector_state *vcs; + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + struct vmw_private *dev_priv = vmw_priv(crtc->dev); + struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb); + + vcs = vmw_connector_state_to_vcs(du->connector.state); + + if ((dest.x2 > new_fb->width || + dest.y2 > new_fb->height)) { + DRM_ERROR("CRTC area outside of framebuffer\n"); + return -EINVAL; + } + + /* Only one active implicit framebuffer at a time. */ + mutex_lock(&dev_priv->global_kms_state_mutex); + if (vcs->is_implicit && dev_priv->implicit_fb && + !(dev_priv->num_implicit == 1 && du->active_implicit) + && dev_priv->implicit_fb != vfb) { + DRM_ERROR("Multiple implicit framebuffers " + "not supported.\n"); + ret = -EINVAL; + } + mutex_unlock(&dev_priv->global_kms_state_mutex); + } + + + return ret; +} + + +/** + * vmw_du_cursor_plane_atomic_check - check if the new state is okay + * + * @plane: cursor plane + * @state: info on the new plane state + * + * This is a chance to fail if the new cursor state does not fit + * our requirements. + * + * Returns 0 on success + */ +int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + int ret = 0; + struct vmw_surface *surface = NULL; + struct drm_framebuffer *fb = new_state->fb; + + + /* Turning off */ + if (!fb) + return ret; + + /* A lot of the code assumes this */ + if (new_state->crtc_w != 64 || new_state->crtc_h != 64) { + DRM_ERROR("Invalid cursor dimensions (%d, %d)\n", + new_state->crtc_w, new_state->crtc_h); + ret = -EINVAL; + } + + if (!vmw_framebuffer_to_vfb(fb)->dmabuf) + surface = vmw_framebuffer_to_vfbs(fb)->surface; + + if (surface && !surface->snooper.image) { + DRM_ERROR("surface not suitable for cursor\n"); + ret = -EINVAL; + } + + return ret; +} + + +int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *new_state) +{ + struct vmw_display_unit *du = vmw_crtc_to_du(new_state->crtc); + int connector_mask = 1 << drm_connector_index(&du->connector); + bool has_primary = new_state->plane_mask & + BIT(drm_plane_index(crtc->primary)); + + /* We always want to have an active plane with an active CRTC */ + if (has_primary != new_state->enable) + return -EINVAL; + + + if (new_state->connector_mask != connector_mask && + new_state->connector_mask != 0) { + DRM_ERROR("Invalid connectors configuration\n"); + return -EINVAL; + } + + /* + * Our virtual device does not have a dot clock, so use the logical + * clock value as the dot clock. + */ + if (new_state->mode.crtc_clock == 0) + new_state->adjusted_mode.crtc_clock = new_state->mode.clock; + + return 0; +} + + +void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ +} + + +void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct drm_pending_vblank_event *event = crtc->state->event; + + if (event) { + crtc->state->event = NULL; + + spin_lock_irq(&crtc->dev->event_lock); + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + } + +} + + +/** + * vmw_du_crtc_duplicate_state - duplicate crtc state + * @crtc: DRM crtc + * + * Allocates and returns a copy of the crtc state (both common and + * vmw-specific) for the specified crtc. + * + * Returns: The newly allocated crtc state, or NULL on failure. + */ +struct drm_crtc_state * +vmw_du_crtc_duplicate_state(struct drm_crtc *crtc) +{ + struct drm_crtc_state *state; + struct vmw_crtc_state *vcs; + + if (WARN_ON(!crtc->state)) + return NULL; + + vcs = kmemdup(crtc->state, sizeof(*vcs), GFP_KERNEL); + + if (!vcs) + return NULL; + + state = &vcs->base; + + __drm_atomic_helper_crtc_duplicate_state(crtc, state); + + return state; +} + + +/** + * vmw_du_crtc_reset - creates a blank vmw crtc state + * @crtc: DRM crtc + * + * Resets the atomic state for @crtc by freeing the state pointer (which + * might be NULL, e.g. at driver load time) and allocating a new empty state + * object. + */ +void vmw_du_crtc_reset(struct drm_crtc *crtc) +{ + struct vmw_crtc_state *vcs; + + + if (crtc->state) { + __drm_atomic_helper_crtc_destroy_state(crtc->state); + + kfree(vmw_crtc_state_to_vcs(crtc->state)); + } + + vcs = kzalloc(sizeof(*vcs), GFP_KERNEL); + + if (!vcs) { + DRM_ERROR("Cannot allocate vmw_crtc_state\n"); + return; + } + + crtc->state = &vcs->base; + crtc->state->crtc = crtc; +} + + +/** + * vmw_du_crtc_destroy_state - destroy crtc state + * @crtc: DRM crtc + * @state: state object to destroy + * + * Destroys the crtc state (both common and vmw-specific) for the + * specified plane. + */ +void +vmw_du_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + drm_atomic_helper_crtc_destroy_state(crtc, state); +} + + +/** + * vmw_du_plane_duplicate_state - duplicate plane state + * @plane: drm plane + * + * Allocates and returns a copy of the plane state (both common and + * vmw-specific) for the specified plane. + * + * Returns: The newly allocated plane state, or NULL on failure. + */ +struct drm_plane_state * +vmw_du_plane_duplicate_state(struct drm_plane *plane) +{ + struct drm_plane_state *state; + struct vmw_plane_state *vps; + + vps = kmemdup(plane->state, sizeof(*vps), GFP_KERNEL); + + if (!vps) + return NULL; + + vps->pinned = 0; + + /* Mapping is managed by prepare_fb/cleanup_fb */ + memset(&vps->guest_map, 0, sizeof(vps->guest_map)); + memset(&vps->host_map, 0, sizeof(vps->host_map)); + vps->cpp = 0; + + /* Each ref counted resource needs to be acquired again */ + if (vps->surf) + (void) vmw_surface_reference(vps->surf); + + if (vps->dmabuf) + (void) vmw_dmabuf_reference(vps->dmabuf); + + state = &vps->base; + + __drm_atomic_helper_plane_duplicate_state(plane, state); + + return state; +} + + +/** + * vmw_du_plane_reset - creates a blank vmw plane state + * @plane: drm plane + * + * Resets the atomic state for @plane by freeing the state pointer (which might + * be NULL, e.g. at driver load time) and allocating a new empty state object. + */ +void vmw_du_plane_reset(struct drm_plane *plane) +{ + struct vmw_plane_state *vps; + + + if (plane->state) + vmw_du_plane_destroy_state(plane, plane->state); + + vps = kzalloc(sizeof(*vps), GFP_KERNEL); + + if (!vps) { + DRM_ERROR("Cannot allocate vmw_plane_state\n"); + return; + } + + plane->state = &vps->base; + plane->state->plane = plane; + plane->state->rotation = DRM_ROTATE_0; +} + + +/** + * vmw_du_plane_destroy_state - destroy plane state + * @plane: DRM plane + * @state: state object to destroy + * + * Destroys the plane state (both common and vmw-specific) for the + * specified plane. + */ +void +vmw_du_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct vmw_plane_state *vps = vmw_plane_state_to_vps(state); + + + /* Should have been freed by cleanup_fb */ + if (vps->guest_map.virtual) { + DRM_ERROR("Guest mapping not freed\n"); + ttm_bo_kunmap(&vps->guest_map); + } + + if (vps->host_map.virtual) { + DRM_ERROR("Host mapping not freed\n"); + ttm_bo_kunmap(&vps->host_map); + } + + if (vps->surf) + vmw_surface_unreference(&vps->surf); + + if (vps->dmabuf) + vmw_dmabuf_unreference(&vps->dmabuf); + + drm_atomic_helper_plane_destroy_state(plane, state); +} + + +/** + * vmw_du_connector_duplicate_state - duplicate connector state + * @connector: DRM connector + * + * Allocates and returns a copy of the connector state (both common and + * vmw-specific) for the specified connector. + * + * Returns: The newly allocated connector state, or NULL on failure. + */ +struct drm_connector_state * +vmw_du_connector_duplicate_state(struct drm_connector *connector) +{ + struct drm_connector_state *state; + struct vmw_connector_state *vcs; + + if (WARN_ON(!connector->state)) + return NULL; + + vcs = kmemdup(connector->state, sizeof(*vcs), GFP_KERNEL); + + if (!vcs) + return NULL; + + state = &vcs->base; + + __drm_atomic_helper_connector_duplicate_state(connector, state); + + return state; +} + + +/** + * vmw_du_connector_reset - creates a blank vmw connector state + * @connector: DRM connector + * + * Resets the atomic state for @connector by freeing the state pointer (which + * might be NULL, e.g. at driver load time) and allocating a new empty state + * object. + */ +void vmw_du_connector_reset(struct drm_connector *connector) +{ + struct vmw_connector_state *vcs; + + + if (connector->state) { + __drm_atomic_helper_connector_destroy_state(connector->state); + + kfree(vmw_connector_state_to_vcs(connector->state)); + } + + vcs = kzalloc(sizeof(*vcs), GFP_KERNEL); + + if (!vcs) { + DRM_ERROR("Cannot allocate vmw_connector_state\n"); + return; + } + + __drm_atomic_helper_connector_reset(connector, &vcs->base); +} + + +/** + * vmw_du_connector_destroy_state - destroy connector state + * @connector: DRM connector + * @state: state object to destroy + * + * Destroys the connector state (both common and vmw-specific) for the + * specified plane. + */ +void +vmw_du_connector_destroy_state(struct drm_connector *connector, + struct drm_connector_state *state) +{ + drm_atomic_helper_connector_destroy_state(connector, state); +} /* * Generic framebuffer code */ @@ -884,6 +1449,25 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, return ret; } + +/** + * vmw_kms_srf_ok - check if a surface can be created + * + * @width: requested width + * @height: requested height + * + * Surfaces need to be less than texture size + */ +static bool +vmw_kms_srf_ok(struct vmw_private *dev_priv, uint32_t width, uint32_t height) +{ + if (width > dev_priv->texture_max_width || + height > dev_priv->texture_max_height) + return false; + + return true; +} + /** * vmw_kms_new_framebuffer - Create a new framebuffer. * @@ -912,7 +1496,8 @@ vmw_kms_new_framebuffer(struct vmw_private *dev_priv, * therefore, wrap the DMA buf in a surface so we can use the * SurfaceCopy command. */ - if (dmabuf && only_2d && + if (vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height) && + dmabuf && only_2d && dev_priv->active_display_unit == vmw_du_screen_target) { ret = vmw_create_dmabuf_proxy(dev_priv->dev, mode_cmd, dmabuf, &surface); @@ -1005,6 +1590,16 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, if (ret) goto err_out; + + if (!bo && + !vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height)) { + DRM_ERROR("Surface size cannot exceed %dx%d", + dev_priv->texture_max_width, + dev_priv->texture_max_height); + goto err_out; + } + + vfb = vmw_kms_new_framebuffer(dev_priv, bo, surface, !(dev_priv->capabilities & SVGA_CAP_3D), mode_cmd); @@ -1030,8 +1625,56 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, return &vfb->base; } + + +/** + * 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 + */ +int +vmw_kms_atomic_check_modeset(struct drm_device *dev, + struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + struct vmw_private *dev_priv = vmw_priv(dev); + int i; + + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + unsigned long requested_bb_mem = 0; + + if (dev_priv->active_display_unit == vmw_du_screen_target) { + if (crtc->primary->fb) { + int cpp = crtc->primary->fb->pitches[0] / + crtc->primary->fb->width; + + requested_bb_mem += crtc->mode.hdisplay * cpp * + crtc->mode.vdisplay; + } + + if (requested_bb_mem > dev_priv->prim_bb_mem) + return -EINVAL; + } + } + + return drm_atomic_helper_check(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, + .atomic_commit = drm_atomic_helper_commit, }; static int vmw_kms_generic_present(struct vmw_private *dev_priv, @@ -1539,7 +2182,10 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector, if (dev_priv->active_display_unit == vmw_du_screen_target) { max_width = min(max_width, dev_priv->stdu_max_width); + max_width = min(max_width, dev_priv->texture_max_width); + max_height = min(max_height, dev_priv->stdu_max_height); + max_height = min(max_height, dev_priv->texture_max_height); } /* Add preferred mode */ @@ -1607,6 +2253,71 @@ int vmw_du_connector_set_property(struct drm_connector *connector, } + +/** + * vmw_du_connector_atomic_set_property - Atomic version of get property + * + * @crtc - crtc the property is associated with + * + * Returns: + * Zero on success, negative errno on failure. + */ +int +vmw_du_connector_atomic_set_property(struct drm_connector *connector, + struct drm_connector_state *state, + struct drm_property *property, + uint64_t val) +{ + struct vmw_private *dev_priv = vmw_priv(connector->dev); + struct vmw_connector_state *vcs = vmw_connector_state_to_vcs(state); + struct vmw_display_unit *du = vmw_connector_to_du(connector); + + + if (property == dev_priv->implicit_placement_property) { + vcs->is_implicit = val; + + /* + * We should really be doing a drm_atomic_commit() to + * commit the new state, but since this doesn't cause + * an immedate state change, this is probably ok + */ + du->is_implicit = vcs->is_implicit; + } else { + return -EINVAL; + } + + return 0; +} + + +/** + * vmw_du_connector_atomic_get_property - Atomic version of get property + * + * @connector - connector the property is associated with + * + * Returns: + * Zero on success, negative errno on failure. + */ +int +vmw_du_connector_atomic_get_property(struct drm_connector *connector, + const struct drm_connector_state *state, + struct drm_property *property, + uint64_t *val) +{ + struct vmw_private *dev_priv = vmw_priv(connector->dev); + struct vmw_connector_state *vcs = vmw_connector_state_to_vcs(state); + + if (property == dev_priv->implicit_placement_property) + *val = vcs->is_implicit; + else { + DRM_ERROR("Invalid Property %s\n", property->name); + return -EINVAL; + } + + return 0; +} + + int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -2223,3 +2934,23 @@ vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv, "implicit_placement", 0, 1); } + + +/** + * vmw_kms_set_config - Wrapper around drm_atomic_helper_set_config + * + * @set: The configuration to set. + * + * The vmwgfx Xorg driver doesn't assign the mode::type member, which + * when drm_mode_set_crtcinfo is called as part of the configuration setting + * causes it to return incorrect crtc dimensions causing severe problems in + * the vmwgfx modesetting. So explicitly clear that member before calling + * into drm_atomic_helper_set_config. + */ +int vmw_kms_set_config(struct drm_mode_set *set) +{ + if (set && set->mode) + set->mode->type = 0; + + return drm_atomic_helper_set_config(set); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index cb36e1d70133..9c161d29aaeb 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -33,6 +33,8 @@ #include #include "vmwgfx_drv.h" + + /** * struct vmw_kms_dirty - closure structure for the vmw_kms_helper_dirty * function. @@ -125,19 +127,71 @@ struct vmw_framebuffer_dmabuf { }; -/* - * Basic cursor manipulation - */ -int vmw_cursor_update_image(struct vmw_private *dev_priv, - u32 *image, u32 width, u32 height, - u32 hotspotX, u32 hotspotY); -int vmw_cursor_update_dmabuf(struct vmw_private *dev_priv, - struct vmw_dma_buffer *dmabuf, - u32 width, u32 height, - u32 hotspotX, u32 hotspotY); -void vmw_cursor_update_position(struct vmw_private *dev_priv, - bool show, int x, int y); +static const uint32_t vmw_primary_plane_formats[] = { + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGB888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, +}; +static const uint32_t vmw_cursor_plane_formats[] = { + DRM_FORMAT_ARGB8888, +}; + + +#define vmw_crtc_state_to_vcs(x) container_of(x, struct vmw_crtc_state, base) +#define vmw_plane_state_to_vps(x) container_of(x, struct vmw_plane_state, base) +#define vmw_connector_state_to_vcs(x) \ + container_of(x, struct vmw_connector_state, base) + +/** + * Derived class for crtc state object + * + * @base DRM crtc object + */ +struct vmw_crtc_state { + struct drm_crtc_state base; +}; + +/** + * Derived class for plane state object + * + * @base DRM plane object + * @surf Display surface for STDU + * @dmabuf display dmabuf for SOU + * @content_fb_type Used by STDU. + * @dmabuf_size Size of the dmabuf, used by Screen Object Display Unit + * @pinned pin count for STDU display surface + */ +struct vmw_plane_state { + struct drm_plane_state base; + struct vmw_surface *surf; + struct vmw_dma_buffer *dmabuf; + + int content_fb_type; + unsigned long dmabuf_size; + + int pinned; + + /* For CPU Blit */ + struct ttm_bo_kmap_obj host_map, guest_map; + unsigned int cpp; +}; + + +/** + * Derived class for connector state object + * + * @base DRM connector object + * @is_implicit connector property + * + */ +struct vmw_connector_state { + struct drm_connector_state base; + + bool is_implicit; +}; /** * Base class display unit. @@ -150,6 +204,8 @@ struct vmw_display_unit { struct drm_crtc crtc; struct drm_encoder encoder; struct drm_connector connector; + struct drm_plane primary; + struct drm_plane cursor; struct vmw_surface *cursor_surface; struct vmw_dma_buffer *cursor_dmabuf; @@ -203,6 +259,18 @@ int vmw_du_crtc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv, uint32_t handle, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y); int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y); +int vmw_du_connector_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t val); +int vmw_du_connector_atomic_set_property(struct drm_connector *connector, + struct drm_connector_state *state, + struct drm_property *property, + uint64_t val); +int +vmw_du_connector_atomic_get_property(struct drm_connector *connector, + const struct drm_connector_state *state, + struct drm_property *property, + uint64_t *val); int vmw_du_connector_dpms(struct drm_connector *connector, int mode); void vmw_du_connector_save(struct drm_connector *connector); void vmw_du_connector_restore(struct drm_connector *connector); @@ -210,9 +278,6 @@ enum drm_connector_status vmw_du_connector_detect(struct drm_connector *connector, bool force); int vmw_du_connector_fill_modes(struct drm_connector *connector, uint32_t max_width, uint32_t max_height); -int vmw_du_connector_set_property(struct drm_connector *connector, - struct drm_property *property, - uint64_t val); int vmw_kms_helper_dirty(struct vmw_private *dev_priv, struct vmw_framebuffer *framebuffer, const struct drm_clip_rect *clips, @@ -270,6 +335,55 @@ void vmw_kms_update_implicit_fb(struct vmw_private *dev_priv, void vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv, bool immutable); +/* Universal Plane Helpers */ +void vmw_du_primary_plane_destroy(struct drm_plane *plane); +void vmw_du_cursor_plane_destroy(struct drm_plane *plane); +int vmw_du_cursor_plane_disable(struct drm_plane *plane); +int vmw_du_cursor_plane_update(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, + unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h); + +/* Atomic Helpers */ +int vmw_du_primary_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state); +int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state); +void vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state); +void vmw_du_cursor_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state); +int vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state); +void vmw_du_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state); +void vmw_du_plane_reset(struct drm_plane *plane); +struct drm_plane_state *vmw_du_plane_duplicate_state(struct drm_plane *plane); +void vmw_du_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state); +void vmw_du_plane_unpin_surf(struct vmw_plane_state *vps, + bool unreference); + +int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state); +void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state); +void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state); +void vmw_du_crtc_reset(struct drm_crtc *crtc); +struct drm_crtc_state *vmw_du_crtc_duplicate_state(struct drm_crtc *crtc); +void vmw_du_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); +void vmw_du_connector_reset(struct drm_connector *connector); +struct drm_connector_state * +vmw_du_connector_duplicate_state(struct drm_connector *connector); + +void vmw_du_connector_destroy_state(struct drm_connector *connector, + struct drm_connector_state *state); /* * Legacy display unit functions - vmwgfx_ldu.c @@ -339,5 +453,6 @@ int vmw_kms_stdu_dma(struct vmw_private *dev_priv, bool to_surface, bool interruptible); +int vmw_kms_set_config(struct drm_mode_set *set); #endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 3806148e1bdb..d3987bcf53f8 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -27,6 +27,8 @@ #include "vmwgfx_kms.h" #include +#include +#include #define vmw_crtc_to_ldu(x) \ @@ -75,10 +77,9 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) { struct vmw_legacy_display *lds = dev_priv->ldu_priv; struct vmw_legacy_display_unit *entry; - struct vmw_display_unit *du = NULL; struct drm_framebuffer *fb = NULL; struct drm_crtc *crtc = NULL; - int i = 0, ret; + int i = 0; /* If there is no display topology the host just assumes * that the guest will set the same layout as the host. @@ -94,7 +95,7 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) if (crtc == NULL) return 0; - fb = entry->base.crtc.primary->fb; + fb = entry->base.crtc.primary->state->fb; return vmw_kms_write_svga(dev_priv, w, h, fb->pitches[0], fb->format->cpp[0] * 8, @@ -103,7 +104,7 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) if (!list_empty(&lds->active)) { entry = list_entry(lds->active.next, typeof(*entry), active); - fb = entry->base.crtc.primary->fb; + fb = entry->base.crtc.primary->state->fb; vmw_kms_write_svga(dev_priv, fb->width, fb->height, fb->pitches[0], fb->format->cpp[0] * 8, fb->format->depth); @@ -132,25 +133,6 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) lds->last_num_active = lds->num_active; - - /* Find the first du with a cursor. */ - list_for_each_entry(entry, &lds->active, active) { - du = &entry->base; - - if (!du->cursor_dmabuf) - continue; - - ret = vmw_cursor_update_dmabuf(dev_priv, - du->cursor_dmabuf, - 64, 64, - du->hotspot_x, - du->hotspot_y); - if (ret == 0) - break; - - DRM_ERROR("Could not update cursor image\n"); - } - return 0; } @@ -185,6 +167,7 @@ static int vmw_ldu_add_active(struct vmw_private *vmw_priv, if (vfb != ld->fb) { if (ld->fb && ld->fb->unpin) ld->fb->unpin(ld->fb); + vmw_svga_enable(vmw_priv); if (vfb->pin) vfb->pin(vfb); ld->fb = vfb; @@ -208,101 +191,59 @@ static int vmw_ldu_add_active(struct vmw_private *vmw_priv, return 0; } -static int vmw_ldu_crtc_set_config(struct drm_mode_set *set) +/** + * vmw_ldu_crtc_mode_set_nofb - Enable svga + * + * @crtc: CRTC associated with the new screen + * + * For LDU, just enable the svga + */ +static void vmw_ldu_crtc_mode_set_nofb(struct drm_crtc *crtc) { - struct vmw_private *dev_priv; - struct vmw_legacy_display_unit *ldu; - struct drm_connector *connector; - struct drm_display_mode *mode; - struct drm_encoder *encoder; - struct vmw_framebuffer *vfb; - struct drm_framebuffer *fb; - struct drm_crtc *crtc; +} - if (!set) - return -EINVAL; +/** + * vmw_ldu_crtc_helper_prepare - Noop + * + * @crtc: CRTC associated with the new screen + * + * Prepares the CRTC for a mode set, but we don't need to do anything here. + * + */ +static void vmw_ldu_crtc_helper_prepare(struct drm_crtc *crtc) +{ +} - if (!set->crtc) - return -EINVAL; +/** + * vmw_ldu_crtc_helper_commit - Noop + * + * @crtc: CRTC associated with the new screen + * + * This is called after a mode set has been completed. Here's + * usually a good place to call vmw_ldu_add_active/vmw_ldu_del_active + * but since for LDU the display plane is closely tied to the + * CRTC, it makes more sense to do those at plane update time. + */ +static void vmw_ldu_crtc_helper_commit(struct drm_crtc *crtc) +{ +} - /* get the ldu */ - crtc = set->crtc; - ldu = vmw_crtc_to_ldu(crtc); - vfb = set->fb ? vmw_framebuffer_to_vfb(set->fb) : NULL; - dev_priv = vmw_priv(crtc->dev); - - if (set->num_connectors > 1) { - DRM_ERROR("to many connectors\n"); - return -EINVAL; - } - - if (set->num_connectors == 1 && - set->connectors[0] != &ldu->base.connector) { - DRM_ERROR("connector doesn't match %p %p\n", - set->connectors[0], &ldu->base.connector); - return -EINVAL; - } - - /* ldu only supports one fb active at the time */ - if (dev_priv->ldu_priv->fb && vfb && - !(dev_priv->ldu_priv->num_active == 1 && - !list_empty(&ldu->active)) && - dev_priv->ldu_priv->fb != vfb) { - DRM_ERROR("Multiple framebuffers not supported\n"); - return -EINVAL; - } - - /* since they always map one to one these are safe */ - connector = &ldu->base.connector; - encoder = &ldu->base.encoder; - - /* should we turn the crtc off? */ - if (set->num_connectors == 0 || !set->mode || !set->fb) { - - connector->encoder = NULL; - encoder->crtc = NULL; - crtc->primary->fb = NULL; - crtc->enabled = false; - - vmw_ldu_del_active(dev_priv, ldu); - - return vmw_ldu_commit_list(dev_priv); - } - - - /* we now know we want to set a mode */ - mode = set->mode; - fb = set->fb; - - if (set->x + mode->hdisplay > fb->width || - set->y + mode->vdisplay > fb->height) { - DRM_ERROR("set outside of framebuffer\n"); - return -EINVAL; - } - - vmw_svga_enable(dev_priv); - - crtc->primary->fb = fb; - encoder->crtc = crtc; - connector->encoder = encoder; - crtc->x = set->x; - crtc->y = set->y; - crtc->mode = *mode; - crtc->enabled = true; - ldu->base.set_gui_x = set->x; - ldu->base.set_gui_y = set->y; - - vmw_ldu_add_active(dev_priv, ldu, vfb); - - return vmw_ldu_commit_list(dev_priv); +/** + * vmw_ldu_crtc_helper_disable - Turns off CRTC + * + * @crtc: CRTC to be turned off + */ +static void vmw_ldu_crtc_helper_disable(struct drm_crtc *crtc) +{ } static const struct drm_crtc_funcs vmw_legacy_crtc_funcs = { - .cursor_set2 = vmw_du_crtc_cursor_set2, - .cursor_move = vmw_du_crtc_cursor_move, .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_ldu_crtc_destroy, - .set_config = vmw_ldu_crtc_set_config, + .reset = vmw_du_crtc_reset, + .atomic_duplicate_state = vmw_du_crtc_duplicate_state, + .atomic_destroy_state = vmw_du_crtc_destroy_state, + .set_config = vmw_kms_set_config, }; @@ -334,15 +275,138 @@ static const struct drm_connector_funcs vmw_legacy_connector_funcs = { .fill_modes = vmw_du_connector_fill_modes, .set_property = vmw_du_connector_set_property, .destroy = vmw_ldu_connector_destroy, + .reset = vmw_du_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .atomic_set_property = vmw_du_connector_atomic_set_property, + .atomic_get_property = vmw_du_connector_atomic_get_property, }; +static const struct +drm_connector_helper_funcs vmw_ldu_connector_helper_funcs = { + .best_encoder = drm_atomic_helper_best_encoder, +}; + +/* + * Legacy Display Plane Functions + */ + +/** + * vmw_ldu_primary_plane_cleanup_fb - Noop + * + * @plane: display plane + * @old_state: Contains the FB to clean up + * + * Unpins the display surface + * + * Returns 0 on success + */ +static void +vmw_ldu_primary_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ +} + + +/** + * vmw_ldu_primary_plane_prepare_fb - Noop + * + * @plane: display plane + * @new_state: info on the new plane state, including the FB + * + * Returns 0 on success + */ +static int +vmw_ldu_primary_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + return 0; +} + + +static void +vmw_ldu_primary_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct vmw_private *dev_priv; + struct vmw_legacy_display_unit *ldu; + struct vmw_framebuffer *vfb; + struct drm_framebuffer *fb; + struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc; + + + ldu = vmw_crtc_to_ldu(crtc); + dev_priv = vmw_priv(plane->dev); + fb = plane->state->fb; + + vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; + + if (vfb) + vmw_ldu_add_active(dev_priv, ldu, vfb); + else + vmw_ldu_del_active(dev_priv, ldu); + + vmw_ldu_commit_list(dev_priv); +} + + +static const struct drm_plane_funcs vmw_ldu_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = vmw_du_primary_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, +}; + +static const struct drm_plane_funcs vmw_ldu_cursor_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = vmw_du_cursor_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, +}; + +/* + * Atomic Helpers + */ +static const struct +drm_plane_helper_funcs vmw_ldu_cursor_plane_helper_funcs = { + .atomic_check = vmw_du_cursor_plane_atomic_check, + .atomic_update = vmw_du_cursor_plane_atomic_update, + .prepare_fb = vmw_du_cursor_plane_prepare_fb, + .cleanup_fb = vmw_du_plane_cleanup_fb, +}; + +static const struct +drm_plane_helper_funcs vmw_ldu_primary_plane_helper_funcs = { + .atomic_check = vmw_du_primary_plane_atomic_check, + .atomic_update = vmw_ldu_primary_plane_atomic_update, + .prepare_fb = vmw_ldu_primary_plane_prepare_fb, + .cleanup_fb = vmw_ldu_primary_plane_cleanup_fb, +}; + +static const struct drm_crtc_helper_funcs vmw_ldu_crtc_helper_funcs = { + .prepare = vmw_ldu_crtc_helper_prepare, + .commit = vmw_ldu_crtc_helper_commit, + .disable = vmw_ldu_crtc_helper_disable, + .mode_set_nofb = vmw_ldu_crtc_mode_set_nofb, + .atomic_check = vmw_du_crtc_atomic_check, + .atomic_begin = vmw_du_crtc_atomic_begin, + .atomic_flush = vmw_du_crtc_atomic_flush, +}; + + static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) { struct vmw_legacy_display_unit *ldu; struct drm_device *dev = dev_priv->dev; struct drm_connector *connector; struct drm_encoder *encoder; + struct drm_plane *primary, *cursor; struct drm_crtc *crtc; + int ret; ldu = kzalloc(sizeof(*ldu), GFP_KERNEL); if (!ldu) @@ -352,6 +416,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) crtc = &ldu->base.crtc; encoder = &ldu->base.encoder; connector = &ldu->base.connector; + primary = &ldu->base.primary; + cursor = &ldu->base.cursor; INIT_LIST_HEAD(&ldu->active); @@ -359,21 +425,86 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) ldu->base.pref_width = dev_priv->initial_width; ldu->base.pref_height = dev_priv->initial_height; ldu->base.pref_mode = NULL; + + /* + * Remove this after enabling atomic because property values can + * only exist in a state object + */ ldu->base.is_implicit = true; - drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, - DRM_MODE_CONNECTOR_VIRTUAL); - connector->status = vmw_du_connector_detect(connector, true); + /* Initialize primary plane */ + vmw_du_plane_reset(primary); - drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs, - DRM_MODE_ENCODER_VIRTUAL, NULL); - drm_mode_connector_attach_encoder(connector, encoder); + ret = drm_universal_plane_init(dev, &ldu->base.primary, + 0, &vmw_ldu_plane_funcs, + vmw_primary_plane_formats, + ARRAY_SIZE(vmw_primary_plane_formats), + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + DRM_ERROR("Failed to initialize primary plane"); + goto err_free; + } + + drm_plane_helper_add(primary, &vmw_ldu_primary_plane_helper_funcs); + + /* Initialize cursor plane */ + vmw_du_plane_reset(cursor); + + ret = drm_universal_plane_init(dev, &ldu->base.cursor, + 0, &vmw_ldu_cursor_funcs, + vmw_cursor_plane_formats, + ARRAY_SIZE(vmw_cursor_plane_formats), + DRM_PLANE_TYPE_CURSOR, NULL); + if (ret) { + DRM_ERROR("Failed to initialize cursor plane"); + drm_plane_cleanup(&ldu->base.primary); + goto err_free; + } + + drm_plane_helper_add(cursor, &vmw_ldu_cursor_plane_helper_funcs); + + + vmw_du_connector_reset(connector); + ret = drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + if (ret) { + DRM_ERROR("Failed to initialize connector\n"); + goto err_free; + } + + drm_connector_helper_add(connector, &vmw_ldu_connector_helper_funcs); + connector->status = vmw_du_connector_detect(connector, true); + vmw_connector_state_to_vcs(connector->state)->is_implicit = true; + + + ret = drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs, + DRM_MODE_ENCODER_VIRTUAL, NULL); + if (ret) { + DRM_ERROR("Failed to initialize encoder\n"); + goto err_free_connector; + } + + (void) drm_mode_connector_attach_encoder(connector, encoder); encoder->possible_crtcs = (1 << unit); encoder->possible_clones = 0; - (void) drm_connector_register(connector); + ret = drm_connector_register(connector); + if (ret) { + DRM_ERROR("Failed to register connector\n"); + goto err_free_encoder; + } - drm_crtc_init(dev, crtc, &vmw_legacy_crtc_funcs); + + vmw_du_crtc_reset(crtc); + ret = drm_crtc_init_with_planes(dev, crtc, &ldu->base.primary, + &ldu->base.cursor, + &vmw_legacy_crtc_funcs, NULL); + if (ret) { + DRM_ERROR("Failed to initialize CRTC\n"); + goto err_free_unregister; + } + + drm_crtc_helper_add(crtc, &vmw_ldu_crtc_helper_funcs); drm_mode_crtc_set_gamma_size(crtc, 256); @@ -390,6 +521,16 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) 1); return 0; + +err_free_unregister: + drm_connector_unregister(connector); +err_free_encoder: + drm_encoder_cleanup(encoder); +err_free_connector: + drm_connector_cleanup(connector); +err_free: + kfree(ldu); + return ret; } int vmw_kms_ldu_init_display(struct vmw_private *dev_priv) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 27033d944b08..fa1037ec8e5f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -45,31 +45,6 @@ struct vmw_bo_user_rep { uint64_t map_handle; }; -struct vmw_stream { - struct vmw_resource res; - uint32_t stream_id; -}; - -struct vmw_user_stream { - struct ttm_base_object base; - struct vmw_stream stream; -}; - - -static uint64_t vmw_user_stream_size; - -static const struct vmw_res_func vmw_stream_func = { - .res_type = vmw_res_stream, - .needs_backup = false, - .may_evict = false, - .type_name = "video streams", - .backup_placement = NULL, - .create = NULL, - .destroy = NULL, - .bind = NULL, - .unbind = NULL -}; - static inline struct vmw_dma_buffer * vmw_dma_buffer(struct ttm_buffer_object *bo) { @@ -259,24 +234,6 @@ void vmw_resource_activate(struct vmw_resource *res, write_unlock(&dev_priv->resource_lock); } -static struct vmw_resource *vmw_resource_lookup(struct vmw_private *dev_priv, - struct idr *idr, int id) -{ - struct vmw_resource *res; - - read_lock(&dev_priv->resource_lock); - res = idr_find(idr, id); - if (!res || !res->avail || !kref_get_unless_zero(&res->kref)) - res = NULL; - - read_unlock(&dev_priv->resource_lock); - - if (unlikely(res == NULL)) - return NULL; - - return res; -} - /** * vmw_user_resource_lookup_handle - lookup a struct resource from a * TTM user-space handle and perform basic type checks @@ -776,217 +733,6 @@ int vmw_user_dmabuf_reference(struct ttm_object_file *tfile, TTM_REF_USAGE, NULL); } -/* - * Stream management - */ - -static void vmw_stream_destroy(struct vmw_resource *res) -{ - struct vmw_private *dev_priv = res->dev_priv; - struct vmw_stream *stream; - int ret; - - DRM_INFO("%s: unref\n", __func__); - stream = container_of(res, struct vmw_stream, res); - - ret = vmw_overlay_unref(dev_priv, stream->stream_id); - WARN_ON(ret != 0); -} - -static int vmw_stream_init(struct vmw_private *dev_priv, - struct vmw_stream *stream, - void (*res_free) (struct vmw_resource *res)) -{ - struct vmw_resource *res = &stream->res; - int ret; - - ret = vmw_resource_init(dev_priv, res, false, res_free, - &vmw_stream_func); - - if (unlikely(ret != 0)) { - if (res_free == NULL) - kfree(stream); - else - res_free(&stream->res); - return ret; - } - - ret = vmw_overlay_claim(dev_priv, &stream->stream_id); - if (ret) { - vmw_resource_unreference(&res); - return ret; - } - - DRM_INFO("%s: claimed\n", __func__); - - vmw_resource_activate(&stream->res, vmw_stream_destroy); - return 0; -} - -static void vmw_user_stream_free(struct vmw_resource *res) -{ - struct vmw_user_stream *stream = - container_of(res, struct vmw_user_stream, stream.res); - struct vmw_private *dev_priv = res->dev_priv; - - ttm_base_object_kfree(stream, base); - ttm_mem_global_free(vmw_mem_glob(dev_priv), - vmw_user_stream_size); -} - -/** - * This function is called when user space has no more references on the - * base object. It releases the base-object's reference on the resource object. - */ - -static void vmw_user_stream_base_release(struct ttm_base_object **p_base) -{ - struct ttm_base_object *base = *p_base; - struct vmw_user_stream *stream = - container_of(base, struct vmw_user_stream, base); - struct vmw_resource *res = &stream->stream.res; - - *p_base = NULL; - vmw_resource_unreference(&res); -} - -int vmw_stream_unref_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_resource *res; - struct vmw_user_stream *stream; - struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - struct idr *idr = &dev_priv->res_idr[vmw_res_stream]; - int ret = 0; - - - res = vmw_resource_lookup(dev_priv, idr, arg->stream_id); - if (unlikely(res == NULL)) - return -EINVAL; - - if (res->res_free != &vmw_user_stream_free) { - ret = -EINVAL; - goto out; - } - - stream = container_of(res, struct vmw_user_stream, stream.res); - if (stream->base.tfile != tfile) { - ret = -EINVAL; - goto out; - } - - ttm_ref_object_base_unref(tfile, stream->base.hash.key, TTM_REF_USAGE); -out: - vmw_resource_unreference(&res); - return ret; -} - -int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_user_stream *stream; - struct vmw_resource *res; - struct vmw_resource *tmp; - struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - int ret; - - /* - * Approximate idr memory usage with 128 bytes. It will be limited - * by maximum number_of streams anyway? - */ - - if (unlikely(vmw_user_stream_size == 0)) - vmw_user_stream_size = ttm_round_pot(sizeof(*stream)) + 128; - - ret = ttm_read_lock(&dev_priv->reservation_sem, true); - if (unlikely(ret != 0)) - return ret; - - ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), - vmw_user_stream_size, - false, true); - ttm_read_unlock(&dev_priv->reservation_sem); - if (unlikely(ret != 0)) { - if (ret != -ERESTARTSYS) - DRM_ERROR("Out of graphics memory for stream" - " creation.\n"); - - goto out_ret; - } - - stream = kmalloc(sizeof(*stream), GFP_KERNEL); - if (unlikely(stream == NULL)) { - ttm_mem_global_free(vmw_mem_glob(dev_priv), - vmw_user_stream_size); - ret = -ENOMEM; - goto out_ret; - } - - res = &stream->stream.res; - stream->base.shareable = false; - stream->base.tfile = NULL; - - /* - * From here on, the destructor takes over resource freeing. - */ - - ret = vmw_stream_init(dev_priv, &stream->stream, vmw_user_stream_free); - if (unlikely(ret != 0)) - goto out_ret; - - tmp = vmw_resource_reference(res); - ret = ttm_base_object_init(tfile, &stream->base, false, VMW_RES_STREAM, - &vmw_user_stream_base_release, NULL); - - if (unlikely(ret != 0)) { - vmw_resource_unreference(&tmp); - goto out_err; - } - - arg->stream_id = res->id; -out_err: - vmw_resource_unreference(&res); -out_ret: - return ret; -} - -int vmw_user_stream_lookup(struct vmw_private *dev_priv, - struct ttm_object_file *tfile, - uint32_t *inout_id, struct vmw_resource **out) -{ - struct vmw_user_stream *stream; - struct vmw_resource *res; - int ret; - - res = vmw_resource_lookup(dev_priv, &dev_priv->res_idr[vmw_res_stream], - *inout_id); - if (unlikely(res == NULL)) - return -EINVAL; - - if (res->res_free != &vmw_user_stream_free) { - ret = -EINVAL; - goto err_ref; - } - - stream = container_of(res, struct vmw_user_stream, stream.res); - if (stream->base.tfile != tfile) { - ret = -EPERM; - goto err_ref; - } - - *inout_id = stream->stream.stream_id; - *out = res; - return 0; -err_ref: - vmw_resource_unreference(&res); - return ret; -} - - /** * vmw_dumb_create - Create a dumb kms buffer * diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h index 5994ef6265e0..ac05968a832b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h @@ -30,6 +30,8 @@ #include "vmwgfx_drv.h" +#define VMW_IDA_ACC_SIZE 128 + enum vmw_cmdbuf_res_state { VMW_CMDBUF_RES_COMMITTED, VMW_CMDBUF_RES_ADD, @@ -83,6 +85,35 @@ struct vmw_res_func { enum vmw_cmdbuf_res_state state); }; +/** + * struct vmw_simple_resource_func - members and functions common for the + * simple resource helpers. + * @res_func: struct vmw_res_func as described above. + * @ttm_res_type: TTM resource type used for handle recognition. + * @size: Size of the simple resource information struct. + * @init: Initialize the simple resource information. + * @hw_destroy: A resource hw_destroy function. + * @set_arg_handle: Set the handle output argument of the ioctl create struct. + */ +struct vmw_simple_resource_func { + const struct vmw_res_func res_func; + int ttm_res_type; + size_t size; + int (*init)(struct vmw_resource *res, void *data); + void (*hw_destroy)(struct vmw_resource *res); + void (*set_arg_handle)(void *data, u32 handle); +}; + +/** + * struct vmw_simple_resource - Kernel only side simple resource + * @res: The resource we derive from. + * @func: The method and member virtual table. + */ +struct vmw_simple_resource { + struct vmw_resource res; + const struct vmw_simple_resource_func *func; +}; + int vmw_resource_alloc_id(struct vmw_resource *res); void vmw_resource_release_id(struct vmw_resource *res); int vmw_resource_init(struct vmw_private *dev_priv, struct vmw_resource *res, @@ -91,4 +122,13 @@ int vmw_resource_init(struct vmw_private *dev_priv, struct vmw_resource *res, const struct vmw_res_func *func); void vmw_resource_activate(struct vmw_resource *res, void (*hw_destroy) (struct vmw_resource *)); +int +vmw_simple_resource_create_ioctl(struct drm_device *dev, + void *data, + struct drm_file *file_priv, + const struct vmw_simple_resource_func *func); +struct vmw_resource * +vmw_simple_resource_lookup(struct ttm_object_file *tfile, + uint32_t handle, + const struct vmw_simple_resource_func *func); #endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index d4268efc37d2..2664e4c16750 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -27,6 +27,8 @@ #include "vmwgfx_kms.h" #include +#include +#include #define vmw_crtc_to_sou(x) \ @@ -203,203 +205,116 @@ static int vmw_sou_fifo_destroy(struct vmw_private *dev_priv, } /** - * Free the backing store. + * vmw_sou_crtc_mode_set_nofb - Create new screen + * + * @crtc: CRTC associated with the new screen + * + * This function creates/destroys a screen. This function cannot fail, so if + * somehow we run into a failure, just do the best we can to get out. */ -static void vmw_sou_backing_free(struct vmw_private *dev_priv, - struct vmw_screen_object_unit *sou) -{ - vmw_dmabuf_unreference(&sou->buffer); - sou->buffer_size = 0; -} - -/** - * Allocate the backing store for the buffer. - */ -static int vmw_sou_backing_alloc(struct vmw_private *dev_priv, - struct vmw_screen_object_unit *sou, - unsigned long size) -{ - int ret; - - if (sou->buffer_size == size) - return 0; - - if (sou->buffer) - vmw_sou_backing_free(dev_priv, sou); - - sou->buffer = kzalloc(sizeof(*sou->buffer), GFP_KERNEL); - if (unlikely(sou->buffer == NULL)) - return -ENOMEM; - - /* After we have alloced the backing store might not be able to - * resume the overlays, this is preferred to failing to alloc. - */ - vmw_overlay_pause_all(dev_priv); - ret = vmw_dmabuf_init(dev_priv, sou->buffer, size, - &vmw_vram_ne_placement, - false, &vmw_dmabuf_bo_free); - vmw_overlay_resume_all(dev_priv); - - if (unlikely(ret != 0)) - sou->buffer = NULL; /* vmw_dmabuf_init frees on error */ - else - sou->buffer_size = size; - - return ret; -} - -static int vmw_sou_crtc_set_config(struct drm_mode_set *set) +static void vmw_sou_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct vmw_private *dev_priv; struct vmw_screen_object_unit *sou; - struct drm_connector *connector; - struct drm_display_mode *mode; - struct drm_encoder *encoder; struct vmw_framebuffer *vfb; struct drm_framebuffer *fb; - struct drm_crtc *crtc; - int ret = 0; + struct drm_plane_state *ps; + struct vmw_plane_state *vps; + int ret; - if (!set) - return -EINVAL; - if (!set->crtc) - return -EINVAL; - - /* get the sou */ - crtc = set->crtc; - sou = vmw_crtc_to_sou(crtc); - vfb = set->fb ? vmw_framebuffer_to_vfb(set->fb) : NULL; + 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); - if (set->num_connectors > 1) { - DRM_ERROR("Too many connectors\n"); - return -EINVAL; - } + vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; - if (set->num_connectors == 1 && - set->connectors[0] != &sou->base.connector) { - DRM_ERROR("Connector doesn't match %p %p\n", - set->connectors[0], &sou->base.connector); - return -EINVAL; - } - - /* Only one active implicit frame-buffer at a time. */ - mutex_lock(&dev_priv->global_kms_state_mutex); - if (sou->base.is_implicit && - dev_priv->implicit_fb && vfb && - !(dev_priv->num_implicit == 1 && - sou->base.active_implicit) && - dev_priv->implicit_fb != vfb) { - mutex_unlock(&dev_priv->global_kms_state_mutex); - DRM_ERROR("Multiple implicit framebuffers not supported.\n"); - return -EINVAL; - } - mutex_unlock(&dev_priv->global_kms_state_mutex); - - /* since they always map one to one these are safe */ - connector = &sou->base.connector; - encoder = &sou->base.encoder; - - /* should we turn the crtc off */ - if (set->num_connectors == 0 || !set->mode || !set->fb) { + if (sou->defined) { ret = vmw_sou_fifo_destroy(dev_priv, sou); - /* the hardware has hung don't do anything more */ - if (unlikely(ret != 0)) - return ret; + if (ret) { + DRM_ERROR("Failed to destroy Screen Object\n"); + return; + } + } - connector->encoder = NULL; - encoder->crtc = NULL; - crtc->primary->fb = NULL; - crtc->x = 0; - crtc->y = 0; - crtc->enabled = false; + if (vfb) { + sou->buffer = vps->dmabuf; + sou->buffer_size = vps->dmabuf_size; + + ret = vmw_sou_fifo_create(dev_priv, sou, crtc->x, crtc->y, + &crtc->mode); + if (ret) + DRM_ERROR("Failed to define Screen Object %dx%d\n", + crtc->x, crtc->y); + + vmw_kms_add_active(dev_priv, &sou->base, vfb); + } else { + sou->buffer = NULL; + sou->buffer_size = 0; vmw_kms_del_active(dev_priv, &sou->base); + } +} - vmw_sou_backing_free(dev_priv, sou); +/** + * vmw_sou_crtc_helper_prepare - Noop + * + * @crtc: CRTC associated with the new screen + * + * Prepares the CRTC for a mode set, but we don't need to do anything here. + */ +static void vmw_sou_crtc_helper_prepare(struct drm_crtc *crtc) +{ +} - return 0; +/** + * vmw_sou_crtc_helper_commit - Noop + * + * @crtc: CRTC associated with the new screen + * + * This is called after a mode set has been completed. + */ +static void vmw_sou_crtc_helper_commit(struct drm_crtc *crtc) +{ +} + +/** + * vmw_sou_crtc_helper_disable - Turns off CRTC + * + * @crtc: CRTC to be turned off + */ +static void vmw_sou_crtc_helper_disable(struct drm_crtc *crtc) +{ + struct vmw_private *dev_priv; + struct vmw_screen_object_unit *sou; + int ret; + + + if (!crtc) { + DRM_ERROR("CRTC is NULL\n"); + return; } + sou = vmw_crtc_to_sou(crtc); + dev_priv = vmw_priv(crtc->dev); - /* we now know we want to set a mode */ - mode = set->mode; - fb = set->fb; - - if (set->x + mode->hdisplay > fb->width || - set->y + mode->vdisplay > fb->height) { - DRM_ERROR("set outside of framebuffer\n"); - return -EINVAL; - } - - vmw_svga_enable(dev_priv); - - if (mode->hdisplay != crtc->mode.hdisplay || - mode->vdisplay != crtc->mode.vdisplay) { - /* no need to check if depth is different, because backing - * store depth is forced to 4 by the device. - */ - + if (sou->defined) { ret = vmw_sou_fifo_destroy(dev_priv, sou); - /* the hardware has hung don't do anything more */ - if (unlikely(ret != 0)) - return ret; - - vmw_sou_backing_free(dev_priv, sou); + if (ret) + DRM_ERROR("Failed to destroy Screen Object\n"); } - - if (!sou->buffer) { - /* forced to depth 4 by the device */ - size_t size = mode->hdisplay * mode->vdisplay * 4; - ret = vmw_sou_backing_alloc(dev_priv, sou, size); - if (unlikely(ret != 0)) - return ret; - } - - ret = vmw_sou_fifo_create(dev_priv, sou, set->x, set->y, mode); - if (unlikely(ret != 0)) { - /* - * We are in a bit of a situation here, the hardware has - * hung and we may or may not have a buffer hanging of - * the screen object, best thing to do is not do anything - * if we where defined, if not just turn the crtc of. - * Not what userspace wants but it needs to htfu. - */ - if (sou->defined) - return ret; - - connector->encoder = NULL; - encoder->crtc = NULL; - crtc->primary->fb = NULL; - crtc->x = 0; - crtc->y = 0; - crtc->enabled = false; - - return ret; - } - - vmw_kms_add_active(dev_priv, &sou->base, vfb); - - connector->encoder = encoder; - encoder->crtc = crtc; - crtc->mode = *mode; - crtc->primary->fb = fb; - crtc->x = set->x; - crtc->y = set->y; - crtc->enabled = true; - - return 0; } static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc, - struct drm_framebuffer *fb, + struct drm_framebuffer *new_fb, struct drm_pending_vblank_event *event, uint32_t flags) { struct vmw_private *dev_priv = vmw_priv(crtc->dev); struct drm_framebuffer *old_fb = crtc->primary->fb; - struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(fb); + struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb); struct vmw_fence_obj *fence = NULL; struct drm_vmw_rect vclips; int ret; @@ -407,7 +322,12 @@ static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc, if (!vmw_kms_crtc_flippable(dev_priv, crtc)) return -EINVAL; - crtc->primary->fb = fb; + flags &= ~DRM_MODE_PAGE_FLIP_ASYNC; + ret = drm_atomic_helper_page_flip(crtc, new_fb, NULL, flags); + if (ret) { + DRM_ERROR("Page flip error %d.\n", ret); + return ret; + } /* do a full screen dirty update */ vclips.x = crtc->x; @@ -454,16 +374,17 @@ static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc, return ret; out_no_fence: - crtc->primary->fb = old_fb; + drm_atomic_set_fb_for_plane(crtc->primary->state, old_fb); return ret; } static const struct drm_crtc_funcs vmw_screen_object_crtc_funcs = { - .cursor_set2 = vmw_du_crtc_cursor_set2, - .cursor_move = vmw_du_crtc_cursor_move, .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_sou_crtc_destroy, - .set_config = vmw_sou_crtc_set_config, + .reset = vmw_du_crtc_reset, + .atomic_duplicate_state = vmw_du_crtc_duplicate_state, + .atomic_destroy_state = vmw_du_crtc_destroy_state, + .set_config = vmw_kms_set_config, .page_flip = vmw_sou_crtc_page_flip, }; @@ -495,15 +416,180 @@ static const struct drm_connector_funcs vmw_sou_connector_funcs = { .fill_modes = vmw_du_connector_fill_modes, .set_property = vmw_du_connector_set_property, .destroy = vmw_sou_connector_destroy, + .reset = vmw_du_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .atomic_set_property = vmw_du_connector_atomic_set_property, + .atomic_get_property = vmw_du_connector_atomic_get_property, }; + +static const struct +drm_connector_helper_funcs vmw_sou_connector_helper_funcs = { + .best_encoder = drm_atomic_helper_best_encoder, +}; + + + +/* + * Screen Object Display Plane Functions + */ + +/** + * vmw_sou_primary_plane_cleanup_fb - Frees sou backing buffer + * + * @plane: display plane + * @old_state: Contains the FB to clean up + * + * Unpins the display surface + * + * Returns 0 on success + */ +static void +vmw_sou_primary_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); + + vmw_dmabuf_unreference(&vps->dmabuf); + vps->dmabuf_size = 0; + + vmw_du_plane_cleanup_fb(plane, old_state); +} + + +/** + * vmw_sou_primary_plane_prepare_fb - allocate backing buffer + * + * @plane: display plane + * @new_state: info on the new plane state, including the FB + * + * The SOU backing buffer is our equivalent of the display plane. + * + * Returns 0 on success + */ +static int +vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct drm_framebuffer *new_fb = new_state->fb; + struct drm_crtc *crtc = plane->state->crtc ?: new_state->crtc; + struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); + struct vmw_private *dev_priv; + size_t size; + int ret; + + + if (!new_fb) { + vmw_dmabuf_unreference(&vps->dmabuf); + vps->dmabuf_size = 0; + + return 0; + } + + size = new_state->crtc_w * new_state->crtc_h * 4; + + if (vps->dmabuf) { + if (vps->dmabuf_size == size) + return 0; + + vmw_dmabuf_unreference(&vps->dmabuf); + vps->dmabuf_size = 0; + } + + vps->dmabuf = kzalloc(sizeof(*vps->dmabuf), GFP_KERNEL); + if (!vps->dmabuf) + return -ENOMEM; + + dev_priv = vmw_priv(crtc->dev); + vmw_svga_enable(dev_priv); + + /* After we have alloced the backing store might not be able to + * resume the overlays, this is preferred to failing to alloc. + */ + vmw_overlay_pause_all(dev_priv); + ret = vmw_dmabuf_init(dev_priv, vps->dmabuf, size, + &vmw_vram_ne_placement, + false, &vmw_dmabuf_bo_free); + vmw_overlay_resume_all(dev_priv); + + if (ret != 0) + vps->dmabuf = NULL; /* vmw_dmabuf_init frees on error */ + else + vps->dmabuf_size = size; + + return ret; +} + + +static void +vmw_sou_primary_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_crtc *crtc = plane->state->crtc; + + if (crtc) + crtc->primary->fb = plane->state->fb; +} + + +static const struct drm_plane_funcs vmw_sou_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = vmw_du_primary_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, +}; + +static const struct drm_plane_funcs vmw_sou_cursor_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = vmw_du_cursor_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, +}; + +/* + * Atomic Helpers + */ +static const struct +drm_plane_helper_funcs vmw_sou_cursor_plane_helper_funcs = { + .atomic_check = vmw_du_cursor_plane_atomic_check, + .atomic_update = vmw_du_cursor_plane_atomic_update, + .prepare_fb = vmw_du_cursor_plane_prepare_fb, + .cleanup_fb = vmw_du_plane_cleanup_fb, +}; + +static const struct +drm_plane_helper_funcs vmw_sou_primary_plane_helper_funcs = { + .atomic_check = vmw_du_primary_plane_atomic_check, + .atomic_update = vmw_sou_primary_plane_atomic_update, + .prepare_fb = vmw_sou_primary_plane_prepare_fb, + .cleanup_fb = vmw_sou_primary_plane_cleanup_fb, +}; + +static const struct drm_crtc_helper_funcs vmw_sou_crtc_helper_funcs = { + .prepare = vmw_sou_crtc_helper_prepare, + .commit = vmw_sou_crtc_helper_commit, + .disable = vmw_sou_crtc_helper_disable, + .mode_set_nofb = vmw_sou_crtc_mode_set_nofb, + .atomic_check = vmw_du_crtc_atomic_check, + .atomic_begin = vmw_du_crtc_atomic_begin, + .atomic_flush = vmw_du_crtc_atomic_flush, +}; + + static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) { struct vmw_screen_object_unit *sou; struct drm_device *dev = dev_priv->dev; struct drm_connector *connector; struct drm_encoder *encoder; + struct drm_plane *primary, *cursor; struct drm_crtc *crtc; + int ret; sou = kzalloc(sizeof(*sou), GFP_KERNEL); if (!sou) @@ -513,27 +599,93 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) crtc = &sou->base.crtc; encoder = &sou->base.encoder; connector = &sou->base.connector; + primary = &sou->base.primary; + cursor = &sou->base.cursor; sou->base.active_implicit = false; sou->base.pref_active = (unit == 0); sou->base.pref_width = dev_priv->initial_width; sou->base.pref_height = dev_priv->initial_height; sou->base.pref_mode = NULL; + + /* + * Remove this after enabling atomic because property values can + * only exist in a state object + */ sou->base.is_implicit = false; - drm_connector_init(dev, connector, &vmw_sou_connector_funcs, - DRM_MODE_CONNECTOR_VIRTUAL); - connector->status = vmw_du_connector_detect(connector, true); + /* Initialize primary plane */ + vmw_du_plane_reset(primary); - drm_encoder_init(dev, encoder, &vmw_screen_object_encoder_funcs, - DRM_MODE_ENCODER_VIRTUAL, NULL); - drm_mode_connector_attach_encoder(connector, encoder); + ret = drm_universal_plane_init(dev, &sou->base.primary, + 0, &vmw_sou_plane_funcs, + vmw_primary_plane_formats, + ARRAY_SIZE(vmw_primary_plane_formats), + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + DRM_ERROR("Failed to initialize primary plane"); + goto err_free; + } + + drm_plane_helper_add(primary, &vmw_sou_primary_plane_helper_funcs); + + /* Initialize cursor plane */ + vmw_du_plane_reset(cursor); + + ret = drm_universal_plane_init(dev, &sou->base.cursor, + 0, &vmw_sou_cursor_funcs, + vmw_cursor_plane_formats, + ARRAY_SIZE(vmw_cursor_plane_formats), + DRM_PLANE_TYPE_CURSOR, NULL); + if (ret) { + DRM_ERROR("Failed to initialize cursor plane"); + drm_plane_cleanup(&sou->base.primary); + goto err_free; + } + + drm_plane_helper_add(cursor, &vmw_sou_cursor_plane_helper_funcs); + + vmw_du_connector_reset(connector); + ret = drm_connector_init(dev, connector, &vmw_sou_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + if (ret) { + DRM_ERROR("Failed to initialize connector\n"); + goto err_free; + } + + drm_connector_helper_add(connector, &vmw_sou_connector_helper_funcs); + connector->status = vmw_du_connector_detect(connector, true); + vmw_connector_state_to_vcs(connector->state)->is_implicit = false; + + + ret = drm_encoder_init(dev, encoder, &vmw_screen_object_encoder_funcs, + DRM_MODE_ENCODER_VIRTUAL, NULL); + if (ret) { + DRM_ERROR("Failed to initialize encoder\n"); + goto err_free_connector; + } + + (void) drm_mode_connector_attach_encoder(connector, encoder); encoder->possible_crtcs = (1 << unit); encoder->possible_clones = 0; - (void) drm_connector_register(connector); + ret = drm_connector_register(connector); + if (ret) { + DRM_ERROR("Failed to register connector\n"); + goto err_free_encoder; + } - drm_crtc_init(dev, crtc, &vmw_screen_object_crtc_funcs); + + vmw_du_crtc_reset(crtc); + ret = drm_crtc_init_with_planes(dev, crtc, &sou->base.primary, + &sou->base.cursor, + &vmw_screen_object_crtc_funcs, NULL); + if (ret) { + DRM_ERROR("Failed to initialize CRTC\n"); + goto err_free_unregister; + } + + drm_crtc_helper_add(crtc, &vmw_sou_crtc_helper_funcs); drm_mode_crtc_set_gamma_size(crtc, 256); @@ -550,6 +702,16 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) sou->base.is_implicit); return 0; + +err_free_unregister: + drm_connector_unregister(connector); +err_free_encoder: + drm_encoder_cleanup(encoder); +err_free_connector: + drm_connector_cleanup(connector); +err_free: + kfree(sou); + return ret; } int vmw_kms_sou_init_display(struct vmw_private *dev_priv) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_simple_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_simple_resource.c new file mode 100644 index 000000000000..051d3b39b0ea --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_simple_resource.c @@ -0,0 +1,256 @@ +/************************************************************************** + * + * Copyright © 2016 VMware, Inc., Palo Alto, CA., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "vmwgfx_drv.h" +#include "vmwgfx_resource_priv.h" + +/** + * struct vmw_user_simple_resource - User-space simple resource struct + * + * @base: The TTM base object implementing user-space visibility. + * @account_size: How much memory was accounted for this object. + * @simple: The embedded struct vmw_simple_resource. + */ +struct vmw_user_simple_resource { + struct ttm_base_object base; + size_t account_size; + struct vmw_simple_resource simple; +/* + * Nothing to be placed after @simple, since size of @simple is + * unknown. + */ +}; + + +/** + * vmw_simple_resource_init - Initialize a simple resource object. + * + * @dev_priv: Pointer to a struct device private. + * @simple: The struct vmw_simple_resource to initialize. + * @data: Data passed to the information initialization function. + * @res_free: Function pointer to destroy the simple resource. + * + * Returns: + * 0 if succeeded. + * Negative error value if error, in which case the resource will have been + * freed. + */ +static int vmw_simple_resource_init(struct vmw_private *dev_priv, + struct vmw_simple_resource *simple, + void *data, + void (*res_free)(struct vmw_resource *res)) +{ + struct vmw_resource *res = &simple->res; + int ret; + + ret = vmw_resource_init(dev_priv, res, false, res_free, + &simple->func->res_func); + + if (ret) { + res_free(res); + return ret; + } + + ret = simple->func->init(res, data); + if (ret) { + vmw_resource_unreference(&res); + return ret; + } + + vmw_resource_activate(&simple->res, simple->func->hw_destroy); + + return 0; +} + +/** + * vmw_simple_resource_free - Free a simple resource object. + * + * @res: The struct vmw_resource member of the simple resource object. + * + * Frees memory and memory accounting for the object. + */ +static void vmw_simple_resource_free(struct vmw_resource *res) +{ + struct vmw_user_simple_resource *usimple = + container_of(res, struct vmw_user_simple_resource, + simple.res); + struct vmw_private *dev_priv = res->dev_priv; + size_t size = usimple->account_size; + + ttm_base_object_kfree(usimple, base); + ttm_mem_global_free(vmw_mem_glob(dev_priv), size); +} + +/** + * vmw_simple_resource_base_release - TTM object release callback + * + * @p_base: The struct ttm_base_object member of the simple resource object. + * + * Called when the last reference to the embedded struct ttm_base_object is + * gone. Typically results in an object free, unless there are other + * references to the embedded struct vmw_resource. + */ +static void vmw_simple_resource_base_release(struct ttm_base_object **p_base) +{ + struct ttm_base_object *base = *p_base; + struct vmw_user_simple_resource *usimple = + container_of(base, struct vmw_user_simple_resource, base); + struct vmw_resource *res = &usimple->simple.res; + + *p_base = NULL; + vmw_resource_unreference(&res); +} + +/** + * vmw_simple_resource_create_ioctl - Helper to set up an ioctl function to + * create a struct vmw_simple_resource. + * + * @dev: Pointer to a struct drm device. + * @data: Ioctl argument. + * @file_priv: Pointer to a struct drm_file identifying the caller. + * @func: Pointer to a struct vmw_simple_resource_func identifying the + * simple resource type. + * + * Returns: + * 0 if success, + * Negative error value on error. + */ +int +vmw_simple_resource_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv, + const struct vmw_simple_resource_func *func) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_user_simple_resource *usimple; + struct vmw_resource *res; + struct vmw_resource *tmp; + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + size_t alloc_size; + size_t account_size; + int ret; + + alloc_size = offsetof(struct vmw_user_simple_resource, simple) + + func->size; + account_size = ttm_round_pot(alloc_size) + VMW_IDA_ACC_SIZE; + + ret = ttm_read_lock(&dev_priv->reservation_sem, true); + if (ret) + return ret; + + ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), account_size, + false, true); + ttm_read_unlock(&dev_priv->reservation_sem); + if (ret) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Out of graphics memory for %s" + " creation.\n", func->res_func.type_name); + + goto out_ret; + } + + usimple = kzalloc(alloc_size, GFP_KERNEL); + if (!usimple) { + ttm_mem_global_free(vmw_mem_glob(dev_priv), + account_size); + ret = -ENOMEM; + goto out_ret; + } + + usimple->simple.func = func; + usimple->account_size = account_size; + res = &usimple->simple.res; + usimple->base.shareable = false; + usimple->base.tfile = NULL; + + /* + * From here on, the destructor takes over resource freeing. + */ + ret = vmw_simple_resource_init(dev_priv, &usimple->simple, + data, vmw_simple_resource_free); + if (ret) + goto out_ret; + + tmp = vmw_resource_reference(res); + ret = ttm_base_object_init(tfile, &usimple->base, false, + func->ttm_res_type, + &vmw_simple_resource_base_release, NULL); + + if (ret) { + vmw_resource_unreference(&tmp); + goto out_err; + } + + func->set_arg_handle(data, usimple->base.hash.key); +out_err: + vmw_resource_unreference(&res); +out_ret: + return ret; +} + +/** + * vmw_simple_resource_lookup - Look up a simple resource from its user-space + * handle. + * + * @tfile: struct ttm_object_file identifying the caller. + * @handle: The user-space handle. + * @func: The struct vmw_simple_resource_func identifying the simple resource + * type. + * + * Returns: Refcounted pointer to the embedded struct vmw_resource if + * successfule. Error pointer otherwise. + */ +struct vmw_resource * +vmw_simple_resource_lookup(struct ttm_object_file *tfile, + uint32_t handle, + const struct vmw_simple_resource_func *func) +{ + struct vmw_user_simple_resource *usimple; + struct ttm_base_object *base; + struct vmw_resource *res; + + base = ttm_base_object_lookup(tfile, handle); + if (!base) { + DRM_ERROR("Invalid %s handle 0x%08lx.\n", + func->res_func.type_name, + (unsigned long) handle); + return ERR_PTR(-ESRCH); + } + + if (ttm_base_object_type(base) != func->ttm_res_type) { + ttm_base_object_unref(&base); + DRM_ERROR("Invalid type of %s handle 0x%08lx.\n", + func->res_func.type_name, + (unsigned long) handle); + return ERR_PTR(-EINVAL); + } + + usimple = container_of(base, typeof(*usimple), base); + res = vmw_resource_reference(&usimple->simple.res); + ttm_base_object_unref(&base); + + return res; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index b27cd18ee66a..e872ffe5f0db 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -28,6 +28,9 @@ #include "vmwgfx_kms.h" #include "device_include/svga3d_surfacedefs.h" #include +#include +#include + #define vmw_crtc_to_stdu(x) \ container_of(x, struct vmw_screen_target_display_unit, base.crtc) @@ -101,11 +104,15 @@ struct vmw_stdu_surface_copy { */ struct vmw_screen_target_display_unit { struct vmw_display_unit base; - - struct vmw_surface *display_srf; + const struct vmw_surface *display_srf; enum stdu_content_type content_fb_type; + s32 display_width, display_height; bool defined; + + /* For CPU Blit */ + struct ttm_bo_kmap_obj host_map, guest_map; + unsigned int cpp; }; @@ -114,32 +121,6 @@ static void vmw_stdu_destroy(struct vmw_screen_target_display_unit *stdu); -/****************************************************************************** - * Screen Target Display Unit helper Functions - *****************************************************************************/ - -/** - * vmw_stdu_unpin_display - unpins the resource associated with display surface - * - * @stdu: contains the display surface - * - * If the display surface was privatedly allocated by - * vmw_surface_gb_priv_define() and not registered as a framebuffer, then it - * won't be automatically cleaned up when all the framebuffers are freed. As - * such, we have to explicitly call vmw_resource_unreference() to get it freed. - */ -static void vmw_stdu_unpin_display(struct vmw_screen_target_display_unit *stdu) -{ - if (stdu->display_srf) { - struct vmw_resource *res = &stdu->display_srf->res; - - vmw_resource_unpin(res); - vmw_surface_unreference(&stdu->display_srf); - } -} - - - /****************************************************************************** * Screen Target Display Unit CRTC Functions *****************************************************************************/ @@ -208,6 +189,8 @@ static int vmw_stdu_define_st(struct vmw_private *dev_priv, vmw_fifo_commit(dev_priv, sizeof(*cmd)); stdu->defined = true; + stdu->display_width = mode->hdisplay; + stdu->display_height = mode->vdisplay; return 0; } @@ -225,7 +208,7 @@ static int vmw_stdu_define_st(struct vmw_private *dev_priv, */ static int vmw_stdu_bind_st(struct vmw_private *dev_priv, struct vmw_screen_target_display_unit *stdu, - struct vmw_resource *res) + const struct vmw_resource *res) { SVGA3dSurfaceImageId image; @@ -305,7 +288,6 @@ static int vmw_stdu_update_st(struct vmw_private *dev_priv, struct vmw_screen_target_display_unit *stdu) { struct vmw_stdu_update *cmd; - struct drm_crtc *crtc = &stdu->base.crtc; if (!stdu->defined) { DRM_ERROR("No screen target defined"); @@ -319,8 +301,9 @@ static int vmw_stdu_update_st(struct vmw_private *dev_priv, return -ENOMEM; } - vmw_stdu_populate_update(cmd, stdu->base.unit, 0, crtc->mode.hdisplay, - 0, crtc->mode.vdisplay); + vmw_stdu_populate_update(cmd, stdu->base.unit, + 0, stdu->display_width, + 0, stdu->display_height); vmw_fifo_commit(dev_priv, sizeof(*cmd)); @@ -370,261 +353,110 @@ static int vmw_stdu_destroy_st(struct vmw_private *dev_priv, DRM_ERROR("Failed to sync with HW"); stdu->defined = false; + stdu->display_width = 0; + stdu->display_height = 0; return ret; } -/** - * vmw_stdu_bind_fb - Bind an fb to a defined screen target - * - * @dev_priv: Pointer to a device private struct. - * @crtc: The crtc holding the screen target. - * @mode: The mode currently used by the screen target. Must be non-NULL. - * @new_fb: The new framebuffer to bind. Must be non-NULL. - * - * RETURNS: - * 0 on success, error code on failure. - */ -static int vmw_stdu_bind_fb(struct vmw_private *dev_priv, - struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_framebuffer *new_fb) -{ - struct vmw_screen_target_display_unit *stdu = vmw_crtc_to_stdu(crtc); - struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb); - struct vmw_surface *new_display_srf = NULL; - enum stdu_content_type new_content_type; - struct vmw_framebuffer_surface *new_vfbs; - int ret; - - WARN_ON_ONCE(!stdu->defined); - - new_vfbs = (vfb->dmabuf) ? NULL : vmw_framebuffer_to_vfbs(new_fb); - - if (new_vfbs && new_vfbs->surface->base_size.width == mode->hdisplay && - new_vfbs->surface->base_size.height == mode->vdisplay) - new_content_type = SAME_AS_DISPLAY; - else if (vfb->dmabuf) - new_content_type = SEPARATE_DMA; - else - new_content_type = SEPARATE_SURFACE; - - if (new_content_type != SAME_AS_DISPLAY && - !stdu->display_srf) { - struct vmw_surface content_srf; - struct drm_vmw_size display_base_size = {0}; - - display_base_size.width = mode->hdisplay; - display_base_size.height = mode->vdisplay; - display_base_size.depth = 1; - - /* - * If content buffer is a DMA buf, then we have to construct - * surface info - */ - if (new_content_type == SEPARATE_DMA) { - - switch (new_fb->format->cpp[0] * 8) { - case 32: - content_srf.format = SVGA3D_X8R8G8B8; - break; - - case 16: - content_srf.format = SVGA3D_R5G6B5; - break; - - case 8: - content_srf.format = SVGA3D_P8; - break; - - default: - DRM_ERROR("Invalid format\n"); - return -EINVAL; - } - - content_srf.flags = 0; - content_srf.mip_levels[0] = 1; - content_srf.multisample_count = 0; - } else { - content_srf = *new_vfbs->surface; - } - - - ret = vmw_surface_gb_priv_define(crtc->dev, - 0, /* because kernel visible only */ - content_srf.flags, - content_srf.format, - true, /* a scanout buffer */ - content_srf.mip_levels[0], - content_srf.multisample_count, - 0, - display_base_size, - &new_display_srf); - if (unlikely(ret != 0)) { - DRM_ERROR("Could not allocate screen target surface.\n"); - return ret; - } - } else if (new_content_type == SAME_AS_DISPLAY) { - new_display_srf = vmw_surface_reference(new_vfbs->surface); - } - - if (new_display_srf) { - /* Pin new surface before flipping */ - ret = vmw_resource_pin(&new_display_srf->res, false); - if (ret) - goto out_srf_unref; - - ret = vmw_stdu_bind_st(dev_priv, stdu, &new_display_srf->res); - if (ret) - goto out_srf_unpin; - - /* Unpin and unreference old surface */ - vmw_stdu_unpin_display(stdu); - - /* Transfer the reference */ - stdu->display_srf = new_display_srf; - new_display_srf = NULL; - } - - crtc->primary->fb = new_fb; - stdu->content_fb_type = new_content_type; - return 0; - -out_srf_unpin: - vmw_resource_unpin(&new_display_srf->res); -out_srf_unref: - vmw_surface_unreference(&new_display_srf); - return ret; -} /** - * vmw_stdu_crtc_set_config - Sets a mode + * vmw_stdu_crtc_mode_set_nofb - Updates screen target size * - * @set: mode parameters + * @crtc: CRTC associated with the screen target * - * This function is the device-specific portion of the DRM CRTC mode set. - * For the SVGA device, we do this by defining a Screen Target, binding a - * GB Surface to that target, and finally update the screen target. + * This function defines/destroys a screen target * - * RETURNS: - * 0 on success, error code otherwise */ -static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) +static void vmw_stdu_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct vmw_private *dev_priv; - struct vmw_framebuffer *vfb; struct vmw_screen_target_display_unit *stdu; - struct drm_display_mode *mode; - struct drm_framebuffer *new_fb; - struct drm_crtc *crtc; - struct drm_encoder *encoder; - struct drm_connector *connector; - bool turning_off; - int ret; + int ret; - if (!set || !set->crtc) - return -EINVAL; - - crtc = set->crtc; stdu = vmw_crtc_to_stdu(crtc); - mode = set->mode; - new_fb = set->fb; dev_priv = vmw_priv(crtc->dev); - turning_off = set->num_connectors == 0 || !mode || !new_fb; - vfb = (new_fb) ? vmw_framebuffer_to_vfb(new_fb) : NULL; - - if (set->num_connectors > 1) { - DRM_ERROR("Too many connectors\n"); - return -EINVAL; - } - - if (set->num_connectors == 1 && - set->connectors[0] != &stdu->base.connector) { - DRM_ERROR("Connectors don't match %p %p\n", - set->connectors[0], &stdu->base.connector); - return -EINVAL; - } - - if (!turning_off && (set->x + mode->hdisplay > new_fb->width || - set->y + mode->vdisplay > new_fb->height)) { - DRM_ERROR("Set outside of framebuffer\n"); - return -EINVAL; - } - - /* Only one active implicit frame-buffer at a time. */ - mutex_lock(&dev_priv->global_kms_state_mutex); - if (!turning_off && stdu->base.is_implicit && dev_priv->implicit_fb && - !(dev_priv->num_implicit == 1 && stdu->base.active_implicit) - && dev_priv->implicit_fb != vfb) { - mutex_unlock(&dev_priv->global_kms_state_mutex); - DRM_ERROR("Multiple implicit framebuffers not supported.\n"); - return -EINVAL; - } - mutex_unlock(&dev_priv->global_kms_state_mutex); - - /* Since they always map one to one these are safe */ - connector = &stdu->base.connector; - encoder = &stdu->base.encoder; if (stdu->defined) { ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); if (ret) - return ret; + DRM_ERROR("Failed to blank CRTC\n"); - vmw_stdu_unpin_display(stdu); (void) vmw_stdu_update_st(dev_priv, stdu); - vmw_kms_del_active(dev_priv, &stdu->base); ret = vmw_stdu_destroy_st(dev_priv, stdu); if (ret) - return ret; + DRM_ERROR("Failed to destroy Screen Target\n"); - crtc->primary->fb = NULL; - crtc->enabled = false; - encoder->crtc = NULL; - connector->encoder = NULL; stdu->content_fb_type = SAME_AS_DISPLAY; - crtc->x = set->x; - crtc->y = set->y; } - if (turning_off) - return 0; + if (!crtc->state->enable) + return; - /* - * Steps to displaying a surface, assume surface is already - * bound: - * 1. define a screen target - * 2. bind a fb to the screen target - * 3. update that screen target (this is done later by - * vmw_kms_stdu_do_surface_dirty_or_present) - */ - /* - * Note on error handling: We can't really restore the crtc to - * it's original state on error, but we at least update the - * current state to what's submitted to hardware to enable - * future recovery. - */ vmw_svga_enable(dev_priv); - ret = vmw_stdu_define_st(dev_priv, stdu, mode, set->x, set->y); + ret = vmw_stdu_define_st(dev_priv, stdu, &crtc->mode, crtc->x, crtc->y); + if (ret) - return ret; + DRM_ERROR("Failed to define Screen Target of size %dx%d\n", + crtc->x, crtc->y); +} - crtc->x = set->x; - crtc->y = set->y; - crtc->mode = *mode; - ret = vmw_stdu_bind_fb(dev_priv, crtc, mode, new_fb); - if (ret) - return ret; +static void vmw_stdu_crtc_helper_prepare(struct drm_crtc *crtc) +{ +} - vmw_kms_add_active(dev_priv, &stdu->base, vfb); - crtc->enabled = true; - connector->encoder = encoder; - encoder->crtc = crtc; - return 0; +static void vmw_stdu_crtc_helper_commit(struct drm_crtc *crtc) +{ + struct vmw_private *dev_priv; + struct vmw_screen_target_display_unit *stdu; + struct vmw_framebuffer *vfb; + struct drm_framebuffer *fb; + + + stdu = vmw_crtc_to_stdu(crtc); + dev_priv = vmw_priv(crtc->dev); + fb = crtc->primary->fb; + + vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; + + if (vfb) + vmw_kms_add_active(dev_priv, &stdu->base, vfb); + else + vmw_kms_del_active(dev_priv, &stdu->base); +} + +static void vmw_stdu_crtc_helper_disable(struct drm_crtc *crtc) +{ + struct vmw_private *dev_priv; + struct vmw_screen_target_display_unit *stdu; + int ret; + + + if (!crtc) { + DRM_ERROR("CRTC is NULL\n"); + return; + } + + stdu = vmw_crtc_to_stdu(crtc); + dev_priv = vmw_priv(crtc->dev); + + if (stdu->defined) { + ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); + if (ret) + DRM_ERROR("Failed to blank CRTC\n"); + + (void) vmw_stdu_update_st(dev_priv, stdu); + + ret = vmw_stdu_destroy_st(dev_priv, stdu); + if (ret) + DRM_ERROR("Failed to destroy Screen Target\n"); + + stdu->content_fb_type = SAME_AS_DISPLAY; + } } /** @@ -653,9 +485,9 @@ static int vmw_stdu_crtc_page_flip(struct drm_crtc *crtc, { struct vmw_private *dev_priv = vmw_priv(crtc->dev); - struct vmw_screen_target_display_unit *stdu; - struct drm_vmw_rect vclips; + struct vmw_screen_target_display_unit *stdu = vmw_crtc_to_stdu(crtc); struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb); + struct drm_vmw_rect vclips; int ret; dev_priv = vmw_priv(crtc->dev); @@ -664,25 +496,42 @@ static int vmw_stdu_crtc_page_flip(struct drm_crtc *crtc, if (!stdu->defined || !vmw_kms_crtc_flippable(dev_priv, crtc)) return -EINVAL; - ret = vmw_stdu_bind_fb(dev_priv, crtc, &crtc->mode, new_fb); - if (ret) + /* + * We're always async, but the helper doesn't know how to set async + * so lie to the helper. Also, the helper expects someone + * to pick the event up from the crtc state, and if nobody does, + * it will free it. Since we handle the event in this function, + * don't hand it to the helper. + */ + flags &= ~DRM_MODE_PAGE_FLIP_ASYNC; + ret = drm_atomic_helper_page_flip(crtc, new_fb, NULL, flags); + if (ret) { + DRM_ERROR("Page flip error %d.\n", ret); return ret; + } if (stdu->base.is_implicit) vmw_kms_update_implicit_fb(dev_priv, crtc); + /* + * Now that we've bound a new surface to the screen target, + * update the contents. + */ vclips.x = crtc->x; vclips.y = crtc->y; vclips.w = crtc->mode.hdisplay; vclips.h = crtc->mode.vdisplay; + if (vfb->dmabuf) ret = vmw_kms_stdu_dma(dev_priv, NULL, vfb, NULL, NULL, &vclips, 1, 1, true, false); else ret = vmw_kms_stdu_surface_dirty(dev_priv, vfb, NULL, &vclips, NULL, 0, 0, 1, 1, NULL); - if (ret) + if (ret) { + DRM_ERROR("Page flip update error %d.\n", ret); return ret; + } if (event) { struct vmw_fence_obj *fence = NULL; @@ -699,7 +548,7 @@ static int vmw_stdu_crtc_page_flip(struct drm_crtc *crtc, true); vmw_fence_obj_unreference(&fence); } else { - vmw_fifo_flush(dev_priv, false); + (void) vmw_fifo_flush(dev_priv, false); } return 0; @@ -791,6 +640,129 @@ static void vmw_stdu_dmabuf_fifo_commit(struct vmw_kms_dirty *dirty) ddirty->right = ddirty->bottom = S32_MIN; } + +/** + * vmw_stdu_dmabuf_cpu_clip - Callback to encode a CPU blit + * + * @dirty: The closure structure. + * + * This function calculates the bounding box for all the incoming clips + */ +static void vmw_stdu_dmabuf_cpu_clip(struct vmw_kms_dirty *dirty) +{ + struct vmw_stdu_dirty *ddirty = + container_of(dirty, struct vmw_stdu_dirty, base); + + dirty->num_hits = 1; + + /* Calculate bounding box */ + ddirty->left = min_t(s32, ddirty->left, dirty->unit_x1); + ddirty->top = min_t(s32, ddirty->top, dirty->unit_y1); + ddirty->right = max_t(s32, ddirty->right, dirty->unit_x2); + ddirty->bottom = max_t(s32, ddirty->bottom, dirty->unit_y2); +} + + +/** + * vmw_stdu_dmabuf_cpu_commit - Callback to do a CPU blit from DMAbuf + * + * @dirty: The closure structure. + * + * For the special case when we cannot create a proxy surface in a + * 2D VM, we have to do a CPU blit ourselves. + */ +static void vmw_stdu_dmabuf_cpu_commit(struct vmw_kms_dirty *dirty) +{ + struct vmw_stdu_dirty *ddirty = + container_of(dirty, struct vmw_stdu_dirty, base); + struct vmw_screen_target_display_unit *stdu = + container_of(dirty->unit, typeof(*stdu), base); + s32 width, height; + s32 src_pitch, dst_pitch; + u8 *src, *dst; + bool not_used; + + + if (!dirty->num_hits) + return; + + width = ddirty->right - ddirty->left; + height = ddirty->bottom - ddirty->top; + + if (width == 0 || height == 0) + return; + + + /* Assume we are blitting from Host (display_srf) to Guest (dmabuf) */ + src_pitch = stdu->display_srf->base_size.width * stdu->cpp; + src = ttm_kmap_obj_virtual(&stdu->host_map, ¬_used); + src += dirty->unit_y1 * src_pitch + dirty->unit_x1 * stdu->cpp; + + dst_pitch = ddirty->pitch; + dst = ttm_kmap_obj_virtual(&stdu->guest_map, ¬_used); + dst += dirty->fb_y * dst_pitch + dirty->fb_x * stdu->cpp; + + + /* Figure out the real direction */ + if (ddirty->transfer == SVGA3D_WRITE_HOST_VRAM) { + u8 *tmp; + s32 tmp_pitch; + + tmp = src; + tmp_pitch = src_pitch; + + src = dst; + src_pitch = dst_pitch; + + dst = tmp; + dst_pitch = tmp_pitch; + } + + /* CPU Blit */ + while (height-- > 0) { + memcpy(dst, src, width * stdu->cpp); + dst += dst_pitch; + src += src_pitch; + } + + if (ddirty->transfer == SVGA3D_WRITE_HOST_VRAM) { + struct vmw_private *dev_priv; + struct vmw_stdu_update *cmd; + struct drm_clip_rect region; + int ret; + + /* We are updating the actual surface, not a proxy */ + region.x1 = ddirty->left; + region.x2 = ddirty->right; + region.y1 = ddirty->top; + region.y2 = ddirty->bottom; + ret = vmw_kms_update_proxy( + (struct vmw_resource *) &stdu->display_srf->res, + (const struct drm_clip_rect *) ®ion, 1, 1); + if (ret) + goto out_cleanup; + + + dev_priv = vmw_priv(stdu->base.crtc.dev); + cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); + + if (!cmd) { + DRM_ERROR("Cannot reserve FIFO space to update STDU"); + goto out_cleanup; + } + + vmw_stdu_populate_update(cmd, stdu->base.unit, + ddirty->left, ddirty->right, + ddirty->top, ddirty->bottom); + + vmw_fifo_commit(dev_priv, sizeof(*cmd)); + } + +out_cleanup: + ddirty->left = ddirty->top = S32_MAX; + ddirty->right = ddirty->bottom = S32_MIN; +} + /** * vmw_kms_stdu_dma - Perform a DMA transfer between a dma-buffer backed * framebuffer and the screen target system. @@ -849,6 +821,13 @@ int vmw_kms_stdu_dma(struct vmw_private *dev_priv, if (to_surface) ddirty.base.fifo_reserve_size += sizeof(struct vmw_stdu_update); + /* 2D VMs cannot use SVGA_3D_CMD_SURFACE_DMA so do CPU blit instead */ + if (!(dev_priv->capabilities & SVGA_CAP_3D)) { + ddirty.base.fifo_commit = vmw_stdu_dmabuf_cpu_commit; + ddirty.base.clip = vmw_stdu_dmabuf_cpu_clip; + ddirty.base.fifo_reserve_size = 0; + } + ret = vmw_kms_helper_dirty(dev_priv, vfb, clips, vclips, 0, 0, num_clips, increment, &ddirty.base); vmw_kms_helper_buffer_finish(dev_priv, file_priv, buf, NULL, @@ -1015,11 +994,12 @@ int vmw_kms_stdu_surface_dirty(struct vmw_private *dev_priv, * Screen Target CRTC dispatch table */ static const struct drm_crtc_funcs vmw_stdu_crtc_funcs = { - .cursor_set2 = vmw_du_crtc_cursor_set2, - .cursor_move = vmw_du_crtc_cursor_move, .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_stdu_crtc_destroy, - .set_config = vmw_stdu_crtc_set_config, + .reset = vmw_du_crtc_reset, + .atomic_duplicate_state = vmw_du_crtc_duplicate_state, + .atomic_destroy_state = vmw_du_crtc_destroy_state, + .set_config = vmw_kms_set_config, .page_flip = vmw_stdu_crtc_page_flip, }; @@ -1077,9 +1057,354 @@ static const struct drm_connector_funcs vmw_stdu_connector_funcs = { .fill_modes = vmw_du_connector_fill_modes, .set_property = vmw_du_connector_set_property, .destroy = vmw_stdu_connector_destroy, + .reset = vmw_du_connector_reset, + .atomic_duplicate_state = vmw_du_connector_duplicate_state, + .atomic_destroy_state = vmw_du_connector_destroy_state, + .atomic_set_property = vmw_du_connector_atomic_set_property, + .atomic_get_property = vmw_du_connector_atomic_get_property, }; +static const struct +drm_connector_helper_funcs vmw_stdu_connector_helper_funcs = { + .best_encoder = drm_atomic_helper_best_encoder, +}; + + + +/****************************************************************************** + * Screen Target Display Plane Functions + *****************************************************************************/ + + + +/** + * vmw_stdu_primary_plane_cleanup_fb - Unpins the display surface + * + * @plane: display plane + * @old_state: Contains the FB to clean up + * + * Unpins the display surface + * + * Returns 0 on success + */ +static void +vmw_stdu_primary_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); + + if (vps->guest_map.virtual) + ttm_bo_kunmap(&vps->guest_map); + + if (vps->host_map.virtual) + ttm_bo_kunmap(&vps->host_map); + + if (vps->surf) + WARN_ON(!vps->pinned); + + vmw_du_plane_cleanup_fb(plane, old_state); + + vps->content_fb_type = SAME_AS_DISPLAY; + vps->cpp = 0; +} + + + +/** + * vmw_stdu_primary_plane_prepare_fb - Readies the display surface + * + * @plane: display plane + * @new_state: info on the new plane state, including the FB + * + * This function allocates a new display surface if the content is + * backed by a DMA. The display surface is pinned here, and it'll + * be unpinned in .cleanup_fb() + * + * Returns 0 on success + */ +static int +vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct vmw_private *dev_priv = vmw_priv(plane->dev); + struct drm_framebuffer *new_fb = new_state->fb; + struct vmw_framebuffer *vfb; + struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); + enum stdu_content_type new_content_type; + struct vmw_framebuffer_surface *new_vfbs; + struct drm_crtc *crtc = new_state->crtc; + uint32_t hdisplay = new_state->crtc_w, vdisplay = new_state->crtc_h; + int ret; + + /* No FB to prepare */ + if (!new_fb) { + if (vps->surf) { + WARN_ON(vps->pinned != 0); + vmw_surface_unreference(&vps->surf); + } + + return 0; + } + + vfb = vmw_framebuffer_to_vfb(new_fb); + new_vfbs = (vfb->dmabuf) ? NULL : vmw_framebuffer_to_vfbs(new_fb); + + if (new_vfbs && new_vfbs->surface->base_size.width == hdisplay && + new_vfbs->surface->base_size.height == vdisplay) + new_content_type = SAME_AS_DISPLAY; + else if (vfb->dmabuf) + new_content_type = SEPARATE_DMA; + else + new_content_type = SEPARATE_SURFACE; + + if (new_content_type != SAME_AS_DISPLAY) { + struct vmw_surface content_srf; + struct drm_vmw_size display_base_size = {0}; + + display_base_size.width = hdisplay; + display_base_size.height = vdisplay; + display_base_size.depth = 1; + + /* + * If content buffer is a DMA buf, then we have to construct + * surface info + */ + if (new_content_type == SEPARATE_DMA) { + + switch (new_fb->format->cpp[0]*8) { + case 32: + content_srf.format = SVGA3D_X8R8G8B8; + break; + + case 16: + content_srf.format = SVGA3D_R5G6B5; + break; + + case 8: + content_srf.format = SVGA3D_P8; + break; + + default: + DRM_ERROR("Invalid format\n"); + return -EINVAL; + } + + content_srf.flags = 0; + content_srf.mip_levels[0] = 1; + content_srf.multisample_count = 0; + } else { + content_srf = *new_vfbs->surface; + } + + if (vps->surf) { + struct drm_vmw_size cur_base_size = vps->surf->base_size; + + if (cur_base_size.width != display_base_size.width || + cur_base_size.height != display_base_size.height || + vps->surf->format != content_srf.format) { + WARN_ON(vps->pinned != 0); + vmw_surface_unreference(&vps->surf); + } + + } + + if (!vps->surf) { + ret = vmw_surface_gb_priv_define + (crtc->dev, + /* Kernel visible only */ + 0, + content_srf.flags, + content_srf.format, + true, /* a scanout buffer */ + content_srf.mip_levels[0], + content_srf.multisample_count, + 0, + display_base_size, + &vps->surf); + if (ret != 0) { + DRM_ERROR("Couldn't allocate STDU surface.\n"); + return ret; + } + } + } else { + /* + * prepare_fb and clean_fb should only take care of pinning + * and unpinning. References are tracked by state objects. + * The only time we add a reference in prepare_fb is if the + * state object doesn't have a reference to begin with + */ + if (vps->surf) { + WARN_ON(vps->pinned != 0); + vmw_surface_unreference(&vps->surf); + } + + vps->surf = vmw_surface_reference(new_vfbs->surface); + } + + if (vps->surf) { + + /* Pin new surface before flipping */ + ret = vmw_resource_pin(&vps->surf->res, false); + if (ret) + goto out_srf_unref; + + vps->pinned++; + } + + vps->content_fb_type = new_content_type; + + /* + * This should only happen if the DMA buf is too large to create a + * proxy surface for. + * If we are a 2D VM with a DMA buffer then we have to use CPU blit + * so cache these mappings + */ + if (vps->content_fb_type == SEPARATE_DMA && + !(dev_priv->capabilities & SVGA_CAP_3D)) { + + struct vmw_framebuffer_dmabuf *new_vfbd; + + new_vfbd = vmw_framebuffer_to_vfbd(new_fb); + + ret = ttm_bo_reserve(&new_vfbd->buffer->base, false, false, + NULL); + if (ret) + goto out_srf_unpin; + + ret = ttm_bo_kmap(&new_vfbd->buffer->base, 0, + new_vfbd->buffer->base.num_pages, + &vps->guest_map); + + ttm_bo_unreserve(&new_vfbd->buffer->base); + + if (ret) { + DRM_ERROR("Failed to map content buffer to CPU\n"); + goto out_srf_unpin; + } + + ret = ttm_bo_kmap(&vps->surf->res.backup->base, 0, + vps->surf->res.backup->base.num_pages, + &vps->host_map); + if (ret) { + DRM_ERROR("Failed to map display buffer to CPU\n"); + ttm_bo_kunmap(&vps->guest_map); + goto out_srf_unpin; + } + + vps->cpp = new_fb->pitches[0] / new_fb->width; + } + + return 0; + +out_srf_unpin: + vmw_resource_unpin(&vps->surf->res); + vps->pinned--; + +out_srf_unref: + vmw_surface_unreference(&vps->surf); + return ret; +} + + + +/** + * vmw_stdu_primary_plane_atomic_update - formally switches STDU to new plane + * + * @plane: display plane + * @old_state: Only used to get crtc info + * + * Formally update stdu->display_srf to the new plane, and bind the new + * plane STDU. This function is called during the commit phase when + * all the preparation have been done and all the configurations have + * been checked. + */ +static void +vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct vmw_private *dev_priv; + struct vmw_screen_target_display_unit *stdu; + struct vmw_plane_state *vps = vmw_plane_state_to_vps(plane->state); + struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc; + int ret; + + stdu = vmw_crtc_to_stdu(crtc); + dev_priv = vmw_priv(crtc->dev); + + stdu->display_srf = vps->surf; + stdu->content_fb_type = vps->content_fb_type; + stdu->cpp = vps->cpp; + memcpy(&stdu->guest_map, &vps->guest_map, sizeof(vps->guest_map)); + memcpy(&stdu->host_map, &vps->host_map, sizeof(vps->host_map)); + + if (!stdu->defined) + return; + + if (plane->state->fb) + ret = vmw_stdu_bind_st(dev_priv, stdu, &stdu->display_srf->res); + else + ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); + + /* + * We cannot really fail this function, so if we do, then output an + * error and quit + */ + if (ret) + DRM_ERROR("Failed to bind surface to STDU.\n"); + else + crtc->primary->fb = plane->state->fb; +} + + +static const struct drm_plane_funcs vmw_stdu_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = vmw_du_primary_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, +}; + +static const struct drm_plane_funcs vmw_stdu_cursor_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = vmw_du_cursor_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, +}; + + +/* + * Atomic Helpers + */ +static const struct +drm_plane_helper_funcs vmw_stdu_cursor_plane_helper_funcs = { + .atomic_check = vmw_du_cursor_plane_atomic_check, + .atomic_update = vmw_du_cursor_plane_atomic_update, + .prepare_fb = vmw_du_cursor_plane_prepare_fb, + .cleanup_fb = vmw_du_plane_cleanup_fb, +}; + +static const struct +drm_plane_helper_funcs vmw_stdu_primary_plane_helper_funcs = { + .atomic_check = vmw_du_primary_plane_atomic_check, + .atomic_update = vmw_stdu_primary_plane_atomic_update, + .prepare_fb = vmw_stdu_primary_plane_prepare_fb, + .cleanup_fb = vmw_stdu_primary_plane_cleanup_fb, +}; + +static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = { + .prepare = vmw_stdu_crtc_helper_prepare, + .commit = vmw_stdu_crtc_helper_commit, + .disable = vmw_stdu_crtc_helper_disable, + .mode_set_nofb = vmw_stdu_crtc_mode_set_nofb, + .atomic_check = vmw_du_crtc_atomic_check, + .atomic_begin = vmw_du_crtc_atomic_begin, + .atomic_flush = vmw_du_crtc_atomic_flush, +}; + /** * vmw_stdu_init - Sets up a Screen Target Display Unit @@ -1097,7 +1422,9 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) struct drm_device *dev = dev_priv->dev; struct drm_connector *connector; struct drm_encoder *encoder; + struct drm_plane *primary, *cursor; struct drm_crtc *crtc; + int ret; stdu = kzalloc(sizeof(*stdu), GFP_KERNEL); @@ -1108,25 +1435,90 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) crtc = &stdu->base.crtc; encoder = &stdu->base.encoder; connector = &stdu->base.connector; + primary = &stdu->base.primary; + cursor = &stdu->base.cursor; stdu->base.pref_active = (unit == 0); stdu->base.pref_width = dev_priv->initial_width; stdu->base.pref_height = dev_priv->initial_height; + + /* + * Remove this after enabling atomic because property values can + * only exist in a state object + */ stdu->base.is_implicit = false; - drm_connector_init(dev, connector, &vmw_stdu_connector_funcs, - DRM_MODE_CONNECTOR_VIRTUAL); - connector->status = vmw_du_connector_detect(connector, false); + /* Initialize primary plane */ + vmw_du_plane_reset(primary); - drm_encoder_init(dev, encoder, &vmw_stdu_encoder_funcs, - DRM_MODE_ENCODER_VIRTUAL, NULL); - drm_mode_connector_attach_encoder(connector, encoder); + ret = drm_universal_plane_init(dev, primary, + 0, &vmw_stdu_plane_funcs, + vmw_primary_plane_formats, + ARRAY_SIZE(vmw_primary_plane_formats), + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + DRM_ERROR("Failed to initialize primary plane"); + goto err_free; + } + + drm_plane_helper_add(primary, &vmw_stdu_primary_plane_helper_funcs); + + /* Initialize cursor plane */ + vmw_du_plane_reset(cursor); + + ret = drm_universal_plane_init(dev, cursor, + 0, &vmw_stdu_cursor_funcs, + vmw_cursor_plane_formats, + ARRAY_SIZE(vmw_cursor_plane_formats), + DRM_PLANE_TYPE_CURSOR, NULL); + if (ret) { + DRM_ERROR("Failed to initialize cursor plane"); + drm_plane_cleanup(&stdu->base.primary); + goto err_free; + } + + drm_plane_helper_add(cursor, &vmw_stdu_cursor_plane_helper_funcs); + + vmw_du_connector_reset(connector); + + ret = drm_connector_init(dev, connector, &vmw_stdu_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + if (ret) { + DRM_ERROR("Failed to initialize connector\n"); + goto err_free; + } + + drm_connector_helper_add(connector, &vmw_stdu_connector_helper_funcs); + connector->status = vmw_du_connector_detect(connector, false); + vmw_connector_state_to_vcs(connector->state)->is_implicit = false; + + ret = drm_encoder_init(dev, encoder, &vmw_stdu_encoder_funcs, + DRM_MODE_ENCODER_VIRTUAL, NULL); + if (ret) { + DRM_ERROR("Failed to initialize encoder\n"); + goto err_free_connector; + } + + (void) drm_mode_connector_attach_encoder(connector, encoder); encoder->possible_crtcs = (1 << unit); encoder->possible_clones = 0; - (void) drm_connector_register(connector); + ret = drm_connector_register(connector); + if (ret) { + DRM_ERROR("Failed to register connector\n"); + goto err_free_encoder; + } - drm_crtc_init(dev, crtc, &vmw_stdu_crtc_funcs); + vmw_du_crtc_reset(crtc); + ret = drm_crtc_init_with_planes(dev, crtc, &stdu->base.primary, + &stdu->base.cursor, + &vmw_stdu_crtc_funcs, NULL); + if (ret) { + DRM_ERROR("Failed to initialize CRTC\n"); + goto err_free_unregister; + } + + drm_crtc_helper_add(crtc, &vmw_stdu_crtc_helper_funcs); drm_mode_crtc_set_gamma_size(crtc, 256); @@ -1142,6 +1534,16 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) dev_priv->implicit_placement_property, stdu->base.is_implicit); return 0; + +err_free_unregister: + drm_connector_unregister(connector); +err_free_encoder: + drm_encoder_cleanup(encoder); +err_free_connector: + drm_connector_cleanup(connector); +err_free: + kfree(stdu); + return ret; } @@ -1155,8 +1557,6 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) */ static void vmw_stdu_destroy(struct vmw_screen_target_display_unit *stdu) { - vmw_stdu_unpin_display(stdu); - vmw_du_cleanup(&stdu->base); kfree(stdu); } @@ -1201,6 +1601,31 @@ int vmw_kms_stdu_init_display(struct vmw_private *dev_priv) dev_priv->active_display_unit = vmw_du_screen_target; + if (dev_priv->capabilities & SVGA_CAP_3D) { + /* + * For 3D VMs, display (scanout) buffer size is the smaller of + * max texture and max STDU + */ + uint32_t max_width, max_height; + + max_width = min(dev_priv->texture_max_width, + dev_priv->stdu_max_width); + max_height = min(dev_priv->texture_max_height, + dev_priv->stdu_max_height); + + dev->mode_config.max_width = max_width; + dev->mode_config.max_height = max_height; + } else { + /* + * Given various display aspect ratios, there's no way to + * estimate these using prim_bb_mem. So just set these to + * something arbitrarily large and we will reject any layout + * that doesn't fit prim_bb_mem later + */ + dev->mode_config.max_width = 16384; + dev->mode_config.max_height = 16384; + } + vmw_kms_create_implicit_placement_property(dev_priv, false); for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index b445ce9b9757..41b9d20d6ae7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -814,7 +814,6 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, } else { srf->snooper.image = NULL; } - srf->snooper.crtc = NULL; user_srf->prime.base.shareable = false; user_srf->prime.base.tfile = NULL; @@ -1480,10 +1479,24 @@ int vmw_surface_gb_priv_define(struct drm_device *dev, *srf_out = NULL; if (for_scanout) { + uint32_t max_width, max_height; + if (!svga3dsurface_is_screen_target_format(format)) { DRM_ERROR("Invalid Screen Target surface format."); return -EINVAL; } + + max_width = min(dev_priv->texture_max_width, + dev_priv->stdu_max_width); + max_height = min(dev_priv->texture_max_height, + dev_priv->stdu_max_height); + + if (size.width > max_width || size.height > max_height) { + DRM_ERROR("%ux%u\n, exeeds max surface size %ux%u", + size.width, size.height, + max_width, max_height); + return -EINVAL; + } } else { const struct svga3d_surface_desc *desc; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_va.c b/drivers/gpu/drm/vmwgfx/vmwgfx_va.c new file mode 100644 index 000000000000..b4162fd78600 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_va.c @@ -0,0 +1,168 @@ +/************************************************************************** + * + * Copyright © 2012-2016 VMware, Inc., Palo Alto, CA., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "vmwgfx_drv.h" +#include "vmwgfx_resource_priv.h" + +/** + * struct vmw_stream - Overlay stream simple resource. + * @sres: The simple resource we derive from. + * @stream_id: The overlay stream id. + */ +struct vmw_stream { + struct vmw_simple_resource sres; + u32 stream_id; +}; + +/** + * vmw_stream - Typecast a struct vmw_resource to a struct vmw_stream. + * @res: Pointer to the struct vmw_resource. + * + * Returns: Returns a pointer to the struct vmw_stream. + */ +static struct vmw_stream * +vmw_stream(struct vmw_resource *res) +{ + return container_of(res, struct vmw_stream, sres.res); +} + +/*************************************************************************** + * Simple resource callbacks for struct vmw_stream + **************************************************************************/ +static void vmw_stream_hw_destroy(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct vmw_stream *stream = vmw_stream(res); + int ret; + + ret = vmw_overlay_unref(dev_priv, stream->stream_id); + WARN_ON_ONCE(ret != 0); +} + +static int vmw_stream_init(struct vmw_resource *res, void *data) +{ + struct vmw_stream *stream = vmw_stream(res); + + return vmw_overlay_claim(res->dev_priv, &stream->stream_id); +} + +static void vmw_stream_set_arg_handle(void *data, u32 handle) +{ + struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; + + arg->stream_id = handle; +} + +static const struct vmw_simple_resource_func va_stream_func = { + .res_func = { + .res_type = vmw_res_stream, + .needs_backup = false, + .may_evict = false, + .type_name = "overlay stream", + .backup_placement = NULL, + .create = NULL, + .destroy = NULL, + .bind = NULL, + .unbind = NULL + }, + .ttm_res_type = VMW_RES_STREAM, + .size = sizeof(struct vmw_stream), + .init = vmw_stream_init, + .hw_destroy = vmw_stream_hw_destroy, + .set_arg_handle = vmw_stream_set_arg_handle, +}; + +/*************************************************************************** + * End simple resource callbacks for struct vmw_stream + **************************************************************************/ + +/** + * vmw_stream_unref_ioctl - Ioctl to unreference a user-space handle to + * a struct vmw_stream. + * @dev: Pointer to the drm device. + * @data: The ioctl argument + * @file_priv: Pointer to a struct drm_file identifying the caller. + * + * Return: + * 0 if successful. + * Negative error value on failure. + */ +int vmw_stream_unref_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; + + return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, + arg->stream_id, TTM_REF_USAGE); +} + +/** + * vmw_stream_claim_ioctl - Ioctl to claim a struct vmw_stream overlay. + * @dev: Pointer to the drm device. + * @data: The ioctl argument + * @file_priv: Pointer to a struct drm_file identifying the caller. + * + * Return: + * 0 if successful. + * Negative error value on failure. + */ +int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return vmw_simple_resource_create_ioctl(dev, data, file_priv, + &va_stream_func); +} + +/** + * vmw_user_stream_lookup - Look up a struct vmw_user_stream from a handle. + * @dev_priv: Pointer to a struct vmw_private. + * @tfile: struct ttm_object_file identifying the caller. + * @inout_id: In: The user-space handle. Out: The stream id. + * @out: On output contains a refcounted pointer to the embedded + * struct vmw_resource. + * + * Return: + * 0 if successful. + * Negative error value on failure. + */ +int vmw_user_stream_lookup(struct vmw_private *dev_priv, + struct ttm_object_file *tfile, + uint32_t *inout_id, struct vmw_resource **out) +{ + struct vmw_stream *stream; + struct vmw_resource *res = + vmw_simple_resource_lookup(tfile, *inout_id, &va_stream_func); + + if (IS_ERR(res)) + return PTR_ERR(res); + + stream = vmw_stream(res); + *inout_id = stream->stream_id; + *out = res; + + return 0; +} diff --git a/include/uapi/drm/vmwgfx_drm.h b/include/uapi/drm/vmwgfx_drm.h index d325a4107916..d9dfde9aa757 100644 --- a/include/uapi/drm/vmwgfx_drm.h +++ b/include/uapi/drm/vmwgfx_drm.h @@ -41,6 +41,7 @@ extern "C" { #define DRM_VMW_GET_PARAM 0 #define DRM_VMW_ALLOC_DMABUF 1 #define DRM_VMW_UNREF_DMABUF 2 +#define DRM_VMW_HANDLE_CLOSE 2 #define DRM_VMW_CURSOR_BYPASS 3 /* guarded by DRM_VMW_PARAM_NUM_STREAMS != 0*/ #define DRM_VMW_CONTROL_STREAM 4 @@ -1092,6 +1093,29 @@ union drm_vmw_extended_context_arg { struct drm_vmw_context_arg rep; }; +/*************************************************************************/ +/* + * DRM_VMW_HANDLE_CLOSE - Close a user-space handle and release its + * underlying resource. + * + * Note that this ioctl is overlaid on the DRM_VMW_UNREF_DMABUF Ioctl. + * The ioctl arguments therefore need to be identical in layout. + * + */ + +/** + * struct drm_vmw_handle_close_arg + * + * @handle: Handle to close. + * + * Argument to the DRM_VMW_HANDLE_CLOSE Ioctl. + */ +struct drm_vmw_handle_close_arg { + __u32 handle; + __u32 pad64; +}; + + #if defined(__cplusplus) } #endif