162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT 262306a36Sopenharmony_ci/************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 762306a36Sopenharmony_ci * copy of this software and associated documentation files (the 862306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 962306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 1062306a36Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 1162306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 1262306a36Sopenharmony_ci * the following conditions: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the 1562306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 1662306a36Sopenharmony_ci * of the Software. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1962306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2062306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 2162306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 2262306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 2362306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 2462306a36Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci **************************************************************************/ 2762306a36Sopenharmony_ci#include "vmwgfx_bo.h" 2862306a36Sopenharmony_ci#include "vmwgfx_drv.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "device_include/svga_overlay.h" 3162306a36Sopenharmony_ci#include "device_include/svga_escape.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <drm/ttm/ttm_placement.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define VMW_MAX_NUM_STREAMS 1 3662306a36Sopenharmony_ci#define VMW_OVERLAY_CAP_MASK (SVGA_FIFO_CAP_VIDEO | SVGA_FIFO_CAP_ESCAPE) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct vmw_stream { 3962306a36Sopenharmony_ci struct vmw_bo *buf; 4062306a36Sopenharmony_ci bool claimed; 4162306a36Sopenharmony_ci bool paused; 4262306a36Sopenharmony_ci struct drm_vmw_control_stream_arg saved; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * Overlay control 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_cistruct vmw_overlay { 4962306a36Sopenharmony_ci /* 5062306a36Sopenharmony_ci * Each stream is a single overlay. In Xv these are called ports. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci struct mutex mutex; 5362306a36Sopenharmony_ci struct vmw_stream stream[VMW_MAX_NUM_STREAMS]; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistruct vmw_escape_header { 5762306a36Sopenharmony_ci uint32_t cmd; 5862306a36Sopenharmony_ci SVGAFifoCmdEscape body; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct vmw_escape_video_flush { 6262306a36Sopenharmony_ci struct vmw_escape_header escape; 6362306a36Sopenharmony_ci SVGAEscapeVideoFlush flush; 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic inline void fill_escape(struct vmw_escape_header *header, 6762306a36Sopenharmony_ci uint32_t size) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci header->cmd = SVGA_CMD_ESCAPE; 7062306a36Sopenharmony_ci header->body.nsid = SVGA_ESCAPE_NSID_VMWARE; 7162306a36Sopenharmony_ci header->body.size = size; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic inline void fill_flush(struct vmw_escape_video_flush *cmd, 7562306a36Sopenharmony_ci uint32_t stream_id) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci fill_escape(&cmd->escape, sizeof(cmd->flush)); 7862306a36Sopenharmony_ci cmd->flush.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_FLUSH; 7962306a36Sopenharmony_ci cmd->flush.streamId = stream_id; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* 8362306a36Sopenharmony_ci * Send put command to hw. 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * Returns 8662306a36Sopenharmony_ci * -ERESTARTSYS if interrupted by a signal. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_cistatic int vmw_overlay_send_put(struct vmw_private *dev_priv, 8962306a36Sopenharmony_ci struct vmw_bo *buf, 9062306a36Sopenharmony_ci struct drm_vmw_control_stream_arg *arg, 9162306a36Sopenharmony_ci bool interruptible) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct vmw_escape_video_flush *flush; 9462306a36Sopenharmony_ci size_t fifo_size; 9562306a36Sopenharmony_ci bool have_so = (dev_priv->active_display_unit == vmw_du_screen_object); 9662306a36Sopenharmony_ci int i, num_items; 9762306a36Sopenharmony_ci SVGAGuestPtr ptr; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci struct { 10062306a36Sopenharmony_ci struct vmw_escape_header escape; 10162306a36Sopenharmony_ci struct { 10262306a36Sopenharmony_ci uint32_t cmdType; 10362306a36Sopenharmony_ci uint32_t streamId; 10462306a36Sopenharmony_ci } header; 10562306a36Sopenharmony_ci } *cmds; 10662306a36Sopenharmony_ci struct { 10762306a36Sopenharmony_ci uint32_t registerId; 10862306a36Sopenharmony_ci uint32_t value; 10962306a36Sopenharmony_ci } *items; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* defines are a index needs + 1 */ 11262306a36Sopenharmony_ci if (have_so) 11362306a36Sopenharmony_ci num_items = SVGA_VIDEO_DST_SCREEN_ID + 1; 11462306a36Sopenharmony_ci else 11562306a36Sopenharmony_ci num_items = SVGA_VIDEO_PITCH_3 + 1; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci fifo_size = sizeof(*cmds) + sizeof(*flush) + sizeof(*items) * num_items; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci cmds = VMW_CMD_RESERVE(dev_priv, fifo_size); 12062306a36Sopenharmony_ci /* hardware has hung, can't do anything here */ 12162306a36Sopenharmony_ci if (!cmds) 12262306a36Sopenharmony_ci return -ENOMEM; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci items = (typeof(items))&cmds[1]; 12562306a36Sopenharmony_ci flush = (struct vmw_escape_video_flush *)&items[num_items]; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* the size is header + number of items */ 12862306a36Sopenharmony_ci fill_escape(&cmds->escape, sizeof(*items) * (num_items + 1)); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci cmds->header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS; 13162306a36Sopenharmony_ci cmds->header.streamId = arg->stream_id; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* the IDs are neatly numbered */ 13462306a36Sopenharmony_ci for (i = 0; i < num_items; i++) 13562306a36Sopenharmony_ci items[i].registerId = i; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci vmw_bo_get_guest_ptr(&buf->tbo, &ptr); 13862306a36Sopenharmony_ci ptr.offset += arg->offset; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci items[SVGA_VIDEO_ENABLED].value = true; 14162306a36Sopenharmony_ci items[SVGA_VIDEO_FLAGS].value = arg->flags; 14262306a36Sopenharmony_ci items[SVGA_VIDEO_DATA_OFFSET].value = ptr.offset; 14362306a36Sopenharmony_ci items[SVGA_VIDEO_FORMAT].value = arg->format; 14462306a36Sopenharmony_ci items[SVGA_VIDEO_COLORKEY].value = arg->color_key; 14562306a36Sopenharmony_ci items[SVGA_VIDEO_SIZE].value = arg->size; 14662306a36Sopenharmony_ci items[SVGA_VIDEO_WIDTH].value = arg->width; 14762306a36Sopenharmony_ci items[SVGA_VIDEO_HEIGHT].value = arg->height; 14862306a36Sopenharmony_ci items[SVGA_VIDEO_SRC_X].value = arg->src.x; 14962306a36Sopenharmony_ci items[SVGA_VIDEO_SRC_Y].value = arg->src.y; 15062306a36Sopenharmony_ci items[SVGA_VIDEO_SRC_WIDTH].value = arg->src.w; 15162306a36Sopenharmony_ci items[SVGA_VIDEO_SRC_HEIGHT].value = arg->src.h; 15262306a36Sopenharmony_ci items[SVGA_VIDEO_DST_X].value = arg->dst.x; 15362306a36Sopenharmony_ci items[SVGA_VIDEO_DST_Y].value = arg->dst.y; 15462306a36Sopenharmony_ci items[SVGA_VIDEO_DST_WIDTH].value = arg->dst.w; 15562306a36Sopenharmony_ci items[SVGA_VIDEO_DST_HEIGHT].value = arg->dst.h; 15662306a36Sopenharmony_ci items[SVGA_VIDEO_PITCH_1].value = arg->pitch[0]; 15762306a36Sopenharmony_ci items[SVGA_VIDEO_PITCH_2].value = arg->pitch[1]; 15862306a36Sopenharmony_ci items[SVGA_VIDEO_PITCH_3].value = arg->pitch[2]; 15962306a36Sopenharmony_ci if (have_so) { 16062306a36Sopenharmony_ci items[SVGA_VIDEO_DATA_GMRID].value = ptr.gmrId; 16162306a36Sopenharmony_ci items[SVGA_VIDEO_DST_SCREEN_ID].value = SVGA_ID_INVALID; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci fill_flush(flush, arg->stream_id); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci vmw_cmd_commit(dev_priv, fifo_size); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/* 17262306a36Sopenharmony_ci * Send stop command to hw. 17362306a36Sopenharmony_ci * 17462306a36Sopenharmony_ci * Returns 17562306a36Sopenharmony_ci * -ERESTARTSYS if interrupted by a signal. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_cistatic int vmw_overlay_send_stop(struct vmw_private *dev_priv, 17862306a36Sopenharmony_ci uint32_t stream_id, 17962306a36Sopenharmony_ci bool interruptible) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct { 18262306a36Sopenharmony_ci struct vmw_escape_header escape; 18362306a36Sopenharmony_ci SVGAEscapeVideoSetRegs body; 18462306a36Sopenharmony_ci struct vmw_escape_video_flush flush; 18562306a36Sopenharmony_ci } *cmds; 18662306a36Sopenharmony_ci int ret; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci for (;;) { 18962306a36Sopenharmony_ci cmds = VMW_CMD_RESERVE(dev_priv, sizeof(*cmds)); 19062306a36Sopenharmony_ci if (cmds) 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ret = vmw_fallback_wait(dev_priv, false, true, 0, 19462306a36Sopenharmony_ci interruptible, 3*HZ); 19562306a36Sopenharmony_ci if (interruptible && ret == -ERESTARTSYS) 19662306a36Sopenharmony_ci return ret; 19762306a36Sopenharmony_ci else 19862306a36Sopenharmony_ci BUG_ON(ret != 0); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci fill_escape(&cmds->escape, sizeof(cmds->body)); 20262306a36Sopenharmony_ci cmds->body.header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS; 20362306a36Sopenharmony_ci cmds->body.header.streamId = stream_id; 20462306a36Sopenharmony_ci cmds->body.items[0].registerId = SVGA_VIDEO_ENABLED; 20562306a36Sopenharmony_ci cmds->body.items[0].value = false; 20662306a36Sopenharmony_ci fill_flush(&cmds->flush, stream_id); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci vmw_cmd_commit(dev_priv, sizeof(*cmds)); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* 21462306a36Sopenharmony_ci * Move a buffer to vram or gmr if @pin is set, else unpin the buffer. 21562306a36Sopenharmony_ci * 21662306a36Sopenharmony_ci * With the introduction of screen objects buffers could now be 21762306a36Sopenharmony_ci * used with GMRs instead of being locked to vram. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_cistatic int vmw_overlay_move_buffer(struct vmw_private *dev_priv, 22062306a36Sopenharmony_ci struct vmw_bo *buf, 22162306a36Sopenharmony_ci bool pin, bool inter) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci if (!pin) 22462306a36Sopenharmony_ci return vmw_bo_unpin(dev_priv, buf, inter); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (dev_priv->active_display_unit == vmw_du_legacy) 22762306a36Sopenharmony_ci return vmw_bo_pin_in_vram(dev_priv, buf, inter); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return vmw_bo_pin_in_vram_or_gmr(dev_priv, buf, inter); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/* 23362306a36Sopenharmony_ci * Stop or pause a stream. 23462306a36Sopenharmony_ci * 23562306a36Sopenharmony_ci * If the stream is paused the no evict flag is removed from the buffer 23662306a36Sopenharmony_ci * but left in vram. This allows for instance mode_set to evict it 23762306a36Sopenharmony_ci * should it need to. 23862306a36Sopenharmony_ci * 23962306a36Sopenharmony_ci * The caller must hold the overlay lock. 24062306a36Sopenharmony_ci * 24162306a36Sopenharmony_ci * @stream_id which stream to stop/pause. 24262306a36Sopenharmony_ci * @pause true to pause, false to stop completely. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_cistatic int vmw_overlay_stop(struct vmw_private *dev_priv, 24562306a36Sopenharmony_ci uint32_t stream_id, bool pause, 24662306a36Sopenharmony_ci bool interruptible) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 24962306a36Sopenharmony_ci struct vmw_stream *stream = &overlay->stream[stream_id]; 25062306a36Sopenharmony_ci int ret; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* no buffer attached the stream is completely stopped */ 25362306a36Sopenharmony_ci if (!stream->buf) 25462306a36Sopenharmony_ci return 0; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* If the stream is paused this is already done */ 25762306a36Sopenharmony_ci if (!stream->paused) { 25862306a36Sopenharmony_ci ret = vmw_overlay_send_stop(dev_priv, stream_id, 25962306a36Sopenharmony_ci interruptible); 26062306a36Sopenharmony_ci if (ret) 26162306a36Sopenharmony_ci return ret; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* We just remove the NO_EVICT flag so no -ENOMEM */ 26462306a36Sopenharmony_ci ret = vmw_overlay_move_buffer(dev_priv, stream->buf, false, 26562306a36Sopenharmony_ci interruptible); 26662306a36Sopenharmony_ci if (interruptible && ret == -ERESTARTSYS) 26762306a36Sopenharmony_ci return ret; 26862306a36Sopenharmony_ci else 26962306a36Sopenharmony_ci BUG_ON(ret != 0); 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (!pause) { 27362306a36Sopenharmony_ci vmw_bo_unreference(&stream->buf); 27462306a36Sopenharmony_ci stream->paused = false; 27562306a36Sopenharmony_ci } else { 27662306a36Sopenharmony_ci stream->paused = true; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return 0; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci/* 28362306a36Sopenharmony_ci * Update a stream and send any put or stop fifo commands needed. 28462306a36Sopenharmony_ci * 28562306a36Sopenharmony_ci * The caller must hold the overlay lock. 28662306a36Sopenharmony_ci * 28762306a36Sopenharmony_ci * Returns 28862306a36Sopenharmony_ci * -ENOMEM if buffer doesn't fit in vram. 28962306a36Sopenharmony_ci * -ERESTARTSYS if interrupted. 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_cistatic int vmw_overlay_update_stream(struct vmw_private *dev_priv, 29262306a36Sopenharmony_ci struct vmw_bo *buf, 29362306a36Sopenharmony_ci struct drm_vmw_control_stream_arg *arg, 29462306a36Sopenharmony_ci bool interruptible) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 29762306a36Sopenharmony_ci struct vmw_stream *stream = &overlay->stream[arg->stream_id]; 29862306a36Sopenharmony_ci int ret = 0; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (!buf) 30162306a36Sopenharmony_ci return -EINVAL; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci DRM_DEBUG(" %s: old %p, new %p, %spaused\n", __func__, 30462306a36Sopenharmony_ci stream->buf, buf, stream->paused ? "" : "not "); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (stream->buf != buf) { 30762306a36Sopenharmony_ci ret = vmw_overlay_stop(dev_priv, arg->stream_id, 30862306a36Sopenharmony_ci false, interruptible); 30962306a36Sopenharmony_ci if (ret) 31062306a36Sopenharmony_ci return ret; 31162306a36Sopenharmony_ci } else if (!stream->paused) { 31262306a36Sopenharmony_ci /* If the buffers match and not paused then just send 31362306a36Sopenharmony_ci * the put command, no need to do anything else. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible); 31662306a36Sopenharmony_ci if (ret == 0) 31762306a36Sopenharmony_ci stream->saved = *arg; 31862306a36Sopenharmony_ci else 31962306a36Sopenharmony_ci BUG_ON(!interruptible); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return ret; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* We don't start the old stream if we are interrupted. 32562306a36Sopenharmony_ci * Might return -ENOMEM if it can't fit the buffer in vram. 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_ci ret = vmw_overlay_move_buffer(dev_priv, buf, true, interruptible); 32862306a36Sopenharmony_ci if (ret) 32962306a36Sopenharmony_ci return ret; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible); 33262306a36Sopenharmony_ci if (ret) { 33362306a36Sopenharmony_ci /* This one needs to happen no matter what. We only remove 33462306a36Sopenharmony_ci * the NO_EVICT flag so this is safe from -ENOMEM. 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci BUG_ON(vmw_overlay_move_buffer(dev_priv, buf, false, false) 33762306a36Sopenharmony_ci != 0); 33862306a36Sopenharmony_ci return ret; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (stream->buf != buf) 34262306a36Sopenharmony_ci stream->buf = vmw_bo_reference(buf); 34362306a36Sopenharmony_ci stream->saved = *arg; 34462306a36Sopenharmony_ci /* stream is no longer stopped/paused */ 34562306a36Sopenharmony_ci stream->paused = false; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci/* 35162306a36Sopenharmony_ci * Try to resume all paused streams. 35262306a36Sopenharmony_ci * 35362306a36Sopenharmony_ci * Used by the kms code after moving a new scanout buffer to vram. 35462306a36Sopenharmony_ci * 35562306a36Sopenharmony_ci * Takes the overlay lock. 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_ciint vmw_overlay_resume_all(struct vmw_private *dev_priv) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 36062306a36Sopenharmony_ci int i, ret; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (!overlay) 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci mutex_lock(&overlay->mutex); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { 36862306a36Sopenharmony_ci struct vmw_stream *stream = &overlay->stream[i]; 36962306a36Sopenharmony_ci if (!stream->paused) 37062306a36Sopenharmony_ci continue; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci ret = vmw_overlay_update_stream(dev_priv, stream->buf, 37362306a36Sopenharmony_ci &stream->saved, false); 37462306a36Sopenharmony_ci if (ret != 0) 37562306a36Sopenharmony_ci DRM_INFO("%s: *warning* failed to resume stream %i\n", 37662306a36Sopenharmony_ci __func__, i); 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci mutex_unlock(&overlay->mutex); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/* 38562306a36Sopenharmony_ci * Pauses all active streams. 38662306a36Sopenharmony_ci * 38762306a36Sopenharmony_ci * Used by the kms code when moving a new scanout buffer to vram. 38862306a36Sopenharmony_ci * 38962306a36Sopenharmony_ci * Takes the overlay lock. 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_ciint vmw_overlay_pause_all(struct vmw_private *dev_priv) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 39462306a36Sopenharmony_ci int i, ret; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (!overlay) 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci mutex_lock(&overlay->mutex); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { 40262306a36Sopenharmony_ci if (overlay->stream[i].paused) 40362306a36Sopenharmony_ci DRM_INFO("%s: *warning* stream %i already paused\n", 40462306a36Sopenharmony_ci __func__, i); 40562306a36Sopenharmony_ci ret = vmw_overlay_stop(dev_priv, i, true, false); 40662306a36Sopenharmony_ci WARN_ON(ret != 0); 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci mutex_unlock(&overlay->mutex); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return 0; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic bool vmw_overlay_available(const struct vmw_private *dev_priv) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci return (dev_priv->overlay_priv != NULL && 41862306a36Sopenharmony_ci ((vmw_fifo_caps(dev_priv) & VMW_OVERLAY_CAP_MASK) == 41962306a36Sopenharmony_ci VMW_OVERLAY_CAP_MASK)); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ciint vmw_overlay_ioctl(struct drm_device *dev, void *data, 42362306a36Sopenharmony_ci struct drm_file *file_priv) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; 42662306a36Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 42762306a36Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 42862306a36Sopenharmony_ci struct drm_vmw_control_stream_arg *arg = 42962306a36Sopenharmony_ci (struct drm_vmw_control_stream_arg *)data; 43062306a36Sopenharmony_ci struct vmw_bo *buf; 43162306a36Sopenharmony_ci struct vmw_resource *res; 43262306a36Sopenharmony_ci int ret; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (!vmw_overlay_available(dev_priv)) 43562306a36Sopenharmony_ci return -ENOSYS; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci ret = vmw_user_stream_lookup(dev_priv, tfile, &arg->stream_id, &res); 43862306a36Sopenharmony_ci if (ret) 43962306a36Sopenharmony_ci return ret; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci mutex_lock(&overlay->mutex); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (!arg->enabled) { 44462306a36Sopenharmony_ci ret = vmw_overlay_stop(dev_priv, arg->stream_id, false, true); 44562306a36Sopenharmony_ci goto out_unlock; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci ret = vmw_user_bo_lookup(file_priv, arg->handle, &buf); 44962306a36Sopenharmony_ci if (ret) 45062306a36Sopenharmony_ci goto out_unlock; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci ret = vmw_overlay_update_stream(dev_priv, buf, arg, true); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci vmw_user_bo_unref(&buf); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ciout_unlock: 45762306a36Sopenharmony_ci mutex_unlock(&overlay->mutex); 45862306a36Sopenharmony_ci vmw_resource_unreference(&res); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return ret; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ciint vmw_overlay_num_overlays(struct vmw_private *dev_priv) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci if (!vmw_overlay_available(dev_priv)) 46662306a36Sopenharmony_ci return 0; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci return VMW_MAX_NUM_STREAMS; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ciint vmw_overlay_num_free_overlays(struct vmw_private *dev_priv) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 47462306a36Sopenharmony_ci int i, k; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (!vmw_overlay_available(dev_priv)) 47762306a36Sopenharmony_ci return 0; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci mutex_lock(&overlay->mutex); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci for (i = 0, k = 0; i < VMW_MAX_NUM_STREAMS; i++) 48262306a36Sopenharmony_ci if (!overlay->stream[i].claimed) 48362306a36Sopenharmony_ci k++; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci mutex_unlock(&overlay->mutex); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return k; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ciint vmw_overlay_claim(struct vmw_private *dev_priv, uint32_t *out) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 49362306a36Sopenharmony_ci int i; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (!overlay) 49662306a36Sopenharmony_ci return -ENOSYS; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci mutex_lock(&overlay->mutex); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (overlay->stream[i].claimed) 50362306a36Sopenharmony_ci continue; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci overlay->stream[i].claimed = true; 50662306a36Sopenharmony_ci *out = i; 50762306a36Sopenharmony_ci mutex_unlock(&overlay->mutex); 50862306a36Sopenharmony_ci return 0; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci mutex_unlock(&overlay->mutex); 51262306a36Sopenharmony_ci return -ESRCH; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ciint vmw_overlay_unref(struct vmw_private *dev_priv, uint32_t stream_id) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci BUG_ON(stream_id >= VMW_MAX_NUM_STREAMS); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (!overlay) 52262306a36Sopenharmony_ci return -ENOSYS; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci mutex_lock(&overlay->mutex); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci WARN_ON(!overlay->stream[stream_id].claimed); 52762306a36Sopenharmony_ci vmw_overlay_stop(dev_priv, stream_id, false, false); 52862306a36Sopenharmony_ci overlay->stream[stream_id].claimed = false; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci mutex_unlock(&overlay->mutex); 53162306a36Sopenharmony_ci return 0; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ciint vmw_overlay_init(struct vmw_private *dev_priv) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct vmw_overlay *overlay; 53762306a36Sopenharmony_ci int i; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (dev_priv->overlay_priv) 54062306a36Sopenharmony_ci return -EINVAL; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); 54362306a36Sopenharmony_ci if (!overlay) 54462306a36Sopenharmony_ci return -ENOMEM; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci mutex_init(&overlay->mutex); 54762306a36Sopenharmony_ci for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { 54862306a36Sopenharmony_ci overlay->stream[i].buf = NULL; 54962306a36Sopenharmony_ci overlay->stream[i].paused = false; 55062306a36Sopenharmony_ci overlay->stream[i].claimed = false; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci dev_priv->overlay_priv = overlay; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci return 0; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ciint vmw_overlay_close(struct vmw_private *dev_priv) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci struct vmw_overlay *overlay = dev_priv->overlay_priv; 56162306a36Sopenharmony_ci bool forgotten_buffer = false; 56262306a36Sopenharmony_ci int i; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (!overlay) 56562306a36Sopenharmony_ci return -ENOSYS; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { 56862306a36Sopenharmony_ci if (overlay->stream[i].buf) { 56962306a36Sopenharmony_ci forgotten_buffer = true; 57062306a36Sopenharmony_ci vmw_overlay_stop(dev_priv, i, false, false); 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci WARN_ON(forgotten_buffer); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci dev_priv->overlay_priv = NULL; 57762306a36Sopenharmony_ci kfree(overlay); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return 0; 58062306a36Sopenharmony_ci} 581