diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx')
34 files changed, 16012 insertions, 2634 deletions
diff --git a/drivers/gpu/drm/vmwgfx/Kconfig b/drivers/gpu/drm/vmwgfx/Kconfig index 30ad13344f7..67720f70fe2 100644 --- a/drivers/gpu/drm/vmwgfx/Kconfig +++ b/drivers/gpu/drm/vmwgfx/Kconfig @@ -1,13 +1,25 @@  config DRM_VMWGFX  	tristate "DRM driver for VMware Virtual GPU" -	depends on DRM && PCI && FB +	depends on DRM && PCI  	select FB_DEFERRED_IO  	select FB_CFB_FILLRECT  	select FB_CFB_COPYAREA  	select FB_CFB_IMAGEBLIT  	select DRM_TTM +	# Only needed for the transitional use of drm_crtc_init - can be removed +	# again once vmwgfx sets up the primary plane itself. +	select DRM_KMS_HELPER  	help -	  KMS enabled DRM driver for SVGA2 virtual hardware. +	  Choose this option if you would like to run 3D acceleration +	  in a VMware virtual machine. +	  This is a KMS enabled DRM driver for the VMware SVGA2 +	  virtual hardware. +	  The compiled module will be called "vmwgfx.ko". + +config DRM_VMWGFX_FBCON +	depends on DRM_VMWGFX && FB +	bool "Enable framebuffer console under vmwgfx by default" +	help +	   Choose this option if you are shipping a new vmwgfx +	   userspace driver that supports using the kernel driver. -	  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 index c9281a1b1d3..458cdf6d81e 100644 --- a/drivers/gpu/drm/vmwgfx/Makefile +++ b/drivers/gpu/drm/vmwgfx/Makefile @@ -4,6 +4,8 @@ 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 vmwgfx_fence.o vmwgfx_gmrid_manager.o +	    vmwgfx_overlay.o vmwgfx_marker.o vmwgfx_gmrid_manager.o \ +	    vmwgfx_fence.o vmwgfx_dmabuf.o vmwgfx_scrn.o vmwgfx_context.o \ +	    vmwgfx_surface.o vmwgfx_prime.o vmwgfx_mob.o vmwgfx_shader.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 index 77cb4533100..f58dc7dd15c 100644 --- a/drivers/gpu/drm/vmwgfx/svga3d_reg.h +++ b/drivers/gpu/drm/vmwgfx/svga3d_reg.h @@ -34,6 +34,8 @@  #include "svga_reg.h" +typedef uint32 PPN; +typedef __le64 PPN64;  /*   * 3D Hardware Version @@ -57,7 +59,8 @@ typedef enum {     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, +   SVGA3D_HWVERSION_WS8_B1    = SVGA3D_MAKE_HWVERSION(2, 1), +   SVGA3D_HWVERSION_CURRENT   = SVGA3D_HWVERSION_WS8_B1,  } SVGA3dHardwareVersion;  /* @@ -67,7 +70,11 @@ typedef enum {  typedef uint32 SVGA3dBool; /* 32-bit Bool definition */  #define SVGA3D_NUM_CLIPPLANES                   6  #define SVGA3D_MAX_SIMULTANEOUS_RENDER_TARGETS  8 +#define SVGA3D_MAX_CONTEXT_IDS                  256 +#define SVGA3D_MAX_SURFACE_IDS                  (32 * 1024) +#define SVGA3D_NUM_TEXTURE_UNITS                32 +#define SVGA3D_NUM_LIGHTS                       8  /*   * Surface formats. @@ -79,78 +86,182 @@ typedef uint32 SVGA3dBool; /* 32-bit Bool definition */   */  typedef enum SVGA3dSurfaceFormat { -   SVGA3D_FORMAT_INVALID = 0, +   SVGA3D_FORMAT_MIN                   = 0, +   SVGA3D_FORMAT_INVALID               = 0, -   SVGA3D_X8R8G8B8       = 1, -   SVGA3D_A8R8G8B8       = 2, +   SVGA3D_X8R8G8B8                     = 1, +   SVGA3D_A8R8G8B8                     = 2, -   SVGA3D_R5G6B5         = 3, -   SVGA3D_X1R5G5B5       = 4, -   SVGA3D_A1R5G5B5       = 5, -   SVGA3D_A4R4G4B4       = 6, +   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_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_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_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_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_ARGB_S10E5                   = 24,   /* 16-bit floating-point ARGB */ +   SVGA3D_ARGB_S23E8                   = 25,   /* 32-bit floating-point ARGB */ -   SVGA3D_A2R10G10B10    = 26, +   SVGA3D_A2R10G10B10                  = 26,     /* signed formats */ -   SVGA3D_V8U8           = 27, -   SVGA3D_Q8W8V8U8       = 28, -   SVGA3D_CxV8U8         = 29, +   SVGA3D_V8U8                         = 27, +   SVGA3D_Q8W8V8U8                     = 28, +   SVGA3D_CxV8U8                       = 29,     /* mixed formats */ -   SVGA3D_X8L8V8U8       = 30, -   SVGA3D_A2W10V10U10    = 31, +   SVGA3D_X8L8V8U8                     = 30, +   SVGA3D_A2W10V10U10                  = 31, -   SVGA3D_ALPHA8         = 32, +   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, +   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_BUFFER                       = 37, -   SVGA3D_Z_D24X8        = 38, +   SVGA3D_Z_D24X8                      = 38, -   SVGA3D_V16U16         = 39, +   SVGA3D_V16U16                       = 39, -   SVGA3D_G16R16         = 40, -   SVGA3D_A16B16G16R16   = 41, +   SVGA3D_G16R16                       = 40, +   SVGA3D_A16B16G16R16                 = 41,     /* Packed Video formats */ -   SVGA3D_UYVY           = 42, -   SVGA3D_YUY2           = 43, - -   SVGA3D_FORMAT_MAX +   SVGA3D_UYVY                         = 42, +   SVGA3D_YUY2                         = 43, + +   /* Planar video formats */ +   SVGA3D_NV12                         = 44, + +   /* Video format with alpha */ +   SVGA3D_AYUV                         = 45, + +   SVGA3D_R32G32B32A32_TYPELESS        = 46, +   SVGA3D_R32G32B32A32_FLOAT           = 25, +   SVGA3D_R32G32B32A32_UINT            = 47, +   SVGA3D_R32G32B32A32_SINT            = 48, +   SVGA3D_R32G32B32_TYPELESS           = 49, +   SVGA3D_R32G32B32_FLOAT              = 50, +   SVGA3D_R32G32B32_UINT               = 51, +   SVGA3D_R32G32B32_SINT               = 52, +   SVGA3D_R16G16B16A16_TYPELESS        = 53, +   SVGA3D_R16G16B16A16_FLOAT           = 24, +   SVGA3D_R16G16B16A16_UNORM           = 41, +   SVGA3D_R16G16B16A16_UINT            = 54, +   SVGA3D_R16G16B16A16_SNORM           = 55, +   SVGA3D_R16G16B16A16_SINT            = 56, +   SVGA3D_R32G32_TYPELESS              = 57, +   SVGA3D_R32G32_FLOAT                 = 36, +   SVGA3D_R32G32_UINT                  = 58, +   SVGA3D_R32G32_SINT                  = 59, +   SVGA3D_R32G8X24_TYPELESS            = 60, +   SVGA3D_D32_FLOAT_S8X24_UINT         = 61, +   SVGA3D_R32_FLOAT_X8X24_TYPELESS     = 62, +   SVGA3D_X32_TYPELESS_G8X24_UINT      = 63, +   SVGA3D_R10G10B10A2_TYPELESS         = 64, +   SVGA3D_R10G10B10A2_UNORM            = 26, +   SVGA3D_R10G10B10A2_UINT             = 65, +   SVGA3D_R11G11B10_FLOAT              = 66, +   SVGA3D_R8G8B8A8_TYPELESS            = 67, +   SVGA3D_R8G8B8A8_UNORM               = 68, +   SVGA3D_R8G8B8A8_UNORM_SRGB          = 69, +   SVGA3D_R8G8B8A8_UINT                = 70, +   SVGA3D_R8G8B8A8_SNORM               = 28, +   SVGA3D_R8G8B8A8_SINT                = 71, +   SVGA3D_R16G16_TYPELESS              = 72, +   SVGA3D_R16G16_FLOAT                 = 35, +   SVGA3D_R16G16_UNORM                 = 40, +   SVGA3D_R16G16_UINT                  = 73, +   SVGA3D_R16G16_SNORM                 = 39, +   SVGA3D_R16G16_SINT                  = 74, +   SVGA3D_R32_TYPELESS                 = 75, +   SVGA3D_D32_FLOAT                    = 76, +   SVGA3D_R32_FLOAT                    = 34, +   SVGA3D_R32_UINT                     = 77, +   SVGA3D_R32_SINT                     = 78, +   SVGA3D_R24G8_TYPELESS               = 79, +   SVGA3D_D24_UNORM_S8_UINT            = 80, +   SVGA3D_R24_UNORM_X8_TYPELESS        = 81, +   SVGA3D_X24_TYPELESS_G8_UINT         = 82, +   SVGA3D_R8G8_TYPELESS                = 83, +   SVGA3D_R8G8_UNORM                   = 84, +   SVGA3D_R8G8_UINT                    = 85, +   SVGA3D_R8G8_SNORM                   = 27, +   SVGA3D_R8G8_SINT                    = 86, +   SVGA3D_R16_TYPELESS                 = 87, +   SVGA3D_R16_FLOAT                    = 33, +   SVGA3D_D16_UNORM                    = 8, +   SVGA3D_R16_UNORM                    = 88, +   SVGA3D_R16_UINT                     = 89, +   SVGA3D_R16_SNORM                    = 90, +   SVGA3D_R16_SINT                     = 91, +   SVGA3D_R8_TYPELESS                  = 92, +   SVGA3D_R8_UNORM                     = 93, +   SVGA3D_R8_UINT                      = 94, +   SVGA3D_R8_SNORM                     = 95, +   SVGA3D_R8_SINT                      = 96, +   SVGA3D_A8_UNORM                     = 32, +   SVGA3D_R1_UNORM                     = 97, +   SVGA3D_R9G9B9E5_SHAREDEXP           = 98, +   SVGA3D_R8G8_B8G8_UNORM              = 99, +   SVGA3D_G8R8_G8B8_UNORM              = 100, +   SVGA3D_BC1_TYPELESS                 = 101, +   SVGA3D_BC1_UNORM                    = 15, +   SVGA3D_BC1_UNORM_SRGB               = 102, +   SVGA3D_BC2_TYPELESS                 = 103, +   SVGA3D_BC2_UNORM                    = 17, +   SVGA3D_BC2_UNORM_SRGB               = 104, +   SVGA3D_BC3_TYPELESS                 = 105, +   SVGA3D_BC3_UNORM                    = 19, +   SVGA3D_BC3_UNORM_SRGB               = 106, +   SVGA3D_BC4_TYPELESS                 = 107, +   SVGA3D_BC4_UNORM                    = 108, +   SVGA3D_BC4_SNORM                    = 109, +   SVGA3D_BC5_TYPELESS                 = 110, +   SVGA3D_BC5_UNORM                    = 111, +   SVGA3D_BC5_SNORM                    = 112, +   SVGA3D_B5G6R5_UNORM                 = 3, +   SVGA3D_B5G5R5A1_UNORM               = 5, +   SVGA3D_B8G8R8A8_UNORM               = 2, +   SVGA3D_B8G8R8X8_UNORM               = 1, +   SVGA3D_R10G10B10_XR_BIAS_A2_UNORM   = 113, +   SVGA3D_B8G8R8A8_TYPELESS            = 114, +   SVGA3D_B8G8R8A8_UNORM_SRGB          = 115, +   SVGA3D_B8G8R8X8_TYPELESS            = 116, +   SVGA3D_B8G8R8X8_UNORM_SRGB          = 117, + +   /* Advanced D3D9 depth formats. */ +   SVGA3D_Z_DF16                       = 118, +   SVGA3D_Z_DF24                       = 119, +   SVGA3D_Z_D24S8_INT                  = 120, + +   /* Planar video formats. */ +   SVGA3D_YV12                         = 121, + +   SVGA3D_FORMAT_MAX                   = 122,  } SVGA3dSurfaceFormat;  typedef uint32 SVGA3dColor; /* a, r, g, b */ @@ -414,10 +525,20 @@ typedef enum {     SVGA3D_RS_SRCBLENDALPHA             = 94,    /* SVGA3dBlendOp */     SVGA3D_RS_DSTBLENDALPHA             = 95,    /* SVGA3dBlendOp */     SVGA3D_RS_BLENDEQUATIONALPHA        = 96,    /* SVGA3dBlendEquation */ +   SVGA3D_RS_TRANSPARENCYANTIALIAS     = 97,    /* SVGA3dTransparencyAntialiasType */ +   SVGA3D_RS_LINEAA                    = 98,    /* SVGA3dBool */ +   SVGA3D_RS_LINEWIDTH                 = 99,    /* float */     SVGA3D_RS_MAX  } SVGA3dRenderStateName;  typedef enum { +   SVGA3D_TRANSPARENCYANTIALIAS_NORMAL            = 0, +   SVGA3D_TRANSPARENCYANTIALIAS_ALPHATOCOVERAGE   = 1, +   SVGA3D_TRANSPARENCYANTIALIAS_SUPERSAMPLE       = 2, +   SVGA3D_TRANSPARENCYANTIALIAS_MAX +} SVGA3dTransparencyAntialiasType; + +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 */ @@ -728,10 +849,10 @@ typedef enum {     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_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; @@ -799,19 +920,19 @@ typedef enum {  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_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; @@ -819,10 +940,10 @@ typedef enum {     SVGA3D_DECLMETHOD_DEFAULT     = 0,     SVGA3D_DECLMETHOD_PARTIALU,     SVGA3D_DECLMETHOD_PARTIALV, -   SVGA3D_DECLMETHOD_CROSSUV,          // Normal +   SVGA3D_DECLMETHOD_CROSSUV,          /* Normal */     SVGA3D_DECLMETHOD_UV, -   SVGA3D_DECLMETHOD_LOOKUP,           // Lookup a displacement map -   SVGA3D_DECLMETHOD_LOOKUPPRESAMPLED, // Lookup a pre-sampled displacement map +   SVGA3D_DECLMETHOD_LOOKUP,           /* Lookup a displacement map */ +   SVGA3D_DECLMETHOD_LOOKUPPRESAMPLED, /* Lookup a pre-sampled displacement map */  } SVGA3dDeclMethod;  typedef enum { @@ -930,16 +1051,21 @@ typedef enum {  } SVGA3dCubeFace;  typedef enum { -   SVGA3D_SHADERTYPE_COMPILED_DX8               = 0, +   SVGA3D_SHADERTYPE_INVALID                    = 0, +   SVGA3D_SHADERTYPE_MIN                        = 1,     SVGA3D_SHADERTYPE_VS                         = 1,     SVGA3D_SHADERTYPE_PS                         = 2, -   SVGA3D_SHADERTYPE_MAX +   SVGA3D_SHADERTYPE_MAX                        = 3, +   SVGA3D_SHADERTYPE_GS                         = 3,  } SVGA3dShaderType; +#define SVGA3D_NUM_SHADERTYPE (SVGA3D_SHADERTYPE_MAX - SVGA3D_SHADERTYPE_MIN) +  typedef enum {     SVGA3D_CONST_TYPE_FLOAT                      = 0,     SVGA3D_CONST_TYPE_INT                        = 1,     SVGA3D_CONST_TYPE_BOOL                       = 2, +   SVGA3D_CONST_TYPE_MAX  } SVGA3dShaderConstType;  #define SVGA3D_MAX_SURFACE_FACES                6 @@ -968,12 +1094,18 @@ typedef enum {  } SVGA3dTransferType;  /* - * The maximum number vertex arrays we're guaranteed to support in + * The maximum number of vertex arrays we're guaranteed to support in   * SVGA_3D_CMD_DRAWPRIMITIVES.   */  #define SVGA3D_MAX_VERTEX_ARRAYS   32  /* + * The maximum number of primitive ranges we're guaranteed to support + * in SVGA_3D_CMD_DRAWPRIMITIVES. + */ +#define SVGA3D_MAX_DRAW_PRIMITIVE_RANGES 32 + +/*   * Identifiers for commands in the command FIFO.   *   * IDs between 1000 and 1039 (inclusive) were used by obsolete versions of @@ -990,7 +1122,7 @@ typedef enum {  #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_DEFINE         SVGA_3D_CMD_BASE + 0     /* Deprecated */  #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 @@ -1008,7 +1140,7 @@ typedef enum {  #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_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 @@ -1018,11 +1150,90 @@ typedef enum {  #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_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 +#define SVGA_3D_CMD_SURFACE_DEFINE_V2      SVGA_3D_CMD_BASE + 30 +#define SVGA_3D_CMD_GENERATE_MIPMAPS       SVGA_3D_CMD_BASE + 31 +#define SVGA_3D_CMD_ACTIVATE_SURFACE       SVGA_3D_CMD_BASE + 40 +#define SVGA_3D_CMD_DEACTIVATE_SURFACE     SVGA_3D_CMD_BASE + 41 +#define SVGA_3D_CMD_SCREEN_DMA               1082 +#define SVGA_3D_CMD_SET_UNITY_SURFACE_COOKIE 1083 +#define SVGA_3D_CMD_OPEN_CONTEXT_SURFACE     1084 + +#define SVGA_3D_CMD_LOGICOPS_BITBLT          1085 +#define SVGA_3D_CMD_LOGICOPS_TRANSBLT        1086 +#define SVGA_3D_CMD_LOGICOPS_STRETCHBLT      1087 +#define SVGA_3D_CMD_LOGICOPS_COLORFILL       1088 +#define SVGA_3D_CMD_LOGICOPS_ALPHABLEND      1089 +#define SVGA_3D_CMD_LOGICOPS_CLEARTYPEBLEND  1090 + +#define SVGA_3D_CMD_SET_OTABLE_BASE          1091 +#define SVGA_3D_CMD_READBACK_OTABLE          1092 + +#define SVGA_3D_CMD_DEFINE_GB_MOB            1093 +#define SVGA_3D_CMD_DESTROY_GB_MOB           1094 +#define SVGA_3D_CMD_REDEFINE_GB_MOB          1095 +#define SVGA_3D_CMD_UPDATE_GB_MOB_MAPPING    1096 + +#define SVGA_3D_CMD_DEFINE_GB_SURFACE        1097 +#define SVGA_3D_CMD_DESTROY_GB_SURFACE       1098 +#define SVGA_3D_CMD_BIND_GB_SURFACE          1099 +#define SVGA_3D_CMD_COND_BIND_GB_SURFACE     1100 +#define SVGA_3D_CMD_UPDATE_GB_IMAGE          1101 +#define SVGA_3D_CMD_UPDATE_GB_SURFACE        1102 +#define SVGA_3D_CMD_READBACK_GB_IMAGE        1103 +#define SVGA_3D_CMD_READBACK_GB_SURFACE      1104 +#define SVGA_3D_CMD_INVALIDATE_GB_IMAGE      1105 +#define SVGA_3D_CMD_INVALIDATE_GB_SURFACE    1106 + +#define SVGA_3D_CMD_DEFINE_GB_CONTEXT        1107 +#define SVGA_3D_CMD_DESTROY_GB_CONTEXT       1108 +#define SVGA_3D_CMD_BIND_GB_CONTEXT          1109 +#define SVGA_3D_CMD_READBACK_GB_CONTEXT      1110 +#define SVGA_3D_CMD_INVALIDATE_GB_CONTEXT    1111 + +#define SVGA_3D_CMD_DEFINE_GB_SHADER         1112 +#define SVGA_3D_CMD_DESTROY_GB_SHADER        1113 +#define SVGA_3D_CMD_BIND_GB_SHADER           1114 + +#define SVGA_3D_CMD_SET_OTABLE_BASE64        1115 + +#define SVGA_3D_CMD_BEGIN_GB_QUERY           1116 +#define SVGA_3D_CMD_END_GB_QUERY             1117 +#define SVGA_3D_CMD_WAIT_FOR_GB_QUERY        1118 + +#define SVGA_3D_CMD_NOP                      1119 + +#define SVGA_3D_CMD_ENABLE_GART              1120 +#define SVGA_3D_CMD_DISABLE_GART             1121 +#define SVGA_3D_CMD_MAP_MOB_INTO_GART        1122 +#define SVGA_3D_CMD_UNMAP_GART_RANGE         1123 + +#define SVGA_3D_CMD_DEFINE_GB_SCREENTARGET   1124 +#define SVGA_3D_CMD_DESTROY_GB_SCREENTARGET  1125 +#define SVGA_3D_CMD_BIND_GB_SCREENTARGET     1126 +#define SVGA_3D_CMD_UPDATE_GB_SCREENTARGET   1127 + +#define SVGA_3D_CMD_READBACK_GB_IMAGE_PARTIAL   1128 +#define SVGA_3D_CMD_INVALIDATE_GB_IMAGE_PARTIAL 1129 + +#define SVGA_3D_CMD_SET_GB_SHADERCONSTS_INLINE  1130 +#define SVGA_3D_CMD_GB_SCREEN_DMA               1131 +#define SVGA_3D_CMD_BIND_GB_SURFACE_WITH_PITCH  1132 +#define SVGA_3D_CMD_GB_MOB_FENCE                1133 +#define SVGA_3D_CMD_DEFINE_GB_SURFACE_V2        1134 +#define SVGA_3D_CMD_DEFINE_GB_MOB64          1135 +#define SVGA_3D_CMD_REDEFINE_GB_MOB64        1136 +#define SVGA_3D_CMD_NOP_ERROR                1137 + +#define SVGA_3D_CMD_RESERVED1                1138 +#define SVGA_3D_CMD_RESERVED2                1139 +#define SVGA_3D_CMD_RESERVED3                1140 +#define SVGA_3D_CMD_RESERVED4                1141 +#define SVGA_3D_CMD_RESERVED5                1142 + +#define SVGA_3D_CMD_MAX                      1142 +#define SVGA_3D_CMD_FUTURE_MAX               3000  /*   * Common substructures used in multiple FIFO commands: @@ -1031,9 +1242,9 @@ typedef enum {  typedef struct {     union {        struct { -         uint16  function;       // SVGA3dFogFunction -         uint8   type;           // SVGA3dFogType -         uint8   base;           // SVGA3dFogBase +         uint16  function;       /* SVGA3dFogFunction */ +         uint8   type;           /* SVGA3dFogType */ +         uint8   base;           /* SVGA3dFogBase */        };        uint32     uintValue;     }; @@ -1109,6 +1320,8 @@ typedef enum {     SVGA3D_SURFACE_HINT_RENDERTARGET    = (1 << 6),     SVGA3D_SURFACE_HINT_DEPTHSTENCIL    = (1 << 7),     SVGA3D_SURFACE_HINT_WRITEONLY       = (1 << 8), +   SVGA3D_SURFACE_MASKABLE_ANTIALIAS   = (1 << 9), +   SVGA3D_SURFACE_AUTOGENMIPMAPS       = (1 << 10),  } SVGA3dSurfaceFlags;  typedef @@ -1121,6 +1334,12 @@ struct {     uint32                      sid;     SVGA3dSurfaceFlags          surfaceFlags;     SVGA3dSurfaceFormat         format; +   /* +    * If surfaceFlags has SVGA3D_SURFACE_CUBEMAP bit set, all SVGA3dSurfaceFace +    * structures must have the same value of numMipLevels field. +    * Otherwise, all but the first SVGA3dSurfaceFace structures must have the +    * numMipLevels set to 0. +    */     SVGA3dSurfaceFace           face[SVGA3D_MAX_SURFACE_FACES];     /*      * Followed by an SVGA3dSize structure for each mip level in each face. @@ -1135,6 +1354,31 @@ struct {  typedef  struct { +   uint32                      sid; +   SVGA3dSurfaceFlags          surfaceFlags; +   SVGA3dSurfaceFormat         format; +   /* +    * If surfaceFlags has SVGA3D_SURFACE_CUBEMAP bit set, all SVGA3dSurfaceFace +    * structures must have the same value of numMipLevels field. +    * Otherwise, all but the first SVGA3dSurfaceFace structures must have the +    * numMipLevels set to 0. +    */ +   SVGA3dSurfaceFace           face[SVGA3D_MAX_SURFACE_FACES]; +   uint32                      multisampleCount; +   SVGA3dTextureFilter         autogenFilter; +   /* +    * 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_v2;     /* SVGA_3D_CMD_SURFACE_DEFINE_V2 */ + +typedef +struct {     uint32               sid;  } SVGA3dCmdDestroySurface;      /* SVGA_3D_CMD_SURFACE_DESTROY */ @@ -1474,10 +1718,12 @@ struct {      * SVGA3dCmdDrawPrimitives structure. In order,      * they are:      * -    * 1. SVGA3dVertexDecl, quantity 'numVertexDecls' -    * 2. SVGA3dPrimitiveRange, quantity 'numRanges' +    * 1. SVGA3dVertexDecl, quantity 'numVertexDecls', but no more than +    *    SVGA3D_MAX_VERTEX_ARRAYS; +    * 2. SVGA3dPrimitiveRange, quantity 'numRanges', but no more than +    *    SVGA3D_MAX_DRAW_PRIMITIVE_RANGES;      * 3. Optionally, SVGA3dVertexDivisor, quantity 'numVertexDecls' (contains -    *    the frequency divisor for this the corresponding vertex decl) +    *    the frequency divisor for the corresponding vertex decl).      */  } SVGA3dCmdDrawPrimitives;      /* SVGA_3D_CMD_DRAWPRIMITIVES */ @@ -1671,6 +1917,513 @@ struct {     /* Clipping: zero or more SVGASignedRects follow */  } SVGA3dCmdBlitSurfaceToScreen;         /* SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN */ +typedef +struct { +   uint32               sid; +   SVGA3dTextureFilter  filter; +} SVGA3dCmdGenerateMipmaps;             /* SVGA_3D_CMD_GENERATE_MIPMAPS */ + + +/* + * Guest-backed surface definitions. + */ + +typedef uint32 SVGAMobId; + +typedef enum SVGAMobFormat { +   SVGA3D_MOBFMT_INVALID = SVGA3D_INVALID_ID, +   SVGA3D_MOBFMT_PTDEPTH_0 = 0, +   SVGA3D_MOBFMT_PTDEPTH_1 = 1, +   SVGA3D_MOBFMT_PTDEPTH_2 = 2, +   SVGA3D_MOBFMT_RANGE     = 3, +   SVGA3D_MOBFMT_PTDEPTH64_0 = 4, +   SVGA3D_MOBFMT_PTDEPTH64_1 = 5, +   SVGA3D_MOBFMT_PTDEPTH64_2 = 6, +   SVGA3D_MOBFMT_MAX, +} SVGAMobFormat; + +/* + * Sizes of opaque types. + */ + +#define SVGA3D_OTABLE_MOB_ENTRY_SIZE 16 +#define SVGA3D_OTABLE_CONTEXT_ENTRY_SIZE 8 +#define SVGA3D_OTABLE_SURFACE_ENTRY_SIZE 64 +#define SVGA3D_OTABLE_SHADER_ENTRY_SIZE 16 +#define SVGA3D_OTABLE_SCREEN_TARGET_ENTRY_SIZE 64 +#define SVGA3D_CONTEXT_DATA_SIZE 16384 + +/* + * SVGA3dCmdSetOTableBase -- + * + * This command allows the guest to specify the base PPN of the + * specified object table. + */ + +typedef enum { +   SVGA_OTABLE_MOB           = 0, +   SVGA_OTABLE_MIN           = 0, +   SVGA_OTABLE_SURFACE       = 1, +   SVGA_OTABLE_CONTEXT       = 2, +   SVGA_OTABLE_SHADER        = 3, +   SVGA_OTABLE_SCREEN_TARGET = 4, +   SVGA_OTABLE_DX9_MAX       = 5, +   SVGA_OTABLE_MAX           = 8 +} SVGAOTableType; + +typedef +struct { +   SVGAOTableType type; +   PPN baseAddress; +   uint32 sizeInBytes; +   uint32 validSizeInBytes; +   SVGAMobFormat ptDepth; +} __packed +SVGA3dCmdSetOTableBase;  /* SVGA_3D_CMD_SET_OTABLE_BASE */ + +typedef +struct { +   SVGAOTableType type; +   PPN64 baseAddress; +   uint32 sizeInBytes; +   uint32 validSizeInBytes; +   SVGAMobFormat ptDepth; +} __packed +SVGA3dCmdSetOTableBase64;  /* SVGA_3D_CMD_SET_OTABLE_BASE64 */ + +typedef +struct { +   SVGAOTableType type; +} __packed +SVGA3dCmdReadbackOTable;  /* SVGA_3D_CMD_READBACK_OTABLE */ + +/* + * Define a memory object (Mob) in the OTable. + */ + +typedef +struct SVGA3dCmdDefineGBMob { +   SVGAMobId mobid; +   SVGAMobFormat ptDepth; +   PPN base; +   uint32 sizeInBytes; +} __packed +SVGA3dCmdDefineGBMob;   /* SVGA_3D_CMD_DEFINE_GB_MOB */ + + +/* + * Destroys an object in the OTable. + */ + +typedef +struct SVGA3dCmdDestroyGBMob { +   SVGAMobId mobid; +} __packed +SVGA3dCmdDestroyGBMob;   /* SVGA_3D_CMD_DESTROY_GB_MOB */ + +/* + * Redefine an object in the OTable. + */ + +typedef +struct SVGA3dCmdRedefineGBMob { +   SVGAMobId mobid; +   SVGAMobFormat ptDepth; +   PPN base; +   uint32 sizeInBytes; +} __packed +SVGA3dCmdRedefineGBMob;   /* SVGA_3D_CMD_REDEFINE_GB_MOB */ + +/* + * Define a memory object (Mob) in the OTable with a PPN64 base. + */ + +typedef +struct SVGA3dCmdDefineGBMob64 { +   SVGAMobId mobid; +   SVGAMobFormat ptDepth; +   PPN64 base; +   uint32 sizeInBytes; +} __packed +SVGA3dCmdDefineGBMob64;   /* SVGA_3D_CMD_DEFINE_GB_MOB64 */ + +/* + * Redefine an object in the OTable with PPN64 base. + */ + +typedef +struct SVGA3dCmdRedefineGBMob64 { +   SVGAMobId mobid; +   SVGAMobFormat ptDepth; +   PPN64 base; +   uint32 sizeInBytes; +} __packed +SVGA3dCmdRedefineGBMob64;   /* SVGA_3D_CMD_REDEFINE_GB_MOB64 */ + +/* + * Notification that the page tables have been modified. + */ + +typedef +struct SVGA3dCmdUpdateGBMobMapping { +   SVGAMobId mobid; +} __packed +SVGA3dCmdUpdateGBMobMapping;   /* SVGA_3D_CMD_UPDATE_GB_MOB_MAPPING */ + +/* + * Define a guest-backed surface. + */ + +typedef +struct SVGA3dCmdDefineGBSurface { +   uint32 sid; +   SVGA3dSurfaceFlags surfaceFlags; +   SVGA3dSurfaceFormat format; +   uint32 numMipLevels; +   uint32 multisampleCount; +   SVGA3dTextureFilter autogenFilter; +   SVGA3dSize size; +} __packed +SVGA3dCmdDefineGBSurface;   /* SVGA_3D_CMD_DEFINE_GB_SURFACE */ + +/* + * Destroy a guest-backed surface. + */ + +typedef +struct SVGA3dCmdDestroyGBSurface { +   uint32 sid; +} __packed +SVGA3dCmdDestroyGBSurface;   /* SVGA_3D_CMD_DESTROY_GB_SURFACE */ + +/* + * Bind a guest-backed surface to an object. + */ + +typedef +struct SVGA3dCmdBindGBSurface { +   uint32 sid; +   SVGAMobId mobid; +} __packed +SVGA3dCmdBindGBSurface;   /* SVGA_3D_CMD_BIND_GB_SURFACE */ + +/* + * Conditionally bind a mob to a guest backed surface if testMobid + * matches the currently bound mob.  Optionally issue a readback on + * the surface while it is still bound to the old mobid if the mobid + * is changed by this command. + */ + +#define SVGA3D_COND_BIND_GB_SURFACE_FLAG_READBACK (1 << 0) + +typedef +struct{ +   uint32 sid; +   SVGAMobId testMobid; +   SVGAMobId mobid; +   uint32 flags; +} __packed +SVGA3dCmdCondBindGBSurface;          /* SVGA_3D_CMD_COND_BIND_GB_SURFACE */ + +/* + * Update an image in a guest-backed surface. + * (Inform the device that the guest-contents have been updated.) + */ + +typedef +struct SVGA3dCmdUpdateGBImage { +   SVGA3dSurfaceImageId image; +   SVGA3dBox box; +} __packed +SVGA3dCmdUpdateGBImage;   /* SVGA_3D_CMD_UPDATE_GB_IMAGE */ + +/* + * Update an entire guest-backed surface. + * (Inform the device that the guest-contents have been updated.) + */ + +typedef +struct SVGA3dCmdUpdateGBSurface { +   uint32 sid; +} __packed +SVGA3dCmdUpdateGBSurface;   /* SVGA_3D_CMD_UPDATE_GB_SURFACE */ + +/* + * Readback an image in a guest-backed surface. + * (Request the device to flush the dirty contents into the guest.) + */ + +typedef +struct SVGA3dCmdReadbackGBImage { +   SVGA3dSurfaceImageId image; +} __packed +SVGA3dCmdReadbackGBImage;   /* SVGA_3D_CMD_READBACK_GB_IMAGE*/ + +/* + * Readback an entire guest-backed surface. + * (Request the device to flush the dirty contents into the guest.) + */ + +typedef +struct SVGA3dCmdReadbackGBSurface { +   uint32 sid; +} __packed +SVGA3dCmdReadbackGBSurface;   /* SVGA_3D_CMD_READBACK_GB_SURFACE */ + +/* + * Readback a sub rect of an image in a guest-backed surface.  After + * issuing this command the driver is required to issue an update call + * of the same region before issuing any other commands that reference + * this surface or rendering is not guaranteed. + */ + +typedef +struct SVGA3dCmdReadbackGBImagePartial { +   SVGA3dSurfaceImageId image; +   SVGA3dBox box; +   uint32 invertBox; +} __packed +SVGA3dCmdReadbackGBImagePartial; /* SVGA_3D_CMD_READBACK_GB_IMAGE_PARTIAL */ + +/* + * Invalidate an image in a guest-backed surface. + * (Notify the device that the contents can be lost.) + */ + +typedef +struct SVGA3dCmdInvalidateGBImage { +   SVGA3dSurfaceImageId image; +} __packed +SVGA3dCmdInvalidateGBImage;   /* SVGA_3D_CMD_INVALIDATE_GB_IMAGE */ + +/* + * Invalidate an entire guest-backed surface. + * (Notify the device that the contents if all images can be lost.) + */ + +typedef +struct SVGA3dCmdInvalidateGBSurface { +   uint32 sid; +} __packed +SVGA3dCmdInvalidateGBSurface; /* SVGA_3D_CMD_INVALIDATE_GB_SURFACE */ + +/* + * Invalidate a sub rect of an image in a guest-backed surface.  After + * issuing this command the driver is required to issue an update call + * of the same region before issuing any other commands that reference + * this surface or rendering is not guaranteed. + */ + +typedef +struct SVGA3dCmdInvalidateGBImagePartial { +   SVGA3dSurfaceImageId image; +   SVGA3dBox box; +   uint32 invertBox; +} __packed +SVGA3dCmdInvalidateGBImagePartial; /* SVGA_3D_CMD_INVALIDATE_GB_IMAGE_PARTIAL */ + +/* + * Define a guest-backed context. + */ + +typedef +struct SVGA3dCmdDefineGBContext { +   uint32 cid; +} __packed +SVGA3dCmdDefineGBContext;   /* SVGA_3D_CMD_DEFINE_GB_CONTEXT */ + +/* + * Destroy a guest-backed context. + */ + +typedef +struct SVGA3dCmdDestroyGBContext { +   uint32 cid; +} __packed +SVGA3dCmdDestroyGBContext;   /* SVGA_3D_CMD_DESTROY_GB_CONTEXT */ + +/* + * Bind a guest-backed context. + * + * validContents should be set to 0 for new contexts, + * and 1 if this is an old context which is getting paged + * back on to the device. + * + * For new contexts, it is recommended that the driver + * issue commands to initialize all interesting state + * prior to rendering. + */ + +typedef +struct SVGA3dCmdBindGBContext { +   uint32 cid; +   SVGAMobId mobid; +   uint32 validContents; +} __packed +SVGA3dCmdBindGBContext;   /* SVGA_3D_CMD_BIND_GB_CONTEXT */ + +/* + * Readback a guest-backed context. + * (Request that the device flush the contents back into guest memory.) + */ + +typedef +struct SVGA3dCmdReadbackGBContext { +   uint32 cid; +} __packed +SVGA3dCmdReadbackGBContext;   /* SVGA_3D_CMD_READBACK_GB_CONTEXT */ + +/* + * Invalidate a guest-backed context. + */ +typedef +struct SVGA3dCmdInvalidateGBContext { +   uint32 cid; +} __packed +SVGA3dCmdInvalidateGBContext;   /* SVGA_3D_CMD_INVALIDATE_GB_CONTEXT */ + +/* + * Define a guest-backed shader. + */ + +typedef +struct SVGA3dCmdDefineGBShader { +   uint32 shid; +   SVGA3dShaderType type; +   uint32 sizeInBytes; +} __packed +SVGA3dCmdDefineGBShader;   /* SVGA_3D_CMD_DEFINE_GB_SHADER */ + +/* + * Bind a guest-backed shader. + */ + +typedef struct SVGA3dCmdBindGBShader { +   uint32 shid; +   SVGAMobId mobid; +   uint32 offsetInBytes; +} __packed +SVGA3dCmdBindGBShader;   /* SVGA_3D_CMD_BIND_GB_SHADER */ + +/* + * Destroy a guest-backed shader. + */ + +typedef struct SVGA3dCmdDestroyGBShader { +   uint32 shid; +} __packed +SVGA3dCmdDestroyGBShader;   /* SVGA_3D_CMD_DESTROY_GB_SHADER */ + +typedef +struct { +   uint32                  cid; +   uint32                  regStart; +   SVGA3dShaderType        shaderType; +   SVGA3dShaderConstType   constType; + +   /* +    * Followed by a variable number of shader constants. +    * +    * Note that FLOAT and INT constants are 4-dwords in length, while +    * BOOL constants are 1-dword in length. +    */ +} __packed +SVGA3dCmdSetGBShaderConstInline; +/* SVGA_3D_CMD_SET_GB_SHADERCONSTS_INLINE */ + +typedef +struct { +   uint32               cid; +   SVGA3dQueryType      type; +} __packed +SVGA3dCmdBeginGBQuery;           /* SVGA_3D_CMD_BEGIN_GB_QUERY */ + +typedef +struct { +   uint32               cid; +   SVGA3dQueryType      type; +   SVGAMobId mobid; +   uint32 offset; +} __packed +SVGA3dCmdEndGBQuery;                  /* SVGA_3D_CMD_END_GB_QUERY */ + + +/* + * SVGA_3D_CMD_WAIT_FOR_GB_QUERY -- + * + *    The semantics of this command are identical to the + *    SVGA_3D_CMD_WAIT_FOR_QUERY except that the results are written + *    to a Mob instead of a GMR. + */ + +typedef +struct { +   uint32               cid; +   SVGA3dQueryType      type; +   SVGAMobId mobid; +   uint32 offset; +} __packed +SVGA3dCmdWaitForGBQuery;          /* SVGA_3D_CMD_WAIT_FOR_GB_QUERY */ + +typedef +struct { +   SVGAMobId mobid; +   uint32 fbOffset; +   uint32 initalized; +} __packed +SVGA3dCmdEnableGart;              /* SVGA_3D_CMD_ENABLE_GART */ + +typedef +struct { +   SVGAMobId mobid; +   uint32 gartOffset; +} __packed +SVGA3dCmdMapMobIntoGart;          /* SVGA_3D_CMD_MAP_MOB_INTO_GART */ + + +typedef +struct { +   uint32 gartOffset; +   uint32 numPages; +} __packed +SVGA3dCmdUnmapGartRange;          /* SVGA_3D_CMD_UNMAP_GART_RANGE */ + + +/* + * Screen Targets + */ +#define SVGA_STFLAG_PRIMARY (1 << 0) + +typedef +struct { +   uint32 stid; +   uint32 width; +   uint32 height; +   int32 xRoot; +   int32 yRoot; +   uint32 flags; +} __packed +SVGA3dCmdDefineGBScreenTarget;    /* SVGA_3D_CMD_DEFINE_GB_SCREENTARGET */ + +typedef +struct { +   uint32 stid; +} __packed +SVGA3dCmdDestroyGBScreenTarget;  /* SVGA_3D_CMD_DESTROY_GB_SCREENTARGET */ + +typedef +struct { +   uint32 stid; +   SVGA3dSurfaceImageId image; +} __packed +SVGA3dCmdBindGBScreenTarget;  /* SVGA_3D_CMD_BIND_GB_SCREENTARGET */ + +typedef +struct { +   uint32 stid; +   SVGA3dBox box; +} __packed +SVGA3dCmdUpdateGBScreenTarget;  /* SVGA_3D_CMD_UPDATE_GB_SCREENTARGET */  /*   * Capability query index. @@ -1774,12 +2527,69 @@ typedef enum {     SVGA3D_DEVCAP_SURFACEFMT_A16B16G16R16           = 67,     SVGA3D_DEVCAP_SURFACEFMT_UYVY                   = 68,     SVGA3D_DEVCAP_SURFACEFMT_YUY2                   = 69, +   SVGA3D_DEVCAP_MULTISAMPLE_NONMASKABLESAMPLES    = 70, +   SVGA3D_DEVCAP_MULTISAMPLE_MASKABLESAMPLES       = 71, +   SVGA3D_DEVCAP_ALPHATOCOVERAGE                   = 72, +   SVGA3D_DEVCAP_SUPERSAMPLE                       = 73, +   SVGA3D_DEVCAP_AUTOGENMIPMAPS                    = 74, +   SVGA3D_DEVCAP_SURFACEFMT_NV12                   = 75, +   SVGA3D_DEVCAP_SURFACEFMT_AYUV                   = 76, + +   /* +    * This is the maximum number of SVGA context IDs that the guest +    * can define using SVGA_3D_CMD_CONTEXT_DEFINE. +    */ +   SVGA3D_DEVCAP_MAX_CONTEXT_IDS                   = 77, + +   /* +    * This is the maximum number of SVGA surface IDs that the guest +    * can define using SVGA_3D_CMD_SURFACE_DEFINE*. +    */ +   SVGA3D_DEVCAP_MAX_SURFACE_IDS                   = 78, + +   SVGA3D_DEVCAP_SURFACEFMT_Z_DF16                 = 79, +   SVGA3D_DEVCAP_SURFACEFMT_Z_DF24                 = 80, +   SVGA3D_DEVCAP_SURFACEFMT_Z_D24S8_INT            = 81, + +   SVGA3D_DEVCAP_SURFACEFMT_BC4_UNORM              = 82, +   SVGA3D_DEVCAP_SURFACEFMT_BC5_UNORM              = 83, + +   /* +    * Deprecated. +    */ +   SVGA3D_DEVCAP_VGPU10                            = 84, + +   /* +    * This contains several SVGA_3D_CAPS_VIDEO_DECODE elements +    * ored together, one for every type of video decoding supported. +    */ +   SVGA3D_DEVCAP_VIDEO_DECODE                      = 85, + +   /* +    * This contains several SVGA_3D_CAPS_VIDEO_PROCESS elements +    * ored together, one for every type of video processing supported. +    */ +   SVGA3D_DEVCAP_VIDEO_PROCESS                     = 86, + +   SVGA3D_DEVCAP_LINE_AA                           = 87,  /* boolean */ +   SVGA3D_DEVCAP_LINE_STIPPLE                      = 88,  /* boolean */ +   SVGA3D_DEVCAP_MAX_LINE_WIDTH                    = 89,  /* float */ +   SVGA3D_DEVCAP_MAX_AA_LINE_WIDTH                 = 90,  /* float */ + +   SVGA3D_DEVCAP_SURFACEFMT_YV12                   = 91,     /* -    * 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. +    * Does the host support the SVGA logic ops commands?      */ +   SVGA3D_DEVCAP_LOGICOPS                          = 92, + +   /* +    * What support does the host have for screen targets? +    * +    * See the SVGA3D_SCREENTARGET_CAP bits below. +    */ +   SVGA3D_DEVCAP_SCREENTARGETS                     = 93, +     SVGA3D_DEVCAP_MAX                                  /* This must be the last index. */  } SVGA3dDevCapIndex; @@ -1790,4 +2600,28 @@ typedef union {     float  f;  } SVGA3dDevCapResult; +typedef enum { +   SVGA3DCAPS_RECORD_UNKNOWN        = 0, +   SVGA3DCAPS_RECORD_DEVCAPS_MIN    = 0x100, +   SVGA3DCAPS_RECORD_DEVCAPS        = 0x100, +   SVGA3DCAPS_RECORD_DEVCAPS_MAX    = 0x1ff, +} SVGA3dCapsRecordType; + +typedef +struct SVGA3dCapsRecordHeader { +   uint32 length; +   SVGA3dCapsRecordType type; +} +SVGA3dCapsRecordHeader; + +typedef +struct SVGA3dCapsRecord { +   SVGA3dCapsRecordHeader header; +   uint32 data[1]; +} +SVGA3dCapsRecord; + + +typedef uint32 SVGA3dCapPair[2]; +  #endif /* _SVGA3D_REG_H_ */ diff --git a/drivers/gpu/drm/vmwgfx/svga3d_surfacedefs.h b/drivers/gpu/drm/vmwgfx/svga3d_surfacedefs.h new file mode 100644 index 00000000000..ef338509614 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/svga3d_surfacedefs.h @@ -0,0 +1,912 @@ +/************************************************************************** + * + * Copyright © 2008-2012 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. + * + **************************************************************************/ + +#ifdef __KERNEL__ + +#include <drm/vmwgfx_drm.h> +#define surf_size_struct struct drm_vmw_size + +#else /* __KERNEL__ */ + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(_A) (sizeof(_A) / sizeof((_A)[0])) +#endif /* ARRAY_SIZE */ + +#define DIV_ROUND_UP(x, y)  (((x) + (y) - 1) / (y)) +#define max_t(type, x, y)  ((x) > (y) ? (x) : (y)) +#define min_t(type, x, y)  ((x) < (y) ? (x) : (y)) +#define surf_size_struct SVGA3dSize +#define u32 uint32 +#define u64 uint64_t +#define U32_MAX ((u32)~0U) + +#endif /* __KERNEL__ */ + +#include "svga3d_reg.h" + +/* + * enum svga3d_block_desc describes the active data channels in a block. + * + * There can be at-most four active channels in a block: + *    1. Red, bump W, luminance and depth are stored in the first channel. + *    2. Green, bump V and stencil are stored in the second channel. + *    3. Blue and bump U are stored in the third channel. + *    4. Alpha and bump Q are stored in the fourth channel. + * + * Block channels can be used to store compressed and buffer data: + *    1. For compressed formats, only the data channel is used and its size + *       is equal to that of a singular block in the compression scheme. + *    2. For buffer formats, only the data channel is used and its size is + *       exactly one byte in length. + *    3. In each case the bit depth represent the size of a singular block. + * + * Note: Compressed and IEEE formats do not use the bitMask structure. + */ + +enum svga3d_block_desc { +	SVGA3DBLOCKDESC_NONE        = 0,         /* No channels are active */ +	SVGA3DBLOCKDESC_BLUE        = 1 << 0,    /* Block with red channel +						    data */ +	SVGA3DBLOCKDESC_U           = 1 << 0,    /* Block with bump U channel +						    data */ +	SVGA3DBLOCKDESC_UV_VIDEO    = 1 << 7,    /* Block with alternating video +						    U and V */ +	SVGA3DBLOCKDESC_GREEN       = 1 << 1,    /* Block with green channel +						    data */ +	SVGA3DBLOCKDESC_V           = 1 << 1,    /* Block with bump V channel +						    data */ +	SVGA3DBLOCKDESC_STENCIL     = 1 << 1,    /* Block with a stencil +						    channel */ +	SVGA3DBLOCKDESC_RED         = 1 << 2,    /* Block with blue channel +						    data */ +	SVGA3DBLOCKDESC_W           = 1 << 2,    /* Block with bump W channel +						    data */ +	SVGA3DBLOCKDESC_LUMINANCE   = 1 << 2,    /* Block with luminance channel +						    data */ +	SVGA3DBLOCKDESC_Y           = 1 << 2,    /* Block with video luminance +						    data */ +	SVGA3DBLOCKDESC_DEPTH       = 1 << 2,    /* Block with depth channel */ +	SVGA3DBLOCKDESC_ALPHA       = 1 << 3,    /* Block with an alpha +						    channel */ +	SVGA3DBLOCKDESC_Q           = 1 << 3,    /* Block with bump Q channel +						    data */ +	SVGA3DBLOCKDESC_BUFFER      = 1 << 4,    /* Block stores 1 byte of +						    data */ +	SVGA3DBLOCKDESC_COMPRESSED  = 1 << 5,    /* Block stores n bytes of +						    data depending on the +						    compression method used */ +	SVGA3DBLOCKDESC_IEEE_FP     = 1 << 6,    /* Block stores data in an IEEE +						    floating point +						    representation in +						    all channels */ +	SVGA3DBLOCKDESC_PLANAR_YUV  = 1 << 8,    /* Three separate blocks store +						    data. */ +	SVGA3DBLOCKDESC_U_VIDEO     = 1 << 9,    /* Block with U video data */ +	SVGA3DBLOCKDESC_V_VIDEO     = 1 << 10,   /* Block with V video data */ +	SVGA3DBLOCKDESC_EXP         = 1 << 11,   /* Shared exponent */ +	SVGA3DBLOCKDESC_SRGB        = 1 << 12,   /* Data is in sRGB format */ +	SVGA3DBLOCKDESC_2PLANAR_YUV = 1 << 13,   /* 2 planes of Y, UV, +						    e.g., NV12. */ +	SVGA3DBLOCKDESC_3PLANAR_YUV = 1 << 14,   /* 3 planes of separate +						    Y, U, V, e.g., YV12. */ + +	SVGA3DBLOCKDESC_RG         = SVGA3DBLOCKDESC_RED | +	SVGA3DBLOCKDESC_GREEN, +	SVGA3DBLOCKDESC_RGB        = SVGA3DBLOCKDESC_RG | +	SVGA3DBLOCKDESC_BLUE, +	SVGA3DBLOCKDESC_RGB_SRGB   = SVGA3DBLOCKDESC_RGB | +	SVGA3DBLOCKDESC_SRGB, +	SVGA3DBLOCKDESC_RGBA       = SVGA3DBLOCKDESC_RGB | +	SVGA3DBLOCKDESC_ALPHA, +	SVGA3DBLOCKDESC_RGBA_SRGB  = SVGA3DBLOCKDESC_RGBA | +	SVGA3DBLOCKDESC_SRGB, +	SVGA3DBLOCKDESC_UV         = SVGA3DBLOCKDESC_U | +	SVGA3DBLOCKDESC_V, +	SVGA3DBLOCKDESC_UVL        = SVGA3DBLOCKDESC_UV | +	SVGA3DBLOCKDESC_LUMINANCE, +	SVGA3DBLOCKDESC_UVW        = SVGA3DBLOCKDESC_UV | +	SVGA3DBLOCKDESC_W, +	SVGA3DBLOCKDESC_UVWA       = SVGA3DBLOCKDESC_UVW | +	SVGA3DBLOCKDESC_ALPHA, +	SVGA3DBLOCKDESC_UVWQ       = SVGA3DBLOCKDESC_U | +	SVGA3DBLOCKDESC_V | +	SVGA3DBLOCKDESC_W | +	SVGA3DBLOCKDESC_Q, +	SVGA3DBLOCKDESC_LA         = SVGA3DBLOCKDESC_LUMINANCE | +	SVGA3DBLOCKDESC_ALPHA, +	SVGA3DBLOCKDESC_R_FP       = SVGA3DBLOCKDESC_RED | +	SVGA3DBLOCKDESC_IEEE_FP, +	SVGA3DBLOCKDESC_RG_FP      = SVGA3DBLOCKDESC_R_FP | +	SVGA3DBLOCKDESC_GREEN, +	SVGA3DBLOCKDESC_RGB_FP     = SVGA3DBLOCKDESC_RG_FP | +	SVGA3DBLOCKDESC_BLUE, +	SVGA3DBLOCKDESC_RGBA_FP    = SVGA3DBLOCKDESC_RGB_FP | +	SVGA3DBLOCKDESC_ALPHA, +	SVGA3DBLOCKDESC_DS         = SVGA3DBLOCKDESC_DEPTH | +	SVGA3DBLOCKDESC_STENCIL, +	SVGA3DBLOCKDESC_YUV        = SVGA3DBLOCKDESC_UV_VIDEO | +	SVGA3DBLOCKDESC_Y, +	SVGA3DBLOCKDESC_AYUV       = SVGA3DBLOCKDESC_ALPHA | +	SVGA3DBLOCKDESC_Y | +	SVGA3DBLOCKDESC_U_VIDEO | +	SVGA3DBLOCKDESC_V_VIDEO, +	SVGA3DBLOCKDESC_RGBE       = SVGA3DBLOCKDESC_RGB | +	SVGA3DBLOCKDESC_EXP, +	SVGA3DBLOCKDESC_COMPRESSED_SRGB = SVGA3DBLOCKDESC_COMPRESSED | +	SVGA3DBLOCKDESC_SRGB, +	SVGA3DBLOCKDESC_NV12       = SVGA3DBLOCKDESC_PLANAR_YUV | +	SVGA3DBLOCKDESC_2PLANAR_YUV, +	SVGA3DBLOCKDESC_YV12       = SVGA3DBLOCKDESC_PLANAR_YUV | +	SVGA3DBLOCKDESC_3PLANAR_YUV, +}; + +/* + * SVGA3dSurfaceDesc describes the actual pixel data. + * + * This structure provides the following information: + *    1. Block description. + *    2. Dimensions of a block in the surface. + *    3. Size of block in bytes. + *    4. Bit depth of the pixel data. + *    5. Channel bit depths and masks (if applicable). + */ +#define SVGA3D_CHANNEL_DEF(type)		\ +	struct {				\ +		union {				\ +			type blue;              \ +			type u;                 \ +			type uv_video;          \ +			type u_video;           \ +		};				\ +		union {				\ +			type green;             \ +			type v;                 \ +			type stencil;           \ +			type v_video;           \ +		};				\ +		union {				\ +			type red;               \ +			type w;                 \ +			type luminance;         \ +			type y;                 \ +			type depth;             \ +			type data;              \ +		};				\ +		union {				\ +			type alpha;             \ +			type q;                 \ +			type exp;               \ +		};				\ +	} + +struct svga3d_surface_desc { +	enum svga3d_block_desc block_desc; +	surf_size_struct block_size; +	u32 bytes_per_block; +	u32 pitch_bytes_per_block; + +	struct { +		u32 total; +		SVGA3D_CHANNEL_DEF(uint8); +	} bit_depth; + +	struct { +		SVGA3D_CHANNEL_DEF(uint8); +	} bit_offset; +}; + +static const struct svga3d_surface_desc svga3d_surface_descs[] = { +	{SVGA3DBLOCKDESC_NONE, +	 {1, 1, 1},  0, 0, {0, {{0}, {0}, {0}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_FORMAT_INVALID */ + +	{SVGA3DBLOCKDESC_RGB, +	 {1, 1, 1},  4, 4, {24, {{8}, {8}, {8}, {0} } }, +	 {{{0}, {8}, {16}, {24} } } },   /* SVGA3D_X8R8G8B8 */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  4, 4, {32, {{8}, {8}, {8}, {8} } }, +	 {{{0}, {8}, {16}, {24} } } },   /* SVGA3D_A8R8G8B8 */ + +	{SVGA3DBLOCKDESC_RGB, +	 {1, 1, 1},  2, 2, {16, {{5}, {6}, {5}, {0} } }, +	 {{{0}, {5}, {11}, {0} } } },    /* SVGA3D_R5G6B5 */ + +	{SVGA3DBLOCKDESC_RGB, +	 {1, 1, 1},  2, 2, {15, {{5}, {5}, {5}, {0} } }, +	 {{{0}, {5}, {10}, {0} } } },    /* SVGA3D_X1R5G5B5 */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  2, 2, {16, {{5}, {5}, {5}, {1} } }, +	 {{{0}, {5}, {10}, {15} } } },   /* SVGA3D_A1R5G5B5 */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  2, 2, {16, {{4}, {4}, {4}, {4} } }, +	 {{{0}, {4}, {8}, {12} } } },    /* SVGA3D_A4R4G4B4 */ + +	{SVGA3DBLOCKDESC_DEPTH, +	 {1, 1, 1},  4, 4, {32, {{0}, {0}, {32}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_Z_D32 */ + +	{SVGA3DBLOCKDESC_DEPTH, +	 {1, 1, 1},  2, 2, {16, {{0}, {0}, {16}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_Z_D16 */ + +	{SVGA3DBLOCKDESC_DS, +	 {1, 1, 1},  4, 4, {32, {{0}, {8}, {24}, {0} } }, +	 {{{0}, {24}, {0}, {0} } } },    /* SVGA3D_Z_D24S8 */ + +	{SVGA3DBLOCKDESC_DS, +	 {1, 1, 1},  2, 2, {16, {{0}, {1}, {15}, {0} } }, +	 {{{0}, {15}, {0}, {0} } } },    /* SVGA3D_Z_D15S1 */ + +	{SVGA3DBLOCKDESC_LUMINANCE, +	 {1, 1, 1},  1, 1, {8, {{0}, {0}, {8}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_LUMINANCE8 */ + +	{SVGA3DBLOCKDESC_LA, +	 {1, 1, 1},  1, 1, {8, {{0}, {0}, {4}, {4} } }, +	 {{{0}, {0}, {0}, {4} } } },     /* SVGA3D_LUMINANCE4_ALPHA4 */ + +	{SVGA3DBLOCKDESC_LUMINANCE, +	 {1, 1, 1},  2, 2, {16, {{0}, {0}, {16}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_LUMINANCE16 */ + +	{SVGA3DBLOCKDESC_LA, +	 {1, 1, 1},  2, 2, {16, {{0}, {0}, {8}, {8} } }, +	 {{{0}, {0}, {0}, {8} } } },     /* SVGA3D_LUMINANCE8_ALPHA8 */ + +	{SVGA3DBLOCKDESC_COMPRESSED, +	 {4, 4, 1},  8, 8, {64, {{0}, {0}, {64}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_DXT1 */ + +	{SVGA3DBLOCKDESC_COMPRESSED, +	 {4, 4, 1},  16, 16, {128, {{0}, {0}, {128}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_DXT2 */ + +	{SVGA3DBLOCKDESC_COMPRESSED, +	 {4, 4, 1},  16, 16, {128, {{0}, {0}, {128}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_DXT3 */ + +	{SVGA3DBLOCKDESC_COMPRESSED, +	 {4, 4, 1},  16, 16, {128, {{0}, {0}, {128}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_DXT4 */ + +	{SVGA3DBLOCKDESC_COMPRESSED, +	 {4, 4, 1},  16, 16, {128, {{0}, {0}, {128}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_DXT5 */ + +	{SVGA3DBLOCKDESC_UV, +	 {1, 1, 1},  2, 2, {16, {{0}, {0}, {8}, {8} } }, +	 {{{0}, {0}, {0}, {8} } } },     /* SVGA3D_BUMPU8V8 */ + +	{SVGA3DBLOCKDESC_UVL, +	 {1, 1, 1},  2, 2, {16, {{5}, {5}, {6}, {0} } }, +	 {{{11}, {6}, {0}, {0} } } },    /* SVGA3D_BUMPL6V5U5 */ + +	{SVGA3DBLOCKDESC_UVL, +	 {1, 1, 1},  4, 4, {32, {{8}, {8}, {8}, {0} } }, +	 {{{16}, {8}, {0}, {0} } } },    /* SVGA3D_BUMPX8L8V8U8 */ + +	{SVGA3DBLOCKDESC_UVL, +	 {1, 1, 1},  3, 3, {24, {{8}, {8}, {8}, {0} } }, +	 {{{16}, {8}, {0}, {0} } } },    /* SVGA3D_BUMPL8V8U8 */ + +	{SVGA3DBLOCKDESC_RGBA_FP, +	 {1, 1, 1},  8, 8, {64, {{16}, {16}, {16}, {16} } }, +	 {{{32}, {16}, {0}, {48} } } },  /* SVGA3D_ARGB_S10E5 */ + +	{SVGA3DBLOCKDESC_RGBA_FP, +	 {1, 1, 1},  16, 16, {128, {{32}, {32}, {32}, {32} } }, +	 {{{64}, {32}, {0}, {96} } } },  /* SVGA3D_ARGB_S23E8 */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  4, 4, {32, {{10}, {10}, {10}, {2} } }, +	 {{{0}, {10}, {20}, {30} } } },  /* SVGA3D_A2R10G10B10 */ + +	{SVGA3DBLOCKDESC_UV, +	 {1, 1, 1},  2, 2, {16, {{8}, {8}, {0}, {0} } }, +	 {{{8}, {0}, {0}, {0} } } },     /* SVGA3D_V8U8 */ + +	{SVGA3DBLOCKDESC_UVWQ, +	 {1, 1, 1},  4, 4, {32, {{8}, {8}, {8}, {8} } }, +	 {{{24}, {16}, {8}, {0} } } },   /* SVGA3D_Q8W8V8U8 */ + +	{SVGA3DBLOCKDESC_UV, +	 {1, 1, 1},  2, 2, {16, {{8}, {8}, {0}, {0} } }, +	 {{{8}, {0}, {0}, {0} } } },     /* SVGA3D_CxV8U8 */ + +	{SVGA3DBLOCKDESC_UVL, +	 {1, 1, 1},  4, 4, {24, {{8}, {8}, {8}, {0} } }, +	 {{{16}, {8}, {0}, {0} } } },    /* SVGA3D_X8L8V8U8 */ + +	{SVGA3DBLOCKDESC_UVWA, +	 {1, 1, 1},  4, 4, {32, {{10}, {10}, {10}, {2} } }, +	 {{{0}, {10}, {20}, {30} } } },  /* SVGA3D_A2W10V10U10 */ + +	{SVGA3DBLOCKDESC_ALPHA, +	 {1, 1, 1},  1, 1, {8, {{0}, {0}, {0}, {8} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_ALPHA8 */ + +	{SVGA3DBLOCKDESC_R_FP, +	 {1, 1, 1},  2, 2, {16, {{0}, {0}, {16}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R_S10E5 */ + +	{SVGA3DBLOCKDESC_R_FP, +	 {1, 1, 1},  4, 4, {32, {{0}, {0}, {32}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R_S23E8 */ + +	{SVGA3DBLOCKDESC_RG_FP, +	 {1, 1, 1},  4, 4, {32, {{0}, {16}, {16}, {0} } }, +	 {{{0}, {16}, {0}, {0} } } },    /* SVGA3D_RG_S10E5 */ + +	{SVGA3DBLOCKDESC_RG_FP, +	 {1, 1, 1},  8, 8, {64, {{0}, {32}, {32}, {0} } }, +	 {{{0}, {32}, {0}, {0} } } },    /* SVGA3D_RG_S23E8 */ + +	{SVGA3DBLOCKDESC_BUFFER, +	 {1, 1, 1},  1, 1, {8, {{0}, {0}, {8}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_BUFFER */ + +	{SVGA3DBLOCKDESC_DEPTH, +	 {1, 1, 1},  4, 4, {32, {{0}, {0}, {24}, {0} } }, +	 {{{0}, {24}, {0}, {0} } } },    /* SVGA3D_Z_D24X8 */ + +	{SVGA3DBLOCKDESC_UV, +	 {1, 1, 1},  4, 4, {32, {{16}, {16}, {0}, {0} } }, +	 {{{16}, {0}, {0}, {0} } } },    /* SVGA3D_V16U16 */ + +	{SVGA3DBLOCKDESC_RG, +	 {1, 1, 1},  4, 4, {32, {{0}, {16}, {16}, {0} } }, +	 {{{0}, {0}, {16}, {0} } } },    /* SVGA3D_G16R16 */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  8, 8, {64, {{16}, {16}, {16}, {16} } }, +	 {{{32}, {16}, {0}, {48} } } },  /* SVGA3D_A16B16G16R16 */ + +	{SVGA3DBLOCKDESC_YUV, +	 {1, 1, 1},  2, 2, {16, {{8}, {0}, {8}, {0} } }, +	 {{{0}, {0}, {8}, {0} } } },     /* SVGA3D_UYVY */ + +	{SVGA3DBLOCKDESC_YUV, +	 {1, 1, 1},  2, 2, {16, {{8}, {0}, {8}, {0} } }, +	 {{{8}, {0}, {0}, {0} } } },     /* SVGA3D_YUY2 */ + +	{SVGA3DBLOCKDESC_NV12, +	 {2, 2, 1},  6, 2, {48, {{0}, {0}, {48}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_NV12 */ + +	{SVGA3DBLOCKDESC_AYUV, +	 {1, 1, 1},  4, 4, {32, {{8}, {8}, {8}, {8} } }, +	 {{{0}, {8}, {16}, {24} } } },   /* SVGA3D_AYUV */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  16, 16, {128, {{32}, {32}, {32}, {32} } }, +	 {{{64}, {32}, {0}, {96} } } },  /* SVGA3D_R32G32B32A32_TYPELESS */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  16, 16, {128, {{32}, {32}, {32}, {32} } }, +	 {{{64}, {32}, {0}, {96} } } },  /* SVGA3D_R32G32B32A32_UINT */ + +	{SVGA3DBLOCKDESC_UVWQ, +	 {1, 1, 1},  16, 16, {128, {{32}, {32}, {32}, {32} } }, +	 {{{64}, {32}, {0}, {96} } } },  /* SVGA3D_R32G32B32A32_SINT */ + +	{SVGA3DBLOCKDESC_RGB, +	 {1, 1, 1},  12, 12, {96, {{32}, {32}, {32}, {0} } }, +	 {{{64}, {32}, {0}, {0} } } },   /* SVGA3D_R32G32B32_TYPELESS */ + +	{SVGA3DBLOCKDESC_RGB_FP, +	 {1, 1, 1},  12, 12, {96, {{32}, {32}, {32}, {0} } }, +	 {{{64}, {32}, {0}, {0} } } },   /* SVGA3D_R32G32B32_FLOAT */ + +	{SVGA3DBLOCKDESC_RGB, +	 {1, 1, 1},  12, 12, {96, {{32}, {32}, {32}, {0} } }, +	 {{{64}, {32}, {0}, {0} } } },   /* SVGA3D_R32G32B32_UINT */ + +	{SVGA3DBLOCKDESC_UVW, +	 {1, 1, 1},  12, 12, {96, {{32}, {32}, {32}, {0} } }, +	 {{{64}, {32}, {0}, {0} } } },   /* SVGA3D_R32G32B32_SINT */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  8, 8, {64, {{16}, {16}, {16}, {16} } }, +	 {{{32}, {16}, {0}, {48} } } },  /* SVGA3D_R16G16B16A16_TYPELESS */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  8, 8, {64, {{16}, {16}, {16}, {16} } }, +	 {{{32}, {16}, {0}, {48} } } },  /* SVGA3D_R16G16B16A16_UINT */ + +	{SVGA3DBLOCKDESC_UVWQ, +	 {1, 1, 1},  8, 8, {64, {{16}, {16}, {16}, {16} } }, +	 {{{32}, {16}, {0}, {48} } } },  /* SVGA3D_R16G16B16A16_SNORM */ + +	{SVGA3DBLOCKDESC_UVWQ, +	 {1, 1, 1},  8, 8, {64, {{16}, {16}, {16}, {16} } }, +	 {{{32}, {16}, {0}, {48} } } },  /* SVGA3D_R16G16B16A16_SINT */ + +	{SVGA3DBLOCKDESC_RG, +	 {1, 1, 1},  8, 8, {64, {{0}, {32}, {32}, {0} } }, +	 {{{0}, {32}, {0}, {0} } } },    /* SVGA3D_R32G32_TYPELESS */ + +	{SVGA3DBLOCKDESC_RG, +	 {1, 1, 1},  8, 8, {64, {{0}, {32}, {32}, {0} } }, +	 {{{0}, {32}, {0}, {0} } } },    /* SVGA3D_R32G32_UINT */ + +	{SVGA3DBLOCKDESC_UV, +	 {1, 1, 1},  8, 8, {64, {{0}, {32}, {32}, {0} } }, +	 {{{0}, {32}, {0}, {0} } } },    /* SVGA3D_R32G32_SINT */ + +	{SVGA3DBLOCKDESC_RG, +	 {1, 1, 1},  8, 8, {64, {{0}, {8}, {32}, {0} } }, +	 {{{0}, {32}, {0}, {0} } } },    /* SVGA3D_R32G8X24_TYPELESS */ + +	{SVGA3DBLOCKDESC_DS, +	 {1, 1, 1},  8, 8, {64, {{0}, {8}, {32}, {0} } }, +	 {{{0}, {32}, {0}, {0} } } },    /* SVGA3D_D32_FLOAT_S8X24_UINT */ + +	{SVGA3DBLOCKDESC_R_FP, +	 {1, 1, 1},  8, 8, {64, {{0}, {0}, {32}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },    /* SVGA3D_R32_FLOAT_X8_X24_TYPELESS */ + +	{SVGA3DBLOCKDESC_GREEN, +	 {1, 1, 1},  8, 8, {64, {{0}, {8}, {0}, {0} } }, +	 {{{0}, {32}, {0}, {0} } } },    /* SVGA3D_X32_TYPELESS_G8X24_UINT */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  4, 4, {32, {{10}, {10}, {10}, {2} } }, +	 {{{0}, {10}, {20}, {30} } } },  /* SVGA3D_R10G10B10A2_TYPELESS */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  4, 4, {32, {{10}, {10}, {10}, {2} } }, +	 {{{0}, {10}, {20}, {30} } } },  /* SVGA3D_R10G10B10A2_UINT */ + +	{SVGA3DBLOCKDESC_RGB_FP, +	 {1, 1, 1},  4, 4, {32, {{10}, {11}, {11}, {0} } }, +	 {{{0}, {10}, {21}, {0} } } },  /* SVGA3D_R11G11B10_FLOAT */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  4, 4, {32, {{8}, {8}, {8}, {8} } }, +	 {{{16}, {8}, {0}, {24} } } },   /* SVGA3D_R8G8B8A8_TYPELESS */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  4, 4, {32, {{8}, {8}, {8}, {8} } }, +	 {{{16}, {8}, {0}, {24} } } },   /* SVGA3D_R8G8B8A8_UNORM */ + +	{SVGA3DBLOCKDESC_RGBA_SRGB, +	 {1, 1, 1},  4, 4, {32, {{8}, {8}, {8}, {8} } }, +	 {{{16}, {8}, {0}, {24} } } },   /* SVGA3D_R8G8B8A8_UNORM_SRGB */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  4, 4, {32, {{8}, {8}, {8}, {8} } }, +	 {{{16}, {8}, {0}, {24} } } },   /* SVGA3D_R8G8B8A8_UINT */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  4, 4, {32, {{8}, {8}, {8}, {8} } }, +	 {{{16}, {8}, {0}, {24} } } },   /* SVGA3D_R8G8B8A8_SINT */ + +	{SVGA3DBLOCKDESC_RG, +	 {1, 1, 1},  4, 4, {32, {{0}, {16}, {16}, {0} } }, +	 {{{0}, {16}, {0}, {0} } } },    /* SVGA3D_R16G16_TYPELESS */ + +	{SVGA3DBLOCKDESC_RG_FP, +	 {1, 1, 1},  4, 4, {32, {{0}, {16}, {16}, {0} } }, +	 {{{0}, {16}, {0}, {0} } } },    /* SVGA3D_R16G16_UINT */ + +	{SVGA3DBLOCKDESC_UV, +	 {1, 1, 1},  4, 4, {32, {{0}, {16}, {16}, {0} } }, +	 {{{0}, {16}, {0}, {0} } } },    /* SVGA3D_R16G16_SINT */ + +	{SVGA3DBLOCKDESC_RED, +	 {1, 1, 1},  4, 4, {32, {{0}, {0}, {32}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R32_TYPELESS */ + +	{SVGA3DBLOCKDESC_DEPTH, +	 {1, 1, 1},  4, 4, {32, {{0}, {0}, {32}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_D32_FLOAT */ + +	{SVGA3DBLOCKDESC_RED, +	 {1, 1, 1},  4, 4, {32, {{0}, {0}, {32}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R32_UINT */ + +	{SVGA3DBLOCKDESC_RED, +	 {1, 1, 1},  4, 4, {32, {{0}, {0}, {32}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R32_SINT */ + +	{SVGA3DBLOCKDESC_RG, +	 {1, 1, 1},  4, 4, {32, {{0}, {8}, {24}, {0} } }, +	 {{{0}, {24}, {0}, {0} } } },    /* SVGA3D_R24G8_TYPELESS */ + +	{SVGA3DBLOCKDESC_DS, +	 {1, 1, 1},  4, 4, {32, {{0}, {8}, {24}, {0} } }, +	 {{{0}, {24}, {0}, {0} } } },    /* SVGA3D_D24_UNORM_S8_UINT */ + +	{SVGA3DBLOCKDESC_RED, +	 {1, 1, 1},  4, 4, {32, {{0}, {0}, {24}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R24_UNORM_X8_TYPELESS */ + +	{SVGA3DBLOCKDESC_GREEN, +	 {1, 1, 1},  4, 4, {32, {{0}, {8}, {0}, {0} } }, +	 {{{0}, {24}, {0}, {0} } } },    /* SVGA3D_X24_TYPELESS_G8_UINT */ + +	{SVGA3DBLOCKDESC_RG, +	 {1, 1, 1},  2, 2, {16, {{0}, {8}, {8}, {0} } }, +	 {{{0}, {8}, {0}, {0} } } },     /* SVGA3D_R8G8_TYPELESS */ + +	{SVGA3DBLOCKDESC_RG, +	 {1, 1, 1},  2, 2, {16, {{0}, {8}, {8}, {0} } }, +	 {{{0}, {8}, {0}, {0} } } },     /* SVGA3D_R8G8_UNORM */ + +	{SVGA3DBLOCKDESC_RG, +	 {1, 1, 1},  2, 2, {16, {{0}, {8}, {8}, {0} } }, +	 {{{0}, {8}, {0}, {0} } } },     /* SVGA3D_R8G8_UINT */ + +	{SVGA3DBLOCKDESC_UV, +	 {1, 1, 1},  2, 2, {16, {{0}, {8}, {8}, {0} } }, +	 {{{0}, {8}, {0}, {0} } } },     /* SVGA3D_R8G8_SINT */ + +	{SVGA3DBLOCKDESC_RED, +	 {1, 1, 1},  2, 2, {16, {{0}, {0}, {16}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R16_TYPELESS */ + +	{SVGA3DBLOCKDESC_RED, +	 {1, 1, 1},  2, 2, {16, {{0}, {0}, {16}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R16_UNORM */ + +	{SVGA3DBLOCKDESC_RED, +	 {1, 1, 1},  2, 2, {16, {{0}, {0}, {16}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R16_UINT */ + +	{SVGA3DBLOCKDESC_U, +	 {1, 1, 1},  2, 2, {16, {{0}, {0}, {16}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R16_SNORM */ + +	{SVGA3DBLOCKDESC_U, +	 {1, 1, 1},  2, 2, {16, {{0}, {0}, {16}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R16_SINT */ + +	{SVGA3DBLOCKDESC_RED, +	 {1, 1, 1},  1, 1, {8, {{0}, {0}, {8}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R8_TYPELESS */ + +	{SVGA3DBLOCKDESC_RED, +	 {1, 1, 1},  1, 1, {8, {{0}, {0}, {8}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R8_UNORM */ + +	{SVGA3DBLOCKDESC_RED, +	 {1, 1, 1},  1, 1, {8, {{0}, {0}, {8}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R8_UINT */ + +	{SVGA3DBLOCKDESC_U, +	 {1, 1, 1},  1, 1, {8, {{0}, {0}, {8}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R8_SNORM */ + +	{SVGA3DBLOCKDESC_U, +	 {1, 1, 1},  1, 1, {8, {{0}, {0}, {8}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R8_SINT */ + +	{SVGA3DBLOCKDESC_RED, +	 {8, 1, 1},  1, 1, {8, {{0}, {0}, {8}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_R1_UNORM */ + +	{SVGA3DBLOCKDESC_RGBE, +	 {1, 1, 1},  4, 4, {32, {{9}, {9}, {9}, {5} } }, +	 {{{18}, {9}, {0}, {27} } } },   /* SVGA3D_R9G9B9E5_SHAREDEXP */ + +	{SVGA3DBLOCKDESC_RG, +	 {1, 1, 1},  2, 2, {16, {{0}, {8}, {8}, {0} } }, +	 {{{0}, {8}, {0}, {0} } } },     /* SVGA3D_R8G8_B8G8_UNORM */ + +	{SVGA3DBLOCKDESC_RG, +	 {1, 1, 1},  2, 2, {16, {{0}, {8}, {8}, {0} } }, +	 {{{0}, {8}, {0}, {0} } } },     /* SVGA3D_G8R8_G8B8_UNORM */ + +	{SVGA3DBLOCKDESC_COMPRESSED, +	 {4, 4, 1},  8, 8, {64, {{0}, {0}, {64}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_BC1_TYPELESS */ + +	{SVGA3DBLOCKDESC_COMPRESSED_SRGB, +	 {4, 4, 1},  8, 8, {64, {{0}, {0}, {64}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_BC1_UNORM_SRGB */ + +	{SVGA3DBLOCKDESC_COMPRESSED, +	 {4, 4, 1},  16, 16, {128, {{0}, {0}, {128}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_BC2_TYPELESS */ + +	{SVGA3DBLOCKDESC_COMPRESSED_SRGB, +	 {4, 4, 1},  16, 16, {128, {{0}, {0}, {128}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_BC2_UNORM_SRGB */ + +	{SVGA3DBLOCKDESC_COMPRESSED, +	 {4, 4, 1},  16, 16, {128, {{0}, {0}, {128}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_BC3_TYPELESS */ + +	{SVGA3DBLOCKDESC_COMPRESSED_SRGB, +	 {4, 4, 1},  16, 16, {128, {{0}, {0}, {128}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_BC3_UNORM_SRGB */ + +	{SVGA3DBLOCKDESC_COMPRESSED, +	 {4, 4, 1},  8, 8, {64, {{0}, {0}, {64}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_BC4_TYPELESS */ + +	{SVGA3DBLOCKDESC_COMPRESSED, +	 {4, 4, 1},  8, 8, {64, {{0}, {0}, {64}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_BC4_UNORM */ + +	{SVGA3DBLOCKDESC_COMPRESSED, +	 {4, 4, 1},  8, 8, {64, {{0}, {0}, {64}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_BC4_SNORM */ + +	{SVGA3DBLOCKDESC_COMPRESSED, +	 {4, 4, 1},  16, 16, {128, {{0}, {0}, {128}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_BC5_TYPELESS */ + +	{SVGA3DBLOCKDESC_COMPRESSED, +	 {4, 4, 1},  16, 16, {128, {{0}, {0}, {128}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_BC5_UNORM */ + +	{SVGA3DBLOCKDESC_COMPRESSED, +	 {4, 4, 1},  16, 16, {128, {{0}, {0}, {128}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_BC5_SNORM */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  4, 4, {32, {{10}, {10}, {10}, {2} } }, +	 {{{0}, {10}, {20}, {30} } } },  /* SVGA3D_R10G10B10_XR_BIAS_A2_UNORM */ + +	{SVGA3DBLOCKDESC_RGBA, +	 {1, 1, 1},  4, 4, {32, {{8}, {8}, {8}, {8} } }, +	 {{{0}, {8}, {16}, {24} } } },   /* SVGA3D_B8G8R8A8_TYPELESS */ + +	{SVGA3DBLOCKDESC_RGBA_SRGB, +	 {1, 1, 1},  4, 4, {32, {{8}, {8}, {8}, {8} } }, +	 {{{0}, {8}, {16}, {24} } } },   /* SVGA3D_B8G8R8A8_UNORM_SRGB */ + +	{SVGA3DBLOCKDESC_RGB, +	 {1, 1, 1},  4, 4, {24, {{8}, {8}, {8}, {0} } }, +	 {{{0}, {8}, {16}, {24} } } },   /* SVGA3D_B8G8R8X8_TYPELESS */ + +	{SVGA3DBLOCKDESC_RGB_SRGB, +	 {1, 1, 1},  4, 4, {24, {{8}, {8}, {8}, {0} } }, +	 {{{0}, {8}, {16}, {24} } } },   /* SVGA3D_B8G8R8X8_UNORM_SRGB */ + +	{SVGA3DBLOCKDESC_DEPTH, +	 {1, 1, 1},  2, 2, {16, {{0}, {0}, {16}, {0} } }, +	 {{{0}, {0}, {0}, {0} } } },     /* SVGA3D_Z_DF16 */ + +	{SVGA3DBLOCKDESC_DS, +	 {1, 1, 1},  4, 4, {32, {{0}, {8}, {24}, {0} } }, +	 {{{0}, {24}, {0}, {0} } } },    /* SVGA3D_Z_DF24 */ + +	{SVGA3DBLOCKDESC_DS, +	 {1, 1, 1},  4, 4, {32, {{0}, {8}, {24}, {0} } }, +	 {{{0}, {24}, {0}, {0} } } },    /* SVGA3D_Z_D24S8_INT */ +}; + +static inline u32 clamped_umul32(u32 a, u32 b) +{ +	u64 tmp = (u64) a*b; +	return (tmp > (u64) U32_MAX) ? U32_MAX : tmp; +} + +static inline const struct svga3d_surface_desc * +svga3dsurface_get_desc(SVGA3dSurfaceFormat format) +{ +	if (format < ARRAY_SIZE(svga3d_surface_descs)) +		return &svga3d_surface_descs[format]; + +	return &svga3d_surface_descs[SVGA3D_FORMAT_INVALID]; +} + +/* + *---------------------------------------------------------------------- + * + * svga3dsurface_get_mip_size -- + * + *      Given a base level size and the mip level, compute the size of + *      the mip level. + * + * Results: + *      See above. + * + * Side effects: + *      None. + * + *---------------------------------------------------------------------- + */ + +static inline surf_size_struct +svga3dsurface_get_mip_size(surf_size_struct base_level, u32 mip_level) +{ +	surf_size_struct size; + +	size.width = max_t(u32, base_level.width >> mip_level, 1); +	size.height = max_t(u32, base_level.height >> mip_level, 1); +	size.depth = max_t(u32, base_level.depth >> mip_level, 1); +	return size; +} + +static inline void +svga3dsurface_get_size_in_blocks(const struct svga3d_surface_desc *desc, +				 const surf_size_struct *pixel_size, +				 surf_size_struct *block_size) +{ +	block_size->width = DIV_ROUND_UP(pixel_size->width, +					 desc->block_size.width); +	block_size->height = DIV_ROUND_UP(pixel_size->height, +					  desc->block_size.height); +	block_size->depth = DIV_ROUND_UP(pixel_size->depth, +					 desc->block_size.depth); +} + +static inline bool +svga3dsurface_is_planar_surface(const struct svga3d_surface_desc *desc) +{ +	return (desc->block_desc & SVGA3DBLOCKDESC_PLANAR_YUV) != 0; +} + +static inline u32 +svga3dsurface_calculate_pitch(const struct svga3d_surface_desc *desc, +			      const surf_size_struct *size) +{ +	u32 pitch; +	surf_size_struct blocks; + +	svga3dsurface_get_size_in_blocks(desc, size, &blocks); + +	pitch = blocks.width * desc->pitch_bytes_per_block; + +	return pitch; +} + +/* + *----------------------------------------------------------------------------- + * + * svga3dsurface_get_image_buffer_size -- + * + *      Return the number of bytes of buffer space required to store + *      one image of a surface, optionally using the specified pitch. + * + *      If pitch is zero, it is assumed that rows are tightly packed. + * + *      This function is overflow-safe. If the result would have + *      overflowed, instead we return MAX_UINT32. + * + * Results: + *      Byte count. + * + * Side effects: + *      None. + * + *----------------------------------------------------------------------------- + */ + +static inline u32 +svga3dsurface_get_image_buffer_size(const struct svga3d_surface_desc *desc, +				    const surf_size_struct *size, +				    u32 pitch) +{ +	surf_size_struct image_blocks; +	u32 slice_size, total_size; + +	svga3dsurface_get_size_in_blocks(desc, size, &image_blocks); + +	if (svga3dsurface_is_planar_surface(desc)) { +		total_size = clamped_umul32(image_blocks.width, +					    image_blocks.height); +		total_size = clamped_umul32(total_size, image_blocks.depth); +		total_size = clamped_umul32(total_size, desc->bytes_per_block); +		return total_size; +	} + +	if (pitch == 0) +		pitch = svga3dsurface_calculate_pitch(desc, size); + +	slice_size = clamped_umul32(image_blocks.height, pitch); +	total_size = clamped_umul32(slice_size, image_blocks.depth); + +	return total_size; +} + +static inline u32 +svga3dsurface_get_serialized_size(SVGA3dSurfaceFormat format, +				  surf_size_struct base_level_size, +				  u32 num_mip_levels, +				  bool cubemap) +{ +	const struct svga3d_surface_desc *desc = svga3dsurface_get_desc(format); +	u64 total_size = 0; +	u32 mip; + +	for (mip = 0; mip < num_mip_levels; mip++) { +		surf_size_struct size = +			svga3dsurface_get_mip_size(base_level_size, mip); +		total_size += svga3dsurface_get_image_buffer_size(desc, +								  &size, 0); +	} + +	if (cubemap) +		total_size *= SVGA3D_MAX_SURFACE_FACES; + +	return (u32) min_t(u64, total_size, (u64) U32_MAX); +} + + +/** + * svga3dsurface_get_pixel_offset - Compute the offset (in bytes) to a pixel + * in an image (or volume). + * + * @width: The image width in pixels. + * @height: The image height in pixels + */ +static inline u32 +svga3dsurface_get_pixel_offset(SVGA3dSurfaceFormat format, +			       u32 width, u32 height, +			       u32 x, u32 y, u32 z) +{ +	const struct svga3d_surface_desc *desc = svga3dsurface_get_desc(format); +	const u32 bw = desc->block_size.width, bh = desc->block_size.height; +	const u32 bd = desc->block_size.depth; +	const u32 rowstride = DIV_ROUND_UP(width, bw) * desc->bytes_per_block; +	const u32 imgstride = DIV_ROUND_UP(height, bh) * rowstride; +	const u32 offset = (z / bd * imgstride + +			    y / bh * rowstride + +			    x / bw * desc->bytes_per_block); +	return offset; +} + + +static inline u32 +svga3dsurface_get_image_offset(SVGA3dSurfaceFormat format, +			       surf_size_struct baseLevelSize, +			       u32 numMipLevels, +			       u32 face, +			       u32 mip) + +{ +	u32 offset; +	u32 mipChainBytes; +	u32 mipChainBytesToLevel; +	u32 i; +	const struct svga3d_surface_desc *desc; +	surf_size_struct mipSize; +	u32 bytes; + +	desc = svga3dsurface_get_desc(format); + +	mipChainBytes = 0; +	mipChainBytesToLevel = 0; +	for (i = 0; i < numMipLevels; i++) { +		mipSize = svga3dsurface_get_mip_size(baseLevelSize, i); +		bytes = svga3dsurface_get_image_buffer_size(desc, &mipSize, 0); +		mipChainBytes += bytes; +		if (i < mip) +			mipChainBytesToLevel += bytes; +	} + +	offset = mipChainBytes * face + mipChainBytesToLevel; + +	return offset; +} diff --git a/drivers/gpu/drm/vmwgfx/svga_escape.h b/drivers/gpu/drm/vmwgfx/svga_escape.h index 7b85e9b8c85..8e8d9682e01 100644 --- a/drivers/gpu/drm/vmwgfx/svga_escape.h +++ b/drivers/gpu/drm/vmwgfx/svga_escape.h @@ -75,7 +75,7 @@   */  #define SVGA_ESCAPE_VMWARE_HINT               0x00030000 -#define SVGA_ESCAPE_VMWARE_HINT_FULLSCREEN    0x00030001  // Deprecated +#define SVGA_ESCAPE_VMWARE_HINT_FULLSCREEN    0x00030001  /* Deprecated */  typedef  struct { diff --git a/drivers/gpu/drm/vmwgfx/svga_overlay.h b/drivers/gpu/drm/vmwgfx/svga_overlay.h index f753d73c14b..f38416fcb04 100644 --- a/drivers/gpu/drm/vmwgfx/svga_overlay.h +++ b/drivers/gpu/drm/vmwgfx/svga_overlay.h @@ -38,9 +38,9 @@   * 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' +#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, @@ -68,7 +68,7 @@ struct SVGAEscapeVideoSetRegs {        uint32 streamId;     } header; -   // May include zero or more items. +   /* May include zero or more items. */     struct {        uint32 registerId;        uint32 value; @@ -134,12 +134,12 @@ struct {   */  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) +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; @@ -198,4 +198,4 @@ VMwareVideoGetAttributes(const SVGAOverlayFormat format,    // IN      return true;  } -#endif // _SVGA_OVERLAY_H_ +#endif /* _SVGA_OVERLAY_H_ */ diff --git a/drivers/gpu/drm/vmwgfx/svga_reg.h b/drivers/gpu/drm/vmwgfx/svga_reg.h index 1b96c2ec07d..11323dd5196 100644 --- a/drivers/gpu/drm/vmwgfx/svga_reg.h +++ b/drivers/gpu/drm/vmwgfx/svga_reg.h @@ -39,6 +39,15 @@  #define PCI_DEVICE_ID_VMWARE_SVGA2      0x0405  /* + * SVGA_REG_ENABLE bit definitions. + */ +#define SVGA_REG_ENABLE_DISABLE     0 +#define SVGA_REG_ENABLE_ENABLE      1 +#define SVGA_REG_ENABLE_HIDE        2 +#define SVGA_REG_ENABLE_ENABLE_HIDE (SVGA_REG_ENABLE_ENABLE |\ +				     SVGA_REG_ENABLE_HIDE) + +/*   * 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. @@ -158,7 +167,19 @@ enum {     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_REG_GMRS_MAX_PAGES = 46,    /* Maximum number of 4KB pages for all GMRs */ +   SVGA_REG_MEMORY_SIZE = 47,       /* Total dedicated device memory excluding FIFO */ +   SVGA_REG_COMMAND_LOW = 48,       /* Lower 32 bits and submits commands */ +   SVGA_REG_COMMAND_HIGH = 49,      /* Upper 32 bits of command buffer PA */ +   SVGA_REG_MAX_PRIMARY_BOUNDING_BOX_MEM = 50,   /* Max primary memory */ +   SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB = 51, /* Suggested limit on mob mem */ +   SVGA_REG_DEV_CAP = 52,           /* Write dev cap index, read value */ +   SVGA_REG_CMD_PREPEND_LOW = 53, +   SVGA_REG_CMD_PREPEND_HIGH = 54, +   SVGA_REG_SCREENTARGET_MAX_WIDTH = 55, +   SVGA_REG_SCREENTARGET_MAX_HEIGHT = 56, +   SVGA_REG_MOB_MAX_SIZE = 57, +   SVGA_REG_TOP = 58,               /* 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 */ @@ -265,7 +286,7 @@ enum {   * possible.   */  #define SVGA_GMR_NULL         ((uint32) -1) -#define SVGA_GMR_FRAMEBUFFER  ((uint32) -2)  // Guest Framebuffer (GFB) +#define SVGA_GMR_FRAMEBUFFER  ((uint32) -2)  /* Guest Framebuffer (GFB) */  typedef  struct SVGAGuestMemDescriptor { @@ -306,13 +327,35 @@ struct SVGAGMRImageFormat {        struct {           uint32 bitsPerPixel : 8;           uint32 colorDepth   : 8; -         uint32 reserved     : 16;  // Must be zero +         uint32 reserved     : 16;  /* Must be zero */        };        uint32 value;     };  } SVGAGMRImageFormat; +typedef +struct SVGAGuestImage { +   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; +} SVGAGuestImage; +  /*   * SVGAColorBGRX --   * @@ -328,7 +371,7 @@ struct SVGAColorBGRX {           uint32 b : 8;           uint32 g : 8;           uint32 r : 8; -         uint32 x : 8;  // Unused +         uint32 x : 8;  /* Unused */        };        uint32 value; @@ -370,24 +413,38 @@ struct SVGASignedPoint {   *  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. + * + * SVGA_CAP_GMR2 -- + *    Provides asynchronous commands to define and remap guest memory + *    regions.  Adds device registers SVGA_REG_GMRS_MAX_PAGES and + *    SVGA_REG_MEMORY_SIZE. + * + * SVGA_CAP_SCREEN_OBJECT_2 -- + *    Allow screen object support, and require backing stores from the + *    guest for each screen object.   */  #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_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_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_DISPLAY_TOPOLOGY   0x00080000   /* Legacy multi-monitor support */  #define SVGA_CAP_GMR                0x00100000  #define SVGA_CAP_TRACES             0x00200000 - +#define SVGA_CAP_GMR2               0x00400000 +#define SVGA_CAP_SCREEN_OBJECT_2    0x00800000 +#define SVGA_CAP_COMMAND_BUFFERS    0x01000000 +#define SVGA_CAP_DEAD1              0x02000000 +#define SVGA_CAP_CMD_BUFFERS_2      0x04000000 +#define SVGA_CAP_GBOBJECTS          0x08000000  /*   * FIFO register indices. @@ -431,7 +488,7 @@ enum {     SVGA_FIFO_CAPABILITIES = 4,     SVGA_FIFO_FLAGS, -   // Valid with SVGA_FIFO_CAP_FENCE: +   /* Valid with SVGA_FIFO_CAP_FENCE: */     SVGA_FIFO_FENCE,     /* @@ -444,33 +501,47 @@ enum {      * extended FIFO.      */ -   // Valid if exists (i.e. if extended FIFO enabled): +   /* 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: +   /* Valid with SVGA_FIFO_CAP_PITCHLOCK: */     SVGA_FIFO_PITCHLOCK, -   // Valid with SVGA_FIFO_CAP_CURSOR_BYPASS_3: +   /* 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: +   /* Valid with SVGA_FIFO_CAP_RESERVE: */     SVGA_FIFO_RESERVED,           /* Bytes past NEXT_CMD with real contents */     /* -    * Valid with SVGA_FIFO_CAP_SCREEN_OBJECT: +    * Valid with SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2:      *      * 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. +    * as a signed offset relative to that screen's origin.      */     SVGA_FIFO_CURSOR_SCREEN_ID,     /* +    * Valid with SVGA_FIFO_CAP_DEAD +    * +    * An arbitrary value written by the host, drivers should not use it. +    */ +   SVGA_FIFO_DEAD, + +   /* +    * Valid with SVGA_FIFO_CAP_3D_HWVERSION_REVISED: +    * +    * Contains 3D HWVERSION (see SVGA3dHardwareVersion in svga3d_reg.h) +    * on platforms that can enforce graphics resource limits. +    */ +   SVGA_FIFO_3D_HWVERSION_REVISED, + +   /*      * 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 @@ -508,7 +579,7 @@ enum {      * sets SVGA_FIFO_MIN high enough to leave room for them.      */ -   // Valid if register exists: +   /* 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" */ @@ -709,6 +780,37 @@ enum {   *   *       - When a screen is resized, either using Screen Object commands or   *         legacy multimon registers, its contents are preserved. + * + * SVGA_FIFO_CAP_GMR2 -- + * + *    Provides new commands to define and remap guest memory regions (GMR). + * + *    New 2D commands: + *       DEFINE_GMR2, REMAP_GMR2. + * + * SVGA_FIFO_CAP_3D_HWVERSION_REVISED -- + * + *    Indicates new register SVGA_FIFO_3D_HWVERSION_REVISED exists. + *    This register may replace SVGA_FIFO_3D_HWVERSION on platforms + *    that enforce graphics resource limits.  This allows the platform + *    to clear SVGA_FIFO_3D_HWVERSION and disable 3D in legacy guest + *    drivers that do not limit their resources. + * + *    Note this is an alias to SVGA_FIFO_CAP_GMR2 because these indicators + *    are codependent (and thus we use a single capability bit). + * + * SVGA_FIFO_CAP_SCREEN_OBJECT_2 -- + * + *    Modifies the DEFINE_SCREEN command to include a guest provided + *    backing store in GMR memory and the bytesPerLine for the backing + *    store.  This capability requires the use of a backing store when + *    creating screen objects.  However if SVGA_FIFO_CAP_SCREEN_OBJECT + *    is present then backing stores are optional. + * + * SVGA_FIFO_CAP_DEAD -- + * + *    Drivers should not use this cap bit.  This cap bit can not be + *    reused since some hosts already expose it.   */  #define SVGA_FIFO_CAP_NONE                  0 @@ -720,6 +822,10 @@ enum {  #define SVGA_FIFO_CAP_ESCAPE            (1<<5)  #define SVGA_FIFO_CAP_RESERVE           (1<<6)  #define SVGA_FIFO_CAP_SCREEN_OBJECT     (1<<7) +#define SVGA_FIFO_CAP_GMR2              (1<<8) +#define SVGA_FIFO_CAP_3D_HWVERSION_REVISED  SVGA_FIFO_CAP_GMR2 +#define SVGA_FIFO_CAP_SCREEN_OBJECT_2   (1<<9) +#define SVGA_FIFO_CAP_DEAD              (1<<10)  /* @@ -730,7 +836,7 @@ enum {  #define SVGA_FIFO_FLAG_NONE                 0  #define SVGA_FIFO_FLAG_ACCELFRONT       (1<<0) -#define SVGA_FIFO_FLAG_RESERVED        (1<<31) // Internal use only +#define SVGA_FIFO_FLAG_RESERVED        (1<<31) /* Internal use only */  /*   * FIFO reservation sentinel value @@ -763,22 +869,22 @@ enum {     SVGA_VIDEO_DATA_OFFSET,     SVGA_VIDEO_FORMAT,     SVGA_VIDEO_COLORKEY, -   SVGA_VIDEO_SIZE,          // Deprecated +   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_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_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  }; @@ -829,15 +935,51 @@ typedef struct SVGAOverlayUnit {   *    compatibility. New flags can be added, and the struct may grow,   *    but existing fields must retain their meaning.   * + *    Added with SVGA_FIFO_CAP_SCREEN_OBJECT_2 are required fields of + *    a SVGAGuestPtr that is used to back the screen contents.  This + *    memory must come from the GFB.  The guest is not allowed to + *    access the memory and doing so will have undefined results.  The + *    backing store is required to be page aligned and the size is + *    padded to the next page boundry.  The number of pages is: + *       (bytesPerLine * size.width * 4 + PAGE_SIZE - 1) / PAGE_SIZE + * + *    The pitch in the backingStore is required to be at least large + *    enough to hold a 32bbp scanline.  It is recommended that the + *    driver pad bytesPerLine for a potential performance win. + * + *    The cloneCount field is treated as a hint from the guest that + *    the user wants this display to be cloned, countCount times.  A + *    value of zero means no cloning should happen. + */ + +#define SVGA_SCREEN_MUST_BE_SET     (1 << 0) /* Must be set or results undefined */ +#define SVGA_SCREEN_HAS_ROOT SVGA_SCREEN_MUST_BE_SET /* Deprecated */ +#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 */ + +/* + * Added with SVGA_FIFO_CAP_SCREEN_OBJECT_2.  When the screen is + * deactivated the base layer is defined to lose all contents and + * become black.  When a screen is deactivated the backing store is + * optional.  When set backingPtr and bytesPerLine will be ignored.   */ +#define SVGA_SCREEN_DEACTIVATE  (1 << 3) -#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 +/* + * Added with SVGA_FIFO_CAP_SCREEN_OBJECT_2.  When this flag is set + * the screen contents will be outputted as all black to the user + * though the base layer contents is preserved.  The screen base layer + * can still be read and written to like normal though the no visible + * effect will be seen by the user.  When the flag is changed the + * screen will be blanked or redrawn to the current contents as needed + * without any extra commands from the driver.  This flag only has an + * effect when the screen is not deactivated. + */ +#define SVGA_SCREEN_BLANKING (1 << 4)  typedef  struct SVGAScreenObject { -   uint32 structSize;   // sizeof(SVGAScreenObject) +   uint32 structSize;   /* sizeof(SVGAScreenObject) */     uint32 id;     uint32 flags;     struct { @@ -847,7 +989,14 @@ struct SVGAScreenObject {     struct {        int32 x;        int32 y; -   } root;              // Only used if SVGA_SCREEN_HAS_ROOT is set. +   } root; + +   /* +    * Added and required by SVGA_FIFO_CAP_SCREEN_OBJECT_2, optional +    * with SVGA_FIFO_CAP_SCREEN_OBJECT. +    */ +   SVGAGuestImage backingStore; +   uint32 cloneCount;  } SVGAScreenObject; @@ -885,6 +1034,8 @@ typedef enum {     SVGA_CMD_BLIT_SCREEN_TO_GMRFB  = 38,     SVGA_CMD_ANNOTATION_FILL       = 39,     SVGA_CMD_ANNOTATION_COPY       = 40, +   SVGA_CMD_DEFINE_GMR2           = 41, +   SVGA_CMD_REMAP_GMR2            = 42,     SVGA_CMD_MAX  } SVGAFifoCmdId; @@ -920,7 +1071,7 @@ typedef enum {   */  typedef -struct { +struct SVGAFifoCmdUpdate {     uint32 x;     uint32 y;     uint32 width; @@ -939,7 +1090,7 @@ struct {   */  typedef -struct { +struct SVGAFifoCmdRectCopy {     uint32 srcX;     uint32 srcY;     uint32 destX; @@ -963,14 +1114,14 @@ struct {   */  typedef -struct { -   uint32 id;             // Reserved, must be zero. +struct SVGAFifoCmdDefineCursor { +   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 +   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. @@ -992,8 +1143,8 @@ struct {   */  typedef -struct { -   uint32 id;             // Reserved, must be zero. +struct SVGAFifoCmdDefineAlphaCursor { +   uint32 id;             /* Reserved, must be zero. */     uint32 hotspotX;     uint32 hotspotY;     uint32 width; @@ -1015,7 +1166,7 @@ struct {   */  typedef -struct { +struct SVGAFifoCmdUpdateVerbose {     uint32 x;     uint32 y;     uint32 width; @@ -1040,13 +1191,13 @@ struct {  #define  SVGA_ROP_COPY                    0x03  typedef -struct { -   uint32 color;     // In the same format as the GFB +struct SVGAFifoCmdFrontRopFill { +   uint32 color;     /* In the same format as the GFB */     uint32 x;     uint32 y;     uint32 width;     uint32 height; -   uint32 rop;       // Must be SVGA_ROP_COPY +   uint32 rop;       /* Must be SVGA_ROP_COPY */  } SVGAFifoCmdFrontRopFill; @@ -1083,7 +1234,7 @@ struct {   */  typedef -struct { +struct SVGAFifoCmdEscape {     uint32 nsid;     uint32 size;     /* followed by 'size' bytes of data */ @@ -1113,12 +1264,12 @@ struct {   *    registers (SVGA_REG_NUM_GUEST_DISPLAYS, SVGA_REG_DISPLAY_*).   *   * Availability: - *    SVGA_FIFO_CAP_SCREEN_OBJECT + *    SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2   */  typedef  struct { -   SVGAScreenObject screen;   // Variable-length according to version +   SVGAScreenObject screen;   /* Variable-length according to version */  } SVGAFifoCmdDefineScreen; @@ -1129,7 +1280,7 @@ struct {   *    re-use.   *   * Availability: - *    SVGA_FIFO_CAP_SCREEN_OBJECT + *    SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2   */  typedef @@ -1182,7 +1333,7 @@ struct {   *    GMRFB.   *   * Availability: - *    SVGA_FIFO_CAP_SCREEN_OBJECT + *    SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2   */  typedef @@ -1219,7 +1370,7 @@ struct {   *    SVGA_CMD_ANNOTATION_* commands for details.   *   * Availability: - *    SVGA_FIFO_CAP_SCREEN_OBJECT + *    SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2   */  typedef @@ -1267,7 +1418,7 @@ struct {   *    the time any subsequent FENCE commands are reached.   *   * Availability: - *    SVGA_FIFO_CAP_SCREEN_OBJECT + *    SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2   */  typedef @@ -1302,7 +1453,7 @@ struct {   *    user's display is being remoted over a network connection.   *   * Availability: - *    SVGA_FIFO_CAP_SCREEN_OBJECT + *    SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2   */  typedef @@ -1334,7 +1485,7 @@ struct {   *    undefined.   *   * Availability: - *    SVGA_FIFO_CAP_SCREEN_OBJECT + *    SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2   */  typedef @@ -1343,4 +1494,72 @@ struct {     uint32           srcScreenId;  } SVGAFifoCmdAnnotationCopy; + +/* + * SVGA_CMD_DEFINE_GMR2 -- + * + *    Define guest memory region v2.  See the description of GMRs above. + * + * Availability: + *    SVGA_CAP_GMR2 + */ + +typedef +struct { +   uint32 gmrId; +   uint32 numPages; +} SVGAFifoCmdDefineGMR2; + + +/* + * SVGA_CMD_REMAP_GMR2 -- + * + *    Remap guest memory region v2.  See the description of GMRs above. + * + *    This command allows guest to modify a portion of an existing GMR by + *    invalidating it or reassigning it to different guest physical pages. + *    The pages are identified by physical page number (PPN).  The pages + *    are assumed to be pinned and valid for DMA operations. + * + *    Description of command flags: + * + *    SVGA_REMAP_GMR2_VIA_GMR: If enabled, references a PPN list in a GMR. + *       The PPN list must not overlap with the remap region (this can be + *       handled trivially by referencing a separate GMR).  If flag is + *       disabled, PPN list is appended to SVGARemapGMR command. + * + *    SVGA_REMAP_GMR2_PPN64: If set, PPN list is in PPN64 format, otherwise + *       it is in PPN32 format. + * + *    SVGA_REMAP_GMR2_SINGLE_PPN: If set, PPN list contains a single entry. + *       A single PPN can be used to invalidate a portion of a GMR or + *       map it to to a single guest scratch page. + * + * Availability: + *    SVGA_CAP_GMR2 + */ + +typedef enum { +   SVGA_REMAP_GMR2_PPN32         = 0, +   SVGA_REMAP_GMR2_VIA_GMR       = (1 << 0), +   SVGA_REMAP_GMR2_PPN64         = (1 << 1), +   SVGA_REMAP_GMR2_SINGLE_PPN    = (1 << 2), +} SVGARemapGMR2Flags; + +typedef +struct { +   uint32 gmrId; +   SVGARemapGMR2Flags flags; +   uint32 offsetPages; /* offset in pages to begin remap */ +   uint32 numPages; /* number of pages to remap */ +   /* +    * Followed by additional data depending on SVGARemapGMR2Flags. +    * +    * If flag SVGA_REMAP_GMR2_VIA_GMR is set, single SVGAGuestPtr follows. +    * Otherwise an array of page descriptors in PPN32 or PPN64 format +    * (according to flag SVGA_REMAP_GMR2_PPN64) follows.  If flag +    * SVGA_REMAP_GMR2_SINGLE_PPN is set, array contains a single entry. +    */ +} SVGAFifoCmdRemapGMR2; +  #endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c index 80bc37b274e..6327cfc3680 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c @@ -26,8 +26,9 @@   **************************************************************************/  #include "vmwgfx_drv.h" -#include "ttm/ttm_bo_driver.h" -#include "ttm/ttm_placement.h" +#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_placement.h> +#include <drm/ttm/ttm_page_alloc.h>  static uint32_t vram_placement_flags = TTM_PL_FLAG_VRAM |  	TTM_PL_FLAG_CACHED; @@ -39,9 +40,20 @@ static uint32_t vram_ne_placement_flags = TTM_PL_FLAG_VRAM |  static uint32_t sys_placement_flags = TTM_PL_FLAG_SYSTEM |  	TTM_PL_FLAG_CACHED; +static uint32_t sys_ne_placement_flags = TTM_PL_FLAG_SYSTEM | +	TTM_PL_FLAG_CACHED | +	TTM_PL_FLAG_NO_EVICT; +  static uint32_t gmr_placement_flags = VMW_PL_FLAG_GMR |  	TTM_PL_FLAG_CACHED; +static uint32_t gmr_ne_placement_flags = VMW_PL_FLAG_GMR | +	TTM_PL_FLAG_CACHED | +	TTM_PL_FLAG_NO_EVICT; + +static uint32_t mob_placement_flags = VMW_PL_FLAG_MOB | +	TTM_PL_FLAG_CACHED; +  struct ttm_placement vmw_vram_placement = {  	.fpfn = 0,  	.lpfn = 0, @@ -56,6 +68,11 @@ static uint32_t vram_gmr_placement_flags[] = {  	VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED  }; +static uint32_t gmr_vram_placement_flags[] = { +	VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED, +	TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED +}; +  struct ttm_placement vmw_vram_gmr_placement = {  	.fpfn = 0,  	.lpfn = 0, @@ -65,6 +82,20 @@ struct ttm_placement vmw_vram_gmr_placement = {  	.busy_placement = &gmr_placement_flags  }; +static uint32_t vram_gmr_ne_placement_flags[] = { +	TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT, +	VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT +}; + +struct ttm_placement vmw_vram_gmr_ne_placement = { +	.fpfn = 0, +	.lpfn = 0, +	.num_placement = 2, +	.placement = vram_gmr_ne_placement_flags, +	.num_busy_placement = 1, +	.busy_placement = &gmr_ne_placement_flags +}; +  struct ttm_placement vmw_vram_sys_placement = {  	.fpfn = 0,  	.lpfn = 0, @@ -92,92 +123,561 @@ struct ttm_placement vmw_sys_placement = {  	.busy_placement = &sys_placement_flags  }; -struct vmw_ttm_backend { -	struct ttm_backend backend; -	struct page **pages; -	unsigned long num_pages; +struct ttm_placement vmw_sys_ne_placement = { +	.fpfn = 0, +	.lpfn = 0, +	.num_placement = 1, +	.placement = &sys_ne_placement_flags, +	.num_busy_placement = 1, +	.busy_placement = &sys_ne_placement_flags +}; + +static uint32_t evictable_placement_flags[] = { +	TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED, +	TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED, +	VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED, +	VMW_PL_FLAG_MOB | TTM_PL_FLAG_CACHED +}; + +struct ttm_placement vmw_evictable_placement = { +	.fpfn = 0, +	.lpfn = 0, +	.num_placement = 4, +	.placement = evictable_placement_flags, +	.num_busy_placement = 1, +	.busy_placement = &sys_placement_flags +}; + +struct ttm_placement vmw_srf_placement = { +	.fpfn = 0, +	.lpfn = 0, +	.num_placement = 1, +	.num_busy_placement = 2, +	.placement = &gmr_placement_flags, +	.busy_placement = gmr_vram_placement_flags +}; + +struct ttm_placement vmw_mob_placement = { +	.fpfn = 0, +	.lpfn = 0, +	.num_placement = 1, +	.num_busy_placement = 1, +	.placement = &mob_placement_flags, +	.busy_placement = &mob_placement_flags +}; + +struct vmw_ttm_tt { +	struct ttm_dma_tt dma_ttm;  	struct vmw_private *dev_priv;  	int gmr_id; +	struct vmw_mob *mob; +	int mem_type; +	struct sg_table sgt; +	struct vmw_sg_table vsgt; +	uint64_t sg_alloc_size; +	bool mapped;  }; -static int vmw_ttm_populate(struct ttm_backend *backend, -			    unsigned long num_pages, struct page **pages, -			    struct page *dummy_read_page) +const size_t vmw_tt_size = sizeof(struct vmw_ttm_tt); + +/** + * Helper functions to advance a struct vmw_piter iterator. + * + * @viter: Pointer to the iterator. + * + * These functions return false if past the end of the list, + * true otherwise. Functions are selected depending on the current + * DMA mapping mode. + */ +static bool __vmw_piter_non_sg_next(struct vmw_piter *viter) +{ +	return ++(viter->i) < viter->num_pages; +} + +static bool __vmw_piter_sg_next(struct vmw_piter *viter) +{ +	return __sg_page_iter_next(&viter->iter); +} + + +/** + * Helper functions to return a pointer to the current page. + * + * @viter: Pointer to the iterator + * + * These functions return a pointer to the page currently + * pointed to by @viter. Functions are selected depending on the + * current mapping mode. + */ +static struct page *__vmw_piter_non_sg_page(struct vmw_piter *viter) +{ +	return viter->pages[viter->i]; +} + +static struct page *__vmw_piter_sg_page(struct vmw_piter *viter) +{ +	return sg_page_iter_page(&viter->iter); +} + + +/** + * Helper functions to return the DMA address of the current page. + * + * @viter: Pointer to the iterator + * + * These functions return the DMA address of the page currently + * pointed to by @viter. Functions are selected depending on the + * current mapping mode. + */ +static dma_addr_t __vmw_piter_phys_addr(struct vmw_piter *viter) +{ +	return page_to_phys(viter->pages[viter->i]); +} + +static dma_addr_t __vmw_piter_dma_addr(struct vmw_piter *viter) +{ +	return viter->addrs[viter->i]; +} + +static dma_addr_t __vmw_piter_sg_addr(struct vmw_piter *viter)  { -	struct vmw_ttm_backend *vmw_be = -	    container_of(backend, struct vmw_ttm_backend, backend); +	return sg_page_iter_dma_address(&viter->iter); +} -	vmw_be->pages = pages; -	vmw_be->num_pages = num_pages; + +/** + * vmw_piter_start - Initialize a struct vmw_piter. + * + * @viter: Pointer to the iterator to initialize + * @vsgt: Pointer to a struct vmw_sg_table to initialize from + * + * Note that we're following the convention of __sg_page_iter_start, so that + * the iterator doesn't point to a valid page after initialization; it has + * to be advanced one step first. + */ +void vmw_piter_start(struct vmw_piter *viter, const struct vmw_sg_table *vsgt, +		     unsigned long p_offset) +{ +	viter->i = p_offset - 1; +	viter->num_pages = vsgt->num_pages; +	switch (vsgt->mode) { +	case vmw_dma_phys: +		viter->next = &__vmw_piter_non_sg_next; +		viter->dma_address = &__vmw_piter_phys_addr; +		viter->page = &__vmw_piter_non_sg_page; +		viter->pages = vsgt->pages; +		break; +	case vmw_dma_alloc_coherent: +		viter->next = &__vmw_piter_non_sg_next; +		viter->dma_address = &__vmw_piter_dma_addr; +		viter->page = &__vmw_piter_non_sg_page; +		viter->addrs = vsgt->addrs; +		viter->pages = vsgt->pages; +		break; +	case vmw_dma_map_populate: +	case vmw_dma_map_bind: +		viter->next = &__vmw_piter_sg_next; +		viter->dma_address = &__vmw_piter_sg_addr; +		viter->page = &__vmw_piter_sg_page; +		__sg_page_iter_start(&viter->iter, vsgt->sgt->sgl, +				     vsgt->sgt->orig_nents, p_offset); +		break; +	default: +		BUG(); +	} +} + +/** + * vmw_ttm_unmap_from_dma - unmap  device addresses previsouly mapped for + * TTM pages + * + * @vmw_tt: Pointer to a struct vmw_ttm_backend + * + * Used to free dma mappings previously mapped by vmw_ttm_map_for_dma. + */ +static void vmw_ttm_unmap_from_dma(struct vmw_ttm_tt *vmw_tt) +{ +	struct device *dev = vmw_tt->dev_priv->dev->dev; + +	dma_unmap_sg(dev, vmw_tt->sgt.sgl, vmw_tt->sgt.nents, +		DMA_BIDIRECTIONAL); +	vmw_tt->sgt.nents = vmw_tt->sgt.orig_nents; +} + +/** + * vmw_ttm_map_for_dma - map TTM pages to get device addresses + * + * @vmw_tt: Pointer to a struct vmw_ttm_backend + * + * This function is used to get device addresses from the kernel DMA layer. + * However, it's violating the DMA API in that when this operation has been + * performed, it's illegal for the CPU to write to the pages without first + * unmapping the DMA mappings, or calling dma_sync_sg_for_cpu(). It is + * therefore only legal to call this function if we know that the function + * dma_sync_sg_for_cpu() is a NOP, and dma_sync_sg_for_device() is at most + * a CPU write buffer flush. + */ +static int vmw_ttm_map_for_dma(struct vmw_ttm_tt *vmw_tt) +{ +	struct device *dev = vmw_tt->dev_priv->dev->dev; +	int ret; + +	ret = dma_map_sg(dev, vmw_tt->sgt.sgl, vmw_tt->sgt.orig_nents, +			 DMA_BIDIRECTIONAL); +	if (unlikely(ret == 0)) +		return -ENOMEM; + +	vmw_tt->sgt.nents = ret;  	return 0;  } -static int vmw_ttm_bind(struct ttm_backend *backend, struct ttm_mem_reg *bo_mem) +/** + * vmw_ttm_map_dma - Make sure TTM pages are visible to the device + * + * @vmw_tt: Pointer to a struct vmw_ttm_tt + * + * Select the correct function for and make sure the TTM pages are + * visible to the device. Allocate storage for the device mappings. + * If a mapping has already been performed, indicated by the storage + * pointer being non NULL, the function returns success. + */ +static int vmw_ttm_map_dma(struct vmw_ttm_tt *vmw_tt)  { -	struct vmw_ttm_backend *vmw_be = -	    container_of(backend, struct vmw_ttm_backend, backend); +	struct vmw_private *dev_priv = vmw_tt->dev_priv; +	struct ttm_mem_global *glob = vmw_mem_glob(dev_priv); +	struct vmw_sg_table *vsgt = &vmw_tt->vsgt; +	struct vmw_piter iter; +	dma_addr_t old; +	int ret = 0; +	static size_t sgl_size; +	static size_t sgt_size; + +	if (vmw_tt->mapped) +		return 0; -	vmw_be->gmr_id = bo_mem->start; +	vsgt->mode = dev_priv->map_mode; +	vsgt->pages = vmw_tt->dma_ttm.ttm.pages; +	vsgt->num_pages = vmw_tt->dma_ttm.ttm.num_pages; +	vsgt->addrs = vmw_tt->dma_ttm.dma_address; +	vsgt->sgt = &vmw_tt->sgt; + +	switch (dev_priv->map_mode) { +	case vmw_dma_map_bind: +	case vmw_dma_map_populate: +		if (unlikely(!sgl_size)) { +			sgl_size = ttm_round_pot(sizeof(struct scatterlist)); +			sgt_size = ttm_round_pot(sizeof(struct sg_table)); +		} +		vmw_tt->sg_alloc_size = sgt_size + sgl_size * vsgt->num_pages; +		ret = ttm_mem_global_alloc(glob, vmw_tt->sg_alloc_size, false, +					   true); +		if (unlikely(ret != 0)) +			return ret; + +		ret = sg_alloc_table_from_pages(&vmw_tt->sgt, vsgt->pages, +						vsgt->num_pages, 0, +						(unsigned long) +						vsgt->num_pages << PAGE_SHIFT, +						GFP_KERNEL); +		if (unlikely(ret != 0)) +			goto out_sg_alloc_fail; + +		if (vsgt->num_pages > vmw_tt->sgt.nents) { +			uint64_t over_alloc = +				sgl_size * (vsgt->num_pages - +					    vmw_tt->sgt.nents); + +			ttm_mem_global_free(glob, over_alloc); +			vmw_tt->sg_alloc_size -= over_alloc; +		} + +		ret = vmw_ttm_map_for_dma(vmw_tt); +		if (unlikely(ret != 0)) +			goto out_map_fail; + +		break; +	default: +		break; +	} + +	old = ~((dma_addr_t) 0); +	vmw_tt->vsgt.num_regions = 0; +	for (vmw_piter_start(&iter, vsgt, 0); vmw_piter_next(&iter);) { +		dma_addr_t cur = vmw_piter_dma_addr(&iter); + +		if (cur != old + PAGE_SIZE) +			vmw_tt->vsgt.num_regions++; +		old = cur; +	} + +	vmw_tt->mapped = true; +	return 0; + +out_map_fail: +	sg_free_table(vmw_tt->vsgt.sgt); +	vmw_tt->vsgt.sgt = NULL; +out_sg_alloc_fail: +	ttm_mem_global_free(glob, vmw_tt->sg_alloc_size); +	return ret; +} -	return vmw_gmr_bind(vmw_be->dev_priv, vmw_be->pages, -			    vmw_be->num_pages, vmw_be->gmr_id); +/** + * vmw_ttm_unmap_dma - Tear down any TTM page device mappings + * + * @vmw_tt: Pointer to a struct vmw_ttm_tt + * + * Tear down any previously set up device DMA mappings and free + * any storage space allocated for them. If there are no mappings set up, + * this function is a NOP. + */ +static void vmw_ttm_unmap_dma(struct vmw_ttm_tt *vmw_tt) +{ +	struct vmw_private *dev_priv = vmw_tt->dev_priv; + +	if (!vmw_tt->vsgt.sgt) +		return; + +	switch (dev_priv->map_mode) { +	case vmw_dma_map_bind: +	case vmw_dma_map_populate: +		vmw_ttm_unmap_from_dma(vmw_tt); +		sg_free_table(vmw_tt->vsgt.sgt); +		vmw_tt->vsgt.sgt = NULL; +		ttm_mem_global_free(vmw_mem_glob(dev_priv), +				    vmw_tt->sg_alloc_size); +		break; +	default: +		break; +	} +	vmw_tt->mapped = false;  } -static int vmw_ttm_unbind(struct ttm_backend *backend) + +/** + * vmw_bo_map_dma - Make sure buffer object pages are visible to the device + * + * @bo: Pointer to a struct ttm_buffer_object + * + * Wrapper around vmw_ttm_map_dma, that takes a TTM buffer object pointer + * instead of a pointer to a struct vmw_ttm_backend as argument. + * Note that the buffer object must be either pinned or reserved before + * calling this function. + */ +int vmw_bo_map_dma(struct ttm_buffer_object *bo)  { -	struct vmw_ttm_backend *vmw_be = -	    container_of(backend, struct vmw_ttm_backend, backend); +	struct vmw_ttm_tt *vmw_tt = +		container_of(bo->ttm, struct vmw_ttm_tt, dma_ttm.ttm); -	vmw_gmr_unbind(vmw_be->dev_priv, vmw_be->gmr_id); +	return vmw_ttm_map_dma(vmw_tt); +} + + +/** + * vmw_bo_unmap_dma - Make sure buffer object pages are visible to the device + * + * @bo: Pointer to a struct ttm_buffer_object + * + * Wrapper around vmw_ttm_unmap_dma, that takes a TTM buffer object pointer + * instead of a pointer to a struct vmw_ttm_backend as argument. + */ +void vmw_bo_unmap_dma(struct ttm_buffer_object *bo) +{ +	struct vmw_ttm_tt *vmw_tt = +		container_of(bo->ttm, struct vmw_ttm_tt, dma_ttm.ttm); + +	vmw_ttm_unmap_dma(vmw_tt); +} + + +/** + * vmw_bo_sg_table - Return a struct vmw_sg_table object for a + * TTM buffer object + * + * @bo: Pointer to a struct ttm_buffer_object + * + * Returns a pointer to a struct vmw_sg_table object. The object should + * not be freed after use. + * Note that for the device addresses to be valid, the buffer object must + * either be reserved or pinned. + */ +const struct vmw_sg_table *vmw_bo_sg_table(struct ttm_buffer_object *bo) +{ +	struct vmw_ttm_tt *vmw_tt = +		container_of(bo->ttm, struct vmw_ttm_tt, dma_ttm.ttm); + +	return &vmw_tt->vsgt; +} + + +static int vmw_ttm_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem) +{ +	struct vmw_ttm_tt *vmw_be = +		container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); +	int ret; + +	ret = vmw_ttm_map_dma(vmw_be); +	if (unlikely(ret != 0)) +		return ret; + +	vmw_be->gmr_id = bo_mem->start; +	vmw_be->mem_type = bo_mem->mem_type; + +	switch (bo_mem->mem_type) { +	case VMW_PL_GMR: +		return vmw_gmr_bind(vmw_be->dev_priv, &vmw_be->vsgt, +				    ttm->num_pages, vmw_be->gmr_id); +	case VMW_PL_MOB: +		if (unlikely(vmw_be->mob == NULL)) { +			vmw_be->mob = +				vmw_mob_create(ttm->num_pages); +			if (unlikely(vmw_be->mob == NULL)) +				return -ENOMEM; +		} + +		return vmw_mob_bind(vmw_be->dev_priv, vmw_be->mob, +				    &vmw_be->vsgt, ttm->num_pages, +				    vmw_be->gmr_id); +	default: +		BUG(); +	}  	return 0;  } -static void vmw_ttm_clear(struct ttm_backend *backend) +static int vmw_ttm_unbind(struct ttm_tt *ttm)  { -	struct vmw_ttm_backend *vmw_be = -		container_of(backend, struct vmw_ttm_backend, backend); +	struct vmw_ttm_tt *vmw_be = +		container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); + +	switch (vmw_be->mem_type) { +	case VMW_PL_GMR: +		vmw_gmr_unbind(vmw_be->dev_priv, vmw_be->gmr_id); +		break; +	case VMW_PL_MOB: +		vmw_mob_unbind(vmw_be->dev_priv, vmw_be->mob); +		break; +	default: +		BUG(); +	} + +	if (vmw_be->dev_priv->map_mode == vmw_dma_map_bind) +		vmw_ttm_unmap_dma(vmw_be); -	vmw_be->pages = NULL; -	vmw_be->num_pages = 0; +	return 0;  } -static void vmw_ttm_destroy(struct ttm_backend *backend) + +static void vmw_ttm_destroy(struct ttm_tt *ttm)  { -	struct vmw_ttm_backend *vmw_be = -	    container_of(backend, struct vmw_ttm_backend, backend); +	struct vmw_ttm_tt *vmw_be = +		container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); + +	vmw_ttm_unmap_dma(vmw_be); +	if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent) +		ttm_dma_tt_fini(&vmw_be->dma_ttm); +	else +		ttm_tt_fini(ttm); + +	if (vmw_be->mob) +		vmw_mob_destroy(vmw_be->mob);  	kfree(vmw_be);  } + +static int vmw_ttm_populate(struct ttm_tt *ttm) +{ +	struct vmw_ttm_tt *vmw_tt = +		container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); +	struct vmw_private *dev_priv = vmw_tt->dev_priv; +	struct ttm_mem_global *glob = vmw_mem_glob(dev_priv); +	int ret; + +	if (ttm->state != tt_unpopulated) +		return 0; + +	if (dev_priv->map_mode == vmw_dma_alloc_coherent) { +		size_t size = +			ttm_round_pot(ttm->num_pages * sizeof(dma_addr_t)); +		ret = ttm_mem_global_alloc(glob, size, false, true); +		if (unlikely(ret != 0)) +			return ret; + +		ret = ttm_dma_populate(&vmw_tt->dma_ttm, dev_priv->dev->dev); +		if (unlikely(ret != 0)) +			ttm_mem_global_free(glob, size); +	} else +		ret = ttm_pool_populate(ttm); + +	return ret; +} + +static void vmw_ttm_unpopulate(struct ttm_tt *ttm) +{ +	struct vmw_ttm_tt *vmw_tt = container_of(ttm, struct vmw_ttm_tt, +						 dma_ttm.ttm); +	struct vmw_private *dev_priv = vmw_tt->dev_priv; +	struct ttm_mem_global *glob = vmw_mem_glob(dev_priv); + + +	if (vmw_tt->mob) { +		vmw_mob_destroy(vmw_tt->mob); +		vmw_tt->mob = NULL; +	} + +	vmw_ttm_unmap_dma(vmw_tt); +	if (dev_priv->map_mode == vmw_dma_alloc_coherent) { +		size_t size = +			ttm_round_pot(ttm->num_pages * sizeof(dma_addr_t)); + +		ttm_dma_unpopulate(&vmw_tt->dma_ttm, dev_priv->dev->dev); +		ttm_mem_global_free(glob, size); +	} else +		ttm_pool_unpopulate(ttm); +} +  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) +static struct ttm_tt *vmw_ttm_tt_create(struct ttm_bo_device *bdev, +				 unsigned long size, uint32_t page_flags, +				 struct page *dummy_read_page)  { -	struct vmw_ttm_backend *vmw_be; +	struct vmw_ttm_tt *vmw_be; +	int ret; -	vmw_be = kmalloc(sizeof(*vmw_be), GFP_KERNEL); +	vmw_be = kzalloc(sizeof(*vmw_be), GFP_KERNEL);  	if (!vmw_be)  		return NULL; -	vmw_be->backend.func = &vmw_ttm_func; +	vmw_be->dma_ttm.ttm.func = &vmw_ttm_func;  	vmw_be->dev_priv = container_of(bdev, struct vmw_private, bdev); - -	return &vmw_be->backend; +	vmw_be->mob = NULL; + +	if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent) +		ret = ttm_dma_tt_init(&vmw_be->dma_ttm, bdev, size, page_flags, +				      dummy_read_page); +	else +		ret = ttm_tt_init(&vmw_be->dma_ttm.ttm, bdev, size, page_flags, +				  dummy_read_page); +	if (unlikely(ret != 0)) +		goto out_no_init; + +	return &vmw_be->dma_ttm.ttm; +out_no_init: +	kfree(vmw_be); +	return NULL;  } -int vmw_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) +static 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, +static int vmw_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,  		      struct ttm_mem_type_manager *man)  {  	switch (type) { @@ -197,6 +697,7 @@ int vmw_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,  		man->default_caching = TTM_PL_FLAG_CACHED;  		break;  	case VMW_PL_GMR: +	case VMW_PL_MOB:  		/*  		 * "Guest Memory Regions" is an aperture like feature with  		 *  one slot per bo. There is an upper limit of the number of @@ -215,19 +716,18 @@ int vmw_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,  	return 0;  } -void vmw_evict_flags(struct ttm_buffer_object *bo, +static 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; +	struct ttm_object_file *tfile = +		vmw_fpriv((struct drm_file *)filp->private_data)->tfile; + +	return vmw_user_dmabuf_verify_access(bo, tfile);  }  static int vmw_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) @@ -245,6 +745,7 @@ static int vmw_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg  	switch (mem->mem_type) {  	case TTM_PL_SYSTEM:  	case VMW_PL_GMR: +	case VMW_PL_MOB:  		return 0;  	case TTM_PL_VRAM:  		mem->bus.offset = mem->start << PAGE_SHIFT; @@ -273,43 +774,73 @@ static int vmw_ttm_fault_reserve_notify(struct ttm_buffer_object *bo)  static void *vmw_sync_obj_ref(void *sync_obj)  { -	return sync_obj; + +	return (void *) +		vmw_fence_obj_reference((struct vmw_fence_obj *) sync_obj);  }  static void vmw_sync_obj_unref(void **sync_obj)  { -	*sync_obj = NULL; +	vmw_fence_obj_unreference((struct vmw_fence_obj **) sync_obj);  } -static int vmw_sync_obj_flush(void *sync_obj, void *sync_arg) +static int vmw_sync_obj_flush(void *sync_obj)  { -	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); +	vmw_fence_obj_flush((struct vmw_fence_obj *) sync_obj);  	return 0;  } -static bool vmw_sync_obj_signaled(void *sync_obj, void *sync_arg) +static bool vmw_sync_obj_signaled(void *sync_obj)  { -	struct vmw_private *dev_priv = (struct vmw_private *)sync_arg; -	uint32_t sequence = (unsigned long) sync_obj; +	return	vmw_fence_obj_signaled((struct vmw_fence_obj *) sync_obj, +				       DRM_VMW_FENCE_FLAG_EXEC); -	return vmw_fence_signaled(dev_priv, sequence);  } -static int vmw_sync_obj_wait(void *sync_obj, void *sync_arg, -			     bool lazy, bool interruptible) +static int vmw_sync_obj_wait(void *sync_obj, bool lazy, bool interruptible)  { -	struct vmw_private *dev_priv = (struct vmw_private *)sync_arg; -	uint32_t sequence = (unsigned long) sync_obj; +	return vmw_fence_obj_wait((struct vmw_fence_obj *) sync_obj, +				  DRM_VMW_FENCE_FLAG_EXEC, +				  lazy, interruptible, +				  VMW_FENCE_WAIT_TIMEOUT); +} -	return vmw_wait_fence(dev_priv, false, sequence, false, 3*HZ); +/** + * vmw_move_notify - TTM move_notify_callback + * + * @bo:             The TTM buffer object about to move. + * @mem:            The truct ttm_mem_reg indicating to what memory + *                  region the move is taking place. + * + * Calls move_notify for all subsystems needing it. + * (currently only resources). + */ +static void vmw_move_notify(struct ttm_buffer_object *bo, +			    struct ttm_mem_reg *mem) +{ +	vmw_resource_move_notify(bo, mem);  } + +/** + * vmw_swap_notify - TTM move_notify_callback + * + * @bo:             The TTM buffer object about to be swapped out. + */ +static void vmw_swap_notify(struct ttm_buffer_object *bo) +{ +	struct ttm_bo_device *bdev = bo->bdev; + +	spin_lock(&bdev->fence_lock); +	ttm_bo_wait(bo, false, false, false); +	spin_unlock(&bdev->fence_lock); +} + +  struct ttm_bo_driver vmw_bo_driver = { -	.create_ttm_backend_entry = vmw_ttm_backend_init, +	.ttm_tt_create = &vmw_ttm_tt_create, +	.ttm_tt_populate = &vmw_ttm_populate, +	.ttm_tt_unpopulate = &vmw_ttm_unpopulate,  	.invalidate_caches = vmw_invalidate_caches,  	.init_mem_type = vmw_init_mem_type,  	.evict_flags = vmw_evict_flags, @@ -320,8 +851,8 @@ struct ttm_bo_driver vmw_bo_driver = {  	.sync_obj_flush = vmw_sync_obj_flush,  	.sync_obj_unref = vmw_sync_obj_unref,  	.sync_obj_ref = vmw_sync_obj_ref, -	.move_notify = NULL, -	.swap_notify = NULL, +	.move_notify = vmw_move_notify, +	.swap_notify = vmw_swap_notify,  	.fault_reserve_notify = &vmw_ttm_fault_reserve_notify,  	.io_mem_reserve = &vmw_ttm_io_mem_reserve,  	.io_mem_free = &vmw_ttm_io_mem_free, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c new file mode 100644 index 00000000000..8bb26dcd9ea --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c @@ -0,0 +1,903 @@ +/************************************************************************** + * + * Copyright © 2009-2012 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_resource_priv.h" +#include "ttm/ttm_placement.h" + +struct vmw_user_context { +	struct ttm_base_object base; +	struct vmw_resource res; +	struct vmw_ctx_binding_state cbs; +}; + + + +typedef int (*vmw_scrub_func)(struct vmw_ctx_bindinfo *, bool); + +static void vmw_user_context_free(struct vmw_resource *res); +static struct vmw_resource * +vmw_user_context_base_to_res(struct ttm_base_object *base); + +static int vmw_gb_context_create(struct vmw_resource *res); +static int vmw_gb_context_bind(struct vmw_resource *res, +			       struct ttm_validate_buffer *val_buf); +static int vmw_gb_context_unbind(struct vmw_resource *res, +				 bool readback, +				 struct ttm_validate_buffer *val_buf); +static int vmw_gb_context_destroy(struct vmw_resource *res); +static int vmw_context_scrub_shader(struct vmw_ctx_bindinfo *bi, bool rebind); +static int vmw_context_scrub_render_target(struct vmw_ctx_bindinfo *bi, +					   bool rebind); +static int vmw_context_scrub_texture(struct vmw_ctx_bindinfo *bi, bool rebind); +static void vmw_context_binding_state_scrub(struct vmw_ctx_binding_state *cbs); +static void vmw_context_binding_state_kill(struct vmw_ctx_binding_state *cbs); +static uint64_t vmw_user_context_size; + +static const struct vmw_user_resource_conv user_context_conv = { +	.object_type = VMW_RES_CONTEXT, +	.base_obj_to_res = vmw_user_context_base_to_res, +	.res_free = vmw_user_context_free +}; + +const struct vmw_user_resource_conv *user_context_converter = +	&user_context_conv; + + +static const struct vmw_res_func vmw_legacy_context_func = { +	.res_type = vmw_res_context, +	.needs_backup = false, +	.may_evict = false, +	.type_name = "legacy contexts", +	.backup_placement = NULL, +	.create = NULL, +	.destroy = NULL, +	.bind = NULL, +	.unbind = NULL +}; + +static const struct vmw_res_func vmw_gb_context_func = { +	.res_type = vmw_res_context, +	.needs_backup = true, +	.may_evict = true, +	.type_name = "guest backed contexts", +	.backup_placement = &vmw_mob_placement, +	.create = vmw_gb_context_create, +	.destroy = vmw_gb_context_destroy, +	.bind = vmw_gb_context_bind, +	.unbind = vmw_gb_context_unbind +}; + +static const vmw_scrub_func vmw_scrub_funcs[vmw_ctx_binding_max] = { +	[vmw_ctx_binding_shader] = vmw_context_scrub_shader, +	[vmw_ctx_binding_rt] = vmw_context_scrub_render_target, +	[vmw_ctx_binding_tex] = vmw_context_scrub_texture }; + +/** + * 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; + + +	if (res->func->destroy == vmw_gb_context_destroy) { +		mutex_lock(&dev_priv->cmdbuf_mutex); +		mutex_lock(&dev_priv->binding_mutex); +		(void) vmw_context_binding_state_kill +			(&container_of(res, struct vmw_user_context, res)->cbs); +		(void) vmw_gb_context_destroy(res); +		mutex_unlock(&dev_priv->binding_mutex); +		if (dev_priv->pinned_bo != NULL && +		    !dev_priv->query_cid_valid) +			__vmw_execbuf_release_pinned_bo(dev_priv, NULL); +		mutex_unlock(&dev_priv->cmdbuf_mutex); +		return; +	} + +	vmw_execbuf_release_pinned_bo(dev_priv); +	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)); +	vmw_3d_resource_dec(dev_priv, false); +} + +static int vmw_gb_context_init(struct vmw_private *dev_priv, +			       struct vmw_resource *res, +			       void (*res_free) (struct vmw_resource *res)) +{ +	int ret; +	struct vmw_user_context *uctx = +		container_of(res, struct vmw_user_context, res); + +	ret = vmw_resource_init(dev_priv, res, true, +				res_free, &vmw_gb_context_func); +	res->backup_size = SVGA3D_CONTEXT_DATA_SIZE; + +	if (unlikely(ret != 0)) { +		if (res_free) +			res_free(res); +		else +			kfree(res); +		return ret; +	} + +	memset(&uctx->cbs, 0, sizeof(uctx->cbs)); +	INIT_LIST_HEAD(&uctx->cbs.list); + +	vmw_resource_activate(res, vmw_hw_context_destroy); +	return 0; +} + +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; + +	if (dev_priv->has_mob) +		return vmw_gb_context_init(dev_priv, res, res_free); + +	ret = vmw_resource_init(dev_priv, res, false, +				res_free, &vmw_legacy_context_func); + +	if (unlikely(ret != 0)) { +		DRM_ERROR("Failed to allocate a resource id.\n"); +		goto out_early; +	} + +	if (unlikely(res->id >= SVGA3D_MAX_CONTEXT_IDS)) { +		DRM_ERROR("Out of hw context ids.\n"); +		vmw_resource_unreference(&res); +		return -ENOMEM; +	} + +	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)); +	(void) vmw_3d_resource_inc(dev_priv, false); +	vmw_resource_activate(res, vmw_hw_context_destroy); +	return 0; + +out_early: +	if (res_free == NULL) +		kfree(res); +	else +		res_free(res); +	return ret; +} + +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; +} + + +static int vmw_gb_context_create(struct vmw_resource *res) +{ +	struct vmw_private *dev_priv = res->dev_priv; +	int ret; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdDefineGBContext body; +	} *cmd; + +	if (likely(res->id != -1)) +		return 0; + +	ret = vmw_resource_alloc_id(res); +	if (unlikely(ret != 0)) { +		DRM_ERROR("Failed to allocate a context id.\n"); +		goto out_no_id; +	} + +	if (unlikely(res->id >= VMWGFX_NUM_GB_CONTEXT)) { +		ret = -EBUSY; +		goto out_no_fifo; +	} + +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for context " +			  "creation.\n"); +		ret = -ENOMEM; +		goto out_no_fifo; +	} + +	cmd->header.id = SVGA_3D_CMD_DEFINE_GB_CONTEXT; +	cmd->header.size = sizeof(cmd->body); +	cmd->body.cid = res->id; +	vmw_fifo_commit(dev_priv, sizeof(*cmd)); +	(void) vmw_3d_resource_inc(dev_priv, false); + +	return 0; + +out_no_fifo: +	vmw_resource_release_id(res); +out_no_id: +	return ret; +} + +static int vmw_gb_context_bind(struct vmw_resource *res, +			       struct ttm_validate_buffer *val_buf) +{ +	struct vmw_private *dev_priv = res->dev_priv; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdBindGBContext body; +	} *cmd; +	struct ttm_buffer_object *bo = val_buf->bo; + +	BUG_ON(bo->mem.mem_type != VMW_PL_MOB); + +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for context " +			  "binding.\n"); +		return -ENOMEM; +	} + +	cmd->header.id = SVGA_3D_CMD_BIND_GB_CONTEXT; +	cmd->header.size = sizeof(cmd->body); +	cmd->body.cid = res->id; +	cmd->body.mobid = bo->mem.start; +	cmd->body.validContents = res->backup_dirty; +	res->backup_dirty = false; +	vmw_fifo_commit(dev_priv, sizeof(*cmd)); + +	return 0; +} + +static int vmw_gb_context_unbind(struct vmw_resource *res, +				 bool readback, +				 struct ttm_validate_buffer *val_buf) +{ +	struct vmw_private *dev_priv = res->dev_priv; +	struct ttm_buffer_object *bo = val_buf->bo; +	struct vmw_fence_obj *fence; +	struct vmw_user_context *uctx = +		container_of(res, struct vmw_user_context, res); + +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdReadbackGBContext body; +	} *cmd1; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdBindGBContext body; +	} *cmd2; +	uint32_t submit_size; +	uint8_t *cmd; + + +	BUG_ON(bo->mem.mem_type != VMW_PL_MOB); + +	mutex_lock(&dev_priv->binding_mutex); +	vmw_context_binding_state_scrub(&uctx->cbs); + +	submit_size = sizeof(*cmd2) + (readback ? sizeof(*cmd1) : 0); + +	cmd = vmw_fifo_reserve(dev_priv, submit_size); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for context " +			  "unbinding.\n"); +		mutex_unlock(&dev_priv->binding_mutex); +		return -ENOMEM; +	} + +	cmd2 = (void *) cmd; +	if (readback) { +		cmd1 = (void *) cmd; +		cmd1->header.id = SVGA_3D_CMD_READBACK_GB_CONTEXT; +		cmd1->header.size = sizeof(cmd1->body); +		cmd1->body.cid = res->id; +		cmd2 = (void *) (&cmd1[1]); +	} +	cmd2->header.id = SVGA_3D_CMD_BIND_GB_CONTEXT; +	cmd2->header.size = sizeof(cmd2->body); +	cmd2->body.cid = res->id; +	cmd2->body.mobid = SVGA3D_INVALID_ID; + +	vmw_fifo_commit(dev_priv, submit_size); +	mutex_unlock(&dev_priv->binding_mutex); + +	/* +	 * Create a fence object and fence the backup buffer. +	 */ + +	(void) vmw_execbuf_fence_commands(NULL, dev_priv, +					  &fence, NULL); + +	vmw_fence_single_bo(bo, fence); + +	if (likely(fence != NULL)) +		vmw_fence_obj_unreference(&fence); + +	return 0; +} + +static int vmw_gb_context_destroy(struct vmw_resource *res) +{ +	struct vmw_private *dev_priv = res->dev_priv; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdDestroyGBContext body; +	} *cmd; + +	if (likely(res->id == -1)) +		return 0; + +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for context " +			  "destruction.\n"); +		return -ENOMEM; +	} + +	cmd->header.id = SVGA_3D_CMD_DESTROY_GB_CONTEXT; +	cmd->header.size = sizeof(cmd->body); +	cmd->body.cid = res->id; +	vmw_fifo_commit(dev_priv, sizeof(*cmd)); +	if (dev_priv->query_cid == res->id) +		dev_priv->query_cid_valid = false; +	vmw_resource_release_id(res); +	vmw_3d_resource_dec(dev_priv, false); + +	return 0; +} + +/** + * User-space context management: + */ + +static struct vmw_resource * +vmw_user_context_base_to_res(struct ttm_base_object *base) +{ +	return &(container_of(base, struct vmw_user_context, base)->res); +} + +static void vmw_user_context_free(struct vmw_resource *res) +{ +	struct vmw_user_context *ctx = +	    container_of(res, struct vmw_user_context, res); +	struct vmw_private *dev_priv = res->dev_priv; + +	ttm_base_object_kfree(ctx, base); +	ttm_mem_global_free(vmw_mem_glob(dev_priv), +			    vmw_user_context_size); +} + +/** + * 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 drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data; +	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + +	return ttm_ref_object_base_unref(tfile, arg->cid, TTM_REF_USAGE); +} + +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; +	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; + + +	/* +	 * Approximate idr memory usage with 128 bytes. It will be limited +	 * by maximum number_of contexts anyway. +	 */ + +	if (unlikely(vmw_user_context_size == 0)) +		vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128; + +	ret = ttm_read_lock(&dev_priv->reservation_sem, true); +	if (unlikely(ret != 0)) +		return ret; + +	ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), +				   vmw_user_context_size, +				   false, true); +	if (unlikely(ret != 0)) { +		if (ret != -ERESTARTSYS) +			DRM_ERROR("Out of graphics memory for context" +				  " creation.\n"); +		goto out_unlock; +	} + +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); +	if (unlikely(ctx == NULL)) { +		ttm_mem_global_free(vmw_mem_glob(dev_priv), +				    vmw_user_context_size); +		ret = -ENOMEM; +		goto out_unlock; +	} + +	res = &ctx->res; +	ctx->base.shareable = false; +	ctx->base.tfile = NULL; + +	/* +	 * From here on, the destructor takes over resource freeing. +	 */ + +	ret = vmw_context_init(dev_priv, res, vmw_user_context_free); +	if (unlikely(ret != 0)) +		goto out_unlock; + +	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 = ctx->base.hash.key; +out_err: +	vmw_resource_unreference(&res); +out_unlock: +	ttm_read_unlock(&dev_priv->reservation_sem); +	return ret; + +} + +/** + * vmw_context_scrub_shader - scrub a shader binding from a context. + * + * @bi: single binding information. + * @rebind: Whether to issue a bind instead of scrub command. + */ +static int vmw_context_scrub_shader(struct vmw_ctx_bindinfo *bi, bool rebind) +{ +	struct vmw_private *dev_priv = bi->ctx->dev_priv; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdSetShader body; +	} *cmd; + +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for shader " +			  "unbinding.\n"); +		return -ENOMEM; +	} + +	cmd->header.id = SVGA_3D_CMD_SET_SHADER; +	cmd->header.size = sizeof(cmd->body); +	cmd->body.cid = bi->ctx->id; +	cmd->body.type = bi->i1.shader_type; +	cmd->body.shid = ((rebind) ? bi->res->id : SVGA3D_INVALID_ID); +	vmw_fifo_commit(dev_priv, sizeof(*cmd)); + +	return 0; +} + +/** + * vmw_context_scrub_render_target - scrub a render target binding + * from a context. + * + * @bi: single binding information. + * @rebind: Whether to issue a bind instead of scrub command. + */ +static int vmw_context_scrub_render_target(struct vmw_ctx_bindinfo *bi, +					   bool rebind) +{ +	struct vmw_private *dev_priv = bi->ctx->dev_priv; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdSetRenderTarget body; +	} *cmd; + +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for render target " +			  "unbinding.\n"); +		return -ENOMEM; +	} + +	cmd->header.id = SVGA_3D_CMD_SETRENDERTARGET; +	cmd->header.size = sizeof(cmd->body); +	cmd->body.cid = bi->ctx->id; +	cmd->body.type = bi->i1.rt_type; +	cmd->body.target.sid = ((rebind) ? bi->res->id : SVGA3D_INVALID_ID); +	cmd->body.target.face = 0; +	cmd->body.target.mipmap = 0; +	vmw_fifo_commit(dev_priv, sizeof(*cmd)); + +	return 0; +} + +/** + * vmw_context_scrub_texture - scrub a texture binding from a context. + * + * @bi: single binding information. + * @rebind: Whether to issue a bind instead of scrub command. + * + * TODO: Possibly complement this function with a function that takes + * a list of texture bindings and combines them to a single command. + */ +static int vmw_context_scrub_texture(struct vmw_ctx_bindinfo *bi, +				     bool rebind) +{ +	struct vmw_private *dev_priv = bi->ctx->dev_priv; +	struct { +		SVGA3dCmdHeader header; +		struct { +			SVGA3dCmdSetTextureState c; +			SVGA3dTextureState s1; +		} body; +	} *cmd; + +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for texture " +			  "unbinding.\n"); +		return -ENOMEM; +	} + + +	cmd->header.id = SVGA_3D_CMD_SETTEXTURESTATE; +	cmd->header.size = sizeof(cmd->body); +	cmd->body.c.cid = bi->ctx->id; +	cmd->body.s1.stage = bi->i1.texture_stage; +	cmd->body.s1.name = SVGA3D_TS_BIND_TEXTURE; +	cmd->body.s1.value = ((rebind) ? bi->res->id : SVGA3D_INVALID_ID); +	vmw_fifo_commit(dev_priv, sizeof(*cmd)); + +	return 0; +} + +/** + * vmw_context_binding_drop: Stop tracking a context binding + * + * @cb: Pointer to binding tracker storage. + * + * Stops tracking a context binding, and re-initializes its storage. + * Typically used when the context binding is replaced with a binding to + * another (or the same, for that matter) resource. + */ +static void vmw_context_binding_drop(struct vmw_ctx_binding *cb) +{ +	list_del(&cb->ctx_list); +	if (!list_empty(&cb->res_list)) +		list_del(&cb->res_list); +	cb->bi.ctx = NULL; +} + +/** + * vmw_context_binding_add: Start tracking a context binding + * + * @cbs: Pointer to the context binding state tracker. + * @bi: Information about the binding to track. + * + * Performs basic checks on the binding to make sure arguments are within + * bounds and then starts tracking the binding in the context binding + * state structure @cbs. + */ +int vmw_context_binding_add(struct vmw_ctx_binding_state *cbs, +			    const struct vmw_ctx_bindinfo *bi) +{ +	struct vmw_ctx_binding *loc; + +	switch (bi->bt) { +	case vmw_ctx_binding_rt: +		if (unlikely((unsigned)bi->i1.rt_type >= SVGA3D_RT_MAX)) { +			DRM_ERROR("Illegal render target type %u.\n", +				  (unsigned) bi->i1.rt_type); +			return -EINVAL; +		} +		loc = &cbs->render_targets[bi->i1.rt_type]; +		break; +	case vmw_ctx_binding_tex: +		if (unlikely((unsigned)bi->i1.texture_stage >= +			     SVGA3D_NUM_TEXTURE_UNITS)) { +			DRM_ERROR("Illegal texture/sampler unit %u.\n", +				  (unsigned) bi->i1.texture_stage); +			return -EINVAL; +		} +		loc = &cbs->texture_units[bi->i1.texture_stage]; +		break; +	case vmw_ctx_binding_shader: +		if (unlikely((unsigned)bi->i1.shader_type >= +			     SVGA3D_SHADERTYPE_MAX)) { +			DRM_ERROR("Illegal shader type %u.\n", +				  (unsigned) bi->i1.shader_type); +			return -EINVAL; +		} +		loc = &cbs->shaders[bi->i1.shader_type]; +		break; +	default: +		BUG(); +	} + +	if (loc->bi.ctx != NULL) +		vmw_context_binding_drop(loc); + +	loc->bi = *bi; +	loc->bi.scrubbed = false; +	list_add_tail(&loc->ctx_list, &cbs->list); +	INIT_LIST_HEAD(&loc->res_list); + +	return 0; +} + +/** + * vmw_context_binding_transfer: Transfer a context binding tracking entry. + * + * @cbs: Pointer to the persistent context binding state tracker. + * @bi: Information about the binding to track. + * + */ +static void vmw_context_binding_transfer(struct vmw_ctx_binding_state *cbs, +					 const struct vmw_ctx_bindinfo *bi) +{ +	struct vmw_ctx_binding *loc; + +	switch (bi->bt) { +	case vmw_ctx_binding_rt: +		loc = &cbs->render_targets[bi->i1.rt_type]; +		break; +	case vmw_ctx_binding_tex: +		loc = &cbs->texture_units[bi->i1.texture_stage]; +		break; +	case vmw_ctx_binding_shader: +		loc = &cbs->shaders[bi->i1.shader_type]; +		break; +	default: +		BUG(); +	} + +	if (loc->bi.ctx != NULL) +		vmw_context_binding_drop(loc); + +	if (bi->res != NULL) { +		loc->bi = *bi; +		list_add_tail(&loc->ctx_list, &cbs->list); +		list_add_tail(&loc->res_list, &bi->res->binding_head); +	} +} + +/** + * vmw_context_binding_kill - Kill a binding on the device + * and stop tracking it. + * + * @cb: Pointer to binding tracker storage. + * + * Emits FIFO commands to scrub a binding represented by @cb. + * Then stops tracking the binding and re-initializes its storage. + */ +static void vmw_context_binding_kill(struct vmw_ctx_binding *cb) +{ +	if (!cb->bi.scrubbed) { +		(void) vmw_scrub_funcs[cb->bi.bt](&cb->bi, false); +		cb->bi.scrubbed = true; +	} +	vmw_context_binding_drop(cb); +} + +/** + * vmw_context_binding_state_kill - Kill all bindings associated with a + * struct vmw_ctx_binding state structure, and re-initialize the structure. + * + * @cbs: Pointer to the context binding state tracker. + * + * Emits commands to scrub all bindings associated with the + * context binding state tracker. Then re-initializes the whole structure. + */ +static void vmw_context_binding_state_kill(struct vmw_ctx_binding_state *cbs) +{ +	struct vmw_ctx_binding *entry, *next; + +	list_for_each_entry_safe(entry, next, &cbs->list, ctx_list) +		vmw_context_binding_kill(entry); +} + +/** + * vmw_context_binding_state_scrub - Scrub all bindings associated with a + * struct vmw_ctx_binding state structure. + * + * @cbs: Pointer to the context binding state tracker. + * + * Emits commands to scrub all bindings associated with the + * context binding state tracker. + */ +static void vmw_context_binding_state_scrub(struct vmw_ctx_binding_state *cbs) +{ +	struct vmw_ctx_binding *entry; + +	list_for_each_entry(entry, &cbs->list, ctx_list) { +		if (!entry->bi.scrubbed) { +			(void) vmw_scrub_funcs[entry->bi.bt](&entry->bi, false); +			entry->bi.scrubbed = true; +		} +	} +} + +/** + * vmw_context_binding_res_list_kill - Kill all bindings on a + * resource binding list + * + * @head: list head of resource binding list + * + * Kills all bindings associated with a specific resource. Typically + * called before the resource is destroyed. + */ +void vmw_context_binding_res_list_kill(struct list_head *head) +{ +	struct vmw_ctx_binding *entry, *next; + +	list_for_each_entry_safe(entry, next, head, res_list) +		vmw_context_binding_kill(entry); +} + +/** + * vmw_context_binding_res_list_scrub - Scrub all bindings on a + * resource binding list + * + * @head: list head of resource binding list + * + * Scrub all bindings associated with a specific resource. Typically + * called before the resource is evicted. + */ +void vmw_context_binding_res_list_scrub(struct list_head *head) +{ +	struct vmw_ctx_binding *entry; + +	list_for_each_entry(entry, head, res_list) { +		if (!entry->bi.scrubbed) { +			(void) vmw_scrub_funcs[entry->bi.bt](&entry->bi, false); +			entry->bi.scrubbed = true; +		} +	} +} + +/** + * vmw_context_binding_state_transfer - Commit staged binding info + * + * @ctx: Pointer to context to commit the staged binding info to. + * @from: Staged binding info built during execbuf. + * + * Transfers binding info from a temporary structure to the persistent + * structure in the context. This can be done once commands + */ +void vmw_context_binding_state_transfer(struct vmw_resource *ctx, +					struct vmw_ctx_binding_state *from) +{ +	struct vmw_user_context *uctx = +		container_of(ctx, struct vmw_user_context, res); +	struct vmw_ctx_binding *entry, *next; + +	list_for_each_entry_safe(entry, next, &from->list, ctx_list) +		vmw_context_binding_transfer(&uctx->cbs, &entry->bi); +} + +/** + * vmw_context_rebind_all - Rebind all scrubbed bindings of a context + * + * @ctx: The context resource + * + * Walks through the context binding list and rebinds all scrubbed + * resources. + */ +int vmw_context_rebind_all(struct vmw_resource *ctx) +{ +	struct vmw_ctx_binding *entry; +	struct vmw_user_context *uctx = +		container_of(ctx, struct vmw_user_context, res); +	struct vmw_ctx_binding_state *cbs = &uctx->cbs; +	int ret; + +	list_for_each_entry(entry, &cbs->list, ctx_list) { +		if (likely(!entry->bi.scrubbed)) +			continue; + +		if (WARN_ON(entry->bi.res == NULL || entry->bi.res->id == +			    SVGA3D_INVALID_ID)) +			continue; + +		ret = vmw_scrub_funcs[entry->bi.bt](&entry->bi, true); +		if (unlikely(ret != 0)) +			return ret; + +		entry->bi.scrubbed = false; +	} + +	return 0; +} + +/** + * vmw_context_binding_list - Return a list of context bindings + * + * @ctx: The context resource + * + * Returns the current list of bindings of the given context. Note that + * this list becomes stale as soon as the dev_priv::binding_mutex is unlocked. + */ +struct list_head *vmw_context_binding_list(struct vmw_resource *ctx) +{ +	return &(container_of(ctx, struct vmw_user_context, res)->cbs.list); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c new file mode 100644 index 00000000000..70ddce8358b --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c @@ -0,0 +1,315 @@ +/************************************************************************** + * + * Copyright © 2011 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 <drm/ttm/ttm_placement.h> + +#include <drm/drmP.h> +#include "vmwgfx_drv.h" + + +/** + * vmw_dmabuf_to_placement - Validate a buffer to placement. + * + * @dev_priv:  Driver private. + * @buf:  DMA buffer to move. + * @pin:  Pin buffer if true. + * @interruptible:  Use interruptible wait. + * + * May only be called by the current master since it assumes that the + * master lock is the current master's lock. + * This function takes the master's lock in write mode. + * Flushes and unpins the query bo to avoid failures. + * + * Returns + *  -ERESTARTSYS if interrupted by a signal. + */ +int vmw_dmabuf_to_placement(struct vmw_private *dev_priv, +			    struct vmw_dma_buffer *buf, +			    struct ttm_placement *placement, +			    bool interruptible) +{ +	struct ttm_buffer_object *bo = &buf->base; +	int ret; + +	ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible); +	if (unlikely(ret != 0)) +		return ret; + +	vmw_execbuf_release_pinned_bo(dev_priv); + +	ret = ttm_bo_reserve(bo, interruptible, false, false, 0); +	if (unlikely(ret != 0)) +		goto err; + +	ret = ttm_bo_validate(bo, placement, interruptible, false); + +	ttm_bo_unreserve(bo); + +err: +	ttm_write_unlock(&dev_priv->reservation_sem); +	return ret; +} + +/** + * vmw_dmabuf_to_vram_or_gmr - Move a buffer to vram or gmr. + * + * May only be called by the current master since it assumes that the + * master lock is the current master's lock. + * This function takes the master's lock in write mode. + * Flushes and unpins the query bo if @pin == true to avoid failures. + * + * @dev_priv:  Driver private. + * @buf:  DMA buffer to move. + * @pin:  Pin buffer if true. + * @interruptible:  Use interruptible wait. + * + * Returns + * -ERESTARTSYS if interrupted by a signal. + */ +int vmw_dmabuf_to_vram_or_gmr(struct vmw_private *dev_priv, +			      struct vmw_dma_buffer *buf, +			      bool pin, bool interruptible) +{ +	struct ttm_buffer_object *bo = &buf->base; +	struct ttm_placement *placement; +	int ret; + +	ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible); +	if (unlikely(ret != 0)) +		return ret; + +	if (pin) +		vmw_execbuf_release_pinned_bo(dev_priv); + +	ret = ttm_bo_reserve(bo, interruptible, false, false, 0); +	if (unlikely(ret != 0)) +		goto err; + +	/** +	 * Put BO in VRAM if there is space, otherwise as a GMR. +	 * If there is no space in VRAM and GMR ids are all used up, +	 * start evicting GMRs to make room. If the DMA buffer can't be +	 * used as a GMR, this will return -ENOMEM. +	 */ + +	if (pin) +		placement = &vmw_vram_gmr_ne_placement; +	else +		placement = &vmw_vram_gmr_placement; + +	ret = ttm_bo_validate(bo, placement, interruptible, false); +	if (likely(ret == 0) || ret == -ERESTARTSYS) +		goto err_unreserve; + + +	/** +	 * If that failed, try VRAM again, this time evicting +	 * previous contents. +	 */ + +	if (pin) +		placement = &vmw_vram_ne_placement; +	else +		placement = &vmw_vram_placement; + +	ret = ttm_bo_validate(bo, placement, interruptible, false); + +err_unreserve: +	ttm_bo_unreserve(bo); +err: +	ttm_write_unlock(&dev_priv->reservation_sem); +	return ret; +} + +/** + * vmw_dmabuf_to_vram - Move a buffer to vram. + * + * May only be called by the current master since it assumes that the + * master lock is the current master's lock. + * This function takes the master's lock in write mode. + * + * @dev_priv:  Driver private. + * @buf:  DMA buffer to move. + * @pin:  Pin buffer in vram if true. + * @interruptible:  Use interruptible wait. + * + * Returns + * -ERESTARTSYS if interrupted by a signal. + */ +int vmw_dmabuf_to_vram(struct vmw_private *dev_priv, +		       struct vmw_dma_buffer *buf, +		       bool pin, bool interruptible) +{ +	struct ttm_placement *placement; + +	if (pin) +		placement = &vmw_vram_ne_placement; +	else +		placement = &vmw_vram_placement; + +	return vmw_dmabuf_to_placement(dev_priv, buf, +				       placement, +				       interruptible); +} + +/** + * vmw_dmabuf_to_start_of_vram - Move a buffer to start of vram. + * + * May only be called by the current master since it assumes that the + * master lock is the current master's lock. + * This function takes the master's lock in write mode. + * Flushes and unpins the query bo if @pin == true to avoid failures. + * + * @dev_priv:  Driver private. + * @buf:  DMA buffer to move. + * @pin:  Pin buffer in vram if true. + * @interruptible:  Use interruptible wait. + * + * Returns + * -ERESTARTSYS if interrupted by a signal. + */ +int vmw_dmabuf_to_start_of_vram(struct vmw_private *dev_priv, +				struct vmw_dma_buffer *buf, +				bool pin, bool interruptible) +{ +	struct ttm_buffer_object *bo = &buf->base; +	struct ttm_placement placement; +	int ret = 0; + +	if (pin) +		placement = vmw_vram_ne_placement; +	else +		placement = vmw_vram_placement; +	placement.lpfn = bo->num_pages; + +	ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible); +	if (unlikely(ret != 0)) +		return ret; + +	if (pin) +		vmw_execbuf_release_pinned_bo(dev_priv); +	ret = ttm_bo_reserve(bo, interruptible, false, false, 0); +	if (unlikely(ret != 0)) +		goto err_unlock; + +	/* Is this buffer already in vram but not at the start of it? */ +	if (bo->mem.mem_type == TTM_PL_VRAM && +	    bo->mem.start < bo->num_pages && +	    bo->mem.start > 0) +		(void) ttm_bo_validate(bo, &vmw_sys_placement, false, false); + +	ret = ttm_bo_validate(bo, &placement, interruptible, false); + +	/* For some reason we didn't up at the start of vram */ +	WARN_ON(ret == 0 && bo->offset != 0); + +	ttm_bo_unreserve(bo); +err_unlock: +	ttm_write_unlock(&dev_priv->reservation_sem); + +	return ret; +} + + +/** + * vmw_dmabuf_upin - Unpin the buffer given buffer, does not move the buffer. + * + * May only be called by the current master since it assumes that the + * master lock is the current master's lock. + * This function takes the master's lock in write mode. + * + * @dev_priv:  Driver private. + * @buf:  DMA buffer to unpin. + * @interruptible:  Use interruptible wait. + * + * Returns + * -ERESTARTSYS if interrupted by a signal. + */ +int vmw_dmabuf_unpin(struct vmw_private *dev_priv, +		     struct vmw_dma_buffer *buf, +		     bool interruptible) +{ +	/* +	 * We could in theory early out if the buffer is +	 * unpinned but we need to lock and reserve the buffer +	 * anyways so we don't gain much by that. +	 */ +	return vmw_dmabuf_to_placement(dev_priv, buf, +				       &vmw_evictable_placement, +				       interruptible); +} + + +/** + * vmw_bo_get_guest_ptr - Get the guest ptr representing the current placement + * of a buffer. + * + * @bo: Pointer to a struct ttm_buffer_object. Must be pinned or reserved. + * @ptr: SVGAGuestPtr returning the result. + */ +void vmw_bo_get_guest_ptr(const struct ttm_buffer_object *bo, +			  SVGAGuestPtr *ptr) +{ +	if (bo->mem.mem_type == TTM_PL_VRAM) { +		ptr->gmrId = SVGA_GMR_FRAMEBUFFER; +		ptr->offset = bo->offset; +	} else { +		ptr->gmrId = bo->mem.start; +		ptr->offset = 0; +	} +} + + +/** + * vmw_bo_pin - Pin or unpin a buffer object without moving it. + * + * @bo: The buffer object. Must be reserved. + * @pin: Whether to pin or unpin. + * + */ +void vmw_bo_pin(struct ttm_buffer_object *bo, bool pin) +{ +	uint32_t pl_flags; +	struct ttm_placement placement; +	uint32_t old_mem_type = bo->mem.mem_type; +	int ret; + +	lockdep_assert_held(&bo->resv->lock.base); + +	pl_flags = TTM_PL_FLAG_VRAM | VMW_PL_FLAG_GMR | VMW_PL_FLAG_MOB +		| TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED; +	if (pin) +		pl_flags |= TTM_PL_FLAG_NO_EVICT; + +	memset(&placement, 0, sizeof(placement)); +	placement.num_placement = 1; +	placement.placement = &pl_flags; + +	ret = ttm_bo_validate(bo, &placement, false, true); + +	BUG_ON(ret != 0 || bo->mem.mem_type != old_mem_type); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 10ca97ee020..246a62bab37 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -24,19 +24,25 @@   * USE OR OTHER DEALINGS IN THE SOFTWARE.   *   **************************************************************************/ +#include <linux/module.h> -#include "drmP.h" +#include <drm/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" +#include <drm/ttm/ttm_placement.h> +#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_object.h> +#include <drm/ttm/ttm_module.h> +#include <linux/dma_remapping.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 +#define VMW_MIN_INITIAL_WIDTH 800 +#define VMW_MIN_INITIAL_HEIGHT 600 + +  /**   * Fully encoded drm commands. Might move to vmw_drm.h   */ @@ -82,16 +88,45 @@  #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_GET_3D_CAP				\ +	DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_GET_3D_CAP,		\ +		 struct drm_vmw_get_3d_cap_arg)  #define DRM_IOCTL_VMW_FENCE_WAIT				\  	DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_FENCE_WAIT,		\  		 struct drm_vmw_fence_wait_arg) +#define DRM_IOCTL_VMW_FENCE_SIGNALED				\ +	DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_FENCE_SIGNALED,	\ +		 struct drm_vmw_fence_signaled_arg) +#define DRM_IOCTL_VMW_FENCE_UNREF				\ +	DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_FENCE_UNREF,		\ +		 struct drm_vmw_fence_arg) +#define DRM_IOCTL_VMW_FENCE_EVENT				\ +	DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_FENCE_EVENT,		\ +		 struct drm_vmw_fence_event_arg) +#define DRM_IOCTL_VMW_PRESENT					\ +	DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_PRESENT,		\ +		 struct drm_vmw_present_arg) +#define DRM_IOCTL_VMW_PRESENT_READBACK				\ +	DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_PRESENT_READBACK,	\ +		 struct drm_vmw_present_readback_arg)  #define DRM_IOCTL_VMW_UPDATE_LAYOUT				\ -	DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_UPDATE_LAYOUT,	\ +	DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UPDATE_LAYOUT,	\  		 struct drm_vmw_update_layout_arg) - +#define DRM_IOCTL_VMW_CREATE_SHADER				\ +	DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_CREATE_SHADER,	\ +		 struct drm_vmw_shader_create_arg) +#define DRM_IOCTL_VMW_UNREF_SHADER				\ +	DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_SHADER,	\ +		 struct drm_vmw_shader_arg) +#define DRM_IOCTL_VMW_GB_SURFACE_CREATE				\ +	DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_GB_SURFACE_CREATE,	\ +		 union drm_vmw_gb_surface_create_arg) +#define DRM_IOCTL_VMW_GB_SURFACE_REF				\ +	DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_GB_SURFACE_REF,	\ +		 union drm_vmw_gb_surface_reference_arg) +#define DRM_IOCTL_VMW_SYNCCPU					\ +	DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_SYNCCPU,		\ +		 struct drm_vmw_synccpu_arg)  /**   * The core DRM version of this macro doesn't account for @@ -105,13 +140,13 @@   * Ioctl definitions.   */ -static struct drm_ioctl_desc vmw_ioctls[] = { +static const struct drm_ioctl_desc vmw_ioctls[] = {  	VMW_IOCTL_DEF(VMW_GET_PARAM, vmw_getparam_ioctl, -		      DRM_AUTH | DRM_UNLOCKED), +		      DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW),  	VMW_IOCTL_DEF(VMW_ALLOC_DMABUF, vmw_dmabuf_alloc_ioctl, -		      DRM_AUTH | DRM_UNLOCKED), +		      DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW),  	VMW_IOCTL_DEF(VMW_UNREF_DMABUF, vmw_dmabuf_unref_ioctl, -		      DRM_AUTH | DRM_UNLOCKED), +		      DRM_UNLOCKED | DRM_RENDER_ALLOW),  	VMW_IOCTL_DEF(VMW_CURSOR_BYPASS,  		      vmw_kms_cursor_bypass_ioctl,  		      DRM_MASTER | DRM_CONTROL_ALLOW | DRM_UNLOCKED), @@ -124,31 +159,66 @@ static struct drm_ioctl_desc vmw_ioctls[] = {  		      DRM_MASTER | DRM_CONTROL_ALLOW | DRM_UNLOCKED),  	VMW_IOCTL_DEF(VMW_CREATE_CONTEXT, vmw_context_define_ioctl, -		      DRM_AUTH | DRM_UNLOCKED), +		      DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW),  	VMW_IOCTL_DEF(VMW_UNREF_CONTEXT, vmw_context_destroy_ioctl, -		      DRM_AUTH | DRM_UNLOCKED), +		      DRM_UNLOCKED | DRM_RENDER_ALLOW),  	VMW_IOCTL_DEF(VMW_CREATE_SURFACE, vmw_surface_define_ioctl, -		      DRM_AUTH | DRM_UNLOCKED), +		      DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW),  	VMW_IOCTL_DEF(VMW_UNREF_SURFACE, vmw_surface_destroy_ioctl, -		      DRM_AUTH | DRM_UNLOCKED), +		      DRM_UNLOCKED | DRM_RENDER_ALLOW),  	VMW_IOCTL_DEF(VMW_REF_SURFACE, vmw_surface_reference_ioctl, -		      DRM_AUTH | DRM_UNLOCKED), +		      DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW),  	VMW_IOCTL_DEF(VMW_EXECBUF, vmw_execbuf_ioctl, -		      DRM_AUTH | DRM_UNLOCKED), -	VMW_IOCTL_DEF(VMW_FIFO_DEBUG, vmw_fifo_debug_ioctl, -		      DRM_AUTH | DRM_ROOT_ONLY | DRM_MASTER | DRM_UNLOCKED), -	VMW_IOCTL_DEF(VMW_FENCE_WAIT, vmw_fence_wait_ioctl, -		      DRM_AUTH | DRM_UNLOCKED), -	VMW_IOCTL_DEF(VMW_UPDATE_LAYOUT, vmw_kms_update_layout_ioctl, -		      DRM_MASTER | DRM_CONTROL_ALLOW | DRM_UNLOCKED) +		      DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW), +	VMW_IOCTL_DEF(VMW_FENCE_WAIT, vmw_fence_obj_wait_ioctl, +		      DRM_UNLOCKED | DRM_RENDER_ALLOW), +	VMW_IOCTL_DEF(VMW_FENCE_SIGNALED, +		      vmw_fence_obj_signaled_ioctl, +		      DRM_UNLOCKED | DRM_RENDER_ALLOW), +	VMW_IOCTL_DEF(VMW_FENCE_UNREF, vmw_fence_obj_unref_ioctl, +		      DRM_UNLOCKED | DRM_RENDER_ALLOW), +	VMW_IOCTL_DEF(VMW_FENCE_EVENT, vmw_fence_event_ioctl, +		      DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW), +	VMW_IOCTL_DEF(VMW_GET_3D_CAP, vmw_get_cap_3d_ioctl, +		      DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW), + +	/* these allow direct access to the framebuffers mark as master only */ +	VMW_IOCTL_DEF(VMW_PRESENT, vmw_present_ioctl, +		      DRM_MASTER | DRM_AUTH | DRM_UNLOCKED), +	VMW_IOCTL_DEF(VMW_PRESENT_READBACK, +		      vmw_present_readback_ioctl, +		      DRM_MASTER | DRM_AUTH | DRM_UNLOCKED), +	VMW_IOCTL_DEF(VMW_UPDATE_LAYOUT, +		      vmw_kms_update_layout_ioctl, +		      DRM_MASTER | DRM_UNLOCKED), +	VMW_IOCTL_DEF(VMW_CREATE_SHADER, +		      vmw_shader_define_ioctl, +		      DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW), +	VMW_IOCTL_DEF(VMW_UNREF_SHADER, +		      vmw_shader_destroy_ioctl, +		      DRM_UNLOCKED | DRM_RENDER_ALLOW), +	VMW_IOCTL_DEF(VMW_GB_SURFACE_CREATE, +		      vmw_gb_surface_define_ioctl, +		      DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW), +	VMW_IOCTL_DEF(VMW_GB_SURFACE_REF, +		      vmw_gb_surface_reference_ioctl, +		      DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW), +	VMW_IOCTL_DEF(VMW_SYNCCPU, +		      vmw_user_dmabuf_synccpu_ioctl, +		      DRM_UNLOCKED | DRM_RENDER_ALLOW),  };  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}  }; +MODULE_DEVICE_TABLE(pci, vmw_pci_id_list); -static int enable_fbdev; +static int enable_fbdev = IS_ENABLED(CONFIG_DRM_VMWGFX_FBCON); +static int vmw_force_iommu; +static int vmw_restrict_iommu; +static int vmw_force_coherent; +static int vmw_restrict_dma_mask;  static int vmw_probe(struct pci_dev *, const struct pci_device_id *);  static void vmw_master_init(struct vmw_master *); @@ -157,6 +227,15 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,  MODULE_PARM_DESC(enable_fbdev, "Enable vmwgfx fbdev");  module_param_named(enable_fbdev, enable_fbdev, int, 0600); +MODULE_PARM_DESC(force_dma_api, "Force using the DMA API for TTM pages"); +module_param_named(force_dma_api, vmw_force_iommu, int, 0600); +MODULE_PARM_DESC(restrict_iommu, "Try to limit IOMMU usage for TTM pages"); +module_param_named(restrict_iommu, vmw_restrict_iommu, int, 0600); +MODULE_PARM_DESC(force_coherent, "Force coherent TTM pages"); +module_param_named(force_coherent, vmw_force_coherent, int, 0600); +MODULE_PARM_DESC(restrict_dma_mask, "Restrict DMA mask to 44 bits with IOMMU"); +module_param_named(restrict_dma_mask, vmw_restrict_dma_mask, int, 0600); +  static void vmw_print_capabilities(uint32_t capabilities)  { @@ -189,6 +268,75 @@ static void vmw_print_capabilities(uint32_t capabilities)  		DRM_INFO("  GMR.\n");  	if (capabilities & SVGA_CAP_TRACES)  		DRM_INFO("  Traces.\n"); +	if (capabilities & SVGA_CAP_GMR2) +		DRM_INFO("  GMR2.\n"); +	if (capabilities & SVGA_CAP_SCREEN_OBJECT_2) +		DRM_INFO("  Screen Object 2.\n"); +	if (capabilities & SVGA_CAP_COMMAND_BUFFERS) +		DRM_INFO("  Command Buffers.\n"); +	if (capabilities & SVGA_CAP_CMD_BUFFERS_2) +		DRM_INFO("  Command Buffers 2.\n"); +	if (capabilities & SVGA_CAP_GBOBJECTS) +		DRM_INFO("  Guest Backed Resources.\n"); +} + +/** + * vmw_dummy_query_bo_create - create a bo to hold a dummy query result + * + * @dev_priv: A device private structure. + * + * This function creates a small buffer object that holds the query + * result for dummy queries emitted as query barriers. + * The function will then map the first page and initialize a pending + * occlusion query result structure, Finally it will unmap the buffer. + * No interruptible waits are done within this function. + * + * Returns an error if bo creation or initialization fails. + */ +static int vmw_dummy_query_bo_create(struct vmw_private *dev_priv) +{ +	int ret; +	struct ttm_buffer_object *bo; +	struct ttm_bo_kmap_obj map; +	volatile SVGA3dQueryResult *result; +	bool dummy; + +	/* +	 * Create the bo as pinned, so that a tryreserve will +	 * immediately succeed. This is because we're the only +	 * user of the bo currently. +	 */ +	ret = ttm_bo_create(&dev_priv->bdev, +			    PAGE_SIZE, +			    ttm_bo_type_device, +			    &vmw_sys_ne_placement, +			    0, false, NULL, +			    &bo); + +	if (unlikely(ret != 0)) +		return ret; + +	ret = ttm_bo_reserve(bo, false, true, false, 0); +	BUG_ON(ret != 0); + +	ret = ttm_bo_kmap(bo, 0, 1, &map); +	if (likely(ret == 0)) { +		result = ttm_kmap_obj_virtual(&map, &dummy); +		result->totalSize = sizeof(*result); +		result->state = SVGA3D_QUERYSTATE_PENDING; +		result->result32 = 0xff; +		ttm_bo_kunmap(&map); +	} +	vmw_bo_pin(bo, false); +	ttm_bo_unreserve(bo); + +	if (unlikely(ret != 0)) { +		DRM_ERROR("Dummy query buffer map failed.\n"); +		ttm_bo_unref(&bo); +	} else +		dev_priv->dummy_query_bo = bo; + +	return ret;  }  static int vmw_request_device(struct vmw_private *dev_priv) @@ -200,16 +348,55 @@ static int vmw_request_device(struct vmw_private *dev_priv)  		DRM_ERROR("Unable to initialize FIFO.\n");  		return ret;  	} +	vmw_fence_fifo_up(dev_priv->fman); +	if (dev_priv->has_mob) { +		ret = vmw_otables_setup(dev_priv); +		if (unlikely(ret != 0)) { +			DRM_ERROR("Unable to initialize " +				  "guest Memory OBjects.\n"); +			goto out_no_mob; +		} +	} +	ret = vmw_dummy_query_bo_create(dev_priv); +	if (unlikely(ret != 0)) +		goto out_no_query_bo;  	return 0; + +out_no_query_bo: +	if (dev_priv->has_mob) +		vmw_otables_takedown(dev_priv); +out_no_mob: +	vmw_fence_fifo_down(dev_priv->fman); +	vmw_fifo_release(dev_priv, &dev_priv->fifo); +	return ret;  }  static void vmw_release_device(struct vmw_private *dev_priv)  { +	/* +	 * Previous destructions should've released +	 * the pinned bo. +	 */ + +	BUG_ON(dev_priv->pinned_bo != NULL); + +	ttm_bo_unref(&dev_priv->dummy_query_bo); +	if (dev_priv->has_mob) +		vmw_otables_takedown(dev_priv); +	vmw_fence_fifo_down(dev_priv->fman);  	vmw_fifo_release(dev_priv, &dev_priv->fifo);  } -int vmw_3d_resource_inc(struct vmw_private *dev_priv) + +/** + * Increase the 3d resource refcount. + * If the count was prevously zero, initialize the fifo, switching to svga + * mode. Note that the master holds a ref as well, and may request an + * explicit switch to svga mode if fb is not running, using @unhide_svga. + */ +int vmw_3d_resource_inc(struct vmw_private *dev_priv, +			bool unhide_svga)  {  	int ret = 0; @@ -218,54 +405,221 @@ int vmw_3d_resource_inc(struct vmw_private *dev_priv)  		ret = vmw_request_device(dev_priv);  		if (unlikely(ret != 0))  			--dev_priv->num_3d_resources; +	} else if (unhide_svga) { +		mutex_lock(&dev_priv->hw_mutex); +		vmw_write(dev_priv, SVGA_REG_ENABLE, +			  vmw_read(dev_priv, SVGA_REG_ENABLE) & +			  ~SVGA_REG_ENABLE_HIDE); +		mutex_unlock(&dev_priv->hw_mutex);  	} +  	mutex_unlock(&dev_priv->release_mutex);  	return ret;  } - -void vmw_3d_resource_dec(struct vmw_private *dev_priv) +/** + * Decrease the 3d resource refcount. + * If the count reaches zero, disable the fifo, switching to vga mode. + * Note that the master holds a refcount as well, and may request an + * explicit switch to vga mode when it releases its refcount to account + * for the situation of an X server vt switch to VGA with 3d resources + * active. + */ +void vmw_3d_resource_dec(struct vmw_private *dev_priv, +			 bool hide_svga)  {  	int32_t n3d;  	mutex_lock(&dev_priv->release_mutex);  	if (unlikely(--dev_priv->num_3d_resources == 0))  		vmw_release_device(dev_priv); +	else if (hide_svga) { +		mutex_lock(&dev_priv->hw_mutex); +		vmw_write(dev_priv, SVGA_REG_ENABLE, +			  vmw_read(dev_priv, SVGA_REG_ENABLE) | +			  SVGA_REG_ENABLE_HIDE); +		mutex_unlock(&dev_priv->hw_mutex); +	} +  	n3d = (int32_t) dev_priv->num_3d_resources;  	mutex_unlock(&dev_priv->release_mutex);  	BUG_ON(n3d < 0);  } +/** + * Sets the initial_[width|height] fields on the given vmw_private. + * + * It does so by reading SVGA_REG_[WIDTH|HEIGHT] regs and then + * clamping the value to fb_max_[width|height] fields and the + * VMW_MIN_INITIAL_[WIDTH|HEIGHT]. + * If the values appear to be invalid, set them to + * VMW_MIN_INITIAL_[WIDTH|HEIGHT]. + */ +static void vmw_get_initial_size(struct vmw_private *dev_priv) +{ +	uint32_t width; +	uint32_t height; + +	width = vmw_read(dev_priv, SVGA_REG_WIDTH); +	height = vmw_read(dev_priv, SVGA_REG_HEIGHT); + +	width = max_t(uint32_t, width, VMW_MIN_INITIAL_WIDTH); +	height = max_t(uint32_t, height, VMW_MIN_INITIAL_HEIGHT); + +	if (width > dev_priv->fb_max_width || +	    height > dev_priv->fb_max_height) { + +		/* +		 * This is a host error and shouldn't occur. +		 */ + +		width = VMW_MIN_INITIAL_WIDTH; +		height = VMW_MIN_INITIAL_HEIGHT; +	} + +	dev_priv->initial_width = width; +	dev_priv->initial_height = height; +} + +/** + * vmw_dma_select_mode - Determine how DMA mappings should be set up for this + * system. + * + * @dev_priv: Pointer to a struct vmw_private + * + * This functions tries to determine the IOMMU setup and what actions + * need to be taken by the driver to make system pages visible to the + * device. + * If this function decides that DMA is not possible, it returns -EINVAL. + * The driver may then try to disable features of the device that require + * DMA. + */ +static int vmw_dma_select_mode(struct vmw_private *dev_priv) +{ +	static const char *names[vmw_dma_map_max] = { +		[vmw_dma_phys] = "Using physical TTM page addresses.", +		[vmw_dma_alloc_coherent] = "Using coherent TTM pages.", +		[vmw_dma_map_populate] = "Keeping DMA mappings.", +		[vmw_dma_map_bind] = "Giving up DMA mappings early."}; +#ifdef CONFIG_X86 +	const struct dma_map_ops *dma_ops = get_dma_ops(dev_priv->dev->dev); + +#ifdef CONFIG_INTEL_IOMMU +	if (intel_iommu_enabled) { +		dev_priv->map_mode = vmw_dma_map_populate; +		goto out_fixup; +	} +#endif + +	if (!(vmw_force_iommu || vmw_force_coherent)) { +		dev_priv->map_mode = vmw_dma_phys; +		DRM_INFO("DMA map mode: %s\n", names[dev_priv->map_mode]); +		return 0; +	} + +	dev_priv->map_mode = vmw_dma_map_populate; + +	if (dma_ops->sync_single_for_cpu) +		dev_priv->map_mode = vmw_dma_alloc_coherent; +#ifdef CONFIG_SWIOTLB +	if (swiotlb_nr_tbl() == 0) +		dev_priv->map_mode = vmw_dma_map_populate; +#endif + +#ifdef CONFIG_INTEL_IOMMU +out_fixup: +#endif +	if (dev_priv->map_mode == vmw_dma_map_populate && +	    vmw_restrict_iommu) +		dev_priv->map_mode = vmw_dma_map_bind; + +	if (vmw_force_coherent) +		dev_priv->map_mode = vmw_dma_alloc_coherent; + +#if !defined(CONFIG_SWIOTLB) && !defined(CONFIG_INTEL_IOMMU) +	/* +	 * No coherent page pool +	 */ +	if (dev_priv->map_mode == vmw_dma_alloc_coherent) +		return -EINVAL; +#endif + +#else /* CONFIG_X86 */ +	dev_priv->map_mode = vmw_dma_map_populate; +#endif /* CONFIG_X86 */ + +	DRM_INFO("DMA map mode: %s\n", names[dev_priv->map_mode]); + +	return 0; +} + +/** + * vmw_dma_masks - set required page- and dma masks + * + * @dev: Pointer to struct drm-device + * + * With 32-bit we can only handle 32 bit PFNs. Optionally set that + * restriction also for 64-bit systems. + */ +#ifdef CONFIG_INTEL_IOMMU +static int vmw_dma_masks(struct vmw_private *dev_priv) +{ +	struct drm_device *dev = dev_priv->dev; + +	if (intel_iommu_enabled && +	    (sizeof(unsigned long) == 4 || vmw_restrict_dma_mask)) { +		DRM_INFO("Restricting DMA addresses to 44 bits.\n"); +		return dma_set_mask(dev->dev, DMA_BIT_MASK(44)); +	} +	return 0; +} +#else +static int vmw_dma_masks(struct vmw_private *dev_priv) +{ +	return 0; +} +#endif +  static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)  {  	struct vmw_private *dev_priv;  	int ret;  	uint32_t svga_id; +	enum vmw_res_type i; +	bool refuse_dma = false;  	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)); + +	pci_set_master(dev->pdev);  	dev_priv->dev = dev;  	dev_priv->vmw_chipset = chipset; -	dev_priv->last_read_sequence = (uint32_t) -100; +	dev_priv->last_read_seqno = (uint32_t) -100;  	mutex_init(&dev_priv->hw_mutex);  	mutex_init(&dev_priv->cmdbuf_mutex);  	mutex_init(&dev_priv->release_mutex); +	mutex_init(&dev_priv->binding_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); +	ttm_lock_init(&dev_priv->reservation_sem); + +	for (i = vmw_res_context; i < vmw_res_max; ++i) { +		idr_init(&dev_priv->res_idr[i]); +		INIT_LIST_HEAD(&dev_priv->res_lru[i]); +	} +  	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); +	dev_priv->fence_queue_waiters = 0;  	atomic_set(&dev_priv->fifo_queue_waiters, 0); +	dev_priv->used_memory_size = 0; +  	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); @@ -278,19 +632,16 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)  	svga_id = vmw_read(dev_priv, SVGA_REG_ID);  	if (svga_id != SVGA_ID_2) {  		ret = -ENOSYS; -		DRM_ERROR("Unsuported SVGA ID 0x%x\n", svga_id); +		DRM_ERROR("Unsupported SVGA ID 0x%x\n", svga_id);  		mutex_unlock(&dev_priv->hw_mutex);  		goto out_err0;  	}  	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); +	ret = vmw_dma_select_mode(dev_priv); +	if (unlikely(ret != 0)) { +		DRM_INFO("Restricting capabilities due to IOMMU setup.\n"); +		refuse_dma = true;  	}  	dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE); @@ -298,16 +649,62 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)  	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); +	vmw_get_initial_size(dev_priv); + +	if (dev_priv->capabilities & SVGA_CAP_GMR2) { +		dev_priv->max_gmr_ids = +			vmw_read(dev_priv, SVGA_REG_GMR_MAX_IDS); +		dev_priv->max_gmr_pages = +			vmw_read(dev_priv, SVGA_REG_GMRS_MAX_PAGES); +		dev_priv->memory_size = +			vmw_read(dev_priv, SVGA_REG_MEMORY_SIZE); +		dev_priv->memory_size -= dev_priv->vram_size; +	} else { +		/* +		 * An arbitrary limit of 512MiB on surface +		 * memory. But all HWV8 hardware supports GMR2. +		 */ +		dev_priv->memory_size = 512*1024*1024; +	} +	dev_priv->max_mob_pages = 0; +	dev_priv->max_mob_size = 0; +	if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS) { +		uint64_t mem_size = +			vmw_read(dev_priv, +				 SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB); + +		dev_priv->max_mob_pages = mem_size * 1024 / PAGE_SIZE; +		dev_priv->prim_bb_mem = +			vmw_read(dev_priv, +				 SVGA_REG_MAX_PRIMARY_BOUNDING_BOX_MEM); +		dev_priv->max_mob_size = +			vmw_read(dev_priv, SVGA_REG_MOB_MAX_SIZE); +	} else +		dev_priv->prim_bb_mem = dev_priv->vram_size; + +	ret = vmw_dma_masks(dev_priv); +	if (unlikely(ret != 0)) { +		mutex_unlock(&dev_priv->hw_mutex); +		goto out_err0; +	} + +	if (unlikely(dev_priv->prim_bb_mem < dev_priv->vram_size)) +		dev_priv->prim_bb_mem = dev_priv->vram_size; +  	mutex_unlock(&dev_priv->hw_mutex);  	vmw_print_capabilities(dev_priv->capabilities); -	if (dev_priv->capabilities & SVGA_CAP_GMR) { +	if (dev_priv->capabilities & SVGA_CAP_GMR2) {  		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("Max number of GMR pages is %u\n", +			 (unsigned)dev_priv->max_gmr_pages); +		DRM_INFO("Max dedicated hypervisor surface memory is %u kiB\n", +			 (unsigned)dev_priv->memory_size / 1024);  	} +	DRM_INFO("Maximum display memory size is %u kiB\n", +		 dev_priv->prim_bb_mem / 1024);  	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", @@ -325,7 +722,9 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)  	ret = ttm_bo_device_init(&dev_priv->bdev,  				 dev_priv->bo_global_ref.ref.object, -				 &vmw_bo_driver, VMWGFX_FILE_PAGE_OFFSET, +				 &vmw_bo_driver, +				 dev->anon_inode->i_mapping, +				 VMWGFX_FILE_PAGE_OFFSET,  				 false);  	if (unlikely(ret != 0)) {  		DRM_ERROR("Failed initializing TTM buffer object driver.\n"); @@ -340,15 +739,26 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)  	}  	dev_priv->has_gmr = true; -	if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR, -			   dev_priv->max_gmr_ids) != 0) { +	if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) || +	    refuse_dma || ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR, +					 VMW_PL_GMR) != 0) {  		DRM_INFO("No GMR memory available. "  			 "Graphics memory resources are very limited.\n");  		dev_priv->has_gmr = false;  	} -	dev_priv->mmio_mtrr = drm_mtrr_add(dev_priv->mmio_start, -					   dev_priv->mmio_size, DRM_MTRR_WC); +	if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS) { +		dev_priv->has_mob = true; +		if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_MOB, +				   VMW_PL_MOB) != 0) { +			DRM_INFO("No MOB memory available. " +				 "3D will be disabled.\n"); +			dev_priv->has_mob = false; +		} +	} + +	dev_priv->mmio_mtrr = arch_phys_wc_add(dev_priv->mmio_start, +					       dev_priv->mmio_size);  	dev_priv->mmio_virt = ioremap_wc(dev_priv->mmio_start,  					 dev_priv->mmio_size); @@ -369,7 +779,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)  	}  	dev_priv->tdev = ttm_object_device_init -	    (dev_priv->mem_global_ref.object, 12); +		(dev_priv->mem_global_ref.object, 12, &vmw_prime_dmabuf_ops);  	if (unlikely(dev_priv->tdev == NULL)) {  		DRM_ERROR("Unable to initialize TTM object management.\n"); @@ -394,30 +804,34 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)  			goto out_no_device;  		}  	} + +	if (dev_priv->capabilities & SVGA_CAP_IRQMASK) { +		ret = drm_irq_install(dev, dev->pdev->irq); +		if (ret != 0) { +			DRM_ERROR("Failed installing irq: %d\n", ret); +			goto out_no_irq; +		} +	} + +	dev_priv->fman = vmw_fence_manager_init(dev_priv); +	if (unlikely(dev_priv->fman == NULL)) { +		ret = -ENOMEM; +		goto out_no_fman; +	} + +	vmw_kms_save_vga(dev_priv); + +	/* Start kms and overlay systems, needs fifo. */  	ret = vmw_kms_init(dev_priv);  	if (unlikely(ret != 0))  		goto out_no_kms;  	vmw_overlay_init(dev_priv); +  	if (dev_priv->enable_fb) { -		ret = vmw_3d_resource_inc(dev_priv); +		ret = vmw_3d_resource_inc(dev_priv, true);  		if (unlikely(ret != 0))  			goto out_no_fifo; -		vmw_kms_save_vga(dev_priv);  		vmw_fb_init(dev_priv); -		DRM_INFO("%s", vmw_fifo_have_3d(dev_priv) ? -			 "Detected device 3D availability.\n" : -			 "Detected no device 3D availability.\n"); -	} else { -		DRM_INFO("Delayed 3D detection since we're not " -			 "running the device in SVGA mode yet.\n"); -	} - -	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; -		}  	}  	dev_priv->pm_nb.notifier_call = vmwgfx_pm_notifier; @@ -425,16 +839,16 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)  	return 0; -out_no_irq: -	if (dev_priv->enable_fb) { -		vmw_fb_close(dev_priv); -		vmw_kms_restore_vga(dev_priv); -		vmw_3d_resource_dec(dev_priv); -	}  out_no_fifo:  	vmw_overlay_close(dev_priv);  	vmw_kms_close(dev_priv);  out_no_kms: +	vmw_kms_restore_vga(dev_priv); +	vmw_fence_manager_takedown(dev_priv->fman); +out_no_fman: +	if (dev_priv->capabilities & SVGA_CAP_IRQMASK) +		drm_irq_uninstall(dev_priv->dev); +out_no_irq:  	if (dev_priv->stealth)  		pci_release_region(dev->pdev, 2);  	else @@ -444,8 +858,9 @@ out_no_device:  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); +	arch_phys_wc_del(dev_priv->mmio_mtrr); +	if (dev_priv->has_mob) +		(void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB);  	if (dev_priv->has_gmr)  		(void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR);  	(void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); @@ -454,9 +869,9 @@ out_err2:  out_err1:  	vmw_ttm_global_release(dev_priv);  out_err0: -	idr_destroy(&dev_priv->surface_idr); -	idr_destroy(&dev_priv->context_idr); -	idr_destroy(&dev_priv->stream_idr); +	for (i = vmw_res_context; i < vmw_res_max; ++i) +		idr_destroy(&dev_priv->res_idr[i]); +  	kfree(dev_priv);  	return ret;  } @@ -464,18 +879,24 @@ out_err0:  static int vmw_driver_unload(struct drm_device *dev)  {  	struct vmw_private *dev_priv = vmw_priv(dev); +	enum vmw_res_type i;  	unregister_pm_notifier(&dev_priv->pm_nb); -	if (dev_priv->capabilities & SVGA_CAP_IRQMASK) -		drm_irq_uninstall(dev_priv->dev); +	if (dev_priv->ctx.res_ht_initialized) +		drm_ht_remove(&dev_priv->ctx.res_ht); +	if (dev_priv->ctx.cmd_bounce) +		vfree(dev_priv->ctx.cmd_bounce);  	if (dev_priv->enable_fb) {  		vmw_fb_close(dev_priv);  		vmw_kms_restore_vga(dev_priv); -		vmw_3d_resource_dec(dev_priv); +		vmw_3d_resource_dec(dev_priv, false);  	}  	vmw_kms_close(dev_priv);  	vmw_overlay_close(dev_priv); +	vmw_fence_manager_takedown(dev_priv->fman); +	if (dev_priv->capabilities & SVGA_CAP_IRQMASK) +		drm_irq_uninstall(dev_priv->dev);  	if (dev_priv->stealth)  		pci_release_region(dev->pdev, 2);  	else @@ -483,31 +904,50 @@ static int vmw_driver_unload(struct drm_device *dev)  	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); +	arch_phys_wc_del(dev_priv->mmio_mtrr); +	if (dev_priv->has_mob) +		(void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB);  	if (dev_priv->has_gmr)  		(void)ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR);  	(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); -	idr_destroy(&dev_priv->surface_idr); -	idr_destroy(&dev_priv->context_idr); -	idr_destroy(&dev_priv->stream_idr); + +	for (i = vmw_res_context; i < vmw_res_max; ++i) +		idr_destroy(&dev_priv->res_idr[i]);  	kfree(dev_priv);  	return 0;  } +static void vmw_preclose(struct drm_device *dev, +			 struct drm_file *file_priv) +{ +	struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); +	struct vmw_private *dev_priv = vmw_priv(dev); + +	vmw_event_fence_fpriv_gone(dev_priv->fman, &vmw_fp->fence_events); +} +  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) + +	if (vmw_fp->locked_master) { +		struct vmw_master *vmaster = +			vmw_master(vmw_fp->locked_master); + +		ttm_lock_set_kill(&vmaster->lock, true, SIGTERM); +		ttm_vt_unlock(&vmaster->lock);  		drm_master_put(&vmw_fp->locked_master); +	} + +	vmw_compat_shader_man_destroy(vmw_fp->shman); +	ttm_object_file_release(&vmw_fp->tfile);  	kfree(vmw_fp);  } @@ -521,29 +961,90 @@ static int vmw_driver_open(struct drm_device *dev, struct drm_file *file_priv)  	if (unlikely(vmw_fp == NULL))  		return ret; +	INIT_LIST_HEAD(&vmw_fp->fence_events);  	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; +	vmw_fp->shman = vmw_compat_shader_man_create(dev_priv); +	if (IS_ERR(vmw_fp->shman)) +		goto out_no_shman; -	if (unlikely(dev_priv->bdev.dev_mapping == NULL)) -		dev_priv->bdev.dev_mapping = -			file_priv->filp->f_path.dentry->d_inode->i_mapping; +	file_priv->driver_priv = vmw_fp;  	return 0; +out_no_shman: +	ttm_object_file_release(&vmw_fp->tfile);  out_no_tfile:  	kfree(vmw_fp);  	return ret;  } -static long vmw_unlocked_ioctl(struct file *filp, unsigned int cmd, -			       unsigned long arg) +static struct vmw_master *vmw_master_check(struct drm_device *dev, +					   struct drm_file *file_priv, +					   unsigned int flags) +{ +	int ret; +	struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); +	struct vmw_master *vmaster; + +	if (file_priv->minor->type != DRM_MINOR_LEGACY || +	    !(flags & DRM_AUTH)) +		return NULL; + +	ret = mutex_lock_interruptible(&dev->master_mutex); +	if (unlikely(ret != 0)) +		return ERR_PTR(-ERESTARTSYS); + +	if (file_priv->is_master) { +		mutex_unlock(&dev->master_mutex); +		return NULL; +	} + +	/* +	 * Check if we were previously master, but now dropped. +	 */ +	if (vmw_fp->locked_master) { +		mutex_unlock(&dev->master_mutex); +		DRM_ERROR("Dropped master trying to access ioctl that " +			  "requires authentication.\n"); +		return ERR_PTR(-EACCES); +	} +	mutex_unlock(&dev->master_mutex); + +	/* +	 * Taking the drm_global_mutex after the TTM lock might deadlock +	 */ +	if (!(flags & DRM_UNLOCKED)) { +		DRM_ERROR("Refusing locked ioctl access.\n"); +		return ERR_PTR(-EDEADLK); +	} + +	/* +	 * Take the TTM lock. Possibly sleep waiting for the authenticating +	 * master to become master again, or for a SIGTERM if the +	 * authenticating master exits. +	 */ +	vmaster = vmw_master(file_priv->master); +	ret = ttm_read_lock(&vmaster->lock, true); +	if (unlikely(ret != 0)) +		vmaster = ERR_PTR(ret); + +	return vmaster; +} + +static long vmw_generic_ioctl(struct file *filp, unsigned int cmd, +			      unsigned long arg, +			      long (*ioctl_func)(struct file *, unsigned int, +						 unsigned long))  {  	struct drm_file *file_priv = filp->private_data;  	struct drm_device *dev = file_priv->minor->dev;  	unsigned int nr = DRM_IOCTL_NR(cmd); +	struct vmw_master *vmaster; +	unsigned int flags; +	long ret;  	/*  	 * Do extra checking on driver private ioctls. @@ -551,42 +1052,51 @@ static long vmw_unlocked_ioctl(struct file *filp, unsigned int cmd,  	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]; +		const struct drm_ioctl_desc *ioctl = +			&vmw_ioctls[nr - DRM_COMMAND_BASE];  		if (unlikely(ioctl->cmd_drv != cmd)) {  			DRM_ERROR("Invalid command format, ioctl %d\n",  				  nr - DRM_COMMAND_BASE);  			return -EINVAL;  		} +		flags = ioctl->flags; +	} else if (!drm_ioctl_flags(nr, &flags)) +		return -EINVAL; + +	vmaster = vmw_master_check(dev, file_priv, flags); +	if (unlikely(IS_ERR(vmaster))) { +		DRM_INFO("IOCTL ERROR %d\n", nr); +		return PTR_ERR(vmaster);  	} -	return drm_ioctl(filp, cmd, arg); +	ret = ioctl_func(filp, cmd, arg); +	if (vmaster) +		ttm_read_unlock(&vmaster->lock); + +	return ret;  } -static int vmw_firstopen(struct drm_device *dev) +static long vmw_unlocked_ioctl(struct file *filp, unsigned int cmd, +			       unsigned long arg)  { -	struct vmw_private *dev_priv = vmw_priv(dev); -	dev_priv->is_opened = true; +	return vmw_generic_ioctl(filp, cmd, arg, &drm_ioctl); +} -	return 0; +#ifdef CONFIG_COMPAT +static long vmw_compat_ioctl(struct file *filp, unsigned int cmd, +			     unsigned long arg) +{ +	return vmw_generic_ioctl(filp, cmd, arg, &drm_compat_ioctl);  } +#endif  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; @@ -596,7 +1106,7 @@ static void vmw_lastclose(struct drm_device *dev)  	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {  		set.crtc = crtc; -		ret = crtc->funcs->set_config(&set); +		ret = drm_mode_set_config_internal(&set);  		WARN_ON(ret != 0);  	} @@ -646,7 +1156,7 @@ static int vmw_master_set(struct drm_device *dev,  	int ret = 0;  	if (!dev_priv->enable_fb) { -		ret = vmw_3d_resource_inc(dev_priv); +		ret = vmw_3d_resource_inc(dev_priv, true);  		if (unlikely(ret != 0))  			return ret;  		vmw_kms_save_vga(dev_priv); @@ -684,11 +1194,11 @@ static int vmw_master_set(struct drm_device *dev,  out_no_active_lock:  	if (!dev_priv->enable_fb) { +		vmw_kms_restore_vga(dev_priv); +		vmw_3d_resource_dec(dev_priv, true);  		mutex_lock(&dev_priv->hw_mutex);  		vmw_write(dev_priv, SVGA_REG_TRACES, 1);  		mutex_unlock(&dev_priv->hw_mutex); -		vmw_kms_restore_vga(dev_priv); -		vmw_3d_resource_dec(dev_priv);  	}  	return ret;  } @@ -709,24 +1219,23 @@ static void vmw_master_drop(struct drm_device *dev,  	vmw_fp->locked_master = drm_master_get(file_priv->master);  	ret = ttm_vt_lock(&vmaster->lock, false, vmw_fp->tfile); -	vmw_kms_idle_workqueues(vmaster); -  	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); +	ttm_lock_set_kill(&vmaster->lock, false, SIGTERM); +	vmw_execbuf_release_pinned_bo(dev_priv);  	if (!dev_priv->enable_fb) {  		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_kms_restore_vga(dev_priv); +		vmw_3d_resource_dec(dev_priv, true);  		mutex_lock(&dev_priv->hw_mutex);  		vmw_write(dev_priv, SVGA_REG_TRACES, 1);  		mutex_unlock(&dev_priv->hw_mutex); -		vmw_kms_restore_vga(dev_priv); -		vmw_3d_resource_dec(dev_priv);  	}  	dev_priv->active_master = &dev_priv->fbdev_master; @@ -750,24 +1259,25 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,  {  	struct vmw_private *dev_priv =  		container_of(nb, struct vmw_private, pm_nb); -	struct vmw_master *vmaster = dev_priv->active_master;  	switch (val) {  	case PM_HIBERNATION_PREPARE:  	case PM_SUSPEND_PREPARE: -		ttm_suspend_lock(&vmaster->lock); +		ttm_suspend_lock(&dev_priv->reservation_sem);  		/**  		 * This empties VRAM and unbinds all GMR bindings.  		 * Buffer contents is moved to swappable memory.  		 */ +		vmw_execbuf_release_pinned_bo(dev_priv); +		vmw_resource_evict_all(dev_priv);  		ttm_bo_swapout_all(&dev_priv->bdev);  		break;  	case PM_POST_HIBERNATION:  	case PM_POST_SUSPEND:  	case PM_POST_RESTORE: -		ttm_suspend_unlock(&vmaster->lock); +		ttm_suspend_unlock(&dev_priv->reservation_sem);  		break;  	case PM_RESTORE_PREPARE: @@ -835,7 +1345,7 @@ static int vmw_pm_prepare(struct device *kdev)  	 */  	dev_priv->suspended = true;  	if (dev_priv->enable_fb) -		vmw_3d_resource_dec(dev_priv); +			vmw_3d_resource_dec(dev_priv, true);  	if (dev_priv->num_3d_resources != 0) { @@ -843,7 +1353,7 @@ static int vmw_pm_prepare(struct device *kdev)  			 "while 3D resources are active.\n");  		if (dev_priv->enable_fb) -			vmw_3d_resource_inc(dev_priv); +			vmw_3d_resource_inc(dev_priv, true);  		dev_priv->suspended = false;  		return -EBUSY;  	} @@ -857,12 +1367,17 @@ static void vmw_pm_complete(struct device *kdev)  	struct drm_device *dev = pci_get_drvdata(pdev);  	struct vmw_private *dev_priv = vmw_priv(dev); +	mutex_lock(&dev_priv->hw_mutex); +	vmw_write(dev_priv, SVGA_REG_ID, SVGA_ID_2); +	(void) vmw_read(dev_priv, SVGA_REG_ID); +	mutex_unlock(&dev_priv->hw_mutex); +  	/**  	 * Reclaim 3d reference held by fbdev and potentially  	 * start fifo.  	 */  	if (dev_priv->enable_fb) -		vmw_3d_resource_inc(dev_priv); +			vmw_3d_resource_inc(dev_priv, false);  	dev_priv->suspended = false;  } @@ -874,50 +1389,51 @@ static const struct dev_pm_ops vmw_pm_ops = {  	.resume = vmw_pm_resume,  }; +static const struct file_operations vmwgfx_driver_fops = { +	.owner = THIS_MODULE, +	.open = drm_open, +	.release = drm_release, +	.unlocked_ioctl = vmw_unlocked_ioctl, +	.mmap = vmw_mmap, +	.poll = vmw_fops_poll, +	.read = vmw_fops_read, +#if defined(CONFIG_COMPAT) +	.compat_ioctl = vmw_compat_ioctl, +#endif +	.llseek = noop_llseek, +}; +  static struct drm_driver driver = {  	.driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | -	DRIVER_MODESET, +	DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER,  	.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,  	.get_vblank_counter = vmw_get_vblank_counter, -	.reclaim_buffers_locked = NULL, +	.enable_vblank = vmw_enable_vblank, +	.disable_vblank = vmw_disable_vblank,  	.ioctls = vmw_ioctls, -	.num_ioctls = DRM_ARRAY_SIZE(vmw_ioctls), -	.dma_quiescent = NULL,	/*vmw_dma_quiescent, */ +	.num_ioctls = ARRAY_SIZE(vmw_ioctls),  	.master_create = vmw_master_create,  	.master_destroy = vmw_master_destroy,  	.master_set = vmw_master_set,  	.master_drop = vmw_master_drop,  	.open = vmw_driver_open, +	.preclose = vmw_preclose,  	.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 -		 .llseek = noop_llseek, -	}, -	.pci_driver = { -		 .name = VMWGFX_DRIVER_NAME, -		 .id_table = vmw_pci_id_list, -		 .probe = vmw_probe, -		 .remove = vmw_remove, -		 .driver = { -			 .pm = &vmw_pm_ops -		 } -	 }, + +	.dumb_create = vmw_dumb_create, +	.dumb_map_offset = vmw_dumb_map_offset, +	.dumb_destroy = vmw_dumb_destroy, + +	.prime_fd_to_handle = vmw_prime_fd_to_handle, +	.prime_handle_to_fd = vmw_prime_handle_to_fd, + +	.fops = &vmwgfx_driver_fops,  	.name = VMWGFX_DRIVER_NAME,  	.desc = VMWGFX_DRIVER_DESC,  	.date = VMWGFX_DRIVER_DATE, @@ -926,6 +1442,16 @@ static struct drm_driver driver = {  	.patchlevel = VMWGFX_DRIVER_PATCHLEVEL  }; +static struct pci_driver vmw_pci_driver = { +	.name = VMWGFX_DRIVER_NAME, +	.id_table = vmw_pci_id_list, +	.probe = vmw_probe, +	.remove = vmw_remove, +	.driver = { +		.pm = &vmw_pm_ops +	} +}; +  static int vmw_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  {  	return drm_get_pci_dev(pdev, ent, &driver); @@ -934,7 +1460,7 @@ static int vmw_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  static int __init vmwgfx_init(void)  {  	int ret; -	ret = drm_init(&driver); +	ret = drm_pci_init(&driver, &vmw_pci_driver);  	if (ret)  		DRM_ERROR("Failed initializing DRM.\n");  	return ret; @@ -942,7 +1468,7 @@ static int __init vmwgfx_init(void)  static void __exit vmwgfx_exit(void)  { -	drm_exit(&driver); +	drm_pci_exit(&driver, &vmw_pci_driver);  }  module_init(vmwgfx_init); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index e7a58d05504..6b252a887ae 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -29,59 +29,107 @@  #define _VMWGFX_DRV_H_  #include "vmwgfx_reg.h" -#include "drmP.h" -#include "vmwgfx_drm.h" -#include "drm_hashtab.h" -#include "linux/suspend.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 "20100927" -#define VMWGFX_DRIVER_MAJOR 1 -#define VMWGFX_DRIVER_MINOR 4 +#include <drm/drmP.h> +#include <drm/vmwgfx_drm.h> +#include <drm/drm_hashtab.h> +#include <linux/suspend.h> +#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_object.h> +#include <drm/ttm/ttm_lock.h> +#include <drm/ttm/ttm_execbuf_util.h> +#include <drm/ttm/ttm_module.h> +#include "vmwgfx_fence.h" + +#define VMWGFX_DRIVER_DATE "20140325" +#define VMWGFX_DRIVER_MAJOR 2 +#define VMWGFX_DRIVER_MINOR 6  #define VMWGFX_DRIVER_PATCHLEVEL 0  #define VMWGFX_FILE_PAGE_OFFSET 0x00100000  #define VMWGFX_FIFO_STATIC_SIZE (1024*1024)  #define VMWGFX_MAX_RELOCATIONS 2048 -#define VMWGFX_MAX_GMRS 2048 +#define VMWGFX_MAX_VALIDATIONS 2048  #define VMWGFX_MAX_DISPLAYS 16 +#define VMWGFX_CMD_BOUNCE_INIT_SIZE 32768 +#define VMWGFX_ENABLE_SCREEN_TARGET_OTABLE 0 + +/* + * Perhaps we should have sysfs entries for these. + */ +#define VMWGFX_NUM_GB_CONTEXT 256 +#define VMWGFX_NUM_GB_SHADER 20000 +#define VMWGFX_NUM_GB_SURFACE 32768 +#define VMWGFX_NUM_GB_SCREEN_TARGET VMWGFX_MAX_DISPLAYS +#define VMWGFX_NUM_MOB (VMWGFX_NUM_GB_CONTEXT +\ +			VMWGFX_NUM_GB_SHADER +\ +			VMWGFX_NUM_GB_SURFACE +\ +			VMWGFX_NUM_GB_SCREEN_TARGET)  #define VMW_PL_GMR TTM_PL_PRIV0  #define VMW_PL_FLAG_GMR TTM_PL_FLAG_PRIV0 +#define VMW_PL_MOB TTM_PL_PRIV1 +#define VMW_PL_FLAG_MOB TTM_PL_FLAG_PRIV1 + +#define VMW_RES_CONTEXT ttm_driver_type0 +#define VMW_RES_SURFACE ttm_driver_type1 +#define VMW_RES_STREAM ttm_driver_type2 +#define VMW_RES_FENCE ttm_driver_type3 +#define VMW_RES_SHADER ttm_driver_type4 + +struct vmw_compat_shader_manager;  struct vmw_fpriv {  	struct drm_master *locked_master;  	struct ttm_object_file *tfile; +	struct list_head fence_events; +	bool gb_aware; +	struct vmw_compat_shader_manager *shman;  };  struct vmw_dma_buffer {  	struct ttm_buffer_object base; -	struct list_head validate_list; -	bool gmr_bound; -	uint32_t cur_validate_node; -	bool on_validate_list; +	struct list_head res_list;  }; +/** + * struct vmw_validate_buffer - Carries validation info about buffers. + * + * @base: Validation info for TTM. + * @hash: Hash entry for quick lookup of the TTM buffer object. + * + * This structure contains also driver private validation info + * on top of the info needed by TTM. + */ +struct vmw_validate_buffer { +	struct ttm_validate_buffer base; +	struct drm_hash_item hash; +	bool validate_as_mob; +}; + +struct vmw_res_func;  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); +	unsigned long backup_size; +	bool res_dirty; /* Protected by backup buffer reserved */ +	bool backup_dirty; /* Protected by backup buffer reserved */ +	struct vmw_dma_buffer *backup; +	unsigned long backup_offset; +	const struct vmw_res_func *func; +	struct list_head lru_head; /* Protected by the resource lock */ +	struct list_head mob_head; /* Protected by @backup reserved */ +	struct list_head binding_head; /* Protected by binding_mutex */  	void (*res_free) (struct vmw_resource *res); +	void (*hw_destroy) (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 +enum vmw_res_type { +	vmw_res_context, +	vmw_res_surface, +	vmw_res_stream, +	vmw_res_shader, +	vmw_res_max  };  struct vmw_cursor_snooper { @@ -90,21 +138,26 @@ struct vmw_cursor_snooper {  	uint32_t *image;  }; +struct vmw_framebuffer; +struct vmw_surface_offset; +  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 base_size;  	struct drm_vmw_size *sizes;  	uint32_t num_sizes; -  	bool scanout; -  	/* TODO so far just a extra pointer */  	struct vmw_cursor_snooper snooper; +	struct vmw_surface_offset *offsets; +	SVGA3dTextureFilter autogen_filter; +	uint32_t multisample_count;  }; -struct vmw_fence_queue { +struct vmw_marker_queue {  	struct list_head head;  	struct timespec lag;  	struct timespec lag_time; @@ -115,36 +168,180 @@ 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 mutex fifo_mutex;  	struct rw_semaphore rwsem; -	struct vmw_fence_queue fence_queue; +	struct vmw_marker_queue marker_queue;  };  struct vmw_relocation { +	SVGAMobId *mob_loc;  	SVGAGuestPtr *location;  	uint32_t index;  }; +/** + * struct vmw_res_cache_entry - resource information cache entry + * + * @valid: Whether the entry is valid, which also implies that the execbuf + * code holds a reference to the resource, and it's placed on the + * validation list. + * @handle: User-space handle of a resource. + * @res: Non-ref-counted pointer to the resource. + * + * Used to avoid frequent repeated user-space handle lookups of the + * same resource. + */ +struct vmw_res_cache_entry { +	bool valid; +	uint32_t handle; +	struct vmw_resource *res; +	struct vmw_resource_val_node *node; +}; + +/** + * enum vmw_dma_map_mode - indicate how to perform TTM page dma mappings. + */ +enum vmw_dma_map_mode { +	vmw_dma_phys,           /* Use physical page addresses */ +	vmw_dma_alloc_coherent, /* Use TTM coherent pages */ +	vmw_dma_map_populate,   /* Unmap from DMA just after unpopulate */ +	vmw_dma_map_bind,       /* Unmap from DMA just before unbind */ +	vmw_dma_map_max +}; + +/** + * struct vmw_sg_table - Scatter/gather table for binding, with additional + * device-specific information. + * + * @sgt: Pointer to a struct sg_table with binding information + * @num_regions: Number of regions with device-address contigous pages + */ +struct vmw_sg_table { +	enum vmw_dma_map_mode mode; +	struct page **pages; +	const dma_addr_t *addrs; +	struct sg_table *sgt; +	unsigned long num_regions; +	unsigned long num_pages; +}; + +/** + * struct vmw_piter - Page iterator that iterates over a list of pages + * and DMA addresses that could be either a scatter-gather list or + * arrays + * + * @pages: Array of page pointers to the pages. + * @addrs: DMA addresses to the pages if coherent pages are used. + * @iter: Scatter-gather page iterator. Current position in SG list. + * @i: Current position in arrays. + * @num_pages: Number of pages total. + * @next: Function to advance the iterator. Returns false if past the list + * of pages, true otherwise. + * @dma_address: Function to return the DMA address of the current page. + */ +struct vmw_piter { +	struct page **pages; +	const dma_addr_t *addrs; +	struct sg_page_iter iter; +	unsigned long i; +	unsigned long num_pages; +	bool (*next)(struct vmw_piter *); +	dma_addr_t (*dma_address)(struct vmw_piter *); +	struct page *(*page)(struct vmw_piter *); +}; + +/* + * enum vmw_ctx_binding_type - abstract resource to context binding types + */ +enum vmw_ctx_binding_type { +	vmw_ctx_binding_shader, +	vmw_ctx_binding_rt, +	vmw_ctx_binding_tex, +	vmw_ctx_binding_max +}; + +/** + * struct vmw_ctx_bindinfo - structure representing a single context binding + * + * @ctx: Pointer to the context structure. NULL means the binding is not + * active. + * @res: Non ref-counted pointer to the bound resource. + * @bt: The binding type. + * @i1: Union of information needed to unbind. + */ +struct vmw_ctx_bindinfo { +	struct vmw_resource *ctx; +	struct vmw_resource *res; +	enum vmw_ctx_binding_type bt; +	bool scrubbed; +	union { +		SVGA3dShaderType shader_type; +		SVGA3dRenderTargetType rt_type; +		uint32 texture_stage; +	} i1; +}; + +/** + * struct vmw_ctx_binding - structure representing a single context binding + *                        - suitable for tracking in a context + * + * @ctx_list: List head for context. + * @res_list: List head for bound resource. + * @bi: Binding info + */ +struct vmw_ctx_binding { +	struct list_head ctx_list; +	struct list_head res_list; +	struct vmw_ctx_bindinfo bi; +}; + + +/** + * struct vmw_ctx_binding_state - context binding state + * + * @list: linked list of individual bindings. + * @render_targets: Render target bindings. + * @texture_units: Texture units/samplers bindings. + * @shaders: Shader bindings. + * + * Note that this structure also provides storage space for the individual + * struct vmw_ctx_binding objects, so that no dynamic allocation is needed + * for individual bindings. + * + */ +struct vmw_ctx_binding_state { +	struct list_head list; +	struct vmw_ctx_binding render_targets[SVGA3D_RT_MAX]; +	struct vmw_ctx_binding texture_units[SVGA3D_NUM_TEXTURE_UNITS]; +	struct vmw_ctx_binding shaders[SVGA3D_SHADERTYPE_MAX]; +}; +  struct vmw_sw_context{ -	struct ida bo_list; -	uint32_t last_cid; -	bool cid_valid; -	uint32_t last_sid; -	uint32_t sid_translation; -	bool sid_valid; -	struct ttm_object_file *tfile; +	struct drm_open_hash res_ht; +	bool res_ht_initialized; +	bool kernel; /**< is the called made from the kernel */ +	struct vmw_fpriv *fp;  	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]; +	struct vmw_validate_buffer val_bufs[VMWGFX_MAX_VALIDATIONS];  	uint32_t cur_val_buf; +	uint32_t *cmd_bounce; +	uint32_t cmd_bounce_size; +	struct list_head resource_list; +	uint32_t fence_flags; +	struct ttm_buffer_object *cur_query_bo; +	struct list_head res_relocations; +	uint32_t *buf_start; +	struct vmw_res_cache_entry res_cache[vmw_res_max]; +	struct vmw_resource *last_query_ctx; +	bool needs_post_query_barrier; +	struct vmw_resource *error_resource; +	struct vmw_ctx_binding_state staged_bindings; +	struct list_head staged_shaders;  };  struct vmw_legacy_display; @@ -176,16 +373,23 @@ struct vmw_private {  	unsigned int io_start;  	uint32_t vram_start;  	uint32_t vram_size; +	uint32_t prim_bb_mem;  	uint32_t mmio_start;  	uint32_t mmio_size;  	uint32_t fb_max_width;  	uint32_t fb_max_height; +	uint32_t initial_width; +	uint32_t initial_height;  	__le32 __iomem *mmio_virt;  	int mmio_mtrr;  	uint32_t capabilities; -	uint32_t max_gmr_descriptors;  	uint32_t max_gmr_ids; +	uint32_t max_gmr_pages; +	uint32_t max_mob_pages; +	uint32_t max_mob_size; +	uint32_t memory_size;  	bool has_gmr; +	bool has_mob;  	struct mutex hw_mutex;  	/* @@ -195,12 +399,7 @@ struct vmw_private {  	struct vmw_vga_topology_state vga_save[VMWGFX_MAX_DISPLAYS];  	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_green_mask; -	uint32_t vga_blue_mask;  	uint32_t vga_bpl;  	uint32_t vga_pitchlock; @@ -212,6 +411,7 @@ struct vmw_private {  	void *fb_info;  	struct vmw_legacy_display *ldu_priv; +	struct vmw_screen_object_display *sou_priv;  	struct vmw_overlay *overlay_priv;  	/* @@ -219,10 +419,7 @@ struct vmw_private {  	 */  	rwlock_t resource_lock; -	struct idr context_idr; -	struct idr surface_idr; -	struct idr stream_idr; - +	struct idr res_idr[vmw_res_max];  	/*  	 * Block lastclose from racing with firstopen.  	 */ @@ -240,13 +437,16 @@ struct vmw_private {  	 * Fencing and IRQs.  	 */ -	atomic_t fence_seq; +	atomic_t marker_seq;  	wait_queue_head_t fence_queue;  	wait_queue_head_t fifo_queue; -	atomic_t fence_queue_waiters; +	int fence_queue_waiters; /* Protected by hw_mutex */ +	int goal_queue_waiters; /* Protected by hw_mutex */  	atomic_t fifo_queue_waiters; -	uint32_t last_read_sequence; +	uint32_t last_read_seqno;  	spinlock_t irq_lock; +	struct vmw_fence_manager *fman; +	uint32_t irq_mask;  	/*  	 * Device state @@ -264,15 +464,14 @@ struct vmw_private {  	 */  	struct vmw_sw_context ctx; -	uint32_t val_seq;  	struct mutex cmdbuf_mutex; +	struct mutex binding_mutex;  	/**  	 * Operating mode.  	 */  	bool stealth; -	bool is_opened;  	bool enable_fb;  	/** @@ -286,8 +485,50 @@ struct vmw_private {  	struct mutex release_mutex;  	uint32_t num_3d_resources; + +	/* +	 * Replace this with an rwsem as soon as we have down_xx_interruptible() +	 */ +	struct ttm_lock reservation_sem; + +	/* +	 * Query processing. These members +	 * are protected by the cmdbuf mutex. +	 */ + +	struct ttm_buffer_object *dummy_query_bo; +	struct ttm_buffer_object *pinned_bo; +	uint32_t query_cid; +	uint32_t query_cid_valid; +	bool dummy_query_bo_pinned; + +	/* +	 * Surface swapping. The "surface_lru" list is protected by the +	 * resource lock in order to be able to destroy a surface and take +	 * it off the lru atomically. "used_memory_size" is currently +	 * protected by the cmdbuf mutex for simplicity. +	 */ + +	struct list_head res_lru[vmw_res_max]; +	uint32_t used_memory_size; + +	/* +	 * DMA mapping stuff. +	 */ +	enum vmw_dma_map_mode map_mode; + +	/* +	 * Guest Backed stuff +	 */ +	struct ttm_buffer_object *otable_bo; +	struct vmw_otable *otables;  }; +static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res) +{ +	return container_of(res, struct vmw_surface, res); +} +  static inline struct vmw_private *vmw_priv(struct drm_device *dev)  {  	return (struct vmw_private *)dev->dev_private; @@ -320,15 +561,15 @@ static inline uint32_t vmw_read(struct vmw_private *dev_priv,  	return val;  } -int vmw_3d_resource_inc(struct vmw_private *dev_priv); -void vmw_3d_resource_dec(struct vmw_private *dev_priv); +int vmw_3d_resource_inc(struct vmw_private *dev_priv, bool unhide_svga); +void vmw_3d_resource_dec(struct vmw_private *dev_priv, bool hide_svga);  /**   * GMR utilities - vmwgfx_gmr.c   */  extern int vmw_gmr_bind(struct vmw_private *dev_priv, -			struct page *pages[], +			const struct vmw_sg_table *vsgt,  			unsigned long num_pages,  			int gmr_id);  extern void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id); @@ -336,53 +577,54 @@ extern void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id);  /**   * Resource utilities - vmwgfx_resource.c   */ +struct vmw_user_resource_conv; -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_handle(struct vmw_private *dev_priv, -					  struct ttm_object_file *tfile, -					  uint32_t handle, -					  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, -			     uint32_t handle, int *id); +extern struct vmw_resource * +vmw_resource_reference_unless_doomed(struct vmw_resource *res); +extern int vmw_resource_validate(struct vmw_resource *res); +extern int vmw_resource_reserve(struct vmw_resource *res, bool no_backup); +extern bool vmw_resource_needs_backup(const struct vmw_resource *res); +extern int vmw_user_lookup_handle(struct vmw_private *dev_priv, +				  struct ttm_object_file *tfile, +				  uint32_t handle, +				  struct vmw_surface **out_surf, +				  struct vmw_dma_buffer **out_buf); +extern int vmw_user_resource_lookup_handle( +	struct vmw_private *dev_priv, +	struct ttm_object_file *tfile, +	uint32_t handle, +	const struct vmw_user_resource_conv *converter, +	struct vmw_resource **p_res);  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_user_dmabuf_verify_access(struct ttm_buffer_object *bo, +				  struct ttm_object_file *tfile); +extern int vmw_user_dmabuf_alloc(struct vmw_private *dev_priv, +				 struct ttm_object_file *tfile, +				 uint32_t size, +				 bool shareable, +				 uint32_t *handle, +				 struct vmw_dma_buffer **p_dma_buf); +extern int vmw_user_dmabuf_reference(struct ttm_object_file *tfile, +				     struct vmw_dma_buffer *dma_buf, +				     uint32_t *handle);  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 int vmw_user_dmabuf_synccpu_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 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, @@ -391,7 +633,37 @@ extern int vmw_user_stream_lookup(struct vmw_private *dev_priv,  				  struct ttm_object_file *tfile,  				  uint32_t *inout_id,  				  struct vmw_resource **out); +extern void vmw_resource_unreserve(struct vmw_resource *res, +				   struct vmw_dma_buffer *new_backup, +				   unsigned long new_backup_offset); +extern void vmw_resource_move_notify(struct ttm_buffer_object *bo, +				     struct ttm_mem_reg *mem); +extern void vmw_fence_single_bo(struct ttm_buffer_object *bo, +				struct vmw_fence_obj *fence); +extern void vmw_resource_evict_all(struct vmw_private *dev_priv); +/** + * DMA buffer helper routines - vmwgfx_dmabuf.c + */ +extern int vmw_dmabuf_to_placement(struct vmw_private *vmw_priv, +				   struct vmw_dma_buffer *bo, +				   struct ttm_placement *placement, +				   bool interruptible); +extern int vmw_dmabuf_to_vram(struct vmw_private *dev_priv, +			      struct vmw_dma_buffer *buf, +			      bool pin, bool interruptible); +extern int vmw_dmabuf_to_vram_or_gmr(struct vmw_private *dev_priv, +				     struct vmw_dma_buffer *buf, +				     bool pin, bool interruptible); +extern int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv, +				       struct vmw_dma_buffer *bo, +				       bool pin, bool interruptible); +extern int vmw_dmabuf_unpin(struct vmw_private *vmw_priv, +			    struct vmw_dma_buffer *bo, +			    bool interruptible); +extern void vmw_bo_get_guest_ptr(const struct ttm_buffer_object *buf, +				 SVGAGuestPtr *ptr); +extern void vmw_bo_pin(struct ttm_buffer_object *bo, bool pin);  /**   * Misc Ioctl functionality - vmwgfx_ioctl.c @@ -399,8 +671,16 @@ extern int vmw_user_stream_lookup(struct vmw_private *dev_priv,  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, +extern int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data,  				struct drm_file *file_priv); +extern int vmw_present_ioctl(struct drm_device *dev, void *data, +			     struct drm_file *file_priv); +extern int vmw_present_readback_ioctl(struct drm_device *dev, void *data, +				      struct drm_file *file_priv); +extern unsigned int vmw_fops_poll(struct file *filp, +				  struct poll_table_struct *wait); +extern ssize_t vmw_fops_read(struct file *filp, char __user *buffer, +			     size_t count, loff_t *offset);  /**   * Fifo utilities - vmwgfx_fifo.c @@ -413,11 +693,12 @@ extern void vmw_fifo_release(struct vmw_private *dev_priv,  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); +			       uint32_t *seqno);  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);  extern bool vmw_fifo_have_3d(struct vmw_private *dev_priv);  extern bool vmw_fifo_have_pitchlock(struct vmw_private *dev_priv); +extern int vmw_fifo_emit_dummy_query(struct vmw_private *dev_priv, +				     uint32_t cid);  /**   * TTM glue - vmwgfx_ttm_glue.c @@ -431,13 +712,62 @@ extern int vmw_mmap(struct file *filp, struct vm_area_struct *vma);   * TTM buffer object driver - vmwgfx_buffer.c   */ +extern const size_t vmw_tt_size;  extern struct ttm_placement vmw_vram_placement;  extern struct ttm_placement vmw_vram_ne_placement;  extern struct ttm_placement vmw_vram_sys_placement;  extern struct ttm_placement vmw_vram_gmr_placement; +extern struct ttm_placement vmw_vram_gmr_ne_placement;  extern struct ttm_placement vmw_sys_placement; +extern struct ttm_placement vmw_sys_ne_placement; +extern struct ttm_placement vmw_evictable_placement; +extern struct ttm_placement vmw_srf_placement; +extern struct ttm_placement vmw_mob_placement;  extern struct ttm_bo_driver vmw_bo_driver;  extern int vmw_dma_quiescent(struct drm_device *dev); +extern int vmw_bo_map_dma(struct ttm_buffer_object *bo); +extern void vmw_bo_unmap_dma(struct ttm_buffer_object *bo); +extern const struct vmw_sg_table * +vmw_bo_sg_table(struct ttm_buffer_object *bo); +extern void vmw_piter_start(struct vmw_piter *viter, +			    const struct vmw_sg_table *vsgt, +			    unsigned long p_offs); + +/** + * vmw_piter_next - Advance the iterator one page. + * + * @viter: Pointer to the iterator to advance. + * + * Returns false if past the list of pages, true otherwise. + */ +static inline bool vmw_piter_next(struct vmw_piter *viter) +{ +	return viter->next(viter); +} + +/** + * vmw_piter_dma_addr - Return the DMA address of the current page. + * + * @viter: Pointer to the iterator + * + * Returns the DMA address of the page pointed to by @viter. + */ +static inline dma_addr_t vmw_piter_dma_addr(struct vmw_piter *viter) +{ +	return viter->dma_address(viter); +} + +/** + * vmw_piter_page - Return a pointer to the current page. + * + * @viter: Pointer to the iterator + * + * Returns the DMA address of the page pointed to by @viter. + */ +static inline struct page *vmw_piter_page(struct vmw_piter *viter) +{ +	return viter->page(viter); +}  /**   * Command submission - vmwgfx_execbuf.c @@ -445,45 +775,70 @@ extern int vmw_dma_quiescent(struct drm_device *dev);  extern int vmw_execbuf_ioctl(struct drm_device *dev, void *data,  			     struct drm_file *file_priv); +extern int vmw_execbuf_process(struct drm_file *file_priv, +			       struct vmw_private *dev_priv, +			       void __user *user_commands, +			       void *kernel_commands, +			       uint32_t command_size, +			       uint64_t throttle_us, +			       struct drm_vmw_fence_rep __user +			       *user_fence_rep, +			       struct vmw_fence_obj **out_fence); +extern void __vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv, +					    struct vmw_fence_obj *fence); +extern void vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv); + +extern int vmw_execbuf_fence_commands(struct drm_file *file_priv, +				      struct vmw_private *dev_priv, +				      struct vmw_fence_obj **p_fence, +				      uint32_t *p_handle); +extern void vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv, +					struct vmw_fpriv *vmw_fp, +					int ret, +					struct drm_vmw_fence_rep __user +					*user_fence_rep, +					struct vmw_fence_obj *fence, +					uint32_t fence_handle);  /**   * 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 irqreturn_t vmw_irq_handler(int irq, void *arg); +extern int vmw_wait_seqno(struct vmw_private *dev_priv, bool lazy, +			     uint32_t seqno, 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 bool vmw_seqno_passed(struct vmw_private *dev_priv, +				uint32_t seqno);  extern int vmw_fallback_wait(struct vmw_private *dev_priv,  			     bool lazy,  			     bool fifo_idle, -			     uint32_t sequence, +			     uint32_t seqno,  			     bool interruptible,  			     unsigned long timeout); -extern void vmw_update_sequence(struct vmw_private *dev_priv, +extern void vmw_update_seqno(struct vmw_private *dev_priv,  				struct vmw_fifo_state *fifo_state); - +extern void vmw_seqno_waiter_add(struct vmw_private *dev_priv); +extern void vmw_seqno_waiter_remove(struct vmw_private *dev_priv); +extern void vmw_goal_waiter_add(struct vmw_private *dev_priv); +extern void vmw_goal_waiter_remove(struct vmw_private *dev_priv);  /** - * Rudimentary fence objects currently used only for throttling - - * vmwgfx_fence.c + * Rudimentary fence-like objects currently used only for throttling - + * vmwgfx_marker.c   */ -extern void vmw_fence_queue_init(struct vmw_fence_queue *queue); -extern void vmw_fence_queue_takedown(struct vmw_fence_queue *queue); -extern int vmw_fence_push(struct vmw_fence_queue *queue, -			  uint32_t sequence); -extern int vmw_fence_pull(struct vmw_fence_queue *queue, -			  uint32_t signaled_sequence); +extern void vmw_marker_queue_init(struct vmw_marker_queue *queue); +extern void vmw_marker_queue_takedown(struct vmw_marker_queue *queue); +extern int vmw_marker_push(struct vmw_marker_queue *queue, +			  uint32_t seqno); +extern int vmw_marker_pull(struct vmw_marker_queue *queue, +			  uint32_t signaled_seqno);  extern int vmw_wait_lag(struct vmw_private *dev_priv, -			struct vmw_fence_queue *queue, uint32_t us); +			struct vmw_marker_queue *queue, uint32_t us);  /**   * Kernel framebuffer - vmwgfx_fb.c @@ -509,17 +864,42 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf,  			  struct ttm_object_file *tfile,  			  struct ttm_buffer_object *bo,  			  SVGA3dCmdHeader *header); -void vmw_kms_write_svga(struct vmw_private *vmw_priv, -			unsigned width, unsigned height, unsigned pitch, -			unsigned bbp, unsigned depth); -int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, -				struct drm_file *file_priv); +int vmw_kms_write_svga(struct vmw_private *vmw_priv, +		       unsigned width, unsigned height, unsigned pitch, +		       unsigned bpp, unsigned depth);  void vmw_kms_idle_workqueues(struct vmw_master *vmaster);  bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv,  				uint32_t pitch,  				uint32_t height);  u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc); +int vmw_enable_vblank(struct drm_device *dev, int crtc); +void vmw_disable_vblank(struct drm_device *dev, int crtc); +int vmw_kms_present(struct vmw_private *dev_priv, +		    struct drm_file *file_priv, +		    struct vmw_framebuffer *vfb, +		    struct vmw_surface *surface, +		    uint32_t sid, int32_t destX, int32_t destY, +		    struct drm_vmw_rect *clips, +		    uint32_t num_clips); +int vmw_kms_readback(struct vmw_private *dev_priv, +		     struct drm_file *file_priv, +		     struct vmw_framebuffer *vfb, +		     struct drm_vmw_fence_rep __user *user_fence_rep, +		     struct drm_vmw_rect *clips, +		     uint32_t num_clips); +int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, +				struct drm_file *file_priv); + +int vmw_dumb_create(struct drm_file *file_priv, +		    struct drm_device *dev, +		    struct drm_mode_create_dumb *args); +int vmw_dumb_map_offset(struct drm_file *file_priv, +			struct drm_device *dev, uint32_t handle, +			uint64_t *offset); +int vmw_dumb_destroy(struct drm_file *file_priv, +		     struct drm_device *dev, +		     uint32_t handle);  /**   * Overlay control - vmwgfx_overlay.c   */ @@ -543,6 +923,115 @@ int vmw_overlay_num_free_overlays(struct vmw_private *dev_priv);  extern const struct ttm_mem_type_manager_func vmw_gmrid_manager_func;  /** + * Prime - vmwgfx_prime.c + */ + +extern const struct dma_buf_ops vmw_prime_dmabuf_ops; +extern int vmw_prime_fd_to_handle(struct drm_device *dev, +				  struct drm_file *file_priv, +				  int fd, u32 *handle); +extern int vmw_prime_handle_to_fd(struct drm_device *dev, +				  struct drm_file *file_priv, +				  uint32_t handle, uint32_t flags, +				  int *prime_fd); + +/* + * MemoryOBject management -  vmwgfx_mob.c + */ +struct vmw_mob; +extern int vmw_mob_bind(struct vmw_private *dev_priv, struct vmw_mob *mob, +			const struct vmw_sg_table *vsgt, +			unsigned long num_data_pages, int32_t mob_id); +extern void vmw_mob_unbind(struct vmw_private *dev_priv, +			   struct vmw_mob *mob); +extern void vmw_mob_destroy(struct vmw_mob *mob); +extern struct vmw_mob *vmw_mob_create(unsigned long data_pages); +extern int vmw_otables_setup(struct vmw_private *dev_priv); +extern void vmw_otables_takedown(struct vmw_private *dev_priv); + +/* + * Context management - vmwgfx_context.c + */ + +extern const struct vmw_user_resource_conv *user_context_converter; + +extern struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv); + +extern int vmw_context_check(struct vmw_private *dev_priv, +			     struct ttm_object_file *tfile, +			     int id, +			     struct vmw_resource **p_res); +extern int vmw_context_define_ioctl(struct drm_device *dev, void *data, +				    struct drm_file *file_priv); +extern int vmw_context_destroy_ioctl(struct drm_device *dev, void *data, +				     struct drm_file *file_priv); +extern int vmw_context_binding_add(struct vmw_ctx_binding_state *cbs, +				   const struct vmw_ctx_bindinfo *ci); +extern void +vmw_context_binding_state_transfer(struct vmw_resource *res, +				   struct vmw_ctx_binding_state *cbs); +extern void vmw_context_binding_res_list_kill(struct list_head *head); +extern void vmw_context_binding_res_list_scrub(struct list_head *head); +extern int vmw_context_rebind_all(struct vmw_resource *ctx); +extern struct list_head *vmw_context_binding_list(struct vmw_resource *ctx); + +/* + * Surface management - vmwgfx_surface.c + */ + +extern const struct vmw_user_resource_conv *user_surface_converter; + +extern void vmw_surface_res_free(struct vmw_resource *res); +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_gb_surface_define_ioctl(struct drm_device *dev, void *data, +				       struct drm_file *file_priv); +extern int vmw_gb_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, +			     uint32_t handle, int *id); +extern int vmw_surface_validate(struct vmw_private *dev_priv, +				struct vmw_surface *srf); + +/* + * Shader management - vmwgfx_shader.c + */ + +extern const struct vmw_user_resource_conv *user_shader_converter; + +extern int vmw_shader_define_ioctl(struct drm_device *dev, void *data, +				   struct drm_file *file_priv); +extern int vmw_shader_destroy_ioctl(struct drm_device *dev, void *data, +				    struct drm_file *file_priv); +extern int vmw_compat_shader_lookup(struct vmw_compat_shader_manager *man, +				    SVGA3dShaderType shader_type, +				    u32 *user_key); +extern void vmw_compat_shaders_commit(struct vmw_compat_shader_manager *man, +				      struct list_head *list); +extern void vmw_compat_shaders_revert(struct vmw_compat_shader_manager *man, +				      struct list_head *list); +extern int vmw_compat_shader_remove(struct vmw_compat_shader_manager *man, +				    u32 user_key, +				    SVGA3dShaderType shader_type, +				    struct list_head *list); +extern int vmw_compat_shader_add(struct vmw_compat_shader_manager *man, +				 u32 user_key, const void *bytecode, +				 SVGA3dShaderType shader_type, +				 size_t size, +				 struct ttm_object_file *tfile, +				 struct list_head *list); +extern struct vmw_compat_shader_manager * +vmw_compat_shader_man_create(struct vmw_private *dev_priv); +extern void +vmw_compat_shader_man_destroy(struct vmw_compat_shader_manager *man); + + +/**   * Inline helper functions   */ @@ -564,10 +1053,13 @@ static inline struct vmw_surface *vmw_surface_reference(struct vmw_surface *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; +	if (tmp_buf != NULL) { +		struct ttm_buffer_object *bo = &tmp_buf->base; -	ttm_bo_unref(&bo); +		ttm_bo_unref(&bo); +	}  }  static inline struct vmw_dma_buffer *vmw_dmabuf_reference(struct vmw_dma_buffer *buf) @@ -577,4 +1069,8 @@ static inline struct vmw_dma_buffer *vmw_dmabuf_reference(struct vmw_dma_buffer  	return NULL;  } +static inline struct ttm_mem_global *vmw_mem_glob(struct vmw_private *dev_priv) +{ +	return (struct ttm_mem_global *) dev_priv->mem_global_ref.object; +}  #endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 76954e3528c..87df0b3674f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -27,8 +27,259 @@  #include "vmwgfx_drv.h"  #include "vmwgfx_reg.h" -#include "ttm/ttm_bo_api.h" -#include "ttm/ttm_placement.h" +#include <drm/ttm/ttm_bo_api.h> +#include <drm/ttm/ttm_placement.h> + +#define VMW_RES_HT_ORDER 12 + +/** + * struct vmw_resource_relocation - Relocation info for resources + * + * @head: List head for the software context's relocation list. + * @res: Non-ref-counted pointer to the resource. + * @offset: Offset of 4 byte entries into the command buffer where the + * id that needs fixup is located. + */ +struct vmw_resource_relocation { +	struct list_head head; +	const struct vmw_resource *res; +	unsigned long offset; +}; + +/** + * struct vmw_resource_val_node - Validation info for resources + * + * @head: List head for the software context's resource list. + * @hash: Hash entry for quick resouce to val_node lookup. + * @res: Ref-counted pointer to the resource. + * @switch_backup: Boolean whether to switch backup buffer on unreserve. + * @new_backup: Refcounted pointer to the new backup buffer. + * @staged_bindings: If @res is a context, tracks bindings set up during + * the command batch. Otherwise NULL. + * @new_backup_offset: New backup buffer offset if @new_backup is non-NUll. + * @first_usage: Set to true the first time the resource is referenced in + * the command stream. + * @no_buffer_needed: Resources do not need to allocate buffer backup on + * reservation. The command stream will provide one. + */ +struct vmw_resource_val_node { +	struct list_head head; +	struct drm_hash_item hash; +	struct vmw_resource *res; +	struct vmw_dma_buffer *new_backup; +	struct vmw_ctx_binding_state *staged_bindings; +	unsigned long new_backup_offset; +	bool first_usage; +	bool no_buffer_needed; +}; + +/** + * struct vmw_cmd_entry - Describe a command for the verifier + * + * @user_allow: Whether allowed from the execbuf ioctl. + * @gb_disable: Whether disabled if guest-backed objects are available. + * @gb_enable: Whether enabled iff guest-backed objects are available. + */ +struct vmw_cmd_entry { +	int (*func) (struct vmw_private *, struct vmw_sw_context *, +		     SVGA3dCmdHeader *); +	bool user_allow; +	bool gb_disable; +	bool gb_enable; +}; + +#define VMW_CMD_DEF(_cmd, _func, _user_allow, _gb_disable, _gb_enable)	\ +	[(_cmd) - SVGA_3D_CMD_BASE] = {(_func), (_user_allow),\ +				       (_gb_disable), (_gb_enable)} + +/** + * vmw_resource_unreserve - unreserve resources previously reserved for + * command submission. + * + * @list_head: list of resources to unreserve. + * @backoff: Whether command submission failed. + */ +static void vmw_resource_list_unreserve(struct list_head *list, +					bool backoff) +{ +	struct vmw_resource_val_node *val; + +	list_for_each_entry(val, list, head) { +		struct vmw_resource *res = val->res; +		struct vmw_dma_buffer *new_backup = +			backoff ? NULL : val->new_backup; + +		/* +		 * Transfer staged context bindings to the +		 * persistent context binding tracker. +		 */ +		if (unlikely(val->staged_bindings)) { +			if (!backoff) { +				vmw_context_binding_state_transfer +					(val->res, val->staged_bindings); +			} +			kfree(val->staged_bindings); +			val->staged_bindings = NULL; +		} +		vmw_resource_unreserve(res, new_backup, +			val->new_backup_offset); +		vmw_dmabuf_unreference(&val->new_backup); +	} +} + + +/** + * vmw_resource_val_add - Add a resource to the software context's + * resource list if it's not already on it. + * + * @sw_context: Pointer to the software context. + * @res: Pointer to the resource. + * @p_node On successful return points to a valid pointer to a + * struct vmw_resource_val_node, if non-NULL on entry. + */ +static int vmw_resource_val_add(struct vmw_sw_context *sw_context, +				struct vmw_resource *res, +				struct vmw_resource_val_node **p_node) +{ +	struct vmw_resource_val_node *node; +	struct drm_hash_item *hash; +	int ret; + +	if (likely(drm_ht_find_item(&sw_context->res_ht, (unsigned long) res, +				    &hash) == 0)) { +		node = container_of(hash, struct vmw_resource_val_node, hash); +		node->first_usage = false; +		if (unlikely(p_node != NULL)) +			*p_node = node; +		return 0; +	} + +	node = kzalloc(sizeof(*node), GFP_KERNEL); +	if (unlikely(node == NULL)) { +		DRM_ERROR("Failed to allocate a resource validation " +			  "entry.\n"); +		return -ENOMEM; +	} + +	node->hash.key = (unsigned long) res; +	ret = drm_ht_insert_item(&sw_context->res_ht, &node->hash); +	if (unlikely(ret != 0)) { +		DRM_ERROR("Failed to initialize a resource validation " +			  "entry.\n"); +		kfree(node); +		return ret; +	} +	list_add_tail(&node->head, &sw_context->resource_list); +	node->res = vmw_resource_reference(res); +	node->first_usage = true; + +	if (unlikely(p_node != NULL)) +		*p_node = node; + +	return 0; +} + +/** + * vmw_resource_context_res_add - Put resources previously bound to a context on + * the validation list + * + * @dev_priv: Pointer to a device private structure + * @sw_context: Pointer to a software context used for this command submission + * @ctx: Pointer to the context resource + * + * This function puts all resources that were previously bound to @ctx on + * the resource validation list. This is part of the context state reemission + */ +static int vmw_resource_context_res_add(struct vmw_private *dev_priv, +					struct vmw_sw_context *sw_context, +					struct vmw_resource *ctx) +{ +	struct list_head *binding_list; +	struct vmw_ctx_binding *entry; +	int ret = 0; +	struct vmw_resource *res; + +	mutex_lock(&dev_priv->binding_mutex); +	binding_list = vmw_context_binding_list(ctx); + +	list_for_each_entry(entry, binding_list, ctx_list) { +		res = vmw_resource_reference_unless_doomed(entry->bi.res); +		if (unlikely(res == NULL)) +			continue; + +		ret = vmw_resource_val_add(sw_context, entry->bi.res, NULL); +		vmw_resource_unreference(&res); +		if (unlikely(ret != 0)) +			break; +	} + +	mutex_unlock(&dev_priv->binding_mutex); +	return ret; +} + +/** + * vmw_resource_relocation_add - Add a relocation to the relocation list + * + * @list: Pointer to head of relocation list. + * @res: The resource. + * @offset: Offset into the command buffer currently being parsed where the + * id that needs fixup is located. Granularity is 4 bytes. + */ +static int vmw_resource_relocation_add(struct list_head *list, +				       const struct vmw_resource *res, +				       unsigned long offset) +{ +	struct vmw_resource_relocation *rel; + +	rel = kmalloc(sizeof(*rel), GFP_KERNEL); +	if (unlikely(rel == NULL)) { +		DRM_ERROR("Failed to allocate a resource relocation.\n"); +		return -ENOMEM; +	} + +	rel->res = res; +	rel->offset = offset; +	list_add_tail(&rel->head, list); + +	return 0; +} + +/** + * vmw_resource_relocations_free - Free all relocations on a list + * + * @list: Pointer to the head of the relocation list. + */ +static void vmw_resource_relocations_free(struct list_head *list) +{ +	struct vmw_resource_relocation *rel, *n; + +	list_for_each_entry_safe(rel, n, list, head) { +		list_del(&rel->head); +		kfree(rel); +	} +} + +/** + * vmw_resource_relocations_apply - Apply all relocations on a list + * + * @cb: Pointer to the start of the command buffer bein patch. This need + * not be the same buffer as the one being parsed when the relocation + * list was built, but the contents must be the same modulo the + * resource ids. + * @list: Pointer to the head of the relocation list. + */ +static void vmw_resource_relocations_apply(uint32_t *cb, +					   struct list_head *list) +{ +	struct vmw_resource_relocation *rel; + +	list_for_each_entry(rel, list, head) { +		if (likely(rel->res != NULL)) +			cb[rel->offset] = rel->res->id; +		else +			cb[rel->offset] = SVGA_3D_CMD_NOP; +	} +}  static int vmw_cmd_invalid(struct vmw_private *dev_priv,  			   struct vmw_sw_context *sw_context, @@ -44,64 +295,320 @@ static int vmw_cmd_ok(struct vmw_private *dev_priv,  	return 0;  } -static int vmw_cmd_cid_check(struct vmw_private *dev_priv, -			     struct vmw_sw_context *sw_context, -			     SVGA3dCmdHeader *header) +/** + * vmw_bo_to_validate_list - add a bo to a validate list + * + * @sw_context: The software context used for this command submission batch. + * @bo: The buffer object to add. + * @validate_as_mob: Validate this buffer as a MOB. + * @p_val_node: If non-NULL Will be updated with the validate node number + * on return. + * + * Returns -EINVAL if the limit of number of buffer objects per command + * submission is reached. + */ +static int vmw_bo_to_validate_list(struct vmw_sw_context *sw_context, +				   struct ttm_buffer_object *bo, +				   bool validate_as_mob, +				   uint32_t *p_val_node)  { -	struct vmw_cid_cmd { -		SVGA3dCmdHeader header; -		__le32 cid; -	} *cmd; +	uint32_t val_node; +	struct vmw_validate_buffer *vval_buf; +	struct ttm_validate_buffer *val_buf; +	struct drm_hash_item *hash;  	int ret; -	cmd = container_of(header, struct vmw_cid_cmd, header); -	if (likely(sw_context->cid_valid && cmd->cid == sw_context->last_cid)) +	if (likely(drm_ht_find_item(&sw_context->res_ht, (unsigned long) bo, +				    &hash) == 0)) { +		vval_buf = container_of(hash, struct vmw_validate_buffer, +					hash); +		if (unlikely(vval_buf->validate_as_mob != validate_as_mob)) { +			DRM_ERROR("Inconsistent buffer usage.\n"); +			return -EINVAL; +		} +		val_buf = &vval_buf->base; +		val_node = vval_buf - sw_context->val_bufs; +	} else { +		val_node = sw_context->cur_val_buf; +		if (unlikely(val_node >= VMWGFX_MAX_VALIDATIONS)) { +			DRM_ERROR("Max number of DMA buffers per submission " +				  "exceeded.\n"); +			return -EINVAL; +		} +		vval_buf = &sw_context->val_bufs[val_node]; +		vval_buf->hash.key = (unsigned long) bo; +		ret = drm_ht_insert_item(&sw_context->res_ht, &vval_buf->hash); +		if (unlikely(ret != 0)) { +			DRM_ERROR("Failed to initialize a buffer validation " +				  "entry.\n"); +			return ret; +		} +		++sw_context->cur_val_buf; +		val_buf = &vval_buf->base; +		val_buf->bo = ttm_bo_reference(bo); +		val_buf->reserved = false; +		list_add_tail(&val_buf->head, &sw_context->validate_nodes); +		vval_buf->validate_as_mob = validate_as_mob; +	} + +	sw_context->fence_flags |= DRM_VMW_FENCE_FLAG_EXEC; + +	if (p_val_node) +		*p_val_node = val_node; + +	return 0; +} + +/** + * vmw_resources_reserve - Reserve all resources on the sw_context's + * resource list. + * + * @sw_context: Pointer to the software context. + * + * Note that since vmware's command submission currently is protected by + * the cmdbuf mutex, no fancy deadlock avoidance is required for resources, + * since only a single thread at once will attempt this. + */ +static int vmw_resources_reserve(struct vmw_sw_context *sw_context) +{ +	struct vmw_resource_val_node *val; +	int ret; + +	list_for_each_entry(val, &sw_context->resource_list, head) { +		struct vmw_resource *res = val->res; + +		ret = vmw_resource_reserve(res, val->no_buffer_needed); +		if (unlikely(ret != 0)) +			return ret; + +		if (res->backup) { +			struct ttm_buffer_object *bo = &res->backup->base; + +			ret = vmw_bo_to_validate_list +				(sw_context, bo, +				 vmw_resource_needs_backup(res), NULL); + +			if (unlikely(ret != 0)) +				return ret; +		} +	} +	return 0; +} + +/** + * vmw_resources_validate - Validate all resources on the sw_context's + * resource list. + * + * @sw_context: Pointer to the software context. + * + * Before this function is called, all resource backup buffers must have + * been validated. + */ +static int vmw_resources_validate(struct vmw_sw_context *sw_context) +{ +	struct vmw_resource_val_node *val; +	int ret; + +	list_for_each_entry(val, &sw_context->resource_list, head) { +		struct vmw_resource *res = val->res; + +		ret = vmw_resource_validate(res); +		if (unlikely(ret != 0)) { +			if (ret != -ERESTARTSYS) +				DRM_ERROR("Failed to validate resource.\n"); +			return ret; +		} +	} +	return 0; +} + +/** + * vmw_cmd_compat_res_check - Check that a resource is present and if so, put it + * on the resource validate list unless it's already there. + * + * @dev_priv: Pointer to a device private structure. + * @sw_context: Pointer to the software context. + * @res_type: Resource type. + * @converter: User-space visisble type specific information. + * @id: user-space resource id handle. + * @id_loc: Pointer to the location in the command buffer currently being + * parsed from where the user-space resource id handle is located. + * @p_val: Pointer to pointer to resource validalidation node. Populated + * on exit. + */ +static int +vmw_cmd_compat_res_check(struct vmw_private *dev_priv, +			 struct vmw_sw_context *sw_context, +			 enum vmw_res_type res_type, +			 const struct vmw_user_resource_conv *converter, +			 uint32_t id, +			 uint32_t *id_loc, +			 struct vmw_resource_val_node **p_val) +{ +	struct vmw_res_cache_entry *rcache = +		&sw_context->res_cache[res_type]; +	struct vmw_resource *res; +	struct vmw_resource_val_node *node; +	int ret; + +	if (id == SVGA3D_INVALID_ID) { +		if (p_val) +			*p_val = NULL; +		if (res_type == vmw_res_context) { +			DRM_ERROR("Illegal context invalid id.\n"); +			return -EINVAL; +		}  		return 0; +	} + +	/* +	 * Fastpath in case of repeated commands referencing the same +	 * resource +	 */ + +	if (likely(rcache->valid && id == rcache->handle)) { +		const struct vmw_resource *res = rcache->res; + +		rcache->node->first_usage = false; +		if (p_val) +			*p_val = rcache->node; + +		return vmw_resource_relocation_add +			(&sw_context->res_relocations, res, +			 id_loc - sw_context->buf_start); +	} -	ret = vmw_context_check(dev_priv, sw_context->tfile, cmd->cid); +	ret = vmw_user_resource_lookup_handle(dev_priv, +					      sw_context->fp->tfile, +					      id, +					      converter, +					      &res);  	if (unlikely(ret != 0)) { -		DRM_ERROR("Could not find or use context %u\n", -			  (unsigned) cmd->cid); +		DRM_ERROR("Could not find or use resource 0x%08x.\n", +			  (unsigned) id); +		dump_stack();  		return ret;  	} -	sw_context->last_cid = cmd->cid; -	sw_context->cid_valid = true; +	rcache->valid = true; +	rcache->res = res; +	rcache->handle = id; + +	ret = vmw_resource_relocation_add(&sw_context->res_relocations, +					  res, +					  id_loc - sw_context->buf_start); +	if (unlikely(ret != 0)) +		goto out_no_reloc; + +	ret = vmw_resource_val_add(sw_context, res, &node); +	if (unlikely(ret != 0)) +		goto out_no_reloc; + +	rcache->node = node; +	if (p_val) +		*p_val = node; + +	if (dev_priv->has_mob && node->first_usage && +	    res_type == vmw_res_context) { +		ret = vmw_resource_context_res_add(dev_priv, sw_context, res); +		if (unlikely(ret != 0)) +			goto out_no_reloc; +		node->staged_bindings = +			kzalloc(sizeof(*node->staged_bindings), GFP_KERNEL); +		if (node->staged_bindings == NULL) { +			DRM_ERROR("Failed to allocate context binding " +				  "information.\n"); +			goto out_no_reloc; +		} +		INIT_LIST_HEAD(&node->staged_bindings->list); +	} +	vmw_resource_unreference(&res);  	return 0; + +out_no_reloc: +	BUG_ON(sw_context->error_resource != NULL); +	sw_context->error_resource = res; + +	return ret;  } -static int vmw_cmd_sid_check(struct vmw_private *dev_priv, -			     struct vmw_sw_context *sw_context, -			     uint32_t *sid) +/** + * vmw_cmd_res_check - Check that a resource is present and if so, put it + * on the resource validate list unless it's already there. + * + * @dev_priv: Pointer to a device private structure. + * @sw_context: Pointer to the software context. + * @res_type: Resource type. + * @converter: User-space visisble type specific information. + * @id_loc: Pointer to the location in the command buffer currently being + * parsed from where the user-space resource id handle is located. + * @p_val: Pointer to pointer to resource validalidation node. Populated + * on exit. + */ +static int +vmw_cmd_res_check(struct vmw_private *dev_priv, +		  struct vmw_sw_context *sw_context, +		  enum vmw_res_type res_type, +		  const struct vmw_user_resource_conv *converter, +		  uint32_t *id_loc, +		  struct vmw_resource_val_node **p_val)  { -	if (*sid == SVGA3D_INVALID_ID) -		return 0; +	return vmw_cmd_compat_res_check(dev_priv, sw_context, res_type, +					converter, *id_loc, id_loc, p_val); +} -	if (unlikely((!sw_context->sid_valid  || -		      *sid != sw_context->last_sid))) { -		int real_id; -		int ret = vmw_surface_check(dev_priv, sw_context->tfile, -					    *sid, &real_id); +/** + * vmw_rebind_contexts - Rebind all resources previously bound to + * referenced contexts. + * + * @sw_context: Pointer to the software context. + * + * Rebind context binding points that have been scrubbed because of eviction. + */ +static int vmw_rebind_contexts(struct vmw_sw_context *sw_context) +{ +	struct vmw_resource_val_node *val; +	int ret; +	list_for_each_entry(val, &sw_context->resource_list, head) { +		if (likely(!val->staged_bindings)) +			continue; + +		ret = vmw_context_rebind_all(val->res);  		if (unlikely(ret != 0)) { -			DRM_ERROR("Could ot find or use surface 0x%08x " -				  "address 0x%08lx\n", -				  (unsigned int) *sid, -				  (unsigned long) sid); +			if (ret != -ERESTARTSYS) +				DRM_ERROR("Failed to rebind context.\n");  			return ret;  		} - -		sw_context->last_sid = *sid; -		sw_context->sid_valid = true; -		*sid = real_id; -		sw_context->sid_translation = real_id; -	} else -		*sid = sw_context->sid_translation; +	}  	return 0;  } +/** + * vmw_cmd_cid_check - Check a command header for valid context information. + * + * @dev_priv: Pointer to a device private structure. + * @sw_context: Pointer to the software context. + * @header: A command header with an embedded user-space context handle. + * + * Convenience function: Call vmw_cmd_res_check with the user-space context + * handle embedded in @header. + */ +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; +		uint32_t cid; +	} *cmd; + +	cmd = container_of(header, struct vmw_cid_cmd, header); +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, +				 user_context_converter, &cmd->cid, NULL); +}  static int vmw_cmd_set_render_target_check(struct vmw_private *dev_priv,  					   struct vmw_sw_context *sw_context, @@ -111,15 +618,35 @@ static int vmw_cmd_set_render_target_check(struct vmw_private *dev_priv,  		SVGA3dCmdHeader header;  		SVGA3dCmdSetRenderTarget body;  	} *cmd; +	struct vmw_resource_val_node *ctx_node; +	struct vmw_resource_val_node *res_node;  	int ret; -	ret = vmw_cmd_cid_check(dev_priv, sw_context, header); +	cmd = container_of(header, struct vmw_sid_cmd, header); + +	ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, +				user_context_converter, &cmd->body.cid, +				&ctx_node);  	if (unlikely(ret != 0))  		return ret; -	cmd = container_of(header, struct vmw_sid_cmd, header); -	ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.target.sid); -	return ret; +	ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				user_surface_converter, +				&cmd->body.target.sid, &res_node); +	if (unlikely(ret != 0)) +		return ret; + +	if (dev_priv->has_mob) { +		struct vmw_ctx_bindinfo bi; + +		bi.ctx = ctx_node->res; +		bi.res = res_node ? res_node->res : NULL; +		bi.bt = vmw_ctx_binding_rt; +		bi.i1.rt_type = cmd->body.type; +		return vmw_context_binding_add(ctx_node->staged_bindings, &bi); +	} + +	return 0;  }  static int vmw_cmd_surface_copy_check(struct vmw_private *dev_priv, @@ -133,10 +660,14 @@ static int vmw_cmd_surface_copy_check(struct vmw_private *dev_priv,  	int ret;  	cmd = container_of(header, struct vmw_sid_cmd, header); -	ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.src.sid); +	ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				user_surface_converter, +				&cmd->body.src.sid, NULL);  	if (unlikely(ret != 0))  		return ret; -	return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.dest.sid); +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				 user_surface_converter, +				 &cmd->body.dest.sid, NULL);  }  static int vmw_cmd_stretch_blt_check(struct vmw_private *dev_priv, @@ -150,10 +681,14 @@ static int vmw_cmd_stretch_blt_check(struct vmw_private *dev_priv,  	int ret;  	cmd = container_of(header, struct vmw_sid_cmd, header); -	ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.src.sid); +	ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				user_surface_converter, +				&cmd->body.src.sid, NULL);  	if (unlikely(ret != 0))  		return ret; -	return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.dest.sid); +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				 user_surface_converter, +				 &cmd->body.dest.sid, NULL);  }  static int vmw_cmd_blt_surf_screen_check(struct vmw_private *dev_priv, @@ -166,7 +701,10 @@ static int vmw_cmd_blt_surf_screen_check(struct vmw_private *dev_priv,  	} *cmd;  	cmd = container_of(header, struct vmw_sid_cmd, header); -	return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.srcImage.sid); + +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				 user_surface_converter, +				 &cmd->body.srcImage.sid, NULL);  }  static int vmw_cmd_present_check(struct vmw_private *dev_priv, @@ -178,10 +716,213 @@ static int vmw_cmd_present_check(struct vmw_private *dev_priv,  		SVGA3dCmdPresent body;  	} *cmd; +  	cmd = container_of(header, struct vmw_sid_cmd, header); -	return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.sid); + +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				 user_surface_converter, &cmd->body.sid, +				 NULL); +} + +/** + * vmw_query_bo_switch_prepare - Prepare to switch pinned buffer for queries. + * + * @dev_priv: The device private structure. + * @new_query_bo: The new buffer holding query results. + * @sw_context: The software context used for this command submission. + * + * This function checks whether @new_query_bo is suitable for holding + * query results, and if another buffer currently is pinned for query + * results. If so, the function prepares the state of @sw_context for + * switching pinned buffers after successful submission of the current + * command batch. + */ +static int vmw_query_bo_switch_prepare(struct vmw_private *dev_priv, +				       struct ttm_buffer_object *new_query_bo, +				       struct vmw_sw_context *sw_context) +{ +	struct vmw_res_cache_entry *ctx_entry = +		&sw_context->res_cache[vmw_res_context]; +	int ret; + +	BUG_ON(!ctx_entry->valid); +	sw_context->last_query_ctx = ctx_entry->res; + +	if (unlikely(new_query_bo != sw_context->cur_query_bo)) { + +		if (unlikely(new_query_bo->num_pages > 4)) { +			DRM_ERROR("Query buffer too large.\n"); +			return -EINVAL; +		} + +		if (unlikely(sw_context->cur_query_bo != NULL)) { +			sw_context->needs_post_query_barrier = true; +			ret = vmw_bo_to_validate_list(sw_context, +						      sw_context->cur_query_bo, +						      dev_priv->has_mob, NULL); +			if (unlikely(ret != 0)) +				return ret; +		} +		sw_context->cur_query_bo = new_query_bo; + +		ret = vmw_bo_to_validate_list(sw_context, +					      dev_priv->dummy_query_bo, +					      dev_priv->has_mob, NULL); +		if (unlikely(ret != 0)) +			return ret; + +	} + +	return 0; +} + + +/** + * vmw_query_bo_switch_commit - Finalize switching pinned query buffer + * + * @dev_priv: The device private structure. + * @sw_context: The software context used for this command submission batch. + * + * This function will check if we're switching query buffers, and will then, + * issue a dummy occlusion query wait used as a query barrier. When the fence + * object following that query wait has signaled, we are sure that all + * preceding queries have finished, and the old query buffer can be unpinned. + * However, since both the new query buffer and the old one are fenced with + * that fence, we can do an asynchronus unpin now, and be sure that the + * old query buffer won't be moved until the fence has signaled. + * + * As mentioned above, both the new - and old query buffers need to be fenced + * using a sequence emitted *after* calling this function. + */ +static void vmw_query_bo_switch_commit(struct vmw_private *dev_priv, +				     struct vmw_sw_context *sw_context) +{ +	/* +	 * The validate list should still hold references to all +	 * contexts here. +	 */ + +	if (sw_context->needs_post_query_barrier) { +		struct vmw_res_cache_entry *ctx_entry = +			&sw_context->res_cache[vmw_res_context]; +		struct vmw_resource *ctx; +		int ret; + +		BUG_ON(!ctx_entry->valid); +		ctx = ctx_entry->res; + +		ret = vmw_fifo_emit_dummy_query(dev_priv, ctx->id); + +		if (unlikely(ret != 0)) +			DRM_ERROR("Out of fifo space for dummy query.\n"); +	} + +	if (dev_priv->pinned_bo != sw_context->cur_query_bo) { +		if (dev_priv->pinned_bo) { +			vmw_bo_pin(dev_priv->pinned_bo, false); +			ttm_bo_unref(&dev_priv->pinned_bo); +		} + +		if (!sw_context->needs_post_query_barrier) { +			vmw_bo_pin(sw_context->cur_query_bo, true); + +			/* +			 * We pin also the dummy_query_bo buffer so that we +			 * don't need to validate it when emitting +			 * dummy queries in context destroy paths. +			 */ + +			vmw_bo_pin(dev_priv->dummy_query_bo, true); +			dev_priv->dummy_query_bo_pinned = true; + +			BUG_ON(sw_context->last_query_ctx == NULL); +			dev_priv->query_cid = sw_context->last_query_ctx->id; +			dev_priv->query_cid_valid = true; +			dev_priv->pinned_bo = +				ttm_bo_reference(sw_context->cur_query_bo); +		} +	}  } +/** + * vmw_translate_mob_pointer - Prepare to translate a user-space buffer + * handle to a MOB id. + * + * @dev_priv: Pointer to a device private structure. + * @sw_context: The software context used for this command batch validation. + * @id: Pointer to the user-space handle to be translated. + * @vmw_bo_p: Points to a location that, on successful return will carry + * a reference-counted pointer to the DMA buffer identified by the + * user-space handle in @id. + * + * This function saves information needed to translate a user-space buffer + * handle to a MOB id. The translation does not take place immediately, but + * during a call to vmw_apply_relocations(). This function builds a relocation + * list and a list of buffers to validate. The former needs to be freed using + * either vmw_apply_relocations() or vmw_free_relocations(). The latter + * needs to be freed using vmw_clear_validations. + */ +static int vmw_translate_mob_ptr(struct vmw_private *dev_priv, +				 struct vmw_sw_context *sw_context, +				 SVGAMobId *id, +				 struct vmw_dma_buffer **vmw_bo_p) +{ +	struct vmw_dma_buffer *vmw_bo = NULL; +	struct ttm_buffer_object *bo; +	uint32_t handle = *id; +	struct vmw_relocation *reloc; +	int ret; + +	ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo); +	if (unlikely(ret != 0)) { +		DRM_ERROR("Could not find or use MOB buffer.\n"); +		return -EINVAL; +	} +	bo = &vmw_bo->base; + +	if (unlikely(sw_context->cur_reloc >= VMWGFX_MAX_RELOCATIONS)) { +		DRM_ERROR("Max number relocations per submission" +			  " exceeded\n"); +		ret = -EINVAL; +		goto out_no_reloc; +	} + +	reloc = &sw_context->relocs[sw_context->cur_reloc++]; +	reloc->mob_loc = id; +	reloc->location = NULL; + +	ret = vmw_bo_to_validate_list(sw_context, bo, true, &reloc->index); +	if (unlikely(ret != 0)) +		goto out_no_reloc; + +	*vmw_bo_p = vmw_bo; +	return 0; + +out_no_reloc: +	vmw_dmabuf_unreference(&vmw_bo); +	vmw_bo_p = NULL; +	return ret; +} + +/** + * vmw_translate_guest_pointer - Prepare to translate a user-space buffer + * handle to a valid SVGAGuestPtr + * + * @dev_priv: Pointer to a device private structure. + * @sw_context: The software context used for this command batch validation. + * @ptr: Pointer to the user-space handle to be translated. + * @vmw_bo_p: Points to a location that, on successful return will carry + * a reference-counted pointer to the DMA buffer identified by the + * user-space handle in @id. + * + * This function saves information needed to translate a user-space buffer + * handle to a valid SVGAGuestPtr. The translation does not take place + * immediately, but during a call to vmw_apply_relocations(). + * This function builds a relocation list and a list of buffers to validate. + * The former needs to be freed using either vmw_apply_relocations() or + * vmw_free_relocations(). The latter needs to be freed using + * vmw_clear_validations. + */  static int vmw_translate_guest_ptr(struct vmw_private *dev_priv,  				   struct vmw_sw_context *sw_context,  				   SVGAGuestPtr *ptr, @@ -191,11 +932,9 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv,  	struct ttm_buffer_object *bo;  	uint32_t handle = ptr->gmrId;  	struct vmw_relocation *reloc; -	uint32_t cur_validate_node; -	struct ttm_validate_buffer *val_buf;  	int ret; -	ret = vmw_user_dmabuf_lookup(sw_context->tfile, handle, &vmw_bo); +	ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo);  	if (unlikely(ret != 0)) {  		DRM_ERROR("Could not find or use GMR region.\n");  		return -EINVAL; @@ -212,22 +951,10 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv,  	reloc = &sw_context->relocs[sw_context->cur_reloc++];  	reloc->location = 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; +	ret = vmw_bo_to_validate_list(sw_context, bo, false, &reloc->index); +	if (unlikely(ret != 0))  		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; -	}  	*vmw_bo_p = vmw_bo;  	return 0; @@ -237,6 +964,113 @@ out_no_reloc:  	return ret;  } +/** + * vmw_cmd_begin_gb_query - validate a  SVGA_3D_CMD_BEGIN_GB_QUERY command. + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context used for this command submission. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_begin_gb_query(struct vmw_private *dev_priv, +				  struct vmw_sw_context *sw_context, +				  SVGA3dCmdHeader *header) +{ +	struct vmw_begin_gb_query_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdBeginGBQuery q; +	} *cmd; + +	cmd = container_of(header, struct vmw_begin_gb_query_cmd, +			   header); + +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, +				 user_context_converter, &cmd->q.cid, +				 NULL); +} + +/** + * vmw_cmd_begin_query - validate a  SVGA_3D_CMD_BEGIN_QUERY command. + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context used for this command submission. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_begin_query(struct vmw_private *dev_priv, +			       struct vmw_sw_context *sw_context, +			       SVGA3dCmdHeader *header) +{ +	struct vmw_begin_query_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdBeginQuery q; +	} *cmd; + +	cmd = container_of(header, struct vmw_begin_query_cmd, +			   header); + +	if (unlikely(dev_priv->has_mob)) { +		struct { +			SVGA3dCmdHeader header; +			SVGA3dCmdBeginGBQuery q; +		} gb_cmd; + +		BUG_ON(sizeof(gb_cmd) != sizeof(*cmd)); + +		gb_cmd.header.id = SVGA_3D_CMD_BEGIN_GB_QUERY; +		gb_cmd.header.size = cmd->header.size; +		gb_cmd.q.cid = cmd->q.cid; +		gb_cmd.q.type = cmd->q.type; + +		memcpy(cmd, &gb_cmd, sizeof(*cmd)); +		return vmw_cmd_begin_gb_query(dev_priv, sw_context, header); +	} + +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, +				 user_context_converter, &cmd->q.cid, +				 NULL); +} + +/** + * vmw_cmd_end_gb_query - validate a  SVGA_3D_CMD_END_GB_QUERY command. + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context used for this command submission. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_end_gb_query(struct vmw_private *dev_priv, +				struct vmw_sw_context *sw_context, +				SVGA3dCmdHeader *header) +{ +	struct vmw_dma_buffer *vmw_bo; +	struct vmw_query_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdEndGBQuery q; +	} *cmd; +	int ret; + +	cmd = container_of(header, struct vmw_query_cmd, header); +	ret = vmw_cmd_cid_check(dev_priv, sw_context, header); +	if (unlikely(ret != 0)) +		return ret; + +	ret = vmw_translate_mob_ptr(dev_priv, sw_context, +				    &cmd->q.mobid, +				    &vmw_bo); +	if (unlikely(ret != 0)) +		return ret; + +	ret = vmw_query_bo_switch_prepare(dev_priv, &vmw_bo->base, sw_context); + +	vmw_dmabuf_unreference(&vmw_bo); +	return ret; +} + +/** + * vmw_cmd_end_query - validate a  SVGA_3D_CMD_END_QUERY command. + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context used for this command submission. + * @header: Pointer to the command header in the command stream. + */  static int vmw_cmd_end_query(struct vmw_private *dev_priv,  			     struct vmw_sw_context *sw_context,  			     SVGA3dCmdHeader *header) @@ -249,6 +1083,25 @@ static int vmw_cmd_end_query(struct vmw_private *dev_priv,  	int ret;  	cmd = container_of(header, struct vmw_query_cmd, header); +	if (dev_priv->has_mob) { +		struct { +			SVGA3dCmdHeader header; +			SVGA3dCmdEndGBQuery q; +		} gb_cmd; + +		BUG_ON(sizeof(gb_cmd) != sizeof(*cmd)); + +		gb_cmd.header.id = SVGA_3D_CMD_END_GB_QUERY; +		gb_cmd.header.size = cmd->header.size; +		gb_cmd.q.cid = cmd->q.cid; +		gb_cmd.q.type = cmd->q.type; +		gb_cmd.q.mobid = cmd->q.guestResult.gmrId; +		gb_cmd.q.offset = cmd->q.guestResult.offset; + +		memcpy(cmd, &gb_cmd, sizeof(*cmd)); +		return vmw_cmd_end_gb_query(dev_priv, sw_context, header); +	} +  	ret = vmw_cmd_cid_check(dev_priv, sw_context, header);  	if (unlikely(ret != 0))  		return ret; @@ -259,10 +1112,52 @@ static int vmw_cmd_end_query(struct vmw_private *dev_priv,  	if (unlikely(ret != 0))  		return ret; +	ret = vmw_query_bo_switch_prepare(dev_priv, &vmw_bo->base, sw_context); + +	vmw_dmabuf_unreference(&vmw_bo); +	return ret; +} + +/** + * vmw_cmd_wait_gb_query - validate a  SVGA_3D_CMD_WAIT_GB_QUERY command. + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context used for this command submission. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_wait_gb_query(struct vmw_private *dev_priv, +				 struct vmw_sw_context *sw_context, +				 SVGA3dCmdHeader *header) +{ +	struct vmw_dma_buffer *vmw_bo; +	struct vmw_query_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdWaitForGBQuery q; +	} *cmd; +	int ret; + +	cmd = container_of(header, struct vmw_query_cmd, header); +	ret = vmw_cmd_cid_check(dev_priv, sw_context, header); +	if (unlikely(ret != 0)) +		return ret; + +	ret = vmw_translate_mob_ptr(dev_priv, sw_context, +				    &cmd->q.mobid, +				    &vmw_bo); +	if (unlikely(ret != 0)) +		return ret; +  	vmw_dmabuf_unreference(&vmw_bo);  	return 0;  } +/** + * vmw_cmd_wait_query - validate a  SVGA_3D_CMD_WAIT_QUERY command. + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context used for this command submission. + * @header: Pointer to the command header in the command stream. + */  static int vmw_cmd_wait_query(struct vmw_private *dev_priv,  			      struct vmw_sw_context *sw_context,  			      SVGA3dCmdHeader *header) @@ -275,6 +1170,25 @@ static int vmw_cmd_wait_query(struct vmw_private *dev_priv,  	int ret;  	cmd = container_of(header, struct vmw_query_cmd, header); +	if (dev_priv->has_mob) { +		struct { +			SVGA3dCmdHeader header; +			SVGA3dCmdWaitForGBQuery q; +		} gb_cmd; + +		BUG_ON(sizeof(gb_cmd) != sizeof(*cmd)); + +		gb_cmd.header.id = SVGA_3D_CMD_WAIT_FOR_GB_QUERY; +		gb_cmd.header.size = cmd->header.size; +		gb_cmd.q.cid = cmd->q.cid; +		gb_cmd.q.type = cmd->q.type; +		gb_cmd.q.mobid = cmd->q.guestResult.gmrId; +		gb_cmd.q.offset = cmd->q.guestResult.offset; + +		memcpy(cmd, &gb_cmd, sizeof(*cmd)); +		return vmw_cmd_wait_gb_query(dev_priv, sw_context, header); +	} +  	ret = vmw_cmd_cid_check(dev_priv, sw_context, header);  	if (unlikely(ret != 0))  		return ret; @@ -289,48 +1203,62 @@ static int vmw_cmd_wait_query(struct vmw_private *dev_priv,  	return 0;  } -  static int vmw_cmd_dma(struct vmw_private *dev_priv,  		       struct vmw_sw_context *sw_context,  		       SVGA3dCmdHeader *header)  {  	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;  	int ret; +	SVGA3dCmdSurfaceDMASuffix *suffix; +	uint32_t bo_size;  	cmd = container_of(header, struct vmw_dma_cmd, header); +	suffix = (SVGA3dCmdSurfaceDMASuffix *)((unsigned long) &cmd->dma + +					       header->size - sizeof(*suffix)); + +	/* Make sure device and verifier stays in sync. */ +	if (unlikely(suffix->suffixSize != sizeof(*suffix))) { +		DRM_ERROR("Invalid DMA suffix size.\n"); +		return -EINVAL; +	} +  	ret = vmw_translate_guest_ptr(dev_priv, sw_context,  				      &cmd->dma.guest.ptr,  				      &vmw_bo);  	if (unlikely(ret != 0))  		return ret; -	bo = &vmw_bo->base; -	ret = vmw_user_surface_lookup_handle(dev_priv, sw_context->tfile, -					     cmd->dma.host.sid, &srf); -	if (ret) { -		DRM_ERROR("could not find surface\n"); -		goto out_no_reloc; +	/* Make sure DMA doesn't cross BO boundaries. */ +	bo_size = vmw_bo->base.num_pages * PAGE_SIZE; +	if (unlikely(cmd->dma.guest.ptr.offset > bo_size)) { +		DRM_ERROR("Invalid DMA offset.\n"); +		return -EINVAL;  	} -	/** -	 * Patch command stream with device SID. -	 */ +	bo_size -= cmd->dma.guest.ptr.offset; +	if (unlikely(suffix->maximumOffset > bo_size)) +		suffix->maximumOffset = bo_size; -	cmd->dma.host.sid = srf->res.id; -	vmw_kms_cursor_snoop(srf, sw_context->tfile, bo, header); -	/** -	 * FIXME: May deadlock here when called from the -	 * command parsing code. -	 */ -	vmw_surface_unreference(&srf); +	ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				user_surface_converter, &cmd->dma.host.sid, +				NULL); +	if (unlikely(ret != 0)) { +		if (unlikely(ret != -ERESTARTSYS)) +			DRM_ERROR("could not find surface for DMA.\n"); +		goto out_no_surface; +	} -out_no_reloc: +	srf = vmw_res_to_srf(sw_context->res_cache[vmw_res_surface].res); + +	vmw_kms_cursor_snoop(srf, sw_context->fp->tfile, &vmw_bo->base, +			     header); + +out_no_surface:  	vmw_dmabuf_unreference(&vmw_bo);  	return ret;  } @@ -363,8 +1291,9 @@ static int vmw_cmd_draw(struct vmw_private *dev_priv,  	}  	for (i = 0; i < cmd->body.numVertexDecls; ++i, ++decl) { -		ret = vmw_cmd_sid_check(dev_priv, sw_context, -					&decl->array.surfaceId); +		ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +					user_surface_converter, +					&decl->array.surfaceId, NULL);  		if (unlikely(ret != 0))  			return ret;  	} @@ -378,8 +1307,9 @@ static int vmw_cmd_draw(struct vmw_private *dev_priv,  	range = (SVGA3dPrimitiveRange *) decl;  	for (i = 0; i < cmd->body.numRanges; ++i, ++range) { -		ret = vmw_cmd_sid_check(dev_priv, sw_context, -					&range->indexArray.surfaceId); +		ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +					user_surface_converter, +					&range->indexArray.surfaceId, NULL);  		if (unlikely(ret != 0))  			return ret;  	} @@ -394,15 +1324,22 @@ static int vmw_cmd_tex_state(struct vmw_private *dev_priv,  	struct vmw_tex_state_cmd {  		SVGA3dCmdHeader header;  		SVGA3dCmdSetTextureState state; -	}; +	} *cmd;  	SVGA3dTextureState *last_state = (SVGA3dTextureState *)  	  ((unsigned long) header + header->size + sizeof(header));  	SVGA3dTextureState *cur_state = (SVGA3dTextureState *)  		((unsigned long) header + sizeof(struct vmw_tex_state_cmd)); +	struct vmw_resource_val_node *ctx_node; +	struct vmw_resource_val_node *res_node;  	int ret; -	ret = vmw_cmd_cid_check(dev_priv, sw_context, header); +	cmd = container_of(header, struct vmw_tex_state_cmd, +			   header); + +	ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, +				user_context_converter, &cmd->state.cid, +				&ctx_node);  	if (unlikely(ret != 0))  		return ret; @@ -410,56 +1347,683 @@ static int vmw_cmd_tex_state(struct vmw_private *dev_priv,  		if (likely(cur_state->name != SVGA3D_TS_BIND_TEXTURE))  			continue; -		ret = vmw_cmd_sid_check(dev_priv, sw_context, -					&cur_state->value); +		ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +					user_surface_converter, +					&cur_state->value, &res_node); +		if (unlikely(ret != 0)) +			return ret; + +		if (dev_priv->has_mob) { +			struct vmw_ctx_bindinfo bi; + +			bi.ctx = ctx_node->res; +			bi.res = res_node ? res_node->res : NULL; +			bi.bt = vmw_ctx_binding_tex; +			bi.i1.texture_stage = cur_state->stage; +			vmw_context_binding_add(ctx_node->staged_bindings, +						&bi); +		} +	} + +	return 0; +} + +static int vmw_cmd_check_define_gmrfb(struct vmw_private *dev_priv, +				      struct vmw_sw_context *sw_context, +				      void *buf) +{ +	struct vmw_dma_buffer *vmw_bo; +	int ret; + +	struct { +		uint32_t header; +		SVGAFifoCmdDefineGMRFB body; +	} *cmd = buf; + +	ret = vmw_translate_guest_ptr(dev_priv, sw_context, +				      &cmd->body.ptr, +				      &vmw_bo); +	if (unlikely(ret != 0)) +		return ret; + +	vmw_dmabuf_unreference(&vmw_bo); + +	return ret; +} + +/** + * vmw_cmd_switch_backup - Utility function to handle backup buffer switching + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context being used for this batch. + * @res_type: The resource type. + * @converter: Information about user-space binding for this resource type. + * @res_id: Pointer to the user-space resource handle in the command stream. + * @buf_id: Pointer to the user-space backup buffer handle in the command + * stream. + * @backup_offset: Offset of backup into MOB. + * + * This function prepares for registering a switch of backup buffers + * in the resource metadata just prior to unreserving. + */ +static int vmw_cmd_switch_backup(struct vmw_private *dev_priv, +				 struct vmw_sw_context *sw_context, +				 enum vmw_res_type res_type, +				 const struct vmw_user_resource_conv +				 *converter, +				 uint32_t *res_id, +				 uint32_t *buf_id, +				 unsigned long backup_offset) +{ +	int ret; +	struct vmw_dma_buffer *dma_buf; +	struct vmw_resource_val_node *val_node; + +	ret = vmw_cmd_res_check(dev_priv, sw_context, res_type, +				converter, res_id, &val_node); +	if (unlikely(ret != 0)) +		return ret; + +	ret = vmw_translate_mob_ptr(dev_priv, sw_context, buf_id, &dma_buf); +	if (unlikely(ret != 0)) +		return ret; + +	if (val_node->first_usage) +		val_node->no_buffer_needed = true; + +	vmw_dmabuf_unreference(&val_node->new_backup); +	val_node->new_backup = dma_buf; +	val_node->new_backup_offset = backup_offset; + +	return 0; +} + +/** + * vmw_cmd_bind_gb_surface - Validate an SVGA_3D_CMD_BIND_GB_SURFACE + * command + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context being used for this batch. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_bind_gb_surface(struct vmw_private *dev_priv, +				   struct vmw_sw_context *sw_context, +				   SVGA3dCmdHeader *header) +{ +	struct vmw_bind_gb_surface_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdBindGBSurface body; +	} *cmd; + +	cmd = container_of(header, struct vmw_bind_gb_surface_cmd, header); + +	return vmw_cmd_switch_backup(dev_priv, sw_context, vmw_res_surface, +				     user_surface_converter, +				     &cmd->body.sid, &cmd->body.mobid, +				     0); +} + +/** + * vmw_cmd_update_gb_image - Validate an SVGA_3D_CMD_UPDATE_GB_IMAGE + * command + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context being used for this batch. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_update_gb_image(struct vmw_private *dev_priv, +				   struct vmw_sw_context *sw_context, +				   SVGA3dCmdHeader *header) +{ +	struct vmw_gb_surface_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdUpdateGBImage body; +	} *cmd; + +	cmd = container_of(header, struct vmw_gb_surface_cmd, header); + +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				 user_surface_converter, +				 &cmd->body.image.sid, NULL); +} + +/** + * vmw_cmd_update_gb_surface - Validate an SVGA_3D_CMD_UPDATE_GB_SURFACE + * command + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context being used for this batch. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_update_gb_surface(struct vmw_private *dev_priv, +				     struct vmw_sw_context *sw_context, +				     SVGA3dCmdHeader *header) +{ +	struct vmw_gb_surface_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdUpdateGBSurface body; +	} *cmd; + +	cmd = container_of(header, struct vmw_gb_surface_cmd, header); + +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				 user_surface_converter, +				 &cmd->body.sid, NULL); +} + +/** + * vmw_cmd_readback_gb_image - Validate an SVGA_3D_CMD_READBACK_GB_IMAGE + * command + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context being used for this batch. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_readback_gb_image(struct vmw_private *dev_priv, +				     struct vmw_sw_context *sw_context, +				     SVGA3dCmdHeader *header) +{ +	struct vmw_gb_surface_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdReadbackGBImage body; +	} *cmd; + +	cmd = container_of(header, struct vmw_gb_surface_cmd, header); + +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				 user_surface_converter, +				 &cmd->body.image.sid, NULL); +} + +/** + * vmw_cmd_readback_gb_surface - Validate an SVGA_3D_CMD_READBACK_GB_SURFACE + * command + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context being used for this batch. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_readback_gb_surface(struct vmw_private *dev_priv, +				       struct vmw_sw_context *sw_context, +				       SVGA3dCmdHeader *header) +{ +	struct vmw_gb_surface_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdReadbackGBSurface body; +	} *cmd; + +	cmd = container_of(header, struct vmw_gb_surface_cmd, header); + +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				 user_surface_converter, +				 &cmd->body.sid, NULL); +} + +/** + * vmw_cmd_invalidate_gb_image - Validate an SVGA_3D_CMD_INVALIDATE_GB_IMAGE + * command + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context being used for this batch. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_invalidate_gb_image(struct vmw_private *dev_priv, +				       struct vmw_sw_context *sw_context, +				       SVGA3dCmdHeader *header) +{ +	struct vmw_gb_surface_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdInvalidateGBImage body; +	} *cmd; + +	cmd = container_of(header, struct vmw_gb_surface_cmd, header); + +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				 user_surface_converter, +				 &cmd->body.image.sid, NULL); +} + +/** + * vmw_cmd_invalidate_gb_surface - Validate an + * SVGA_3D_CMD_INVALIDATE_GB_SURFACE command + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context being used for this batch. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_invalidate_gb_surface(struct vmw_private *dev_priv, +					 struct vmw_sw_context *sw_context, +					 SVGA3dCmdHeader *header) +{ +	struct vmw_gb_surface_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdInvalidateGBSurface body; +	} *cmd; + +	cmd = container_of(header, struct vmw_gb_surface_cmd, header); + +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				 user_surface_converter, +				 &cmd->body.sid, NULL); +} + + +/** + * vmw_cmd_shader_define - Validate an SVGA_3D_CMD_SHADER_DEFINE + * command + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context being used for this batch. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_shader_define(struct vmw_private *dev_priv, +				 struct vmw_sw_context *sw_context, +				 SVGA3dCmdHeader *header) +{ +	struct vmw_shader_define_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdDefineShader body; +	} *cmd; +	int ret; +	size_t size; + +	cmd = container_of(header, struct vmw_shader_define_cmd, +			   header); + +	ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, +				user_context_converter, &cmd->body.cid, +				NULL); +	if (unlikely(ret != 0)) +		return ret; + +	if (unlikely(!dev_priv->has_mob)) +		return 0; + +	size = cmd->header.size - sizeof(cmd->body); +	ret = vmw_compat_shader_add(sw_context->fp->shman, +				    cmd->body.shid, cmd + 1, +				    cmd->body.type, size, +				    sw_context->fp->tfile, +				    &sw_context->staged_shaders); +	if (unlikely(ret != 0)) +		return ret; + +	return vmw_resource_relocation_add(&sw_context->res_relocations, +					   NULL, &cmd->header.id - +					   sw_context->buf_start); + +	return 0; +} + +/** + * vmw_cmd_shader_destroy - Validate an SVGA_3D_CMD_SHADER_DESTROY + * command + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context being used for this batch. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_shader_destroy(struct vmw_private *dev_priv, +				  struct vmw_sw_context *sw_context, +				  SVGA3dCmdHeader *header) +{ +	struct vmw_shader_destroy_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdDestroyShader body; +	} *cmd; +	int ret; + +	cmd = container_of(header, struct vmw_shader_destroy_cmd, +			   header); + +	ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, +				user_context_converter, &cmd->body.cid, +				NULL); +	if (unlikely(ret != 0)) +		return ret; + +	if (unlikely(!dev_priv->has_mob)) +		return 0; + +	ret = vmw_compat_shader_remove(sw_context->fp->shman, +				       cmd->body.shid, +				       cmd->body.type, +				       &sw_context->staged_shaders); +	if (unlikely(ret != 0)) +		return ret; + +	return vmw_resource_relocation_add(&sw_context->res_relocations, +					   NULL, &cmd->header.id - +					   sw_context->buf_start); + +	return 0; +} + +/** + * vmw_cmd_set_shader - Validate an SVGA_3D_CMD_SET_SHADER + * command + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context being used for this batch. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_set_shader(struct vmw_private *dev_priv, +			      struct vmw_sw_context *sw_context, +			      SVGA3dCmdHeader *header) +{ +	struct vmw_set_shader_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdSetShader body; +	} *cmd; +	struct vmw_resource_val_node *ctx_node; +	int ret; + +	cmd = container_of(header, struct vmw_set_shader_cmd, +			   header); + +	ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, +				user_context_converter, &cmd->body.cid, +				&ctx_node); +	if (unlikely(ret != 0)) +		return ret; + +	if (dev_priv->has_mob) { +		struct vmw_ctx_bindinfo bi; +		struct vmw_resource_val_node *res_node; +		u32 shid = cmd->body.shid; + +		if (shid != SVGA3D_INVALID_ID) +			(void) vmw_compat_shader_lookup(sw_context->fp->shman, +							cmd->body.type, +							&shid); + +		ret = vmw_cmd_compat_res_check(dev_priv, sw_context, +					       vmw_res_shader, +					       user_shader_converter, +					       shid, +					       &cmd->body.shid, &res_node);  		if (unlikely(ret != 0))  			return ret; + +		bi.ctx = ctx_node->res; +		bi.res = res_node ? res_node->res : NULL; +		bi.bt = vmw_ctx_binding_shader; +		bi.i1.shader_type = cmd->body.type; +		return vmw_context_binding_add(ctx_node->staged_bindings, &bi);  	}  	return 0;  } +/** + * vmw_cmd_set_shader_const - Validate an SVGA_3D_CMD_SET_SHADER_CONST + * command + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context being used for this batch. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_set_shader_const(struct vmw_private *dev_priv, +				    struct vmw_sw_context *sw_context, +				    SVGA3dCmdHeader *header) +{ +	struct vmw_set_shader_const_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdSetShaderConst body; +	} *cmd; +	int ret; + +	cmd = container_of(header, struct vmw_set_shader_const_cmd, +			   header); + +	ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, +				user_context_converter, &cmd->body.cid, +				NULL); +	if (unlikely(ret != 0)) +		return ret; + +	if (dev_priv->has_mob) +		header->id = SVGA_3D_CMD_SET_GB_SHADERCONSTS_INLINE; + +	return 0; +} + +/** + * vmw_cmd_bind_gb_shader - Validate an SVGA_3D_CMD_BIND_GB_SHADER + * command + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context being used for this batch. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_bind_gb_shader(struct vmw_private *dev_priv, +				  struct vmw_sw_context *sw_context, +				  SVGA3dCmdHeader *header) +{ +	struct vmw_bind_gb_shader_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdBindGBShader body; +	} *cmd; + +	cmd = container_of(header, struct vmw_bind_gb_shader_cmd, +			   header); -typedef int (*vmw_cmd_func) (struct vmw_private *, -			     struct vmw_sw_context *, -			     SVGA3dCmdHeader *); +	return vmw_cmd_switch_backup(dev_priv, sw_context, vmw_res_shader, +				     user_shader_converter, +				     &cmd->body.shid, &cmd->body.mobid, +				     cmd->body.offsetInBytes); +} -#define VMW_CMD_DEF(cmd, func) \ -	[cmd - SVGA_3D_CMD_BASE] = func +static int vmw_cmd_check_not_3d(struct vmw_private *dev_priv, +				struct vmw_sw_context *sw_context, +				void *buf, uint32_t *size) +{ +	uint32_t size_remaining = *size; +	uint32_t cmd_id; + +	cmd_id = le32_to_cpu(((uint32_t *)buf)[0]); +	switch (cmd_id) { +	case SVGA_CMD_UPDATE: +		*size = sizeof(uint32_t) + sizeof(SVGAFifoCmdUpdate); +		break; +	case SVGA_CMD_DEFINE_GMRFB: +		*size = sizeof(uint32_t) + sizeof(SVGAFifoCmdDefineGMRFB); +		break; +	case SVGA_CMD_BLIT_GMRFB_TO_SCREEN: +		*size = sizeof(uint32_t) + sizeof(SVGAFifoCmdBlitGMRFBToScreen); +		break; +	case SVGA_CMD_BLIT_SCREEN_TO_GMRFB: +		*size = sizeof(uint32_t) + sizeof(SVGAFifoCmdBlitGMRFBToScreen); +		break; +	default: +		DRM_ERROR("Unsupported SVGA command: %u.\n", cmd_id); +		return -EINVAL; +	} + +	if (*size > size_remaining) { +		DRM_ERROR("Invalid SVGA command (size mismatch):" +			  " %u.\n", cmd_id); +		return -EINVAL; +	} + +	if (unlikely(!sw_context->kernel)) { +		DRM_ERROR("Kernel only SVGA command: %u.\n", cmd_id); +		return -EPERM; +	} + +	if (cmd_id == SVGA_CMD_DEFINE_GMRFB) +		return vmw_cmd_check_define_gmrfb(dev_priv, sw_context, buf); + +	return 0; +} -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), +static const struct vmw_cmd_entry vmw_cmd_entries[SVGA_3D_CMD_MAX] = { +	VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_DEFINE, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_DESTROY, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_COPY, &vmw_cmd_surface_copy_check, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_STRETCHBLT, &vmw_cmd_stretch_blt_check, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_DMA, &vmw_cmd_dma, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_CONTEXT_DEFINE, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_CONTEXT_DESTROY, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SETTRANSFORM, &vmw_cmd_cid_check, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SETZRANGE, &vmw_cmd_cid_check, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SETRENDERSTATE, &vmw_cmd_cid_check, +		    true, false, false),  	VMW_CMD_DEF(SVGA_3D_CMD_SETRENDERTARGET, -		    &vmw_cmd_set_render_target_check), -	VMW_CMD_DEF(SVGA_3D_CMD_SETTEXTURESTATE, &vmw_cmd_tex_state), -	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_draw), -	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_end_query), -	VMW_CMD_DEF(SVGA_3D_CMD_WAIT_FOR_QUERY, &vmw_cmd_wait_query), -	VMW_CMD_DEF(SVGA_3D_CMD_PRESENT_READBACK, &vmw_cmd_ok), +		    &vmw_cmd_set_render_target_check, true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SETTEXTURESTATE, &vmw_cmd_tex_state, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SETMATERIAL, &vmw_cmd_cid_check, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SETLIGHTDATA, &vmw_cmd_cid_check, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SETLIGHTENABLED, &vmw_cmd_cid_check, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SETVIEWPORT, &vmw_cmd_cid_check, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SETCLIPPLANE, &vmw_cmd_cid_check, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_CLEAR, &vmw_cmd_cid_check, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_PRESENT, &vmw_cmd_present_check, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SHADER_DEFINE, &vmw_cmd_shader_define, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SHADER_DESTROY, &vmw_cmd_shader_destroy, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SET_SHADER, &vmw_cmd_set_shader, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SET_SHADER_CONST, &vmw_cmd_set_shader_const, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_DRAW_PRIMITIVES, &vmw_cmd_draw, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SETSCISSORRECT, &vmw_cmd_cid_check, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_BEGIN_QUERY, &vmw_cmd_begin_query, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_END_QUERY, &vmw_cmd_end_query, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_WAIT_FOR_QUERY, &vmw_cmd_wait_query, +		    true, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_PRESENT_READBACK, &vmw_cmd_ok, +		    true, false, false),  	VMW_CMD_DEF(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN, -		    &vmw_cmd_blt_surf_screen_check) +		    &vmw_cmd_blt_surf_screen_check, false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_DEFINE_V2, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_GENERATE_MIPMAPS, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_ACTIVATE_SURFACE, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_DEACTIVATE_SURFACE, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SCREEN_DMA, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SET_UNITY_SURFACE_COOKIE, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_OPEN_CONTEXT_SURFACE, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_LOGICOPS_BITBLT, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_LOGICOPS_TRANSBLT, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_LOGICOPS_STRETCHBLT, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_LOGICOPS_COLORFILL, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_LOGICOPS_ALPHABLEND, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_LOGICOPS_CLEARTYPEBLEND, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_SET_OTABLE_BASE, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_READBACK_OTABLE, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_DEFINE_GB_MOB, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_DESTROY_GB_MOB, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_REDEFINE_GB_MOB, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_UPDATE_GB_MOB_MAPPING, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_DEFINE_GB_SURFACE, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_DESTROY_GB_SURFACE, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_BIND_GB_SURFACE, &vmw_cmd_bind_gb_surface, +		    true, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_COND_BIND_GB_SURFACE, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_UPDATE_GB_IMAGE, &vmw_cmd_update_gb_image, +		    true, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_UPDATE_GB_SURFACE, +		    &vmw_cmd_update_gb_surface, true, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_READBACK_GB_IMAGE, +		    &vmw_cmd_readback_gb_image, true, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_READBACK_GB_SURFACE, +		    &vmw_cmd_readback_gb_surface, true, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_INVALIDATE_GB_IMAGE, +		    &vmw_cmd_invalidate_gb_image, true, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_INVALIDATE_GB_SURFACE, +		    &vmw_cmd_invalidate_gb_surface, true, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_DEFINE_GB_CONTEXT, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_DESTROY_GB_CONTEXT, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_BIND_GB_CONTEXT, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_READBACK_GB_CONTEXT, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_INVALIDATE_GB_CONTEXT, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_DEFINE_GB_SHADER, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_BIND_GB_SHADER, &vmw_cmd_bind_gb_shader, +		    true, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_DESTROY_GB_SHADER, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_SET_OTABLE_BASE64, &vmw_cmd_invalid, +		    false, false, false), +	VMW_CMD_DEF(SVGA_3D_CMD_BEGIN_GB_QUERY, &vmw_cmd_begin_gb_query, +		    true, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_END_GB_QUERY, &vmw_cmd_end_gb_query, +		    true, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_WAIT_FOR_GB_QUERY, &vmw_cmd_wait_gb_query, +		    true, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_NOP, &vmw_cmd_ok, +		    true, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_ENABLE_GART, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_DISABLE_GART, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_MAP_MOB_INTO_GART, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_UNMAP_GART_RANGE, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_DEFINE_GB_SCREENTARGET, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_DESTROY_GB_SCREENTARGET, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_BIND_GB_SCREENTARGET, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_UPDATE_GB_SCREENTARGET, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_READBACK_GB_IMAGE_PARTIAL, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_INVALIDATE_GB_IMAGE_PARTIAL, &vmw_cmd_invalid, +		    false, false, true), +	VMW_CMD_DEF(SVGA_3D_CMD_SET_GB_SHADERCONSTS_INLINE, &vmw_cmd_cid_check, +		    true, false, true)  };  static int vmw_cmd_check(struct vmw_private *dev_priv, @@ -470,41 +2034,71 @@ static int vmw_cmd_check(struct vmw_private *dev_priv,  	uint32_t size_remaining = *size;  	SVGA3dCmdHeader *header = (SVGA3dCmdHeader *) buf;  	int ret; +	const struct vmw_cmd_entry *entry; +	bool gb = dev_priv->capabilities & SVGA_CAP_GBOBJECTS; + +	cmd_id = le32_to_cpu(((uint32_t *)buf)[0]); +	/* Handle any none 3D commands */ +	if (unlikely(cmd_id < SVGA_CMD_MAX)) +		return vmw_cmd_check_not_3d(dev_priv, sw_context, buf, size); -	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(*size > size_remaining)) -		goto out_err; +		goto out_invalid;  	if (unlikely(cmd_id >= SVGA_3D_CMD_MAX - SVGA_3D_CMD_BASE)) -		goto out_err; +		goto out_invalid; + +	entry = &vmw_cmd_entries[cmd_id]; +	if (unlikely(!entry->func)) +		goto out_invalid; -	ret = vmw_cmd_funcs[cmd_id](dev_priv, sw_context, header); +	if (unlikely(!entry->user_allow && !sw_context->kernel)) +		goto out_privileged; + +	if (unlikely(entry->gb_disable && gb)) +		goto out_old; + +	if (unlikely(entry->gb_enable && !gb)) +		goto out_new; + +	ret = entry->func(dev_priv, sw_context, header);  	if (unlikely(ret != 0)) -		goto out_err; +		goto out_invalid;  	return 0; -out_err: -	DRM_ERROR("Illegal / Invalid SVGA3D command: %d\n", +out_invalid: +	DRM_ERROR("Invalid SVGA3D command: %d\n", +		  cmd_id + SVGA_3D_CMD_BASE); +	return -EINVAL; +out_privileged: +	DRM_ERROR("Privileged SVGA3D command: %d\n", +		  cmd_id + SVGA_3D_CMD_BASE); +	return -EPERM; +out_old: +	DRM_ERROR("Deprecated (disallowed) SVGA3D command: %d\n", +		  cmd_id + SVGA_3D_CMD_BASE); +	return -EINVAL; +out_new: +	DRM_ERROR("SVGA3D command: %d not supported by virtual hardware.\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) +			     void *buf, +			     uint32_t size)  {  	int32_t cur_size = size;  	int ret; +	sw_context->buf_start = buf; +  	while (cur_size > 0) {  		size = cur_size;  		ret = vmw_cmd_check(dev_priv, sw_context, buf, &size); @@ -536,36 +2130,89 @@ static void vmw_apply_relocations(struct vmw_sw_context *sw_context)  	for (i = 0; i < sw_context->cur_reloc; ++i) {  		reloc = &sw_context->relocs[i]; -		validate = &sw_context->val_bufs[reloc->index]; +		validate = &sw_context->val_bufs[reloc->index].base;  		bo = validate->bo; -		if (bo->mem.mem_type == TTM_PL_VRAM) { +		switch (bo->mem.mem_type) { +		case TTM_PL_VRAM:  			reloc->location->offset += bo->offset;  			reloc->location->gmrId = SVGA_GMR_FRAMEBUFFER; -		} else +			break; +		case VMW_PL_GMR:  			reloc->location->gmrId = bo->mem.start; +			break; +		case VMW_PL_MOB: +			*reloc->mob_loc = bo->mem.start; +			break; +		default: +			BUG(); +		}  	}  	vmw_free_relocations(sw_context);  } +/** + * vmw_resource_list_unrefererence - Free up a resource list and unreference + * all resources referenced by it. + * + * @list: The resource list. + */ +static void vmw_resource_list_unreference(struct list_head *list) +{ +	struct vmw_resource_val_node *val, *val_next; + +	/* +	 * Drop references to resources held during command submission. +	 */ + +	list_for_each_entry_safe(val, val_next, list, head) { +		list_del_init(&val->head); +		vmw_resource_unreference(&val->res); +		if (unlikely(val->staged_bindings)) +			kfree(val->staged_bindings); +		kfree(val); +	} +} +  static void vmw_clear_validations(struct vmw_sw_context *sw_context)  { -	struct ttm_validate_buffer *entry, *next; +	struct vmw_validate_buffer *entry, *next; +	struct vmw_resource_val_node *val; +	/* +	 * Drop references to DMA buffers held during command submission. +	 */  	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); +				 base.head) { +		list_del(&entry->base.head); +		ttm_bo_unref(&entry->base.bo); +		(void) drm_ht_remove_item(&sw_context->res_ht, &entry->hash);  		sw_context->cur_val_buf--;  	}  	BUG_ON(sw_context->cur_val_buf != 0); + +	list_for_each_entry(val, &sw_context->resource_list, head) +		(void) drm_ht_remove_item(&sw_context->res_ht, &val->hash);  }  static int vmw_validate_single_buffer(struct vmw_private *dev_priv, -				      struct ttm_buffer_object *bo) +				      struct ttm_buffer_object *bo, +				      bool validate_as_mob)  {  	int ret; + +	/* +	 * Don't validate pinned buffers. +	 */ + +	if (bo == dev_priv->pinned_bo || +	    (bo == dev_priv->dummy_query_bo && +	     dev_priv->dummy_query_bo_pinned)) +		return 0; + +	if (validate_as_mob) +		return ttm_bo_validate(bo, &vmw_mob_placement, true, false); +  	/**  	 * Put BO in VRAM if there is space, otherwise as a GMR.  	 * If there is no space in VRAM and GMR ids are all used up, @@ -573,7 +2220,7 @@ static int vmw_validate_single_buffer(struct vmw_private *dev_priv,  	 * used as a GMR, this will return -ENOMEM.  	 */ -	ret = ttm_bo_validate(bo, &vmw_vram_gmr_placement, true, false, false); +	ret = ttm_bo_validate(bo, &vmw_vram_gmr_placement, true, false);  	if (likely(ret == 0 || ret == -ERESTARTSYS))  		return ret; @@ -583,78 +2230,242 @@ static int vmw_validate_single_buffer(struct vmw_private *dev_priv,  	 */  	DRM_INFO("Falling through to VRAM.\n"); -	ret = ttm_bo_validate(bo, &vmw_vram_placement, true, false, false); +	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; +	struct vmw_validate_buffer *entry;  	int ret; -	list_for_each_entry(entry, &sw_context->validate_nodes, head) { -		ret = vmw_validate_single_buffer(dev_priv, entry->bo); +	list_for_each_entry(entry, &sw_context->validate_nodes, base.head) { +		ret = vmw_validate_single_buffer(dev_priv, entry->base.bo, +						 entry->validate_as_mob);  		if (unlikely(ret != 0))  			return ret;  	}  	return 0;  } -int vmw_execbuf_ioctl(struct drm_device *dev, void *data, -		      struct drm_file *file_priv) +static int vmw_resize_cmd_bounce(struct vmw_sw_context *sw_context, +				 uint32_t size) +{ +	if (likely(sw_context->cmd_bounce_size >= size)) +		return 0; + +	if (sw_context->cmd_bounce_size == 0) +		sw_context->cmd_bounce_size = VMWGFX_CMD_BOUNCE_INIT_SIZE; + +	while (sw_context->cmd_bounce_size < size) { +		sw_context->cmd_bounce_size = +			PAGE_ALIGN(sw_context->cmd_bounce_size + +				   (sw_context->cmd_bounce_size >> 1)); +	} + +	if (sw_context->cmd_bounce != NULL) +		vfree(sw_context->cmd_bounce); + +	sw_context->cmd_bounce = vmalloc(sw_context->cmd_bounce_size); + +	if (sw_context->cmd_bounce == NULL) { +		DRM_ERROR("Failed to allocate command bounce buffer.\n"); +		sw_context->cmd_bounce_size = 0; +		return -ENOMEM; +	} + +	return 0; +} + +/** + * vmw_execbuf_fence_commands - create and submit a command stream fence + * + * Creates a fence object and submits a command stream marker. + * If this fails for some reason, We sync the fifo and return NULL. + * It is then safe to fence buffers with a NULL pointer. + * + * If @p_handle is not NULL @file_priv must also not be NULL. Creates + * a userspace handle if @p_handle is not NULL, otherwise not. + */ + +int vmw_execbuf_fence_commands(struct drm_file *file_priv, +			       struct vmw_private *dev_priv, +			       struct vmw_fence_obj **p_fence, +			       uint32_t *p_handle)  { -	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); +	int ret; +	bool synced = false; -	ret = ttm_read_lock(&vmaster->lock, true); -	if (unlikely(ret != 0)) -		return ret; +	/* p_handle implies file_priv. */ +	BUG_ON(p_handle != NULL && file_priv == NULL); -	ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex); +	ret = vmw_fifo_send_fence(dev_priv, &sequence);  	if (unlikely(ret != 0)) { -		ret = -ERESTARTSYS; -		goto out_no_cmd_mutex; +		DRM_ERROR("Fence submission error. Syncing.\n"); +		synced = true;  	} -	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; +	if (p_handle != NULL) +		ret = vmw_user_fence_create(file_priv, dev_priv->fman, +					    sequence, +					    DRM_VMW_FENCE_FLAG_EXEC, +					    p_fence, p_handle); +	else +		ret = vmw_fence_create(dev_priv->fman, sequence, +				       DRM_VMW_FENCE_FLAG_EXEC, +				       p_fence); + +	if (unlikely(ret != 0 && !synced)) { +		(void) vmw_fallback_wait(dev_priv, false, false, +					 sequence, false, +					 VMW_FENCE_WAIT_TIMEOUT); +		*p_fence = NULL;  	} -	user_cmd = (void __user *)(unsigned long)arg->commands; -	ret = copy_from_user(cmd, user_cmd, arg->command_size); +	return 0; +} -	if (unlikely(ret != 0)) { -		ret = -EFAULT; -		DRM_ERROR("Failed copying commands.\n"); -		goto out_commit; +/** + * vmw_execbuf_copy_fence_user - copy fence object information to + * user-space. + * + * @dev_priv: Pointer to a vmw_private struct. + * @vmw_fp: Pointer to the struct vmw_fpriv representing the calling file. + * @ret: Return value from fence object creation. + * @user_fence_rep: User space address of a struct drm_vmw_fence_rep to + * which the information should be copied. + * @fence: Pointer to the fenc object. + * @fence_handle: User-space fence handle. + * + * This function copies fence information to user-space. If copying fails, + * The user-space struct drm_vmw_fence_rep::error member is hopefully + * left untouched, and if it's preloaded with an -EFAULT by user-space, + * the error will hopefully be detected. + * Also if copying fails, user-space will be unable to signal the fence + * object so we wait for it immediately, and then unreference the + * user-space reference. + */ +void +vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv, +			    struct vmw_fpriv *vmw_fp, +			    int ret, +			    struct drm_vmw_fence_rep __user *user_fence_rep, +			    struct vmw_fence_obj *fence, +			    uint32_t fence_handle) +{ +	struct drm_vmw_fence_rep fence_rep; + +	if (user_fence_rep == NULL) +		return; + +	memset(&fence_rep, 0, sizeof(fence_rep)); + +	fence_rep.error = ret; +	if (ret == 0) { +		BUG_ON(fence == NULL); + +		fence_rep.handle = fence_handle; +		fence_rep.seqno = fence->seqno; +		vmw_update_seqno(dev_priv, &dev_priv->fifo); +		fence_rep.passed_seqno = dev_priv->last_read_seqno;  	} -	sw_context->tfile = vmw_fpriv(file_priv)->tfile; -	sw_context->cid_valid = false; -	sw_context->sid_valid = false; +	/* +	 * copy_to_user errors will be detected by user space not +	 * seeing fence_rep::error filled in. Typically +	 * user-space would have pre-set that member to -EFAULT. +	 */ +	ret = copy_to_user(user_fence_rep, &fence_rep, +			   sizeof(fence_rep)); + +	/* +	 * User-space lost the fence object. We need to sync +	 * and unreference the handle. +	 */ +	if (unlikely(ret != 0) && (fence_rep.error == 0)) { +		ttm_ref_object_base_unref(vmw_fp->tfile, +					  fence_handle, TTM_REF_USAGE); +		DRM_ERROR("Fence copy error. Syncing.\n"); +		(void) vmw_fence_obj_wait(fence, fence->signal_mask, +					  false, false, +					  VMW_FENCE_WAIT_TIMEOUT); +	} +} + +int vmw_execbuf_process(struct drm_file *file_priv, +			struct vmw_private *dev_priv, +			void __user *user_commands, +			void *kernel_commands, +			uint32_t command_size, +			uint64_t throttle_us, +			struct drm_vmw_fence_rep __user *user_fence_rep, +			struct vmw_fence_obj **out_fence) +{ +	struct vmw_sw_context *sw_context = &dev_priv->ctx; +	struct vmw_fence_obj *fence = NULL; +	struct vmw_resource *error_resource; +	struct list_head resource_list; +	struct ww_acquire_ctx ticket; +	uint32_t handle; +	void *cmd; +	int ret; + +	ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex); +	if (unlikely(ret != 0)) +		return -ERESTARTSYS; + +	if (kernel_commands == NULL) { +		sw_context->kernel = false; + +		ret = vmw_resize_cmd_bounce(sw_context, command_size); +		if (unlikely(ret != 0)) +			goto out_unlock; + + +		ret = copy_from_user(sw_context->cmd_bounce, +				     user_commands, command_size); + +		if (unlikely(ret != 0)) { +			ret = -EFAULT; +			DRM_ERROR("Failed copying commands.\n"); +			goto out_unlock; +		} +		kernel_commands = sw_context->cmd_bounce; +	} else +		sw_context->kernel = true; + +	sw_context->fp = vmw_fpriv(file_priv);  	sw_context->cur_reloc = 0;  	sw_context->cur_val_buf = 0; - +	sw_context->fence_flags = 0; +	INIT_LIST_HEAD(&sw_context->resource_list); +	sw_context->cur_query_bo = dev_priv->pinned_bo; +	sw_context->last_query_ctx = NULL; +	sw_context->needs_post_query_barrier = false; +	memset(sw_context->res_cache, 0, sizeof(sw_context->res_cache));  	INIT_LIST_HEAD(&sw_context->validate_nodes); +	INIT_LIST_HEAD(&sw_context->res_relocations); +	if (!sw_context->res_ht_initialized) { +		ret = drm_ht_create(&sw_context->res_ht, VMW_RES_HT_ORDER); +		if (unlikely(ret != 0)) +			goto out_unlock; +		sw_context->res_ht_initialized = true; +	} +	INIT_LIST_HEAD(&sw_context->staged_shaders); -	ret = vmw_cmd_check_all(dev_priv, sw_context, cmd, arg->command_size); +	INIT_LIST_HEAD(&resource_list); +	ret = vmw_cmd_check_all(dev_priv, sw_context, kernel_commands, +				command_size);  	if (unlikely(ret != 0)) -		goto out_err; -	ret = ttm_eu_reserve_buffers(&sw_context->validate_nodes, -				     dev_priv->val_seq++); +		goto out_err_nores; + +	ret = vmw_resources_reserve(sw_context); +	if (unlikely(ret != 0)) +		goto out_err_nores; + +	ret = ttm_eu_reserve_buffers(&ticket, &sw_context->validate_nodes);  	if (unlikely(ret != 0))  		goto out_err; @@ -662,59 +2473,299 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data,  	if (unlikely(ret != 0))  		goto out_err; -	vmw_apply_relocations(sw_context); +	ret = vmw_resources_validate(sw_context); +	if (unlikely(ret != 0)) +		goto out_err; -	if (arg->throttle_us) { -		ret = vmw_wait_lag(dev_priv, &dev_priv->fifo.fence_queue, -				   arg->throttle_us); +	if (throttle_us) { +		ret = vmw_wait_lag(dev_priv, &dev_priv->fifo.marker_queue, +				   throttle_us);  		if (unlikely(ret != 0))  			goto out_err;  	} -	vmw_fifo_commit(dev_priv, arg->command_size); +	ret = mutex_lock_interruptible(&dev_priv->binding_mutex); +	if (unlikely(ret != 0)) { +		ret = -ERESTARTSYS; +		goto out_err; +	} -	ret = vmw_fifo_send_fence(dev_priv, &sequence); +	if (dev_priv->has_mob) { +		ret = vmw_rebind_contexts(sw_context); +		if (unlikely(ret != 0)) +			goto out_unlock_binding; +	} -	ttm_eu_fence_buffer_objects(&sw_context->validate_nodes, -				    (void *)(unsigned long) sequence); -	vmw_clear_validations(sw_context); -	mutex_unlock(&dev_priv->cmdbuf_mutex); +	cmd = vmw_fifo_reserve(dev_priv, command_size); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving fifo space for commands.\n"); +		ret = -ENOMEM; +		goto out_unlock_binding; +	} + +	vmw_apply_relocations(sw_context); +	memcpy(cmd, kernel_commands, command_size); + +	vmw_resource_relocations_apply(cmd, &sw_context->res_relocations); +	vmw_resource_relocations_free(&sw_context->res_relocations); +	vmw_fifo_commit(dev_priv, command_size); + +	vmw_query_bo_switch_commit(dev_priv, sw_context); +	ret = vmw_execbuf_fence_commands(file_priv, dev_priv, +					 &fence, +					 (user_fence_rep) ? &handle : NULL);  	/*  	 * This error is harmless, because if fence submission fails, -	 * vmw_fifo_send_fence will sync. +	 * vmw_fifo_send_fence will sync. The error will be propagated to +	 * user-space in @fence_rep  	 */  	if (ret != 0)  		DRM_ERROR("Fence submission error. Syncing.\n"); -	fence_rep.error = ret; -	fence_rep.fence_seq = (uint64_t) sequence; -	fence_rep.pad64 = 0; +	vmw_resource_list_unreserve(&sw_context->resource_list, false); +	mutex_unlock(&dev_priv->binding_mutex); + +	ttm_eu_fence_buffer_objects(&ticket, &sw_context->validate_nodes, +				    (void *) fence); + +	if (unlikely(dev_priv->pinned_bo != NULL && +		     !dev_priv->query_cid_valid)) +		__vmw_execbuf_release_pinned_bo(dev_priv, fence); + +	vmw_clear_validations(sw_context); +	vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), ret, +				    user_fence_rep, fence, handle); + +	/* Don't unreference when handing fence out */ +	if (unlikely(out_fence != NULL)) { +		*out_fence = fence; +		fence = NULL; +	} else if (likely(fence != NULL)) { +		vmw_fence_obj_unreference(&fence); +	} -	user_fence_rep = (struct drm_vmw_fence_rep __user *) -	    (unsigned long)arg->fence_rep; +	list_splice_init(&sw_context->resource_list, &resource_list); +	vmw_compat_shaders_commit(sw_context->fp->shman, +				  &sw_context->staged_shaders); +	mutex_unlock(&dev_priv->cmdbuf_mutex);  	/* -	 * copy_to_user errors will be detected by user space not -	 * seeing fence_rep::error filled in. +	 * Unreference resources outside of the cmdbuf_mutex to +	 * avoid deadlocks in resource destruction paths.  	 */ +	vmw_resource_list_unreference(&resource_list); -	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_unlock_binding: +	mutex_unlock(&dev_priv->binding_mutex);  out_err: +	ttm_eu_backoff_reservation(&ticket, &sw_context->validate_nodes); +out_err_nores: +	vmw_resource_list_unreserve(&sw_context->resource_list, true); +	vmw_resource_relocations_free(&sw_context->res_relocations);  	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); +	if (unlikely(dev_priv->pinned_bo != NULL && +		     !dev_priv->query_cid_valid)) +		__vmw_execbuf_release_pinned_bo(dev_priv, NULL); +out_unlock: +	list_splice_init(&sw_context->resource_list, &resource_list); +	error_resource = sw_context->error_resource; +	sw_context->error_resource = NULL; +	vmw_compat_shaders_revert(sw_context->fp->shman, +				  &sw_context->staged_shaders); +	mutex_unlock(&dev_priv->cmdbuf_mutex); + +	/* +	 * Unreference resources outside of the cmdbuf_mutex to +	 * avoid deadlocks in resource destruction paths. +	 */ +	vmw_resource_list_unreference(&resource_list); +	if (unlikely(error_resource != NULL)) +		vmw_resource_unreference(&error_resource); + +	return ret; +} + +/** + * vmw_execbuf_unpin_panic - Idle the fifo and unpin the query buffer. + * + * @dev_priv: The device private structure. + * + * This function is called to idle the fifo and unpin the query buffer + * if the normal way to do this hits an error, which should typically be + * extremely rare. + */ +static void vmw_execbuf_unpin_panic(struct vmw_private *dev_priv) +{ +	DRM_ERROR("Can't unpin query buffer. Trying to recover.\n"); + +	(void) vmw_fallback_wait(dev_priv, false, true, 0, false, 10*HZ); +	vmw_bo_pin(dev_priv->pinned_bo, false); +	vmw_bo_pin(dev_priv->dummy_query_bo, false); +	dev_priv->dummy_query_bo_pinned = false; +} + + +/** + * __vmw_execbuf_release_pinned_bo - Flush queries and unpin the pinned + * query bo. + * + * @dev_priv: The device private structure. + * @fence: If non-NULL should point to a struct vmw_fence_obj issued + * _after_ a query barrier that flushes all queries touching the current + * buffer pointed to by @dev_priv->pinned_bo + * + * This function should be used to unpin the pinned query bo, or + * as a query barrier when we need to make sure that all queries have + * finished before the next fifo command. (For example on hardware + * context destructions where the hardware may otherwise leak unfinished + * queries). + * + * This function does not return any failure codes, but make attempts + * to do safe unpinning in case of errors. + * + * The function will synchronize on the previous query barrier, and will + * thus not finish until that barrier has executed. + * + * the @dev_priv->cmdbuf_mutex needs to be held by the current thread + * before calling this function. + */ +void __vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv, +				     struct vmw_fence_obj *fence) +{ +	int ret = 0; +	struct list_head validate_list; +	struct ttm_validate_buffer pinned_val, query_val; +	struct vmw_fence_obj *lfence = NULL; +	struct ww_acquire_ctx ticket; + +	if (dev_priv->pinned_bo == NULL) +		goto out_unlock; + +	INIT_LIST_HEAD(&validate_list); + +	pinned_val.bo = ttm_bo_reference(dev_priv->pinned_bo); +	list_add_tail(&pinned_val.head, &validate_list); + +	query_val.bo = ttm_bo_reference(dev_priv->dummy_query_bo); +	list_add_tail(&query_val.head, &validate_list); + +	do { +		ret = ttm_eu_reserve_buffers(&ticket, &validate_list); +	} while (ret == -ERESTARTSYS); + +	if (unlikely(ret != 0)) { +		vmw_execbuf_unpin_panic(dev_priv); +		goto out_no_reserve; +	} + +	if (dev_priv->query_cid_valid) { +		BUG_ON(fence != NULL); +		ret = vmw_fifo_emit_dummy_query(dev_priv, dev_priv->query_cid); +		if (unlikely(ret != 0)) { +			vmw_execbuf_unpin_panic(dev_priv); +			goto out_no_emit; +		} +		dev_priv->query_cid_valid = false; +	} + +	vmw_bo_pin(dev_priv->pinned_bo, false); +	vmw_bo_pin(dev_priv->dummy_query_bo, false); +	dev_priv->dummy_query_bo_pinned = false; + +	if (fence == NULL) { +		(void) vmw_execbuf_fence_commands(NULL, dev_priv, &lfence, +						  NULL); +		fence = lfence; +	} +	ttm_eu_fence_buffer_objects(&ticket, &validate_list, (void *) fence); +	if (lfence != NULL) +		vmw_fence_obj_unreference(&lfence); + +	ttm_bo_unref(&query_val.bo); +	ttm_bo_unref(&pinned_val.bo); +	ttm_bo_unref(&dev_priv->pinned_bo); +  out_unlock: +	return; + +out_no_emit: +	ttm_eu_backoff_reservation(&ticket, &validate_list); +out_no_reserve: +	ttm_bo_unref(&query_val.bo); +	ttm_bo_unref(&pinned_val.bo); +	ttm_bo_unref(&dev_priv->pinned_bo); +} + +/** + * vmw_execbuf_release_pinned_bo - Flush queries and unpin the pinned + * query bo. + * + * @dev_priv: The device private structure. + * + * This function should be used to unpin the pinned query bo, or + * as a query barrier when we need to make sure that all queries have + * finished before the next fifo command. (For example on hardware + * context destructions where the hardware may otherwise leak unfinished + * queries). + * + * This function does not return any failure codes, but make attempts + * to do safe unpinning in case of errors. + * + * The function will synchronize on the previous query barrier, and will + * thus not finish until that barrier has executed. + */ +void vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv) +{ +	mutex_lock(&dev_priv->cmdbuf_mutex); +	if (dev_priv->query_cid_valid) +		__vmw_execbuf_release_pinned_bo(dev_priv, NULL);  	mutex_unlock(&dev_priv->cmdbuf_mutex); -out_no_cmd_mutex: -	ttm_read_unlock(&vmaster->lock); +} + + +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; +	int ret; + +	/* +	 * This will allow us to extend the ioctl argument while +	 * maintaining backwards compatibility: +	 * We take different code paths depending on the value of +	 * arg->version. +	 */ + +	if (unlikely(arg->version != DRM_VMW_EXECBUF_VERSION)) { +		DRM_ERROR("Incorrect execbuf version.\n"); +		DRM_ERROR("You're running outdated experimental " +			  "vmwgfx user-space drivers."); +		return -EINVAL; +	} + +	ret = ttm_read_lock(&dev_priv->reservation_sem, true); +	if (unlikely(ret != 0)) +		return ret; + +	ret = vmw_execbuf_process(file_priv, dev_priv, +				  (void __user *)(unsigned long)arg->commands, +				  NULL, arg->command_size, arg->throttle_us, +				  (void __user *)(unsigned long)arg->fence_rep, +				  NULL); + +	if (unlikely(ret != 0)) +		goto out_unlock; + +	vmw_kms_cursor_post_execbuf(dev_priv); + +out_unlock: +	ttm_read_unlock(&dev_priv->reservation_sem);  	return ret;  } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index 41d9a5b73c0..b031b48dbb3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -26,10 +26,12 @@   *   **************************************************************************/ -#include "drmP.h" +#include <linux/export.h> + +#include <drm/drmP.h>  #include "vmwgfx_drv.h" -#include "ttm/ttm_placement.h" +#include <drm/ttm/ttm_placement.h>  #define VMW_DIRTY_DELAY (HZ / 30) @@ -145,7 +147,7 @@ static int vmw_fb_check_var(struct fb_var_screeninfo *var,  	}  	if (!vmw_kms_validate_mode_vram(vmw_priv, -					info->fix.line_length, +					var->xres * var->bits_per_pixel/8,  					var->yoffset + var->yres)) {  		DRM_ERROR("Requested geom can not fit in framebuffer\n");  		return -EINVAL; @@ -158,10 +160,16 @@ 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; +	int ret; + +	info->fix.line_length = info->var.xres * info->var.bits_per_pixel/8; + +	ret = vmw_kms_write_svga(vmw_priv, info->var.xres, info->var.yres, +				 info->fix.line_length, +				 par->bpp, par->depth); +	if (ret) +		return ret; -	vmw_kms_write_svga(vmw_priv, info->var.xres, info->var.yres, -			   info->fix.line_length, -			   par->bpp, par->depth);  	if (vmw_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY) {  		/* TODO check if pitch and offset changes */  		vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1); @@ -371,14 +379,13 @@ static int vmw_fb_create_bo(struct vmw_private *vmw_priv,  	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; +	(void) ttm_write_lock(&vmw_priv->reservation_sem, false);  	vmw_bo = kmalloc(sizeof(*vmw_bo), GFP_KERNEL); -	if (!vmw_bo) +	if (!vmw_bo) { +		ret = -ENOMEM;  		goto err_unlock; +	}  	ret = vmw_dmabuf_init(vmw_priv, vmw_bo, size,  			      &ne_placement, @@ -405,24 +412,20 @@ int vmw_fb_init(struct vmw_private *vmw_priv)  	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; +	unsigned fb_bpp, fb_depth, fb_offset, fb_pitch, fb_size;  	int ret; -	/* XXX These shouldn't be hardcoded. */ -	initial_width = 800; -	initial_height = 600; - -	fb_bbp = 32; +	fb_bpp = 32;  	fb_depth = 24;  	/* XXX As shouldn't these be as well. */  	fb_width = min(vmw_priv->fb_max_width, (unsigned)2048);  	fb_height = min(vmw_priv->fb_max_height, (unsigned)2048); -	initial_width = min(fb_width, initial_width); -	initial_height = min(fb_height, initial_height); +	initial_width = min(vmw_priv->initial_width, fb_width); +	initial_height = min(vmw_priv->initial_height, fb_height); -	fb_pitch = fb_width * fb_bbp / 8; +	fb_pitch = fb_width * fb_bpp / 8;  	fb_size = fb_pitch * fb_height;  	fb_offset = vmw_read(vmw_priv, SVGA_REG_FB_OFFSET); @@ -437,7 +440,7 @@ int vmw_fb_init(struct vmw_private *vmw_priv)  	par = info->par;  	par->vmw_priv = vmw_priv;  	par->depth = fb_depth; -	par->bpp = fb_bbp; +	par->bpp = fb_bpp;  	par->vmalloc = NULL;  	par->max_width = fb_width;  	par->max_height = fb_height; @@ -480,9 +483,6 @@ int vmw_fb_init(struct vmw_private *vmw_priv)  	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; @@ -512,19 +512,7 @@ int vmw_fb_init(struct vmw_private *vmw_priv)  	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 +	/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */  	info->apertures = alloc_apertures(1);  	if (!info->apertures) { @@ -591,58 +579,6 @@ int vmw_fb_close(struct vmw_private *vmw_priv)  	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, 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 (bo->mem.mem_type == TTM_PL_VRAM && -	    bo->mem.start < bo->num_pages && -	    bo->mem.start > 0) -		(void) ttm_bo_validate(bo, &vmw_sys_placement, false, -				       false, false); - -	ret = ttm_bo_validate(bo, &ne_placement, false, false, false); - -	/* Could probably bug on */ -	WARN_ON(bo->offset != 0); - -	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; @@ -659,12 +595,12 @@ int vmw_fb_off(struct vmw_private *vmw_priv)  	par->dirty.active = false;  	spin_unlock_irqrestore(&par->dirty.lock, flags); -	flush_scheduled_work(); +	flush_delayed_work(&info->deferred_work);  	par->bo_ptr = NULL;  	ttm_bo_kunmap(&par->map); -	vmw_dmabuf_from_vram(vmw_priv, par->vmw_bo); +	vmw_dmabuf_unpin(vmw_priv, par->vmw_bo, false);  	return 0;  } @@ -690,7 +626,7 @@ int vmw_fb_on(struct vmw_private *vmw_priv)  	/* 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); +	ret = vmw_dmabuf_to_start_of_vram(vmw_priv, par->vmw_bo, true, false);  	if (unlikely(ret != 0)) {  		DRM_ERROR("could not move buffer to start of VRAM\n");  		goto err_no_buffer; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c index 61eacc1b5ca..436b013b423 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -1,6 +1,6 @@  /**************************************************************************   * - * Copyright (C) 2010 VMware, Inc., Palo Alto, CA., USA + * Copyright © 2011 VMware, Inc., Palo Alto, CA., USA   * All Rights Reserved.   *   * Permission is hereby granted, free of charge, to any person obtaining a @@ -25,149 +25,1131 @@   *   **************************************************************************/ - +#include <drm/drmP.h>  #include "vmwgfx_drv.h" -struct vmw_fence { -	struct list_head head; -	uint32_t sequence; -	struct timespec submitted; +#define VMW_FENCE_WRAP (1 << 31) + +struct vmw_fence_manager { +	int num_fence_objects; +	struct vmw_private *dev_priv; +	spinlock_t lock; +	struct list_head fence_list; +	struct work_struct work; +	u32 user_fence_size; +	u32 fence_size; +	u32 event_fence_action_size; +	bool fifo_down; +	struct list_head cleanup_list; +	uint32_t pending_actions[VMW_ACTION_MAX]; +	struct mutex goal_irq_mutex; +	bool goal_irq_on; /* Protected by @goal_irq_mutex */ +	bool seqno_valid; /* Protected by @lock, and may not be set to true +			     without the @goal_irq_mutex held. */ +}; + +struct vmw_user_fence { +	struct ttm_base_object base; +	struct vmw_fence_obj fence; +}; + +/** + * struct vmw_event_fence_action - fence action that delivers a drm event. + * + * @e: A struct drm_pending_event that controls the event delivery. + * @action: A struct vmw_fence_action to hook up to a fence. + * @fence: A referenced pointer to the fence to keep it alive while @action + * hangs on it. + * @dev: Pointer to a struct drm_device so we can access the event stuff. + * @kref: Both @e and @action has destructors, so we need to refcount. + * @size: Size accounted for this object. + * @tv_sec: If non-null, the variable pointed to will be assigned + * current time tv_sec val when the fence signals. + * @tv_usec: Must be set if @tv_sec is set, and the variable pointed to will + * be assigned the current time tv_usec val when the fence signals. + */ +struct vmw_event_fence_action { +	struct vmw_fence_action action; +	struct list_head fpriv_head; + +	struct drm_pending_event *event; +	struct vmw_fence_obj *fence; +	struct drm_device *dev; + +	uint32_t *tv_sec; +	uint32_t *tv_usec;  }; -void vmw_fence_queue_init(struct vmw_fence_queue *queue) +/** + * Note on fencing subsystem usage of irqs: + * Typically the vmw_fences_update function is called + * + * a) When a new fence seqno has been submitted by the fifo code. + * b) On-demand when we have waiters. Sleeping waiters will switch on the + * ANY_FENCE irq and call vmw_fences_update function each time an ANY_FENCE + * irq is received. When the last fence waiter is gone, that IRQ is masked + * away. + * + * In situations where there are no waiters and we don't submit any new fences, + * fence objects may not be signaled. This is perfectly OK, since there are + * no consumers of the signaled data, but that is NOT ok when there are fence + * actions attached to a fence. The fencing subsystem then makes use of the + * FENCE_GOAL irq and sets the fence goal seqno to that of the next fence + * which has an action attached, and each time vmw_fences_update is called, + * the subsystem makes sure the fence goal seqno is updated. + * + * The fence goal seqno irq is on as long as there are unsignaled fence + * objects with actions attached to them. + */ + +static void vmw_fence_obj_destroy_locked(struct kref *kref)  { -	INIT_LIST_HEAD(&queue->head); -	queue->lag = ns_to_timespec(0); -	getrawmonotonic(&queue->lag_time); -	spin_lock_init(&queue->lock); +	struct vmw_fence_obj *fence = +		container_of(kref, struct vmw_fence_obj, kref); + +	struct vmw_fence_manager *fman = fence->fman; +	unsigned int num_fences; + +	list_del_init(&fence->head); +	num_fences = --fman->num_fence_objects; +	spin_unlock_irq(&fman->lock); +	if (fence->destroy) +		fence->destroy(fence); +	else +		kfree(fence); + +	spin_lock_irq(&fman->lock);  } -void vmw_fence_queue_takedown(struct vmw_fence_queue *queue) + +/** + * Execute signal actions on fences recently signaled. + * This is done from a workqueue so we don't have to execute + * signal actions from atomic context. + */ + +static void vmw_fence_work_func(struct work_struct *work)  { -	struct vmw_fence *fence, *next; +	struct vmw_fence_manager *fman = +		container_of(work, struct vmw_fence_manager, work); +	struct list_head list; +	struct vmw_fence_action *action, *next_action; +	bool seqno_valid; -	spin_lock(&queue->lock); -	list_for_each_entry_safe(fence, next, &queue->head, head) { -		kfree(fence); +	do { +		INIT_LIST_HEAD(&list); +		mutex_lock(&fman->goal_irq_mutex); + +		spin_lock_irq(&fman->lock); +		list_splice_init(&fman->cleanup_list, &list); +		seqno_valid = fman->seqno_valid; +		spin_unlock_irq(&fman->lock); + +		if (!seqno_valid && fman->goal_irq_on) { +			fman->goal_irq_on = false; +			vmw_goal_waiter_remove(fman->dev_priv); +		} +		mutex_unlock(&fman->goal_irq_mutex); + +		if (list_empty(&list)) +			return; + +		/* +		 * At this point, only we should be able to manipulate the +		 * list heads of the actions we have on the private list. +		 * hence fman::lock not held. +		 */ + +		list_for_each_entry_safe(action, next_action, &list, head) { +			list_del_init(&action->head); +			if (action->cleanup) +				action->cleanup(action); +		} +	} while (1); +} + +struct vmw_fence_manager *vmw_fence_manager_init(struct vmw_private *dev_priv) +{ +	struct vmw_fence_manager *fman = kzalloc(sizeof(*fman), GFP_KERNEL); + +	if (unlikely(fman == NULL)) +		return NULL; + +	fman->dev_priv = dev_priv; +	spin_lock_init(&fman->lock); +	INIT_LIST_HEAD(&fman->fence_list); +	INIT_LIST_HEAD(&fman->cleanup_list); +	INIT_WORK(&fman->work, &vmw_fence_work_func); +	fman->fifo_down = true; +	fman->user_fence_size = ttm_round_pot(sizeof(struct vmw_user_fence)); +	fman->fence_size = ttm_round_pot(sizeof(struct vmw_fence_obj)); +	fman->event_fence_action_size = +		ttm_round_pot(sizeof(struct vmw_event_fence_action)); +	mutex_init(&fman->goal_irq_mutex); + +	return fman; +} + +void vmw_fence_manager_takedown(struct vmw_fence_manager *fman) +{ +	unsigned long irq_flags; +	bool lists_empty; + +	(void) cancel_work_sync(&fman->work); + +	spin_lock_irqsave(&fman->lock, irq_flags); +	lists_empty = list_empty(&fman->fence_list) && +		list_empty(&fman->cleanup_list); +	spin_unlock_irqrestore(&fman->lock, irq_flags); + +	BUG_ON(!lists_empty); +	kfree(fman); +} + +static int vmw_fence_obj_init(struct vmw_fence_manager *fman, +			      struct vmw_fence_obj *fence, +			      u32 seqno, +			      uint32_t mask, +			      void (*destroy) (struct vmw_fence_obj *fence)) +{ +	unsigned long irq_flags; +	unsigned int num_fences; +	int ret = 0; + +	fence->seqno = seqno; +	INIT_LIST_HEAD(&fence->seq_passed_actions); +	fence->fman = fman; +	fence->signaled = 0; +	fence->signal_mask = mask; +	kref_init(&fence->kref); +	fence->destroy = destroy; +	init_waitqueue_head(&fence->queue); + +	spin_lock_irqsave(&fman->lock, irq_flags); +	if (unlikely(fman->fifo_down)) { +		ret = -EBUSY; +		goto out_unlock;  	} -	spin_unlock(&queue->lock); +	list_add_tail(&fence->head, &fman->fence_list); +	num_fences = ++fman->num_fence_objects; + +out_unlock: +	spin_unlock_irqrestore(&fman->lock, irq_flags); +	return ret; +  } -int vmw_fence_push(struct vmw_fence_queue *queue, -		   uint32_t sequence) +struct vmw_fence_obj *vmw_fence_obj_reference(struct vmw_fence_obj *fence)  { -	struct vmw_fence *fence = kmalloc(sizeof(*fence), GFP_KERNEL); +	if (unlikely(fence == NULL)) +		return NULL; -	if (unlikely(!fence)) -		return -ENOMEM; +	kref_get(&fence->kref); +	return fence; +} + +/** + * vmw_fence_obj_unreference + * + * Note that this function may not be entered with disabled irqs since + * it may re-enable them in the destroy function. + * + */ +void vmw_fence_obj_unreference(struct vmw_fence_obj **fence_p) +{ +	struct vmw_fence_obj *fence = *fence_p; +	struct vmw_fence_manager *fman; + +	if (unlikely(fence == NULL)) +		return; + +	fman = fence->fman; +	*fence_p = NULL; +	spin_lock_irq(&fman->lock); +	BUG_ON(atomic_read(&fence->kref.refcount) == 0); +	kref_put(&fence->kref, vmw_fence_obj_destroy_locked); +	spin_unlock_irq(&fman->lock); +} + +static void vmw_fences_perform_actions(struct vmw_fence_manager *fman, +				struct list_head *list) +{ +	struct vmw_fence_action *action, *next_action; + +	list_for_each_entry_safe(action, next_action, list, head) { +		list_del_init(&action->head); +		fman->pending_actions[action->type]--; +		if (action->seq_passed != NULL) +			action->seq_passed(action); + +		/* +		 * Add the cleanup action to the cleanup list so that +		 * it will be performed by a worker task. +		 */ + +		list_add_tail(&action->head, &fman->cleanup_list); +	} +} + +/** + * vmw_fence_goal_new_locked - Figure out a new device fence goal + * seqno if needed. + * + * @fman: Pointer to a fence manager. + * @passed_seqno: The seqno the device currently signals as passed. + * + * This function should be called with the fence manager lock held. + * It is typically called when we have a new passed_seqno, and + * we might need to update the fence goal. It checks to see whether + * the current fence goal has already passed, and, in that case, + * scans through all unsignaled fences to get the next fence object with an + * action attached, and sets the seqno of that fence as a new fence goal. + * + * returns true if the device goal seqno was updated. False otherwise. + */ +static bool vmw_fence_goal_new_locked(struct vmw_fence_manager *fman, +				      u32 passed_seqno) +{ +	u32 goal_seqno; +	__le32 __iomem *fifo_mem; +	struct vmw_fence_obj *fence; -	fence->sequence = sequence; -	getrawmonotonic(&fence->submitted); -	spin_lock(&queue->lock); -	list_add_tail(&fence->head, &queue->head); -	spin_unlock(&queue->lock); +	if (likely(!fman->seqno_valid)) +		return false; +	fifo_mem = fman->dev_priv->mmio_virt; +	goal_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE_GOAL); +	if (likely(passed_seqno - goal_seqno >= VMW_FENCE_WRAP)) +		return false; + +	fman->seqno_valid = false; +	list_for_each_entry(fence, &fman->fence_list, head) { +		if (!list_empty(&fence->seq_passed_actions)) { +			fman->seqno_valid = true; +			iowrite32(fence->seqno, +				  fifo_mem + SVGA_FIFO_FENCE_GOAL); +			break; +		} +	} + +	return true; +} + + +/** + * vmw_fence_goal_check_locked - Replace the device fence goal seqno if + * needed. + * + * @fence: Pointer to a struct vmw_fence_obj the seqno of which should be + * considered as a device fence goal. + * + * This function should be called with the fence manager lock held. + * It is typically called when an action has been attached to a fence to + * check whether the seqno of that fence should be used for a fence + * goal interrupt. This is typically needed if the current fence goal is + * invalid, or has a higher seqno than that of the current fence object. + * + * returns true if the device goal seqno was updated. False otherwise. + */ +static bool vmw_fence_goal_check_locked(struct vmw_fence_obj *fence) +{ +	u32 goal_seqno; +	__le32 __iomem *fifo_mem; + +	if (fence->signaled & DRM_VMW_FENCE_FLAG_EXEC) +		return false; + +	fifo_mem = fence->fman->dev_priv->mmio_virt; +	goal_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE_GOAL); +	if (likely(fence->fman->seqno_valid && +		   goal_seqno - fence->seqno < VMW_FENCE_WRAP)) +		return false; + +	iowrite32(fence->seqno, fifo_mem + SVGA_FIFO_FENCE_GOAL); +	fence->fman->seqno_valid = true; + +	return true; +} + +void vmw_fences_update(struct vmw_fence_manager *fman) +{ +	unsigned long flags; +	struct vmw_fence_obj *fence, *next_fence; +	struct list_head action_list; +	bool needs_rerun; +	uint32_t seqno, new_seqno; +	__le32 __iomem *fifo_mem = fman->dev_priv->mmio_virt; + +	seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE); +rerun: +	spin_lock_irqsave(&fman->lock, flags); +	list_for_each_entry_safe(fence, next_fence, &fman->fence_list, head) { +		if (seqno - fence->seqno < VMW_FENCE_WRAP) { +			list_del_init(&fence->head); +			fence->signaled |= DRM_VMW_FENCE_FLAG_EXEC; +			INIT_LIST_HEAD(&action_list); +			list_splice_init(&fence->seq_passed_actions, +					 &action_list); +			vmw_fences_perform_actions(fman, &action_list); +			wake_up_all(&fence->queue); +		} else +			break; +	} + +	needs_rerun = vmw_fence_goal_new_locked(fman, seqno); + +	if (!list_empty(&fman->cleanup_list)) +		(void) schedule_work(&fman->work); +	spin_unlock_irqrestore(&fman->lock, flags); + +	/* +	 * Rerun if the fence goal seqno was updated, and the +	 * hardware might have raced with that update, so that +	 * we missed a fence_goal irq. +	 */ + +	if (unlikely(needs_rerun)) { +		new_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE); +		if (new_seqno != seqno) { +			seqno = new_seqno; +			goto rerun; +		} +	} +} + +bool vmw_fence_obj_signaled(struct vmw_fence_obj *fence, +			    uint32_t flags) +{ +	struct vmw_fence_manager *fman = fence->fman; +	unsigned long irq_flags; +	uint32_t signaled; + +	spin_lock_irqsave(&fman->lock, irq_flags); +	signaled = fence->signaled; +	spin_unlock_irqrestore(&fman->lock, irq_flags); + +	flags &= fence->signal_mask; +	if ((signaled & flags) == flags) +		return 1; + +	if ((signaled & DRM_VMW_FENCE_FLAG_EXEC) == 0) +		vmw_fences_update(fman); + +	spin_lock_irqsave(&fman->lock, irq_flags); +	signaled = fence->signaled; +	spin_unlock_irqrestore(&fman->lock, irq_flags); + +	return ((signaled & flags) == flags); +} + +int vmw_fence_obj_wait(struct vmw_fence_obj *fence, +		       uint32_t flags, bool lazy, +		       bool interruptible, unsigned long timeout) +{ +	struct vmw_private *dev_priv = fence->fman->dev_priv; +	long ret; + +	if (likely(vmw_fence_obj_signaled(fence, flags))) +		return 0; + +	vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); +	vmw_seqno_waiter_add(dev_priv); + +	if (interruptible) +		ret = wait_event_interruptible_timeout +			(fence->queue, +			 vmw_fence_obj_signaled(fence, flags), +			 timeout); +	else +		ret = wait_event_timeout +			(fence->queue, +			 vmw_fence_obj_signaled(fence, flags), +			 timeout); + +	vmw_seqno_waiter_remove(dev_priv); + +	if (unlikely(ret == 0)) +		ret = -EBUSY; +	else if (likely(ret > 0)) +		ret = 0; + +	return ret; +} + +void vmw_fence_obj_flush(struct vmw_fence_obj *fence) +{ +	struct vmw_private *dev_priv = fence->fman->dev_priv; + +	vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); +} + +static void vmw_fence_destroy(struct vmw_fence_obj *fence) +{ +	struct vmw_fence_manager *fman = fence->fman; + +	kfree(fence); +	/* +	 * Free kernel space accounting. +	 */ +	ttm_mem_global_free(vmw_mem_glob(fman->dev_priv), +			    fman->fence_size); +} + +int vmw_fence_create(struct vmw_fence_manager *fman, +		     uint32_t seqno, +		     uint32_t mask, +		     struct vmw_fence_obj **p_fence) +{ +	struct ttm_mem_global *mem_glob = vmw_mem_glob(fman->dev_priv); +	struct vmw_fence_obj *fence; +	int ret; + +	ret = ttm_mem_global_alloc(mem_glob, fman->fence_size, +				   false, false); +	if (unlikely(ret != 0)) +		return ret; + +	fence = kzalloc(sizeof(*fence), GFP_KERNEL); +	if (unlikely(fence == NULL)) { +		ret = -ENOMEM; +		goto out_no_object; +	} + +	ret = vmw_fence_obj_init(fman, fence, seqno, mask, +				 vmw_fence_destroy); +	if (unlikely(ret != 0)) +		goto out_err_init; + +	*p_fence = fence;  	return 0; + +out_err_init: +	kfree(fence); +out_no_object: +	ttm_mem_global_free(mem_glob, fman->fence_size); +	return ret;  } -int vmw_fence_pull(struct vmw_fence_queue *queue, -		   uint32_t signaled_sequence) + +static void vmw_user_fence_destroy(struct vmw_fence_obj *fence)  { -	struct vmw_fence *fence, *next; -	struct timespec now; -	bool updated = false; +	struct vmw_user_fence *ufence = +		container_of(fence, struct vmw_user_fence, fence); +	struct vmw_fence_manager *fman = fence->fman; -	spin_lock(&queue->lock); -	getrawmonotonic(&now); +	ttm_base_object_kfree(ufence, base); +	/* +	 * Free kernel space accounting. +	 */ +	ttm_mem_global_free(vmw_mem_glob(fman->dev_priv), +			    fman->user_fence_size); +} -	if (list_empty(&queue->head)) { -		queue->lag = ns_to_timespec(0); -		queue->lag_time = now; -		updated = true; -		goto out_unlock; +static void vmw_user_fence_base_release(struct ttm_base_object **p_base) +{ +	struct ttm_base_object *base = *p_base; +	struct vmw_user_fence *ufence = +		container_of(base, struct vmw_user_fence, base); +	struct vmw_fence_obj *fence = &ufence->fence; + +	*p_base = NULL; +	vmw_fence_obj_unreference(&fence); +} + +int vmw_user_fence_create(struct drm_file *file_priv, +			  struct vmw_fence_manager *fman, +			  uint32_t seqno, +			  uint32_t mask, +			  struct vmw_fence_obj **p_fence, +			  uint32_t *p_handle) +{ +	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; +	struct vmw_user_fence *ufence; +	struct vmw_fence_obj *tmp; +	struct ttm_mem_global *mem_glob = vmw_mem_glob(fman->dev_priv); +	int ret; + +	/* +	 * Kernel memory space accounting, since this object may +	 * be created by a user-space request. +	 */ + +	ret = ttm_mem_global_alloc(mem_glob, fman->user_fence_size, +				   false, false); +	if (unlikely(ret != 0)) +		return ret; + +	ufence = kzalloc(sizeof(*ufence), GFP_KERNEL); +	if (unlikely(ufence == NULL)) { +		ret = -ENOMEM; +		goto out_no_object;  	} -	list_for_each_entry_safe(fence, next, &queue->head, head) { -		if (signaled_sequence - fence->sequence > (1 << 30)) -			continue; +	ret = vmw_fence_obj_init(fman, &ufence->fence, seqno, +				 mask, vmw_user_fence_destroy); +	if (unlikely(ret != 0)) { +		kfree(ufence); +		goto out_no_object; +	} -		queue->lag = timespec_sub(now, fence->submitted); -		queue->lag_time = now; -		updated = true; -		list_del(&fence->head); -		kfree(fence); +	/* +	 * The base object holds a reference which is freed in +	 * vmw_user_fence_base_release. +	 */ +	tmp = vmw_fence_obj_reference(&ufence->fence); +	ret = ttm_base_object_init(tfile, &ufence->base, false, +				   VMW_RES_FENCE, +				   &vmw_user_fence_base_release, NULL); + + +	if (unlikely(ret != 0)) { +		/* +		 * Free the base object's reference +		 */ +		vmw_fence_obj_unreference(&tmp); +		goto out_err; +	} + +	*p_fence = &ufence->fence; +	*p_handle = ufence->base.hash.key; + +	return 0; +out_err: +	tmp = &ufence->fence; +	vmw_fence_obj_unreference(&tmp); +out_no_object: +	ttm_mem_global_free(mem_glob, fman->user_fence_size); +	return ret; +} + + +/** + * vmw_fence_fifo_down - signal all unsignaled fence objects. + */ + +void vmw_fence_fifo_down(struct vmw_fence_manager *fman) +{ +	unsigned long irq_flags; +	struct list_head action_list; +	int ret; + +	/* +	 * The list may be altered while we traverse it, so always +	 * restart when we've released the fman->lock. +	 */ + +	spin_lock_irqsave(&fman->lock, irq_flags); +	fman->fifo_down = true; +	while (!list_empty(&fman->fence_list)) { +		struct vmw_fence_obj *fence = +			list_entry(fman->fence_list.prev, struct vmw_fence_obj, +				   head); +		kref_get(&fence->kref); +		spin_unlock_irq(&fman->lock); + +		ret = vmw_fence_obj_wait(fence, fence->signal_mask, +					 false, false, +					 VMW_FENCE_WAIT_TIMEOUT); + +		if (unlikely(ret != 0)) { +			list_del_init(&fence->head); +			fence->signaled |= DRM_VMW_FENCE_FLAG_EXEC; +			INIT_LIST_HEAD(&action_list); +			list_splice_init(&fence->seq_passed_actions, +					 &action_list); +			vmw_fences_perform_actions(fman, &action_list); +			wake_up_all(&fence->queue); +		} + +		spin_lock_irq(&fman->lock); + +		BUG_ON(!list_empty(&fence->head)); +		kref_put(&fence->kref, vmw_fence_obj_destroy_locked); +	} +	spin_unlock_irqrestore(&fman->lock, irq_flags); +} + +void vmw_fence_fifo_up(struct vmw_fence_manager *fman) +{ +	unsigned long irq_flags; + +	spin_lock_irqsave(&fman->lock, irq_flags); +	fman->fifo_down = false; +	spin_unlock_irqrestore(&fman->lock, irq_flags); +} + + +int vmw_fence_obj_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; +	struct ttm_base_object *base; +	struct vmw_fence_obj *fence; +	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; +	int ret; +	uint64_t wait_timeout = ((uint64_t)arg->timeout_us * HZ); + +	/* +	 * 64-bit division not present on 32-bit systems, so do an +	 * approximation. (Divide by 1000000). +	 */ + +	wait_timeout = (wait_timeout >> 20) + (wait_timeout >> 24) - +	  (wait_timeout >> 26); + +	if (!arg->cookie_valid) { +		arg->cookie_valid = 1; +		arg->kernel_cookie = jiffies + wait_timeout; +	} + +	base = ttm_base_object_lookup(tfile, arg->handle); +	if (unlikely(base == NULL)) { +		printk(KERN_ERR "Wait invalid fence object handle " +		       "0x%08lx.\n", +		       (unsigned long)arg->handle); +		return -EINVAL; +	} + +	fence = &(container_of(base, struct vmw_user_fence, base)->fence); + +	timeout = jiffies; +	if (time_after_eq(timeout, (unsigned long)arg->kernel_cookie)) { +		ret = ((vmw_fence_obj_signaled(fence, arg->flags)) ? +		       0 : -EBUSY); +		goto out; +	} + +	timeout = (unsigned long)arg->kernel_cookie - timeout; + +	ret = vmw_fence_obj_wait(fence, arg->flags, arg->lazy, true, timeout); + +out: +	ttm_base_object_unref(&base); + +	/* +	 * Optionally unref the fence object. +	 */ + +	if (ret == 0 && (arg->wait_options & DRM_VMW_WAIT_OPTION_UNREF)) +		return ttm_ref_object_base_unref(tfile, arg->handle, +						 TTM_REF_USAGE); +	return ret; +} + +int vmw_fence_obj_signaled_ioctl(struct drm_device *dev, void *data, +				 struct drm_file *file_priv) +{ +	struct drm_vmw_fence_signaled_arg *arg = +		(struct drm_vmw_fence_signaled_arg *) data; +	struct ttm_base_object *base; +	struct vmw_fence_obj *fence; +	struct vmw_fence_manager *fman; +	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; +	struct vmw_private *dev_priv = vmw_priv(dev); + +	base = ttm_base_object_lookup(tfile, arg->handle); +	if (unlikely(base == NULL)) { +		printk(KERN_ERR "Fence signaled invalid fence object handle " +		       "0x%08lx.\n", +		       (unsigned long)arg->handle); +		return -EINVAL;  	} +	fence = &(container_of(base, struct vmw_user_fence, base)->fence); +	fman = fence->fman; + +	arg->signaled = vmw_fence_obj_signaled(fence, arg->flags); +	spin_lock_irq(&fman->lock); + +	arg->signaled_flags = fence->signaled; +	arg->passed_seqno = dev_priv->last_read_seqno; +	spin_unlock_irq(&fman->lock); + +	ttm_base_object_unref(&base); + +	return 0; +} + + +int vmw_fence_obj_unref_ioctl(struct drm_device *dev, void *data, +			      struct drm_file *file_priv) +{ +	struct drm_vmw_fence_arg *arg = +		(struct drm_vmw_fence_arg *) data; + +	return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, +					 arg->handle, +					 TTM_REF_USAGE); +} + +/** + * vmw_event_fence_fpriv_gone - Remove references to struct drm_file objects + * + * @fman: Pointer to a struct vmw_fence_manager + * @event_list: Pointer to linked list of struct vmw_event_fence_action objects + * with pointers to a struct drm_file object about to be closed. + * + * This function removes all pending fence events with references to a + * specific struct drm_file object about to be closed. The caller is required + * to pass a list of all struct vmw_event_fence_action objects with such + * events attached. This function is typically called before the + * struct drm_file object's event management is taken down. + */ +void vmw_event_fence_fpriv_gone(struct vmw_fence_manager *fman, +				struct list_head *event_list) +{ +	struct vmw_event_fence_action *eaction; +	struct drm_pending_event *event; +	unsigned long irq_flags; + +	while (1) { +		spin_lock_irqsave(&fman->lock, irq_flags); +		if (list_empty(event_list)) +			goto out_unlock; +		eaction = list_first_entry(event_list, +					   struct vmw_event_fence_action, +					   fpriv_head); +		list_del_init(&eaction->fpriv_head); +		event = eaction->event; +		eaction->event = NULL; +		spin_unlock_irqrestore(&fman->lock, irq_flags); +		event->destroy(event); +	}  out_unlock: -	spin_unlock(&queue->lock); +	spin_unlock_irqrestore(&fman->lock, irq_flags); +} + + +/** + * vmw_event_fence_action_seq_passed + * + * @action: The struct vmw_fence_action embedded in a struct + * vmw_event_fence_action. + * + * This function is called when the seqno of the fence where @action is + * attached has passed. It queues the event on the submitter's event list. + * This function is always called from atomic context, and may be called + * from irq context. + */ +static void vmw_event_fence_action_seq_passed(struct vmw_fence_action *action) +{ +	struct vmw_event_fence_action *eaction = +		container_of(action, struct vmw_event_fence_action, action); +	struct drm_device *dev = eaction->dev; +	struct drm_pending_event *event = eaction->event; +	struct drm_file *file_priv; +	unsigned long irq_flags; + +	if (unlikely(event == NULL)) +		return; + +	file_priv = event->file_priv; +	spin_lock_irqsave(&dev->event_lock, irq_flags); + +	if (likely(eaction->tv_sec != NULL)) { +		struct timeval tv; + +		do_gettimeofday(&tv); +		*eaction->tv_sec = tv.tv_sec; +		*eaction->tv_usec = tv.tv_usec; +	} + +	list_del_init(&eaction->fpriv_head); +	list_add_tail(&eaction->event->link, &file_priv->event_list); +	eaction->event = NULL; +	wake_up_all(&file_priv->event_wait); +	spin_unlock_irqrestore(&dev->event_lock, irq_flags); +} + +/** + * vmw_event_fence_action_cleanup + * + * @action: The struct vmw_fence_action embedded in a struct + * vmw_event_fence_action. + * + * This function is the struct vmw_fence_action destructor. It's typically + * called from a workqueue. + */ +static void vmw_event_fence_action_cleanup(struct vmw_fence_action *action) +{ +	struct vmw_event_fence_action *eaction = +		container_of(action, struct vmw_event_fence_action, action); +	struct vmw_fence_manager *fman = eaction->fence->fman; +	unsigned long irq_flags; -	return (updated) ? 0 : -EBUSY; +	spin_lock_irqsave(&fman->lock, irq_flags); +	list_del(&eaction->fpriv_head); +	spin_unlock_irqrestore(&fman->lock, irq_flags); + +	vmw_fence_obj_unreference(&eaction->fence); +	kfree(eaction);  } -static struct timespec vmw_timespec_add(struct timespec t1, -					struct timespec t2) + +/** + * vmw_fence_obj_add_action - Add an action to a fence object. + * + * @fence - The fence object. + * @action - The action to add. + * + * Note that the action callbacks may be executed before this function + * returns. + */ +static void vmw_fence_obj_add_action(struct vmw_fence_obj *fence, +			      struct vmw_fence_action *action)  { -	t1.tv_sec += t2.tv_sec; -	t1.tv_nsec += t2.tv_nsec; -	if (t1.tv_nsec >= 1000000000L) { -		t1.tv_sec += 1; -		t1.tv_nsec -= 1000000000L; +	struct vmw_fence_manager *fman = fence->fman; +	unsigned long irq_flags; +	bool run_update = false; + +	mutex_lock(&fman->goal_irq_mutex); +	spin_lock_irqsave(&fman->lock, irq_flags); + +	fman->pending_actions[action->type]++; +	if (fence->signaled & DRM_VMW_FENCE_FLAG_EXEC) { +		struct list_head action_list; + +		INIT_LIST_HEAD(&action_list); +		list_add_tail(&action->head, &action_list); +		vmw_fences_perform_actions(fman, &action_list); +	} else { +		list_add_tail(&action->head, &fence->seq_passed_actions); + +		/* +		 * This function may set fman::seqno_valid, so it must +		 * be run with the goal_irq_mutex held. +		 */ +		run_update = vmw_fence_goal_check_locked(fence); +	} + +	spin_unlock_irqrestore(&fman->lock, irq_flags); + +	if (run_update) { +		if (!fman->goal_irq_on) { +			fman->goal_irq_on = true; +			vmw_goal_waiter_add(fman->dev_priv); +		} +		vmw_fences_update(fman);  	} +	mutex_unlock(&fman->goal_irq_mutex); -	return t1;  } -static struct timespec vmw_fifo_lag(struct vmw_fence_queue *queue) +/** + * vmw_event_fence_action_create - Post an event for sending when a fence + * object seqno has passed. + * + * @file_priv: The file connection on which the event should be posted. + * @fence: The fence object on which to post the event. + * @event: Event to be posted. This event should've been alloced + * using k[mz]alloc, and should've been completely initialized. + * @interruptible: Interruptible waits if possible. + * + * As a side effect, the object pointed to by @event may have been + * freed when this function returns. If this function returns with + * an error code, the caller needs to free that object. + */ + +int vmw_event_fence_action_queue(struct drm_file *file_priv, +				 struct vmw_fence_obj *fence, +				 struct drm_pending_event *event, +				 uint32_t *tv_sec, +				 uint32_t *tv_usec, +				 bool interruptible)  { -	struct timespec now; +	struct vmw_event_fence_action *eaction; +	struct vmw_fence_manager *fman = fence->fman; +	struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); +	unsigned long irq_flags; + +	eaction = kzalloc(sizeof(*eaction), GFP_KERNEL); +	if (unlikely(eaction == NULL)) +		return -ENOMEM; + +	eaction->event = event; + +	eaction->action.seq_passed = vmw_event_fence_action_seq_passed; +	eaction->action.cleanup = vmw_event_fence_action_cleanup; +	eaction->action.type = VMW_ACTION_EVENT; -	spin_lock(&queue->lock); -	getrawmonotonic(&now); -	queue->lag = vmw_timespec_add(queue->lag, -				      timespec_sub(now, queue->lag_time)); -	queue->lag_time = now; -	spin_unlock(&queue->lock); -	return queue->lag; +	eaction->fence = vmw_fence_obj_reference(fence); +	eaction->dev = fman->dev_priv->dev; +	eaction->tv_sec = tv_sec; +	eaction->tv_usec = tv_usec; + +	spin_lock_irqsave(&fman->lock, irq_flags); +	list_add_tail(&eaction->fpriv_head, &vmw_fp->fence_events); +	spin_unlock_irqrestore(&fman->lock, irq_flags); + +	vmw_fence_obj_add_action(fence, &eaction->action); + +	return 0;  } +struct vmw_event_fence_pending { +	struct drm_pending_event base; +	struct drm_vmw_event_fence event; +}; -static bool vmw_lag_lt(struct vmw_fence_queue *queue, -		       uint32_t us) +static int vmw_event_fence_action_create(struct drm_file *file_priv, +				  struct vmw_fence_obj *fence, +				  uint32_t flags, +				  uint64_t user_data, +				  bool interruptible)  { -	struct timespec lag, cond; +	struct vmw_event_fence_pending *event; +	struct drm_device *dev = fence->fman->dev_priv->dev; +	unsigned long irq_flags; +	int ret; + +	spin_lock_irqsave(&dev->event_lock, irq_flags); + +	ret = (file_priv->event_space < sizeof(event->event)) ? -EBUSY : 0; +	if (likely(ret == 0)) +		file_priv->event_space -= sizeof(event->event); + +	spin_unlock_irqrestore(&dev->event_lock, irq_flags); + +	if (unlikely(ret != 0)) { +		DRM_ERROR("Failed to allocate event space for this file.\n"); +		goto out_no_space; +	} + -	cond = ns_to_timespec((s64) us * 1000); -	lag = vmw_fifo_lag(queue); -	return (timespec_compare(&lag, &cond) < 1); +	event = kzalloc(sizeof(*event), GFP_KERNEL); +	if (unlikely(event == NULL)) { +		DRM_ERROR("Failed to allocate an event.\n"); +		ret = -ENOMEM; +		goto out_no_event; +	} + +	event->event.base.type = DRM_VMW_EVENT_FENCE_SIGNALED; +	event->event.base.length = sizeof(*event); +	event->event.user_data = user_data; + +	event->base.event = &event->event.base; +	event->base.file_priv = file_priv; +	event->base.destroy = (void (*) (struct drm_pending_event *)) kfree; + + +	if (flags & DRM_VMW_FE_FLAG_REQ_TIME) +		ret = vmw_event_fence_action_queue(file_priv, fence, +						   &event->base, +						   &event->event.tv_sec, +						   &event->event.tv_usec, +						   interruptible); +	else +		ret = vmw_event_fence_action_queue(file_priv, fence, +						   &event->base, +						   NULL, +						   NULL, +						   interruptible); +	if (ret != 0) +		goto out_no_queue; + +out_no_queue: +	event->base.destroy(&event->base); +out_no_event: +	spin_lock_irqsave(&dev->event_lock, irq_flags); +	file_priv->event_space += sizeof(*event); +	spin_unlock_irqrestore(&dev->event_lock, irq_flags); +out_no_space: +	return ret;  } -int vmw_wait_lag(struct vmw_private *dev_priv, -		 struct vmw_fence_queue *queue, uint32_t us) +int vmw_fence_event_ioctl(struct drm_device *dev, void *data, +			  struct drm_file *file_priv)  { -	struct vmw_fence *fence; -	uint32_t sequence; +	struct vmw_private *dev_priv = vmw_priv(dev); +	struct drm_vmw_fence_event_arg *arg = +		(struct drm_vmw_fence_event_arg *) data; +	struct vmw_fence_obj *fence = NULL; +	struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); +	struct drm_vmw_fence_rep __user *user_fence_rep = +		(struct drm_vmw_fence_rep __user *)(unsigned long) +		arg->fence_rep; +	uint32_t handle;  	int ret; -	while (!vmw_lag_lt(queue, us)) { -		spin_lock(&queue->lock); -		if (list_empty(&queue->head)) -			sequence = atomic_read(&dev_priv->fence_seq); -		else { -			fence = list_first_entry(&queue->head, -						 struct vmw_fence, head); -			sequence = fence->sequence; +	/* +	 * Look up an existing fence object, +	 * and if user-space wants a new reference, +	 * add one. +	 */ +	if (arg->handle) { +		struct ttm_base_object *base = +			ttm_base_object_lookup_for_ref(dev_priv->tdev, +						       arg->handle); + +		if (unlikely(base == NULL)) { +			DRM_ERROR("Fence event invalid fence object handle " +				  "0x%08lx.\n", +				  (unsigned long)arg->handle); +			return -EINVAL;  		} -		spin_unlock(&queue->lock); +		fence = &(container_of(base, struct vmw_user_fence, +				       base)->fence); +		(void) vmw_fence_obj_reference(fence); -		ret = vmw_wait_fence(dev_priv, false, sequence, true, -				     3*HZ); +		if (user_fence_rep != NULL) { +			bool existed; -		if (unlikely(ret != 0)) +			ret = ttm_ref_object_add(vmw_fp->tfile, base, +						 TTM_REF_USAGE, &existed); +			if (unlikely(ret != 0)) { +				DRM_ERROR("Failed to reference a fence " +					  "object.\n"); +				goto out_no_ref_obj; +			} +			handle = base->hash.key; +		} +		ttm_base_object_unref(&base); +	} + +	/* +	 * Create a new fence object. +	 */ +	if (!fence) { +		ret = vmw_execbuf_fence_commands(file_priv, dev_priv, +						 &fence, +						 (user_fence_rep) ? +						 &handle : NULL); +		if (unlikely(ret != 0)) { +			DRM_ERROR("Fence event failed to create fence.\n");  			return ret; +		} +	} + +	BUG_ON(fence == NULL); -		(void) vmw_fence_pull(queue, sequence); +	if (arg->flags & DRM_VMW_FE_FLAG_REQ_TIME) +		ret = vmw_event_fence_action_create(file_priv, fence, +						    arg->flags, +						    arg->user_data, +						    true); +	else +		ret = vmw_event_fence_action_create(file_priv, fence, +						    arg->flags, +						    arg->user_data, +						    true); + +	if (unlikely(ret != 0)) { +		if (ret != -ERESTARTSYS) +			DRM_ERROR("Failed to attach event to fence.\n"); +		goto out_no_create;  	} + +	vmw_execbuf_copy_fence_user(dev_priv, vmw_fp, 0, user_fence_rep, fence, +				    handle); +	vmw_fence_obj_unreference(&fence);  	return 0; +out_no_create: +	if (user_fence_rep != NULL) +		ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, +					  handle, TTM_REF_USAGE); +out_no_ref_obj: +	vmw_fence_obj_unreference(&fence); +	return ret;  } - - diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h new file mode 100644 index 00000000000..faf2e787386 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h @@ -0,0 +1,120 @@ +/************************************************************************** + * + * Copyright © 2011 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_FENCE_H_ + +#define VMW_FENCE_WAIT_TIMEOUT (5*HZ) + +struct vmw_private; + +struct vmw_fence_manager; + +/** + * + * + */ +enum vmw_action_type { +	VMW_ACTION_EVENT = 0, +	VMW_ACTION_MAX +}; + +struct vmw_fence_action { +	struct list_head head; +	enum vmw_action_type type; +	void (*seq_passed) (struct vmw_fence_action *action); +	void (*cleanup) (struct vmw_fence_action *action); +}; + +struct vmw_fence_obj { +	struct kref kref; +	u32 seqno; + +	struct vmw_fence_manager *fman; +	struct list_head head; +	uint32_t signaled; +	uint32_t signal_mask; +	struct list_head seq_passed_actions; +	void (*destroy)(struct vmw_fence_obj *fence); +	wait_queue_head_t queue; +}; + +extern struct vmw_fence_manager * +vmw_fence_manager_init(struct vmw_private *dev_priv); + +extern void vmw_fence_manager_takedown(struct vmw_fence_manager *fman); + +extern void vmw_fence_obj_unreference(struct vmw_fence_obj **fence_p); + +extern struct vmw_fence_obj * +vmw_fence_obj_reference(struct vmw_fence_obj *fence); + +extern void vmw_fences_update(struct vmw_fence_manager *fman); + +extern bool vmw_fence_obj_signaled(struct vmw_fence_obj *fence, +				   uint32_t flags); + +extern int vmw_fence_obj_wait(struct vmw_fence_obj *fence, uint32_t flags, +			      bool lazy, +			      bool interruptible, unsigned long timeout); + +extern void vmw_fence_obj_flush(struct vmw_fence_obj *fence); + +extern int vmw_fence_create(struct vmw_fence_manager *fman, +			    uint32_t seqno, +			    uint32_t mask, +			    struct vmw_fence_obj **p_fence); + +extern int vmw_user_fence_create(struct drm_file *file_priv, +				 struct vmw_fence_manager *fman, +				 uint32_t sequence, +				 uint32_t mask, +				 struct vmw_fence_obj **p_fence, +				 uint32_t *p_handle); + +extern void vmw_fence_fifo_up(struct vmw_fence_manager *fman); + +extern void vmw_fence_fifo_down(struct vmw_fence_manager *fman); + +extern int vmw_fence_obj_wait_ioctl(struct drm_device *dev, void *data, +				    struct drm_file *file_priv); + +extern int vmw_fence_obj_signaled_ioctl(struct drm_device *dev, void *data, +					struct drm_file *file_priv); + +extern int vmw_fence_obj_unref_ioctl(struct drm_device *dev, void *data, +				     struct drm_file *file_priv); +extern int vmw_fence_event_ioctl(struct drm_device *dev, void *data, +				 struct drm_file *file_priv); +extern void vmw_event_fence_fpriv_gone(struct vmw_fence_manager *fman, +				       struct list_head *event_list); +extern int vmw_event_fence_action_queue(struct drm_file *filee_priv, +					struct vmw_fence_obj *fence, +					struct drm_pending_event *event, +					uint32_t *tv_sec, +					uint32_t *tv_usec, +					bool interruptible); +#endif /* _VMWGFX_FENCE_H_ */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c index 635c0ffee7f..6ccd993e26b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c @@ -26,13 +26,31 @@   **************************************************************************/  #include "vmwgfx_drv.h" -#include "drmP.h" -#include "ttm/ttm_placement.h" +#include <drm/drmP.h> +#include <drm/ttm/ttm_placement.h>  bool vmw_fifo_have_3d(struct vmw_private *dev_priv)  {  	__le32 __iomem *fifo_mem = dev_priv->mmio_virt;  	uint32_t fifo_min, hwversion; +	const struct vmw_fifo_state *fifo = &dev_priv->fifo; + +	if (!(dev_priv->capabilities & SVGA_CAP_3D)) +		return false; + +	if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS) { +		uint32_t result; + +		if (!dev_priv->has_mob) +			return false; + +		mutex_lock(&dev_priv->hw_mutex); +		vmw_write(dev_priv, SVGA_REG_DEV_CAP, SVGA3D_DEVCAP_3D); +		result = vmw_read(dev_priv, SVGA_REG_DEV_CAP); +		mutex_unlock(&dev_priv->hw_mutex); + +		return (result != 0); +	}  	if (!(dev_priv->capabilities & SVGA_CAP_EXTENDED_FIFO))  		return false; @@ -41,11 +59,20 @@ bool vmw_fifo_have_3d(struct vmw_private *dev_priv)  	if (fifo_min <= SVGA_FIFO_3D_HWVERSION * sizeof(unsigned int))  		return false; -	hwversion = ioread32(fifo_mem + SVGA_FIFO_3D_HWVERSION); +	hwversion = ioread32(fifo_mem + +			     ((fifo->capabilities & +			       SVGA_FIFO_CAP_3D_HWVERSION_REVISED) ? +			      SVGA_FIFO_3D_HWVERSION_REVISED : +			      SVGA_FIFO_3D_HWVERSION)); +  	if (hwversion == 0)  		return false; -	if (hwversion < SVGA3D_HWVERSION_WS65_B1) +	if (hwversion < SVGA3D_HWVERSION_WS8_B1) +		return false; + +	/* Non-Screen Object path does not support surfaces */ +	if (!dev_priv->sou_priv)  		return false;  	return true; @@ -72,22 +99,12 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)  	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; @@ -137,14 +154,10 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)  		 (unsigned int) min,  		 (unsigned int) fifo->capabilities); -	atomic_set(&dev_priv->fence_seq, dev_priv->last_read_sequence); -	iowrite32(dev_priv->last_read_sequence, fifo_mem + SVGA_FIFO_FENCE); -	vmw_fence_queue_init(&fifo->fence_queue); +	atomic_set(&dev_priv->marker_seq, dev_priv->last_read_seqno); +	iowrite32(dev_priv->last_read_seqno, fifo_mem + SVGA_FIFO_FENCE); +	vmw_marker_queue_init(&fifo->marker_queue);  	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) @@ -170,7 +183,7 @@ void vmw_fifo_release(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)  	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); +	dev_priv->last_read_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE);  	vmw_write(dev_priv, SVGA_REG_CONFIG_DONE,  		  dev_priv->config_done_state); @@ -180,12 +193,7 @@ void vmw_fifo_release(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)  		  dev_priv->traces_state);  	mutex_unlock(&dev_priv->hw_mutex); -	vmw_fence_queue_takedown(&fifo->fence_queue); - -	if (likely(fifo->last_buffer != NULL)) { -		vfree(fifo->last_buffer); -		fifo->last_buffer = NULL; -	} +	vmw_marker_queue_takedown(&fifo->marker_queue);  	if (likely(fifo->static_buffer != NULL)) {  		vfree(fifo->static_buffer); @@ -262,9 +270,8 @@ static int vmw_fifo_wait(struct vmw_private *dev_priv,  		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); +		dev_priv->irq_mask |= SVGA_IRQFLAG_FIFO_PROGRESS; +		vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask);  		spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);  	}  	mutex_unlock(&dev_priv->hw_mutex); @@ -286,9 +293,8 @@ static int vmw_fifo_wait(struct vmw_private *dev_priv,  	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); +		dev_priv->irq_mask &= ~SVGA_IRQFLAG_FIFO_PROGRESS; +		vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask);  		spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);  	}  	mutex_unlock(&dev_priv->hw_mutex); @@ -296,6 +302,16 @@ static int vmw_fifo_wait(struct vmw_private *dev_priv,  	return ret;  } +/** + * Reserve @bytes number of bytes in the fifo. + * + * This function will return NULL (error) on two conditions: + *  If it timeouts waiting for fifo space, or if @bytes is larger than the + *   available fifo space. + * + * Returns: + *   Pointer to the fifo, or null on error (possible hardware hang). + */  void *vmw_fifo_reserve(struct vmw_private *dev_priv, uint32_t bytes)  {  	struct vmw_fifo_state *fifo_state = &dev_priv->fifo; @@ -466,7 +482,7 @@ void vmw_fifo_commit(struct vmw_private *dev_priv, uint32_t bytes)  	mutex_unlock(&fifo_state->fifo_mutex);  } -int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *sequence) +int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *seqno)  {  	struct vmw_fifo_state *fifo_state = &dev_priv->fifo;  	struct svga_fifo_cmd_fence *cmd_fence; @@ -476,16 +492,16 @@ int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *sequence)  	fm = vmw_fifo_reserve(dev_priv, bytes);  	if (unlikely(fm == NULL)) { -		*sequence = atomic_read(&dev_priv->fence_seq); +		*seqno = atomic_read(&dev_priv->marker_seq);  		ret = -ENOMEM; -		(void)vmw_fallback_wait(dev_priv, false, true, *sequence, +		(void)vmw_fallback_wait(dev_priv, false, true, *seqno,  					false, 3*HZ);  		goto out_err;  	}  	do { -		*sequence = atomic_add_return(1, &dev_priv->fence_seq); -	} while (*sequence == 0); +		*seqno = atomic_add_return(1, &dev_priv->marker_seq); +	} while (*seqno == 0);  	if (!(fifo_state->capabilities & SVGA_FIFO_CAP_FENCE)) { @@ -502,61 +518,132 @@ int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *sequence)  	cmd_fence = (struct svga_fifo_cmd_fence *)  	    ((unsigned long)fm + sizeof(__le32)); -	iowrite32(*sequence, &cmd_fence->fence); -	fifo_state->last_buffer_add = true; +	iowrite32(*seqno, &cmd_fence->fence);  	vmw_fifo_commit(dev_priv, bytes); -	fifo_state->last_buffer_add = false; -	(void) vmw_fence_push(&fifo_state->fence_queue, *sequence); -	vmw_update_sequence(dev_priv, fifo_state); +	(void) vmw_marker_push(&fifo_state->marker_queue, *seqno); +	vmw_update_seqno(dev_priv, fifo_state);  out_err:  	return ret;  }  /** - * Map the first page of the FIFO read-only to user-space. + * vmw_fifo_emit_dummy_legacy_query - emits a dummy query to the fifo using + * legacy query commands. + * + * @dev_priv: The device private structure. + * @cid: The hardware context id used for the query. + * + * See the vmw_fifo_emit_dummy_query documentation.   */ +static int vmw_fifo_emit_dummy_legacy_query(struct vmw_private *dev_priv, +					    uint32_t cid) +{ +	/* +	 * A query wait without a preceding query end will +	 * actually finish all queries for this cid +	 * without writing to the query result structure. +	 */ + +	struct ttm_buffer_object *bo = dev_priv->dummy_query_bo; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdWaitForQuery body; +	} *cmd; + +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); -static int vmw_fifo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Out of fifo space for dummy query.\n"); +		return -ENOMEM; +	} + +	cmd->header.id = SVGA_3D_CMD_WAIT_FOR_QUERY; +	cmd->header.size = sizeof(cmd->body); +	cmd->body.cid = cid; +	cmd->body.type = SVGA3D_QUERYTYPE_OCCLUSION; + +	if (bo->mem.mem_type == TTM_PL_VRAM) { +		cmd->body.guestResult.gmrId = SVGA_GMR_FRAMEBUFFER; +		cmd->body.guestResult.offset = bo->offset; +	} else { +		cmd->body.guestResult.gmrId = bo->mem.start; +		cmd->body.guestResult.offset = 0; +	} + +	vmw_fifo_commit(dev_priv, sizeof(*cmd)); + +	return 0; +} + +/** + * vmw_fifo_emit_dummy_gb_query - emits a dummy query to the fifo using + * guest-backed resource query commands. + * + * @dev_priv: The device private structure. + * @cid: The hardware context id used for the query. + * + * See the vmw_fifo_emit_dummy_query documentation. + */ +static int vmw_fifo_emit_dummy_gb_query(struct vmw_private *dev_priv, +					uint32_t cid)  { -	int ret; -	unsigned long address = (unsigned long)vmf->virtual_address; +	/* +	 * A query wait without a preceding query end will +	 * actually finish all queries for this cid +	 * without writing to the query result structure. +	 */ + +	struct ttm_buffer_object *bo = dev_priv->dummy_query_bo; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdWaitForGBQuery body; +	} *cmd; + +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); + +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Out of fifo space for dummy query.\n"); +		return -ENOMEM; +	} -	if (address != vma->vm_start) -		return VM_FAULT_SIGBUS; +	cmd->header.id = SVGA_3D_CMD_WAIT_FOR_GB_QUERY; +	cmd->header.size = sizeof(cmd->body); +	cmd->body.cid = cid; +	cmd->body.type = SVGA3D_QUERYTYPE_OCCLUSION; +	BUG_ON(bo->mem.mem_type != VMW_PL_MOB); +	cmd->body.mobid = bo->mem.start; +	cmd->body.offset = 0; -	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; +	vmw_fifo_commit(dev_priv, sizeof(*cmd)); -	return VM_FAULT_SIGBUS; +	return 0;  } -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) +/** + * vmw_fifo_emit_dummy_gb_query - emits a dummy query to the fifo using + * appropriate resource query commands. + * + * @dev_priv: The device private structure. + * @cid: The hardware context id used for the query. + * + * This function is used to emit a dummy occlusion query with + * no primitives rendered between query begin and query end. + * It's used to provide a query barrier, in order to know that when + * this query is finished, all preceding queries are also finished. + * + * A Query results structure should have been initialized at the start + * of the dev_priv->dummy_query_bo buffer object. And that buffer object + * must also be either reserved or pinned when this function is called. + * + * Returns -ENOMEM on failure to reserve fifo space. + */ +int vmw_fifo_emit_dummy_query(struct vmw_private *dev_priv, +			      uint32_t cid)  { -	struct drm_file *file_priv; -	struct vmw_private *dev_priv; - -	file_priv = 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; +	if (dev_priv->has_mob) +		return vmw_fifo_emit_dummy_gb_query(dev_priv, cid); + +	return vmw_fifo_emit_dummy_legacy_query(dev_priv, cid);  } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c index de0c5948521..61d8d803199 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c @@ -1,6 +1,6 @@  /**************************************************************************   * - * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA + * Copyright © 2009-2011 VMware, Inc., Palo Alto, CA., USA   * All Rights Reserved.   *   * Permission is hereby granted, free of charge, to any person obtaining a @@ -26,176 +26,126 @@   **************************************************************************/  #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) +#include <drm/drmP.h> +#include <drm/ttm/ttm_bo_driver.h> + +#define VMW_PPN_SIZE (sizeof(unsigned long)) +/* A future safe maximum remap size. */ +#define VMW_PPN_PER_REMAP ((31 * 1024) / VMW_PPN_SIZE) +#define DMA_ADDR_INVALID ((dma_addr_t) 0) +#define DMA_PAGE_INVALID 0UL + +static int vmw_gmr2_bind(struct vmw_private *dev_priv, +			 struct vmw_piter *iter, +			 unsigned long num_pages, +			 int gmr_id)  { -	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; +	SVGAFifoCmdDefineGMR2 define_cmd; +	SVGAFifoCmdRemapGMR2 remap_cmd; +	uint32_t *cmd; +	uint32_t *cmd_orig; +	uint32_t define_size = sizeof(define_cmd) + sizeof(*cmd); +	uint32_t remap_num = num_pages / VMW_PPN_PER_REMAP + ((num_pages % VMW_PPN_PER_REMAP) > 0); +	uint32_t remap_size = VMW_PPN_SIZE * num_pages + (sizeof(remap_cmd) + sizeof(*cmd)) * remap_num; +	uint32_t remap_pos = 0; +	uint32_t cmd_size = define_size + remap_size; +	uint32_t i; + +	cmd_orig = cmd = vmw_fifo_reserve(dev_priv, cmd_size); +	if (unlikely(cmd == NULL)) +		return -ENOMEM; + +	define_cmd.gmrId = gmr_id; +	define_cmd.numPages = num_pages; + +	*cmd++ = SVGA_CMD_DEFINE_GMR2; +	memcpy(cmd, &define_cmd, sizeof(define_cmd)); +	cmd += sizeof(define_cmd) / sizeof(*cmd); + +	/* +	 * Need to split the command if there are too many +	 * pages that goes into the gmr. +	 */ + +	remap_cmd.gmrId = gmr_id; +	remap_cmd.flags = (VMW_PPN_SIZE > sizeof(*cmd)) ? +		SVGA_REMAP_GMR2_PPN64 : SVGA_REMAP_GMR2_PPN32; + +	while (num_pages > 0) { +		unsigned long nr = min(num_pages, (unsigned long)VMW_PPN_PER_REMAP); + +		remap_cmd.offsetPages = remap_pos; +		remap_cmd.numPages = nr; + +		*cmd++ = SVGA_CMD_REMAP_GMR2; +		memcpy(cmd, &remap_cmd, sizeof(remap_cmd)); +		cmd += sizeof(remap_cmd) / sizeof(*cmd); + +		for (i = 0; i < nr; ++i) { +			if (VMW_PPN_SIZE <= 4) +				*cmd = vmw_piter_dma_addr(iter) >> PAGE_SHIFT; +			else +				*((uint64_t *)cmd) = vmw_piter_dma_addr(iter) >> +					PAGE_SHIFT; + +			cmd += VMW_PPN_SIZE / sizeof(*cmd); +			vmw_piter_next(iter);  		} -		(++desc_virtual)->ppn = cpu_to_le32(0); -		desc_virtual->num_pages = cpu_to_le32(0); +		num_pages -= nr; +		remap_pos += nr;  	} -	if (likely(page_virtual != NULL)) -		kunmap_atomic(page_virtual, KM_USER0); +	BUG_ON(cmd != cmd_orig + cmd_size / sizeof(*cmd)); -	return 0; -out_err: -	list_for_each_entry_safe(page, next, desc_pages, lru) { -		list_del_init(&page->lru); -		__free_page(page); -	} -	return ret; -} +	vmw_fifo_commit(dev_priv, cmd_size); -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); -	} +	return 0;  } -static void vmw_gmr_fire_descriptors(struct vmw_private *dev_priv, -				     int gmr_id, struct list_head *desc_pages) +static void vmw_gmr2_unbind(struct vmw_private *dev_priv, +			    int gmr_id)  { -	struct page *page; +	SVGAFifoCmdDefineGMR2 define_cmd; +	uint32_t define_size = sizeof(define_cmd) + 4; +	uint32_t *cmd; -	if (unlikely(list_empty(desc_pages))) +	cmd = vmw_fifo_reserve(dev_priv, define_size); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("GMR2 unbind failed.\n");  		return; +	} +	define_cmd.gmrId = gmr_id; +	define_cmd.numPages = 0; -	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); +	*cmd++ = SVGA_CMD_DEFINE_GMR2; +	memcpy(cmd, &define_cmd, sizeof(define_cmd)); +	vmw_fifo_commit(dev_priv, define_size);  } -/** - * 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 page *pages[], +		 const struct vmw_sg_table *vsgt,  		 unsigned long num_pages,  		 int gmr_id)  { -	struct list_head desc_pages; -	int ret; - -	if (unlikely(!(dev_priv->capabilities & SVGA_CAP_GMR))) -		return -EINVAL; +	struct vmw_piter data_iter; -	if (vmw_gmr_count_descriptors(pages, num_pages) > -	    dev_priv->max_gmr_descriptors) -		return -EINVAL; +	vmw_piter_start(&data_iter, vsgt, 0); -	INIT_LIST_HEAD(&desc_pages); +	if (unlikely(!vmw_piter_next(&data_iter))) +		return 0; -	ret = vmw_gmr_build_descriptors(&desc_pages, pages, num_pages); -	if (unlikely(ret != 0)) -		return ret; - -	vmw_gmr_fire_descriptors(dev_priv, gmr_id, &desc_pages); -	vmw_gmr_free_descriptors(&desc_pages); +	if (unlikely(!(dev_priv->capabilities & SVGA_CAP_GMR2))) +		return -EINVAL; -	return 0; +	return vmw_gmr2_bind(dev_priv, &data_iter, num_pages, gmr_id);  }  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); +	if (likely(dev_priv->capabilities & SVGA_CAP_GMR2)) +		vmw_gmr2_unbind(dev_priv, gmr_id);  } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c index ac6e0d1bd62..b1273e8e9a6 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c @@ -29,9 +29,9 @@   */  #include "vmwgfx_drv.h" -#include "ttm/ttm_module.h" -#include "ttm/ttm_bo_driver.h" -#include "ttm/ttm_placement.h" +#include <drm/ttm/ttm_module.h> +#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_placement.h>  #include <linux/idr.h>  #include <linux/spinlock.h>  #include <linux/kernel.h> @@ -40,6 +40,8 @@ struct vmwgfx_gmrid_man {  	spinlock_t lock;  	struct ida gmr_ida;  	uint32_t max_gmr_ids; +	uint32_t max_gmr_pages; +	uint32_t used_gmr_pages;  };  static int vmw_gmrid_man_get_node(struct ttm_mem_type_manager *man, @@ -49,33 +51,50 @@ static int vmw_gmrid_man_get_node(struct ttm_mem_type_manager *man,  {  	struct vmwgfx_gmrid_man *gman =  		(struct vmwgfx_gmrid_man *)man->priv; -	int ret; +	int ret = 0;  	int id;  	mem->mm_node = NULL; -	do { -		if (unlikely(ida_pre_get(&gman->gmr_ida, GFP_KERNEL) == 0)) -			return -ENOMEM; +	spin_lock(&gman->lock); + +	if (gman->max_gmr_pages > 0) { +		gman->used_gmr_pages += bo->num_pages; +		if (unlikely(gman->used_gmr_pages > gman->max_gmr_pages)) +			goto out_err_locked; +	} +	do { +		spin_unlock(&gman->lock); +		if (unlikely(ida_pre_get(&gman->gmr_ida, GFP_KERNEL) == 0)) { +			ret = -ENOMEM; +			goto out_err; +		}  		spin_lock(&gman->lock); -		ret = ida_get_new(&gman->gmr_ida, &id); +		ret = ida_get_new(&gman->gmr_ida, &id);  		if (unlikely(ret == 0 && id >= gman->max_gmr_ids)) {  			ida_remove(&gman->gmr_ida, id); -			spin_unlock(&gman->lock); -			return 0; +			ret = 0; +			goto out_err_locked;  		} - -		spin_unlock(&gman->lock); -  	} while (ret == -EAGAIN);  	if (likely(ret == 0)) {  		mem->mm_node = gman;  		mem->start = id; -	} +		mem->num_pages = bo->num_pages; +	} else +		goto out_err_locked; +	spin_unlock(&gman->lock); +	return 0; + +out_err: +	spin_lock(&gman->lock); +out_err_locked: +	gman->used_gmr_pages -= bo->num_pages; +	spin_unlock(&gman->lock);  	return ret;  } @@ -88,6 +107,7 @@ static void vmw_gmrid_man_put_node(struct ttm_mem_type_manager *man,  	if (mem->mm_node) {  		spin_lock(&gman->lock);  		ida_remove(&gman->gmr_ida, mem->start); +		gman->used_gmr_pages -= mem->num_pages;  		spin_unlock(&gman->lock);  		mem->mm_node = NULL;  	} @@ -96,6 +116,8 @@ static void vmw_gmrid_man_put_node(struct ttm_mem_type_manager *man,  static int vmw_gmrid_man_init(struct ttm_mem_type_manager *man,  			      unsigned long p_size)  { +	struct vmw_private *dev_priv = +		container_of(man->bdev, struct vmw_private, bdev);  	struct vmwgfx_gmrid_man *gman =  		kzalloc(sizeof(*gman), GFP_KERNEL); @@ -103,8 +125,21 @@ static int vmw_gmrid_man_init(struct ttm_mem_type_manager *man,  		return -ENOMEM;  	spin_lock_init(&gman->lock); +	gman->used_gmr_pages = 0;  	ida_init(&gman->gmr_ida); -	gman->max_gmr_ids = p_size; + +	switch (p_size) { +	case VMW_PL_GMR: +		gman->max_gmr_ids = dev_priv->max_gmr_ids; +		gman->max_gmr_pages = dev_priv->max_gmr_pages; +		break; +	case VMW_PL_MOB: +		gman->max_gmr_ids = VMWGFX_NUM_MOB; +		gman->max_gmr_pages = dev_priv->max_mob_pages; +		break; +	default: +		BUG(); +	}  	man->priv = (void *) gman;  	return 0;  } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index 570d57775a5..37881ecf5d7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -26,7 +26,13 @@   **************************************************************************/  #include "vmwgfx_drv.h" -#include "vmwgfx_drm.h" +#include <drm/vmwgfx_drm.h> +#include "vmwgfx_kms.h" + +struct svga_3d_compat_cap { +	SVGA3dCapsRecordHeader header; +	SVGA3dCapPair pairs[SVGA3D_DEVCAP_MAX]; +};  int vmw_getparam_ioctl(struct drm_device *dev, void *data,  		       struct drm_file *file_priv) @@ -34,6 +40,7 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data,  	struct vmw_private *dev_priv = vmw_priv(dev);  	struct drm_vmw_getparam_arg *param =  	    (struct drm_vmw_getparam_arg *)data; +	struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);  	switch (param->param) {  	case DRM_VMW_PARAM_NUM_STREAMS: @@ -45,9 +52,6 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data,  	case DRM_VMW_PARAM_3D:  		param->value = vmw_fifo_have_3d(dev_priv) ? 1 : 0;  		break; -	case DRM_VMW_PARAM_FIFO_OFFSET: -		param->value = dev_priv->mmio_start; -		break;  	case DRM_VMW_PARAM_HW_CAPS:  		param->value = dev_priv->capabilities;  		break; @@ -55,7 +59,51 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data,  		param->value = dev_priv->fifo.capabilities;  		break;  	case DRM_VMW_PARAM_MAX_FB_SIZE: -		param->value = dev_priv->vram_size; +		param->value = dev_priv->prim_bb_mem; +		break; +	case DRM_VMW_PARAM_FIFO_HW_VERSION: +	{ +		__le32 __iomem *fifo_mem = dev_priv->mmio_virt; +		const struct vmw_fifo_state *fifo = &dev_priv->fifo; + +		if ((dev_priv->capabilities & SVGA_CAP_GBOBJECTS)) { +			param->value = SVGA3D_HWVERSION_WS8_B1; +			break; +		} + +		param->value = +			ioread32(fifo_mem + +				 ((fifo->capabilities & +				   SVGA_FIFO_CAP_3D_HWVERSION_REVISED) ? +				  SVGA_FIFO_3D_HWVERSION_REVISED : +				  SVGA_FIFO_3D_HWVERSION)); +		break; +	} +	case DRM_VMW_PARAM_MAX_SURF_MEMORY: +		if ((dev_priv->capabilities & SVGA_CAP_GBOBJECTS) && +		    !vmw_fp->gb_aware) +			param->value = dev_priv->max_mob_pages * PAGE_SIZE / 2; +		else +			param->value = dev_priv->memory_size; +		break; +	case DRM_VMW_PARAM_3D_CAPS_SIZE: +		if ((dev_priv->capabilities & SVGA_CAP_GBOBJECTS) && +		    vmw_fp->gb_aware) +			param->value = SVGA3D_DEVCAP_MAX * sizeof(uint32_t); +		else if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS) +			param->value = sizeof(struct svga_3d_compat_cap) + +				sizeof(uint32_t); +		else +			param->value = (SVGA_FIFO_3D_CAPS_LAST - +					SVGA_FIFO_3D_CAPS + 1) * +				sizeof(uint32_t); +		break; +	case DRM_VMW_PARAM_MAX_MOB_MEMORY: +		vmw_fp->gb_aware = true; +		param->value = dev_priv->max_mob_pages * PAGE_SIZE; +		break; +	case DRM_VMW_PARAM_MAX_MOB_SIZE: +		param->value = dev_priv->max_mob_size;  		break;  	default:  		DRM_ERROR("Illegal vmwgfx get param request: %d\n", @@ -66,25 +114,309 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data,  	return 0;  } -int vmw_fifo_debug_ioctl(struct drm_device *dev, void *data, +static int vmw_fill_compat_cap(struct vmw_private *dev_priv, void *bounce, +			       size_t size) +{ +	struct svga_3d_compat_cap *compat_cap = +		(struct svga_3d_compat_cap *) bounce; +	unsigned int i; +	size_t pair_offset = offsetof(struct svga_3d_compat_cap, pairs); +	unsigned int max_size; + +	if (size < pair_offset) +		return -EINVAL; + +	max_size = (size - pair_offset) / sizeof(SVGA3dCapPair); + +	if (max_size > SVGA3D_DEVCAP_MAX) +		max_size = SVGA3D_DEVCAP_MAX; + +	compat_cap->header.length = +		(pair_offset + max_size * sizeof(SVGA3dCapPair)) / sizeof(u32); +	compat_cap->header.type = SVGA3DCAPS_RECORD_DEVCAPS; + +	mutex_lock(&dev_priv->hw_mutex); +	for (i = 0; i < max_size; ++i) { +		vmw_write(dev_priv, SVGA_REG_DEV_CAP, i); +		compat_cap->pairs[i][0] = i; +		compat_cap->pairs[i][1] = vmw_read(dev_priv, SVGA_REG_DEV_CAP); +	} +	mutex_unlock(&dev_priv->hw_mutex); + +	return 0; +} + + +int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data,  			 struct drm_file *file_priv)  { +	struct drm_vmw_get_3d_cap_arg *arg = +		(struct drm_vmw_get_3d_cap_arg *) data;  	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; +	uint32_t size; +	__le32 __iomem *fifo_mem; +	void __user *buffer = (void __user *)((unsigned long)(arg->buffer)); +	void *bounce; +	int ret; +	bool gb_objects = !!(dev_priv->capabilities & SVGA_CAP_GBOBJECTS); +	struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); -	if (unlikely(fifo_state->last_buffer == NULL)) +	if (unlikely(arg->pad64 != 0)) { +		DRM_ERROR("Illegal GET_3D_CAP argument.\n");  		return -EINVAL; +	} + +	if (gb_objects && vmw_fp->gb_aware) +		size = SVGA3D_DEVCAP_MAX * sizeof(uint32_t); +	else if (gb_objects) +		size = sizeof(struct svga_3d_compat_cap) + sizeof(uint32_t); +	else +		size = (SVGA_FIFO_3D_CAPS_LAST - SVGA_FIFO_3D_CAPS + 1) * +			sizeof(uint32_t); + +	if (arg->max_size < size) +		size = arg->max_size; + +	bounce = vzalloc(size); +	if (unlikely(bounce == NULL)) { +		DRM_ERROR("Failed to allocate bounce buffer for 3D caps.\n"); +		return -ENOMEM; +	} -	if (arg->debug_buffer_size < fifo_state->last_data_size) { -		arg->used_size = arg->debug_buffer_size; -		arg->did_not_fit = 1; +	if (gb_objects && vmw_fp->gb_aware) { +		int i, num; +		uint32_t *bounce32 = (uint32_t *) bounce; + +		num = size / sizeof(uint32_t); +		if (num > SVGA3D_DEVCAP_MAX) +			num = SVGA3D_DEVCAP_MAX; + +		mutex_lock(&dev_priv->hw_mutex); +		for (i = 0; i < num; ++i) { +			vmw_write(dev_priv, SVGA_REG_DEV_CAP, i); +			*bounce32++ = vmw_read(dev_priv, SVGA_REG_DEV_CAP); +		} +		mutex_unlock(&dev_priv->hw_mutex); +	} else if (gb_objects) { +		ret = vmw_fill_compat_cap(dev_priv, bounce, size); +		if (unlikely(ret != 0)) +			goto out_err;  	} else { -		arg->used_size = fifo_state->last_data_size; -		arg->did_not_fit = 0; +		fifo_mem = dev_priv->mmio_virt; +		memcpy_fromio(bounce, &fifo_mem[SVGA_FIFO_3D_CAPS], size); +	} + +	ret = copy_to_user(buffer, bounce, size); +	if (ret) +		ret = -EFAULT; +out_err: +	vfree(bounce); + +	if (unlikely(ret != 0)) +		DRM_ERROR("Failed to report 3D caps info.\n"); + +	return ret; +} + +int vmw_present_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 drm_vmw_present_arg *arg = +		(struct drm_vmw_present_arg *)data; +	struct vmw_surface *surface; +	struct drm_vmw_rect __user *clips_ptr; +	struct drm_vmw_rect *clips = NULL; +	struct drm_framebuffer *fb; +	struct vmw_framebuffer *vfb; +	struct vmw_resource *res; +	uint32_t num_clips; +	int ret; + +	num_clips = arg->num_clips; +	clips_ptr = (struct drm_vmw_rect *)(unsigned long)arg->clips_ptr; + +	if (unlikely(num_clips == 0)) +		return 0; + +	if (clips_ptr == NULL) { +		DRM_ERROR("Variable clips_ptr must be specified.\n"); +		ret = -EINVAL; +		goto out_clips;  	} -	return copy_to_user(buffer, fifo_state->last_buffer, arg->used_size); + +	clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL); +	if (clips == NULL) { +		DRM_ERROR("Failed to allocate clip rect list.\n"); +		ret = -ENOMEM; +		goto out_clips; +	} + +	ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips)); +	if (ret) { +		DRM_ERROR("Failed to copy clip rects from userspace.\n"); +		ret = -EFAULT; +		goto out_no_copy; +	} + +	drm_modeset_lock_all(dev); + +	fb = drm_framebuffer_lookup(dev, arg->fb_id); +	if (!fb) { +		DRM_ERROR("Invalid framebuffer id.\n"); +		ret = -ENOENT; +		goto out_no_fb; +	} +	vfb = vmw_framebuffer_to_vfb(fb); + +	ret = ttm_read_lock(&dev_priv->reservation_sem, true); +	if (unlikely(ret != 0)) +		goto out_no_ttm_lock; + +	ret = vmw_user_resource_lookup_handle(dev_priv, tfile, arg->sid, +					      user_surface_converter, +					      &res); +	if (ret) +		goto out_no_surface; + +	surface = vmw_res_to_srf(res); +	ret = vmw_kms_present(dev_priv, file_priv, +			      vfb, surface, arg->sid, +			      arg->dest_x, arg->dest_y, +			      clips, num_clips); + +	/* vmw_user_surface_lookup takes one ref so does new_fb */ +	vmw_surface_unreference(&surface); + +out_no_surface: +	ttm_read_unlock(&dev_priv->reservation_sem); +out_no_ttm_lock: +	drm_framebuffer_unreference(fb); +out_no_fb: +	drm_modeset_unlock_all(dev); +out_no_copy: +	kfree(clips); +out_clips: +	return ret; +} + +int vmw_present_readback_ioctl(struct drm_device *dev, void *data, +			       struct drm_file *file_priv) +{ +	struct vmw_private *dev_priv = vmw_priv(dev); +	struct drm_vmw_present_readback_arg *arg = +		(struct drm_vmw_present_readback_arg *)data; +	struct drm_vmw_fence_rep __user *user_fence_rep = +		(struct drm_vmw_fence_rep __user *) +		(unsigned long)arg->fence_rep; +	struct drm_vmw_rect __user *clips_ptr; +	struct drm_vmw_rect *clips = NULL; +	struct drm_framebuffer *fb; +	struct vmw_framebuffer *vfb; +	uint32_t num_clips; +	int ret; + +	num_clips = arg->num_clips; +	clips_ptr = (struct drm_vmw_rect *)(unsigned long)arg->clips_ptr; + +	if (unlikely(num_clips == 0)) +		return 0; + +	if (clips_ptr == NULL) { +		DRM_ERROR("Argument clips_ptr must be specified.\n"); +		ret = -EINVAL; +		goto out_clips; +	} + +	clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL); +	if (clips == NULL) { +		DRM_ERROR("Failed to allocate clip rect list.\n"); +		ret = -ENOMEM; +		goto out_clips; +	} + +	ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips)); +	if (ret) { +		DRM_ERROR("Failed to copy clip rects from userspace.\n"); +		ret = -EFAULT; +		goto out_no_copy; +	} + +	drm_modeset_lock_all(dev); + +	fb = drm_framebuffer_lookup(dev, arg->fb_id); +	if (!fb) { +		DRM_ERROR("Invalid framebuffer id.\n"); +		ret = -ENOENT; +		goto out_no_fb; +	} + +	vfb = vmw_framebuffer_to_vfb(fb); +	if (!vfb->dmabuf) { +		DRM_ERROR("Framebuffer not dmabuf backed.\n"); +		ret = -EINVAL; +		goto out_no_ttm_lock; +	} + +	ret = ttm_read_lock(&dev_priv->reservation_sem, true); +	if (unlikely(ret != 0)) +		goto out_no_ttm_lock; + +	ret = vmw_kms_readback(dev_priv, file_priv, +			       vfb, user_fence_rep, +			       clips, num_clips); + +	ttm_read_unlock(&dev_priv->reservation_sem); +out_no_ttm_lock: +	drm_framebuffer_unreference(fb); +out_no_fb: +	drm_modeset_unlock_all(dev); +out_no_copy: +	kfree(clips); +out_clips: +	return ret; +} + + +/** + * vmw_fops_poll - wrapper around the drm_poll function + * + * @filp: See the linux fops poll documentation. + * @wait: See the linux fops poll documentation. + * + * Wrapper around the drm_poll function that makes sure the device is + * processing the fifo if drm_poll decides to wait. + */ +unsigned int vmw_fops_poll(struct file *filp, struct poll_table_struct *wait) +{ +	struct drm_file *file_priv = filp->private_data; +	struct vmw_private *dev_priv = +		vmw_priv(file_priv->minor->dev); + +	vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); +	return drm_poll(filp, wait); +} + + +/** + * vmw_fops_read - wrapper around the drm_read function + * + * @filp: See the linux fops read documentation. + * @buffer: See the linux fops read documentation. + * @count: See the linux fops read documentation. + * offset: See the linux fops read documentation. + * + * Wrapper around the drm_read function that makes sure the device is + * processing the fifo if drm_read decides to wait. + */ +ssize_t vmw_fops_read(struct file *filp, char __user *buffer, +		      size_t count, loff_t *offset) +{ +	struct drm_file *file_priv = filp->private_data; +	struct vmw_private *dev_priv = +		vmw_priv(file_priv->minor->dev); + +	vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); +	return drm_read(filp, buffer, count, offset);  } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c index e92298a6a38..0c423766c44 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c @@ -25,35 +25,42 @@   *   **************************************************************************/ -#include "drmP.h" +#include <drm/drmP.h>  #include "vmwgfx_drv.h"  #define VMW_FENCE_WRAP (1 << 24) -irqreturn_t vmw_irq_handler(DRM_IRQ_ARGS) +irqreturn_t vmw_irq_handler(int irq, void *arg)  {  	struct drm_device *dev = (struct drm_device *)arg;  	struct vmw_private *dev_priv = vmw_priv(dev); -	uint32_t status; +	uint32_t status, masked_status;  	spin_lock(&dev_priv->irq_lock);  	status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); +	masked_status = status & dev_priv->irq_mask;  	spin_unlock(&dev_priv->irq_lock); -	if (status & SVGA_IRQFLAG_ANY_FENCE) +	if (likely(status)) +		outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); + +	if (!masked_status) +		return IRQ_NONE; + +	if (masked_status & (SVGA_IRQFLAG_ANY_FENCE | +			     SVGA_IRQFLAG_FENCE_GOAL)) { +		vmw_fences_update(dev_priv->fman);  		wake_up_all(&dev_priv->fence_queue); -	if (status & SVGA_IRQFLAG_FIFO_PROGRESS) +	} + +	if (masked_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; +	return IRQ_HANDLED;  } -static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t sequence) +static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t seqno)  {  	uint32_t busy; @@ -64,43 +71,43 @@ static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t sequence)  	return (busy == 0);  } -void vmw_update_sequence(struct vmw_private *dev_priv, +void vmw_update_seqno(struct vmw_private *dev_priv,  			 struct vmw_fifo_state *fifo_state)  {  	__le32 __iomem *fifo_mem = dev_priv->mmio_virt; +	uint32_t seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE); -	uint32_t sequence = ioread32(fifo_mem + SVGA_FIFO_FENCE); - -	if (dev_priv->last_read_sequence != sequence) { -		dev_priv->last_read_sequence = sequence; -		vmw_fence_pull(&fifo_state->fence_queue, sequence); +	if (dev_priv->last_read_seqno != seqno) { +		dev_priv->last_read_seqno = seqno; +		vmw_marker_pull(&fifo_state->marker_queue, seqno); +		vmw_fences_update(dev_priv->fman);  	}  } -bool vmw_fence_signaled(struct vmw_private *dev_priv, -			uint32_t sequence) +bool vmw_seqno_passed(struct vmw_private *dev_priv, +			 uint32_t seqno)  {  	struct vmw_fifo_state *fifo_state;  	bool ret; -	if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP)) +	if (likely(dev_priv->last_read_seqno - seqno < VMW_FENCE_WRAP))  		return true;  	fifo_state = &dev_priv->fifo; -	vmw_update_sequence(dev_priv, fifo_state); -	if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP)) +	vmw_update_seqno(dev_priv, fifo_state); +	if (likely(dev_priv->last_read_seqno - seqno < VMW_FENCE_WRAP))  		return true;  	if (!(fifo_state->capabilities & SVGA_FIFO_CAP_FENCE) && -	    vmw_fifo_idle(dev_priv, sequence)) +	    vmw_fifo_idle(dev_priv, seqno))  		return true;  	/** -	 * Then check if the sequence is higher than what we've actually +	 * Then check if the seqno is higher than what we've actually  	 * emitted. Then the fence is stale and signaled.  	 */ -	ret = ((atomic_read(&dev_priv->fence_seq) - sequence) +	ret = ((atomic_read(&dev_priv->marker_seq) - seqno)  	       > VMW_FENCE_WRAP);  	return ret; @@ -109,7 +116,7 @@ bool vmw_fence_signaled(struct vmw_private *dev_priv,  int vmw_fallback_wait(struct vmw_private *dev_priv,  		      bool lazy,  		      bool fifo_idle, -		      uint32_t sequence, +		      uint32_t seqno,  		      bool interruptible,  		      unsigned long timeout)  { @@ -123,7 +130,7 @@ int vmw_fallback_wait(struct vmw_private *dev_priv,  	DEFINE_WAIT(__wait);  	wait_condition = (fifo_idle) ? &vmw_fifo_idle : -		&vmw_fence_signaled; +		&vmw_seqno_passed;  	/**  	 * Block command submission while waiting for idle. @@ -131,14 +138,14 @@ int vmw_fallback_wait(struct vmw_private *dev_priv,  	if (fifo_idle)  		down_read(&fifo_state->rwsem); -	signal_seq = atomic_read(&dev_priv->fence_seq); +	signal_seq = atomic_read(&dev_priv->marker_seq);  	ret = 0;  	for (;;) {  		prepare_to_wait(&dev_priv->fence_queue, &__wait,  				(interruptible) ?  				TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); -		if (wait_condition(dev_priv, sequence)) +		if (wait_condition(dev_priv, seqno))  			break;  		if (time_after_eq(jiffies, end_jiffies)) {  			DRM_ERROR("SVGA device lockup.\n"); @@ -175,68 +182,110 @@ int vmw_fallback_wait(struct vmw_private *dev_priv,  	return ret;  } -int vmw_wait_fence(struct vmw_private *dev_priv, -		   bool lazy, uint32_t sequence, -		   bool interruptible, unsigned long timeout) +void vmw_seqno_waiter_add(struct vmw_private *dev_priv) +{ +	mutex_lock(&dev_priv->hw_mutex); +	if (dev_priv->fence_queue_waiters++ == 0) { +		unsigned long irq_flags; + +		spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); +		outl(SVGA_IRQFLAG_ANY_FENCE, +		     dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); +		dev_priv->irq_mask |= SVGA_IRQFLAG_ANY_FENCE; +		vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); +		spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); +	} +	mutex_unlock(&dev_priv->hw_mutex); +} + +void vmw_seqno_waiter_remove(struct vmw_private *dev_priv) +{ +	mutex_lock(&dev_priv->hw_mutex); +	if (--dev_priv->fence_queue_waiters == 0) { +		unsigned long irq_flags; + +		spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); +		dev_priv->irq_mask &= ~SVGA_IRQFLAG_ANY_FENCE; +		vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); +		spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); +	} +	mutex_unlock(&dev_priv->hw_mutex); +} + + +void vmw_goal_waiter_add(struct vmw_private *dev_priv) +{ +	mutex_lock(&dev_priv->hw_mutex); +	if (dev_priv->goal_queue_waiters++ == 0) { +		unsigned long irq_flags; + +		spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); +		outl(SVGA_IRQFLAG_FENCE_GOAL, +		     dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); +		dev_priv->irq_mask |= SVGA_IRQFLAG_FENCE_GOAL; +		vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); +		spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); +	} +	mutex_unlock(&dev_priv->hw_mutex); +} + +void vmw_goal_waiter_remove(struct vmw_private *dev_priv) +{ +	mutex_lock(&dev_priv->hw_mutex); +	if (--dev_priv->goal_queue_waiters == 0) { +		unsigned long irq_flags; + +		spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); +		dev_priv->irq_mask &= ~SVGA_IRQFLAG_FENCE_GOAL; +		vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); +		spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); +	} +	mutex_unlock(&dev_priv->hw_mutex); +} + +int vmw_wait_seqno(struct vmw_private *dev_priv, +		      bool lazy, uint32_t seqno, +		      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)) +	if (likely(dev_priv->last_read_seqno - seqno < VMW_FENCE_WRAP))  		return 0; -	if (likely(vmw_fence_signaled(dev_priv, sequence))) +	if (likely(vmw_seqno_passed(dev_priv, seqno)))  		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, +		return vmw_fallback_wait(dev_priv, lazy, true, seqno,  					 interruptible, timeout);  	if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK)) -		return vmw_fallback_wait(dev_priv, lazy, false, sequence, +		return vmw_fallback_wait(dev_priv, lazy, false, seqno,  					 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); +	vmw_seqno_waiter_add(dev_priv);  	if (interruptible)  		ret = wait_event_interruptible_timeout  		    (dev_priv->fence_queue, -		     vmw_fence_signaled(dev_priv, sequence), +		     vmw_seqno_passed(dev_priv, seqno),  		     timeout);  	else  		ret = wait_event_timeout  		    (dev_priv->fence_queue, -		     vmw_fence_signaled(dev_priv, sequence), +		     vmw_seqno_passed(dev_priv, seqno),  		     timeout); +	vmw_seqno_waiter_remove(dev_priv); +  	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;  } @@ -273,25 +322,3 @@ void vmw_irq_uninstall(struct drm_device *dev)  	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 index cceeb42789b..8f3edc4710f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -27,11 +27,47 @@  #include "vmwgfx_kms.h" +  /* Might need a hrtimer here? */  #define VMWGFX_PRESENT_RATE ((HZ / 60 > 0) ? HZ / 60 : 1) -static int vmw_surface_dmabuf_pin(struct vmw_framebuffer *vfb); -static int vmw_surface_dmabuf_unpin(struct vmw_framebuffer *vfb); + +struct vmw_clip_rect { +	int x1, x2, y1, y2; +}; + +/** + * Clip @num_rects number of @rects against @clip storing the + * results in @out_rects and the number of passed rects in @out_num. + */ +static void vmw_clip_cliprects(struct drm_clip_rect *rects, +			int num_rects, +			struct vmw_clip_rect clip, +			SVGASignedRect *out_rects, +			int *out_num) +{ +	int i, k; + +	for (i = 0, k = 0; i < num_rects; i++) { +		int x1 = max_t(int, clip.x1, rects[i].x1); +		int y1 = max_t(int, clip.y1, rects[i].y1); +		int x2 = min_t(int, clip.x2, rects[i].x2); +		int y2 = min_t(int, clip.y2, rects[i].y2); + +		if (x1 >= x2) +			continue; +		if (y1 >= y2) +			continue; + +		out_rects[k].left   = x1; +		out_rects[k].top    = y1; +		out_rects[k].right  = x2; +		out_rects[k].bottom = y2; +		k++; +	} + +	*out_num = k; +}  void vmw_display_unit_cleanup(struct vmw_display_unit *du)  { @@ -39,6 +75,7 @@ void vmw_display_unit_cleanup(struct vmw_display_unit *du)  		vmw_surface_unreference(&du->cursor_surface);  	if (du->cursor_dmabuf)  		vmw_dmabuf_unreference(&du->cursor_dmabuf); +	drm_sysfs_connector_remove(&du->connector);  	drm_crtc_cleanup(&du->crtc);  	drm_encoder_cleanup(&du->encoder);  	drm_connector_cleanup(&du->connector); @@ -84,6 +121,43 @@ int vmw_cursor_update_image(struct vmw_private *dev_priv,  	return 0;  } +int vmw_cursor_update_dmabuf(struct vmw_private *dev_priv, +			     struct vmw_dma_buffer *dmabuf, +			     u32 width, u32 height, +			     u32 hotspotX, u32 hotspotY) +{ +	struct ttm_bo_kmap_obj map; +	unsigned long kmap_offset; +	unsigned long kmap_num; +	void *virtual; +	bool dummy; +	int ret; + +	kmap_offset = 0; +	kmap_num = (width*height*4 + PAGE_SIZE - 1) >> 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); +	ret = vmw_cursor_update_image(dev_priv, virtual, width, height, +				      hotspotX, hotspotY); + +	ttm_bo_kunmap(&map); +err_unreserve: +	ttm_bo_unreserve(&dmabuf->base); + +	return ret; +} + +  void vmw_cursor_update_position(struct vmw_private *dev_priv,  				bool show, int x, int y)  { @@ -101,30 +175,47 @@ 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; +	/* +	 * FIXME: Unclear whether there's any global state touched by the +	 * cursor_set function, especially vmw_cursor_update_position looks +	 * suspicious. For now take the easy route and reacquire all locks. We +	 * can do this since the caller in the drm core doesn't check anything +	 * which is protected by any looks. +	 */ +	drm_modeset_unlock(&crtc->mutex); +	drm_modeset_lock_all(dev_priv->dev); + +	/* A lot of the code assumes this */ +	if (handle && (width != 64 || height != 64)) { +		ret = -EINVAL; +		goto out; +	} +  	if (handle) { -		ret = vmw_user_surface_lookup_handle(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; -			} +		struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + +		ret = vmw_user_lookup_handle(dev_priv, tfile, +					     handle, &surface, &dmabuf); +		if (ret) { +			DRM_ERROR("failed to find surface or dmabuf: %i\n", ret); +			ret = -EINVAL; +			goto out;  		}  	} +	/* need to do this before taking down old image */ +	if (surface && !surface->snooper.image) { +		DRM_ERROR("surface not suitable for cursor\n"); +		vmw_surface_unreference(&surface); +		ret = -EINVAL; +		goto out; +	} +  	/* takedown old cursor */  	if (du->cursor_surface) {  		du->cursor_surface->snooper.crtc = NULL; @@ -143,44 +234,27 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,  		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); - +		ret = vmw_cursor_update_dmabuf(dev_priv, dmabuf, width, height, +					       du->hotspot_x, du->hotspot_y);  	} else {  		vmw_cursor_update_position(dev_priv, false, 0, 0); -		return 0; +		ret = 0; +		goto out;  	} -	vmw_cursor_update_position(dev_priv, true, du->cursor_x, du->cursor_y); +	vmw_cursor_update_position(dev_priv, true, +				   du->cursor_x + du->hotspot_x, +				   du->cursor_y + du->hotspot_y); -	return 0; +	ret = 0; +out: +	drm_modeset_unlock_all(dev_priv->dev); +	drm_modeset_lock(&crtc->mutex, NULL); + +	return ret;  }  int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) @@ -192,8 +266,22 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)  	du->cursor_x = x + crtc->x;  	du->cursor_y = y + crtc->y; +	/* +	 * FIXME: Unclear whether there's any global state touched by the +	 * cursor_set function, especially vmw_cursor_update_position looks +	 * suspicious. For now take the easy route and reacquire all locks. We +	 * can do this since the caller in the drm core doesn't check anything +	 * which is protected by any looks. +	 */ +	drm_modeset_unlock(&crtc->mutex); +	drm_modeset_lock_all(dev_priv->dev); +  	vmw_cursor_update_position(dev_priv, shown, -				   du->cursor_x, du->cursor_y); +				   du->cursor_x + du->hotspot_x, +				   du->cursor_y + du->hotspot_y); + +	drm_modeset_unlock_all(dev_priv->dev); +	drm_modeset_lock(&crtc->mutex, NULL);  	return 0;  } @@ -214,7 +302,7 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf,  		SVGA3dCmdHeader header;  		SVGA3dCmdSurfaceDMA dma;  	} *cmd; -	int ret; +	int i, ret;  	cmd = container_of(header, struct vmw_dma_cmd, header); @@ -236,16 +324,19 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf,  	box_count = (cmd->header.size - sizeof(SVGA3dCmdSurfaceDMA)) /  			sizeof(SVGA3dCopyBox); -	if (cmd->dma.guest.pitch != (64 * 4) || -	    cmd->dma.guest.ptr.offset % PAGE_SIZE || +	if (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) { +	    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"); +		/* TODO handle more dst & src != 0 */ +		/* TODO handle more then one copy */ +		DRM_ERROR("Cant snoop dma request for cursor!\n"); +		DRM_ERROR("(%u, %u, %u) (%u, %u, %u) (%ux%ux%u) %u %u\n", +			  box->srcx, box->srcy, box->srcz, +			  box->x, box->y, box->z, +			  box->w, box->h, box->d, box_count, +			  cmd->dma.guest.ptr.offset);  		return;  	} @@ -264,7 +355,16 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf,  	virtual = ttm_kmap_obj_virtual(&map, &dummy); -	memcpy(srf->snooper.image, virtual, 64*64*4); +	if (box->w == 64 && cmd->dma.guest.pitch == 64*4) { +		memcpy(srf->snooper.image, virtual, 64*64*4); +	} else { +		/* Image is unsigned pointer. */ +		for (i = 0; i < box->h; i++) +			memcpy(srf->snooper.image + i * 64, +			       virtual + i * cmd->dma.guest.pitch, +			       box->w * 4); +	} +  	srf->snooper.age++;  	/* we can't call this function from this function since execbuf has @@ -308,16 +408,6 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)   * 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   */ @@ -329,42 +419,11 @@ struct vmw_framebuffer_surface {  	struct vmw_framebuffer base;  	struct vmw_surface *surface;  	struct vmw_dma_buffer *buffer; -	struct delayed_work d_work; -	struct mutex work_lock; -	bool present_fs;  	struct list_head head;  	struct drm_master *master;  }; -/** - * vmw_kms_idle_workqueues - Flush workqueues on this master - * - * @vmaster - Pointer identifying the master, for the surfaces of which - * we idle the dirty work queues. - * - * This function should be called with the ttm lock held in exclusive mode - * to idle all dirty work queues before the fifo is taken down. - * - * The work task may actually requeue itself, but after the flush returns we're - * sure that there's nothing to present, since the ttm lock is held in - * exclusive mode, so the fifo will never get used. - */ - -void vmw_kms_idle_workqueues(struct vmw_master *vmaster) -{ -	struct vmw_framebuffer_surface *entry; - -	mutex_lock(&vmaster->fb_surf_mutex); -	list_for_each_entry(entry, &vmaster->fb_surf, head) { -		if (cancel_delayed_work_sync(&entry->d_work)) -			(void) entry->d_work.work.func(&entry->d_work.work); - -		(void) cancel_delayed_work_sync(&entry->d_work); -	} -	mutex_unlock(&vmaster->fb_surf_mutex); -} - -void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer) +static void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer)  {  	struct vmw_framebuffer_surface *vfbs =  		vmw_framebuffer_to_vfbs(framebuffer); @@ -375,111 +434,186 @@ void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer)  	list_del(&vfbs->head);  	mutex_unlock(&vmaster->fb_surf_mutex); -	cancel_delayed_work_sync(&vfbs->d_work);  	drm_master_put(&vfbs->master);  	drm_framebuffer_cleanup(framebuffer);  	vmw_surface_unreference(&vfbs->surface); +	ttm_base_object_unref(&vfbs->base.user_obj);  	kfree(vfbs);  } -static void vmw_framebuffer_present_fs_callback(struct work_struct *work) +static int do_surface_dirty_sou(struct vmw_private *dev_priv, +				struct drm_file *file_priv, +				struct vmw_framebuffer *framebuffer, +				unsigned flags, unsigned color, +				struct drm_clip_rect *clips, +				unsigned num_clips, int inc, +				struct vmw_fence_obj **out_fence)  { -	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 vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; +	struct drm_clip_rect *clips_ptr; +	struct drm_clip_rect *tmp; +	struct drm_crtc *crtc; +	size_t fifo_size; +	int i, num_units; +	int ret = 0; /* silence warning */ +	int left, right, top, bottom;  	struct {  		SVGA3dCmdHeader header; -		SVGA3dCmdPresent body; -		SVGA3dCopyRect cr; +		SVGA3dCmdBlitSurfaceToScreen body;  	} *cmd; +	SVGASignedRect *blits; -	/** -	 * Strictly we should take the ttm_lock in read mode before accessing -	 * the fifo, to make sure the fifo is present and up. However, -	 * instead we flush all workqueues under the ttm lock in exclusive mode -	 * before taking down the fifo. -	 */ -	mutex_lock(&vfbs->work_lock); -	if (!vfbs->present_fs) -		goto out_unlock; +	num_units = 0; +	list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, +			    head) { +		if (crtc->primary->fb != &framebuffer->base) +			continue; +		units[num_units++] = vmw_crtc_to_du(crtc); +	} -	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); -} +	BUG_ON(!clips || !num_clips); + +	tmp = kzalloc(sizeof(*tmp) * num_clips, GFP_KERNEL); +	if (unlikely(tmp == NULL)) { +		DRM_ERROR("Temporary cliprect memory alloc failed.\n"); +		return -ENOMEM; +	} + +	fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num_clips; +	cmd = kzalloc(fifo_size, GFP_KERNEL); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Temporary fifo memory alloc failed.\n"); +		ret = -ENOMEM; +		goto out_free_tmp; +	} +	/* setup blits pointer */ +	blits = (SVGASignedRect *)&cmd[1]; + +	/* initial clip region */ +	left = clips->x1; +	right = clips->x2; +	top = clips->y1; +	bottom = clips->y2; + +	/* skip the first clip rect */ +	for (i = 1, clips_ptr = clips + inc; +	     i < num_clips; i++, clips_ptr += inc) { +		left = min_t(int, left, (int)clips_ptr->x1); +		right = max_t(int, right, (int)clips_ptr->x2); +		top = min_t(int, top, (int)clips_ptr->y1); +		bottom = max_t(int, bottom, (int)clips_ptr->y2); +	} + +	/* only need to do this once */ +	cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN); +	cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header)); + +	cmd->body.srcRect.left = left; +	cmd->body.srcRect.right = right; +	cmd->body.srcRect.top = top; +	cmd->body.srcRect.bottom = bottom; + +	clips_ptr = clips; +	for (i = 0; i < num_clips; i++, clips_ptr += inc) { +		tmp[i].x1 = clips_ptr->x1 - left; +		tmp[i].x2 = clips_ptr->x2 - left; +		tmp[i].y1 = clips_ptr->y1 - top; +		tmp[i].y2 = clips_ptr->y2 - top; +	} + +	/* do per unit writing, reuse fifo for each */ +	for (i = 0; i < num_units; i++) { +		struct vmw_display_unit *unit = units[i]; +		struct vmw_clip_rect clip; +		int num; + +		clip.x1 = left - unit->crtc.x; +		clip.y1 = top - unit->crtc.y; +		clip.x2 = right - unit->crtc.x; +		clip.y2 = bottom - unit->crtc.y; + +		/* skip any crtcs that misses the clip region */ +		if (clip.x1 >= unit->crtc.mode.hdisplay || +		    clip.y1 >= unit->crtc.mode.vdisplay || +		    clip.x2 <= 0 || clip.y2 <= 0) +			continue; + +		/* +		 * In order for the clip rects to be correctly scaled +		 * the src and dest rects needs to be the same size. +		 */ +		cmd->body.destRect.left = clip.x1; +		cmd->body.destRect.right = clip.x2; +		cmd->body.destRect.top = clip.y1; +		cmd->body.destRect.bottom = clip.y2; + +		/* create a clip rect of the crtc in dest coords */ +		clip.x2 = unit->crtc.mode.hdisplay - clip.x1; +		clip.y2 = unit->crtc.mode.vdisplay - clip.y1; +		clip.x1 = 0 - clip.x1; +		clip.y1 = 0 - clip.y1; + +		/* need to reset sid as it is changed by execbuf */ +		cmd->body.srcImage.sid = cpu_to_le32(framebuffer->user_handle); +		cmd->body.destScreenId = unit->unit; + +		/* clip and write blits to cmd stream */ +		vmw_clip_cliprects(tmp, num_clips, clip, blits, &num); + +		/* if no cliprects hit skip this */ +		if (num == 0) +			continue; + +		/* only return the last fence */ +		if (out_fence && *out_fence) +			vmw_fence_obj_unreference(out_fence); + +		/* recalculate package length */ +		fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num; +		cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header)); +		ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, +					  fifo_size, 0, NULL, out_fence); + +		if (unlikely(ret != 0)) +			break; +	} + + +	kfree(cmd); +out_free_tmp: +	kfree(tmp); + +	return ret; +} -int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, +static int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,  				  struct drm_file *file_priv,  				  unsigned flags, unsigned color,  				  struct drm_clip_rect *clips,  				  unsigned num_clips)  {  	struct vmw_private *dev_priv = vmw_priv(framebuffer->dev); -	struct vmw_master *vmaster = vmw_master(file_priv->master);  	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; -	int ret; - -	struct { -		SVGA3dCmdHeader header; -		SVGA3dCmdPresent body; -		SVGA3dCopyRect cr; -	} *cmd; +	int ret, inc = 1;  	if (unlikely(vfbs->master != file_priv->master))  		return -EINVAL; -	ret = ttm_read_lock(&vmaster->lock, true); -	if (unlikely(ret != 0)) -		return ret; +	/* Require ScreenObject support for 3D */ +	if (!dev_priv->sou_priv) +		return -EINVAL; -	if (!num_clips || -	    !(dev_priv->fifo.capabilities & -	      SVGA_FIFO_CAP_SCREEN_OBJECT)) { -		int ret; +	drm_modeset_lock_all(dev_priv->dev); -		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); -		} -		ttm_read_unlock(&vmaster->lock); -		return 0; +	ret = ttm_read_lock(&dev_priv->reservation_sem, true); +	if (unlikely(ret != 0)) { +		drm_modeset_unlock_all(dev_priv->dev); +		return ret;  	}  	if (!num_clips) { @@ -493,37 +627,20 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,  		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"); -		ttm_read_unlock(&vmaster->lock); -		return -ENOMEM; -	} - -	memset(cmd, 0, sizeof(*cmd)); +	ret = do_surface_dirty_sou(dev_priv, file_priv, &vfbs->base, +				   flags, color, +				   clips, num_clips, inc, NULL); -	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); +	ttm_read_unlock(&dev_priv->reservation_sem); -	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); -	} +	drm_modeset_unlock_all(dev_priv->dev); -	vmw_fifo_commit(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr)); -	ttm_read_unlock(&vmaster->lock);  	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,  };  static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, @@ -540,15 +657,23 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,  	struct vmw_master *vmaster = vmw_master(file_priv->master);  	int ret; +	/* 3D is only supported on HWv8 hosts which supports screen objects */ +	if (!dev_priv->sou_priv) +		return -ENOSYS; +  	/*  	 * Sanity checks.  	 */ +	/* Surface must be marked as a scanout. */ +	if (unlikely(!surface->scanout)) +		return -EINVAL; +  	if (unlikely(surface->mip_levels[0] != 1 ||  		     surface->num_sizes != 1 || -		     surface->sizes[0].width < mode_cmd->width || -		     surface->sizes[0].height < mode_cmd->height || -		     surface->sizes[0].depth != 1)) { +		     surface->base_size.width < mode_cmd->width || +		     surface->base_size.height < mode_cmd->height || +		     surface->base_size.depth != 1)) {  		DRM_ERROR("Incompatible surface dimensions "  			  "for requested mode.\n");  		return -EINVAL; @@ -567,6 +692,9 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,  	case 15:  		format = SVGA3D_A1R5G5B5;  		break; +	case 8: +		format = SVGA3D_LUMINANCE8; +		break;  	default:  		DRM_ERROR("Invalid color depth: %d\n", mode_cmd->depth);  		return -EINVAL; @@ -583,39 +711,37 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,  		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; +		ret = -EINVAL; +		goto out_err2;  	}  	/* XXX get the first 3 from the surface info */  	vfbs->base.base.bits_per_pixel = mode_cmd->bpp; -	vfbs->base.base.pitch = mode_cmd->pitch; +	vfbs->base.base.pitches[0] = mode_cmd->pitch;  	vfbs->base.base.depth = mode_cmd->depth;  	vfbs->base.base.width = mode_cmd->width;  	vfbs->base.base.height = mode_cmd->height; -	vfbs->base.pin = &vmw_surface_dmabuf_pin; -	vfbs->base.unpin = &vmw_surface_dmabuf_unpin;  	vfbs->surface = surface; +	vfbs->base.user_handle = mode_cmd->handle;  	vfbs->master = drm_master_get(file_priv->master); -	mutex_init(&vfbs->work_lock);  	mutex_lock(&vmaster->fb_surf_mutex); -	INIT_DELAYED_WORK(&vfbs->d_work, &vmw_framebuffer_present_fs_callback);  	list_add_tail(&vfbs->head, &vmaster->fb_surf);  	mutex_unlock(&vmaster->fb_surf_mutex);  	*out = &vfbs->base; +	ret = drm_framebuffer_init(dev, &vfbs->base.base, +				   &vmw_framebuffer_surface_funcs); +	if (ret) +		goto out_err3; +  	return 0;  out_err3: -	drm_framebuffer_cleanup(&vfbs->base.base); +	vmw_surface_unreference(&surface);  out_err2:  	kfree(vfbs);  out_err1: @@ -634,55 +760,40 @@ struct vmw_framebuffer_dmabuf {  	struct vmw_dma_buffer *buffer;  }; -void vmw_framebuffer_dmabuf_destroy(struct drm_framebuffer *framebuffer) +static 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); +	ttm_base_object_unref(&vfbd->base.user_obj);  	kfree(vfbd);  } -int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, -				 struct drm_file *file_priv, -				 unsigned flags, unsigned color, -				 struct drm_clip_rect *clips, -				 unsigned num_clips) +static int do_dmabuf_dirty_ldu(struct vmw_private *dev_priv, +			       struct vmw_framebuffer *framebuffer, +			       unsigned flags, unsigned color, +			       struct drm_clip_rect *clips, +			       unsigned num_clips, int increment)  { -	struct vmw_private *dev_priv = vmw_priv(framebuffer->dev); -	struct vmw_master *vmaster = vmw_master(file_priv->master); -	struct drm_clip_rect norect; -	int ret; +	size_t fifo_size; +	int i; +  	struct {  		uint32_t header;  		SVGAFifoCmdUpdate body;  	} *cmd; -	int i, increment = 1; - -	ret = ttm_read_lock(&vmaster->lock, true); -	if (unlikely(ret != 0)) -		return ret; -	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; -		increment = 2; -	} - -	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) * num_clips); +	fifo_size = sizeof(*cmd) * num_clips; +	cmd = vmw_fifo_reserve(dev_priv, fifo_size);  	if (unlikely(cmd == NULL)) {  		DRM_ERROR("Fifo reserve failed.\n"); -		ttm_read_unlock(&vmaster->lock);  		return -ENOMEM;  	} +	memset(cmd, 0, fifo_size);  	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->x1); @@ -691,57 +802,206 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,  		cmd[i].body.height = cpu_to_le32(clips->y2 - clips->y1);  	} -	vmw_fifo_commit(dev_priv, sizeof(*cmd) * num_clips); -	ttm_read_unlock(&vmaster->lock); - +	vmw_fifo_commit(dev_priv, fifo_size);  	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_surface_dmabuf_pin(struct vmw_framebuffer *vfb) +static int do_dmabuf_define_gmrfb(struct drm_file *file_priv, +				  struct vmw_private *dev_priv, +				  struct vmw_framebuffer *framebuffer)  { -	struct vmw_private *dev_priv = vmw_priv(vfb->base.dev); -	struct vmw_framebuffer_surface *vfbs = -		vmw_framebuffer_to_vfbs(&vfb->base); -	unsigned long size = vfbs->base.base.pitch * vfbs->base.base.height; +	int depth = framebuffer->base.depth; +	size_t fifo_size;  	int ret; -	vfbs->buffer = kzalloc(sizeof(*vfbs->buffer), GFP_KERNEL); -	if (unlikely(vfbs->buffer == NULL)) +	struct { +		uint32_t header; +		SVGAFifoCmdDefineGMRFB body; +	} *cmd; + +	/* Emulate RGBA support, contrary to svga_reg.h this is not +	 * supported by hosts. This is only a problem if we are reading +	 * this value later and expecting what we uploaded back. +	 */ +	if (depth == 32) +		depth = 24; + +	fifo_size = sizeof(*cmd); +	cmd = kmalloc(fifo_size, GFP_KERNEL); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed to allocate temporary cmd buffer.\n");  		return -ENOMEM; +	} -	vmw_overlay_pause_all(dev_priv); -	ret = vmw_dmabuf_init(dev_priv, vfbs->buffer, size, -			       &vmw_vram_ne_placement, -			       false, &vmw_dmabuf_bo_free); -	vmw_overlay_resume_all(dev_priv); +	memset(cmd, 0, fifo_size); +	cmd->header = SVGA_CMD_DEFINE_GMRFB; +	cmd->body.format.bitsPerPixel = framebuffer->base.bits_per_pixel; +	cmd->body.format.colorDepth = depth; +	cmd->body.format.reserved = 0; +	cmd->body.bytesPerLine = framebuffer->base.pitches[0]; +	cmd->body.ptr.gmrId = framebuffer->user_handle; +	cmd->body.ptr.offset = 0; + +	ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, +				  fifo_size, 0, NULL, NULL); + +	kfree(cmd); + +	return ret; +} + +static int do_dmabuf_dirty_sou(struct drm_file *file_priv, +			       struct vmw_private *dev_priv, +			       struct vmw_framebuffer *framebuffer, +			       unsigned flags, unsigned color, +			       struct drm_clip_rect *clips, +			       unsigned num_clips, int increment, +			       struct vmw_fence_obj **out_fence) +{ +	struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; +	struct drm_clip_rect *clips_ptr; +	int i, k, num_units, ret; +	struct drm_crtc *crtc; +	size_t fifo_size; + +	struct { +		uint32_t header; +		SVGAFifoCmdBlitGMRFBToScreen body; +	} *blits; + +	ret = do_dmabuf_define_gmrfb(file_priv, dev_priv, framebuffer);  	if (unlikely(ret != 0)) -		vfbs->buffer = NULL; +		return ret; /* define_gmrfb prints warnings */ + +	fifo_size = sizeof(*blits) * num_clips; +	blits = kmalloc(fifo_size, GFP_KERNEL); +	if (unlikely(blits == NULL)) { +		DRM_ERROR("Failed to allocate temporary cmd buffer.\n"); +		return -ENOMEM; +	} + +	num_units = 0; +	list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) { +		if (crtc->primary->fb != &framebuffer->base) +			continue; +		units[num_units++] = vmw_crtc_to_du(crtc); +	} + +	for (k = 0; k < num_units; k++) { +		struct vmw_display_unit *unit = units[k]; +		int hit_num = 0; + +		clips_ptr = clips; +		for (i = 0; i < num_clips; i++, clips_ptr += increment) { +			int clip_x1 = clips_ptr->x1 - unit->crtc.x; +			int clip_y1 = clips_ptr->y1 - unit->crtc.y; +			int clip_x2 = clips_ptr->x2 - unit->crtc.x; +			int clip_y2 = clips_ptr->y2 - unit->crtc.y; +			int move_x, move_y; + +			/* skip any crtcs that misses the clip region */ +			if (clip_x1 >= unit->crtc.mode.hdisplay || +			    clip_y1 >= unit->crtc.mode.vdisplay || +			    clip_x2 <= 0 || clip_y2 <= 0) +				continue; + +			/* clip size to crtc size */ +			clip_x2 = min_t(int, clip_x2, unit->crtc.mode.hdisplay); +			clip_y2 = min_t(int, clip_y2, unit->crtc.mode.vdisplay); + +			/* translate both src and dest to bring clip into screen */ +			move_x = min_t(int, clip_x1, 0); +			move_y = min_t(int, clip_y1, 0); + +			/* actual translate done here */ +			blits[hit_num].header = SVGA_CMD_BLIT_GMRFB_TO_SCREEN; +			blits[hit_num].body.destScreenId = unit->unit; +			blits[hit_num].body.srcOrigin.x = clips_ptr->x1 - move_x; +			blits[hit_num].body.srcOrigin.y = clips_ptr->y1 - move_y; +			blits[hit_num].body.destRect.left = clip_x1 - move_x; +			blits[hit_num].body.destRect.top = clip_y1 - move_y; +			blits[hit_num].body.destRect.right = clip_x2; +			blits[hit_num].body.destRect.bottom = clip_y2; +			hit_num++; +		} + +		/* no clips hit the crtc */ +		if (hit_num == 0) +			continue; + +		/* only return the last fence */ +		if (out_fence && *out_fence) +			vmw_fence_obj_unreference(out_fence); + +		fifo_size = sizeof(*blits) * hit_num; +		ret = vmw_execbuf_process(file_priv, dev_priv, NULL, blits, +					  fifo_size, 0, NULL, out_fence); + +		if (unlikely(ret != 0)) +			break; +	} + +	kfree(blits);  	return ret;  } -static int vmw_surface_dmabuf_unpin(struct vmw_framebuffer *vfb) +static int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, +				 struct drm_file *file_priv, +				 unsigned flags, unsigned color, +				 struct drm_clip_rect *clips, +				 unsigned num_clips)  { -	struct ttm_buffer_object *bo; -	struct vmw_framebuffer_surface *vfbs = -		vmw_framebuffer_to_vfbs(&vfb->base); +	struct vmw_private *dev_priv = vmw_priv(framebuffer->dev); +	struct vmw_framebuffer_dmabuf *vfbd = +		vmw_framebuffer_to_vfbd(framebuffer); +	struct drm_clip_rect norect; +	int ret, increment = 1; -	if (unlikely(vfbs->buffer == NULL)) -		return 0; +	drm_modeset_lock_all(dev_priv->dev); -	bo = &vfbs->buffer->base; -	ttm_bo_unref(&bo); -	vfbs->buffer = NULL; +	ret = ttm_read_lock(&dev_priv->reservation_sem, true); +	if (unlikely(ret != 0)) { +		drm_modeset_unlock_all(dev_priv->dev); +		return ret; +	} -	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; +		increment = 2; +	} + +	if (dev_priv->ldu_priv) { +		ret = do_dmabuf_dirty_ldu(dev_priv, &vfbd->base, +					  flags, color, +					  clips, num_clips, increment); +	} else { +		ret = do_dmabuf_dirty_sou(file_priv, dev_priv, &vfbd->base, +					  flags, color, +					  clips, num_clips, increment, NULL); +	} + +	ttm_read_unlock(&dev_priv->reservation_sem); + +	drm_modeset_unlock_all(dev_priv->dev); + +	return ret;  } +static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = { +	.destroy = vmw_framebuffer_dmabuf_destroy, +	.dirty = vmw_framebuffer_dmabuf_dirty, +}; + +/** + * Pin the dmabuffer to the start of vram. + */  static int vmw_framebuffer_dmabuf_pin(struct vmw_framebuffer *vfb)  {  	struct vmw_private *dev_priv = vmw_priv(vfb->base.dev); @@ -749,10 +1009,12 @@ static int vmw_framebuffer_dmabuf_pin(struct vmw_framebuffer *vfb)  		vmw_framebuffer_to_vfbd(&vfb->base);  	int ret; +	/* This code should not be used with screen objects */ +	BUG_ON(dev_priv->sou_priv);  	vmw_overlay_pause_all(dev_priv); -	ret = vmw_dmabuf_to_start_of_vram(dev_priv, vfbd->buffer); +	ret = vmw_dmabuf_to_start_of_vram(dev_priv, vfbd->buffer, true, false);  	vmw_overlay_resume_all(dev_priv); @@ -772,7 +1034,7 @@ static int vmw_framebuffer_dmabuf_unpin(struct vmw_framebuffer *vfb)  		return 0;  	} -	return vmw_dmabuf_from_vram(dev_priv, vfbd->buffer); +	return vmw_dmabuf_unpin(dev_priv, vfbd->buffer, false);  }  static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, @@ -794,36 +1056,68 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,  		return -EINVAL;  	} +	/* Limited framebuffer color depth support for screen objects */ +	if (dev_priv->sou_priv) { +		switch (mode_cmd->depth) { +		case 32: +		case 24: +			/* Only support 32 bpp for 32 and 24 depth fbs */ +			if (mode_cmd->bpp == 32) +				break; + +			DRM_ERROR("Invalid color depth/bbp: %d %d\n", +				  mode_cmd->depth, mode_cmd->bpp); +			return -EINVAL; +		case 16: +		case 15: +			/* Only support 16 bpp for 16 and 15 depth fbs */ +			if (mode_cmd->bpp == 16) +				break; + +			DRM_ERROR("Invalid color depth/bbp: %d %d\n", +				  mode_cmd->depth, mode_cmd->bpp); +			return -EINVAL; +		default: +			DRM_ERROR("Invalid color depth: %d\n", mode_cmd->depth); +			return -EINVAL; +		} +	} +  	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; +		ret = -EINVAL; +		goto out_err2;  	}  	vfbd->base.base.bits_per_pixel = mode_cmd->bpp; -	vfbd->base.base.pitch = mode_cmd->pitch; +	vfbd->base.base.pitches[0] = mode_cmd->pitch;  	vfbd->base.base.depth = mode_cmd->depth;  	vfbd->base.base.width = mode_cmd->width;  	vfbd->base.base.height = mode_cmd->height; -	vfbd->base.pin = vmw_framebuffer_dmabuf_pin; -	vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin; +	if (!dev_priv->sou_priv) { +		vfbd->base.pin = vmw_framebuffer_dmabuf_pin; +		vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin; +	} +	vfbd->base.dmabuf = true;  	vfbd->buffer = dmabuf; +	vfbd->base.user_handle = mode_cmd->handle;  	*out = &vfbd->base; +	ret = drm_framebuffer_init(dev, &vfbd->base.base, +				   &vmw_framebuffer_dmabuf_funcs); +	if (ret) +		goto out_err3; +  	return 0;  out_err3: -	drm_framebuffer_cleanup(&vfbd->base.base); +	vmw_dmabuf_unreference(&dmabuf);  out_err2:  	kfree(vfbd);  out_err1: @@ -836,85 +1130,336 @@ out_err1:  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 drm_mode_fb_cmd2 *mode_cmd2)  {  	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; -	u64 required_size; +	struct ttm_base_object *user_obj; +	struct drm_mode_fb_cmd mode_cmd;  	int ret; +	mode_cmd.width = mode_cmd2->width; +	mode_cmd.height = mode_cmd2->height; +	mode_cmd.pitch = mode_cmd2->pitches[0]; +	mode_cmd.handle = mode_cmd2->handles[0]; +	drm_fb_get_bpp_depth(mode_cmd2->pixel_format, &mode_cmd.depth, +				    &mode_cmd.bpp); +  	/**  	 * This code should be conditioned on Screen Objects not being used.  	 * If screen objects are used, we can allocate a GMR to hold the  	 * requested framebuffer.  	 */ -	required_size = mode_cmd->pitch * mode_cmd->height; -	if (unlikely(required_size > (u64) dev_priv->vram_size)) { +	if (!vmw_kms_validate_mode_vram(dev_priv, +					mode_cmd.pitch, +					mode_cmd.height)) {  		DRM_ERROR("VRAM size is too small for requested mode.\n"); -		return NULL; +		return ERR_PTR(-ENOMEM); +	} + +	/* +	 * Take a reference on the user object of the resource +	 * backing the kms fb. This ensures that user-space handle +	 * lookups on that resource will always work as long as +	 * it's registered with a kms framebuffer. This is important, +	 * since vmw_execbuf_process identifies resources in the +	 * command stream using user-space handles. +	 */ + +	user_obj = ttm_base_object_lookup(tfile, mode_cmd.handle); +	if (unlikely(user_obj == NULL)) { +		DRM_ERROR("Could not locate requested kms frame buffer.\n"); +		return ERR_PTR(-ENOENT);  	}  	/**  	 * End conditioned code.  	 */ -	ret = vmw_user_surface_lookup_handle(dev_priv, tfile, -					     mode_cmd->handle, &surface); +	/* returns either a dmabuf or surface */ +	ret = vmw_user_lookup_handle(dev_priv, tfile, +				     mode_cmd.handle, +				     &surface, &bo);  	if (ret) -		goto try_dmabuf; - -	if (!surface->scanout) -		goto err_not_scanout; - -	ret = vmw_kms_new_framebuffer_surface(dev_priv, file_priv, surface, -					      &vfb, mode_cmd); - -	/* vmw_user_surface_lookup takes one ref so does new_fb */ -	vmw_surface_unreference(&surface); +		goto err_out; + +	/* Create the new framebuffer depending one what we got back */ +	if (bo) +		ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, bo, &vfb, +						     &mode_cmd); +	else if (surface) +		ret = vmw_kms_new_framebuffer_surface(dev_priv, file_priv, +						      surface, &vfb, &mode_cmd); +	else +		BUG(); + +err_out: +	/* vmw_user_lookup_handle takes one ref so does new_fb */ +	if (bo) +		vmw_dmabuf_unreference(&bo); +	if (surface) +		vmw_surface_unreference(&surface);  	if (ret) {  		DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret); +		ttm_base_object_unref(&user_obj);  		return ERR_PTR(ret); -	} +	} else +		vfb->user_obj = user_obj; +  	return &vfb->base; +} -try_dmabuf: -	DRM_INFO("%s: trying buffer\n", __func__); +static const struct drm_mode_config_funcs vmw_kms_funcs = { +	.fb_create = vmw_kms_fb_create, +}; -	ret = vmw_user_dmabuf_lookup(tfile, mode_cmd->handle, &bo); -	if (ret) { -		DRM_ERROR("failed to find buffer: %i\n", ret); -		return ERR_PTR(-ENOENT); +int vmw_kms_present(struct vmw_private *dev_priv, +		    struct drm_file *file_priv, +		    struct vmw_framebuffer *vfb, +		    struct vmw_surface *surface, +		    uint32_t sid, +		    int32_t destX, int32_t destY, +		    struct drm_vmw_rect *clips, +		    uint32_t num_clips) +{ +	struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; +	struct drm_clip_rect *tmp; +	struct drm_crtc *crtc; +	size_t fifo_size; +	int i, k, num_units; +	int ret = 0; /* silence warning */ +	int left, right, top, bottom; + +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdBlitSurfaceToScreen body; +	} *cmd; +	SVGASignedRect *blits; + +	num_units = 0; +	list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) { +		if (crtc->primary->fb != &vfb->base) +			continue; +		units[num_units++] = vmw_crtc_to_du(crtc);  	} -	ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, bo, &vfb, -					     mode_cmd); +	BUG_ON(surface == NULL); +	BUG_ON(!clips || !num_clips); -	/* vmw_user_dmabuf_lookup takes one ref so does new_fb */ -	vmw_dmabuf_unreference(&bo); +	tmp = kzalloc(sizeof(*tmp) * num_clips, GFP_KERNEL); +	if (unlikely(tmp == NULL)) { +		DRM_ERROR("Temporary cliprect memory alloc failed.\n"); +		return -ENOMEM; +	} -	if (ret) { -		DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret); -		return ERR_PTR(ret); +	fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num_clips; +	cmd = kmalloc(fifo_size, GFP_KERNEL); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed to allocate temporary fifo memory.\n"); +		ret = -ENOMEM; +		goto out_free_tmp;  	} -	return &vfb->base; +	left = clips->x; +	right = clips->x + clips->w; +	top = clips->y; +	bottom = clips->y + clips->h; -err_not_scanout: -	DRM_ERROR("surface not marked as scanout\n"); -	/* vmw_user_surface_lookup takes one ref */ -	vmw_surface_unreference(&surface); +	for (i = 1; i < num_clips; i++) { +		left = min_t(int, left, (int)clips[i].x); +		right = max_t(int, right, (int)clips[i].x + clips[i].w); +		top = min_t(int, top, (int)clips[i].y); +		bottom = max_t(int, bottom, (int)clips[i].y + clips[i].h); +	} + +	/* only need to do this once */ +	memset(cmd, 0, fifo_size); +	cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN); + +	blits = (SVGASignedRect *)&cmd[1]; + +	cmd->body.srcRect.left = left; +	cmd->body.srcRect.right = right; +	cmd->body.srcRect.top = top; +	cmd->body.srcRect.bottom = bottom; + +	for (i = 0; i < num_clips; i++) { +		tmp[i].x1 = clips[i].x - left; +		tmp[i].x2 = clips[i].x + clips[i].w - left; +		tmp[i].y1 = clips[i].y - top; +		tmp[i].y2 = clips[i].y + clips[i].h - top; +	} + +	for (k = 0; k < num_units; k++) { +		struct vmw_display_unit *unit = units[k]; +		struct vmw_clip_rect clip; +		int num; + +		clip.x1 = left + destX - unit->crtc.x; +		clip.y1 = top + destY - unit->crtc.y; +		clip.x2 = right + destX - unit->crtc.x; +		clip.y2 = bottom + destY - unit->crtc.y; + +		/* skip any crtcs that misses the clip region */ +		if (clip.x1 >= unit->crtc.mode.hdisplay || +		    clip.y1 >= unit->crtc.mode.vdisplay || +		    clip.x2 <= 0 || clip.y2 <= 0) +			continue; + +		/* +		 * In order for the clip rects to be correctly scaled +		 * the src and dest rects needs to be the same size. +		 */ +		cmd->body.destRect.left = clip.x1; +		cmd->body.destRect.right = clip.x2; +		cmd->body.destRect.top = clip.y1; +		cmd->body.destRect.bottom = clip.y2; + +		/* create a clip rect of the crtc in dest coords */ +		clip.x2 = unit->crtc.mode.hdisplay - clip.x1; +		clip.y2 = unit->crtc.mode.vdisplay - clip.y1; +		clip.x1 = 0 - clip.x1; +		clip.y1 = 0 - clip.y1; + +		/* need to reset sid as it is changed by execbuf */ +		cmd->body.srcImage.sid = sid; +		cmd->body.destScreenId = unit->unit; + +		/* clip and write blits to cmd stream */ +		vmw_clip_cliprects(tmp, num_clips, clip, blits, &num); + +		/* if no cliprects hit skip this */ +		if (num == 0) +			continue; + +		/* recalculate package length */ +		fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num; +		cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header)); +		ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, +					  fifo_size, 0, NULL, NULL); + +		if (unlikely(ret != 0)) +			break; +	} + +	kfree(cmd); +out_free_tmp: +	kfree(tmp); -	return ERR_PTR(-EINVAL); +	return ret;  } -static struct drm_mode_config_funcs vmw_kms_funcs = { -	.fb_create = vmw_kms_fb_create, -}; +int vmw_kms_readback(struct vmw_private *dev_priv, +		     struct drm_file *file_priv, +		     struct vmw_framebuffer *vfb, +		     struct drm_vmw_fence_rep __user *user_fence_rep, +		     struct drm_vmw_rect *clips, +		     uint32_t num_clips) +{ +	struct vmw_framebuffer_dmabuf *vfbd = +		vmw_framebuffer_to_vfbd(&vfb->base); +	struct vmw_dma_buffer *dmabuf = vfbd->buffer; +	struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; +	struct drm_crtc *crtc; +	size_t fifo_size; +	int i, k, ret, num_units, blits_pos; + +	struct { +		uint32_t header; +		SVGAFifoCmdDefineGMRFB body; +	} *cmd; +	struct { +		uint32_t header; +		SVGAFifoCmdBlitScreenToGMRFB body; +	} *blits; + +	num_units = 0; +	list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) { +		if (crtc->primary->fb != &vfb->base) +			continue; +		units[num_units++] = vmw_crtc_to_du(crtc); +	} + +	BUG_ON(dmabuf == NULL); +	BUG_ON(!clips || !num_clips); + +	/* take a safe guess at fifo size */ +	fifo_size = sizeof(*cmd) + sizeof(*blits) * num_clips * num_units; +	cmd = kmalloc(fifo_size, GFP_KERNEL); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed to allocate temporary fifo memory.\n"); +		return -ENOMEM; +	} + +	memset(cmd, 0, fifo_size); +	cmd->header = SVGA_CMD_DEFINE_GMRFB; +	cmd->body.format.bitsPerPixel = vfb->base.bits_per_pixel; +	cmd->body.format.colorDepth = vfb->base.depth; +	cmd->body.format.reserved = 0; +	cmd->body.bytesPerLine = vfb->base.pitches[0]; +	cmd->body.ptr.gmrId = vfb->user_handle; +	cmd->body.ptr.offset = 0; + +	blits = (void *)&cmd[1]; +	blits_pos = 0; +	for (i = 0; i < num_units; i++) { +		struct drm_vmw_rect *c = clips; +		for (k = 0; k < num_clips; k++, c++) { +			/* transform clip coords to crtc origin based coords */ +			int clip_x1 = c->x - units[i]->crtc.x; +			int clip_x2 = c->x - units[i]->crtc.x + c->w; +			int clip_y1 = c->y - units[i]->crtc.y; +			int clip_y2 = c->y - units[i]->crtc.y + c->h; +			int dest_x = c->x; +			int dest_y = c->y; + +			/* compensate for clipping, we negate +			 * a negative number and add that. +			 */ +			if (clip_x1 < 0) +				dest_x += -clip_x1; +			if (clip_y1 < 0) +				dest_y += -clip_y1; + +			/* clip */ +			clip_x1 = max(clip_x1, 0); +			clip_y1 = max(clip_y1, 0); +			clip_x2 = min(clip_x2, units[i]->crtc.mode.hdisplay); +			clip_y2 = min(clip_y2, units[i]->crtc.mode.vdisplay); + +			/* and cull any rects that misses the crtc */ +			if (clip_x1 >= units[i]->crtc.mode.hdisplay || +			    clip_y1 >= units[i]->crtc.mode.vdisplay || +			    clip_x2 <= 0 || clip_y2 <= 0) +				continue; + +			blits[blits_pos].header = SVGA_CMD_BLIT_SCREEN_TO_GMRFB; +			blits[blits_pos].body.srcScreenId = units[i]->unit; +			blits[blits_pos].body.destOrigin.x = dest_x; +			blits[blits_pos].body.destOrigin.y = dest_y; + +			blits[blits_pos].body.srcRect.left = clip_x1; +			blits[blits_pos].body.srcRect.top = clip_y1; +			blits[blits_pos].body.srcRect.right = clip_x2; +			blits[blits_pos].body.srcRect.bottom = clip_y2; +			blits_pos++; +		} +	} +	/* reset size here and use calculated exact size from loops */ +	fifo_size = sizeof(*cmd) + sizeof(*blits) * blits_pos; + +	ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, fifo_size, +				  0, user_fence_rep, NULL); + +	kfree(cmd); + +	return ret; +}  int vmw_kms_init(struct vmw_private *dev_priv)  { @@ -929,7 +1474,9 @@ int vmw_kms_init(struct vmw_private *dev_priv)  	dev->mode_config.max_width = 8192;  	dev->mode_config.max_height = 8192; -	ret = vmw_kms_init_legacy_display_system(dev_priv); +	ret = vmw_kms_init_screen_object_display(dev_priv); +	if (ret) /* Fallback */ +		(void)vmw_kms_init_legacy_display_system(dev_priv);  	return 0;  } @@ -942,7 +1489,10 @@ int vmw_kms_close(struct vmw_private *dev_priv)  	 * drm_encoder_cleanup which takes the lock we deadlock.  	 */  	drm_mode_config_cleanup(dev_priv->dev); -	vmw_kms_close_legacy_display_system(dev_priv); +	if (dev_priv->sou_priv) +		vmw_kms_close_screen_object_display(dev_priv); +	else +		vmw_kms_close_legacy_display_system(dev_priv);  	return 0;  } @@ -971,7 +1521,7 @@ int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,  	obj = drm_mode_object_find(dev, arg->crtc_id, DRM_MODE_OBJECT_CRTC);  	if (!obj) { -		ret = -EINVAL; +		ret = -ENOENT;  		goto out;  	} @@ -987,9 +1537,9 @@ out:  	return ret;  } -void vmw_kms_write_svga(struct vmw_private *vmw_priv, +int vmw_kms_write_svga(struct vmw_private *vmw_priv,  			unsigned width, unsigned height, unsigned pitch, -			unsigned bbp, unsigned depth) +			unsigned bpp, unsigned depth)  {  	if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK)  		vmw_write(vmw_priv, SVGA_REG_PITCHLOCK, pitch); @@ -997,11 +1547,15 @@ void vmw_kms_write_svga(struct vmw_private *vmw_priv,  		iowrite32(pitch, vmw_priv->mmio_virt + SVGA_FIFO_PITCHLOCK);  	vmw_write(vmw_priv, SVGA_REG_WIDTH, width);  	vmw_write(vmw_priv, SVGA_REG_HEIGHT, height); -	vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, bbp); -	vmw_write(vmw_priv, SVGA_REG_DEPTH, 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); +	vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, bpp); + +	if (vmw_read(vmw_priv, SVGA_REG_DEPTH) != depth) { +		DRM_ERROR("Invalid depth %u for %u bpp, host expects %u\n", +			  depth, bpp, vmw_read(vmw_priv, SVGA_REG_DEPTH)); +		return -EINVAL; +	} + +	return 0;  }  int vmw_kms_save_vga(struct vmw_private *vmw_priv) @@ -1011,12 +1565,7 @@ int vmw_kms_save_vga(struct vmw_private *vmw_priv)  	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_depth = vmw_read(vmw_priv, SVGA_REG_DEPTH);  	vmw_priv->vga_bpp = vmw_read(vmw_priv, SVGA_REG_BITS_PER_PIXEL); -	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_blue_mask = vmw_read(vmw_priv, SVGA_REG_BLUE_MASK); -	vmw_priv->vga_green_mask = vmw_read(vmw_priv, SVGA_REG_GREEN_MASK);  	if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK)  		vmw_priv->vga_pitchlock =  		  vmw_read(vmw_priv, SVGA_REG_PITCHLOCK); @@ -1065,12 +1614,7 @@ 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_DEPTH, vmw_priv->vga_depth);  	vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, vmw_priv->vga_bpp); -	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);  	if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK)  		vmw_write(vmw_priv, SVGA_REG_PITCHLOCK,  			  vmw_priv->vga_pitchlock); @@ -1095,30 +1639,407 @@ int vmw_kms_restore_vga(struct vmw_private *vmw_priv)  	return 0;  } +bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, +				uint32_t pitch, +				uint32_t height) +{ +	return ((u64) pitch * (u64) height) < (u64) dev_priv->prim_bb_mem; +} + + +/** + * Function called by DRM code called with vbl_lock held. + */ +u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc) +{ +	return 0; +} + +/** + * Function called by DRM code called with vbl_lock held. + */ +int vmw_enable_vblank(struct drm_device *dev, int crtc) +{ +	return -ENOSYS; +} + +/** + * Function called by DRM code called with vbl_lock held. + */ +void vmw_disable_vblank(struct drm_device *dev, int crtc) +{ +} + + +/* + * Small shared kms functions. + */ + +static int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num, +			 struct drm_vmw_rect *rects) +{ +	struct drm_device *dev = dev_priv->dev; +	struct vmw_display_unit *du; +	struct drm_connector *con; + +	mutex_lock(&dev->mode_config.mutex); + +#if 0 +	{ +		unsigned int i; + +		DRM_INFO("%s: new layout ", __func__); +		for (i = 0; i < num; i++) +			DRM_INFO("(%i, %i %ux%u) ", rects[i].x, rects[i].y, +				 rects[i].w, rects[i].h); +		DRM_INFO("\n"); +	} +#endif + +	list_for_each_entry(con, &dev->mode_config.connector_list, head) { +		du = vmw_connector_to_du(con); +		if (num > du->unit) { +			du->pref_width = rects[du->unit].w; +			du->pref_height = rects[du->unit].h; +			du->pref_active = true; +			du->gui_x = rects[du->unit].x; +			du->gui_y = rects[du->unit].y; +		} else { +			du->pref_width = 800; +			du->pref_height = 600; +			du->pref_active = false; +		} +		con->status = vmw_du_connector_detect(con, true); +	} + +	mutex_unlock(&dev->mode_config.mutex); + +	return 0; +} + +int vmw_du_page_flip(struct drm_crtc *crtc, +		     struct drm_framebuffer *fb, +		     struct drm_pending_vblank_event *event, +		     uint32_t page_flip_flags) +{ +	struct vmw_private *dev_priv = vmw_priv(crtc->dev); +	struct drm_framebuffer *old_fb = crtc->primary->fb; +	struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(fb); +	struct drm_file *file_priv ; +	struct vmw_fence_obj *fence = NULL; +	struct drm_clip_rect clips; +	int ret; + +	if (event == NULL) +		return -EINVAL; + +	/* require ScreenObject support for page flipping */ +	if (!dev_priv->sou_priv) +		return -ENOSYS; + +	file_priv = event->base.file_priv; +	if (!vmw_kms_screen_object_flippable(dev_priv, crtc)) +		return -EINVAL; + +	crtc->primary->fb = fb; + +	/* do a full screen dirty update */ +	clips.x1 = clips.y1 = 0; +	clips.x2 = fb->width; +	clips.y2 = fb->height; + +	if (vfb->dmabuf) +		ret = do_dmabuf_dirty_sou(file_priv, dev_priv, vfb, +					  0, 0, &clips, 1, 1, &fence); +	else +		ret = do_surface_dirty_sou(dev_priv, file_priv, vfb, +					   0, 0, &clips, 1, 1, &fence); + + +	if (ret != 0) +		goto out_no_fence; +	if (!fence) { +		ret = -EINVAL; +		goto out_no_fence; +	} + +	ret = vmw_event_fence_action_queue(file_priv, fence, +					   &event->base, +					   &event->event.tv_sec, +					   &event->event.tv_usec, +					   true); + +	/* +	 * No need to hold on to this now. The only cleanup +	 * we need to do if we fail is unref the fence. +	 */ +	vmw_fence_obj_unreference(&fence); + +	if (vmw_crtc_to_du(crtc)->is_implicit) +		vmw_kms_screen_object_update_implicit_fb(dev_priv, crtc); + +	return ret; + +out_no_fence: +	crtc->primary->fb = old_fb; +	return ret; +} + + +void vmw_du_crtc_save(struct drm_crtc *crtc) +{ +} + +void vmw_du_crtc_restore(struct drm_crtc *crtc) +{ +} + +void vmw_du_crtc_gamma_set(struct drm_crtc *crtc, +			   u16 *r, u16 *g, u16 *b, +			   uint32_t start, uint32_t size) +{ +	struct vmw_private *dev_priv = vmw_priv(crtc->dev); +	int i; + +	for (i = 0; i < size; i++) { +		DRM_DEBUG("%d r/g/b = 0x%04x / 0x%04x / 0x%04x\n", i, +			  r[i], g[i], b[i]); +		vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 0, r[i] >> 8); +		vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 1, g[i] >> 8); +		vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 2, b[i] >> 8); +	} +} + +void vmw_du_connector_dpms(struct drm_connector *connector, int mode) +{ +} + +void vmw_du_connector_save(struct drm_connector *connector) +{ +} + +void vmw_du_connector_restore(struct drm_connector *connector) +{ +} + +enum drm_connector_status +vmw_du_connector_detect(struct drm_connector *connector, bool force) +{ +	uint32_t num_displays; +	struct drm_device *dev = connector->dev; +	struct vmw_private *dev_priv = vmw_priv(dev); +	struct vmw_display_unit *du = vmw_connector_to_du(connector); + +	mutex_lock(&dev_priv->hw_mutex); +	num_displays = vmw_read(dev_priv, SVGA_REG_NUM_DISPLAYS); +	mutex_unlock(&dev_priv->hw_mutex); + +	return ((vmw_connector_to_du(connector)->unit < num_displays && +		 du->pref_active) ? +		connector_status_connected : connector_status_disconnected); +} + +static struct drm_display_mode vmw_kms_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, 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) }, +}; + +/** + * vmw_guess_mode_timing - Provide fake timings for a + * 60Hz vrefresh mode. + * + * @mode - Pointer to a struct drm_display_mode with hdisplay and vdisplay + * members filled in. + */ +static void vmw_guess_mode_timing(struct drm_display_mode *mode) +{ +	mode->hsync_start = mode->hdisplay + 50; +	mode->hsync_end = mode->hsync_start + 50; +	mode->htotal = mode->hsync_end + 50; + +	mode->vsync_start = mode->vdisplay + 50; +	mode->vsync_end = mode->vsync_start + 50; +	mode->vtotal = mode->vsync_end + 50; + +	mode->clock = (u32)mode->htotal * (u32)mode->vtotal / 100 * 6; +	mode->vrefresh = drm_mode_vrefresh(mode); +} + + +int vmw_du_connector_fill_modes(struct drm_connector *connector, +				uint32_t max_width, uint32_t max_height) +{ +	struct vmw_display_unit *du = vmw_connector_to_du(connector); +	struct drm_device *dev = connector->dev; +	struct vmw_private *dev_priv = vmw_priv(dev); +	struct drm_display_mode *mode = NULL; +	struct drm_display_mode *bmode; +	struct drm_display_mode prefmode = { DRM_MODE("preferred", +		DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, +		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +		DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) +	}; +	int i; + +	/* Add preferred mode */ +	{ +		mode = drm_mode_duplicate(dev, &prefmode); +		if (!mode) +			return 0; +		mode->hdisplay = du->pref_width; +		mode->vdisplay = du->pref_height; +		vmw_guess_mode_timing(mode); + +		if (vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * 2, +					       mode->vdisplay)) { +			drm_mode_probed_add(connector, mode); +		} else { +			drm_mode_destroy(dev, mode); +			mode = NULL; +		} + +		if (du->pref_mode) { +			list_del_init(&du->pref_mode->head); +			drm_mode_destroy(dev, du->pref_mode); +		} + +		/* mode might be null here, this is intended */ +		du->pref_mode = mode; +	} + +	for (i = 0; vmw_kms_connector_builtin[i].type != 0; i++) { +		bmode = &vmw_kms_connector_builtin[i]; +		if (bmode->hdisplay > max_width || +		    bmode->vdisplay > max_height) +			continue; + +		if (!vmw_kms_validate_mode_vram(dev_priv, bmode->hdisplay * 2, +						bmode->vdisplay)) +			continue; + +		mode = drm_mode_duplicate(dev, bmode); +		if (!mode) +			return 0; +		mode->vrefresh = drm_mode_vrefresh(mode); + +		drm_mode_probed_add(connector, mode); +	} + +	/* Move the prefered mode first, help apps pick the right mode. */ +	if (du->pref_mode) +		list_move(&du->pref_mode->head, &connector->probed_modes); + +	drm_mode_connector_list_update(connector, true); + +	return 1; +} + +int vmw_du_connector_set_property(struct drm_connector *connector, +				  struct drm_property *property, +				  uint64_t val) +{ +	return 0; +} + +  int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,  				struct drm_file *file_priv)  {  	struct vmw_private *dev_priv = vmw_priv(dev);  	struct drm_vmw_update_layout_arg *arg =  		(struct drm_vmw_update_layout_arg *)data; -	struct vmw_master *vmaster = vmw_master(file_priv->master);  	void __user *user_rects;  	struct drm_vmw_rect *rects;  	unsigned rects_size;  	int ret; +	int i; +	struct drm_mode_config *mode_config = &dev->mode_config; -	ret = ttm_read_lock(&vmaster->lock, true); +	ret = ttm_read_lock(&dev_priv->reservation_sem, true);  	if (unlikely(ret != 0))  		return ret;  	if (!arg->num_outputs) {  		struct drm_vmw_rect def_rect = {0, 0, 800, 600}; -		vmw_kms_ldu_update_layout(dev_priv, 1, &def_rect); +		vmw_du_update_layout(dev_priv, 1, &def_rect);  		goto out_unlock;  	}  	rects_size = arg->num_outputs * sizeof(struct drm_vmw_rect); -	rects = kzalloc(rects_size, GFP_KERNEL); +	rects = kcalloc(arg->num_outputs, sizeof(struct drm_vmw_rect), +			GFP_KERNEL);  	if (unlikely(!rects)) {  		ret = -ENOMEM;  		goto out_unlock; @@ -1132,23 +2053,22 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,  		goto out_free;  	} -	vmw_kms_ldu_update_layout(dev_priv, arg->num_outputs, rects); +	for (i = 0; i < arg->num_outputs; ++i) { +		if (rects[i].x < 0 || +		    rects[i].y < 0 || +		    rects[i].x + rects[i].w > mode_config->max_width || +		    rects[i].y + rects[i].h > mode_config->max_height) { +			DRM_ERROR("Invalid GUI layout.\n"); +			ret = -EINVAL; +			goto out_free; +		} +	} + +	vmw_du_update_layout(dev_priv, arg->num_outputs, rects);  out_free:  	kfree(rects);  out_unlock: -	ttm_read_unlock(&vmaster->lock); +	ttm_read_unlock(&dev_priv->reservation_sem);  	return ret;  } - -bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, -				uint32_t pitch, -				uint32_t height) -{ -	return ((u64) pitch * (u64) height) < (u64) dev_priv->vram_size; -} - -u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc) -{ -	return 0; -} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index 8a398a0339b..8d038c36bd5 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -28,9 +28,12 @@  #ifndef VMWGFX_KMS_H_  #define VMWGFX_KMS_H_ -#include "drmP.h" +#include <drm/drmP.h> +#include <drm/drm_crtc_helper.h>  #include "vmwgfx_drv.h" +#define VMWGFX_NUM_DISPLAY_UNITS 8 +  #define vmw_framebuffer_to_vfb(x) \  	container_of(x, struct vmw_framebuffer, base) @@ -45,6 +48,9 @@ struct vmw_framebuffer {  	struct drm_framebuffer base;  	int (*pin)(struct vmw_framebuffer *fb);  	int (*unpin)(struct vmw_framebuffer *fb); +	bool dmabuf; +	struct ttm_base_object *user_obj; +	uint32_t user_handle;  }; @@ -57,9 +63,14 @@ struct vmw_framebuffer {  int vmw_cursor_update_image(struct vmw_private *dev_priv,  			    u32 *image, u32 width, u32 height,  			    u32 hotspotX, u32 hotspotY); +int vmw_cursor_update_dmabuf(struct vmw_private *dev_priv, +			     struct vmw_dma_buffer *dmabuf, +			     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.   * @@ -83,22 +94,74 @@ struct vmw_display_unit {  	int hotspot_y;  	unsigned unit; + +	/* +	 * Prefered mode tracking. +	 */ +	unsigned pref_width; +	unsigned pref_height; +	bool pref_active; +	struct drm_display_mode *pref_mode; + +	/* +	 * Gui positioning +	 */ +	int gui_x; +	int gui_y; +	bool is_implicit;  }; +#define vmw_crtc_to_du(x) \ +	container_of(x, struct vmw_display_unit, crtc) +#define vmw_connector_to_du(x) \ +	container_of(x, struct vmw_display_unit, connector) + +  /*   * Shared display unit functions - vmwgfx_kms.c   */  void vmw_display_unit_cleanup(struct vmw_display_unit *du); +int vmw_du_page_flip(struct drm_crtc *crtc, +		     struct drm_framebuffer *fb, +		     struct drm_pending_vblank_event *event, +		     uint32_t page_flip_flags); +void vmw_du_crtc_save(struct drm_crtc *crtc); +void vmw_du_crtc_restore(struct drm_crtc *crtc); +void vmw_du_crtc_gamma_set(struct drm_crtc *crtc, +			   u16 *r, u16 *g, u16 *b, +			   uint32_t start, uint32_t size);  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); +void vmw_du_connector_dpms(struct drm_connector *connector, int mode); +void vmw_du_connector_save(struct drm_connector *connector); +void vmw_du_connector_restore(struct drm_connector *connector); +enum drm_connector_status +vmw_du_connector_detect(struct drm_connector *connector, bool force); +int vmw_du_connector_fill_modes(struct drm_connector *connector, +				uint32_t max_width, uint32_t max_height); +int vmw_du_connector_set_property(struct drm_connector *connector, +				  struct drm_property *property, +				  uint64_t val); +  /*   * Legacy display unit functions - vmwgfx_ldu.c   */  int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv);  int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv); -int vmw_kms_ldu_update_layout(struct vmw_private *dev_priv, unsigned num, + +/* + * Screen Objects display functions - vmwgfx_scrn.c + */ +int vmw_kms_init_screen_object_display(struct vmw_private *dev_priv); +int vmw_kms_close_screen_object_display(struct vmw_private *dev_priv); +int vmw_kms_sou_update_layout(struct vmw_private *dev_priv, unsigned num,  			      struct drm_vmw_rect *rects); +bool vmw_kms_screen_object_flippable(struct vmw_private *dev_priv, +				     struct drm_crtc *crtc); +void vmw_kms_screen_object_update_implicit_fb(struct vmw_private *dev_priv, +					      struct drm_crtc *crtc); +  #endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 29113c9b26a..b2b9bd23aee 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -27,7 +27,6 @@  #include "vmwgfx_kms.h" -#define VMWGFX_LDU_NUM_DU 8  #define vmw_crtc_to_ldu(x) \  	container_of(x, struct vmw_legacy_display_unit, base.crtc) @@ -51,11 +50,6 @@ struct vmw_legacy_display {  struct vmw_legacy_display_unit {  	struct vmw_display_unit base; -	unsigned pref_width; -	unsigned pref_height; -	bool pref_active; -	struct drm_display_mode *pref_mode; -  	struct list_head active;  }; @@ -71,20 +65,6 @@ static void vmw_ldu_destroy(struct vmw_legacy_display_unit *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 start, uint32_t size) -{ -} -  static void vmw_ldu_crtc_destroy(struct drm_crtc *crtc)  {  	vmw_ldu_destroy(vmw_crtc_to_ldu(crtc)); @@ -94,9 +74,10 @@ 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 vmw_display_unit *du = NULL;  	struct drm_framebuffer *fb = NULL;  	struct drm_crtc *crtc = NULL; -	int i = 0; +	int i = 0, ret;  	/* If there is no display topology the host just assumes  	 * that the guest will set the same layout as the host. @@ -112,19 +93,17 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv)  		if (crtc == NULL)  			return 0; -		fb = entry->base.crtc.fb; - -		vmw_kms_write_svga(dev_priv, w, h, fb->pitch, -				   fb->bits_per_pixel, fb->depth); +		fb = entry->base.crtc.primary->fb; -		return 0; +		return vmw_kms_write_svga(dev_priv, w, h, fb->pitches[0], +					  fb->bits_per_pixel, fb->depth);  	}  	if (!list_empty(&lds->active)) {  		entry = list_entry(lds->active.next, typeof(*entry), active); -		fb = entry->base.crtc.fb; +		fb = entry->base.crtc.primary->fb; -		vmw_kms_write_svga(dev_priv, fb->width, fb->height, fb->pitch, +		vmw_kms_write_svga(dev_priv, fb->width, fb->height, fb->pitches[0],  				   fb->bits_per_pixel, fb->depth);  	} @@ -151,6 +130,25 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv)  	lds->last_num_active = lds->num_active; + +	/* Find the first du with a cursor. */ +	list_for_each_entry(entry, &lds->active, active) { +		du = &entry->base; + +		if (!du->cursor_dmabuf) +			continue; + +		ret = vmw_cursor_update_dmabuf(dev_priv, +					       du->cursor_dmabuf, +					       64, 64, +					       du->hotspot_x, +					       du->hotspot_y); +		if (ret == 0) +			break; + +		DRM_ERROR("Could not update cursor image\n"); +	} +  	return 0;  } @@ -261,13 +259,12 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)  		connector->encoder = NULL;  		encoder->crtc = NULL; -		crtc->fb = NULL; +		crtc->primary->fb = NULL; +		crtc->enabled = false;  		vmw_ldu_del_active(dev_priv, ldu); -		vmw_ldu_commit_list(dev_priv); - -		return 0; +		return vmw_ldu_commit_list(dev_priv);  	} @@ -283,30 +280,30 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)  	vmw_fb_off(dev_priv); -	crtc->fb = fb; +	crtc->primary->fb = fb;  	encoder->crtc = crtc;  	connector->encoder = encoder;  	crtc->x = set->x;  	crtc->y = set->y;  	crtc->mode = *mode; +	crtc->enabled = true;  	vmw_ldu_add_active(dev_priv, ldu, vfb); -	vmw_ldu_commit_list(dev_priv); - -	return 0; +	return vmw_ldu_commit_list(dev_priv);  }  static struct drm_crtc_funcs vmw_legacy_crtc_funcs = { -	.save = vmw_ldu_crtc_save, -	.restore = vmw_ldu_crtc_restore, +	.save = vmw_du_crtc_save, +	.restore = vmw_du_crtc_restore,  	.cursor_set = vmw_du_crtc_cursor_set,  	.cursor_move = vmw_du_crtc_cursor_move, -	.gamma_set = vmw_ldu_crtc_gamma_set, +	.gamma_set = vmw_du_crtc_gamma_set,  	.destroy = vmw_ldu_crtc_destroy,  	.set_config = vmw_ldu_crtc_set_config,  }; +  /*   * Legacy Display Unit encoder functions   */ @@ -324,182 +321,18 @@ static struct drm_encoder_funcs vmw_legacy_encoder_funcs = {   * 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, -				 bool force) -{ -	if (vmw_connector_to_ldu(connector)->pref_active) -		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, 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 vmw_legacy_display_unit *ldu = vmw_connector_to_ldu(connector); -	struct drm_device *dev = connector->dev; -	struct vmw_private *dev_priv = vmw_priv(dev); -	struct drm_display_mode *mode = NULL; -	struct drm_display_mode *bmode; -	struct drm_display_mode prefmode = { DRM_MODE("preferred", -		DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, -		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -		DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) -	}; -	int i; - -	/* Add preferred mode */ -	{ -		mode = drm_mode_duplicate(dev, &prefmode); -		if (!mode) -			return 0; -		mode->hdisplay = ldu->pref_width; -		mode->vdisplay = ldu->pref_height; -		mode->vrefresh = drm_mode_vrefresh(mode); -		if (vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * 2, -					       mode->vdisplay)) { -			drm_mode_probed_add(connector, mode); - -			if (ldu->pref_mode) { -				list_del_init(&ldu->pref_mode->head); -				drm_mode_destroy(dev, ldu->pref_mode); -			} - -			ldu->pref_mode = mode; -		} -	} - -	for (i = 0; vmw_ldu_connector_builtin[i].type != 0; i++) { -		bmode = &vmw_ldu_connector_builtin[i]; -		if (bmode->hdisplay > max_width || -		    bmode->vdisplay > max_height) -			continue; - -		if (!vmw_kms_validate_mode_vram(dev_priv, bmode->hdisplay * 2, -						bmode->vdisplay)) -			continue; - -		mode = drm_mode_duplicate(dev, bmode); -		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, +	.dpms = vmw_du_connector_dpms, +	.save = vmw_du_connector_save, +	.restore = vmw_du_connector_restore, +	.detect = vmw_du_connector_detect, +	.fill_modes = vmw_du_connector_fill_modes, +	.set_property = vmw_du_connector_set_property,  	.destroy = vmw_ldu_connector_destroy,  }; @@ -522,24 +355,29 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)  	INIT_LIST_HEAD(&ldu->active); -	ldu->pref_active = (unit == 0); -	ldu->pref_width = 800; -	ldu->pref_height = 600; -	ldu->pref_mode = NULL; +	ldu->base.pref_active = (unit == 0); +	ldu->base.pref_width = dev_priv->initial_width; +	ldu->base.pref_height = dev_priv->initial_height; +	ldu->base.pref_mode = NULL; +	ldu->base.is_implicit = true;  	drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, -			   DRM_MODE_CONNECTOR_LVDS); -	connector->status = vmw_ldu_connector_detect(connector, true); +			   DRM_MODE_CONNECTOR_VIRTUAL); +	connector->status = vmw_du_connector_detect(connector, true);  	drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs, -			 DRM_MODE_ENCODER_LVDS); +			 DRM_MODE_ENCODER_VIRTUAL);  	drm_mode_connector_attach_encoder(connector, encoder);  	encoder->possible_crtcs = (1 << unit);  	encoder->possible_clones = 0; +	(void) drm_sysfs_connector_add(connector); +  	drm_crtc_init(dev, crtc, &vmw_legacy_crtc_funcs); -	drm_connector_attach_property(connector, +	drm_mode_crtc_set_gamma_size(crtc, 256); + +	drm_object_attach_property(&connector->base,  				      dev->mode_config.dirty_info_property,  				      1); @@ -549,8 +387,7 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)  int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv)  {  	struct drm_device *dev = dev_priv->dev; -	int i; -	int ret; +	int i, ret;  	if (dev_priv->ldu_priv) {  		DRM_INFO("ldu system already on\n"); @@ -558,7 +395,6 @@ int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv)  	}  	dev_priv->ldu_priv = kmalloc(sizeof(*dev_priv->ldu_priv), GFP_KERNEL); -  	if (!dev_priv->ldu_priv)  		return -ENOMEM; @@ -567,18 +403,31 @@ int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv)  	dev_priv->ldu_priv->last_num_active = 0;  	dev_priv->ldu_priv->fb = NULL; -	drm_mode_create_dirty_info_property(dev_priv->dev); +	/* for old hardware without multimon only enable one display */ +	if (dev_priv->capabilities & SVGA_CAP_MULTIMON) +		ret = drm_vblank_init(dev, VMWGFX_NUM_DISPLAY_UNITS); +	else +		ret = drm_vblank_init(dev, 1); +	if (ret != 0) +		goto err_free; + +	ret = drm_mode_create_dirty_info_property(dev); +	if (ret != 0) +		goto err_vblank_cleanup; -	if (dev_priv->capabilities & SVGA_CAP_MULTIMON) { -		for (i = 0; i < VMWGFX_LDU_NUM_DU; ++i) +	if (dev_priv->capabilities & SVGA_CAP_MULTIMON) +		for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i)  			vmw_ldu_init(dev_priv, i); -		ret = drm_vblank_init(dev, VMWGFX_LDU_NUM_DU); -	} else { -		/* for old hardware without multimon only enable one display */ +	else  		vmw_ldu_init(dev_priv, 0); -		ret = drm_vblank_init(dev, 1); -	} +	return 0; + +err_vblank_cleanup: +	drm_vblank_cleanup(dev); +err_free: +	kfree(dev_priv->ldu_priv); +	dev_priv->ldu_priv = NULL;  	return ret;  } @@ -586,52 +435,14 @@ int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv)  {  	struct drm_device *dev = dev_priv->dev; -	drm_vblank_cleanup(dev);  	if (!dev_priv->ldu_priv)  		return -ENOSYS; +	drm_vblank_cleanup(dev); +  	BUG_ON(!list_empty(&dev_priv->ldu_priv->active));  	kfree(dev_priv->ldu_priv);  	return 0;  } - -int vmw_kms_ldu_update_layout(struct vmw_private *dev_priv, unsigned num, -			      struct drm_vmw_rect *rects) -{ -	struct drm_device *dev = dev_priv->dev; -	struct vmw_legacy_display_unit *ldu; -	struct drm_connector *con; -	int i; - -	mutex_lock(&dev->mode_config.mutex); - -#if 0 -	DRM_INFO("%s: new layout ", __func__); -	for (i = 0; i < (int)num; i++) -		DRM_INFO("(%i, %i %ux%u) ", rects[i].x, rects[i].y, -			 rects[i].w, rects[i].h); -	DRM_INFO("\n"); -#else -	(void)i; -#endif - -	list_for_each_entry(con, &dev->mode_config.connector_list, head) { -		ldu = vmw_connector_to_ldu(con); -		if (num > ldu->base.unit) { -			ldu->pref_width = rects[ldu->base.unit].w; -			ldu->pref_height = rects[ldu->base.unit].h; -			ldu->pref_active = true; -		} else { -			ldu->pref_width = 800; -			ldu->pref_height = 600; -			ldu->pref_active = false; -		} -		con->status = vmw_ldu_connector_detect(con, true); -	} - -	mutex_unlock(&dev->mode_config.mutex); - -	return 0; -} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_marker.c b/drivers/gpu/drm/vmwgfx/vmwgfx_marker.c new file mode 100644 index 00000000000..8a8725c2716 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_marker.c @@ -0,0 +1,171 @@ +/************************************************************************** + * + * Copyright (C) 2010 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" + +struct vmw_marker { +	struct list_head head; +	uint32_t seqno; +	struct timespec submitted; +}; + +void vmw_marker_queue_init(struct vmw_marker_queue *queue) +{ +	INIT_LIST_HEAD(&queue->head); +	queue->lag = ns_to_timespec(0); +	getrawmonotonic(&queue->lag_time); +	spin_lock_init(&queue->lock); +} + +void vmw_marker_queue_takedown(struct vmw_marker_queue *queue) +{ +	struct vmw_marker *marker, *next; + +	spin_lock(&queue->lock); +	list_for_each_entry_safe(marker, next, &queue->head, head) { +		kfree(marker); +	} +	spin_unlock(&queue->lock); +} + +int vmw_marker_push(struct vmw_marker_queue *queue, +		   uint32_t seqno) +{ +	struct vmw_marker *marker = kmalloc(sizeof(*marker), GFP_KERNEL); + +	if (unlikely(!marker)) +		return -ENOMEM; + +	marker->seqno = seqno; +	getrawmonotonic(&marker->submitted); +	spin_lock(&queue->lock); +	list_add_tail(&marker->head, &queue->head); +	spin_unlock(&queue->lock); + +	return 0; +} + +int vmw_marker_pull(struct vmw_marker_queue *queue, +		   uint32_t signaled_seqno) +{ +	struct vmw_marker *marker, *next; +	struct timespec now; +	bool updated = false; + +	spin_lock(&queue->lock); +	getrawmonotonic(&now); + +	if (list_empty(&queue->head)) { +		queue->lag = ns_to_timespec(0); +		queue->lag_time = now; +		updated = true; +		goto out_unlock; +	} + +	list_for_each_entry_safe(marker, next, &queue->head, head) { +		if (signaled_seqno - marker->seqno > (1 << 30)) +			continue; + +		queue->lag = timespec_sub(now, marker->submitted); +		queue->lag_time = now; +		updated = true; +		list_del(&marker->head); +		kfree(marker); +	} + +out_unlock: +	spin_unlock(&queue->lock); + +	return (updated) ? 0 : -EBUSY; +} + +static struct timespec vmw_timespec_add(struct timespec t1, +					struct timespec t2) +{ +	t1.tv_sec += t2.tv_sec; +	t1.tv_nsec += t2.tv_nsec; +	if (t1.tv_nsec >= 1000000000L) { +		t1.tv_sec += 1; +		t1.tv_nsec -= 1000000000L; +	} + +	return t1; +} + +static struct timespec vmw_fifo_lag(struct vmw_marker_queue *queue) +{ +	struct timespec now; + +	spin_lock(&queue->lock); +	getrawmonotonic(&now); +	queue->lag = vmw_timespec_add(queue->lag, +				      timespec_sub(now, queue->lag_time)); +	queue->lag_time = now; +	spin_unlock(&queue->lock); +	return queue->lag; +} + + +static bool vmw_lag_lt(struct vmw_marker_queue *queue, +		       uint32_t us) +{ +	struct timespec lag, cond; + +	cond = ns_to_timespec((s64) us * 1000); +	lag = vmw_fifo_lag(queue); +	return (timespec_compare(&lag, &cond) < 1); +} + +int vmw_wait_lag(struct vmw_private *dev_priv, +		 struct vmw_marker_queue *queue, uint32_t us) +{ +	struct vmw_marker *marker; +	uint32_t seqno; +	int ret; + +	while (!vmw_lag_lt(queue, us)) { +		spin_lock(&queue->lock); +		if (list_empty(&queue->head)) +			seqno = atomic_read(&dev_priv->marker_seq); +		else { +			marker = list_first_entry(&queue->head, +						 struct vmw_marker, head); +			seqno = marker->seqno; +		} +		spin_unlock(&queue->lock); + +		ret = vmw_wait_seqno(dev_priv, false, seqno, true, +					3*HZ); + +		if (unlikely(ret != 0)) +			return ret; + +		(void) vmw_marker_pull(queue, seqno); +	} +	return 0; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c new file mode 100644 index 00000000000..04a64b8cd3c --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c @@ -0,0 +1,656 @@ +/************************************************************************** + * + * Copyright © 2012 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" + +/* + * If we set up the screen target otable, screen objects stop working. + */ + +#define VMW_OTABLE_SETUP_SUB ((VMWGFX_ENABLE_SCREEN_TARGET_OTABLE) ? 0 : 1) + +#ifdef CONFIG_64BIT +#define VMW_PPN_SIZE 8 +#define VMW_MOBFMT_PTDEPTH_0 SVGA3D_MOBFMT_PTDEPTH64_0 +#define VMW_MOBFMT_PTDEPTH_1 SVGA3D_MOBFMT_PTDEPTH64_1 +#define VMW_MOBFMT_PTDEPTH_2 SVGA3D_MOBFMT_PTDEPTH64_2 +#else +#define VMW_PPN_SIZE 4 +#define VMW_MOBFMT_PTDEPTH_0 SVGA3D_MOBFMT_PTDEPTH_0 +#define VMW_MOBFMT_PTDEPTH_1 SVGA3D_MOBFMT_PTDEPTH_1 +#define VMW_MOBFMT_PTDEPTH_2 SVGA3D_MOBFMT_PTDEPTH_2 +#endif + +/* + * struct vmw_mob - Structure containing page table and metadata for a + * Guest Memory OBject. + * + * @num_pages       Number of pages that make up the page table. + * @pt_level        The indirection level of the page table. 0-2. + * @pt_root_page    DMA address of the level 0 page of the page table. + */ +struct vmw_mob { +	struct ttm_buffer_object *pt_bo; +	unsigned long num_pages; +	unsigned pt_level; +	dma_addr_t pt_root_page; +	uint32_t id; +}; + +/* + * struct vmw_otable - Guest Memory OBject table metadata + * + * @size:           Size of the table (page-aligned). + * @page_table:     Pointer to a struct vmw_mob holding the page table. + */ +struct vmw_otable { +	unsigned long size; +	struct vmw_mob *page_table; +}; + +static int vmw_mob_pt_populate(struct vmw_private *dev_priv, +			       struct vmw_mob *mob); +static void vmw_mob_pt_setup(struct vmw_mob *mob, +			     struct vmw_piter data_iter, +			     unsigned long num_data_pages); + +/* + * vmw_setup_otable_base - Issue an object table base setup command to + * the device + * + * @dev_priv:       Pointer to a device private structure + * @type:           Type of object table base + * @offset          Start of table offset into dev_priv::otable_bo + * @otable          Pointer to otable metadata; + * + * This function returns -ENOMEM if it fails to reserve fifo space, + * and may block waiting for fifo space. + */ +static int vmw_setup_otable_base(struct vmw_private *dev_priv, +				 SVGAOTableType type, +				 unsigned long offset, +				 struct vmw_otable *otable) +{ +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdSetOTableBase64 body; +	} *cmd; +	struct vmw_mob *mob; +	const struct vmw_sg_table *vsgt; +	struct vmw_piter iter; +	int ret; + +	BUG_ON(otable->page_table != NULL); + +	vsgt = vmw_bo_sg_table(dev_priv->otable_bo); +	vmw_piter_start(&iter, vsgt, offset >> PAGE_SHIFT); +	WARN_ON(!vmw_piter_next(&iter)); + +	mob = vmw_mob_create(otable->size >> PAGE_SHIFT); +	if (unlikely(mob == NULL)) { +		DRM_ERROR("Failed creating OTable page table.\n"); +		return -ENOMEM; +	} + +	if (otable->size <= PAGE_SIZE) { +		mob->pt_level = VMW_MOBFMT_PTDEPTH_0; +		mob->pt_root_page = vmw_piter_dma_addr(&iter); +	} else if (vsgt->num_regions == 1) { +		mob->pt_level = SVGA3D_MOBFMT_RANGE; +		mob->pt_root_page = vmw_piter_dma_addr(&iter); +	} else { +		ret = vmw_mob_pt_populate(dev_priv, mob); +		if (unlikely(ret != 0)) +			goto out_no_populate; + +		vmw_mob_pt_setup(mob, iter, otable->size >> PAGE_SHIFT); +		mob->pt_level += VMW_MOBFMT_PTDEPTH_1 - SVGA3D_MOBFMT_PTDEPTH_1; +	} + +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for OTable setup.\n"); +		ret = -ENOMEM; +		goto out_no_fifo; +	} + +	memset(cmd, 0, sizeof(*cmd)); +	cmd->header.id = SVGA_3D_CMD_SET_OTABLE_BASE64; +	cmd->header.size = sizeof(cmd->body); +	cmd->body.type = type; +	cmd->body.baseAddress = cpu_to_le64(mob->pt_root_page >> PAGE_SHIFT); +	cmd->body.sizeInBytes = otable->size; +	cmd->body.validSizeInBytes = 0; +	cmd->body.ptDepth = mob->pt_level; + +	/* +	 * The device doesn't support this, But the otable size is +	 * determined at compile-time, so this BUG shouldn't trigger +	 * randomly. +	 */ +	BUG_ON(mob->pt_level == VMW_MOBFMT_PTDEPTH_2); + +	vmw_fifo_commit(dev_priv, sizeof(*cmd)); +	otable->page_table = mob; + +	return 0; + +out_no_fifo: +out_no_populate: +	vmw_mob_destroy(mob); +	return ret; +} + +/* + * vmw_takedown_otable_base - Issue an object table base takedown command + * to the device + * + * @dev_priv:       Pointer to a device private structure + * @type:           Type of object table base + * + */ +static void vmw_takedown_otable_base(struct vmw_private *dev_priv, +				     SVGAOTableType type, +				     struct vmw_otable *otable) +{ +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdSetOTableBase body; +	} *cmd; +	struct ttm_buffer_object *bo; + +	if (otable->page_table == NULL) +		return; + +	bo = otable->page_table->pt_bo; +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for OTable " +			  "takedown.\n"); +	} else { +		memset(cmd, 0, sizeof(*cmd)); +		cmd->header.id = SVGA_3D_CMD_SET_OTABLE_BASE; +		cmd->header.size = sizeof(cmd->body); +		cmd->body.type = type; +		cmd->body.baseAddress = 0; +		cmd->body.sizeInBytes = 0; +		cmd->body.validSizeInBytes = 0; +		cmd->body.ptDepth = SVGA3D_MOBFMT_INVALID; +		vmw_fifo_commit(dev_priv, sizeof(*cmd)); +	} + +	if (bo) { +		int ret; + +		ret = ttm_bo_reserve(bo, false, true, false, NULL); +		BUG_ON(ret != 0); + +		vmw_fence_single_bo(bo, NULL); +		ttm_bo_unreserve(bo); +	} + +	vmw_mob_destroy(otable->page_table); +	otable->page_table = NULL; +} + +/* + * vmw_otables_setup - Set up guest backed memory object tables + * + * @dev_priv:       Pointer to a device private structure + * + * Takes care of the device guest backed surface + * initialization, by setting up the guest backed memory object tables. + * Returns 0 on success and various error codes on failure. A succesful return + * means the object tables can be taken down using the vmw_otables_takedown + * function. + */ +int vmw_otables_setup(struct vmw_private *dev_priv) +{ +	unsigned long offset; +	unsigned long bo_size; +	struct vmw_otable *otables; +	SVGAOTableType i; +	int ret; + +	otables = kzalloc(SVGA_OTABLE_DX9_MAX * sizeof(*otables), +			  GFP_KERNEL); +	if (unlikely(otables == NULL)) { +		DRM_ERROR("Failed to allocate space for otable " +			  "metadata.\n"); +		return -ENOMEM; +	} + +	otables[SVGA_OTABLE_MOB].size = +		VMWGFX_NUM_MOB * SVGA3D_OTABLE_MOB_ENTRY_SIZE; +	otables[SVGA_OTABLE_SURFACE].size = +		VMWGFX_NUM_GB_SURFACE * SVGA3D_OTABLE_SURFACE_ENTRY_SIZE; +	otables[SVGA_OTABLE_CONTEXT].size = +		VMWGFX_NUM_GB_CONTEXT * SVGA3D_OTABLE_CONTEXT_ENTRY_SIZE; +	otables[SVGA_OTABLE_SHADER].size = +		VMWGFX_NUM_GB_SHADER * SVGA3D_OTABLE_SHADER_ENTRY_SIZE; +	otables[SVGA_OTABLE_SCREEN_TARGET].size = +		VMWGFX_NUM_GB_SCREEN_TARGET * +		SVGA3D_OTABLE_SCREEN_TARGET_ENTRY_SIZE; + +	bo_size = 0; +	for (i = 0; i < SVGA_OTABLE_DX9_MAX; ++i) { +		otables[i].size = +			(otables[i].size + PAGE_SIZE - 1) & PAGE_MASK; +		bo_size += otables[i].size; +	} + +	ret = ttm_bo_create(&dev_priv->bdev, bo_size, +			    ttm_bo_type_device, +			    &vmw_sys_ne_placement, +			    0, false, NULL, +			    &dev_priv->otable_bo); + +	if (unlikely(ret != 0)) +		goto out_no_bo; + +	ret = ttm_bo_reserve(dev_priv->otable_bo, false, true, false, NULL); +	BUG_ON(ret != 0); +	ret = vmw_bo_driver.ttm_tt_populate(dev_priv->otable_bo->ttm); +	if (unlikely(ret != 0)) +		goto out_unreserve; +	ret = vmw_bo_map_dma(dev_priv->otable_bo); +	if (unlikely(ret != 0)) +		goto out_unreserve; + +	ttm_bo_unreserve(dev_priv->otable_bo); + +	offset = 0; +	for (i = 0; i < SVGA_OTABLE_DX9_MAX - VMW_OTABLE_SETUP_SUB; ++i) { +		ret = vmw_setup_otable_base(dev_priv, i, offset, +					    &otables[i]); +		if (unlikely(ret != 0)) +			goto out_no_setup; +		offset += otables[i].size; +	} + +	dev_priv->otables = otables; +	return 0; + +out_unreserve: +	ttm_bo_unreserve(dev_priv->otable_bo); +out_no_setup: +	for (i = 0; i < SVGA_OTABLE_DX9_MAX - VMW_OTABLE_SETUP_SUB; ++i) +		vmw_takedown_otable_base(dev_priv, i, &otables[i]); + +	ttm_bo_unref(&dev_priv->otable_bo); +out_no_bo: +	kfree(otables); +	return ret; +} + + +/* + * vmw_otables_takedown - Take down guest backed memory object tables + * + * @dev_priv:       Pointer to a device private structure + * + * Take down the Guest Memory Object tables. + */ +void vmw_otables_takedown(struct vmw_private *dev_priv) +{ +	SVGAOTableType i; +	struct ttm_buffer_object *bo = dev_priv->otable_bo; +	int ret; + +	for (i = 0; i < SVGA_OTABLE_DX9_MAX - VMW_OTABLE_SETUP_SUB; ++i) +		vmw_takedown_otable_base(dev_priv, i, +					 &dev_priv->otables[i]); + +	ret = ttm_bo_reserve(bo, false, true, false, NULL); +	BUG_ON(ret != 0); + +	vmw_fence_single_bo(bo, NULL); +	ttm_bo_unreserve(bo); + +	ttm_bo_unref(&dev_priv->otable_bo); +	kfree(dev_priv->otables); +	dev_priv->otables = NULL; +} + + +/* + * vmw_mob_calculate_pt_pages - Calculate the number of page table pages + * needed for a guest backed memory object. + * + * @data_pages:  Number of data pages in the memory object buffer. + */ +static unsigned long vmw_mob_calculate_pt_pages(unsigned long data_pages) +{ +	unsigned long data_size = data_pages * PAGE_SIZE; +	unsigned long tot_size = 0; + +	while (likely(data_size > PAGE_SIZE)) { +		data_size = DIV_ROUND_UP(data_size, PAGE_SIZE); +		data_size *= VMW_PPN_SIZE; +		tot_size += (data_size + PAGE_SIZE - 1) & PAGE_MASK; +	} + +	return tot_size >> PAGE_SHIFT; +} + +/* + * vmw_mob_create - Create a mob, but don't populate it. + * + * @data_pages:  Number of data pages of the underlying buffer object. + */ +struct vmw_mob *vmw_mob_create(unsigned long data_pages) +{ +	struct vmw_mob *mob = kzalloc(sizeof(*mob), GFP_KERNEL); + +	if (unlikely(mob == NULL)) +		return NULL; + +	mob->num_pages = vmw_mob_calculate_pt_pages(data_pages); + +	return mob; +} + +/* + * vmw_mob_pt_populate - Populate the mob pagetable + * + * @mob:         Pointer to the mob the pagetable of which we want to + *               populate. + * + * This function allocates memory to be used for the pagetable, and + * adjusts TTM memory accounting accordingly. Returns ENOMEM if + * memory resources aren't sufficient and may cause TTM buffer objects + * to be swapped out by using the TTM memory accounting function. + */ +static int vmw_mob_pt_populate(struct vmw_private *dev_priv, +			       struct vmw_mob *mob) +{ +	int ret; +	BUG_ON(mob->pt_bo != NULL); + +	ret = ttm_bo_create(&dev_priv->bdev, mob->num_pages * PAGE_SIZE, +			    ttm_bo_type_device, +			    &vmw_sys_ne_placement, +			    0, false, NULL, &mob->pt_bo); +	if (unlikely(ret != 0)) +		return ret; + +	ret = ttm_bo_reserve(mob->pt_bo, false, true, false, NULL); + +	BUG_ON(ret != 0); +	ret = vmw_bo_driver.ttm_tt_populate(mob->pt_bo->ttm); +	if (unlikely(ret != 0)) +		goto out_unreserve; +	ret = vmw_bo_map_dma(mob->pt_bo); +	if (unlikely(ret != 0)) +		goto out_unreserve; + +	ttm_bo_unreserve(mob->pt_bo); +	 +	return 0; + +out_unreserve: +	ttm_bo_unreserve(mob->pt_bo); +	ttm_bo_unref(&mob->pt_bo); + +	return ret; +} + +/** + * vmw_mob_assign_ppn - Assign a value to a page table entry + * + * @addr: Pointer to pointer to page table entry. + * @val: The page table entry + * + * Assigns a value to a page table entry pointed to by *@addr and increments + * *@addr according to the page table entry size. + */ +#if (VMW_PPN_SIZE == 8) +static void vmw_mob_assign_ppn(__le32 **addr, dma_addr_t val) +{ +	*((__le64 *) *addr) = cpu_to_le64(val >> PAGE_SHIFT); +	*addr += 2; +} +#else +static void vmw_mob_assign_ppn(__le32 **addr, dma_addr_t val) +{ +	*(*addr)++ = cpu_to_le32(val >> PAGE_SHIFT); +} +#endif + +/* + * vmw_mob_build_pt - Build a pagetable + * + * @data_addr:      Array of DMA addresses to the underlying buffer + *                  object's data pages. + * @num_data_pages: Number of buffer object data pages. + * @pt_pages:       Array of page pointers to the page table pages. + * + * Returns the number of page table pages actually used. + * Uses atomic kmaps of highmem pages to avoid TLB thrashing. + */ +static unsigned long vmw_mob_build_pt(struct vmw_piter *data_iter, +				      unsigned long num_data_pages, +				      struct vmw_piter *pt_iter) +{ +	unsigned long pt_size = num_data_pages * VMW_PPN_SIZE; +	unsigned long num_pt_pages = DIV_ROUND_UP(pt_size, PAGE_SIZE); +	unsigned long pt_page; +	__le32 *addr, *save_addr; +	unsigned long i; +	struct page *page; + +	for (pt_page = 0; pt_page < num_pt_pages; ++pt_page) { +		page = vmw_piter_page(pt_iter); + +		save_addr = addr = kmap_atomic(page); + +		for (i = 0; i < PAGE_SIZE / VMW_PPN_SIZE; ++i) { +			vmw_mob_assign_ppn(&addr, +					   vmw_piter_dma_addr(data_iter)); +			if (unlikely(--num_data_pages == 0)) +				break; +			WARN_ON(!vmw_piter_next(data_iter)); +		} +		kunmap_atomic(save_addr); +		vmw_piter_next(pt_iter); +	} + +	return num_pt_pages; +} + +/* + * vmw_mob_build_pt - Set up a multilevel mob pagetable + * + * @mob:            Pointer to a mob whose page table needs setting up. + * @data_addr       Array of DMA addresses to the buffer object's data + *                  pages. + * @num_data_pages: Number of buffer object data pages. + * + * Uses tail recursion to set up a multilevel mob page table. + */ +static void vmw_mob_pt_setup(struct vmw_mob *mob, +			     struct vmw_piter data_iter, +			     unsigned long num_data_pages) +{ +	unsigned long num_pt_pages = 0; +	struct ttm_buffer_object *bo = mob->pt_bo; +	struct vmw_piter save_pt_iter; +	struct vmw_piter pt_iter; +	const struct vmw_sg_table *vsgt; +	int ret; + +	ret = ttm_bo_reserve(bo, false, true, false, NULL); +	BUG_ON(ret != 0); + +	vsgt = vmw_bo_sg_table(bo); +	vmw_piter_start(&pt_iter, vsgt, 0); +	BUG_ON(!vmw_piter_next(&pt_iter)); +	mob->pt_level = 0; +	while (likely(num_data_pages > 1)) { +		++mob->pt_level; +		BUG_ON(mob->pt_level > 2); +		save_pt_iter = pt_iter; +		num_pt_pages = vmw_mob_build_pt(&data_iter, num_data_pages, +						&pt_iter); +		data_iter = save_pt_iter; +		num_data_pages = num_pt_pages; +	} + +	mob->pt_root_page = vmw_piter_dma_addr(&save_pt_iter); +	ttm_bo_unreserve(bo); +} + +/* + * vmw_mob_destroy - Destroy a mob, unpopulating first if necessary. + * + * @mob:            Pointer to a mob to destroy. + */ +void vmw_mob_destroy(struct vmw_mob *mob) +{ +	if (mob->pt_bo) +		ttm_bo_unref(&mob->pt_bo); +	kfree(mob); +} + +/* + * vmw_mob_unbind - Hide a mob from the device. + * + * @dev_priv:       Pointer to a device private. + * @mob_id:         Device id of the mob to unbind. + */ +void vmw_mob_unbind(struct vmw_private *dev_priv, +		    struct vmw_mob *mob) +{ +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdDestroyGBMob body; +	} *cmd; +	int ret; +	struct ttm_buffer_object *bo = mob->pt_bo; + +	if (bo) { +		ret = ttm_bo_reserve(bo, false, true, false, NULL); +		/* +		 * Noone else should be using this buffer. +		 */ +		BUG_ON(ret != 0); +	} + +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for Memory " +			  "Object unbinding.\n"); +	} else { +		cmd->header.id = SVGA_3D_CMD_DESTROY_GB_MOB; +		cmd->header.size = sizeof(cmd->body); +		cmd->body.mobid = mob->id; +		vmw_fifo_commit(dev_priv, sizeof(*cmd)); +	} +	if (bo) { +		vmw_fence_single_bo(bo, NULL); +		ttm_bo_unreserve(bo); +	} +	vmw_3d_resource_dec(dev_priv, false); +} + +/* + * vmw_mob_bind - Make a mob visible to the device after first + *                populating it if necessary. + * + * @dev_priv:       Pointer to a device private. + * @mob:            Pointer to the mob we're making visible. + * @data_addr:      Array of DMA addresses to the data pages of the underlying + *                  buffer object. + * @num_data_pages: Number of data pages of the underlying buffer + *                  object. + * @mob_id:         Device id of the mob to bind + * + * This function is intended to be interfaced with the ttm_tt backend + * code. + */ +int vmw_mob_bind(struct vmw_private *dev_priv, +		 struct vmw_mob *mob, +		 const struct vmw_sg_table *vsgt, +		 unsigned long num_data_pages, +		 int32_t mob_id) +{ +	int ret; +	bool pt_set_up = false; +	struct vmw_piter data_iter; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdDefineGBMob64 body; +	} *cmd; + +	mob->id = mob_id; +	vmw_piter_start(&data_iter, vsgt, 0); +	if (unlikely(!vmw_piter_next(&data_iter))) +		return 0; + +	if (likely(num_data_pages == 1)) { +		mob->pt_level = VMW_MOBFMT_PTDEPTH_0; +		mob->pt_root_page = vmw_piter_dma_addr(&data_iter); +	} else if (vsgt->num_regions == 1) { +		mob->pt_level = SVGA3D_MOBFMT_RANGE; +		mob->pt_root_page = vmw_piter_dma_addr(&data_iter); +	} else if (unlikely(mob->pt_bo == NULL)) { +		ret = vmw_mob_pt_populate(dev_priv, mob); +		if (unlikely(ret != 0)) +			return ret; + +		vmw_mob_pt_setup(mob, data_iter, num_data_pages); +		pt_set_up = true; +		mob->pt_level += VMW_MOBFMT_PTDEPTH_1 - SVGA3D_MOBFMT_PTDEPTH_1; +	} + +	(void) vmw_3d_resource_inc(dev_priv, false); + +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for Memory " +			  "Object binding.\n"); +		goto out_no_cmd_space; +	} + +	cmd->header.id = SVGA_3D_CMD_DEFINE_GB_MOB64; +	cmd->header.size = sizeof(cmd->body); +	cmd->body.mobid = mob_id; +	cmd->body.ptDepth = mob->pt_level; +	cmd->body.base = cpu_to_le64(mob->pt_root_page >> PAGE_SHIFT); +	cmd->body.sizeInBytes = num_data_pages * PAGE_SIZE; + +	vmw_fifo_commit(dev_priv, sizeof(*cmd)); + +	return 0; + +out_no_cmd_space: +	vmw_3d_resource_dec(dev_priv, false); +	if (pt_set_up) +		ttm_bo_unref(&mob->pt_bo); + +	return -ENOMEM; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c index f1a52f9e729..87e39f68e9d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c @@ -26,15 +26,16 @@   **************************************************************************/ -#include "drmP.h" +#include <drm/drmP.h>  #include "vmwgfx_drv.h" -#include "ttm/ttm_placement.h" +#include <drm/ttm/ttm_placement.h>  #include "svga_overlay.h"  #include "svga_escape.h"  #define VMW_MAX_NUM_STREAMS 1 +#define VMW_OVERLAY_CAP_MASK (SVGA_FIFO_CAP_VIDEO | SVGA_FIFO_CAP_ESCAPE)  struct vmw_stream {  	struct vmw_dma_buffer *buf; @@ -87,48 +88,6 @@ static inline void fill_flush(struct vmw_escape_video_flush *cmd,  }  /** - * 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_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 (pin) -		overlay_placement = &vmw_vram_ne_placement; - -	ret = ttm_bo_validate(bo, overlay_placement, interruptible, false, false); - -	ttm_bo_unreserve(bo); - -err: -	ttm_read_unlock(&dev_priv->active_master->lock); - -	return ret; -} - -/**   * Send put command to hw.   *   * Returns @@ -139,68 +98,80 @@ static int vmw_overlay_send_put(struct vmw_private *dev_priv,  				struct drm_vmw_control_stream_arg *arg,  				bool interruptible)  { +	struct vmw_escape_video_flush *flush; +	size_t fifo_size; +	bool have_so = dev_priv->sou_priv ? true : false; +	int i, num_items; +	SVGAGuestPtr ptr; +  	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; +			uint32_t cmdType; +			uint32_t streamId; +		} header;  	} *cmds; -	uint32_t offset; -	int i, ret; +	struct { +		uint32_t registerId; +		uint32_t value; +	} *items; -	for (;;) { -		cmds = vmw_fifo_reserve(dev_priv, sizeof(*cmds)); -		if (cmds) -			break; +	/* defines are a index needs + 1 */ +	if (have_so) +		num_items = SVGA_VIDEO_DST_SCREEN_ID + 1; +	else +		num_items = SVGA_VIDEO_PITCH_3 + 1; -		ret = vmw_fallback_wait(dev_priv, false, true, 0, -					interruptible, 3*HZ); -		if (interruptible && ret == -ERESTARTSYS) -			return ret; -		else -			BUG_ON(ret != 0); +	fifo_size = sizeof(*cmds) + sizeof(*flush) + sizeof(*items) * num_items; + +	cmds = vmw_fifo_reserve(dev_priv, fifo_size); +	/* hardware has hung, can't do anything here */ +	if (!cmds) +		return -ENOMEM; + +	items = (typeof(items))&cmds[1]; +	flush = (struct vmw_escape_video_flush *)&items[num_items]; + +	/* the size is header + number of items */ +	fill_escape(&cmds->escape, sizeof(*items) * (num_items + 1)); + +	cmds->header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS; +	cmds->header.streamId = arg->stream_id; + +	/* the IDs are neatly numbered */ +	for (i = 0; i < num_items; i++) +		items[i].registerId = i; + +	vmw_bo_get_guest_ptr(&buf->base, &ptr); +	ptr.offset += arg->offset; + +	items[SVGA_VIDEO_ENABLED].value     = true; +	items[SVGA_VIDEO_FLAGS].value       = arg->flags; +	items[SVGA_VIDEO_DATA_OFFSET].value = ptr.offset; +	items[SVGA_VIDEO_FORMAT].value      = arg->format; +	items[SVGA_VIDEO_COLORKEY].value    = arg->color_key; +	items[SVGA_VIDEO_SIZE].value        = arg->size; +	items[SVGA_VIDEO_WIDTH].value       = arg->width; +	items[SVGA_VIDEO_HEIGHT].value      = arg->height; +	items[SVGA_VIDEO_SRC_X].value       = arg->src.x; +	items[SVGA_VIDEO_SRC_Y].value       = arg->src.y; +	items[SVGA_VIDEO_SRC_WIDTH].value   = arg->src.w; +	items[SVGA_VIDEO_SRC_HEIGHT].value  = arg->src.h; +	items[SVGA_VIDEO_DST_X].value       = arg->dst.x; +	items[SVGA_VIDEO_DST_Y].value       = arg->dst.y; +	items[SVGA_VIDEO_DST_WIDTH].value   = arg->dst.w; +	items[SVGA_VIDEO_DST_HEIGHT].value  = arg->dst.h; +	items[SVGA_VIDEO_PITCH_1].value     = arg->pitch[0]; +	items[SVGA_VIDEO_PITCH_2].value     = arg->pitch[1]; +	items[SVGA_VIDEO_PITCH_3].value     = arg->pitch[2]; +	if (have_so) { +		items[SVGA_VIDEO_DATA_GMRID].value    = ptr.gmrId; +		items[SVGA_VIDEO_DST_SCREEN_ID].value = SVGA_ID_INVALID;  	} -	fill_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); +	fill_flush(flush, arg->stream_id); -	vmw_fifo_commit(dev_priv, sizeof(*cmds)); +	vmw_fifo_commit(dev_priv, fifo_size);  	return 0;  } @@ -248,6 +219,25 @@ static int vmw_overlay_send_stop(struct vmw_private *dev_priv,  }  /** + * Move a buffer to vram or gmr if @pin is set, else unpin the buffer. + * + * With the introduction of screen objects buffers could now be + * used with GMRs instead of being locked to vram. + */ +static int vmw_overlay_move_buffer(struct vmw_private *dev_priv, +				   struct vmw_dma_buffer *buf, +				   bool pin, bool inter) +{ +	if (!pin) +		return vmw_dmabuf_unpin(dev_priv, buf, inter); + +	if (!dev_priv->sou_priv) +		return vmw_dmabuf_to_vram(dev_priv, buf, true, inter); + +	return vmw_dmabuf_to_vram_or_gmr(dev_priv, buf, true, inter); +} + +/**   * Stop or pause a stream.   *   * If the stream is paused the no evict flag is removed from the buffer @@ -279,8 +269,8 @@ static int vmw_overlay_stop(struct vmw_private *dev_priv,  			return ret;  		/* We just remove the NO_EVICT flag so no -ENOMEM */ -		ret = vmw_dmabuf_pin_in_vram(dev_priv, stream->buf, false, -					     interruptible); +		ret = vmw_overlay_move_buffer(dev_priv, stream->buf, false, +					      interruptible);  		if (interruptible && ret == -ERESTARTSYS)  			return ret;  		else @@ -342,7 +332,7 @@ static int vmw_overlay_update_stream(struct vmw_private *dev_priv,  	/* 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); +	ret = vmw_overlay_move_buffer(dev_priv, buf, true, interruptible);  	if (ret)  		return ret; @@ -351,7 +341,8 @@ static int vmw_overlay_update_stream(struct vmw_private *dev_priv,  		/* 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); +		BUG_ON(vmw_overlay_move_buffer(dev_priv, buf, false, false) +		       != 0);  		return ret;  	} @@ -459,6 +450,14 @@ int vmw_overlay_pause_all(struct vmw_private *dev_priv)  	return 0;  } + +static bool vmw_overlay_available(const struct vmw_private *dev_priv) +{ +	return (dev_priv->overlay_priv != NULL &&  +		((dev_priv->fifo.capabilities & VMW_OVERLAY_CAP_MASK) == +		 VMW_OVERLAY_CAP_MASK)); +} +  int vmw_overlay_ioctl(struct drm_device *dev, void *data,  		      struct drm_file *file_priv)  { @@ -471,7 +470,7 @@ int vmw_overlay_ioctl(struct drm_device *dev, void *data,  	struct vmw_resource *res;  	int ret; -	if (!overlay) +	if (!vmw_overlay_available(dev_priv))  		return -ENOSYS;  	ret = vmw_user_stream_lookup(dev_priv, tfile, &arg->stream_id, &res); @@ -502,7 +501,7 @@ out_unlock:  int vmw_overlay_num_overlays(struct vmw_private *dev_priv)  { -	if (!dev_priv->overlay_priv) +	if (!vmw_overlay_available(dev_priv))  		return 0;  	return VMW_MAX_NUM_STREAMS; @@ -513,7 +512,7 @@ int vmw_overlay_num_free_overlays(struct vmw_private *dev_priv)  	struct vmw_overlay *overlay = dev_priv->overlay_priv;  	int i, k; -	if (!overlay) +	if (!vmw_overlay_available(dev_priv))  		return 0;  	mutex_lock(&overlay->mutex); @@ -579,17 +578,10 @@ int vmw_overlay_init(struct vmw_private *dev_priv)  	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(sizeof(*overlay), GFP_KERNEL); +	overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);  	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; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_prime.c b/drivers/gpu/drm/vmwgfx/vmwgfx_prime.c new file mode 100644 index 00000000000..31fe32d8d65 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_prime.c @@ -0,0 +1,137 @@ +/************************************************************************** + * + * Copyright © 2013 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. + * + **************************************************************************/ +/* + * Authors: + *     Thomas Hellstrom <thellstrom@vmware.com> + * + */ + +#include "vmwgfx_drv.h" +#include <linux/dma-buf.h> +#include <drm/ttm/ttm_object.h> + +/* + * DMA-BUF attach- and mapping methods. No need to implement + * these until we have other virtual devices use them. + */ + +static int vmw_prime_map_attach(struct dma_buf *dma_buf, +				struct device *target_dev, +				struct dma_buf_attachment *attach) +{ +	return -ENOSYS; +} + +static void vmw_prime_map_detach(struct dma_buf *dma_buf, +				 struct dma_buf_attachment *attach) +{ +} + +static struct sg_table *vmw_prime_map_dma_buf(struct dma_buf_attachment *attach, +					      enum dma_data_direction dir) +{ +	return ERR_PTR(-ENOSYS); +} + +static void vmw_prime_unmap_dma_buf(struct dma_buf_attachment *attach, +				    struct sg_table *sgb, +				    enum dma_data_direction dir) +{ +} + +static void *vmw_prime_dmabuf_vmap(struct dma_buf *dma_buf) +{ +	return NULL; +} + +static void vmw_prime_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr) +{ +} + +static void *vmw_prime_dmabuf_kmap_atomic(struct dma_buf *dma_buf, +		unsigned long page_num) +{ +	return NULL; +} + +static void vmw_prime_dmabuf_kunmap_atomic(struct dma_buf *dma_buf, +		unsigned long page_num, void *addr) +{ + +} +static void *vmw_prime_dmabuf_kmap(struct dma_buf *dma_buf, +		unsigned long page_num) +{ +	return NULL; +} + +static void vmw_prime_dmabuf_kunmap(struct dma_buf *dma_buf, +		unsigned long page_num, void *addr) +{ + +} + +static int vmw_prime_dmabuf_mmap(struct dma_buf *dma_buf, +				 struct vm_area_struct *vma) +{ +	WARN_ONCE(true, "Attempted use of dmabuf mmap. Bad.\n"); +	return -ENOSYS; +} + +const struct dma_buf_ops vmw_prime_dmabuf_ops =  { +	.attach = vmw_prime_map_attach, +	.detach = vmw_prime_map_detach, +	.map_dma_buf = vmw_prime_map_dma_buf, +	.unmap_dma_buf = vmw_prime_unmap_dma_buf, +	.release = NULL, +	.kmap = vmw_prime_dmabuf_kmap, +	.kmap_atomic = vmw_prime_dmabuf_kmap_atomic, +	.kunmap = vmw_prime_dmabuf_kunmap, +	.kunmap_atomic = vmw_prime_dmabuf_kunmap_atomic, +	.mmap = vmw_prime_dmabuf_mmap, +	.vmap = vmw_prime_dmabuf_vmap, +	.vunmap = vmw_prime_dmabuf_vunmap, +}; + +int vmw_prime_fd_to_handle(struct drm_device *dev, +			   struct drm_file *file_priv, +			   int fd, u32 *handle) +{ +	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + +	return ttm_prime_fd_to_handle(tfile, fd, handle); +} + +int vmw_prime_handle_to_fd(struct drm_device *dev, +			   struct drm_file *file_priv, +			   uint32_t handle, uint32_t flags, +			   int *prime_fd) +{ +	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + +	return ttm_prime_handle_to_fd(tfile, handle, flags, prime_fd); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 36e129f0023..01d68f0a69d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -26,27 +26,16 @@   **************************************************************************/  #include "vmwgfx_drv.h" -#include "vmwgfx_drm.h" -#include "ttm/ttm_object.h" -#include "ttm/ttm_placement.h" -#include "drmP.h" +#include <drm/vmwgfx_drm.h> +#include <drm/ttm/ttm_object.h> +#include <drm/ttm/ttm_placement.h> +#include <drm/drmP.h> +#include "vmwgfx_resource_priv.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; -}; +#define VMW_RES_EVICT_ERR_COUNT 10  struct vmw_user_dma_buffer { -	struct ttm_base_object base; +	struct ttm_prime_object prime;  	struct vmw_dma_buffer dma;  }; @@ -65,6 +54,21 @@ struct vmw_user_stream {  	struct vmw_stream stream;  }; + +static uint64_t vmw_user_stream_size; + +static const struct vmw_res_func vmw_stream_func = { +	.res_type = vmw_res_stream, +	.needs_backup = false, +	.may_evict = false, +	.type_name = "video streams", +	.backup_placement = NULL, +	.create = NULL, +	.destroy = NULL, +	.bind = NULL, +	.unbind = NULL +}; +  static inline struct vmw_dma_buffer *  vmw_dma_buffer(struct ttm_buffer_object *bo)  { @@ -84,24 +88,76 @@ struct vmw_resource *vmw_resource_reference(struct vmw_resource *res)  	return res;  } +struct vmw_resource * +vmw_resource_reference_unless_doomed(struct vmw_resource *res) +{ +	return kref_get_unless_zero(&res->kref) ? res : NULL; +} + +/** + * vmw_resource_release_id - release a resource id to the id manager. + * + * @res: Pointer to the resource. + * + * Release the resource id to the resource id manager and set it to -1 + */ +void vmw_resource_release_id(struct vmw_resource *res) +{ +	struct vmw_private *dev_priv = res->dev_priv; +	struct idr *idr = &dev_priv->res_idr[res->func->res_type]; + +	write_lock(&dev_priv->resource_lock); +	if (res->id != -1) +		idr_remove(idr, res->id); +	res->id = -1; +	write_unlock(&dev_priv->resource_lock); +} +  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; +	int id; +	struct idr *idr = &dev_priv->res_idr[res->func->res_type]; -	idr_remove(res->idr, res->id); +	res->avail = false; +	list_del_init(&res->lru_head);  	write_unlock(&dev_priv->resource_lock); +	if (res->backup) { +		struct ttm_buffer_object *bo = &res->backup->base; + +		ttm_bo_reserve(bo, false, false, false, 0); +		if (!list_empty(&res->mob_head) && +		    res->func->unbind != NULL) { +			struct ttm_validate_buffer val_buf; -	if (likely(res->hw_destroy != NULL)) +			val_buf.bo = bo; +			res->func->unbind(res, false, &val_buf); +		} +		res->backup_dirty = false; +		list_del_init(&res->mob_head); +		ttm_bo_unreserve(bo); +		vmw_dmabuf_unreference(&res->backup); +	} + +	if (likely(res->hw_destroy != NULL)) {  		res->hw_destroy(res); +		mutex_lock(&dev_priv->binding_mutex); +		vmw_context_binding_res_list_kill(&res->binding_head); +		mutex_unlock(&dev_priv->binding_mutex); +	} +	id = res->id;  	if (res->res_free != NULL)  		res->res_free(res);  	else  		kfree(res);  	write_lock(&dev_priv->resource_lock); + +	if (id != -1) +		idr_remove(idr, id);  }  void vmw_resource_unreference(struct vmw_resource **p_res) @@ -115,33 +171,69 @@ void vmw_resource_unreference(struct vmw_resource **p_res)  	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)) + +/** + * vmw_resource_alloc_id - release a resource id to the id manager. + * + * @res: Pointer to the resource. + * + * Allocate the lowest free resource from the resource manager, and set + * @res->id to that id. Returns 0 on success and -ENOMEM on failure. + */ +int vmw_resource_alloc_id(struct vmw_resource *res)  { +	struct vmw_private *dev_priv = res->dev_priv;  	int ret; +	struct idr *idr = &dev_priv->res_idr[res->func->res_type]; + +	BUG_ON(res->id != -1); + +	idr_preload(GFP_KERNEL); +	write_lock(&dev_priv->resource_lock); + +	ret = idr_alloc(idr, res, 1, 0, GFP_NOWAIT); +	if (ret >= 0) +		res->id = ret; +	write_unlock(&dev_priv->resource_lock); +	idr_preload_end(); +	return ret < 0 ? ret : 0; +} + +/** + * vmw_resource_init - initialize a struct vmw_resource + * + * @dev_priv:       Pointer to a device private struct. + * @res:            The struct vmw_resource to initialize. + * @obj_type:       Resource object type. + * @delay_id:       Boolean whether to defer device id allocation until + *                  the first validation. + * @res_free:       Resource destructor. + * @func:           Resource function table. + */ +int vmw_resource_init(struct vmw_private *dev_priv, struct vmw_resource *res, +		      bool delay_id, +		      void (*res_free) (struct vmw_resource *res), +		      const struct vmw_res_func *func) +{  	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; +	res->func = func; +	INIT_LIST_HEAD(&res->lru_head); +	INIT_LIST_HEAD(&res->mob_head); +	INIT_LIST_HEAD(&res->binding_head); +	res->id = -1; +	res->backup = NULL; +	res->backup_offset = 0; +	res->backup_dirty = false; +	res->res_dirty = false; +	if (delay_id) +		return 0; +	else +		return vmw_resource_alloc_id(res);  }  /** @@ -156,9 +248,8 @@ static int vmw_resource_init(struct vmw_private *dev_priv,   * 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 *)) +void vmw_resource_activate(struct vmw_resource *res, +			   void (*hw_destroy) (struct vmw_resource *))  {  	struct vmw_private *dev_priv = res->dev_priv; @@ -188,651 +279,396 @@ struct vmw_resource *vmw_resource_lookup(struct vmw_private *dev_priv,  }  /** - * Context management: + * vmw_user_resource_lookup_handle - lookup a struct resource from a + * TTM user-space handle and perform basic type checks + * + * @dev_priv:     Pointer to a device private struct + * @tfile:        Pointer to a struct ttm_object_file identifying the caller + * @handle:       The TTM user-space handle + * @converter:    Pointer to an object describing the resource type + * @p_res:        On successful return the location pointed to will contain + *                a pointer to a refcounted struct vmw_resource. + * + * If the handle can't be found or is associated with an incorrect resource + * type, -EINVAL will be returned.   */ - -static void vmw_hw_context_destroy(struct vmw_resource *res) +int vmw_user_resource_lookup_handle(struct vmw_private *dev_priv, +				    struct ttm_object_file *tfile, +				    uint32_t handle, +				    const struct vmw_user_resource_conv +				    *converter, +				    struct vmw_resource **p_res)  { +	struct ttm_base_object *base; +	struct vmw_resource *res; +	int ret = -EINVAL; -	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)); -	vmw_3d_resource_dec(dev_priv); -} - -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; +	base = ttm_base_object_lookup(tfile, handle); +	if (unlikely(base == NULL)) +		return -EINVAL; -	ret = vmw_resource_init(dev_priv, res, &dev_priv->context_idr, -				VMW_RES_CONTEXT, res_free); +	if (unlikely(ttm_base_object_type(base) != converter->object_type)) +		goto out_bad_resource; -	if (unlikely(ret != 0)) { -		if (res_free == NULL) -			kfree(res); -		else -			res_free(res); -		return ret; -	} +	res = converter->base_obj_to_res(base); -	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); -	if (unlikely(cmd == NULL)) { -		DRM_ERROR("Fifo reserve failed.\n"); -		vmw_resource_unreference(&res); -		return -ENOMEM; +	read_lock(&dev_priv->resource_lock); +	if (!res->avail || res->res_free != converter->res_free) { +		read_unlock(&dev_priv->resource_lock); +		goto out_bad_resource;  	} -	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)); -	(void) vmw_3d_resource_inc(dev_priv); -	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; -} +	kref_get(&res->kref); +	read_unlock(&dev_priv->resource_lock); -/** - * User-space context management: - */ +	*p_res = res; +	ret = 0; -static void vmw_user_context_free(struct vmw_resource *res) -{ -	struct vmw_user_context *ctx = -	    container_of(res, struct vmw_user_context, res); +out_bad_resource: +	ttm_base_object_unref(&base); -	kfree(ctx); +	return ret;  }  /** - * 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. + * Helper function that looks either a surface or dmabuf. + * + * The pointer this pointed at by out_surf and out_buf needs to be null.   */ - -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) +int vmw_user_lookup_handle(struct vmw_private *dev_priv, +			   struct ttm_object_file *tfile, +			   uint32_t handle, +			   struct vmw_surface **out_surf, +			   struct vmw_dma_buffer **out_buf)  { -	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; +	BUG_ON(*out_surf || *out_buf); -	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; +	ret = vmw_user_resource_lookup_handle(dev_priv, tfile, handle, +					      user_surface_converter, +					      &res); +	if (!ret) { +		*out_surf = vmw_res_to_srf(res); +		return 0;  	} -	arg->cid = res->id; -out_err: -	vmw_resource_unreference(&res); +	*out_surf = NULL; +	ret = vmw_user_dmabuf_lookup(tfile, handle, out_buf);  	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. + * Buffer management.   */ -static void vmw_hw_surface_destroy(struct vmw_resource *res) +/** + * vmw_dmabuf_acc_size - Calculate the pinned memory usage of buffers + * + * @dev_priv: Pointer to a struct vmw_private identifying the device. + * @size: The requested buffer size. + * @user: Whether this is an ordinary dma buffer or a user dma buffer. + */ +static size_t vmw_dmabuf_acc_size(struct vmw_private *dev_priv, size_t size, +				  bool user)  { +	static size_t struct_size, user_struct_size; +	size_t num_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; +	size_t page_array_size = ttm_round_pot(num_pages * sizeof(void *)); -	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; -	} +	if (unlikely(struct_size == 0)) { +		size_t backend_size = ttm_round_pot(vmw_tt_size); -	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); +		struct_size = backend_size + +			ttm_round_pot(sizeof(struct vmw_dma_buffer)); +		user_struct_size = backend_size + +			ttm_round_pot(sizeof(struct vmw_user_dma_buffer)); +	} -	vmw_fifo_commit(dev_priv, sizeof(*cmd)); -	vmw_3d_resource_dec(dev_priv); -} +	if (dev_priv->map_mode == vmw_dma_alloc_coherent) +		page_array_size += +			ttm_round_pot(num_pages * sizeof(dma_addr_t)); -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); +	return ((user) ? user_struct_size : struct_size) + +		page_array_size;  } -int vmw_surface_init(struct vmw_private *dev_priv, -		     struct vmw_surface *srf, -		     void (*res_free) (struct vmw_resource *res)) +void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo)  { -	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); -	} +	struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo); -	vmw_fifo_commit(dev_priv, submit_size); -	(void) vmw_3d_resource_inc(dev_priv); -	vmw_resource_activate(res, vmw_hw_surface_destroy); -	return 0; +	kfree(vmw_bo);  } -static void vmw_user_surface_free(struct vmw_resource *res) +static void vmw_user_dmabuf_destroy(struct ttm_buffer_object *bo)  { -	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); +	struct vmw_user_dma_buffer *vmw_user_bo = vmw_user_dma_buffer(bo); -	kfree(srf->sizes); -	kfree(srf->snooper.image); -	kfree(user_srf); +	ttm_prime_object_kfree(vmw_user_bo, prime);  } -int vmw_user_surface_lookup_handle(struct vmw_private *dev_priv, -				   struct ttm_object_file *tfile, -				   uint32_t handle, struct vmw_surface **out) +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 vmw_resource *res; -	struct vmw_surface *srf; -	struct vmw_user_surface *user_srf; -	struct ttm_base_object *base; -	int ret = -EINVAL; - -	base = ttm_base_object_lookup(tfile, handle); -	if (unlikely(base == NULL)) -		return -EINVAL; - -	if (unlikely(base->object_type != VMW_RES_SURFACE)) -		goto out_bad_resource; - -	user_srf = container_of(base, struct vmw_user_surface, base); -	srf = &user_srf->srf; -	res = &srf->res; - -	read_lock(&dev_priv->resource_lock); - -	if (!res->avail || res->res_free != &vmw_user_surface_free) { -		read_unlock(&dev_priv->resource_lock); -		goto out_bad_resource; -	} +	struct ttm_bo_device *bdev = &dev_priv->bdev; +	size_t acc_size; +	int ret; +	bool user = (bo_free == &vmw_user_dmabuf_destroy); -	kref_get(&res->kref); -	read_unlock(&dev_priv->resource_lock); +	BUG_ON(!bo_free && (!user && (bo_free != vmw_dmabuf_bo_free))); -	*out = srf; -	ret = 0; +	acc_size = vmw_dmabuf_acc_size(dev_priv, size, user); +	memset(vmw_bo, 0, sizeof(*vmw_bo)); -out_bad_resource: -	ttm_base_object_unref(&base); +	INIT_LIST_HEAD(&vmw_bo->res_list); +	ret = ttm_bo_init(bdev, &vmw_bo->base, size, +			  ttm_bo_type_device, placement, +			  0, interruptible, +			  NULL, acc_size, NULL, bo_free);  	return ret;  } -static void vmw_user_surface_base_release(struct ttm_base_object **p_base) +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 vmw_user_surface *user_srf = -	    container_of(base, struct vmw_user_surface, base); -	struct vmw_resource *res = &user_srf->srf.res; +	struct ttm_buffer_object *bo;  	*p_base = NULL; -	vmw_resource_unreference(&res); + +	if (unlikely(base == NULL)) +		return; + +	vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, +				   prime.base); +	bo = &vmw_user_bo->dma.base; +	ttm_bo_unref(&bo);  } -int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data, -			      struct drm_file *file_priv) +static void vmw_user_dmabuf_ref_obj_release(struct ttm_base_object *base, +					    enum ttm_ref_type ref_type)  { -	struct drm_vmw_surface_arg *arg = (struct drm_vmw_surface_arg *)data; -	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - -	return ttm_ref_object_base_unref(tfile, arg->sid, TTM_REF_USAGE); +	struct vmw_user_dma_buffer *user_bo; +	user_bo = container_of(base, struct vmw_user_dma_buffer, prime.base); + +	switch (ref_type) { +	case TTM_REF_SYNCCPU_WRITE: +		ttm_bo_synccpu_write_release(&user_bo->dma.base); +		break; +	default: +		BUG(); +	}  } -int vmw_surface_define_ioctl(struct drm_device *dev, void *data, -			     struct drm_file *file_priv) +/** + * vmw_user_dmabuf_alloc - Allocate a user dma buffer + * + * @dev_priv: Pointer to a struct device private. + * @tfile: Pointer to a struct ttm_object_file on which to register the user + * object. + * @size: Size of the dma buffer. + * @shareable: Boolean whether the buffer is shareable with other open files. + * @handle: Pointer to where the handle value should be assigned. + * @p_dma_buf: Pointer to where the refcounted struct vmw_dma_buffer pointer + * should be assigned. + */ +int vmw_user_dmabuf_alloc(struct vmw_private *dev_priv, +			  struct ttm_object_file *tfile, +			  uint32_t size, +			  bool shareable, +			  uint32_t *handle, +			  struct vmw_dma_buffer **p_dma_buf)  { -	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; +	struct vmw_user_dma_buffer *user_bo; +	struct ttm_buffer_object *tmp;  	int ret; -	int i; -	if (unlikely(user_srf == NULL)) +	user_bo = kzalloc(sizeof(*user_bo), GFP_KERNEL); +	if (unlikely(user_bo == NULL)) { +		DRM_ERROR("Failed to allocate a buffer.\n");  		return -ENOMEM; - -	srf = &user_srf->srf; -	res = &srf->res; - -	srf->flags = req->flags; -	srf->format = req->format; -	srf->scanout = req->scanout; -	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)) { -		ret = -EFAULT; -		goto out_err1; -	} - -	if (srf->scanout && -	    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"); -			ret = -ENOMEM; -			goto out_err1; -		} -	} else { -		srf->snooper.image = NULL;  	} -	srf->snooper.crtc = NULL; - -	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); +	ret = vmw_dmabuf_init(dev_priv, &user_bo->dma, size, +			      (dev_priv->has_mob) ? +			      &vmw_sys_placement : +			      &vmw_vram_sys_placement, true, +			      &vmw_user_dmabuf_destroy);  	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; -	} - -	rep->sid = user_srf->base.hash.key; -	if (rep->sid == SVGA3D_INVALID_ID) -		DRM_ERROR("Created bad Surface ID.\n"); - -	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) -{ -	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_surface *srf; -	struct vmw_user_surface *user_srf; -	struct drm_vmw_size __user *user_sizes; -	struct ttm_base_object *base; -	int ret = -EINVAL; - -	base = ttm_base_object_lookup(tfile, req->sid); -	if (unlikely(base == NULL)) { -		DRM_ERROR("Could not find surface to reference.\n"); -		return -EINVAL; -	} - -	if (unlikely(base->object_type != VMW_RES_SURFACE)) -		goto out_bad_resource; - -	user_srf = container_of(base, struct vmw_user_surface, base); -	srf = &user_srf->srf; - -	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_no_reference; -	} - -	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)); +	tmp = ttm_bo_reference(&user_bo->dma.base); +	ret = ttm_prime_object_init(tfile, +				    size, +				    &user_bo->prime, +				    shareable, +				    ttm_buffer_type, +				    &vmw_user_dmabuf_release, +				    &vmw_user_dmabuf_ref_obj_release);  	if (unlikely(ret != 0)) { -		DRM_ERROR("copy_to_user failed %p %u\n", -			  user_sizes, srf->num_sizes); -		ret = -EFAULT; +		ttm_bo_unref(&tmp); +		goto out_no_base_object;  	} -out_bad_resource: -out_no_reference: -	ttm_base_object_unref(&base); - -	return ret; -} - -int vmw_surface_check(struct vmw_private *dev_priv, -		      struct ttm_object_file *tfile, -		      uint32_t handle, int *id) -{ -	struct ttm_base_object *base; -	struct vmw_user_surface *user_srf; - -	int ret = -EPERM; - -	base = ttm_base_object_lookup(tfile, handle); -	if (unlikely(base == NULL)) -		return -EINVAL; -	if (unlikely(base->object_type != VMW_RES_SURFACE)) -		goto out_bad_surface; +	*p_dma_buf = &user_bo->dma; +	*handle = user_bo->prime.base.hash.key; -	user_srf = container_of(base, struct vmw_user_surface, base); -	*id = user_srf->srf.res.id; -	ret = 0; - -out_bad_surface: -	/** -	 * FIXME: May deadlock here when called from the -	 * command parsing code. -	 */ - -	ttm_base_object_unref(&base); +out_no_base_object:  	return ret;  }  /** - * Buffer management. + * vmw_user_dmabuf_verify_access - verify access permissions on this + * buffer object. + * + * @bo: Pointer to the buffer object being accessed + * @tfile: Identifying the caller.   */ - -static size_t vmw_dmabuf_acc_size(struct ttm_bo_global *glob, -				  unsigned long num_pages) +int vmw_user_dmabuf_verify_access(struct ttm_buffer_object *bo, +				  struct ttm_object_file *tfile)  { -	static size_t bo_user_size = ~0; +	struct vmw_user_dma_buffer *vmw_user_bo; -	size_t page_array_size = -	    (num_pages * sizeof(void *) + PAGE_SIZE - 1) & PAGE_MASK; +	if (unlikely(bo->destroy != vmw_user_dmabuf_destroy)) +		return -EPERM; -	if (unlikely(bo_user_size == ~0)) { -		bo_user_size = glob->ttm_bo_extra_size + -		    ttm_round_pot(sizeof(struct vmw_dma_buffer)); -	} +	vmw_user_bo = vmw_user_dma_buffer(bo); -	return bo_user_size + page_array_size; -} +	/* Check that the caller has opened the object. */ +	if (likely(ttm_ref_object_exists(tfile, &vmw_user_bo->prime.base))) +		return 0; -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; - -	ttm_mem_global_free(glob->mem_glob, bo->acc_size); -	kfree(vmw_bo); +	DRM_ERROR("Could not grant buffer access.\n"); +	return -EPERM;  } -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)) +/** + * vmw_user_dmabuf_synccpu_grab - Grab a struct vmw_user_dma_buffer for cpu + * access, idling previous GPU operations on the buffer and optionally + * blocking it for further command submissions. + * + * @user_bo: Pointer to the buffer object being grabbed for CPU access + * @tfile: Identifying the caller. + * @flags: Flags indicating how the grab should be performed. + * + * A blocking grab will be automatically released when @tfile is closed. + */ +static int vmw_user_dmabuf_synccpu_grab(struct vmw_user_dma_buffer *user_bo, +					struct ttm_object_file *tfile, +					uint32_t flags)  { -	struct ttm_bo_device *bdev = &dev_priv->bdev; -	struct ttm_mem_global *mem_glob = bdev->glob->mem_glob; -	size_t acc_size; +	struct ttm_buffer_object *bo = &user_bo->dma.base; +	bool existed;  	int ret; -	BUG_ON(!bo_free); - -	acc_size = -	    vmw_dmabuf_acc_size(bdev->glob, -				(size + PAGE_SIZE - 1) >> PAGE_SHIFT); +	if (flags & drm_vmw_synccpu_allow_cs) { +		struct ttm_bo_device *bdev = bo->bdev; -	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); +		spin_lock(&bdev->fence_lock); +		ret = ttm_bo_wait(bo, false, true, +				  !!(flags & drm_vmw_synccpu_dontblock)); +		spin_unlock(&bdev->fence_lock);  		return ret;  	} -	memset(vmw_bo, 0, sizeof(*vmw_bo)); +	ret = ttm_bo_synccpu_write_grab +		(bo, !!(flags & drm_vmw_synccpu_dontblock)); +	if (unlikely(ret != 0)) +		return ret; -	INIT_LIST_HEAD(&vmw_bo->validate_list); +	ret = ttm_ref_object_add(tfile, &user_bo->prime.base, +				 TTM_REF_SYNCCPU_WRITE, &existed); +	if (ret != 0 || existed) +		ttm_bo_synccpu_write_release(&user_bo->dma.base); -	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) +/** + * vmw_user_dmabuf_synccpu_release - Release a previous grab for CPU access, + * and unblock command submission on the buffer if blocked. + * + * @handle: Handle identifying the buffer object. + * @tfile: Identifying the caller. + * @flags: Flags indicating the type of release. + */ +static int vmw_user_dmabuf_synccpu_release(uint32_t handle, +					   struct ttm_object_file *tfile, +					   uint32_t flags)  { -	struct vmw_user_dma_buffer *vmw_user_bo = vmw_user_dma_buffer(bo); -	struct ttm_bo_global *glob = bo->glob; +	if (!(flags & drm_vmw_synccpu_allow_cs)) +		return ttm_ref_object_base_unref(tfile, handle, +						 TTM_REF_SYNCCPU_WRITE); -	ttm_mem_global_free(glob->mem_glob, bo->acc_size); -	kfree(vmw_user_bo); +	return 0;  } -static void vmw_user_dmabuf_release(struct ttm_base_object **p_base) +/** + * vmw_user_dmabuf_synccpu_release - ioctl function implementing the synccpu + * functionality. + * + * @dev: Identifies the drm device. + * @data: Pointer to the ioctl argument. + * @file_priv: Identifies the caller. + * + * This function checks the ioctl arguments for validity and calls the + * relevant synccpu functions. + */ +int vmw_user_dmabuf_synccpu_ioctl(struct drm_device *dev, void *data, +				  struct drm_file *file_priv)  { -	struct vmw_user_dma_buffer *vmw_user_bo; -	struct ttm_base_object *base = *p_base; -	struct ttm_buffer_object *bo; +	struct drm_vmw_synccpu_arg *arg = +		(struct drm_vmw_synccpu_arg *) data; +	struct vmw_dma_buffer *dma_buf; +	struct vmw_user_dma_buffer *user_bo; +	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; +	int ret; -	*p_base = NULL; +	if ((arg->flags & (drm_vmw_synccpu_read | drm_vmw_synccpu_write)) == 0 +	    || (arg->flags & ~(drm_vmw_synccpu_read | drm_vmw_synccpu_write | +			       drm_vmw_synccpu_dontblock | +			       drm_vmw_synccpu_allow_cs)) != 0) { +		DRM_ERROR("Illegal synccpu flags.\n"); +		return -EINVAL; +	} -	if (unlikely(base == NULL)) -		return; +	switch (arg->op) { +	case drm_vmw_synccpu_grab: +		ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &dma_buf); +		if (unlikely(ret != 0)) +			return ret; + +		user_bo = container_of(dma_buf, struct vmw_user_dma_buffer, +				       dma); +		ret = vmw_user_dmabuf_synccpu_grab(user_bo, tfile, arg->flags); +		vmw_dmabuf_unreference(&dma_buf); +		if (unlikely(ret != 0 && ret != -ERESTARTSYS && +			     ret != -EBUSY)) { +			DRM_ERROR("Failed synccpu grab on handle 0x%08x.\n", +				  (unsigned int) arg->handle); +			return ret; +		} +		break; +	case drm_vmw_synccpu_release: +		ret = vmw_user_dmabuf_synccpu_release(arg->handle, tfile, +						      arg->flags); +		if (unlikely(ret != 0)) { +			DRM_ERROR("Failed synccpu release on handle 0x%08x.\n", +				  (unsigned int) arg->handle); +			return ret; +		} +		break; +	default: +		DRM_ERROR("Invalid synccpu operation.\n"); +		return -EINVAL; +	} -	vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, base); -	bo = &vmw_user_bo->dma.base; -	ttm_bo_unref(&bo); +	return 0;  }  int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data, @@ -843,46 +679,30 @@ int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data,  	    (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); +	struct vmw_dma_buffer *dma_buf; +	uint32_t handle;  	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); +	ret = ttm_read_lock(&dev_priv->reservation_sem, true); +	if (unlikely(ret != 0))  		return ret; -	} -	ret = vmw_dmabuf_init(dev_priv, &vmw_user_bo->dma, req->size, -			      &vmw_vram_sys_placement, true, -			      &vmw_user_dmabuf_destroy); +	ret = vmw_user_dmabuf_alloc(dev_priv, vmw_fpriv(file_priv)->tfile, +				    req->size, false, &handle, &dma_buf);  	if (unlikely(ret != 0)) -		return ret; +		goto out_no_dmabuf; -	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); +	rep->handle = handle; +	rep->map_handle = drm_vma_node_offset_addr(&dma_buf->base.vma_node); +	rep->cur_gmr_id = handle; +	rep->cur_gmr_offset = 0; -	ttm_read_unlock(&vmaster->lock); +	vmw_dmabuf_unreference(&dma_buf); -	return 0; +out_no_dmabuf: +	ttm_read_unlock(&dev_priv->reservation_sem); + +	return ret;  }  int vmw_dmabuf_unref_ioctl(struct drm_device *dev, void *data, @@ -896,27 +716,6 @@ int vmw_dmabuf_unref_ioctl(struct drm_device *dev, void *data,  					 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; -} -  int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,  			   uint32_t handle, struct vmw_dma_buffer **out)  { @@ -930,14 +729,15 @@ int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,  		return -ESRCH;  	} -	if (unlikely(base->object_type != ttm_buffer_type)) { +	if (unlikely(ttm_base_object_type(base) != 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); +	vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, +				   prime.base);  	(void)ttm_bo_reference(&vmw_user_bo->dma.base);  	ttm_base_object_unref(&base);  	*out = &vmw_user_bo->dma; @@ -945,6 +745,22 @@ int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,  	return 0;  } +int vmw_user_dmabuf_reference(struct ttm_object_file *tfile, +			      struct vmw_dma_buffer *dma_buf, +			      uint32_t *handle) +{ +	struct vmw_user_dma_buffer *user_bo; + +	if (dma_buf->base.destroy != vmw_user_dmabuf_destroy) +		return -EINVAL; + +	user_bo = container_of(dma_buf, struct vmw_user_dma_buffer, dma); + +	*handle = user_bo->prime.base.hash.key; +	return ttm_ref_object_add(tfile, &user_bo->prime.base, +				  TTM_REF_USAGE, NULL); +} +  /*   * Stream management   */ @@ -969,8 +785,8 @@ static int vmw_stream_init(struct vmw_private *dev_priv,  	struct vmw_resource *res = &stream->res;  	int ret; -	ret = vmw_resource_init(dev_priv, res, &dev_priv->stream_idr, -				VMW_RES_STREAM, res_free); +	ret = vmw_resource_init(dev_priv, res, false, res_free, +				&vmw_stream_func);  	if (unlikely(ret != 0)) {  		if (res_free == NULL) @@ -992,16 +808,15 @@ static int vmw_stream_init(struct vmw_private *dev_priv,  	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); +	struct vmw_private *dev_priv = res->dev_priv; -	kfree(stream); +	ttm_base_object_kfree(stream, base); +	ttm_mem_global_free(vmw_mem_glob(dev_priv), +			    vmw_user_stream_size);  }  /** @@ -1028,9 +843,11 @@ int vmw_stream_unref_ioctl(struct drm_device *dev, void *data,  	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; +	struct idr *idr = &dev_priv->res_idr[vmw_res_stream];  	int ret = 0; -	res = vmw_resource_lookup(dev_priv, &dev_priv->stream_idr, arg->stream_id); + +	res = vmw_resource_lookup(dev_priv, idr, arg->stream_id);  	if (unlikely(res == NULL))  		return -EINVAL; @@ -1055,23 +872,55 @@ 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_user_stream *stream;  	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; +	/* +	 * Approximate idr memory usage with 128 bytes. It will be limited +	 * by maximum number_of streams anyway? +	 */ + +	if (unlikely(vmw_user_stream_size == 0)) +		vmw_user_stream_size = ttm_round_pot(sizeof(*stream)) + 128; + +	ret = ttm_read_lock(&dev_priv->reservation_sem, true); +	if (unlikely(ret != 0)) +		return ret; + +	ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), +				   vmw_user_stream_size, +				   false, true); +	if (unlikely(ret != 0)) { +		if (ret != -ERESTARTSYS) +			DRM_ERROR("Out of graphics memory for stream" +				  " creation.\n"); +		goto out_unlock; +	} + + +	stream = kmalloc(sizeof(*stream), GFP_KERNEL); +	if (unlikely(stream == NULL)) { +		ttm_mem_global_free(vmw_mem_glob(dev_priv), +				    vmw_user_stream_size); +		ret = -ENOMEM; +		goto out_unlock; +	}  	res = &stream->stream.res;  	stream->base.shareable = false;  	stream->base.tfile = NULL; +	/* +	 * From here on, the destructor takes over resource freeing. +	 */ +  	ret = vmw_stream_init(dev_priv, &stream->stream, vmw_user_stream_free);  	if (unlikely(ret != 0)) -		return ret; +		goto out_unlock;  	tmp = vmw_resource_reference(res);  	ret = ttm_base_object_init(tfile, &stream->base, false, VMW_RES_STREAM, @@ -1085,6 +934,8 @@ int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,  	arg->stream_id = res->id;  out_err:  	vmw_resource_unreference(&res); +out_unlock: +	ttm_read_unlock(&dev_priv->reservation_sem);  	return ret;  } @@ -1096,7 +947,8 @@ int vmw_user_stream_lookup(struct vmw_private *dev_priv,  	struct vmw_resource *res;  	int ret; -	res = vmw_resource_lookup(dev_priv, &dev_priv->stream_idr, *inout_id); +	res = vmw_resource_lookup(dev_priv, &dev_priv->res_idr[vmw_res_stream], +				  *inout_id);  	if (unlikely(res == NULL))  		return -EINVAL; @@ -1118,3 +970,608 @@ err_ref:  	vmw_resource_unreference(&res);  	return ret;  } + + +/** + * vmw_dumb_create - Create a dumb kms buffer + * + * @file_priv: Pointer to a struct drm_file identifying the caller. + * @dev: Pointer to the drm device. + * @args: Pointer to a struct drm_mode_create_dumb structure + * + * This is a driver callback for the core drm create_dumb functionality. + * Note that this is very similar to the vmw_dmabuf_alloc ioctl, except + * that the arguments have a different format. + */ +int vmw_dumb_create(struct drm_file *file_priv, +		    struct drm_device *dev, +		    struct drm_mode_create_dumb *args) +{ +	struct vmw_private *dev_priv = vmw_priv(dev); +	struct vmw_dma_buffer *dma_buf; +	int ret; + +	args->pitch = args->width * ((args->bpp + 7) / 8); +	args->size = args->pitch * args->height; + +	ret = ttm_read_lock(&dev_priv->reservation_sem, true); +	if (unlikely(ret != 0)) +		return ret; + +	ret = vmw_user_dmabuf_alloc(dev_priv, vmw_fpriv(file_priv)->tfile, +				    args->size, false, &args->handle, +				    &dma_buf); +	if (unlikely(ret != 0)) +		goto out_no_dmabuf; + +	vmw_dmabuf_unreference(&dma_buf); +out_no_dmabuf: +	ttm_read_unlock(&dev_priv->reservation_sem); +	return ret; +} + +/** + * vmw_dumb_map_offset - Return the address space offset of a dumb buffer + * + * @file_priv: Pointer to a struct drm_file identifying the caller. + * @dev: Pointer to the drm device. + * @handle: Handle identifying the dumb buffer. + * @offset: The address space offset returned. + * + * This is a driver callback for the core drm dumb_map_offset functionality. + */ +int vmw_dumb_map_offset(struct drm_file *file_priv, +			struct drm_device *dev, uint32_t handle, +			uint64_t *offset) +{ +	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; +	struct vmw_dma_buffer *out_buf; +	int ret; + +	ret = vmw_user_dmabuf_lookup(tfile, handle, &out_buf); +	if (ret != 0) +		return -EINVAL; + +	*offset = drm_vma_node_offset_addr(&out_buf->base.vma_node); +	vmw_dmabuf_unreference(&out_buf); +	return 0; +} + +/** + * vmw_dumb_destroy - Destroy a dumb boffer + * + * @file_priv: Pointer to a struct drm_file identifying the caller. + * @dev: Pointer to the drm device. + * @handle: Handle identifying the dumb buffer. + * + * This is a driver callback for the core drm dumb_destroy functionality. + */ +int vmw_dumb_destroy(struct drm_file *file_priv, +		     struct drm_device *dev, +		     uint32_t handle) +{ +	return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, +					 handle, TTM_REF_USAGE); +} + +/** + * vmw_resource_buf_alloc - Allocate a backup buffer for a resource. + * + * @res:            The resource for which to allocate a backup buffer. + * @interruptible:  Whether any sleeps during allocation should be + *                  performed while interruptible. + */ +static int vmw_resource_buf_alloc(struct vmw_resource *res, +				  bool interruptible) +{ +	unsigned long size = +		(res->backup_size + PAGE_SIZE - 1) & PAGE_MASK; +	struct vmw_dma_buffer *backup; +	int ret; + +	if (likely(res->backup)) { +		BUG_ON(res->backup->base.num_pages * PAGE_SIZE < size); +		return 0; +	} + +	backup = kzalloc(sizeof(*backup), GFP_KERNEL); +	if (unlikely(backup == NULL)) +		return -ENOMEM; + +	ret = vmw_dmabuf_init(res->dev_priv, backup, res->backup_size, +			      res->func->backup_placement, +			      interruptible, +			      &vmw_dmabuf_bo_free); +	if (unlikely(ret != 0)) +		goto out_no_dmabuf; + +	res->backup = backup; + +out_no_dmabuf: +	return ret; +} + +/** + * vmw_resource_do_validate - Make a resource up-to-date and visible + *                            to the device. + * + * @res:            The resource to make visible to the device. + * @val_buf:        Information about a buffer possibly + *                  containing backup data if a bind operation is needed. + * + * On hardware resource shortage, this function returns -EBUSY and + * should be retried once resources have been freed up. + */ +static int vmw_resource_do_validate(struct vmw_resource *res, +				    struct ttm_validate_buffer *val_buf) +{ +	int ret = 0; +	const struct vmw_res_func *func = res->func; + +	if (unlikely(res->id == -1)) { +		ret = func->create(res); +		if (unlikely(ret != 0)) +			return ret; +	} + +	if (func->bind && +	    ((func->needs_backup && list_empty(&res->mob_head) && +	      val_buf->bo != NULL) || +	     (!func->needs_backup && val_buf->bo != NULL))) { +		ret = func->bind(res, val_buf); +		if (unlikely(ret != 0)) +			goto out_bind_failed; +		if (func->needs_backup) +			list_add_tail(&res->mob_head, &res->backup->res_list); +	} + +	/* +	 * Only do this on write operations, and move to +	 * vmw_resource_unreserve if it can be called after +	 * backup buffers have been unreserved. Otherwise +	 * sort out locking. +	 */ +	res->res_dirty = true; + +	return 0; + +out_bind_failed: +	func->destroy(res); + +	return ret; +} + +/** + * vmw_resource_unreserve - Unreserve a resource previously reserved for + * command submission. + * + * @res:               Pointer to the struct vmw_resource to unreserve. + * @new_backup:        Pointer to new backup buffer if command submission + *                     switched. + * @new_backup_offset: New backup offset if @new_backup is !NULL. + * + * Currently unreserving a resource means putting it back on the device's + * resource lru list, so that it can be evicted if necessary. + */ +void vmw_resource_unreserve(struct vmw_resource *res, +			    struct vmw_dma_buffer *new_backup, +			    unsigned long new_backup_offset) +{ +	struct vmw_private *dev_priv = res->dev_priv; + +	if (!list_empty(&res->lru_head)) +		return; + +	if (new_backup && new_backup != res->backup) { + +		if (res->backup) { +			lockdep_assert_held(&res->backup->base.resv->lock.base); +			list_del_init(&res->mob_head); +			vmw_dmabuf_unreference(&res->backup); +		} + +		res->backup = vmw_dmabuf_reference(new_backup); +		lockdep_assert_held(&new_backup->base.resv->lock.base); +		list_add_tail(&res->mob_head, &new_backup->res_list); +	} +	if (new_backup) +		res->backup_offset = new_backup_offset; + +	if (!res->func->may_evict || res->id == -1) +		return; + +	write_lock(&dev_priv->resource_lock); +	list_add_tail(&res->lru_head, +		      &res->dev_priv->res_lru[res->func->res_type]); +	write_unlock(&dev_priv->resource_lock); +} + +/** + * vmw_resource_check_buffer - Check whether a backup buffer is needed + *                             for a resource and in that case, allocate + *                             one, reserve and validate it. + * + * @res:            The resource for which to allocate a backup buffer. + * @interruptible:  Whether any sleeps during allocation should be + *                  performed while interruptible. + * @val_buf:        On successful return contains data about the + *                  reserved and validated backup buffer. + */ +static int +vmw_resource_check_buffer(struct vmw_resource *res, +			  bool interruptible, +			  struct ttm_validate_buffer *val_buf) +{ +	struct list_head val_list; +	bool backup_dirty = false; +	int ret; + +	if (unlikely(res->backup == NULL)) { +		ret = vmw_resource_buf_alloc(res, interruptible); +		if (unlikely(ret != 0)) +			return ret; +	} + +	INIT_LIST_HEAD(&val_list); +	val_buf->bo = ttm_bo_reference(&res->backup->base); +	list_add_tail(&val_buf->head, &val_list); +	ret = ttm_eu_reserve_buffers(NULL, &val_list); +	if (unlikely(ret != 0)) +		goto out_no_reserve; + +	if (res->func->needs_backup && list_empty(&res->mob_head)) +		return 0; + +	backup_dirty = res->backup_dirty; +	ret = ttm_bo_validate(&res->backup->base, +			      res->func->backup_placement, +			      true, false); + +	if (unlikely(ret != 0)) +		goto out_no_validate; + +	return 0; + +out_no_validate: +	ttm_eu_backoff_reservation(NULL, &val_list); +out_no_reserve: +	ttm_bo_unref(&val_buf->bo); +	if (backup_dirty) +		vmw_dmabuf_unreference(&res->backup); + +	return ret; +} + +/** + * vmw_resource_reserve - Reserve a resource for command submission + * + * @res:            The resource to reserve. + * + * This function takes the resource off the LRU list and make sure + * a backup buffer is present for guest-backed resources. However, + * the buffer may not be bound to the resource at this point. + * + */ +int vmw_resource_reserve(struct vmw_resource *res, bool no_backup) +{ +	struct vmw_private *dev_priv = res->dev_priv; +	int ret; + +	write_lock(&dev_priv->resource_lock); +	list_del_init(&res->lru_head); +	write_unlock(&dev_priv->resource_lock); + +	if (res->func->needs_backup && res->backup == NULL && +	    !no_backup) { +		ret = vmw_resource_buf_alloc(res, true); +		if (unlikely(ret != 0)) +			return ret; +	} + +	return 0; +} + +/** + * vmw_resource_backoff_reservation - Unreserve and unreference a + *                                    backup buffer + *. + * @val_buf:        Backup buffer information. + */ +static void +vmw_resource_backoff_reservation(struct ttm_validate_buffer *val_buf) +{ +	struct list_head val_list; + +	if (likely(val_buf->bo == NULL)) +		return; + +	INIT_LIST_HEAD(&val_list); +	list_add_tail(&val_buf->head, &val_list); +	ttm_eu_backoff_reservation(NULL, &val_list); +	ttm_bo_unref(&val_buf->bo); +} + +/** + * vmw_resource_do_evict - Evict a resource, and transfer its data + *                         to a backup buffer. + * + * @res:            The resource to evict. + * @interruptible:  Whether to wait interruptible. + */ +int vmw_resource_do_evict(struct vmw_resource *res, bool interruptible) +{ +	struct ttm_validate_buffer val_buf; +	const struct vmw_res_func *func = res->func; +	int ret; + +	BUG_ON(!func->may_evict); + +	val_buf.bo = NULL; +	ret = vmw_resource_check_buffer(res, interruptible, &val_buf); +	if (unlikely(ret != 0)) +		return ret; + +	if (unlikely(func->unbind != NULL && +		     (!func->needs_backup || !list_empty(&res->mob_head)))) { +		ret = func->unbind(res, res->res_dirty, &val_buf); +		if (unlikely(ret != 0)) +			goto out_no_unbind; +		list_del_init(&res->mob_head); +	} +	ret = func->destroy(res); +	res->backup_dirty = true; +	res->res_dirty = false; +out_no_unbind: +	vmw_resource_backoff_reservation(&val_buf); + +	return ret; +} + + +/** + * vmw_resource_validate - Make a resource up-to-date and visible + *                         to the device. + * + * @res:            The resource to make visible to the device. + * + * On succesful return, any backup DMA buffer pointed to by @res->backup will + * be reserved and validated. + * On hardware resource shortage, this function will repeatedly evict + * resources of the same type until the validation succeeds. + */ +int vmw_resource_validate(struct vmw_resource *res) +{ +	int ret; +	struct vmw_resource *evict_res; +	struct vmw_private *dev_priv = res->dev_priv; +	struct list_head *lru_list = &dev_priv->res_lru[res->func->res_type]; +	struct ttm_validate_buffer val_buf; +	unsigned err_count = 0; + +	if (likely(!res->func->may_evict)) +		return 0; + +	val_buf.bo = NULL; +	if (res->backup) +		val_buf.bo = &res->backup->base; +	do { +		ret = vmw_resource_do_validate(res, &val_buf); +		if (likely(ret != -EBUSY)) +			break; + +		write_lock(&dev_priv->resource_lock); +		if (list_empty(lru_list) || !res->func->may_evict) { +			DRM_ERROR("Out of device device resources " +				  "for %s.\n", res->func->type_name); +			ret = -EBUSY; +			write_unlock(&dev_priv->resource_lock); +			break; +		} + +		evict_res = vmw_resource_reference +			(list_first_entry(lru_list, struct vmw_resource, +					  lru_head)); +		list_del_init(&evict_res->lru_head); + +		write_unlock(&dev_priv->resource_lock); + +		ret = vmw_resource_do_evict(evict_res, true); +		if (unlikely(ret != 0)) { +			write_lock(&dev_priv->resource_lock); +			list_add_tail(&evict_res->lru_head, lru_list); +			write_unlock(&dev_priv->resource_lock); +			if (ret == -ERESTARTSYS || +			    ++err_count > VMW_RES_EVICT_ERR_COUNT) { +				vmw_resource_unreference(&evict_res); +				goto out_no_validate; +			} +		} + +		vmw_resource_unreference(&evict_res); +	} while (1); + +	if (unlikely(ret != 0)) +		goto out_no_validate; +	else if (!res->func->needs_backup && res->backup) { +		list_del_init(&res->mob_head); +		vmw_dmabuf_unreference(&res->backup); +	} + +	return 0; + +out_no_validate: +	return ret; +} + +/** + * vmw_fence_single_bo - Utility function to fence a single TTM buffer + *                       object without unreserving it. + * + * @bo:             Pointer to the struct ttm_buffer_object to fence. + * @fence:          Pointer to the fence. If NULL, this function will + *                  insert a fence into the command stream.. + * + * Contrary to the ttm_eu version of this function, it takes only + * a single buffer object instead of a list, and it also doesn't + * unreserve the buffer object, which needs to be done separately. + */ +void vmw_fence_single_bo(struct ttm_buffer_object *bo, +			 struct vmw_fence_obj *fence) +{ +	struct ttm_bo_device *bdev = bo->bdev; +	struct ttm_bo_driver *driver = bdev->driver; +	struct vmw_fence_obj *old_fence_obj; +	struct vmw_private *dev_priv = +		container_of(bdev, struct vmw_private, bdev); + +	if (fence == NULL) +		vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); +	else +		driver->sync_obj_ref(fence); + +	spin_lock(&bdev->fence_lock); + +	old_fence_obj = bo->sync_obj; +	bo->sync_obj = fence; + +	spin_unlock(&bdev->fence_lock); + +	if (old_fence_obj) +		vmw_fence_obj_unreference(&old_fence_obj); +} + +/** + * vmw_resource_move_notify - TTM move_notify_callback + * + * @bo:             The TTM buffer object about to move. + * @mem:            The truct ttm_mem_reg indicating to what memory + *                  region the move is taking place. + * + * Evicts the Guest Backed hardware resource if the backup + * buffer is being moved out of MOB memory. + * Note that this function should not race with the resource + * validation code as long as it accesses only members of struct + * resource that remain static while bo::res is !NULL and + * while we have @bo reserved. struct resource::backup is *not* a + * static member. The resource validation code will take care + * to set @bo::res to NULL, while having @bo reserved when the + * buffer is no longer bound to the resource, so @bo:res can be + * used to determine whether there is a need to unbind and whether + * it is safe to unbind. + */ +void vmw_resource_move_notify(struct ttm_buffer_object *bo, +			      struct ttm_mem_reg *mem) +{ +	struct vmw_dma_buffer *dma_buf; + +	if (mem == NULL) +		return; + +	if (bo->destroy != vmw_dmabuf_bo_free && +	    bo->destroy != vmw_user_dmabuf_destroy) +		return; + +	dma_buf = container_of(bo, struct vmw_dma_buffer, base); + +	if (mem->mem_type != VMW_PL_MOB) { +		struct vmw_resource *res, *n; +		struct ttm_bo_device *bdev = bo->bdev; +		struct ttm_validate_buffer val_buf; + +		val_buf.bo = bo; + +		list_for_each_entry_safe(res, n, &dma_buf->res_list, mob_head) { + +			if (unlikely(res->func->unbind == NULL)) +				continue; + +			(void) res->func->unbind(res, true, &val_buf); +			res->backup_dirty = true; +			res->res_dirty = false; +			list_del_init(&res->mob_head); +		} + +		spin_lock(&bdev->fence_lock); +		(void) ttm_bo_wait(bo, false, false, false); +		spin_unlock(&bdev->fence_lock); +	} +} + +/** + * vmw_resource_needs_backup - Return whether a resource needs a backup buffer. + * + * @res:            The resource being queried. + */ +bool vmw_resource_needs_backup(const struct vmw_resource *res) +{ +	return res->func->needs_backup; +} + +/** + * vmw_resource_evict_type - Evict all resources of a specific type + * + * @dev_priv:       Pointer to a device private struct + * @type:           The resource type to evict + * + * To avoid thrashing starvation or as part of the hibernation sequence, + * try to evict all evictable resources of a specific type. + */ +static void vmw_resource_evict_type(struct vmw_private *dev_priv, +				    enum vmw_res_type type) +{ +	struct list_head *lru_list = &dev_priv->res_lru[type]; +	struct vmw_resource *evict_res; +	unsigned err_count = 0; +	int ret; + +	do { +		write_lock(&dev_priv->resource_lock); + +		if (list_empty(lru_list)) +			goto out_unlock; + +		evict_res = vmw_resource_reference( +			list_first_entry(lru_list, struct vmw_resource, +					 lru_head)); +		list_del_init(&evict_res->lru_head); +		write_unlock(&dev_priv->resource_lock); + +		ret = vmw_resource_do_evict(evict_res, false); +		if (unlikely(ret != 0)) { +			write_lock(&dev_priv->resource_lock); +			list_add_tail(&evict_res->lru_head, lru_list); +			write_unlock(&dev_priv->resource_lock); +			if (++err_count > VMW_RES_EVICT_ERR_COUNT) { +				vmw_resource_unreference(&evict_res); +				return; +			} +		} + +		vmw_resource_unreference(&evict_res); +	} while (1); + +out_unlock: +	write_unlock(&dev_priv->resource_lock); +} + +/** + * vmw_resource_evict_all - Evict all evictable resources + * + * @dev_priv:       Pointer to a device private struct + * + * To avoid thrashing starvation or as part of the hibernation sequence, + * evict all evictable resources. In particular this means that all + * guest-backed resources that are registered with the device are + * evicted and the OTable becomes clean. + */ +void vmw_resource_evict_all(struct vmw_private *dev_priv) +{ +	enum vmw_res_type type; + +	mutex_lock(&dev_priv->cmdbuf_mutex); + +	for (type = 0; type < vmw_res_max; ++type) +		vmw_resource_evict_type(dev_priv, type); + +	mutex_unlock(&dev_priv->cmdbuf_mutex); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h new file mode 100644 index 00000000000..f3adeed2854 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h @@ -0,0 +1,84 @@ +/************************************************************************** + * + * Copyright © 2012 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_RESOURCE_PRIV_H_ +#define _VMWGFX_RESOURCE_PRIV_H_ + +#include "vmwgfx_drv.h" + +/** + * struct vmw_user_resource_conv - Identify a derived user-exported resource + * type and provide a function to convert its ttm_base_object pointer to + * a struct vmw_resource + */ +struct vmw_user_resource_conv { +	enum ttm_object_type object_type; +	struct vmw_resource *(*base_obj_to_res)(struct ttm_base_object *base); +	void (*res_free) (struct vmw_resource *res); +}; + +/** + * struct vmw_res_func - members and functions common for a resource type + * + * @res_type:          Enum that identifies the lru list to use for eviction. + * @needs_backup:      Whether the resource is guest-backed and needs + *                     persistent buffer storage. + * @type_name:         String that identifies the resource type. + * @backup_placement:  TTM placement for backup buffers. + * @may_evict          Whether the resource may be evicted. + * @create:            Create a hardware resource. + * @destroy:           Destroy a hardware resource. + * @bind:              Bind a hardware resource to persistent buffer storage. + * @unbind:            Unbind a hardware resource from persistent + *                     buffer storage. + */ + +struct vmw_res_func { +	enum vmw_res_type res_type; +	bool needs_backup; +	const char *type_name; +	struct ttm_placement *backup_placement; +	bool may_evict; + +	int (*create) (struct vmw_resource *res); +	int (*destroy) (struct vmw_resource *res); +	int (*bind) (struct vmw_resource *res, +		     struct ttm_validate_buffer *val_buf); +	int (*unbind) (struct vmw_resource *res, +		       bool readback, +		       struct ttm_validate_buffer *val_buf); +}; + +int vmw_resource_alloc_id(struct vmw_resource *res); +void vmw_resource_release_id(struct vmw_resource *res); +int vmw_resource_init(struct vmw_private *dev_priv, struct vmw_resource *res, +		      bool delay_id, +		      void (*res_free) (struct vmw_resource *res), +		      const struct vmw_res_func *func); +void vmw_resource_activate(struct vmw_resource *res, +			   void (*hw_destroy) (struct vmw_resource *)); +#endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c new file mode 100644 index 00000000000..a95d3a0cabe --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -0,0 +1,576 @@ +/************************************************************************** + * + * Copyright © 2011 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_sou(x) \ +	container_of(x, struct vmw_screen_object_unit, base.crtc) +#define vmw_encoder_to_sou(x) \ +	container_of(x, struct vmw_screen_object_unit, base.encoder) +#define vmw_connector_to_sou(x) \ +	container_of(x, struct vmw_screen_object_unit, base.connector) + +struct vmw_screen_object_display { +	unsigned num_implicit; + +	struct vmw_framebuffer *implicit_fb; +}; + +/** + * Display unit using screen objects. + */ +struct vmw_screen_object_unit { +	struct vmw_display_unit base; + +	unsigned long buffer_size; /**< Size of allocated buffer */ +	struct vmw_dma_buffer *buffer; /**< Backing store buffer */ + +	bool defined; +	bool active_implicit; +}; + +static void vmw_sou_destroy(struct vmw_screen_object_unit *sou) +{ +	vmw_display_unit_cleanup(&sou->base); +	kfree(sou); +} + + +/* + * Screen Object Display Unit CRTC functions + */ + +static void vmw_sou_crtc_destroy(struct drm_crtc *crtc) +{ +	vmw_sou_destroy(vmw_crtc_to_sou(crtc)); +} + +static void vmw_sou_del_active(struct vmw_private *vmw_priv, +			      struct vmw_screen_object_unit *sou) +{ +	struct vmw_screen_object_display *ld = vmw_priv->sou_priv; + +	if (sou->active_implicit) { +		if (--(ld->num_implicit) == 0) +			ld->implicit_fb = NULL; +		sou->active_implicit = false; +	} +} + +static void vmw_sou_add_active(struct vmw_private *vmw_priv, +			      struct vmw_screen_object_unit *sou, +			      struct vmw_framebuffer *vfb) +{ +	struct vmw_screen_object_display *ld = vmw_priv->sou_priv; + +	BUG_ON(!ld->num_implicit && ld->implicit_fb); + +	if (!sou->active_implicit && sou->base.is_implicit) { +		ld->implicit_fb = vfb; +		sou->active_implicit = true; +		ld->num_implicit++; +	} +} + +/** + * Send the fifo command to create a screen. + */ +static int vmw_sou_fifo_create(struct vmw_private *dev_priv, +			       struct vmw_screen_object_unit *sou, +			       uint32_t x, uint32_t y, +			       struct drm_display_mode *mode) +{ +	size_t fifo_size; + +	struct { +		struct { +			uint32_t cmdType; +		} header; +		SVGAScreenObject obj; +	} *cmd; + +	BUG_ON(!sou->buffer); + +	fifo_size = sizeof(*cmd); +	cmd = vmw_fifo_reserve(dev_priv, fifo_size); +	/* The hardware has hung, nothing we can do about it here. */ +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Fifo reserve failed.\n"); +		return -ENOMEM; +	} + +	memset(cmd, 0, fifo_size); +	cmd->header.cmdType = SVGA_CMD_DEFINE_SCREEN; +	cmd->obj.structSize = sizeof(SVGAScreenObject); +	cmd->obj.id = sou->base.unit; +	cmd->obj.flags = SVGA_SCREEN_HAS_ROOT | +		(sou->base.unit == 0 ? SVGA_SCREEN_IS_PRIMARY : 0); +	cmd->obj.size.width = mode->hdisplay; +	cmd->obj.size.height = mode->vdisplay; +	if (sou->base.is_implicit) { +		cmd->obj.root.x = x; +		cmd->obj.root.y = y; +	} else { +		cmd->obj.root.x = sou->base.gui_x; +		cmd->obj.root.y = sou->base.gui_y; +	} + +	/* Ok to assume that buffer is pinned in vram */ +	vmw_bo_get_guest_ptr(&sou->buffer->base, &cmd->obj.backingStore.ptr); +	cmd->obj.backingStore.pitch = mode->hdisplay * 4; + +	vmw_fifo_commit(dev_priv, fifo_size); + +	sou->defined = true; + +	return 0; +} + +/** + * Send the fifo command to destroy a screen. + */ +static int vmw_sou_fifo_destroy(struct vmw_private *dev_priv, +				struct vmw_screen_object_unit *sou) +{ +	size_t fifo_size; +	int ret; + +	struct { +		struct { +			uint32_t cmdType; +		} header; +		SVGAFifoCmdDestroyScreen body; +	} *cmd; + +	/* no need to do anything */ +	if (unlikely(!sou->defined)) +		return 0; + +	fifo_size = sizeof(*cmd); +	cmd = vmw_fifo_reserve(dev_priv, fifo_size); +	/* the hardware has hung, nothing we can do about it here */ +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Fifo reserve failed.\n"); +		return -ENOMEM; +	} + +	memset(cmd, 0, fifo_size); +	cmd->header.cmdType = SVGA_CMD_DESTROY_SCREEN; +	cmd->body.screenId = sou->base.unit; + +	vmw_fifo_commit(dev_priv, fifo_size); + +	/* Force sync */ +	ret = vmw_fallback_wait(dev_priv, false, true, 0, false, 3*HZ); +	if (unlikely(ret != 0)) +		DRM_ERROR("Failed to sync with HW"); +	else +		sou->defined = false; + +	return ret; +} + +/** + * Free the backing store. + */ +static void vmw_sou_backing_free(struct vmw_private *dev_priv, +				 struct vmw_screen_object_unit *sou) +{ +	struct ttm_buffer_object *bo; + +	if (unlikely(sou->buffer == NULL)) +		return; + +	bo = &sou->buffer->base; +	ttm_bo_unref(&bo); +	sou->buffer = NULL; +	sou->buffer_size = 0; +} + +/** + * Allocate the backing store for the buffer. + */ +static int vmw_sou_backing_alloc(struct vmw_private *dev_priv, +				 struct vmw_screen_object_unit *sou, +				 unsigned long size) +{ +	int ret; + +	if (sou->buffer_size == size) +		return 0; + +	if (sou->buffer) +		vmw_sou_backing_free(dev_priv, sou); + +	sou->buffer = kzalloc(sizeof(*sou->buffer), GFP_KERNEL); +	if (unlikely(sou->buffer == NULL)) +		return -ENOMEM; + +	/* After we have alloced the backing store might not be able to +	 * resume the overlays, this is preferred to failing to alloc. +	 */ +	vmw_overlay_pause_all(dev_priv); +	ret = vmw_dmabuf_init(dev_priv, sou->buffer, size, +			      &vmw_vram_ne_placement, +			      false, &vmw_dmabuf_bo_free); +	vmw_overlay_resume_all(dev_priv); + +	if (unlikely(ret != 0)) +		sou->buffer = NULL; /* vmw_dmabuf_init frees on error */ +	else +		sou->buffer_size = size; + +	return ret; +} + +static int vmw_sou_crtc_set_config(struct drm_mode_set *set) +{ +	struct vmw_private *dev_priv; +	struct vmw_screen_object_unit *sou; +	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; +	int ret = 0; + +	if (!set) +		return -EINVAL; + +	if (!set->crtc) +		return -EINVAL; + +	/* get the sou */ +	crtc = set->crtc; +	sou = vmw_crtc_to_sou(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] != &sou->base.connector) { +		DRM_ERROR("connector doesn't match %p %p\n", +			set->connectors[0], &sou->base.connector); +		return -EINVAL; +	} + +	/* sou only supports one fb active at the time */ +	if (sou->base.is_implicit && +	    dev_priv->sou_priv->implicit_fb && vfb && +	    !(dev_priv->sou_priv->num_implicit == 1 && +	      sou->active_implicit) && +	    dev_priv->sou_priv->implicit_fb != vfb) { +		DRM_ERROR("Multiple framebuffers not supported\n"); +		return -EINVAL; +	} + +	/* since they always map one to one these are safe */ +	connector = &sou->base.connector; +	encoder = &sou->base.encoder; + +	/* should we turn the crtc off */ +	if (set->num_connectors == 0 || !set->mode || !set->fb) { +		ret = vmw_sou_fifo_destroy(dev_priv, sou); +		/* the hardware has hung don't do anything more */ +		if (unlikely(ret != 0)) +			return ret; + +		connector->encoder = NULL; +		encoder->crtc = NULL; +		crtc->primary->fb = NULL; +		crtc->x = 0; +		crtc->y = 0; +		crtc->enabled = false; + +		vmw_sou_del_active(dev_priv, sou); + +		vmw_sou_backing_free(dev_priv, sou); + +		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); + +	if (mode->hdisplay != crtc->mode.hdisplay || +	    mode->vdisplay != crtc->mode.vdisplay) { +		/* no need to check if depth is different, because backing +		 * store depth is forced to 4 by the device. +		 */ + +		ret = vmw_sou_fifo_destroy(dev_priv, sou); +		/* the hardware has hung don't do anything more */ +		if (unlikely(ret != 0)) +			return ret; + +		vmw_sou_backing_free(dev_priv, sou); +	} + +	if (!sou->buffer) { +		/* forced to depth 4 by the device */ +		size_t size = mode->hdisplay * mode->vdisplay * 4; +		ret = vmw_sou_backing_alloc(dev_priv, sou, size); +		if (unlikely(ret != 0)) +			return ret; +	} + +	ret = vmw_sou_fifo_create(dev_priv, sou, set->x, set->y, mode); +	if (unlikely(ret != 0)) { +		/* +		 * We are in a bit of a situation here, the hardware has +		 * hung and we may or may not have a buffer hanging of +		 * the screen object, best thing to do is not do anything +		 * if we where defined, if not just turn the crtc of. +		 * Not what userspace wants but it needs to htfu. +		 */ +		if (sou->defined) +			return ret; + +		connector->encoder = NULL; +		encoder->crtc = NULL; +		crtc->primary->fb = NULL; +		crtc->x = 0; +		crtc->y = 0; +		crtc->enabled = false; + +		return ret; +	} + +	vmw_sou_add_active(dev_priv, sou, vfb); + +	connector->encoder = encoder; +	encoder->crtc = crtc; +	crtc->mode = *mode; +	crtc->primary->fb = fb; +	crtc->x = set->x; +	crtc->y = set->y; +	crtc->enabled = true; + +	return 0; +} + +static struct drm_crtc_funcs vmw_screen_object_crtc_funcs = { +	.save = vmw_du_crtc_save, +	.restore = vmw_du_crtc_restore, +	.cursor_set = vmw_du_crtc_cursor_set, +	.cursor_move = vmw_du_crtc_cursor_move, +	.gamma_set = vmw_du_crtc_gamma_set, +	.destroy = vmw_sou_crtc_destroy, +	.set_config = vmw_sou_crtc_set_config, +	.page_flip = vmw_du_page_flip, +}; + +/* + * Screen Object Display Unit encoder functions + */ + +static void vmw_sou_encoder_destroy(struct drm_encoder *encoder) +{ +	vmw_sou_destroy(vmw_encoder_to_sou(encoder)); +} + +static struct drm_encoder_funcs vmw_screen_object_encoder_funcs = { +	.destroy = vmw_sou_encoder_destroy, +}; + +/* + * Screen Object Display Unit connector functions + */ + +static void vmw_sou_connector_destroy(struct drm_connector *connector) +{ +	vmw_sou_destroy(vmw_connector_to_sou(connector)); +} + +static struct drm_connector_funcs vmw_legacy_connector_funcs = { +	.dpms = vmw_du_connector_dpms, +	.save = vmw_du_connector_save, +	.restore = vmw_du_connector_restore, +	.detect = vmw_du_connector_detect, +	.fill_modes = vmw_du_connector_fill_modes, +	.set_property = vmw_du_connector_set_property, +	.destroy = vmw_sou_connector_destroy, +}; + +static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) +{ +	struct vmw_screen_object_unit *sou; +	struct drm_device *dev = dev_priv->dev; +	struct drm_connector *connector; +	struct drm_encoder *encoder; +	struct drm_crtc *crtc; + +	sou = kzalloc(sizeof(*sou), GFP_KERNEL); +	if (!sou) +		return -ENOMEM; + +	sou->base.unit = unit; +	crtc = &sou->base.crtc; +	encoder = &sou->base.encoder; +	connector = &sou->base.connector; + +	sou->active_implicit = false; + +	sou->base.pref_active = (unit == 0); +	sou->base.pref_width = dev_priv->initial_width; +	sou->base.pref_height = dev_priv->initial_height; +	sou->base.pref_mode = NULL; +	sou->base.is_implicit = true; + +	drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, +			   DRM_MODE_CONNECTOR_VIRTUAL); +	connector->status = vmw_du_connector_detect(connector, true); + +	drm_encoder_init(dev, encoder, &vmw_screen_object_encoder_funcs, +			 DRM_MODE_ENCODER_VIRTUAL); +	drm_mode_connector_attach_encoder(connector, encoder); +	encoder->possible_crtcs = (1 << unit); +	encoder->possible_clones = 0; + +	(void) drm_sysfs_connector_add(connector); + +	drm_crtc_init(dev, crtc, &vmw_screen_object_crtc_funcs); + +	drm_mode_crtc_set_gamma_size(crtc, 256); + +	drm_object_attach_property(&connector->base, +				      dev->mode_config.dirty_info_property, +				      1); + +	return 0; +} + +int vmw_kms_init_screen_object_display(struct vmw_private *dev_priv) +{ +	struct drm_device *dev = dev_priv->dev; +	int i, ret; + +	if (dev_priv->sou_priv) { +		DRM_INFO("sou system already on\n"); +		return -EINVAL; +	} + +	if (!(dev_priv->capabilities & SVGA_CAP_SCREEN_OBJECT_2)) { +		DRM_INFO("Not using screen objects," +			 " missing cap SCREEN_OBJECT_2\n"); +		return -ENOSYS; +	} + +	ret = -ENOMEM; +	dev_priv->sou_priv = kmalloc(sizeof(*dev_priv->sou_priv), GFP_KERNEL); +	if (unlikely(!dev_priv->sou_priv)) +		goto err_no_mem; + +	dev_priv->sou_priv->num_implicit = 0; +	dev_priv->sou_priv->implicit_fb = NULL; + +	ret = drm_vblank_init(dev, VMWGFX_NUM_DISPLAY_UNITS); +	if (unlikely(ret != 0)) +		goto err_free; + +	ret = drm_mode_create_dirty_info_property(dev); +	if (unlikely(ret != 0)) +		goto err_vblank_cleanup; + +	for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) +		vmw_sou_init(dev_priv, i); + +	DRM_INFO("Screen objects system initialized\n"); + +	return 0; + +err_vblank_cleanup: +	drm_vblank_cleanup(dev); +err_free: +	kfree(dev_priv->sou_priv); +	dev_priv->sou_priv = NULL; +err_no_mem: +	return ret; +} + +int vmw_kms_close_screen_object_display(struct vmw_private *dev_priv) +{ +	struct drm_device *dev = dev_priv->dev; + +	if (!dev_priv->sou_priv) +		return -ENOSYS; + +	drm_vblank_cleanup(dev); + +	kfree(dev_priv->sou_priv); + +	return 0; +} + +/** + * Returns if this unit can be page flipped. + * Must be called with the mode_config mutex held. + */ +bool vmw_kms_screen_object_flippable(struct vmw_private *dev_priv, +				     struct drm_crtc *crtc) +{ +	struct vmw_screen_object_unit *sou = vmw_crtc_to_sou(crtc); + +	if (!sou->base.is_implicit) +		return true; + +	if (dev_priv->sou_priv->num_implicit != 1) +		return false; + +	return true; +} + +/** + * Update the implicit fb to the current fb of this crtc. + * Must be called with the mode_config mutex held. + */ +void vmw_kms_screen_object_update_implicit_fb(struct vmw_private *dev_priv, +					      struct drm_crtc *crtc) +{ +	struct vmw_screen_object_unit *sou = vmw_crtc_to_sou(crtc); + +	BUG_ON(!sou->base.is_implicit); + +	dev_priv->sou_priv->implicit_fb = +		vmw_framebuffer_to_vfb(sou->base.crtc.primary->fb); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c new file mode 100644 index 00000000000..c1559eeaffe --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c @@ -0,0 +1,811 @@ +/************************************************************************** + * + * Copyright © 2009-2012 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_resource_priv.h" +#include "ttm/ttm_placement.h" + +#define VMW_COMPAT_SHADER_HT_ORDER 12 + +struct vmw_shader { +	struct vmw_resource res; +	SVGA3dShaderType type; +	uint32_t size; +}; + +struct vmw_user_shader { +	struct ttm_base_object base; +	struct vmw_shader shader; +}; + +/** + * enum vmw_compat_shader_state - Staging state for compat shaders + */ +enum vmw_compat_shader_state { +	VMW_COMPAT_COMMITED, +	VMW_COMPAT_ADD, +	VMW_COMPAT_DEL +}; + +/** + * struct vmw_compat_shader - Metadata for compat shaders. + * + * @handle: The TTM handle of the guest backed shader. + * @tfile: The struct ttm_object_file the guest backed shader is registered + * with. + * @hash: Hash item for lookup. + * @head: List head for staging lists or the compat shader manager list. + * @state: Staging state. + * + * The structure is protected by the cmdbuf lock. + */ +struct vmw_compat_shader { +	u32 handle; +	struct ttm_object_file *tfile; +	struct drm_hash_item hash; +	struct list_head head; +	enum vmw_compat_shader_state state; +}; + +/** + * struct vmw_compat_shader_manager - Compat shader manager. + * + * @shaders: Hash table containing staged and commited compat shaders + * @list: List of commited shaders. + * @dev_priv: Pointer to a device private structure. + * + * @shaders and @list are protected by the cmdbuf mutex for now. + */ +struct vmw_compat_shader_manager { +	struct drm_open_hash shaders; +	struct list_head list; +	struct vmw_private *dev_priv; +}; + +static void vmw_user_shader_free(struct vmw_resource *res); +static struct vmw_resource * +vmw_user_shader_base_to_res(struct ttm_base_object *base); + +static int vmw_gb_shader_create(struct vmw_resource *res); +static int vmw_gb_shader_bind(struct vmw_resource *res, +			       struct ttm_validate_buffer *val_buf); +static int vmw_gb_shader_unbind(struct vmw_resource *res, +				 bool readback, +				 struct ttm_validate_buffer *val_buf); +static int vmw_gb_shader_destroy(struct vmw_resource *res); + +static uint64_t vmw_user_shader_size; + +static const struct vmw_user_resource_conv user_shader_conv = { +	.object_type = VMW_RES_SHADER, +	.base_obj_to_res = vmw_user_shader_base_to_res, +	.res_free = vmw_user_shader_free +}; + +const struct vmw_user_resource_conv *user_shader_converter = +	&user_shader_conv; + + +static const struct vmw_res_func vmw_gb_shader_func = { +	.res_type = vmw_res_shader, +	.needs_backup = true, +	.may_evict = true, +	.type_name = "guest backed shaders", +	.backup_placement = &vmw_mob_placement, +	.create = vmw_gb_shader_create, +	.destroy = vmw_gb_shader_destroy, +	.bind = vmw_gb_shader_bind, +	.unbind = vmw_gb_shader_unbind +}; + +/** + * Shader management: + */ + +static inline struct vmw_shader * +vmw_res_to_shader(struct vmw_resource *res) +{ +	return container_of(res, struct vmw_shader, res); +} + +static void vmw_hw_shader_destroy(struct vmw_resource *res) +{ +	(void) vmw_gb_shader_destroy(res); +} + +static int vmw_gb_shader_init(struct vmw_private *dev_priv, +			      struct vmw_resource *res, +			      uint32_t size, +			      uint64_t offset, +			      SVGA3dShaderType type, +			      struct vmw_dma_buffer *byte_code, +			      void (*res_free) (struct vmw_resource *res)) +{ +	struct vmw_shader *shader = vmw_res_to_shader(res); +	int ret; + +	ret = vmw_resource_init(dev_priv, res, true, +				res_free, &vmw_gb_shader_func); + + +	if (unlikely(ret != 0)) { +		if (res_free) +			res_free(res); +		else +			kfree(res); +		return ret; +	} + +	res->backup_size = size; +	if (byte_code) { +		res->backup = vmw_dmabuf_reference(byte_code); +		res->backup_offset = offset; +	} +	shader->size = size; +	shader->type = type; + +	vmw_resource_activate(res, vmw_hw_shader_destroy); +	return 0; +} + +static int vmw_gb_shader_create(struct vmw_resource *res) +{ +	struct vmw_private *dev_priv = res->dev_priv; +	struct vmw_shader *shader = vmw_res_to_shader(res); +	int ret; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdDefineGBShader body; +	} *cmd; + +	if (likely(res->id != -1)) +		return 0; + +	ret = vmw_resource_alloc_id(res); +	if (unlikely(ret != 0)) { +		DRM_ERROR("Failed to allocate a shader id.\n"); +		goto out_no_id; +	} + +	if (unlikely(res->id >= VMWGFX_NUM_GB_SHADER)) { +		ret = -EBUSY; +		goto out_no_fifo; +	} + +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for shader " +			  "creation.\n"); +		ret = -ENOMEM; +		goto out_no_fifo; +	} + +	cmd->header.id = SVGA_3D_CMD_DEFINE_GB_SHADER; +	cmd->header.size = sizeof(cmd->body); +	cmd->body.shid = res->id; +	cmd->body.type = shader->type; +	cmd->body.sizeInBytes = shader->size; +	vmw_fifo_commit(dev_priv, sizeof(*cmd)); +	(void) vmw_3d_resource_inc(dev_priv, false); + +	return 0; + +out_no_fifo: +	vmw_resource_release_id(res); +out_no_id: +	return ret; +} + +static int vmw_gb_shader_bind(struct vmw_resource *res, +			      struct ttm_validate_buffer *val_buf) +{ +	struct vmw_private *dev_priv = res->dev_priv; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdBindGBShader body; +	} *cmd; +	struct ttm_buffer_object *bo = val_buf->bo; + +	BUG_ON(bo->mem.mem_type != VMW_PL_MOB); + +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for shader " +			  "binding.\n"); +		return -ENOMEM; +	} + +	cmd->header.id = SVGA_3D_CMD_BIND_GB_SHADER; +	cmd->header.size = sizeof(cmd->body); +	cmd->body.shid = res->id; +	cmd->body.mobid = bo->mem.start; +	cmd->body.offsetInBytes = 0; +	res->backup_dirty = false; +	vmw_fifo_commit(dev_priv, sizeof(*cmd)); + +	return 0; +} + +static int vmw_gb_shader_unbind(struct vmw_resource *res, +				bool readback, +				struct ttm_validate_buffer *val_buf) +{ +	struct vmw_private *dev_priv = res->dev_priv; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdBindGBShader body; +	} *cmd; +	struct vmw_fence_obj *fence; + +	BUG_ON(res->backup->base.mem.mem_type != VMW_PL_MOB); + +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for shader " +			  "unbinding.\n"); +		return -ENOMEM; +	} + +	cmd->header.id = SVGA_3D_CMD_BIND_GB_SHADER; +	cmd->header.size = sizeof(cmd->body); +	cmd->body.shid = res->id; +	cmd->body.mobid = SVGA3D_INVALID_ID; +	cmd->body.offsetInBytes = 0; +	vmw_fifo_commit(dev_priv, sizeof(*cmd)); + +	/* +	 * Create a fence object and fence the backup buffer. +	 */ + +	(void) vmw_execbuf_fence_commands(NULL, dev_priv, +					  &fence, NULL); + +	vmw_fence_single_bo(val_buf->bo, fence); + +	if (likely(fence != NULL)) +		vmw_fence_obj_unreference(&fence); + +	return 0; +} + +static int vmw_gb_shader_destroy(struct vmw_resource *res) +{ +	struct vmw_private *dev_priv = res->dev_priv; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdDestroyGBShader body; +	} *cmd; + +	if (likely(res->id == -1)) +		return 0; + +	mutex_lock(&dev_priv->binding_mutex); +	vmw_context_binding_res_list_scrub(&res->binding_head); + +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for shader " +			  "destruction.\n"); +		mutex_unlock(&dev_priv->binding_mutex); +		return -ENOMEM; +	} + +	cmd->header.id = SVGA_3D_CMD_DESTROY_GB_SHADER; +	cmd->header.size = sizeof(cmd->body); +	cmd->body.shid = res->id; +	vmw_fifo_commit(dev_priv, sizeof(*cmd)); +	mutex_unlock(&dev_priv->binding_mutex); +	vmw_resource_release_id(res); +	vmw_3d_resource_dec(dev_priv, false); + +	return 0; +} + +/** + * User-space shader management: + */ + +static struct vmw_resource * +vmw_user_shader_base_to_res(struct ttm_base_object *base) +{ +	return &(container_of(base, struct vmw_user_shader, base)-> +		 shader.res); +} + +static void vmw_user_shader_free(struct vmw_resource *res) +{ +	struct vmw_user_shader *ushader = +		container_of(res, struct vmw_user_shader, shader.res); +	struct vmw_private *dev_priv = res->dev_priv; + +	ttm_base_object_kfree(ushader, base); +	ttm_mem_global_free(vmw_mem_glob(dev_priv), +			    vmw_user_shader_size); +} + +/** + * 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_shader_base_release(struct ttm_base_object **p_base) +{ +	struct ttm_base_object *base = *p_base; +	struct vmw_resource *res = vmw_user_shader_base_to_res(base); + +	*p_base = NULL; +	vmw_resource_unreference(&res); +} + +int vmw_shader_destroy_ioctl(struct drm_device *dev, void *data, +			      struct drm_file *file_priv) +{ +	struct drm_vmw_shader_arg *arg = (struct drm_vmw_shader_arg *)data; +	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + +	return ttm_ref_object_base_unref(tfile, arg->handle, +					 TTM_REF_USAGE); +} + +static int vmw_shader_alloc(struct vmw_private *dev_priv, +			    struct vmw_dma_buffer *buffer, +			    size_t shader_size, +			    size_t offset, +			    SVGA3dShaderType shader_type, +			    struct ttm_object_file *tfile, +			    u32 *handle) +{ +	struct vmw_user_shader *ushader; +	struct vmw_resource *res, *tmp; +	int ret; + +	/* +	 * Approximate idr memory usage with 128 bytes. It will be limited +	 * by maximum number_of shaders anyway. +	 */ +	if (unlikely(vmw_user_shader_size == 0)) +		vmw_user_shader_size = +			ttm_round_pot(sizeof(struct vmw_user_shader)) + 128; + +	ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), +				   vmw_user_shader_size, +				   false, true); +	if (unlikely(ret != 0)) { +		if (ret != -ERESTARTSYS) +			DRM_ERROR("Out of graphics memory for shader " +				  "creation.\n"); +		goto out; +	} + +	ushader = kzalloc(sizeof(*ushader), GFP_KERNEL); +	if (unlikely(ushader == NULL)) { +		ttm_mem_global_free(vmw_mem_glob(dev_priv), +				    vmw_user_shader_size); +		ret = -ENOMEM; +		goto out; +	} + +	res = &ushader->shader.res; +	ushader->base.shareable = false; +	ushader->base.tfile = NULL; + +	/* +	 * From here on, the destructor takes over resource freeing. +	 */ + +	ret = vmw_gb_shader_init(dev_priv, res, shader_size, +				 offset, shader_type, buffer, +				 vmw_user_shader_free); +	if (unlikely(ret != 0)) +		goto out; + +	tmp = vmw_resource_reference(res); +	ret = ttm_base_object_init(tfile, &ushader->base, false, +				   VMW_RES_SHADER, +				   &vmw_user_shader_base_release, NULL); + +	if (unlikely(ret != 0)) { +		vmw_resource_unreference(&tmp); +		goto out_err; +	} + +	if (handle) +		*handle = ushader->base.hash.key; +out_err: +	vmw_resource_unreference(&res); +out: +	return ret; +} + + +int vmw_shader_define_ioctl(struct drm_device *dev, void *data, +			     struct drm_file *file_priv) +{ +	struct vmw_private *dev_priv = vmw_priv(dev); +	struct drm_vmw_shader_create_arg *arg = +		(struct drm_vmw_shader_create_arg *)data; +	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; +	struct vmw_dma_buffer *buffer = NULL; +	SVGA3dShaderType shader_type; +	int ret; + +	if (arg->buffer_handle != SVGA3D_INVALID_ID) { +		ret = vmw_user_dmabuf_lookup(tfile, arg->buffer_handle, +					     &buffer); +		if (unlikely(ret != 0)) { +			DRM_ERROR("Could not find buffer for shader " +				  "creation.\n"); +			return ret; +		} + +		if ((u64)buffer->base.num_pages * PAGE_SIZE < +		    (u64)arg->size + (u64)arg->offset) { +			DRM_ERROR("Illegal buffer- or shader size.\n"); +			ret = -EINVAL; +			goto out_bad_arg; +		} +	} + +	switch (arg->shader_type) { +	case drm_vmw_shader_type_vs: +		shader_type = SVGA3D_SHADERTYPE_VS; +		break; +	case drm_vmw_shader_type_ps: +		shader_type = SVGA3D_SHADERTYPE_PS; +		break; +	case drm_vmw_shader_type_gs: +		shader_type = SVGA3D_SHADERTYPE_GS; +		break; +	default: +		DRM_ERROR("Illegal shader type.\n"); +		ret = -EINVAL; +		goto out_bad_arg; +	} + +	ret = ttm_read_lock(&dev_priv->reservation_sem, true); +	if (unlikely(ret != 0)) +		goto out_bad_arg; + +	ret = vmw_shader_alloc(dev_priv, buffer, arg->size, arg->offset, +			       shader_type, tfile, &arg->shader_handle); + +	ttm_read_unlock(&dev_priv->reservation_sem); +out_bad_arg: +	vmw_dmabuf_unreference(&buffer); +	return ret; +} + +/** + * vmw_compat_shader_lookup - Look up a compat shader + * + * @man: Pointer to the compat shader manager. + * @shader_type: The shader type, that combined with the user_key identifies + * the shader. + * @user_key: On entry, this should be a pointer to the user_key. + * On successful exit, it will contain the guest-backed shader's TTM handle. + * + * Returns 0 on success. Non-zero on failure, in which case the value pointed + * to by @user_key is unmodified. + */ +int vmw_compat_shader_lookup(struct vmw_compat_shader_manager *man, +			     SVGA3dShaderType shader_type, +			     u32 *user_key) +{ +	struct drm_hash_item *hash; +	int ret; +	unsigned long key = *user_key | (shader_type << 24); + +	ret = drm_ht_find_item(&man->shaders, key, &hash); +	if (unlikely(ret != 0)) +		return ret; + +	*user_key = drm_hash_entry(hash, struct vmw_compat_shader, +				   hash)->handle; + +	return 0; +} + +/** + * vmw_compat_shader_free - Free a compat shader. + * + * @man: Pointer to the compat shader manager. + * @entry: Pointer to a struct vmw_compat_shader. + * + * Frees a struct vmw_compat_shder entry and drops its reference to the + * guest backed shader. + */ +static void vmw_compat_shader_free(struct vmw_compat_shader_manager *man, +				   struct vmw_compat_shader *entry) +{ +	list_del(&entry->head); +	WARN_ON(drm_ht_remove_item(&man->shaders, &entry->hash)); +	WARN_ON(ttm_ref_object_base_unref(entry->tfile, entry->handle, +					  TTM_REF_USAGE)); +	kfree(entry); +} + +/** + * vmw_compat_shaders_commit - Commit a list of compat shader actions. + * + * @man: Pointer to the compat shader manager. + * @list: Caller's list of compat shader actions. + * + * This function commits a list of compat shader additions or removals. + * It is typically called when the execbuf ioctl call triggering these + * actions has commited the fifo contents to the device. + */ +void vmw_compat_shaders_commit(struct vmw_compat_shader_manager *man, +			       struct list_head *list) +{ +	struct vmw_compat_shader *entry, *next; + +	list_for_each_entry_safe(entry, next, list, head) { +		list_del(&entry->head); +		switch (entry->state) { +		case VMW_COMPAT_ADD: +			entry->state = VMW_COMPAT_COMMITED; +			list_add_tail(&entry->head, &man->list); +			break; +		case VMW_COMPAT_DEL: +			ttm_ref_object_base_unref(entry->tfile, entry->handle, +						  TTM_REF_USAGE); +			kfree(entry); +			break; +		default: +			BUG(); +			break; +		} +	} +} + +/** + * vmw_compat_shaders_revert - Revert a list of compat shader actions + * + * @man: Pointer to the compat shader manager. + * @list: Caller's list of compat shader actions. + * + * This function reverts a list of compat shader additions or removals. + * It is typically called when the execbuf ioctl call triggering these + * actions failed for some reason, and the command stream was never + * submitted. + */ +void vmw_compat_shaders_revert(struct vmw_compat_shader_manager *man, +			       struct list_head *list) +{ +	struct vmw_compat_shader *entry, *next; +	int ret; + +	list_for_each_entry_safe(entry, next, list, head) { +		switch (entry->state) { +		case VMW_COMPAT_ADD: +			vmw_compat_shader_free(man, entry); +			break; +		case VMW_COMPAT_DEL: +			ret = drm_ht_insert_item(&man->shaders, &entry->hash); +			list_del(&entry->head); +			list_add_tail(&entry->head, &man->list); +			entry->state = VMW_COMPAT_COMMITED; +			break; +		default: +			BUG(); +			break; +		} +	} +} + +/** + * vmw_compat_shader_remove - Stage a compat shader for removal. + * + * @man: Pointer to the compat shader manager + * @user_key: The key that is used to identify the shader. The key is + * unique to the shader type. + * @shader_type: Shader type. + * @list: Caller's list of staged shader actions. + * + * This function stages a compat shader for removal and removes the key from + * the shader manager's hash table. If the shader was previously only staged + * for addition it is completely removed (But the execbuf code may keep a + * reference if it was bound to a context between addition and removal). If + * it was previously commited to the manager, it is staged for removal. + */ +int vmw_compat_shader_remove(struct vmw_compat_shader_manager *man, +			     u32 user_key, SVGA3dShaderType shader_type, +			     struct list_head *list) +{ +	struct vmw_compat_shader *entry; +	struct drm_hash_item *hash; +	int ret; + +	ret = drm_ht_find_item(&man->shaders, user_key | (shader_type << 24), +			       &hash); +	if (likely(ret != 0)) +		return -EINVAL; + +	entry = drm_hash_entry(hash, struct vmw_compat_shader, hash); + +	switch (entry->state) { +	case VMW_COMPAT_ADD: +		vmw_compat_shader_free(man, entry); +		break; +	case VMW_COMPAT_COMMITED: +		(void) drm_ht_remove_item(&man->shaders, &entry->hash); +		list_del(&entry->head); +		entry->state = VMW_COMPAT_DEL; +		list_add_tail(&entry->head, list); +		break; +	default: +		BUG(); +		break; +	} + +	return 0; +} + +/** + * vmw_compat_shader_add - Create a compat shader and add the + * key to the manager + * + * @man: Pointer to the compat shader manager + * @user_key: The key that is used to identify the shader. The key is + * unique to the shader type. + * @bytecode: Pointer to the bytecode of the shader. + * @shader_type: Shader type. + * @tfile: Pointer to a struct ttm_object_file that the guest-backed shader is + * to be created with. + * @list: Caller's list of staged shader actions. + * + * Note that only the key is added to the shader manager's hash table. + * The shader is not yet added to the shader manager's list of shaders. + */ +int vmw_compat_shader_add(struct vmw_compat_shader_manager *man, +			  u32 user_key, const void *bytecode, +			  SVGA3dShaderType shader_type, +			  size_t size, +			  struct ttm_object_file *tfile, +			  struct list_head *list) +{ +	struct vmw_dma_buffer *buf; +	struct ttm_bo_kmap_obj map; +	bool is_iomem; +	struct vmw_compat_shader *compat; +	u32 handle; +	int ret; + +	if (user_key > ((1 << 24) - 1) || (unsigned) shader_type > 16) +		return -EINVAL; + +	/* Allocate and pin a DMA buffer */ +	buf = kzalloc(sizeof(*buf), GFP_KERNEL); +	if (unlikely(buf == NULL)) +		return -ENOMEM; + +	ret = vmw_dmabuf_init(man->dev_priv, buf, size, &vmw_sys_ne_placement, +			      true, vmw_dmabuf_bo_free); +	if (unlikely(ret != 0)) +		goto out; + +	ret = ttm_bo_reserve(&buf->base, false, true, false, NULL); +	if (unlikely(ret != 0)) +		goto no_reserve; + +	/* Map and copy shader bytecode. */ +	ret = ttm_bo_kmap(&buf->base, 0, PAGE_ALIGN(size) >> PAGE_SHIFT, +			  &map); +	if (unlikely(ret != 0)) { +		ttm_bo_unreserve(&buf->base); +		goto no_reserve; +	} + +	memcpy(ttm_kmap_obj_virtual(&map, &is_iomem), bytecode, size); +	WARN_ON(is_iomem); + +	ttm_bo_kunmap(&map); +	ret = ttm_bo_validate(&buf->base, &vmw_sys_placement, false, true); +	WARN_ON(ret != 0); +	ttm_bo_unreserve(&buf->base); + +	/* Create a guest-backed shader container backed by the dma buffer */ +	ret = vmw_shader_alloc(man->dev_priv, buf, size, 0, shader_type, +			       tfile, &handle); +	vmw_dmabuf_unreference(&buf); +	if (unlikely(ret != 0)) +		goto no_reserve; +	/* +	 * Create a compat shader structure and stage it for insertion +	 * in the manager +	 */ +	compat = kzalloc(sizeof(*compat), GFP_KERNEL); +	if (compat == NULL) +		goto no_compat; + +	compat->hash.key = user_key |  (shader_type << 24); +	ret = drm_ht_insert_item(&man->shaders, &compat->hash); +	if (unlikely(ret != 0)) +		goto out_invalid_key; + +	compat->state = VMW_COMPAT_ADD; +	compat->handle = handle; +	compat->tfile = tfile; +	list_add_tail(&compat->head, list); + +	return 0; + +out_invalid_key: +	kfree(compat); +no_compat: +	ttm_ref_object_base_unref(tfile, handle, TTM_REF_USAGE); +no_reserve: +out: +	return ret; +} + +/** + * vmw_compat_shader_man_create - Create a compat shader manager + * + * @dev_priv: Pointer to a device private structure. + * + * Typically done at file open time. If successful returns a pointer to a + * compat shader manager. Otherwise returns an error pointer. + */ +struct vmw_compat_shader_manager * +vmw_compat_shader_man_create(struct vmw_private *dev_priv) +{ +	struct vmw_compat_shader_manager *man; +	int ret; + +	man = kzalloc(sizeof(*man), GFP_KERNEL); +	if (man == NULL) +		return ERR_PTR(-ENOMEM); + +	man->dev_priv = dev_priv; +	INIT_LIST_HEAD(&man->list); +	ret = drm_ht_create(&man->shaders, VMW_COMPAT_SHADER_HT_ORDER); +	if (ret == 0) +		return man; + +	kfree(man); +	return ERR_PTR(ret); +} + +/** + * vmw_compat_shader_man_destroy - Destroy a compat shader manager + * + * @man: Pointer to the shader manager to destroy. + * + * Typically done at file close time. + */ +void vmw_compat_shader_man_destroy(struct vmw_compat_shader_manager *man) +{ +	struct vmw_compat_shader *entry, *next; + +	mutex_lock(&man->dev_priv->cmdbuf_mutex); +	list_for_each_entry_safe(entry, next, &man->list, head) +		vmw_compat_shader_free(man, entry); + +	mutex_unlock(&man->dev_priv->cmdbuf_mutex); +	kfree(man); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c new file mode 100644 index 00000000000..4ecdbf3e59d --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -0,0 +1,1431 @@ +/************************************************************************** + * + * Copyright © 2009-2012 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_resource_priv.h" +#include <ttm/ttm_placement.h> +#include "svga3d_surfacedefs.h" + +/** + * struct vmw_user_surface - User-space visible surface resource + * + * @base:           The TTM base object handling user-space visibility. + * @srf:            The surface metadata. + * @size:           TTM accounting size for the surface. + * @master:         master of the creating client. Used for security check. + */ +struct vmw_user_surface { +	struct ttm_prime_object prime; +	struct vmw_surface srf; +	uint32_t size; +	struct drm_master *master; +}; + +/** + * struct vmw_surface_offset - Backing store mip level offset info + * + * @face:           Surface face. + * @mip:            Mip level. + * @bo_offset:      Offset into backing store of this mip level. + * + */ +struct vmw_surface_offset { +	uint32_t face; +	uint32_t mip; +	uint32_t bo_offset; +}; + +static void vmw_user_surface_free(struct vmw_resource *res); +static struct vmw_resource * +vmw_user_surface_base_to_res(struct ttm_base_object *base); +static int vmw_legacy_srf_bind(struct vmw_resource *res, +			       struct ttm_validate_buffer *val_buf); +static int vmw_legacy_srf_unbind(struct vmw_resource *res, +				 bool readback, +				 struct ttm_validate_buffer *val_buf); +static int vmw_legacy_srf_create(struct vmw_resource *res); +static int vmw_legacy_srf_destroy(struct vmw_resource *res); +static int vmw_gb_surface_create(struct vmw_resource *res); +static int vmw_gb_surface_bind(struct vmw_resource *res, +			       struct ttm_validate_buffer *val_buf); +static int vmw_gb_surface_unbind(struct vmw_resource *res, +				 bool readback, +				 struct ttm_validate_buffer *val_buf); +static int vmw_gb_surface_destroy(struct vmw_resource *res); + + +static const struct vmw_user_resource_conv user_surface_conv = { +	.object_type = VMW_RES_SURFACE, +	.base_obj_to_res = vmw_user_surface_base_to_res, +	.res_free = vmw_user_surface_free +}; + +const struct vmw_user_resource_conv *user_surface_converter = +	&user_surface_conv; + + +static uint64_t vmw_user_surface_size; + +static const struct vmw_res_func vmw_legacy_surface_func = { +	.res_type = vmw_res_surface, +	.needs_backup = false, +	.may_evict = true, +	.type_name = "legacy surfaces", +	.backup_placement = &vmw_srf_placement, +	.create = &vmw_legacy_srf_create, +	.destroy = &vmw_legacy_srf_destroy, +	.bind = &vmw_legacy_srf_bind, +	.unbind = &vmw_legacy_srf_unbind +}; + +static const struct vmw_res_func vmw_gb_surface_func = { +	.res_type = vmw_res_surface, +	.needs_backup = true, +	.may_evict = true, +	.type_name = "guest backed surfaces", +	.backup_placement = &vmw_mob_placement, +	.create = vmw_gb_surface_create, +	.destroy = vmw_gb_surface_destroy, +	.bind = vmw_gb_surface_bind, +	.unbind = vmw_gb_surface_unbind +}; + +/** + * struct vmw_surface_dma - SVGA3D DMA command + */ +struct vmw_surface_dma { +	SVGA3dCmdHeader header; +	SVGA3dCmdSurfaceDMA body; +	SVGA3dCopyBox cb; +	SVGA3dCmdSurfaceDMASuffix suffix; +}; + +/** + * struct vmw_surface_define - SVGA3D Surface Define command + */ +struct vmw_surface_define { +	SVGA3dCmdHeader header; +	SVGA3dCmdDefineSurface body; +}; + +/** + * struct vmw_surface_destroy - SVGA3D Surface Destroy command + */ +struct vmw_surface_destroy { +	SVGA3dCmdHeader header; +	SVGA3dCmdDestroySurface body; +}; + + +/** + * vmw_surface_dma_size - Compute fifo size for a dma command. + * + * @srf: Pointer to a struct vmw_surface + * + * Computes the required size for a surface dma command for backup or + * restoration of the surface represented by @srf. + */ +static inline uint32_t vmw_surface_dma_size(const struct vmw_surface *srf) +{ +	return srf->num_sizes * sizeof(struct vmw_surface_dma); +} + + +/** + * vmw_surface_define_size - Compute fifo size for a surface define command. + * + * @srf: Pointer to a struct vmw_surface + * + * Computes the required size for a surface define command for the definition + * of the surface represented by @srf. + */ +static inline uint32_t vmw_surface_define_size(const struct vmw_surface *srf) +{ +	return sizeof(struct vmw_surface_define) + srf->num_sizes * +		sizeof(SVGA3dSize); +} + + +/** + * vmw_surface_destroy_size - Compute fifo size for a surface destroy command. + * + * Computes the required size for a surface destroy command for the destruction + * of a hw surface. + */ +static inline uint32_t vmw_surface_destroy_size(void) +{ +	return sizeof(struct vmw_surface_destroy); +} + +/** + * vmw_surface_destroy_encode - Encode a surface_destroy command. + * + * @id: The surface id + * @cmd_space: Pointer to memory area in which the commands should be encoded. + */ +static void vmw_surface_destroy_encode(uint32_t id, +				       void *cmd_space) +{ +	struct vmw_surface_destroy *cmd = (struct vmw_surface_destroy *) +		cmd_space; + +	cmd->header.id = SVGA_3D_CMD_SURFACE_DESTROY; +	cmd->header.size = sizeof(cmd->body); +	cmd->body.sid = id; +} + +/** + * vmw_surface_define_encode - Encode a surface_define command. + * + * @srf: Pointer to a struct vmw_surface object. + * @cmd_space: Pointer to memory area in which the commands should be encoded. + */ +static void vmw_surface_define_encode(const struct vmw_surface *srf, +				      void *cmd_space) +{ +	struct vmw_surface_define *cmd = (struct vmw_surface_define *) +		cmd_space; +	struct drm_vmw_size *src_size; +	SVGA3dSize *cmd_size; +	uint32_t cmd_len; +	int i; + +	cmd_len = sizeof(cmd->body) + srf->num_sizes * sizeof(SVGA3dSize); + +	cmd->header.id = SVGA_3D_CMD_SURFACE_DEFINE; +	cmd->header.size = cmd_len; +	cmd->body.sid = srf->res.id; +	cmd->body.surfaceFlags = 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 = 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 = src_size->width; +		cmd_size->height = src_size->height; +		cmd_size->depth = src_size->depth; +	} +} + +/** + * vmw_surface_dma_encode - Encode a surface_dma command. + * + * @srf: Pointer to a struct vmw_surface object. + * @cmd_space: Pointer to memory area in which the commands should be encoded. + * @ptr: Pointer to an SVGAGuestPtr indicating where the surface contents + * should be placed or read from. + * @to_surface: Boolean whether to DMA to the surface or from the surface. + */ +static void vmw_surface_dma_encode(struct vmw_surface *srf, +				   void *cmd_space, +				   const SVGAGuestPtr *ptr, +				   bool to_surface) +{ +	uint32_t i; +	struct vmw_surface_dma *cmd = (struct vmw_surface_dma *)cmd_space; +	const struct svga3d_surface_desc *desc = +		svga3dsurface_get_desc(srf->format); + +	for (i = 0; i < srf->num_sizes; ++i) { +		SVGA3dCmdHeader *header = &cmd->header; +		SVGA3dCmdSurfaceDMA *body = &cmd->body; +		SVGA3dCopyBox *cb = &cmd->cb; +		SVGA3dCmdSurfaceDMASuffix *suffix = &cmd->suffix; +		const struct vmw_surface_offset *cur_offset = &srf->offsets[i]; +		const struct drm_vmw_size *cur_size = &srf->sizes[i]; + +		header->id = SVGA_3D_CMD_SURFACE_DMA; +		header->size = sizeof(*body) + sizeof(*cb) + sizeof(*suffix); + +		body->guest.ptr = *ptr; +		body->guest.ptr.offset += cur_offset->bo_offset; +		body->guest.pitch = svga3dsurface_calculate_pitch(desc, +								  cur_size); +		body->host.sid = srf->res.id; +		body->host.face = cur_offset->face; +		body->host.mipmap = cur_offset->mip; +		body->transfer = ((to_surface) ?  SVGA3D_WRITE_HOST_VRAM : +				  SVGA3D_READ_HOST_VRAM); +		cb->x = 0; +		cb->y = 0; +		cb->z = 0; +		cb->srcx = 0; +		cb->srcy = 0; +		cb->srcz = 0; +		cb->w = cur_size->width; +		cb->h = cur_size->height; +		cb->d = cur_size->depth; + +		suffix->suffixSize = sizeof(*suffix); +		suffix->maximumOffset = +			svga3dsurface_get_image_buffer_size(desc, cur_size, +							    body->guest.pitch); +		suffix->flags.discard = 0; +		suffix->flags.unsynchronized = 0; +		suffix->flags.reserved = 0; +		++cmd; +	} +}; + + +/** + * vmw_hw_surface_destroy - destroy a Device surface + * + * @res:        Pointer to a struct vmw_resource embedded in a struct + *              vmw_surface. + * + * Destroys a the device surface associated with a struct vmw_surface if + * any, and adjusts accounting and resource count accordingly. + */ +static void vmw_hw_surface_destroy(struct vmw_resource *res) +{ + +	struct vmw_private *dev_priv = res->dev_priv; +	struct vmw_surface *srf; +	void *cmd; + +	if (res->func->destroy == vmw_gb_surface_destroy) { +		(void) vmw_gb_surface_destroy(res); +		return; +	} + +	if (res->id != -1) { + +		cmd = vmw_fifo_reserve(dev_priv, vmw_surface_destroy_size()); +		if (unlikely(cmd == NULL)) { +			DRM_ERROR("Failed reserving FIFO space for surface " +				  "destruction.\n"); +			return; +		} + +		vmw_surface_destroy_encode(res->id, cmd); +		vmw_fifo_commit(dev_priv, vmw_surface_destroy_size()); + +		/* +		 * used_memory_size_atomic, or separate lock +		 * to avoid taking dev_priv::cmdbuf_mutex in +		 * the destroy path. +		 */ + +		mutex_lock(&dev_priv->cmdbuf_mutex); +		srf = vmw_res_to_srf(res); +		dev_priv->used_memory_size -= res->backup_size; +		mutex_unlock(&dev_priv->cmdbuf_mutex); +	} +	vmw_3d_resource_dec(dev_priv, false); +} + +/** + * vmw_legacy_srf_create - Create a device surface as part of the + * resource validation process. + * + * @res: Pointer to a struct vmw_surface. + * + * If the surface doesn't have a hw id. + * + * Returns -EBUSY if there wasn't sufficient device resources to + * complete the validation. Retry after freeing up resources. + * + * May return other errors if the kernel is out of guest resources. + */ +static int vmw_legacy_srf_create(struct vmw_resource *res) +{ +	struct vmw_private *dev_priv = res->dev_priv; +	struct vmw_surface *srf; +	uint32_t submit_size; +	uint8_t *cmd; +	int ret; + +	if (likely(res->id != -1)) +		return 0; + +	srf = vmw_res_to_srf(res); +	if (unlikely(dev_priv->used_memory_size + res->backup_size >= +		     dev_priv->memory_size)) +		return -EBUSY; + +	/* +	 * Alloc id for the resource. +	 */ + +	ret = vmw_resource_alloc_id(res); +	if (unlikely(ret != 0)) { +		DRM_ERROR("Failed to allocate a surface id.\n"); +		goto out_no_id; +	} + +	if (unlikely(res->id >= SVGA3D_MAX_SURFACE_IDS)) { +		ret = -EBUSY; +		goto out_no_fifo; +	} + +	/* +	 * Encode surface define- commands. +	 */ + +	submit_size = vmw_surface_define_size(srf); +	cmd = vmw_fifo_reserve(dev_priv, submit_size); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for surface " +			  "creation.\n"); +		ret = -ENOMEM; +		goto out_no_fifo; +	} + +	vmw_surface_define_encode(srf, cmd); +	vmw_fifo_commit(dev_priv, submit_size); +	/* +	 * Surface memory usage accounting. +	 */ + +	dev_priv->used_memory_size += res->backup_size; +	return 0; + +out_no_fifo: +	vmw_resource_release_id(res); +out_no_id: +	return ret; +} + +/** + * vmw_legacy_srf_dma - Copy backup data to or from a legacy surface. + * + * @res:            Pointer to a struct vmw_res embedded in a struct + *                  vmw_surface. + * @val_buf:        Pointer to a struct ttm_validate_buffer containing + *                  information about the backup buffer. + * @bind:           Boolean wether to DMA to the surface. + * + * Transfer backup data to or from a legacy surface as part of the + * validation process. + * May return other errors if the kernel is out of guest resources. + * The backup buffer will be fenced or idle upon successful completion, + * and if the surface needs persistent backup storage, the backup buffer + * will also be returned reserved iff @bind is true. + */ +static int vmw_legacy_srf_dma(struct vmw_resource *res, +			      struct ttm_validate_buffer *val_buf, +			      bool bind) +{ +	SVGAGuestPtr ptr; +	struct vmw_fence_obj *fence; +	uint32_t submit_size; +	struct vmw_surface *srf = vmw_res_to_srf(res); +	uint8_t *cmd; +	struct vmw_private *dev_priv = res->dev_priv; + +	BUG_ON(val_buf->bo == NULL); + +	submit_size = vmw_surface_dma_size(srf); +	cmd = vmw_fifo_reserve(dev_priv, submit_size); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for surface " +			  "DMA.\n"); +		return -ENOMEM; +	} +	vmw_bo_get_guest_ptr(val_buf->bo, &ptr); +	vmw_surface_dma_encode(srf, cmd, &ptr, bind); + +	vmw_fifo_commit(dev_priv, submit_size); + +	/* +	 * Create a fence object and fence the backup buffer. +	 */ + +	(void) vmw_execbuf_fence_commands(NULL, dev_priv, +					  &fence, NULL); + +	vmw_fence_single_bo(val_buf->bo, fence); + +	if (likely(fence != NULL)) +		vmw_fence_obj_unreference(&fence); + +	return 0; +} + +/** + * vmw_legacy_srf_bind - Perform a legacy surface bind as part of the + *                       surface validation process. + * + * @res:            Pointer to a struct vmw_res embedded in a struct + *                  vmw_surface. + * @val_buf:        Pointer to a struct ttm_validate_buffer containing + *                  information about the backup buffer. + * + * This function will copy backup data to the surface if the + * backup buffer is dirty. + */ +static int vmw_legacy_srf_bind(struct vmw_resource *res, +			       struct ttm_validate_buffer *val_buf) +{ +	if (!res->backup_dirty) +		return 0; + +	return vmw_legacy_srf_dma(res, val_buf, true); +} + + +/** + * vmw_legacy_srf_unbind - Perform a legacy surface unbind as part of the + *                         surface eviction process. + * + * @res:            Pointer to a struct vmw_res embedded in a struct + *                  vmw_surface. + * @val_buf:        Pointer to a struct ttm_validate_buffer containing + *                  information about the backup buffer. + * + * This function will copy backup data from the surface. + */ +static int vmw_legacy_srf_unbind(struct vmw_resource *res, +				 bool readback, +				 struct ttm_validate_buffer *val_buf) +{ +	if (unlikely(readback)) +		return vmw_legacy_srf_dma(res, val_buf, false); +	return 0; +} + +/** + * vmw_legacy_srf_destroy - Destroy a device surface as part of a + *                          resource eviction process. + * + * @res:            Pointer to a struct vmw_res embedded in a struct + *                  vmw_surface. + */ +static int vmw_legacy_srf_destroy(struct vmw_resource *res) +{ +	struct vmw_private *dev_priv = res->dev_priv; +	uint32_t submit_size; +	uint8_t *cmd; + +	BUG_ON(res->id == -1); + +	/* +	 * Encode the dma- and surface destroy commands. +	 */ + +	submit_size = vmw_surface_destroy_size(); +	cmd = vmw_fifo_reserve(dev_priv, submit_size); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for surface " +			  "eviction.\n"); +		return -ENOMEM; +	} + +	vmw_surface_destroy_encode(res->id, cmd); +	vmw_fifo_commit(dev_priv, submit_size); + +	/* +	 * Surface memory usage accounting. +	 */ + +	dev_priv->used_memory_size -= res->backup_size; + +	/* +	 * Release the surface ID. +	 */ + +	vmw_resource_release_id(res); + +	return 0; +} + + +/** + * vmw_surface_init - initialize a struct vmw_surface + * + * @dev_priv:       Pointer to a device private struct. + * @srf:            Pointer to the struct vmw_surface to initialize. + * @res_free:       Pointer to a resource destructor used to free + *                  the object. + */ +static int vmw_surface_init(struct vmw_private *dev_priv, +			    struct vmw_surface *srf, +			    void (*res_free) (struct vmw_resource *res)) +{ +	int ret; +	struct vmw_resource *res = &srf->res; + +	BUG_ON(res_free == NULL); +	if (!dev_priv->has_mob) +		(void) vmw_3d_resource_inc(dev_priv, false); +	ret = vmw_resource_init(dev_priv, res, true, res_free, +				(dev_priv->has_mob) ? &vmw_gb_surface_func : +				&vmw_legacy_surface_func); + +	if (unlikely(ret != 0)) { +		if (!dev_priv->has_mob) +			vmw_3d_resource_dec(dev_priv, false); +		res_free(res); +		return ret; +	} + +	/* +	 * The surface won't be visible to hardware until a +	 * surface validate. +	 */ + +	vmw_resource_activate(res, vmw_hw_surface_destroy); +	return ret; +} + +/** + * vmw_user_surface_base_to_res - TTM base object to resource converter for + *                                user visible surfaces + * + * @base:           Pointer to a TTM base object + * + * Returns the struct vmw_resource embedded in a struct vmw_surface + * for the user-visible object identified by the TTM base object @base. + */ +static struct vmw_resource * +vmw_user_surface_base_to_res(struct ttm_base_object *base) +{ +	return &(container_of(base, struct vmw_user_surface, +			      prime.base)->srf.res); +} + +/** + * vmw_user_surface_free - User visible surface resource destructor + * + * @res:            A struct vmw_resource embedded in a struct vmw_surface. + */ +static void vmw_user_surface_free(struct vmw_resource *res) +{ +	struct vmw_surface *srf = vmw_res_to_srf(res); +	struct vmw_user_surface *user_srf = +	    container_of(srf, struct vmw_user_surface, srf); +	struct vmw_private *dev_priv = srf->res.dev_priv; +	uint32_t size = user_srf->size; + +	if (user_srf->master) +		drm_master_put(&user_srf->master); +	kfree(srf->offsets); +	kfree(srf->sizes); +	kfree(srf->snooper.image); +	ttm_prime_object_kfree(user_srf, prime); +	ttm_mem_global_free(vmw_mem_glob(dev_priv), size); +} + +/** + * vmw_user_surface_free - User visible surface TTM base object destructor + * + * @p_base:         Pointer to a pointer to a TTM base object + *                  embedded in a struct vmw_user_surface. + * + * Drops the base object's reference on its resource, and the + * pointer pointed to by *p_base is set to NULL. + */ +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, prime.base); +	struct vmw_resource *res = &user_srf->srf.res; + +	*p_base = NULL; +	vmw_resource_unreference(&res); +} + +/** + * vmw_user_surface_destroy_ioctl - Ioctl function implementing + *                                  the user surface destroy functionality. + * + * @dev:            Pointer to a struct drm_device. + * @data:           Pointer to data copied from / to user-space. + * @file_priv:      Pointer to a drm file private structure. + */ +int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data, +			      struct drm_file *file_priv) +{ +	struct drm_vmw_surface_arg *arg = (struct drm_vmw_surface_arg *)data; +	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + +	return ttm_ref_object_base_unref(tfile, arg->sid, TTM_REF_USAGE); +} + +/** + * vmw_user_surface_define_ioctl - Ioctl function implementing + *                                  the user surface define functionality. + * + * @dev:            Pointer to a struct drm_device. + * @data:           Pointer to data copied from / to user-space. + * @file_priv:      Pointer to a drm file private structure. + */ +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; +	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, j; +	uint32_t cur_bo_offset; +	struct drm_vmw_size *cur_size; +	struct vmw_surface_offset *cur_offset; +	uint32_t num_sizes; +	uint32_t size; +	const struct svga3d_surface_desc *desc; + +	if (unlikely(vmw_user_surface_size == 0)) +		vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) + +			128; + +	num_sizes = 0; +	for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) +		num_sizes += req->mip_levels[i]; + +	if (num_sizes > DRM_VMW_MAX_SURFACE_FACES * +	    DRM_VMW_MAX_MIP_LEVELS) +		return -EINVAL; + +	size = vmw_user_surface_size + 128 + +		ttm_round_pot(num_sizes * sizeof(struct drm_vmw_size)) + +		ttm_round_pot(num_sizes * sizeof(struct vmw_surface_offset)); + + +	desc = svga3dsurface_get_desc(req->format); +	if (unlikely(desc->block_desc == SVGA3DBLOCKDESC_NONE)) { +		DRM_ERROR("Invalid surface format for surface creation.\n"); +		return -EINVAL; +	} + +	ret = ttm_read_lock(&dev_priv->reservation_sem, true); +	if (unlikely(ret != 0)) +		return ret; + +	ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), +				   size, false, true); +	if (unlikely(ret != 0)) { +		if (ret != -ERESTARTSYS) +			DRM_ERROR("Out of graphics memory for surface" +				  " creation.\n"); +		goto out_unlock; +	} + +	user_srf = kzalloc(sizeof(*user_srf), GFP_KERNEL); +	if (unlikely(user_srf == NULL)) { +		ret = -ENOMEM; +		goto out_no_user_srf; +	} + +	srf = &user_srf->srf; +	res = &srf->res; + +	srf->flags = req->flags; +	srf->format = req->format; +	srf->scanout = req->scanout; + +	memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels)); +	srf->num_sizes = num_sizes; +	user_srf->size = size; + +	srf->sizes = kmalloc(srf->num_sizes * sizeof(*srf->sizes), GFP_KERNEL); +	if (unlikely(srf->sizes == NULL)) { +		ret = -ENOMEM; +		goto out_no_sizes; +	} +	srf->offsets = kmalloc(srf->num_sizes * sizeof(*srf->offsets), +			       GFP_KERNEL); +	if (unlikely(srf->sizes == NULL)) { +		ret = -ENOMEM; +		goto out_no_offsets; +	} + +	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)) { +		ret = -EFAULT; +		goto out_no_copy; +	} + +	srf->base_size = *srf->sizes; +	srf->autogen_filter = SVGA3D_TEX_FILTER_NONE; +	srf->multisample_count = 0; + +	cur_bo_offset = 0; +	cur_offset = srf->offsets; +	cur_size = srf->sizes; + +	for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) { +		for (j = 0; j < srf->mip_levels[i]; ++j) { +			uint32_t stride = svga3dsurface_calculate_pitch +				(desc, cur_size); + +			cur_offset->face = i; +			cur_offset->mip = j; +			cur_offset->bo_offset = cur_bo_offset; +			cur_bo_offset += svga3dsurface_get_image_buffer_size +				(desc, cur_size, stride); +			++cur_offset; +			++cur_size; +		} +	} +	res->backup_size = cur_bo_offset; +	if (srf->scanout && +	    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"); +			ret = -ENOMEM; +			goto out_no_copy; +		} +	} else { +		srf->snooper.image = NULL; +	} +	srf->snooper.crtc = NULL; + +	user_srf->prime.base.shareable = false; +	user_srf->prime.base.tfile = NULL; +	if (drm_is_primary_client(file_priv)) +		user_srf->master = drm_master_get(file_priv->master); + +	/** +	 * 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)) +		goto out_unlock; + +	/* +	 * A gb-aware client referencing a shared surface will +	 * expect a backup buffer to be present. +	 */ +	if (dev_priv->has_mob && req->shareable) { +		uint32_t backup_handle; + +		ret = vmw_user_dmabuf_alloc(dev_priv, tfile, +					    res->backup_size, +					    true, +					    &backup_handle, +					    &res->backup); +		if (unlikely(ret != 0)) { +			vmw_resource_unreference(&res); +			goto out_unlock; +		} +	} + +	tmp = vmw_resource_reference(&srf->res); +	ret = ttm_prime_object_init(tfile, res->backup_size, &user_srf->prime, +				    req->shareable, VMW_RES_SURFACE, +				    &vmw_user_surface_base_release, NULL); + +	if (unlikely(ret != 0)) { +		vmw_resource_unreference(&tmp); +		vmw_resource_unreference(&res); +		goto out_unlock; +	} + +	rep->sid = user_srf->prime.base.hash.key; +	vmw_resource_unreference(&res); + +	ttm_read_unlock(&dev_priv->reservation_sem); +	return 0; +out_no_copy: +	kfree(srf->offsets); +out_no_offsets: +	kfree(srf->sizes); +out_no_sizes: +	ttm_prime_object_kfree(user_srf, prime); +out_no_user_srf: +	ttm_mem_global_free(vmw_mem_glob(dev_priv), size); +out_unlock: +	ttm_read_unlock(&dev_priv->reservation_sem); +	return ret; +} + + +static int +vmw_surface_handle_reference(struct vmw_private *dev_priv, +			     struct drm_file *file_priv, +			     uint32_t u_handle, +			     enum drm_vmw_handle_type handle_type, +			     struct ttm_base_object **base_p) +{ +	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; +	struct vmw_user_surface *user_srf; +	uint32_t handle; +	struct ttm_base_object *base; +	int ret; + +	if (handle_type == DRM_VMW_HANDLE_PRIME) { +		ret = ttm_prime_fd_to_handle(tfile, u_handle, &handle); +		if (unlikely(ret != 0)) +			return ret; +	} else { +		if (unlikely(drm_is_render_client(file_priv))) { +			DRM_ERROR("Render client refused legacy " +				  "surface reference.\n"); +			return -EACCES; +		} +		handle = u_handle; +	} + +	ret = -EINVAL; +	base = ttm_base_object_lookup_for_ref(dev_priv->tdev, handle); +	if (unlikely(base == NULL)) { +		DRM_ERROR("Could not find surface to reference.\n"); +		goto out_no_lookup; +	} + +	if (unlikely(ttm_base_object_type(base) != VMW_RES_SURFACE)) { +		DRM_ERROR("Referenced object is not a surface.\n"); +		goto out_bad_resource; +	} + +	if (handle_type != DRM_VMW_HANDLE_PRIME) { +		user_srf = container_of(base, struct vmw_user_surface, +					prime.base); + +		/* +		 * Make sure the surface creator has the same +		 * authenticating master. +		 */ +		if (drm_is_primary_client(file_priv) && +		    user_srf->master != file_priv->master) { +			DRM_ERROR("Trying to reference surface outside of" +				  " master domain.\n"); +			ret = -EACCES; +			goto out_bad_resource; +		} + +		ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL); +		if (unlikely(ret != 0)) { +			DRM_ERROR("Could not add a reference to a surface.\n"); +			goto out_bad_resource; +		} +	} + +	*base_p = base; +	return 0; + +out_bad_resource: +	ttm_base_object_unref(&base); +out_no_lookup: +	if (handle_type == DRM_VMW_HANDLE_PRIME) +		(void) ttm_ref_object_base_unref(tfile, handle, TTM_REF_USAGE); + +	return ret; +} + +/** + * vmw_user_surface_define_ioctl - Ioctl function implementing + *                                  the user surface reference functionality. + * + * @dev:            Pointer to a struct drm_device. + * @data:           Pointer to data copied from / to user-space. + * @file_priv:      Pointer to a drm file private structure. + */ +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_surface *srf; +	struct vmw_user_surface *user_srf; +	struct drm_vmw_size __user *user_sizes; +	struct ttm_base_object *base; +	int ret; + +	ret = vmw_surface_handle_reference(dev_priv, file_priv, req->sid, +					   req->handle_type, &base); +	if (unlikely(ret != 0)) +		return ret; + +	user_srf = container_of(base, struct vmw_user_surface, prime.base); +	srf = &user_srf->srf; + +	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->base_size, +				   sizeof(srf->base_size)); +	if (unlikely(ret != 0)) { +		DRM_ERROR("copy_to_user failed %p %u\n", +			  user_sizes, srf->num_sizes); +		ttm_ref_object_base_unref(tfile, base->hash.key, TTM_REF_USAGE); +		ret = -EFAULT; +	} + +	ttm_base_object_unref(&base); + +	return ret; +} + +/** + * vmw_surface_define_encode - Encode a surface_define command. + * + * @srf: Pointer to a struct vmw_surface object. + * @cmd_space: Pointer to memory area in which the commands should be encoded. + */ +static int vmw_gb_surface_create(struct vmw_resource *res) +{ +	struct vmw_private *dev_priv = res->dev_priv; +	struct vmw_surface *srf = vmw_res_to_srf(res); +	uint32_t cmd_len, submit_len; +	int ret; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdDefineGBSurface body; +	} *cmd; + +	if (likely(res->id != -1)) +		return 0; + +	(void) vmw_3d_resource_inc(dev_priv, false); +	ret = vmw_resource_alloc_id(res); +	if (unlikely(ret != 0)) { +		DRM_ERROR("Failed to allocate a surface id.\n"); +		goto out_no_id; +	} + +	if (unlikely(res->id >= VMWGFX_NUM_GB_SURFACE)) { +		ret = -EBUSY; +		goto out_no_fifo; +	} + +	cmd_len = sizeof(cmd->body); +	submit_len = sizeof(*cmd); +	cmd = vmw_fifo_reserve(dev_priv, submit_len); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for surface " +			  "creation.\n"); +		ret = -ENOMEM; +		goto out_no_fifo; +	} + +	cmd->header.id = SVGA_3D_CMD_DEFINE_GB_SURFACE; +	cmd->header.size = cmd_len; +	cmd->body.sid = srf->res.id; +	cmd->body.surfaceFlags = srf->flags; +	cmd->body.format = cpu_to_le32(srf->format); +	cmd->body.numMipLevels = srf->mip_levels[0]; +	cmd->body.multisampleCount = srf->multisample_count; +	cmd->body.autogenFilter = srf->autogen_filter; +	cmd->body.size.width = srf->base_size.width; +	cmd->body.size.height = srf->base_size.height; +	cmd->body.size.depth = srf->base_size.depth; +	vmw_fifo_commit(dev_priv, submit_len); + +	return 0; + +out_no_fifo: +	vmw_resource_release_id(res); +out_no_id: +	vmw_3d_resource_dec(dev_priv, false); +	return ret; +} + + +static int vmw_gb_surface_bind(struct vmw_resource *res, +			       struct ttm_validate_buffer *val_buf) +{ +	struct vmw_private *dev_priv = res->dev_priv; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdBindGBSurface body; +	} *cmd1; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdUpdateGBSurface body; +	} *cmd2; +	uint32_t submit_size; +	struct ttm_buffer_object *bo = val_buf->bo; + +	BUG_ON(bo->mem.mem_type != VMW_PL_MOB); + +	submit_size = sizeof(*cmd1) + (res->backup_dirty ? sizeof(*cmd2) : 0); + +	cmd1 = vmw_fifo_reserve(dev_priv, submit_size); +	if (unlikely(cmd1 == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for surface " +			  "binding.\n"); +		return -ENOMEM; +	} + +	cmd1->header.id = SVGA_3D_CMD_BIND_GB_SURFACE; +	cmd1->header.size = sizeof(cmd1->body); +	cmd1->body.sid = res->id; +	cmd1->body.mobid = bo->mem.start; +	if (res->backup_dirty) { +		cmd2 = (void *) &cmd1[1]; +		cmd2->header.id = SVGA_3D_CMD_UPDATE_GB_SURFACE; +		cmd2->header.size = sizeof(cmd2->body); +		cmd2->body.sid = res->id; +		res->backup_dirty = false; +	} +	vmw_fifo_commit(dev_priv, submit_size); + +	return 0; +} + +static int vmw_gb_surface_unbind(struct vmw_resource *res, +				 bool readback, +				 struct ttm_validate_buffer *val_buf) +{ +	struct vmw_private *dev_priv = res->dev_priv; +	struct ttm_buffer_object *bo = val_buf->bo; +	struct vmw_fence_obj *fence; + +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdReadbackGBSurface body; +	} *cmd1; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdInvalidateGBSurface body; +	} *cmd2; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdBindGBSurface body; +	} *cmd3; +	uint32_t submit_size; +	uint8_t *cmd; + + +	BUG_ON(bo->mem.mem_type != VMW_PL_MOB); + +	submit_size = sizeof(*cmd3) + (readback ? sizeof(*cmd1) : sizeof(*cmd2)); +	cmd = vmw_fifo_reserve(dev_priv, submit_size); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for surface " +			  "unbinding.\n"); +		return -ENOMEM; +	} + +	if (readback) { +		cmd1 = (void *) cmd; +		cmd1->header.id = SVGA_3D_CMD_READBACK_GB_SURFACE; +		cmd1->header.size = sizeof(cmd1->body); +		cmd1->body.sid = res->id; +		cmd3 = (void *) &cmd1[1]; +	} else { +		cmd2 = (void *) cmd; +		cmd2->header.id = SVGA_3D_CMD_INVALIDATE_GB_SURFACE; +		cmd2->header.size = sizeof(cmd2->body); +		cmd2->body.sid = res->id; +		cmd3 = (void *) &cmd2[1]; +	} + +	cmd3->header.id = SVGA_3D_CMD_BIND_GB_SURFACE; +	cmd3->header.size = sizeof(cmd3->body); +	cmd3->body.sid = res->id; +	cmd3->body.mobid = SVGA3D_INVALID_ID; + +	vmw_fifo_commit(dev_priv, submit_size); + +	/* +	 * Create a fence object and fence the backup buffer. +	 */ + +	(void) vmw_execbuf_fence_commands(NULL, dev_priv, +					  &fence, NULL); + +	vmw_fence_single_bo(val_buf->bo, fence); + +	if (likely(fence != NULL)) +		vmw_fence_obj_unreference(&fence); + +	return 0; +} + +static int vmw_gb_surface_destroy(struct vmw_resource *res) +{ +	struct vmw_private *dev_priv = res->dev_priv; +	struct { +		SVGA3dCmdHeader header; +		SVGA3dCmdDestroyGBSurface body; +	} *cmd; + +	if (likely(res->id == -1)) +		return 0; + +	mutex_lock(&dev_priv->binding_mutex); +	vmw_context_binding_res_list_scrub(&res->binding_head); + +	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); +	if (unlikely(cmd == NULL)) { +		DRM_ERROR("Failed reserving FIFO space for surface " +			  "destruction.\n"); +		mutex_unlock(&dev_priv->binding_mutex); +		return -ENOMEM; +	} + +	cmd->header.id = SVGA_3D_CMD_DESTROY_GB_SURFACE; +	cmd->header.size = sizeof(cmd->body); +	cmd->body.sid = res->id; +	vmw_fifo_commit(dev_priv, sizeof(*cmd)); +	mutex_unlock(&dev_priv->binding_mutex); +	vmw_resource_release_id(res); +	vmw_3d_resource_dec(dev_priv, false); + +	return 0; +} + +/** + * vmw_gb_surface_define_ioctl - Ioctl function implementing + *                               the user surface define functionality. + * + * @dev:            Pointer to a struct drm_device. + * @data:           Pointer to data copied from / to user-space. + * @file_priv:      Pointer to a drm file private structure. + */ +int vmw_gb_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; +	struct vmw_surface *srf; +	struct vmw_resource *res; +	struct vmw_resource *tmp; +	union drm_vmw_gb_surface_create_arg *arg = +	    (union drm_vmw_gb_surface_create_arg *)data; +	struct drm_vmw_gb_surface_create_req *req = &arg->req; +	struct drm_vmw_gb_surface_create_rep *rep = &arg->rep; +	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; +	int ret; +	uint32_t size; +	const struct svga3d_surface_desc *desc; +	uint32_t backup_handle; + +	if (unlikely(vmw_user_surface_size == 0)) +		vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) + +			128; + +	size = vmw_user_surface_size + 128; + +	desc = svga3dsurface_get_desc(req->format); +	if (unlikely(desc->block_desc == SVGA3DBLOCKDESC_NONE)) { +		DRM_ERROR("Invalid surface format for surface creation.\n"); +		return -EINVAL; +	} + +	ret = ttm_read_lock(&dev_priv->reservation_sem, true); +	if (unlikely(ret != 0)) +		return ret; + +	ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), +				   size, false, true); +	if (unlikely(ret != 0)) { +		if (ret != -ERESTARTSYS) +			DRM_ERROR("Out of graphics memory for surface" +				  " creation.\n"); +		goto out_unlock; +	} + +	user_srf = kzalloc(sizeof(*user_srf), GFP_KERNEL); +	if (unlikely(user_srf == NULL)) { +		ret = -ENOMEM; +		goto out_no_user_srf; +	} + +	srf = &user_srf->srf; +	res = &srf->res; + +	srf->flags = req->svga3d_flags; +	srf->format = req->format; +	srf->scanout = req->drm_surface_flags & drm_vmw_surface_flag_scanout; +	srf->mip_levels[0] = req->mip_levels; +	srf->num_sizes = 1; +	srf->sizes = NULL; +	srf->offsets = NULL; +	user_srf->size = size; +	srf->base_size = req->base_size; +	srf->autogen_filter = SVGA3D_TEX_FILTER_NONE; +	srf->multisample_count = req->multisample_count; +	res->backup_size = svga3dsurface_get_serialized_size +	  (srf->format, srf->base_size, srf->mip_levels[0], +	   srf->flags & SVGA3D_SURFACE_CUBEMAP); + +	user_srf->prime.base.shareable = false; +	user_srf->prime.base.tfile = NULL; +	if (drm_is_primary_client(file_priv)) +		user_srf->master = drm_master_get(file_priv->master); + +	/** +	 * 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)) +		goto out_unlock; + +	if (req->buffer_handle != SVGA3D_INVALID_ID) { +		ret = vmw_user_dmabuf_lookup(tfile, req->buffer_handle, +					     &res->backup); +	} else if (req->drm_surface_flags & +		   drm_vmw_surface_flag_create_buffer) +		ret = vmw_user_dmabuf_alloc(dev_priv, tfile, +					    res->backup_size, +					    req->drm_surface_flags & +					    drm_vmw_surface_flag_shareable, +					    &backup_handle, +					    &res->backup); + +	if (unlikely(ret != 0)) { +		vmw_resource_unreference(&res); +		goto out_unlock; +	} + +	tmp = vmw_resource_reference(&srf->res); +	ret = ttm_prime_object_init(tfile, res->backup_size, &user_srf->prime, +				    req->drm_surface_flags & +				    drm_vmw_surface_flag_shareable, +				    VMW_RES_SURFACE, +				    &vmw_user_surface_base_release, NULL); + +	if (unlikely(ret != 0)) { +		vmw_resource_unreference(&tmp); +		vmw_resource_unreference(&res); +		goto out_unlock; +	} + +	rep->handle = user_srf->prime.base.hash.key; +	rep->backup_size = res->backup_size; +	if (res->backup) { +		rep->buffer_map_handle = +			drm_vma_node_offset_addr(&res->backup->base.vma_node); +		rep->buffer_size = res->backup->base.num_pages * PAGE_SIZE; +		rep->buffer_handle = backup_handle; +	} else { +		rep->buffer_map_handle = 0; +		rep->buffer_size = 0; +		rep->buffer_handle = SVGA3D_INVALID_ID; +	} + +	vmw_resource_unreference(&res); + +	ttm_read_unlock(&dev_priv->reservation_sem); +	return 0; +out_no_user_srf: +	ttm_mem_global_free(vmw_mem_glob(dev_priv), size); +out_unlock: +	ttm_read_unlock(&dev_priv->reservation_sem); +	return ret; +} + +/** + * vmw_gb_surface_reference_ioctl - Ioctl function implementing + *                                  the user surface reference functionality. + * + * @dev:            Pointer to a struct drm_device. + * @data:           Pointer to data copied from / to user-space. + * @file_priv:      Pointer to a drm file private structure. + */ +int vmw_gb_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_gb_surface_reference_arg *arg = +	    (union drm_vmw_gb_surface_reference_arg *)data; +	struct drm_vmw_surface_arg *req = &arg->req; +	struct drm_vmw_gb_surface_ref_rep *rep = &arg->rep; +	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; +	struct vmw_surface *srf; +	struct vmw_user_surface *user_srf; +	struct ttm_base_object *base; +	uint32_t backup_handle; +	int ret = -EINVAL; + +	ret = vmw_surface_handle_reference(dev_priv, file_priv, req->sid, +					   req->handle_type, &base); +	if (unlikely(ret != 0)) +		return ret; + +	user_srf = container_of(base, struct vmw_user_surface, prime.base); +	srf = &user_srf->srf; +	if (srf->res.backup == NULL) { +		DRM_ERROR("Shared GB surface is missing a backup buffer.\n"); +		goto out_bad_resource; +	} + +	mutex_lock(&dev_priv->cmdbuf_mutex); /* Protect res->backup */ +	ret = vmw_user_dmabuf_reference(tfile, srf->res.backup, +					&backup_handle); +	mutex_unlock(&dev_priv->cmdbuf_mutex); + +	if (unlikely(ret != 0)) { +		DRM_ERROR("Could not add a reference to a GB surface " +			  "backup buffer.\n"); +		(void) ttm_ref_object_base_unref(tfile, base->hash.key, +						 TTM_REF_USAGE); +		goto out_bad_resource; +	} + +	rep->creq.svga3d_flags = srf->flags; +	rep->creq.format = srf->format; +	rep->creq.mip_levels = srf->mip_levels[0]; +	rep->creq.drm_surface_flags = 0; +	rep->creq.multisample_count = srf->multisample_count; +	rep->creq.autogen_filter = srf->autogen_filter; +	rep->creq.buffer_handle = backup_handle; +	rep->creq.base_size = srf->base_size; +	rep->crep.handle = user_srf->prime.base.hash.key; +	rep->crep.backup_size = srf->res.backup_size; +	rep->crep.buffer_handle = backup_handle; +	rep->crep.buffer_map_handle = +		drm_vma_node_offset_addr(&srf->res.backup->base.vma_node); +	rep->crep.buffer_size = srf->res.backup->base.num_pages * PAGE_SIZE; + +out_bad_resource: +	ttm_base_object_unref(&base); + +	return ret; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c index 1e8eedd901e..98d6bfb3a99 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c @@ -25,7 +25,7 @@   *   **************************************************************************/ -#include "drmP.h" +#include <drm/drmP.h>  #include "vmwgfx_drv.h"  int vmw_mmap(struct file *filp, struct vm_area_struct *vma) @@ -34,9 +34,8 @@ int vmw_mmap(struct file *filp, struct vm_area_struct *vma)  	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); +		DRM_ERROR("Illegal attempt to mmap old fifo space.\n"); +		return -EINVAL;  	}  	file_priv = filp->private_data;  | 
