162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT 262306a36Sopenharmony_ci/************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 762306a36Sopenharmony_ci * copy of this software and associated documentation files (the 862306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 962306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 1062306a36Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 1162306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 1262306a36Sopenharmony_ci * the following conditions: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the 1562306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 1662306a36Sopenharmony_ci * of the Software. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1962306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2062306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 2162306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 2262306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 2362306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 2462306a36Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci **************************************************************************/ 2762306a36Sopenharmony_ci#include "vmwgfx_kms.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "vmwgfx_bo.h" 3062306a36Sopenharmony_ci#include "vmw_surface_cache.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <drm/drm_atomic.h> 3362306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 3462306a36Sopenharmony_ci#include <drm/drm_damage_helper.h> 3562306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 3662306a36Sopenharmony_ci#include <drm/drm_rect.h> 3762306a36Sopenharmony_ci#include <drm/drm_sysfs.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_civoid vmw_du_cleanup(struct vmw_display_unit *du) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(du->primary.dev); 4262306a36Sopenharmony_ci drm_plane_cleanup(&du->primary); 4362306a36Sopenharmony_ci if (vmw_cmd_supported(dev_priv)) 4462306a36Sopenharmony_ci drm_plane_cleanup(&du->cursor.base); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci drm_connector_unregister(&du->connector); 4762306a36Sopenharmony_ci drm_crtc_cleanup(&du->crtc); 4862306a36Sopenharmony_ci drm_encoder_cleanup(&du->encoder); 4962306a36Sopenharmony_ci drm_connector_cleanup(&du->connector); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * Display Unit Cursor functions 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int vmw_du_cursor_plane_unmap_cm(struct vmw_plane_state *vps); 5762306a36Sopenharmony_cistatic void vmw_cursor_update_mob(struct vmw_private *dev_priv, 5862306a36Sopenharmony_ci struct vmw_plane_state *vps, 5962306a36Sopenharmony_ci u32 *image, u32 width, u32 height, 6062306a36Sopenharmony_ci u32 hotspotX, u32 hotspotY); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct vmw_svga_fifo_cmd_define_cursor { 6362306a36Sopenharmony_ci u32 cmd; 6462306a36Sopenharmony_ci SVGAFifoCmdDefineAlphaCursor cursor; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/** 6862306a36Sopenharmony_ci * vmw_send_define_cursor_cmd - queue a define cursor command 6962306a36Sopenharmony_ci * @dev_priv: the private driver struct 7062306a36Sopenharmony_ci * @image: buffer which holds the cursor image 7162306a36Sopenharmony_ci * @width: width of the mouse cursor image 7262306a36Sopenharmony_ci * @height: height of the mouse cursor image 7362306a36Sopenharmony_ci * @hotspotX: the horizontal position of mouse hotspot 7462306a36Sopenharmony_ci * @hotspotY: the vertical position of mouse hotspot 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_cistatic void vmw_send_define_cursor_cmd(struct vmw_private *dev_priv, 7762306a36Sopenharmony_ci u32 *image, u32 width, u32 height, 7862306a36Sopenharmony_ci u32 hotspotX, u32 hotspotY) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct vmw_svga_fifo_cmd_define_cursor *cmd; 8162306a36Sopenharmony_ci const u32 image_size = width * height * sizeof(*image); 8262306a36Sopenharmony_ci const u32 cmd_size = sizeof(*cmd) + image_size; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* Try to reserve fifocmd space and swallow any failures; 8562306a36Sopenharmony_ci such reservations cannot be left unconsumed for long 8662306a36Sopenharmony_ci under the risk of clogging other fifocmd users, so 8762306a36Sopenharmony_ci we treat reservations separtely from the way we treat 8862306a36Sopenharmony_ci other fallible KMS-atomic resources at prepare_fb */ 8962306a36Sopenharmony_ci cmd = VMW_CMD_RESERVE(dev_priv, cmd_size); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (unlikely(!cmd)) 9262306a36Sopenharmony_ci return; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci memset(cmd, 0, sizeof(*cmd)); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci memcpy(&cmd[1], image, image_size); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci cmd->cmd = SVGA_CMD_DEFINE_ALPHA_CURSOR; 9962306a36Sopenharmony_ci cmd->cursor.id = 0; 10062306a36Sopenharmony_ci cmd->cursor.width = width; 10162306a36Sopenharmony_ci cmd->cursor.height = height; 10262306a36Sopenharmony_ci cmd->cursor.hotspotX = hotspotX; 10362306a36Sopenharmony_ci cmd->cursor.hotspotY = hotspotY; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci vmw_cmd_commit_flush(dev_priv, cmd_size); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/** 10962306a36Sopenharmony_ci * vmw_cursor_update_image - update the cursor image on the provided plane 11062306a36Sopenharmony_ci * @dev_priv: the private driver struct 11162306a36Sopenharmony_ci * @vps: the plane state of the cursor plane 11262306a36Sopenharmony_ci * @image: buffer which holds the cursor image 11362306a36Sopenharmony_ci * @width: width of the mouse cursor image 11462306a36Sopenharmony_ci * @height: height of the mouse cursor image 11562306a36Sopenharmony_ci * @hotspotX: the horizontal position of mouse hotspot 11662306a36Sopenharmony_ci * @hotspotY: the vertical position of mouse hotspot 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_cistatic void vmw_cursor_update_image(struct vmw_private *dev_priv, 11962306a36Sopenharmony_ci struct vmw_plane_state *vps, 12062306a36Sopenharmony_ci u32 *image, u32 width, u32 height, 12162306a36Sopenharmony_ci u32 hotspotX, u32 hotspotY) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci if (vps->cursor.bo) 12462306a36Sopenharmony_ci vmw_cursor_update_mob(dev_priv, vps, image, 12562306a36Sopenharmony_ci vps->base.crtc_w, vps->base.crtc_h, 12662306a36Sopenharmony_ci hotspotX, hotspotY); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci else 12962306a36Sopenharmony_ci vmw_send_define_cursor_cmd(dev_priv, image, width, height, 13062306a36Sopenharmony_ci hotspotX, hotspotY); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/** 13562306a36Sopenharmony_ci * vmw_cursor_update_mob - Update cursor vis CursorMob mechanism 13662306a36Sopenharmony_ci * 13762306a36Sopenharmony_ci * Called from inside vmw_du_cursor_plane_atomic_update to actually 13862306a36Sopenharmony_ci * make the cursor-image live. 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * @dev_priv: device to work with 14162306a36Sopenharmony_ci * @vps: the plane state of the cursor plane 14262306a36Sopenharmony_ci * @image: cursor source data to fill the MOB with 14362306a36Sopenharmony_ci * @width: source data width 14462306a36Sopenharmony_ci * @height: source data height 14562306a36Sopenharmony_ci * @hotspotX: cursor hotspot x 14662306a36Sopenharmony_ci * @hotspotY: cursor hotspot Y 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_cistatic void vmw_cursor_update_mob(struct vmw_private *dev_priv, 14962306a36Sopenharmony_ci struct vmw_plane_state *vps, 15062306a36Sopenharmony_ci u32 *image, u32 width, u32 height, 15162306a36Sopenharmony_ci u32 hotspotX, u32 hotspotY) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci SVGAGBCursorHeader *header; 15462306a36Sopenharmony_ci SVGAGBAlphaCursorHeader *alpha_header; 15562306a36Sopenharmony_ci const u32 image_size = width * height * sizeof(*image); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci header = vmw_bo_map_and_cache(vps->cursor.bo); 15862306a36Sopenharmony_ci alpha_header = &header->header.alphaHeader; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci memset(header, 0, sizeof(*header)); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci header->type = SVGA_ALPHA_CURSOR; 16362306a36Sopenharmony_ci header->sizeInBytes = image_size; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci alpha_header->hotspotX = hotspotX; 16662306a36Sopenharmony_ci alpha_header->hotspotY = hotspotY; 16762306a36Sopenharmony_ci alpha_header->width = width; 16862306a36Sopenharmony_ci alpha_header->height = height; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci memcpy(header + 1, image, image_size); 17162306a36Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_CURSOR_MOBID, 17262306a36Sopenharmony_ci vps->cursor.bo->tbo.resource->start); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic u32 vmw_du_cursor_mob_size(u32 w, u32 h) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci return w * h * sizeof(u32) + sizeof(SVGAGBCursorHeader); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/** 18262306a36Sopenharmony_ci * vmw_du_cursor_plane_acquire_image -- Acquire the image data 18362306a36Sopenharmony_ci * @vps: cursor plane state 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_cistatic u32 *vmw_du_cursor_plane_acquire_image(struct vmw_plane_state *vps) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci if (vps->surf) { 18862306a36Sopenharmony_ci if (vps->surf_mapped) 18962306a36Sopenharmony_ci return vmw_bo_map_and_cache(vps->surf->res.guest_memory_bo); 19062306a36Sopenharmony_ci return vps->surf->snooper.image; 19162306a36Sopenharmony_ci } else if (vps->bo) 19262306a36Sopenharmony_ci return vmw_bo_map_and_cache(vps->bo); 19362306a36Sopenharmony_ci return NULL; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic bool vmw_du_cursor_plane_has_changed(struct vmw_plane_state *old_vps, 19762306a36Sopenharmony_ci struct vmw_plane_state *new_vps) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci void *old_image; 20062306a36Sopenharmony_ci void *new_image; 20162306a36Sopenharmony_ci u32 size; 20262306a36Sopenharmony_ci bool changed; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (old_vps->base.crtc_w != new_vps->base.crtc_w || 20562306a36Sopenharmony_ci old_vps->base.crtc_h != new_vps->base.crtc_h) 20662306a36Sopenharmony_ci return true; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (old_vps->cursor.hotspot_x != new_vps->cursor.hotspot_x || 20962306a36Sopenharmony_ci old_vps->cursor.hotspot_y != new_vps->cursor.hotspot_y) 21062306a36Sopenharmony_ci return true; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci size = new_vps->base.crtc_w * new_vps->base.crtc_h * sizeof(u32); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci old_image = vmw_du_cursor_plane_acquire_image(old_vps); 21562306a36Sopenharmony_ci new_image = vmw_du_cursor_plane_acquire_image(new_vps); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci changed = false; 21862306a36Sopenharmony_ci if (old_image && new_image) 21962306a36Sopenharmony_ci changed = memcmp(old_image, new_image, size) != 0; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return changed; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic void vmw_du_destroy_cursor_mob(struct vmw_bo **vbo) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci if (!(*vbo)) 22762306a36Sopenharmony_ci return; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci ttm_bo_unpin(&(*vbo)->tbo); 23062306a36Sopenharmony_ci vmw_bo_unreference(vbo); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic void vmw_du_put_cursor_mob(struct vmw_cursor_plane *vcp, 23462306a36Sopenharmony_ci struct vmw_plane_state *vps) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci u32 i; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (!vps->cursor.bo) 23962306a36Sopenharmony_ci return; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci vmw_du_cursor_plane_unmap_cm(vps); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* Look for a free slot to return this mob to the cache. */ 24462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++) { 24562306a36Sopenharmony_ci if (!vcp->cursor_mobs[i]) { 24662306a36Sopenharmony_ci vcp->cursor_mobs[i] = vps->cursor.bo; 24762306a36Sopenharmony_ci vps->cursor.bo = NULL; 24862306a36Sopenharmony_ci return; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Cache is full: See if this mob is bigger than an existing mob. */ 25362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++) { 25462306a36Sopenharmony_ci if (vcp->cursor_mobs[i]->tbo.base.size < 25562306a36Sopenharmony_ci vps->cursor.bo->tbo.base.size) { 25662306a36Sopenharmony_ci vmw_du_destroy_cursor_mob(&vcp->cursor_mobs[i]); 25762306a36Sopenharmony_ci vcp->cursor_mobs[i] = vps->cursor.bo; 25862306a36Sopenharmony_ci vps->cursor.bo = NULL; 25962306a36Sopenharmony_ci return; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* Destroy it if it's not worth caching. */ 26462306a36Sopenharmony_ci vmw_du_destroy_cursor_mob(&vps->cursor.bo); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic int vmw_du_get_cursor_mob(struct vmw_cursor_plane *vcp, 26862306a36Sopenharmony_ci struct vmw_plane_state *vps) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct vmw_private *dev_priv = vcp->base.dev->dev_private; 27162306a36Sopenharmony_ci u32 size = vmw_du_cursor_mob_size(vps->base.crtc_w, vps->base.crtc_h); 27262306a36Sopenharmony_ci u32 i; 27362306a36Sopenharmony_ci u32 cursor_max_dim, mob_max_size; 27462306a36Sopenharmony_ci struct vmw_fence_obj *fence = NULL; 27562306a36Sopenharmony_ci int ret; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (!dev_priv->has_mob || 27862306a36Sopenharmony_ci (dev_priv->capabilities2 & SVGA_CAP2_CURSOR_MOB) == 0) 27962306a36Sopenharmony_ci return -EINVAL; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci mob_max_size = vmw_read(dev_priv, SVGA_REG_MOB_MAX_SIZE); 28262306a36Sopenharmony_ci cursor_max_dim = vmw_read(dev_priv, SVGA_REG_CURSOR_MAX_DIMENSION); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (size > mob_max_size || vps->base.crtc_w > cursor_max_dim || 28562306a36Sopenharmony_ci vps->base.crtc_h > cursor_max_dim) 28662306a36Sopenharmony_ci return -EINVAL; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (vps->cursor.bo) { 28962306a36Sopenharmony_ci if (vps->cursor.bo->tbo.base.size >= size) 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci vmw_du_put_cursor_mob(vcp, vps); 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* Look for an unused mob in the cache. */ 29562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++) { 29662306a36Sopenharmony_ci if (vcp->cursor_mobs[i] && 29762306a36Sopenharmony_ci vcp->cursor_mobs[i]->tbo.base.size >= size) { 29862306a36Sopenharmony_ci vps->cursor.bo = vcp->cursor_mobs[i]; 29962306a36Sopenharmony_ci vcp->cursor_mobs[i] = NULL; 30062306a36Sopenharmony_ci return 0; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci /* Create a new mob if we can't find an existing one. */ 30462306a36Sopenharmony_ci ret = vmw_bo_create_and_populate(dev_priv, size, 30562306a36Sopenharmony_ci VMW_BO_DOMAIN_MOB, 30662306a36Sopenharmony_ci &vps->cursor.bo); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (ret != 0) 30962306a36Sopenharmony_ci return ret; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* Fence the mob creation so we are guarateed to have the mob */ 31262306a36Sopenharmony_ci ret = ttm_bo_reserve(&vps->cursor.bo->tbo, false, false, NULL); 31362306a36Sopenharmony_ci if (ret != 0) 31462306a36Sopenharmony_ci goto teardown; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ret = vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); 31762306a36Sopenharmony_ci if (ret != 0) { 31862306a36Sopenharmony_ci ttm_bo_unreserve(&vps->cursor.bo->tbo); 31962306a36Sopenharmony_ci goto teardown; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci dma_fence_wait(&fence->base, false); 32362306a36Sopenharmony_ci dma_fence_put(&fence->base); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci ttm_bo_unreserve(&vps->cursor.bo->tbo); 32662306a36Sopenharmony_ci return 0; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_citeardown: 32962306a36Sopenharmony_ci vmw_du_destroy_cursor_mob(&vps->cursor.bo); 33062306a36Sopenharmony_ci return ret; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic void vmw_cursor_update_position(struct vmw_private *dev_priv, 33562306a36Sopenharmony_ci bool show, int x, int y) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci const uint32_t svga_cursor_on = show ? SVGA_CURSOR_ON_SHOW 33862306a36Sopenharmony_ci : SVGA_CURSOR_ON_HIDE; 33962306a36Sopenharmony_ci uint32_t count; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci spin_lock(&dev_priv->cursor_lock); 34262306a36Sopenharmony_ci if (dev_priv->capabilities2 & SVGA_CAP2_EXTRA_REGS) { 34362306a36Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_CURSOR4_X, x); 34462306a36Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_CURSOR4_Y, y); 34562306a36Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_CURSOR4_SCREEN_ID, SVGA3D_INVALID_ID); 34662306a36Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_CURSOR4_ON, svga_cursor_on); 34762306a36Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_CURSOR4_SUBMIT, 1); 34862306a36Sopenharmony_ci } else if (vmw_is_cursor_bypass3_enabled(dev_priv)) { 34962306a36Sopenharmony_ci vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_ON, svga_cursor_on); 35062306a36Sopenharmony_ci vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_X, x); 35162306a36Sopenharmony_ci vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_Y, y); 35262306a36Sopenharmony_ci count = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_CURSOR_COUNT); 35362306a36Sopenharmony_ci vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_COUNT, ++count); 35462306a36Sopenharmony_ci } else { 35562306a36Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_CURSOR_X, x); 35662306a36Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_CURSOR_Y, y); 35762306a36Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_CURSOR_ON, svga_cursor_on); 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci spin_unlock(&dev_priv->cursor_lock); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_civoid vmw_kms_cursor_snoop(struct vmw_surface *srf, 36362306a36Sopenharmony_ci struct ttm_object_file *tfile, 36462306a36Sopenharmony_ci struct ttm_buffer_object *bo, 36562306a36Sopenharmony_ci SVGA3dCmdHeader *header) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct ttm_bo_kmap_obj map; 36862306a36Sopenharmony_ci unsigned long kmap_offset; 36962306a36Sopenharmony_ci unsigned long kmap_num; 37062306a36Sopenharmony_ci SVGA3dCopyBox *box; 37162306a36Sopenharmony_ci unsigned box_count; 37262306a36Sopenharmony_ci void *virtual; 37362306a36Sopenharmony_ci bool is_iomem; 37462306a36Sopenharmony_ci struct vmw_dma_cmd { 37562306a36Sopenharmony_ci SVGA3dCmdHeader header; 37662306a36Sopenharmony_ci SVGA3dCmdSurfaceDMA dma; 37762306a36Sopenharmony_ci } *cmd; 37862306a36Sopenharmony_ci int i, ret; 37962306a36Sopenharmony_ci const struct SVGA3dSurfaceDesc *desc = 38062306a36Sopenharmony_ci vmw_surface_get_desc(VMW_CURSOR_SNOOP_FORMAT); 38162306a36Sopenharmony_ci const u32 image_pitch = VMW_CURSOR_SNOOP_WIDTH * desc->pitchBytesPerBlock; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci cmd = container_of(header, struct vmw_dma_cmd, header); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* No snooper installed, nothing to copy */ 38662306a36Sopenharmony_ci if (!srf->snooper.image) 38762306a36Sopenharmony_ci return; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (cmd->dma.host.face != 0 || cmd->dma.host.mipmap != 0) { 39062306a36Sopenharmony_ci DRM_ERROR("face and mipmap for cursors should never != 0\n"); 39162306a36Sopenharmony_ci return; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (cmd->header.size < 64) { 39562306a36Sopenharmony_ci DRM_ERROR("at least one full copy box must be given\n"); 39662306a36Sopenharmony_ci return; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci box = (SVGA3dCopyBox *)&cmd[1]; 40062306a36Sopenharmony_ci box_count = (cmd->header.size - sizeof(SVGA3dCmdSurfaceDMA)) / 40162306a36Sopenharmony_ci sizeof(SVGA3dCopyBox); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (cmd->dma.guest.ptr.offset % PAGE_SIZE || 40462306a36Sopenharmony_ci box->x != 0 || box->y != 0 || box->z != 0 || 40562306a36Sopenharmony_ci box->srcx != 0 || box->srcy != 0 || box->srcz != 0 || 40662306a36Sopenharmony_ci box->d != 1 || box_count != 1 || 40762306a36Sopenharmony_ci box->w > VMW_CURSOR_SNOOP_WIDTH || box->h > VMW_CURSOR_SNOOP_HEIGHT) { 40862306a36Sopenharmony_ci /* TODO handle none page aligned offsets */ 40962306a36Sopenharmony_ci /* TODO handle more dst & src != 0 */ 41062306a36Sopenharmony_ci /* TODO handle more then one copy */ 41162306a36Sopenharmony_ci DRM_ERROR("Can't snoop dma request for cursor!\n"); 41262306a36Sopenharmony_ci DRM_ERROR("(%u, %u, %u) (%u, %u, %u) (%ux%ux%u) %u %u\n", 41362306a36Sopenharmony_ci box->srcx, box->srcy, box->srcz, 41462306a36Sopenharmony_ci box->x, box->y, box->z, 41562306a36Sopenharmony_ci box->w, box->h, box->d, box_count, 41662306a36Sopenharmony_ci cmd->dma.guest.ptr.offset); 41762306a36Sopenharmony_ci return; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci kmap_offset = cmd->dma.guest.ptr.offset >> PAGE_SHIFT; 42162306a36Sopenharmony_ci kmap_num = (VMW_CURSOR_SNOOP_HEIGHT*image_pitch) >> PAGE_SHIFT; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci ret = ttm_bo_reserve(bo, true, false, NULL); 42462306a36Sopenharmony_ci if (unlikely(ret != 0)) { 42562306a36Sopenharmony_ci DRM_ERROR("reserve failed\n"); 42662306a36Sopenharmony_ci return; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci ret = ttm_bo_kmap(bo, kmap_offset, kmap_num, &map); 43062306a36Sopenharmony_ci if (unlikely(ret != 0)) 43162306a36Sopenharmony_ci goto err_unreserve; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci virtual = ttm_kmap_obj_virtual(&map, &is_iomem); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (box->w == VMW_CURSOR_SNOOP_WIDTH && cmd->dma.guest.pitch == image_pitch) { 43662306a36Sopenharmony_ci memcpy(srf->snooper.image, virtual, 43762306a36Sopenharmony_ci VMW_CURSOR_SNOOP_HEIGHT*image_pitch); 43862306a36Sopenharmony_ci } else { 43962306a36Sopenharmony_ci /* Image is unsigned pointer. */ 44062306a36Sopenharmony_ci for (i = 0; i < box->h; i++) 44162306a36Sopenharmony_ci memcpy(srf->snooper.image + i * image_pitch, 44262306a36Sopenharmony_ci virtual + i * cmd->dma.guest.pitch, 44362306a36Sopenharmony_ci box->w * desc->pitchBytesPerBlock); 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci srf->snooper.age++; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci ttm_bo_kunmap(&map); 44962306a36Sopenharmony_cierr_unreserve: 45062306a36Sopenharmony_ci ttm_bo_unreserve(bo); 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/** 45462306a36Sopenharmony_ci * vmw_kms_legacy_hotspot_clear - Clear legacy hotspots 45562306a36Sopenharmony_ci * 45662306a36Sopenharmony_ci * @dev_priv: Pointer to the device private struct. 45762306a36Sopenharmony_ci * 45862306a36Sopenharmony_ci * Clears all legacy hotspots. 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_civoid vmw_kms_legacy_hotspot_clear(struct vmw_private *dev_priv) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct drm_device *dev = &dev_priv->drm; 46362306a36Sopenharmony_ci struct vmw_display_unit *du; 46462306a36Sopenharmony_ci struct drm_crtc *crtc; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci drm_modeset_lock_all(dev); 46762306a36Sopenharmony_ci drm_for_each_crtc(crtc, dev) { 46862306a36Sopenharmony_ci du = vmw_crtc_to_du(crtc); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci du->hotspot_x = 0; 47162306a36Sopenharmony_ci du->hotspot_y = 0; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci drm_modeset_unlock_all(dev); 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_civoid vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct drm_device *dev = &dev_priv->drm; 47962306a36Sopenharmony_ci struct vmw_display_unit *du; 48062306a36Sopenharmony_ci struct drm_crtc *crtc; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci mutex_lock(&dev->mode_config.mutex); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 48562306a36Sopenharmony_ci du = vmw_crtc_to_du(crtc); 48662306a36Sopenharmony_ci if (!du->cursor_surface || 48762306a36Sopenharmony_ci du->cursor_age == du->cursor_surface->snooper.age || 48862306a36Sopenharmony_ci !du->cursor_surface->snooper.image) 48962306a36Sopenharmony_ci continue; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci du->cursor_age = du->cursor_surface->snooper.age; 49262306a36Sopenharmony_ci vmw_send_define_cursor_cmd(dev_priv, 49362306a36Sopenharmony_ci du->cursor_surface->snooper.image, 49462306a36Sopenharmony_ci VMW_CURSOR_SNOOP_WIDTH, 49562306a36Sopenharmony_ci VMW_CURSOR_SNOOP_HEIGHT, 49662306a36Sopenharmony_ci du->hotspot_x + du->core_hotspot_x, 49762306a36Sopenharmony_ci du->hotspot_y + du->core_hotspot_y); 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci mutex_unlock(&dev->mode_config.mutex); 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_civoid vmw_du_cursor_plane_destroy(struct drm_plane *plane) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct vmw_cursor_plane *vcp = vmw_plane_to_vcp(plane); 50762306a36Sopenharmony_ci u32 i; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci vmw_cursor_update_position(plane->dev->dev_private, false, 0, 0); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++) 51262306a36Sopenharmony_ci vmw_du_destroy_cursor_mob(&vcp->cursor_mobs[i]); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci drm_plane_cleanup(plane); 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_civoid vmw_du_primary_plane_destroy(struct drm_plane *plane) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci drm_plane_cleanup(plane); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* Planes are static in our case so we don't free it */ 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci/** 52762306a36Sopenharmony_ci * vmw_du_plane_unpin_surf - unpins resource associated with a framebuffer surface 52862306a36Sopenharmony_ci * 52962306a36Sopenharmony_ci * @vps: plane state associated with the display surface 53062306a36Sopenharmony_ci * @unreference: true if we also want to unreference the display. 53162306a36Sopenharmony_ci */ 53262306a36Sopenharmony_civoid vmw_du_plane_unpin_surf(struct vmw_plane_state *vps, 53362306a36Sopenharmony_ci bool unreference) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci if (vps->surf) { 53662306a36Sopenharmony_ci if (vps->pinned) { 53762306a36Sopenharmony_ci vmw_resource_unpin(&vps->surf->res); 53862306a36Sopenharmony_ci vps->pinned--; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (unreference) { 54262306a36Sopenharmony_ci if (vps->pinned) 54362306a36Sopenharmony_ci DRM_ERROR("Surface still pinned\n"); 54462306a36Sopenharmony_ci vmw_surface_unreference(&vps->surf); 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci/** 55162306a36Sopenharmony_ci * vmw_du_plane_cleanup_fb - Unpins the plane surface 55262306a36Sopenharmony_ci * 55362306a36Sopenharmony_ci * @plane: display plane 55462306a36Sopenharmony_ci * @old_state: Contains the FB to clean up 55562306a36Sopenharmony_ci * 55662306a36Sopenharmony_ci * Unpins the framebuffer surface 55762306a36Sopenharmony_ci * 55862306a36Sopenharmony_ci * Returns 0 on success 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_civoid 56162306a36Sopenharmony_civmw_du_plane_cleanup_fb(struct drm_plane *plane, 56262306a36Sopenharmony_ci struct drm_plane_state *old_state) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci vmw_du_plane_unpin_surf(vps, false); 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci/** 57162306a36Sopenharmony_ci * vmw_du_cursor_plane_map_cm - Maps the cursor mobs. 57262306a36Sopenharmony_ci * 57362306a36Sopenharmony_ci * @vps: plane_state 57462306a36Sopenharmony_ci * 57562306a36Sopenharmony_ci * Returns 0 on success 57662306a36Sopenharmony_ci */ 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic int 57962306a36Sopenharmony_civmw_du_cursor_plane_map_cm(struct vmw_plane_state *vps) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci int ret; 58262306a36Sopenharmony_ci u32 size = vmw_du_cursor_mob_size(vps->base.crtc_w, vps->base.crtc_h); 58362306a36Sopenharmony_ci struct ttm_buffer_object *bo; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (!vps->cursor.bo) 58662306a36Sopenharmony_ci return -EINVAL; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci bo = &vps->cursor.bo->tbo; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (bo->base.size < size) 59162306a36Sopenharmony_ci return -EINVAL; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (vps->cursor.bo->map.virtual) 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci ret = ttm_bo_reserve(bo, false, false, NULL); 59762306a36Sopenharmony_ci if (unlikely(ret != 0)) 59862306a36Sopenharmony_ci return -ENOMEM; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci vmw_bo_map_and_cache(vps->cursor.bo); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci ttm_bo_unreserve(bo); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (unlikely(ret != 0)) 60562306a36Sopenharmony_ci return -ENOMEM; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci/** 61262306a36Sopenharmony_ci * vmw_du_cursor_plane_unmap_cm - Unmaps the cursor mobs. 61362306a36Sopenharmony_ci * 61462306a36Sopenharmony_ci * @vps: state of the cursor plane 61562306a36Sopenharmony_ci * 61662306a36Sopenharmony_ci * Returns 0 on success 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic int 62062306a36Sopenharmony_civmw_du_cursor_plane_unmap_cm(struct vmw_plane_state *vps) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci int ret = 0; 62362306a36Sopenharmony_ci struct vmw_bo *vbo = vps->cursor.bo; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (!vbo || !vbo->map.virtual) 62662306a36Sopenharmony_ci return 0; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci ret = ttm_bo_reserve(&vbo->tbo, true, false, NULL); 62962306a36Sopenharmony_ci if (likely(ret == 0)) { 63062306a36Sopenharmony_ci vmw_bo_unmap(vbo); 63162306a36Sopenharmony_ci ttm_bo_unreserve(&vbo->tbo); 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci return ret; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci/** 63962306a36Sopenharmony_ci * vmw_du_cursor_plane_cleanup_fb - Unpins the plane surface 64062306a36Sopenharmony_ci * 64162306a36Sopenharmony_ci * @plane: cursor plane 64262306a36Sopenharmony_ci * @old_state: contains the state to clean up 64362306a36Sopenharmony_ci * 64462306a36Sopenharmony_ci * Unmaps all cursor bo mappings and unpins the cursor surface 64562306a36Sopenharmony_ci * 64662306a36Sopenharmony_ci * Returns 0 on success 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_civoid 64962306a36Sopenharmony_civmw_du_cursor_plane_cleanup_fb(struct drm_plane *plane, 65062306a36Sopenharmony_ci struct drm_plane_state *old_state) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct vmw_cursor_plane *vcp = vmw_plane_to_vcp(plane); 65362306a36Sopenharmony_ci struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (vps->surf_mapped) { 65662306a36Sopenharmony_ci vmw_bo_unmap(vps->surf->res.guest_memory_bo); 65762306a36Sopenharmony_ci vps->surf_mapped = false; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci vmw_du_cursor_plane_unmap_cm(vps); 66162306a36Sopenharmony_ci vmw_du_put_cursor_mob(vcp, vps); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci vmw_du_plane_unpin_surf(vps, false); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (vps->surf) { 66662306a36Sopenharmony_ci vmw_surface_unreference(&vps->surf); 66762306a36Sopenharmony_ci vps->surf = NULL; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (vps->bo) { 67162306a36Sopenharmony_ci vmw_bo_unreference(&vps->bo); 67262306a36Sopenharmony_ci vps->bo = NULL; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci/** 67862306a36Sopenharmony_ci * vmw_du_cursor_plane_prepare_fb - Readies the cursor by referencing it 67962306a36Sopenharmony_ci * 68062306a36Sopenharmony_ci * @plane: display plane 68162306a36Sopenharmony_ci * @new_state: info on the new plane state, including the FB 68262306a36Sopenharmony_ci * 68362306a36Sopenharmony_ci * Returns 0 on success 68462306a36Sopenharmony_ci */ 68562306a36Sopenharmony_ciint 68662306a36Sopenharmony_civmw_du_cursor_plane_prepare_fb(struct drm_plane *plane, 68762306a36Sopenharmony_ci struct drm_plane_state *new_state) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct drm_framebuffer *fb = new_state->fb; 69062306a36Sopenharmony_ci struct vmw_cursor_plane *vcp = vmw_plane_to_vcp(plane); 69162306a36Sopenharmony_ci struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); 69262306a36Sopenharmony_ci int ret = 0; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if (vps->surf) { 69562306a36Sopenharmony_ci if (vps->surf_mapped) { 69662306a36Sopenharmony_ci vmw_bo_unmap(vps->surf->res.guest_memory_bo); 69762306a36Sopenharmony_ci vps->surf_mapped = false; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci vmw_surface_unreference(&vps->surf); 70062306a36Sopenharmony_ci vps->surf = NULL; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (vps->bo) { 70462306a36Sopenharmony_ci vmw_bo_unreference(&vps->bo); 70562306a36Sopenharmony_ci vps->bo = NULL; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (fb) { 70962306a36Sopenharmony_ci if (vmw_framebuffer_to_vfb(fb)->bo) { 71062306a36Sopenharmony_ci vps->bo = vmw_framebuffer_to_vfbd(fb)->buffer; 71162306a36Sopenharmony_ci vmw_bo_reference(vps->bo); 71262306a36Sopenharmony_ci } else { 71362306a36Sopenharmony_ci vps->surf = vmw_framebuffer_to_vfbs(fb)->surface; 71462306a36Sopenharmony_ci vmw_surface_reference(vps->surf); 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (!vps->surf && vps->bo) { 71962306a36Sopenharmony_ci const u32 size = new_state->crtc_w * new_state->crtc_h * sizeof(u32); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci /* 72262306a36Sopenharmony_ci * Not using vmw_bo_map_and_cache() helper here as we need to 72362306a36Sopenharmony_ci * reserve the ttm_buffer_object first which 72462306a36Sopenharmony_ci * vmw_bo_map_and_cache() omits. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ci ret = ttm_bo_reserve(&vps->bo->tbo, true, false, NULL); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if (unlikely(ret != 0)) 72962306a36Sopenharmony_ci return -ENOMEM; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci ret = ttm_bo_kmap(&vps->bo->tbo, 0, PFN_UP(size), &vps->bo->map); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci ttm_bo_unreserve(&vps->bo->tbo); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (unlikely(ret != 0)) 73662306a36Sopenharmony_ci return -ENOMEM; 73762306a36Sopenharmony_ci } else if (vps->surf && !vps->bo && vps->surf->res.guest_memory_bo) { 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci WARN_ON(vps->surf->snooper.image); 74062306a36Sopenharmony_ci ret = ttm_bo_reserve(&vps->surf->res.guest_memory_bo->tbo, true, false, 74162306a36Sopenharmony_ci NULL); 74262306a36Sopenharmony_ci if (unlikely(ret != 0)) 74362306a36Sopenharmony_ci return -ENOMEM; 74462306a36Sopenharmony_ci vmw_bo_map_and_cache(vps->surf->res.guest_memory_bo); 74562306a36Sopenharmony_ci ttm_bo_unreserve(&vps->surf->res.guest_memory_bo->tbo); 74662306a36Sopenharmony_ci vps->surf_mapped = true; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (vps->surf || vps->bo) { 75062306a36Sopenharmony_ci vmw_du_get_cursor_mob(vcp, vps); 75162306a36Sopenharmony_ci vmw_du_cursor_plane_map_cm(vps); 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci return 0; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_civoid 75962306a36Sopenharmony_civmw_du_cursor_plane_atomic_update(struct drm_plane *plane, 76062306a36Sopenharmony_ci struct drm_atomic_state *state) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 76362306a36Sopenharmony_ci plane); 76462306a36Sopenharmony_ci struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 76562306a36Sopenharmony_ci plane); 76662306a36Sopenharmony_ci struct drm_crtc *crtc = new_state->crtc ?: old_state->crtc; 76762306a36Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(crtc->dev); 76862306a36Sopenharmony_ci struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 76962306a36Sopenharmony_ci struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); 77062306a36Sopenharmony_ci struct vmw_plane_state *old_vps = vmw_plane_state_to_vps(old_state); 77162306a36Sopenharmony_ci s32 hotspot_x, hotspot_y; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci hotspot_x = du->hotspot_x; 77462306a36Sopenharmony_ci hotspot_y = du->hotspot_y; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (new_state->fb) { 77762306a36Sopenharmony_ci hotspot_x += new_state->fb->hot_x; 77862306a36Sopenharmony_ci hotspot_y += new_state->fb->hot_y; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci du->cursor_surface = vps->surf; 78262306a36Sopenharmony_ci du->cursor_bo = vps->bo; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (!vps->surf && !vps->bo) { 78562306a36Sopenharmony_ci vmw_cursor_update_position(dev_priv, false, 0, 0); 78662306a36Sopenharmony_ci return; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci vps->cursor.hotspot_x = hotspot_x; 79062306a36Sopenharmony_ci vps->cursor.hotspot_y = hotspot_y; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (vps->surf) { 79362306a36Sopenharmony_ci du->cursor_age = du->cursor_surface->snooper.age; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (!vmw_du_cursor_plane_has_changed(old_vps, vps)) { 79762306a36Sopenharmony_ci /* 79862306a36Sopenharmony_ci * If it hasn't changed, avoid making the device do extra 79962306a36Sopenharmony_ci * work by keeping the old cursor active. 80062306a36Sopenharmony_ci */ 80162306a36Sopenharmony_ci struct vmw_cursor_plane_state tmp = old_vps->cursor; 80262306a36Sopenharmony_ci old_vps->cursor = vps->cursor; 80362306a36Sopenharmony_ci vps->cursor = tmp; 80462306a36Sopenharmony_ci } else { 80562306a36Sopenharmony_ci void *image = vmw_du_cursor_plane_acquire_image(vps); 80662306a36Sopenharmony_ci if (image) 80762306a36Sopenharmony_ci vmw_cursor_update_image(dev_priv, vps, image, 80862306a36Sopenharmony_ci new_state->crtc_w, 80962306a36Sopenharmony_ci new_state->crtc_h, 81062306a36Sopenharmony_ci hotspot_x, hotspot_y); 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci du->cursor_x = new_state->crtc_x + du->set_gui_x; 81462306a36Sopenharmony_ci du->cursor_y = new_state->crtc_y + du->set_gui_y; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci vmw_cursor_update_position(dev_priv, true, 81762306a36Sopenharmony_ci du->cursor_x + hotspot_x, 81862306a36Sopenharmony_ci du->cursor_y + hotspot_y); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci du->core_hotspot_x = hotspot_x - du->hotspot_x; 82162306a36Sopenharmony_ci du->core_hotspot_y = hotspot_y - du->hotspot_y; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci/** 82662306a36Sopenharmony_ci * vmw_du_primary_plane_atomic_check - check if the new state is okay 82762306a36Sopenharmony_ci * 82862306a36Sopenharmony_ci * @plane: display plane 82962306a36Sopenharmony_ci * @state: info on the new plane state, including the FB 83062306a36Sopenharmony_ci * 83162306a36Sopenharmony_ci * Check if the new state is settable given the current state. Other 83262306a36Sopenharmony_ci * than what the atomic helper checks, we care about crtc fitting 83362306a36Sopenharmony_ci * the FB and maintaining one active framebuffer. 83462306a36Sopenharmony_ci * 83562306a36Sopenharmony_ci * Returns 0 on success 83662306a36Sopenharmony_ci */ 83762306a36Sopenharmony_ciint vmw_du_primary_plane_atomic_check(struct drm_plane *plane, 83862306a36Sopenharmony_ci struct drm_atomic_state *state) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 84162306a36Sopenharmony_ci plane); 84262306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = NULL; 84362306a36Sopenharmony_ci struct drm_framebuffer *new_fb = new_state->fb; 84462306a36Sopenharmony_ci int ret; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (new_state->crtc) 84762306a36Sopenharmony_ci crtc_state = drm_atomic_get_new_crtc_state(state, 84862306a36Sopenharmony_ci new_state->crtc); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci ret = drm_atomic_helper_check_plane_state(new_state, crtc_state, 85162306a36Sopenharmony_ci DRM_PLANE_NO_SCALING, 85262306a36Sopenharmony_ci DRM_PLANE_NO_SCALING, 85362306a36Sopenharmony_ci false, true); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (!ret && new_fb) { 85662306a36Sopenharmony_ci struct drm_crtc *crtc = new_state->crtc; 85762306a36Sopenharmony_ci struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci vmw_connector_state_to_vcs(du->connector.state); 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci return ret; 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci/** 86862306a36Sopenharmony_ci * vmw_du_cursor_plane_atomic_check - check if the new state is okay 86962306a36Sopenharmony_ci * 87062306a36Sopenharmony_ci * @plane: cursor plane 87162306a36Sopenharmony_ci * @state: info on the new plane state 87262306a36Sopenharmony_ci * 87362306a36Sopenharmony_ci * This is a chance to fail if the new cursor state does not fit 87462306a36Sopenharmony_ci * our requirements. 87562306a36Sopenharmony_ci * 87662306a36Sopenharmony_ci * Returns 0 on success 87762306a36Sopenharmony_ci */ 87862306a36Sopenharmony_ciint vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, 87962306a36Sopenharmony_ci struct drm_atomic_state *state) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 88262306a36Sopenharmony_ci plane); 88362306a36Sopenharmony_ci int ret = 0; 88462306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = NULL; 88562306a36Sopenharmony_ci struct vmw_surface *surface = NULL; 88662306a36Sopenharmony_ci struct drm_framebuffer *fb = new_state->fb; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (new_state->crtc) 88962306a36Sopenharmony_ci crtc_state = drm_atomic_get_new_crtc_state(new_state->state, 89062306a36Sopenharmony_ci new_state->crtc); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci ret = drm_atomic_helper_check_plane_state(new_state, crtc_state, 89362306a36Sopenharmony_ci DRM_PLANE_NO_SCALING, 89462306a36Sopenharmony_ci DRM_PLANE_NO_SCALING, 89562306a36Sopenharmony_ci true, true); 89662306a36Sopenharmony_ci if (ret) 89762306a36Sopenharmony_ci return ret; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci /* Turning off */ 90062306a36Sopenharmony_ci if (!fb) 90162306a36Sopenharmony_ci return 0; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci /* A lot of the code assumes this */ 90462306a36Sopenharmony_ci if (new_state->crtc_w != 64 || new_state->crtc_h != 64) { 90562306a36Sopenharmony_ci DRM_ERROR("Invalid cursor dimensions (%d, %d)\n", 90662306a36Sopenharmony_ci new_state->crtc_w, new_state->crtc_h); 90762306a36Sopenharmony_ci return -EINVAL; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci if (!vmw_framebuffer_to_vfb(fb)->bo) { 91162306a36Sopenharmony_ci surface = vmw_framebuffer_to_vfbs(fb)->surface; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci WARN_ON(!surface); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (!surface || 91662306a36Sopenharmony_ci (!surface->snooper.image && !surface->res.guest_memory_bo)) { 91762306a36Sopenharmony_ci DRM_ERROR("surface not suitable for cursor\n"); 91862306a36Sopenharmony_ci return -EINVAL; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci return 0; 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ciint vmw_du_crtc_atomic_check(struct drm_crtc *crtc, 92762306a36Sopenharmony_ci struct drm_atomic_state *state) 92862306a36Sopenharmony_ci{ 92962306a36Sopenharmony_ci struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state, 93062306a36Sopenharmony_ci crtc); 93162306a36Sopenharmony_ci struct vmw_display_unit *du = vmw_crtc_to_du(new_state->crtc); 93262306a36Sopenharmony_ci int connector_mask = drm_connector_mask(&du->connector); 93362306a36Sopenharmony_ci bool has_primary = new_state->plane_mask & 93462306a36Sopenharmony_ci drm_plane_mask(crtc->primary); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci /* We always want to have an active plane with an active CRTC */ 93762306a36Sopenharmony_ci if (has_primary != new_state->enable) 93862306a36Sopenharmony_ci return -EINVAL; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci if (new_state->connector_mask != connector_mask && 94262306a36Sopenharmony_ci new_state->connector_mask != 0) { 94362306a36Sopenharmony_ci DRM_ERROR("Invalid connectors configuration\n"); 94462306a36Sopenharmony_ci return -EINVAL; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci /* 94862306a36Sopenharmony_ci * Our virtual device does not have a dot clock, so use the logical 94962306a36Sopenharmony_ci * clock value as the dot clock. 95062306a36Sopenharmony_ci */ 95162306a36Sopenharmony_ci if (new_state->mode.crtc_clock == 0) 95262306a36Sopenharmony_ci new_state->adjusted_mode.crtc_clock = new_state->mode.clock; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci return 0; 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_civoid vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, 95962306a36Sopenharmony_ci struct drm_atomic_state *state) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_civoid vmw_du_crtc_atomic_flush(struct drm_crtc *crtc, 96562306a36Sopenharmony_ci struct drm_atomic_state *state) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci/** 97162306a36Sopenharmony_ci * vmw_du_crtc_duplicate_state - duplicate crtc state 97262306a36Sopenharmony_ci * @crtc: DRM crtc 97362306a36Sopenharmony_ci * 97462306a36Sopenharmony_ci * Allocates and returns a copy of the crtc state (both common and 97562306a36Sopenharmony_ci * vmw-specific) for the specified crtc. 97662306a36Sopenharmony_ci * 97762306a36Sopenharmony_ci * Returns: The newly allocated crtc state, or NULL on failure. 97862306a36Sopenharmony_ci */ 97962306a36Sopenharmony_cistruct drm_crtc_state * 98062306a36Sopenharmony_civmw_du_crtc_duplicate_state(struct drm_crtc *crtc) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci struct drm_crtc_state *state; 98362306a36Sopenharmony_ci struct vmw_crtc_state *vcs; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci if (WARN_ON(!crtc->state)) 98662306a36Sopenharmony_ci return NULL; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci vcs = kmemdup(crtc->state, sizeof(*vcs), GFP_KERNEL); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (!vcs) 99162306a36Sopenharmony_ci return NULL; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci state = &vcs->base; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci __drm_atomic_helper_crtc_duplicate_state(crtc, state); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci return state; 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci/** 100262306a36Sopenharmony_ci * vmw_du_crtc_reset - creates a blank vmw crtc state 100362306a36Sopenharmony_ci * @crtc: DRM crtc 100462306a36Sopenharmony_ci * 100562306a36Sopenharmony_ci * Resets the atomic state for @crtc by freeing the state pointer (which 100662306a36Sopenharmony_ci * might be NULL, e.g. at driver load time) and allocating a new empty state 100762306a36Sopenharmony_ci * object. 100862306a36Sopenharmony_ci */ 100962306a36Sopenharmony_civoid vmw_du_crtc_reset(struct drm_crtc *crtc) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci struct vmw_crtc_state *vcs; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci if (crtc->state) { 101562306a36Sopenharmony_ci __drm_atomic_helper_crtc_destroy_state(crtc->state); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci kfree(vmw_crtc_state_to_vcs(crtc->state)); 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci vcs = kzalloc(sizeof(*vcs), GFP_KERNEL); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci if (!vcs) { 102362306a36Sopenharmony_ci DRM_ERROR("Cannot allocate vmw_crtc_state\n"); 102462306a36Sopenharmony_ci return; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, &vcs->base); 102862306a36Sopenharmony_ci} 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci/** 103262306a36Sopenharmony_ci * vmw_du_crtc_destroy_state - destroy crtc state 103362306a36Sopenharmony_ci * @crtc: DRM crtc 103462306a36Sopenharmony_ci * @state: state object to destroy 103562306a36Sopenharmony_ci * 103662306a36Sopenharmony_ci * Destroys the crtc state (both common and vmw-specific) for the 103762306a36Sopenharmony_ci * specified plane. 103862306a36Sopenharmony_ci */ 103962306a36Sopenharmony_civoid 104062306a36Sopenharmony_civmw_du_crtc_destroy_state(struct drm_crtc *crtc, 104162306a36Sopenharmony_ci struct drm_crtc_state *state) 104262306a36Sopenharmony_ci{ 104362306a36Sopenharmony_ci drm_atomic_helper_crtc_destroy_state(crtc, state); 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci/** 104862306a36Sopenharmony_ci * vmw_du_plane_duplicate_state - duplicate plane state 104962306a36Sopenharmony_ci * @plane: drm plane 105062306a36Sopenharmony_ci * 105162306a36Sopenharmony_ci * Allocates and returns a copy of the plane state (both common and 105262306a36Sopenharmony_ci * vmw-specific) for the specified plane. 105362306a36Sopenharmony_ci * 105462306a36Sopenharmony_ci * Returns: The newly allocated plane state, or NULL on failure. 105562306a36Sopenharmony_ci */ 105662306a36Sopenharmony_cistruct drm_plane_state * 105762306a36Sopenharmony_civmw_du_plane_duplicate_state(struct drm_plane *plane) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci struct drm_plane_state *state; 106062306a36Sopenharmony_ci struct vmw_plane_state *vps; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci vps = kmemdup(plane->state, sizeof(*vps), GFP_KERNEL); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (!vps) 106562306a36Sopenharmony_ci return NULL; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci vps->pinned = 0; 106862306a36Sopenharmony_ci vps->cpp = 0; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci memset(&vps->cursor, 0, sizeof(vps->cursor)); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci /* Each ref counted resource needs to be acquired again */ 107362306a36Sopenharmony_ci if (vps->surf) 107462306a36Sopenharmony_ci (void) vmw_surface_reference(vps->surf); 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci if (vps->bo) 107762306a36Sopenharmony_ci (void) vmw_bo_reference(vps->bo); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci state = &vps->base; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci __drm_atomic_helper_plane_duplicate_state(plane, state); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci return state; 108462306a36Sopenharmony_ci} 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci/** 108862306a36Sopenharmony_ci * vmw_du_plane_reset - creates a blank vmw plane state 108962306a36Sopenharmony_ci * @plane: drm plane 109062306a36Sopenharmony_ci * 109162306a36Sopenharmony_ci * Resets the atomic state for @plane by freeing the state pointer (which might 109262306a36Sopenharmony_ci * be NULL, e.g. at driver load time) and allocating a new empty state object. 109362306a36Sopenharmony_ci */ 109462306a36Sopenharmony_civoid vmw_du_plane_reset(struct drm_plane *plane) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci struct vmw_plane_state *vps; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (plane->state) 109962306a36Sopenharmony_ci vmw_du_plane_destroy_state(plane, plane->state); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci vps = kzalloc(sizeof(*vps), GFP_KERNEL); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci if (!vps) { 110462306a36Sopenharmony_ci DRM_ERROR("Cannot allocate vmw_plane_state\n"); 110562306a36Sopenharmony_ci return; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci __drm_atomic_helper_plane_reset(plane, &vps->base); 110962306a36Sopenharmony_ci} 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci/** 111362306a36Sopenharmony_ci * vmw_du_plane_destroy_state - destroy plane state 111462306a36Sopenharmony_ci * @plane: DRM plane 111562306a36Sopenharmony_ci * @state: state object to destroy 111662306a36Sopenharmony_ci * 111762306a36Sopenharmony_ci * Destroys the plane state (both common and vmw-specific) for the 111862306a36Sopenharmony_ci * specified plane. 111962306a36Sopenharmony_ci */ 112062306a36Sopenharmony_civoid 112162306a36Sopenharmony_civmw_du_plane_destroy_state(struct drm_plane *plane, 112262306a36Sopenharmony_ci struct drm_plane_state *state) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci struct vmw_plane_state *vps = vmw_plane_state_to_vps(state); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci /* Should have been freed by cleanup_fb */ 112762306a36Sopenharmony_ci if (vps->surf) 112862306a36Sopenharmony_ci vmw_surface_unreference(&vps->surf); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci if (vps->bo) 113162306a36Sopenharmony_ci vmw_bo_unreference(&vps->bo); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci drm_atomic_helper_plane_destroy_state(plane, state); 113462306a36Sopenharmony_ci} 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci/** 113862306a36Sopenharmony_ci * vmw_du_connector_duplicate_state - duplicate connector state 113962306a36Sopenharmony_ci * @connector: DRM connector 114062306a36Sopenharmony_ci * 114162306a36Sopenharmony_ci * Allocates and returns a copy of the connector state (both common and 114262306a36Sopenharmony_ci * vmw-specific) for the specified connector. 114362306a36Sopenharmony_ci * 114462306a36Sopenharmony_ci * Returns: The newly allocated connector state, or NULL on failure. 114562306a36Sopenharmony_ci */ 114662306a36Sopenharmony_cistruct drm_connector_state * 114762306a36Sopenharmony_civmw_du_connector_duplicate_state(struct drm_connector *connector) 114862306a36Sopenharmony_ci{ 114962306a36Sopenharmony_ci struct drm_connector_state *state; 115062306a36Sopenharmony_ci struct vmw_connector_state *vcs; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci if (WARN_ON(!connector->state)) 115362306a36Sopenharmony_ci return NULL; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci vcs = kmemdup(connector->state, sizeof(*vcs), GFP_KERNEL); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci if (!vcs) 115862306a36Sopenharmony_ci return NULL; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci state = &vcs->base; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci __drm_atomic_helper_connector_duplicate_state(connector, state); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci return state; 116562306a36Sopenharmony_ci} 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci/** 116962306a36Sopenharmony_ci * vmw_du_connector_reset - creates a blank vmw connector state 117062306a36Sopenharmony_ci * @connector: DRM connector 117162306a36Sopenharmony_ci * 117262306a36Sopenharmony_ci * Resets the atomic state for @connector by freeing the state pointer (which 117362306a36Sopenharmony_ci * might be NULL, e.g. at driver load time) and allocating a new empty state 117462306a36Sopenharmony_ci * object. 117562306a36Sopenharmony_ci */ 117662306a36Sopenharmony_civoid vmw_du_connector_reset(struct drm_connector *connector) 117762306a36Sopenharmony_ci{ 117862306a36Sopenharmony_ci struct vmw_connector_state *vcs; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci if (connector->state) { 118262306a36Sopenharmony_ci __drm_atomic_helper_connector_destroy_state(connector->state); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci kfree(vmw_connector_state_to_vcs(connector->state)); 118562306a36Sopenharmony_ci } 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci vcs = kzalloc(sizeof(*vcs), GFP_KERNEL); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci if (!vcs) { 119062306a36Sopenharmony_ci DRM_ERROR("Cannot allocate vmw_connector_state\n"); 119162306a36Sopenharmony_ci return; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci __drm_atomic_helper_connector_reset(connector, &vcs->base); 119562306a36Sopenharmony_ci} 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci/** 119962306a36Sopenharmony_ci * vmw_du_connector_destroy_state - destroy connector state 120062306a36Sopenharmony_ci * @connector: DRM connector 120162306a36Sopenharmony_ci * @state: state object to destroy 120262306a36Sopenharmony_ci * 120362306a36Sopenharmony_ci * Destroys the connector state (both common and vmw-specific) for the 120462306a36Sopenharmony_ci * specified plane. 120562306a36Sopenharmony_ci */ 120662306a36Sopenharmony_civoid 120762306a36Sopenharmony_civmw_du_connector_destroy_state(struct drm_connector *connector, 120862306a36Sopenharmony_ci struct drm_connector_state *state) 120962306a36Sopenharmony_ci{ 121062306a36Sopenharmony_ci drm_atomic_helper_connector_destroy_state(connector, state); 121162306a36Sopenharmony_ci} 121262306a36Sopenharmony_ci/* 121362306a36Sopenharmony_ci * Generic framebuffer code 121462306a36Sopenharmony_ci */ 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci/* 121762306a36Sopenharmony_ci * Surface framebuffer code 121862306a36Sopenharmony_ci */ 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_cistatic void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer) 122162306a36Sopenharmony_ci{ 122262306a36Sopenharmony_ci struct vmw_framebuffer_surface *vfbs = 122362306a36Sopenharmony_ci vmw_framebuffer_to_vfbs(framebuffer); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci drm_framebuffer_cleanup(framebuffer); 122662306a36Sopenharmony_ci vmw_surface_unreference(&vfbs->surface); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci kfree(vfbs); 122962306a36Sopenharmony_ci} 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci/** 123262306a36Sopenharmony_ci * vmw_kms_readback - Perform a readback from the screen system to 123362306a36Sopenharmony_ci * a buffer-object backed framebuffer. 123462306a36Sopenharmony_ci * 123562306a36Sopenharmony_ci * @dev_priv: Pointer to the device private structure. 123662306a36Sopenharmony_ci * @file_priv: Pointer to a struct drm_file identifying the caller. 123762306a36Sopenharmony_ci * Must be set to NULL if @user_fence_rep is NULL. 123862306a36Sopenharmony_ci * @vfb: Pointer to the buffer-object backed framebuffer. 123962306a36Sopenharmony_ci * @user_fence_rep: User-space provided structure for fence information. 124062306a36Sopenharmony_ci * Must be set to non-NULL if @file_priv is non-NULL. 124162306a36Sopenharmony_ci * @vclips: Array of clip rects. 124262306a36Sopenharmony_ci * @num_clips: Number of clip rects in @vclips. 124362306a36Sopenharmony_ci * 124462306a36Sopenharmony_ci * Returns 0 on success, negative error code on failure. -ERESTARTSYS if 124562306a36Sopenharmony_ci * interrupted. 124662306a36Sopenharmony_ci */ 124762306a36Sopenharmony_ciint vmw_kms_readback(struct vmw_private *dev_priv, 124862306a36Sopenharmony_ci struct drm_file *file_priv, 124962306a36Sopenharmony_ci struct vmw_framebuffer *vfb, 125062306a36Sopenharmony_ci struct drm_vmw_fence_rep __user *user_fence_rep, 125162306a36Sopenharmony_ci struct drm_vmw_rect *vclips, 125262306a36Sopenharmony_ci uint32_t num_clips) 125362306a36Sopenharmony_ci{ 125462306a36Sopenharmony_ci switch (dev_priv->active_display_unit) { 125562306a36Sopenharmony_ci case vmw_du_screen_object: 125662306a36Sopenharmony_ci return vmw_kms_sou_readback(dev_priv, file_priv, vfb, 125762306a36Sopenharmony_ci user_fence_rep, vclips, num_clips, 125862306a36Sopenharmony_ci NULL); 125962306a36Sopenharmony_ci case vmw_du_screen_target: 126062306a36Sopenharmony_ci return vmw_kms_stdu_readback(dev_priv, file_priv, vfb, 126162306a36Sopenharmony_ci user_fence_rep, NULL, vclips, num_clips, 126262306a36Sopenharmony_ci 1, NULL); 126362306a36Sopenharmony_ci default: 126462306a36Sopenharmony_ci WARN_ONCE(true, 126562306a36Sopenharmony_ci "Readback called with invalid display system.\n"); 126662306a36Sopenharmony_ci} 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci return -ENOSYS; 126962306a36Sopenharmony_ci} 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_cistatic const struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = { 127362306a36Sopenharmony_ci .destroy = vmw_framebuffer_surface_destroy, 127462306a36Sopenharmony_ci .dirty = drm_atomic_helper_dirtyfb, 127562306a36Sopenharmony_ci}; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_cistatic int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, 127862306a36Sopenharmony_ci struct vmw_surface *surface, 127962306a36Sopenharmony_ci struct vmw_framebuffer **out, 128062306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 128162306a36Sopenharmony_ci *mode_cmd, 128262306a36Sopenharmony_ci bool is_bo_proxy) 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci{ 128562306a36Sopenharmony_ci struct drm_device *dev = &dev_priv->drm; 128662306a36Sopenharmony_ci struct vmw_framebuffer_surface *vfbs; 128762306a36Sopenharmony_ci enum SVGA3dSurfaceFormat format; 128862306a36Sopenharmony_ci int ret; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci /* 3D is only supported on HWv8 and newer hosts */ 129162306a36Sopenharmony_ci if (dev_priv->active_display_unit == vmw_du_legacy) 129262306a36Sopenharmony_ci return -ENOSYS; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci /* 129562306a36Sopenharmony_ci * Sanity checks. 129662306a36Sopenharmony_ci */ 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci if (!drm_any_plane_has_format(&dev_priv->drm, 129962306a36Sopenharmony_ci mode_cmd->pixel_format, 130062306a36Sopenharmony_ci mode_cmd->modifier[0])) { 130162306a36Sopenharmony_ci drm_dbg(&dev_priv->drm, 130262306a36Sopenharmony_ci "unsupported pixel format %p4cc / modifier 0x%llx\n", 130362306a36Sopenharmony_ci &mode_cmd->pixel_format, mode_cmd->modifier[0]); 130462306a36Sopenharmony_ci return -EINVAL; 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci /* Surface must be marked as a scanout. */ 130862306a36Sopenharmony_ci if (unlikely(!surface->metadata.scanout)) 130962306a36Sopenharmony_ci return -EINVAL; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci if (unlikely(surface->metadata.mip_levels[0] != 1 || 131262306a36Sopenharmony_ci surface->metadata.num_sizes != 1 || 131362306a36Sopenharmony_ci surface->metadata.base_size.width < mode_cmd->width || 131462306a36Sopenharmony_ci surface->metadata.base_size.height < mode_cmd->height || 131562306a36Sopenharmony_ci surface->metadata.base_size.depth != 1)) { 131662306a36Sopenharmony_ci DRM_ERROR("Incompatible surface dimensions " 131762306a36Sopenharmony_ci "for requested mode.\n"); 131862306a36Sopenharmony_ci return -EINVAL; 131962306a36Sopenharmony_ci } 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci switch (mode_cmd->pixel_format) { 132262306a36Sopenharmony_ci case DRM_FORMAT_ARGB8888: 132362306a36Sopenharmony_ci format = SVGA3D_A8R8G8B8; 132462306a36Sopenharmony_ci break; 132562306a36Sopenharmony_ci case DRM_FORMAT_XRGB8888: 132662306a36Sopenharmony_ci format = SVGA3D_X8R8G8B8; 132762306a36Sopenharmony_ci break; 132862306a36Sopenharmony_ci case DRM_FORMAT_RGB565: 132962306a36Sopenharmony_ci format = SVGA3D_R5G6B5; 133062306a36Sopenharmony_ci break; 133162306a36Sopenharmony_ci case DRM_FORMAT_XRGB1555: 133262306a36Sopenharmony_ci format = SVGA3D_A1R5G5B5; 133362306a36Sopenharmony_ci break; 133462306a36Sopenharmony_ci default: 133562306a36Sopenharmony_ci DRM_ERROR("Invalid pixel format: %p4cc\n", 133662306a36Sopenharmony_ci &mode_cmd->pixel_format); 133762306a36Sopenharmony_ci return -EINVAL; 133862306a36Sopenharmony_ci } 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci /* 134162306a36Sopenharmony_ci * For DX, surface format validation is done when surface->scanout 134262306a36Sopenharmony_ci * is set. 134362306a36Sopenharmony_ci */ 134462306a36Sopenharmony_ci if (!has_sm4_context(dev_priv) && format != surface->metadata.format) { 134562306a36Sopenharmony_ci DRM_ERROR("Invalid surface format for requested mode.\n"); 134662306a36Sopenharmony_ci return -EINVAL; 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci vfbs = kzalloc(sizeof(*vfbs), GFP_KERNEL); 135062306a36Sopenharmony_ci if (!vfbs) { 135162306a36Sopenharmony_ci ret = -ENOMEM; 135262306a36Sopenharmony_ci goto out_err1; 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci drm_helper_mode_fill_fb_struct(dev, &vfbs->base.base, mode_cmd); 135662306a36Sopenharmony_ci vfbs->surface = vmw_surface_reference(surface); 135762306a36Sopenharmony_ci vfbs->base.user_handle = mode_cmd->handles[0]; 135862306a36Sopenharmony_ci vfbs->is_bo_proxy = is_bo_proxy; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci *out = &vfbs->base; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci ret = drm_framebuffer_init(dev, &vfbs->base.base, 136362306a36Sopenharmony_ci &vmw_framebuffer_surface_funcs); 136462306a36Sopenharmony_ci if (ret) 136562306a36Sopenharmony_ci goto out_err2; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci return 0; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ciout_err2: 137062306a36Sopenharmony_ci vmw_surface_unreference(&surface); 137162306a36Sopenharmony_ci kfree(vfbs); 137262306a36Sopenharmony_ciout_err1: 137362306a36Sopenharmony_ci return ret; 137462306a36Sopenharmony_ci} 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci/* 137762306a36Sopenharmony_ci * Buffer-object framebuffer code 137862306a36Sopenharmony_ci */ 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_cistatic int vmw_framebuffer_bo_create_handle(struct drm_framebuffer *fb, 138162306a36Sopenharmony_ci struct drm_file *file_priv, 138262306a36Sopenharmony_ci unsigned int *handle) 138362306a36Sopenharmony_ci{ 138462306a36Sopenharmony_ci struct vmw_framebuffer_bo *vfbd = 138562306a36Sopenharmony_ci vmw_framebuffer_to_vfbd(fb); 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci return drm_gem_handle_create(file_priv, &vfbd->buffer->tbo.base, handle); 138862306a36Sopenharmony_ci} 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_cistatic void vmw_framebuffer_bo_destroy(struct drm_framebuffer *framebuffer) 139162306a36Sopenharmony_ci{ 139262306a36Sopenharmony_ci struct vmw_framebuffer_bo *vfbd = 139362306a36Sopenharmony_ci vmw_framebuffer_to_vfbd(framebuffer); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci drm_framebuffer_cleanup(framebuffer); 139662306a36Sopenharmony_ci vmw_bo_unreference(&vfbd->buffer); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci kfree(vfbd); 139962306a36Sopenharmony_ci} 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_cistatic const struct drm_framebuffer_funcs vmw_framebuffer_bo_funcs = { 140262306a36Sopenharmony_ci .create_handle = vmw_framebuffer_bo_create_handle, 140362306a36Sopenharmony_ci .destroy = vmw_framebuffer_bo_destroy, 140462306a36Sopenharmony_ci .dirty = drm_atomic_helper_dirtyfb, 140562306a36Sopenharmony_ci}; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci/** 140862306a36Sopenharmony_ci * vmw_create_bo_proxy - create a proxy surface for the buffer object 140962306a36Sopenharmony_ci * 141062306a36Sopenharmony_ci * @dev: DRM device 141162306a36Sopenharmony_ci * @mode_cmd: parameters for the new surface 141262306a36Sopenharmony_ci * @bo_mob: MOB backing the buffer object 141362306a36Sopenharmony_ci * @srf_out: newly created surface 141462306a36Sopenharmony_ci * 141562306a36Sopenharmony_ci * When the content FB is a buffer object, we create a surface as a proxy to the 141662306a36Sopenharmony_ci * same buffer. This way we can do a surface copy rather than a surface DMA. 141762306a36Sopenharmony_ci * This is a more efficient approach 141862306a36Sopenharmony_ci * 141962306a36Sopenharmony_ci * RETURNS: 142062306a36Sopenharmony_ci * 0 on success, error code otherwise 142162306a36Sopenharmony_ci */ 142262306a36Sopenharmony_cistatic int vmw_create_bo_proxy(struct drm_device *dev, 142362306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd, 142462306a36Sopenharmony_ci struct vmw_bo *bo_mob, 142562306a36Sopenharmony_ci struct vmw_surface **srf_out) 142662306a36Sopenharmony_ci{ 142762306a36Sopenharmony_ci struct vmw_surface_metadata metadata = {0}; 142862306a36Sopenharmony_ci uint32_t format; 142962306a36Sopenharmony_ci struct vmw_resource *res; 143062306a36Sopenharmony_ci unsigned int bytes_pp; 143162306a36Sopenharmony_ci int ret; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci switch (mode_cmd->pixel_format) { 143462306a36Sopenharmony_ci case DRM_FORMAT_ARGB8888: 143562306a36Sopenharmony_ci case DRM_FORMAT_XRGB8888: 143662306a36Sopenharmony_ci format = SVGA3D_X8R8G8B8; 143762306a36Sopenharmony_ci bytes_pp = 4; 143862306a36Sopenharmony_ci break; 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci case DRM_FORMAT_RGB565: 144162306a36Sopenharmony_ci case DRM_FORMAT_XRGB1555: 144262306a36Sopenharmony_ci format = SVGA3D_R5G6B5; 144362306a36Sopenharmony_ci bytes_pp = 2; 144462306a36Sopenharmony_ci break; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci case 8: 144762306a36Sopenharmony_ci format = SVGA3D_P8; 144862306a36Sopenharmony_ci bytes_pp = 1; 144962306a36Sopenharmony_ci break; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci default: 145262306a36Sopenharmony_ci DRM_ERROR("Invalid framebuffer format %p4cc\n", 145362306a36Sopenharmony_ci &mode_cmd->pixel_format); 145462306a36Sopenharmony_ci return -EINVAL; 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci metadata.format = format; 145862306a36Sopenharmony_ci metadata.mip_levels[0] = 1; 145962306a36Sopenharmony_ci metadata.num_sizes = 1; 146062306a36Sopenharmony_ci metadata.base_size.width = mode_cmd->pitches[0] / bytes_pp; 146162306a36Sopenharmony_ci metadata.base_size.height = mode_cmd->height; 146262306a36Sopenharmony_ci metadata.base_size.depth = 1; 146362306a36Sopenharmony_ci metadata.scanout = true; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci ret = vmw_gb_surface_define(vmw_priv(dev), &metadata, srf_out); 146662306a36Sopenharmony_ci if (ret) { 146762306a36Sopenharmony_ci DRM_ERROR("Failed to allocate proxy content buffer\n"); 146862306a36Sopenharmony_ci return ret; 146962306a36Sopenharmony_ci } 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci res = &(*srf_out)->res; 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci /* Reserve and switch the backing mob. */ 147462306a36Sopenharmony_ci mutex_lock(&res->dev_priv->cmdbuf_mutex); 147562306a36Sopenharmony_ci (void) vmw_resource_reserve(res, false, true); 147662306a36Sopenharmony_ci vmw_user_bo_unref(&res->guest_memory_bo); 147762306a36Sopenharmony_ci res->guest_memory_bo = vmw_user_bo_ref(bo_mob); 147862306a36Sopenharmony_ci res->guest_memory_offset = 0; 147962306a36Sopenharmony_ci vmw_resource_unreserve(res, false, false, false, NULL, 0); 148062306a36Sopenharmony_ci mutex_unlock(&res->dev_priv->cmdbuf_mutex); 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci return 0; 148362306a36Sopenharmony_ci} 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_cistatic int vmw_kms_new_framebuffer_bo(struct vmw_private *dev_priv, 148862306a36Sopenharmony_ci struct vmw_bo *bo, 148962306a36Sopenharmony_ci struct vmw_framebuffer **out, 149062306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 149162306a36Sopenharmony_ci *mode_cmd) 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci{ 149462306a36Sopenharmony_ci struct drm_device *dev = &dev_priv->drm; 149562306a36Sopenharmony_ci struct vmw_framebuffer_bo *vfbd; 149662306a36Sopenharmony_ci unsigned int requested_size; 149762306a36Sopenharmony_ci int ret; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci requested_size = mode_cmd->height * mode_cmd->pitches[0]; 150062306a36Sopenharmony_ci if (unlikely(requested_size > bo->tbo.base.size)) { 150162306a36Sopenharmony_ci DRM_ERROR("Screen buffer object size is too small " 150262306a36Sopenharmony_ci "for requested mode.\n"); 150362306a36Sopenharmony_ci return -EINVAL; 150462306a36Sopenharmony_ci } 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci if (!drm_any_plane_has_format(&dev_priv->drm, 150762306a36Sopenharmony_ci mode_cmd->pixel_format, 150862306a36Sopenharmony_ci mode_cmd->modifier[0])) { 150962306a36Sopenharmony_ci drm_dbg(&dev_priv->drm, 151062306a36Sopenharmony_ci "unsupported pixel format %p4cc / modifier 0x%llx\n", 151162306a36Sopenharmony_ci &mode_cmd->pixel_format, mode_cmd->modifier[0]); 151262306a36Sopenharmony_ci return -EINVAL; 151362306a36Sopenharmony_ci } 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci vfbd = kzalloc(sizeof(*vfbd), GFP_KERNEL); 151662306a36Sopenharmony_ci if (!vfbd) { 151762306a36Sopenharmony_ci ret = -ENOMEM; 151862306a36Sopenharmony_ci goto out_err1; 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci vfbd->base.base.obj[0] = &bo->tbo.base; 152262306a36Sopenharmony_ci drm_helper_mode_fill_fb_struct(dev, &vfbd->base.base, mode_cmd); 152362306a36Sopenharmony_ci vfbd->base.bo = true; 152462306a36Sopenharmony_ci vfbd->buffer = vmw_bo_reference(bo); 152562306a36Sopenharmony_ci vfbd->base.user_handle = mode_cmd->handles[0]; 152662306a36Sopenharmony_ci *out = &vfbd->base; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci ret = drm_framebuffer_init(dev, &vfbd->base.base, 152962306a36Sopenharmony_ci &vmw_framebuffer_bo_funcs); 153062306a36Sopenharmony_ci if (ret) 153162306a36Sopenharmony_ci goto out_err2; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci return 0; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ciout_err2: 153662306a36Sopenharmony_ci vmw_bo_unreference(&bo); 153762306a36Sopenharmony_ci kfree(vfbd); 153862306a36Sopenharmony_ciout_err1: 153962306a36Sopenharmony_ci return ret; 154062306a36Sopenharmony_ci} 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci/** 154462306a36Sopenharmony_ci * vmw_kms_srf_ok - check if a surface can be created 154562306a36Sopenharmony_ci * 154662306a36Sopenharmony_ci * @dev_priv: Pointer to device private struct. 154762306a36Sopenharmony_ci * @width: requested width 154862306a36Sopenharmony_ci * @height: requested height 154962306a36Sopenharmony_ci * 155062306a36Sopenharmony_ci * Surfaces need to be less than texture size 155162306a36Sopenharmony_ci */ 155262306a36Sopenharmony_cistatic bool 155362306a36Sopenharmony_civmw_kms_srf_ok(struct vmw_private *dev_priv, uint32_t width, uint32_t height) 155462306a36Sopenharmony_ci{ 155562306a36Sopenharmony_ci if (width > dev_priv->texture_max_width || 155662306a36Sopenharmony_ci height > dev_priv->texture_max_height) 155762306a36Sopenharmony_ci return false; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci return true; 156062306a36Sopenharmony_ci} 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci/** 156362306a36Sopenharmony_ci * vmw_kms_new_framebuffer - Create a new framebuffer. 156462306a36Sopenharmony_ci * 156562306a36Sopenharmony_ci * @dev_priv: Pointer to device private struct. 156662306a36Sopenharmony_ci * @bo: Pointer to buffer object to wrap the kms framebuffer around. 156762306a36Sopenharmony_ci * Either @bo or @surface must be NULL. 156862306a36Sopenharmony_ci * @surface: Pointer to a surface to wrap the kms framebuffer around. 156962306a36Sopenharmony_ci * Either @bo or @surface must be NULL. 157062306a36Sopenharmony_ci * @only_2d: No presents will occur to this buffer object based framebuffer. 157162306a36Sopenharmony_ci * This helps the code to do some important optimizations. 157262306a36Sopenharmony_ci * @mode_cmd: Frame-buffer metadata. 157362306a36Sopenharmony_ci */ 157462306a36Sopenharmony_cistruct vmw_framebuffer * 157562306a36Sopenharmony_civmw_kms_new_framebuffer(struct vmw_private *dev_priv, 157662306a36Sopenharmony_ci struct vmw_bo *bo, 157762306a36Sopenharmony_ci struct vmw_surface *surface, 157862306a36Sopenharmony_ci bool only_2d, 157962306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 158062306a36Sopenharmony_ci{ 158162306a36Sopenharmony_ci struct vmw_framebuffer *vfb = NULL; 158262306a36Sopenharmony_ci bool is_bo_proxy = false; 158362306a36Sopenharmony_ci int ret; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci /* 158662306a36Sopenharmony_ci * We cannot use the SurfaceDMA command in an non-accelerated VM, 158762306a36Sopenharmony_ci * therefore, wrap the buffer object in a surface so we can use the 158862306a36Sopenharmony_ci * SurfaceCopy command. 158962306a36Sopenharmony_ci */ 159062306a36Sopenharmony_ci if (vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height) && 159162306a36Sopenharmony_ci bo && only_2d && 159262306a36Sopenharmony_ci mode_cmd->width > 64 && /* Don't create a proxy for cursor */ 159362306a36Sopenharmony_ci dev_priv->active_display_unit == vmw_du_screen_target) { 159462306a36Sopenharmony_ci ret = vmw_create_bo_proxy(&dev_priv->drm, mode_cmd, 159562306a36Sopenharmony_ci bo, &surface); 159662306a36Sopenharmony_ci if (ret) 159762306a36Sopenharmony_ci return ERR_PTR(ret); 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci is_bo_proxy = true; 160062306a36Sopenharmony_ci } 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci /* Create the new framebuffer depending one what we have */ 160362306a36Sopenharmony_ci if (surface) { 160462306a36Sopenharmony_ci ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb, 160562306a36Sopenharmony_ci mode_cmd, 160662306a36Sopenharmony_ci is_bo_proxy); 160762306a36Sopenharmony_ci /* 160862306a36Sopenharmony_ci * vmw_create_bo_proxy() adds a reference that is no longer 160962306a36Sopenharmony_ci * needed 161062306a36Sopenharmony_ci */ 161162306a36Sopenharmony_ci if (is_bo_proxy) 161262306a36Sopenharmony_ci vmw_surface_unreference(&surface); 161362306a36Sopenharmony_ci } else if (bo) { 161462306a36Sopenharmony_ci ret = vmw_kms_new_framebuffer_bo(dev_priv, bo, &vfb, 161562306a36Sopenharmony_ci mode_cmd); 161662306a36Sopenharmony_ci } else { 161762306a36Sopenharmony_ci BUG(); 161862306a36Sopenharmony_ci } 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci if (ret) 162162306a36Sopenharmony_ci return ERR_PTR(ret); 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci return vfb; 162462306a36Sopenharmony_ci} 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci/* 162762306a36Sopenharmony_ci * Generic Kernel modesetting functions 162862306a36Sopenharmony_ci */ 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_cistatic struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, 163162306a36Sopenharmony_ci struct drm_file *file_priv, 163262306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 163362306a36Sopenharmony_ci{ 163462306a36Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 163562306a36Sopenharmony_ci struct vmw_framebuffer *vfb = NULL; 163662306a36Sopenharmony_ci struct vmw_surface *surface = NULL; 163762306a36Sopenharmony_ci struct vmw_bo *bo = NULL; 163862306a36Sopenharmony_ci int ret; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci /* returns either a bo or surface */ 164162306a36Sopenharmony_ci ret = vmw_user_lookup_handle(dev_priv, file_priv, 164262306a36Sopenharmony_ci mode_cmd->handles[0], 164362306a36Sopenharmony_ci &surface, &bo); 164462306a36Sopenharmony_ci if (ret) { 164562306a36Sopenharmony_ci DRM_ERROR("Invalid buffer object handle %u (0x%x).\n", 164662306a36Sopenharmony_ci mode_cmd->handles[0], mode_cmd->handles[0]); 164762306a36Sopenharmony_ci goto err_out; 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci if (!bo && 165262306a36Sopenharmony_ci !vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height)) { 165362306a36Sopenharmony_ci DRM_ERROR("Surface size cannot exceed %dx%d\n", 165462306a36Sopenharmony_ci dev_priv->texture_max_width, 165562306a36Sopenharmony_ci dev_priv->texture_max_height); 165662306a36Sopenharmony_ci goto err_out; 165762306a36Sopenharmony_ci } 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci vfb = vmw_kms_new_framebuffer(dev_priv, bo, surface, 166162306a36Sopenharmony_ci !(dev_priv->capabilities & SVGA_CAP_3D), 166262306a36Sopenharmony_ci mode_cmd); 166362306a36Sopenharmony_ci if (IS_ERR(vfb)) { 166462306a36Sopenharmony_ci ret = PTR_ERR(vfb); 166562306a36Sopenharmony_ci goto err_out; 166662306a36Sopenharmony_ci } 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_cierr_out: 166962306a36Sopenharmony_ci /* vmw_user_lookup_handle takes one ref so does new_fb */ 167062306a36Sopenharmony_ci if (bo) 167162306a36Sopenharmony_ci vmw_user_bo_unref(&bo); 167262306a36Sopenharmony_ci if (surface) 167362306a36Sopenharmony_ci vmw_surface_unreference(&surface); 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci if (ret) { 167662306a36Sopenharmony_ci DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret); 167762306a36Sopenharmony_ci return ERR_PTR(ret); 167862306a36Sopenharmony_ci } 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci return &vfb->base; 168162306a36Sopenharmony_ci} 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci/** 168462306a36Sopenharmony_ci * vmw_kms_check_display_memory - Validates display memory required for a 168562306a36Sopenharmony_ci * topology 168662306a36Sopenharmony_ci * @dev: DRM device 168762306a36Sopenharmony_ci * @num_rects: number of drm_rect in rects 168862306a36Sopenharmony_ci * @rects: array of drm_rect representing the topology to validate indexed by 168962306a36Sopenharmony_ci * crtc index. 169062306a36Sopenharmony_ci * 169162306a36Sopenharmony_ci * Returns: 169262306a36Sopenharmony_ci * 0 on success otherwise negative error code 169362306a36Sopenharmony_ci */ 169462306a36Sopenharmony_cistatic int vmw_kms_check_display_memory(struct drm_device *dev, 169562306a36Sopenharmony_ci uint32_t num_rects, 169662306a36Sopenharmony_ci struct drm_rect *rects) 169762306a36Sopenharmony_ci{ 169862306a36Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 169962306a36Sopenharmony_ci struct drm_rect bounding_box = {0}; 170062306a36Sopenharmony_ci u64 total_pixels = 0, pixel_mem, bb_mem; 170162306a36Sopenharmony_ci int i; 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci for (i = 0; i < num_rects; i++) { 170462306a36Sopenharmony_ci /* 170562306a36Sopenharmony_ci * For STDU only individual screen (screen target) is limited by 170662306a36Sopenharmony_ci * SCREENTARGET_MAX_WIDTH/HEIGHT registers. 170762306a36Sopenharmony_ci */ 170862306a36Sopenharmony_ci if (dev_priv->active_display_unit == vmw_du_screen_target && 170962306a36Sopenharmony_ci (drm_rect_width(&rects[i]) > dev_priv->stdu_max_width || 171062306a36Sopenharmony_ci drm_rect_height(&rects[i]) > dev_priv->stdu_max_height)) { 171162306a36Sopenharmony_ci VMW_DEBUG_KMS("Screen size not supported.\n"); 171262306a36Sopenharmony_ci return -EINVAL; 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci /* Bounding box upper left is at (0,0). */ 171662306a36Sopenharmony_ci if (rects[i].x2 > bounding_box.x2) 171762306a36Sopenharmony_ci bounding_box.x2 = rects[i].x2; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci if (rects[i].y2 > bounding_box.y2) 172062306a36Sopenharmony_ci bounding_box.y2 = rects[i].y2; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci total_pixels += (u64) drm_rect_width(&rects[i]) * 172362306a36Sopenharmony_ci (u64) drm_rect_height(&rects[i]); 172462306a36Sopenharmony_ci } 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci /* Virtual svga device primary limits are always in 32-bpp. */ 172762306a36Sopenharmony_ci pixel_mem = total_pixels * 4; 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci /* 173062306a36Sopenharmony_ci * For HV10 and below prim_bb_mem is vram size. When 173162306a36Sopenharmony_ci * SVGA_REG_MAX_PRIMARY_BOUNDING_BOX_MEM is not present vram size is 173262306a36Sopenharmony_ci * limit on primary bounding box 173362306a36Sopenharmony_ci */ 173462306a36Sopenharmony_ci if (pixel_mem > dev_priv->max_primary_mem) { 173562306a36Sopenharmony_ci VMW_DEBUG_KMS("Combined output size too large.\n"); 173662306a36Sopenharmony_ci return -EINVAL; 173762306a36Sopenharmony_ci } 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci /* SVGA_CAP_NO_BB_RESTRICTION is available for STDU only. */ 174062306a36Sopenharmony_ci if (dev_priv->active_display_unit != vmw_du_screen_target || 174162306a36Sopenharmony_ci !(dev_priv->capabilities & SVGA_CAP_NO_BB_RESTRICTION)) { 174262306a36Sopenharmony_ci bb_mem = (u64) bounding_box.x2 * bounding_box.y2 * 4; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci if (bb_mem > dev_priv->max_primary_mem) { 174562306a36Sopenharmony_ci VMW_DEBUG_KMS("Topology is beyond supported limits.\n"); 174662306a36Sopenharmony_ci return -EINVAL; 174762306a36Sopenharmony_ci } 174862306a36Sopenharmony_ci } 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci return 0; 175162306a36Sopenharmony_ci} 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci/** 175462306a36Sopenharmony_ci * vmw_crtc_state_and_lock - Return new or current crtc state with locked 175562306a36Sopenharmony_ci * crtc mutex 175662306a36Sopenharmony_ci * @state: The atomic state pointer containing the new atomic state 175762306a36Sopenharmony_ci * @crtc: The crtc 175862306a36Sopenharmony_ci * 175962306a36Sopenharmony_ci * This function returns the new crtc state if it's part of the state update. 176062306a36Sopenharmony_ci * Otherwise returns the current crtc state. It also makes sure that the 176162306a36Sopenharmony_ci * crtc mutex is locked. 176262306a36Sopenharmony_ci * 176362306a36Sopenharmony_ci * Returns: A valid crtc state pointer or NULL. It may also return a 176462306a36Sopenharmony_ci * pointer error, in particular -EDEADLK if locking needs to be rerun. 176562306a36Sopenharmony_ci */ 176662306a36Sopenharmony_cistatic struct drm_crtc_state * 176762306a36Sopenharmony_civmw_crtc_state_and_lock(struct drm_atomic_state *state, struct drm_crtc *crtc) 176862306a36Sopenharmony_ci{ 176962306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 177262306a36Sopenharmony_ci if (crtc_state) { 177362306a36Sopenharmony_ci lockdep_assert_held(&crtc->mutex.mutex.base); 177462306a36Sopenharmony_ci } else { 177562306a36Sopenharmony_ci int ret = drm_modeset_lock(&crtc->mutex, state->acquire_ctx); 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci if (ret != 0 && ret != -EALREADY) 177862306a36Sopenharmony_ci return ERR_PTR(ret); 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci crtc_state = crtc->state; 178162306a36Sopenharmony_ci } 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci return crtc_state; 178462306a36Sopenharmony_ci} 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci/** 178762306a36Sopenharmony_ci * vmw_kms_check_implicit - Verify that all implicit display units scan out 178862306a36Sopenharmony_ci * from the same fb after the new state is committed. 178962306a36Sopenharmony_ci * @dev: The drm_device. 179062306a36Sopenharmony_ci * @state: The new state to be checked. 179162306a36Sopenharmony_ci * 179262306a36Sopenharmony_ci * Returns: 179362306a36Sopenharmony_ci * Zero on success, 179462306a36Sopenharmony_ci * -EINVAL on invalid state, 179562306a36Sopenharmony_ci * -EDEADLK if modeset locking needs to be rerun. 179662306a36Sopenharmony_ci */ 179762306a36Sopenharmony_cistatic int vmw_kms_check_implicit(struct drm_device *dev, 179862306a36Sopenharmony_ci struct drm_atomic_state *state) 179962306a36Sopenharmony_ci{ 180062306a36Sopenharmony_ci struct drm_framebuffer *implicit_fb = NULL; 180162306a36Sopenharmony_ci struct drm_crtc *crtc; 180262306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 180362306a36Sopenharmony_ci struct drm_plane_state *plane_state; 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci drm_for_each_crtc(crtc, dev) { 180662306a36Sopenharmony_ci struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci if (!du->is_implicit) 180962306a36Sopenharmony_ci continue; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci crtc_state = vmw_crtc_state_and_lock(state, crtc); 181262306a36Sopenharmony_ci if (IS_ERR(crtc_state)) 181362306a36Sopenharmony_ci return PTR_ERR(crtc_state); 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci if (!crtc_state || !crtc_state->enable) 181662306a36Sopenharmony_ci continue; 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci /* 181962306a36Sopenharmony_ci * Can't move primary planes across crtcs, so this is OK. 182062306a36Sopenharmony_ci * It also means we don't need to take the plane mutex. 182162306a36Sopenharmony_ci */ 182262306a36Sopenharmony_ci plane_state = du->primary.state; 182362306a36Sopenharmony_ci if (plane_state->crtc != crtc) 182462306a36Sopenharmony_ci continue; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci if (!implicit_fb) 182762306a36Sopenharmony_ci implicit_fb = plane_state->fb; 182862306a36Sopenharmony_ci else if (implicit_fb != plane_state->fb) 182962306a36Sopenharmony_ci return -EINVAL; 183062306a36Sopenharmony_ci } 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci return 0; 183362306a36Sopenharmony_ci} 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci/** 183662306a36Sopenharmony_ci * vmw_kms_check_topology - Validates topology in drm_atomic_state 183762306a36Sopenharmony_ci * @dev: DRM device 183862306a36Sopenharmony_ci * @state: the driver state object 183962306a36Sopenharmony_ci * 184062306a36Sopenharmony_ci * Returns: 184162306a36Sopenharmony_ci * 0 on success otherwise negative error code 184262306a36Sopenharmony_ci */ 184362306a36Sopenharmony_cistatic int vmw_kms_check_topology(struct drm_device *dev, 184462306a36Sopenharmony_ci struct drm_atomic_state *state) 184562306a36Sopenharmony_ci{ 184662306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state, *new_crtc_state; 184762306a36Sopenharmony_ci struct drm_rect *rects; 184862306a36Sopenharmony_ci struct drm_crtc *crtc; 184962306a36Sopenharmony_ci uint32_t i; 185062306a36Sopenharmony_ci int ret = 0; 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci rects = kcalloc(dev->mode_config.num_crtc, sizeof(struct drm_rect), 185362306a36Sopenharmony_ci GFP_KERNEL); 185462306a36Sopenharmony_ci if (!rects) 185562306a36Sopenharmony_ci return -ENOMEM; 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci drm_for_each_crtc(crtc, dev) { 185862306a36Sopenharmony_ci struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 185962306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci i = drm_crtc_index(crtc); 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci crtc_state = vmw_crtc_state_and_lock(state, crtc); 186462306a36Sopenharmony_ci if (IS_ERR(crtc_state)) { 186562306a36Sopenharmony_ci ret = PTR_ERR(crtc_state); 186662306a36Sopenharmony_ci goto clean; 186762306a36Sopenharmony_ci } 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci if (!crtc_state) 187062306a36Sopenharmony_ci continue; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci if (crtc_state->enable) { 187362306a36Sopenharmony_ci rects[i].x1 = du->gui_x; 187462306a36Sopenharmony_ci rects[i].y1 = du->gui_y; 187562306a36Sopenharmony_ci rects[i].x2 = du->gui_x + crtc_state->mode.hdisplay; 187662306a36Sopenharmony_ci rects[i].y2 = du->gui_y + crtc_state->mode.vdisplay; 187762306a36Sopenharmony_ci } else { 187862306a36Sopenharmony_ci rects[i].x1 = 0; 187962306a36Sopenharmony_ci rects[i].y1 = 0; 188062306a36Sopenharmony_ci rects[i].x2 = 0; 188162306a36Sopenharmony_ci rects[i].y2 = 0; 188262306a36Sopenharmony_ci } 188362306a36Sopenharmony_ci } 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci /* Determine change to topology due to new atomic state */ 188662306a36Sopenharmony_ci for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, 188762306a36Sopenharmony_ci new_crtc_state, i) { 188862306a36Sopenharmony_ci struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 188962306a36Sopenharmony_ci struct drm_connector *connector; 189062306a36Sopenharmony_ci struct drm_connector_state *conn_state; 189162306a36Sopenharmony_ci struct vmw_connector_state *vmw_conn_state; 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci if (!du->pref_active && new_crtc_state->enable) { 189462306a36Sopenharmony_ci VMW_DEBUG_KMS("Enabling a disabled display unit\n"); 189562306a36Sopenharmony_ci ret = -EINVAL; 189662306a36Sopenharmony_ci goto clean; 189762306a36Sopenharmony_ci } 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci /* 190062306a36Sopenharmony_ci * For vmwgfx each crtc has only one connector attached and it 190162306a36Sopenharmony_ci * is not changed so don't really need to check the 190262306a36Sopenharmony_ci * crtc->connector_mask and iterate over it. 190362306a36Sopenharmony_ci */ 190462306a36Sopenharmony_ci connector = &du->connector; 190562306a36Sopenharmony_ci conn_state = drm_atomic_get_connector_state(state, connector); 190662306a36Sopenharmony_ci if (IS_ERR(conn_state)) { 190762306a36Sopenharmony_ci ret = PTR_ERR(conn_state); 190862306a36Sopenharmony_ci goto clean; 190962306a36Sopenharmony_ci } 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci vmw_conn_state = vmw_connector_state_to_vcs(conn_state); 191262306a36Sopenharmony_ci vmw_conn_state->gui_x = du->gui_x; 191362306a36Sopenharmony_ci vmw_conn_state->gui_y = du->gui_y; 191462306a36Sopenharmony_ci } 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci ret = vmw_kms_check_display_memory(dev, dev->mode_config.num_crtc, 191762306a36Sopenharmony_ci rects); 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ciclean: 192062306a36Sopenharmony_ci kfree(rects); 192162306a36Sopenharmony_ci return ret; 192262306a36Sopenharmony_ci} 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci/** 192562306a36Sopenharmony_ci * vmw_kms_atomic_check_modeset- validate state object for modeset changes 192662306a36Sopenharmony_ci * 192762306a36Sopenharmony_ci * @dev: DRM device 192862306a36Sopenharmony_ci * @state: the driver state object 192962306a36Sopenharmony_ci * 193062306a36Sopenharmony_ci * This is a simple wrapper around drm_atomic_helper_check_modeset() for 193162306a36Sopenharmony_ci * us to assign a value to mode->crtc_clock so that 193262306a36Sopenharmony_ci * drm_calc_timestamping_constants() won't throw an error message 193362306a36Sopenharmony_ci * 193462306a36Sopenharmony_ci * Returns: 193562306a36Sopenharmony_ci * Zero for success or -errno 193662306a36Sopenharmony_ci */ 193762306a36Sopenharmony_cistatic int 193862306a36Sopenharmony_civmw_kms_atomic_check_modeset(struct drm_device *dev, 193962306a36Sopenharmony_ci struct drm_atomic_state *state) 194062306a36Sopenharmony_ci{ 194162306a36Sopenharmony_ci struct drm_crtc *crtc; 194262306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 194362306a36Sopenharmony_ci bool need_modeset = false; 194462306a36Sopenharmony_ci int i, ret; 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci ret = drm_atomic_helper_check(dev, state); 194762306a36Sopenharmony_ci if (ret) 194862306a36Sopenharmony_ci return ret; 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci ret = vmw_kms_check_implicit(dev, state); 195162306a36Sopenharmony_ci if (ret) { 195262306a36Sopenharmony_ci VMW_DEBUG_KMS("Invalid implicit state\n"); 195362306a36Sopenharmony_ci return ret; 195462306a36Sopenharmony_ci } 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci for_each_new_crtc_in_state(state, crtc, crtc_state, i) { 195762306a36Sopenharmony_ci if (drm_atomic_crtc_needs_modeset(crtc_state)) 195862306a36Sopenharmony_ci need_modeset = true; 195962306a36Sopenharmony_ci } 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci if (need_modeset) 196262306a36Sopenharmony_ci return vmw_kms_check_topology(dev, state); 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci return ret; 196562306a36Sopenharmony_ci} 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_cistatic const struct drm_mode_config_funcs vmw_kms_funcs = { 196862306a36Sopenharmony_ci .fb_create = vmw_kms_fb_create, 196962306a36Sopenharmony_ci .atomic_check = vmw_kms_atomic_check_modeset, 197062306a36Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 197162306a36Sopenharmony_ci}; 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_cistatic int vmw_kms_generic_present(struct vmw_private *dev_priv, 197462306a36Sopenharmony_ci struct drm_file *file_priv, 197562306a36Sopenharmony_ci struct vmw_framebuffer *vfb, 197662306a36Sopenharmony_ci struct vmw_surface *surface, 197762306a36Sopenharmony_ci uint32_t sid, 197862306a36Sopenharmony_ci int32_t destX, int32_t destY, 197962306a36Sopenharmony_ci struct drm_vmw_rect *clips, 198062306a36Sopenharmony_ci uint32_t num_clips) 198162306a36Sopenharmony_ci{ 198262306a36Sopenharmony_ci return vmw_kms_sou_do_surface_dirty(dev_priv, vfb, NULL, clips, 198362306a36Sopenharmony_ci &surface->res, destX, destY, 198462306a36Sopenharmony_ci num_clips, 1, NULL, NULL); 198562306a36Sopenharmony_ci} 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ciint vmw_kms_present(struct vmw_private *dev_priv, 198962306a36Sopenharmony_ci struct drm_file *file_priv, 199062306a36Sopenharmony_ci struct vmw_framebuffer *vfb, 199162306a36Sopenharmony_ci struct vmw_surface *surface, 199262306a36Sopenharmony_ci uint32_t sid, 199362306a36Sopenharmony_ci int32_t destX, int32_t destY, 199462306a36Sopenharmony_ci struct drm_vmw_rect *clips, 199562306a36Sopenharmony_ci uint32_t num_clips) 199662306a36Sopenharmony_ci{ 199762306a36Sopenharmony_ci int ret; 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci switch (dev_priv->active_display_unit) { 200062306a36Sopenharmony_ci case vmw_du_screen_target: 200162306a36Sopenharmony_ci ret = vmw_kms_stdu_surface_dirty(dev_priv, vfb, NULL, clips, 200262306a36Sopenharmony_ci &surface->res, destX, destY, 200362306a36Sopenharmony_ci num_clips, 1, NULL, NULL); 200462306a36Sopenharmony_ci break; 200562306a36Sopenharmony_ci case vmw_du_screen_object: 200662306a36Sopenharmony_ci ret = vmw_kms_generic_present(dev_priv, file_priv, vfb, surface, 200762306a36Sopenharmony_ci sid, destX, destY, clips, 200862306a36Sopenharmony_ci num_clips); 200962306a36Sopenharmony_ci break; 201062306a36Sopenharmony_ci default: 201162306a36Sopenharmony_ci WARN_ONCE(true, 201262306a36Sopenharmony_ci "Present called with invalid display system.\n"); 201362306a36Sopenharmony_ci ret = -ENOSYS; 201462306a36Sopenharmony_ci break; 201562306a36Sopenharmony_ci } 201662306a36Sopenharmony_ci if (ret) 201762306a36Sopenharmony_ci return ret; 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci vmw_cmd_flush(dev_priv, false); 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci return 0; 202262306a36Sopenharmony_ci} 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_cistatic void 202562306a36Sopenharmony_civmw_kms_create_hotplug_mode_update_property(struct vmw_private *dev_priv) 202662306a36Sopenharmony_ci{ 202762306a36Sopenharmony_ci if (dev_priv->hotplug_mode_update_property) 202862306a36Sopenharmony_ci return; 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci dev_priv->hotplug_mode_update_property = 203162306a36Sopenharmony_ci drm_property_create_range(&dev_priv->drm, 203262306a36Sopenharmony_ci DRM_MODE_PROP_IMMUTABLE, 203362306a36Sopenharmony_ci "hotplug_mode_update", 0, 1); 203462306a36Sopenharmony_ci} 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ciint vmw_kms_init(struct vmw_private *dev_priv) 203762306a36Sopenharmony_ci{ 203862306a36Sopenharmony_ci struct drm_device *dev = &dev_priv->drm; 203962306a36Sopenharmony_ci int ret; 204062306a36Sopenharmony_ci static const char *display_unit_names[] = { 204162306a36Sopenharmony_ci "Invalid", 204262306a36Sopenharmony_ci "Legacy", 204362306a36Sopenharmony_ci "Screen Object", 204462306a36Sopenharmony_ci "Screen Target", 204562306a36Sopenharmony_ci "Invalid (max)" 204662306a36Sopenharmony_ci }; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci drm_mode_config_init(dev); 204962306a36Sopenharmony_ci dev->mode_config.funcs = &vmw_kms_funcs; 205062306a36Sopenharmony_ci dev->mode_config.min_width = 1; 205162306a36Sopenharmony_ci dev->mode_config.min_height = 1; 205262306a36Sopenharmony_ci dev->mode_config.max_width = dev_priv->texture_max_width; 205362306a36Sopenharmony_ci dev->mode_config.max_height = dev_priv->texture_max_height; 205462306a36Sopenharmony_ci dev->mode_config.preferred_depth = dev_priv->assume_16bpp ? 16 : 32; 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci drm_mode_create_suggested_offset_properties(dev); 205762306a36Sopenharmony_ci vmw_kms_create_hotplug_mode_update_property(dev_priv); 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci ret = vmw_kms_stdu_init_display(dev_priv); 206062306a36Sopenharmony_ci if (ret) { 206162306a36Sopenharmony_ci ret = vmw_kms_sou_init_display(dev_priv); 206262306a36Sopenharmony_ci if (ret) /* Fallback */ 206362306a36Sopenharmony_ci ret = vmw_kms_ldu_init_display(dev_priv); 206462306a36Sopenharmony_ci } 206562306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(display_unit_names) != (vmw_du_max + 1)); 206662306a36Sopenharmony_ci drm_info(&dev_priv->drm, "%s display unit initialized\n", 206762306a36Sopenharmony_ci display_unit_names[dev_priv->active_display_unit]); 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci return ret; 207062306a36Sopenharmony_ci} 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ciint vmw_kms_close(struct vmw_private *dev_priv) 207362306a36Sopenharmony_ci{ 207462306a36Sopenharmony_ci int ret = 0; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci /* 207762306a36Sopenharmony_ci * Docs says we should take the lock before calling this function 207862306a36Sopenharmony_ci * but since it destroys encoders and our destructor calls 207962306a36Sopenharmony_ci * drm_encoder_cleanup which takes the lock we deadlock. 208062306a36Sopenharmony_ci */ 208162306a36Sopenharmony_ci drm_mode_config_cleanup(&dev_priv->drm); 208262306a36Sopenharmony_ci if (dev_priv->active_display_unit == vmw_du_legacy) 208362306a36Sopenharmony_ci ret = vmw_kms_ldu_close_display(dev_priv); 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci return ret; 208662306a36Sopenharmony_ci} 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ciint vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data, 208962306a36Sopenharmony_ci struct drm_file *file_priv) 209062306a36Sopenharmony_ci{ 209162306a36Sopenharmony_ci struct drm_vmw_cursor_bypass_arg *arg = data; 209262306a36Sopenharmony_ci struct vmw_display_unit *du; 209362306a36Sopenharmony_ci struct drm_crtc *crtc; 209462306a36Sopenharmony_ci int ret = 0; 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci mutex_lock(&dev->mode_config.mutex); 209762306a36Sopenharmony_ci if (arg->flags & DRM_VMW_CURSOR_BYPASS_ALL) { 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 210062306a36Sopenharmony_ci du = vmw_crtc_to_du(crtc); 210162306a36Sopenharmony_ci du->hotspot_x = arg->xhot; 210262306a36Sopenharmony_ci du->hotspot_y = arg->yhot; 210362306a36Sopenharmony_ci } 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci mutex_unlock(&dev->mode_config.mutex); 210662306a36Sopenharmony_ci return 0; 210762306a36Sopenharmony_ci } 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci crtc = drm_crtc_find(dev, file_priv, arg->crtc_id); 211062306a36Sopenharmony_ci if (!crtc) { 211162306a36Sopenharmony_ci ret = -ENOENT; 211262306a36Sopenharmony_ci goto out; 211362306a36Sopenharmony_ci } 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci du = vmw_crtc_to_du(crtc); 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci du->hotspot_x = arg->xhot; 211862306a36Sopenharmony_ci du->hotspot_y = arg->yhot; 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ciout: 212162306a36Sopenharmony_ci mutex_unlock(&dev->mode_config.mutex); 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci return ret; 212462306a36Sopenharmony_ci} 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ciint vmw_kms_write_svga(struct vmw_private *vmw_priv, 212762306a36Sopenharmony_ci unsigned width, unsigned height, unsigned pitch, 212862306a36Sopenharmony_ci unsigned bpp, unsigned depth) 212962306a36Sopenharmony_ci{ 213062306a36Sopenharmony_ci if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK) 213162306a36Sopenharmony_ci vmw_write(vmw_priv, SVGA_REG_PITCHLOCK, pitch); 213262306a36Sopenharmony_ci else if (vmw_fifo_have_pitchlock(vmw_priv)) 213362306a36Sopenharmony_ci vmw_fifo_mem_write(vmw_priv, SVGA_FIFO_PITCHLOCK, pitch); 213462306a36Sopenharmony_ci vmw_write(vmw_priv, SVGA_REG_WIDTH, width); 213562306a36Sopenharmony_ci vmw_write(vmw_priv, SVGA_REG_HEIGHT, height); 213662306a36Sopenharmony_ci if ((vmw_priv->capabilities & SVGA_CAP_8BIT_EMULATION) != 0) 213762306a36Sopenharmony_ci vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, bpp); 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci if (vmw_read(vmw_priv, SVGA_REG_DEPTH) != depth) { 214062306a36Sopenharmony_ci DRM_ERROR("Invalid depth %u for %u bpp, host expects %u\n", 214162306a36Sopenharmony_ci depth, bpp, vmw_read(vmw_priv, SVGA_REG_DEPTH)); 214262306a36Sopenharmony_ci return -EINVAL; 214362306a36Sopenharmony_ci } 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci return 0; 214662306a36Sopenharmony_ci} 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_cibool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, 214962306a36Sopenharmony_ci uint32_t pitch, 215062306a36Sopenharmony_ci uint32_t height) 215162306a36Sopenharmony_ci{ 215262306a36Sopenharmony_ci return ((u64) pitch * (u64) height) < (u64) 215362306a36Sopenharmony_ci ((dev_priv->active_display_unit == vmw_du_screen_target) ? 215462306a36Sopenharmony_ci dev_priv->max_primary_mem : dev_priv->vram_size); 215562306a36Sopenharmony_ci} 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci/** 215862306a36Sopenharmony_ci * vmw_du_update_layout - Update the display unit with topology from resolution 215962306a36Sopenharmony_ci * plugin and generate DRM uevent 216062306a36Sopenharmony_ci * @dev_priv: device private 216162306a36Sopenharmony_ci * @num_rects: number of drm_rect in rects 216262306a36Sopenharmony_ci * @rects: toplogy to update 216362306a36Sopenharmony_ci */ 216462306a36Sopenharmony_cistatic int vmw_du_update_layout(struct vmw_private *dev_priv, 216562306a36Sopenharmony_ci unsigned int num_rects, struct drm_rect *rects) 216662306a36Sopenharmony_ci{ 216762306a36Sopenharmony_ci struct drm_device *dev = &dev_priv->drm; 216862306a36Sopenharmony_ci struct vmw_display_unit *du; 216962306a36Sopenharmony_ci struct drm_connector *con; 217062306a36Sopenharmony_ci struct drm_connector_list_iter conn_iter; 217162306a36Sopenharmony_ci struct drm_modeset_acquire_ctx ctx; 217262306a36Sopenharmony_ci struct drm_crtc *crtc; 217362306a36Sopenharmony_ci int ret; 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci /* Currently gui_x/y is protected with the crtc mutex */ 217662306a36Sopenharmony_ci mutex_lock(&dev->mode_config.mutex); 217762306a36Sopenharmony_ci drm_modeset_acquire_init(&ctx, 0); 217862306a36Sopenharmony_ciretry: 217962306a36Sopenharmony_ci drm_for_each_crtc(crtc, dev) { 218062306a36Sopenharmony_ci ret = drm_modeset_lock(&crtc->mutex, &ctx); 218162306a36Sopenharmony_ci if (ret < 0) { 218262306a36Sopenharmony_ci if (ret == -EDEADLK) { 218362306a36Sopenharmony_ci drm_modeset_backoff(&ctx); 218462306a36Sopenharmony_ci goto retry; 218562306a36Sopenharmony_ci } 218662306a36Sopenharmony_ci goto out_fini; 218762306a36Sopenharmony_ci } 218862306a36Sopenharmony_ci } 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 219162306a36Sopenharmony_ci drm_for_each_connector_iter(con, &conn_iter) { 219262306a36Sopenharmony_ci du = vmw_connector_to_du(con); 219362306a36Sopenharmony_ci if (num_rects > du->unit) { 219462306a36Sopenharmony_ci du->pref_width = drm_rect_width(&rects[du->unit]); 219562306a36Sopenharmony_ci du->pref_height = drm_rect_height(&rects[du->unit]); 219662306a36Sopenharmony_ci du->pref_active = true; 219762306a36Sopenharmony_ci du->gui_x = rects[du->unit].x1; 219862306a36Sopenharmony_ci du->gui_y = rects[du->unit].y1; 219962306a36Sopenharmony_ci } else { 220062306a36Sopenharmony_ci du->pref_width = VMWGFX_MIN_INITIAL_WIDTH; 220162306a36Sopenharmony_ci du->pref_height = VMWGFX_MIN_INITIAL_HEIGHT; 220262306a36Sopenharmony_ci du->pref_active = false; 220362306a36Sopenharmony_ci du->gui_x = 0; 220462306a36Sopenharmony_ci du->gui_y = 0; 220562306a36Sopenharmony_ci } 220662306a36Sopenharmony_ci } 220762306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci list_for_each_entry(con, &dev->mode_config.connector_list, head) { 221062306a36Sopenharmony_ci du = vmw_connector_to_du(con); 221162306a36Sopenharmony_ci if (num_rects > du->unit) { 221262306a36Sopenharmony_ci drm_object_property_set_value 221362306a36Sopenharmony_ci (&con->base, dev->mode_config.suggested_x_property, 221462306a36Sopenharmony_ci du->gui_x); 221562306a36Sopenharmony_ci drm_object_property_set_value 221662306a36Sopenharmony_ci (&con->base, dev->mode_config.suggested_y_property, 221762306a36Sopenharmony_ci du->gui_y); 221862306a36Sopenharmony_ci } else { 221962306a36Sopenharmony_ci drm_object_property_set_value 222062306a36Sopenharmony_ci (&con->base, dev->mode_config.suggested_x_property, 222162306a36Sopenharmony_ci 0); 222262306a36Sopenharmony_ci drm_object_property_set_value 222362306a36Sopenharmony_ci (&con->base, dev->mode_config.suggested_y_property, 222462306a36Sopenharmony_ci 0); 222562306a36Sopenharmony_ci } 222662306a36Sopenharmony_ci con->status = vmw_du_connector_detect(con, true); 222762306a36Sopenharmony_ci } 222862306a36Sopenharmony_ciout_fini: 222962306a36Sopenharmony_ci drm_modeset_drop_locks(&ctx); 223062306a36Sopenharmony_ci drm_modeset_acquire_fini(&ctx); 223162306a36Sopenharmony_ci mutex_unlock(&dev->mode_config.mutex); 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci drm_sysfs_hotplug_event(dev); 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci return 0; 223662306a36Sopenharmony_ci} 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ciint vmw_du_crtc_gamma_set(struct drm_crtc *crtc, 223962306a36Sopenharmony_ci u16 *r, u16 *g, u16 *b, 224062306a36Sopenharmony_ci uint32_t size, 224162306a36Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx) 224262306a36Sopenharmony_ci{ 224362306a36Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(crtc->dev); 224462306a36Sopenharmony_ci int i; 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci for (i = 0; i < size; i++) { 224762306a36Sopenharmony_ci DRM_DEBUG("%d r/g/b = 0x%04x / 0x%04x / 0x%04x\n", i, 224862306a36Sopenharmony_ci r[i], g[i], b[i]); 224962306a36Sopenharmony_ci vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 0, r[i] >> 8); 225062306a36Sopenharmony_ci vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 1, g[i] >> 8); 225162306a36Sopenharmony_ci vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 2, b[i] >> 8); 225262306a36Sopenharmony_ci } 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci return 0; 225562306a36Sopenharmony_ci} 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ciint vmw_du_connector_dpms(struct drm_connector *connector, int mode) 225862306a36Sopenharmony_ci{ 225962306a36Sopenharmony_ci return 0; 226062306a36Sopenharmony_ci} 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_cienum drm_connector_status 226362306a36Sopenharmony_civmw_du_connector_detect(struct drm_connector *connector, bool force) 226462306a36Sopenharmony_ci{ 226562306a36Sopenharmony_ci uint32_t num_displays; 226662306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 226762306a36Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 226862306a36Sopenharmony_ci struct vmw_display_unit *du = vmw_connector_to_du(connector); 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci num_displays = vmw_read(dev_priv, SVGA_REG_NUM_DISPLAYS); 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci return ((vmw_connector_to_du(connector)->unit < num_displays && 227362306a36Sopenharmony_ci du->pref_active) ? 227462306a36Sopenharmony_ci connector_status_connected : connector_status_disconnected); 227562306a36Sopenharmony_ci} 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_cistatic struct drm_display_mode vmw_kms_connector_builtin[] = { 227862306a36Sopenharmony_ci /* 640x480@60Hz */ 227962306a36Sopenharmony_ci { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, 228062306a36Sopenharmony_ci 752, 800, 0, 480, 489, 492, 525, 0, 228162306a36Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 228262306a36Sopenharmony_ci /* 800x600@60Hz */ 228362306a36Sopenharmony_ci { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, 228462306a36Sopenharmony_ci 968, 1056, 0, 600, 601, 605, 628, 0, 228562306a36Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 228662306a36Sopenharmony_ci /* 1024x768@60Hz */ 228762306a36Sopenharmony_ci { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, 228862306a36Sopenharmony_ci 1184, 1344, 0, 768, 771, 777, 806, 0, 228962306a36Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 229062306a36Sopenharmony_ci /* 1152x864@75Hz */ 229162306a36Sopenharmony_ci { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, 229262306a36Sopenharmony_ci 1344, 1600, 0, 864, 865, 868, 900, 0, 229362306a36Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 229462306a36Sopenharmony_ci /* 1280x720@60Hz */ 229562306a36Sopenharmony_ci { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74500, 1280, 1344, 229662306a36Sopenharmony_ci 1472, 1664, 0, 720, 723, 728, 748, 0, 229762306a36Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 229862306a36Sopenharmony_ci /* 1280x768@60Hz */ 229962306a36Sopenharmony_ci { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344, 230062306a36Sopenharmony_ci 1472, 1664, 0, 768, 771, 778, 798, 0, 230162306a36Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 230262306a36Sopenharmony_ci /* 1280x800@60Hz */ 230362306a36Sopenharmony_ci { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352, 230462306a36Sopenharmony_ci 1480, 1680, 0, 800, 803, 809, 831, 0, 230562306a36Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 230662306a36Sopenharmony_ci /* 1280x960@60Hz */ 230762306a36Sopenharmony_ci { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376, 230862306a36Sopenharmony_ci 1488, 1800, 0, 960, 961, 964, 1000, 0, 230962306a36Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 231062306a36Sopenharmony_ci /* 1280x1024@60Hz */ 231162306a36Sopenharmony_ci { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328, 231262306a36Sopenharmony_ci 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, 231362306a36Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 231462306a36Sopenharmony_ci /* 1360x768@60Hz */ 231562306a36Sopenharmony_ci { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424, 231662306a36Sopenharmony_ci 1536, 1792, 0, 768, 771, 777, 795, 0, 231762306a36Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 231862306a36Sopenharmony_ci /* 1440x1050@60Hz */ 231962306a36Sopenharmony_ci { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488, 232062306a36Sopenharmony_ci 1632, 1864, 0, 1050, 1053, 1057, 1089, 0, 232162306a36Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 232262306a36Sopenharmony_ci /* 1440x900@60Hz */ 232362306a36Sopenharmony_ci { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520, 232462306a36Sopenharmony_ci 1672, 1904, 0, 900, 903, 909, 934, 0, 232562306a36Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 232662306a36Sopenharmony_ci /* 1600x1200@60Hz */ 232762306a36Sopenharmony_ci { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664, 232862306a36Sopenharmony_ci 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 232962306a36Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 233062306a36Sopenharmony_ci /* 1680x1050@60Hz */ 233162306a36Sopenharmony_ci { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784, 233262306a36Sopenharmony_ci 1960, 2240, 0, 1050, 1053, 1059, 1089, 0, 233362306a36Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 233462306a36Sopenharmony_ci /* 1792x1344@60Hz */ 233562306a36Sopenharmony_ci { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920, 233662306a36Sopenharmony_ci 2120, 2448, 0, 1344, 1345, 1348, 1394, 0, 233762306a36Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 233862306a36Sopenharmony_ci /* 1853x1392@60Hz */ 233962306a36Sopenharmony_ci { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952, 234062306a36Sopenharmony_ci 2176, 2528, 0, 1392, 1393, 1396, 1439, 0, 234162306a36Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 234262306a36Sopenharmony_ci /* 1920x1080@60Hz */ 234362306a36Sopenharmony_ci { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 173000, 1920, 2048, 234462306a36Sopenharmony_ci 2248, 2576, 0, 1080, 1083, 1088, 1120, 0, 234562306a36Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 234662306a36Sopenharmony_ci /* 1920x1200@60Hz */ 234762306a36Sopenharmony_ci { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056, 234862306a36Sopenharmony_ci 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, 234962306a36Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 235062306a36Sopenharmony_ci /* 1920x1440@60Hz */ 235162306a36Sopenharmony_ci { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048, 235262306a36Sopenharmony_ci 2256, 2600, 0, 1440, 1441, 1444, 1500, 0, 235362306a36Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 235462306a36Sopenharmony_ci /* 2560x1440@60Hz */ 235562306a36Sopenharmony_ci { DRM_MODE("2560x1440", DRM_MODE_TYPE_DRIVER, 241500, 2560, 2608, 235662306a36Sopenharmony_ci 2640, 2720, 0, 1440, 1443, 1448, 1481, 0, 235762306a36Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 235862306a36Sopenharmony_ci /* 2560x1600@60Hz */ 235962306a36Sopenharmony_ci { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752, 236062306a36Sopenharmony_ci 3032, 3504, 0, 1600, 1603, 1609, 1658, 0, 236162306a36Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 236262306a36Sopenharmony_ci /* 2880x1800@60Hz */ 236362306a36Sopenharmony_ci { DRM_MODE("2880x1800", DRM_MODE_TYPE_DRIVER, 337500, 2880, 2928, 236462306a36Sopenharmony_ci 2960, 3040, 0, 1800, 1803, 1809, 1852, 0, 236562306a36Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 236662306a36Sopenharmony_ci /* 3840x2160@60Hz */ 236762306a36Sopenharmony_ci { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 533000, 3840, 3888, 236862306a36Sopenharmony_ci 3920, 4000, 0, 2160, 2163, 2168, 2222, 0, 236962306a36Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 237062306a36Sopenharmony_ci /* 3840x2400@60Hz */ 237162306a36Sopenharmony_ci { DRM_MODE("3840x2400", DRM_MODE_TYPE_DRIVER, 592250, 3840, 3888, 237262306a36Sopenharmony_ci 3920, 4000, 0, 2400, 2403, 2409, 2469, 0, 237362306a36Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 237462306a36Sopenharmony_ci /* Terminate */ 237562306a36Sopenharmony_ci { DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) }, 237662306a36Sopenharmony_ci}; 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci/** 237962306a36Sopenharmony_ci * vmw_guess_mode_timing - Provide fake timings for a 238062306a36Sopenharmony_ci * 60Hz vrefresh mode. 238162306a36Sopenharmony_ci * 238262306a36Sopenharmony_ci * @mode: Pointer to a struct drm_display_mode with hdisplay and vdisplay 238362306a36Sopenharmony_ci * members filled in. 238462306a36Sopenharmony_ci */ 238562306a36Sopenharmony_civoid vmw_guess_mode_timing(struct drm_display_mode *mode) 238662306a36Sopenharmony_ci{ 238762306a36Sopenharmony_ci mode->hsync_start = mode->hdisplay + 50; 238862306a36Sopenharmony_ci mode->hsync_end = mode->hsync_start + 50; 238962306a36Sopenharmony_ci mode->htotal = mode->hsync_end + 50; 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_ci mode->vsync_start = mode->vdisplay + 50; 239262306a36Sopenharmony_ci mode->vsync_end = mode->vsync_start + 50; 239362306a36Sopenharmony_ci mode->vtotal = mode->vsync_end + 50; 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci mode->clock = (u32)mode->htotal * (u32)mode->vtotal / 100 * 6; 239662306a36Sopenharmony_ci} 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ciint vmw_du_connector_fill_modes(struct drm_connector *connector, 240062306a36Sopenharmony_ci uint32_t max_width, uint32_t max_height) 240162306a36Sopenharmony_ci{ 240262306a36Sopenharmony_ci struct vmw_display_unit *du = vmw_connector_to_du(connector); 240362306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 240462306a36Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 240562306a36Sopenharmony_ci struct drm_display_mode *mode = NULL; 240662306a36Sopenharmony_ci struct drm_display_mode *bmode; 240762306a36Sopenharmony_ci struct drm_display_mode prefmode = { DRM_MODE("preferred", 240862306a36Sopenharmony_ci DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 240962306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241062306a36Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) 241162306a36Sopenharmony_ci }; 241262306a36Sopenharmony_ci int i; 241362306a36Sopenharmony_ci u32 assumed_bpp = 4; 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci if (dev_priv->assume_16bpp) 241662306a36Sopenharmony_ci assumed_bpp = 2; 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci max_width = min(max_width, dev_priv->texture_max_width); 241962306a36Sopenharmony_ci max_height = min(max_height, dev_priv->texture_max_height); 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci /* 242262306a36Sopenharmony_ci * For STDU extra limit for a mode on SVGA_REG_SCREENTARGET_MAX_WIDTH/ 242362306a36Sopenharmony_ci * HEIGHT registers. 242462306a36Sopenharmony_ci */ 242562306a36Sopenharmony_ci if (dev_priv->active_display_unit == vmw_du_screen_target) { 242662306a36Sopenharmony_ci max_width = min(max_width, dev_priv->stdu_max_width); 242762306a36Sopenharmony_ci max_height = min(max_height, dev_priv->stdu_max_height); 242862306a36Sopenharmony_ci } 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci /* Add preferred mode */ 243162306a36Sopenharmony_ci mode = drm_mode_duplicate(dev, &prefmode); 243262306a36Sopenharmony_ci if (!mode) 243362306a36Sopenharmony_ci return 0; 243462306a36Sopenharmony_ci mode->hdisplay = du->pref_width; 243562306a36Sopenharmony_ci mode->vdisplay = du->pref_height; 243662306a36Sopenharmony_ci vmw_guess_mode_timing(mode); 243762306a36Sopenharmony_ci drm_mode_set_name(mode); 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_ci if (vmw_kms_validate_mode_vram(dev_priv, 244062306a36Sopenharmony_ci mode->hdisplay * assumed_bpp, 244162306a36Sopenharmony_ci mode->vdisplay)) { 244262306a36Sopenharmony_ci drm_mode_probed_add(connector, mode); 244362306a36Sopenharmony_ci } else { 244462306a36Sopenharmony_ci drm_mode_destroy(dev, mode); 244562306a36Sopenharmony_ci mode = NULL; 244662306a36Sopenharmony_ci } 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ci if (du->pref_mode) { 244962306a36Sopenharmony_ci list_del_init(&du->pref_mode->head); 245062306a36Sopenharmony_ci drm_mode_destroy(dev, du->pref_mode); 245162306a36Sopenharmony_ci } 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci /* mode might be null here, this is intended */ 245462306a36Sopenharmony_ci du->pref_mode = mode; 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci for (i = 0; vmw_kms_connector_builtin[i].type != 0; i++) { 245762306a36Sopenharmony_ci bmode = &vmw_kms_connector_builtin[i]; 245862306a36Sopenharmony_ci if (bmode->hdisplay > max_width || 245962306a36Sopenharmony_ci bmode->vdisplay > max_height) 246062306a36Sopenharmony_ci continue; 246162306a36Sopenharmony_ci 246262306a36Sopenharmony_ci if (!vmw_kms_validate_mode_vram(dev_priv, 246362306a36Sopenharmony_ci bmode->hdisplay * assumed_bpp, 246462306a36Sopenharmony_ci bmode->vdisplay)) 246562306a36Sopenharmony_ci continue; 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci mode = drm_mode_duplicate(dev, bmode); 246862306a36Sopenharmony_ci if (!mode) 246962306a36Sopenharmony_ci return 0; 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_ci drm_mode_probed_add(connector, mode); 247262306a36Sopenharmony_ci } 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_ci drm_connector_list_update(connector); 247562306a36Sopenharmony_ci /* Move the prefered mode first, help apps pick the right mode. */ 247662306a36Sopenharmony_ci drm_mode_sort(&connector->modes); 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci return 1; 247962306a36Sopenharmony_ci} 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci/** 248262306a36Sopenharmony_ci * vmw_kms_update_layout_ioctl - Handler for DRM_VMW_UPDATE_LAYOUT ioctl 248362306a36Sopenharmony_ci * @dev: drm device for the ioctl 248462306a36Sopenharmony_ci * @data: data pointer for the ioctl 248562306a36Sopenharmony_ci * @file_priv: drm file for the ioctl call 248662306a36Sopenharmony_ci * 248762306a36Sopenharmony_ci * Update preferred topology of display unit as per ioctl request. The topology 248862306a36Sopenharmony_ci * is expressed as array of drm_vmw_rect. 248962306a36Sopenharmony_ci * e.g. 249062306a36Sopenharmony_ci * [0 0 640 480] [640 0 800 600] [0 480 640 480] 249162306a36Sopenharmony_ci * 249262306a36Sopenharmony_ci * NOTE: 249362306a36Sopenharmony_ci * The x and y offset (upper left) in drm_vmw_rect cannot be less than 0. Beside 249462306a36Sopenharmony_ci * device limit on topology, x + w and y + h (lower right) cannot be greater 249562306a36Sopenharmony_ci * than INT_MAX. So topology beyond these limits will return with error. 249662306a36Sopenharmony_ci * 249762306a36Sopenharmony_ci * Returns: 249862306a36Sopenharmony_ci * Zero on success, negative errno on failure. 249962306a36Sopenharmony_ci */ 250062306a36Sopenharmony_ciint vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, 250162306a36Sopenharmony_ci struct drm_file *file_priv) 250262306a36Sopenharmony_ci{ 250362306a36Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 250462306a36Sopenharmony_ci struct drm_mode_config *mode_config = &dev->mode_config; 250562306a36Sopenharmony_ci struct drm_vmw_update_layout_arg *arg = 250662306a36Sopenharmony_ci (struct drm_vmw_update_layout_arg *)data; 250762306a36Sopenharmony_ci void __user *user_rects; 250862306a36Sopenharmony_ci struct drm_vmw_rect *rects; 250962306a36Sopenharmony_ci struct drm_rect *drm_rects; 251062306a36Sopenharmony_ci unsigned rects_size; 251162306a36Sopenharmony_ci int ret, i; 251262306a36Sopenharmony_ci 251362306a36Sopenharmony_ci if (!arg->num_outputs) { 251462306a36Sopenharmony_ci struct drm_rect def_rect = {0, 0, 251562306a36Sopenharmony_ci VMWGFX_MIN_INITIAL_WIDTH, 251662306a36Sopenharmony_ci VMWGFX_MIN_INITIAL_HEIGHT}; 251762306a36Sopenharmony_ci vmw_du_update_layout(dev_priv, 1, &def_rect); 251862306a36Sopenharmony_ci return 0; 251962306a36Sopenharmony_ci } 252062306a36Sopenharmony_ci 252162306a36Sopenharmony_ci rects_size = arg->num_outputs * sizeof(struct drm_vmw_rect); 252262306a36Sopenharmony_ci rects = kcalloc(arg->num_outputs, sizeof(struct drm_vmw_rect), 252362306a36Sopenharmony_ci GFP_KERNEL); 252462306a36Sopenharmony_ci if (unlikely(!rects)) 252562306a36Sopenharmony_ci return -ENOMEM; 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci user_rects = (void __user *)(unsigned long)arg->rects; 252862306a36Sopenharmony_ci ret = copy_from_user(rects, user_rects, rects_size); 252962306a36Sopenharmony_ci if (unlikely(ret != 0)) { 253062306a36Sopenharmony_ci DRM_ERROR("Failed to get rects.\n"); 253162306a36Sopenharmony_ci ret = -EFAULT; 253262306a36Sopenharmony_ci goto out_free; 253362306a36Sopenharmony_ci } 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci drm_rects = (struct drm_rect *)rects; 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci VMW_DEBUG_KMS("Layout count = %u\n", arg->num_outputs); 253862306a36Sopenharmony_ci for (i = 0; i < arg->num_outputs; i++) { 253962306a36Sopenharmony_ci struct drm_vmw_rect curr_rect; 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci /* Verify user-space for overflow as kernel use drm_rect */ 254262306a36Sopenharmony_ci if ((rects[i].x + rects[i].w > INT_MAX) || 254362306a36Sopenharmony_ci (rects[i].y + rects[i].h > INT_MAX)) { 254462306a36Sopenharmony_ci ret = -ERANGE; 254562306a36Sopenharmony_ci goto out_free; 254662306a36Sopenharmony_ci } 254762306a36Sopenharmony_ci 254862306a36Sopenharmony_ci curr_rect = rects[i]; 254962306a36Sopenharmony_ci drm_rects[i].x1 = curr_rect.x; 255062306a36Sopenharmony_ci drm_rects[i].y1 = curr_rect.y; 255162306a36Sopenharmony_ci drm_rects[i].x2 = curr_rect.x + curr_rect.w; 255262306a36Sopenharmony_ci drm_rects[i].y2 = curr_rect.y + curr_rect.h; 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci VMW_DEBUG_KMS(" x1 = %d y1 = %d x2 = %d y2 = %d\n", 255562306a36Sopenharmony_ci drm_rects[i].x1, drm_rects[i].y1, 255662306a36Sopenharmony_ci drm_rects[i].x2, drm_rects[i].y2); 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci /* 255962306a36Sopenharmony_ci * Currently this check is limiting the topology within 256062306a36Sopenharmony_ci * mode_config->max (which actually is max texture size 256162306a36Sopenharmony_ci * supported by virtual device). This limit is here to address 256262306a36Sopenharmony_ci * window managers that create a big framebuffer for whole 256362306a36Sopenharmony_ci * topology. 256462306a36Sopenharmony_ci */ 256562306a36Sopenharmony_ci if (drm_rects[i].x1 < 0 || drm_rects[i].y1 < 0 || 256662306a36Sopenharmony_ci drm_rects[i].x2 > mode_config->max_width || 256762306a36Sopenharmony_ci drm_rects[i].y2 > mode_config->max_height) { 256862306a36Sopenharmony_ci VMW_DEBUG_KMS("Invalid layout %d %d %d %d\n", 256962306a36Sopenharmony_ci drm_rects[i].x1, drm_rects[i].y1, 257062306a36Sopenharmony_ci drm_rects[i].x2, drm_rects[i].y2); 257162306a36Sopenharmony_ci ret = -EINVAL; 257262306a36Sopenharmony_ci goto out_free; 257362306a36Sopenharmony_ci } 257462306a36Sopenharmony_ci } 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_ci ret = vmw_kms_check_display_memory(dev, arg->num_outputs, drm_rects); 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci if (ret == 0) 257962306a36Sopenharmony_ci vmw_du_update_layout(dev_priv, arg->num_outputs, drm_rects); 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_ciout_free: 258262306a36Sopenharmony_ci kfree(rects); 258362306a36Sopenharmony_ci return ret; 258462306a36Sopenharmony_ci} 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_ci/** 258762306a36Sopenharmony_ci * vmw_kms_helper_dirty - Helper to build commands and perform actions based 258862306a36Sopenharmony_ci * on a set of cliprects and a set of display units. 258962306a36Sopenharmony_ci * 259062306a36Sopenharmony_ci * @dev_priv: Pointer to a device private structure. 259162306a36Sopenharmony_ci * @framebuffer: Pointer to the framebuffer on which to perform the actions. 259262306a36Sopenharmony_ci * @clips: A set of struct drm_clip_rect. Either this os @vclips must be NULL. 259362306a36Sopenharmony_ci * Cliprects are given in framebuffer coordinates. 259462306a36Sopenharmony_ci * @vclips: A set of struct drm_vmw_rect cliprects. Either this or @clips must 259562306a36Sopenharmony_ci * be NULL. Cliprects are given in source coordinates. 259662306a36Sopenharmony_ci * @dest_x: X coordinate offset for the crtc / destination clip rects. 259762306a36Sopenharmony_ci * @dest_y: Y coordinate offset for the crtc / destination clip rects. 259862306a36Sopenharmony_ci * @num_clips: Number of cliprects in the @clips or @vclips array. 259962306a36Sopenharmony_ci * @increment: Integer with which to increment the clip counter when looping. 260062306a36Sopenharmony_ci * Used to skip a predetermined number of clip rects. 260162306a36Sopenharmony_ci * @dirty: Closure structure. See the description of struct vmw_kms_dirty. 260262306a36Sopenharmony_ci */ 260362306a36Sopenharmony_ciint vmw_kms_helper_dirty(struct vmw_private *dev_priv, 260462306a36Sopenharmony_ci struct vmw_framebuffer *framebuffer, 260562306a36Sopenharmony_ci const struct drm_clip_rect *clips, 260662306a36Sopenharmony_ci const struct drm_vmw_rect *vclips, 260762306a36Sopenharmony_ci s32 dest_x, s32 dest_y, 260862306a36Sopenharmony_ci int num_clips, 260962306a36Sopenharmony_ci int increment, 261062306a36Sopenharmony_ci struct vmw_kms_dirty *dirty) 261162306a36Sopenharmony_ci{ 261262306a36Sopenharmony_ci struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; 261362306a36Sopenharmony_ci struct drm_crtc *crtc; 261462306a36Sopenharmony_ci u32 num_units = 0; 261562306a36Sopenharmony_ci u32 i, k; 261662306a36Sopenharmony_ci 261762306a36Sopenharmony_ci dirty->dev_priv = dev_priv; 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_ci /* If crtc is passed, no need to iterate over other display units */ 262062306a36Sopenharmony_ci if (dirty->crtc) { 262162306a36Sopenharmony_ci units[num_units++] = vmw_crtc_to_du(dirty->crtc); 262262306a36Sopenharmony_ci } else { 262362306a36Sopenharmony_ci list_for_each_entry(crtc, &dev_priv->drm.mode_config.crtc_list, 262462306a36Sopenharmony_ci head) { 262562306a36Sopenharmony_ci struct drm_plane *plane = crtc->primary; 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ci if (plane->state->fb == &framebuffer->base) 262862306a36Sopenharmony_ci units[num_units++] = vmw_crtc_to_du(crtc); 262962306a36Sopenharmony_ci } 263062306a36Sopenharmony_ci } 263162306a36Sopenharmony_ci 263262306a36Sopenharmony_ci for (k = 0; k < num_units; k++) { 263362306a36Sopenharmony_ci struct vmw_display_unit *unit = units[k]; 263462306a36Sopenharmony_ci s32 crtc_x = unit->crtc.x; 263562306a36Sopenharmony_ci s32 crtc_y = unit->crtc.y; 263662306a36Sopenharmony_ci s32 crtc_width = unit->crtc.mode.hdisplay; 263762306a36Sopenharmony_ci s32 crtc_height = unit->crtc.mode.vdisplay; 263862306a36Sopenharmony_ci const struct drm_clip_rect *clips_ptr = clips; 263962306a36Sopenharmony_ci const struct drm_vmw_rect *vclips_ptr = vclips; 264062306a36Sopenharmony_ci 264162306a36Sopenharmony_ci dirty->unit = unit; 264262306a36Sopenharmony_ci if (dirty->fifo_reserve_size > 0) { 264362306a36Sopenharmony_ci dirty->cmd = VMW_CMD_RESERVE(dev_priv, 264462306a36Sopenharmony_ci dirty->fifo_reserve_size); 264562306a36Sopenharmony_ci if (!dirty->cmd) 264662306a36Sopenharmony_ci return -ENOMEM; 264762306a36Sopenharmony_ci 264862306a36Sopenharmony_ci memset(dirty->cmd, 0, dirty->fifo_reserve_size); 264962306a36Sopenharmony_ci } 265062306a36Sopenharmony_ci dirty->num_hits = 0; 265162306a36Sopenharmony_ci for (i = 0; i < num_clips; i++, clips_ptr += increment, 265262306a36Sopenharmony_ci vclips_ptr += increment) { 265362306a36Sopenharmony_ci s32 clip_left; 265462306a36Sopenharmony_ci s32 clip_top; 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_ci /* 265762306a36Sopenharmony_ci * Select clip array type. Note that integer type 265862306a36Sopenharmony_ci * in @clips is unsigned short, whereas in @vclips 265962306a36Sopenharmony_ci * it's 32-bit. 266062306a36Sopenharmony_ci */ 266162306a36Sopenharmony_ci if (clips) { 266262306a36Sopenharmony_ci dirty->fb_x = (s32) clips_ptr->x1; 266362306a36Sopenharmony_ci dirty->fb_y = (s32) clips_ptr->y1; 266462306a36Sopenharmony_ci dirty->unit_x2 = (s32) clips_ptr->x2 + dest_x - 266562306a36Sopenharmony_ci crtc_x; 266662306a36Sopenharmony_ci dirty->unit_y2 = (s32) clips_ptr->y2 + dest_y - 266762306a36Sopenharmony_ci crtc_y; 266862306a36Sopenharmony_ci } else { 266962306a36Sopenharmony_ci dirty->fb_x = vclips_ptr->x; 267062306a36Sopenharmony_ci dirty->fb_y = vclips_ptr->y; 267162306a36Sopenharmony_ci dirty->unit_x2 = dirty->fb_x + vclips_ptr->w + 267262306a36Sopenharmony_ci dest_x - crtc_x; 267362306a36Sopenharmony_ci dirty->unit_y2 = dirty->fb_y + vclips_ptr->h + 267462306a36Sopenharmony_ci dest_y - crtc_y; 267562306a36Sopenharmony_ci } 267662306a36Sopenharmony_ci 267762306a36Sopenharmony_ci dirty->unit_x1 = dirty->fb_x + dest_x - crtc_x; 267862306a36Sopenharmony_ci dirty->unit_y1 = dirty->fb_y + dest_y - crtc_y; 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci /* Skip this clip if it's outside the crtc region */ 268162306a36Sopenharmony_ci if (dirty->unit_x1 >= crtc_width || 268262306a36Sopenharmony_ci dirty->unit_y1 >= crtc_height || 268362306a36Sopenharmony_ci dirty->unit_x2 <= 0 || dirty->unit_y2 <= 0) 268462306a36Sopenharmony_ci continue; 268562306a36Sopenharmony_ci 268662306a36Sopenharmony_ci /* Clip right and bottom to crtc limits */ 268762306a36Sopenharmony_ci dirty->unit_x2 = min_t(s32, dirty->unit_x2, 268862306a36Sopenharmony_ci crtc_width); 268962306a36Sopenharmony_ci dirty->unit_y2 = min_t(s32, dirty->unit_y2, 269062306a36Sopenharmony_ci crtc_height); 269162306a36Sopenharmony_ci 269262306a36Sopenharmony_ci /* Clip left and top to crtc limits */ 269362306a36Sopenharmony_ci clip_left = min_t(s32, dirty->unit_x1, 0); 269462306a36Sopenharmony_ci clip_top = min_t(s32, dirty->unit_y1, 0); 269562306a36Sopenharmony_ci dirty->unit_x1 -= clip_left; 269662306a36Sopenharmony_ci dirty->unit_y1 -= clip_top; 269762306a36Sopenharmony_ci dirty->fb_x -= clip_left; 269862306a36Sopenharmony_ci dirty->fb_y -= clip_top; 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_ci dirty->clip(dirty); 270162306a36Sopenharmony_ci } 270262306a36Sopenharmony_ci 270362306a36Sopenharmony_ci dirty->fifo_commit(dirty); 270462306a36Sopenharmony_ci } 270562306a36Sopenharmony_ci 270662306a36Sopenharmony_ci return 0; 270762306a36Sopenharmony_ci} 270862306a36Sopenharmony_ci 270962306a36Sopenharmony_ci/** 271062306a36Sopenharmony_ci * vmw_kms_helper_validation_finish - Helper for post KMS command submission 271162306a36Sopenharmony_ci * cleanup and fencing 271262306a36Sopenharmony_ci * @dev_priv: Pointer to the device-private struct 271362306a36Sopenharmony_ci * @file_priv: Pointer identifying the client when user-space fencing is used 271462306a36Sopenharmony_ci * @ctx: Pointer to the validation context 271562306a36Sopenharmony_ci * @out_fence: If non-NULL, returned refcounted fence-pointer 271662306a36Sopenharmony_ci * @user_fence_rep: If non-NULL, pointer to user-space address area 271762306a36Sopenharmony_ci * in which to copy user-space fence info 271862306a36Sopenharmony_ci */ 271962306a36Sopenharmony_civoid vmw_kms_helper_validation_finish(struct vmw_private *dev_priv, 272062306a36Sopenharmony_ci struct drm_file *file_priv, 272162306a36Sopenharmony_ci struct vmw_validation_context *ctx, 272262306a36Sopenharmony_ci struct vmw_fence_obj **out_fence, 272362306a36Sopenharmony_ci struct drm_vmw_fence_rep __user * 272462306a36Sopenharmony_ci user_fence_rep) 272562306a36Sopenharmony_ci{ 272662306a36Sopenharmony_ci struct vmw_fence_obj *fence = NULL; 272762306a36Sopenharmony_ci uint32_t handle = 0; 272862306a36Sopenharmony_ci int ret = 0; 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci if (file_priv || user_fence_rep || vmw_validation_has_bos(ctx) || 273162306a36Sopenharmony_ci out_fence) 273262306a36Sopenharmony_ci ret = vmw_execbuf_fence_commands(file_priv, dev_priv, &fence, 273362306a36Sopenharmony_ci file_priv ? &handle : NULL); 273462306a36Sopenharmony_ci vmw_validation_done(ctx, fence); 273562306a36Sopenharmony_ci if (file_priv) 273662306a36Sopenharmony_ci vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), 273762306a36Sopenharmony_ci ret, user_fence_rep, fence, 273862306a36Sopenharmony_ci handle, -1); 273962306a36Sopenharmony_ci if (out_fence) 274062306a36Sopenharmony_ci *out_fence = fence; 274162306a36Sopenharmony_ci else 274262306a36Sopenharmony_ci vmw_fence_obj_unreference(&fence); 274362306a36Sopenharmony_ci} 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci/** 274662306a36Sopenharmony_ci * vmw_kms_update_proxy - Helper function to update a proxy surface from 274762306a36Sopenharmony_ci * its backing MOB. 274862306a36Sopenharmony_ci * 274962306a36Sopenharmony_ci * @res: Pointer to the surface resource 275062306a36Sopenharmony_ci * @clips: Clip rects in framebuffer (surface) space. 275162306a36Sopenharmony_ci * @num_clips: Number of clips in @clips. 275262306a36Sopenharmony_ci * @increment: Integer with which to increment the clip counter when looping. 275362306a36Sopenharmony_ci * Used to skip a predetermined number of clip rects. 275462306a36Sopenharmony_ci * 275562306a36Sopenharmony_ci * This function makes sure the proxy surface is updated from its backing MOB 275662306a36Sopenharmony_ci * using the region given by @clips. The surface resource @res and its backing 275762306a36Sopenharmony_ci * MOB needs to be reserved and validated on call. 275862306a36Sopenharmony_ci */ 275962306a36Sopenharmony_ciint vmw_kms_update_proxy(struct vmw_resource *res, 276062306a36Sopenharmony_ci const struct drm_clip_rect *clips, 276162306a36Sopenharmony_ci unsigned num_clips, 276262306a36Sopenharmony_ci int increment) 276362306a36Sopenharmony_ci{ 276462306a36Sopenharmony_ci struct vmw_private *dev_priv = res->dev_priv; 276562306a36Sopenharmony_ci struct drm_vmw_size *size = &vmw_res_to_srf(res)->metadata.base_size; 276662306a36Sopenharmony_ci struct { 276762306a36Sopenharmony_ci SVGA3dCmdHeader header; 276862306a36Sopenharmony_ci SVGA3dCmdUpdateGBImage body; 276962306a36Sopenharmony_ci } *cmd; 277062306a36Sopenharmony_ci SVGA3dBox *box; 277162306a36Sopenharmony_ci size_t copy_size = 0; 277262306a36Sopenharmony_ci int i; 277362306a36Sopenharmony_ci 277462306a36Sopenharmony_ci if (!clips) 277562306a36Sopenharmony_ci return 0; 277662306a36Sopenharmony_ci 277762306a36Sopenharmony_ci cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd) * num_clips); 277862306a36Sopenharmony_ci if (!cmd) 277962306a36Sopenharmony_ci return -ENOMEM; 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_ci for (i = 0; i < num_clips; ++i, clips += increment, ++cmd) { 278262306a36Sopenharmony_ci box = &cmd->body.box; 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_ci cmd->header.id = SVGA_3D_CMD_UPDATE_GB_IMAGE; 278562306a36Sopenharmony_ci cmd->header.size = sizeof(cmd->body); 278662306a36Sopenharmony_ci cmd->body.image.sid = res->id; 278762306a36Sopenharmony_ci cmd->body.image.face = 0; 278862306a36Sopenharmony_ci cmd->body.image.mipmap = 0; 278962306a36Sopenharmony_ci 279062306a36Sopenharmony_ci if (clips->x1 > size->width || clips->x2 > size->width || 279162306a36Sopenharmony_ci clips->y1 > size->height || clips->y2 > size->height) { 279262306a36Sopenharmony_ci DRM_ERROR("Invalid clips outsize of framebuffer.\n"); 279362306a36Sopenharmony_ci return -EINVAL; 279462306a36Sopenharmony_ci } 279562306a36Sopenharmony_ci 279662306a36Sopenharmony_ci box->x = clips->x1; 279762306a36Sopenharmony_ci box->y = clips->y1; 279862306a36Sopenharmony_ci box->z = 0; 279962306a36Sopenharmony_ci box->w = clips->x2 - clips->x1; 280062306a36Sopenharmony_ci box->h = clips->y2 - clips->y1; 280162306a36Sopenharmony_ci box->d = 1; 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_ci copy_size += sizeof(*cmd); 280462306a36Sopenharmony_ci } 280562306a36Sopenharmony_ci 280662306a36Sopenharmony_ci vmw_cmd_commit(dev_priv, copy_size); 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci return 0; 280962306a36Sopenharmony_ci} 281062306a36Sopenharmony_ci 281162306a36Sopenharmony_ci/** 281262306a36Sopenharmony_ci * vmw_kms_create_implicit_placement_property - Set up the implicit placement 281362306a36Sopenharmony_ci * property. 281462306a36Sopenharmony_ci * 281562306a36Sopenharmony_ci * @dev_priv: Pointer to a device private struct. 281662306a36Sopenharmony_ci * 281762306a36Sopenharmony_ci * Sets up the implicit placement property unless it's already set up. 281862306a36Sopenharmony_ci */ 281962306a36Sopenharmony_civoid 282062306a36Sopenharmony_civmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv) 282162306a36Sopenharmony_ci{ 282262306a36Sopenharmony_ci if (dev_priv->implicit_placement_property) 282362306a36Sopenharmony_ci return; 282462306a36Sopenharmony_ci 282562306a36Sopenharmony_ci dev_priv->implicit_placement_property = 282662306a36Sopenharmony_ci drm_property_create_range(&dev_priv->drm, 282762306a36Sopenharmony_ci DRM_MODE_PROP_IMMUTABLE, 282862306a36Sopenharmony_ci "implicit_placement", 0, 1); 282962306a36Sopenharmony_ci} 283062306a36Sopenharmony_ci 283162306a36Sopenharmony_ci/** 283262306a36Sopenharmony_ci * vmw_kms_suspend - Save modesetting state and turn modesetting off. 283362306a36Sopenharmony_ci * 283462306a36Sopenharmony_ci * @dev: Pointer to the drm device 283562306a36Sopenharmony_ci * Return: 0 on success. Negative error code on failure. 283662306a36Sopenharmony_ci */ 283762306a36Sopenharmony_ciint vmw_kms_suspend(struct drm_device *dev) 283862306a36Sopenharmony_ci{ 283962306a36Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci dev_priv->suspend_state = drm_atomic_helper_suspend(dev); 284262306a36Sopenharmony_ci if (IS_ERR(dev_priv->suspend_state)) { 284362306a36Sopenharmony_ci int ret = PTR_ERR(dev_priv->suspend_state); 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ci DRM_ERROR("Failed kms suspend: %d\n", ret); 284662306a36Sopenharmony_ci dev_priv->suspend_state = NULL; 284762306a36Sopenharmony_ci 284862306a36Sopenharmony_ci return ret; 284962306a36Sopenharmony_ci } 285062306a36Sopenharmony_ci 285162306a36Sopenharmony_ci return 0; 285262306a36Sopenharmony_ci} 285362306a36Sopenharmony_ci 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_ci/** 285662306a36Sopenharmony_ci * vmw_kms_resume - Re-enable modesetting and restore state 285762306a36Sopenharmony_ci * 285862306a36Sopenharmony_ci * @dev: Pointer to the drm device 285962306a36Sopenharmony_ci * Return: 0 on success. Negative error code on failure. 286062306a36Sopenharmony_ci * 286162306a36Sopenharmony_ci * State is resumed from a previous vmw_kms_suspend(). It's illegal 286262306a36Sopenharmony_ci * to call this function without a previous vmw_kms_suspend(). 286362306a36Sopenharmony_ci */ 286462306a36Sopenharmony_ciint vmw_kms_resume(struct drm_device *dev) 286562306a36Sopenharmony_ci{ 286662306a36Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 286762306a36Sopenharmony_ci int ret; 286862306a36Sopenharmony_ci 286962306a36Sopenharmony_ci if (WARN_ON(!dev_priv->suspend_state)) 287062306a36Sopenharmony_ci return 0; 287162306a36Sopenharmony_ci 287262306a36Sopenharmony_ci ret = drm_atomic_helper_resume(dev, dev_priv->suspend_state); 287362306a36Sopenharmony_ci dev_priv->suspend_state = NULL; 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_ci return ret; 287662306a36Sopenharmony_ci} 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci/** 287962306a36Sopenharmony_ci * vmw_kms_lost_device - Notify kms that modesetting capabilities will be lost 288062306a36Sopenharmony_ci * 288162306a36Sopenharmony_ci * @dev: Pointer to the drm device 288262306a36Sopenharmony_ci */ 288362306a36Sopenharmony_civoid vmw_kms_lost_device(struct drm_device *dev) 288462306a36Sopenharmony_ci{ 288562306a36Sopenharmony_ci drm_atomic_helper_shutdown(dev); 288662306a36Sopenharmony_ci} 288762306a36Sopenharmony_ci 288862306a36Sopenharmony_ci/** 288962306a36Sopenharmony_ci * vmw_du_helper_plane_update - Helper to do plane update on a display unit. 289062306a36Sopenharmony_ci * @update: The closure structure. 289162306a36Sopenharmony_ci * 289262306a36Sopenharmony_ci * Call this helper after setting callbacks in &vmw_du_update_plane to do plane 289362306a36Sopenharmony_ci * update on display unit. 289462306a36Sopenharmony_ci * 289562306a36Sopenharmony_ci * Return: 0 on success or a negative error code on failure. 289662306a36Sopenharmony_ci */ 289762306a36Sopenharmony_ciint vmw_du_helper_plane_update(struct vmw_du_update_plane *update) 289862306a36Sopenharmony_ci{ 289962306a36Sopenharmony_ci struct drm_plane_state *state = update->plane->state; 290062306a36Sopenharmony_ci struct drm_plane_state *old_state = update->old_state; 290162306a36Sopenharmony_ci struct drm_atomic_helper_damage_iter iter; 290262306a36Sopenharmony_ci struct drm_rect clip; 290362306a36Sopenharmony_ci struct drm_rect bb; 290462306a36Sopenharmony_ci DECLARE_VAL_CONTEXT(val_ctx, NULL, 0); 290562306a36Sopenharmony_ci uint32_t reserved_size = 0; 290662306a36Sopenharmony_ci uint32_t submit_size = 0; 290762306a36Sopenharmony_ci uint32_t curr_size = 0; 290862306a36Sopenharmony_ci uint32_t num_hits = 0; 290962306a36Sopenharmony_ci void *cmd_start; 291062306a36Sopenharmony_ci char *cmd_next; 291162306a36Sopenharmony_ci int ret; 291262306a36Sopenharmony_ci 291362306a36Sopenharmony_ci /* 291462306a36Sopenharmony_ci * Iterate in advance to check if really need plane update and find the 291562306a36Sopenharmony_ci * number of clips that actually are in plane src for fifo allocation. 291662306a36Sopenharmony_ci */ 291762306a36Sopenharmony_ci drm_atomic_helper_damage_iter_init(&iter, old_state, state); 291862306a36Sopenharmony_ci drm_atomic_for_each_plane_damage(&iter, &clip) 291962306a36Sopenharmony_ci num_hits++; 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_ci if (num_hits == 0) 292262306a36Sopenharmony_ci return 0; 292362306a36Sopenharmony_ci 292462306a36Sopenharmony_ci if (update->vfb->bo) { 292562306a36Sopenharmony_ci struct vmw_framebuffer_bo *vfbbo = 292662306a36Sopenharmony_ci container_of(update->vfb, typeof(*vfbbo), base); 292762306a36Sopenharmony_ci 292862306a36Sopenharmony_ci /* 292962306a36Sopenharmony_ci * For screen targets we want a mappable bo, for everything else we want 293062306a36Sopenharmony_ci * accelerated i.e. host backed (vram or gmr) bo. If the display unit 293162306a36Sopenharmony_ci * is not screen target then mob's shouldn't be available. 293262306a36Sopenharmony_ci */ 293362306a36Sopenharmony_ci if (update->dev_priv->active_display_unit == vmw_du_screen_target) { 293462306a36Sopenharmony_ci vmw_bo_placement_set(vfbbo->buffer, 293562306a36Sopenharmony_ci VMW_BO_DOMAIN_SYS | VMW_BO_DOMAIN_MOB | VMW_BO_DOMAIN_GMR, 293662306a36Sopenharmony_ci VMW_BO_DOMAIN_SYS | VMW_BO_DOMAIN_MOB | VMW_BO_DOMAIN_GMR); 293762306a36Sopenharmony_ci } else { 293862306a36Sopenharmony_ci WARN_ON(update->dev_priv->has_mob); 293962306a36Sopenharmony_ci vmw_bo_placement_set_default_accelerated(vfbbo->buffer); 294062306a36Sopenharmony_ci } 294162306a36Sopenharmony_ci ret = vmw_validation_add_bo(&val_ctx, vfbbo->buffer); 294262306a36Sopenharmony_ci } else { 294362306a36Sopenharmony_ci struct vmw_framebuffer_surface *vfbs = 294462306a36Sopenharmony_ci container_of(update->vfb, typeof(*vfbs), base); 294562306a36Sopenharmony_ci 294662306a36Sopenharmony_ci ret = vmw_validation_add_resource(&val_ctx, &vfbs->surface->res, 294762306a36Sopenharmony_ci 0, VMW_RES_DIRTY_NONE, NULL, 294862306a36Sopenharmony_ci NULL); 294962306a36Sopenharmony_ci } 295062306a36Sopenharmony_ci 295162306a36Sopenharmony_ci if (ret) 295262306a36Sopenharmony_ci return ret; 295362306a36Sopenharmony_ci 295462306a36Sopenharmony_ci ret = vmw_validation_prepare(&val_ctx, update->mutex, update->intr); 295562306a36Sopenharmony_ci if (ret) 295662306a36Sopenharmony_ci goto out_unref; 295762306a36Sopenharmony_ci 295862306a36Sopenharmony_ci reserved_size = update->calc_fifo_size(update, num_hits); 295962306a36Sopenharmony_ci cmd_start = VMW_CMD_RESERVE(update->dev_priv, reserved_size); 296062306a36Sopenharmony_ci if (!cmd_start) { 296162306a36Sopenharmony_ci ret = -ENOMEM; 296262306a36Sopenharmony_ci goto out_revert; 296362306a36Sopenharmony_ci } 296462306a36Sopenharmony_ci 296562306a36Sopenharmony_ci cmd_next = cmd_start; 296662306a36Sopenharmony_ci 296762306a36Sopenharmony_ci if (update->post_prepare) { 296862306a36Sopenharmony_ci curr_size = update->post_prepare(update, cmd_next); 296962306a36Sopenharmony_ci cmd_next += curr_size; 297062306a36Sopenharmony_ci submit_size += curr_size; 297162306a36Sopenharmony_ci } 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_ci if (update->pre_clip) { 297462306a36Sopenharmony_ci curr_size = update->pre_clip(update, cmd_next, num_hits); 297562306a36Sopenharmony_ci cmd_next += curr_size; 297662306a36Sopenharmony_ci submit_size += curr_size; 297762306a36Sopenharmony_ci } 297862306a36Sopenharmony_ci 297962306a36Sopenharmony_ci bb.x1 = INT_MAX; 298062306a36Sopenharmony_ci bb.y1 = INT_MAX; 298162306a36Sopenharmony_ci bb.x2 = INT_MIN; 298262306a36Sopenharmony_ci bb.y2 = INT_MIN; 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_ci drm_atomic_helper_damage_iter_init(&iter, old_state, state); 298562306a36Sopenharmony_ci drm_atomic_for_each_plane_damage(&iter, &clip) { 298662306a36Sopenharmony_ci uint32_t fb_x = clip.x1; 298762306a36Sopenharmony_ci uint32_t fb_y = clip.y1; 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_ci vmw_du_translate_to_crtc(state, &clip); 299062306a36Sopenharmony_ci if (update->clip) { 299162306a36Sopenharmony_ci curr_size = update->clip(update, cmd_next, &clip, fb_x, 299262306a36Sopenharmony_ci fb_y); 299362306a36Sopenharmony_ci cmd_next += curr_size; 299462306a36Sopenharmony_ci submit_size += curr_size; 299562306a36Sopenharmony_ci } 299662306a36Sopenharmony_ci bb.x1 = min_t(int, bb.x1, clip.x1); 299762306a36Sopenharmony_ci bb.y1 = min_t(int, bb.y1, clip.y1); 299862306a36Sopenharmony_ci bb.x2 = max_t(int, bb.x2, clip.x2); 299962306a36Sopenharmony_ci bb.y2 = max_t(int, bb.y2, clip.y2); 300062306a36Sopenharmony_ci } 300162306a36Sopenharmony_ci 300262306a36Sopenharmony_ci curr_size = update->post_clip(update, cmd_next, &bb); 300362306a36Sopenharmony_ci submit_size += curr_size; 300462306a36Sopenharmony_ci 300562306a36Sopenharmony_ci if (reserved_size < submit_size) 300662306a36Sopenharmony_ci submit_size = 0; 300762306a36Sopenharmony_ci 300862306a36Sopenharmony_ci vmw_cmd_commit(update->dev_priv, submit_size); 300962306a36Sopenharmony_ci 301062306a36Sopenharmony_ci vmw_kms_helper_validation_finish(update->dev_priv, NULL, &val_ctx, 301162306a36Sopenharmony_ci update->out_fence, NULL); 301262306a36Sopenharmony_ci return ret; 301362306a36Sopenharmony_ci 301462306a36Sopenharmony_ciout_revert: 301562306a36Sopenharmony_ci vmw_validation_revert(&val_ctx); 301662306a36Sopenharmony_ci 301762306a36Sopenharmony_ciout_unref: 301862306a36Sopenharmony_ci vmw_validation_unref_lists(&val_ctx); 301962306a36Sopenharmony_ci return ret; 302062306a36Sopenharmony_ci} 3021