Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/ipc/glue/test/browser/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 491 B image not shown  

Quelle  vmwgfx_overlay.c   Sprache: unbekannt

 
// SPDX-License-Identifier: GPL-2.0 OR MIT
/**************************************************************************
 *
 * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 **************************************************************************/

#include "vmwgfx_bo.h"
#include "vmwgfx_drv.h"

#include "device_include/svga_overlay.h"
#include "device_include/svga_escape.h"

#include <drm/ttm/ttm_placement.h>

#define VMW_MAX_NUM_STREAMS 1
#define VMW_OVERLAY_CAP_MASK (SVGA_FIFO_CAP_VIDEO | SVGA_FIFO_CAP_ESCAPE)

struct vmw_stream {
 struct vmw_bo *buf;
 bool claimed;
 bool paused;
 struct drm_vmw_control_stream_arg saved;
};

/*
 * Overlay control
 */

struct vmw_overlay {
 /*
 * Each stream is a single overlay. In Xv these are called ports.
 */

 struct mutex mutex;
 struct vmw_stream stream[VMW_MAX_NUM_STREAMS];
};

struct vmw_escape_header {
 uint32_t cmd;
 SVGAFifoCmdEscape body;
};

struct vmw_escape_video_flush {
 struct vmw_escape_header escape;
 SVGAEscapeVideoFlush flush;
};

static inline void fill_escape(struct vmw_escape_header *header,
          uint32_t size)
{
 header->cmd = SVGA_CMD_ESCAPE;
 header->body.nsid = SVGA_ESCAPE_NSID_VMWARE;
 header->body.size = size;
}

static inline void fill_flush(struct vmw_escape_video_flush *cmd,
         uint32_t stream_id)
{
 fill_escape(&cmd->escape, sizeof(cmd->flush));
 cmd->flush.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_FLUSH;
 cmd->flush.streamId = stream_id;
}

/*
 * Send put command to hw.
 *
 * Returns
 * -ERESTARTSYS if interrupted by a signal.
 */

static int vmw_overlay_send_put(struct vmw_private *dev_priv,
    struct vmw_bo *buf,
    struct drm_vmw_control_stream_arg *arg,
    bool interruptible)
{
 struct vmw_escape_video_flush *flush;
 size_t fifo_size;
 bool have_so = (dev_priv->active_display_unit != vmw_du_legacy);
 int i, num_items;
 SVGAGuestPtr ptr;

 struct {
  struct vmw_escape_header escape;
  struct {
   uint32_t cmdType;
   uint32_t streamId;
  } header;
 } *cmds;
 struct {
  uint32_t registerId;
  uint32_t value;
 } *items;

 /* defines are a index needs + 1 */
 if (have_so)
  num_items = SVGA_VIDEO_DST_SCREEN_ID + 1;
 else
  num_items = SVGA_VIDEO_PITCH_3 + 1;

 fifo_size = sizeof(*cmds) + sizeof(*flush) + sizeof(*items) * num_items;

 cmds = VMW_CMD_RESERVE(dev_priv, fifo_size);
 /* hardware has hung, can't do anything here */
 if (!cmds)
  return -ENOMEM;

 items = (typeof(items))&cmds[1];
 flush = (struct vmw_escape_video_flush *)&items[num_items];

 /* the size is header + number of items */
 fill_escape(&cmds->escape, sizeof(*items) * (num_items + 1));

 cmds->header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS;
 cmds->header.streamId = arg->stream_id;

 /* the IDs are neatly numbered */
 for (i = 0; i < num_items; i++)
  items[i].registerId = i;

 vmw_bo_get_guest_ptr(&buf->tbo, &ptr);
 ptr.offset += arg->offset;

 items[SVGA_VIDEO_ENABLED].value     = true;
 items[SVGA_VIDEO_FLAGS].value       = arg->flags;
 items[SVGA_VIDEO_DATA_OFFSET].value = ptr.offset;
 items[SVGA_VIDEO_FORMAT].value      = arg->format;
 items[SVGA_VIDEO_COLORKEY].value    = arg->color_key;
 items[SVGA_VIDEO_SIZE].value        = arg->size;
 items[SVGA_VIDEO_WIDTH].value       = arg->width;
 items[SVGA_VIDEO_HEIGHT].value      = arg->height;
 items[SVGA_VIDEO_SRC_X].value       = arg->src.x;
 items[SVGA_VIDEO_SRC_Y].value       = arg->src.y;
 items[SVGA_VIDEO_SRC_WIDTH].value   = arg->src.w;
 items[SVGA_VIDEO_SRC_HEIGHT].value  = arg->src.h;
 items[SVGA_VIDEO_DST_X].value       = arg->dst.x;
 items[SVGA_VIDEO_DST_Y].value       = arg->dst.y;
 items[SVGA_VIDEO_DST_WIDTH].value   = arg->dst.w;
 items[SVGA_VIDEO_DST_HEIGHT].value  = arg->dst.h;
 items[SVGA_VIDEO_PITCH_1].value     = arg->pitch[0];
 items[SVGA_VIDEO_PITCH_2].value     = arg->pitch[1];
 items[SVGA_VIDEO_PITCH_3].value     = arg->pitch[2];
 if (have_so) {
  items[SVGA_VIDEO_DATA_GMRID].value    = ptr.gmrId;
  items[SVGA_VIDEO_DST_SCREEN_ID].value = SVGA_ID_INVALID;
 }

 fill_flush(flush, arg->stream_id);

 vmw_cmd_commit(dev_priv, fifo_size);

 return 0;
}

/*
 * Send stop command to hw.
 *
 * Returns
 * -ERESTARTSYS if interrupted by a signal.
 */

static int vmw_overlay_send_stop(struct vmw_private *dev_priv,
     uint32_t stream_id,
     bool interruptible)
{
 struct {
  struct vmw_escape_header escape;
  SVGAEscapeVideoSetRegs body;
  struct vmw_escape_video_flush flush;
 } *cmds;
 int ret;

 for (;;) {
  cmds = VMW_CMD_RESERVE(dev_priv, sizeof(*cmds));
  if (cmds)
   break;

  ret = vmw_fallback_wait(dev_priv, falsetrue, 0,
     interruptible, 3*HZ);
  if (interruptible && ret == -ERESTARTSYS)
   return ret;
  else
   BUG_ON(ret != 0);
 }

 fill_escape(&cmds->escape, sizeof(cmds->body));
 cmds->body.header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS;
 cmds->body.header.streamId = stream_id;
 cmds->body.items[0].registerId = SVGA_VIDEO_ENABLED;
 cmds->body.items[0].value = false;
 fill_flush(&cmds->flush, stream_id);

 vmw_cmd_commit(dev_priv, sizeof(*cmds));

 return 0;
}

/*
 * Move a buffer to vram or gmr if @pin is set, else unpin the buffer.
 *
 * With the introduction of screen objects buffers could now be
 * used with GMRs instead of being locked to vram.
 */

static int vmw_overlay_move_buffer(struct vmw_private *dev_priv,
       struct vmw_bo *buf,
       bool pin, bool inter)
{
 if (!pin)
  return vmw_bo_unpin(dev_priv, buf, inter);

 if (dev_priv->active_display_unit == vmw_du_legacy)
  return vmw_bo_pin_in_vram(dev_priv, buf, inter);

 return vmw_bo_pin_in_vram_or_gmr(dev_priv, buf, inter);
}

/*
 * Stop or pause a stream.
 *
 * If the stream is paused the no evict flag is removed from the buffer
 * but left in vram. This allows for instance mode_set to evict it
 * should it need to.
 *
 * The caller must hold the overlay lock.
 *
 * @stream_id which stream to stop/pause.
 * @pause true to pause, false to stop completely.
 */

static int vmw_overlay_stop(struct vmw_private *dev_priv,
       uint32_t stream_id, bool pause,
       bool interruptible)
{
 struct vmw_overlay *overlay = dev_priv->overlay_priv;
 struct vmw_stream *stream = &overlay->stream[stream_id];
 int ret;

 /* no buffer attached the stream is completely stopped */
 if (!stream->buf)
  return 0;

 /* If the stream is paused this is already done */
 if (!stream->paused) {
  ret = vmw_overlay_send_stop(dev_priv, stream_id,
         interruptible);
  if (ret)
   return ret;

  /* We just remove the NO_EVICT flag so no -ENOMEM */
  ret = vmw_overlay_move_buffer(dev_priv, stream->buf, false,
           interruptible);
  if (interruptible && ret == -ERESTARTSYS)
   return ret;
  else
   BUG_ON(ret != 0);
 }

 if (!pause) {
  vmw_bo_unreference(&stream->buf);
  stream->paused = false;
 } else {
  stream->paused = true;
 }

 return 0;
}

/*
 * Update a stream and send any put or stop fifo commands needed.
 *
 * The caller must hold the overlay lock.
 *
 * Returns
 * -ENOMEM if buffer doesn't fit in vram.
 * -ERESTARTSYS if interrupted.
 */

static int vmw_overlay_update_stream(struct vmw_private *dev_priv,
         struct vmw_bo *buf,
         struct drm_vmw_control_stream_arg *arg,
         bool interruptible)
{
 struct vmw_overlay *overlay = dev_priv->overlay_priv;
 struct vmw_stream *stream = &overlay->stream[arg->stream_id];
 int ret = 0;

 if (!buf)
  return -EINVAL;

 DRM_DEBUG(" %s: old %p, new %p, %spaused\n", __func__,
    stream->buf, buf, stream->paused ? "" : "not ");

 if (stream->buf != buf) {
  ret = vmw_overlay_stop(dev_priv, arg->stream_id,
           false, interruptible);
  if (ret)
   return ret;
 } else if (!stream->paused) {
  /* If the buffers match and not paused then just send
 * the put command, no need to do anything else.
 */

  ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible);
  if (ret == 0)
   stream->saved = *arg;
  else
   BUG_ON(!interruptible);

  return ret;
 }

 /* We don't start the old stream if we are interrupted.
 * Might return -ENOMEM if it can't fit the buffer in vram.
 */

 ret = vmw_overlay_move_buffer(dev_priv, buf, true, interruptible);
 if (ret)
  return ret;

 ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible);
 if (ret) {
  /* This one needs to happen no matter what. We only remove
 * the NO_EVICT flag so this is safe from -ENOMEM.
 */

  BUG_ON(vmw_overlay_move_buffer(dev_priv, buf, falsefalse)
         != 0);
  return ret;
 }

 if (stream->buf != buf)
  stream->buf = vmw_bo_reference(buf);
 stream->saved = *arg;
 /* stream is no longer stopped/paused */
 stream->paused = false;

 return 0;
}

/*
 * Try to resume all paused streams.
 *
 * Used by the kms code after moving a new scanout buffer to vram.
 *
 * Takes the overlay lock.
 */

int vmw_overlay_resume_all(struct vmw_private *dev_priv)
{
 struct vmw_overlay *overlay = dev_priv->overlay_priv;
 int i, ret;

 if (!overlay)
  return 0;

 mutex_lock(&overlay->mutex);

 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
  struct vmw_stream *stream = &overlay->stream[i];
  if (!stream->paused)
   continue;

  ret = vmw_overlay_update_stream(dev_priv, stream->buf,
      &stream->saved, false);
  if (ret != 0)
   DRM_INFO("%s: *warning* failed to resume stream %i\n",
     __func__, i);
 }

 mutex_unlock(&overlay->mutex);

 return 0;
}

/*
 * Pauses all active streams.
 *
 * Used by the kms code when moving a new scanout buffer to vram.
 *
 * Takes the overlay lock.
 */

int vmw_overlay_pause_all(struct vmw_private *dev_priv)
{
 struct vmw_overlay *overlay = dev_priv->overlay_priv;
 int i, ret;

 if (!overlay)
  return 0;

 mutex_lock(&overlay->mutex);

 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
  if (overlay->stream[i].paused)
   DRM_INFO("%s: *warning* stream %i already paused\n",
     __func__, i);
  ret = vmw_overlay_stop(dev_priv, i, truefalse);
  WARN_ON(ret != 0);
 }

 mutex_unlock(&overlay->mutex);

 return 0;
}


static bool vmw_overlay_available(const struct vmw_private *dev_priv)
{
 return (dev_priv->overlay_priv != NULL &&
  ((vmw_fifo_caps(dev_priv) & VMW_OVERLAY_CAP_MASK) ==
   VMW_OVERLAY_CAP_MASK));
}

int vmw_overlay_ioctl(struct drm_device *dev, void *data,
        struct drm_file *file_priv)
{
 struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
 struct vmw_private *dev_priv = vmw_priv(dev);
 struct vmw_overlay *overlay = dev_priv->overlay_priv;
 struct drm_vmw_control_stream_arg *arg =
     (struct drm_vmw_control_stream_arg *)data;
 struct vmw_bo *buf;
 struct vmw_resource *res;
 int ret;

 if (!vmw_overlay_available(dev_priv))
  return -ENOSYS;

 ret = vmw_user_stream_lookup(dev_priv, tfile, &arg->stream_id, &res);
 if (ret)
  return ret;

 mutex_lock(&overlay->mutex);

 if (!arg->enabled) {
  ret = vmw_overlay_stop(dev_priv, arg->stream_id, falsetrue);
  goto out_unlock;
 }

 ret = vmw_user_bo_lookup(file_priv, arg->handle, &buf);
 if (ret)
  goto out_unlock;

 ret = vmw_overlay_update_stream(dev_priv, buf, arg, true);

 vmw_user_bo_unref(&buf);

out_unlock:
 mutex_unlock(&overlay->mutex);
 vmw_resource_unreference(&res);

 return ret;
}

int vmw_overlay_num_overlays(struct vmw_private *dev_priv)
{
 if (!vmw_overlay_available(dev_priv))
  return 0;

 return VMW_MAX_NUM_STREAMS;
}

int vmw_overlay_num_free_overlays(struct vmw_private *dev_priv)
{
 struct vmw_overlay *overlay = dev_priv->overlay_priv;
 int i, k;

 if (!vmw_overlay_available(dev_priv))
  return 0;

 mutex_lock(&overlay->mutex);

 for (i = 0, k = 0; i < VMW_MAX_NUM_STREAMS; i++)
  if (!overlay->stream[i].claimed)
   k++;

 mutex_unlock(&overlay->mutex);

 return k;
}

int vmw_overlay_claim(struct vmw_private *dev_priv, uint32_t *out)
{
 struct vmw_overlay *overlay = dev_priv->overlay_priv;
 int i;

 if (!overlay)
  return -ENOSYS;

 mutex_lock(&overlay->mutex);

 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {

  if (overlay->stream[i].claimed)
   continue;

  overlay->stream[i].claimed = true;
  *out = i;
  mutex_unlock(&overlay->mutex);
  return 0;
 }

 mutex_unlock(&overlay->mutex);
 return -ESRCH;
}

int vmw_overlay_unref(struct vmw_private *dev_priv, uint32_t stream_id)
{
 struct vmw_overlay *overlay = dev_priv->overlay_priv;

 BUG_ON(stream_id >= VMW_MAX_NUM_STREAMS);

 if (!overlay)
  return -ENOSYS;

 mutex_lock(&overlay->mutex);

 WARN_ON(!overlay->stream[stream_id].claimed);
 vmw_overlay_stop(dev_priv, stream_id, falsefalse);
 overlay->stream[stream_id].claimed = false;

 mutex_unlock(&overlay->mutex);
 return 0;
}

int vmw_overlay_init(struct vmw_private *dev_priv)
{
 struct vmw_overlay *overlay;
 int i;

 if (dev_priv->overlay_priv)
  return -EINVAL;

 overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
 if (!overlay)
  return -ENOMEM;

 mutex_init(&overlay->mutex);
 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
  overlay->stream[i].buf = NULL;
  overlay->stream[i].paused = false;
  overlay->stream[i].claimed = false;
 }

 dev_priv->overlay_priv = overlay;

 return 0;
}

int vmw_overlay_close(struct vmw_private *dev_priv)
{
 struct vmw_overlay *overlay = dev_priv->overlay_priv;
 bool forgotten_buffer = false;
 int i;

 if (!overlay)
  return -ENOSYS;

 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
  if (overlay->stream[i].buf) {
   forgotten_buffer = true;
   vmw_overlay_stop(dev_priv, i, falsefalse);
  }
 }

 WARN_ON(forgotten_buffer);

 dev_priv->overlay_priv = NULL;
 kfree(overlay);

 return 0;
}

Messung V0.5
C=92 H=92 G=91

[ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ]