// 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 #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; }