diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx')
34 files changed, 16009 insertions, 2628 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 10fc01f69c4..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 @@ -265,13 +465,13 @@ struct vmw_private { struct vmw_sw_context ctx; struct mutex cmdbuf_mutex; + struct mutex binding_mutex; /** * Operating mode. */ bool stealth; - bool is_opened; bool enable_fb; /** @@ -285,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; @@ -319,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); @@ -335,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, @@ -390,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 @@ -398,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 @@ -412,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 @@ -430,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 @@ -444,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 @@ -508,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 */ @@ -542,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 */ @@ -563,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) @@ -576,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 41b95ed6dbc..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,77 +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); + 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; @@ -661,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 bfab60c938a..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; @@ -509,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) { @@ -588,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; @@ -656,12 +595,12 @@ int vmw_fb_off(struct vmw_private *vmw_priv) par->dirty.active = false; spin_unlock_irqrestore(&par->dirty.lock, flags); - flush_delayed_work_sync(&info->deferred_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; } @@ -687,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 5408b1b7996..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; - - ret = vmw_context_init(dev_priv, res, vmw_user_context_free); - if (unlikely(ret != 0)) - return ret; + BUG_ON(*out_surf || *out_buf); - 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; - } - - 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); + if (unlikely(struct_size == 0)) { + size_t backend_size = ttm_round_pot(vmw_tt_size); - vmw_fifo_commit(dev_priv, sizeof(*cmd)); - vmw_3d_resource_dec(dev_priv); -} + 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)); + } -void vmw_surface_res_free(struct vmw_resource *res) -{ - struct vmw_surface *srf = container_of(res, struct vmw_surface, res); + if (dev_priv->map_mode == vmw_dma_alloc_coherent) + page_array_size += + ttm_round_pot(num_pages * sizeof(dma_addr_t)); - 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; - - user_srf = container_of(base, struct vmw_user_surface, base); - *id = user_srf->srf.res.id; - ret = 0; + *p_dma_buf = &user_bo->dma; + *handle = user_bo->prime.base.hash.key; -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); + if (flags & drm_vmw_synccpu_allow_cs) { + struct ttm_bo_device *bdev = bo->bdev; - acc_size = - vmw_dmabuf_acc_size(bdev->glob, - (size + PAGE_SIZE - 1) >> PAGE_SHIFT); - - ret = ttm_mem_global_alloc(mem_glob, acc_size, false, false); - if (unlikely(ret != 0)) { - /* we must free the bo here as - * ttm_buffer_object_init does so as well */ - bo_free(&vmw_bo->base); + 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,28 @@ 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)) 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)) - goto out_no_base_object; - 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; - } + 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; + + vmw_dmabuf_unreference(&dma_buf); -out_no_base_object: - ttm_bo_unref(&tmp); out_no_dmabuf: - ttm_read_unlock(&vmaster->lock); + ttm_read_unlock(&dev_priv->reservation_sem); return ret; } @@ -898,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) { @@ -932,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; @@ -947,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 */ @@ -971,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) @@ -994,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); } /** @@ -1030,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; @@ -1057,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, @@ -1087,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; } @@ -1098,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; @@ -1120,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; |
