Commit cbc8cc04 authored by Dave Airlie's avatar Dave Airlie

Merge remote branch 'korg/drm-vmware-staging' into drm-core-next

parents 5012f506 fb1d9738
...@@ -30,6 +30,7 @@ obj-$(CONFIG_DRM_I830) += i830/ ...@@ -30,6 +30,7 @@ obj-$(CONFIG_DRM_I830) += i830/
obj-$(CONFIG_DRM_I915) += i915/ obj-$(CONFIG_DRM_I915) += i915/
obj-$(CONFIG_DRM_SIS) += sis/ obj-$(CONFIG_DRM_SIS) += sis/
obj-$(CONFIG_DRM_SAVAGE)+= savage/ obj-$(CONFIG_DRM_SAVAGE)+= savage/
obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
obj-$(CONFIG_DRM_VIA) +=via/ obj-$(CONFIG_DRM_VIA) +=via/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-y += i2c/ obj-y += i2c/
config DRM_VMWGFX
tristate "DRM driver for VMware Virtual GPU"
depends on DRM && PCI
select FB_DEFERRED_IO
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select DRM_TTM
help
KMS enabled DRM driver for SVGA2 virtual hardware.
If unsure say n. The compiled module will be
called vmwgfx.ko
ccflags-y := -Iinclude/drm
vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \
vmwgfx_fb.o vmwgfx_ioctl.o vmwgfx_resource.o vmwgfx_buffer.o \
vmwgfx_fifo.o vmwgfx_irq.o vmwgfx_ldu.o vmwgfx_ttm_glue.o \
vmwgfx_overlay.o
obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o
/**********************************************************
* Copyright 1998-2009 VMware, Inc. All rights reserved.
*
* 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 AUTHORS OR COPYRIGHT HOLDERS
* 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.
*
**********************************************************/
/*
* svga3d_reg.h --
*
* SVGA 3D hardware definitions
*/
#ifndef _SVGA3D_REG_H_
#define _SVGA3D_REG_H_
#include "svga_reg.h"
/*
* 3D Hardware Version
*
* The hardware version is stored in the SVGA_FIFO_3D_HWVERSION fifo
* register. Is set by the host and read by the guest. This lets
* us make new guest drivers which are backwards-compatible with old
* SVGA hardware revisions. It does not let us support old guest
* drivers. Good enough for now.
*
*/
#define SVGA3D_MAKE_HWVERSION(major, minor) (((major) << 16) | ((minor) & 0xFF))
#define SVGA3D_MAJOR_HWVERSION(version) ((version) >> 16)
#define SVGA3D_MINOR_HWVERSION(version) ((version) & 0xFF)
typedef enum {
SVGA3D_HWVERSION_WS5_RC1 = SVGA3D_MAKE_HWVERSION(0, 1),
SVGA3D_HWVERSION_WS5_RC2 = SVGA3D_MAKE_HWVERSION(0, 2),
SVGA3D_HWVERSION_WS51_RC1 = SVGA3D_MAKE_HWVERSION(0, 3),
SVGA3D_HWVERSION_WS6_B1 = SVGA3D_MAKE_HWVERSION(1, 1),
SVGA3D_HWVERSION_FUSION_11 = SVGA3D_MAKE_HWVERSION(1, 4),
SVGA3D_HWVERSION_WS65_B1 = SVGA3D_MAKE_HWVERSION(2, 0),
SVGA3D_HWVERSION_CURRENT = SVGA3D_HWVERSION_WS65_B1,
} SVGA3dHardwareVersion;
/*
* Generic Types
*/
typedef uint32 SVGA3dBool; /* 32-bit Bool definition */
#define SVGA3D_NUM_CLIPPLANES 6
#define SVGA3D_MAX_SIMULTANEOUS_RENDER_TARGETS 8
/*
* Surface formats.
*
* If you modify this list, be sure to keep GLUtil.c in sync. It
* includes the internal format definition of each surface in
* GLUtil_ConvertSurfaceFormat, and it contains a table of
* human-readable names in GLUtil_GetFormatName.
*/
typedef enum SVGA3dSurfaceFormat {
SVGA3D_FORMAT_INVALID = 0,
SVGA3D_X8R8G8B8 = 1,
SVGA3D_A8R8G8B8 = 2,
SVGA3D_R5G6B5 = 3,
SVGA3D_X1R5G5B5 = 4,
SVGA3D_A1R5G5B5 = 5,
SVGA3D_A4R4G4B4 = 6,
SVGA3D_Z_D32 = 7,
SVGA3D_Z_D16 = 8,
SVGA3D_Z_D24S8 = 9,
SVGA3D_Z_D15S1 = 10,
SVGA3D_LUMINANCE8 = 11,
SVGA3D_LUMINANCE4_ALPHA4 = 12,
SVGA3D_LUMINANCE16 = 13,
SVGA3D_LUMINANCE8_ALPHA8 = 14,
SVGA3D_DXT1 = 15,
SVGA3D_DXT2 = 16,
SVGA3D_DXT3 = 17,
SVGA3D_DXT4 = 18,
SVGA3D_DXT5 = 19,
SVGA3D_BUMPU8V8 = 20,
SVGA3D_BUMPL6V5U5 = 21,
SVGA3D_BUMPX8L8V8U8 = 22,
SVGA3D_BUMPL8V8U8 = 23,
SVGA3D_ARGB_S10E5 = 24, /* 16-bit floating-point ARGB */
SVGA3D_ARGB_S23E8 = 25, /* 32-bit floating-point ARGB */
SVGA3D_A2R10G10B10 = 26,
/* signed formats */
SVGA3D_V8U8 = 27,
SVGA3D_Q8W8V8U8 = 28,
SVGA3D_CxV8U8 = 29,
/* mixed formats */
SVGA3D_X8L8V8U8 = 30,
SVGA3D_A2W10V10U10 = 31,
SVGA3D_ALPHA8 = 32,
/* Single- and dual-component floating point formats */
SVGA3D_R_S10E5 = 33,
SVGA3D_R_S23E8 = 34,
SVGA3D_RG_S10E5 = 35,
SVGA3D_RG_S23E8 = 36,
/*
* Any surface can be used as a buffer object, but SVGA3D_BUFFER is
* the most efficient format to use when creating new surfaces
* expressly for index or vertex data.
*/
SVGA3D_BUFFER = 37,
SVGA3D_Z_D24X8 = 38,
SVGA3D_V16U16 = 39,
SVGA3D_G16R16 = 40,
SVGA3D_A16B16G16R16 = 41,
/* Packed Video formats */
SVGA3D_UYVY = 42,
SVGA3D_YUY2 = 43,
SVGA3D_FORMAT_MAX
} SVGA3dSurfaceFormat;
typedef uint32 SVGA3dColor; /* a, r, g, b */
/*
* These match the D3DFORMAT_OP definitions used by Direct3D. We need
* them so that we can query the host for what the supported surface
* operations are (when we're using the D3D backend, in particular),
* and so we can send those operations to the guest.
*/
typedef enum {
SVGA3DFORMAT_OP_TEXTURE = 0x00000001,
SVGA3DFORMAT_OP_VOLUMETEXTURE = 0x00000002,
SVGA3DFORMAT_OP_CUBETEXTURE = 0x00000004,
SVGA3DFORMAT_OP_OFFSCREEN_RENDERTARGET = 0x00000008,
SVGA3DFORMAT_OP_SAME_FORMAT_RENDERTARGET = 0x00000010,
SVGA3DFORMAT_OP_ZSTENCIL = 0x00000040,
SVGA3DFORMAT_OP_ZSTENCIL_WITH_ARBITRARY_COLOR_DEPTH = 0x00000080,
/*
* This format can be used as a render target if the current display mode
* is the same depth if the alpha channel is ignored. e.g. if the device
* can render to A8R8G8B8 when the display mode is X8R8G8B8, then the
* format op list entry for A8R8G8B8 should have this cap.
*/
SVGA3DFORMAT_OP_SAME_FORMAT_UP_TO_ALPHA_RENDERTARGET = 0x00000100,
/*
* This format contains DirectDraw support (including Flip). This flag
* should not to be set on alpha formats.
*/
SVGA3DFORMAT_OP_DISPLAYMODE = 0x00000400,
/*
* The rasterizer can support some level of Direct3D support in this format
* and implies that the driver can create a Context in this mode (for some
* render target format). When this flag is set, the SVGA3DFORMAT_OP_DISPLAYMODE
* flag must also be set.
*/
SVGA3DFORMAT_OP_3DACCELERATION = 0x00000800,
/*
* This is set for a private format when the driver has put the bpp in
* the structure.
*/
SVGA3DFORMAT_OP_PIXELSIZE = 0x00001000,
/*
* Indicates that this format can be converted to any RGB format for which
* SVGA3DFORMAT_OP_MEMBEROFGROUP_ARGB is specified
*/
SVGA3DFORMAT_OP_CONVERT_TO_ARGB = 0x00002000,
/*
* Indicates that this format can be used to create offscreen plain surfaces.
*/
SVGA3DFORMAT_OP_OFFSCREENPLAIN = 0x00004000,
/*
* Indicated that this format can be read as an SRGB texture (meaning that the
* sampler will linearize the looked up data)
*/
SVGA3DFORMAT_OP_SRGBREAD = 0x00008000,
/*
* Indicates that this format can be used in the bumpmap instructions
*/
SVGA3DFORMAT_OP_BUMPMAP = 0x00010000,
/*
* Indicates that this format can be sampled by the displacement map sampler
*/
SVGA3DFORMAT_OP_DMAP = 0x00020000,
/*
* Indicates that this format cannot be used with texture filtering
*/
SVGA3DFORMAT_OP_NOFILTER = 0x00040000,
/*
* Indicates that format conversions are supported to this RGB format if
* SVGA3DFORMAT_OP_CONVERT_TO_ARGB is specified in the source format.
*/
SVGA3DFORMAT_OP_MEMBEROFGROUP_ARGB = 0x00080000,
/*
* Indicated that this format can be written as an SRGB target (meaning that the
* pixel pipe will DE-linearize data on output to format)
*/
SVGA3DFORMAT_OP_SRGBWRITE = 0x00100000,
/*
* Indicates that this format cannot be used with alpha blending
*/
SVGA3DFORMAT_OP_NOALPHABLEND = 0x00200000,
/*
* Indicates that the device can auto-generated sublevels for resources
* of this format
*/
SVGA3DFORMAT_OP_AUTOGENMIPMAP = 0x00400000,
/*
* Indicates that this format can be used by vertex texture sampler
*/
SVGA3DFORMAT_OP_VERTEXTEXTURE = 0x00800000,
/*
* Indicates that this format supports neither texture coordinate wrap
* modes, nor mipmapping
*/
SVGA3DFORMAT_OP_NOTEXCOORDWRAPNORMIP = 0x01000000
} SVGA3dFormatOp;
/*
* This structure is a conversion of SVGA3DFORMAT_OP_*.
* Entries must be located at the same position.
*/
typedef union {
uint32 value;
struct {
uint32 texture : 1;
uint32 volumeTexture : 1;
uint32 cubeTexture : 1;
uint32 offscreenRenderTarget : 1;
uint32 sameFormatRenderTarget : 1;
uint32 unknown1 : 1;
uint32 zStencil : 1;
uint32 zStencilArbitraryDepth : 1;
uint32 sameFormatUpToAlpha : 1;
uint32 unknown2 : 1;
uint32 displayMode : 1;
uint32 acceleration3d : 1;
uint32 pixelSize : 1;
uint32 convertToARGB : 1;
uint32 offscreenPlain : 1;
uint32 sRGBRead : 1;
uint32 bumpMap : 1;
uint32 dmap : 1;
uint32 noFilter : 1;
uint32 memberOfGroupARGB : 1;
uint32 sRGBWrite : 1;
uint32 noAlphaBlend : 1;
uint32 autoGenMipMap : 1;
uint32 vertexTexture : 1;
uint32 noTexCoordWrapNorMip : 1;
};
} SVGA3dSurfaceFormatCaps;
/*
* SVGA_3D_CMD_SETRENDERSTATE Types. All value types
* must fit in a uint32.
*/
typedef enum {
SVGA3D_RS_INVALID = 0,
SVGA3D_RS_ZENABLE = 1, /* SVGA3dBool */
SVGA3D_RS_ZWRITEENABLE = 2, /* SVGA3dBool */
SVGA3D_RS_ALPHATESTENABLE = 3, /* SVGA3dBool */
SVGA3D_RS_DITHERENABLE = 4, /* SVGA3dBool */
SVGA3D_RS_BLENDENABLE = 5, /* SVGA3dBool */
SVGA3D_RS_FOGENABLE = 6, /* SVGA3dBool */
SVGA3D_RS_SPECULARENABLE = 7, /* SVGA3dBool */
SVGA3D_RS_STENCILENABLE = 8, /* SVGA3dBool */
SVGA3D_RS_LIGHTINGENABLE = 9, /* SVGA3dBool */
SVGA3D_RS_NORMALIZENORMALS = 10, /* SVGA3dBool */
SVGA3D_RS_POINTSPRITEENABLE = 11, /* SVGA3dBool */
SVGA3D_RS_POINTSCALEENABLE = 12, /* SVGA3dBool */
SVGA3D_RS_STENCILREF = 13, /* uint32 */
SVGA3D_RS_STENCILMASK = 14, /* uint32 */
SVGA3D_RS_STENCILWRITEMASK = 15, /* uint32 */
SVGA3D_RS_FOGSTART = 16, /* float */
SVGA3D_RS_FOGEND = 17, /* float */
SVGA3D_RS_FOGDENSITY = 18, /* float */
SVGA3D_RS_POINTSIZE = 19, /* float */
SVGA3D_RS_POINTSIZEMIN = 20, /* float */
SVGA3D_RS_POINTSIZEMAX = 21, /* float */
SVGA3D_RS_POINTSCALE_A = 22, /* float */
SVGA3D_RS_POINTSCALE_B = 23, /* float */
SVGA3D_RS_POINTSCALE_C = 24, /* float */
SVGA3D_RS_FOGCOLOR = 25, /* SVGA3dColor */
SVGA3D_RS_AMBIENT = 26, /* SVGA3dColor */
SVGA3D_RS_CLIPPLANEENABLE = 27, /* SVGA3dClipPlanes */
SVGA3D_RS_FOGMODE = 28, /* SVGA3dFogMode */
SVGA3D_RS_FILLMODE = 29, /* SVGA3dFillMode */
SVGA3D_RS_SHADEMODE = 30, /* SVGA3dShadeMode */
SVGA3D_RS_LINEPATTERN = 31, /* SVGA3dLinePattern */
SVGA3D_RS_SRCBLEND = 32, /* SVGA3dBlendOp */
SVGA3D_RS_DSTBLEND = 33, /* SVGA3dBlendOp */
SVGA3D_RS_BLENDEQUATION = 34, /* SVGA3dBlendEquation */
SVGA3D_RS_CULLMODE = 35, /* SVGA3dFace */
SVGA3D_RS_ZFUNC = 36, /* SVGA3dCmpFunc */
SVGA3D_RS_ALPHAFUNC = 37, /* SVGA3dCmpFunc */
SVGA3D_RS_STENCILFUNC = 38, /* SVGA3dCmpFunc */
SVGA3D_RS_STENCILFAIL = 39, /* SVGA3dStencilOp */
SVGA3D_RS_STENCILZFAIL = 40, /* SVGA3dStencilOp */
SVGA3D_RS_STENCILPASS = 41, /* SVGA3dStencilOp */
SVGA3D_RS_ALPHAREF = 42, /* float (0.0 .. 1.0) */
SVGA3D_RS_FRONTWINDING = 43, /* SVGA3dFrontWinding */
SVGA3D_RS_COORDINATETYPE = 44, /* SVGA3dCoordinateType */
SVGA3D_RS_ZBIAS = 45, /* float */
SVGA3D_RS_RANGEFOGENABLE = 46, /* SVGA3dBool */
SVGA3D_RS_COLORWRITEENABLE = 47, /* SVGA3dColorMask */
SVGA3D_RS_VERTEXMATERIALENABLE = 48, /* SVGA3dBool */
SVGA3D_RS_DIFFUSEMATERIALSOURCE = 49, /* SVGA3dVertexMaterial */
SVGA3D_RS_SPECULARMATERIALSOURCE = 50, /* SVGA3dVertexMaterial */
SVGA3D_RS_AMBIENTMATERIALSOURCE = 51, /* SVGA3dVertexMaterial */
SVGA3D_RS_EMISSIVEMATERIALSOURCE = 52, /* SVGA3dVertexMaterial */
SVGA3D_RS_TEXTUREFACTOR = 53, /* SVGA3dColor */
SVGA3D_RS_LOCALVIEWER = 54, /* SVGA3dBool */
SVGA3D_RS_SCISSORTESTENABLE = 55, /* SVGA3dBool */
SVGA3D_RS_BLENDCOLOR = 56, /* SVGA3dColor */
SVGA3D_RS_STENCILENABLE2SIDED = 57, /* SVGA3dBool */
SVGA3D_RS_CCWSTENCILFUNC = 58, /* SVGA3dCmpFunc */
SVGA3D_RS_CCWSTENCILFAIL = 59, /* SVGA3dStencilOp */
SVGA3D_RS_CCWSTENCILZFAIL = 60, /* SVGA3dStencilOp */
SVGA3D_RS_CCWSTENCILPASS = 61, /* SVGA3dStencilOp */
SVGA3D_RS_VERTEXBLEND = 62, /* SVGA3dVertexBlendFlags */
SVGA3D_RS_SLOPESCALEDEPTHBIAS = 63, /* float */
SVGA3D_RS_DEPTHBIAS = 64, /* float */
/*
* Output Gamma Level
*
* Output gamma effects the gamma curve of colors that are output from the
* rendering pipeline. A value of 1.0 specifies a linear color space. If the
* value is <= 0.0, gamma correction is ignored and linear color space is
* used.
*/
SVGA3D_RS_OUTPUTGAMMA = 65, /* float */
SVGA3D_RS_ZVISIBLE = 66, /* SVGA3dBool */
SVGA3D_RS_LASTPIXEL = 67, /* SVGA3dBool */
SVGA3D_RS_CLIPPING = 68, /* SVGA3dBool */
SVGA3D_RS_WRAP0 = 69, /* SVGA3dWrapFlags */
SVGA3D_RS_WRAP1 = 70, /* SVGA3dWrapFlags */
SVGA3D_RS_WRAP2 = 71, /* SVGA3dWrapFlags */
SVGA3D_RS_WRAP3 = 72, /* SVGA3dWrapFlags */
SVGA3D_RS_WRAP4 = 73, /* SVGA3dWrapFlags */
SVGA3D_RS_WRAP5 = 74, /* SVGA3dWrapFlags */
SVGA3D_RS_WRAP6 = 75, /* SVGA3dWrapFlags */
SVGA3D_RS_WRAP7 = 76, /* SVGA3dWrapFlags */
SVGA3D_RS_WRAP8 = 77, /* SVGA3dWrapFlags */
SVGA3D_RS_WRAP9 = 78, /* SVGA3dWrapFlags */
SVGA3D_RS_WRAP10 = 79, /* SVGA3dWrapFlags */
SVGA3D_RS_WRAP11 = 80, /* SVGA3dWrapFlags */
SVGA3D_RS_WRAP12 = 81, /* SVGA3dWrapFlags */
SVGA3D_RS_WRAP13 = 82, /* SVGA3dWrapFlags */
SVGA3D_RS_WRAP14 = 83, /* SVGA3dWrapFlags */
SVGA3D_RS_WRAP15 = 84, /* SVGA3dWrapFlags */
SVGA3D_RS_MULTISAMPLEANTIALIAS = 85, /* SVGA3dBool */
SVGA3D_RS_MULTISAMPLEMASK = 86, /* uint32 */
SVGA3D_RS_INDEXEDVERTEXBLENDENABLE = 87, /* SVGA3dBool */
SVGA3D_RS_TWEENFACTOR = 88, /* float */
SVGA3D_RS_ANTIALIASEDLINEENABLE = 89, /* SVGA3dBool */
SVGA3D_RS_COLORWRITEENABLE1 = 90, /* SVGA3dColorMask */
SVGA3D_RS_COLORWRITEENABLE2 = 91, /* SVGA3dColorMask */
SVGA3D_RS_COLORWRITEENABLE3 = 92, /* SVGA3dColorMask */
SVGA3D_RS_SEPARATEALPHABLENDENABLE = 93, /* SVGA3dBool */
SVGA3D_RS_SRCBLENDALPHA = 94, /* SVGA3dBlendOp */
SVGA3D_RS_DSTBLENDALPHA = 95, /* SVGA3dBlendOp */
SVGA3D_RS_BLENDEQUATIONALPHA = 96, /* SVGA3dBlendEquation */
SVGA3D_RS_MAX
} SVGA3dRenderStateName;
typedef enum {
SVGA3D_VERTEXMATERIAL_NONE = 0, /* Use the value in the current material */
SVGA3D_VERTEXMATERIAL_DIFFUSE = 1, /* Use the value in the diffuse component */
SVGA3D_VERTEXMATERIAL_SPECULAR = 2, /* Use the value in the specular component */
} SVGA3dVertexMaterial;
typedef enum {
SVGA3D_FILLMODE_INVALID = 0,
SVGA3D_FILLMODE_POINT = 1,
SVGA3D_FILLMODE_LINE = 2,
SVGA3D_FILLMODE_FILL = 3,
SVGA3D_FILLMODE_MAX
} SVGA3dFillModeType;
typedef
union {
struct {
uint16 mode; /* SVGA3dFillModeType */
uint16 face; /* SVGA3dFace */
};
uint32 uintValue;
} SVGA3dFillMode;
typedef enum {
SVGA3D_SHADEMODE_INVALID = 0,
SVGA3D_SHADEMODE_FLAT = 1,
SVGA3D_SHADEMODE_SMOOTH = 2,
SVGA3D_SHADEMODE_PHONG = 3, /* Not supported */
SVGA3D_SHADEMODE_MAX
} SVGA3dShadeMode;
typedef
union {
struct {
uint16 repeat;
uint16 pattern;
};
uint32 uintValue;
} SVGA3dLinePattern;
typedef enum {
SVGA3D_BLENDOP_INVALID = 0,
SVGA3D_BLENDOP_ZERO = 1,
SVGA3D_BLENDOP_ONE = 2,
SVGA3D_BLENDOP_SRCCOLOR = 3,
SVGA3D_BLENDOP_INVSRCCOLOR = 4,
SVGA3D_BLENDOP_SRCALPHA = 5,
SVGA3D_BLENDOP_INVSRCALPHA = 6,
SVGA3D_BLENDOP_DESTALPHA = 7,
SVGA3D_BLENDOP_INVDESTALPHA = 8,
SVGA3D_BLENDOP_DESTCOLOR = 9,
SVGA3D_BLENDOP_INVDESTCOLOR = 10,
SVGA3D_BLENDOP_SRCALPHASAT = 11,
SVGA3D_BLENDOP_BLENDFACTOR = 12,
SVGA3D_BLENDOP_INVBLENDFACTOR = 13,
SVGA3D_BLENDOP_MAX
} SVGA3dBlendOp;
typedef enum {
SVGA3D_BLENDEQ_INVALID = 0,
SVGA3D_BLENDEQ_ADD = 1,
SVGA3D_BLENDEQ_SUBTRACT = 2,
SVGA3D_BLENDEQ_REVSUBTRACT = 3,
SVGA3D_BLENDEQ_MINIMUM = 4,
SVGA3D_BLENDEQ_MAXIMUM = 5,
SVGA3D_BLENDEQ_MAX
} SVGA3dBlendEquation;
typedef enum {
SVGA3D_FRONTWINDING_INVALID = 0,
SVGA3D_FRONTWINDING_CW = 1,
SVGA3D_FRONTWINDING_CCW = 2,
SVGA3D_FRONTWINDING_MAX
} SVGA3dFrontWinding;
typedef enum {
SVGA3D_FACE_INVALID = 0,
SVGA3D_FACE_NONE = 1,
SVGA3D_FACE_FRONT = 2,
SVGA3D_FACE_BACK = 3,
SVGA3D_FACE_FRONT_BACK = 4,
SVGA3D_FACE_MAX
} SVGA3dFace;
/*
* The order and the values should not be changed
*/
typedef enum {
SVGA3D_CMP_INVALID = 0,
SVGA3D_CMP_NEVER = 1,
SVGA3D_CMP_LESS = 2,
SVGA3D_CMP_EQUAL = 3,
SVGA3D_CMP_LESSEQUAL = 4,
SVGA3D_CMP_GREATER = 5,
SVGA3D_CMP_NOTEQUAL = 6,
SVGA3D_CMP_GREATEREQUAL = 7,
SVGA3D_CMP_ALWAYS = 8,
SVGA3D_CMP_MAX
} SVGA3dCmpFunc;
/*
* SVGA3D_FOGFUNC_* specifies the fog equation, or PER_VERTEX which allows
* the fog factor to be specified in the alpha component of the specular
* (a.k.a. secondary) vertex color.
*/
typedef enum {
SVGA3D_FOGFUNC_INVALID = 0,
SVGA3D_FOGFUNC_EXP = 1,
SVGA3D_FOGFUNC_EXP2 = 2,
SVGA3D_FOGFUNC_LINEAR = 3,
SVGA3D_FOGFUNC_PER_VERTEX = 4
} SVGA3dFogFunction;
/*
* SVGA3D_FOGTYPE_* specifies if fog factors are computed on a per-vertex
* or per-pixel basis.
*/
typedef enum {
SVGA3D_FOGTYPE_INVALID = 0,
SVGA3D_FOGTYPE_VERTEX = 1,
SVGA3D_FOGTYPE_PIXEL = 2,
SVGA3D_FOGTYPE_MAX = 3
} SVGA3dFogType;
/*
* SVGA3D_FOGBASE_* selects depth or range-based fog. Depth-based fog is
* computed using the eye Z value of each pixel (or vertex), whereas range-
* based fog is computed using the actual distance (range) to the eye.
*/
typedef enum {
SVGA3D_FOGBASE_INVALID = 0,
SVGA3D_FOGBASE_DEPTHBASED = 1,
SVGA3D_FOGBASE_RANGEBASED = 2,
SVGA3D_FOGBASE_MAX = 3
} SVGA3dFogBase;
typedef enum {
SVGA3D_STENCILOP_INVALID = 0,
SVGA3D_STENCILOP_KEEP = 1,
SVGA3D_STENCILOP_ZERO = 2,
SVGA3D_STENCILOP_REPLACE = 3,
SVGA3D_STENCILOP_INCRSAT = 4,
SVGA3D_STENCILOP_DECRSAT = 5,
SVGA3D_STENCILOP_INVERT = 6,
SVGA3D_STENCILOP_INCR = 7,
SVGA3D_STENCILOP_DECR = 8,
SVGA3D_STENCILOP_MAX
} SVGA3dStencilOp;
typedef enum {
SVGA3D_CLIPPLANE_0 = (1 << 0),
SVGA3D_CLIPPLANE_1 = (1 << 1),
SVGA3D_CLIPPLANE_2 = (1 << 2),
SVGA3D_CLIPPLANE_3 = (1 << 3),
SVGA3D_CLIPPLANE_4 = (1 << 4),
SVGA3D_CLIPPLANE_5 = (1 << 5),
} SVGA3dClipPlanes;
typedef enum {
SVGA3D_CLEAR_COLOR = 0x1,
SVGA3D_CLEAR_DEPTH = 0x2,
SVGA3D_CLEAR_STENCIL = 0x4
} SVGA3dClearFlag;
typedef enum {
SVGA3D_RT_DEPTH = 0,
SVGA3D_RT_STENCIL = 1,
SVGA3D_RT_COLOR0 = 2,
SVGA3D_RT_COLOR1 = 3,
SVGA3D_RT_COLOR2 = 4,
SVGA3D_RT_COLOR3 = 5,
SVGA3D_RT_COLOR4 = 6,
SVGA3D_RT_COLOR5 = 7,
SVGA3D_RT_COLOR6 = 8,
SVGA3D_RT_COLOR7 = 9,
SVGA3D_RT_MAX,
SVGA3D_RT_INVALID = ((uint32)-1),
} SVGA3dRenderTargetType;
#define SVGA3D_MAX_RT_COLOR (SVGA3D_RT_COLOR7 - SVGA3D_RT_COLOR0 + 1)
typedef
union {
struct {
uint32 red : 1;
uint32 green : 1;
uint32 blue : 1;
uint32 alpha : 1;
};
uint32 uintValue;
} SVGA3dColorMask;
typedef enum {
SVGA3D_VBLEND_DISABLE = 0,
SVGA3D_VBLEND_1WEIGHT = 1,
SVGA3D_VBLEND_2WEIGHT = 2,
SVGA3D_VBLEND_3WEIGHT = 3,
} SVGA3dVertexBlendFlags;
typedef enum {
SVGA3D_WRAPCOORD_0 = 1 << 0,
SVGA3D_WRAPCOORD_1 = 1 << 1,
SVGA3D_WRAPCOORD_2 = 1 << 2,
SVGA3D_WRAPCOORD_3 = 1 << 3,
SVGA3D_WRAPCOORD_ALL = 0xF,
} SVGA3dWrapFlags;
/*
* SVGA_3D_CMD_TEXTURESTATE Types. All value types
* must fit in a uint32.
*/
typedef enum {
SVGA3D_TS_INVALID = 0,
SVGA3D_TS_BIND_TEXTURE = 1, /* SVGA3dSurfaceId */
SVGA3D_TS_COLOROP = 2, /* SVGA3dTextureCombiner */
SVGA3D_TS_COLORARG1 = 3, /* SVGA3dTextureArgData */
SVGA3D_TS_COLORARG2 = 4, /* SVGA3dTextureArgData */
SVGA3D_TS_ALPHAOP = 5, /* SVGA3dTextureCombiner */
SVGA3D_TS_ALPHAARG1 = 6, /* SVGA3dTextureArgData */
SVGA3D_TS_ALPHAARG2 = 7, /* SVGA3dTextureArgData */
SVGA3D_TS_ADDRESSU = 8, /* SVGA3dTextureAddress */
SVGA3D_TS_ADDRESSV = 9, /* SVGA3dTextureAddress */
SVGA3D_TS_MIPFILTER = 10, /* SVGA3dTextureFilter */
SVGA3D_TS_MAGFILTER = 11, /* SVGA3dTextureFilter */
SVGA3D_TS_MINFILTER = 12, /* SVGA3dTextureFilter */
SVGA3D_TS_BORDERCOLOR = 13, /* SVGA3dColor */
SVGA3D_TS_TEXCOORDINDEX = 14, /* uint32 */
SVGA3D_TS_TEXTURETRANSFORMFLAGS = 15, /* SVGA3dTexTransformFlags */
SVGA3D_TS_TEXCOORDGEN = 16, /* SVGA3dTextureCoordGen */
SVGA3D_TS_BUMPENVMAT00 = 17, /* float */
SVGA3D_TS_BUMPENVMAT01 = 18, /* float */
SVGA3D_TS_BUMPENVMAT10 = 19, /* float */
SVGA3D_TS_BUMPENVMAT11 = 20, /* float */
SVGA3D_TS_TEXTURE_MIPMAP_LEVEL = 21, /* uint32 */
SVGA3D_TS_TEXTURE_LOD_BIAS = 22, /* float */
SVGA3D_TS_TEXTURE_ANISOTROPIC_LEVEL = 23, /* uint32 */
SVGA3D_TS_ADDRESSW = 24, /* SVGA3dTextureAddress */
/*
* Sampler Gamma Level
*
* Sampler gamma effects the color of samples taken from the sampler. A
* value of 1.0 will produce linear samples. If the value is <= 0.0 the
* gamma value is ignored and a linear space is used.
*/
SVGA3D_TS_GAMMA = 25, /* float */
SVGA3D_TS_BUMPENVLSCALE = 26, /* float */
SVGA3D_TS_BUMPENVLOFFSET = 27, /* float */
SVGA3D_TS_COLORARG0 = 28, /* SVGA3dTextureArgData */
SVGA3D_TS_ALPHAARG0 = 29, /* SVGA3dTextureArgData */
SVGA3D_TS_MAX
} SVGA3dTextureStateName;
typedef enum {
SVGA3D_TC_INVALID = 0,
SVGA3D_TC_DISABLE = 1,
SVGA3D_TC_SELECTARG1 = 2,
SVGA3D_TC_SELECTARG2 = 3,
SVGA3D_TC_MODULATE = 4,
SVGA3D_TC_ADD = 5,
SVGA3D_TC_ADDSIGNED = 6,
SVGA3D_TC_SUBTRACT = 7,
SVGA3D_TC_BLENDTEXTUREALPHA = 8,
SVGA3D_TC_BLENDDIFFUSEALPHA = 9,
SVGA3D_TC_BLENDCURRENTALPHA = 10,
SVGA3D_TC_BLENDFACTORALPHA = 11,
SVGA3D_TC_MODULATE2X = 12,
SVGA3D_TC_MODULATE4X = 13,
SVGA3D_TC_DSDT = 14,
SVGA3D_TC_DOTPRODUCT3 = 15,
SVGA3D_TC_BLENDTEXTUREALPHAPM = 16,
SVGA3D_TC_ADDSIGNED2X = 17,
SVGA3D_TC_ADDSMOOTH = 18,
SVGA3D_TC_PREMODULATE = 19,
SVGA3D_TC_MODULATEALPHA_ADDCOLOR = 20,
SVGA3D_TC_MODULATECOLOR_ADDALPHA = 21,
SVGA3D_TC_MODULATEINVALPHA_ADDCOLOR = 22,
SVGA3D_TC_MODULATEINVCOLOR_ADDALPHA = 23,
SVGA3D_TC_BUMPENVMAPLUMINANCE = 24,
SVGA3D_TC_MULTIPLYADD = 25,
SVGA3D_TC_LERP = 26,
SVGA3D_TC_MAX
} SVGA3dTextureCombiner;
#define SVGA3D_TC_CAP_BIT(svga3d_tc_op) (svga3d_tc_op ? (1 << (svga3d_tc_op - 1)) : 0)
typedef enum {
SVGA3D_TEX_ADDRESS_INVALID = 0,
SVGA3D_TEX_ADDRESS_WRAP = 1,
SVGA3D_TEX_ADDRESS_MIRROR = 2,
SVGA3D_TEX_ADDRESS_CLAMP = 3,
SVGA3D_TEX_ADDRESS_BORDER = 4,
SVGA3D_TEX_ADDRESS_MIRRORONCE = 5,
SVGA3D_TEX_ADDRESS_EDGE = 6,
SVGA3D_TEX_ADDRESS_MAX
} SVGA3dTextureAddress;
/*
* SVGA3D_TEX_FILTER_NONE as the minification filter means mipmapping is
* disabled, and the rasterizer should use the magnification filter instead.
*/
typedef enum {
SVGA3D_TEX_FILTER_NONE = 0,
SVGA3D_TEX_FILTER_NEAREST = 1,
SVGA3D_TEX_FILTER_LINEAR = 2,
SVGA3D_TEX_FILTER_ANISOTROPIC = 3,
SVGA3D_TEX_FILTER_FLATCUBIC = 4, // Deprecated, not implemented
SVGA3D_TEX_FILTER_GAUSSIANCUBIC = 5, // Deprecated, not implemented
SVGA3D_TEX_FILTER_PYRAMIDALQUAD = 6, // Not currently implemented
SVGA3D_TEX_FILTER_GAUSSIANQUAD = 7, // Not currently implemented
SVGA3D_TEX_FILTER_MAX
} SVGA3dTextureFilter;
typedef enum {
SVGA3D_TEX_TRANSFORM_OFF = 0,
SVGA3D_TEX_TRANSFORM_S = (1 << 0),
SVGA3D_TEX_TRANSFORM_T = (1 << 1),
SVGA3D_TEX_TRANSFORM_R = (1 << 2),
SVGA3D_TEX_TRANSFORM_Q = (1 << 3),
SVGA3D_TEX_PROJECTED = (1 << 15),
} SVGA3dTexTransformFlags;
typedef enum {
SVGA3D_TEXCOORD_GEN_OFF = 0,
SVGA3D_TEXCOORD_GEN_EYE_POSITION = 1,
SVGA3D_TEXCOORD_GEN_EYE_NORMAL = 2,
SVGA3D_TEXCOORD_GEN_REFLECTIONVECTOR = 3,
SVGA3D_TEXCOORD_GEN_SPHERE = 4,
SVGA3D_TEXCOORD_GEN_MAX
} SVGA3dTextureCoordGen;
/*
* Texture argument constants for texture combiner
*/
typedef enum {
SVGA3D_TA_INVALID = 0,
SVGA3D_TA_CONSTANT = 1,
SVGA3D_TA_PREVIOUS = 2,
SVGA3D_TA_DIFFUSE = 3,
SVGA3D_TA_TEXTURE = 4,
SVGA3D_TA_SPECULAR = 5,
SVGA3D_TA_MAX
} SVGA3dTextureArgData;
#define SVGA3D_TM_MASK_LEN 4
/* Modifiers for texture argument constants defined above. */
typedef enum {
SVGA3D_TM_NONE = 0,
SVGA3D_TM_ALPHA = (1 << SVGA3D_TM_MASK_LEN),
SVGA3D_TM_ONE_MINUS = (2 << SVGA3D_TM_MASK_LEN),
} SVGA3dTextureArgModifier;
#define SVGA3D_INVALID_ID ((uint32)-1)
#define SVGA3D_MAX_CLIP_PLANES 6
/*
* This is the limit to the number of fixed-function texture
* transforms and texture coordinates we can support. It does *not*
* correspond to the number of texture image units (samplers) we
* support!
*/
#define SVGA3D_MAX_TEXTURE_COORDS 8
/*
* Vertex declarations
*
* Notes:
*
* SVGA3D_DECLUSAGE_POSITIONT is for pre-transformed vertices. If you
* draw with any POSITIONT vertex arrays, the programmable vertex
* pipeline will be implicitly disabled. Drawing will take place as if
* no vertex shader was bound.
*/
typedef enum {
SVGA3D_DECLUSAGE_POSITION = 0,
SVGA3D_DECLUSAGE_BLENDWEIGHT, // 1
SVGA3D_DECLUSAGE_BLENDINDICES, // 2
SVGA3D_DECLUSAGE_NORMAL, // 3
SVGA3D_DECLUSAGE_PSIZE, // 4
SVGA3D_DECLUSAGE_TEXCOORD, // 5
SVGA3D_DECLUSAGE_TANGENT, // 6
SVGA3D_DECLUSAGE_BINORMAL, // 7
SVGA3D_DECLUSAGE_TESSFACTOR, // 8
SVGA3D_DECLUSAGE_POSITIONT, // 9
SVGA3D_DECLUSAGE_COLOR, // 10
SVGA3D_DECLUSAGE_FOG, // 11
SVGA3D_DECLUSAGE_DEPTH, // 12
SVGA3D_DECLUSAGE_SAMPLE, // 13
SVGA3D_DECLUSAGE_MAX
} SVGA3dDeclUsage;
typedef enum {
SVGA3D_DECLMETHOD_DEFAULT = 0,
SVGA3D_DECLMETHOD_PARTIALU,
SVGA3D_DECLMETHOD_PARTIALV,
SVGA3D_DECLMETHOD_CROSSUV, // Normal
SVGA3D_DECLMETHOD_UV,
SVGA3D_DECLMETHOD_LOOKUP, // Lookup a displacement map
SVGA3D_DECLMETHOD_LOOKUPPRESAMPLED, // Lookup a pre-sampled displacement map
} SVGA3dDeclMethod;
typedef enum {
SVGA3D_DECLTYPE_FLOAT1 = 0,
SVGA3D_DECLTYPE_FLOAT2 = 1,
SVGA3D_DECLTYPE_FLOAT3 = 2,
SVGA3D_DECLTYPE_FLOAT4 = 3,
SVGA3D_DECLTYPE_D3DCOLOR = 4,
SVGA3D_DECLTYPE_UBYTE4 = 5,
SVGA3D_DECLTYPE_SHORT2 = 6,
SVGA3D_DECLTYPE_SHORT4 = 7,
SVGA3D_DECLTYPE_UBYTE4N = 8,
SVGA3D_DECLTYPE_SHORT2N = 9,
SVGA3D_DECLTYPE_SHORT4N = 10,
SVGA3D_DECLTYPE_USHORT2N = 11,
SVGA3D_DECLTYPE_USHORT4N = 12,
SVGA3D_DECLTYPE_UDEC3 = 13,
SVGA3D_DECLTYPE_DEC3N = 14,
SVGA3D_DECLTYPE_FLOAT16_2 = 15,
SVGA3D_DECLTYPE_FLOAT16_4 = 16,
SVGA3D_DECLTYPE_MAX,
} SVGA3dDeclType;
/*
* This structure is used for the divisor for geometry instancing;
* it's a direct translation of the Direct3D equivalent.
*/
typedef union {
struct {
/*
* For index data, this number represents the number of instances to draw.
* For instance data, this number represents the number of
* instances/vertex in this stream
*/
uint32 count : 30;
/*
* This is 1 if this is supposed to be the data that is repeated for
* every instance.
*/
uint32 indexedData : 1;
/*
* This is 1 if this is supposed to be the per-instance data.
*/
uint32 instanceData : 1;
};
uint32 value;
} SVGA3dVertexDivisor;
typedef enum {
SVGA3D_PRIMITIVE_INVALID = 0,
SVGA3D_PRIMITIVE_TRIANGLELIST = 1,
SVGA3D_PRIMITIVE_POINTLIST = 2,
SVGA3D_PRIMITIVE_LINELIST = 3,
SVGA3D_PRIMITIVE_LINESTRIP = 4,
SVGA3D_PRIMITIVE_TRIANGLESTRIP = 5,
SVGA3D_PRIMITIVE_TRIANGLEFAN = 6,
SVGA3D_PRIMITIVE_MAX
} SVGA3dPrimitiveType;
typedef enum {
SVGA3D_COORDINATE_INVALID = 0,
SVGA3D_COORDINATE_LEFTHANDED = 1,
SVGA3D_COORDINATE_RIGHTHANDED = 2,
SVGA3D_COORDINATE_MAX
} SVGA3dCoordinateType;
typedef enum {
SVGA3D_TRANSFORM_INVALID = 0,
SVGA3D_TRANSFORM_WORLD = 1,
SVGA3D_TRANSFORM_VIEW = 2,
SVGA3D_TRANSFORM_PROJECTION = 3,
SVGA3D_TRANSFORM_TEXTURE0 = 4,
SVGA3D_TRANSFORM_TEXTURE1 = 5,
SVGA3D_TRANSFORM_TEXTURE2 = 6,
SVGA3D_TRANSFORM_TEXTURE3 = 7,
SVGA3D_TRANSFORM_TEXTURE4 = 8,
SVGA3D_TRANSFORM_TEXTURE5 = 9,
SVGA3D_TRANSFORM_TEXTURE6 = 10,
SVGA3D_TRANSFORM_TEXTURE7 = 11,
SVGA3D_TRANSFORM_WORLD1 = 12,
SVGA3D_TRANSFORM_WORLD2 = 13,
SVGA3D_TRANSFORM_WORLD3 = 14,
SVGA3D_TRANSFORM_MAX
} SVGA3dTransformType;
typedef enum {
SVGA3D_LIGHTTYPE_INVALID = 0,
SVGA3D_LIGHTTYPE_POINT = 1,
SVGA3D_LIGHTTYPE_SPOT1 = 2, /* 1-cone, in degrees */
SVGA3D_LIGHTTYPE_SPOT2 = 3, /* 2-cone, in radians */
SVGA3D_LIGHTTYPE_DIRECTIONAL = 4,
SVGA3D_LIGHTTYPE_MAX
} SVGA3dLightType;
typedef enum {
SVGA3D_CUBEFACE_POSX = 0,
SVGA3D_CUBEFACE_NEGX = 1,
SVGA3D_CUBEFACE_POSY = 2,
SVGA3D_CUBEFACE_NEGY = 3,
SVGA3D_CUBEFACE_POSZ = 4,
SVGA3D_CUBEFACE_NEGZ = 5,
} SVGA3dCubeFace;
typedef enum {
SVGA3D_SHADERTYPE_COMPILED_DX8 = 0,
SVGA3D_SHADERTYPE_VS = 1,
SVGA3D_SHADERTYPE_PS = 2,
SVGA3D_SHADERTYPE_MAX
} SVGA3dShaderType;
typedef enum {
SVGA3D_CONST_TYPE_FLOAT = 0,
SVGA3D_CONST_TYPE_INT = 1,
SVGA3D_CONST_TYPE_BOOL = 2,
} SVGA3dShaderConstType;
#define SVGA3D_MAX_SURFACE_FACES 6
typedef enum {
SVGA3D_STRETCH_BLT_POINT = 0,
SVGA3D_STRETCH_BLT_LINEAR = 1,
SVGA3D_STRETCH_BLT_MAX
} SVGA3dStretchBltMode;
typedef enum {
SVGA3D_QUERYTYPE_OCCLUSION = 0,
SVGA3D_QUERYTYPE_MAX
} SVGA3dQueryType;
typedef enum {
SVGA3D_QUERYSTATE_PENDING = 0, /* Waiting on the host (set by guest) */
SVGA3D_QUERYSTATE_SUCCEEDED = 1, /* Completed successfully (set by host) */
SVGA3D_QUERYSTATE_FAILED = 2, /* Completed unsuccessfully (set by host) */
SVGA3D_QUERYSTATE_NEW = 3, /* Never submitted (For guest use only) */
} SVGA3dQueryState;
typedef enum {
SVGA3D_WRITE_HOST_VRAM = 1,
SVGA3D_READ_HOST_VRAM = 2,
} SVGA3dTransferType;
/*
* The maximum number vertex arrays we're guaranteed to support in
* SVGA_3D_CMD_DRAWPRIMITIVES.
*/
#define SVGA3D_MAX_VERTEX_ARRAYS 32
/*
* Identifiers for commands in the command FIFO.
*
* IDs between 1000 and 1039 (inclusive) were used by obsolete versions of
* the SVGA3D protocol and remain reserved; they should not be used in the
* future.
*
* IDs between 1040 and 1999 (inclusive) are available for use by the
* current SVGA3D protocol.
*
* FIFO clients other than SVGA3D should stay below 1000, or at 2000
* and up.
*/
#define SVGA_3D_CMD_LEGACY_BASE 1000
#define SVGA_3D_CMD_BASE 1040
#define SVGA_3D_CMD_SURFACE_DEFINE SVGA_3D_CMD_BASE + 0
#define SVGA_3D_CMD_SURFACE_DESTROY SVGA_3D_CMD_BASE + 1
#define SVGA_3D_CMD_SURFACE_COPY SVGA_3D_CMD_BASE + 2
#define SVGA_3D_CMD_SURFACE_STRETCHBLT SVGA_3D_CMD_BASE + 3
#define SVGA_3D_CMD_SURFACE_DMA SVGA_3D_CMD_BASE + 4
#define SVGA_3D_CMD_CONTEXT_DEFINE SVGA_3D_CMD_BASE + 5
#define SVGA_3D_CMD_CONTEXT_DESTROY SVGA_3D_CMD_BASE + 6
#define SVGA_3D_CMD_SETTRANSFORM SVGA_3D_CMD_BASE + 7
#define SVGA_3D_CMD_SETZRANGE SVGA_3D_CMD_BASE + 8
#define SVGA_3D_CMD_SETRENDERSTATE SVGA_3D_CMD_BASE + 9
#define SVGA_3D_CMD_SETRENDERTARGET SVGA_3D_CMD_BASE + 10
#define SVGA_3D_CMD_SETTEXTURESTATE SVGA_3D_CMD_BASE + 11
#define SVGA_3D_CMD_SETMATERIAL SVGA_3D_CMD_BASE + 12
#define SVGA_3D_CMD_SETLIGHTDATA SVGA_3D_CMD_BASE + 13
#define SVGA_3D_CMD_SETLIGHTENABLED SVGA_3D_CMD_BASE + 14
#define SVGA_3D_CMD_SETVIEWPORT SVGA_3D_CMD_BASE + 15
#define SVGA_3D_CMD_SETCLIPPLANE SVGA_3D_CMD_BASE + 16
#define SVGA_3D_CMD_CLEAR SVGA_3D_CMD_BASE + 17
#define SVGA_3D_CMD_PRESENT SVGA_3D_CMD_BASE + 18 // Deprecated
#define SVGA_3D_CMD_SHADER_DEFINE SVGA_3D_CMD_BASE + 19
#define SVGA_3D_CMD_SHADER_DESTROY SVGA_3D_CMD_BASE + 20
#define SVGA_3D_CMD_SET_SHADER SVGA_3D_CMD_BASE + 21
#define SVGA_3D_CMD_SET_SHADER_CONST SVGA_3D_CMD_BASE + 22
#define SVGA_3D_CMD_DRAW_PRIMITIVES SVGA_3D_CMD_BASE + 23
#define SVGA_3D_CMD_SETSCISSORRECT SVGA_3D_CMD_BASE + 24
#define SVGA_3D_CMD_BEGIN_QUERY SVGA_3D_CMD_BASE + 25
#define SVGA_3D_CMD_END_QUERY SVGA_3D_CMD_BASE + 26
#define SVGA_3D_CMD_WAIT_FOR_QUERY SVGA_3D_CMD_BASE + 27
#define SVGA_3D_CMD_PRESENT_READBACK SVGA_3D_CMD_BASE + 28 // Deprecated
#define SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN SVGA_3D_CMD_BASE + 29
#define SVGA_3D_CMD_MAX SVGA_3D_CMD_BASE + 30
#define SVGA_3D_CMD_FUTURE_MAX 2000
/*
* Common substructures used in multiple FIFO commands:
*/
typedef struct {
union {
struct {
uint16 function; // SVGA3dFogFunction
uint8 type; // SVGA3dFogType
uint8 base; // SVGA3dFogBase
};
uint32 uintValue;
};
} SVGA3dFogMode;
/*
* Uniquely identify one image (a 1D/2D/3D array) from a surface. This
* is a surface ID as well as face/mipmap indices.
*/
typedef
struct SVGA3dSurfaceImageId {
uint32 sid;
uint32 face;
uint32 mipmap;
} SVGA3dSurfaceImageId;
typedef
struct SVGA3dGuestImage {
SVGAGuestPtr ptr;
/*
* A note on interpretation of pitch: This value of pitch is the
* number of bytes between vertically adjacent image
* blocks. Normally this is the number of bytes between the first
* pixel of two adjacent scanlines. With compressed textures,
* however, this may represent the number of bytes between
* compression blocks rather than between rows of pixels.
*
* XXX: Compressed textures currently must be tightly packed in guest memory.
*
* If the image is 1-dimensional, pitch is ignored.
*
* If 'pitch' is zero, the SVGA3D device calculates a pitch value
* assuming each row of blocks is tightly packed.
*/
uint32 pitch;
} SVGA3dGuestImage;
/*
* FIFO command format definitions:
*/
/*
* The data size header following cmdNum for every 3d command
*/
typedef
struct {
uint32 id;
uint32 size;
} SVGA3dCmdHeader;
/*
* A surface is a hierarchy of host VRAM surfaces: 1D, 2D, or 3D, with
* optional mipmaps and cube faces.
*/
typedef
struct {
uint32 width;
uint32 height;
uint32 depth;
} SVGA3dSize;
typedef enum {
SVGA3D_SURFACE_CUBEMAP = (1 << 0),
SVGA3D_SURFACE_HINT_STATIC = (1 << 1),
SVGA3D_SURFACE_HINT_DYNAMIC = (1 << 2),
SVGA3D_SURFACE_HINT_INDEXBUFFER = (1 << 3),
SVGA3D_SURFACE_HINT_VERTEXBUFFER = (1 << 4),
SVGA3D_SURFACE_HINT_TEXTURE = (1 << 5),
SVGA3D_SURFACE_HINT_RENDERTARGET = (1 << 6),
SVGA3D_SURFACE_HINT_DEPTHSTENCIL = (1 << 7),
SVGA3D_SURFACE_HINT_WRITEONLY = (1 << 8),
} SVGA3dSurfaceFlags;
typedef
struct {
uint32 numMipLevels;
} SVGA3dSurfaceFace;
typedef
struct {
uint32 sid;
SVGA3dSurfaceFlags surfaceFlags;
SVGA3dSurfaceFormat format;
SVGA3dSurfaceFace face[SVGA3D_MAX_SURFACE_FACES];
/*
* Followed by an SVGA3dSize structure for each mip level in each face.
*
* A note on surface sizes: Sizes are always specified in pixels,
* even if the true surface size is not a multiple of the minimum
* block size of the surface's format. For example, a 3x3x1 DXT1
* compressed texture would actually be stored as a 4x4x1 image in
* memory.
*/
} SVGA3dCmdDefineSurface; /* SVGA_3D_CMD_SURFACE_DEFINE */
typedef
struct {
uint32 sid;
} SVGA3dCmdDestroySurface; /* SVGA_3D_CMD_SURFACE_DESTROY */
typedef
struct {
uint32 cid;
} SVGA3dCmdDefineContext; /* SVGA_3D_CMD_CONTEXT_DEFINE */
typedef
struct {
uint32 cid;
} SVGA3dCmdDestroyContext; /* SVGA_3D_CMD_CONTEXT_DESTROY */
typedef
struct {
uint32 cid;
SVGA3dClearFlag clearFlag;
uint32 color;
float depth;
uint32 stencil;
/* Followed by variable number of SVGA3dRect structures */
} SVGA3dCmdClear; /* SVGA_3D_CMD_CLEAR */
typedef
struct SVGA3dCopyRect {
uint32 x;
uint32 y;
uint32 w;
uint32 h;
uint32 srcx;
uint32 srcy;
} SVGA3dCopyRect;
typedef
struct SVGA3dCopyBox {
uint32 x;
uint32 y;
uint32 z;
uint32 w;
uint32 h;
uint32 d;
uint32 srcx;
uint32 srcy;
uint32 srcz;
} SVGA3dCopyBox;
typedef
struct {
uint32 x;
uint32 y;
uint32 w;
uint32 h;
} SVGA3dRect;
typedef
struct {
uint32 x;
uint32 y;
uint32 z;
uint32 w;
uint32 h;
uint32 d;
} SVGA3dBox;
typedef
struct {
uint32 x;
uint32 y;
uint32 z;
} SVGA3dPoint;
typedef
struct {
SVGA3dLightType type;
SVGA3dBool inWorldSpace;
float diffuse[4];
float specular[4];
float ambient[4];
float position[4];
float direction[4];
float range;
float falloff;
float attenuation0;
float attenuation1;
float attenuation2;
float theta;
float phi;
} SVGA3dLightData;
typedef
struct {
uint32 sid;
/* Followed by variable number of SVGA3dCopyRect structures */
} SVGA3dCmdPresent; /* SVGA_3D_CMD_PRESENT */
typedef
struct {
SVGA3dRenderStateName state;
union {
uint32 uintValue;
float floatValue;
};
} SVGA3dRenderState;
typedef
struct {
uint32 cid;
/* Followed by variable number of SVGA3dRenderState structures */
} SVGA3dCmdSetRenderState; /* SVGA_3D_CMD_SETRENDERSTATE */
typedef
struct {
uint32 cid;
SVGA3dRenderTargetType type;
SVGA3dSurfaceImageId target;
} SVGA3dCmdSetRenderTarget; /* SVGA_3D_CMD_SETRENDERTARGET */
typedef
struct {
SVGA3dSurfaceImageId src;
SVGA3dSurfaceImageId dest;
/* Followed by variable number of SVGA3dCopyBox structures */
} SVGA3dCmdSurfaceCopy; /* SVGA_3D_CMD_SURFACE_COPY */
typedef
struct {
SVGA3dSurfaceImageId src;
SVGA3dSurfaceImageId dest;
SVGA3dBox boxSrc;
SVGA3dBox boxDest;
SVGA3dStretchBltMode mode;
} SVGA3dCmdSurfaceStretchBlt; /* SVGA_3D_CMD_SURFACE_STRETCHBLT */
typedef
struct {
/*
* If the discard flag is present in a surface DMA operation, the host may
* discard the contents of the current mipmap level and face of the target
* surface before applying the surface DMA contents.
*/
uint32 discard : 1;
/*
* If the unsynchronized flag is present, the host may perform this upload
* without syncing to pending reads on this surface.
*/
uint32 unsynchronized : 1;
/*
* Guests *MUST* set the reserved bits to 0 before submitting the command
* suffix as future flags may occupy these bits.
*/
uint32 reserved : 30;
} SVGA3dSurfaceDMAFlags;
typedef
struct {
SVGA3dGuestImage guest;
SVGA3dSurfaceImageId host;
SVGA3dTransferType transfer;
/*
* Followed by variable number of SVGA3dCopyBox structures. For consistency
* in all clipping logic and coordinate translation, we define the
* "source" in each copyBox as the guest image and the
* "destination" as the host image, regardless of transfer
* direction.
*
* For efficiency, the SVGA3D device is free to copy more data than
* specified. For example, it may round copy boxes outwards such
* that they lie on particular alignment boundaries.
*/
} SVGA3dCmdSurfaceDMA; /* SVGA_3D_CMD_SURFACE_DMA */
/*
* SVGA3dCmdSurfaceDMASuffix --
*
* This is a command suffix that will appear after a SurfaceDMA command in
* the FIFO. It contains some extra information that hosts may use to
* optimize performance or protect the guest. This suffix exists to preserve
* backwards compatibility while also allowing for new functionality to be
* implemented.
*/
typedef
struct {
uint32 suffixSize;
/*
* The maximum offset is used to determine the maximum offset from the
* guestPtr base address that will be accessed or written to during this
* surfaceDMA. If the suffix is supported, the host will respect this
* boundary while performing surface DMAs.
*
* Defaults to MAX_UINT32
*/
uint32 maximumOffset;
/*
* A set of flags that describes optimizations that the host may perform
* while performing this surface DMA operation. The guest should never rely
* on behaviour that is different when these flags are set for correctness.
*
* Defaults to 0
*/
SVGA3dSurfaceDMAFlags flags;
} SVGA3dCmdSurfaceDMASuffix;
/*
* SVGA_3D_CMD_DRAW_PRIMITIVES --
*
* This command is the SVGA3D device's generic drawing entry point.
* It can draw multiple ranges of primitives, optionally using an
* index buffer, using an arbitrary collection of vertex buffers.
*
* Each SVGA3dVertexDecl defines a distinct vertex array to bind
* during this draw call. The declarations specify which surface
* the vertex data lives in, what that vertex data is used for,
* and how to interpret it.
*
* Each SVGA3dPrimitiveRange defines a collection of primitives
* to render using the same vertex arrays. An index buffer is
* optional.
*/
typedef
struct {
/*
* A range hint is an optional specification for the range of indices
* in an SVGA3dArray that will be used. If 'last' is zero, it is assumed
* that the entire array will be used.
*
* These are only hints. The SVGA3D device may use them for
* performance optimization if possible, but it's also allowed to
* ignore these values.
*/
uint32 first;
uint32 last;
} SVGA3dArrayRangeHint;
typedef
struct {
/*
* Define the origin and shape of a vertex or index array. Both
* 'offset' and 'stride' are in bytes. The provided surface will be
* reinterpreted as a flat array of bytes in the same format used
* by surface DMA operations. To avoid unnecessary conversions, the
* surface should be created with the SVGA3D_BUFFER format.
*
* Index 0 in the array starts 'offset' bytes into the surface.
* Index 1 begins at byte 'offset + stride', etc. Array indices may
* not be negative.
*/
uint32 surfaceId;
uint32 offset;
uint32 stride;
} SVGA3dArray;
typedef
struct {
/*
* Describe a vertex array's data type, and define how it is to be
* used by the fixed function pipeline or the vertex shader. It
* isn't useful to have two VertexDecls with the same
* VertexArrayIdentity in one draw call.
*/
SVGA3dDeclType type;
SVGA3dDeclMethod method;
SVGA3dDeclUsage usage;
uint32 usageIndex;
} SVGA3dVertexArrayIdentity;
typedef
struct {
SVGA3dVertexArrayIdentity identity;
SVGA3dArray array;
SVGA3dArrayRangeHint rangeHint;
} SVGA3dVertexDecl;
typedef
struct {
/*
* Define a group of primitives to render, from sequential indices.
*
* The value of 'primitiveType' and 'primitiveCount' imply the
* total number of vertices that will be rendered.
*/
SVGA3dPrimitiveType primType;
uint32 primitiveCount;
/*
* Optional index buffer. If indexArray.surfaceId is
* SVGA3D_INVALID_ID, we render without an index buffer. Rendering
* without an index buffer is identical to rendering with an index
* buffer containing the sequence [0, 1, 2, 3, ...].
*
* If an index buffer is in use, indexWidth specifies the width in
* bytes of each index value. It must be less than or equal to
* indexArray.stride.
*
* (Currently, the SVGA3D device requires index buffers to be tightly
* packed. In other words, indexWidth == indexArray.stride)
*/
SVGA3dArray indexArray;
uint32 indexWidth;
/*
* Optional index bias. This number is added to all indices from
* indexArray before they are used as vertex array indices. This
* can be used in multiple ways:
*
* - When not using an indexArray, this bias can be used to
* specify where in the vertex arrays to begin rendering.
*
* - A positive number here is equivalent to increasing the
* offset in each vertex array.
*
* - A negative number can be used to render using a small
* vertex array and an index buffer that contains large
* values. This may be used by some applications that
* crop a vertex buffer without modifying their index
* buffer.
*
* Note that rendering with a negative bias value may be slower and
* use more memory than rendering with a positive or zero bias.
*/
int32 indexBias;
} SVGA3dPrimitiveRange;
typedef
struct {
uint32 cid;
uint32 numVertexDecls;
uint32 numRanges;
/*
* There are two variable size arrays after the
* SVGA3dCmdDrawPrimitives structure. In order,
* they are:
*
* 1. SVGA3dVertexDecl, quantity 'numVertexDecls'
* 2. SVGA3dPrimitiveRange, quantity 'numRanges'
* 3. Optionally, SVGA3dVertexDivisor, quantity 'numVertexDecls' (contains
* the frequency divisor for this the corresponding vertex decl)
*/
} SVGA3dCmdDrawPrimitives; /* SVGA_3D_CMD_DRAWPRIMITIVES */
typedef
struct {
uint32 stage;
SVGA3dTextureStateName name;
union {
uint32 value;
float floatValue;
};
} SVGA3dTextureState;
typedef
struct {
uint32 cid;
/* Followed by variable number of SVGA3dTextureState structures */
} SVGA3dCmdSetTextureState; /* SVGA_3D_CMD_SETTEXTURESTATE */
typedef
struct {
uint32 cid;
SVGA3dTransformType type;
float matrix[16];
} SVGA3dCmdSetTransform; /* SVGA_3D_CMD_SETTRANSFORM */
typedef
struct {
float min;
float max;
} SVGA3dZRange;
typedef
struct {
uint32 cid;
SVGA3dZRange zRange;
} SVGA3dCmdSetZRange; /* SVGA_3D_CMD_SETZRANGE */
typedef
struct {
float diffuse[4];
float ambient[4];
float specular[4];
float emissive[4];
float shininess;
} SVGA3dMaterial;
typedef
struct {
uint32 cid;
SVGA3dFace face;
SVGA3dMaterial material;
} SVGA3dCmdSetMaterial; /* SVGA_3D_CMD_SETMATERIAL */
typedef
struct {
uint32 cid;
uint32 index;
SVGA3dLightData data;
} SVGA3dCmdSetLightData; /* SVGA_3D_CMD_SETLIGHTDATA */
typedef
struct {
uint32 cid;
uint32 index;
uint32 enabled;
} SVGA3dCmdSetLightEnabled; /* SVGA_3D_CMD_SETLIGHTENABLED */
typedef
struct {
uint32 cid;
SVGA3dRect rect;
} SVGA3dCmdSetViewport; /* SVGA_3D_CMD_SETVIEWPORT */
typedef
struct {
uint32 cid;
SVGA3dRect rect;
} SVGA3dCmdSetScissorRect; /* SVGA_3D_CMD_SETSCISSORRECT */
typedef
struct {
uint32 cid;
uint32 index;
float plane[4];
} SVGA3dCmdSetClipPlane; /* SVGA_3D_CMD_SETCLIPPLANE */
typedef
struct {
uint32 cid;
uint32 shid;
SVGA3dShaderType type;
/* Followed by variable number of DWORDs for shader bycode */
} SVGA3dCmdDefineShader; /* SVGA_3D_CMD_SHADER_DEFINE */
typedef
struct {
uint32 cid;
uint32 shid;
SVGA3dShaderType type;
} SVGA3dCmdDestroyShader; /* SVGA_3D_CMD_SHADER_DESTROY */
typedef
struct {
uint32 cid;
uint32 reg; /* register number */
SVGA3dShaderType type;
SVGA3dShaderConstType ctype;
uint32 values[4];
} SVGA3dCmdSetShaderConst; /* SVGA_3D_CMD_SET_SHADER_CONST */
typedef
struct {
uint32 cid;
SVGA3dShaderType type;
uint32 shid;
} SVGA3dCmdSetShader; /* SVGA_3D_CMD_SET_SHADER */
typedef
struct {
uint32 cid;
SVGA3dQueryType type;
} SVGA3dCmdBeginQuery; /* SVGA_3D_CMD_BEGIN_QUERY */
typedef
struct {
uint32 cid;
SVGA3dQueryType type;
SVGAGuestPtr guestResult; /* Points to an SVGA3dQueryResult structure */
} SVGA3dCmdEndQuery; /* SVGA_3D_CMD_END_QUERY */
typedef
struct {
uint32 cid; /* Same parameters passed to END_QUERY */
SVGA3dQueryType type;
SVGAGuestPtr guestResult;
} SVGA3dCmdWaitForQuery; /* SVGA_3D_CMD_WAIT_FOR_QUERY */
typedef
struct {
uint32 totalSize; /* Set by guest before query is ended. */
SVGA3dQueryState state; /* Set by host or guest. See SVGA3dQueryState. */
union { /* Set by host on exit from PENDING state */
uint32 result32;
};
} SVGA3dQueryResult;
/*
* SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN --
*
* This is a blit from an SVGA3D surface to a Screen Object. Just
* like GMR-to-screen blits, this blit may be directed at a
* specific screen or to the virtual coordinate space.
*
* The blit copies from a rectangular region of an SVGA3D surface
* image to a rectangular region of a screen or screens.
*
* This command takes an optional variable-length list of clipping
* rectangles after the body of the command. If no rectangles are
* specified, there is no clipping region. The entire destRect is
* drawn to. If one or more rectangles are included, they describe
* a clipping region. The clip rectangle coordinates are measured
* relative to the top-left corner of destRect.
*
* This clipping region serves multiple purposes:
*
* - It can be used to perform an irregularly shaped blit more
* efficiently than by issuing many separate blit commands.
*
* - It is equivalent to allowing blits with non-integer
* source coordinates. You could blit just one half-pixel
* of a source, for example, by specifying a larger
* destination rectangle than you need, then removing
* part of it using a clip rectangle.
*
* Availability:
* SVGA_FIFO_CAP_SCREEN_OBJECT
*
* Limitations:
*
* - Currently, no backend supports blits from a mipmap or face
* other than the first one.
*/
typedef
struct {
SVGA3dSurfaceImageId srcImage;
SVGASignedRect srcRect;
uint32 destScreenId; /* Screen ID or SVGA_ID_INVALID for virt. coords */
SVGASignedRect destRect; /* Supports scaling if src/rest different size */
/* Clipping: zero or more SVGASignedRects follow */
} SVGA3dCmdBlitSurfaceToScreen; /* SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN */
/*
* Capability query index.
*
* Notes:
*
* 1. SVGA3D_DEVCAP_MAX_TEXTURES reflects the maximum number of
* fixed-function texture units available. Each of these units
* work in both FFP and Shader modes, and they support texture
* transforms and texture coordinates. The host may have additional
* texture image units that are only usable with shaders.
*
* 2. The BUFFER_FORMAT capabilities are deprecated, and they always
* return TRUE. Even on physical hardware that does not support
* these formats natively, the SVGA3D device will provide an emulation
* which should be invisible to the guest OS.
*
* In general, the SVGA3D device should support any operation on
* any surface format, it just may perform some of these
* operations in software depending on the capabilities of the
* available physical hardware.
*
* XXX: In the future, we will add capabilities that describe in
* detail what formats are supported in hardware for what kinds
* of operations.
*/
typedef enum {
SVGA3D_DEVCAP_3D = 0,
SVGA3D_DEVCAP_MAX_LIGHTS = 1,
SVGA3D_DEVCAP_MAX_TEXTURES = 2, /* See note (1) */
SVGA3D_DEVCAP_MAX_CLIP_PLANES = 3,
SVGA3D_DEVCAP_VERTEX_SHADER_VERSION = 4,
SVGA3D_DEVCAP_VERTEX_SHADER = 5,
SVGA3D_DEVCAP_FRAGMENT_SHADER_VERSION = 6,
SVGA3D_DEVCAP_FRAGMENT_SHADER = 7,
SVGA3D_DEVCAP_MAX_RENDER_TARGETS = 8,
SVGA3D_DEVCAP_S23E8_TEXTURES = 9,
SVGA3D_DEVCAP_S10E5_TEXTURES = 10,
SVGA3D_DEVCAP_MAX_FIXED_VERTEXBLEND = 11,
SVGA3D_DEVCAP_D16_BUFFER_FORMAT = 12, /* See note (2) */
SVGA3D_DEVCAP_D24S8_BUFFER_FORMAT = 13, /* See note (2) */
SVGA3D_DEVCAP_D24X8_BUFFER_FORMAT = 14, /* See note (2) */
SVGA3D_DEVCAP_QUERY_TYPES = 15,
SVGA3D_DEVCAP_TEXTURE_GRADIENT_SAMPLING = 16,
SVGA3D_DEVCAP_MAX_POINT_SIZE = 17,
SVGA3D_DEVCAP_MAX_SHADER_TEXTURES = 18,
SVGA3D_DEVCAP_MAX_TEXTURE_WIDTH = 19,
SVGA3D_DEVCAP_MAX_TEXTURE_HEIGHT = 20,
SVGA3D_DEVCAP_MAX_VOLUME_EXTENT = 21,
SVGA3D_DEVCAP_MAX_TEXTURE_REPEAT = 22,
SVGA3D_DEVCAP_MAX_TEXTURE_ASPECT_RATIO = 23,
SVGA3D_DEVCAP_MAX_TEXTURE_ANISOTROPY = 24,
SVGA3D_DEVCAP_MAX_PRIMITIVE_COUNT = 25,
SVGA3D_DEVCAP_MAX_VERTEX_INDEX = 26,
SVGA3D_DEVCAP_MAX_VERTEX_SHADER_INSTRUCTIONS = 27,
SVGA3D_DEVCAP_MAX_FRAGMENT_SHADER_INSTRUCTIONS = 28,
SVGA3D_DEVCAP_MAX_VERTEX_SHADER_TEMPS = 29,
SVGA3D_DEVCAP_MAX_FRAGMENT_SHADER_TEMPS = 30,
SVGA3D_DEVCAP_TEXTURE_OPS = 31,
SVGA3D_DEVCAP_SURFACEFMT_X8R8G8B8 = 32,
SVGA3D_DEVCAP_SURFACEFMT_A8R8G8B8 = 33,
SVGA3D_DEVCAP_SURFACEFMT_A2R10G10B10 = 34,
SVGA3D_DEVCAP_SURFACEFMT_X1R5G5B5 = 35,
SVGA3D_DEVCAP_SURFACEFMT_A1R5G5B5 = 36,
SVGA3D_DEVCAP_SURFACEFMT_A4R4G4B4 = 37,
SVGA3D_DEVCAP_SURFACEFMT_R5G6B5 = 38,
SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE16 = 39,
SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE8_ALPHA8 = 40,
SVGA3D_DEVCAP_SURFACEFMT_ALPHA8 = 41,
SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE8 = 42,
SVGA3D_DEVCAP_SURFACEFMT_Z_D16 = 43,
SVGA3D_DEVCAP_SURFACEFMT_Z_D24S8 = 44,
SVGA3D_DEVCAP_SURFACEFMT_Z_D24X8 = 45,
SVGA3D_DEVCAP_SURFACEFMT_DXT1 = 46,
SVGA3D_DEVCAP_SURFACEFMT_DXT2 = 47,
SVGA3D_DEVCAP_SURFACEFMT_DXT3 = 48,
SVGA3D_DEVCAP_SURFACEFMT_DXT4 = 49,
SVGA3D_DEVCAP_SURFACEFMT_DXT5 = 50,
SVGA3D_DEVCAP_SURFACEFMT_BUMPX8L8V8U8 = 51,
SVGA3D_DEVCAP_SURFACEFMT_A2W10V10U10 = 52,
SVGA3D_DEVCAP_SURFACEFMT_BUMPU8V8 = 53,
SVGA3D_DEVCAP_SURFACEFMT_Q8W8V8U8 = 54,
SVGA3D_DEVCAP_SURFACEFMT_CxV8U8 = 55,
SVGA3D_DEVCAP_SURFACEFMT_R_S10E5 = 56,
SVGA3D_DEVCAP_SURFACEFMT_R_S23E8 = 57,
SVGA3D_DEVCAP_SURFACEFMT_RG_S10E5 = 58,
SVGA3D_DEVCAP_SURFACEFMT_RG_S23E8 = 59,
SVGA3D_DEVCAP_SURFACEFMT_ARGB_S10E5 = 60,
SVGA3D_DEVCAP_SURFACEFMT_ARGB_S23E8 = 61,
SVGA3D_DEVCAP_MAX_VERTEX_SHADER_TEXTURES = 63,
/*
* Note that MAX_SIMULTANEOUS_RENDER_TARGETS is a maximum count of color
* render targets. This does no include the depth or stencil targets.
*/
SVGA3D_DEVCAP_MAX_SIMULTANEOUS_RENDER_TARGETS = 64,
SVGA3D_DEVCAP_SURFACEFMT_V16U16 = 65,
SVGA3D_DEVCAP_SURFACEFMT_G16R16 = 66,
SVGA3D_DEVCAP_SURFACEFMT_A16B16G16R16 = 67,
SVGA3D_DEVCAP_SURFACEFMT_UYVY = 68,
SVGA3D_DEVCAP_SURFACEFMT_YUY2 = 69,
/*
* Don't add new caps into the previous section; the values in this
* enumeration must not change. You can put new values right before
* SVGA3D_DEVCAP_MAX.
*/
SVGA3D_DEVCAP_MAX /* This must be the last index. */
} SVGA3dDevCapIndex;
typedef union {
Bool b;
uint32 u;
int32 i;
float f;
} SVGA3dDevCapResult;
#endif /* _SVGA3D_REG_H_ */
/**********************************************************
* Copyright 2007-2009 VMware, Inc. All rights reserved.
*
* 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 AUTHORS OR COPYRIGHT HOLDERS
* 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.
*
**********************************************************/
/*
* 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.
*
* Major command number allocation:
*
* 0000: Reserved
* 0001: SVGA_ESCAPE_VMWARE_LOG (svga_binary_logger.h)
* 0002: SVGA_ESCAPE_VMWARE_VIDEO (svga_overlay.h)
* 0003: SVGA_ESCAPE_VMWARE_HINT (svga_escape.h)
*/
#define SVGA_ESCAPE_VMWARE_MAJOR_MASK 0xFFFF0000
/*
* SVGA Hint commands.
*
* These escapes let the SVGA driver provide optional information to
* he host about the state of the guest or guest applications. The
* host can use these hints to make user interface or performance
* decisions.
*
* Notes:
*
* - SVGA_ESCAPE_VMWARE_HINT_FULLSCREEN is deprecated for guests
* that use the SVGA Screen Object extension. Instead of sending
* this escape, use the SVGA_SCREEN_FULLSCREEN_HINT flag on your
* Screen Object.
*/
#define SVGA_ESCAPE_VMWARE_HINT 0x00030000
#define SVGA_ESCAPE_VMWARE_HINT_FULLSCREEN 0x00030001 // Deprecated
typedef
struct {
uint32 command;
uint32 fullscreen;
struct {
int32 x, y;
} monitorPosition;
} SVGAEscapeHintFullscreen;
#endif /* _SVGA_ESCAPE_H_ */
/**********************************************************
* Copyright 2007-2009 VMware, Inc. All rights reserved.
*
* 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 AUTHORS OR COPYRIGHT HOLDERS
* 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.
*
**********************************************************/
/*
* svga_overlay.h --
*
* Definitions for video-overlay support.
*/
#ifndef _SVGA_OVERLAY_H_
#define _SVGA_OVERLAY_H_
#include "svga_reg.h"
/*
* Video formats we support
*/
#define VMWARE_FOURCC_YV12 0x32315659 // 'Y' 'V' '1' '2'
#define VMWARE_FOURCC_YUY2 0x32595559 // 'Y' 'U' 'Y' '2'
#define VMWARE_FOURCC_UYVY 0x59565955 // 'U' 'Y' 'V' 'Y'
typedef enum {
SVGA_OVERLAY_FORMAT_INVALID = 0,
SVGA_OVERLAY_FORMAT_YV12 = VMWARE_FOURCC_YV12,
SVGA_OVERLAY_FORMAT_YUY2 = VMWARE_FOURCC_YUY2,
SVGA_OVERLAY_FORMAT_UYVY = VMWARE_FOURCC_UYVY,
} SVGAOverlayFormat;
#define SVGA_VIDEO_COLORKEY_MASK 0x00ffffff
#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;
/*
* Struct definitions for the video overlay commands built on
* SVGAFifoCmdEscape.
*/
typedef
struct {
uint32 command;
uint32 overlay;
} SVGAFifoEscapeCmdVideoBase;
typedef
struct {
SVGAFifoEscapeCmdVideoBase videoCmd;
} SVGAFifoEscapeCmdVideoFlush;
typedef
struct {
SVGAFifoEscapeCmdVideoBase videoCmd;
struct {
uint32 regId;
uint32 value;
} items[1];
} SVGAFifoEscapeCmdVideoSetRegs;
typedef
struct {
SVGAFifoEscapeCmdVideoBase videoCmd;
struct {
uint32 regId;
uint32 value;
} items[SVGA_VIDEO_NUM_REGS];
} SVGAFifoEscapeCmdVideoSetAllRegs;
/*
*----------------------------------------------------------------------
*
* VMwareVideoGetAttributes --
*
* Computes the size, pitches and offsets for YUV frames.
*
* Results:
* TRUE on success; otherwise FALSE on failure.
*
* Side effects:
* Pitches and offsets for the given YUV frame are put in 'pitches'
* and 'offsets' respectively. They are both optional though.
*
*----------------------------------------------------------------------
*/
static inline bool
VMwareVideoGetAttributes(const SVGAOverlayFormat format, // IN
uint32 *width, // IN / OUT
uint32 *height, // IN / OUT
uint32 *size, // OUT
uint32 *pitches, // OUT (optional)
uint32 *offsets) // OUT (optional)
{
int tmp;
*width = (*width + 1) & ~1;
if (offsets) {
offsets[0] = 0;
}
switch (format) {
case VMWARE_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 VMWARE_FOURCC_YUY2:
case VMWARE_FOURCC_UYVY:
*size = *width * 2;
if (pitches) {
pitches[0] = *size;
}
*size *= *height;
break;
default:
return false;
}
return true;
}
#endif // _SVGA_OVERLAY_H_
/**********************************************************
* Copyright 1998-2009 VMware, Inc. All rights reserved.
*
* 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 AUTHORS OR COPYRIGHT HOLDERS
* 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.
*
**********************************************************/
/*
* svga_reg.h --
*
* Virtual hardware definitions for the VMware SVGA II device.
*/
#ifndef _SVGA_REG_H_
#define _SVGA_REG_H_
/*
* PCI device IDs.
*/
#define PCI_VENDOR_ID_VMWARE 0x15AD
#define PCI_DEVICE_ID_VMWARE_SVGA2 0x0405
/*
* Legal values for the SVGA_REG_CURSOR_ON register in old-fashioned
* cursor bypass mode. This is still supported, but no new guest
* drivers should use it.
*/
#define SVGA_CURSOR_ON_HIDE 0x0 /* Must be 0 to maintain backward compatibility */
#define SVGA_CURSOR_ON_SHOW 0x1 /* Must be 1 to maintain backward compatibility */
#define SVGA_CURSOR_ON_REMOVE_FROM_FB 0x2 /* Remove the cursor from the framebuffer because we need to see what's under it */
#define SVGA_CURSOR_ON_RESTORE_TO_FB 0x3 /* Put the cursor back in the framebuffer so the user can see it */
/*
* The maximum framebuffer size that can traced for e.g. guests in VESA mode.
* The changeMap in the monitor is proportional to this number. 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!).
*
* NB: For compatibility reasons, this value must be greater than 0xff0000.
* See bug 335072.
*/
#define SVGA_FB_MAX_TRACEABLE_SIZE 0x1000000
#define SVGA_MAX_PSEUDOCOLOR_DEPTH 8
#define SVGA_MAX_PSEUDOCOLORS (1 << SVGA_MAX_PSEUDOCOLOR_DEPTH)
#define SVGA_NUM_PALETTE_REGS (3 * SVGA_MAX_PSEUDOCOLORS)
#define SVGA_MAGIC 0x900000UL
#define SVGA_MAKE_ID(ver) (SVGA_MAGIC << 8 | (ver))
/* Version 2 let the address of the frame buffer be unsigned on Win32 */
#define SVGA_VERSION_2 2
#define SVGA_ID_2 SVGA_MAKE_ID(SVGA_VERSION_2)
/* Version 1 has new registers starting with SVGA_REG_CAPABILITIES so
PALETTE_BASE has moved */
#define SVGA_VERSION_1 1
#define SVGA_ID_1 SVGA_MAKE_ID(SVGA_VERSION_1)
/* Version 0 is the initial version */
#define SVGA_VERSION_0 0
#define SVGA_ID_0 SVGA_MAKE_ID(SVGA_VERSION_0)
/* "Invalid" value for all SVGA IDs. (Version ID, screen object ID, surface ID...) */
#define SVGA_ID_INVALID 0xFFFFFFFF
/* Port offsets, relative to BAR0 */
#define SVGA_INDEX_PORT 0x0
#define SVGA_VALUE_PORT 0x1
#define SVGA_BIOS_PORT 0x2
#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 */
/*
* Registers
*/
enum {
SVGA_REG_ID = 0,
SVGA_REG_ENABLE = 1,
SVGA_REG_WIDTH = 2,
SVGA_REG_HEIGHT = 3,
SVGA_REG_MAX_WIDTH = 4,
SVGA_REG_MAX_HEIGHT = 5,
SVGA_REG_DEPTH = 6,
SVGA_REG_BITS_PER_PIXEL = 7, /* Current bpp in the guest */
SVGA_REG_PSEUDOCOLOR = 8,
SVGA_REG_RED_MASK = 9,
SVGA_REG_GREEN_MASK = 10,
SVGA_REG_BLUE_MASK = 11,
SVGA_REG_BYTES_PER_LINE = 12,
SVGA_REG_FB_START = 13, /* (Deprecated) */
SVGA_REG_FB_OFFSET = 14,
SVGA_REG_VRAM_SIZE = 15,
SVGA_REG_FB_SIZE = 16,
/* ID 0 implementation only had the above registers, then the palette */
SVGA_REG_CAPABILITIES = 17,
SVGA_REG_MEM_START = 18, /* (Deprecated) */
SVGA_REG_MEM_SIZE = 19,
SVGA_REG_CONFIG_DONE = 20, /* Set when memory area configured */
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, /* (Deprecated) */
SVGA_REG_CURSOR_X = 25, /* (Deprecated) */
SVGA_REG_CURSOR_Y = 26, /* (Deprecated) */
SVGA_REG_CURSOR_ON = 27, /* (Deprecated) */
SVGA_REG_HOST_BITS_PER_PIXEL = 28, /* (Deprecated) */
SVGA_REG_SCRATCH_SIZE = 29, /* Number of scratch registers */
SVGA_REG_MEM_REGS = 30, /* Number of FIFO registers */
SVGA_REG_NUM_DISPLAYS = 31, /* (Deprecated) */
SVGA_REG_PITCHLOCK = 32, /* Fixed pitch for all modes */
SVGA_REG_IRQMASK = 33, /* Interrupt mask */
/* Legacy multi-monitor support */
SVGA_REG_NUM_GUEST_DISPLAYS = 34,/* Number of guest displays in X/Y direction */
SVGA_REG_DISPLAY_ID = 35, /* Display ID for the following display attributes */
SVGA_REG_DISPLAY_IS_PRIMARY = 36,/* Whether this is a primary display */
SVGA_REG_DISPLAY_POSITION_X = 37,/* The display position x */
SVGA_REG_DISPLAY_POSITION_Y = 38,/* The display position y */
SVGA_REG_DISPLAY_WIDTH = 39, /* The display's width */
SVGA_REG_DISPLAY_HEIGHT = 40, /* The display's height */
/* See "Guest memory regions" below. */
SVGA_REG_GMR_ID = 41,
SVGA_REG_GMR_DESCRIPTOR = 42,
SVGA_REG_GMR_MAX_IDS = 43,
SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH = 44,
SVGA_REG_TRACES = 45, /* Enable trace-based updates even when FIFO is on */
SVGA_REG_TOP = 46, /* 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 */
SVGA_SCRATCH_BASE = SVGA_PALETTE_BASE + SVGA_NUM_PALETTE_REGS
/* Base of scratch registers */
/* Next reg[SVGA_REG_SCRATCH_SIZE] registers exist for scratch usage:
First 4 are reserved for VESA BIOS Extension; any remaining are for
the use of the current SVGA driver. */
};
/*
* Guest memory regions (GMRs):
*
* This is a new memory mapping feature available in SVGA devices
* which have the SVGA_CAP_GMR bit set. Previously, there were two
* fixed memory regions available with which to share data between the
* device and the driver: the FIFO ('MEM') and the framebuffer. GMRs
* are our name for an extensible way of providing arbitrary DMA
* buffers for use between the driver and the SVGA device. They are a
* new alternative to framebuffer memory, usable for both 2D and 3D
* graphics operations.
*
* Since GMR mapping must be done synchronously with guest CPU
* execution, we use a new pair of SVGA registers:
*
* SVGA_REG_GMR_ID --
*
* Read/write.
* This register holds the 32-bit ID (a small positive integer)
* of a GMR to create, delete, or redefine. Writing this register
* has no side-effects.
*
* SVGA_REG_GMR_DESCRIPTOR --
*
* Write-only.
* Writing this register will create, delete, or redefine the GMR
* specified by the above ID register. If this register is zero,
* the GMR is deleted. Any pointers into this GMR (including those
* currently being processed by FIFO commands) will be
* synchronously invalidated.
*
* If this register is nonzero, it must be the physical page
* number (PPN) of a data structure which describes the physical
* layout of the memory region this GMR should describe. The
* descriptor structure will be read synchronously by the SVGA
* device when this register is written. The descriptor need not
* remain allocated for the lifetime of the GMR.
*
* The guest driver should write SVGA_REG_GMR_ID first, then
* SVGA_REG_GMR_DESCRIPTOR.
*
* SVGA_REG_GMR_MAX_IDS --
*
* Read-only.
* The SVGA device may choose to support a maximum number of
* user-defined GMR IDs. This register holds the number of supported
* IDs. (The maximum supported ID plus 1)
*
* SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH --
*
* Read-only.
* The SVGA device may choose to put a limit on the total number
* of SVGAGuestMemDescriptor structures it will read when defining
* a single GMR.
*
* The descriptor structure is an array of SVGAGuestMemDescriptor
* structures. Each structure may do one of three things:
*
* - Terminate the GMR descriptor list.
* (ppn==0, numPages==0)
*
* - Add a PPN or range of PPNs to the GMR's virtual address space.
* (ppn != 0, numPages != 0)
*
* - Provide the PPN of the next SVGAGuestMemDescriptor, in order to
* support multi-page GMR descriptor tables without forcing the
* driver to allocate physically contiguous memory.
* (ppn != 0, numPages == 0)
*
* Note that each physical page of SVGAGuestMemDescriptor structures
* can describe at least 2MB of guest memory. If the driver needs to
* use more than one page of descriptor structures, it must use one of
* its SVGAGuestMemDescriptors to point to an additional page. The
* device will never automatically cross a page boundary.
*
* Once the driver has described a GMR, it is immediately available
* for use via any FIFO command that uses an SVGAGuestPtr structure.
* These pointers include a GMR identifier plus an offset into that
* GMR.
*
* The driver must check the SVGA_CAP_GMR bit before using the GMR
* registers.
*/
/*
* Special GMR IDs, allowing SVGAGuestPtrs to point to framebuffer
* memory as well. In the future, these IDs could even be used to
* allow legacy memory regions to be redefined by the guest as GMRs.
*
* Using the guest framebuffer (GFB) at BAR1 for general purpose DMA
* is being phased out. Please try to use user-defined GMRs whenever
* possible.
*/
#define SVGA_GMR_NULL ((uint32) -1)
#define SVGA_GMR_FRAMEBUFFER ((uint32) -2) // Guest Framebuffer (GFB)
typedef
struct SVGAGuestMemDescriptor {
uint32 ppn;
uint32 numPages;
} SVGAGuestMemDescriptor;
typedef
struct SVGAGuestPtr {
uint32 gmrId;
uint32 offset;
} SVGAGuestPtr;
/*
* SVGAGMRImageFormat --
*
* This is a packed representation of the source 2D image format
* for a GMR-to-screen blit. Currently it is defined as an encoding
* of the screen's color depth and bits-per-pixel, however, 16 bits
* are reserved for future use to identify other encodings (such as
* RGBA or higher-precision images).
*
* Currently supported formats:
*
* bpp depth Format Name
* --- ----- -----------
* 32 24 32-bit BGRX
* 24 24 24-bit BGR
* 16 16 RGB 5-6-5
* 16 15 RGB 5-5-5
*
*/
typedef
struct SVGAGMRImageFormat {
union {
struct {
uint32 bitsPerPixel : 8;
uint32 colorDepth : 8;
uint32 reserved : 16; // Must be zero
};
uint32 value;
};
} SVGAGMRImageFormat;
/*
* SVGAColorBGRX --
*
* A 24-bit color format (BGRX), which does not depend on the
* format of the legacy guest framebuffer (GFB) or the current
* GMRFB state.
*/
typedef
struct SVGAColorBGRX {
union {
struct {
uint32 b : 8;
uint32 g : 8;
uint32 r : 8;
uint32 x : 8; // Unused
};
uint32 value;
};
} SVGAColorBGRX;
/*
* SVGASignedRect --
* SVGASignedPoint --
*
* Signed rectangle and point primitives. These are used by the new
* 2D primitives for drawing to Screen Objects, which can occupy a
* signed virtual coordinate space.
*
* SVGASignedRect specifies a half-open interval: the (left, top)
* pixel is part of the rectangle, but the (right, bottom) pixel is
* not.
*/
typedef
struct SVGASignedRect {
int32 left;
int32 top;
int32 right;
int32 bottom;
} SVGASignedRect;
typedef
struct SVGASignedPoint {
int32 x;
int32 y;
} SVGASignedPoint;
/*
* Capabilities
*
* Note the holes in the bitfield. Missing bits have been deprecated,
* and must not be reused. Those capabilities will never be reported
* by new versions of the SVGA device.
*/
#define SVGA_CAP_NONE 0x00000000
#define SVGA_CAP_RECT_COPY 0x00000002
#define SVGA_CAP_CURSOR 0x00000020
#define SVGA_CAP_CURSOR_BYPASS 0x00000040 // Legacy (Use Cursor Bypass 3 instead)
#define SVGA_CAP_CURSOR_BYPASS_2 0x00000080 // Legacy (Use Cursor Bypass 3 instead)
#define SVGA_CAP_8BIT_EMULATION 0x00000100
#define SVGA_CAP_ALPHA_CURSOR 0x00000200
#define SVGA_CAP_3D 0x00004000
#define SVGA_CAP_EXTENDED_FIFO 0x00008000
#define SVGA_CAP_MULTIMON 0x00010000 // Legacy multi-monitor support
#define SVGA_CAP_PITCHLOCK 0x00020000
#define SVGA_CAP_IRQMASK 0x00040000
#define SVGA_CAP_DISPLAY_TOPOLOGY 0x00080000 // Legacy multi-monitor support
#define SVGA_CAP_GMR 0x00100000
#define SVGA_CAP_TRACES 0x00200000
/*
* 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 {
/*
* 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,
SVGA_FIFO_MAX, /* The distance from MIN to MAX must be at least 10K */
SVGA_FIFO_NEXT_CMD,
SVGA_FIFO_STOP,
/*
* 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,
/*
* 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 */
/*
* Valid with SVGA_FIFO_CAP_SCREEN_OBJECT:
*
* By default this is SVGA_ID_INVALID, to indicate that the cursor
* coordinates are specified relative to the virtual root. If this
* is set to a specific screen ID, cursor position is reinterpreted
* as a signed offset relative to that screen's origin. This is the
* only way to place the cursor on a non-rooted screen.
*/
SVGA_FIFO_CURSOR_SCREEN_ID,
/*
* 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.
*/
// 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
*
* XXX: Add longer descriptions for each capability, including a list
* of the new features that each capability provides.
*
* SVGA_FIFO_CAP_SCREEN_OBJECT --
*
* Provides dynamic multi-screen rendering, for improved Unity and
* multi-monitor modes. With Screen Object, the guest can
* dynamically create and destroy 'screens', which can represent
* Unity windows or virtual monitors. Screen Object also provides
* strong guarantees that DMA operations happen only when
* guest-initiated. Screen Object deprecates the BAR1 guest
* framebuffer (GFB) and all commands that work only with the GFB.
*
* New registers:
* FIFO_CURSOR_SCREEN_ID, VIDEO_DATA_GMRID, VIDEO_DST_SCREEN_ID
*
* New 2D commands:
* DEFINE_SCREEN, DESTROY_SCREEN, DEFINE_GMRFB, BLIT_GMRFB_TO_SCREEN,
* BLIT_SCREEN_TO_GMRFB, ANNOTATION_FILL, ANNOTATION_COPY
*
* New 3D commands:
* BLIT_SURFACE_TO_SCREEN
*
* New guarantees:
*
* - The host will not read or write guest memory, including the GFB,
* except when explicitly initiated by a DMA command.
*
* - All DMA, including legacy DMA like UPDATE and PRESENT_READBACK,
* is guaranteed to complete before any subsequent FENCEs.
*
* - All legacy commands which affect a Screen (UPDATE, PRESENT,
* PRESENT_READBACK) as well as new Screen blit commands will
* all behave consistently as blits, and memory will be read
* or written in FIFO order.
*
* For example, if you PRESENT from one SVGA3D surface to multiple
* places on the screen, the data copied will always be from the
* SVGA3D surface at the time the PRESENT was issued in the FIFO.
* This was not necessarily true on devices without Screen Object.
*
* This means that on devices that support Screen Object, the
* PRESENT_READBACK command should not be necessary unless you
* actually want to read back the results of 3D rendering into
* system memory. (And for that, the BLIT_SCREEN_TO_GMRFB
* command provides a strict superset of functionality.)
*
* - When a screen is resized, either using Screen Object commands or
* legacy multimon registers, its contents are preserved.
*/
#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)
#define SVGA_FIFO_CAP_SCREEN_OBJECT (1<<7)
/*
* FIFO Flags
*
* Accel Front -- Driver should use front buffer only commands
*/
#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, // Deprecated
SVGA_VIDEO_WIDTH,
SVGA_VIDEO_HEIGHT,
SVGA_VIDEO_SRC_X,
SVGA_VIDEO_SRC_Y,
SVGA_VIDEO_SRC_WIDTH,
SVGA_VIDEO_SRC_HEIGHT,
SVGA_VIDEO_DST_X, // Signed int32
SVGA_VIDEO_DST_Y, // Signed int32
SVGA_VIDEO_DST_WIDTH,
SVGA_VIDEO_DST_HEIGHT,
SVGA_VIDEO_PITCH_1,
SVGA_VIDEO_PITCH_2,
SVGA_VIDEO_PITCH_3,
SVGA_VIDEO_DATA_GMRID, // Optional, defaults to SVGA_GMR_FRAMEBUFFER
SVGA_VIDEO_DST_SCREEN_ID, // Optional, defaults to virtual coords (SVGA_ID_INVALID)
SVGA_VIDEO_NUM_REGS
};
/*
* SVGA Overlay Units
*
* width and height relate to the entire source video frame.
* srcX, srcY, srcWidth and srcHeight represent subset of the source
* video frame to be displayed.
*/
typedef struct SVGAOverlayUnit {
uint32 enabled;
uint32 flags;
uint32 dataOffset;
uint32 format;
uint32 colorKey;
uint32 size;
uint32 width;
uint32 height;
uint32 srcX;
uint32 srcY;
uint32 srcWidth;
uint32 srcHeight;
int32 dstX;
int32 dstY;
uint32 dstWidth;
uint32 dstHeight;
uint32 pitches[3];
uint32 dataGMRId;
uint32 dstScreenId;
} SVGAOverlayUnit;
/*
* SVGAScreenObject --
*
* This is a new way to represent a guest's multi-monitor screen or
* Unity window. Screen objects are only supported if the
* SVGA_FIFO_CAP_SCREEN_OBJECT capability bit is set.
*
* If Screen Objects are supported, they can be used to fully
* replace the functionality provided by the framebuffer registers
* (SVGA_REG_WIDTH, HEIGHT, etc.) and by SVGA_CAP_DISPLAY_TOPOLOGY.
*
* The screen object is a struct with guaranteed binary
* compatibility. New flags can be added, and the struct may grow,
* but existing fields must retain their meaning.
*
*/
#define SVGA_SCREEN_HAS_ROOT (1 << 0) // Screen is present in the virtual coord space
#define SVGA_SCREEN_IS_PRIMARY (1 << 1) // Guest considers this screen to be 'primary'
#define SVGA_SCREEN_FULLSCREEN_HINT (1 << 2) // Guest is running a fullscreen app here
typedef
struct SVGAScreenObject {
uint32 structSize; // sizeof(SVGAScreenObject)
uint32 id;
uint32 flags;
struct {
uint32 width;
uint32 height;
} size;
struct {
int32 x;
int32 y;
} root; // Only used if SVGA_SCREEN_HAS_ROOT is set.
} SVGAScreenObject;
/*
* Commands in the command FIFO:
*
* Command IDs defined below are used for the traditional 2D FIFO
* communication (not all commands are available for all versions of the
* SVGA FIFO protocol).
*
* Note the holes in the command ID numbers: These commands have been
* deprecated, and the old IDs must not be reused.
*
* Command IDs from 1000 to 1999 are reserved for use by the SVGA3D
* protocol.
*
* Each command's parameters are described by the comments and
* structs below.
*/
typedef enum {
SVGA_CMD_INVALID_CMD = 0,
SVGA_CMD_UPDATE = 1,
SVGA_CMD_RECT_COPY = 3,
SVGA_CMD_DEFINE_CURSOR = 19,
SVGA_CMD_DEFINE_ALPHA_CURSOR = 22,
SVGA_CMD_UPDATE_VERBOSE = 25,
SVGA_CMD_FRONT_ROP_FILL = 29,
SVGA_CMD_FENCE = 30,
SVGA_CMD_ESCAPE = 33,
SVGA_CMD_DEFINE_SCREEN = 34,
SVGA_CMD_DESTROY_SCREEN = 35,
SVGA_CMD_DEFINE_GMRFB = 36,
SVGA_CMD_BLIT_GMRFB_TO_SCREEN = 37,
SVGA_CMD_BLIT_SCREEN_TO_GMRFB = 38,
SVGA_CMD_ANNOTATION_FILL = 39,
SVGA_CMD_ANNOTATION_COPY = 40,
SVGA_CMD_MAX
} SVGAFifoCmdId;
#define SVGA_CMD_MAX_ARGS 64
/*
* SVGA_CMD_UPDATE --
*
* This is a DMA transfer which copies from the Guest Framebuffer
* (GFB) at BAR1 + SVGA_REG_FB_OFFSET to any screens which
* intersect with the provided virtual rectangle.
*
* This command does not support using arbitrary guest memory as a
* data source- it only works with the pre-defined GFB memory.
* This command also does not support signed virtual coordinates.
* If you have defined screens (using SVGA_CMD_DEFINE_SCREEN) with
* negative root x/y coordinates, the negative portion of those
* screens will not be reachable by this command.
*
* This command is not necessary when using framebuffer
* traces. Traces are automatically enabled if the SVGA FIFO is
* disabled, and you may explicitly enable/disable traces using
* SVGA_REG_TRACES. With traces enabled, any write to the GFB will
* automatically act as if a subsequent SVGA_CMD_UPDATE was issued.
*
* Traces and SVGA_CMD_UPDATE are the only supported ways to render
* pseudocolor screen updates. The newer Screen Object commands
* only support true color formats.
*
* Availability:
* Always available.
*/
typedef
struct {
uint32 x;
uint32 y;
uint32 width;
uint32 height;
} SVGAFifoCmdUpdate;
/*
* SVGA_CMD_RECT_COPY --
*
* Perform a rectangular DMA transfer from one area of the GFB to
* another, and copy the result to any screens which intersect it.
*
* Availability:
* SVGA_CAP_RECT_COPY
*/
typedef
struct {
uint32 srcX;
uint32 srcY;
uint32 destX;
uint32 destY;
uint32 width;
uint32 height;
} SVGAFifoCmdRectCopy;
/*
* SVGA_CMD_DEFINE_CURSOR --
*
* Provide a new cursor image, as an AND/XOR mask.
*
* The recommended way to position the cursor overlay is by using
* the SVGA_FIFO_CURSOR_* registers, supported by the
* SVGA_FIFO_CAP_CURSOR_BYPASS_3 capability.
*
* Availability:
* SVGA_CAP_CURSOR
*/
typedef
struct {
uint32 id; // Reserved, must be zero.
uint32 hotspotX;
uint32 hotspotY;
uint32 width;
uint32 height;
uint32 andMaskDepth; // Value must be 1 or equal to BITS_PER_PIXEL
uint32 xorMaskDepth; // Value must be 1 or equal to BITS_PER_PIXEL
/*
* Followed by scanline data for AND mask, then XOR mask.
* Each scanline is padded to a 32-bit boundary.
*/
} SVGAFifoCmdDefineCursor;
/*
* SVGA_CMD_DEFINE_ALPHA_CURSOR --
*
* Provide a new cursor image, in 32-bit BGRA format.
*
* The recommended way to position the cursor overlay is by using
* the SVGA_FIFO_CURSOR_* registers, supported by the
* SVGA_FIFO_CAP_CURSOR_BYPASS_3 capability.
*
* Availability:
* SVGA_CAP_ALPHA_CURSOR
*/
typedef
struct {
uint32 id; // Reserved, must be zero.
uint32 hotspotX;
uint32 hotspotY;
uint32 width;
uint32 height;
/* Followed by scanline data */
} SVGAFifoCmdDefineAlphaCursor;
/*
* SVGA_CMD_UPDATE_VERBOSE --
*
* Just like SVGA_CMD_UPDATE, but also provide a per-rectangle
* 'reason' value, an opaque cookie which is used by internal
* debugging tools. Third party drivers should not use this
* command.
*
* Availability:
* SVGA_CAP_EXTENDED_FIFO
*/
typedef
struct {
uint32 x;
uint32 y;
uint32 width;
uint32 height;
uint32 reason;
} SVGAFifoCmdUpdateVerbose;
/*
* SVGA_CMD_FRONT_ROP_FILL --
*
* This is a hint which tells the SVGA device that the driver has
* just filled a rectangular region of the GFB with a solid
* color. Instead of reading these pixels from the GFB, the device
* can assume that they all equal 'color'. This is primarily used
* for remote desktop protocols.
*
* Availability:
* SVGA_FIFO_CAP_ACCELFRONT
*/
#define SVGA_ROP_COPY 0x03
typedef
struct {
uint32 color; // In the same format as the GFB
uint32 x;
uint32 y;
uint32 width;
uint32 height;
uint32 rop; // Must be SVGA_ROP_COPY
} SVGAFifoCmdFrontRopFill;
/*
* SVGA_CMD_FENCE --
*
* Insert a synchronization fence. When the SVGA device reaches
* this command, it will copy the 'fence' value into the
* SVGA_FIFO_FENCE register. It will also compare the fence against
* SVGA_FIFO_FENCE_GOAL. If the fence matches the goal and the
* SVGA_IRQFLAG_FENCE_GOAL interrupt is enabled, the device will
* raise this interrupt.
*
* Availability:
* SVGA_FIFO_FENCE for this command,
* SVGA_CAP_IRQMASK for SVGA_FIFO_FENCE_GOAL.
*/
typedef
struct {
uint32 fence;
} SVGAFifoCmdFence;
/*
* SVGA_CMD_ESCAPE --
*
* Send an extended or vendor-specific variable length command.
* This is used for video overlay, third party plugins, and
* internal debugging tools. See svga_escape.h
*
* Availability:
* SVGA_FIFO_CAP_ESCAPE
*/
typedef
struct {
uint32 nsid;
uint32 size;
/* followed by 'size' bytes of data */
} SVGAFifoCmdEscape;
/*
* SVGA_CMD_DEFINE_SCREEN --
*
* Define or redefine an SVGAScreenObject. See the description of
* SVGAScreenObject above. The video driver is responsible for
* generating new screen IDs. They should be small positive
* integers. The virtual device will have an implementation
* specific upper limit on the number of screen IDs
* supported. Drivers are responsible for recycling IDs. The first
* valid ID is zero.
*
* - Interaction with other registers:
*
* For backwards compatibility, when the GFB mode registers (WIDTH,
* HEIGHT, PITCHLOCK, BITS_PER_PIXEL) are modified, the SVGA device
* deletes all screens other than screen #0, and redefines screen
* #0 according to the specified mode. Drivers that use
* SVGA_CMD_DEFINE_SCREEN should destroy or redefine screen #0.
*
* If you use screen objects, do not use the legacy multi-mon
* registers (SVGA_REG_NUM_GUEST_DISPLAYS, SVGA_REG_DISPLAY_*).
*
* Availability:
* SVGA_FIFO_CAP_SCREEN_OBJECT
*/
typedef
struct {
SVGAScreenObject screen; // Variable-length according to version
} SVGAFifoCmdDefineScreen;
/*
* SVGA_CMD_DESTROY_SCREEN --
*
* Destroy an SVGAScreenObject. Its ID is immediately available for
* re-use.
*
* Availability:
* SVGA_FIFO_CAP_SCREEN_OBJECT
*/
typedef
struct {
uint32 screenId;
} SVGAFifoCmdDestroyScreen;
/*
* SVGA_CMD_DEFINE_GMRFB --
*
* This command sets a piece of SVGA device state called the
* Guest Memory Region Framebuffer, or GMRFB. The GMRFB is a
* piece of light-weight state which identifies the location and
* format of an image in guest memory or in BAR1. The GMRFB has
* an arbitrary size, and it doesn't need to match the geometry
* of the GFB or any screen object.
*
* The GMRFB can be redefined as often as you like. You could
* always use the same GMRFB, you could redefine it before
* rendering from a different guest screen, or you could even
* redefine it before every blit.
*
* There are multiple ways to use this command. The simplest way is
* to use it to move the framebuffer either to elsewhere in the GFB
* (BAR1) memory region, or to a user-defined GMR. This lets a
* driver use a framebuffer allocated entirely out of normal system
* memory, which we encourage.
*
* Another way to use this command is to set up a ring buffer of
* updates in GFB memory. If a driver wants to ensure that no
* frames are skipped by the SVGA device, it is important that the
* driver not modify the source data for a blit until the device is
* done processing the command. One efficient way to accomplish
* this is to use a ring of small DMA buffers. Each buffer is used
* for one blit, then we move on to the next buffer in the
* ring. The FENCE mechanism is used to protect each buffer from
* re-use until the device is finished with that buffer's
* corresponding blit.
*
* This command does not affect the meaning of SVGA_CMD_UPDATE.
* UPDATEs always occur from the legacy GFB memory area. This
* command has no support for pseudocolor GMRFBs. Currently only
* true-color 15, 16, and 24-bit depths are supported. Future
* devices may expose capabilities for additional framebuffer
* formats.
*
* The default GMRFB value is undefined. Drivers must always send
* this command at least once before performing any blit from the
* GMRFB.
*
* Availability:
* SVGA_FIFO_CAP_SCREEN_OBJECT
*/
typedef
struct {
SVGAGuestPtr ptr;
uint32 bytesPerLine;
SVGAGMRImageFormat format;
} SVGAFifoCmdDefineGMRFB;
/*
* SVGA_CMD_BLIT_GMRFB_TO_SCREEN --
*
* This is a guest-to-host blit. It performs a DMA operation to
* copy a rectangular region of pixels from the current GMRFB to
* one or more Screen Objects.
*
* The destination coordinate may be specified relative to a
* screen's origin (if a screen ID is specified) or relative to the
* virtual coordinate system's origin (if the screen ID is
* SVGA_ID_INVALID). The actual destination may span zero or more
* screens, in the case of a virtual destination rect or a rect
* which extends off the edge of the specified screen.
*
* This command writes to the screen's "base layer": the underlying
* framebuffer which exists below any cursor or video overlays. No
* action is necessary to explicitly hide or update any overlays
* which exist on top of the updated region.
*
* The SVGA device is guaranteed to finish reading from the GMRFB
* by the time any subsequent FENCE commands are reached.
*
* This command consumes an annotation. See the
* SVGA_CMD_ANNOTATION_* commands for details.
*
* Availability:
* SVGA_FIFO_CAP_SCREEN_OBJECT
*/
typedef
struct {
SVGASignedPoint srcOrigin;
SVGASignedRect destRect;
uint32 destScreenId;
} SVGAFifoCmdBlitGMRFBToScreen;
/*
* SVGA_CMD_BLIT_SCREEN_TO_GMRFB --
*
* This is a host-to-guest blit. It performs a DMA operation to
* copy a rectangular region of pixels from a single Screen Object
* back to the current GMRFB.
*
* Usage note: This command should be used rarely. It will
* typically be inefficient, but it is necessary for some types of
* synchronization between 3D (GPU) and 2D (CPU) rendering into
* overlapping areas of a screen.
*
* The source coordinate is specified relative to a screen's
* origin. The provided screen ID must be valid. If any parameters
* are invalid, the resulting pixel values are undefined.
*
* This command reads the screen's "base layer". Overlays like
* video and cursor are not included, but any data which was sent
* using a blit-to-screen primitive will be available, no matter
* whether the data's original source was the GMRFB or the 3D
* acceleration hardware.
*
* Note that our guest-to-host blits and host-to-guest blits aren't
* symmetric in their current implementation. While the parameters
* are identical, host-to-guest blits are a lot less featureful.
* They do not support clipping: If the source parameters don't
* fully fit within a screen, the blit fails. They must originate
* from exactly one screen. Virtual coordinates are not directly
* supported.
*
* Host-to-guest blits do support the same set of GMRFB formats
* offered by guest-to-host blits.
*
* The SVGA device is guaranteed to finish writing to the GMRFB by
* the time any subsequent FENCE commands are reached.
*
* Availability:
* SVGA_FIFO_CAP_SCREEN_OBJECT
*/
typedef
struct {
SVGASignedPoint destOrigin;
SVGASignedRect srcRect;
uint32 srcScreenId;
} SVGAFifoCmdBlitScreenToGMRFB;
/*
* SVGA_CMD_ANNOTATION_FILL --
*
* This is a blit annotation. This command stores a small piece of
* device state which is consumed by the next blit-to-screen
* command. The state is only cleared by commands which are
* specifically documented as consuming an annotation. Other
* commands (such as ESCAPEs for debugging) may intervene between
* the annotation and its associated blit.
*
* This annotation is a promise about the contents of the next
* blit: The video driver is guaranteeing that all pixels in that
* blit will have the same value, specified here as a color in
* SVGAColorBGRX format.
*
* The SVGA device can still render the blit correctly even if it
* ignores this annotation, but the annotation may allow it to
* perform the blit more efficiently, for example by ignoring the
* source data and performing a fill in hardware.
*
* This annotation is most important for performance when the
* user's display is being remoted over a network connection.
*
* Availability:
* SVGA_FIFO_CAP_SCREEN_OBJECT
*/
typedef
struct {
SVGAColorBGRX color;
} SVGAFifoCmdAnnotationFill;
/*
* SVGA_CMD_ANNOTATION_COPY --
*
* This is a blit annotation. See SVGA_CMD_ANNOTATION_FILL for more
* information about annotations.
*
* This annotation is a promise about the contents of the next
* blit: The video driver is guaranteeing that all pixels in that
* blit will have the same value as those which already exist at an
* identically-sized region on the same or a different screen.
*
* Note that the source pixels for the COPY in this annotation are
* sampled before applying the anqnotation's associated blit. They
* are allowed to overlap with the blit's destination pixels.
*
* The copy source rectangle is specified the same way as the blit
* destination: it can be a rectangle which spans zero or more
* screens, specified relative to either a screen or to the virtual
* coordinate system's origin. If the source rectangle includes
* pixels which are not from exactly one screen, the results are
* undefined.
*
* Availability:
* SVGA_FIFO_CAP_SCREEN_OBJECT
*/
typedef
struct {
SVGASignedPoint srcOrigin;
uint32 srcScreenId;
} SVGAFifoCmdAnnotationCopy;
#endif
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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.
*
**************************************************************************/
/**
* Silly typedefs for the svga headers. Currently the headers are shared
* between all components that talk to svga. And as such the headers are
* are in a completely different style and use weird defines.
*
* This file lets all the ugly be prefixed with svga*.
*/
#ifndef _SVGA_TYPES_H_
#define _SVGA_TYPES_H_
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint8_t uint8;
typedef int32_t int32;
typedef bool Bool;
#endif
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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_drv.h"
#include "ttm/ttm_bo_driver.h"
#include "ttm/ttm_placement.h"
static uint32_t vram_placement_flags = TTM_PL_FLAG_VRAM |
TTM_PL_FLAG_CACHED;
static uint32_t vram_ne_placement_flags = TTM_PL_FLAG_VRAM |
TTM_PL_FLAG_CACHED |
TTM_PL_FLAG_NO_EVICT;
static uint32_t sys_placement_flags = TTM_PL_FLAG_SYSTEM |
TTM_PL_FLAG_CACHED;
struct ttm_placement vmw_vram_placement = {
.fpfn = 0,
.lpfn = 0,
.num_placement = 1,
.placement = &vram_placement_flags,
.num_busy_placement = 1,
.busy_placement = &vram_placement_flags
};
struct ttm_placement vmw_vram_ne_placement = {
.fpfn = 0,
.lpfn = 0,
.num_placement = 1,
.placement = &vram_ne_placement_flags,
.num_busy_placement = 1,
.busy_placement = &vram_ne_placement_flags
};
struct ttm_placement vmw_sys_placement = {
.fpfn = 0,
.lpfn = 0,
.num_placement = 1,
.placement = &sys_placement_flags,
.num_busy_placement = 1,
.busy_placement = &sys_placement_flags
};
struct vmw_ttm_backend {
struct ttm_backend backend;
};
static int vmw_ttm_populate(struct ttm_backend *backend,
unsigned long num_pages, struct page **pages,
struct page *dummy_read_page)
{
return 0;
}
static int vmw_ttm_bind(struct ttm_backend *backend, struct ttm_mem_reg *bo_mem)
{
return 0;
}
static int vmw_ttm_unbind(struct ttm_backend *backend)
{
return 0;
}
static void vmw_ttm_clear(struct ttm_backend *backend)
{
}
static void vmw_ttm_destroy(struct ttm_backend *backend)
{
struct vmw_ttm_backend *vmw_be =
container_of(backend, struct vmw_ttm_backend, backend);
kfree(vmw_be);
}
static struct ttm_backend_func vmw_ttm_func = {
.populate = vmw_ttm_populate,
.clear = vmw_ttm_clear,
.bind = vmw_ttm_bind,
.unbind = vmw_ttm_unbind,
.destroy = vmw_ttm_destroy,
};
struct ttm_backend *vmw_ttm_backend_init(struct ttm_bo_device *bdev)
{
struct vmw_ttm_backend *vmw_be;
vmw_be = kmalloc(sizeof(*vmw_be), GFP_KERNEL);
if (!vmw_be)
return NULL;
vmw_be->backend.func = &vmw_ttm_func;
return &vmw_be->backend;
}
int vmw_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags)
{
return 0;
}
int vmw_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
struct ttm_mem_type_manager *man)
{
struct vmw_private *dev_priv =
container_of(bdev, struct vmw_private, bdev);
switch (type) {
case TTM_PL_SYSTEM:
/* System memory */
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
man->available_caching = TTM_PL_MASK_CACHING;
man->default_caching = TTM_PL_FLAG_CACHED;
break;
case TTM_PL_VRAM:
/* "On-card" video ram */
man->gpu_offset = 0;
man->io_offset = dev_priv->vram_start;
man->io_size = dev_priv->vram_size;
man->flags = TTM_MEMTYPE_FLAG_FIXED |
TTM_MEMTYPE_FLAG_NEEDS_IOREMAP | TTM_MEMTYPE_FLAG_MAPPABLE;
man->io_addr = NULL;
man->available_caching = TTM_PL_MASK_CACHING;
man->default_caching = TTM_PL_FLAG_WC;
break;
default:
DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);
return -EINVAL;
}
return 0;
}
void vmw_evict_flags(struct ttm_buffer_object *bo,
struct ttm_placement *placement)
{
*placement = vmw_sys_placement;
}
/**
* FIXME: Proper access checks on buffers.
*/
static int vmw_verify_access(struct ttm_buffer_object *bo, struct file *filp)
{
return 0;
}
/**
* FIXME: We're using the old vmware polling method to sync.
* Do this with fences instead.
*/
static void *vmw_sync_obj_ref(void *sync_obj)
{
return sync_obj;
}
static void vmw_sync_obj_unref(void **sync_obj)
{
*sync_obj = NULL;
}
static int vmw_sync_obj_flush(void *sync_obj, void *sync_arg)
{
struct vmw_private *dev_priv = (struct vmw_private *)sync_arg;
mutex_lock(&dev_priv->hw_mutex);
vmw_write(dev_priv, SVGA_REG_SYNC, SVGA_SYNC_GENERIC);
mutex_unlock(&dev_priv->hw_mutex);
return 0;
}
static bool vmw_sync_obj_signaled(void *sync_obj, void *sync_arg)
{
struct vmw_private *dev_priv = (struct vmw_private *)sync_arg;
uint32_t sequence = (unsigned long) sync_obj;
return vmw_fence_signaled(dev_priv, sequence);
}
static int vmw_sync_obj_wait(void *sync_obj, void *sync_arg,
bool lazy, bool interruptible)
{
struct vmw_private *dev_priv = (struct vmw_private *)sync_arg;
uint32_t sequence = (unsigned long) sync_obj;
return vmw_wait_fence(dev_priv, false, sequence, false, 3*HZ);
}
struct ttm_bo_driver vmw_bo_driver = {
.create_ttm_backend_entry = vmw_ttm_backend_init,
.invalidate_caches = vmw_invalidate_caches,
.init_mem_type = vmw_init_mem_type,
.evict_flags = vmw_evict_flags,
.move = NULL,
.verify_access = vmw_verify_access,
.sync_obj_signaled = vmw_sync_obj_signaled,
.sync_obj_wait = vmw_sync_obj_wait,
.sync_obj_flush = vmw_sync_obj_flush,
.sync_obj_unref = vmw_sync_obj_unref,
.sync_obj_ref = vmw_sync_obj_ref
};
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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 "drmP.h"
#include "vmwgfx_drv.h"
#include "ttm/ttm_placement.h"
#include "ttm/ttm_bo_driver.h"
#include "ttm/ttm_object.h"
#include "ttm/ttm_module.h"
#define VMWGFX_DRIVER_NAME "vmwgfx"
#define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices"
#define VMWGFX_CHIP_SVGAII 0
#define VMW_FB_RESERVATION 0
/**
* Fully encoded drm commands. Might move to vmw_drm.h
*/
#define DRM_IOCTL_VMW_GET_PARAM \
DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_GET_PARAM, \
struct drm_vmw_getparam_arg)
#define DRM_IOCTL_VMW_ALLOC_DMABUF \
DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_ALLOC_DMABUF, \
union drm_vmw_alloc_dmabuf_arg)
#define DRM_IOCTL_VMW_UNREF_DMABUF \
DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_DMABUF, \
struct drm_vmw_unref_dmabuf_arg)
#define DRM_IOCTL_VMW_CURSOR_BYPASS \
DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_CURSOR_BYPASS, \
struct drm_vmw_cursor_bypass_arg)
#define DRM_IOCTL_VMW_CONTROL_STREAM \
DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_CONTROL_STREAM, \
struct drm_vmw_control_stream_arg)
#define DRM_IOCTL_VMW_CLAIM_STREAM \
DRM_IOR(DRM_COMMAND_BASE + DRM_VMW_CLAIM_STREAM, \
struct drm_vmw_stream_arg)
#define DRM_IOCTL_VMW_UNREF_STREAM \
DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_STREAM, \
struct drm_vmw_stream_arg)
#define DRM_IOCTL_VMW_CREATE_CONTEXT \
DRM_IOR(DRM_COMMAND_BASE + DRM_VMW_CREATE_CONTEXT, \
struct drm_vmw_context_arg)
#define DRM_IOCTL_VMW_UNREF_CONTEXT \
DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_CONTEXT, \
struct drm_vmw_context_arg)
#define DRM_IOCTL_VMW_CREATE_SURFACE \
DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_CREATE_SURFACE, \
union drm_vmw_surface_create_arg)
#define DRM_IOCTL_VMW_UNREF_SURFACE \
DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_SURFACE, \
struct drm_vmw_surface_arg)
#define DRM_IOCTL_VMW_REF_SURFACE \
DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_REF_SURFACE, \
union drm_vmw_surface_reference_arg)
#define DRM_IOCTL_VMW_EXECBUF \
DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_EXECBUF, \
struct drm_vmw_execbuf_arg)
#define DRM_IOCTL_VMW_FIFO_DEBUG \
DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_FIFO_DEBUG, \
struct drm_vmw_fifo_debug_arg)
#define DRM_IOCTL_VMW_FENCE_WAIT \
DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_FENCE_WAIT, \
struct drm_vmw_fence_wait_arg)
/**
* The core DRM version of this macro doesn't account for
* DRM_COMMAND_BASE.
*/
#define VMW_IOCTL_DEF(ioctl, func, flags) \
[DRM_IOCTL_NR(ioctl) - DRM_COMMAND_BASE] = {ioctl, flags, func}
/**
* Ioctl definitions.
*/
static struct drm_ioctl_desc vmw_ioctls[] = {
VMW_IOCTL_DEF(DRM_IOCTL_VMW_GET_PARAM, vmw_getparam_ioctl, 0),
VMW_IOCTL_DEF(DRM_IOCTL_VMW_ALLOC_DMABUF, vmw_dmabuf_alloc_ioctl,
0),
VMW_IOCTL_DEF(DRM_IOCTL_VMW_UNREF_DMABUF, vmw_dmabuf_unref_ioctl,
0),
VMW_IOCTL_DEF(DRM_IOCTL_VMW_CURSOR_BYPASS,
vmw_kms_cursor_bypass_ioctl, 0),
VMW_IOCTL_DEF(DRM_IOCTL_VMW_CONTROL_STREAM, vmw_overlay_ioctl,
0),
VMW_IOCTL_DEF(DRM_IOCTL_VMW_CLAIM_STREAM, vmw_stream_claim_ioctl,
0),
VMW_IOCTL_DEF(DRM_IOCTL_VMW_UNREF_STREAM, vmw_stream_unref_ioctl,
0),
VMW_IOCTL_DEF(DRM_IOCTL_VMW_CREATE_CONTEXT, vmw_context_define_ioctl,
0),
VMW_IOCTL_DEF(DRM_IOCTL_VMW_UNREF_CONTEXT, vmw_context_destroy_ioctl,
0),
VMW_IOCTL_DEF(DRM_IOCTL_VMW_CREATE_SURFACE, vmw_surface_define_ioctl,
0),
VMW_IOCTL_DEF(DRM_IOCTL_VMW_UNREF_SURFACE, vmw_surface_destroy_ioctl,
0),
VMW_IOCTL_DEF(DRM_IOCTL_VMW_REF_SURFACE, vmw_surface_reference_ioctl,
0),
VMW_IOCTL_DEF(DRM_IOCTL_VMW_EXECBUF, vmw_execbuf_ioctl,
0),
VMW_IOCTL_DEF(DRM_IOCTL_VMW_FIFO_DEBUG, vmw_fifo_debug_ioctl,
0),
VMW_IOCTL_DEF(DRM_IOCTL_VMW_FENCE_WAIT, vmw_fence_wait_ioctl,
0)
};
static struct pci_device_id vmw_pci_id_list[] = {
{0x15ad, 0x0405, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VMWGFX_CHIP_SVGAII},
{0, 0, 0}
};
static char *vmw_devname = "vmwgfx";
static int vmw_probe(struct pci_dev *, const struct pci_device_id *);
static void vmw_master_init(struct vmw_master *);
static void vmw_print_capabilities(uint32_t capabilities)
{
DRM_INFO("Capabilities:\n");
if (capabilities & SVGA_CAP_RECT_COPY)
DRM_INFO(" Rect copy.\n");
if (capabilities & SVGA_CAP_CURSOR)
DRM_INFO(" Cursor.\n");
if (capabilities & SVGA_CAP_CURSOR_BYPASS)
DRM_INFO(" Cursor bypass.\n");
if (capabilities & SVGA_CAP_CURSOR_BYPASS_2)
DRM_INFO(" Cursor bypass 2.\n");
if (capabilities & SVGA_CAP_8BIT_EMULATION)
DRM_INFO(" 8bit emulation.\n");
if (capabilities & SVGA_CAP_ALPHA_CURSOR)
DRM_INFO(" Alpha cursor.\n");
if (capabilities & SVGA_CAP_3D)
DRM_INFO(" 3D.\n");
if (capabilities & SVGA_CAP_EXTENDED_FIFO)
DRM_INFO(" Extended Fifo.\n");
if (capabilities & SVGA_CAP_MULTIMON)
DRM_INFO(" Multimon.\n");
if (capabilities & SVGA_CAP_PITCHLOCK)
DRM_INFO(" Pitchlock.\n");
if (capabilities & SVGA_CAP_IRQMASK)
DRM_INFO(" Irq mask.\n");
if (capabilities & SVGA_CAP_DISPLAY_TOPOLOGY)
DRM_INFO(" Display Topology.\n");
if (capabilities & SVGA_CAP_GMR)
DRM_INFO(" GMR.\n");
if (capabilities & SVGA_CAP_TRACES)
DRM_INFO(" Traces.\n");
}
static int vmw_request_device(struct vmw_private *dev_priv)
{
int ret;
vmw_kms_save_vga(dev_priv);
ret = vmw_fifo_init(dev_priv, &dev_priv->fifo);
if (unlikely(ret != 0)) {
DRM_ERROR("Unable to initialize FIFO.\n");
return ret;
}
return 0;
}
static void vmw_release_device(struct vmw_private *dev_priv)
{
vmw_fifo_release(dev_priv, &dev_priv->fifo);
vmw_kms_restore_vga(dev_priv);
}
static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
{
struct vmw_private *dev_priv;
int ret;
dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
if (unlikely(dev_priv == NULL)) {
DRM_ERROR("Failed allocating a device private struct.\n");
return -ENOMEM;
}
memset(dev_priv, 0, sizeof(*dev_priv));
dev_priv->dev = dev;
dev_priv->vmw_chipset = chipset;
mutex_init(&dev_priv->hw_mutex);
mutex_init(&dev_priv->cmdbuf_mutex);
rwlock_init(&dev_priv->resource_lock);
idr_init(&dev_priv->context_idr);
idr_init(&dev_priv->surface_idr);
idr_init(&dev_priv->stream_idr);
ida_init(&dev_priv->gmr_ida);
mutex_init(&dev_priv->init_mutex);
init_waitqueue_head(&dev_priv->fence_queue);
init_waitqueue_head(&dev_priv->fifo_queue);
atomic_set(&dev_priv->fence_queue_waiters, 0);
atomic_set(&dev_priv->fifo_queue_waiters, 0);
INIT_LIST_HEAD(&dev_priv->gmr_lru);
dev_priv->io_start = pci_resource_start(dev->pdev, 0);
dev_priv->vram_start = pci_resource_start(dev->pdev, 1);
dev_priv->mmio_start = pci_resource_start(dev->pdev, 2);
mutex_lock(&dev_priv->hw_mutex);
dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES);
if (dev_priv->capabilities & SVGA_CAP_GMR) {
dev_priv->max_gmr_descriptors =
vmw_read(dev_priv,
SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH);
dev_priv->max_gmr_ids =
vmw_read(dev_priv, SVGA_REG_GMR_MAX_IDS);
}
dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE);
dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE);
dev_priv->fb_max_width = vmw_read(dev_priv, SVGA_REG_MAX_WIDTH);
dev_priv->fb_max_height = vmw_read(dev_priv, SVGA_REG_MAX_HEIGHT);
mutex_unlock(&dev_priv->hw_mutex);
vmw_print_capabilities(dev_priv->capabilities);
if (dev_priv->capabilities & SVGA_CAP_GMR) {
DRM_INFO("Max GMR ids is %u\n",
(unsigned)dev_priv->max_gmr_ids);
DRM_INFO("Max GMR descriptors is %u\n",
(unsigned)dev_priv->max_gmr_descriptors);
}
DRM_INFO("VRAM at 0x%08x size is %u kiB\n",
dev_priv->vram_start, dev_priv->vram_size / 1024);
DRM_INFO("MMIO at 0x%08x size is %u kiB\n",
dev_priv->mmio_start, dev_priv->mmio_size / 1024);
ret = vmw_ttm_global_init(dev_priv);
if (unlikely(ret != 0))
goto out_err0;
vmw_master_init(&dev_priv->fbdev_master);
ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM);
dev_priv->active_master = &dev_priv->fbdev_master;
ret = ttm_bo_device_init(&dev_priv->bdev,
dev_priv->bo_global_ref.ref.object,
&vmw_bo_driver, VMWGFX_FILE_PAGE_OFFSET,
false);
if (unlikely(ret != 0)) {
DRM_ERROR("Failed initializing TTM buffer object driver.\n");
goto out_err1;
}
ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_VRAM,
(dev_priv->vram_size >> PAGE_SHIFT));
if (unlikely(ret != 0)) {
DRM_ERROR("Failed initializing memory manager for VRAM.\n");
goto out_err2;
}
dev_priv->mmio_mtrr = drm_mtrr_add(dev_priv->mmio_start,
dev_priv->mmio_size, DRM_MTRR_WC);
dev_priv->mmio_virt = ioremap_wc(dev_priv->mmio_start,
dev_priv->mmio_size);
if (unlikely(dev_priv->mmio_virt == NULL)) {
ret = -ENOMEM;
DRM_ERROR("Failed mapping MMIO.\n");
goto out_err3;
}
dev_priv->tdev = ttm_object_device_init
(dev_priv->mem_global_ref.object, 12);
if (unlikely(dev_priv->tdev == NULL)) {
DRM_ERROR("Unable to initialize TTM object management.\n");
ret = -ENOMEM;
goto out_err4;
}
dev->dev_private = dev_priv;
if (!dev->devname)
dev->devname = vmw_devname;
if (dev_priv->capabilities & SVGA_CAP_IRQMASK) {
ret = drm_irq_install(dev);
if (unlikely(ret != 0)) {
DRM_ERROR("Failed installing irq: %d\n", ret);
goto out_no_irq;
}
}
ret = pci_request_regions(dev->pdev, "vmwgfx probe");
dev_priv->stealth = (ret != 0);
if (dev_priv->stealth) {
/**
* Request at least the mmio PCI resource.
*/
DRM_INFO("It appears like vesafb is loaded. "
"Ignore above error if any. Entering stealth mode.\n");
ret = pci_request_region(dev->pdev, 2, "vmwgfx stealth probe");
if (unlikely(ret != 0)) {
DRM_ERROR("Failed reserving the SVGA MMIO resource.\n");
goto out_no_device;
}
vmw_kms_init(dev_priv);
vmw_overlay_init(dev_priv);
} else {
ret = vmw_request_device(dev_priv);
if (unlikely(ret != 0))
goto out_no_device;
vmw_kms_init(dev_priv);
vmw_overlay_init(dev_priv);
vmw_fb_init(dev_priv);
}
return 0;
out_no_device:
if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
drm_irq_uninstall(dev_priv->dev);
if (dev->devname == vmw_devname)
dev->devname = NULL;
out_no_irq:
ttm_object_device_release(&dev_priv->tdev);
out_err4:
iounmap(dev_priv->mmio_virt);
out_err3:
drm_mtrr_del(dev_priv->mmio_mtrr, dev_priv->mmio_start,
dev_priv->mmio_size, DRM_MTRR_WC);
(void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
out_err2:
(void)ttm_bo_device_release(&dev_priv->bdev);
out_err1:
vmw_ttm_global_release(dev_priv);
out_err0:
ida_destroy(&dev_priv->gmr_ida);
idr_destroy(&dev_priv->surface_idr);
idr_destroy(&dev_priv->context_idr);
idr_destroy(&dev_priv->stream_idr);
kfree(dev_priv);
return ret;
}
static int vmw_driver_unload(struct drm_device *dev)
{
struct vmw_private *dev_priv = vmw_priv(dev);
DRM_INFO(VMWGFX_DRIVER_NAME " unload.\n");
if (!dev_priv->stealth) {
vmw_fb_close(dev_priv);
vmw_kms_close(dev_priv);
vmw_overlay_close(dev_priv);
vmw_release_device(dev_priv);
pci_release_regions(dev->pdev);
} else {
vmw_kms_close(dev_priv);
vmw_overlay_close(dev_priv);
pci_release_region(dev->pdev, 2);
}
if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
drm_irq_uninstall(dev_priv->dev);
if (dev->devname == vmw_devname)
dev->devname = NULL;
ttm_object_device_release(&dev_priv->tdev);
iounmap(dev_priv->mmio_virt);
drm_mtrr_del(dev_priv->mmio_mtrr, dev_priv->mmio_start,
dev_priv->mmio_size, DRM_MTRR_WC);
(void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
(void)ttm_bo_device_release(&dev_priv->bdev);
vmw_ttm_global_release(dev_priv);
ida_destroy(&dev_priv->gmr_ida);
idr_destroy(&dev_priv->surface_idr);
idr_destroy(&dev_priv->context_idr);
idr_destroy(&dev_priv->stream_idr);
kfree(dev_priv);
return 0;
}
static void vmw_postclose(struct drm_device *dev,
struct drm_file *file_priv)
{
struct vmw_fpriv *vmw_fp;
vmw_fp = vmw_fpriv(file_priv);
ttm_object_file_release(&vmw_fp->tfile);
if (vmw_fp->locked_master)
drm_master_put(&vmw_fp->locked_master);
kfree(vmw_fp);
}
static int vmw_driver_open(struct drm_device *dev, struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct vmw_fpriv *vmw_fp;
int ret = -ENOMEM;
vmw_fp = kzalloc(sizeof(*vmw_fp), GFP_KERNEL);
if (unlikely(vmw_fp == NULL))
return ret;
vmw_fp->tfile = ttm_object_file_init(dev_priv->tdev, 10);
if (unlikely(vmw_fp->tfile == NULL))
goto out_no_tfile;
file_priv->driver_priv = vmw_fp;
if (unlikely(dev_priv->bdev.dev_mapping == NULL))
dev_priv->bdev.dev_mapping =
file_priv->filp->f_path.dentry->d_inode->i_mapping;
return 0;
out_no_tfile:
kfree(vmw_fp);
return ret;
}
static long vmw_unlocked_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct drm_file *file_priv = filp->private_data;
struct drm_device *dev = file_priv->minor->dev;
unsigned int nr = DRM_IOCTL_NR(cmd);
long ret;
/*
* The driver private ioctls and TTM ioctls should be
* thread-safe.
*/
if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END)
&& (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) {
struct drm_ioctl_desc *ioctl =
&vmw_ioctls[nr - DRM_COMMAND_BASE];
if (unlikely(ioctl->cmd != cmd)) {
DRM_ERROR("Invalid command format, ioctl %d\n",
nr - DRM_COMMAND_BASE);
return -EINVAL;
}
return drm_ioctl(filp->f_path.dentry->d_inode,
filp, cmd, arg);
}
/*
* Not all old drm ioctls are thread-safe.
*/
lock_kernel();
ret = drm_ioctl(filp->f_path.dentry->d_inode, filp, cmd, arg);
unlock_kernel();
return ret;
}
static int vmw_firstopen(struct drm_device *dev)
{
struct vmw_private *dev_priv = vmw_priv(dev);
dev_priv->is_opened = true;
return 0;
}
static void vmw_lastclose(struct drm_device *dev)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct drm_crtc *crtc;
struct drm_mode_set set;
int ret;
/**
* Do nothing on the lastclose call from drm_unload.
*/
if (!dev_priv->is_opened)
return;
dev_priv->is_opened = false;
set.x = 0;
set.y = 0;
set.fb = NULL;
set.mode = NULL;
set.connectors = NULL;
set.num_connectors = 0;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
set.crtc = crtc;
ret = crtc->funcs->set_config(&set);
WARN_ON(ret != 0);
}
}
static void vmw_master_init(struct vmw_master *vmaster)
{
ttm_lock_init(&vmaster->lock);
}
static int vmw_master_create(struct drm_device *dev,
struct drm_master *master)
{
struct vmw_master *vmaster;
DRM_INFO("Master create.\n");
vmaster = kzalloc(sizeof(*vmaster), GFP_KERNEL);
if (unlikely(vmaster == NULL))
return -ENOMEM;
ttm_lock_init(&vmaster->lock);
ttm_lock_set_kill(&vmaster->lock, true, SIGTERM);
master->driver_priv = vmaster;
return 0;
}
static void vmw_master_destroy(struct drm_device *dev,
struct drm_master *master)
{
struct vmw_master *vmaster = vmw_master(master);
DRM_INFO("Master destroy.\n");
master->driver_priv = NULL;
kfree(vmaster);
}
static int vmw_master_set(struct drm_device *dev,
struct drm_file *file_priv,
bool from_open)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
struct vmw_master *active = dev_priv->active_master;
struct vmw_master *vmaster = vmw_master(file_priv->master);
int ret = 0;
DRM_INFO("Master set.\n");
if (dev_priv->stealth) {
ret = vmw_request_device(dev_priv);
if (unlikely(ret != 0))
return ret;
}
if (active) {
BUG_ON(active != &dev_priv->fbdev_master);
ret = ttm_vt_lock(&active->lock, false, vmw_fp->tfile);
if (unlikely(ret != 0))
goto out_no_active_lock;
ttm_lock_set_kill(&active->lock, true, SIGTERM);
ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM);
if (unlikely(ret != 0)) {
DRM_ERROR("Unable to clean VRAM on "
"master drop.\n");
}
dev_priv->active_master = NULL;
}
ttm_lock_set_kill(&vmaster->lock, false, SIGTERM);
if (!from_open) {
ttm_vt_unlock(&vmaster->lock);
BUG_ON(vmw_fp->locked_master != file_priv->master);
drm_master_put(&vmw_fp->locked_master);
}
dev_priv->active_master = vmaster;
return 0;
out_no_active_lock:
vmw_release_device(dev_priv);
return ret;
}
static void vmw_master_drop(struct drm_device *dev,
struct drm_file *file_priv,
bool from_release)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
struct vmw_master *vmaster = vmw_master(file_priv->master);
int ret;
DRM_INFO("Master drop.\n");
/**
* Make sure the master doesn't disappear while we have
* it locked.
*/
vmw_fp->locked_master = drm_master_get(file_priv->master);
ret = ttm_vt_lock(&vmaster->lock, false, vmw_fp->tfile);
if (unlikely((ret != 0))) {
DRM_ERROR("Unable to lock TTM at VT switch.\n");
drm_master_put(&vmw_fp->locked_master);
}
ttm_lock_set_kill(&vmaster->lock, true, SIGTERM);
if (dev_priv->stealth) {
ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM);
if (unlikely(ret != 0))
DRM_ERROR("Unable to clean VRAM on master drop.\n");
vmw_release_device(dev_priv);
}
dev_priv->active_master = &dev_priv->fbdev_master;
ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM);
ttm_vt_unlock(&dev_priv->fbdev_master.lock);
if (!dev_priv->stealth)
vmw_fb_on(dev_priv);
}
static void vmw_remove(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
drm_put_dev(dev);
}
static struct drm_driver driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED |
DRIVER_MODESET,
.load = vmw_driver_load,
.unload = vmw_driver_unload,
.firstopen = vmw_firstopen,
.lastclose = vmw_lastclose,
.irq_preinstall = vmw_irq_preinstall,
.irq_postinstall = vmw_irq_postinstall,
.irq_uninstall = vmw_irq_uninstall,
.irq_handler = vmw_irq_handler,
.reclaim_buffers_locked = NULL,
.get_map_ofs = drm_core_get_map_ofs,
.get_reg_ofs = drm_core_get_reg_ofs,
.ioctls = vmw_ioctls,
.num_ioctls = DRM_ARRAY_SIZE(vmw_ioctls),
.dma_quiescent = NULL, /*vmw_dma_quiescent, */
.master_create = vmw_master_create,
.master_destroy = vmw_master_destroy,
.master_set = vmw_master_set,
.master_drop = vmw_master_drop,
.open = vmw_driver_open,
.postclose = vmw_postclose,
.fops = {
.owner = THIS_MODULE,
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = vmw_unlocked_ioctl,
.mmap = vmw_mmap,
.poll = drm_poll,
.fasync = drm_fasync,
#if defined(CONFIG_COMPAT)
.compat_ioctl = drm_compat_ioctl,
#endif
},
.pci_driver = {
.name = VMWGFX_DRIVER_NAME,
.id_table = vmw_pci_id_list,
.probe = vmw_probe,
.remove = vmw_remove
},
.name = VMWGFX_DRIVER_NAME,
.desc = VMWGFX_DRIVER_DESC,
.date = VMWGFX_DRIVER_DATE,
.major = VMWGFX_DRIVER_MAJOR,
.minor = VMWGFX_DRIVER_MINOR,
.patchlevel = VMWGFX_DRIVER_PATCHLEVEL
};
static int vmw_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
return drm_get_dev(pdev, ent, &driver);
}
static int __init vmwgfx_init(void)
{
int ret;
ret = drm_init(&driver);
if (ret)
DRM_ERROR("Failed initializing DRM.\n");
return ret;
}
static void __exit vmwgfx_exit(void)
{
drm_exit(&driver);
}
module_init(vmwgfx_init);
module_exit(vmwgfx_exit);
MODULE_AUTHOR("VMware Inc. and others");
MODULE_DESCRIPTION("Standalone drm driver for the VMware SVGA device");
MODULE_LICENSE("GPL and additional rights");
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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.
*
**************************************************************************/
#ifndef _VMWGFX_DRV_H_
#define _VMWGFX_DRV_H_
#include "vmwgfx_reg.h"
#include "drmP.h"
#include "vmwgfx_drm.h"
#include "drm_hashtab.h"
#include "ttm/ttm_bo_driver.h"
#include "ttm/ttm_object.h"
#include "ttm/ttm_lock.h"
#include "ttm/ttm_execbuf_util.h"
#include "ttm/ttm_module.h"
#define VMWGFX_DRIVER_DATE "20090724"
#define VMWGFX_DRIVER_MAJOR 0
#define VMWGFX_DRIVER_MINOR 1
#define VMWGFX_DRIVER_PATCHLEVEL 2
#define VMWGFX_FILE_PAGE_OFFSET 0x00100000
#define VMWGFX_FIFO_STATIC_SIZE (1024*1024)
#define VMWGFX_MAX_RELOCATIONS 2048
#define VMWGFX_MAX_GMRS 2048
struct vmw_fpriv {
struct drm_master *locked_master;
struct ttm_object_file *tfile;
};
struct vmw_dma_buffer {
struct ttm_buffer_object base;
struct list_head validate_list;
struct list_head gmr_lru;
uint32_t gmr_id;
bool gmr_bound;
uint32_t cur_validate_node;
bool on_validate_list;
};
struct vmw_resource {
struct kref kref;
struct vmw_private *dev_priv;
struct idr *idr;
int id;
enum ttm_object_type res_type;
bool avail;
void (*hw_destroy) (struct vmw_resource *res);
void (*res_free) (struct vmw_resource *res);
/* TODO is a generic snooper needed? */
#if 0
void (*snoop)(struct vmw_resource *res,
struct ttm_object_file *tfile,
SVGA3dCmdHeader *header);
void *snoop_priv;
#endif
};
struct vmw_cursor_snooper {
struct drm_crtc *crtc;
size_t age;
uint32_t *image;
};
struct vmw_surface {
struct vmw_resource res;
uint32_t flags;
uint32_t format;
uint32_t mip_levels[DRM_VMW_MAX_SURFACE_FACES];
struct drm_vmw_size *sizes;
uint32_t num_sizes;
/* TODO so far just a extra pointer */
struct vmw_cursor_snooper snooper;
};
struct vmw_fifo_state {
unsigned long reserved_size;
__le32 *dynamic_buffer;
__le32 *static_buffer;
__le32 *last_buffer;
uint32_t last_data_size;
uint32_t last_buffer_size;
bool last_buffer_add;
unsigned long static_buffer_size;
bool using_bounce_buffer;
uint32_t capabilities;
struct rw_semaphore rwsem;
};
struct vmw_relocation {
SVGAGuestPtr *location;
uint32_t index;
};
struct vmw_sw_context{
struct ida bo_list;
uint32_t last_cid;
bool cid_valid;
uint32_t last_sid;
bool sid_valid;
struct ttm_object_file *tfile;
struct list_head validate_nodes;
struct vmw_relocation relocs[VMWGFX_MAX_RELOCATIONS];
uint32_t cur_reloc;
struct ttm_validate_buffer val_bufs[VMWGFX_MAX_GMRS];
uint32_t cur_val_buf;
};
struct vmw_legacy_display;
struct vmw_overlay;
struct vmw_master {
struct ttm_lock lock;
};
struct vmw_private {
struct ttm_bo_device bdev;
struct ttm_bo_global_ref bo_global_ref;
struct ttm_global_reference mem_global_ref;
struct vmw_fifo_state fifo;
struct drm_device *dev;
unsigned long vmw_chipset;
unsigned int io_start;
uint32_t vram_start;
uint32_t vram_size;
uint32_t mmio_start;
uint32_t mmio_size;
uint32_t fb_max_width;
uint32_t fb_max_height;
__le32 __iomem *mmio_virt;
int mmio_mtrr;
uint32_t capabilities;
uint32_t max_gmr_descriptors;
uint32_t max_gmr_ids;
struct mutex hw_mutex;
/*
* VGA registers.
*/
uint32_t vga_width;
uint32_t vga_height;
uint32_t vga_depth;
uint32_t vga_bpp;
uint32_t vga_pseudo;
uint32_t vga_red_mask;
uint32_t vga_blue_mask;
uint32_t vga_green_mask;
/*
* Framebuffer info.
*/
void *fb_info;
struct vmw_legacy_display *ldu_priv;
struct vmw_overlay *overlay_priv;
/*
* Context and surface management.
*/
rwlock_t resource_lock;
struct idr context_idr;
struct idr surface_idr;
struct idr stream_idr;
/*
* Block lastclose from racing with firstopen.
*/
struct mutex init_mutex;
/*
* A resource manager for kernel-only surfaces and
* contexts.
*/
struct ttm_object_device *tdev;
/*
* Fencing and IRQs.
*/
uint32_t fence_seq;
wait_queue_head_t fence_queue;
wait_queue_head_t fifo_queue;
atomic_t fence_queue_waiters;
atomic_t fifo_queue_waiters;
uint32_t last_read_sequence;
spinlock_t irq_lock;
/*
* Device state
*/
uint32_t traces_state;
uint32_t enable_state;
uint32_t config_done_state;
/**
* Execbuf
*/
/**
* Protected by the cmdbuf mutex.
*/
struct vmw_sw_context ctx;
uint32_t val_seq;
struct mutex cmdbuf_mutex;
/**
* GMR management. Protected by the lru spinlock.
*/
struct ida gmr_ida;
struct list_head gmr_lru;
/**
* Operating mode.
*/
bool stealth;
bool is_opened;
/**
* Master management.
*/
struct vmw_master *active_master;
struct vmw_master fbdev_master;
};
static inline struct vmw_private *vmw_priv(struct drm_device *dev)
{
return (struct vmw_private *)dev->dev_private;
}
static inline struct vmw_fpriv *vmw_fpriv(struct drm_file *file_priv)
{
return (struct vmw_fpriv *)file_priv->driver_priv;
}
static inline struct vmw_master *vmw_master(struct drm_master *master)
{
return (struct vmw_master *) master->driver_priv;
}
static inline void vmw_write(struct vmw_private *dev_priv,
unsigned int offset, uint32_t value)
{
outl(offset, dev_priv->io_start + VMWGFX_INDEX_PORT);
outl(value, dev_priv->io_start + VMWGFX_VALUE_PORT);
}
static inline uint32_t vmw_read(struct vmw_private *dev_priv,
unsigned int offset)
{
uint32_t val;
outl(offset, dev_priv->io_start + VMWGFX_INDEX_PORT);
val = inl(dev_priv->io_start + VMWGFX_VALUE_PORT);
return val;
}
/**
* GMR utilities - vmwgfx_gmr.c
*/
extern int vmw_gmr_bind(struct vmw_private *dev_priv,
struct ttm_buffer_object *bo);
extern void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id);
/**
* Resource utilities - vmwgfx_resource.c
*/
extern struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv);
extern void vmw_resource_unreference(struct vmw_resource **p_res);
extern struct vmw_resource *vmw_resource_reference(struct vmw_resource *res);
extern int vmw_context_destroy_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int vmw_context_define_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int vmw_context_check(struct vmw_private *dev_priv,
struct ttm_object_file *tfile,
int id);
extern void vmw_surface_res_free(struct vmw_resource *res);
extern int vmw_surface_init(struct vmw_private *dev_priv,
struct vmw_surface *srf,
void (*res_free) (struct vmw_resource *res));
extern int vmw_user_surface_lookup(struct vmw_private *dev_priv,
struct ttm_object_file *tfile,
int sid, struct vmw_surface **out);
extern int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int vmw_surface_reference_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int vmw_surface_check(struct vmw_private *dev_priv,
struct ttm_object_file *tfile,
int id);
extern void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo);
extern int vmw_dmabuf_init(struct vmw_private *dev_priv,
struct vmw_dma_buffer *vmw_bo,
size_t size, struct ttm_placement *placement,
bool interuptable,
void (*bo_free) (struct ttm_buffer_object *bo));
extern int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int vmw_dmabuf_unref_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern uint32_t vmw_dmabuf_validate_node(struct ttm_buffer_object *bo,
uint32_t cur_validate_node);
extern void vmw_dmabuf_validate_clear(struct ttm_buffer_object *bo);
extern int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,
uint32_t id, struct vmw_dma_buffer **out);
extern uint32_t vmw_dmabuf_gmr(struct ttm_buffer_object *bo);
extern void vmw_dmabuf_set_gmr(struct ttm_buffer_object *bo, uint32_t id);
extern int vmw_gmr_id_alloc(struct vmw_private *dev_priv, uint32_t *p_id);
extern int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv,
struct vmw_dma_buffer *bo);
extern int vmw_dmabuf_from_vram(struct vmw_private *vmw_priv,
struct vmw_dma_buffer *bo);
extern int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int vmw_stream_unref_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int vmw_user_stream_lookup(struct vmw_private *dev_priv,
struct ttm_object_file *tfile,
uint32_t *inout_id,
struct vmw_resource **out);
/**
* Misc Ioctl functionality - vmwgfx_ioctl.c
*/
extern int vmw_getparam_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int vmw_fifo_debug_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
/**
* Fifo utilities - vmwgfx_fifo.c
*/
extern int vmw_fifo_init(struct vmw_private *dev_priv,
struct vmw_fifo_state *fifo);
extern void vmw_fifo_release(struct vmw_private *dev_priv,
struct vmw_fifo_state *fifo);
extern void *vmw_fifo_reserve(struct vmw_private *dev_priv, uint32_t bytes);
extern void vmw_fifo_commit(struct vmw_private *dev_priv, uint32_t bytes);
extern int vmw_fifo_send_fence(struct vmw_private *dev_priv,
uint32_t *sequence);
extern void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason);
extern int vmw_fifo_mmap(struct file *filp, struct vm_area_struct *vma);
/**
* TTM glue - vmwgfx_ttm_glue.c
*/
extern int vmw_ttm_global_init(struct vmw_private *dev_priv);
extern void vmw_ttm_global_release(struct vmw_private *dev_priv);
extern int vmw_mmap(struct file *filp, struct vm_area_struct *vma);
/**
* TTM buffer object driver - vmwgfx_buffer.c
*/
extern struct ttm_placement vmw_vram_placement;
extern struct ttm_placement vmw_vram_ne_placement;
extern struct ttm_placement vmw_sys_placement;
extern struct ttm_bo_driver vmw_bo_driver;
extern int vmw_dma_quiescent(struct drm_device *dev);
/**
* Command submission - vmwgfx_execbuf.c
*/
extern int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
/**
* IRQs and wating - vmwgfx_irq.c
*/
extern irqreturn_t vmw_irq_handler(DRM_IRQ_ARGS);
extern int vmw_wait_fence(struct vmw_private *dev_priv, bool lazy,
uint32_t sequence, bool interruptible,
unsigned long timeout);
extern void vmw_irq_preinstall(struct drm_device *dev);
extern int vmw_irq_postinstall(struct drm_device *dev);
extern void vmw_irq_uninstall(struct drm_device *dev);
extern bool vmw_fence_signaled(struct vmw_private *dev_priv,
uint32_t sequence);
extern int vmw_fence_wait_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int vmw_fallback_wait(struct vmw_private *dev_priv,
bool lazy,
bool fifo_idle,
uint32_t sequence,
bool interruptible,
unsigned long timeout);
/**
* Kernel framebuffer - vmwgfx_fb.c
*/
int vmw_fb_init(struct vmw_private *vmw_priv);
int vmw_fb_close(struct vmw_private *dev_priv);
int vmw_fb_off(struct vmw_private *vmw_priv);
int vmw_fb_on(struct vmw_private *vmw_priv);
/**
* Kernel modesetting - vmwgfx_kms.c
*/
int vmw_kms_init(struct vmw_private *dev_priv);
int vmw_kms_close(struct vmw_private *dev_priv);
int vmw_kms_save_vga(struct vmw_private *vmw_priv);
int vmw_kms_restore_vga(struct vmw_private *vmw_priv);
int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv);
void vmw_kms_cursor_snoop(struct vmw_surface *srf,
struct ttm_object_file *tfile,
struct ttm_buffer_object *bo,
SVGA3dCmdHeader *header);
/**
* Overlay control - vmwgfx_overlay.c
*/
int vmw_overlay_init(struct vmw_private *dev_priv);
int vmw_overlay_close(struct vmw_private *dev_priv);
int vmw_overlay_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int vmw_overlay_stop_all(struct vmw_private *dev_priv);
int vmw_overlay_resume_all(struct vmw_private *dev_priv);
int vmw_overlay_pause_all(struct vmw_private *dev_priv);
int vmw_overlay_claim(struct vmw_private *dev_priv, uint32_t *out);
int vmw_overlay_unref(struct vmw_private *dev_priv, uint32_t stream_id);
int vmw_overlay_num_overlays(struct vmw_private *dev_priv);
int vmw_overlay_num_free_overlays(struct vmw_private *dev_priv);
/**
* Inline helper functions
*/
static inline void vmw_surface_unreference(struct vmw_surface **srf)
{
struct vmw_surface *tmp_srf = *srf;
struct vmw_resource *res = &tmp_srf->res;
*srf = NULL;
vmw_resource_unreference(&res);
}
static inline struct vmw_surface *vmw_surface_reference(struct vmw_surface *srf)
{
(void) vmw_resource_reference(&srf->res);
return srf;
}
static inline void vmw_dmabuf_unreference(struct vmw_dma_buffer **buf)
{
struct vmw_dma_buffer *tmp_buf = *buf;
struct ttm_buffer_object *bo = &tmp_buf->base;
*buf = NULL;
ttm_bo_unref(&bo);
}
static inline struct vmw_dma_buffer *vmw_dmabuf_reference(struct vmw_dma_buffer *buf)
{
if (ttm_bo_reference(&buf->base))
return buf;
return NULL;
}
#endif
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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_drv.h"
#include "vmwgfx_reg.h"
#include "ttm/ttm_bo_api.h"
#include "ttm/ttm_placement.h"
static int vmw_cmd_invalid(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
SVGA3dCmdHeader *header)
{
return capable(CAP_SYS_ADMIN) ? : -EINVAL;
}
static int vmw_cmd_ok(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
SVGA3dCmdHeader *header)
{
return 0;
}
static int vmw_cmd_cid_check(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
SVGA3dCmdHeader *header)
{
struct vmw_cid_cmd {
SVGA3dCmdHeader header;
__le32 cid;
} *cmd;
int ret;
cmd = container_of(header, struct vmw_cid_cmd, header);
if (likely(sw_context->cid_valid && cmd->cid == sw_context->last_cid))
return 0;
ret = vmw_context_check(dev_priv, sw_context->tfile, cmd->cid);
if (unlikely(ret != 0)) {
DRM_ERROR("Could not find or use context %u\n",
(unsigned) cmd->cid);
return ret;
}
sw_context->last_cid = cmd->cid;
sw_context->cid_valid = true;
return 0;
}
static int vmw_cmd_sid_check(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
uint32_t sid)
{
if (unlikely((!sw_context->sid_valid || sid != sw_context->last_sid) &&
sid != SVGA3D_INVALID_ID)) {
int ret = vmw_surface_check(dev_priv, sw_context->tfile, sid);
if (unlikely(ret != 0)) {
DRM_ERROR("Could ot find or use surface %u\n",
(unsigned) sid);
return ret;
}
sw_context->last_sid = sid;
sw_context->sid_valid = true;
}
return 0;
}
static int vmw_cmd_set_render_target_check(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
SVGA3dCmdHeader *header)
{
struct vmw_sid_cmd {
SVGA3dCmdHeader header;
SVGA3dCmdSetRenderTarget body;
} *cmd;
int ret;
ret = vmw_cmd_cid_check(dev_priv, sw_context, header);
if (unlikely(ret != 0))
return ret;
cmd = container_of(header, struct vmw_sid_cmd, header);
return vmw_cmd_sid_check(dev_priv, sw_context, cmd->body.target.sid);
}
static int vmw_cmd_surface_copy_check(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
SVGA3dCmdHeader *header)
{
struct vmw_sid_cmd {
SVGA3dCmdHeader header;
SVGA3dCmdSurfaceCopy body;
} *cmd;
int ret;
cmd = container_of(header, struct vmw_sid_cmd, header);
ret = vmw_cmd_sid_check(dev_priv, sw_context, cmd->body.src.sid);
if (unlikely(ret != 0))
return ret;
return vmw_cmd_sid_check(dev_priv, sw_context, cmd->body.dest.sid);
}
static int vmw_cmd_stretch_blt_check(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
SVGA3dCmdHeader *header)
{
struct vmw_sid_cmd {
SVGA3dCmdHeader header;
SVGA3dCmdSurfaceStretchBlt body;
} *cmd;
int ret;
cmd = container_of(header, struct vmw_sid_cmd, header);
ret = vmw_cmd_sid_check(dev_priv, sw_context, cmd->body.src.sid);
if (unlikely(ret != 0))
return ret;
return vmw_cmd_sid_check(dev_priv, sw_context, cmd->body.dest.sid);
}
static int vmw_cmd_blt_surf_screen_check(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
SVGA3dCmdHeader *header)
{
struct vmw_sid_cmd {
SVGA3dCmdHeader header;
SVGA3dCmdBlitSurfaceToScreen body;
} *cmd;
cmd = container_of(header, struct vmw_sid_cmd, header);
return vmw_cmd_sid_check(dev_priv, sw_context, cmd->body.srcImage.sid);
}
static int vmw_cmd_present_check(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
SVGA3dCmdHeader *header)
{
struct vmw_sid_cmd {
SVGA3dCmdHeader header;
SVGA3dCmdPresent body;
} *cmd;
cmd = container_of(header, struct vmw_sid_cmd, header);
return vmw_cmd_sid_check(dev_priv, sw_context, cmd->body.sid);
}
static int vmw_cmd_dma(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
SVGA3dCmdHeader *header)
{
uint32_t handle;
struct vmw_dma_buffer *vmw_bo = NULL;
struct ttm_buffer_object *bo;
struct vmw_surface *srf = NULL;
struct vmw_dma_cmd {
SVGA3dCmdHeader header;
SVGA3dCmdSurfaceDMA dma;
} *cmd;
struct vmw_relocation *reloc;
int ret;
uint32_t cur_validate_node;
struct ttm_validate_buffer *val_buf;
cmd = container_of(header, struct vmw_dma_cmd, header);
ret = vmw_cmd_sid_check(dev_priv, sw_context, cmd->dma.host.sid);
if (unlikely(ret != 0))
return ret;
handle = cmd->dma.guest.ptr.gmrId;
ret = vmw_user_dmabuf_lookup(sw_context->tfile, handle, &vmw_bo);
if (unlikely(ret != 0)) {
DRM_ERROR("Could not find or use GMR region.\n");
return -EINVAL;
}
bo = &vmw_bo->base;
if (unlikely(sw_context->cur_reloc >= VMWGFX_MAX_RELOCATIONS)) {
DRM_ERROR("Max number of DMA commands per submission"
" exceeded\n");
ret = -EINVAL;
goto out_no_reloc;
}
reloc = &sw_context->relocs[sw_context->cur_reloc++];
reloc->location = &cmd->dma.guest.ptr;
cur_validate_node = vmw_dmabuf_validate_node(bo, sw_context->cur_val_buf);
if (unlikely(cur_validate_node >= VMWGFX_MAX_GMRS)) {
DRM_ERROR("Max number of DMA buffers per submission"
" exceeded.\n");
ret = -EINVAL;
goto out_no_reloc;
}
reloc->index = cur_validate_node;
if (unlikely(cur_validate_node == sw_context->cur_val_buf)) {
val_buf = &sw_context->val_bufs[cur_validate_node];
val_buf->bo = ttm_bo_reference(bo);
val_buf->new_sync_obj_arg = (void *) dev_priv;
list_add_tail(&val_buf->head, &sw_context->validate_nodes);
++sw_context->cur_val_buf;
}
ret = vmw_user_surface_lookup(dev_priv, sw_context->tfile,
cmd->dma.host.sid, &srf);
if (ret) {
DRM_ERROR("could not find surface\n");
goto out_no_reloc;
}
vmw_kms_cursor_snoop(srf, sw_context->tfile, bo, header);
vmw_surface_unreference(&srf);
out_no_reloc:
vmw_dmabuf_unreference(&vmw_bo);
return ret;
}
typedef int (*vmw_cmd_func) (struct vmw_private *,
struct vmw_sw_context *,
SVGA3dCmdHeader *);
#define VMW_CMD_DEF(cmd, func) \
[cmd - SVGA_3D_CMD_BASE] = func
static vmw_cmd_func vmw_cmd_funcs[SVGA_3D_CMD_MAX] = {
VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_DEFINE, &vmw_cmd_invalid),
VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_DESTROY, &vmw_cmd_invalid),
VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_COPY, &vmw_cmd_surface_copy_check),
VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_STRETCHBLT, &vmw_cmd_stretch_blt_check),
VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_DMA, &vmw_cmd_dma),
VMW_CMD_DEF(SVGA_3D_CMD_CONTEXT_DEFINE, &vmw_cmd_invalid),
VMW_CMD_DEF(SVGA_3D_CMD_CONTEXT_DESTROY, &vmw_cmd_invalid),
VMW_CMD_DEF(SVGA_3D_CMD_SETTRANSFORM, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_SETZRANGE, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_SETRENDERSTATE, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_SETRENDERTARGET,
&vmw_cmd_set_render_target_check),
VMW_CMD_DEF(SVGA_3D_CMD_SETTEXTURESTATE, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_SETMATERIAL, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_SETLIGHTDATA, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_SETLIGHTENABLED, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_SETVIEWPORT, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_SETCLIPPLANE, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_CLEAR, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_PRESENT, &vmw_cmd_present_check),
VMW_CMD_DEF(SVGA_3D_CMD_SHADER_DEFINE, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_SHADER_DESTROY, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_SET_SHADER, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_SET_SHADER_CONST, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_DRAW_PRIMITIVES, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_SETSCISSORRECT, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_BEGIN_QUERY, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_END_QUERY, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_WAIT_FOR_QUERY, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_PRESENT_READBACK, &vmw_cmd_ok),
VMW_CMD_DEF(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN,
&vmw_cmd_blt_surf_screen_check)
};
static int vmw_cmd_check(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
void *buf, uint32_t *size)
{
uint32_t cmd_id;
SVGA3dCmdHeader *header = (SVGA3dCmdHeader *) buf;
int ret;
cmd_id = ((uint32_t *)buf)[0];
if (cmd_id == SVGA_CMD_UPDATE) {
*size = 5 << 2;
return 0;
}
cmd_id = le32_to_cpu(header->id);
*size = le32_to_cpu(header->size) + sizeof(SVGA3dCmdHeader);
cmd_id -= SVGA_3D_CMD_BASE;
if (unlikely(cmd_id >= SVGA_3D_CMD_MAX - SVGA_3D_CMD_BASE))
goto out_err;
ret = vmw_cmd_funcs[cmd_id](dev_priv, sw_context, header);
if (unlikely(ret != 0))
goto out_err;
return 0;
out_err:
DRM_ERROR("Illegal / Invalid SVGA3D command: %d\n",
cmd_id + SVGA_3D_CMD_BASE);
return -EINVAL;
}
static int vmw_cmd_check_all(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
void *buf, uint32_t size)
{
int32_t cur_size = size;
int ret;
while (cur_size > 0) {
ret = vmw_cmd_check(dev_priv, sw_context, buf, &size);
if (unlikely(ret != 0))
return ret;
buf = (void *)((unsigned long) buf + size);
cur_size -= size;
}
if (unlikely(cur_size != 0)) {
DRM_ERROR("Command verifier out of sync.\n");
return -EINVAL;
}
return 0;
}
static void vmw_free_relocations(struct vmw_sw_context *sw_context)
{
sw_context->cur_reloc = 0;
}
static void vmw_apply_relocations(struct vmw_sw_context *sw_context)
{
uint32_t i;
struct vmw_relocation *reloc;
struct ttm_validate_buffer *validate;
struct ttm_buffer_object *bo;
for (i = 0; i < sw_context->cur_reloc; ++i) {
reloc = &sw_context->relocs[i];
validate = &sw_context->val_bufs[reloc->index];
bo = validate->bo;
reloc->location->offset += bo->offset;
reloc->location->gmrId = vmw_dmabuf_gmr(bo);
}
vmw_free_relocations(sw_context);
}
static void vmw_clear_validations(struct vmw_sw_context *sw_context)
{
struct ttm_validate_buffer *entry, *next;
list_for_each_entry_safe(entry, next, &sw_context->validate_nodes,
head) {
list_del(&entry->head);
vmw_dmabuf_validate_clear(entry->bo);
ttm_bo_unref(&entry->bo);
sw_context->cur_val_buf--;
}
BUG_ON(sw_context->cur_val_buf != 0);
}
static int vmw_validate_single_buffer(struct vmw_private *dev_priv,
struct ttm_buffer_object *bo)
{
int ret;
if (vmw_dmabuf_gmr(bo) != SVGA_GMR_NULL)
return 0;
ret = vmw_gmr_bind(dev_priv, bo);
if (likely(ret == 0 || ret == -ERESTART))
return ret;
ret = ttm_bo_validate(bo, &vmw_vram_placement, true, false);
return ret;
}
static int vmw_validate_buffers(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context)
{
struct ttm_validate_buffer *entry;
int ret;
list_for_each_entry(entry, &sw_context->validate_nodes, head) {
ret = vmw_validate_single_buffer(dev_priv, entry->bo);
if (unlikely(ret != 0))
return ret;
}
return 0;
}
int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct drm_vmw_execbuf_arg *arg = (struct drm_vmw_execbuf_arg *)data;
struct drm_vmw_fence_rep fence_rep;
struct drm_vmw_fence_rep __user *user_fence_rep;
int ret;
void *user_cmd;
void *cmd;
uint32_t sequence;
struct vmw_sw_context *sw_context = &dev_priv->ctx;
struct vmw_master *vmaster = vmw_master(file_priv->master);
ret = ttm_read_lock(&vmaster->lock, true);
if (unlikely(ret != 0))
return ret;
ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex);
if (unlikely(ret != 0)) {
ret = -ERESTART;
goto out_no_cmd_mutex;
}
cmd = vmw_fifo_reserve(dev_priv, arg->command_size);
if (unlikely(cmd == NULL)) {
DRM_ERROR("Failed reserving fifo space for commands.\n");
ret = -ENOMEM;
goto out_unlock;
}
user_cmd = (void __user *)(unsigned long)arg->commands;
ret = copy_from_user(cmd, user_cmd, arg->command_size);
if (unlikely(ret != 0)) {
DRM_ERROR("Failed copying commands.\n");
goto out_commit;
}
sw_context->tfile = vmw_fpriv(file_priv)->tfile;
sw_context->cid_valid = false;
sw_context->sid_valid = false;
sw_context->cur_reloc = 0;
sw_context->cur_val_buf = 0;
INIT_LIST_HEAD(&sw_context->validate_nodes);
ret = vmw_cmd_check_all(dev_priv, sw_context, cmd, arg->command_size);
if (unlikely(ret != 0))
goto out_err;
ret = ttm_eu_reserve_buffers(&sw_context->validate_nodes,
dev_priv->val_seq++);
if (unlikely(ret != 0))
goto out_err;
ret = vmw_validate_buffers(dev_priv, sw_context);
if (unlikely(ret != 0))
goto out_err;
vmw_apply_relocations(sw_context);
vmw_fifo_commit(dev_priv, arg->command_size);
ret = vmw_fifo_send_fence(dev_priv, &sequence);
ttm_eu_fence_buffer_objects(&sw_context->validate_nodes,
(void *)(unsigned long) sequence);
vmw_clear_validations(sw_context);
mutex_unlock(&dev_priv->cmdbuf_mutex);
/*
* This error is harmless, because if fence submission fails,
* vmw_fifo_send_fence will sync.
*/
if (ret != 0)
DRM_ERROR("Fence submission error. Syncing.\n");
fence_rep.error = ret;
fence_rep.fence_seq = (uint64_t) sequence;
user_fence_rep = (struct drm_vmw_fence_rep __user *)
(unsigned long)arg->fence_rep;
/*
* copy_to_user errors will be detected by user space not
* seeing fence_rep::error filled in.
*/
ret = copy_to_user(user_fence_rep, &fence_rep, sizeof(fence_rep));
vmw_kms_cursor_post_execbuf(dev_priv);
ttm_read_unlock(&vmaster->lock);
return 0;
out_err:
vmw_free_relocations(sw_context);
ttm_eu_backoff_reservation(&sw_context->validate_nodes);
vmw_clear_validations(sw_context);
out_commit:
vmw_fifo_commit(dev_priv, 0);
out_unlock:
mutex_unlock(&dev_priv->cmdbuf_mutex);
out_no_cmd_mutex:
ttm_read_unlock(&vmaster->lock);
return ret;
}
/**************************************************************************
*
* Copyright © 2007 David Airlie
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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 "drmP.h"
#include "vmwgfx_drv.h"
#include "ttm/ttm_placement.h"
#define VMW_DIRTY_DELAY (HZ / 30)
struct vmw_fb_par {
struct vmw_private *vmw_priv;
void *vmalloc;
struct vmw_dma_buffer *vmw_bo;
struct ttm_bo_kmap_obj map;
u32 pseudo_palette[17];
unsigned depth;
unsigned bpp;
unsigned max_width;
unsigned max_height;
void *bo_ptr;
unsigned bo_size;
bool bo_iowrite;
struct {
spinlock_t lock;
bool active;
unsigned x1;
unsigned y1;
unsigned x2;
unsigned y2;
} dirty;
};
static int vmw_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp,
struct fb_info *info)
{
struct vmw_fb_par *par = info->par;
u32 *pal = par->pseudo_palette;
if (regno > 15) {
DRM_ERROR("Bad regno %u.\n", regno);
return 1;
}
switch (par->depth) {
case 24:
case 32:
pal[regno] = ((red & 0xff00) << 8) |
(green & 0xff00) |
((blue & 0xff00) >> 8);
break;
default:
DRM_ERROR("Bad depth %u, bpp %u.\n", par->depth, par->bpp);
return 1;
}
return 0;
}
static int vmw_fb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
int depth = var->bits_per_pixel;
struct vmw_fb_par *par = info->par;
struct vmw_private *vmw_priv = par->vmw_priv;
switch (var->bits_per_pixel) {
case 32:
depth = (var->transp.length > 0) ? 32 : 24;
break;
default:
DRM_ERROR("Bad bpp %u.\n", var->bits_per_pixel);
return -EINVAL;
}
switch (depth) {
case 24:
var->red.offset = 16;
var->green.offset = 8;
var->blue.offset = 0;
var->red.length = 8;
var->green.length = 8;
var->blue.length = 8;
var->transp.length = 0;
var->transp.offset = 0;
break;
case 32:
var->red.offset = 16;
var->green.offset = 8;
var->blue.offset = 0;
var->red.length = 8;
var->green.length = 8;
var->blue.length = 8;
var->transp.length = 8;
var->transp.offset = 24;
break;
default:
DRM_ERROR("Bad depth %u.\n", depth);
return -EINVAL;
}
/* without multimon its hard to resize */
if (!(vmw_priv->capabilities & SVGA_CAP_MULTIMON) &&
(var->xres != par->max_width ||
var->yres != par->max_height)) {
DRM_ERROR("Tried to resize, but we don't have multimon\n");
return -EINVAL;
}
if (var->xres > par->max_width ||
var->yres > par->max_height) {
DRM_ERROR("Requested geom can not fit in framebuffer\n");
return -EINVAL;
}
return 0;
}
static int vmw_fb_set_par(struct fb_info *info)
{
struct vmw_fb_par *par = info->par;
struct vmw_private *vmw_priv = par->vmw_priv;
if (vmw_priv->capabilities & SVGA_CAP_MULTIMON) {
vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, 0);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_IS_PRIMARY, true);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_X, 0);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_Y, 0);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_WIDTH, 0);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_HEIGHT, 0);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
vmw_write(vmw_priv, SVGA_REG_ENABLE, 1);
vmw_write(vmw_priv, SVGA_REG_WIDTH, par->max_width);
vmw_write(vmw_priv, SVGA_REG_HEIGHT, par->max_height);
vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, par->bpp);
vmw_write(vmw_priv, SVGA_REG_DEPTH, par->depth);
vmw_write(vmw_priv, SVGA_REG_RED_MASK, 0x00ff0000);
vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, 0x0000ff00);
vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, 0x000000ff);
/* TODO check if pitch and offset changes */
vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, 0);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_IS_PRIMARY, true);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_X, info->var.xoffset);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_Y, info->var.yoffset);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_WIDTH, info->var.xres);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_HEIGHT, info->var.yres);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
} else {
vmw_write(vmw_priv, SVGA_REG_WIDTH, info->var.xres);
vmw_write(vmw_priv, SVGA_REG_HEIGHT, info->var.yres);
/* TODO check if pitch and offset changes */
}
return 0;
}
static int vmw_fb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
return 0;
}
static int vmw_fb_blank(int blank, struct fb_info *info)
{
return 0;
}
/*
* Dirty code
*/
static void vmw_fb_dirty_flush(struct vmw_fb_par *par)
{
struct vmw_private *vmw_priv = par->vmw_priv;
struct fb_info *info = vmw_priv->fb_info;
int stride = (info->fix.line_length / 4);
int *src = (int *)info->screen_base;
__le32 __iomem *vram_mem = par->bo_ptr;
unsigned long flags;
unsigned x, y, w, h;
int i, k;
struct {
uint32_t header;
SVGAFifoCmdUpdate body;
} *cmd;
spin_lock_irqsave(&par->dirty.lock, flags);
if (!par->dirty.active) {
spin_unlock_irqrestore(&par->dirty.lock, flags);
return;
}
x = par->dirty.x1;
y = par->dirty.y1;
w = min(par->dirty.x2, info->var.xres) - x;
h = min(par->dirty.y2, info->var.yres) - y;
par->dirty.x1 = par->dirty.x2 = 0;
par->dirty.y1 = par->dirty.y2 = 0;
spin_unlock_irqrestore(&par->dirty.lock, flags);
for (i = y * stride; i < info->fix.smem_len / 4; i += stride) {
for (k = i+x; k < i+x+w && k < info->fix.smem_len / 4; k++)
iowrite32(src[k], vram_mem + k);
}
#if 0
DRM_INFO("%s, (%u, %u) (%ux%u)\n", __func__, x, y, w, h);
#endif
cmd = vmw_fifo_reserve(vmw_priv, sizeof(*cmd));
if (unlikely(cmd == NULL)) {
DRM_ERROR("Fifo reserve failed.\n");
return;
}
cmd->header = cpu_to_le32(SVGA_CMD_UPDATE);
cmd->body.x = cpu_to_le32(x);
cmd->body.y = cpu_to_le32(y);
cmd->body.width = cpu_to_le32(w);
cmd->body.height = cpu_to_le32(h);
vmw_fifo_commit(vmw_priv, sizeof(*cmd));
}
static void vmw_fb_dirty_mark(struct vmw_fb_par *par,
unsigned x1, unsigned y1,
unsigned width, unsigned height)
{
struct fb_info *info = par->vmw_priv->fb_info;
unsigned long flags;
unsigned x2 = x1 + width;
unsigned y2 = y1 + height;
spin_lock_irqsave(&par->dirty.lock, flags);
if (par->dirty.x1 == par->dirty.x2) {
par->dirty.x1 = x1;
par->dirty.y1 = y1;
par->dirty.x2 = x2;
par->dirty.y2 = y2;
/* if we are active start the dirty work
* we share the work with the defio system */
if (par->dirty.active)
schedule_delayed_work(&info->deferred_work, VMW_DIRTY_DELAY);
} else {
if (x1 < par->dirty.x1)
par->dirty.x1 = x1;
if (y1 < par->dirty.y1)
par->dirty.y1 = y1;
if (x2 > par->dirty.x2)
par->dirty.x2 = x2;
if (y2 > par->dirty.y2)
par->dirty.y2 = y2;
}
spin_unlock_irqrestore(&par->dirty.lock, flags);
}
static void vmw_deferred_io(struct fb_info *info,
struct list_head *pagelist)
{
struct vmw_fb_par *par = info->par;
unsigned long start, end, min, max;
unsigned long flags;
struct page *page;
int y1, y2;
min = ULONG_MAX;
max = 0;
list_for_each_entry(page, pagelist, lru) {
start = page->index << PAGE_SHIFT;
end = start + PAGE_SIZE - 1;
min = min(min, start);
max = max(max, end);
}
if (min < max) {
y1 = min / info->fix.line_length;
y2 = (max / info->fix.line_length) + 1;
spin_lock_irqsave(&par->dirty.lock, flags);
par->dirty.x1 = 0;
par->dirty.y1 = y1;
par->dirty.x2 = info->var.xres;
par->dirty.y2 = y2;
spin_unlock_irqrestore(&par->dirty.lock, flags);
}
vmw_fb_dirty_flush(par);
};
struct fb_deferred_io vmw_defio = {
.delay = VMW_DIRTY_DELAY,
.deferred_io = vmw_deferred_io,
};
/*
* Draw code
*/
static void vmw_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
{
cfb_fillrect(info, rect);
vmw_fb_dirty_mark(info->par, rect->dx, rect->dy,
rect->width, rect->height);
}
static void vmw_fb_copyarea(struct fb_info *info, const struct fb_copyarea *region)
{
cfb_copyarea(info, region);
vmw_fb_dirty_mark(info->par, region->dx, region->dy,
region->width, region->height);
}
static void vmw_fb_imageblit(struct fb_info *info, const struct fb_image *image)
{
cfb_imageblit(info, image);
vmw_fb_dirty_mark(info->par, image->dx, image->dy,
image->width, image->height);
}
/*
* Bring up code
*/
static struct fb_ops vmw_fb_ops = {
.owner = THIS_MODULE,
.fb_check_var = vmw_fb_check_var,
.fb_set_par = vmw_fb_set_par,
.fb_setcolreg = vmw_fb_setcolreg,
.fb_fillrect = vmw_fb_fillrect,
.fb_copyarea = vmw_fb_copyarea,
.fb_imageblit = vmw_fb_imageblit,
.fb_pan_display = vmw_fb_pan_display,
.fb_blank = vmw_fb_blank,
};
static int vmw_fb_create_bo(struct vmw_private *vmw_priv,
size_t size, struct vmw_dma_buffer **out)
{
struct vmw_dma_buffer *vmw_bo;
struct ttm_placement ne_placement = vmw_vram_ne_placement;
int ret;
ne_placement.lpfn = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
/* interuptable? */
ret = ttm_write_lock(&vmw_priv->fbdev_master.lock, false);
if (unlikely(ret != 0))
return ret;
vmw_bo = kmalloc(sizeof(*vmw_bo), GFP_KERNEL);
if (!vmw_bo)
goto err_unlock;
ret = vmw_dmabuf_init(vmw_priv, vmw_bo, size,
&ne_placement,
false,
&vmw_dmabuf_bo_free);
if (unlikely(ret != 0))
goto err_unlock; /* init frees the buffer on failure */
*out = vmw_bo;
ttm_write_unlock(&vmw_priv->fbdev_master.lock);
return 0;
err_unlock:
ttm_write_unlock(&vmw_priv->fbdev_master.lock);
return ret;
}
int vmw_fb_init(struct vmw_private *vmw_priv)
{
struct device *device = &vmw_priv->dev->pdev->dev;
struct vmw_fb_par *par;
struct fb_info *info;
unsigned initial_width, initial_height;
unsigned fb_width, fb_height;
unsigned fb_bbp, fb_depth, fb_offset, fb_pitch, fb_size;
int ret;
initial_width = 800;
initial_height = 600;
fb_bbp = 32;
fb_depth = 24;
if (vmw_priv->capabilities & SVGA_CAP_MULTIMON) {
fb_width = min(vmw_priv->fb_max_width, (unsigned)2048);
fb_height = min(vmw_priv->fb_max_height, (unsigned)2048);
} else {
fb_width = min(vmw_priv->fb_max_width, initial_width);
fb_height = min(vmw_priv->fb_max_height, initial_height);
}
initial_width = min(fb_width, initial_width);
initial_height = min(fb_height, initial_height);
vmw_write(vmw_priv, SVGA_REG_WIDTH, fb_width);
vmw_write(vmw_priv, SVGA_REG_HEIGHT, fb_height);
vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, fb_bbp);
vmw_write(vmw_priv, SVGA_REG_DEPTH, fb_depth);
vmw_write(vmw_priv, SVGA_REG_RED_MASK, 0x00ff0000);
vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, 0x0000ff00);
vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, 0x000000ff);
fb_size = vmw_read(vmw_priv, SVGA_REG_FB_SIZE);
fb_offset = vmw_read(vmw_priv, SVGA_REG_FB_OFFSET);
fb_pitch = vmw_read(vmw_priv, SVGA_REG_BYTES_PER_LINE);
DRM_DEBUG("width %u\n", vmw_read(vmw_priv, SVGA_REG_MAX_WIDTH));
DRM_DEBUG("height %u\n", vmw_read(vmw_priv, SVGA_REG_MAX_HEIGHT));
DRM_DEBUG("width %u\n", vmw_read(vmw_priv, SVGA_REG_WIDTH));
DRM_DEBUG("height %u\n", vmw_read(vmw_priv, SVGA_REG_HEIGHT));
DRM_DEBUG("bpp %u\n", vmw_read(vmw_priv, SVGA_REG_BITS_PER_PIXEL));
DRM_DEBUG("depth %u\n", vmw_read(vmw_priv, SVGA_REG_DEPTH));
DRM_DEBUG("bpl %u\n", vmw_read(vmw_priv, SVGA_REG_BYTES_PER_LINE));
DRM_DEBUG("r mask %08x\n", vmw_read(vmw_priv, SVGA_REG_RED_MASK));
DRM_DEBUG("g mask %08x\n", vmw_read(vmw_priv, SVGA_REG_GREEN_MASK));
DRM_DEBUG("b mask %08x\n", vmw_read(vmw_priv, SVGA_REG_BLUE_MASK));
DRM_DEBUG("fb_offset 0x%08x\n", fb_offset);
DRM_DEBUG("fb_pitch %u\n", fb_pitch);
DRM_DEBUG("fb_size %u kiB\n", fb_size / 1024);
info = framebuffer_alloc(sizeof(*par), device);
if (!info)
return -ENOMEM;
/*
* Par
*/
vmw_priv->fb_info = info;
par = info->par;
par->vmw_priv = vmw_priv;
par->depth = fb_depth;
par->bpp = fb_bbp;
par->vmalloc = NULL;
par->max_width = fb_width;
par->max_height = fb_height;
/*
* Create buffers and alloc memory
*/
par->vmalloc = vmalloc(fb_size);
if (unlikely(par->vmalloc == NULL)) {
ret = -ENOMEM;
goto err_free;
}
ret = vmw_fb_create_bo(vmw_priv, fb_size, &par->vmw_bo);
if (unlikely(ret != 0))
goto err_free;
ret = ttm_bo_kmap(&par->vmw_bo->base,
0,
par->vmw_bo->base.num_pages,
&par->map);
if (unlikely(ret != 0))
goto err_unref;
par->bo_ptr = ttm_kmap_obj_virtual(&par->map, &par->bo_iowrite);
par->bo_size = fb_size;
/*
* Fixed and var
*/
strcpy(info->fix.id, "svgadrmfb");
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = FB_VISUAL_TRUECOLOR;
info->fix.type_aux = 0;
info->fix.xpanstep = 1; /* doing it in hw */
info->fix.ypanstep = 1; /* doing it in hw */
info->fix.ywrapstep = 0;
info->fix.accel = FB_ACCEL_NONE;
info->fix.line_length = fb_pitch;
info->fix.smem_start = 0;
info->fix.smem_len = fb_size;
info->fix.mmio_start = 0;
info->fix.mmio_len = 0;
info->pseudo_palette = par->pseudo_palette;
info->screen_base = par->vmalloc;
info->screen_size = fb_size;
info->flags = FBINFO_DEFAULT;
info->fbops = &vmw_fb_ops;
/* 24 depth per default */
info->var.red.offset = 16;
info->var.green.offset = 8;
info->var.blue.offset = 0;
info->var.red.length = 8;
info->var.green.length = 8;
info->var.blue.length = 8;
info->var.transp.offset = 0;
info->var.transp.length = 0;
info->var.xres_virtual = fb_width;
info->var.yres_virtual = fb_height;
info->var.bits_per_pixel = par->bpp;
info->var.xoffset = 0;
info->var.yoffset = 0;
info->var.activate = FB_ACTIVATE_NOW;
info->var.height = -1;
info->var.width = -1;
info->var.xres = initial_width;
info->var.yres = initial_height;
#if 0
info->pixmap.size = 64*1024;
info->pixmap.buf_align = 8;
info->pixmap.access_align = 32;
info->pixmap.flags = FB_PIXMAP_SYSTEM;
info->pixmap.scan_align = 1;
#else
info->pixmap.size = 0;
info->pixmap.buf_align = 8;
info->pixmap.access_align = 32;
info->pixmap.flags = FB_PIXMAP_SYSTEM;
info->pixmap.scan_align = 1;
#endif
/*
* Dirty & Deferred IO
*/
par->dirty.x1 = par->dirty.x2 = 0;
par->dirty.y1 = par->dirty.y1 = 0;
par->dirty.active = true;
spin_lock_init(&par->dirty.lock);
info->fbdefio = &vmw_defio;
fb_deferred_io_init(info);
ret = register_framebuffer(info);
if (unlikely(ret != 0))
goto err_defio;
return 0;
err_defio:
fb_deferred_io_cleanup(info);
ttm_bo_kunmap(&par->map);
err_unref:
ttm_bo_unref((struct ttm_buffer_object **)&par->vmw_bo);
err_free:
vfree(par->vmalloc);
framebuffer_release(info);
vmw_priv->fb_info = NULL;
return ret;
}
int vmw_fb_close(struct vmw_private *vmw_priv)
{
struct fb_info *info;
struct vmw_fb_par *par;
struct ttm_buffer_object *bo;
if (!vmw_priv->fb_info)
return 0;
info = vmw_priv->fb_info;
par = info->par;
bo = &par->vmw_bo->base;
par->vmw_bo = NULL;
/* ??? order */
fb_deferred_io_cleanup(info);
unregister_framebuffer(info);
ttm_bo_kunmap(&par->map);
ttm_bo_unref(&bo);
vfree(par->vmalloc);
framebuffer_release(info);
return 0;
}
int vmw_dmabuf_from_vram(struct vmw_private *vmw_priv,
struct vmw_dma_buffer *vmw_bo)
{
struct ttm_buffer_object *bo = &vmw_bo->base;
int ret = 0;
ret = ttm_bo_reserve(bo, false, false, false, 0);
if (unlikely(ret != 0))
return ret;
ret = ttm_bo_validate(bo, &vmw_sys_placement, false, false);
ttm_bo_unreserve(bo);
return ret;
}
int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv,
struct vmw_dma_buffer *vmw_bo)
{
struct ttm_buffer_object *bo = &vmw_bo->base;
struct ttm_placement ne_placement = vmw_vram_ne_placement;
int ret = 0;
ne_placement.lpfn = bo->num_pages;
/* interuptable? */
ret = ttm_write_lock(&vmw_priv->active_master->lock, false);
if (unlikely(ret != 0))
return ret;
ret = ttm_bo_reserve(bo, false, false, false, 0);
if (unlikely(ret != 0))
goto err_unlock;
if (vmw_bo->gmr_bound) {
vmw_gmr_unbind(vmw_priv, vmw_bo->gmr_id);
spin_lock(&bo->glob->lru_lock);
ida_remove(&vmw_priv->gmr_ida, vmw_bo->gmr_id);
spin_unlock(&bo->glob->lru_lock);
vmw_bo->gmr_bound = NULL;
}
ret = ttm_bo_validate(bo, &ne_placement, false, false);
ttm_bo_unreserve(bo);
err_unlock:
ttm_write_unlock(&vmw_priv->active_master->lock);
return ret;
}
int vmw_fb_off(struct vmw_private *vmw_priv)
{
struct fb_info *info;
struct vmw_fb_par *par;
unsigned long flags;
if (!vmw_priv->fb_info)
return -EINVAL;
info = vmw_priv->fb_info;
par = info->par;
spin_lock_irqsave(&par->dirty.lock, flags);
par->dirty.active = false;
spin_unlock_irqrestore(&par->dirty.lock, flags);
flush_scheduled_work();
par->bo_ptr = NULL;
ttm_bo_kunmap(&par->map);
vmw_dmabuf_from_vram(vmw_priv, par->vmw_bo);
return 0;
}
int vmw_fb_on(struct vmw_private *vmw_priv)
{
struct fb_info *info;
struct vmw_fb_par *par;
unsigned long flags;
bool dummy;
int ret;
if (!vmw_priv->fb_info)
return -EINVAL;
info = vmw_priv->fb_info;
par = info->par;
/* we are already active */
if (par->bo_ptr != NULL)
return 0;
/* Make sure that all overlays are stoped when we take over */
vmw_overlay_stop_all(vmw_priv);
ret = vmw_dmabuf_to_start_of_vram(vmw_priv, par->vmw_bo);
if (unlikely(ret != 0)) {
DRM_ERROR("could not move buffer to start of VRAM\n");
goto err_no_buffer;
}
ret = ttm_bo_kmap(&par->vmw_bo->base,
0,
par->vmw_bo->base.num_pages,
&par->map);
BUG_ON(ret != 0);
par->bo_ptr = ttm_kmap_obj_virtual(&par->map, &dummy);
spin_lock_irqsave(&par->dirty.lock, flags);
par->dirty.active = true;
spin_unlock_irqrestore(&par->dirty.lock, flags);
err_no_buffer:
vmw_fb_set_par(info);
vmw_fb_dirty_mark(par, 0, 0, info->var.xres, info->var.yres);
/* If there already was stuff dirty we wont
* schedule a new work, so lets do it now */
schedule_delayed_work(&info->deferred_work, 0);
return 0;
}
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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_drv.h"
#include "drmP.h"
#include "ttm/ttm_placement.h"
int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)
{
__le32 __iomem *fifo_mem = dev_priv->mmio_virt;
uint32_t max;
uint32_t min;
uint32_t dummy;
int ret;
fifo->static_buffer_size = VMWGFX_FIFO_STATIC_SIZE;
fifo->static_buffer = vmalloc(fifo->static_buffer_size);
if (unlikely(fifo->static_buffer == NULL))
return -ENOMEM;
fifo->last_buffer_size = VMWGFX_FIFO_STATIC_SIZE;
fifo->last_data_size = 0;
fifo->last_buffer_add = false;
fifo->last_buffer = vmalloc(fifo->last_buffer_size);
if (unlikely(fifo->last_buffer == NULL)) {
ret = -ENOMEM;
goto out_err;
}
fifo->dynamic_buffer = NULL;
fifo->reserved_size = 0;
fifo->using_bounce_buffer = false;
init_rwsem(&fifo->rwsem);
/*
* Allow mapping the first page read-only to user-space.
*/
DRM_INFO("width %d\n", vmw_read(dev_priv, SVGA_REG_WIDTH));
DRM_INFO("height %d\n", vmw_read(dev_priv, SVGA_REG_HEIGHT));
DRM_INFO("bpp %d\n", vmw_read(dev_priv, SVGA_REG_BITS_PER_PIXEL));
mutex_lock(&dev_priv->hw_mutex);
dev_priv->enable_state = vmw_read(dev_priv, SVGA_REG_ENABLE);
dev_priv->config_done_state = vmw_read(dev_priv, SVGA_REG_CONFIG_DONE);
vmw_write(dev_priv, SVGA_REG_ENABLE, 1);
min = 4;
if (dev_priv->capabilities & SVGA_CAP_EXTENDED_FIFO)
min = vmw_read(dev_priv, SVGA_REG_MEM_REGS);
min <<= 2;
if (min < PAGE_SIZE)
min = PAGE_SIZE;
iowrite32(min, fifo_mem + SVGA_FIFO_MIN);
iowrite32(dev_priv->mmio_size, fifo_mem + SVGA_FIFO_MAX);
wmb();
iowrite32(min, fifo_mem + SVGA_FIFO_NEXT_CMD);
iowrite32(min, fifo_mem + SVGA_FIFO_STOP);
iowrite32(0, fifo_mem + SVGA_FIFO_BUSY);
mb();
vmw_write(dev_priv, SVGA_REG_CONFIG_DONE, 1);
mutex_unlock(&dev_priv->hw_mutex);
max = ioread32(fifo_mem + SVGA_FIFO_MAX);
min = ioread32(fifo_mem + SVGA_FIFO_MIN);
fifo->capabilities = ioread32(fifo_mem + SVGA_FIFO_CAPABILITIES);
DRM_INFO("Fifo max 0x%08x min 0x%08x cap 0x%08x\n",
(unsigned int) max,
(unsigned int) min,
(unsigned int) fifo->capabilities);
dev_priv->fence_seq = (uint32_t) -100;
dev_priv->last_read_sequence = (uint32_t) -100;
iowrite32(dev_priv->last_read_sequence, fifo_mem + SVGA_FIFO_FENCE);
return vmw_fifo_send_fence(dev_priv, &dummy);
out_err:
vfree(fifo->static_buffer);
fifo->static_buffer = NULL;
return ret;
}
void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason)
{
__le32 __iomem *fifo_mem = dev_priv->mmio_virt;
mutex_lock(&dev_priv->hw_mutex);
if (unlikely(ioread32(fifo_mem + SVGA_FIFO_BUSY) == 0)) {
iowrite32(1, fifo_mem + SVGA_FIFO_BUSY);
vmw_write(dev_priv, SVGA_REG_SYNC, reason);
}
mutex_unlock(&dev_priv->hw_mutex);
}
void vmw_fifo_release(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)
{
__le32 __iomem *fifo_mem = dev_priv->mmio_virt;
mutex_lock(&dev_priv->hw_mutex);
while (vmw_read(dev_priv, SVGA_REG_BUSY) != 0)
vmw_write(dev_priv, SVGA_REG_SYNC, SVGA_SYNC_GENERIC);
dev_priv->last_read_sequence = ioread32(fifo_mem + SVGA_FIFO_FENCE);
vmw_write(dev_priv, SVGA_REG_CONFIG_DONE,
dev_priv->config_done_state);
vmw_write(dev_priv, SVGA_REG_ENABLE,
dev_priv->enable_state);
mutex_unlock(&dev_priv->hw_mutex);
if (likely(fifo->last_buffer != NULL)) {
vfree(fifo->last_buffer);
fifo->last_buffer = NULL;
}
if (likely(fifo->static_buffer != NULL)) {
vfree(fifo->static_buffer);
fifo->static_buffer = NULL;
}
if (likely(fifo->dynamic_buffer != NULL)) {
vfree(fifo->dynamic_buffer);
fifo->dynamic_buffer = NULL;
}
}
static bool vmw_fifo_is_full(struct vmw_private *dev_priv, uint32_t bytes)
{
__le32 __iomem *fifo_mem = dev_priv->mmio_virt;
uint32_t max = ioread32(fifo_mem + SVGA_FIFO_MAX);
uint32_t next_cmd = ioread32(fifo_mem + SVGA_FIFO_NEXT_CMD);
uint32_t min = ioread32(fifo_mem + SVGA_FIFO_MIN);
uint32_t stop = ioread32(fifo_mem + SVGA_FIFO_STOP);
return ((max - next_cmd) + (stop - min) <= bytes);
}
static int vmw_fifo_wait_noirq(struct vmw_private *dev_priv,
uint32_t bytes, bool interruptible,
unsigned long timeout)
{
int ret = 0;
unsigned long end_jiffies = jiffies + timeout;
DEFINE_WAIT(__wait);
DRM_INFO("Fifo wait noirq.\n");
for (;;) {
prepare_to_wait(&dev_priv->fifo_queue, &__wait,
(interruptible) ?
TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
if (!vmw_fifo_is_full(dev_priv, bytes))
break;
if (time_after_eq(jiffies, end_jiffies)) {
ret = -EBUSY;
DRM_ERROR("SVGA device lockup.\n");
break;
}
schedule_timeout(1);
if (interruptible && signal_pending(current)) {
ret = -ERESTART;
break;
}
}
finish_wait(&dev_priv->fifo_queue, &__wait);
wake_up_all(&dev_priv->fifo_queue);
DRM_INFO("Fifo noirq exit.\n");
return ret;
}
static int vmw_fifo_wait(struct vmw_private *dev_priv,
uint32_t bytes, bool interruptible,
unsigned long timeout)
{
long ret = 1L;
unsigned long irq_flags;
if (likely(!vmw_fifo_is_full(dev_priv, bytes)))
return 0;
vmw_fifo_ping_host(dev_priv, SVGA_SYNC_FIFOFULL);
if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
return vmw_fifo_wait_noirq(dev_priv, bytes,
interruptible, timeout);
mutex_lock(&dev_priv->hw_mutex);
if (atomic_add_return(1, &dev_priv->fifo_queue_waiters) > 0) {
spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
outl(SVGA_IRQFLAG_FIFO_PROGRESS,
dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
vmw_write(dev_priv, SVGA_REG_IRQMASK,
vmw_read(dev_priv, SVGA_REG_IRQMASK) |
SVGA_IRQFLAG_FIFO_PROGRESS);
spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
}
mutex_unlock(&dev_priv->hw_mutex);
if (interruptible)
ret = wait_event_interruptible_timeout
(dev_priv->fifo_queue,
!vmw_fifo_is_full(dev_priv, bytes), timeout);
else
ret = wait_event_timeout
(dev_priv->fifo_queue,
!vmw_fifo_is_full(dev_priv, bytes), timeout);
if (unlikely(ret == -ERESTARTSYS))
ret = -ERESTART;
else if (unlikely(ret == 0))
ret = -EBUSY;
else if (likely(ret > 0))
ret = 0;
mutex_lock(&dev_priv->hw_mutex);
if (atomic_dec_and_test(&dev_priv->fifo_queue_waiters)) {
spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
vmw_write(dev_priv, SVGA_REG_IRQMASK,
vmw_read(dev_priv, SVGA_REG_IRQMASK) &
~SVGA_IRQFLAG_FIFO_PROGRESS);
spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
}
mutex_unlock(&dev_priv->hw_mutex);
return ret;
}
void *vmw_fifo_reserve(struct vmw_private *dev_priv, uint32_t bytes)
{
struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
__le32 __iomem *fifo_mem = dev_priv->mmio_virt;
uint32_t max;
uint32_t min;
uint32_t next_cmd;
uint32_t reserveable = fifo_state->capabilities & SVGA_FIFO_CAP_RESERVE;
int ret;
down_write(&fifo_state->rwsem);
max = ioread32(fifo_mem + SVGA_FIFO_MAX);
min = ioread32(fifo_mem + SVGA_FIFO_MIN);
next_cmd = ioread32(fifo_mem + SVGA_FIFO_NEXT_CMD);
if (unlikely(bytes >= (max - min)))
goto out_err;
BUG_ON(fifo_state->reserved_size != 0);
BUG_ON(fifo_state->dynamic_buffer != NULL);
fifo_state->reserved_size = bytes;
while (1) {
uint32_t stop = ioread32(fifo_mem + SVGA_FIFO_STOP);
bool need_bounce = false;
bool reserve_in_place = false;
if (next_cmd >= stop) {
if (likely((next_cmd + bytes < max ||
(next_cmd + bytes == max && stop > min))))
reserve_in_place = true;
else if (vmw_fifo_is_full(dev_priv, bytes)) {
ret = vmw_fifo_wait(dev_priv, bytes,
false, 3 * HZ);
if (unlikely(ret != 0))
goto out_err;
} else
need_bounce = true;
} else {
if (likely((next_cmd + bytes < stop)))
reserve_in_place = true;
else {
ret = vmw_fifo_wait(dev_priv, bytes,
false, 3 * HZ);
if (unlikely(ret != 0))
goto out_err;
}
}
if (reserve_in_place) {
if (reserveable || bytes <= sizeof(uint32_t)) {
fifo_state->using_bounce_buffer = false;
if (reserveable)
iowrite32(bytes, fifo_mem +
SVGA_FIFO_RESERVED);
return fifo_mem + (next_cmd >> 2);
} else {
need_bounce = true;
}
}
if (need_bounce) {
fifo_state->using_bounce_buffer = true;
if (bytes < fifo_state->static_buffer_size)
return fifo_state->static_buffer;
else {
fifo_state->dynamic_buffer = vmalloc(bytes);
return fifo_state->dynamic_buffer;
}
}
}
out_err:
fifo_state->reserved_size = 0;
up_write(&fifo_state->rwsem);
return NULL;
}
static void vmw_fifo_res_copy(struct vmw_fifo_state *fifo_state,
__le32 __iomem *fifo_mem,
uint32_t next_cmd,
uint32_t max, uint32_t min, uint32_t bytes)
{
uint32_t chunk_size = max - next_cmd;
uint32_t rest;
uint32_t *buffer = (fifo_state->dynamic_buffer != NULL) ?
fifo_state->dynamic_buffer : fifo_state->static_buffer;
if (bytes < chunk_size)
chunk_size = bytes;
iowrite32(bytes, fifo_mem + SVGA_FIFO_RESERVED);
mb();
memcpy_toio(fifo_mem + (next_cmd >> 2), buffer, chunk_size);
rest = bytes - chunk_size;
if (rest)
memcpy_toio(fifo_mem + (min >> 2), buffer + (chunk_size >> 2),
rest);
}
static void vmw_fifo_slow_copy(struct vmw_fifo_state *fifo_state,
__le32 __iomem *fifo_mem,
uint32_t next_cmd,
uint32_t max, uint32_t min, uint32_t bytes)
{
uint32_t *buffer = (fifo_state->dynamic_buffer != NULL) ?
fifo_state->dynamic_buffer : fifo_state->static_buffer;
while (bytes > 0) {
iowrite32(*buffer++, fifo_mem + (next_cmd >> 2));
next_cmd += sizeof(uint32_t);
if (unlikely(next_cmd == max))
next_cmd = min;
mb();
iowrite32(next_cmd, fifo_mem + SVGA_FIFO_NEXT_CMD);
mb();
bytes -= sizeof(uint32_t);
}
}
void vmw_fifo_commit(struct vmw_private *dev_priv, uint32_t bytes)
{
struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
__le32 __iomem *fifo_mem = dev_priv->mmio_virt;
uint32_t next_cmd = ioread32(fifo_mem + SVGA_FIFO_NEXT_CMD);
uint32_t max = ioread32(fifo_mem + SVGA_FIFO_MAX);
uint32_t min = ioread32(fifo_mem + SVGA_FIFO_MIN);
bool reserveable = fifo_state->capabilities & SVGA_FIFO_CAP_RESERVE;
BUG_ON((bytes & 3) != 0);
BUG_ON(bytes > fifo_state->reserved_size);
fifo_state->reserved_size = 0;
if (fifo_state->using_bounce_buffer) {
if (reserveable)
vmw_fifo_res_copy(fifo_state, fifo_mem,
next_cmd, max, min, bytes);
else
vmw_fifo_slow_copy(fifo_state, fifo_mem,
next_cmd, max, min, bytes);
if (fifo_state->dynamic_buffer) {
vfree(fifo_state->dynamic_buffer);
fifo_state->dynamic_buffer = NULL;
}
}
if (fifo_state->using_bounce_buffer || reserveable) {
next_cmd += bytes;
if (next_cmd >= max)
next_cmd -= max - min;
mb();
iowrite32(next_cmd, fifo_mem + SVGA_FIFO_NEXT_CMD);
}
if (reserveable)
iowrite32(0, fifo_mem + SVGA_FIFO_RESERVED);
mb();
vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
up_write(&fifo_state->rwsem);
}
int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *sequence)
{
struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
struct svga_fifo_cmd_fence *cmd_fence;
void *fm;
int ret = 0;
uint32_t bytes = sizeof(__le32) + sizeof(*cmd_fence);
fm = vmw_fifo_reserve(dev_priv, bytes);
if (unlikely(fm == NULL)) {
down_write(&fifo_state->rwsem);
*sequence = dev_priv->fence_seq;
up_write(&fifo_state->rwsem);
ret = -ENOMEM;
(void)vmw_fallback_wait(dev_priv, false, true, *sequence,
false, 3*HZ);
goto out_err;
}
do {
*sequence = dev_priv->fence_seq++;
} while (*sequence == 0);
if (!(fifo_state->capabilities & SVGA_FIFO_CAP_FENCE)) {
/*
* Don't request hardware to send a fence. The
* waiting code in vmwgfx_irq.c will emulate this.
*/
vmw_fifo_commit(dev_priv, 0);
return 0;
}
*(__le32 *) fm = cpu_to_le32(SVGA_CMD_FENCE);
cmd_fence = (struct svga_fifo_cmd_fence *)
((unsigned long)fm + sizeof(__le32));
iowrite32(*sequence, &cmd_fence->fence);
fifo_state->last_buffer_add = true;
vmw_fifo_commit(dev_priv, bytes);
fifo_state->last_buffer_add = false;
out_err:
return ret;
}
/**
* Map the first page of the FIFO read-only to user-space.
*/
static int vmw_fifo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
int ret;
unsigned long address = (unsigned long)vmf->virtual_address;
if (address != vma->vm_start)
return VM_FAULT_SIGBUS;
ret = vm_insert_pfn(vma, address, vma->vm_pgoff);
if (likely(ret == -EBUSY || ret == 0))
return VM_FAULT_NOPAGE;
else if (ret == -ENOMEM)
return VM_FAULT_OOM;
return VM_FAULT_SIGBUS;
}
static struct vm_operations_struct vmw_fifo_vm_ops = {
.fault = vmw_fifo_vm_fault,
.open = NULL,
.close = NULL
};
int vmw_fifo_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct drm_file *file_priv;
struct vmw_private *dev_priv;
file_priv = (struct drm_file *)filp->private_data;
dev_priv = vmw_priv(file_priv->minor->dev);
if (vma->vm_pgoff != (dev_priv->mmio_start >> PAGE_SHIFT) ||
(vma->vm_end - vma->vm_start) != PAGE_SIZE)
return -EINVAL;
vma->vm_flags &= ~(VM_WRITE | VM_MAYWRITE);
vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_SHARED;
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
vma->vm_page_prot = ttm_io_prot(TTM_PL_FLAG_UNCACHED,
vma->vm_page_prot);
vma->vm_ops = &vmw_fifo_vm_ops;
return 0;
}
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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_drv.h"
#include "drmP.h"
#include "ttm/ttm_bo_driver.h"
/**
* FIXME: Adjust to the ttm lowmem / highmem storage to minimize
* the number of used descriptors.
*/
static int vmw_gmr_build_descriptors(struct list_head *desc_pages,
struct page *pages[],
unsigned long num_pages)
{
struct page *page, *next;
struct svga_guest_mem_descriptor *page_virtual = NULL;
struct svga_guest_mem_descriptor *desc_virtual = NULL;
unsigned int desc_per_page;
unsigned long prev_pfn;
unsigned long pfn;
int ret;
desc_per_page = PAGE_SIZE /
sizeof(struct svga_guest_mem_descriptor) - 1;
while (likely(num_pages != 0)) {
page = alloc_page(__GFP_HIGHMEM);
if (unlikely(page == NULL)) {
ret = -ENOMEM;
goto out_err;
}
list_add_tail(&page->lru, desc_pages);
/*
* Point previous page terminating descriptor to this
* page before unmapping it.
*/
if (likely(page_virtual != NULL)) {
desc_virtual->ppn = page_to_pfn(page);
kunmap_atomic(page_virtual, KM_USER0);
}
page_virtual = kmap_atomic(page, KM_USER0);
desc_virtual = page_virtual - 1;
prev_pfn = ~(0UL);
while (likely(num_pages != 0)) {
pfn = page_to_pfn(*pages);
if (pfn != prev_pfn + 1) {
if (desc_virtual - page_virtual ==
desc_per_page - 1)
break;
(++desc_virtual)->ppn = cpu_to_le32(pfn);
desc_virtual->num_pages = cpu_to_le32(1);
} else {
uint32_t tmp =
le32_to_cpu(desc_virtual->num_pages);
desc_virtual->num_pages = cpu_to_le32(tmp + 1);
}
prev_pfn = pfn;
--num_pages;
++pages;
}
(++desc_virtual)->ppn = cpu_to_le32(0);
desc_virtual->num_pages = cpu_to_le32(0);
}
if (likely(page_virtual != NULL))
kunmap_atomic(page_virtual, KM_USER0);
return 0;
out_err:
list_for_each_entry_safe(page, next, desc_pages, lru) {
list_del_init(&page->lru);
__free_page(page);
}
return ret;
}
static inline void vmw_gmr_free_descriptors(struct list_head *desc_pages)
{
struct page *page, *next;
list_for_each_entry_safe(page, next, desc_pages, lru) {
list_del_init(&page->lru);
__free_page(page);
}
}
static void vmw_gmr_fire_descriptors(struct vmw_private *dev_priv,
int gmr_id, struct list_head *desc_pages)
{
struct page *page;
if (unlikely(list_empty(desc_pages)))
return;
page = list_entry(desc_pages->next, struct page, lru);
mutex_lock(&dev_priv->hw_mutex);
vmw_write(dev_priv, SVGA_REG_GMR_ID, gmr_id);
wmb();
vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, page_to_pfn(page));
mb();
mutex_unlock(&dev_priv->hw_mutex);
}
/**
* FIXME: Adjust to the ttm lowmem / highmem storage to minimize
* the number of used descriptors.
*/
static unsigned long vmw_gmr_count_descriptors(struct page *pages[],
unsigned long num_pages)
{
unsigned long prev_pfn = ~(0UL);
unsigned long pfn;
unsigned long descriptors = 0;
while (num_pages--) {
pfn = page_to_pfn(*pages++);
if (prev_pfn + 1 != pfn)
++descriptors;
prev_pfn = pfn;
}
return descriptors;
}
int vmw_gmr_bind(struct vmw_private *dev_priv,
struct ttm_buffer_object *bo)
{
struct ttm_tt *ttm = bo->ttm;
unsigned long descriptors;
int ret;
uint32_t id;
struct list_head desc_pages;
if (!(dev_priv->capabilities & SVGA_CAP_GMR))
return -EINVAL;
ret = ttm_tt_populate(ttm);
if (unlikely(ret != 0))
return ret;
descriptors = vmw_gmr_count_descriptors(ttm->pages, ttm->num_pages);
if (unlikely(descriptors > dev_priv->max_gmr_descriptors))
return -EINVAL;
INIT_LIST_HEAD(&desc_pages);
ret = vmw_gmr_build_descriptors(&desc_pages, ttm->pages,
ttm->num_pages);
if (unlikely(ret != 0))
return ret;
ret = vmw_gmr_id_alloc(dev_priv, &id);
if (unlikely(ret != 0))
goto out_no_id;
vmw_gmr_fire_descriptors(dev_priv, id, &desc_pages);
vmw_gmr_free_descriptors(&desc_pages);
vmw_dmabuf_set_gmr(bo, id);
return 0;
out_no_id:
vmw_gmr_free_descriptors(&desc_pages);
return ret;
}
void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id)
{
mutex_lock(&dev_priv->hw_mutex);
vmw_write(dev_priv, SVGA_REG_GMR_ID, gmr_id);
wmb();
vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, 0);
mb();
mutex_unlock(&dev_priv->hw_mutex);
}
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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_drv.h"
#include "vmwgfx_drm.h"
int vmw_getparam_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct drm_vmw_getparam_arg *param =
(struct drm_vmw_getparam_arg *)data;
switch (param->param) {
case DRM_VMW_PARAM_NUM_STREAMS:
param->value = vmw_overlay_num_overlays(dev_priv);
break;
case DRM_VMW_PARAM_NUM_FREE_STREAMS:
param->value = vmw_overlay_num_free_overlays(dev_priv);
break;
case DRM_VMW_PARAM_3D:
param->value = dev_priv->capabilities & SVGA_CAP_3D ? 1 : 0;
break;
case DRM_VMW_PARAM_FIFO_OFFSET:
param->value = dev_priv->mmio_start;
break;
default:
DRM_ERROR("Illegal vmwgfx get param request: %d\n",
param->param);
return -EINVAL;
}
return 0;
}
int vmw_fifo_debug_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
struct drm_vmw_fifo_debug_arg *arg =
(struct drm_vmw_fifo_debug_arg *)data;
__le32 __user *buffer = (__le32 __user *)
(unsigned long)arg->debug_buffer;
if (unlikely(fifo_state->last_buffer == NULL))
return -EINVAL;
if (arg->debug_buffer_size < fifo_state->last_data_size) {
arg->used_size = arg->debug_buffer_size;
arg->did_not_fit = 1;
} else {
arg->used_size = fifo_state->last_data_size;
arg->did_not_fit = 0;
}
return copy_to_user(buffer, fifo_state->last_buffer, arg->used_size);
}
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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 "drmP.h"
#include "vmwgfx_drv.h"
#define VMW_FENCE_WRAP (1 << 24)
irqreturn_t vmw_irq_handler(DRM_IRQ_ARGS)
{
struct drm_device *dev = (struct drm_device *)arg;
struct vmw_private *dev_priv = vmw_priv(dev);
uint32_t status;
spin_lock(&dev_priv->irq_lock);
status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
spin_unlock(&dev_priv->irq_lock);
if (status & SVGA_IRQFLAG_ANY_FENCE)
wake_up_all(&dev_priv->fence_queue);
if (status & SVGA_IRQFLAG_FIFO_PROGRESS)
wake_up_all(&dev_priv->fifo_queue);
if (likely(status)) {
outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t sequence)
{
uint32_t busy;
mutex_lock(&dev_priv->hw_mutex);
busy = vmw_read(dev_priv, SVGA_REG_BUSY);
mutex_unlock(&dev_priv->hw_mutex);
return (busy == 0);
}
bool vmw_fence_signaled(struct vmw_private *dev_priv,
uint32_t sequence)
{
__le32 __iomem *fifo_mem = dev_priv->mmio_virt;
struct vmw_fifo_state *fifo_state;
bool ret;
if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP))
return true;
dev_priv->last_read_sequence = ioread32(fifo_mem + SVGA_FIFO_FENCE);
if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP))
return true;
fifo_state = &dev_priv->fifo;
if (!(fifo_state->capabilities & SVGA_FIFO_CAP_FENCE) &&
vmw_fifo_idle(dev_priv, sequence))
return true;
/**
* Below is to signal stale fences that have wrapped.
* First, block fence submission.
*/
down_read(&fifo_state->rwsem);
/**
* Then check if the sequence is higher than what we've actually
* emitted. Then the fence is stale and signaled.
*/
ret = ((dev_priv->fence_seq - sequence) > VMW_FENCE_WRAP);
up_read(&fifo_state->rwsem);
return ret;
}
int vmw_fallback_wait(struct vmw_private *dev_priv,
bool lazy,
bool fifo_idle,
uint32_t sequence,
bool interruptible,
unsigned long timeout)
{
struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
uint32_t count = 0;
uint32_t signal_seq;
int ret;
unsigned long end_jiffies = jiffies + timeout;
bool (*wait_condition)(struct vmw_private *, uint32_t);
DEFINE_WAIT(__wait);
wait_condition = (fifo_idle) ? &vmw_fifo_idle :
&vmw_fence_signaled;
/**
* Block command submission while waiting for idle.
*/
if (fifo_idle)
down_read(&fifo_state->rwsem);
signal_seq = dev_priv->fence_seq;
ret = 0;
for (;;) {
prepare_to_wait(&dev_priv->fence_queue, &__wait,
(interruptible) ?
TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
if (wait_condition(dev_priv, sequence))
break;
if (time_after_eq(jiffies, end_jiffies)) {
DRM_ERROR("SVGA device lockup.\n");
break;
}
if (lazy)
schedule_timeout(1);
else if ((++count & 0x0F) == 0) {
/**
* FIXME: Use schedule_hr_timeout here for
* newer kernels and lower CPU utilization.
*/
__set_current_state(TASK_RUNNING);
schedule();
__set_current_state((interruptible) ?
TASK_INTERRUPTIBLE :
TASK_UNINTERRUPTIBLE);
}
if (interruptible && signal_pending(current)) {
ret = -ERESTART;
break;
}
}
finish_wait(&dev_priv->fence_queue, &__wait);
if (ret == 0 && fifo_idle) {
__le32 __iomem *fifo_mem = dev_priv->mmio_virt;
iowrite32(signal_seq, fifo_mem + SVGA_FIFO_FENCE);
}
wake_up_all(&dev_priv->fence_queue);
if (fifo_idle)
up_read(&fifo_state->rwsem);
return ret;
}
int vmw_wait_fence(struct vmw_private *dev_priv,
bool lazy, uint32_t sequence,
bool interruptible, unsigned long timeout)
{
long ret;
unsigned long irq_flags;
struct vmw_fifo_state *fifo = &dev_priv->fifo;
if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP))
return 0;
if (likely(vmw_fence_signaled(dev_priv, sequence)))
return 0;
vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
if (!(fifo->capabilities & SVGA_FIFO_CAP_FENCE))
return vmw_fallback_wait(dev_priv, lazy, true, sequence,
interruptible, timeout);
if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
return vmw_fallback_wait(dev_priv, lazy, false, sequence,
interruptible, timeout);
mutex_lock(&dev_priv->hw_mutex);
if (atomic_add_return(1, &dev_priv->fence_queue_waiters) > 0) {
spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
outl(SVGA_IRQFLAG_ANY_FENCE,
dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
vmw_write(dev_priv, SVGA_REG_IRQMASK,
vmw_read(dev_priv, SVGA_REG_IRQMASK) |
SVGA_IRQFLAG_ANY_FENCE);
spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
}
mutex_unlock(&dev_priv->hw_mutex);
if (interruptible)
ret = wait_event_interruptible_timeout
(dev_priv->fence_queue,
vmw_fence_signaled(dev_priv, sequence),
timeout);
else
ret = wait_event_timeout
(dev_priv->fence_queue,
vmw_fence_signaled(dev_priv, sequence),
timeout);
if (unlikely(ret == -ERESTARTSYS))
ret = -ERESTART;
else if (unlikely(ret == 0))
ret = -EBUSY;
else if (likely(ret > 0))
ret = 0;
mutex_lock(&dev_priv->hw_mutex);
if (atomic_dec_and_test(&dev_priv->fence_queue_waiters)) {
spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
vmw_write(dev_priv, SVGA_REG_IRQMASK,
vmw_read(dev_priv, SVGA_REG_IRQMASK) &
~SVGA_IRQFLAG_ANY_FENCE);
spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
}
mutex_unlock(&dev_priv->hw_mutex);
return ret;
}
void vmw_irq_preinstall(struct drm_device *dev)
{
struct vmw_private *dev_priv = vmw_priv(dev);
uint32_t status;
if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
return;
spin_lock_init(&dev_priv->irq_lock);
status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
}
int vmw_irq_postinstall(struct drm_device *dev)
{
return 0;
}
void vmw_irq_uninstall(struct drm_device *dev)
{
struct vmw_private *dev_priv = vmw_priv(dev);
uint32_t status;
if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
return;
mutex_lock(&dev_priv->hw_mutex);
vmw_write(dev_priv, SVGA_REG_IRQMASK, 0);
mutex_unlock(&dev_priv->hw_mutex);
status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
}
#define VMW_FENCE_WAIT_TIMEOUT 3*HZ;
int vmw_fence_wait_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_vmw_fence_wait_arg *arg =
(struct drm_vmw_fence_wait_arg *)data;
unsigned long timeout;
if (!arg->cookie_valid) {
arg->cookie_valid = 1;
arg->kernel_cookie = jiffies + VMW_FENCE_WAIT_TIMEOUT;
}
timeout = jiffies;
if (time_after_eq(timeout, (unsigned long)arg->kernel_cookie))
return -EBUSY;
timeout = (unsigned long)arg->kernel_cookie - timeout;
return vmw_wait_fence(vmw_priv(dev), true, arg->sequence, true, timeout);
}
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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_kms.h"
/* Might need a hrtimer here? */
#define VMWGFX_PRESENT_RATE ((HZ / 60 > 0) ? HZ / 60 : 1)
void vmw_display_unit_cleanup(struct vmw_display_unit *du)
{
if (du->cursor_surface)
vmw_surface_unreference(&du->cursor_surface);
if (du->cursor_dmabuf)
vmw_dmabuf_unreference(&du->cursor_dmabuf);
drm_crtc_cleanup(&du->crtc);
drm_encoder_cleanup(&du->encoder);
drm_connector_cleanup(&du->connector);
}
/*
* Display Unit Cursor functions
*/
int vmw_cursor_update_image(struct vmw_private *dev_priv,
u32 *image, u32 width, u32 height,
u32 hotspotX, u32 hotspotY)
{
struct {
u32 cmd;
SVGAFifoCmdDefineAlphaCursor cursor;
} *cmd;
u32 image_size = width * height * 4;
u32 cmd_size = sizeof(*cmd) + image_size;
if (!image)
return -EINVAL;
cmd = vmw_fifo_reserve(dev_priv, cmd_size);
if (unlikely(cmd == NULL)) {
DRM_ERROR("Fifo reserve failed.\n");
return -ENOMEM;
}
memset(cmd, 0, sizeof(*cmd));
memcpy(&cmd[1], image, image_size);
cmd->cmd = cpu_to_le32(SVGA_CMD_DEFINE_ALPHA_CURSOR);
cmd->cursor.id = cpu_to_le32(0);
cmd->cursor.width = cpu_to_le32(width);
cmd->cursor.height = cpu_to_le32(height);
cmd->cursor.hotspotX = cpu_to_le32(hotspotX);
cmd->cursor.hotspotY = cpu_to_le32(hotspotY);
vmw_fifo_commit(dev_priv, cmd_size);
return 0;
}
void vmw_cursor_update_position(struct vmw_private *dev_priv,
bool show, int x, int y)
{
__le32 __iomem *fifo_mem = dev_priv->mmio_virt;
uint32_t count;
iowrite32(show ? 1 : 0, fifo_mem + SVGA_FIFO_CURSOR_ON);
iowrite32(x, fifo_mem + SVGA_FIFO_CURSOR_X);
iowrite32(y, fifo_mem + SVGA_FIFO_CURSOR_Y);
count = ioread32(fifo_mem + SVGA_FIFO_CURSOR_COUNT);
iowrite32(++count, fifo_mem + SVGA_FIFO_CURSOR_COUNT);
}
int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
uint32_t handle, uint32_t width, uint32_t height)
{
struct vmw_private *dev_priv = vmw_priv(crtc->dev);
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
struct vmw_surface *surface = NULL;
struct vmw_dma_buffer *dmabuf = NULL;
int ret;
if (handle) {
ret = vmw_user_surface_lookup(dev_priv, tfile,
handle, &surface);
if (!ret) {
if (!surface->snooper.image) {
DRM_ERROR("surface not suitable for cursor\n");
return -EINVAL;
}
} else {
ret = vmw_user_dmabuf_lookup(tfile,
handle, &dmabuf);
if (ret) {
DRM_ERROR("failed to find surface or dmabuf: %i\n", ret);
return -EINVAL;
}
}
}
/* takedown old cursor */
if (du->cursor_surface) {
du->cursor_surface->snooper.crtc = NULL;
vmw_surface_unreference(&du->cursor_surface);
}
if (du->cursor_dmabuf)
vmw_dmabuf_unreference(&du->cursor_dmabuf);
/* setup new image */
if (surface) {
/* vmw_user_surface_lookup takes one reference */
du->cursor_surface = surface;
du->cursor_surface->snooper.crtc = crtc;
du->cursor_age = du->cursor_surface->snooper.age;
vmw_cursor_update_image(dev_priv, surface->snooper.image,
64, 64, du->hotspot_x, du->hotspot_y);
} else if (dmabuf) {
struct ttm_bo_kmap_obj map;
unsigned long kmap_offset;
unsigned long kmap_num;
void *virtual;
bool dummy;
/* vmw_user_surface_lookup takes one reference */
du->cursor_dmabuf = dmabuf;
kmap_offset = 0;
kmap_num = (64*64*4) >> PAGE_SHIFT;
ret = ttm_bo_reserve(&dmabuf->base, true, false, false, 0);
if (unlikely(ret != 0)) {
DRM_ERROR("reserve failed\n");
return -EINVAL;
}
ret = ttm_bo_kmap(&dmabuf->base, kmap_offset, kmap_num, &map);
if (unlikely(ret != 0))
goto err_unreserve;
virtual = ttm_kmap_obj_virtual(&map, &dummy);
vmw_cursor_update_image(dev_priv, virtual, 64, 64,
du->hotspot_x, du->hotspot_y);
ttm_bo_kunmap(&map);
err_unreserve:
ttm_bo_unreserve(&dmabuf->base);
} else {
vmw_cursor_update_position(dev_priv, false, 0, 0);
return 0;
}
vmw_cursor_update_position(dev_priv, true, du->cursor_x, du->cursor_y);
return 0;
}
int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
{
struct vmw_private *dev_priv = vmw_priv(crtc->dev);
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
bool shown = du->cursor_surface || du->cursor_dmabuf ? true : false;
du->cursor_x = x + crtc->x;
du->cursor_y = y + crtc->y;
vmw_cursor_update_position(dev_priv, shown,
du->cursor_x, du->cursor_y);
return 0;
}
void vmw_kms_cursor_snoop(struct vmw_surface *srf,
struct ttm_object_file *tfile,
struct ttm_buffer_object *bo,
SVGA3dCmdHeader *header)
{
struct ttm_bo_kmap_obj map;
unsigned long kmap_offset;
unsigned long kmap_num;
SVGA3dCopyBox *box;
unsigned box_count;
void *virtual;
bool dummy;
struct vmw_dma_cmd {
SVGA3dCmdHeader header;
SVGA3dCmdSurfaceDMA dma;
} *cmd;
int ret;
cmd = container_of(header, struct vmw_dma_cmd, header);
/* No snooper installed */
if (!srf->snooper.image)
return;
if (cmd->dma.host.face != 0 || cmd->dma.host.mipmap != 0) {
DRM_ERROR("face and mipmap for cursors should never != 0\n");
return;
}
if (cmd->header.size < 64) {
DRM_ERROR("at least one full copy box must be given\n");
return;
}
box = (SVGA3dCopyBox *)&cmd[1];
box_count = (cmd->header.size - sizeof(SVGA3dCmdSurfaceDMA)) /
sizeof(SVGA3dCopyBox);
if (cmd->dma.guest.pitch != (64 * 4) ||
cmd->dma.guest.ptr.offset % PAGE_SIZE ||
box->x != 0 || box->y != 0 || box->z != 0 ||
box->srcx != 0 || box->srcy != 0 || box->srcz != 0 ||
box->w != 64 || box->h != 64 || box->d != 1 ||
box_count != 1) {
/* TODO handle none page aligned offsets */
/* TODO handle partial uploads and pitch != 256 */
/* TODO handle more then one copy (size != 64) */
DRM_ERROR("lazy programer, cant handle wierd stuff\n");
return;
}
kmap_offset = cmd->dma.guest.ptr.offset >> PAGE_SHIFT;
kmap_num = (64*64*4) >> PAGE_SHIFT;
ret = ttm_bo_reserve(bo, true, false, false, 0);
if (unlikely(ret != 0)) {
DRM_ERROR("reserve failed\n");
return;
}
ret = ttm_bo_kmap(bo, kmap_offset, kmap_num, &map);
if (unlikely(ret != 0))
goto err_unreserve;
virtual = ttm_kmap_obj_virtual(&map, &dummy);
memcpy(srf->snooper.image, virtual, 64*64*4);
srf->snooper.age++;
/* we can't call this function from this function since execbuf has
* reserved fifo space.
*
* if (srf->snooper.crtc)
* vmw_ldu_crtc_cursor_update_image(dev_priv,
* srf->snooper.image, 64, 64,
* du->hotspot_x, du->hotspot_y);
*/
ttm_bo_kunmap(&map);
err_unreserve:
ttm_bo_unreserve(bo);
}
void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)
{
struct drm_device *dev = dev_priv->dev;
struct vmw_display_unit *du;
struct drm_crtc *crtc;
mutex_lock(&dev->mode_config.mutex);
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
du = vmw_crtc_to_du(crtc);
if (!du->cursor_surface ||
du->cursor_age == du->cursor_surface->snooper.age)
continue;
du->cursor_age = du->cursor_surface->snooper.age;
vmw_cursor_update_image(dev_priv,
du->cursor_surface->snooper.image,
64, 64, du->hotspot_x, du->hotspot_y);
}
mutex_unlock(&dev->mode_config.mutex);
}
/*
* Generic framebuffer code
*/
int vmw_framebuffer_create_handle(struct drm_framebuffer *fb,
struct drm_file *file_priv,
unsigned int *handle)
{
if (handle)
handle = 0;
return 0;
}
/*
* Surface framebuffer code
*/
#define vmw_framebuffer_to_vfbs(x) \
container_of(x, struct vmw_framebuffer_surface, base.base)
struct vmw_framebuffer_surface {
struct vmw_framebuffer base;
struct vmw_surface *surface;
struct delayed_work d_work;
struct mutex work_lock;
bool present_fs;
};
void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer)
{
struct vmw_framebuffer_surface *vfb =
vmw_framebuffer_to_vfbs(framebuffer);
cancel_delayed_work_sync(&vfb->d_work);
drm_framebuffer_cleanup(framebuffer);
vmw_surface_unreference(&vfb->surface);
kfree(framebuffer);
}
static void vmw_framebuffer_present_fs_callback(struct work_struct *work)
{
struct delayed_work *d_work =
container_of(work, struct delayed_work, work);
struct vmw_framebuffer_surface *vfbs =
container_of(d_work, struct vmw_framebuffer_surface, d_work);
struct vmw_surface *surf = vfbs->surface;
struct drm_framebuffer *framebuffer = &vfbs->base.base;
struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
struct {
SVGA3dCmdHeader header;
SVGA3dCmdPresent body;
SVGA3dCopyRect cr;
} *cmd;
mutex_lock(&vfbs->work_lock);
if (!vfbs->present_fs)
goto out_unlock;
cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
if (unlikely(cmd == NULL))
goto out_resched;
cmd->header.id = cpu_to_le32(SVGA_3D_CMD_PRESENT);
cmd->header.size = cpu_to_le32(sizeof(cmd->body) + sizeof(cmd->cr));
cmd->body.sid = cpu_to_le32(surf->res.id);
cmd->cr.x = cpu_to_le32(0);
cmd->cr.y = cpu_to_le32(0);
cmd->cr.srcx = cmd->cr.x;
cmd->cr.srcy = cmd->cr.y;
cmd->cr.w = cpu_to_le32(framebuffer->width);
cmd->cr.h = cpu_to_le32(framebuffer->height);
vfbs->present_fs = false;
vmw_fifo_commit(dev_priv, sizeof(*cmd));
out_resched:
/**
* Will not re-add if already pending.
*/
schedule_delayed_work(&vfbs->d_work, VMWGFX_PRESENT_RATE);
out_unlock:
mutex_unlock(&vfbs->work_lock);
}
int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
unsigned flags, unsigned color,
struct drm_clip_rect *clips,
unsigned num_clips)
{
struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
struct vmw_framebuffer_surface *vfbs =
vmw_framebuffer_to_vfbs(framebuffer);
struct vmw_surface *surf = vfbs->surface;
struct drm_clip_rect norect;
SVGA3dCopyRect *cr;
int i, inc = 1;
struct {
SVGA3dCmdHeader header;
SVGA3dCmdPresent body;
SVGA3dCopyRect cr;
} *cmd;
if (!num_clips ||
!(dev_priv->fifo.capabilities &
SVGA_FIFO_CAP_SCREEN_OBJECT)) {
int ret;
mutex_lock(&vfbs->work_lock);
vfbs->present_fs = true;
ret = schedule_delayed_work(&vfbs->d_work, VMWGFX_PRESENT_RATE);
mutex_unlock(&vfbs->work_lock);
if (ret) {
/**
* No work pending, Force immediate present.
*/
vmw_framebuffer_present_fs_callback(&vfbs->d_work.work);
}
return 0;
}
if (!num_clips) {
num_clips = 1;
clips = &norect;
norect.x1 = norect.y1 = 0;
norect.x2 = framebuffer->width;
norect.y2 = framebuffer->height;
} else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
num_clips /= 2;
inc = 2; /* skip source rects */
}
cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr));
if (unlikely(cmd == NULL)) {
DRM_ERROR("Fifo reserve failed.\n");
return -ENOMEM;
}
memset(cmd, 0, sizeof(*cmd));
cmd->header.id = cpu_to_le32(SVGA_3D_CMD_PRESENT);
cmd->header.size = cpu_to_le32(sizeof(cmd->body) + num_clips * sizeof(cmd->cr));
cmd->body.sid = cpu_to_le32(surf->res.id);
for (i = 0, cr = &cmd->cr; i < num_clips; i++, cr++, clips += inc) {
cr->x = cpu_to_le16(clips->x1);
cr->y = cpu_to_le16(clips->y1);
cr->srcx = cr->x;
cr->srcy = cr->y;
cr->w = cpu_to_le16(clips->x2 - clips->x1);
cr->h = cpu_to_le16(clips->y2 - clips->y1);
}
vmw_fifo_commit(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr));
return 0;
}
static struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = {
.destroy = vmw_framebuffer_surface_destroy,
.dirty = vmw_framebuffer_surface_dirty,
.create_handle = vmw_framebuffer_create_handle,
};
int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
struct vmw_surface *surface,
struct vmw_framebuffer **out,
unsigned width, unsigned height)
{
struct drm_device *dev = dev_priv->dev;
struct vmw_framebuffer_surface *vfbs;
int ret;
vfbs = kzalloc(sizeof(*vfbs), GFP_KERNEL);
if (!vfbs) {
ret = -ENOMEM;
goto out_err1;
}
ret = drm_framebuffer_init(dev, &vfbs->base.base,
&vmw_framebuffer_surface_funcs);
if (ret)
goto out_err2;
if (!vmw_surface_reference(surface)) {
DRM_ERROR("failed to reference surface %p\n", surface);
goto out_err3;
}
/* XXX get the first 3 from the surface info */
vfbs->base.base.bits_per_pixel = 32;
vfbs->base.base.pitch = width * 32 / 4;
vfbs->base.base.depth = 24;
vfbs->base.base.width = width;
vfbs->base.base.height = height;
vfbs->base.pin = NULL;
vfbs->base.unpin = NULL;
vfbs->surface = surface;
mutex_init(&vfbs->work_lock);
INIT_DELAYED_WORK(&vfbs->d_work, &vmw_framebuffer_present_fs_callback);
*out = &vfbs->base;
return 0;
out_err3:
drm_framebuffer_cleanup(&vfbs->base.base);
out_err2:
kfree(vfbs);
out_err1:
return ret;
}
/*
* Dmabuf framebuffer code
*/
#define vmw_framebuffer_to_vfbd(x) \
container_of(x, struct vmw_framebuffer_dmabuf, base.base)
struct vmw_framebuffer_dmabuf {
struct vmw_framebuffer base;
struct vmw_dma_buffer *buffer;
};
void vmw_framebuffer_dmabuf_destroy(struct drm_framebuffer *framebuffer)
{
struct vmw_framebuffer_dmabuf *vfbd =
vmw_framebuffer_to_vfbd(framebuffer);
drm_framebuffer_cleanup(framebuffer);
vmw_dmabuf_unreference(&vfbd->buffer);
kfree(vfbd);
}
int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
unsigned flags, unsigned color,
struct drm_clip_rect *clips,
unsigned num_clips)
{
struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
struct drm_clip_rect norect;
struct {
uint32_t header;
SVGAFifoCmdUpdate body;
} *cmd;
int i, increment = 1;
if (!num_clips ||
!(dev_priv->fifo.capabilities &
SVGA_FIFO_CAP_SCREEN_OBJECT)) {
num_clips = 1;
clips = &norect;
norect.x1 = norect.y1 = 0;
norect.x2 = framebuffer->width;
norect.y2 = framebuffer->height;
} else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
num_clips /= 2;
increment = 2;
}
cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) * num_clips);
if (unlikely(cmd == NULL)) {
DRM_ERROR("Fifo reserve failed.\n");
return -ENOMEM;
}
for (i = 0; i < num_clips; i++, clips += increment) {
cmd[i].header = cpu_to_le32(SVGA_CMD_UPDATE);
cmd[i].body.x = cpu_to_le32(clips[i].x1);
cmd[i].body.y = cpu_to_le32(clips[i].y1);
cmd[i].body.width = cpu_to_le32(clips[i].x2 - clips[i].x1);
cmd[i].body.height = cpu_to_le32(clips[i].y2 - clips[i].y1);
}
vmw_fifo_commit(dev_priv, sizeof(*cmd) * num_clips);
return 0;
}
static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = {
.destroy = vmw_framebuffer_dmabuf_destroy,
.dirty = vmw_framebuffer_dmabuf_dirty,
.create_handle = vmw_framebuffer_create_handle,
};
static int vmw_framebuffer_dmabuf_pin(struct vmw_framebuffer *vfb)
{
struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
struct vmw_framebuffer_dmabuf *vfbd =
vmw_framebuffer_to_vfbd(&vfb->base);
int ret;
vmw_overlay_pause_all(dev_priv);
ret = vmw_dmabuf_to_start_of_vram(dev_priv, vfbd->buffer);
if (dev_priv->capabilities & SVGA_CAP_MULTIMON) {
vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1);
vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, 0);
vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, true);
vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, 0);
vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, 0);
vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, 0);
vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, 0);
vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
vmw_write(dev_priv, SVGA_REG_ENABLE, 1);
vmw_write(dev_priv, SVGA_REG_WIDTH, vfb->base.width);
vmw_write(dev_priv, SVGA_REG_HEIGHT, vfb->base.height);
vmw_write(dev_priv, SVGA_REG_BITS_PER_PIXEL, vfb->base.bits_per_pixel);
vmw_write(dev_priv, SVGA_REG_DEPTH, vfb->base.depth);
vmw_write(dev_priv, SVGA_REG_RED_MASK, 0x00ff0000);
vmw_write(dev_priv, SVGA_REG_GREEN_MASK, 0x0000ff00);
vmw_write(dev_priv, SVGA_REG_BLUE_MASK, 0x000000ff);
} else
WARN_ON(true);
vmw_overlay_resume_all(dev_priv);
return 0;
}
static int vmw_framebuffer_dmabuf_unpin(struct vmw_framebuffer *vfb)
{
struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
struct vmw_framebuffer_dmabuf *vfbd =
vmw_framebuffer_to_vfbd(&vfb->base);
if (!vfbd->buffer) {
WARN_ON(!vfbd->buffer);
return 0;
}
return vmw_dmabuf_from_vram(dev_priv, vfbd->buffer);
}
int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
struct vmw_dma_buffer *dmabuf,
struct vmw_framebuffer **out,
unsigned width, unsigned height)
{
struct drm_device *dev = dev_priv->dev;
struct vmw_framebuffer_dmabuf *vfbd;
int ret;
vfbd = kzalloc(sizeof(*vfbd), GFP_KERNEL);
if (!vfbd) {
ret = -ENOMEM;
goto out_err1;
}
ret = drm_framebuffer_init(dev, &vfbd->base.base,
&vmw_framebuffer_dmabuf_funcs);
if (ret)
goto out_err2;
if (!vmw_dmabuf_reference(dmabuf)) {
DRM_ERROR("failed to reference dmabuf %p\n", dmabuf);
goto out_err3;
}
/* XXX get the first 3 from the surface info */
vfbd->base.base.bits_per_pixel = 32;
vfbd->base.base.pitch = width * 32 / 4;
vfbd->base.base.depth = 24;
vfbd->base.base.width = width;
vfbd->base.base.height = height;
vfbd->base.pin = vmw_framebuffer_dmabuf_pin;
vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin;
vfbd->buffer = dmabuf;
*out = &vfbd->base;
return 0;
out_err3:
drm_framebuffer_cleanup(&vfbd->base.base);
out_err2:
kfree(vfbd);
out_err1:
return ret;
}
/*
* Generic Kernel modesetting functions
*/
static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
struct drm_file *file_priv,
struct drm_mode_fb_cmd *mode_cmd)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
struct vmw_framebuffer *vfb = NULL;
struct vmw_surface *surface = NULL;
struct vmw_dma_buffer *bo = NULL;
int ret;
ret = vmw_user_surface_lookup(dev_priv, tfile,
mode_cmd->handle, &surface);
if (ret)
goto try_dmabuf;
ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb,
mode_cmd->width, mode_cmd->height);
/* vmw_user_surface_lookup takes one ref so does new_fb */
vmw_surface_unreference(&surface);
if (ret) {
DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
return NULL;
}
return &vfb->base;
try_dmabuf:
DRM_INFO("%s: trying buffer\n", __func__);
ret = vmw_user_dmabuf_lookup(tfile, mode_cmd->handle, &bo);
if (ret) {
DRM_ERROR("failed to find buffer: %i\n", ret);
return NULL;
}
ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, bo, &vfb,
mode_cmd->width, mode_cmd->height);
/* vmw_user_dmabuf_lookup takes one ref so does new_fb */
vmw_dmabuf_unreference(&bo);
if (ret) {
DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
return NULL;
}
return &vfb->base;
}
static int vmw_kms_fb_changed(struct drm_device *dev)
{
return 0;
}
static struct drm_mode_config_funcs vmw_kms_funcs = {
.fb_create = vmw_kms_fb_create,
.fb_changed = vmw_kms_fb_changed,
};
int vmw_kms_init(struct vmw_private *dev_priv)
{
struct drm_device *dev = dev_priv->dev;
int ret;
drm_mode_config_init(dev);
dev->mode_config.funcs = &vmw_kms_funcs;
dev->mode_config.min_width = 640;
dev->mode_config.min_height = 480;
dev->mode_config.max_width = 2048;
dev->mode_config.max_height = 2048;
ret = vmw_kms_init_legacy_display_system(dev_priv);
return 0;
}
int vmw_kms_close(struct vmw_private *dev_priv)
{
/*
* Docs says we should take the lock before calling this function
* but since it destroys encoders and our destructor calls
* drm_encoder_cleanup which takes the lock we deadlock.
*/
drm_mode_config_cleanup(dev_priv->dev);
vmw_kms_close_legacy_display_system(dev_priv);
return 0;
}
int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_vmw_cursor_bypass_arg *arg = data;
struct vmw_display_unit *du;
struct drm_mode_object *obj;
struct drm_crtc *crtc;
int ret = 0;
mutex_lock(&dev->mode_config.mutex);
if (arg->flags & DRM_VMW_CURSOR_BYPASS_ALL) {
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
du = vmw_crtc_to_du(crtc);
du->hotspot_x = arg->xhot;
du->hotspot_y = arg->yhot;
}
mutex_unlock(&dev->mode_config.mutex);
return 0;
}
obj = drm_mode_object_find(dev, arg->crtc_id, DRM_MODE_OBJECT_CRTC);
if (!obj) {
ret = -EINVAL;
goto out;
}
crtc = obj_to_crtc(obj);
du = vmw_crtc_to_du(crtc);
du->hotspot_x = arg->xhot;
du->hotspot_y = arg->yhot;
out:
mutex_unlock(&dev->mode_config.mutex);
return ret;
}
int vmw_kms_save_vga(struct vmw_private *vmw_priv)
{
/*
* setup a single multimon monitor with the size
* of 0x0, this stops the UI from resizing when we
* change the framebuffer size
*/
if (vmw_priv->capabilities & SVGA_CAP_MULTIMON) {
vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, 0);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_IS_PRIMARY, true);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_X, 0);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_Y, 0);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_WIDTH, 0);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_HEIGHT, 0);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
}
vmw_priv->vga_width = vmw_read(vmw_priv, SVGA_REG_WIDTH);
vmw_priv->vga_height = vmw_read(vmw_priv, SVGA_REG_HEIGHT);
vmw_priv->vga_bpp = vmw_read(vmw_priv, SVGA_REG_BITS_PER_PIXEL);
vmw_priv->vga_depth = vmw_read(vmw_priv, SVGA_REG_DEPTH);
vmw_priv->vga_pseudo = vmw_read(vmw_priv, SVGA_REG_PSEUDOCOLOR);
vmw_priv->vga_red_mask = vmw_read(vmw_priv, SVGA_REG_RED_MASK);
vmw_priv->vga_green_mask = vmw_read(vmw_priv, SVGA_REG_GREEN_MASK);
vmw_priv->vga_blue_mask = vmw_read(vmw_priv, SVGA_REG_BLUE_MASK);
return 0;
}
int vmw_kms_restore_vga(struct vmw_private *vmw_priv)
{
vmw_write(vmw_priv, SVGA_REG_WIDTH, vmw_priv->vga_width);
vmw_write(vmw_priv, SVGA_REG_HEIGHT, vmw_priv->vga_height);
vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, vmw_priv->vga_bpp);
vmw_write(vmw_priv, SVGA_REG_DEPTH, vmw_priv->vga_depth);
vmw_write(vmw_priv, SVGA_REG_PSEUDOCOLOR, vmw_priv->vga_pseudo);
vmw_write(vmw_priv, SVGA_REG_RED_MASK, vmw_priv->vga_red_mask);
vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, vmw_priv->vga_green_mask);
vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, vmw_priv->vga_blue_mask);
/* TODO check for multimon */
vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 0);
return 0;
}
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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.
*
**************************************************************************/
#ifndef VMWGFX_KMS_H_
#define VMWGFX_KMS_H_
#include "drmP.h"
#include "vmwgfx_drv.h"
#define vmw_framebuffer_to_vfb(x) \
container_of(x, struct vmw_framebuffer, base)
/**
* Base class for framebuffers
*
* @pin is called the when ever a crtc uses this framebuffer
* @unpin is called
*/
struct vmw_framebuffer {
struct drm_framebuffer base;
int (*pin)(struct vmw_framebuffer *fb);
int (*unpin)(struct vmw_framebuffer *fb);
};
#define vmw_crtc_to_du(x) \
container_of(x, struct vmw_display_unit, crtc)
/*
* Basic cursor manipulation
*/
int vmw_cursor_update_image(struct vmw_private *dev_priv,
u32 *image, u32 width, u32 height,
u32 hotspotX, u32 hotspotY);
void vmw_cursor_update_position(struct vmw_private *dev_priv,
bool show, int x, int y);
/**
* Base class display unit.
*
* Since the SVGA hw doesn't have a concept of a crtc, encoder or connector
* so the display unit is all of them at the same time. This is true for both
* legacy multimon and screen objects.
*/
struct vmw_display_unit {
struct drm_crtc crtc;
struct drm_encoder encoder;
struct drm_connector connector;
struct vmw_surface *cursor_surface;
struct vmw_dma_buffer *cursor_dmabuf;
size_t cursor_age;
int cursor_x;
int cursor_y;
int hotspot_x;
int hotspot_y;
unsigned unit;
};
/*
* Shared display unit functions - vmwgfx_kms.c
*/
void vmw_display_unit_cleanup(struct vmw_display_unit *du);
int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
uint32_t handle, uint32_t width, uint32_t height);
int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y);
/*
* Legacy display unit functions - vmwgfx_ldu.h
*/
int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv);
int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv);
#endif
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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_kms.h"
#define vmw_crtc_to_ldu(x) \
container_of(x, struct vmw_legacy_display_unit, base.crtc)
#define vmw_encoder_to_ldu(x) \
container_of(x, struct vmw_legacy_display_unit, base.encoder)
#define vmw_connector_to_ldu(x) \
container_of(x, struct vmw_legacy_display_unit, base.connector)
struct vmw_legacy_display {
struct list_head active;
unsigned num_active;
struct vmw_framebuffer *fb;
};
/**
* Display unit using the legacy register interface.
*/
struct vmw_legacy_display_unit {
struct vmw_display_unit base;
struct list_head active;
unsigned unit;
};
static void vmw_ldu_destroy(struct vmw_legacy_display_unit *ldu)
{
list_del_init(&ldu->active);
vmw_display_unit_cleanup(&ldu->base);
kfree(ldu);
}
/*
* Legacy Display Unit CRTC functions
*/
static void vmw_ldu_crtc_save(struct drm_crtc *crtc)
{
}
static void vmw_ldu_crtc_restore(struct drm_crtc *crtc)
{
}
static void vmw_ldu_crtc_gamma_set(struct drm_crtc *crtc,
u16 *r, u16 *g, u16 *b,
uint32_t size)
{
}
static void vmw_ldu_crtc_destroy(struct drm_crtc *crtc)
{
vmw_ldu_destroy(vmw_crtc_to_ldu(crtc));
}
static int vmw_ldu_commit_list(struct vmw_private *dev_priv)
{
struct vmw_legacy_display *lds = dev_priv->ldu_priv;
struct vmw_legacy_display_unit *entry;
struct drm_crtc *crtc;
int i = 0;
/* to stop the screen from changing size on resize */
vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 0);
for (i = 0; i < lds->num_active; i++) {
vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, i);
vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, !i);
vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, 0);
vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, 0);
vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, 0);
vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, 0);
vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
}
/* Now set the mode */
vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, lds->num_active);
i = 0;
list_for_each_entry(entry, &lds->active, active) {
crtc = &entry->base.crtc;
vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, i);
vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, !i);
vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, crtc->x);
vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, crtc->y);
vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, crtc->mode.hdisplay);
vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, crtc->mode.vdisplay);
vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
i++;
}
return 0;
}
static int vmw_ldu_del_active(struct vmw_private *vmw_priv,
struct vmw_legacy_display_unit *ldu)
{
struct vmw_legacy_display *ld = vmw_priv->ldu_priv;
if (list_empty(&ldu->active))
return 0;
list_del_init(&ldu->active);
if (--(ld->num_active) == 0) {
BUG_ON(!ld->fb);
if (ld->fb->unpin)
ld->fb->unpin(ld->fb);
ld->fb = NULL;
}
return 0;
}
static int vmw_ldu_add_active(struct vmw_private *vmw_priv,
struct vmw_legacy_display_unit *ldu,
struct vmw_framebuffer *vfb)
{
struct vmw_legacy_display *ld = vmw_priv->ldu_priv;
struct vmw_legacy_display_unit *entry;
struct list_head *at;
if (!list_empty(&ldu->active))
return 0;
at = &ld->active;
list_for_each_entry(entry, &ld->active, active) {
if (entry->unit > ldu->unit)
break;
at = &entry->active;
}
list_add(&ldu->active, at);
if (ld->num_active++ == 0) {
BUG_ON(ld->fb);
if (vfb->pin)
vfb->pin(vfb);
ld->fb = vfb;
}
return 0;
}
static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)
{
struct vmw_private *dev_priv;
struct vmw_legacy_display_unit *ldu;
struct drm_connector *connector;
struct drm_display_mode *mode;
struct drm_encoder *encoder;
struct vmw_framebuffer *vfb;
struct drm_framebuffer *fb;
struct drm_crtc *crtc;
if (!set)
return -EINVAL;
if (!set->crtc)
return -EINVAL;
/* get the ldu */
crtc = set->crtc;
ldu = vmw_crtc_to_ldu(crtc);
vfb = set->fb ? vmw_framebuffer_to_vfb(set->fb) : NULL;
dev_priv = vmw_priv(crtc->dev);
if (set->num_connectors > 1) {
DRM_ERROR("to many connectors\n");
return -EINVAL;
}
if (set->num_connectors == 1 &&
set->connectors[0] != &ldu->base.connector) {
DRM_ERROR("connector doesn't match %p %p\n",
set->connectors[0], &ldu->base.connector);
return -EINVAL;
}
/* ldu only supports one fb active at the time */
if (dev_priv->ldu_priv->fb && vfb &&
dev_priv->ldu_priv->fb != vfb) {
DRM_ERROR("Multiple framebuffers not supported\n");
return -EINVAL;
}
/* since they always map one to one these are safe */
connector = &ldu->base.connector;
encoder = &ldu->base.encoder;
/* should we turn the crtc off? */
if (set->num_connectors == 0 || !set->mode || !set->fb) {
connector->encoder = NULL;
encoder->crtc = NULL;
crtc->fb = NULL;
vmw_ldu_del_active(dev_priv, ldu);
vmw_ldu_commit_list(dev_priv);
return 0;
}
/* we now know we want to set a mode */
mode = set->mode;
fb = set->fb;
if (set->x + mode->hdisplay > fb->width ||
set->y + mode->vdisplay > fb->height) {
DRM_ERROR("set outside of framebuffer\n");
return -EINVAL;
}
vmw_fb_off(dev_priv);
crtc->fb = fb;
encoder->crtc = crtc;
connector->encoder = encoder;
crtc->x = set->x;
crtc->y = set->y;
crtc->mode = *mode;
vmw_ldu_add_active(dev_priv, ldu, vfb);
vmw_ldu_commit_list(dev_priv);
return 0;
}
static struct drm_crtc_funcs vmw_legacy_crtc_funcs = {
.save = vmw_ldu_crtc_save,
.restore = vmw_ldu_crtc_restore,
.cursor_set = vmw_du_crtc_cursor_set,
.cursor_move = vmw_du_crtc_cursor_move,
.gamma_set = vmw_ldu_crtc_gamma_set,
.destroy = vmw_ldu_crtc_destroy,
.set_config = vmw_ldu_crtc_set_config,
};
/*
* Legacy Display Unit encoder functions
*/
static void vmw_ldu_encoder_destroy(struct drm_encoder *encoder)
{
vmw_ldu_destroy(vmw_encoder_to_ldu(encoder));
}
static struct drm_encoder_funcs vmw_legacy_encoder_funcs = {
.destroy = vmw_ldu_encoder_destroy,
};
/*
* Legacy Display Unit connector functions
*/
static void vmw_ldu_connector_dpms(struct drm_connector *connector, int mode)
{
}
static void vmw_ldu_connector_save(struct drm_connector *connector)
{
}
static void vmw_ldu_connector_restore(struct drm_connector *connector)
{
}
static enum drm_connector_status
vmw_ldu_connector_detect(struct drm_connector *connector)
{
/* XXX vmwctrl should control connection status */
if (vmw_connector_to_ldu(connector)->base.unit == 0)
return connector_status_connected;
return connector_status_disconnected;
}
static struct drm_display_mode vmw_ldu_connector_builtin[] = {
/* 640x480@60Hz */
{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
752, 800, 0, 480, 489, 492, 525, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
/* 800x600@60Hz */
{ DRM_MODE("800x600",
DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
40000, 800, 840, 968, 1056, 0, 600, 601, 605, 628,
0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
/* 1024x768@60Hz */
{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
1184, 1344, 0, 768, 771, 777, 806, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
/* 1152x864@75Hz */
{ DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
1344, 1600, 0, 864, 865, 868, 900, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
/* 1280x768@60Hz */
{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
1472, 1664, 0, 768, 771, 778, 798, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
/* 1280x800@60Hz */
{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
1480, 1680, 0, 800, 803, 809, 831, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
/* 1280x960@60Hz */
{ DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
1488, 1800, 0, 960, 961, 964, 1000, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
/* 1280x1024@60Hz */
{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
/* 1360x768@60Hz */
{ DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
1536, 1792, 0, 768, 771, 777, 795, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
/* 1440x1050@60Hz */
{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
/* 1440x900@60Hz */
{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
1672, 1904, 0, 900, 903, 909, 934, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
/* 1600x1200@60Hz */
{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
/* 1680x1050@60Hz */
{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
/* 1792x1344@60Hz */
{ DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
/* 1853x1392@60Hz */
{ DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
/* 1920x1200@60Hz */
{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
/* 1920x1440@60Hz */
{ DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
/* 2560x1600@60Hz */
{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
/* Terminate */
{ DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) },
};
static int vmw_ldu_connector_fill_modes(struct drm_connector *connector,
uint32_t max_width, uint32_t max_height)
{
struct drm_device *dev = connector->dev;
struct drm_display_mode *mode = NULL;
int i;
for (i = 0; vmw_ldu_connector_builtin[i].type != 0; i++) {
if (vmw_ldu_connector_builtin[i].hdisplay > max_width ||
vmw_ldu_connector_builtin[i].vdisplay > max_height)
continue;
mode = drm_mode_duplicate(dev, &vmw_ldu_connector_builtin[i]);
if (!mode)
return 0;
mode->vrefresh = drm_mode_vrefresh(mode);
drm_mode_probed_add(connector, mode);
}
drm_mode_connector_list_update(connector);
return 1;
}
static int vmw_ldu_connector_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t val)
{
return 0;
}
static void vmw_ldu_connector_destroy(struct drm_connector *connector)
{
vmw_ldu_destroy(vmw_connector_to_ldu(connector));
}
static struct drm_connector_funcs vmw_legacy_connector_funcs = {
.dpms = vmw_ldu_connector_dpms,
.save = vmw_ldu_connector_save,
.restore = vmw_ldu_connector_restore,
.detect = vmw_ldu_connector_detect,
.fill_modes = vmw_ldu_connector_fill_modes,
.set_property = vmw_ldu_connector_set_property,
.destroy = vmw_ldu_connector_destroy,
};
static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
{
struct vmw_legacy_display_unit *ldu;
struct drm_device *dev = dev_priv->dev;
struct drm_connector *connector;
struct drm_encoder *encoder;
struct drm_crtc *crtc;
ldu = kzalloc(sizeof(*ldu), GFP_KERNEL);
if (!ldu)
return -ENOMEM;
ldu->unit = unit;
crtc = &ldu->base.crtc;
encoder = &ldu->base.encoder;
connector = &ldu->base.connector;
drm_connector_init(dev, connector, &vmw_legacy_connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
/* Initial status */
if (unit == 0)
connector->status = connector_status_connected;
else
connector->status = connector_status_disconnected;
drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs,
DRM_MODE_ENCODER_LVDS);
drm_mode_connector_attach_encoder(connector, encoder);
encoder->possible_crtcs = (1 << unit);
encoder->possible_clones = 0;
INIT_LIST_HEAD(&ldu->active);
drm_crtc_init(dev, crtc, &vmw_legacy_crtc_funcs);
drm_connector_attach_property(connector,
dev->mode_config.dirty_info_property,
1);
return 0;
}
int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv)
{
if (dev_priv->ldu_priv) {
DRM_INFO("ldu system already on\n");
return -EINVAL;
}
dev_priv->ldu_priv = kmalloc(GFP_KERNEL, sizeof(*dev_priv->ldu_priv));
if (!dev_priv->ldu_priv)
return -ENOMEM;
INIT_LIST_HEAD(&dev_priv->ldu_priv->active);
dev_priv->ldu_priv->num_active = 0;
dev_priv->ldu_priv->fb = NULL;
drm_mode_create_dirty_info_property(dev_priv->dev);
vmw_ldu_init(dev_priv, 0);
vmw_ldu_init(dev_priv, 1);
vmw_ldu_init(dev_priv, 2);
vmw_ldu_init(dev_priv, 3);
vmw_ldu_init(dev_priv, 4);
vmw_ldu_init(dev_priv, 5);
vmw_ldu_init(dev_priv, 6);
vmw_ldu_init(dev_priv, 7);
return 0;
}
int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv)
{
if (!dev_priv->ldu_priv)
return -ENOSYS;
BUG_ON(!list_empty(&dev_priv->ldu_priv->active));
kfree(dev_priv->ldu_priv);
return 0;
}
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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 "drmP.h"
#include "vmwgfx_drv.h"
#include "ttm/ttm_placement.h"
#include "svga_overlay.h"
#include "svga_escape.h"
#define VMW_MAX_NUM_STREAMS 1
struct vmw_stream {
struct vmw_dma_buffer *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];
};
static inline struct vmw_overlay *vmw_overlay(struct drm_device *dev)
{
struct vmw_private *dev_priv = vmw_priv(dev);
return dev_priv ? dev_priv->overlay_priv : NULL;
}
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;
}
/**
* Pin or unpin a buffer in vram.
*
* @dev_priv: Driver private.
* @buf: DMA buffer to pin or unpin.
* @pin: Pin buffer in vram if true.
* @interruptible: Use interruptible wait.
*
* Takes the current masters ttm lock in read.
*
* Returns
* -ERESTARTSYS if interrupted by a signal.
*/
static int vmw_dmabuf_pin_in_vram(struct vmw_private *dev_priv,
struct vmw_dma_buffer *buf,
bool pin, bool interruptible)
{
struct ttm_buffer_object *bo = &buf->base;
struct ttm_bo_global *glob = bo->glob;
struct ttm_placement *overlay_placement = &vmw_vram_placement;
int ret;
ret = ttm_read_lock(&dev_priv->active_master->lock, interruptible);
if (unlikely(ret != 0))
return ret;
ret = ttm_bo_reserve(bo, interruptible, false, false, 0);
if (unlikely(ret != 0))
goto err;
if (buf->gmr_bound) {
vmw_gmr_unbind(dev_priv, buf->gmr_id);
spin_lock(&glob->lru_lock);
ida_remove(&dev_priv->gmr_ida, buf->gmr_id);
spin_unlock(&glob->lru_lock);
buf->gmr_bound = NULL;
}
if (pin)
overlay_placement = &vmw_vram_ne_placement;
ret = ttm_bo_validate(bo, overlay_placement, interruptible, false);
ttm_bo_unreserve(bo);
err:
ttm_read_unlock(&dev_priv->active_master->lock);
return ret;
}
/**
* 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_dma_buffer *buf,
struct drm_vmw_control_stream_arg *arg,
bool interruptible)
{
struct {
struct vmw_escape_header escape;
struct {
struct {
uint32_t cmdType;
uint32_t streamId;
} header;
struct {
uint32_t registerId;
uint32_t value;
} items[SVGA_VIDEO_PITCH_3 + 1];
} body;
struct vmw_escape_video_flush flush;
} *cmds;
uint32_t offset;
int i, ret;
for (;;) {
cmds = vmw_fifo_reserve(dev_priv, sizeof(*cmds));
if (cmds)
break;
ret = vmw_fallback_wait(dev_priv, false, true, 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 = arg->stream_id;
for (i = 0; i <= SVGA_VIDEO_PITCH_3; i++)
cmds->body.items[i].registerId = i;
offset = buf->base.offset + arg->offset;
cmds->body.items[SVGA_VIDEO_ENABLED].value = true;
cmds->body.items[SVGA_VIDEO_FLAGS].value = arg->flags;
cmds->body.items[SVGA_VIDEO_DATA_OFFSET].value = offset;
cmds->body.items[SVGA_VIDEO_FORMAT].value = arg->format;
cmds->body.items[SVGA_VIDEO_COLORKEY].value = arg->color_key;
cmds->body.items[SVGA_VIDEO_SIZE].value = arg->size;
cmds->body.items[SVGA_VIDEO_WIDTH].value = arg->width;
cmds->body.items[SVGA_VIDEO_HEIGHT].value = arg->height;
cmds->body.items[SVGA_VIDEO_SRC_X].value = arg->src.x;
cmds->body.items[SVGA_VIDEO_SRC_Y].value = arg->src.y;
cmds->body.items[SVGA_VIDEO_SRC_WIDTH].value = arg->src.w;
cmds->body.items[SVGA_VIDEO_SRC_HEIGHT].value = arg->src.h;
cmds->body.items[SVGA_VIDEO_DST_X].value = arg->dst.x;
cmds->body.items[SVGA_VIDEO_DST_Y].value = arg->dst.y;
cmds->body.items[SVGA_VIDEO_DST_WIDTH].value = arg->dst.w;
cmds->body.items[SVGA_VIDEO_DST_HEIGHT].value = arg->dst.h;
cmds->body.items[SVGA_VIDEO_PITCH_1].value = arg->pitch[0];
cmds->body.items[SVGA_VIDEO_PITCH_2].value = arg->pitch[1];
cmds->body.items[SVGA_VIDEO_PITCH_3].value = arg->pitch[2];
fill_flush(&cmds->flush, arg->stream_id);
vmw_fifo_commit(dev_priv, sizeof(*cmds));
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_fifo_reserve(dev_priv, sizeof(*cmds));
if (cmds)
break;
ret = vmw_fallback_wait(dev_priv, false, true, 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_fifo_commit(dev_priv, sizeof(*cmds));
return 0;
}
/**
* 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_dmabuf_pin_in_vram(dev_priv, stream->buf, false,
interruptible);
if (interruptible && ret == -ERESTARTSYS)
return ret;
else
BUG_ON(ret != 0);
}
if (!pause) {
vmw_dmabuf_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_dma_buffer *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_dmabuf_pin_in_vram(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_dmabuf_pin_in_vram(dev_priv, buf, false, false) != 0);
return ret;
}
if (stream->buf != buf)
stream->buf = vmw_dmabuf_reference(buf);
stream->saved = *arg;
return 0;
}
/**
* Stop all streams.
*
* Used by the fb code when starting.
*
* Takes the overlay lock.
*/
int vmw_overlay_stop_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->buf)
continue;
ret = vmw_overlay_stop(dev_priv, i, false, false);
WARN_ON(ret != 0);
}
mutex_unlock(&overlay->mutex);
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, true, false);
WARN_ON(ret != 0);
}
mutex_unlock(&overlay->mutex);
return 0;
}
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_dma_buffer *buf;
struct vmw_resource *res;
int ret;
if (!overlay)
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, false, true);
goto out_unlock;
}
ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &buf);
if (ret)
goto out_unlock;
ret = vmw_overlay_update_stream(dev_priv, buf, arg, true);
vmw_dmabuf_unreference(&buf);
out_unlock:
mutex_unlock(&overlay->mutex);
vmw_resource_unreference(&res);
return ret;
}
int vmw_overlay_num_overlays(struct vmw_private *dev_priv)
{
if (!dev_priv->overlay_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 (!overlay)
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, false, false);
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;
if (!(dev_priv->fifo.capabilities & SVGA_FIFO_CAP_VIDEO) &&
(dev_priv->fifo.capabilities & SVGA_FIFO_CAP_ESCAPE)) {
DRM_INFO("hardware doesn't support overlays\n");
return -ENOSYS;
}
overlay = kmalloc(GFP_KERNEL, sizeof(*overlay));
if (!overlay)
return -ENOMEM;
memset(overlay, 0, sizeof(*overlay));
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, false, false);
}
}
WARN_ON(forgotten_buffer);
dev_priv->overlay_priv = NULL;
kfree(overlay);
return 0;
}
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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.
*
**************************************************************************/
/**
* This file contains virtual hardware defines for kernel space.
*/
#ifndef _VMWGFX_REG_H_
#define _VMWGFX_REG_H_
#include <linux/types.h>
#define VMWGFX_INDEX_PORT 0x0
#define VMWGFX_VALUE_PORT 0x1
#define VMWGFX_IRQSTATUS_PORT 0x8
struct svga_guest_mem_descriptor {
__le32 ppn;
__le32 num_pages;
};
struct svga_fifo_cmd_fence {
__le32 fence;
};
#define SVGA_SYNC_GENERIC 1
#define SVGA_SYNC_FIFOFULL 2
#include "svga_types.h"
#include "svga3d_reg.h"
#endif
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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_drv.h"
#include "vmwgfx_drm.h"
#include "ttm/ttm_object.h"
#include "ttm/ttm_placement.h"
#include "drmP.h"
#define VMW_RES_CONTEXT ttm_driver_type0
#define VMW_RES_SURFACE ttm_driver_type1
#define VMW_RES_STREAM ttm_driver_type2
struct vmw_user_context {
struct ttm_base_object base;
struct vmw_resource res;
};
struct vmw_user_surface {
struct ttm_base_object base;
struct vmw_surface srf;
};
struct vmw_user_dma_buffer {
struct ttm_base_object base;
struct vmw_dma_buffer dma;
};
struct vmw_bo_user_rep {
uint32_t handle;
uint64_t map_handle;
};
struct vmw_stream {
struct vmw_resource res;
uint32_t stream_id;
};
struct vmw_user_stream {
struct ttm_base_object base;
struct vmw_stream stream;
};
static inline struct vmw_dma_buffer *
vmw_dma_buffer(struct ttm_buffer_object *bo)
{
return container_of(bo, struct vmw_dma_buffer, base);
}
static inline struct vmw_user_dma_buffer *
vmw_user_dma_buffer(struct ttm_buffer_object *bo)
{
struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
return container_of(vmw_bo, struct vmw_user_dma_buffer, dma);
}
struct vmw_resource *vmw_resource_reference(struct vmw_resource *res)
{
kref_get(&res->kref);
return res;
}
static void vmw_resource_release(struct kref *kref)
{
struct vmw_resource *res =
container_of(kref, struct vmw_resource, kref);
struct vmw_private *dev_priv = res->dev_priv;
idr_remove(res->idr, res->id);
write_unlock(&dev_priv->resource_lock);
if (likely(res->hw_destroy != NULL))
res->hw_destroy(res);
if (res->res_free != NULL)
res->res_free(res);
else
kfree(res);
write_lock(&dev_priv->resource_lock);
}
void vmw_resource_unreference(struct vmw_resource **p_res)
{
struct vmw_resource *res = *p_res;
struct vmw_private *dev_priv = res->dev_priv;
*p_res = NULL;
write_lock(&dev_priv->resource_lock);
kref_put(&res->kref, vmw_resource_release);
write_unlock(&dev_priv->resource_lock);
}
static int vmw_resource_init(struct vmw_private *dev_priv,
struct vmw_resource *res,
struct idr *idr,
enum ttm_object_type obj_type,
void (*res_free) (struct vmw_resource *res))
{
int ret;
kref_init(&res->kref);
res->hw_destroy = NULL;
res->res_free = res_free;
res->res_type = obj_type;
res->idr = idr;
res->avail = false;
res->dev_priv = dev_priv;
do {
if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
return -ENOMEM;
write_lock(&dev_priv->resource_lock);
ret = idr_get_new_above(idr, res, 1, &res->id);
write_unlock(&dev_priv->resource_lock);
} while (ret == -EAGAIN);
return ret;
}
/**
* vmw_resource_activate
*
* @res: Pointer to the newly created resource
* @hw_destroy: Destroy function. NULL if none.
*
* Activate a resource after the hardware has been made aware of it.
* Set tye destroy function to @destroy. Typically this frees the
* resource and destroys the hardware resources associated with it.
* Activate basically means that the function vmw_resource_lookup will
* find it.
*/
static void vmw_resource_activate(struct vmw_resource *res,
void (*hw_destroy) (struct vmw_resource *))
{
struct vmw_private *dev_priv = res->dev_priv;
write_lock(&dev_priv->resource_lock);
res->avail = true;
res->hw_destroy = hw_destroy;
write_unlock(&dev_priv->resource_lock);
}
struct vmw_resource *vmw_resource_lookup(struct vmw_private *dev_priv,
struct idr *idr, int id)
{
struct vmw_resource *res;
read_lock(&dev_priv->resource_lock);
res = idr_find(idr, id);
if (res && res->avail)
kref_get(&res->kref);
else
res = NULL;
read_unlock(&dev_priv->resource_lock);
if (unlikely(res == NULL))
return NULL;
return res;
}
/**
* Context management:
*/
static void vmw_hw_context_destroy(struct vmw_resource *res)
{
struct vmw_private *dev_priv = res->dev_priv;
struct {
SVGA3dCmdHeader header;
SVGA3dCmdDestroyContext body;
} *cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
if (unlikely(cmd == NULL)) {
DRM_ERROR("Failed reserving FIFO space for surface "
"destruction.\n");
return;
}
cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DESTROY);
cmd->header.size = cpu_to_le32(sizeof(cmd->body));
cmd->body.cid = cpu_to_le32(res->id);
vmw_fifo_commit(dev_priv, sizeof(*cmd));
}
static int vmw_context_init(struct vmw_private *dev_priv,
struct vmw_resource *res,
void (*res_free) (struct vmw_resource *res))
{
int ret;
struct {
SVGA3dCmdHeader header;
SVGA3dCmdDefineContext body;
} *cmd;
ret = vmw_resource_init(dev_priv, res, &dev_priv->context_idr,
VMW_RES_CONTEXT, res_free);
if (unlikely(ret != 0)) {
if (res_free == NULL)
kfree(res);
else
res_free(res);
return ret;
}
cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
if (unlikely(cmd == NULL)) {
DRM_ERROR("Fifo reserve failed.\n");
vmw_resource_unreference(&res);
return -ENOMEM;
}
cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DEFINE);
cmd->header.size = cpu_to_le32(sizeof(cmd->body));
cmd->body.cid = cpu_to_le32(res->id);
vmw_fifo_commit(dev_priv, sizeof(*cmd));
vmw_resource_activate(res, vmw_hw_context_destroy);
return 0;
}
struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv)
{
struct vmw_resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
int ret;
if (unlikely(res == NULL))
return NULL;
ret = vmw_context_init(dev_priv, res, NULL);
return (ret == 0) ? res : NULL;
}
/**
* User-space context management:
*/
static void vmw_user_context_free(struct vmw_resource *res)
{
struct vmw_user_context *ctx =
container_of(res, struct vmw_user_context, res);
kfree(ctx);
}
/**
* This function is called when user space has no more references on the
* base object. It releases the base-object's reference on the resource object.
*/
static void vmw_user_context_base_release(struct ttm_base_object **p_base)
{
struct ttm_base_object *base = *p_base;
struct vmw_user_context *ctx =
container_of(base, struct vmw_user_context, base);
struct vmw_resource *res = &ctx->res;
*p_base = NULL;
vmw_resource_unreference(&res);
}
int vmw_context_destroy_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct vmw_resource *res;
struct vmw_user_context *ctx;
struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data;
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
int ret = 0;
res = vmw_resource_lookup(dev_priv, &dev_priv->context_idr, arg->cid);
if (unlikely(res == NULL))
return -EINVAL;
if (res->res_free != &vmw_user_context_free) {
ret = -EINVAL;
goto out;
}
ctx = container_of(res, struct vmw_user_context, res);
if (ctx->base.tfile != tfile && !ctx->base.shareable) {
ret = -EPERM;
goto out;
}
ttm_ref_object_base_unref(tfile, ctx->base.hash.key, TTM_REF_USAGE);
out:
vmw_resource_unreference(&res);
return ret;
}
int vmw_context_define_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct vmw_user_context *ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
struct vmw_resource *res;
struct vmw_resource *tmp;
struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data;
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
int ret;
if (unlikely(ctx == NULL))
return -ENOMEM;
res = &ctx->res;
ctx->base.shareable = false;
ctx->base.tfile = NULL;
ret = vmw_context_init(dev_priv, res, vmw_user_context_free);
if (unlikely(ret != 0))
return ret;
tmp = vmw_resource_reference(&ctx->res);
ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT,
&vmw_user_context_base_release, NULL);
if (unlikely(ret != 0)) {
vmw_resource_unreference(&tmp);
goto out_err;
}
arg->cid = res->id;
out_err:
vmw_resource_unreference(&res);
return ret;
}
int vmw_context_check(struct vmw_private *dev_priv,
struct ttm_object_file *tfile,
int id)
{
struct vmw_resource *res;
int ret = 0;
read_lock(&dev_priv->resource_lock);
res = idr_find(&dev_priv->context_idr, id);
if (res && res->avail) {
struct vmw_user_context *ctx =
container_of(res, struct vmw_user_context, res);
if (ctx->base.tfile != tfile && !ctx->base.shareable)
ret = -EPERM;
} else
ret = -EINVAL;
read_unlock(&dev_priv->resource_lock);
return ret;
}
/**
* Surface management.
*/
static void vmw_hw_surface_destroy(struct vmw_resource *res)
{
struct vmw_private *dev_priv = res->dev_priv;
struct {
SVGA3dCmdHeader header;
SVGA3dCmdDestroySurface body;
} *cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
if (unlikely(cmd == NULL)) {
DRM_ERROR("Failed reserving FIFO space for surface "
"destruction.\n");
return;
}
cmd->header.id = cpu_to_le32(SVGA_3D_CMD_SURFACE_DESTROY);
cmd->header.size = cpu_to_le32(sizeof(cmd->body));
cmd->body.sid = cpu_to_le32(res->id);
vmw_fifo_commit(dev_priv, sizeof(*cmd));
}
void vmw_surface_res_free(struct vmw_resource *res)
{
struct vmw_surface *srf = container_of(res, struct vmw_surface, res);
kfree(srf->sizes);
kfree(srf->snooper.image);
kfree(srf);
}
int vmw_surface_init(struct vmw_private *dev_priv,
struct vmw_surface *srf,
void (*res_free) (struct vmw_resource *res))
{
int ret;
struct {
SVGA3dCmdHeader header;
SVGA3dCmdDefineSurface body;
} *cmd;
SVGA3dSize *cmd_size;
struct vmw_resource *res = &srf->res;
struct drm_vmw_size *src_size;
size_t submit_size;
uint32_t cmd_len;
int i;
BUG_ON(res_free == NULL);
ret = vmw_resource_init(dev_priv, res, &dev_priv->surface_idr,
VMW_RES_SURFACE, res_free);
if (unlikely(ret != 0)) {
res_free(res);
return ret;
}
submit_size = sizeof(*cmd) + srf->num_sizes * sizeof(SVGA3dSize);
cmd_len = sizeof(cmd->body) + srf->num_sizes * sizeof(SVGA3dSize);
cmd = vmw_fifo_reserve(dev_priv, submit_size);
if (unlikely(cmd == NULL)) {
DRM_ERROR("Fifo reserve failed for create surface.\n");
vmw_resource_unreference(&res);
return -ENOMEM;
}
cmd->header.id = cpu_to_le32(SVGA_3D_CMD_SURFACE_DEFINE);
cmd->header.size = cpu_to_le32(cmd_len);
cmd->body.sid = cpu_to_le32(res->id);
cmd->body.surfaceFlags = cpu_to_le32(srf->flags);
cmd->body.format = cpu_to_le32(srf->format);
for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) {
cmd->body.face[i].numMipLevels =
cpu_to_le32(srf->mip_levels[i]);
}
cmd += 1;
cmd_size = (SVGA3dSize *) cmd;
src_size = srf->sizes;
for (i = 0; i < srf->num_sizes; ++i, cmd_size++, src_size++) {
cmd_size->width = cpu_to_le32(src_size->width);
cmd_size->height = cpu_to_le32(src_size->height);
cmd_size->depth = cpu_to_le32(src_size->depth);
}
vmw_fifo_commit(dev_priv, submit_size);
vmw_resource_activate(res, vmw_hw_surface_destroy);
return 0;
}
static void vmw_user_surface_free(struct vmw_resource *res)
{
struct vmw_surface *srf = container_of(res, struct vmw_surface, res);
struct vmw_user_surface *user_srf =
container_of(srf, struct vmw_user_surface, srf);
kfree(srf->sizes);
kfree(srf->snooper.image);
kfree(user_srf);
}
int vmw_user_surface_lookup(struct vmw_private *dev_priv,
struct ttm_object_file *tfile,
int sid, struct vmw_surface **out)
{
struct vmw_resource *res;
struct vmw_surface *srf;
struct vmw_user_surface *user_srf;
res = vmw_resource_lookup(dev_priv, &dev_priv->surface_idr, sid);
if (unlikely(res == NULL))
return -EINVAL;
if (res->res_free != &vmw_user_surface_free)
return -EINVAL;
srf = container_of(res, struct vmw_surface, res);
user_srf = container_of(srf, struct vmw_user_surface, srf);
if (user_srf->base.tfile != tfile && !user_srf->base.shareable)
return -EPERM;
*out = srf;
return 0;
}
static void vmw_user_surface_base_release(struct ttm_base_object **p_base)
{
struct ttm_base_object *base = *p_base;
struct vmw_user_surface *user_srf =
container_of(base, struct vmw_user_surface, base);
struct vmw_resource *res = &user_srf->srf.res;
*p_base = NULL;
vmw_resource_unreference(&res);
}
int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct vmw_resource *res;
struct vmw_surface *srf;
struct vmw_user_surface *user_srf;
struct drm_vmw_surface_arg *arg = (struct drm_vmw_surface_arg *)data;
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
int ret = 0;
res = vmw_resource_lookup(dev_priv, &dev_priv->surface_idr, arg->sid);
if (unlikely(res == NULL))
return -EINVAL;
if (res->res_free != &vmw_user_surface_free) {
ret = -EINVAL;
goto out;
}
srf = container_of(res, struct vmw_surface, res);
user_srf = container_of(srf, struct vmw_user_surface, srf);
if (user_srf->base.tfile != tfile && !user_srf->base.shareable) {
ret = -EPERM;
goto out;
}
ttm_ref_object_base_unref(tfile, user_srf->base.hash.key,
TTM_REF_USAGE);
out:
vmw_resource_unreference(&res);
return ret;
}
int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct vmw_user_surface *user_srf =
kmalloc(sizeof(*user_srf), GFP_KERNEL);
struct vmw_surface *srf;
struct vmw_resource *res;
struct vmw_resource *tmp;
union drm_vmw_surface_create_arg *arg =
(union drm_vmw_surface_create_arg *)data;
struct drm_vmw_surface_create_req *req = &arg->req;
struct drm_vmw_surface_arg *rep = &arg->rep;
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
struct drm_vmw_size __user *user_sizes;
int ret;
int i;
if (unlikely(user_srf == NULL))
return -ENOMEM;
srf = &user_srf->srf;
res = &srf->res;
srf->flags = req->flags;
srf->format = req->format;
memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels));
srf->num_sizes = 0;
for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i)
srf->num_sizes += srf->mip_levels[i];
if (srf->num_sizes > DRM_VMW_MAX_SURFACE_FACES *
DRM_VMW_MAX_MIP_LEVELS) {
ret = -EINVAL;
goto out_err0;
}
srf->sizes = kmalloc(srf->num_sizes * sizeof(*srf->sizes), GFP_KERNEL);
if (unlikely(srf->sizes == NULL)) {
ret = -ENOMEM;
goto out_err0;
}
user_sizes = (struct drm_vmw_size __user *)(unsigned long)
req->size_addr;
ret = copy_from_user(srf->sizes, user_sizes,
srf->num_sizes * sizeof(*srf->sizes));
if (unlikely(ret != 0))
goto out_err1;
user_srf->base.shareable = false;
user_srf->base.tfile = NULL;
/**
* From this point, the generic resource management functions
* destroy the object on failure.
*/
ret = vmw_surface_init(dev_priv, srf, vmw_user_surface_free);
if (unlikely(ret != 0))
return ret;
tmp = vmw_resource_reference(&srf->res);
ret = ttm_base_object_init(tfile, &user_srf->base,
req->shareable, VMW_RES_SURFACE,
&vmw_user_surface_base_release, NULL);
if (unlikely(ret != 0)) {
vmw_resource_unreference(&tmp);
vmw_resource_unreference(&res);
return ret;
}
if (srf->flags & (1 << 9) &&
srf->num_sizes == 1 &&
srf->sizes[0].width == 64 &&
srf->sizes[0].height == 64 &&
srf->format == SVGA3D_A8R8G8B8) {
srf->snooper.image = kmalloc(64 * 64 * 4, GFP_KERNEL);
/* clear the image */
if (srf->snooper.image)
memset(srf->snooper.image, 0x00, 64 * 64 * 4);
else
DRM_ERROR("Failed to allocate cursor_image\n");
} else {
srf->snooper.image = NULL;
}
srf->snooper.crtc = NULL;
rep->sid = res->id;
vmw_resource_unreference(&res);
return 0;
out_err1:
kfree(srf->sizes);
out_err0:
kfree(user_srf);
return ret;
}
int vmw_surface_reference_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
union drm_vmw_surface_reference_arg *arg =
(union drm_vmw_surface_reference_arg *)data;
struct drm_vmw_surface_arg *req = &arg->req;
struct drm_vmw_surface_create_req *rep = &arg->rep;
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
struct vmw_resource *res;
struct vmw_surface *srf;
struct vmw_user_surface *user_srf;
struct drm_vmw_size __user *user_sizes;
int ret;
res = vmw_resource_lookup(dev_priv, &dev_priv->surface_idr, req->sid);
if (unlikely(res == NULL))
return -EINVAL;
if (res->res_free != &vmw_user_surface_free) {
ret = -EINVAL;
goto out;
}
srf = container_of(res, struct vmw_surface, res);
user_srf = container_of(srf, struct vmw_user_surface, srf);
if (user_srf->base.tfile != tfile && !user_srf->base.shareable) {
DRM_ERROR("Tried to reference none shareable surface\n");
ret = -EPERM;
goto out;
}
ret = ttm_ref_object_add(tfile, &user_srf->base, TTM_REF_USAGE, NULL);
if (unlikely(ret != 0)) {
DRM_ERROR("Could not add a reference to a surface.\n");
goto out;
}
rep->flags = srf->flags;
rep->format = srf->format;
memcpy(rep->mip_levels, srf->mip_levels, sizeof(srf->mip_levels));
user_sizes = (struct drm_vmw_size __user *)(unsigned long)
rep->size_addr;
if (user_sizes)
ret = copy_to_user(user_sizes, srf->sizes,
srf->num_sizes * sizeof(*srf->sizes));
if (unlikely(ret != 0)) {
DRM_ERROR("copy_to_user failed %p %u\n",
user_sizes, srf->num_sizes);
/**
* FIXME: Unreference surface here?
*/
goto out;
}
out:
vmw_resource_unreference(&res);
return ret;
}
int vmw_surface_check(struct vmw_private *dev_priv,
struct ttm_object_file *tfile,
int id)
{
struct vmw_resource *res;
int ret = 0;
read_lock(&dev_priv->resource_lock);
res = idr_find(&dev_priv->surface_idr, id);
if (res && res->avail) {
struct vmw_surface *srf =
container_of(res, struct vmw_surface, res);
struct vmw_user_surface *usrf =
container_of(srf, struct vmw_user_surface, srf);
if (usrf->base.tfile != tfile && !usrf->base.shareable)
ret = -EPERM;
} else
ret = -EINVAL;
read_unlock(&dev_priv->resource_lock);
return ret;
}
/**
* Buffer management.
*/
static size_t vmw_dmabuf_acc_size(struct ttm_bo_global *glob,
unsigned long num_pages)
{
static size_t bo_user_size = ~0;
size_t page_array_size =
(num_pages * sizeof(void *) + PAGE_SIZE - 1) & PAGE_MASK;
if (unlikely(bo_user_size == ~0)) {
bo_user_size = glob->ttm_bo_extra_size +
ttm_round_pot(sizeof(struct vmw_dma_buffer));
}
return bo_user_size + page_array_size;
}
void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo)
{
struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
struct ttm_bo_global *glob = bo->glob;
struct vmw_private *dev_priv =
container_of(bo->bdev, struct vmw_private, bdev);
ttm_mem_global_free(glob->mem_glob, bo->acc_size);
if (vmw_bo->gmr_bound) {
vmw_gmr_unbind(dev_priv, vmw_bo->gmr_id);
spin_lock(&glob->lru_lock);
ida_remove(&dev_priv->gmr_ida, vmw_bo->gmr_id);
spin_unlock(&glob->lru_lock);
}
kfree(vmw_bo);
}
int vmw_dmabuf_init(struct vmw_private *dev_priv,
struct vmw_dma_buffer *vmw_bo,
size_t size, struct ttm_placement *placement,
bool interruptible,
void (*bo_free) (struct ttm_buffer_object *bo))
{
struct ttm_bo_device *bdev = &dev_priv->bdev;
struct ttm_mem_global *mem_glob = bdev->glob->mem_glob;
size_t acc_size;
int ret;
BUG_ON(!bo_free);
acc_size =
vmw_dmabuf_acc_size(bdev->glob,
(size + PAGE_SIZE - 1) >> PAGE_SHIFT);
ret = ttm_mem_global_alloc(mem_glob, acc_size, false, false);
if (unlikely(ret != 0)) {
/* we must free the bo here as
* ttm_buffer_object_init does so as well */
bo_free(&vmw_bo->base);
return ret;
}
memset(vmw_bo, 0, sizeof(*vmw_bo));
INIT_LIST_HEAD(&vmw_bo->gmr_lru);
INIT_LIST_HEAD(&vmw_bo->validate_list);
vmw_bo->gmr_id = 0;
vmw_bo->gmr_bound = false;
ret = ttm_bo_init(bdev, &vmw_bo->base, size,
ttm_bo_type_device, placement,
0, 0, interruptible,
NULL, acc_size, bo_free);
return ret;
}
static void vmw_user_dmabuf_destroy(struct ttm_buffer_object *bo)
{
struct vmw_user_dma_buffer *vmw_user_bo = vmw_user_dma_buffer(bo);
struct vmw_dma_buffer *vmw_bo = &vmw_user_bo->dma;
struct ttm_bo_global *glob = bo->glob;
struct vmw_private *dev_priv =
container_of(bo->bdev, struct vmw_private, bdev);
ttm_mem_global_free(glob->mem_glob, bo->acc_size);
if (vmw_bo->gmr_bound) {
vmw_gmr_unbind(dev_priv, vmw_bo->gmr_id);
spin_lock(&glob->lru_lock);
ida_remove(&dev_priv->gmr_ida, vmw_bo->gmr_id);
spin_unlock(&glob->lru_lock);
}
kfree(vmw_user_bo);
}
static void vmw_user_dmabuf_release(struct ttm_base_object **p_base)
{
struct vmw_user_dma_buffer *vmw_user_bo;
struct ttm_base_object *base = *p_base;
struct ttm_buffer_object *bo;
*p_base = NULL;
if (unlikely(base == NULL))
return;
vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, base);
bo = &vmw_user_bo->dma.base;
ttm_bo_unref(&bo);
}
int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
union drm_vmw_alloc_dmabuf_arg *arg =
(union drm_vmw_alloc_dmabuf_arg *)data;
struct drm_vmw_alloc_dmabuf_req *req = &arg->req;
struct drm_vmw_dmabuf_rep *rep = &arg->rep;
struct vmw_user_dma_buffer *vmw_user_bo;
struct ttm_buffer_object *tmp;
struct vmw_master *vmaster = vmw_master(file_priv->master);
int ret;
vmw_user_bo = kzalloc(sizeof(*vmw_user_bo), GFP_KERNEL);
if (unlikely(vmw_user_bo == NULL))
return -ENOMEM;
ret = ttm_read_lock(&vmaster->lock, true);
if (unlikely(ret != 0)) {
kfree(vmw_user_bo);
return ret;
}
ret = vmw_dmabuf_init(dev_priv, &vmw_user_bo->dma, req->size,
&vmw_vram_placement, true,
&vmw_user_dmabuf_destroy);
if (unlikely(ret != 0))
return ret;
tmp = ttm_bo_reference(&vmw_user_bo->dma.base);
ret = ttm_base_object_init(vmw_fpriv(file_priv)->tfile,
&vmw_user_bo->base,
false,
ttm_buffer_type,
&vmw_user_dmabuf_release, NULL);
if (unlikely(ret != 0)) {
ttm_bo_unref(&tmp);
} else {
rep->handle = vmw_user_bo->base.hash.key;
rep->map_handle = vmw_user_bo->dma.base.addr_space_offset;
rep->cur_gmr_id = vmw_user_bo->base.hash.key;
rep->cur_gmr_offset = 0;
}
ttm_bo_unref(&tmp);
ttm_read_unlock(&vmaster->lock);
return 0;
}
int vmw_dmabuf_unref_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_vmw_unref_dmabuf_arg *arg =
(struct drm_vmw_unref_dmabuf_arg *)data;
return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile,
arg->handle,
TTM_REF_USAGE);
}
uint32_t vmw_dmabuf_validate_node(struct ttm_buffer_object *bo,
uint32_t cur_validate_node)
{
struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
if (likely(vmw_bo->on_validate_list))
return vmw_bo->cur_validate_node;
vmw_bo->cur_validate_node = cur_validate_node;
vmw_bo->on_validate_list = true;
return cur_validate_node;
}
void vmw_dmabuf_validate_clear(struct ttm_buffer_object *bo)
{
struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
vmw_bo->on_validate_list = false;
}
uint32_t vmw_dmabuf_gmr(struct ttm_buffer_object *bo)
{
struct vmw_dma_buffer *vmw_bo;
if (bo->mem.mem_type == TTM_PL_VRAM)
return SVGA_GMR_FRAMEBUFFER;
vmw_bo = vmw_dma_buffer(bo);
return (vmw_bo->gmr_bound) ? vmw_bo->gmr_id : SVGA_GMR_NULL;
}
void vmw_dmabuf_set_gmr(struct ttm_buffer_object *bo, uint32_t id)
{
struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
vmw_bo->gmr_bound = true;
vmw_bo->gmr_id = id;
}
int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,
uint32_t handle, struct vmw_dma_buffer **out)
{
struct vmw_user_dma_buffer *vmw_user_bo;
struct ttm_base_object *base;
base = ttm_base_object_lookup(tfile, handle);
if (unlikely(base == NULL)) {
printk(KERN_ERR "Invalid buffer object handle 0x%08lx.\n",
(unsigned long)handle);
return -ESRCH;
}
if (unlikely(base->object_type != ttm_buffer_type)) {
ttm_base_object_unref(&base);
printk(KERN_ERR "Invalid buffer object handle 0x%08lx.\n",
(unsigned long)handle);
return -EINVAL;
}
vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, base);
(void)ttm_bo_reference(&vmw_user_bo->dma.base);
ttm_base_object_unref(&base);
*out = &vmw_user_bo->dma;
return 0;
}
/**
* TODO: Implement a gmr id eviction mechanism. Currently we just fail
* when we're out of ids, causing GMR space to be allocated
* out of VRAM.
*/
int vmw_gmr_id_alloc(struct vmw_private *dev_priv, uint32_t *p_id)
{
struct ttm_bo_global *glob = dev_priv->bdev.glob;
int id;
int ret;
do {
if (unlikely(ida_pre_get(&dev_priv->gmr_ida, GFP_KERNEL) == 0))
return -ENOMEM;
spin_lock(&glob->lru_lock);
ret = ida_get_new(&dev_priv->gmr_ida, &id);
spin_unlock(&glob->lru_lock);
} while (ret == -EAGAIN);
if (unlikely(ret != 0))
return ret;
if (unlikely(id >= dev_priv->max_gmr_ids)) {
spin_lock(&glob->lru_lock);
ida_remove(&dev_priv->gmr_ida, id);
spin_unlock(&glob->lru_lock);
return -EBUSY;
}
*p_id = (uint32_t) id;
return 0;
}
/*
* Stream managment
*/
static void vmw_stream_destroy(struct vmw_resource *res)
{
struct vmw_private *dev_priv = res->dev_priv;
struct vmw_stream *stream;
int ret;
DRM_INFO("%s: unref\n", __func__);
stream = container_of(res, struct vmw_stream, res);
ret = vmw_overlay_unref(dev_priv, stream->stream_id);
WARN_ON(ret != 0);
}
static int vmw_stream_init(struct vmw_private *dev_priv,
struct vmw_stream *stream,
void (*res_free) (struct vmw_resource *res))
{
struct vmw_resource *res = &stream->res;
int ret;
ret = vmw_resource_init(dev_priv, res, &dev_priv->stream_idr,
VMW_RES_STREAM, res_free);
if (unlikely(ret != 0)) {
if (res_free == NULL)
kfree(stream);
else
res_free(&stream->res);
return ret;
}
ret = vmw_overlay_claim(dev_priv, &stream->stream_id);
if (ret) {
vmw_resource_unreference(&res);
return ret;
}
DRM_INFO("%s: claimed\n", __func__);
vmw_resource_activate(&stream->res, vmw_stream_destroy);
return 0;
}
/**
* User-space context management:
*/
static void vmw_user_stream_free(struct vmw_resource *res)
{
struct vmw_user_stream *stream =
container_of(res, struct vmw_user_stream, stream.res);
kfree(stream);
}
/**
* This function is called when user space has no more references on the
* base object. It releases the base-object's reference on the resource object.
*/
static void vmw_user_stream_base_release(struct ttm_base_object **p_base)
{
struct ttm_base_object *base = *p_base;
struct vmw_user_stream *stream =
container_of(base, struct vmw_user_stream, base);
struct vmw_resource *res = &stream->stream.res;
*p_base = NULL;
vmw_resource_unreference(&res);
}
int vmw_stream_unref_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct vmw_resource *res;
struct vmw_user_stream *stream;
struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data;
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
int ret = 0;
res = vmw_resource_lookup(dev_priv, &dev_priv->stream_idr, arg->stream_id);
if (unlikely(res == NULL))
return -EINVAL;
if (res->res_free != &vmw_user_stream_free) {
ret = -EINVAL;
goto out;
}
stream = container_of(res, struct vmw_user_stream, stream.res);
if (stream->base.tfile != tfile) {
ret = -EINVAL;
goto out;
}
ttm_ref_object_base_unref(tfile, stream->base.hash.key, TTM_REF_USAGE);
out:
vmw_resource_unreference(&res);
return ret;
}
int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct vmw_user_stream *stream = kmalloc(sizeof(*stream), GFP_KERNEL);
struct vmw_resource *res;
struct vmw_resource *tmp;
struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data;
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
int ret;
if (unlikely(stream == NULL))
return -ENOMEM;
res = &stream->stream.res;
stream->base.shareable = false;
stream->base.tfile = NULL;
ret = vmw_stream_init(dev_priv, &stream->stream, vmw_user_stream_free);
if (unlikely(ret != 0))
return ret;
tmp = vmw_resource_reference(res);
ret = ttm_base_object_init(tfile, &stream->base, false, VMW_RES_STREAM,
&vmw_user_stream_base_release, NULL);
if (unlikely(ret != 0)) {
vmw_resource_unreference(&tmp);
goto out_err;
}
arg->stream_id = res->id;
out_err:
vmw_resource_unreference(&res);
return ret;
}
int vmw_user_stream_lookup(struct vmw_private *dev_priv,
struct ttm_object_file *tfile,
uint32_t *inout_id, struct vmw_resource **out)
{
struct vmw_user_stream *stream;
struct vmw_resource *res;
int ret;
res = vmw_resource_lookup(dev_priv, &dev_priv->stream_idr, *inout_id);
if (unlikely(res == NULL))
return -EINVAL;
if (res->res_free != &vmw_user_stream_free) {
ret = -EINVAL;
goto err_ref;
}
stream = container_of(res, struct vmw_user_stream, stream.res);
if (stream->base.tfile != tfile) {
ret = -EPERM;
goto err_ref;
}
*inout_id = stream->stream.stream_id;
*out = res;
return 0;
err_ref:
vmw_resource_unreference(&res);
return ret;
}
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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 "drmP.h"
#include "vmwgfx_drv.h"
int vmw_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct drm_file *file_priv;
struct vmw_private *dev_priv;
if (unlikely(vma->vm_pgoff < VMWGFX_FILE_PAGE_OFFSET)) {
if (vmw_fifo_mmap(filp, vma) == 0)
return 0;
return drm_mmap(filp, vma);
}
file_priv = (struct drm_file *)filp->private_data;
dev_priv = vmw_priv(file_priv->minor->dev);
return ttm_bo_mmap(filp, vma, &dev_priv->bdev);
}
static int vmw_ttm_mem_global_init(struct ttm_global_reference *ref)
{
DRM_INFO("global init.\n");
return ttm_mem_global_init(ref->object);
}
static void vmw_ttm_mem_global_release(struct ttm_global_reference *ref)
{
ttm_mem_global_release(ref->object);
}
int vmw_ttm_global_init(struct vmw_private *dev_priv)
{
struct ttm_global_reference *global_ref;
int ret;
global_ref = &dev_priv->mem_global_ref;
global_ref->global_type = TTM_GLOBAL_TTM_MEM;
global_ref->size = sizeof(struct ttm_mem_global);
global_ref->init = &vmw_ttm_mem_global_init;
global_ref->release = &vmw_ttm_mem_global_release;
ret = ttm_global_item_ref(global_ref);
if (unlikely(ret != 0)) {
DRM_ERROR("Failed setting up TTM memory accounting.\n");
return ret;
}
dev_priv->bo_global_ref.mem_glob =
dev_priv->mem_global_ref.object;
global_ref = &dev_priv->bo_global_ref.ref;
global_ref->global_type = TTM_GLOBAL_TTM_BO;
global_ref->size = sizeof(struct ttm_bo_global);
global_ref->init = &ttm_bo_global_init;
global_ref->release = &ttm_bo_global_release;
ret = ttm_global_item_ref(global_ref);
if (unlikely(ret != 0)) {
DRM_ERROR("Failed setting up TTM buffer objects.\n");
goto out_no_bo;
}
return 0;
out_no_bo:
ttm_global_item_unref(&dev_priv->mem_global_ref);
return ret;
}
void vmw_ttm_global_release(struct vmw_private *dev_priv)
{
ttm_global_item_unref(&dev_priv->bo_global_ref.ref);
ttm_global_item_unref(&dev_priv->mem_global_ref);
}
...@@ -101,6 +101,8 @@ source "drivers/staging/p9auth/Kconfig" ...@@ -101,6 +101,8 @@ source "drivers/staging/p9auth/Kconfig"
source "drivers/staging/line6/Kconfig" source "drivers/staging/line6/Kconfig"
source "drivers/gpu/drm/vmwgfx/Kconfig"
source "drivers/gpu/drm/radeon/Kconfig" source "drivers/gpu/drm/radeon/Kconfig"
source "drivers/gpu/drm/nouveau/Kconfig" source "drivers/gpu/drm/nouveau/Kconfig"
......
...@@ -7,5 +7,6 @@ unifdef-y += r128_drm.h ...@@ -7,5 +7,6 @@ unifdef-y += r128_drm.h
unifdef-y += radeon_drm.h unifdef-y += radeon_drm.h
unifdef-y += sis_drm.h unifdef-y += sis_drm.h
unifdef-y += savage_drm.h unifdef-y += savage_drm.h
unifdef-y += vmwgfx_drm.h
unifdef-y += via_drm.h unifdef-y += via_drm.h
unifdef-y += nouveau_drm.h unifdef-y += nouveau_drm.h
...@@ -77,7 +77,11 @@ enum ttm_object_type { ...@@ -77,7 +77,11 @@ enum ttm_object_type {
ttm_buffer_type, ttm_buffer_type,
ttm_lock_type, ttm_lock_type,
ttm_driver_type0 = 256, ttm_driver_type0 = 256,
ttm_driver_type1 ttm_driver_type1,
ttm_driver_type2,
ttm_driver_type3,
ttm_driver_type4,
ttm_driver_type5
}; };
struct ttm_object_file; struct ttm_object_file;
......
/**************************************************************************
*
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* 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.
*
**************************************************************************/
#ifndef __VMWGFX_DRM_H__
#define __VMWGFX_DRM_H__
#define DRM_VMW_MAX_SURFACE_FACES 6
#define DRM_VMW_MAX_MIP_LEVELS 24
#define DRM_VMW_EXT_NAME_LEN 128
#define DRM_VMW_GET_PARAM 0
#define DRM_VMW_ALLOC_DMABUF 1
#define DRM_VMW_UNREF_DMABUF 2
#define DRM_VMW_CURSOR_BYPASS 3
/* guarded by DRM_VMW_PARAM_NUM_STREAMS != 0*/
#define DRM_VMW_CONTROL_STREAM 4
#define DRM_VMW_CLAIM_STREAM 5
#define DRM_VMW_UNREF_STREAM 6
/* guarded by DRM_VMW_PARAM_3D == 1 */
#define DRM_VMW_CREATE_CONTEXT 7
#define DRM_VMW_UNREF_CONTEXT 8
#define DRM_VMW_CREATE_SURFACE 9
#define DRM_VMW_UNREF_SURFACE 10
#define DRM_VMW_REF_SURFACE 11
#define DRM_VMW_EXECBUF 12
#define DRM_VMW_FIFO_DEBUG 13
#define DRM_VMW_FENCE_WAIT 14
/*************************************************************************/
/**
* DRM_VMW_GET_PARAM - get device information.
*
* DRM_VMW_PARAM_FIFO_OFFSET:
* Offset to use to map the first page of the FIFO read-only.
* The fifo is mapped using the mmap() system call on the drm device.
*
* DRM_VMW_PARAM_OVERLAY_IOCTL:
* Does the driver support the overlay ioctl.
*/
#define DRM_VMW_PARAM_NUM_STREAMS 0
#define DRM_VMW_PARAM_NUM_FREE_STREAMS 1
#define DRM_VMW_PARAM_3D 2
#define DRM_VMW_PARAM_FIFO_OFFSET 3
/**
* struct drm_vmw_getparam_arg
*
* @value: Returned value. //Out
* @param: Parameter to query. //In.
*
* Argument to the DRM_VMW_GET_PARAM Ioctl.
*/
struct drm_vmw_getparam_arg {
uint64_t value;
uint32_t param;
uint32_t pad64;
};
/*************************************************************************/
/**
* DRM_VMW_EXTENSION - Query device extensions.
*/
/**
* struct drm_vmw_extension_rep
*
* @exists: The queried extension exists.
* @driver_ioctl_offset: Ioctl number of the first ioctl in the extension.
* @driver_sarea_offset: Offset to any space in the DRI SAREA
* used by the extension.
* @major: Major version number of the extension.
* @minor: Minor version number of the extension.
* @pl: Patch level version number of the extension.
*
* Output argument to the DRM_VMW_EXTENSION Ioctl.
*/
struct drm_vmw_extension_rep {
int32_t exists;
uint32_t driver_ioctl_offset;
uint32_t driver_sarea_offset;
uint32_t major;
uint32_t minor;
uint32_t pl;
uint32_t pad64;
};
/**
* union drm_vmw_extension_arg
*
* @extension - Ascii name of the extension to be queried. //In
* @rep - Reply as defined above. //Out
*
* Argument to the DRM_VMW_EXTENSION Ioctl.
*/
union drm_vmw_extension_arg {
char extension[DRM_VMW_EXT_NAME_LEN];
struct drm_vmw_extension_rep rep;
};
/*************************************************************************/
/**
* DRM_VMW_CREATE_CONTEXT - Create a host context.
*
* Allocates a device unique context id, and queues a create context command
* for the host. Does not wait for host completion.
*/
/**
* struct drm_vmw_context_arg
*
* @cid: Device unique context ID.
*
* Output argument to the DRM_VMW_CREATE_CONTEXT Ioctl.
* Input argument to the DRM_VMW_UNREF_CONTEXT Ioctl.
*/
struct drm_vmw_context_arg {
int32_t cid;
uint32_t pad64;
};
/*************************************************************************/
/**
* DRM_VMW_UNREF_CONTEXT - Create a host context.
*
* Frees a global context id, and queues a destroy host command for the host.
* Does not wait for host completion. The context ID can be used directly
* in the command stream and shows up as the same context ID on the host.
*/
/*************************************************************************/
/**
* DRM_VMW_CREATE_SURFACE - Create a host suface.
*
* Allocates a device unique surface id, and queues a create surface command
* for the host. Does not wait for host completion. The surface ID can be
* used directly in the command stream and shows up as the same surface
* ID on the host.
*/
/**
* struct drm_wmv_surface_create_req
*
* @flags: Surface flags as understood by the host.
* @format: Surface format as understood by the host.
* @mip_levels: Number of mip levels for each face.
* An unused face should have 0 encoded.
* @size_addr: Address of a user-space array of sruct drm_vmw_size
* cast to an uint64_t for 32-64 bit compatibility.
* The size of the array should equal the total number of mipmap levels.
* @shareable: Boolean whether other clients (as identified by file descriptors)
* may reference this surface.
*
* Input data to the DRM_VMW_CREATE_SURFACE Ioctl.
* Output data from the DRM_VMW_REF_SURFACE Ioctl.
*/
struct drm_vmw_surface_create_req {
uint32_t flags;
uint32_t format;
uint32_t mip_levels[DRM_VMW_MAX_SURFACE_FACES];
uint64_t size_addr;
int32_t shareable;
uint32_t pad64;
};
/**
* struct drm_wmv_surface_arg
*
* @sid: Surface id of created surface or surface to destroy or reference.
*
* Output data from the DRM_VMW_CREATE_SURFACE Ioctl.
* Input argument to the DRM_VMW_UNREF_SURFACE Ioctl.
* Input argument to the DRM_VMW_REF_SURFACE Ioctl.
*/
struct drm_vmw_surface_arg {
int32_t sid;
uint32_t pad64;
};
/**
* struct drm_vmw_size ioctl.
*
* @width - mip level width
* @height - mip level height
* @depth - mip level depth
*
* Description of a mip level.
* Input data to the DRM_WMW_CREATE_SURFACE Ioctl.
*/
struct drm_vmw_size {
uint32_t width;
uint32_t height;
uint32_t depth;
uint32_t pad64;
};
/**
* union drm_vmw_surface_create_arg
*
* @rep: Output data as described above.
* @req: Input data as described above.
*
* Argument to the DRM_VMW_CREATE_SURFACE Ioctl.
*/
union drm_vmw_surface_create_arg {
struct drm_vmw_surface_arg rep;
struct drm_vmw_surface_create_req req;
};
/*************************************************************************/
/**
* DRM_VMW_REF_SURFACE - Reference a host surface.
*
* Puts a reference on a host surface with a give sid, as previously
* returned by the DRM_VMW_CREATE_SURFACE ioctl.
* A reference will make sure the surface isn't destroyed while we hold
* it and will allow the calling client to use the surface ID in the command
* stream.
*
* On successful return, the Ioctl returns the surface information given
* in the DRM_VMW_CREATE_SURFACE ioctl.
*/
/**
* union drm_vmw_surface_reference_arg
*
* @rep: Output data as described above.
* @req: Input data as described above.
*
* Argument to the DRM_VMW_REF_SURFACE Ioctl.
*/
union drm_vmw_surface_reference_arg {
struct drm_vmw_surface_create_req rep;
struct drm_vmw_surface_arg req;
};
/*************************************************************************/
/**
* DRM_VMW_UNREF_SURFACE - Unreference a host surface.
*
* Clear a reference previously put on a host surface.
* When all references are gone, including the one implicitly placed
* on creation,
* a destroy surface command will be queued for the host.
* Does not wait for completion.
*/
/*************************************************************************/
/**
* DRM_VMW_EXECBUF
*
* Submit a command buffer for execution on the host, and return a
* fence sequence that when signaled, indicates that the command buffer has
* executed.
*/
/**
* struct drm_vmw_execbuf_arg
*
* @commands: User-space address of a command buffer cast to an uint64_t.
* @command-size: Size in bytes of the command buffer.
* @fence_rep: User-space address of a struct drm_vmw_fence_rep cast to an
* uint64_t.
*
* Argument to the DRM_VMW_EXECBUF Ioctl.
*/
struct drm_vmw_execbuf_arg {
uint64_t commands;
uint32_t command_size;
uint32_t pad64;
uint64_t fence_rep;
};
/**
* struct drm_vmw_fence_rep
*
* @fence_seq: Fence sequence associated with a command submission.
* @error: This member should've been set to -EFAULT on submission.
* The following actions should be take on completion:
* error == -EFAULT: Fence communication failed. The host is synchronized.
* Use the last fence id read from the FIFO fence register.
* error != 0 && error != -EFAULT:
* Fence submission failed. The host is synchronized. Use the fence_seq member.
* error == 0: All is OK, The host may not be synchronized.
* Use the fence_seq member.
*
* Input / Output data to the DRM_VMW_EXECBUF Ioctl.
*/
struct drm_vmw_fence_rep {
uint64_t fence_seq;
int32_t error;
uint32_t pad64;
};
/*************************************************************************/
/**
* DRM_VMW_ALLOC_DMABUF
*
* Allocate a DMA buffer that is visible also to the host.
* NOTE: The buffer is
* identified by a handle and an offset, which are private to the guest, but
* useable in the command stream. The guest kernel may translate these
* and patch up the command stream accordingly. In the future, the offset may
* be zero at all times, or it may disappear from the interface before it is
* fixed.
*
* The DMA buffer may stay user-space mapped in the guest at all times,
* and is thus suitable for sub-allocation.
*
* DMA buffers are mapped using the mmap() syscall on the drm device.
*/
/**
* struct drm_vmw_alloc_dmabuf_req
*
* @size: Required minimum size of the buffer.
*
* Input data to the DRM_VMW_ALLOC_DMABUF Ioctl.
*/
struct drm_vmw_alloc_dmabuf_req {
uint32_t size;
uint32_t pad64;
};
/**
* struct drm_vmw_dmabuf_rep
*
* @map_handle: Offset to use in the mmap() call used to map the buffer.
* @handle: Handle unique to this buffer. Used for unreferencing.
* @cur_gmr_id: GMR id to use in the command stream when this buffer is
* referenced. See not above.
* @cur_gmr_offset: Offset to use in the command stream when this buffer is
* referenced. See note above.
*
* Output data from the DRM_VMW_ALLOC_DMABUF Ioctl.
*/
struct drm_vmw_dmabuf_rep {
uint64_t map_handle;
uint32_t handle;
uint32_t cur_gmr_id;
uint32_t cur_gmr_offset;
uint32_t pad64;
};
/**
* union drm_vmw_dmabuf_arg
*
* @req: Input data as described above.
* @rep: Output data as described above.
*
* Argument to the DRM_VMW_ALLOC_DMABUF Ioctl.
*/
union drm_vmw_alloc_dmabuf_arg {
struct drm_vmw_alloc_dmabuf_req req;
struct drm_vmw_dmabuf_rep rep;
};
/*************************************************************************/
/**
* DRM_VMW_UNREF_DMABUF - Free a DMA buffer.
*
*/
/**
* struct drm_vmw_unref_dmabuf_arg
*
* @handle: Handle indicating what buffer to free. Obtained from the
* DRM_VMW_ALLOC_DMABUF Ioctl.
*
* Argument to the DRM_VMW_UNREF_DMABUF Ioctl.
*/
struct drm_vmw_unref_dmabuf_arg {
uint32_t handle;
uint32_t pad64;
};
/*************************************************************************/
/**
* DRM_VMW_FIFO_DEBUG - Get last FIFO submission.
*
* This IOCTL copies the last FIFO submission directly out of the FIFO buffer.
*/
/**
* struct drm_vmw_fifo_debug_arg
*
* @debug_buffer: User space address of a debug_buffer cast to an uint64_t //In
* @debug_buffer_size: Size in bytes of debug buffer //In
* @used_size: Number of bytes copied to the buffer // Out
* @did_not_fit: Boolean indicating that the fifo contents did not fit. //Out
*
* Argument to the DRM_VMW_FIFO_DEBUG Ioctl.
*/
struct drm_vmw_fifo_debug_arg {
uint64_t debug_buffer;
uint32_t debug_buffer_size;
uint32_t used_size;
int32_t did_not_fit;
uint32_t pad64;
};
struct drm_vmw_fence_wait_arg {
uint64_t sequence;
uint64_t kernel_cookie;
int32_t cookie_valid;
int32_t pad64;
};
/*************************************************************************/
/**
* DRM_VMW_CONTROL_STREAM - Control overlays, aka streams.
*
* This IOCTL controls the overlay units of the svga device.
* The SVGA overlay units does not work like regular hardware units in
* that they do not automaticaly read back the contents of the given dma
* buffer. But instead only read back for each call to this ioctl, and
* at any point between this call being made and a following call that
* either changes the buffer or disables the stream.
*/
/**
* struct drm_vmw_rect
*
* Defines a rectangle. Used in the overlay ioctl to define
* source and destination rectangle.
*/
struct drm_vmw_rect {
int32_t x;
int32_t y;
uint32_t w;
uint32_t h;
};
/**
* struct drm_vmw_control_stream_arg
*
* @stream_id: Stearm to control
* @enabled: If false all following arguments are ignored.
* @handle: Handle to buffer for getting data from.
* @format: Format of the overlay as understood by the host.
* @width: Width of the overlay.
* @height: Height of the overlay.
* @size: Size of the overlay in bytes.
* @pitch: Array of pitches, the two last are only used for YUV12 formats.
* @offset: Offset from start of dma buffer to overlay.
* @src: Source rect, must be within the defined area above.
* @dst: Destination rect, x and y may be negative.
*
* Argument to the DRM_VMW_CONTROL_STREAM Ioctl.
*/
struct drm_vmw_control_stream_arg {
uint32_t stream_id;
uint32_t enabled;
uint32_t flags;
uint32_t color_key;
uint32_t handle;
uint32_t offset;
int32_t format;
uint32_t size;
uint32_t width;
uint32_t height;
uint32_t pitch[3];
uint32_t pad64;
struct drm_vmw_rect src;
struct drm_vmw_rect dst;
};
/*************************************************************************/
/**
* DRM_VMW_CURSOR_BYPASS - Give extra information about cursor bypass.
*
*/
#define DRM_VMW_CURSOR_BYPASS_ALL (1 << 0)
#define DRM_VMW_CURSOR_BYPASS_FLAGS (1)
/**
* struct drm_vmw_cursor_bypass_arg
*
* @flags: Flags.
* @crtc_id: Crtc id, only used if DMR_CURSOR_BYPASS_ALL isn't passed.
* @xpos: X position of cursor.
* @ypos: Y position of cursor.
* @xhot: X hotspot.
* @yhot: Y hotspot.
*
* Argument to the DRM_VMW_CURSOR_BYPASS Ioctl.
*/
struct drm_vmw_cursor_bypass_arg {
uint32_t flags;
uint32_t crtc_id;
int32_t xpos;
int32_t ypos;
int32_t xhot;
int32_t yhot;
};
/*************************************************************************/
/**
* DRM_VMW_CLAIM_STREAM - Claim a single stream.
*/
/**
* struct drm_vmw_context_arg
*
* @stream_id: Device unique context ID.
*
* Output argument to the DRM_VMW_CREATE_CONTEXT Ioctl.
* Input argument to the DRM_VMW_UNREF_CONTEXT Ioctl.
*/
struct drm_vmw_stream_arg {
uint32_t stream_id;
uint32_t pad64;
};
/*************************************************************************/
/**
* DRM_VMW_UNREF_STREAM - Unclaim a stream.
*
* Return a single stream that was claimed by this process. Also makes
* sure that the stream has been stopped.
*/
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment