From bfd8398dde2d2de238c600eece9374d24d7d8549 Mon Sep 17 00:00:00 2001 From: Bankim Bhavsar Date: Wed, 23 Jan 2008 22:13:07 -0800 Subject: [PATCH] Xv extension for VMware's video driver This patch implements the Xv extension for VMware's X video driver. The Xv specification can be found here http://www.xfree86.org/current/DESIGN16.html I've written a trivial offscreen memory manager that allocates memory from the bottom part of the Video RAM and it can handle only 1 video-stream. Eventually we intend to support upto 32 video-streams (there is already support for multiple video streams in respective backends). --- README | 7 + configure.ac | 1 + src/Makefile.am | 4 +- src/svga_escape.h | 30 ++ src/svga_overlay.h | 49 ++ src/svga_reg.h | 354 ++++++++++++- src/vmware.c | 15 + src/vmware.h | 20 + src/vmwarevideo.c | 1218 ++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 1680 insertions(+), 18 deletions(-) create mode 100644 src/svga_escape.h create mode 100644 src/svga_overlay.h create mode 100644 src/vmwarevideo.c diff --git a/README b/README index 564d57b..0ddbbac 100644 --- a/README +++ b/README @@ -23,6 +23,12 @@ svga_limits.h svga_modes.h A list of default display modes that are built into the driver. +svga_overlay.h + A list of definitions required for Xv extension support. Included by vmwarevideo.c + +svga_escape.h + A list of definitions for the SVGA Escape commands. + guest_os.h Values for the GUEST_ID register. @@ -262,6 +268,7 @@ table shows which capability indicates support for which command. SVGA_CMD_DEFINE_ALPHA_CURSOR SVGA_CAP_ALPHA_CURSOR SVGA_CMD_DRAW_GLYPH SVGA_CAP_GLYPH SVGA_CMD_DRAW_GLYPH_CLIPPED SVGA_CAP_GLYPH_CLIPPING + SVGA_CMD_ESCAPE SVGA_FIFO_CAP_ESCAPE Note: SVGA_CMD_DISPLAY_CURSOR and SVGA_CMD_MOVE_CURSOR should not be used. Drivers wishing hardware cursor support should use cursor bypass (see below). diff --git a/configure.ac b/configure.ac index f33841b..91b01dc 100644 --- a/configure.ac +++ b/configure.ac @@ -52,6 +52,7 @@ XORG_DRIVER_CHECK_EXT(RANDR, randrproto) XORG_DRIVER_CHECK_EXT(RENDER, renderproto) XORG_DRIVER_CHECK_EXT(DPMSExtension, xextproto) XORG_DRIVER_CHECK_EXT(XINERAMA, xineramaproto) +XORG_DRIVER_CHECK_EXT(XV, videoproto) # Checks for pkg-config packages PKG_CHECK_MODULES(XORG, [xorg-server >= 1.0.99.901 xproto fontsproto $REQUIRED_MODULES]) diff --git a/src/Makefile.am b/src/Makefile.am index d6000fc..0cd746d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -45,4 +45,6 @@ vmware_drv_la_SOURCES = \ vmwarectrl.c \ vmwarectrl.h \ vmwarectrlproto.h \ - vmwarexinerama.c + vmwarexinerama.c \ + vmwarevideo.c + diff --git a/src/svga_escape.h b/src/svga_escape.h new file mode 100644 index 0000000..c9e7b23 --- /dev/null +++ b/src/svga_escape.h @@ -0,0 +1,30 @@ +/* ********************************************************** + * Copyright 2007 VMware, Inc. All rights reserved. + * **********************************************************/ + +/* + * svga_escape.h -- + * + * Definitions for our own (vendor-specific) SVGA Escape commands. + */ + +#ifndef _SVGA_ESCAPE_H_ +#define _SVGA_ESCAPE_H_ + +/* + * Namespace IDs for the escape command + */ + +#define SVGA_ESCAPE_NSID_VMWARE 0x00000000 +#define SVGA_ESCAPE_NSID_DEVEL 0xFFFFFFFF + +/* + * Within SVGA_ESCAPE_NSID_VMWARE, we multiplex commands according to + * the first DWORD of escape data (after the nsID and size). As a + * guideline we're using the high word and low word as a major and + * minor command number, respectively. + */ + +#define SVGA_ESCAPE_VMWARE_MAJOR_MASK 0xFFFF0000 + +#endif /* _SVGA_ESCAPE_H_ */ diff --git a/src/svga_overlay.h b/src/svga_overlay.h new file mode 100644 index 0000000..58f71e4 --- /dev/null +++ b/src/svga_overlay.h @@ -0,0 +1,49 @@ +/* ********************************************************** + * Copyright 2007 VMware, Inc. All rights reserved. + * **********************************************************/ + +/* + * svga_overlay.h -- + * + * Definitions for video-overlay support. + */ + +#ifndef _SVGA_OVERLAY_H_ +#define _SVGA_OVERLAY_H_ + +/* + * Video formats we support + */ + +#define VMWARE_FOURCC_YV12 0x32315659 // 'Y' 'V' '1' '2' +#define VMWARE_FOURCC_YUY2 0x32595559 // 'Y' 'U' 'Y' '2' + +#define SVGA_ESCAPE_VMWARE_VIDEO 0x00020000 + +#define SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS 0x00020001 + /* FIFO escape layout: + * Type, Stream Id, (Register Id, Value) pairs */ + +#define SVGA_ESCAPE_VMWARE_VIDEO_FLUSH 0x00020002 + /* FIFO escape layout: + * Type, Stream Id */ + +typedef struct SVGAEscapeVideoSetRegs { + struct { + uint32 cmdType; + uint32 streamId; + } header; + + // May include zero or more items. + struct { + uint32 registerId; + uint32 value; + } items[1]; +} SVGAEscapeVideoSetRegs; + +typedef struct SVGAEscapeVideoFlush { + uint32 cmdType; + uint32 streamId; +} SVGAEscapeVideoFlush; + +#endif // _SVGA_OVERLAY_H_ diff --git a/src/svga_reg.h b/src/svga_reg.h index 871a8ff..4560fe7 100644 --- a/src/svga_reg.h +++ b/src/svga_reg.h @@ -1,7 +1,5 @@ /* ********************************************************** - * Copyright (C) 1998-2001 VMware, Inc. - * All Rights Reserved - * $Id$ + * Copyright 1998 VMware, Inc. All rights reserved. * **********************************************************/ /* @@ -9,13 +7,13 @@ * * SVGA hardware definitions */ -/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/vmware/svga_reg.h,v 1.7 2002/12/10 04:17:19 dawes Exp $ */ #ifndef _SVGA_REG_H_ #define _SVGA_REG_H_ #define INCLUDE_ALLOW_USERLEVEL #define INCLUDE_ALLOW_VMMEXT +#define INCLUDE_ALLOW_VMCORE #include "includeCheck.h" /* @@ -30,7 +28,17 @@ #define SVGA_MAX_HEIGHT 1770 #define SVGA_MAX_BITS_PER_PIXEL 32 #define SVGA_MAX_DEPTH 24 +#define SVGA_MAX_DISPLAYS 10 +/* + * The maximum size of the onscreen framebuffer. The size of the + * changeMap in the monitor is proportional to this number since it's + * the amount of memory we need to trace in VESA mode. Therefore, we'd + * like to keep it as small as possible to reduce monitor overhead (using + * SVGA_VRAM_MAX_SIZE for this increases the size of the shared area + * by over 4k!). + */ + #define SVGA_FB_MAX_SIZE \ ((((SVGA_MAX_WIDTH * SVGA_MAX_HEIGHT * \ SVGA_MAX_BITS_PER_PIXEL / 8) >> PAGE_SHIFT) + 1) << PAGE_SHIFT) @@ -67,6 +75,17 @@ #define SVGA_VALUE_PORT 0x1 #define SVGA_BIOS_PORT 0x2 #define SVGA_NUM_PORTS 0x3 +#define SVGA_IRQSTATUS_PORT 0x8 + +/* + * Interrupt source flags for IRQSTATUS_PORT and IRQMASK. + * + * Interrupts are only supported when the + * SVGA_CAP_IRQMASK capability is present. + */ +#define SVGA_IRQFLAG_ANY_FENCE 0x1 /* Any fence was passed */ +#define SVGA_IRQFLAG_FIFO_PROGRESS 0x2 /* Made forward progress in the FIFO */ +#define SVGA_IRQFLAG_FENCE_GOAL 0x4 /* SVGA_FIFO_FENCE_GOAL reached */ /* This port is deprecated, but retained because of old drivers. */ #define SVGA_LEGACY_ACCEL_PORT 0x3 @@ -106,8 +125,8 @@ enum { SVGA_REG_MEM_START = 18, /* Memory for command FIFO and bitmaps */ SVGA_REG_MEM_SIZE = 19, SVGA_REG_CONFIG_DONE = 20, /* Set when memory area configured */ - SVGA_REG_SYNC = 21, /* Write to force synchronization */ - SVGA_REG_BUSY = 22, /* Read to check if sync is done */ + SVGA_REG_SYNC = 21, /* See "FIFO Synchronization Registers" */ + SVGA_REG_BUSY = 22, /* See "FIFO Synchronization Registers" */ SVGA_REG_GUEST_ID = 23, /* Set guest OS identifier */ SVGA_REG_CURSOR_ID = 24, /* ID of cursor */ SVGA_REG_CURSOR_X = 25, /* Set cursor X position */ @@ -118,7 +137,8 @@ enum { SVGA_REG_MEM_REGS = 30, /* Number of FIFO registers */ SVGA_REG_NUM_DISPLAYS = 31, /* Number of guest displays */ SVGA_REG_PITCHLOCK = 32, /* Fixed pitch for all modes */ - SVGA_REG_TOP = 33, /* Must be 1 more than the last register */ + SVGA_REG_IRQMASK = 33, /* Interrupt mask */ + SVGA_REG_TOP = 34, /* Must be 1 more than the last register */ SVGA_PALETTE_BASE = 1024, /* Base of SVGA color map */ /* Next 768 (== 256*3) registers exist for colormap */ @@ -153,6 +173,7 @@ enum { #define SVGA_CAP_EXTENDED_FIFO 0x08000 #define SVGA_CAP_MULTIMON 0x10000 #define SVGA_CAP_PITCHLOCK 0x20000 +#define SVGA_CAP_IRQMASK 0x40000 /* * Raster op codes (same encoding as X) used by FIFO drivers. @@ -247,12 +268,24 @@ enum { /* - * FIFO offsets (viewed as an array of 32-bit words) + * FIFO register indices. + * + * The FIFO is a chunk of device memory mapped into guest physmem. It + * is always treated as 32-bit words. + * + * The guest driver gets to decide how to partition it between + * - FIFO registers (there are always at least 4, specifying where the + * following data area is and how much data it contains; there may be + * more registers following these, depending on the FIFO protocol + * version in use) + * - FIFO data, written by the guest and slurped out by the VMX. + * These indices are 32-bit word offsets into the FIFO. */ enum { /* - * The original defined FIFO offsets + * Block 1 (basic registers): The originally defined FIFO registers. + * These exist and are valid for all versions of the FIFO protocol. */ SVGA_FIFO_MIN = 0, @@ -261,35 +294,244 @@ enum { SVGA_FIFO_STOP, /* - * Additional offsets added as of SVGA_CAP_EXTENDED_FIFO + * Block 2 (extended registers): Mandatory registers for the extended + * FIFO. These exist if the SVGA caps register includes + * SVGA_CAP_EXTENDED_FIFO; some of them are valid only if their + * associated capability bit is enabled. + * + * Note that when originally defined, SVGA_CAP_EXTENDED_FIFO implied + * support only for (FIFO registers) CAPABILITIES, FLAGS, and FENCE. + * This means that the guest has to test individually (in most cases + * using FIFO caps) for the presence of registers after this; the VMX + * can define "extended FIFO" to mean whatever it wants, and currently + * won't enable it unless there's room for that set and much more. */ SVGA_FIFO_CAPABILITIES = 4, SVGA_FIFO_FLAGS, + // Valid with SVGA_FIFO_CAP_FENCE: SVGA_FIFO_FENCE, - SVGA_FIFO_3D_HWVERSION, /* Check SVGA3dHardwareVersion in svga3d_reg.h */ - SVGA_FIFO_PITCHLOCK, /* - * Always keep this last. It's not an offset with semantic value, but - * rather a convenient way to produce the value of fifo[SVGA_FIFO_NUM_REGS] + * Block 3a (optional extended registers): Additional registers for the + * extended FIFO, whose presence isn't actually implied by + * SVGA_CAP_EXTENDED_FIFO; these exist if SVGA_FIFO_MIN is high enough to + * leave room for them. + * + * These in block 3a, the VMX currently considers mandatory for the + * extended FIFO. + */ + + // Valid if exists (i.e. if extended FIFO enabled): + SVGA_FIFO_3D_HWVERSION, /* See SVGA3dHardwareVersion in svga3d_reg.h */ + // Valid with SVGA_FIFO_CAP_PITCHLOCK: + SVGA_FIFO_PITCHLOCK, + // Valid with SVGA_FIFO_CAP_CURSOR_BYPASS_3: + SVGA_FIFO_CURSOR_ON, /* Cursor bypass 3 show/hide register */ + SVGA_FIFO_CURSOR_X, /* Cursor bypass 3 x register */ + SVGA_FIFO_CURSOR_Y, /* Cursor bypass 3 y register */ + SVGA_FIFO_CURSOR_COUNT, /* Incremented when any of the other 3 change */ + SVGA_FIFO_CURSOR_LAST_UPDATED,/* Last time the host updated the cursor */ + // Valid with SVGA_FIFO_CAP_RESERVE: + SVGA_FIFO_RESERVED, /* Bytes past NEXT_CMD with real contents */ + /* + * XXX: The gap here, up until SVGA_FIFO_3D_CAPS, can be used for new + * registers, but this must be done carefully and with judicious use of + * capability bits, since comparisons based on SVGA_FIFO_MIN aren't + * enough to tell you whether the register exists: we've shipped drivers + * and products that used SVGA_FIFO_3D_CAPS but didn't know about some of + * the earlier ones. The actual order of introduction was: + * - PITCHLOCK + * - 3D_CAPS + * - CURSOR_* (cursor bypass 3) + * - RESERVED + * So, code that wants to know whether it can use any of the + * aforementioned registers, or anything else added after PITCHLOCK and + * before 3D_CAPS, needs to reason about something other than + * SVGA_FIFO_MIN. + */ + /* + * 3D caps block space; valid with 3D hardware version >= + * SVGA3D_HWVERSION_WS6_B1. + */ + SVGA_FIFO_3D_CAPS = 32, + SVGA_FIFO_3D_CAPS_LAST = 32 + 255, + + /* + * End of VMX's current definition of "extended-FIFO registers". + * Registers before here are always enabled/disabled as a block; either + * the extended FIFO is enabled and includes all preceding registers, or + * it's disabled entirely. + * + * Block 3b (truly optional extended registers): Additional registers for + * the extended FIFO, which the VMX already knows how to enable and + * disable with correct granularity. + * + * Registers after here exist if and only if the guest SVGA driver + * sets SVGA_FIFO_MIN high enough to leave room for them. */ - SVGA_FIFO_NUM_REGS + // Valid if register exists: + SVGA_FIFO_GUEST_3D_HWVERSION, /* Guest driver's 3D version */ + SVGA_FIFO_FENCE_GOAL, /* Matching target for SVGA_IRQFLAG_FENCE_GOAL */ + SVGA_FIFO_BUSY, /* See "FIFO Synchronization Registers" */ + + /* + * Always keep this last. This defines the maximum number of + * registers we know about. At power-on, this value is placed in + * the SVGA_REG_MEM_REGS register, and we expect the guest driver + * to allocate this much space in FIFO memory for registers. + */ + SVGA_FIFO_NUM_REGS }; + +/* + * Definition of registers included in extended FIFO support. + * + * The guest SVGA driver gets to allocate the FIFO between registers + * and data. It must always allocate at least 4 registers, but old + * drivers stopped there. + * + * The VMX will enable extended FIFO support if and only if the guest + * left enough room for all registers defined as part of the mandatory + * set for the extended FIFO. + * + * Note that the guest drivers typically allocate the FIFO only at + * initialization time, not at mode switches, so it's likely that the + * number of FIFO registers won't change without a reboot. + * + * All registers less than this value are guaranteed to be present if + * svgaUser->fifo.extended is set. Any later registers must be tested + * individually for compatibility at each use (in the VMX). + * + * This value is used only by the VMX, so it can change without + * affecting driver compatibility; keep it that way? + */ +#define SVGA_FIFO_EXTENDED_MANDATORY_REGS (SVGA_FIFO_3D_CAPS_LAST + 1) + + +/* + * FIFO Synchronization Registers + * + * This explains the relationship between the various FIFO + * sync-related registers in IOSpace and in FIFO space. + * + * SVGA_REG_SYNC -- + * + * The SYNC register can be used in two different ways by the guest: + * + * 1. If the guest wishes to fully sync (drain) the FIFO, + * it will write once to SYNC then poll on the BUSY + * register. The FIFO is sync'ed once BUSY is zero. + * + * 2. If the guest wants to asynchronously wake up the host, + * it will write once to SYNC without polling on BUSY. + * Ideally it will do this after some new commands have + * been placed in the FIFO, and after reading a zero + * from SVGA_FIFO_BUSY. + * + * (1) is the original behaviour that SYNC was designed to + * support. Originally, a write to SYNC would implicitly + * trigger a read from BUSY. This causes us to synchronously + * process the FIFO. + * + * This behaviour has since been changed so that writing SYNC + * will *not* implicitly cause a read from BUSY. Instead, it + * makes a channel call which asynchronously wakes up the MKS + * thread. + * + * New guests can use this new behaviour to implement (2) + * efficiently. This lets guests get the host's attention + * without waiting for the MKS to poll, which gives us much + * better CPU utilization on SMP hosts and on UP hosts while + * we're blocked on the host GPU. + * + * Old guests shouldn't notice the behaviour change. SYNC was + * never guaranteed to process the entire FIFO, since it was + * bounded to a particular number of CPU cycles. Old guests will + * still loop on the BUSY register until the FIFO is empty. + * + * Writing to SYNC currently has the following side-effects: + * + * - Sets SVGA_REG_BUSY to TRUE (in the monitor) + * - Asynchronously wakes up the MKS thread for FIFO processing + * - The value written to SYNC is recorded as a "reason", for + * stats purposes. + * + * If SVGA_FIFO_BUSY is available, drivers are advised to only + * write to SYNC if SVGA_FIFO_BUSY is FALSE. Drivers should set + * SVGA_FIFO_BUSY to TRUE after writing to SYNC. The MKS will + * eventually set SVGA_FIFO_BUSY on its own, but this approach + * lets the driver avoid sending multiple asynchronous wakeup + * messages to the MKS thread. + * + * SVGA_REG_BUSY -- + * + * This register is set to TRUE when SVGA_REG_SYNC is written, + * and it reads as FALSE when the FIFO has been completely + * drained. + * + * Every read from this register causes us to synchronously + * process FIFO commands. There is no guarantee as to how many + * commands each read will process. + * + * CPU time spent processing FIFO commands will be billed to + * the guest. + * + * New drivers should avoid using this register unless they + * need to guarantee that the FIFO is completely drained. It + * is overkill for performing a sync-to-fence. Older drivers + * will use this register for any type of synchronization. + * + * SVGA_FIFO_BUSY -- + * + * This register is a fast way for the guest driver to check + * whether the FIFO is already being processed. It reads and + * writes at normal RAM speeds, with no monitor intervention. + * + * If this register reads as TRUE, the host is guaranteeing that + * any new commands written into the FIFO will be noticed before + * the MKS goes back to sleep. + * + * If this register reads as FALSE, no such guarantee can be + * made. + * + * The guest should use this register to quickly determine + * whether or not it needs to wake up the host. If the guest + * just wrote a command or group of commands that it would like + * the host to begin processing, it should: + * + * 1. Read SVGA_FIFO_BUSY. If it reads as TRUE, no further + * action is necessary. + * + * 2. Write TRUE to SVGA_FIFO_BUSY. This informs future guest + * code that we've already sent a SYNC to the host and we + * don't need to send a duplicate. + * + * 3. Write a reason to SVGA_REG_SYNC. This will send an + * asynchronous wakeup to the MKS thread. + */ + + /* * FIFO Capabilities * * Fence -- Fence register and command are supported * Accel Front -- Front buffer only commands are supported * Pitch Lock -- Pitch lock register is supported + * Video -- SVGA Video overlay units are supported + * Escape -- Escape command is supported */ #define SVGA_FIFO_CAP_NONE 0 #define SVGA_FIFO_CAP_FENCE (1<<0) #define SVGA_FIFO_CAP_ACCELFRONT (1<<1) #define SVGA_FIFO_CAP_PITCHLOCK (1<<2) +#define SVGA_FIFO_CAP_VIDEO (1<<3) +#define SVGA_FIFO_CAP_CURSOR_BYPASS_3 (1<<4) +#define SVGA_FIFO_CAP_ESCAPE (1<<5) +#define SVGA_FIFO_CAP_RESERVE (1<<6) /* @@ -300,6 +542,72 @@ enum { #define SVGA_FIFO_FLAG_NONE 0 #define SVGA_FIFO_FLAG_ACCELFRONT (1<<0) +#define SVGA_FIFO_FLAG_RESERVED (1<<31) // Internal use only + +/* + * FIFO reservation sentinel value + */ + +#define SVGA_FIFO_RESERVED_UNKNOWN 0xffffffff + + +/* + * Video overlay support + */ + +#define SVGA_NUM_OVERLAY_UNITS 32 + + +/* + * Video capabilities that the guest is currently using + */ + +#define SVGA_VIDEO_FLAG_COLORKEY 0x0001 + + +/* + * Offsets for the video overlay registers + */ + +enum { + SVGA_VIDEO_ENABLED = 0, + SVGA_VIDEO_FLAGS, + SVGA_VIDEO_DATA_OFFSET, + SVGA_VIDEO_FORMAT, + SVGA_VIDEO_COLORKEY, + SVGA_VIDEO_SIZE, + SVGA_VIDEO_X, + SVGA_VIDEO_Y, + SVGA_VIDEO_SRC_WIDTH, + SVGA_VIDEO_SRC_HEIGHT, + SVGA_VIDEO_DST_WIDTH, + SVGA_VIDEO_DST_HEIGHT, + SVGA_VIDEO_PITCH_1, + SVGA_VIDEO_PITCH_2, + SVGA_VIDEO_PITCH_3, + SVGA_VIDEO_NUM_REGS +}; + + +/* + * SVGA Overlay Units + */ + +typedef struct SVGAOverlayUnit { + uint32 enabled; + uint32 flags; + uint32 dataOffset; + uint32 format; + uint32 colorKey; + uint32 size; + uint32 x; + uint32 y; + uint32 srcWidth; + uint32 srcHeight; + uint32 dstWidth; + uint32 dstHeight; + uint32 pitches[3]; +} SVGAOverlayUnit; /* @@ -319,6 +627,7 @@ enum { #define SVGA_PIXMAP_SCANLINE_SIZE(w,bpp) (( ((w)*(bpp))+31 ) >> 5) #define SVGA_GLYPH_SIZE(w,h) ((((((w) + 7) >> 3) * (h)) + 3) >> 2) #define SVGA_GLYPH_SCANLINE_SIZE(w) (((w) + 7) >> 3) +#define SVGA_ESCAPE_SIZE(s) (((s) + 3) >> 2) /* * Increment from one scanline to the next of a bitmap or pixmap @@ -468,14 +777,25 @@ enum { /* FIFO layout: Fence value */ -#define SVGA_CMD_MAX 31 +#define SVGA_CMD_VIDEO_PLAY_OBSOLETE 31 + /* Obsolete; do not use. */ + +#define SVGA_CMD_VIDEO_END_OBSOLETE 32 + /* Obsolete; do not use. */ + +#define SVGA_CMD_ESCAPE 33 + /* FIFO layout: + Namespace ID, size(bytes), data */ + +#define SVGA_CMD_MAX 34 #define SVGA_CMD_MAX_ARGS 64 /* * Location and size of SVGA frame buffer and the FIFO. */ -#define SVGA_VRAM_MAX_SIZE (16 * 1024 * 1024) +#define SVGA_VRAM_MIN_SIZE (4 * 640 * 480) // bytes +#define SVGA_VRAM_MAX_SIZE (128 * 1024 * 1024) #define SVGA_VRAM_SIZE_WS (16 * 1024 * 1024) // 16 MB #define SVGA_MEM_SIZE_WS (2 * 1024 * 1024) // 2 MB diff --git a/src/vmware.c b/src/vmware.c index d71e48e..3ea425e 100644 --- a/src/vmware.c +++ b/src/vmware.c @@ -1176,6 +1176,8 @@ VMWAREModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode, Bool rebuildPixmap) VmwareLog(("fbSize: %u\n", pVMWARE->FbSize)); VmwareLog(("New dispWidth: %u\n", pScrn->displayWidth)); + vmwareCheckVideoSanity(pScrn); + if (rebuildPixmap) { pScrn->pScreen->ModifyPixmapHeader((*pScrn->pScreen->GetScreenPixmap)(pScrn->pScreen), pScrn->pScreen->width, @@ -1303,6 +1305,10 @@ VMWARECloseScreen(int scrnIndex, ScreenPtr pScreen) VmwareLog(("cursorSema: %d\n", pVMWARE->cursorSema)); if (*pVMWARE->pvtSema) { + if (pVMWARE->videoStreams) { + vmwareVideoEnd(pScreen); + } + if (pVMWARE->CursorInfoRec) { vmwareCursorCloseScreen(pScreen); } @@ -1688,6 +1694,15 @@ VMWAREScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv) xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options); } + /* Initialize Xv extension */ + pVMWARE->videoStreams = NULL; + if (vmwareVideoEnabled(pVMWARE)) { + if (!vmwareVideoInit(pScreen)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Xv initialization failed\n"); + } + } + + /* Done */ return TRUE; } diff --git a/src/vmware.h b/src/vmware.h index 527e3fa..7edefd5 100644 --- a/src/vmware.h +++ b/src/vmware.h @@ -142,6 +142,11 @@ typedef struct { VMWAREXineramaPtr xineramaNextState; unsigned int xineramaNextNumOutputs; + /* + * Xv + */ + DevUnion *videoStreams; + } VMWARERec, *VMWAREPtr; #define VMWAREPTR(p) ((VMWAREPtr)((p)->driverPrivate)) @@ -263,4 +268,19 @@ void VMwareCtrl_ExtInit(ScrnInfoPtr pScrn); /* vmwarexinerama.c */ void VMwareXinerama_ExtInit(ScrnInfoPtr pScrn); +/* vmwarevideo.c */ +Bool vmwareInitVideo( + ScreenPtr pScreen + ); +void vmwareVideoEnd( + ScreenPtr pScreen + ); +Bool vmwareVideoEnabled( + VMWAREPtr pVMWARE + ); + +void vmwareCheckVideoSanity( + ScrnInfoPtr pScrn + ); + #endif diff --git a/src/vmwarevideo.c b/src/vmwarevideo.c new file mode 100644 index 0000000..6c081c8 --- /dev/null +++ b/src/vmwarevideo.c @@ -0,0 +1,1218 @@ +/* + * Copyright 2007 by VMware, Inc. + * + * 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, sublicense, + * 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 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 NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Except as contained in this notice, the name of the copyright holder(s) + * and author(s) shall not be used in advertising or otherwise to promote + * the sale, use or other dealings in this Software without prior written + * authorization from the copyright holder(s) and author(s). + */ + +/* + * vmwarevideo.c -- + * + * Xv extension support. + * See http://www.xfree86.org/current/DESIGN16.html + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "vmware.h" +#include "xf86xv.h" +#include "fourcc.h" +#include "svga_escape.h" +#include "svga_overlay.h" + +#include +/* + * Need this to figure out which prototype to use for XvPutImage + */ +#include "xorgVersion.h" + +#define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, TRUE) + +/* + * Used to pack structs + */ +#define PACKED __attribute__((__packed__)) + +/* + * Number of videos that can be played simultaneously + */ +#define VMWARE_VID_NUM_PORTS 1 + +/* + * Using green as the default colorKey + */ +#define VMWARE_VIDEO_COLORKEY 0x00ff00 + +/* + * Maximum dimensions + */ +#define VMWARE_VID_MAX_WIDTH 2048 +#define VMWARE_VID_MAX_HEIGHT 2048 + +#define VMWARE_VID_NUM_ENCODINGS 1 +static XF86VideoEncodingRec vmwareVideoEncodings[] = +{ + { + 0, + "XV_IMAGE", + VMWARE_VID_MAX_WIDTH, VMWARE_VID_MAX_HEIGHT, + {1, 1} + } +}; + +#define VMWARE_VID_NUM_FORMATS 2 +static XF86VideoFormatRec vmwareVideoFormats[] = +{ + { 16, TrueColor}, + { 24, TrueColor} +}; + +#define VMWARE_VID_NUM_IMAGES 2 +static XF86ImageRec vmwareVideoImages[] = +{ + XVIMAGE_YV12, + XVIMAGE_YUY2 +}; + +#define VMWARE_VID_NUM_ATTRIBUTES 1 +static XF86AttributeRec vmwareVideoAttributes[] = +{ + { + XvGettable | XvSettable, + 0x000000, + 0xffffff, + "XV_COLORKEY" + } +}; + +/* + * Video frames are stored in a circular list of buffers. + */ +#define VMWARE_VID_NUM_BUFFERS 1 +/* + * Defines the structure used to hold and pass video data to the host + */ +typedef struct { + uint32 dataOffset; + pointer data; +} VMWAREVideoBuffer; + +typedef struct { + uint32 size; + uint32 offset; +} VMWAREOffscreenRec, *VMWAREOffscreenPtr; + +/* + * Trivial offscreen manager that allocates memory from the + * bottom of the VRAM. + */ +static VMWAREOffscreenRec offscreenMgr; + +/* + * structs that reside in fmt_priv. + */ +typedef struct { + int pitches[3]; + int offsets[3]; +} VMWAREVideoFmtData; + +/* + * Structure representing a specific video stream. + */ +struct VMWAREVideoRec { + uint32 streamId; + /* + * Function prototype same as XvPutImage. + */ + int (*play)(ScrnInfoPtr, struct VMWAREVideoRec *, + short, short, short, short, short, + short, short, short, int, unsigned char*, + short, short); + /* + * Offscreen memory region used to pass video data to the host. + */ + VMWAREOffscreenPtr fbarea; + VMWAREVideoBuffer bufs[VMWARE_VID_NUM_BUFFERS]; + uint8 currBuf; + uint32 size; + uint32 colorKey; + uint32 flags; + VMWAREVideoFmtData *fmt_priv; +}; + +typedef struct VMWAREVideoRec VMWAREVideoRec; +typedef VMWAREVideoRec *VMWAREVideoPtr; + +/* + * Callback functions + */ +#if XORG_VERSION_CURRENT > XORG_VERSION_NUMERIC(7, 0, 0, 0, 0) || XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(4, 0, 0, 0, 0) +static int vmwareXvPutImage(ScrnInfoPtr pScrn, short src_x, short src_y, + short drw_x, short drw_y, short src_w, short src_h, + short drw_w, short drw_h, int image, + unsigned char *buf, short width, short height, + Bool sync, RegionPtr clipBoxes, pointer data, + DrawablePtr dst); +#else +static int vmwareXvPutImage(ScrnInfoPtr pScrn, short src_x, short src_y, + short drw_x, short drw_y, short src_w, short src_h, + short drw_w, short drw_h, int image, + unsigned char *buf, short width, short height, + Bool sync, RegionPtr clipBoxes, pointer data); +#endif +static void vmwareStopVideo(ScrnInfoPtr pScrn, pointer data, Bool Cleanup); +static int vmwareQueryImageAttributes(ScrnInfoPtr pScrn, int format, + unsigned short *width, + unsigned short *height, int *pitches, + int *offsets); +static int vmwareSetPortAttribute(ScrnInfoPtr pScrn, Atom attribute, + INT32 value, pointer data); +static int vmwareGetPortAttribute(ScrnInfoPtr pScrn, Atom attribute, + INT32 *value, pointer data); +static void vmwareQueryBestSize(ScrnInfoPtr pScrn, Bool motion, + short vid_w, short vid_h, short drw_w, + short drw_h, unsigned int *p_w, + unsigned int *p_h, pointer data); + +/* + * Local functions for video streams + */ +static XF86VideoAdaptorPtr vmwareVideoSetup(ScrnInfoPtr pScrn); +static int vmwareVideoInitStream(ScrnInfoPtr pScrn, VMWAREVideoPtr pVid, + short src_x, short src_y, short drw_x, + short drw_y, short src_w, short src_h, + short drw_w, short drw_h, int format, + unsigned char *buf, short width, short height); +static int vmwareVideoInitAttributes(ScrnInfoPtr pScrn, VMWAREVideoPtr pVid, + int format, unsigned short width, + unsigned short height); +static int vmwareVideoPlay(ScrnInfoPtr pScrn, VMWAREVideoPtr pVid, + short src_x, short src_y, short drw_x, + short drw_y, short src_w, short src_h, + short drw_w, short drw_h, int format, + unsigned char *buf, short width, + short height); +static void vmwareVideoFlush(VMWAREPtr pVMWARE, uint32 streamId); +static void vmwareVideoSetOneReg(VMWAREPtr pVMWARE, uint32 streamId, + uint32 regId, uint32 value); +static void vmwareVideoEndStream(ScrnInfoPtr pScrn, VMWAREVideoPtr pVid); + +/* + * Offscreen memory manager functions + */ +static void vmwareOffscreenInit(); +static VMWAREOffscreenPtr vmwareOffscreenAllocate(VMWAREPtr pVMWARE, + uint32 size); +static void vmwareOffscreenFree(VMWAREOffscreenPtr memptr); + + +/* + *----------------------------------------------------------------------------- + * + * vmwareCheckVideoSanity -- + * + * Ensures that on ModeSwitch the offscreen memory used + * by the Xv streams doesn't become part of the guest framebuffer. + * + * Results: + * None + * + * Side effects: + * If it is found that the offscreen used by video streams lies + * within the range of the framebuffer(after ModeSwitch) then the video + * streams will be stopped. + * + *----------------------------------------------------------------------------- + */ + +void +vmwareCheckVideoSanity(ScrnInfoPtr pScrn) +{ + VMWAREPtr pVMWARE = VMWAREPTR(pScrn); + VMWAREVideoPtr pVid; + + if (offscreenMgr.size == 0 || + offscreenMgr.offset > pVMWARE->FbSize) { + return ; + } + + pVid = (VMWAREVideoPtr) &pVMWARE->videoStreams[VMWARE_VID_NUM_PORTS]; + vmwareStopVideo(pScrn, pVid, TRUE); +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareOffscreenInit -- + * + * Initializes the trivial Offscreen memory manager. + * + * Results: + * None. + * + * Side effects: + * Initializes the Offscreen manager meta-data structure. + * + *----------------------------------------------------------------------------- + */ + +static void +vmwareOffscreenInit() +{ + offscreenMgr.size = 0; + offscreenMgr.offset = 0; +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareOffscreenAllocate -- + * + * Allocates offscreen memory. + * Memory is allocated from the bottom part of the VRAM. + * The memory manager is trivial iand can handle only 1 video-stream. + * ---------- + * | | + * | FB | + * | | + * |--------- + * | | + * | | + * |--------| + * | Offscr | + * |--------| + * + * VRAM + * + * Results: + * Pointer to the allocated Offscreen memory. + * + * Side effects: + * Updates the Offscreen memory manager meta-data structure. + * + *----------------------------------------------------------------------------- + */ + +static VMWAREOffscreenPtr +vmwareOffscreenAllocate(VMWAREPtr pVMWARE, uint32 size) +{ + VMWAREOffscreenPtr memptr; + + if ((pVMWARE->videoRam - pVMWARE->FbSize - pVMWARE->fbPitch - 7) < size) { + return NULL; + } + + memptr = xalloc(sizeof(VMWAREOffscreenRec)); + if (!memptr) { + return NULL; + } + memptr->size = size; + memptr->offset = (pVMWARE->videoRam - size) & ~7; + + VmwareLog(("vmwareOffscreenAllocate: Offset:%x", memptr->offset)); + + offscreenMgr.size = memptr->size; + offscreenMgr.offset = memptr->offset; + return memptr; +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareOffscreenFree -- + * + * Frees the allocated offscreen memory. + * + * Results: + * None. + * + * Side effects: + * Updates the Offscreen memory manager meta-data structure. + * + *----------------------------------------------------------------------------- + */ + +static void +vmwareOffscreenFree(VMWAREOffscreenPtr memptr) +{ + if (memptr) { + free(memptr); + } + + offscreenMgr.size = 0; + offscreenMgr.offset = 0; +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareVideoEnabled -- + * + * Checks if Video FIFO and Escape FIFO cap are enabled. + * + * Results: + * TRUE if required caps are enabled, FALSE otherwise. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +Bool vmwareVideoEnabled(VMWAREPtr pVMWARE) +{ + return ((pVMWARE->vmwareCapability & SVGA_CAP_EXTENDED_FIFO) && + (pVMWARE->vmwareFIFO[SVGA_FIFO_CAPABILITIES] & + (SVGA_FIFO_CAP_VIDEO | SVGA_FIFO_CAP_ESCAPE))); +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareVideoInit -- + * + * Initializes Xv support. + * + * Results: + * TRUE on success, FALSE on error. + * + * Side effects: + * Xv support is initialized. Memory is allocated for all supported + * video streams. + * + *----------------------------------------------------------------------------- + */ + +Bool vmwareVideoInit(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = infoFromScreen(pScreen); + VMWAREPtr pVMWARE = VMWAREPTR(pScrn); + XF86VideoAdaptorPtr *overlayAdaptors, *newAdaptors = NULL; + XF86VideoAdaptorPtr newAdaptor = NULL; + int numAdaptors; + + TRACEPOINT + + vmwareOffscreenInit(); + + numAdaptors = xf86XVListGenericAdaptors(pScrn, &overlayAdaptors); + + newAdaptor = vmwareVideoSetup(pScrn); + if (!newAdaptor) { + VmwareLog(("Failed to initialize Xv extension \n")); + return FALSE; + } + + if (!numAdaptors) { + numAdaptors = 1; + overlayAdaptors = &newAdaptor; + } else { + newAdaptors = xalloc((numAdaptors + 1) * + sizeof(XF86VideoAdaptorPtr*)); + if (!newAdaptors) { + xf86XVFreeVideoAdaptorRec(newAdaptor); + return FALSE; + } + + memcpy(newAdaptors, overlayAdaptors, + numAdaptors * sizeof(XF86VideoAdaptorPtr)); + newAdaptors[numAdaptors++] = newAdaptor; + overlayAdaptors = newAdaptors; + } + + if (!xf86XVScreenInit(pScreen, overlayAdaptors, numAdaptors)) { + VmwareLog(("Failed to initialize Xv extension\n")); + xf86XVFreeVideoAdaptorRec(newAdaptor); + return FALSE; + } + + if (newAdaptors) { + xfree(newAdaptors); + } + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Initialized VMware Xv extension successfully.\n"); + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareVideoEnd -- + * + * Unitializes video. + * + * Results: + * None. + * + * Side effects: + * pVMWARE->videoStreams = NULL + * + *----------------------------------------------------------------------------- + */ + +void vmwareVideoEnd(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = infoFromScreen(pScreen); + VMWAREPtr pVMWARE = VMWAREPTR(pScrn); + VMWAREVideoPtr pVid; + int i; + + TRACEPOINT + + /* + * Video streams are allocated after the DevUnion array + * (see VideoSetup) + */ + pVid = (VMWAREVideoPtr) &pVMWARE->videoStreams[VMWARE_VID_NUM_PORTS]; + for (i = 0; i < VMWARE_VID_NUM_PORTS; ++i) { + vmwareVideoEndStream(pScrn, &pVid[i]); + } + + free(pVMWARE->videoStreams); + pVMWARE->videoStreams = NULL; +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareVideoSetup -- + * + * Initializes a XF86VideoAdaptor structure with the capabilities and + * functions supported by this video driver. + * + * Results: + * On success initialized XF86VideoAdaptor struct or NULL on error + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static XF86VideoAdaptorPtr vmwareVideoSetup(ScrnInfoPtr pScrn) +{ + VMWAREPtr pVMWARE = VMWAREPTR(pScrn); + XF86VideoAdaptorPtr adaptor; + VMWAREVideoPtr pPriv; + DevUnion *du; + int i; + + TRACEPOINT + + adaptor = xf86XVAllocateVideoAdaptorRec(pScrn); + if (!adaptor) { + VmwareLog(("Not enough memory\n")); + return NULL; + } + du = xcalloc(1, VMWARE_VID_NUM_PORTS * + (sizeof(DevUnion) + sizeof(VMWAREVideoRec))); + + if (!du) { + VmwareLog(("Not enough memory.\n")); + xf86XVFreeVideoAdaptorRec(adaptor); + return NULL; + } + + adaptor->type = XvInputMask | XvImageMask | XvWindowMask; + adaptor->flags = VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT; + adaptor->name = "VMware Video Engine"; + adaptor->nEncodings = VMWARE_VID_NUM_ENCODINGS; + adaptor->pEncodings = vmwareVideoEncodings; + adaptor->nFormats = VMWARE_VID_NUM_FORMATS; + adaptor->pFormats = vmwareVideoFormats; + adaptor->nPorts = VMWARE_VID_NUM_PORTS; + + pPriv = (VMWAREVideoPtr) &du[VMWARE_VID_NUM_PORTS]; + adaptor->pPortPrivates = du; + + for (i = 0; i < VMWARE_VID_NUM_PORTS; ++i) { + pPriv[i].streamId = i; + pPriv[i].play = vmwareVideoInitStream; + pPriv[i].flags = SVGA_VIDEO_FLAG_COLORKEY; + pPriv[i].colorKey = VMWARE_VIDEO_COLORKEY; + adaptor->pPortPrivates[i].ptr = &pPriv[i]; + } + pVMWARE->videoStreams = du; + + adaptor->nAttributes = VMWARE_VID_NUM_ATTRIBUTES; + adaptor->pAttributes = vmwareVideoAttributes; + + adaptor->nImages = VMWARE_VID_NUM_IMAGES; + adaptor->pImages = vmwareVideoImages; + + adaptor->PutVideo = NULL; + adaptor->PutStill = NULL; + adaptor->GetVideo = NULL; + adaptor->GetStill = NULL; + adaptor->StopVideo = vmwareStopVideo; + adaptor->SetPortAttribute = vmwareSetPortAttribute; + adaptor->GetPortAttribute = vmwareGetPortAttribute; + adaptor->QueryBestSize = vmwareQueryBestSize; + adaptor->PutImage = vmwareXvPutImage; + adaptor->QueryImageAttributes = vmwareQueryImageAttributes; + + return adaptor; +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareVideoInitStream -- + * + * Initializes a video stream in response to the first PutImage() on a + * video stream. The process goes as follows: + * - Figure out characteristics according to format + * - Allocate offscreen memory + * - Pass on video to Play() functions + * + * Results: + * Success or XvBadAlloc on failure. + * + * Side effects: + * Video stream is initialized and its first frame sent to the host + * (done by VideoPlay() function called at the end) + * + *----------------------------------------------------------------------------- + */ + +static int vmwareVideoInitStream(ScrnInfoPtr pScrn, VMWAREVideoPtr pVid, + short src_x, short src_y, short drw_x, + short drw_y, short src_w, short src_h, + short drw_w, short drw_h, int format, + unsigned char *buf, short width, short height) +{ + VMWAREPtr pVMWARE = VMWAREPTR(pScrn); + ScreenPtr pScreen = pScrn->pScreen; + int i; + + TRACEPOINT + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Initializing Xv video-stream with id:%d format:%d\n", + pVid->streamId, format); + + pVid->size = vmwareVideoInitAttributes(pScrn, pVid, format, width, + height); + + if (pVid->size == -1) { + VmwareLog(("Could not initialize 0x%x video stream\n", format)); + return XvBadAlloc; + } + + pVid->play = vmwareVideoPlay; + + pVid->fbarea = vmwareOffscreenAllocate(pVMWARE, + pVid->size * VMWARE_VID_NUM_BUFFERS); + + if (!pVid->fbarea) { + VmwareLog(("Could not allocate offscreen memory\n")); + vmwareVideoEndStream(pScrn, pVid); + return BadAlloc; + } + + pVid->bufs[0].dataOffset = pVid->fbarea->offset; + pVid->bufs[0].data = pVMWARE->FbBase + pVid->bufs[0].dataOffset; + + for (i = 1; i < VMWARE_VID_NUM_BUFFERS; ++i) { + pVid->bufs[i].dataOffset = pVid->bufs[i-1].dataOffset + pVid->size; + pVid->bufs[i].data = pVMWARE->FbBase + pVid->bufs[i].dataOffset; + } + pVid->currBuf = 0; + + VmwareLog(("Got offscreen region, offset %d, size %d " + "(yuv size in bytes: %d)\n", + pVid->fbarea->offset, pVid->fbarea->size, pVid->size)); + + return pVid->play(pScrn, pVid, src_x, src_y, drw_x, drw_y, src_w, src_h, + drw_w, drw_h, format, buf, width, height); +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareVideoInitAttributes -- + * + * Fetches the format specific attributes using QueryImageAttributes(). + * + * Results: + * size of the YUV frame on success and -1 on error. + * + * Side effects: + * The video stream gets the format specific attributes(fmtData). + * + *----------------------------------------------------------------------------- + */ + +static int vmwareVideoInitAttributes(ScrnInfoPtr pScrn, VMWAREVideoPtr pVid, + int format, unsigned short width, + unsigned short height) +{ + int size; + VMWAREVideoFmtData *fmtData; + + TRACEPOINT + + fmtData = xcalloc(1, sizeof(VMWAREVideoFmtData)); + if (!fmtData) { + return -1; + } + + size = vmwareQueryImageAttributes(pScrn, format, &width, &height, + fmtData->pitches, fmtData->offsets); + if (size == -1) { + free(fmtData); + return -1; + } + + pVid->fmt_priv = fmtData; + return size; +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareVideoPlay -- + * + * Sends all the attributes associated with the video frame using the + * FIFO ESCAPE mechanism to the host. + * + * Results: + * Always returns Success. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static int vmwareVideoPlay(ScrnInfoPtr pScrn, VMWAREVideoPtr pVid, + short src_x, short src_y, short drw_x, + short drw_y, short src_w, short src_h, + short drw_w, short drw_h, int format, + unsigned char *buf, short width, + short height) +{ + VMWAREPtr pVMWARE = VMWAREPTR(pScrn); + uint32 *fifoItem; + int i, regId; + struct PACKED _item { + uint32 regId; + uint32 value; + }; + + struct PACKED _body { + uint32 escape; + uint32 streamId; + struct _item items[SVGA_VIDEO_NUM_REGS]; + }; + + struct PACKED _cmdSetRegs { + uint32 cmd; + uint32 nsid; + uint32 size; + struct _body body; + }; + + struct _cmdSetRegs cmdSetRegs; + + memcpy(pVid->bufs[pVid->currBuf].data, buf, pVid->size); + + cmdSetRegs.cmd = SVGA_CMD_ESCAPE; + cmdSetRegs.nsid = SVGA_ESCAPE_NSID_VMWARE; + cmdSetRegs.size = sizeof(cmdSetRegs.body); + cmdSetRegs.body.escape = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS; + cmdSetRegs.body.streamId = pVid->streamId; + + for (i = SVGA_VIDEO_ENABLED; i < SVGA_VIDEO_NUM_REGS; i++) { + cmdSetRegs.body.items[i].regId = i; + } + + cmdSetRegs.body.items[SVGA_VIDEO_ENABLED].value = TRUE; + cmdSetRegs.body.items[SVGA_VIDEO_DATA_OFFSET].value = + pVid->bufs[pVid->currBuf].dataOffset; + cmdSetRegs.body.items[SVGA_VIDEO_SIZE].value = pVid->size; + cmdSetRegs.body.items[SVGA_VIDEO_FORMAT].value = format; + cmdSetRegs.body.items[SVGA_VIDEO_X].value = drw_x; + cmdSetRegs.body.items[SVGA_VIDEO_Y].value = drw_y; + cmdSetRegs.body.items[SVGA_VIDEO_SRC_WIDTH].value = width; + cmdSetRegs.body.items[SVGA_VIDEO_SRC_HEIGHT].value = height; + cmdSetRegs.body.items[SVGA_VIDEO_DST_WIDTH]. value = drw_w; + cmdSetRegs.body.items[SVGA_VIDEO_DST_HEIGHT].value = drw_h; + cmdSetRegs.body.items[SVGA_VIDEO_COLORKEY].value = pVid->colorKey; + cmdSetRegs.body.items[SVGA_VIDEO_FLAGS].value = pVid->flags; + + for (i = 0, regId = SVGA_VIDEO_PITCH_1; i < 3; i++, regId++) { + cmdSetRegs.body.items[regId].value = pVid->fmt_priv->pitches[i]; + } + + fifoItem = (uint32 *) &cmdSetRegs; + for (i = 0; i < sizeof(cmdSetRegs) / sizeof(uint32); i++) { + vmwareWriteWordToFIFO(pVMWARE, fifoItem[i]); + } + + vmwareVideoFlush(pVMWARE, pVid->streamId); + + pVid->currBuf = ++pVid->currBuf & (VMWARE_VID_NUM_BUFFERS - 1); + return Success; +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareVideoFlush -- + * + * Sends the VIDEO_FLUSH command (FIFO ESCAPE mechanism) asking the host + * to play the video stream or end it. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static void vmwareVideoFlush(VMWAREPtr pVMWARE, uint32 streamId) +{ + struct PACKED _body { + uint32 escape; + uint32 streamId; + }; + + struct PACKED _cmdFlush { + uint32 cmd; + uint32 nsid; + uint32 size; + struct _body body; + }; + + struct _cmdFlush cmdFlush; + uint32 *fifoItem; + int i; + + cmdFlush.cmd = SVGA_CMD_ESCAPE; + cmdFlush.nsid = SVGA_ESCAPE_NSID_VMWARE; + cmdFlush.size = sizeof(cmdFlush.body); + cmdFlush.body.escape = SVGA_ESCAPE_VMWARE_VIDEO_FLUSH; + cmdFlush.body.streamId = streamId; + + fifoItem = (uint32 *) &cmdFlush; + for (i = 0; i < sizeof(cmdFlush) / sizeof(uint32); i++) { + vmwareWriteWordToFIFO(pVMWARE, fifoItem[i]); + } +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareVideoSetOneReg -- + * + * Sets one video register using the FIFO ESCAPE mechanidm. + * + * Results: + * None. + * + * Side effects: + * None. + *----------------------------------------------------------------------------- + */ + +static void vmwareVideoSetOneReg(VMWAREPtr pVMWARE, uint32 streamId, + uint32 regId, uint32 value) +{ + struct PACKED _item { + uint32 regId; + uint32 value; + }; + + struct PACKED _body { + uint32 escape; + uint32 streamId; + struct _item item; + }; + + struct PACKED _cmdSetRegs { + uint32 cmd; + uint32 nsid; + uint32 size; + struct _body body; + }; + + struct _cmdSetRegs cmdSetRegs; + int i; + uint32 *fifoItem; + + cmdSetRegs.cmd = SVGA_CMD_ESCAPE; + cmdSetRegs.nsid = SVGA_ESCAPE_NSID_VMWARE; + cmdSetRegs.size = sizeof(cmdSetRegs.body); + cmdSetRegs.body.escape = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS; + cmdSetRegs.body.streamId = streamId; + cmdSetRegs.body.item.regId = regId; + cmdSetRegs.body.item.value = value; + + fifoItem = (uint32 *) &cmdSetRegs; + for (i = 0; i < sizeof(cmdSetRegs) / sizeof(uint32); i++) { + vmwareWriteWordToFIFO(pVMWARE, fifoItem[i]); + } +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareVideoEndStream -- + * + * Frees up all resources (if any) taken by a video stream. + * + * Results: + * None. + * + * Side effects: + * Same as above. + * + *----------------------------------------------------------------------------- + */ + +static void vmwareVideoEndStream(ScrnInfoPtr pScrn, VMWAREVideoPtr pVid) +{ + uint32 id, colorKey, flags; + + if (pVid->fmt_priv) { + free(pVid->fmt_priv); + } + + if (pVid->fbarea) { + vmwareOffscreenFree(pVid->fbarea); + pVid->fbarea = NULL; + } + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Terminating Xv video-stream id:%d\n", pVid->streamId); + /* + * reset stream for next video + */ + id = pVid->streamId; + colorKey = pVid->colorKey; + flags = pVid->flags; + memset(pVid, 0, sizeof(*pVid)); + pVid->streamId = id; + pVid->play = vmwareVideoInitStream; + pVid->colorKey = colorKey; + pVid->flags = flags; +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareXvPutImage -- + * + * Main video playback function. It copies the passed data which is in + * the specified format (e.g. FOURCC_YV12) into the overlay. + * + * If sync is TRUE the driver should not return from this + * function until it is through reading the data from buf. + * + * XXX: src_x, src_y, src_w and src_h are used to denote that only + * part of the source image is to be displayed. We ignore as didn't + * find applications that use them. + * + * There are two function prototypes to cope with the API change in X.org + * 7.1 + * + * Results: + * Success or XvBadAlloc on failure + * + * Side effects: + * Video stream will be played(initialized if 1st frame) on success + * or will fail on error. + * + *----------------------------------------------------------------------------- + */ + +#if XORG_VERSION_CURRENT > XORG_VERSION_NUMERIC(7, 0, 0, 0, 0) || XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(4, 0, 0, 0, 0) +static int vmwareXvPutImage(ScrnInfoPtr pScrn, short src_x, short src_y, + short drw_x, short drw_y, short src_w, short src_h, + short drw_w, short drw_h, int format, + unsigned char *buf, short width, short height, + Bool sync, RegionPtr clipBoxes, pointer data, + DrawablePtr dst) +#else +static int vmwareXvPutImage(ScrnInfoPtr pScrn, short src_x, short src_y, + short drw_x, short drw_y, short src_w, short src_h, + short drw_w, short drw_h, int format, + unsigned char *buf, short width, short height, + Bool sync, RegionPtr clipBoxes, pointer data) +#endif +{ + VMWAREPtr pVMWARE = VMWAREPTR(pScrn); + VMWAREVideoPtr pVid = data; + + TRACEPOINT + + if (!vmwareVideoEnabled(pVMWARE)) { + return XvBadAlloc; + } + + return pVid->play(pScrn, pVid, src_x, src_y, drw_x, drw_y, src_w, src_h, + drw_w, drw_h, format, buf, width, height); +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareStopVideo -- + * + * Called when we should stop playing video for a particular stream. If + * Cleanup is FALSE, the "stop" operation is only temporary, and thus we + * don't do anything. If Cleanup is TRUE we kill the video stream by + * sending a message to the host and freeing up the stream. + * + * Results: + * None. + * + * Side effects: + * See above. + * + *----------------------------------------------------------------------------- + */ + +static void vmwareStopVideo(ScrnInfoPtr pScrn, pointer data, Bool Cleanup) +{ + VMWAREVideoPtr pVid = data; + VMWAREPtr pVMWARE = VMWAREPTR(pScrn); + TRACEPOINT + + if (!vmwareVideoEnabled(pVMWARE)) { + return; + } + if (!Cleanup) { + return; + } + vmwareVideoSetOneReg(pVMWARE, pVid->streamId, + SVGA_VIDEO_ENABLED, FALSE); + + vmwareVideoFlush(pVMWARE, pVid->streamId); + vmwareVideoEndStream(pScrn, pVid); + +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareQueryImageAttributes -- + * + * From the spec: This function is called to let the driver specify how data + * for a particular image of size width by height should be stored. + * Sometimes only the size and corrected width and height are needed. In + * that case pitches and offsets are NULL. + * + * Results: + * The size of the memory required for the image, or -1 on error. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static int vmwareQueryImageAttributes(ScrnInfoPtr pScrn, int format, + unsigned short *width, + unsigned short *height, int *pitches, + int *offsets) +{ + INT32 size, tmp; + + TRACEPOINT + + if (*width > VMWARE_VID_MAX_WIDTH) { + *width = VMWARE_VID_MAX_WIDTH; + } + if (*height > VMWARE_VID_MAX_HEIGHT) { + *height = VMWARE_VID_MAX_HEIGHT; + } + + *width = (*width + 1) & ~1; + if (offsets != NULL) { + offsets[0] = 0; + } + + switch (format) { + case FOURCC_YV12: + *height = (*height + 1) & ~1; + size = (*width + 3) & ~3; + if (pitches) { + pitches[0] = size; + } + size *= *height; + if (offsets) { + offsets[1] = size; + } + tmp = ((*width >> 1) + 3) & ~3; + if (pitches) { + pitches[1] = pitches[2] = tmp; + } + tmp *= (*height >> 1); + size += tmp; + if (offsets) { + offsets[2] = size; + } + size += tmp; + break; + case FOURCC_YUY2: + size = *width * 2; + if (pitches) { + pitches[0] = size; + } + size *= *height; + break; + default: + VmwareLog(("Query for invalid video format %d\n", format)); + return -1; + } + return size; +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareSetPortAttribute -- + * + * From the spec: A port may have particular attributes such as colorKey, hue, + * saturation, brightness or contrast. Xv clients set these + * attribute values by sending attribute strings (Atoms) to the server. + * + * Results: + * Success if the attribute exists and XvBadAlloc otherwise. + * + * Side effects: + * The respective attribute gets the new value. + * + *----------------------------------------------------------------------------- + */ + +static int vmwareSetPortAttribute(ScrnInfoPtr pScrn, Atom attribute, + INT32 value, pointer data) +{ + VMWAREVideoPtr pVid = (VMWAREVideoPtr) data; + Atom xvColorKey = MAKE_ATOM("XV_COLORKEY"); + + if (attribute == xvColorKey) { + pVid->colorKey = value; + } else { + return XvBadAlloc; + } + return Success; +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareGetPortAttribute -- + * + * From the spec: A port may have particular attributes such as hue, + * saturation, brightness or contrast. Xv clients get these + * attribute values by sending attribute strings (Atoms) to the server + * + * Results: + * Success if the attribute exists and XvBadAlloc otherwise. + * + * Side effects: + * "value" contains the requested attribute on success. + * + *----------------------------------------------------------------------------- + */ + +static int vmwareGetPortAttribute(ScrnInfoPtr pScrn, Atom attribute, + INT32 *value, pointer data) +{ + VMWAREVideoPtr pVid = (VMWAREVideoPtr) data; + Atom xvColorKey = MAKE_ATOM("XV_COLORKEY"); + + if (attribute == xvColorKey) { + *value = pVid->colorKey; + } else { + return XvBadAlloc; + } + return Success; +} + + +/* + *----------------------------------------------------------------------------- + * + * vmwareQueryBestSize -- + * + * From the spec: QueryBestSize provides the client with a way to query what + * the destination dimensions would end up being if they were to request + * that an area vid_w by vid_h from the video stream be scaled to rectangle + * of drw_w by drw_h on the screen. Since it is not expected that all + * hardware will be able to get the target dimensions exactly, it is + * important that the driver provide this function. + * + * This function seems to never be called, but to be on the safe side + * we apply the same logic that QueryImageAttributes has for width + * and height + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static void vmwareQueryBestSize(ScrnInfoPtr pScrn, Bool motion, + short vid_w, short vid_h, short drw_w, + short drw_h, unsigned int *p_w, + unsigned int *p_h, pointer data) +{ + *p_w = (drw_w + 1) & ~1; + *p_h = drw_h; + + return; +} +