18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT 28c2ecf20Sopenharmony_ci/************************************************************************** 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2009-2015 VMware, Inc., Palo Alto, CA., USA 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 78c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the 88c2ecf20Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 98c2ecf20Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 108c2ecf20Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 118c2ecf20Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 128c2ecf20Sopenharmony_ci * the following conditions: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the 158c2ecf20Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 168c2ecf20Sopenharmony_ci * of the Software. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 198c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 208c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 218c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 228c2ecf20Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 238c2ecf20Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 248c2ecf20Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci **************************************************************************/ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 298c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 308c2ecf20Sopenharmony_ci#include <drm/drm_damage_helper.h> 318c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 328c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h> 338c2ecf20Sopenharmony_ci#include <drm/drm_rect.h> 348c2ecf20Sopenharmony_ci#include <drm/drm_sysfs.h> 358c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include "vmwgfx_kms.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Might need a hrtimer here? */ 408c2ecf20Sopenharmony_ci#define VMWGFX_PRESENT_RATE ((HZ / 60 > 0) ? HZ / 60 : 1) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_civoid vmw_du_cleanup(struct vmw_display_unit *du) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci drm_plane_cleanup(&du->primary); 458c2ecf20Sopenharmony_ci drm_plane_cleanup(&du->cursor); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci drm_connector_unregister(&du->connector); 488c2ecf20Sopenharmony_ci drm_crtc_cleanup(&du->crtc); 498c2ecf20Sopenharmony_ci drm_encoder_cleanup(&du->encoder); 508c2ecf20Sopenharmony_ci drm_connector_cleanup(&du->connector); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * Display Unit Cursor functions 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic int vmw_cursor_update_image(struct vmw_private *dev_priv, 588c2ecf20Sopenharmony_ci u32 *image, u32 width, u32 height, 598c2ecf20Sopenharmony_ci u32 hotspotX, u32 hotspotY) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct { 628c2ecf20Sopenharmony_ci u32 cmd; 638c2ecf20Sopenharmony_ci SVGAFifoCmdDefineAlphaCursor cursor; 648c2ecf20Sopenharmony_ci } *cmd; 658c2ecf20Sopenharmony_ci u32 image_size = width * height * 4; 668c2ecf20Sopenharmony_ci u32 cmd_size = sizeof(*cmd) + image_size; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (!image) 698c2ecf20Sopenharmony_ci return -EINVAL; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci cmd = VMW_FIFO_RESERVE(dev_priv, cmd_size); 728c2ecf20Sopenharmony_ci if (unlikely(cmd == NULL)) 738c2ecf20Sopenharmony_ci return -ENOMEM; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci memset(cmd, 0, sizeof(*cmd)); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci memcpy(&cmd[1], image, image_size); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci cmd->cmd = SVGA_CMD_DEFINE_ALPHA_CURSOR; 808c2ecf20Sopenharmony_ci cmd->cursor.id = 0; 818c2ecf20Sopenharmony_ci cmd->cursor.width = width; 828c2ecf20Sopenharmony_ci cmd->cursor.height = height; 838c2ecf20Sopenharmony_ci cmd->cursor.hotspotX = hotspotX; 848c2ecf20Sopenharmony_ci cmd->cursor.hotspotY = hotspotY; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci vmw_fifo_commit_flush(dev_priv, cmd_size); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int vmw_cursor_update_bo(struct vmw_private *dev_priv, 928c2ecf20Sopenharmony_ci struct vmw_buffer_object *bo, 938c2ecf20Sopenharmony_ci u32 width, u32 height, 948c2ecf20Sopenharmony_ci u32 hotspotX, u32 hotspotY) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct ttm_bo_kmap_obj map; 978c2ecf20Sopenharmony_ci unsigned long kmap_offset; 988c2ecf20Sopenharmony_ci unsigned long kmap_num; 998c2ecf20Sopenharmony_ci void *virtual; 1008c2ecf20Sopenharmony_ci bool dummy; 1018c2ecf20Sopenharmony_ci int ret; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci kmap_offset = 0; 1048c2ecf20Sopenharmony_ci kmap_num = (width*height*4 + PAGE_SIZE - 1) >> PAGE_SHIFT; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci ret = ttm_bo_reserve(&bo->base, true, false, NULL); 1078c2ecf20Sopenharmony_ci if (unlikely(ret != 0)) { 1088c2ecf20Sopenharmony_ci DRM_ERROR("reserve failed\n"); 1098c2ecf20Sopenharmony_ci return -EINVAL; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci ret = ttm_bo_kmap(&bo->base, kmap_offset, kmap_num, &map); 1138c2ecf20Sopenharmony_ci if (unlikely(ret != 0)) 1148c2ecf20Sopenharmony_ci goto err_unreserve; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci virtual = ttm_kmap_obj_virtual(&map, &dummy); 1178c2ecf20Sopenharmony_ci ret = vmw_cursor_update_image(dev_priv, virtual, width, height, 1188c2ecf20Sopenharmony_ci hotspotX, hotspotY); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci ttm_bo_kunmap(&map); 1218c2ecf20Sopenharmony_cierr_unreserve: 1228c2ecf20Sopenharmony_ci ttm_bo_unreserve(&bo->base); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return ret; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void vmw_cursor_update_position(struct vmw_private *dev_priv, 1298c2ecf20Sopenharmony_ci bool show, int x, int y) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci u32 *fifo_mem = dev_priv->mmio_virt; 1328c2ecf20Sopenharmony_ci uint32_t count; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci spin_lock(&dev_priv->cursor_lock); 1358c2ecf20Sopenharmony_ci vmw_mmio_write(show ? 1 : 0, fifo_mem + SVGA_FIFO_CURSOR_ON); 1368c2ecf20Sopenharmony_ci vmw_mmio_write(x, fifo_mem + SVGA_FIFO_CURSOR_X); 1378c2ecf20Sopenharmony_ci vmw_mmio_write(y, fifo_mem + SVGA_FIFO_CURSOR_Y); 1388c2ecf20Sopenharmony_ci count = vmw_mmio_read(fifo_mem + SVGA_FIFO_CURSOR_COUNT); 1398c2ecf20Sopenharmony_ci vmw_mmio_write(++count, fifo_mem + SVGA_FIFO_CURSOR_COUNT); 1408c2ecf20Sopenharmony_ci spin_unlock(&dev_priv->cursor_lock); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_civoid vmw_kms_cursor_snoop(struct vmw_surface *srf, 1458c2ecf20Sopenharmony_ci struct ttm_object_file *tfile, 1468c2ecf20Sopenharmony_ci struct ttm_buffer_object *bo, 1478c2ecf20Sopenharmony_ci SVGA3dCmdHeader *header) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct ttm_bo_kmap_obj map; 1508c2ecf20Sopenharmony_ci unsigned long kmap_offset; 1518c2ecf20Sopenharmony_ci unsigned long kmap_num; 1528c2ecf20Sopenharmony_ci SVGA3dCopyBox *box; 1538c2ecf20Sopenharmony_ci unsigned box_count; 1548c2ecf20Sopenharmony_ci void *virtual; 1558c2ecf20Sopenharmony_ci bool dummy; 1568c2ecf20Sopenharmony_ci struct vmw_dma_cmd { 1578c2ecf20Sopenharmony_ci SVGA3dCmdHeader header; 1588c2ecf20Sopenharmony_ci SVGA3dCmdSurfaceDMA dma; 1598c2ecf20Sopenharmony_ci } *cmd; 1608c2ecf20Sopenharmony_ci int i, ret; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci cmd = container_of(header, struct vmw_dma_cmd, header); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* No snooper installed */ 1658c2ecf20Sopenharmony_ci if (!srf->snooper.image) 1668c2ecf20Sopenharmony_ci return; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (cmd->dma.host.face != 0 || cmd->dma.host.mipmap != 0) { 1698c2ecf20Sopenharmony_ci DRM_ERROR("face and mipmap for cursors should never != 0\n"); 1708c2ecf20Sopenharmony_ci return; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (cmd->header.size < 64) { 1748c2ecf20Sopenharmony_ci DRM_ERROR("at least one full copy box must be given\n"); 1758c2ecf20Sopenharmony_ci return; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci box = (SVGA3dCopyBox *)&cmd[1]; 1798c2ecf20Sopenharmony_ci box_count = (cmd->header.size - sizeof(SVGA3dCmdSurfaceDMA)) / 1808c2ecf20Sopenharmony_ci sizeof(SVGA3dCopyBox); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (cmd->dma.guest.ptr.offset % PAGE_SIZE || 1838c2ecf20Sopenharmony_ci box->x != 0 || box->y != 0 || box->z != 0 || 1848c2ecf20Sopenharmony_ci box->srcx != 0 || box->srcy != 0 || box->srcz != 0 || 1858c2ecf20Sopenharmony_ci box->d != 1 || box_count != 1 || 1868c2ecf20Sopenharmony_ci box->w > 64 || box->h > 64) { 1878c2ecf20Sopenharmony_ci /* TODO handle none page aligned offsets */ 1888c2ecf20Sopenharmony_ci /* TODO handle more dst & src != 0 */ 1898c2ecf20Sopenharmony_ci /* TODO handle more then one copy */ 1908c2ecf20Sopenharmony_ci DRM_ERROR("Can't snoop dma request for cursor!\n"); 1918c2ecf20Sopenharmony_ci DRM_ERROR("(%u, %u, %u) (%u, %u, %u) (%ux%ux%u) %u %u\n", 1928c2ecf20Sopenharmony_ci box->srcx, box->srcy, box->srcz, 1938c2ecf20Sopenharmony_ci box->x, box->y, box->z, 1948c2ecf20Sopenharmony_ci box->w, box->h, box->d, box_count, 1958c2ecf20Sopenharmony_ci cmd->dma.guest.ptr.offset); 1968c2ecf20Sopenharmony_ci return; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci kmap_offset = cmd->dma.guest.ptr.offset >> PAGE_SHIFT; 2008c2ecf20Sopenharmony_ci kmap_num = (64*64*4) >> PAGE_SHIFT; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci ret = ttm_bo_reserve(bo, true, false, NULL); 2038c2ecf20Sopenharmony_ci if (unlikely(ret != 0)) { 2048c2ecf20Sopenharmony_ci DRM_ERROR("reserve failed\n"); 2058c2ecf20Sopenharmony_ci return; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci ret = ttm_bo_kmap(bo, kmap_offset, kmap_num, &map); 2098c2ecf20Sopenharmony_ci if (unlikely(ret != 0)) 2108c2ecf20Sopenharmony_ci goto err_unreserve; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci virtual = ttm_kmap_obj_virtual(&map, &dummy); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (box->w == 64 && cmd->dma.guest.pitch == 64*4) { 2158c2ecf20Sopenharmony_ci memcpy(srf->snooper.image, virtual, 64*64*4); 2168c2ecf20Sopenharmony_ci } else { 2178c2ecf20Sopenharmony_ci /* Image is unsigned pointer. */ 2188c2ecf20Sopenharmony_ci for (i = 0; i < box->h; i++) 2198c2ecf20Sopenharmony_ci memcpy(srf->snooper.image + i * 64, 2208c2ecf20Sopenharmony_ci virtual + i * cmd->dma.guest.pitch, 2218c2ecf20Sopenharmony_ci box->w * 4); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci srf->snooper.age++; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci ttm_bo_kunmap(&map); 2278c2ecf20Sopenharmony_cierr_unreserve: 2288c2ecf20Sopenharmony_ci ttm_bo_unreserve(bo); 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci/** 2328c2ecf20Sopenharmony_ci * vmw_kms_legacy_hotspot_clear - Clear legacy hotspots 2338c2ecf20Sopenharmony_ci * 2348c2ecf20Sopenharmony_ci * @dev_priv: Pointer to the device private struct. 2358c2ecf20Sopenharmony_ci * 2368c2ecf20Sopenharmony_ci * Clears all legacy hotspots. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_civoid vmw_kms_legacy_hotspot_clear(struct vmw_private *dev_priv) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct drm_device *dev = dev_priv->dev; 2418c2ecf20Sopenharmony_ci struct vmw_display_unit *du; 2428c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci drm_modeset_lock_all(dev); 2458c2ecf20Sopenharmony_ci drm_for_each_crtc(crtc, dev) { 2468c2ecf20Sopenharmony_ci du = vmw_crtc_to_du(crtc); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci du->hotspot_x = 0; 2498c2ecf20Sopenharmony_ci du->hotspot_y = 0; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci drm_modeset_unlock_all(dev); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_civoid vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct drm_device *dev = dev_priv->dev; 2578c2ecf20Sopenharmony_ci struct vmw_display_unit *du; 2588c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci mutex_lock(&dev->mode_config.mutex); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 2638c2ecf20Sopenharmony_ci du = vmw_crtc_to_du(crtc); 2648c2ecf20Sopenharmony_ci if (!du->cursor_surface || 2658c2ecf20Sopenharmony_ci du->cursor_age == du->cursor_surface->snooper.age) 2668c2ecf20Sopenharmony_ci continue; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci du->cursor_age = du->cursor_surface->snooper.age; 2698c2ecf20Sopenharmony_ci vmw_cursor_update_image(dev_priv, 2708c2ecf20Sopenharmony_ci du->cursor_surface->snooper.image, 2718c2ecf20Sopenharmony_ci 64, 64, 2728c2ecf20Sopenharmony_ci du->hotspot_x + du->core_hotspot_x, 2738c2ecf20Sopenharmony_ci du->hotspot_y + du->core_hotspot_y); 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci mutex_unlock(&dev->mode_config.mutex); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_civoid vmw_du_cursor_plane_destroy(struct drm_plane *plane) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci vmw_cursor_update_position(plane->dev->dev_private, false, 0, 0); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci drm_plane_cleanup(plane); 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_civoid vmw_du_primary_plane_destroy(struct drm_plane *plane) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci drm_plane_cleanup(plane); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* Planes are static in our case so we don't free it */ 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci/** 2978c2ecf20Sopenharmony_ci * vmw_du_vps_unpin_surf - unpins resource associated with a framebuffer surface 2988c2ecf20Sopenharmony_ci * 2998c2ecf20Sopenharmony_ci * @vps: plane state associated with the display surface 3008c2ecf20Sopenharmony_ci * @unreference: true if we also want to unreference the display. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_civoid vmw_du_plane_unpin_surf(struct vmw_plane_state *vps, 3038c2ecf20Sopenharmony_ci bool unreference) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci if (vps->surf) { 3068c2ecf20Sopenharmony_ci if (vps->pinned) { 3078c2ecf20Sopenharmony_ci vmw_resource_unpin(&vps->surf->res); 3088c2ecf20Sopenharmony_ci vps->pinned--; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (unreference) { 3128c2ecf20Sopenharmony_ci if (vps->pinned) 3138c2ecf20Sopenharmony_ci DRM_ERROR("Surface still pinned\n"); 3148c2ecf20Sopenharmony_ci vmw_surface_unreference(&vps->surf); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci/** 3218c2ecf20Sopenharmony_ci * vmw_du_plane_cleanup_fb - Unpins the cursor 3228c2ecf20Sopenharmony_ci * 3238c2ecf20Sopenharmony_ci * @plane: display plane 3248c2ecf20Sopenharmony_ci * @old_state: Contains the FB to clean up 3258c2ecf20Sopenharmony_ci * 3268c2ecf20Sopenharmony_ci * Unpins the framebuffer surface 3278c2ecf20Sopenharmony_ci * 3288c2ecf20Sopenharmony_ci * Returns 0 on success 3298c2ecf20Sopenharmony_ci */ 3308c2ecf20Sopenharmony_civoid 3318c2ecf20Sopenharmony_civmw_du_plane_cleanup_fb(struct drm_plane *plane, 3328c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci vmw_du_plane_unpin_surf(vps, false); 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/** 3418c2ecf20Sopenharmony_ci * vmw_du_cursor_plane_prepare_fb - Readies the cursor by referencing it 3428c2ecf20Sopenharmony_ci * 3438c2ecf20Sopenharmony_ci * @plane: display plane 3448c2ecf20Sopenharmony_ci * @new_state: info on the new plane state, including the FB 3458c2ecf20Sopenharmony_ci * 3468c2ecf20Sopenharmony_ci * Returns 0 on success 3478c2ecf20Sopenharmony_ci */ 3488c2ecf20Sopenharmony_ciint 3498c2ecf20Sopenharmony_civmw_du_cursor_plane_prepare_fb(struct drm_plane *plane, 3508c2ecf20Sopenharmony_ci struct drm_plane_state *new_state) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = new_state->fb; 3538c2ecf20Sopenharmony_ci struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (vps->surf) 3578c2ecf20Sopenharmony_ci vmw_surface_unreference(&vps->surf); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (vps->bo) 3608c2ecf20Sopenharmony_ci vmw_bo_unreference(&vps->bo); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (fb) { 3638c2ecf20Sopenharmony_ci if (vmw_framebuffer_to_vfb(fb)->bo) { 3648c2ecf20Sopenharmony_ci vps->bo = vmw_framebuffer_to_vfbd(fb)->buffer; 3658c2ecf20Sopenharmony_ci vmw_bo_reference(vps->bo); 3668c2ecf20Sopenharmony_ci } else { 3678c2ecf20Sopenharmony_ci vps->surf = vmw_framebuffer_to_vfbs(fb)->surface; 3688c2ecf20Sopenharmony_ci vmw_surface_reference(vps->surf); 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_civoid 3778c2ecf20Sopenharmony_civmw_du_cursor_plane_atomic_update(struct drm_plane *plane, 3788c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc; 3818c2ecf20Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(crtc->dev); 3828c2ecf20Sopenharmony_ci struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 3838c2ecf20Sopenharmony_ci struct vmw_plane_state *vps = vmw_plane_state_to_vps(plane->state); 3848c2ecf20Sopenharmony_ci s32 hotspot_x, hotspot_y; 3858c2ecf20Sopenharmony_ci int ret = 0; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci hotspot_x = du->hotspot_x; 3898c2ecf20Sopenharmony_ci hotspot_y = du->hotspot_y; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (plane->state->fb) { 3928c2ecf20Sopenharmony_ci hotspot_x += plane->state->fb->hot_x; 3938c2ecf20Sopenharmony_ci hotspot_y += plane->state->fb->hot_y; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci du->cursor_surface = vps->surf; 3978c2ecf20Sopenharmony_ci du->cursor_bo = vps->bo; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (vps->surf) { 4008c2ecf20Sopenharmony_ci du->cursor_age = du->cursor_surface->snooper.age; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci ret = vmw_cursor_update_image(dev_priv, 4038c2ecf20Sopenharmony_ci vps->surf->snooper.image, 4048c2ecf20Sopenharmony_ci 64, 64, hotspot_x, 4058c2ecf20Sopenharmony_ci hotspot_y); 4068c2ecf20Sopenharmony_ci } else if (vps->bo) { 4078c2ecf20Sopenharmony_ci ret = vmw_cursor_update_bo(dev_priv, vps->bo, 4088c2ecf20Sopenharmony_ci plane->state->crtc_w, 4098c2ecf20Sopenharmony_ci plane->state->crtc_h, 4108c2ecf20Sopenharmony_ci hotspot_x, hotspot_y); 4118c2ecf20Sopenharmony_ci } else { 4128c2ecf20Sopenharmony_ci vmw_cursor_update_position(dev_priv, false, 0, 0); 4138c2ecf20Sopenharmony_ci return; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (!ret) { 4178c2ecf20Sopenharmony_ci du->cursor_x = plane->state->crtc_x + du->set_gui_x; 4188c2ecf20Sopenharmony_ci du->cursor_y = plane->state->crtc_y + du->set_gui_y; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci vmw_cursor_update_position(dev_priv, true, 4218c2ecf20Sopenharmony_ci du->cursor_x + hotspot_x, 4228c2ecf20Sopenharmony_ci du->cursor_y + hotspot_y); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci du->core_hotspot_x = hotspot_x - du->hotspot_x; 4258c2ecf20Sopenharmony_ci du->core_hotspot_y = hotspot_y - du->hotspot_y; 4268c2ecf20Sopenharmony_ci } else { 4278c2ecf20Sopenharmony_ci DRM_ERROR("Failed to update cursor image\n"); 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci/** 4338c2ecf20Sopenharmony_ci * vmw_du_primary_plane_atomic_check - check if the new state is okay 4348c2ecf20Sopenharmony_ci * 4358c2ecf20Sopenharmony_ci * @plane: display plane 4368c2ecf20Sopenharmony_ci * @state: info on the new plane state, including the FB 4378c2ecf20Sopenharmony_ci * 4388c2ecf20Sopenharmony_ci * Check if the new state is settable given the current state. Other 4398c2ecf20Sopenharmony_ci * than what the atomic helper checks, we care about crtc fitting 4408c2ecf20Sopenharmony_ci * the FB and maintaining one active framebuffer. 4418c2ecf20Sopenharmony_ci * 4428c2ecf20Sopenharmony_ci * Returns 0 on success 4438c2ecf20Sopenharmony_ci */ 4448c2ecf20Sopenharmony_ciint vmw_du_primary_plane_atomic_check(struct drm_plane *plane, 4458c2ecf20Sopenharmony_ci struct drm_plane_state *state) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state = NULL; 4488c2ecf20Sopenharmony_ci struct drm_framebuffer *new_fb = state->fb; 4498c2ecf20Sopenharmony_ci int ret; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (state->crtc) 4528c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci ret = drm_atomic_helper_check_plane_state(state, crtc_state, 4558c2ecf20Sopenharmony_ci DRM_PLANE_HELPER_NO_SCALING, 4568c2ecf20Sopenharmony_ci DRM_PLANE_HELPER_NO_SCALING, 4578c2ecf20Sopenharmony_ci false, true); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (!ret && new_fb) { 4608c2ecf20Sopenharmony_ci struct drm_crtc *crtc = state->crtc; 4618c2ecf20Sopenharmony_ci struct vmw_connector_state *vcs; 4628c2ecf20Sopenharmony_ci struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci vcs = vmw_connector_state_to_vcs(du->connector.state); 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return ret; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci/** 4738c2ecf20Sopenharmony_ci * vmw_du_cursor_plane_atomic_check - check if the new state is okay 4748c2ecf20Sopenharmony_ci * 4758c2ecf20Sopenharmony_ci * @plane: cursor plane 4768c2ecf20Sopenharmony_ci * @state: info on the new plane state 4778c2ecf20Sopenharmony_ci * 4788c2ecf20Sopenharmony_ci * This is a chance to fail if the new cursor state does not fit 4798c2ecf20Sopenharmony_ci * our requirements. 4808c2ecf20Sopenharmony_ci * 4818c2ecf20Sopenharmony_ci * Returns 0 on success 4828c2ecf20Sopenharmony_ci */ 4838c2ecf20Sopenharmony_ciint vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, 4848c2ecf20Sopenharmony_ci struct drm_plane_state *new_state) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci int ret = 0; 4878c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state = NULL; 4888c2ecf20Sopenharmony_ci struct vmw_surface *surface = NULL; 4898c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = new_state->fb; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (new_state->crtc) 4928c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_new_crtc_state(new_state->state, 4938c2ecf20Sopenharmony_ci new_state->crtc); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci ret = drm_atomic_helper_check_plane_state(new_state, crtc_state, 4968c2ecf20Sopenharmony_ci DRM_PLANE_HELPER_NO_SCALING, 4978c2ecf20Sopenharmony_ci DRM_PLANE_HELPER_NO_SCALING, 4988c2ecf20Sopenharmony_ci true, true); 4998c2ecf20Sopenharmony_ci if (ret) 5008c2ecf20Sopenharmony_ci return ret; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* Turning off */ 5038c2ecf20Sopenharmony_ci if (!fb) 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* A lot of the code assumes this */ 5078c2ecf20Sopenharmony_ci if (new_state->crtc_w != 64 || new_state->crtc_h != 64) { 5088c2ecf20Sopenharmony_ci DRM_ERROR("Invalid cursor dimensions (%d, %d)\n", 5098c2ecf20Sopenharmony_ci new_state->crtc_w, new_state->crtc_h); 5108c2ecf20Sopenharmony_ci ret = -EINVAL; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (!vmw_framebuffer_to_vfb(fb)->bo) 5148c2ecf20Sopenharmony_ci surface = vmw_framebuffer_to_vfbs(fb)->surface; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (surface && !surface->snooper.image) { 5178c2ecf20Sopenharmony_ci DRM_ERROR("surface not suitable for cursor\n"); 5188c2ecf20Sopenharmony_ci ret = -EINVAL; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci return ret; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ciint vmw_du_crtc_atomic_check(struct drm_crtc *crtc, 5268c2ecf20Sopenharmony_ci struct drm_crtc_state *new_state) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci struct vmw_display_unit *du = vmw_crtc_to_du(new_state->crtc); 5298c2ecf20Sopenharmony_ci int connector_mask = drm_connector_mask(&du->connector); 5308c2ecf20Sopenharmony_ci bool has_primary = new_state->plane_mask & 5318c2ecf20Sopenharmony_ci drm_plane_mask(crtc->primary); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* We always want to have an active plane with an active CRTC */ 5348c2ecf20Sopenharmony_ci if (has_primary != new_state->enable) 5358c2ecf20Sopenharmony_ci return -EINVAL; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (new_state->connector_mask != connector_mask && 5398c2ecf20Sopenharmony_ci new_state->connector_mask != 0) { 5408c2ecf20Sopenharmony_ci DRM_ERROR("Invalid connectors configuration\n"); 5418c2ecf20Sopenharmony_ci return -EINVAL; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* 5458c2ecf20Sopenharmony_ci * Our virtual device does not have a dot clock, so use the logical 5468c2ecf20Sopenharmony_ci * clock value as the dot clock. 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_ci if (new_state->mode.crtc_clock == 0) 5498c2ecf20Sopenharmony_ci new_state->adjusted_mode.crtc_clock = new_state->mode.clock; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_civoid vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, 5568c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_civoid vmw_du_crtc_atomic_flush(struct drm_crtc *crtc, 5628c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci struct drm_pending_vblank_event *event = crtc->state->event; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci if (event) { 5678c2ecf20Sopenharmony_ci crtc->state->event = NULL; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 5708c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(crtc, event); 5718c2ecf20Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci/** 5778c2ecf20Sopenharmony_ci * vmw_du_crtc_duplicate_state - duplicate crtc state 5788c2ecf20Sopenharmony_ci * @crtc: DRM crtc 5798c2ecf20Sopenharmony_ci * 5808c2ecf20Sopenharmony_ci * Allocates and returns a copy of the crtc state (both common and 5818c2ecf20Sopenharmony_ci * vmw-specific) for the specified crtc. 5828c2ecf20Sopenharmony_ci * 5838c2ecf20Sopenharmony_ci * Returns: The newly allocated crtc state, or NULL on failure. 5848c2ecf20Sopenharmony_ci */ 5858c2ecf20Sopenharmony_cistruct drm_crtc_state * 5868c2ecf20Sopenharmony_civmw_du_crtc_duplicate_state(struct drm_crtc *crtc) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci struct drm_crtc_state *state; 5898c2ecf20Sopenharmony_ci struct vmw_crtc_state *vcs; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (WARN_ON(!crtc->state)) 5928c2ecf20Sopenharmony_ci return NULL; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci vcs = kmemdup(crtc->state, sizeof(*vcs), GFP_KERNEL); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci if (!vcs) 5978c2ecf20Sopenharmony_ci return NULL; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci state = &vcs->base; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci __drm_atomic_helper_crtc_duplicate_state(crtc, state); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci return state; 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci/** 6088c2ecf20Sopenharmony_ci * vmw_du_crtc_reset - creates a blank vmw crtc state 6098c2ecf20Sopenharmony_ci * @crtc: DRM crtc 6108c2ecf20Sopenharmony_ci * 6118c2ecf20Sopenharmony_ci * Resets the atomic state for @crtc by freeing the state pointer (which 6128c2ecf20Sopenharmony_ci * might be NULL, e.g. at driver load time) and allocating a new empty state 6138c2ecf20Sopenharmony_ci * object. 6148c2ecf20Sopenharmony_ci */ 6158c2ecf20Sopenharmony_civoid vmw_du_crtc_reset(struct drm_crtc *crtc) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci struct vmw_crtc_state *vcs; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (crtc->state) { 6218c2ecf20Sopenharmony_ci __drm_atomic_helper_crtc_destroy_state(crtc->state); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci kfree(vmw_crtc_state_to_vcs(crtc->state)); 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci vcs = kzalloc(sizeof(*vcs), GFP_KERNEL); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (!vcs) { 6298c2ecf20Sopenharmony_ci DRM_ERROR("Cannot allocate vmw_crtc_state\n"); 6308c2ecf20Sopenharmony_ci return; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, &vcs->base); 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci/** 6388c2ecf20Sopenharmony_ci * vmw_du_crtc_destroy_state - destroy crtc state 6398c2ecf20Sopenharmony_ci * @crtc: DRM crtc 6408c2ecf20Sopenharmony_ci * @state: state object to destroy 6418c2ecf20Sopenharmony_ci * 6428c2ecf20Sopenharmony_ci * Destroys the crtc state (both common and vmw-specific) for the 6438c2ecf20Sopenharmony_ci * specified plane. 6448c2ecf20Sopenharmony_ci */ 6458c2ecf20Sopenharmony_civoid 6468c2ecf20Sopenharmony_civmw_du_crtc_destroy_state(struct drm_crtc *crtc, 6478c2ecf20Sopenharmony_ci struct drm_crtc_state *state) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci drm_atomic_helper_crtc_destroy_state(crtc, state); 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci/** 6548c2ecf20Sopenharmony_ci * vmw_du_plane_duplicate_state - duplicate plane state 6558c2ecf20Sopenharmony_ci * @plane: drm plane 6568c2ecf20Sopenharmony_ci * 6578c2ecf20Sopenharmony_ci * Allocates and returns a copy of the plane state (both common and 6588c2ecf20Sopenharmony_ci * vmw-specific) for the specified plane. 6598c2ecf20Sopenharmony_ci * 6608c2ecf20Sopenharmony_ci * Returns: The newly allocated plane state, or NULL on failure. 6618c2ecf20Sopenharmony_ci */ 6628c2ecf20Sopenharmony_cistruct drm_plane_state * 6638c2ecf20Sopenharmony_civmw_du_plane_duplicate_state(struct drm_plane *plane) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci struct drm_plane_state *state; 6668c2ecf20Sopenharmony_ci struct vmw_plane_state *vps; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci vps = kmemdup(plane->state, sizeof(*vps), GFP_KERNEL); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (!vps) 6718c2ecf20Sopenharmony_ci return NULL; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci vps->pinned = 0; 6748c2ecf20Sopenharmony_ci vps->cpp = 0; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci /* Each ref counted resource needs to be acquired again */ 6778c2ecf20Sopenharmony_ci if (vps->surf) 6788c2ecf20Sopenharmony_ci (void) vmw_surface_reference(vps->surf); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (vps->bo) 6818c2ecf20Sopenharmony_ci (void) vmw_bo_reference(vps->bo); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci state = &vps->base; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci __drm_atomic_helper_plane_duplicate_state(plane, state); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci return state; 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci/** 6928c2ecf20Sopenharmony_ci * vmw_du_plane_reset - creates a blank vmw plane state 6938c2ecf20Sopenharmony_ci * @plane: drm plane 6948c2ecf20Sopenharmony_ci * 6958c2ecf20Sopenharmony_ci * Resets the atomic state for @plane by freeing the state pointer (which might 6968c2ecf20Sopenharmony_ci * be NULL, e.g. at driver load time) and allocating a new empty state object. 6978c2ecf20Sopenharmony_ci */ 6988c2ecf20Sopenharmony_civoid vmw_du_plane_reset(struct drm_plane *plane) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci struct vmw_plane_state *vps; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (plane->state) 7048c2ecf20Sopenharmony_ci vmw_du_plane_destroy_state(plane, plane->state); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci vps = kzalloc(sizeof(*vps), GFP_KERNEL); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (!vps) { 7098c2ecf20Sopenharmony_ci DRM_ERROR("Cannot allocate vmw_plane_state\n"); 7108c2ecf20Sopenharmony_ci return; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci __drm_atomic_helper_plane_reset(plane, &vps->base); 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci/** 7188c2ecf20Sopenharmony_ci * vmw_du_plane_destroy_state - destroy plane state 7198c2ecf20Sopenharmony_ci * @plane: DRM plane 7208c2ecf20Sopenharmony_ci * @state: state object to destroy 7218c2ecf20Sopenharmony_ci * 7228c2ecf20Sopenharmony_ci * Destroys the plane state (both common and vmw-specific) for the 7238c2ecf20Sopenharmony_ci * specified plane. 7248c2ecf20Sopenharmony_ci */ 7258c2ecf20Sopenharmony_civoid 7268c2ecf20Sopenharmony_civmw_du_plane_destroy_state(struct drm_plane *plane, 7278c2ecf20Sopenharmony_ci struct drm_plane_state *state) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci struct vmw_plane_state *vps = vmw_plane_state_to_vps(state); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci /* Should have been freed by cleanup_fb */ 7338c2ecf20Sopenharmony_ci if (vps->surf) 7348c2ecf20Sopenharmony_ci vmw_surface_unreference(&vps->surf); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (vps->bo) 7378c2ecf20Sopenharmony_ci vmw_bo_unreference(&vps->bo); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci drm_atomic_helper_plane_destroy_state(plane, state); 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci/** 7448c2ecf20Sopenharmony_ci * vmw_du_connector_duplicate_state - duplicate connector state 7458c2ecf20Sopenharmony_ci * @connector: DRM connector 7468c2ecf20Sopenharmony_ci * 7478c2ecf20Sopenharmony_ci * Allocates and returns a copy of the connector state (both common and 7488c2ecf20Sopenharmony_ci * vmw-specific) for the specified connector. 7498c2ecf20Sopenharmony_ci * 7508c2ecf20Sopenharmony_ci * Returns: The newly allocated connector state, or NULL on failure. 7518c2ecf20Sopenharmony_ci */ 7528c2ecf20Sopenharmony_cistruct drm_connector_state * 7538c2ecf20Sopenharmony_civmw_du_connector_duplicate_state(struct drm_connector *connector) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci struct drm_connector_state *state; 7568c2ecf20Sopenharmony_ci struct vmw_connector_state *vcs; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (WARN_ON(!connector->state)) 7598c2ecf20Sopenharmony_ci return NULL; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci vcs = kmemdup(connector->state, sizeof(*vcs), GFP_KERNEL); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci if (!vcs) 7648c2ecf20Sopenharmony_ci return NULL; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci state = &vcs->base; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci __drm_atomic_helper_connector_duplicate_state(connector, state); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci return state; 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci/** 7758c2ecf20Sopenharmony_ci * vmw_du_connector_reset - creates a blank vmw connector state 7768c2ecf20Sopenharmony_ci * @connector: DRM connector 7778c2ecf20Sopenharmony_ci * 7788c2ecf20Sopenharmony_ci * Resets the atomic state for @connector by freeing the state pointer (which 7798c2ecf20Sopenharmony_ci * might be NULL, e.g. at driver load time) and allocating a new empty state 7808c2ecf20Sopenharmony_ci * object. 7818c2ecf20Sopenharmony_ci */ 7828c2ecf20Sopenharmony_civoid vmw_du_connector_reset(struct drm_connector *connector) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci struct vmw_connector_state *vcs; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci if (connector->state) { 7888c2ecf20Sopenharmony_ci __drm_atomic_helper_connector_destroy_state(connector->state); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci kfree(vmw_connector_state_to_vcs(connector->state)); 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci vcs = kzalloc(sizeof(*vcs), GFP_KERNEL); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (!vcs) { 7968c2ecf20Sopenharmony_ci DRM_ERROR("Cannot allocate vmw_connector_state\n"); 7978c2ecf20Sopenharmony_ci return; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci __drm_atomic_helper_connector_reset(connector, &vcs->base); 8018c2ecf20Sopenharmony_ci} 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci/** 8058c2ecf20Sopenharmony_ci * vmw_du_connector_destroy_state - destroy connector state 8068c2ecf20Sopenharmony_ci * @connector: DRM connector 8078c2ecf20Sopenharmony_ci * @state: state object to destroy 8088c2ecf20Sopenharmony_ci * 8098c2ecf20Sopenharmony_ci * Destroys the connector state (both common and vmw-specific) for the 8108c2ecf20Sopenharmony_ci * specified plane. 8118c2ecf20Sopenharmony_ci */ 8128c2ecf20Sopenharmony_civoid 8138c2ecf20Sopenharmony_civmw_du_connector_destroy_state(struct drm_connector *connector, 8148c2ecf20Sopenharmony_ci struct drm_connector_state *state) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci drm_atomic_helper_connector_destroy_state(connector, state); 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ci/* 8198c2ecf20Sopenharmony_ci * Generic framebuffer code 8208c2ecf20Sopenharmony_ci */ 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci/* 8238c2ecf20Sopenharmony_ci * Surface framebuffer code 8248c2ecf20Sopenharmony_ci */ 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci struct vmw_framebuffer_surface *vfbs = 8298c2ecf20Sopenharmony_ci vmw_framebuffer_to_vfbs(framebuffer); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci drm_framebuffer_cleanup(framebuffer); 8328c2ecf20Sopenharmony_ci vmw_surface_unreference(&vfbs->surface); 8338c2ecf20Sopenharmony_ci if (vfbs->base.user_obj) 8348c2ecf20Sopenharmony_ci ttm_base_object_unref(&vfbs->base.user_obj); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci kfree(vfbs); 8378c2ecf20Sopenharmony_ci} 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci/** 8408c2ecf20Sopenharmony_ci * vmw_kms_readback - Perform a readback from the screen system to 8418c2ecf20Sopenharmony_ci * a buffer-object backed framebuffer. 8428c2ecf20Sopenharmony_ci * 8438c2ecf20Sopenharmony_ci * @dev_priv: Pointer to the device private structure. 8448c2ecf20Sopenharmony_ci * @file_priv: Pointer to a struct drm_file identifying the caller. 8458c2ecf20Sopenharmony_ci * Must be set to NULL if @user_fence_rep is NULL. 8468c2ecf20Sopenharmony_ci * @vfb: Pointer to the buffer-object backed framebuffer. 8478c2ecf20Sopenharmony_ci * @user_fence_rep: User-space provided structure for fence information. 8488c2ecf20Sopenharmony_ci * Must be set to non-NULL if @file_priv is non-NULL. 8498c2ecf20Sopenharmony_ci * @vclips: Array of clip rects. 8508c2ecf20Sopenharmony_ci * @num_clips: Number of clip rects in @vclips. 8518c2ecf20Sopenharmony_ci * 8528c2ecf20Sopenharmony_ci * Returns 0 on success, negative error code on failure. -ERESTARTSYS if 8538c2ecf20Sopenharmony_ci * interrupted. 8548c2ecf20Sopenharmony_ci */ 8558c2ecf20Sopenharmony_ciint vmw_kms_readback(struct vmw_private *dev_priv, 8568c2ecf20Sopenharmony_ci struct drm_file *file_priv, 8578c2ecf20Sopenharmony_ci struct vmw_framebuffer *vfb, 8588c2ecf20Sopenharmony_ci struct drm_vmw_fence_rep __user *user_fence_rep, 8598c2ecf20Sopenharmony_ci struct drm_vmw_rect *vclips, 8608c2ecf20Sopenharmony_ci uint32_t num_clips) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci switch (dev_priv->active_display_unit) { 8638c2ecf20Sopenharmony_ci case vmw_du_screen_object: 8648c2ecf20Sopenharmony_ci return vmw_kms_sou_readback(dev_priv, file_priv, vfb, 8658c2ecf20Sopenharmony_ci user_fence_rep, vclips, num_clips, 8668c2ecf20Sopenharmony_ci NULL); 8678c2ecf20Sopenharmony_ci case vmw_du_screen_target: 8688c2ecf20Sopenharmony_ci return vmw_kms_stdu_dma(dev_priv, file_priv, vfb, 8698c2ecf20Sopenharmony_ci user_fence_rep, NULL, vclips, num_clips, 8708c2ecf20Sopenharmony_ci 1, false, true, NULL); 8718c2ecf20Sopenharmony_ci default: 8728c2ecf20Sopenharmony_ci WARN_ONCE(true, 8738c2ecf20Sopenharmony_ci "Readback called with invalid display system.\n"); 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci return -ENOSYS; 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_cistatic const struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = { 8818c2ecf20Sopenharmony_ci .destroy = vmw_framebuffer_surface_destroy, 8828c2ecf20Sopenharmony_ci .dirty = drm_atomic_helper_dirtyfb, 8838c2ecf20Sopenharmony_ci}; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cistatic int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, 8868c2ecf20Sopenharmony_ci struct vmw_surface *surface, 8878c2ecf20Sopenharmony_ci struct vmw_framebuffer **out, 8888c2ecf20Sopenharmony_ci const struct drm_mode_fb_cmd2 8898c2ecf20Sopenharmony_ci *mode_cmd, 8908c2ecf20Sopenharmony_ci bool is_bo_proxy) 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci struct drm_device *dev = dev_priv->dev; 8948c2ecf20Sopenharmony_ci struct vmw_framebuffer_surface *vfbs; 8958c2ecf20Sopenharmony_ci enum SVGA3dSurfaceFormat format; 8968c2ecf20Sopenharmony_ci int ret; 8978c2ecf20Sopenharmony_ci struct drm_format_name_buf format_name; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci /* 3D is only supported on HWv8 and newer hosts */ 9008c2ecf20Sopenharmony_ci if (dev_priv->active_display_unit == vmw_du_legacy) 9018c2ecf20Sopenharmony_ci return -ENOSYS; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci /* 9048c2ecf20Sopenharmony_ci * Sanity checks. 9058c2ecf20Sopenharmony_ci */ 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* Surface must be marked as a scanout. */ 9088c2ecf20Sopenharmony_ci if (unlikely(!surface->metadata.scanout)) 9098c2ecf20Sopenharmony_ci return -EINVAL; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci if (unlikely(surface->metadata.mip_levels[0] != 1 || 9128c2ecf20Sopenharmony_ci surface->metadata.num_sizes != 1 || 9138c2ecf20Sopenharmony_ci surface->metadata.base_size.width < mode_cmd->width || 9148c2ecf20Sopenharmony_ci surface->metadata.base_size.height < mode_cmd->height || 9158c2ecf20Sopenharmony_ci surface->metadata.base_size.depth != 1)) { 9168c2ecf20Sopenharmony_ci DRM_ERROR("Incompatible surface dimensions " 9178c2ecf20Sopenharmony_ci "for requested mode.\n"); 9188c2ecf20Sopenharmony_ci return -EINVAL; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci switch (mode_cmd->pixel_format) { 9228c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB8888: 9238c2ecf20Sopenharmony_ci format = SVGA3D_A8R8G8B8; 9248c2ecf20Sopenharmony_ci break; 9258c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB8888: 9268c2ecf20Sopenharmony_ci format = SVGA3D_X8R8G8B8; 9278c2ecf20Sopenharmony_ci break; 9288c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB565: 9298c2ecf20Sopenharmony_ci format = SVGA3D_R5G6B5; 9308c2ecf20Sopenharmony_ci break; 9318c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB1555: 9328c2ecf20Sopenharmony_ci format = SVGA3D_A1R5G5B5; 9338c2ecf20Sopenharmony_ci break; 9348c2ecf20Sopenharmony_ci default: 9358c2ecf20Sopenharmony_ci DRM_ERROR("Invalid pixel format: %s\n", 9368c2ecf20Sopenharmony_ci drm_get_format_name(mode_cmd->pixel_format, &format_name)); 9378c2ecf20Sopenharmony_ci return -EINVAL; 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci /* 9418c2ecf20Sopenharmony_ci * For DX, surface format validation is done when surface->scanout 9428c2ecf20Sopenharmony_ci * is set. 9438c2ecf20Sopenharmony_ci */ 9448c2ecf20Sopenharmony_ci if (!has_sm4_context(dev_priv) && format != surface->metadata.format) { 9458c2ecf20Sopenharmony_ci DRM_ERROR("Invalid surface format for requested mode.\n"); 9468c2ecf20Sopenharmony_ci return -EINVAL; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci vfbs = kzalloc(sizeof(*vfbs), GFP_KERNEL); 9508c2ecf20Sopenharmony_ci if (!vfbs) { 9518c2ecf20Sopenharmony_ci ret = -ENOMEM; 9528c2ecf20Sopenharmony_ci goto out_err1; 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci drm_helper_mode_fill_fb_struct(dev, &vfbs->base.base, mode_cmd); 9568c2ecf20Sopenharmony_ci vfbs->surface = vmw_surface_reference(surface); 9578c2ecf20Sopenharmony_ci vfbs->base.user_handle = mode_cmd->handles[0]; 9588c2ecf20Sopenharmony_ci vfbs->is_bo_proxy = is_bo_proxy; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci *out = &vfbs->base; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci ret = drm_framebuffer_init(dev, &vfbs->base.base, 9638c2ecf20Sopenharmony_ci &vmw_framebuffer_surface_funcs); 9648c2ecf20Sopenharmony_ci if (ret) 9658c2ecf20Sopenharmony_ci goto out_err2; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci return 0; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ciout_err2: 9708c2ecf20Sopenharmony_ci vmw_surface_unreference(&surface); 9718c2ecf20Sopenharmony_ci kfree(vfbs); 9728c2ecf20Sopenharmony_ciout_err1: 9738c2ecf20Sopenharmony_ci return ret; 9748c2ecf20Sopenharmony_ci} 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci/* 9778c2ecf20Sopenharmony_ci * Buffer-object framebuffer code 9788c2ecf20Sopenharmony_ci */ 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic void vmw_framebuffer_bo_destroy(struct drm_framebuffer *framebuffer) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci struct vmw_framebuffer_bo *vfbd = 9838c2ecf20Sopenharmony_ci vmw_framebuffer_to_vfbd(framebuffer); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci drm_framebuffer_cleanup(framebuffer); 9868c2ecf20Sopenharmony_ci vmw_bo_unreference(&vfbd->buffer); 9878c2ecf20Sopenharmony_ci if (vfbd->base.user_obj) 9888c2ecf20Sopenharmony_ci ttm_base_object_unref(&vfbd->base.user_obj); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci kfree(vfbd); 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cistatic int vmw_framebuffer_bo_dirty(struct drm_framebuffer *framebuffer, 9948c2ecf20Sopenharmony_ci struct drm_file *file_priv, 9958c2ecf20Sopenharmony_ci unsigned int flags, unsigned int color, 9968c2ecf20Sopenharmony_ci struct drm_clip_rect *clips, 9978c2ecf20Sopenharmony_ci unsigned int num_clips) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(framebuffer->dev); 10008c2ecf20Sopenharmony_ci struct vmw_framebuffer_bo *vfbd = 10018c2ecf20Sopenharmony_ci vmw_framebuffer_to_vfbd(framebuffer); 10028c2ecf20Sopenharmony_ci struct drm_clip_rect norect; 10038c2ecf20Sopenharmony_ci int ret, increment = 1; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci drm_modeset_lock_all(dev_priv->dev); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci ret = ttm_read_lock(&dev_priv->reservation_sem, true); 10088c2ecf20Sopenharmony_ci if (unlikely(ret != 0)) { 10098c2ecf20Sopenharmony_ci drm_modeset_unlock_all(dev_priv->dev); 10108c2ecf20Sopenharmony_ci return ret; 10118c2ecf20Sopenharmony_ci } 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci if (!num_clips) { 10148c2ecf20Sopenharmony_ci num_clips = 1; 10158c2ecf20Sopenharmony_ci clips = &norect; 10168c2ecf20Sopenharmony_ci norect.x1 = norect.y1 = 0; 10178c2ecf20Sopenharmony_ci norect.x2 = framebuffer->width; 10188c2ecf20Sopenharmony_ci norect.y2 = framebuffer->height; 10198c2ecf20Sopenharmony_ci } else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) { 10208c2ecf20Sopenharmony_ci num_clips /= 2; 10218c2ecf20Sopenharmony_ci increment = 2; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci switch (dev_priv->active_display_unit) { 10258c2ecf20Sopenharmony_ci case vmw_du_legacy: 10268c2ecf20Sopenharmony_ci ret = vmw_kms_ldu_do_bo_dirty(dev_priv, &vfbd->base, 0, 0, 10278c2ecf20Sopenharmony_ci clips, num_clips, increment); 10288c2ecf20Sopenharmony_ci break; 10298c2ecf20Sopenharmony_ci default: 10308c2ecf20Sopenharmony_ci ret = -EINVAL; 10318c2ecf20Sopenharmony_ci WARN_ONCE(true, "Dirty called with invalid display system.\n"); 10328c2ecf20Sopenharmony_ci break; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci vmw_fifo_flush(dev_priv, false); 10368c2ecf20Sopenharmony_ci ttm_read_unlock(&dev_priv->reservation_sem); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci drm_modeset_unlock_all(dev_priv->dev); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci return ret; 10418c2ecf20Sopenharmony_ci} 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_cistatic int vmw_framebuffer_bo_dirty_ext(struct drm_framebuffer *framebuffer, 10448c2ecf20Sopenharmony_ci struct drm_file *file_priv, 10458c2ecf20Sopenharmony_ci unsigned int flags, unsigned int color, 10468c2ecf20Sopenharmony_ci struct drm_clip_rect *clips, 10478c2ecf20Sopenharmony_ci unsigned int num_clips) 10488c2ecf20Sopenharmony_ci{ 10498c2ecf20Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(framebuffer->dev); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci if (dev_priv->active_display_unit == vmw_du_legacy) 10528c2ecf20Sopenharmony_ci return vmw_framebuffer_bo_dirty(framebuffer, file_priv, flags, 10538c2ecf20Sopenharmony_ci color, clips, num_clips); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci return drm_atomic_helper_dirtyfb(framebuffer, file_priv, flags, color, 10568c2ecf20Sopenharmony_ci clips, num_clips); 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_cistatic const struct drm_framebuffer_funcs vmw_framebuffer_bo_funcs = { 10608c2ecf20Sopenharmony_ci .destroy = vmw_framebuffer_bo_destroy, 10618c2ecf20Sopenharmony_ci .dirty = vmw_framebuffer_bo_dirty_ext, 10628c2ecf20Sopenharmony_ci}; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci/** 10658c2ecf20Sopenharmony_ci * Pin the bofer in a location suitable for access by the 10668c2ecf20Sopenharmony_ci * display system. 10678c2ecf20Sopenharmony_ci */ 10688c2ecf20Sopenharmony_cistatic int vmw_framebuffer_pin(struct vmw_framebuffer *vfb) 10698c2ecf20Sopenharmony_ci{ 10708c2ecf20Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(vfb->base.dev); 10718c2ecf20Sopenharmony_ci struct vmw_buffer_object *buf; 10728c2ecf20Sopenharmony_ci struct ttm_placement *placement; 10738c2ecf20Sopenharmony_ci int ret; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci buf = vfb->bo ? vmw_framebuffer_to_vfbd(&vfb->base)->buffer : 10768c2ecf20Sopenharmony_ci vmw_framebuffer_to_vfbs(&vfb->base)->surface->res.backup; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci if (!buf) 10798c2ecf20Sopenharmony_ci return 0; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci switch (dev_priv->active_display_unit) { 10828c2ecf20Sopenharmony_ci case vmw_du_legacy: 10838c2ecf20Sopenharmony_ci vmw_overlay_pause_all(dev_priv); 10848c2ecf20Sopenharmony_ci ret = vmw_bo_pin_in_start_of_vram(dev_priv, buf, false); 10858c2ecf20Sopenharmony_ci vmw_overlay_resume_all(dev_priv); 10868c2ecf20Sopenharmony_ci break; 10878c2ecf20Sopenharmony_ci case vmw_du_screen_object: 10888c2ecf20Sopenharmony_ci case vmw_du_screen_target: 10898c2ecf20Sopenharmony_ci if (vfb->bo) { 10908c2ecf20Sopenharmony_ci if (dev_priv->capabilities & SVGA_CAP_3D) { 10918c2ecf20Sopenharmony_ci /* 10928c2ecf20Sopenharmony_ci * Use surface DMA to get content to 10938c2ecf20Sopenharmony_ci * sreen target surface. 10948c2ecf20Sopenharmony_ci */ 10958c2ecf20Sopenharmony_ci placement = &vmw_vram_gmr_placement; 10968c2ecf20Sopenharmony_ci } else { 10978c2ecf20Sopenharmony_ci /* Use CPU blit. */ 10988c2ecf20Sopenharmony_ci placement = &vmw_sys_placement; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci } else { 11018c2ecf20Sopenharmony_ci /* Use surface / image update */ 11028c2ecf20Sopenharmony_ci placement = &vmw_mob_placement; 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci return vmw_bo_pin_in_placement(dev_priv, buf, placement, false); 11068c2ecf20Sopenharmony_ci default: 11078c2ecf20Sopenharmony_ci return -EINVAL; 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci return ret; 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_cistatic int vmw_framebuffer_unpin(struct vmw_framebuffer *vfb) 11148c2ecf20Sopenharmony_ci{ 11158c2ecf20Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(vfb->base.dev); 11168c2ecf20Sopenharmony_ci struct vmw_buffer_object *buf; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci buf = vfb->bo ? vmw_framebuffer_to_vfbd(&vfb->base)->buffer : 11198c2ecf20Sopenharmony_ci vmw_framebuffer_to_vfbs(&vfb->base)->surface->res.backup; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci if (WARN_ON(!buf)) 11228c2ecf20Sopenharmony_ci return 0; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci return vmw_bo_unpin(dev_priv, buf, false); 11258c2ecf20Sopenharmony_ci} 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci/** 11288c2ecf20Sopenharmony_ci * vmw_create_bo_proxy - create a proxy surface for the buffer object 11298c2ecf20Sopenharmony_ci * 11308c2ecf20Sopenharmony_ci * @dev: DRM device 11318c2ecf20Sopenharmony_ci * @mode_cmd: parameters for the new surface 11328c2ecf20Sopenharmony_ci * @bo_mob: MOB backing the buffer object 11338c2ecf20Sopenharmony_ci * @srf_out: newly created surface 11348c2ecf20Sopenharmony_ci * 11358c2ecf20Sopenharmony_ci * When the content FB is a buffer object, we create a surface as a proxy to the 11368c2ecf20Sopenharmony_ci * same buffer. This way we can do a surface copy rather than a surface DMA. 11378c2ecf20Sopenharmony_ci * This is a more efficient approach 11388c2ecf20Sopenharmony_ci * 11398c2ecf20Sopenharmony_ci * RETURNS: 11408c2ecf20Sopenharmony_ci * 0 on success, error code otherwise 11418c2ecf20Sopenharmony_ci */ 11428c2ecf20Sopenharmony_cistatic int vmw_create_bo_proxy(struct drm_device *dev, 11438c2ecf20Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd, 11448c2ecf20Sopenharmony_ci struct vmw_buffer_object *bo_mob, 11458c2ecf20Sopenharmony_ci struct vmw_surface **srf_out) 11468c2ecf20Sopenharmony_ci{ 11478c2ecf20Sopenharmony_ci struct vmw_surface_metadata metadata = {0}; 11488c2ecf20Sopenharmony_ci uint32_t format; 11498c2ecf20Sopenharmony_ci struct vmw_resource *res; 11508c2ecf20Sopenharmony_ci unsigned int bytes_pp; 11518c2ecf20Sopenharmony_ci struct drm_format_name_buf format_name; 11528c2ecf20Sopenharmony_ci int ret; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci switch (mode_cmd->pixel_format) { 11558c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB8888: 11568c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB8888: 11578c2ecf20Sopenharmony_ci format = SVGA3D_X8R8G8B8; 11588c2ecf20Sopenharmony_ci bytes_pp = 4; 11598c2ecf20Sopenharmony_ci break; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB565: 11628c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB1555: 11638c2ecf20Sopenharmony_ci format = SVGA3D_R5G6B5; 11648c2ecf20Sopenharmony_ci bytes_pp = 2; 11658c2ecf20Sopenharmony_ci break; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci case 8: 11688c2ecf20Sopenharmony_ci format = SVGA3D_P8; 11698c2ecf20Sopenharmony_ci bytes_pp = 1; 11708c2ecf20Sopenharmony_ci break; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci default: 11738c2ecf20Sopenharmony_ci DRM_ERROR("Invalid framebuffer format %s\n", 11748c2ecf20Sopenharmony_ci drm_get_format_name(mode_cmd->pixel_format, &format_name)); 11758c2ecf20Sopenharmony_ci return -EINVAL; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci metadata.format = format; 11798c2ecf20Sopenharmony_ci metadata.mip_levels[0] = 1; 11808c2ecf20Sopenharmony_ci metadata.num_sizes = 1; 11818c2ecf20Sopenharmony_ci metadata.base_size.width = mode_cmd->pitches[0] / bytes_pp; 11828c2ecf20Sopenharmony_ci metadata.base_size.height = mode_cmd->height; 11838c2ecf20Sopenharmony_ci metadata.base_size.depth = 1; 11848c2ecf20Sopenharmony_ci metadata.scanout = true; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci ret = vmw_gb_surface_define(vmw_priv(dev), 0, &metadata, srf_out); 11878c2ecf20Sopenharmony_ci if (ret) { 11888c2ecf20Sopenharmony_ci DRM_ERROR("Failed to allocate proxy content buffer\n"); 11898c2ecf20Sopenharmony_ci return ret; 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci res = &(*srf_out)->res; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci /* Reserve and switch the backing mob. */ 11958c2ecf20Sopenharmony_ci mutex_lock(&res->dev_priv->cmdbuf_mutex); 11968c2ecf20Sopenharmony_ci (void) vmw_resource_reserve(res, false, true); 11978c2ecf20Sopenharmony_ci vmw_bo_unreference(&res->backup); 11988c2ecf20Sopenharmony_ci res->backup = vmw_bo_reference(bo_mob); 11998c2ecf20Sopenharmony_ci res->backup_offset = 0; 12008c2ecf20Sopenharmony_ci vmw_resource_unreserve(res, false, false, false, NULL, 0); 12018c2ecf20Sopenharmony_ci mutex_unlock(&res->dev_priv->cmdbuf_mutex); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci return 0; 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_cistatic int vmw_kms_new_framebuffer_bo(struct vmw_private *dev_priv, 12098c2ecf20Sopenharmony_ci struct vmw_buffer_object *bo, 12108c2ecf20Sopenharmony_ci struct vmw_framebuffer **out, 12118c2ecf20Sopenharmony_ci const struct drm_mode_fb_cmd2 12128c2ecf20Sopenharmony_ci *mode_cmd) 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci{ 12158c2ecf20Sopenharmony_ci struct drm_device *dev = dev_priv->dev; 12168c2ecf20Sopenharmony_ci struct vmw_framebuffer_bo *vfbd; 12178c2ecf20Sopenharmony_ci unsigned int requested_size; 12188c2ecf20Sopenharmony_ci struct drm_format_name_buf format_name; 12198c2ecf20Sopenharmony_ci int ret; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci requested_size = mode_cmd->height * mode_cmd->pitches[0]; 12228c2ecf20Sopenharmony_ci if (unlikely(requested_size > bo->base.num_pages * PAGE_SIZE)) { 12238c2ecf20Sopenharmony_ci DRM_ERROR("Screen buffer object size is too small " 12248c2ecf20Sopenharmony_ci "for requested mode.\n"); 12258c2ecf20Sopenharmony_ci return -EINVAL; 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci /* Limited framebuffer color depth support for screen objects */ 12298c2ecf20Sopenharmony_ci if (dev_priv->active_display_unit == vmw_du_screen_object) { 12308c2ecf20Sopenharmony_ci switch (mode_cmd->pixel_format) { 12318c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB8888: 12328c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB8888: 12338c2ecf20Sopenharmony_ci break; 12348c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB1555: 12358c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB565: 12368c2ecf20Sopenharmony_ci break; 12378c2ecf20Sopenharmony_ci default: 12388c2ecf20Sopenharmony_ci DRM_ERROR("Invalid pixel format: %s\n", 12398c2ecf20Sopenharmony_ci drm_get_format_name(mode_cmd->pixel_format, &format_name)); 12408c2ecf20Sopenharmony_ci return -EINVAL; 12418c2ecf20Sopenharmony_ci } 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci vfbd = kzalloc(sizeof(*vfbd), GFP_KERNEL); 12458c2ecf20Sopenharmony_ci if (!vfbd) { 12468c2ecf20Sopenharmony_ci ret = -ENOMEM; 12478c2ecf20Sopenharmony_ci goto out_err1; 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci drm_helper_mode_fill_fb_struct(dev, &vfbd->base.base, mode_cmd); 12518c2ecf20Sopenharmony_ci vfbd->base.bo = true; 12528c2ecf20Sopenharmony_ci vfbd->buffer = vmw_bo_reference(bo); 12538c2ecf20Sopenharmony_ci vfbd->base.user_handle = mode_cmd->handles[0]; 12548c2ecf20Sopenharmony_ci *out = &vfbd->base; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci ret = drm_framebuffer_init(dev, &vfbd->base.base, 12578c2ecf20Sopenharmony_ci &vmw_framebuffer_bo_funcs); 12588c2ecf20Sopenharmony_ci if (ret) 12598c2ecf20Sopenharmony_ci goto out_err2; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci return 0; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ciout_err2: 12648c2ecf20Sopenharmony_ci vmw_bo_unreference(&bo); 12658c2ecf20Sopenharmony_ci kfree(vfbd); 12668c2ecf20Sopenharmony_ciout_err1: 12678c2ecf20Sopenharmony_ci return ret; 12688c2ecf20Sopenharmony_ci} 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci/** 12728c2ecf20Sopenharmony_ci * vmw_kms_srf_ok - check if a surface can be created 12738c2ecf20Sopenharmony_ci * 12748c2ecf20Sopenharmony_ci * @width: requested width 12758c2ecf20Sopenharmony_ci * @height: requested height 12768c2ecf20Sopenharmony_ci * 12778c2ecf20Sopenharmony_ci * Surfaces need to be less than texture size 12788c2ecf20Sopenharmony_ci */ 12798c2ecf20Sopenharmony_cistatic bool 12808c2ecf20Sopenharmony_civmw_kms_srf_ok(struct vmw_private *dev_priv, uint32_t width, uint32_t height) 12818c2ecf20Sopenharmony_ci{ 12828c2ecf20Sopenharmony_ci if (width > dev_priv->texture_max_width || 12838c2ecf20Sopenharmony_ci height > dev_priv->texture_max_height) 12848c2ecf20Sopenharmony_ci return false; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci return true; 12878c2ecf20Sopenharmony_ci} 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci/** 12908c2ecf20Sopenharmony_ci * vmw_kms_new_framebuffer - Create a new framebuffer. 12918c2ecf20Sopenharmony_ci * 12928c2ecf20Sopenharmony_ci * @dev_priv: Pointer to device private struct. 12938c2ecf20Sopenharmony_ci * @bo: Pointer to buffer object to wrap the kms framebuffer around. 12948c2ecf20Sopenharmony_ci * Either @bo or @surface must be NULL. 12958c2ecf20Sopenharmony_ci * @surface: Pointer to a surface to wrap the kms framebuffer around. 12968c2ecf20Sopenharmony_ci * Either @bo or @surface must be NULL. 12978c2ecf20Sopenharmony_ci * @only_2d: No presents will occur to this buffer object based framebuffer. 12988c2ecf20Sopenharmony_ci * This helps the code to do some important optimizations. 12998c2ecf20Sopenharmony_ci * @mode_cmd: Frame-buffer metadata. 13008c2ecf20Sopenharmony_ci */ 13018c2ecf20Sopenharmony_cistruct vmw_framebuffer * 13028c2ecf20Sopenharmony_civmw_kms_new_framebuffer(struct vmw_private *dev_priv, 13038c2ecf20Sopenharmony_ci struct vmw_buffer_object *bo, 13048c2ecf20Sopenharmony_ci struct vmw_surface *surface, 13058c2ecf20Sopenharmony_ci bool only_2d, 13068c2ecf20Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 13078c2ecf20Sopenharmony_ci{ 13088c2ecf20Sopenharmony_ci struct vmw_framebuffer *vfb = NULL; 13098c2ecf20Sopenharmony_ci bool is_bo_proxy = false; 13108c2ecf20Sopenharmony_ci int ret; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci /* 13138c2ecf20Sopenharmony_ci * We cannot use the SurfaceDMA command in an non-accelerated VM, 13148c2ecf20Sopenharmony_ci * therefore, wrap the buffer object in a surface so we can use the 13158c2ecf20Sopenharmony_ci * SurfaceCopy command. 13168c2ecf20Sopenharmony_ci */ 13178c2ecf20Sopenharmony_ci if (vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height) && 13188c2ecf20Sopenharmony_ci bo && only_2d && 13198c2ecf20Sopenharmony_ci mode_cmd->width > 64 && /* Don't create a proxy for cursor */ 13208c2ecf20Sopenharmony_ci dev_priv->active_display_unit == vmw_du_screen_target) { 13218c2ecf20Sopenharmony_ci ret = vmw_create_bo_proxy(dev_priv->dev, mode_cmd, 13228c2ecf20Sopenharmony_ci bo, &surface); 13238c2ecf20Sopenharmony_ci if (ret) 13248c2ecf20Sopenharmony_ci return ERR_PTR(ret); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci is_bo_proxy = true; 13278c2ecf20Sopenharmony_ci } 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci /* Create the new framebuffer depending one what we have */ 13308c2ecf20Sopenharmony_ci if (surface) { 13318c2ecf20Sopenharmony_ci ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb, 13328c2ecf20Sopenharmony_ci mode_cmd, 13338c2ecf20Sopenharmony_ci is_bo_proxy); 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci /* 13368c2ecf20Sopenharmony_ci * vmw_create_bo_proxy() adds a reference that is no longer 13378c2ecf20Sopenharmony_ci * needed 13388c2ecf20Sopenharmony_ci */ 13398c2ecf20Sopenharmony_ci if (is_bo_proxy) 13408c2ecf20Sopenharmony_ci vmw_surface_unreference(&surface); 13418c2ecf20Sopenharmony_ci } else if (bo) { 13428c2ecf20Sopenharmony_ci ret = vmw_kms_new_framebuffer_bo(dev_priv, bo, &vfb, 13438c2ecf20Sopenharmony_ci mode_cmd); 13448c2ecf20Sopenharmony_ci } else { 13458c2ecf20Sopenharmony_ci BUG(); 13468c2ecf20Sopenharmony_ci } 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci if (ret) 13498c2ecf20Sopenharmony_ci return ERR_PTR(ret); 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci vfb->pin = vmw_framebuffer_pin; 13528c2ecf20Sopenharmony_ci vfb->unpin = vmw_framebuffer_unpin; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci return vfb; 13558c2ecf20Sopenharmony_ci} 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci/* 13588c2ecf20Sopenharmony_ci * Generic Kernel modesetting functions 13598c2ecf20Sopenharmony_ci */ 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_cistatic struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, 13628c2ecf20Sopenharmony_ci struct drm_file *file_priv, 13638c2ecf20Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 13648c2ecf20Sopenharmony_ci{ 13658c2ecf20Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 13668c2ecf20Sopenharmony_ci struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; 13678c2ecf20Sopenharmony_ci struct vmw_framebuffer *vfb = NULL; 13688c2ecf20Sopenharmony_ci struct vmw_surface *surface = NULL; 13698c2ecf20Sopenharmony_ci struct vmw_buffer_object *bo = NULL; 13708c2ecf20Sopenharmony_ci struct ttm_base_object *user_obj; 13718c2ecf20Sopenharmony_ci int ret; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci /* 13748c2ecf20Sopenharmony_ci * Take a reference on the user object of the resource 13758c2ecf20Sopenharmony_ci * backing the kms fb. This ensures that user-space handle 13768c2ecf20Sopenharmony_ci * lookups on that resource will always work as long as 13778c2ecf20Sopenharmony_ci * it's registered with a kms framebuffer. This is important, 13788c2ecf20Sopenharmony_ci * since vmw_execbuf_process identifies resources in the 13798c2ecf20Sopenharmony_ci * command stream using user-space handles. 13808c2ecf20Sopenharmony_ci */ 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci user_obj = ttm_base_object_lookup(tfile, mode_cmd->handles[0]); 13838c2ecf20Sopenharmony_ci if (unlikely(user_obj == NULL)) { 13848c2ecf20Sopenharmony_ci DRM_ERROR("Could not locate requested kms frame buffer.\n"); 13858c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci /** 13898c2ecf20Sopenharmony_ci * End conditioned code. 13908c2ecf20Sopenharmony_ci */ 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci /* returns either a bo or surface */ 13938c2ecf20Sopenharmony_ci ret = vmw_user_lookup_handle(dev_priv, tfile, 13948c2ecf20Sopenharmony_ci mode_cmd->handles[0], 13958c2ecf20Sopenharmony_ci &surface, &bo); 13968c2ecf20Sopenharmony_ci if (ret) 13978c2ecf20Sopenharmony_ci goto err_out; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci if (!bo && 14018c2ecf20Sopenharmony_ci !vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height)) { 14028c2ecf20Sopenharmony_ci DRM_ERROR("Surface size cannot exceed %dx%d", 14038c2ecf20Sopenharmony_ci dev_priv->texture_max_width, 14048c2ecf20Sopenharmony_ci dev_priv->texture_max_height); 14058c2ecf20Sopenharmony_ci goto err_out; 14068c2ecf20Sopenharmony_ci } 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci vfb = vmw_kms_new_framebuffer(dev_priv, bo, surface, 14108c2ecf20Sopenharmony_ci !(dev_priv->capabilities & SVGA_CAP_3D), 14118c2ecf20Sopenharmony_ci mode_cmd); 14128c2ecf20Sopenharmony_ci if (IS_ERR(vfb)) { 14138c2ecf20Sopenharmony_ci ret = PTR_ERR(vfb); 14148c2ecf20Sopenharmony_ci goto err_out; 14158c2ecf20Sopenharmony_ci } 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_cierr_out: 14188c2ecf20Sopenharmony_ci /* vmw_user_lookup_handle takes one ref so does new_fb */ 14198c2ecf20Sopenharmony_ci if (bo) 14208c2ecf20Sopenharmony_ci vmw_bo_unreference(&bo); 14218c2ecf20Sopenharmony_ci if (surface) 14228c2ecf20Sopenharmony_ci vmw_surface_unreference(&surface); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci if (ret) { 14258c2ecf20Sopenharmony_ci DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret); 14268c2ecf20Sopenharmony_ci ttm_base_object_unref(&user_obj); 14278c2ecf20Sopenharmony_ci return ERR_PTR(ret); 14288c2ecf20Sopenharmony_ci } else 14298c2ecf20Sopenharmony_ci vfb->user_obj = user_obj; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci return &vfb->base; 14328c2ecf20Sopenharmony_ci} 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci/** 14358c2ecf20Sopenharmony_ci * vmw_kms_check_display_memory - Validates display memory required for a 14368c2ecf20Sopenharmony_ci * topology 14378c2ecf20Sopenharmony_ci * @dev: DRM device 14388c2ecf20Sopenharmony_ci * @num_rects: number of drm_rect in rects 14398c2ecf20Sopenharmony_ci * @rects: array of drm_rect representing the topology to validate indexed by 14408c2ecf20Sopenharmony_ci * crtc index. 14418c2ecf20Sopenharmony_ci * 14428c2ecf20Sopenharmony_ci * Returns: 14438c2ecf20Sopenharmony_ci * 0 on success otherwise negative error code 14448c2ecf20Sopenharmony_ci */ 14458c2ecf20Sopenharmony_cistatic int vmw_kms_check_display_memory(struct drm_device *dev, 14468c2ecf20Sopenharmony_ci uint32_t num_rects, 14478c2ecf20Sopenharmony_ci struct drm_rect *rects) 14488c2ecf20Sopenharmony_ci{ 14498c2ecf20Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 14508c2ecf20Sopenharmony_ci struct drm_rect bounding_box = {0}; 14518c2ecf20Sopenharmony_ci u64 total_pixels = 0, pixel_mem, bb_mem; 14528c2ecf20Sopenharmony_ci int i; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci for (i = 0; i < num_rects; i++) { 14558c2ecf20Sopenharmony_ci /* 14568c2ecf20Sopenharmony_ci * For STDU only individual screen (screen target) is limited by 14578c2ecf20Sopenharmony_ci * SCREENTARGET_MAX_WIDTH/HEIGHT registers. 14588c2ecf20Sopenharmony_ci */ 14598c2ecf20Sopenharmony_ci if (dev_priv->active_display_unit == vmw_du_screen_target && 14608c2ecf20Sopenharmony_ci (drm_rect_width(&rects[i]) > dev_priv->stdu_max_width || 14618c2ecf20Sopenharmony_ci drm_rect_height(&rects[i]) > dev_priv->stdu_max_height)) { 14628c2ecf20Sopenharmony_ci VMW_DEBUG_KMS("Screen size not supported.\n"); 14638c2ecf20Sopenharmony_ci return -EINVAL; 14648c2ecf20Sopenharmony_ci } 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci /* Bounding box upper left is at (0,0). */ 14678c2ecf20Sopenharmony_ci if (rects[i].x2 > bounding_box.x2) 14688c2ecf20Sopenharmony_ci bounding_box.x2 = rects[i].x2; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci if (rects[i].y2 > bounding_box.y2) 14718c2ecf20Sopenharmony_ci bounding_box.y2 = rects[i].y2; 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci total_pixels += (u64) drm_rect_width(&rects[i]) * 14748c2ecf20Sopenharmony_ci (u64) drm_rect_height(&rects[i]); 14758c2ecf20Sopenharmony_ci } 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci /* Virtual svga device primary limits are always in 32-bpp. */ 14788c2ecf20Sopenharmony_ci pixel_mem = total_pixels * 4; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci /* 14818c2ecf20Sopenharmony_ci * For HV10 and below prim_bb_mem is vram size. When 14828c2ecf20Sopenharmony_ci * SVGA_REG_MAX_PRIMARY_BOUNDING_BOX_MEM is not present vram size is 14838c2ecf20Sopenharmony_ci * limit on primary bounding box 14848c2ecf20Sopenharmony_ci */ 14858c2ecf20Sopenharmony_ci if (pixel_mem > dev_priv->prim_bb_mem) { 14868c2ecf20Sopenharmony_ci VMW_DEBUG_KMS("Combined output size too large.\n"); 14878c2ecf20Sopenharmony_ci return -EINVAL; 14888c2ecf20Sopenharmony_ci } 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci /* SVGA_CAP_NO_BB_RESTRICTION is available for STDU only. */ 14918c2ecf20Sopenharmony_ci if (dev_priv->active_display_unit != vmw_du_screen_target || 14928c2ecf20Sopenharmony_ci !(dev_priv->capabilities & SVGA_CAP_NO_BB_RESTRICTION)) { 14938c2ecf20Sopenharmony_ci bb_mem = (u64) bounding_box.x2 * bounding_box.y2 * 4; 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci if (bb_mem > dev_priv->prim_bb_mem) { 14968c2ecf20Sopenharmony_ci VMW_DEBUG_KMS("Topology is beyond supported limits.\n"); 14978c2ecf20Sopenharmony_ci return -EINVAL; 14988c2ecf20Sopenharmony_ci } 14998c2ecf20Sopenharmony_ci } 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci return 0; 15028c2ecf20Sopenharmony_ci} 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci/** 15058c2ecf20Sopenharmony_ci * vmw_crtc_state_and_lock - Return new or current crtc state with locked 15068c2ecf20Sopenharmony_ci * crtc mutex 15078c2ecf20Sopenharmony_ci * @state: The atomic state pointer containing the new atomic state 15088c2ecf20Sopenharmony_ci * @crtc: The crtc 15098c2ecf20Sopenharmony_ci * 15108c2ecf20Sopenharmony_ci * This function returns the new crtc state if it's part of the state update. 15118c2ecf20Sopenharmony_ci * Otherwise returns the current crtc state. It also makes sure that the 15128c2ecf20Sopenharmony_ci * crtc mutex is locked. 15138c2ecf20Sopenharmony_ci * 15148c2ecf20Sopenharmony_ci * Returns: A valid crtc state pointer or NULL. It may also return a 15158c2ecf20Sopenharmony_ci * pointer error, in particular -EDEADLK if locking needs to be rerun. 15168c2ecf20Sopenharmony_ci */ 15178c2ecf20Sopenharmony_cistatic struct drm_crtc_state * 15188c2ecf20Sopenharmony_civmw_crtc_state_and_lock(struct drm_atomic_state *state, struct drm_crtc *crtc) 15198c2ecf20Sopenharmony_ci{ 15208c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 15238c2ecf20Sopenharmony_ci if (crtc_state) { 15248c2ecf20Sopenharmony_ci lockdep_assert_held(&crtc->mutex.mutex.base); 15258c2ecf20Sopenharmony_ci } else { 15268c2ecf20Sopenharmony_ci int ret = drm_modeset_lock(&crtc->mutex, state->acquire_ctx); 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci if (ret != 0 && ret != -EALREADY) 15298c2ecf20Sopenharmony_ci return ERR_PTR(ret); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci crtc_state = crtc->state; 15328c2ecf20Sopenharmony_ci } 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci return crtc_state; 15358c2ecf20Sopenharmony_ci} 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci/** 15388c2ecf20Sopenharmony_ci * vmw_kms_check_implicit - Verify that all implicit display units scan out 15398c2ecf20Sopenharmony_ci * from the same fb after the new state is committed. 15408c2ecf20Sopenharmony_ci * @dev: The drm_device. 15418c2ecf20Sopenharmony_ci * @state: The new state to be checked. 15428c2ecf20Sopenharmony_ci * 15438c2ecf20Sopenharmony_ci * Returns: 15448c2ecf20Sopenharmony_ci * Zero on success, 15458c2ecf20Sopenharmony_ci * -EINVAL on invalid state, 15468c2ecf20Sopenharmony_ci * -EDEADLK if modeset locking needs to be rerun. 15478c2ecf20Sopenharmony_ci */ 15488c2ecf20Sopenharmony_cistatic int vmw_kms_check_implicit(struct drm_device *dev, 15498c2ecf20Sopenharmony_ci struct drm_atomic_state *state) 15508c2ecf20Sopenharmony_ci{ 15518c2ecf20Sopenharmony_ci struct drm_framebuffer *implicit_fb = NULL; 15528c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 15538c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 15548c2ecf20Sopenharmony_ci struct drm_plane_state *plane_state; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci drm_for_each_crtc(crtc, dev) { 15578c2ecf20Sopenharmony_ci struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci if (!du->is_implicit) 15608c2ecf20Sopenharmony_ci continue; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci crtc_state = vmw_crtc_state_and_lock(state, crtc); 15638c2ecf20Sopenharmony_ci if (IS_ERR(crtc_state)) 15648c2ecf20Sopenharmony_ci return PTR_ERR(crtc_state); 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci if (!crtc_state || !crtc_state->enable) 15678c2ecf20Sopenharmony_ci continue; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci /* 15708c2ecf20Sopenharmony_ci * Can't move primary planes across crtcs, so this is OK. 15718c2ecf20Sopenharmony_ci * It also means we don't need to take the plane mutex. 15728c2ecf20Sopenharmony_ci */ 15738c2ecf20Sopenharmony_ci plane_state = du->primary.state; 15748c2ecf20Sopenharmony_ci if (plane_state->crtc != crtc) 15758c2ecf20Sopenharmony_ci continue; 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci if (!implicit_fb) 15788c2ecf20Sopenharmony_ci implicit_fb = plane_state->fb; 15798c2ecf20Sopenharmony_ci else if (implicit_fb != plane_state->fb) 15808c2ecf20Sopenharmony_ci return -EINVAL; 15818c2ecf20Sopenharmony_ci } 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci return 0; 15848c2ecf20Sopenharmony_ci} 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci/** 15878c2ecf20Sopenharmony_ci * vmw_kms_check_topology - Validates topology in drm_atomic_state 15888c2ecf20Sopenharmony_ci * @dev: DRM device 15898c2ecf20Sopenharmony_ci * @state: the driver state object 15908c2ecf20Sopenharmony_ci * 15918c2ecf20Sopenharmony_ci * Returns: 15928c2ecf20Sopenharmony_ci * 0 on success otherwise negative error code 15938c2ecf20Sopenharmony_ci */ 15948c2ecf20Sopenharmony_cistatic int vmw_kms_check_topology(struct drm_device *dev, 15958c2ecf20Sopenharmony_ci struct drm_atomic_state *state) 15968c2ecf20Sopenharmony_ci{ 15978c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state, *new_crtc_state; 15988c2ecf20Sopenharmony_ci struct drm_rect *rects; 15998c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 16008c2ecf20Sopenharmony_ci uint32_t i; 16018c2ecf20Sopenharmony_ci int ret = 0; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci rects = kcalloc(dev->mode_config.num_crtc, sizeof(struct drm_rect), 16048c2ecf20Sopenharmony_ci GFP_KERNEL); 16058c2ecf20Sopenharmony_ci if (!rects) 16068c2ecf20Sopenharmony_ci return -ENOMEM; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci drm_for_each_crtc(crtc, dev) { 16098c2ecf20Sopenharmony_ci struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 16108c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci i = drm_crtc_index(crtc); 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci crtc_state = vmw_crtc_state_and_lock(state, crtc); 16158c2ecf20Sopenharmony_ci if (IS_ERR(crtc_state)) { 16168c2ecf20Sopenharmony_ci ret = PTR_ERR(crtc_state); 16178c2ecf20Sopenharmony_ci goto clean; 16188c2ecf20Sopenharmony_ci } 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci if (!crtc_state) 16218c2ecf20Sopenharmony_ci continue; 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci if (crtc_state->enable) { 16248c2ecf20Sopenharmony_ci rects[i].x1 = du->gui_x; 16258c2ecf20Sopenharmony_ci rects[i].y1 = du->gui_y; 16268c2ecf20Sopenharmony_ci rects[i].x2 = du->gui_x + crtc_state->mode.hdisplay; 16278c2ecf20Sopenharmony_ci rects[i].y2 = du->gui_y + crtc_state->mode.vdisplay; 16288c2ecf20Sopenharmony_ci } else { 16298c2ecf20Sopenharmony_ci rects[i].x1 = 0; 16308c2ecf20Sopenharmony_ci rects[i].y1 = 0; 16318c2ecf20Sopenharmony_ci rects[i].x2 = 0; 16328c2ecf20Sopenharmony_ci rects[i].y2 = 0; 16338c2ecf20Sopenharmony_ci } 16348c2ecf20Sopenharmony_ci } 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci /* Determine change to topology due to new atomic state */ 16378c2ecf20Sopenharmony_ci for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, 16388c2ecf20Sopenharmony_ci new_crtc_state, i) { 16398c2ecf20Sopenharmony_ci struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 16408c2ecf20Sopenharmony_ci struct drm_connector *connector; 16418c2ecf20Sopenharmony_ci struct drm_connector_state *conn_state; 16428c2ecf20Sopenharmony_ci struct vmw_connector_state *vmw_conn_state; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci if (!du->pref_active && new_crtc_state->enable) { 16458c2ecf20Sopenharmony_ci VMW_DEBUG_KMS("Enabling a disabled display unit\n"); 16468c2ecf20Sopenharmony_ci ret = -EINVAL; 16478c2ecf20Sopenharmony_ci goto clean; 16488c2ecf20Sopenharmony_ci } 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci /* 16518c2ecf20Sopenharmony_ci * For vmwgfx each crtc has only one connector attached and it 16528c2ecf20Sopenharmony_ci * is not changed so don't really need to check the 16538c2ecf20Sopenharmony_ci * crtc->connector_mask and iterate over it. 16548c2ecf20Sopenharmony_ci */ 16558c2ecf20Sopenharmony_ci connector = &du->connector; 16568c2ecf20Sopenharmony_ci conn_state = drm_atomic_get_connector_state(state, connector); 16578c2ecf20Sopenharmony_ci if (IS_ERR(conn_state)) { 16588c2ecf20Sopenharmony_ci ret = PTR_ERR(conn_state); 16598c2ecf20Sopenharmony_ci goto clean; 16608c2ecf20Sopenharmony_ci } 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci vmw_conn_state = vmw_connector_state_to_vcs(conn_state); 16638c2ecf20Sopenharmony_ci vmw_conn_state->gui_x = du->gui_x; 16648c2ecf20Sopenharmony_ci vmw_conn_state->gui_y = du->gui_y; 16658c2ecf20Sopenharmony_ci } 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci ret = vmw_kms_check_display_memory(dev, dev->mode_config.num_crtc, 16688c2ecf20Sopenharmony_ci rects); 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ciclean: 16718c2ecf20Sopenharmony_ci kfree(rects); 16728c2ecf20Sopenharmony_ci return ret; 16738c2ecf20Sopenharmony_ci} 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci/** 16768c2ecf20Sopenharmony_ci * vmw_kms_atomic_check_modeset- validate state object for modeset changes 16778c2ecf20Sopenharmony_ci * 16788c2ecf20Sopenharmony_ci * @dev: DRM device 16798c2ecf20Sopenharmony_ci * @state: the driver state object 16808c2ecf20Sopenharmony_ci * 16818c2ecf20Sopenharmony_ci * This is a simple wrapper around drm_atomic_helper_check_modeset() for 16828c2ecf20Sopenharmony_ci * us to assign a value to mode->crtc_clock so that 16838c2ecf20Sopenharmony_ci * drm_calc_timestamping_constants() won't throw an error message 16848c2ecf20Sopenharmony_ci * 16858c2ecf20Sopenharmony_ci * Returns: 16868c2ecf20Sopenharmony_ci * Zero for success or -errno 16878c2ecf20Sopenharmony_ci */ 16888c2ecf20Sopenharmony_cistatic int 16898c2ecf20Sopenharmony_civmw_kms_atomic_check_modeset(struct drm_device *dev, 16908c2ecf20Sopenharmony_ci struct drm_atomic_state *state) 16918c2ecf20Sopenharmony_ci{ 16928c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 16938c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 16948c2ecf20Sopenharmony_ci bool need_modeset = false; 16958c2ecf20Sopenharmony_ci int i, ret; 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci ret = drm_atomic_helper_check(dev, state); 16988c2ecf20Sopenharmony_ci if (ret) 16998c2ecf20Sopenharmony_ci return ret; 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci ret = vmw_kms_check_implicit(dev, state); 17028c2ecf20Sopenharmony_ci if (ret) { 17038c2ecf20Sopenharmony_ci VMW_DEBUG_KMS("Invalid implicit state\n"); 17048c2ecf20Sopenharmony_ci return ret; 17058c2ecf20Sopenharmony_ci } 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci for_each_new_crtc_in_state(state, crtc, crtc_state, i) { 17088c2ecf20Sopenharmony_ci if (drm_atomic_crtc_needs_modeset(crtc_state)) 17098c2ecf20Sopenharmony_ci need_modeset = true; 17108c2ecf20Sopenharmony_ci } 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci if (need_modeset) 17138c2ecf20Sopenharmony_ci return vmw_kms_check_topology(dev, state); 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci return ret; 17168c2ecf20Sopenharmony_ci} 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_cistatic const struct drm_mode_config_funcs vmw_kms_funcs = { 17198c2ecf20Sopenharmony_ci .fb_create = vmw_kms_fb_create, 17208c2ecf20Sopenharmony_ci .atomic_check = vmw_kms_atomic_check_modeset, 17218c2ecf20Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 17228c2ecf20Sopenharmony_ci}; 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_cistatic int vmw_kms_generic_present(struct vmw_private *dev_priv, 17258c2ecf20Sopenharmony_ci struct drm_file *file_priv, 17268c2ecf20Sopenharmony_ci struct vmw_framebuffer *vfb, 17278c2ecf20Sopenharmony_ci struct vmw_surface *surface, 17288c2ecf20Sopenharmony_ci uint32_t sid, 17298c2ecf20Sopenharmony_ci int32_t destX, int32_t destY, 17308c2ecf20Sopenharmony_ci struct drm_vmw_rect *clips, 17318c2ecf20Sopenharmony_ci uint32_t num_clips) 17328c2ecf20Sopenharmony_ci{ 17338c2ecf20Sopenharmony_ci return vmw_kms_sou_do_surface_dirty(dev_priv, vfb, NULL, clips, 17348c2ecf20Sopenharmony_ci &surface->res, destX, destY, 17358c2ecf20Sopenharmony_ci num_clips, 1, NULL, NULL); 17368c2ecf20Sopenharmony_ci} 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ciint vmw_kms_present(struct vmw_private *dev_priv, 17408c2ecf20Sopenharmony_ci struct drm_file *file_priv, 17418c2ecf20Sopenharmony_ci struct vmw_framebuffer *vfb, 17428c2ecf20Sopenharmony_ci struct vmw_surface *surface, 17438c2ecf20Sopenharmony_ci uint32_t sid, 17448c2ecf20Sopenharmony_ci int32_t destX, int32_t destY, 17458c2ecf20Sopenharmony_ci struct drm_vmw_rect *clips, 17468c2ecf20Sopenharmony_ci uint32_t num_clips) 17478c2ecf20Sopenharmony_ci{ 17488c2ecf20Sopenharmony_ci int ret; 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci switch (dev_priv->active_display_unit) { 17518c2ecf20Sopenharmony_ci case vmw_du_screen_target: 17528c2ecf20Sopenharmony_ci ret = vmw_kms_stdu_surface_dirty(dev_priv, vfb, NULL, clips, 17538c2ecf20Sopenharmony_ci &surface->res, destX, destY, 17548c2ecf20Sopenharmony_ci num_clips, 1, NULL, NULL); 17558c2ecf20Sopenharmony_ci break; 17568c2ecf20Sopenharmony_ci case vmw_du_screen_object: 17578c2ecf20Sopenharmony_ci ret = vmw_kms_generic_present(dev_priv, file_priv, vfb, surface, 17588c2ecf20Sopenharmony_ci sid, destX, destY, clips, 17598c2ecf20Sopenharmony_ci num_clips); 17608c2ecf20Sopenharmony_ci break; 17618c2ecf20Sopenharmony_ci default: 17628c2ecf20Sopenharmony_ci WARN_ONCE(true, 17638c2ecf20Sopenharmony_ci "Present called with invalid display system.\n"); 17648c2ecf20Sopenharmony_ci ret = -ENOSYS; 17658c2ecf20Sopenharmony_ci break; 17668c2ecf20Sopenharmony_ci } 17678c2ecf20Sopenharmony_ci if (ret) 17688c2ecf20Sopenharmony_ci return ret; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci vmw_fifo_flush(dev_priv, false); 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci return 0; 17738c2ecf20Sopenharmony_ci} 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_cistatic void 17768c2ecf20Sopenharmony_civmw_kms_create_hotplug_mode_update_property(struct vmw_private *dev_priv) 17778c2ecf20Sopenharmony_ci{ 17788c2ecf20Sopenharmony_ci if (dev_priv->hotplug_mode_update_property) 17798c2ecf20Sopenharmony_ci return; 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci dev_priv->hotplug_mode_update_property = 17828c2ecf20Sopenharmony_ci drm_property_create_range(dev_priv->dev, 17838c2ecf20Sopenharmony_ci DRM_MODE_PROP_IMMUTABLE, 17848c2ecf20Sopenharmony_ci "hotplug_mode_update", 0, 1); 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci if (!dev_priv->hotplug_mode_update_property) 17878c2ecf20Sopenharmony_ci return; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci} 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ciint vmw_kms_init(struct vmw_private *dev_priv) 17928c2ecf20Sopenharmony_ci{ 17938c2ecf20Sopenharmony_ci struct drm_device *dev = dev_priv->dev; 17948c2ecf20Sopenharmony_ci int ret; 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci drm_mode_config_init(dev); 17978c2ecf20Sopenharmony_ci dev->mode_config.funcs = &vmw_kms_funcs; 17988c2ecf20Sopenharmony_ci dev->mode_config.min_width = 1; 17998c2ecf20Sopenharmony_ci dev->mode_config.min_height = 1; 18008c2ecf20Sopenharmony_ci dev->mode_config.max_width = dev_priv->texture_max_width; 18018c2ecf20Sopenharmony_ci dev->mode_config.max_height = dev_priv->texture_max_height; 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci drm_mode_create_suggested_offset_properties(dev); 18048c2ecf20Sopenharmony_ci vmw_kms_create_hotplug_mode_update_property(dev_priv); 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci ret = vmw_kms_stdu_init_display(dev_priv); 18078c2ecf20Sopenharmony_ci if (ret) { 18088c2ecf20Sopenharmony_ci ret = vmw_kms_sou_init_display(dev_priv); 18098c2ecf20Sopenharmony_ci if (ret) /* Fallback */ 18108c2ecf20Sopenharmony_ci ret = vmw_kms_ldu_init_display(dev_priv); 18118c2ecf20Sopenharmony_ci } 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci return ret; 18148c2ecf20Sopenharmony_ci} 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ciint vmw_kms_close(struct vmw_private *dev_priv) 18178c2ecf20Sopenharmony_ci{ 18188c2ecf20Sopenharmony_ci int ret = 0; 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci /* 18218c2ecf20Sopenharmony_ci * Docs says we should take the lock before calling this function 18228c2ecf20Sopenharmony_ci * but since it destroys encoders and our destructor calls 18238c2ecf20Sopenharmony_ci * drm_encoder_cleanup which takes the lock we deadlock. 18248c2ecf20Sopenharmony_ci */ 18258c2ecf20Sopenharmony_ci drm_mode_config_cleanup(dev_priv->dev); 18268c2ecf20Sopenharmony_ci if (dev_priv->active_display_unit == vmw_du_legacy) 18278c2ecf20Sopenharmony_ci ret = vmw_kms_ldu_close_display(dev_priv); 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci return ret; 18308c2ecf20Sopenharmony_ci} 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ciint vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data, 18338c2ecf20Sopenharmony_ci struct drm_file *file_priv) 18348c2ecf20Sopenharmony_ci{ 18358c2ecf20Sopenharmony_ci struct drm_vmw_cursor_bypass_arg *arg = data; 18368c2ecf20Sopenharmony_ci struct vmw_display_unit *du; 18378c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 18388c2ecf20Sopenharmony_ci int ret = 0; 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci mutex_lock(&dev->mode_config.mutex); 18428c2ecf20Sopenharmony_ci if (arg->flags & DRM_VMW_CURSOR_BYPASS_ALL) { 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 18458c2ecf20Sopenharmony_ci du = vmw_crtc_to_du(crtc); 18468c2ecf20Sopenharmony_ci du->hotspot_x = arg->xhot; 18478c2ecf20Sopenharmony_ci du->hotspot_y = arg->yhot; 18488c2ecf20Sopenharmony_ci } 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci mutex_unlock(&dev->mode_config.mutex); 18518c2ecf20Sopenharmony_ci return 0; 18528c2ecf20Sopenharmony_ci } 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci crtc = drm_crtc_find(dev, file_priv, arg->crtc_id); 18558c2ecf20Sopenharmony_ci if (!crtc) { 18568c2ecf20Sopenharmony_ci ret = -ENOENT; 18578c2ecf20Sopenharmony_ci goto out; 18588c2ecf20Sopenharmony_ci } 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci du = vmw_crtc_to_du(crtc); 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci du->hotspot_x = arg->xhot; 18638c2ecf20Sopenharmony_ci du->hotspot_y = arg->yhot; 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ciout: 18668c2ecf20Sopenharmony_ci mutex_unlock(&dev->mode_config.mutex); 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci return ret; 18698c2ecf20Sopenharmony_ci} 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ciint vmw_kms_write_svga(struct vmw_private *vmw_priv, 18728c2ecf20Sopenharmony_ci unsigned width, unsigned height, unsigned pitch, 18738c2ecf20Sopenharmony_ci unsigned bpp, unsigned depth) 18748c2ecf20Sopenharmony_ci{ 18758c2ecf20Sopenharmony_ci if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK) 18768c2ecf20Sopenharmony_ci vmw_write(vmw_priv, SVGA_REG_PITCHLOCK, pitch); 18778c2ecf20Sopenharmony_ci else if (vmw_fifo_have_pitchlock(vmw_priv)) 18788c2ecf20Sopenharmony_ci vmw_mmio_write(pitch, vmw_priv->mmio_virt + 18798c2ecf20Sopenharmony_ci SVGA_FIFO_PITCHLOCK); 18808c2ecf20Sopenharmony_ci vmw_write(vmw_priv, SVGA_REG_WIDTH, width); 18818c2ecf20Sopenharmony_ci vmw_write(vmw_priv, SVGA_REG_HEIGHT, height); 18828c2ecf20Sopenharmony_ci vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, bpp); 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci if (vmw_read(vmw_priv, SVGA_REG_DEPTH) != depth) { 18858c2ecf20Sopenharmony_ci DRM_ERROR("Invalid depth %u for %u bpp, host expects %u\n", 18868c2ecf20Sopenharmony_ci depth, bpp, vmw_read(vmw_priv, SVGA_REG_DEPTH)); 18878c2ecf20Sopenharmony_ci return -EINVAL; 18888c2ecf20Sopenharmony_ci } 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci return 0; 18918c2ecf20Sopenharmony_ci} 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_cibool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, 18948c2ecf20Sopenharmony_ci uint32_t pitch, 18958c2ecf20Sopenharmony_ci uint32_t height) 18968c2ecf20Sopenharmony_ci{ 18978c2ecf20Sopenharmony_ci return ((u64) pitch * (u64) height) < (u64) 18988c2ecf20Sopenharmony_ci ((dev_priv->active_display_unit == vmw_du_screen_target) ? 18998c2ecf20Sopenharmony_ci dev_priv->prim_bb_mem : dev_priv->vram_size); 19008c2ecf20Sopenharmony_ci} 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci/** 19048c2ecf20Sopenharmony_ci * Function called by DRM code called with vbl_lock held. 19058c2ecf20Sopenharmony_ci */ 19068c2ecf20Sopenharmony_ciu32 vmw_get_vblank_counter(struct drm_crtc *crtc) 19078c2ecf20Sopenharmony_ci{ 19088c2ecf20Sopenharmony_ci return 0; 19098c2ecf20Sopenharmony_ci} 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci/** 19128c2ecf20Sopenharmony_ci * Function called by DRM code called with vbl_lock held. 19138c2ecf20Sopenharmony_ci */ 19148c2ecf20Sopenharmony_ciint vmw_enable_vblank(struct drm_crtc *crtc) 19158c2ecf20Sopenharmony_ci{ 19168c2ecf20Sopenharmony_ci return -EINVAL; 19178c2ecf20Sopenharmony_ci} 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci/** 19208c2ecf20Sopenharmony_ci * Function called by DRM code called with vbl_lock held. 19218c2ecf20Sopenharmony_ci */ 19228c2ecf20Sopenharmony_civoid vmw_disable_vblank(struct drm_crtc *crtc) 19238c2ecf20Sopenharmony_ci{ 19248c2ecf20Sopenharmony_ci} 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci/** 19278c2ecf20Sopenharmony_ci * vmw_du_update_layout - Update the display unit with topology from resolution 19288c2ecf20Sopenharmony_ci * plugin and generate DRM uevent 19298c2ecf20Sopenharmony_ci * @dev_priv: device private 19308c2ecf20Sopenharmony_ci * @num_rects: number of drm_rect in rects 19318c2ecf20Sopenharmony_ci * @rects: toplogy to update 19328c2ecf20Sopenharmony_ci */ 19338c2ecf20Sopenharmony_cistatic int vmw_du_update_layout(struct vmw_private *dev_priv, 19348c2ecf20Sopenharmony_ci unsigned int num_rects, struct drm_rect *rects) 19358c2ecf20Sopenharmony_ci{ 19368c2ecf20Sopenharmony_ci struct drm_device *dev = dev_priv->dev; 19378c2ecf20Sopenharmony_ci struct vmw_display_unit *du; 19388c2ecf20Sopenharmony_ci struct drm_connector *con; 19398c2ecf20Sopenharmony_ci struct drm_connector_list_iter conn_iter; 19408c2ecf20Sopenharmony_ci struct drm_modeset_acquire_ctx ctx; 19418c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 19428c2ecf20Sopenharmony_ci int ret; 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci /* Currently gui_x/y is protected with the crtc mutex */ 19458c2ecf20Sopenharmony_ci mutex_lock(&dev->mode_config.mutex); 19468c2ecf20Sopenharmony_ci drm_modeset_acquire_init(&ctx, 0); 19478c2ecf20Sopenharmony_ciretry: 19488c2ecf20Sopenharmony_ci drm_for_each_crtc(crtc, dev) { 19498c2ecf20Sopenharmony_ci ret = drm_modeset_lock(&crtc->mutex, &ctx); 19508c2ecf20Sopenharmony_ci if (ret < 0) { 19518c2ecf20Sopenharmony_ci if (ret == -EDEADLK) { 19528c2ecf20Sopenharmony_ci drm_modeset_backoff(&ctx); 19538c2ecf20Sopenharmony_ci goto retry; 19548c2ecf20Sopenharmony_ci } 19558c2ecf20Sopenharmony_ci goto out_fini; 19568c2ecf20Sopenharmony_ci } 19578c2ecf20Sopenharmony_ci } 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 19608c2ecf20Sopenharmony_ci drm_for_each_connector_iter(con, &conn_iter) { 19618c2ecf20Sopenharmony_ci du = vmw_connector_to_du(con); 19628c2ecf20Sopenharmony_ci if (num_rects > du->unit) { 19638c2ecf20Sopenharmony_ci du->pref_width = drm_rect_width(&rects[du->unit]); 19648c2ecf20Sopenharmony_ci du->pref_height = drm_rect_height(&rects[du->unit]); 19658c2ecf20Sopenharmony_ci du->pref_active = true; 19668c2ecf20Sopenharmony_ci du->gui_x = rects[du->unit].x1; 19678c2ecf20Sopenharmony_ci du->gui_y = rects[du->unit].y1; 19688c2ecf20Sopenharmony_ci } else { 19698c2ecf20Sopenharmony_ci du->pref_width = 800; 19708c2ecf20Sopenharmony_ci du->pref_height = 600; 19718c2ecf20Sopenharmony_ci du->pref_active = false; 19728c2ecf20Sopenharmony_ci du->gui_x = 0; 19738c2ecf20Sopenharmony_ci du->gui_y = 0; 19748c2ecf20Sopenharmony_ci } 19758c2ecf20Sopenharmony_ci } 19768c2ecf20Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci list_for_each_entry(con, &dev->mode_config.connector_list, head) { 19798c2ecf20Sopenharmony_ci du = vmw_connector_to_du(con); 19808c2ecf20Sopenharmony_ci if (num_rects > du->unit) { 19818c2ecf20Sopenharmony_ci drm_object_property_set_value 19828c2ecf20Sopenharmony_ci (&con->base, dev->mode_config.suggested_x_property, 19838c2ecf20Sopenharmony_ci du->gui_x); 19848c2ecf20Sopenharmony_ci drm_object_property_set_value 19858c2ecf20Sopenharmony_ci (&con->base, dev->mode_config.suggested_y_property, 19868c2ecf20Sopenharmony_ci du->gui_y); 19878c2ecf20Sopenharmony_ci } else { 19888c2ecf20Sopenharmony_ci drm_object_property_set_value 19898c2ecf20Sopenharmony_ci (&con->base, dev->mode_config.suggested_x_property, 19908c2ecf20Sopenharmony_ci 0); 19918c2ecf20Sopenharmony_ci drm_object_property_set_value 19928c2ecf20Sopenharmony_ci (&con->base, dev->mode_config.suggested_y_property, 19938c2ecf20Sopenharmony_ci 0); 19948c2ecf20Sopenharmony_ci } 19958c2ecf20Sopenharmony_ci con->status = vmw_du_connector_detect(con, true); 19968c2ecf20Sopenharmony_ci } 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci drm_sysfs_hotplug_event(dev); 19998c2ecf20Sopenharmony_ciout_fini: 20008c2ecf20Sopenharmony_ci drm_modeset_drop_locks(&ctx); 20018c2ecf20Sopenharmony_ci drm_modeset_acquire_fini(&ctx); 20028c2ecf20Sopenharmony_ci mutex_unlock(&dev->mode_config.mutex); 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci return 0; 20058c2ecf20Sopenharmony_ci} 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ciint vmw_du_crtc_gamma_set(struct drm_crtc *crtc, 20088c2ecf20Sopenharmony_ci u16 *r, u16 *g, u16 *b, 20098c2ecf20Sopenharmony_ci uint32_t size, 20108c2ecf20Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx) 20118c2ecf20Sopenharmony_ci{ 20128c2ecf20Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(crtc->dev); 20138c2ecf20Sopenharmony_ci int i; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 20168c2ecf20Sopenharmony_ci DRM_DEBUG("%d r/g/b = 0x%04x / 0x%04x / 0x%04x\n", i, 20178c2ecf20Sopenharmony_ci r[i], g[i], b[i]); 20188c2ecf20Sopenharmony_ci vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 0, r[i] >> 8); 20198c2ecf20Sopenharmony_ci vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 1, g[i] >> 8); 20208c2ecf20Sopenharmony_ci vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 2, b[i] >> 8); 20218c2ecf20Sopenharmony_ci } 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci return 0; 20248c2ecf20Sopenharmony_ci} 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ciint vmw_du_connector_dpms(struct drm_connector *connector, int mode) 20278c2ecf20Sopenharmony_ci{ 20288c2ecf20Sopenharmony_ci return 0; 20298c2ecf20Sopenharmony_ci} 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_cienum drm_connector_status 20328c2ecf20Sopenharmony_civmw_du_connector_detect(struct drm_connector *connector, bool force) 20338c2ecf20Sopenharmony_ci{ 20348c2ecf20Sopenharmony_ci uint32_t num_displays; 20358c2ecf20Sopenharmony_ci struct drm_device *dev = connector->dev; 20368c2ecf20Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 20378c2ecf20Sopenharmony_ci struct vmw_display_unit *du = vmw_connector_to_du(connector); 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci num_displays = vmw_read(dev_priv, SVGA_REG_NUM_DISPLAYS); 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci return ((vmw_connector_to_du(connector)->unit < num_displays && 20428c2ecf20Sopenharmony_ci du->pref_active) ? 20438c2ecf20Sopenharmony_ci connector_status_connected : connector_status_disconnected); 20448c2ecf20Sopenharmony_ci} 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_cistatic struct drm_display_mode vmw_kms_connector_builtin[] = { 20478c2ecf20Sopenharmony_ci /* 640x480@60Hz */ 20488c2ecf20Sopenharmony_ci { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, 20498c2ecf20Sopenharmony_ci 752, 800, 0, 480, 489, 492, 525, 0, 20508c2ecf20Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 20518c2ecf20Sopenharmony_ci /* 800x600@60Hz */ 20528c2ecf20Sopenharmony_ci { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, 20538c2ecf20Sopenharmony_ci 968, 1056, 0, 600, 601, 605, 628, 0, 20548c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 20558c2ecf20Sopenharmony_ci /* 1024x768@60Hz */ 20568c2ecf20Sopenharmony_ci { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, 20578c2ecf20Sopenharmony_ci 1184, 1344, 0, 768, 771, 777, 806, 0, 20588c2ecf20Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 20598c2ecf20Sopenharmony_ci /* 1152x864@75Hz */ 20608c2ecf20Sopenharmony_ci { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, 20618c2ecf20Sopenharmony_ci 1344, 1600, 0, 864, 865, 868, 900, 0, 20628c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 20638c2ecf20Sopenharmony_ci /* 1280x768@60Hz */ 20648c2ecf20Sopenharmony_ci { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344, 20658c2ecf20Sopenharmony_ci 1472, 1664, 0, 768, 771, 778, 798, 0, 20668c2ecf20Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 20678c2ecf20Sopenharmony_ci /* 1280x800@60Hz */ 20688c2ecf20Sopenharmony_ci { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352, 20698c2ecf20Sopenharmony_ci 1480, 1680, 0, 800, 803, 809, 831, 0, 20708c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 20718c2ecf20Sopenharmony_ci /* 1280x960@60Hz */ 20728c2ecf20Sopenharmony_ci { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376, 20738c2ecf20Sopenharmony_ci 1488, 1800, 0, 960, 961, 964, 1000, 0, 20748c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 20758c2ecf20Sopenharmony_ci /* 1280x1024@60Hz */ 20768c2ecf20Sopenharmony_ci { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328, 20778c2ecf20Sopenharmony_ci 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, 20788c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 20798c2ecf20Sopenharmony_ci /* 1360x768@60Hz */ 20808c2ecf20Sopenharmony_ci { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424, 20818c2ecf20Sopenharmony_ci 1536, 1792, 0, 768, 771, 777, 795, 0, 20828c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 20838c2ecf20Sopenharmony_ci /* 1440x1050@60Hz */ 20848c2ecf20Sopenharmony_ci { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488, 20858c2ecf20Sopenharmony_ci 1632, 1864, 0, 1050, 1053, 1057, 1089, 0, 20868c2ecf20Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 20878c2ecf20Sopenharmony_ci /* 1440x900@60Hz */ 20888c2ecf20Sopenharmony_ci { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520, 20898c2ecf20Sopenharmony_ci 1672, 1904, 0, 900, 903, 909, 934, 0, 20908c2ecf20Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 20918c2ecf20Sopenharmony_ci /* 1600x1200@60Hz */ 20928c2ecf20Sopenharmony_ci { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664, 20938c2ecf20Sopenharmony_ci 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 20948c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 20958c2ecf20Sopenharmony_ci /* 1680x1050@60Hz */ 20968c2ecf20Sopenharmony_ci { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784, 20978c2ecf20Sopenharmony_ci 1960, 2240, 0, 1050, 1053, 1059, 1089, 0, 20988c2ecf20Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 20998c2ecf20Sopenharmony_ci /* 1792x1344@60Hz */ 21008c2ecf20Sopenharmony_ci { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920, 21018c2ecf20Sopenharmony_ci 2120, 2448, 0, 1344, 1345, 1348, 1394, 0, 21028c2ecf20Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 21038c2ecf20Sopenharmony_ci /* 1853x1392@60Hz */ 21048c2ecf20Sopenharmony_ci { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952, 21058c2ecf20Sopenharmony_ci 2176, 2528, 0, 1392, 1393, 1396, 1439, 0, 21068c2ecf20Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 21078c2ecf20Sopenharmony_ci /* 1920x1200@60Hz */ 21088c2ecf20Sopenharmony_ci { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056, 21098c2ecf20Sopenharmony_ci 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, 21108c2ecf20Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 21118c2ecf20Sopenharmony_ci /* 1920x1440@60Hz */ 21128c2ecf20Sopenharmony_ci { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048, 21138c2ecf20Sopenharmony_ci 2256, 2600, 0, 1440, 1441, 1444, 1500, 0, 21148c2ecf20Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 21158c2ecf20Sopenharmony_ci /* 2560x1600@60Hz */ 21168c2ecf20Sopenharmony_ci { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752, 21178c2ecf20Sopenharmony_ci 3032, 3504, 0, 1600, 1603, 1609, 1658, 0, 21188c2ecf20Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 21198c2ecf20Sopenharmony_ci /* Terminate */ 21208c2ecf20Sopenharmony_ci { DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) }, 21218c2ecf20Sopenharmony_ci}; 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci/** 21248c2ecf20Sopenharmony_ci * vmw_guess_mode_timing - Provide fake timings for a 21258c2ecf20Sopenharmony_ci * 60Hz vrefresh mode. 21268c2ecf20Sopenharmony_ci * 21278c2ecf20Sopenharmony_ci * @mode - Pointer to a struct drm_display_mode with hdisplay and vdisplay 21288c2ecf20Sopenharmony_ci * members filled in. 21298c2ecf20Sopenharmony_ci */ 21308c2ecf20Sopenharmony_civoid vmw_guess_mode_timing(struct drm_display_mode *mode) 21318c2ecf20Sopenharmony_ci{ 21328c2ecf20Sopenharmony_ci mode->hsync_start = mode->hdisplay + 50; 21338c2ecf20Sopenharmony_ci mode->hsync_end = mode->hsync_start + 50; 21348c2ecf20Sopenharmony_ci mode->htotal = mode->hsync_end + 50; 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci mode->vsync_start = mode->vdisplay + 50; 21378c2ecf20Sopenharmony_ci mode->vsync_end = mode->vsync_start + 50; 21388c2ecf20Sopenharmony_ci mode->vtotal = mode->vsync_end + 50; 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_ci mode->clock = (u32)mode->htotal * (u32)mode->vtotal / 100 * 6; 21418c2ecf20Sopenharmony_ci} 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ciint vmw_du_connector_fill_modes(struct drm_connector *connector, 21458c2ecf20Sopenharmony_ci uint32_t max_width, uint32_t max_height) 21468c2ecf20Sopenharmony_ci{ 21478c2ecf20Sopenharmony_ci struct vmw_display_unit *du = vmw_connector_to_du(connector); 21488c2ecf20Sopenharmony_ci struct drm_device *dev = connector->dev; 21498c2ecf20Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 21508c2ecf20Sopenharmony_ci struct drm_display_mode *mode = NULL; 21518c2ecf20Sopenharmony_ci struct drm_display_mode *bmode; 21528c2ecf20Sopenharmony_ci struct drm_display_mode prefmode = { DRM_MODE("preferred", 21538c2ecf20Sopenharmony_ci DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 21548c2ecf20Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21558c2ecf20Sopenharmony_ci DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) 21568c2ecf20Sopenharmony_ci }; 21578c2ecf20Sopenharmony_ci int i; 21588c2ecf20Sopenharmony_ci u32 assumed_bpp = 4; 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci if (dev_priv->assume_16bpp) 21618c2ecf20Sopenharmony_ci assumed_bpp = 2; 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci max_width = min(max_width, dev_priv->texture_max_width); 21648c2ecf20Sopenharmony_ci max_height = min(max_height, dev_priv->texture_max_height); 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci /* 21678c2ecf20Sopenharmony_ci * For STDU extra limit for a mode on SVGA_REG_SCREENTARGET_MAX_WIDTH/ 21688c2ecf20Sopenharmony_ci * HEIGHT registers. 21698c2ecf20Sopenharmony_ci */ 21708c2ecf20Sopenharmony_ci if (dev_priv->active_display_unit == vmw_du_screen_target) { 21718c2ecf20Sopenharmony_ci max_width = min(max_width, dev_priv->stdu_max_width); 21728c2ecf20Sopenharmony_ci max_height = min(max_height, dev_priv->stdu_max_height); 21738c2ecf20Sopenharmony_ci } 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci /* Add preferred mode */ 21768c2ecf20Sopenharmony_ci mode = drm_mode_duplicate(dev, &prefmode); 21778c2ecf20Sopenharmony_ci if (!mode) 21788c2ecf20Sopenharmony_ci return 0; 21798c2ecf20Sopenharmony_ci mode->hdisplay = du->pref_width; 21808c2ecf20Sopenharmony_ci mode->vdisplay = du->pref_height; 21818c2ecf20Sopenharmony_ci vmw_guess_mode_timing(mode); 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci if (vmw_kms_validate_mode_vram(dev_priv, 21848c2ecf20Sopenharmony_ci mode->hdisplay * assumed_bpp, 21858c2ecf20Sopenharmony_ci mode->vdisplay)) { 21868c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, mode); 21878c2ecf20Sopenharmony_ci } else { 21888c2ecf20Sopenharmony_ci drm_mode_destroy(dev, mode); 21898c2ecf20Sopenharmony_ci mode = NULL; 21908c2ecf20Sopenharmony_ci } 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci if (du->pref_mode) { 21938c2ecf20Sopenharmony_ci list_del_init(&du->pref_mode->head); 21948c2ecf20Sopenharmony_ci drm_mode_destroy(dev, du->pref_mode); 21958c2ecf20Sopenharmony_ci } 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci /* mode might be null here, this is intended */ 21988c2ecf20Sopenharmony_ci du->pref_mode = mode; 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci for (i = 0; vmw_kms_connector_builtin[i].type != 0; i++) { 22018c2ecf20Sopenharmony_ci bmode = &vmw_kms_connector_builtin[i]; 22028c2ecf20Sopenharmony_ci if (bmode->hdisplay > max_width || 22038c2ecf20Sopenharmony_ci bmode->vdisplay > max_height) 22048c2ecf20Sopenharmony_ci continue; 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci if (!vmw_kms_validate_mode_vram(dev_priv, 22078c2ecf20Sopenharmony_ci bmode->hdisplay * assumed_bpp, 22088c2ecf20Sopenharmony_ci bmode->vdisplay)) 22098c2ecf20Sopenharmony_ci continue; 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_ci mode = drm_mode_duplicate(dev, bmode); 22128c2ecf20Sopenharmony_ci if (!mode) 22138c2ecf20Sopenharmony_ci return 0; 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, mode); 22168c2ecf20Sopenharmony_ci } 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_ci drm_connector_list_update(connector); 22198c2ecf20Sopenharmony_ci /* Move the prefered mode first, help apps pick the right mode. */ 22208c2ecf20Sopenharmony_ci drm_mode_sort(&connector->modes); 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci return 1; 22238c2ecf20Sopenharmony_ci} 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci/** 22268c2ecf20Sopenharmony_ci * vmw_kms_update_layout_ioctl - Handler for DRM_VMW_UPDATE_LAYOUT ioctl 22278c2ecf20Sopenharmony_ci * @dev: drm device for the ioctl 22288c2ecf20Sopenharmony_ci * @data: data pointer for the ioctl 22298c2ecf20Sopenharmony_ci * @file_priv: drm file for the ioctl call 22308c2ecf20Sopenharmony_ci * 22318c2ecf20Sopenharmony_ci * Update preferred topology of display unit as per ioctl request. The topology 22328c2ecf20Sopenharmony_ci * is expressed as array of drm_vmw_rect. 22338c2ecf20Sopenharmony_ci * e.g. 22348c2ecf20Sopenharmony_ci * [0 0 640 480] [640 0 800 600] [0 480 640 480] 22358c2ecf20Sopenharmony_ci * 22368c2ecf20Sopenharmony_ci * NOTE: 22378c2ecf20Sopenharmony_ci * The x and y offset (upper left) in drm_vmw_rect cannot be less than 0. Beside 22388c2ecf20Sopenharmony_ci * device limit on topology, x + w and y + h (lower right) cannot be greater 22398c2ecf20Sopenharmony_ci * than INT_MAX. So topology beyond these limits will return with error. 22408c2ecf20Sopenharmony_ci * 22418c2ecf20Sopenharmony_ci * Returns: 22428c2ecf20Sopenharmony_ci * Zero on success, negative errno on failure. 22438c2ecf20Sopenharmony_ci */ 22448c2ecf20Sopenharmony_ciint vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, 22458c2ecf20Sopenharmony_ci struct drm_file *file_priv) 22468c2ecf20Sopenharmony_ci{ 22478c2ecf20Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 22488c2ecf20Sopenharmony_ci struct drm_mode_config *mode_config = &dev->mode_config; 22498c2ecf20Sopenharmony_ci struct drm_vmw_update_layout_arg *arg = 22508c2ecf20Sopenharmony_ci (struct drm_vmw_update_layout_arg *)data; 22518c2ecf20Sopenharmony_ci void __user *user_rects; 22528c2ecf20Sopenharmony_ci struct drm_vmw_rect *rects; 22538c2ecf20Sopenharmony_ci struct drm_rect *drm_rects; 22548c2ecf20Sopenharmony_ci unsigned rects_size; 22558c2ecf20Sopenharmony_ci int ret, i; 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_ci if (!arg->num_outputs) { 22588c2ecf20Sopenharmony_ci struct drm_rect def_rect = {0, 0, 800, 600}; 22598c2ecf20Sopenharmony_ci VMW_DEBUG_KMS("Default layout x1 = %d y1 = %d x2 = %d y2 = %d\n", 22608c2ecf20Sopenharmony_ci def_rect.x1, def_rect.y1, 22618c2ecf20Sopenharmony_ci def_rect.x2, def_rect.y2); 22628c2ecf20Sopenharmony_ci vmw_du_update_layout(dev_priv, 1, &def_rect); 22638c2ecf20Sopenharmony_ci return 0; 22648c2ecf20Sopenharmony_ci } 22658c2ecf20Sopenharmony_ci 22668c2ecf20Sopenharmony_ci rects_size = arg->num_outputs * sizeof(struct drm_vmw_rect); 22678c2ecf20Sopenharmony_ci rects = kcalloc(arg->num_outputs, sizeof(struct drm_vmw_rect), 22688c2ecf20Sopenharmony_ci GFP_KERNEL); 22698c2ecf20Sopenharmony_ci if (unlikely(!rects)) 22708c2ecf20Sopenharmony_ci return -ENOMEM; 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_ci user_rects = (void __user *)(unsigned long)arg->rects; 22738c2ecf20Sopenharmony_ci ret = copy_from_user(rects, user_rects, rects_size); 22748c2ecf20Sopenharmony_ci if (unlikely(ret != 0)) { 22758c2ecf20Sopenharmony_ci DRM_ERROR("Failed to get rects.\n"); 22768c2ecf20Sopenharmony_ci ret = -EFAULT; 22778c2ecf20Sopenharmony_ci goto out_free; 22788c2ecf20Sopenharmony_ci } 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_ci drm_rects = (struct drm_rect *)rects; 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci VMW_DEBUG_KMS("Layout count = %u\n", arg->num_outputs); 22838c2ecf20Sopenharmony_ci for (i = 0; i < arg->num_outputs; i++) { 22848c2ecf20Sopenharmony_ci struct drm_vmw_rect curr_rect; 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci /* Verify user-space for overflow as kernel use drm_rect */ 22878c2ecf20Sopenharmony_ci if ((rects[i].x + rects[i].w > INT_MAX) || 22888c2ecf20Sopenharmony_ci (rects[i].y + rects[i].h > INT_MAX)) { 22898c2ecf20Sopenharmony_ci ret = -ERANGE; 22908c2ecf20Sopenharmony_ci goto out_free; 22918c2ecf20Sopenharmony_ci } 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci curr_rect = rects[i]; 22948c2ecf20Sopenharmony_ci drm_rects[i].x1 = curr_rect.x; 22958c2ecf20Sopenharmony_ci drm_rects[i].y1 = curr_rect.y; 22968c2ecf20Sopenharmony_ci drm_rects[i].x2 = curr_rect.x + curr_rect.w; 22978c2ecf20Sopenharmony_ci drm_rects[i].y2 = curr_rect.y + curr_rect.h; 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_ci VMW_DEBUG_KMS(" x1 = %d y1 = %d x2 = %d y2 = %d\n", 23008c2ecf20Sopenharmony_ci drm_rects[i].x1, drm_rects[i].y1, 23018c2ecf20Sopenharmony_ci drm_rects[i].x2, drm_rects[i].y2); 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ci /* 23048c2ecf20Sopenharmony_ci * Currently this check is limiting the topology within 23058c2ecf20Sopenharmony_ci * mode_config->max (which actually is max texture size 23068c2ecf20Sopenharmony_ci * supported by virtual device). This limit is here to address 23078c2ecf20Sopenharmony_ci * window managers that create a big framebuffer for whole 23088c2ecf20Sopenharmony_ci * topology. 23098c2ecf20Sopenharmony_ci */ 23108c2ecf20Sopenharmony_ci if (drm_rects[i].x1 < 0 || drm_rects[i].y1 < 0 || 23118c2ecf20Sopenharmony_ci drm_rects[i].x2 > mode_config->max_width || 23128c2ecf20Sopenharmony_ci drm_rects[i].y2 > mode_config->max_height) { 23138c2ecf20Sopenharmony_ci VMW_DEBUG_KMS("Invalid layout %d %d %d %d\n", 23148c2ecf20Sopenharmony_ci drm_rects[i].x1, drm_rects[i].y1, 23158c2ecf20Sopenharmony_ci drm_rects[i].x2, drm_rects[i].y2); 23168c2ecf20Sopenharmony_ci ret = -EINVAL; 23178c2ecf20Sopenharmony_ci goto out_free; 23188c2ecf20Sopenharmony_ci } 23198c2ecf20Sopenharmony_ci } 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ci ret = vmw_kms_check_display_memory(dev, arg->num_outputs, drm_rects); 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci if (ret == 0) 23248c2ecf20Sopenharmony_ci vmw_du_update_layout(dev_priv, arg->num_outputs, drm_rects); 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_ciout_free: 23278c2ecf20Sopenharmony_ci kfree(rects); 23288c2ecf20Sopenharmony_ci return ret; 23298c2ecf20Sopenharmony_ci} 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci/** 23328c2ecf20Sopenharmony_ci * vmw_kms_helper_dirty - Helper to build commands and perform actions based 23338c2ecf20Sopenharmony_ci * on a set of cliprects and a set of display units. 23348c2ecf20Sopenharmony_ci * 23358c2ecf20Sopenharmony_ci * @dev_priv: Pointer to a device private structure. 23368c2ecf20Sopenharmony_ci * @framebuffer: Pointer to the framebuffer on which to perform the actions. 23378c2ecf20Sopenharmony_ci * @clips: A set of struct drm_clip_rect. Either this os @vclips must be NULL. 23388c2ecf20Sopenharmony_ci * Cliprects are given in framebuffer coordinates. 23398c2ecf20Sopenharmony_ci * @vclips: A set of struct drm_vmw_rect cliprects. Either this or @clips must 23408c2ecf20Sopenharmony_ci * be NULL. Cliprects are given in source coordinates. 23418c2ecf20Sopenharmony_ci * @dest_x: X coordinate offset for the crtc / destination clip rects. 23428c2ecf20Sopenharmony_ci * @dest_y: Y coordinate offset for the crtc / destination clip rects. 23438c2ecf20Sopenharmony_ci * @num_clips: Number of cliprects in the @clips or @vclips array. 23448c2ecf20Sopenharmony_ci * @increment: Integer with which to increment the clip counter when looping. 23458c2ecf20Sopenharmony_ci * Used to skip a predetermined number of clip rects. 23468c2ecf20Sopenharmony_ci * @dirty: Closure structure. See the description of struct vmw_kms_dirty. 23478c2ecf20Sopenharmony_ci */ 23488c2ecf20Sopenharmony_ciint vmw_kms_helper_dirty(struct vmw_private *dev_priv, 23498c2ecf20Sopenharmony_ci struct vmw_framebuffer *framebuffer, 23508c2ecf20Sopenharmony_ci const struct drm_clip_rect *clips, 23518c2ecf20Sopenharmony_ci const struct drm_vmw_rect *vclips, 23528c2ecf20Sopenharmony_ci s32 dest_x, s32 dest_y, 23538c2ecf20Sopenharmony_ci int num_clips, 23548c2ecf20Sopenharmony_ci int increment, 23558c2ecf20Sopenharmony_ci struct vmw_kms_dirty *dirty) 23568c2ecf20Sopenharmony_ci{ 23578c2ecf20Sopenharmony_ci struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; 23588c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 23598c2ecf20Sopenharmony_ci u32 num_units = 0; 23608c2ecf20Sopenharmony_ci u32 i, k; 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci dirty->dev_priv = dev_priv; 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci /* If crtc is passed, no need to iterate over other display units */ 23658c2ecf20Sopenharmony_ci if (dirty->crtc) { 23668c2ecf20Sopenharmony_ci units[num_units++] = vmw_crtc_to_du(dirty->crtc); 23678c2ecf20Sopenharmony_ci } else { 23688c2ecf20Sopenharmony_ci list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, 23698c2ecf20Sopenharmony_ci head) { 23708c2ecf20Sopenharmony_ci struct drm_plane *plane = crtc->primary; 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci if (plane->state->fb == &framebuffer->base) 23738c2ecf20Sopenharmony_ci units[num_units++] = vmw_crtc_to_du(crtc); 23748c2ecf20Sopenharmony_ci } 23758c2ecf20Sopenharmony_ci } 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ci for (k = 0; k < num_units; k++) { 23788c2ecf20Sopenharmony_ci struct vmw_display_unit *unit = units[k]; 23798c2ecf20Sopenharmony_ci s32 crtc_x = unit->crtc.x; 23808c2ecf20Sopenharmony_ci s32 crtc_y = unit->crtc.y; 23818c2ecf20Sopenharmony_ci s32 crtc_width = unit->crtc.mode.hdisplay; 23828c2ecf20Sopenharmony_ci s32 crtc_height = unit->crtc.mode.vdisplay; 23838c2ecf20Sopenharmony_ci const struct drm_clip_rect *clips_ptr = clips; 23848c2ecf20Sopenharmony_ci const struct drm_vmw_rect *vclips_ptr = vclips; 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci dirty->unit = unit; 23878c2ecf20Sopenharmony_ci if (dirty->fifo_reserve_size > 0) { 23888c2ecf20Sopenharmony_ci dirty->cmd = VMW_FIFO_RESERVE(dev_priv, 23898c2ecf20Sopenharmony_ci dirty->fifo_reserve_size); 23908c2ecf20Sopenharmony_ci if (!dirty->cmd) 23918c2ecf20Sopenharmony_ci return -ENOMEM; 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ci memset(dirty->cmd, 0, dirty->fifo_reserve_size); 23948c2ecf20Sopenharmony_ci } 23958c2ecf20Sopenharmony_ci dirty->num_hits = 0; 23968c2ecf20Sopenharmony_ci for (i = 0; i < num_clips; i++, clips_ptr += increment, 23978c2ecf20Sopenharmony_ci vclips_ptr += increment) { 23988c2ecf20Sopenharmony_ci s32 clip_left; 23998c2ecf20Sopenharmony_ci s32 clip_top; 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci /* 24028c2ecf20Sopenharmony_ci * Select clip array type. Note that integer type 24038c2ecf20Sopenharmony_ci * in @clips is unsigned short, whereas in @vclips 24048c2ecf20Sopenharmony_ci * it's 32-bit. 24058c2ecf20Sopenharmony_ci */ 24068c2ecf20Sopenharmony_ci if (clips) { 24078c2ecf20Sopenharmony_ci dirty->fb_x = (s32) clips_ptr->x1; 24088c2ecf20Sopenharmony_ci dirty->fb_y = (s32) clips_ptr->y1; 24098c2ecf20Sopenharmony_ci dirty->unit_x2 = (s32) clips_ptr->x2 + dest_x - 24108c2ecf20Sopenharmony_ci crtc_x; 24118c2ecf20Sopenharmony_ci dirty->unit_y2 = (s32) clips_ptr->y2 + dest_y - 24128c2ecf20Sopenharmony_ci crtc_y; 24138c2ecf20Sopenharmony_ci } else { 24148c2ecf20Sopenharmony_ci dirty->fb_x = vclips_ptr->x; 24158c2ecf20Sopenharmony_ci dirty->fb_y = vclips_ptr->y; 24168c2ecf20Sopenharmony_ci dirty->unit_x2 = dirty->fb_x + vclips_ptr->w + 24178c2ecf20Sopenharmony_ci dest_x - crtc_x; 24188c2ecf20Sopenharmony_ci dirty->unit_y2 = dirty->fb_y + vclips_ptr->h + 24198c2ecf20Sopenharmony_ci dest_y - crtc_y; 24208c2ecf20Sopenharmony_ci } 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci dirty->unit_x1 = dirty->fb_x + dest_x - crtc_x; 24238c2ecf20Sopenharmony_ci dirty->unit_y1 = dirty->fb_y + dest_y - crtc_y; 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_ci /* Skip this clip if it's outside the crtc region */ 24268c2ecf20Sopenharmony_ci if (dirty->unit_x1 >= crtc_width || 24278c2ecf20Sopenharmony_ci dirty->unit_y1 >= crtc_height || 24288c2ecf20Sopenharmony_ci dirty->unit_x2 <= 0 || dirty->unit_y2 <= 0) 24298c2ecf20Sopenharmony_ci continue; 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_ci /* Clip right and bottom to crtc limits */ 24328c2ecf20Sopenharmony_ci dirty->unit_x2 = min_t(s32, dirty->unit_x2, 24338c2ecf20Sopenharmony_ci crtc_width); 24348c2ecf20Sopenharmony_ci dirty->unit_y2 = min_t(s32, dirty->unit_y2, 24358c2ecf20Sopenharmony_ci crtc_height); 24368c2ecf20Sopenharmony_ci 24378c2ecf20Sopenharmony_ci /* Clip left and top to crtc limits */ 24388c2ecf20Sopenharmony_ci clip_left = min_t(s32, dirty->unit_x1, 0); 24398c2ecf20Sopenharmony_ci clip_top = min_t(s32, dirty->unit_y1, 0); 24408c2ecf20Sopenharmony_ci dirty->unit_x1 -= clip_left; 24418c2ecf20Sopenharmony_ci dirty->unit_y1 -= clip_top; 24428c2ecf20Sopenharmony_ci dirty->fb_x -= clip_left; 24438c2ecf20Sopenharmony_ci dirty->fb_y -= clip_top; 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_ci dirty->clip(dirty); 24468c2ecf20Sopenharmony_ci } 24478c2ecf20Sopenharmony_ci 24488c2ecf20Sopenharmony_ci dirty->fifo_commit(dirty); 24498c2ecf20Sopenharmony_ci } 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_ci return 0; 24528c2ecf20Sopenharmony_ci} 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_ci/** 24558c2ecf20Sopenharmony_ci * vmw_kms_helper_validation_finish - Helper for post KMS command submission 24568c2ecf20Sopenharmony_ci * cleanup and fencing 24578c2ecf20Sopenharmony_ci * @dev_priv: Pointer to the device-private struct 24588c2ecf20Sopenharmony_ci * @file_priv: Pointer identifying the client when user-space fencing is used 24598c2ecf20Sopenharmony_ci * @ctx: Pointer to the validation context 24608c2ecf20Sopenharmony_ci * @out_fence: If non-NULL, returned refcounted fence-pointer 24618c2ecf20Sopenharmony_ci * @user_fence_rep: If non-NULL, pointer to user-space address area 24628c2ecf20Sopenharmony_ci * in which to copy user-space fence info 24638c2ecf20Sopenharmony_ci */ 24648c2ecf20Sopenharmony_civoid vmw_kms_helper_validation_finish(struct vmw_private *dev_priv, 24658c2ecf20Sopenharmony_ci struct drm_file *file_priv, 24668c2ecf20Sopenharmony_ci struct vmw_validation_context *ctx, 24678c2ecf20Sopenharmony_ci struct vmw_fence_obj **out_fence, 24688c2ecf20Sopenharmony_ci struct drm_vmw_fence_rep __user * 24698c2ecf20Sopenharmony_ci user_fence_rep) 24708c2ecf20Sopenharmony_ci{ 24718c2ecf20Sopenharmony_ci struct vmw_fence_obj *fence = NULL; 24728c2ecf20Sopenharmony_ci uint32_t handle = 0; 24738c2ecf20Sopenharmony_ci int ret = 0; 24748c2ecf20Sopenharmony_ci 24758c2ecf20Sopenharmony_ci if (file_priv || user_fence_rep || vmw_validation_has_bos(ctx) || 24768c2ecf20Sopenharmony_ci out_fence) 24778c2ecf20Sopenharmony_ci ret = vmw_execbuf_fence_commands(file_priv, dev_priv, &fence, 24788c2ecf20Sopenharmony_ci file_priv ? &handle : NULL); 24798c2ecf20Sopenharmony_ci vmw_validation_done(ctx, fence); 24808c2ecf20Sopenharmony_ci if (file_priv) 24818c2ecf20Sopenharmony_ci vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), 24828c2ecf20Sopenharmony_ci ret, user_fence_rep, fence, 24838c2ecf20Sopenharmony_ci handle, -1); 24848c2ecf20Sopenharmony_ci if (out_fence) 24858c2ecf20Sopenharmony_ci *out_fence = fence; 24868c2ecf20Sopenharmony_ci else 24878c2ecf20Sopenharmony_ci vmw_fence_obj_unreference(&fence); 24888c2ecf20Sopenharmony_ci} 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ci/** 24918c2ecf20Sopenharmony_ci * vmw_kms_update_proxy - Helper function to update a proxy surface from 24928c2ecf20Sopenharmony_ci * its backing MOB. 24938c2ecf20Sopenharmony_ci * 24948c2ecf20Sopenharmony_ci * @res: Pointer to the surface resource 24958c2ecf20Sopenharmony_ci * @clips: Clip rects in framebuffer (surface) space. 24968c2ecf20Sopenharmony_ci * @num_clips: Number of clips in @clips. 24978c2ecf20Sopenharmony_ci * @increment: Integer with which to increment the clip counter when looping. 24988c2ecf20Sopenharmony_ci * Used to skip a predetermined number of clip rects. 24998c2ecf20Sopenharmony_ci * 25008c2ecf20Sopenharmony_ci * This function makes sure the proxy surface is updated from its backing MOB 25018c2ecf20Sopenharmony_ci * using the region given by @clips. The surface resource @res and its backing 25028c2ecf20Sopenharmony_ci * MOB needs to be reserved and validated on call. 25038c2ecf20Sopenharmony_ci */ 25048c2ecf20Sopenharmony_ciint vmw_kms_update_proxy(struct vmw_resource *res, 25058c2ecf20Sopenharmony_ci const struct drm_clip_rect *clips, 25068c2ecf20Sopenharmony_ci unsigned num_clips, 25078c2ecf20Sopenharmony_ci int increment) 25088c2ecf20Sopenharmony_ci{ 25098c2ecf20Sopenharmony_ci struct vmw_private *dev_priv = res->dev_priv; 25108c2ecf20Sopenharmony_ci struct drm_vmw_size *size = &vmw_res_to_srf(res)->metadata.base_size; 25118c2ecf20Sopenharmony_ci struct { 25128c2ecf20Sopenharmony_ci SVGA3dCmdHeader header; 25138c2ecf20Sopenharmony_ci SVGA3dCmdUpdateGBImage body; 25148c2ecf20Sopenharmony_ci } *cmd; 25158c2ecf20Sopenharmony_ci SVGA3dBox *box; 25168c2ecf20Sopenharmony_ci size_t copy_size = 0; 25178c2ecf20Sopenharmony_ci int i; 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci if (!clips) 25208c2ecf20Sopenharmony_ci return 0; 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_ci cmd = VMW_FIFO_RESERVE(dev_priv, sizeof(*cmd) * num_clips); 25238c2ecf20Sopenharmony_ci if (!cmd) 25248c2ecf20Sopenharmony_ci return -ENOMEM; 25258c2ecf20Sopenharmony_ci 25268c2ecf20Sopenharmony_ci for (i = 0; i < num_clips; ++i, clips += increment, ++cmd) { 25278c2ecf20Sopenharmony_ci box = &cmd->body.box; 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci cmd->header.id = SVGA_3D_CMD_UPDATE_GB_IMAGE; 25308c2ecf20Sopenharmony_ci cmd->header.size = sizeof(cmd->body); 25318c2ecf20Sopenharmony_ci cmd->body.image.sid = res->id; 25328c2ecf20Sopenharmony_ci cmd->body.image.face = 0; 25338c2ecf20Sopenharmony_ci cmd->body.image.mipmap = 0; 25348c2ecf20Sopenharmony_ci 25358c2ecf20Sopenharmony_ci if (clips->x1 > size->width || clips->x2 > size->width || 25368c2ecf20Sopenharmony_ci clips->y1 > size->height || clips->y2 > size->height) { 25378c2ecf20Sopenharmony_ci DRM_ERROR("Invalid clips outsize of framebuffer.\n"); 25388c2ecf20Sopenharmony_ci return -EINVAL; 25398c2ecf20Sopenharmony_ci } 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci box->x = clips->x1; 25428c2ecf20Sopenharmony_ci box->y = clips->y1; 25438c2ecf20Sopenharmony_ci box->z = 0; 25448c2ecf20Sopenharmony_ci box->w = clips->x2 - clips->x1; 25458c2ecf20Sopenharmony_ci box->h = clips->y2 - clips->y1; 25468c2ecf20Sopenharmony_ci box->d = 1; 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_ci copy_size += sizeof(*cmd); 25498c2ecf20Sopenharmony_ci } 25508c2ecf20Sopenharmony_ci 25518c2ecf20Sopenharmony_ci vmw_fifo_commit(dev_priv, copy_size); 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci return 0; 25548c2ecf20Sopenharmony_ci} 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ciint vmw_kms_fbdev_init_data(struct vmw_private *dev_priv, 25578c2ecf20Sopenharmony_ci unsigned unit, 25588c2ecf20Sopenharmony_ci u32 max_width, 25598c2ecf20Sopenharmony_ci u32 max_height, 25608c2ecf20Sopenharmony_ci struct drm_connector **p_con, 25618c2ecf20Sopenharmony_ci struct drm_crtc **p_crtc, 25628c2ecf20Sopenharmony_ci struct drm_display_mode **p_mode) 25638c2ecf20Sopenharmony_ci{ 25648c2ecf20Sopenharmony_ci struct drm_connector *con; 25658c2ecf20Sopenharmony_ci struct vmw_display_unit *du; 25668c2ecf20Sopenharmony_ci struct drm_display_mode *mode; 25678c2ecf20Sopenharmony_ci int i = 0; 25688c2ecf20Sopenharmony_ci int ret = 0; 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_ci mutex_lock(&dev_priv->dev->mode_config.mutex); 25718c2ecf20Sopenharmony_ci list_for_each_entry(con, &dev_priv->dev->mode_config.connector_list, 25728c2ecf20Sopenharmony_ci head) { 25738c2ecf20Sopenharmony_ci if (i == unit) 25748c2ecf20Sopenharmony_ci break; 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_ci ++i; 25778c2ecf20Sopenharmony_ci } 25788c2ecf20Sopenharmony_ci 25798c2ecf20Sopenharmony_ci if (&con->head == &dev_priv->dev->mode_config.connector_list) { 25808c2ecf20Sopenharmony_ci DRM_ERROR("Could not find initial display unit.\n"); 25818c2ecf20Sopenharmony_ci ret = -EINVAL; 25828c2ecf20Sopenharmony_ci goto out_unlock; 25838c2ecf20Sopenharmony_ci } 25848c2ecf20Sopenharmony_ci 25858c2ecf20Sopenharmony_ci if (list_empty(&con->modes)) 25868c2ecf20Sopenharmony_ci (void) vmw_du_connector_fill_modes(con, max_width, max_height); 25878c2ecf20Sopenharmony_ci 25888c2ecf20Sopenharmony_ci if (list_empty(&con->modes)) { 25898c2ecf20Sopenharmony_ci DRM_ERROR("Could not find initial display mode.\n"); 25908c2ecf20Sopenharmony_ci ret = -EINVAL; 25918c2ecf20Sopenharmony_ci goto out_unlock; 25928c2ecf20Sopenharmony_ci } 25938c2ecf20Sopenharmony_ci 25948c2ecf20Sopenharmony_ci du = vmw_connector_to_du(con); 25958c2ecf20Sopenharmony_ci *p_con = con; 25968c2ecf20Sopenharmony_ci *p_crtc = &du->crtc; 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_ci list_for_each_entry(mode, &con->modes, head) { 25998c2ecf20Sopenharmony_ci if (mode->type & DRM_MODE_TYPE_PREFERRED) 26008c2ecf20Sopenharmony_ci break; 26018c2ecf20Sopenharmony_ci } 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci if (&mode->head == &con->modes) { 26048c2ecf20Sopenharmony_ci WARN_ONCE(true, "Could not find initial preferred mode.\n"); 26058c2ecf20Sopenharmony_ci *p_mode = list_first_entry(&con->modes, 26068c2ecf20Sopenharmony_ci struct drm_display_mode, 26078c2ecf20Sopenharmony_ci head); 26088c2ecf20Sopenharmony_ci } else { 26098c2ecf20Sopenharmony_ci *p_mode = mode; 26108c2ecf20Sopenharmony_ci } 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci out_unlock: 26138c2ecf20Sopenharmony_ci mutex_unlock(&dev_priv->dev->mode_config.mutex); 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci return ret; 26168c2ecf20Sopenharmony_ci} 26178c2ecf20Sopenharmony_ci 26188c2ecf20Sopenharmony_ci/** 26198c2ecf20Sopenharmony_ci * vmw_kms_create_implicit_placement_proparty - Set up the implicit placement 26208c2ecf20Sopenharmony_ci * property. 26218c2ecf20Sopenharmony_ci * 26228c2ecf20Sopenharmony_ci * @dev_priv: Pointer to a device private struct. 26238c2ecf20Sopenharmony_ci * 26248c2ecf20Sopenharmony_ci * Sets up the implicit placement property unless it's already set up. 26258c2ecf20Sopenharmony_ci */ 26268c2ecf20Sopenharmony_civoid 26278c2ecf20Sopenharmony_civmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv) 26288c2ecf20Sopenharmony_ci{ 26298c2ecf20Sopenharmony_ci if (dev_priv->implicit_placement_property) 26308c2ecf20Sopenharmony_ci return; 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci dev_priv->implicit_placement_property = 26338c2ecf20Sopenharmony_ci drm_property_create_range(dev_priv->dev, 26348c2ecf20Sopenharmony_ci DRM_MODE_PROP_IMMUTABLE, 26358c2ecf20Sopenharmony_ci "implicit_placement", 0, 1); 26368c2ecf20Sopenharmony_ci} 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci/** 26398c2ecf20Sopenharmony_ci * vmw_kms_suspend - Save modesetting state and turn modesetting off. 26408c2ecf20Sopenharmony_ci * 26418c2ecf20Sopenharmony_ci * @dev: Pointer to the drm device 26428c2ecf20Sopenharmony_ci * Return: 0 on success. Negative error code on failure. 26438c2ecf20Sopenharmony_ci */ 26448c2ecf20Sopenharmony_ciint vmw_kms_suspend(struct drm_device *dev) 26458c2ecf20Sopenharmony_ci{ 26468c2ecf20Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 26478c2ecf20Sopenharmony_ci 26488c2ecf20Sopenharmony_ci dev_priv->suspend_state = drm_atomic_helper_suspend(dev); 26498c2ecf20Sopenharmony_ci if (IS_ERR(dev_priv->suspend_state)) { 26508c2ecf20Sopenharmony_ci int ret = PTR_ERR(dev_priv->suspend_state); 26518c2ecf20Sopenharmony_ci 26528c2ecf20Sopenharmony_ci DRM_ERROR("Failed kms suspend: %d\n", ret); 26538c2ecf20Sopenharmony_ci dev_priv->suspend_state = NULL; 26548c2ecf20Sopenharmony_ci 26558c2ecf20Sopenharmony_ci return ret; 26568c2ecf20Sopenharmony_ci } 26578c2ecf20Sopenharmony_ci 26588c2ecf20Sopenharmony_ci return 0; 26598c2ecf20Sopenharmony_ci} 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_ci/** 26638c2ecf20Sopenharmony_ci * vmw_kms_resume - Re-enable modesetting and restore state 26648c2ecf20Sopenharmony_ci * 26658c2ecf20Sopenharmony_ci * @dev: Pointer to the drm device 26668c2ecf20Sopenharmony_ci * Return: 0 on success. Negative error code on failure. 26678c2ecf20Sopenharmony_ci * 26688c2ecf20Sopenharmony_ci * State is resumed from a previous vmw_kms_suspend(). It's illegal 26698c2ecf20Sopenharmony_ci * to call this function without a previous vmw_kms_suspend(). 26708c2ecf20Sopenharmony_ci */ 26718c2ecf20Sopenharmony_ciint vmw_kms_resume(struct drm_device *dev) 26728c2ecf20Sopenharmony_ci{ 26738c2ecf20Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 26748c2ecf20Sopenharmony_ci int ret; 26758c2ecf20Sopenharmony_ci 26768c2ecf20Sopenharmony_ci if (WARN_ON(!dev_priv->suspend_state)) 26778c2ecf20Sopenharmony_ci return 0; 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci ret = drm_atomic_helper_resume(dev, dev_priv->suspend_state); 26808c2ecf20Sopenharmony_ci dev_priv->suspend_state = NULL; 26818c2ecf20Sopenharmony_ci 26828c2ecf20Sopenharmony_ci return ret; 26838c2ecf20Sopenharmony_ci} 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_ci/** 26868c2ecf20Sopenharmony_ci * vmw_kms_lost_device - Notify kms that modesetting capabilities will be lost 26878c2ecf20Sopenharmony_ci * 26888c2ecf20Sopenharmony_ci * @dev: Pointer to the drm device 26898c2ecf20Sopenharmony_ci */ 26908c2ecf20Sopenharmony_civoid vmw_kms_lost_device(struct drm_device *dev) 26918c2ecf20Sopenharmony_ci{ 26928c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(dev); 26938c2ecf20Sopenharmony_ci} 26948c2ecf20Sopenharmony_ci 26958c2ecf20Sopenharmony_ci/** 26968c2ecf20Sopenharmony_ci * vmw_du_helper_plane_update - Helper to do plane update on a display unit. 26978c2ecf20Sopenharmony_ci * @update: The closure structure. 26988c2ecf20Sopenharmony_ci * 26998c2ecf20Sopenharmony_ci * Call this helper after setting callbacks in &vmw_du_update_plane to do plane 27008c2ecf20Sopenharmony_ci * update on display unit. 27018c2ecf20Sopenharmony_ci * 27028c2ecf20Sopenharmony_ci * Return: 0 on success or a negative error code on failure. 27038c2ecf20Sopenharmony_ci */ 27048c2ecf20Sopenharmony_ciint vmw_du_helper_plane_update(struct vmw_du_update_plane *update) 27058c2ecf20Sopenharmony_ci{ 27068c2ecf20Sopenharmony_ci struct drm_plane_state *state = update->plane->state; 27078c2ecf20Sopenharmony_ci struct drm_plane_state *old_state = update->old_state; 27088c2ecf20Sopenharmony_ci struct drm_atomic_helper_damage_iter iter; 27098c2ecf20Sopenharmony_ci struct drm_rect clip; 27108c2ecf20Sopenharmony_ci struct drm_rect bb; 27118c2ecf20Sopenharmony_ci DECLARE_VAL_CONTEXT(val_ctx, NULL, 0); 27128c2ecf20Sopenharmony_ci uint32_t reserved_size = 0; 27138c2ecf20Sopenharmony_ci uint32_t submit_size = 0; 27148c2ecf20Sopenharmony_ci uint32_t curr_size = 0; 27158c2ecf20Sopenharmony_ci uint32_t num_hits = 0; 27168c2ecf20Sopenharmony_ci void *cmd_start; 27178c2ecf20Sopenharmony_ci char *cmd_next; 27188c2ecf20Sopenharmony_ci int ret; 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ci /* 27218c2ecf20Sopenharmony_ci * Iterate in advance to check if really need plane update and find the 27228c2ecf20Sopenharmony_ci * number of clips that actually are in plane src for fifo allocation. 27238c2ecf20Sopenharmony_ci */ 27248c2ecf20Sopenharmony_ci drm_atomic_helper_damage_iter_init(&iter, old_state, state); 27258c2ecf20Sopenharmony_ci drm_atomic_for_each_plane_damage(&iter, &clip) 27268c2ecf20Sopenharmony_ci num_hits++; 27278c2ecf20Sopenharmony_ci 27288c2ecf20Sopenharmony_ci if (num_hits == 0) 27298c2ecf20Sopenharmony_ci return 0; 27308c2ecf20Sopenharmony_ci 27318c2ecf20Sopenharmony_ci if (update->vfb->bo) { 27328c2ecf20Sopenharmony_ci struct vmw_framebuffer_bo *vfbbo = 27338c2ecf20Sopenharmony_ci container_of(update->vfb, typeof(*vfbbo), base); 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci ret = vmw_validation_add_bo(&val_ctx, vfbbo->buffer, false, 27368c2ecf20Sopenharmony_ci update->cpu_blit); 27378c2ecf20Sopenharmony_ci } else { 27388c2ecf20Sopenharmony_ci struct vmw_framebuffer_surface *vfbs = 27398c2ecf20Sopenharmony_ci container_of(update->vfb, typeof(*vfbs), base); 27408c2ecf20Sopenharmony_ci 27418c2ecf20Sopenharmony_ci ret = vmw_validation_add_resource(&val_ctx, &vfbs->surface->res, 27428c2ecf20Sopenharmony_ci 0, VMW_RES_DIRTY_NONE, NULL, 27438c2ecf20Sopenharmony_ci NULL); 27448c2ecf20Sopenharmony_ci } 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci if (ret) 27478c2ecf20Sopenharmony_ci return ret; 27488c2ecf20Sopenharmony_ci 27498c2ecf20Sopenharmony_ci ret = vmw_validation_prepare(&val_ctx, update->mutex, update->intr); 27508c2ecf20Sopenharmony_ci if (ret) 27518c2ecf20Sopenharmony_ci goto out_unref; 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci reserved_size = update->calc_fifo_size(update, num_hits); 27548c2ecf20Sopenharmony_ci cmd_start = VMW_FIFO_RESERVE(update->dev_priv, reserved_size); 27558c2ecf20Sopenharmony_ci if (!cmd_start) { 27568c2ecf20Sopenharmony_ci ret = -ENOMEM; 27578c2ecf20Sopenharmony_ci goto out_revert; 27588c2ecf20Sopenharmony_ci } 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_ci cmd_next = cmd_start; 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_ci if (update->post_prepare) { 27638c2ecf20Sopenharmony_ci curr_size = update->post_prepare(update, cmd_next); 27648c2ecf20Sopenharmony_ci cmd_next += curr_size; 27658c2ecf20Sopenharmony_ci submit_size += curr_size; 27668c2ecf20Sopenharmony_ci } 27678c2ecf20Sopenharmony_ci 27688c2ecf20Sopenharmony_ci if (update->pre_clip) { 27698c2ecf20Sopenharmony_ci curr_size = update->pre_clip(update, cmd_next, num_hits); 27708c2ecf20Sopenharmony_ci cmd_next += curr_size; 27718c2ecf20Sopenharmony_ci submit_size += curr_size; 27728c2ecf20Sopenharmony_ci } 27738c2ecf20Sopenharmony_ci 27748c2ecf20Sopenharmony_ci bb.x1 = INT_MAX; 27758c2ecf20Sopenharmony_ci bb.y1 = INT_MAX; 27768c2ecf20Sopenharmony_ci bb.x2 = INT_MIN; 27778c2ecf20Sopenharmony_ci bb.y2 = INT_MIN; 27788c2ecf20Sopenharmony_ci 27798c2ecf20Sopenharmony_ci drm_atomic_helper_damage_iter_init(&iter, old_state, state); 27808c2ecf20Sopenharmony_ci drm_atomic_for_each_plane_damage(&iter, &clip) { 27818c2ecf20Sopenharmony_ci uint32_t fb_x = clip.x1; 27828c2ecf20Sopenharmony_ci uint32_t fb_y = clip.y1; 27838c2ecf20Sopenharmony_ci 27848c2ecf20Sopenharmony_ci vmw_du_translate_to_crtc(state, &clip); 27858c2ecf20Sopenharmony_ci if (update->clip) { 27868c2ecf20Sopenharmony_ci curr_size = update->clip(update, cmd_next, &clip, fb_x, 27878c2ecf20Sopenharmony_ci fb_y); 27888c2ecf20Sopenharmony_ci cmd_next += curr_size; 27898c2ecf20Sopenharmony_ci submit_size += curr_size; 27908c2ecf20Sopenharmony_ci } 27918c2ecf20Sopenharmony_ci bb.x1 = min_t(int, bb.x1, clip.x1); 27928c2ecf20Sopenharmony_ci bb.y1 = min_t(int, bb.y1, clip.y1); 27938c2ecf20Sopenharmony_ci bb.x2 = max_t(int, bb.x2, clip.x2); 27948c2ecf20Sopenharmony_ci bb.y2 = max_t(int, bb.y2, clip.y2); 27958c2ecf20Sopenharmony_ci } 27968c2ecf20Sopenharmony_ci 27978c2ecf20Sopenharmony_ci curr_size = update->post_clip(update, cmd_next, &bb); 27988c2ecf20Sopenharmony_ci submit_size += curr_size; 27998c2ecf20Sopenharmony_ci 28008c2ecf20Sopenharmony_ci if (reserved_size < submit_size) 28018c2ecf20Sopenharmony_ci submit_size = 0; 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ci vmw_fifo_commit(update->dev_priv, submit_size); 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_ci vmw_kms_helper_validation_finish(update->dev_priv, NULL, &val_ctx, 28068c2ecf20Sopenharmony_ci update->out_fence, NULL); 28078c2ecf20Sopenharmony_ci return ret; 28088c2ecf20Sopenharmony_ci 28098c2ecf20Sopenharmony_ciout_revert: 28108c2ecf20Sopenharmony_ci vmw_validation_revert(&val_ctx); 28118c2ecf20Sopenharmony_ci 28128c2ecf20Sopenharmony_ciout_unref: 28138c2ecf20Sopenharmony_ci vmw_validation_unref_lists(&val_ctx); 28148c2ecf20Sopenharmony_ci return ret; 28158c2ecf20Sopenharmony_ci} 2816