diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx')
23 files changed, 1135 insertions, 1131 deletions
diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile index 46a4ab688a7f..b168fd7fe9b3 100644 --- a/drivers/gpu/drm/vmwgfx/Makefile +++ b/drivers/gpu/drm/vmwgfx/Makefile @@ -10,6 +10,6 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \ vmwgfx_simple_resource.o vmwgfx_va.o vmwgfx_blit.o \ vmwgfx_validation.o vmwgfx_page_dirty.o vmwgfx_streamoutput.o \ vmwgfx_devcaps.o ttm_object.o vmwgfx_system_manager.o \ - vmwgfx_gem.o vmwgfx_vkms.o + vmwgfx_gem.o vmwgfx_vkms.o vmwgfx_cursor_plane.o obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c index 64bd7d74854e..fa5841fda659 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c @@ -429,7 +429,7 @@ static void *map_external(struct vmw_bo *bo, struct iosys_map *map) void *ptr = NULL; int ret; - if (bo->tbo.base.import_attach) { + if (drm_gem_is_imported(&bo->tbo.base)) { ret = dma_buf_vmap(bo->tbo.base.dma_buf, map); if (ret) { drm_dbg_driver(&vmw->drm, @@ -447,7 +447,7 @@ out: static void unmap_external(struct vmw_bo *bo, struct iosys_map *map) { - if (bo->tbo.base.import_attach) + if (drm_gem_is_imported(&bo->tbo.base)) dma_buf_vunmap(bo->tbo.base.dma_buf, map); else vmw_bo_unmap(bo); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c index 9b5b8c1f063b..f031a312c783 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c @@ -36,8 +36,7 @@ static void vmw_bo_release(struct vmw_bo *vbo) { struct vmw_resource *res; - WARN_ON(vbo->tbo.base.funcs && - kref_read(&vbo->tbo.base.refcount) != 0); + WARN_ON(kref_read(&vbo->tbo.base.refcount) != 0); vmw_bo_unmap(vbo); xa_destroy(&vbo->detached_resources); @@ -51,11 +50,13 @@ static void vmw_bo_release(struct vmw_bo *vbo) mutex_lock(&res->dev_priv->cmdbuf_mutex); (void)vmw_resource_reserve(res, false, true); vmw_resource_mob_detach(res); + if (res->dirty) + res->func->dirty_free(res); if (res->coherent) vmw_bo_dirty_release(res->guest_memory_bo); res->guest_memory_bo = NULL; res->guest_memory_offset = 0; - vmw_resource_unreserve(res, false, false, false, NULL, + vmw_resource_unreserve(res, true, false, false, NULL, 0); mutex_unlock(&res->dev_priv->cmdbuf_mutex); } @@ -73,9 +74,9 @@ static void vmw_bo_free(struct ttm_buffer_object *bo) { struct vmw_bo *vbo = to_vmw_bo(&bo->base); - WARN_ON(vbo->dirty); WARN_ON(!RB_EMPTY_ROOT(&vbo->res_tree)); vmw_bo_release(vbo); + WARN_ON(vbo->dirty); kfree(vbo); } @@ -467,6 +468,7 @@ int vmw_bo_create(struct vmw_private *vmw, if (unlikely(ret != 0)) goto out_error; + (*p_bo)->tbo.base.funcs = &vmw_gem_object_funcs; return ret; out_error: *p_bo = NULL; @@ -848,9 +850,9 @@ void vmw_bo_placement_set_default_accelerated(struct vmw_bo *bo) vmw_bo_placement_set(bo, domain, domain); } -void vmw_bo_add_detached_resource(struct vmw_bo *vbo, struct vmw_resource *res) +int vmw_bo_add_detached_resource(struct vmw_bo *vbo, struct vmw_resource *res) { - xa_store(&vbo->detached_resources, (unsigned long)res, res, GFP_KERNEL); + return xa_err(xa_store(&vbo->detached_resources, (unsigned long)res, res, GFP_KERNEL)); } void vmw_bo_del_detached_resource(struct vmw_bo *vbo, struct vmw_resource *res) @@ -887,3 +889,9 @@ out: surf = vmw_res_to_srf(res); return surf; } + +s32 vmw_bo_mobid(struct vmw_bo *vbo) +{ + WARN_ON(vbo->tbo.resource->mem_type != VMW_PL_MOB); + return (s32)vbo->tbo.resource->start; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.h b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.h index 11e330c7c7f5..cf84a163bfcb 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.h @@ -141,7 +141,7 @@ void vmw_bo_move_notify(struct ttm_buffer_object *bo, struct ttm_resource *mem); void vmw_bo_swap_notify(struct ttm_buffer_object *bo); -void vmw_bo_add_detached_resource(struct vmw_bo *vbo, struct vmw_resource *res); +int vmw_bo_add_detached_resource(struct vmw_bo *vbo, struct vmw_resource *res); void vmw_bo_del_detached_resource(struct vmw_bo *vbo, struct vmw_resource *res); struct vmw_surface *vmw_bo_surface(struct vmw_bo *vbo); @@ -204,12 +204,12 @@ static inline void vmw_bo_unreference(struct vmw_bo **buf) *buf = NULL; if (tmp_buf) - ttm_bo_put(&tmp_buf->tbo); + drm_gem_object_put(&tmp_buf->tbo.base); } static inline struct vmw_bo *vmw_bo_reference(struct vmw_bo *buf) { - ttm_bo_get(&buf->tbo); + drm_gem_object_get(&buf->tbo.base); return buf; } @@ -233,4 +233,6 @@ static inline struct vmw_bo *to_vmw_bo(struct drm_gem_object *gobj) return container_of((gobj), struct vmw_bo, tbo.base); } +s32 vmw_bo_mobid(struct vmw_bo *vbo); + #endif // VMWGFX_BO_H diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c index a7c07692262b..98331c4c0335 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c @@ -432,7 +432,7 @@ static int vmw_cotable_resize(struct vmw_resource *res, size_t new_size) * for the new COTable. Initially pin the buffer object to make sure * we can use tryreserve without failure. */ - ret = vmw_gem_object_create(dev_priv, &bo_params, &buf); + ret = vmw_bo_create(dev_priv, &bo_params, &buf); if (ret) { DRM_ERROR("Failed initializing new cotable MOB.\n"); goto out_done; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c new file mode 100644 index 000000000000..718832b08d96 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c @@ -0,0 +1,844 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright (c) 2024-2025 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + * + **************************************************************************/ +#include "vmwgfx_cursor_plane.h" + +#include "vmwgfx_bo.h" +#include "vmwgfx_drv.h" +#include "vmwgfx_kms.h" +#include "vmwgfx_resource_priv.h" +#include "vmw_surface_cache.h" + +#include "drm/drm_atomic.h" +#include "drm/drm_atomic_helper.h" +#include "drm/drm_plane.h" +#include <asm/page.h> + +#define VMW_CURSOR_SNOOP_FORMAT SVGA3D_A8R8G8B8 +#define VMW_CURSOR_SNOOP_WIDTH 64 +#define VMW_CURSOR_SNOOP_HEIGHT 64 + +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); +} + +static void +vmw_cursor_plane_update_legacy(struct vmw_private *vmw, + struct vmw_plane_state *vps) +{ + struct vmw_surface *surface = vmw_user_object_surface(&vps->uo); + s32 hotspot_x = vps->cursor.legacy.hotspot_x + vps->base.hotspot_x; + s32 hotspot_y = vps->cursor.legacy.hotspot_y + vps->base.hotspot_y; + + if (WARN_ON(!surface || !surface->snooper.image)) + return; + + if (vps->cursor.legacy.id != surface->snooper.id) { + vmw_send_define_cursor_cmd(vmw, surface->snooper.image, + vps->base.crtc_w, vps->base.crtc_h, + hotspot_x, hotspot_y); + vps->cursor.legacy.id = surface->snooper.id; + } +} + +static enum vmw_cursor_update_type +vmw_cursor_update_type(struct vmw_private *vmw, struct vmw_plane_state *vps) +{ + struct vmw_surface *surface = vmw_user_object_surface(&vps->uo); + + if (surface && surface->snooper.image) + return VMW_CURSOR_UPDATE_LEGACY; + + if (vmw->has_mob) { + if ((vmw->capabilities2 & SVGA_CAP2_CURSOR_MOB) != 0) + return VMW_CURSOR_UPDATE_MOB; + } + + return VMW_CURSOR_UPDATE_NONE; +} + +static void vmw_cursor_update_mob(struct vmw_private *vmw, + struct vmw_plane_state *vps) +{ + SVGAGBCursorHeader *header; + SVGAGBAlphaCursorHeader *alpha_header; + struct vmw_bo *bo = vmw_user_object_buffer(&vps->uo); + u32 *image = vmw_bo_map_and_cache(bo); + const u32 image_size = vps->base.crtc_w * vps->base.crtc_h * sizeof(*image); + + header = vmw_bo_map_and_cache(vps->cursor.mob); + alpha_header = &header->header.alphaHeader; + + memset(header, 0, sizeof(*header)); + + header->type = SVGA_ALPHA_CURSOR; + header->sizeInBytes = image_size; + + alpha_header->hotspotX = vps->cursor.legacy.hotspot_x + vps->base.hotspot_x; + alpha_header->hotspotY = vps->cursor.legacy.hotspot_y + vps->base.hotspot_y; + alpha_header->width = vps->base.crtc_w; + alpha_header->height = vps->base.crtc_h; + + memcpy(header + 1, image, image_size); + vmw_write(vmw, SVGA_REG_CURSOR_MOBID, vmw_bo_mobid(vps->cursor.mob)); + + vmw_bo_unmap(bo); + vmw_bo_unmap(vps->cursor.mob); +} + +static u32 vmw_cursor_mob_size(enum vmw_cursor_update_type update_type, + u32 w, u32 h) +{ + switch (update_type) { + case VMW_CURSOR_UPDATE_LEGACY: + case VMW_CURSOR_UPDATE_NONE: + return 0; + case VMW_CURSOR_UPDATE_MOB: + return w * h * sizeof(u32) + sizeof(SVGAGBCursorHeader); + } + return 0; +} + +static void vmw_cursor_mob_destroy(struct vmw_bo **vbo) +{ + if (!(*vbo)) + return; + + ttm_bo_unpin(&(*vbo)->tbo); + vmw_bo_unreference(vbo); +} + +/** + * vmw_cursor_mob_unmap - Unmaps the cursor mobs. + * + * @vps: state of the cursor plane + * + * Returns 0 on success + */ + +static int +vmw_cursor_mob_unmap(struct vmw_plane_state *vps) +{ + int ret = 0; + struct vmw_bo *vbo = vps->cursor.mob; + + 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; +} + +static void vmw_cursor_mob_put(struct vmw_cursor_plane *vcp, + struct vmw_plane_state *vps) +{ + u32 i; + + if (!vps->cursor.mob) + return; + + vmw_cursor_mob_unmap(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.mob; + vps->cursor.mob = 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.mob->tbo.base.size) { + vmw_cursor_mob_destroy(&vcp->cursor_mobs[i]); + vcp->cursor_mobs[i] = vps->cursor.mob; + vps->cursor.mob = NULL; + return; + } + } + + /* Destroy it if it's not worth caching. */ + vmw_cursor_mob_destroy(&vps->cursor.mob); +} + +static int vmw_cursor_mob_get(struct vmw_cursor_plane *vcp, + struct vmw_plane_state *vps) +{ + struct vmw_private *dev_priv = vmw_priv(vcp->base.dev); + u32 size = vmw_cursor_mob_size(vps->cursor.update_type, + 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.mob) { + if (vps->cursor.mob->tbo.base.size >= size) + return 0; + vmw_cursor_mob_put(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.mob = 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.mob); + + if (ret != 0) + return ret; + + /* Fence the mob creation so we are guarateed to have the mob */ + ret = ttm_bo_reserve(&vps->cursor.mob->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.mob->tbo); + goto teardown; + } + + dma_fence_wait(&fence->base, false); + dma_fence_put(&fence->base); + + ttm_bo_unreserve(&vps->cursor.mob->tbo); + + return 0; + +teardown: + vmw_cursor_mob_destroy(&vps->cursor.mob); + return ret; +} + +static void vmw_cursor_update_position(struct vmw_private *dev_priv, + bool show, int x, int y) +{ + const u32 svga_cursor_on = show ? SVGA_CURSOR_ON_SHOW + : SVGA_CURSOR_ON_HIDE; + u32 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; + u32 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.id++; + + ttm_bo_kunmap(&map); +err_unreserve: + ttm_bo_unreserve(bo); +} + +void vmw_cursor_plane_destroy(struct drm_plane *plane) +{ + struct vmw_cursor_plane *vcp = vmw_plane_to_vcp(plane); + u32 i; + + vmw_cursor_update_position(vmw_priv(plane->dev), false, 0, 0); + + for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++) + vmw_cursor_mob_destroy(&vcp->cursor_mobs[i]); + + drm_plane_cleanup(plane); +} + +/** + * vmw_cursor_mob_map - Maps the cursor mobs. + * + * @vps: plane_state + * + * Returns 0 on success + */ + +static int +vmw_cursor_mob_map(struct vmw_plane_state *vps) +{ + int ret; + u32 size = vmw_cursor_mob_size(vps->cursor.update_type, + vps->base.crtc_w, vps->base.crtc_h); + struct vmw_bo *vbo = vps->cursor.mob; + + if (!vbo) + return -EINVAL; + + if (vbo->tbo.base.size < size) + return -EINVAL; + + if (vbo->map.virtual) + return 0; + + ret = ttm_bo_reserve(&vbo->tbo, false, false, NULL); + if (unlikely(ret != 0)) + return -ENOMEM; + + vmw_bo_map_and_cache(vbo); + + ttm_bo_unreserve(&vbo->tbo); + + return 0; +} + +/** + * vmw_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_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 (!vmw_user_object_is_null(&vps->uo)) + vmw_user_object_unmap(&vps->uo); + + vmw_cursor_mob_unmap(vps); + vmw_cursor_mob_put(vcp, vps); + + vmw_du_plane_unpin_surf(vps); + vmw_user_object_unref(&vps->uo); +} + +static bool +vmw_cursor_buffer_changed(struct vmw_plane_state *new_vps, + struct vmw_plane_state *old_vps) +{ + struct vmw_bo *new_bo = vmw_user_object_buffer(&new_vps->uo); + struct vmw_bo *old_bo = vmw_user_object_buffer(&old_vps->uo); + struct vmw_surface *surf; + bool dirty = false; + int ret; + + if (new_bo != old_bo) + return true; + + if (new_bo) { + if (!old_bo) { + return true; + } else if (new_bo->dirty) { + vmw_bo_dirty_scan(new_bo); + dirty = vmw_bo_is_dirty(new_bo); + if (dirty) { + surf = vmw_user_object_surface(&new_vps->uo); + if (surf) + vmw_bo_dirty_transfer_to_res(&surf->res); + else + vmw_bo_dirty_clear(new_bo); + } + return dirty; + } else if (new_bo != old_bo) { + /* + * Currently unused because the top exits right away. + * In most cases buffer being different will mean + * that the contents is different. For the few percent + * of cases where that's not true the cost of doing + * the memcmp on all other seems to outweight the + * benefits. Leave the conditional to be able to + * trivially validate it by removing the initial + * if (new_bo != old_bo) at the start. + */ + void *old_image; + void *new_image; + bool changed = false; + struct ww_acquire_ctx ctx; + const u32 size = new_vps->base.crtc_w * + new_vps->base.crtc_h * sizeof(u32); + + ww_acquire_init(&ctx, &reservation_ww_class); + + ret = ttm_bo_reserve(&old_bo->tbo, false, false, &ctx); + if (ret != 0) { + ww_acquire_fini(&ctx); + return true; + } + + ret = ttm_bo_reserve(&new_bo->tbo, false, false, &ctx); + if (ret != 0) { + ttm_bo_unreserve(&old_bo->tbo); + ww_acquire_fini(&ctx); + return true; + } + + old_image = vmw_bo_map_and_cache(old_bo); + new_image = vmw_bo_map_and_cache(new_bo); + + if (old_image && new_image && old_image != new_image) + changed = memcmp(old_image, new_image, size) != + 0; + + ttm_bo_unreserve(&new_bo->tbo); + ttm_bo_unreserve(&old_bo->tbo); + + ww_acquire_fini(&ctx); + + return changed; + } + return false; + } + + return false; +} + +static bool +vmw_cursor_plane_changed(struct vmw_plane_state *new_vps, + struct vmw_plane_state *old_vps) +{ + 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->base.hotspot_x != new_vps->base.hotspot_x || + old_vps->base.hotspot_y != new_vps->base.hotspot_y) + return true; + + if (old_vps->cursor.legacy.hotspot_x != + new_vps->cursor.legacy.hotspot_x || + old_vps->cursor.legacy.hotspot_y != + new_vps->cursor.legacy.hotspot_y) + return true; + + if (old_vps->base.fb != new_vps->base.fb) + return true; + + return false; +} + +/** + * vmw_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_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); + struct vmw_plane_state *old_vps = vmw_plane_state_to_vps(plane->state); + struct vmw_private *vmw = vmw_priv(plane->dev); + struct vmw_bo *bo = NULL; + struct vmw_surface *surface; + int ret = 0; + + if (!vmw_user_object_is_null(&vps->uo)) { + vmw_user_object_unmap(&vps->uo); + vmw_user_object_unref(&vps->uo); + } + + if (fb) { + if (vmw_framebuffer_to_vfb(fb)->bo) { + vps->uo.buffer = vmw_framebuffer_to_vfbd(fb)->buffer; + vps->uo.surface = NULL; + } else { + memcpy(&vps->uo, &vmw_framebuffer_to_vfbs(fb)->uo, sizeof(vps->uo)); + } + vmw_user_object_ref(&vps->uo); + } + + vps->cursor.update_type = vmw_cursor_update_type(vmw, vps); + switch (vps->cursor.update_type) { + case VMW_CURSOR_UPDATE_LEGACY: + surface = vmw_user_object_surface(&vps->uo); + if (!surface || vps->cursor.legacy.id == surface->snooper.id) + vps->cursor.update_type = VMW_CURSOR_UPDATE_NONE; + break; + case VMW_CURSOR_UPDATE_MOB: { + bo = vmw_user_object_buffer(&vps->uo); + if (bo) { + struct ttm_operation_ctx ctx = { false, false }; + + ret = ttm_bo_reserve(&bo->tbo, true, false, NULL); + if (ret != 0) + return -ENOMEM; + + ret = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); + if (ret != 0) + return -ENOMEM; + + /* + * vmw_bo_pin_reserved also validates, so to skip + * the extra validation use ttm_bo_pin directly + */ + if (!bo->tbo.pin_count) + ttm_bo_pin(&bo->tbo); + + if (vmw_framebuffer_to_vfb(fb)->bo) { + const u32 size = new_state->crtc_w * + new_state->crtc_h * + sizeof(u32); + + (void)vmw_bo_map_and_cache_size(bo, size); + } else { + vmw_bo_map_and_cache(bo); + } + ttm_bo_unreserve(&bo->tbo); + } + if (!vmw_user_object_is_null(&vps->uo)) { + if (!vmw_cursor_plane_changed(vps, old_vps) && + !vmw_cursor_buffer_changed(vps, old_vps)) { + vps->cursor.update_type = + VMW_CURSOR_UPDATE_NONE; + } else { + vmw_cursor_mob_get(vcp, vps); + vmw_cursor_mob_map(vps); + } + } + } + break; + case VMW_CURSOR_UPDATE_NONE: + /* do nothing */ + break; + } + + return 0; +} + +/** + * vmw_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_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); + struct vmw_private *vmw = vmw_priv(plane->dev); + int ret = 0; + struct drm_crtc_state *crtc_state = NULL; + struct vmw_surface *surface = NULL; + struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); + enum vmw_cursor_update_type update_type; + 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; + + update_type = vmw_cursor_update_type(vmw, vps); + if (update_type == VMW_CURSOR_UPDATE_LEGACY) { + if (new_state->crtc_w != VMW_CURSOR_SNOOP_WIDTH || + new_state->crtc_h != VMW_CURSOR_SNOOP_HEIGHT) { + drm_warn(&vmw->drm, + "Invalid cursor dimensions (%d, %d)\n", + new_state->crtc_w, new_state->crtc_h); + return -EINVAL; + } + surface = vmw_user_object_surface(&vps->uo); + if (!surface || !surface->snooper.image) { + drm_warn(&vmw->drm, + "surface not suitable for cursor\n"); + return -EINVAL; + } + } + + return 0; +} + +void +vmw_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(plane->dev); + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); + s32 hotspot_x, hotspot_y, cursor_x, cursor_y; + + /* + * Hide the cursor if the new bo is null + */ + if (vmw_user_object_is_null(&vps->uo)) { + vmw_cursor_update_position(dev_priv, false, 0, 0); + return; + } + + switch (vps->cursor.update_type) { + case VMW_CURSOR_UPDATE_LEGACY: + vmw_cursor_plane_update_legacy(dev_priv, vps); + break; + case VMW_CURSOR_UPDATE_MOB: + vmw_cursor_update_mob(dev_priv, vps); + break; + case VMW_CURSOR_UPDATE_NONE: + /* do nothing */ + break; + } + + /* + * For all update types update the cursor position + */ + cursor_x = new_state->crtc_x + du->set_gui_x; + cursor_y = new_state->crtc_y + du->set_gui_y; + + hotspot_x = vps->cursor.legacy.hotspot_x + new_state->hotspot_x; + hotspot_y = vps->cursor.legacy.hotspot_y + new_state->hotspot_y; + + vmw_cursor_update_position(dev_priv, true, cursor_x + hotspot_x, + cursor_y + hotspot_y); +} + +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 vmw_plane_state *vps; + 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); + vps = vmw_plane_state_to_vps(du->cursor.base.state); + vps->cursor.legacy.hotspot_x = arg->xhot; + vps->cursor.legacy.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); + vps = vmw_plane_state_to_vps(du->cursor.base.state); + vps->cursor.legacy.hotspot_x = arg->xhot; + vps->cursor.legacy.hotspot_y = arg->yhot; + +out: + mutex_unlock(&dev->mode_config.mutex); + + return ret; +} + +void *vmw_cursor_snooper_create(struct drm_file *file_priv, + struct vmw_surface_metadata *metadata) +{ + if (!file_priv->atomic && metadata->scanout && + metadata->num_sizes == 1 && + metadata->sizes[0].width == VMW_CURSOR_SNOOP_WIDTH && + metadata->sizes[0].height == VMW_CURSOR_SNOOP_HEIGHT && + metadata->format == VMW_CURSOR_SNOOP_FORMAT) { + const struct SVGA3dSurfaceDesc *desc = + vmw_surface_get_desc(VMW_CURSOR_SNOOP_FORMAT); + const u32 cursor_size_bytes = VMW_CURSOR_SNOOP_WIDTH * + VMW_CURSOR_SNOOP_HEIGHT * + desc->pitchBytesPerBlock; + void *image = kzalloc(cursor_size_bytes, GFP_KERNEL); + + if (!image) { + DRM_ERROR("Failed to allocate cursor_image\n"); + return ERR_PTR(-ENOMEM); + } + return image; + } + return NULL; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.h b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.h new file mode 100644 index 000000000000..40694925a70e --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/************************************************************************** + * + * Copyright (c) 2024-2025 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + * + **************************************************************************/ + +#ifndef VMWGFX_CURSOR_PLANE_H +#define VMWGFX_CURSOR_PLANE_H + +#include "device_include/svga3d_cmd.h" +#include "drm/drm_file.h" +#include "drm/drm_fourcc.h" +#include "drm/drm_plane.h" + +#include <linux/types.h> + +struct SVGA3dCmdHeader; +struct ttm_buffer_object; +struct vmw_bo; +struct vmw_cursor; +struct vmw_private; +struct vmw_surface; +struct vmw_user_object; + +#define vmw_plane_to_vcp(x) container_of(x, struct vmw_cursor_plane, base) + +static const u32 __maybe_unused vmw_cursor_plane_formats[] = { + DRM_FORMAT_ARGB8888, +}; + +enum vmw_cursor_update_type { + VMW_CURSOR_UPDATE_NONE = 0, + VMW_CURSOR_UPDATE_LEGACY, + VMW_CURSOR_UPDATE_MOB, +}; + +struct vmw_cursor_plane_state { + enum vmw_cursor_update_type update_type; + bool changed; + bool surface_changed; + struct vmw_bo *mob; + struct { + s32 hotspot_x; + s32 hotspot_y; + u32 id; + } legacy; +}; + +/** + * Derived class for cursor plane object + * + * @base DRM plane object + * @cursor.cursor_mobs Cursor mobs available for re-use + */ +struct vmw_cursor_plane { + struct drm_plane base; + + struct vmw_bo *cursor_mobs[3]; +}; + +struct vmw_surface_metadata; +void *vmw_cursor_snooper_create(struct drm_file *file_priv, + struct vmw_surface_metadata *metadata); +void vmw_cursor_cmd_dma_snoop(SVGA3dCmdHeader *header, + struct vmw_surface *srf, + struct ttm_buffer_object *bo); + +void vmw_cursor_plane_destroy(struct drm_plane *plane); + +int vmw_cursor_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state); +void vmw_cursor_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state); +int vmw_cursor_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state); +void vmw_cursor_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state); + +#endif /* VMWGFX_CURSOR_H */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 0f32471c8533..0695a342b1ef 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -1,31 +1,11 @@ // 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_drv.h" #include "vmwgfx_bo.h" @@ -1324,9 +1304,6 @@ static void vmw_master_set(struct drm_device *dev, static void vmw_master_drop(struct drm_device *dev, struct drm_file *file_priv) { - struct vmw_private *dev_priv = vmw_priv(dev); - - vmw_kms_legacy_hotspot_clear(dev_priv); } bool vmwgfx_supported(struct vmw_private *vmw) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 5275ef632d4b..594af8eb04c6 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -1,29 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 OR MIT */ /************************************************************************** * - * Copyright (c) 2009-2024 Broadcom. All Rights Reserved. The term + * Copyright (c) 2009-2025 Broadcom. All Rights Reserved. The term * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * - * 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. - * **************************************************************************/ #ifndef _VMWGFX_DRV_H_ @@ -58,7 +38,7 @@ #define VMWGFX_DRIVER_NAME "vmwgfx" #define VMWGFX_DRIVER_MAJOR 2 -#define VMWGFX_DRIVER_MINOR 20 +#define VMWGFX_DRIVER_MINOR 21 #define VMWGFX_DRIVER_PATCHLEVEL 0 #define VMWGFX_FIFO_STATIC_SIZE (1024*1024) #define VMWGFX_NUM_DISPLAY_UNITS 8 @@ -100,10 +80,6 @@ #define VMW_RES_SHADER ttm_driver_type4 #define VMW_RES_HT_ORDER 12 -#define VMW_CURSOR_SNOOP_FORMAT SVGA3D_A8R8G8B8 -#define VMW_CURSOR_SNOOP_WIDTH 64 -#define VMW_CURSOR_SNOOP_HEIGHT 64 - #define MKSSTAT_CAPACITY_LOG2 5U #define MKSSTAT_CAPACITY (1U << MKSSTAT_CAPACITY_LOG2) @@ -201,7 +177,7 @@ enum vmw_cmdbuf_res_type { struct vmw_cmdbuf_res_manager; struct vmw_cursor_snooper { - size_t age; + size_t id; uint32_t *image; }; @@ -846,9 +822,7 @@ static inline bool vmw_resource_mob_attached(const struct vmw_resource *res) * GEM related functionality - vmwgfx_gem.c */ struct vmw_bo_params; -int vmw_gem_object_create(struct vmw_private *vmw, - struct vmw_bo_params *params, - struct vmw_bo **p_vbo); +extern const struct drm_gem_object_funcs vmw_gem_object_funcs; extern int vmw_gem_object_create_with_handle(struct vmw_private *dev_priv, struct drm_file *filp, uint32_t size, @@ -1050,7 +1024,6 @@ int vmw_kms_init(struct vmw_private *dev_priv); int vmw_kms_close(struct vmw_private *dev_priv); int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv); void vmw_kms_cursor_snoop(struct vmw_surface *srf, struct ttm_object_file *tfile, struct ttm_buffer_object *bo, @@ -1067,7 +1040,6 @@ int vmw_kms_present(struct vmw_private *dev_priv, uint32_t num_clips); int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -void vmw_kms_legacy_hotspot_clear(struct vmw_private *dev_priv); int vmw_kms_suspend(struct drm_device *dev); int vmw_kms_resume(struct drm_device *dev); void vmw_kms_lost_device(struct drm_device *dev); @@ -1393,8 +1365,10 @@ int vmw_mksstat_remove_all(struct vmw_private *dev_priv); DRM_DEBUG_DRIVER(fmt, ##__VA_ARGS__) /* Resource dirtying - vmwgfx_page_dirty.c */ +bool vmw_bo_is_dirty(struct vmw_bo *vbo); void vmw_bo_dirty_scan(struct vmw_bo *vbo); int vmw_bo_dirty_add(struct vmw_bo *vbo); +void vmw_bo_dirty_clear(struct vmw_bo *vbo); void vmw_bo_dirty_transfer_to_res(struct vmw_resource *res); void vmw_bo_dirty_clear_res(struct vmw_resource *res); void vmw_bo_dirty_release(struct vmw_bo *vbo); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 2e52d73eba48..e831e324e737 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -1,29 +1,11 @@ // 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_binding.h" #include "vmwgfx_bo.h" #include "vmwgfx_drv.h" @@ -4086,6 +4068,23 @@ static int vmw_execbuf_tie_context(struct vmw_private *dev_priv, return 0; } +/* + * DMA fence callback to remove a seqno_waiter + */ +struct seqno_waiter_rm_context { + struct dma_fence_cb base; + struct vmw_private *dev_priv; +}; + +static void seqno_waiter_rm_cb(struct dma_fence *f, struct dma_fence_cb *cb) +{ + struct seqno_waiter_rm_context *ctx = + container_of(cb, struct seqno_waiter_rm_context, base); + + vmw_seqno_waiter_remove(ctx->dev_priv); + kfree(ctx); +} + int vmw_execbuf_process(struct drm_file *file_priv, struct vmw_private *dev_priv, void __user *user_commands, void *kernel_commands, @@ -4266,6 +4265,15 @@ int vmw_execbuf_process(struct drm_file *file_priv, } else { /* Link the fence with the FD created earlier */ fd_install(out_fence_fd, sync_file->file); + struct seqno_waiter_rm_context *ctx = + kmalloc(sizeof(*ctx), GFP_KERNEL); + ctx->dev_priv = dev_priv; + vmw_seqno_waiter_add(dev_priv); + if (dma_fence_add_callback(&fence->base, &ctx->base, + seqno_waiter_rm_cb) < 0) { + vmw_seqno_waiter_remove(dev_priv); + kfree(ctx); + } } } @@ -4512,8 +4520,6 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data, if (unlikely(ret != 0)) goto out; - vmw_kms_cursor_post_execbuf(dev_priv); - out: if (in_fence) dma_fence_put(in_fence); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c index ed5015ced392..c55382167c1b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c @@ -84,11 +84,11 @@ static int vmw_gem_vmap(struct drm_gem_object *obj, struct iosys_map *map) struct ttm_buffer_object *bo = drm_gem_ttm_of_gem(obj); int ret; - if (obj->import_attach) { - ret = dma_buf_vmap(obj->import_attach->dmabuf, map); + if (drm_gem_is_imported(obj)) { + ret = dma_buf_vmap(obj->dma_buf, map); if (!ret) { if (drm_WARN_ON(obj->dev, map->is_iomem)) { - dma_buf_vunmap(obj->import_attach->dmabuf, map); + dma_buf_vunmap(obj->dma_buf, map); return -EIO; } } @@ -101,8 +101,8 @@ static int vmw_gem_vmap(struct drm_gem_object *obj, struct iosys_map *map) static void vmw_gem_vunmap(struct drm_gem_object *obj, struct iosys_map *map) { - if (obj->import_attach) - dma_buf_vunmap(obj->import_attach->dmabuf, map); + if (drm_gem_is_imported(obj)) + dma_buf_vunmap(obj->dma_buf, map); else drm_gem_ttm_vunmap(obj, map); } @@ -111,7 +111,7 @@ static int vmw_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) { int ret; - if (obj->import_attach) { + if (drm_gem_is_imported(obj)) { /* * Reset both vm_ops and vm_private_data, so we don't end up with * vm_ops pointing to our implementation if the dma-buf backend @@ -140,7 +140,7 @@ static const struct vm_operations_struct vmw_vm_ops = { .close = ttm_bo_vm_close, }; -static const struct drm_gem_object_funcs vmw_gem_object_funcs = { +const struct drm_gem_object_funcs vmw_gem_object_funcs = { .free = vmw_gem_object_free, .open = vmw_gem_object_open, .close = vmw_gem_object_close, @@ -154,20 +154,6 @@ static const struct drm_gem_object_funcs vmw_gem_object_funcs = { .vm_ops = &vmw_vm_ops, }; -int vmw_gem_object_create(struct vmw_private *vmw, - struct vmw_bo_params *params, - struct vmw_bo **p_vbo) -{ - int ret = vmw_bo_create(vmw, params, p_vbo); - - if (ret != 0) - goto out_no_bo; - - (*p_vbo)->tbo.base.funcs = &vmw_gem_object_funcs; -out_no_bo: - return ret; -} - int vmw_gem_object_create_with_handle(struct vmw_private *dev_priv, struct drm_file *filp, uint32_t size, @@ -183,7 +169,7 @@ int vmw_gem_object_create_with_handle(struct vmw_private *dev_priv, .pin = false }; - ret = vmw_gem_object_create(dev_priv, ¶ms, p_vbo); + ret = vmw_bo_create(dev_priv, ¶ms, p_vbo); if (ret != 0) goto out_no_bo; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 800a79e035ed..05b1c54a070c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1,33 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 OR MIT /************************************************************************** * - * Copyright (c) 2009-2024 Broadcom. All Rights Reserved. The term + * Copyright (c) 2009-2025 Broadcom. All Rights Reserved. The term * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * - * 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_kms.h" #include "vmwgfx_bo.h" +#include "vmwgfx_resource_priv.h" #include "vmwgfx_vkms.h" #include "vmw_surface_cache.h" @@ -59,474 +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) -{ - struct vmw_surface *surf; - - if (vmw_user_object_is_null(&vps->uo)) - return NULL; - - surf = vmw_user_object_surface(&vps->uo); - if (surf && !vmw_user_object_is_mapped(&vps->uo)) - return surf->snooper.image; - - return vmw_user_object_map(&vps->uo); -} - -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 && 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 = vmw_priv(vcp->base.dev); - 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(vmw_priv(plane->dev), 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) { @@ -575,262 +89,6 @@ vmw_du_plane_cleanup_fb(struct drm_plane *plane, /** - * 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 (!vmw_user_object_is_null(&vps->uo)) - vmw_user_object_unmap(&vps->uo); - - vmw_du_cursor_plane_unmap_cm(vps); - vmw_du_put_cursor_mob(vcp, vps); - - vmw_du_plane_unpin_surf(vps); - vmw_user_object_unref(&vps->uo); -} - - -/** - * 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); - struct vmw_bo *bo = NULL; - int ret = 0; - - if (!vmw_user_object_is_null(&vps->uo)) { - vmw_user_object_unmap(&vps->uo); - vmw_user_object_unref(&vps->uo); - } - - if (fb) { - if (vmw_framebuffer_to_vfb(fb)->bo) { - vps->uo.buffer = vmw_framebuffer_to_vfbd(fb)->buffer; - vps->uo.surface = NULL; - } else { - memcpy(&vps->uo, &vmw_framebuffer_to_vfbs(fb)->uo, sizeof(vps->uo)); - } - vmw_user_object_ref(&vps->uo); - } - - bo = vmw_user_object_buffer(&vps->uo); - if (bo) { - struct ttm_operation_ctx ctx = {false, false}; - - ret = ttm_bo_reserve(&bo->tbo, true, false, NULL); - if (ret != 0) - return -ENOMEM; - - ret = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); - if (ret != 0) - return -ENOMEM; - - vmw_bo_pin_reserved(bo, true); - if (vmw_framebuffer_to_vfb(fb)->bo) { - const u32 size = new_state->crtc_w * new_state->crtc_h * sizeof(u32); - - (void)vmw_bo_map_and_cache_size(bo, size); - } else { - vmw_bo_map_and_cache(bo); - } - ttm_bo_unreserve(&bo->tbo); - } - - if (!vmw_user_object_is_null(&vps->uo)) { - 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); - struct vmw_bo *old_bo = NULL; - struct vmw_bo *new_bo = NULL; - struct ww_acquire_ctx ctx; - s32 hotspot_x, hotspot_y; - int ret; - - hotspot_x = du->hotspot_x + new_state->hotspot_x; - hotspot_y = du->hotspot_y + new_state->hotspot_y; - - du->cursor_surface = vmw_user_object_surface(&vps->uo); - - if (vmw_user_object_is_null(&vps->uo)) { - vmw_cursor_update_position(dev_priv, false, 0, 0); - return; - } - - vps->cursor.hotspot_x = hotspot_x; - vps->cursor.hotspot_y = hotspot_y; - - if (du->cursor_surface) - du->cursor_age = du->cursor_surface->snooper.age; - - ww_acquire_init(&ctx, &reservation_ww_class); - - if (!vmw_user_object_is_null(&old_vps->uo)) { - old_bo = vmw_user_object_buffer(&old_vps->uo); - ret = ttm_bo_reserve(&old_bo->tbo, false, false, &ctx); - if (ret != 0) - return; - } - - if (!vmw_user_object_is_null(&vps->uo)) { - new_bo = vmw_user_object_buffer(&vps->uo); - if (old_bo != new_bo) { - ret = ttm_bo_reserve(&new_bo->tbo, false, false, &ctx); - if (ret != 0) { - if (old_bo) { - ttm_bo_unreserve(&old_bo->tbo); - ww_acquire_fini(&ctx); - } - return; - } - } else { - new_bo = NULL; - } - } - 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); - } - - if (new_bo) - ttm_bo_unreserve(&new_bo->tbo); - if (old_bo) - ttm_bo_unreserve(&old_bo->tbo); - - ww_acquire_fini(&ctx); - - 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_primary_plane_atomic_check - check if the new state is okay * * @plane: display plane @@ -873,66 +131,6 @@ int vmw_du_primary_plane_atomic_check(struct drm_plane *plane, 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_user_object_surface(&vmw_framebuffer_to_vfbs(fb)->uo); - - 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) { @@ -1076,7 +274,7 @@ 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 */ vmw_user_object_ref(&vps->uo); @@ -1221,7 +419,20 @@ 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_user_object_unref(&vfbs->uo); @@ -1375,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); @@ -1505,6 +717,8 @@ 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_user_object uo = {0}; + struct vmw_bo *bo; + struct vmw_surface *surface; int ret; /* returns either a bo or surface */ @@ -1534,6 +748,8 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, } err_out: + 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); @@ -1542,6 +758,14 @@ err_out: 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; } @@ -1974,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) @@ -2670,7 +1856,7 @@ 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; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index 2a6c6d6581e0..511e29cdb987 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -1,40 +1,21 @@ /* SPDX-License-Identifier: GPL-2.0 OR MIT */ /************************************************************************** * - * Copyright (c) 2009-2024 Broadcom. All Rights Reserved. The term + * Copyright (c) 2009-2025 Broadcom. All Rights Reserved. The term * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * - * 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. - * **************************************************************************/ #ifndef VMWGFX_KMS_H_ #define VMWGFX_KMS_H_ +#include "vmwgfx_cursor_plane.h" +#include "vmwgfx_drv.h" + #include <drm/drm_encoder.h> #include <drm/drm_framebuffer.h> #include <drm/drm_probe_helper.h> -#include "vmwgfx_drv.h" - /** * struct vmw_du_update_plane - Closure structure for vmw_du_helper_plane_update * @plane: Plane which is being updated. @@ -235,16 +216,11 @@ static const uint32_t __maybe_unused vmw_primary_plane_formats[] = { DRM_FORMAT_XRGB1555, }; -static const uint32_t __maybe_unused 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) -#define vmw_plane_to_vcp(x) container_of(x, struct vmw_cursor_plane, base) /** * Derived class for crtc state object @@ -255,11 +231,6 @@ struct vmw_crtc_state { struct drm_crtc_state base; }; -struct vmw_cursor_plane_state { - struct vmw_bo *bo; - s32 hotspot_x; - s32 hotspot_y; -}; /** * Derived class for plane state object @@ -283,7 +254,6 @@ struct vmw_plane_state { /* For CPU Blit */ unsigned int cpp; - bool surf_mapped; struct vmw_cursor_plane_state cursor; }; @@ -317,17 +287,6 @@ struct vmw_connector_state { int gui_y; }; -/** - * Derived class for cursor plane object - * - * @base DRM plane object - * @cursor.cursor_mobs Cursor mobs available for re-use - */ -struct vmw_cursor_plane { - struct drm_plane base; - - struct vmw_bo *cursor_mobs[3]; -}; /** * Base class display unit. @@ -343,17 +302,6 @@ struct vmw_display_unit { struct drm_plane primary; struct vmw_cursor_plane cursor; - struct vmw_surface *cursor_surface; - size_t cursor_age; - - int cursor_x; - int cursor_y; - - int hotspot_x; - int hotspot_y; - s32 core_hotspot_x; - s32 core_hotspot_y; - unsigned unit; /* @@ -403,8 +351,6 @@ struct vmw_display_unit { */ void vmw_du_init(struct vmw_display_unit *du); void vmw_du_cleanup(struct vmw_display_unit *du); -void vmw_du_crtc_save(struct drm_crtc *crtc); -void vmw_du_crtc_restore(struct drm_crtc *crtc); int vmw_du_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t size, @@ -435,7 +381,7 @@ int vmw_kms_helper_dirty(struct vmw_private *dev_priv, int increment, struct vmw_kms_dirty *dirty); enum drm_mode_status vmw_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode); + const struct drm_display_mode *mode); int vmw_connector_get_modes(struct drm_connector *connector); void vmw_kms_helper_validation_finish(struct vmw_private *dev_priv, @@ -460,19 +406,10 @@ void vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv); /* Universal Plane Helpers */ void vmw_du_primary_plane_destroy(struct drm_plane *plane); -void vmw_du_cursor_plane_destroy(struct drm_plane *plane); /* Atomic Helpers */ int vmw_du_primary_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state); -int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, - struct drm_atomic_state *state); -void vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, - struct drm_atomic_state *state); -int vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane, - struct drm_plane_state *new_state); -void vmw_du_cursor_plane_cleanup_fb(struct drm_plane *plane, - struct drm_plane_state *old_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); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index f0b429525467..c23c9195f0dc 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -372,7 +372,7 @@ static const struct drm_plane_funcs vmw_ldu_plane_funcs = { 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, + .destroy = vmw_cursor_plane_destroy, .reset = vmw_du_plane_reset, .atomic_duplicate_state = vmw_du_plane_duplicate_state, .atomic_destroy_state = vmw_du_plane_destroy_state, @@ -383,10 +383,10 @@ static const struct drm_plane_funcs vmw_ldu_cursor_funcs = { */ 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_cursor_plane_cleanup_fb, + .atomic_check = vmw_cursor_plane_atomic_check, + .atomic_update = vmw_cursor_plane_atomic_update, + .prepare_fb = vmw_cursor_plane_prepare_fb, + .cleanup_fb = vmw_cursor_plane_cleanup_fb, }; static const struct diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c index 7055cbefc768..d8204d4265d3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c @@ -282,8 +282,7 @@ out_no_setup: } vmw_bo_unpin_unlocked(&batch->otable_bo->tbo); - ttm_bo_put(&batch->otable_bo->tbo); - batch->otable_bo = NULL; + vmw_bo_unreference(&batch->otable_bo); return ret; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c index 1f15990d3934..1d9a42cbc88f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c @@ -289,7 +289,7 @@ static int vmw_send_msg(struct rpc_channel *channel, const char *msg) return -EINVAL; } -STACK_FRAME_NON_STANDARD(vmw_send_msg); +STACK_FRAME_NON_STANDARD_FP(vmw_send_msg); /** diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c index 74ff2812d66a..7de20e56082c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c @@ -1,27 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 OR MIT /************************************************************************** * - * Copyright 2019-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) 2019-2025 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * **************************************************************************/ #include "vmwgfx_bo.h" @@ -71,6 +52,11 @@ struct vmw_bo_dirty { unsigned long bitmap[]; }; +bool vmw_bo_is_dirty(struct vmw_bo *vbo) +{ + return vbo->dirty && (vbo->dirty->start < vbo->dirty->end); +} + /** * vmw_bo_dirty_scan_pagetable - Perform a pagetable scan for dirty bits * @vbo: The buffer object to scan @@ -341,6 +327,41 @@ void vmw_bo_dirty_transfer_to_res(struct vmw_resource *res) dirty->end = res_start; } +void vmw_bo_dirty_clear(struct vmw_bo *vbo) +{ + struct vmw_bo_dirty *dirty = vbo->dirty; + pgoff_t start, cur, end; + unsigned long res_start = 0; + unsigned long res_end = vbo->tbo.base.size; + + WARN_ON_ONCE(res_start & ~PAGE_MASK); + res_start >>= PAGE_SHIFT; + res_end = DIV_ROUND_UP(res_end, PAGE_SIZE); + + if (res_start >= dirty->end || res_end <= dirty->start) + return; + + cur = max(res_start, dirty->start); + res_end = max(res_end, dirty->end); + while (cur < res_end) { + unsigned long num; + + start = find_next_bit(&dirty->bitmap[0], res_end, cur); + if (start >= res_end) + break; + + end = find_next_zero_bit(&dirty->bitmap[0], res_end, start + 1); + cur = end + 1; + num = end - start; + bitmap_clear(&dirty->bitmap[0], start, num); + } + + if (res_start <= dirty->start && res_end > dirty->start) + dirty->start = res_end; + if (res_start < dirty->end && res_end >= dirty->end) + dirty->end = res_start; +} + /** * vmw_bo_dirty_clear_res - Clear a resource's dirty region from * its backing mob. diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index a73af8a355fb..388011696941 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -273,7 +273,7 @@ int vmw_user_resource_lookup_handle(struct vmw_private *dev_priv, goto out_bad_resource; res = converter->base_obj_to_res(base); - kref_get(&res->kref); + vmw_resource_reference(res); *p_res = res; ret = 0; @@ -347,7 +347,7 @@ static int vmw_resource_buf_alloc(struct vmw_resource *res, return 0; } - ret = vmw_gem_object_create(res->dev_priv, &bo_params, &gbo); + ret = vmw_bo_create(res->dev_priv, &bo_params, &gbo); if (unlikely(ret != 0)) goto out_no_bo; @@ -531,9 +531,9 @@ vmw_resource_check_buffer(struct ww_acquire_ctx *ticket, } INIT_LIST_HEAD(&val_list); - ttm_bo_get(&res->guest_memory_bo->tbo); val_buf->bo = &res->guest_memory_bo->tbo; val_buf->num_shared = 0; + drm_gem_object_get(&val_buf->bo->base); list_add_tail(&val_buf->head, &val_list); ret = ttm_eu_reserve_buffers(ticket, &val_list, interruptible, NULL); if (unlikely(ret != 0)) @@ -557,7 +557,7 @@ vmw_resource_check_buffer(struct ww_acquire_ctx *ticket, out_no_validate: ttm_eu_backoff_reservation(ticket, &val_list); out_no_reserve: - ttm_bo_put(val_buf->bo); + drm_gem_object_put(&val_buf->bo->base); val_buf->bo = NULL; if (guest_memory_dirty) vmw_user_bo_unref(&res->guest_memory_bo); @@ -619,7 +619,7 @@ vmw_resource_backoff_reservation(struct ww_acquire_ctx *ticket, INIT_LIST_HEAD(&val_list); list_add_tail(&val_buf->head, &val_list); ttm_eu_backoff_reservation(ticket, &val_list); - ttm_bo_put(val_buf->bo); + drm_gem_object_put(&val_buf->bo->base); val_buf->bo = NULL; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index 32029d80b72b..5f5f5a94301f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -445,7 +445,7 @@ vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane, * resume the overlays, this is preferred to failing to alloc. */ vmw_overlay_pause_all(dev_priv); - ret = vmw_gem_object_create(dev_priv, &bo_params, &vps->uo.buffer); + ret = vmw_bo_create(dev_priv, &bo_params, &vps->uo.buffer); vmw_overlay_resume_all(dev_priv); if (ret) return ret; @@ -764,7 +764,7 @@ static const struct drm_plane_funcs vmw_sou_plane_funcs = { 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, + .destroy = vmw_cursor_plane_destroy, .reset = vmw_du_plane_reset, .atomic_duplicate_state = vmw_du_plane_duplicate_state, .atomic_destroy_state = vmw_du_plane_destroy_state, @@ -775,10 +775,10 @@ static const struct drm_plane_funcs vmw_sou_cursor_funcs = { */ 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_cursor_plane_cleanup_fb, + .atomic_check = vmw_cursor_plane_atomic_check, + .atomic_update = vmw_cursor_plane_atomic_update, + .prepare_fb = vmw_cursor_plane_prepare_fb, + .cleanup_fb = vmw_cursor_plane_cleanup_fb, }; static const struct diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 114a75069e1c..20aab725e53a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -839,7 +839,7 @@ static void vmw_stdu_connector_destroy(struct drm_connector *connector) static enum drm_mode_status vmw_stdu_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; @@ -1482,7 +1482,7 @@ static const struct drm_plane_funcs vmw_stdu_plane_funcs = { 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, + .destroy = vmw_cursor_plane_destroy, .reset = vmw_du_plane_reset, .atomic_duplicate_state = vmw_du_plane_duplicate_state, .atomic_destroy_state = vmw_du_plane_destroy_state, @@ -1494,10 +1494,10 @@ static const struct drm_plane_funcs vmw_stdu_cursor_funcs = { */ 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_cursor_plane_cleanup_fb, + .atomic_check = vmw_cursor_plane_atomic_check, + .atomic_update = vmw_cursor_plane_atomic_update, + .prepare_fb = vmw_cursor_plane_prepare_fb, + .cleanup_fb = vmw_cursor_plane_cleanup_fb, }; static const struct @@ -1584,6 +1584,7 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) } drm_plane_helper_add(&cursor->base, &vmw_stdu_cursor_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(&cursor->base); ret = drm_connector_init(dev, connector, &vmw_stdu_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index 5721c74da3e0..7e281c3c6bc5 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -1,32 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 OR MIT /************************************************************************** * - * Copyright (c) 2009-2024 Broadcom. All Rights Reserved. The term + * Copyright (c) 2009-2025 Broadcom. All Rights Reserved. The term * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * - * 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_bo.h" +#include "vmwgfx_cursor_plane.h" #include "vmwgfx_drv.h" #include "vmwgfx_resource_priv.h" #include "vmwgfx_so.h" @@ -658,7 +639,7 @@ static void vmw_user_surface_free(struct vmw_resource *res) struct vmw_user_surface *user_srf = container_of(srf, struct vmw_user_surface, srf); - WARN_ON_ONCE(res->dirty); + WARN_ON(res->dirty); if (user_srf->master) drm_master_put(&user_srf->master); kfree(srf->offsets); @@ -689,8 +670,7 @@ static void vmw_user_surface_base_release(struct ttm_base_object **p_base) * Dumb buffers own the resource and they'll unref the * resource themselves */ - if (res && res->guest_memory_bo && res->guest_memory_bo->is_dumb) - return; + WARN_ON(res && res->guest_memory_bo && res->guest_memory_bo->is_dumb); vmw_resource_unreference(&res); } @@ -818,25 +798,11 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, } } res->guest_memory_size = cur_bo_offset; - if (!file_priv->atomic && - metadata->scanout && - metadata->num_sizes == 1 && - metadata->sizes[0].width == VMW_CURSOR_SNOOP_WIDTH && - metadata->sizes[0].height == VMW_CURSOR_SNOOP_HEIGHT && - metadata->format == VMW_CURSOR_SNOOP_FORMAT) { - const struct SVGA3dSurfaceDesc *desc = - vmw_surface_get_desc(VMW_CURSOR_SNOOP_FORMAT); - const u32 cursor_size_bytes = VMW_CURSOR_SNOOP_WIDTH * - VMW_CURSOR_SNOOP_HEIGHT * - desc->pitchBytesPerBlock; - srf->snooper.image = kzalloc(cursor_size_bytes, GFP_KERNEL); - if (!srf->snooper.image) { - DRM_ERROR("Failed to allocate cursor_image\n"); - ret = -ENOMEM; - goto out_no_copy; - } - } else { - srf->snooper.image = NULL; + + srf->snooper.image = vmw_cursor_snooper_create(file_priv, metadata); + if (IS_ERR(srf->snooper.image)) { + ret = PTR_ERR(srf->snooper.image); + goto out_no_copy; } if (drm_is_primary_client(file_priv)) @@ -864,14 +830,17 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, .pin = false }; - ret = vmw_gem_object_create(dev_priv, - ¶ms, - &res->guest_memory_bo); + ret = vmw_bo_create(dev_priv, ¶ms, &res->guest_memory_bo); + if (unlikely(ret != 0)) { + vmw_resource_unreference(&res); + goto out_unlock; + } + + ret = vmw_bo_add_detached_resource(res->guest_memory_bo, res); if (unlikely(ret != 0)) { vmw_resource_unreference(&res); goto out_unlock; } - vmw_bo_add_detached_resource(res->guest_memory_bo, res); } tmp = vmw_resource_reference(&srf->res); @@ -1670,6 +1639,14 @@ vmw_gb_surface_define_internal(struct drm_device *dev, } + if (res->guest_memory_bo) { + ret = vmw_bo_add_detached_resource(res->guest_memory_bo, res); + if (unlikely(ret != 0)) { + vmw_resource_unreference(&res); + goto out_unlock; + } + } + tmp = vmw_resource_reference(res); ret = ttm_prime_object_init(tfile, res->guest_memory_size, &user_srf->prime, VMW_RES_SURFACE, @@ -1684,7 +1661,6 @@ vmw_gb_surface_define_internal(struct drm_device *dev, rep->handle = user_srf->prime.base.handle; rep->backup_size = res->guest_memory_size; if (res->guest_memory_bo) { - vmw_bo_add_detached_resource(res->guest_memory_bo, res); rep->buffer_map_handle = drm_vma_node_offset_addr(&res->guest_memory_bo->tbo.base.vma_node); rep->buffer_size = res->guest_memory_bo->tbo.base.size; @@ -2358,12 +2334,19 @@ int vmw_dumb_create(struct drm_file *file_priv, vbo = res->guest_memory_bo; vbo->is_dumb = true; vbo->dumb_surface = vmw_res_to_srf(res); - + drm_gem_object_put(&vbo->tbo.base); + /* + * Unset the user surface dtor since this in not actually exposed + * to userspace. The suface is owned via the dumb_buffer's GEM handle + */ + struct vmw_user_surface *usurf = container_of(vbo->dumb_surface, + struct vmw_user_surface, srf); + usurf->prime.base.refcount_release = NULL; err: if (res) vmw_resource_unreference(&res); - if (ret) - ttm_ref_object_base_unref(tfile, arg.rep.handle); + + ttm_ref_object_base_unref(tfile, arg.rep.handle); return ret; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c index e7625b3f71e0..7ee93e7191c7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c @@ -262,9 +262,8 @@ int vmw_validation_add_bo(struct vmw_validation_context *ctx, bo_node->hash.key); } val_buf = &bo_node->base; - val_buf->bo = ttm_bo_get_unless_zero(&vbo->tbo); - if (!val_buf->bo) - return -ESRCH; + vmw_bo_reference(vbo); + val_buf->bo = &vbo->tbo; val_buf->num_shared = 0; list_add_tail(&val_buf->head, &ctx->bo_list); } @@ -656,7 +655,7 @@ void vmw_validation_unref_lists(struct vmw_validation_context *ctx) struct vmw_validation_res_node *val; list_for_each_entry(entry, &ctx->bo_list, base.head) { - ttm_bo_put(entry->base.bo); + drm_gem_object_put(&entry->base.bo->base); entry->base.bo = NULL; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c index 8651b788e98b..aec774fa4d7b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c @@ -290,8 +290,8 @@ vmw_vkms_enable_vblank(struct drm_crtc *crtc) drm_calc_timestamping_constants(crtc, &crtc->mode); - hrtimer_init(&du->vkms.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - du->vkms.timer.function = &vmw_vkms_vblank_simulate; + hrtimer_setup(&du->vkms.timer, &vmw_vkms_vblank_simulate, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); du->vkms.period_ns = ktime_set(0, vblank->framedur_ns); hrtimer_start(&du->vkms.timer, du->vkms.period_ns, HRTIMER_MODE_REL); |