18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT 28c2ecf20Sopenharmony_ci/************************************************************************** 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2009-2014 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/ttm/ttm_placement.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "device_include/svga_overlay.h" 318c2ecf20Sopenharmony_ci#include "device_include/svga_escape.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include "vmwgfx_drv.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define VMW_MAX_NUM_STREAMS 1 368c2ecf20Sopenharmony_ci#define VMW_OVERLAY_CAP_MASK (SVGA_FIFO_CAP_VIDEO | SVGA_FIFO_CAP_ESCAPE) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct vmw_stream { 398c2ecf20Sopenharmony_ci struct vmw_buffer_object *buf; 408c2ecf20Sopenharmony_ci bool claimed; 418c2ecf20Sopenharmony_ci bool paused; 428c2ecf20Sopenharmony_ci struct drm_vmw_control_stream_arg saved; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/** 468c2ecf20Sopenharmony_ci * Overlay control 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_cistruct vmw_overlay { 498c2ecf20Sopenharmony_ci /* 508c2ecf20Sopenharmony_ci * Each stream is a single overlay. In Xv these are called ports. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci struct mutex mutex; 538c2ecf20Sopenharmony_ci struct vmw_stream stream[VMW_MAX_NUM_STREAMS]; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic inline struct vmw_overlay *vmw_overlay(struct drm_device *dev) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 598c2ecf20Sopenharmony_ci return dev_priv ? dev_priv->overlay_priv : NULL; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistruct vmw_escape_header { 638c2ecf20Sopenharmony_ci uint32_t cmd; 648c2ecf20Sopenharmony_ci SVGAFifoCmdEscape body; 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistruct vmw_escape_video_flush { 688c2ecf20Sopenharmony_ci struct vmw_escape_header escape; 698c2ecf20Sopenharmony_ci SVGAEscapeVideoFlush flush; 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic inline void fill_escape(struct vmw_escape_header *header, 738c2ecf20Sopenharmony_ci uint32_t size) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci header->cmd = SVGA_CMD_ESCAPE; 768c2ecf20Sopenharmony_ci header->body.nsid = SVGA_ESCAPE_NSID_VMWARE; 778c2ecf20Sopenharmony_ci header->body.size = size; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic inline void fill_flush(struct vmw_escape_video_flush *cmd, 818c2ecf20Sopenharmony_ci uint32_t stream_id) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci fill_escape(&cmd->escape, sizeof(cmd->flush)); 848c2ecf20Sopenharmony_ci cmd->flush.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_FLUSH; 858c2ecf20Sopenharmony_ci cmd->flush.streamId = stream_id; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/** 898c2ecf20Sopenharmony_ci * Send put command to hw. 908c2ecf20Sopenharmony_ci * 918c2ecf20Sopenharmony_ci * Returns 928c2ecf20Sopenharmony_ci * -ERESTARTSYS if interrupted by a signal. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_cistatic int vmw_overlay_send_put(struct vmw_private *dev_priv, 958c2ecf20Sopenharmony_ci struct vmw_buffer_object *buf, 968c2ecf20Sopenharmony_ci struct drm_vmw_control_stream_arg *arg, 978c2ecf20Sopenharmony_ci bool interruptible) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct vmw_escape_video_flush *flush; 1008c2ecf20Sopenharmony_ci size_t fifo_size; 1018c2ecf20Sopenharmony_ci bool have_so = (dev_priv->active_display_unit == vmw_du_screen_object); 1028c2ecf20Sopenharmony_ci int i, num_items; 1038c2ecf20Sopenharmony_ci SVGAGuestPtr ptr; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci struct { 1068c2ecf20Sopenharmony_ci struct vmw_escape_header escape; 1078c2ecf20Sopenharmony_ci struct { 1088c2ecf20Sopenharmony_ci uint32_t cmdType; 1098c2ecf20Sopenharmony_ci uint32_t streamId; 1108c2ecf20Sopenharmony_ci } header; 1118c2ecf20Sopenharmony_ci } *cmds; 1128c2ecf20Sopenharmony_ci struct { 1138c2ecf20Sopenharmony_ci uint32_t registerId; 1148c2ecf20Sopenharmony_ci uint32_t value; 1158c2ecf20Sopenharmony_ci } *items; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* defines are a index needs + 1 */ 1188c2ecf20Sopenharmony_ci if (have_so) 1198c2ecf20Sopenharmony_ci num_items = SVGA_VIDEO_DST_SCREEN_ID + 1; 1208c2ecf20Sopenharmony_ci else 1218c2ecf20Sopenharmony_ci num_items = SVGA_VIDEO_PITCH_3 + 1; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci fifo_size = sizeof(*cmds) + sizeof(*flush) + sizeof(*items) * num_items; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci cmds = VMW_FIFO_RESERVE(dev_priv, fifo_size); 1268c2ecf20Sopenharmony_ci /* hardware has hung, can't do anything here */ 1278c2ecf20Sopenharmony_ci if (!cmds) 1288c2ecf20Sopenharmony_ci return -ENOMEM; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci items = (typeof(items))&cmds[1]; 1318c2ecf20Sopenharmony_ci flush = (struct vmw_escape_video_flush *)&items[num_items]; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* the size is header + number of items */ 1348c2ecf20Sopenharmony_ci fill_escape(&cmds->escape, sizeof(*items) * (num_items + 1)); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci cmds->header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS; 1378c2ecf20Sopenharmony_ci cmds->header.streamId = arg->stream_id; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* the IDs are neatly numbered */ 1408c2ecf20Sopenharmony_ci for (i = 0; i < num_items; i++) 1418c2ecf20Sopenharmony_ci items[i].registerId = i; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci vmw_bo_get_guest_ptr(&buf->base, &ptr); 1448c2ecf20Sopenharmony_ci ptr.offset += arg->offset; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci items[SVGA_VIDEO_ENABLED].value = true; 1478c2ecf20Sopenharmony_ci items[SVGA_VIDEO_FLAGS].value = arg->flags; 1488c2ecf20Sopenharmony_ci items[SVGA_VIDEO_DATA_OFFSET].value = ptr.offset; 1498c2ecf20Sopenharmony_ci items[SVGA_VIDEO_FORMAT].value = arg->format; 1508c2ecf20Sopenharmony_ci items[SVGA_VIDEO_COLORKEY].value = arg->color_key; 1518c2ecf20Sopenharmony_ci items[SVGA_VIDEO_SIZE].value = arg->size; 1528c2ecf20Sopenharmony_ci items[SVGA_VIDEO_WIDTH].value = arg->width; 1538c2ecf20Sopenharmony_ci items[SVGA_VIDEO_HEIGHT].value = arg->height; 1548c2ecf20Sopenharmony_ci items[SVGA_VIDEO_SRC_X].value = arg->src.x; 1558c2ecf20Sopenharmony_ci items[SVGA_VIDEO_SRC_Y].value = arg->src.y; 1568c2ecf20Sopenharmony_ci items[SVGA_VIDEO_SRC_WIDTH].value = arg->src.w; 1578c2ecf20Sopenharmony_ci items[SVGA_VIDEO_SRC_HEIGHT].value = arg->src.h; 1588c2ecf20Sopenharmony_ci items[SVGA_VIDEO_DST_X].value = arg->dst.x; 1598c2ecf20Sopenharmony_ci items[SVGA_VIDEO_DST_Y].value = arg->dst.y; 1608c2ecf20Sopenharmony_ci items[SVGA_VIDEO_DST_WIDTH].value = arg->dst.w; 1618c2ecf20Sopenharmony_ci items[SVGA_VIDEO_DST_HEIGHT].value = arg->dst.h; 1628c2ecf20Sopenharmony_ci items[SVGA_VIDEO_PITCH_1].value = arg->pitch[0]; 1638c2ecf20Sopenharmony_ci items[SVGA_VIDEO_PITCH_2].value = arg->pitch[1]; 1648c2ecf20Sopenharmony_ci items[SVGA_VIDEO_PITCH_3].value = arg->pitch[2]; 1658c2ecf20Sopenharmony_ci if (have_so) { 1668c2ecf20Sopenharmony_ci items[SVGA_VIDEO_DATA_GMRID].value = ptr.gmrId; 1678c2ecf20Sopenharmony_ci items[SVGA_VIDEO_DST_SCREEN_ID].value = SVGA_ID_INVALID; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci fill_flush(flush, arg->stream_id); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci vmw_fifo_commit(dev_priv, fifo_size); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/** 1788c2ecf20Sopenharmony_ci * Send stop command to hw. 1798c2ecf20Sopenharmony_ci * 1808c2ecf20Sopenharmony_ci * Returns 1818c2ecf20Sopenharmony_ci * -ERESTARTSYS if interrupted by a signal. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_cistatic int vmw_overlay_send_stop(struct vmw_private *dev_priv, 1848c2ecf20Sopenharmony_ci uint32_t stream_id, 1858c2ecf20Sopenharmony_ci bool interruptible) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct { 1888c2ecf20Sopenharmony_ci struct vmw_escape_header escape; 1898c2ecf20Sopenharmony_ci SVGAEscapeVideoSetRegs body; 1908c2ecf20Sopenharmony_ci struct vmw_escape_video_flush flush; 1918c2ecf20Sopenharmony_ci } *cmds; 1928c2ecf20Sopenharmony_ci int ret; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci for (;;) { 1958c2ecf20Sopenharmony_ci cmds = VMW_FIFO_RESERVE(dev_priv, sizeof(*cmds)); 1968c2ecf20Sopenharmony_ci if (cmds) 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci ret = vmw_fallback_wait(dev_priv, false, true, 0, 2008c2ecf20Sopenharmony_ci interruptible, 3*HZ); 2018c2ecf20Sopenharmony_ci if (interruptible && ret == -ERESTARTSYS) 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci else 2048c2ecf20Sopenharmony_ci BUG_ON(ret != 0); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci fill_escape(&cmds->escape, sizeof(cmds->body)); 2088c2ecf20Sopenharmony_ci cmds->body.header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS; 2098c2ecf20Sopenharmony_ci cmds->body.header.streamId = stream_id; 2108c2ecf20Sopenharmony_ci cmds->body.items[0].registerId = SVGA_VIDEO_ENABLED; 2118c2ecf20Sopenharmony_ci cmds->body.items[0].value = false; 2128c2ecf20Sopenharmony_ci fill_flush(&cmds->flush, stream_id); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci vmw_fifo_commit(dev_priv, sizeof(*cmds)); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/** 2208c2ecf20Sopenharmony_ci * Move a buffer to vram or gmr if @pin is set, else unpin the buffer. 2218c2ecf20Sopenharmony_ci * 2228c2ecf20Sopenharmony_ci * With the introduction of screen objects buffers could now be 2238c2ecf20Sopenharmony_ci * used with GMRs instead of being locked to vram. 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_cistatic int vmw_overlay_move_buffer(struct vmw_private *dev_priv, 2268c2ecf20Sopenharmony_ci struct vmw_buffer_object *buf, 2278c2ecf20Sopenharmony_ci bool pin, bool inter) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci if (!pin) 2308c2ecf20Sopenharmony_ci return vmw_bo_unpin(dev_priv, buf, inter); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (dev_priv->active_display_unit == vmw_du_legacy) 2338c2ecf20Sopenharmony_ci return vmw_bo_pin_in_vram(dev_priv, buf, inter); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return vmw_bo_pin_in_vram_or_gmr(dev_priv, buf, inter); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/** 2398c2ecf20Sopenharmony_ci * Stop or pause a stream. 2408c2ecf20Sopenharmony_ci * 2418c2ecf20Sopenharmony_ci * If the stream is paused the no evict flag is removed from the buffer 2428c2ecf20Sopenharmony_ci * but left in vram. This allows for instance mode_set to evict it 2438c2ecf20Sopenharmony_ci * should it need to. 2448c2ecf20Sopenharmony_ci * 2458c2ecf20Sopenharmony_ci * The caller must hold the overlay lock. 2468c2ecf20Sopenharmony_ci * 2478c2ecf20Sopenharmony_ci * @stream_id which stream to stop/pause. 2488c2ecf20Sopenharmony_ci * @pause true to pause, false to stop completely. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_cistatic int vmw_overlay_stop(struct vmw_private *dev_priv, 2518c2ecf20Sopenharmony_ci uint32_t stream_id, bool pause, 2528c2ecf20Sopenharmony_ci bool interruptible) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 2558c2ecf20Sopenharmony_ci struct vmw_stream *stream = &overlay->stream[stream_id]; 2568c2ecf20Sopenharmony_ci int ret; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* no buffer attached the stream is completely stopped */ 2598c2ecf20Sopenharmony_ci if (!stream->buf) 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* If the stream is paused this is already done */ 2638c2ecf20Sopenharmony_ci if (!stream->paused) { 2648c2ecf20Sopenharmony_ci ret = vmw_overlay_send_stop(dev_priv, stream_id, 2658c2ecf20Sopenharmony_ci interruptible); 2668c2ecf20Sopenharmony_ci if (ret) 2678c2ecf20Sopenharmony_ci return ret; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* We just remove the NO_EVICT flag so no -ENOMEM */ 2708c2ecf20Sopenharmony_ci ret = vmw_overlay_move_buffer(dev_priv, stream->buf, false, 2718c2ecf20Sopenharmony_ci interruptible); 2728c2ecf20Sopenharmony_ci if (interruptible && ret == -ERESTARTSYS) 2738c2ecf20Sopenharmony_ci return ret; 2748c2ecf20Sopenharmony_ci else 2758c2ecf20Sopenharmony_ci BUG_ON(ret != 0); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (!pause) { 2798c2ecf20Sopenharmony_ci vmw_bo_unreference(&stream->buf); 2808c2ecf20Sopenharmony_ci stream->paused = false; 2818c2ecf20Sopenharmony_ci } else { 2828c2ecf20Sopenharmony_ci stream->paused = true; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/** 2898c2ecf20Sopenharmony_ci * Update a stream and send any put or stop fifo commands needed. 2908c2ecf20Sopenharmony_ci * 2918c2ecf20Sopenharmony_ci * The caller must hold the overlay lock. 2928c2ecf20Sopenharmony_ci * 2938c2ecf20Sopenharmony_ci * Returns 2948c2ecf20Sopenharmony_ci * -ENOMEM if buffer doesn't fit in vram. 2958c2ecf20Sopenharmony_ci * -ERESTARTSYS if interrupted. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_cistatic int vmw_overlay_update_stream(struct vmw_private *dev_priv, 2988c2ecf20Sopenharmony_ci struct vmw_buffer_object *buf, 2998c2ecf20Sopenharmony_ci struct drm_vmw_control_stream_arg *arg, 3008c2ecf20Sopenharmony_ci bool interruptible) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 3038c2ecf20Sopenharmony_ci struct vmw_stream *stream = &overlay->stream[arg->stream_id]; 3048c2ecf20Sopenharmony_ci int ret = 0; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (!buf) 3078c2ecf20Sopenharmony_ci return -EINVAL; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci DRM_DEBUG(" %s: old %p, new %p, %spaused\n", __func__, 3108c2ecf20Sopenharmony_ci stream->buf, buf, stream->paused ? "" : "not "); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (stream->buf != buf) { 3138c2ecf20Sopenharmony_ci ret = vmw_overlay_stop(dev_priv, arg->stream_id, 3148c2ecf20Sopenharmony_ci false, interruptible); 3158c2ecf20Sopenharmony_ci if (ret) 3168c2ecf20Sopenharmony_ci return ret; 3178c2ecf20Sopenharmony_ci } else if (!stream->paused) { 3188c2ecf20Sopenharmony_ci /* If the buffers match and not paused then just send 3198c2ecf20Sopenharmony_ci * the put command, no need to do anything else. 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_ci ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible); 3228c2ecf20Sopenharmony_ci if (ret == 0) 3238c2ecf20Sopenharmony_ci stream->saved = *arg; 3248c2ecf20Sopenharmony_ci else 3258c2ecf20Sopenharmony_ci BUG_ON(!interruptible); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return ret; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* We don't start the old stream if we are interrupted. 3318c2ecf20Sopenharmony_ci * Might return -ENOMEM if it can't fit the buffer in vram. 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci ret = vmw_overlay_move_buffer(dev_priv, buf, true, interruptible); 3348c2ecf20Sopenharmony_ci if (ret) 3358c2ecf20Sopenharmony_ci return ret; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible); 3388c2ecf20Sopenharmony_ci if (ret) { 3398c2ecf20Sopenharmony_ci /* This one needs to happen no matter what. We only remove 3408c2ecf20Sopenharmony_ci * the NO_EVICT flag so this is safe from -ENOMEM. 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_ci BUG_ON(vmw_overlay_move_buffer(dev_priv, buf, false, false) 3438c2ecf20Sopenharmony_ci != 0); 3448c2ecf20Sopenharmony_ci return ret; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (stream->buf != buf) 3488c2ecf20Sopenharmony_ci stream->buf = vmw_bo_reference(buf); 3498c2ecf20Sopenharmony_ci stream->saved = *arg; 3508c2ecf20Sopenharmony_ci /* stream is no longer stopped/paused */ 3518c2ecf20Sopenharmony_ci stream->paused = false; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return 0; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci/** 3578c2ecf20Sopenharmony_ci * Try to resume all paused streams. 3588c2ecf20Sopenharmony_ci * 3598c2ecf20Sopenharmony_ci * Used by the kms code after moving a new scanout buffer to vram. 3608c2ecf20Sopenharmony_ci * 3618c2ecf20Sopenharmony_ci * Takes the overlay lock. 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_ciint vmw_overlay_resume_all(struct vmw_private *dev_priv) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 3668c2ecf20Sopenharmony_ci int i, ret; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (!overlay) 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci mutex_lock(&overlay->mutex); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { 3748c2ecf20Sopenharmony_ci struct vmw_stream *stream = &overlay->stream[i]; 3758c2ecf20Sopenharmony_ci if (!stream->paused) 3768c2ecf20Sopenharmony_ci continue; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci ret = vmw_overlay_update_stream(dev_priv, stream->buf, 3798c2ecf20Sopenharmony_ci &stream->saved, false); 3808c2ecf20Sopenharmony_ci if (ret != 0) 3818c2ecf20Sopenharmony_ci DRM_INFO("%s: *warning* failed to resume stream %i\n", 3828c2ecf20Sopenharmony_ci __func__, i); 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci mutex_unlock(&overlay->mutex); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci/** 3918c2ecf20Sopenharmony_ci * Pauses all active streams. 3928c2ecf20Sopenharmony_ci * 3938c2ecf20Sopenharmony_ci * Used by the kms code when moving a new scanout buffer to vram. 3948c2ecf20Sopenharmony_ci * 3958c2ecf20Sopenharmony_ci * Takes the overlay lock. 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ciint vmw_overlay_pause_all(struct vmw_private *dev_priv) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 4008c2ecf20Sopenharmony_ci int i, ret; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (!overlay) 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci mutex_lock(&overlay->mutex); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { 4088c2ecf20Sopenharmony_ci if (overlay->stream[i].paused) 4098c2ecf20Sopenharmony_ci DRM_INFO("%s: *warning* stream %i already paused\n", 4108c2ecf20Sopenharmony_ci __func__, i); 4118c2ecf20Sopenharmony_ci ret = vmw_overlay_stop(dev_priv, i, true, false); 4128c2ecf20Sopenharmony_ci WARN_ON(ret != 0); 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci mutex_unlock(&overlay->mutex); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return 0; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic bool vmw_overlay_available(const struct vmw_private *dev_priv) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci return (dev_priv->overlay_priv != NULL && 4248c2ecf20Sopenharmony_ci ((dev_priv->fifo.capabilities & VMW_OVERLAY_CAP_MASK) == 4258c2ecf20Sopenharmony_ci VMW_OVERLAY_CAP_MASK)); 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ciint vmw_overlay_ioctl(struct drm_device *dev, void *data, 4298c2ecf20Sopenharmony_ci struct drm_file *file_priv) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; 4328c2ecf20Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 4338c2ecf20Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 4348c2ecf20Sopenharmony_ci struct drm_vmw_control_stream_arg *arg = 4358c2ecf20Sopenharmony_ci (struct drm_vmw_control_stream_arg *)data; 4368c2ecf20Sopenharmony_ci struct vmw_buffer_object *buf; 4378c2ecf20Sopenharmony_ci struct vmw_resource *res; 4388c2ecf20Sopenharmony_ci int ret; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (!vmw_overlay_available(dev_priv)) 4418c2ecf20Sopenharmony_ci return -ENOSYS; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci ret = vmw_user_stream_lookup(dev_priv, tfile, &arg->stream_id, &res); 4448c2ecf20Sopenharmony_ci if (ret) 4458c2ecf20Sopenharmony_ci return ret; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci mutex_lock(&overlay->mutex); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (!arg->enabled) { 4508c2ecf20Sopenharmony_ci ret = vmw_overlay_stop(dev_priv, arg->stream_id, false, true); 4518c2ecf20Sopenharmony_ci goto out_unlock; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci ret = vmw_user_bo_lookup(tfile, arg->handle, &buf, NULL); 4558c2ecf20Sopenharmony_ci if (ret) 4568c2ecf20Sopenharmony_ci goto out_unlock; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci ret = vmw_overlay_update_stream(dev_priv, buf, arg, true); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci vmw_bo_unreference(&buf); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ciout_unlock: 4638c2ecf20Sopenharmony_ci mutex_unlock(&overlay->mutex); 4648c2ecf20Sopenharmony_ci vmw_resource_unreference(&res); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci return ret; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ciint vmw_overlay_num_overlays(struct vmw_private *dev_priv) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci if (!vmw_overlay_available(dev_priv)) 4728c2ecf20Sopenharmony_ci return 0; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci return VMW_MAX_NUM_STREAMS; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ciint vmw_overlay_num_free_overlays(struct vmw_private *dev_priv) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 4808c2ecf20Sopenharmony_ci int i, k; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (!vmw_overlay_available(dev_priv)) 4838c2ecf20Sopenharmony_ci return 0; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci mutex_lock(&overlay->mutex); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci for (i = 0, k = 0; i < VMW_MAX_NUM_STREAMS; i++) 4888c2ecf20Sopenharmony_ci if (!overlay->stream[i].claimed) 4898c2ecf20Sopenharmony_ci k++; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci mutex_unlock(&overlay->mutex); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return k; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ciint vmw_overlay_claim(struct vmw_private *dev_priv, uint32_t *out) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 4998c2ecf20Sopenharmony_ci int i; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (!overlay) 5028c2ecf20Sopenharmony_ci return -ENOSYS; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci mutex_lock(&overlay->mutex); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (overlay->stream[i].claimed) 5098c2ecf20Sopenharmony_ci continue; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci overlay->stream[i].claimed = true; 5128c2ecf20Sopenharmony_ci *out = i; 5138c2ecf20Sopenharmony_ci mutex_unlock(&overlay->mutex); 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci mutex_unlock(&overlay->mutex); 5188c2ecf20Sopenharmony_ci return -ESRCH; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ciint vmw_overlay_unref(struct vmw_private *dev_priv, uint32_t stream_id) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci BUG_ON(stream_id >= VMW_MAX_NUM_STREAMS); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (!overlay) 5288c2ecf20Sopenharmony_ci return -ENOSYS; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci mutex_lock(&overlay->mutex); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci WARN_ON(!overlay->stream[stream_id].claimed); 5338c2ecf20Sopenharmony_ci vmw_overlay_stop(dev_priv, stream_id, false, false); 5348c2ecf20Sopenharmony_ci overlay->stream[stream_id].claimed = false; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci mutex_unlock(&overlay->mutex); 5378c2ecf20Sopenharmony_ci return 0; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ciint vmw_overlay_init(struct vmw_private *dev_priv) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci struct vmw_overlay *overlay; 5438c2ecf20Sopenharmony_ci int i; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (dev_priv->overlay_priv) 5468c2ecf20Sopenharmony_ci return -EINVAL; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); 5498c2ecf20Sopenharmony_ci if (!overlay) 5508c2ecf20Sopenharmony_ci return -ENOMEM; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci mutex_init(&overlay->mutex); 5538c2ecf20Sopenharmony_ci for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { 5548c2ecf20Sopenharmony_ci overlay->stream[i].buf = NULL; 5558c2ecf20Sopenharmony_ci overlay->stream[i].paused = false; 5568c2ecf20Sopenharmony_ci overlay->stream[i].claimed = false; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci dev_priv->overlay_priv = overlay; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci return 0; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ciint vmw_overlay_close(struct vmw_private *dev_priv) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 5678c2ecf20Sopenharmony_ci bool forgotten_buffer = false; 5688c2ecf20Sopenharmony_ci int i; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (!overlay) 5718c2ecf20Sopenharmony_ci return -ENOSYS; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { 5748c2ecf20Sopenharmony_ci if (overlay->stream[i].buf) { 5758c2ecf20Sopenharmony_ci forgotten_buffer = true; 5768c2ecf20Sopenharmony_ci vmw_overlay_stop(dev_priv, i, false, false); 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci WARN_ON(forgotten_buffer); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci dev_priv->overlay_priv = NULL; 5838c2ecf20Sopenharmony_ci kfree(overlay); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci return 0; 5868c2ecf20Sopenharmony_ci} 587