diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_kms.c')
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 1357 |
1 files changed, 222 insertions, 1135 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index cd4925346ed4..05b1c54a070c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1,32 +1,16 @@ // SPDX-License-Identifier: GPL-2.0 OR MIT /************************************************************************** * - * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA - * - * 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. + * Copyright (c) 2009-2025 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * **************************************************************************/ + #include "vmwgfx_kms.h" #include "vmwgfx_bo.h" +#include "vmwgfx_resource_priv.h" +#include "vmwgfx_vkms.h" #include "vmw_surface_cache.h" #include <drm/drm_atomic.h> @@ -37,9 +21,16 @@ #include <drm/drm_sysfs.h> #include <drm/drm_edid.h> +void vmw_du_init(struct vmw_display_unit *du) +{ + vmw_vkms_crtc_init(&du->crtc); +} + void vmw_du_cleanup(struct vmw_display_unit *du) { struct vmw_private *dev_priv = vmw_priv(du->primary.dev); + + vmw_vkms_crtc_cleanup(&du->crtc); drm_plane_cleanup(&du->primary); if (vmw_cmd_supported(dev_priv)) drm_plane_cleanup(&du->cursor.base); @@ -50,471 +41,6 @@ void vmw_du_cleanup(struct vmw_display_unit *du) drm_connector_cleanup(&du->connector); } -/* - * Display Unit Cursor functions - */ - -static int vmw_du_cursor_plane_unmap_cm(struct vmw_plane_state *vps); -static void vmw_cursor_update_mob(struct vmw_private *dev_priv, - struct vmw_plane_state *vps, - u32 *image, u32 width, u32 height, - u32 hotspotX, u32 hotspotY); - -struct vmw_svga_fifo_cmd_define_cursor { - u32 cmd; - SVGAFifoCmdDefineAlphaCursor cursor; -}; - -/** - * vmw_send_define_cursor_cmd - queue a define cursor command - * @dev_priv: the private driver struct - * @image: buffer which holds the cursor image - * @width: width of the mouse cursor image - * @height: height of the mouse cursor image - * @hotspotX: the horizontal position of mouse hotspot - * @hotspotY: the vertical position of mouse hotspot - */ -static void vmw_send_define_cursor_cmd(struct vmw_private *dev_priv, - u32 *image, u32 width, u32 height, - u32 hotspotX, u32 hotspotY) -{ - struct vmw_svga_fifo_cmd_define_cursor *cmd; - const u32 image_size = width * height * sizeof(*image); - const u32 cmd_size = sizeof(*cmd) + image_size; - - /* Try to reserve fifocmd space and swallow any failures; - such reservations cannot be left unconsumed for long - under the risk of clogging other fifocmd users, so - we treat reservations separtely from the way we treat - other fallible KMS-atomic resources at prepare_fb */ - cmd = VMW_CMD_RESERVE(dev_priv, cmd_size); - - if (unlikely(!cmd)) - return; - - memset(cmd, 0, sizeof(*cmd)); - - memcpy(&cmd[1], image, image_size); - - cmd->cmd = SVGA_CMD_DEFINE_ALPHA_CURSOR; - cmd->cursor.id = 0; - cmd->cursor.width = width; - cmd->cursor.height = height; - cmd->cursor.hotspotX = hotspotX; - cmd->cursor.hotspotY = hotspotY; - - vmw_cmd_commit_flush(dev_priv, cmd_size); -} - -/** - * vmw_cursor_update_image - update the cursor image on the provided plane - * @dev_priv: the private driver struct - * @vps: the plane state of the cursor plane - * @image: buffer which holds the cursor image - * @width: width of the mouse cursor image - * @height: height of the mouse cursor image - * @hotspotX: the horizontal position of mouse hotspot - * @hotspotY: the vertical position of mouse hotspot - */ -static void vmw_cursor_update_image(struct vmw_private *dev_priv, - struct vmw_plane_state *vps, - u32 *image, u32 width, u32 height, - u32 hotspotX, u32 hotspotY) -{ - if (vps->cursor.bo) - vmw_cursor_update_mob(dev_priv, vps, image, - vps->base.crtc_w, vps->base.crtc_h, - hotspotX, hotspotY); - - else - vmw_send_define_cursor_cmd(dev_priv, image, width, height, - hotspotX, hotspotY); -} - - -/** - * vmw_cursor_update_mob - Update cursor vis CursorMob mechanism - * - * Called from inside vmw_du_cursor_plane_atomic_update to actually - * make the cursor-image live. - * - * @dev_priv: device to work with - * @vps: the plane state of the cursor plane - * @image: cursor source data to fill the MOB with - * @width: source data width - * @height: source data height - * @hotspotX: cursor hotspot x - * @hotspotY: cursor hotspot Y - */ -static void vmw_cursor_update_mob(struct vmw_private *dev_priv, - struct vmw_plane_state *vps, - u32 *image, u32 width, u32 height, - u32 hotspotX, u32 hotspotY) -{ - SVGAGBCursorHeader *header; - SVGAGBAlphaCursorHeader *alpha_header; - const u32 image_size = width * height * sizeof(*image); - - header = vmw_bo_map_and_cache(vps->cursor.bo); - alpha_header = &header->header.alphaHeader; - - memset(header, 0, sizeof(*header)); - - header->type = SVGA_ALPHA_CURSOR; - header->sizeInBytes = image_size; - - alpha_header->hotspotX = hotspotX; - alpha_header->hotspotY = hotspotY; - alpha_header->width = width; - alpha_header->height = height; - - memcpy(header + 1, image, image_size); - vmw_write(dev_priv, SVGA_REG_CURSOR_MOBID, - vps->cursor.bo->tbo.resource->start); -} - - -static u32 vmw_du_cursor_mob_size(u32 w, u32 h) -{ - return w * h * sizeof(u32) + sizeof(SVGAGBCursorHeader); -} - -/** - * vmw_du_cursor_plane_acquire_image -- Acquire the image data - * @vps: cursor plane state - */ -static u32 *vmw_du_cursor_plane_acquire_image(struct vmw_plane_state *vps) -{ - if (vps->surf) { - if (vps->surf_mapped) - return vmw_bo_map_and_cache(vps->surf->res.guest_memory_bo); - return vps->surf->snooper.image; - } else if (vps->bo) - return vmw_bo_map_and_cache(vps->bo); - return NULL; -} - -static bool vmw_du_cursor_plane_has_changed(struct vmw_plane_state *old_vps, - struct vmw_plane_state *new_vps) -{ - void *old_image; - void *new_image; - u32 size; - bool changed; - - if (old_vps->base.crtc_w != new_vps->base.crtc_w || - old_vps->base.crtc_h != new_vps->base.crtc_h) - return true; - - if (old_vps->cursor.hotspot_x != new_vps->cursor.hotspot_x || - old_vps->cursor.hotspot_y != new_vps->cursor.hotspot_y) - return true; - - size = new_vps->base.crtc_w * new_vps->base.crtc_h * sizeof(u32); - - old_image = vmw_du_cursor_plane_acquire_image(old_vps); - new_image = vmw_du_cursor_plane_acquire_image(new_vps); - - changed = false; - if (old_image && new_image) - changed = memcmp(old_image, new_image, size) != 0; - - return changed; -} - -static void vmw_du_destroy_cursor_mob(struct vmw_bo **vbo) -{ - if (!(*vbo)) - return; - - ttm_bo_unpin(&(*vbo)->tbo); - vmw_bo_unreference(vbo); -} - -static void vmw_du_put_cursor_mob(struct vmw_cursor_plane *vcp, - struct vmw_plane_state *vps) -{ - u32 i; - - if (!vps->cursor.bo) - return; - - vmw_du_cursor_plane_unmap_cm(vps); - - /* Look for a free slot to return this mob to the cache. */ - for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++) { - if (!vcp->cursor_mobs[i]) { - vcp->cursor_mobs[i] = vps->cursor.bo; - vps->cursor.bo = NULL; - return; - } - } - - /* Cache is full: See if this mob is bigger than an existing mob. */ - for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++) { - if (vcp->cursor_mobs[i]->tbo.base.size < - vps->cursor.bo->tbo.base.size) { - vmw_du_destroy_cursor_mob(&vcp->cursor_mobs[i]); - vcp->cursor_mobs[i] = vps->cursor.bo; - vps->cursor.bo = NULL; - return; - } - } - - /* Destroy it if it's not worth caching. */ - vmw_du_destroy_cursor_mob(&vps->cursor.bo); -} - -static int vmw_du_get_cursor_mob(struct vmw_cursor_plane *vcp, - struct vmw_plane_state *vps) -{ - struct vmw_private *dev_priv = vcp->base.dev->dev_private; - u32 size = vmw_du_cursor_mob_size(vps->base.crtc_w, vps->base.crtc_h); - u32 i; - u32 cursor_max_dim, mob_max_size; - struct vmw_fence_obj *fence = NULL; - int ret; - - if (!dev_priv->has_mob || - (dev_priv->capabilities2 & SVGA_CAP2_CURSOR_MOB) == 0) - return -EINVAL; - - mob_max_size = vmw_read(dev_priv, SVGA_REG_MOB_MAX_SIZE); - cursor_max_dim = vmw_read(dev_priv, SVGA_REG_CURSOR_MAX_DIMENSION); - - if (size > mob_max_size || vps->base.crtc_w > cursor_max_dim || - vps->base.crtc_h > cursor_max_dim) - return -EINVAL; - - if (vps->cursor.bo) { - if (vps->cursor.bo->tbo.base.size >= size) - return 0; - vmw_du_put_cursor_mob(vcp, vps); - } - - /* Look for an unused mob in the cache. */ - for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++) { - if (vcp->cursor_mobs[i] && - vcp->cursor_mobs[i]->tbo.base.size >= size) { - vps->cursor.bo = vcp->cursor_mobs[i]; - vcp->cursor_mobs[i] = NULL; - return 0; - } - } - /* Create a new mob if we can't find an existing one. */ - ret = vmw_bo_create_and_populate(dev_priv, size, - VMW_BO_DOMAIN_MOB, - &vps->cursor.bo); - - if (ret != 0) - return ret; - - /* Fence the mob creation so we are guarateed to have the mob */ - ret = ttm_bo_reserve(&vps->cursor.bo->tbo, false, false, NULL); - if (ret != 0) - goto teardown; - - ret = vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); - if (ret != 0) { - ttm_bo_unreserve(&vps->cursor.bo->tbo); - goto teardown; - } - - dma_fence_wait(&fence->base, false); - dma_fence_put(&fence->base); - - ttm_bo_unreserve(&vps->cursor.bo->tbo); - return 0; - -teardown: - vmw_du_destroy_cursor_mob(&vps->cursor.bo); - return ret; -} - - -static void vmw_cursor_update_position(struct vmw_private *dev_priv, - bool show, int x, int y) -{ - const uint32_t svga_cursor_on = show ? SVGA_CURSOR_ON_SHOW - : SVGA_CURSOR_ON_HIDE; - uint32_t count; - - spin_lock(&dev_priv->cursor_lock); - if (dev_priv->capabilities2 & SVGA_CAP2_EXTRA_REGS) { - vmw_write(dev_priv, SVGA_REG_CURSOR4_X, x); - vmw_write(dev_priv, SVGA_REG_CURSOR4_Y, y); - vmw_write(dev_priv, SVGA_REG_CURSOR4_SCREEN_ID, SVGA3D_INVALID_ID); - vmw_write(dev_priv, SVGA_REG_CURSOR4_ON, svga_cursor_on); - vmw_write(dev_priv, SVGA_REG_CURSOR4_SUBMIT, 1); - } else if (vmw_is_cursor_bypass3_enabled(dev_priv)) { - vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_ON, svga_cursor_on); - vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_X, x); - vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_Y, y); - count = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_CURSOR_COUNT); - vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_COUNT, ++count); - } else { - vmw_write(dev_priv, SVGA_REG_CURSOR_X, x); - vmw_write(dev_priv, SVGA_REG_CURSOR_Y, y); - vmw_write(dev_priv, SVGA_REG_CURSOR_ON, svga_cursor_on); - } - spin_unlock(&dev_priv->cursor_lock); -} - -void vmw_kms_cursor_snoop(struct vmw_surface *srf, - struct ttm_object_file *tfile, - struct ttm_buffer_object *bo, - SVGA3dCmdHeader *header) -{ - struct ttm_bo_kmap_obj map; - unsigned long kmap_offset; - unsigned long kmap_num; - SVGA3dCopyBox *box; - unsigned box_count; - void *virtual; - bool is_iomem; - struct vmw_dma_cmd { - SVGA3dCmdHeader header; - SVGA3dCmdSurfaceDMA dma; - } *cmd; - int i, ret; - const struct SVGA3dSurfaceDesc *desc = - vmw_surface_get_desc(VMW_CURSOR_SNOOP_FORMAT); - const u32 image_pitch = VMW_CURSOR_SNOOP_WIDTH * desc->pitchBytesPerBlock; - - cmd = container_of(header, struct vmw_dma_cmd, header); - - /* No snooper installed, nothing to copy */ - if (!srf->snooper.image) - return; - - if (cmd->dma.host.face != 0 || cmd->dma.host.mipmap != 0) { - DRM_ERROR("face and mipmap for cursors should never != 0\n"); - return; - } - - if (cmd->header.size < 64) { - DRM_ERROR("at least one full copy box must be given\n"); - return; - } - - box = (SVGA3dCopyBox *)&cmd[1]; - box_count = (cmd->header.size - sizeof(SVGA3dCmdSurfaceDMA)) / - sizeof(SVGA3dCopyBox); - - if (cmd->dma.guest.ptr.offset % PAGE_SIZE || - box->x != 0 || box->y != 0 || box->z != 0 || - box->srcx != 0 || box->srcy != 0 || box->srcz != 0 || - box->d != 1 || box_count != 1 || - box->w > VMW_CURSOR_SNOOP_WIDTH || box->h > VMW_CURSOR_SNOOP_HEIGHT) { - /* TODO handle none page aligned offsets */ - /* TODO handle more dst & src != 0 */ - /* TODO handle more then one copy */ - DRM_ERROR("Can't snoop dma request for cursor!\n"); - DRM_ERROR("(%u, %u, %u) (%u, %u, %u) (%ux%ux%u) %u %u\n", - box->srcx, box->srcy, box->srcz, - box->x, box->y, box->z, - box->w, box->h, box->d, box_count, - cmd->dma.guest.ptr.offset); - return; - } - - kmap_offset = cmd->dma.guest.ptr.offset >> PAGE_SHIFT; - kmap_num = (VMW_CURSOR_SNOOP_HEIGHT*image_pitch) >> PAGE_SHIFT; - - ret = ttm_bo_reserve(bo, true, false, NULL); - if (unlikely(ret != 0)) { - DRM_ERROR("reserve failed\n"); - return; - } - - ret = ttm_bo_kmap(bo, kmap_offset, kmap_num, &map); - if (unlikely(ret != 0)) - goto err_unreserve; - - virtual = ttm_kmap_obj_virtual(&map, &is_iomem); - - if (box->w == VMW_CURSOR_SNOOP_WIDTH && cmd->dma.guest.pitch == image_pitch) { - memcpy(srf->snooper.image, virtual, - VMW_CURSOR_SNOOP_HEIGHT*image_pitch); - } else { - /* Image is unsigned pointer. */ - for (i = 0; i < box->h; i++) - memcpy(srf->snooper.image + i * image_pitch, - virtual + i * cmd->dma.guest.pitch, - box->w * desc->pitchBytesPerBlock); - } - - srf->snooper.age++; - - ttm_bo_kunmap(&map); -err_unreserve: - ttm_bo_unreserve(bo); -} - -/** - * vmw_kms_legacy_hotspot_clear - Clear legacy hotspots - * - * @dev_priv: Pointer to the device private struct. - * - * Clears all legacy hotspots. - */ -void vmw_kms_legacy_hotspot_clear(struct vmw_private *dev_priv) -{ - struct drm_device *dev = &dev_priv->drm; - struct vmw_display_unit *du; - struct drm_crtc *crtc; - - drm_modeset_lock_all(dev); - drm_for_each_crtc(crtc, dev) { - du = vmw_crtc_to_du(crtc); - - du->hotspot_x = 0; - du->hotspot_y = 0; - } - drm_modeset_unlock_all(dev); -} - -void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv) -{ - struct drm_device *dev = &dev_priv->drm; - struct vmw_display_unit *du; - struct drm_crtc *crtc; - - mutex_lock(&dev->mode_config.mutex); - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - du = vmw_crtc_to_du(crtc); - if (!du->cursor_surface || - du->cursor_age == du->cursor_surface->snooper.age || - !du->cursor_surface->snooper.image) - continue; - - du->cursor_age = du->cursor_surface->snooper.age; - vmw_send_define_cursor_cmd(dev_priv, - du->cursor_surface->snooper.image, - VMW_CURSOR_SNOOP_WIDTH, - VMW_CURSOR_SNOOP_HEIGHT, - du->hotspot_x + du->core_hotspot_x, - du->hotspot_y + du->core_hotspot_y); - } - - mutex_unlock(&dev->mode_config.mutex); -} - - -void vmw_du_cursor_plane_destroy(struct drm_plane *plane) -{ - struct vmw_cursor_plane *vcp = vmw_plane_to_vcp(plane); - u32 i; - - vmw_cursor_update_position(plane->dev->dev_private, false, 0, 0); - - for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++) - vmw_du_destroy_cursor_mob(&vcp->cursor_mobs[i]); - - drm_plane_cleanup(plane); -} - void vmw_du_primary_plane_destroy(struct drm_plane *plane) { @@ -528,22 +54,16 @@ void vmw_du_primary_plane_destroy(struct drm_plane *plane) * vmw_du_plane_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) +void vmw_du_plane_unpin_surf(struct vmw_plane_state *vps) { - if (vps->surf) { + struct vmw_surface *surf = vmw_user_object_surface(&vps->uo); + + if (surf) { if (vps->pinned) { - vmw_resource_unpin(&vps->surf->res); + vmw_resource_unpin(&surf->res); vps->pinned--; } - - if (unreference) { - if (vps->pinned) - DRM_ERROR("Surface still pinned\n"); - vmw_surface_unreference(&vps->surf); - } } } @@ -564,257 +84,7 @@ vmw_du_plane_cleanup_fb(struct drm_plane *plane, { struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); - vmw_du_plane_unpin_surf(vps, false); -} - - -/** - * vmw_du_cursor_plane_map_cm - Maps the cursor mobs. - * - * @vps: plane_state - * - * Returns 0 on success - */ - -static int -vmw_du_cursor_plane_map_cm(struct vmw_plane_state *vps) -{ - int ret; - u32 size = vmw_du_cursor_mob_size(vps->base.crtc_w, vps->base.crtc_h); - struct ttm_buffer_object *bo; - - if (!vps->cursor.bo) - return -EINVAL; - - bo = &vps->cursor.bo->tbo; - - if (bo->base.size < size) - return -EINVAL; - - if (vps->cursor.bo->map.virtual) - return 0; - - ret = ttm_bo_reserve(bo, false, false, NULL); - if (unlikely(ret != 0)) - return -ENOMEM; - - vmw_bo_map_and_cache(vps->cursor.bo); - - ttm_bo_unreserve(bo); - - if (unlikely(ret != 0)) - return -ENOMEM; - - return 0; -} - - -/** - * vmw_du_cursor_plane_unmap_cm - Unmaps the cursor mobs. - * - * @vps: state of the cursor plane - * - * Returns 0 on success - */ - -static int -vmw_du_cursor_plane_unmap_cm(struct vmw_plane_state *vps) -{ - int ret = 0; - struct vmw_bo *vbo = vps->cursor.bo; - - if (!vbo || !vbo->map.virtual) - return 0; - - ret = ttm_bo_reserve(&vbo->tbo, true, false, NULL); - if (likely(ret == 0)) { - vmw_bo_unmap(vbo); - ttm_bo_unreserve(&vbo->tbo); - } - - return ret; -} - - -/** - * vmw_du_cursor_plane_cleanup_fb - Unpins the plane surface - * - * @plane: cursor plane - * @old_state: contains the state to clean up - * - * Unmaps all cursor bo mappings and unpins the cursor surface - * - * Returns 0 on success - */ -void -vmw_du_cursor_plane_cleanup_fb(struct drm_plane *plane, - struct drm_plane_state *old_state) -{ - struct vmw_cursor_plane *vcp = vmw_plane_to_vcp(plane); - struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); - - if (vps->surf_mapped) { - vmw_bo_unmap(vps->surf->res.guest_memory_bo); - vps->surf_mapped = false; - } - - vmw_du_cursor_plane_unmap_cm(vps); - vmw_du_put_cursor_mob(vcp, vps); - - vmw_du_plane_unpin_surf(vps, false); - - if (vps->surf) { - vmw_surface_unreference(&vps->surf); - vps->surf = NULL; - } - - if (vps->bo) { - vmw_bo_unreference(&vps->bo); - vps->bo = NULL; - } -} - - -/** - * 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_cursor_plane *vcp = vmw_plane_to_vcp(plane); - struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); - int ret = 0; - - if (vps->surf) { - if (vps->surf_mapped) { - vmw_bo_unmap(vps->surf->res.guest_memory_bo); - vps->surf_mapped = false; - } - vmw_surface_unreference(&vps->surf); - vps->surf = NULL; - } - - if (vps->bo) { - vmw_bo_unreference(&vps->bo); - vps->bo = NULL; - } - - if (fb) { - if (vmw_framebuffer_to_vfb(fb)->bo) { - vps->bo = vmw_framebuffer_to_vfbd(fb)->buffer; - vmw_bo_reference(vps->bo); - } else { - vps->surf = vmw_framebuffer_to_vfbs(fb)->surface; - vmw_surface_reference(vps->surf); - } - } - - if (!vps->surf && vps->bo) { - const u32 size = new_state->crtc_w * new_state->crtc_h * sizeof(u32); - - /* - * Not using vmw_bo_map_and_cache() helper here as we need to - * reserve the ttm_buffer_object first which - * vmw_bo_map_and_cache() omits. - */ - ret = ttm_bo_reserve(&vps->bo->tbo, true, false, NULL); - - if (unlikely(ret != 0)) - return -ENOMEM; - - ret = ttm_bo_kmap(&vps->bo->tbo, 0, PFN_UP(size), &vps->bo->map); - - ttm_bo_unreserve(&vps->bo->tbo); - - if (unlikely(ret != 0)) - return -ENOMEM; - } else if (vps->surf && !vps->bo && vps->surf->res.guest_memory_bo) { - - WARN_ON(vps->surf->snooper.image); - ret = ttm_bo_reserve(&vps->surf->res.guest_memory_bo->tbo, true, false, - NULL); - if (unlikely(ret != 0)) - return -ENOMEM; - vmw_bo_map_and_cache(vps->surf->res.guest_memory_bo); - ttm_bo_unreserve(&vps->surf->res.guest_memory_bo->tbo); - vps->surf_mapped = true; - } - - if (vps->surf || vps->bo) { - vmw_du_get_cursor_mob(vcp, vps); - vmw_du_cursor_plane_map_cm(vps); - } - - return 0; -} - - -void -vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, - struct drm_atomic_state *state) -{ - struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, - plane); - struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, - plane); - struct drm_crtc *crtc = new_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(new_state); - struct vmw_plane_state *old_vps = vmw_plane_state_to_vps(old_state); - s32 hotspot_x, hotspot_y; - - hotspot_x = du->hotspot_x + new_state->hotspot_x; - hotspot_y = du->hotspot_y + new_state->hotspot_y; - - du->cursor_surface = vps->surf; - du->cursor_bo = vps->bo; - - if (!vps->surf && !vps->bo) { - vmw_cursor_update_position(dev_priv, false, 0, 0); - return; - } - - vps->cursor.hotspot_x = hotspot_x; - vps->cursor.hotspot_y = hotspot_y; - - if (vps->surf) { - du->cursor_age = du->cursor_surface->snooper.age; - } - - if (!vmw_du_cursor_plane_has_changed(old_vps, vps)) { - /* - * If it hasn't changed, avoid making the device do extra - * work by keeping the old cursor active. - */ - struct vmw_cursor_plane_state tmp = old_vps->cursor; - old_vps->cursor = vps->cursor; - vps->cursor = tmp; - } else { - void *image = vmw_du_cursor_plane_acquire_image(vps); - if (image) - vmw_cursor_update_image(dev_priv, vps, image, - new_state->crtc_w, - new_state->crtc_h, - hotspot_x, hotspot_y); - } - - du->cursor_x = new_state->crtc_x + du->set_gui_x; - du->cursor_y = new_state->crtc_y + du->set_gui_y; - - vmw_cursor_update_position(dev_priv, true, - du->cursor_x + hotspot_x, - du->cursor_y + hotspot_y); - - du->core_hotspot_x = hotspot_x - du->hotspot_x; - du->core_hotspot_y = hotspot_y - du->hotspot_y; + vmw_du_plane_unpin_surf(vps); } @@ -858,81 +128,13 @@ int vmw_du_primary_plane_atomic_check(struct drm_plane *plane, DRM_PLANE_NO_SCALING, DRM_PLANE_NO_SCALING, false, true); - - if (!ret && new_fb) { - struct drm_crtc *crtc = new_state->crtc; - struct vmw_display_unit *du = vmw_crtc_to_du(crtc); - - vmw_connector_state_to_vcs(du->connector.state); - } - - 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_atomic_state *state) -{ - struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, - plane); - int ret = 0; - struct drm_crtc_state *crtc_state = NULL; - struct vmw_surface *surface = NULL; - struct drm_framebuffer *fb = new_state->fb; - - if (new_state->crtc) - crtc_state = drm_atomic_get_new_crtc_state(new_state->state, - new_state->crtc); - - ret = drm_atomic_helper_check_plane_state(new_state, crtc_state, - DRM_PLANE_NO_SCALING, - DRM_PLANE_NO_SCALING, - true, true); - if (ret) - return ret; - - /* Turning off */ - if (!fb) - return 0; - - /* 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); - return -EINVAL; - } - - if (!vmw_framebuffer_to_vfb(fb)->bo) { - surface = vmw_framebuffer_to_vfbs(fb)->surface; - - WARN_ON(!surface); - - if (!surface || - (!surface->snooper.image && !surface->res.guest_memory_bo)) { - DRM_ERROR("surface not suitable for cursor\n"); - return -EINVAL; - } - } - - return 0; -} - - int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) { + struct vmw_private *vmw = vmw_priv(crtc->dev); struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state, crtc); struct vmw_display_unit *du = vmw_crtc_to_du(new_state->crtc); @@ -940,9 +142,13 @@ int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, bool has_primary = new_state->plane_mask & drm_plane_mask(crtc->primary); - /* We always want to have an active plane with an active CRTC */ - if (has_primary != new_state->enable) - return -EINVAL; + /* + * This is fine in general, but broken userspace might expect + * some actual rendering so give a clue as why it's blank. + */ + if (new_state->enable && !has_primary) + drm_dbg_driver(&vmw->drm, + "CRTC without a primary plane will be blank.\n"); if (new_state->connector_mask != connector_mask && @@ -965,15 +171,9 @@ int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_atomic_state *state) { + vmw_vkms_crtc_atomic_begin(crtc, state); } - -void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc, - struct drm_atomic_state *state) -{ -} - - /** * vmw_du_crtc_duplicate_state - duplicate crtc state * @crtc: DRM crtc @@ -1074,15 +274,10 @@ vmw_du_plane_duplicate_state(struct drm_plane *plane) vps->pinned = 0; vps->cpp = 0; - memset(&vps->cursor, 0, sizeof(vps->cursor)); + vps->cursor.mob = NULL; /* Each ref counted resource needs to be acquired again */ - if (vps->surf) - (void) vmw_surface_reference(vps->surf); - - if (vps->bo) - (void) vmw_bo_reference(vps->bo); - + vmw_user_object_ref(&vps->uo); state = &vps->base; __drm_atomic_helper_plane_duplicate_state(plane, state); @@ -1131,11 +326,7 @@ vmw_du_plane_destroy_state(struct drm_plane *plane, struct vmw_plane_state *vps = vmw_plane_state_to_vps(state); /* Should have been freed by cleanup_fb */ - if (vps->surf) - vmw_surface_unreference(&vps->surf); - - if (vps->bo) - vmw_bo_unreference(&vps->bo); + vmw_user_object_unref(&vps->uo); drm_atomic_helper_plane_destroy_state(plane, state); } @@ -1228,9 +419,22 @@ static void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer) { struct vmw_framebuffer_surface *vfbs = vmw_framebuffer_to_vfbs(framebuffer); + struct vmw_bo *bo = vmw_user_object_buffer(&vfbs->uo); + struct vmw_surface *surf = vmw_user_object_surface(&vfbs->uo); + if (bo) { + vmw_bo_dirty_release(bo); + /* + * bo->dirty is reference counted so it being NULL + * means that the surface wasn't coherent to begin + * with and so we have to free the dirty tracker + * in the vmw_resource + */ + if (!bo->dirty && surf && surf->res.dirty) + surf->res.func->dirty_free(&surf->res); + } drm_framebuffer_cleanup(framebuffer); - vmw_surface_unreference(&vfbs->surface); + vmw_user_object_unref(&vfbs->uo); kfree(vfbs); } @@ -1275,29 +479,42 @@ int vmw_kms_readback(struct vmw_private *dev_priv, return -ENOSYS; } +static int vmw_framebuffer_surface_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + struct vmw_framebuffer_surface *vfbs = vmw_framebuffer_to_vfbs(fb); + struct vmw_bo *bo = vmw_user_object_buffer(&vfbs->uo); + + if (WARN_ON(!bo)) + return -EINVAL; + return drm_gem_handle_create(file_priv, &bo->tbo.base, handle); +} static const struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = { + .create_handle = vmw_framebuffer_surface_create_handle, .destroy = vmw_framebuffer_surface_destroy, .dirty = drm_atomic_helper_dirtyfb, }; static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, - struct vmw_surface *surface, + struct vmw_user_object *uo, struct vmw_framebuffer **out, const struct drm_mode_fb_cmd2 - *mode_cmd, - bool is_bo_proxy) + *mode_cmd) { struct drm_device *dev = &dev_priv->drm; struct vmw_framebuffer_surface *vfbs; - enum SVGA3dSurfaceFormat format; + struct vmw_surface *surface; int ret; /* 3D is only supported on HWv8 and newer hosts */ if (dev_priv->active_display_unit == vmw_du_legacy) return -ENOSYS; + surface = vmw_user_object_surface(uo); + /* * Sanity checks. */ @@ -1325,34 +542,6 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, return -EINVAL; } - switch (mode_cmd->pixel_format) { - case DRM_FORMAT_ARGB8888: - format = SVGA3D_A8R8G8B8; - break; - case DRM_FORMAT_XRGB8888: - format = SVGA3D_X8R8G8B8; - break; - case DRM_FORMAT_RGB565: - format = SVGA3D_R5G6B5; - break; - case DRM_FORMAT_XRGB1555: - format = SVGA3D_A1R5G5B5; - break; - default: - DRM_ERROR("Invalid pixel format: %p4cc\n", - &mode_cmd->pixel_format); - return -EINVAL; - } - - /* - * For DX, surface format validation is done when surface->scanout - * is set. - */ - if (!has_sm4_context(dev_priv) && format != surface->metadata.format) { - DRM_ERROR("Invalid surface format for requested mode.\n"); - return -EINVAL; - } - vfbs = kzalloc(sizeof(*vfbs), GFP_KERNEL); if (!vfbs) { ret = -ENOMEM; @@ -1360,9 +549,8 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, } drm_helper_mode_fill_fb_struct(dev, &vfbs->base.base, mode_cmd); - vfbs->surface = vmw_surface_reference(surface); - vfbs->base.user_handle = mode_cmd->handles[0]; - vfbs->is_bo_proxy = is_bo_proxy; + memcpy(&vfbs->uo, uo, sizeof(vfbs->uo)); + vmw_user_object_ref(&vfbs->uo); *out = &vfbs->base; @@ -1374,7 +562,7 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, return 0; out_err2: - vmw_surface_unreference(&surface); + vmw_user_object_unref(&vfbs->uo); kfree(vfbs); out_err1: return ret; @@ -1390,7 +578,6 @@ static int vmw_framebuffer_bo_create_handle(struct drm_framebuffer *fb, { struct vmw_framebuffer_bo *vfbd = vmw_framebuffer_to_vfbd(fb); - return drm_gem_handle_create(file_priv, &vfbd->buffer->tbo.base, handle); } @@ -1399,6 +586,7 @@ static void vmw_framebuffer_bo_destroy(struct drm_framebuffer *framebuffer) struct vmw_framebuffer_bo *vfbd = vmw_framebuffer_to_vfbd(framebuffer); + vmw_bo_dirty_release(vfbd->buffer); drm_framebuffer_cleanup(framebuffer); vmw_bo_unreference(&vfbd->buffer); @@ -1411,86 +599,6 @@ static const struct drm_framebuffer_funcs vmw_framebuffer_bo_funcs = { .dirty = drm_atomic_helper_dirtyfb, }; -/** - * vmw_create_bo_proxy - create a proxy surface for the buffer object - * - * @dev: DRM device - * @mode_cmd: parameters for the new surface - * @bo_mob: MOB backing the buffer object - * @srf_out: newly created surface - * - * When the content FB is a buffer object, we create a surface as a proxy to the - * same buffer. This way we can do a surface copy rather than a surface DMA. - * This is a more efficient approach - * - * RETURNS: - * 0 on success, error code otherwise - */ -static int vmw_create_bo_proxy(struct drm_device *dev, - const struct drm_mode_fb_cmd2 *mode_cmd, - struct vmw_bo *bo_mob, - struct vmw_surface **srf_out) -{ - struct vmw_surface_metadata metadata = {0}; - uint32_t format; - struct vmw_resource *res; - unsigned int bytes_pp; - int ret; - - switch (mode_cmd->pixel_format) { - case DRM_FORMAT_ARGB8888: - case DRM_FORMAT_XRGB8888: - format = SVGA3D_X8R8G8B8; - bytes_pp = 4; - break; - - case DRM_FORMAT_RGB565: - case DRM_FORMAT_XRGB1555: - format = SVGA3D_R5G6B5; - bytes_pp = 2; - break; - - case 8: - format = SVGA3D_P8; - bytes_pp = 1; - break; - - default: - DRM_ERROR("Invalid framebuffer format %p4cc\n", - &mode_cmd->pixel_format); - return -EINVAL; - } - - metadata.format = format; - metadata.mip_levels[0] = 1; - metadata.num_sizes = 1; - metadata.base_size.width = mode_cmd->pitches[0] / bytes_pp; - metadata.base_size.height = mode_cmd->height; - metadata.base_size.depth = 1; - metadata.scanout = true; - - ret = vmw_gb_surface_define(vmw_priv(dev), &metadata, srf_out); - if (ret) { - DRM_ERROR("Failed to allocate proxy content buffer\n"); - return ret; - } - - res = &(*srf_out)->res; - - /* Reserve and switch the backing mob. */ - mutex_lock(&res->dev_priv->cmdbuf_mutex); - (void) vmw_resource_reserve(res, false, true); - vmw_user_bo_unref(&res->guest_memory_bo); - res->guest_memory_bo = vmw_user_bo_ref(bo_mob); - res->guest_memory_offset = 0; - vmw_resource_unreserve(res, false, false, false, NULL, 0); - mutex_unlock(&res->dev_priv->cmdbuf_mutex); - - return 0; -} - - - static int vmw_kms_new_framebuffer_bo(struct vmw_private *dev_priv, struct vmw_bo *bo, struct vmw_framebuffer **out, @@ -1529,7 +637,6 @@ static int vmw_kms_new_framebuffer_bo(struct vmw_private *dev_priv, drm_helper_mode_fill_fb_struct(dev, &vfbd->base.base, mode_cmd); vfbd->base.bo = true; vfbd->buffer = vmw_bo_reference(bo); - vfbd->base.user_handle = mode_cmd->handles[0]; *out = &vfbd->base; ret = drm_framebuffer_init(dev, &vfbd->base.base, @@ -1570,55 +677,24 @@ vmw_kms_srf_ok(struct vmw_private *dev_priv, uint32_t width, uint32_t height) * vmw_kms_new_framebuffer - Create a new framebuffer. * * @dev_priv: Pointer to device private struct. - * @bo: Pointer to buffer object to wrap the kms framebuffer around. - * Either @bo or @surface must be NULL. - * @surface: Pointer to a surface to wrap the kms framebuffer around. - * Either @bo or @surface must be NULL. - * @only_2d: No presents will occur to this buffer object based framebuffer. - * This helps the code to do some important optimizations. + * @uo: Pointer to user object to wrap the kms framebuffer around. + * Either the buffer or surface inside the user object must be NULL. * @mode_cmd: Frame-buffer metadata. */ struct vmw_framebuffer * vmw_kms_new_framebuffer(struct vmw_private *dev_priv, - struct vmw_bo *bo, - struct vmw_surface *surface, - bool only_2d, + struct vmw_user_object *uo, const struct drm_mode_fb_cmd2 *mode_cmd) { struct vmw_framebuffer *vfb = NULL; - bool is_bo_proxy = false; int ret; - /* - * We cannot use the SurfaceDMA command in an non-accelerated VM, - * therefore, wrap the buffer object in a surface so we can use the - * SurfaceCopy command. - */ - if (vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height) && - bo && only_2d && - mode_cmd->width > 64 && /* Don't create a proxy for cursor */ - dev_priv->active_display_unit == vmw_du_screen_target) { - ret = vmw_create_bo_proxy(&dev_priv->drm, mode_cmd, - bo, &surface); - if (ret) - return ERR_PTR(ret); - - is_bo_proxy = true; - } - /* Create the new framebuffer depending one what we have */ - if (surface) { - ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb, - mode_cmd, - is_bo_proxy); - /* - * vmw_create_bo_proxy() adds a reference that is no longer - * needed - */ - if (is_bo_proxy) - vmw_surface_unreference(&surface); - } else if (bo) { - ret = vmw_kms_new_framebuffer_bo(dev_priv, bo, &vfb, + if (vmw_user_object_surface(uo)) { + ret = vmw_kms_new_framebuffer_surface(dev_priv, uo, &vfb, + mode_cmd); + } else if (uo->buffer) { + ret = vmw_kms_new_framebuffer_bo(dev_priv, uo->buffer, &vfb, mode_cmd); } else { BUG(); @@ -1640,14 +716,14 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, { struct vmw_private *dev_priv = vmw_priv(dev); struct vmw_framebuffer *vfb = NULL; - struct vmw_surface *surface = NULL; - struct vmw_bo *bo = NULL; + struct vmw_user_object uo = {0}; + struct vmw_bo *bo; + struct vmw_surface *surface; int ret; /* returns either a bo or surface */ - ret = vmw_user_lookup_handle(dev_priv, file_priv, - mode_cmd->handles[0], - &surface, &bo); + ret = vmw_user_object_lookup(dev_priv, file_priv, mode_cmd->handles[0], + &uo); if (ret) { DRM_ERROR("Invalid buffer object handle %u (0x%x).\n", mode_cmd->handles[0], mode_cmd->handles[0]); @@ -1655,35 +731,41 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, } - if (!bo && + if (vmw_user_object_surface(&uo) && !vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height)) { DRM_ERROR("Surface size cannot exceed %dx%d\n", dev_priv->texture_max_width, dev_priv->texture_max_height); + ret = -EINVAL; goto err_out; } - vfb = vmw_kms_new_framebuffer(dev_priv, bo, surface, - !(dev_priv->capabilities & SVGA_CAP_3D), - mode_cmd); + vfb = vmw_kms_new_framebuffer(dev_priv, &uo, mode_cmd); if (IS_ERR(vfb)) { ret = PTR_ERR(vfb); goto err_out; } err_out: - /* vmw_user_lookup_handle takes one ref so does new_fb */ - if (bo) - vmw_user_bo_unref(&bo); - if (surface) - vmw_surface_unreference(&surface); + bo = vmw_user_object_buffer(&uo); + surface = vmw_user_object_surface(&uo); + /* vmw_user_object_lookup takes one ref so does new_fb */ + vmw_user_object_unref(&uo); if (ret) { DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret); return ERR_PTR(ret); } + ttm_bo_reserve(&bo->tbo, false, false, NULL); + ret = vmw_bo_dirty_add(bo); + if (!ret && surface && surface->res.func->dirty_alloc) { + surface->res.coherent = true; + ret = surface->res.func->dirty_alloc(&surface->res); + } + ttm_bo_unreserve(&bo->tbo); + return &vfb->base; } @@ -2040,6 +1122,29 @@ vmw_kms_create_hotplug_mode_update_property(struct vmw_private *dev_priv) "hotplug_mode_update", 0, 1); } +static void +vmw_atomic_commit_tail(struct drm_atomic_state *old_state) +{ + struct vmw_private *vmw = vmw_priv(old_state->dev); + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state; + int i; + + drm_atomic_helper_commit_tail(old_state); + + if (vmw->vkms_enabled) { + for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) { + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + (void)old_crtc_state; + flush_work(&du->vkms.crc_generator_work); + } + } +} + +static const struct drm_mode_config_helper_funcs vmw_mode_config_helpers = { + .atomic_commit_tail = vmw_atomic_commit_tail, +}; + int vmw_kms_init(struct vmw_private *dev_priv) { struct drm_device *dev = &dev_priv->drm; @@ -2059,6 +1164,7 @@ int vmw_kms_init(struct vmw_private *dev_priv) dev->mode_config.max_width = dev_priv->texture_max_width; dev->mode_config.max_height = dev_priv->texture_max_height; dev->mode_config.preferred_depth = dev_priv->assume_16bpp ? 16 : 32; + dev->mode_config.helper_private = &vmw_mode_config_helpers; drm_mode_create_suggested_offset_properties(dev); vmw_kms_create_hotplug_mode_update_property(dev_priv); @@ -2092,44 +1198,6 @@ int vmw_kms_close(struct vmw_private *dev_priv) return ret; } -int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_vmw_cursor_bypass_arg *arg = data; - struct vmw_display_unit *du; - struct drm_crtc *crtc; - int ret = 0; - - mutex_lock(&dev->mode_config.mutex); - if (arg->flags & DRM_VMW_CURSOR_BYPASS_ALL) { - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - du = vmw_crtc_to_du(crtc); - du->hotspot_x = arg->xhot; - du->hotspot_y = arg->yhot; - } - - mutex_unlock(&dev->mode_config.mutex); - return 0; - } - - crtc = drm_crtc_find(dev, file_priv, arg->crtc_id); - if (!crtc) { - ret = -ENOENT; - goto out; - } - - du = vmw_crtc_to_du(crtc); - - du->hotspot_x = arg->xhot; - du->hotspot_y = arg->yhot; - -out: - mutex_unlock(&dev->mode_config.mutex); - - return ret; -} - int vmw_kms_write_svga(struct vmw_private *vmw_priv, unsigned width, unsigned height, unsigned pitch, unsigned bpp, unsigned depth) @@ -2152,13 +1220,12 @@ int vmw_kms_write_svga(struct vmw_private *vmw_priv, return 0; } +static bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, - uint32_t pitch, - uint32_t height) + u64 pitch, + u64 height) { - return ((u64) pitch * (u64) height) < (u64) - ((dev_priv->active_display_unit == vmw_du_screen_target) ? - dev_priv->max_primary_mem : dev_priv->vram_size); + return (pitch * height) < (u64)dev_priv->vram_size; } /** @@ -2328,7 +1395,7 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, struct drm_mode_config *mode_config = &dev->mode_config; struct drm_vmw_update_layout_arg *arg = (struct drm_vmw_update_layout_arg *)data; - void __user *user_rects; + const void __user *user_rects; struct drm_vmw_rect *rects; struct drm_rect *drm_rects; unsigned rects_size; @@ -2340,6 +1407,8 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, VMWGFX_MIN_INITIAL_HEIGHT}; vmw_du_update_layout(dev_priv, 1, &def_rect); return 0; + } else if (arg->num_outputs > VMWGFX_NUM_DISPLAY_UNITS) { + return -E2BIG; } rects_size = arg->num_outputs * sizeof(struct drm_vmw_rect); @@ -2567,72 +1636,6 @@ void vmw_kms_helper_validation_finish(struct vmw_private *dev_priv, } /** - * vmw_kms_update_proxy - Helper function to update a proxy surface from - * its backing MOB. - * - * @res: Pointer to the surface resource - * @clips: Clip rects in framebuffer (surface) space. - * @num_clips: Number of clips in @clips. - * @increment: Integer with which to increment the clip counter when looping. - * Used to skip a predetermined number of clip rects. - * - * This function makes sure the proxy surface is updated from its backing MOB - * using the region given by @clips. The surface resource @res and its backing - * MOB needs to be reserved and validated on call. - */ -int vmw_kms_update_proxy(struct vmw_resource *res, - const struct drm_clip_rect *clips, - unsigned num_clips, - int increment) -{ - struct vmw_private *dev_priv = res->dev_priv; - struct drm_vmw_size *size = &vmw_res_to_srf(res)->metadata.base_size; - struct { - SVGA3dCmdHeader header; - SVGA3dCmdUpdateGBImage body; - } *cmd; - SVGA3dBox *box; - size_t copy_size = 0; - int i; - - if (!clips) - return 0; - - cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd) * num_clips); - if (!cmd) - return -ENOMEM; - - for (i = 0; i < num_clips; ++i, clips += increment, ++cmd) { - box = &cmd->body.box; - - cmd->header.id = SVGA_3D_CMD_UPDATE_GB_IMAGE; - cmd->header.size = sizeof(cmd->body); - cmd->body.image.sid = res->id; - cmd->body.image.face = 0; - cmd->body.image.mipmap = 0; - - if (clips->x1 > size->width || clips->x2 > size->width || - clips->y1 > size->height || clips->y2 > size->height) { - DRM_ERROR("Invalid clips outsize of framebuffer.\n"); - return -EINVAL; - } - - box->x = clips->x1; - box->y = clips->y1; - box->z = 0; - box->w = clips->x2 - clips->x1; - box->h = clips->y2 - clips->y1; - box->d = 1; - - copy_size += sizeof(*cmd); - } - - vmw_cmd_commit(dev_priv, copy_size); - - return 0; -} - -/** * vmw_kms_create_implicit_placement_property - Set up the implicit placement * property. * @@ -2766,8 +1769,9 @@ int vmw_du_helper_plane_update(struct vmw_du_update_plane *update) } else { struct vmw_framebuffer_surface *vfbs = container_of(update->vfb, typeof(*vfbs), base); + struct vmw_surface *surf = vmw_user_object_surface(&vfbs->uo); - ret = vmw_validation_add_resource(&val_ctx, &vfbs->surface->res, + ret = vmw_validation_add_resource(&val_ctx, &surf->res, 0, VMW_RES_DIRTY_NONE, NULL, NULL); } @@ -2852,27 +1856,20 @@ out_unref: * Returns MODE_OK on success, or a drm_mode_status error code. */ enum drm_mode_status vmw_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) + const struct drm_display_mode *mode) { + enum drm_mode_status ret; struct drm_device *dev = connector->dev; struct vmw_private *dev_priv = vmw_priv(dev); - u32 max_width = dev_priv->texture_max_width; - u32 max_height = dev_priv->texture_max_height; u32 assumed_cpp = 4; if (dev_priv->assume_16bpp) assumed_cpp = 2; - if (dev_priv->active_display_unit == vmw_du_screen_target) { - max_width = min(dev_priv->stdu_max_width, max_width); - max_height = min(dev_priv->stdu_max_height, max_height); - } - - if (max_width < mode->hdisplay) - return MODE_BAD_HVALUE; - - if (max_height < mode->vdisplay) - return MODE_BAD_VVALUE; + ret = drm_mode_validate_size(mode, dev_priv->texture_max_width, + dev_priv->texture_max_height); + if (ret != MODE_OK) + return ret; if (!vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * assumed_cpp, @@ -2930,3 +1927,93 @@ int vmw_connector_get_modes(struct drm_connector *connector) return num_modes; } + +struct vmw_user_object *vmw_user_object_ref(struct vmw_user_object *uo) +{ + if (uo->buffer) + vmw_user_bo_ref(uo->buffer); + else if (uo->surface) + vmw_surface_reference(uo->surface); + return uo; +} + +void vmw_user_object_unref(struct vmw_user_object *uo) +{ + if (uo->buffer) + vmw_user_bo_unref(&uo->buffer); + else if (uo->surface) + vmw_surface_unreference(&uo->surface); +} + +struct vmw_bo * +vmw_user_object_buffer(struct vmw_user_object *uo) +{ + if (uo->buffer) + return uo->buffer; + else if (uo->surface) + return uo->surface->res.guest_memory_bo; + return NULL; +} + +struct vmw_surface * +vmw_user_object_surface(struct vmw_user_object *uo) +{ + if (uo->buffer) + return uo->buffer->dumb_surface; + return uo->surface; +} + +void *vmw_user_object_map(struct vmw_user_object *uo) +{ + struct vmw_bo *bo = vmw_user_object_buffer(uo); + + WARN_ON(!bo); + return vmw_bo_map_and_cache(bo); +} + +void *vmw_user_object_map_size(struct vmw_user_object *uo, size_t size) +{ + struct vmw_bo *bo = vmw_user_object_buffer(uo); + + WARN_ON(!bo); + return vmw_bo_map_and_cache_size(bo, size); +} + +void vmw_user_object_unmap(struct vmw_user_object *uo) +{ + struct vmw_bo *bo = vmw_user_object_buffer(uo); + int ret; + + WARN_ON(!bo); + + /* Fence the mob creation so we are guarateed to have the mob */ + ret = ttm_bo_reserve(&bo->tbo, false, false, NULL); + if (ret != 0) + return; + + vmw_bo_unmap(bo); + vmw_bo_pin_reserved(bo, false); + + ttm_bo_unreserve(&bo->tbo); +} + +bool vmw_user_object_is_mapped(struct vmw_user_object *uo) +{ + struct vmw_bo *bo; + + if (!uo || vmw_user_object_is_null(uo)) + return false; + + bo = vmw_user_object_buffer(uo); + + if (WARN_ON(!bo)) + return false; + + WARN_ON(bo->map.bo && !bo->map.virtual); + return bo->map.virtual; +} + +bool vmw_user_object_is_null(struct vmw_user_object *uo) +{ + return !uo->buffer && !uo->surface; +} |