diff options
Diffstat (limited to 'drivers/gpu')
24 files changed, 10812 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 470ef6779db..39c5aa75b8f 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_DRM_I830)	+= i830/  obj-$(CONFIG_DRM_I915)  += i915/  obj-$(CONFIG_DRM_SIS)   += sis/  obj-$(CONFIG_DRM_SAVAGE)+= savage/ +obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/  obj-$(CONFIG_DRM_VIA)	+=via/  obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/  obj-y			+= i2c/ diff --git a/drivers/gpu/drm/vmwgfx/Kconfig b/drivers/gpu/drm/vmwgfx/Kconfig new file mode 100644 index 00000000000..f20b8bcbef3 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/Kconfig @@ -0,0 +1,13 @@ +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 diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile new file mode 100644 index 00000000000..1a3cb6816d1 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/Makefile @@ -0,0 +1,9 @@ + +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 diff --git a/drivers/gpu/drm/vmwgfx/svga3d_reg.h b/drivers/gpu/drm/vmwgfx/svga3d_reg.h new file mode 100644 index 00000000000..77cb4533100 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/svga3d_reg.h @@ -0,0 +1,1793 @@ +/********************************************************** + * 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_ */ diff --git a/drivers/gpu/drm/vmwgfx/svga_escape.h b/drivers/gpu/drm/vmwgfx/svga_escape.h new file mode 100644 index 00000000000..7b85e9b8c85 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/svga_escape.h @@ -0,0 +1,89 @@ +/********************************************************** + * 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_ */ diff --git a/drivers/gpu/drm/vmwgfx/svga_overlay.h b/drivers/gpu/drm/vmwgfx/svga_overlay.h new file mode 100644 index 00000000000..f753d73c14b --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/svga_overlay.h @@ -0,0 +1,201 @@ +/********************************************************** + * 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_ diff --git a/drivers/gpu/drm/vmwgfx/svga_reg.h b/drivers/gpu/drm/vmwgfx/svga_reg.h new file mode 100644 index 00000000000..1b96c2ec07d --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/svga_reg.h @@ -0,0 +1,1346 @@ +/********************************************************** + * 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 diff --git a/drivers/gpu/drm/vmwgfx/svga_types.h b/drivers/gpu/drm/vmwgfx/svga_types.h new file mode 100644 index 00000000000..55836dedcfc --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/svga_types.h @@ -0,0 +1,45 @@ +/************************************************************************** + * + * 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 diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c new file mode 100644 index 00000000000..d6f2d2b882e --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c @@ -0,0 +1,229 @@ +/************************************************************************** + * + * 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 +}; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c new file mode 100644 index 00000000000..7b48bb3b63b --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -0,0 +1,735 @@ +/************************************************************************** + * + * 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"); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h new file mode 100644 index 00000000000..43546d09d1b --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -0,0 +1,511 @@ +/************************************************************************** + * + * 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 diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c new file mode 100644 index 00000000000..7a39f3e6dc2 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -0,0 +1,516 @@ +/************************************************************************** + * + * 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; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c new file mode 100644 index 00000000000..641dde76ada --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -0,0 +1,742 @@ +/************************************************************************** + * + * 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; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c new file mode 100644 index 00000000000..76b0693e245 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c @@ -0,0 +1,521 @@ +/************************************************************************** + * + * 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; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c new file mode 100644 index 00000000000..5f8908a5d7f --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c @@ -0,0 +1,213 @@ +/************************************************************************** + * + * 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); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c new file mode 100644 index 00000000000..5fa6a4ed238 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -0,0 +1,81 @@ +/************************************************************************** + * + * 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); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c new file mode 100644 index 00000000000..9e0f0306eed --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c @@ -0,0 +1,295 @@ +/************************************************************************** + * + * 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); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c new file mode 100644 index 00000000000..e9403be446f --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -0,0 +1,872 @@ +/************************************************************************** + * + * 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; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h new file mode 100644 index 00000000000..8b95249f053 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -0,0 +1,102 @@ +/************************************************************************** + * + * 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 diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c new file mode 100644 index 00000000000..90891593bf6 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -0,0 +1,516 @@ +/************************************************************************** + * + * 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; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c new file mode 100644 index 00000000000..bb6e6a096d2 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c @@ -0,0 +1,634 @@ +/************************************************************************** + * + * 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; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_reg.h b/drivers/gpu/drm/vmwgfx/vmwgfx_reg.h new file mode 100644 index 00000000000..9d0dd3a342e --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_reg.h @@ -0,0 +1,57 @@ +/************************************************************************** + * + * 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 diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c new file mode 100644 index 00000000000..a1ceed0c8e0 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -0,0 +1,1192 @@ +/************************************************************************** + * + * 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; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c new file mode 100644 index 00000000000..e3df4adfb4d --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c @@ -0,0 +1,99 @@ +/************************************************************************** + * + * 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); +}  |