Home | History | Annotate | Line # | Download | only in vmwgfx
      1 /*	$NetBSD: vmwgfx_overlay.c,v 1.4 2022/10/25 23:35:43 riastradh Exp $	*/
      2 
      3 // SPDX-License-Identifier: GPL-2.0 OR MIT
      4 /**************************************************************************
      5  *
      6  * Copyright 2009-2014 VMware, Inc., Palo Alto, CA., USA
      7  *
      8  * Permission is hereby granted, free of charge, to any person obtaining a
      9  * copy of this software and associated documentation files (the
     10  * "Software"), to deal in the Software without restriction, including
     11  * without limitation the rights to use, copy, modify, merge, publish,
     12  * distribute, sub license, and/or sell copies of the Software, and to
     13  * permit persons to whom the Software is furnished to do so, subject to
     14  * the following conditions:
     15  *
     16  * The above copyright notice and this permission notice (including the
     17  * next paragraph) shall be included in all copies or substantial portions
     18  * of the Software.
     19  *
     20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     22  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
     23  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
     24  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
     25  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
     26  * USE OR OTHER DEALINGS IN THE SOFTWARE.
     27  *
     28  **************************************************************************/
     29 
     30 #include <sys/cdefs.h>
     31 __KERNEL_RCSID(0, "$NetBSD: vmwgfx_overlay.c,v 1.4 2022/10/25 23:35:43 riastradh Exp $");
     32 
     33 #include <drm/ttm/ttm_placement.h>
     34 
     35 #include "device_include/svga_overlay.h"
     36 #include "device_include/svga_escape.h"
     37 
     38 #include "vmwgfx_drv.h"
     39 
     40 #include <linux/nbsd-namespace.h>
     41 
     42 #define VMW_MAX_NUM_STREAMS 1
     43 #define VMW_OVERLAY_CAP_MASK (SVGA_FIFO_CAP_VIDEO | SVGA_FIFO_CAP_ESCAPE)
     44 
     45 struct vmw_stream {
     46 	struct vmw_buffer_object *buf;
     47 	bool claimed;
     48 	bool paused;
     49 	struct drm_vmw_control_stream_arg saved;
     50 };
     51 
     52 /**
     53  * Overlay control
     54  */
     55 struct vmw_overlay {
     56 	/*
     57 	 * Each stream is a single overlay. In Xv these are called ports.
     58 	 */
     59 	struct mutex mutex;
     60 	struct vmw_stream stream[VMW_MAX_NUM_STREAMS];
     61 };
     62 
     63 static inline struct vmw_overlay *vmw_overlay(struct drm_device *dev)
     64 {
     65 	struct vmw_private *dev_priv = vmw_priv(dev);
     66 	return dev_priv ? dev_priv->overlay_priv : NULL;
     67 }
     68 
     69 struct vmw_escape_header {
     70 	uint32_t cmd;
     71 	SVGAFifoCmdEscape body;
     72 };
     73 
     74 struct vmw_escape_video_flush {
     75 	struct vmw_escape_header escape;
     76 	SVGAEscapeVideoFlush flush;
     77 };
     78 
     79 static inline void fill_escape(struct vmw_escape_header *header,
     80 			       uint32_t size)
     81 {
     82 	header->cmd = SVGA_CMD_ESCAPE;
     83 	header->body.nsid = SVGA_ESCAPE_NSID_VMWARE;
     84 	header->body.size = size;
     85 }
     86 
     87 static inline void fill_flush(struct vmw_escape_video_flush *cmd,
     88 			      uint32_t stream_id)
     89 {
     90 	fill_escape(&cmd->escape, sizeof(cmd->flush));
     91 	cmd->flush.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_FLUSH;
     92 	cmd->flush.streamId = stream_id;
     93 }
     94 
     95 /**
     96  * Send put command to hw.
     97  *
     98  * Returns
     99  * -ERESTARTSYS if interrupted by a signal.
    100  */
    101 static int vmw_overlay_send_put(struct vmw_private *dev_priv,
    102 				struct vmw_buffer_object *buf,
    103 				struct drm_vmw_control_stream_arg *arg,
    104 				bool interruptible)
    105 {
    106 	struct vmw_escape_video_flush *flush;
    107 	size_t fifo_size;
    108 	bool have_so = (dev_priv->active_display_unit == vmw_du_screen_object);
    109 	int i, num_items;
    110 	SVGAGuestPtr ptr;
    111 
    112 	struct {
    113 		struct vmw_escape_header escape;
    114 		struct {
    115 			uint32_t cmdType;
    116 			uint32_t streamId;
    117 		} header;
    118 	} *cmds;
    119 	struct {
    120 		uint32_t registerId;
    121 		uint32_t value;
    122 	} *items;
    123 
    124 	/* defines are a index needs + 1 */
    125 	if (have_so)
    126 		num_items = SVGA_VIDEO_DST_SCREEN_ID + 1;
    127 	else
    128 		num_items = SVGA_VIDEO_PITCH_3 + 1;
    129 
    130 	fifo_size = sizeof(*cmds) + sizeof(*flush) + sizeof(*items) * num_items;
    131 
    132 	cmds = VMW_FIFO_RESERVE(dev_priv, fifo_size);
    133 	/* hardware has hung, can't do anything here */
    134 	if (!cmds)
    135 		return -ENOMEM;
    136 
    137 	items = (typeof(items))&cmds[1];
    138 	flush = (struct vmw_escape_video_flush *)&items[num_items];
    139 
    140 	/* the size is header + number of items */
    141 	fill_escape(&cmds->escape, sizeof(*items) * (num_items + 1));
    142 
    143 	cmds->header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS;
    144 	cmds->header.streamId = arg->stream_id;
    145 
    146 	/* the IDs are neatly numbered */
    147 	for (i = 0; i < num_items; i++)
    148 		items[i].registerId = i;
    149 
    150 	vmw_bo_get_guest_ptr(&buf->base, &ptr);
    151 	ptr.offset += arg->offset;
    152 
    153 	items[SVGA_VIDEO_ENABLED].value     = true;
    154 	items[SVGA_VIDEO_FLAGS].value       = arg->flags;
    155 	items[SVGA_VIDEO_DATA_OFFSET].value = ptr.offset;
    156 	items[SVGA_VIDEO_FORMAT].value      = arg->format;
    157 	items[SVGA_VIDEO_COLORKEY].value    = arg->color_key;
    158 	items[SVGA_VIDEO_SIZE].value        = arg->size;
    159 	items[SVGA_VIDEO_WIDTH].value       = arg->width;
    160 	items[SVGA_VIDEO_HEIGHT].value      = arg->height;
    161 	items[SVGA_VIDEO_SRC_X].value       = arg->src.x;
    162 	items[SVGA_VIDEO_SRC_Y].value       = arg->src.y;
    163 	items[SVGA_VIDEO_SRC_WIDTH].value   = arg->src.w;
    164 	items[SVGA_VIDEO_SRC_HEIGHT].value  = arg->src.h;
    165 	items[SVGA_VIDEO_DST_X].value       = arg->dst.x;
    166 	items[SVGA_VIDEO_DST_Y].value       = arg->dst.y;
    167 	items[SVGA_VIDEO_DST_WIDTH].value   = arg->dst.w;
    168 	items[SVGA_VIDEO_DST_HEIGHT].value  = arg->dst.h;
    169 	items[SVGA_VIDEO_PITCH_1].value     = arg->pitch[0];
    170 	items[SVGA_VIDEO_PITCH_2].value     = arg->pitch[1];
    171 	items[SVGA_VIDEO_PITCH_3].value     = arg->pitch[2];
    172 	if (have_so) {
    173 		items[SVGA_VIDEO_DATA_GMRID].value    = ptr.gmrId;
    174 		items[SVGA_VIDEO_DST_SCREEN_ID].value = SVGA_ID_INVALID;
    175 	}
    176 
    177 	fill_flush(flush, arg->stream_id);
    178 
    179 	vmw_fifo_commit(dev_priv, fifo_size);
    180 
    181 	return 0;
    182 }
    183 
    184 /**
    185  * Send stop command to hw.
    186  *
    187  * Returns
    188  * -ERESTARTSYS if interrupted by a signal.
    189  */
    190 static int vmw_overlay_send_stop(struct vmw_private *dev_priv,
    191 				 uint32_t stream_id,
    192 				 bool interruptible)
    193 {
    194 	struct {
    195 		struct vmw_escape_header escape;
    196 		SVGAEscapeVideoSetRegs body;
    197 		struct vmw_escape_video_flush flush;
    198 	} *cmds;
    199 	int ret;
    200 
    201 	for (;;) {
    202 		cmds = VMW_FIFO_RESERVE(dev_priv, sizeof(*cmds));
    203 		if (cmds)
    204 			break;
    205 
    206 		ret = vmw_fallback_wait(dev_priv, false, true, 0,
    207 					interruptible, 3*HZ);
    208 		if (interruptible && ret == -ERESTARTSYS)
    209 			return ret;
    210 		else
    211 			BUG_ON(ret != 0);
    212 	}
    213 
    214 	fill_escape(&cmds->escape, sizeof(cmds->body));
    215 	cmds->body.header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS;
    216 	cmds->body.header.streamId = stream_id;
    217 	cmds->body.items[0].registerId = SVGA_VIDEO_ENABLED;
    218 	cmds->body.items[0].value = false;
    219 	fill_flush(&cmds->flush, stream_id);
    220 
    221 	vmw_fifo_commit(dev_priv, sizeof(*cmds));
    222 
    223 	return 0;
    224 }
    225 
    226 /**
    227  * Move a buffer to vram or gmr if @pin is set, else unpin the buffer.
    228  *
    229  * With the introduction of screen objects buffers could now be
    230  * used with GMRs instead of being locked to vram.
    231  */
    232 static int vmw_overlay_move_buffer(struct vmw_private *dev_priv,
    233 				   struct vmw_buffer_object *buf,
    234 				   bool pin, bool inter)
    235 {
    236 	if (!pin)
    237 		return vmw_bo_unpin(dev_priv, buf, inter);
    238 
    239 	if (dev_priv->active_display_unit == vmw_du_legacy)
    240 		return vmw_bo_pin_in_vram(dev_priv, buf, inter);
    241 
    242 	return vmw_bo_pin_in_vram_or_gmr(dev_priv, buf, inter);
    243 }
    244 
    245 /**
    246  * Stop or pause a stream.
    247  *
    248  * If the stream is paused the no evict flag is removed from the buffer
    249  * but left in vram. This allows for instance mode_set to evict it
    250  * should it need to.
    251  *
    252  * The caller must hold the overlay lock.
    253  *
    254  * @stream_id which stream to stop/pause.
    255  * @pause true to pause, false to stop completely.
    256  */
    257 static int vmw_overlay_stop(struct vmw_private *dev_priv,
    258 			    uint32_t stream_id, bool pause,
    259 			    bool interruptible)
    260 {
    261 	struct vmw_overlay *overlay = dev_priv->overlay_priv;
    262 	struct vmw_stream *stream = &overlay->stream[stream_id];
    263 	int ret;
    264 
    265 	/* no buffer attached the stream is completely stopped */
    266 	if (!stream->buf)
    267 		return 0;
    268 
    269 	/* If the stream is paused this is already done */
    270 	if (!stream->paused) {
    271 		ret = vmw_overlay_send_stop(dev_priv, stream_id,
    272 					    interruptible);
    273 		if (ret)
    274 			return ret;
    275 
    276 		/* We just remove the NO_EVICT flag so no -ENOMEM */
    277 		ret = vmw_overlay_move_buffer(dev_priv, stream->buf, false,
    278 					      interruptible);
    279 		if (interruptible && ret == -ERESTARTSYS)
    280 			return ret;
    281 		else
    282 			BUG_ON(ret != 0);
    283 	}
    284 
    285 	if (!pause) {
    286 		vmw_bo_unreference(&stream->buf);
    287 		stream->paused = false;
    288 	} else {
    289 		stream->paused = true;
    290 	}
    291 
    292 	return 0;
    293 }
    294 
    295 /**
    296  * Update a stream and send any put or stop fifo commands needed.
    297  *
    298  * The caller must hold the overlay lock.
    299  *
    300  * Returns
    301  * -ENOMEM if buffer doesn't fit in vram.
    302  * -ERESTARTSYS if interrupted.
    303  */
    304 static int vmw_overlay_update_stream(struct vmw_private *dev_priv,
    305 				     struct vmw_buffer_object *buf,
    306 				     struct drm_vmw_control_stream_arg *arg,
    307 				     bool interruptible)
    308 {
    309 	struct vmw_overlay *overlay = dev_priv->overlay_priv;
    310 	struct vmw_stream *stream = &overlay->stream[arg->stream_id];
    311 	int ret = 0;
    312 
    313 	if (!buf)
    314 		return -EINVAL;
    315 
    316 	DRM_DEBUG("   %s: old %p, new %p, %spaused\n", __func__,
    317 		  stream->buf, buf, stream->paused ? "" : "not ");
    318 
    319 	if (stream->buf != buf) {
    320 		ret = vmw_overlay_stop(dev_priv, arg->stream_id,
    321 				       false, interruptible);
    322 		if (ret)
    323 			return ret;
    324 	} else if (!stream->paused) {
    325 		/* If the buffers match and not paused then just send
    326 		 * the put command, no need to do anything else.
    327 		 */
    328 		ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible);
    329 		if (ret == 0)
    330 			stream->saved = *arg;
    331 		else
    332 			BUG_ON(!interruptible);
    333 
    334 		return ret;
    335 	}
    336 
    337 	/* We don't start the old stream if we are interrupted.
    338 	 * Might return -ENOMEM if it can't fit the buffer in vram.
    339 	 */
    340 	ret = vmw_overlay_move_buffer(dev_priv, buf, true, interruptible);
    341 	if (ret)
    342 		return ret;
    343 
    344 	ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible);
    345 	if (ret) {
    346 		/* This one needs to happen no matter what. We only remove
    347 		 * the NO_EVICT flag so this is safe from -ENOMEM.
    348 		 */
    349 		BUG_ON(vmw_overlay_move_buffer(dev_priv, buf, false, false)
    350 		       != 0);
    351 		return ret;
    352 	}
    353 
    354 	if (stream->buf != buf)
    355 		stream->buf = vmw_bo_reference(buf);
    356 	stream->saved = *arg;
    357 	/* stream is no longer stopped/paused */
    358 	stream->paused = false;
    359 
    360 	return 0;
    361 }
    362 
    363 /**
    364  * Stop all streams.
    365  *
    366  * Used by the fb code when starting.
    367  *
    368  * Takes the overlay lock.
    369  */
    370 int vmw_overlay_stop_all(struct vmw_private *dev_priv)
    371 {
    372 	struct vmw_overlay *overlay = dev_priv->overlay_priv;
    373 	int i, ret;
    374 
    375 	if (!overlay)
    376 		return 0;
    377 
    378 	mutex_lock(&overlay->mutex);
    379 
    380 	for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
    381 		struct vmw_stream *stream = &overlay->stream[i];
    382 		if (!stream->buf)
    383 			continue;
    384 
    385 		ret = vmw_overlay_stop(dev_priv, i, false, false);
    386 		WARN_ON(ret != 0);
    387 	}
    388 
    389 	mutex_unlock(&overlay->mutex);
    390 
    391 	return 0;
    392 }
    393 
    394 /**
    395  * Try to resume all paused streams.
    396  *
    397  * Used by the kms code after moving a new scanout buffer to vram.
    398  *
    399  * Takes the overlay lock.
    400  */
    401 int vmw_overlay_resume_all(struct vmw_private *dev_priv)
    402 {
    403 	struct vmw_overlay *overlay = dev_priv->overlay_priv;
    404 	int i, ret;
    405 
    406 	if (!overlay)
    407 		return 0;
    408 
    409 	mutex_lock(&overlay->mutex);
    410 
    411 	for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
    412 		struct vmw_stream *stream = &overlay->stream[i];
    413 		if (!stream->paused)
    414 			continue;
    415 
    416 		ret = vmw_overlay_update_stream(dev_priv, stream->buf,
    417 						&stream->saved, false);
    418 		if (ret != 0)
    419 			DRM_INFO("%s: *warning* failed to resume stream %i\n",
    420 				 __func__, i);
    421 	}
    422 
    423 	mutex_unlock(&overlay->mutex);
    424 
    425 	return 0;
    426 }
    427 
    428 /**
    429  * Pauses all active streams.
    430  *
    431  * Used by the kms code when moving a new scanout buffer to vram.
    432  *
    433  * Takes the overlay lock.
    434  */
    435 int vmw_overlay_pause_all(struct vmw_private *dev_priv)
    436 {
    437 	struct vmw_overlay *overlay = dev_priv->overlay_priv;
    438 	int i, ret;
    439 
    440 	if (!overlay)
    441 		return 0;
    442 
    443 	mutex_lock(&overlay->mutex);
    444 
    445 	for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
    446 		if (overlay->stream[i].paused)
    447 			DRM_INFO("%s: *warning* stream %i already paused\n",
    448 				 __func__, i);
    449 		ret = vmw_overlay_stop(dev_priv, i, true, false);
    450 		WARN_ON(ret != 0);
    451 	}
    452 
    453 	mutex_unlock(&overlay->mutex);
    454 
    455 	return 0;
    456 }
    457 
    458 
    459 static bool vmw_overlay_available(const struct vmw_private *dev_priv)
    460 {
    461 	return (dev_priv->overlay_priv != NULL &&
    462 		((dev_priv->fifo.capabilities & VMW_OVERLAY_CAP_MASK) ==
    463 		 VMW_OVERLAY_CAP_MASK));
    464 }
    465 
    466 int vmw_overlay_ioctl(struct drm_device *dev, void *data,
    467 		      struct drm_file *file_priv)
    468 {
    469 	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
    470 	struct vmw_private *dev_priv = vmw_priv(dev);
    471 	struct vmw_overlay *overlay = dev_priv->overlay_priv;
    472 	struct drm_vmw_control_stream_arg *arg =
    473 	    (struct drm_vmw_control_stream_arg *)data;
    474 	struct vmw_buffer_object *buf;
    475 	struct vmw_resource *res;
    476 	int ret;
    477 
    478 	if (!vmw_overlay_available(dev_priv))
    479 		return -ENOSYS;
    480 
    481 	ret = vmw_user_stream_lookup(dev_priv, tfile, &arg->stream_id, &res);
    482 	if (ret)
    483 		return ret;
    484 
    485 	mutex_lock(&overlay->mutex);
    486 
    487 	if (!arg->enabled) {
    488 		ret = vmw_overlay_stop(dev_priv, arg->stream_id, false, true);
    489 		goto out_unlock;
    490 	}
    491 
    492 	ret = vmw_user_bo_lookup(tfile, arg->handle, &buf, NULL);
    493 	if (ret)
    494 		goto out_unlock;
    495 
    496 	ret = vmw_overlay_update_stream(dev_priv, buf, arg, true);
    497 
    498 	vmw_bo_unreference(&buf);
    499 
    500 out_unlock:
    501 	mutex_unlock(&overlay->mutex);
    502 	vmw_resource_unreference(&res);
    503 
    504 	return ret;
    505 }
    506 
    507 int vmw_overlay_num_overlays(struct vmw_private *dev_priv)
    508 {
    509 	if (!vmw_overlay_available(dev_priv))
    510 		return 0;
    511 
    512 	return VMW_MAX_NUM_STREAMS;
    513 }
    514 
    515 int vmw_overlay_num_free_overlays(struct vmw_private *dev_priv)
    516 {
    517 	struct vmw_overlay *overlay = dev_priv->overlay_priv;
    518 	int i, k;
    519 
    520 	if (!vmw_overlay_available(dev_priv))
    521 		return 0;
    522 
    523 	mutex_lock(&overlay->mutex);
    524 
    525 	for (i = 0, k = 0; i < VMW_MAX_NUM_STREAMS; i++)
    526 		if (!overlay->stream[i].claimed)
    527 			k++;
    528 
    529 	mutex_unlock(&overlay->mutex);
    530 
    531 	return k;
    532 }
    533 
    534 int vmw_overlay_claim(struct vmw_private *dev_priv, uint32_t *out)
    535 {
    536 	struct vmw_overlay *overlay = dev_priv->overlay_priv;
    537 	int i;
    538 
    539 	if (!overlay)
    540 		return -ENOSYS;
    541 
    542 	mutex_lock(&overlay->mutex);
    543 
    544 	for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
    545 
    546 		if (overlay->stream[i].claimed)
    547 			continue;
    548 
    549 		overlay->stream[i].claimed = true;
    550 		*out = i;
    551 		mutex_unlock(&overlay->mutex);
    552 		return 0;
    553 	}
    554 
    555 	mutex_unlock(&overlay->mutex);
    556 	return -ESRCH;
    557 }
    558 
    559 int vmw_overlay_unref(struct vmw_private *dev_priv, uint32_t stream_id)
    560 {
    561 	struct vmw_overlay *overlay = dev_priv->overlay_priv;
    562 
    563 	BUG_ON(stream_id >= VMW_MAX_NUM_STREAMS);
    564 
    565 	if (!overlay)
    566 		return -ENOSYS;
    567 
    568 	mutex_lock(&overlay->mutex);
    569 
    570 	WARN_ON(!overlay->stream[stream_id].claimed);
    571 	vmw_overlay_stop(dev_priv, stream_id, false, false);
    572 	overlay->stream[stream_id].claimed = false;
    573 
    574 	mutex_unlock(&overlay->mutex);
    575 	return 0;
    576 }
    577 
    578 int vmw_overlay_init(struct vmw_private *dev_priv)
    579 {
    580 	struct vmw_overlay *overlay;
    581 	int i;
    582 
    583 	if (dev_priv->overlay_priv)
    584 		return -EINVAL;
    585 
    586 	overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
    587 	if (!overlay)
    588 		return -ENOMEM;
    589 
    590 	mutex_init(&overlay->mutex);
    591 	for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
    592 		overlay->stream[i].buf = NULL;
    593 		overlay->stream[i].paused = false;
    594 		overlay->stream[i].claimed = false;
    595 	}
    596 
    597 	dev_priv->overlay_priv = overlay;
    598 
    599 	return 0;
    600 }
    601 
    602 int vmw_overlay_close(struct vmw_private *dev_priv)
    603 {
    604 	struct vmw_overlay *overlay = dev_priv->overlay_priv;
    605 	bool forgotten_buffer = false;
    606 	int i;
    607 
    608 	if (!overlay)
    609 		return -ENOSYS;
    610 
    611 	for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
    612 		if (overlay->stream[i].buf) {
    613 			forgotten_buffer = true;
    614 			vmw_overlay_stop(dev_priv, i, false, false);
    615 		}
    616 	}
    617 
    618 	WARN_ON(forgotten_buffer);
    619 
    620 	dev_priv->overlay_priv = NULL;
    621 	kfree(overlay);
    622 
    623 	return 0;
    624 }
    625